From d11237d6a1da3cd60e6bfc9189f1305f1f9de984 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Wed, 1 Mar 2023 16:20:42 +0000 Subject: [PATCH] lnworker: start watching already redeemed chans if txs are missing This fixes a bug where if one runs `wallet.clear_history()` they would see exceptions later: ``` Traceback (most recent call last): File "/home/user/wspace/electrum/electrum/gui/qt/main_window.py", line 866, in timer_actions self.update_wallet() File "/home/user/wspace/electrum/electrum/gui/qt/main_window.py", line 1021, in update_wallet self.update_tabs() File "/home/user/wspace/electrum/electrum/gui/qt/main_window.py", line 1033, in update_tabs self.utxo_list.update() File "/home/user/wspace/electrum/electrum/gui/qt/utxo_list.py", line 103, in update self.refresh_row(name, idx) File "/home/user/wspace/electrum/electrum/gui/qt/utxo_list.py", line 124, in refresh_row parents = self.wallet.get_tx_parents(txid) File "/home/user/wspace/electrum/electrum/wallet.py", line 885, in get_tx_parents result.update(self.get_tx_parents(_txid)) File "/home/user/wspace/electrum/electrum/wallet.py", line 881, in get_tx_parents for i, txin in enumerate(tx.inputs()): AttributeError: 'NoneType' object has no attribute 'inputs' ``` This is related to the privacy analysis, which assumes that for each tx item in the history list we should have the raw tx in the db. This is no longer true after wallet.clear_history(), if the wallet has certain LN channels. E.g. an already redeemed channel that was local-force-closed, as that closing tx is not related to the wallet directly. In commit 3541ecb5765ae4971a7bb8109e4600e58d7f603b, we decided not to watch already redeemed channels. This is potentially good for e.g. privacy, as the server would otherwise see us subscribe to that chan. However it means that after running wallet.clear_history() txs related to the channel but not to the wallet won't be re-downloaded. Instead, now if there are missing txs for a redeemed channel, we start watching it, hence the synchronizer will re-downloaded the txs. --- electrum/lnchannel.py | 23 +++++++++++++++++++++++ electrum/lnworker.py | 4 ++-- electrum/wallet.py | 1 + 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py index 5458a2b0c..5329dd271 100644 --- a/electrum/lnchannel.py +++ b/electrum/lnchannel.py @@ -230,6 +230,29 @@ class AbstractChannel(Logger, ABC): def is_redeemed(self): return self.get_state() == ChannelState.REDEEMED + def need_to_subscribe(self) -> bool: + """Whether lnwatcher/synchronizer need to be watching this channel.""" + if not self.is_redeemed(): + return True + # Chan already deeply closed. Still, if some txs are missing, we should sub. + # check we have funding tx + # note: tx might not be directly related to the wallet, e.g. chan opened by remote + if (funding_item := self.get_funding_height()) is None: + return True + if self.lnworker: + funding_txid, funding_height, funding_timestamp = funding_item + if self.lnworker.wallet.adb.get_transaction(funding_txid) is None: + return True + # check we have closing tx + # note: tx might not be directly related to the wallet, e.g. local-fclose + if (closing_item := self.get_closing_height()) is None: + return True + if self.lnworker: + closing_txid, closing_height, closing_timestamp = closing_item + if self.lnworker.wallet.adb.get_transaction(closing_txid) is None: + return True + return False + @abstractmethod def get_close_options(self) -> Sequence[ChanCloseOption]: pass diff --git a/electrum/lnworker.py b/electrum/lnworker.py index 5f6a579c2..5ecaaabb7 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -762,10 +762,10 @@ class LNWallet(LNWorker): self.lnrater = LNRater(self, network) for chan in self.channels.values(): - if not chan.is_redeemed(): + if chan.need_to_subscribe(): self.lnwatcher.add_channel(chan.funding_outpoint.to_str(), chan.get_funding_address()) for cb in self.channel_backups.values(): - if not cb.is_redeemed(): + if cb.need_to_subscribe(): self.lnwatcher.add_channel(cb.funding_outpoint.to_str(), cb.get_funding_address()) for coro in [ diff --git a/electrum/wallet.py b/electrum/wallet.py index 0c112f551..a82d43375 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -878,6 +878,7 @@ class Abstract_Wallet(ABC, Logger, EventListener): result = {} parents = [] tx = self.adb.get_transaction(txid) + assert tx, f"cannot find {txid} in db" for i, txin in enumerate(tx.inputs()): _txid = txin.prevout.txid.hex() parents.append(_txid)