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.
2001for (size_t i = 0; i < partition_data.size(); ++i) {2002auto& data = partition_data[i];2003// Find the sequence of the representative of the partition Cluster i is in, and store2004// it with the Cluster.2005auto rep_seq = find_root_fn(&data)->sequence;2006an_clusters[i].second = rep_seq;2007// Find all dependencies whose child Cluster is Cluster i, and annotate them with rep.2008while (deps_it != an_deps.end()) {2009auto [par, chl] = deps_it->first;2010auto chl_cluster = FindCluster(chl, level);2011Assume(chl_cluster != nullptr);2012if (chl_cluster->m_sequence > data.sequence) break;2013deps_it->second = rep_seq;2014++deps_it;2015}2016}2017}2019// Sort both an_clusters and an_deps by sequence number of the representative of the2020// partition they are in, grouping all those applying to the same partition together.2021std::ranges::sort(an_deps, [](auto& a, auto& b) noexcept { return a.second < b.second; });2022std::ranges::sort(an_clusters, [](auto& a, auto& b) noexcept { return a.second < b.second; });2024// Translate the resulting cluster groups to the m_group_data structure, and the dependencies2025// back to m_deps_to_add.2026clusterset.m_group_data = GroupData{};2027clusterset.m_group_data->m_group_clusters.reserve(an_clusters.size());2028clusterset.m_deps_to_add.clear();2029clusterset.m_deps_to_add.reserve(an_deps.size());2030clusterset.m_oversized = false;2031auto an_deps_it = an_deps.begin();2032auto an_clusters_it = an_clusters.begin();2033while (an_clusters_it != an_clusters.end()) {2034// Process all clusters/dependencies belonging to the partition with representative rep.2035auto rep = an_clusters_it->second;2036// Create and initialize a new GroupData entry for the partition.2037auto& new_entry = clusterset.m_group_data->m_groups.emplace_back();2038new_entry.m_cluster_offset = clusterset.m_group_data->m_group_clusters.size();2039new_entry.m_cluster_count = 0;2040new_entry.m_deps_offset = clusterset.m_deps_to_add.size();2041new_entry.m_deps_count = 0;2042uint32_t total_count{0};2043uint64_t total_size{0};2044// Add all its clusters to it (copying those from an_clusters to m_group_clusters).2045while (an_clusters_it != an_clusters.end() && an_clusters_it->second == rep) {2046clusterset.m_group_data->m_group_clusters.push_back(an_clusters_it->first);2047total_count += an_clusters_it->first->GetTxCount();2048total_size += an_clusters_it->first->GetTotalTxSize();2049++an_clusters_it;2050++new_entry.m_cluster_count;2051}2052// Add all its dependencies to it (copying those back from an_deps to m_deps_to_add).2053while (an_deps_it != an_deps.end() && an_deps_it->second == rep) {2054clusterset.m_deps_to_add.push_back(an_deps_it->first);2055++an_deps_it;2056++new_entry.m_deps_count;2057}2058// Detect oversizedness.2059if (total_count > m_max_cluster_count || total_size > m_max_cluster_size) {2060clusterset.m_oversized = true;2061}2062}2063Assume(an_deps_it == an_deps.end());2064Assume(an_clusters_it == an_clusters.end());2065Compact();2066}2068void TxGraphImpl::Merge(std::span<Cluster*> to_merge, int level) noexcept2069{2070Assume(!to_merge.empty());2071// Nothing to do if a group consists of just a single Cluster.2072if (to_merge.size() == 1) return;2074// Move the largest Cluster to the front of to_merge. As all transactions in other to-be-merged2075// Clusters will be moved to that one, putting the largest one first minimizes the number of2076// moves.2077size_t max_size_pos{0};2078DepGraphIndex max_size = to_merge[max_size_pos]->GetTxCount();2079GetClusterSet(level).m_cluster_usage -= to_merge[max_size_pos]->TotalMemoryUsage();2080DepGraphIndex total_size = max_size;2081for (size_t i = 1; i < to_merge.size(); ++i) {2082GetClusterSet(level).m_cluster_usage -= to_merge[i]->TotalMemoryUsage();2083DepGraphIndex size = to_merge[i]->GetTxCount();2084total_size += size;2085if (size > max_size) {2086max_size_pos = i;2087max_size = size;2088}2089}2090if (max_size_pos != 0) std::swap(to_merge[0], to_merge[max_size_pos]);2092size_t start_idx = 1;2093Cluster* into_cluster = to_merge[0];2094if (total_size > into_cluster->GetMaxTxCount()) {2095// The into_merge cluster is too small to fit all transactions being merged. Construct a2096// a new Cluster using an implementation that matches the total size, and merge everything2097// in there.2098auto new_cluster = CreateEmptyCluster(total_size);2099into_cluster = new_cluster.get();2100InsertCluster(level, std::move(new_cluster), QualityLevel::OPTIMAL);2101start_idx = 0;2102}2104// Merge all further Clusters in the group into the result (first one, or new one), and delete2105// them.2106for (size_t i = start_idx; i < to_merge.size(); ++i) {2107into_cluster->Merge(*this, level, *to_merge[i]);2108DeleteCluster(*to_merge[i], level);2109}2110into_cluster->Compact();2111GetClusterSet(level).m_cluster_usage += into_cluster->TotalMemoryUsage();2112}2114void TxGraphImpl::ApplyDependencies(int level) noexcept2115{2116auto& clusterset = GetClusterSet(level);2117// Do not bother computing groups if we already know the result will be oversized.2118if (clusterset.m_oversized == true) return;2119// Compute the groups of to-be-merged Clusters (which also applies all removals, and splits).2120GroupClusters(level);2121Assume(clusterset.m_group_data.has_value());2122// Nothing to do if there are no dependencies to be added.2123if (clusterset.m_deps_to_add.empty()) return;2124// Dependencies cannot be applied if it would result in oversized clusters.2125if (clusterset.m_oversized == true) return;2127// For each group of to-be-merged Clusters.2128for (const auto& group_entry : clusterset.m_group_data->m_groups) {2129auto cluster_span = std::span{clusterset.m_group_data->m_group_clusters}2130.subspan(group_entry.m_cluster_offset, group_entry.m_cluster_count);2131// Pull in all the Clusters that contain dependencies.2132if (level == 1) {2133for (Cluster*& cluster : cluster_span) {2134cluster = PullIn(cluster, cluster->GetLevel(*this));2135}2136}2137// Invoke Merge() to merge them into a single Cluster.2138Merge(cluster_span, level);2139// Actually apply all to-be-added dependencies (all parents and children from this grouping2140// belong to the same Cluster at this point because of the merging above).2141auto deps_span = std::span{clusterset.m_deps_to_add}2142.subspan(group_entry.m_deps_offset, group_entry.m_deps_count);2143Assume(!deps_span.empty());2144const auto& loc = m_entries[deps_span[0].second].m_locator[level];2145Assume(loc.IsPresent());2146loc.cluster->ApplyDependencies(*this, level, deps_span);2147}2149// Wipe the list of to-be-added dependencies now that they are applied.2150clusterset.m_deps_to_add.clear();2151Compact();2152// Also no further Cluster mergings are needed (note that we clear, but don't set to2153// std::nullopt, as that would imply the groupings are unknown).2154clusterset.m_group_data = GroupData{};2155}2157std::pair<uint64_t, bool> GenericClusterImpl::Relinearize(TxGraphImpl& graph, int level, uint64_t max_cost) noexcept2158{2159// We can only relinearize Clusters that do not need splitting.2160Assume(!NeedsSplitting());2161// No work is required for Clusters which are already optimally linearized.2162if (IsOptimal()) return {0, false};2163// Invoke the actual linearization algorithm (passing in the existing one).2164uint64_t rng_seed = graph.m_rng.rand64();2165const auto fallback_order = [&](DepGraphIndex a, DepGraphIndex b) noexcept {2166const auto ref_a = graph.m_entries[m_mapping[a]].m_ref;2167const auto ref_b = graph.m_entries[m_mapping[b]].m_ref;2168return graph.m_fallback_order(*ref_a, *ref_b);2169};2170auto [linearization, optimal, cost] = Linearize(2171/*depgraph=*/m_depgraph,2172/*max_cost=*/max_cost,2173/*rng_seed=*/rng_seed,2174/*fallback_order=*/fallback_order,2175/*old_linearization=*/m_linearization,2176/*is_topological=*/IsTopological());2177// Postlinearize to improve the linearization (if optimal, only the sub-chunk order).2178// This also guarantees that all chunks are connected (even when non-optimal).2179PostLinearize(m_depgraph, linearization);2180// Update the linearization.2181m_linearization = std::move(linearization);2182// Update the Cluster's quality.2183bool improved = false;2184if (optimal) {2185graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::OPTIMAL);2186improved = true;2187} else if (max_cost >= graph.m_acceptable_cost && !IsAcceptable()) {2188graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::ACCEPTABLE);2189improved = true;2190} else if (!IsTopological()) {2191graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);2192improved = true;2193}2194// Update the Entry objects.2195Updated(graph, /*level=*/level, /*rename=*/false);2196return {cost, improved};2197}2199std::pair<uint64_t, bool> SingletonClusterImpl::Relinearize(TxGraphImpl& graph, int level, uint64_t max_cost) noexcept2200{2201// All singletons are optimal, oversized, or need splitting. Each of these precludes2202// Relinearize from being called.2203assert(false);2204return {0, false};2205}2207void TxGraphImpl::MakeAcceptable(Cluster& cluster, int level) noexcept2208{2209// Relinearize the Cluster if needed.2210if (!cluster.NeedsSplitting() && !cluster.IsAcceptable() && !cluster.IsOversized()) {2211cluster.Relinearize(*this, level, m_acceptable_cost);2212}2213}2215void TxGraphImpl::MakeAllAcceptable(int level) noexcept2216{2217ApplyDependencies(level);2218auto& clusterset = GetClusterSet(level);2219if (clusterset.m_oversized == true) return;2220for (auto quality : {QualityLevel::NEEDS_FIX, QualityLevel::NEEDS_RELINEARIZE}) {2221auto& queue = clusterset.m_clusters[int(quality)];2222while (!queue.empty()) {2223MakeAcceptable(*queue.back().get(), level);2224}2225}2226}2228GenericClusterImpl::GenericClusterImpl(uint64_t sequence) noexcept : Cluster{sequence} {}2230void TxGraphImpl::AddTransaction(Ref& arg, const FeePerWeight& feerate) noexcept2231{2232Assume(m_main_chunkindex_observers == 0 || GetTopLevel() != 0);2233Assume(feerate.size > 0);2234Assume(GetRefGraph(arg) == nullptr);2235// Construct a new Entry, and link it with the Ref.2236auto idx = m_entries.size();2237m_entries.emplace_back();2238auto& entry = m_entries.back();2239entry.m_main_chunkindex_iterator = m_main_chunkindex.end();2240entry.m_ref = &arg;2241GetRefGraph(arg) = this;2242GetRefIndex(arg) = idx;2243// Construct a new singleton Cluster (which is necessarily optimally linearized).2244bool oversized = uint64_t(feerate.size) > m_max_cluster_size;2245auto cluster = CreateEmptyCluster(1);2246cluster->AppendTransaction(idx, feerate);2247auto cluster_ptr = cluster.get();2248int level = GetTopLevel();2249auto& clusterset = GetClusterSet(level);2250InsertCluster(level, std::move(cluster), oversized ? QualityLevel::OVERSIZED_SINGLETON : QualityLevel::OPTIMAL);
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.