From 446879ade06d67490e6e6eea437bf7073c1b5802 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Tue, 4 Apr 2023 13:37:10 +0000 Subject: [PATCH] lnwatcher.maybe_redeem: wanted_height should always be absolute previously, if prev_height.height was <= 0, lnwatcher was calling adb.set_future_tx() with weird wanted_height values (with ~sweep_info.csv_delay) --- electrum/address_synchronizer.py | 6 ++++-- electrum/lnwatcher.py | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py index ea969efbf..e54b176c7 100644 --- a/electrum/address_synchronizer.py +++ b/electrum/address_synchronizer.py @@ -79,7 +79,7 @@ class AddressSynchronizer(Logger, EventListener): # locks: if you need to take multiple ones, acquire them in the order they are defined here! self.lock = threading.RLock() self.transaction_lock = threading.RLock() - self.future_tx = {} # type: Dict[str, int] # txid -> wanted height + self.future_tx = {} # type: Dict[str, int] # txid -> wanted (abs) height # Txs the server claims are mined but still pending verification: self.unverified_tx = defaultdict(int) # type: Dict[str, int] # txid -> height. Access with self.lock. # Txs the server claims are in the mempool: @@ -655,7 +655,9 @@ class AddressSynchronizer(Logger, EventListener): return cached_local_height return self.network.get_local_height() if self.network else self.db.get('stored_height', 0) - def set_future_tx(self, txid:str, wanted_height: int): + def set_future_tx(self, txid: str, *, wanted_height: int): + # note: wanted_height is always an absolute height, even in case of CSV-locked txs. + # In case of a CSV-locked tx with unconfirmed inputs, the wanted_height is a best-case guess. with self.lock: self.future_tx[txid] = wanted_height diff --git a/electrum/lnwatcher.py b/electrum/lnwatcher.py index 51b741b42..deeb46492 100644 --- a/electrum/lnwatcher.py +++ b/electrum/lnwatcher.py @@ -514,13 +514,18 @@ class LNWalletWatcher(LNWatcher): wanted_height = sweep_info.cltv_expiry if wanted_height - local_height > 0: can_broadcast = False - reason = 'waiting for {}: CLTV ({} > {})'.format(name, local_height, sweep_info.cltv_expiry) + # self.logger.debug(f"pending redeem for {prevout}. waiting for {name}: CLTV ({local_height=}, {wanted_height=})") if sweep_info.csv_delay: prev_height = self.adb.get_tx_height(prev_txid) - wanted_height = sweep_info.csv_delay + prev_height.height - 1 - if prev_height.height <= 0 or wanted_height - local_height > 0: + if prev_height.height > 0: + wanted_height = prev_height.height + sweep_info.csv_delay - 1 + else: + wanted_height = local_height + sweep_info.csv_delay + if wanted_height - local_height > 0: can_broadcast = False - reason = 'waiting for {}: CSV ({} >= {})'.format(name, prev_height.conf, sweep_info.csv_delay) + # self.logger.debug( + # f"pending redeem for {prevout}. waiting for {name}: CSV " + # f"({local_height=}, {wanted_height=}, {prev_height.height=}, {sweep_info.csv_delay=})") if can_broadcast: self.logger.info(f'we can broadcast: {name}') tx_was_added = await self.network.try_broadcasting(new_tx, name) @@ -536,8 +541,9 @@ class LNWalletWatcher(LNWatcher): self.logger.info(f'added redeem tx: {name}. prevout: {prevout}') else: tx_was_added = False - # set future tx regardless of tx_was_added, because it is not persisted - self.adb.set_future_tx(new_tx.txid(), wanted_height) + # set future tx regardless of tx_was_added, because it is not persisted + # (and wanted_height can change if input of CSV was not mined before) + self.adb.set_future_tx(new_tx.txid(), wanted_height=wanted_height) if tx_was_added: self.lnworker.wallet.set_label(new_tx.txid(), name) if old_tx and old_tx.txid() != new_tx.txid():