Browse Source

qml: implement initial wallet from master key

patch-4
Sander van Grieken 3 years ago
parent
commit
008805d97c
  1. 18
      electrum/gui/qml/components/NewWalletWizard.qml
  2. 86
      electrum/gui/qml/components/wizard/WCHaveMasterKey.qml
  3. 2
      electrum/gui/qml/components/wizard/WCKeystoreType.qml
  4. 45
      electrum/gui/qml/qebitcoin.py
  5. 39
      electrum/gui/qml/qewalletdb.py

18
electrum/gui/qml/components/NewWalletWizard.qml

@ -47,8 +47,12 @@ Wizard {
if (wizard_data['seed_type'] != 'bip39' && Daemon.singlePassword)
page.last = true
break
// case 'masterkey'
// case 'hardware'
case 'masterkey':
page = _loadNextComponent(components.havemasterkey, wizard_data)
page.next.connect(function() {havemasterkeyDone()})
if (Daemon.singlePassword)
page.last = true
break
}
}
@ -87,6 +91,12 @@ Wizard {
page.last = true
}
function havemasterkeyDone(d) {
console.log('have master key done')
var page = _loadNextComponent(components.walletpassword, wizard_data)
page.last = true
}
Item {
id: components
property Component walletname: Component {
@ -117,6 +127,10 @@ Wizard {
WCBIP39Refine {}
}
property Component havemasterkey: Component {
WCHaveMasterKey {}
}
property Component walletpassword: Component {
WCWalletPassword {}
}

86
electrum/gui/qml/components/wizard/WCHaveMasterKey.qml

@ -0,0 +1,86 @@
import QtQuick 2.6
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.1
import org.electrum 1.0
import "../controls"
WizardComponent {
id: root
valid: false
onAccept: {
wizard_data['master_key'] = masterkey_ta.text
}
function verifyMasterKey(key) {
return valid = bitcoin.verify_master_key(key)
}
ColumnLayout {
width: parent.width
Label { text: qsTr('Create keystore from a master key') }
RowLayout {
TextArea {
id: masterkey_ta
Layout.fillWidth: true
Layout.minimumHeight: 80
focus: true
wrapMode: TextEdit.WrapAnywhere
onTextChanged: verifyMasterKey(text)
}
ColumnLayout {
ToolButton {
icon.source: '../../../icons/paste.png'
icon.height: constants.iconSizeMedium
icon.width: constants.iconSizeMedium
onClicked: {
if (verifyMasterKey(AppController.clipboardToText()))
masterkey_ta.text = AppController.clipboardToText()
}
}
ToolButton {
icon.source: '../../../icons/qrcode.png'
icon.height: constants.iconSizeMedium
icon.width: constants.iconSizeMedium
scale: 1.2
onClicked: {
var scan = qrscan.createObject(root)
scan.onFound.connect(function() {
if (verifyMasterKey(scan.scanData))
masterkey_ta.text = scan.scanData
scan.destroy()
})
}
}
}
}
}
Component {
id: qrscan
QRScan {
width: root.width
height: root.height
ToolButton {
icon.source: '../../../icons/closebutton.png'
icon.height: constants.iconSizeMedium
icon.width: constants.iconSizeMedium
anchors.right: parent.right
anchors.top: parent.top
onClicked: {
parent.destroy()
}
}
}
}
Bitcoin {
id: bitcoin
}
}

2
electrum/gui/qml/components/wizard/WCKeystoreType.qml

@ -27,13 +27,13 @@ WizardComponent {
text: qsTr('I already have a seed')
}
RadioButton {
enabled: false
ButtonGroup.group: keystoregroup
property string keystoretype: 'masterkey'
text: qsTr('Use a master key')
}
RadioButton {
enabled: false
visible: false
ButtonGroup.group: keystoregroup
property string keystoretype: 'hardware'
text: qsTr('Use a hardware device')

45
electrum/gui/qml/qebitcoin.py

@ -3,8 +3,9 @@ import asyncio
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum import mnemonic
from electrum.bip32 import is_bip32_derivation
from electrum.keystore import bip39_is_checksum_valid
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 parse_URI, create_bip21_uri, InvalidBitcoinURI, get_asyncio_loop
@ -46,6 +47,12 @@ class QEBitcoin(QObject):
def validation_message(self):
return self.validationMessage
@validation_message.setter
def validation_message(self, msg):
if self.validationMessage != msg:
self.validationMessage = msg
self.validationMessageChanged.emit()
@pyqtSlot()
@pyqtSlot(str)
@pyqtSlot(str,str)
@ -68,16 +75,16 @@ class QEBitcoin(QObject):
seed_type = ''
seed_valid = False
validation_message = ''
self.validationMessage = ''
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)
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
self.validationMessage = 'BIP39 (%s)' % status
if is_checksum:
seed_type = 'bip39'
@ -87,9 +94,9 @@ class QEBitcoin(QObject):
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)
self.validationMessage = '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)
self.validationMessage = 'SLIP39: %s' % str(e)
seed_valid = False # for now
# cosigning seed
@ -100,16 +107,32 @@ class QEBitcoin(QObject):
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))
@pyqtSlot(str, result=bool)
@pyqtSlot(str, str, result=bool)
def verify_master_key(self, key, wallet_type='standard'):
self.validationMessage = ''
if not keystore.is_master_key(key):
self.validationMessage = _('Not a master key')
return False
if wallet_type == 'standard':
# validation message?
k = keystore.from_master_key(key)
has_xpub = isinstance(k, keystore.Xpub)
assert has_xpub
t1 = xpub_type(k.xpub)
if t1 not in ['standard', 'p2wpkh', 'p2wpkh-p2sh']:
self.validationMessage = '%s: %s' % (_('Wrong key type'), t1)
return False
return True
return False
@pyqtSlot(str, result=bool)
def verify_derivation_path(self, path):
return is_bip32_derivation(path)

39
electrum/gui/qml/qewalletdb.py

@ -5,7 +5,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum.logging import get_logger
from electrum.storage import WalletStorage, StorageEncryptionVersion
from electrum.wallet_db import WalletDB
from electrum.bip32 import normalize_bip32_derivation
from electrum.bip32 import normalize_bip32_derivation, xpub_type
from electrum.util import InvalidPassword
from electrum import keystore
@ -178,6 +178,8 @@ class QEWalletDB(QObject):
data = js_data.toVariant()
self._logger.debug(str(data))
assert data['wallet_type'] == 'standard' # only standard wallets for now
if single_password_enabled and single_password:
data['encrypt'] = True
data['password'] = single_password
@ -188,17 +190,27 @@ class QEWalletDB(QObject):
raise Exception('file already exists at path')
storage = WalletStorage(path)
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['keystore_type'] in ['createseed', 'haveseed']:
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)
else:
raise Exception('unsupported/unknown seed_type %s' % data['seed_type'])
elif data['keystore_type'] == 'masterkey':
k = keystore.from_master_key(data['master_key'])
has_xpub = isinstance(k, keystore.Xpub)
assert has_xpub
t1 = xpub_type(k.xpub)
if t1 not in ['standard', 'p2wpkh', 'p2wpkh-p2sh']:
raise Exception('wrong key type %s' % t1)
else:
raise Exception('unsupported/unknown seed_type %s' % data['seed_type'])
raise Exception('unsupported/unknown keystore_type %s' % data['keystore_type'])
if data['encrypt']:
if k.may_have_password():
@ -209,7 +221,8 @@ class QEWalletDB(QObject):
db.set_keystore_encryption(bool(data['password']) and data['encrypt'])
db.put('wallet_type', data['wallet_type'])
db.put('seed_type', data['seed_type'])
if 'seed_type' in data:
db.put('seed_type', data['seed_type'])
db.put('keystore', k.dump())
if k.can_have_deterministic_lightning_xprv():
db.put('lightning_xprv', k.get_lightning_xprv(data['password'] if data['encrypt'] else None))
@ -223,5 +236,5 @@ class QEWalletDB(QObject):
self.createSuccess.emit()
except Exception as e:
self._logger.error(str(e))
self._logger.error(repr(e))
self.createError.emit(str(e))

Loading…
Cancel
Save