update wallet format again, for keystore
This commit is contained in:
@@ -148,7 +148,6 @@ class BaseWizard(object):
|
|||||||
self.restore_keys_dialog(title=title, message=message, run_next=self.on_restore, is_valid=v)
|
self.restore_keys_dialog(title=title, message=message, run_next=self.on_restore, is_valid=v)
|
||||||
|
|
||||||
def choose_hw(self):
|
def choose_hw(self):
|
||||||
self.storage.put('key_type', 'hardware')
|
|
||||||
hw_wallet_types, choices = self.plugins.hardware_wallets('create')
|
hw_wallet_types, choices = self.plugins.hardware_wallets('create')
|
||||||
choices = zip(hw_wallet_types, choices)
|
choices = zip(hw_wallet_types, choices)
|
||||||
title = _('Hardware Keystore')
|
title = _('Hardware Keystore')
|
||||||
@@ -162,7 +161,7 @@ class BaseWizard(object):
|
|||||||
self.choice_dialog(title=title, message=msg, choices=choices, run_next=self.on_hardware)
|
self.choice_dialog(title=title, message=msg, choices=choices, run_next=self.on_hardware)
|
||||||
|
|
||||||
def on_hardware(self, hw_type):
|
def on_hardware(self, hw_type):
|
||||||
self.storage.put('hardware_type', hw_type)
|
self.hw_type = hw_type
|
||||||
title = _('Hardware wallet') + ' [%s]' % hw_type
|
title = _('Hardware wallet') + ' [%s]' % hw_type
|
||||||
message = _('Do you have a device, or do you want to restore a wallet using an existing seed?')
|
message = _('Do you have a device, or do you want to restore a wallet using an existing seed?')
|
||||||
choices = [
|
choices = [
|
||||||
@@ -178,10 +177,9 @@ class BaseWizard(object):
|
|||||||
def on_hardware_account_id(self, account_id):
|
def on_hardware_account_id(self, account_id):
|
||||||
from keystore import load_keystore, bip44_derivation
|
from keystore import load_keystore, bip44_derivation
|
||||||
derivation = bip44_derivation(int(account_id))
|
derivation = bip44_derivation(int(account_id))
|
||||||
self.storage.put('derivation', derivation)
|
plugin = self.plugins.get_plugin(self.hw_type)
|
||||||
name = self.storage.get('hardware_type')
|
k = plugin.create_keystore(self.hw_type, derivation, self)
|
||||||
plugin = self.plugins.get_plugin(name)
|
self.create_wallet(k, None)
|
||||||
plugin.on_create_wallet(self.storage, self)
|
|
||||||
|
|
||||||
def on_hardware_seed(self):
|
def on_hardware_seed(self):
|
||||||
self.storage.put('key_type', 'hw_seed')
|
self.storage.put('key_type', 'hw_seed')
|
||||||
@@ -209,13 +207,13 @@ class BaseWizard(object):
|
|||||||
derivation = "m/44'/0'/%d'"%account_id
|
derivation = "m/44'/0'/%d'"%account_id
|
||||||
self.storage.put('account_id', account_id)
|
self.storage.put('account_id', account_id)
|
||||||
k.add_xprv_from_seed(bip32_seed, derivation, password)
|
k.add_xprv_from_seed(bip32_seed, derivation, password)
|
||||||
k.save(self.storage, 'x/')
|
self.storage.put('keystore', k.dump())
|
||||||
self.wallet = Standard_Wallet(self.storage)
|
self.wallet = Standard_Wallet(self.storage)
|
||||||
self.run('create_addresses')
|
self.run('create_addresses')
|
||||||
|
|
||||||
def create_wallet(self, k, password):
|
def create_wallet(self, k, password):
|
||||||
if self.wallet_type == 'standard':
|
if self.wallet_type == 'standard':
|
||||||
k.save(self.storage, 'x/')
|
self.storage.put('keystore', k.dump())
|
||||||
self.wallet = Standard_Wallet(self.storage)
|
self.wallet = Standard_Wallet(self.storage)
|
||||||
self.run('create_addresses')
|
self.run('create_addresses')
|
||||||
elif self.wallet_type == 'multisig':
|
elif self.wallet_type == 'multisig':
|
||||||
@@ -232,7 +230,7 @@ class BaseWizard(object):
|
|||||||
d = self.storage.get('master_public_keys', {})
|
d = self.storage.get('master_public_keys', {})
|
||||||
if keystore.xpub in d.values():
|
if keystore.xpub in d.values():
|
||||||
raise BaseException('duplicate key')
|
raise BaseException('duplicate key')
|
||||||
keystore.save(self.storage, 'x%d/'%(i+1))
|
self.storage.put('x%d/'%(i+1), keystore.dump())
|
||||||
|
|
||||||
def add_cosigners(self, password, i):
|
def add_cosigners(self, password, i):
|
||||||
self.add_cosigner_dialog(run_next=lambda x: self.on_cosigner(x, password, i), index=i, is_valid=keystore.is_xpub)
|
self.add_cosigner_dialog(run_next=lambda x: self.on_cosigner(x, password, i), index=i, is_valid=keystore.is_xpub)
|
||||||
|
|||||||
165
lib/keystore.py
165
lib/keystore.py
@@ -43,9 +43,6 @@ class KeyStore(PrintError):
|
|||||||
def has_seed(self):
|
def has_seed(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def has_password(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_watching_only(self):
|
def is_watching_only(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -57,10 +54,6 @@ class Software_KeyStore(KeyStore):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
KeyStore.__init__(self)
|
KeyStore.__init__(self)
|
||||||
self.use_encryption = False
|
|
||||||
|
|
||||||
def has_password(self):
|
|
||||||
return self.use_encryption
|
|
||||||
|
|
||||||
def sign_message(self, sequence, message, password):
|
def sign_message(self, sequence, message, password):
|
||||||
sec = self.get_private_key(sequence, password)
|
sec = self.get_private_key(sequence, password)
|
||||||
@@ -79,9 +72,11 @@ class Software_KeyStore(KeyStore):
|
|||||||
class Imported_KeyStore(Software_KeyStore):
|
class Imported_KeyStore(Software_KeyStore):
|
||||||
# keystore for imported private keys
|
# keystore for imported private keys
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, d):
|
||||||
Software_KeyStore.__init__(self)
|
Software_KeyStore.__init__(self)
|
||||||
self.keypairs = {}
|
self.keypairs = d.get('keypairs', {})
|
||||||
|
self.receiving_pubkeys = self.keypairs.keys()
|
||||||
|
self.change_pubkeys = []
|
||||||
|
|
||||||
def is_deterministic(self):
|
def is_deterministic(self):
|
||||||
return False
|
return False
|
||||||
@@ -92,16 +87,11 @@ class Imported_KeyStore(Software_KeyStore):
|
|||||||
def get_master_public_key(self):
|
def get_master_public_key(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def load(self, storage, name):
|
def dump(self):
|
||||||
self.keypairs = storage.get('keypairs', {})
|
return {
|
||||||
self.use_encryption = storage.get('use_encryption', False)
|
'type': 'imported',
|
||||||
self.receiving_pubkeys = self.keypairs.keys()
|
'keypairs': self.keypairs,
|
||||||
self.change_pubkeys = []
|
}
|
||||||
|
|
||||||
def save(self, storage, root_name):
|
|
||||||
storage.put('key_type', 'imported')
|
|
||||||
storage.put('keypairs', self.keypairs)
|
|
||||||
storage.put('use_encryption', self.use_encryption)
|
|
||||||
|
|
||||||
def can_import(self):
|
def can_import(self):
|
||||||
return True
|
return True
|
||||||
@@ -148,25 +138,21 @@ class Imported_KeyStore(Software_KeyStore):
|
|||||||
b = pw_decode(v, old_password)
|
b = pw_decode(v, old_password)
|
||||||
c = pw_encode(b, new_password)
|
c = pw_encode(b, new_password)
|
||||||
self.keypairs[k] = b
|
self.keypairs[k] = b
|
||||||
self.use_encryption = (new_password is not None)
|
|
||||||
|
|
||||||
|
|
||||||
class Deterministic_KeyStore(Software_KeyStore):
|
class Deterministic_KeyStore(Software_KeyStore):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, d):
|
||||||
Software_KeyStore.__init__(self)
|
Software_KeyStore.__init__(self)
|
||||||
self.seed = ''
|
self.seed = d.get('seed', '')
|
||||||
|
|
||||||
def is_deterministic(self):
|
def is_deterministic(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def load(self, storage, name):
|
def dump(self):
|
||||||
self.seed = storage.get('seed', '')
|
return {
|
||||||
self.use_encryption = storage.get('use_encryption', False)
|
'seed': self.seed,
|
||||||
|
}
|
||||||
def save(self, storage, name):
|
|
||||||
storage.put('seed', self.seed)
|
|
||||||
storage.put('use_encryption', self.use_encryption)
|
|
||||||
|
|
||||||
def has_seed(self):
|
def has_seed(self):
|
||||||
return self.seed != ''
|
return self.seed != ''
|
||||||
@@ -180,7 +166,6 @@ class Deterministic_KeyStore(Software_KeyStore):
|
|||||||
self.seed_version, self.seed = self.format_seed(seed)
|
self.seed_version, self.seed = self.format_seed(seed)
|
||||||
if password:
|
if password:
|
||||||
self.seed = pw_encode(self.seed, password)
|
self.seed = pw_encode(self.seed, password)
|
||||||
self.use_encryption = (password is not None)
|
|
||||||
|
|
||||||
def get_seed(self, password):
|
def get_seed(self, password):
|
||||||
return pw_decode(self.seed, password).encode('utf8')
|
return pw_decode(self.seed, password).encode('utf8')
|
||||||
@@ -235,27 +220,21 @@ class Xpub:
|
|||||||
|
|
||||||
class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
|
class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, d):
|
||||||
Xpub.__init__(self)
|
Xpub.__init__(self)
|
||||||
Deterministic_KeyStore.__init__(self)
|
Deterministic_KeyStore.__init__(self, d)
|
||||||
self.xprv = None
|
self.xpub = d.get('xpub')
|
||||||
|
self.xprv = d.get('xprv')
|
||||||
|
|
||||||
def format_seed(self, seed):
|
def format_seed(self, seed):
|
||||||
return NEW_SEED_VERSION, ' '.join(seed.split())
|
return NEW_SEED_VERSION, ' '.join(seed.split())
|
||||||
|
|
||||||
def load(self, storage, name):
|
def dump(self):
|
||||||
Deterministic_KeyStore.load(self, storage, name)
|
d = Deterministic_KeyStore.dump(self)
|
||||||
self.xpub = storage.get('master_public_keys', {}).get(name)
|
d['type'] = 'bip32'
|
||||||
self.xprv = storage.get('master_private_keys', {}).get(name)
|
d['xpub'] = self.xpub
|
||||||
|
d['xprv'] = self.xprv
|
||||||
def save(self, storage, name):
|
return d
|
||||||
Deterministic_KeyStore.save(self, storage, name)
|
|
||||||
d = storage.get('master_public_keys', {})
|
|
||||||
d[name] = self.xpub
|
|
||||||
storage.put('master_public_keys', d)
|
|
||||||
d = storage.get('master_private_keys', {})
|
|
||||||
d[name] = self.xprv
|
|
||||||
storage.put('master_private_keys', d)
|
|
||||||
|
|
||||||
def add_master_private_key(self, xprv, password):
|
def add_master_private_key(self, xprv, password):
|
||||||
self.xprv = pw_encode(xprv, password)
|
self.xprv = pw_encode(xprv, password)
|
||||||
@@ -279,7 +258,6 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
|
|||||||
if self.xprv is not None:
|
if self.xprv is not None:
|
||||||
b = pw_decode(self.xprv, old_password)
|
b = pw_decode(self.xprv, old_password)
|
||||||
self.xprv = pw_encode(b, new_password)
|
self.xprv = pw_encode(b, new_password)
|
||||||
self.use_encryption = (new_password is not None)
|
|
||||||
|
|
||||||
def is_watching_only(self):
|
def is_watching_only(self):
|
||||||
return self.xprv is None
|
return self.xprv is None
|
||||||
@@ -340,18 +318,14 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
|
|||||||
|
|
||||||
class Old_KeyStore(Deterministic_KeyStore):
|
class Old_KeyStore(Deterministic_KeyStore):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, d):
|
||||||
Deterministic_KeyStore.__init__(self)
|
Deterministic_KeyStore.__init__(self, d)
|
||||||
self.mpk = None
|
self.mpk = d.get('mpk').decode('hex')
|
||||||
|
|
||||||
def load(self, storage, name):
|
def dump(self):
|
||||||
Deterministic_KeyStore.load(self, storage, name)
|
d = Deterministic_KeyStore.dump(self)
|
||||||
self.mpk = storage.get('master_public_key').decode('hex')
|
d['mpk'] = self.mpk.encode('hex')
|
||||||
|
return d
|
||||||
def save(self, storage, name):
|
|
||||||
Deterministic_KeyStore.save(self, storage, name)
|
|
||||||
storage.put('wallet_type', 'old')
|
|
||||||
storage.put('master_public_key', self.mpk.encode('hex'))
|
|
||||||
|
|
||||||
def add_seed(self, seed, password):
|
def add_seed(self, seed, password):
|
||||||
Deterministic_KeyStore.add_seed(self, seed, password)
|
Deterministic_KeyStore.add_seed(self, seed, password)
|
||||||
@@ -473,7 +447,6 @@ class Old_KeyStore(Deterministic_KeyStore):
|
|||||||
if self.has_seed():
|
if self.has_seed():
|
||||||
decoded = self.get_seed(old_password)
|
decoded = self.get_seed(old_password)
|
||||||
self.seed = pw_encode(decoded, new_password)
|
self.seed = pw_encode(decoded, new_password)
|
||||||
self.use_encryption = (new_password is not None)
|
|
||||||
|
|
||||||
|
|
||||||
class Hardware_KeyStore(KeyStore, Xpub):
|
class Hardware_KeyStore(KeyStore, Xpub):
|
||||||
@@ -485,24 +458,26 @@ class Hardware_KeyStore(KeyStore, Xpub):
|
|||||||
#restore_wallet_class = BIP32_RD_Wallet
|
#restore_wallet_class = BIP32_RD_Wallet
|
||||||
max_change_outputs = 1
|
max_change_outputs = 1
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, d):
|
||||||
Xpub.__init__(self)
|
Xpub.__init__(self)
|
||||||
KeyStore.__init__(self)
|
KeyStore.__init__(self)
|
||||||
# Errors and other user interaction is done through the wallet's
|
# Errors and other user interaction is done through the wallet's
|
||||||
# handler. The handler is per-window and preserved across
|
# handler. The handler is per-window and preserved across
|
||||||
# device reconnects
|
# device reconnects
|
||||||
|
self.xpub = d.get('xpub')
|
||||||
|
self.derivation = d.get('derivation')
|
||||||
self.handler = None
|
self.handler = None
|
||||||
|
|
||||||
def is_deterministic(self):
|
def is_deterministic(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def load(self, storage, name):
|
def dump(self):
|
||||||
self.xpub = storage.get('master_public_keys', {}).get(name)
|
return {
|
||||||
|
'type': 'hardware',
|
||||||
def save(self, storage, name):
|
'hw_type': self.hw_type,
|
||||||
d = storage.get('master_public_keys', {})
|
'xpub': self.xpub,
|
||||||
d[name] = self.xpub
|
'derivation':self.derivation,
|
||||||
storage.put('master_public_keys', d)
|
}
|
||||||
|
|
||||||
def unpaired(self):
|
def unpaired(self):
|
||||||
'''A device paired with the wallet was diconnected. This can be
|
'''A device paired with the wallet was diconnected. This can be
|
||||||
@@ -572,37 +547,37 @@ def xpubkey_to_address(x_pubkey):
|
|||||||
return pubkey, address
|
return pubkey, address
|
||||||
|
|
||||||
|
|
||||||
keystores = []
|
hw_keystores = {}
|
||||||
|
|
||||||
|
def register_keystore(hw_type, constructor):
|
||||||
|
hw_keystores[hw_type] = constructor
|
||||||
|
|
||||||
|
def hardware_keystore(hw_type, d):
|
||||||
|
if hw_type in hw_keystores:
|
||||||
|
constructor = hw_keystores[hw_type]
|
||||||
|
return constructor(d)
|
||||||
|
raise BaseException('unknown hardware type', hw_type)
|
||||||
|
|
||||||
def load_keystore(storage, name):
|
def load_keystore(storage, name):
|
||||||
w = storage.get('wallet_type')
|
w = storage.get('wallet_type', 'standard')
|
||||||
t = storage.get('key_type', 'seed')
|
d = storage.get(name, {})
|
||||||
seed_version = storage.get_seed_version()
|
t = d.get('type')
|
||||||
if seed_version == OLD_SEED_VERSION or w == 'old':
|
if not t:
|
||||||
k = Old_KeyStore()
|
raise BaseException('wallet format requires update')
|
||||||
|
if t == 'old':
|
||||||
|
k = Old_KeyStore(d)
|
||||||
elif t == 'imported':
|
elif t == 'imported':
|
||||||
k = Imported_KeyStore()
|
k = Imported_KeyStore(d)
|
||||||
elif name and name not in [ 'x/', 'x1/' ]:
|
elif t == 'bip32':
|
||||||
k = BIP32_KeyStore()
|
k = BIP32_KeyStore(d)
|
||||||
elif t in ['seed', 'hw_seed']:
|
|
||||||
k = BIP32_KeyStore()
|
|
||||||
elif t == 'hardware':
|
elif t == 'hardware':
|
||||||
hw_type = storage.get('hardware_type')
|
hw_type = d.get('hw_type')
|
||||||
for cat, _type, constructor in keystores:
|
k = hardware_keystore(hw_type, d)
|
||||||
if cat == 'hardware' and _type == hw_type:
|
|
||||||
k = constructor()
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise BaseException('unknown hardware type')
|
|
||||||
else:
|
else:
|
||||||
raise BaseException('unknown wallet type', t)
|
raise BaseException('unknown wallet type', t)
|
||||||
k.load(storage, name)
|
|
||||||
return k
|
return k
|
||||||
|
|
||||||
|
|
||||||
def register_keystore(category, type, constructor):
|
|
||||||
keystores.append((category, type, constructor))
|
|
||||||
|
|
||||||
|
|
||||||
def is_old_mpk(mpk):
|
def is_old_mpk(mpk):
|
||||||
try:
|
try:
|
||||||
@@ -649,35 +624,35 @@ def bip44_derivation(account_id):
|
|||||||
|
|
||||||
def from_seed(seed, password):
|
def from_seed(seed, password):
|
||||||
if is_old_seed(seed):
|
if is_old_seed(seed):
|
||||||
keystore = Old_KeyStore()
|
keystore = Old_KeyStore({})
|
||||||
keystore.add_seed(seed, password)
|
keystore.add_seed(seed, password)
|
||||||
elif is_new_seed(seed):
|
elif is_new_seed(seed):
|
||||||
keystore = BIP32_KeyStore()
|
keystore = BIP32_KeyStore({})
|
||||||
keystore.add_seed(seed, password)
|
keystore.add_seed(seed, password)
|
||||||
bip32_seed = Mnemonic.mnemonic_to_seed(seed, '')
|
bip32_seed = Mnemonic.mnemonic_to_seed(seed, '')
|
||||||
keystore.add_xprv_from_seed(bip32_seed, "m/", password)
|
keystore.add_xprv_from_seed(bip32_seed, "m/", password)
|
||||||
return keystore
|
return keystore
|
||||||
|
|
||||||
def from_private_key_list(text, password):
|
def from_private_key_list(text, password):
|
||||||
keystore = Imported_KeyStore()
|
keystore = Imported_KeyStore({})
|
||||||
for x in text.split():
|
for x in text.split():
|
||||||
keystore.import_key(x, None)
|
keystore.import_key(x, None)
|
||||||
keystore.update_password(None, password)
|
keystore.update_password(None, password)
|
||||||
return keystore
|
return keystore
|
||||||
|
|
||||||
def from_old_mpk(mpk):
|
def from_old_mpk(mpk):
|
||||||
keystore = Old_KeyStore()
|
keystore = Old_KeyStore({})
|
||||||
keystore.add_master_public_key(mpk)
|
keystore.add_master_public_key(mpk)
|
||||||
return keystore
|
return keystore
|
||||||
|
|
||||||
def from_xpub(xpub):
|
def from_xpub(xpub):
|
||||||
keystore = BIP32_KeyStore()
|
keystore = BIP32_KeyStore({})
|
||||||
keystore.add_master_public_key(xpub)
|
keystore.add_master_public_key(xpub)
|
||||||
return keystore
|
return keystore
|
||||||
|
|
||||||
def from_xprv(xprv, password):
|
def from_xprv(xprv, password):
|
||||||
xpub = bitcoin.xpub_from_xprv(xprv)
|
xpub = bitcoin.xpub_from_xprv(xprv)
|
||||||
keystore = BIP32_KeyStore()
|
keystore = BIP32_KeyStore({})
|
||||||
keystore.add_master_private_key(xprv, password)
|
keystore.add_master_private_key(xprv, password)
|
||||||
keystore.add_master_public_key(xpub)
|
keystore.add_master_public_key(xpub)
|
||||||
return keystore
|
return keystore
|
||||||
|
|||||||
@@ -164,12 +164,12 @@ class Plugins(DaemonThread):
|
|||||||
|
|
||||||
def register_keystore(self, name, gui_good, details):
|
def register_keystore(self, name, gui_good, details):
|
||||||
from keystore import register_keystore
|
from keystore import register_keystore
|
||||||
def dynamic_constructor():
|
def dynamic_constructor(d):
|
||||||
return self.get_plugin(name).keystore_class()
|
return self.get_plugin(name).keystore_class(d)
|
||||||
if details[0] == 'hardware':
|
if details[0] == 'hardware':
|
||||||
self.hw_wallets[name] = (gui_good, details)
|
self.hw_wallets[name] = (gui_good, details)
|
||||||
self.print_error("registering keystore %s: %s" %(name, details))
|
self.print_error("registering hardware %s: %s" %(name, details))
|
||||||
register_keystore(details[0], details[1], dynamic_constructor)
|
register_keystore(details[1], dynamic_constructor)
|
||||||
|
|
||||||
def get_plugin(self, name):
|
def get_plugin(self, name):
|
||||||
if not name in self.plugins:
|
if not name in self.plugins:
|
||||||
|
|||||||
@@ -37,6 +37,17 @@ from i18n import _
|
|||||||
from util import NotEnoughFunds, PrintError, profiler
|
from util import NotEnoughFunds, PrintError, profiler
|
||||||
from plugins import run_hook, plugin_loaders
|
from plugins import run_hook, plugin_loaders
|
||||||
from keystore import bip44_derivation
|
from keystore import bip44_derivation
|
||||||
|
from version import OLD_SEED_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
def multisig_type(wallet_type):
|
||||||
|
'''If wallet_type is mofn multi-sig, return [m, n],
|
||||||
|
otherwise return None.'''
|
||||||
|
match = re.match('(\d+)of(\d+)', wallet_type)
|
||||||
|
if match:
|
||||||
|
match = [int(x) for x in match.group(1, 2)]
|
||||||
|
return match
|
||||||
|
|
||||||
|
|
||||||
class WalletStorage(PrintError):
|
class WalletStorage(PrintError):
|
||||||
|
|
||||||
@@ -198,8 +209,9 @@ class WalletStorage(PrintError):
|
|||||||
|
|
||||||
def requires_upgrade(self):
|
def requires_upgrade(self):
|
||||||
r = False
|
r = False
|
||||||
r |= self.convert_wallet_type(True)
|
if self.file_exists:
|
||||||
r |= self.convert_imported(True)
|
r |= self.convert_wallet_type(True)
|
||||||
|
r |= self.convert_imported(True)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def upgrade(self):
|
def upgrade(self):
|
||||||
@@ -209,17 +221,52 @@ class WalletStorage(PrintError):
|
|||||||
|
|
||||||
def convert_wallet_type(self, is_test):
|
def convert_wallet_type(self, is_test):
|
||||||
assert not self.requires_split()
|
assert not self.requires_split()
|
||||||
wallet_type = self.get('wallet_type')
|
d = self.get('keystore', {})
|
||||||
if wallet_type not in ['trezor', 'keepkey']:
|
t = d.get('type')
|
||||||
|
if t:
|
||||||
return False
|
return False
|
||||||
if is_test:
|
if is_test:
|
||||||
return True
|
return True
|
||||||
self.put('wallet_type', 'standard')
|
wallet_type = self.get('wallet_type')
|
||||||
self.put('key_type', 'hardware')
|
seed_version = self.get_seed_version()
|
||||||
self.put('hardware_type', wallet_type)
|
if seed_version == OLD_SEED_VERSION or wallet_type == 'old':
|
||||||
xpub = self.get('master_public_keys')["x/0'"]
|
seed = self.get('seed')
|
||||||
self.put('master_public_keys', {'x/': xpub})
|
d = {
|
||||||
self.put('derivation', bip44_derivation(0))
|
'type': 'old',
|
||||||
|
'seed': seed
|
||||||
|
}
|
||||||
|
self.put('wallet_type', 'standard')
|
||||||
|
self.put('keystore', d)
|
||||||
|
|
||||||
|
elif wallet_type == 'standard':
|
||||||
|
xpub = self.get('master_public_keys')["x/"]
|
||||||
|
xprv = self.get('master_private_keys')["x/"]
|
||||||
|
d = {
|
||||||
|
'type': 'bip32',
|
||||||
|
'xpub': xpub,
|
||||||
|
'xprv': xprv,
|
||||||
|
'seed': self.get('seed', '')
|
||||||
|
}
|
||||||
|
self.put('wallet_type', 'standard')
|
||||||
|
self.put('keystore', d)
|
||||||
|
|
||||||
|
elif wallet_type in ['trezor', 'keepkey']:
|
||||||
|
xpub = self.get('master_public_keys')["x/0'"]
|
||||||
|
d = {
|
||||||
|
'type': 'hardware',
|
||||||
|
'hardware_type': wallet_type,
|
||||||
|
'xpub': xpub,
|
||||||
|
'derivation': bip44_derivation(0),
|
||||||
|
}
|
||||||
|
self.put('wallet_type', 'standard')
|
||||||
|
self.put('keystore', d)
|
||||||
|
|
||||||
|
elif multisig_type(wallet_type):
|
||||||
|
raise BaseException('not implemented')
|
||||||
|
|
||||||
|
elif wallet_type in ['2fa']:
|
||||||
|
raise BaseException('not implemented')
|
||||||
|
|
||||||
|
|
||||||
def convert_imported(self, test):
|
def convert_imported(self, test):
|
||||||
# '/x' is the internal ID for imported accounts
|
# '/x' is the internal ID for imported accounts
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ from util import NotEnoughFunds, PrintError, profiler
|
|||||||
from bitcoin import *
|
from bitcoin import *
|
||||||
from version import *
|
from version import *
|
||||||
from keystore import load_keystore
|
from keystore import load_keystore
|
||||||
|
from storage import multisig_type
|
||||||
|
|
||||||
from transaction import Transaction
|
from transaction import Transaction
|
||||||
from plugins import run_hook
|
from plugins import run_hook
|
||||||
@@ -1164,6 +1165,9 @@ class Abstract_Wallet(PrintError):
|
|||||||
if self.synchronizer:
|
if self.synchronizer:
|
||||||
self.synchronizer.add(address)
|
self.synchronizer.add(address)
|
||||||
|
|
||||||
|
def has_password(self):
|
||||||
|
return self.storage.get('use_encryption', False)
|
||||||
|
|
||||||
|
|
||||||
class Imported_Wallet(Abstract_Wallet):
|
class Imported_Wallet(Abstract_Wallet):
|
||||||
# wallet made of imported addresses
|
# wallet made of imported addresses
|
||||||
@@ -1242,7 +1246,7 @@ class P2PK_Wallet(Abstract_Wallet):
|
|||||||
return public_key_to_bc_address(pubkey.decode('hex'))
|
return public_key_to_bc_address(pubkey.decode('hex'))
|
||||||
|
|
||||||
def load_keystore(self):
|
def load_keystore(self):
|
||||||
self.keystore = load_keystore(self.storage, self.root_name)
|
self.keystore = load_keystore(self.storage, 'keystore')
|
||||||
|
|
||||||
def get_pubkey(self, c, i):
|
def get_pubkey(self, c, i):
|
||||||
pubkey_list = self.change_pubkeys if c else self.receiving_pubkeys
|
pubkey_list = self.change_pubkeys if c else self.receiving_pubkeys
|
||||||
@@ -1402,7 +1406,6 @@ class Deterministic_Wallet(Abstract_Wallet):
|
|||||||
|
|
||||||
|
|
||||||
class Standard_Wallet(Deterministic_Wallet, P2PK_Wallet):
|
class Standard_Wallet(Deterministic_Wallet, P2PK_Wallet):
|
||||||
root_name = 'x/'
|
|
||||||
wallet_type = 'standard'
|
wallet_type = 'standard'
|
||||||
|
|
||||||
def __init__(self, storage):
|
def __init__(self, storage):
|
||||||
@@ -1426,22 +1429,23 @@ class Standard_Wallet(Deterministic_Wallet, P2PK_Wallet):
|
|||||||
def can_change_password(self):
|
def can_change_password(self):
|
||||||
return self.keystore.can_change_password()
|
return self.keystore.can_change_password()
|
||||||
|
|
||||||
def has_password(self):
|
|
||||||
return self.keystore.has_password()
|
|
||||||
|
|
||||||
def check_password(self, password):
|
def check_password(self, password):
|
||||||
self.keystore.check_password(password)
|
self.keystore.check_password(password)
|
||||||
|
|
||||||
def update_password(self, old_pw, new_pw):
|
def update_password(self, old_pw, new_pw):
|
||||||
self.keystore.update_password(old_pw, new_pw)
|
self.keystore.update_password(old_pw, new_pw)
|
||||||
self.keystore.save(self.storage, self.root_name)
|
self.save_keystore()
|
||||||
|
self.storage.put('use_encryption', (new_pw is not None))
|
||||||
|
|
||||||
|
def save_keystore(self):
|
||||||
|
self.storage.put('keystore', self.keystore.dump())
|
||||||
|
|
||||||
def can_import_privkey(self):
|
def can_import_privkey(self):
|
||||||
return self.keystore.can_import()
|
return self.keystore.can_import()
|
||||||
|
|
||||||
def import_key(self, pk, pw):
|
def import_key(self, pk, pw):
|
||||||
pubkey = self.keystore.import_key(pk, pw)
|
pubkey = self.keystore.import_key(pk, pw)
|
||||||
self.keystore.save(self.storage, self.root_name)
|
self.save_keystore()
|
||||||
self.receiving_pubkeys.append(pubkey)
|
self.receiving_pubkeys.append(pubkey)
|
||||||
self.save_pubkeys()
|
self.save_pubkeys()
|
||||||
addr = self.pubkeys_to_address(pubkey)
|
addr = self.pubkeys_to_address(pubkey)
|
||||||
@@ -1452,12 +1456,11 @@ class Standard_Wallet(Deterministic_Wallet, P2PK_Wallet):
|
|||||||
|
|
||||||
class Multisig_Wallet(Deterministic_Wallet):
|
class Multisig_Wallet(Deterministic_Wallet):
|
||||||
# generic m of n
|
# generic m of n
|
||||||
root_name = "x1/"
|
|
||||||
gap_limit = 20
|
gap_limit = 20
|
||||||
|
|
||||||
def __init__(self, storage):
|
def __init__(self, storage):
|
||||||
self.wallet_type = storage.get('wallet_type')
|
self.wallet_type = storage.get('wallet_type')
|
||||||
self.m, self.n = Wallet.multisig_type(self.wallet_type)
|
self.m, self.n = multisig_type(self.wallet_type)
|
||||||
Deterministic_Wallet.__init__(self, storage)
|
Deterministic_Wallet.__init__(self, storage)
|
||||||
|
|
||||||
def get_pubkeys(self, c, i):
|
def get_pubkeys(self, c, i):
|
||||||
@@ -1481,10 +1484,10 @@ class Multisig_Wallet(Deterministic_Wallet):
|
|||||||
for i in range(self.n):
|
for i in range(self.n):
|
||||||
name = 'x%d/'%(i+1)
|
name = 'x%d/'%(i+1)
|
||||||
self.keystores[name] = load_keystore(self.storage, name)
|
self.keystores[name] = load_keystore(self.storage, name)
|
||||||
self.keystore = self.keystores[self.root_name]
|
self.keystore = self.keystores['x1/']
|
||||||
|
|
||||||
def get_keystore(self):
|
def get_keystore(self):
|
||||||
return self.keystores.get(self.root_name)
|
return self.keystores.get('x1/')
|
||||||
|
|
||||||
def get_keystores(self):
|
def get_keystores(self):
|
||||||
return self.keystores.values()
|
return self.keystores.values()
|
||||||
@@ -1492,7 +1495,8 @@ class Multisig_Wallet(Deterministic_Wallet):
|
|||||||
def update_password(self, old_pw, new_pw):
|
def update_password(self, old_pw, new_pw):
|
||||||
for name, keystore in self.keystores.items():
|
for name, keystore in self.keystores.items():
|
||||||
keystore.update_password(old_pw, new_pw)
|
keystore.update_password(old_pw, new_pw)
|
||||||
keystore.save(self.storage, name)
|
self.storage.put(name, keystore.dump())
|
||||||
|
self.storage.put('use_encryption', (new_pw is not None))
|
||||||
|
|
||||||
def has_seed(self):
|
def has_seed(self):
|
||||||
return self.keystore.has_seed()
|
return self.keystore.has_seed()
|
||||||
@@ -1500,9 +1504,6 @@ class Multisig_Wallet(Deterministic_Wallet):
|
|||||||
def can_change_password(self):
|
def can_change_password(self):
|
||||||
return self.keystore.can_change_password()
|
return self.keystore.can_change_password()
|
||||||
|
|
||||||
def has_password(self):
|
|
||||||
return self.keystore.has_password()
|
|
||||||
|
|
||||||
def is_watching_only(self):
|
def is_watching_only(self):
|
||||||
return not any([not k.is_watching_only() for k in self.get_keystores()])
|
return not any([not k.is_watching_only() for k in self.get_keystores()])
|
||||||
|
|
||||||
@@ -1568,18 +1569,9 @@ class Wallet(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def wallet_class(wallet_type):
|
def wallet_class(wallet_type):
|
||||||
if Wallet.multisig_type(wallet_type):
|
if multisig_type(wallet_type):
|
||||||
return Multisig_Wallet
|
return Multisig_Wallet
|
||||||
if wallet_type in wallet_constructors:
|
if wallet_type in wallet_constructors:
|
||||||
return wallet_constructors[wallet_type]
|
return wallet_constructors[wallet_type]
|
||||||
raise RuntimeError("Unknown wallet type: " + wallet_type)
|
raise RuntimeError("Unknown wallet type: " + wallet_type)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def multisig_type(wallet_type):
|
|
||||||
'''If wallet_type is mofn multi-sig, return [m, n],
|
|
||||||
otherwise return None.'''
|
|
||||||
match = re.match('(\d+)of(\d+)', wallet_type)
|
|
||||||
if match:
|
|
||||||
match = [int(x) for x in match.group(1, 2)]
|
|
||||||
return match
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,7 @@ from ..hw_wallet import HW_PluginBase
|
|||||||
TIM_NEW, TIM_RECOVER, TIM_MNEMONIC, TIM_PRIVKEY = range(0, 4)
|
TIM_NEW, TIM_RECOVER, TIM_MNEMONIC, TIM_PRIVKEY = range(0, 4)
|
||||||
|
|
||||||
class TrezorCompatibleKeyStore(Hardware_KeyStore):
|
class TrezorCompatibleKeyStore(Hardware_KeyStore):
|
||||||
|
hw_type = 'trezor'
|
||||||
def load(self, storage, name):
|
|
||||||
self.xpub = storage.get('master_public_keys', {}).get(name)
|
|
||||||
self.derivation = storage.get('derivation')
|
|
||||||
|
|
||||||
def get_derivation(self):
|
def get_derivation(self):
|
||||||
return self.derivation
|
return self.derivation
|
||||||
|
|||||||
@@ -284,14 +284,13 @@ def qt_plugin_class(base_plugin_class):
|
|||||||
# Trigger a pairing
|
# Trigger a pairing
|
||||||
keystore.thread.add(partial(self.get_client, keystore))
|
keystore.thread.add(partial(self.get_client, keystore))
|
||||||
|
|
||||||
def on_create_wallet(self, storage, wizard):
|
def create_keystore(self, hw_type, derivation, wizard):
|
||||||
from electrum.keystore import load_keystore
|
from electrum.keystore import hardware_keystore
|
||||||
handler = self.create_handler(wizard)
|
handler = self.create_handler(wizard)
|
||||||
thread = TaskThread(wizard, wizard.on_error)
|
thread = TaskThread(wizard, wizard.on_error)
|
||||||
# Setup device and create accounts in separate thread; wait until done
|
# Setup device and create accounts in separate thread; wait until done
|
||||||
loop = QEventLoop()
|
loop = QEventLoop()
|
||||||
exc_info = []
|
exc_info = []
|
||||||
derivation = storage.get('derivation')
|
|
||||||
self.setup_device(derivation, thread, handler, on_done=loop.quit,
|
self.setup_device(derivation, thread, handler, on_done=loop.quit,
|
||||||
on_error=lambda info: exc_info.extend(info))
|
on_error=lambda info: exc_info.extend(info))
|
||||||
loop.exec_()
|
loop.exec_()
|
||||||
@@ -299,9 +298,15 @@ def qt_plugin_class(base_plugin_class):
|
|||||||
if exc_info:
|
if exc_info:
|
||||||
wizard.on_error(exc_info)
|
wizard.on_error(exc_info)
|
||||||
raise UserCancelled
|
raise UserCancelled
|
||||||
storage.put('master_public_keys', {'/x':self.xpub})
|
# create keystore
|
||||||
keystore = load_keystore(storage, '/x') # this calls the dynamic constructor
|
d = {
|
||||||
wizard.create_wallet(keystore, None)
|
'xpub': self.xpub,
|
||||||
|
'type': 'hardware',
|
||||||
|
'hw_type': hw_type,
|
||||||
|
'derivation': derivation
|
||||||
|
}
|
||||||
|
k = hardware_keystore(hw_type, d)
|
||||||
|
return k
|
||||||
|
|
||||||
@hook
|
@hook
|
||||||
def receive_menu(self, menu, addrs, wallet):
|
def receive_menu(self, menu, addrs, wallet):
|
||||||
|
|||||||
Reference in New Issue
Block a user