Browse Source

commands/wallet: separate out 'create' and 'restore' core parts

so that they are easier to use from python scripts
sqlite_db
SomberNight 6 years ago
parent
commit
ae80f143e7
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 87
      electrum/commands.py
  2. 79
      electrum/wallet.py

87
electrum/commands.py

@ -46,7 +46,7 @@ from .paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED
from .synchronizer import Notifier from .synchronizer import Notifier
from .storage import WalletStorage from .storage import WalletStorage
from . import keystore from . import keystore
from .wallet import Wallet, Imported_Wallet, Abstract_Wallet from .wallet import Wallet, Imported_Wallet, Abstract_Wallet, create_new_wallet, restore_wallet_from_text
from .address_synchronizer import TX_HEIGHT_LOCAL from .address_synchronizer import TX_HEIGHT_LOCAL
from .mnemonic import Mnemonic from .mnemonic import Mnemonic
@ -139,22 +139,16 @@ class Commands:
@command('') @command('')
def create(self, passphrase=None, password=None, encrypt_file=True, segwit=False): def create(self, passphrase=None, password=None, encrypt_file=True, segwit=False):
"""Create a new wallet""" """Create a new wallet"""
storage = WalletStorage(self.config.get_wallet_path()) d = create_new_wallet(path=self.config.get_wallet_path(),
if storage.file_exists(): passphrase=passphrase,
raise Exception("Remove the existing wallet first!") password=password,
encrypt_file=encrypt_file,
seed_type = 'segwit' if segwit else 'standard' segwit=segwit)
seed = Mnemonic('en').make_seed(seed_type) return {
k = keystore.from_seed(seed, passphrase) 'seed': d['seed'],
storage.put('keystore', k.dump()) 'path': d['wallet'].storage.path,
storage.put('wallet_type', 'standard') 'msg': d['msg'],
wallet = Wallet(storage) }
wallet.update_password(old_pw=None, new_pw=password, encrypt_storage=encrypt_file)
wallet.synchronize()
msg = "Please keep your seed in a safe place; if you lose it, you will not be able to restore your wallet."
wallet.storage.write()
return {'seed': seed, 'path': wallet.storage.path, 'msg': msg}
@command('') @command('')
def restore(self, text, passphrase=None, password=None, encrypt_file=True): def restore(self, text, passphrase=None, password=None, encrypt_file=True):
@ -162,55 +156,16 @@ class Commands:
public key, a master private key, a list of bitcoin addresses public key, a master private key, a list of bitcoin addresses
or bitcoin private keys. If you want to be prompted for your or bitcoin private keys. If you want to be prompted for your
seed, type '?' or ':' (concealed) """ seed, type '?' or ':' (concealed) """
storage = WalletStorage(self.config.get_wallet_path()) d = restore_wallet_from_text(text,
if storage.file_exists(): path=self.config.get_wallet_path(),
raise Exception("Remove the existing wallet first!") passphrase=passphrase,
password=password,
text = text.strip() encrypt_file=encrypt_file,
if keystore.is_address_list(text): network=self.network)
wallet = Imported_Wallet(storage) return {
addresses = text.split() 'path': d['wallet'].storage.path,
good_inputs, bad_inputs = wallet.import_addresses(addresses, write_to_disk=False) 'msg': d['msg'],
# FIXME tell user about bad_inputs }
if not good_inputs:
raise Exception("None of the given addresses can be imported")
elif keystore.is_private_key_list(text, allow_spaces_inside_key=False):
k = keystore.Imported_KeyStore({})
storage.put('keystore', k.dump())
wallet = Imported_Wallet(storage)
keys = keystore.get_private_keys(text)
good_inputs, bad_inputs = wallet.import_private_keys(keys, None, write_to_disk=False)
# FIXME tell user about bad_inputs
if not good_inputs:
raise Exception("None of the given privkeys can be imported")
else:
if keystore.is_seed(text):
k = keystore.from_seed(text, passphrase)
elif keystore.is_master_key(text):
k = keystore.from_master_key(text)
else:
raise Exception("Seed or key not recognized")
storage.put('keystore', k.dump())
storage.put('wallet_type', 'standard')
wallet = Wallet(storage)
assert not storage.file_exists(), "file was created too soon! plaintext keys might have been written to disk"
wallet.update_password(old_pw=None, new_pw=password, encrypt_storage=encrypt_file)
wallet.synchronize()
if self.network:
wallet.start_network(self.network)
print_error("Recovering wallet...")
wallet.wait_until_synchronized()
wallet.stop_threads()
# note: we don't wait for SPV
msg = "Recovery successful" if wallet.is_found() else "Found no history for this wallet"
else:
msg = ("This wallet was restored offline. It may contain more addresses than displayed. "
"Start a daemon (not offline) to sync history.")
wallet.storage.write()
return {'path': wallet.storage.path, 'msg': msg}
@command('wp') @command('wp')
def password(self, password=None, new_password=None): def password(self, password=None, new_password=None):

79
electrum/wallet.py

@ -45,10 +45,11 @@ from .util import (NotEnoughFunds, PrintError, UserCancelled, profiler,
format_satoshis, format_fee_satoshis, NoDynamicFeeEstimates, format_satoshis, format_fee_satoshis, NoDynamicFeeEstimates,
WalletFileException, BitcoinException, WalletFileException, BitcoinException,
InvalidPassword, format_time, timestamp_to_datetime, Satoshis, InvalidPassword, format_time, timestamp_to_datetime, Satoshis,
Fiat, bfh, bh2u, TxMinedInfo) Fiat, bfh, bh2u, TxMinedInfo, print_error)
from .bitcoin import (COIN, TYPE_ADDRESS, is_address, address_to_script, from .bitcoin import (COIN, TYPE_ADDRESS, is_address, address_to_script,
is_minikey, relayfee, dust_threshold) is_minikey, relayfee, dust_threshold)
from .crypto import sha256d from .crypto import sha256d
from . import keystore
from .keystore import load_keystore, Hardware_KeyStore from .keystore import load_keystore, Hardware_KeyStore
from .util import multisig_type from .util import multisig_type
from .storage import STO_EV_PLAINTEXT, STO_EV_USER_PW, STO_EV_XPUB_PW, WalletStorage from .storage import STO_EV_PLAINTEXT, STO_EV_USER_PW, STO_EV_XPUB_PW, WalletStorage
@ -62,6 +63,7 @@ from .paymentrequest import (PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED,
from .contacts import Contacts from .contacts import Contacts
from .interface import RequestTimedOut from .interface import RequestTimedOut
from .ecc_fast import is_using_fast_ecc from .ecc_fast import is_using_fast_ecc
from .mnemonic import Mnemonic
if TYPE_CHECKING: if TYPE_CHECKING:
from .network import Network from .network import Network
@ -1848,3 +1850,78 @@ class Wallet(object):
if wallet_type in wallet_constructors: if wallet_type in wallet_constructors:
return wallet_constructors[wallet_type] return wallet_constructors[wallet_type]
raise WalletFileException("Unknown wallet type: " + str(wallet_type)) raise WalletFileException("Unknown wallet type: " + str(wallet_type))
def create_new_wallet(*, path, passphrase=None, password=None, encrypt_file=True, segwit=True):
"""Create a new wallet"""
storage = WalletStorage(path)
if storage.file_exists():
raise Exception("Remove the existing wallet first!")
seed_type = 'segwit' if segwit else 'standard'
seed = Mnemonic('en').make_seed(seed_type)
k = keystore.from_seed(seed, passphrase)
storage.put('keystore', k.dump())
storage.put('wallet_type', 'standard')
wallet = Wallet(storage)
wallet.update_password(old_pw=None, new_pw=password, encrypt_storage=encrypt_file)
wallet.synchronize()
msg = "Please keep your seed in a safe place; if you lose it, you will not be able to restore your wallet."
wallet.storage.write()
return {'seed': seed, 'wallet': wallet, 'msg': msg}
def restore_wallet_from_text(text, *, path, network, passphrase=None, password=None, encrypt_file=True):
"""Restore a wallet from text. Text can be a seed phrase, a master
public key, a master private key, a list of bitcoin addresses
or bitcoin private keys."""
storage = WalletStorage(path)
if storage.file_exists():
raise Exception("Remove the existing wallet first!")
text = text.strip()
if keystore.is_address_list(text):
wallet = Imported_Wallet(storage)
addresses = text.split()
good_inputs, bad_inputs = wallet.import_addresses(addresses, write_to_disk=False)
# FIXME tell user about bad_inputs
if not good_inputs:
raise Exception("None of the given addresses can be imported")
elif keystore.is_private_key_list(text, allow_spaces_inside_key=False):
k = keystore.Imported_KeyStore({})
storage.put('keystore', k.dump())
wallet = Imported_Wallet(storage)
keys = keystore.get_private_keys(text)
good_inputs, bad_inputs = wallet.import_private_keys(keys, None, write_to_disk=False)
# FIXME tell user about bad_inputs
if not good_inputs:
raise Exception("None of the given privkeys can be imported")
else:
if keystore.is_seed(text):
k = keystore.from_seed(text, passphrase)
elif keystore.is_master_key(text):
k = keystore.from_master_key(text)
else:
raise Exception("Seed or key not recognized")
storage.put('keystore', k.dump())
storage.put('wallet_type', 'standard')
wallet = Wallet(storage)
assert not storage.file_exists(), "file was created too soon! plaintext keys might have been written to disk"
wallet.update_password(old_pw=None, new_pw=password, encrypt_storage=encrypt_file)
wallet.synchronize()
if network:
wallet.start_network(network)
print_error("Recovering wallet...")
wallet.wait_until_synchronized()
wallet.stop_threads()
# note: we don't wait for SPV
msg = "Recovery successful" if wallet.is_found() else "Found no history for this wallet"
else:
msg = ("This wallet was restored offline. It may contain more addresses than displayed. "
"Start a daemon (not offline) to sync history.")
wallet.storage.write()
return {'wallet': wallet, 'msg': msg}

Loading…
Cancel
Save