qt, qml: allow BIP39 seeds which fail checksum or wordlist (fixes #8720)
removes verifySeed from qebitcoin as this code was 99% duplicate of wizard.validate_seed
This commit is contained in:
@@ -18,17 +18,20 @@ WizardComponent {
|
||||
property int participants: 0
|
||||
property string multisigMasterPubkey: wizard_data['multisig_master_pubkey']
|
||||
|
||||
property string _seedType
|
||||
property string _validationMessage
|
||||
|
||||
function apply() {
|
||||
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'] = bitcoin.seedType
|
||||
wizard_data['multisig_cosigner_data'][cosigner.toString()]['seed_type'] = _seedType
|
||||
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'] = bitcoin.seedType
|
||||
wizard_data['seed_type'] = _seedType
|
||||
wizard_data['seed_extend'] = extendcb.checked
|
||||
wizard_data['seed_extra_words'] = extendcb.checked ? customwordstext.text : ''
|
||||
|
||||
@@ -38,7 +41,7 @@ WizardComponent {
|
||||
wizard_data['script_type'] = {
|
||||
'standard': 'p2sh',
|
||||
'segwit': 'p2wsh'
|
||||
}[bitcoin.seedType]
|
||||
}[_seedType]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,14 +69,18 @@ WizardComponent {
|
||||
|
||||
function checkValid() {
|
||||
valid = false
|
||||
validationtext.text = ''
|
||||
_validationMessage = ''
|
||||
|
||||
if (extendcb.checked && customwordstext.text == '')
|
||||
return
|
||||
|
||||
var validSeed = bitcoin.verifySeed(seedtext.text, seed_variant_cb.currentValue, wizard_data['wallet_type'])
|
||||
if (!cosigner || !validSeed) {
|
||||
valid = validSeed
|
||||
var verifyResult = wiz.verifySeed(seedtext.text, seed_variant_cb.currentValue, wizard_data['wallet_type'])
|
||||
|
||||
_validationMessage = verifyResult.message
|
||||
_seedType = verifyResult.type
|
||||
|
||||
if (!cosigner || !verifyResult.valid) {
|
||||
valid = verifyResult.valid
|
||||
return
|
||||
} else {
|
||||
// bip39 validate after derivation path is known
|
||||
@@ -196,21 +203,19 @@ WizardComponent {
|
||||
placeholderText: cosigner ? qsTr('Enter cosigner seed') : qsTr('Enter your seed')
|
||||
|
||||
indicatorValid: root.valid
|
||||
|
||||
? root._seedType == 'bip39' && root._validationMessage
|
||||
? false
|
||||
: root.valid
|
||||
: root.valid
|
||||
indicatorText: root.valid
|
||||
? root._validationMessage
|
||||
? root._validationMessage
|
||||
: root._seedType
|
||||
: ''
|
||||
onTextChanged: {
|
||||
startValidationTimer()
|
||||
}
|
||||
}
|
||||
TextArea {
|
||||
id: validationtext
|
||||
visible: text
|
||||
Layout.fillWidth: true
|
||||
readOnly: true
|
||||
wrapMode: TextInput.WordWrap
|
||||
background: Rectangle {
|
||||
color: 'transparent'
|
||||
}
|
||||
}
|
||||
|
||||
ElCheckBox {
|
||||
id: extendcb
|
||||
@@ -231,14 +236,10 @@ WizardComponent {
|
||||
}
|
||||
}
|
||||
|
||||
Bitcoin {
|
||||
id: bitcoin
|
||||
onSeedTypeChanged: seedtext.indicatorText = bitcoin.seedType
|
||||
}
|
||||
|
||||
function startValidationTimer() {
|
||||
valid = false
|
||||
seedtext.indicatorText = ''
|
||||
root._seedType = ''
|
||||
root._validationMessage = ''
|
||||
validationTimer.restart()
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,9 @@ from electrum import keystore
|
||||
from electrum.i18n import _
|
||||
from electrum.bip32 import is_bip32_derivation, xpub_type
|
||||
from electrum.logging import get_logger
|
||||
from electrum.slip39 import decode_mnemonic, Slip39Error
|
||||
from electrum.util import get_asyncio_loop
|
||||
from electrum.transaction import tx_from_any
|
||||
from electrum.mnemonic import Mnemonic, is_any_2fa_seed_type
|
||||
from electrum.mnemonic import Mnemonic
|
||||
from electrum.old_mnemonic import wordlist as old_wordlist
|
||||
from electrum.bitcoin import is_address
|
||||
|
||||
@@ -50,7 +49,7 @@ class QEBitcoin(QObject):
|
||||
|
||||
@pyqtSlot()
|
||||
@pyqtSlot(str)
|
||||
@pyqtSlot(str,str)
|
||||
@pyqtSlot(str, str)
|
||||
def generateSeed(self, seed_type='segwit', language='en'):
|
||||
self._logger.debug('generating seed of type ' + str(seed_type))
|
||||
|
||||
@@ -61,50 +60,6 @@ class QEBitcoin(QObject):
|
||||
|
||||
asyncio.run_coroutine_threadsafe(co_gen_seed(seed_type, language), get_asyncio_loop())
|
||||
|
||||
@pyqtSlot(str,str,str, result=bool)
|
||||
def verifySeed(self, seed, seed_variant, wallet_type='standard'):
|
||||
seed_type = ''
|
||||
seed_valid = False
|
||||
self.validationMessage = ''
|
||||
|
||||
if seed_variant == 'electrum':
|
||||
seed_type = mnemonic.seed_type(seed)
|
||||
if seed_type != '':
|
||||
seed_valid = True
|
||||
elif seed_variant == 'bip39':
|
||||
is_checksum, is_wordlist = keystore.bip39_is_checksum_valid(seed)
|
||||
status = ('checksum: ' + ('ok' if is_checksum else 'failed')) if is_wordlist else 'unknown wordlist'
|
||||
self.validationMessage = 'BIP39 (%s)' % status
|
||||
|
||||
if is_checksum:
|
||||
seed_type = 'bip39'
|
||||
seed_valid = True
|
||||
elif seed_variant == 'slip39': # TODO: incomplete impl, this code only validates a single share.
|
||||
try:
|
||||
share = decode_mnemonic(seed)
|
||||
seed_type = 'slip39'
|
||||
self.validationMessage = 'SLIP39: share #%d in %dof%d scheme' % (share.group_index, share.group_threshold, share.group_count)
|
||||
except Slip39Error as e:
|
||||
self.validationMessage = 'SLIP39: %s' % str(e)
|
||||
seed_valid = False # for now
|
||||
else:
|
||||
raise Exception(f'unknown seed variant {seed_variant}')
|
||||
|
||||
# check if seed matches wallet type
|
||||
if wallet_type == '2fa' and not is_any_2fa_seed_type(seed_type):
|
||||
seed_valid = False
|
||||
elif wallet_type == 'standard' and seed_type not in ['old', 'standard', 'segwit', 'bip39']:
|
||||
seed_valid = False
|
||||
elif wallet_type == 'multisig' and seed_type not in ['standard', 'segwit', 'bip39']:
|
||||
seed_valid = False
|
||||
|
||||
self._seed_type = seed_type
|
||||
self.seedTypeChanged.emit()
|
||||
|
||||
self._logger.debug('seed verified: ' + str(seed_valid))
|
||||
|
||||
return seed_valid
|
||||
|
||||
@pyqtSlot(str, str, result=bool)
|
||||
def verifyMasterKey(self, key, wallet_type='standard'):
|
||||
self.validationMessage = ''
|
||||
|
||||
@@ -107,6 +107,15 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
|
||||
def isMatchingSeed(self, seed, seed_again):
|
||||
return mnemonic.is_matching_seed(seed=seed, seed_again=seed_again)
|
||||
|
||||
@pyqtSlot(str, str, str, result='QVariantMap')
|
||||
def verifySeed(self, seed, seed_variant, wallet_type='standard'):
|
||||
seed_valid, seed_type, validation_message = self.validate_seed(seed, seed_variant, wallet_type)
|
||||
return {
|
||||
'valid': seed_valid,
|
||||
'type': seed_type,
|
||||
'message': validation_message
|
||||
}
|
||||
|
||||
@pyqtSlot('QJSValue', bool, str)
|
||||
def createStorage(self, js_data, single_password_enabled, single_password):
|
||||
self._logger.info('Creating wallet from wizard data')
|
||||
|
||||
@@ -267,8 +267,9 @@ class SeedLayout(QVBoxLayout):
|
||||
if self.seed_type == 'bip39':
|
||||
from electrum.keystore import bip39_is_checksum_valid
|
||||
is_checksum, is_wordlist = bip39_is_checksum_valid(s)
|
||||
status = ('checksum: ' + ('ok' if is_checksum else 'failed')) if is_wordlist else 'unknown wordlist'
|
||||
label = 'BIP39' + ' (%s)'%status
|
||||
label = ''
|
||||
if bool(s):
|
||||
label = ('' if is_checksum else _('BIP39 Checksum failed')) if is_wordlist else _('Unknown BIP39 wordlist')
|
||||
elif self.seed_type == 'slip39':
|
||||
self.slip39_mnemonics[self.slip39_mnemonic_index] = s
|
||||
try:
|
||||
|
||||
@@ -478,12 +478,13 @@ class NewWalletWizard(AbstractWizard):
|
||||
seed_valid = True
|
||||
elif seed_variant == 'bip39':
|
||||
is_checksum, is_wordlist = keystore.bip39_is_checksum_valid(seed)
|
||||
status = ('checksum: ' + ('ok' if is_checksum else 'failed')) if is_wordlist else 'unknown wordlist'
|
||||
validation_message = 'BIP39 (%s)' % status
|
||||
|
||||
if is_checksum:
|
||||
seed_type = 'bip39'
|
||||
seed_valid = True
|
||||
validation_message = ('' if is_checksum else _('BIP39 checksum failed')) if is_wordlist else _('Unknown BIP39 wordlist')
|
||||
if not bool(seed):
|
||||
validation_message = ''
|
||||
seed_type = 'bip39'
|
||||
# bip39 always valid, even if checksum failed, see #8720
|
||||
# however, reject empty string
|
||||
seed_valid = bool(seed)
|
||||
elif seed_variant == 'slip39':
|
||||
# seed shares should be already validated by wizard page, we have a combined encrypted seed
|
||||
if seed and isinstance(seed, EncryptedSeed):
|
||||
|
||||
Reference in New Issue
Block a user