clusterlin: Add FixLinearization function + fuzz test

This function takes an existing ordering for transactions in a DepGraph, and
makes it a valid linearization for it (i.e., topological). Any topological
prefix of the input remains untouched.
This commit is contained in:
Pieter Wuille
2024-10-25 14:11:50 -04:00
parent 770d39a376
commit 0aa874a357
2 changed files with 94 additions and 0 deletions

View File

@@ -1118,3 +1118,65 @@ FUZZ_TARGET(clusterlin_merge)
auto cmp2 = CompareChunks(chunking_merged, chunking2);
assert(cmp2 >= 0);
}
FUZZ_TARGET(clusterlin_fix_linearization)
{
// Verify expected properties of FixLinearization() on arbitrary linearizations.
// Retrieve a depgraph from the fuzz input.
SpanReader reader(buffer);
DepGraph<TestBitSet> depgraph;
try {
reader >> Using<DepGraphFormatter>(depgraph);
} catch (const std::ios_base::failure&) {}
// Construct an arbitrary linearization (not necessarily topological for depgraph).
std::vector<ClusterIndex> linearization;
/** Which transactions of depgraph are yet to be included in linearization. */
TestBitSet todo = depgraph.Positions();
while (todo.Any()) {
// Read a number from the fuzz input in range [0, todo.Count()).
uint64_t val{0};
try {
reader >> VARINT(val);
} catch (const std::ios_base::failure&) {}
val %= todo.Count();
// Find the val'th element in todo, remove it from todo, and append it to linearization.
for (auto idx : todo) {
if (val == 0) {
linearization.push_back(idx);
todo.Reset(idx);
break;
}
--val;
}
}
assert(linearization.size() == depgraph.TxCount());
// Determine what prefix of linearization is topological, i.e., the position of the first entry
// in linearization which corresponds to a transaction that is not preceded by all its
// ancestors.
size_t topo_prefix = 0;
todo = depgraph.Positions();
while (topo_prefix < linearization.size()) {
ClusterIndex idx = linearization[topo_prefix];
todo.Reset(idx);
if (todo.Overlaps(depgraph.Ancestors(idx))) break;
++topo_prefix;
}
// Then make a fixed copy of linearization.
auto linearization_fixed = linearization;
FixLinearization(depgraph, linearization_fixed);
// Sanity check it (which includes testing whether it is topological).
SanityCheck(depgraph, linearization_fixed);
// FixLinearization does not modify the topological prefix of linearization.
assert(std::equal(linearization.begin(), linearization.begin() + topo_prefix,
linearization_fixed.begin()));
// This also means that if linearization was entirely topological, FixLinearization cannot have
// modified it. This is implied by the assertion above already, but repeat it explicitly.
if (topo_prefix == linearization.size()) {
assert(linearization == linearization_fixed);
}
}