Merge bitcoin/bitcoin#35496: kernel: add btck_set_mock_time for testing time-dependent paths

156f2c6c49 kernel: add `btck_set_mock_time` for testing time-dependent paths (stringintech)

Pull request description:

  Some kernel paths read the current time (e.g. header validation's future-time check, and the `btck_SynchronizationState` carried by `btck_NotifyBlockTip` / `btck_NotifyHeaderTip` callbacks), which makes them awkward to exercise deterministically in tests. This PR exposes `btck_set_mock_time` as a wrapper over the existing `SetMockTime`, mirroring what the node has via the `setmocktime` RPC.

  Prior IRC discussion: https://gnusha.org/bitcoin-kernel/2026-06-04.log

ACKs for top commit:
  josibake:
    ACK 156f2c6c49
  purpleKarrot:
    ACK 156f2c6c49
  achow101:
    ACK 156f2c6c49
  janb84:
    ACK 156f2c6c49
  sedited:
    ACK 156f2c6c49

Tree-SHA512: da61faab0477fdc0de0e16420f228923d57733c42ef91b8947ef576e43abc8a1b78b9c1fa2ae60550ed5fd545f4d1c714dad0191d4442d4063112031ef7d1027
This commit is contained in:
Ava Chow
2026-08-11 13:18:13 -07:00
4 changed files with 90 additions and 0 deletions

View File

@@ -1414,3 +1414,51 @@ BOOST_AUTO_TEST_CASE(btck_transaction_check_tests)
"ffffffff00ffffffff000100000000000000000000000000000000000000000000000000000000"
"00000000000000ffffffff010000000000000000015100000000");
}
class KernelMockTime
{
public:
explicit KernelMockTime(std::chrono::seconds timestamp) { set(timestamp); }
~KernelMockTime()
{
set_mock_time(std::chrono::seconds{0});
}
KernelMockTime(const KernelMockTime&) = delete;
KernelMockTime& operator=(const KernelMockTime&) = delete;
void set(std::chrono::seconds timestamp) { set_mock_time(timestamp); }
};
BOOST_AUTO_TEST_CASE(btck_set_mock_time_tests)
{
// Out-of-range timestamps throw
BOOST_CHECK_EXCEPTION(set_mock_time(std::chrono::seconds{-1}), std::runtime_error, HasReason("timestamp out of range"));
constexpr std::chrono::seconds max_time{std::numeric_limits<uint32_t>::max()};
BOOST_CHECK_EXCEPTION(set_mock_time(max_time + std::chrono::seconds{1}), std::runtime_error, HasReason("timestamp out of range"));
// Confirm the mock time actually takes effect by exercising the header future-time check
auto test_directory{TestDirectory{"set_mock_time_test_bitcoin_kernel"}};
auto notifications{std::make_shared<TestKernelNotifications>()};
auto context{create_context(notifications, ChainType::REGTEST)};
auto chainman{create_chainman(
test_directory, /*reindex=*/false, /*wipe_chainstate=*/false,
/*block_tree_db_in_memory=*/true, /*chainstate_db_in_memory=*/true, context)};
Block block{hex_string_to_byte_vec(REGTEST_BLOCK_DATA[0])};
BlockHeader header{block.GetHeader()};
const std::chrono::seconds block_time{header.Timestamp()};
// With the time set 3h before the header, the kernel must see the header as >2h in the future and reject it
KernelMockTime mock_time{block_time - std::chrono::hours{3}};
BlockValidationState future_state{chainman->ProcessBlockHeader(header)};
BOOST_CHECK(future_state.GetValidationMode() == ValidationMode::INVALID);
BOOST_CHECK(future_state.GetBlockValidationResult() == BlockValidationResult::TIME_FUTURE);
// At the upper bound the header is far in the past and must be accepted; this also
// confirms the future-time check's "now + 2h" computation doesn't overflow when now is at its max.
mock_time.set(max_time);
BlockValidationState ok_state{chainman->ProcessBlockHeader(header)};
BOOST_CHECK(ok_state.GetValidationMode() == ValidationMode::VALID);
BOOST_CHECK(ok_state.GetBlockValidationResult() == BlockValidationResult::UNSET);
}