request account_id in wizard, for hardware wallets. cleanup bip44 code
This commit is contained in:
parent
71de14240d
commit
d9021788fa
|
@ -372,14 +372,20 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
|||
return action
|
||||
|
||||
@wizard_dialog
|
||||
def input_dialog(self, title, message, run_next):
|
||||
def account_id_dialog(self, run_next):
|
||||
message = '\n'.join([
|
||||
_('Enter your account number here.'),
|
||||
_('If you are not sure what this is, leave this field to zero.')
|
||||
])
|
||||
default = '0'
|
||||
title = _('Account Number')
|
||||
line = QLineEdit()
|
||||
line.setText(default)
|
||||
vbox = QVBoxLayout()
|
||||
vbox.addWidget(QLabel(message))
|
||||
vbox.addWidget(line)
|
||||
self.set_main_layout(vbox, title)
|
||||
action = line.text()
|
||||
return action
|
||||
return int(line.text())
|
||||
|
||||
@wizard_dialog
|
||||
def show_xpub_dialog(self, xpub, run_next):
|
||||
|
|
|
@ -173,16 +173,42 @@ class BaseWizard(object):
|
|||
self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run)
|
||||
|
||||
def on_hardware_device(self):
|
||||
f = lambda x: self.run('on_hardware_account_id', x)
|
||||
self.account_id_dialog(run_next=f)
|
||||
|
||||
def on_hardware_account_id(self, account_id):
|
||||
from keystore import load_keystore
|
||||
self.storage.put('account_id', int(account_id))
|
||||
keystore = load_keystore(self.storage, None)
|
||||
keystore.plugin.on_create_wallet(keystore, self)
|
||||
|
||||
def on_hardware_seed(self):
|
||||
from keystore import load_keystore
|
||||
self.storage.put('key_type', 'hw_seed')
|
||||
keystore = load_keystore(self.storage, None)
|
||||
self.plugin = keystore #fixme .plugin
|
||||
keystore.on_restore_wallet(self)
|
||||
is_valid = lambda x: True #fixme: bip39
|
||||
f = lambda seed: self.run('on_bip39_seed', seed)
|
||||
self.restore_seed_dialog(run_next=f, is_valid=is_valid)
|
||||
|
||||
def on_bip_39_seed(self, seed):
|
||||
f = lambda passphrase: self.run('on_bip39_passphrase', seed, passphrase)
|
||||
self.request_passphrase(self.storage.get('hw_type'), run_next=f)
|
||||
|
||||
def on_bip39_passphrase(self, seed, passphrase):
|
||||
f = lambda account_id: self.run('on_bip44_account_id', seed, passphrase, account_id)
|
||||
self.account_id_dialog(run_next=f)
|
||||
|
||||
def on_bip44_account_id(self, seed, passphrase, account_id):
|
||||
f = lambda pw: self.run('on_bip44', seed, passphrase, account_id, pw)
|
||||
self.request_password(run_next=f)
|
||||
|
||||
def on_bip44(self, seed, passphrase, account_id, password):
|
||||
import keystore
|
||||
k = keystore.BIP32_KeyStore()
|
||||
k.add_seed(seed, password)
|
||||
bip32_seed = keystore.bip39_to_seed(seed, passphrase)
|
||||
derivation = "m/44'/0'/%d'"%account_id
|
||||
self.storage.put('account_id', account_id)
|
||||
k.add_xprv_from_seed(bip32_seed, derivation, password)
|
||||
k.save(self.storage, 'x/')
|
||||
self.wallet = Standard_Wallet(self.storage)
|
||||
self.run('create_addresses')
|
||||
|
||||
|
|
|
@ -216,7 +216,6 @@ class Xpub:
|
|||
|
||||
|
||||
class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
|
||||
root_derivation = "m/"
|
||||
|
||||
def __init__(self):
|
||||
Xpub.__init__(self)
|
||||
|
@ -298,43 +297,19 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
|
|||
if keypairs:
|
||||
tx.sign(keypairs)
|
||||
|
||||
def derive_xkeys(self, root, derivation, password):
|
||||
x = self.master_private_keys[root]
|
||||
root_xprv = pw_decode(x, password)
|
||||
xprv, xpub = bip32_private_derivation(root_xprv, root, derivation)
|
||||
return xpub, xprv
|
||||
|
||||
def get_mnemonic(self, password):
|
||||
return self.get_seed(password)
|
||||
|
||||
def mnemonic_to_seed(self, seed, password):
|
||||
return Mnemonic.mnemonic_to_seed(seed, password)
|
||||
|
||||
@classmethod
|
||||
def make_seed(self, lang=None):
|
||||
return Mnemonic(lang).make_seed()
|
||||
|
||||
#@classmethod
|
||||
#def address_derivation(self, account_id, change, address_index):
|
||||
# account_derivation = self.account_derivation(account_id)
|
||||
# return "%s/%d/%d" % (account_derivation, change, address_index)
|
||||
|
||||
#def address_id(self, address):
|
||||
# acc_id, (change, address_index) = self.get_address_index(address)
|
||||
# return self.address_derivation(acc_id, change, address_index)
|
||||
|
||||
def add_seed_and_xprv(self, seed, password, passphrase=''):
|
||||
xprv, xpub = bip32_root(self.mnemonic_to_seed(seed, passphrase))
|
||||
xprv, xpub = bip32_private_derivation(xprv, "m/", self.root_derivation)
|
||||
self.add_seed(seed, password)
|
||||
self.add_master_private_key(xprv, password)
|
||||
self.add_master_public_key(xpub)
|
||||
|
||||
def add_xprv(self, xprv, password):
|
||||
xpub = bitcoin.xpub_from_xprv(xprv)
|
||||
self.add_master_private_key(xprv, password)
|
||||
self.add_master_public_key(xpub)
|
||||
|
||||
def add_xprv_from_seed(self, bip32_seed, derivation, password):
|
||||
xprv, xpub = bip32_root(bip32_seed)
|
||||
xprv, xpub = bip32_private_derivation(xprv, "m/", derivation)
|
||||
self.add_xprv(xprv, password)
|
||||
|
||||
def can_sign(self, xpub):
|
||||
return xpub == self.xpub and self.xprv is not None
|
||||
|
||||
|
@ -533,54 +508,19 @@ class Hardware_KeyStore(KeyStore, Xpub):
|
|||
def can_change_password(self):
|
||||
return False
|
||||
|
||||
def derive_xkeys(self, root, derivation, password):
|
||||
if self.master_public_keys.get(self.root_name):
|
||||
return BIP44_wallet.derive_xkeys(self, root, derivation, password)
|
||||
# When creating a wallet we need to ask the device for the
|
||||
# master public key
|
||||
xpub = self.get_public_key(derivation)
|
||||
return xpub, None
|
||||
|
||||
|
||||
class BIP44_KeyStore(BIP32_KeyStore):
|
||||
root_derivation = "m/44'/0'/0'"
|
||||
def bip39_normalize_passphrase(passphrase):
|
||||
return normalize('NFKD', unicode(passphrase or ''))
|
||||
|
||||
@classmethod
|
||||
def normalize_passphrase(self, passphrase):
|
||||
return normalize('NFKD', unicode(passphrase or ''))
|
||||
|
||||
def is_valid_seed(self, seed):
|
||||
return True
|
||||
|
||||
def mnemonic_to_seed(self, mnemonic, passphrase):
|
||||
# See BIP39
|
||||
import pbkdf2, hashlib, hmac
|
||||
PBKDF2_ROUNDS = 2048
|
||||
mnemonic = normalize('NFKD', ' '.join(mnemonic.split()))
|
||||
passphrase = self.normalize_passphrase(passphrase)
|
||||
return pbkdf2.PBKDF2(mnemonic, 'mnemonic' + passphrase,
|
||||
iterations = PBKDF2_ROUNDS, macmodule = hmac,
|
||||
digestmodule = hashlib.sha512).read(64)
|
||||
|
||||
def on_restore_wallet(self, wizard):
|
||||
#assert isinstance(keystore, self.keystore_class)
|
||||
#msg = _("Enter the seed for your %s wallet:" % self.device)
|
||||
#title=_('Restore hardware wallet'),
|
||||
f = lambda seed: wizard.run('on_restore_seed', seed)
|
||||
wizard.restore_seed_dialog(run_next=f, is_valid=self.is_valid_seed)
|
||||
|
||||
def on_restore_seed(self, wizard, seed):
|
||||
f = lambda passphrase: wizard.run('on_restore_passphrase', seed, passphrase)
|
||||
self.device = ''
|
||||
wizard.request_passphrase(self.device, run_next=f)
|
||||
|
||||
def on_restore_passphrase(self, wizard, seed, passphrase):
|
||||
f = lambda pw: wizard.run('on_restore_password', seed, passphrase, pw)
|
||||
wizard.request_password(run_next=f)
|
||||
|
||||
def on_restore_password(self, wizard, seed, passphrase, password):
|
||||
self.add_seed_and_xprv(seed, password, passphrase)
|
||||
self.save(wizard.storage, 'x/')
|
||||
def bip39_to_seed(mnemonic, passphrase):
|
||||
import pbkdf2, hashlib, hmac
|
||||
PBKDF2_ROUNDS = 2048
|
||||
mnemonic = normalize('NFKD', ' '.join(mnemonic.split()))
|
||||
passphrase = bip39_normalize_passphrase(passphrase)
|
||||
return pbkdf2.PBKDF2(mnemonic, 'mnemonic' + passphrase,
|
||||
iterations = PBKDF2_ROUNDS, macmodule = hmac,
|
||||
digestmodule = hashlib.sha512).read(64)
|
||||
|
||||
|
||||
|
||||
|
@ -596,7 +536,7 @@ def load_keystore(storage, name):
|
|||
k = Imported_KeyStore()
|
||||
elif name and name not in [ 'x/', 'x1/' ]:
|
||||
k = BIP32_KeyStore()
|
||||
elif t == 'seed':
|
||||
elif t in ['seed', 'hw_seed']:
|
||||
k = BIP32_KeyStore()
|
||||
elif t == 'hardware':
|
||||
hw_type = storage.get('hardware_type')
|
||||
|
@ -606,8 +546,6 @@ def load_keystore(storage, name):
|
|||
break
|
||||
else:
|
||||
raise BaseException('unknown hardware type')
|
||||
elif t == 'hw_seed':
|
||||
k = BIP44_KeyStore()
|
||||
else:
|
||||
raise BaseException('unknown wallet type', t)
|
||||
k.load(storage, name)
|
||||
|
@ -665,7 +603,9 @@ def from_seed(seed, password):
|
|||
keystore.add_seed(seed, password)
|
||||
elif is_new_seed(seed):
|
||||
keystore = BIP32_KeyStore()
|
||||
keystore.add_seed_and_xprv(seed, password)
|
||||
keystore.add_seed(seed, password)
|
||||
bip32_seed = Mnemonic.mnemonic_to_seed(seed, '')
|
||||
keystore.add_xprv_from_seed(bip32_seed, "m/", password)
|
||||
return keystore
|
||||
|
||||
def from_private_key_list(text, password):
|
||||
|
|
|
@ -3,7 +3,7 @@ from struct import pack
|
|||
|
||||
from electrum.i18n import _
|
||||
from electrum.util import PrintError, UserCancelled
|
||||
from electrum.keystore import BIP44_KeyStore
|
||||
from electrum.keystore import bip39_normalize_passphrase
|
||||
from electrum.bitcoin import EncodeBase58Check
|
||||
|
||||
|
||||
|
@ -65,7 +65,7 @@ class GuiMixin(object):
|
|||
passphrase = self.handler.get_passphrase(msg, self.creating_wallet)
|
||||
if passphrase is None:
|
||||
return self.proto.Cancel()
|
||||
passphrase = BIP44_KeyStore.normalize_passphrase(passphrase)
|
||||
passphrase = bip39_normalize_passphrase(passphrase)
|
||||
return self.proto.PassphraseAck(passphrase=passphrase)
|
||||
|
||||
def callback_WordRequest(self, msg):
|
||||
|
|
|
@ -22,14 +22,13 @@ from ..hw_wallet import HW_PluginBase
|
|||
TIM_NEW, TIM_RECOVER, TIM_MNEMONIC, TIM_PRIVKEY = range(0, 4)
|
||||
|
||||
class TrezorCompatibleKeyStore(Hardware_KeyStore):
|
||||
root = "m/44'/0'"
|
||||
account_id = 0
|
||||
|
||||
def load(self, storage, name):
|
||||
self.xpub = storage.get('master_public_keys', {}).get(name)
|
||||
self.account_id = storage.get('account_id')
|
||||
|
||||
def get_derivation(self):
|
||||
return self.root + "/%d'"%self.account_id
|
||||
return "m/44'/0'/%d'"%self.account_id
|
||||
|
||||
def get_client(self, force_pair=True):
|
||||
return self.plugin.get_client(self, force_pair)
|
||||
|
|
Loading…
Reference in New Issue