Skip to content

Commit d02df7d

Browse files
committed
Merge bitcoin#26715: Introduce MockableDatabase for wallet unit tests
33e2b82 wallet, bench: Remove unused database options from WalletBenchLoading (Andrew Chow) 80ace04 tests: Modify records directly in wallet ckey loading test (Andrew Chow) b3bb17d tests: Update DuplicateMockDatabase for MockableDatabase (Andrew Chow) f0eecf5 scripted-diff: Replace CreateMockWalletDB with CreateMockableWalletDB (Andrew Chow) 075962b wallet, tests: Include wallet/test/util.h (Andrew Chow) 14aa4cb wallet: Move DummyDatabase to salvage (Andrew Chow) f67a385 wallet, tests: Replace usage of dummy db with mockable db (Andrew Chow) 33c6245 Introduce MockableDatabase for wallet unit tests (Andrew Chow) Pull request description: For the wallet's unit tests, we currently use either `DummyDatabase` or memory-only versions of either BDB or SQLite. The tests that use `DummyDatabase` just need a `WalletDatabase` so that the `CWallet` can be constructed, while the tests using the memory-only databases just need a backing data store. There is also a `FailDatabase` that is similar to `DummyDatabase` except it fails be default or can have a configured return value. Having all of these different database types can make it difficult to write tests, particularly tests that work when either BDB or SQLite is disabled. This PR unifies all of these different unit test database classes into a single `MockableDatabase`. Like `DummyDatabase`, most functions do nothing and just return true. Like `FailDatabase`, the return value of some functions can be configured on the fly to test various failure cases. Like the memory-only databases, records can actually be written to the `MockableDatabase` and be retrieved later, but all of this is still held in memory. Using `MockableDatabase` completely removes the need for having BDB or SQLite backed wallets in the unit tests for the tests that are not actually testing specific database behaviors. Because `MockableDatabase`s can be created by each unit test, we can also control what records are stored in the database. Records can be added and removed externally from the typical database modification functions. This will give us greater ability to test failure conditions, particularly those involving corrupted records. Possible alternative to bitcoin#26644 ACKs for top commit: furszy: ACK 33e2b82 TheCharlatan: ACK 33e2b82 Tree-SHA512: c2b09eff9728d063d2d4aea28a0f0e64e40b76483e75dc53f08667df23bd25834d52656cd4eafb02e552db0b9e619cfdb1b1c65b26b5436ee2c971d804768bcc
2 parents b2c85bd + 33e2b82 commit d02df7d

21 files changed

+345
-263
lines changed

build_msvc/libtest_util/libtest_util.vcxproj.in

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<ConfigurationType>StaticLibrary</ConfigurationType>
99
</PropertyGroup>
1010
<ItemGroup>
11+
<ClCompile Include="..\..\src\wallet\test\util.cpp" />
1112
@SOURCE_FILES@
1213
</ItemGroup>
1314
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<ItemGroup>
1111
<ClCompile Include="..\..\src\init\bitcoin-qt.cpp" />
1212
<ClCompile Include="..\..\src\test\util\setup_common.cpp" />
13+
<ClCompile Include="..\..\src\wallet\test\util.cpp" />
1314
<ClCompile Include="..\..\src\qt\test\addressbooktests.cpp" />
1415
<ClCompile Include="..\..\src\qt\test\apptests.cpp" />
1516
<ClCompile Include="..\..\src\qt\test\optiontests.cpp" />

src/bench/coin_selection.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <wallet/coinselection.h>
1010
#include <wallet/spend.h>
1111
#include <wallet/wallet.h>
12+
#include <wallet/test/util.h>
1213

1314
#include <set>
1415

@@ -20,7 +21,7 @@ using wallet::CWallet;
2021
using wallet::CWalletTx;
2122
using wallet::CoinEligibilityFilter;
2223
using wallet::CoinSelectionParams;
23-
using wallet::CreateDummyWalletDatabase;
24+
using wallet::CreateMockableWalletDatabase;
2425
using wallet::OutputGroup;
2526
using wallet::SelectCoinsBnB;
2627
using wallet::TxStateInactive;
@@ -46,7 +47,7 @@ static void CoinSelection(benchmark::Bench& bench)
4647
{
4748
NodeContext node;
4849
auto chain = interfaces::MakeChain(node);
49-
CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
50+
CWallet wallet(chain.get(), "", CreateMockableWalletDatabase());
5051
std::vector<std::unique_ptr<CWalletTx>> wtxs;
5152
LOCK(wallet.cs_wallet);
5253

src/bench/wallet_balance.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include <optional>
1616

1717
using wallet::CWallet;
18-
using wallet::CreateMockWalletDatabase;
18+
using wallet::CreateMockableWalletDatabase;
1919
using wallet::DBErrors;
2020
using wallet::GetBalance;
2121
using wallet::WALLET_FLAG_DESCRIPTORS;
@@ -28,7 +28,7 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
2828

2929
const auto& ADDRESS_WATCHONLY = ADDRESS_BCRT1_UNSPENDABLE;
3030

31-
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()};
31+
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()};
3232
{
3333
LOCK(wallet.cs_wallet);
3434
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);

src/bench/wallet_create_tx.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include <wallet/wallet.h>
1616

1717
using wallet::CWallet;
18-
using wallet::CreateMockWalletDatabase;
18+
using wallet::CreateMockableWalletDatabase;
1919
using wallet::DBErrors;
2020
using wallet::WALLET_FLAG_DESCRIPTORS;
2121

@@ -83,7 +83,7 @@ static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type
8383
{
8484
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
8585

86-
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()};
86+
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()};
8787
{
8888
LOCK(wallet.cs_wallet);
8989
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@@ -136,7 +136,7 @@ static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type
136136
static void AvailableCoins(benchmark::Bench& bench, const std::vector<OutputType>& output_type)
137137
{
138138
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
139-
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()};
139+
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()};
140140
{
141141
LOCK(wallet.cs_wallet);
142142
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);

src/bench/wallet_loading.cpp

+11-16
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,17 @@
1717
#include <optional>
1818

1919
using wallet::CWallet;
20-
using wallet::DatabaseFormat;
21-
using wallet::DatabaseOptions;
20+
using wallet::CreateMockableWalletDatabase;
2221
using wallet::TxStateInactive;
2322
using wallet::WALLET_FLAG_DESCRIPTORS;
2423
using wallet::WalletContext;
2524
using wallet::WalletDatabase;
2625

27-
static std::shared_ptr<CWallet> BenchLoadWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context, DatabaseOptions& options)
26+
static std::shared_ptr<CWallet> BenchLoadWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context, uint64_t create_flags)
2827
{
2928
bilingual_str error;
3029
std::vector<bilingual_str> warnings;
31-
auto wallet = CWallet::Create(context, "", std::move(database), options.create_flags, error, warnings);
30+
auto wallet = CWallet::Create(context, "", std::move(database), create_flags, error, warnings);
3231
NotifyWalletLoaded(context, wallet);
3332
if (context.chain) {
3433
wallet->postInitProcess();
@@ -55,39 +54,35 @@ static void AddTx(CWallet& wallet)
5554
static void WalletLoading(benchmark::Bench& bench, bool legacy_wallet)
5655
{
5756
const auto test_setup = MakeNoLogFileContext<TestingSetup>();
58-
test_setup->m_args.ForceSetArg("-unsafesqlitesync", "1");
5957

6058
WalletContext context;
6159
context.args = &test_setup->m_args;
6260
context.chain = test_setup->m_node.chain.get();
6361

6462
// Setup the wallet
6563
// Loading the wallet will also create it
66-
DatabaseOptions options;
67-
if (legacy_wallet) {
68-
options.require_format = DatabaseFormat::BERKELEY;
69-
} else {
70-
options.create_flags = WALLET_FLAG_DESCRIPTORS;
71-
options.require_format = DatabaseFormat::SQLITE;
64+
uint64_t create_flags = 0;
65+
if (!legacy_wallet) {
66+
create_flags = WALLET_FLAG_DESCRIPTORS;
7267
}
73-
auto database = CreateMockWalletDatabase(options);
74-
auto wallet = BenchLoadWallet(std::move(database), context, options);
68+
auto database = CreateMockableWalletDatabase();
69+
auto wallet = BenchLoadWallet(std::move(database), context, create_flags);
7570

7671
// Generate a bunch of transactions and addresses to put into the wallet
7772
for (int i = 0; i < 1000; ++i) {
7873
AddTx(*wallet);
7974
}
8075

81-
database = DuplicateMockDatabase(wallet->GetDatabase(), options);
76+
database = DuplicateMockDatabase(wallet->GetDatabase());
8277

8378
// reload the wallet for the actual benchmark
8479
BenchUnloadWallet(std::move(wallet));
8580

8681
bench.epochs(5).run([&] {
87-
wallet = BenchLoadWallet(std::move(database), context, options);
82+
wallet = BenchLoadWallet(std::move(database), context, create_flags);
8883

8984
// Cleanup
90-
database = DuplicateMockDatabase(wallet->GetDatabase(), options);
85+
database = DuplicateMockDatabase(wallet->GetDatabase());
9186
BenchUnloadWallet(std::move(wallet));
9287
});
9388
}

src/qt/test/addressbooktests.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <key.h>
2020
#include <key_io.h>
2121
#include <wallet/wallet.h>
22+
#include <wallet/test/util.h>
2223
#include <walletinitinterface.h>
2324

2425
#include <chrono>
@@ -31,7 +32,7 @@
3132

3233
using wallet::AddWallet;
3334
using wallet::CWallet;
34-
using wallet::CreateMockWalletDatabase;
35+
using wallet::CreateMockableWalletDatabase;
3536
using wallet::RemoveWallet;
3637
using wallet::WALLET_FLAG_DESCRIPTORS;
3738
using wallet::WalletContext;
@@ -75,7 +76,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
7576
auto wallet_loader = interfaces::MakeWalletLoader(*test.m_node.chain, *Assert(test.m_node.args));
7677
test.m_node.wallet_loader = wallet_loader.get();
7778
node.setContext(&test.m_node);
78-
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
79+
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockableWalletDatabase());
7980
wallet->LoadWallet();
8081
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
8182
{

src/qt/test/wallettests.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <qt/walletmodel.h>
2727
#include <test/util/setup_common.h>
2828
#include <validation.h>
29+
#include <wallet/test/util.h>
2930
#include <wallet/wallet.h>
3031

3132
#include <chrono>
@@ -46,7 +47,7 @@
4647

4748
using wallet::AddWallet;
4849
using wallet::CWallet;
49-
using wallet::CreateMockWalletDatabase;
50+
using wallet::CreateMockableWalletDatabase;
5051
using wallet::RemoveWallet;
5152
using wallet::WALLET_FLAG_DESCRIPTORS;
5253
using wallet::WALLET_FLAG_DISABLE_PRIVATE_KEYS;
@@ -189,7 +190,7 @@ void SyncUpWallet(const std::shared_ptr<CWallet>& wallet, interfaces::Node& node
189190

190191
std::shared_ptr<CWallet> SetupLegacyWatchOnlyWallet(interfaces::Node& node, TestChain100Setup& test)
191192
{
192-
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
193+
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockableWalletDatabase());
193194
wallet->LoadWallet();
194195
{
195196
LOCK(wallet->cs_wallet);
@@ -207,7 +208,7 @@ std::shared_ptr<CWallet> SetupLegacyWatchOnlyWallet(interfaces::Node& node, Test
207208

208209
std::shared_ptr<CWallet> SetupDescriptorsWallet(interfaces::Node& node, TestChain100Setup& test)
209210
{
210-
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
211+
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockableWalletDatabase());
211212
wallet->LoadWallet();
212213
LOCK(wallet->cs_wallet);
213214
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);

src/wallet/db.h

-45
Original file line numberDiff line numberDiff line change
@@ -174,51 +174,6 @@ class WalletDatabase
174174
virtual std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) = 0;
175175
};
176176

177-
class DummyCursor : public DatabaseCursor
178-
{
179-
Status Next(DataStream& key, DataStream& value) override { return Status::FAIL; }
180-
};
181-
182-
/** RAII class that provides access to a DummyDatabase. Never fails. */
183-
class DummyBatch : public DatabaseBatch
184-
{
185-
private:
186-
bool ReadKey(DataStream&& key, DataStream& value) override { return true; }
187-
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override { return true; }
188-
bool EraseKey(DataStream&& key) override { return true; }
189-
bool HasKey(DataStream&& key) override { return true; }
190-
bool ErasePrefix(Span<const std::byte> prefix) override { return true; }
191-
192-
public:
193-
void Flush() override {}
194-
void Close() override {}
195-
196-
std::unique_ptr<DatabaseCursor> GetNewCursor() override { return std::make_unique<DummyCursor>(); }
197-
bool TxnBegin() override { return true; }
198-
bool TxnCommit() override { return true; }
199-
bool TxnAbort() override { return true; }
200-
};
201-
202-
/** A dummy WalletDatabase that does nothing and never fails. Only used by unit tests.
203-
**/
204-
class DummyDatabase : public WalletDatabase
205-
{
206-
public:
207-
void Open() override {};
208-
void AddRef() override {}
209-
void RemoveRef() override {}
210-
bool Rewrite(const char* pszSkip=nullptr) override { return true; }
211-
bool Backup(const std::string& strDest) const override { return true; }
212-
void Close() override {}
213-
void Flush() override {}
214-
bool PeriodicFlush() override { return true; }
215-
void IncrementUpdateCounter() override { ++nUpdateCounter; }
216-
void ReloadDbEnv() override {}
217-
std::string Filename() override { return "dummy"; }
218-
std::string Format() override { return "dummy"; }
219-
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<DummyBatch>(); }
220-
};
221-
222177
enum class DatabaseFormat {
223178
BERKELEY,
224179
SQLITE,

src/wallet/salvage.cpp

+46-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,51 @@ static bool KeyFilter(const std::string& type)
2323
return WalletBatch::IsKeyType(type) || type == DBKeys::HDCHAIN;
2424
}
2525

26+
class DummyCursor : public DatabaseCursor
27+
{
28+
Status Next(DataStream& key, DataStream& value) override { return Status::FAIL; }
29+
};
30+
31+
/** RAII class that provides access to a DummyDatabase. Never fails. */
32+
class DummyBatch : public DatabaseBatch
33+
{
34+
private:
35+
bool ReadKey(DataStream&& key, DataStream& value) override { return true; }
36+
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite=true) override { return true; }
37+
bool EraseKey(DataStream&& key) override { return true; }
38+
bool HasKey(DataStream&& key) override { return true; }
39+
bool ErasePrefix(Span<const std::byte> prefix) override { return true; }
40+
41+
public:
42+
void Flush() override {}
43+
void Close() override {}
44+
45+
std::unique_ptr<DatabaseCursor> GetNewCursor() override { return std::make_unique<DummyCursor>(); }
46+
bool TxnBegin() override { return true; }
47+
bool TxnCommit() override { return true; }
48+
bool TxnAbort() override { return true; }
49+
};
50+
51+
/** A dummy WalletDatabase that does nothing and never fails. Only used by salvage.
52+
**/
53+
class DummyDatabase : public WalletDatabase
54+
{
55+
public:
56+
void Open() override {};
57+
void AddRef() override {}
58+
void RemoveRef() override {}
59+
bool Rewrite(const char* pszSkip=nullptr) override { return true; }
60+
bool Backup(const std::string& strDest) const override { return true; }
61+
void Close() override {}
62+
void Flush() override {}
63+
bool PeriodicFlush() override { return true; }
64+
void IncrementUpdateCounter() override { ++nUpdateCounter; }
65+
void ReloadDbEnv() override {}
66+
std::string Filename() override { return "dummy"; }
67+
std::string Format() override { return "dummy"; }
68+
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<DummyBatch>(); }
69+
};
70+
2671
bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
2772
{
2873
DatabaseOptions options;
@@ -135,7 +180,7 @@ bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bil
135180
}
136181

137182
DbTxn* ptxn = env->TxnBegin();
138-
CWallet dummyWallet(nullptr, "", CreateDummyWalletDatabase());
183+
CWallet dummyWallet(nullptr, "", std::make_unique<DummyDatabase>());
139184
for (KeyValPair& row : salvagedData)
140185
{
141186
/* Filter for only private key type KV pairs to be added to the salvaged wallet */

src/wallet/test/coinselector_tests.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <wallet/coincontrol.h>
1313
#include <wallet/coinselection.h>
1414
#include <wallet/spend.h>
15+
#include <wallet/test/util.h>
1516
#include <wallet/test/wallet_test_fixture.h>
1617
#include <wallet/wallet.h>
1718

@@ -176,7 +177,7 @@ inline std::vector<OutputGroup>& KnapsackGroupOutputs(const CoinsResult& availab
176177

177178
static std::unique_ptr<CWallet> NewWallet(const node::NodeContext& m_node, const std::string& wallet_name = "")
178179
{
179-
std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), wallet_name, CreateMockWalletDatabase());
180+
std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), wallet_name, CreateMockableWalletDatabase());
180181
BOOST_CHECK(wallet->LoadWallet() == DBErrors::LOAD_OK);
181182
LOCK(wallet->cs_wallet);
182183
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);

src/wallet/test/group_outputs_tests.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <wallet/coinselection.h>
88
#include <wallet/spend.h>
9+
#include <wallet/test/util.h>
910
#include <wallet/wallet.h>
1011

1112
#include <boost/test/unit_test.hpp>
@@ -17,7 +18,7 @@ static int nextLockTime = 0;
1718

1819
static std::shared_ptr<CWallet> NewWallet(const node::NodeContext& m_node)
1920
{
20-
std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
21+
std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
2122
wallet->LoadWallet();
2223
LOCK(wallet->cs_wallet);
2324
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);

0 commit comments

Comments
 (0)