kernel: Add chainstate loading when instantiating a ChainstateManager
What changed, and why it matters
This commit changes the Bitcoin Core kernel library so that creating a ChainstateManager automatically loads and verifies the existing blockchain data and connects to the best known block. It is a normal initialization improvement, not a security fix. There is no evidence of a vulnerability being patched.
No security action required. Treat as routine API behavior change; downstream users should note that ChainstateManager creation now performs I/O and validation work that may fail or throw.
Security signals we found
No security-relevant signal: code is an initialization refactor in the kernel library API
Error handling added for chainstate load/verify/activate failures, but these are existing failure paths now surfaced earlier
Evidence from the diff
The patch moves chainstate loading into the ChainstateManager constructor path in the kernel API. It calls node::LoadChainstate, node::VerifyLoadedChainstate, and ActivateBestChain during btck_chainstate_manager_create. The change is architectural: previously the caller had to load the chainstate separately; now it happens internally. No input validation, bounds checking, or memory safety logic is altered. Error cases return nullptr and log messages.
Changed components
src/kernel/bitcoinkernel.cppsrc/kernel/bitcoinkernel.hBitcoin Core kernel library ChainstateManager instantiationInspect captured patch +39 / −6
diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp
index be0f9808..8eec1bc7 100644
--- a/src/kernel/bitcoinkernel.cpp
+++ b/src/kernel/bitcoinkernel.cpp
@@ -7,6 +7,7 @@
#include <kernel/bitcoinkernel.h>
#include <consensus/amount.h>
+#include <consensus/validation.h>
#include <kernel/caches.h>
#include <kernel/chainparams.h>
#include <kernel/checks.h>
@@ -16,6 +17,7 @@
#include <kernel/warning.h>
#include <logging.h>
#include <node/blockstorage.h>
+#include <node/chainstate.h>
#include <primitives/transaction.h>
#include <script/interpreter.h>
#include <script/script.h>
@@ -38,6 +40,7 @@
#include <memory>
#include <span>
#include <string>
+#include <tuple>
#include <utility>
#include <vector>
@@ -363,6 +366,7 @@ struct ChainstateManagerOptions {
ChainstateManager::Options m_chainman_options GUARDED_BY(m_mutex);
node::BlockManager::Options m_blockman_options GUARDED_BY(m_mutex);
std::shared_ptr<const Context> m_context;
+ node::ChainstateLoadOptions m_chainstate_load_options GUARDED_BY(m_mutex);
ChainstateManagerOptions(const std::shared_ptr<const Context>& context, const fs::path& data_dir, const fs::path& blocks_dir)
: m_chainman_options{ChainstateManager::Options{
@@ -377,7 +381,7 @@ struct ChainstateManagerOptions {
.path = data_dir / "blocks" / "index",
.cache_bytes = kernel::CacheSizes{DEFAULT_KERNEL_CACHE}.block_tree_db,
}}},
- m_context{context}
+ m_context{context}, m_chainstate_load_options{node::ChainstateLoadOptions{}}
{
}
};
@@ -703,16 +707,44 @@ void btck_chainstate_manager_options_destroy(btck_ChainstateManagerOptions* opti
btck_ChainstateManager* btck_chainstate_manager_create(
const btck_ChainstateManagerOptions* chainman_opts)
{
+ auto& opts{btck_ChainstateManagerOptions::get(chainman_opts)};
+ std::unique_ptr<ChainstateManager> chainman;
try {
- auto& opts{btck_ChainstateManagerOptions::get(chainman_opts)};
LOCK(opts.m_mutex);
- auto& context{opts.m_context};
- auto chainman{std::make_unique<ChainstateManager>(*context->m_interrupt, opts.m_chainman_options, opts.m_blockman_options)};
- return btck_ChainstateManager::create(std::move(chainman), context);
+ chainman = std::make_unique<ChainstateManager>(*opts.m_context->m_interrupt, opts.m_chainman_options, opts.m_blockman_options);
} catch (const std::exception& e) {
LogError("Failed to create chainstate manager: %s", e.what());
return nullptr;
}
+
+ try {
+ const auto chainstate_load_opts{WITH_LOCK(opts.m_mutex, return opts.m_chainstate_load_options)};
+
+ kernel::CacheSizes cache_sizes{DEFAULT_KERNEL_CACHE};
+ auto [status, chainstate_err]{node::LoadChainstate(*chainman, cache_sizes, chainstate_load_opts)};
+ if (status != node::ChainstateLoadStatus::SUCCESS) {
+ LogError("Failed to load chain state from your data directory: %s", chainstate_err.original);
+ return nullptr;
+ }
+ std::tie(status, chainstate_err) = node::VerifyLoadedChainstate(*chainman, chainstate_load_opts);
+ if (status != node::ChainstateLoadStatus::SUCCESS) {
+ LogError("Failed to verify loaded chain state from your datadir: %s", chainstate_err.original);
+ return nullptr;
+ }
+
+ for (Chainstate* chainstate : WITH_LOCK(chainman->GetMutex(), return chainman->GetAll())) {
+ BlockValidationState state;
+ if (!chainstate->ActivateBestChain(state, nullptr)) {
+ LogError("Failed to connect best block: %s", state.ToString());
+ return nullptr;
+ }
+ }
+ } catch (const std::exception& e) {
+ LogError("Failed to load chainstate: %s", e.what());
+ return nullptr;
+ }
+
+ return btck_ChainstateManager::create(std::move(chainman), opts.m_context);
}
void btck_chainstate_manager_destroy(btck_ChainstateManager* chainman)
diff --git a/src/kernel/bitcoinkernel.h b/src/kernel/bitcoinkernel.h
index 63cad6ad..d49e0e5b 100644
--- a/src/kernel/bitcoinkernel.h
+++ b/src/kernel/bitcoinkernel.h
@@ -755,7 +755,8 @@ BITCOINKERNEL_API void btck_chainstate_manager_options_destroy(btck_ChainstateMa
/**
* @brief Create a chainstate manager. This is the main object for many
- * validation tasks as well as for retrieving data from the chain. *
+ * validation tasks as well as for retrieving data from the chain and
+ * interacting with its chainstate and indexes.
*
* @param[in] chainstate_manager_options Non-null, created by @ref btck_chainstate_manager_options_create.
* @return The allocated chainstate manager, or null on error.
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.