URI: 
       tTurn daemon subcommands into RPCs - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 9cfeadea706dbbb46b1a4ab7b38ec11a4f527669
   DIR parent 4397767a5e1686da719dc1dcc04572f4bdbbfc70
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Mon, 19 Aug 2019 12:46:31 +0200
       
       Turn daemon subcommands into RPCs
       
       Diffstat:
         M electrum/commands.py                |      61 ++++++++++++++++++++++++++++++-
         M electrum/daemon.py                  |      47 +++----------------------------
         M electrum/tests/regtest/regtest.sh   |      77 ++++++++++++++++---------------
         M run_electrum                        |       8 +++-----
       
       4 files changed, 106 insertions(+), 87 deletions(-)
       ---
   DIR diff --git a/electrum/commands.py b/electrum/commands.py
       t@@ -50,6 +50,9 @@ from .address_synchronizer import TX_HEIGHT_LOCAL
        from .mnemonic import Mnemonic
        from .lnutil import SENT, RECEIVED
        from .lnpeer import channel_id_from_funding_tx
       +from .plugin import run_hook
       +from .version import ELECTRUM_VERSION
       +
        
        if TYPE_CHECKING:
            from .network import Network
       t@@ -105,10 +108,12 @@ def command(s):
        
        class Commands:
        
       -    def __init__(self, config: 'SimpleConfig', wallet: Abstract_Wallet,
       -                 network: Optional['Network'], callback=None):
       +    def __init__(self, config: 'SimpleConfig',
       +                 wallet: Abstract_Wallet,
       +                 network: Optional['Network'], daemon=None, callback=None):
                self.config = config
                self.wallet = wallet
       +        self.daemon = daemon
                self.network = network
                self._callback = callback
                self.lnworker = self.wallet.lnworker if self.wallet else None
       t@@ -140,6 +145,58 @@ class Commands:
                """List of commands"""
                return ' '.join(sorted(known_commands.keys()))
        
       +    @command('n')
       +    async def getinfo(self):
       +        """ network info """
       +        net_params = self.network.get_parameters()
       +        response = {
       +            'path': self.network.config.path,
       +            'server': net_params.host,
       +            'blockchain_height': self.network.get_local_height(),
       +            'server_height': self.network.get_server_height(),
       +            'spv_nodes': len(self.network.get_interfaces()),
       +            'connected': self.network.is_connected(),
       +            'auto_connect': net_params.auto_connect,
       +            'version': ELECTRUM_VERSION,
       +            'default_wallet': self.config.get_wallet_path(),
       +            'fee_per_kb': self.config.fee_per_kb(),
       +        }
       +        return response
       +
       +    @command('n')
       +    async def stop(self):
       +        """Stop daemon"""
       +        self.daemon.stop()
       +        return "Daemon stopped"
       +
       +    @command('n')
       +    async def list_wallets(self):
       +        """List wallets open in daemon"""
       +        return [{'path':k, 'synchronized':w.is_up_to_date()} for k, w in self.daemon.wallets.items()]
       +
       +    @command('n')
       +    async def load_wallet(self):
       +        """Open wallet in daemon"""
       +        path = self.config.get_wallet_path()
       +        wallet = self.daemon.load_wallet(path, self.config.get('password'))
       +        if wallet is not None:
       +            self.wallet = wallet
       +            run_hook('load_wallet', wallet, None)
       +        response = wallet is not None
       +        return response
       +
       +    @command('n')
       +    async def close_wallet(self):
       +        """Close wallet"""
       +        path = self.config.get_wallet_path()
       +        path = standardize_path(path)
       +        if path in self.wallets:
       +            self.stop_wallet(path)
       +            response = True
       +        else:
       +            response = False
       +        return response
       +
            @command('')
            async def create(self, passphrase=None, password=None, encrypt_file=True, seed_type=None):
                """Create a new wallet.
   DIR diff --git a/electrum/daemon.py b/electrum/daemon.py
       t@@ -38,7 +38,6 @@ import jsonrpcclient
        import jsonrpcserver
        from jsonrpcclient.clients.aiohttp_client import AiohttpClient
        
       -from .version import ELECTRUM_VERSION
        from .network import Network
        from .util import (json_decode, to_bytes, to_string, profiler, standardize_path, constant_time_compare)
        from .wallet import Wallet, Abstract_Wallet
       t@@ -46,7 +45,6 @@ from .storage import WalletStorage
        from .commands import known_commands, Commands
        from .simple_config import SimpleConfig
        from .exchange_rate import FxThread
       -from .plugin import run_hook
        from .logging import get_logger, Logger
        
        
       t@@ -243,7 +241,7 @@ class Daemon(Logger):
                self.methods.add(self.ping)
                self.methods.add(self.gui)
                self.methods.add(self.daemon)
       -        self.cmd_runner = Commands(self.config, None, self.network)
       +        self.cmd_runner = Commands(self.config, None, self.network, self)
                for cmdname in known_commands:
                    self.methods.add(getattr(self.cmd_runner, cmdname))
                self.methods.add(self.run_cmdline)
       t@@ -263,46 +261,9 @@ class Daemon(Logger):
            async def daemon(self, config_options):
                config = SimpleConfig(config_options)
                sub = config.get('subcommand')
       -        assert sub in [None, 'start', 'stop', 'status', 'load_wallet', 'close_wallet']
       +        assert sub in [None, 'start', 'stop']
                if sub in [None, 'start']:
                    response = "Daemon already running"
       -        elif sub == 'load_wallet':
       -            path = config.get_wallet_path()
       -            wallet = self.load_wallet(path, config.get('password'))
       -            if wallet is not None:
       -                self.cmd_runner.wallet = wallet
       -                run_hook('load_wallet', wallet, None)
       -            response = wallet is not None
       -        elif sub == 'close_wallet':
       -            path = config.get_wallet_path()
       -            path = standardize_path(path)
       -            if path in self.wallets:
       -                self.stop_wallet(path)
       -                response = True
       -            else:
       -                response = False
       -        elif sub == 'status':
       -            if self.network:
       -                net_params = self.network.get_parameters()
       -                current_wallet = self.cmd_runner.wallet
       -                current_wallet_path = current_wallet.storage.path \
       -                                      if current_wallet else None
       -                response = {
       -                    'path': self.network.config.path,
       -                    'server': net_params.host,
       -                    'blockchain_height': self.network.get_local_height(),
       -                    'server_height': self.network.get_server_height(),
       -                    'spv_nodes': len(self.network.get_interfaces()),
       -                    'connected': self.network.is_connected(),
       -                    'auto_connect': net_params.auto_connect,
       -                    'version': ELECTRUM_VERSION,
       -                    'wallets': {k: w.is_up_to_date()
       -                                for k, w in self.wallets.items()},
       -                    'current_wallet': current_wallet_path,
       -                    'fee_per_kb': self.config.fee_per_kb(),
       -                }
       -            else:
       -                response = "Daemon offline"
                elif sub == 'stop':
                    self.stop()
                    response = "Daemon stopped"
       t@@ -382,7 +343,7 @@ class Daemon(Logger):
                    path = standardize_path(path)
                    wallet = self.wallets.get(path)
                    if wallet is None:
       -                return {'error': 'Wallet "%s" is not loaded. Use "electrum daemon load_wallet"'%os.path.basename(path) }
       +                return {'error': 'Wallet "%s" is not loaded. Use "electrum load_wallet"'%os.path.basename(path) }
                else:
                    wallet = None
                # arguments passed to function
       t@@ -393,7 +354,7 @@ class Daemon(Logger):
                kwargs = {}
                for x in cmd.options:
                    kwargs[x] = (config_options.get(x) if x in ['password', 'new_password'] else config.get(x))
       -        cmd_runner = Commands(config, wallet, self.network)
       +        cmd_runner = Commands(config, wallet, self.network, self)
                func = getattr(cmd_runner, cmd.name)
                try:
                    result = await func(*args, **kwargs)
   DIR diff --git a/electrum/tests/regtest/regtest.sh b/electrum/tests/regtest/regtest.sh
       t@@ -43,8 +43,11 @@ if [[ $1 == "init" ]]; then
            $bob create > /dev/null
            $carol create > /dev/null
            $alice setconfig log_to_file True
       -    $bob setconfig log_to_file True
       +    $bob   setconfig log_to_file True
            $carol setconfig log_to_file True
       +    $alice setconfig server 127.0.0.1:51001:t
       +    $bob   setconfig server 127.0.0.1:51001:t
       +    $carol setconfig server 127.0.0.1:51001:t
            $bob setconfig lightning_listen localhost:9735
            $bob setconfig lightning_forward_payments true
            echo "funding alice and carol"
       t@@ -55,20 +58,20 @@ fi
        
        # start daemons. Bob is started first because he is listening
        if [[ $1 == "start" ]]; then
       -    $bob daemon -s 127.0.0.1:51001:t start
       -    $alice daemon -s 127.0.0.1:51001:t start
       -    $carol daemon -s 127.0.0.1:51001:t start
       +    $bob daemon start
       +    $alice daemon start
       +    $carol daemon start
            sleep 1 # time to accept commands
       -    $bob daemon load_wallet
       -    $alice daemon load_wallet
       -    $carol daemon load_wallet
       +    $bob load_wallet
       +    $alice load_wallet
       +    $carol load_wallet
            sleep 10 # give time to synchronize
        fi
        
        if [[ $1 == "stop" ]]; then
       -    $alice daemon stop || true
       -    $bob daemon stop || true
       -    $carol daemon stop || true
       +    $alice stop || true
       +    $bob stop || true
       +    $carol stop || true
        fi
        
        if [[ $1 == "open" ]]; then
       t@@ -129,10 +132,10 @@ if [[ $1 == "breach" ]]; then
        fi
        
        if [[ $1 == "redeem_htlcs" ]]; then
       -    $bob daemon stop
       -    ELECTRUM_DEBUG_LIGHTNING_SETTLE_DELAY=10 $bob daemon -s 127.0.0.1:51001:t start
       +    $bob stop
       +    ELECTRUM_DEBUG_LIGHTNING_SETTLE_DELAY=10 $bob daemon start
            sleep 1
       -    $bob daemon load_wallet
       +    $bob load_wallet
            sleep 1
            # alice opens channel
            bob_node=$($bob nodeid)
       t@@ -149,7 +152,7 @@ if [[ $1 == "redeem_htlcs" ]]; then
                exit 1
            fi
            # bob goes away
       -    $bob daemon stop
       +    $bob stop
            echo "alice balance before closing channel:" $($alice getbalance)
            balance_before=$($alice getbalance | jq '[.confirmed, .unconfirmed, .lightning] | to_entries | map(select(.value != null).value) | map(tonumber) | add ')
            # alice force closes the channel
       t@@ -177,10 +180,10 @@ fi
        
        
        if [[ $1 == "breach_with_unspent_htlc" ]]; then
       -    $bob daemon stop
       -    ELECTRUM_DEBUG_LIGHTNING_SETTLE_DELAY=3 $bob daemon -s 127.0.0.1:51001:t start
       +    $bob stop
       +    ELECTRUM_DEBUG_LIGHTNING_SETTLE_DELAY=3 $bob daemon start
            sleep 1
       -    $bob daemon load_wallet
       +    $bob load_wallet
            wait_until_funded
            echo "alice opens channel"
            bob_node=$($bob nodeid)
       t@@ -205,24 +208,24 @@ if [[ $1 == "breach_with_unspent_htlc" ]]; then
            echo $($bob getbalance)
            echo "alice breaches with old ctx"
            echo $ctx
       -    height1=$($bob daemon status | jq '.blockchain_height')
       +    height1=$($bob getinfo | jq '.blockchain_height')
            $bitcoin_cli sendrawtransaction $ctx
            new_blocks 1
            # wait until breach is confirmed
       -    while height2=$($bob daemon status | jq '.blockchain_height') && [ $(($height2 - $height1)) -ne 1 ]; do
       +    while height2=$($bob getinfo | jq '.blockchain_height') && [ $(($height2 - $height1)) -ne 1 ]; do
                echo "waiting for block"
                sleep 1
            done
            new_blocks 1
            # wait until next block is confirmed, so that htlc tx and redeem tx are confirmed too
       -    while height3=$($bob daemon status | jq '.blockchain_height') && [ $(($height3 - $height2)) -ne 1 ]; do
       +    while height3=$($bob getinfo | jq '.blockchain_height') && [ $(($height3 - $height2)) -ne 1 ]; do
                echo "waiting for block"
                sleep 1
            done
            # wait until wallet is synchronized
       -    while b=$($bob daemon status | jq '.wallets | ."/tmp/bob/regtest/wallets/default_wallet"') && [ "$b" != "true" ]; do
       -  echo "waiting for wallet sync $b"
       -  sleep 1
       +    while b=$($bob list_wallets | jq '.[0]|.synchronized') && [ "$b" != "true" ]; do
       +        echo "waiting for wallet sync: $b"
       +        sleep 1
            done
            echo $($bob getbalance)
            balance_after=$($bob getbalance | jq '[.confirmed, .unconfirmed] | to_entries | map(select(.value != null).value) | map(tonumber) | add ')
       t@@ -234,10 +237,10 @@ fi
        
        
        if [[ $1 == "breach_with_spent_htlc" ]]; then
       -    $bob daemon stop
       -    ELECTRUM_DEBUG_LIGHTNING_SETTLE_DELAY=3 $bob daemon -s 127.0.0.1:51001:t start
       +    $bob stop
       +    ELECTRUM_DEBUG_LIGHTNING_SETTLE_DELAY=3 $bob daemon start
            sleep 1
       -    $bob daemon load_wallet
       +    $bob load_wallet
            wait_until_funded
            echo "alice opens channel"
            bob_node=$($bob nodeid)
       t@@ -262,7 +265,7 @@ if [[ $1 == "breach_with_spent_htlc" ]]; then
            fi
            echo $($bob getbalance)
            echo "bob goes offline"
       -    $bob daemon stop
       +    $bob stop
            ctx_id=$($bitcoin_cli sendrawtransaction $ctx)
            echo "alice breaches with old ctx:" $ctx_id
            new_blocks 1
       t@@ -275,11 +278,11 @@ if [[ $1 == "breach_with_spent_htlc" ]]; then
            # (to_local needs to_self_delay blocks; htlc needs whatever we put in invoice)
            new_blocks 150
            echo "alice spends to_local and htlc outputs"
       -    $alice daemon stop
       +    $alice stop
            cp /tmp/alice/regtest/wallets/toxic_wallet /tmp/alice/regtest/wallets/default_wallet
       -    $alice daemon -s 127.0.0.1:51001:t start
       +    $alice daemon start
            sleep 1
       -    $alice daemon load_wallet
       +    $alice load_wallet
            # wait until alice has spent both ctx outputs
            while [[ $($bitcoin_cli gettxout $ctx_id 0) ]]; do
                echo "waiting until alice spends ctx outputs"
       t@@ -291,9 +294,9 @@ if [[ $1 == "breach_with_spent_htlc" ]]; then
            done
            new_blocks 1
            echo "bob comes back"
       -    $bob daemon -s 127.0.0.1:51001:t start
       +    $bob daemon start
            sleep 1
       -    $bob daemon load_wallet
       +    $bob load_wallet
            while [[ $($bitcoin_cli getmempoolinfo | jq '.size') != "1" ]]; do
                echo "waiting for bob's transaction"
                sleep 1
       t@@ -311,15 +314,15 @@ fi
        
        if [[ $1 == "watchtower" ]]; then
            # carol is a watchtower of alice
       -    $alice daemon stop
       -    $carol daemon stop
       +    $alice stop
       +    $carol stop
            $alice setconfig watchtower_url http://127.0.0.1:12345
            $carol setconfig watchtower_host 127.0.0.1
            $carol setconfig watchtower_port 12345
       -    $carol daemon -s 127.0.0.1:51001:t start
       -    $alice daemon -s 127.0.0.1:51001:t start
       +    $carol daemon start
       +    $alice daemon start
            sleep 1
       -    $alice daemon load_wallet
       +    $alice load_wallet
            echo "waiting until alice funded"
            wait_until_funded
            echo "alice opens channel"
   DIR diff --git a/run_electrum b/run_electrum
       t@@ -159,8 +159,9 @@ def init_cmdline(config_options, server):
                print_stderr("In particular, DO NOT use 'redeem private key' services proposed by third parties.")
        
            # commands needing password
       -    if (cmd.requires_wallet and storage.is_encrypted() and server is False)\
       -       or (cmd.requires_password and (storage.get('use_encryption') or storage.is_encrypted())):
       +    if  ( (cmd.requires_wallet and storage.is_encrypted() and server is False)\
       +       or (cmdname == 'load_wallet' and storage.is_encrypted())\
       +       or (cmd.requires_password and (storage.is_encrypted() or storage.get('use_encryption')))):
                if storage.is_encrypted_with_hw_device():
                    # this case is handled later in the control flow
                    password = None
       t@@ -392,9 +393,6 @@ if __name__ == '__main__':
        
            elif cmdname == 'daemon':
        
       -        if subcommand in ['load_wallet']:
       -            init_daemon(config_options)
       -
                if subcommand in [None, 'start']:
                    configure_logging(config)
                    fd = daemon.get_file_descriptor(config)