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 <random.h>7#include <serialize.h>8#include <streams.h>9#include <test/fuzz/FuzzedDataProvider.h>10#include <test/fuzz/fuzz.h>11#include <test/util/cluster_linearize.h>12#include <util/bitset.h>13#include <util/feefrac.h>15#include <algorithm>16#include <cstdint>17#include <utility>18#include <vector>20/*21* The tests in this file primarily cover the candidate finder classes and linearization algorithms.22*23* <----: An implementation (at the start of the line --) is tested in the test marked with *,24* possibly by comparison with other implementations (at the end of the line ->).25* <<---: The right side is implemented using the left side.26*27* +---------------------+ +-----------+28* | SpanningForestState | <<-------------------- | Linearize |29* +---------------------+ +-----------+30* | |31* | | ^^ PRODUCTION CODE32* | | ||33* ==============================================================================================34* | | ||35* |-clusterlin_sfl* | vv TEST CODE36* | |37* \------------------------------------\ |-clusterlin_linearize*38* | |39* v v40* +-----------------------+ +-----------------+41* | SimpleCandidateFinder | <<-------------------| SimpleLinearize |42* +-----------------------+ +-----------------+43* | |44* |-clusterlin_simple_finder* |-clusterlin_simple_linearize*45* v v46* +---------------------------+ +---------------------+47* | ExhaustiveCandidateFinder | | ExhaustiveLinearize |48* +---------------------------+ +---------------------+49*50* More tests are included for lower-level and related functions and classes:51* - DepGraph tests:52* - clusterlin_depgraph_sim53* - clusterlin_depgraph_serialization54* - clusterlin_components55* - ChunkLinearization and ChunkLinearizationInfo tests:56* - clusterlin_chunking57* - PostLinearize tests:58* - clusterlin_postlinearize59* - clusterlin_postlinearize_tree60* - clusterlin_postlinearize_moved_leaf61* - MakeConnected tests (a test-only function):62* - clusterlin_make_connected63*/65using namespace cluster_linearize;67namespace {69/** A simple finder class for candidate sets (topologically-valid subsets with high feerate), only70* used by SimpleLinearize below. */71template<typename SetType>72class SimpleCandidateFinder73{74/** Internal dependency graph. */75const DepGraph<SetType>& m_depgraph;76/** Which transaction are left to include. */77SetType m_todo;79public:80/** Construct an SimpleCandidateFinder for a given graph. */81SimpleCandidateFinder(const DepGraph<SetType>& depgraph LIFETIMEBOUND) noexcept :82m_depgraph(depgraph), m_todo{depgraph.Positions()} {}84/** Remove a set of transactions from the set of to-be-linearized ones. */85void MarkDone(SetType select) noexcept { m_todo -= select; }87/** Determine whether unlinearized transactions remain. */88bool AllDone() const noexcept { return m_todo.None(); }90/** Find a candidate set using at most max_iterations iterations, and the number of iterations91* actually performed. If that number is less than max_iterations, then the result is optimal.92*93* Always returns a connected set of transactions.94*95* Complexity: O(N * M), where M is the number of connected topological subsets of the cluster.96* That number is bounded by M <= 2^(N-1).97*/98std::pair<SetInfo<SetType>, uint64_t> FindCandidateSet(uint64_t max_iterations) const noexcept99{100uint64_t iterations_left = max_iterations;101// Queue of work units. Each consists of:102// - inc: set of transactions definitely included103// - und: set of transactions that can be added to inc still104std::vector<std::pair<SetType, SetType>> queue;105// Initially we have just one queue element, with the entire graph in und.106queue.emplace_back(SetType{}, m_todo);107// Best solution so far. Initialize with the remaining ancestors of the first remaining108// transaction.109SetInfo best(m_depgraph, m_depgraph.Ancestors(m_todo.First()) & m_todo);110// Process the queue.111while (!queue.empty() && iterations_left) {112// Pop top element of the queue.113auto [inc, und] = queue.back();114queue.pop_back();115// Look for a transaction to consider adding/removing.116bool inc_none = inc.None();117for (auto split : und) {118// If inc is empty, consider any split transaction. Otherwise only consider119// transactions that share ancestry with inc so far (which means only connected120// sets will be considered).121if (inc_none || inc.Overlaps(m_depgraph.Ancestors(split))) {122--iterations_left;123// Add a queue entry with split included.124SetInfo new_inc(m_depgraph, inc | (m_todo & m_depgraph.Ancestors(split)));125queue.emplace_back(new_inc.transactions, und - new_inc.transactions);126// Add a queue entry with split excluded.127queue.emplace_back(inc, und - m_depgraph.Descendants(split));128// Update statistics to account for the candidate new_inc.129if (ByRatioNegSize{new_inc.feerate} > ByRatioNegSize{best.feerate}) best = new_inc;130break;131}132}133}134return {std::move(best), max_iterations - iterations_left};135}136};138/** A very simple finder class for optimal candidate sets, which tries every subset.139*140* It is even simpler than SimpleCandidateFinder, and exists just to help test the correctness of141* SimpleCandidateFinder, so that it can be used in SimpleLinearize, which is then used to test the142* correctness of Linearize.143*/144template<typename SetType>145class ExhaustiveCandidateFinder146{147/** Internal dependency graph. */148const DepGraph<SetType>& m_depgraph;149/** Which transaction are left to include. */150SetType m_todo;152public:153/** Construct an ExhaustiveCandidateFinder for a given graph. */154ExhaustiveCandidateFinder(const DepGraph<SetType>& depgraph LIFETIMEBOUND) noexcept :155m_depgraph(depgraph), m_todo{depgraph.Positions()} {}157/** Remove a set of transactions from the set of to-be-linearized ones. */158void MarkDone(SetType select) noexcept { m_todo -= select; }160/** Determine whether unlinearized transactions remain. */161bool AllDone() const noexcept { return m_todo.None(); }163/** Find the optimal remaining candidate set.164*165* Complexity: O(N * 2^N).166*/167SetInfo<SetType> FindCandidateSet() const noexcept168{169// Best solution so far.170SetInfo<SetType> best{m_todo, m_depgraph.FeeRate(m_todo)};171// The number of combinations to try.172uint64_t limit = (uint64_t{1} << m_todo.Count()) - 1;173// Try the transitive closure of every non-empty subset of m_todo.174for (uint64_t x = 1; x < limit; ++x) {175// If bit number b is set in x, then the remaining ancestors of the b'th remaining176// transaction in m_todo are included.177SetType txn;178auto x_shifted{x};179for (auto i : m_todo) {180if (x_shifted & 1) txn |= m_depgraph.Ancestors(i);181x_shifted >>= 1;182}183SetInfo cur(m_depgraph, txn & m_todo);184if (ByRatioNegSize{cur.feerate} > ByRatioNegSize{best.feerate}) best = cur;185}186return best;187}188};190/** A simple linearization algorithm.191*192* This matches Linearize() in interface and behavior, though with fewer optimizations, lacking193* the ability to pass in an existing linearization, and linearizing by simply finding the194* consecutive remaining highest-feerate topological subset using SimpleCandidateFinder.195*/196template<typename SetType>197std::pair<std::vector<DepGraphIndex>, bool> SimpleLinearize(const DepGraph<SetType>& depgraph, uint64_t max_iterations)198{199std::vector<DepGraphIndex> linearization;200SimpleCandidateFinder finder(depgraph);201SetType todo = depgraph.Positions();202bool optimal = true;203while (todo.Any()) {204auto [candidate, iterations_done] = finder.FindCandidateSet(max_iterations);205if (iterations_done == max_iterations) optimal = false;206depgraph.AppendTopo(linearization, candidate.transactions);207todo -= candidate.transactions;208finder.MarkDone(candidate.transactions);209max_iterations -= iterations_done;210}211return {std::move(linearization), optimal};212}214/** An even simpler linearization algorithm that tries all permutations.215*216* This roughly matches SimpleLinearize() (and Linearize) in interface and behavior, but always217* tries all topologically-valid transaction orderings, has no way to bound how much work it does,218* and always finds the optimal. With an O(n!) complexity, it should only be used for small219* clusters.220*/221template<typename SetType>222std::vector<DepGraphIndex> ExhaustiveLinearize(const DepGraph<SetType>& depgraph)223{224// The best linearization so far, and its chunking.225std::vector<DepGraphIndex> linearization;226std::vector<FeeFrac> chunking;228std::vector<DepGraphIndex> perm_linearization;229// Initialize with the lexicographically-first linearization.230for (DepGraphIndex i : depgraph.Positions()) perm_linearization.push_back(i);231// Iterate over all valid permutations.232do {233/** What prefix of perm_linearization is topological. */234DepGraphIndex topo_length{0};235TestBitSet perm_done;236while (topo_length < perm_linearization.size()) {237auto i = perm_linearization[topo_length];238perm_done.Set(i);239if (!depgraph.Ancestors(i).IsSubsetOf(perm_done)) break;240++topo_length;241}242if (topo_length == perm_linearization.size()) {243// If all of perm_linearization is topological, check if it is perhaps our best244// linearization so far.245auto perm_chunking = ChunkLinearization(depgraph, perm_linearization);246auto cmp = CompareChunks(perm_chunking, chunking);247// If the diagram is better, or if it is equal but with more chunks (because we248// prefer minimal chunks), consider this better.249if (linearization.empty() || cmp > 0 || (cmp == 0 && perm_chunking.size() > chunking.size())) {250linearization = perm_linearization;
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.