electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 5d9b9492e16fce6c61332dd1cc3a37978a033ad1
parent 41f9da1559f838fd191eb936359b911635010d2a
Author: ThomasV <thomasv@gitorious>
Date:   Mon, 21 Apr 2014 22:09:15 +0200

abstract and improve seed and key methods

Diffstat:
Melectrum | 11++++++-----
Mgui/qt/installwizard.py | 33++++++++++++++-------------------
Mgui/qt/password_dialog.py | 8++++----
Mlib/wallet.py | 173+++++++++++++++++++++++++++++++++++--------------------------------------------
4 files changed, 101 insertions(+), 124 deletions(-)

diff --git 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.") diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py @@ -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) diff --git a/gui/qt/password_dialog.py b/gui/qt/password_dialog.py @@ -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()) diff --git a/lib/wallet.py b/lib/wallet.py @@ -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