Compare commits

...

25 Commits

Author SHA1 Message Date
Reproducibility Matters 139299de0f
Merge 4fce466d1a into b510893d00 2025-10-08 19:20:05 +02:00
TheCharlatan 4fce466d1a
kernel: Fix bitcoin-chainstate for windows
And turn it on in the CI.
2025-10-08 18:59:46 +02:00
TheCharlatan 1db7bde6d2
kernel: Add Purpose section to header documentation 2025-10-08 18:59:45 +02:00
TheCharlatan 2db540a173
kernel: Allowing reducing exports
Now that an API has been defined, remove the override for symbol
visibility of the library. It is now possible to build the library with
reduced exports.
2025-10-08 18:59:44 +02:00
TheCharlatan 79d3f30ebe
kernel: Add pure kernel bitcoin-chainstate
Re-write the bitcoin-chainstate utility by only using the kernel C++ API
header instead of internal Bitcoin Core code.
2025-10-08 18:59:43 +02:00
TheCharlatan 7918a4d17d
Kernel: Add functions for working with outpoints
This introduces the transaction outpoint, input and id types. This now
allows a user to retrieve a transaction output from a prior transaction
that a transaction outpoint is pointing to.

This is exercised in the tests by verifying the script of every
transaction in the test chain.
2025-10-08 18:59:42 +02:00
TheCharlatan 00000cb55a
kernel: Add block hash type and block tree utility functions to C header
Introduce btck_BlockHash as a type-safe identifier for a block. Adds
functions to retrieve block tree entries by hash or height, get block
hashes and heights from entries. access the genesis block, and check if
blocks are in the active chain.
2025-10-08 18:59:42 +02:00
TheCharlatan d9d040fd16
kernel: Add function to read block undo data from disk to C header
This adds functions for reading the undo data from disk with a retrieved
block tree entry. The undo data of a block contains all the spent
script pubkeys of all the transactions in a block. For ease of
understanding the undo data is renamed to spent outputs with seperate
data structures exposed for a block's and a transaction's spent outputs.

In normal operations undo data is used during re-orgs. This data might
also be useful for building external indexes, or to scan for silent
payment transactions.

Internally the block undo data contains a vector of transaction undo
data which contains a vector of the coins consumed. The coins are all
int the order of the transaction inputs of the consuming transactions.
Each coin can be used to retrieve a transaction output and in turn a
script pubkey and amount.

This translates to the three-level hierarchy the api provides: Block
spent outputs contain transaction spent outputs, which contain
individual coins. Each coin includes the associated output, the height
of the block is contained in, and whether it is from a coinbase
transaction.
2025-10-08 18:59:41 +02:00
TheCharlatan 3a2b8d855d
kernel: Add functions to read block from disk to C header
This adds functions for reading a block from disk with a retrieved block
tree entry. External services that wish to build their own index, or
analyze blocks can use this to retrieve block data.

The block tree can now be traversed from the tip backwards. This is
guaranteed to work, since the chainstate maintains an internal block
tree index in memory and every block (besides the genesis) has an
ancestor.

The user can use this function to iterate through all blocks in the
chain (starting from the tip). The tip is retrieved from a separate
`Chain` object, which allows distinguishing whether entries are
currently in the best chain. Once the block tree entry for the genesis
block is reached a nullptr is returned if the user attempts to get the
previous entry.
2025-10-08 18:59:40 +02:00
TheCharlatan e3d4d2461e
kernel: Add function for copying block data to C header
This adds a function for streaming bytes into a user-owned data
structure.

Use it in the tests for verifying the implementation of the validation
interface's `BlockChecked` method.
2025-10-08 18:59:39 +02:00
TheCharlatan fbbec31efc
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).
2025-10-08 18:59:38 +02:00
TheCharlatan 403fb9b120
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.

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.

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.
2025-10-08 18:59:37 +02:00
TheCharlatan 8d9088c5db
kernel: Add interrupt function to C header
Calling interrupt can halt long-running functions associated with
objects that were created through the passed-in context.
2025-10-08 18:59:36 +02:00
TheCharlatan 0459e9ac29
kernel: Add import blocks function to C header
Add `btck_import_blocks` to import block data and rebuild indexes. The
function can either reindex all existing block files if the indexes were
previously wiped through the chainstate load options, or import blocks
from a single specified file path.
2025-10-08 18:59:35 +02:00
TheCharlatan 41f81149c8
kernel: Add chainstate load options for in-memory dbs in C header
This allows a user to run the kernel without creating on-disk files for
the block tree and chainstate indexes. This is potentially useful in
scenarios where the user needs to do some ephemeral validation
operations.

One specific use case is when linearizing the blocks on disk. The block
files store blocks out of order, so a program may utilize the library
and its header to read the blocks with one chainstate manager, and then
write them back in order, and without orphans, with another chainstate
maanger. To save disk resources and if the indexes are not required once
done, it may be beneficial to keep the indexes in memory for the
chainstate manager that writes the blocks back again.
2025-10-08 18:59:34 +02:00
TheCharlatan 57a2b40476
kernel: Add options for reindexing in C header
Adds options for wiping the chainstate and block tree indexes to the
chainstate load options. In combination and once the
`*_import_blocks(...)` function is added in a later commit, this
triggers a reindex. For now, it just wipes the existing data.
2025-10-08 18:59:33 +02:00
TheCharlatan 42c106df05
kernel: Add block validation to C header
The added function allows the user process and validate a given block
with the chainstate manager. The *_process_block(...) function does some
preliminary checks on the block before passing it to
`ProcessNewBlock(...)`. These are similar to the checks in the
`submitblock()` rpc.

Richer processing of the block validation result will be made available
in the following commits through the validation interface.

The commits also adds a utility for deserializing a `CBlock`
(`kernel_block_create()`) that may then be passed to the library for
processing.

The tests exercise the function for both mainnet and regtest. The
commit also adds the data of 206 regtest blocks (some blocks also
contain transactions).
2025-10-08 18:59:32 +02:00
TheCharlatan 6b1ee82417
kernel: Add chainstate loading when instantiating a ChainstateManager
The library will now internally load the chainstate when a new
ChainstateManager is instantiated.

Options for controlling details of loading the chainstate will be added
over the next few commits.
2025-10-08 18:59:32 +02:00
TheCharlatan 783db175aa
kernel: Add chainstate manager option for setting worker threads
Re-use the same pattern used for the context options. This allows users
to set the number of threads used in the validation thread pool.
2025-10-08 18:59:31 +02:00
TheCharlatan dee984def6
kernel: Add chainstate manager object to C header
This is the main driver class for anything validation related, so expose
it here.

Creating the chainstate manager options will currently also trigger the
creation of their respectively configured directories.

The chainstate manager and block manager options are consolidated into a
single object. The kernel might eventually introduce a separate block
manager object for the purposes of being a light-weight block store
reader.

The chainstate manager will associate with the context with which it was
created for the duration of its lifetime. It is only valid if that
context remains in memory too.

The tests now also create dedicated temporary directories. This is
similar to the behaviour in the existing unit test framework.

Co-authored-by: stickies-v <stickies-v@protonmail.com>
2025-10-08 18:59:30 +02:00
TheCharlatan 96651a3f86
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.
2025-10-08 18:59:29 +02:00
TheCharlatan 86cd091d74
kernel: Add chain params context option to C header
As a first option, add the chainparams. For now these can only be
instantiated with default values. In future they may be expanded to take
their own options for regtest and signet configurations.

This commit also introduces a unique pattern for setting the option
values when calling the `*_set(...)` function.
2025-10-08 18:59:28 +02:00
TheCharlatan cc89fcc6da
kernel: Add kernel library context object
The context introduced here holds the objects that will be required for
running validation tasks, such as the chosen chain parameters, callbacks
for validation events, and interrupt handling. These will be used by the
chainstate manager introduced in subsequent commits.

This commit also introduces conventions for defining option objects. A
common pattern throughout the C header will be:
```
options = object_option_create();
object = object_create(options);
```
This allows for more consistent usage of a "builder pattern" for
objects where options can be configured independently from
instantiation.
2025-10-08 18:59:27 +02:00
TheCharlatan 4a0f6ba7a7
kernel: Add logging to kernel library C header
Exposing logging in the kernel library allows users to follow
operations. Users of the C header can use
`kernel_logging_connection_create(...)` to pass a callback function to
Bitcoin Core's internal logger. Additionally the level and category can
be globally configured.

By default, the logger buffers messages until
`kernel_loggin_connection_create(...)` is called. If the user does not
want any logging messages, it is recommended that
`kernel_disable_logging()` is called, which permanently disables the
logging and any buffering of messages.
2025-10-08 18:58:37 +02:00
TheCharlatan fcb0590623
kernel: Introduce initial kernel C header API
As a first step, implement the equivalent of what was implemented in the
now deprecated libbitcoinconsensus header. Also add a test binary to
exercise the header and library.

Unlike the deprecated libbitcoinconsensus the kernel library can now use
the hardware-accelerated sha256 implementations thanks for its
statically-initialzed context. The functions kept around for
backwards-compatibility in the libbitcoinconsensus header are not ported
over. As a new header, it should not be burdened by previous
implementations. Also add a new error code for handling invalid flag
combinations, which would otherwise cause a crash.

The macros used in the new C header were adapted from the libsecp256k1
header.

To make use of the C header from C++ code, a C++ header is also
introduced for wrapping the C header. This makes it safer and easier to
use from C++ code.

Co-authored-by: stickies-v <stickies-v@protonmail.com>
2025-10-08 18:58:29 +02:00
18 changed files with 5622 additions and 251 deletions

View File

@ -42,6 +42,8 @@ def main():
"-DBUILD_BENCH=ON",
"-DBUILD_FUZZ_BINARY=ON",
"-DWITH_USDT=ON",
"-DBUILD_KERNEL_LIB=ON",
"-DBUILD_KERNEL_TEST=ON",
"-DCMAKE_CXX_FLAGS=-Wno-error=unused-member-function",
])
run(["cmake", "--build", "build", "-j", str(num_procs)])

View File

@ -202,7 +202,7 @@ jobs:
job-type: [standard, fuzz]
include:
- job-type: standard
generate-options: '-DBUILD_GUI=ON -DWITH_ZMQ=ON -DBUILD_BENCH=ON -DWERROR=ON'
generate-options: '-DBUILD_GUI=ON -DWITH_ZMQ=ON -DBUILD_BENCH=ON -DBUILD_KERNEL_LIB=ON -DBUILD_UTIL_CHAINSTATE=ON -DBUILD_KERNEL_TEST=OFF -DWERROR=ON'
job-name: 'Windows native, VS 2022'
- job-type: fuzz
generate-options: '-DVCPKG_MANIFEST_NO_DEFAULT_FEATURES=ON -DVCPKG_MANIFEST_FEATURES="wallet" -DBUILD_GUI=OFF -DBUILD_FOR_FUZZING=ON -DWERROR=ON'
@ -307,6 +307,7 @@ jobs:
BITCOINTX: '${{ github.workspace }}\build\bin\Release\bitcoin-tx.exe'
BITCOINUTIL: '${{ github.workspace }}\build\bin\Release\bitcoin-util.exe'
BITCOINWALLET: '${{ github.workspace }}\build\bin\Release\bitcoin-wallet.exe'
BITCOINCHAINSTATE: '${{ github.workspace }}\build\bin\Release\bitcoin-chainstate.exe'
TEST_RUNNER_EXTRA: ${{ github.event_name != 'pull_request' && '--extended' || '' }}
run: py -3 test/functional/test_runner.py --jobs $NUMBER_OF_PROCESSORS --ci --quiet --tmpdirprefix="${RUNNER_TEMP}" --combinedlogslen=99999999 --timeout-factor=${TEST_RUNNER_TIMEOUT_FACTOR} ${TEST_RUNNER_EXTRA}

View File

@ -105,6 +105,7 @@ option(BUILD_UTIL "Build bitcoin-util executable." ${BUILD_TESTS})
option(BUILD_UTIL_CHAINSTATE "Build experimental bitcoin-chainstate executable." OFF)
option(BUILD_KERNEL_LIB "Build experimental bitcoinkernel library." ${BUILD_UTIL_CHAINSTATE})
option(BUILD_KERNEL_TEST "Build tests for the experimental bitcoinkernel library." ${BUILD_KERNEL_LIB})
option(ENABLE_WALLET "Enable wallet." ON)
if(ENABLE_WALLET)
@ -210,6 +211,7 @@ if(BUILD_FOR_FUZZING)
set(BUILD_UTIL OFF)
set(BUILD_UTIL_CHAINSTATE OFF)
set(BUILD_KERNEL_LIB OFF)
set(BUILD_KERNEL_TEST OFF)
set(BUILD_WALLET_TOOL OFF)
set(BUILD_GUI OFF)
set(ENABLE_EXTERNAL_SIGNER OFF)
@ -668,6 +670,7 @@ message(" bitcoin-util ........................ ${BUILD_UTIL}")
message(" bitcoin-wallet ...................... ${BUILD_WALLET_TOOL}")
message(" bitcoin-chainstate (experimental) ... ${BUILD_UTIL_CHAINSTATE}")
message(" libbitcoinkernel (experimental) ..... ${BUILD_KERNEL_LIB}")
message(" kernel-test (experimental) .......... ${BUILD_KERNEL_TEST}")
message("Optional features:")
message(" wallet support ...................... ${ENABLE_WALLET}")
message(" external signer ..................... ${ENABLE_EXTERNAL_SIGNER}")

View File

@ -12,7 +12,7 @@ export CONTAINER_NAME="ci_mac_native" # macos does not use a container, but the
export PIP_PACKAGES="--break-system-packages zmq"
export GOAL="install deploy"
export CMAKE_GENERATOR="Ninja"
export BITCOIN_CONFIG="-DBUILD_GUI=ON -DWITH_ZMQ=ON -DREDUCE_EXPORTS=ON -DCMAKE_EXE_LINKER_FLAGS='-Wl,-stack_size -Wl,0x80000'"
export BITCOIN_CONFIG="-DBUILD_GUI=ON -DWITH_ZMQ=ON -DBUILD_KERNEL_LIB=ON -DBUILD_KERNEL_TEST=ON -DREDUCE_EXPORTS=ON -DCMAKE_EXE_LINKER_FLAGS='-Wl,-stack_size -Wl,0x80000'"
export CI_OS_NAME="macos"
export NO_DEPENDS=1
export OSX_SDK=""

View File

@ -13,4 +13,4 @@ export PACKAGES="python3-zmq python3-pip clang-16 llvm-16 libc++abi-16-dev libc+
export PIP_PACKAGES="--break-system-packages pycapnp"
export DEP_OPTS="NO_WALLET=1 CC=clang-16 CXX='clang++-16 -stdlib=libc++'"
export GOAL="install"
export BITCOIN_CONFIG="-DREDUCE_EXPORTS=ON -DBUILD_UTIL_CHAINSTATE=ON -DBUILD_KERNEL_LIB=ON -DBUILD_SHARED_LIBS=ON"
export BITCOIN_CONFIG="-DREDUCE_EXPORTS=ON -DBUILD_UTIL_CHAINSTATE=ON -DBUILD_KERNEL_LIB=ON -DBUILD_KERNEL_TEST=ON -DBUILD_SHARED_LIBS=ON"

View File

@ -14,5 +14,5 @@ export PACKAGES="g++-mingw-w64-x86-64-posix nsis"
export RUN_UNIT_TESTS=false
export RUN_FUNCTIONAL_TESTS=false
export GOAL="deploy"
export BITCOIN_CONFIG="-DREDUCE_EXPORTS=ON -DBUILD_GUI_TESTS=OFF \
export BITCOIN_CONFIG="-DREDUCE_EXPORTS=ON -DBUILD_GUI_TESTS=OFF -DBUILD_KERNEL_LIB=ON -DBUILD_KERNEL_TEST=ON \
-DCMAKE_CXX_FLAGS='-Wno-error=maybe-uninitialized'"

View File

@ -404,6 +404,9 @@ endif()
if(BUILD_KERNEL_LIB)
add_subdirectory(kernel)
if (BUILD_KERNEL_TEST)
add_subdirectory(test/kernel)
endif()
endif()
if(BUILD_UTIL_CHAINSTATE)

View File

@ -1,53 +1,144 @@
// Copyright (c) 2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
// The bitcoin-chainstate executable serves to surface the dependencies required
// by a program wishing to use Bitcoin Core's consensus engine as it is right
// now.
//
// DEVELOPER NOTE: Since this is a "demo-only", experimental, etc. executable,
// it may diverge from Bitcoin Core's coding style.
//
// It is part of the libbitcoinkernel project.
#include <kernel/chainparams.h>
#include <kernel/chainstatemanager_opts.h>
#include <kernel/checks.h>
#include <kernel/context.h>
#include <kernel/warning.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <kernel/caches.h>
#include <logging.h>
#include <node/blockstorage.h>
#include <node/chainstate.h>
#include <random.h>
#include <script/sigcache.h>
#include <util/chaintype.h>
#include <util/fs.h>
#include <util/signalinterrupt.h>
#include <util/task_runner.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <kernel/bitcoinkernel_wrapper.h>
#include <cassert>
#include <cstdint>
#include <functional>
#include <iosfwd>
#include <memory>
#include <charconv>
#include <filesystem>
#include <iostream>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#ifdef WIN32
// clang-format off
#include <windows.h>
// clang-format on
#include <codecvt>
#include <locale>
#include <shellapi.h>
#endif
using namespace btck;
std::vector<std::byte> hex_string_to_byte_vec(std::string_view hex)
{
std::vector<std::byte> bytes;
bytes.reserve(hex.length() / 2);
for (size_t i{0}; i < hex.length(); i += 2) {
uint8_t byte_value;
auto [ptr, ec] = std::from_chars(hex.data() + i, hex.data() + i + 2, byte_value, 16);
if (ec != std::errc{} || ptr != hex.data() + i + 2) {
throw std::invalid_argument("Invalid hex character");
}
bytes.push_back(static_cast<std::byte>(byte_value));
}
return bytes;
}
class KernelLog
{
public:
void LogMessage(std::string_view message)
{
std::cout << "kernel: " << message;
}
};
class TestValidationInterface : public ValidationInterface<TestValidationInterface>
{
public:
TestValidationInterface() = default;
std::optional<std::string> m_expected_valid_block = std::nullopt;
void BlockChecked(const Block block, const BlockValidationState state) override
{
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;
}
}
}
};
class TestKernelNotifications : public KernelNotifications<TestKernelNotifications>
{
public:
void BlockTipHandler(SynchronizationState, const BlockTreeEntry, double) override
{
std::cout << "Block tip changed" << std::endl;
}
void ProgressHandler(std::string_view title, int progress_percent, bool resume_possible) override
{
std::cout << "Made progress: " << title << " " << progress_percent << "%" << std::endl;
}
void WarningSetHandler(Warning warning, std::string_view message) override
{
std::cout << message << std::endl;
}
void WarningUnsetHandler(Warning warning) override
{
std::cout << "Warning unset: " << static_cast<std::underlying_type_t<Warning>>(warning) << std::endl;
}
void FlushErrorHandler(std::string_view error) override
{
std::cout << error << std::endl;
}
void FatalErrorHandler(std::string_view error) override
{
std::cout << error << std::endl;
}
};
int main(int argc, char* argv[])
{
// We do not enable logging for this app, so explicitly disable it.
// To enable logging instead, replace with:
// LogInstance().m_print_to_console = true;
// LogInstance().StartLogging();
LogInstance().DisableLogging();
// SETUP: Argument parsing and handling
if (argc != 2) {
std::cerr
@ -58,213 +149,81 @@ int main(int argc, char* argv[])
<< " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl;
return 1;
}
fs::path abs_datadir{fs::absolute(argv[1])};
fs::create_directories(abs_datadir);
#ifdef WIN32
int win_argc;
wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &win_argc);
std::vector<std::string> utf8_args(win_argc);
std::vector<char*> win_argv(win_argc);
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
for (int i = 0; i < win_argc; i++) {
utf8_args[i] = utf8_cvt.to_bytes(wargv[i]);
win_argv[i] = &utf8_args[i][0];
}
LocalFree(wargv);
argc = win_argc;
argv = win_argv.data();
#endif
// SETUP: Context
kernel::Context kernel_context{};
// We can't use a goto here, but we can use an assert since none of the
// things instantiated so far requires running the epilogue to be torn down
// properly
assert(kernel::SanityChecks(kernel_context));
std::filesystem::path abs_datadir{std::filesystem::absolute(argv[1])};
std::filesystem::create_directories(abs_datadir);
ValidationSignals validation_signals{std::make_unique<util::ImmediateTaskRunner>()};
class KernelNotifications : public kernel::Notifications
{
public:
kernel::InterruptResult blockTip(SynchronizationState, const CBlockIndex&, double) override
{
std::cout << "Block tip changed" << std::endl;
return {};
}
void headerTip(SynchronizationState, int64_t height, int64_t timestamp, bool presync) override
{
std::cout << "Header tip changed: " << height << ", " << timestamp << ", " << presync << std::endl;
}
void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override
{
std::cout << "Progress: " << title.original << ", " << progress_percent << ", " << resume_possible << std::endl;
}
void warningSet(kernel::Warning id, const bilingual_str& message) override
{
std::cout << "Warning " << static_cast<int>(id) << " set: " << message.original << std::endl;
}
void warningUnset(kernel::Warning id) override
{
std::cout << "Warning " << static_cast<int>(id) << " unset" << std::endl;
}
void flushError(const bilingual_str& message) override
{
std::cerr << "Error flushing block data to disk: " << message.original << std::endl;
}
void fatalError(const bilingual_str& message) override
{
std::cerr << "Error: " << message.original << std::endl;
}
btck_LoggingOptions logging_options = {
.log_timestamps = true,
.log_time_micros = false,
.log_threadnames = false,
.log_sourcelocations = false,
.always_print_category_levels = true,
};
auto notifications = std::make_unique<KernelNotifications>();
kernel::CacheSizes cache_sizes{DEFAULT_KERNEL_CACHE};
Logger logger{std::make_unique<KernelLog>(KernelLog{}), logging_options};
// SETUP: Chainstate
auto chainparams = CChainParams::Main();
const ChainstateManager::Options chainman_opts{
.chainparams = *chainparams,
.datadir = abs_datadir,
.notifications = *notifications,
.signals = &validation_signals,
};
const node::BlockManager::Options blockman_opts{
.chainparams = chainman_opts.chainparams,
.blocks_dir = abs_datadir / "blocks",
.notifications = chainman_opts.notifications,
.block_tree_db_params = DBParams{
.path = abs_datadir / "blocks" / "index",
.cache_bytes = cache_sizes.block_tree_db,
},
};
util::SignalInterrupt interrupt;
ChainstateManager chainman{interrupt, chainman_opts, blockman_opts};
ContextOptions options{};
ChainParams params{ChainType::MAINNET};
options.SetChainParams(params);
node::ChainstateLoadOptions options;
auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
if (status != node::ChainstateLoadStatus::SUCCESS) {
std::cerr << "Failed to load Chain state from your datadir." << std::endl;
goto epilogue;
} else {
std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
if (status != node::ChainstateLoadStatus::SUCCESS) {
std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl;
goto epilogue;
}
options.SetNotifications(std::make_shared<TestKernelNotifications>());
options.SetValidationInterface(std::make_shared<TestValidationInterface>());
Context context{options};
ChainstateManagerOptions chainman_opts{context, abs_datadir.string(), (abs_datadir / "blocks").string()};
chainman_opts.SetWorkerThreads(4);
std::unique_ptr<ChainMan> chainman;
try {
chainman = std::make_unique<ChainMan>(context, chainman_opts);
} catch (std::exception&) {
std::cerr << "Failed to instantiate ChainMan, exiting" << std::endl;
return 1;
}
for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
BlockValidationState state;
if (!chainstate->ActivateBestChain(state, nullptr)) {
std::cerr << "Failed to connect best block (" << state.ToString() << ")" << std::endl;
goto epilogue;
}
}
// Main program logic starts here
std::cout
<< "Hello! I'm going to print out some information about your datadir." << std::endl
<< "\t"
<< "Path: " << abs_datadir << std::endl;
{
LOCK(chainman.GetMutex());
std::cout
<< "\t" << "Blockfiles Indexed: " << std::boolalpha << chainman.m_blockman.m_blockfiles_indexed.load() << std::noboolalpha << std::endl
<< "\t" << "Snapshot Active: " << std::boolalpha << chainman.IsSnapshotActive() << std::noboolalpha << std::endl
<< "\t" << "Active Height: " << chainman.ActiveHeight() << std::endl
<< "\t" << "Active IBD: " << std::boolalpha << chainman.IsInitialBlockDownload() << std::noboolalpha << std::endl;
CBlockIndex* tip = chainman.ActiveTip();
if (tip) {
std::cout << "\t" << tip->ToString() << std::endl;
}
}
std::cout << "Enter the block you want to validate on the next line:" << std::endl;
for (std::string line; std::getline(std::cin, line);) {
if (line.empty()) {
std::cerr << "Empty line found" << std::endl;
break;
std::cerr << "Empty line found, try again:" << std::endl;
continue;
}
std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
CBlock& block = *blockptr;
if (!DecodeHexBlk(block, line)) {
std::cerr << "Block decode failed" << std::endl;
break;
auto raw_block{hex_string_to_byte_vec(line)};
std::unique_ptr<Block> block;
try {
block = std::make_unique<Block>(raw_block);
} catch (std::exception&) {
std::cerr << "Block decode failed, try again:" << std::endl;
continue;
}
{
LOCK(cs_main);
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
if (pindex) {
chainman.UpdateUncommittedBlockStructures(block, pindex);
}
bool new_block = false;
bool accepted = chainman->ProcessBlock(*block, &new_block);
if (accepted) {
std::cerr << "Block has not yet been rejected" << std::endl;
} else {
std::cerr << "Block was not accepted" << std::endl;
}
// Adapted from rpc/mining.cpp
class submitblock_StateCatcher final : public CValidationInterface
{
public:
uint256 hash;
bool found;
BlockValidationState state;
explicit submitblock_StateCatcher(const uint256& hashIn) : hash(hashIn), found(false), state() {}
protected:
void BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& stateIn) override
{
if (block->GetHash() != hash)
return;
found = true;
state = stateIn;
}
};
bool new_block;
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
validation_signals.RegisterSharedValidationInterface(sc);
bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block);
validation_signals.UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) {
std::cerr << "duplicate" << std::endl;
break;
}
if (!sc->found) {
std::cerr << "inconclusive" << std::endl;
break;
}
std::cout << sc->state.ToString() << std::endl;
switch (sc->state.GetResult()) {
case BlockValidationResult::BLOCK_RESULT_UNSET:
std::cerr << "initial value. Block has not yet been rejected" << std::endl;
break;
case BlockValidationResult::BLOCK_HEADER_LOW_WORK:
std::cerr << "the block header may be on a too-little-work chain" << std::endl;
break;
case BlockValidationResult::BLOCK_CONSENSUS:
std::cerr << "invalid by consensus rules (excluding any below reasons)" << std::endl;
break;
case BlockValidationResult::BLOCK_CACHED_INVALID:
std::cerr << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
break;
case BlockValidationResult::BLOCK_INVALID_HEADER:
std::cerr << "invalid proof of work or time too old" << std::endl;
break;
case BlockValidationResult::BLOCK_MUTATED:
std::cerr << "the block's data didn't match the data committed to by the PoW" << std::endl;
break;
case BlockValidationResult::BLOCK_MISSING_PREV:
std::cerr << "We don't have the previous block the checked one is built on" << std::endl;
break;
case BlockValidationResult::BLOCK_INVALID_PREV:
std::cerr << "A block this one builds on is invalid" << std::endl;
break;
case BlockValidationResult::BLOCK_TIME_FUTURE:
std::cerr << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
break;
}
}
epilogue:
// Without this precise shutdown sequence, there will be a lot of nullptr
// dereferencing and UB.
validation_signals.FlushBackgroundCallbacks();
{
LOCK(cs_main);
for (Chainstate* chainstate : chainman.GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
chainstate->ResetCoinsViews();
}
if (!new_block) {
std::cerr << "Block is a duplicate" << std::endl;
}
}
}

View File

@ -96,20 +96,15 @@ target_link_libraries(bitcoinkernel
target_include_directories(bitcoinkernel PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src/leveldb/include>)
# libbitcoinkernel requires default symbol visibility, explicitly
# specify that here so that things still work even when user
# configures with -DREDUCE_EXPORTS=ON
#
# Note this is a quick hack that will be removed as we
# incrementally define what to export from the library.
set_target_properties(bitcoinkernel PROPERTIES
CXX_VISIBILITY_PRESET default
)
# Add a convenience libbitcoinkernel target as a synonym for bitcoinkernel.
add_custom_target(libbitcoinkernel)
add_dependencies(libbitcoinkernel bitcoinkernel)
get_target_property(bitcoinkernel_type bitcoinkernel TYPE)
if(bitcoinkernel_type STREQUAL "STATIC_LIBRARY")
target_compile_definitions(bitcoinkernel PUBLIC BITCOINKERNEL_STATIC)
endif()
configure_file(${PROJECT_SOURCE_DIR}/libbitcoinkernel.pc.in ${PROJECT_BINARY_DIR}/libbitcoinkernel.pc @ONLY)
install(FILES ${PROJECT_BINARY_DIR}/libbitcoinkernel.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" COMPONENT libbitcoinkernel)
@ -124,3 +119,5 @@ install(TARGETS bitcoinkernel
DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT libbitcoinkernel
)
install(FILES bitcoinkernel.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT libbitcoinkernel)

File diff suppressed because it is too large Load Diff

1551
src/kernel/bitcoinkernel.h Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -201,6 +201,7 @@ static const std::map<std::string, BCLog::LogFlags, std::less<>> LOG_CATEGORIES_
{"txreconciliation", BCLog::TXRECONCILIATION},
{"scan", BCLog::SCAN},
{"txpackages", BCLog::TXPACKAGES},
{"kernel", BCLog::KERNEL},
};
static const std::unordered_map<BCLog::LogFlags, std::string> LOG_CATEGORIES_BY_FLAG{

View File

@ -94,6 +94,7 @@ namespace BCLog {
TXRECONCILIATION = (CategoryMask{1} << 26),
SCAN = (CategoryMask{1} << 27),
TXPACKAGES = (CategoryMask{1} << 28),
KERNEL = (CategoryMask{1} << 29),
ALL = ~NONE,
};
enum class Level {
@ -256,6 +257,12 @@ namespace BCLog {
m_print_callbacks.erase(it);
}
size_t NumConnections()
{
StdLockGuard scoped_lock(m_cs);
return m_print_callbacks.size();
}
/** Start logging (and flush all buffered messages) */
bool StartLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
/** Only for testing */
@ -287,6 +294,11 @@ namespace BCLog {
StdLockGuard scoped_lock(m_cs);
m_category_log_levels = levels;
}
void AddCategoryLogLevel(LogFlags category, Level level)
{
StdLockGuard scoped_lock(m_cs);
m_category_log_levels[category] = level;
}
bool SetCategoryLogLevel(std::string_view category_str, std::string_view level_str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
Level LogLevel() const { return m_log_level.load(); }

View File

@ -0,0 +1,16 @@
add_executable(test_kernel
test_kernel.cpp
)
target_link_libraries(test_kernel
PRIVATE
core_interface
bitcoinkernel
Boost::headers
)
set_target_properties(test_kernel PROPERTIES
SKIP_BUILD_RPATH OFF
)
add_test(NAME test_kernel COMMAND test_kernel)

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -373,6 +373,8 @@ fn lint_std_filesystem() -> LintResult {
"./src/",
":(exclude)src/ipc/libmultiprocess/",
":(exclude)src/util/fs.h",
":(exclude)src/test/kernel/test_kernel.cpp",
":(exclude)src/bitcoin-chainstate.cpp",
])
.status()
.expect("command error")