URI: 
       twizard: add password only once all keystores are known - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 3ac357171a09adfdc3c563bd9cdcc0a34a986c51
   DIR parent b907a668ec59ac5500df5137f58201fbd1a8af4e
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Thu, 25 Aug 2016 12:18:51 +0200
       
       wizard: add password only once all keystores are known
       
       Diffstat:
         M lib/base_wizard.py                  |      66 +++++++++++++++++--------------
         M lib/keystore.py                     |      73 ++++++++++++++-----------------
         M plugins/trustedcoin/trustedcoin.py  |      35 +++++++++++++++++++------------
       
       3 files changed, 92 insertions(+), 82 deletions(-)
       ---
   DIR diff --git a/lib/base_wizard.py b/lib/base_wizard.py
       t@@ -159,13 +159,8 @@ class BaseWizard(object):
                self.restore_keys_dialog(title=title, message=message, run_next=self.on_restore_from_key, is_valid=v)
        
            def on_restore_from_key(self, text):
       -        def f(password):
       -            k = keystore.from_keys(text, password)
       -            self.on_keystore(k, password)
       -        if keystore.is_private(text):
       -            self.run('request_password', run_next=f)
       -        else:
       -            f(None)
       +        k = keystore.from_keys(text)
       +        self.on_keystore(k)
        
            def choose_hw_device(self):
                title = _('Hardware Keystore')
       t@@ -228,43 +223,36 @@ class BaseWizard(object):
        
            def on_seed(self, seed, add_passphrase, is_bip39):
                self.is_bip39 = is_bip39
       -        f = lambda x: self.run('on_passphrase', seed, x)
       +        f = lambda x: self.run('create_keystore', seed, x)
                if add_passphrase:
                    self.request_passphrase(run_next=f)
                else:
                    f('')
        
       -    def on_passphrase(self, seed, passphrase):
       -        f = lambda x: self.run('on_password', seed, passphrase, x)
       -        self.request_password(run_next=f)
       -
       -    def on_password(self, seed, passphrase, password):
       +    def create_keystore(self, seed, passphrase):
                if self.is_bip39:
       -            f = lambda account_id: self.run('on_bip44', seed, passphrase, password, account_id)
       +            f = lambda account_id: self.run('on_bip44', seed, passphrase, account_id)
                    self.account_id_dialog(run_next=f)
                else:
       -            k = keystore.from_seed(seed, passphrase, password)
       -            self.on_keystore(k, password)
       +            k = keystore.from_seed(seed, passphrase)
       +            self.on_keystore(k)
        
       -    def on_bip44(self, seed, passphrase, password, account_id):
       +    def on_bip44(self, seed, passphrase, account_id):
                import keystore
                k = keystore.BIP32_KeyStore({})
                bip32_seed = keystore.bip39_to_seed(seed, passphrase)
                derivation = "m/44'/0'/%d'"%account_id
       -        k.add_xprv_from_seed(bip32_seed, derivation, password)
       -        self.on_keystore(k, password)
       +        k.add_xprv_from_seed(bip32_seed, derivation)
       +        self.on_keystore(k)
        
       -    def on_keystore(self, k, password):
       +    def on_keystore(self, k):
                if self.wallet_type == 'standard':
       -            self.storage.put('keystore', k.dump())
       -            self.wallet = Standard_Wallet(self.storage)
       -            self.run('create_addresses')
       +            self.keystores.append(k)
       +            self.run('create_wallet')
                elif self.wallet_type == 'multisig':
       -
                    if k.xpub in map(lambda x: x.xpub, self.keystores):
                        raise BaseException('duplicate key')
                    self.keystores.append(k)
       -
                    if len(self.keystores) == 1:
                        xpub = k.get_master_public_key()
                        self.stack = []
       t@@ -272,11 +260,29 @@ class BaseWizard(object):
                    elif len(self.keystores) < self.n:
                        self.run('choose_keystore')
                    else:
       -                for i, k in enumerate(self.keystores):
       -                    self.storage.put('x%d/'%(i+1), k.dump())
       -                self.storage.write()
       -                self.wallet = Multisig_Wallet(self.storage)
       -                self.run('create_addresses')
       +                self.run('create_wallet')
       +
       +    def create_wallet(self):
       +        if any(k.may_have_password() for k in self.keystores):
       +            self.request_password(run_next=self.on_password)
       +        else:
       +            self.on_password(None)
       +
       +    def on_password(self, password):
       +        self.storage.put('use_encryption', bool(password))
       +        for k in self.keystores:
       +            if k.may_have_password():
       +                k.update_password(None, password)
       +        if self.wallet_type == 'standard':
       +            self.storage.put('keystore', k.dump())
       +            self.wallet = Standard_Wallet(self.storage)
       +            self.run('create_addresses')
       +        elif self.wallet_type == 'multisig':
       +            for i, k in enumerate(self.keystores):
       +                self.storage.put('x%d/'%(i+1), k.dump())
       +            self.storage.write()
       +            self.wallet = Multisig_Wallet(self.storage)
       +            self.run('create_addresses')
        
            def show_xpub_and_add_cosigners(self, xpub):
                self.show_xpub_dialog(xpub=xpub, run_next=lambda x: self.run('choose_keystore'))
   DIR diff --git a/lib/keystore.py b/lib/keystore.py
       t@@ -49,12 +49,14 @@ class KeyStore(PrintError):
            def can_import(self):
                return False
        
       -
        class Software_KeyStore(KeyStore):
        
            def __init__(self):
                KeyStore.__init__(self)
        
       +    def may_have_password(self):
       +        return not self.is_watching_only()
       +
            def sign_message(self, sequence, message, password):
                sec = self.get_private_key(sequence, password)
                key = regenerate_key(sec)
       t@@ -161,12 +163,10 @@ class Deterministic_KeyStore(Software_KeyStore):
            def can_change_password(self):
                return not self.is_watching_only()
        
       -    def add_seed(self, seed, password):
       +    def add_seed(self, seed):
                if self.seed:
                    raise Exception("a seed exists")
                self.seed_version, self.seed = self.format_seed(seed)
       -        if password:
       -            self.seed = pw_encode(self.seed, password)
        
            def get_seed(self, password):
                return pw_decode(self.seed, password).encode('utf8')
       t@@ -179,9 +179,6 @@ class Xpub:
                self.xpub_receive = None
                self.xpub_change = None
        
       -    def add_master_public_key(self, xpub):
       -        self.xpub = xpub
       -
            def get_master_public_key(self):
                return self.xpub
        
       t@@ -237,9 +234,6 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
                d['xprv'] = self.xprv
                return d
        
       -    def add_master_private_key(self, xprv, password):
       -        self.xprv = pw_encode(xprv, password)
       -
            def get_master_private_key(self, password):
                return pw_decode(self.xprv, password)
        
       t@@ -297,15 +291,14 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
            def get_mnemonic(self, password):
                return self.get_seed(password)
        
       -    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(self, xprv):
       +        self.xprv = xprv
       +        self.xpub = bitcoin.xpub_from_xprv(xprv)
        
       -    def add_xprv_from_seed(self, bip32_seed, derivation, password):
       +    def add_xprv_from_seed(self, bip32_seed, derivation):
                xprv, xpub = bip32_root(bip32_seed)
                xprv, xpub = bip32_private_derivation(xprv, "m/", derivation)
       -        self.add_xprv(xprv, password)
       +        self.add_xprv(xprv)
        
            def can_sign(self, xpub):
                return xpub == self.xpub and self.xprv is not None
       t@@ -328,9 +321,9 @@ class Old_KeyStore(Deterministic_KeyStore):
                d['mpk'] = self.mpk.encode('hex')
                return d
        
       -    def add_seed(self, seed, password):
       -        Deterministic_KeyStore.add_seed(self, seed, password)
       -        self.mpk = self.mpk_from_seed(self.get_seed(password))
       +    def add_seed(self, seed):
       +        Deterministic_KeyStore.add_seed(self, seed)
       +        self.mpk = self.mpk_from_seed(seed)
        
            def add_master_public_key(self, mpk):
                self.mpk = mpk.decode('hex')
       t@@ -469,6 +462,9 @@ class Hardware_KeyStore(KeyStore, Xpub):
                self.derivation = d.get('derivation')
                self.handler = None
        
       +    def may_have_password(self):
       +        return False
       +
            def is_deterministic(self):
                return True
        
       t@@ -623,22 +619,21 @@ is_bip32_key = lambda x: is_xprv(x) or is_xpub(x)
        def bip44_derivation(account_id):
            return "m/44'/0'/%d'"% int(account_id)
        
       -def from_seed(seed, passphrase, password):
       +def from_seed(seed, passphrase):
            if is_old_seed(seed):
                keystore = Old_KeyStore({})
       -        keystore.add_seed(seed, password)
       +        keystore.add_seed(seed)
            elif is_new_seed(seed):
                keystore = BIP32_KeyStore({})
       -        keystore.add_seed(seed, password)
       +        keystore.add_seed(seed)
                bip32_seed = Mnemonic.mnemonic_to_seed(seed, passphrase)
       -        keystore.add_xprv_from_seed(bip32_seed, "m/", password)
       +        keystore.add_xprv_from_seed(bip32_seed, "m/")
            return keystore
        
       -def from_private_key_list(text, password):
       +def from_private_key_list(text):
            keystore = Imported_KeyStore({})
            for x in text.split():
                keystore.import_key(x, None)
       -    keystore.update_password(None, password)
            return keystore
        
        def from_old_mpk(mpk):
       t@@ -647,36 +642,36 @@ def from_old_mpk(mpk):
            return keystore
        
        def from_xpub(xpub):
       -    keystore = BIP32_KeyStore({})
       -    keystore.add_master_public_key(xpub)
       -    return keystore
       +    k = BIP32_KeyStore({})
       +    k.xpub = xpub
       +    return k
        
       -def from_xprv(xprv, password):
       +def from_xprv(xprv):
            xpub = bitcoin.xpub_from_xprv(xprv)
       -    keystore = BIP32_KeyStore({})
       -    keystore.add_master_private_key(xprv, password)
       -    keystore.add_master_public_key(xpub)
       -    return keystore
       +    k = BIP32_KeyStore({})
       +    k.xprv = xprv
       +    k.xpub = xpub
       +    return k
        
       -def xprv_from_seed(seed, password):
       +def xprv_from_seed(seed):
            # do not store the seed, only the master xprv
            xprv, xpub = bip32_root(Mnemonic.mnemonic_to_seed(seed, ''))
       -    return from_xprv(xprv, password)
       +    return from_xprv(xprv)
        
       -def xpub_from_seed(seed, passphrase):
       +def xpub_from_seed(seed):
            # store only master xpub
            xprv, xpub = bip32_root(Mnemonic.mnemonic_to_seed(seed,''))
            return from_xpub(xpub)
        
       -def from_keys(text, password):
       +def from_keys(text):
            if is_xprv(text):
       -        k = from_xprv(text, password)
       +        k = from_xprv(text)
            elif is_old_mpk(text):
                k = from_old_mpk(text)
            elif is_xpub(text):
                k = from_xpub(text)
            elif is_private_key_list(text):
       -        k = from_private_key_list(text, password)
       +        k = from_private_key_list(text)
            else:
                raise BaseException('Invalid key')
            return k
   DIR diff --git a/plugins/trustedcoin/trustedcoin.py b/plugins/trustedcoin/trustedcoin.py
       t@@ -352,14 +352,20 @@ class TrustedCoinPlugin(BasePlugin):
                seed = self.make_seed()
                wizard.show_seed_dialog(run_next=wizard.confirm_seed, seed_text=seed)
        
       -    def create_keystore(self, wizard, seed, password):
       +    def create_keystore(self, wizard, seed, passphrase):
       +        assert passphrase == ''
                # this overloads the wizard's method
                words = seed.split()
                n = len(words)/2
       -        keystore1 = keystore.xprv_from_seed(' '.join(words[0:n]), password)
       -        keystore2 = keystore.xpub_from_seed(' '.join(words[n:]))
       -        wizard.storage.put('x1/', keystore1.dump())
       -        wizard.storage.put('x2/', keystore2.dump())
       +        k1 = keystore.xprv_from_seed(' '.join(words[0:n]))
       +        k2 = keystore.xpub_from_seed(' '.join(words[n:]))
       +        wizard.request_password(run_next=lambda pw: self.on_password(wizard, pw, k1, k2))
       +
       +    def on_password(self, wizard, password, k1, k2):
       +        k1.update_password(None, password)
       +        wizard.storage.put('use_encryption', bool(password))
       +        wizard.storage.put('x1/', k1.dump())
       +        wizard.storage.put('x2/', k2.dump())
                wizard.storage.write()
                msg = [
                    _("Your wallet file is: %s.")%os.path.abspath(wizard.storage.path),
       t@@ -389,14 +395,17 @@ class TrustedCoinPlugin(BasePlugin):
                storage = wizard.storage
                words = seed.split()
                n = len(words)/2
       -        keystore1 = keystore.xprv_from_seed(' '.join(words[0:n]), password)
       -        keystore2 = keystore.xprv_from_seed(' '.join(words[n:]), password)
       -        storage.put('x1/', keystore1.dump())
       -        storage.put('x2/', keystore2.dump())
       +        k1 = keystore.xprv_from_seed(' '.join(words[0:n]))
       +        k2 = keystore.xprv_from_seed(' '.join(words[n:]))
       +        k1.update_password(None, password)
       +        k2.update_password(None, password)
                long_user_id, short_id = get_user_id(storage)
                xpub3 = make_xpub(signing_xpub, long_user_id)
       -        keystore3 = keystore.from_xpub(xpub3)
       -        storage.put('x3/', keystore3.dump())
       +        k3 = keystore.from_xpub(xpub3)
       +        storage.put('use_encryption', bool(password))
       +        storage.put('x1/', k1.dump())
       +        storage.put('x2/', k2.dump())
       +        storage.put('x3/', k3.dump())
                wizard.wallet = Wallet(storage)
                wizard.create_addresses()
        
       t@@ -436,8 +445,8 @@ class TrustedCoinPlugin(BasePlugin):
                if not self.setup_google_auth(wizard, short_id, otp_secret):
                    wizard.show_message("otp error")
                    return
       -        keystore3 = keystore.from_xpub(xpub3)
       -        wizard.storage.put('x3/', keystore3.dump())
       +        k3 = keystore.from_xpub(xpub3)
       +        wizard.storage.put('x3/', k3.dump())
                wizard.storage.put('use_trustedcoin', True)
                wizard.storage.write()
                wizard.wallet = Wallet(wizard.storage)