Move trezor-specific install wizard code to plugin

This commit is contained in:
Neil Booth 2016-01-09 15:20:31 +09:00
parent 54cdd551fe
commit 3e8598c245
4 changed files with 107 additions and 116 deletions

View File

@ -418,88 +418,3 @@ class InstallWizard(WindowModalDialog, WizardBase):
self.set_layout(vbox)
if not self.exec_():
raise UserCancelled
def request_trezor_init_settings(self, method, device):
vbox = QVBoxLayout()
main_label = QLabel(_("Initialization settings for your %s:") % device)
vbox.addWidget(main_label)
OK_button = OkButton(self, _('Next'))
if method in [self.TIM_NEW, self.TIM_RECOVER]:
gb = QGroupBox()
vbox1 = QVBoxLayout()
gb.setLayout(vbox1)
vbox.addWidget(gb)
gb.setTitle(_("Select your seed length:"))
choices = [
_("12 words"),
_("18 words"),
_("24 words"),
]
bg = QButtonGroup()
for i, choice in enumerate(choices):
rb = QRadioButton(gb)
rb.setText(choice)
bg.addButton(rb)
bg.setId(rb, i)
vbox1.addWidget(rb)
rb.setChecked(True)
cb_pin = QCheckBox(_('Enable PIN protection'))
cb_pin.setChecked(True)
else:
text = QTextEdit()
text.setMaximumHeight(60)
if method == self.TIM_MNEMONIC:
msg = _("Enter your BIP39 mnemonic:")
else:
msg = _("Enter the master private key beginning with xprv:")
def set_enabled():
OK_button.setEnabled(Wallet.is_xprv(
self.get_seed_text(text)))
text.textChanged.connect(set_enabled)
OK_button.setEnabled(False)
vbox.addWidget(QLabel(msg))
vbox.addWidget(text)
pin = QLineEdit()
pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,10}')))
pin.setMaximumWidth(100)
hbox_pin = QHBoxLayout()
hbox_pin.addWidget(QLabel(_("Enter your PIN (digits 1-9):")))
hbox_pin.addWidget(pin)
hbox_pin.addStretch(1)
label = QLabel(_("Enter a label to name your device:"))
name = QLineEdit()
hl = QHBoxLayout()
hl.addWidget(label)
hl.addWidget(name)
hl.addStretch(1)
vbox.addLayout(hl)
if method in [self.TIM_NEW, self.TIM_RECOVER]:
vbox.addWidget(cb_pin)
else:
vbox.addLayout(hbox_pin)
cb_phrase = QCheckBox(_('Enable Passphrase protection'))
cb_phrase.setChecked(False)
vbox.addWidget(cb_phrase)
vbox.addStretch(1)
vbox.addLayout(Buttons(CancelButton(self), OK_button))
self.set_layout(vbox)
if not self.exec_():
raise UserCancelled
if method in [self.TIM_NEW, self.TIM_RECOVER]:
item = bg.checkedId()
pin = cb_pin.isChecked()
else:
item = ' '.join(str(self.get_seed_text(text)).split())
pin = str(pin.text())
return (item, unicode(name.text()), pin, cb_phrase.isChecked())

View File

@ -48,7 +48,6 @@ class WizardBase(PrintError):
('multisig', _("Multi-signature wallet")),
('hardware', _("Hardware wallet")),
]
TIM_NEW, TIM_RECOVER, TIM_MNEMONIC, TIM_PRIVKEY = range(0, 4)
# Derived classes must set:
# self.language_for_seed
@ -103,23 +102,6 @@ class WizardBase(PrintError):
dynamic feedback. If not provided, Wallet.is_any is used."""
raise NotImplementedError
def request_trezor_init_settings(self, method, device):
"""Ask the user for the information needed to initialize a trezor-
compatible device. Method is one of the TIM_ trezor init
method constants. TIM_NEW and TIM_RECOVER should ask how many
seed words to use, and return 0, 1 or 2 for a 12, 18 or 24
word seed respectively. TIM_MNEMONIC should ask for a
mnemonic. TIM_PRIVKEY should ask for a master private key.
All four methods should additionally ask for a name to label
the device, PIN information and whether passphrase protection is
to be enabled (True/False, default to False). For TIM_NEW and
TIM_RECOVER, the pin information is whether pin protection
is required (True/False, default to True); for TIM_MNEMONIC and
TIM_PRIVKEY is is the pin as a string of digits 1-9.
The result is a 4-tuple: (TIM specific data, label, pininfo,
passphraseprotection)."""
raise NotImplementedError
def request_many(self, n, xpub_hot=None):
"""If xpub_hot is provided, a new wallet is being created. Request N
master public keys for cosigners; xpub_hot is the master xpub

View File

@ -14,7 +14,9 @@ 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
# Trezor initialization methods
TIM_NEW, TIM_RECOVER, TIM_MNEMONIC, TIM_PRIVKEY = range(0, 4)
class DeviceDisconnectedError(Exception):
pass
@ -247,7 +249,7 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
if isinstance(wallet, self.wallet_class):
self.device_manager().close_wallet(wallet)
def initialize_device(self, wallet, wizard):
def initialize_device(self, wallet):
# Prevent timeouts during initialization
wallet.last_operation = self.prevent_timeout
@ -268,22 +270,22 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
_("Upload a master private key")
]
method = wizard.query_choice(msg, methods)
method = wallet.handler.query_choice(msg, methods)
(item, label, pin_protection, passphrase_protection) \
= wizard.request_trezor_init_settings(method, self.device)
= wallet.handler.request_trezor_init_settings(method, self.device)
client = self.get_client(wallet)
language = 'english'
if method == WizardBase.TIM_NEW:
if method == 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:
elif method == 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:
elif method == TIM_MNEMONIC:
pin = pin_protection # It's the pin, not a boolean
client.load_device_by_mnemonic(str(item), pin,
passphrase_protection,
@ -293,7 +295,7 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
client.load_device_by_xprv(item, pin, passphrase_protection,
label, language)
def select_device(self, wallet, wizard):
def select_device(self, wallet):
'''Called when creating a new wallet. Select the device to use. If
the device is uninitialized, go through the intialization
process.'''
@ -306,10 +308,10 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
labels = list(map(client_desc, clients))
msg = _("Please select which %s device to use:") % self.device
client = clients[wizard.query_choice(msg, labels)]
client = clients[wallet.handler.query_choice(msg, labels)]
self.device_manager().pair_wallet(wallet, client)
if not client.is_initialized():
self.initialize_device(wallet, wizard)
self.initialize_device(wallet)
def on_restore_wallet(self, wallet, wizard):
assert isinstance(wallet, self.wallet_class)

View File

@ -8,12 +8,13 @@ from PyQt4.Qt import QVBoxLayout, QLabel, SIGNAL
from electrum_gui.qt.main_window import StatusBarButton
from electrum_gui.qt.password_dialog import PasswordDialog
from electrum_gui.qt.util import *
from plugin import TrezorCompatiblePlugin
from .plugin import TrezorCompatiblePlugin, TIM_NEW, TIM_RECOVER, TIM_MNEMONIC
from electrum.i18n import _
from electrum.plugins import hook, DeviceMgr
from electrum.util import PrintError
from electrum.wallet import BIP44_Wallet
from electrum.wallet import Wallet, BIP44_Wallet
from electrum.wizard import UserCancelled
# By far the trickiest thing about this handler is the window stack;
@ -134,6 +135,97 @@ class QtHandler(PrintError):
finally:
assert dialog == self.window_stack.pop()
def query_choice(self, msg, labels):
return self.win.query_choice(msg, labels)
def request_trezor_init_settings(self, method, device):
wizard = self.win
vbox = QVBoxLayout()
main_label = QLabel(_("Initialization settings for your %s:") % device)
vbox.addWidget(main_label)
OK_button = OkButton(wizard, _('Next'))
def clean_text(widget):
text = unicode(widget.toPlainText()).strip()
return ' '.join(text.split())
if method in [TIM_NEW, TIM_RECOVER]:
gb = QGroupBox()
vbox1 = QVBoxLayout()
gb.setLayout(vbox1)
vbox.addWidget(gb)
gb.setTitle(_("Select your seed length:"))
choices = [
_("12 words"),
_("18 words"),
_("24 words"),
]
bg = QButtonGroup()
for i, choice in enumerate(choices):
rb = QRadioButton(gb)
rb.setText(choice)
bg.addButton(rb)
bg.setId(rb, i)
vbox1.addWidget(rb)
rb.setChecked(True)
cb_pin = QCheckBox(_('Enable PIN protection'))
cb_pin.setChecked(True)
else:
text = QTextEdit()
text.setMaximumHeight(60)
if method == TIM_MNEMONIC:
msg = _("Enter your BIP39 mnemonic:")
else:
msg = _("Enter the master private key beginning with xprv:")
def set_enabled():
OK_button.setEnabled(Wallet.is_xprv(clean_text(text)))
text.textChanged.connect(set_enabled)
OK_button.setEnabled(False)
vbox.addWidget(QLabel(msg))
vbox.addWidget(text)
pin = QLineEdit()
pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,10}')))
pin.setMaximumWidth(100)
hbox_pin = QHBoxLayout()
hbox_pin.addWidget(QLabel(_("Enter your PIN (digits 1-9):")))
hbox_pin.addWidget(pin)
hbox_pin.addStretch(1)
label = QLabel(_("Enter a label to name your device:"))
name = QLineEdit()
hl = QHBoxLayout()
hl.addWidget(label)
hl.addWidget(name)
hl.addStretch(1)
vbox.addLayout(hl)
if method in [TIM_NEW, TIM_RECOVER]:
vbox.addWidget(cb_pin)
else:
vbox.addLayout(hbox_pin)
cb_phrase = QCheckBox(_('Enable Passphrase protection'))
cb_phrase.setChecked(False)
vbox.addWidget(cb_phrase)
vbox.addStretch(1)
vbox.addLayout(Buttons(CancelButton(wizard), OK_button))
wizard.set_layout(vbox)
if not wizard.exec_():
raise UserCancelled
if method in [TIM_NEW, TIM_RECOVER]:
item = bg.checkedId()
pin = cb_pin.isChecked()
else:
item = ' '.join(str(clean_text(text)).split())
pin = str(pin.text())
return (item, unicode(name.text()), pin, cb_phrase.isChecked())
def qt_plugin_class(base_plugin_class):
@ -159,7 +251,7 @@ def qt_plugin_class(base_plugin_class):
def on_create_wallet(self, wallet, wizard):
assert type(wallet) == self.wallet_class
wallet.handler = self.create_handler(wizard)
self.select_device(wallet, wizard)
self.select_device(wallet)
wallet.create_hd_account(None)
@hook