URI: 
       tmerge jsonrpc gui and daemon - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 4682d95a76459a5fe837630f83f60653c3f32c5a
   DIR parent e190024f735fe9255d868e758f5092f9f2924524
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Mon, 30 Nov 2015 10:09:54 +0100
       
       merge jsonrpc gui and daemon
       
       Diffstat:
         M electrum                            |     151 ++++---------------------------
         D gui/jsonrpc.py                      |      72 -------------------------------
         A lib/daemon.py                       |     158 +++++++++++++++++++++++++++++++
         M lib/wallet.py                       |       4 +++-
       
       4 files changed, 181 insertions(+), 204 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -81,6 +81,7 @@ from electrum import SimpleConfig, Network, Wallet, WalletStorage
        from electrum.util import print_msg, print_error, print_stderr, json_encode, json_decode, set_verbosity, InvalidPassword
        from electrum.plugins import Plugins, run_hook, always_hook
        from electrum.commands import get_parser, known_commands, Commands, config_variables
       +from electrum.daemon import Daemon, get_daemon
        
        
        # get password routine
       t@@ -233,138 +234,20 @@ def init_cmdline(config):
            return cmd, password
        
        
       -def run_command(config, cmd, network, wallet, password):
       -    if wallet and network:
       -        wallet.wait_until_synchronized()
       +def run_offline_command(config, cmd, wallet, password):
            # arguments passed to function
            args = map(lambda x: config.get(x), cmd.params)
            # decode json arguments
            args = map(json_decode, args)
            # options
            args += map(lambda x: config.get(x), cmd.options)
       -    cmd_runner = Commands(config, wallet, network)
       +    cmd_runner = Commands(config, wallet, None)
            cmd_runner.password = password
            func = getattr(cmd_runner, cmd.name)
            result = func(*args)
            return result
        
        
       -class ClientThread(util.DaemonThread):
       -
       -    def __init__(self, server, s):
       -        util.DaemonThread.__init__(self)
       -        self.server = server
       -        self.client_pipe = util.SocketPipe(s)
       -        self.network = self.server.network
       -
       -    def run(self):
       -        config_options = self.client_pipe.get()
       -        password = config_options.get('password')
       -        config = SimpleConfig(config_options)
       -        cmd = config.get('cmd')
       -        if cmd == 'gui':
       -            if self.server.gui:
       -                if hasattr(server.gui, 'new_window'):
       -                    path = config.get_wallet_path()
       -                    self.server.gui.new_window(path, config.get('url'))
       -                    response = "ok"
       -                else:
       -                    response = "error: current GUI does not support multiple windows"
       -            else:
       -                response = "Error: Electrum is running in daemon mode. Please stop the daemon first."
       -        elif cmd == 'daemon':
       -            sub = config.get('subcommand')
       -            assert sub in ['start', 'stop', 'status']
       -            if sub == 'start':
       -                response = "Daemon already running"
       -            elif sub == 'status':
       -                p = self.network.get_parameters()
       -                response = {
       -                    'path': self.network.config.path,
       -                    'server': p[0],
       -                    'blockchain_height': self.network.get_local_height(),
       -                    'server_height': self.network.get_server_height(),
       -                    'nodes': self.network.get_interfaces(),
       -                    'connected': self.network.is_connected(),
       -                    'auto_connect': p[4],
       -                    'wallets': self.server.wallets.keys(),
       -                }
       -            elif sub == 'stop':
       -                self.server.stop()
       -                response = "Daemon stopped"
       -        else:
       -            c = known_commands[cmd]
       -            wallet = self.server.load_wallet(config) if c.requires_wallet else None
       -            try:
       -                response = run_command(config, c, self.network, wallet, password)
       -            except BaseException as e:
       -                err = traceback.format_exc()
       -                response = {'error':err}
       -        # send response and exit
       -        self.client_pipe.send(response)
       -
       -
       -
       -
       -class NetworkServer(util.DaemonThread):
       -
       -    def __init__(self, config, network):
       -        util.DaemonThread.__init__(self)
       -        self.debug = False
       -        self.config = config
       -        self.pipe = util.QueuePipe()
       -        self.network = network
       -        self.lock = threading.RLock()
       -        # gui is None is we run as daemon
       -        self.gui = None
       -        self.wallets = {}
       -
       -    def load_wallet(self, config):
       -        path = config.get_wallet_path()
       -        if path in self.wallets:
       -            wallet = self.wallets[path]
       -        else:
       -            storage = WalletStorage(path)
       -            wallet = Wallet(storage)
       -            wallet.start_threads(self.network)
       -            self.wallets[path] = wallet
       -        return wallet
       -
       -    def run(self):
       -        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       -        s.bind(('', 0))
       -        lockfile = os.path.join(self.config.path, 'lock')
       -        with open(lockfile, 'w') as f:
       -            f.write("%d"%s.getsockname()[1])
       -        s.listen(5)
       -        s.settimeout(0.1)
       -        while self.is_running():
       -            try:
       -                connection, address = s.accept()
       -            except socket.timeout:
       -                continue
       -            client = ClientThread(self, connection)
       -            client.start()
       -        print_error("Daemon exiting")
       -
       -    def stop(self):
       -        for k, wallet in self.wallets.items():
       -            wallet.stop_threads()
       -        util.DaemonThread.stop(self)
       -
       -
       -def get_daemon(config):
       -    lockfile = os.path.join(config.path, 'lock')
       -    try:
       -        with open(lockfile) as f:
       -            num = int(f.read())
       -        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       -        s.connect(('', num))
       -        return s
       -    except:
       -        return False
       -
       -
        if __name__ == '__main__':
        
            # make sure that certificates are here
       t@@ -441,20 +324,23 @@ if __name__ == '__main__':
                        wallet = Wallet(storage)
                    else:
                        wallet = None
       -            result = run_command(config, cmd, None, wallet, password)
       +            result = run_offline_command(config, cmd, wallet, password)
                    print_msg(json_encode(result))
                    sys.exit(0)
                else:
                    config_options['password'] = password
        
       -    # check if daemon is running
       -    s = get_daemon(config)
       -    if s:
       -        p = util.SocketPipe(s)
       -        p.set_timeout(1000000)
       -        p.send(config_options)
       -        result = p.get()
       -        s.close()
       +    server = get_daemon(config)
       +
       +    # daemon is running
       +    if server is not None:
       +        cmdname = config_options.get('cmd')
       +        if cmdname == 'daemon':
       +            result = server.daemon(config_options)
       +        elif cmdname == 'gui':
       +            result = server.gui(config_options)
       +        else:
       +            result = server.run_cmdline(config_options)
                if type(result) in [str, unicode]:
                    print_msg(result)
                elif type(result) is dict and result.get('error'):
       t@@ -470,10 +356,13 @@ if __name__ == '__main__':
                    network.start()
                else:
                    network = None
       -        server = NetworkServer(config, network)
       +        server = Daemon(config, network)
                server.start()
                server.gui = init_gui(config, network, plugins)
                server.gui.main()
       +        server.stop()
       +        sys.exit(0)
       +
            elif cmd_name == 'daemon':
                subcommand = config.get('subcommand')
                if subcommand in ['status', 'stop']:
       t@@ -484,7 +373,7 @@ if __name__ == '__main__':
                    if p == 0:
                        network = Network(config, plugins)
                        network.start()
       -                server = NetworkServer(config, network)
       +                server = Daemon(config, network)
                        if config.get('websocket_server'):
                            from electrum import websockets
                            websockets.WebSocketServer(config, network).start()
   DIR diff --git a/gui/jsonrpc.py b/gui/jsonrpc.py
       t@@ -1,72 +0,0 @@
       -#!/usr/bin/env python
       -#
       -# Electrum - lightweight Bitcoin client
       -# Copyright (C) 2015 Thomas Voegtlin
       -#
       -# This program is free software: you can redistribute it and/or modify
       -# it under the terms of the GNU General Public License as published by
       -# the Free Software Foundation, either version 3 of the License, or
       -# (at your option) any later version.
       -#
       -# This program is distributed in the hope that it will be useful,
       -# but WITHOUT ANY WARRANTY; without even the implied warranty of
       -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
       -# GNU General Public License for more details.
       -#
       -# You should have received a copy of the GNU General Public License
       -# along with this program. If not, see <http://www.gnu.org/licenses/>.
       -
       -
       -"""
       -jsonrpc interface for webservers.
       -may be called from your php script.
       -"""
       -
       -import socket, os
       -from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer, SimpleJSONRPCRequestHandler
       -
       -from electrum.wallet import WalletStorage, Wallet
       -from electrum.commands import known_commands, Commands
       -
       -
       -class RequestHandler(SimpleJSONRPCRequestHandler):
       -
       -    def do_OPTIONS(self):
       -        self.send_response(200)
       -        self.end_headers()
       -
       -    def end_headers(self):
       -        self.send_header("Access-Control-Allow-Headers",
       -                         "Origin, X-Requested-With, Content-Type, Accept")
       -        self.send_header("Access-Control-Allow-Origin", "*")
       -        SimpleJSONRPCRequestHandler.end_headers(self)
       -
       -
       -
       -class ElectrumGui:
       -
       -    def __init__(self, config, network, plugins):
       -        self.network = network
       -        self.config = config
       -        storage = WalletStorage(self.config.get_wallet_path())
       -        if not storage.file_exists:
       -            raise BaseException("Wallet not found")
       -        self.wallet = Wallet(storage)
       -        self.cmd_runner = Commands(self.config, self.wallet, self.network)
       -        host = config.get('rpchost', 'localhost')
       -        port = config.get('rpcport', 7777)
       -        self.server = SimpleJSONRPCServer((host, port), requestHandler=RequestHandler)
       -        self.server.socket.settimeout(1)
       -        for cmdname in known_commands:
       -            self.server.register_function(getattr(self.cmd_runner, cmdname), cmdname)
       -
       -    def main(self):
       -        self.wallet.start_threads(self.network)
       -        while True:
       -            try:
       -                self.server.handle_request()
       -            except socket.timeout:
       -                continue
       -            except:
       -                break
       -        self.wallet.stop_threads()
   DIR diff --git a/lib/daemon.py b/lib/daemon.py
       t@@ -0,0 +1,158 @@
       +#!/usr/bin/env python
       +#
       +# Electrum - lightweight Bitcoin client
       +# Copyright (C) 2015 Thomas Voegtlin
       +#
       +# This program is free software: you can redistribute it and/or modify
       +# it under the terms of the GNU General Public License as published by
       +# the Free Software Foundation, either version 3 of the License, or
       +# (at your option) any later version.
       +#
       +# This program is distributed in the hope that it will be useful,
       +# but WITHOUT ANY WARRANTY; without even the implied warranty of
       +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
       +# GNU General Public License for more details.
       +#
       +# You should have received a copy of the GNU General Public License
       +# along with this program. If not, see <http://www.gnu.org/licenses/>.
       +
       +import socket, os
       +import jsonrpclib
       +from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer, SimpleJSONRPCRequestHandler
       +
       +import util
       +from util import print_msg, print_error, print_stderr, json_encode, json_decode, set_verbosity, InvalidPassword
       +from wallet import WalletStorage, Wallet
       +from commands import known_commands, Commands
       +from simple_config import SimpleConfig
       +from network import Network
       +
       +
       +def get_daemon(config):
       +    host = config.get('rpchost', 'localhost')
       +    port = config.get('rpcport', 7777)
       +    server = jsonrpclib.Server('http://%s:%d' % (host, port))
       +    # check if daemon is running
       +    try:
       +        server.ping()
       +        return server
       +    except:
       +        pass
       +
       +
       +class RequestHandler(SimpleJSONRPCRequestHandler):
       +
       +    def do_OPTIONS(self):
       +        self.send_response(200)
       +        self.end_headers()
       +
       +    def end_headers(self):
       +        self.send_header("Access-Control-Allow-Headers",
       +                         "Origin, X-Requested-With, Content-Type, Accept")
       +        self.send_header("Access-Control-Allow-Origin", "*")
       +        SimpleJSONRPCRequestHandler.end_headers(self)
       +
       +
       +
       +class Daemon(util.DaemonThread):
       +
       +    def __init__(self, config, network):
       +        util.DaemonThread.__init__(self)
       +        self.config = config
       +        self.network = network
       +        self.wallets = {}
       +        self.wallet = self.load_wallet(config)
       +        self.cmd_runner = Commands(self.config, self.wallet, self.network)
       +        host = config.get('rpchost', 'localhost')
       +        port = config.get('rpcport', 7777)
       +        self.server = SimpleJSONRPCServer((host, port), requestHandler=RequestHandler, logRequests=False)
       +        self.server.socket.settimeout(1)
       +        for cmdname in known_commands:
       +            self.server.register_function(getattr(self.cmd_runner, cmdname), cmdname)
       +        self.server.register_function(self.run_cmdline, 'run_cmdline')
       +        self.server.register_function(self.ping, 'ping')
       +        self.server.register_function(self.daemon, 'daemon')
       +        self.server.register_function(self.gui, 'gui')
       +
       +    def ping(self):
       +        return True
       +
       +    def daemon(self, config):
       +        sub = config.get('subcommand')
       +        assert sub in ['start', 'stop', 'status']
       +        if sub == 'start':
       +            response = "Daemon already running"
       +        elif sub == 'status':
       +            p = self.network.get_parameters()
       +            response = {
       +                'path': self.network.config.path,
       +                'server': p[0],
       +                'blockchain_height': self.network.get_local_height(),
       +                'server_height': self.network.get_server_height(),
       +                'nodes': self.network.get_interfaces(),
       +                'connected': self.network.is_connected(),
       +                'auto_connect': p[4],
       +                'wallets': self.wallets.keys(),
       +            }
       +        elif sub == 'stop':
       +            self.stop()
       +            response = "Daemon stopped"
       +        return response
       +
       +    def gui(self, config_options):
       +        config = SimpleConfig(config_options)
       +        if self.gui:
       +            if hasattr(self.gui, 'new_window'):
       +                path = config.get_wallet_path()
       +                self.gui.new_window(path, config.get('url'))
       +                response = "ok"
       +            else:
       +                response = "error: current GUI does not support multiple windows"
       +        else:
       +            response = "Error: Electrum is running in daemon mode. Please stop the daemon first."
       +        return response
       +
       +    def load_wallet(self, config):
       +        path = config.get_wallet_path()
       +        if path in self.wallets:
       +            wallet = self.wallets[path]
       +        else:
       +            storage = WalletStorage(path)
       +            wallet = Wallet(storage)
       +            wallet.start_threads(self.network)
       +            self.wallets[path] = wallet
       +        return wallet
       +
       +    def run_cmdline(self, config_options):
       +        password = config_options.get('password')
       +        config = SimpleConfig(config_options)
       +        cmdname = config.get('cmd')
       +        cmd = known_commands[cmdname]
       +        wallet = self.load_wallet(config) if cmd.requires_wallet else None
       +        if wallet:
       +            wallet.wait_until_synchronized()
       +        # arguments passed to function
       +        args = map(lambda x: config.get(x), cmd.params)
       +        # decode json arguments
       +        args = map(json_decode, args)
       +        # options
       +        args += map(lambda x: config.get(x), cmd.options)
       +        cmd_runner = Commands(config, wallet, self.network)
       +        cmd_runner.password = password
       +        func = getattr(cmd_runner, cmd.name)
       +        result = func(*args)
       +        return result
       +
       +    def run(self):
       +        while self.is_running():
       +            try:
       +                self.server.handle_request()
       +            except socket.timeout:
       +                continue
       +            except:
       +                break
       +
       +    def stop(self):
       +        for k, wallet in self.wallets.items():
       +            wallet.stop_threads()
       +        util.DaemonThread.stop(self)
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -118,7 +118,9 @@ class WalletStorage(PrintError):
                        self.write()
        
            def write(self):
       -        assert not threading.currentThread().isDaemon()
       +        if threading.currentThread().isDaemon():
       +            self.print_error('warning: daemon thread cannot write wallet')
       +            return
                if not self.modified:
                    return
                s = json.dumps(self.data, indent=4, sort_keys=True)