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.
251}253/** Given a set of Refs (given as a vector of pointers), expand the set to include all its254* ancestors (desc=false) or all its descendants (desc=true) in this graph. */255void IncludeAncDesc(std::vector<TxGraph::Ref*>& arg, bool desc)256{257std::vector<TxGraph::Ref*> ret;258for (auto ptr : arg) {259auto simpos = Find(ptr);260if (simpos != MISSING) {261for (auto i : desc ? graph.Descendants(simpos) : graph.Ancestors(simpos)) {262ret.push_back(simmap[i].get());263}264} else {265ret.push_back(ptr);266}267}268// Construct deduplicated version in input (do not use std::sort/std::unique for269// deduplication as it'd rely on non-deterministic pointer comparison).270arg.clear();271for (auto ptr : ret) {272if (std::find(arg.begin(), arg.end(), ptr) == arg.end()) {273arg.push_back(ptr);274}275}276}279/** Verify that set contains transactions from every oversized cluster, and nothing from280* non-oversized ones. */281bool MatchesOversizedClusters(const SetType& set)282{283if (set.Any() && !IsOversized()) return false;285auto todo = graph.Positions();286if (!set.IsSubsetOf(todo)) return false;288// Walk all clusters, and make sure all of set doesn't come from non-oversized clusters289while (todo.Any()) {290auto component = graph.FindConnectedComponent(todo);291// Determine whether component is oversized, due to either the size or count limit.292bool is_oversized = component.Count() > max_cluster_count;293uint64_t component_size{0};294for (auto i : component) component_size += graph.FeeRate(i).size;295is_oversized |= component_size > max_cluster_size;296// Check whether overlap with set matches is_oversized.297if (is_oversized != set.Overlaps(component)) return false;298todo -= component;299}300return true;301}302};304} // namespace306FUZZ_TARGET(txgraph)307{308// This is a big simulation test for TxGraph, which performs a fuzz-derived sequence of valid309// operations on a TxGraph instance, as well as on a simpler (mostly) reimplementation (see310// SimTxGraph above), comparing the outcome of functions that return a result, and finally311// performing a full comparison between the two.313SeedRandomStateForTest(SeedRand::ZEROS);314FuzzedDataProvider provider(buffer.data(), buffer.size());316/** Internal test RNG, used only for decisions which would require significant amount of data317* to be read from the provider, without realistically impacting test sensitivity, and for318* specialized test cases that are hard to perform more generically. */319InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());321/** Variable used whenever an empty SimTxObject is needed. */322SimTxObject empty_ref;324/** The maximum number of transactions per (non-oversized) cluster we will use in this325* simulation. */326auto max_cluster_count = provider.ConsumeIntegralInRange<DepGraphIndex>(1, MAX_CLUSTER_COUNT_LIMIT);327/** The maximum total size of transactions in a (non-oversized) cluster. */328auto max_cluster_size = provider.ConsumeIntegralInRange<uint64_t>(1, 0x3fffff * MAX_CLUSTER_COUNT_LIMIT);329/** The amount of work to consider a cluster acceptably linearized. */330auto acceptable_cost = provider.ConsumeIntegralInRange<uint64_t>(0, 10000);332/** The set of uint64_t "txid"s that have been assigned before. */333std::set<uint64_t> assigned_txids;335// Construct a real graph, and a vector of simulated graphs (main, and possibly staging).336auto fallback_order = [&](const TxGraph::Ref& a, const TxGraph::Ref& b) noexcept {337uint64_t txid_a = static_cast<const SimTxObject&>(a).m_txid;338uint64_t txid_b = static_cast<const SimTxObject&>(b).m_txid;339assert(assigned_txids.contains(txid_a));340assert(assigned_txids.contains(txid_b));341return txid_a <=> txid_b;342};343auto real = MakeTxGraph(344/*max_cluster_count=*/max_cluster_count,345/*max_cluster_size=*/max_cluster_size,346/*acceptable_cost=*/acceptable_cost,347/*fallback_order=*/fallback_order);349std::vector<SimTxGraph> sims;350sims.reserve(2);351sims.emplace_back(max_cluster_count, max_cluster_size);353/** Struct encapsulating information about a BlockBuilder that's currently live. */354struct BlockBuilderData355{356/** BlockBuilder object from real. */357std::unique_ptr<TxGraph::BlockBuilder> builder;358/** The set of transactions marked as included in *builder. */359SimTxGraph::SetType included;360/** The set of transactions marked as included or skipped in *builder. */361SimTxGraph::SetType done;362/** The last chunk feerate returned by *builder. IsEmpty() if none yet. */363FeePerWeight last_feerate;365BlockBuilderData(std::unique_ptr<TxGraph::BlockBuilder> builder_in) : builder(std::move(builder_in)) {}366};368/** Currently active block builders. */369std::vector<BlockBuilderData> block_builders;371/** Function to pick any SimTxObject (for either sim in sims: from sim.simmap or sim.removed, or the372* empty one). */373auto pick_fn = [&]() noexcept -> SimTxObject* {374size_t tx_count[2] = {sims[0].GetTransactionCount(), 0};375/** The number of possible choices. */376size_t choices = tx_count[0] + sims[0].removed.size() + 1;377if (sims.size() == 2) {378tx_count[1] = sims[1].GetTransactionCount();379choices += tx_count[1] + sims[1].removed.size();380}381/** Pick one of them. */382auto choice = provider.ConsumeIntegralInRange<size_t>(0, choices - 1);383// Consider both main and (if it exists) staging.384for (size_t level = 0; level < sims.size(); ++level) {385auto& sim = sims[level];386if (choice < tx_count[level]) {387// Return from graph.388for (auto i : sim.graph.Positions()) {389if (choice == 0) return sim.GetRef(i);390--choice;391}392assert(false);393} else {394choice -= tx_count[level];395}396if (choice < sim.removed.size()) {397// Return from removed.398return sim.removed[choice].get();399} else {400choice -= sim.removed.size();401}402}403// Return empty.404assert(choice == 0);405return &empty_ref;406};408/** Function to construct the correct fee-size diagram a real graph has based on its graph409* order (as reported by GetCluster(), so it works for both main and staging). */410auto get_diagram_fn = [&](TxGraph::Level level_select) -> std::vector<FeeFrac> {411int level = level_select == TxGraph::Level::MAIN ? 0 : sims.size() - 1;412auto& sim = sims[level];413// For every transaction in the graph, request its cluster, and throw them into a set.414std::set<std::vector<TxGraph::Ref*>> clusters;415for (auto i : sim.graph.Positions()) {416auto ref = sim.GetRef(i);417clusters.insert(real->GetCluster(*ref, level_select));418}419// Compute the chunkings of each (deduplicated) cluster.420size_t num_tx{0};421std::vector<FeeFrac> chunk_feerates;422for (const auto& cluster : clusters) {423num_tx += cluster.size();424std::vector<SimTxGraph::Pos> linearization;425linearization.reserve(cluster.size());426for (auto refptr : cluster) linearization.push_back(sim.Find(refptr));427for (const FeeFrac& chunk_feerate : ChunkLinearization(sim.graph, linearization)) {428chunk_feerates.push_back(chunk_feerate);429}430}431// Verify the number of transactions after deduplicating clusters. This implicitly verifies432// that GetCluster on each element of a cluster reports the cluster transactions in the same433// order.434assert(num_tx == sim.GetTransactionCount());435// Sort by feerate only, since violating topological constraints within same-feerate436// chunks won't affect diagram comparisons.437std::ranges::sort(chunk_feerates, std::greater<ByRatioNegSize<FeeFrac>>{});438return chunk_feerates;439};441LIMITED_WHILE (provider.remaining_bytes() > 0, 200) {442// Read a one-byte command.443int command = provider.ConsumeIntegral<uint8_t>();444int orig_command = command;446// Treat the lowest bit of a command as a flag (which selects a variant of some of the447// operations), and the second-lowest bit as a way of selecting main vs. staging, and leave448// the rest of the bits in command.449bool alt = command & 1;450TxGraph::Level level_select = (command & 2) ? TxGraph::Level::MAIN : TxGraph::Level::TOP;451command >>= 2;453/** Use the bottom 2 bits of command to select an entry in the block_builders vector (if454* any). These use the same bits as alt/level_select, so don't use those in actions below455* where builder_idx is used as well. */456int builder_idx = block_builders.empty() ? -1 : int((orig_command & 3) % block_builders.size());458// Provide convenient aliases for the top simulated graph (main, or staging if it exists),459// one for the simulated graph selected based on level_select (for operations that can operate460// on both graphs), and one that always refers to the main graph.461auto& top_sim = sims.back();462auto& sel_sim = level_select == TxGraph::Level::MAIN ? sims[0] : top_sim;463auto& main_sim = sims[0];465// Keep decrementing command for each applicable operation, until one is hit. Multiple466// iterations may be necessary.467while (true) {468if ((block_builders.empty() || sims.size() > 1) && top_sim.GetTransactionCount() < SimTxGraph::MAX_TRANSACTIONS && command-- == 0) {469// AddTransaction.470int64_t fee;471int32_t size;472if (alt) {473// If alt is true, pick fee and size from the entire range.474fee = provider.ConsumeIntegralInRange<int64_t>(-0x8000000000000, 0x7ffffffffffff);475size = provider.ConsumeIntegralInRange<int32_t>(1, 0x3fffff);476} else {477// Otherwise, use smaller range which consume fewer fuzz input bytes, as just478// these are likely sufficient to trigger all interesting code paths already.479fee = provider.ConsumeIntegral<uint8_t>();480size = provider.ConsumeIntegralInRange<uint32_t>(1, 0xff);481}482FeePerWeight feerate{fee, size};483// Pick a novel txid (and not 0, which is reserved for empty_ref).484uint64_t txid;485do {486txid = rng.rand64();487} while (txid == 0 || assigned_txids.contains(txid));488assigned_txids.insert(txid);489// Create the transaction in the simulation and the real graph.490top_sim.AddTransaction(*real, feerate, txid);491break;492} else if ((block_builders.empty() || sims.size() > 1) && top_sim.GetTransactionCount() + top_sim.removed.size() > 1 && command-- == 0) {493// AddDependency.494auto par = pick_fn();495auto chl = pick_fn();496auto pos_par = top_sim.Find(par);497auto pos_chl = top_sim.Find(chl);498if (pos_par != SimTxGraph::MISSING && pos_chl != SimTxGraph::MISSING) {499// Determine if adding this would introduce a cycle (not allowed by TxGraph),500// and if so, skip.
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.