json rpc daemon
authorThomasV <thomasv@gitorious>
Thu, 23 Jan 2014 16:06:47 +0000 (17:06 +0100)
committerThomasV <thomasv@gitorious>
Thu, 23 Jan 2014 16:08:09 +0000 (17:08 +0100)
electrum
lib/commands.py
lib/network.py
lib/version.py
lib/wallet.py

index a8934c2..f6b8506 100755 (executable)
--- 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)
index ef04fe0..7203d81 100644 (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"
+
 
 
index b400ade..35b2db1 100644 (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):
index 022e226..bb49d26 100644 (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
 
index fd9a103..e8fbc37 100644 (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