qt: add HWW unlock wizardcomponent
This commit is contained in:
@@ -30,7 +30,9 @@ import traceback
|
||||
import threading
|
||||
from typing import Optional, TYPE_CHECKING, List, Sequence
|
||||
|
||||
from electrum import GuiImportError
|
||||
from electrum import GuiImportError, WalletStorage
|
||||
from .wizard.server_connect import QEServerConnectWizard
|
||||
from .wizard.wallet import QENewWalletWizard
|
||||
|
||||
try:
|
||||
import PyQt5
|
||||
@@ -401,6 +403,48 @@ class ElectrumGui(BaseElectrumGui, Logger):
|
||||
return window
|
||||
|
||||
def _start_wizard_to_select_or_create_wallet(self, path) -> Optional[Abstract_Wallet]:
|
||||
dialog = QENewWalletWizard(self.config, self.app, self.plugins, self.daemon, path)
|
||||
result = dialog.exec()
|
||||
# TODO: use dialog.open() instead to avoid new event loop spawn?
|
||||
self.logger.info(f'{result}')
|
||||
if result == QENewWalletWizard.Rejected:
|
||||
self.logger.info('ok bye bye')
|
||||
return
|
||||
|
||||
d = dialog.get_wizard_data()
|
||||
|
||||
if d['wallet_is_open']:
|
||||
for window in self.windows:
|
||||
if window.wallet.storage.path == d['wallet_name']:
|
||||
return window.wallet
|
||||
raise Exception('found by wizard but not here?!')
|
||||
|
||||
if not d['wallet_exists']:
|
||||
self.logger.info('about to create wallet')
|
||||
dialog.create_storage()
|
||||
wallet_file = dialog.path
|
||||
else:
|
||||
wallet_file = d['wallet_name']
|
||||
|
||||
storage = WalletStorage(wallet_file)
|
||||
if storage.is_encrypted_with_user_pw() or storage.is_encrypted_with_hw_device():
|
||||
storage.decrypt(d['password'])
|
||||
|
||||
db = WalletDB(storage.read(), manual_upgrades=False)
|
||||
# TODO
|
||||
# wizard.run_upgrades(storage, db)
|
||||
# TODO
|
||||
# return if wallet creation is not complete
|
||||
# if storage is None or db.get_action():
|
||||
# return
|
||||
|
||||
wallet = Wallet(db, storage, config=self.config)
|
||||
wallet.start_network(self.daemon.network)
|
||||
self.daemon.add_wallet(wallet)
|
||||
return wallet
|
||||
|
||||
|
||||
def _start_wizard_to_select_or_create_wallet2(self, path) -> Optional[Abstract_Wallet]:
|
||||
wizard = InstallWizard(self.config, self.app, self.plugins, gui_object=self)
|
||||
try:
|
||||
path, storage = wizard.select_storage(path, self.daemon.get_wallet)
|
||||
@@ -441,9 +485,8 @@ class ElectrumGui(BaseElectrumGui, Logger):
|
||||
if self.daemon.network:
|
||||
# first-start network-setup
|
||||
if not self.config.cv.NETWORK_AUTO_CONNECT.is_set():
|
||||
wizard = InstallWizard(self.config, self.app, self.plugins, gui_object=self)
|
||||
wizard.init_network(self.daemon.network)
|
||||
wizard.terminate()
|
||||
dialog = QEServerConnectWizard(self.config, self.app, self.plugins, self.daemon)
|
||||
dialog.exec()
|
||||
# start network
|
||||
self.daemon.start_network()
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from electrum.i18n import _
|
||||
from .wizard import QEAbstractWizard, WizardComponent
|
||||
from electrum.wizard import ServerConnectWizard
|
||||
from electrum.gui.qt.network_dialog import ProxyWidget, ServerWidget
|
||||
from electrum.gui.qt.util import ChoiceWidget
|
||||
from .wizard import QEAbstractWizard, WizardComponent
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from electrum.simple_config import SimpleConfig
|
||||
@@ -21,7 +21,7 @@ class QEServerConnectWizard(ServerConnectWizard, QEAbstractWizard):
|
||||
|
||||
self.setWindowTitle(_('Network and server configuration'))
|
||||
|
||||
# attach view names
|
||||
# attach gui classes
|
||||
self.navmap_merge({
|
||||
'autoconnect': { 'gui': WCAutoConnect },
|
||||
'proxy_ask': { 'gui': WCProxyAsk },
|
||||
|
||||
@@ -86,8 +86,9 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
|
||||
'last': lambda d: d['wallet_exists'] and not d['wallet_needs_hw_unlock']
|
||||
},
|
||||
'hw_unlock': {
|
||||
'last': True,
|
||||
'gui': WCChooseHWDevice
|
||||
# 'last': True,
|
||||
'gui': WCChooseHWDevice,
|
||||
'next': lambda d: self.on_hardware_device(d, new_wallet=False)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -156,17 +157,11 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
|
||||
|
||||
path = os.path.join(os.path.dirname(self._daemon.config.get_wallet_path()), data['wallet_name'])
|
||||
|
||||
try:
|
||||
super().create_storage(path, data)
|
||||
super().create_storage(path, data)
|
||||
|
||||
# minimally populate self after create
|
||||
self._password = data['password']
|
||||
self.path = path
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
self._logger.error(f"createStorage errored: {e!r}")
|
||||
return False
|
||||
# minimally populate self after create
|
||||
self._password = data['password']
|
||||
self.path = path
|
||||
|
||||
|
||||
class WCWalletName(WizardComponent):
|
||||
@@ -262,10 +257,10 @@ class WCWalletName(WizardComponent):
|
||||
+ _("Press 'Next' to choose device to decrypt.")
|
||||
self.wallet_needs_hw_unlock = True
|
||||
else:
|
||||
msg = _("Press 'Next' to open this wallet.")
|
||||
msg = _("Press 'Finish' to open this wallet.")
|
||||
else:
|
||||
msg = _("This file is already open in memory.") + "\n" \
|
||||
+ _("Press 'Next' to create/focus window.")
|
||||
+ _("Press 'Finish' to create/focus window.")
|
||||
if msg is None:
|
||||
msg = _('Cannot read file')
|
||||
msg_label.setText(msg)
|
||||
@@ -294,7 +289,7 @@ class WCWalletName(WizardComponent):
|
||||
self.wizard_data['wallet_name'] = self.name_e.text()
|
||||
self.wizard_data['wallet_exists'] = self.wallet_exists
|
||||
self.wizard_data['wallet_is_open'] = self.wallet_is_open
|
||||
self.wizard_data['wallet_open_password'] = self.pw_e.text()
|
||||
self.wizard_data['password'] = self.pw_e.text()
|
||||
self.wizard_data['wallet_needs_hw_unlock'] = self.wallet_needs_hw_unlock
|
||||
|
||||
|
||||
@@ -1135,3 +1130,53 @@ class WCWalletPasswordHardware(WizardComponent):
|
||||
# client.handler = self.plugin.create_handler(self.wizard)
|
||||
self.wizard_data['password'] = client.get_password_for_storage_encryption()
|
||||
|
||||
|
||||
class WCHWUnlock(WizardComponent, Logger):
|
||||
def __init__(self, parent, wizard):
|
||||
WizardComponent.__init__(self, parent, wizard, title=_('unlock'))
|
||||
Logger.__init__(self)
|
||||
self.plugins = wizard.plugins
|
||||
self.plugin = None
|
||||
self._busy = True
|
||||
self.password = None
|
||||
|
||||
self.ok_l = WWLabel(_('Hardware successfully unlocked'))
|
||||
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 = _('Unlocking {} ({})').format(_info.model_name, _info.label)
|
||||
|
||||
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)
|
||||
|
||||
def unlock_task(client):
|
||||
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.logger.error(repr(e))
|
||||
self.unlock_done()
|
||||
|
||||
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()
|
||||
self.valid = True
|
||||
else:
|
||||
self.valid = False
|
||||
|
||||
def apply(self):
|
||||
if self.valid:
|
||||
self.wizard_data['password'] = self.password
|
||||
|
||||
@@ -13,15 +13,12 @@ from electrum.gui.qt.util import Buttons, icon_path, MessageBoxMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from electrum.simple_config import SimpleConfig
|
||||
from electrum.plugin import Plugins
|
||||
from electrum.daemon import Daemon
|
||||
from electrum.gui.qt import QElectrumApplication
|
||||
|
||||
|
||||
class QEAbstractWizard(QDialog, MessageBoxMixin):
|
||||
_logger = get_logger(__name__)
|
||||
|
||||
# def __init__(self, config: 'SimpleConfig', app: QApplication, plugins: 'Plugins', *, gui_object: 'ElectrumGui'):
|
||||
def __init__(self, config: 'SimpleConfig', app: 'QElectrumApplication'):
|
||||
QDialog.__init__(self, None)
|
||||
self.app = app
|
||||
@@ -51,6 +48,7 @@ class QEAbstractWizard(QDialog, MessageBoxMixin):
|
||||
please_wait_layout.addWidget(self.please_wait_l)
|
||||
please_wait_layout.addStretch(1)
|
||||
self.please_wait = QWidget()
|
||||
self.please_wait.setVisible(False)
|
||||
self.please_wait.setLayout(please_wait_layout)
|
||||
|
||||
error_layout = QVBoxLayout()
|
||||
@@ -63,6 +61,7 @@ class QEAbstractWizard(QDialog, MessageBoxMixin):
|
||||
error_layout.addWidget(self.error_msg)
|
||||
error_layout.addStretch(1)
|
||||
self.error = QWidget()
|
||||
self.error.setVisible(False)
|
||||
self.error.setLayout(error_layout)
|
||||
|
||||
outer_vbox = QVBoxLayout(self)
|
||||
|
||||
@@ -482,8 +482,11 @@ class JadePlugin(HW_PluginBase):
|
||||
|
||||
# new wizard
|
||||
|
||||
def wizard_entry_for_device(self, device_info: 'DeviceInfo') -> str:
|
||||
return 'jade_start' if device_info.initialized else 'jade_not_initialized'
|
||||
def wizard_entry_for_device(self, device_info: 'DeviceInfo', *, new_wallet=True) -> str:
|
||||
if new_wallet:
|
||||
return 'jade_start' if device_info.initialized else 'jade_not_initialized'
|
||||
else:
|
||||
return 'jade_unlock'
|
||||
|
||||
# insert trezor pages in new wallet wizard
|
||||
def extend_wizard(self, wizard: 'NewWalletWizard'):
|
||||
@@ -496,6 +499,9 @@ class JadePlugin(HW_PluginBase):
|
||||
'accept': wizard.maybe_master_pubkey,
|
||||
'last': lambda d: wizard.is_single_password() and wizard.last_cosigner(d)
|
||||
},
|
||||
'jade_not_initialized': {}
|
||||
'jade_not_initialized': {},
|
||||
'jade_unlock': {
|
||||
'last': True
|
||||
},
|
||||
}
|
||||
wizard.navmap_merge(views)
|
||||
|
||||
@@ -12,7 +12,7 @@ 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.wallet import WCScriptAndDerivation, WCHWUnlock
|
||||
from electrum.gui.qt.wizard.wizard import WizardComponent
|
||||
|
||||
from .jade import JadePlugin
|
||||
@@ -50,6 +50,7 @@ class Plugin(JadePlugin, QtPluginBase):
|
||||
'jade_start': { 'gui': WCScriptAndDerivation },
|
||||
'jade_xpub': { 'gui': WCJadeXPub },
|
||||
'jade_not_initialized': {'gui': WCJadeNope},
|
||||
'jade_unlock': {'gui': WCHWUnlock}
|
||||
}
|
||||
wizard.navmap_merge(views)
|
||||
|
||||
@@ -64,7 +65,9 @@ class Jade_Handler(QtHandlerBase):
|
||||
super(Jade_Handler, self).__init__(win, 'Jade')
|
||||
|
||||
|
||||
class WCJadeXPub(WizardComponent, Logger): # TODO: almost verbatim copy of trezor WCTrezorXPub, generalize!
|
||||
# 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)
|
||||
@@ -139,3 +142,4 @@ 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')))
|
||||
|
||||
|
||||
@@ -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
|
||||
from electrum.gui.qt.wizard.wallet import WCScriptAndDerivation, WCHWUnlock
|
||||
from electrum.gui.qt.wizard.wizard import WizardComponent
|
||||
|
||||
from .trezor import (TrezorPlugin, TIM_NEW, TIM_RECOVER, TrezorInitSettings,
|
||||
@@ -478,6 +478,7 @@ class Plugin(TrezorPlugin, QtPlugin):
|
||||
'trezor_not_initialized': { 'gui': WCTrezorInitMethod },
|
||||
'trezor_choose_new_recover': { 'gui': WCTrezorInitParams },
|
||||
'trezor_do_init': { 'gui': WCTrezorInit },
|
||||
'trezor_unlock': {'gui': WCHWUnlock},
|
||||
}
|
||||
wizard.navmap_merge(views)
|
||||
|
||||
|
||||
@@ -529,8 +529,11 @@ class TrezorPlugin(HW_PluginBase):
|
||||
|
||||
# new wizard
|
||||
|
||||
def wizard_entry_for_device(self, device_info: 'DeviceInfo') -> str:
|
||||
return 'trezor_not_initialized' if not device_info.initialized else 'trezor_start'
|
||||
def wizard_entry_for_device(self, device_info: 'DeviceInfo', *, new_wallet=True) -> str:
|
||||
if new_wallet: # new wallet
|
||||
return 'trezor_not_initialized' if not device_info.initialized else 'trezor_start'
|
||||
else: # unlock existing wallet
|
||||
return 'trezor_unlock'
|
||||
|
||||
# insert trezor pages in new wallet wizard
|
||||
def extend_wizard(self, wizard: 'NewWalletWizard'):
|
||||
@@ -552,5 +555,8 @@ class TrezorPlugin(HW_PluginBase):
|
||||
'trezor_do_init': {
|
||||
'next': 'trezor_start',
|
||||
},
|
||||
'trezor_unlock': {
|
||||
'last': True
|
||||
},
|
||||
}
|
||||
wizard.navmap_merge(views)
|
||||
|
||||
@@ -174,6 +174,7 @@ class AbstractWizard:
|
||||
|
||||
def sanitize_stack_item(self, _stack_item) -> dict:
|
||||
sensitive_keys = ['seed', 'seed_extra_words', 'master_key', 'private_key_list', 'password']
|
||||
|
||||
def sanitize(_dict):
|
||||
result = {}
|
||||
for item in _dict:
|
||||
@@ -322,11 +323,11 @@ class NewWalletWizard(AbstractWizard):
|
||||
def wallet_password_view(self, wizard_data: dict) -> str:
|
||||
return 'wallet_password_hardware' if self.is_hardware(wizard_data) else 'wallet_password'
|
||||
|
||||
def on_hardware_device(self, wizard_data: dict) -> str:
|
||||
def on_hardware_device(self, wizard_data: dict, new_wallet=True) -> str:
|
||||
_type, _info = wizard_data['hardware_device']
|
||||
run_hook('init_wallet_wizard', self)
|
||||
run_hook('init_wallet_wizard', self) # TODO: currently only used for hww, hook name might be confusing
|
||||
plugin = self.plugins.get_plugin(_type)
|
||||
return plugin.wizard_entry_for_device(_info)
|
||||
return plugin.wizard_entry_for_device(_info, new_wallet=new_wallet)
|
||||
|
||||
def on_have_or_confirm_seed(self, wizard_data: dict) -> str:
|
||||
if self.needs_derivation_path(wizard_data):
|
||||
|
||||
Reference in New Issue
Block a user