commit f4b390a79f127d0bbe5c178bd849ec87b85f1a5a
parent 1d4631d6470930224e407bffae884a6b4edc514f
Author: ThomasV <thomasv@gitorious>
Date: Wed, 13 Aug 2014 16:05:43 +0200
bip44
Diffstat:
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: