mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
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:
parent
770d39a376
commit
0aa874a357
2 changed files with 94 additions and 0 deletions
|
@ -1336,6 +1336,38 @@ std::vector<ClusterIndex> MergeLinearizations(const DepGraph<SetType>& depgraph,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/** Make linearization topological, retaining its ordering where possible. */
|
||||
template<typename SetType>
|
||||
void FixLinearization(const DepGraph<SetType>& depgraph, std::span<ClusterIndex> linearization) noexcept
|
||||
{
|
||||
// This algorithm can be summarized as moving every element in the linearization backwards
|
||||
// until it is placed after all its ancestors.
|
||||
SetType done;
|
||||
const auto len = linearization.size();
|
||||
// Iterate over the elements of linearization from back to front (i is distance from back).
|
||||
for (ClusterIndex i = 0; i < len; ++i) {
|
||||
/** The element at that position. */
|
||||
ClusterIndex elem = linearization[len - 1 - i];
|
||||
/** j represents how far from the back of the linearization elem should be placed. */
|
||||
ClusterIndex j = i;
|
||||
// Figure out which elements need to be moved before elem.
|
||||
SetType place_before = done & depgraph.Ancestors(elem);
|
||||
// Find which position to place elem in (updating j), continuously moving the elements
|
||||
// in between forward.
|
||||
while (place_before.Any()) {
|
||||
// j cannot be 0 here; if it was, then there was necessarily nothing earlier which
|
||||
// elem needs to be place before anymore, and place_before would be empty.
|
||||
Assume(j > 0);
|
||||
auto to_swap = linearization[len - 1 - (j - 1)];
|
||||
place_before.Reset(to_swap);
|
||||
linearization[len - 1 - (j--)] = to_swap;
|
||||
}
|
||||
// Put elem in its final position and mark it as done.
|
||||
linearization[len - 1 - j] = elem;
|
||||
done.Set(elem);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cluster_linearize
|
||||
|
||||
#endif // BITCOIN_CLUSTER_LINEARIZE_H
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue