From b2169b745e50612a95b70f630078c8fd6ec0be18 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Sat, 15 May 2021 06:31:03 +0200 Subject: [PATCH] wallet: (fix) "batch_rbf" must not mutate LN funding txs The "Batch RBF transactions" feature mutates existing "local" and "unconfirmed RBF" transactions when creating new transactions: it simply adds the new outputs to the existing txs (and updates the change). The "RBF" flag is only enforced for unconfirmed txs, not for local txs. The bug was that given a local LN funding tx, when creating a new channel with "batch_rbf" enabled, we would modify the existing local tx, and broadcast that. related: #7298 --- electrum/wallet.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/electrum/wallet.py b/electrum/wallet.py index 14a733a29..9f10b965a 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -586,6 +586,10 @@ class Abstract_Wallet(AddressSynchronizer, ABC): """Returns a map: pubkey -> (keystore, derivation_suffix)""" return {} + def is_lightning_funding_tx(self, txid: str) -> bool: + return any([chan.funding_outpoint.txid == txid + for chan in self.lnworker.channels.values()]) + def get_tx_info(self, tx: Transaction) -> TxWalletDetails: tx_wallet_delta = self.get_wallet_delta(tx) is_relevant = tx_wallet_delta.is_relevant @@ -598,8 +602,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC): tx_hash = tx.txid() # note: txid can be None! e.g. when called from GUI tx dialog is_lightning_funding_tx = False if self.has_lightning() and tx_hash is not None: - is_lightning_funding_tx = any([chan.funding_outpoint.txid == tx_hash - for chan in self.lnworker.channels.values()]) + is_lightning_funding_tx = self.is_lightning_funding_tx(tx_hash) tx_we_already_have_in_db = self.db.get_transaction(tx_hash) can_save_as_local = (is_relevant and tx.txid() is not None and (tx_we_already_have_in_db is None or not tx_we_already_have_in_db.is_complete())) @@ -1241,6 +1244,9 @@ class Abstract_Wallet(AddressSynchronizer, ABC): # all inputs should be is_mine if not all([self.is_mine(self.get_txin_address(txin)) for txin in tx.inputs()]): continue + # do not mutate LN funding txs, as that would change their txid + if self.is_lightning_funding_tx(txid): + continue # prefer txns already in mempool (vs local) if hist_item.tx_mined_status.height == TX_HEIGHT_LOCAL: candidate = tx