1
0

qml: change wizard passphrase flow

This commit is contained in:
f321x
2025-07-08 14:12:56 +02:00
parent bb5b1b3932
commit 290da21187
7 changed files with 204 additions and 69 deletions

View File

@@ -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']
}
}
}

View File

@@ -14,8 +14,7 @@ WizardComponent {
function checkValid() { function checkValid() {
var seedvalid = wizard.wiz.isMatchingSeed(wizard_data['seed'], confirm.text) var seedvalid = wizard.wiz.isMatchingSeed(wizard_data['seed'], confirm.text)
var customwordsvalid = customwordstext.text == wizard_data['seed_extra_words'] valid = seedvalid
valid = seedvalid && (wizard_data['seed_extend'] ? customwordsvalid : true)
} }
Flickable { Flickable {
@@ -46,19 +45,6 @@ WizardComponent {
placeholderText: qsTr('Enter your seed') placeholderText: qsTr('Enter your seed')
onTextChanged: checkValid() 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']
}
} }

View File

@@ -9,13 +9,12 @@ import "../controls"
WizardComponent { WizardComponent {
securePage: true securePage: true
valid: seedtext.text != '' && extendcb.checked ? customwordstext.text != '' : true valid: seedtext.text != ''
function apply() { function apply() {
wizard_data['seed'] = seedtext.text wizard_data['seed'] = seedtext.text
wizard_data['seed_variant'] = 'electrum' // generated seed always electrum variant wizard_data['seed_variant'] = 'electrum' // generated seed always electrum variant
wizard_data['seed_extend'] = extendcb.checked wizard_data['seed_extend'] = true // true so we get forwarded to the passphrase page
wizard_data['seed_extra_words'] = extendcb.checked ? customwordstext.text : ''
} }
function setWarningText(numwords) { 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 : { Component.onCompleted : {
setWarningText(12) setWarningText(12)
} }

View File

@@ -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.'),
'<br/>',
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()
}
}

View File

@@ -24,19 +24,16 @@ WizardComponent {
property bool _seedValid property bool _seedValid
function apply() { function apply() {
var seed_extend = extendcb.checked && _canPassphrase
if (cosigner) { if (cosigner) {
wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed'] = seedtext.text 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_variant'] = seed_variant_cb.currentValue
wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_type'] = _seedType 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_extend'] = _canPassphrase
wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_extra_words'] = seed_extend ? customwordstext.text : ''
} else { } else {
wizard_data['seed'] = seedtext.text wizard_data['seed'] = seedtext.text
wizard_data['seed_variant'] = seed_variant_cb.currentValue wizard_data['seed_variant'] = seed_variant_cb.currentValue
wizard_data['seed_type'] = _seedType wizard_data['seed_type'] = _seedType
wizard_data['seed_extend'] = seed_extend wizard_data['seed_extend'] = _canPassphrase
wizard_data['seed_extra_words'] = seed_extend ? customwordstext.text : ''
// determine script type from electrum seed type // determine script type from electrum seed type
// (used to limit script type options for bip39 cosigners) // (used to limit script type options for bip39 cosigners)
@@ -52,22 +49,20 @@ WizardComponent {
function setSeedTypeHelpText() { function setSeedTypeHelpText() {
var t = { var t = {
'electrum': [ '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('Electrum seeds are the default seed type.'),
qsTr('If you are restoring from a seed previously created by Electrum, choose this option') qsTr('If you are restoring from a seed previously created by Electrum, choose this option')
].join(' '), ].join(' '),
'bip39': [ 'bip39': [
qsTr('BIP39 seeds can be imported in Electrum, so that users can access funds locked in other wallets.'), qsTr('BIP39 seeds can be imported in Electrum, so that users can access funds locked in other wallets.'),
'<br/><br/>', qsTr('BIP39 seeds do not include a version number, which compromises compatibility with future software.'),
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.')
].join(' '), ].join(' '),
'slip39': [ 'slip39': [
qsTr('SLIP39 seeds can be imported in Electrum, so that users can access funds locked in other wallets.'), qsTr('SLIP39 seeds can be imported in Electrum, so that users can access funds locked in other wallets.'),
'<br/><br/>',
qsTr('However, we do not generate SLIP39 seeds.')
].join(' ') ].join(' ')
} }
infotext.text = t[seed_variant_cb.currentValue] infotext.text = t[seed_variant_cb.currentValue]
infotext.visible = !cosigner && !is2fa && seed_variant_cb.currentValue != 'electrum'
} }
function checkValid() { function checkValid() {
@@ -100,11 +95,6 @@ WizardComponent {
} }
} }
if (_canPassphrase && extendcb.checked && customwordstext.text == '') {
valid = false
return
}
valid = _seedValid valid = _seedValid
} }
@@ -196,7 +186,6 @@ WizardComponent {
InfoTextArea { InfoTextArea {
id: infotext id: infotext
visible: !cosigner && !is2fa
Layout.fillWidth: true Layout.fillWidth: true
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.bottomMargin: constants.paddingLarge Layout.bottomMargin: constants.paddingLarge
@@ -221,26 +210,6 @@ WizardComponent {
startValidationTimer() 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()
}
} }
} }

View File

@@ -68,14 +68,18 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
'wallet_type': {'gui': 'WCWalletType'}, 'wallet_type': {'gui': 'WCWalletType'},
'keystore_type': {'gui': 'WCKeystoreType'}, 'keystore_type': {'gui': 'WCKeystoreType'},
'create_seed': {'gui': 'WCCreateSeed'}, 'create_seed': {'gui': 'WCCreateSeed'},
'create_ext': {'gui': 'WCEnterExt'},
'confirm_seed': {'gui': 'WCConfirmSeed'}, 'confirm_seed': {'gui': 'WCConfirmSeed'},
'confirm_ext': {'gui': 'WCConfirmExt'},
'have_seed': {'gui': 'WCHaveSeed'}, 'have_seed': {'gui': 'WCHaveSeed'},
'have_ext': {'gui': 'WCEnterExt'},
'script_and_derivation': {'gui': 'WCScriptAndDerivation'}, 'script_and_derivation': {'gui': 'WCScriptAndDerivation'},
'have_master_key': {'gui': 'WCHaveMasterKey'}, 'have_master_key': {'gui': 'WCHaveMasterKey'},
'multisig': {'gui': 'WCMultisig'}, 'multisig': {'gui': 'WCMultisig'},
'multisig_cosigner_keystore': {'gui': 'WCCosignerKeystore'}, 'multisig_cosigner_keystore': {'gui': 'WCCosignerKeystore'},
'multisig_cosigner_key': {'gui': 'WCHaveMasterKey'}, 'multisig_cosigner_key': {'gui': 'WCHaveMasterKey'},
'multisig_cosigner_seed': {'gui': 'WCHaveSeed'}, 'multisig_cosigner_seed': {'gui': 'WCHaveSeed'},
'multisig_cosigner_have_ext': {'gui': 'WCEnterExt'},
'multisig_cosigner_script_and_derivation': {'gui': 'WCScriptAndDerivation'}, 'multisig_cosigner_script_and_derivation': {'gui': 'WCScriptAndDerivation'},
'imported': {'gui': 'WCImport'}, 'imported': {'gui': 'WCImport'},
'wallet_password': {'gui': 'WCWalletPassword'} 'wallet_password': {'gui': 'WCWalletPassword'}

View File

@@ -333,7 +333,7 @@ class KeystoreWizard(AbstractWizard):
def keystore_from_data(self, wallet_type: str, data: dict): def keystore_from_data(self, wallet_type: str, data: dict):
if data['keystore_type'] in ['createseed', 'haveseed'] and 'seed' in data: 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': if data['seed_variant'] == 'electrum':
for_multisig = wallet_type in ['multisig'] for_multisig = wallet_type in ['multisig']
return keystore.from_seed(data['seed'], passphrase=seed_extension, for_multisig=for_multisig) return keystore.from_seed(data['seed'], passphrase=seed_extension, for_multisig=for_multisig)