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.
2501}2503std::vector<TxGraph::Ref*> TxGraphImpl::GetDescendantsUnion(std::span<const Ref* const> args, Level level_select) noexcept2504{2505// Apply all dependencies, as the result might be incorrect otherwise.2506size_t level = GetSpecifiedLevel(level_select);2507ApplyDependencies(level);2508// Ancestry cannot be known if unapplied dependencies remain.2509Assume(GetClusterSet(level).m_deps_to_add.empty());2511// Translate args to matches.2512std::vector<std::pair<Cluster*, DepGraphIndex>> matches;2513matches.reserve(args.size());2514for (auto arg : args) {2515Assume(arg);2516// Skip empty Refs.2517if (GetRefGraph(*arg) == nullptr) continue;2518Assume(GetRefGraph(*arg) == this);2519// Find the Cluster the argument is in, and skip if none is found.2520auto [cluster, cluster_level] = FindClusterAndLevel(GetRefIndex(*arg), level);2521if (cluster == nullptr) continue;2522// Append to matches.2523matches.emplace_back(cluster, m_entries[GetRefIndex(*arg)].m_locator[cluster_level].index);2524}2525// Group by Cluster.2526std::ranges::sort(matches, [](auto& a, auto& b) noexcept { return CompareClusters(a.first, b.first) < 0; });2527// Dispatch to the Clusters.2528std::span match_span(matches);2529std::vector<TxGraph::Ref*> ret;2530while (!match_span.empty()) {2531match_span.front().first->GetDescendantRefs(*this, match_span, ret);2532}2533return ret;2534}2536std::vector<TxGraph::Ref*> TxGraphImpl::GetCluster(const Ref& arg, Level level_select) noexcept2537{2538// Return the empty vector if the Ref is empty (which may be indicative of the transaction2539// having been removed already.2540if (GetRefGraph(arg) == nullptr) return {};2541Assume(GetRefGraph(arg) == this);2542// Apply all removals and dependencies, as the result might be incorrect otherwise.2543size_t level = GetSpecifiedLevel(level_select);2544ApplyDependencies(level);2545// Cluster linearization cannot be known if unapplied dependencies remain.2546Assume(GetClusterSet(level).m_deps_to_add.empty());2547// Find the Cluster the argument is in, and return the empty vector if it isn't in any.2548auto [cluster, cluster_level] = FindClusterAndLevel(GetRefIndex(arg), level);2549if (cluster == nullptr) return {};2550// Make sure the Cluster has an acceptable quality level, and then dispatch to it.2551MakeAcceptable(*cluster, cluster_level);2552std::vector<TxGraph::Ref*> ret(cluster->GetTxCount());2553cluster->GetClusterRefs(*this, ret, 0);2554return ret;2555}2557TxGraph::GraphIndex TxGraphImpl::GetTransactionCount(Level level_select) noexcept2558{2559size_t level = GetSpecifiedLevel(level_select);2560ApplyRemovals(level);2561return GetClusterSet(level).m_txcount;2562}2564FeePerWeight TxGraphImpl::GetIndividualFeerate(const Ref& arg) noexcept2565{2566// Return the empty FeePerWeight if the passed Ref is empty.2567if (GetRefGraph(arg) == nullptr) return {};2568Assume(GetRefGraph(arg) == this);2569// Find the cluster the argument is in (the level does not matter as individual feerates will2570// be identical if it occurs in both), and return the empty FeePerWeight if it isn't in any.2571Cluster* cluster{nullptr};2572int level;2573for (level = 0; level <= GetTopLevel(); ++level) {2574// Apply removals, so that we can correctly report FeePerWeight{} for non-existing2575// transactions.2576ApplyRemovals(level);2577if (m_entries[GetRefIndex(arg)].m_locator[level].IsPresent()) {2578cluster = m_entries[GetRefIndex(arg)].m_locator[level].cluster;2579break;2580}2581}2582if (cluster == nullptr) return {};2583// Dispatch to the Cluster.2584return cluster->GetIndividualFeerate(m_entries[GetRefIndex(arg)].m_locator[level].index);2585}2587FeePerWeight TxGraphImpl::GetMainChunkFeerate(const Ref& arg) noexcept2588{2589// Return the empty FeePerWeight if the passed Ref is empty.2590if (GetRefGraph(arg) == nullptr) return {};2591Assume(GetRefGraph(arg) == this);2592// Apply all removals and dependencies, as the result might be inaccurate otherwise.2593ApplyDependencies(/*level=*/0);2594// Chunk feerates cannot be accurately known if unapplied dependencies remain.2595Assume(m_main_clusterset.m_deps_to_add.empty());2596// Find the cluster the argument is in, and return the empty FeePerWeight if it isn't in any.2597auto [cluster, cluster_level] = FindClusterAndLevel(GetRefIndex(arg), /*level=*/0);2598if (cluster == nullptr) return {};2599// Make sure the Cluster has an acceptable quality level, and then return the transaction's2600// chunk feerate.2601MakeAcceptable(*cluster, cluster_level);2602const auto& entry = m_entries[GetRefIndex(arg)];2603return entry.m_main_chunk_feerate;2604}2606bool TxGraphImpl::IsOversized(Level level_select) noexcept2607{2608size_t level = GetSpecifiedLevel(level_select);2609auto& clusterset = GetClusterSet(level);2610if (clusterset.m_oversized.has_value()) {2611// Return cached value if known.2612return *clusterset.m_oversized;2613}2614ApplyRemovals(level);2615if (clusterset.m_txcount_oversized > 0) {2616clusterset.m_oversized = true;2617} else {2618// Find which Clusters will need to be merged together, as that is where the oversize2619// property is assessed.2620GroupClusters(level);2621}2622Assume(clusterset.m_oversized.has_value());2623return *clusterset.m_oversized;2624}2626void TxGraphImpl::StartStaging() noexcept2627{2628// Staging cannot already exist.2629Assume(!m_staging_clusterset.has_value());2630// Apply all remaining dependencies in main before creating a staging graph. Once staging2631// exists, we cannot merge Clusters anymore (because of interference with Clusters being2632// pulled into staging), so to make sure all inspectors are available (if not oversized), do2633// all merging work now. Call SplitAll() first, so that even if ApplyDependencies does not do2634// any thing due to knowing the result is oversized, splitting is still performed.2635SplitAll(0);2636ApplyDependencies(0);2637// Construct the staging ClusterSet.2638m_staging_clusterset.emplace();2639// Copy statistics, precomputed data, and to-be-applied dependencies (only if oversized) to2640// the new graph. To-be-applied removals will always be empty at this point.2641m_staging_clusterset->m_txcount = m_main_clusterset.m_txcount;2642m_staging_clusterset->m_txcount_oversized = m_main_clusterset.m_txcount_oversized;2643m_staging_clusterset->m_deps_to_add = m_main_clusterset.m_deps_to_add;2644m_staging_clusterset->m_group_data = m_main_clusterset.m_group_data;2645m_staging_clusterset->m_oversized = m_main_clusterset.m_oversized;2646Assume(m_staging_clusterset->m_oversized.has_value());2647m_staging_clusterset->m_cluster_usage = 0;2648}2650void TxGraphImpl::AbortStaging() noexcept2651{2652// Staging must exist.2653Assume(m_staging_clusterset.has_value());2654// Mark all removed transactions as Missing (so the staging locator for these transactions2655// can be reused if another staging is created).2656for (auto idx : m_staging_clusterset->m_removed) {2657m_entries[idx].m_locator[1].SetMissing();2658}2659// Do the same with the non-removed transactions in staging Clusters.2660for (int quality = 0; quality < int(QualityLevel::NONE); ++quality) {2661for (auto& cluster : m_staging_clusterset->m_clusters[quality]) {2662cluster->MakeStagingTransactionsMissing(*this);2663}2664}2665// Destroy the staging ClusterSet.2666m_staging_clusterset.reset();2667Compact();2668if (!m_main_clusterset.m_group_data.has_value()) {2669// In case m_oversized in main was kept after a Ref destruction while staging exists, we2670// need to re-evaluate m_oversized now.2671if (m_main_clusterset.m_to_remove.empty() && m_main_clusterset.m_txcount_oversized > 0) {2672// It is possible that a Ref destruction caused a removal in main while staging existed.2673// In this case, m_txcount_oversized may be inaccurate.2674m_main_clusterset.m_oversized = true;2675} else {2676m_main_clusterset.m_oversized = std::nullopt;2677}2678}2679}2681void TxGraphImpl::CommitStaging() noexcept2682{2683// Staging must exist.2684Assume(m_staging_clusterset.has_value());2685Assume(m_main_chunkindex_observers == 0);2686// Get rid of removed transactions in staging before moving to main, so they do not need to be2687// added to the chunk index there. Doing so is impossible if they were unlinked, and thus have2688// no Ref anymore to pass to the fallback comparator.2689ApplyRemovals(/*up_to_level=*/1);2690// Delete all conflicting Clusters in main, to make place for moving the staging ones2691// there. All of these have been copied to staging in PullIn().2692auto conflicts = GetConflicts();2693for (Cluster* conflict : conflicts) {2694conflict->Clear(*this, /*level=*/0);2695DeleteCluster(*conflict, /*level=*/0);2696}2697// Mark the removed transactions as Missing (so the staging locator for these transactions2698// can be reused if another staging is created).2699for (auto idx : m_staging_clusterset->m_removed) {2700m_entries[idx].m_locator[1].SetMissing();2701}2702// Then move all Clusters in staging to main.2703for (int quality = 0; quality < int(QualityLevel::NONE); ++quality) {2704auto& stage_sets = m_staging_clusterset->m_clusters[quality];2705while (!stage_sets.empty()) {2706stage_sets.back()->MoveToMain(*this);2707}2708}2709// Move all statistics, precomputed data, and to-be-applied removals and dependencies.2710m_main_clusterset.m_deps_to_add = std::move(m_staging_clusterset->m_deps_to_add);2711m_main_clusterset.m_to_remove = std::move(m_staging_clusterset->m_to_remove);2712m_main_clusterset.m_group_data = std::move(m_staging_clusterset->m_group_data);2713m_main_clusterset.m_oversized = m_staging_clusterset->m_oversized;2714m_main_clusterset.m_txcount = m_staging_clusterset->m_txcount;2715m_main_clusterset.m_txcount_oversized = m_staging_clusterset->m_txcount_oversized;2716// Delete the old staging graph, after all its information was moved to main.2717m_staging_clusterset.reset();2718Compact();2719}2721void GenericClusterImpl::SetFee(TxGraphImpl& graph, int level, DepGraphIndex idx, int64_t fee) noexcept2722{2723// Make sure the specified DepGraphIndex exists in this Cluster.2724Assume(m_depgraph.Positions()[idx]);2725// Bail out if the fee isn't actually being changed.2726if (m_depgraph.FeeRate(idx).fee == fee) return;2727// Update the fee, remember that relinearization will be necessary, and update the Entries2728// in the same Cluster.2729m_depgraph.FeeRate(idx).fee = fee;2730if (m_quality == QualityLevel::OVERSIZED_SINGLETON) {2731// Nothing to do.2732} else if (IsAcceptable()) {2733graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);2734}2735Updated(graph, /*level=*/level, /*rename=*/false);2736}2738void SingletonClusterImpl::SetFee(TxGraphImpl& graph, int level, DepGraphIndex idx, int64_t fee) noexcept2739{2740Assume(GetTxCount());2741Assume(idx == 0);2742m_feerate.fee = fee;2743Updated(graph, /*level=*/level, /*rename=*/false);2744}2746void TxGraphImpl::SetTransactionFee(const Ref& ref, int64_t fee) noexcept2747{2748// Don't do anything if the passed Ref is empty.2749if (GetRefGraph(ref) == nullptr) return;2750Assume(GetRefGraph(ref) == this);
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.