electrum

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

commit f4b390a79f127d0bbe5c178bd849ec87b85f1a5a
parent 1d4631d6470930224e407bffae884a6b4edc514f
Author: ThomasV <thomasv@gitorious>
Date:   Wed, 13 Aug 2014 16:05:43 +0200

bip44

Diffstat:
Melectrum | 5++---
Mgui/qt/installwizard.py | 42+++++++++++++++++++++---------------------
Mlib/version.py | 7+++++--
Mlib/wallet.py | 188+++++++++++++++++++++++++++++++++++++++----------------------------------------
4 files changed, 121 insertions(+), 121 deletions(-)

diff --git a/electrum b/electrum @@ -87,7 +87,6 @@ def arg_parser(): parser.add_option("-G", "--gap", dest="gap_limit", default=None, help="gap limit") parser.add_option("-W", "--password", dest="password", default=None, help="set password for usage with commands (currently only implemented for create command, do not use it for longrunning gui session since the password is visible in /proc)") parser.add_option("-1", "--oneserver", action="store_true", dest="oneserver", default=False, help="connect to one server only") - parser.add_option("--bip32", action="store_true", dest="bip32", default=False, help="bip32 (not final)") parser.add_option("--2of3", action="store_true", dest="2of3", default=False, help="create 2of3 wallet") parser.add_option("--mpk", dest="mpk", default=False, help="restore from master public key") parser.add_option("-m", action="store_true", dest="hide_gui", default=False, help="hide GUI on startup") @@ -288,7 +287,7 @@ if __name__ == '__main__': sys.exit("Error: Invalid seed") wallet = Wallet.from_seed(seed, storage) wallet.add_seed(seed, password) - wallet.create_accounts(password) + wallet.create_main_account(password) if not options.offline: network = Network(config) @@ -309,7 +308,7 @@ if __name__ == '__main__': wallet = Wallet(storage) seed = wallet.make_seed() wallet.add_seed(seed, password) - wallet.create_accounts(password) + wallet.create_main_account(password) wallet.synchronize() 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.") diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py @@ -404,25 +404,25 @@ class InstallWizard(QDialog): wallet.add_seed(seed, password) elif action == 'add_cosigner': - xpub_hot = wallet.master_public_keys.get("m/") - r = self.multi_mpk_dialog(xpub_hot, 1) + xpub1 = wallet.master_public_keys.get("x1/") + r = self.multi_mpk_dialog(xpub1, 1) if not r: return - xpub_cold = r[0] - wallet.add_master_public_key("cold/", xpub_cold) + xpub2 = r[0] + wallet.add_master_public_key("x2/", xpub2) elif action == 'add_two_cosigners': - xpub_hot = wallet.master_public_keys.get("m/") - r = self.multi_mpk_dialog(xpub_hot, 2) + xpub1 = wallet.master_public_keys.get("x1/") + r = self.multi_mpk_dialog(xpub1, 2) if not r: return - xpub1, xpub2 = r - wallet.add_master_public_key("cold/", xpub1) - wallet.add_master_public_key("remote/", xpub2) + xpub2, xpub3 = r + wallet.add_master_public_key("x2/", xpub2) + wallet.add_master_public_key("x3/", xpub3) elif action == 'create_accounts': try: - wallet.create_accounts(password) + wallet.create_main_account(password) except BaseException as e: QMessageBox.information(None, _('Error'), str(e), _('OK')) return @@ -476,7 +476,7 @@ class InstallWizard(QDialog): password = self.password_dialog() wallet = Wallet.from_seed(text, self.storage) wallet.add_seed(text, password) - wallet.create_accounts(password) + wallet.create_main_account(password) elif Wallet.is_xprv(text): password = self.password_dialog() wallet = Wallet.from_xprv(text, password, self.storage) @@ -507,17 +507,17 @@ class InstallWizard(QDialog): if Wallet.is_seed(text2): wallet.add_cold_seed(text2, password) else: - wallet.add_master_public_key("cold/", text2) + wallet.add_master_public_key("x2/", text2) else: assert Wallet.is_xpub(text1) if Wallet.is_seed(text2): wallet.add_seed(text2, password) - wallet.add_master_public_key("cold/", text1) + wallet.add_master_public_key("x2/", text1) else: - wallet.add_master_public_key("m/", text1) - wallet.add_master_public_key("cold/", text2) + wallet.add_master_public_key("x1/", text1) + wallet.add_master_public_key("x2/", text2) - wallet.create_accounts(password) + wallet.create_main_account(password) elif t in ['2of3']: @@ -536,17 +536,17 @@ class InstallWizard(QDialog): if Wallet.is_seed(text2): wallet.add_cold_seed(text2, password) else: - wallet.add_master_public_key("cold/", text2) + wallet.add_master_public_key("x2/", text2) elif Wallet.is_xpub(text1): if Wallet.is_seed(text2): wallet.add_seed(text2, password) - wallet.add_master_public_key("cold/", text1) + wallet.add_master_public_key("x2/", text1) else: - wallet.add_master_public_key("m/", text1) - wallet.add_master_public_key("cold/", text2) + wallet.add_master_public_key("x1/", text1) + wallet.add_master_public_key("x2/", text2) - wallet.create_accounts(password) + wallet.create_main_account(password) else: wallet = run_hook('installwizard_restore', self, self.storage) diff --git a/lib/version.py b/lib/version.py @@ -1,5 +1,8 @@ ELECTRUM_VERSION = "1.9.8" # version of the client package PROTOCOL_VERSION = '0.9' # protocol version requested -NEW_SEED_VERSION = 7 # bip32 wallets +NEW_SEED_VERSION = 8 # bip32 wallets OLD_SEED_VERSION = 4 # old electrum deterministic generation -SEED_PREFIX = '01' # the hash of the mnemonic seed must begin with this + + +# The hash of the mnemonic seed must begin with this +SEED_PREFIX = '01' # for BIP44 diff --git a/lib/wallet.py b/lib/wallet.py @@ -1204,68 +1204,58 @@ class Deterministic_Wallet(Abstract_Wallet): if not self.accounts: return 'create_accounts' + def get_master_public_keys(self): + out = {} + for k, account in self.accounts.items(): + name = self.get_account_name(k) + mpk_text = '\n\n'.join( account.get_master_pubkeys() ) + out[name] = mpk_text + return out class BIP32_Wallet(Deterministic_Wallet): - # bip32 derivation + # Wallet with a single BIP32 account, no seed + # gap limit 20 def __init__(self, storage): Deterministic_Wallet.__init__(self, storage) self.master_public_keys = storage.get('master_public_keys', {}) self.master_private_keys = storage.get('master_private_keys', {}) + self.gap_limit = 20 def default_account(self): - return self.accounts["m/0'"] + return self.accounts['0'] def is_watching_only(self): return not bool(self.master_private_keys) - def can_create_accounts(self): - return 'm/' in self.master_private_keys.keys() - def get_master_public_key(self): - return self.master_public_keys.get("m/") - - def get_master_public_keys(self): - out = {} - for k, account in self.accounts.items(): - name = self.get_account_name(k) - mpk_text = '\n\n'.join( account.get_master_pubkeys() ) - out[name] = mpk_text - return out + return self.master_public_keys.get(self.root_name) def get_master_private_key(self, account, password): k = self.master_private_keys.get(account) if not k: return - xpriv = pw_decode( k, password) - return xpriv + xprv = pw_decode(k, password) + return xprv def check_password(self, password): - xpriv = self.get_master_private_key( "m/", password ) - xpub = self.master_public_keys["m/"] + xpriv = self.get_master_private_key(self.root_name, password) + xpub = self.master_public_keys[self.root_name] assert deserialize_xkey(xpriv)[3] == deserialize_xkey(xpub)[3] def create_xprv_wallet(self, xprv, password): xpub = bitcoin.xpub_from_xprv(xprv) account = BIP32_Account({'xpub':xpub}) - account_id = 'm/' + bitcoin.get_xkey_name(xpub) self.storage.put('seed_version', self.seed_version, True) - self.add_master_private_key(account_id, xprv, password) - self.add_master_public_key(account_id, xpub) - self.add_account(account_id, account) + self.add_master_private_key(self.root_name, xprv, password) + self.add_master_public_key(self.root_name, xpub) + self.add_account('0', account) def create_xpub_wallet(self, xpub): account = BIP32_Account({'xpub':xpub}) - account_id = 'm/' + bitcoin.get_xkey_name(xpub) self.storage.put('seed_version', self.seed_version, True) - self.add_master_public_key(account_id, xpub) - self.add_account(account_id, account) - - def create_accounts(self, password): - # First check the password is valid (this raises if it isn't). - if not self.is_watching_only(): - self.check_password(password) - self.create_account('Main account', password) + self.add_master_public_key(self.root_name, xpub) + self.add_account('0', account) def add_master_public_key(self, name, xpub): self.master_public_keys[name] = xpub @@ -1275,6 +1265,19 @@ class BIP32_Wallet(Deterministic_Wallet): 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, derivation, password): + x = self.master_private_keys.get(root) + if x: + master_xpriv = pw_decode(x, password ) + xpriv, xpub = bip32_private_derivation(master_xpriv, root, derivation) + self.add_master_public_key(derivation, xpub) + self.add_master_private_key(derivation, xpriv, password) + else: + master_xpub = self.master_public_keys[root] + xpub = bip32_public_derivation(master_xpub, root, derivation) + self.add_master_public_key(derivation, xpub) + return xpub + def can_sign(self, tx): if self.is_watching_only(): return False @@ -1292,11 +1295,19 @@ class BIP32_Wallet(Deterministic_Wallet): class BIP32_HD_Wallet(BIP32_Wallet): - # sequence of accounts + # wallet that can create accounts + + def create_main_account(self, password): + # First check the password is valid (this raises if it isn't). + if not self.is_watching_only(): + self.check_password(password) + self.create_account('Main account', password) + + def can_create_accounts(self): + return self.root_name in self.master_private_keys.keys() def create_account(self, name, password): - i = self.num_accounts() - account_id = self.account_id(i) + account_id = "%d"%self.num_accounts() account = self.make_account(account_id, password) self.add_account(account_id, account) if name: @@ -1328,8 +1339,7 @@ class BIP32_HD_Wallet(BIP32_Wallet): self.next_addresses.pop(account_id) def next_account_address(self, password): - i = self.num_accounts() - account_id = self.account_id(i) + account_id = '%d'%self.num_accounts() addr = self.next_addresses.get(account_id) if not addr: account = self.make_account(account_id, password) @@ -1338,12 +1348,10 @@ class BIP32_HD_Wallet(BIP32_Wallet): self.storage.put('next_addresses', self.next_addresses) return account_id, addr - 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""" - xpub = self.add_master_keys("m/", account_id, password) + derivation = self.root_name + "%d'"%int(account_id) + xpub = self.add_master_keys(self.root_name, derivation, password) account = BIP32_Account({'xpub':xpub}) return account @@ -1355,29 +1363,23 @@ class BIP32_HD_Wallet(BIP32_Wallet): keys.append(k) i = 0 while True: - account_id = self.account_id(i) - if account_id not in keys: break + account_id = '%d'%i + if account_id not in keys: + break i += 1 return i - 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 - - -class NewWallet(BIP32_HD_Wallet): +class BIP39_Wallet(BIP32_Wallet): # BIP39 seed generation + def create_master_keys(self, password): + seed = self.get_seed(password) + xprv, xpub = bip32_root(seed) + xprv, xpub = bip32_private_derivation(xprv, "m/", self.root_derivation) + self.add_master_public_key(self.root_name, xpub) + self.add_master_private_key(self.root_name, xprv, password) + @classmethod def make_seed(self, custom_entropy=1): import mnemonic @@ -1405,45 +1407,41 @@ class NewWallet(BIP32_HD_Wallet): import unicodedata return NEW_SEED_VERSION, unicodedata.normalize('NFC', unicode(seed.strip())) - def create_master_keys(self, password): - seed = self.get_seed(password) - xpriv, xpub = bip32_root(seed) - self.add_master_public_key("m/", xpub) - self.add_master_private_key("m/", xpriv, password) +class NewWallet(BIP32_HD_Wallet, BIP39_Wallet): + # bip 44 + root_name = 'root/' + root_derivation = "m/44'/0'" -class Wallet_2of2(NewWallet): - """ This class is used for multisignature addresses""" +class Wallet_2of2(BIP39_Wallet): + # Wallet with multisig addresses. + # Cannot create accounts + root_name = "x1/" + root_derivation = "m/44'/0'" def __init__(self, storage): - NewWallet.__init__(self, storage) + BIP39_Wallet.__init__(self, storage) self.storage.put('wallet_type', '2of2', True) - def default_account(self): - return self.accounts['m/'] - - def can_create_accounts(self): - return False - def can_import(self): return False - def create_account(self, name, password): - xpub1 = self.master_public_keys.get("m/") - xpub2 = self.master_public_keys.get("cold/") + def create_main_account(self, password): + xpub1 = self.master_public_keys.get("x1/") + xpub2 = self.master_public_keys.get("x2/") account = BIP32_Account_2of2({'xpub':xpub1, 'xpub2':xpub2}) - self.add_account('m/', account) + self.add_account('0', account) def get_master_public_keys(self): - xpub1 = self.master_public_keys.get("m/") - xpub2 = self.master_public_keys.get("cold/") - return {'hot':xpub1, 'cold':xpub2} + xpub1 = self.master_public_keys.get("x1/") + xpub2 = self.master_public_keys.get("x2/") + return {'x1':xpub1, 'x2':xpub2} def get_action(self): - xpub1 = self.master_public_keys.get("m/") - xpub2 = self.master_public_keys.get("cold/") + xpub1 = self.master_public_keys.get("x1/") + xpub2 = self.master_public_keys.get("x2/") if xpub1 is None: return 'create_seed' if xpub2 is None: @@ -1459,23 +1457,23 @@ class Wallet_2of3(Wallet_2of2): Wallet_2of2.__init__(self, storage) self.storage.put('wallet_type', '2of3', True) - def create_account(self, name, password): - xpub1 = self.master_public_keys.get("m/") - xpub2 = self.master_public_keys.get("cold/") - xpub3 = self.master_public_keys.get("remote/") + def create_main_account(self, password): + xpub1 = self.master_public_keys.get("x1/") + xpub2 = self.master_public_keys.get("x2/") + xpub3 = self.master_public_keys.get("x3/") account = BIP32_Account_2of3({'xpub':xpub1, 'xpub2':xpub2, 'xpub3':xpub3}) - self.add_account('m/', account) + self.add_account('0', account) def get_master_public_keys(self): - xpub1 = self.master_public_keys.get("m/") - xpub2 = self.master_public_keys.get("cold/") - xpub3 = self.master_public_keys.get("remote/") - return {'hot':xpub1, 'cold':xpub2, 'remote':xpub3} + xpub1 = self.master_public_keys.get("x1/") + xpub2 = self.master_public_keys.get("x2/") + xpub3 = self.master_public_keys.get("x3/") + return {'x1':xpub1, 'x2':xpub2, 'x3':xpub3} def get_action(self): - xpub1 = self.master_public_keys.get("m/") - xpub2 = self.master_public_keys.get("cold/") - xpub3 = self.master_public_keys.get("remote/") + xpub1 = self.master_public_keys.get("x1/") + xpub2 = self.master_public_keys.get("x2/") + xpub3 = self.master_public_keys.get("x3/") if xpub1 is None: return 'create_seed' if xpub2 is None or xpub3 is None: @@ -1523,7 +1521,7 @@ class OldWallet(Deterministic_Wallet): def get_master_public_keys(self): return {'Main Account':self.get_master_public_key()} - def create_accounts(self, password): + def create_main_account(self, password): mpk = self.storage.get("master_public_key") self.create_account(mpk) @@ -1574,7 +1572,7 @@ class Wallet(object): config = storage.config self.wallet_types = [ - #('standard', ("Standard wallet"), NewWallet if config.get('bip32') else OldWallet), + ('standard', ("Standard wallet"), NewWallet), ('imported', ("Imported wallet"), Imported_Wallet), ('2of2', ("Multisig wallet (2 of 2)"), Wallet_2of2), ('2of3', ("Multisig wallet (2 of 3)"), Wallet_2of3) @@ -1586,7 +1584,7 @@ class Wallet(object): return WalletClass(storage) if not storage.file_exists: - seed_version = NEW_SEED_VERSION if config.get('bip32') is True else OLD_SEED_VERSION + seed_version = NEW_SEED_VERSION else: seed_version = storage.get('seed_version') if not seed_version: