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.
This commit is contained in:
TheCharlatan 2024-06-22 21:08:10 +02:00
parent 57a2b40476
commit 41f81149c8
No known key found for this signature in database
GPG Key ID: 9B79B45691DB4173
4 changed files with 80 additions and 5 deletions

View File

@ -715,6 +715,24 @@ int btck_chainstate_manager_options_set_wipe_dbs(btck_ChainstateManagerOptions*
return 0; return 0;
} }
void btck_chainstate_manager_options_set_block_tree_db_in_memory(
btck_ChainstateManagerOptions* chainman_opts,
int block_tree_db_in_memory)
{
auto& opts{btck_ChainstateManagerOptions::get(chainman_opts)};
LOCK(opts.m_mutex);
opts.m_blockman_options.block_tree_db_params.memory_only = block_tree_db_in_memory == 1;
}
void btck_chainstate_manager_options_set_chainstate_db_in_memory(
btck_ChainstateManagerOptions* chainman_opts,
int chainstate_db_in_memory)
{
auto& opts{btck_ChainstateManagerOptions::get(chainman_opts)};
LOCK(opts.m_mutex);
opts.m_chainstate_load_options.coins_db_in_memory = chainstate_db_in_memory == 1;
}
btck_ChainstateManager* btck_chainstate_manager_create( btck_ChainstateManager* btck_chainstate_manager_create(
const btck_ChainstateManagerOptions* chainman_opts) const btck_ChainstateManagerOptions* chainman_opts)
{ {

View File

@ -744,6 +744,26 @@ BITCOINKERNEL_API int btck_chainstate_manager_options_set_wipe_dbs(
int wipe_block_tree_db, int wipe_block_tree_db,
int wipe_chainstate_db) BITCOINKERNEL_ARG_NONNULL(1); int wipe_chainstate_db) BITCOINKERNEL_ARG_NONNULL(1);
/**
* @brief Sets block tree db in memory in the options.
*
* @param[in] chainstate_manager_options Non-null, created by @ref btck_chainstate_manager_options_create.
* @param[in] block_tree_db_in_memory Set block tree db in memory.
*/
BITCOINKERNEL_API void btck_chainstate_manager_options_set_block_tree_db_in_memory(
btck_ChainstateManagerOptions* chainstate_manager_options,
int block_tree_db_in_memory) BITCOINKERNEL_ARG_NONNULL(1);
/**
* @brief Sets chainstate db in memory in the options.
*
* @param[in] chainstate_manager_options Non-null, created by @ref btck_chainstate_manager_options_create.
* @param[in] chainstate_db_in_memory Set chainstate db in memory.
*/
BITCOINKERNEL_API void btck_chainstate_manager_options_set_chainstate_db_in_memory(
btck_ChainstateManagerOptions* chainstate_manager_options,
int chainstate_db_in_memory) BITCOINKERNEL_ARG_NONNULL(1);
/** /**
* Destroy the chainstate manager options. * Destroy the chainstate manager options.
*/ */

View File

@ -678,6 +678,16 @@ public:
return btck_chainstate_manager_options_set_wipe_dbs(get(), wipe_block_tree, wipe_chainstate) == 0; return btck_chainstate_manager_options_set_wipe_dbs(get(), wipe_block_tree, wipe_chainstate) == 0;
} }
void SetBlockTreeDbInMemory(bool block_tree_db_in_memory)
{
btck_chainstate_manager_options_set_block_tree_db_in_memory(get(), block_tree_db_in_memory);
}
void SetChainstateDbInMemory(bool chainstate_db_in_memory)
{
btck_chainstate_manager_options_set_chainstate_db_in_memory(get(), chainstate_db_in_memory);
}
friend class ChainMan; friend class ChainMan;
}; };

View File

@ -539,6 +539,8 @@ BOOST_AUTO_TEST_CASE(btck_chainman_tests)
std::unique_ptr<ChainMan> create_chainman(TestDirectory& test_directory, std::unique_ptr<ChainMan> create_chainman(TestDirectory& test_directory,
bool reindex, bool reindex,
bool wipe_chainstate, bool wipe_chainstate,
bool block_tree_db_in_memory,
bool chainstate_db_in_memory,
Context& context) Context& context)
{ {
auto mainnet_test_directory{TestDirectory{"mainnet_test_bitcoin_kernel"}}; auto mainnet_test_directory{TestDirectory{"mainnet_test_bitcoin_kernel"}};
@ -551,6 +553,12 @@ std::unique_ptr<ChainMan> create_chainman(TestDirectory& test_directory,
if (wipe_chainstate) { if (wipe_chainstate) {
chainman_opts.SetWipeDbs(/*wipe_block_tree=*/false, /*wipe_chainstate=*/wipe_chainstate); chainman_opts.SetWipeDbs(/*wipe_block_tree=*/false, /*wipe_chainstate=*/wipe_chainstate);
} }
if (block_tree_db_in_memory) {
chainman_opts.SetBlockTreeDbInMemory(block_tree_db_in_memory);
}
if (chainstate_db_in_memory) {
chainman_opts.SetChainstateDbInMemory(chainstate_db_in_memory);
}
auto chainman{std::make_unique<ChainMan>(context, chainman_opts)}; auto chainman{std::make_unique<ChainMan>(context, chainman_opts)};
return chainman; return chainman;
@ -571,21 +579,21 @@ void chainman_reindex_test(TestDirectory& test_directory)
auto notifications{std::make_shared<TestKernelNotifications>()}; auto notifications{std::make_shared<TestKernelNotifications>()};
auto context{create_context(notifications, ChainType::MAINNET)}; auto context{create_context(notifications, ChainType::MAINNET)};
auto chainman{create_chainman(test_directory, true, false, context)}; auto chainman{create_chainman(test_directory, true, false, false, false, context)};
} }
void chainman_reindex_chainstate_test(TestDirectory& test_directory) void chainman_reindex_chainstate_test(TestDirectory& test_directory)
{ {
auto notifications{std::make_shared<TestKernelNotifications>()}; auto notifications{std::make_shared<TestKernelNotifications>()};
auto context{create_context(notifications, ChainType::MAINNET)}; auto context{create_context(notifications, ChainType::MAINNET)};
auto chainman{create_chainman(test_directory, false, true, context)}; auto chainman{create_chainman(test_directory, false, true, false, false, context)};
} }
void chainman_mainnet_validation_test(TestDirectory& test_directory) void chainman_mainnet_validation_test(TestDirectory& test_directory)
{ {
auto notifications{std::make_shared<TestKernelNotifications>()}; auto notifications{std::make_shared<TestKernelNotifications>()};
auto context{create_context(notifications, ChainType::MAINNET)}; auto context{create_context(notifications, ChainType::MAINNET)};
auto chainman{create_chainman(test_directory, false, false, context)}; auto chainman{create_chainman(test_directory, false, false, false, false, context)};
{ {
// Process an invalid block // Process an invalid block
@ -638,6 +646,25 @@ BOOST_AUTO_TEST_CASE(btck_chainman_mainnet_tests)
chainman_reindex_chainstate_test(test_directory); chainman_reindex_chainstate_test(test_directory);
} }
BOOST_AUTO_TEST_CASE(btck_chainman_in_memory_tests)
{
auto in_memory_test_directory{TestDirectory{"in-memory_test_bitcoin_kernel"}};
auto notifications{std::make_shared<TestKernelNotifications>()};
auto context{create_context(notifications, ChainType::REGTEST)};
auto chainman{create_chainman(in_memory_test_directory, false, false, true, true, context)};
for (auto& raw_block : REGTEST_BLOCK_DATA) {
Block block{as_bytes(raw_block)};
bool new_block{false};
chainman->ProcessBlock(block, &new_block);
BOOST_CHECK(new_block);
}
BOOST_CHECK(!std::filesystem::exists(in_memory_test_directory.m_directory / "blocks" / "index"));
BOOST_CHECK(!std::filesystem::exists(in_memory_test_directory.m_directory / "chainstate"));
}
BOOST_AUTO_TEST_CASE(btck_chainman_regtest_tests) BOOST_AUTO_TEST_CASE(btck_chainman_regtest_tests)
{ {
auto test_directory{TestDirectory{"regtest_test_bitcoin_kernel"}}; auto test_directory{TestDirectory{"regtest_test_bitcoin_kernel"}};
@ -651,7 +678,7 @@ BOOST_AUTO_TEST_CASE(btck_chainman_regtest_tests)
const size_t mid{REGTEST_BLOCK_DATA.size() / 2}; const size_t mid{REGTEST_BLOCK_DATA.size() / 2};
{ {
auto chainman{create_chainman(test_directory, false, false, context)}; auto chainman{create_chainman(test_directory, false, false, false, false, context)};
for (size_t i{0}; i < mid; i++) { for (size_t i{0}; i < mid; i++) {
Block block{as_bytes(REGTEST_BLOCK_DATA[i])}; Block block{as_bytes(REGTEST_BLOCK_DATA[i])};
bool new_block{false}; bool new_block{false};
@ -660,7 +687,7 @@ BOOST_AUTO_TEST_CASE(btck_chainman_regtest_tests)
} }
} }
auto chainman{create_chainman(test_directory, false, false, context)}; auto chainman{create_chainman(test_directory, false, false, false, false, context)};
for (size_t i{mid}; i < REGTEST_BLOCK_DATA.size(); i++) { for (size_t i{mid}; i < REGTEST_BLOCK_DATA.size(); i++) {
Block block{as_bytes(REGTEST_BLOCK_DATA[i])}; Block block{as_bytes(REGTEST_BLOCK_DATA[i])};