Skip to content

Commit bf1843b

Browse files
committed
add read only option for mdbx
1 parent 0516a4c commit bf1843b

10 files changed

+37
-12
lines changed

src/dbwrapper.cpp

+12-5
Original file line numberDiff line numberDiff line change
@@ -420,14 +420,14 @@ MDBXWrapper::MDBXWrapper(const DBParams& params)
420420
: CDBWrapperBase(params),
421421
m_db_context{std::make_unique<MDBXContext>()}
422422
{
423-
if (params.wipe_data) {
423+
if (params.wipe_data && !params.read_only) {
424424
LogInfo("Wiping MDBX in %s\n", fs::PathToString(params.path));
425425
DestroyDB(fs::PathToString(params.path));
426426
}
427427

428428
TryCreateDirectories(params.path);
429429

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

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

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

439+
// Set read-only mode if specified
440+
if (params.read_only) {
441+
DBContext().operate_params.mode = mdbx::env::mode::readonly;
442+
}
443+
439444
// initialize the mdbx environment.
440445
DBContext().env = mdbx::env_managed(params.path, DBContext().create_params, DBContext().operate_params);
441446

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

446-
if (params.obfuscate && WriteObfuscateKeyIfNotExists()){
447-
LogInfo("Wrote new obfuscate key for %s: %s\n", fs::PathToString(params.path), HexStr(obfuscate_key));
451+
if (!params.read_only) {
452+
if (params.obfuscate && WriteObfuscateKeyIfNotExists()){
453+
LogInfo("Wrote new obfuscate key for %s: %s\n", fs::PathToString(params.path), HexStr(obfuscate_key));
454+
}
455+
LogInfo("Using obfuscation key for %s: %s\n", fs::PathToString(params.path), HexStr(GetObfuscateKey()));
448456
}
449-
LogInfo("Using obfuscation key for %s: %s\n", fs::PathToString(params.path), HexStr(GetObfuscateKey()));
450457
}
451458

452459
MDBXWrapper::~MDBXWrapper() = default;

src/dbwrapper.h

+5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ struct DBParams {
4242
//! If true, store data obfuscated via simple XOR. If false, XOR with a
4343
//! zero'd byte array.
4444
bool obfuscate = false;
45+
//! Open the database in read-only mode, e.g., from a process outside bitcoind
46+
//! Note: leveldb does not support multi-process readers, so it might make
47+
//! more sense to have this be a DBOptions flag, since it is implementation
48+
//! specific?
49+
bool read_only = false;
4550
//! Passed-through options.
4651
DBOptions options{};
4752
};

src/kernel/bitcoinkernel.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,10 @@ void kernel_chainstate_load_options_set(
10191019
chainstate_load_opts->coins_db_in_memory = value;
10201020
return;
10211021
}
1022+
case kernel_ChainstateLoadOptionType::kernel_READONLY: {
1023+
chainstate_load_opts->read_only = value;
1024+
return;
1025+
}
10221026
} // no default case, so the compiler can warn about missing cases
10231027
assert(false);
10241028
}
@@ -1384,7 +1388,7 @@ bool kernel_chainstate_manager_process_transaction(
13841388
bool kernel_chainstate_manager_process_block(
13851389
const kernel_Context* context_,
13861390
kernel_ChainstateManager* chainman_,
1387-
kernel_Block* block_,
1391+
kernel_Block* block_,
13881392
kernel_ProcessBlockStatus* status)
13891393
{
13901394
auto& chainman{*cast_chainstate_manager(chainman_)};
@@ -1565,7 +1569,7 @@ kernel_BlockHeader* kernel_get_block_header(kernel_Block* block_)
15651569
{
15661570
auto block{cast_cblocksharedpointer(block_)};
15671571
return reinterpret_cast<kernel_BlockHeader*>(new CBlockHeader{(*block)->GetBlockHeader()});
1568-
}
1572+
}
15691573

15701574
kernel_BlockHeader* kernel_block_header_create(const unsigned char* raw_block_header, size_t raw_block_header_len)
15711575
{

src/kernel/bitcoinkernel.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ typedef enum {
340340
kernel_WIPE_CHAINSTATE_DB_CHAINSTATE_LOAD_OPTION, //! Set the wipe chainstate option, default is false.
341341
kernel_BLOCK_TREE_DB_IN_MEMORY_CHAINSTATE_LOAD_OPTION, //! Set the block tree db in memory option, default is false.
342342
kernel_CHAINSTATE_DB_IN_MEMORY_CHAINSTATE_LOAD_OPTION, //! Set the coins db in memory option, default is false.
343+
kernel_READONLY, //! Open the block tree db and coins db in read-only mode, e.g., from a process outside bitcoind
343344
} kernel_ChainstateLoadOptionType;
344345

345346
/**
@@ -1129,7 +1130,7 @@ kernel_BlockUndo* BITCOINKERNEL_WARN_UNUSED_RESULT kernel_read_block_undo_from_d
11291130

11301131
/**
11311132
* @brief Validates a passed in block header and on success adds it to the header chain.
1132-
*
1133+
*
11331134
* @param[in] chainman Non-null.
11341135
* @param[in] header Non-null, the header to be validated.
11351136
* @return True if the header was successfully validated.

src/node/chainstate.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ static ChainstateLoadResult CompleteChainstateInitialization(
4646
.cache_bytes = static_cast<size_t>(cache_sizes.block_tree_db),
4747
.memory_only = options.block_tree_db_in_memory,
4848
.wipe_data = options.wipe_block_tree_db,
49+
.read_only = options.read_only,
4950
.options = chainman.m_options.block_tree_db});
5051

5152
if (options.wipe_block_tree_db) {
@@ -110,7 +111,8 @@ static ChainstateLoadResult CompleteChainstateInitialization(
110111
chainstate->InitCoinsDB(
111112
/*cache_size_bytes=*/chainman.m_total_coinsdb_cache * init_cache_fraction,
112113
/*in_memory=*/options.coins_db_in_memory,
113-
/*should_wipe=*/options.wipe_chainstate_db);
114+
/*should_wipe=*/options.wipe_chainstate_db,
115+
/*read_only=*/options.read_only);
114116

115117
if (options.coins_error_cb) {
116118
chainstate->CoinsErrorCatcher().AddReadErrCallback(options.coins_error_cb);

src/node/chainstate.h

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ struct ChainstateLoadOptions {
3030
// will cause the chainstate database to be rebuilt starting from genesis.
3131
bool wipe_chainstate_db{false};
3232
bool prune{false};
33+
// Open the block_tree and coins_db databases in read only mode, e.g., from a
34+
// process other than bitcoind
35+
bool read_only{false};
3336
//! Setting require_full_verification to true will require all checks at
3437
//! check_level (below) to succeed for loading to succeed. Setting it to
3538
//! false will skip checks if cache is not big enough to run them, so may be

src/test/validation_chainstate_tests.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
2929
CTxMemPool& mempool = *Assert(m_node.mempool);
3030
Chainstate& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
3131
c1.InitCoinsDB(
32-
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
32+
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false, /*read_only=*/false);
3333
WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));
3434
BOOST_REQUIRE(c1.LoadGenesisBlock()); // Need at least one block loaded to be able to flush caches
3535

src/test/validation_chainstatemanager_tests.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager, TestChain100Setup)
7272
Chainstate& c2 = WITH_LOCK(::cs_main, return manager.ActivateExistingSnapshot(snapshot_blockhash));
7373
chainstates.push_back(&c2);
7474
c2.InitCoinsDB(
75-
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
75+
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false, /*read_only=*/false);
7676
{
7777
LOCK(::cs_main);
7878
c2.InitCoinsCache(1 << 23);
@@ -138,7 +138,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_rebalance_caches, TestChain100Setup)
138138
Chainstate& c2 = WITH_LOCK(cs_main, return manager.ActivateExistingSnapshot(*snapshot_base->phashBlock));
139139
chainstates.push_back(&c2);
140140
c2.InitCoinsDB(
141-
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
141+
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false, /*read_only=*/false);
142142

143143
// Reset IBD state so IsInitialBlockDownload() returns true and causes
144144
// MaybeRebalancesCaches() to prioritize the snapshot chainstate, giving it

src/validation.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -1961,6 +1961,7 @@ void Chainstate::InitCoinsDB(
19611961
size_t cache_size_bytes,
19621962
bool in_memory,
19631963
bool should_wipe,
1964+
bool read_only,
19641965
fs::path leveldb_name)
19651966
{
19661967
if (m_from_snapshot_blockhash) {
@@ -1974,6 +1975,7 @@ void Chainstate::InitCoinsDB(
19741975
.memory_only = in_memory,
19751976
.wipe_data = should_wipe,
19761977
.obfuscate = true,
1978+
.read_only = read_only,
19771979
.options = m_chainman.m_options.coins_db},
19781980
m_chainman.m_options.coins_view);
19791981
}

src/validation.h

+1
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ class Chainstate
574574
size_t cache_size_bytes,
575575
bool in_memory,
576576
bool should_wipe,
577+
bool read_only,
577578
fs::path leveldb_name = "chainstate");
578579

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

0 commit comments

Comments
 (0)