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)