mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 06:49:38 -04:00
test: Remove legacy wallet unit tests
This commit is contained in:
parent
d9ac9dbd8e
commit
f94f9399ac
4 changed files with 19 additions and 469 deletions
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include <addresstype.h>
|
||||
#include <bench/bench.h>
|
||||
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
||||
#include <key.h>
|
||||
#include <key_io.h>
|
||||
#include <script/descriptor.h>
|
||||
|
@ -26,7 +25,7 @@
|
|||
#include <utility>
|
||||
|
||||
namespace wallet {
|
||||
static void WalletIsMine(benchmark::Bench& bench, bool legacy_wallet, int num_combo = 0)
|
||||
static void WalletIsMine(benchmark::Bench& bench, int num_combo = 0)
|
||||
{
|
||||
const auto test_setup = MakeNoLogFileContext<TestingSetup>();
|
||||
|
||||
|
@ -36,16 +35,13 @@ static void WalletIsMine(benchmark::Bench& bench, bool legacy_wallet, int num_co
|
|||
|
||||
// Setup the wallet
|
||||
// Loading the wallet will also create it
|
||||
uint64_t create_flags = 0;
|
||||
if (!legacy_wallet) {
|
||||
create_flags = WALLET_FLAG_DESCRIPTORS;
|
||||
}
|
||||
uint64_t create_flags = WALLET_FLAG_DESCRIPTORS;
|
||||
auto database = CreateMockableWalletDatabase();
|
||||
auto wallet = TestLoadWallet(std::move(database), context, create_flags);
|
||||
|
||||
// For a descriptor wallet, fill with num_combo combo descriptors with random keys
|
||||
// This benchmarks a non-HD wallet migrated to descriptors
|
||||
if (!legacy_wallet && num_combo > 0) {
|
||||
if (num_combo > 0) {
|
||||
LOCK(wallet->cs_wallet);
|
||||
for (int i = 0; i < num_combo; ++i) {
|
||||
CKey key;
|
||||
|
@ -70,13 +66,8 @@ static void WalletIsMine(benchmark::Bench& bench, bool legacy_wallet, int num_co
|
|||
TestUnloadWallet(std::move(wallet));
|
||||
}
|
||||
|
||||
#ifdef USE_BDB
|
||||
static void WalletIsMineLegacy(benchmark::Bench& bench) { WalletIsMine(bench, /*legacy_wallet=*/true); }
|
||||
BENCHMARK(WalletIsMineLegacy, benchmark::PriorityLevel::LOW);
|
||||
#endif
|
||||
|
||||
static void WalletIsMineDescriptors(benchmark::Bench& bench) { WalletIsMine(bench, /*legacy_wallet=*/false); }
|
||||
static void WalletIsMineMigratedDescriptors(benchmark::Bench& bench) { WalletIsMine(bench, /*legacy_wallet=*/false, /*num_combo=*/2000); }
|
||||
static void WalletIsMineDescriptors(benchmark::Bench& bench) { WalletIsMine(bench); }
|
||||
static void WalletIsMineMigratedDescriptors(benchmark::Bench& bench) { WalletIsMine(bench, /*num_combo=*/2000); }
|
||||
BENCHMARK(WalletIsMineDescriptors, benchmark::PriorityLevel::LOW);
|
||||
BENCHMARK(WalletIsMineMigratedDescriptors, benchmark::PriorityLevel::LOW);
|
||||
} // namespace wallet
|
||||
|
|
|
@ -2,17 +2,12 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <test/util/setup_common.h>
|
||||
#include <util/check.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/translation.h>
|
||||
#ifdef USE_BDB
|
||||
#include <wallet/bdb.h>
|
||||
#endif
|
||||
#include <wallet/sqlite.h>
|
||||
#include <wallet/migrate.h>
|
||||
#include <wallet/test/util.h>
|
||||
|
@ -60,82 +55,13 @@ static void CheckPrefix(DatabaseBatch& batch, std::span<const std::byte> prefix,
|
|||
|
||||
BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
|
||||
|
||||
#ifdef USE_BDB
|
||||
static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, fs::path& database_filename)
|
||||
{
|
||||
fs::path data_file = BDBDataFile(path);
|
||||
database_filename = data_file.filename();
|
||||
return GetBerkeleyEnv(data_file.parent_path(), false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(getwalletenv_file)
|
||||
{
|
||||
fs::path test_name = "test_name.dat";
|
||||
const fs::path datadir = m_args.GetDataDirNet();
|
||||
fs::path file_path = datadir / test_name;
|
||||
std::ofstream f{file_path};
|
||||
f.close();
|
||||
|
||||
fs::path filename;
|
||||
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
|
||||
BOOST_CHECK_EQUAL(filename, test_name);
|
||||
BOOST_CHECK_EQUAL(env->Directory(), datadir);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(getwalletenv_directory)
|
||||
{
|
||||
fs::path expected_name = "wallet.dat";
|
||||
const fs::path datadir = m_args.GetDataDirNet();
|
||||
|
||||
fs::path filename;
|
||||
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
|
||||
BOOST_CHECK_EQUAL(filename, expected_name);
|
||||
BOOST_CHECK_EQUAL(env->Directory(), datadir);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
|
||||
{
|
||||
fs::path datadir = m_args.GetDataDirNet() / "1";
|
||||
fs::path datadir_2 = m_args.GetDataDirNet() / "2";
|
||||
fs::path filename;
|
||||
|
||||
std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);
|
||||
std::shared_ptr<BerkeleyEnvironment> env_2 = GetWalletEnv(datadir, filename);
|
||||
std::shared_ptr<BerkeleyEnvironment> env_3 = GetWalletEnv(datadir_2, filename);
|
||||
|
||||
BOOST_CHECK(env_1 == env_2);
|
||||
BOOST_CHECK(env_2 != env_3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
|
||||
{
|
||||
fs::path datadir = gArgs.GetDataDirNet() / "1";
|
||||
fs::path datadir_2 = gArgs.GetDataDirNet() / "2";
|
||||
fs::path filename;
|
||||
|
||||
std::shared_ptr <BerkeleyEnvironment> env_1_a = GetWalletEnv(datadir, filename);
|
||||
std::shared_ptr <BerkeleyEnvironment> env_2_a = GetWalletEnv(datadir_2, filename);
|
||||
env_1_a.reset();
|
||||
|
||||
std::shared_ptr<BerkeleyEnvironment> env_1_b = GetWalletEnv(datadir, filename);
|
||||
std::shared_ptr<BerkeleyEnvironment> env_2_b = GetWalletEnv(datadir_2, filename);
|
||||
|
||||
BOOST_CHECK(env_1_a != env_1_b);
|
||||
BOOST_CHECK(env_2_a == env_2_b);
|
||||
}
|
||||
#endif
|
||||
|
||||
static std::vector<std::unique_ptr<WalletDatabase>> TestDatabases(const fs::path& path_root)
|
||||
{
|
||||
std::vector<std::unique_ptr<WalletDatabase>> dbs;
|
||||
DatabaseOptions options;
|
||||
DatabaseStatus status;
|
||||
bilingual_str error;
|
||||
#ifdef USE_BDB
|
||||
dbs.emplace_back(MakeBerkeleyDatabase(path_root / "bdb", options, status, error));
|
||||
// Needs BDB to make the DB to read
|
||||
dbs.emplace_back(std::make_unique<BerkeleyRODatabase>(BDBDataFile(path_root / "bdb"), /*open=*/false));
|
||||
#endif
|
||||
// Unable to test BerkeleyRO since we cannot create a new BDB database to open
|
||||
dbs.emplace_back(MakeSQLiteDatabase(path_root / "sqlite", options, status, error));
|
||||
dbs.emplace_back(CreateMockableWalletDatabase());
|
||||
return dbs;
|
||||
|
@ -148,15 +74,10 @@ BOOST_AUTO_TEST_CASE(db_cursor_prefix_range_test)
|
|||
std::vector<std::string> prefixes = {"", "FIRST", "SECOND", "P\xfe\xff", "P\xff\x01", "\xff\xff"};
|
||||
|
||||
std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
|
||||
if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
|
||||
// For BerkeleyRO, open the file now. This must happen after BDB has written to the file
|
||||
database->Open();
|
||||
} else {
|
||||
// Write elements to it if not berkeleyro
|
||||
for (unsigned int i = 0; i < 10; i++) {
|
||||
for (const auto& prefix : prefixes) {
|
||||
BOOST_CHECK(handler->Write(std::make_pair(prefix, i), i));
|
||||
}
|
||||
// Write elements to it
|
||||
for (unsigned int i = 0; i < 10; i++) {
|
||||
for (const auto& prefix : prefixes) {
|
||||
BOOST_CHECK(handler->Write(std::make_pair(prefix, i), i));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,14 +127,9 @@ BOOST_AUTO_TEST_CASE(db_cursor_prefix_byte_test)
|
|||
for (const auto& database : TestDatabases(m_path_root)) {
|
||||
std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
|
||||
|
||||
if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
|
||||
// For BerkeleyRO, open the file now. This must happen after BDB has written to the file
|
||||
database->Open();
|
||||
} else {
|
||||
// Write elements to it if not berkeleyro
|
||||
for (const auto& [k, v] : {e, p, ps, f, fs, ff, ffs}) {
|
||||
batch->Write(std::span{k}, std::span{v});
|
||||
}
|
||||
// Write elements to it if not berkeleyro
|
||||
for (const auto& [k, v] : {e, p, ps, f, fs, ff, ffs}) {
|
||||
batch->Write(std::span{k}, std::span{v});
|
||||
}
|
||||
|
||||
CheckPrefix(*batch, StringBytes(""), {e, p, ps, f, fs, ff, ffs});
|
||||
|
@ -231,10 +147,6 @@ BOOST_AUTO_TEST_CASE(db_availability_after_write_error)
|
|||
// To simulate the behavior, record overwrites are disallowed, and the test verifies
|
||||
// that the database remains active after failing to store an existing record.
|
||||
for (const auto& database : TestDatabases(m_path_root)) {
|
||||
if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
|
||||
// Skip this test if BerkeleyRO
|
||||
continue;
|
||||
}
|
||||
// Write original record
|
||||
std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
|
||||
std::string key = "key";
|
||||
|
|
|
@ -195,141 +195,6 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
|
||||
{
|
||||
// Cap last block file size, and mine new block in a new block file.
|
||||
CBlockIndex* oldTip = WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain().Tip());
|
||||
WITH_LOCK(::cs_main, m_node.chainman->m_blockman.GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE);
|
||||
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
||||
CBlockIndex* newTip = WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain().Tip());
|
||||
|
||||
// Prune the older block file.
|
||||
int file_number;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
file_number = oldTip->GetBlockPos().nFile;
|
||||
Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
|
||||
}
|
||||
m_node.chainman->m_blockman.UnlinkPrunedFiles({file_number});
|
||||
|
||||
// Verify importmulti RPC returns failure for a key whose creation time is
|
||||
// before the missing block, and success for a key whose creation time is
|
||||
// after.
|
||||
{
|
||||
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
|
||||
wallet->SetupLegacyScriptPubKeyMan();
|
||||
WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
|
||||
WalletContext context;
|
||||
context.args = &m_args;
|
||||
AddWallet(context, wallet);
|
||||
UniValue keys;
|
||||
keys.setArray();
|
||||
UniValue key;
|
||||
key.setObject();
|
||||
key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(coinbaseKey.GetPubKey())));
|
||||
key.pushKV("timestamp", 0);
|
||||
key.pushKV("internal", UniValue(true));
|
||||
keys.push_back(key);
|
||||
key.clear();
|
||||
key.setObject();
|
||||
CKey futureKey = GenerateRandomKey();
|
||||
key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey.GetPubKey())));
|
||||
key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1);
|
||||
key.pushKV("internal", UniValue(true));
|
||||
keys.push_back(std::move(key));
|
||||
JSONRPCRequest request;
|
||||
request.context = &context;
|
||||
request.params.setArray();
|
||||
request.params.push_back(std::move(keys));
|
||||
|
||||
UniValue response = importmulti().HandleRequest(request);
|
||||
BOOST_CHECK_EQUAL(response.write(),
|
||||
strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Rescan failed for key with creation "
|
||||
"timestamp %d. There was an error reading a block from time %d, which is after or within %d "
|
||||
"seconds of key creation, and could contain transactions pertaining to the key. As a result, "
|
||||
"transactions and coins using this key may not appear in the wallet. This error could be caused "
|
||||
"by pruning or data corruption (see bitcoind log for details) and could be dealt with by "
|
||||
"downloading and rescanning the relevant blocks (see -reindex option and rescanblockchain "
|
||||
"RPC).\"}},{\"success\":true}]",
|
||||
0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
|
||||
RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify importwallet RPC starts rescan at earliest block with timestamp
|
||||
// greater or equal than key birthday. Previously there was a bug where
|
||||
// importwallet RPC would start the scan at the latest block with timestamp less
|
||||
// than or equal to key birthday.
|
||||
BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
||||
{
|
||||
// Create two blocks with same timestamp to verify that importwallet rescan
|
||||
// will pick up both blocks, not just the first.
|
||||
const int64_t BLOCK_TIME = WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain().Tip()->GetBlockTimeMax() + 5);
|
||||
SetMockTime(BLOCK_TIME);
|
||||
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
|
||||
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
|
||||
|
||||
// Set key birthday to block time increased by the timestamp window, so
|
||||
// rescan will start at the block time.
|
||||
const int64_t KEY_TIME = BLOCK_TIME + TIMESTAMP_WINDOW;
|
||||
SetMockTime(KEY_TIME);
|
||||
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
|
||||
|
||||
std::string backup_file = fs::PathToString(m_args.GetDataDirNet() / "wallet.backup");
|
||||
|
||||
// Import key into wallet and call dumpwallet to create backup file.
|
||||
{
|
||||
WalletContext context;
|
||||
context.args = &m_args;
|
||||
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
|
||||
{
|
||||
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
|
||||
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
|
||||
spk_man->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
|
||||
spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
|
||||
|
||||
AddWallet(context, wallet);
|
||||
LOCK(Assert(m_node.chainman)->GetMutex());
|
||||
wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
|
||||
}
|
||||
JSONRPCRequest request;
|
||||
request.context = &context;
|
||||
request.params.setArray();
|
||||
request.params.push_back(backup_file);
|
||||
|
||||
wallet::dumpwallet().HandleRequest(request);
|
||||
RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
|
||||
}
|
||||
|
||||
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
|
||||
// were scanned, and no prior blocks were scanned.
|
||||
{
|
||||
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
|
||||
LOCK(wallet->cs_wallet);
|
||||
wallet->SetupLegacyScriptPubKeyMan();
|
||||
|
||||
WalletContext context;
|
||||
context.args = &m_args;
|
||||
JSONRPCRequest request;
|
||||
request.context = &context;
|
||||
request.params.setArray();
|
||||
request.params.push_back(backup_file);
|
||||
AddWallet(context, wallet);
|
||||
LOCK(Assert(m_node.chainman)->GetMutex());
|
||||
wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
|
||||
wallet::importwallet().HandleRequest(request);
|
||||
RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
|
||||
|
||||
BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U);
|
||||
BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U);
|
||||
for (size_t i = 0; i < m_coinbase_txns.size(); ++i) {
|
||||
bool found = wallet->GetWalletTx(m_coinbase_txns[i]->GetHash());
|
||||
bool expected = i >= 100;
|
||||
BOOST_CHECK_EQUAL(found, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies that wallet settings can be added and removed
|
||||
// concurrently, ensuring no race conditions occur during either process.
|
||||
BOOST_FIXTURE_TEST_CASE(write_wallet_settings_concurrently, TestingSetup)
|
||||
|
@ -495,87 +360,6 @@ BOOST_FIXTURE_TEST_CASE(LoadReceiveRequests, TestingSetup)
|
|||
}
|
||||
}
|
||||
|
||||
// Test some watch-only LegacyScriptPubKeyMan methods by the procedure of loading (LoadWatchOnly),
|
||||
// checking (HaveWatchOnly), getting (GetWatchPubKey) and removing (RemoveWatchOnly) a
|
||||
// given PubKey, resp. its corresponding P2PK Script. Results of the impact on
|
||||
// the address -> PubKey map is dependent on whether the PubKey is a point on the curve
|
||||
static void TestWatchOnlyPubKey(LegacyScriptPubKeyMan* spk_man, const CPubKey& add_pubkey)
|
||||
{
|
||||
CScript p2pk = GetScriptForRawPubKey(add_pubkey);
|
||||
CKeyID add_address = add_pubkey.GetID();
|
||||
CPubKey found_pubkey;
|
||||
LOCK(spk_man->cs_KeyStore);
|
||||
|
||||
// all Scripts (i.e. also all PubKeys) are added to the general watch-only set
|
||||
BOOST_CHECK(!spk_man->HaveWatchOnly(p2pk));
|
||||
spk_man->LoadWatchOnly(p2pk);
|
||||
BOOST_CHECK(spk_man->HaveWatchOnly(p2pk));
|
||||
|
||||
// only PubKeys on the curve shall be added to the watch-only address -> PubKey map
|
||||
bool is_pubkey_fully_valid = add_pubkey.IsFullyValid();
|
||||
if (is_pubkey_fully_valid) {
|
||||
BOOST_CHECK(spk_man->GetWatchPubKey(add_address, found_pubkey));
|
||||
BOOST_CHECK(found_pubkey == add_pubkey);
|
||||
} else {
|
||||
BOOST_CHECK(!spk_man->GetWatchPubKey(add_address, found_pubkey));
|
||||
BOOST_CHECK(found_pubkey == CPubKey()); // passed key is unchanged
|
||||
}
|
||||
|
||||
spk_man->RemoveWatchOnly(p2pk);
|
||||
BOOST_CHECK(!spk_man->HaveWatchOnly(p2pk));
|
||||
|
||||
if (is_pubkey_fully_valid) {
|
||||
BOOST_CHECK(!spk_man->GetWatchPubKey(add_address, found_pubkey));
|
||||
BOOST_CHECK(found_pubkey == add_pubkey); // passed key is unchanged
|
||||
}
|
||||
}
|
||||
|
||||
// Cryptographically invalidate a PubKey whilst keeping length and first byte
|
||||
static void PollutePubKey(CPubKey& pubkey)
|
||||
{
|
||||
assert(pubkey.size() >= 1);
|
||||
std::vector<unsigned char> pubkey_raw;
|
||||
pubkey_raw.push_back(pubkey[0]);
|
||||
pubkey_raw.insert(pubkey_raw.end(), pubkey.size() - 1, 0);
|
||||
pubkey = CPubKey(pubkey_raw);
|
||||
assert(!pubkey.IsFullyValid());
|
||||
assert(pubkey.IsValid());
|
||||
}
|
||||
|
||||
// Test watch-only logic for PubKeys
|
||||
BOOST_AUTO_TEST_CASE(WatchOnlyPubKeys)
|
||||
{
|
||||
CKey key;
|
||||
CPubKey pubkey;
|
||||
LegacyScriptPubKeyMan* spk_man = m_wallet.GetOrCreateLegacyScriptPubKeyMan();
|
||||
|
||||
BOOST_CHECK(!spk_man->HaveWatchOnly());
|
||||
|
||||
// uncompressed valid PubKey
|
||||
key.MakeNewKey(false);
|
||||
pubkey = key.GetPubKey();
|
||||
assert(!pubkey.IsCompressed());
|
||||
TestWatchOnlyPubKey(spk_man, pubkey);
|
||||
|
||||
// uncompressed cryptographically invalid PubKey
|
||||
PollutePubKey(pubkey);
|
||||
TestWatchOnlyPubKey(spk_man, pubkey);
|
||||
|
||||
// compressed valid PubKey
|
||||
key.MakeNewKey(true);
|
||||
pubkey = key.GetPubKey();
|
||||
assert(pubkey.IsCompressed());
|
||||
TestWatchOnlyPubKey(spk_man, pubkey);
|
||||
|
||||
// compressed cryptographically invalid PubKey
|
||||
PollutePubKey(pubkey);
|
||||
TestWatchOnlyPubKey(spk_man, pubkey);
|
||||
|
||||
// invalid empty PubKey
|
||||
pubkey = CPubKey();
|
||||
TestWatchOnlyPubKey(spk_man, pubkey);
|
||||
}
|
||||
|
||||
class ListCoinsTestingSetup : public TestChain100Setup
|
||||
{
|
||||
public:
|
||||
|
@ -718,22 +502,12 @@ BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTest, ListCoinsTest)
|
|||
|
||||
BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
|
||||
{
|
||||
{
|
||||
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
|
||||
wallet->SetupLegacyScriptPubKeyMan();
|
||||
wallet->SetMinVersion(FEATURE_LATEST);
|
||||
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
||||
BOOST_CHECK(!wallet->TopUpKeyPool(1000));
|
||||
BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, ""));
|
||||
}
|
||||
{
|
||||
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
|
||||
LOCK(wallet->cs_wallet);
|
||||
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
|
||||
wallet->SetMinVersion(FEATURE_LATEST);
|
||||
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
||||
BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, ""));
|
||||
}
|
||||
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
|
||||
LOCK(wallet->cs_wallet);
|
||||
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
|
||||
wallet->SetMinVersion(FEATURE_LATEST);
|
||||
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
||||
BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, ""));
|
||||
}
|
||||
|
||||
// Explicit calculation which is used to test the wallet constant
|
||||
|
|
|
@ -84,132 +84,5 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_descriptors, TestingSetup)
|
|||
}
|
||||
}
|
||||
|
||||
bool HasAnyRecordOfType(WalletDatabase& db, const std::string& key)
|
||||
{
|
||||
std::unique_ptr<DatabaseBatch> batch = db.MakeBatch(false);
|
||||
BOOST_CHECK(batch);
|
||||
std::unique_ptr<DatabaseCursor> cursor = batch->GetNewCursor();
|
||||
BOOST_CHECK(cursor);
|
||||
while (true) {
|
||||
DataStream ssKey{};
|
||||
DataStream ssValue{};
|
||||
DatabaseCursor::Status status = cursor->Next(ssKey, ssValue);
|
||||
assert(status != DatabaseCursor::Status::FAIL);
|
||||
if (status == DatabaseCursor::Status::DONE) break;
|
||||
std::string type;
|
||||
ssKey >> type;
|
||||
if (type == key) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
SerializeData MakeSerializeData(const Args&... args)
|
||||
{
|
||||
DataStream s{};
|
||||
SerializeMany(s, args...);
|
||||
return {s.begin(), s.end()};
|
||||
}
|
||||
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(wallet_load_ckey, TestingSetup)
|
||||
{
|
||||
SerializeData ckey_record_key;
|
||||
SerializeData ckey_record_value;
|
||||
MockableData records;
|
||||
|
||||
{
|
||||
// Context setup.
|
||||
// Create and encrypt legacy wallet
|
||||
std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase()));
|
||||
LOCK(wallet->cs_wallet);
|
||||
auto legacy_spkm = wallet->GetOrCreateLegacyScriptPubKeyMan();
|
||||
BOOST_CHECK(legacy_spkm->SetupGeneration(true));
|
||||
|
||||
// Retrieve a key
|
||||
CTxDestination dest = *Assert(legacy_spkm->GetNewDestination(OutputType::LEGACY));
|
||||
CKeyID key_id = GetKeyForDestination(*legacy_spkm, dest);
|
||||
CKey first_key;
|
||||
BOOST_CHECK(legacy_spkm->GetKey(key_id, first_key));
|
||||
|
||||
// Encrypt the wallet
|
||||
BOOST_CHECK(wallet->EncryptWallet("encrypt"));
|
||||
wallet->Flush();
|
||||
|
||||
// Store a copy of all the records
|
||||
records = GetMockableDatabase(*wallet).m_records;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
{
|
||||
// First test case:
|
||||
// Erase all the crypted keys from db and unlock the wallet.
|
||||
// The wallet will only re-write the crypted keys to db if any checksum is missing at load time.
|
||||
// So, if any 'ckey' record re-appears on db, then the checksums were not properly calculated, and we are re-writing
|
||||
// the records every time that 'CWallet::Unlock' gets called, which is not good.
|
||||
|
||||
// Load the wallet and check that is encrypted
|
||||
std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase(records)));
|
||||
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::LOAD_OK);
|
||||
BOOST_CHECK(wallet->IsCrypted());
|
||||
BOOST_CHECK(HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
|
||||
|
||||
// Now delete all records and check that the 'Unlock' function doesn't re-write them
|
||||
BOOST_CHECK(wallet->GetLegacyScriptPubKeyMan()->DeleteRecords());
|
||||
BOOST_CHECK(!HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
|
||||
BOOST_CHECK(wallet->Unlock("encrypt"));
|
||||
BOOST_CHECK(!HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
|
||||
}
|
||||
|
||||
{
|
||||
// Second test case:
|
||||
// Verify that loading up a 'ckey' with no checksum triggers a complete re-write of the crypted keys.
|
||||
|
||||
// Cut off the 32 byte checksum from a ckey record
|
||||
records[ckey_record_key].resize(ckey_record_value.size() - 32);
|
||||
|
||||
// Load the wallet and check that is encrypted
|
||||
std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase(records)));
|
||||
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::LOAD_OK);
|
||||
BOOST_CHECK(wallet->IsCrypted());
|
||||
BOOST_CHECK(HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
|
||||
|
||||
// Now delete all ckey records and check that the 'Unlock' function re-writes them
|
||||
// (this is because the wallet, at load time, found a ckey record with no checksum)
|
||||
BOOST_CHECK(wallet->GetLegacyScriptPubKeyMan()->DeleteRecords());
|
||||
BOOST_CHECK(!HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
|
||||
BOOST_CHECK(wallet->Unlock("encrypt"));
|
||||
BOOST_CHECK(HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
|
||||
}
|
||||
|
||||
{
|
||||
// Third test case:
|
||||
// Verify that loading up a 'ckey' with an invalid checksum throws an error.
|
||||
|
||||
// Cut off the 32 byte checksum from a ckey record
|
||||
records[ckey_record_key].resize(ckey_record_value.size() - 32);
|
||||
// Fill in the checksum space with 0s
|
||||
records[ckey_record_key].resize(ckey_record_value.size());
|
||||
|
||||
std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase(records)));
|
||||
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::CORRUPT);
|
||||
}
|
||||
|
||||
{
|
||||
// Fourth test case:
|
||||
// Verify that loading up a 'ckey' with an invalid pubkey throws an error
|
||||
CPubKey invalid_key;
|
||||
BOOST_CHECK(!invalid_key.IsValid());
|
||||
SerializeData key = MakeSerializeData(DBKeys::CRYPTED_KEY, invalid_key);
|
||||
records[key] = ckey_record_value;
|
||||
|
||||
std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase(records)));
|
||||
BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::CORRUPT);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
} // namespace wallet
|
||||
|
|
Loading…
Add table
Reference in a new issue