From 6b45070b2f7c899dd9bdd1a7bf423ad23a014374 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 20 Jun 2017 10:47:02 +0200 Subject: [PATCH] allow arbitrary derivations with bip39 seeds, to let multibit users recover funds --- gui/qt/installwizard.py | 2 +- lib/base_wizard.py | 26 ++++++++++---------------- lib/bitcoin.py | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py index 6288163f..7d1d6c68 100644 --- a/gui/qt/installwizard.py +++ b/gui/qt/installwizard.py @@ -453,7 +453,7 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): line = QLineEdit() line.setText(default) def f(text): - self.next_button.setEnabled(test(text)) + self.next_button.setEnabled(test(unicode(text))) line.textEdited.connect(f) vbox.addWidget(line) vbox.addWidget(WWLabel(warning)) diff --git a/lib/base_wizard.py b/lib/base_wizard.py index c86c56e8..31cb426f 100644 --- a/lib/base_wizard.py +++ b/lib/base_wizard.py @@ -222,21 +222,16 @@ class BaseWizard(object): # This is partially compatible with BIP45; assumes index=0 self.on_hw_derivation(name, device_info, "m/45'/0") else: - f = lambda x: self.run('on_hw_derivation', name, device_info, bip44_derivation(int(x))) - self.account_id_dialog(f) + f = lambda x: self.run('on_hw_derivation', name, device_info, str(x)) + self.derivation_dialog(f) - def account_id_dialog(self, f): + def derivation_dialog(self, f): + default = bip44_derivation(0) message = '\n'.join([ - _('Enter your BIP44 account number here.'), - _('If you are not sure what this is, leave this field to zero.') + _('Enter your wallet derivation here.'), + _('If you are not sure what this is, leave this field unchanged.') ]) - def is_int(x): - try: - int(x) - return True - except: - return False - self.line_dialog(run_next=f, title=_('Account Number'), message=message, default='0', test=is_int) + self.line_dialog(run_next=f, title=_('Derivation'), message=message, default=default, test=bitcoin.is_bip32_derivation) def on_hw_derivation(self, name, device_info, derivation): from keystore import hardware_keystore @@ -293,17 +288,16 @@ class BaseWizard(object): raise BaseException('Unknown seed type', seed_type) def on_restore_bip39(self, seed, passphrase): - f = lambda x: self.run('on_bip44', seed, passphrase, int(x)) - self.account_id_dialog(f) + f = lambda x: self.run('on_bip44', seed, passphrase, str(x)) + self.derivation_dialog(f) def create_keystore(self, seed, passphrase): k = keystore.from_seed(seed, passphrase) self.on_keystore(k) - def on_bip44(self, seed, passphrase, account_id): + def on_bip44(self, seed, passphrase, derivation): k = keystore.BIP32_KeyStore({}) bip32_seed = keystore.bip39_to_seed(seed, passphrase) - derivation = bip44_derivation(account_id) k.add_xprv_from_seed(bip32_seed, 0, derivation) self.on_keystore(k) diff --git a/lib/bitcoin.py b/lib/bitcoin.py index 770d3574..bcc289b2 100644 --- a/lib/bitcoin.py +++ b/lib/bitcoin.py @@ -820,6 +820,21 @@ def xpub_from_pubkey(xtype, cK): return serialize_xpub(xtype, chr(0)*32, cK) +def bip32_derivation(s): + assert s.startswith('m/') + s = s[2:] + for n in s.split('/'): + if n == '': continue + i = int(n[:-1]) + BIP32_PRIME if n[-1] == "'" else int(n) + yield i + +def is_bip32_derivation(x): + try: + [ i for i in bip32_derivation(x)] + return True + except : + return False + def bip32_private_derivation(xprv, branch, sequence): assert sequence.startswith(branch) if branch == sequence: