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:
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