mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
commands/wallet: separate out 'create' and 'restore' core parts
so that they are easier to use from python scripts
This commit is contained in:
parent
b2128af958
commit
ae80f143e7
2 changed files with 99 additions and 67 deletions
|
@ -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):
|
||||||
|
|
|
@ -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…
Add table
Reference in a new issue