Merge bitcoin/bitcoin#26715: Introduce MockableDatabase for wallet unit tests

33e2b82a4f wallet, bench: Remove unused database options from WalletBenchLoading (Andrew Chow)
80ace042d8 tests: Modify records directly in wallet ckey loading test (Andrew Chow)
b3bb17d5d0 tests: Update DuplicateMockDatabase for MockableDatabase (Andrew Chow)
f0eecf5e40 scripted-diff: Replace CreateMockWalletDB with CreateMockableWalletDB (Andrew Chow)
075962bc25 wallet, tests: Include wallet/test/util.h (Andrew Chow)
14aa4cb1e4 wallet: Move DummyDatabase to salvage (Andrew Chow)
f67a385556 wallet, tests: Replace usage of dummy db with mockable db (Andrew Chow)
33c6245ac1 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 #26644

ACKs for top commit:
  furszy:
    ACK 33e2b82
  TheCharlatan:
    ACK 33e2b82a4f

Tree-SHA512: c2b09eff9728d063d2d4aea28a0f0e64e40b76483e75dc53f08667df23bd25834d52656cd4eafb02e552db0b9e619cfdb1b1c65b26b5436ee2c971d804768bcc
This commit is contained in:
fanquake 2023-05-15 11:18:44 +01:00
commit d02df7db6b
No known key found for this signature in database
GPG key ID: 2EEB9F5CC09526C1
21 changed files with 341 additions and 259 deletions

View file

@ -8,6 +8,7 @@
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\src\wallet\test\util.cpp" />
@SOURCE_FILES@ @SOURCE_FILES@
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

View file

@ -10,6 +10,7 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\src\init\bitcoin-qt.cpp" /> <ClCompile Include="..\..\src\init\bitcoin-qt.cpp" />
<ClCompile Include="..\..\src\test\util\setup_common.cpp" /> <ClCompile Include="..\..\src\test\util\setup_common.cpp" />
<ClCompile Include="..\..\src\wallet\test\util.cpp" />
<ClCompile Include="..\..\src\qt\test\addressbooktests.cpp" /> <ClCompile Include="..\..\src\qt\test\addressbooktests.cpp" />
<ClCompile Include="..\..\src\qt\test\apptests.cpp" /> <ClCompile Include="..\..\src\qt\test\apptests.cpp" />
<ClCompile Include="..\..\src\qt\test\optiontests.cpp" /> <ClCompile Include="..\..\src\qt\test\optiontests.cpp" />

View file

@ -9,6 +9,7 @@
#include <wallet/coinselection.h> #include <wallet/coinselection.h>
#include <wallet/spend.h> #include <wallet/spend.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <wallet/test/util.h>
#include <set> #include <set>
@ -20,7 +21,7 @@ using wallet::CWallet;
using wallet::CWalletTx; using wallet::CWalletTx;
using wallet::CoinEligibilityFilter; using wallet::CoinEligibilityFilter;
using wallet::CoinSelectionParams; using wallet::CoinSelectionParams;
using wallet::CreateDummyWalletDatabase; using wallet::CreateMockableWalletDatabase;
using wallet::OutputGroup; using wallet::OutputGroup;
using wallet::SelectCoinsBnB; using wallet::SelectCoinsBnB;
using wallet::TxStateInactive; using wallet::TxStateInactive;
@ -46,7 +47,7 @@ static void CoinSelection(benchmark::Bench& bench)
{ {
NodeContext node; NodeContext node;
auto chain = interfaces::MakeChain(node); auto chain = interfaces::MakeChain(node);
CWallet wallet(chain.get(), "", CreateDummyWalletDatabase()); CWallet wallet(chain.get(), "", CreateMockableWalletDatabase());
std::vector<std::unique_ptr<CWalletTx>> wtxs; std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);

View file

@ -15,7 +15,7 @@
#include <optional> #include <optional>
using wallet::CWallet; using wallet::CWallet;
using wallet::CreateMockWalletDatabase; using wallet::CreateMockableWalletDatabase;
using wallet::DBErrors; using wallet::DBErrors;
using wallet::GetBalance; using wallet::GetBalance;
using wallet::WALLET_FLAG_DESCRIPTORS; using wallet::WALLET_FLAG_DESCRIPTORS;
@ -28,7 +28,7 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
const auto& ADDRESS_WATCHONLY = ADDRESS_BCRT1_UNSPENDABLE; const auto& ADDRESS_WATCHONLY = ADDRESS_BCRT1_UNSPENDABLE;
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()}; CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()};
{ {
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS); wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);

View file

@ -15,7 +15,7 @@
#include <wallet/wallet.h> #include <wallet/wallet.h>
using wallet::CWallet; using wallet::CWallet;
using wallet::CreateMockWalletDatabase; using wallet::CreateMockableWalletDatabase;
using wallet::DBErrors; using wallet::DBErrors;
using wallet::WALLET_FLAG_DESCRIPTORS; using wallet::WALLET_FLAG_DESCRIPTORS;
@ -83,7 +83,7 @@ static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type
{ {
const auto test_setup = MakeNoLogFileContext<const TestingSetup>(); const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()}; CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()};
{ {
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS); wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@ -136,7 +136,7 @@ static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type
static void AvailableCoins(benchmark::Bench& bench, const std::vector<OutputType>& output_type) static void AvailableCoins(benchmark::Bench& bench, const std::vector<OutputType>& output_type)
{ {
const auto test_setup = MakeNoLogFileContext<const TestingSetup>(); const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()}; CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase()};
{ {
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS); wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);

View file

@ -17,18 +17,17 @@
#include <optional> #include <optional>
using wallet::CWallet; using wallet::CWallet;
using wallet::DatabaseFormat; using wallet::CreateMockableWalletDatabase;
using wallet::DatabaseOptions;
using wallet::TxStateInactive; using wallet::TxStateInactive;
using wallet::WALLET_FLAG_DESCRIPTORS; using wallet::WALLET_FLAG_DESCRIPTORS;
using wallet::WalletContext; using wallet::WalletContext;
using wallet::WalletDatabase; using wallet::WalletDatabase;
static std::shared_ptr<CWallet> BenchLoadWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context, DatabaseOptions& options) static std::shared_ptr<CWallet> BenchLoadWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context, uint64_t create_flags)
{ {
bilingual_str error; bilingual_str error;
std::vector<bilingual_str> warnings; std::vector<bilingual_str> warnings;
auto wallet = CWallet::Create(context, "", std::move(database), options.create_flags, error, warnings); auto wallet = CWallet::Create(context, "", std::move(database), create_flags, error, warnings);
NotifyWalletLoaded(context, wallet); NotifyWalletLoaded(context, wallet);
if (context.chain) { if (context.chain) {
wallet->postInitProcess(); wallet->postInitProcess();
@ -55,7 +54,6 @@ static void AddTx(CWallet& wallet)
static void WalletLoading(benchmark::Bench& bench, bool legacy_wallet) static void WalletLoading(benchmark::Bench& bench, bool legacy_wallet)
{ {
const auto test_setup = MakeNoLogFileContext<TestingSetup>(); const auto test_setup = MakeNoLogFileContext<TestingSetup>();
test_setup->m_args.ForceSetArg("-unsafesqlitesync", "1");
WalletContext context; WalletContext context;
context.args = &test_setup->m_args; context.args = &test_setup->m_args;
@ -63,31 +61,28 @@ static void WalletLoading(benchmark::Bench& bench, bool legacy_wallet)
// Setup the wallet // Setup the wallet
// Loading the wallet will also create it // Loading the wallet will also create it
DatabaseOptions options; uint64_t create_flags = 0;
if (legacy_wallet) { if (!legacy_wallet) {
options.require_format = DatabaseFormat::BERKELEY; create_flags = WALLET_FLAG_DESCRIPTORS;
} else {
options.create_flags = WALLET_FLAG_DESCRIPTORS;
options.require_format = DatabaseFormat::SQLITE;
} }
auto database = CreateMockWalletDatabase(options); auto database = CreateMockableWalletDatabase();
auto wallet = BenchLoadWallet(std::move(database), context, options); auto wallet = BenchLoadWallet(std::move(database), context, create_flags);
// Generate a bunch of transactions and addresses to put into the wallet // Generate a bunch of transactions and addresses to put into the wallet
for (int i = 0; i < 1000; ++i) { for (int i = 0; i < 1000; ++i) {
AddTx(*wallet); AddTx(*wallet);
} }
database = DuplicateMockDatabase(wallet->GetDatabase(), options); database = DuplicateMockDatabase(wallet->GetDatabase());
// reload the wallet for the actual benchmark // reload the wallet for the actual benchmark
BenchUnloadWallet(std::move(wallet)); BenchUnloadWallet(std::move(wallet));
bench.epochs(5).run([&] { bench.epochs(5).run([&] {
wallet = BenchLoadWallet(std::move(database), context, options); wallet = BenchLoadWallet(std::move(database), context, create_flags);
// Cleanup // Cleanup
database = DuplicateMockDatabase(wallet->GetDatabase(), options); database = DuplicateMockDatabase(wallet->GetDatabase());
BenchUnloadWallet(std::move(wallet)); BenchUnloadWallet(std::move(wallet));
}); });
} }

View file

@ -19,6 +19,7 @@
#include <key.h> #include <key.h>
#include <key_io.h> #include <key_io.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <wallet/test/util.h>
#include <walletinitinterface.h> #include <walletinitinterface.h>
#include <chrono> #include <chrono>
@ -31,7 +32,7 @@
using wallet::AddWallet; using wallet::AddWallet;
using wallet::CWallet; using wallet::CWallet;
using wallet::CreateMockWalletDatabase; using wallet::CreateMockableWalletDatabase;
using wallet::RemoveWallet; using wallet::RemoveWallet;
using wallet::WALLET_FLAG_DESCRIPTORS; using wallet::WALLET_FLAG_DESCRIPTORS;
using wallet::WalletContext; using wallet::WalletContext;
@ -75,7 +76,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
auto wallet_loader = interfaces::MakeWalletLoader(*test.m_node.chain, *Assert(test.m_node.args)); auto wallet_loader = interfaces::MakeWalletLoader(*test.m_node.chain, *Assert(test.m_node.args));
test.m_node.wallet_loader = wallet_loader.get(); test.m_node.wallet_loader = wallet_loader.get();
node.setContext(&test.m_node); node.setContext(&test.m_node);
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase()); const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockableWalletDatabase());
wallet->LoadWallet(); wallet->LoadWallet();
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS); wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
{ {

View file

@ -26,6 +26,7 @@
#include <qt/walletmodel.h> #include <qt/walletmodel.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <validation.h> #include <validation.h>
#include <wallet/test/util.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <chrono> #include <chrono>
@ -46,7 +47,7 @@
using wallet::AddWallet; using wallet::AddWallet;
using wallet::CWallet; using wallet::CWallet;
using wallet::CreateMockWalletDatabase; using wallet::CreateMockableWalletDatabase;
using wallet::RemoveWallet; using wallet::RemoveWallet;
using wallet::WALLET_FLAG_DESCRIPTORS; using wallet::WALLET_FLAG_DESCRIPTORS;
using wallet::WALLET_FLAG_DISABLE_PRIVATE_KEYS; using wallet::WALLET_FLAG_DISABLE_PRIVATE_KEYS;
@ -189,7 +190,7 @@ void SyncUpWallet(const std::shared_ptr<CWallet>& wallet, interfaces::Node& node
std::shared_ptr<CWallet> SetupLegacyWatchOnlyWallet(interfaces::Node& node, TestChain100Setup& test) std::shared_ptr<CWallet> SetupLegacyWatchOnlyWallet(interfaces::Node& node, TestChain100Setup& test)
{ {
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase()); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockableWalletDatabase());
wallet->LoadWallet(); wallet->LoadWallet();
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
@ -207,7 +208,7 @@ std::shared_ptr<CWallet> SetupLegacyWatchOnlyWallet(interfaces::Node& node, Test
std::shared_ptr<CWallet> SetupDescriptorsWallet(interfaces::Node& node, TestChain100Setup& test) std::shared_ptr<CWallet> SetupDescriptorsWallet(interfaces::Node& node, TestChain100Setup& test)
{ {
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase()); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockableWalletDatabase());
wallet->LoadWallet(); wallet->LoadWallet();
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS); wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);

View file

@ -174,51 +174,6 @@ public:
virtual std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) = 0; virtual std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) = 0;
}; };
class DummyCursor : public DatabaseCursor
{
Status Next(DataStream& key, DataStream& value) override { return Status::FAIL; }
};
/** RAII class that provides access to a DummyDatabase. Never fails. */
class DummyBatch : public DatabaseBatch
{
private:
bool ReadKey(DataStream&& key, DataStream& value) override { return true; }
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override { return true; }
bool EraseKey(DataStream&& key) override { return true; }
bool HasKey(DataStream&& key) override { return true; }
bool ErasePrefix(Span<const std::byte> prefix) override { return true; }
public:
void Flush() override {}
void Close() override {}
std::unique_ptr<DatabaseCursor> GetNewCursor() override { return std::make_unique<DummyCursor>(); }
bool TxnBegin() override { return true; }
bool TxnCommit() override { return true; }
bool TxnAbort() override { return true; }
};
/** A dummy WalletDatabase that does nothing and never fails. Only used by unit tests.
**/
class DummyDatabase : public WalletDatabase
{
public:
void Open() override {};
void AddRef() override {}
void RemoveRef() override {}
bool Rewrite(const char* pszSkip=nullptr) override { return true; }
bool Backup(const std::string& strDest) const override { return true; }
void Close() override {}
void Flush() override {}
bool PeriodicFlush() override { return true; }
void IncrementUpdateCounter() override { ++nUpdateCounter; }
void ReloadDbEnv() override {}
std::string Filename() override { return "dummy"; }
std::string Format() override { return "dummy"; }
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<DummyBatch>(); }
};
enum class DatabaseFormat { enum class DatabaseFormat {
BERKELEY, BERKELEY,
SQLITE, SQLITE,

View file

@ -23,6 +23,51 @@ static bool KeyFilter(const std::string& type)
return WalletBatch::IsKeyType(type) || type == DBKeys::HDCHAIN; return WalletBatch::IsKeyType(type) || type == DBKeys::HDCHAIN;
} }
class DummyCursor : public DatabaseCursor
{
Status Next(DataStream& key, DataStream& value) override { return Status::FAIL; }
};
/** RAII class that provides access to a DummyDatabase. Never fails. */
class DummyBatch : public DatabaseBatch
{
private:
bool ReadKey(DataStream&& key, DataStream& value) override { return true; }
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite=true) override { return true; }
bool EraseKey(DataStream&& key) override { return true; }
bool HasKey(DataStream&& key) override { return true; }
bool ErasePrefix(Span<const std::byte> prefix) override { return true; }
public:
void Flush() override {}
void Close() override {}
std::unique_ptr<DatabaseCursor> GetNewCursor() override { return std::make_unique<DummyCursor>(); }
bool TxnBegin() override { return true; }
bool TxnCommit() override { return true; }
bool TxnAbort() override { return true; }
};
/** A dummy WalletDatabase that does nothing and never fails. Only used by salvage.
**/
class DummyDatabase : public WalletDatabase
{
public:
void Open() override {};
void AddRef() override {}
void RemoveRef() override {}
bool Rewrite(const char* pszSkip=nullptr) override { return true; }
bool Backup(const std::string& strDest) const override { return true; }
void Close() override {}
void Flush() override {}
bool PeriodicFlush() override { return true; }
void IncrementUpdateCounter() override { ++nUpdateCounter; }
void ReloadDbEnv() override {}
std::string Filename() override { return "dummy"; }
std::string Format() override { return "dummy"; }
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<DummyBatch>(); }
};
bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings) bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
{ {
DatabaseOptions options; DatabaseOptions options;
@ -135,7 +180,7 @@ bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bil
} }
DbTxn* ptxn = env->TxnBegin(); DbTxn* ptxn = env->TxnBegin();
CWallet dummyWallet(nullptr, "", CreateDummyWalletDatabase()); CWallet dummyWallet(nullptr, "", std::make_unique<DummyDatabase>());
for (KeyValPair& row : salvagedData) for (KeyValPair& row : salvagedData)
{ {
/* Filter for only private key type KV pairs to be added to the salvaged wallet */ /* Filter for only private key type KV pairs to be added to the salvaged wallet */

View file

@ -12,6 +12,7 @@
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
#include <wallet/coinselection.h> #include <wallet/coinselection.h>
#include <wallet/spend.h> #include <wallet/spend.h>
#include <wallet/test/util.h>
#include <wallet/test/wallet_test_fixture.h> #include <wallet/test/wallet_test_fixture.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
@ -176,7 +177,7 @@ inline std::vector<OutputGroup>& KnapsackGroupOutputs(const CoinsResult& availab
static std::unique_ptr<CWallet> NewWallet(const node::NodeContext& m_node, const std::string& wallet_name = "") static std::unique_ptr<CWallet> NewWallet(const node::NodeContext& m_node, const std::string& wallet_name = "")
{ {
std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), wallet_name, CreateMockWalletDatabase()); std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), wallet_name, CreateMockableWalletDatabase());
BOOST_CHECK(wallet->LoadWallet() == DBErrors::LOAD_OK); BOOST_CHECK(wallet->LoadWallet() == DBErrors::LOAD_OK);
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS); wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);

View file

@ -6,6 +6,7 @@
#include <wallet/coinselection.h> #include <wallet/coinselection.h>
#include <wallet/spend.h> #include <wallet/spend.h>
#include <wallet/test/util.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
@ -17,7 +18,7 @@ static int nextLockTime = 0;
static std::shared_ptr<CWallet> NewWallet(const node::NodeContext& m_node) static std::shared_ptr<CWallet> NewWallet(const node::NodeContext& m_node)
{ {
std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase()); std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
wallet->LoadWallet(); wallet->LoadWallet();
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS); wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);

View file

@ -10,6 +10,7 @@
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <wallet/types.h> #include <wallet/types.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <wallet/test/util.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
@ -55,7 +56,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK compressed - Legacy // P2PK compressed - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(pubkeys[0]); scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
@ -74,7 +75,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK compressed - Descriptor // P2PK compressed - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "pk(" + EncodeSecret(keys[0]) + ")"; std::string desc_str = "pk(" + EncodeSecret(keys[0]) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, true); auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@ -86,7 +87,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK uncompressed - Legacy // P2PK uncompressed - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey); scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
@ -105,7 +106,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK uncompressed - Descriptor // P2PK uncompressed - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "pk(" + EncodeSecret(uncompressedKey) + ")"; std::string desc_str = "pk(" + EncodeSecret(uncompressedKey) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, true); auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@ -117,7 +118,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH compressed - Legacy // P2PKH compressed - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0])); scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0]));
@ -136,7 +137,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH compressed - Descriptor // P2PKH compressed - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "pkh(" + EncodeSecret(keys[0]) + ")"; std::string desc_str = "pkh(" + EncodeSecret(keys[0]) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, true); auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@ -148,7 +149,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH uncompressed - Legacy // P2PKH uncompressed - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey)); scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey));
@ -167,7 +168,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH uncompressed - Descriptor // P2PKH uncompressed - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "pkh(" + EncodeSecret(uncompressedKey) + ")"; std::string desc_str = "pkh(" + EncodeSecret(uncompressedKey) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, true); auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@ -179,7 +180,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH - Legacy // P2SH - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -206,7 +207,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH - Descriptor // P2SH - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "sh(pkh(" + EncodeSecret(keys[0]) + "))"; std::string desc_str = "sh(pkh(" + EncodeSecret(keys[0]) + "))";
auto spk_manager = CreateDescriptor(keystore, desc_str, true); auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@ -219,7 +220,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2SH (invalid) - Legacy // (P2PKH inside) P2SH inside P2SH (invalid) - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -238,7 +239,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2SH (invalid) - Descriptor // (P2PKH inside) P2SH inside P2SH (invalid) - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "sh(sh(" + EncodeSecret(keys[0]) + "))"; std::string desc_str = "sh(sh(" + EncodeSecret(keys[0]) + "))";
auto spk_manager = CreateDescriptor(keystore, desc_str, false); auto spk_manager = CreateDescriptor(keystore, desc_str, false);
@ -247,7 +248,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2WSH (invalid) - Legacy // (P2PKH inside) P2SH inside P2WSH (invalid) - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -266,7 +267,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2WSH (invalid) - Descriptor // (P2PKH inside) P2SH inside P2WSH (invalid) - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "wsh(sh(" + EncodeSecret(keys[0]) + "))"; std::string desc_str = "wsh(sh(" + EncodeSecret(keys[0]) + "))";
auto spk_manager = CreateDescriptor(keystore, desc_str, false); auto spk_manager = CreateDescriptor(keystore, desc_str, false);
@ -275,7 +276,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH inside P2WSH (invalid) - Legacy // P2WPKH inside P2WSH (invalid) - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -292,7 +293,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH inside P2WSH (invalid) - Descriptor // P2WPKH inside P2WSH (invalid) - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "wsh(wpkh(" + EncodeSecret(keys[0]) + "))"; std::string desc_str = "wsh(wpkh(" + EncodeSecret(keys[0]) + "))";
auto spk_manager = CreateDescriptor(keystore, desc_str, false); auto spk_manager = CreateDescriptor(keystore, desc_str, false);
@ -301,7 +302,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2WSH inside P2WSH (invalid) - Legacy // (P2PKH inside) P2WSH inside P2WSH (invalid) - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -320,7 +321,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2WSH inside P2WSH (invalid) - Descriptor // (P2PKH inside) P2WSH inside P2WSH (invalid) - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "wsh(wsh(" + EncodeSecret(keys[0]) + "))"; std::string desc_str = "wsh(wsh(" + EncodeSecret(keys[0]) + "))";
auto spk_manager = CreateDescriptor(keystore, desc_str, false); auto spk_manager = CreateDescriptor(keystore, desc_str, false);
@ -329,7 +330,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH compressed - Legacy // P2WPKH compressed - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@ -345,7 +346,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH compressed - Descriptor // P2WPKH compressed - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "wpkh(" + EncodeSecret(keys[0]) + ")"; std::string desc_str = "wpkh(" + EncodeSecret(keys[0]) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, true); auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@ -357,7 +358,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH uncompressed - Legacy // P2WPKH uncompressed - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@ -378,7 +379,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH uncompressed (invalid) - Descriptor // P2WPKH uncompressed (invalid) - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "wpkh(" + EncodeSecret(uncompressedKey) + ")"; std::string desc_str = "wpkh(" + EncodeSecret(uncompressedKey) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, false); auto spk_manager = CreateDescriptor(keystore, desc_str, false);
@ -387,7 +388,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// scriptPubKey multisig - Legacy // scriptPubKey multisig - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -422,7 +423,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// scriptPubKey multisig - Descriptor // scriptPubKey multisig - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "multi(2, " + EncodeSecret(uncompressedKey) + ", " + EncodeSecret(keys[1]) + ")"; std::string desc_str = "multi(2, " + EncodeSecret(uncompressedKey) + ", " + EncodeSecret(keys[1]) + ")";
auto spk_manager = CreateDescriptor(keystore, desc_str, true); auto spk_manager = CreateDescriptor(keystore, desc_str, true);
@ -434,7 +435,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH multisig - Legacy // P2SH multisig - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@ -457,7 +458,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH multisig - Descriptor // P2SH multisig - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "sh(multi(2, " + EncodeSecret(uncompressedKey) + ", " + EncodeSecret(keys[1]) + "))"; std::string desc_str = "sh(multi(2, " + EncodeSecret(uncompressedKey) + ", " + EncodeSecret(keys[1]) + "))";
@ -471,7 +472,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with compressed keys - Legacy // P2WSH multisig with compressed keys - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@ -500,7 +501,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with compressed keys - Descriptor // P2WSH multisig with compressed keys - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "wsh(multi(2, " + EncodeSecret(keys[0]) + ", " + EncodeSecret(keys[1]) + "))"; std::string desc_str = "wsh(multi(2, " + EncodeSecret(keys[0]) + ", " + EncodeSecret(keys[1]) + "))";
@ -514,7 +515,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with uncompressed key - Legacy // P2WSH multisig with uncompressed key - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@ -543,7 +544,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with uncompressed key (invalid) - Descriptor // P2WSH multisig with uncompressed key (invalid) - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "wsh(multi(2, " + EncodeSecret(uncompressedKey) + ", " + EncodeSecret(keys[1]) + "))"; std::string desc_str = "wsh(multi(2, " + EncodeSecret(uncompressedKey) + ", " + EncodeSecret(keys[1]) + "))";
@ -553,7 +554,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig wrapped in P2SH - Legacy // P2WSH multisig wrapped in P2SH - Legacy
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -583,7 +584,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig wrapped in P2SH - Descriptor // P2WSH multisig wrapped in P2SH - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "sh(wsh(multi(2, " + EncodeSecret(keys[0]) + ", " + EncodeSecret(keys[1]) + ")))"; std::string desc_str = "sh(wsh(multi(2, " + EncodeSecret(keys[0]) + ", " + EncodeSecret(keys[1]) + ")))";
@ -598,7 +599,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// Combo - Descriptor // Combo - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "combo(" + EncodeSecret(keys[0]) + ")"; std::string desc_str = "combo(" + EncodeSecret(keys[0]) + ")";
@ -642,7 +643,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// Taproot - Descriptor // Taproot - Descriptor
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
std::string desc_str = "tr(" + EncodeSecret(keys[0]) + ")"; std::string desc_str = "tr(" + EncodeSecret(keys[0]) + ")";
@ -660,7 +661,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// OP_RETURN // OP_RETURN
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@ -675,7 +676,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unspendable // witness unspendable
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@ -690,7 +691,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unknown // witness unknown
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@ -705,7 +706,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// Nonstandard // Nonstandard
{ {
CWallet keystore(chain.get(), "", CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));

View file

@ -7,6 +7,7 @@
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <wallet/scriptpubkeyman.h> #include <wallet/scriptpubkeyman.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <wallet/test/util.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
@ -18,7 +19,7 @@ BOOST_FIXTURE_TEST_SUITE(scriptpubkeyman_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(CanProvide) BOOST_AUTO_TEST_CASE(CanProvide)
{ {
// Set up wallet and keyman variables. // Set up wallet and keyman variables.
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase()); CWallet wallet(m_node.chain.get(), "", CreateMockableWalletDatabase());
LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan(); LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan();
// Make a 1 of 2 multisig script // Make a 1 of 2 multisig script

View file

@ -7,6 +7,7 @@
#include <chain.h> #include <chain.h>
#include <key.h> #include <key.h>
#include <key_io.h> #include <key_io.h>
#include <streams.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <wallet/walletdb.h> #include <wallet/walletdb.h>
@ -16,7 +17,7 @@
namespace wallet { namespace wallet {
std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key) std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key)
{ {
auto wallet = std::make_unique<CWallet>(&chain, "", CreateMockWalletDatabase()); auto wallet = std::make_unique<CWallet>(&chain, "", CreateMockableWalletDatabase());
{ {
LOCK2(wallet->cs_wallet, ::cs_main); LOCK2(wallet->cs_wallet, ::cs_main);
wallet->SetLastBlockProcessed(cchain.Height(), cchain.Tip()->GetBlockHash()); wallet->SetLastBlockProcessed(cchain.Height(), cchain.Tip()->GetBlockHash());
@ -44,28 +45,9 @@ std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cc
return wallet; return wallet;
} }
std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database, DatabaseOptions& options) std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database)
{ {
auto new_database = CreateMockWalletDatabase(options); return std::make_unique<MockableDatabase>(dynamic_cast<MockableDatabase&>(database).m_records);
// Get a cursor to the original database
auto batch = database.MakeBatch();
std::unique_ptr<wallet::DatabaseCursor> cursor = batch->GetNewCursor();
// Get a batch for the new database
auto new_batch = new_database->MakeBatch();
// Read all records from the original database and write them to the new one
while (true) {
DataStream key{};
DataStream value{};
DatabaseCursor::Status status = cursor->Next(key, value);
assert(status != DatabaseCursor::Status::FAIL);
if (status == DatabaseCursor::Status::DONE) break;
new_batch->Write(key, value);
}
return new_database;
} }
std::string getnewaddress(CWallet& w) std::string getnewaddress(CWallet& w)
@ -79,4 +61,93 @@ CTxDestination getNewDestination(CWallet& w, OutputType output_type)
return *Assert(w.GetNewDestination(output_type, "")); return *Assert(w.GetNewDestination(output_type, ""));
} }
DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value)
{
if (!m_pass) {
return Status::FAIL;
}
if (m_cursor == m_cursor_end) {
return Status::DONE;
}
const auto& [key_data, value_data] = *m_cursor;
key.write(key_data);
value.write(value_data);
m_cursor++;
return Status::MORE;
}
bool MockableBatch::ReadKey(DataStream&& key, DataStream& value)
{
if (!m_pass) {
return false;
}
SerializeData key_data{key.begin(), key.end()};
const auto& it = m_records.find(key_data);
if (it == m_records.end()) {
return false;
}
value.write(it->second);
return true;
}
bool MockableBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
{
if (!m_pass) {
return false;
}
SerializeData key_data{key.begin(), key.end()};
SerializeData value_data{value.begin(), value.end()};
auto [it, inserted] = m_records.emplace(key_data, value_data);
if (!inserted && overwrite) { // Overwrite if requested
it->second = value_data;
inserted = true;
}
return inserted;
}
bool MockableBatch::EraseKey(DataStream&& key)
{
if (!m_pass) {
return false;
}
SerializeData key_data{key.begin(), key.end()};
m_records.erase(key_data);
return true;
}
bool MockableBatch::HasKey(DataStream&& key)
{
if (!m_pass) {
return false;
}
SerializeData key_data{key.begin(), key.end()};
return m_records.count(key_data) > 0;
}
bool MockableBatch::ErasePrefix(Span<const std::byte> prefix)
{
if (!m_pass) {
return false;
}
auto it = m_records.begin();
while (it != m_records.end()) {
auto& key = it->first;
if (key.size() < prefix.size() || std::search(key.begin(), key.end(), prefix.begin(), prefix.end()) != key.begin()) {
it++;
continue;
}
it = m_records.erase(it);
}
return true;
}
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(std::map<SerializeData, SerializeData> records)
{
return std::make_unique<MockableDatabase>(records);
}
MockableDatabase& GetMockableDatabase(CWallet& wallet)
{
return dynamic_cast<MockableDatabase&>(wallet.GetDatabase());
}
} // namespace wallet } // namespace wallet

View file

@ -6,6 +6,8 @@
#define BITCOIN_WALLET_TEST_UTIL_H #define BITCOIN_WALLET_TEST_UTIL_H
#include <script/standard.h> #include <script/standard.h>
#include <wallet/db.h>
#include <memory> #include <memory>
class ArgsManager; class ArgsManager;
@ -18,19 +20,90 @@ class Chain;
namespace wallet { namespace wallet {
class CWallet; class CWallet;
struct DatabaseOptions;
class WalletDatabase; class WalletDatabase;
std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key); std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key);
// Creates a copy of the provided database // Creates a copy of the provided database
std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database, DatabaseOptions& options); std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database);
/** Returns a new encoded destination from the wallet (hardcoded to BECH32) */ /** Returns a new encoded destination from the wallet (hardcoded to BECH32) */
std::string getnewaddress(CWallet& w); std::string getnewaddress(CWallet& w);
/** Returns a new destination, of an specific type, from the wallet */ /** Returns a new destination, of an specific type, from the wallet */
CTxDestination getNewDestination(CWallet& w, OutputType output_type); CTxDestination getNewDestination(CWallet& w, OutputType output_type);
class MockableCursor: public DatabaseCursor
{
public:
std::map<SerializeData, SerializeData>::const_iterator m_cursor;
std::map<SerializeData, SerializeData>::const_iterator m_cursor_end;
bool m_pass;
explicit MockableCursor(const std::map<SerializeData, SerializeData>& records, bool pass) : m_cursor(records.begin()), m_cursor_end(records.end()), m_pass(pass) {}
~MockableCursor() {}
Status Next(DataStream& key, DataStream& value) override;
};
class MockableBatch : public DatabaseBatch
{
private:
std::map<SerializeData, SerializeData>& m_records;
bool m_pass;
bool ReadKey(DataStream&& key, DataStream& value) override;
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite=true) override;
bool EraseKey(DataStream&& key) override;
bool HasKey(DataStream&& key) override;
bool ErasePrefix(Span<const std::byte> prefix) override;
public:
explicit MockableBatch(std::map<SerializeData, SerializeData>& records, bool pass) : m_records(records), m_pass(pass) {}
~MockableBatch() {}
void Flush() override {}
void Close() override {}
std::unique_ptr<DatabaseCursor> GetNewCursor() override
{
return std::make_unique<MockableCursor>(m_records, m_pass);
}
bool TxnBegin() override { return m_pass; }
bool TxnCommit() override { return m_pass; }
bool TxnAbort() override { return m_pass; }
};
/** A WalletDatabase whose contents and return values can be modified as needed for testing
**/
class MockableDatabase : public WalletDatabase
{
public:
std::map<SerializeData, SerializeData> m_records;
bool m_pass{true};
MockableDatabase(std::map<SerializeData, SerializeData> records = {}) : WalletDatabase(), m_records(records) {}
~MockableDatabase() {};
void Open() override {}
void AddRef() override {}
void RemoveRef() override {}
bool Rewrite(const char* pszSkip=nullptr) override { return m_pass; }
bool Backup(const std::string& strDest) const override { return m_pass; }
void Flush() override {}
void Close() override {}
bool PeriodicFlush() override { return m_pass; }
void IncrementUpdateCounter() override {}
void ReloadDbEnv() override {}
std::string Filename() override { return "mockable"; }
std::string Format() override { return "mock"; }
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<MockableBatch>(m_records, m_pass); }
};
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(std::map<SerializeData, SerializeData> records = {});
MockableDatabase& GetMockableDatabase(CWallet& wallet);
} // namespace wallet } // namespace wallet
#endif // BITCOIN_WALLET_TEST_UTIL_H #endif // BITCOIN_WALLET_TEST_UTIL_H

View file

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <wallet/test/util.h>
#include <wallet/test/wallet_test_fixture.h> #include <wallet/test/wallet_test_fixture.h>
#include <scheduler.h> #include <scheduler.h>
@ -11,7 +12,7 @@ namespace wallet {
WalletTestingSetup::WalletTestingSetup(const ChainType chainType) WalletTestingSetup::WalletTestingSetup(const ChainType chainType)
: TestingSetup(chainType), : TestingSetup(chainType),
m_wallet_loader{interfaces::MakeWalletLoader(*m_node.chain, *Assert(m_node.args))}, m_wallet_loader{interfaces::MakeWalletLoader(*m_node.chain, *Assert(m_node.args))},
m_wallet(m_node.chain.get(), "", CreateMockWalletDatabase()) m_wallet(m_node.chain.get(), "", CreateMockableWalletDatabase())
{ {
m_wallet.LoadWallet(); m_wallet.LoadWallet();
m_chain_notifications_handler = m_node.chain->handleNotifications({ &m_wallet, [](CWallet*) {} }); m_chain_notifications_handler = m_node.chain->handleNotifications({ &m_wallet, [](CWallet*) {} });

View file

@ -97,7 +97,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions fails to read an unknown start block. // Verify ScanForWalletTransactions fails to read an unknown start block.
{ {
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase()); CWallet wallet(m_node.chain.get(), "", CreateMockableWalletDatabase());
{ {
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex()); LOCK(Assert(m_node.chainman)->GetMutex());
@ -118,7 +118,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions picks up transactions in both the old // Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files. // and new block files.
{ {
CWallet wallet(m_node.chain.get(), "", CreateMockWalletDatabase()); CWallet wallet(m_node.chain.get(), "", CreateMockableWalletDatabase());
{ {
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex()); LOCK(Assert(m_node.chainman)->GetMutex());
@ -163,7 +163,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions only picks transactions in the new block // Verify ScanForWalletTransactions only picks transactions in the new block
// file. // file.
{ {
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase()); CWallet wallet(m_node.chain.get(), "", CreateMockableWalletDatabase());
{ {
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex()); LOCK(Assert(m_node.chainman)->GetMutex());
@ -191,7 +191,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions scans no blocks. // Verify ScanForWalletTransactions scans no blocks.
{ {
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase()); CWallet wallet(m_node.chain.get(), "", CreateMockableWalletDatabase());
{ {
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex()); LOCK(Assert(m_node.chainman)->GetMutex());
@ -231,7 +231,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// before the missing block, and success for a key whose creation time is // before the missing block, and success for a key whose creation time is
// after. // after.
{ {
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase()); const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan(); wallet->SetupLegacyScriptPubKeyMan();
WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash())); WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
WalletContext context; WalletContext context;
@ -297,7 +297,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{ {
WalletContext context; WalletContext context;
context.args = &m_args; context.args = &m_args;
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase()); const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
{ {
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan(); auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore); LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
@ -320,7 +320,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned. // were scanned, and no prior blocks were scanned.
{ {
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase()); const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->SetupLegacyScriptPubKeyMan(); wallet->SetupLegacyScriptPubKeyMan();
@ -354,7 +354,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// debit functions. // debit functions.
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup) BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{ {
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase()); CWallet wallet(m_node.chain.get(), "", CreateMockableWalletDatabase());
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex()); LOCK(Assert(m_node.chainman)->GetMutex());
@ -708,7 +708,7 @@ BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTest, ListCoinsTest)
BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup) BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{ {
{ {
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase()); const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan(); wallet->SetupLegacyScriptPubKeyMan();
wallet->SetMinVersion(FEATURE_LATEST); wallet->SetMinVersion(FEATURE_LATEST);
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS); wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
@ -716,7 +716,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, "")); BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, ""));
} }
{ {
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase()); const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS); wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet->SetMinVersion(FEATURE_LATEST); wallet->SetMinVersion(FEATURE_LATEST);
@ -950,62 +950,13 @@ BOOST_FIXTURE_TEST_CASE(ZapSelectTx, TestChain100Setup)
TestUnloadWallet(std::move(wallet)); TestUnloadWallet(std::move(wallet));
} }
class FailCursor : public DatabaseCursor
{
public:
Status Next(DataStream& key, DataStream& value) override { return Status::FAIL; }
};
/** RAII class that provides access to a FailDatabase. Which fails if needed. */
class FailBatch : public DatabaseBatch
{
private:
bool m_pass{true};
bool ReadKey(DataStream&& key, DataStream& value) override { return m_pass; }
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override { return m_pass; }
bool EraseKey(DataStream&& key) override { return m_pass; }
bool HasKey(DataStream&& key) override { return m_pass; }
bool ErasePrefix(Span<const std::byte> prefix) override { return m_pass; }
public:
explicit FailBatch(bool pass) : m_pass(pass) {}
void Flush() override {}
void Close() override {}
std::unique_ptr<DatabaseCursor> GetNewCursor() override { return std::make_unique<FailCursor>(); }
bool TxnBegin() override { return false; }
bool TxnCommit() override { return false; }
bool TxnAbort() override { return false; }
};
/** A dummy WalletDatabase that does nothing, only fails if needed.**/
class FailDatabase : public WalletDatabase
{
public:
bool m_pass{true}; // false when this db should fail
void Open() override {};
void AddRef() override {}
void RemoveRef() override {}
bool Rewrite(const char* pszSkip=nullptr) override { return true; }
bool Backup(const std::string& strDest) const override { return true; }
void Close() override {}
void Flush() override {}
bool PeriodicFlush() override { return true; }
void IncrementUpdateCounter() override { ++nUpdateCounter; }
void ReloadDbEnv() override {}
std::string Filename() override { return "faildb"; }
std::string Format() override { return "faildb"; }
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<FailBatch>(m_pass); }
};
/** /**
* Checks a wallet invalid state where the inputs (prev-txs) of a new arriving transaction are not marked dirty, * Checks a wallet invalid state where the inputs (prev-txs) of a new arriving transaction are not marked dirty,
* while the transaction that spends them exist inside the in-memory wallet tx map (not stored on db due a db write failure). * while the transaction that spends them exist inside the in-memory wallet tx map (not stored on db due a db write failure).
*/ */
BOOST_FIXTURE_TEST_CASE(wallet_sync_tx_invalid_state_test, TestingSetup) BOOST_FIXTURE_TEST_CASE(wallet_sync_tx_invalid_state_test, TestingSetup)
{ {
CWallet wallet(m_node.chain.get(), "", std::make_unique<FailDatabase>()); CWallet wallet(m_node.chain.get(), "", CreateMockableWalletDatabase());
{ {
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS); wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
@ -1052,7 +1003,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_sync_tx_invalid_state_test, TestingSetup)
// 1) Make db always fail // 1) Make db always fail
// 2) Try to add a transaction that spends the previously created transaction and // 2) Try to add a transaction that spends the previously created transaction and
// verify that we are not moving forward if the wallet cannot store it // verify that we are not moving forward if the wallet cannot store it
static_cast<FailDatabase&>(wallet.GetDatabase()).m_pass = false; GetMockableDatabase(wallet).m_pass = false;
mtx.vin.clear(); mtx.vin.clear();
mtx.vin.push_back(CTxIn(good_tx_id, 0)); mtx.vin.push_back(CTxIn(good_tx_id, 0));
BOOST_CHECK_EXCEPTION(wallet.transactionAddedToMempool(MakeTransactionRef(mtx)), BOOST_CHECK_EXCEPTION(wallet.transactionAddedToMempool(MakeTransactionRef(mtx)),

View file

@ -34,7 +34,7 @@ public:
BOOST_FIXTURE_TEST_CASE(wallet_load_unknown_descriptor, TestingSetup) BOOST_FIXTURE_TEST_CASE(wallet_load_unknown_descriptor, TestingSetup)
{ {
std::unique_ptr<WalletDatabase> database = CreateMockWalletDatabase(); std::unique_ptr<WalletDatabase> database = CreateMockableWalletDatabase();
{ {
// Write unknown active descriptor // Write unknown active descriptor
WalletBatch batch(*database, false); WalletBatch batch(*database, false);
@ -70,38 +70,45 @@ bool HasAnyRecordOfType(WalletDatabase& db, const std::string& key)
return false; return false;
} }
BOOST_FIXTURE_TEST_CASE(wallet_load_verif_crypted_key_checksum, TestingSetup) template<typename... Args>
SerializeData MakeSerializeData(const Args&... args)
{ {
// The test duplicates the db so each case has its own db instance. CDataStream s(0, 0);
int NUMBER_OF_TESTS = 4; SerializeMany(s, args...);
std::vector<std::unique_ptr<WalletDatabase>> dbs; return {s.begin(), s.end()};
CKey first_key; }
auto get_db = [](std::vector<std::unique_ptr<WalletDatabase>>& dbs) {
std::unique_ptr<WalletDatabase> db = std::move(dbs.back());
dbs.pop_back();
return db;
};
{ // Context setup.
BOOST_FIXTURE_TEST_CASE(wallet_load_ckey, TestingSetup)
{
SerializeData ckey_record_key;
SerializeData ckey_record_value;
std::map<SerializeData, SerializeData> records;
{
// Context setup.
// Create and encrypt legacy wallet // Create and encrypt legacy wallet
std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockWalletDatabase())); std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase()));
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
auto legacy_spkm = wallet->GetOrCreateLegacyScriptPubKeyMan(); auto legacy_spkm = wallet->GetOrCreateLegacyScriptPubKeyMan();
BOOST_CHECK(legacy_spkm->SetupGeneration(true)); BOOST_CHECK(legacy_spkm->SetupGeneration(true));
// Get the first key in the wallet // Retrieve a key
CTxDestination dest = *Assert(legacy_spkm->GetNewDestination(OutputType::LEGACY)); CTxDestination dest = *Assert(legacy_spkm->GetNewDestination(OutputType::LEGACY));
CKeyID key_id = GetKeyForDestination(*legacy_spkm, dest); CKeyID key_id = GetKeyForDestination(*legacy_spkm, dest);
CKey first_key;
BOOST_CHECK(legacy_spkm->GetKey(key_id, first_key)); BOOST_CHECK(legacy_spkm->GetKey(key_id, first_key));
// Encrypt the wallet and duplicate database // Encrypt the wallet
BOOST_CHECK(wallet->EncryptWallet("encrypt")); BOOST_CHECK(wallet->EncryptWallet("encrypt"));
wallet->Flush(); wallet->Flush();
DatabaseOptions options; // Store a copy of all the records
for (int i=0; i < NUMBER_OF_TESTS; i++) { records = GetMockableDatabase(*wallet).m_records;
dbs.emplace_back(DuplicateMockDatabase(wallet->GetDatabase(), options));
} // Get the record for the retrieved key
ckey_record_key = MakeSerializeData(DBKeys::CRYPTED_KEY, first_key.GetPubKey());
ckey_record_value = records.at(ckey_record_key);
} }
{ {
@ -112,7 +119,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_verif_crypted_key_checksum, TestingSetup)
// the records every time that 'CWallet::Unlock' gets called, which is not good. // the records every time that 'CWallet::Unlock' gets called, which is not good.
// Load the wallet and check that is encrypted // Load the wallet and check that is encrypted
std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", get_db(dbs))); std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase(records)));
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::LOAD_OK); BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::LOAD_OK);
BOOST_CHECK(wallet->IsCrypted()); BOOST_CHECK(wallet->IsCrypted());
BOOST_CHECK(HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY)); BOOST_CHECK(HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
@ -127,18 +134,12 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_verif_crypted_key_checksum, TestingSetup)
{ {
// Second test case: // Second test case:
// Verify that loading up a 'ckey' with no checksum triggers a complete re-write of the crypted keys. // Verify that loading up a 'ckey' with no checksum triggers a complete re-write of the crypted keys.
std::unique_ptr<WalletDatabase> db = get_db(dbs);
{
std::unique_ptr<DatabaseBatch> batch = db->MakeBatch(false);
std::pair<std::vector<unsigned char>, uint256> value;
BOOST_CHECK(batch->Read(std::make_pair(DBKeys::CRYPTED_KEY, first_key.GetPubKey()), value));
const auto key = std::make_pair(DBKeys::CRYPTED_KEY, first_key.GetPubKey()); // Cut off the 32 byte checksum from a ckey record
BOOST_CHECK(batch->Write(key, value.first, /*fOverwrite=*/true)); records[ckey_record_key].resize(ckey_record_value.size() - 32);
}
// Load the wallet and check that is encrypted // Load the wallet and check that is encrypted
std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(db))); std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase(records)));
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::LOAD_OK); BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::LOAD_OK);
BOOST_CHECK(wallet->IsCrypted()); BOOST_CHECK(wallet->IsCrypted());
BOOST_CHECK(HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY)); BOOST_CHECK(HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
@ -154,35 +155,25 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_verif_crypted_key_checksum, TestingSetup)
{ {
// Third test case: // Third test case:
// Verify that loading up a 'ckey' with an invalid checksum throws an error. // Verify that loading up a 'ckey' with an invalid checksum throws an error.
std::unique_ptr<WalletDatabase> db = get_db(dbs);
{
std::unique_ptr<DatabaseBatch> batch = db->MakeBatch(false);
std::vector<unsigned char> crypted_data;
BOOST_CHECK(batch->Read(std::make_pair(DBKeys::CRYPTED_KEY, first_key.GetPubKey()), crypted_data));
// Write an invalid checksum // Cut off the 32 byte checksum from a ckey record
std::pair<std::vector<unsigned char>, uint256> value = std::make_pair(crypted_data, uint256::ONE); records[ckey_record_key].resize(ckey_record_value.size() - 32);
const auto key = std::make_pair(DBKeys::CRYPTED_KEY, first_key.GetPubKey()); // Fill in the checksum space with 0s
BOOST_CHECK(batch->Write(key, value, /*fOverwrite=*/true)); records[ckey_record_key].resize(ckey_record_value.size());
}
std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(db))); std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase(records)));
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::CORRUPT); BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::CORRUPT);
} }
{ {
// Fourth test case: // Fourth test case:
// Verify that loading up a 'ckey' with an invalid pubkey throws an error // Verify that loading up a 'ckey' with an invalid pubkey throws an error
std::unique_ptr<WalletDatabase> db = get_db(dbs); CPubKey invalid_key;
{ BOOST_ASSERT(!invalid_key.IsValid());
CPubKey invalid_key; SerializeData key = MakeSerializeData(DBKeys::CRYPTED_KEY, invalid_key);
BOOST_ASSERT(!invalid_key.IsValid()); records[key] = ckey_record_value;
const auto key = std::make_pair(DBKeys::CRYPTED_KEY, invalid_key);
std::pair<std::vector<unsigned char>, uint256> value;
BOOST_CHECK(db->MakeBatch(false)->Write(key, value, /*fOverwrite=*/true));
}
std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(db))); std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase(records)));
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::CORRUPT); BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::CORRUPT);
} }
} }

View file

@ -1263,12 +1263,6 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
return nullptr; return nullptr;
} }
/** Return object for accessing dummy database with no read/write capabilities. */
std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase()
{
return std::make_unique<DummyDatabase>();
}
/** Return object for accessing temporary in-memory database. */ /** Return object for accessing temporary in-memory database. */
std::unique_ptr<WalletDatabase> CreateMockWalletDatabase(DatabaseOptions& options) std::unique_ptr<WalletDatabase> CreateMockWalletDatabase(DatabaseOptions& options)
{ {

View file

@ -306,9 +306,6 @@ using KeyFilterFn = std::function<bool(const std::string&)>;
//! Unserialize a given Key-Value pair and load it into the wallet //! Unserialize a given Key-Value pair and load it into the wallet
bool ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn = nullptr); bool ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn = nullptr);
/** Return object for accessing dummy database with no read/write capabilities. */
std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase();
/** Return object for accessing temporary in-memory database. */ /** Return object for accessing temporary in-memory database. */
std::unique_ptr<WalletDatabase> CreateMockWalletDatabase(DatabaseOptions& options); std::unique_ptr<WalletDatabase> CreateMockWalletDatabase(DatabaseOptions& options);
std::unique_ptr<WalletDatabase> CreateMockWalletDatabase(); std::unique_ptr<WalletDatabase> CreateMockWalletDatabase();