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.
501/** Consume a NodeId from provider. */502auto read_peer_fn = [&]() -> NodeId { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_PEERS - 1); };503/** Consume both a transaction (index into txn) and a NodeId from provider. */504auto read_tx_peer_fn = [&]() -> std::pair<unsigned, NodeId> {505auto code = provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * NUM_PEERS - 1);506return {code % NUM_TX, code / NUM_TX};507};508/** Determine if we have any announcements of the given transaction in the simulation. */509auto have_tx_fn = [&](unsigned tx) -> bool {510for (auto& ann : sim_announcements) {511if (ann.tx == tx) return true;512}513return false;514};515/** Count the number of peers in the simulation. */516auto count_peers_fn = [&]() -> unsigned {517std::bitset<NUM_PEERS> mask;518for (auto& ann : sim_announcements) {519mask.set(ann.announcer);520}521return mask.count();522};523/** Determine if we have any reconsiderable announcements of a given transaction. */524auto have_reconsiderable_fn = [&](unsigned tx) -> bool {525for (auto& ann : sim_announcements) {526if (ann.reconsider && ann.tx == tx) return true;527}528return false;529};530/** Determine if a peer has any transactions to reconsider. */531auto have_reconsider_fn = [&](NodeId peer) -> bool {532for (auto& ann : sim_announcements) {533if (ann.reconsider && ann.announcer == peer) return true;534}535return false;536};537/** Get an iterator to an existing (wtxid, peer) pair in the simulation. */538auto find_announce_wtxid_fn = [&](const Wtxid& wtxid, NodeId peer) -> std::vector<SimAnnouncement>::iterator {539for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {540if (txn[it->tx]->GetWitnessHash() == wtxid && it->announcer == peer) return it;541}542return sim_announcements.end();543};544/** Get an iterator to an existing (tx, peer) pair in the simulation. */545auto find_announce_fn = [&](unsigned tx, NodeId peer) {546for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {547if (it->tx == tx && it->announcer == peer) return it;548}549return sim_announcements.end();550};551/** Compute a peer's DoS score according to simulation data. */552auto dos_score_fn = [&](NodeId peer, int32_t max_count, int32_t max_usage) -> FeeFrac {553int64_t count{0};554int64_t usage{0};555for (auto& ann : sim_announcements) {556if (ann.announcer != peer) continue;557count += 1 + (txn[ann.tx]->vin.size() / 10);558usage += GetTransactionWeight(*txn[ann.tx]);559}560return std::max<ByRatioNegSize<FeeFrac>>(FeeFrac{count, max_count}, FeeFrac{usage, max_usage});561};563//564// 5. Run through a scenario of mutators on both real and simulated orphanage.565//567LIMITED_WHILE (provider.remaining_bytes() > 0, 200) {568int command = provider.ConsumeIntegralInRange<uint8_t>(0, 15);569while (true) {570if (sim_announcements.size() < MAX_ANN && command-- == 0) {571// AddTx572auto [tx, peer] = read_tx_peer_fn();573bool added = real->AddTx(txn[tx], peer);574bool sim_have_tx = have_tx_fn(tx);575assert(added == !sim_have_tx);576if (find_announce_fn(tx, peer) == sim_announcements.end()) {577sim_announcements.emplace_back(tx, peer, false);578}579break;580} else if (sim_announcements.size() < MAX_ANN && command-- == 0) {581// AddAnnouncer582auto [tx, peer] = read_tx_peer_fn();583bool added = real->AddAnnouncer(txn[tx]->GetWitnessHash(), peer);584bool sim_have_tx = have_tx_fn(tx);585auto sim_it = find_announce_fn(tx, peer);586assert(added == (sim_it == sim_announcements.end() && sim_have_tx));587if (added) {588sim_announcements.emplace_back(tx, peer, false);589}590break;591} else if (command-- == 0) {592// EraseTx593auto tx = read_tx_fn();594bool erased = real->EraseTx(txn[tx]->GetWitnessHash());595bool sim_have = have_tx_fn(tx);596assert(erased == sim_have);597std::erase_if(sim_announcements, [&](auto& ann) { return ann.tx == tx; });598break;599} else if (command-- == 0) {600// EraseForPeer601auto peer = read_peer_fn();602real->EraseForPeer(peer);603std::erase_if(sim_announcements, [&](auto& ann) { return ann.announcer == peer; });604break;605} else if (command-- == 0) {606// EraseForBlock607auto pattern = provider.ConsumeIntegralInRange<uint64_t>(0, (uint64_t{1} << NUM_TX) - 1);608CBlock block;609std::set<COutPoint> spent;610for (unsigned tx = 0; tx < NUM_TX; ++tx) {611if ((pattern >> tx) & 1) {612block.vtx.emplace_back(txn[tx]);613for (auto& txin : block.vtx.back()->vin) {614spent.insert(txin.prevout);615}616}617}618std::shuffle(block.vtx.begin(), block.vtx.end(), rng);619real->EraseForBlock(block);620std::erase_if(sim_announcements, [&](auto& ann) {621for (auto& txin : txn[ann.tx]->vin) {622if (spent.contains(txin.prevout)) return true;623}624return false;625});626break;627} else if (command-- == 0) {628// AddChildrenToWorkSet629auto tx = read_tx_fn();630FastRandomContext rand_ctx(rng.rand256());631auto added = real->AddChildrenToWorkSet(*txn[tx], rand_ctx);632/** Set of not-already-reconsiderable child wtxids. */633std::set<Wtxid> child_wtxids;634for (unsigned child_tx = 0; child_tx < NUM_TX; ++child_tx) {635if (!have_tx_fn(child_tx)) continue;636if (have_reconsiderable_fn(child_tx)) continue;637bool child_of = false;638for (auto& txin : txn[child_tx]->vin) {639if (txin.prevout.hash == txn[tx]->GetHash()) {640child_of = true;641break;642}643}644if (child_of) {645child_wtxids.insert(txn[child_tx]->GetWitnessHash());646}647}648for (auto& [wtxid, peer] : added) {649// Wtxid must be a child of tx that is not yet reconsiderable.650auto child_wtxid_it = child_wtxids.find(wtxid);651assert(child_wtxid_it != child_wtxids.end());652// Announcement must exist.653auto sim_ann_it = find_announce_wtxid_fn(wtxid, peer);654assert(sim_ann_it != sim_announcements.end());655// Announcement must not yet be reconsiderable.656assert(sim_ann_it->reconsider == false);657// Make reconsiderable.658sim_ann_it->reconsider = true;659// Remove from child_wtxids map, to disallow it being reported a second time in added.660child_wtxids.erase(wtxid);661}662// Verify that AddChildrenToWorkSet does not select announcements that were already reconsiderable:663// Check all child wtxids which did not occur at least once in the result were already reconsiderable664// due to a previous AddChildrenToWorkSet.665assert(child_wtxids.empty());666break;667} else if (command-- == 0) {668// GetTxToReconsider.669auto peer = read_peer_fn();670auto result = real->GetTxToReconsider(peer);671if (result) {672// A transaction was found. It must have a corresponding reconsiderable673// announcement from peer.674auto sim_ann_it = find_announce_wtxid_fn(result->GetWitnessHash(), peer);675assert(sim_ann_it != sim_announcements.end());676assert(sim_ann_it->announcer == peer);677assert(sim_ann_it->reconsider);678// Make it non-reconsiderable.679sim_ann_it->reconsider = false;680} else {681// No reconsiderable transaction was found from peer. Verify that it does not682// have any.683assert(!have_reconsider_fn(peer));684}685break;686}687}688// Always trim after each command if needed.689const auto max_ann = max_global_latency_score / std::max<unsigned>(1, count_peers_fn());690const auto max_mem = reserved_peer_usage;691while (true) {692// Count global usage and number of peers.693node::TxOrphanage::Usage total_usage{0};694node::TxOrphanage::Count total_latency_score = sim_announcements.size();695for (unsigned tx = 0; tx < NUM_TX; ++tx) {696if (have_tx_fn(tx)) {697total_usage += GetTransactionWeight(*txn[tx]);698total_latency_score += txn[tx]->vin.size() / 10;699}700}701auto num_peers = count_peers_fn();702bool oversized = (total_usage > reserved_peer_usage * num_peers) ||703(total_latency_score > real->MaxGlobalLatencyScore());704if (!oversized) break;705// Find worst peer.706FeeFrac worst_dos_score{0, 1};707unsigned worst_peer = unsigned(-1);708for (unsigned peer = 0; peer < NUM_PEERS; ++peer) {709auto dos_score = dos_score_fn(peer, max_ann, max_mem);710// Use >= so that the more recent peer (higher NodeId) wins in case of711// ties.712if (ByRatioNegSize{dos_score} >= ByRatioNegSize{worst_dos_score}) {713worst_dos_score = dos_score;714worst_peer = peer;715}716}717assert(worst_peer != unsigned(-1));718assert(ByRatio{worst_dos_score} > ByRatio{FeeFrac(1, 1)});719// Find oldest announcement from worst_peer, preferring non-reconsiderable ones.720bool done{false};721for (int reconsider = 0; reconsider < 2; ++reconsider) {722for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {723if (it->announcer != worst_peer || it->reconsider != reconsider) continue;724sim_announcements.erase(it);725done = true;726break;727}728if (done) break;729}730assert(done);731}732// We must now be within limits, otherwise LimitOrphans should have continued further.733// We don't check the contents of the orphanage until the end to make fuzz runs faster.734assert(real->TotalLatencyScore() <= real->MaxGlobalLatencyScore());735assert(real->TotalOrphanUsage() <= real->MaxGlobalUsage());736}738//739// 6. Perform a full comparison between the real orphanage's inspectors and the simulation.740//742real->SanityCheck();745auto all_orphans = real->GetOrphanTransactions();746node::TxOrphanage::Usage orphan_usage{0};747std::vector<node::TxOrphanage::Usage> usage_by_peer(NUM_PEERS);748node::TxOrphanage::Count unique_orphans{0};749std::vector<node::TxOrphanage::Count> count_by_peer(NUM_PEERS);750node::TxOrphanage::Count total_latency_score = sim_announcements.size();
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.