diff --git a/src/cluster_linearize.h b/src/cluster_linearize.h index 32cedf9f84..28dde840ef 100644 --- a/src/cluster_linearize.h +++ b/src/cluster_linearize.h @@ -309,6 +309,17 @@ public: return a < b; }); } + + /** Check if this graph is acyclic. */ + bool IsAcyclic() const noexcept + { + for (auto i : Positions()) { + if ((Ancestors(i) & Descendants(i)) != SetType::Singleton(i)) { + return false; + } + } + return true; + } }; /** A set of transactions together with their aggregate feerate. */ diff --git a/src/test/fuzz/cluster_linearize.cpp b/src/test/fuzz/cluster_linearize.cpp index de066237b2..f5c0c897c9 100644 --- a/src/test/fuzz/cluster_linearize.cpp +++ b/src/test/fuzz/cluster_linearize.cpp @@ -401,13 +401,42 @@ FUZZ_TARGET(clusterlin_depgraph_serialization) // Construct a graph by deserializing. SpanReader reader(buffer); DepGraph depgraph; + ClusterIndex par_code{0}, chl_code{0}; try { - reader >> Using(depgraph); + reader >> Using(depgraph) >> VARINT(par_code) >> VARINT(chl_code); } catch (const std::ios_base::failure&) {} SanityCheck(depgraph); // Verify the graph is a DAG. - assert(IsAcyclic(depgraph)); + assert(depgraph.IsAcyclic()); + + // Introduce a cycle, and then test that IsAcyclic returns false. + if (depgraph.TxCount() < 2) return; + ClusterIndex par(0), chl(0); + // Pick any transaction of depgraph as parent. + par_code %= depgraph.TxCount(); + for (auto i : depgraph.Positions()) { + if (par_code == 0) { + par = i; + break; + } + --par_code; + } + // Pick any ancestor of par (excluding itself) as child, if any. + auto ancestors = depgraph.Ancestors(par) - TestBitSet::Singleton(par); + if (ancestors.None()) return; + chl_code %= ancestors.Count(); + for (auto i : ancestors) { + if (chl_code == 0) { + chl = i; + break; + } + --chl_code; + } + // Add the cycle-introducing dependency. + depgraph.AddDependencies(TestBitSet::Singleton(par), chl); + // Check that we now detect a cycle. + assert(!depgraph.IsAcyclic()); } FUZZ_TARGET(clusterlin_components) diff --git a/src/test/util/cluster_linearize.h b/src/test/util/cluster_linearize.h index 7ae56232ba..3db51a6b80 100644 --- a/src/test/util/cluster_linearize.h +++ b/src/test/util/cluster_linearize.h @@ -23,18 +23,6 @@ using namespace cluster_linearize; using TestBitSet = BitSet<32>; -/** Check if a graph is acyclic. */ -template -bool IsAcyclic(const DepGraph& depgraph) noexcept -{ - for (ClusterIndex i : depgraph.Positions()) { - if ((depgraph.Ancestors(i) & depgraph.Descendants(i)) != SetType::Singleton(i)) { - return false; - } - } - return true; -} - /** A formatter for a bespoke serialization for acyclic DepGraph objects. * * The serialization format outputs information about transactions in a topological order (parents @@ -337,7 +325,7 @@ void SanityCheck(const DepGraph& depgraph) assert((depgraph.Descendants(child) & children).IsSubsetOf(SetType::Singleton(child))); } } - if (IsAcyclic(depgraph)) { + if (depgraph.IsAcyclic()) { // If DepGraph is acyclic, serialize + deserialize must roundtrip. std::vector ser; VectorWriter writer(ser, 0);