From b30e15f4329ab0ee6bb5c4c1d1f6067be364c59e Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Fri, 31 May 2024 13:34:00 +0200 Subject: [PATCH] kernel: Add functions for the block validation state to C header These allow for the interpretation of the data in a `BlockChecked` validation interface callback. The validation state passed through `BlockChecked` is the source of truth for the validity of a block (the mode). It is also useful to get richer information in case a block failed to validate (the result). --- src/kernel/bitcoinkernel.cpp | 34 +++++++++++++++++++++ src/kernel/bitcoinkernel.h | 42 ++++++++++++++++++++++++++ src/kernel/bitcoinkernel_wrapper.h | 28 +++++++++++++++++ src/test/kernel/test_kernel.cpp | 48 +++++++++++++++++++++++++++++- 4 files changed, 151 insertions(+), 1 deletion(-) diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp index 9c96a1c4900..e115518df81 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 b41ff90e744..e1f99b53151 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 7105f9e95d6..249f98a3684 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(btck_block_validation_state_get_validation_mode(m_state)); + } + + BlockValidationResult GetBlockValidationResult() const + { + return static_cast(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 4cf3df7f57e..0e21cc5689f 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