mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-01 00:34:01 +02:00
kernel: Expose context-free block validation
This introduces a context-free validation entry point for full blocks in the kernel C and C++ APIs. * Add `btck_block_check`, a C function that wraps `CheckBlock` and runs header and body checks for a `btck_Block` using `btck_ConsensusParams`. Callers provide a `btck_BlockValidationState` to receive the result and supply a `btck_BlockCheckFlags` bitmask to control POW and merkle-root verification. * Add `btck_BlockCheckFlags` in the C API, plus the corresponding `BlockCheckFlags` scoped enum in the C++ wrapper, including a `*_ALL` convenience value. * Add `Block::Check()` to the C++ wrapper to mirror the new C function and return a bool while filling a `BlockValidationState`. * Add a test `(btck_check_block_context_free)` that verifies a known valid mainnet block passes with `BlockCheckFlags::ALL` and that truncated block data fails deserialization. Co-authored-by: yuvicc <yuvichh01@gmail.com>
This commit is contained in:
@@ -1129,6 +1129,19 @@ btck_Block* btck_block_copy(const btck_Block* block)
|
||||
return btck_Block::copy(block);
|
||||
}
|
||||
|
||||
int btck_block_check(const btck_Block* block, const btck_ConsensusParams* consensus_params, btck_BlockCheckFlags flags, btck_BlockValidationState* validation_state)
|
||||
{
|
||||
auto& state = btck_BlockValidationState::get(validation_state);
|
||||
state = BlockValidationState{};
|
||||
|
||||
const bool check_pow = (flags & btck_BlockCheckFlags_POW) != 0;
|
||||
const bool check_merkle = (flags & btck_BlockCheckFlags_MERKLE) != 0;
|
||||
|
||||
const bool result = CheckBlock(*btck_Block::get(block), state, btck_ConsensusParams::get(consensus_params), /*fCheckPOW=*/check_pow, /*fCheckMerkleRoot=*/check_merkle);
|
||||
|
||||
return result ? 1 : 0;
|
||||
}
|
||||
|
||||
size_t btck_block_count_transactions(const btck_Block* block)
|
||||
{
|
||||
return btck_Block::get(block)->vtx.size();
|
||||
|
||||
@@ -1263,6 +1263,40 @@ BITCOINKERNEL_API btck_Block* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_create
|
||||
BITCOINKERNEL_API btck_Block* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_copy(
|
||||
const btck_Block* block) BITCOINKERNEL_ARG_NONNULL(1);
|
||||
|
||||
/** Bitflags to control context-free block checks (optional). */
|
||||
typedef uint32_t btck_BlockCheckFlags;
|
||||
#define btck_BlockCheckFlags_BASE ((btck_BlockCheckFlags)0) //!< run the base context-free block checks only
|
||||
#define btck_BlockCheckFlags_POW ((btck_BlockCheckFlags)(1U << 0)) //!< run CheckProofOfWork via CheckBlockHeader
|
||||
#define btck_BlockCheckFlags_MERKLE ((btck_BlockCheckFlags)(1U << 1)) //!< verify merkle root (and mutation detection)
|
||||
#define btck_BlockCheckFlags_ALL ((btck_BlockCheckFlags)(btck_BlockCheckFlags_POW | btck_BlockCheckFlags_MERKLE)) //!< enable all optional context-free block checks
|
||||
|
||||
/**
|
||||
* @brief Perform context-free validation checks on a btck_Block.
|
||||
*
|
||||
* Runs the base context-free block checks (size limits, coinbase structure,
|
||||
* transaction checks, and sigop limits) using the supplied
|
||||
* btck_ConsensusParams. The proof-of-work and merkle-root checks are optional
|
||||
* and can be toggled via @p flags. Note that this does not include any
|
||||
* transaction script, timestamps, order, or other checks that may require more
|
||||
* context.
|
||||
*
|
||||
* @param[in] block Non-null, btck_Block to validate.
|
||||
* @param[in] consensus_params Non-null, btck_ConsensusParams for validation.
|
||||
* @param[in] flags Bitmask of btck_BlockCheckFlags controlling the
|
||||
* optional POW and merkle-root checks. Use
|
||||
* btck_BlockCheckFlags_BASE to run only the base
|
||||
* checks.
|
||||
* @param[in,out] validation_state Non-null, previously created with
|
||||
* btck_block_validation_state_create and updated
|
||||
* in-place with the validation result.
|
||||
* @return 1 if the btck_Block passed the checks, 0 otherwise.
|
||||
*/
|
||||
BITCOINKERNEL_API int BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_check(
|
||||
const btck_Block* block,
|
||||
const btck_ConsensusParams* consensus_params,
|
||||
btck_BlockCheckFlags flags,
|
||||
btck_BlockValidationState* validation_state) BITCOINKERNEL_ARG_NONNULL(1, 2, 4);
|
||||
|
||||
/**
|
||||
* @brief Count the number of transactions contained in a block.
|
||||
*
|
||||
|
||||
@@ -97,6 +97,13 @@ enum class ScriptVerificationFlags : btck_ScriptVerificationFlags {
|
||||
ALL = btck_ScriptVerificationFlags_ALL
|
||||
};
|
||||
|
||||
enum class BlockCheckFlags : btck_BlockCheckFlags {
|
||||
BASE = btck_BlockCheckFlags_BASE,
|
||||
POW = btck_BlockCheckFlags_POW,
|
||||
MERKLE = btck_BlockCheckFlags_MERKLE,
|
||||
ALL = btck_BlockCheckFlags_ALL
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_bitmask_enum : std::false_type {
|
||||
};
|
||||
@@ -105,6 +112,10 @@ template <>
|
||||
struct is_bitmask_enum<ScriptVerificationFlags> : std::true_type {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct is_bitmask_enum<BlockCheckFlags> : std::true_type {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept BitmaskEnum = is_bitmask_enum<T>::value;
|
||||
|
||||
@@ -370,6 +381,7 @@ public:
|
||||
class PrecomputedTransactionData;
|
||||
class Transaction;
|
||||
class TransactionOutput;
|
||||
class BlockValidationState;
|
||||
|
||||
template <typename Derived>
|
||||
class ScriptPubkeyApi
|
||||
@@ -803,6 +815,10 @@ public:
|
||||
return TransactionView{btck_block_get_transaction_at(get(), index)};
|
||||
}
|
||||
|
||||
bool Check(const ConsensusParamsView& consensus_params,
|
||||
BlockCheckFlags flags,
|
||||
BlockValidationState& state) const;
|
||||
|
||||
MAKE_RANGE_METHOD(Transactions, Block, &Block::CountTransactions, &Block::GetTransaction, *this)
|
||||
|
||||
BlockHash GetHash() const
|
||||
@@ -958,6 +974,13 @@ public:
|
||||
BlockValidationState(const BlockValidationStateView& view) : Handle{view} {}
|
||||
};
|
||||
|
||||
inline bool Block::Check(const ConsensusParamsView& consensus_params,
|
||||
BlockCheckFlags flags,
|
||||
BlockValidationState& state) const
|
||||
{
|
||||
return btck_block_check(get(), consensus_params.get(), static_cast<btck_BlockCheckFlags>(flags), state.get()) == 1;
|
||||
}
|
||||
|
||||
class ValidationInterface
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -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"}};
|
||||
|
||||
Reference in New Issue
Block a user