This commit is contained in:
ThomasV 2014-08-13 16:05:43 +02:00
parent 1d4631d647
commit f4b390a79f
4 changed files with 129 additions and 129 deletions

View File

@ -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.")

View File

@ -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)

View File

@ -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

View File

@ -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: