From 3463e683064cdde2450a5b1c02c7ad0264bd4baa Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 7 Feb 2025 09:52:03 +0100 Subject: [PATCH] add accounting addresses --- electrum/gui/qt/history_list.py | 2 +- electrum/lnwatcher.py | 33 +++++++++++++++++++++++++++++++++ electrum/submarine_swaps.py | 1 + electrum/wallet.py | 15 +++++++++++++-- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/electrum/gui/qt/history_list.py b/electrum/gui/qt/history_list.py index 2e307e69e..27b12e031 100644 --- a/electrum/gui/qt/history_list.py +++ b/electrum/gui/qt/history_list.py @@ -255,7 +255,7 @@ class HistoryModel(CustomModel, Logger): def get_domain(self): """Overridden in address_dialog.py""" - return self.window.wallet.get_addresses() + return None def should_include_lightning_payments(self) -> bool: """Overridden in address_dialog.py""" diff --git a/electrum/lnwatcher.py b/electrum/lnwatcher.py index d9e9b1734..c4c2c9d13 100644 --- a/electrum/lnwatcher.py +++ b/electrum/lnwatcher.py @@ -149,6 +149,7 @@ class LNWatcher(Logger, EventListener): Side-effécts: - sets defaults labels + - populates wallet._accounting_addresses """ chan = self.lnworker.channel_by_txo(funding_outpoint) if not chan: @@ -175,10 +176,12 @@ class LNWatcher(Logger, EventListener): self.lnworker.wallet.set_default_label(prevout2, htlc_sweep_info.name) if htlc_tx_spender: keep_watching |= not self.adb.is_deeply_mined(htlc_tx_spender) + self.maybe_add_accounting_address(htlc_tx_spender, htlc_sweep_info) else: keep_watching |= self.maybe_redeem(htlc_sweep_info) keep_watching |= not self.adb.is_deeply_mined(spender_txid) self.maybe_extract_preimage(chan, spender_tx, prevout) + self.maybe_add_accounting_address(spender_txid, sweep_info) else: keep_watching |= self.maybe_redeem(sweep_info) return keep_watching @@ -199,3 +202,33 @@ class LNWatcher(Logger, EventListener): spender_txin, is_deeply_mined=self.adb.is_deeply_mined(spender_tx.txid()), ) + + def maybe_add_accounting_address(self, spender_txid: str, sweep_info: 'SweepInfo'): + spender_tx = self.adb.get_transaction(spender_txid) if spender_txid else None + if not spender_tx: + return + for i, txin in enumerate(spender_tx.inputs()): + if txin.prevout == sweep_info.txin.prevout: + break + else: + return + if sweep_info.name in ['first-stage-htlc', 'first-stage-htlc-anchors']: + # always consider ours + pass + else: + privkey = sweep_info.txin.privkey + witness = txin.witness_elements() + for sig in witness: + sig = witness[0] + # fixme: verify sig is ours + witness2 = sweep_info.txin.make_witness(sig) + if txin.witness == witness2: + break + else: + self.logger.info(f"signature not found {sweep_info.name}, {txin.prevout.to_str()}") + return + self.logger.info(f'adding txin address {sweep_info.name}, {txin.prevout.to_str()}') + prev_txid, prev_index = txin.prevout.to_str().split(':') + prev_tx = self.adb.get_transaction(prev_txid) + txout = prev_tx.outputs()[int(prev_index)] + self.lnworker.wallet._accounting_addresses.add(txout.address) diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py index d4a1bbaf0..72f44f38c 100644 --- a/electrum/submarine_swaps.py +++ b/electrum/submarine_swaps.py @@ -1186,6 +1186,7 @@ class SwapManager(Logger): 'group_label': group_label, 'label': _('Refund transaction'), } + self.wallet._accounting_addresses.add(swap.lockup_address) return d def get_group_id_for_payment_hash(self, payment_hash): diff --git a/electrum/wallet.py b/electrum/wallet.py index 53d8b6715..6d0932544 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -420,6 +420,7 @@ class Abstract_Wallet(ABC, Logger, EventListener): self._last_full_history = None self._tx_parents_cache = {} self._default_labels = {} + self._accounting_addresses = set() # addresses counted as ours after successful sweep self.taskgroup = OldTaskGroup() @@ -874,6 +875,15 @@ class Abstract_Wallet(ABC, Logger, EventListener): def get_swaps_by_funding_tx(self, tx: Transaction) -> Iterable['SwapData']: return self.lnworker.swap_manager.get_swaps_by_funding_tx(tx) if self.lnworker else [] + def is_accounting_address(self, addr): + """ + Addresses from which we have been able to sweep funds. + We consider them 'ours' for accounting purposes, so that the + wallet history does not show funds going in and out of the wallet. + """ + # must be a sweep utxo AND we swept (spending tx is a wallet tx) + return addr in self._accounting_addresses + def get_wallet_delta(self, tx: Transaction) -> TxWalletDelta: """Return the effect a transaction has on the wallet. This method must use self.is_mine, not self.adb.is_mine() @@ -885,7 +895,7 @@ class Abstract_Wallet(ABC, Logger, EventListener): for txin in tx.inputs(): addr = self.adb.get_txin_address(txin) value = self.adb.get_txin_value(txin, address=addr) - if self.is_mine(addr): + if self.is_mine(addr) or self.is_accounting_address(addr): num_input_ismine += 1 is_relevant = True assert value is not None @@ -896,7 +906,7 @@ class Abstract_Wallet(ABC, Logger, EventListener): v_in += value for txout in tx.outputs(): v_out += txout.value - if self.is_mine(txout.address): + if self.is_mine(txout.address) or self.is_accounting_address(txout.address): v_out_mine += txout.value is_relevant = True delta = v_out_mine - v_in_mine @@ -1169,6 +1179,7 @@ class Abstract_Wallet(ABC, Logger, EventListener): groups = self.lnworker.get_groups_for_onchain_history() if self.lnworker else {} if domain is None: domain = self.get_addresses() + domain += list(self._accounting_addresses) now = time.time() transactions = OrderedDictWithIndex()