URI: 
       twallet: simplify get_wallet_delta - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit f125a06453ddba01666b2af4a6cee20b77dd1ab1
   DIR parent da6080421e816c0c63517fe1046d431c587ce049
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Sat, 17 Oct 2020 17:58:22 +0200
       
       wallet: simplify get_wallet_delta
       
       Diffstat:
         M electrum/address_synchronizer.py    |      97 +++++++++++++------------------
         M electrum/gui/kivy/uix/dialogs/tx_d… |       4 ++--
         M electrum/gui/qt/main_window.py      |      12 ++++++------
         M electrum/wallet.py                  |      26 +++++++++++++-------------
       
       4 files changed, 62 insertions(+), 77 deletions(-)
       ---
   DIR diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py
       t@@ -57,6 +57,14 @@ class HistoryItem(NamedTuple):
            balance: Optional[int]
        
        
       +class TxWalletDelta(NamedTuple):
       +    is_relevant: bool  # "related to wallet?"
       +    is_any_input_ismine: bool
       +    is_all_input_ismine: bool
       +    delta: int
       +    fee: Optional[int]
       +
       +
        class AddressSynchronizer(Logger):
            """
            inherited by wallet
       t@@ -652,7 +660,7 @@ class AddressSynchronizer(Logger):
            def get_tx_delta(self, tx_hash, address):
                """effect of tx on address"""
                delta = 0
       -        # substract the value of coins sent from address
       +        # subtract the value of coins sent from address
                d = self.db.get_txi_addr(tx_hash, address)
                for n, v in d:
                    delta -= v
       t@@ -662,65 +670,43 @@ class AddressSynchronizer(Logger):
                    delta += v
                return delta
        
       -    @with_transaction_lock
       -    def get_tx_value(self, txid):
       -        """effect of tx on the entire domain"""
       -        delta = 0
       -        for addr in self.db.get_txi_addresses(txid):
       -            d = self.db.get_txi_addr(txid, addr)
       -            for n, v in d:
       -                delta -= v
       -        for addr in self.db.get_txo_addresses(txid):
       -            d = self.db.get_txo_addr(txid, addr)
       -            for n, (v, cb) in d.items():
       -                delta += v
       -        return delta
       -
       -    def get_wallet_delta(self, tx: Transaction):
       -        """ effect of tx on wallet """
       +    def get_wallet_delta(self, tx: Transaction) -> TxWalletDelta:
       +        """effect of tx on wallet"""
                is_relevant = False  # "related to wallet?"
       -        is_mine = False  # "is any input mine?"
       -        is_pruned = False
       -        is_partial = False
       -        v_in = v_out = v_out_mine = 0
       -        for txin in tx.inputs():
       -            addr = self.get_txin_address(txin)
       -            if self.is_mine(addr):
       -                is_mine = True
       -                is_relevant = True
       +        num_input_ismine = 0
       +        v_in = v_in_mine = v_out = v_out_mine = 0
       +        with self.lock, self.transaction_lock:
       +            for txin in tx.inputs():
       +                addr = self.get_txin_address(txin)
                        value = self.get_txin_value(txin, address=addr)
       +                if self.is_mine(addr):
       +                    num_input_ismine += 1
       +                    is_relevant = True
       +                    assert value is not None
       +                    v_in_mine += value
                        if value is None:
       -                    is_pruned = True
       -                else:
       +                    v_in = None
       +                elif v_in is not None:
                            v_in += value
       -            else:
       -                is_partial = True
       -        if not is_mine:
       -            is_partial = False
       -        for o in tx.outputs():
       -            v_out += o.value
       -            if self.is_mine(o.address):
       -                v_out_mine += o.value
       -                is_relevant = True
       -        if is_pruned:
       -            # some inputs are mine:
       -            fee = None
       -            if is_mine:
       -                v = v_out_mine - v_out
       -            else:
       -                # no input is mine
       -                v = v_out_mine
       +            for txout in tx.outputs():
       +                v_out += txout.value
       +                if self.is_mine(txout.address):
       +                    v_out_mine += txout.value
       +                    is_relevant = True
       +        delta = v_out_mine - v_in_mine
       +        if v_in is not None:
       +            fee = v_in - v_out
                else:
       -            v = v_out_mine - v_in
       -            if is_partial:
       -                # some inputs are mine, but not all
       -                fee = None
       -            else:
       -                # all inputs are mine
       -                fee = v_in - v_out
       -        if not is_mine:
                    fee = None
       -        return is_relevant, is_mine, v, fee
       +        if fee is None and isinstance(tx, PartialTransaction):
       +            fee = tx.get_fee()
       +        return TxWalletDelta(
       +            is_relevant=is_relevant,
       +            is_any_input_ismine=num_input_ismine > 0,
       +            is_all_input_ismine=num_input_ismine == len(tx.inputs()),
       +            delta=delta,
       +            fee=fee,
       +        )
        
            def get_tx_fee(self, txid: str) -> Optional[int]:
                """ Returns tx_fee or None. Use server fee only if tx is unconfirmed and not mine"""
       t@@ -747,8 +733,7 @@ class AddressSynchronizer(Logger):
                tx = self.db.get_transaction(txid)
                if not tx:
                    return None
       -        with self.lock, self.transaction_lock:
       -            is_relevant, is_mine, v, fee = self.get_wallet_delta(tx)
       +        fee = self.get_wallet_delta(tx).fee
                # save result
                self.db.add_tx_fee_we_calculated(txid, fee)
                self.db.add_num_inputs_to_tx(txid, len(tx.inputs()))
   DIR diff --git a/electrum/gui/kivy/uix/dialogs/tx_dialog.py b/electrum/gui/kivy/uix/dialogs/tx_dialog.py
       t@@ -232,7 +232,7 @@ class TxDialog(Factory.Popup):
        
            def do_rbf(self):
                from .bump_fee_dialog import BumpFeeDialog
       -        is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(self.tx)
       +        fee = self.wallet.get_wallet_delta(self.tx).fee
                if fee is None:
                    self.app.show_error(_("Can't bump fee: unknown fee for original transaction."))
                    return
       t@@ -257,7 +257,7 @@ class TxDialog(Factory.Popup):
        
            def do_dscancel(self):
                from .dscancel_dialog import DSCancelDialog
       -        is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(self.tx)
       +        fee = self.wallet.get_wallet_delta(self.tx).fee
                if fee is None:
                    self.app.show_error(_('Cannot cancel transaction') + ': ' + _('unknown fee for original transaction'))
                    return
   DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
       t@@ -812,18 +812,18 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
                if len(txns) >= 3:
                    total_amount = 0
                    for tx in txns:
       -                is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx)
       -                if not is_relevant:
       +                tx_wallet_delta = self.wallet.get_wallet_delta(tx)
       +                if not tx_wallet_delta.is_relevant:
                            continue
       -                total_amount += v
       +                total_amount += tx_wallet_delta.delta
                    self.notify(_("{} new transactions: Total amount received in the new transactions {}")
                                .format(len(txns), self.format_amount_and_units(total_amount)))
                else:
                    for tx in txns:
       -                is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx)
       -                if not is_relevant:
       +                tx_wallet_delta = self.wallet.get_wallet_delta(tx)
       +                if not tx_wallet_delta.is_relevant:
                            continue
       -                self.notify(_("New transaction: {}").format(self.format_amount_and_units(v)))
       +                self.notify(_("New transaction: {}").format(self.format_amount_and_units(tx_wallet_delta.delta)))
        
            def notify(self, message):
                if self.tray:
   DIR diff --git a/electrum/wallet.py b/electrum/wallet.py
       t@@ -557,10 +557,11 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
                """Returns a map: pubkey -> (keystore, derivation_suffix)"""
                return {}
        
       -    def get_tx_info(self, tx) -> TxWalletDetails:
       -        is_relevant, is_mine, v, fee = self.get_wallet_delta(tx)
       -        if fee is None and isinstance(tx, PartialTransaction):
       -            fee = tx.get_fee()
       +    def get_tx_info(self, tx: Transaction) -> TxWalletDetails:
       +        tx_wallet_delta = self.get_wallet_delta(tx)
       +        is_relevant = tx_wallet_delta.is_relevant
       +        is_any_input_ismine = tx_wallet_delta.is_any_input_ismine
       +        fee = tx_wallet_delta.fee
                exp_n = None
                can_broadcast = False
                can_bump = False
       t@@ -596,28 +597,27 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
                                size = tx.estimated_size()
                                fee_per_byte = fee / size
                                exp_n = self.config.fee_to_depth(fee_per_byte)
       -                    can_bump = is_mine and not tx.is_final()
       -                    can_dscancel = (is_mine and not tx.is_final()
       +                    can_bump = is_any_input_ismine and not tx.is_final()
       +                    can_dscancel = (is_any_input_ismine and not tx.is_final()
                                            and not all([self.is_mine(txout.address) for txout in tx.outputs()]))
                        else:
                            status = _('Local')
                            can_broadcast = self.network is not None
       -                    can_bump = is_mine and not tx.is_final()
       +                    can_bump = is_any_input_ismine and not tx.is_final()
                    else:
                        status = _("Signed")
                        can_broadcast = self.network is not None
                else:
       +            assert isinstance(tx, PartialTransaction)
                    s, r = tx.signature_count()
                    status = _("Unsigned") if s == 0 else _('Partially signed') + ' (%d/%d)'%(s,r)
        
                if is_relevant:
       -            if is_mine:
       -                if fee is not None:
       -                    amount = v + fee
       -                else:
       -                    amount = v
       +            if tx_wallet_delta.is_all_input_ismine:
       +                assert fee is not None
       +                amount = tx_wallet_delta.delta + fee
                    else:
       -                amount = v
       +                amount = tx_wallet_delta.delta
                else:
                    amount = None