txgraph: Allow Refs to outlive the TxGraph (feature)

This commit is contained in:
Pieter Wuille 2025-03-21 15:17:42 -04:00
parent 82fa3573e1
commit 22c68cd153
3 changed files with 22 additions and 1 deletions

View file

@ -587,4 +587,10 @@ FUZZ_TARGET(txgraph)
// Sanity check again (because invoking inspectors may modify internal unobservable state).
real->SanityCheck();
// Kill the TxGraph object.
real.reset();
// Kill the simulated graphs, with all remaining Refs in it. If any, this verifies that Refs
// can outlive the TxGraph that created them.
sims.clear();
}

View file

@ -320,6 +320,9 @@ public:
Assume(max_cluster_count <= MAX_CLUSTER_COUNT_LIMIT);
}
/** Destructor. */
~TxGraphImpl() noexcept;
// Cannot move or copy (would invalidate TxGraphImpl* in Ref, MiningOrder, EvictionOrder).
TxGraphImpl(const TxGraphImpl&) = delete;
TxGraphImpl& operator=(const TxGraphImpl&) = delete;
@ -809,6 +812,17 @@ void Cluster::ApplyDependencies(TxGraphImpl& graph, std::span<std::pair<GraphInd
Updated(graph);
}
TxGraphImpl::~TxGraphImpl() noexcept
{
// If Refs outlive the TxGraphImpl they refer to, unlink them, so that their destructor does not
// try to reach into a non-existing TxGraphImpl anymore.
for (auto& entry : m_entries) {
if (entry.m_ref != nullptr) {
GetRefGraph(*entry.m_ref) = nullptr;
}
}
}
std::unique_ptr<Cluster> TxGraphImpl::ExtractCluster(int level, QualityLevel quality, ClusterSetIndex setindex) noexcept
{
Assume(quality != QualityLevel::NONE);

View file

@ -63,7 +63,8 @@ public:
/** Construct a new transaction with the specified feerate, and return a Ref to it.
* If a staging graph exists, the new transaction is only created there. In all
* further calls, only Refs created by AddTransaction() are allowed to be passed to this
* TxGraph object (or empty Ref objects). */
* TxGraph object (or empty Ref objects). Ref objects may outlive the TxGraph they were
* created for. */
[[nodiscard]] virtual Ref AddTransaction(const FeePerWeight& feerate) noexcept = 0;
/** Remove the specified transaction. If a staging graph exists, the removal only happens
* there. This is a no-op if the transaction was already removed.