update bip32 accounts and wallet
This commit is contained in:
parent
ffc72e65df
commit
5a43b633d6
2
electrum
2
electrum
|
@ -90,7 +90,7 @@ def arg_parser():
|
|||
parser.add_option("-G", "--gap", dest="gap_limit", default=None, help="gap limit")
|
||||
parser.add_option("-W", "--password", dest="password", default=None, help="set password for usage with commands (currently only implemented for create command, do not use it for longrunning gui session since the password is visible in /proc)")
|
||||
parser.add_option("-1", "--oneserver", action="store_true", dest="oneserver", default=False, help="connect to one server only")
|
||||
#parser.add_option("--bip32", action="store_true", dest="bip32", default=False, help="bip32 (not final)")
|
||||
parser.add_option("--bip32", action="store_true", dest="bip32", default=False, help="bip32 (not final)")
|
||||
parser.add_option("--mpk", dest="mpk", default=False, help="restore from master public key")
|
||||
return parser
|
||||
|
||||
|
|
|
@ -1477,7 +1477,8 @@ class ElectrumWindow(QMainWindow):
|
|||
self.tabs.setCurrentIndex(3)
|
||||
|
||||
|
||||
def new_account_dialog(self):
|
||||
@protected
|
||||
def new_account_dialog(self, password):
|
||||
|
||||
dialog = QDialog(self)
|
||||
dialog.setModal(1)
|
||||
|
@ -1501,7 +1502,7 @@ class ElectrumWindow(QMainWindow):
|
|||
name = str(e.text())
|
||||
if not name: return
|
||||
|
||||
self.wallet.create_pending_account('1', name)
|
||||
self.wallet.create_pending_account('1of1', name, password)
|
||||
self.update_receive_tab()
|
||||
self.tabs.setCurrentIndex(2)
|
||||
|
||||
|
@ -1545,11 +1546,6 @@ class ElectrumWindow(QMainWindow):
|
|||
dialog.setModal(1)
|
||||
dialog.setWindowTitle(_("Master Public Keys"))
|
||||
|
||||
chain_text = QTextEdit()
|
||||
chain_text.setReadOnly(True)
|
||||
chain_text.setMaximumHeight(170)
|
||||
chain_qrw = QRCodeWidget()
|
||||
|
||||
mpk_text = QTextEdit()
|
||||
mpk_text.setReadOnly(True)
|
||||
mpk_text.setMaximumHeight(170)
|
||||
|
@ -1561,17 +1557,10 @@ class ElectrumWindow(QMainWindow):
|
|||
main_layout.addWidget(mpk_text, 1, 1)
|
||||
main_layout.addWidget(mpk_qrw, 1, 2)
|
||||
|
||||
main_layout.addWidget(QLabel(_('Chain')), 2, 0)
|
||||
main_layout.addWidget(chain_text, 2, 1)
|
||||
main_layout.addWidget(chain_qrw, 2, 2)
|
||||
|
||||
def update(key):
|
||||
c, K, cK = self.wallet.master_public_keys[str(key)]
|
||||
chain_text.setText(c)
|
||||
chain_qrw.set_addr(c)
|
||||
chain_qrw.update_qr()
|
||||
mpk_text.setText(K)
|
||||
mpk_qrw.set_addr(K)
|
||||
xpub = self.wallet.master_public_keys[str(key)]
|
||||
mpk_text.setText(xpub)
|
||||
mpk_qrw.set_addr(xpub)
|
||||
mpk_qrw.update_qr()
|
||||
|
||||
key_selector = QComboBox()
|
||||
|
@ -1683,6 +1672,7 @@ class ElectrumWindow(QMainWindow):
|
|||
try:
|
||||
pk_list = self.wallet.get_private_key(address, password)
|
||||
except Exception as e:
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
self.show_message(str(e))
|
||||
return
|
||||
|
||||
|
@ -2269,30 +2259,30 @@ class ElectrumWindow(QMainWindow):
|
|||
|
||||
|
||||
def show_account_details(self, k):
|
||||
account = self.wallet.accounts[k]
|
||||
|
||||
d = QDialog(self)
|
||||
d.setWindowTitle(_('Account Details'))
|
||||
d.setModal(1)
|
||||
|
||||
vbox = QVBoxLayout(d)
|
||||
roots = self.wallet.get_roots(k)
|
||||
|
||||
name = self.wallet.get_account_name(k)
|
||||
label = QLabel('Name: ' + name)
|
||||
vbox.addWidget(label)
|
||||
|
||||
acctype = '2 of 2' if len(roots) == 2 else '2 of 3' if len(roots) == 3 else 'Single key'
|
||||
vbox.addWidget(QLabel('Type: ' + acctype))
|
||||
vbox.addWidget(QLabel(_('Address type') + ': ' + account.get_type()))
|
||||
|
||||
label = QLabel('Derivation: ' + k)
|
||||
vbox.addWidget(label)
|
||||
vbox.addWidget(QLabel(_('Derivation') + ': ' + k))
|
||||
|
||||
#for root in roots:
|
||||
# mpk = self.wallet.master_public_keys[root]
|
||||
# text = QTextEdit()
|
||||
# text.setReadOnly(True)
|
||||
# text.setMaximumHeight(120)
|
||||
# text.setText(repr(mpk))
|
||||
# vbox.addWidget(text)
|
||||
vbox.addWidget(QLabel(_('Master Public Key:')))
|
||||
|
||||
text = QTextEdit()
|
||||
text.setReadOnly(True)
|
||||
text.setMaximumHeight(170)
|
||||
vbox.addWidget(text)
|
||||
|
||||
mpk_text = '\n'.join( account.get_master_pubkeys() )
|
||||
text.setText(mpk_text)
|
||||
|
||||
vbox.addLayout(close_button(d))
|
||||
d.exec_()
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
|
||||
from bitcoin import *
|
||||
from i18n import _
|
||||
from transaction import Transaction
|
||||
|
||||
class Account(object):
|
||||
|
@ -115,20 +116,23 @@ class OldAccount(Account):
|
|||
def redeem_script(self, sequence):
|
||||
return None
|
||||
|
||||
def get_master_pubkeys(self):
|
||||
return [self.mpk]
|
||||
|
||||
def get_type(self):
|
||||
return _('Old Electrum format')
|
||||
|
||||
|
||||
|
||||
class BIP32_Account(Account):
|
||||
|
||||
def __init__(self, v):
|
||||
Account.__init__(self, v)
|
||||
self.c = v['c'].decode('hex')
|
||||
self.K = v['K'].decode('hex')
|
||||
self.cK = v['cK'].decode('hex')
|
||||
self.xpub = v['xpub']
|
||||
|
||||
def dump(self):
|
||||
d = Account.dump(self)
|
||||
d['c'] = self.c.encode('hex')
|
||||
d['K'] = self.K.encode('hex')
|
||||
d['cK'] = self.cK.encode('hex')
|
||||
d['xpub'] = self.xpub
|
||||
return d
|
||||
|
||||
def get_address(self, for_change, n):
|
||||
|
@ -140,39 +144,41 @@ class BIP32_Account(Account):
|
|||
return self.get_address(0,0)
|
||||
|
||||
def get_pubkey(self, for_change, n):
|
||||
K = self.K
|
||||
chain = self.c
|
||||
_, _, _, c, cK = deserialize_xkey(self.xpub)
|
||||
for i in [for_change, n]:
|
||||
K, K_compressed, chain = CKD_prime(K, chain, i)
|
||||
return K_compressed.encode('hex')
|
||||
cK, c = CKD_pub(cK, c, i)
|
||||
return cK.encode('hex')
|
||||
|
||||
def redeem_script(self, sequence):
|
||||
return None
|
||||
|
||||
def get_pubkeys(self, sequence):
|
||||
return [self.get_pubkey(*sequence)]
|
||||
|
||||
def get_master_pubkeys(self):
|
||||
return [self.xpub]
|
||||
|
||||
def get_type(self):
|
||||
return _('Standard 1 of 1')
|
||||
#acctype = 'multisig 2 of 2' if len(roots) == 2 else 'multisig 2 of 3' if len(roots) == 3 else 'standard 1 of 1'
|
||||
|
||||
|
||||
class BIP32_Account_2of2(BIP32_Account):
|
||||
|
||||
def __init__(self, v):
|
||||
BIP32_Account.__init__(self, v)
|
||||
self.c2 = v['c2'].decode('hex')
|
||||
self.K2 = v['K2'].decode('hex')
|
||||
self.cK2 = v['cK2'].decode('hex')
|
||||
self.xpub2 = v['xpub2']
|
||||
|
||||
def dump(self):
|
||||
d = BIP32_Account.dump(self)
|
||||
d['c2'] = self.c2.encode('hex')
|
||||
d['K2'] = self.K2.encode('hex')
|
||||
d['cK2'] = self.cK2.encode('hex')
|
||||
d['xpub2'] = self.xpub2
|
||||
return d
|
||||
|
||||
def get_pubkey2(self, for_change, n):
|
||||
K = self.K2
|
||||
chain = self.c2
|
||||
_, _, _, c, cK = deserialize_xkey(self.xpub2)
|
||||
for i in [for_change, n]:
|
||||
K, K_compressed, chain = CKD_prime(K, chain, i)
|
||||
return K_compressed.encode('hex')
|
||||
cK, c = CKD_prime(cK, c, i)
|
||||
return cK.encode('hex')
|
||||
|
||||
def redeem_script(self, sequence):
|
||||
chain, i = sequence
|
||||
|
@ -187,27 +193,29 @@ class BIP32_Account_2of2(BIP32_Account):
|
|||
def get_pubkeys(self, sequence):
|
||||
return [ self.get_pubkey( *sequence ), self.get_pubkey2( *sequence )]
|
||||
|
||||
def get_master_pubkeys(self):
|
||||
return [self.xpub, self.xpub2]
|
||||
|
||||
def get_type(self):
|
||||
return _('Multisig 2 of 2')
|
||||
|
||||
|
||||
class BIP32_Account_2of3(BIP32_Account_2of2):
|
||||
|
||||
def __init__(self, v):
|
||||
BIP32_Account_2of2.__init__(self, v)
|
||||
self.c3 = v['c3'].decode('hex')
|
||||
self.K3 = v['K3'].decode('hex')
|
||||
self.cK3 = v['cK3'].decode('hex')
|
||||
self.xpub3 = v['xpub3']
|
||||
|
||||
def dump(self):
|
||||
d = BIP32_Account_2of2.dump(self)
|
||||
d['c3'] = self.c3.encode('hex')
|
||||
d['K3'] = self.K3.encode('hex')
|
||||
d['cK3'] = self.cK3.encode('hex')
|
||||
d['xpub3'] = self.xpub3
|
||||
return d
|
||||
|
||||
def get_pubkey3(self, for_change, n):
|
||||
K = self.K3
|
||||
chain = self.c3
|
||||
_, _, _, c, cK = deserialize_xkey(self.xpub3)
|
||||
for i in [for_change, n]:
|
||||
K, K_compressed, chain = CKD_prime(K, chain, i)
|
||||
return K_compressed.encode('hex')
|
||||
cK, c = CKD_prime(cK, c, i)
|
||||
return cK.encode('hex')
|
||||
|
||||
def get_redeem_script(self, sequence):
|
||||
chain, i = sequence
|
||||
|
@ -219,5 +227,11 @@ class BIP32_Account_2of3(BIP32_Account_2of2):
|
|||
def get_pubkeys(self, sequence):
|
||||
return [ self.get_pubkey( *sequence ), self.get_pubkey2( *sequence ), self.get_pubkey3( *sequence )]
|
||||
|
||||
def get_master_pubkeys(self):
|
||||
return [self.xpub, self.xpub2, self.xpub3]
|
||||
|
||||
def get_type(self):
|
||||
return _('Multisig 2 of 3')
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
ELECTRUM_VERSION = "1.9.8" # version of the client package
|
||||
PROTOCOL_VERSION = '0.9' # protocol version requested
|
||||
NEW_SEED_VERSION = 6 # bip32 wallets
|
||||
NEW_SEED_VERSION = 7 # bip32 wallets
|
||||
OLD_SEED_VERSION = 4 # old electrum deterministic generation
|
||||
SEED_PREFIX = '01' # the hash of the mnemonic seed must begin with this
|
||||
|
|
306
lib/wallet.py
306
lib/wallet.py
|
@ -321,96 +321,40 @@ class NewWallet:
|
|||
self.create_accounts(password)
|
||||
|
||||
|
||||
def create_watching_only_wallet(self, K0, c0):
|
||||
cK0 = "" #FIXME
|
||||
self.master_public_keys = {
|
||||
"m/0'/": (c0, K0, cK0),
|
||||
}
|
||||
def create_watching_only_wallet(self, xpub):
|
||||
self.master_public_keys = { "m/": xpub }
|
||||
self.storage.put('master_public_keys', self.master_public_keys, True)
|
||||
self.storage.put('seed_version', self.seed_version, True)
|
||||
self.create_account('1of1','Main account')
|
||||
account = BIP32_Account({'xpub':xpub})
|
||||
self.add_account("m/", account)
|
||||
|
||||
|
||||
def create_accounts(self, password):
|
||||
seed = pw_decode(self.seed, password)
|
||||
# create default account
|
||||
self.create_master_keys('1of1', password)
|
||||
self.create_account('1of1','Main account')
|
||||
self.create_master_keys(password)
|
||||
self.create_account('Main account', password)
|
||||
|
||||
|
||||
def create_master_keys(self, account_type, password):
|
||||
master_k, master_c, master_K, master_cK = bip32_init(self.get_seed(password))
|
||||
if account_type == '1of1':
|
||||
k0, c0, K0, cK0 = bip32_private_derivation(master_k, master_c, "m/", "m/0'/")
|
||||
self.master_public_keys["m/0'/"] = (c0, K0, cK0)
|
||||
self.master_private_keys["m/0'/"] = pw_encode(k0, password)
|
||||
elif account_type == '2of2':
|
||||
k1, c1, K1, cK1 = bip32_private_derivation(master_k, master_c, "m/", "m/1'/")
|
||||
k2, c2, K2, cK2 = bip32_private_derivation(master_k, master_c, "m/", "m/2'/")
|
||||
self.master_public_keys["m/1'/"] = (c1, K1, cK1)
|
||||
self.master_public_keys["m/2'/"] = (c2, K2, cK2)
|
||||
self.master_private_keys["m/1'/"] = pw_encode(k1, password)
|
||||
self.master_private_keys["m/2'/"] = pw_encode(k2, password)
|
||||
elif account_type == '2of3':
|
||||
k3, c3, K3, cK3 = bip32_private_derivation(master_k, master_c, "m/", "m/3'/")
|
||||
k4, c4, K4, cK4 = bip32_private_derivation(master_k, master_c, "m/", "m/4'/")
|
||||
k5, c5, K5, cK5 = bip32_private_derivation(master_k, master_c, "m/", "m/5'/")
|
||||
self.master_public_keys["m/3'/"] = (c3, K3, cK3)
|
||||
self.master_public_keys["m/4'/"] = (c4, K4, cK4)
|
||||
self.master_public_keys["m/5'/"] = (c5, K5, cK5)
|
||||
self.master_private_keys["m/3'/"] = pw_encode(k3, password)
|
||||
self.master_private_keys["m/4'/"] = pw_encode(k4, password)
|
||||
self.master_private_keys["m/5'/"] = pw_encode(k5, password)
|
||||
|
||||
def create_master_keys(self, password):
|
||||
xpriv, xpub = bip32_root(self.get_seed(password))
|
||||
self.master_public_keys["m/"] = xpub
|
||||
self.master_private_keys["m/"] = pw_encode(xpriv, password)
|
||||
self.storage.put('master_public_keys', self.master_public_keys, True)
|
||||
self.storage.put('master_private_keys', self.master_private_keys, True)
|
||||
|
||||
def has_master_public_keys(self, account_type):
|
||||
if account_type == '1of1':
|
||||
return "m/0'/" in self.master_public_keys
|
||||
elif account_type == '2of2':
|
||||
return set(["m/1'/", "m/2'/"]) <= set(self.master_public_keys.keys())
|
||||
elif account_type == '2of3':
|
||||
return set(["m/3'/", "m/4'/", "m/5'/"]) <= set(self.master_public_keys.keys())
|
||||
|
||||
def find_root_by_master_key(self, c, K):
|
||||
for key, v in self.master_public_keys.items():
|
||||
def find_root_by_master_key(self, xpub):
|
||||
for key, xpub2 in self.master_public_keys.items():
|
||||
if key == "m/":continue
|
||||
cc, KK, _ = v
|
||||
if (c == cc) and (K == KK):
|
||||
if xpub == xpub2:
|
||||
return key
|
||||
|
||||
def deseed_root(self, seed, password):
|
||||
# for safety, we ask the user to enter their seed
|
||||
assert seed == self.get_seed(password)
|
||||
self.seed = ''
|
||||
self.storage.put('seed', '', True)
|
||||
|
||||
|
||||
def deseed_branch(self, k):
|
||||
# check that parent has no seed
|
||||
# assert self.seed == ''
|
||||
self.master_private_keys.pop(k)
|
||||
self.storage.put('master_private_keys', self.master_private_keys, True)
|
||||
|
||||
|
||||
def is_watching_only(self):
|
||||
return (self.seed == '') and (self.master_private_keys == {})
|
||||
|
||||
|
||||
|
||||
def account_id(self, account_type, i):
|
||||
if account_type == '1of1':
|
||||
return "m/0'/%d"%i
|
||||
elif account_type == '2of2':
|
||||
return "m/1'/%d & m/2'/%d"%(i,i)
|
||||
elif account_type == '2of3':
|
||||
return "m/3'/%d & m/4'/%d & m/5'/%d"%(i,i,i)
|
||||
else:
|
||||
raise Exception('unknown account type')
|
||||
|
||||
|
||||
def num_accounts(self, account_type):
|
||||
def num_accounts(self, account_type = '1of1'):
|
||||
keys = self.accounts.keys()
|
||||
i = 0
|
||||
while True:
|
||||
|
@ -420,47 +364,35 @@ class NewWallet:
|
|||
return i
|
||||
|
||||
|
||||
def new_account_address(self, account_type = '1of1'):
|
||||
def next_account_address(self, account_type, password):
|
||||
i = self.num_accounts(account_type)
|
||||
k = self.account_id(account_type,i)
|
||||
account_id = self.account_id(account_type, i)
|
||||
|
||||
addr = self.next_addresses.get(k)
|
||||
addr = self.next_addresses.get(account_id)
|
||||
if not addr:
|
||||
account_id, account = self.next_account(account_type)
|
||||
account = self.make_account(account_id, password)
|
||||
addr = account.first_address()
|
||||
self.next_addresses[k] = addr
|
||||
self.storage.put('next_addresses',self.next_addresses)
|
||||
self.next_addresses[account_id] = addr
|
||||
self.storage.put('next_addresses', self.next_addresses)
|
||||
|
||||
return k, addr
|
||||
return account_id, addr
|
||||
|
||||
def account_id(self, account_type, i):
|
||||
if account_type == '1of1':
|
||||
return "m/%d'"%i
|
||||
else:
|
||||
raise
|
||||
|
||||
def next_account(self, account_type = '1of1'):
|
||||
|
||||
i = self.num_accounts(account_type)
|
||||
account_id = self.account_id(account_type,i)
|
||||
|
||||
if account_type is '1of1':
|
||||
master_c0, master_K0, _ = self.master_public_keys["m/0'/"]
|
||||
c0, K0, cK0 = bip32_public_derivation(master_c0.decode('hex'), master_K0.decode('hex'), "m/0'/", "m/0'/%d"%i)
|
||||
account = BIP32_Account({ 'c':c0, 'K':K0, 'cK':cK0 })
|
||||
|
||||
elif account_type == '2of2':
|
||||
master_c1, master_K1, _ = self.master_public_keys["m/1'/"]
|
||||
c1, K1, cK1 = bip32_public_derivation(master_c1.decode('hex'), master_K1.decode('hex'), "m/1'/", "m/1'/%d"%i)
|
||||
master_c2, master_K2, _ = self.master_public_keys["m/2'/"]
|
||||
c2, K2, cK2 = bip32_public_derivation(master_c2.decode('hex'), master_K2.decode('hex'), "m/2'/", "m/2'/%d"%i)
|
||||
account = BIP32_Account_2of2({ 'c':c1, 'K':K1, 'cK':cK1, 'c2':c2, 'K2':K2, 'cK2':cK2 })
|
||||
|
||||
elif account_type == '2of3':
|
||||
master_c3, master_K3, _ = self.master_public_keys["m/3'/"]
|
||||
c3, K3, cK3 = bip32_public_derivation(master_c3.decode('hex'), master_K3.decode('hex'), "m/3'/", "m/3'/%d"%i)
|
||||
master_c4, master_K4, _ = self.master_public_keys["m/4'/"]
|
||||
c4, K4, cK4 = bip32_public_derivation(master_c4.decode('hex'), master_K4.decode('hex'), "m/4'/", "m/4'/%d"%i)
|
||||
master_c5, master_K5, _ = self.master_public_keys["m/5'/"]
|
||||
c5, K5, cK5 = bip32_public_derivation(master_c5.decode('hex'), master_K5.decode('hex'), "m/5'/", "m/5'/%d"%i)
|
||||
account = BIP32_Account_2of3({ 'c':c3, 'K':K3, 'cK':cK3, 'c2':c4, 'K2':K4, 'cK2':cK4, 'c3':c5, 'K3':K5, 'cK3':cK5 })
|
||||
|
||||
return account_id, account
|
||||
def make_account(self, account_id, password):
|
||||
"""Creates and saves the master keys, but does not save the account"""
|
||||
master_xpriv = pw_decode( self.master_private_keys["m/"] , password )
|
||||
xpriv, xpub = bip32_private_derivation(master_xpriv, "m/", account_id)
|
||||
self.master_private_keys[account_id] = pw_encode(xpriv, password)
|
||||
self.master_public_keys[account_id] = xpub
|
||||
self.storage.put('master_public_keys', self.master_public_keys, True)
|
||||
self.storage.put('master_private_keys', self.master_private_keys, True)
|
||||
account = BIP32_Account({'xpub':xpub})
|
||||
return account
|
||||
|
||||
|
||||
def set_label(self, name, text = None):
|
||||
|
@ -482,17 +414,24 @@ class NewWallet:
|
|||
return changed
|
||||
|
||||
|
||||
|
||||
def create_account(self, account_type = '1of1', name = None):
|
||||
k, account = self.next_account(account_type)
|
||||
if k in self.pending_accounts:
|
||||
self.pending_accounts.pop(k)
|
||||
self.storage.put('pending_accounts', self.pending_accounts)
|
||||
|
||||
self.accounts[k] = account
|
||||
self.save_accounts()
|
||||
def create_account(self, name, password):
|
||||
i = self.num_accounts('1of1')
|
||||
account_id = self.account_id('1of1', i)
|
||||
account = self.make_account(account_id, password)
|
||||
self.add_account(account_id, account)
|
||||
if name:
|
||||
self.set_label(k, name)
|
||||
self.set_label(account_id, name)
|
||||
|
||||
# add address of the next account
|
||||
_, _ = self.next_account_address('1of1', password)
|
||||
|
||||
|
||||
def add_account(self, account_id, account):
|
||||
self.accounts[account_id] = account
|
||||
if account_id in self.pending_accounts:
|
||||
self.pending_accounts.pop(account_id)
|
||||
self.storage.put('pending_accounts', self.pending_accounts)
|
||||
self.save_accounts()
|
||||
|
||||
|
||||
def save_accounts(self):
|
||||
|
@ -525,10 +464,10 @@ class NewWallet:
|
|||
def account_is_pending(self, k):
|
||||
return k in self.pending_accounts
|
||||
|
||||
def create_pending_account(self, acct_type, name):
|
||||
k, addr = self.new_account_address(acct_type)
|
||||
self.set_label(k, name)
|
||||
self.pending_accounts[k] = addr
|
||||
def create_pending_account(self, acct_type, name, password):
|
||||
account_id, addr = self.next_account_address(acct_type, password)
|
||||
self.set_label(account_id, name)
|
||||
self.pending_accounts[account_id] = addr
|
||||
self.storage.put('pending_accounts', self.pending_accounts)
|
||||
|
||||
def get_pending_accounts(self):
|
||||
|
@ -559,20 +498,13 @@ class NewWallet:
|
|||
return s[0] == 1
|
||||
|
||||
def get_master_public_key(self):
|
||||
c, K, cK = self.storage.get("master_public_keys")["m/0'/"]
|
||||
return repr((c, K))
|
||||
return self.storage.get("master_public_keys")["m/"]
|
||||
|
||||
def get_master_private_key(self, account, password):
|
||||
k = self.master_private_keys.get(account)
|
||||
if not k: return
|
||||
master_k = pw_decode( k, password)
|
||||
master_c, master_K, master_Kc = self.master_public_keys[account]
|
||||
try:
|
||||
K, Kc = get_pubkeys_from_secret(master_k.decode('hex'))
|
||||
assert K.encode('hex') == master_K
|
||||
except Exception:
|
||||
raise Exception("Invalid password")
|
||||
return master_k
|
||||
xpriv = pw_decode( k, password)
|
||||
return xpriv
|
||||
|
||||
|
||||
def get_address_index(self, address):
|
||||
|
@ -605,13 +537,14 @@ class NewWallet:
|
|||
roots = []
|
||||
for a in account.split('&'):
|
||||
s = a.strip()
|
||||
m = re.match("(m/\d+'/)(\d+)", s)
|
||||
m = re.match("m/(\d+')", s)
|
||||
roots.append( m.group(1) )
|
||||
return roots
|
||||
|
||||
|
||||
def is_seeded(self, account):
|
||||
if type(account) is int:
|
||||
return self.seed is not None
|
||||
return True
|
||||
|
||||
|
||||
for root in self.get_roots(account):
|
||||
if root not in self.master_private_keys.keys():
|
||||
|
@ -619,29 +552,27 @@ class NewWallet:
|
|||
return True
|
||||
|
||||
def rebase_sequence(self, account, sequence):
|
||||
# account has one or more xpub
|
||||
# sequence is a sequence of public derivations
|
||||
c, i = sequence
|
||||
dd = []
|
||||
for a in account.split('&'):
|
||||
s = a.strip()
|
||||
m = re.match("(m/\d+'/)(\d+)", s)
|
||||
root = m.group(1)
|
||||
num = int(m.group(2))
|
||||
m = re.match("m/(\d+)'", s)
|
||||
root = "m/"
|
||||
num = int(m.group(1))
|
||||
dd.append( (root, [num,c,i] ) )
|
||||
return dd
|
||||
|
||||
|
||||
def get_keyID(self, account, sequence):
|
||||
if account == 0:
|
||||
a, b = sequence
|
||||
mpk = self.storage.get('master_public_key')
|
||||
return 'old(%s,%d,%d)'%(mpk,a,b)
|
||||
|
||||
def get_keyID(self, account, sequence):
|
||||
rs = self.rebase_sequence(account, sequence)
|
||||
dd = []
|
||||
for root, public_sequence in rs:
|
||||
c, K, cK = self.master_public_keys[root]
|
||||
xpub = self.master_public_keys[root]
|
||||
s = '/' + '/'.join( map(lambda x:str(x), public_sequence) )
|
||||
dd.append( 'bip32(%s,%s,%s)'%(c, cK, s) )
|
||||
dd.append( 'bip32(%s,%s)'%(xpub, s) )
|
||||
return '&'.join(dd)
|
||||
|
||||
|
||||
|
@ -666,20 +597,14 @@ class NewWallet:
|
|||
if address in self.imported_keys.keys():
|
||||
out.append( pw_decode( self.imported_keys[address], password ) )
|
||||
else:
|
||||
account, sequence = self.get_address_index(address)
|
||||
if account == 0:
|
||||
pk = self.accounts[account].get_private_key(seed, sequence)
|
||||
out.append(pk)
|
||||
return out
|
||||
|
||||
# assert address == self.accounts[account].get_address(*sequence)
|
||||
rs = self.rebase_sequence( account, sequence)
|
||||
account_id, sequence = self.get_address_index(address)
|
||||
#rs = self.rebase_sequence( account, sequence)
|
||||
rs = [(account_id, sequence)]
|
||||
for root, public_sequence in rs:
|
||||
|
||||
if root not in self.master_private_keys.keys(): continue
|
||||
master_k = self.get_master_private_key(root, password)
|
||||
master_c, _, _ = self.master_public_keys[root]
|
||||
pk = bip32_private_key( public_sequence, master_k.decode('hex'), master_c.decode('hex'))
|
||||
xpriv = self.get_master_private_key(root, password)
|
||||
if not xpriv: continue
|
||||
_, _, _, c, k = deserialize_xkey(xpriv)
|
||||
pk = bip32_private_key( public_sequence, k, c )
|
||||
out.append(pk)
|
||||
|
||||
return out
|
||||
|
@ -849,7 +774,7 @@ class NewWallet:
|
|||
for tx_hash, tx_height in h:
|
||||
if tx_height == 0:
|
||||
tx_age = 0
|
||||
else:
|
||||
else:
|
||||
tx_age = self.network.get_local_height() - tx_height + 1
|
||||
if tx_age > age:
|
||||
age = tx_age
|
||||
|
@ -877,16 +802,14 @@ class NewWallet:
|
|||
return new_addresses
|
||||
|
||||
|
||||
|
||||
def create_pending_accounts(self):
|
||||
for account_type in ['1of1','2of2','2of3']:
|
||||
if not self.has_master_public_keys(account_type):
|
||||
continue
|
||||
k, a = self.new_account_address(account_type)
|
||||
if self.address_is_old(a):
|
||||
print_error( "creating account", a )
|
||||
self.create_account(account_type)
|
||||
self.next_addresses.pop(k)
|
||||
def check_pending_accounts(self):
|
||||
for account_id, addr in self.next_addresses.items():
|
||||
if self.address_is_old(addr):
|
||||
print_error( "creating account", account_id )
|
||||
xpub = self.master_public_keys[account_id]
|
||||
account = BIP32_Account({'xpub':xpub})
|
||||
self.add_account(account_id, account)
|
||||
self.next_addresses.pop(account_id)
|
||||
|
||||
|
||||
def synchronize_account(self, account):
|
||||
|
@ -897,8 +820,7 @@ class NewWallet:
|
|||
|
||||
|
||||
def synchronize(self):
|
||||
if self.master_public_keys:
|
||||
self.create_pending_accounts()
|
||||
self.check_pending_accounts()
|
||||
new = []
|
||||
for account in self.accounts.values():
|
||||
new += self.synchronize_account(account)
|
||||
|
@ -1761,10 +1683,10 @@ class OldWallet(NewWallet):
|
|||
self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]})
|
||||
self.save_accounts()
|
||||
|
||||
def create_watching_only_wallet(self, K0):
|
||||
def create_watching_only_wallet(self, mpk):
|
||||
self.seed_version = OLD_SEED_VERSION
|
||||
self.storage.put('seed_version', self.seed_version, True)
|
||||
self.create_account(K0)
|
||||
self.create_account(mpk)
|
||||
|
||||
def get_seed(self, password):
|
||||
seed = pw_decode(self.seed, password)
|
||||
|
@ -1801,7 +1723,32 @@ class OldWallet(NewWallet):
|
|||
assert k == 0
|
||||
return 'Main account'
|
||||
|
||||
def is_seeded(self, account):
|
||||
return self.seed is not None
|
||||
|
||||
def get_private_key(self, address, password):
|
||||
if self.is_watching_only():
|
||||
return []
|
||||
|
||||
# first check the provided password
|
||||
seed = self.get_seed(password)
|
||||
|
||||
out = []
|
||||
if address in self.imported_keys.keys():
|
||||
out.append( pw_decode( self.imported_keys[address], password ) )
|
||||
else:
|
||||
account_id, sequence = self.get_address_index(address)
|
||||
pk = self.accounts[0].get_private_key(seed, sequence)
|
||||
out.append(pk)
|
||||
return out
|
||||
|
||||
def get_keyID(self, account, sequence):
|
||||
a, b = sequence
|
||||
mpk = self.storage.get('master_public_key')
|
||||
return 'old(%s,%d,%d)'%(mpk,a,b)
|
||||
|
||||
def check_pending_accounts(self):
|
||||
pass
|
||||
|
||||
|
||||
# former WalletFactory
|
||||
|
@ -1867,19 +1814,20 @@ class Wallet(object):
|
|||
|
||||
|
||||
@classmethod
|
||||
def from_mpk(self, s, storage):
|
||||
try:
|
||||
mpk, chain = s.split(':')
|
||||
except:
|
||||
mpk = s
|
||||
chain = False
|
||||
def from_mpk(self, mpk, storage):
|
||||
|
||||
if chain:
|
||||
w = NewWallet(storage)
|
||||
w.create_watching_only_wallet(mpk, chain)
|
||||
else:
|
||||
try:
|
||||
int(mpk, 16)
|
||||
old = True
|
||||
except:
|
||||
old = False
|
||||
|
||||
if old:
|
||||
w = OldWallet(storage)
|
||||
w.seed = ''
|
||||
w.create_watching_only_wallet(mpk)
|
||||
else:
|
||||
w = NewWallet(storage)
|
||||
w.create_watching_only_wallet(mpk)
|
||||
|
||||
return w
|
||||
|
|
Loading…
Reference in New Issue