Skip to content

Commit 82fa357

Browse files
committed
txgraph: Destroying Ref means removing transaction (feature)
Before this commit, if a TxGraph::Ref object is destroyed, it becomes impossible to refer to, but the actual corresponding transaction node in the TxGraph remains, and remains indefinitely as there is no way to remove it. Fix this by making the destruction of TxGraph::Ref trigger immediate removal of the corresponding transaction in TxGraph, both in main and staging if it exists.
1 parent 6b037ce commit 82fa357

File tree

3 files changed

+179
-71
lines changed

3 files changed

+179
-71
lines changed

src/test/fuzz/txgraph.cpp

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,30 @@ struct SimTxGraph
146146
if (oversized.has_value() && *oversized) oversized = std::nullopt;
147147
}
148148

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+
149173
/** Construct the set with all positions in this graph corresponding to the specified
150174
* TxGraph::Refs. All of them must occur in this graph and not be removed. */
151175
SetType MakeSet(std::span<TxGraph::Ref* const> arg)
@@ -327,9 +351,9 @@ FUZZ_TARGET(txgraph)
327351
}
328352
break;
329353
} 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.
333357

334358
// Pick a Ref of sel_sim.removed to destroy. Note that the same Ref may still occur
335359
// in the other graph, and thus not actually trigger ~Ref yet (which is exactly
@@ -341,6 +365,28 @@ FUZZ_TARGET(txgraph)
341365
}
342366
sel_sim.removed.pop_back();
343367
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;
344390
} else if (command-- == 0) {
345391
// SetTransactionFee.
346392
int64_t fee;
@@ -457,6 +503,10 @@ FUZZ_TARGET(txgraph)
457503
// AbortStaging.
458504
real->AbortStaging();
459505
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;
460510
break;
461511
}
462512
}
@@ -537,13 +587,4 @@ FUZZ_TARGET(txgraph)
537587

538588
// Sanity check again (because invoking inspectors may modify internal unobservable state).
539589
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-
}
549590
}

0 commit comments

Comments
 (0)