URI: 
       tabstract and improve seed and key methods - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 5d9b9492e16fce6c61332dd1cc3a37978a033ad1
   DIR parent 41f9da1559f838fd191eb936359b911635010d2a
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Mon, 21 Apr 2014 22:09:15 +0200
       
       abstract and improve seed and key methods
       
       Diffstat:
         M electrum                            |      11 ++++++-----
         M gui/qt/installwizard.py             |      33 +++++++++++++------------------
         M gui/qt/password_dialog.py           |       8 ++++----
         M lib/wallet.py                       |     173 ++++++++++++++-----------------
       
       4 files changed, 101 insertions(+), 124 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -273,15 +273,16 @@ if __name__ == '__main__':
                else:
                    if not config.get('2of3'):
                        wallet = Wallet(storage)
       -                wallet.init_seed(None)
       -                wallet.save_seed(password)
       +                seed = wallet.make_seed()
       +                wallet.save_seed(seed, password)
       +                wallet.create_accounts(password)
                        wallet.synchronize()
       -                print_msg("Your wallet generation seed is:\n\"%s\"" % wallet.get_mnemonic(password))
       +                print_msg("Your wallet generation seed is:\n\"%s\"" % seed)
                        print_msg("Please keep it in a safe place; if you lose it, you will not be able to restore your wallet.")
                    else:
                        wallet = Wallet_2of3(storage)
       -                cold_seed = wallet.init_cold_seed()
       -                wallet.save_cold_seed()
       +                cold_seed = wallet.make_seed()
       +                #wallet.save_seed()
                        print_msg("Your cold seed is:\n\"%s\"" % cold_seed)
                        print_msg("Please store it on paper. ")
                        print_msg("Open this file on your online computer to complete your wallet creation.")
   DIR diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py
       t@@ -219,12 +219,12 @@ class InstallWizard(QDialog):
                return self.exec_()
        
        
       -    def password_dialog(self, wallet):
       +    def password_dialog(self):
                msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
                      +_("Leave these fields empty if you want to disable encryption.")
                from password_dialog import make_password_dialog, run_password_dialog
       -        self.set_layout( make_password_dialog(self, wallet, msg) )
       -        return run_password_dialog(self, wallet, self)
       +        self.set_layout( make_password_dialog(self, None, msg) )
       +        return run_password_dialog(self, None, self)[2]
        
        
            def choose_wallet_type(self):
       t@@ -285,16 +285,14 @@ class InstallWizard(QDialog):
                if action in ['create', 'create2of3']:
        
                    wallet = Wallet(self.storage)
       -
       -            wallet.init_seed(None)
       -            seed = wallet.get_mnemonic(None)
       +            seed = wallet.make_seed()
                    sid = 'hot' if action == 'create2of3' else None
                    if not self.show_seed(seed, sid):
                        return
                    if not self.verify_seed(seed, sid):
                        return
       -            ok, old_password, password = self.password_dialog(wallet)
       -            wallet.save_seed(password)
       +            password = self.password_dialog()
       +            wallet.save_seed(seed, password)
        
                    if action == 'create2of3':
                        run_hook('create_third_key', wallet, self)
       t@@ -306,8 +304,6 @@ class InstallWizard(QDialog):
                    self.waiting_dialog(wallet.synchronize)
        
                elif action == 'restore':
       -            # dialog box will accept either seed or xpub. 
       -            # use two boxes for 2of3
                    t = self.choose_wallet_type()
                    if not t: 
                        return
       t@@ -315,9 +311,9 @@ class InstallWizard(QDialog):
                    if t == 'standard':
                        text = self.enter_seed_dialog(True, None)
                        if Wallet.is_seed(text):
       +                    password = self.password_dialog()
                            wallet = Wallet.from_seed(text, self.storage)
       -                    ok, old_password, password = self.password_dialog(wallet)
       -                    wallet.save_seed(password)
       +                    wallet.save_seed(text, password)
                            wallet.create_accounts(password)
                        elif Wallet.is_mpk(text):
                            wallet = Wallet.from_mpk(text, self.storage)
       t@@ -329,19 +325,18 @@ class InstallWizard(QDialog):
                        if not r: 
                            return
                        text1, text2 = r
       +                password = self.password_dialog()
                        wallet = Wallet_2of3(self.storage)
        
                        if Wallet.is_seed(text1):
       -                    xpriv, xpub = bip32_root(text1)
       +                    wallet.add_root("m/", text1, password)
                        elif Wallet.is_mpk(text1):
       -                    xpub = text1
       -                wallet.add_master_public_key("m/", xpub)
       -
       +                    wallet.add_master_public_key("m/", text1)
       +                
                        if Wallet.is_seed(text2):
       -                    xpriv2, xpub2 = bip32_root(text2)
       +                    wallet.add_root("cold/", text2, password)
                        elif Wallet.is_mpk(text2):
       -                    xpub2 = text2
       -                wallet.add_master_public_key("cold/", xpub2)
       +                    wallet.add_master_public_key("cold/", text2)
        
                        run_hook('restore_third_key', wallet, self)
        
   DIR diff --git a/gui/qt/password_dialog.py b/gui/qt/password_dialog.py
       t@@ -42,7 +42,7 @@ def make_password_dialog(self, wallet, msg):
            grid.setColumnStretch(1,1)
        
            logo = QLabel()
       -    lockfile = ":icons/lock.png" if wallet.use_encryption else ":icons/unlock.png"
       +    lockfile = ":icons/lock.png" if wallet and wallet.use_encryption else ":icons/unlock.png"
            logo.setPixmap(QPixmap(lockfile).scaledToWidth(36))
            logo.setAlignment(Qt.AlignCenter)
        
       t@@ -55,7 +55,7 @@ def make_password_dialog(self, wallet, msg):
            grid.setColumnMinimumWidth(0, 250)
            grid.setColumnStretch(1,1)
            
       -    if wallet.use_encryption:
       +    if wallet and wallet.use_encryption:
                grid.addWidget(QLabel(_('Password')), 0, 0)
                grid.addWidget(self.pw, 0, 1)
                
       t@@ -73,14 +73,14 @@ def make_password_dialog(self, wallet, msg):
        
        def run_password_dialog(self, wallet, parent):
                
       -    if not wallet.seed:
       +    if wallet and not wallet.seed:
                QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
                return False, None, None
        
            if not self.exec_():
                return False, None, None
        
       -    password = unicode(self.pw.text()) if wallet.use_encryption else None
       +    password = unicode(self.pw.text()) if wallet and wallet.use_encryption else None
            new_password = unicode(self.new_pw.text())
            new_password2 = unicode(self.conf_pw.text())
        
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -295,27 +295,22 @@ class NewWallet:
                return seed
        
        
       -    def init_seed(self, seed):
       -        import mnemonic, unicodedata
       -        
       -        if self.seed: 
       -            raise Exception("a seed exists")
       -
       -        self.seed_version = NEW_SEED_VERSION
       -
       -        if not seed:
       -            self.seed = self.make_seed()
       -            return
       -
       -        self.seed = unicodedata.normalize('NFC', unicode(seed.strip()))
       +    def prepare_seed(self, seed):
       +        import unicodedata
       +        return NEW_SEED_VERSION, unicodedata.normalize('NFC', unicode(seed.strip()))
        
        
       -            
       -
       -    def save_seed(self, password):
       +    def save_seed(self, seed, password):
       +        if self.seed: 
       +            raise Exception("a seed exists")
       +        
       +        self.seed_version, self.seed = self.prepare_seed(seed)
                if password: 
                    self.seed = pw_encode( self.seed, password)
                    self.use_encryption = True
       +        else:
       +            self.use_encryption = False
       +
                self.storage.put('seed', self.seed, True)
                self.storage.put('seed_version', self.seed_version, True)
                self.storage.put('use_encryption', self.use_encryption,True)
       t@@ -323,11 +318,11 @@ class NewWallet:
        
        
            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)
       -        account = BIP32_Account({'xpub':xpub})
       -        self.add_account("m/", account)
       +        self.add_master_public_key("m/", xpub)
       +        xpub0 = self.add_master_keys("m/", "m/0'", None)
       +        account = BIP32_Account({'xpub':xpub0})
       +        self.add_account("m/0'", account)
        
        
            def create_accounts(self, password):
       t@@ -340,11 +335,37 @@ class NewWallet:
                self.storage.put('master_public_keys', self.master_public_keys, True)
        
        
       +    def add_master_private_key(self, name, xpriv, password):
       +        self.master_private_keys[name] = pw_encode(xpriv, password)
       +        self.storage.put('master_private_keys', self.master_private_keys, True)
       +
       +
       +    def add_master_keys(self, root, account_id, password):
       +        x = self.master_private_keys.get(root)
       +        if x: 
       +            master_xpriv = pw_decode(x, password )
       +            xpriv, xpub = bip32_private_derivation(master_xpriv, root, account_id)
       +            self.add_master_public_key(account_id, xpub)
       +            self.add_master_private_key(account_id, xpriv, password)
       +        else:
       +            master_xpub = self.master_public_keys[root]
       +            xpub = bip32_public_derivation(master_xpub, root, account_id)
       +            self.add_master_public_key(account_id, xpub)
       +        return xpub
       +
       +
       +    def add_root(self, name, mnemonic, password, add_private = True):
       +        seed = mnemonic_to_seed(mnemonic,'').encode('hex')
       +        xpriv, xpub = bip32_root(seed)
       +        self.add_master_public_key(name, xpub)
       +        if add_private:
       +            self.add_master_private_key(name, xpriv, password)
       +
       +
            def create_master_keys(self, password):
                xpriv, xpub = bip32_root(self.get_seed(password))
                self.add_master_public_key("m/", xpub)
       -        self.master_private_keys["m/"] = pw_encode(xpriv, password)
       -        self.storage.put('master_private_keys', self.master_private_keys, True)
       +        self.add_master_private_key("m/", xpriv, password)
        
        
            def find_root_by_master_key(self, xpub):
       t@@ -357,19 +378,19 @@ class NewWallet:
                return (self.seed == '') and (self.master_private_keys == {})
        
        
       -    def num_accounts(self, account_type = '1of1'):
       +    def num_accounts(self):
                keys = self.accounts.keys()
                i = 0
                while True:
       -            account_id = self.account_id(account_type, i)
       +            account_id = self.account_id(i)
                    if account_id not in keys: break
                    i += 1
                return i
        
        
       -    def next_account_address(self, account_type, password):
       -        i = self.num_accounts(account_type)
       -        account_id = self.account_id(account_type, i)
       +    def next_account_address(self, password):
       +        i = self.num_accounts()
       +        account_id = self.account_id(i)
        
                addr = self.next_addresses.get(account_id)
                if not addr: 
       t@@ -380,20 +401,12 @@ class NewWallet:
        
                return account_id, addr
        
       -    def account_id(self, account_type, i):
       -        if account_type == '1of1':
       -            return "m/%d'"%i
       -        else:
       -            raise
       +    def account_id(self, i):
       +        return "m/%d'"%i
        
            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)
       +        xpub = self.add_master_keys("m/", account_id, password)
                account = BIP32_Account({'xpub':xpub})
                return account
        
       t@@ -418,15 +431,15 @@ class NewWallet:
        
        
            def create_account(self, name, password):
       -        i = self.num_accounts('1of1')
       -        account_id = self.account_id('1of1', i)
       +        i = self.num_accounts()
       +        account_id = self.account_id(i)
                account = self.make_account(account_id, password)
                self.add_account(account_id, account)
                if name:
                    self.set_label(account_id, name)
        
                # add address of the next account
       -        _, _ = self.next_account_address('1of1', password)
       +        _, _ = self.next_account_address(password)
        
        
            def add_account(self, account_id, account):
       t@@ -471,8 +484,8 @@ class NewWallet:
            def account_is_pending(self, k):
                return k in self.pending_accounts
        
       -    def create_pending_account(self, acct_type, name, password):
       -        account_id, addr = self.next_account_address(acct_type, password)
       +    def create_pending_account(self, name, password):
       +        account_id, addr = self.next_account_address(password)
                self.set_label(account_id, name)
                self.pending_accounts[account_id] = addr
                self.storage.put('pending_accounts', self.pending_accounts)
       t@@ -1472,32 +1485,16 @@ class Wallet_2of2(NewWallet):
                NewWallet.__init__(self, storage)
                self.storage.put('wallet_type', '2of2', True)
        
       -    def init_cold_seed(self):
       -        cold_seed = self.make_seed()
       -        seed = mnemonic_to_seed(cold_seed,'').encode('hex')
       -        xpriv, xpub = bip32_root(seed)
       -        self.master_public_keys["cold/"] = xpub
       -        return cold_seed
       -
       -    def save_cold_seed(self):
       -        self.storage.put('master_public_keys', self.master_public_keys, True)
       -
        
            def make_account(self, account_id, password):
       -        # if accounts are hardened, we cannot make it symmetric on the other wallet
       -
                """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)
       -
       -        xpub_cold = self.master_public_keys["cold/"]
       -        account = BIP32_Account_2of2({'xpub':xpub, 'xpub2':xpub_cold})
       +        xpub1 = self.add_master_keys("m/", account_id, password)
       +        xpub2 = self.add_master_keys("cold/", account_id, password)
       +        account = BIP32_Account_2of2({'xpub':xpub1, 'xpub2':xpub2})
                return account
        
       +    def account_id(self, i):
       +        return "m/%d"%i
        
        class Wallet_2of3(Wallet_2of2):
        
       t@@ -1506,21 +1503,14 @@ class Wallet_2of3(Wallet_2of2):
                self.storage.put('wallet_type', '2of3', True)
        
            def make_account(self, account_id, password):
       -        # if accounts are hardened, we cannot make it symmetric on the other wallet
       -
       -        """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)
       -
       -        xpub_cold = self.master_public_keys["cold/"]
       -        xpub_remote = self.master_public_keys["remote/"]
       -        account = BIP32_Account_2of3({'xpub':xpub, 'xpub2':xpub_cold, 'xpub3':xpub_remote})
       +        xpub1 = self.add_master_keys("m/", account_id, password)
       +        xpub2 = self.add_master_keys("cold/", account_id.replace("m/","cold/"), password)
       +        xpub3 = self.add_master_keys("remote/", account_id.replace("m/","remote/"), password)
       +        account = BIP32_Account_2of3({'xpub':xpub1, 'xpub2':xpub2, 'xpub3':xpub3})
                return account
        
       +    def account_id(self, i):
       +        return "m/%d"%i
        
        
        
       t@@ -1699,38 +1689,30 @@ class WalletSynchronizer(threading.Thread):
        
        class OldWallet(NewWallet):
        
       -    def init_seed(self, seed):
       +    def make_seed(self):
                import mnemonic
       -        
       -        if self.seed: 
       -            raise Exception("a seed exists")
       +        seed = random_seed(128)
       +        return ' '.join(mnemonic.mn_encode(seed))
        
       -        if not seed:
       -            seed = random_seed(128)
       -
       -        self.seed_version = OLD_SEED_VERSION
        
       +    def prepare_seed(self, seed):
       +        import mnemonic
                # see if seed was entered as hex
                seed = seed.strip()
                try:
                    assert seed
                    seed.decode('hex')
       -            self.seed = str(seed)
       -            return
       +            return OLD_SEED_VERSION, str(seed)
                except Exception:
                    pass
        
                words = seed.split()
       -        try:
       -            mnemonic.mn_decode(words)
       -        except Exception:
       -            raise
       -
       -        self.seed = mnemonic.mn_decode(words)
       -
       -        if not self.seed:
       +        seed = mnemonic.mn_decode(words)
       +        if not seed:
                    raise Exception("Invalid seed")
                    
       +        return OLD_SEED_VERSION, seed
       +
        
            def create_master_keys(self, password):
                seed = pw_decode(self.seed, password)
       t@@ -1886,7 +1868,6 @@ class Wallet(object):
                elif is_new_seed(seed):
                    klass = NewWallet
                w = klass(storage)
       -        w.init_seed(seed)
                return w
        
            @classmethod