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.
1// Copyright (c) The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <dbwrapper.h>6#include <compat/byteswap.h>7#include <random.h>8#include <sync.h>9#include <test/fuzz/FuzzedDataProvider.h>10#include <test/fuzz/fuzz.h>11#include <test/fuzz/util.h>12#include <test/util/random.h>13#include <test/util/setup_common.h>14#include <util/byte_units.h>15#include <util/check.h>16#include <util/threadpool.h>18#include <leveldb/env.h>19#include <leveldb/helpers/memenv/memenv.h>21#include <algorithm>22#include <cassert>23#include <cstdint>24#include <deque>25#include <functional>26#include <future>27#include <latch>28#include <map>29#include <memory>30#include <numeric>31#include <optional>32#include <set>33#include <span>34#include <string>35#include <tuple>36#include <vector>38namespace {40/**41* A leveldb::Env that wraps a memenv and captures scheduled background42* work (compaction) instead of dispatching to a real thread. The fuzz43* harness calls RunOne() or DrainWork() at fuzzer-chosen points to44* execute it, giving deterministic control over when compaction45* interleaves with foreground operations.46*47* Deadlock prevention: LevelDB's MakeRoomForWrite blocks on a condition48* variable when the previous immutable memtable is still awaiting compaction,49* or when the L0 file count hits kL0_StopWritesTrigger. Since both conditions50* can only be resolved by the (deferred) background work, the harness drains51* all pending work before every write to avoid a single-threaded deadlock.52* Callers must also DrainWork() before destroying the CDBWrapper, since the53* leveldb destructor waits for any pending background work to complete.54*55* The same reasoning rules out exercising DBOptions::force_compact under56* this env, because CompactRange(nullptr, nullptr) blocks waiting for57* background work that is queued on the (blocked) foreground thread. The58* sibling dbwrapper_threaded target covers that path.59*/60class DeterministicEnv final : public leveldb::EnvWrapper61{62using WorkFunction = void (*)(void*);64struct Work {65WorkFunction function;66void* arg;67};69Mutex m_mutex;70std::deque<Work> m_queue GUARDED_BY(m_mutex);72public:73explicit DeterministicEnv(leveldb::Env* base) : EnvWrapper(base) {}75void Schedule(WorkFunction function, void* arg) override EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)76{77LOCK(m_mutex);78m_queue.push_back({function, arg});79}81/** Execute one pending background task. The task may schedule a82* successor which is left pending for a later call. */83bool RunOne() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)84{85Work work;86{87LOCK(m_mutex);88if (m_queue.empty()) return false;89work = m_queue.front();90m_queue.pop_front();91}92work.function(work.arg);93return true;94}96/** Execute pending background tasks until none remain. */97void DrainWork() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) { while (RunOne()) {} }98};100constexpr size_t MAX_VALUE_LEN{4096};101constexpr uint8_t MAX_VALUE_MULTIPLIER{8};102constexpr size_t WRITE_BATCH_HEADER{12}; // See kHeader in db/write_batch.cc104/** Mirror of CDBWrapper::OBFUSCATION_KEY, the fixed key under which leveldb105* stores the obfuscation metadata entry when obfuscation is enabled. */106const std::string OBFUSCATION_KEY{"\000obfuscate_key", 14};108/** Generate a deterministic value from key and size. The fuzz input picks109* a 16-bit length (up to MAX_VALUE_LEN) and an 8-bit multiplier so that a110* small amount of fuzz input can produce a wide range of value sizes. */111std::vector<uint8_t> MakeValue(uint16_t key, uint32_t size)112{113std::vector<uint8_t> v(size);114std::iota(v.begin(), v.end(), static_cast<uint8_t>(key ^ (key >> 8)));115return v;116}118/** Equivalent to leveldb::BytewiseComparator() on 2-byte little-endian119* serialized uint16_t keys, while keeping the oracle keyed by uint16_t. */120struct LevelDBBytewiseU16Cmp {121bool operator()(uint16_t a, uint16_t b) const { return internal_bswap_16(a) < internal_bswap_16(b); }122};124/** key → value-size map ordered by LevelDB's bytewise comparator. */125using Oracle = std::map<uint16_t, uint32_t, LevelDBBytewiseU16Cmp>;127struct FailUnserialize {128template <typename Stream>129void Unserialize(Stream&) { throw std::ios_base::failure{"always fail"}; }130};132uint16_t ConsumeKey(FuzzedDataProvider& provider) { return provider.ConsumeIntegral<uint16_t>(); }133uint32_t ConsumeValueSize(FuzzedDataProvider& provider)134{135const uint16_t len{provider.ConsumeIntegralInRange<uint16_t>(0, MAX_VALUE_LEN)};136const uint8_t multiplier{provider.ConsumeIntegralInRange<uint8_t>(1, MAX_VALUE_MULTIPLIER)};137return static_cast<uint32_t>(len) * multiplier;138}140/** Verify that the DB iterator matches the oracle, handling the obfuscation141* metadata entry (stored under a non-uint16_t key) when obfuscation is on. */142void VerifyIterator(CDBWrapper& dbw, const Oracle& oracle,143bool obfuscate, std::optional<uint16_t> seek_key = std::nullopt)144{145const std::unique_ptr<CDBIterator> it{dbw.NewIterator()};146auto oracle_it{seek_key ? oracle.lower_bound(*seek_key) : oracle.begin()};147if (seek_key) {148it->Seek(*seek_key);149} else {150it->SeekToFirst();151}152for (; it->Valid(); it->Next()) {153uint16_t db_key;154assert(it->GetKey(db_key));155if (oracle_it != oracle.end() && db_key == oracle_it->first) {156std::vector<uint8_t> db_value;157assert(it->GetValue(db_value));158assert(db_value == MakeValue(db_key, oracle_it->second));159++oracle_it;160} else {161assert(obfuscate);162std::string key_str;163assert(it->GetKey(key_str));164assert(key_str == OBFUSCATION_KEY);165}166}167assert(oracle_it == oracle.end());168}170/** Maximum number of concurrent reader threads in dbwrapper_concurrent_reads. */171constexpr size_t MAX_READ_WORKERS{8};173/** Maximum number of queries each worker executes in dbwrapper_concurrent_reads. */174constexpr size_t MAX_READ_QUERIES_PER_WORKER{128};176ThreadPool g_read_pool{"dbfuzz"};178void StartReadPoolIfNeeded()179{180if (!g_read_pool.WorkersCount()) g_read_pool.Start(MAX_READ_WORKERS);181}183/** Build randomized DBParams from the fuzz input, shared by all targets. */184DBParams ConsumeDBParams(FuzzedDataProvider& provider, leveldb::Env* testing_env,185bool obfuscate, DBOptions options = {})186{187return DBParams{188.path = "dbwrapper_fuzz",189.cache_bytes = provider.ConsumeIntegralInRange<size_t>(64 << 10, 1_MiB),190.obfuscate = obfuscate,191.bloom_filter = provider.ConsumeBool(),192.options = options,193.testing_env = testing_env,194.max_file_size = provider.ConsumeBool()195? DBWRAPPER_MAX_FILE_SIZE196: provider.ConsumeIntegralInRange<size_t>(1_MiB, 4_MiB),197};198}200template <typename DrainWorkFn, typename RunOneFn>201void TestDbWrapper(FuzzedDataProvider& provider,202leveldb::Env* testing_env,203DrainWorkFn drain_work,204RunOneFn run_one,205bool allow_force_compact)206{207SeedRandomStateForTest(SeedRand::ZEROS);209const bool obfuscate{provider.ConsumeBool()};211const auto make_db{[&](DBOptions options = {}) {212return std::make_unique<CDBWrapper>(ConsumeDBParams(provider, testing_env, obfuscate, options));213}};214std::unique_ptr<CDBWrapper> dbw{make_db()};216// Oracle: key → value size. Content is reconstructed via MakeValue().217Oracle oracle;219LIMITED_WHILE (provider.ConsumeBool(), 1'000) {220CallOneOf(221provider,222// --- Mutations ---223[&] {224const auto key{ConsumeKey(provider)};225const auto size{ConsumeValueSize(provider)};226drain_work();227dbw->Write(key, MakeValue(key, size), /*fSync=*/provider.ConsumeBool());228oracle[key] = size;229},230[&] {231const auto key{ConsumeKey(provider)};232drain_work();233dbw->Erase(key, /*fSync=*/provider.ConsumeBool());234oracle.erase(key);235},236[&] {237CDBBatch batch{*dbw};238std::map<uint16_t, uint32_t> batch_writes;239std::set<uint16_t> batch_erases;240const auto fill{[&] {241LIMITED_WHILE (provider.ConsumeBool(), 20) {242const auto key{ConsumeKey(provider)};243if (provider.ConsumeBool()) {244const auto size{ConsumeValueSize(provider)};245batch.Write(key, MakeValue(key, size));246batch_writes[key] = size;247batch_erases.erase(key);248} else {249batch.Erase(key);250batch_erases.insert(key);
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.