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.
252// The actual limit, MaxPeerLatencyScore(), may be higher, since TxOrphanage only counts peers253// that have announced an orphan. The honest peer will not experience evictions if it never254// exceeds this.255const unsigned int honest_latency_limit = global_latency_score_limit / num_peers;256// Honest peer will not experience evictions if it never exceeds this.257const int64_t honest_mem_limit = per_peer_weight_reservation;259std::vector<COutPoint> outpoints; // Duplicates are tolerated260outpoints.reserve(400);262// initial outpoints used to construct transactions later263for (uint8_t i = 0; i < 4; i++) {264outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);265}267// These are honest peer's live announcements. We expect them to be protected from eviction.268std::set<Wtxid> protected_wtxids;270LIMITED_WHILE (outpoints.size() < 400 && fuzzed_data_provider.ConsumeBool(), 1000) {271// construct transaction272const CTransactionRef tx = [&] {273CMutableTransaction tx_mut;274const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());275const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);276// pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not277// running any transaction validation logic before adding transactions to the orphanage278tx_mut.vin.reserve(num_in);279for (uint32_t i = 0; i < num_in; i++) {280auto& prevout = PickValue(fuzzed_data_provider, outpoints);281// try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen282tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));283}284// output amount or spendability will not affect txorphanage285tx_mut.vout.reserve(num_out);286for (uint32_t i = 0; i < num_out; i++) {287const auto payload_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 100000);288if (payload_size) {289tx_mut.vout.emplace_back(0, CScript() << OP_RETURN << std::vector<unsigned char>(payload_size));290} else {291tx_mut.vout.emplace_back(0, CScript{});292}293}294auto new_tx = MakeTransactionRef(tx_mut);295// add newly constructed outpoints to the coin pool296for (uint32_t i = 0; i < num_out; i++) {297outpoints.emplace_back(new_tx->GetHash(), i);298}299return new_tx;300}();302const auto wtxid{tx->GetWitnessHash()};304// orphanage functions305LIMITED_WHILE (fuzzed_data_provider.remaining_bytes(), 10 * global_latency_score_limit) {306NodeId peer_id = fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, num_peers - 1);307const auto tx_weight{GetTransactionWeight(*tx)};309// This protected peer will never send orphans that would310// exceed their own personal allotment, so is never evicted.311const bool peer_is_protected{protected_peers[peer_id]};313CallOneOf(314fuzzed_data_provider,315[&] { // AddTx316bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);317if (peer_is_protected && !have_tx_and_peer &&318(orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||319orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {320// We never want our protected peer oversized or over-announced321} else {322orphanage->AddTx(tx, peer_id);323if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {324protected_wtxids.insert(wtxid);325}326}327},328[&] { // AddAnnouncer329bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);330// AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.331{332if (peer_is_protected && !have_tx_and_peer &&333(orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||334orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {335// We never want our protected peer oversized336} else {337orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);338if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {339protected_wtxids.insert(wtxid);340}341}342}343},344[&] { // EraseTx345if (protected_wtxids.contains(tx->GetWitnessHash())) {346protected_wtxids.erase(wtxid);347}348orphanage->EraseTx(wtxid);349Assert(!orphanage->HaveTx(wtxid));350},351[&] { // EraseForPeer352if (!protected_peers[peer_id]) {353orphanage->EraseForPeer(peer_id);354Assert(orphanage->UsageByPeer(peer_id) == 0);355Assert(orphanage->LatencyScoreFromPeer(peer_id) == 0);356Assert(orphanage->AnnouncementsFromPeer(peer_id) == 0);357}358}359);360}361}363orphanage->SanityCheck();364// All of the honest peer's announcements are still present.365for (const auto& wtxid : protected_wtxids) {366Assert(orphanage->HaveTx(wtxid));367}368}370FUZZ_TARGET(txorphanage_sim)371{372SeedRandomStateForTest(SeedRand::ZEROS);373// This is a comprehensive simulation fuzz test, which runs through a scenario involving up to374// 16 transactions (which may have simple or complex topology, and may have duplicate txids375// with distinct wtxids, and up to 16 peers. The scenario is performed both on a real376// TxOrphanage object and the behavior is compared with a naive reimplementation (just a vector377// of announcements) where possible, and tested for desired properties where not possible.379//380// 1. Setup.381//383/** The total number of transactions this simulation uses (not all of which will necessarily384* be present in the orphanage at once). */385static constexpr unsigned NUM_TX = 16;386/** The number of peers this simulation uses (not all of which will necessarily be present in387* the orphanage at once). */388static constexpr unsigned NUM_PEERS = 16;389/** The maximum number of announcements this simulation uses (which may be higher than the390* number permitted inside the orphanage). */391static constexpr unsigned MAX_ANN = 64;393FuzzedDataProvider provider(buffer.data(), buffer.size());394/** Local RNG. Only used for topology/sizes of the transaction set, the order of transactions395* in EraseForBlock, and for the randomized passed to AddChildrenToWorkSet. */396InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());398//399// 2. Construct an interesting set of 16 transactions.400//402// - Pick a topological order among the transactions.403std::vector<unsigned> txorder(NUM_TX);404std::iota(txorder.begin(), txorder.end(), unsigned{0});405std::shuffle(txorder.begin(), txorder.end(), rng);406// - Pick a set of dependencies (pair<child_index, parent_index>).407std::vector<std::pair<unsigned, unsigned>> deps;408deps.reserve((NUM_TX * (NUM_TX - 1)) / 2);409for (unsigned p = 0; p < NUM_TX - 1; ++p) {410for (unsigned c = p + 1; c < NUM_TX; ++c) {411deps.emplace_back(c, p);412}413}414std::shuffle(deps.begin(), deps.end(), rng);415deps.resize(provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * 4 - 1));416// - Construct the actual transactions.417std::set<Wtxid> wtxids;418std::vector<CTransactionRef> txn(NUM_TX);419node::TxOrphanage::Usage total_usage{0};420for (unsigned t = 0; t < NUM_TX; ++t) {421CMutableTransaction tx;422if (t > 0 && rng.randrange(4) == 0) {423// Occasionally duplicate the previous transaction, so that repetitions of the same424// txid are possible (with different wtxid).425tx = CMutableTransaction(*txn[txorder[t - 1]]);426} else {427tx.version = 1;428tx.nLockTime = 0xffffffff;429// Construct 1 to 16 outputs.430auto num_outputs = rng.randrange<unsigned>(1 << rng.randrange<unsigned>(5)) + 1;431for (unsigned output = 0; output < num_outputs; ++output) {432CScript scriptpubkey;433scriptpubkey.resize(provider.ConsumeIntegralInRange<unsigned>(20, 34));434tx.vout.emplace_back(CAmount{0}, std::move(scriptpubkey));435}436// Construct inputs (one for each dependency).437for (auto& [child, parent] : deps) {438if (child == t) {439auto& partx = txn[txorder[parent]];440assert(partx->version == 1);441COutPoint outpoint(partx->GetHash(), rng.randrange<size_t>(partx->vout.size()));442tx.vin.emplace_back(outpoint);443tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));444}445}446// Construct fallback input in case there are no dependencies.447if (tx.vin.empty()) {448COutPoint outpoint(Txid::FromUint256(rng.rand256()), rng.randrange<size_t>(16));449tx.vin.emplace_back(outpoint);450tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));451}452}453// Optionally modify the witness (allowing wtxid != txid), and certainly when the wtxid454// already exists.455while (wtxids.contains(CTransaction(tx).GetWitnessHash()) || rng.randrange(4) == 0) {456auto& input = tx.vin[rng.randrange(tx.vin.size())];457if (rng.randbool()) {458input.scriptWitness.stack.resize(1);459input.scriptWitness.stack[0].resize(rng.randrange(100));460} else {461input.scriptWitness.stack.resize(0);462}463}464// Convert to CTransactionRef.465txn[txorder[t]] = MakeTransactionRef(std::move(tx));466wtxids.insert(txn[txorder[t]]->GetWitnessHash());467auto weight = GetTransactionWeight(*txn[txorder[t]]);468assert(weight < MAX_STANDARD_TX_WEIGHT);469total_usage += GetTransactionWeight(*txn[txorder[t]]);470}472//473// 3. Initialize real orphanage474//476auto max_global_latency_score = provider.ConsumeIntegralInRange<node::TxOrphanage::Count>(NUM_PEERS, MAX_ANN);477auto reserved_peer_usage = provider.ConsumeIntegralInRange<node::TxOrphanage::Usage>(1, total_usage);478auto real = node::MakeTxOrphanage(max_global_latency_score, reserved_peer_usage);480//481// 4. Functions and data structures for the simulation.482//484/** Data structure representing one announcement (pair of (tx, peer), plus whether it's485* reconsiderable or not. */486struct SimAnnouncement487{488unsigned tx;489NodeId announcer;490bool reconsider{false};491SimAnnouncement(unsigned tx_in, NodeId announcer_in, bool reconsider_in) noexcept :492tx(tx_in), announcer(announcer_in), reconsider(reconsider_in) {}493};494/** The entire simulated orphanage is represented by this list of announcements, in495* announcement order (unlike TxOrphanageImpl which uses a sequence number to represent496* announcement order). New announcements are added to the back. */497std::vector<SimAnnouncement> sim_announcements;499/** Consume a transaction (index into txn) from provider. */500auto read_tx_fn = [&]() -> unsigned { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX - 1); };
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.