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 <compare>6#include <cstdint>7#include <functional>8#include <memory>9#include <optional>10#include <utility>11#include <vector>13#include <util/feefrac.h>15#ifndef BITCOIN_TXGRAPH_H16#define BITCOIN_TXGRAPH_H18inline constexpr unsigned MAX_CLUSTER_COUNT_LIMIT{64};20/** Data structure to encapsulate fees, sizes, and dependencies for a set of transactions.21*22* Each TxGraph represents one or two such graphs ("main", and optionally "staging"), to allow for23* working with batches of changes that may still be discarded.24*25* The connected components within each transaction graph are called clusters: whenever one26* transaction is reachable from another, through any sequence of is-parent-of or is-child-of27* relations, they belong to the same cluster (so clusters include parents, children, but also28* grandparents, siblings, cousins twice removed, ...).29*30* For each graph, TxGraph implicitly defines an associated total ordering on its transactions31* (its linearization) that respects topology (parents go before their children), aiming for it to32* be close to the optimal order those transactions should be mined in if the goal is fee33* maximization, though this is a best effort only, not a strong guarantee.34*35* For more explanation, see https://delvingbitcoin.org/t/introduction-to-cluster-linearization/103236*37* This linearization is partitioned into chunks: groups of transactions that according to this38* order would be mined together. Each chunk consists of the highest-feerate prefix of what remains39* of the linearization after removing previous chunks. TxGraph guarantees that the maintained40* linearization always results in chunks consisting of transactions that are connected. A chunk's41* transactions always belong to the same cluster.42*43* The interface is designed to accommodate an implementation that only stores the transitive44* closure of dependencies, so if B spends C, it does not distinguish between "A spending B" and45* "A spending both B and C".46*/47class TxGraph48{49public:50/** Internal identifier for a transaction within a TxGraph. */51using GraphIndex = uint32_t;53/** Data type used to reference transactions within a TxGraph.54*55* Every transaction within a TxGraph has exactly one corresponding TxGraph::Ref, held by users56* of the class. Destroying the TxGraph::Ref removes the corresponding transaction (in both the57* main and staging graphs).58*59* Users of the class can inherit from TxGraph::Ref. If all Refs are inherited this way, the60* Ref* pointers returned by TxGraph functions can be cast to, and used as, this inherited type.61*/62class Ref;64enum class Level {65TOP, //!< Refers to staging if it exists, main otherwise.66MAIN //!< Always refers to the main graph, whether staging is present or not.67};69/** Virtual destructor, so inheriting is safe. */70virtual ~TxGraph() = default;71/** Initialize arg (which must be an empty Ref) to refer to a new transaction in this graph72* with the specified feerate.73*74* If a staging graph exists, the new transaction is only created there. feerate.size must be75* strictly positive. In all further calls, only Refs passed to AddTransaction() are allowed76* to be passed to this TxGraph object (or empty Ref objects). Ref objects may outlive the77* TxGraph they were added to. */78virtual void AddTransaction(Ref& arg, const FeePerWeight& feerate) noexcept = 0;79/** Remove the specified transaction. If a staging graph exists, the removal only happens80* there. This is a no-op if the transaction was already removed.81*82* TxGraph may internally reorder transaction removals with dependency additions for83* performance reasons. If together with any transaction removal all its descendants, or all84* its ancestors, are removed as well (which is what always happens in realistic scenarios),85* this reordering will not affect the behavior of TxGraph.86*87* As an example, imagine 3 transactions A,B,C where B depends on A. If a dependency of C on B88* is added, and then B is deleted, C will still depend on A. If the deletion of B is reordered89* before the C->B dependency is added, the dependency adding has no effect. If, together with90* the deletion of B also either A or C is deleted, there is no distinction between the91* original order case and the reordered case.92*/93virtual void RemoveTransaction(const Ref& arg) noexcept = 0;94/** Add a dependency between two specified transactions. If a staging graph exists, the95* dependency is only added there. Parent may not be a descendant of child already (but may96* be an ancestor of it already, in which case this is a no-op). If either transaction is97* already removed, this is a no-op. */98virtual void AddDependency(const Ref& parent, const Ref& child) noexcept = 0;99/** Modify the fee of the specified transaction, in both the main graph and the staging100* graph if it exists. Wherever the transaction does not exist (or was removed), this has no101* effect. */102virtual void SetTransactionFee(const Ref& arg, int64_t fee) noexcept = 0;104/** TxGraph is internally lazy, and will not compute many things until they are needed.105* Calling DoWork will perform some work now (controlled by max_cost) so that future operations106* are fast, if there is any. Returns whether all currently-available work is done. This can107* be invoked while oversized, but oversized graphs will be skipped by this call. */108virtual bool DoWork(uint64_t max_cost) noexcept = 0;110/** Create a staging graph (which cannot exist already). This acts as if a full copy of111* the transaction graph is made, upon which further modifications are made. This copy can112* be inspected, and then either discarded, or the main graph can be replaced by it by113* committing it. */114virtual void StartStaging() noexcept = 0;115/** Discard the existing active staging graph (which must exist). */116virtual void AbortStaging() noexcept = 0;117/** Replace the main graph with the staging graph (which must exist). */118virtual void CommitStaging() noexcept = 0;119/** Check whether a staging graph exists. */120virtual bool HaveStaging() const noexcept = 0;122/** Determine whether the graph is oversized (contains a connected component of more than the123* configured maximum cluster count). Some of the functions below are not available124* for oversized graphs. The mutators above are always available. Removing a transaction by125* destroying its Ref while staging exists will not clear main's oversizedness until staging126* is aborted or committed. */127virtual bool IsOversized(Level level) noexcept = 0;128/** Determine whether arg exists in the graph (i.e., was not removed). This is129* available even for oversized graphs. */130virtual bool Exists(const Ref& arg, Level level) noexcept = 0;131/** Get the individual transaction feerate of transaction arg. Returns the empty FeePerWeight132* if arg does not exist in either main or staging. This is available even for oversized133* graphs. */134virtual FeePerWeight GetIndividualFeerate(const Ref& arg) noexcept = 0;135/** Get the feerate of the chunk which transaction arg is in, in the main graph. Returns the136* empty FeePerWeight if arg does not exist in the main graph. The main graph must not be137* oversized. */138virtual FeePerWeight GetMainChunkFeerate(const Ref& arg) noexcept = 0;139/** Get pointers to all transactions in the cluster which arg is in. The transactions are140* returned in graph order. The queried graph must not be oversized. Returns {} if141* arg does not exist in the queried graph. */142virtual std::vector<Ref*> GetCluster(const Ref& arg, Level level) noexcept = 0;143/** Get pointers to all ancestors of the specified transaction (including the transaction144* itself), in unspecified order. The queried graph must not be oversized.145* Returns {} if arg does not exist in the graph. */146virtual std::vector<Ref*> GetAncestors(const Ref& arg, Level level) noexcept = 0;147/** Get pointers to all descendants of the specified transaction (including the transaction148* itself), in unspecified order. The queried graph must not be oversized.149* Returns {} if arg does not exist in the graph. */150virtual std::vector<Ref*> GetDescendants(const Ref& arg, Level level) noexcept = 0;151/** Like GetAncestors, but return the Refs for all transactions in the union of the provided152* arguments' ancestors (each transaction is only reported once). Refs that do not exist in153* the queried graph are ignored. Null refs are not allowed. */154virtual std::vector<Ref*> GetAncestorsUnion(std::span<const Ref* const> args, Level level) noexcept = 0;155/** Like GetDescendants, but return the Refs for all transactions in the union of the provided156* arguments' descendants (each transaction is only reported once). Refs that do not exist in157* the queried graph are ignored. Null refs are not allowed. */158virtual std::vector<Ref*> GetDescendantsUnion(std::span<const Ref* const> args, Level level) noexcept = 0;159/** Get the total number of transactions in the graph. This is available even160* for oversized graphs. */161virtual GraphIndex GetTransactionCount(Level level) noexcept = 0;162/** Compare two transactions according to their order in the main graph. Both transactions must163* be in the main graph. The main graph must not be oversized. */164virtual std::strong_ordering CompareMainOrder(const Ref& a, const Ref& b) noexcept = 0;165/** Count the number of distinct clusters that the specified transactions belong to. Refs that166* do not exist in the queried graph are ignored. Refs can not be null. The queried graph must167* not be oversized. */168virtual GraphIndex CountDistinctClusters(std::span<const Ref* const>, Level level) noexcept = 0;169/** For both main and staging (which must both exist and not be oversized), return the combined170* respective feerate diagrams, including chunks from all clusters, but excluding clusters171* that appear identically in both. Use FeeFrac rather than FeePerWeight so CompareChunks is172* usable without type-conversion. */173virtual std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>> GetMainStagingDiagrams() noexcept = 0;174/** Remove transactions (including their own descendants) according to a fast but best-effort175* strategy such that the TxGraph's cluster and size limits are respected. Applies to staging176* if it exists, and to main otherwise. Returns the list of all removed transactions in177* unspecified order. This has no effect unless the relevant graph is oversized. */178virtual std::vector<Ref*> Trim() noexcept = 0;180/** Interface returned by GetBlockBuilder. */181class BlockBuilder182{183protected:184/** Make constructor non-public (use TxGraph::GetBlockBuilder()). */185BlockBuilder() noexcept = default;186public:187/** Support safe inheritance. */188virtual ~BlockBuilder() = default;189/** Get the chunk that is currently suggested to be included, plus its feerate, if any. */190virtual std::optional<std::pair<std::vector<Ref*>, FeePerWeight>> GetCurrentChunk() noexcept = 0;191/** Mark the current chunk as included, and progress to the next one. */192virtual void Include() noexcept = 0;193/** Mark the current chunk as skipped, and progress to the next one. Further chunks from194* the same cluster as the current one will not be reported anymore. */195virtual void Skip() noexcept = 0;196};198/** Construct a block builder, drawing chunks in order, from the main graph, which cannot be199* oversized. While the returned object exists, no mutators on the main graph are allowed.200* The BlockBuilder object must not outlive the TxGraph it was created with. */201virtual std::unique_ptr<BlockBuilder> GetBlockBuilder() noexcept = 0;202/** Get the last chunk in the main graph, i.e., the last chunk that would be returned by a203* BlockBuilder created now, together with its feerate. The chunk is returned in204* reverse-topological order, so every element is preceded by all its descendants. The main205* graph must not be oversized. If the graph is empty, {{}, FeePerWeight{}} is returned. */206virtual std::pair<std::vector<Ref*>, FeePerWeight> GetWorstMainChunk() noexcept = 0;208/** Get the approximate memory usage for this object, just counting the main graph. If a209* staging graph is present, return a number corresponding to memory usage after210* AbortStaging() would be called. BlockBuilders' memory usage, memory usage of internally211* queued operations, and memory due to temporary caches, is not included here. Can always be212* called. */213virtual size_t GetMainMemoryUsage() noexcept = 0;215/** Perform an internal consistency check on this object. */216virtual void SanityCheck() const = 0;218protected:219// Allow TxGraph::Ref to call UpdateRef and UnlinkRef.220friend class TxGraph::Ref;221/** Inform the TxGraph implementation that a TxGraph::Ref has moved. */222virtual void UpdateRef(GraphIndex index, Ref& new_location) noexcept = 0;223/** Inform the TxGraph implementation that a TxGraph::Ref was destroyed. */224virtual void UnlinkRef(GraphIndex index) noexcept = 0;225// Allow TxGraph implementations (inheriting from it) to access Ref internals.226static TxGraph*& GetRefGraph(Ref& arg) noexcept { return arg.m_graph; }227static TxGraph* GetRefGraph(const Ref& arg) noexcept { return arg.m_graph; }228static GraphIndex& GetRefIndex(Ref& arg) noexcept { return arg.m_index; }229static GraphIndex GetRefIndex(const Ref& arg) noexcept { return arg.m_index; }231public:232class Ref233{234// Allow TxGraph's GetRefGraph and GetRefIndex to access internals.235friend class TxGraph;236/** Which Graph the Entry lives in. nullptr if this Ref is empty. */237TxGraph* m_graph = nullptr;238/** Index into the Graph's m_entries. Only used if m_graph != nullptr. */239GraphIndex m_index = GraphIndex(-1);240public:241/** Construct an empty Ref. It can be initialized through TxGraph::AddTransaction. */242Ref() noexcept = default;243/** Destroy this Ref. If it is not empty, the corresponding transaction is removed (in both244* main and staging, if it exists). */245virtual ~Ref();246// Support move-constructing a Ref.247Ref& operator=(Ref&& other) noexcept = delete;248Ref(Ref&& other) noexcept;249// Do not permit copy constructing or copy assignment. A TxGraph entry can have at most one250// Ref pointing to it.
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.