// 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 #include #include namespace wallet { std::unique_ptr CreateSyncedWallet(interfaces::Chain& chain, ChainstateManager& chainman, const CKey& key) { struct ChainInfo { int height; uint256 tip_hash; uint256 genesis_hash; }; const ChainInfo chain_info{WITH_LOCK(chainman.GetMutex(), return (ChainInfo{ chainman.ActiveChain().Height(), chainman.ActiveChain().Tip()->GetBlockHash(), chainman.ActiveChain().Genesis()->GetBlockHash()}))}; auto wallet = std::make_unique(&chain, "", CreateMockableWalletDatabase()); { LOCK(wallet->cs_wallet); wallet->SetLastBlockProcessed(chain_info.height, chain_info.tip_hash); } wallet->LoadWallet(); { 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(chain_info.genesis_hash, /*start_height=*/0, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/false); assert(result.status == CWallet::ScanResult::SUCCESS); assert(result.last_scanned_block == chain_info.tip_hash); assert(*result.last_scanned_height == chain_info.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) { SyncWithValidationInterfaceQueue(); 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::LEGACY; return EncodeDestination(getNewDestination(w, output_type)); } CTxDestination getNewDestination(CWallet& w, OutputType output_type) { assert(output_type == OutputType::LEGACY); return *Assert(w.GetNewDestination("")); } DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value) { if (!m_pass) { return Status::FAIL; } if (m_cursor == m_cursor_end) { return Status::DONE; } 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.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(std::map records) { return std::make_unique(records); } MockableDatabase& GetMockableDatabase(CWallet& wallet) { return dynamic_cast(wallet.GetDatabase()); } } // namespace wallet