json rpc daemon

This commit is contained in:
ThomasV 2014-01-23 17:06:47 +01:00
parent 989a95bf25
commit d4fbe85d7d
5 changed files with 126 additions and 53 deletions

View File

@ -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)

View File

@ -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('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('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('getaddresshistory', 1, 1, True, True, False, 'Return the transaction history of a wallet address', 'getaddresshistory <address>')
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 <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('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>')
@ -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 <privatekey>')
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('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 <address>')
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"

View File

@ -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):

View File

@ -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

View File

@ -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