electrum

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

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:
Melectrum/address_synchronizer.py | 97+++++++++++++++++++++++++++++++++----------------------------------------------
Melectrum/gui/kivy/uix/dialogs/tx_dialog.py | 4++--
Melectrum/gui/qt/main_window.py | 12++++++------
Melectrum/wallet.py | 26+++++++++++++-------------
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