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).
This commit is contained in:
TheCharlatan
2024-05-31 13:34:00 +02:00
parent aa262da7bc
commit b30e15f432
4 changed files with 151 additions and 1 deletions

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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