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.
3251Next();3252}3254void BlockBuilderImpl::Skip() noexcept3255{3256// When skipping a chunk we need to not include anything more of the cluster, as that could make3257// the result topologically invalid. However, don't do this if the chunk is known to be the last3258// chunk of the cluster. This may significantly reduce the size of m_excluded_clusters,3259// especially when many singleton clusters are ignored.3260if (m_cur_cluster != nullptr && !m_known_end_of_cluster) {3261m_excluded_clusters.insert(m_cur_cluster->m_sequence);3262}3263Next();3264}3266std::unique_ptr<TxGraph::BlockBuilder> TxGraphImpl::GetBlockBuilder() noexcept3267{3268return std::make_unique<BlockBuilderImpl>(*this);3269}3271std::pair<std::vector<TxGraph::Ref*>, FeePerWeight> TxGraphImpl::GetWorstMainChunk() noexcept3272{3273std::pair<std::vector<Ref*>, FeePerWeight> ret;3274// Make sure all clusters in main are up to date, and acceptable.3275MakeAllAcceptable(0);3276Assume(m_main_clusterset.m_deps_to_add.empty());3277// If the graph is not empty, populate ret.3278if (!m_main_chunkindex.empty()) {3279const auto& chunk_data = *m_main_chunkindex.rbegin();3280const auto& chunk_end_entry = m_entries[chunk_data.m_graph_index];3281Cluster* cluster = chunk_end_entry.m_locator[0].cluster;3282if (chunk_data.m_chunk_count == LinearizationIndex(-1) || chunk_data.m_chunk_count == 1) {3283// Special case for singletons.3284ret.first.resize(1);3285Assume(chunk_end_entry.m_ref != nullptr);3286ret.first[0] = chunk_end_entry.m_ref;3287} else {3288ret.first.resize(chunk_data.m_chunk_count);3289auto start_pos = chunk_end_entry.m_main_lin_index + 1 - chunk_data.m_chunk_count;3290cluster->GetClusterRefs(*this, ret.first, start_pos);3291std::reverse(ret.first.begin(), ret.first.end());3292}3293ret.second = chunk_end_entry.m_main_chunk_feerate;3294}3295return ret;3296}3298std::vector<TxGraph::Ref*> TxGraphImpl::Trim() noexcept3299{3300int level = GetTopLevel();3301Assume(m_main_chunkindex_observers == 0 || level != 0);3302std::vector<TxGraph::Ref*> ret;3304// Compute the groups of to-be-merged Clusters (which also applies all removals, and splits).3305auto& clusterset = GetClusterSet(level);3306if (clusterset.m_oversized == false) return ret;3307GroupClusters(level);3308Assume(clusterset.m_group_data.has_value());3309// Nothing to do if not oversized.3310Assume(clusterset.m_oversized.has_value());3311if (clusterset.m_oversized == false) return ret;3313// In this function, would-be clusters (as precomputed in m_group_data by GroupClusters) are3314// trimmed by removing transactions in them such that the resulting clusters satisfy the size3315// and count limits.3316//3317// It works by defining for each would-be cluster a rudimentary linearization: at every point3318// the highest-chunk-feerate remaining transaction is picked among those with no unmet3319// dependencies. "Dependency" here means either a to-be-added dependency (m_deps_to_add), or3320// an implicit dependency added between any two consecutive transaction in their current3321// cluster linearization. So it can be seen as a "merge sort" of the chunks of the clusters,3322// but respecting the dependencies being added.3323//3324// This rudimentary linearization is computed lazily, by putting all eligible (no unmet3325// dependencies) transactions in a heap, and popping the highest-feerate one from it. Along the3326// way, the counts and sizes of the would-be clusters up to that point are tracked (by3327// partitioning the involved transactions using a union-find structure). Any transaction whose3328// addition would cause a violation is removed, along with all their descendants.3329//3330// A next invocation of GroupClusters (after applying the removals) will compute the new3331// resulting clusters, and none of them will violate the limits.3333/** All dependencies (both to be added ones, and implicit ones between consecutive transactions3334* in existing cluster linearizations), sorted by parent. */3335std::vector<std::pair<GraphIndex, GraphIndex>> deps_by_parent;3336/** Same, but sorted by child. */3337std::vector<std::pair<GraphIndex, GraphIndex>> deps_by_child;3338/** Information about all transactions involved in a Cluster group to be trimmed, sorted by3339* GraphIndex. It contains entries both for transactions that have already been included,3340* and ones that have not yet been. */3341std::vector<TrimTxData> trim_data;3342/** Iterators into trim_data, treated as a max heap according to cmp_fn below. Each entry is3343* a transaction that has not yet been included yet, but all its ancestors have. */3344std::vector<std::vector<TrimTxData>::iterator> trim_heap;3345/** The list of representatives of the partitions a given transaction depends on. */3346std::vector<TrimTxData*> current_deps;3348/** Function to define the ordering of trim_heap. */3349static constexpr auto cmp_fn = [](auto a, auto b) noexcept {3350// Sort by increasing chunk feerate, and then by decreasing size.3351// We do not need to sort by cluster or within clusters, because due to the implicit3352// dependency between consecutive linearization elements, no two transactions from the3353// same Cluster will ever simultaneously be in the heap.3354return ByRatioNegSize{a->m_chunk_feerate} < ByRatioNegSize{b->m_chunk_feerate};3355};3357/** Given a TrimTxData entry, find the representative of the partition it is in. */3358static constexpr auto find_fn = [](TrimTxData* arg) noexcept {3359while (arg != arg->m_uf_parent) {3360// Replace pointer to parent with pointer to grandparent (path splitting).3361// See https://en.wikipedia.org/wiki/Disjoint-set_data_structure#Finding_set_representatives.3362auto par = arg->m_uf_parent;3363arg->m_uf_parent = par->m_uf_parent;3364arg = par;3365}3366return arg;3367};3369/** Given two TrimTxData entries, union the partitions they are in, and return the3370* representative. */3371static constexpr auto union_fn = [](TrimTxData* arg1, TrimTxData* arg2) noexcept {3372// Replace arg1 and arg2 by their representatives.3373auto rep1 = find_fn(arg1);3374auto rep2 = find_fn(arg2);3375// Bail out if both representatives are the same, because that means arg1 and arg2 are in3376// the same partition already.3377if (rep1 == rep2) return rep1;3378// Pick the lower-count root to become a child of the higher-count one.3379// See https://en.wikipedia.org/wiki/Disjoint-set_data_structure#Union_by_size.3380if (rep1->m_uf_count < rep2->m_uf_count) std::swap(rep1, rep2);3381rep2->m_uf_parent = rep1;3382// Add the statistics of arg2 (which is no longer a representative) to those of arg1 (which3383// is now the representative for both).3384rep1->m_uf_size += rep2->m_uf_size;3385rep1->m_uf_count += rep2->m_uf_count;3386return rep1;3387};3389/** Get iterator to TrimTxData entry for a given index. */3390auto locate_fn = [&](GraphIndex index) noexcept {3391auto it = std::lower_bound(trim_data.begin(), trim_data.end(), index, [](TrimTxData& elem, GraphIndex idx) noexcept {3392return elem.m_index < idx;3393});3394Assume(it != trim_data.end() && it->m_index == index);3395return it;3396};3398// For each group of to-be-merged Clusters.3399for (const auto& group_data : clusterset.m_group_data->m_groups) {3400trim_data.clear();3401trim_heap.clear();3402deps_by_child.clear();3403deps_by_parent.clear();3405// Gather trim data and implicit dependency data from all involved Clusters.3406auto cluster_span = std::span{clusterset.m_group_data->m_group_clusters}3407.subspan(group_data.m_cluster_offset, group_data.m_cluster_count);3408uint64_t size{0};3409for (Cluster* cluster : cluster_span) {3410MakeAcceptable(*cluster, cluster->GetLevel(*this));3411size += cluster->AppendTrimData(trim_data, deps_by_child);3412}3413// If this group of Clusters does not violate any limits, continue to the next group.3414if (trim_data.size() <= m_max_cluster_count && size <= m_max_cluster_size) continue;3415// Sort the trim data by GraphIndex. In what follows, we will treat this sorted vector as3416// a map from GraphIndex to TrimTxData via locate_fn, and its ordering will not change3417// anymore.3418std::ranges::sort(trim_data, [](auto& a, auto& b) noexcept { return a.m_index < b.m_index; });3420// Add the explicitly added dependencies to deps_by_child.3421deps_by_child.insert(deps_by_child.end(),3422clusterset.m_deps_to_add.begin() + group_data.m_deps_offset,3423clusterset.m_deps_to_add.begin() + group_data.m_deps_offset + group_data.m_deps_count);3425// Sort deps_by_child by child transaction GraphIndex. The order will not be changed3426// anymore after this.3427std::ranges::sort(deps_by_child, [](auto& a, auto& b) noexcept { return a.second < b.second; });3428// Fill m_parents_count and m_parents_offset in trim_data, as well as m_deps_left, and3429// initially populate trim_heap. Because of the sort above, all dependencies involving the3430// same child are grouped together, so a single linear scan suffices.3431auto deps_it = deps_by_child.begin();3432for (auto trim_it = trim_data.begin(); trim_it != trim_data.end(); ++trim_it) {3433trim_it->m_parent_offset = deps_it - deps_by_child.begin();3434trim_it->m_deps_left = 0;3435while (deps_it != deps_by_child.end() && deps_it->second == trim_it->m_index) {3436++trim_it->m_deps_left;3437++deps_it;3438}3439trim_it->m_parent_count = trim_it->m_deps_left;3440// If this transaction has no unmet dependencies, and is not oversized, add it to the3441// heap (just append for now, the heapification happens below).3442if (trim_it->m_deps_left == 0 && trim_it->m_tx_size <= m_max_cluster_size) {3443trim_heap.push_back(trim_it);3444}3445}3446Assume(deps_it == deps_by_child.end());3448// Construct deps_by_parent, sorted by parent transaction GraphIndex. The order will not be3449// changed anymore after this.3450deps_by_parent = deps_by_child;3451std::ranges::sort(deps_by_parent, [](auto& a, auto& b) noexcept { return a.first < b.first; });3452// Fill m_children_offset and m_children_count in trim_data. Because of the sort above, all3453// dependencies involving the same parent are grouped together, so a single linear scan3454// suffices.3455deps_it = deps_by_parent.begin();3456for (auto& trim_entry : trim_data) {3457trim_entry.m_children_count = 0;3458trim_entry.m_children_offset = deps_it - deps_by_parent.begin();3459while (deps_it != deps_by_parent.end() && deps_it->first == trim_entry.m_index) {3460++trim_entry.m_children_count;3461++deps_it;3462}3463}3464Assume(deps_it == deps_by_parent.end());3466// Build a heap of all transactions with 0 unmet dependencies.3467std::make_heap(trim_heap.begin(), trim_heap.end(), cmp_fn);3469// Iterate over to-be-included transactions, and convert them to included transactions, or3470// skip them if adding them would violate resource limits of the would-be cluster.3471//3472// It is possible that the heap empties without ever hitting either cluster limit, in case3473// the implied graph (to be added dependencies plus implicit dependency between each3474// original transaction and its predecessor in the linearization it came from) contains3475// cycles. Such cycles will be removed entirely, because each of the transactions in the3476// cycle permanently have unmet dependencies. However, this cannot occur in real scenarios3477// where Trim() is called to deal with reorganizations that would violate cluster limits,3478// as all added dependencies are in the same direction (from old mempool transactions to3479// new from-block transactions); cycles require dependencies in both directions to be3480// added.3481while (!trim_heap.empty()) {3482// Move the best remaining transaction to the end of trim_heap.3483std::pop_heap(trim_heap.begin(), trim_heap.end(), cmp_fn);3484// Pop it, and find its TrimTxData.3485auto& entry = *trim_heap.back();3486trim_heap.pop_back();3488// Initialize it as a singleton partition.3489entry.m_uf_parent = &entry;3490entry.m_uf_count = 1;3491entry.m_uf_size = entry.m_tx_size;3493// Find the distinct transaction partitions this entry depends on.3494current_deps.clear();3495for (auto& [par, chl] : std::span{deps_by_child}.subspan(entry.m_parent_offset, entry.m_parent_count)) {3496Assume(chl == entry.m_index);3497current_deps.push_back(find_fn(&*locate_fn(par)));3498}3499std::ranges::sort(current_deps);3500current_deps.erase(std::ranges::unique(current_deps).begin(), current_deps.end());
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.