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) 2011 The LevelDB Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file. See the AUTHORS file for names of contributors.5#include <sys/types.h>7#include "db/db_impl.h"8#include "db/filename.h"9#include "db/log_format.h"10#include "db/version_set.h"11#include "leveldb/cache.h"12#include "leveldb/db.h"13#include "leveldb/table.h"14#include "leveldb/write_batch.h"15#include "util/logging.h"16#include "util/testharness.h"17#include "util/testutil.h"19namespace leveldb {21static const int kValueSize = 1000;23class CorruptionTest {24public:25CorruptionTest()26: db_(nullptr),27dbname_("/memenv/corruption_test"),28tiny_cache_(NewLRUCache(100)) {29options_.env = &env_;30options_.block_cache = tiny_cache_;31DestroyDB(dbname_, options_);33options_.create_if_missing = true;34Reopen();35options_.create_if_missing = false;36}38~CorruptionTest() {39delete db_;40delete tiny_cache_;41}43Status TryReopen() {44delete db_;45db_ = nullptr;46return DB::Open(options_, dbname_, &db_);47}49void Reopen() { ASSERT_OK(TryReopen()); }51void RepairDB() {52delete db_;53db_ = nullptr;54ASSERT_OK(::leveldb::RepairDB(dbname_, options_));55}57void Build(int n) {58std::string key_space, value_space;59WriteBatch batch;60for (int i = 0; i < n; i++) {61// if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);62Slice key = Key(i, &key_space);63batch.Clear();64batch.Put(key, Value(i, &value_space));65WriteOptions options;66// Corrupt() doesn't work without this sync on windows; stat reports 0 for67// the file size.68if (i == n - 1) {69options.sync = true;70}71ASSERT_OK(db_->Write(options, &batch));72}73}75void Check(int min_expected, int max_expected) {76int next_expected = 0;77int missed = 0;78int bad_keys = 0;79int bad_values = 0;80int correct = 0;81std::string value_space;82Iterator* iter = db_->NewIterator(ReadOptions());83for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {84uint64_t key;85Slice in(iter->key());86if (in == "" || in == "~") {87// Ignore boundary keys.88continue;89}90if (!ConsumeDecimalNumber(&in, &key) || !in.empty() ||91key < next_expected) {92bad_keys++;93continue;94}95missed += (key - next_expected);96next_expected = key + 1;97if (iter->value() != Value(key, &value_space)) {98bad_values++;99} else {100correct++;101}102}103delete iter;105fprintf(stderr,106"expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n",107min_expected, max_expected, correct, bad_keys, bad_values, missed);108ASSERT_LE(min_expected, correct);109ASSERT_GE(max_expected, correct);110}112void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {113// Pick file to corrupt114std::vector<std::string> filenames;115ASSERT_OK(env_.target()->GetChildren(dbname_, &filenames));116uint64_t number;117FileType type;118std::string fname;119int picked_number = -1;120for (size_t i = 0; i < filenames.size(); i++) {121if (ParseFileName(filenames[i], &number, &type) && type == filetype &&122int(number) > picked_number) { // Pick latest file123fname = dbname_ + "/" + filenames[i];124picked_number = number;125}126}127ASSERT_TRUE(!fname.empty()) << filetype;129uint64_t file_size;130ASSERT_OK(env_.target()->GetFileSize(fname, &file_size));132if (offset < 0) {133// Relative to end of file; make it absolute134if (-offset > file_size) {135offset = 0;136} else {137offset = file_size + offset;138}139}140if (offset > file_size) {141offset = file_size;142}143if (offset + bytes_to_corrupt > file_size) {144bytes_to_corrupt = file_size - offset;145}147// Do it148std::string contents;149Status s = ReadFileToString(env_.target(), fname, &contents);150ASSERT_TRUE(s.ok()) << s.ToString();151for (int i = 0; i < bytes_to_corrupt; i++) {152contents[i + offset] ^= 0x80;153}154s = WriteStringToFile(env_.target(), contents, fname);155ASSERT_TRUE(s.ok()) << s.ToString();156}158int Property(const std::string& name) {159std::string property;160int result;161if (db_->GetProperty(name, &property) &&162sscanf(property.c_str(), "%d", &result) == 1) {163return result;164} else {165return -1;166}167}169// Return the ith key170Slice Key(int i, std::string* storage) {171char buf[100];172snprintf(buf, sizeof(buf), "%016d", i);173storage->assign(buf, strlen(buf));174return Slice(*storage);175}177// Return the value to associate with the specified key178Slice Value(int k, std::string* storage) {179Random r(k);180return test::RandomString(&r, kValueSize, storage);181}183test::ErrorEnv env_;184Options options_;185DB* db_;187private:188std::string dbname_;189Cache* tiny_cache_;190};192TEST(CorruptionTest, Recovery) {193Build(100);194Check(100, 100);195Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record196Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block197Reopen();199// The 64 records in the first two log blocks are completely lost.200Check(36, 36);201}203TEST(CorruptionTest, RecoverWriteError) {204env_.writable_file_error_ = true;205Status s = TryReopen();206ASSERT_TRUE(!s.ok());207}209TEST(CorruptionTest, NewFileErrorDuringWrite) {210// Do enough writing to force minor compaction211env_.writable_file_error_ = true;212const int num = 3 + (Options().write_buffer_size / kValueSize);213std::string value_storage;214Status s;215for (int i = 0; s.ok() && i < num; i++) {216WriteBatch batch;217batch.Put("a", Value(100, &value_storage));218s = db_->Write(WriteOptions(), &batch);219}220ASSERT_TRUE(!s.ok());221ASSERT_GE(env_.num_writable_file_errors_, 1);222env_.writable_file_error_ = false;223Reopen();224}226TEST(CorruptionTest, TableFile) {227Build(100);228DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);229dbi->TEST_CompactMemTable();230dbi->TEST_CompactRange(0, nullptr, nullptr);231dbi->TEST_CompactRange(1, nullptr, nullptr);233Corrupt(kTableFile, 100, 1);234Check(90, 99);235}237TEST(CorruptionTest, TableFileRepair) {238options_.block_size = 2 * kValueSize; // Limit scope of corruption239options_.paranoid_checks = true;240Reopen();241Build(100);242DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);243dbi->TEST_CompactMemTable();244dbi->TEST_CompactRange(0, nullptr, nullptr);245dbi->TEST_CompactRange(1, nullptr, nullptr);247Corrupt(kTableFile, 100, 1);248RepairDB();249Reopen();250Check(95, 99);
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.