Browse Source

Turn daemon subcommands into RPCs

dependabot/pip/contrib/deterministic-build/ecdsa-0.13.3
ThomasV 5 years ago
parent
commit
9cfeadea70
  1. 61
      electrum/commands.py
  2. 47
      electrum/daemon.py
  3. 77
      electrum/tests/regtest/regtest.sh
  4. 8
      run_electrum

61
electrum/commands.py

@ -50,6 +50,9 @@ from .address_synchronizer import TX_HEIGHT_LOCAL
from .mnemonic import Mnemonic from .mnemonic import Mnemonic
from .lnutil import SENT, RECEIVED from .lnutil import SENT, RECEIVED
from .lnpeer import channel_id_from_funding_tx from .lnpeer import channel_id_from_funding_tx
from .plugin import run_hook
from .version import ELECTRUM_VERSION
if TYPE_CHECKING: if TYPE_CHECKING:
from .network import Network from .network import Network
@ -105,10 +108,12 @@ def command(s):
class Commands: class Commands:
def __init__(self, config: 'SimpleConfig', wallet: Abstract_Wallet, def __init__(self, config: 'SimpleConfig',
network: Optional['Network'], callback=None): wallet: Abstract_Wallet,
network: Optional['Network'], daemon=None, callback=None):
self.config = config self.config = config
self.wallet = wallet self.wallet = wallet
self.daemon = daemon
self.network = network self.network = network
self._callback = callback self._callback = callback
self.lnworker = self.wallet.lnworker if self.wallet else None self.lnworker = self.wallet.lnworker if self.wallet else None
@ -140,6 +145,58 @@ class Commands:
"""List of commands""" """List of commands"""
return ' '.join(sorted(known_commands.keys())) 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('') @command('')
async def create(self, passphrase=None, password=None, encrypt_file=True, seed_type=None): async def create(self, passphrase=None, password=None, encrypt_file=True, seed_type=None):
"""Create a new wallet. """Create a new wallet.

47
electrum/daemon.py

@ -38,7 +38,6 @@ import jsonrpcclient
import jsonrpcserver import jsonrpcserver
from jsonrpcclient.clients.aiohttp_client import AiohttpClient from jsonrpcclient.clients.aiohttp_client import AiohttpClient
from .version import ELECTRUM_VERSION
from .network import Network from .network import Network
from .util import (json_decode, to_bytes, to_string, profiler, standardize_path, constant_time_compare) from .util import (json_decode, to_bytes, to_string, profiler, standardize_path, constant_time_compare)
from .wallet import Wallet, Abstract_Wallet from .wallet import Wallet, Abstract_Wallet
@ -46,7 +45,6 @@ from .storage import WalletStorage
from .commands import known_commands, Commands from .commands import known_commands, Commands
from .simple_config import SimpleConfig from .simple_config import SimpleConfig
from .exchange_rate import FxThread from .exchange_rate import FxThread
from .plugin import run_hook
from .logging import get_logger, Logger from .logging import get_logger, Logger
@ -243,7 +241,7 @@ class Daemon(Logger):
self.methods.add(self.ping) self.methods.add(self.ping)
self.methods.add(self.gui) self.methods.add(self.gui)
self.methods.add(self.daemon) 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: for cmdname in known_commands:
self.methods.add(getattr(self.cmd_runner, cmdname)) self.methods.add(getattr(self.cmd_runner, cmdname))
self.methods.add(self.run_cmdline) self.methods.add(self.run_cmdline)
@ -263,46 +261,9 @@ class Daemon(Logger):
async def daemon(self, config_options): async def daemon(self, config_options):
config = SimpleConfig(config_options) config = SimpleConfig(config_options)
sub = config.get('subcommand') 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']: if sub in [None, 'start']:
response = "Daemon already running" 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': elif sub == 'stop':
self.stop() self.stop()
response = "Daemon stopped" response = "Daemon stopped"
@ -382,7 +343,7 @@ class Daemon(Logger):
path = standardize_path(path) path = standardize_path(path)
wallet = self.wallets.get(path) wallet = self.wallets.get(path)
if wallet is None: 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: else:
wallet = None wallet = None
# arguments passed to function # arguments passed to function
@ -393,7 +354,7 @@ class Daemon(Logger):
kwargs = {} kwargs = {}
for x in cmd.options: for x in cmd.options:
kwargs[x] = (config_options.get(x) if x in ['password', 'new_password'] else config.get(x)) 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) func = getattr(cmd_runner, cmd.name)
try: try:
result = await func(*args, **kwargs) result = await func(*args, **kwargs)

77
electrum/tests/regtest/regtest.sh

@ -43,8 +43,11 @@ if [[ $1 == "init" ]]; then
$bob create > /dev/null $bob create > /dev/null
$carol create > /dev/null $carol create > /dev/null
$alice setconfig log_to_file True $alice setconfig log_to_file True
$bob setconfig log_to_file True $bob setconfig log_to_file True
$carol 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_listen localhost:9735
$bob setconfig lightning_forward_payments true $bob setconfig lightning_forward_payments true
echo "funding alice and carol" echo "funding alice and carol"
@ -55,20 +58,20 @@ fi
# start daemons. Bob is started first because he is listening # start daemons. Bob is started first because he is listening
if [[ $1 == "start" ]]; then if [[ $1 == "start" ]]; then
$bob daemon -s 127.0.0.1:51001:t start $bob daemon start
$alice daemon -s 127.0.0.1:51001:t start $alice daemon start
$carol daemon -s 127.0.0.1:51001:t start $carol daemon start
sleep 1 # time to accept commands sleep 1 # time to accept commands
$bob daemon load_wallet $bob load_wallet
$alice daemon load_wallet $alice load_wallet
$carol daemon load_wallet $carol load_wallet
sleep 10 # give time to synchronize sleep 10 # give time to synchronize
fi fi
if [[ $1 == "stop" ]]; then if [[ $1 == "stop" ]]; then
$alice daemon stop || true $alice stop || true
$bob daemon stop || true $bob stop || true
$carol daemon stop || true $carol stop || true
fi fi
if [[ $1 == "open" ]]; then if [[ $1 == "open" ]]; then
@ -129,10 +132,10 @@ if [[ $1 == "breach" ]]; then
fi fi
if [[ $1 == "redeem_htlcs" ]]; then if [[ $1 == "redeem_htlcs" ]]; then
$bob daemon stop $bob stop
ELECTRUM_DEBUG_LIGHTNING_SETTLE_DELAY=10 $bob daemon -s 127.0.0.1:51001:t start ELECTRUM_DEBUG_LIGHTNING_SETTLE_DELAY=10 $bob daemon start
sleep 1 sleep 1
$bob daemon load_wallet $bob load_wallet
sleep 1 sleep 1
# alice opens channel # alice opens channel
bob_node=$($bob nodeid) bob_node=$($bob nodeid)
@ -149,7 +152,7 @@ if [[ $1 == "redeem_htlcs" ]]; then
exit 1 exit 1
fi fi
# bob goes away # bob goes away
$bob daemon stop $bob stop
echo "alice balance before closing channel:" $($alice getbalance) 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 ') balance_before=$($alice getbalance | jq '[.confirmed, .unconfirmed, .lightning] | to_entries | map(select(.value != null).value) | map(tonumber) | add ')
# alice force closes the channel # alice force closes the channel
@ -177,10 +180,10 @@ fi
if [[ $1 == "breach_with_unspent_htlc" ]]; then if [[ $1 == "breach_with_unspent_htlc" ]]; then
$bob daemon stop $bob stop
ELECTRUM_DEBUG_LIGHTNING_SETTLE_DELAY=3 $bob daemon -s 127.0.0.1:51001:t start ELECTRUM_DEBUG_LIGHTNING_SETTLE_DELAY=3 $bob daemon start
sleep 1 sleep 1
$bob daemon load_wallet $bob load_wallet
wait_until_funded wait_until_funded
echo "alice opens channel" echo "alice opens channel"
bob_node=$($bob nodeid) bob_node=$($bob nodeid)
@ -205,24 +208,24 @@ if [[ $1 == "breach_with_unspent_htlc" ]]; then
echo $($bob getbalance) echo $($bob getbalance)
echo "alice breaches with old ctx" echo "alice breaches with old ctx"
echo $ctx echo $ctx
height1=$($bob daemon status | jq '.blockchain_height') height1=$($bob getinfo | jq '.blockchain_height')
$bitcoin_cli sendrawtransaction $ctx $bitcoin_cli sendrawtransaction $ctx
new_blocks 1 new_blocks 1
# wait until breach is confirmed # 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" echo "waiting for block"
sleep 1 sleep 1
done done
new_blocks 1 new_blocks 1
# wait until next block is confirmed, so that htlc tx and redeem tx are confirmed too # 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" echo "waiting for block"
sleep 1 sleep 1
done done
# wait until wallet is synchronized # wait until wallet is synchronized
while b=$($bob daemon status | jq '.wallets | ."/tmp/bob/regtest/wallets/default_wallet"') && [ "$b" != "true" ]; do while b=$($bob list_wallets | jq '.[0]|.synchronized') && [ "$b" != "true" ]; do
echo "waiting for wallet sync $b" echo "waiting for wallet sync: $b"
sleep 1 sleep 1
done done
echo $($bob getbalance) echo $($bob getbalance)
balance_after=$($bob getbalance | jq '[.confirmed, .unconfirmed] | to_entries | map(select(.value != null).value) | map(tonumber) | add ') balance_after=$($bob getbalance | jq '[.confirmed, .unconfirmed] | to_entries | map(select(.value != null).value) | map(tonumber) | add ')
@ -234,10 +237,10 @@ fi
if [[ $1 == "breach_with_spent_htlc" ]]; then if [[ $1 == "breach_with_spent_htlc" ]]; then
$bob daemon stop $bob stop
ELECTRUM_DEBUG_LIGHTNING_SETTLE_DELAY=3 $bob daemon -s 127.0.0.1:51001:t start ELECTRUM_DEBUG_LIGHTNING_SETTLE_DELAY=3 $bob daemon start
sleep 1 sleep 1
$bob daemon load_wallet $bob load_wallet
wait_until_funded wait_until_funded
echo "alice opens channel" echo "alice opens channel"
bob_node=$($bob nodeid) bob_node=$($bob nodeid)
@ -262,7 +265,7 @@ if [[ $1 == "breach_with_spent_htlc" ]]; then
fi fi
echo $($bob getbalance) echo $($bob getbalance)
echo "bob goes offline" echo "bob goes offline"
$bob daemon stop $bob stop
ctx_id=$($bitcoin_cli sendrawtransaction $ctx) ctx_id=$($bitcoin_cli sendrawtransaction $ctx)
echo "alice breaches with old ctx:" $ctx_id echo "alice breaches with old ctx:" $ctx_id
new_blocks 1 new_blocks 1
@ -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) # (to_local needs to_self_delay blocks; htlc needs whatever we put in invoice)
new_blocks 150 new_blocks 150
echo "alice spends to_local and htlc outputs" 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 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 sleep 1
$alice daemon load_wallet $alice load_wallet
# wait until alice has spent both ctx outputs # wait until alice has spent both ctx outputs
while [[ $($bitcoin_cli gettxout $ctx_id 0) ]]; do while [[ $($bitcoin_cli gettxout $ctx_id 0) ]]; do
echo "waiting until alice spends ctx outputs" echo "waiting until alice spends ctx outputs"
@ -291,9 +294,9 @@ if [[ $1 == "breach_with_spent_htlc" ]]; then
done done
new_blocks 1 new_blocks 1
echo "bob comes back" echo "bob comes back"
$bob daemon -s 127.0.0.1:51001:t start $bob daemon start
sleep 1 sleep 1
$bob daemon load_wallet $bob load_wallet
while [[ $($bitcoin_cli getmempoolinfo | jq '.size') != "1" ]]; do while [[ $($bitcoin_cli getmempoolinfo | jq '.size') != "1" ]]; do
echo "waiting for bob's transaction" echo "waiting for bob's transaction"
sleep 1 sleep 1
@ -311,15 +314,15 @@ fi
if [[ $1 == "watchtower" ]]; then if [[ $1 == "watchtower" ]]; then
# carol is a watchtower of alice # carol is a watchtower of alice
$alice daemon stop $alice stop
$carol daemon stop $carol stop
$alice setconfig watchtower_url http://127.0.0.1:12345 $alice setconfig watchtower_url http://127.0.0.1:12345
$carol setconfig watchtower_host 127.0.0.1 $carol setconfig watchtower_host 127.0.0.1
$carol setconfig watchtower_port 12345 $carol setconfig watchtower_port 12345
$carol daemon -s 127.0.0.1:51001:t start $carol daemon start
$alice daemon -s 127.0.0.1:51001:t start $alice daemon start
sleep 1 sleep 1
$alice daemon load_wallet $alice load_wallet
echo "waiting until alice funded" echo "waiting until alice funded"
wait_until_funded wait_until_funded
echo "alice opens channel" echo "alice opens channel"

8
run_electrum

@ -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.") print_stderr("In particular, DO NOT use 'redeem private key' services proposed by third parties.")
# commands needing password # commands needing password
if (cmd.requires_wallet and storage.is_encrypted() and server is False)\ 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())): 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(): if storage.is_encrypted_with_hw_device():
# this case is handled later in the control flow # this case is handled later in the control flow
password = None password = None
@ -392,9 +393,6 @@ if __name__ == '__main__':
elif cmdname == 'daemon': elif cmdname == 'daemon':
if subcommand in ['load_wallet']:
init_daemon(config_options)
if subcommand in [None, 'start']: if subcommand in [None, 'start']:
configure_logging(config) configure_logging(config)
fd = daemon.get_file_descriptor(config) fd = daemon.get_file_descriptor(config)

Loading…
Cancel
Save