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.
501public:502inline void InitializeBegin() noexcept {}503inline void InitializeEnd(int num_txns, int num_deps) noexcept504{505// Cost of initialization.506m_cost += 39 * num_txns;507// Cost of producing linearization at the end.508m_cost += 48 * num_txns + 4 * num_deps;509}510inline void GetLinearizationBegin() noexcept {}511inline void GetLinearizationEnd(int num_txns, int num_deps) noexcept512{513// Note that we account for the cost of the final linearization at the beginning (see514// InitializeEnd), because the cost budget decision needs to be made before calling515// GetLinearization.516// This function exists here to allow overriding it easily for benchmark purposes.517}518inline void MakeTopologicalBegin() noexcept {}519inline void MakeTopologicalEnd(int num_chunks, int num_steps) noexcept520{521m_cost += 20 * num_chunks + 28 * num_steps;522}523inline void StartOptimizingBegin() noexcept {}524inline void StartOptimizingEnd(int num_chunks) noexcept { m_cost += 13 * num_chunks; }525inline void ActivateBegin() noexcept {}526inline void ActivateEnd(int num_deps) noexcept { m_cost += 10 * num_deps + 1; }527inline void DeactivateBegin() noexcept {}528inline void DeactivateEnd(int num_deps) noexcept { m_cost += 11 * num_deps + 8; }529inline void MergeChunksBegin() noexcept {}530inline void MergeChunksMid(int num_txns) noexcept { m_cost += 2 * num_txns; }531inline void MergeChunksEnd(int num_steps) noexcept { m_cost += 3 * num_steps + 5; }532inline void PickMergeCandidateBegin() noexcept {}533inline void PickMergeCandidateEnd(int num_steps) noexcept { m_cost += 8 * num_steps; }534inline void PickChunkToOptimizeBegin() noexcept {}535inline void PickChunkToOptimizeEnd(int num_steps) noexcept { m_cost += num_steps + 4; }536inline void PickDependencyToSplitBegin() noexcept {}537inline void PickDependencyToSplitEnd(int num_txns) noexcept { m_cost += 8 * num_txns + 9; }538inline void StartMinimizingBegin() noexcept {}539inline void StartMinimizingEnd(int num_chunks) noexcept { m_cost += 18 * num_chunks; }540inline void MinimizeStepBegin() noexcept {}541inline void MinimizeStepMid(int num_txns) noexcept { m_cost += 11 * num_txns + 11; }542inline void MinimizeStepEnd(bool split) noexcept { m_cost += 17 * split + 7; }544inline uint64_t GetCost() const noexcept { return m_cost; }545};547/** Class to represent the internal state of the spanning-forest linearization (SFL) algorithm.548*549* At all times, each dependency is marked as either "active" or "inactive". The subset of active550* dependencies is the state of the SFL algorithm. The implementation maintains several other551* values to speed up operations, but everything is ultimately a function of what that subset of552* active dependencies is.553*554* Given such a subset, define a chunk as the set of transactions that are connected through active555* dependencies (ignoring their parent/child direction). Thus, every state implies a particular556* partitioning of the graph into chunks (including potential singletons). In the extreme, each557* transaction may be in its own chunk, or in the other extreme all transactions may form a single558* chunk. A chunk's feerate is its total fee divided by its total size.559*560* The algorithm consists of switching dependencies between active and inactive. The final561* linearization that is produced at the end consists of these chunks, sorted from high to low562* feerate, each individually sorted in an arbitrary but topological (= no child before parent)563* way.564*565* We define four quality properties the state can have:566*567* - acyclic: The state is acyclic whenever no cycle of active dependencies exists within the568* graph, ignoring the parent/child direction. This is equivalent to saying that within569* each chunk the set of active dependencies form a tree, and thus the overall set of570* active dependencies in the graph form a spanning forest, giving the algorithm its571* name. Being acyclic is also equivalent to every chunk of N transactions having572* exactly N-1 active dependencies.573*574* For example in a diamond graph, D->{B,C}->A, the 4 dependencies cannot be575* simultaneously active. If at least one is inactive, the state is acyclic.576*577* The algorithm maintains an acyclic state at *all* times as an invariant. This implies578* that activating a dependency always corresponds to merging two chunks, and that579* deactivating one always corresponds to splitting two chunks.580*581* - topological: We say the state is topological whenever it is acyclic and no inactive dependency582* exists between two distinct chunks such that the child chunk has higher or equal583* feerate than the parent chunk.584*585* The relevance is that whenever the state is topological, the produced output586* linearization will be topological too (i.e., not have children before parents).587* Note that the "or equal" part of the definition matters: if not, one can end up588* in a situation with mutually-dependent equal-feerate chunks that cannot be589* linearized. For example C->{A,B} and D->{A,B}, with C->A and D->B active. The AC590* chunk depends on DB through C->B, and the BD chunk depends on AC through D->A.591* Merging them into a single ABCD chunk fixes this.592*593* The algorithm attempts to keep the state topological as much as possible, so it594* can be interrupted to produce an output whenever, but will sometimes need to595* temporarily deviate from it when improving the state.596*597* - optimal: For every active dependency, define its top and bottom set as the set of transactions598* in the chunks that would result if the dependency were deactivated; the top being the599* one with the dependency's parent, and the bottom being the one with the child. Note600* that due to acyclicity, every deactivation splits a chunk exactly in two.601*602* We say the state is optimal whenever it is topological and it has no active603* dependency whose top feerate is strictly higher than its bottom feerate. The604* relevance is that it can be proven that whenever the state is optimal, the produced605* linearization will also be optimal (in the convexified feerate diagram sense). It can606* also be proven that for every graph at least one optimal state exists.607*608* Note that it is possible for the SFL state to not be optimal, but the produced609* linearization to still be optimal. This happens when the chunks of a state are610* identical to those of an optimal state, but the exact set of active dependencies611* within a chunk differ in such a way that the state optimality condition is not612* satisfied. Thus, the state being optimal is more a "the eventual output is *known*613* to be optimal".614*615* - minimal: We say the state is minimal when it is:616* - acyclic617* - topological, except that inactive dependencies between equal-feerate chunks are618* allowed as long as they do not form a loop.619* - like optimal, no active dependencies whose top feerate is strictly higher than620* the bottom feerate are allowed.621* - no chunk contains a proper non-empty subset which includes all its own in-chunk622* dependencies of the same feerate as the chunk itself.623*624* A minimal state effectively corresponds to an optimal state, where every chunk has625* been split into its minimal equal-feerate components.626*627* The algorithm terminates whenever a minimal state is reached.628*629*630* This leads to the following high-level algorithm:631* - Start with all dependencies inactive, and thus all transactions in their own chunk. This is632* definitely acyclic.633* - Activate dependencies (merging chunks) until the state is topological.634* - Loop until optimal (no dependencies with higher-feerate top than bottom), or time runs out:635* - Deactivate a violating dependency, potentially making the state non-topological.636* - Activate other dependencies to make the state topological again.637* - If there is time left and the state is optimal:638* - Attempt to split chunks into equal-feerate parts without mutual dependencies between them.639* When this succeeds, recurse into them.640* - If no such chunks can be found, the state is minimal.641* - Output the chunks from high to low feerate, each internally sorted topologically.642*643* When merging, we always either:644* - Merge upwards: merge a chunk with the lowest-feerate other chunk it depends on, among those645* with lower or equal feerate than itself.646* - Merge downwards: merge a chunk with the highest-feerate other chunk that depends on it, among647* those with higher or equal feerate than itself.648*649* Using these strategies in the improvement loop above guarantees that the output linearization650* after a deactivate + merge step is never worse or incomparable (in the convexified feerate651* diagram sense) than the output linearization that would be produced before the step. With that,652* we can refine the high-level algorithm to:653* - Start with all dependencies inactive.654* - Perform merges as described until none are possible anymore, making the state topological.655* - Loop until optimal or time runs out:656* - Pick a dependency D to deactivate among those with higher feerate top than bottom.657* - Deactivate D, causing the chunk it is in to split into top T and bottom B.658* - Do an upwards merge of T, if possible. If so, repeat the same with the merged result.659* - Do a downwards merge of B, if possible. If so, repeat the same with the merged result.660* - Split chunks further to obtain a minimal state, see below.661* - Output the chunks from high to low feerate, each internally sorted topologically.662*663* Instead of performing merges arbitrarily to make the initial state topological, it is possible664* to do so guided by an existing linearization. This has the advantage that the state's would-be665* output linearization is immediately as good as the existing linearization it was based on:666* - Start with all dependencies inactive.667* - For each transaction t in the existing linearization:668* - Find the chunk C that transaction is in (which will be singleton).669* - Do an upwards merge of C, if possible. If so, repeat the same with the merged result.670* No downwards merges are needed in this case.671*672* After reaching an optimal state, it can be transformed into a minimal state by attempting to673* split chunks further into equal-feerate parts. To do so, pick a specific transaction in each674* chunk (the pivot), and rerun the above split-then-merge procedure again:675* - first, while pretending the pivot transaction has an infinitesimally higher (or lower) fee676* than it really has. If a split exists with the pivot in the top part (or bottom part), this677* will find it.678* - if that fails to split, repeat while pretending the pivot transaction has an infinitesimally679* lower (or higher) fee. If a split exists with the pivot in the bottom part (or top part), this680* will find it.681* - if either succeeds, repeat the procedure for the newly found chunks to split them further.682* If not, the chunk is already minimal.683* If the chunk can be split into equal-feerate parts, then the pivot must exist in either the top684* or bottom part of that potential split. By trying both with the same pivot, if a split exists,685* it will be found.686*687* What remains to be specified are a number of heuristics:688*689* - How to decide which chunks to merge:690* - The merge upwards and downward rules specify that the lowest-feerate respectively691* highest-feerate candidate chunk is merged with, but if there are multiple equal-feerate692* candidates, a uniformly random one among them is picked.693*694* - How to decide what dependency to activate (when merging chunks):695* - After picking two chunks to be merged (see above), a uniformly random dependency between the696* two chunks is activated.697*698* - How to decide which chunk to find a dependency to split in:699* - A round-robin queue of chunks to improve is maintained. The initial ordering of this queue700* is uniformly randomly permuted.701*702* - How to decide what dependency to deactivate (when splitting chunks):703* - Inside the selected chunk (see above), among the dependencies whose top feerate is strictly704* higher than its bottom feerate in the selected chunk, if any, a uniformly random dependency705* is deactivated.706* - After every split, it is possible that the top and the bottom chunk merge with each other707* again in the merge sequence (through a top->bottom dependency, not through the deactivated708* one, which was bottom->top). Call this a self-merge. If a self-merge does not occur after709* a split, the resulting linearization is strictly improved (the area under the convexified710* feerate diagram increases by at least gain/2), while self-merges do not change it.711*712* - How to decide the exact output linearization:713* - When there are multiple equal-feerate chunks with no dependencies between them, pick the714* smallest one first. If there are multiple smallest ones, pick the one that contains the715* last transaction (according to the provided fallback order) last (note that this is not the716* same as picking the chunk with the first transaction first).717* - Within chunks, pick among all transactions without missing dependencies the one with the718* highest individual feerate. If there are multiple ones with the same individual feerate,719* pick the smallest first. If there are multiple with the same fee and size, pick the one720* that sorts first according to the fallback order first.721*/722template<typename SetType, typename CostModel = SFLDefaultCostModel>723class SpanningForestState724{725private:726/** Internal RNG. */727InsecureRandomContext m_rng;729/** Data type to represent indexing into m_tx_data. */730using TxIdx = DepGraphIndex;731/** Data type to represent indexing into m_set_info. Use the smallest type possible to improve732* cache locality. */733using SetIdx = std::conditional_t<(SetType::Size() <= 0xff),734uint8_t,735std::conditional_t<(SetType::Size() <= 0xffff),736uint16_t,737uint32_t>>;738/** An invalid SetIdx. */739static constexpr SetIdx INVALID_SET_IDX = SetIdx(-1);741/** Structure with information about a single transaction. */742struct TxData {743/** The top set for every active child dependency this transaction has, indexed by child744* TxIdx. Only defined for indexes in active_children. */745std::array<SetIdx, SetType::Size()> dep_top_idx;746/** The set of parent transactions of this transaction. Immutable after construction. */747SetType parents;748/** The set of child transactions of this transaction. Immutable after construction. */749SetType children;750/** The set of child transactions reachable through an active dependency. */
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.