bip44
This commit is contained in:
parent
1d4631d647
commit
f4b390a79f
5
electrum
5
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.")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
204
lib/wallet.py
204
lib/wallet.py
|
@ -1204,28 +1204,6 @@ class Deterministic_Wallet(Abstract_Wallet):
|
|||
if not self.accounts:
|
||||
return 'create_accounts'
|
||||
|
||||
|
||||
|
||||
class BIP32_Wallet(Deterministic_Wallet):
|
||||
# bip32 derivation
|
||||
|
||||
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', {})
|
||||
|
||||
def default_account(self):
|
||||
return self.accounts["m/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():
|
||||
|
@ -1234,38 +1212,50 @@ class BIP32_Wallet(Deterministic_Wallet):
|
|||
out[name] = mpk_text
|
||||
return out
|
||||
|
||||
|
||||
class BIP32_Wallet(Deterministic_Wallet):
|
||||
# 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['0']
|
||||
|
||||
def is_watching_only(self):
|
||||
return not bool(self.master_private_keys)
|
||||
|
||||
def get_master_public_key(self):
|
||||
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:
|
||||
|
|
Loading…
Reference in New Issue