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/** An implementation of Cluster that uses a DepGraph and vectors, to support arbitrary numbers of252* transactions up to MAX_CLUSTER_COUNT_LIMIT. */253class GenericClusterImpl final : public Cluster254{255friend class TxGraphImpl;256/** The DepGraph for this cluster, holding all feerates, and ancestors/descendants. */257DepGraph<SetType> m_depgraph;258/** m_mapping[i] gives the GraphIndex for the position i transaction in m_depgraph. Values for259* positions i that do not exist in m_depgraph shouldn't ever be accessed and thus don't260* matter. m_mapping.size() equals m_depgraph.PositionRange(). */261std::vector<GraphIndex> m_mapping;262/** The current linearization of the cluster. m_linearization.size() equals263* m_depgraph.TxCount(). This is always kept topological. */264std::vector<DepGraphIndex> m_linearization;266public:267/** The smallest number of transactions this Cluster implementation is intended for. */268static constexpr DepGraphIndex MIN_INTENDED_TX_COUNT{2};269/** The largest number of transactions this Cluster implementation supports. */270static constexpr DepGraphIndex MAX_TX_COUNT{SetType::Size()};272GenericClusterImpl() noexcept = delete;273/** Construct an empty GenericClusterImpl. */274explicit GenericClusterImpl(uint64_t sequence) noexcept;276size_t TotalMemoryUsage() const noexcept final;277constexpr DepGraphIndex GetMinIntendedTxCount() const noexcept final { return MIN_INTENDED_TX_COUNT; }278constexpr DepGraphIndex GetMaxTxCount() const noexcept final { return MAX_TX_COUNT; }279DepGraphIndex GetDepGraphIndexRange() const noexcept final { return m_depgraph.PositionRange(); }280LinearizationIndex GetTxCount() const noexcept final { return m_linearization.size(); }281uint64_t GetTotalTxSize() const noexcept final;282GraphIndex GetClusterEntry(DepGraphIndex index) const noexcept final { return m_mapping[index]; }283DepGraphIndex AppendTransaction(GraphIndex graph_idx, FeePerWeight feerate) noexcept final;284void AddDependencies(SetType parents, DepGraphIndex child) noexcept final;285void ExtractTransactions(const std::function<void (DepGraphIndex, GraphIndex, FeePerWeight)>& visit1_fn, const std::function<void (DepGraphIndex, GraphIndex, SetType)>& visit2_fn) noexcept final;286int GetLevel(const TxGraphImpl& graph) const noexcept final;287void UpdateMapping(DepGraphIndex cluster_idx, GraphIndex graph_idx) noexcept final { m_mapping[cluster_idx] = graph_idx; }288void Updated(TxGraphImpl& graph, int level, bool rename) noexcept final;289void RemoveChunkData(TxGraphImpl& graph) noexcept final;290Cluster* CopyToStaging(TxGraphImpl& graph) const noexcept final;291void GetConflicts(const TxGraphImpl& graph, std::vector<Cluster*>& out) const noexcept final;292void MakeStagingTransactionsMissing(TxGraphImpl& graph) noexcept final;293void Clear(TxGraphImpl& graph, int level) noexcept final;294void MoveToMain(TxGraphImpl& graph) noexcept final;295void Compact() noexcept final;296void ApplyRemovals(TxGraphImpl& graph, int level, std::span<GraphIndex>& to_remove) noexcept final;297[[nodiscard]] bool Split(TxGraphImpl& graph, int level) noexcept final;298void Merge(TxGraphImpl& graph, int level, Cluster& cluster) noexcept final;299void ApplyDependencies(TxGraphImpl& graph, int level, std::span<std::pair<GraphIndex, GraphIndex>> to_apply) noexcept final;300std::pair<uint64_t, bool> Relinearize(TxGraphImpl& graph, int level, uint64_t max_cost) noexcept final;301void AppendChunkFeerates(std::vector<FeeFrac>& ret) const noexcept final;302uint64_t AppendTrimData(std::vector<TrimTxData>& ret, std::vector<std::pair<GraphIndex, GraphIndex>>& deps) const noexcept final;303void GetAncestorRefs(const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>& args, std::vector<TxGraph::Ref*>& output) noexcept final;304void GetDescendantRefs(const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>& args, std::vector<TxGraph::Ref*>& output) noexcept final;305bool GetClusterRefs(TxGraphImpl& graph, std::span<TxGraph::Ref*> range, LinearizationIndex start_pos) noexcept final;306FeePerWeight GetIndividualFeerate(DepGraphIndex idx) noexcept final;307void SetFee(TxGraphImpl& graph, int level, DepGraphIndex idx, int64_t fee) noexcept final;308void SanityCheck(const TxGraphImpl& graph, int level) const final;309};311/** An implementation of Cluster that only supports 1 transaction. */312class SingletonClusterImpl final : public Cluster313{314friend class TxGraphImpl;316/** The feerate of the (singular) transaction in this Cluster. */317FeePerWeight m_feerate;318/** Constant to indicate that this Cluster is empty. */319static constexpr auto NO_GRAPH_INDEX = GraphIndex(-1);320/** The GraphIndex of the transaction. NO_GRAPH_INDEX if this Cluster is empty. */321GraphIndex m_graph_index = NO_GRAPH_INDEX;323public:324/** The smallest number of transactions this Cluster implementation is intended for. */325static constexpr DepGraphIndex MIN_INTENDED_TX_COUNT{1};326/** The largest number of transactions this Cluster implementation supports. */327static constexpr DepGraphIndex MAX_TX_COUNT{1};329SingletonClusterImpl() noexcept = delete;330/** Construct an empty SingletonClusterImpl. */331explicit SingletonClusterImpl(uint64_t sequence) noexcept : Cluster(sequence) {}333size_t TotalMemoryUsage() const noexcept final;334constexpr DepGraphIndex GetMinIntendedTxCount() const noexcept final { return MIN_INTENDED_TX_COUNT; }335constexpr DepGraphIndex GetMaxTxCount() const noexcept final { return MAX_TX_COUNT; }336LinearizationIndex GetTxCount() const noexcept final { return m_graph_index != NO_GRAPH_INDEX; }337DepGraphIndex GetDepGraphIndexRange() const noexcept final { return GetTxCount(); }338uint64_t GetTotalTxSize() const noexcept final { return GetTxCount() ? m_feerate.size : 0; }339GraphIndex GetClusterEntry(DepGraphIndex index) const noexcept final { Assume(index == 0); Assume(GetTxCount()); return m_graph_index; }340DepGraphIndex AppendTransaction(GraphIndex graph_idx, FeePerWeight feerate) noexcept final;341void AddDependencies(SetType parents, DepGraphIndex child) noexcept final;342void ExtractTransactions(const std::function<void (DepGraphIndex, GraphIndex, FeePerWeight)>& visit1_fn, const std::function<void (DepGraphIndex, GraphIndex, SetType)>& visit2_fn) noexcept final;343int GetLevel(const TxGraphImpl& graph) const noexcept final;344void UpdateMapping(DepGraphIndex cluster_idx, GraphIndex graph_idx) noexcept final { Assume(cluster_idx == 0); m_graph_index = graph_idx; }345void Updated(TxGraphImpl& graph, int level, bool rename) noexcept final;346void RemoveChunkData(TxGraphImpl& graph) noexcept final;347Cluster* CopyToStaging(TxGraphImpl& graph) const noexcept final;348void GetConflicts(const TxGraphImpl& graph, std::vector<Cluster*>& out) const noexcept final;349void MakeStagingTransactionsMissing(TxGraphImpl& graph) noexcept final;350void Clear(TxGraphImpl& graph, int level) noexcept final;351void MoveToMain(TxGraphImpl& graph) noexcept final;352void Compact() noexcept final;353void ApplyRemovals(TxGraphImpl& graph, int level, std::span<GraphIndex>& to_remove) noexcept final;354[[nodiscard]] bool Split(TxGraphImpl& graph, int level) noexcept final;355void Merge(TxGraphImpl& graph, int level, Cluster& cluster) noexcept final;356void ApplyDependencies(TxGraphImpl& graph, int level, std::span<std::pair<GraphIndex, GraphIndex>> to_apply) noexcept final;357std::pair<uint64_t, bool> Relinearize(TxGraphImpl& graph, int level, uint64_t max_cost) noexcept final;358void AppendChunkFeerates(std::vector<FeeFrac>& ret) const noexcept final;359uint64_t AppendTrimData(std::vector<TrimTxData>& ret, std::vector<std::pair<GraphIndex, GraphIndex>>& deps) const noexcept final;360void GetAncestorRefs(const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>& args, std::vector<TxGraph::Ref*>& output) noexcept final;361void GetDescendantRefs(const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>& args, std::vector<TxGraph::Ref*>& output) noexcept final;362bool GetClusterRefs(TxGraphImpl& graph, std::span<TxGraph::Ref*> range, LinearizationIndex start_pos) noexcept final;363FeePerWeight GetIndividualFeerate(DepGraphIndex idx) noexcept final;364void SetFee(TxGraphImpl& graph, int level, DepGraphIndex idx, int64_t fee) noexcept final;365void SanityCheck(const TxGraphImpl& graph, int level) const final;366};368/** The transaction graph, including staged changes.369*370* The overall design of the data structure consists of 3 interlinked representations:371* - The transactions (held as a vector of TxGraphImpl::Entry inside TxGraphImpl).372* - The clusters (Cluster objects in per-quality vectors inside TxGraphImpl::ClusterSet).373* - The Refs (TxGraph::Ref objects, held externally by users of the TxGraph class)374*375* The Clusters are kept in one or two ClusterSet objects, one for the "main" graph, and one for376* the proposed changes ("staging"). If a transaction occurs in both, they share the same Entry,377* but there will be a separate Cluster per graph.378*379* Clusters and Refs contain the index of the Entry objects they refer to, and the Entry objects380* refer back to the Clusters and Refs the corresponding transaction is contained in.381*382* While redundant, this permits moving all of them independently, without invalidating things383* or costly iteration to fix up everything:384* - Entry objects can be moved to fill holes left by removed transactions in the Entry vector385* (see TxGraphImpl::Compact).386* - Clusters can be rewritten continuously (removals can cause them to split, new dependencies387* can cause them to be merged).388* - Ref objects can be held outside the class, while permitting them to be moved around, and389* inherited from.390*/391class TxGraphImpl final : public TxGraph392{393friend class Cluster;394friend class SingletonClusterImpl;395friend class GenericClusterImpl;396friend class BlockBuilderImpl;397private:398/** Internal RNG. */399FastRandomContext m_rng;400/** This TxGraphImpl's maximum cluster count limit. */401const DepGraphIndex m_max_cluster_count;402/** This TxGraphImpl's maximum cluster size limit. */403const uint64_t m_max_cluster_size;404/** The amount of linearization work needed per cluster to be considered acceptable. */405const uint64_t m_acceptable_cost;406/** Fallback ordering for transactions. */407const std::function<std::strong_ordering(const TxGraph::Ref&, const TxGraph::Ref&)> m_fallback_order;409/** Information about one group of Clusters to be merged. */410struct GroupEntry411{412/** Where the clusters to be merged start in m_group_clusters. */413uint32_t m_cluster_offset;414/** How many clusters to merge. */415uint32_t m_cluster_count;416/** Where the dependencies for this cluster group in m_deps_to_add start. */417uint32_t m_deps_offset;418/** How many dependencies to add. */419uint32_t m_deps_count;420};422/** Information about all groups of Clusters to be merged. */423struct GroupData424{425/** The groups of Clusters to be merged. */426std::vector<GroupEntry> m_groups;427/** Which clusters are to be merged. GroupEntry::m_cluster_offset indexes into this. */428std::vector<Cluster*> m_group_clusters;429};431/** The collection of all Clusters in main or staged. */432struct ClusterSet433{434/** The vectors of clusters, one vector per quality level. ClusterSetIndex indexes into each. */435std::array<std::vector<std::unique_ptr<Cluster>>, int(QualityLevel::NONE)> m_clusters;436/** Which removals have yet to be applied. */437std::vector<GraphIndex> m_to_remove;438/** Which dependencies are to be added ((parent,child) pairs). GroupData::m_deps_offset indexes439* into this. */440std::vector<std::pair<GraphIndex, GraphIndex>> m_deps_to_add;441/** Information about the merges to be performed, if known. */442std::optional<GroupData> m_group_data = GroupData{};443/** Which entries were removed in this ClusterSet (so they can be wiped on abort). This444* includes all entries which have an (R) removed locator at this level (staging only),445* plus optionally any transaction in m_unlinked. */446std::vector<GraphIndex> m_removed;447/** Total number of transactions in this graph (sum of all transaction counts in all448* Clusters, and for staging also those inherited from the main ClusterSet). */449GraphIndex m_txcount{0};450/** Total number of individually oversized transactions in the graph. */451GraphIndex m_txcount_oversized{0};452/** Whether this graph is oversized (if known). */453std::optional<bool> m_oversized{false};454/** The combined TotalMemoryUsage of all clusters in this level (only Clusters that455* are materialized; in staging, implicit Clusters from main are not counted), */456size_t m_cluster_usage{0};458ClusterSet() noexcept = default;459};461/** The main ClusterSet. */462ClusterSet m_main_clusterset;463/** The staging ClusterSet, if any. */464std::optional<ClusterSet> m_staging_clusterset;465/** Next sequence number to assign to created Clusters. */466uint64_t m_next_sequence_counter{0};468/** Information about a chunk in the main graph. */469struct ChunkData470{471/** The Entry which is the last transaction of the chunk. */472mutable GraphIndex m_graph_index;473/** How many transactions the chunk contains (-1 = singleton tail of cluster). */474LinearizationIndex m_chunk_count;476ChunkData(GraphIndex graph_index, LinearizationIndex chunk_count) noexcept :477m_graph_index{graph_index}, m_chunk_count{chunk_count} {}478};480/** Compare two Cluster* by their m_sequence value (while supporting nullptr). */481static std::strong_ordering CompareClusters(Cluster* a, Cluster* b) noexcept482{483// The nullptr pointer compares before everything else.484if (a == nullptr || b == nullptr) {485return (a != nullptr) <=> (b != nullptr);486}487// If neither pointer is nullptr, compare the Clusters' sequence numbers.488Assume(a == b || a->m_sequence != b->m_sequence);489return a->m_sequence <=> b->m_sequence;490}492/** Compare two entries (which must both exist within the main graph). */493std::strong_ordering CompareMainTransactions(GraphIndex a, GraphIndex b) const noexcept494{495if (a == b) return std::strong_ordering::equal;496Assume(a < m_entries.size() && b < m_entries.size());497const auto& entry_a = m_entries[a];498const auto& entry_b = m_entries[b];499// Compare chunk feerates, and return result if it differs.500auto feerate_cmp = ByRatio{entry_b.m_main_chunk_feerate} <=> ByRatio{entry_a.m_main_chunk_feerate};
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.