kernel: Add notifications context option to C header

The notifications are used for notifying on connected blocks and on
warning and fatal error conditions.

The user of the C header may define callbacks that gets passed to the
internal notification object in the
`kernel_NotificationInterfaceCallbacks` struct.

Each of the callbacks take a `user_data` argument that gets populated
from the `user_data` value in the struct. It can be used to recreate the
structure containing the callbacks on the user's side, or to give the
callbacks additional contextual information.
This commit is contained in:
TheCharlatan
2024-06-03 14:51:29 +02:00
parent 9e1bac4585
commit c62f657ba3
4 changed files with 270 additions and 3 deletions

View File

@@ -12,6 +12,7 @@
#include <kernel/context.h>
#include <kernel/cs_main.h>
#include <kernel/notifications_interface.h>
#include <kernel/warning.h>
#include <logging.h>
#include <primitives/transaction.h>
#include <script/interpreter.h>
@@ -23,6 +24,7 @@
#include <util/result.h>
#include <util/signalinterrupt.h>
#include <util/translation.h>
#include <validation.h>
#include <cassert>
#include <cstddef>
@@ -36,6 +38,8 @@
#include <utility>
#include <vector>
class CBlockIndex;
// 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};
@@ -120,6 +124,12 @@ struct Handle {
}
};
} // namespace
struct btck_BlockTreeEntry: Handle<btck_BlockTreeEntry, CBlockIndex> {};
namespace {
BCLog::Level get_bclog_level(btck_LogLevel level)
{
switch (level) {
@@ -176,6 +186,30 @@ BCLog::LogFlags get_bclog_flag(btck_LogCategory category)
assert(false);
}
btck_SynchronizationState cast_state(SynchronizationState state)
{
switch (state) {
case SynchronizationState::INIT_REINDEX:
return btck_SynchronizationState_INIT_REINDEX;
case SynchronizationState::INIT_DOWNLOAD:
return btck_SynchronizationState_INIT_DOWNLOAD;
case SynchronizationState::POST_INIT:
return btck_SynchronizationState_POST_INIT;
} // no default case, so the compiler can warn about missing cases
assert(false);
}
btck_Warning cast_btck_warning(kernel::Warning warning)
{
switch (warning) {
case kernel::Warning::UNKNOWN_NEW_RULES_ACTIVATED:
return btck_Warning_UNKNOWN_NEW_RULES_ACTIVATED;
case kernel::Warning::LARGE_WORK_INVALID_CHAIN:
return btck_Warning_LARGE_WORK_INVALID_CHAIN;
} // no default case, so the compiler can warn about missing cases
assert(false);
}
struct LoggingConnection {
std::unique_ptr<std::list<std::function<void(const std::string&)>>::iterator> m_connection;
void* m_user_data;
@@ -224,9 +258,61 @@ struct LoggingConnection {
}
};
class KernelNotifications final : public kernel::Notifications
{
private:
btck_NotificationInterfaceCallbacks m_cbs;
public:
KernelNotifications(btck_NotificationInterfaceCallbacks cbs)
: m_cbs{cbs}
{
}
~KernelNotifications()
{
if (m_cbs.user_data && m_cbs.user_data_destroy) {
m_cbs.user_data_destroy(m_cbs.user_data);
}
m_cbs.user_data_destroy = nullptr;
m_cbs.user_data = nullptr;
}
kernel::InterruptResult blockTip(SynchronizationState state, const CBlockIndex& index, double verification_progress) override
{
if (m_cbs.block_tip) m_cbs.block_tip(m_cbs.user_data, cast_state(state), btck_BlockTreeEntry::ref(&index), verification_progress);
return {};
}
void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) override
{
if (m_cbs.header_tip) m_cbs.header_tip(m_cbs.user_data, cast_state(state), height, timestamp, presync ? 1 : 0);
}
void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override
{
if (m_cbs.progress) m_cbs.progress(m_cbs.user_data, title.original.c_str(), title.original.length(), progress_percent, resume_possible ? 1 : 0);
}
void warningSet(kernel::Warning id, const bilingual_str& message) override
{
if (m_cbs.warning_set) m_cbs.warning_set(m_cbs.user_data, cast_btck_warning(id), message.original.c_str(), message.original.length());
}
void warningUnset(kernel::Warning id) override
{
if (m_cbs.warning_unset) m_cbs.warning_unset(m_cbs.user_data, cast_btck_warning(id));
}
void flushError(const bilingual_str& message) override
{
if (m_cbs.flush_error) m_cbs.flush_error(m_cbs.user_data, message.original.c_str(), message.original.length());
}
void fatalError(const bilingual_str& message) override
{
if (m_cbs.fatal_error) m_cbs.fatal_error(m_cbs.user_data, message.original.c_str(), message.original.length());
}
};
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);
};
class Context
@@ -234,7 +320,7 @@ class Context
public:
std::unique_ptr<kernel::Context> m_context;
std::unique_ptr<kernel::Notifications> m_notifications;
std::shared_ptr<KernelNotifications> m_notifications;
std::unique_ptr<util::SignalInterrupt> m_interrupt;
@@ -242,7 +328,6 @@ public:
Context(const ContextOptions* options, bool& sane)
: m_context{std::make_unique<kernel::Context>()},
m_notifications{std::make_unique<kernel::Notifications>()},
m_interrupt{std::make_unique<util::SignalInterrupt>()}
{
if (options) {
@@ -250,11 +335,18 @@ public:
if (options->m_chainparams) {
m_chainparams = std::make_unique<const CChainParams>(*options->m_chainparams);
}
if (options->m_notifications) {
m_notifications = options->m_notifications;
}
}
if (!m_chainparams) {
m_chainparams = CChainParams::Main();
}
if (!m_notifications) {
m_notifications = std::make_shared<KernelNotifications>(btck_NotificationInterfaceCallbacks{
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr});
}
if (!kernel::SanityChecks(*m_context)) {
sane = false;
@@ -511,6 +603,13 @@ void btck_context_options_set_chainparams(btck_ContextOptions* options, const bt
btck_ContextOptions::get(options).m_chainparams = std::make_unique<const CChainParams>(btck_ChainParameters::get(chain_parameters));
}
void btck_context_options_set_notifications(btck_ContextOptions* options, btck_NotificationInterfaceCallbacks notifications)
{
// The KernelNotifications are copy-initialized, so the caller can free them again.
LOCK(btck_ContextOptions::get(options).m_mutex);
btck_ContextOptions::get(options).m_notifications = std::make_shared<KernelNotifications>(notifications);
}
void btck_context_options_destroy(btck_ContextOptions* options)
{
delete options;