commit f125a06453ddba01666b2af4a6cee20b77dd1ab1
parent da6080421e816c0c63517fe1046d431c587ce049
Author: SomberNight <somber.night@protonmail.com>
Date: Sat, 17 Oct 2020 17:58:22 +0200
wallet: simplify get_wallet_delta
Diffstat:
4 files changed, 62 insertions(+), 77 deletions(-)
diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py
@@ -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
@@ -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
@@ -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"""
@@ -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()))
diff --git a/electrum/gui/kivy/uix/dialogs/tx_dialog.py b/electrum/gui/kivy/uix/dialogs/tx_dialog.py
@@ -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
@@ -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
diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
@@ -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:
diff --git a/electrum/wallet.py b/electrum/wallet.py
@@ -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
@@ -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