clusterlin: split tx/chunk dep counting (preparation)
What changed, and why it matters
This is a routine internal code cleanup in Bitcoin Core's transaction clustering/linearization logic. It splits one combined counter into two separate counters for clarity, with no functional change intended. There is no security issue visible in the commit.
No security action needed; treat as normal code review/merge.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors LoadLinearization in src/cluster_linearize.h by replacing a std::vector
Changed components
src/cluster_linearize.hsrc/test/util/cluster_linearize.hInspect captured patch +24 / −26
diff --git a/src/cluster_linearize.h b/src/cluster_linearize.h
index d2041b9b..57d6a7a6 100644
--- a/src/cluster_linearize.h
+++ b/src/cluster_linearize.h
@@ -1274,28 +1274,26 @@ public:
* chunk feerate (high to low), chunk size (small to large), and by least maximum element
* according to the fallback order (which is the second pair element). */
std::vector<std::pair<TxIdx, TxIdx>> ready_chunks;
- /** Information about chunks:
- * - The first value is only used for chunk representatives, and counts the number of
- * unmet dependencies this chunk has on other chunks (not including dependencies within
- * the chunk itself).
- * - The second value is the number of unmet dependencies overall.
- */
- std::vector<std::pair<TxIdx, TxIdx>> chunk_deps(m_tx_data.size(), {0, 0});
+ /** For every chunk, indexed by representative, the number of unmet dependencies the chunk has on
+ * other chunks (not including dependencies within the chunk itself). */
+ std::vector<TxIdx> chunk_deps(m_tx_data.size(), 0);
+ /** For every transaction, indexed by TxIdx, the number of unmet dependencies the
+ * transaction has. */
+ std::vector<TxIdx> tx_deps(m_tx_data.size(), 0);
/** The set of all chunk representatives. */
SetType chunk_reps;
/** A heap with all transactions within the current chunk that can be included, sorted by
* tx feerate (high to low), tx size (small to large), and fallback order. */
std::vector<TxIdx> ready_tx;
- // Populate chunk_deps[c] with the number of {out-of-chunk dependencies, dependencies} the
- // child has.
+ // Populate chunk_deps and tx_deps.
for (TxIdx chl_idx : m_transaction_idxs) {
const auto& chl_data = m_tx_data[chl_idx];
- chunk_deps[chl_idx].second = chl_data.parents.Count();
+ tx_deps[chl_idx] = chl_data.parents.Count();
auto chl_chunk_rep = chl_data.chunk_rep;
chunk_reps.Set(chl_chunk_rep);
for (auto par_idx : chl_data.parents) {
auto par_chunk_rep = m_tx_data[par_idx].chunk_rep;
- chunk_deps[chl_chunk_rep].first += (par_chunk_rep != chl_chunk_rep);
+ chunk_deps[chl_chunk_rep] += (par_chunk_rep != chl_chunk_rep);
}
}
/** Function to compute the highest element of a chunk, by fallback_order. */
@@ -1355,7 +1353,7 @@ public:
};
// Construct a heap with all chunks that have no out-of-chunk dependencies.
for (TxIdx chunk_rep : chunk_reps) {
- if (chunk_deps[chunk_rep].first == 0) {
+ if (chunk_deps[chunk_rep] == 0) {
ready_chunks.emplace_back(chunk_rep, max_fallback_fn(chunk_rep));
}
}
@@ -1366,12 +1364,12 @@ public:
std::pop_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
ready_chunks.pop_back();
Assume(m_tx_data[chunk_rep].chunk_rep == chunk_rep);
- Assume(chunk_deps[chunk_rep].first == 0);
+ Assume(chunk_deps[chunk_rep] == 0);
const auto& chunk_txn = m_tx_data[chunk_rep].chunk_setinfo.transactions;
// Build heap of all includable transactions in chunk.
Assume(ready_tx.empty());
for (TxIdx tx_idx : chunk_txn) {
- if (chunk_deps[tx_idx].second == 0) ready_tx.push_back(tx_idx);
+ if (tx_deps[tx_idx] == 0) ready_tx.push_back(tx_idx);
}
Assume(!ready_tx.empty());
std::make_heap(ready_tx.begin(), ready_tx.end(), tx_cmp_fn);
@@ -1389,16 +1387,16 @@ public:
for (TxIdx chl_idx : tx_data.children) {
auto& chl_data = m_tx_data[chl_idx];
// Decrement tx dependency count.
- Assume(chunk_deps[chl_idx].second > 0);
- if (--chunk_deps[chl_idx].second == 0 && chunk_txn[chl_idx]) {
+ Assume(tx_deps[chl_idx] > 0);
+ if (--tx_deps[chl_idx] == 0 && chunk_txn[chl_idx]) {
// Child tx has no dependencies left, and is in this chunk. Add it to the tx heap.
ready_tx.push_back(chl_idx);
std::push_heap(ready_tx.begin(), ready_tx.end(), tx_cmp_fn);
}
// Decrement chunk dependency count if this is out-of-chunk dependency.
if (chl_data.chunk_rep != chunk_rep) {
- Assume(chunk_deps[chl_data.chunk_rep].first > 0);
- if (--chunk_deps[chl_data.chunk_rep].first == 0) {
+ Assume(chunk_deps[chl_data.chunk_rep] > 0);
+ if (--chunk_deps[chl_data.chunk_rep] == 0) {
// Child chunk has no dependencies left. Add it to the chunk heap.
ready_chunks.emplace_back(chl_data.chunk_rep, max_fallback_fn(chl_data.chunk_rep));
std::push_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
diff --git a/src/test/util/cluster_linearize.h b/src/test/util/cluster_linearize.h
index 40ce7f62..b6b07d1b 100644
--- a/src/test/util/cluster_linearize.h
+++ b/src/test/util/cluster_linearize.h
@@ -402,14 +402,14 @@ inline uint64_t MaxOptimalLinearizationIters(DepGraphIndex cluster_count)
// *some* reasonable cost bound, optimal linearizations are always found.
static constexpr uint64_t ITERS[65] = {
0,
- 0, 4, 10, 34, 76, 118, 184, 225,
- 320, 376, 464, 573, 830, 868, 1019, 1468,
- 1375, 1785, 1880, 1854, 2551, 2559, 4336, 4784,
- 5547, 5807, 6157, 6075, 6961, 7403, 7756, 8001,
- 8041, 7579, 8483, 10077, 9015, 9388, 9626, 12371,
- 12847, 12102, 15173, 15800, 20319, 22190, 23183, 24361,
- 24909, 19225, 27419, 23789, 25909, 21993, 25596, 24130,
- 26349, 31823, 31855, 31250, 32688, 34825, 41710, 45478
+ 0, 4, 10, 34, 76, 156, 229, 380,
+ 432, 607, 738, 896, 1037, 1366, 1464, 1711,
+ 2060, 2542, 3068, 3116, 4029, 3949, 5324, 5402,
+ 6481, 7161, 7441, 8329, 9307, 9353, 11104, 11269,
+ 11791, 11981, 12413, 14513, 15331, 12397, 13581, 19665,
+ 18737, 16581, 23217, 25542, 27123, 28913, 32969, 33951,
+ 34414, 26227, 38792, 38045, 40814, 29622, 38732, 32122,
+ 35915, 49823, 39722, 43765, 44002, 49716, 59417, 67035
};
assert(cluster_count < std::size(ITERS));
// Multiply the table number by two, to account for the fact that they are not absolutes.
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.