URI: 
       ttx dialog: show various warnings if input amounts cannot be verified (#6217) - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 947af921268341251e1a8d521905e1f7e3a96f19
   DIR parent 61ccc1ccd3a437d98084089f1d4014ba46c96e3b
  HTML Author: ghost43 <somber.night@protonmail.com>
       Date:   Mon,  8 Jun 2020 14:24:41 +0000
       
       ttx dialog: show various warnings if input amounts cannot be verified (#6217)
       
       see #5749
       Diffstat:
         M electrum/gui/kivy/uix/dialogs/tx_d… |       9 +++++----
         M electrum/gui/qt/transaction_dialog… |      16 +++++++---------
         M electrum/transaction.py             |      19 -------------------
         M electrum/wallet.py                  |      34 +++++++++++++++++++++++++++++++
       
       4 files changed, 46 insertions(+), 32 deletions(-)
       ---
   DIR diff --git a/electrum/gui/kivy/uix/dialogs/tx_dialog.py b/electrum/gui/kivy/uix/dialogs/tx_dialog.py
       t@@ -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):
       t@@ -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
   DIR diff --git a/electrum/gui/qt/transaction_dialog.py b/electrum/gui/qt/transaction_dialog.py
       t@@ -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):
       t@@ -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:
       t@@ -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)
   DIR diff --git a/electrum/transaction.py b/electrum/transaction.py
       t@@ -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 = {}
   DIR diff --git a/electrum/wallet.py b/electrum/wallet.py
       t@@ -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