qt: add bitbox02 to new wizard
This commit is contained in:
@@ -24,6 +24,8 @@ import electrum.ecc as ecc
|
|||||||
|
|
||||||
from ..hw_wallet import HW_PluginBase, HardwareClientBase, HardwareHandlerBase
|
from ..hw_wallet import HW_PluginBase, HardwareClientBase, HardwareHandlerBase
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from electrum.wizard import NewWalletWizard
|
||||||
|
|
||||||
_logger = get_logger(__name__)
|
_logger = get_logger(__name__)
|
||||||
|
|
||||||
@@ -720,3 +722,29 @@ class BitBox02Plugin(HW_PluginBase):
|
|||||||
# distinguish devices.
|
# distinguish devices.
|
||||||
id_ = str(d['path'])
|
id_ = str(d['path'])
|
||||||
return device._replace(id_=id_)
|
return device._replace(id_=id_)
|
||||||
|
|
||||||
|
# new wizard
|
||||||
|
|
||||||
|
def wizard_entry_for_device(self, device_info: 'DeviceInfo', *, new_wallet=True) -> str:
|
||||||
|
if new_wallet:
|
||||||
|
return 'bitbox_start' if device_info.initialized else 'bitbox_not_initialized'
|
||||||
|
else:
|
||||||
|
return 'bitbox_unlock'
|
||||||
|
|
||||||
|
# insert trezor pages in new wallet wizard
|
||||||
|
def extend_wizard(self, wizard: 'NewWalletWizard'):
|
||||||
|
views = {
|
||||||
|
'bitbox_start': {
|
||||||
|
'next': 'bitbox_xpub',
|
||||||
|
},
|
||||||
|
'bitbox_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)
|
||||||
|
},
|
||||||
|
'bitbox_not_initialized': {},
|
||||||
|
'bitbox_unlock': {
|
||||||
|
'last': True
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wizard.navmap_merge(views)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import threading
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QPushButton,
|
QPushButton,
|
||||||
@@ -13,7 +15,7 @@ from PyQt5.QtCore import Qt, QMetaObject, Q_RETURN_ARG, pyqtSlot
|
|||||||
from electrum.gui.qt.util import (
|
from electrum.gui.qt.util import (
|
||||||
WindowModalDialog,
|
WindowModalDialog,
|
||||||
OkButton,
|
OkButton,
|
||||||
ButtonsTextEdit,
|
ButtonsTextEdit, WWLabel,
|
||||||
)
|
)
|
||||||
|
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
@@ -22,6 +24,12 @@ from electrum.plugin import hook
|
|||||||
from .bitbox02 import BitBox02Plugin
|
from .bitbox02 import BitBox02Plugin
|
||||||
from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
|
from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
|
||||||
from ..hw_wallet.plugin import only_hook_if_libraries_available
|
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
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from electrum.gui.qt.wizard.wallet import QENewWalletWizard
|
||||||
|
|
||||||
|
|
||||||
class Plugin(BitBox02Plugin, QtPluginBase):
|
class Plugin(BitBox02Plugin, QtPluginBase):
|
||||||
@@ -64,6 +72,21 @@ class Plugin(BitBox02Plugin, QtPluginBase):
|
|||||||
device_name = "{} ({})".format(self.device, keystore.label)
|
device_name = "{} ({})".format(self.device, keystore.label)
|
||||||
mpk_text.addButton("eye1.png", on_button_click, _("Show on {}").format(device_name))
|
mpk_text.addButton("eye1.png", on_button_click, _("Show on {}").format(device_name))
|
||||||
|
|
||||||
|
@hook
|
||||||
|
def init_wallet_wizard(self, wizard: 'QENewWalletWizard'):
|
||||||
|
self.extend_wizard(wizard)
|
||||||
|
|
||||||
|
# insert trezor pages in new wallet wizard
|
||||||
|
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_unlock': {'gui': WCHWUnlock}
|
||||||
|
}
|
||||||
|
wizard.navmap_merge(views)
|
||||||
|
|
||||||
|
|
||||||
class BitBox02_Handler(QtHandlerBase):
|
class BitBox02_Handler(QtHandlerBase):
|
||||||
MESSAGE_DIALOG_TITLE = _("BitBox02 Status")
|
MESSAGE_DIALOG_TITLE = _("BitBox02 Status")
|
||||||
@@ -105,3 +128,83 @@ class BitBox02_Handler(QtHandlerBase):
|
|||||||
dialog.setLayout(vbox)
|
dialog.setLayout(vbox)
|
||||||
dialog.exec_()
|
dialog.exec_()
|
||||||
return name.text().strip()
|
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')))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user