diff --git a/electrum/gui/qml/components/NewWalletWizard.qml b/electrum/gui/qml/components/NewWalletWizard.qml
index 8a0c13c21..37365ba8c 100644
--- a/electrum/gui/qml/components/NewWalletWizard.qml
+++ b/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'
diff --git a/electrum/gui/qml/components/Wallets.qml b/electrum/gui/qml/components/Wallets.qml
index 9877754e4..11c627ec0 100644
--- a/electrum/gui/qml/components/Wallets.qml
+++ b/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'])
})
}
}
diff --git a/electrum/gui/qml/components/WizardComponents.qml b/electrum/gui/qml/components/WizardComponents.qml
index c8dd3df93..7110820ab 100644
--- a/electrum/gui/qml/components/WizardComponents.qml
+++ b/electrum/gui/qml/components/WizardComponents.qml
@@ -123,18 +123,18 @@ Item {
function setWarningText(numwords) {
var t = [
- "
",
- 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."),
- "
",
- "" + qsTr("WARNING") + ":",
- "",
- "- " + qsTr("Never disclose your seed.") + "
",
- "- " + qsTr("Never type it on a website.") + "
",
- "- " + qsTr("Do not store it electronically.") + "
",
- "
"
+ '',
+ 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.'),
+ '
',
+ '' + qsTr('WARNING') + ':',
+ '',
+ '- ' + qsTr('Never disclose your seed.') + '
',
+ '- ' + qsTr('Never type it on a website.') + '
',
+ '- ' + qsTr('Do not store it electronically.') + '
',
+ '
'
]
- 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()
}
diff --git a/electrum/gui/qml/qebitcoin.py b/electrum/gui/qml/qebitcoin.py
index d4f602235..16ed23a05 100644
--- a/electrum/gui/qml/qebitcoin.py
+++ b/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))
diff --git a/electrum/gui/qml/qedaemon.py b/electrum/gui/qml/qedaemon.py
index e98e7dd90..8feeab858 100644
--- a/electrum/gui/qml/qedaemon.py
+++ b/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
diff --git a/electrum/gui/qml/qewalletdb.py b/electrum/gui/qml/qewalletdb.py
index fe0d3bb16..7a638860f 100644
--- a/electrum/gui/qml/qewalletdb.py
+++ b/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))