From b2a41b63632041b2d4e0e1762e114749ae35b702 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Fri, 11 Aug 2023 15:34:13 +0200 Subject: [PATCH] qt: jade for new wizard --- electrum/plugins/jade/jade.py | 20 +++++++ electrum/plugins/jade/qt.py | 108 ++++++++++++++++++++++++++++++++-- electrum/plugins/trezor/qt.py | 6 +- 3 files changed, 126 insertions(+), 8 deletions(-) diff --git a/electrum/plugins/jade/jade.py b/electrum/plugins/jade/jade.py index c29004109..dc5512cd0 100644 --- a/electrum/plugins/jade/jade.py +++ b/electrum/plugins/jade/jade.py @@ -476,3 +476,23 @@ class JadePlugin(HW_PluginBase): if hw_address != address: keystore.handler.show_error(_('The address generated by {} does not match!').format(self.device)) + + # new wizard + + def wizard_entry_for_device(self, device_info: 'DeviceInfo') -> str: + return 'jade_start' if device_info.initialized else 'jade_not_initialized' + + # insert trezor pages in new wallet wizard + def extend_wizard(self, wizard: 'NewWalletWizard'): + views = { + 'jade_start': { + 'next': 'jade_xpub', + }, + 'jade_xpub': { + 'next': lambda d: wizard.wallet_password_view(d) if wizard.last_cosigner(d) else 'multisig_cosigner_keystore', + 'accept': wizard.maybe_master_pubkey, + 'last': lambda d: wizard.is_single_password() and wizard.last_cosigner(d) + }, + 'jade_not_initialized': {} + } + wizard.navmap_merge(views) diff --git a/electrum/plugins/jade/qt.py b/electrum/plugins/jade/qt.py index ee135f62d..2e5c27b7e 100644 --- a/electrum/plugins/jade/qt.py +++ b/electrum/plugins/jade/qt.py @@ -1,16 +1,20 @@ +import threading from functools import partial -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtWidgets import QLabel, QVBoxLayout +from PyQt5.QtCore import pyqtSignal, Qt from electrum.i18n import _ from electrum.plugin import hook from electrum.wallet import Standard_Wallet -from electrum.gui.qt.util import WindowModalDialog +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 +from electrum.gui.qt.wizard.wizard import WizardComponent from .jade import JadePlugin -from ..hw_wallet.qt import QtHandlerBase, QtPluginBase -from ..hw_wallet.plugin import only_hook_if_libraries_available class Plugin(JadePlugin, QtPluginBase): @@ -20,7 +24,7 @@ class Plugin(JadePlugin, QtPluginBase): def create_handler(self, window): return Jade_Handler(window) - @only_hook_if_libraries_available + @plugin.only_hook_if_libraries_available @hook def receive_menu(self, menu, addrs, wallet): if type(wallet) is not Standard_Wallet: @@ -31,6 +35,21 @@ class Plugin(JadePlugin, QtPluginBase): keystore.thread.add(partial(self.show_address, wallet, addrs[0])) menu.addAction(_("Show on Jade"), show_address) + @hook + def init_wallet_wizard(self, wizard: 'QEWalletWizard'): + self.extend_wizard(wizard) + + # insert trezor pages in new wallet wizard + def extend_wizard(self, wizard: 'NewWalletWizard'): + super().extend_wizard(wizard) + views = { + 'jade_start': { 'gui': WCScriptAndDerivation }, + 'jade_xpub': { 'gui': WCJadeXPub }, + 'jade_not_initialized': {'gui': WCJadeNope}, + } + wizard.navmap_merge(views) + + class Jade_Handler(QtHandlerBase): setup_signal = pyqtSignal() auth_signal = pyqtSignal(object, object) @@ -39,3 +58,80 @@ class Jade_Handler(QtHandlerBase): def __init__(self, win): super(Jade_Handler, self).__init__(win, 'Jade') + + +class WCJadeXPub(WizardComponent, Logger): # TODO: almost verbatim copy of trezor WCTrezorXPub, generalize! + 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'))) diff --git a/electrum/plugins/trezor/qt.py b/electrum/plugins/trezor/qt.py index 1c03d0fdf..367a0929f 100644 --- a/electrum/plugins/trezor/qt.py +++ b/electrum/plugins/trezor/qt.py @@ -826,7 +826,7 @@ class WCTrezorXPub(WizardComponent, Logger): def get_xpub_task(client, derivation, xtype): try: - self.xpub = client.get_xpub(derivation, xtype) + 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() @@ -914,6 +914,9 @@ class WCTrezorInit(WizardComponent, Logger): Logger.__init__(self) self.plugins = wizard.plugins self.plugin = self.plugins.get_plugin('trezor') + + self.layout().addWidget(WWLabel('Done')) + self._busy = True def on_ready(self): @@ -937,7 +940,6 @@ class WCTrezorInit(WizardComponent, Logger): def init_done(self): self.logger.info('Done initialize device') self.busy = False - self.layout().addWidget(WWLabel('Done')) def apply(self): pass