abstract and improve seed and key methods

This commit is contained in:
ThomasV 2014-04-21 22:09:15 +02:00
parent 41f9da1559
commit 5d9b9492e1
4 changed files with 101 additions and 124 deletions

View file

@ -273,15 +273,16 @@ if __name__ == '__main__':
else: else:
if not config.get('2of3'): if not config.get('2of3'):
wallet = Wallet(storage) wallet = Wallet(storage)
wallet.init_seed(None) seed = wallet.make_seed()
wallet.save_seed(password) wallet.save_seed(seed, password)
wallet.create_accounts(password)
wallet.synchronize() 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.") print_msg("Please keep it in a safe place; if you lose it, you will not be able to restore your wallet.")
else: else:
wallet = Wallet_2of3(storage) wallet = Wallet_2of3(storage)
cold_seed = wallet.init_cold_seed() cold_seed = wallet.make_seed()
wallet.save_cold_seed() #wallet.save_seed()
print_msg("Your cold seed is:\n\"%s\"" % cold_seed) print_msg("Your cold seed is:\n\"%s\"" % cold_seed)
print_msg("Please store it on paper. ") print_msg("Please store it on paper. ")
print_msg("Open this file on your online computer to complete your wallet creation.") print_msg("Open this file on your online computer to complete your wallet creation.")

View file

@ -219,12 +219,12 @@ class InstallWizard(QDialog):
return self.exec_() return self.exec_()
def password_dialog(self, wallet): def password_dialog(self):
msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\ msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
+_("Leave these fields empty if you want to disable encryption.") +_("Leave these fields empty if you want to disable encryption.")
from password_dialog import make_password_dialog, run_password_dialog from password_dialog import make_password_dialog, run_password_dialog
self.set_layout( make_password_dialog(self, wallet, msg) ) self.set_layout( make_password_dialog(self, None, msg) )
return run_password_dialog(self, wallet, self) return run_password_dialog(self, None, self)[2]
def choose_wallet_type(self): def choose_wallet_type(self):
@ -285,16 +285,14 @@ class InstallWizard(QDialog):
if action in ['create', 'create2of3']: if action in ['create', 'create2of3']:
wallet = Wallet(self.storage) wallet = Wallet(self.storage)
seed = wallet.make_seed()
wallet.init_seed(None)
seed = wallet.get_mnemonic(None)
sid = 'hot' if action == 'create2of3' else None sid = 'hot' if action == 'create2of3' else None
if not self.show_seed(seed, sid): if not self.show_seed(seed, sid):
return return
if not self.verify_seed(seed, sid): if not self.verify_seed(seed, sid):
return return
ok, old_password, password = self.password_dialog(wallet) password = self.password_dialog()
wallet.save_seed(password) wallet.save_seed(seed, password)
if action == 'create2of3': if action == 'create2of3':
run_hook('create_third_key', wallet, self) run_hook('create_third_key', wallet, self)
@ -306,8 +304,6 @@ class InstallWizard(QDialog):
self.waiting_dialog(wallet.synchronize) self.waiting_dialog(wallet.synchronize)
elif action == 'restore': elif action == 'restore':
# dialog box will accept either seed or xpub.
# use two boxes for 2of3
t = self.choose_wallet_type() t = self.choose_wallet_type()
if not t: if not t:
return return
@ -315,9 +311,9 @@ class InstallWizard(QDialog):
if t == 'standard': if t == 'standard':
text = self.enter_seed_dialog(True, None) text = self.enter_seed_dialog(True, None)
if Wallet.is_seed(text): if Wallet.is_seed(text):
password = self.password_dialog()
wallet = Wallet.from_seed(text, self.storage) wallet = Wallet.from_seed(text, self.storage)
ok, old_password, password = self.password_dialog(wallet) wallet.save_seed(text, password)
wallet.save_seed(password)
wallet.create_accounts(password) wallet.create_accounts(password)
elif Wallet.is_mpk(text): elif Wallet.is_mpk(text):
wallet = Wallet.from_mpk(text, self.storage) wallet = Wallet.from_mpk(text, self.storage)
@ -329,19 +325,18 @@ class InstallWizard(QDialog):
if not r: if not r:
return return
text1, text2 = r text1, text2 = r
password = self.password_dialog()
wallet = Wallet_2of3(self.storage) wallet = Wallet_2of3(self.storage)
if Wallet.is_seed(text1): if Wallet.is_seed(text1):
xpriv, xpub = bip32_root(text1) wallet.add_root("m/", text1, password)
elif Wallet.is_mpk(text1): elif Wallet.is_mpk(text1):
xpub = text1 wallet.add_master_public_key("m/", text1)
wallet.add_master_public_key("m/", xpub)
if Wallet.is_seed(text2): if Wallet.is_seed(text2):
xpriv2, xpub2 = bip32_root(text2) wallet.add_root("cold/", text2, password)
elif Wallet.is_mpk(text2): elif Wallet.is_mpk(text2):
xpub2 = text2 wallet.add_master_public_key("cold/", text2)
wallet.add_master_public_key("cold/", xpub2)
run_hook('restore_third_key', wallet, self) run_hook('restore_third_key', wallet, self)

View file

@ -42,7 +42,7 @@ def make_password_dialog(self, wallet, msg):
grid.setColumnStretch(1,1) grid.setColumnStretch(1,1)
logo = QLabel() 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.setPixmap(QPixmap(lockfile).scaledToWidth(36))
logo.setAlignment(Qt.AlignCenter) logo.setAlignment(Qt.AlignCenter)
@ -55,7 +55,7 @@ def make_password_dialog(self, wallet, msg):
grid.setColumnMinimumWidth(0, 250) grid.setColumnMinimumWidth(0, 250)
grid.setColumnStretch(1,1) grid.setColumnStretch(1,1)
if wallet.use_encryption: if wallet and wallet.use_encryption:
grid.addWidget(QLabel(_('Password')), 0, 0) grid.addWidget(QLabel(_('Password')), 0, 0)
grid.addWidget(self.pw, 0, 1) grid.addWidget(self.pw, 0, 1)
@ -73,14 +73,14 @@ def make_password_dialog(self, wallet, msg):
def run_password_dialog(self, wallet, parent): def run_password_dialog(self, wallet, parent):
if not wallet.seed: if wallet and not wallet.seed:
QMessageBox.information(parent, _('Error'), _('No seed'), _('OK')) QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
return False, None, None return False, None, None
if not self.exec_(): if not self.exec_():
return False, None, None 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_password = unicode(self.new_pw.text())
new_password2 = unicode(self.conf_pw.text()) new_password2 = unicode(self.conf_pw.text())

View file

@ -295,27 +295,22 @@ class NewWallet:
return seed return seed
def init_seed(self, seed): def prepare_seed(self, seed):
import mnemonic, unicodedata import unicodedata
return NEW_SEED_VERSION, unicodedata.normalize('NFC', unicode(seed.strip()))
def save_seed(self, seed, password):
if self.seed: if self.seed:
raise Exception("a seed exists") raise Exception("a seed exists")
self.seed_version = NEW_SEED_VERSION self.seed_version, self.seed = self.prepare_seed(seed)
if not seed:
self.seed = self.make_seed()
return
self.seed = unicodedata.normalize('NFC', unicode(seed.strip()))
def save_seed(self, password):
if password: if password:
self.seed = pw_encode( self.seed, password) self.seed = pw_encode( self.seed, password)
self.use_encryption = True self.use_encryption = True
else:
self.use_encryption = False
self.storage.put('seed', self.seed, True) self.storage.put('seed', self.seed, True)
self.storage.put('seed_version', self.seed_version, True) self.storage.put('seed_version', self.seed_version, True)
self.storage.put('use_encryption', self.use_encryption,True) self.storage.put('use_encryption', self.use_encryption,True)
@ -323,11 +318,11 @@ class NewWallet:
def create_watching_only_wallet(self, xpub): 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) self.storage.put('seed_version', self.seed_version, True)
account = BIP32_Account({'xpub':xpub}) self.add_master_public_key("m/", xpub)
self.add_account("m/", account) 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): def create_accounts(self, password):
@ -340,11 +335,37 @@ class NewWallet:
self.storage.put('master_public_keys', self.master_public_keys, True) 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): def create_master_keys(self, password):
xpriv, xpub = bip32_root(self.get_seed(password)) xpriv, xpub = bip32_root(self.get_seed(password))
self.add_master_public_key("m/", xpub) self.add_master_public_key("m/", xpub)
self.master_private_keys["m/"] = pw_encode(xpriv, password) self.add_master_private_key("m/", xpriv, password)
self.storage.put('master_private_keys', self.master_private_keys, True)
def find_root_by_master_key(self, xpub): def find_root_by_master_key(self, xpub):
@ -357,19 +378,19 @@ class NewWallet:
return (self.seed == '') and (self.master_private_keys == {}) return (self.seed == '') and (self.master_private_keys == {})
def num_accounts(self, account_type = '1of1'): def num_accounts(self):
keys = self.accounts.keys() keys = self.accounts.keys()
i = 0 i = 0
while True: while True:
account_id = self.account_id(account_type, i) account_id = self.account_id(i)
if account_id not in keys: break if account_id not in keys: break
i += 1 i += 1
return i return i
def next_account_address(self, account_type, password): def next_account_address(self, password):
i = self.num_accounts(account_type) i = self.num_accounts()
account_id = self.account_id(account_type, i) account_id = self.account_id(i)
addr = self.next_addresses.get(account_id) addr = self.next_addresses.get(account_id)
if not addr: if not addr:
@ -380,20 +401,12 @@ class NewWallet:
return account_id, addr return account_id, addr
def account_id(self, account_type, i): def account_id(self, i):
if account_type == '1of1': return "m/%d'"%i
return "m/%d'"%i
else:
raise
def make_account(self, account_id, password): def make_account(self, account_id, password):
"""Creates and saves the master keys, but does not save the account""" """Creates and saves the master keys, but does not save the account"""
master_xpriv = pw_decode( self.master_private_keys["m/"] , password ) xpub = self.add_master_keys("m/", account_id, 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)
account = BIP32_Account({'xpub':xpub}) account = BIP32_Account({'xpub':xpub})
return account return account
@ -418,15 +431,15 @@ class NewWallet:
def create_account(self, name, password): def create_account(self, name, password):
i = self.num_accounts('1of1') i = self.num_accounts()
account_id = self.account_id('1of1', i) account_id = self.account_id(i)
account = self.make_account(account_id, password) account = self.make_account(account_id, password)
self.add_account(account_id, account) self.add_account(account_id, account)
if name: if name:
self.set_label(account_id, name) self.set_label(account_id, name)
# add address of the next account # add address of the next account
_, _ = self.next_account_address('1of1', password) _, _ = self.next_account_address(password)
def add_account(self, account_id, account): def add_account(self, account_id, account):
@ -471,8 +484,8 @@ class NewWallet:
def account_is_pending(self, k): def account_is_pending(self, k):
return k in self.pending_accounts return k in self.pending_accounts
def create_pending_account(self, acct_type, name, password): def create_pending_account(self, name, password):
account_id, addr = self.next_account_address(acct_type, password) account_id, addr = self.next_account_address(password)
self.set_label(account_id, name) self.set_label(account_id, name)
self.pending_accounts[account_id] = addr self.pending_accounts[account_id] = addr
self.storage.put('pending_accounts', self.pending_accounts) self.storage.put('pending_accounts', self.pending_accounts)
@ -1472,32 +1485,16 @@ class Wallet_2of2(NewWallet):
NewWallet.__init__(self, storage) NewWallet.__init__(self, storage)
self.storage.put('wallet_type', '2of2', True) 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): 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""" """Creates and saves the master keys, but does not save the account"""
master_xpriv = pw_decode( self.master_private_keys["m/"] , password ) xpub1 = self.add_master_keys("m/", account_id, password)
xpriv, xpub = bip32_private_derivation(master_xpriv, "m/", account_id) xpub2 = self.add_master_keys("cold/", account_id, password)
self.master_private_keys[account_id] = pw_encode(xpriv, password) account = BIP32_Account_2of2({'xpub':xpub1, 'xpub2':xpub2})
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})
return account return account
def account_id(self, i):
return "m/%d"%i
class Wallet_2of3(Wallet_2of2): class Wallet_2of3(Wallet_2of2):
@ -1506,21 +1503,14 @@ class Wallet_2of3(Wallet_2of2):
self.storage.put('wallet_type', '2of3', True) self.storage.put('wallet_type', '2of3', True)
def make_account(self, account_id, password): def make_account(self, account_id, password):
# if accounts are hardened, we cannot make it symmetric on the other wallet xpub1 = self.add_master_keys("m/", account_id, password)
xpub2 = self.add_master_keys("cold/", account_id.replace("m/","cold/"), password)
"""Creates and saves the master keys, but does not save the account""" xpub3 = self.add_master_keys("remote/", account_id.replace("m/","remote/"), password)
master_xpriv = pw_decode( self.master_private_keys["m/"] , password ) account = BIP32_Account_2of3({'xpub':xpub1, 'xpub2':xpub2, 'xpub3':xpub3})
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})
return account return account
def account_id(self, i):
return "m/%d"%i
@ -1699,38 +1689,30 @@ class WalletSynchronizer(threading.Thread):
class OldWallet(NewWallet): class OldWallet(NewWallet):
def init_seed(self, seed): def make_seed(self):
import mnemonic import mnemonic
seed = random_seed(128)
if self.seed: return ' '.join(mnemonic.mn_encode(seed))
raise Exception("a seed exists")
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 # see if seed was entered as hex
seed = seed.strip() seed = seed.strip()
try: try:
assert seed assert seed
seed.decode('hex') seed.decode('hex')
self.seed = str(seed) return OLD_SEED_VERSION, str(seed)
return
except Exception: except Exception:
pass pass
words = seed.split() words = seed.split()
try: seed = mnemonic.mn_decode(words)
mnemonic.mn_decode(words) if not seed:
except Exception:
raise
self.seed = mnemonic.mn_decode(words)
if not self.seed:
raise Exception("Invalid seed") raise Exception("Invalid seed")
return OLD_SEED_VERSION, seed
def create_master_keys(self, password): def create_master_keys(self, password):
seed = pw_decode(self.seed, password) seed = pw_decode(self.seed, password)
@ -1886,7 +1868,6 @@ class Wallet(object):
elif is_new_seed(seed): elif is_new_seed(seed):
klass = NewWallet klass = NewWallet
w = klass(storage) w = klass(storage)
w.init_seed(seed)
return w return w
@classmethod @classmethod