txospenderindex: disable bloom filters to optimize disk usage
What changed, and why it matters
This change is a straightforward performance and disk-space optimization. It adds an option to disable LevelDB bloom filters for database instances that only scan through data with iterators, because bloom filters only help when looking up individual records directly. The txospenderindex is updated to use this new option. There is no security bug being fixed here.
No security action required. Treat as a normal optimization/refactoring commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit introduces a bloom_filter boolean to DBParams, plumbs it through GetOptions() to conditionally set options.filter_policy, and defaults it to true to preserve existing behavior. BaseIndex::DB gains an f_bloom parameter defaulting to true. The TxoSpenderIndex constructor explicitly passes f_bloom=false because its reads are iterator-based and bloom filters are not consulted during iterator seeks. The fuzz test is updated to randomize the new parameter.
Changed components
src/dbwrapper.cppsrc/dbwrapper.hsrc/index/base.cppsrc/index/base.hsrc/index/txospenderindex.cppsrc/test/fuzz/dbwrapper.cppInspect captured patch +10 / −6
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index ffe6f267..ed914273 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -136,12 +136,12 @@ static void SetMaxOpenFiles(leveldb::Options *options) {
options->max_open_files, default_open_files);
}
-static leveldb::Options GetOptions(size_t nCacheSize)
+static leveldb::Options GetOptions(size_t nCacheSize, bool bloom_filter)
{
leveldb::Options options;
options.block_cache = leveldb::NewLRUCache(nCacheSize / 2);
options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously
- options.filter_policy = leveldb::NewBloomFilterPolicy(10);
+ options.filter_policy = bloom_filter ? leveldb::NewBloomFilterPolicy(10) : nullptr;
options.compression = leveldb::kNoCompression;
options.info_log = new CBitcoinLevelDBLogger();
if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) {
@@ -225,7 +225,7 @@ CDBWrapper::CDBWrapper(const DBParams& params)
DBContext().iteroptions.verify_checksums = true;
DBContext().iteroptions.fill_cache = false;
DBContext().syncoptions.sync = true;
- DBContext().options = GetOptions(params.cache_bytes);
+ DBContext().options = GetOptions(params.cache_bytes, params.bloom_filter);
DBContext().options.create_if_missing = true;
DBContext().options.max_file_size = params.max_file_size;
assert(!(params.testing_env && params.memory_only));
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index 83da6feb..1e2174e2 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -47,6 +47,8 @@ struct DBParams {
//! If true, store data obfuscated via simple XOR. If false, XOR with a
//! zero'd byte array.
bool obfuscate = false;
+ //! If true, build a LevelDB bloom filter to accelerate point lookups.
+ bool bloom_filter = true;
//! Passed-through options.
DBOptions options{};
//! If non-null, use this as the leveldb::Env instead of the default.
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 906ed265..3c474d7c 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -65,13 +65,14 @@ CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash)
return locator;
}
-BaseIndex::DB::DB(const fs::path& path, size_t n_cache_size, bool f_memory, bool f_wipe, bool f_obfuscate) :
+BaseIndex::DB::DB(const fs::path& path, size_t n_cache_size, bool f_memory, bool f_wipe, bool f_obfuscate, bool f_bloom) :
CDBWrapper{DBParams{
.path = path,
.cache_bytes = n_cache_size,
.memory_only = f_memory,
.wipe_data = f_wipe,
.obfuscate = f_obfuscate,
+ .bloom_filter = f_bloom,
.options = [] { DBOptions options; node::ReadDatabaseArgs(gArgs, options); return options; }()}}
{}
diff --git a/src/index/base.h b/src/index/base.h
index 6d7e86ec..c39ac7a3 100644
--- a/src/index/base.h
+++ b/src/index/base.h
@@ -65,7 +65,7 @@ protected:
{
public:
DB(const fs::path& path, size_t n_cache_size,
- bool f_memory = false, bool f_wipe = false, bool f_obfuscate = false);
+ bool f_memory = false, bool f_wipe = false, bool f_obfuscate = false, bool f_bloom = true);
/// Read block locator of the chain that the index is in sync with.
/// Note, the returned locator will be empty if no record exists.
diff --git a/src/index/txospenderindex.cpp b/src/index/txospenderindex.cpp
index 50b9bfeb..304104df 100644
--- a/src/index/txospenderindex.cpp
+++ b/src/index/txospenderindex.cpp
@@ -62,7 +62,7 @@ struct DBKey {
};
TxoSpenderIndex::TxoSpenderIndex(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory, bool f_wipe)
- : BaseIndex(std::move(chain), "txospenderindex", "txospenderidx"), m_db{std::make_unique<DB>(gArgs.GetDataDirNet() / "indexes" / "txospenderindex" / "db", n_cache_size, f_memory, f_wipe)}
+ : BaseIndex(std::move(chain), "txospenderindex", "txospenderidx"), m_db{std::make_unique<DB>(gArgs.GetDataDirNet() / "indexes" / "txospenderindex" / "db", n_cache_size, f_memory, f_wipe, /*f_obfuscate=*/false, /*f_bloom=*/false)}
{
if (!m_db->Read("siphash_key", m_siphash_key)) {
FastRandomContext rng(false);
diff --git a/src/test/fuzz/dbwrapper.cpp b/src/test/fuzz/dbwrapper.cpp
index 265f0ca3..66b7327f 100644
--- a/src/test/fuzz/dbwrapper.cpp
+++ b/src/test/fuzz/dbwrapper.cpp
@@ -188,6 +188,7 @@ DBParams ConsumeDBParams(FuzzedDataProvider& provider, leveldb::Env* testing_env
.path = "dbwrapper_fuzz",
.cache_bytes = provider.ConsumeIntegralInRange<size_t>(64 << 10, 1_MiB),
.obfuscate = obfuscate,
+ .bloom_filter = provider.ConsumeBool(),
.options = options,
.testing_env = testing_env,
.max_file_size = provider.ConsumeBool()
Why this scored 15/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.