Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251batch_writes.erase(key);252}253}254}};255fill();256if (provider.ConsumeBool()) {257assert(batch.ApproximateSize() >= WRITE_BATCH_HEADER);258batch.Clear();259assert(batch.ApproximateSize() == WRITE_BATCH_HEADER);260batch_writes.clear();261batch_erases.clear();262fill();263}264drain_work();265dbw->WriteBatch(batch, /*fSync=*/provider.ConsumeBool());266for (const auto& [k, v] : batch_writes) oracle[k] = v;267for (const auto& k : batch_erases) oracle.erase(k);268},269[&] {270drain_work();271dbw.reset();272DBOptions options{};273if (allow_force_compact && provider.ConsumeBool()) {274options.force_compact = true;275}276dbw = make_db(options);277VerifyIterator(*dbw, oracle, obfuscate);278},279// --- Reads ---280[&] {281const auto key{ConsumeKey(provider)};282std::vector<uint8_t> value;283const bool found{dbw->Read(key, value)};284if (const auto it{oracle.find(key)}; it != oracle.end()) {285assert(found && value == MakeValue(key, it->second));286} else {287assert(!found);288}289},290[&] {291const auto key{ConsumeKey(provider)};292assert(dbw->Exists(key) == oracle.contains(key));293},294[&] {295uint16_t key{};296if (!oracle.empty() && provider.ConsumeBool()) {297auto it{oracle.begin()};298std::advance(it, provider.ConsumeIntegralInRange<size_t>(0, oracle.size() - 1));299key = it->first;300} else {301key = ConsumeKey(provider);302}303FailUnserialize wrong_type;304assert(!dbw->Read(key, wrong_type));305},306[&] {307const auto seek_key{provider.ConsumeBool()308? std::optional<uint16_t>{ConsumeKey(provider)}309: std::nullopt};310VerifyIterator(*dbw, oracle, obfuscate, seek_key);311},312// --- Stats ---313[&] {314assert(dbw->IsEmpty() == (oracle.empty() && !obfuscate));315},316[&] {317const auto [k1, k2]{std::minmax({ConsumeKey(provider), ConsumeKey(provider)}, LevelDBBytewiseU16Cmp{})};318const size_t estimate_size{dbw->EstimateSize(k1, k2)};319if (k1 == k2) assert(estimate_size == 0);320},321[&] {322(void)dbw->DynamicMemoryUsage();323},324// --- Compaction control (no-op when run_one is no-op) ---325[&] {326run_one();327});328}330VerifyIterator(*dbw, oracle, obfuscate);331drain_work();332}334} // namespace336FUZZ_TARGET(dbwrapper, .init = [] { static auto setup{MakeNoLogFileContext<>()}; })337{338FuzzedDataProvider provider{buffer.data(), buffer.size()};340const auto memenv{std::unique_ptr<leveldb::Env>{leveldb::NewMemEnv(leveldb::Env::Default())}};341DeterministicEnv det_env{memenv.get()};342TestDbWrapper(343provider, &det_env,344[&] { det_env.DrainWork(); },345[&] { return det_env.RunOne(); },346/*allow_force_compact=*/false);347}349FUZZ_TARGET(dbwrapper_threaded, .init = [] { static auto setup{MakeNoLogFileContext<>()}; })350{351FuzzedDataProvider provider{buffer.data(), buffer.size()};353const auto memenv{std::unique_ptr<leveldb::Env>{leveldb::NewMemEnv(leveldb::Env::Default())}};354TestDbWrapper(355provider, memenv.get(),356/*drain_work=*/[] {},357/*run_one=*/[] { return false; },358/*allow_force_compact=*/true);359}361FUZZ_TARGET(dbwrapper_concurrent_reads, .init = [] { static auto setup{MakeNoLogFileContext<>()}; })362{363StartReadPoolIfNeeded();364SeedRandomStateForTest(SeedRand::ZEROS);366FuzzedDataProvider provider{buffer.data(), buffer.size()};368const auto memenv{std::unique_ptr<leveldb::Env>{leveldb::NewMemEnv(leveldb::Env::Default())}};369DeterministicEnv det_env{memenv.get()};371CDBWrapper db{ConsumeDBParams(provider, &det_env, /*obfuscate=*/provider.ConsumeBool())};373// Seed the DB. Drain work after small batches so we don't deadlock on a374// scheduled compaction.375const size_t num_entries{provider.ConsumeIntegralInRange<size_t>(100, 5'000)};376std::vector<uint16_t> keys;377keys.reserve(num_entries);378Oracle oracle;379constexpr size_t SEED_BATCH_SIZE{400};380for (size_t start{0}; start < num_entries; start += SEED_BATCH_SIZE) {381CDBBatch batch{db};382const size_t end{std::min(start + SEED_BATCH_SIZE, num_entries)};383for (size_t i{start}; i < end; ++i) {384const auto k{ConsumeKey(provider)};385const auto size{ConsumeValueSize(provider)};386batch.Write(k, MakeValue(k, size));387keys.push_back(k);388oracle[k] = size;389}390det_env.DrainWork();391db.WriteBatch(batch, /*fSync=*/true);392}394while (provider.ConsumeBool() && det_env.RunOne()) {}396// Build query list from seeded and random keys.397const size_t num_queries{provider.ConsumeIntegralInRange<size_t>(1, 2'000)};398enum class ReadOp { Read, Exists, IteratorSeek };399std::vector<std::tuple<ReadOp, uint16_t>> queries;400queries.reserve(num_queries);401for (size_t i{0}; i < num_queries; ++i) {402const auto op{provider.PickValueInArray({ReadOp::Read, ReadOp::Exists, ReadOp::IteratorSeek})};403const uint16_t key{provider.ConsumeBool()404? keys[provider.ConsumeIntegralInRange<size_t>(0, keys.size() - 1)]405: ConsumeKey(provider)};406queries.emplace_back(op, key);407}410// Workers + main thread synchronize on the latch so all reads start together.411std::latch start_latch{static_cast<ptrdiff_t>(MAX_READ_WORKERS + 1)};412std::vector<std::function<void()>> tasks(MAX_READ_WORKERS);413FastRandomContext rng{ConsumeUInt256(provider)};414std::ranges::generate(tasks, [&] {415return [&, seed = rng.rand256()] {416FastRandomContext thread_rng{seed};417std::vector<size_t> order(queries.size());418std::iota(order.begin(), order.end(), size_t{0});419std::ranges::shuffle(order, thread_rng);420const size_t queries_to_run{std::min(queries.size(), MAX_READ_QUERIES_PER_WORKER)};421std::vector<uint8_t> v;422std::string key_str;423start_latch.arrive_and_wait();424const std::unique_ptr<CDBIterator> it{db.NewIterator()};425// Every read must agree with the oracle, the source of truth.426for (const auto i : std::span{order}.first(queries_to_run)) {427const auto& [op, key] = queries[i];428switch (op) {429case ReadOp::Read:430if (const auto oit{oracle.find(key)}; oit != oracle.end()) {431assert(db.Read(key, v) && v == MakeValue(key, oit->second));432} else {433assert(!db.Read(key, v));434}435break;436case ReadOp::Exists:437assert(db.Exists(key) == oracle.contains(key));438break;439case ReadOp::IteratorSeek:440it->Seek(key);441// Skip the obfuscation metadata entry (a non-uint16_t key) if we land442// on it, so the result matches the oracle, which only tracks user keys.443if (it->Valid() && it->GetKey(key_str) && key_str == OBFUSCATION_KEY) it->Next();444if (const auto oit{oracle.lower_bound(key)}; oit != oracle.end()) {445assert(it->Valid());446uint16_t actual_key;447assert(it->GetKey(actual_key) && actual_key == oit->first);448assert(it->GetValue(v) && v == MakeValue(actual_key, oit->second));449} else {450assert(!it->Valid());451}452break;453}454}455};456});457auto futures{*Assert(g_read_pool.Submit(std::move(tasks)))};459// Release the workers and immediately run the queued compaction on this460// thread, so compaction races against the concurrent reads.461start_latch.arrive_and_wait();462det_env.DrainWork();464for (auto& fut : futures) fut.get();465det_env.DrainWork();466}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.