Browse Source

qt: generalize wizard HWW xpub

master
Sander van Grieken 2 years ago
parent
commit
66e9f502b0
  1. 100
      electrum/gui/qt/wizard/wallet.py
  2. 93
      electrum/plugins/bitbox02/qt.py
  3. 93
      electrum/plugins/jade/qt.py
  4. 81
      electrum/plugins/trezor/qt.py

100
electrum/gui/qt/wizard/wallet.py

@ -86,7 +86,6 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
'last': lambda d: d['wallet_exists'] and not d['wallet_needs_hw_unlock']
},
'hw_unlock': {
# 'last': True,
'gui': WCChooseHWDevice,
'next': lambda d: self.on_hardware_device(d, new_wallet=False)
}
@ -830,7 +829,7 @@ class WCMultisig(WizardComponent):
class WCImport(WizardComponent):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title=_('Import Bitcoin Addresses'))
WizardComponent.__init__(self, parent, wizard, title=_('Import Bitcoin Addresses or Private Keys'))
message = _(
'Enter a list of Bitcoin addresses (this will create a watching-only wallet), or a list of private keys.')
header_layout = QHBoxLayout()
@ -1111,7 +1110,7 @@ class WCChooseHWDevice(WizardComponent, Logger):
class WCWalletPasswordHardware(WizardComponent):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title=_('Password HW'))
WizardComponent.__init__(self, parent, wizard, title=_('Encrypt using hardware'))
self.plugins = wizard.plugins
self.playout = PasswordLayoutForHW(MSG_HW_STORAGE_ENCRYPTION)
@ -1133,7 +1132,7 @@ class WCWalletPasswordHardware(WizardComponent):
class WCHWUnlock(WizardComponent, Logger):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title=_('unlock'))
WizardComponent.__init__(self, parent, wizard, title=_('Unlocking hardware'))
Logger.__init__(self)
self.plugins = wizard.plugins
self.plugin = None
@ -1157,19 +1156,14 @@ class WCHWUnlock(WizardComponent, Logger):
try:
self.password = client.get_password_for_storage_encryption()
except Exception as e:
# TODO: handle user interaction exceptions (e.g. invalid pin) more gracefully
self.error = repr(e)
self.error = repr(e) # TODO: handle user interaction exceptions (e.g. invalid pin) more gracefully
self.logger.error(repr(e))
self.unlock_done()
self.busy = False
self.validate()
t = threading.Thread(target=unlock_task, args=(client,), daemon=True)
t.start()
def unlock_done(self):
self.logger.debug(f'Done unlock')
self.busy = False
self.validate()
def validate(self):
if self.password and not self.error:
self.apply()
@ -1180,3 +1174,85 @@ class WCHWUnlock(WizardComponent, Logger):
def apply(self):
if self.valid:
self.wizard_data['password'] = self.password
class WCHWXPub(WizardComponent, Logger):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title=_('Retrieving extended public key from hardware'))
Logger.__init__(self)
self.plugins = wizard.plugins
self.plugin = None
self._busy = True
self.xpub = None
self.root_fingerprint = None
self.label = None
self.soft_device_id = None
self.ok_l = WWLabel(_('Hardware keystore added to wallet'))
self.ok_l.setAlignment(Qt.AlignCenter)
self.layout().addWidget(self.ok_l)
def on_ready(self):
_name, _info = self.wizard_data['hardware_device']
self.plugin = self.plugins.get_plugin(_info.plugin_name)
self.title = _('Retrieving extended public key from {} ({})').format(_info.model_name, _info.label)
device_id = _info.device.id_
client = self.plugins.device_manager.client_by_id(device_id, scan_now=False)
if not client.handler:
client.handler = self.plugin.create_handler(self.wizard)
cosigner = self.wizard.current_cosigner(self.wizard_data)
xtype = cosigner['script_type']
derivation = cosigner['derivation_path']
def get_xpub_task(client, derivation, xtype):
try:
self.xpub = self.get_xpub_from_client(client, derivation, xtype)
self.root_fingerprint = client.request_root_fingerprint_from_device()
self.label = client.label()
self.soft_device_id = client.get_soft_device_id()
except Exception as e:
self.error = repr(e) # TODO: handle user interaction exceptions (e.g. invalid pin) more gracefully
self.logger.error(repr(e))
self.logger.debug(f'Done retrieve xpub: {self.xpub}')
self.busy = False
self.validate()
t = threading.Thread(target=get_xpub_task, args=(client, derivation, xtype), daemon=True)
t.start()
def get_xpub_from_client(self, client, derivation, xtype): # override for HWW specific client if needed
return client.get_xpub(derivation, xtype)
def validate(self):
if self.xpub and not self.error:
self.apply()
valid, error = self.wizard.check_multisig_constraints(self.wizard_data)
if not valid:
self.error = '\n'.join([
_('Could not add hardware keystore to wallet'),
error
])
self.valid = valid
else:
self.valid = False
def apply(self):
_name, _info = self.wizard_data['hardware_device']
cosigner_data = self.wizard.current_cosigner(self.wizard_data)
cosigner_data['hw_type'] = _info.plugin_name
cosigner_data['master_key'] = self.xpub
cosigner_data['root_fingerprint'] = self.root_fingerprint
cosigner_data['label'] = self.label
cosigner_data['soft_device_id'] = self.soft_device_id
class WCHWUninitialized(WizardComponent):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title=_('Hardware not initialized'))
def on_ready(self):
_name, _info = self.wizard_data['hardware_device']
self.layout().addWidget(WWLabel(_('This {} is not initialized. Cannot continue').format(_info.model_name)))

93
electrum/plugins/bitbox02/qt.py

@ -1,4 +1,3 @@
import threading
from functools import partial
from typing import TYPE_CHECKING
@ -15,7 +14,7 @@ from PyQt5.QtCore import Qt, QMetaObject, Q_RETURN_ARG, pyqtSlot
from electrum.gui.qt.util import (
WindowModalDialog,
OkButton,
ButtonsTextEdit, WWLabel,
ButtonsTextEdit,
)
from electrum.i18n import _
@ -24,9 +23,7 @@ from electrum.plugin import hook
from .bitbox02 import BitBox02Plugin
from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
from ..hw_wallet.plugin import only_hook_if_libraries_available
from electrum.gui.qt.wizard.wizard import WizardComponent
from electrum.gui.qt.wizard.wallet import WCScriptAndDerivation, WCHWUnlock
from electrum.logging import Logger
from electrum.gui.qt.wizard.wallet import WCScriptAndDerivation, WCHWUnlock, WCHWUninitialized, WCHWXPub
if TYPE_CHECKING:
from electrum.gui.qt.wizard.wallet import QENewWalletWizard
@ -80,9 +77,9 @@ class Plugin(BitBox02Plugin, QtPluginBase):
def extend_wizard(self, wizard: 'QENewWalletWizard'):
super().extend_wizard(wizard)
views = {
'bitbox_start': { 'gui': WCScriptAndDerivation },
'bitbox_xpub': { 'gui': WCBitboxXPub },
'bitbox_not_initialized': {'gui': WCBitboxNope},
'bitbox_start': {'gui': WCScriptAndDerivation},
'bitbox_xpub': {'gui': WCHWXPub},
'bitbox_not_initialized': {'gui': WCHWUninitialized},
'bitbox_unlock': {'gui': WCHWUnlock}
}
wizard.navmap_merge(views)
@ -128,83 +125,3 @@ class BitBox02_Handler(QtHandlerBase):
dialog.setLayout(vbox)
dialog.exec_()
return name.text().strip()
# TODO: almost verbatim copy of trezor WCTrezorXPub, generalize!
# problem: client.get_xpub is not uniform
class WCBitboxXPub(WizardComponent, Logger):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title=_('Hardware wallet information'))
Logger.__init__(self)
self.plugins = wizard.plugins
self.plugin = self.plugins.get_plugin('bitbox02')
self.busy_msg = _('Unlock your Bitbox02')
self._busy = True
self.xpub = None
self.root_fingerprint = None
self.label = None
self.soft_device_id = None
self.ok_l = WWLabel(_('Hardware keystore added to wallet'))
self.ok_l.setAlignment(Qt.AlignCenter)
self.layout().addWidget(self.ok_l)
def on_ready(self):
_name, _info = self.wizard_data['hardware_device']
device_id = _info.device.id_
client = self.plugins.device_manager.client_by_id(device_id, scan_now=False)
if not client.handler:
client.handler = self.plugin.create_handler(self.wizard)
cosigner = self.wizard.current_cosigner(self.wizard_data)
xtype = cosigner['script_type']
derivation = cosigner['derivation_path']
def get_xpub_task(client, derivation, xtype):
try:
self.xpub = client.get_xpub(derivation, xtype)
self.root_fingerprint = client.request_root_fingerprint_from_device()
self.label = client.label()
self.soft_device_id = client.get_soft_device_id()
except Exception as e:
# TODO: handle user interaction exceptions (e.g. invalid pin) more gracefully
self.error = repr(e)
self.logger.error(repr(e))
self.xpub_done()
t = threading.Thread(target=get_xpub_task, args=(client, derivation, xtype), daemon=True)
t.start()
def xpub_done(self):
self.logger.debug(f'Done retrieve xpub: {self.xpub}')
self.busy = False
self.validate()
def validate(self):
if self.xpub and not self.error:
self.apply()
valid, error = self.wizard.check_multisig_constraints(self.wizard_data)
if not valid:
self.error = '\n'.join([
_('Could not add hardware keystore to wallet'),
error
])
self.valid = valid
else:
self.valid = False
def apply(self):
cosigner_data = self.wizard.current_cosigner(self.wizard_data)
cosigner_data['hw_type'] = 'bitbox02'
cosigner_data['master_key'] = self.xpub
cosigner_data['root_fingerprint'] = self.root_fingerprint
cosigner_data['label'] = self.label
cosigner_data['soft_device_id'] = self.soft_device_id
class WCBitboxNope(WizardComponent):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title=_('Bitbox02 not initialized'))
self.layout().addWidget(WWLabel(_('This Bitbox02 is not initialized. Cannot continue')))

93
electrum/plugins/jade/qt.py

@ -1,19 +1,15 @@
import threading
from functools import partial
from typing import TYPE_CHECKING
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtCore import pyqtSignal
from electrum.i18n import _
from electrum.plugin import hook
from electrum.wallet import Standard_Wallet
from electrum.logging import Logger
from electrum.plugins.hw_wallet.qt import QtHandlerBase, QtPluginBase
from electrum.plugins.hw_wallet import plugin
from electrum.gui.qt.util import WWLabel
from electrum.gui.qt.wizard.wallet import WCScriptAndDerivation, WCHWUnlock
from electrum.gui.qt.wizard.wizard import WizardComponent
from electrum.gui.qt.wizard.wallet import WCScriptAndDerivation, WCHWUnlock, WCHWXPub, WCHWUninitialized
from .jade import JadePlugin
@ -47,9 +43,9 @@ class Plugin(JadePlugin, QtPluginBase):
def extend_wizard(self, wizard: 'QENewWalletWizard'):
super().extend_wizard(wizard)
views = {
'jade_start': { 'gui': WCScriptAndDerivation },
'jade_xpub': { 'gui': WCJadeXPub },
'jade_not_initialized': {'gui': WCJadeNope},
'jade_start': {'gui': WCScriptAndDerivation},
'jade_xpub': {'gui': WCHWXPub},
'jade_not_initialized': {'gui': WCHWUninitialized},
'jade_unlock': {'gui': WCHWUnlock}
}
wizard.navmap_merge(views)
@ -64,82 +60,3 @@ class Jade_Handler(QtHandlerBase):
def __init__(self, win):
super(Jade_Handler, self).__init__(win, 'Jade')
# TODO: almost verbatim copy of trezor WCTrezorXPub, generalize!
# problem: client.get_xpub is not uniform
class WCJadeXPub(WizardComponent, Logger):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title=_('Hardware wallet information'))
Logger.__init__(self)
self.plugins = wizard.plugins
self.plugin = self.plugins.get_plugin('jade')
self.busy_msg = _('Unlock your Jade')
self._busy = True
self.xpub = None
self.root_fingerprint = None
self.label = None
self.soft_device_id = None
self.ok_l = WWLabel(_('Hardware keystore added to wallet'))
self.ok_l.setAlignment(Qt.AlignCenter)
self.layout().addWidget(self.ok_l)
def on_ready(self):
_name, _info = self.wizard_data['hardware_device']
device_id = _info.device.id_
client = self.plugins.device_manager.client_by_id(device_id, scan_now=False)
if not client.handler:
client.handler = self.plugin.create_handler(self.wizard)
cosigner = self.wizard.current_cosigner(self.wizard_data)
xtype = cosigner['script_type']
derivation = cosigner['derivation_path']
def get_xpub_task(client, derivation, xtype):
try:
self.xpub = client.get_xpub(derivation, xtype)
self.root_fingerprint = client.request_root_fingerprint_from_device()
self.label = client.label()
self.soft_device_id = client.get_soft_device_id()
except Exception as e:
# TODO: handle user interaction exceptions (e.g. invalid pin) more gracefully
self.error = repr(e)
self.logger.error(repr(e))
self.xpub_done()
t = threading.Thread(target=get_xpub_task, args=(client, derivation, xtype), daemon=True)
t.start()
def xpub_done(self):
self.logger.debug(f'Done retrieve xpub: {self.xpub}')
self.busy = False
self.validate()
def validate(self):
if self.xpub and not self.error:
self.apply()
valid, error = self.wizard.check_multisig_constraints(self.wizard_data)
if not valid:
self.error = '\n'.join([
_('Could not add hardware keystore to wallet'),
error
])
self.valid = valid
else:
self.valid = False
def apply(self):
cosigner_data = self.wizard.current_cosigner(self.wizard_data)
cosigner_data['hw_type'] = 'jade'
cosigner_data['master_key'] = self.xpub
cosigner_data['root_fingerprint'] = self.root_fingerprint
cosigner_data['label'] = self.label
cosigner_data['soft_device_id'] = self.soft_device_id
class WCJadeNope(WizardComponent):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title=_('Jade not initialized'))
self.layout().addWidget(WWLabel(_('This Jade is not initialized. Cannot continue')))

81
electrum/plugins/trezor/qt.py

@ -17,7 +17,7 @@ from electrum.plugins.hw_wallet.plugin import only_hook_if_libraries_available
from electrum.gui.qt.util import (WindowModalDialog, WWLabel, Buttons, CancelButton,
OkButton, CloseButton, PasswordLineEdit, getOpenFileName, ChoiceWidget)
from electrum.gui.qt.wizard.wallet import WCScriptAndDerivation, WCHWUnlock
from electrum.gui.qt.wizard.wallet import WCScriptAndDerivation, WCHWUnlock, WCHWXPub
from electrum.gui.qt.wizard.wizard import WizardComponent
from .trezor import (TrezorPlugin, TIM_NEW, TIM_RECOVER, TrezorInitSettings,
@ -473,11 +473,11 @@ class Plugin(TrezorPlugin, QtPlugin):
def extend_wizard(self, wizard: 'QENewWalletWizard'):
super().extend_wizard(wizard)
views = {
'trezor_start': { 'gui': WCScriptAndDerivation },
'trezor_xpub': { 'gui': WCTrezorXPub },
'trezor_not_initialized': { 'gui': WCTrezorInitMethod },
'trezor_choose_new_recover': { 'gui': WCTrezorInitParams },
'trezor_do_init': { 'gui': WCTrezorInit },
'trezor_start': {'gui': WCScriptAndDerivation},
'trezor_xpub': {'gui': WCTrezorXPub},
'trezor_not_initialized': {'gui': WCTrezorInitMethod},
'trezor_choose_new_recover': {'gui': WCTrezorInitParams},
'trezor_do_init': {'gui': WCTrezorInit},
'trezor_unlock': {'gui': WCHWUnlock},
}
wizard.navmap_merge(views)
@ -801,73 +801,12 @@ class SettingsDialog(WindowModalDialog):
invoke_client(None)
class WCTrezorXPub(WizardComponent, Logger):
class WCTrezorXPub(WCHWXPub):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title=_('Hardware wallet information'))
Logger.__init__(self)
self.plugins = wizard.plugins
self.plugin = self.plugins.get_plugin('trezor')
self._busy = True
self.xpub = None
self.root_fingerprint = None
self.label = None
self.soft_device_id = None
self.ok_l = WWLabel(_('Hardware keystore added to wallet'))
self.ok_l.setAlignment(Qt.AlignCenter)
self.layout().addWidget(self.ok_l)
def on_ready(self):
_name, _info = self.wizard_data['hardware_device']
device_id = _info.device.id_
client = self.plugins.device_manager.client_by_id(device_id, scan_now=False)
client.handler = self.plugin.create_handler(self.wizard)
cosigner = self.wizard.current_cosigner(self.wizard_data)
xtype = cosigner['script_type']
derivation = cosigner['derivation_path']
def get_xpub_task(client, derivation, xtype):
try:
self.xpub = client.get_xpub(derivation, xtype, True)
self.root_fingerprint = client.request_root_fingerprint_from_device()
self.label = client.label()
self.soft_device_id = client.get_soft_device_id()
except Exception as e:
# TODO: handle user interaction exceptions (e.g. invalid pin) more gracefully
self.error = repr(e)
self.logger.error(repr(e))
self.xpub_done()
t = threading.Thread(target=get_xpub_task, args=(client, derivation, xtype), daemon=True)
t.start()
WCHWXPub.__init__(self, parent, wizard)
def xpub_done(self):
self.logger.debug(f'Done retrieve xpub: {self.xpub}')
self.busy = False
self.validate()
def validate(self):
if self.xpub and not self.error:
self.apply()
valid, error = self.wizard.check_multisig_constraints(self.wizard_data)
if not valid:
self.error = '\n'.join([
_('Could not add hardware keystore to wallet'),
error
])
self.valid = valid
else:
self.valid = False
def apply(self):
cosigner_data = self.wizard.current_cosigner(self.wizard_data)
cosigner_data['hw_type'] = 'trezor'
cosigner_data['master_key'] = self.xpub
cosigner_data['root_fingerprint'] = self.root_fingerprint
cosigner_data['label'] = self.label
cosigner_data['soft_device_id'] = self.soft_device_id
def get_xpub_from_client(self, client, derivation, xtype):
return client.get_xpub(derivation, xtype, True)
class WCTrezorInitMethod(WizardComponent, Logger):

Loading…
Cancel
Save