electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 5a43b633d625611db5074bb1759d14b577286f4f
parent ffc72e65df01bb397d51ca70ff04eb60fab9b8a2
Author: ThomasV <thomasv@gitorious>
Date:   Tue,  1 Apr 2014 11:25:12 +0200

update bip32 accounts and wallet

Diffstat:
Melectrum | 2+-
Mgui/qt/main_window.py | 50++++++++++++++++++++------------------------------
Mlib/account.py | 74++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mlib/version.py | 2+-
Mlib/wallet.py | 300+++++++++++++++++++++++++++++++++----------------------------------------------
5 files changed, 190 insertions(+), 238 deletions(-)

diff --git a/electrum b/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 diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py @@ -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)) + + vbox.addWidget(QLabel(_('Master Public Key:'))) + + text = QTextEdit() + text.setReadOnly(True) + text.setMaximumHeight(170) + vbox.addWidget(text) - #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) + mpk_text = '\n'.join( account.get_master_pubkeys() ) + text.setText(mpk_text) vbox.addLayout(close_button(d)) d.exec_() diff --git a/lib/account.py b/lib/account.py @@ -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') + diff --git a/lib/version.py b/lib/version.py @@ -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 diff --git a/lib/wallet.py b/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) - - return k, addr + self.next_addresses[account_id] = addr + self.storage.put('next_addresses', self.next_addresses) + return account_id, addr - 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 }) + def account_id(self, account_type, i): + if account_type == '1of1': + return "m/%d'"%i + else: + raise - 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, 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(account_id, name) - 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) + # add address of the next account + _, _ = self.next_account_address('1of1', password) - self.accounts[k] = account + + 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() - if name: - self.set_label(k, name) 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): + def from_mpk(self, mpk, storage): + try: - mpk, chain = s.split(':') + int(mpk, 16) + old = True except: - mpk = s - chain = False + old = False - if chain: - w = NewWallet(storage) - w.create_watching_only_wallet(mpk, chain) - else: + 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