kernel: add btck_set_mock_time for testing time-dependent paths

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 commit exposes `btck_set_mock_time` as a wrapper over the existing `SetMockTime`, mirroring what the node has via the `setmocktime` RPC.
This commit is contained in:
stringintech
2026-06-09 18:04:05 +03:30
parent 577999c2ce
commit 156f2c6c49
4 changed files with 90 additions and 0 deletions

View File

@@ -1386,3 +1386,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);
}