electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 947af921268341251e1a8d521905e1f7e3a96f19
parent 61ccc1ccd3a437d98084089f1d4014ba46c96e3b
Author: ghost43 <somber.night@protonmail.com>
Date:   Mon,  8 Jun 2020 14:24:41 +0000

tx dialog: show various warnings if input amounts cannot be verified (#6217)

see #5749
Diffstat:
Melectrum/gui/kivy/uix/dialogs/tx_dialog.py | 9+++++----
Melectrum/gui/qt/transaction_dialog.py | 16+++++++---------
Melectrum/transaction.py | 19-------------------
Melectrum/wallet.py | 34++++++++++++++++++++++++++++++++++
4 files changed, 46 insertions(+), 32 deletions(-)

diff --git a/electrum/gui/kivy/uix/dialogs/tx_dialog.py b/electrum/gui/kivy/uix/dialogs/tx_dialog.py @@ -132,9 +132,10 @@ class TxDialog(Factory.Popup): self.tx = tx # type: Transaction self._action_button_fn = lambda btn: None - # if the wallet can populate the inputs with more info, do it now. - # as a result, e.g. we might learn an imported address tx is segwit, - # or that a beyond-gap-limit address is is_mine + # If the wallet can populate the inputs with more info, do it now. + # As a result, e.g. we might learn an imported address tx is segwit, + # or that a beyond-gap-limit address is is_mine. + # note: this might fetch prev txs over the network. tx.add_info_from_wallet(self.wallet) def on_open(self): @@ -173,7 +174,7 @@ class TxDialog(Factory.Popup): risk_of_burning_coins = (isinstance(self.tx, PartialTransaction) and self.can_sign and fee is not None - and self.tx.is_there_risk_of_burning_coins_as_fees()) + and bool(self.wallet.get_warning_for_risk_of_burning_coins_as_fees(self.tx))) if fee is not None and not risk_of_burning_coins: self.fee_str = format_amount(fee) fee_per_kb = fee / self.tx.estimated_size() * 1000 diff --git a/electrum/gui/qt/transaction_dialog.py b/electrum/gui/qt/transaction_dialog.py @@ -211,9 +211,10 @@ class BaseTxDialog(QDialog, MessageBoxMixin): self.tx.deserialize() except BaseException as e: raise SerializationError(e) - # if the wallet can populate the inputs with more info, do it now. - # as a result, e.g. we might learn an imported address tx is segwit, - # or that a beyond-gap-limit address is is_mine + # If the wallet can populate the inputs with more info, do it now. + # As a result, e.g. we might learn an imported address tx is segwit, + # or that a beyond-gap-limit address is is_mine. + # note: this might fetch prev txs over the network. tx.add_info_from_wallet(self.wallet) def do_broadcast(self): @@ -479,8 +480,9 @@ class BaseTxDialog(QDialog, MessageBoxMixin): fee_str += ' - ' + _('Warning') + ': ' + _("high fee") + '!' if isinstance(self.tx, PartialTransaction): risk_of_burning_coins = (can_sign and fee is not None - and self.tx.is_there_risk_of_burning_coins_as_fees()) - self.fee_warning_icon.setVisible(risk_of_burning_coins) + and self.wallet.get_warning_for_risk_of_burning_coins_as_fees(self.tx)) + self.fee_warning_icon.setToolTip(str(risk_of_burning_coins)) + self.fee_warning_icon.setVisible(bool(risk_of_burning_coins)) self.fee_label.setText(fee_str) self.size_label.setText(size_str) if ln_amount is None: @@ -596,10 +598,6 @@ class BaseTxDialog(QDialog, MessageBoxMixin): pixmap_size = round(2 * char_width_in_lineedit()) pixmap = pixmap.scaled(pixmap_size, pixmap_size, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.fee_warning_icon.setPixmap(pixmap) - self.fee_warning_icon.setToolTip(_("Warning") + ": " - + _("The fee could not be verified. Signing non-segwit inputs is risky:\n" - "if this transaction was maliciously modified before you sign,\n" - "you might end up paying a higher mining fee than displayed.")) self.fee_warning_icon.setVisible(False) fee_hbox.addWidget(self.fee_warning_icon) fee_hbox.addStretch(1) diff --git a/electrum/transaction.py b/electrum/transaction.py @@ -1953,25 +1953,6 @@ class PartialTransaction(Transaction): for txin in self.inputs(): txin.convert_utxo_to_witness_utxo() - def is_there_risk_of_burning_coins_as_fees(self) -> bool: - """Returns whether there is risk of burning coins as fees if we sign. - - Note: - - legacy sighash does not commit to any input amounts - - BIP-0143 sighash only commits to the *corresponding* input amount - - BIP-taproot sighash commits to *all* input amounts - """ - for txin in self.inputs(): - # if we have full previous tx, we *know* the input amount - if txin.utxo: - continue - # if we have just the previous output, we only have guarantees if - # the sighash commits to this data - if txin.witness_utxo and Transaction.is_segwit_input(txin): - continue - return True - return False - def remove_signatures(self): for txin in self.inputs(): txin.part_sigs = {} diff --git a/electrum/wallet.py b/electrum/wallet.py @@ -2023,6 +2023,40 @@ class Abstract_Wallet(AddressSynchronizer, ABC): self.sign_transaction(tx, password) return tx + def get_warning_for_risk_of_burning_coins_as_fees(self, tx: 'PartialTransaction') -> Optional[str]: + """Returns a warning message if there is risk of burning coins as fees if we sign. + Note that if not all inputs are ismine, e.g. coinjoin, the risk is not just about fees. + + Note: + - legacy sighash does not commit to any input amounts + - BIP-0143 sighash only commits to the *corresponding* input amount + - BIP-taproot sighash commits to *all* input amounts + """ + assert isinstance(tx, PartialTransaction) + # if we have all full previous txs, we *know* all the input amounts -> fine + if all([txin.utxo for txin in tx.inputs()]): + return None + # a single segwit input -> fine + if len(tx.inputs()) == 1 and Transaction.is_segwit_input(tx.inputs()[0]) and tx.inputs()[0].witness_utxo: + return None + # coinjoin or similar + if any([not self.is_mine(txin.address) for txin in tx.inputs()]): + return (_("Warning") + ": " + + _("The input amounts could not be verified as the previous transactions are missing.\n" + "The amount of money being spent CANNOT be verified.")) + # some inputs are legacy + if any([not Transaction.is_segwit_input(txin) for txin in tx.inputs()]): + return (_("Warning") + ": " + + _("The fee could not be verified. Signing non-segwit inputs is risky:\n" + "if this transaction was maliciously modified before you sign,\n" + "you might end up paying a higher mining fee than displayed.")) + # all inputs are segwit + # https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-August/014843.html + return (_("Warning") + ": " + + _("If you received this transaction from an untrusted device, " + "do not accept to sign it more than once,\n" + "otherwise you could end up paying a different fee.")) + class Simple_Wallet(Abstract_Wallet): # wallet with a single keystore