fuzz: exercise the transaction-handling path in process_message(s) #35482

pull HowHsu wants to merge 5 commits into bitcoin:master from HowHsu:fuzz-assert-mempool-empty changing 26 files +144 −104
  1. HowHsu commented at 8:57 AM on June 8, 2026: contributor

    Problem

    process_message and process_messages keep the node in IBD (ResetIbd()) and mine their coinbases with the default bare-OP_TRUE output script. As a result net_processing returns early at the IsInitialBlockDownload() check and never reaches the transaction-handling path; and even if it did, a tx spending a bare-OP_TRUE coinbase is rejected as NONSTANDARD by ValidateInputsStandardness. The reused mempool therefore always stays empty and that path is never exercised.

    Changes

    Both targets now get the same treatment:

    1. Toggle IBD from the test input — a bool decides whether to also JumpOutOfIbd(), exercising both the IBD and non-IBD paths. In process_message it is consumed last, so existing corpus entries read false and are unchanged. In process_messages the messages run in a loop, so the bool must be consumed first (see the corpus note below).
    2. Use a spendable P2WSH_OP_TRUE coinbase — both anyone-can-spend (an OP_TRUE witness, no signature) and a standard witness output, so a fuzz-built tx spending a mature coinbase can actually be accepted into the mempool.
    3. Reset the rng before rebuilding (preparation) — rebuilding the chainman (and, in the next commit, the mempool) consumes the global PRNG. Reset it with MakeRandDeterministicDANGEROUS() first so the rebuild is deterministic across iterations. Mirrors the cmpctblock harness.
    4. Reset the reused mempool — now that the mempool can become non-empty, rebuild it together with the chainman in ResetChainmanAndMempool() when the block index grew or the mempool changed. A dirty mempool is detected by its sequence number rather than its size, since a tx can be added and removed within one iteration (leaving the size unchanged).

    Corpus note

    In process_messages the IBD bool is consumed before the message loop (first integral read), which shifts the FuzzedDataProvider layout. Existing process_messages corpus entries can be migrated by appending a single 0x00 byte at the end (read as false, keeping the IBD path); every other consumed value stays the same. This is a qa-assets change accompanying this PR.

  2. DrahtBot added the label Fuzzing on Jun 8, 2026
  3. DrahtBot commented at 8:57 AM on June 8, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/35482.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process. A summary of reviews will appear here.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #35581 (node: add block template manager and track waitNext fee inflow by ismaelsadeeq)
    • #35557 (kernel, validation: Add btck_chainstate_manager_set_clock_time by ryanofsky)
    • #35351 (net: Disallow invalid HeadersSyncState due to lagging clock by hodlinator)
    • #24230 (indexes: Stop using node internal types and locking cs_main, improve sync logic by ryanofsky)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. maflcko commented at 9:09 AM on June 10, 2026: member

    Hmm, what makes the fuzz target produce valid blocks, but not valid transactions? Is it that the prevhash can be solved by a fuzz engine for some reason, but the prevout can not? I guess that could be, because one may be a linear memory compare, and the other a map lookup?

    Not sure what to do here, but an alternative could be to add the mempool reset logic and hope that fuzz engines in the future will be smart enough to solve a map lookup?

  5. sedited commented at 9:33 AM on June 10, 2026: contributor

    I think I would also prefer if the mempools are just reset instead. Having an empty mempool at all times shouldn't be an invariant of the test.

  6. HowHsu force-pushed on Jun 11, 2026
  7. maflcko commented at 8:52 AM on June 11, 2026: member

    I don't think this change makes sense. Can you please answer my previous questions why this code is not hit?

    I know the answer, but the burden to explain pull requests is on the author.

    Hint: You'll have to look at the runtime coverage.

    Absent that, I don't think this change makes sense, because it is blindly modifying dead/unreachable code without a context or goal.

    <!-- 019eb5c9-2420-7d01-88dd-bb944b831d13 # in dir /tmp/tmp.a9CcqXNjti/git_dir# fuzz process_message tx. 101 history 102 git diff 103 git diff -U1 104 history root@21d4968c4719:/tmp/tmp.a9CcqXNjti/git_dir# git diff -U1 diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp index f5d5989..6bfcb7a 100644 --- a/src/test/fuzz/process_message.cpp +++ b/src/test/fuzz/process_message.cpp @@ -6,3 +6,5 @@ #include <banman.h> +#include <coins.h> #include <consensus/consensus.h> +#include <core_io.h> #include <kernel/chainparams.h> @@ -45,2 +47,3 @@ TestingSetup* g_setup; std::string_view LIMIT_TO_MESSAGE_TYPE{}; +std::vector<COutPoint> g_mature_coinbase_outpoints; @@ -52,5 +55,10 @@ void ResetChainman(TestingSetup& setup) setup.LoadVerifyActivateChainstate(); + g_mature_coinbase_outpoints.clear(); for (int i = 0; i < 2 * COINBASE_MATURITY; i++) { node::BlockCreateOptions options; - MineBlock(setup.m_node, options); + options.coinbase_output_script = P2WSH_OP_TRUE; + const COutPoint coinbase_outpoint{MineBlock(setup.m_node, options)}; + if (i < COINBASE_MATURITY) { + g_mature_coinbase_outpoints.push_back(coinbase_outpoint); + } } @@ -89,2 +97,24 @@ FUZZ_TARGET(process_message, .init = initialize_process_message) + Assert(!g_mature_coinbase_outpoints.empty()); + + const COutPoint prevout{PickValue(fuzzed_data_provider, g_mature_coinbase_outpoints)}; + CAmount coin_value; + { + LOCK(cs_main); + coin_value = node.chainman->ActiveChainstate().CoinsTip().AccessCoin(prevout).out.nValue; + } + Assert(coin_value > 1); + CMutableTransaction mtx; + mtx.vin.resize(1); + mtx.vin[0].prevout = prevout; + mtx.vin[0].scriptWitness.stack = {WITNESS_STACK_ELEM_OP_TRUE}; + mtx.vout.emplace_back(coin_value - 1, P2WSH_OP_TRUE); + const CTransactionRef tx{MakeTransactionRef(mtx)}; + + LOCK(cs_main); + const auto tx_result{node.chainman->ProcessTransaction(tx)}; + Assert(tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID); + std::cout << EncodeHexTx(*tx) << std::endl; + Assert(node.mempool->size() == 0); + // Reset, so that dangling pointers can be detected by sanitizers.

  8. HowHsu commented at 9:10 AM on June 11, 2026: contributor

    Hmm, what makes the fuzz target produce valid blocks, but not valid transactions? Is it that the prevhash can be solved by a fuzz engine for some reason, but the prevout can not? I guess that could be, because one may be a linear memory compare, and the other a map lookup?

    Not sure what to do here, but an alternative could be to add the mempool reset logic and hope that fuzz engines in the future will be smart enough to solve a map lookup?

    I measured this on the process_message corpus: out of 2386 inputs, 90 grow the block index (so the fuzz engine does solve a valid hashPrevBlock), but 0 make the mempool non-empty. It's probably not prevhash-vs-prevout: both are unordered_map lookups (BlockMap and the UTXO set), about equally hard, and those 90 hits show the engine can solve that kind of key. The real difference is signature verification. A block reaches the block index with no script checks at all — CheckBlock/AcceptBlock only verify (simplified) PoW + merkle + prevhash, and CheckInputScripts only runs later in ConnectBlock (where coinbase is skipped) — so an empty block with just a coinbase needs no signatures. A transaction, on the other hand, must pass CheckInputScripts to enter the mempool, and a fuzz engine can't brute-force a valid signature.

  9. maflcko commented at 9:15 AM on June 11, 2026: member

    No, that is the wrong explanation and ai slop. A signature isn't needed for OP_TRUE.

    Please don't use llm bots if you don't understand their output. You are neither helping yourself, nor anyone else.

  10. HowHsu commented at 9:16 AM on June 11, 2026: contributor

    No, that is the wrong explanation and ai slop. A signature isn't needed for OP_TRUE.

    Please don't use llm bots if you don't understand their output. You are neither helping yourself, nor anyone else.

    Need sometime to investigate it.

  11. maflcko commented at 9:20 AM on June 11, 2026: member

    Need sometime to investigate it.

    Sure, take all the time you need. I am happy to answer any questions you may have.

    Also, for future reference, copy-pasting LLM output without marking it as such is against the guidelines: See https://github.com/bitcoin/bitcoin/pull/35386/changes#diff-0db3c9d8a653e398b629930edc9e7dcde74514ef45c26f02a7bd51157d52dcee

  12. HowHsu commented at 1:54 PM on June 11, 2026: contributor

    No, that is the wrong explanation and ai slop. A signature isn't needed for OP_TRUE.

    Please don't use llm bots if you don't understand their output. You are neither helping yourself, nor anyone else.

    Still investigating. The first blocker I noticed from the coverage report is that the IBD state seems fixed. Shouldn't it be controlled by the test input so we can cover both the IBD and !IBD code paths?

    The second one is the prevout, I'm gonna construct a valid one to pass it.

  13. maflcko commented at 2:08 PM on June 11, 2026: member

    Still investigating. The first blocker I noticed from the coverage report is that the IBD state seems fixed. Shouldn't it be controlled by the test input so we can cover both the IBD and !IBD code paths?

    Yeah, see:

    src/test/util/validation.cpp:void TestChainstateManager::ResetIbd()
    src/test/util/validation.cpp:void TestChainstateManager::JumpOutOfIbd()
    

    The second one is the prevout, I'm gonna construct a valid one to pass it.

    I actually don't know if the prevout is the problem. If a fuzz engine can solve the prevhash, it should be able to solve the prevout?

    I think there is at least one more other reason why it fails...

  14. HowHsu commented at 9:05 AM on June 12, 2026: contributor

    Still investigating. The first blocker I noticed from the coverage report is that the IBD state seems fixed. Shouldn't it be controlled by the test input so we can cover both the IBD and !IBD code paths?

    Yeah, see:

    src/test/util/validation.cpp:void TestChainstateManager::ResetIbd()
    src/test/util/validation.cpp:void TestChainstateManager::JumpOutOfIbd()
    

    The second one is the prevout, I'm gonna construct a valid one to pass it.

    I actually don't know if the prevout is the problem. If a fuzz engine can solve the prevhash, it should be able to solve the prevout?

    I think there is at least one more other reason why it fails...

    Finally found it: ValidateInputsStandardness() --> Solver() returns TxoutType::NONSTANDARD the rootcause is the default coinbase_output_script is CScript() << OP_TRUE. From fuzz/cmpctblock.cpp, I found P2WSH_OP_TRUE is good to pass it.

  15. maflcko commented at 9:39 AM on June 12, 2026: member

    Jup.

    Going further, I don't know (haven't tried) if a fuzz engine can solve this. The hash and the havoc effect makes this a bit difficult, but I think you can manually copy-paste a hex transaction into a fuzz input.

    If you want to do that, I've pushed https://github.com/maflcko/DrahtBot/commit/bef76857851c4166bde3204b98d065578c89139a#diff-020ce280c4497d9badfc07d9034d427afaaed448e7289604e207e07bb03b654bR565 (untested, experimental)

  16. HowHsu commented at 12:07 PM on June 12, 2026: contributor

    Jup.

    Going further, I don't know (haven't tried) if a fuzz engine can solve this. The hash and the havoc effect makes this a bit difficult, but I think you can manually copy-paste a hex transaction into a fuzz input.

    If you want to do that, I've pushed maflcko/DrahtBot@bef7685#diff-020ce280c4497d9badfc07d9034d427afaaed448e7289604e207e07bb03b654bR565 (untested, experimental)

    Yes, it's hard to let the fuzz engine solve it, previously I hacked the code to manually construct a Tx for connman.ProcessMessagesOnce. I 'll look into your code, thanks!

  17. HowHsu commented at 4:09 PM on June 12, 2026: contributor

    Jup.

    Going further, I don't know (haven't tried) if a fuzz engine can solve this. The hash and the havoc effect makes this a bit difficult, but I think you can manually copy-paste a hex transaction into a fuzz input.

    If you want to do that, I've pushed maflcko/DrahtBot@bef7685#diff-020ce280c4497d9badfc07d9034d427afaaed448e7289604e207e07bb03b654bR565 (untested, experimental)

    Using your tool (plus a small local modification), I found the following input:

     74780066e604002000702b01c0004200ccf6020008005c5f02000000000101748d0a3aa658ffd81616ff63f7037a1e0bcc72b5f9bc051c903d990de2350a150000000000ffffffff0118ee052a010000002200204ae81572f06e1b88fd5c5ced7a1a000945432e83e1551e6f721ee9c00b8cc33260010151000000005c5f013a65786302ac87ffff07ffffff7c0000030301fb03ffff043b00747505000100ff0200001d0002d9270c0000fefefefafe81333461504b030104000000fb000161706900000000000000ff000000
    
    

    It successfully reaches the point where a transaction is accepted into the mempool. The input depends on my local changes, though, so it's currently 205 bytes. I'll push the code tomorrow so the result can be reproduced.

  18. HowHsu renamed this:
    fuzz: assert the reused mempool stays empty in process_message(s)
    fuzz: exercise the transaction-handling path in process_message
    on Jun 14, 2026
  19. HowHsu force-pushed on Jun 14, 2026
  20. HowHsu commented at 2:31 PM on June 14, 2026: contributor

    Hi @maflcko,

    I ran into a problem with process_messages: adding a ConsumeBool() for the IBD state invalidates all existing process_messages corpus entries. What's the usual practice for handling this?

  21. HowHsu force-pushed on Jun 14, 2026
  22. DrahtBot added the label CI failed on Jun 14, 2026
  23. DrahtBot removed the label CI failed on Jun 14, 2026
  24. maflcko commented at 6:10 AM on June 15, 2026: member

    Usually that is not a problem and just ignored, because the hope is that the fuzz engines will re-discover everything.

    However, it should also be possible to manually transform all fuzz inputs with the same tool/concept I shared above. Instead of manually injecting a bool in the right place in a single fuzz input, one would have to do it for all fuzz inputs. It should be possible with a script, but I've not yet written it and thus never tried it.

  25. maflcko commented at 6:39 AM on June 15, 2026: member

    I've pushed an update to the inverse_fdp, so that the main.rs file is stand-alone and only holds fn main. Now, it should be trivial to modify the C++ code to write to stdout (when run on all old fuzz inputs) the correct main.rs file with a main function, which will then write (when run) the new correct fuzz input format for all fuzz inputs to a new folder.

  26. HowHsu marked this as a draft on Jun 15, 2026
  27. in src/test/fuzz/process_message.cpp:147 in 3fa4c3591e
     143 | @@ -137,8 +144,9 @@ FUZZ_TARGET(process_message, .init = initialize_process_message)
     144 |      node.validation_signals->SyncWithValidationInterfaceQueue();
     145 |      node.validation_signals->UnregisterValidationInterface(node.peerman.get());
     146 |      node.connman->StopNodes();
     147 | -    if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())) {
     148 | -        // Reuse the global chainman, but reset it when it is dirty
     149 | +    if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size()) || node.mempool->size() > 0) {
    


    Crypt-iQ commented at 1:28 PM on June 15, 2026:

    || node.mempool->size() > 0 branch won't be taken and will lead to non-determinism if a tx is added & removed and the mempool size doesn't change (I forget how this can happen, maybe with time-based eviction?). Also, the rng needs to be reset as creating a fresh mempool will still use the dirty rng state. See for reference the cmpctblock harness: it fixes the first problem by comparing mempool sequence numbers, and the second problem by calling MakeRandDeterministicDANGEROUS.


    HowHsu commented at 7:41 AM on June 17, 2026:

    Checked that, you are right, thanks, I'll update it later.


    HowHsu commented at 3:25 AM on June 18, 2026:

    Updated.

  28. HowHsu force-pushed on Jun 17, 2026
  29. HowHsu renamed this:
    fuzz: exercise the transaction-handling path in process_message
    fuzz: exercise the transaction-handling path in process_message(s)
    on Jun 17, 2026
  30. HowHsu marked this as ready for review on Jun 18, 2026
  31. in src/test/fuzz/process_message.cpp:130 in 88f10bdc3b outdated
     124 | @@ -125,6 +125,10 @@ FUZZ_TARGET(process_message, .init = initialize_process_message)
     125 |      connman.FlushSendBuffer(p2p_node);
     126 |      (void)connman.ReceiveMsgFrom(p2p_node, std::move(net_msg));
     127 |  
     128 | +    if (fuzzed_data_provider.ConsumeBool()) {
     129 | +        chainman.JumpOutOfIbd();
     130 | +    }
    


    maflcko commented at 7:30 AM on June 18, 2026:

    88f10bdc3bcc6ae8383ede05a525d0e3c1db22e1: I don't understand this commit message. It claims:

    The targets keep the node in IBD via ResetIbd()

    However, the target code literally says "any time to successfully reset ibd" from commit fa0a864b383a794910cdb215bb836dae12357bc6. So this seems like either a bug was added in commit fa0a864b383a794910cdb215bb836dae12357bc6 (or later), and you are effectively reverting that commit.

    It could make sense to properly explain where the bug was added and then also mention that this reverts that commit?


    maflcko commented at 7:30 AM on June 18, 2026:

    Also, there are other fuzz targets that use this pattern, so if they are affected by the same bug, they should be fixed as well.


    HowHsu commented at 11:02 AM on June 18, 2026:

    88f10bd: I don't understand this commit message. It claims:

    The targets keep the node in IBD via ResetIbd()

    However, the target code literally says "any time to successfully reset ibd" from commit fa0a864. So this seems like either a bug was added in commit fa0a864 (or later), and you are effectively reverting that commit.

    It could make sense to properly explain where the bug was added and then also mention that this reverts that commit?

    Updated.


    HowHsu commented at 11:05 AM on June 18, 2026:

    Also, there are other fuzz targets that use this pattern, so if they are affected by the same bug, they should be fixed as well.

    I think it can be addressed in a separate PR which focuses on this bug in other targets.


    maflcko commented at 2:38 PM on June 18, 2026:

    The other target is using the exact same pattern. For every change you submit, please think about minimizing the review load.

    It is easier to review one change once, instead of reviewing it several times, spread over time and several pull requests


    HowHsu commented at 3:38 PM on June 18, 2026:

    The other target is using the exact same pattern. For every change you submit, please think about minimizing the review load.

    It is easier to review one change once, instead of reviewing it several times, spread over time and several pull requests

    Got it. I'll take a closer look at the !IBD code path changes in p2p_handshake tomorrow. It seems those changes aren't specifically for exercising the transaction-handling path, so the PR title and description will likely need to be updated accordingly.


    HowHsu commented at 2:24 PM on June 23, 2026:

    The other target is using the exact same pattern. For every change you submit, please think about minimizing the review load.

    It is easier to review one change once, instead of reviewing it several times, spread over time and several pull requests

    Updated

  32. in src/test/fuzz/process_message.cpp:54 in 0c8546b343 outdated
      48 | @@ -44,19 +49,28 @@ namespace {
      49 |  TestingSetup* g_setup;
      50 |  std::string_view LIMIT_TO_MESSAGE_TYPE{};
      51 |  
      52 | -void ResetChainman(TestingSetup& setup)
      53 | +void ResetChainmanAndMempool(TestingSetup& setup)
      54 |  {
      55 | -    SetMockTime(setup.m_node.chainman->GetParams().GenesisBlock().Time());
      56 | +    SetMockTime(Params().GenesisBlock().Time());
    


    maflcko commented at 7:30 AM on June 18, 2026:

    nit: This seems like the wrong style change for a few reasons:

    • When there is a params local, the local should be preferred, not the global Params().
    • This function is duplicated thrice, so maybe a std::vector<std::pair<COutPoint, CAmount>> ResetChainmanAndMempool(TestingSetup& setup) should sit in src/test/util/validation.h. (This refactor can be done in a preparatory commit).
    • Mixing the SetMockTime global mutator with the FakeNodeClock below seems confusing. Ideally only one is used. I haven't tested this, but my recommendation would be:
    auto& GetFakeNodeClock(){
    static FakeNodeClock g_fake_clock{};
    return g_fake_clock;
    }
    

    and then call this helper in the init and in the target code.


    HowHsu commented at 3:14 PM on June 18, 2026:
    • Mixing the SetMockTime global mutator with the FakeNodeClock below seems confusing. Ideally only one is used. I haven't tested this, but my recommendation would be:
    auto& GetFakeNodeClock(){
    static FakeNodeClock g_fake_clock{};
    return g_fake_clock;
    }
    

    and then call this helper in the init and in the target code.

    This sounds like a separate topic. Could we first replace SetMockTime with FakeNodeClock in this mempool reset PR, and then do the broader FakeNodeClock refactoring in a follow-up PR?


    HowHsu commented at 2:26 PM on June 23, 2026:

    nit: This seems like the wrong style change for a few reasons:

    • When there is a params local, the local should be preferred, not the global Params().
    • This function is duplicated thrice, so maybe a std::vector<std::pair<COutPoint, CAmount>> ResetChainmanAndMempool(TestingSetup& setup) should sit in src/test/util/validation.h. (This refactor can be done in a preparatory commit).
    • Mixing the SetMockTime global mutator with the FakeNodeClock below seems confusing. Ideally only one is used. I haven't tested this, but my recommendation would be:
    auto& GetFakeNodeClock(){
    static FakeNodeClock g_fake_clock{};
    return g_fake_clock;
    }
    

    and then call this helper in the init and in the target code.

    I've done this and included it as a commit in this PR, though it still feels like it belongs to a separate topic.

  33. maflcko commented at 7:31 AM on June 18, 2026: member

    left two style nits

  34. HowHsu requested review from Crypt-iQ on Jun 18, 2026
  35. HowHsu force-pushed on Jun 18, 2026
  36. HowHsu force-pushed on Jun 18, 2026
  37. DrahtBot added the label CI failed on Jun 18, 2026
  38. HowHsu force-pushed on Jun 18, 2026
  39. in src/test/fuzz/process_messages.cpp:81 in 5e0c1903ec
      77 |      chainman.ResetIbd();
      78 |      chainman.DisableNextWrite();
      79 |  
      80 | +    if (fuzzed_data_provider.ConsumeBool()) {
      81 | +        chainman.JumpOutOfIbd();
      82 | +    }
    


    maflcko commented at 3:08 PM on June 18, 2026:

    nit: I wonder if this is the right place. Commit fa0a864b383a794910cdb215bb836dae12357bc6 had this logic in the loop, which would allow a few messages under IBD, and other messages outside IBD.


    HowHsu commented at 2:23 PM on June 23, 2026:

    Updated

  40. DrahtBot removed the label CI failed on Jun 19, 2026
  41. HowHsu force-pushed on Jun 20, 2026
  42. HowHsu force-pushed on Jun 20, 2026
  43. DrahtBot added the label CI failed on Jun 20, 2026
  44. DrahtBot removed the label CI failed on Jun 20, 2026
  45. HowHsu requested review from maflcko on Jun 22, 2026
  46. DrahtBot added the label Needs rebase on Jun 27, 2026
  47. fuzz: share a single FakeNodeClock across fuzz targets
    FakeNodeClock is a LimitOne type, so init/reset helpers and target code cannot
    each hold their own instance at the same time. Add a GetFakeNodeClock() that
    returns a single process-wide FakeNodeClock, and route all fuzz targets through
    it, replacing their local FakeNodeClock and the genesis-time SetMockTime() calls
    in the chainman reset/init helpers.
    
    The shared instance is initialized with 0s rather than a default-constructed
    FakeNodeClock{}: the latter's Now<NodeSeconds>() reads the system clock and would
    trip the g_used_system_time check the first time GetFakeNodeClock() is
    constructed inside a target body (i.e. for targets whose .init does not already
    construct it, such as p2p_headers_presync and p2p_handshake).
    ef18579f10
  48. test: add helper to reset chainman and mempool
    Move the reusable chainman/mempool reset logic from the cmpctblock fuzz target
    into test util validation helpers. Return the mature coinbase outputs so
    cmpctblock can keep using them while other callers can ignore the result.
    dedc8c4bf4
  49. fuzz: let the test input toggle IBD in the p2p fuzz targets
    process_message, process_messages and p2p_handshake keep the node
    in IBD via ResetIbd(), so net_processing returns early at the
    IsInitialBlockDownload() check and never exercises the non-IBD code
    paths.
    
    For process_message(s), this used to be controllable from the fuzz
    input via a jump_out_of_ibd bool that called JumpOutOfIbd().
    Commit fa0a864b (#20908) dropped that toggle because mocktime made
    it redundant: back then IsInitialBlockDownload() evaluated the tip
    timestamp against the current mocked time on every call, so
    SetMockTime(ConsumeTime(...)) alone could drive the node in and out
    of IBD. That stopped working in #34253, which turned
    IsInitialBlockDownload() into a lock-free read of the cached
    m_cached_is_ibd flag, latched only by UpdateIBDStatus() on chain
    activation; mocktime no longer affects it.
    
    p2p_handshake never had such a toggle. Since it only performs the
    version handshake and no block is connected, UpdateIBDStatus() is
    never reached, so it is permanently stuck in IBD too.
    
    Restore the toggle. In process_message (a single message), the bool
    is consumed last. In process_messages and p2p_handshake, the toggle
    lives inside the message loop, as it did before fa0a864b. A latched
    bool decides before each message whether to call JumpOutOfIbd(), so
    some messages can be handled under IBD and the rest after leaving it.
    
    Fixes: #34253
    ec5c1666b1
  50. fuzz: reset the rng before rebuilding the chainman
    The dirty global chainman is rebuilt between iterations via ResetChainman(),
    which consumes the global PRNG. It runs at the end of an iteration, by which
    point the rng has already been used, so without resetting it the rebuilt state
    would depend on the dirty rng and not be reproducible across iterations.
    9312ba2b73
  51. fuzz: reset the reused mempool in process_message(s)
    The mempool is reused across iterations of process_message and
    process_messages, but it was never reset, so a transaction accepted
    into the mempool by one input would leak into the next iteration. A
    random payload can produce a transaction that passes
    AcceptToMemoryPool.
    
    Rebuild the mempool together with the chainman in
    ResetChainmanAndMempool(), called when either the block index grew or
    the mempool was modified. The mempool is bound to the chainman at
    chainman construction (ChainstateLoadOptions::mempool), so the two
    must be reset together: the mempool is rebuilt first, then the
    chainman, which re-binds the fresh mempool. This mirrors the
    cmpctblock harness.
    
    Compare the mempool sequence number rather than its size to detect a
    dirty mempool: a transaction may be added and removed within a single
    iteration, leaving the size unchanged while the mempool state is still
    dirty.
    49bfa1b446
  52. HowHsu force-pushed on Jun 29, 2026
  53. HowHsu commented at 1:37 PM on June 29, 2026: contributor

    Rebased the branch

  54. DrahtBot removed the label Needs rebase on Jun 29, 2026


maflckoCrypt-iQ

Labels

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-07-02 03:50 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me