From: ThomasV Date: Thu, 23 Jan 2014 16:06:47 +0000 (+0100) Subject: json rpc daemon X-Git-Url: https://git.novaco.in/?p=electrum-nvc.git;a=commitdiff_plain;h=be506af2460e47527d03dc7d864ec72a04d8d7e7 json rpc daemon --- diff --git a/electrum b/electrum index a8934c2..f6b8506 100755 --- a/electrum +++ b/electrum @@ -105,15 +105,34 @@ def print_help_cb(self, opt, value, parser): def run_command(cmd, password=None, args=[]): + import xmlrpclib, socket cmd_runner = Commands(wallet, network) - func = getattr(cmd_runner, cmd) + func = getattr(cmd_runner, cmd.name) cmd_runner.password = password + + if cmd.requires_network and not options.offline: + cmd_runner.network = xmlrpclib.ServerProxy('http://localhost:8000') + if wallet: + wallet.start_threads(cmd_runner.network) + wallet.update() + else: + cmd_runner.network = None + try: result = func(*args[1:]) + except socket.error: + print "Network server not found." + sys.exit(1) except Exception: traceback.print_exc(file=sys.stdout) sys.exit(1) + + if cmd.requires_network and not options.offline: + if wallet: + wallet.stop_threads() + + if type(result) == str: util.print_msg(result) elif result is not None: @@ -201,6 +220,11 @@ if __name__ == '__main__': print_msg("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option") sys.exit(0) + + + + + if cmd.name in ['create', 'restore']: if storage.file_exists: sys.exit("Error: Remove the existing wallet first!") @@ -345,19 +369,41 @@ if __name__ == '__main__': print_msg("Warning: Final argument was reconstructed from several arguments:", repr(message)) args = args[0:cmd.min_args] + [message] - # open session - if cmd.requires_network and not options.offline: - network = Network(config) - if not network.start(wait=True): - print_msg("Not connected, aborting.") - sys.exit(1) - print_error("Connected to " + network.interface.connection_msg) - if wallet: - wallet.start_threads(network) - wallet.update() - else: - network = None + if cmd.name == 'start_network': + pid = os.fork() + if (pid == 0): # The first child. + os.chdir("/") + os.setsid() + os.umask(0) + pid2 = os.fork() + if (pid2 == 0): # Second child + from SimpleXMLRPCServer import SimpleXMLRPCServer + # start the daemon + network = Network(config) + if not network.start(wait=True): + print_msg("Not connected, aborting.") + sys.exit(1) + print_msg("Connected to " + network.interface.connection_msg) + server = SimpleXMLRPCServer(('localhost',8000), allow_none=True, logRequests=False) + server.register_function(network.synchronous_get, 'synchronous_get') + server.register_function(network.get_servers, 'get_servers') + server.register_function(network.main_server, 'main_server') + server.register_function(network.send, 'send') + server.register_function(network.subscribe, 'subscribe') + server.register_function(network.is_connected, 'is_connected') + server.register_function(network.is_up_to_date, 'is_up_to_date') + server.register_function(lambda: setattr(server,'running', False), 'stop') + server.running = True + while server.running: + server.handle_request() + print_msg("Server stopped") + sys.exit(0) + else: + sys.exit(0) + else: + sys.exit(0) + # run the command if cmd.name == 'deseed': @@ -394,11 +440,8 @@ if __name__ == '__main__': wallet.update_password(password, new_password) else: - run_command(cmd.name, password, args) + run_command(cmd, password, args) - if network: - if wallet: - wallet.stop_threads() - network.stop() - time.sleep(0.1) - sys.exit(0) + + time.sleep(0.1) + sys.exit(0) diff --git a/lib/commands.py b/lib/commands.py index ef04fe0..7203d81 100644 --- a/lib/commands.py +++ b/lib/commands.py @@ -67,9 +67,9 @@ register_command('dumpprivkeys', 0, 0, False, True, True, 'dump all pr register_command('freeze', 1, 1, False, True, True, 'Freeze the funds at one of your wallet\'s addresses', 'freeze
') register_command('getbalance', 0, 1, True, True, False, 'Return the balance of your wallet, or of one account in your wallet', 'getbalance []') register_command('getservers', 0, 0, True, False, False, 'Return the list of available servers') -register_command('getversion', 0, 0, False, False, False, 'Return the version of your client', 'getversion') -register_command('getaddressbalance', 1, 1, True, True, False, 'Return the balance of an address', 'getaddressbalance
') -register_command('getaddresshistory', 1, 1, True, True, False, 'Return the transaction history of a wallet address', 'getaddresshistory
') +register_command('getversion', 0, 0, False, False, False, 'Return the version of your client', 'getversion') +register_command('getaddressbalance', 1, 1, True, False, False, 'Return the balance of an address', 'getaddressbalance
') +register_command('getaddresshistory', 1, 1, True, False, False, 'Return the transaction history of a wallet address', 'getaddresshistory
') register_command('getconfig', 1, 1, False, False, False, 'Return a configuration variable', 'getconfig ') register_command('getpubkeys', 1, 1, False, True, False, 'Return the public keys for a wallet address', 'getpubkeys ') register_command('getrawtransaction', 1, 1, True, False, False, 'Retrieve a transaction', 'getrawtransaction ') @@ -79,7 +79,8 @@ register_command('help', 0, 1, False, False, False, 'Prints this register_command('history', 0, 0, True, True, False, 'Returns the transaction history of your wallet') register_command('importprivkey', 1, 1, False, True, True, 'Import a private key', 'importprivkey ') register_command('listaddresses', 2, 2, False, True, False, 'Returns your list of addresses.', '', listaddr_options) -register_command('listunspent', 0, 0, True, True, False, 'Returns the list of unspent inputs in your wallet.') +register_command('listunspent', 0, 0, True, False, False, 'Returns the list of unspent inputs in your wallet.') +register_command('getaddressunspent', 1, 1, True, False, False, 'Returns the list of unspent inputs in your wallet.') register_command('mktx', 5, 5, False, True, True, 'Create a signed transaction', 'mktx [label]', payto_options) register_command('mksendmanytx', 4, 4, False, True, True, 'Create a signed transaction', mksendmany_syntax, payto_options) register_command('payto', 5, 5, True, True, True, 'Create and broadcast a transaction.', payto_syntax, payto_options) @@ -95,6 +96,8 @@ register_command('unfreeze', 1, 1, False, True, False, 'Unfreeze th register_command('validateaddress', 1, 1, False, False, False, 'Check that the address is valid', 'validateaddress
') register_command('verifymessage', 3,-1, False, False, False, 'Verifies a signature', verifymessage_syntax) +register_command('start_network', 0, 0, False, False, False, 'start the daemon') +register_command('stop_network', 0, 0, True, False, False, 'stop the daemon') @@ -106,6 +109,7 @@ class Commands: self._callback = callback self.password = None + def _run(self, method, args, password_getter): cmd = known_commands[method] if cmd.requires_password and self.wallet.use_encryption: @@ -117,11 +121,14 @@ class Commands: apply(self._callback, ()) return result + def getaddresshistory(self, addr): - assert self.wallet.is_mine(addr) - h = self.wallet.get_history(addr) - if h is None: h = self.network.synchronous_get([ ('blockchain.address.get_history',[addr]) ])[0] - return h + return self.network.synchronous_get([ ('blockchain.address.get_history',[addr]) ])[0] + + + def stop_network(self): + return self.network.stop() + def listunspent(self): import copy @@ -129,6 +136,11 @@ class Commands: for i in l: i["value"] = str(Decimal(i["value"])/100000000) return l + + def getaddressunspent(self, addr): + return self.network.synchronous_get([ ('blockchain.address.getunspent',[addr]) ])[0] + + def createrawtransaction(self, inputs, outputs): # convert to own format for i in inputs: @@ -199,10 +211,11 @@ class Commands: return out def getaddressbalance(self, addr): - c, u = self.wallet.get_addr_balance(addr) - out = { "confirmed": str(Decimal(c)/100000000) } - if u: out["unconfirmed"] = str(Decimal(u)/100000000) - return out + # c, u = self.wallet.get_addr_balance(addr) + # out = { "confirmed": str(Decimal(c)/100000000) } + # if u: out["unconfirmed"] = str(Decimal(u)/100000000) + # return out + return self.network.synchronous_get([ ('blockchain.address.get_balance',[addr]) ])[0] def getservers(self): return self.network.get_servers() @@ -350,11 +363,19 @@ class Commands: if cmd.options: print_msg("options:\n" + cmd.options) return None + def getrawtransaction(self, tx_hash): + import transaction if self.wallet: tx = self.wallet.transactions.get(tx_hash) if tx: return tx - return self.network.retrieve_transaction(tx_hash) + + r = self.network.synchronous_get([ ('blockchain.transaction.get',[tx_hash]) ])[0] + if r: + return transaction.Transaction(r) + else: + return "unknown transaction" + diff --git a/lib/network.py b/lib/network.py index b400ade..35b2db1 100644 --- a/lib/network.py +++ b/lib/network.py @@ -82,6 +82,14 @@ class Network(threading.Thread): return self.interface and self.interface.is_connected + def is_up_to_date(self): + return self.interface.is_up_to_date() + + + def main_server(self): + return self.interface.server + + def send_subscriptions(self): for cb, sub in self.subscriptions.items(): self.interface.send(sub, cb) @@ -372,11 +380,11 @@ class Network(threading.Thread): return out - def retrieve_transaction(self, tx_hash, tx_height=0): - import transaction - r = self.synchronous_get([ ('blockchain.transaction.get',[tx_hash, tx_height]) ])[0] - if r: - return transaction.Transaction(r) + #def retrieve_transaction(self, tx_hash, tx_height=0): + # import transaction + # r = self.synchronous_get([ ('blockchain.transaction.get',[tx_hash, tx_height]) ])[0] + # if r: + # return transaction.Transaction(r) def parse_servers(self, result): diff --git a/lib/version.py b/lib/version.py index 022e226..bb49d26 100644 --- a/lib/version.py +++ b/lib/version.py @@ -1,5 +1,5 @@ -ELECTRUM_VERSION = "1.9.7" # version of the client package -PROTOCOL_VERSION = '0.6' # protocol version requested +ELECTRUM_VERSION = "2.0" # version of the client package +PROTOCOL_VERSION = '0.9' # protocol version requested SEED_VERSION = 4 # bump this every time the seed generation is modified SEED_PREFIX = '01' # the hash of the mnemonic seed must begin with this diff --git a/lib/wallet.py b/lib/wallet.py index fd9a103..e8fbc37 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -1550,7 +1550,7 @@ class Wallet: def start_threads(self, network): from verifier import TxVerifier self.network = network - if self.network: + if self.network is not None: self.verifier = TxVerifier(self.network, self.storage) self.verifier.start() self.set_verifier(self.verifier) @@ -1635,12 +1635,12 @@ class WalletSynchronizer(threading.Thread): if not self.network.is_connected(): self.network.wait_until_connected() - self.run_interface(self.network.interface) + self.run_interface() - def run_interface(self, interface): + def run_interface(self): - print_error("synchronizer: connected to", interface.server) + print_error("synchronizer: connected to", self.network.main_server()) requested_tx = [] missing_tx = [] @@ -1670,12 +1670,12 @@ class WalletSynchronizer(threading.Thread): # request missing transactions for tx_hash, tx_height in missing_tx: if (tx_hash, tx_height) not in requested_tx: - interface.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], lambda i,r: self.queue.put(r)) + self.network.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], lambda i,r: self.queue.put(r)) requested_tx.append( (tx_hash, tx_height) ) missing_tx = [] # detect if situation has changed - if interface.is_up_to_date() and self.queue.empty(): + if self.network.is_up_to_date() and self.queue.empty(): if not self.wallet.is_up_to_date(): self.wallet.set_up_to_date(True) self.was_updated = True @@ -1685,7 +1685,7 @@ class WalletSynchronizer(threading.Thread): self.was_updated = True if self.was_updated: - self.wallet.network.trigger_callback('updated') + self.network.trigger_callback('updated') self.was_updated = False # 2. get a response @@ -1694,8 +1694,9 @@ class WalletSynchronizer(threading.Thread): except Queue.Empty: continue - if interface != self.network.interface: - break + # see if it changed + #if interface != self.network.interface: + # break if not r: continue @@ -1713,7 +1714,7 @@ class WalletSynchronizer(threading.Thread): addr = params[0] if self.wallet.get_status(self.wallet.get_history(addr)) != result: if requested_histories.get(addr) is None: - interface.send([('blockchain.address.get_history', [addr])], lambda i,r:self.queue.put(r)) + self.network.send([('blockchain.address.get_history', [addr])], lambda i,r:self.queue.put(r)) requested_histories[addr] = result elif method == 'blockchain.address.get_history': @@ -1763,7 +1764,7 @@ class WalletSynchronizer(threading.Thread): print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) ) if self.was_updated and not requested_tx: - self.wallet.network.trigger_callback('updated') - self.wallet.network.trigger_callback("new_transaction") # Updated gets called too many times from other places as well; if we use that signal we get the notification three times - + self.network.trigger_callback('updated') + # Updated gets called too many times from other places as well; if we use that signal we get the notification three times + self.network.trigger_callback("new_transaction") self.was_updated = False