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.
1251// Update memory usage.1252graph.GetClusterSet(/*level=*/1).m_cluster_usage += ptr->TotalMemoryUsage();1253return ptr;1254}1256void GenericClusterImpl::ApplyRemovals(TxGraphImpl& graph, int level, std::span<GraphIndex>& to_remove) noexcept1257{1258// Iterate over the prefix of to_remove that applies to this cluster.1259Assume(!to_remove.empty());1260SetType todo;1261graph.GetClusterSet(level).m_cluster_usage -= TotalMemoryUsage();1262do {1263GraphIndex idx = to_remove.front();1264Assume(idx < graph.m_entries.size());1265auto& entry = graph.m_entries[idx];1266auto& locator = entry.m_locator[level];1267// Stop once we hit an entry that applies to another Cluster.1268if (locator.cluster != this) break;1269// - Remember it in a set of to-remove DepGraphIndexes.1270todo.Set(locator.index);1271// - Remove from m_mapping. This isn't strictly necessary as unused positions in m_mapping1272// are just never accessed, but set it to -1 here to increase the ability to detect a bug1273// that causes it to be accessed regardless.1274m_mapping[locator.index] = GraphIndex(-1);1275// - Remove its linearization index from the Entry (if in main).1276if (level == 0) {1277entry.m_main_lin_index = LinearizationIndex(-1);1278}1279// - Mark it as missing/removed in the Entry's locator.1280graph.ClearLocator(level, idx, m_quality == QualityLevel::OVERSIZED_SINGLETON);1281to_remove = to_remove.subspan(1);1282} while(!to_remove.empty());1284Assume(todo.Any());1285// Wipe from the Cluster's DepGraph (this is O(n) regardless of the number of entries1286// removed, so we benefit from batching all the removals).1287m_depgraph.RemoveTransactions(todo);1288m_mapping.resize(m_depgraph.PositionRange());1290// Filter removed transactions out of m_linearization.1291m_linearization.erase(std::remove_if(m_linearization.begin(), m_linearization.end(),1292[&](auto pos) { return todo[pos]; }),1293m_linearization.end());1295Compact();1296graph.GetClusterSet(level).m_cluster_usage += TotalMemoryUsage();1297auto new_quality = IsTopological() ? QualityLevel::NEEDS_SPLIT : QualityLevel::NEEDS_SPLIT_FIX;1298graph.SetClusterQuality(level, m_quality, m_setindex, new_quality);1299Updated(graph, /*level=*/level, /*rename=*/false);1300}1302void SingletonClusterImpl::ApplyRemovals(TxGraphImpl& graph, int level, std::span<GraphIndex>& to_remove) noexcept1303{1304// We can only remove the one transaction this Cluster has.1305Assume(!to_remove.empty());1306Assume(GetTxCount());1307Assume(to_remove.front() == m_graph_index);1308// Pop all copies of m_graph_index from the front of to_remove (at least one, but there may be1309// multiple).1310do {1311to_remove = to_remove.subspan(1);1312} while (!to_remove.empty() && to_remove.front() == m_graph_index);1313// Clear this cluster.1314graph.ClearLocator(level, m_graph_index, m_quality == QualityLevel::OVERSIZED_SINGLETON);1315m_graph_index = NO_GRAPH_INDEX;1316graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_SPLIT);1317// No need to account for m_cluster_usage changes here, as SingletonClusterImpl has constant1318// memory usage.1319}1321void GenericClusterImpl::Clear(TxGraphImpl& graph, int level) noexcept1322{1323Assume(GetTxCount());1324graph.GetClusterSet(level).m_cluster_usage -= TotalMemoryUsage();1325for (auto i : m_linearization) {1326graph.ClearLocator(level, m_mapping[i], m_quality == QualityLevel::OVERSIZED_SINGLETON);1327}1328m_depgraph = {};1329m_linearization.clear();1330m_mapping.clear();1331}1333void SingletonClusterImpl::Clear(TxGraphImpl& graph, int level) noexcept1334{1335Assume(GetTxCount());1336graph.GetClusterSet(level).m_cluster_usage -= TotalMemoryUsage();1337graph.ClearLocator(level, m_graph_index, m_quality == QualityLevel::OVERSIZED_SINGLETON);1338m_graph_index = NO_GRAPH_INDEX;1339}1341void GenericClusterImpl::MoveToMain(TxGraphImpl& graph) noexcept1342{1343for (auto i : m_linearization) {1344GraphIndex idx = m_mapping[i];1345auto& entry = graph.m_entries[idx];1346entry.m_locator[1].SetMissing();1347}1348auto quality = m_quality;1349// Subtract memory usage from staging and add it to main.1350graph.GetClusterSet(/*level=*/1).m_cluster_usage -= TotalMemoryUsage();1351graph.GetClusterSet(/*level=*/0).m_cluster_usage += TotalMemoryUsage();1352// Remove cluster itself from staging and add it to main.1353auto cluster = graph.ExtractCluster(1, quality, m_setindex);1354graph.InsertCluster(/*level=*/0, std::move(cluster), quality);1355Updated(graph, /*level=*/0, /*rename=*/false);1356}1358void SingletonClusterImpl::MoveToMain(TxGraphImpl& graph) noexcept1359{1360if (GetTxCount()) {1361auto& entry = graph.m_entries[m_graph_index];1362entry.m_locator[1].SetMissing();1363}1364auto quality = m_quality;1365graph.GetClusterSet(/*level=*/1).m_cluster_usage -= TotalMemoryUsage();1366auto cluster = graph.ExtractCluster(/*level=*/1, quality, m_setindex);1367graph.InsertCluster(/*level=*/0, std::move(cluster), quality);1368graph.GetClusterSet(/*level=*/0).m_cluster_usage += TotalMemoryUsage();1369Updated(graph, /*level=*/0, /*rename=*/false);1370}1372void GenericClusterImpl::Compact() noexcept1373{1374m_linearization.shrink_to_fit();1375m_mapping.shrink_to_fit();1376m_depgraph.Compact();1377}1379void SingletonClusterImpl::Compact() noexcept1380{1381// Nothing to compact; SingletonClusterImpl is constant size.1382}1384void GenericClusterImpl::AppendChunkFeerates(std::vector<FeeFrac>& ret) const noexcept1385{1386auto chunk_feerates = ChunkLinearization(m_depgraph, m_linearization);1387ret.reserve(ret.size() + chunk_feerates.size());1388ret.insert(ret.end(), chunk_feerates.begin(), chunk_feerates.end());1389}1391void SingletonClusterImpl::AppendChunkFeerates(std::vector<FeeFrac>& ret) const noexcept1392{1393if (GetTxCount()) {1394ret.push_back(m_feerate);1395}1396}1398uint64_t GenericClusterImpl::AppendTrimData(std::vector<TrimTxData>& ret, std::vector<std::pair<GraphIndex, GraphIndex>>& deps) const noexcept1399{1400Assume(IsAcceptable());1401auto linchunking = ChunkLinearizationInfo(m_depgraph, m_linearization);1402LinearizationIndex pos{0};1403uint64_t size{0};1404auto prev_index = GraphIndex(-1);1405// Iterate over the chunks of this cluster's linearization.1406for (const auto& [chunk, chunk_feerate] : linchunking) {1407// Iterate over the transactions of that chunk, in linearization order.1408auto chunk_tx_count = chunk.Count();1409for (unsigned j = 0; j < chunk_tx_count; ++j) {1410auto cluster_idx = m_linearization[pos];1411// The transaction must appear in the chunk.1412Assume(chunk[cluster_idx]);1413// Construct a new element in ret.1414auto& entry = ret.emplace_back();1415entry.m_chunk_feerate = FeePerWeight::FromFeeFrac(chunk_feerate);1416entry.m_index = m_mapping[cluster_idx];1417// If this is not the first transaction of the cluster linearization, it has an1418// implicit dependency on its predecessor.1419if (pos != 0) deps.emplace_back(prev_index, entry.m_index);1420prev_index = entry.m_index;1421entry.m_tx_size = m_depgraph.FeeRate(cluster_idx).size;1422size += entry.m_tx_size;1423++pos;1424}1425}1426return size;1427}1429uint64_t SingletonClusterImpl::AppendTrimData(std::vector<TrimTxData>& ret, std::vector<std::pair<GraphIndex, GraphIndex>>& deps) const noexcept1430{1431if (!GetTxCount()) return 0;1432auto& entry = ret.emplace_back();1433entry.m_chunk_feerate = m_feerate;1434entry.m_index = m_graph_index;1435entry.m_tx_size = m_feerate.size;1436return m_feerate.size;1437}1439bool GenericClusterImpl::Split(TxGraphImpl& graph, int level) noexcept1440{1441// This function can only be called when the Cluster needs splitting.1442Assume(NeedsSplitting());1443// Determine the new quality the split-off Clusters will have.1444QualityLevel new_quality = IsTopological() ? QualityLevel::NEEDS_RELINEARIZE : QualityLevel::NEEDS_FIX;1445/** Which positions are still left in this Cluster. */1446auto todo = m_depgraph.Positions();1447/** Mapping from transaction positions in this Cluster to the Cluster where it ends up, and1448* its position therein. */1449std::vector<std::pair<Cluster*, DepGraphIndex>> remap(m_depgraph.PositionRange());1450std::vector<Cluster*> new_clusters;1451bool first{true};1452// Iterate over the connected components of this Cluster's m_depgraph.1453while (todo.Any()) {1454auto component = m_depgraph.FindConnectedComponent(todo);1455auto component_size = component.Count();1456auto split_quality = component_size <= 1 ? QualityLevel::OPTIMAL : new_quality;1457if (first && component == todo && SetType::Fill(component_size) == component && component_size >= MIN_INTENDED_TX_COUNT) {1458// The existing Cluster is an entire component, without holes. Leave it be, but update1459// its quality. If there are holes, we continue, so that the Cluster is reconstructed1460// without holes, reducing memory usage. If the component's size is below the intended1461// transaction count for this Cluster implementation, continue so that it can get1462// converted.1463Assume(todo == m_depgraph.Positions());1464graph.SetClusterQuality(level, m_quality, m_setindex, split_quality);1465// If this made the quality ACCEPTABLE or OPTIMAL, we need to compute and cache its1466// chunking.1467Updated(graph, /*level=*/level, /*rename=*/false);1468return false;1469}1470first = false;1471// Construct a new Cluster to hold the found component.1472auto new_cluster = graph.CreateEmptyCluster(component_size);1473new_clusters.push_back(new_cluster.get());1474// Remember that all the component's transactions go to this new Cluster. The positions1475// will be determined below, so use -1 for now.1476for (auto i : component) {1477remap[i] = {new_cluster.get(), DepGraphIndex(-1)};1478}1479graph.InsertCluster(level, std::move(new_cluster), split_quality);1480todo -= component;1481}1482// We have to split the Cluster up. Remove accounting for the existing one first.1483graph.GetClusterSet(level).m_cluster_usage -= TotalMemoryUsage();1484// Redistribute the transactions.1485for (auto i : m_linearization) {1486/** The cluster which transaction originally in position i is moved to. */1487Cluster* new_cluster = remap[i].first;1488// Copy the transaction to the new cluster's depgraph, and remember the position.1489remap[i].second = new_cluster->AppendTransaction(m_mapping[i], FeePerWeight::FromFeeFrac(m_depgraph.FeeRate(i)));1490}1491// Redistribute the dependencies.1492for (auto i : m_linearization) {1493/** The cluster transaction in position i is moved to. */1494Cluster* new_cluster = remap[i].first;1495// Copy its parents, translating positions.1496SetType new_parents;1497for (auto par : m_depgraph.GetReducedParents(i)) new_parents.Set(remap[par].second);1498new_cluster->AddDependencies(new_parents, remap[i].second);1499}1500// Update all the Locators of moved transactions, and memory usage.
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.