Browse Source

validate seeds for Electrum, BIP39, SLIP39 seeds and perform create wallet in from seed scenario

Currently only Electrum seeds are considered valid.
For BIP39 additional dialog is needed.
For SLIP39 multiple mnemonics need to be supported to generate a seed
patch-4
Sander van Grieken 3 years ago
parent
commit
7e1606fe86
  1. 2
      electrum/gui/qml/components/NewWalletWizard.qml
  2. 2
      electrum/gui/qml/components/Wallets.qml
  3. 85
      electrum/gui/qml/components/WizardComponents.qml
  4. 82
      electrum/gui/qml/qebitcoin.py
  5. 5
      electrum/gui/qml/qedaemon.py
  6. 6
      electrum/gui/qml/qewalletdb.py

2
electrum/gui/qml/components/NewWalletWizard.qml

@ -11,6 +11,8 @@ Wizard {
signal walletCreated
property alias path: walletdb.path
enter: null // disable transition
// State transition functions. These functions are called when the 'Next'

2
electrum/gui/qml/components/Wallets.qml

@ -107,6 +107,8 @@ Pane {
dialog.open()
dialog.walletCreated.connect(function() {
Daemon.availableWallets.reload()
// and load the new wallet
Daemon.load_wallet(dialog.path, dialog.wizard_data['password'])
})
}
}

85
electrum/gui/qml/components/WizardComponents.qml

@ -123,18 +123,18 @@ Item {
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>"
'<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("")
warningtext.text = t.join(' ')
}
Flickable {
@ -187,7 +187,7 @@ Item {
id: bitcoin
onGeneratedSeedChanged: {
seedtext.text = generated_seed
setWarningText(generated_seed.split(" ").length)
setWarningText(generated_seed.split(' ').length)
}
}
}
@ -195,16 +195,16 @@ Item {
property Component haveseed: Component {
WizardComponent {
id: root
valid: false
onAccept: {
wizard_data['seed'] = seedtext.text
wizard_data['seed_type'] = bitcoin.seed_type
wizard_data['seed_extend'] = extendcb.checked
wizard_data['seed_extra_words'] = extendcb.checked ? customwordstext.text : ''
wizard_data['seed_bip39'] = bip39cb.checked
}
function checkValid() {
wizard_data['seed_bip39'] = seed_type.getTypeCode() == 'BIP39'
wizard_data['seed_slip39'] = seed_type.getTypeCode() == 'SLIP39'
}
function setSeedTypeHelpText() {
@ -230,6 +230,10 @@ Item {
infotext.text = t[seed_type.currentText]
}
function checkValid() {
bitcoin.verify_seed(seedtext.text, seed_type.getTypeCode() == 'BIP39', seed_type.getTypeCode() == 'SLIP39')
}
Flickable {
anchors.fill: parent
contentHeight: mainLayout.height
@ -243,11 +247,18 @@ Item {
Label {
text: qsTr('Seed Type')
Layout.fillWidth: true
}
ComboBox {
id: seed_type
model: ['Electrum', 'BIP39', 'SLIP39']
onActivated: setSeedTypeHelpText()
onActivated: {
setSeedTypeHelpText()
checkValid()
}
function getTypeCode() {
return currentText
}
}
InfoTextArea {
id: infotext
@ -263,9 +274,36 @@ Item {
Layout.fillWidth: true
Layout.columnSpan: 2
onTextChanged: {
checkValid()
validationTimer.restart()
}
Rectangle {
anchors.fill: contentText
color: 'green'
border.color: Material.accentColor
radius: 2
}
Label {
id: contentText
anchors.right: parent.right
anchors.bottom: parent.bottom
leftPadding: text != '' ? 16 : 0
rightPadding: text != '' ? 16 : 0
font.bold: false
font.pixelSize: 13
}
}
TextArea {
id: validationtext
visible: text != ''
Layout.fillWidth: true
readOnly: true
wrapMode: TextInput.WordWrap
background: Rectangle {
color: 'transparent'
}
}
CheckBox {
id: extendcb
Layout.columnSpan: 2
@ -284,7 +322,18 @@ Item {
Bitcoin {
id: bitcoin
onSeedTypeChanged: contentText.text = bitcoin.seed_type
onSeedValidChanged: root.valid = bitcoin.seed_valid
onValidationMessageChanged: validationtext.text = bitcoin.validation_message
}
Timer {
id: validationTimer
interval: 500
repeat: false
onTriggered: checkValid()
}
Component.onCompleted: {
setSeedTypeHelpText()
}

82
electrum/gui/qml/qebitcoin.py

@ -3,6 +3,8 @@ import asyncio
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum.logging import get_logger
from electrum.keystore import bip39_is_checksum_valid
from electrum.slip39 import decode_mnemonic, Slip39Error
from electrum import mnemonic
class QEBitcoin(QObject):
@ -18,10 +20,28 @@ class QEBitcoin(QObject):
seedValidChanged = pyqtSignal()
seedValid = False
seedTypeChanged = pyqtSignal()
seedType = ''
validationMessageChanged = pyqtSignal()
validationMessage = ''
@pyqtProperty('QString', notify=generatedSeedChanged)
def generated_seed(self):
return self.generatedSeed
@pyqtProperty(bool, notify=seedValidChanged)
def seed_valid(self):
return self.seedValid
@pyqtProperty('QString', notify=seedTypeChanged)
def seed_type(self):
return self.seedType
@pyqtProperty('QString', notify=validationMessageChanged)
def validation_message(self):
return self.validationMessage
@pyqtSlot()
@pyqtSlot(str)
@pyqtSlot(str,str)
@ -36,17 +56,55 @@ class QEBitcoin(QObject):
loop = asyncio.get_event_loop()
asyncio.run_coroutine_threadsafe(co_gen_seed(seed_type, language), loop)
@pyqtProperty(bool, notify=seedValidChanged)
def seed_valid(self):
return self.seedValid
@pyqtSlot(str)
@pyqtSlot(str,str)
@pyqtSlot(str,str,str)
@pyqtSlot(str,str,str,str)
def verify_seed(self, seed, bip39=False, seed_type='segwit', language='en'):
self._logger.debug('verify seed of type ' + str(seed_type))
#TODO
#self._logger.debug('seed verified')
#self.seedValidChanged.emit()
@pyqtSlot(str,bool,bool)
@pyqtSlot(str,bool,bool,str,str,str)
def verify_seed(self, seed, bip39=False, slip39=False, wallet_type='standard', language='en'):
self._logger.debug('bip39 ' + str(bip39))
self._logger.debug('slip39 ' + str(slip39))
seed_type = ''
seed_valid = False
validation_message = ''
if not (bip39 or slip39):
seed_type = mnemonic.seed_type(seed)
if seed_type != '':
seed_valid = True
elif bip39:
is_checksum, is_wordlist = 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
seed_valid = False # for now
elif slip39: # TODO: incomplete impl, this code only validates a single share.
try:
share = decode_mnemonic(seed)
seed_type = 'slip39'
validation_message = 'SLIP39: share #%d in %dof%d scheme' % (share.group_index, share.group_threshold, share.group_count)
except Slip39Error as e:
validation_message = 'SLIP39: %s' % str(e)
seed_valid = False # for now
# cosigning seed
if wallet_type != 'standard' and seed_type not in ['standard', 'segwit']:
seed_type = ''
seed_valid = False
self.seedType = seed_type
self.seedTypeChanged.emit()
if self.validationMessage != validation_message:
self.validationMessage = validation_message
self.validationMessageChanged.emit()
if self.seedValid != seed_valid:
self.seedValid = seed_valid
self.seedValidChanged.emit()
self._logger.debug('seed verified: ' + str(seed_valid))

5
electrum/gui/qml/qedaemon.py

@ -6,7 +6,7 @@ from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex
from electrum.util import register_callback, get_new_wallet_name
from electrum.logging import get_logger
from electrum.wallet import Wallet, Abstract_Wallet
from electrum.storage import WalletStorage
from electrum.storage import WalletStorage, StorageReadWriteError
from .qewallet import QEWallet
@ -106,6 +106,9 @@ class QEDaemon(QObject):
self._logger.debug('load wallet ' + str(self._path))
try:
storage = WalletStorage(self._path)
if not storage.file_exists():
self.couldNotOpenFile.emit()
return
except StorageReadWriteError as e:
self.couldNotOpenFile.emit()
return

6
electrum/gui/qml/qewalletdb.py

@ -59,8 +59,8 @@ class QEWalletDB(QObject):
if wallet_path == self._path:
return
self._logger.info('setting path: ' + wallet_path)
self.reset()
self._logger.warning('path: ' + wallet_path)
self._path = wallet_path
self.load_storage()
@ -229,6 +229,10 @@ class QEWalletDB(QObject):
db.load_plugins()
db.write(storage)
# minimally populate self after create
self._password = data['password']
self.path = path
self.createSuccess.emit()
except Exception as e:
self._logger.error(str(e))

Loading…
Cancel
Save