abstract and improve seed and key methods
authorThomasV <thomasv@gitorious>
Mon, 21 Apr 2014 20:09:15 +0000 (22:09 +0200)
committerThomasV <thomasv@gitorious>
Mon, 21 Apr 2014 20:09:15 +0000 (22:09 +0200)
electrum
gui/qt/installwizard.py
gui/qt/password_dialog.py
lib/wallet.py

index e834e08..b98acec 100755 (executable)
--- a/electrum
+++ b/electrum
@@ -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.")
index 247e07a..9912dd1 100644 (file)
@@ -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):
@@ -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)
@@ -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
@@ -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)
@@ -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)
 
index 61df771..5de13e8 100644 (file)
@@ -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)
 
@@ -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)
         
@@ -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())
 
index 190b2d7..2da22fe 100644 (file)
@@ -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)
@@ -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):
@@ -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):
@@ -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: 
@@ -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
 
@@ -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):
@@ -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)
@@ -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):
 
@@ -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
 
 
 
@@ -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)
@@ -1886,7 +1868,6 @@ class Wallet(object):
         elif is_new_seed(seed):
             klass = NewWallet
         w = klass(storage)
-        w.init_seed(seed)
         return w
 
     @classmethod