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) 2018-present 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 <boost/test/unit_test.hpp>7#include <test/util/common.h>8#include <test/util/setup_common.h>9#include <util/check.h>10#include <util/fs.h>11#include <util/translation.h>12#include <wallet/sqlite.h>13#include <wallet/migrate.h>14#include <wallet/test/util.h>15#include <wallet/walletutil.h>17#include <cstddef>18#include <memory>19#include <span>20#include <string>21#include <string_view>22#include <utility>23#include <vector>25inline std::ostream& operator<<(std::ostream& os, const std::pair<const SerializeData, SerializeData>& kv)26{27std::span key{kv.first}, value{kv.second};28os << "(\"" << std::string_view{reinterpret_cast<const char*>(key.data()), key.size()} << "\", \""29<< std::string_view{reinterpret_cast<const char*>(value.data()), value.size()} << "\")";30return os;31}33namespace wallet {35inline std::span<const std::byte> StringBytes(std::string_view str)36{37return std::as_bytes(std::span{str});38}40static SerializeData StringData(std::string_view str)41{42auto bytes = StringBytes(str);43return SerializeData{bytes.begin(), bytes.end()};44}46static void CheckPrefix(DatabaseBatch& batch, std::span<const std::byte> prefix, MockableData expected)47{48std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);49MockableData actual;50while (true) {51DataStream key, value;52DatabaseCursor::Status status = cursor->Next(key, value);53if (status == DatabaseCursor::Status::DONE) break;54BOOST_CHECK(status == DatabaseCursor::Status::MORE);55BOOST_CHECK(56actual.emplace(SerializeData(key.begin(), key.end()), SerializeData(value.begin(), value.end())).second);57}58BOOST_CHECK_EQUAL_COLLECTIONS(actual.begin(), actual.end(), expected.begin(), expected.end());59}61BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)63static std::vector<std::unique_ptr<WalletDatabase>> TestDatabases(const fs::path& path_root)64{65std::vector<std::unique_ptr<WalletDatabase>> dbs;66DatabaseOptions options;67DatabaseStatus status;68bilingual_str error;69// Unable to test BerkeleyRO since we cannot create a new BDB database to open70dbs.emplace_back(MakeSQLiteDatabase(path_root / "sqlite", options, status, error));71dbs.emplace_back(CreateMockableWalletDatabase());72return dbs;73}75BOOST_AUTO_TEST_CASE(db_cursor_prefix_range_test)76{77// Test each supported db78for (const auto& database : TestDatabases(m_path_root)) {79std::vector<std::string> prefixes = {"", "FIRST", "SECOND", "P\xfe\xff", "P\xff\x01", "\xff\xff"};81std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();82// Write elements to it83for (unsigned int i = 0; i < 10; i++) {84for (const auto& prefix : prefixes) {85BOOST_CHECK(handler->Write(std::make_pair(prefix, i), i));86}87}89// Now read all the items by prefix and verify that each element gets parsed correctly90for (const auto& prefix : prefixes) {91DataStream s_prefix;92s_prefix << prefix;93std::unique_ptr<DatabaseCursor> cursor = handler->GetNewPrefixCursor(s_prefix);94DataStream key;95DataStream value;96for (int i = 0; i < 10; i++) {97DatabaseCursor::Status status = cursor->Next(key, value);98BOOST_CHECK_EQUAL(status, DatabaseCursor::Status::MORE);100std::string key_back;101unsigned int i_back;102key >> key_back >> i_back;103BOOST_CHECK_EQUAL(key_back, prefix);105unsigned int value_back;106value >> value_back;107BOOST_CHECK_EQUAL(value_back, i_back);108}110// Let's now read it once more, it should return DONE111BOOST_CHECK(cursor->Next(key, value) == DatabaseCursor::Status::DONE);112}113handler.reset();114database->Close();115}116}118// Lower level DatabaseBase::GetNewPrefixCursor test, to cover cases that aren't119// covered in the higher level test above. The higher level test uses120// serialized strings which are prefixed with string length, so it doesn't test121// truly empty prefixes or prefixes that begin with \xff122BOOST_AUTO_TEST_CASE(db_cursor_prefix_byte_test)123{124const MockableData::value_type125e{StringData(""), StringData("e")},126p{StringData("prefix"), StringData("p")},127ps{StringData("prefixsuffix"), StringData("ps")},128f{StringData("\xff"), StringData("f")},129fs{StringData("\xffsuffix"), StringData("fs")},130ff{StringData("\xff\xff"), StringData("ff")},131ffs{StringData("\xff\xffsuffix"), StringData("ffs")};132for (const auto& database : TestDatabases(m_path_root)) {133std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();135// Write elements to it if not berkeleyro136for (const auto& [k, v] : {e, p, ps, f, fs, ff, ffs}) {137batch->Write(std::span{k}, std::span{v});138}140CheckPrefix(*batch, StringBytes(""), {e, p, ps, f, fs, ff, ffs});141CheckPrefix(*batch, StringBytes("prefix"), {p, ps});142CheckPrefix(*batch, StringBytes("\xff"), {f, fs, ff, ffs});143CheckPrefix(*batch, StringBytes("\xff\xff"), {ff, ffs});144batch.reset();145database->Close();146}147}149BOOST_AUTO_TEST_CASE(db_availability_after_write_error)150{151// Ensures the database remains accessible without deadlocking after a write error.152// To simulate the behavior, record overwrites are disallowed, and the test verifies153// that the database remains active after failing to store an existing record.154for (const auto& database : TestDatabases(m_path_root)) {155// Write original record156std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();157std::string key = "key";158std::string value = "value";159std::string value2 = "value_2";160BOOST_CHECK(batch->Write(key, value));161// Attempt to overwrite the record (expect failure)162BOOST_CHECK(!batch->Write(key, value2, /*fOverwrite=*/false));163// Successfully overwrite the record164BOOST_CHECK(batch->Write(key, value2, /*fOverwrite=*/true));165// Sanity-check; read and verify the overwritten value166std::string read_value;167BOOST_CHECK(batch->Read(key, read_value));168BOOST_CHECK_EQUAL(read_value, value2);169}170}172// Verify 'ErasePrefix' functionality using db keys similar to the ones used by the wallet.173// Keys are in the form of std::pair<TYPE, ENTRY_ID>174BOOST_AUTO_TEST_CASE(erase_prefix)175{176const std::string key = "key";177const std::string key2 = "key2";178const std::string value = "value";179const std::string value2 = "value_2";180auto make_key = [](std::string type, std::string id) { return std::make_pair(type, id); };182for (const auto& database : TestDatabases(m_path_root)) {183if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {184// Skip this test if BerkeleyRO185continue;186}187std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();189// Write two entries with the same key type prefix, a third one with a different prefix190// and a fourth one with the type-id values inverted191BOOST_CHECK(batch->Write(make_key(key, value), value));192BOOST_CHECK(batch->Write(make_key(key, value2), value2));193BOOST_CHECK(batch->Write(make_key(key2, value), value));194BOOST_CHECK(batch->Write(make_key(value, key), value));196// Erase the ones with the same prefix and verify result197BOOST_CHECK(batch->TxnBegin());198BOOST_CHECK(batch->ErasePrefix(DataStream() << key));199BOOST_CHECK(batch->TxnCommit());201BOOST_CHECK(!batch->Exists(make_key(key, value)));202BOOST_CHECK(!batch->Exists(make_key(key, value2)));203// Also verify that entries with a different prefix were not erased204BOOST_CHECK(batch->Exists(make_key(key2, value)));205BOOST_CHECK(batch->Exists(make_key(value, key)));206}207}209// Test-only statement execution error210constexpr int TEST_SQLITE_ERROR = -999;212class DbExecBlocker : public SQliteExecHandler213{214private:215SQliteExecHandler m_base_exec;216std::set<std::string> m_blocked_statements;217public:218DbExecBlocker(std::set<std::string> blocked_statements) : m_blocked_statements(blocked_statements) {}219int Exec(SQLiteDatabase& database, const std::string& statement) override {220if (m_blocked_statements.contains(statement)) return TEST_SQLITE_ERROR;221return m_base_exec.Exec(database, statement);222}223};225BOOST_AUTO_TEST_CASE(txn_close_failure_dangling_txn)226{227// Verifies that there is no active dangling, to-be-reversed db txn228// after the batch object that initiated it is destroyed.229DatabaseOptions options;230DatabaseStatus status;231bilingual_str error;232std::unique_ptr<SQLiteDatabase> database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);234std::string key = "key";235std::string value = "value";237std::unique_ptr<SQLiteBatch> batch = std::make_unique<SQLiteBatch>(*database);238BOOST_CHECK(batch->TxnBegin());239BOOST_CHECK(batch->Write(key, value));240// Set a handler to prevent txn abortion during destruction.241// Mimicking a db statement execution failure.242batch->SetExecHandler(std::make_unique<DbExecBlocker>(std::set<std::string>{"ROLLBACK TRANSACTION"}));243// Destroy batch244batch.reset();246// Ensure there is no dangling, to-be-reversed db txn247BOOST_CHECK(!database->HasActiveTxn());249// And, just as a sanity check; verify that new batchs only write what they suppose to write250// and nothing else.
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.