clusterlin: add support for loading existing linearization (feature)
What changed, and why it matters
This commit adds a new feature to Bitcoin Core's internal transaction-cluster linearization code. It lets the algorithm start from an existing ordering of transactions rather than building one from scratch. The change is purely an optimization/feature addition in mempool policy code and does not alter network rules, consensus, or wallet behavior. There is no indication it fixes a security bug.
No security action required. Treat as a normal feature/optimization review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces LoadLinearization() in src/cluster_linearize.h, which seeds the Set-Forest Linearization (SFL) state using a provided transaction order, performing upward merges until no more are possible. It also extends the clusterlin_sfl fuzz target to exercise both loaded and from-scratch topological initialization. The code is local to mempool/cluster linearization logic and is not consensus-critical.
Changed components
src/cluster_linearize.hsrc/test/fuzz/cluster_linearize.cppInspect captured patch +46 / −3
diff --git a/src/cluster_linearize.h b/src/cluster_linearize.h
index 0ea9de09..c01fb864 100644
--- a/src/cluster_linearize.h
+++ b/src/cluster_linearize.h
@@ -777,6 +777,15 @@ public:
* - Do a downwards merge of B, if possible. If so, repeat the same with the merged result.
* - Output the chunks from high to low feerate, each internally sorted topologically.
*
+ * Instead of performing merges arbitrarily to make the initial state topological, it is possible
+ * to do so guided by an existing linearization. This has the advantage that the state's would-be
+ * output linearization is immediately as good as the existing linearization it was based on:
+ * - Start with all dependencies inactive.
+ * - For each transaction t in the existing linearization:
+ * - Find the chunk C that transaction is in (which will be singleton).
+ * - Do an upwards merge of C, if possible. If so, repeat the same with the merged result.
+ * No downwards merges are needed in this case.
+ *
* What remains to be specified are a number of heuristics:
*
* - How to decide which chunks to merge:
@@ -1124,7 +1133,23 @@ public:
}
}
- /** Make state topological. Can be called after constructing. */
+ /** Load an existing linearization. Must be called immediately after constructor. The result is
+ * topological if the linearization is valid. Otherwise, MakeTopological still needs to be
+ * called. */
+ void LoadLinearization(std::span<const DepGraphIndex> old_linearization) noexcept
+ {
+ // Add transactions one by one, in order of existing linearization.
+ for (DepGraphIndex tx : old_linearization) {
+ auto chunk_rep = m_tx_data[tx].chunk_rep;
+ // Merge the chunk upwards, as long as merging succeeds.
+ while (true) {
+ chunk_rep = MergeStep<false>(chunk_rep);
+ if (chunk_rep == TxIdx(-1)) break;
+ }
+ }
+ }
+
+ /** Make state topological. Can be called after constructing, or after LoadLinearization. */
void MakeTopological() noexcept
{
while (true) {
diff --git a/src/test/fuzz/cluster_linearize.cpp b/src/test/fuzz/cluster_linearize.cpp
index fa3e49b5..86e85d1c 100644
--- a/src/test/fuzz/cluster_linearize.cpp
+++ b/src/test/fuzz/cluster_linearize.cpp
@@ -1190,6 +1190,10 @@ FUZZ_TARGET(clusterlin_sfl)
InsecureRandomContext rng(rng_seed);
/** Whether to make the depgraph connected. */
const bool make_connected = flags & 1;
+ /** Whether to load some input linearization into the state. */
+ const bool load_linearization = flags & 2;
+ /** Whether that input linearization is topological. */
+ const bool load_topological = load_linearization && (flags & 4);
// Initialize SFL state.
if (make_connected) MakeConnected(depgraph);
@@ -1222,8 +1226,22 @@ FUZZ_TARGET(clusterlin_sfl)
last_diagram = std::move(diagram);
};
- // Make SFL state topological.
- sfl.MakeTopological();
+ if (load_linearization) {
+ auto input_lin = ReadLinearization(depgraph, reader, load_topological);
+ sfl.LoadLinearization(input_lin);
+ if (load_topological) {
+ // The diagram of the loaded linearization forms an initial lower bound on future
+ // diagrams.
+ last_diagram = ChunkLinearization(depgraph, input_lin);
+ } else {
+ // The input linearization may have been non-topological, so invoke MakeTopological to
+ // fix it still.
+ sfl.MakeTopological();
+ }
+ } else {
+ // Invoke MakeTopological to create an initial from-scratch topological state.
+ sfl.MakeTopological();
+ }
// Loop until optimal.
while (true) {
Why this scored 13/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.