/tmp/bitcoin/src/wallet/test/walletload_tests.cpp
Line | Count | Source |
1 | | // Copyright (c) 2022-present The Bitcoin Core developers |
2 | | // Distributed under the MIT software license, see the accompanying |
3 | | // file COPYING or https://www.opensource.org/licenses/mit-license.php. |
4 | | |
5 | | #include <key.h> |
6 | | #include <script/descriptor.h> |
7 | | #include <wallet/test/util.h> |
8 | | #include <wallet/wallet.h> |
9 | | #include <test/util/common.h> |
10 | | #include <test/util/logging.h> |
11 | | #include <test/util/setup_common.h> |
12 | | |
13 | | #include <boost/test/unit_test.hpp> |
14 | | |
15 | | namespace wallet { |
16 | | |
17 | | BOOST_AUTO_TEST_SUITE(walletload_tests) |
18 | | |
19 | | class DummyDescriptor final : public Descriptor { |
20 | | private: |
21 | | std::string desc; |
22 | | public: |
23 | 2 | explicit DummyDescriptor(const std::string& descriptor) : desc(descriptor) {}; |
24 | 2 | ~DummyDescriptor() = default; |
25 | | |
26 | 2 | std::string ToString(bool compat_format) const override { return desc; } |
27 | 0 | std::string ToCanonicalString() const override { return desc; } |
28 | 0 | std::optional<OutputType> GetOutputType() const override { return OutputType::UNKNOWN; } |
29 | | |
30 | 4 | bool IsRange() const override { return true; } |
31 | 0 | bool IsSolvable() const override { return false; } |
32 | 0 | bool IsSingleType() const override { return true; } |
33 | 0 | bool HavePrivateKeys(const SigningProvider&) const override { return false; } |
34 | 0 | bool ToPrivateString(const SigningProvider& provider, std::string& out) const override { return false; } |
35 | 0 | bool ToNormalizedString(const SigningProvider& provider, std::string& out, const DescriptorCache* cache = nullptr) const override { return false; } |
36 | 0 | bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, DescriptorCache* write_cache = nullptr) const override { return false; }; |
37 | 0 | bool ExpandFromCache(int pos, const DescriptorCache& read_cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override { return false; } |
38 | 0 | void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const override {} |
39 | 0 | std::optional<int64_t> ScriptSize() const override { return {}; } |
40 | 0 | std::optional<int64_t> MaxSatisfactionWeight(bool) const override { return {}; } |
41 | 0 | std::optional<int64_t> MaxSatisfactionElems() const override { return {}; } |
42 | 0 | void GetPubKeys(std::set<CPubKey>& pubkeys, std::set<CExtPubKey>& ext_pubs) const override {} |
43 | 0 | bool HasScripts() const override { return true; } |
44 | 0 | std::vector<std::string> Warnings() const override { return {}; } |
45 | 0 | uint32_t GetMaxKeyExpr() const override { return 0; } |
46 | 0 | size_t GetKeyCount() const override { return 0; } |
47 | 0 | bool CanSelfExpand() const final { return false; } |
48 | | }; |
49 | | |
50 | | BOOST_FIXTURE_TEST_CASE(wallet_load_descriptors, TestingSetup) |
51 | 1 | { |
52 | 1 | bilingual_str _error; |
53 | 1 | std::vector<bilingual_str> _warnings; |
54 | 1 | std::unique_ptr<WalletDatabase> database = CreateMockableWalletDatabase(); |
55 | 1 | { |
56 | | // Write unknown active descriptor |
57 | 1 | WalletBatch batch(*database); |
58 | 1 | std::string unknown_desc = "trx(tpubD6NzVbkrYhZ4Y4S7m6Y5s9GD8FqEMBy56AGphZXuagajudVZEnYyBahZMgHNCTJc2at82YX6s8JiL1Lohu5A3v1Ur76qguNH4QVQ7qYrBQx/86'/1'/0'/0/*)#8pn8tzdt"; |
59 | 1 | WalletDescriptor wallet_descriptor(std::make_shared<DummyDescriptor>(unknown_desc), 0, 0, 0, 0); |
60 | 1 | BOOST_CHECK(batch.WriteDescriptor(uint256(), wallet_descriptor)); |
61 | 1 | BOOST_CHECK(batch.WriteActiveScriptPubKeyMan(static_cast<uint8_t>(OutputType::UNKNOWN), uint256(), false)); |
62 | 1 | } |
63 | | |
64 | 1 | { |
65 | | // Now try to load the wallet and verify the error. |
66 | 1 | const std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(database))); |
67 | 1 | BOOST_CHECK_EQUAL(wallet->PopulateWalletFromDB(_error, _warnings), DBErrors::UNKNOWN_DESCRIPTOR); |
68 | 1 | } |
69 | | |
70 | | // Test 2 |
71 | | // Now write a valid descriptor with a different ID which must be accepted |
72 | 1 | database = CreateMockableWalletDatabase(); |
73 | | |
74 | 1 | { |
75 | | // Write valid descriptor with arbitrary ID |
76 | 1 | WalletBatch batch(*database); |
77 | 1 | std::string desc = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu"; |
78 | 1 | WalletDescriptor wallet_descriptor(std::make_shared<DummyDescriptor>(desc), 0, 0, 0, 0); |
79 | 1 | BOOST_CHECK(batch.WriteDescriptor(uint256::ONE, wallet_descriptor)); |
80 | 1 | } |
81 | | |
82 | 1 | { |
83 | | // Now try to load the wallet and verify the result. |
84 | 1 | const std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(database))); |
85 | 1 | BOOST_CHECK_EQUAL(wallet->PopulateWalletFromDB(_error, _warnings), DBErrors::LOAD_OK); |
86 | 1 | } |
87 | 1 | } |
88 | | |
89 | | BOOST_FIXTURE_TEST_CASE(wallet_load_descriptor_cache_invalid_xpub_size, TestingSetup) |
90 | 1 | { |
91 | | // A cache record whose serialized xpub is not exactly BIP32_EXTKEY_SIZE bytes must be |
92 | | // rejected as corruption. |
93 | 1 | bilingual_str error; |
94 | 1 | std::vector<bilingual_str> warnings; |
95 | | |
96 | 1 | const std::string desc_str = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu"; |
97 | 1 | FlatSigningProvider keys; |
98 | 1 | std::string parse_error; |
99 | 1 | std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, keys, parse_error, /*require_checksum=*/true); |
100 | 1 | BOOST_REQUIRE_MESSAGE(descs.size() == 1, parse_error); |
101 | 1 | std::shared_ptr<Descriptor> descriptor = std::move(descs.at(0)); |
102 | 1 | const uint256 desc_id = CompatDescriptorHash(*descriptor); |
103 | | |
104 | 2 | auto make_db_with_short_cache_xpub = [&](const std::string& cache_type) { |
105 | 2 | std::unique_ptr<WalletDatabase> database = CreateMockableWalletDatabase(); |
106 | 2 | { |
107 | 2 | WalletBatch batch(*database); |
108 | 2 | WalletDescriptor wallet_descriptor(descriptor, 0, 0, 0, 0); |
109 | 2 | BOOST_CHECK(batch.WriteDescriptor(desc_id, wallet_descriptor)); |
110 | 2 | } |
111 | 2 | const std::vector<unsigned char> short_xpub(BIP32_EXTKEY_SIZE - 1, 0); |
112 | 2 | std::unique_ptr<DatabaseBatch> raw = database->MakeBatch(); |
113 | 2 | BOOST_CHECK(raw->Write(std::make_pair(std::make_pair(cache_type, desc_id), uint32_t{0}), short_xpub)); |
114 | 2 | return database; |
115 | 2 | }; |
116 | | |
117 | 1 | { |
118 | 1 | std::unique_ptr<WalletDatabase> database = make_db_with_short_cache_xpub("walletdescriptorcache"); |
119 | 1 | ASSERT_DEBUG_LOG("descriptor cache xpub has invalid size"); |
120 | 1 | const std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(database))); |
121 | 1 | BOOST_CHECK_EQUAL(wallet->PopulateWalletFromDB(error, warnings), DBErrors::CORRUPT); |
122 | 1 | } |
123 | | |
124 | 1 | { |
125 | 1 | std::unique_ptr<WalletDatabase> database = make_db_with_short_cache_xpub("walletdescriptorlhcache"); |
126 | 1 | ASSERT_DEBUG_LOG("descriptor last hardened cache xpub has invalid size"); |
127 | 1 | const std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(database))); |
128 | | BOOST_CHECK_EQUAL(wallet->PopulateWalletFromDB(error, warnings), DBErrors::CORRUPT); |
129 | 1 | } |
130 | 1 | } |
131 | | |
132 | | BOOST_AUTO_TEST_SUITE_END() |
133 | | } // namespace wallet |