mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-06 13:47:56 +02:00
Merge bitcoin/bitcoin#33908: kernel: add context‑free block validation API (btck_check_block_context_free) with POW/Merkle flags
0587c56091kernel: Expose context-free block validation (w0xlt)71f827c3c2kernel: 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-ACK0587c56091achow101: ACK0587c56091sedited: ACK0587c56091Tree-SHA512: 6bd53e4964909335d1f2fee30ff96c95a8dd2c84bcdfe11c50ba369301822e5dea9bbe2376bb6d6b4652875152071eb0446657042b00429f29581da4fcea71a9
This commit is contained in:
@@ -50,6 +50,10 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Consensus {
|
||||
struct Params;
|
||||
} // namespace Consensus
|
||||
|
||||
using kernel::ChainstateRole;
|
||||
using util::ImmediateTaskRunner;
|
||||
|
||||
@@ -496,6 +500,7 @@ struct btck_TransactionOutPoint: Handle<btck_TransactionOutPoint, COutPoint> {};
|
||||
struct btck_Txid: Handle<btck_Txid, Txid> {};
|
||||
struct btck_PrecomputedTransactionData : Handle<btck_PrecomputedTransactionData, PrecomputedTransactionData> {};
|
||||
struct btck_BlockHeader: Handle<btck_BlockHeader, CBlockHeader> {};
|
||||
struct btck_ConsensusParams: Handle<btck_ConsensusParams, Consensus::Params> {};
|
||||
|
||||
btck_Transaction* btck_transaction_create(const void* raw_transaction, size_t raw_transaction_len)
|
||||
{
|
||||
@@ -823,6 +828,11 @@ btck_ChainParameters* btck_chain_parameters_copy(const btck_ChainParameters* cha
|
||||
return btck_ChainParameters::copy(chain_parameters);
|
||||
}
|
||||
|
||||
const btck_ConsensusParams* btck_chain_parameters_get_consensus_params(const btck_ChainParameters* chain_parameters)
|
||||
{
|
||||
return btck_ConsensusParams::ref(&btck_ChainParameters::get(chain_parameters).GetConsensus());
|
||||
}
|
||||
|
||||
void btck_chain_parameters_destroy(btck_ChainParameters* chain_parameters)
|
||||
{
|
||||
delete chain_parameters;
|
||||
@@ -1119,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();
|
||||
|
||||
@@ -225,6 +225,11 @@ typedef struct btck_Block btck_Block;
|
||||
*/
|
||||
typedef struct btck_BlockValidationState btck_BlockValidationState;
|
||||
|
||||
/**
|
||||
* Opaque data structure for holding the Consensus Params.
|
||||
*/
|
||||
typedef struct btck_ConsensusParams btck_ConsensusParams;
|
||||
|
||||
/**
|
||||
* Opaque data structure for holding the currently known best-chain associated
|
||||
* with a chainstate.
|
||||
@@ -864,6 +869,17 @@ BITCOINKERNEL_API btck_ChainParameters* BITCOINKERNEL_WARN_UNUSED_RESULT btck_ch
|
||||
BITCOINKERNEL_API btck_ChainParameters* BITCOINKERNEL_WARN_UNUSED_RESULT btck_chain_parameters_copy(
|
||||
const btck_ChainParameters* chain_parameters) BITCOINKERNEL_ARG_NONNULL(1);
|
||||
|
||||
/**
|
||||
* @brief Get btck_ConsensusParams from btck_ChainParameters. The returned
|
||||
* btck_ConsensusParams pointer is valid only for the lifetime of the
|
||||
* btck_ChainParameters object and must not be destroyed by the caller.
|
||||
*
|
||||
* @param[in] chain_parameters Non-null.
|
||||
* @return The btck_ConsensusParams.
|
||||
*/
|
||||
BITCOINKERNEL_API const btck_ConsensusParams* BITCOINKERNEL_WARN_UNUSED_RESULT btck_chain_parameters_get_consensus_params(
|
||||
const btck_ChainParameters* chain_parameters) BITCOINKERNEL_ARG_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Destroy the chain parameters.
|
||||
*/
|
||||
@@ -1247,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
|
||||
@@ -777,6 +789,12 @@ public:
|
||||
: Handle{header} {}
|
||||
};
|
||||
|
||||
class ConsensusParamsView : public View<btck_ConsensusParams>
|
||||
{
|
||||
public:
|
||||
explicit ConsensusParamsView(const btck_ConsensusParams* ptr) : View{ptr} {}
|
||||
};
|
||||
|
||||
class Block : public Handle<btck_Block, btck_block_copy, btck_block_destroy>
|
||||
{
|
||||
public:
|
||||
@@ -797,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
|
||||
@@ -952,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:
|
||||
@@ -971,6 +1000,11 @@ class ChainParams : public Handle<btck_ChainParameters, btck_chain_parameters_co
|
||||
public:
|
||||
ChainParams(ChainType chain_type)
|
||||
: Handle{btck_chain_parameters_create(static_cast<btck_ChainType>(chain_type))} {}
|
||||
|
||||
ConsensusParamsView GetConsensusParams() const
|
||||
{
|
||||
return ConsensusParamsView{btck_chain_parameters_get_consensus_params(get())};
|
||||
}
|
||||
};
|
||||
|
||||
class ContextOptions : public UniqueHandle<btck_ContextOptions, btck_context_options_destroy>
|
||||
|
||||
@@ -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