kernel: Add functions for the block validation state to C header
What changed, and why it matters
This commit adds new read-only functions to Bitcoin Core's public kernel library so outside programs can ask 'Was this block valid?' and 'Why was it invalid?' when they receive a callback. It does not change any validation rules, fix a bug, or alter how Bitcoin itself processes blocks. It is a straightforward API expansion with no apparent security relevance.
No security action required. Treat as a normal API/feature addition.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch exposes two new C functions in the bitcoinkernel API—btck_block_validation_state_get_validation_mode and btck_block_validation_state_get_block_validation_result—plus matching C++ wrapper helpers and test output. They map internal BlockValidationState values (IsValid/IsInvalid and BlockValidationResult enum cases) to stable public constants. The implementation is a thin, read-only translation layer with no mutation of state, no memory allocation, and no change to consensus logic.
Changed components
src/kernel/bitcoinkernel.cppsrc/kernel/bitcoinkernel.hsrc/kernel/bitcoinkernel_wrapper.hsrc/test/kernel/test_kernel.cppInspect captured patch +151 / −1
diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp
index 9c96a1c4..e115518d 100644
--- a/src/kernel/bitcoinkernel.cpp
+++ b/src/kernel/bitcoinkernel.cpp
@@ -769,6 +769,40 @@ void btck_context_destroy(btck_Context* context)
delete context;
}
+btck_ValidationMode btck_block_validation_state_get_validation_mode(const btck_BlockValidationState* block_validation_state_)
+{
+ auto& block_validation_state = btck_BlockValidationState::get(block_validation_state_);
+ if (block_validation_state.IsValid()) return btck_ValidationMode_VALID;
+ if (block_validation_state.IsInvalid()) return btck_ValidationMode_INVALID;
+ return btck_ValidationMode_INTERNAL_ERROR;
+}
+
+btck_BlockValidationResult btck_block_validation_state_get_block_validation_result(const btck_BlockValidationState* block_validation_state_)
+{
+ auto& block_validation_state = btck_BlockValidationState::get(block_validation_state_);
+ switch (block_validation_state.GetResult()) {
+ case BlockValidationResult::BLOCK_RESULT_UNSET:
+ return btck_BlockValidationResult_UNSET;
+ case BlockValidationResult::BLOCK_CONSENSUS:
+ return btck_BlockValidationResult_CONSENSUS;
+ case BlockValidationResult::BLOCK_CACHED_INVALID:
+ return btck_BlockValidationResult_CACHED_INVALID;
+ case BlockValidationResult::BLOCK_INVALID_HEADER:
+ return btck_BlockValidationResult_INVALID_HEADER;
+ case BlockValidationResult::BLOCK_MUTATED:
+ return btck_BlockValidationResult_MUTATED;
+ case BlockValidationResult::BLOCK_MISSING_PREV:
+ return btck_BlockValidationResult_MISSING_PREV;
+ case BlockValidationResult::BLOCK_INVALID_PREV:
+ return btck_BlockValidationResult_INVALID_PREV;
+ case BlockValidationResult::BLOCK_TIME_FUTURE:
+ return btck_BlockValidationResult_TIME_FUTURE;
+ case BlockValidationResult::BLOCK_HEADER_LOW_WORK:
+ return btck_BlockValidationResult_HEADER_LOW_WORK;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
+}
+
btck_ChainstateManagerOptions* btck_chainstate_manager_options_create(const btck_Context* context, const char* data_dir, size_t data_dir_len, const char* blocks_dir, size_t blocks_dir_len)
{
try {
diff --git a/src/kernel/bitcoinkernel.h b/src/kernel/bitcoinkernel.h
index b41ff90e..e1f99b53 100644
--- a/src/kernel/bitcoinkernel.h
+++ b/src/kernel/bitcoinkernel.h
@@ -249,6 +249,29 @@ typedef void (*btck_ValidationInterfacePoWValidBlock)(void* user_data, btck_Bloc
typedef void (*btck_ValidationInterfaceBlockConnected)(void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry);
typedef void (*btck_ValidationInterfaceBlockDisconnected)(void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry);
+/**
+ * Whether a validated data structure is valid, invalid, or an error was
+ * encountered during processing.
+ */
+typedef uint8_t btck_ValidationMode;
+#define btck_ValidationMode_VALID ((btck_ValidationMode)(0))
+#define btck_ValidationMode_INVALID ((btck_ValidationMode)(1))
+#define btck_ValidationMode_INTERNAL_ERROR ((btck_ValidationMode)(2))
+
+/**
+ * A granular "reason" why a block was invalid.
+ */
+typedef uint32_t btck_BlockValidationResult;
+#define btck_BlockValidationResult_UNSET ((btck_BlockValidationResult)(0)) //!< initial value. Block has not yet been rejected
+#define btck_BlockValidationResult_CONSENSUS ((btck_BlockValidationResult)(1)) //!< invalid by consensus rules (excluding any below reasons)
+#define btck_BlockValidationResult_CACHED_INVALID ((btck_BlockValidationResult)(2)) //!< this block was cached as being invalid and we didn't store the reason why
+#define btck_BlockValidationResult_INVALID_HEADER ((btck_BlockValidationResult)(3)) //!< invalid proof of work or time too old
+#define btck_BlockValidationResult_MUTATED ((btck_BlockValidationResult)(4)) //!< the block's data didn't match the data committed to by the PoW
+#define btck_BlockValidationResult_MISSING_PREV ((btck_BlockValidationResult)(5)) //!< We don't have the previous block the checked one is built on
+#define btck_BlockValidationResult_INVALID_PREV ((btck_BlockValidationResult)(6)) //!< A block this one builds on is invalid
+#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
+
/**
* Holds the validation interface callbacks. The user data pointer may be used
* to point to user-defined structures to make processing the validation
@@ -967,6 +990,25 @@ BITCOINKERNEL_API void btck_block_destroy(btck_Block* block);
///@}
+/** @name BlockValidationState
+ * Functions for working with block validation states.
+ */
+///@{
+
+/**
+ * Returns the validation mode from an opaque block validation state pointer.
+ */
+BITCOINKERNEL_API btck_ValidationMode btck_block_validation_state_get_validation_mode(
+ const btck_BlockValidationState* block_validation_state) BITCOINKERNEL_ARG_NONNULL(1);
+
+/**
+ * Returns the validation result from an opaque block validation state pointer.
+ */
+BITCOINKERNEL_API btck_BlockValidationResult btck_block_validation_state_get_block_validation_result(
+ const btck_BlockValidationState* block_validation_state) BITCOINKERNEL_ARG_NONNULL(1);
+
+///@}
+
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
diff --git a/src/kernel/bitcoinkernel_wrapper.h b/src/kernel/bitcoinkernel_wrapper.h
index 7105f9e9..249f98a3 100644
--- a/src/kernel/bitcoinkernel_wrapper.h
+++ b/src/kernel/bitcoinkernel_wrapper.h
@@ -58,6 +58,24 @@ enum class Warning : btck_Warning {
LARGE_WORK_INVALID_CHAIN = btck_Warning_LARGE_WORK_INVALID_CHAIN
};
+enum class ValidationMode : btck_ValidationMode {
+ VALID = btck_ValidationMode_VALID,
+ INVALID = btck_ValidationMode_INVALID,
+ INTERNAL_ERROR = btck_ValidationMode_INTERNAL_ERROR
+};
+
+enum class BlockValidationResult : btck_BlockValidationResult {
+ UNSET = btck_BlockValidationResult_UNSET,
+ CONSENSUS = btck_BlockValidationResult_CONSENSUS,
+ CACHED_INVALID = btck_BlockValidationResult_CACHED_INVALID,
+ INVALID_HEADER = btck_BlockValidationResult_INVALID_HEADER,
+ MUTATED = btck_BlockValidationResult_MUTATED,
+ MISSING_PREV = btck_BlockValidationResult_MISSING_PREV,
+ INVALID_PREV = btck_BlockValidationResult_INVALID_PREV,
+ TIME_FUTURE = btck_BlockValidationResult_TIME_FUTURE,
+ HEADER_LOW_WORK = btck_BlockValidationResult_HEADER_LOW_WORK
+};
+
enum class ScriptVerifyStatus : btck_ScriptVerifyStatus {
OK = btck_ScriptVerifyStatus_OK,
ERROR_INVALID_FLAGS_COMBINATION = btck_ScriptVerifyStatus_ERROR_INVALID_FLAGS_COMBINATION,
@@ -616,6 +634,16 @@ public:
BlockValidationState& operator=(const BlockValidationState&) = delete;
BlockValidationState(BlockValidationState&&) = delete;
BlockValidationState& operator=(BlockValidationState&&) = delete;
+
+ ValidationMode GetValidationMode() const
+ {
+ return static_cast<ValidationMode>(btck_block_validation_state_get_validation_mode(m_state));
+ }
+
+ BlockValidationResult GetBlockValidationResult() const
+ {
+ return static_cast<BlockValidationResult>(btck_block_validation_state_get_block_validation_result(m_state));
+ }
};
class ValidationInterface
diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp
index 4cf3df7f..0e21cc56 100644
--- a/src/test/kernel/test_kernel.cpp
+++ b/src/test/kernel/test_kernel.cpp
@@ -131,7 +131,53 @@ class TestValidationInterface : public ValidationInterface
public:
void BlockChecked(Block block, const BlockValidationState state) override
{
- std::cout << "Block checked." << std::endl;
+ std::cout << "Block checked: ";
+
+ auto mode{state.GetValidationMode()};
+ switch (mode) {
+ case ValidationMode::VALID: {
+ std::cout << "Valid block" << std::endl;
+ return;
+ }
+ case ValidationMode::INVALID: {
+ std::cout << "Invalid block: ";
+ auto result{state.GetBlockValidationResult()};
+ switch (result) {
+ case BlockValidationResult::UNSET:
+ std::cout << "initial value. Block has not yet been rejected" << std::endl;
+ break;
+ case BlockValidationResult::HEADER_LOW_WORK:
+ std::cout << "the block header may be on a too-little-work chain" << std::endl;
+ break;
+ case BlockValidationResult::CONSENSUS:
+ std::cout << "invalid by consensus rules (excluding any below reasons)" << std::endl;
+ break;
+ case BlockValidationResult::CACHED_INVALID:
+ std::cout << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
+ break;
+ case BlockValidationResult::INVALID_HEADER:
+ std::cout << "invalid proof of work or time too old" << std::endl;
+ break;
+ case BlockValidationResult::MUTATED:
+ std::cout << "the block's data didn't match the data committed to by the PoW" << std::endl;
+ break;
+ case BlockValidationResult::MISSING_PREV:
+ std::cout << "We don't have the previous block the checked one is built on" << std::endl;
+ break;
+ case BlockValidationResult::INVALID_PREV:
+ std::cout << "A block this one builds on is invalid" << std::endl;
+ break;
+ case BlockValidationResult::TIME_FUTURE:
+ std::cout << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
+ break;
+ }
+ return;
+ }
+ case ValidationMode::INTERNAL_ERROR: {
+ std::cout << "Internal error" << std::endl;
+ return;
+ }
+ }
}
void BlockConnected(Block block, BlockTreeEntry entry) override
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.