Trezor: all four available device initializations
Trezor and KeepKey devices can now be initialized by: - device-generated seed - existing seed - BIP39 mnemonic - master private key
This commit is contained in:
@@ -53,10 +53,10 @@ class GuiMixin(object):
|
||||
return self.proto.PassphraseAck(passphrase=passphrase)
|
||||
|
||||
def callback_WordRequest(self, msg):
|
||||
# TODO
|
||||
stderr.write("Enter one word of mnemonic:\n")
|
||||
stderr.flush()
|
||||
word = raw_input()
|
||||
msg = _("Enter seed word as explained on your %s") % self.device
|
||||
word = self.handler().get_word(msg)
|
||||
if word is None:
|
||||
return self.proto.Cancel()
|
||||
return self.proto.WordAck(word=word)
|
||||
|
||||
|
||||
@@ -184,8 +184,9 @@ def trezor_client_class(protocol_mixin, base_client, proto):
|
||||
|
||||
cls = TrezorClient
|
||||
for method in ['apply_settings', 'change_pin', 'get_address',
|
||||
'get_public_node', 'reset_device', 'sign_message',
|
||||
'sign_tx', 'wipe_device']:
|
||||
'get_public_node', 'load_device_by_mnemonic',
|
||||
'load_device_by_xprv', 'recovery_device',
|
||||
'reset_device', 'sign_message', 'sign_tx', 'wipe_device']:
|
||||
setattr(cls, method, wrapper(getattr(cls, method)))
|
||||
|
||||
return cls
|
||||
|
||||
@@ -14,6 +14,7 @@ from electrum.transaction import (deserialize, is_extended_pubkey,
|
||||
from electrum.wallet import BIP32_HD_Wallet, BIP44_Wallet
|
||||
from electrum.util import ThreadJob
|
||||
from electrum.plugins import DeviceMgr
|
||||
from electrum.wizard import WizardBase
|
||||
|
||||
class DeviceDisconnectedError(Exception):
|
||||
pass
|
||||
@@ -251,16 +252,47 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
|
||||
# Prevent timeouts during initialization
|
||||
wallet.last_operation = self.prevent_timeout
|
||||
|
||||
(strength, label, pin_protection, passphrase_protection) \
|
||||
= wizard.request_trezor_reset_settings(self.device)
|
||||
# Initialization method
|
||||
msg = _("Please select how you want to initialize your %s.\n"
|
||||
"The first two are secure as no secret information is entered "
|
||||
"onto your computer.\nFor the last two methods you enter "
|
||||
"secrets into your computer and upload them to the device, "
|
||||
"and so should only be done on a computer you know to be "
|
||||
"trustworthy and free of malware."
|
||||
) % self.device
|
||||
|
||||
assert strength in range(0, 3)
|
||||
strength = 64 * (strength + 2) # 128, 192 or 256
|
||||
language = ''
|
||||
methods = [
|
||||
_("Let the device generate a completely new seed randomly"),
|
||||
_("Recover from an existing %s seed you have previously written "
|
||||
"down" % self.device),
|
||||
_("Upload a BIP39 mnemonic to generate the seed"),
|
||||
_("Upload a master private key")
|
||||
]
|
||||
|
||||
method = wizard.query_choice(msg, methods)
|
||||
(item, label, pin_protection, passphrase_protection) \
|
||||
= wizard.request_trezor_init_settings(method, self.device)
|
||||
|
||||
client = self.get_client(wallet)
|
||||
client.reset_device(True, strength, passphrase_protection,
|
||||
pin_protection, label, language)
|
||||
language = 'english'
|
||||
|
||||
if method == WizardBase.TIM_NEW:
|
||||
strength = 64 * (item + 2) # 128, 192 or 256
|
||||
client.reset_device(True, strength, passphrase_protection,
|
||||
pin_protection, label, language)
|
||||
elif method == WizardBase.TIM_RECOVER:
|
||||
word_count = 6 * (item + 2) # 12, 18 or 24
|
||||
client.recovery_device(word_count, passphrase_protection,
|
||||
pin_protection, label, language)
|
||||
elif method == WizardBase.TIM_MNEMONIC:
|
||||
pin = pin_protection # It's the pin, not a boolean
|
||||
client.load_device_by_mnemonic(str(item), pin,
|
||||
passphrase_protection,
|
||||
label, language)
|
||||
else:
|
||||
pin = pin_protection # It's the pin, not a boolean
|
||||
client.load_device_by_xprv(item, pin, passphrase_protection,
|
||||
label, language)
|
||||
|
||||
def select_device(self, wallet, wizard):
|
||||
'''Called when creating a new wallet. Select the device to use. If
|
||||
@@ -268,9 +300,12 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
|
||||
process.'''
|
||||
self.device_manager().scan_devices()
|
||||
clients = self.device_manager().clients_of_type(self.client_class)
|
||||
suffixes = [_("An unnamed device (wiped)"), _(" (initialized)")]
|
||||
labels = [client.label() + suffixes[client.is_initialized()]
|
||||
for client in clients]
|
||||
suffixes = [_(" (wiped)"), _(" (initialized)")]
|
||||
def client_desc(client):
|
||||
label = client.label() or _("An unnamed device")
|
||||
return label + suffixes[client.is_initialized()]
|
||||
labels = list(map(client_desc, clients))
|
||||
|
||||
msg = _("Please select which %s device to use:") % self.device
|
||||
client = clients[wizard.query_choice(msg, labels)]
|
||||
self.device_manager().pair_wallet(wallet, client)
|
||||
|
||||
@@ -28,6 +28,7 @@ class QtHandler(PrintError):
|
||||
win.connect(win, SIGNAL('message_dialog'), self.message_dialog)
|
||||
win.connect(win, SIGNAL('pin_dialog'), self.pin_dialog)
|
||||
win.connect(win, SIGNAL('passphrase_dialog'), self.passphrase_dialog)
|
||||
win.connect(win, SIGNAL('word_dialog'), self.word_dialog)
|
||||
self.window_stack = [win]
|
||||
self.win = win
|
||||
self.pin_matrix_widget_class = pin_matrix_widget_class
|
||||
@@ -53,6 +54,12 @@ class QtHandler(PrintError):
|
||||
self.done.wait()
|
||||
return self.response
|
||||
|
||||
def get_word(self, msg):
|
||||
self.done.clear()
|
||||
self.win.emit(SIGNAL('word_dialog'), msg)
|
||||
self.done.wait()
|
||||
return self.word
|
||||
|
||||
def get_passphrase(self, msg):
|
||||
self.done.clear()
|
||||
self.win.emit(SIGNAL('passphrase_dialog'), msg)
|
||||
@@ -82,6 +89,20 @@ class QtHandler(PrintError):
|
||||
self.passphrase = passphrase
|
||||
self.done.set()
|
||||
|
||||
def word_dialog(self, msg):
|
||||
dialog = WindowModalDialog(self.window_stack[-1], "")
|
||||
hbox = QHBoxLayout(dialog)
|
||||
hbox.addWidget(QLabel(msg))
|
||||
text = QLineEdit()
|
||||
text.setMaximumWidth(100)
|
||||
text.returnPressed.connect(dialog.accept)
|
||||
hbox.addWidget(text)
|
||||
hbox.addStretch(1)
|
||||
if not self.exec_dialog(dialog):
|
||||
return None
|
||||
self.word = unicode(text.text())
|
||||
self.done.set()
|
||||
|
||||
def message_dialog(self, msg, cancel_callback):
|
||||
# Called more than once during signing, to confirm output and fee
|
||||
self.clear_dialog()
|
||||
@@ -108,7 +129,7 @@ class QtHandler(PrintError):
|
||||
def exec_dialog(self, dialog):
|
||||
self.window_stack.append(dialog)
|
||||
try:
|
||||
dialog.exec_()
|
||||
return dialog.exec_()
|
||||
finally:
|
||||
assert dialog == self.window_stack.pop()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user