kernel: Add validation interface to C header

This adds the infrastructure required to process validation events. For
now the external validation interface only has support for the
`BlockChecked` , `NewPoWValidBlock`, `BlockConnected`, and
`BlockDisconnected` callback. Support for the other internal
validation interface methods can be added in the future.

The validation interface follows an architecture for defining its
callbacks and ownership that is similar to the notifications.

The task runner is created internally with a context, which itself
internally creates a unique ValidationSignals object. When the user
creates a new chainstate manager the validation signals are internally
passed to the chainstate manager through the context.

A validation interface can register for validation events with a
context. Internally the passed in validation interface is registerd with
the validation signals of a context.

The callbacks block any further validation execution when they are
called. It is up to the user to either multiplex them, or use them
otherwise in a multithreaded mechanism to make processing the validation
events non-blocking.

I.e. for a synchronous mechanism, the user executes instructions
directly at the end of the callback function:

```mermaid
sequenceDiagram
    participant V as Validation
    participant C as Callback
    V->>C: Call callback
    Note over C: Process event (blocks)
    C-->>V: Return
    Note over V: Validation resumes

```

To avoid blocking, the user can submit the data to e.g. a worker thread
or event manager, so processing happens asynchronously:

```mermaid
sequenceDiagram
    participant V as Validation
    participant C as Callback
    participant W as Worker Thread
    V->>C: Call callback
    C->>W: Submit to worker thread
    C-->>V: Return immediately
    Note over V: Validation continues
    Note over W: Process event async
```
This commit is contained in:
TheCharlatan
2024-05-30 22:59:48 +02:00
parent d27e27758d
commit aa262da7bc
4 changed files with 219 additions and 4 deletions

View File

@@ -604,6 +604,34 @@ public:
virtual void FatalErrorHandler(std::string_view error) {}
};
class BlockValidationState
{
private:
const btck_BlockValidationState* m_state;
public:
BlockValidationState(const btck_BlockValidationState* state) : m_state{state} {}
BlockValidationState(const BlockValidationState&) = delete;
BlockValidationState& operator=(const BlockValidationState&) = delete;
BlockValidationState(BlockValidationState&&) = delete;
BlockValidationState& operator=(BlockValidationState&&) = delete;
};
class ValidationInterface
{
public:
virtual ~ValidationInterface() = default;
virtual void BlockChecked(Block block, const BlockValidationState state) {}
virtual void PowValidBlock(BlockTreeEntry entry, Block block) {}
virtual void BlockConnected(Block block, BlockTreeEntry entry) {}
virtual void BlockDisconnected(Block block, BlockTreeEntry entry) {}
};
class ChainParams : public Handle<btck_ChainParameters, btck_chain_parameters_copy, btck_chain_parameters_destroy>
{
public:
@@ -641,6 +669,24 @@ public:
.fatal_error = +[](void* user_data, const char* error, size_t error_len) { (*static_cast<user_type>(user_data))->FatalErrorHandler({error, error_len}); },
});
}
template <typename T>
void SetValidationInterface(std::shared_ptr<T> validation_interface)
{
static_assert(std::is_base_of_v<ValidationInterface, T>);
auto heap_vi = std::make_unique<std::shared_ptr<T>>(std::move(validation_interface));
using user_type = std::shared_ptr<T>*;
btck_context_options_set_validation_interface(
get(),
btck_ValidationInterfaceCallbacks{
.user_data = heap_vi.release(),
.user_data_destroy = +[](void* user_data) { delete static_cast<user_type>(user_data); },
.block_checked = +[](void* user_data, btck_Block* block, const btck_BlockValidationState* state) { (*static_cast<user_type>(user_data))->BlockChecked(Block{block}, BlockValidationState{state}); },
.pow_valid_block = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast<user_type>(user_data))->PowValidBlock(BlockTreeEntry{entry}, Block{block}); },
.block_connected = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast<user_type>(user_data))->BlockConnected(Block{block}, BlockTreeEntry{entry}); },
.block_disconnected = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast<user_type>(user_data))->BlockDisconnected(Block{block}, BlockTreeEntry{entry}); },
});
}
};
class Context : public Handle<btck_Context, btck_context_copy, btck_context_destroy>