clusterlin: make optimal linearizations deterministic (feature)

This allows passing in a fallback order comparator to Linearize(), which
is used as final tiebreak when deciding the order of chunks and
transactions within a chunk, rather than a random tiebreak.

The order of transactions within a chunk becomes:
1. Topology (parents before children)
2. Individual transaction feerate (high to low)
3. Weight (small to large)
4. Fallback (low to high fallback order)

The order of chunks within a cluster becomes:
1. Topology (chunks after their dependencies)
2. Feerate (high to low)
3. Weight (small to large)
4. Max-fallback (chunk with lowest maximum-fallback-tx first)

For now, txgraph passes a naive comparator to Linearize(), which makes
the cluster order deterministic when treating the input transactions as
identified by the DepGraphIndex. However, since DepGraphIndexes are the
result of possibly-randomized operations inside txgraph, this doesn't
actually make txgraph's per-cluster ordering deterministic. That will be
changed in a later commit, by using a txid-based fallback instead.
This commit is contained in:
Pieter Wuille
2026-01-07 15:02:02 -05:00
parent 8bfbba3207
commit 39d0052cbf
6 changed files with 361 additions and 275 deletions

View File

@@ -1065,7 +1065,8 @@ FUZZ_TARGET(txgraph)
// that calling Linearize on it does not improve it further.
if (sims[0].real_is_optimal) {
auto real_diagram = ChunkLinearization(sims[0].graph, vec1);
auto [sim_lin, _optimal, _cost] = Linearize(sims[0].graph, 300000, rng.rand64(), vec1);
auto [sim_lin, sim_optimal, _cost] = Linearize(sims[0].graph, 300000, rng.rand64(), IndexTxOrder{}, vec1);
PostLinearize(sims[0].graph, sim_lin);
auto sim_diagram = ChunkLinearization(sims[0].graph, sim_lin);
auto cmp = CompareChunks(real_diagram, sim_diagram);
assert(cmp == 0);
@@ -1094,11 +1095,27 @@ FUZZ_TARGET(txgraph)
auto comp_key = component.First();
auto& comp_prefix_size = comp_prefix_sizes[comp_key];
comp_prefix_size += chunk.feerate.size;
// Verify consistency: within each component (= cluster in txgraph), the
// equal-feerate chunk prefix size must be monotonically increasing.
// Verify consistency: within each group of equal-feerate chunks, the equal-feerate
// chunk prefix size must be monotonically increasing.
assert(comp_prefix_size >= max_chunk_prefix_size);
max_chunk_prefix_size = comp_prefix_size;
}
// Verify that within each cluster, the internal ordering matches that of the
// simulation if that is optimal too, since per-cluster optimal orderings are
// deterministic. Note that both have been PostLinearize()'ed.
if (sim_optimal) {
for (const auto& component : sims[0].GetComponents()) {
std::vector<DepGraphIndex> sim_chunk_lin, real_chunk_lin;
for (auto i : sim_lin) {
if (component[i]) sim_chunk_lin.push_back(i);
}
for (auto i : vec1) {
if (component[i]) real_chunk_lin.push_back(i);
}
assert(sim_chunk_lin == real_chunk_lin);
}
}
}
// For every transaction in the total ordering, find a random one before it and after it,