forked from dashpay/dash
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathwalletdb_tests.cpp
More file actions
153 lines (133 loc) · 7.14 KB
/
Copy pathwalletdb_tests.cpp
File metadata and controls
153 lines (133 loc) · 7.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright (c) 2012-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/util/setup_common.h>
#include <clientversion.h>
#include <streams.h>
#include <uint256.h>
#include <wallet/hdchain.h>
#include <wallet/test/wallet_test_fixture.h>
#include <wallet/walletdb.h>
#include <boost/test/unit_test.hpp>
namespace wallet {
BOOST_FIXTURE_TEST_SUITE(walletdb_tests, WalletTestingSetup)
BOOST_AUTO_TEST_CASE(walletdb_readkeyvalue)
{
/**
* When ReadKeyValue() reads from either a "key" or "wkey" it first reads the CDataStream steam into a
* CPrivKey or CWalletKey respectively and then reads a hash of the pubkey and privkey into a uint256.
* Wallets from 0.8 or before do not store the pubkey/privkey hash, trying to read the hash from old
* wallets throws an exception, for backwards compatibility this read is wrapped in a try block to
* silently fail. The test here makes sure the type of exception thrown from CDataStream::read()
* matches the type we expect, otherwise we need to update the "key"/"wkey" exception type caught.
*/
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
uint256 dummy;
BOOST_CHECK_THROW(ssValue >> dummy, std::ios_base::failure);
}
// Helper: build a key/value stream pair for an HD chain DB record and run ReadKeyValue.
static bool TryReadHDChainRecord(CWallet& wallet, const std::string& dbKey, bool fCrypted, std::string& strErrOut)
{
CHDChain chain;
chain.SetCrypted(fCrypted);
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
ssKey << dbKey;
ssValue << chain;
std::string strType;
LOCK(wallet.cs_wallet);
return ReadKeyValue(&wallet, ssKey, ssValue, strType, strErrOut);
}
BOOST_AUTO_TEST_CASE(walletdb_hdchain_type_mismatch)
{
// Regression: a wallet record claiming HDCHAIN but carrying a crypted CHDChain
// (or vice versa) used to trigger an assert and abort the process. It must now
// surface as a graceful load error.
std::string strErr;
BOOST_CHECK(!TryReadHDChainRecord(m_wallet, DBKeys::HDCHAIN, /*fCrypted=*/true, strErr));
BOOST_CHECK_EQUAL(strErr, "Error reading wallet database: HD chain type mismatch");
strErr.clear();
BOOST_CHECK(!TryReadHDChainRecord(m_wallet, DBKeys::CRYPTED_HDCHAIN, /*fCrypted=*/false, strErr));
BOOST_CHECK_EQUAL(strErr, "Error reading wallet database: HD chain type mismatch");
}
BOOST_AUTO_TEST_CASE(walletdb_platform_data_records)
{
LOCK(m_wallet.cs_wallet);
// Round-trip through the in-memory map: write, prefix query, erase.
const std::vector<unsigned char> value_a{0x01, 0x02};
const std::vector<unsigned char> value_b{0x03};
BOOST_CHECK(m_wallet.WritePlatformData("platform/identity/0", value_a));
BOOST_CHECK(m_wallet.WritePlatformData("platform/identity/1", value_b));
BOOST_CHECK(m_wallet.WritePlatformData("platform/metadata", value_b));
auto records{m_wallet.GetPlatformData("platform/identity/")};
BOOST_CHECK_EQUAL(records.size(), 2U);
BOOST_CHECK(records.at("platform/identity/0") == value_a);
BOOST_CHECK(records.at("platform/identity/1") == value_b);
BOOST_CHECK_EQUAL(m_wallet.GetPlatformData("").size(), 3U);
BOOST_CHECK(m_wallet.GetPlatformData("platform/idem").empty());
// An empty value erases the record.
BOOST_CHECK(m_wallet.WritePlatformData("platform/identity/0", {}));
records = m_wallet.GetPlatformData("platform/identity/");
BOOST_CHECK_EQUAL(records.size(), 1U);
BOOST_CHECK_EQUAL(records.count("platform/identity/0"), 0U);
// The wallet-load path (ReadKeyValue) must populate the same map.
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
ssKey << std::make_pair(DBKeys::PLATFORM_DATA, std::string{"platform/loaded"});
ssValue << value_a;
std::string strType, strErr;
BOOST_CHECK(ReadKeyValue(&m_wallet, ssKey, ssValue, strType, strErr));
BOOST_CHECK_EQUAL(strType, DBKeys::PLATFORM_DATA);
BOOST_CHECK(m_wallet.GetPlatformData("platform/loaded").at("platform/loaded") == value_a);
// A truncated value must fail the read and identify PLATFORM_DATA as the
// failing type so LoadWallet() can classify the damage as noncritical.
CDataStream ssGood(SER_DISK, CLIENT_VERSION);
ssGood << value_a;
CDataStream ssBadKey(SER_DISK, CLIENT_VERSION);
CDataStream ssBadValue(SER_DISK, CLIENT_VERSION);
ssBadKey << std::make_pair(DBKeys::PLATFORM_DATA, std::string{"platform/corrupt"});
ssBadValue.write({ssGood.data(), ssGood.size() - 1});
strType.clear();
strErr.clear();
BOOST_CHECK(!ReadKeyValue(&m_wallet, ssBadKey, ssBadValue, strType, strErr));
BOOST_CHECK_EQUAL(strType, DBKeys::PLATFORM_DATA);
BOOST_CHECK(m_wallet.GetPlatformData("platform/corrupt").empty());
}
//! Load a wallet whose database holds a PLATFORM_DATA record under
//! `record_key` with a truncated value (claims five bytes, carries none),
//! next to an intact record, and report the load result and the Platform
//! data that made it into the wallet.
static DBErrors LoadWithCorruptPlatformRecord(const node::NodeContext& node, const ArgsManager& args,
const std::string& record_key,
std::map<std::string, std::vector<unsigned char>>& platform_data_out)
{
CWallet wallet(node.chain.get(), node.coinjoin_loader.get(), "", args, CreateMockWalletDatabase());
const std::vector<unsigned char> truncated{0x05};
const std::vector<unsigned char> intact_value{0x01, 0x02};
{
const std::unique_ptr<DatabaseBatch> batch{wallet.GetDatabase().MakeBatch()};
BOOST_REQUIRE(batch->Write(std::make_pair(DBKeys::PLATFORM_DATA, record_key), Span{truncated}));
BOOST_REQUIRE(batch->Write(std::make_pair(DBKeys::PLATFORM_DATA, std::string{"platform/intact"}), intact_value));
}
const DBErrors result{WalletBatch(wallet.GetDatabase()).LoadWallet(&wallet)};
platform_data_out = WITH_LOCK(wallet.cs_wallet, return wallet.GetPlatformData(""));
return result;
}
BOOST_AUTO_TEST_CASE(walletdb_platform_data_corruption_policy)
{
// Platform records are opaque cache/metadata. Damage is noncritical, the
// wallet still loads, and intact records survive. There is no reserved
// Platform seed-selection record.
std::map<std::string, std::vector<unsigned char>> platform_data;
BOOST_CHECK(LoadWithCorruptPlatformRecord(m_node, m_args, "platform/metadata", platform_data) ==
DBErrors::NONCRITICAL_ERROR);
BOOST_CHECK_EQUAL(platform_data.count("platform/metadata"), 0U);
BOOST_CHECK(platform_data.at("platform/intact") == std::vector<unsigned char>({0x01, 0x02}));
platform_data.clear();
BOOST_CHECK(LoadWithCorruptPlatformRecord(m_node, m_args, "platform/identity/0", platform_data) ==
DBErrors::NONCRITICAL_ERROR);
BOOST_CHECK_EQUAL(platform_data.count("platform/identity/0"), 0U);
BOOST_CHECK(platform_data.at("platform/intact") == std::vector<unsigned char>({0x01, 0x02}));
}
BOOST_AUTO_TEST_SUITE_END()
} // namespace wallet