@@ -294,6 +294,81 @@ FUZZ_TARGET(clusterlin_depgraph_serialization)
294
294
assert (IsAcyclic (depgraph));
295
295
}
296
296
297
+ FUZZ_TARGET (clusterlin_components)
298
+ {
299
+ // Verify the behavior of DepGraphs's FindConnectedComponent and IsConnected functions.
300
+
301
+ // Construct a depgraph.
302
+ SpanReader reader (buffer);
303
+ DepGraph<TestBitSet> depgraph;
304
+ try {
305
+ reader >> Using<DepGraphFormatter>(depgraph);
306
+ } catch (const std::ios_base::failure&) {}
307
+
308
+ TestBitSet todo = TestBitSet::Fill (depgraph.TxCount ());
309
+ while (todo.Any ()) {
310
+ // Find a connected component inside todo.
311
+ auto component = depgraph.FindConnectedComponent (todo);
312
+
313
+ // The component must be a subset of todo and non-empty.
314
+ assert (component.IsSubsetOf (todo));
315
+ assert (component.Any ());
316
+
317
+ // If todo is the entire graph, and the entire graph is connected, then the component must
318
+ // be the entire graph.
319
+ if (todo == TestBitSet::Fill (depgraph.TxCount ())) {
320
+ assert ((component == todo) == depgraph.IsConnected ());
321
+ }
322
+
323
+ // If subset is connected, then component must match subset.
324
+ assert ((component == todo) == depgraph.IsConnected (todo));
325
+
326
+ // The component cannot have any ancestors or descendants outside of component but in todo.
327
+ for (auto i : component) {
328
+ assert ((depgraph.Ancestors (i) & todo).IsSubsetOf (component));
329
+ assert ((depgraph.Descendants (i) & todo).IsSubsetOf (component));
330
+ }
331
+
332
+ // Starting from any component element, we must be able to reach every element.
333
+ for (auto i : component) {
334
+ // Start with just i as reachable.
335
+ TestBitSet reachable = TestBitSet::Singleton (i);
336
+ // Add in-todo descendants and ancestors to reachable until it does not change anymore.
337
+ while (true ) {
338
+ TestBitSet new_reachable = reachable;
339
+ for (auto j : new_reachable) {
340
+ new_reachable |= depgraph.Ancestors (j) & todo;
341
+ new_reachable |= depgraph.Descendants (j) & todo;
342
+ }
343
+ if (new_reachable == reachable) break ;
344
+ reachable = new_reachable;
345
+ }
346
+ // Verify that the result is the entire component.
347
+ assert (component == reachable);
348
+ }
349
+
350
+ // Construct an arbitrary subset of todo.
351
+ uint64_t subset_bits{0 };
352
+ try {
353
+ reader >> VARINT (subset_bits);
354
+ } catch (const std::ios_base::failure&) {}
355
+ TestBitSet subset;
356
+ for (ClusterIndex i = 0 ; i < depgraph.TxCount (); ++i) {
357
+ if (todo[i]) {
358
+ if (subset_bits & 1 ) subset.Set (i);
359
+ subset_bits >>= 1 ;
360
+ }
361
+ }
362
+ // Which must be non-empty.
363
+ if (subset.None ()) subset = TestBitSet::Singleton (todo.First ());
364
+ // Remove it from todo.
365
+ todo -= subset;
366
+ }
367
+
368
+ // No components can be found in an empty subset.
369
+ assert (depgraph.FindConnectedComponent (todo).None ());
370
+ }
371
+
297
372
FUZZ_TARGET (clusterlin_chunking)
298
373
{
299
374
// Verify the correctness of the ChunkLinearization function.
@@ -357,6 +432,7 @@ FUZZ_TARGET(clusterlin_ancestor_finder)
357
432
assert (best_anc.transactions .Any ());
358
433
assert (best_anc.transactions .IsSubsetOf (todo));
359
434
assert (depgraph.FeeRate (best_anc.transactions ) == best_anc.feerate );
435
+ assert (depgraph.IsConnected (best_anc.transactions ));
360
436
// Check that it is topologically valid.
361
437
for (auto i : best_anc.transactions ) {
362
438
assert ((depgraph.Ancestors (i) & todo).IsSubsetOf (best_anc.transactions ));
@@ -443,6 +519,9 @@ FUZZ_TARGET(clusterlin_search_finder)
443
519
444
520
// Perform quality checks only if SearchCandidateFinder claims an optimal result.
445
521
if (iterations_done < max_iterations) {
522
+ // Optimal sets are always connected.
523
+ assert (depgraph.IsConnected (found.transactions ));
524
+
446
525
// Compare with SimpleCandidateFinder.
447
526
auto [simple, simple_iters] = smp_finder.FindCandidateSet (MAX_SIMPLE_ITERATIONS);
448
527
assert (found.feerate >= simple.feerate );
0 commit comments