mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-13 22:41:25 +02:00
kernel: Expose btck_transaction_check consensus function
Add btck_transaction_check() to the libbitcoinkernel C API, exposing context-free transaction consensus validation (consensus/tx_check.h). Introduces btck_TxValidationState with introspection and lifecycle functions. btck_TxValidationResult is exposed for compatibility with existing validation-state APIs, though btck_transaction_check currently reaches only UNSET and CONSENSUS. Includes C++ wrapper and test coverage for btck_transaction_check using test vectors from tx_valid.json / tx_invalid.json.
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <chain.h>
|
||||
#include <coins.h>
|
||||
#include <consensus/tx_check.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <dbwrapper.h>
|
||||
#include <kernel/caches.h>
|
||||
@@ -146,6 +147,7 @@ struct Handle {
|
||||
struct btck_BlockTreeEntry: Handle<btck_BlockTreeEntry, CBlockIndex> {};
|
||||
struct btck_Block : Handle<btck_Block, std::shared_ptr<const CBlock>> {};
|
||||
struct btck_BlockValidationState : Handle<btck_BlockValidationState, BlockValidationState> {};
|
||||
struct btck_TxValidationState : Handle<btck_TxValidationState, TxValidationState> {};
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -1444,3 +1446,49 @@ void btck_block_header_destroy(btck_BlockHeader* header)
|
||||
{
|
||||
delete header;
|
||||
}
|
||||
|
||||
btck_ValidationMode btck_tx_validation_state_get_validation_mode(const btck_TxValidationState* state_)
|
||||
{
|
||||
const auto& state = btck_TxValidationState::get(state_);
|
||||
if (state.IsValid()) return btck_ValidationMode_VALID;
|
||||
if (state.IsInvalid()) return btck_ValidationMode_INVALID;
|
||||
return btck_ValidationMode_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
btck_TxValidationState* btck_tx_validation_state_create()
|
||||
{
|
||||
return btck_TxValidationState::create();
|
||||
}
|
||||
|
||||
btck_TxValidationResult btck_tx_validation_state_get_tx_validation_result(const btck_TxValidationState* state_)
|
||||
{
|
||||
switch (btck_TxValidationState::get(state_).GetResult()) {
|
||||
case TxValidationResult::TX_RESULT_UNSET: return btck_TxValidationResult_UNSET;
|
||||
case TxValidationResult::TX_CONSENSUS: return btck_TxValidationResult_CONSENSUS;
|
||||
case TxValidationResult::TX_INPUTS_NOT_STANDARD: return btck_TxValidationResult_INPUTS_NOT_STANDARD;
|
||||
case TxValidationResult::TX_NOT_STANDARD: return btck_TxValidationResult_NOT_STANDARD;
|
||||
case TxValidationResult::TX_MISSING_INPUTS: return btck_TxValidationResult_MISSING_INPUTS;
|
||||
case TxValidationResult::TX_PREMATURE_SPEND: return btck_TxValidationResult_PREMATURE_SPEND;
|
||||
case TxValidationResult::TX_WITNESS_MUTATED: return btck_TxValidationResult_WITNESS_MUTATED;
|
||||
case TxValidationResult::TX_WITNESS_STRIPPED: return btck_TxValidationResult_WITNESS_STRIPPED;
|
||||
case TxValidationResult::TX_CONFLICT: return btck_TxValidationResult_CONFLICT;
|
||||
case TxValidationResult::TX_MEMPOOL_POLICY: return btck_TxValidationResult_MEMPOOL_POLICY;
|
||||
case TxValidationResult::TX_NO_MEMPOOL: return btck_TxValidationResult_NO_MEMPOOL;
|
||||
case TxValidationResult::TX_RECONSIDERABLE: return btck_TxValidationResult_RECONSIDERABLE;
|
||||
case TxValidationResult::TX_UNKNOWN: return btck_TxValidationResult_UNKNOWN;
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void btck_tx_validation_state_destroy(btck_TxValidationState* state)
|
||||
{
|
||||
delete state;
|
||||
}
|
||||
|
||||
int btck_transaction_check(const btck_Transaction* tx, btck_TxValidationState* validation_state)
|
||||
{
|
||||
auto& state = btck_TxValidationState::get(validation_state);
|
||||
state = TxValidationState{};
|
||||
const bool ok = CheckTransaction(*btck_Transaction::get(tx), state);
|
||||
return ok ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -242,6 +242,14 @@ typedef struct btck_ConsensusParams btck_ConsensusParams;
|
||||
*/
|
||||
typedef struct btck_Chain btck_Chain;
|
||||
|
||||
/**
|
||||
* Opaque data structure for holding the state of a transaction during validation.
|
||||
*
|
||||
* Contains information indicating whether validation was successful, and if not
|
||||
* which step during transaction validation failed.
|
||||
*/
|
||||
typedef struct btck_TxValidationState btck_TxValidationState;
|
||||
|
||||
/**
|
||||
* Opaque data structure for holding a block's spent outputs.
|
||||
*
|
||||
@@ -388,6 +396,25 @@ typedef uint32_t btck_BlockValidationResult;
|
||||
#define btck_BlockValidationResult_TIME_FUTURE ((btck_BlockValidationResult)(7)) //!< block timestamp was > 2 hours in the future (or our clock is bad)
|
||||
#define btck_BlockValidationResult_HEADER_LOW_WORK ((btck_BlockValidationResult)(8)) //!< the block header may be on a too-little-work chain
|
||||
|
||||
/**
|
||||
* Indicates the reason why a transaction failed validation. The subset of
|
||||
* values reachable depends on which validation function was used.
|
||||
*/
|
||||
typedef uint32_t btck_TxValidationResult;
|
||||
#define btck_TxValidationResult_UNSET ((btck_TxValidationResult)(0)) //!< initial value. Tx has not yet been rejected
|
||||
#define btck_TxValidationResult_CONSENSUS ((btck_TxValidationResult)(1)) //!< invalid by consensus rules
|
||||
#define btck_TxValidationResult_INPUTS_NOT_STANDARD ((btck_TxValidationResult)(2)) //!< inputs (covered by txid) failed policy rules
|
||||
#define btck_TxValidationResult_NOT_STANDARD ((btck_TxValidationResult)(3)) //!< otherwise didn't meet local policy rules
|
||||
#define btck_TxValidationResult_MISSING_INPUTS ((btck_TxValidationResult)(4)) //!< transaction was missing some of its inputs
|
||||
#define btck_TxValidationResult_PREMATURE_SPEND ((btck_TxValidationResult)(5)) //!< transaction spends a coinbase too early, or violates locktime/sequence locks
|
||||
#define btck_TxValidationResult_WITNESS_MUTATED ((btck_TxValidationResult)(6)) //!< witness may have been malleated or is prior to SegWit activation
|
||||
#define btck_TxValidationResult_WITNESS_STRIPPED ((btck_TxValidationResult)(7)) //!< transaction is missing a witness
|
||||
#define btck_TxValidationResult_CONFLICT ((btck_TxValidationResult)(8)) //!< tx already in mempool or conflicts with a tx in the chain
|
||||
#define btck_TxValidationResult_MEMPOOL_POLICY ((btck_TxValidationResult)(9)) //!< violated mempool's fee/size/descendant/RBF/etc limits
|
||||
#define btck_TxValidationResult_NO_MEMPOOL ((btck_TxValidationResult)(10)) //!< this node does not have a mempool so can't validate the transaction
|
||||
#define btck_TxValidationResult_RECONSIDERABLE ((btck_TxValidationResult)(11)) //!< fails some policy, but might be acceptable if submitted in a (different) package
|
||||
#define btck_TxValidationResult_UNKNOWN ((btck_TxValidationResult)(12)) //!< transaction was not validated because package failed
|
||||
|
||||
/**
|
||||
* Holds the validation interface callbacks. The user data pointer may be used
|
||||
* to point to user-defined structures to make processing the validation
|
||||
@@ -505,6 +532,40 @@ typedef uint8_t btck_ChainType;
|
||||
#define btck_ChainType_SIGNET ((btck_ChainType)(3))
|
||||
#define btck_ChainType_REGTEST ((btck_ChainType)(4))
|
||||
|
||||
/** @name TxValidationState
|
||||
* Introspection for transaction validation state.
|
||||
*/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Create a new btck_TxValidationState.
|
||||
*/
|
||||
BITCOINKERNEL_API btck_TxValidationState* BITCOINKERNEL_WARN_UNUSED_RESULT btck_tx_validation_state_create();
|
||||
|
||||
/**
|
||||
* Returns the validation mode from an opaque btck_TxValidationState pointer.
|
||||
*/
|
||||
BITCOINKERNEL_API btck_ValidationMode btck_tx_validation_state_get_validation_mode(
|
||||
const btck_TxValidationState* state) BITCOINKERNEL_ARG_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Returns the validation result from an opaque btck_TxValidationState pointer.
|
||||
*
|
||||
* btck_transaction_check currently produces only btck_TxValidationResult_UNSET
|
||||
* for valid transactions and btck_TxValidationResult_CONSENSUS for invalid
|
||||
* ones. Other values remain exposed for forward compatibility with higher-level
|
||||
* validation entry points.
|
||||
*/
|
||||
BITCOINKERNEL_API btck_TxValidationResult btck_tx_validation_state_get_tx_validation_result(
|
||||
const btck_TxValidationState* state) BITCOINKERNEL_ARG_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Destroy the btck_TxValidationState.
|
||||
*/
|
||||
BITCOINKERNEL_API void btck_tx_validation_state_destroy(btck_TxValidationState* state);
|
||||
|
||||
///@}
|
||||
|
||||
/** @name Transaction
|
||||
* Functions for working with transactions.
|
||||
*/
|
||||
@@ -606,6 +667,27 @@ BITCOINKERNEL_API uint32_t BITCOINKERNEL_WARN_UNUSED_RESULT btck_transaction_get
|
||||
BITCOINKERNEL_API const btck_Txid* BITCOINKERNEL_WARN_UNUSED_RESULT btck_transaction_get_txid(
|
||||
const btck_Transaction* transaction) BITCOINKERNEL_ARG_NONNULL(1);
|
||||
|
||||
/**
|
||||
* @brief Run context-free consensus validation on a btck_Transaction.
|
||||
*
|
||||
* Performs basic structural consensus checks (consensus/tx_check::CheckTransaction)
|
||||
* without requiring blockchain state.
|
||||
*
|
||||
* @param[in] tx Non-null, the transaction to validate.
|
||||
* @param[out] validation_state Non-null, previously created with
|
||||
* btck_tx_validation_state_create. Reset on
|
||||
* entry (any prior contents are overwritten)
|
||||
* and updated in-place with the validation
|
||||
* result before this function returns.
|
||||
* @return 1 if valid, 0 if invalid.
|
||||
* @note Only btck_TxValidationResult_UNSET and
|
||||
* btck_TxValidationResult_CONSENSUS are
|
||||
* reachable via this function.
|
||||
*/
|
||||
BITCOINKERNEL_API int BITCOINKERNEL_WARN_UNUSED_RESULT btck_transaction_check(
|
||||
const btck_Transaction* tx,
|
||||
btck_TxValidationState* validation_state) BITCOINKERNEL_ARG_NONNULL(1, 2);
|
||||
|
||||
/**
|
||||
* Destroy the transaction.
|
||||
*/
|
||||
|
||||
@@ -79,6 +79,22 @@ enum class BlockValidationResult : btck_BlockValidationResult {
|
||||
HEADER_LOW_WORK = btck_BlockValidationResult_HEADER_LOW_WORK
|
||||
};
|
||||
|
||||
enum class TxValidationResult : btck_TxValidationResult {
|
||||
UNSET = btck_TxValidationResult_UNSET,
|
||||
CONSENSUS = btck_TxValidationResult_CONSENSUS,
|
||||
INPUTS_NOT_STANDARD = btck_TxValidationResult_INPUTS_NOT_STANDARD,
|
||||
NOT_STANDARD = btck_TxValidationResult_NOT_STANDARD,
|
||||
MISSING_INPUTS = btck_TxValidationResult_MISSING_INPUTS,
|
||||
PREMATURE_SPEND = btck_TxValidationResult_PREMATURE_SPEND,
|
||||
WITNESS_MUTATED = btck_TxValidationResult_WITNESS_MUTATED,
|
||||
WITNESS_STRIPPED = btck_TxValidationResult_WITNESS_STRIPPED,
|
||||
CONFLICT = btck_TxValidationResult_CONFLICT,
|
||||
MEMPOOL_POLICY = btck_TxValidationResult_MEMPOOL_POLICY,
|
||||
NO_MEMPOOL = btck_TxValidationResult_NO_MEMPOOL,
|
||||
RECONSIDERABLE = btck_TxValidationResult_RECONSIDERABLE,
|
||||
UNKNOWN = btck_TxValidationResult_UNKNOWN
|
||||
};
|
||||
|
||||
enum class ScriptVerifyStatus : btck_ScriptVerifyStatus {
|
||||
OK = btck_ScriptVerifyStatus_OK,
|
||||
ERROR_INVALID_FLAGS_COMBINATION = btck_ScriptVerifyStatus_ERROR_INVALID_FLAGS_COMBINATION,
|
||||
@@ -999,6 +1015,28 @@ inline bool Block::Check(const ConsensusParamsView& consensus_params,
|
||||
return btck_block_check(get(), consensus_params.get(), static_cast<btck_BlockCheckFlags>(flags), state.get()) == 1;
|
||||
}
|
||||
|
||||
class TxValidationState : public UniqueHandle<btck_TxValidationState, btck_tx_validation_state_destroy>
|
||||
{
|
||||
public:
|
||||
using UniqueHandle::UniqueHandle; // inherit ctor
|
||||
explicit TxValidationState() : UniqueHandle{btck_tx_validation_state_create()} {}
|
||||
|
||||
ValidationMode GetValidationMode() const
|
||||
{
|
||||
return static_cast<ValidationMode>(btck_tx_validation_state_get_validation_mode(get()));
|
||||
}
|
||||
|
||||
TxValidationResult GetTxValidationResult() const
|
||||
{
|
||||
return static_cast<TxValidationResult>(btck_tx_validation_state_get_tx_validation_result(get()));
|
||||
}
|
||||
};
|
||||
|
||||
inline bool CheckTransaction(const Transaction& tx, TxValidationState& state)
|
||||
{
|
||||
return btck_transaction_check(tx.get(), state.get()) == 1;
|
||||
}
|
||||
|
||||
class ValidationInterface
|
||||
{
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user