mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-01 01:35:20 +00:00
use separata class for old wallets; decide with WalletFactory
This commit is contained in:
parent
df4b47849d
commit
70bace9b1e
4 changed files with 185 additions and 151 deletions
52
electrum
52
electrum
|
@ -210,17 +210,6 @@ if __name__ == '__main__':
|
||||||
# instanciate wallet for command-line
|
# instanciate wallet for command-line
|
||||||
storage = WalletStorage(config)
|
storage = WalletStorage(config)
|
||||||
|
|
||||||
if cmd.requires_wallet:
|
|
||||||
wallet = Wallet(storage)
|
|
||||||
else:
|
|
||||||
wallet = None
|
|
||||||
|
|
||||||
if cmd.name not in ['create', 'restore'] and 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)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if cmd.name in ['create', 'restore']:
|
if cmd.name in ['create', 'restore']:
|
||||||
if storage.file_exists:
|
if storage.file_exists:
|
||||||
|
@ -236,35 +225,26 @@ if __name__ == '__main__':
|
||||||
if not config.get('server'):
|
if not config.get('server'):
|
||||||
config.set_key('server', pick_random_server())
|
config.set_key('server', pick_random_server())
|
||||||
|
|
||||||
fee = options.tx_fee if options.tx_fee else raw_input("fee (default:%s):" % (str(Decimal(wallet.fee)/100000000)))
|
#fee = options.tx_fee if options.tx_fee else raw_input("fee (default:%s):" % (str(Decimal(wallet.fee)/100000000)))
|
||||||
gap = options.gap_limit if options.gap_limit else raw_input("gap limit (default 5):")
|
#gap = options.gap_limit if options.gap_limit else raw_input("gap limit (default 5):")
|
||||||
|
#if fee:
|
||||||
if fee:
|
# wallet.set_fee(float(fee)*100000000)
|
||||||
wallet.set_fee(float(fee)*100000000)
|
#if gap:
|
||||||
if gap:
|
# wallet.change_gap_limit(int(gap))
|
||||||
wallet.change_gap_limit(int(gap))
|
|
||||||
|
|
||||||
if cmd.name == 'restore':
|
if cmd.name == 'restore':
|
||||||
import getpass
|
import getpass
|
||||||
seed = getpass.getpass(prompt="seed:", stream=None) if options.concealed else raw_input("seed:")
|
seed = getpass.getpass(prompt="seed:", stream=None) if options.concealed else raw_input("seed:")
|
||||||
try:
|
wallet = Wallet.from_seed(str(seed),storage)
|
||||||
seed.decode('hex')
|
if not wallet:
|
||||||
except Exception:
|
sys.exit("Error: Invalid seed")
|
||||||
print_error("Warning: Not hex, trying decode.")
|
|
||||||
seed = mnemonic_decode(seed.split(' '))
|
|
||||||
if not seed:
|
|
||||||
sys.exit("Error: No seed")
|
|
||||||
|
|
||||||
wallet.init_seed(str(seed))
|
|
||||||
wallet.save_seed(password)
|
wallet.save_seed(password)
|
||||||
if not options.offline:
|
if not options.offline:
|
||||||
network = Network(config)
|
network = Network(config)
|
||||||
network.start()
|
network.start()
|
||||||
wallet.start_threads(network)
|
wallet.start_threads(network)
|
||||||
|
|
||||||
print_msg("Recovering wallet...")
|
print_msg("Recovering wallet...")
|
||||||
wallet.restore(lambda x: x)
|
wallet.restore(lambda x: x)
|
||||||
|
|
||||||
if wallet.is_found():
|
if wallet.is_found():
|
||||||
print_msg("Recovery successful")
|
print_msg("Recovery successful")
|
||||||
else:
|
else:
|
||||||
|
@ -274,6 +254,7 @@ if __name__ == '__main__':
|
||||||
print_msg("Warning: This wallet was restored offline. It may contain more addresses than displayed.")
|
print_msg("Warning: This wallet was restored offline. It may contain more addresses than displayed.")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
wallet = Wallet(storage)
|
||||||
wallet.init_seed(None)
|
wallet.init_seed(None)
|
||||||
wallet.save_seed(password)
|
wallet.save_seed(password)
|
||||||
wallet.synchronize()
|
wallet.synchronize()
|
||||||
|
@ -285,6 +266,19 @@ if __name__ == '__main__':
|
||||||
# terminate
|
# terminate
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
if cmd.name not in ['create', 'restore'] and 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)
|
||||||
|
|
||||||
|
|
||||||
|
if cmd.requires_wallet:
|
||||||
|
wallet = Wallet(storage)
|
||||||
|
else:
|
||||||
|
wallet = None
|
||||||
|
|
||||||
|
|
||||||
# important warning
|
# important warning
|
||||||
if cmd.name in ['dumpprivkey', 'dumpprivkeys']:
|
if cmd.name in ['dumpprivkey', 'dumpprivkeys']:
|
||||||
print_msg("WARNING: ALL your private keys are secret.")
|
print_msg("WARNING: ALL your private keys are secret.")
|
||||||
|
|
|
@ -258,13 +258,13 @@ class InstallWizard(QDialog):
|
||||||
if not action:
|
if not action:
|
||||||
return
|
return
|
||||||
|
|
||||||
wallet = Wallet(self.storage)
|
#gap = self.config.get('gap_limit', 5)
|
||||||
gap = self.config.get('gap_limit', 5)
|
#if gap != 5:
|
||||||
if gap != 5:
|
# wallet.gap_limit = gap
|
||||||
wallet.gap_limit = gap
|
# wallet.storage.put('gap_limit', gap, True)
|
||||||
wallet.storage.put('gap_limit', gap, True)
|
|
||||||
|
|
||||||
if action == 'create':
|
if action == 'create':
|
||||||
|
wallet = Wallet(self.storage)
|
||||||
wallet.init_seed(None)
|
wallet.init_seed(None)
|
||||||
if not self.show_seed(wallet):
|
if not self.show_seed(wallet):
|
||||||
return
|
return
|
||||||
|
@ -276,23 +276,14 @@ class InstallWizard(QDialog):
|
||||||
wallet.synchronize() # generate first addresses offline
|
wallet.synchronize() # generate first addresses offline
|
||||||
self.waiting_dialog(create)
|
self.waiting_dialog(create)
|
||||||
|
|
||||||
|
|
||||||
elif action == 'restore':
|
elif action == 'restore':
|
||||||
seed = self.seed_dialog()
|
seed = self.seed_dialog()
|
||||||
if not seed:
|
if not seed:
|
||||||
return
|
return
|
||||||
try:
|
wallet = Wallet.from_seed(seed, self.storage)
|
||||||
wallet.init_seed(seed)
|
|
||||||
except Exception:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc(file=sys.stdout)
|
|
||||||
QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
|
|
||||||
return
|
|
||||||
|
|
||||||
ok, old_password, password = self.password_dialog(wallet)
|
ok, old_password, password = self.password_dialog(wallet)
|
||||||
wallet.save_seed(password)
|
wallet.save_seed(password)
|
||||||
|
|
||||||
|
|
||||||
elif action == 'watching':
|
elif action == 'watching':
|
||||||
mpk = self.mpk_dialog()
|
mpk = self.mpk_dialog()
|
||||||
if not mpk:
|
if not mpk:
|
||||||
|
|
211
lib/wallet.py
211
lib/wallet.py
|
@ -308,29 +308,6 @@ class Wallet:
|
||||||
self.seed = unicodedata.normalize('NFC', unicode(seed.strip()))
|
self.seed = unicodedata.normalize('NFC', unicode(seed.strip()))
|
||||||
return
|
return
|
||||||
|
|
||||||
# find out what kind of wallet we are
|
|
||||||
try:
|
|
||||||
seed.strip().decode('hex')
|
|
||||||
self.seed_version = 4
|
|
||||||
self.seed = str(seed)
|
|
||||||
return
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
words = seed.split()
|
|
||||||
try:
|
|
||||||
mnemonic.mn_decode(words)
|
|
||||||
uses_electrum_words = True
|
|
||||||
except Exception:
|
|
||||||
uses_electrum_words = False
|
|
||||||
|
|
||||||
if uses_electrum_words and len(words) != 13:
|
|
||||||
self.seed_version = 4
|
|
||||||
self.seed = mnemonic.mn_decode(words)
|
|
||||||
else:
|
|
||||||
#assert is_seed(seed)
|
|
||||||
self.seed_version = SEED_VERSION
|
|
||||||
self.seed = seed
|
|
||||||
|
|
||||||
|
|
||||||
def save_seed(self, password):
|
def save_seed(self, password):
|
||||||
|
@ -343,18 +320,8 @@ class Wallet:
|
||||||
self.create_accounts(password)
|
self.create_accounts(password)
|
||||||
|
|
||||||
|
|
||||||
def create_watching_only_wallet(self, params):
|
def create_watching_only_wallet(self, K0, c0):
|
||||||
K0, c0 = params
|
cK0 = "" #FIXME
|
||||||
if not K0:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not c0:
|
|
||||||
self.seed_version = 4
|
|
||||||
self.storage.put('seed_version', self.seed_version, True)
|
|
||||||
self.create_old_account(K0)
|
|
||||||
return
|
|
||||||
|
|
||||||
cK0 = ""
|
|
||||||
self.master_public_keys = {
|
self.master_public_keys = {
|
||||||
"m/0'/": (c0, K0, cK0),
|
"m/0'/": (c0, K0, cK0),
|
||||||
}
|
}
|
||||||
|
@ -365,14 +332,9 @@ class Wallet:
|
||||||
|
|
||||||
def create_accounts(self, password):
|
def create_accounts(self, password):
|
||||||
seed = pw_decode(self.seed, password)
|
seed = pw_decode(self.seed, password)
|
||||||
|
# create default account
|
||||||
if self.seed_version == 4:
|
self.create_master_keys('1', password)
|
||||||
mpk = OldAccount.mpk_from_seed(seed)
|
self.create_account('1','Main account')
|
||||||
self.create_old_account(mpk)
|
|
||||||
else:
|
|
||||||
# create default account
|
|
||||||
self.create_master_keys('1', password)
|
|
||||||
self.create_account('1','Main account')
|
|
||||||
|
|
||||||
|
|
||||||
def create_master_keys(self, account_type, password):
|
def create_master_keys(self, account_type, password):
|
||||||
|
@ -532,12 +494,6 @@ class Wallet:
|
||||||
self.set_label(k, name)
|
self.set_label(k, name)
|
||||||
|
|
||||||
|
|
||||||
def create_old_account(self, mpk):
|
|
||||||
self.storage.put('master_public_key', mpk, True)
|
|
||||||
self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]})
|
|
||||||
self.save_accounts()
|
|
||||||
|
|
||||||
|
|
||||||
def save_accounts(self):
|
def save_accounts(self):
|
||||||
d = {}
|
d = {}
|
||||||
for k, v in self.accounts.items():
|
for k, v in self.accounts.items():
|
||||||
|
@ -602,11 +558,8 @@ class Wallet:
|
||||||
return s[0] == 1
|
return s[0] == 1
|
||||||
|
|
||||||
def get_master_public_key(self):
|
def get_master_public_key(self):
|
||||||
if self.seed_version == 4:
|
c, K, cK = self.storage.get("master_public_keys")["m/0'/"]
|
||||||
return self.storage.get("master_public_key")
|
return repr((c, K))
|
||||||
else:
|
|
||||||
c, K, cK = self.storage.get("master_public_keys")["m/0'/"]
|
|
||||||
return repr((c, K))
|
|
||||||
|
|
||||||
def get_master_private_key(self, account, password):
|
def get_master_private_key(self, account, password):
|
||||||
k = self.master_private_keys.get(account)
|
k = self.master_private_keys.get(account)
|
||||||
|
@ -685,21 +638,12 @@ class Wallet:
|
||||||
|
|
||||||
def get_seed(self, password):
|
def get_seed(self, password):
|
||||||
s = pw_decode(self.seed, password)
|
s = pw_decode(self.seed, password)
|
||||||
if self.seed_version == 4:
|
seed = mnemonic_to_seed(s,'').encode('hex')
|
||||||
seed = s
|
|
||||||
self.accounts[0].check_seed(seed)
|
|
||||||
else:
|
|
||||||
seed = mnemonic_to_seed(s,'').encode('hex')
|
|
||||||
return seed
|
return seed
|
||||||
|
|
||||||
|
|
||||||
def get_mnemonic(self, password):
|
def get_mnemonic(self, password):
|
||||||
import mnemonic
|
return pw_decode(self.seed, password)
|
||||||
s = pw_decode(self.seed, password)
|
|
||||||
if self.seed_version == 4:
|
|
||||||
return ' '.join(mnemonic.mn_encode(s))
|
|
||||||
else:
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
def get_private_key(self, address, password):
|
def get_private_key(self, address, password):
|
||||||
|
@ -752,23 +696,6 @@ class Wallet:
|
||||||
for txin in tx.inputs:
|
for txin in tx.inputs:
|
||||||
keyid = txin.get('KeyID')
|
keyid = txin.get('KeyID')
|
||||||
if keyid:
|
if keyid:
|
||||||
|
|
||||||
if self.seed_version == 4:
|
|
||||||
m = re.match("old\(([0-9a-f]+),(\d+),(\d+)", keyid)
|
|
||||||
if not m: continue
|
|
||||||
mpk = m.group(1)
|
|
||||||
if mpk != self.storage.get('master_public_key'): continue
|
|
||||||
for_change = int(m.group(2))
|
|
||||||
num = int(m.group(3))
|
|
||||||
account = self.accounts[0]
|
|
||||||
addr = account.get_address(for_change, num)
|
|
||||||
txin['address'] = addr # fixme: side effect
|
|
||||||
pk = account.get_private_key(seed, (for_change, num))
|
|
||||||
pubkey = public_key_from_private_key(pk)
|
|
||||||
keypairs[pubkey] = pk
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
|
||||||
roots = []
|
roots = []
|
||||||
for s in keyid.split('&'):
|
for s in keyid.split('&'):
|
||||||
m = re.match("bip32\(([0-9a-f]+),([0-9a-f]+),(/\d+/\d+/\d+)", s)
|
m = re.match("bip32\(([0-9a-f]+),([0-9a-f]+),(/\d+/\d+/\d+)", s)
|
||||||
|
@ -1056,29 +983,23 @@ class Wallet:
|
||||||
|
|
||||||
|
|
||||||
def get_account_name(self, k):
|
def get_account_name(self, k):
|
||||||
if k == 0:
|
default = "Unnamed account"
|
||||||
if self.seed_version == 4:
|
m = re.match("m/0'/(\d+)", k)
|
||||||
name = 'Main account'
|
if m:
|
||||||
|
num = m.group(1)
|
||||||
|
if num == '0':
|
||||||
|
default = "Main account"
|
||||||
else:
|
else:
|
||||||
name = 'Old account'
|
default = "Account %s"%num
|
||||||
else:
|
|
||||||
default = "Unnamed account"
|
|
||||||
m = re.match("m/0'/(\d+)", k)
|
|
||||||
if m:
|
|
||||||
num = m.group(1)
|
|
||||||
if num == '0':
|
|
||||||
default = "Main account"
|
|
||||||
else:
|
|
||||||
default = "Account %s"%num
|
|
||||||
|
|
||||||
m = re.match("m/1'/(\d+) & m/2'/(\d+)", k)
|
m = re.match("m/1'/(\d+) & m/2'/(\d+)", k)
|
||||||
if m:
|
if m:
|
||||||
num = m.group(1)
|
num = m.group(1)
|
||||||
default = "2of2 account %s"%num
|
default = "2of2 account %s"%num
|
||||||
name = self.labels.get(k, default)
|
name = self.labels.get(k, default)
|
||||||
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def get_account_names(self):
|
def get_account_names(self):
|
||||||
accounts = {}
|
accounts = {}
|
||||||
for k, account in self.accounts.items():
|
for k, account in self.accounts.items():
|
||||||
|
@ -1087,6 +1008,7 @@ class Wallet:
|
||||||
accounts[-1] = 'Imported keys'
|
accounts[-1] = 'Imported keys'
|
||||||
return accounts
|
return accounts
|
||||||
|
|
||||||
|
|
||||||
def get_account_addresses(self, a, include_change=True):
|
def get_account_addresses(self, a, include_change=True):
|
||||||
if a is None:
|
if a is None:
|
||||||
o = self.addresses(True)
|
o = self.addresses(True)
|
||||||
|
@ -1764,3 +1686,88 @@ class WalletSynchronizer(threading.Thread):
|
||||||
self.was_updated = False
|
self.was_updated = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class OldWallet(Wallet):
|
||||||
|
|
||||||
|
def init_seed(self, seed):
|
||||||
|
import mnemonic
|
||||||
|
|
||||||
|
if self.seed:
|
||||||
|
raise Exception("a seed exists")
|
||||||
|
|
||||||
|
if not seed:
|
||||||
|
raise
|
||||||
|
|
||||||
|
self.seed_version = 4
|
||||||
|
|
||||||
|
# see if seed was entered as hex
|
||||||
|
try:
|
||||||
|
seed.strip().decode('hex')
|
||||||
|
self.seed = str(seed)
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
words = seed.split()
|
||||||
|
try:
|
||||||
|
mnemonic.mn_decode(words)
|
||||||
|
except Exception:
|
||||||
|
raise
|
||||||
|
|
||||||
|
self.seed = mnemonic.mn_decode(words)
|
||||||
|
|
||||||
|
|
||||||
|
def get_master_public_key(self):
|
||||||
|
return self.storage.get("master_public_key")
|
||||||
|
|
||||||
|
def create_accounts(self, password):
|
||||||
|
seed = pw_decode(self.seed, password)
|
||||||
|
mpk = OldAccount.mpk_from_seed(seed)
|
||||||
|
self.create_account(mpk)
|
||||||
|
|
||||||
|
def create_account(self, mpk):
|
||||||
|
self.storage.put('master_public_key', mpk, True)
|
||||||
|
self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]})
|
||||||
|
self.save_accounts()
|
||||||
|
|
||||||
|
def create_watching_only_wallet(self, K0):
|
||||||
|
self.seed_version = 4
|
||||||
|
self.storage.put('seed_version', self.seed_version, True)
|
||||||
|
self.create_account(K0)
|
||||||
|
|
||||||
|
def get_seed(self, password):
|
||||||
|
seed = pw_decode(self.seed, password)
|
||||||
|
self.accounts[0].check_seed(seed)
|
||||||
|
return seed
|
||||||
|
|
||||||
|
def get_mnemonic(self, password):
|
||||||
|
import mnemonic
|
||||||
|
s = pw_decode(self.seed, password)
|
||||||
|
return ' '.join(mnemonic.mn_encode(s))
|
||||||
|
|
||||||
|
|
||||||
|
def add_keypairs_from_KeyID(self, tx, keypairs, password):
|
||||||
|
# first check the provided password
|
||||||
|
seed = self.get_seed(password)
|
||||||
|
for txin in tx.inputs:
|
||||||
|
keyid = txin.get('KeyID')
|
||||||
|
if keyid:
|
||||||
|
m = re.match("old\(([0-9a-f]+),(\d+),(\d+)", keyid)
|
||||||
|
if not m: continue
|
||||||
|
mpk = m.group(1)
|
||||||
|
if mpk != self.storage.get('master_public_key'): continue
|
||||||
|
for_change = int(m.group(2))
|
||||||
|
num = int(m.group(3))
|
||||||
|
account = self.accounts[0]
|
||||||
|
addr = account.get_address(for_change, num)
|
||||||
|
txin['address'] = addr # fixme: side effect
|
||||||
|
pk = account.get_private_key(seed, (for_change, num))
|
||||||
|
pubkey = public_key_from_private_key(pk)
|
||||||
|
keypairs[pubkey] = pk
|
||||||
|
|
||||||
|
|
||||||
|
def get_account_name(self, k):
|
||||||
|
assert k == 0
|
||||||
|
return 'Main account'
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,53 @@
|
||||||
|
from version import SEED_VERSION
|
||||||
|
from wallet import OldWallet, Wallet
|
||||||
|
|
||||||
class WalletFactory(object):
|
class WalletFactory(object):
|
||||||
def __new__(cls, config):
|
def __new__(self, storage):
|
||||||
if config.get('bitkey', False):
|
|
||||||
|
if storage.get('bitkey', False):
|
||||||
# if user requested support for Bitkey device,
|
# if user requested support for Bitkey device,
|
||||||
# import Bitkey driver
|
# import Bitkey driver
|
||||||
from wallet_bitkey import WalletBitkey
|
from wallet_bitkey import WalletBitkey
|
||||||
return WalletBitkey(config)
|
return WalletBitkey(config)
|
||||||
|
|
||||||
# Load standard wallet
|
seed_version = storage.get('seed_version', SEED_VERSION)
|
||||||
from wallet import Wallet
|
if seed_version not in [4, 6]:
|
||||||
return Wallet(config)
|
msg = "This wallet seed is not supported."
|
||||||
|
if seed_version in [5]:
|
||||||
|
msg += "\nTo open this wallet, try 'git checkout seed_v%d'"%seed_version
|
||||||
|
print msg
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if seed_version == 4:
|
||||||
|
return OldWallet(storage)
|
||||||
|
else:
|
||||||
|
return Wallet(storage)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_seed(self, seed, storage):
|
||||||
|
import mnemonic
|
||||||
|
words = seed.strip().split()
|
||||||
|
try:
|
||||||
|
mnemonic.mn_decode(words)
|
||||||
|
uses_electrum_words = True
|
||||||
|
except Exception:
|
||||||
|
uses_electrum_words = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
seed.decode('hex')
|
||||||
|
is_hex = True
|
||||||
|
except Exception:
|
||||||
|
is_hex = False
|
||||||
|
|
||||||
|
if is_hex or (uses_electrum_words and len(words) != 13):
|
||||||
|
print "old style wallet", len(words), words
|
||||||
|
w = OldWallet(storage)
|
||||||
|
w.init_seed(seed) #hex
|
||||||
|
else:
|
||||||
|
#assert is_seed(seed)
|
||||||
|
w = Wallet(storage)
|
||||||
|
w.init_seed(seed)
|
||||||
|
|
||||||
|
return w
|
||||||
|
|
Loading…
Add table
Reference in a new issue