From 7584ba00cec0d3b29d6589dbe54f74538d844350 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Thu, 9 Mar 2023 14:59:08 +0000 Subject: [PATCH] wallet: kill negative conf numbers for TxMinedInfo fixes https://github.com/spesmilo/electrum/issues/8240 #8240 was triggering an AssertionError in wallet.get_invoice_status, as code there was assuming conf >= 0. To trigger, force-close a LN channel, and while the sweep is waiting on the CSV, try to make a payment in the Send tab to the ismine change address used for the sweep in the future_tx. (order of events can also be reversed) --- electrum/address_synchronizer.py | 7 +++---- electrum/gui/qt/history_list.py | 11 ++++++++--- electrum/util.py | 3 ++- electrum/wallet.py | 9 ++++++--- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py index 7bd06e45d..7dcbe5fd7 100644 --- a/electrum/address_synchronizer.py +++ b/electrum/address_synchronizer.py @@ -674,10 +674,9 @@ class AddressSynchronizer(Logger, EventListener): elif tx_hash in self.unconfirmed_tx: height = self.unconfirmed_tx[tx_hash] return TxMinedInfo(height=height, conf=0) - elif tx_hash in self.future_tx: - num_blocks_remainining = self.future_tx[tx_hash] - self.get_local_height() - if num_blocks_remainining > 0: - return TxMinedInfo(height=TX_HEIGHT_FUTURE, conf=-num_blocks_remainining) + elif wanted_height := self.future_tx.get(tx_hash): + if wanted_height > self.get_local_height(): + return TxMinedInfo(height=TX_HEIGHT_FUTURE, conf=0, wanted_height=wanted_height) else: return TxMinedInfo(height=TX_HEIGHT_LOCAL, conf=0) else: diff --git a/electrum/gui/qt/history_list.py b/electrum/gui/qt/history_list.py index 59c7dc3ef..26180ddda 100644 --- a/electrum/gui/qt/history_list.py +++ b/electrum/gui/qt/history_list.py @@ -425,11 +425,16 @@ class HistoryModel(CustomModel, Logger): @staticmethod def tx_mined_info_from_tx_item(tx_item): - tx_mined_info = TxMinedInfo(height=tx_item['height'], - conf=tx_item['confirmations'], - timestamp=tx_item['timestamp']) + # FIXME a bit hackish to have to reconstruct the TxMinedInfo... + tx_mined_info = TxMinedInfo( + height=tx_item['height'], + conf=tx_item['confirmations'], + timestamp=tx_item['timestamp'], + wanted_height=tx_item.get('wanted_height', None), + ) return tx_mined_info + class HistoryList(MyTreeView, AcceptFileDragDrop): filter_columns = [HistoryColumns.STATUS, HistoryColumns.DESCRIPTION, diff --git a/electrum/util.py b/electrum/util.py index ba7dd690a..a0213a583 100644 --- a/electrum/util.py +++ b/electrum/util.py @@ -1275,10 +1275,11 @@ def with_lock(func): class TxMinedInfo(NamedTuple): height: int # height of block that mined tx - conf: Optional[int] = None # number of confirmations, SPV verified (None means unknown) + conf: Optional[int] = None # number of confirmations, SPV verified. >=0, or None (None means unknown) timestamp: Optional[int] = None # timestamp of block that mined tx txpos: Optional[int] = None # position of tx in serialized block header_hash: Optional[str] = None # hash of block that mined tx + wanted_height: Optional[int] = None # in case of timelock, min abs block height class ShortID(bytes): diff --git a/electrum/wallet.py b/electrum/wallet.py index 2030fa70d..ac5aac422 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -1000,7 +1000,7 @@ class Abstract_Wallet(ABC, Logger, EventListener): monotonic_timestamp = 0 for hist_item in self.adb.get_history(domain=domain): monotonic_timestamp = max(monotonic_timestamp, (hist_item.tx_mined_status.timestamp or 999_999_999_999)) - yield { + d = { 'txid': hist_item.txid, 'fee_sat': hist_item.fee, 'height': hist_item.tx_mined_status.height, @@ -1014,6 +1014,9 @@ class Abstract_Wallet(ABC, Logger, EventListener): 'label': self.get_label_for_txid(hist_item.txid), 'txpos_in_block': hist_item.tx_mined_status.txpos, } + if wanted_height := hist_item.tx_mined_status.wanted_height: + d['wanted_height'] = wanted_height + yield d def create_invoice(self, *, outputs: List[PartialTxOutput], message, pr, URI) -> Invoice: height = self.adb.get_local_height() @@ -1473,8 +1476,8 @@ class Abstract_Wallet(ABC, Logger, EventListener): conf = tx_mined_info.conf timestamp = tx_mined_info.timestamp if height == TX_HEIGHT_FUTURE: - assert conf < 0, conf - num_blocks_remainining = -conf + num_blocks_remainining = tx_mined_info.wanted_height - self.adb.get_local_height() + num_blocks_remainining = max(0, num_blocks_remainining) return 2, f'in {num_blocks_remainining} blocks' if conf == 0: tx = self.db.get_transaction(tx_hash)