URI: 
       tcommands/wallet: separate out 'create' and 'restore' core parts - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit ae80f143e7b82914c2f830d369df99e700bb1ccc
   DIR parent b2128af9585978ade8eab9822665c7cf07167f3d
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Thu, 28 Feb 2019 20:12:24 +0100
       
       commands/wallet: separate out 'create' and 'restore' core parts
       
       so that they are easier to use from python scripts
       
       Diffstat:
         M electrum/commands.py                |      87 ++++++++-----------------------
         M electrum/wallet.py                  |      79 ++++++++++++++++++++++++++++++-
       
       2 files changed, 99 insertions(+), 67 deletions(-)
       ---
   DIR diff --git a/electrum/commands.py b/electrum/commands.py
       t@@ -46,7 +46,7 @@ from .paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED
        from .synchronizer import Notifier
        from .storage import WalletStorage
        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 .mnemonic import Mnemonic
        
       t@@ -139,22 +139,16 @@ class Commands:
            @command('')
            def create(self, passphrase=None, password=None, encrypt_file=True, segwit=False):
                """Create a new wallet"""
       -        storage = WalletStorage(self.config.get_wallet_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, 'path': wallet.storage.path, 'msg': msg}
       +        d = create_new_wallet(path=self.config.get_wallet_path(),
       +                              passphrase=passphrase,
       +                              password=password,
       +                              encrypt_file=encrypt_file,
       +                              segwit=segwit)
       +        return {
       +            'seed': d['seed'],
       +            'path': d['wallet'].storage.path,
       +            'msg': d['msg'],
       +        }
        
            @command('')
            def restore(self, text, passphrase=None, password=None, encrypt_file=True):
       t@@ -162,55 +156,16 @@ class Commands:
                public key, a master private key, a list of bitcoin addresses
                or bitcoin private keys. If you want to be prompted for your
                seed, type '?' or ':' (concealed) """
       -        storage = WalletStorage(self.config.get_wallet_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 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}
       +        d = restore_wallet_from_text(text,
       +                                     path=self.config.get_wallet_path(),
       +                                     passphrase=passphrase,
       +                                     password=password,
       +                                     encrypt_file=encrypt_file,
       +                                     network=self.network)
       +        return {
       +            'path': d['wallet'].storage.path,
       +            'msg': d['msg'],
       +        }
        
            @command('wp')
            def password(self, password=None, new_password=None):
   DIR diff --git a/electrum/wallet.py b/electrum/wallet.py
       t@@ -45,10 +45,11 @@ from .util import (NotEnoughFunds, PrintError, UserCancelled, profiler,
                           format_satoshis, format_fee_satoshis, NoDynamicFeeEstimates,
                           WalletFileException, BitcoinException,
                           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,
                              is_minikey, relayfee, dust_threshold)
        from .crypto import sha256d
       +from . import keystore
        from .keystore import load_keystore, Hardware_KeyStore
        from .util import multisig_type
        from .storage import STO_EV_PLAINTEXT, STO_EV_USER_PW, STO_EV_XPUB_PW, WalletStorage
       t@@ -62,6 +63,7 @@ from .paymentrequest import (PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED,
        from .contacts import Contacts
        from .interface import RequestTimedOut
        from .ecc_fast import is_using_fast_ecc
       +from .mnemonic import Mnemonic
        
        if TYPE_CHECKING:
            from .network import Network
       t@@ -1848,3 +1850,78 @@ class Wallet(object):
                if wallet_type in wallet_constructors:
                    return wallet_constructors[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}