Merge bitcoin/bitcoin#35956: fuzz: scope fake clocks to target phases

4e5327bc98 fuzz: refactor: scope fake clocks to target phases (Hao Xu)
e33410d888 fuzz: document arbitrary mocktimes (Hao Xu)

Pull request description:

  Follow-up to #35482 (https://github.com/bitcoin/bitcoin/pull/35482#discussion_r3612852792), addressing a remaining issue with the lifetime of the mock node clock.

  This replaces the process-wide `FakeNodeClock` accessor with scoped clocks in the affected fuzz target initialization and input-processing phases, following the existing `FakeSteadyClock` pattern. The active clock is passed to `ResetChainmanAndMempool()` by reference.

  Tested the affected fuzz targets with `-runs=1`:

  - `cmpctblock`
  - `process_message`
  - `process_messages`
  - `utxo_snapshot`
  - `utxo_snapshot_invalid`

ACKs for top commit:
  maflcko:
    review ACK 4e5327bc98 🚉
  nervana21:
    re-ACK 4e5327bc98

Tree-SHA512: 7763bb2a06e3f33bcae3ad7b43f6d30a231e39197e8d274eadd496da1194fc0178b27cf016b451f36309d9277ce414c85daf734604c85a9deb3803b26b968e6a
This commit is contained in:
merge-script
2026-08-19 15:57:20 +01:00
8 changed files with 23 additions and 25 deletions

View File

@@ -119,12 +119,13 @@ extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept;
void initialize_cmpctblock()
{
FakeNodeClock init_clock{}; // Uses the existing mock time
static const auto testing_setup = MakeNoLogFileContext<TestingSetup>();
g_setup = testing_setup.get();
g_nBits = Params().GenesisBlock().nBits;
// Replace validation_signals before creating chainman and mempool so they use it.
testing_setup->m_node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<ImmediateBackgroundTaskRunner>());
g_mature_coinbase = ResetChainmanAndMempool(*g_setup);
g_mature_coinbase = ResetChainmanAndMempool(*g_setup, init_clock);
}
FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock)
@@ -132,7 +133,7 @@ FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock)
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
GetFakeNodeClock().set(1610000000s);
FakeNodeClock node_clock{1610000000s}; // 2021-01-07, arbitrary
FakeSteadyClock steady_clock;
auto setup = g_setup;
@@ -422,10 +423,10 @@ FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock)
[&]() {
// Set mock time randomly or to tip's time.
if (fuzzed_data_provider.ConsumeBool()) {
GetFakeNodeClock().set(ConsumeTime(fuzzed_data_provider));
node_clock.set(ConsumeTime(fuzzed_data_provider));
} else {
const NodeSeconds tip_time = WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip()->Time());
GetFakeNodeClock().set(tip_time);
node_clock.set(tip_time);
}
sent_net_msg = false;
@@ -478,6 +479,6 @@ FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock)
if (initial_index_size != end_index_size || initial_sequence != end_sequence) {
MakeRandDeterministicDANGEROUS(uint256::ZERO);
g_mature_coinbase = ResetChainmanAndMempool(*g_setup);
g_mature_coinbase = ResetChainmanAndMempool(*g_setup, node_clock);
}
}

View File

@@ -53,7 +53,7 @@ FUZZ_TARGET(p2p_private_broadcast, .init = ::initialize)
connman.Reset();
auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)};
FakeNodeClock clock_ctx{1610000000s};
FakeNodeClock clock_ctx{1610000000s}; // 2021-01-07, arbitrary
FakeSteadyClock steady_clock;
chainman.ResetIbd();
// Sometimes leave IBD: incoming TX processing (the broadcast-abort path)

View File

@@ -48,6 +48,7 @@ extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept;
void initialize_process_message()
{
FakeNodeClock init_clock{}; // Uses the existing mock time
if (const auto val{std::getenv("LIMIT_TO_MESSAGE_TYPE")}) {
LIMIT_TO_MESSAGE_TYPE = val;
Assert(std::count(ALL_NET_MESSAGE_TYPES.begin(), ALL_NET_MESSAGE_TYPES.end(), LIMIT_TO_MESSAGE_TYPE)); // Unknown message type passed
@@ -59,7 +60,7 @@ void initialize_process_message()
{}),
};
g_setup = testing_setup.get();
ResetChainmanAndMempool(*g_setup);
ResetChainmanAndMempool(*g_setup, init_clock);
}
FUZZ_TARGET(process_message, .init = initialize_process_message)
@@ -73,7 +74,7 @@ FUZZ_TARGET(process_message, .init = initialize_process_message)
auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)};
const auto block_index_size{WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())};
const auto initial_sequence{WITH_LOCK(node.mempool->cs, return node.mempool->GetSequence())};
GetFakeNodeClock().set(1610000000s); // 2021-01-07, arbitrary
FakeNodeClock node_clock{1610000000s}; // 2021-01-07, arbitrary
FakeSteadyClock steady_clock;
chainman.ResetIbd();
chainman.DisableNextWrite();
@@ -107,7 +108,7 @@ FUZZ_TARGET(process_message, .init = initialize_process_message)
connman.AddTestNode(p2p_node);
FillNode(fuzzed_data_provider, connman, p2p_node);
GetFakeNodeClock().set(ConsumeTime(fuzzed_data_provider));
node_clock.set(ConsumeTime(fuzzed_data_provider));
CSerializedNetMsg net_msg;
net_msg.m_type = random_message_type;
@@ -136,6 +137,6 @@ FUZZ_TARGET(process_message, .init = initialize_process_message)
if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size()) || initial_sequence != end_sequence) {
// Reuse the global chainman and mempool, but reset them when dirty.
MakeRandDeterministicDANGEROUS(uint256::ZERO);
ResetChainmanAndMempool(*g_setup);
ResetChainmanAndMempool(*g_setup, node_clock);
}
}

View File

@@ -43,13 +43,14 @@ extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept;
void initialize_process_messages()
{
FakeNodeClock init_clock{}; // Uses the existing mock time
static const auto testing_setup{
MakeNoLogFileContext<TestingSetup>(
/*chain_type=*/ChainType::REGTEST,
{}),
};
g_setup = testing_setup.get();
ResetChainmanAndMempool(*g_setup);
ResetChainmanAndMempool(*g_setup, init_clock);
}
FUZZ_TARGET(process_messages, .init = initialize_process_messages)
@@ -63,7 +64,7 @@ FUZZ_TARGET(process_messages, .init = initialize_process_messages)
auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)};
const auto block_index_size{WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())};
const auto initial_sequence{WITH_LOCK(node.mempool->cs, return node.mempool->GetSequence())};
GetFakeNodeClock().set(1610000000s); // 2021-01-07, arbitrary
FakeNodeClock node_clock{1610000000s}; // 2021-01-07, arbitrary
FakeSteadyClock steady_clock;
chainman.ResetIbd();
chainman.DisableNextWrite();
@@ -107,7 +108,7 @@ FUZZ_TARGET(process_messages, .init = initialize_process_messages)
if (jump_out_of_ibd && chainman.IsInitialBlockDownload()) chainman.JumpOutOfIbd();
const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::MESSAGE_TYPE_SIZE).c_str()};
GetFakeNodeClock().set(ConsumeTime(fuzzed_data_provider));
node_clock.set(ConsumeTime(fuzzed_data_provider));
CSerializedNetMsg net_msg;
net_msg.m_type = random_message_type;
@@ -136,6 +137,6 @@ FUZZ_TARGET(process_messages, .init = initialize_process_messages)
if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size()) || initial_sequence != end_sequence) {
// Reuse the global chainman and mempool, but reset them when dirty.
MakeRandDeterministicDANGEROUS(uint256::ZERO);
ResetChainmanAndMempool(*g_setup);
ResetChainmanAndMempool(*g_setup, node_clock);
}
}

View File

@@ -73,7 +73,7 @@ void initialize_chain()
const auto params{CreateChainParams(ArgsManager{}, ChainType::REGTEST)};
static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)};
g_chain = &chain;
GetFakeNodeClock().set(chain.back()->Time());
FakeNodeClock node_clock{chain.back()->Time()};
// Make sure we can generate a valid snapshot.
sanity_check_snapshot();
@@ -104,7 +104,7 @@ void utxo_snapshot_fuzz(FuzzBufferType buffer)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
GetFakeNodeClock().set(ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)); // regtest genesis block timestamp
FakeNodeClock node_clock{ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)}; // regtest genesis block timestamp
auto& setup{*g_setup};
bool dirty_chainman{false}; // Reuse the global chainman, but reset it when it is dirty
auto& chainman{*setup.m_node.chainman};