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