@@ -27,7 +27,7 @@ struct SimTxGraph
27
27
/* * Maximum number of transactions to support simultaneously. Set this higher than txgraph's
28
28
* cluster count, so we can exercise situations with more transactions than fit in one
29
29
* cluster. */
30
- static constexpr unsigned MAX_TRANSACTIONS = CLUSTER_COUNT_LIMIT * 2 ;
30
+ static constexpr unsigned MAX_TRANSACTIONS = MAX_CLUSTER_COUNT_LIMIT * 2 ;
31
31
/* * Set type to use in the simulation. */
32
32
using SetType = BitSet<MAX_TRANSACTIONS>;
33
33
/* * Data type for representing positions within SimTxGraph::graph. */
@@ -44,6 +44,31 @@ struct SimTxGraph
44
44
std::map<const TxGraph::Ref*, Pos> simrevmap;
45
45
/* * The set of TxGraph::Ref entries that have been removed, but not yet destroyed. */
46
46
std::vector<std::unique_ptr<TxGraph::Ref>> removed;
47
+ /* * Whether the graph is oversized (true = yes, false = no, std::nullopt = unknown). */
48
+ std::optional<bool > oversized;
49
+ /* * The configured maximum number of transactions per cluster. */
50
+ DepGraphIndex max_cluster_count;
51
+
52
+ /* * Construct a new SimData with the specified maximum cluster count. */
53
+ explicit SimTxGraph (DepGraphIndex max_cluster) : max_cluster_count(max_cluster) {}
54
+
55
+ /* * Check whether this graph is oversized (contains a connected component whose number of
56
+ * transactions exceeds max_cluster_count. */
57
+ bool IsOversized ()
58
+ {
59
+ if (!oversized.has_value ()) {
60
+ // Only recompute when oversized isn't already known.
61
+ oversized = false ;
62
+ auto todo = graph.Positions ();
63
+ // Iterate over all connected components of the graph.
64
+ while (todo.Any ()) {
65
+ auto component = graph.FindConnectedComponent (todo);
66
+ if (component.Count () > max_cluster_count) oversized = true ;
67
+ todo -= component;
68
+ }
69
+ }
70
+ return *oversized;
71
+ }
47
72
48
73
/* * Determine the number of (non-removed) transactions in the graph. */
49
74
DepGraphIndex GetTransactionCount () const { return graph.TxCount (); }
@@ -84,6 +109,8 @@ struct SimTxGraph
84
109
auto chl_pos = Find (child);
85
110
if (chl_pos == MISSING) return ;
86
111
graph.AddDependencies (SetType::Singleton (par_pos), chl_pos);
112
+ // This may invalidate our cached oversized value.
113
+ if (oversized.has_value () && !*oversized) oversized = std::nullopt;
87
114
}
88
115
89
116
/* * Modify the transaction fee of a ref, if it exists. */
@@ -105,6 +132,8 @@ struct SimTxGraph
105
132
// invoked until the simulation explicitly decided to do so.
106
133
removed.push_back (std::move (simmap[pos]));
107
134
simmap[pos].reset ();
135
+ // This may invalidate our cached oversized value.
136
+ if (oversized.has_value () && *oversized) oversized = std::nullopt;
108
137
}
109
138
110
139
/* * Construct the set with all positions in this graph corresponding to the specified
@@ -170,9 +199,12 @@ FUZZ_TARGET(txgraph)
170
199
/* * Variable used whenever an empty TxGraph::Ref is needed. */
171
200
TxGraph::Ref empty_ref;
172
201
202
+ // Decide the maximum number of transactions per cluster we will use in this simulation.
203
+ auto max_count = provider.ConsumeIntegralInRange <DepGraphIndex>(1 , MAX_CLUSTER_COUNT_LIMIT);
204
+
173
205
// Construct a real and a simulated graph.
174
- auto real = MakeTxGraph ();
175
- SimTxGraph sim;
206
+ auto real = MakeTxGraph (max_count );
207
+ SimTxGraph sim (max_count) ;
176
208
177
209
/* * Function to pick any Ref (from sim.simmap or sim.removed, or the empty Ref). */
178
210
auto pick_fn = [&]() noexcept -> TxGraph::Ref* {
@@ -245,17 +277,6 @@ FUZZ_TARGET(txgraph)
245
277
// Determine if adding this would introduce a cycle (not allowed by TxGraph),
246
278
// and if so, skip.
247
279
if (sim.graph .Ancestors (pos_par)[pos_chl]) break ;
248
- // Determine if adding this would violate CLUSTER_COUNT_LIMIT, and if so, skip.
249
- auto temp_depgraph = sim.graph ;
250
- temp_depgraph.AddDependencies (SimTxGraph::SetType::Singleton (pos_par), pos_chl);
251
- auto todo = temp_depgraph.Positions ();
252
- bool oversize{false };
253
- while (todo.Any ()) {
254
- auto component = temp_depgraph.FindConnectedComponent (todo);
255
- if (component.Count () > CLUSTER_COUNT_LIMIT) oversize = true ;
256
- todo -= component;
257
- }
258
- if (oversize) break ;
259
280
}
260
281
sim.AddDependency (par, chl);
261
282
real->AddDependency (*par, *chl);
@@ -310,6 +331,10 @@ FUZZ_TARGET(txgraph)
310
331
bool should_exist = sim.Find (ref) != SimTxGraph::MISSING;
311
332
assert (exists == should_exist);
312
333
break ;
334
+ } else if (command-- == 0 ) {
335
+ // IsOversized.
336
+ assert (sim.IsOversized () == real->IsOversized ());
337
+ break ;
313
338
} else if (command-- == 0 ) {
314
339
// GetIndividualFeerate.
315
340
auto ref = pick_fn ();
@@ -321,7 +346,7 @@ FUZZ_TARGET(txgraph)
321
346
assert (feerate == sim.graph .FeeRate (simpos));
322
347
}
323
348
break ;
324
- } else if (command-- == 0 ) {
349
+ } else if (!sim. IsOversized () && command-- == 0 ) {
325
350
// GetChunkFeerate.
326
351
auto ref = pick_fn ();
327
352
auto feerate = real->GetChunkFeerate (*ref);
@@ -334,20 +359,22 @@ FUZZ_TARGET(txgraph)
334
359
assert (feerate.size >= sim.graph .FeeRate (simpos).size );
335
360
}
336
361
break ;
337
- } else if (command-- == 0 ) {
362
+ } else if (!sim. IsOversized () && command-- == 0 ) {
338
363
// GetAncestors/GetDescendants.
339
364
auto ref = pick_fn ();
340
- auto result_set = sim.MakeSet (alt ? real->GetDescendants (*ref) :
341
- real->GetAncestors (*ref));
365
+ auto result = alt ? real->GetDescendants (*ref) : real->GetAncestors (*ref);
366
+ assert (result.size () <= max_count);
367
+ auto result_set = sim.MakeSet (result);
368
+ assert (result.size () == result_set.Count ());
342
369
auto expect_set = sim.GetAncDesc (ref, alt);
343
370
assert (result_set == expect_set);
344
371
break ;
345
- } else if (command-- == 0 ) {
372
+ } else if (!sim. IsOversized () && command-- == 0 ) {
346
373
// GetCluster.
347
374
auto ref = pick_fn ();
348
375
auto result = real->GetCluster (*ref);
349
376
// Check cluster count limit.
350
- assert (result.size () <= CLUSTER_COUNT_LIMIT );
377
+ assert (result.size () <= max_count );
351
378
// Require the result to be topologically valid and not contain duplicates.
352
379
auto left = sim.graph .Positions ();
353
380
for (auto refptr : result) {
@@ -382,56 +409,62 @@ FUZZ_TARGET(txgraph)
382
409
real->SanityCheck ();
383
410
384
411
// Compare simple properties of the graph with the simulation.
412
+ assert (real->IsOversized () == sim.IsOversized ());
385
413
assert (real->GetTransactionCount () == sim.GetTransactionCount ());
386
414
387
- // Perform a full comparison.
388
- auto todo = sim.graph .Positions ();
389
- // Iterate over all connected components of the resulting (simulated) graph, each of which
390
- // should correspond to a cluster in the real one.
391
- while (todo.Any ()) {
392
- auto component = sim.graph .FindConnectedComponent (todo);
393
- todo -= component;
394
- // Iterate over the transactions in that component.
395
- for (auto i : component) {
396
- // Check its individual feerate against simulation.
397
- assert (sim.graph .FeeRate (i) == real->GetIndividualFeerate (*sim.GetRef (i)));
398
- // Check its ancestors against simulation.
399
- auto expect_anc = sim.graph .Ancestors (i);
400
- auto anc = sim.MakeSet (real->GetAncestors (*sim.GetRef (i)));
401
- assert (anc == expect_anc);
402
- // Check its descendants against simulation.
403
- auto expect_desc = sim.graph .Descendants (i);
404
- auto desc = sim.MakeSet (real->GetDescendants (*sim.GetRef (i)));
405
- assert (desc == expect_desc);
406
- // Check the cluster the transaction is part of.
407
- auto cluster = real->GetCluster (*sim.GetRef (i));
408
- assert (sim.MakeSet (cluster) == component);
409
- // Check that the cluster is reported in a valid topological order (its
410
- // linearization).
411
- std::vector<DepGraphIndex> simlin;
412
- SimTxGraph::SetType done;
413
- for (TxGraph::Ref* ptr : cluster) {
414
- auto simpos = sim.Find (ptr);
415
- assert (sim.graph .Descendants (simpos).IsSubsetOf (component - done));
416
- done.Set (simpos);
417
- assert (sim.graph .Ancestors (simpos).IsSubsetOf (done));
418
- simlin.push_back (simpos);
419
- }
420
- // Construct a chunking object for the simulated graph, using the reported cluster
421
- // linearization as ordering, and compare it against the reported chunk feerates.
422
- cluster_linearize::LinearizationChunking simlinchunk (sim.graph , simlin);
423
- DepGraphIndex idx{0 };
424
- for (unsigned chunknum = 0 ; chunknum < simlinchunk.NumChunksLeft (); ++chunknum) {
425
- auto chunk = simlinchunk.GetChunk (chunknum);
426
- // Require that the chunks of cluster linearizations are connected (this must
427
- // be the case as all linearizations inside are PostLinearized).
428
- assert (sim.graph .IsConnected (chunk.transactions ));
429
- // Check the chunk feerates of all transactions in the cluster.
430
- while (chunk.transactions .Any ()) {
431
- assert (chunk.transactions [simlin[idx]]);
432
- chunk.transactions .Reset (simlin[idx]);
433
- assert (chunk.feerate == real->GetChunkFeerate (*cluster[idx]));
434
- ++idx;
415
+ // If the graph (and the simulation) are not oversized, perform a full comparison.
416
+ if (!sim.IsOversized ()) {
417
+ auto todo = sim.graph .Positions ();
418
+ // Iterate over all connected components of the resulting (simulated) graph, each of which
419
+ // should correspond to a cluster in the real one.
420
+ while (todo.Any ()) {
421
+ auto component = sim.graph .FindConnectedComponent (todo);
422
+ todo -= component;
423
+ // Iterate over the transactions in that component.
424
+ for (auto i : component) {
425
+ // Check its individual feerate against simulation.
426
+ assert (sim.graph .FeeRate (i) == real->GetIndividualFeerate (*sim.GetRef (i)));
427
+ // Check its ancestors against simulation.
428
+ auto expect_anc = sim.graph .Ancestors (i);
429
+ auto anc = sim.MakeSet (real->GetAncestors (*sim.GetRef (i)));
430
+ assert (anc.Count () <= max_count);
431
+ assert (anc == expect_anc);
432
+ // Check its descendants against simulation.
433
+ auto expect_desc = sim.graph .Descendants (i);
434
+ auto desc = sim.MakeSet (real->GetDescendants (*sim.GetRef (i)));
435
+ assert (desc.Count () <= max_count);
436
+ assert (desc == expect_desc);
437
+ // Check the cluster the transaction is part of.
438
+ auto cluster = real->GetCluster (*sim.GetRef (i));
439
+ assert (cluster.size () <= max_count);
440
+ assert (sim.MakeSet (cluster) == component);
441
+ // Check that the cluster is reported in a valid topological order (its
442
+ // linearization).
443
+ std::vector<DepGraphIndex> simlin;
444
+ SimTxGraph::SetType done;
445
+ for (TxGraph::Ref* ptr : cluster) {
446
+ auto simpos = sim.Find (ptr);
447
+ assert (sim.graph .Descendants (simpos).IsSubsetOf (component - done));
448
+ done.Set (simpos);
449
+ assert (sim.graph .Ancestors (simpos).IsSubsetOf (done));
450
+ simlin.push_back (simpos);
451
+ }
452
+ // Construct a chunking object for the simulated graph, using the reported cluster
453
+ // linearization as ordering, and compare it against the reported chunk feerates.
454
+ cluster_linearize::LinearizationChunking simlinchunk (sim.graph , simlin);
455
+ DepGraphIndex idx{0 };
456
+ for (unsigned chunknum = 0 ; chunknum < simlinchunk.NumChunksLeft (); ++chunknum) {
457
+ auto chunk = simlinchunk.GetChunk (chunknum);
458
+ // Require that the chunks of cluster linearizations are connected (this must
459
+ // be the case as all linearizations inside are PostLinearized).
460
+ assert (sim.graph .IsConnected (chunk.transactions ));
461
+ // Check the chunk feerates of all transactions in the cluster.
462
+ while (chunk.transactions .Any ()) {
463
+ assert (chunk.transactions [simlin[idx]]);
464
+ chunk.transactions .Reset (simlin[idx]);
465
+ assert (chunk.feerate == real->GetChunkFeerate (*cluster[idx]));
466
+ ++idx;
467
+ }
435
468
}
436
469
}
437
470
}
0 commit comments