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.
1// Copyright (c) The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <cluster_linearize.h>6#include <test/fuzz/FuzzedDataProvider.h>7#include <test/fuzz/fuzz.h>8#include <test/util/cluster_linearize.h>9#include <test/util/random.h>10#include <txgraph.h>11#include <util/bitset.h>12#include <util/feefrac.h>14#include <algorithm>15#include <cstdint>16#include <iterator>17#include <map>18#include <memory>19#include <ranges>20#include <set>21#include <utility>23using namespace cluster_linearize;25namespace {27struct SimTxObject : public TxGraph::Ref28{29// Use random uint64_t as txids for this simulation (0 = empty object).30const uint64_t m_txid{0};31SimTxObject() noexcept = default;32explicit SimTxObject(uint64_t txid) noexcept : m_txid(txid) {}33};35/** Data type representing a naive simulated TxGraph, keeping all transactions (even from36* disconnected components) in a single DepGraph. Unlike the real TxGraph, this only models37* a single graph, and multiple instances are used to simulate main/staging. */38struct SimTxGraph39{40/** Maximum number of transactions to support simultaneously. Set this higher than txgraph's41* cluster count, so we can exercise situations with more transactions than fit in one42* cluster. */43static constexpr unsigned MAX_TRANSACTIONS = MAX_CLUSTER_COUNT_LIMIT * 2;44/** Set type to use in the simulation. */45using SetType = BitSet<MAX_TRANSACTIONS>;46/** Data type for representing positions within SimTxGraph::graph. */47using Pos = DepGraphIndex;48/** Constant to mean "missing in this graph". */49static constexpr auto MISSING = Pos(-1);51/** The dependency graph (for all transactions in the simulation, regardless of52* connectivity/clustering). */53DepGraph<SetType> graph;54/** For each position in graph, which SimTxObject it corresponds with (if any). Use shared_ptr55* so that a SimTxGraph can be copied to create a staging one, while sharing Refs with56* the main graph. */57std::array<std::shared_ptr<SimTxObject>, MAX_TRANSACTIONS> simmap;58/** For each TxGraph::Ref in graph, the position it corresponds with. */59std::map<const TxGraph::Ref*, Pos> simrevmap;60/** The set of SimTxObject entries that have been removed, but not yet destroyed. */61std::vector<std::shared_ptr<SimTxObject>> removed;62/** Whether the graph is oversized (true = yes, false = no, std::nullopt = unknown). */63std::optional<bool> oversized;64/** The configured maximum number of transactions per cluster. */65DepGraphIndex max_cluster_count;66/** Which transactions have been modified in the graph since creation, either directly or by67* being in a cluster which includes modifications. Only relevant for the staging graph. */68SetType modified;69/** The configured maximum total size of transactions per cluster. */70uint64_t max_cluster_size;71/** Whether the corresponding real graph is known to be optimally linearized. */72bool real_is_optimal{false};74/** Construct a new SimTxGraph with the specified maximum cluster count and size. */75explicit SimTxGraph(DepGraphIndex cluster_count, uint64_t cluster_size) :76max_cluster_count(cluster_count), max_cluster_size(cluster_size) {}78// Permit copying and moving.79SimTxGraph(const SimTxGraph&) noexcept = default;80SimTxGraph& operator=(const SimTxGraph&) noexcept = default;81SimTxGraph(SimTxGraph&&) noexcept = default;82SimTxGraph& operator=(SimTxGraph&&) noexcept = default;84/** Get the connected components within this simulated transaction graph. */85std::vector<SetType> GetComponents()86{87auto todo = graph.Positions();88std::vector<SetType> ret;89// Iterate over all connected components of the graph.90while (todo.Any()) {91auto component = graph.FindConnectedComponent(todo);92ret.push_back(component);93todo -= component;94}95return ret;96}98/** Check whether this graph is oversized (contains a connected component whose number of99* transactions exceeds max_cluster_count. */100bool IsOversized()101{102if (!oversized.has_value()) {103// Only recompute when oversized isn't already known.104oversized = false;105for (auto component : GetComponents()) {106if (component.Count() > max_cluster_count) oversized = true;107uint64_t component_size{0};108for (auto i : component) component_size += graph.FeeRate(i).size;109if (component_size > max_cluster_size) oversized = true;110}111}112return *oversized;113}115void MakeModified(DepGraphIndex index)116{117modified |= graph.GetConnectedComponent(graph.Positions(), index);118}120/** Determine the number of (non-removed) transactions in the graph. */121DepGraphIndex GetTransactionCount() const { return graph.TxCount(); }123/** Get the sum of all fees/sizes in the graph. */124FeePerWeight SumAll() const125{126FeePerWeight ret;127for (auto i : graph.Positions()) {128ret += graph.FeeRate(i);129}130return ret;131}133/** Get the position where ref occurs in this simulated graph, or -1 if it does not. */134Pos Find(const TxGraph::Ref* ref) const135{136auto it = simrevmap.find(ref);137if (it != simrevmap.end()) return it->second;138return MISSING;139}141/** Given a position in this simulated graph, get the corresponding SimTxObject. */142SimTxObject* GetRef(Pos pos)143{144assert(graph.Positions()[pos]);145assert(simmap[pos]);146return simmap[pos].get();147}149/** Add a new transaction to the simulation and the specified real graph. */150void AddTransaction(TxGraph& txgraph, const FeePerWeight& feerate, uint64_t txid)151{152assert(graph.TxCount() < MAX_TRANSACTIONS);153auto simpos = graph.AddTransaction(feerate);154real_is_optimal = false;155MakeModified(simpos);156assert(graph.Positions()[simpos]);157simmap[simpos] = std::make_shared<SimTxObject>(txid);158txgraph.AddTransaction(*simmap[simpos], feerate);159auto ptr = simmap[simpos].get();160simrevmap[ptr] = simpos;161// This may invalidate our cached oversized value.162if (oversized.has_value() && !*oversized) oversized = std::nullopt;163}165/** Add a dependency between two positions in this graph. */166void AddDependency(TxGraph::Ref* parent, TxGraph::Ref* child)167{168auto par_pos = Find(parent);169if (par_pos == MISSING) return;170auto chl_pos = Find(child);171if (chl_pos == MISSING) return;172graph.AddDependencies(SetType::Singleton(par_pos), chl_pos);173MakeModified(par_pos);174real_is_optimal = false;175// This may invalidate our cached oversized value.176if (oversized.has_value() && !*oversized) oversized = std::nullopt;177}179/** Modify the transaction fee of a ref, if it exists. */180void SetTransactionFee(TxGraph::Ref* ref, int64_t fee)181{182auto pos = Find(ref);183if (pos == MISSING) return;184// No need to invoke MakeModified, because this equally affects main and staging.185real_is_optimal = false;186graph.FeeRate(pos).fee = fee;187}189/** Remove the transaction in the specified position from the graph. */190void RemoveTransaction(TxGraph::Ref* ref)191{192auto pos = Find(ref);193if (pos == MISSING) return;194MakeModified(pos);195real_is_optimal = false;196graph.RemoveTransactions(SetType::Singleton(pos));197simrevmap.erase(simmap[pos].get());198// Retain the TxGraph::Ref corresponding to this position, so the Ref destruction isn't199// invoked until the simulation explicitly decided to do so.200removed.push_back(std::move(simmap[pos]));201simmap[pos].reset();202// This may invalidate our cached oversized value.203if (oversized.has_value() && *oversized) oversized = std::nullopt;204}206/** Destroy the transaction from the graph, including from the removed set. This will207* trigger TxGraph::Ref::~Ref. reset_oversize controls whether the cached oversized208* value is cleared (destroying does not clear oversizedness in TxGraph of the main209* graph while staging exists). */210void DestroyTransaction(TxGraph::Ref* ref, bool reset_oversize)211{212auto pos = Find(ref);213if (pos == MISSING) {214// Wipe the ref, if it exists, from the removed vector. Use std::partition rather215// than std::erase because we don't care about the order of the entries that216// remain.217auto remove = std::partition(removed.begin(), removed.end(), [&](auto& arg) { return arg.get() != ref; });218removed.erase(remove, removed.end());219} else {220MakeModified(pos);221graph.RemoveTransactions(SetType::Singleton(pos));222real_is_optimal = false;223simrevmap.erase(simmap[pos].get());224simmap[pos].reset();225// This may invalidate our cached oversized value.226if (reset_oversize && oversized.has_value() && *oversized) {227oversized = std::nullopt;228}229}230}232/** Construct the set with all positions in this graph corresponding to the specified233* TxGraph::Refs. All of them must occur in this graph and not be removed. */234SetType MakeSet(std::span<TxGraph::Ref* const> arg)235{236SetType ret;237for (TxGraph::Ref* ptr : arg) {238auto pos = Find(ptr);239assert(pos != Pos(-1));240ret.Set(pos);241}242return ret;243}245/** Get the set of ancestors (desc=false) or descendants (desc=true) in this graph. */246SetType GetAncDesc(TxGraph::Ref* arg, bool desc)247{248auto pos = Find(arg);249if (pos == MISSING) return {};250return desc ? graph.Descendants(pos) : graph.Ancestors(pos);
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.