tests: fix flaky test_simple_close_dust_output_omitted mempool race
What changed, and why it matters
This commit fixes a flaky automated test, not a security bug in the actual Core Lightning software. The test sometimes failed because it took a snapshot of Bitcoin's memory pool and then tried to fetch each transaction, but one transaction could be replaced by a higher-fee version in the tiny gap between those two steps. The fix simply retries the snapshot-and-check loop if a transaction disappears mid-check. No user funds, network behavior, or real-world security is affected.
No security action needed. Treat as a normal test-stability improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change is confined to tests/test_closing.py. It wraps an existing mempool assertion loop in a helper that catches bitcoin.rpc.InvalidAddressOrKeyError (the Python-bitcoinlib wrapper for bitcoind’s -5 ‘No such mempool or blockchain transaction’) and retries via wait_for(). The assertion that every closing transaction has exactly one non-fee output remains unchanged. This is purely a test-hardening patch against an RBF mempool race between two peers’ conflicting close transactions.
Changed components
tests/test_closing.pyInspect captured patch +16 / −5
diff --git a/tests/test_closing.py b/tests/test_closing.py
index 09c1dc0e..97690f53 100644
--- a/tests/test_closing.py
+++ b/tests/test_closing.py
@@ -4301,11 +4301,22 @@ def test_simple_close_dust_output_omitted(node_factory, bitcoind):
# Every closing tx in the mempool must have exactly 1 output: the dust
# output is omitted in all variants. Elements appends an explicit fee
# output (scriptPubKey type 'fee') which must not be counted here.
- for txid in bitcoind.rpc.getrawmempool():
- tx = bitcoind.rpc.getrawtransaction(txid, True)
- real_vouts = [v for v in tx['vout'] if v['scriptPubKey'].get('type') != 'fee']
- assert len(real_vouts) == 1, \
- f"tx {txid} has {len(tx['vout'])} outputs; expected 1 (dust omitted)"
+ # Both sides broadcast conflicting closer txs, and l1's higher-fee tx
+ # can RBF-replace l2's between our getrawmempool() snapshot and the
+ # getrawtransaction() call (bitcoind error -5), so retry with a fresh
+ # snapshot until we see a consistent one.
+ def closing_txs_have_single_output():
+ try:
+ for txid in bitcoind.rpc.getrawmempool():
+ tx = bitcoind.rpc.getrawtransaction(txid, True)
+ real_vouts = [v for v in tx['vout'] if v['scriptPubKey'].get('type') != 'fee']
+ assert len(real_vouts) == 1, \
+ f"tx {txid} has {len(tx['vout'])} outputs; expected 1 (dust omitted)"
+ except bitcoin.rpc.InvalidAddressOrKeyError:
+ return False
+ return True
+
+ wait_for(closing_txs_have_single_output)
def test_simple_close_restart(node_factory, bitcoind):
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.