Merge pull request #9279 from accumulator/qt_wizard_musig_user_feedback
qt: wizard: implement user feedback TODOs, consistently use wizard.check_multisig_constraints
This commit is contained in:
@@ -1259,10 +1259,12 @@ def read_QIcon_from_bytes(b: bytes) -> QIcon:
|
||||
qp = read_QPixmap_from_bytes(b)
|
||||
return QIcon(qp)
|
||||
|
||||
|
||||
class IconLabel(QWidget):
|
||||
HorizontalSpacing = 2
|
||||
def __init__(self, *, text='', final_stretch=True):
|
||||
def __init__(self, *, text='', final_stretch=True, reverse=False, hide_if_empty=False):
|
||||
super(QWidget, self).__init__()
|
||||
self.hide_if_empty = hide_if_empty
|
||||
size = max(16, font_height())
|
||||
self.icon_size = QSize(size, size)
|
||||
layout = QHBoxLayout()
|
||||
@@ -1271,13 +1273,18 @@ class IconLabel(QWidget):
|
||||
self.icon = QLabel()
|
||||
self.label = QLabel(text)
|
||||
self.label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
|
||||
layout.addWidget(self.label)
|
||||
layout.addWidget(self.icon if reverse else self.label)
|
||||
layout.addSpacing(self.HorizontalSpacing)
|
||||
layout.addWidget(self.icon)
|
||||
layout.addWidget(self.label if reverse else self.icon)
|
||||
if final_stretch:
|
||||
layout.addStretch()
|
||||
self.setText(text)
|
||||
|
||||
def setText(self, text):
|
||||
self.label.setText(text)
|
||||
if self.hide_if_empty:
|
||||
self.setVisible(bool(text))
|
||||
|
||||
def setIcon(self, icon):
|
||||
self.icon.setPixmap(icon.pixmap(self.icon_size))
|
||||
self.icon.repaint() # macOS hack for #6269
|
||||
|
||||
@@ -29,7 +29,7 @@ from electrum.gui.qt.bip39_recovery_dialog import Bip39RecoveryDialog
|
||||
from electrum.gui.qt.password_dialog import PasswordLayout, PW_NEW, MSG_ENTER_PASSWORD, PasswordLayoutForHW
|
||||
from electrum.gui.qt.seed_dialog import SeedWidget, MSG_PASSPHRASE_WARN_ISSUE4566, KeysWidget
|
||||
from electrum.gui.qt.util import (PasswordLineEdit, char_width_in_lineedit, WWLabel, InfoButton, font_height,
|
||||
ChoiceWidget, MessageBoxMixin, icon_path)
|
||||
ChoiceWidget, MessageBoxMixin, icon_path, IconLabel, read_QIcon)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from electrum.simple_config import SimpleConfig
|
||||
@@ -450,7 +450,6 @@ class WCCreateSeed(WalletWizardComponent):
|
||||
self.wizard_data['seed_type'] = self.seed_type
|
||||
self.wizard_data['seed_extend'] = self.seed_widget.is_ext
|
||||
self.wizard_data['seed_variant'] = 'electrum'
|
||||
self.wizard_data['seed_extra_words'] = '' # empty default
|
||||
|
||||
def create_seed(self):
|
||||
self.busy = True
|
||||
@@ -516,6 +515,12 @@ class WCEnterExt(WalletWizardComponent, Logger):
|
||||
self.ext_edit.textEdited.connect(self.on_text_edited)
|
||||
self.layout().addWidget(self.ext_edit)
|
||||
self.layout().addStretch(1)
|
||||
self.warn_label = IconLabel(reverse=True, hide_if_empty=True)
|
||||
self.warn_label.setIcon(read_QIcon('warning.png'))
|
||||
self.layout().addWidget(self.warn_label)
|
||||
|
||||
def on_ready(self):
|
||||
self.validate()
|
||||
|
||||
def on_text_edited(self, text):
|
||||
# TODO also for cosigners?
|
||||
@@ -525,30 +530,10 @@ class WCEnterExt(WalletWizardComponent, Logger):
|
||||
|
||||
def validate(self):
|
||||
self.apply()
|
||||
text = self.ext_edit.text()
|
||||
if len(text) == 0:
|
||||
self.valid = False
|
||||
return
|
||||
|
||||
cosigner_data = self.wizard.current_cosigner(self.wizard_data)
|
||||
|
||||
if self.wizard_data['wallet_type'] == 'multisig':
|
||||
if 'seed_variant' in cosigner_data and cosigner_data['seed_variant'] in ['bip39', 'slip39']:
|
||||
# defer validation to when derivation path is known
|
||||
self.valid = True
|
||||
else:
|
||||
if self.wizard.has_duplicate_masterkeys(self.wizard_data):
|
||||
self.logger.debug('Duplicate master keys!')
|
||||
# TODO: user feedback
|
||||
self.valid = False
|
||||
elif self.wizard.has_heterogeneous_masterkeys(self.wizard_data):
|
||||
self.logger.debug('Heterogenous master keys!')
|
||||
# TODO: user feedback
|
||||
self.valid = False
|
||||
else:
|
||||
self.valid = True
|
||||
else:
|
||||
self.valid = True
|
||||
musig_valid, errortext = self.wizard.check_multisig_constraints(self.wizard_data)
|
||||
self.valid = musig_valid
|
||||
self.warn_label.setText(errortext)
|
||||
|
||||
def apply(self):
|
||||
cosigner_data = self.wizard.current_cosigner(self.wizard_data)
|
||||
@@ -567,8 +552,14 @@ class WCConfirmExt(WalletWizardComponent):
|
||||
self.layout().addWidget(self.ext_edit)
|
||||
self.layout().addStretch(1)
|
||||
|
||||
def on_text_edited(self, text):
|
||||
self.valid = text == self.wizard_data['seed_extra_words']
|
||||
def on_ready(self):
|
||||
self.validate()
|
||||
|
||||
def on_text_edited(self, *args):
|
||||
self.validate()
|
||||
|
||||
def validate(self):
|
||||
self.valid = self.ext_edit.text() == self.wizard_data['seed_extra_words']
|
||||
|
||||
def apply(self):
|
||||
pass
|
||||
@@ -580,6 +571,8 @@ class WCHaveSeed(WalletWizardComponent, Logger):
|
||||
Logger.__init__(self)
|
||||
|
||||
self.layout().addWidget(WWLabel(_('Please enter your seed phrase in order to restore your wallet.')))
|
||||
self.warn_label = IconLabel(reverse=True, hide_if_empty=True)
|
||||
self.warn_label.setIcon(read_QIcon('warning.png'))
|
||||
|
||||
self.seed_widget = None
|
||||
self.can_passphrase = True
|
||||
@@ -610,6 +603,8 @@ class WCHaveSeed(WalletWizardComponent, Logger):
|
||||
self.layout().addWidget(self.seed_widget)
|
||||
self.layout().addStretch(1)
|
||||
|
||||
self.layout().addWidget(self.warn_label)
|
||||
|
||||
def is_seed(self, x):
|
||||
# really only used for electrum seeds. bip39 and slip39 are validated in SeedWidget
|
||||
t = mnemonic.calc_seed_type(x)
|
||||
@@ -635,10 +630,11 @@ class WCHaveSeed(WalletWizardComponent, Logger):
|
||||
return
|
||||
|
||||
self.apply()
|
||||
if not self.wizard.check_multisig_constraints(self.wizard_data)[0]:
|
||||
# TODO: user feedback
|
||||
musig_valid, errortext = self.wizard.check_multisig_constraints(self.wizard_data)
|
||||
if not musig_valid:
|
||||
seed_valid = False
|
||||
|
||||
self.warn_label.setText(errortext)
|
||||
self.valid = seed_valid
|
||||
|
||||
def apply(self):
|
||||
@@ -651,7 +647,6 @@ class WCHaveSeed(WalletWizardComponent, Logger):
|
||||
else:
|
||||
cosigner_data['seed_type'] = self.seed_widget.seed_type
|
||||
cosigner_data['seed_extend'] = self.seed_widget.is_ext if self.can_passphrase else False
|
||||
cosigner_data['seed_extra_words'] = '' # empty default
|
||||
|
||||
|
||||
class WCScriptAndDerivation(WalletWizardComponent, Logger):
|
||||
@@ -662,6 +657,9 @@ class WCScriptAndDerivation(WalletWizardComponent, Logger):
|
||||
self.choice_w = None
|
||||
self.derivation_path_edit = None
|
||||
|
||||
self.warn_label = IconLabel(reverse=True, hide_if_empty=True)
|
||||
self.warn_label.setIcon(read_QIcon('warning.png'))
|
||||
|
||||
def on_ready(self):
|
||||
message1 = _('Choose the type of addresses in your wallet.')
|
||||
message2 = ' '.join([
|
||||
@@ -736,6 +734,7 @@ class WCScriptAndDerivation(WalletWizardComponent, Logger):
|
||||
on_choice_click(self.choice_w.selected_index) # set default value for derivation path
|
||||
|
||||
self.layout().addStretch(1)
|
||||
self.layout().addWidget(self.warn_label)
|
||||
|
||||
def validate(self):
|
||||
self.apply()
|
||||
@@ -744,10 +743,12 @@ class WCScriptAndDerivation(WalletWizardComponent, Logger):
|
||||
valid = is_bip32_derivation(cosigner_data['derivation_path'])
|
||||
|
||||
if valid:
|
||||
valid, error = self.wizard.check_multisig_constraints(self.wizard_data)
|
||||
valid, errortext = self.wizard.check_multisig_constraints(self.wizard_data)
|
||||
if not valid:
|
||||
# TODO: user feedback
|
||||
self.logger.error(error)
|
||||
self.logger.error(errortext)
|
||||
self.warn_label.setText(errortext)
|
||||
else:
|
||||
self.warn_label.setText(_('Invalid derivation path'))
|
||||
|
||||
self.valid = valid
|
||||
|
||||
@@ -825,6 +826,9 @@ class WCHaveMasterKey(WalletWizardComponent):
|
||||
self.label.setMinimumWidth(400)
|
||||
self.header_layout.addWidget(self.label)
|
||||
|
||||
self.warn_label = IconLabel(reverse=True, hide_if_empty=True)
|
||||
self.warn_label.setIcon(read_QIcon('warning.png'))
|
||||
|
||||
def on_ready(self):
|
||||
if self.wizard_data['wallet_type'] == 'standard':
|
||||
self.label.setText(self.message_create)
|
||||
@@ -840,10 +844,12 @@ class WCHaveMasterKey(WalletWizardComponent):
|
||||
|
||||
def is_valid(x) -> bool:
|
||||
if not keystore.is_bip32_key(x):
|
||||
self.warn_label.setText(_('Invalid key'))
|
||||
return False
|
||||
self.apply()
|
||||
if not self.wizard.check_multisig_constraints(self.wizard_data)[0]:
|
||||
# TODO: user feedback
|
||||
musig_valid, errortext = self.wizard.check_multisig_constraints(self.wizard_data)
|
||||
self.warn_label.setText(errortext)
|
||||
if not musig_valid:
|
||||
return False
|
||||
return True
|
||||
else:
|
||||
@@ -858,6 +864,8 @@ class WCHaveMasterKey(WalletWizardComponent):
|
||||
self.keys_widget.validChanged.connect(key_valid_changed)
|
||||
|
||||
self.layout().addWidget(self.keys_widget)
|
||||
self.layout().addStretch()
|
||||
self.layout().addWidget(self.warn_label)
|
||||
|
||||
def apply(self):
|
||||
text = self.keys_widget.get_text()
|
||||
|
||||
@@ -414,10 +414,11 @@ class NewWalletWizard(AbstractWizard):
|
||||
|
||||
def keystore_from_data(self, wallet_type: str, data: dict):
|
||||
if data['keystore_type'] in ['createseed', 'haveseed'] and 'seed' in data:
|
||||
seed_extension = data['seed_extra_words'] if data['seed_extend'] else ''
|
||||
if data['seed_variant'] == 'electrum':
|
||||
return keystore.from_seed(data['seed'], passphrase=data['seed_extra_words'], for_multisig=True)
|
||||
return keystore.from_seed(data['seed'], passphrase=seed_extension, for_multisig=True)
|
||||
elif data['seed_variant'] == 'bip39':
|
||||
root_seed = keystore.bip39_to_seed(data['seed'], passphrase=data['seed_extra_words'])
|
||||
root_seed = keystore.bip39_to_seed(data['seed'], passphrase=seed_extension)
|
||||
derivation = normalize_bip32_derivation(data['derivation_path'])
|
||||
if wallet_type == 'multisig':
|
||||
script = data['script_type'] if data['script_type'] != 'p2sh' else 'standard'
|
||||
@@ -425,7 +426,7 @@ class NewWalletWizard(AbstractWizard):
|
||||
script = data['script_type'] if data['script_type'] != 'p2pkh' else 'standard'
|
||||
return keystore.from_bip43_rootseed(root_seed, derivation=derivation, xtype=script)
|
||||
elif data['seed_variant'] == 'slip39':
|
||||
root_seed = data['seed'].decrypt(data['seed_extra_words'])
|
||||
root_seed = data['seed'].decrypt(seed_extension)
|
||||
derivation = normalize_bip32_derivation(data['derivation_path'])
|
||||
if wallet_type == 'multisig':
|
||||
script = data['script_type'] if data['script_type'] != 'p2sh' else 'standard'
|
||||
@@ -548,15 +549,16 @@ class NewWalletWizard(AbstractWizard):
|
||||
for addr in data['address_list'].split():
|
||||
addresses[addr] = {}
|
||||
elif data['keystore_type'] in ['createseed', 'haveseed']:
|
||||
seed_extension = data['seed_extra_words'] if data['seed_extend'] else ''
|
||||
if data['seed_type'] in ['old', 'standard', 'segwit']:
|
||||
self._logger.debug('creating keystore from electrum seed')
|
||||
k = keystore.from_seed(data['seed'], passphrase=data['seed_extra_words'], for_multisig=data['wallet_type'] == 'multisig')
|
||||
k = keystore.from_seed(data['seed'], passphrase=seed_extension, for_multisig=data['wallet_type'] == 'multisig')
|
||||
elif data['seed_type'] in ['bip39', 'slip39']:
|
||||
self._logger.debug('creating keystore from %s seed' % data['seed_type'])
|
||||
if data['seed_type'] == 'bip39':
|
||||
root_seed = keystore.bip39_to_seed(data['seed'], passphrase=data['seed_extra_words'])
|
||||
root_seed = keystore.bip39_to_seed(data['seed'], passphrase=seed_extension)
|
||||
else:
|
||||
root_seed = data['seed'].decrypt(data['seed_extra_words'])
|
||||
root_seed = data['seed'].decrypt(seed_extension)
|
||||
derivation = normalize_bip32_derivation(data['derivation_path'])
|
||||
if data['wallet_type'] == 'multisig':
|
||||
script = data['script_type'] if data['script_type'] != 'p2sh' else 'standard'
|
||||
|
||||
Reference in New Issue
Block a user