Move trezor-specific install wizard code to plugin
This commit is contained in:
parent
54cdd551fe
commit
3e8598c245
|
@ -418,88 +418,3 @@ class InstallWizard(WindowModalDialog, WizardBase):
|
||||||
self.set_layout(vbox)
|
self.set_layout(vbox)
|
||||||
if not self.exec_():
|
if not self.exec_():
|
||||||
raise UserCancelled
|
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())
|
|
||||||
|
|
|
@ -48,7 +48,6 @@ class WizardBase(PrintError):
|
||||||
('multisig', _("Multi-signature wallet")),
|
('multisig', _("Multi-signature wallet")),
|
||||||
('hardware', _("Hardware wallet")),
|
('hardware', _("Hardware wallet")),
|
||||||
]
|
]
|
||||||
TIM_NEW, TIM_RECOVER, TIM_MNEMONIC, TIM_PRIVKEY = range(0, 4)
|
|
||||||
|
|
||||||
# Derived classes must set:
|
# Derived classes must set:
|
||||||
# self.language_for_seed
|
# self.language_for_seed
|
||||||
|
@ -103,23 +102,6 @@ class WizardBase(PrintError):
|
||||||
dynamic feedback. If not provided, Wallet.is_any is used."""
|
dynamic feedback. If not provided, Wallet.is_any is used."""
|
||||||
raise NotImplementedError
|
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):
|
def request_many(self, n, xpub_hot=None):
|
||||||
"""If xpub_hot is provided, a new wallet is being created. Request N
|
"""If xpub_hot is provided, a new wallet is being created. Request N
|
||||||
master public keys for cosigners; xpub_hot is the master xpub
|
master public keys for cosigners; xpub_hot is the master xpub
|
||||||
|
|
|
@ -14,7 +14,9 @@ from electrum.transaction import (deserialize, is_extended_pubkey,
|
||||||
from electrum.wallet import BIP32_HD_Wallet, BIP44_Wallet
|
from electrum.wallet import BIP32_HD_Wallet, BIP44_Wallet
|
||||||
from electrum.util import ThreadJob
|
from electrum.util import ThreadJob
|
||||||
from electrum.plugins import DeviceMgr
|
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):
|
class DeviceDisconnectedError(Exception):
|
||||||
pass
|
pass
|
||||||
|
@ -247,7 +249,7 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
|
||||||
if isinstance(wallet, self.wallet_class):
|
if isinstance(wallet, self.wallet_class):
|
||||||
self.device_manager().close_wallet(wallet)
|
self.device_manager().close_wallet(wallet)
|
||||||
|
|
||||||
def initialize_device(self, wallet, wizard):
|
def initialize_device(self, wallet):
|
||||||
# Prevent timeouts during initialization
|
# Prevent timeouts during initialization
|
||||||
wallet.last_operation = self.prevent_timeout
|
wallet.last_operation = self.prevent_timeout
|
||||||
|
|
||||||
|
@ -268,22 +270,22 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
|
||||||
_("Upload a master private key")
|
_("Upload a master private key")
|
||||||
]
|
]
|
||||||
|
|
||||||
method = wizard.query_choice(msg, methods)
|
method = wallet.handler.query_choice(msg, methods)
|
||||||
(item, label, pin_protection, passphrase_protection) \
|
(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)
|
client = self.get_client(wallet)
|
||||||
language = 'english'
|
language = 'english'
|
||||||
|
|
||||||
if method == WizardBase.TIM_NEW:
|
if method == TIM_NEW:
|
||||||
strength = 64 * (item + 2) # 128, 192 or 256
|
strength = 64 * (item + 2) # 128, 192 or 256
|
||||||
client.reset_device(True, strength, passphrase_protection,
|
client.reset_device(True, strength, passphrase_protection,
|
||||||
pin_protection, label, language)
|
pin_protection, label, language)
|
||||||
elif method == WizardBase.TIM_RECOVER:
|
elif method == TIM_RECOVER:
|
||||||
word_count = 6 * (item + 2) # 12, 18 or 24
|
word_count = 6 * (item + 2) # 12, 18 or 24
|
||||||
client.recovery_device(word_count, passphrase_protection,
|
client.recovery_device(word_count, passphrase_protection,
|
||||||
pin_protection, label, language)
|
pin_protection, label, language)
|
||||||
elif method == WizardBase.TIM_MNEMONIC:
|
elif method == TIM_MNEMONIC:
|
||||||
pin = pin_protection # It's the pin, not a boolean
|
pin = pin_protection # It's the pin, not a boolean
|
||||||
client.load_device_by_mnemonic(str(item), pin,
|
client.load_device_by_mnemonic(str(item), pin,
|
||||||
passphrase_protection,
|
passphrase_protection,
|
||||||
|
@ -293,7 +295,7 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
|
||||||
client.load_device_by_xprv(item, pin, passphrase_protection,
|
client.load_device_by_xprv(item, pin, passphrase_protection,
|
||||||
label, language)
|
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
|
'''Called when creating a new wallet. Select the device to use. If
|
||||||
the device is uninitialized, go through the intialization
|
the device is uninitialized, go through the intialization
|
||||||
process.'''
|
process.'''
|
||||||
|
@ -306,10 +308,10 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
|
||||||
labels = list(map(client_desc, clients))
|
labels = list(map(client_desc, clients))
|
||||||
|
|
||||||
msg = _("Please select which %s device to use:") % self.device
|
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)
|
self.device_manager().pair_wallet(wallet, client)
|
||||||
if not client.is_initialized():
|
if not client.is_initialized():
|
||||||
self.initialize_device(wallet, wizard)
|
self.initialize_device(wallet)
|
||||||
|
|
||||||
def on_restore_wallet(self, wallet, wizard):
|
def on_restore_wallet(self, wallet, wizard):
|
||||||
assert isinstance(wallet, self.wallet_class)
|
assert isinstance(wallet, self.wallet_class)
|
||||||
|
|
|
@ -8,12 +8,13 @@ from PyQt4.Qt import QVBoxLayout, QLabel, SIGNAL
|
||||||
from electrum_gui.qt.main_window import StatusBarButton
|
from electrum_gui.qt.main_window import StatusBarButton
|
||||||
from electrum_gui.qt.password_dialog import PasswordDialog
|
from electrum_gui.qt.password_dialog import PasswordDialog
|
||||||
from electrum_gui.qt.util import *
|
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.i18n import _
|
||||||
from electrum.plugins import hook, DeviceMgr
|
from electrum.plugins import hook, DeviceMgr
|
||||||
from electrum.util import PrintError
|
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;
|
# By far the trickiest thing about this handler is the window stack;
|
||||||
|
@ -134,6 +135,97 @@ class QtHandler(PrintError):
|
||||||
finally:
|
finally:
|
||||||
assert dialog == self.window_stack.pop()
|
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):
|
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):
|
def on_create_wallet(self, wallet, wizard):
|
||||||
assert type(wallet) == self.wallet_class
|
assert type(wallet) == self.wallet_class
|
||||||
wallet.handler = self.create_handler(wizard)
|
wallet.handler = self.create_handler(wizard)
|
||||||
self.select_device(wallet, wizard)
|
self.select_device(wallet)
|
||||||
wallet.create_hd_account(None)
|
wallet.create_hd_account(None)
|
||||||
|
|
||||||
@hook
|
@hook
|
||||||
|
|
Loading…
Reference in New Issue