#35557 kernel, validation: Add btck_chainstate_manager_set_clock_time

full analysis

https://github.com/bitcoin/bitcoin/pull/35557 · ryanofsky · +258/-99 in 31 files, 6 commits · labels: Validation, Kernel

Goal

  • Allow kernel users and multi-instance tests to mock time per instance without relying on global state
  • Prevent kernel validation code from accidentally calling nondeterministic host system clocks

Adds btck_chainstate_manager_set_clock_time to the kernel C API and introduces ChainstateManager::Now as the unified clock injection point across validation paths. Moves NodeClock::now into an isolated translation unit omitted from the kernel library to catch accidental nondeterministic clock calls at link time. Refactors mempool time representation to use chrono time points and fixes a timing race in IBD test assertions.

Problem: Validation code and kernel consumers previously relied on the global NodeClock, preventing kernel applications from executing deterministic block and header validation independently across multiple chainstate manager instances.

Category: Kernel (libbitcoinkernel) (#5 of 18)

P2 · new feature

  • P3 because kernel consumers need deterministic validation without depending on process-global state
  • Instance-scoped time refines the kernel architecture beyond the existing global mock time

Removes global NodeClock state from validation within libbitcoinkernel, fulfilling a core goal of the kernel project to enable multi-instance, side-effect-free library usage.

Membership: Adds btck_chainstate_manager_set_clock_time to the kernel public C API and bitcoinkernel wrapper.

Factors: security/stability 1, bug 0, performance 0, user value 2, leverage 2

Category: Validation (#17 of 48)

P3 · cleanup

  • P3 because routing time through validation logic cleans up scattered direct clock queries
  • Fixes an edge-case race condition in chainstate manager initial block download tests

Unifies time fetching across validation checks into ChainstateManager without changing consensus logic, and resolves an intermittent timing race in the IBD status unit test.

Membership: Changes ChainstateManager to inject validation time via ChainstateManager::Now across IBD, header, and tip verification paths.

Factors: security/stability 0, bug 1, performance 0, user value 0, leverage 1

Category: Utilities (logging, arguments, libraries) (#32 of 66)

P3 · cleanup

  • P3 because isolating nondeterministic clock definitions enforces link-time safety for the kernel
  • Prevents the kernel library from inadvertently calling host system time

Restructures time utility compilation to enforce determinism boundaries at link time, preventing kernel source files from accidentally calling nondeterministic clock methods.

Membership: Modifies util/time.h and util/time.cpp, and introduces util/time_nondet.cpp to restrict NodeClock::now linking.

Factors: security/stability 0, bug 0, performance 0, user value 0, leverage 2

Reviewability: Ready

  • Ready to review: branch builds cleanly, passes CI, and is actively maintained across rebases

The branch is rebased on master, clean of merge conflicts, passing all CI checks, and active.

Author status: Active; author rebased over conflicting PRs and force-pushed repeatedly through September 2026.

Open concerns:

  • Maflcko asked whether this PR remains relevant following the merge of global mock-time PR 35496, and noted a conceptual conflict with PR 35906.

Resolved concerns:

  • Chrono conversions and casts in validation time handling were streamlined to native time points per maflcko's review.
  • Stickies-v proposed a btck_Clock callback abstraction, which author explored in an alternate branch but retained the setter as more direct for application state.
  • Sedited requested test coverage exercising the scoped clock, which the author added in the kernel unit tests and utxo_total_supply fuzz target.

Agreement: Strong

  • Positive sentiment with constructive review on typing, testing, and clock ergonomics
  • Supported keeping the changes unified and requested scoped clock tests (sedited)
  • Commended the test race fix and suggested using a fake clock (seduless)
  • Questioned whether this is still necessary after global mock time merged (maflcko)

Strong: sedited supports the per-instance setter approach; stickies-v's API suggestion was answered without objection.

Sedited provided explicit conceptual backing for the scoped setter approach, and the author addressed stickies-v's alternative proposal with experimental code and detailed rationale.

  • sedited (2026-06-21): 'if we set it like here, it can be moved any number of times. I think I prefer that, as it seems to overlap more closely with the current global mechanics.'
  • stickies-v (2026-06-23): suggested btck_Clock callback API; author answered on 2026-06-29 with prototype and trade-off comparison.

Objections:

ReviewerKindHarmStatusBlockingAuthor repliedQuote
stickies-vinterfaceManaging separate clock timestamps across multiple objects could be awkward and error-prone compared to a shared clock object.resolvednoyes2026-06-23 'This could get a bit annoying and potentially dangerous when users need to start managing clocks across multiple places.'
Settled: 2026-06-29 'My instinct is that it doesn't because if you're creating multiple chainstate managers... setting the time directly on the relevant objects' (author explanation, no further pushback)
maflckocorrectnessdirect access to test-only global mock time may cause silent bugs if mocktime was not used and side-steps g_used_system_time sanitizerresolvednoyes2026-06-18: 'the call to GetMockTime here looks wrong for several reasons: It directly access the test-only global of the mock time, which may be 0, and thus may lead to silent bugs if mocktime was not used here? It side-steps the g_used_system_time runtime sanitizer.'
Settled: 2026-06-18: 'Makes sense, switched GetMockTime() -> Now()'
maflckousefulnessopennono2026-08-17: 'That one was merged, so is this here still relevant? Also, this conflicts with https://github.com/bitcoin/bitcoin/pull/35906#discussion_r3726539086'

Support:

  • sedited: Supports the per-chainstate-manager setter approach as flexible for test setups and consistent with existing mock-time workflows.

Participants: maflcko (objection), sedited (support), stickies-v (objection), seduless (neutral)

State derived from the lists: substantive support, no open objection (sedited)

Review verdicts (DrahtBot): 0

Dependencies

Enables:

  • #35906 First steps towards a stateless, side-effect free validation library

Files

126 lines under test/bench/ci.

  • src/validation.cpp +29/-29
  • src/test/kernel/test_kernel.cpp +35/-0
  • src/test/rbf_tests.cpp +17/-17
  • src/kernel/bitcoinkernel.h +28/-0
  • src/txmempool.h +19/-5
  • src/validation.h +21/-1
  • src/txmempool.cpp +8/-8
  • src/util/time.h +13/-3
  • src/util/time_nondet.cpp +14/-0
  • src/node/mempool_persist.cpp +9/-2
  • src/kernel/bitcoinkernel.cpp +10/-0
  • src/test/fuzz/tx_pool.cpp +5/-5
  • src/test/validation_chainstatemanager_tests.cpp +9/-1
  • src/test/fuzz/utxo_total_supply.cpp +6/-3
  • src/kernel/mempool_entry.h +5/-3
  • src/kernel/bitcoinkernel_wrapper.h +7/-0
  • src/chain.h +4/-2
  • src/test/fuzz/package_eval.cpp +3/-3
  • src/test/util/txmempool.cpp +3/-3
  • src/util/time.cpp +2/-4
  • src/bench/blockencodings.cpp +1/-1
  • src/bench/mempool_ephemeral_spends.cpp +1/-1
  • src/bench/mempool_eviction.cpp +1/-1
  • src/bench/mempool_stress.cpp +1/-1
  • src/bench/rpc_mempool.cpp +1/-1
  • src/node/chainstate.cpp +1/-1
  • src/rpc/mempool.cpp +1/-1
  • src/test/fuzz/rbf.cpp +1/-1
  • src/test/fuzz/util/mempool.cpp +1/-1
  • src/test/util/setup_common.cpp +1/-1
  • src/util/CMakeLists.txt +1/-0

Card

This PR adds btck_chainstate_manager_set_clock_time to the kernel API, replacing global NodeClock access in validation with a scoped ChainstateManager::Now injection point. It also separates nondeterministic clock methods into a distinct translation unit to enforce at link time that libbitcoinkernel code does not depend on system time. The work allows external library consumers to run deterministic validation across multiple chainstate instances without interfering with global state. Review is active and ready, with substantive support from sedited and resolved discussion on alternative clock APIs, though maflcko questioned whether the PR remains necessary after the merge of global mock time in PR 35496.

Data

dossier JSON · extract JSON · model openrouter/google/gemini-3.8-flash, generated 2026-09-17T21:34, confidence high, input hash 2899c0a1372aee07