From 876e2849b4ec9e1f4b89773c2faadcca77831b82 Mon Sep 17 00:00:00 2001 From: marcofleon Date: Fri, 12 Dec 2025 12:19:07 +0000 Subject: [PATCH 1/3] fuzz: Fix incorrect loop bounds in `clusterlin_postlinearize_tree` The dependency graphs generated by this test can have holes (unused indices) in them. This means some of the transactions were skipped when using `depgraph_gen.TxCount()` as the upper bound of the loop. Switch to using `depgraph.Positions()` to correctly handle sparse graphs. --- src/test/fuzz/cluster_linearize.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/fuzz/cluster_linearize.cpp b/src/test/fuzz/cluster_linearize.cpp index 61b95c71313..86734591d99 100644 --- a/src/test/fuzz/cluster_linearize.cpp +++ b/src/test/fuzz/cluster_linearize.cpp @@ -1275,14 +1275,14 @@ FUZZ_TARGET(clusterlin_postlinearize_tree) depgraph_tree.RemoveTransactions(TestBitSet::Fill(depgraph_gen.PositionRange()) - depgraph_gen.Positions()); if (direction & 1) { - for (DepGraphIndex i = 0; i < depgraph_gen.TxCount(); ++i) { + for (DepGraphIndex i : depgraph_gen.Positions()) { auto children = depgraph_gen.GetReducedChildren(i); if (children.Any()) { depgraph_tree.AddDependencies(TestBitSet::Singleton(i), children.First()); } } } else { - for (DepGraphIndex i = 0; i < depgraph_gen.TxCount(); ++i) { + for (DepGraphIndex i : depgraph_gen.Positions()) { auto parents = depgraph_gen.GetReducedParents(i); if (parents.Any()) { depgraph_tree.AddDependencies(TestBitSet::Singleton(parents.First()), i); From ce29d7d6262c8ba2e258b36794992ec79feb6e1f Mon Sep 17 00:00:00 2001 From: marcofleon Date: Fri, 12 Dec 2025 13:35:49 +0000 Subject: [PATCH 2/3] fuzz: Fix variable in `clusterlin_postlinearize_tree` check The test intends to verify that running `PostLinearize` a second time on a tree-structured graph doesn't change the result. But `PostLinearize` was being called on the original variable, not the copy. So the check was comparing the unmodified copy against itself, which is useless. Fix by post-linearizing the correct variable. --- src/test/fuzz/cluster_linearize.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/fuzz/cluster_linearize.cpp b/src/test/fuzz/cluster_linearize.cpp index 86734591d99..d2286caee55 100644 --- a/src/test/fuzz/cluster_linearize.cpp +++ b/src/test/fuzz/cluster_linearize.cpp @@ -1309,8 +1309,8 @@ FUZZ_TARGET(clusterlin_postlinearize_tree) // Verify that post-linearizing again does not change the diagram. The result must be identical // as post_linearization ought to be optimal already with a tree-structured graph. auto post_post_linearization = post_linearization; - PostLinearize(depgraph_tree, post_linearization); - SanityCheck(depgraph_tree, post_linearization); + PostLinearize(depgraph_tree, post_post_linearization); + SanityCheck(depgraph_tree, post_post_linearization); auto post_post_chunking = ChunkLinearization(depgraph_tree, post_post_linearization); auto cmp_post = CompareChunks(post_post_chunking, post_chunking); assert(cmp_post == 0); From a70a14a3f4f4d66025302c00eb4ff8108835cc5d Mon Sep 17 00:00:00 2001 From: marcofleon Date: Fri, 12 Dec 2025 14:52:10 +0000 Subject: [PATCH 3/3] refactor: Separate out logic for building a tree-shaped dependency graph --- src/test/fuzz/cluster_linearize.cpp | 69 +++++++++++++++++------------ 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/src/test/fuzz/cluster_linearize.cpp b/src/test/fuzz/cluster_linearize.cpp index d2286caee55..c1ee78028d2 100644 --- a/src/test/fuzz/cluster_linearize.cpp +++ b/src/test/fuzz/cluster_linearize.cpp @@ -364,6 +364,45 @@ std::vector ReadLinearization(const DepGraph& depgraph, SpanR return linearization; } +/** Given a dependency graph, construct a tree-structured graph. + * + * Copies the nodes from the depgraph, but only keeps the first parent (even direction) + * or the first child (odd direction) for each transaction. + */ +template +DepGraph BuildTreeGraph(const DepGraph& depgraph, uint8_t direction) +{ + DepGraph depgraph_tree; + for (DepGraphIndex i = 0; i < depgraph.PositionRange(); ++i) { + if (depgraph.Positions()[i]) { + depgraph_tree.AddTransaction(depgraph.FeeRate(i)); + } else { + // For holes, add a dummy transaction which is deleted below, so that non-hole + // transactions retain their position. + depgraph_tree.AddTransaction(FeeFrac{}); + } + } + depgraph_tree.RemoveTransactions(BS::Fill(depgraph.PositionRange()) - depgraph.Positions()); + + if (direction & 1) { + for (DepGraphIndex i : depgraph.Positions()) { + auto children = depgraph.GetReducedChildren(i); + if (children.Any()) { + depgraph_tree.AddDependencies(BS::Singleton(i), children.First()); + } + } + } else { + for (DepGraphIndex i : depgraph.Positions()) { + auto parents = depgraph.GetReducedParents(i); + if (parents.Any()) { + depgraph_tree.AddDependencies(BS::Singleton(parents.First()), i); + } + } + } + + return depgraph_tree; +} + } // namespace FUZZ_TARGET(clusterlin_depgraph_sim) @@ -1260,35 +1299,7 @@ FUZZ_TARGET(clusterlin_postlinearize_tree) reader >> direction >> rng_seed >> Using(depgraph_gen); } catch (const std::ios_base::failure&) {} - // Now construct a new graph, copying the nodes, but leaving only the first parent (even - // direction) or the first child (odd direction). - DepGraph depgraph_tree; - for (DepGraphIndex i = 0; i < depgraph_gen.PositionRange(); ++i) { - if (depgraph_gen.Positions()[i]) { - depgraph_tree.AddTransaction(depgraph_gen.FeeRate(i)); - } else { - // For holes, add a dummy transaction which is deleted below, so that non-hole - // transactions retain their position. - depgraph_tree.AddTransaction(FeeFrac{}); - } - } - depgraph_tree.RemoveTransactions(TestBitSet::Fill(depgraph_gen.PositionRange()) - depgraph_gen.Positions()); - - if (direction & 1) { - for (DepGraphIndex i : depgraph_gen.Positions()) { - auto children = depgraph_gen.GetReducedChildren(i); - if (children.Any()) { - depgraph_tree.AddDependencies(TestBitSet::Singleton(i), children.First()); - } - } - } else { - for (DepGraphIndex i : depgraph_gen.Positions()) { - auto parents = depgraph_gen.GetReducedParents(i); - if (parents.Any()) { - depgraph_tree.AddDependencies(TestBitSet::Singleton(parents.First()), i); - } - } - } + auto depgraph_tree = BuildTreeGraph(depgraph_gen, direction); // Retrieve a linearization from the fuzz input. std::vector linearization;