Merge bitcoin/bitcoin#33908: kernel: add context‑free block validation API (btck_check_block_context_free) with POW/Merkle flags

0587c56091 kernel: Expose context-free block validation (w0xlt)
71f827c3c2 kernel: Expose consensus parameters (`btck_ConsensusParams`) (w0xlt)

Pull request description:

  This PR exposes Bitcoin Core’s context‑free block checks to library users via a new C API entry point, `btck_check_block_context_free`.

  Callers can validate a block’s structure (size/weight, coinbase rules, per‑tx context‑free checks) and optionally re‑run Proof‑of‑Work and Merkle‑root verification without touching chainstate, the block index, or the UTXO set.

  Rationale
  Clients embedding the kernel need a pure block sanity check without requiring node state or disk writes (candidate block validation, for example). This API offers that surface in a single call, with optional PoW/Merkle toggles to avoid redundant work when the header has already been validated or when Merkle verification is deferred.

ACKs for top commit:
  yuvicc:
    re-ACK 0587c56091
  achow101:
    ACK 0587c56091
  sedited:
    ACK 0587c56091

Tree-SHA512: 6bd53e4964909335d1f2fee30ff96c95a8dd2c84bcdfe11c50ba369301822e5dea9bbe2376bb6d6b4652875152071eb0446657042b00429f29581da4fcea71a9
This commit is contained in:
Ava Chow
2026-04-02 15:28:07 -07:00
4 changed files with 167 additions and 0 deletions

View File

@@ -10,6 +10,7 @@
#include <boost/test/included/unit_test.hpp>
#include <test/kernel/block_data.h>
#include <test/util/common.h>
#include <charconv>
#include <cstdint>
@@ -913,6 +914,65 @@ void chainman_mainnet_validation_test(TestDirectory& test_directory)
BOOST_CHECK(!new_block);
}
BOOST_AUTO_TEST_CASE(btck_check_block_context_free)
{
constexpr size_t MERKLE_ROOT_OFFSET{4 + 32};
constexpr size_t NBITS_OFFSET{4 + 32 + 32 + 4};
constexpr size_t COINBASE_PREVOUT_N_OFFSET{4 + 32 + 32 + 4 + 4 + 4 + 1 + 4 + 1 + 32};
// Mainnet block 1
auto raw_block = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000");
// Context-free block checks still need consensus params for the optional
// proof-of-work validation path.
ChainParams mainnet_params{ChainType::MAINNET};
auto consensus_params = mainnet_params.GetConsensusParams();
Block block{raw_block};
BlockValidationState state;
BOOST_CHECK(block.Check(consensus_params, BlockCheckFlags::BASE, state));
BOOST_CHECK(state.GetValidationMode() == ValidationMode::VALID);
BOOST_CHECK(block.Check(consensus_params, BlockCheckFlags::ALL, state));
BOOST_CHECK(state.GetValidationMode() == ValidationMode::VALID);
auto bad_merkle_block_data = raw_block;
bad_merkle_block_data[MERKLE_ROOT_OFFSET] ^= std::byte{0x01};
Block bad_merkle_block{bad_merkle_block_data};
BOOST_CHECK(!bad_merkle_block.Check(consensus_params, BlockCheckFlags::MERKLE, state));
BOOST_CHECK(state.GetValidationMode() == ValidationMode::INVALID);
BOOST_CHECK(state.GetBlockValidationResult() == BlockValidationResult::MUTATED);
BOOST_CHECK(bad_merkle_block.Check(consensus_params, BlockCheckFlags::BASE, state));
BOOST_CHECK(state.GetValidationMode() == ValidationMode::VALID);
auto bad_pow_block_data = raw_block;
bad_pow_block_data[NBITS_OFFSET + 3] = std::byte{0x1c};
Block bad_pow_block{bad_pow_block_data};
BOOST_CHECK(!bad_pow_block.Check(consensus_params, BlockCheckFlags::POW, state));
BOOST_CHECK(state.GetValidationMode() == ValidationMode::INVALID);
BOOST_CHECK(state.GetBlockValidationResult() == BlockValidationResult::INVALID_HEADER);
BOOST_CHECK(bad_pow_block.Check(consensus_params, BlockCheckFlags::MERKLE, state));
BOOST_CHECK(state.GetValidationMode() == ValidationMode::VALID);
auto bad_base_block_data = raw_block;
bad_base_block_data[COINBASE_PREVOUT_N_OFFSET] = std::byte{0x00};
Block bad_base_block{bad_base_block_data};
BOOST_CHECK(!bad_base_block.Check(consensus_params, BlockCheckFlags::BASE, state));
BOOST_CHECK(state.GetValidationMode() == ValidationMode::INVALID);
BOOST_CHECK(state.GetBlockValidationResult() == BlockValidationResult::CONSENSUS);
// Test with invalid truncated block data.
auto truncated_block_data = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299");
BOOST_CHECK_EXCEPTION(Block{truncated_block_data}, std::runtime_error,
HasReason{"failed to instantiate btck object"});
}
BOOST_AUTO_TEST_CASE(btck_chainman_mainnet_tests)
{
auto test_directory{TestDirectory{"mainnet_test_bitcoin_kernel"}};