From e329c54162e80361cef4ef90d90e3cb4bfe4e8f1 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Wed, 16 Mar 2022 22:32:16 +0100 Subject: [PATCH] implement bip39 seed to wallet fix auto-upgrade wallet --- electrum/gui/qml/components/OpenWallet.qml | 4 -- electrum/gui/qml/components/Wallets.qml | 30 ++++++----- .../qml/components/wizard/WCBIP39Refine.qml | 44 +++++++++++----- electrum/gui/qml/qebitcoin.py | 4 ++ electrum/gui/qml/qewallet.py | 5 +- electrum/gui/qml/qewalletdb.py | 50 ++++++------------- 6 files changed, 71 insertions(+), 66 deletions(-) diff --git a/electrum/gui/qml/components/OpenWallet.qml b/electrum/gui/qml/components/OpenWallet.qml index 04dbb838d..55b510cae 100644 --- a/electrum/gui/qml/components/OpenWallet.qml +++ b/electrum/gui/qml/components/OpenWallet.qml @@ -95,10 +95,6 @@ Pane { Daemon.availableWallets.reload() app.stack.pop() } - onRequiresUpgradeChanged: { - if (requiresUpgrade) - wallet_db.doUpgrade() - } onReadyChanged: { if (ready) { Daemon.load_wallet(Daemon.path, password.text) diff --git a/electrum/gui/qml/components/Wallets.qml b/electrum/gui/qml/components/Wallets.qml index 11c627ec0..f33f29d6a 100644 --- a/electrum/gui/qml/components/Wallets.qml +++ b/electrum/gui/qml/components/Wallets.qml @@ -1,6 +1,7 @@ import QtQuick 2.6 import QtQuick.Layouts 1.0 import QtQuick.Controls 2.0 +import QtQuick.Controls.Material 2.0 import org.electrum 1.0 @@ -25,25 +26,25 @@ Pane { columns: 4 Label { text: 'Wallet'; Layout.columnSpan: 2 } - Label { text: Daemon.walletName; Layout.columnSpan: 2 } + Label { text: Daemon.walletName; Layout.columnSpan: 2; color: Material.accentColor } + + Label { text: 'derivation path (BIP32)'; visible: Daemon.currentWallet.isDeterministic; Layout.columnSpan: 2 } + Label { text: Daemon.currentWallet.derivationPath; visible: Daemon.currentWallet.isDeterministic; color: Material.accentColor; Layout.columnSpan: 2 } Label { text: 'txinType' } - Label { text: Daemon.currentWallet.txinType } + Label { text: Daemon.currentWallet.txinType; color: Material.accentColor } Label { text: 'is deterministic' } - Label { text: Daemon.currentWallet.isDeterministic } + Label { text: Daemon.currentWallet.isDeterministic; color: Material.accentColor } Label { text: 'is watch only' } - Label { text: Daemon.currentWallet.isWatchOnly } + Label { text: Daemon.currentWallet.isWatchOnly; color: Material.accentColor } Label { text: 'is Encrypted' } - Label { text: Daemon.currentWallet.isEncrypted } + Label { text: Daemon.currentWallet.isEncrypted; color: Material.accentColor } Label { text: 'is Hardware' } - Label { text: Daemon.currentWallet.isHardware } - - Label { text: 'derivation path (BIP32)'; visible: Daemon.currentWallet.isDeterministic } - Label { text: Daemon.currentWallet.derivationPath; visible: Daemon.currentWallet.isDeterministic } + Label { text: Daemon.currentWallet.isHardware; color: Material.accentColor } } } // } @@ -75,16 +76,19 @@ Pane { } RowLayout { - x: 10 spacing: 10 - width: parent.width - 20 + width: parent.width Image { - source: "../../kivy/theming/light/wallet.png" + id: walleticon + source: "../../icons/wallet.png" + fillMode: Image.PreserveAspectFit + Layout.preferredWidth: 32 + Layout.preferredHeight: 32 } Label { - font.pointSize: 11 + font.pixelSize: 18 text: model.name Layout.fillWidth: true } diff --git a/electrum/gui/qml/components/wizard/WCBIP39Refine.qml b/electrum/gui/qml/components/wizard/WCBIP39Refine.qml index 44b5e744e..872d712a7 100644 --- a/electrum/gui/qml/components/wizard/WCBIP39Refine.qml +++ b/electrum/gui/qml/components/wizard/WCBIP39Refine.qml @@ -10,24 +10,36 @@ WizardComponent { valid: false onAccept: { + wizard_data['script_type'] = scripttypegroup.checkedButton.scripttype + wizard_data['derivation_path'] = derivationpathtext.text } - - function setDerivationPath() { - var addrtype = { + function getScriptTypePurposeDict() { + return { 'p2pkh': 44, 'p2wpkh-p2sh': 49, 'p2wpkh': 84 } - var nChain = Network.isTestNet ? 1 : 0 + } + + function validate() { + valid = false + if (!scripttypegroup.checkedButton.scripttype in getScriptTypePurposeDict()) + return + if (!bitcoin.verify_derivation_path(derivationpathtext.text)) + return + valid = true + } + + function setDerivationPath() { + var p = getScriptTypePurposeDict() derivationpathtext.text = - "m/" + addrtype[addresstypegroup.checkedButton.addresstype] + "'/" + "m/" + p[scripttypegroup.checkedButton.scripttype] + "'/" + (Network.isTestNet ? 1 : 0) + "'/0'" } ButtonGroup { - id: addresstypegroup + id: scripttypegroup onCheckedButtonChanged: { - console.log('button changed: ' + checkedButton.addresstype) setDerivationPath() } } @@ -50,18 +62,18 @@ WizardComponent { } Label { text: qsTr('Choose the type of addresses in your wallet.') } RadioButton { - ButtonGroup.group: addresstypegroup - property string addresstype: 'p2pkh' + ButtonGroup.group: scripttypegroup + property string scripttype: 'p2pkh' text: qsTr('legacy (p2pkh)') } RadioButton { - ButtonGroup.group: addresstypegroup - property string addresstype: 'p2wpkh-p2sh' + ButtonGroup.group: scripttypegroup + property string scripttype: 'p2wpkh-p2sh' text: qsTr('wrapped segwit (p2wpkh-p2sh)') } RadioButton { - ButtonGroup.group: addresstypegroup - property string addresstype: 'p2wpkh' + ButtonGroup.group: scripttypegroup + property string scripttype: 'p2wpkh' checked: true text: qsTr('native segwit (p2wpkh)') } @@ -73,8 +85,14 @@ WizardComponent { id: derivationpathtext Layout.fillWidth: true placeholderText: qsTr('Derivation path') + onTextChanged: validate() } } } + + Bitcoin { + id: bitcoin + } + } diff --git a/electrum/gui/qml/qebitcoin.py b/electrum/gui/qml/qebitcoin.py index f556c7bfe..545b8db66 100644 --- a/electrum/gui/qml/qebitcoin.py +++ b/electrum/gui/qml/qebitcoin.py @@ -4,6 +4,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from electrum.logging import get_logger from electrum.keystore import bip39_is_checksum_valid +from electrum.bip32 import is_bip32_derivation from electrum.slip39 import decode_mnemonic, Slip39Error from electrum import mnemonic @@ -107,3 +108,6 @@ class QEBitcoin(QObject): self._logger.debug('seed verified: ' + str(seed_valid)) + @pyqtSlot(str, result=bool) + def verify_derivation_path(self, path): + return is_bip32_derivation(path) diff --git a/electrum/gui/qml/qewallet.py b/electrum/gui/qml/qewallet.py index 060556c9a..8682c95f5 100644 --- a/electrum/gui/qml/qewallet.py +++ b/electrum/gui/qml/qewallet.py @@ -175,7 +175,10 @@ class QEWallet(QObject): @pyqtProperty('QString', notify=dataChanged) def derivationPath(self): - return self.wallet.get_address_path_str(self.wallet.dummy_address()) + keystores = self.wallet.get_keystores() + if len(keystores) > 1: + self._logger.debug('multiple keystores not supported yet') + return keystores[0].get_derivation_prefix() balanceChanged = pyqtSignal() diff --git a/electrum/gui/qml/qewalletdb.py b/electrum/gui/qml/qewalletdb.py index 7a638860f..34000d77a 100644 --- a/electrum/gui/qml/qewalletdb.py +++ b/electrum/gui/qml/qewalletdb.py @@ -5,6 +5,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from electrum.logging import Logger, get_logger from electrum.storage import WalletStorage, StorageEncryptionVersion from electrum.wallet_db import WalletDB +from electrum.bip32 import normalize_bip32_derivation from electrum.util import InvalidPassword from electrum import keystore @@ -28,8 +29,6 @@ class QEWalletDB(QObject): passwordChanged = pyqtSignal() invalidPasswordChanged = pyqtSignal() requiresSplitChanged = pyqtSignal() - requiresUpgradeChanged = pyqtSignal() - upgradingChanged = pyqtSignal() splitFinished = pyqtSignal() readyChanged = pyqtSignal() createError = pyqtSignal([str], arguments=["error"]) @@ -41,8 +40,6 @@ class QEWalletDB(QObject): self._needsHWDevice = False self._password = '' self._requiresSplit = False - self._requiresUpgrade = False - self._upgrading = False self._invalidPassword = False self._storage = None @@ -115,14 +112,6 @@ class QEWalletDB(QObject): def requiresSplit(self): return self._requiresSplit - @pyqtProperty(bool, notify=requiresUpgradeChanged) - def requiresUpgrade(self): - return self._requiresUpgrade - - @pyqtProperty(bool, notify=upgradingChanged) - def upgrading(self): - return self._upgrading - @pyqtProperty(bool, notify=invalidPasswordChanged) def invalidPassword(self): return self._invalidPassword @@ -142,23 +131,6 @@ class QEWalletDB(QObject): self.splitFinished.emit() - @pyqtSlot() - def doUpgrade(self): - self._logger.warning('doUpgrade') - if not self._requiresUpgrade: - return - - self._logger.warning('upgrading') - - self._upgrading = True - self.upgradingChanged.emit() - - self._db.upgrade() - self._db.write(self._storage) - - self._upgrading = False - self.upgradingChanged.emit() - def load_storage(self): self._storage = WalletStorage(self._path) if not self._storage.file_exists(): @@ -188,15 +160,15 @@ class QEWalletDB(QObject): self._requiresSplit = True self.requiresSplitChanged.emit() return - if self._db.requires_upgrade(): - self._logger.warning('requires upgrade') - self._requiresUpgrade = True - self.requiresUpgradeChanged.emit() - return if self._db.get_action(): self._logger.warning('action pending. QML version doesn\'t support continuation of wizard') return + if self._db.requires_upgrade(): + self._logger.warning('wallet requires upgrade, upgrading') + self._db.upgrade() + self._db.write(self._storage) + self._ready = True self.readyChanged.emit() @@ -212,7 +184,15 @@ class QEWalletDB(QObject): raise Exception('file already exists at path') storage = WalletStorage(path) - k = keystore.from_seed(data['seed'], data['seed_extra_words'], data['wallet_type'] == 'multisig') + if data['seed_type'] in ['old', 'standard', 'segwit']: #2fa, 2fa-segwit + self._logger.debug('creating keystore from electrum seed') + k = keystore.from_seed(data['seed'], data['seed_extra_words'], data['wallet_type'] == 'multisig') + elif data['seed_type'] == 'bip39': + self._logger.debug('creating keystore from bip39 seed') + root_seed = keystore.bip39_to_seed(data['seed'], data['seed_extra_words']) + derivation = normalize_bip32_derivation(data['derivation_path']) + script = data['script_type'] if data['script_type'] != 'p2pkh' else 'standard' + k = keystore.from_bip43_rootseed(root_seed, derivation, xtype=script) if data['encrypt']: storage.set_password(data['password'], enc_version=StorageEncryptionVersion.USER_PASSWORD)