URI: 
       trequest account_id in wizard, for hardware wallets. cleanup bip44 code - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit d9021788fae1244cde1374358a91b3cfd55ea548
   DIR parent 71de14240d58e4eb721903f755593c45c1e84cbd
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Mon, 15 Aug 2016 11:48:33 +0200
       
       request account_id in wizard, for hardware wallets. cleanup bip44 code
       
       Diffstat:
         M gui/qt/installwizard.py             |      12 +++++++++---
         M lib/base_wizard.py                  |      34 +++++++++++++++++++++++++++----
         M lib/keystore.py                     |      98 ++++++-------------------------
         M plugins/trezor/clientbase.py        |       4 ++--
         M plugins/trezor/plugin.py            |       5 ++---
       
       5 files changed, 62 insertions(+), 91 deletions(-)
       ---
   DIR diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py
       t@@ -372,14 +372,20 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
                return action
        
            @wizard_dialog
       -    def input_dialog(self, title, message, run_next):
       +    def account_id_dialog(self, run_next):
       +        message = '\n'.join([
       +            _('Enter your account number here.'),
       +            _('If you are not sure what this is, leave this field to zero.')
       +        ])
       +        default = '0'
       +        title = _('Account Number')
                line = QLineEdit()
       +        line.setText(default)
                vbox = QVBoxLayout()
                vbox.addWidget(QLabel(message))
                vbox.addWidget(line)
                self.set_main_layout(vbox, title)
       -        action = line.text()
       -        return action
       +        return int(line.text())
        
            @wizard_dialog
            def show_xpub_dialog(self, xpub, run_next):
   DIR diff --git a/lib/base_wizard.py b/lib/base_wizard.py
       t@@ -173,16 +173,42 @@ class BaseWizard(object):
                self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run)
        
            def on_hardware_device(self):
       +        f = lambda x: self.run('on_hardware_account_id', x)
       +        self.account_id_dialog(run_next=f)
       +
       +    def on_hardware_account_id(self, account_id):
                from keystore import load_keystore
       +        self.storage.put('account_id', int(account_id))
                keystore = load_keystore(self.storage, None)
                keystore.plugin.on_create_wallet(keystore, self)
        
            def on_hardware_seed(self):
       -        from keystore import load_keystore
                self.storage.put('key_type', 'hw_seed')
       -        keystore = load_keystore(self.storage, None)
       -        self.plugin = keystore #fixme .plugin
       -        keystore.on_restore_wallet(self)
       +        is_valid = lambda x: True #fixme: bip39
       +        f = lambda seed: self.run('on_bip39_seed', seed)
       +        self.restore_seed_dialog(run_next=f, is_valid=is_valid)
       +
       +    def on_bip_39_seed(self, seed):
       +        f = lambda passphrase: self.run('on_bip39_passphrase', seed, passphrase)
       +        self.request_passphrase(self.storage.get('hw_type'), run_next=f)
       +
       +    def on_bip39_passphrase(self, seed, passphrase):
       +        f = lambda account_id: self.run('on_bip44_account_id', seed, passphrase, account_id)
       +        self.account_id_dialog(run_next=f)
       +
       +    def on_bip44_account_id(self, seed, passphrase, account_id):
       +        f = lambda pw: self.run('on_bip44', seed, passphrase, account_id, pw)
       +        self.request_password(run_next=f)
       +
       +    def on_bip44(self, seed, passphrase, account_id, password):
       +        import keystore
       +        k = keystore.BIP32_KeyStore()
       +        k.add_seed(seed, password)
       +        bip32_seed = keystore.bip39_to_seed(seed, passphrase)
       +        derivation = "m/44'/0'/%d'"%account_id
       +        self.storage.put('account_id', account_id)
       +        k.add_xprv_from_seed(bip32_seed, derivation, password)
       +        k.save(self.storage, 'x/')
                self.wallet = Standard_Wallet(self.storage)
                self.run('create_addresses')
        
   DIR diff --git a/lib/keystore.py b/lib/keystore.py
       t@@ -216,7 +216,6 @@ class Xpub:
        
        
        class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
       -    root_derivation = "m/"
        
            def __init__(self):
                Xpub.__init__(self)
       t@@ -298,43 +297,19 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
                if keypairs:
                    tx.sign(keypairs)
        
       -    def derive_xkeys(self, root, derivation, password):
       -        x = self.master_private_keys[root]
       -        root_xprv = pw_decode(x, password)
       -        xprv, xpub = bip32_private_derivation(root_xprv, root, derivation)
       -        return xpub, xprv
       -
            def get_mnemonic(self, password):
                return self.get_seed(password)
        
       -    def mnemonic_to_seed(self, seed, password):
       -        return Mnemonic.mnemonic_to_seed(seed, password)
       -
       -    @classmethod
       -    def make_seed(self, lang=None):
       -        return Mnemonic(lang).make_seed()
       -
       -    #@classmethod
       -    #def address_derivation(self, account_id, change, address_index):
       -    #    account_derivation = self.account_derivation(account_id)
       -    #    return "%s/%d/%d" % (account_derivation, change, address_index)
       -
       -    #def address_id(self, address):
       -    #    acc_id, (change, address_index) = self.get_address_index(address)
       -    #    return self.address_derivation(acc_id, change, address_index)
       -
       -    def add_seed_and_xprv(self, seed, password, passphrase=''):
       -        xprv, xpub = bip32_root(self.mnemonic_to_seed(seed, passphrase))
       -        xprv, xpub = bip32_private_derivation(xprv, "m/", self.root_derivation)
       -        self.add_seed(seed, password)
       -        self.add_master_private_key(xprv, password)
       -        self.add_master_public_key(xpub)
       -
            def add_xprv(self, xprv, password):
                xpub = bitcoin.xpub_from_xprv(xprv)
                self.add_master_private_key(xprv, password)
                self.add_master_public_key(xpub)
        
       +    def add_xprv_from_seed(self, bip32_seed, derivation, password):
       +        xprv, xpub = bip32_root(bip32_seed)
       +        xprv, xpub = bip32_private_derivation(xprv, "m/", derivation)
       +        self.add_xprv(xprv, password)
       +
            def can_sign(self, xpub):
                return xpub == self.xpub and self.xprv is not None
        
       t@@ -533,54 +508,19 @@ class Hardware_KeyStore(KeyStore, Xpub):
            def can_change_password(self):
                return False
        
       -    def derive_xkeys(self, root, derivation, password):
       -        if self.master_public_keys.get(self.root_name):
       -            return BIP44_wallet.derive_xkeys(self, root, derivation, password)
       -        # When creating a wallet we need to ask the device for the
       -        # master public key
       -        xpub = self.get_public_key(derivation)
       -        return xpub, None
       -
        
       -class BIP44_KeyStore(BIP32_KeyStore):
       -    root_derivation = "m/44'/0'/0'"
        
       -    @classmethod
       -    def normalize_passphrase(self, passphrase):
       -        return normalize('NFKD', unicode(passphrase or ''))
       -
       -    def is_valid_seed(self, seed):
       -        return True
       -
       -    def mnemonic_to_seed(self, mnemonic, passphrase):
       -        # See BIP39
       -        import pbkdf2, hashlib, hmac
       -        PBKDF2_ROUNDS = 2048
       -        mnemonic = normalize('NFKD', ' '.join(mnemonic.split()))
       -        passphrase = self.normalize_passphrase(passphrase)
       -        return pbkdf2.PBKDF2(mnemonic, 'mnemonic' + passphrase,
       -                             iterations = PBKDF2_ROUNDS, macmodule = hmac,
       -                             digestmodule = hashlib.sha512).read(64)
       +def bip39_normalize_passphrase(passphrase):
       +    return normalize('NFKD', unicode(passphrase or ''))
        
       -    def on_restore_wallet(self, wizard):
       -        #assert isinstance(keystore, self.keystore_class)
       -        #msg = _("Enter the seed for your %s wallet:" % self.device)
       -        #title=_('Restore hardware wallet'),
       -        f = lambda seed: wizard.run('on_restore_seed', seed)
       -        wizard.restore_seed_dialog(run_next=f, is_valid=self.is_valid_seed)
       -
       -    def on_restore_seed(self, wizard, seed):
       -        f = lambda passphrase: wizard.run('on_restore_passphrase', seed, passphrase)
       -        self.device = ''
       -        wizard.request_passphrase(self.device, run_next=f)
       -
       -    def on_restore_passphrase(self, wizard, seed, passphrase):
       -        f = lambda pw: wizard.run('on_restore_password', seed, passphrase, pw)
       -        wizard.request_password(run_next=f)
       -
       -    def on_restore_password(self, wizard, seed, passphrase, password):
       -        self.add_seed_and_xprv(seed, password, passphrase)
       -        self.save(wizard.storage, 'x/')
       +def bip39_to_seed(mnemonic, passphrase):
       +    import pbkdf2, hashlib, hmac
       +    PBKDF2_ROUNDS = 2048
       +    mnemonic = normalize('NFKD', ' '.join(mnemonic.split()))
       +    passphrase = bip39_normalize_passphrase(passphrase)
       +    return pbkdf2.PBKDF2(mnemonic, 'mnemonic' + passphrase,
       +                         iterations = PBKDF2_ROUNDS, macmodule = hmac,
       +                         digestmodule = hashlib.sha512).read(64)
        
        
        
       t@@ -596,7 +536,7 @@ def load_keystore(storage, name):
                k = Imported_KeyStore()
            elif name and name not in [ 'x/', 'x1/' ]:
                k = BIP32_KeyStore()
       -    elif t == 'seed':
       +    elif t in ['seed', 'hw_seed']:
                k = BIP32_KeyStore()
            elif t == 'hardware':
                hw_type = storage.get('hardware_type')
       t@@ -606,8 +546,6 @@ def load_keystore(storage, name):
                        break
                else:
                    raise BaseException('unknown hardware type')
       -    elif t == 'hw_seed':
       -        k = BIP44_KeyStore()
            else:
                raise BaseException('unknown wallet type', t)
            k.load(storage, name)
       t@@ -665,7 +603,9 @@ def from_seed(seed, password):
                keystore.add_seed(seed, password)
            elif is_new_seed(seed):
                keystore = BIP32_KeyStore()
       -        keystore.add_seed_and_xprv(seed, password)
       +        keystore.add_seed(seed, password)
       +        bip32_seed = Mnemonic.mnemonic_to_seed(seed, '')
       +        keystore.add_xprv_from_seed(bip32_seed, "m/", password)
            return keystore
        
        def from_private_key_list(text, password):
   DIR diff --git a/plugins/trezor/clientbase.py b/plugins/trezor/clientbase.py
       t@@ -3,7 +3,7 @@ from struct import pack
        
        from electrum.i18n import _
        from electrum.util import PrintError, UserCancelled
       -from electrum.keystore import BIP44_KeyStore
       +from electrum.keystore import bip39_normalize_passphrase
        from electrum.bitcoin import EncodeBase58Check
        
        
       t@@ -65,7 +65,7 @@ class GuiMixin(object):
                passphrase = self.handler.get_passphrase(msg, self.creating_wallet)
                if passphrase is None:
                    return self.proto.Cancel()
       -        passphrase = BIP44_KeyStore.normalize_passphrase(passphrase)
       +        passphrase = bip39_normalize_passphrase(passphrase)
                return self.proto.PassphraseAck(passphrase=passphrase)
        
            def callback_WordRequest(self, msg):
   DIR diff --git a/plugins/trezor/plugin.py b/plugins/trezor/plugin.py
       t@@ -22,14 +22,13 @@ from ..hw_wallet import HW_PluginBase
        TIM_NEW, TIM_RECOVER, TIM_MNEMONIC, TIM_PRIVKEY = range(0, 4)
        
        class TrezorCompatibleKeyStore(Hardware_KeyStore):
       -    root = "m/44'/0'"
       -    account_id = 0
        
            def load(self, storage, name):
                self.xpub = storage.get('master_public_keys', {}).get(name)
       +        self.account_id = storage.get('account_id')
        
            def get_derivation(self):
       -        return self.root + "/%d'"%self.account_id
       +        return "m/44'/0'/%d'"%self.account_id
        
            def get_client(self, force_pair=True):
                return self.plugin.get_client(self, force_pair)