qml: consolidate multisig support into existing pages WCHaveSeed, WCHaveMasterKey, WCBIP39Refine.
Have these put cosigner data directly in the correct wizard_data leafs instead of relying on wizard accept handlers.
This commit is contained in:
@@ -9,11 +9,18 @@ import "../controls"
|
||||
WizardComponent {
|
||||
valid: false
|
||||
|
||||
property bool isMultisig
|
||||
property bool isMultisig: false
|
||||
property int cosigner: 0
|
||||
property int participants: 0
|
||||
|
||||
function apply() {
|
||||
wizard_data['script_type'] = scripttypegroup.checkedButton.scripttype
|
||||
wizard_data['derivation_path'] = derivationpathtext.text
|
||||
if (cosigner) {
|
||||
wizard_data['multisig_cosigner_data'][cosigner.toString()]['script_type'] = scripttypegroup.checkedButton.scripttype
|
||||
wizard_data['multisig_cosigner_data'][cosigner.toString()]['derivation_path'] = derivationpathtext.text
|
||||
} else {
|
||||
wizard_data['script_type'] = scripttypegroup.checkedButton.scripttype
|
||||
wizard_data['derivation_path'] = derivationpathtext.text
|
||||
}
|
||||
}
|
||||
|
||||
function getScriptTypePurposeDict() {
|
||||
@@ -37,7 +44,7 @@ WizardComponent {
|
||||
var p = isMultisig ? getMultisigScriptTypePurposeDict() : getScriptTypePurposeDict()
|
||||
if (!scripttypegroup.checkedButton.scripttype in p)
|
||||
return
|
||||
if (!bitcoin.verify_derivation_path(derivationpathtext.text))
|
||||
if (!bitcoin.verifyDerivationPath(derivationpathtext.text))
|
||||
return
|
||||
valid = true
|
||||
}
|
||||
@@ -76,13 +83,20 @@ WizardComponent {
|
||||
id: mainLayout
|
||||
width: parent.width
|
||||
|
||||
Label { text: qsTr('Script type and Derivation path') }
|
||||
Label {
|
||||
text: qsTr('Script type and Derivation path')
|
||||
}
|
||||
Button {
|
||||
text: qsTr('Detect Existing Accounts')
|
||||
enabled: false
|
||||
visible: !isMultisig
|
||||
}
|
||||
Label { text: qsTr('Choose the type of addresses in your wallet.') }
|
||||
|
||||
Label {
|
||||
text: qsTr('Choose the type of addresses in your wallet.')
|
||||
}
|
||||
|
||||
// standard
|
||||
RadioButton {
|
||||
ButtonGroup.group: scripttypegroup
|
||||
property string scripttype: 'p2pkh'
|
||||
@@ -102,6 +116,8 @@ WizardComponent {
|
||||
text: qsTr('native segwit (p2wpkh)')
|
||||
visible: !isMultisig
|
||||
}
|
||||
|
||||
// multisig
|
||||
RadioButton {
|
||||
ButtonGroup.group: scripttypegroup
|
||||
property string scripttype: 'p2sh'
|
||||
@@ -121,11 +137,13 @@ WizardComponent {
|
||||
text: qsTr('native segwit multisig (p2wsh)')
|
||||
visible: isMultisig
|
||||
}
|
||||
|
||||
InfoTextArea {
|
||||
Layout.preferredWidth: parent.width
|
||||
text: qsTr('You can override the suggested derivation path.') + ' ' +
|
||||
qsTr('If you are not sure what this is, leave this field unchanged.')
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: derivationpathtext
|
||||
Layout.fillWidth: true
|
||||
@@ -140,7 +158,12 @@ WizardComponent {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
isMultisig = 'multisig' in wizard_data && wizard_data['multisig'] == true
|
||||
isMultisig = wizard_data['wallet_type'] == 'multisig'
|
||||
if (isMultisig) {
|
||||
participants = wizard_data['multisig_participants']
|
||||
if ('multisig_current_cosigner' in wizard_data)
|
||||
cosigner = wizard_data['multisig_current_cosigner']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
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
|
||||
|
||||
ColumnLayout {
|
||||
Label {
|
||||
text: qsTr('TODO: Cosigner key entry')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ WizardComponent {
|
||||
function apply() {
|
||||
wizard_data['cosigner_keystore_type'] = keystoregroup.checkedButton.keystoretype
|
||||
wizard_data['multisig_current_cosigner'] = cosigner
|
||||
wizard_data['multisig_cosigner_data'][cosigner.toString()] = {}
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import QtQuick 2.6
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Controls 2.1
|
||||
|
||||
import org.electrum 1.0
|
||||
|
||||
import "../controls"
|
||||
|
||||
WCHaveSeed {
|
||||
id: root
|
||||
|
||||
headingtext: qsTr('Cosigner #%1 of %2').arg(cosigner).arg(participants)
|
||||
|
||||
property int cosigner: 0
|
||||
property int participants: 0
|
||||
|
||||
function apply() {
|
||||
console.log('apply fn called')
|
||||
wizard_data['cosigner_seed'] = seed
|
||||
wizard_data['cosigner_seed_variant'] = seed_variant
|
||||
wizard_data['cosigner_seed_type'] = seed_type
|
||||
wizard_data['cosigner_seed_extend'] = seed_extend
|
||||
wizard_data['cosigner_seed_extra_words'] = seed_extra_words
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
participants = wizard_data['multisig_participants']
|
||||
cosigner = wizard_data['multisig_current_cosigner']
|
||||
}
|
||||
}
|
||||
@@ -11,18 +11,32 @@ WizardComponent {
|
||||
|
||||
valid: false
|
||||
|
||||
property int cosigner: 0
|
||||
property int participants: 0
|
||||
|
||||
function apply() {
|
||||
wizard_data['master_key'] = masterkey_ta.text
|
||||
if (cosigner) {
|
||||
wizard_data['multisig_cosigner_data'][cosigner.toString()]['master_key'] = masterkey_ta.text
|
||||
} else {
|
||||
wizard_data['master_key'] = masterkey_ta.text
|
||||
}
|
||||
}
|
||||
|
||||
function verifyMasterKey(key) {
|
||||
return valid = bitcoin.verify_master_key(key)
|
||||
return valid = bitcoin.verifyMasterKey(key.trim(), wizard_data['wallet_type'])
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
|
||||
Label { text: qsTr('Create keystore from a master key') }
|
||||
Label {
|
||||
text: qsTr('Cosigner #%1 of %2').arg(cosigner).arg(participants)
|
||||
visible: cosigner
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr('Create keystore from a master key')
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
TextArea {
|
||||
@@ -59,6 +73,18 @@ WizardComponent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: validationtext
|
||||
text: bitcoin.validationMessage
|
||||
visible: bitcoin.validationMessage
|
||||
Layout.fillWidth: true
|
||||
readOnly: true
|
||||
wrapMode: TextInput.WordWrap
|
||||
background: Rectangle {
|
||||
color: 'transparent'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
@@ -83,4 +109,12 @@ WizardComponent {
|
||||
Bitcoin {
|
||||
id: bitcoin
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (wizard_data['wallet_type'] == 'multisig') {
|
||||
if ('multisig_current_cosigner' in wizard_data)
|
||||
cosigner = wizard_data['multisig_current_cosigner']
|
||||
participants = wizard_data['multisig_participants']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,26 +9,27 @@ import "../controls"
|
||||
|
||||
WizardComponent {
|
||||
id: root
|
||||
|
||||
valid: false
|
||||
|
||||
property bool is2fa: false
|
||||
|
||||
property string headingtext
|
||||
|
||||
// expose for WCCosignerSeed 'subclass'
|
||||
property alias seed: seedtext.text
|
||||
property alias seed_variant: seed_variant_cb.currentValue
|
||||
property alias seed_type: bitcoin.seed_type
|
||||
property alias seed_extend: extendcb.checked
|
||||
property string seed_extra_words: extendcb.checked ? customwordstext.text : ''
|
||||
property int cosigner: 0
|
||||
property int participants: 0
|
||||
|
||||
function apply() {
|
||||
console.log('apply fn called (WCHaveSeed)')
|
||||
wizard_data['seed'] = seedtext.text
|
||||
wizard_data['seed_variant'] = seed_variant_cb.currentValue
|
||||
wizard_data['seed_type'] = bitcoin.seed_type
|
||||
wizard_data['seed_extend'] = extendcb.checked
|
||||
wizard_data['seed_extra_words'] = extendcb.checked ? customwordstext.text : ''
|
||||
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.seed_type
|
||||
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.seed_type
|
||||
wizard_data['seed_extend'] = extendcb.checked
|
||||
wizard_data['seed_extra_words'] = extendcb.checked ? customwordstext.text : ''
|
||||
}
|
||||
}
|
||||
|
||||
function setSeedTypeHelpText() {
|
||||
@@ -69,8 +70,8 @@ WizardComponent {
|
||||
|
||||
Label {
|
||||
Layout.columnSpan: 2
|
||||
visible: headingtext
|
||||
text: headingtext
|
||||
text: qsTr('Cosigner #%1 of %2').arg(cosigner).arg(participants)
|
||||
visible: cosigner
|
||||
}
|
||||
|
||||
Label {
|
||||
@@ -129,7 +130,8 @@ WizardComponent {
|
||||
}
|
||||
TextArea {
|
||||
id: validationtext
|
||||
visible: text != ''
|
||||
text: bitcoin.validationMessage
|
||||
visible: bitcoin.validationMessage
|
||||
Layout.fillWidth: true
|
||||
readOnly: true
|
||||
wrapMode: TextInput.WordWrap
|
||||
@@ -157,7 +159,6 @@ WizardComponent {
|
||||
id: bitcoin
|
||||
onSeedTypeChanged: contentText.text = bitcoin.seed_type
|
||||
onSeedValidChanged: root.valid = bitcoin.seed_valid
|
||||
onValidationMessageChanged: validationtext.text = bitcoin.validation_message
|
||||
}
|
||||
|
||||
Timer {
|
||||
@@ -168,8 +169,13 @@ WizardComponent {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (wizard_data['wallet_type'] == '2fa')
|
||||
root.is2fa = true
|
||||
if (wizard_data['wallet_type'] == '2fa') {
|
||||
is2fa = true
|
||||
} else if (wizard_data['wallet_type'] == 'multisig') {
|
||||
participants = wizard_data['multisig_participants']
|
||||
if ('multisig_current_cosigner' in wizard_data)
|
||||
cosigner = wizard_data['multisig_current_cosigner']
|
||||
}
|
||||
setSeedTypeHelpText()
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ WizardComponent {
|
||||
}
|
||||
|
||||
function apply() {
|
||||
wizard_data['multisig'] = true
|
||||
wizard_data['multisig_participants'] = participants
|
||||
wizard_data['multisig_signatures'] = signatures
|
||||
wizard_data['multisig_cosigner_data'] = {}
|
||||
|
||||
@@ -14,14 +14,21 @@ Item {
|
||||
apply()
|
||||
}
|
||||
|
||||
// override this in descendants to put data from the view in wizard_data
|
||||
function apply() { }
|
||||
|
||||
function checkIsLast() {
|
||||
apply()
|
||||
last = wizard.wiz.isLast(wizard_data)
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
checkIsLast()
|
||||
// NOTE: Use Qt.callLater to execute checkIsLast(), and by extension apply(),
|
||||
// otherwise Component.onCompleted handler in descendants is processed
|
||||
// _after_ apply() is called, which may lead to setting the wrong
|
||||
// wizard_data keys if apply() depends on variables set in descendant
|
||||
// Component.onCompleted handler.
|
||||
Qt.callLater(checkIsLast)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class QEBitcoin(QObject):
|
||||
seedType = ''
|
||||
|
||||
validationMessageChanged = pyqtSignal()
|
||||
validationMessage = ''
|
||||
_validationMessage = ''
|
||||
|
||||
@pyqtProperty('QString', notify=generatedSeedChanged)
|
||||
def generated_seed(self):
|
||||
@@ -46,13 +46,13 @@ class QEBitcoin(QObject):
|
||||
return self.seedType
|
||||
|
||||
@pyqtProperty('QString', notify=validationMessageChanged)
|
||||
def validation_message(self):
|
||||
return self.validationMessage
|
||||
def validationMessage(self):
|
||||
return self._validationMessage
|
||||
|
||||
@validation_message.setter
|
||||
def validation_message(self, msg):
|
||||
if self.validationMessage != msg:
|
||||
self.validationMessage = msg
|
||||
@validationMessage.setter
|
||||
def validationMessage(self, msg):
|
||||
if self._validationMessage != msg:
|
||||
self._validationMessage = msg
|
||||
self.validationMessageChanged.emit()
|
||||
|
||||
@pyqtSlot()
|
||||
@@ -115,28 +115,34 @@ class QEBitcoin(QObject):
|
||||
|
||||
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'):
|
||||
def verifyMasterKey(self, key, wallet_type='standard'):
|
||||
self.validationMessage = ''
|
||||
if not keystore.is_master_key(key):
|
||||
self.validationMessage = _('Not a master key')
|
||||
return False
|
||||
|
||||
k = keystore.from_master_key(key)
|
||||
has_xpub = isinstance(k, keystore.Xpub)
|
||||
assert has_xpub
|
||||
t1 = xpub_type(k.xpub)
|
||||
|
||||
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
|
||||
elif wallet_type == 'multisig':
|
||||
if t1 not in ['standard', 'p2wsh', 'p2wsh-p2sh']:
|
||||
self.validationMessage = '%s: %s' % (_('Wrong key type'), t1)
|
||||
return False
|
||||
# TODO: check against other cosigner xpubs
|
||||
return True
|
||||
|
||||
raise Exception(f'Unsupported wallet type: {wallet_type}')
|
||||
|
||||
@pyqtSlot(str, result=bool)
|
||||
def verify_derivation_path(self, path):
|
||||
def verifyDerivationPath(self, path):
|
||||
return is_bip32_derivation(path)
|
||||
|
||||
@pyqtSlot(str, result='QVariantMap')
|
||||
|
||||
@@ -62,13 +62,9 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
|
||||
'multisig': { 'gui': 'WCMultisig' },
|
||||
'multisig_show_masterpubkey': { 'gui': 'WCShowMasterPubkey' },
|
||||
'multisig_cosigner_keystore': { 'gui': 'WCCosignerKeystore' },
|
||||
'multisig_cosigner_key': { 'gui': 'WCCosignerKey' },
|
||||
'multisig_cosigner_seed': { 'gui': 'WCCosignerSeed',
|
||||
'accept': self.accept_cosigner_seed
|
||||
},
|
||||
'multisig_cosigner_bip39_refine': { 'gui': 'WCBIP39Refine',
|
||||
'accept': self.accept_cosigner_bip39refine
|
||||
},
|
||||
'multisig_cosigner_key': { 'gui': 'WCHaveMasterKey' },
|
||||
'multisig_cosigner_seed': { 'gui': 'WCHaveSeed' },
|
||||
'multisig_cosigner_bip39_refine': { 'gui': 'WCBIP39Refine' },
|
||||
'imported': { 'gui': 'WCImport' },
|
||||
'wallet_password': { 'gui': 'WCWalletPassword' }
|
||||
})
|
||||
@@ -86,21 +82,6 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
|
||||
def is_single_password(self):
|
||||
return self._daemon.singlePasswordEnabled
|
||||
|
||||
def accept_cosigner_seed(self, wizard_data):
|
||||
self._logger.debug('accept_cosigner_seed')
|
||||
cosigner = wizard_data['multisig_current_cosigner'] if 'multisig_current_cosigner' in wizard_data else 2
|
||||
wizard_data['multisig_cosigner_data'][str(cosigner)] = {
|
||||
'seed': wizard_data['cosigner_seed'],
|
||||
'seed_variant': wizard_data['cosigner_seed_variant'],
|
||||
'seed_type': wizard_data['cosigner_seed_type'],
|
||||
'seed_extend': wizard_data['cosigner_seed_extend'],
|
||||
'seed_extra_words': wizard_data['cosigner_seed_extra_words']
|
||||
}
|
||||
|
||||
def accept_cosigner_bip39refine(self, wizard_data):
|
||||
pass # TODO
|
||||
|
||||
|
||||
@pyqtSlot('QJSValue', bool, str)
|
||||
def createStorage(self, js_data, single_password_enabled, single_password):
|
||||
self._logger.info('Creating wallet from wizard data')
|
||||
|
||||
@@ -169,15 +169,19 @@ class NewWalletWizard(AbstractWizard):
|
||||
'multisig_show_masterpubkey': {
|
||||
'next': 'multisig_cosigner_keystore'
|
||||
},
|
||||
'multisig_cosigner_keystore': {
|
||||
'multisig_cosigner_keystore': { # this view should set 'multisig_current_cosigner'
|
||||
'next': self.on_cosigner_keystore_type
|
||||
},
|
||||
'multisig_cosigner_key': {
|
||||
'next': lambda d: 'multisig_cosigner_keystore' if self.has_all_cosigner_data(d) else 'wallet_password',
|
||||
'next': lambda d: 'wallet_password' if self.has_all_cosigner_data(d) else 'multisig_cosigner_keystore',
|
||||
'last': lambda v,d: self.is_single_password() and self.has_all_cosigner_data(d)
|
||||
},
|
||||
'multisig_cosigner_seed': {
|
||||
'next': lambda d: 'multisig_cosigner_keystore' if self.has_all_cosigner_data(d) else 'wallet_password',
|
||||
'next': self.on_have_cosigner_seed,
|
||||
'last': lambda v,d: self.is_single_password() and self.has_all_cosigner_data(d)
|
||||
},
|
||||
'multisig_cosigner_bip39_refine': {
|
||||
'next': lambda d: 'wallet_password' if self.has_all_cosigner_data(d) else 'multisig_cosigner_keystore',
|
||||
'last': lambda v,d: self.is_single_password() and self.has_all_cosigner_data(d)
|
||||
},
|
||||
'imported': {
|
||||
@@ -202,7 +206,7 @@ class NewWalletWizard(AbstractWizard):
|
||||
return wizard_data['seed_variant'] == 'bip39'
|
||||
|
||||
def is_multisig(self, wizard_data):
|
||||
return 'multisig' in wizard_data and wizard_data['multisig'] is True
|
||||
return wizard_data['wallet_type'] == 'multisig'
|
||||
|
||||
def on_wallet_type(self, wizard_data):
|
||||
t = wizard_data['wallet_type']
|
||||
@@ -236,8 +240,26 @@ class NewWalletWizard(AbstractWizard):
|
||||
'seed': 'multisig_cosigner_seed'
|
||||
}.get(t)
|
||||
|
||||
def on_have_cosigner_seed(self, wizard_data):
|
||||
current_cosigner_data = wizard_data['multisig_cosigner_data'][str(wizard_data['multisig_current_cosigner'])]
|
||||
if self.has_all_cosigner_data(wizard_data):
|
||||
return 'wallet_password'
|
||||
elif current_cosigner_data['seed_type'] == 'bip39' and 'derivation_path' not in current_cosigner_data:
|
||||
return 'multisig_cosigner_bip39_refine'
|
||||
else:
|
||||
return 'multisig_cosigner_keystore'
|
||||
|
||||
def has_all_cosigner_data(self, wizard_data):
|
||||
return len(wizard_data['multisig_cosigner_data']) < (wizard_data['multisig_participants'] - 1)
|
||||
# number of items in multisig_cosigner_data is less than participants?
|
||||
if len(wizard_data['multisig_cosigner_data']) < (wizard_data['multisig_participants'] - 1):
|
||||
return False
|
||||
|
||||
# if last cosigner uses bip39 seed, we still need derivation path
|
||||
current_cosigner_data = wizard_data['multisig_cosigner_data'][str(wizard_data['multisig_current_cosigner'])]
|
||||
if current_cosigner_data['seed_type'] == 'bip39' and 'derivation_path' not in current_cosigner_data:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def finished(self, wizard_data):
|
||||
self._logger.debug('finished')
|
||||
|
||||
Reference in New Issue
Block a user