@@ -146,6 +146,30 @@ struct SimTxGraph
146
146
if (oversized.has_value () && *oversized) oversized = std::nullopt;
147
147
}
148
148
149
+ /* * Destroy the transaction from the graph, including from the removed set. This will
150
+ * trigger TxGraph::Ref::~Ref. reset_oversize controls whether the cached oversized
151
+ * value is cleared (destroying does not clear oversizedness in TxGraph of the main
152
+ * graph while staging exists). */
153
+ void DestroyTransaction (TxGraph::Ref* ref, bool reset_oversize)
154
+ {
155
+ auto pos = Find (ref);
156
+ if (pos == MISSING) {
157
+ // Wipe the ref, if it exists, from the removed vector. Use std::partition rather
158
+ // than std::erase because we don't care about the order of the entries that
159
+ // remain.
160
+ auto remove = std::partition (removed.begin (), removed.end (), [&](auto & arg) { return arg.get () != ref; });
161
+ removed.erase (remove, removed.end ());
162
+ } else {
163
+ graph.RemoveTransactions (SetType::Singleton (pos));
164
+ simrevmap.erase (simmap[pos].get ());
165
+ simmap[pos].reset ();
166
+ // This may invalidate our cached oversized value.
167
+ if (reset_oversize && oversized.has_value () && *oversized) {
168
+ oversized = std::nullopt;
169
+ }
170
+ }
171
+ }
172
+
149
173
/* * Construct the set with all positions in this graph corresponding to the specified
150
174
* TxGraph::Refs. All of them must occur in this graph and not be removed. */
151
175
SetType MakeSet (std::span<TxGraph::Ref* const > arg)
@@ -327,9 +351,9 @@ FUZZ_TARGET(txgraph)
327
351
}
328
352
break ;
329
353
} else if (sel_sim.removed .size () > 0 && command-- == 0 ) {
330
- // ~Ref. Destroying a TxGraph::Ref has an observable effect on the TxGraph it
331
- // refers to, so this simulation permits doing so separately from other actions on
332
- // TxGraph.
354
+ // ~Ref (of an already-removed transaction) . Destroying a TxGraph::Ref has an
355
+ // observable effect on the TxGraph it refers to, so this simulation permits doing
356
+ // so separately from other actions on TxGraph.
333
357
334
358
// Pick a Ref of sel_sim.removed to destroy. Note that the same Ref may still occur
335
359
// in the other graph, and thus not actually trigger ~Ref yet (which is exactly
@@ -341,6 +365,28 @@ FUZZ_TARGET(txgraph)
341
365
}
342
366
sel_sim.removed .pop_back ();
343
367
break ;
368
+ } else if (command-- == 0 ) {
369
+ // ~Ref (of any transaction).
370
+ std::vector<TxGraph::Ref*> to_destroy;
371
+ to_destroy.push_back (pick_fn ());
372
+ while (true ) {
373
+ // Keep adding either the ancestors or descendants the already picked
374
+ // transactions have in both graphs (main and staging) combined. Destroying
375
+ // will trigger deletions in both, so to have consistent TxGraph behavior, the
376
+ // set must be closed under ancestors, or descendants, in both graphs.
377
+ auto old_size = to_destroy.size ();
378
+ for (auto & sim : sims) sim.IncludeAncDesc (to_destroy, alt);
379
+ if (to_destroy.size () == old_size) break ;
380
+ }
381
+ // The order in which these ancestors/descendants are destroyed should not matter;
382
+ // randomly shuffle them.
383
+ std::shuffle (to_destroy.begin (), to_destroy.end (), rng);
384
+ for (TxGraph::Ref* ptr : to_destroy) {
385
+ for (size_t level = 0 ; level < sims.size (); ++level) {
386
+ sims[level].DestroyTransaction (ptr, level == sims.size () - 1 );
387
+ }
388
+ }
389
+ break ;
344
390
} else if (command-- == 0 ) {
345
391
// SetTransactionFee.
346
392
int64_t fee;
@@ -457,6 +503,10 @@ FUZZ_TARGET(txgraph)
457
503
// AbortStaging.
458
504
real->AbortStaging ();
459
505
sims.pop_back ();
506
+ // Reset the cached oversized value (if TxGraph::Ref destructions triggered
507
+ // removals of main transactions while staging was active, then aborting will
508
+ // cause it to be re-evaluated in TxGraph).
509
+ sims.back ().oversized = std::nullopt;
460
510
break ;
461
511
}
462
512
}
@@ -537,13 +587,4 @@ FUZZ_TARGET(txgraph)
537
587
538
588
// Sanity check again (because invoking inspectors may modify internal unobservable state).
539
589
real->SanityCheck ();
540
-
541
- // Remove all remaining transactions, because Refs cannot be destroyed otherwise (this will be
542
- // addressed in a follow-up commit).
543
- for (auto & sim : sims) {
544
- for (auto i : sim.graph .Positions ()) {
545
- auto ref = sim.GetRef (i);
546
- real->RemoveTransaction (*ref);
547
- }
548
- }
549
590
}
0 commit comments