Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
wallet_bdb_parser.cpp
Go to the documentation of this file.
1// Copyright (c) 2023-present The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <config/bitcoin-config.h> // IWYU pragma: keep
7#include <test/fuzz/fuzz.h>
8#include <test/fuzz/util.h>
10#include <util/fs.h>
11#include <util/time.h>
12#include <util/translation.h>
13#include <wallet/bdb.h>
14#include <wallet/db.h>
15#include <wallet/dump.h>
16#include <wallet/migrate.h>
17
18#include <fstream>
19#include <iostream>
20
23
24namespace {
25TestingSetup* g_setup;
26} // namespace
27
29{
30 static auto testing_setup = MakeNoLogFileContext<TestingSetup>();
31 g_setup = testing_setup.get();
32}
33
35{
36 const auto wallet_path = g_setup->m_args.GetDataDirNet() / "fuzzed_wallet.dat";
37
38 {
39 AutoFile outfile{fsbridge::fopen(wallet_path, "wb")};
40 outfile << Span{buffer};
41 }
42
43 const DatabaseOptions options{};
44 DatabaseStatus status;
45 bilingual_str error;
46
47 fs::path bdb_ro_dumpfile{g_setup->m_args.GetDataDirNet() / "fuzzed_dumpfile_bdb_ro.dump"};
48 if (fs::exists(bdb_ro_dumpfile)) { // Writing into an existing dump file will throw an exception
49 remove(bdb_ro_dumpfile);
50 }
51 g_setup->m_args.ForceSetArg("-dumpfile", fs::PathToString(bdb_ro_dumpfile));
52
53#ifdef USE_BDB
54 bool bdb_ro_err = false;
55 bool bdb_ro_strict_err = false;
56#endif
57 auto db{MakeBerkeleyRODatabase(wallet_path, options, status, error)};
58 if (db) {
59 assert(DumpWallet(g_setup->m_args, *db, error));
60 } else {
61#ifdef USE_BDB
62 bdb_ro_err = true;
63#endif
64 if (error.original.starts_with("AutoFile::ignore: end of file") ||
65 error.original.starts_with("AutoFile::read: end of file") ||
66 error.original.starts_with("AutoFile::seek: ") ||
67 error.original == "Not a BDB file" ||
68 error.original == "Unexpected page type, should be 9 (BTree Metadata)" ||
69 error.original == "Unexpected database flags, should only be 0x20 (subdatabases)" ||
70 error.original == "Unexpected outer database root page type" ||
71 error.original == "Unexpected number of entries in outer database root page" ||
72 error.original == "Subdatabase page number has unexpected length" ||
73 error.original == "Unknown record type in records page" ||
74 error.original == "Unknown record type in internal page" ||
75 error.original == "Unexpected page size" ||
76 error.original == "Unexpected page type" ||
77 error.original == "Page number mismatch" ||
78 error.original == "Bad btree level" ||
79 error.original == "Bad page size" ||
80 error.original == "Meta page number mismatch" ||
81 error.original == "Data record position not in page" ||
82 error.original == "Internal record position not in page" ||
83 error.original == "LSNs are not reset, this database is not completely flushed. Please reopen then close the database with a version that has BDB support" ||
84 error.original == "Records page has odd number of records" ||
85 error.original == "Bad overflow record page type") {
86 // Do nothing
87 } else if (error.original == "Subdatabase last page is greater than database last page" ||
88 error.original == "Page number is greater than database last page" ||
89 error.original == "Last page number could not fit in file" ||
90 error.original == "Subdatabase has an unexpected name" ||
91 error.original == "Unsupported BDB data file version number" ||
92 error.original == "BDB builtin encryption is not supported") {
93#ifdef USE_BDB
94 bdb_ro_strict_err = true;
95#endif
96 } else {
97 throw std::runtime_error(error.original);
98 }
99 }
100
101#ifdef USE_BDB
102 // Try opening with BDB
103 fs::path bdb_dumpfile{g_setup->m_args.GetDataDirNet() / "fuzzed_dumpfile_bdb.dump"};
104 if (fs::exists(bdb_dumpfile)) { // Writing into an existing dump file will throw an exception
105 remove(bdb_dumpfile);
106 }
107 g_setup->m_args.ForceSetArg("-dumpfile", fs::PathToString(bdb_dumpfile));
108
109 try {
110 auto db{MakeBerkeleyDatabase(wallet_path, options, status, error)};
111 if (bdb_ro_err && !db) {
112 return;
113 }
114 assert(db);
115 if (bdb_ro_strict_err) {
116 // BerkeleyRO will be stricter than BDB. Ignore when those specific errors are hit.
117 return;
118 }
119 assert(!bdb_ro_err);
120 assert(DumpWallet(g_setup->m_args, *db, error));
121 } catch (const std::runtime_error& e) {
122 if (bdb_ro_err) return;
123 throw e;
124 }
125
126 // Make sure the dumpfiles match
127 if (fs::exists(bdb_ro_dumpfile) && fs::exists(bdb_dumpfile)) {
128 std::ifstream bdb_ro_dump(bdb_ro_dumpfile, std::ios_base::binary | std::ios_base::in);
129 std::ifstream bdb_dump(bdb_dumpfile, std::ios_base::binary | std::ios_base::in);
130 assert(std::equal(
131 std::istreambuf_iterator<char>(bdb_ro_dump.rdbuf()),
132 std::istreambuf_iterator<char>(),
133 std::istreambuf_iterator<char>(bdb_dump.rdbuf())));
134 }
135#endif
136}
Non-refcounted RAII wrapper for FILE*.
Definition streams.h:389
A Span is an object that can refer to a contiguous sequence of objects.
Definition span.h:98
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition fs.h:33
#define FUZZ_TARGET(...)
Definition fuzz.h:35
static bool exists(const path &p)
Definition fs.h:89
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition fs.h:151
FILE * fopen(const fs::path &p, const char *mode)
Definition fs.cpp:26
DatabaseStatus
Definition db.h:204
std::unique_ptr< T > MakeNoLogFileContext(const ChainType chain_type=ChainType::REGTEST, TestOpts opts={})
Make a test setup that has disk access to the debug.log file disabled.
Testing setup that configures a complete environment.
Bilingual messages:
Definition translation.h:18
std::string original
Definition translation.h:19
assert(!tx.IsCoinBase())
void initialize_wallet_bdb_parser()