Skip to content

Commit 5acdc2b

Browse files
committed
Merge bitcoin/bitcoin#26606: wallet: Implement independent BDB parser
d51fbab wallet, test: Be able to always swap BDB endianness (Ava Chow) 0b75315 test: Test bdb_ro dump of wallet without reset LSNs (Ava Chow) c1984f1 test: Test dumping dbs with overflow pages (Ava Chow) fd7b16e test: Test dumps of other endian BDB files (Ava Chow) 6ace3e9 bdb: Be able to make byteswapped databases (Ava Chow) d987890 Error if LSNs are not reset (Ava Chow) 4d7a3ae Berkeley RO Database fuzz test (TheCharlatan) 3568dce tests: Add BerkeleyRO to db prefix tests (Ava Chow) 70cfbfd wallettool: Optionally use BERKELEY_RO as format when dumping BDB wallets (Ava Chow) dd57713 Add MakeBerkeleyRODatabase (Ava Chow) 6e50bee Implement handling of other endianness in BerkeleyRODatabase (Ava Chow) cdd61c9 wallet: implement independent BDB deserializer in BerkeleyRODatabase (Ava Chow) ecba230 wallet: implement BerkeleyRODatabase::Backup (Ava Chow) 0c8e728 wallet: implement BerkeleyROBatch (Ava Chow) 756ff9b wallet: add dummy BerkeleyRODatabase and BerkeleyROBatch classes (Ava Chow) ca18aea Add AutoFile::seek and tell (Ava Chow) Pull request description: Split from #26596 This PR adds `BerkeleyRODatabase` which is an independent implementation of a BDB file parser. It provides read only access to a BDB file, and can therefore be used as a read only database backend for wallets. This will be used for dumping legacy wallet records and migrating legacy wallets without the need for BDB itself. Wallettool's `dump` command is changed to use `BerkeleyRODatabase` instead of `BerkeleyDatabase` (and `CWallet` itself) to demonstrate that this parser works and to test it against the existing wallettool functional tests. ACKs for top commit: josibake: reACK bitcoin/bitcoin@d51fbab TheCharlatan: Re-ACK d51fbab furszy: reACK d51fbab laanwj: re-ACK d51fbab theStack: ACK d51fbab Tree-SHA512: 1e7b97edf223b2974eed2e9eac1179fc82bb6359e0a66b7d2a0c8b9fa515eae9ea036f1edf7c76cdab2e75ad994962b134b41056ccfbc33b8d54f0859e86657b
2 parents ecd2365 + d51fbab commit 5acdc2b

23 files changed

+1270
-19
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ BITCOIN_CORE_H = \
348348
wallet/feebumper.h \
349349
wallet/fees.h \
350350
wallet/load.h \
351+
wallet/migrate.h \
351352
wallet/receive.h \
352353
wallet/rpc/util.h \
353354
wallet/rpc/wallet.h \
@@ -508,6 +509,7 @@ libbitcoin_wallet_a_SOURCES = \
508509
wallet/fees.cpp \
509510
wallet/interfaces.cpp \
510511
wallet/load.cpp \
512+
wallet/migrate.cpp \
511513
wallet/receive.cpp \
512514
wallet/rpc/addresses.cpp \
513515
wallet/rpc/backup.cpp \

src/Makefile.test.include

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ FUZZ_WALLET_SRC = \
204204
wallet/test/fuzz/coincontrol.cpp \
205205
wallet/test/fuzz/coinselection.cpp \
206206
wallet/test/fuzz/fees.cpp \
207-
wallet/test/fuzz/parse_iso8601.cpp
207+
wallet/test/fuzz/parse_iso8601.cpp \
208+
wallet/test/fuzz/wallet_bdb_parser.cpp
208209

209210
if USE_SQLITE
210211
FUZZ_WALLET_SRC += \

src/bitcoin-wallet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ static void SetupWalletToolArgs(ArgsManager& argsman)
4040
argsman.AddArg("-legacy", "Create legacy wallet. Only for 'create'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
4141
argsman.AddArg("-format=<format>", "The format of the wallet file to create. Either \"bdb\" or \"sqlite\". Only used with 'createfromdump'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
4242
argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
43+
argsman.AddArg("-withinternalbdb", "Use the internal Berkeley DB parser when dumping a Berkeley DB wallet file (default: false)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
4344

4445
argsman.AddCommand("info", "Get wallet info");
4546
argsman.AddCommand("create", "Create new wallet file");

src/dummywallet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
5353
"-walletrejectlongchains",
5454
"-walletcrosschain",
5555
"-unsafesqlitesync",
56+
"-swapbdbendian",
5657
});
5758
}
5859

src/streams.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,28 @@ std::size_t AutoFile::detail_fread(Span<std::byte> dst)
2121
}
2222
}
2323

24+
void AutoFile::seek(int64_t offset, int origin)
25+
{
26+
if (IsNull()) {
27+
throw std::ios_base::failure("AutoFile::seek: file handle is nullptr");
28+
}
29+
if (std::fseek(m_file, offset, origin) != 0) {
30+
throw std::ios_base::failure(feof() ? "AutoFile::seek: end of file" : "AutoFile::seek: fseek failed");
31+
}
32+
}
33+
34+
int64_t AutoFile::tell()
35+
{
36+
if (IsNull()) {
37+
throw std::ios_base::failure("AutoFile::tell: file handle is nullptr");
38+
}
39+
int64_t r{std::ftell(m_file)};
40+
if (r < 0) {
41+
throw std::ios_base::failure("AutoFile::tell: ftell failed");
42+
}
43+
return r;
44+
}
45+
2446
void AutoFile::read(Span<std::byte> dst)
2547
{
2648
if (detail_fread(dst) != dst.size()) {

src/streams.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,9 @@ class AutoFile
435435
/** Implementation detail, only used internally. */
436436
std::size_t detail_fread(Span<std::byte> dst);
437437

438+
void seek(int64_t offset, int origin);
439+
int64_t tell();
440+
438441
//
439442
// Stream subset
440443
//

src/wallet/bdb.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ RecursiveMutex cs_db;
6565
std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to db environment.
6666
} // namespace
6767

68+
static constexpr auto REVERSE_BYTE_ORDER{std::endian::native == std::endian::little ? 4321 : 1234};
69+
6870
bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
6971
{
7072
return memcmp(value, &rhs.value, sizeof(value)) == 0;
@@ -300,7 +302,11 @@ static Span<const std::byte> SpanFromDbt(const SafeDbt& dbt)
300302
}
301303

302304
BerkeleyDatabase::BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options) :
303-
WalletDatabase(), env(std::move(env)), m_filename(std::move(filename)), m_max_log_mb(options.max_log_mb)
305+
WalletDatabase(),
306+
env(std::move(env)),
307+
m_byteswap(options.require_format == DatabaseFormat::BERKELEY_SWAP),
308+
m_filename(std::move(filename)),
309+
m_max_log_mb(options.max_log_mb)
304310
{
305311
auto inserted = this->env->m_databases.emplace(m_filename, std::ref(*this));
306312
assert(inserted.second);
@@ -389,6 +395,10 @@ void BerkeleyDatabase::Open()
389395
}
390396
}
391397

398+
if (m_byteswap) {
399+
pdb_temp->set_lorder(REVERSE_BYTE_ORDER);
400+
}
401+
392402
ret = pdb_temp->open(nullptr, // Txn pointer
393403
fMockDb ? nullptr : strFile.c_str(), // Filename
394404
fMockDb ? strFile.c_str() : "main", // Logical db name
@@ -521,6 +531,10 @@ bool BerkeleyDatabase::Rewrite(const char* pszSkip)
521531
BerkeleyBatch db(*this, true);
522532
std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
523533

534+
if (m_byteswap) {
535+
pdbCopy->set_lorder(REVERSE_BYTE_ORDER);
536+
}
537+
524538
int ret = pdbCopy->open(nullptr, // Txn pointer
525539
strFileRes.c_str(), // Filename
526540
"main", // Logical db name

src/wallet/bdb.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ class BerkeleyDatabase : public WalletDatabase
147147
/** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
148148
std::unique_ptr<Db> m_db;
149149

150+
// Whether to byteswap
151+
bool m_byteswap;
152+
150153
fs::path m_filename;
151154
int64_t m_max_log_mb;
152155

src/wallet/db.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
#include <vector>
1717

1818
namespace wallet {
19+
bool operator<(BytePrefix a, Span<const std::byte> b) { return a.prefix < b.subspan(0, std::min(a.prefix.size(), b.size())); }
20+
bool operator<(Span<const std::byte> a, BytePrefix b) { return a.subspan(0, std::min(a.size(), b.prefix.size())) < b.prefix; }
21+
1922
std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
2023
{
2124
std::vector<fs::path> paths;

src/wallet/db.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ class ArgsManager;
2020
struct bilingual_str;
2121

2222
namespace wallet {
23+
// BytePrefix compares equality with other byte spans that begin with the same prefix.
24+
struct BytePrefix {
25+
Span<const std::byte> prefix;
26+
};
27+
bool operator<(BytePrefix a, Span<const std::byte> b);
28+
bool operator<(Span<const std::byte> a, BytePrefix b);
2329

2430
class DatabaseCursor
2531
{
@@ -177,6 +183,8 @@ class WalletDatabase
177183
enum class DatabaseFormat {
178184
BERKELEY,
179185
SQLITE,
186+
BERKELEY_RO,
187+
BERKELEY_SWAP,
180188
};
181189

182190
struct DatabaseOptions {

0 commit comments

Comments
 (0)