util.cpp raw
1 // Copyright (c) 2021-2022 The Limenka 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 std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key)
21 {
22 auto wallet = std::make_unique<CWallet>(&chain, "", CreateMockableWalletDatabase());
23 {
24 LOCK2(wallet->cs_wallet, ::cs_main);
25 wallet->SetLastBlockProcessed(cchain.Height(), cchain.Tip()->GetBlockHash());
26 wallet->m_default_address_type = OutputType::BECH32M;
27 }
28 {
29 LOCK(wallet->cs_wallet);
30 wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
31 wallet->SetupDescriptorScriptPubKeyMans();
32
33 FlatSigningProvider provider;
34 std::string error;
35 auto descs = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false);
36 assert(descs.size() == 1);
37 auto& desc = descs.at(0);
38 WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
39 if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
40 }
41 WalletRescanReserver reserver(*wallet);
42 reserver.reserve();
43 CWallet::ScanResult result = wallet->ScanForWalletTransactions(cchain.Genesis()->GetBlockHash(), /*start_height=*/0, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/false);
44 assert(result.status == CWallet::ScanResult::SUCCESS);
45 assert(result.last_scanned_block == cchain.Tip()->GetBlockHash());
46 assert(*result.last_scanned_height == cchain.Height());
47 assert(result.last_failed_block.IsNull());
48 return wallet;
49 }
50
51 std::shared_ptr<CWallet> TestLoadWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context, uint64_t create_flags)
52 {
53 bilingual_str error;
54 std::vector<bilingual_str> warnings;
55 auto wallet = CWallet::Create(context, "", std::move(database), create_flags, error, warnings);
56 NotifyWalletLoaded(context, wallet);
57 if (context.chain) {
58 wallet->postInitProcess();
59 }
60 return wallet;
61 }
62
63 std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context)
64 {
65 DatabaseOptions options;
66 options.create_flags = WALLET_FLAG_DESCRIPTORS;
67 DatabaseStatus status;
68 bilingual_str error;
69 std::vector<bilingual_str> warnings;
70 auto database = MakeWalletDatabase("", options, status, error);
71 return TestLoadWallet(std::move(database), context, options.create_flags);
72 }
73
74 void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet)
75 {
76 // Calls SyncWithValidationInterfaceQueue
77 wallet->chain().waitForNotificationsIfTipChanged({});
78 wallet->m_chain_notifications_handler.reset();
79 WaitForDeleteWallet(std::move(wallet));
80 }
81
82 std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database)
83 {
84 return std::make_unique<MockableDatabase>(dynamic_cast<MockableDatabase&>(database).m_records);
85 }
86
87 std::string getnewaddress(CWallet& w)
88 {
89 constexpr auto output_type = OutputType::BECH32;
90 return EncodeDestination(getNewDestination(w, output_type));
91 }
92
93 CTxDestination getNewDestination(CWallet& w, OutputType output_type)
94 {
95 return *Assert(w.GetNewDestination(output_type, ""));
96 }
97
98 MockableCursor::MockableCursor(const MockableData& records, bool pass, Span<const std::byte> prefix)
99 {
100 m_pass = pass;
101 std::tie(m_cursor, m_cursor_end) = records.equal_range(BytePrefix{prefix});
102 }
103
104 DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value)
105 {
106 if (!m_pass) {
107 return Status::FAIL;
108 }
109 if (m_cursor == m_cursor_end) {
110 return Status::DONE;
111 }
112 key.clear();
113 value.clear();
114 const auto& [key_data, value_data] = *m_cursor;
115 key.write(key_data);
116 value.write(value_data);
117 m_cursor++;
118 return Status::MORE;
119 }
120
121 bool MockableBatch::ReadKey(DataStream&& key, DataStream& value)
122 {
123 if (!m_pass) {
124 return false;
125 }
126 SerializeData key_data{key.begin(), key.end()};
127 const auto& it = m_records.find(key_data);
128 if (it == m_records.end()) {
129 return false;
130 }
131 value.clear();
132 value.write(it->second);
133 return true;
134 }
135
136 bool MockableBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
137 {
138 if (!m_pass) {
139 return false;
140 }
141 SerializeData key_data{key.begin(), key.end()};
142 SerializeData value_data{value.begin(), value.end()};
143 auto [it, inserted] = m_records.emplace(key_data, value_data);
144 if (!inserted && overwrite) { // Overwrite if requested
145 it->second = value_data;
146 inserted = true;
147 }
148 return inserted;
149 }
150
151 bool MockableBatch::EraseKey(DataStream&& key)
152 {
153 if (!m_pass) {
154 return false;
155 }
156 SerializeData key_data{key.begin(), key.end()};
157 m_records.erase(key_data);
158 return true;
159 }
160
161 bool MockableBatch::HasKey(DataStream&& key)
162 {
163 if (!m_pass) {
164 return false;
165 }
166 SerializeData key_data{key.begin(), key.end()};
167 return m_records.count(key_data) > 0;
168 }
169
170 bool MockableBatch::ErasePrefix(Span<const std::byte> prefix)
171 {
172 if (!m_pass) {
173 return false;
174 }
175 auto it = m_records.begin();
176 while (it != m_records.end()) {
177 auto& key = it->first;
178 if (key.size() < prefix.size() || std::search(key.begin(), key.end(), prefix.begin(), prefix.end()) != key.begin()) {
179 it++;
180 continue;
181 }
182 it = m_records.erase(it);
183 }
184 return true;
185 }
186
187 std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(MockableData records)
188 {
189 return std::make_unique<MockableDatabase>(records);
190 }
191
192 MockableDatabase& GetMockableDatabase(CWallet& wallet)
193 {
194 return dynamic_cast<MockableDatabase&>(wallet.GetDatabase());
195 }
196
197 wallet::ScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, const bool success)
198 {
199 keystore.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
200
201 FlatSigningProvider keys;
202 std::string error;
203 auto parsed_descs = Parse(desc_str, keys, error, false);
204 Assert(success == (!parsed_descs.empty()));
205 if (!success) return nullptr;
206 auto& desc = parsed_descs.at(0);
207
208 const int64_t range_start = 0, range_end = 1, next_index = 0, timestamp = 1;
209
210 WalletDescriptor w_desc(std::move(desc), timestamp, range_start, range_end, next_index);
211
212 LOCK(keystore.cs_wallet);
213
214 return Assert(keystore.AddWalletDescriptor(w_desc, keys,/*label=*/"", /*internal=*/false));
215 };
216 } // namespace wallet
217