From 7080a7d8e2c5bfac00e716bf3362d118ed1a12a2 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Mon, 28 Aug 2023 13:48:39 +0200 Subject: [PATCH] qt: new wizard 2fa wallet online continuation from offline initial setup --- electrum/gui/qt/__init__.py | 22 +++++++++++++++++---- electrum/gui/qt/wizard/wallet.py | 4 ++-- electrum/gui/qt/wizard/wizard.py | 19 ++++++++++++------ electrum/plugins/trustedcoin/trustedcoin.py | 13 ++++++------ 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py index 6b7d65c00..e8e409af5 100644 --- a/electrum/gui/qt/__init__.py +++ b/electrum/gui/qt/__init__.py @@ -33,6 +33,7 @@ from typing import Optional, TYPE_CHECKING, List, Sequence from electrum import GuiImportError, WalletStorage from .wizard.server_connect import QEServerConnectWizard from .wizard.wallet import QENewWalletWizard +from electrum.wizard import WizardViewState try: import PyQt5 @@ -435,10 +436,23 @@ class ElectrumGui(BaseElectrumGui, Logger): wizard.run_upgrades(db) except UserCancelled: return - # TODO - # return if wallet creation is not complete - # if storage is None or db.get_action(): - # return + + if action := db.get_action(): + # wallet creation is not complete, 2fa online phase + assert action[1] == 'accept_terms_of_use', 'only support for resuming trustedcoin split setup' + data = { + 'xprv1': db.get('x1/')['xprv'], + 'xpub1': db.get('x1/')['xpub'], + 'xpub2': db.get('x2/')['xpub'], + } + wizard = QENewWalletWizard(self.config, self.app, self.plugins, self.daemon, path, + start_viewstate=WizardViewState('trustedcoin_tos_email', data, {})) + result = wizard.exec() + if result == QENewWalletWizard.Rejected: + self.logger.info('ok bye bye') + return + db.put('x3/', wizard.get_wizard_data()['x3/']) + db.write() wallet = Wallet(db, config=self.config) wallet.start_network(self.daemon.network) diff --git a/electrum/gui/qt/wizard/wallet.py b/electrum/gui/qt/wizard/wallet.py index 063139421..27f74f6e5 100644 --- a/electrum/gui/qt/wizard/wallet.py +++ b/electrum/gui/qt/wizard/wallet.py @@ -50,9 +50,9 @@ MSG_HW_STORAGE_ENCRYPTION = _("Set wallet file encryption.") + '\n'\ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard, MessageBoxMixin): _logger = get_logger(__name__) - def __init__(self, config: 'SimpleConfig', app: 'QElectrumApplication', plugins: 'Plugins', daemon: Daemon, path, parent=None): + def __init__(self, config: 'SimpleConfig', app: 'QElectrumApplication', plugins: 'Plugins', daemon: Daemon, path, *, start_viewstate=None): NewWalletWizard.__init__(self, daemon, plugins) - QEAbstractWizard.__init__(self, config, app) + QEAbstractWizard.__init__(self, config, app, start_viewstate=start_viewstate) self.setWindowTitle(_('Create/Restore wallet')) diff --git a/electrum/gui/qt/wizard/wizard.py b/electrum/gui/qt/wizard/wizard.py index 7a5bbf703..1863729eb 100644 --- a/electrum/gui/qt/wizard/wizard.py +++ b/electrum/gui/qt/wizard/wizard.py @@ -1,3 +1,4 @@ +import copy import threading from abc import abstractmethod from typing import TYPE_CHECKING @@ -14,12 +15,13 @@ from electrum.gui.qt.util import Buttons, icon_path, MessageBoxMixin if TYPE_CHECKING: from electrum.simple_config import SimpleConfig from electrum.gui.qt import QElectrumApplication + from electrum.wizard import WizardViewState class QEAbstractWizard(QDialog, MessageBoxMixin): _logger = get_logger(__name__) - def __init__(self, config: 'SimpleConfig', app: 'QElectrumApplication'): + def __init__(self, config: 'SimpleConfig', app: 'QElectrumApplication', *, start_viewstate: 'WizardViewState' = None): QDialog.__init__(self, None) self.app = app self.config = config @@ -92,6 +94,8 @@ class QEAbstractWizard(QDialog, MessageBoxMixin): self.icon_filename = None self.set_icon('electrum.png') + self.start_viewstate = start_viewstate + self.show() self.raise_() @@ -109,8 +113,11 @@ class QEAbstractWizard(QDialog, MessageBoxMixin): return QSize(800, 600) def strt(self): - view = self.start_wizard() - self.load_next_component(view) + if self.start_viewstate is not None: + viewstate = self._current = self.start_viewstate + else: + viewstate = self.start_wizard() + self.load_next_component(viewstate.view, viewstate.wizard_data) def load_next_component(self, view, wdata=None, params=None): if wdata is None: @@ -124,7 +131,7 @@ class QEAbstractWizard(QDialog, MessageBoxMixin): except Exception as e: self._logger.error(f'not a class: {comp!r}') raise e - page.wizard_data = wdata + page.wizard_data = copy.deepcopy(wdata) page.params = params page.updated.connect(self.on_page_updated) self._logger.debug(f'{page!r}') @@ -187,9 +194,9 @@ class QEAbstractWizard(QDialog, MessageBoxMixin): next = self.submit(wd) self.load_next_component(next.view, next.wizard_data, next.params) - def start_wizard(self) -> str: + def start_wizard(self) -> 'WizardViewState': self.start() - return self._current.view + return self._current def view_to_component(self, view) -> QWidget: return self.navmap[view]['gui'] diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index c609bde84..883cd545c 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -821,7 +821,7 @@ class TrustedCoinPlugin(BasePlugin): 'trustedcoin_show_confirm_otp': { 'accept': self.on_accept_otp_secret, 'next': 'wallet_password', - 'last': lambda d: wizard.is_single_password() + 'last': lambda d: wizard.is_single_password() or 'xprv1' in d } } wizard.navmap_merge(views) @@ -832,12 +832,11 @@ class TrustedCoinPlugin(BasePlugin): # wizard = self._wizard # wizard_data = wizard._current.wizard_data - xprv1, xpub1, xprv2, xpub2 = self.xkeys_from_seed(wizard_data['seed'], wizard_data['seed_extra_words']) - - # NOTE: at this point, old style wizard creates a wallet file (w. password if set) and - # stores the keystores and wizard state, in order to separate offline seed creation - # and online retrieval of the OTP secret. For mobile, we don't do this, but - # for desktop the wizard should support this usecase. + if 'seed' not in wizard_data: + # online continuation + xprv1, xpub1, xprv2, xpub2 = (wizard_data['xprv1'], wizard_data['xpub1'], None, wizard_data['xpub2']) + else: + xprv1, xpub1, xprv2, xpub2 = self.xkeys_from_seed(wizard_data['seed'], wizard_data['seed_extra_words']) data = {'x1/': {'xpub': xpub1}, 'x2/': {'xpub': xpub2}}