// Copyright (c) 2021-2022 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 #include #include #include #include #include #include #include #include #include #include namespace wallet { std::unique_ptr CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key) { auto wallet = std::make_unique(&chain, "", CreateMockableWalletDatabase()); { LOCK2(wallet->cs_wallet, ::cs_main); wallet->SetLastBlockProcessed(cchain.Height(), cchain.Tip()->GetBlockHash()); } { LOCK(wallet->cs_wallet); wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS); wallet->SetupDescriptorScriptPubKeyMans(); FlatSigningProvider provider; std::string error; std::unique_ptr desc = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false); assert(desc); WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1); if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false); } WalletRescanReserver reserver(*wallet); reserver.reserve(); CWallet::ScanResult result = wallet->ScanForWalletTransactions(cchain.Genesis()->GetBlockHash(), /*start_height=*/0, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/false); assert(result.status == CWallet::ScanResult::SUCCESS); assert(result.last_scanned_block == cchain.Tip()->GetBlockHash()); assert(*result.last_scanned_height == cchain.Height()); assert(result.last_failed_block.IsNull()); return wallet; } std::shared_ptr TestLoadWallet(std::unique_ptr database, WalletContext& context, uint64_t create_flags) { bilingual_str error; std::vector warnings; auto wallet = CWallet::Create(context, "", std::move(database), create_flags, error, warnings); NotifyWalletLoaded(context, wallet); if (context.chain) { wallet->postInitProcess(); } return wallet; } std::shared_ptr TestLoadWallet(WalletContext& context) { DatabaseOptions options; options.create_flags = WALLET_FLAG_DESCRIPTORS; DatabaseStatus status; bilingual_str error; std::vector warnings; auto database = MakeWalletDatabase("", options, status, error); return TestLoadWallet(std::move(database), context, options.create_flags); } void TestUnloadWallet(std::shared_ptr&& wallet) { // Calls SyncWithValidationInterfaceQueue wallet->chain().waitForNotificationsIfTipChanged({}); wallet->m_chain_notifications_handler.reset(); UnloadWallet(std::move(wallet)); } std::unique_ptr DuplicateMockDatabase(WalletDatabase& database) { return std::make_unique(dynamic_cast(database).m_records); } std::string getnewaddress(CWallet& w) { constexpr auto output_type = OutputType::BECH32; return EncodeDestination(getNewDestination(w, output_type)); } CTxDestination getNewDestination(CWallet& w, OutputType output_type) { return *Assert(w.GetNewDestination(output_type, "")); } // BytePrefix compares equality with other byte spans that begin with the same prefix. struct BytePrefix { Span prefix; }; bool operator<(BytePrefix a, Span b) { return a.prefix < b.subspan(0, std::min(a.prefix.size(), b.size())); } bool operator<(Span a, BytePrefix b) { return a.subspan(0, std::min(a.size(), b.prefix.size())) < b.prefix; } MockableCursor::MockableCursor(const MockableData& records, bool pass, Span prefix) { m_pass = pass; std::tie(m_cursor, m_cursor_end) = records.equal_range(BytePrefix{prefix}); } DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value) { if (!m_pass) { return Status::FAIL; } if (m_cursor == m_cursor_end) { return Status::DONE; } key.clear(); value.clear(); const auto& [key_data, value_data] = *m_cursor; key.write(key_data); value.write(value_data); m_cursor++; return Status::MORE; } bool MockableBatch::ReadKey(DataStream&& key, DataStream& value) { if (!m_pass) { return false; } SerializeData key_data{key.begin(), key.end()}; const auto& it = m_records.find(key_data); if (it == m_records.end()) { return false; } value.clear(); value.write(it->second); return true; } bool MockableBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite) { if (!m_pass) { return false; } SerializeData key_data{key.begin(), key.end()}; SerializeData value_data{value.begin(), value.end()}; auto [it, inserted] = m_records.emplace(key_data, value_data); if (!inserted && overwrite) { // Overwrite if requested it->second = value_data; inserted = true; } return inserted; } bool MockableBatch::EraseKey(DataStream&& key) { if (!m_pass) { return false; } SerializeData key_data{key.begin(), key.end()}; m_records.erase(key_data); return true; } bool MockableBatch::HasKey(DataStream&& key) { if (!m_pass) { return false; } SerializeData key_data{key.begin(), key.end()}; return m_records.count(key_data) > 0; } bool MockableBatch::ErasePrefix(Span prefix) { if (!m_pass) { return false; } auto it = m_records.begin(); while (it != m_records.end()) { auto& key = it->first; if (key.size() < prefix.size() || std::search(key.begin(), key.end(), prefix.begin(), prefix.end()) != key.begin()) { it++; continue; } it = m_records.erase(it); } return true; } std::unique_ptr CreateMockableWalletDatabase(MockableData records) { return std::make_unique(records); } MockableDatabase& GetMockableDatabase(CWallet& wallet) { return dynamic_cast(wallet.GetDatabase()); } } // namespace wallet