Skip to content

Commit

Permalink
add read only option for mdbx
Browse files Browse the repository at this point in the history
  • Loading branch information
josibake committed Sep 19, 2024
1 parent 0516a4c commit bf1843b
Show file tree
Hide file tree
Showing 10 changed files with 37 additions and 12 deletions.
17 changes: 12 additions & 5 deletions src/dbwrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,14 +420,14 @@ MDBXWrapper::MDBXWrapper(const DBParams& params)
: CDBWrapperBase(params),
m_db_context{std::make_unique<MDBXContext>()}
{
if (params.wipe_data) {
if (params.wipe_data && !params.read_only) {
LogInfo("Wiping MDBX in %s\n", fs::PathToString(params.path));
DestroyDB(fs::PathToString(params.path));
}

TryCreateDirectories(params.path);

LogPrintf("Opening MDBX in %s\n", fs::PathToString(params.path));
LogPrintf("Opening MDBX in %s (read-only: %s)\n", fs::PathToString(params.path), params.read_only ? "yes" : "no");

DBContext().create_params.geometry.pagesize = 16384;

Expand All @@ -436,17 +436,24 @@ MDBXWrapper::MDBXWrapper(const DBParams& params)
DBContext().operate_params.options.no_sticky_threads = true;
DBContext().operate_params.durability = mdbx::env::whole_fragile;

// Set read-only mode if specified
if (params.read_only) {
DBContext().operate_params.mode = mdbx::env::mode::readonly;
}

// initialize the mdbx environment.
DBContext().env = mdbx::env_managed(params.path, DBContext().create_params, DBContext().operate_params);

auto txn = DBContext().env.start_read();
DBContext().map = txn.open_map(nullptr, mdbx::key_mode::usual, mdbx::value_mode::single);
txn.commit();

if (params.obfuscate && WriteObfuscateKeyIfNotExists()){
LogInfo("Wrote new obfuscate key for %s: %s\n", fs::PathToString(params.path), HexStr(obfuscate_key));
if (!params.read_only) {
if (params.obfuscate && WriteObfuscateKeyIfNotExists()){
LogInfo("Wrote new obfuscate key for %s: %s\n", fs::PathToString(params.path), HexStr(obfuscate_key));
}
LogInfo("Using obfuscation key for %s: %s\n", fs::PathToString(params.path), HexStr(GetObfuscateKey()));
}
LogInfo("Using obfuscation key for %s: %s\n", fs::PathToString(params.path), HexStr(GetObfuscateKey()));
}

MDBXWrapper::~MDBXWrapper() = default;
Expand Down
5 changes: 5 additions & 0 deletions src/dbwrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ struct DBParams {
//! If true, store data obfuscated via simple XOR. If false, XOR with a
//! zero'd byte array.
bool obfuscate = false;
//! Open the database in read-only mode, e.g., from a process outside bitcoind
//! Note: leveldb does not support multi-process readers, so it might make
//! more sense to have this be a DBOptions flag, since it is implementation
//! specific?
bool read_only = false;
//! Passed-through options.
DBOptions options{};
};
Expand Down
8 changes: 6 additions & 2 deletions src/kernel/bitcoinkernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,10 @@ void kernel_chainstate_load_options_set(
chainstate_load_opts->coins_db_in_memory = value;
return;
}
case kernel_ChainstateLoadOptionType::kernel_READONLY: {
chainstate_load_opts->read_only = value;
return;
}
} // no default case, so the compiler can warn about missing cases
assert(false);
}
Expand Down Expand Up @@ -1384,7 +1388,7 @@ bool kernel_chainstate_manager_process_transaction(
bool kernel_chainstate_manager_process_block(
const kernel_Context* context_,
kernel_ChainstateManager* chainman_,
kernel_Block* block_,
kernel_Block* block_,
kernel_ProcessBlockStatus* status)
{
auto& chainman{*cast_chainstate_manager(chainman_)};
Expand Down Expand Up @@ -1565,7 +1569,7 @@ kernel_BlockHeader* kernel_get_block_header(kernel_Block* block_)
{
auto block{cast_cblocksharedpointer(block_)};
return reinterpret_cast<kernel_BlockHeader*>(new CBlockHeader{(*block)->GetBlockHeader()});
}
}

kernel_BlockHeader* kernel_block_header_create(const unsigned char* raw_block_header, size_t raw_block_header_len)
{
Expand Down
3 changes: 2 additions & 1 deletion src/kernel/bitcoinkernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ typedef enum {
kernel_WIPE_CHAINSTATE_DB_CHAINSTATE_LOAD_OPTION, //! Set the wipe chainstate option, default is false.
kernel_BLOCK_TREE_DB_IN_MEMORY_CHAINSTATE_LOAD_OPTION, //! Set the block tree db in memory option, default is false.
kernel_CHAINSTATE_DB_IN_MEMORY_CHAINSTATE_LOAD_OPTION, //! Set the coins db in memory option, default is false.
kernel_READONLY, //! Open the block tree db and coins db in read-only mode, e.g., from a process outside bitcoind
} kernel_ChainstateLoadOptionType;

/**
Expand Down Expand Up @@ -1129,7 +1130,7 @@ kernel_BlockUndo* BITCOINKERNEL_WARN_UNUSED_RESULT kernel_read_block_undo_from_d

/**
* @brief Validates a passed in block header and on success adds it to the header chain.
*
*
* @param[in] chainman Non-null.
* @param[in] header Non-null, the header to be validated.
* @return True if the header was successfully validated.
Expand Down
4 changes: 3 additions & 1 deletion src/node/chainstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ static ChainstateLoadResult CompleteChainstateInitialization(
.cache_bytes = static_cast<size_t>(cache_sizes.block_tree_db),
.memory_only = options.block_tree_db_in_memory,
.wipe_data = options.wipe_block_tree_db,
.read_only = options.read_only,
.options = chainman.m_options.block_tree_db});

if (options.wipe_block_tree_db) {
Expand Down Expand Up @@ -110,7 +111,8 @@ static ChainstateLoadResult CompleteChainstateInitialization(
chainstate->InitCoinsDB(
/*cache_size_bytes=*/chainman.m_total_coinsdb_cache * init_cache_fraction,
/*in_memory=*/options.coins_db_in_memory,
/*should_wipe=*/options.wipe_chainstate_db);
/*should_wipe=*/options.wipe_chainstate_db,
/*read_only=*/options.read_only);

if (options.coins_error_cb) {
chainstate->CoinsErrorCatcher().AddReadErrCallback(options.coins_error_cb);
Expand Down
3 changes: 3 additions & 0 deletions src/node/chainstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ struct ChainstateLoadOptions {
// will cause the chainstate database to be rebuilt starting from genesis.
bool wipe_chainstate_db{false};
bool prune{false};
// Open the block_tree and coins_db databases in read only mode, e.g., from a
// process other than bitcoind
bool read_only{false};
//! Setting require_full_verification to true will require all checks at
//! check_level (below) to succeed for loading to succeed. Setting it to
//! false will skip checks if cache is not big enough to run them, so may be
Expand Down
2 changes: 1 addition & 1 deletion src/test/validation_chainstate_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
CTxMemPool& mempool = *Assert(m_node.mempool);
Chainstate& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
c1.InitCoinsDB(
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false, /*read_only=*/false);
WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));
BOOST_REQUIRE(c1.LoadGenesisBlock()); // Need at least one block loaded to be able to flush caches

Expand Down
4 changes: 2 additions & 2 deletions src/test/validation_chainstatemanager_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager, TestChain100Setup)
Chainstate& c2 = WITH_LOCK(::cs_main, return manager.ActivateExistingSnapshot(snapshot_blockhash));
chainstates.push_back(&c2);
c2.InitCoinsDB(
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false, /*read_only=*/false);
{
LOCK(::cs_main);
c2.InitCoinsCache(1 << 23);
Expand Down Expand Up @@ -138,7 +138,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_rebalance_caches, TestChain100Setup)
Chainstate& c2 = WITH_LOCK(cs_main, return manager.ActivateExistingSnapshot(*snapshot_base->phashBlock));
chainstates.push_back(&c2);
c2.InitCoinsDB(
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false, /*read_only=*/false);

// Reset IBD state so IsInitialBlockDownload() returns true and causes
// MaybeRebalancesCaches() to prioritize the snapshot chainstate, giving it
Expand Down
2 changes: 2 additions & 0 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1961,6 +1961,7 @@ void Chainstate::InitCoinsDB(
size_t cache_size_bytes,
bool in_memory,
bool should_wipe,
bool read_only,
fs::path leveldb_name)
{
if (m_from_snapshot_blockhash) {
Expand All @@ -1974,6 +1975,7 @@ void Chainstate::InitCoinsDB(
.memory_only = in_memory,
.wipe_data = should_wipe,
.obfuscate = true,
.read_only = read_only,
.options = m_chainman.m_options.coins_db},
m_chainman.m_options.coins_view);
}
Expand Down
1 change: 1 addition & 0 deletions src/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,7 @@ class Chainstate
size_t cache_size_bytes,
bool in_memory,
bool should_wipe,
bool read_only,
fs::path leveldb_name = "chainstate");

//! Initialize the in-memory coins cache (to be done after the health of the on-disk database
Expand Down

0 comments on commit bf1843b

Please sign in to comment.