json rpc daemon
This commit is contained in:
parent
989a95bf25
commit
d4fbe85d7d
83
electrum
83
electrum
|
@ -105,15 +105,34 @@ def print_help_cb(self, opt, value, parser):
|
||||||
|
|
||||||
|
|
||||||
def run_command(cmd, password=None, args=[]):
|
def run_command(cmd, password=None, args=[]):
|
||||||
|
import xmlrpclib, socket
|
||||||
cmd_runner = Commands(wallet, network)
|
cmd_runner = Commands(wallet, network)
|
||||||
func = getattr(cmd_runner, cmd)
|
func = getattr(cmd_runner, cmd.name)
|
||||||
cmd_runner.password = password
|
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:
|
try:
|
||||||
result = func(*args[1:])
|
result = func(*args[1:])
|
||||||
|
except socket.error:
|
||||||
|
print "Network server not found."
|
||||||
|
sys.exit(1)
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc(file=sys.stdout)
|
traceback.print_exc(file=sys.stdout)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if cmd.requires_network and not options.offline:
|
||||||
|
if wallet:
|
||||||
|
wallet.stop_threads()
|
||||||
|
|
||||||
|
|
||||||
if type(result) == str:
|
if type(result) == str:
|
||||||
util.print_msg(result)
|
util.print_msg(result)
|
||||||
elif result is not None:
|
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")
|
print_msg("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if cmd.name in ['create', 'restore']:
|
if cmd.name in ['create', 'restore']:
|
||||||
if storage.file_exists:
|
if storage.file_exists:
|
||||||
sys.exit("Error: Remove the existing wallet first!")
|
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))
|
print_msg("Warning: Final argument was reconstructed from several arguments:", repr(message))
|
||||||
args = args[0:cmd.min_args] + [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:
|
if cmd.name == 'start_network':
|
||||||
wallet.start_threads(network)
|
pid = os.fork()
|
||||||
wallet.update()
|
if (pid == 0): # The first child.
|
||||||
else:
|
os.chdir("/")
|
||||||
network = None
|
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
|
# run the command
|
||||||
if cmd.name == 'deseed':
|
if cmd.name == 'deseed':
|
||||||
|
@ -394,11 +440,8 @@ if __name__ == '__main__':
|
||||||
wallet.update_password(password, new_password)
|
wallet.update_password(password, new_password)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
run_command(cmd.name, password, args)
|
run_command(cmd, password, args)
|
||||||
|
|
||||||
if network:
|
|
||||||
if wallet:
|
time.sleep(0.1)
|
||||||
wallet.stop_threads()
|
sys.exit(0)
|
||||||
network.stop()
|
|
||||||
time.sleep(0.1)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
|
@ -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 <address>')
|
register_command('freeze', 1, 1, False, True, True, 'Freeze the funds at one of your wallet\'s addresses', 'freeze <address>')
|
||||||
register_command('getbalance', 0, 1, True, True, False, 'Return the balance of your wallet, or of one account in your wallet', 'getbalance [<account>]')
|
register_command('getbalance', 0, 1, True, True, False, 'Return the balance of your wallet, or of one account in your wallet', 'getbalance [<account>]')
|
||||||
register_command('getservers', 0, 0, True, False, False, 'Return the list of available servers')
|
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('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 <address>')
|
register_command('getaddressbalance', 1, 1, True, False, False, 'Return the balance of an address', 'getaddressbalance <address>')
|
||||||
register_command('getaddresshistory', 1, 1, True, True, False, 'Return the transaction history of a wallet address', 'getaddresshistory <address>')
|
register_command('getaddresshistory', 1, 1, True, False, False, 'Return the transaction history of a wallet address', 'getaddresshistory <address>')
|
||||||
register_command('getconfig', 1, 1, False, False, False, 'Return a configuration variable', 'getconfig <name>')
|
register_command('getconfig', 1, 1, False, False, False, 'Return a configuration variable', 'getconfig <name>')
|
||||||
register_command('getpubkeys', 1, 1, False, True, False, 'Return the public keys for a wallet address', 'getpubkeys <bitcoin address>')
|
register_command('getpubkeys', 1, 1, False, True, False, 'Return the public keys for a wallet address', 'getpubkeys <bitcoin address>')
|
||||||
register_command('getrawtransaction', 1, 1, True, False, False, 'Retrieve a transaction', 'getrawtransaction <txhash>')
|
register_command('getrawtransaction', 1, 1, True, False, False, 'Retrieve a transaction', 'getrawtransaction <txhash>')
|
||||||
|
@ -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('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 <privatekey>')
|
register_command('importprivkey', 1, 1, False, True, True, 'Import a private key', 'importprivkey <privatekey>')
|
||||||
register_command('listaddresses', 2, 2, False, True, False, 'Returns your list of addresses.', '', listaddr_options)
|
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 <recipient> <amount> [label]', payto_options)
|
register_command('mktx', 5, 5, False, True, True, 'Create a signed transaction', 'mktx <recipient> <amount> [label]', payto_options)
|
||||||
register_command('mksendmanytx', 4, 4, False, True, True, 'Create a signed transaction', mksendmany_syntax, 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)
|
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 <address>')
|
register_command('validateaddress', 1, 1, False, False, False, 'Check that the address is valid', 'validateaddress <address>')
|
||||||
register_command('verifymessage', 3,-1, False, False, False, 'Verifies a signature', verifymessage_syntax)
|
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._callback = callback
|
||||||
self.password = None
|
self.password = None
|
||||||
|
|
||||||
|
|
||||||
def _run(self, method, args, password_getter):
|
def _run(self, method, args, password_getter):
|
||||||
cmd = known_commands[method]
|
cmd = known_commands[method]
|
||||||
if cmd.requires_password and self.wallet.use_encryption:
|
if cmd.requires_password and self.wallet.use_encryption:
|
||||||
|
@ -117,11 +121,14 @@ class Commands:
|
||||||
apply(self._callback, ())
|
apply(self._callback, ())
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def getaddresshistory(self, addr):
|
def getaddresshistory(self, addr):
|
||||||
assert self.wallet.is_mine(addr)
|
return self.network.synchronous_get([ ('blockchain.address.get_history',[addr]) ])[0]
|
||||||
h = self.wallet.get_history(addr)
|
|
||||||
if h is None: h = self.network.synchronous_get([ ('blockchain.address.get_history',[addr]) ])[0]
|
|
||||||
return h
|
def stop_network(self):
|
||||||
|
return self.network.stop()
|
||||||
|
|
||||||
|
|
||||||
def listunspent(self):
|
def listunspent(self):
|
||||||
import copy
|
import copy
|
||||||
|
@ -129,6 +136,11 @@ class Commands:
|
||||||
for i in l: i["value"] = str(Decimal(i["value"])/100000000)
|
for i in l: i["value"] = str(Decimal(i["value"])/100000000)
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
|
||||||
|
def getaddressunspent(self, addr):
|
||||||
|
return self.network.synchronous_get([ ('blockchain.address.getunspent',[addr]) ])[0]
|
||||||
|
|
||||||
|
|
||||||
def createrawtransaction(self, inputs, outputs):
|
def createrawtransaction(self, inputs, outputs):
|
||||||
# convert to own format
|
# convert to own format
|
||||||
for i in inputs:
|
for i in inputs:
|
||||||
|
@ -199,10 +211,11 @@ class Commands:
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def getaddressbalance(self, addr):
|
def getaddressbalance(self, addr):
|
||||||
c, u = self.wallet.get_addr_balance(addr)
|
# c, u = self.wallet.get_addr_balance(addr)
|
||||||
out = { "confirmed": str(Decimal(c)/100000000) }
|
# out = { "confirmed": str(Decimal(c)/100000000) }
|
||||||
if u: out["unconfirmed"] = str(Decimal(u)/100000000)
|
# if u: out["unconfirmed"] = str(Decimal(u)/100000000)
|
||||||
return out
|
# return out
|
||||||
|
return self.network.synchronous_get([ ('blockchain.address.get_balance',[addr]) ])[0]
|
||||||
|
|
||||||
def getservers(self):
|
def getservers(self):
|
||||||
return self.network.get_servers()
|
return self.network.get_servers()
|
||||||
|
@ -350,11 +363,19 @@ class Commands:
|
||||||
if cmd.options: print_msg("options:\n" + cmd.options)
|
if cmd.options: print_msg("options:\n" + cmd.options)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def getrawtransaction(self, tx_hash):
|
def getrawtransaction(self, tx_hash):
|
||||||
|
import transaction
|
||||||
if self.wallet:
|
if self.wallet:
|
||||||
tx = self.wallet.transactions.get(tx_hash)
|
tx = self.wallet.transactions.get(tx_hash)
|
||||||
if tx:
|
if tx:
|
||||||
return 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"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,14 @@ class Network(threading.Thread):
|
||||||
return self.interface and self.interface.is_connected
|
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):
|
def send_subscriptions(self):
|
||||||
for cb, sub in self.subscriptions.items():
|
for cb, sub in self.subscriptions.items():
|
||||||
self.interface.send(sub, cb)
|
self.interface.send(sub, cb)
|
||||||
|
@ -372,11 +380,11 @@ class Network(threading.Thread):
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def retrieve_transaction(self, tx_hash, tx_height=0):
|
#def retrieve_transaction(self, tx_hash, tx_height=0):
|
||||||
import transaction
|
# import transaction
|
||||||
r = self.synchronous_get([ ('blockchain.transaction.get',[tx_hash, tx_height]) ])[0]
|
# r = self.synchronous_get([ ('blockchain.transaction.get',[tx_hash, tx_height]) ])[0]
|
||||||
if r:
|
# if r:
|
||||||
return transaction.Transaction(r)
|
# return transaction.Transaction(r)
|
||||||
|
|
||||||
|
|
||||||
def parse_servers(self, result):
|
def parse_servers(self, result):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
ELECTRUM_VERSION = "1.9.7" # version of the client package
|
ELECTRUM_VERSION = "2.0" # version of the client package
|
||||||
PROTOCOL_VERSION = '0.6' # protocol version requested
|
PROTOCOL_VERSION = '0.9' # protocol version requested
|
||||||
SEED_VERSION = 4 # bump this every time the seed generation is modified
|
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
|
SEED_PREFIX = '01' # the hash of the mnemonic seed must begin with this
|
||||||
|
|
||||||
|
|
|
@ -1550,7 +1550,7 @@ class Wallet:
|
||||||
def start_threads(self, network):
|
def start_threads(self, network):
|
||||||
from verifier import TxVerifier
|
from verifier import TxVerifier
|
||||||
self.network = network
|
self.network = network
|
||||||
if self.network:
|
if self.network is not None:
|
||||||
self.verifier = TxVerifier(self.network, self.storage)
|
self.verifier = TxVerifier(self.network, self.storage)
|
||||||
self.verifier.start()
|
self.verifier.start()
|
||||||
self.set_verifier(self.verifier)
|
self.set_verifier(self.verifier)
|
||||||
|
@ -1635,12 +1635,12 @@ class WalletSynchronizer(threading.Thread):
|
||||||
if not self.network.is_connected():
|
if not self.network.is_connected():
|
||||||
self.network.wait_until_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 = []
|
requested_tx = []
|
||||||
missing_tx = []
|
missing_tx = []
|
||||||
|
@ -1670,12 +1670,12 @@ class WalletSynchronizer(threading.Thread):
|
||||||
# request missing transactions
|
# request missing transactions
|
||||||
for tx_hash, tx_height in missing_tx:
|
for tx_hash, tx_height in missing_tx:
|
||||||
if (tx_hash, tx_height) not in requested_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) )
|
requested_tx.append( (tx_hash, tx_height) )
|
||||||
missing_tx = []
|
missing_tx = []
|
||||||
|
|
||||||
# detect if situation has changed
|
# 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():
|
if not self.wallet.is_up_to_date():
|
||||||
self.wallet.set_up_to_date(True)
|
self.wallet.set_up_to_date(True)
|
||||||
self.was_updated = True
|
self.was_updated = True
|
||||||
|
@ -1685,7 +1685,7 @@ class WalletSynchronizer(threading.Thread):
|
||||||
self.was_updated = True
|
self.was_updated = True
|
||||||
|
|
||||||
if self.was_updated:
|
if self.was_updated:
|
||||||
self.wallet.network.trigger_callback('updated')
|
self.network.trigger_callback('updated')
|
||||||
self.was_updated = False
|
self.was_updated = False
|
||||||
|
|
||||||
# 2. get a response
|
# 2. get a response
|
||||||
|
@ -1694,8 +1694,9 @@ class WalletSynchronizer(threading.Thread):
|
||||||
except Queue.Empty:
|
except Queue.Empty:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if interface != self.network.interface:
|
# see if it changed
|
||||||
break
|
#if interface != self.network.interface:
|
||||||
|
# break
|
||||||
|
|
||||||
if not r:
|
if not r:
|
||||||
continue
|
continue
|
||||||
|
@ -1713,7 +1714,7 @@ class WalletSynchronizer(threading.Thread):
|
||||||
addr = params[0]
|
addr = params[0]
|
||||||
if self.wallet.get_status(self.wallet.get_history(addr)) != result:
|
if self.wallet.get_status(self.wallet.get_history(addr)) != result:
|
||||||
if requested_histories.get(addr) is None:
|
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
|
requested_histories[addr] = result
|
||||||
|
|
||||||
elif method == 'blockchain.address.get_history':
|
elif method == 'blockchain.address.get_history':
|
||||||
|
@ -1763,7 +1764,7 @@ class WalletSynchronizer(threading.Thread):
|
||||||
print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) )
|
print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) )
|
||||||
|
|
||||||
if self.was_updated and not requested_tx:
|
if self.was_updated and not requested_tx:
|
||||||
self.wallet.network.trigger_callback('updated')
|
self.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
|
# 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
|
self.was_updated = False
|
||||||
|
|
Loading…
Reference in New Issue