separation between RPC and non-RPC commands.
This commit is contained in:
parent
62201b37f5
commit
acbe67fd1f
172
electrum
172
electrum
|
@ -107,37 +107,17 @@ def init_gui(config, network, plugins):
|
||||||
return gui
|
return gui
|
||||||
|
|
||||||
|
|
||||||
|
def run_non_RPC(config):
|
||||||
def init_cmdline(config):
|
|
||||||
|
|
||||||
cmdname = config.get('cmd')
|
cmdname = config.get('cmd')
|
||||||
cmd = known_commands[cmdname]
|
|
||||||
|
|
||||||
if cmdname == 'signtransaction' and config.get('privkey'):
|
|
||||||
cmd.requires_wallet = False
|
|
||||||
cmd.requires_password = False
|
|
||||||
|
|
||||||
if cmdname in ['payto', 'paytomany'] and config.get('unsigned'):
|
|
||||||
cmd.requires_password = False
|
|
||||||
|
|
||||||
if cmdname in ['payto', 'paytomany'] and config.get('broadcast'):
|
|
||||||
cmd.requires_network = True
|
|
||||||
|
|
||||||
if cmdname in ['createrawtx'] and config.get('unsigned'):
|
|
||||||
cmd.requires_password = False
|
|
||||||
cmd.requires_wallet = False
|
|
||||||
|
|
||||||
# instanciate wallet for command-line
|
|
||||||
storage = WalletStorage(config.get_wallet_path())
|
storage = WalletStorage(config.get_wallet_path())
|
||||||
|
|
||||||
if cmd.name in ['create', 'restore']:
|
|
||||||
if storage.file_exists:
|
if storage.file_exists:
|
||||||
sys.exit("Error: Remove the existing wallet first!")
|
sys.exit("Error: Remove the existing wallet first!")
|
||||||
|
|
||||||
def password_dialog():
|
def password_dialog():
|
||||||
return prompt_password("Password (hit return if you do not wish to encrypt your wallet):")
|
return prompt_password("Password (hit return if you do not wish to encrypt your wallet):")
|
||||||
|
|
||||||
if cmd.name == 'restore':
|
if cmdname == 'restore':
|
||||||
text = config.get('text')
|
text = config.get('text')
|
||||||
password = password_dialog() if Wallet.is_seed(text) or Wallet.is_xprv(text) or Wallet.is_private_key(text) else None
|
password = password_dialog() if Wallet.is_seed(text) or Wallet.is_xprv(text) or Wallet.is_private_key(text) else None
|
||||||
try:
|
try:
|
||||||
|
@ -156,7 +136,7 @@ def init_cmdline(config):
|
||||||
msg = "This wallet was restored offline. It may contain more addresses than displayed."
|
msg = "This wallet was restored offline. It may contain more addresses than displayed."
|
||||||
print_msg(msg)
|
print_msg(msg)
|
||||||
|
|
||||||
else:
|
elif cmdname == 'create':
|
||||||
password = password_dialog()
|
password = password_dialog()
|
||||||
wallet = Wallet(storage)
|
wallet = Wallet(storage)
|
||||||
seed = wallet.make_seed()
|
seed = wallet.make_seed()
|
||||||
|
@ -167,47 +147,7 @@ def init_cmdline(config):
|
||||||
print_msg("Your wallet generation seed is:\n\"%s\"" % seed)
|
print_msg("Your wallet generation seed is:\n\"%s\"" % seed)
|
||||||
print_msg("Please keep it in a safe place; if you lose it, you will not be able to restore your wallet.")
|
print_msg("Please keep it in a safe place; if you lose it, you will not be able to restore your wallet.")
|
||||||
|
|
||||||
wallet.storage.write()
|
elif cmdname == 'deseed':
|
||||||
print_msg("Wallet saved in '%s'" % wallet.storage.path)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if cmd.requires_wallet and not storage.file_exists:
|
|
||||||
print_msg("Error: Wallet file not found.")
|
|
||||||
print_msg("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# create wallet instance
|
|
||||||
wallet = Wallet(storage) if cmd.requires_wallet else None
|
|
||||||
|
|
||||||
# notify plugins
|
|
||||||
always_hook('cmdline_load_wallet', wallet)
|
|
||||||
|
|
||||||
# important warning
|
|
||||||
if cmd.name in ['getprivatekeys']:
|
|
||||||
print_stderr("WARNING: ALL your private keys are secret.")
|
|
||||||
print_stderr("Exposing a single private key can compromise your entire wallet!")
|
|
||||||
print_stderr("In particular, DO NOT use 'redeem private key' services proposed by third parties.")
|
|
||||||
|
|
||||||
# commands needing password
|
|
||||||
if cmd.requires_password and wallet.use_encryption:
|
|
||||||
if config.get('password'):
|
|
||||||
password = config.get('password')
|
|
||||||
else:
|
|
||||||
password = prompt_password('Password:', False)
|
|
||||||
if not password:
|
|
||||||
print_msg("Error: Password required")
|
|
||||||
sys.exit(1)
|
|
||||||
# check password
|
|
||||||
try:
|
|
||||||
seed = wallet.check_password(password)
|
|
||||||
except InvalidPassword:
|
|
||||||
print_msg("Error: This password does not decode this wallet.")
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
|
||||||
password = None
|
|
||||||
|
|
||||||
# run the command
|
|
||||||
if cmd.name == 'deseed':
|
|
||||||
if not wallet.seed:
|
if not wallet.seed:
|
||||||
print_msg("Error: This wallet has no seed")
|
print_msg("Error: This wallet has no seed")
|
||||||
else:
|
else:
|
||||||
|
@ -228,26 +168,95 @@ def init_cmdline(config):
|
||||||
wallet.storage.write()
|
wallet.storage.write()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
elif cmd.name == 'password':
|
|
||||||
new_password = prompt_password('New password:')
|
|
||||||
wallet.update_password(password, new_password)
|
|
||||||
wallet.storage.write()
|
wallet.storage.write()
|
||||||
|
print_msg("Wallet saved in '%s'" % wallet.storage.path)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
return cmd, password, wallet
|
|
||||||
|
def init_cmdline(config_options):
|
||||||
|
config = SimpleConfig(config_options)
|
||||||
|
cmdname = config.get('cmd')
|
||||||
|
cmd = known_commands[cmdname]
|
||||||
|
|
||||||
|
if cmdname == 'signtransaction' and config.get('privkey'):
|
||||||
|
cmd.requires_wallet = False
|
||||||
|
cmd.requires_password = False
|
||||||
|
|
||||||
|
if cmdname in ['payto', 'paytomany'] and config.get('unsigned'):
|
||||||
|
cmd.requires_password = False
|
||||||
|
|
||||||
|
if cmdname in ['payto', 'paytomany'] and config.get('broadcast'):
|
||||||
|
cmd.requires_network = True
|
||||||
|
|
||||||
|
if cmdname in ['createrawtx'] and config.get('unsigned'):
|
||||||
|
cmd.requires_password = False
|
||||||
|
cmd.requires_wallet = False
|
||||||
|
|
||||||
|
# instanciate wallet for command-line
|
||||||
|
storage = WalletStorage(config.get_wallet_path())
|
||||||
|
|
||||||
|
if cmd.requires_wallet and not storage.file_exists:
|
||||||
|
print_msg("Error: Wallet file not found.")
|
||||||
|
print_msg("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# important warning
|
||||||
|
if cmd.name in ['getprivatekeys']:
|
||||||
|
print_stderr("WARNING: ALL your private keys are secret.")
|
||||||
|
print_stderr("Exposing a single private key can compromise your entire wallet!")
|
||||||
|
print_stderr("In particular, DO NOT use 'redeem private key' services proposed by third parties.")
|
||||||
|
|
||||||
|
# commands needing password
|
||||||
|
if cmd.requires_password and storage.get('use_encryption'):
|
||||||
|
if config.get('password'):
|
||||||
|
password = config.get('password')
|
||||||
|
else:
|
||||||
|
password = prompt_password('Password:', False)
|
||||||
|
if not password:
|
||||||
|
print_msg("Error: Password required")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
password = None
|
||||||
|
|
||||||
|
config_options['password'] = password
|
||||||
|
|
||||||
|
if cmd.name == 'password':
|
||||||
|
new_password = prompt_password('New password:')
|
||||||
|
config_options['new_password'] = new_password
|
||||||
|
|
||||||
|
return cmd, password
|
||||||
|
|
||||||
|
|
||||||
def run_offline_command(config, cmd, wallet, password):
|
def run_offline_command(config, config_options):
|
||||||
|
cmdname = config.get('cmd')
|
||||||
|
cmd = known_commands[cmdname]
|
||||||
|
storage = WalletStorage(config.get_wallet_path())
|
||||||
|
wallet = Wallet(storage) if cmd.requires_wallet else None
|
||||||
|
# check password
|
||||||
|
if cmd.requires_password and storage.get('use_encryption'):
|
||||||
|
password = config_options.get('password')
|
||||||
|
try:
|
||||||
|
seed = wallet.check_password(password)
|
||||||
|
except InvalidPassword:
|
||||||
|
print_msg("Error: This password does not decode this wallet.")
|
||||||
|
sys.exit(1)
|
||||||
|
if cmd.requires_network:
|
||||||
|
print_stderr("Warning: running command offline")
|
||||||
|
# notify plugins
|
||||||
|
always_hook('cmdline_load_wallet', wallet)
|
||||||
# arguments passed to function
|
# arguments passed to function
|
||||||
args = map(lambda x: config.get(x), cmd.params)
|
args = map(lambda x: config.get(x), cmd.params)
|
||||||
# decode json arguments
|
# decode json arguments
|
||||||
args = map(json_decode, args)
|
args = map(json_decode, args)
|
||||||
# options
|
# options
|
||||||
args += map(lambda x: config.get(x), cmd.options)
|
args += map(lambda x: config.get(x), cmd.options)
|
||||||
cmd_runner = Commands(config, wallet, None)
|
cmd_runner = Commands(config, wallet, None,
|
||||||
cmd_runner.password = password
|
password=config_options.get('password'),
|
||||||
|
new_password=config_options.get('new_password'))
|
||||||
func = getattr(cmd_runner, cmd.name)
|
func = getattr(cmd_runner, cmd.name)
|
||||||
result = func(*args)
|
result = func(*args)
|
||||||
|
# save wallet
|
||||||
|
wallet.storage.write()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@ -317,19 +326,22 @@ if __name__ == '__main__':
|
||||||
gui_name = config.get('gui', 'qt') if cmd_name == 'gui' else 'cmdline'
|
gui_name = config.get('gui', 'qt') if cmd_name == 'gui' else 'cmdline'
|
||||||
plugins = Plugins(config, is_bundle or is_local or is_android, gui_name)
|
plugins = Plugins(config, is_bundle or is_local or is_android, gui_name)
|
||||||
|
|
||||||
# run command offline
|
# run non-RPC commands separately
|
||||||
if cmd_name not in ['gui', 'daemon']:
|
if cmd_name in ['create', 'restore', 'deseed']:
|
||||||
cmd, password, wallet = init_cmdline(config)
|
run_non_RPC(config)
|
||||||
if not (cmd.requires_network or cmd.requires_wallet) or config.get('offline'):
|
|
||||||
result = run_offline_command(config, cmd, wallet, password)
|
|
||||||
print_msg(json_encode(result))
|
|
||||||
wallet.storage.write()
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
|
||||||
config_options['password'] = password
|
|
||||||
|
|
||||||
|
# check if a daemon is running
|
||||||
server = get_daemon(config)
|
server = get_daemon(config)
|
||||||
|
|
||||||
|
# if no daemon is running, run the command offline
|
||||||
|
if cmd_name not in ['gui', 'daemon']:
|
||||||
|
init_cmdline(config_options)
|
||||||
|
if server is None: #not (cmd.requires_network or cmd.requires_wallet) or config.get('offline'):
|
||||||
|
result = run_offline_command(config, config_options)
|
||||||
|
print_msg(json_encode(result))
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
# daemon is running
|
# daemon is running
|
||||||
if server is not None:
|
if server is not None:
|
||||||
cmdname = config_options.get('cmd')
|
cmdname = config_options.get('cmd')
|
||||||
|
|
|
@ -74,21 +74,22 @@ def command(s):
|
||||||
|
|
||||||
class Commands:
|
class Commands:
|
||||||
|
|
||||||
def __init__(self, config, wallet, network, callback = None):
|
def __init__(self, config, wallet, network, callback = None, password=None, new_password=None):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.wallet = wallet
|
self.wallet = wallet
|
||||||
self.network = network
|
self.network = network
|
||||||
self._callback = callback
|
self._callback = callback
|
||||||
self.password = None
|
self._password = password
|
||||||
|
self.new_password = new_password
|
||||||
self.contacts = contacts.Contacts(self.config)
|
self.contacts = contacts.Contacts(self.config)
|
||||||
|
|
||||||
def _run(self, method, args, password_getter):
|
def _run(self, method, args, password_getter):
|
||||||
cmd = known_commands[method]
|
cmd = known_commands[method]
|
||||||
if cmd.requires_password and self.wallet.use_encryption:
|
if cmd.requires_password and self.wallet.use_encryption:
|
||||||
self.password = apply(password_getter,())
|
self._password = apply(password_getter,())
|
||||||
f = getattr(self, method)
|
f = getattr(self, method)
|
||||||
result = f(*args)
|
result = f(*args)
|
||||||
self.password = None
|
self._password = None
|
||||||
if self._callback:
|
if self._callback:
|
||||||
apply(self._callback, ())
|
apply(self._callback, ())
|
||||||
return result
|
return result
|
||||||
|
@ -120,7 +121,9 @@ class Commands:
|
||||||
@command('wp')
|
@command('wp')
|
||||||
def password(self):
|
def password(self):
|
||||||
"""Change wallet password. """
|
"""Change wallet password. """
|
||||||
raise BaseException('Not a JSON-RPC command')
|
self.wallet.update_password(self._password, self.new_password)
|
||||||
|
self.wallet.storage.write()
|
||||||
|
return {'password':self.wallet.use_encryption}
|
||||||
|
|
||||||
@command('')
|
@command('')
|
||||||
def getconfig(self, key):
|
def getconfig(self, key):
|
||||||
|
@ -200,7 +203,7 @@ class Commands:
|
||||||
outputs = map(lambda x: ('address', x[0], int(COIN*x[1])), outputs.items())
|
outputs = map(lambda x: ('address', x[0], int(COIN*x[1])), outputs.items())
|
||||||
tx = Transaction.from_io(tx_inputs, outputs)
|
tx = Transaction.from_io(tx_inputs, outputs)
|
||||||
if not unsigned:
|
if not unsigned:
|
||||||
self.wallet.sign_transaction(tx, self.password)
|
self.wallet.sign_transaction(tx, self._password)
|
||||||
return tx.as_dict()
|
return tx.as_dict()
|
||||||
|
|
||||||
@command('wp')
|
@command('wp')
|
||||||
|
@ -212,7 +215,7 @@ class Commands:
|
||||||
pubkey = bitcoin.public_key_from_private_key(privkey)
|
pubkey = bitcoin.public_key_from_private_key(privkey)
|
||||||
t.sign({pubkey:privkey})
|
t.sign({pubkey:privkey})
|
||||||
else:
|
else:
|
||||||
self.wallet.sign_transaction(t, self.password)
|
self.wallet.sign_transaction(t, self._password)
|
||||||
return t.as_dict()
|
return t.as_dict()
|
||||||
|
|
||||||
@command('')
|
@command('')
|
||||||
|
@ -250,7 +253,7 @@ class Commands:
|
||||||
"""Get private keys of addresses. You may pass a single wallet address, or a list of wallet addresses."""
|
"""Get private keys of addresses. You may pass a single wallet address, or a list of wallet addresses."""
|
||||||
is_list = type(address) is list
|
is_list = type(address) is list
|
||||||
domain = address if is_list else [address]
|
domain = address if is_list else [address]
|
||||||
out = [self.wallet.get_private_key(address, self.password) for address in domain]
|
out = [self.wallet.get_private_key(address, self._password) for address in domain]
|
||||||
return out if is_list else out[0]
|
return out if is_list else out[0]
|
||||||
|
|
||||||
@command('w')
|
@command('w')
|
||||||
|
@ -334,19 +337,19 @@ class Commands:
|
||||||
@command('wp')
|
@command('wp')
|
||||||
def getmasterprivate(self):
|
def getmasterprivate(self):
|
||||||
"""Get master private key. Return your wallet\'s master private key"""
|
"""Get master private key. Return your wallet\'s master private key"""
|
||||||
return str(self.wallet.get_master_private_key(self.wallet.root_name, self.password))
|
return str(self.wallet.get_master_private_key(self.wallet.root_name, self._password))
|
||||||
|
|
||||||
@command('wp')
|
@command('wp')
|
||||||
def getseed(self):
|
def getseed(self):
|
||||||
"""Get seed phrase. Print the generation seed of your wallet."""
|
"""Get seed phrase. Print the generation seed of your wallet."""
|
||||||
s = self.wallet.get_mnemonic(self.password)
|
s = self.wallet.get_mnemonic(self._password)
|
||||||
return s.encode('utf8')
|
return s.encode('utf8')
|
||||||
|
|
||||||
@command('wp')
|
@command('wp')
|
||||||
def importprivkey(self, privkey):
|
def importprivkey(self, privkey):
|
||||||
"""Import a private key. """
|
"""Import a private key. """
|
||||||
try:
|
try:
|
||||||
addr = self.wallet.import_key(privkey, self.password)
|
addr = self.wallet.import_key(privkey, self._password)
|
||||||
out = "Keypair imported: " + addr
|
out = "Keypair imported: " + addr
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
out = "Error: " + str(e)
|
out = "Error: " + str(e)
|
||||||
|
@ -377,7 +380,7 @@ class Commands:
|
||||||
def signmessage(self, address, message):
|
def signmessage(self, address, message):
|
||||||
"""Sign a message with a key. Use quotes if your message contains
|
"""Sign a message with a key. Use quotes if your message contains
|
||||||
whitespaces"""
|
whitespaces"""
|
||||||
sig = self.wallet.sign_message(address, message, self.password)
|
sig = self.wallet.sign_message(address, message, self._password)
|
||||||
return base64.b64encode(sig)
|
return base64.b64encode(sig)
|
||||||
|
|
||||||
@command('')
|
@command('')
|
||||||
|
@ -415,7 +418,7 @@ class Commands:
|
||||||
tx = self.wallet.make_unsigned_transaction(coins, final_outputs, self.config, fee, change_addr)
|
tx = self.wallet.make_unsigned_transaction(coins, final_outputs, self.config, fee, change_addr)
|
||||||
str(tx) #this serializes
|
str(tx) #this serializes
|
||||||
if not unsigned:
|
if not unsigned:
|
||||||
self.wallet.sign_transaction(tx, self.password)
|
self.wallet.sign_transaction(tx, self._password)
|
||||||
return tx
|
return tx
|
||||||
|
|
||||||
@command('wpn')
|
@command('wpn')
|
||||||
|
@ -522,7 +525,7 @@ class Commands:
|
||||||
@command('wp')
|
@command('wp')
|
||||||
def decrypt(self, pubkey, encrypted):
|
def decrypt(self, pubkey, encrypted):
|
||||||
"""Decrypt a message encrypted with a public key."""
|
"""Decrypt a message encrypted with a public key."""
|
||||||
return self.wallet.decrypt_message(pubkey, encrypted, self.password)
|
return self.wallet.decrypt_message(pubkey, encrypted, self._password)
|
||||||
|
|
||||||
def _format_request(self, out):
|
def _format_request(self, out):
|
||||||
pr_str = {
|
pr_str = {
|
||||||
|
@ -587,7 +590,7 @@ class Commands:
|
||||||
if not alias:
|
if not alias:
|
||||||
raise BaseException('No alias in your configuration')
|
raise BaseException('No alias in your configuration')
|
||||||
alias_addr = self.contacts.resolve(alias)['address']
|
alias_addr = self.contacts.resolve(alias)['address']
|
||||||
self.wallet.sign_payment_request(address, alias, alias_addr, self.password)
|
self.wallet.sign_payment_request(address, alias, alias_addr, self._password)
|
||||||
|
|
||||||
@command('w')
|
@command('w')
|
||||||
def rmrequest(self, address):
|
def rmrequest(self, address):
|
||||||
|
|
|
@ -135,7 +135,6 @@ class Daemon(DaemonThread):
|
||||||
return wallet
|
return wallet
|
||||||
|
|
||||||
def run_cmdline(self, config_options):
|
def run_cmdline(self, config_options):
|
||||||
password = config_options.get('password')
|
|
||||||
config = SimpleConfig(config_options)
|
config = SimpleConfig(config_options)
|
||||||
cmdname = config.get('cmd')
|
cmdname = config.get('cmd')
|
||||||
cmd = known_commands[cmdname]
|
cmd = known_commands[cmdname]
|
||||||
|
@ -146,8 +145,9 @@ class Daemon(DaemonThread):
|
||||||
args = map(json_decode, args)
|
args = map(json_decode, args)
|
||||||
# options
|
# options
|
||||||
args += map(lambda x: config.get(x), cmd.options)
|
args += map(lambda x: config.get(x), cmd.options)
|
||||||
cmd_runner = Commands(config, wallet, self.network)
|
cmd_runner = Commands(config, wallet, self.network,
|
||||||
cmd_runner.password = password
|
password=config_options.get('password'),
|
||||||
|
new_password=config_options.get('new_password'))
|
||||||
func = getattr(cmd_runner, cmd.name)
|
func = getattr(cmd_runner, cmd.name)
|
||||||
result = func(*args)
|
result = func(*args)
|
||||||
return result
|
return result
|
||||||
|
|
Loading…
Reference in New Issue