Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions electrum/gui/qml/components/wizard/WCConfirmExt.qml
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ WizardComponent {
Label {
Layout.fillWidth: true
wrapMode: Text.Wrap
text: qsTr('Please enter your custom word(s) a second time:')
text: qsTr('Please enter your custom Passphrase a second time:')
}

TextField {
id: customwordstext
Layout.fillWidth: true
Layout.columnSpan: 2
placeholderText: qsTr('Enter your custom word(s) here')
placeholderText: qsTr('Enter your custom Passphrase')
inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
onTextChanged: checkValid()
}
Expand Down
82 changes: 55 additions & 27 deletions electrum/gui/qml/components/wizard/WCCreateSeed.qml
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,8 @@ WizardComponent {
function apply() {
wizard_data['seed'] = seedtext.text
wizard_data['seed_variant'] = 'electrum' // generated seed always electrum variant
wizard_data['seed_extend'] = true // true so we get forwarded to the passphrase page
}

function setWarningText(numwords) {
var t = [
'<p>',
qsTr('Please save these %1 words on paper (order is important).').arg(numwords),
qsTr('This seed will allow you to recover your wallet in case of computer failure.'),
'</p>',
'<b>' + qsTr('WARNING') + ':</b>',
'<ul>',
'<li>' + qsTr('Never disclose your seed.') + '</li>',
'<li>' + qsTr('Never type it on a website.') + '</li>',
'<li>' + qsTr('Do not store it electronically.') + '</li>',
'</ul>'
]
warningtext.text = t.join(' ')
wizard_data['seed_extend'] = extendcb.checked
wizard_data['seed_extra_words'] = extendcb.checked ? customwordstext.text : ''
}

Flickable {
Expand All @@ -44,13 +29,6 @@ WizardComponent {
width: parent.width
columns: 1

InfoTextArea {
id: warningtext
Layout.fillWidth: true
backgroundColor: constants.darkerDialogBackground
iconStyle: InfoTextArea.IconStyle.Warn
}

Label {
Layout.topMargin: constants.paddingMedium
Layout.fillWidth: true
Expand All @@ -70,8 +48,59 @@ WizardComponent {
}
}

Component.onCompleted : {
setWarningText(12)
ElCheckBox {
id: extendcb
Layout.fillWidth: true
enabled: seedtext.text != ''
text: qsTr('Extend this seed with a Passphrase')
onCheckedChanged: checkIsLast()
}

Label {
Layout.fillWidth: true
wrapMode: Text.Wrap
visible: extendcb.checked
text: [
qsTr('You may extend your seed with a Passphrase (e.g. password manager generated passphrase, custom words, a combination of both...).'),
qsTr("You will need to save both your seed and the extension Passphrase together."),
qsTr('Note that this is NOT your wallet file encryption password.'),
qsTr('If you do not know what this is, leave this field empty.'),
].join(' ')
}

TextField {
id: customwordstext
Layout.fillWidth: true
Layout.topMargin: constants.paddingXSmall
visible: extendcb.checked
placeholderText: qsTr('Enter your custom Passphrase')
inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
onTextChanged: checkIsLast()
}

InfoTextArea {
Layout.fillWidth: true
Layout.topMargin: constants.paddingSmall
backgroundColor: constants.darkerDialogBackground
iconStyle: InfoTextArea.IconStyle.Warn
text: {
var n = seedtext.text.split(' ').filter(Boolean).length || 12
var save = extendcb.checked
? qsTr('Please save these %1 words and your custom Passphrase on paper (order is important).').arg(n)
: qsTr('Please save these %1 words on paper (order is important).').arg(n)
return [
'<p>',
save,
qsTr('This seed will allow you to recover your wallet in case of computer failure.'),
'</p>',
'<b>' + qsTr('WARNING') + ':</b>',
'<ul>',
'<li>' + qsTr('Never disclose your seed.') + '</li>',
'<li>' + qsTr('Never type it on a website.') + '</li>',
'<li>' + qsTr('Do not store it electronically.') + '</li>',
'</ul>'
].join(' ')
}
}
}
}
Expand All @@ -84,7 +113,6 @@ WizardComponent {
id: bitcoin
onGeneratedSeedChanged: {
seedtext.text = generatedSeed
setWarningText(generatedSeed.split(' ').length)
}
}
}
68 changes: 35 additions & 33 deletions electrum/gui/qml/components/wizard/WCEnterExt.qml
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,31 @@ WizardComponent {
valid: true

property int cosigner: 0
property string seedVariant: ''

title: seedVariant == 'bip39' ? qsTr('BIP39 Passphrase') : qsTr('Seed Extension')

function apply() {
var seed_extend = extendcb.checked
// page is only shown after the user opted in on the seed page
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 : ''
wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_extend'] = true
wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_extra_words'] = customwordstext.text
} else {
wizard_data['seed_extend'] = seed_extend
wizard_data['seed_extra_words'] = seed_extend ? customwordstext.text : ''
wizard_data['seed_extend'] = true
wizard_data['seed_extra_words'] = 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
}
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
Expand Down Expand Up @@ -69,29 +67,29 @@ WizardComponent {
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()
text: seedVariant == 'bip39'
? [
qsTr('Enter an optional BIP39 passphrase.'),
qsTr('Each passphrase derives a different wallet.'),
qsTr('This is sometimes incorrectly called the "25th word".'),
qsTr('Note that this is NOT your encryption password.'),
qsTr('If you do not know what this is, leave this field empty.'),
].join(' ')
: [
qsTr('You may extend your seed with a Passphrase (e.g. password manager generated passphrase, custom words, a combination of both...).'),
qsTr("You will need to save both your seed and the extension Passphrase together."),
qsTr('Note that this is NOT your wallet file encryption password.'),
qsTr('If you do not know what this is, leave this field empty.'),
].join(' ')
}

TextField {
id: customwordstext
visible: extendcb.checked // users confuse this with the seed re-entry if shown
Layout.fillWidth: true
Layout.columnSpan: 2
placeholderText: qsTr('Enter your custom word(s)')
placeholderText: seedVariant == 'bip39'
? qsTr('Enter your BIP39 Passphrase')
: qsTr('Enter your custom Passphrase')
inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
onTextChanged: startValidationTimer()
}
Expand All @@ -115,6 +113,10 @@ WizardComponent {
if ('multisig_current_cosigner' in wizard_data)
cosigner = wizard_data['multisig_current_cosigner']
}
if (cosigner)
seedVariant = wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_variant']
else
seedVariant = wizard_data['seed_variant']
checkValid()
}
}
102 changes: 99 additions & 3 deletions electrum/gui/qml/components/wizard/WCHaveSeed.qml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ WizardComponent {
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'] = _canPassphrase
wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_extend'] = extendcb.checked
wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_extra_words'] = extendcb.checked ? customwordstext.text : ''
} else {
wizard_data['seed'] = seedtext.text
wizard_data['seed_variant'] = seed_variant_cb.currentValue
wizard_data['seed_type'] = _seedType
wizard_data['seed_extend'] = _canPassphrase
wizard_data['seed_extend'] = extendcb.checked
wizard_data['seed_extra_words'] = extendcb.checked ? customwordstext.text : ''

// determine script type from electrum seed type
// (used to limit script type options for bip39 cosigners)
Expand Down Expand Up @@ -74,6 +76,7 @@ WizardComponent {
_validationMessage = verifyResult.message
_seedType = verifyResult.type
_canPassphrase = verifyResult.can_passphrase
syncExtControls()

if (!cosigner || !verifyResult.valid) {
_seedValid = verifyResult.valid
Expand All @@ -98,6 +101,39 @@ WizardComponent {
valid = _seedValid
}

function syncExtControls() {
var variant = seed_variant_cb.currentValue
if (variant == 'bip39') {
extendcb.text = qsTr('Use a BIP39 passphrase')
extreason.text = ''
if (!seedtext.text.trim()) {
extendcb.checked = false
extendcb.enabled = false
} else {
extendcb.enabled = true
}
return
}
extendcb.text = qsTr('Extend this seed with a Passphrase')
if (!_seedType) {
extendcb.checked = false
extendcb.enabled = false
extreason.text = ''
return
}
if (!_canPassphrase) {
extendcb.checked = false
extendcb.enabled = false
if (_seedType == 'old')
extreason.text = qsTr('Old Electrum seeds have no extra word.')
else
extreason.text = qsTr('This seed cannot be extended with an extra word.')
return
}
extendcb.enabled = true
extreason.text = ''
}

Flickable {
anchors.fill: parent
contentHeight: mainLayout.height
Expand Down Expand Up @@ -183,6 +219,15 @@ WizardComponent {
checkIsLast()
checkValid()
}
delegate: ItemDelegate {
width: seed_variant_cb.width
text: modelData.text
// grey BIP39 only after an Electrum version actually decodes
enabled: !(modelData.value == 'bip39'
&& seed_variant_cb.currentValue == 'electrum'
&& root._seedType
&& root._seedType != 'old')
}
}

InfoTextArea {
Expand Down Expand Up @@ -214,13 +259,64 @@ WizardComponent {
startValidationTimer()
}
}

ElCheckBox {
id: extendcb
Layout.columnSpan: 2
Layout.fillWidth: true
enabled: false
text: qsTr('Extend this seed with a Passphrase')
onCheckedChanged: checkIsLast()
}

Label {
id: extreason
Layout.columnSpan: 2
Layout.fillWidth: true
wrapMode: Text.Wrap
visible: text
}

Label {
Layout.columnSpan: 2
Layout.fillWidth: true
wrapMode: Text.Wrap
visible: extendcb.checked && extendcb.enabled
text: seed_variant_cb.currentValue == 'bip39'
? [
qsTr('Enter an optional BIP39 passphrase.'),
qsTr('Each passphrase derives a different wallet.'),
qsTr('This is sometimes incorrectly called the "25th word".'),
qsTr('Note that this is NOT your encryption password.'),
qsTr('If you do not know what this is, leave this field empty.'),
].join(' ')
: [
qsTr('You may extend your seed with a Passphrase (e.g. password manager generated passphrase, custom words, a combination of both...).'),
qsTr("You will need to save both your seed and the extension Passphrase together."),
qsTr('Note that this is NOT your wallet file encryption password.'),
qsTr('If you do not know what this is, leave this field empty.'),
].join(' ')
}

TextField {
id: customwordstext
Layout.columnSpan: 2
Layout.fillWidth: true
visible: extendcb.checked && extendcb.enabled
placeholderText: seed_variant_cb.currentValue == 'bip39'
? qsTr('Enter your BIP39 Passphrase')
: qsTr('Enter your custom Passphrase')
inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
onTextChanged: checkIsLast()
}
}
}

function startValidationTimer() {
valid = false
root._seedType = ''
root._validationMessage = ''
syncExtControls()
validationTimer.restart()
}

Expand All @@ -230,7 +326,7 @@ WizardComponent {
repeat: false
onTriggered: {
checkValid()
// checkIsLast depends on 'seed_extend'(_canPassphrase) getting set in apply()
// checkIsLast depends on seed_extend from the on-page checkbox
checkIsLast()
}
}
Expand Down
Loading