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

@@ -31,8 +31,10 @@
#include <util/fs.h>
#include <util/result.h>
#include <util/signalinterrupt.h>
#include <util/task_runner.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <cassert>
#include <cstddef>
@@ -47,6 +49,8 @@
#include <utility>
#include <vector>
using util::ImmediateTaskRunner;
// Define G_TRANSLATION_FUN symbol in libbitcoinkernel library so users of the
// library aren't required to export this symbol
extern const std::function<std::string(const char*)> G_TRANSLATION_FUN{nullptr};
@@ -135,6 +139,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> {};
namespace {
@@ -317,10 +322,65 @@ public:
}
};
class KernelValidationInterface final : public CValidationInterface
{
public:
btck_ValidationInterfaceCallbacks m_cbs;
explicit KernelValidationInterface(const btck_ValidationInterfaceCallbacks vi_cbs) : m_cbs{vi_cbs} {}
~KernelValidationInterface()
{
if (m_cbs.user_data && m_cbs.user_data_destroy) {
m_cbs.user_data_destroy(m_cbs.user_data);
}
m_cbs.user_data = nullptr;
m_cbs.user_data_destroy = nullptr;
}
protected:
void BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& stateIn) override
{
if (m_cbs.block_checked) {
m_cbs.block_checked(m_cbs.user_data,
btck_Block::copy(btck_Block::ref(&block)),
btck_BlockValidationState::ref(&stateIn));
}
}
void NewPoWValidBlock(const CBlockIndex* pindex, const std::shared_ptr<const CBlock>& block) override
{
if (m_cbs.pow_valid_block) {
m_cbs.pow_valid_block(m_cbs.user_data,
btck_Block::copy(btck_Block::ref(&block)),
btck_BlockTreeEntry::ref(pindex));
}
}
void BlockConnected(ChainstateRole role, const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override
{
if (m_cbs.block_connected) {
m_cbs.block_connected(m_cbs.user_data,
btck_Block::copy(btck_Block::ref(&block)),
btck_BlockTreeEntry::ref(pindex));
}
}
void BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override
{
if (m_cbs.block_disconnected) {
m_cbs.block_disconnected(m_cbs.user_data,
btck_Block::copy(btck_Block::ref(&block)),
btck_BlockTreeEntry::ref(pindex));
}
}
};
struct ContextOptions {
mutable Mutex m_mutex;
std::unique_ptr<const CChainParams> m_chainparams GUARDED_BY(m_mutex);
std::shared_ptr<KernelNotifications> m_notifications GUARDED_BY(m_mutex);
std::shared_ptr<KernelValidationInterface> m_validation_interface GUARDED_BY(m_mutex);
};
class Context
@@ -332,8 +392,12 @@ public:
std::unique_ptr<util::SignalInterrupt> m_interrupt;
std::unique_ptr<ValidationSignals> m_signals;
std::unique_ptr<const CChainParams> m_chainparams;
std::shared_ptr<KernelValidationInterface> m_validation_interface;
Context(const ContextOptions* options, bool& sane)
: m_context{std::make_unique<kernel::Context>()},
m_interrupt{std::make_unique<util::SignalInterrupt>()}
@@ -346,6 +410,11 @@ public:
if (options->m_notifications) {
m_notifications = options->m_notifications;
}
if (options->m_validation_interface) {
m_signals = std::make_unique<ValidationSignals>(std::make_unique<ImmediateTaskRunner>());
m_validation_interface = options->m_validation_interface;
m_signals->RegisterSharedValidationInterface(m_validation_interface);
}
}
if (!m_chainparams) {
@@ -360,6 +429,13 @@ public:
sane = false;
}
}
~Context()
{
if (m_signals) {
m_signals->UnregisterSharedValidationInterface(m_validation_interface);
}
}
};
//! Helper struct to wrap the ChainstateManager-related Options
@@ -374,7 +450,8 @@ struct ChainstateManagerOptions {
: m_chainman_options{ChainstateManager::Options{
.chainparams = *context->m_chainparams,
.datadir = data_dir,
.notifications = *context->m_notifications}},
.notifications = *context->m_notifications,
.signals = context->m_signals.get()}},
m_blockman_options{node::BlockManager::Options{
.chainparams = *context->m_chainparams,
.blocks_dir = blocks_dir,
@@ -654,6 +731,12 @@ void btck_context_options_set_notifications(btck_ContextOptions* options, btck_N
btck_ContextOptions::get(options).m_notifications = std::make_shared<KernelNotifications>(notifications);
}
void btck_context_options_set_validation_interface(btck_ContextOptions* options, btck_ValidationInterfaceCallbacks vi_cbs)
{
LOCK(btck_ContextOptions::get(options).m_mutex);
btck_ContextOptions::get(options).m_validation_interface = std::make_shared<KernelValidationInterface>(vi_cbs);
}
void btck_context_options_destroy(btck_ContextOptions* options)
{
delete options;