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.
1501for (Cluster* new_cluster : new_clusters) {1502new_cluster->Updated(graph, /*level=*/level, /*rename=*/false);1503new_cluster->Compact();1504graph.GetClusterSet(level).m_cluster_usage += new_cluster->TotalMemoryUsage();1505}1506// Wipe this Cluster, and return that it needs to be deleted.1507m_depgraph = DepGraph<SetType>{};1508m_mapping.clear();1509m_linearization.clear();1510return true;1511}1513bool SingletonClusterImpl::Split(TxGraphImpl& graph, int level) noexcept1514{1515Assume(NeedsSplitting());1516Assume(!GetTxCount());1517graph.GetClusterSet(level).m_cluster_usage -= TotalMemoryUsage();1518return true;1519}1521void GenericClusterImpl::Merge(TxGraphImpl& graph, int level, Cluster& other) noexcept1522{1523/** Vector to store the positions in this Cluster for each position in other. */1524std::vector<DepGraphIndex> remap(other.GetDepGraphIndexRange());1525// Iterate over all transactions in the other Cluster (the one being absorbed).1526other.ExtractTransactions([&](DepGraphIndex pos, GraphIndex idx, FeePerWeight feerate) noexcept {1527// Copy the transaction into this Cluster, and remember its position.1528auto new_pos = m_depgraph.AddTransaction(feerate);1529// Since this cluster must have been made hole-free before being merged into, all added1530// transactions should appear at the end.1531Assume(new_pos == m_mapping.size());1532remap[pos] = new_pos;1533m_mapping.push_back(idx);1534m_linearization.push_back(new_pos);1535}, [&](DepGraphIndex pos, GraphIndex idx, SetType other_parents) noexcept {1536// Copy the transaction's dependencies, translating them using remap.1537SetType parents;1538for (auto par : other_parents) {1539parents.Set(remap[par]);1540}1541m_depgraph.AddDependencies(parents, remap[pos]);1542// Update the transaction's Locator. There is no need to call Updated() to update chunk1543// feerates, as Updated() will be invoked by Cluster::ApplyDependencies on the resulting1544// merged Cluster later anyway.1545auto& entry = graph.m_entries[idx];1546// Discard any potential ChunkData prior to modifying the Cluster (as that could1547// invalidate its ordering).1548if (level == 0) graph.ClearChunkData(entry);1549entry.m_locator[level].SetPresent(this, remap[pos]);1550});1551}1553void SingletonClusterImpl::Merge(TxGraphImpl&, int, Cluster&) noexcept1554{1555// Nothing can be merged into a singleton; it should have been converted to GenericClusterImpl first.1556Assume(false);1557}1559void GenericClusterImpl::ApplyDependencies(TxGraphImpl& graph, int level, std::span<std::pair<GraphIndex, GraphIndex>> to_apply) noexcept1560{1561// Sort the list of dependencies to apply by child, so those can be applied in batch.1562std::ranges::sort(to_apply, [](auto& a, auto& b) { return a.second < b.second; });1563// Iterate over groups of to-be-added dependencies with the same child.1564auto it = to_apply.begin();1565while (it != to_apply.end()) {1566auto& first_child = graph.m_entries[it->second].m_locator[level];1567const auto child_idx = first_child.index;1568// Iterate over all to-be-added dependencies within that same child, gather the relevant1569// parents.1570SetType parents;1571while (it != to_apply.end()) {1572auto& child = graph.m_entries[it->second].m_locator[level];1573auto& parent = graph.m_entries[it->first].m_locator[level];1574Assume(child.cluster == this && parent.cluster == this);1575if (child.index != child_idx) break;1576parents.Set(parent.index);1577++it;1578}1579// Push all dependencies to the underlying DepGraph. Note that this is O(N) in the size of1580// the cluster, regardless of the number of parents being added, so batching them together1581// has a performance benefit.1582m_depgraph.AddDependencies(parents, child_idx);1583}1585// Finally set the cluster to NEEDS_FIX, so its linearization is fixed the next time it is1586// attempted to be made ACCEPTABLE.1587Assume(!NeedsSplitting());1588Assume(!IsOversized());1589graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_FIX);1591// Finally push the changes to graph.m_entries.1592Updated(graph, /*level=*/level, /*rename=*/false);1593}1595void SingletonClusterImpl::ApplyDependencies(TxGraphImpl&, int, std::span<std::pair<GraphIndex, GraphIndex>>) noexcept1596{1597// Nothing can actually be applied.1598Assume(false);1599}1601TxGraphImpl::~TxGraphImpl() noexcept1602{1603// If Refs outlive the TxGraphImpl they refer to, unlink them, so that their destructor does not1604// try to reach into a non-existing TxGraphImpl anymore.1605for (auto& entry : m_entries) {1606if (entry.m_ref != nullptr) {1607GetRefGraph(*entry.m_ref) = nullptr;1608}1609}1610}1612std::unique_ptr<Cluster> TxGraphImpl::ExtractCluster(int level, QualityLevel quality, ClusterSetIndex setindex) noexcept1613{1614Assume(quality != QualityLevel::NONE);1616auto& clusterset = GetClusterSet(level);1617auto& quality_clusters = clusterset.m_clusters[int(quality)];1618Assume(setindex < quality_clusters.size());1620// Extract the Cluster-owning unique_ptr.1621std::unique_ptr<Cluster> ret = std::move(quality_clusters[setindex]);1622ret->m_quality = QualityLevel::NONE;1623ret->m_setindex = ClusterSetIndex(-1);1625// Clean up space in quality_cluster.1626auto max_setindex = quality_clusters.size() - 1;1627if (setindex != max_setindex) {1628// If the cluster was not the last element of quality_clusters, move that to take its place.1629quality_clusters.back()->m_setindex = setindex;1630quality_clusters[setindex] = std::move(quality_clusters.back());1631}1632// The last element of quality_clusters is now unused; drop it.1633quality_clusters.pop_back();1635return ret;1636}1638ClusterSetIndex TxGraphImpl::InsertCluster(int level, std::unique_ptr<Cluster>&& cluster, QualityLevel quality) noexcept1639{1640// Cannot insert with quality level NONE (as that would mean not inserted).1641Assume(quality != QualityLevel::NONE);1642// The passed-in Cluster must not currently be in the TxGraphImpl.1643Assume(cluster->m_quality == QualityLevel::NONE);1645// Append it at the end of the relevant TxGraphImpl::m_cluster.1646auto& clusterset = GetClusterSet(level);1647auto& quality_clusters = clusterset.m_clusters[int(quality)];1648ClusterSetIndex ret = quality_clusters.size();1649cluster->m_quality = quality;1650cluster->m_setindex = ret;1651quality_clusters.push_back(std::move(cluster));1652return ret;1653}1655void TxGraphImpl::SetClusterQuality(int level, QualityLevel old_quality, ClusterSetIndex old_index, QualityLevel new_quality) noexcept1656{1657Assume(new_quality != QualityLevel::NONE);1659// Don't do anything if the quality did not change.1660if (old_quality == new_quality) return;1661// Extract the cluster from where it currently resides.1662auto cluster_ptr = ExtractCluster(level, old_quality, old_index);1663// And re-insert it where it belongs.1664InsertCluster(level, std::move(cluster_ptr), new_quality);1665}1667void TxGraphImpl::DeleteCluster(Cluster& cluster, int level) noexcept1668{1669// Extract the cluster from where it currently resides.1670auto cluster_ptr = ExtractCluster(level, cluster.m_quality, cluster.m_setindex);1671// And throw it away.1672cluster_ptr.reset();1673}1675std::pair<Cluster*, int> TxGraphImpl::FindClusterAndLevel(GraphIndex idx, int level) const noexcept1676{1677Assume(level >= 0 && level <= GetTopLevel());1678auto& entry = m_entries[idx];1679// Search the entry's locators from top to bottom.1680for (int l = level; l >= 0; --l) {1681// If the locator is missing, dig deeper; it may exist at a lower level and therefore be1682// implicitly existing at this level too.1683if (entry.m_locator[l].IsMissing()) continue;1684// If the locator has the entry marked as explicitly removed, stop.1685if (entry.m_locator[l].IsRemoved()) break;1686// Otherwise, we have found the topmost ClusterSet that contains this entry.1687return {entry.m_locator[l].cluster, l};1688}1689// If no non-empty locator was found, or an explicitly removed was hit, return nothing.1690return {nullptr, -1};1691}1693Cluster* TxGraphImpl::PullIn(Cluster* cluster, int level) noexcept1694{1695int to_level = GetTopLevel();1696Assume(to_level == 1);1697Assume(level <= to_level);1698// Copy the Cluster from main to staging, if it's not already there.1699if (level == 0) {1700// Make the Cluster Acceptable before copying. This isn't strictly necessary, but doing it1701// now avoids doing double work later.1702MakeAcceptable(*cluster, level);1703cluster = cluster->CopyToStaging(*this);1704}1705return cluster;1706}1708void TxGraphImpl::ApplyRemovals(int up_to_level) noexcept1709{1710Assume(up_to_level >= 0 && up_to_level <= GetTopLevel());1711for (int level = 0; level <= up_to_level; ++level) {1712auto& clusterset = GetClusterSet(level);1713auto& to_remove = clusterset.m_to_remove;1714// Skip if there is nothing to remove in this level.1715if (to_remove.empty()) continue;1716// Pull in all Clusters that are not in staging.1717if (level == 1) {1718for (GraphIndex index : to_remove) {1719auto [cluster, cluster_level] = FindClusterAndLevel(index, level);1720if (cluster != nullptr) PullIn(cluster, cluster_level);1721}1722}1723// Group the set of to-be-removed entries by Cluster::m_sequence.1724std::ranges::sort(to_remove, [&](GraphIndex a, GraphIndex b) noexcept {1725Cluster* cluster_a = m_entries[a].m_locator[level].cluster;1726Cluster* cluster_b = m_entries[b].m_locator[level].cluster;1727return CompareClusters(cluster_a, cluster_b) < 0;1728});1729// Process per Cluster.1730std::span to_remove_span{to_remove};1731while (!to_remove_span.empty()) {1732Cluster* cluster = m_entries[to_remove_span.front()].m_locator[level].cluster;1733if (cluster != nullptr) {1734// If the first to_remove_span entry's Cluster exists, hand to_remove_span to it, so it1735// can pop off whatever applies to it.1736cluster->ApplyRemovals(*this, level, to_remove_span);1737} else {1738// Otherwise, skip this already-removed entry. This may happen when1739// RemoveTransaction was called twice on the same Ref, for example.1740to_remove_span = to_remove_span.subspan(1);1741}1742}1743to_remove.clear();1744}1745Compact();1746}1748void TxGraphImpl::SwapIndexes(GraphIndex a, GraphIndex b, std::vector<Cluster*>& affected_main) noexcept1749{1750Assume(a < m_entries.size());
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.