From 7cfc667179f5bbe7f65b201865ffc616c75b9320 Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Thu, 9 Jul 2026 12:21:38 -0700 Subject: [PATCH] tests: fix flaky test_simple_close_dust_output_omitted mempool race The test snapshots getrawmempool() and then calls getrawtransaction() on each txid. In simple close both peers broadcast conflicting closer txs: here l2's tx pays only 400sat fee (its entire 400sat balance goes to fees since its own output is dust) while l1's pays 3375sat, so l1's tx can RBF-replace l2's between the snapshot and the fetch. The getrawtransaction() call on the replaced txid then fails with error -5 (No such mempool or blockchain transaction). A CI failure showed exactly this ordering: l2 broadcast its closer tx, l1's higher-fee tx replaced it 14ms later, and the test's per-txid fetch raced the replacement. Retry the whole snapshot-and-check loop when a txid vanishes mid-iteration, keeping the single-output assertion hard. This is the same class of mempool race fixed for test_simple_close_delay_broadcast in 5ae0705f2 ("tests: fix flaky test_simple_close_delay_broadcast mempool race"). Changelog-None --- tests/test_closing.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index 88a9fb1997a1..783de349b9a7 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -4230,11 +4230,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):