From 6a8fa821b80cf71e199b085c60d77f6ca533f6ee Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 10 Dec 2025 15:03:23 -0500 Subject: [PATCH] clusterlin: add support for loading existing linearization (feature) --- src/cluster_linearize.h | 27 ++++++++++++++++++++++++++- src/test/fuzz/cluster_linearize.cpp | 22 ++++++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/cluster_linearize.h b/src/cluster_linearize.h index 0ea9de092d8..c01fb8644cf 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 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(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 fa3e49b5ef7..86e85d1c505 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) {