Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251const bool expect_work{txdownloadman.HaveMoreWork(rand_peer)};252const auto ptx = txdownloadman.GetTxToReconsider(rand_peer);253// expect_work=true doesn't necessarily mean the next item from the workset isn't a254// nullptr, as the transaction could have been removed from orphanage without being255// removed from the peer's workset.256if (ptx) {257// However, if there was a non-null tx in the workset, HaveMoreWork should have258// returned true.259Assert(expect_work);260}261});262// Jump forwards or backwards263auto time_skip = fuzzed_data_provider.PickValueInArray(TIME_SKIPS);264if (fuzzed_data_provider.ConsumeBool()) time_skip *= -1;265time += time_skip;266}267// Disconnect everybody, check that all data structures are empty.268for (NodeId nodeid = 0; nodeid < NUM_PEERS; ++nodeid) {269txdownloadman.DisconnectedPeer(nodeid);270txdownloadman.CheckIsEmpty(nodeid);271}272txdownloadman.CheckIsEmpty();273}275// Give node 0 relay permissions, and nobody else. This helps us remember who is a RelayPermissions276// peer without tracking anything (this is only for the txdownload_impl target).277static bool HasRelayPermissions(NodeId peer) { return peer == 0; }279static void CheckInvariants(const node::TxDownloadManagerImpl& txdownload_impl)280{281txdownload_impl.m_orphanage->SanityCheck();282// We should never have more than the maximum in-flight requests out for a peer.283for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {284if (!HasRelayPermissions(peer)) {285Assert(txdownload_impl.m_txrequest.Count(peer) <= node::MAX_PEER_TX_ANNOUNCEMENTS);286}287}288txdownload_impl.m_txrequest.SanityCheck();289}291FUZZ_TARGET(txdownloadman_impl, .init = initialize)292{293SeedRandomStateForTest(SeedRand::ZEROS);294FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());295FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};297// Initialize a TxDownloadManagerImpl298bilingual_str error;299CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};300node::TxDownloadManagerImpl txdownload_impl{node::TxDownloadOptions{.m_mempool = pool, .m_deterministic_txrequest = true}};302std::chrono::microseconds time{244466666};304LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 500) {305NodeId rand_peer = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, NUM_PEERS - 1);307// Transaction can be one of the premade ones or a randomly generated one308auto rand_tx = fuzzed_data_provider.ConsumeBool() ?309MakeTransactionSpending(PickCoins(fuzzed_data_provider),310/*num_outputs=*/fuzzed_data_provider.ConsumeIntegralInRange(1, 500),311/*add_witness=*/fuzzed_data_provider.ConsumeBool()) :312TRANSACTIONS.at(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, TRANSACTIONS.size() - 1));314CallOneOf(315fuzzed_data_provider,316[&] {317node::TxDownloadConnectionInfo info{318.m_preferred = fuzzed_data_provider.ConsumeBool(),319.m_relay_permissions = HasRelayPermissions(rand_peer),320.m_wtxid_relay = fuzzed_data_provider.ConsumeBool()321};322txdownload_impl.ConnectedPeer(rand_peer, info);323},324[&] {325txdownload_impl.DisconnectedPeer(rand_peer);326txdownload_impl.CheckIsEmpty(rand_peer);327},328[&] {329txdownload_impl.ActiveTipChange();330// After a block update, nothing should be in the rejection caches331for (const auto& tx : TRANSACTIONS) {332Assert(!txdownload_impl.RecentRejectsFilter().contains(tx->GetWitnessHash().ToUint256()));333Assert(!txdownload_impl.RecentRejectsFilter().contains(tx->GetHash().ToUint256()));334Assert(!txdownload_impl.RecentRejectsReconsiderableFilter().contains(tx->GetWitnessHash().ToUint256()));335Assert(!txdownload_impl.RecentRejectsReconsiderableFilter().contains(tx->GetHash().ToUint256()));336}337},338[&] {339CBlock block;340block.vtx.push_back(rand_tx);341txdownload_impl.BlockConnected(std::make_shared<CBlock>(block));342// Block transactions must be removed from orphanage343Assert(!txdownload_impl.m_orphanage->HaveTx(rand_tx->GetWitnessHash()));344},345[&] {346txdownload_impl.BlockDisconnected();347Assert(!txdownload_impl.RecentConfirmedTransactionsFilter().contains(rand_tx->GetWitnessHash().ToUint256()));348Assert(!txdownload_impl.RecentConfirmedTransactionsFilter().contains(rand_tx->GetHash().ToUint256()));349},350[&] {351txdownload_impl.MempoolAcceptedTx(rand_tx);352},353[&] {354TxValidationState state;355state.Invalid(fuzzed_data_provider.PickValueInArray(TESTED_TX_RESULTS), "");356bool first_time_failure{fuzzed_data_provider.ConsumeBool()};358bool reject_contains_wtxid{txdownload_impl.RecentRejectsFilter().contains(rand_tx->GetWitnessHash().ToUint256())};360node::RejectedTxTodo todo = txdownload_impl.MempoolRejectedTx(rand_tx, state, rand_peer, first_time_failure);361Assert(first_time_failure || !todo.m_should_add_extra_compact_tx);362if (!reject_contains_wtxid) Assert(todo.m_unique_parents.size() <= rand_tx->vin.size());363},364[&] {365auto gtxid = fuzzed_data_provider.ConsumeBool() ?366GenTxid{rand_tx->GetHash()} :367GenTxid{rand_tx->GetWitnessHash()};368txdownload_impl.AddTxAnnouncement(rand_peer, gtxid, time);369},370[&] {371const auto getdata_requests = txdownload_impl.GetRequestsToSend(rand_peer, time);372// TxDownloadManager should not be telling us to request things we already have.373// Exclude m_lazy_recent_rejects_reconsiderable because it may request low-feerate parent of orphan.374for (const auto& gtxid : getdata_requests) {375Assert(!txdownload_impl.AlreadyHaveTx(gtxid, /*include_reconsiderable=*/false));376}377},378[&] {379const auto& [should_validate, maybe_package] = txdownload_impl.ReceivedTx(rand_peer, rand_tx);380// The only possible results should be:381// - Don't validate the tx, no package.382// - Don't validate the tx, package.383// - Validate the tx, no package.384// The only combination that doesn't make sense is validate both tx and package.385Assert(!(should_validate && maybe_package.has_value()));386if (should_validate) {387Assert(!txdownload_impl.AlreadyHaveTx(rand_tx->GetWitnessHash(), /*include_reconsiderable=*/true));388}389if (maybe_package.has_value()) {390CheckPackageToValidate(*maybe_package, rand_peer);392const auto& package = maybe_package->m_txns;393// Parent is in m_lazy_recent_rejects_reconsiderable and child is in m_orphanage394Assert(txdownload_impl.RecentRejectsReconsiderableFilter().contains(rand_tx->GetWitnessHash().ToUint256()));395Assert(txdownload_impl.m_orphanage->HaveTx(maybe_package->m_txns.back()->GetWitnessHash()));396// Package has not been rejected397Assert(!txdownload_impl.RecentRejectsReconsiderableFilter().contains(GetPackageHash(package)));398// Neither is in m_lazy_recent_rejects399Assert(!txdownload_impl.RecentRejectsFilter().contains(package.front()->GetWitnessHash().ToUint256()));400Assert(!txdownload_impl.RecentRejectsFilter().contains(package.back()->GetWitnessHash().ToUint256()));401}402},403[&] {404txdownload_impl.ReceivedNotFound(rand_peer, {rand_tx->GetWitnessHash()});405},406[&] {407const bool expect_work{txdownload_impl.HaveMoreWork(rand_peer)};408const auto ptx{txdownload_impl.GetTxToReconsider(rand_peer)};409// expect_work=true doesn't necessarily mean the next item from the workset isn't a410// nullptr, as the transaction could have been removed from orphanage without being411// removed from the peer's workset.412if (ptx) {413// However, if there was a non-null tx in the workset, HaveMoreWork should have414// returned true.415Assert(expect_work);416Assert(txdownload_impl.AlreadyHaveTx(ptx->GetWitnessHash(), /*include_reconsiderable=*/false));417// Presumably we have validated this tx. Use "missing inputs" to keep it in the418// orphanage longer. Later iterations might call MempoolAcceptedTx or419// MempoolRejectedTx with a different error.420TxValidationState state_missing_inputs;421state_missing_inputs.Invalid(TxValidationResult::TX_MISSING_INPUTS, "");422txdownload_impl.MempoolRejectedTx(ptx, state_missing_inputs, rand_peer, fuzzed_data_provider.ConsumeBool());423}424});426auto time_skip = fuzzed_data_provider.PickValueInArray(TIME_SKIPS);427if (fuzzed_data_provider.ConsumeBool()) time_skip *= -1;428time += time_skip;429}430CheckInvariants(txdownload_impl);431// Disconnect everybody, check that all data structures are empty.432for (NodeId nodeid = 0; nodeid < NUM_PEERS; ++nodeid) {433txdownload_impl.DisconnectedPeer(nodeid);434txdownload_impl.CheckIsEmpty(nodeid);435}436txdownload_impl.CheckIsEmpty();437}439} // namespace
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.