From bb5b1b3932f3a1b252b6fb81af065fafa7613f5e Mon Sep 17 00:00:00 2001 From: f321x Date: Tue, 8 Jul 2025 09:52:17 +0200 Subject: [PATCH 1/4] wizard: move pasphrase flow from Qt into Abstract Wizard moves the separate passphrase flow logic from Qt into the Abstract Wizard base class so the same flow can be shared between Qt and QML --- electrum/gui/qt/wizard/wallet.py | 45 +++----------------------------- electrum/wizard.py | 21 ++++++++++++++- 2 files changed, 24 insertions(+), 42 deletions(-) diff --git a/electrum/gui/qt/wizard/wallet.py b/electrum/gui/qt/wizard/wallet.py index e8e6bf0d5..6ba6aa0d4 100644 --- a/electrum/gui/qt/wizard/wallet.py +++ b/electrum/gui/qt/wizard/wallet.py @@ -94,8 +94,11 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard, MessageBoxMixin): 'wallet_type': {'gui': WCWalletType}, 'keystore_type': {'gui': WCKeystoreType}, 'create_seed': {'gui': WCCreateSeed}, + 'create_ext': {'gui': WCEnterExt}, 'confirm_seed': {'gui': WCConfirmSeed}, + 'confirm_ext': {'gui': WCConfirmExt}, 'have_seed': {'gui': WCHaveSeed}, + 'have_ext': {'gui': WCEnterExt}, 'choose_hardware_device': {'gui': WCChooseHWDevice}, 'script_and_derivation': {'gui': WCScriptAndDerivation}, 'have_master_key': {'gui': WCHaveMasterKey}, @@ -103,6 +106,7 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard, MessageBoxMixin): 'multisig_cosigner_keystore': {'gui': WCCosignerKeystore}, 'multisig_cosigner_key': {'gui': WCHaveMasterKey}, 'multisig_cosigner_seed': {'gui': WCHaveSeed}, + 'multisig_cosigner_have_ext': {'gui': WCEnterExt}, 'multisig_cosigner_hardware': {'gui': WCChooseHWDevice}, 'multisig_cosigner_script_and_derivation': {'gui': WCScriptAndDerivation}, 'imported': {'gui': WCImport}, @@ -122,47 +126,6 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard, MessageBoxMixin): } }) - # insert seed extension entry/confirm as separate views - self.navmap_merge({ - 'create_seed': { - 'next': lambda d: 'create_ext' if self.wants_ext(d) else 'confirm_seed' - }, - 'create_ext': { - 'next': 'confirm_seed', - 'gui': WCEnterExt - }, - 'confirm_seed': { - 'next': lambda d: 'confirm_ext' if self.wants_ext(d) else self.on_have_or_confirm_seed(d), - 'accept': lambda d: None if self.wants_ext(d) else self.maybe_master_pubkey(d) - }, - 'confirm_ext': { - 'next': self.on_have_or_confirm_seed, - 'accept': self.maybe_master_pubkey, - 'gui': WCConfirmExt - }, - 'have_seed': { - 'next': lambda d: 'have_ext' if self.wants_ext(d) else self.on_have_or_confirm_seed(d), - 'accept': lambda d: None if self.wants_ext(d) else self.maybe_master_pubkey(d), - 'last': lambda d: self.is_single_password() and not - (self.needs_derivation_path(d) or self.is_multisig(d) or self.wants_ext(d)) - }, - 'have_ext': { - 'next': self.on_have_or_confirm_seed, - 'accept': self.maybe_master_pubkey, - 'gui': WCEnterExt - }, - 'multisig_cosigner_seed': { - 'next': lambda d: 'multisig_cosigner_have_ext' if self.wants_ext(d) else self.on_have_cosigner_seed(d), - 'last': lambda d: self.is_single_password() and self.last_cosigner(d) and not - (self.needs_derivation_path(d) or self.wants_ext(d)) - }, - 'multisig_cosigner_have_ext': { - 'next': self.on_have_cosigner_seed, - 'last': lambda d: self.is_single_password() and self.last_cosigner(d) and not self.needs_derivation_path(d), - 'gui': WCEnterExt - }, - }) - run_hook('init_wallet_wizard', self) @property diff --git a/electrum/wizard.py b/electrum/wizard.py index de79c12f3..0eb094a59 100644 --- a/electrum/wizard.py +++ b/electrum/wizard.py @@ -391,14 +391,28 @@ class NewWalletWizard(KeystoreWizard): 'next': self.on_keystore_type }, 'create_seed': { - 'next': 'confirm_seed' + 'next': lambda d: 'create_ext' if self.wants_ext(d) else 'confirm_seed', + }, + 'create_ext': { + 'next': 'confirm_seed', }, 'confirm_seed': { + 'next': lambda d: 'confirm_ext' if self.wants_ext(d) else self.on_have_or_confirm_seed(d), + 'accept': lambda d: None if self.wants_ext(d) else self.maybe_master_pubkey(d), + 'last': lambda d: self.is_single_password() and not self.is_multisig(d) and not self.wants_ext(d), + }, + 'confirm_ext': { 'next': self.on_have_or_confirm_seed, 'accept': self.maybe_master_pubkey, 'last': lambda d: self.is_single_password() and not self.is_multisig(d) }, 'have_seed': { + 'next': lambda d: 'have_ext' if self.wants_ext(d) else self.on_have_or_confirm_seed(d), + 'accept': lambda d: None if self.wants_ext(d) else self.maybe_master_pubkey(d), + 'last': lambda d: self.is_single_password() and not + (self.needs_derivation_path(d) or self.is_multisig(d) or self.wants_ext(d)), + }, + 'have_ext': { 'next': self.on_have_or_confirm_seed, 'accept': self.maybe_master_pubkey, 'last': lambda d: self.is_single_password() and not @@ -428,6 +442,11 @@ class NewWalletWizard(KeystoreWizard): 'last': lambda d: self.is_single_password() and self.last_cosigner(d) }, 'multisig_cosigner_seed': { + 'next': lambda d: 'multisig_cosigner_have_ext' if self.wants_ext(d) else self.on_have_cosigner_seed(d), + 'last': lambda d: self.is_single_password() and self.last_cosigner(d) and not + (self.needs_derivation_path(d) or self.wants_ext(d)), + }, + 'multisig_cosigner_have_ext': { 'next': self.on_have_cosigner_seed, 'last': lambda d: self.is_single_password() and self.last_cosigner(d) and not self.needs_derivation_path(d) }, From 290da21187d42f2f4e8aca77058c9e67c85dee10 Mon Sep 17 00:00:00 2001 From: f321x Date: Tue, 8 Jul 2025 14:12:56 +0200 Subject: [PATCH 2/4] qml: change wizard passphrase flow --- .../qml/components/wizard/WCConfirmExt.qml | 72 +++++++++++ .../qml/components/wizard/WCConfirmSeed.qml | 16 +-- .../qml/components/wizard/WCCreateSeed.qml | 19 +-- .../gui/qml/components/wizard/WCEnterExt.qml | 119 ++++++++++++++++++ .../gui/qml/components/wizard/WCHaveSeed.qml | 41 +----- electrum/gui/qml/qewizard.py | 4 + electrum/wizard.py | 2 +- 7 files changed, 204 insertions(+), 69 deletions(-) create mode 100644 electrum/gui/qml/components/wizard/WCConfirmExt.qml create mode 100644 electrum/gui/qml/components/wizard/WCEnterExt.qml diff --git a/electrum/gui/qml/components/wizard/WCConfirmExt.qml b/electrum/gui/qml/components/wizard/WCConfirmExt.qml new file mode 100644 index 000000000..abcfc3130 --- /dev/null +++ b/electrum/gui/qml/components/wizard/WCConfirmExt.qml @@ -0,0 +1,72 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Controls.Material + +import org.electrum 1.0 + +import "../controls" + +WizardComponent { + id: root + securePage: true + + valid: false + + property int cosigner: 0 + + function checkValid() { + valid = false + var input = customwordstext.text + if (input == '') { + return + } + + if (cosigner) { + // multisig cosigner + if (input != wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_extra_words']) { + return + } + } else { + if (input != wizard_data['seed_extra_words']) { + return + } + } + valid = true + } + + Flickable { + anchors.fill: parent + contentHeight: mainLayout.height + clip: true + interactive: height < contentHeight + + ColumnLayout { + id: mainLayout + width: parent.width + spacing: constants.paddingLarge + + Label { + Layout.fillWidth: true + wrapMode: Text.Wrap + text: qsTr('Please enter your custom word(s) a second time:') + } + + TextField { + id: customwordstext + Layout.fillWidth: true + Layout.columnSpan: 2 + placeholderText: qsTr('Enter your custom word(s) here') + inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase + onTextChanged: checkValid() + } + } + } + + Component.onCompleted: { + if (wizard_data['wallet_type'] == 'multisig') { + if ('multisig_current_cosigner' in wizard_data) + cosigner = wizard_data['multisig_current_cosigner'] + } + } +} \ No newline at end of file diff --git a/electrum/gui/qml/components/wizard/WCConfirmSeed.qml b/electrum/gui/qml/components/wizard/WCConfirmSeed.qml index a5f27d8e0..a2b058e95 100644 --- a/electrum/gui/qml/components/wizard/WCConfirmSeed.qml +++ b/electrum/gui/qml/components/wizard/WCConfirmSeed.qml @@ -14,8 +14,7 @@ WizardComponent { function checkValid() { var seedvalid = wizard.wiz.isMatchingSeed(wizard_data['seed'], confirm.text) - var customwordsvalid = customwordstext.text == wizard_data['seed_extra_words'] - valid = seedvalid && (wizard_data['seed_extend'] ? customwordsvalid : true) + valid = seedvalid } Flickable { @@ -46,19 +45,6 @@ WizardComponent { placeholderText: qsTr('Enter your seed') onTextChanged: checkValid() } - - TextField { - id: customwordstext - Layout.fillWidth: true - placeholderText: qsTr('Enter your custom word(s)') - inputMethodHints: Qt.ImhNoPredictiveText - - onTextChanged: checkValid() - } } } - - Component.onCompleted: { - customwordstext.visible = wizard_data['seed_extend'] - } } diff --git a/electrum/gui/qml/components/wizard/WCCreateSeed.qml b/electrum/gui/qml/components/wizard/WCCreateSeed.qml index da087706a..efa4df90a 100644 --- a/electrum/gui/qml/components/wizard/WCCreateSeed.qml +++ b/electrum/gui/qml/components/wizard/WCCreateSeed.qml @@ -9,13 +9,12 @@ import "../controls" WizardComponent { securePage: true - valid: seedtext.text != '' && extendcb.checked ? customwordstext.text != '' : true + valid: seedtext.text != '' function apply() { wizard_data['seed'] = seedtext.text wizard_data['seed_variant'] = 'electrum' // generated seed always electrum variant - wizard_data['seed_extend'] = extendcb.checked - wizard_data['seed_extra_words'] = extendcb.checked ? customwordstext.text : '' + wizard_data['seed_extend'] = true // true so we get forwarded to the passphrase page } function setWarningText(numwords) { @@ -70,20 +69,6 @@ WizardComponent { } } - ElCheckBox { - id: extendcb - Layout.fillWidth: true - text: qsTr('Extend seed with custom words') - } - - TextField { - id: customwordstext - visible: extendcb.checked - Layout.fillWidth: true - placeholderText: qsTr('Enter your custom word(s)') - inputMethodHints: Qt.ImhNoPredictiveText - } - Component.onCompleted : { setWarningText(12) } diff --git a/electrum/gui/qml/components/wizard/WCEnterExt.qml b/electrum/gui/qml/components/wizard/WCEnterExt.qml new file mode 100644 index 000000000..b804f09ab --- /dev/null +++ b/electrum/gui/qml/components/wizard/WCEnterExt.qml @@ -0,0 +1,119 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Controls.Material + +import org.electrum 1.0 + +import "../controls" + +WizardComponent { + id: root + securePage: true + + valid: true + + property int cosigner: 0 + + function apply() { + var seed_extend = extendcb.checked + if (cosigner) { + wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_extend'] = seed_extend + wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_extra_words'] = seed_extend ? customwordstext.text : '' + } else { + wizard_data['seed_extend'] = seed_extend + wizard_data['seed_extra_words'] = seed_extend ? customwordstext.text : '' + } + } + + function checkValid() { + valid = false + validationtext.text = '' + + if (extendcb.checked && customwordstext.text == '') { + return + } else { + // passphrase is either disabled or filled with text + apply() + if (cosigner && wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_variant'] == 'electrum') { + // check if master keys are not duplicated after entering passphrase + if (wiz.hasDuplicateMasterKeys(wizard_data)) { + validationtext.text = qsTr('Error: duplicate master public key') + return + } + } + } + valid = true + } + + Flickable { + anchors.fill: parent + contentHeight: mainLayout.height + clip: true + interactive: height < contentHeight + + ColumnLayout { + id: mainLayout + width: parent.width + spacing: constants.paddingLarge + + InfoTextArea { + id: validationtext + Layout.fillWidth: true + Layout.columnSpan: 2 + visible: text + iconStyle: InfoTextArea.IconStyle.Error + } + + Label { + Layout.fillWidth: true + wrapMode: Text.Wrap + text: [ + qsTr('You may extend your seed with custom words.'), + qsTr('Your seed extension must be saved together with your seed.'), + qsTr('Note that this is NOT your encryption password.'), + '
', + qsTr('Do not enable it unless you know what it does!'), + ].join(' ') + } + + ElCheckBox { + id: extendcb + Layout.columnSpan: 2 + Layout.fillWidth: true + text: qsTr('Extend seed with custom words') + onCheckedChanged: checkValid() + } + + TextField { + id: customwordstext + enabled: extendcb.checked + Layout.fillWidth: true + Layout.columnSpan: 2 + placeholderText: qsTr('Enter your custom word(s)') + inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase + onTextChanged: startValidationTimer() + } + } + } + + function startValidationTimer() { + valid = false + validationTimer.restart() + } + + Timer { + id: validationTimer + interval: 250 + repeat: false + onTriggered: checkValid() + } + + Component.onCompleted: { + if (wizard_data['wallet_type'] == 'multisig') { + if ('multisig_current_cosigner' in wizard_data) + cosigner = wizard_data['multisig_current_cosigner'] + } + checkValid() + } +} diff --git a/electrum/gui/qml/components/wizard/WCHaveSeed.qml b/electrum/gui/qml/components/wizard/WCHaveSeed.qml index 4afc638a7..3be2a29d5 100644 --- a/electrum/gui/qml/components/wizard/WCHaveSeed.qml +++ b/electrum/gui/qml/components/wizard/WCHaveSeed.qml @@ -24,19 +24,16 @@ WizardComponent { property bool _seedValid function apply() { - var seed_extend = extendcb.checked && _canPassphrase if (cosigner) { wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed'] = seedtext.text wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_variant'] = seed_variant_cb.currentValue wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_type'] = _seedType - wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_extend'] = seed_extend - wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_extra_words'] = seed_extend ? customwordstext.text : '' + wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_extend'] = _canPassphrase } else { wizard_data['seed'] = seedtext.text wizard_data['seed_variant'] = seed_variant_cb.currentValue wizard_data['seed_type'] = _seedType - wizard_data['seed_extend'] = seed_extend - wizard_data['seed_extra_words'] = seed_extend ? customwordstext.text : '' + wizard_data['seed_extend'] = _canPassphrase // determine script type from electrum seed type // (used to limit script type options for bip39 cosigners) @@ -52,22 +49,20 @@ WizardComponent { function setSeedTypeHelpText() { var t = { 'electrum': [ + // not shown as electrum is the default seed type anyways and the name is self-explanatory qsTr('Electrum seeds are the default seed type.'), qsTr('If you are restoring from a seed previously created by Electrum, choose this option') ].join(' '), 'bip39': [ qsTr('BIP39 seeds can be imported in Electrum, so that users can access funds locked in other wallets.'), - '

', - qsTr('However, we do not generate BIP39 seeds, because they do not meet our safety standard.'), - qsTr('BIP39 seeds do not include a version number, which compromises compatibility with future software.') + qsTr('BIP39 seeds do not include a version number, which compromises compatibility with future software.'), ].join(' '), 'slip39': [ qsTr('SLIP39 seeds can be imported in Electrum, so that users can access funds locked in other wallets.'), - '

', - qsTr('However, we do not generate SLIP39 seeds.') ].join(' ') } infotext.text = t[seed_variant_cb.currentValue] + infotext.visible = !cosigner && !is2fa && seed_variant_cb.currentValue != 'electrum' } function checkValid() { @@ -100,11 +95,6 @@ WizardComponent { } } - if (_canPassphrase && extendcb.checked && customwordstext.text == '') { - valid = false - return - } - valid = _seedValid } @@ -196,7 +186,6 @@ WizardComponent { InfoTextArea { id: infotext - visible: !cosigner && !is2fa Layout.fillWidth: true Layout.columnSpan: 2 Layout.bottomMargin: constants.paddingLarge @@ -221,26 +210,6 @@ WizardComponent { startValidationTimer() } } - - ElCheckBox { - id: extendcb - Layout.columnSpan: 2 - Layout.fillWidth: true - visible: _canPassphrase - text: qsTr('Extend seed with custom words') - onCheckedChanged: startValidationTimer() - } - - TextField { - id: customwordstext - visible: extendcb.checked && extendcb.visible - Layout.fillWidth: true - Layout.columnSpan: 2 - placeholderText: qsTr('Enter your custom word(s)') - inputMethodHints: Qt.ImhNoPredictiveText - - onTextChanged: startValidationTimer() - } } } diff --git a/electrum/gui/qml/qewizard.py b/electrum/gui/qml/qewizard.py index 0a9d51fa1..cd45e9e57 100644 --- a/electrum/gui/qml/qewizard.py +++ b/electrum/gui/qml/qewizard.py @@ -68,14 +68,18 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard): 'wallet_type': {'gui': 'WCWalletType'}, 'keystore_type': {'gui': 'WCKeystoreType'}, 'create_seed': {'gui': 'WCCreateSeed'}, + 'create_ext': {'gui': 'WCEnterExt'}, 'confirm_seed': {'gui': 'WCConfirmSeed'}, + 'confirm_ext': {'gui': 'WCConfirmExt'}, 'have_seed': {'gui': 'WCHaveSeed'}, + 'have_ext': {'gui': 'WCEnterExt'}, 'script_and_derivation': {'gui': 'WCScriptAndDerivation'}, 'have_master_key': {'gui': 'WCHaveMasterKey'}, 'multisig': {'gui': 'WCMultisig'}, 'multisig_cosigner_keystore': {'gui': 'WCCosignerKeystore'}, 'multisig_cosigner_key': {'gui': 'WCHaveMasterKey'}, 'multisig_cosigner_seed': {'gui': 'WCHaveSeed'}, + 'multisig_cosigner_have_ext': {'gui': 'WCEnterExt'}, 'multisig_cosigner_script_and_derivation': {'gui': 'WCScriptAndDerivation'}, 'imported': {'gui': 'WCImport'}, 'wallet_password': {'gui': 'WCWalletPassword'} diff --git a/electrum/wizard.py b/electrum/wizard.py index 0eb094a59..9092e891f 100644 --- a/electrum/wizard.py +++ b/electrum/wizard.py @@ -333,7 +333,7 @@ class KeystoreWizard(AbstractWizard): def keystore_from_data(self, wallet_type: str, data: dict): if data['keystore_type'] in ['createseed', 'haveseed'] and 'seed' in data: - seed_extension = data['seed_extra_words'] if data['seed_extend'] else '' + seed_extension = data.get('seed_extra_words', '') if data['seed_variant'] == 'electrum': for_multisig = wallet_type in ['multisig'] return keystore.from_seed(data['seed'], passphrase=seed_extension, for_multisig=for_multisig) From 1bb8e2d46869dab926e4d2ce607871d9615000bb Mon Sep 17 00:00:00 2001 From: f321x Date: Tue, 8 Jul 2025 17:08:18 +0200 Subject: [PATCH 3/4] adapt trustedcoin to updated passphrase flow in wizard --- electrum/plugins/trustedcoin/qml.py | 9 +++++ electrum/plugins/trustedcoin/qt.py | 41 ++++++--------------- electrum/plugins/trustedcoin/trustedcoin.py | 15 ++++++-- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/electrum/plugins/trustedcoin/qml.py b/electrum/plugins/trustedcoin/qml.py index aa93a1d91..f2e66dab1 100644 --- a/electrum/plugins/trustedcoin/qml.py +++ b/electrum/plugins/trustedcoin/qml.py @@ -69,12 +69,21 @@ class Plugin(TrustedCoinPlugin): 'trustedcoin_create_seed': { 'gui': 'WCCreateSeed', }, + 'trustedcoin_create_ext': { + 'gui': 'WCEnterExt', + }, 'trustedcoin_confirm_seed': { 'gui': 'WCConfirmSeed', }, + 'trustedcoin_confirm_ext': { + 'gui': 'WCConfirmExt', + }, 'trustedcoin_have_seed': { 'gui': 'WCHaveSeed', }, + 'trustedcoin_have_ext': { + 'gui': 'WCEnterExt', + }, 'trustedcoin_keep_disable': { 'gui': '../../../../plugins/trustedcoin/qml/KeepDisable', }, diff --git a/electrum/plugins/trustedcoin/qt.py b/electrum/plugins/trustedcoin/qt.py index 8d32e04a5..93bca30b8 100644 --- a/electrum/plugins/trustedcoin/qt.py +++ b/electrum/plugins/trustedcoin/qt.py @@ -250,14 +250,26 @@ class Plugin(TrustedCoinPlugin): 'gui': WCCreateSeed, 'params': {'icon': self.icon_path('trustedcoin-wizard.png')}, }, + 'trustedcoin_create_ext': { + 'gui': WCEnterExt, + 'params': {'icon': self.icon_path('trustedcoin-wizard.png')}, + }, 'trustedcoin_confirm_seed': { 'gui': WCConfirmSeed, 'params': {'icon': self.icon_path('trustedcoin-wizard.png')}, }, + 'trustedcoin_confirm_ext': { + 'gui': WCConfirmExt, + 'params': {'icon': self.icon_path('trustedcoin-wizard.png')}, + }, 'trustedcoin_have_seed': { 'gui': WCHaveSeed, 'params': {'icon': self.icon_path('trustedcoin-wizard.png')}, }, + 'trustedcoin_have_ext': { + 'gui': WCEnterExt, + 'params': {'icon': self.icon_path('trustedcoin-wizard.png')}, + }, 'trustedcoin_keep_disable': { 'gui': WCKeepDisable, 'params': {'icon': self.icon_path('trustedcoin-wizard.png')}, @@ -277,35 +289,6 @@ class Plugin(TrustedCoinPlugin): } wizard.navmap_merge(views) - # modify default flow, insert seed extension entry/confirm as separate views - ext = { - 'trustedcoin_create_seed': { - 'next': lambda d: 'trustedcoin_create_ext' if wizard.wants_ext(d) else 'trustedcoin_confirm_seed' - }, - 'trustedcoin_create_ext': { - 'gui': WCEnterExt, - 'params': {'icon': self.icon_path('trustedcoin-wizard.png')}, - 'next': 'trustedcoin_confirm_seed', - }, - 'trustedcoin_confirm_seed': { - 'next': lambda d: 'trustedcoin_confirm_ext' if wizard.wants_ext(d) else 'trustedcoin_tos' - }, - 'trustedcoin_confirm_ext': { - 'gui': WCConfirmExt, - 'params': {'icon': self.icon_path('trustedcoin-wizard.png')}, - 'next': 'trustedcoin_tos', - }, - 'trustedcoin_have_seed': { - 'next': lambda d: 'trustedcoin_have_ext' if wizard.wants_ext(d) else 'trustedcoin_keep_disable' - }, - 'trustedcoin_have_ext': { - 'gui': WCEnterExt, - 'params': {'icon': self.icon_path('trustedcoin-wizard.png')}, - 'next': 'trustedcoin_keep_disable', - }, - } - wizard.navmap_merge(ext) - # insert page offering choice to go online or continue on another system ext_online = { 'trustedcoin_continue_online': { diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index 4b9d6b7df..db1605c20 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -586,13 +586,22 @@ class TrustedCoinPlugin(BasePlugin): else 'trustedcoin_have_seed' }, 'trustedcoin_create_seed': { - 'next': 'trustedcoin_confirm_seed' + 'next': lambda d: 'trustedcoin_create_ext' if wizard.wants_ext(d) else 'trustedcoin_confirm_seed', + }, + 'trustedcoin_create_ext': { + 'next': 'trustedcoin_confirm_seed', }, 'trustedcoin_confirm_seed': { - 'next': 'trustedcoin_tos' + 'next': lambda d: 'trustedcoin_confirm_ext' if wizard.wants_ext(d) else 'trustedcoin_tos', + }, + 'trustedcoin_confirm_ext': { + 'next': 'trustedcoin_tos', }, 'trustedcoin_have_seed': { - 'next': 'trustedcoin_keep_disable' + 'next': lambda d: 'trustedcoin_have_ext' if wizard.wants_ext(d) else 'trustedcoin_keep_disable', + }, + 'trustedcoin_have_ext': { + 'next': 'trustedcoin_keep_disable', }, 'trustedcoin_keep_disable': { 'next': lambda d: 'trustedcoin_tos' if d['trustedcoin_keepordisable'] != 'disable' From 307181fe5ee7d37d450c6f9f441956a38e0daa10 Mon Sep 17 00:00:00 2001 From: f321x Date: Wed, 9 Jul 2025 09:05:31 +0200 Subject: [PATCH 4/4] wizard: add unittests for passphrase flow --- tests/test_wizard.py | 67 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/tests/test_wizard.py b/tests/test_wizard.py index 350162502..850c10970 100644 --- a/tests/test_wizard.py +++ b/tests/test_wizard.py @@ -160,6 +160,41 @@ class WalletWizardTestCase(WizardTestCase): self.assertTrue(os.path.exists(wallet_path)) + async def test_create_standard_wallet_newseed_passphrase(self): + w = self.wizard_for(name='test_standard_wallet', wallet_type='standard') + v = w._current + d = v.wizard_data + self.assertEqual('keystore_type', v.view) + + d.update({'keystore_type': 'createseed'}) + v = w.resolve_next(v.view, d) + self.assertEqual('create_seed', v.view) + + d.update({'seed': '9dk', 'seed_type': 'segwit', 'seed_extend': True, + 'seed_variant': 'electrum', + 'seed_extra_words': False}) + + v = w.resolve_next(v.view, d) + self.assertEqual('create_ext', v.view) + + v = w.resolve_next(v.view, d) + self.assertEqual('confirm_seed', v.view) + + v = w.resolve_next(v.view, d) + self.assertEqual('confirm_ext', v.view) + + v = w.resolve_next(v.view, d) + self.assertEqual('wallet_password', v.view) + + d.update({'password': None, 'encrypt': False}) + self.assertTrue(w.is_last_view(v.view, d)) + v = w.resolve_next(v.view, d) + + wallet_path = os.path.join(w._daemon.config.get_datadir_wallet_path(), d['wallet_name']) + w.create_storage(wallet_path, d) + + self.assertTrue(os.path.exists(wallet_path)) + async def test_create_standard_wallet_haveseed_electrum(self): w = self.wizard_for(name='test_standard_wallet', wallet_type='standard') v = w._current @@ -184,6 +219,34 @@ class WalletWizardTestCase(WizardTestCase): self.assertTrue(os.path.exists(wallet_path)) + async def test_create_standard_wallet_haveseed_electrum_passphrase(self): + w = self.wizard_for(name='test_standard_wallet', wallet_type='standard') + v = w._current + d = v.wizard_data + self.assertEqual('keystore_type', v.view) + + d.update({'keystore_type': 'haveseed'}) + v = w.resolve_next(v.view, d) + self.assertEqual('have_seed', v.view) + + d.update({'seed': '9dk', 'seed_type': 'segwit', 'seed_extend': True, 'seed_variant': 'electrum', + 'seed_extra_words': False}) + + v = w.resolve_next(v.view, d) + self.assertEqual('have_ext', v.view) + + v = w.resolve_next(v.view, d) + self.assertEqual('wallet_password', v.view) + + d.update({'password': None, 'encrypt': False}) + self.assertTrue(w.is_last_view(v.view, d)) + v = w.resolve_next(v.view, d) + + wallet_path = os.path.join(w._daemon.config.get_datadir_wallet_path(), d['wallet_name']) + w.create_storage(wallet_path, d) + + self.assertTrue(os.path.exists(wallet_path)) + async def test_create_standard_wallet_haveseed_bip39(self): w = self.wizard_for(name='test_standard_wallet', wallet_type='standard') v = w._current @@ -226,10 +289,12 @@ class WalletWizardTestCase(WizardTestCase): self.assertEqual('trustedcoin_have_seed', v.view) d.update({ 'seed': 'oblige basket safe educate whale bacon celery demand novel slice various awkward', - 'seed_type': '2fa', 'seed_extend': False, 'seed_variant': 'electrum', + 'seed_type': '2fa', 'seed_extend': True, 'seed_variant': 'electrum', 'seed_extra_words': False }) v = w.resolve_next(v.view, d) + self.assertEqual('trustedcoin_have_ext', v.view) + v = w.resolve_next(v.view, d) self.assertEqual('trustedcoin_keep_disable', v.view) d.update({'trustedcoin_keepordisable': 'keep'}) v = w.resolve_next(v.view, d)