Branch data Line data Source code
1 : : // Copyright (c) 2021-present The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #include <wallet/test/util.h>
6 : :
7 : : #include <chain.h>
8 : : #include <key.h>
9 : : #include <key_io.h>
10 : : #include <streams.h>
11 : : #include <test/util/setup_common.h>
12 : : #include <validationinterface.h>
13 : : #include <wallet/context.h>
14 : : #include <wallet/wallet.h>
15 : : #include <wallet/walletdb.h>
16 : :
17 : : #include <memory>
18 : :
19 : : namespace wallet {
20 : 0 : std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key)
21 : : {
22 [ # # # # ]: 0 : auto wallet = std::make_unique<CWallet>(&chain, "", CreateMockableWalletDatabase());
23 : 0 : {
24 [ # # # # ]: 0 : LOCK2(wallet->cs_wallet, ::cs_main);
25 [ # # # # ]: 0 : wallet->SetLastBlockProcessed(cchain.Height(), cchain.Tip()->GetBlockHash());
26 [ # # ]: 0 : }
27 : 0 : {
28 [ # # ]: 0 : LOCK(wallet->cs_wallet);
29 [ # # ]: 0 : wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
30 [ # # ]: 0 : wallet->SetupDescriptorScriptPubKeyMans();
31 : :
32 : 0 : FlatSigningProvider provider;
33 [ # # ]: 0 : std::string error;
34 [ # # # # : 0 : auto descs = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false);
# # # # ]
35 [ # # # # ]: 0 : assert(descs.size() == 1);
36 [ # # ]: 0 : auto& desc = descs.at(0);
37 [ # # # # ]: 0 : WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
38 [ # # # # ]: 0 : Assert(wallet->AddWalletDescriptor(w_desc, provider, "", false));
39 [ # # ]: 0 : }
40 : 0 : WalletRescanReserver reserver(*wallet);
41 : 0 : reserver.reserve();
42 [ # # # # ]: 0 : CWallet::ScanResult result = wallet->ScanForWalletTransactions(cchain.Genesis()->GetBlockHash(), /*start_height=*/0, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/false);
43 [ # # ]: 0 : assert(result.status == CWallet::ScanResult::SUCCESS);
44 [ # # # # ]: 0 : assert(result.last_scanned_block == cchain.Tip()->GetBlockHash());
45 [ # # # # ]: 0 : assert(*result.last_scanned_height == cchain.Height());
46 [ # # ]: 0 : assert(result.last_failed_block.IsNull());
47 : 0 : return wallet;
48 : 0 : }
49 : :
50 : 0 : std::shared_ptr<CWallet> TestCreateWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context, uint64_t create_flags)
51 : : {
52 [ # # ]: 0 : bilingual_str _error;
53 : 0 : std::vector<bilingual_str> _warnings;
54 [ # # # # ]: 0 : auto wallet = CWallet::CreateNew(context, "", std::move(database), create_flags, _error, _warnings);
55 [ # # ]: 0 : NotifyWalletLoaded(context, wallet);
56 [ # # ]: 0 : if (context.chain) {
57 [ # # ]: 0 : wallet->postInitProcess();
58 : : }
59 : 0 : return wallet;
60 : 0 : }
61 : :
62 : 0 : std::shared_ptr<CWallet> TestCreateWallet(WalletContext& context)
63 : : {
64 [ # # ]: 0 : DatabaseOptions options;
65 : 0 : options.require_create = true;
66 : 0 : options.create_flags = WALLET_FLAG_DESCRIPTORS;
67 : 0 : DatabaseStatus status;
68 [ # # ]: 0 : bilingual_str error;
69 : 0 : std::vector<bilingual_str> warnings;
70 [ # # # # ]: 0 : auto database = MakeWalletDatabase("", options, status, error);
71 [ # # ]: 0 : return TestCreateWallet(std::move(database), context, options.create_flags);
72 : 0 : }
73 : :
74 : :
75 : 0 : std::shared_ptr<CWallet> TestLoadWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context)
76 : : {
77 [ # # ]: 0 : bilingual_str error;
78 : 0 : std::vector<bilingual_str> warnings;
79 [ # # # # ]: 0 : auto wallet = CWallet::LoadExisting(context, "", std::move(database), error, warnings);
80 [ # # ]: 0 : NotifyWalletLoaded(context, wallet);
81 [ # # ]: 0 : if (context.chain) {
82 [ # # ]: 0 : wallet->postInitProcess();
83 : : }
84 : 0 : return wallet;
85 : 0 : }
86 : :
87 : 0 : std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context)
88 : : {
89 [ # # ]: 0 : DatabaseOptions options;
90 : 0 : options.require_existing = true;
91 : 0 : DatabaseStatus status;
92 [ # # ]: 0 : bilingual_str error;
93 : 0 : std::vector<bilingual_str> warnings;
94 [ # # # # ]: 0 : auto database = MakeWalletDatabase("", options, status, error);
95 [ # # ]: 0 : return TestLoadWallet(std::move(database), context);
96 : 0 : }
97 : :
98 : 0 : void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet)
99 : : {
100 : : // Calls SyncWithValidationInterfaceQueue
101 : 0 : wallet->chain().waitForNotificationsIfTipChanged({});
102 [ # # ]: 0 : wallet->m_chain_notifications_handler.reset();
103 : 0 : WaitForDeleteWallet(std::move(wallet));
104 : 0 : }
105 : :
106 : 0 : std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database)
107 : : {
108 [ # # ]: 0 : return std::make_unique<MockableDatabase>(dynamic_cast<MockableDatabase&>(database).m_records);
109 : : }
110 : :
111 : 0 : std::string getnewaddress(CWallet& w)
112 : : {
113 : 0 : constexpr auto output_type = OutputType::BECH32;
114 [ # # ]: 0 : return EncodeDestination(getNewDestination(w, output_type));
115 : : }
116 : :
117 : 0 : CTxDestination getNewDestination(CWallet& w, OutputType output_type)
118 : : {
119 [ # # # # ]: 0 : return *Assert(w.GetNewDestination(output_type, ""));
120 : : }
121 : :
122 [ # # ]: 0 : MockableCursor::MockableCursor(const MockableData& records, bool pass, std::span<const std::byte> prefix)
123 : : {
124 : 0 : m_pass = pass;
125 [ # # ]: 0 : std::tie(m_cursor, m_cursor_end) = records.equal_range(BytePrefix{prefix});
126 : 0 : }
127 : :
128 : 0 : DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value)
129 : : {
130 [ # # ]: 0 : if (!m_pass) {
131 : : return Status::FAIL;
132 : : }
133 [ # # ]: 0 : if (m_cursor == m_cursor_end) {
134 : : return Status::DONE;
135 : : }
136 [ # # ]: 0 : key.clear();
137 [ # # ]: 0 : value.clear();
138 [ # # ]: 0 : const auto& [key_data, value_data] = *m_cursor;
139 [ # # ]: 0 : key.write(key_data);
140 [ # # ]: 0 : value.write(value_data);
141 : 0 : m_cursor++;
142 : 0 : return Status::MORE;
143 : : }
144 : :
145 : 0 : bool MockableBatch::ReadKey(DataStream&& key, DataStream& value)
146 : : {
147 [ # # ]: 0 : if (!m_pass) {
148 : : return false;
149 : : }
150 : 0 : SerializeData key_data{key.begin(), key.end()};
151 : 0 : const auto& it = m_records.find(key_data);
152 [ # # ]: 0 : if (it == m_records.end()) {
153 : : return false;
154 : : }
155 [ # # ]: 0 : value.clear();
156 [ # # # # ]: 0 : value.write(it->second);
157 : : return true;
158 : 0 : }
159 : :
160 : 839881 : bool MockableBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
161 : : {
162 [ + - ]: 839881 : if (!m_pass) {
163 : : return false;
164 : : }
165 : 839881 : SerializeData key_data{key.begin(), key.end()};
166 [ + - + - ]: 1679762 : SerializeData value_data{value.begin(), value.end()};
167 [ + - + + ]: 839881 : auto [it, inserted] = m_records.emplace(key_data, value_data);
168 [ + + + - ]: 839881 : if (!inserted && overwrite) { // Overwrite if requested
169 [ + - ]: 406589 : it->second = value_data;
170 : 406589 : inserted = true;
171 : : }
172 : 839881 : return inserted;
173 : 839881 : }
174 : :
175 : 0 : bool MockableBatch::EraseKey(DataStream&& key)
176 : : {
177 [ # # ]: 0 : if (!m_pass) {
178 : : return false;
179 : : }
180 : 0 : SerializeData key_data{key.begin(), key.end()};
181 : 0 : m_records.erase(key_data);
182 : 0 : return true;
183 : 0 : }
184 : :
185 : 0 : bool MockableBatch::HasKey(DataStream&& key)
186 : : {
187 [ # # ]: 0 : if (!m_pass) {
188 : : return false;
189 : : }
190 : 0 : SerializeData key_data{key.begin(), key.end()};
191 : 0 : return m_records.contains(key_data);
192 : 0 : }
193 : :
194 : 0 : bool MockableBatch::ErasePrefix(std::span<const std::byte> prefix)
195 : : {
196 [ # # ]: 0 : if (!m_pass) {
197 : : return false;
198 : : }
199 : 0 : auto it = m_records.begin();
200 [ # # ]: 0 : while (it != m_records.end()) {
201 [ # # ]: 0 : auto& key = it->first;
202 [ # # # # : 0 : if (key.size() < prefix.size() || std::search(key.begin(), key.end(), prefix.begin(), prefix.end()) != key.begin()) {
# # ]
203 : 0 : it++;
204 : 0 : continue;
205 : : }
206 : 0 : it = m_records.erase(it);
207 : : }
208 : : return true;
209 : : }
210 : :
211 : 13818 : std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(MockableData records)
212 : : {
213 : 13818 : return std::make_unique<MockableDatabase>(records);
214 : : }
215 : :
216 : 0 : MockableDatabase& GetMockableDatabase(CWallet& wallet)
217 : : {
218 [ # # ]: 0 : return dynamic_cast<MockableDatabase&>(wallet.GetDatabase());
219 : : }
220 : :
221 : 0 : wallet::DescriptorScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, const bool success)
222 : : {
223 : 0 : keystore.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
224 : :
225 : 0 : FlatSigningProvider keys;
226 [ # # ]: 0 : std::string error;
227 [ # # # # ]: 0 : auto parsed_descs = Parse(desc_str, keys, error, false);
228 [ # # ]: 0 : Assert(success == (!parsed_descs.empty()));
229 [ # # ]: 0 : if (!success) return nullptr;
230 [ # # ]: 0 : auto& desc = parsed_descs.at(0);
231 : :
232 : 0 : const int64_t range_start = 0, range_end = 1, next_index = 0, timestamp = 1;
233 : :
234 [ # # # # ]: 0 : WalletDescriptor w_desc(std::move(desc), timestamp, range_start, range_end, next_index);
235 : :
236 [ # # ]: 0 : LOCK(keystore.cs_wallet);
237 [ # # # # ]: 0 : auto spkm = Assert(keystore.AddWalletDescriptor(w_desc, keys,/*label=*/"", /*internal=*/false));
238 : 0 : return &spkm.value().get();
239 [ # # ]: 0 : };
240 : : } // namespace wallet
|