From 27c6e9c7ff849e9bf17180066233ade72be3588c Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Thu, 23 Jul 2026 13:09:21 -0700 Subject: [PATCH] tests: fix flaky test_onchain_rbf_stops_after_confirmation The test censors l2's sendrawtransaction with an rpcproxy mock while the node RBFs its penalty tx three times, then removes the mock so the next RBF version reaches bitcoind for real and gets mined. But each 'RBF onchain txid' log line precedes the corresponding broadcast, so wait_for_log can return -- and the test un-mock -- while the third replacement is still in flight. That older version then lands in bitcoind's real mempool, the next version's broadcast is rejected as a conflict, and the block mines a version the node no longer tracks, so RBF-ing continues after confirmation and the final assertion fails. Seen under valgrind, where the window between log line and broadcast is wide. Count the broadcasts the censoring mock swallows and wait for all four (initial penalty tx plus three replacements) to have reached the proxy before un-mocking, so no broadcast can be in flight when censoring stops. Fixes: #9347 Changelog-None --- tests/test_closing.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index 97690f53447b..17a46f982d85 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -1765,8 +1765,15 @@ def censoring_sendrawtx(r): bitcoind.generate_block(1) l2.daemon.wait_for_log('RBF onchain txid') - # Stop censoring. Generate a block to trigger rebroadcast (the penalty tx - # enters bitcoind's mempool) but filter it out so the next block mines it. + # Stop censoring — but only once all four censored broadcasts (initial + # penalty tx plus three replacements) have reached the proxy: each 'RBF + # onchain txid' log line precedes the corresponding broadcast, and + # un-mocking while one is in flight lets an old version into bitcoind's + # real mempool, where it gets mined in place of the version the node + # tracks (#9347). The proxy counts the calls its mock swallows. + # Then generate a block to trigger rebroadcast (the penalty tx enters + # bitcoind's mempool) but filter it out so the next block mines it. + wait_for(lambda: l2.daemon.rpcproxy.mock_counts['sendrawtransaction'] >= 4) l2.daemon.rpcproxy.mock_rpc('sendrawtransaction', None) bitcoind.generate_block(1, needfeerate=10000000) l2.daemon.wait_for_log('RBF onchain txid')