txgraph: Group per-graph data in ClusterSet (refactor)

This is a preparation for a next commit where a TxGraph will start representing
potentially two distinct graphs (a main one, and a staging one with proposed
changes).
This commit is contained in:
Pieter Wuille 2024-11-24 08:37:53 -05:00
parent 36dd5edca5
commit 34aa3da5ad

View file

@ -25,7 +25,7 @@ class TxGraphImpl;
/** Position of a DepGraphIndex within a Cluster::m_linearization. */ /** Position of a DepGraphIndex within a Cluster::m_linearization. */
using LinearizationIndex = uint32_t; using LinearizationIndex = uint32_t;
/** Position of a Cluster within Graph::m_clusters. */ /** Position of a Cluster within Graph::ClusterSet::m_clusters. */
using ClusterSetIndex = uint32_t; using ClusterSetIndex = uint32_t;
/** Quality levels for cached cluster linearizations. */ /** Quality levels for cached cluster linearizations. */
@ -41,12 +41,12 @@ enum class QualityLevel
ACCEPTABLE, ACCEPTABLE,
/** The linearization is known to be optimal. */ /** The linearization is known to be optimal. */
OPTIMAL, OPTIMAL,
/** This cluster is not registered in any m_clusters. /** This cluster is not registered in any ClusterSet::m_clusters.
* This must be the last entry in QualityLevel as m_clusters is sized using it. */ * This must be the last entry in QualityLevel as ClusterSet::m_clusters is sized using it. */
NONE, NONE,
}; };
/** A grouping of connected transactions inside a TxGraphImpl. */ /** A grouping of connected transactions inside a TxGraphImpl::ClusterSet. */
class Cluster class Cluster
{ {
friend class TxGraphImpl; friend class TxGraphImpl;
@ -63,7 +63,7 @@ class Cluster
std::vector<DepGraphIndex> m_linearization; std::vector<DepGraphIndex> m_linearization;
/** The quality level of m_linearization. */ /** The quality level of m_linearization. */
QualityLevel m_quality{QualityLevel::NONE}; QualityLevel m_quality{QualityLevel::NONE};
/** Which position this Cluster has in Graph::m_clusters[m_quality]. */ /** Which position this Cluster has in Graph::ClusterSet::m_clusters[m_quality]. */
ClusterSetIndex m_setindex{ClusterSetIndex(-1)}; ClusterSetIndex m_setindex{ClusterSetIndex(-1)};
public: public:
@ -72,7 +72,7 @@ public:
/** Construct a singleton Cluster. */ /** Construct a singleton Cluster. */
explicit Cluster(TxGraphImpl& graph, const FeePerWeight& feerate, GraphIndex graph_index) noexcept; explicit Cluster(TxGraphImpl& graph, const FeePerWeight& feerate, GraphIndex graph_index) noexcept;
// Cannot move or copy (would invalidate Cluster* in Locator and TxGraphImpl). */ // Cannot move or copy (would invalidate Cluster* in Locator and TxGraphImpl::ClusterSet). */
Cluster(const Cluster&) = delete; Cluster(const Cluster&) = delete;
Cluster& operator=(const Cluster&) = delete; Cluster& operator=(const Cluster&) = delete;
Cluster(Cluster&&) = delete; Cluster(Cluster&&) = delete;
@ -192,18 +192,25 @@ private:
bool m_group_oversized; bool m_group_oversized;
}; };
/** The vectors of clusters, one vector per quality level. ClusterSetIndex indexes into each. */ /** The collection of all Clusters in main or staged. */
std::array<std::vector<std::unique_ptr<Cluster>>, int(QualityLevel::NONE)> m_clusters; struct ClusterSet
/** Which removals have yet to be applied. */ {
std::vector<GraphIndex> m_to_remove; /** The vectors of clusters, one vector per quality level. ClusterSetIndex indexes into each. */
/** Which dependencies are to be added ((parent,child) pairs). GroupData::m_deps_offset indexes std::array<std::vector<std::unique_ptr<Cluster>>, int(QualityLevel::NONE)> m_clusters;
* into this. */ /** Which removals have yet to be applied. */
std::vector<std::pair<GraphIndex, GraphIndex>> m_deps_to_add; std::vector<GraphIndex> m_to_remove;
/** Information about the merges to be performed, if known. */ /** Which dependencies are to be added ((parent,child) pairs). GroupData::m_deps_offset indexes
std::optional<GroupData> m_group_data = GroupData{}; * into this. */
/** Total number of transactions in this graph (sum of all transaction counts in all Clusters). std::vector<std::pair<GraphIndex, GraphIndex>> m_deps_to_add;
* */ /** Information about the merges to be performed, if known. */
GraphIndex m_txcount{0}; std::optional<GroupData> m_group_data = GroupData{};
/** Total number of transactions in this graph (sum of all transaction counts in all
* Clusters). */
GraphIndex m_txcount{0};
};
/** The ClusterSet for this TxGraphImpl. */
ClusterSet m_clusterset;
/** A Locator that describes whether, where, and in which Cluster an Entry appears. */ /** A Locator that describes whether, where, and in which Cluster an Entry appears. */
struct Locator struct Locator
@ -380,7 +387,7 @@ void Cluster::ApplyRemovals(TxGraphImpl& graph, std::span<GraphIndex>& to_remove
// - Mark it as removed in the Entry's locator. // - Mark it as removed in the Entry's locator.
locator.SetMissing(); locator.SetMissing();
to_remove = to_remove.subspan(1); to_remove = to_remove.subspan(1);
--graph.m_txcount; --graph.m_clusterset.m_txcount;
} while(!to_remove.empty()); } while(!to_remove.empty());
auto quality = m_quality; auto quality = m_quality;
@ -576,7 +583,7 @@ std::unique_ptr<Cluster> TxGraphImpl::ExtractCluster(QualityLevel quality, Clust
{ {
Assume(quality != QualityLevel::NONE); Assume(quality != QualityLevel::NONE);
auto& quality_clusters = m_clusters[int(quality)]; auto& quality_clusters = m_clusterset.m_clusters[int(quality)];
Assume(setindex < quality_clusters.size()); Assume(setindex < quality_clusters.size());
// Extract the Cluster-owning unique_ptr. // Extract the Cluster-owning unique_ptr.
@ -605,7 +612,7 @@ ClusterSetIndex TxGraphImpl::InsertCluster(std::unique_ptr<Cluster>&& cluster, Q
Assume(cluster->m_quality == QualityLevel::NONE); Assume(cluster->m_quality == QualityLevel::NONE);
// Append it at the end of the relevant TxGraphImpl::m_cluster. // Append it at the end of the relevant TxGraphImpl::m_cluster.
auto& quality_clusters = m_clusters[int(quality)]; auto& quality_clusters = m_clusterset.m_clusters[int(quality)];
ClusterSetIndex ret = quality_clusters.size(); ClusterSetIndex ret = quality_clusters.size();
cluster->m_quality = quality; cluster->m_quality = quality;
cluster->m_setindex = ret; cluster->m_setindex = ret;
@ -635,15 +642,16 @@ void TxGraphImpl::DeleteCluster(Cluster& cluster) noexcept
void TxGraphImpl::ApplyRemovals() noexcept void TxGraphImpl::ApplyRemovals() noexcept
{ {
auto& to_remove = m_to_remove; auto& clusterset = m_clusterset;
auto& to_remove = clusterset.m_to_remove;
// Skip if there is nothing to remove. // Skip if there is nothing to remove.
if (to_remove.empty()) return; if (to_remove.empty()) return;
// Group the set of to-be-removed entries by Cluster*. // Group the set of to-be-removed entries by Cluster*.
std::sort(m_to_remove.begin(), m_to_remove.end(), [&](GraphIndex a, GraphIndex b) noexcept { std::sort(to_remove.begin(), to_remove.end(), [&](GraphIndex a, GraphIndex b) noexcept {
return std::less{}(m_entries[a].m_locator.cluster, m_entries[b].m_locator.cluster); return std::less{}(m_entries[a].m_locator.cluster, m_entries[b].m_locator.cluster);
}); });
// Process per Cluster. // Process per Cluster.
std::span to_remove_span{m_to_remove}; std::span to_remove_span{to_remove};
while (!to_remove_span.empty()) { while (!to_remove_span.empty()) {
Cluster* cluster = m_entries[to_remove_span.front()].m_locator.cluster; Cluster* cluster = m_entries[to_remove_span.front()].m_locator.cluster;
if (cluster != nullptr) { if (cluster != nullptr) {
@ -656,7 +664,7 @@ void TxGraphImpl::ApplyRemovals() noexcept
to_remove_span = to_remove_span.subspan(1); to_remove_span = to_remove_span.subspan(1);
} }
} }
m_to_remove.clear(); to_remove.clear();
Compact(); Compact();
} }
@ -685,8 +693,8 @@ void TxGraphImpl::Compact() noexcept
{ {
// We cannot compact while any to-be-applied operations remain, as we'd need to rewrite them. // We cannot compact while any to-be-applied operations remain, as we'd need to rewrite them.
// It is easier to delay the compaction until they have been applied. // It is easier to delay the compaction until they have been applied.
if (!m_deps_to_add.empty()) return; if (!m_clusterset.m_deps_to_add.empty()) return;
if (!m_to_remove.empty()) return; if (!m_clusterset.m_to_remove.empty()) return;
// Sort the GraphIndexes that need to be cleaned up. They are sorted in reverse, so the last // Sort the GraphIndexes that need to be cleaned up. They are sorted in reverse, so the last
// ones get processed first. This means earlier-processed GraphIndexes will not cause moving of // ones get processed first. This means earlier-processed GraphIndexes will not cause moving of
@ -732,7 +740,7 @@ void TxGraphImpl::SplitAll() noexcept
// Before splitting all Cluster, first make sure all removals are applied. // Before splitting all Cluster, first make sure all removals are applied.
ApplyRemovals(); ApplyRemovals();
for (auto quality : {QualityLevel::NEEDS_SPLIT, QualityLevel::NEEDS_SPLIT_ACCEPTABLE}) { for (auto quality : {QualityLevel::NEEDS_SPLIT, QualityLevel::NEEDS_SPLIT_ACCEPTABLE}) {
auto& queue = m_clusters[int(quality)]; auto& queue = m_clusterset.m_clusters[int(quality)];
while (!queue.empty()) { while (!queue.empty()) {
Split(*queue.back().get()); Split(*queue.back().get());
} }
@ -741,8 +749,9 @@ void TxGraphImpl::SplitAll() noexcept
void TxGraphImpl::GroupClusters() noexcept void TxGraphImpl::GroupClusters() noexcept
{ {
auto& clusterset = m_clusterset;
// If the groupings have been computed already, nothing is left to be done. // If the groupings have been computed already, nothing is left to be done.
if (m_group_data.has_value()) return; if (clusterset.m_group_data.has_value()) return;
// Before computing which Clusters need to be merged together, first apply all removals and // Before computing which Clusters need to be merged together, first apply all removals and
// split the Clusters into connected components. If we would group first, we might end up // split the Clusters into connected components. If we would group first, we might end up
@ -758,7 +767,7 @@ void TxGraphImpl::GroupClusters() noexcept
std::vector<std::pair<std::pair<GraphIndex, GraphIndex>, Cluster*>> an_deps; std::vector<std::pair<std::pair<GraphIndex, GraphIndex>, Cluster*>> an_deps;
// Construct a an_clusters entry for every parent and child in the to-be-applied dependencies. // Construct a an_clusters entry for every parent and child in the to-be-applied dependencies.
for (const auto& [par, chl] : m_deps_to_add) { for (const auto& [par, chl] : clusterset.m_deps_to_add) {
auto par_cluster = m_entries[par].m_locator.cluster; auto par_cluster = m_entries[par].m_locator.cluster;
auto chl_cluster = m_entries[chl].m_locator.cluster; auto chl_cluster = m_entries[chl].m_locator.cluster;
// Skip dependencies for which the parent or child transaction is removed. // Skip dependencies for which the parent or child transaction is removed.
@ -774,7 +783,7 @@ void TxGraphImpl::GroupClusters() noexcept
an_clusters.erase(std::unique(an_clusters.begin(), an_clusters.end()), an_clusters.end()); an_clusters.erase(std::unique(an_clusters.begin(), an_clusters.end()), an_clusters.end());
// Sort the dependencies by child Cluster. // Sort the dependencies by child Cluster.
std::sort(m_deps_to_add.begin(), m_deps_to_add.end(), [&](auto& a, auto& b) noexcept { std::sort(clusterset.m_deps_to_add.begin(), clusterset.m_deps_to_add.end(), [&](auto& a, auto& b) noexcept {
auto [_a_par, a_chl] = a; auto [_a_par, a_chl] = a;
auto [_b_par, b_chl] = b; auto [_b_par, b_chl] = b;
auto a_chl_cluster = m_entries[a_chl].m_locator.cluster; auto a_chl_cluster = m_entries[a_chl].m_locator.cluster;
@ -851,7 +860,7 @@ void TxGraphImpl::GroupClusters() noexcept
// the partitions their Clusters are in. // the partitions their Clusters are in.
Cluster* last_chl_cluster{nullptr}; Cluster* last_chl_cluster{nullptr};
PartitionData* last_partition{nullptr}; PartitionData* last_partition{nullptr};
for (const auto& [par, chl] : m_deps_to_add) { for (const auto& [par, chl] : clusterset.m_deps_to_add) {
auto par_cluster = m_entries[par].m_locator.cluster; auto par_cluster = m_entries[par].m_locator.cluster;
auto chl_cluster = m_entries[chl].m_locator.cluster; auto chl_cluster = m_entries[chl].m_locator.cluster;
// Nothing to do if parent and child are in the same Cluster. // Nothing to do if parent and child are in the same Cluster.
@ -873,8 +882,8 @@ void TxGraphImpl::GroupClusters() noexcept
// Populate the an_clusters and an_deps data structures with the list of input Clusters, // Populate the an_clusters and an_deps data structures with the list of input Clusters,
// and the input dependencies, annotated with the representative of the Cluster partition // and the input dependencies, annotated with the representative of the Cluster partition
// it applies to. // it applies to.
an_deps.reserve(m_deps_to_add.size()); an_deps.reserve(clusterset.m_deps_to_add.size());
auto deps_it = m_deps_to_add.begin(); auto deps_it = clusterset.m_deps_to_add.begin();
for (size_t i = 0; i < partition_data.size(); ++i) { for (size_t i = 0; i < partition_data.size(); ++i) {
auto& data = partition_data[i]; auto& data = partition_data[i];
// Find the representative of the partition Cluster i is in, and store it with the // Find the representative of the partition Cluster i is in, and store it with the
@ -883,7 +892,7 @@ void TxGraphImpl::GroupClusters() noexcept
Assume(an_clusters[i].second == nullptr); Assume(an_clusters[i].second == nullptr);
an_clusters[i].second = rep; an_clusters[i].second = rep;
// Find all dependencies whose child Cluster is Cluster i, and annotate them with rep. // Find all dependencies whose child Cluster is Cluster i, and annotate them with rep.
while (deps_it != m_deps_to_add.end()) { while (deps_it != clusterset.m_deps_to_add.end()) {
auto [par, chl] = *deps_it; auto [par, chl] = *deps_it;
auto chl_cluster = m_entries[chl].m_locator.cluster; auto chl_cluster = m_entries[chl].m_locator.cluster;
if (std::greater{}(chl_cluster, data.cluster)) break; if (std::greater{}(chl_cluster, data.cluster)) break;
@ -906,39 +915,39 @@ void TxGraphImpl::GroupClusters() noexcept
// Translate the resulting cluster groups to the m_group_data structure, and the dependencies // Translate the resulting cluster groups to the m_group_data structure, and the dependencies
// back to m_deps_to_add. // back to m_deps_to_add.
m_group_data = GroupData{}; clusterset.m_group_data = GroupData{};
m_group_data->m_group_clusters.reserve(an_clusters.size()); clusterset.m_group_data->m_group_clusters.reserve(an_clusters.size());
m_group_data->m_group_oversized = false; clusterset.m_group_data->m_group_oversized = false;
m_deps_to_add.clear(); clusterset.m_deps_to_add.clear();
m_deps_to_add.reserve(an_deps.size()); clusterset.m_deps_to_add.reserve(an_deps.size());
auto an_deps_it = an_deps.begin(); auto an_deps_it = an_deps.begin();
auto an_clusters_it = an_clusters.begin(); auto an_clusters_it = an_clusters.begin();
while (an_clusters_it != an_clusters.end()) { while (an_clusters_it != an_clusters.end()) {
// Process all clusters/dependencies belonging to the partition with representative rep. // Process all clusters/dependencies belonging to the partition with representative rep.
auto rep = an_clusters_it->second; auto rep = an_clusters_it->second;
// Create and initialize a new GroupData entry for the partition. // Create and initialize a new GroupData entry for the partition.
auto& new_entry = m_group_data->m_groups.emplace_back(); auto& new_entry = clusterset.m_group_data->m_groups.emplace_back();
new_entry.m_cluster_offset = m_group_data->m_group_clusters.size(); new_entry.m_cluster_offset = clusterset.m_group_data->m_group_clusters.size();
new_entry.m_cluster_count = 0; new_entry.m_cluster_count = 0;
new_entry.m_deps_offset = m_deps_to_add.size(); new_entry.m_deps_offset = clusterset.m_deps_to_add.size();
new_entry.m_deps_count = 0; new_entry.m_deps_count = 0;
uint32_t total_count{0}; uint32_t total_count{0};
// Add all its clusters to it (copying those from an_clusters to m_group_clusters). // Add all its clusters to it (copying those from an_clusters to m_group_clusters).
while (an_clusters_it != an_clusters.end() && an_clusters_it->second == rep) { while (an_clusters_it != an_clusters.end() && an_clusters_it->second == rep) {
m_group_data->m_group_clusters.push_back(an_clusters_it->first); clusterset.m_group_data->m_group_clusters.push_back(an_clusters_it->first);
total_count += an_clusters_it->first->GetTxCount(); total_count += an_clusters_it->first->GetTxCount();
++an_clusters_it; ++an_clusters_it;
++new_entry.m_cluster_count; ++new_entry.m_cluster_count;
} }
// Add all its dependencies to it (copying those back from an_deps to m_deps_to_add). // Add all its dependencies to it (copying those back from an_deps to m_deps_to_add).
while (an_deps_it != an_deps.end() && an_deps_it->second == rep) { while (an_deps_it != an_deps.end() && an_deps_it->second == rep) {
m_deps_to_add.push_back(an_deps_it->first); clusterset.m_deps_to_add.push_back(an_deps_it->first);
++an_deps_it; ++an_deps_it;
++new_entry.m_deps_count; ++new_entry.m_deps_count;
} }
// Detect oversizedness. // Detect oversizedness.
if (total_count > m_max_cluster_count) { if (total_count > m_max_cluster_count) {
m_group_data->m_group_oversized = true; clusterset.m_group_data->m_group_oversized = true;
} }
} }
Assume(an_deps_it == an_deps.end()); Assume(an_deps_it == an_deps.end());
@ -975,23 +984,24 @@ void TxGraphImpl::Merge(std::span<Cluster*> to_merge) noexcept
void TxGraphImpl::ApplyDependencies() noexcept void TxGraphImpl::ApplyDependencies() noexcept
{ {
auto& clusterset = m_clusterset;
// Compute the groups of to-be-merged Clusters (which also applies all removals, and splits). // Compute the groups of to-be-merged Clusters (which also applies all removals, and splits).
GroupClusters(); GroupClusters();
Assume(m_group_data.has_value()); Assume(clusterset.m_group_data.has_value());
// Nothing to do if there are no dependencies to be added. // Nothing to do if there are no dependencies to be added.
if (m_deps_to_add.empty()) return; if (clusterset.m_deps_to_add.empty()) return;
// Dependencies cannot be applied if it would result in oversized clusters. // Dependencies cannot be applied if it would result in oversized clusters.
if (m_group_data->m_group_oversized) return; if (clusterset.m_group_data->m_group_oversized) return;
// For each group of to-be-merged Clusters. // For each group of to-be-merged Clusters.
for (const auto& group_data : m_group_data->m_groups) { for (const auto& group_data : clusterset.m_group_data->m_groups) {
// Invoke Merge() to merge them into a single Cluster. // Invoke Merge() to merge them into a single Cluster.
auto cluster_span = std::span{m_group_data->m_group_clusters} auto cluster_span = std::span{clusterset.m_group_data->m_group_clusters}
.subspan(group_data.m_cluster_offset, group_data.m_cluster_count); .subspan(group_data.m_cluster_offset, group_data.m_cluster_count);
Merge(cluster_span); Merge(cluster_span);
// Actually apply all to-be-added dependencies (all parents and children from this grouping // Actually apply all to-be-added dependencies (all parents and children from this grouping
// belong to the same Cluster at this point because of the merging above). // belong to the same Cluster at this point because of the merging above).
auto deps_span = std::span{m_deps_to_add} auto deps_span = std::span{clusterset.m_deps_to_add}
.subspan(group_data.m_deps_offset, group_data.m_deps_count); .subspan(group_data.m_deps_offset, group_data.m_deps_count);
Assume(!deps_span.empty()); Assume(!deps_span.empty());
const auto& loc = m_entries[deps_span[0].second].m_locator; const auto& loc = m_entries[deps_span[0].second].m_locator;
@ -1000,11 +1010,11 @@ void TxGraphImpl::ApplyDependencies() noexcept
} }
// Wipe the list of to-be-added dependencies now that they are applied. // Wipe the list of to-be-added dependencies now that they are applied.
m_deps_to_add.clear(); clusterset.m_deps_to_add.clear();
Compact(); Compact();
// Also no further Cluster mergings are needed (note that we clear, but don't set to // Also no further Cluster mergings are needed (note that we clear, but don't set to
// std::nullopt, as that would imply the groupings are unknown). // std::nullopt, as that would imply the groupings are unknown).
m_group_data = GroupData{}; clusterset.m_group_data = GroupData{};
} }
void Cluster::Relinearize(TxGraphImpl& graph, uint64_t max_iters) noexcept void Cluster::Relinearize(TxGraphImpl& graph, uint64_t max_iters) noexcept
@ -1060,7 +1070,7 @@ TxGraph::Ref TxGraphImpl::AddTransaction(const FeePerWeight& feerate) noexcept
auto cluster_ptr = cluster.get(); auto cluster_ptr = cluster.get();
InsertCluster(std::move(cluster), QualityLevel::OPTIMAL); InsertCluster(std::move(cluster), QualityLevel::OPTIMAL);
cluster_ptr->Updated(*this); cluster_ptr->Updated(*this);
++m_txcount; ++m_clusterset.m_txcount;
// Return the Ref. // Return the Ref.
return ret; return ret;
} }
@ -1075,9 +1085,9 @@ void TxGraphImpl::RemoveTransaction(const Ref& arg) noexcept
auto cluster = m_entries[GetRefIndex(arg)].m_locator.cluster; auto cluster = m_entries[GetRefIndex(arg)].m_locator.cluster;
if (cluster == nullptr) return; if (cluster == nullptr) return;
// Remember that the transaction is to be removed. // Remember that the transaction is to be removed.
m_to_remove.push_back(GetRefIndex(arg)); m_clusterset.m_to_remove.push_back(GetRefIndex(arg));
// Wipe m_group_data (as it will need to be recomputed). // Wipe m_group_data (as it will need to be recomputed).
m_group_data.reset(); m_clusterset.m_group_data.reset();
} }
void TxGraphImpl::AddDependency(const Ref& parent, const Ref& child) noexcept void TxGraphImpl::AddDependency(const Ref& parent, const Ref& child) noexcept
@ -1095,9 +1105,9 @@ void TxGraphImpl::AddDependency(const Ref& parent, const Ref& child) noexcept
auto chl_cluster = m_entries[GetRefIndex(child)].m_locator.cluster; auto chl_cluster = m_entries[GetRefIndex(child)].m_locator.cluster;
if (chl_cluster == nullptr) return; if (chl_cluster == nullptr) return;
// Remember that this dependency is to be applied. // Remember that this dependency is to be applied.
m_deps_to_add.emplace_back(GetRefIndex(parent), GetRefIndex(child)); m_clusterset.m_deps_to_add.emplace_back(GetRefIndex(parent), GetRefIndex(child));
// Wipe m_group_data (as it will need to be recomputed). // Wipe m_group_data (as it will need to be recomputed).
m_group_data.reset(); m_clusterset.m_group_data.reset();
} }
bool TxGraphImpl::Exists(const Ref& arg) noexcept bool TxGraphImpl::Exists(const Ref& arg) noexcept
@ -1158,7 +1168,7 @@ std::vector<TxGraph::Ref*> TxGraphImpl::GetAncestors(const Ref& arg) noexcept
// Apply all removals and dependencies, as the result might be incorrect otherwise. // Apply all removals and dependencies, as the result might be incorrect otherwise.
ApplyDependencies(); ApplyDependencies();
// Ancestry cannot be known if unapplied dependencies remain. // Ancestry cannot be known if unapplied dependencies remain.
Assume(m_deps_to_add.empty()); Assume(m_clusterset.m_deps_to_add.empty());
// Find the Cluster the argument is in, and return the empty vector if it isn't in any. // Find the Cluster the argument is in, and return the empty vector if it isn't in any.
auto cluster = m_entries[GetRefIndex(arg)].m_locator.cluster; auto cluster = m_entries[GetRefIndex(arg)].m_locator.cluster;
if (cluster == nullptr) return {}; if (cluster == nullptr) return {};
@ -1174,7 +1184,7 @@ std::vector<TxGraph::Ref*> TxGraphImpl::GetDescendants(const Ref& arg) noexcept
// Apply all removals and dependencies, as the result might be incorrect otherwise. // Apply all removals and dependencies, as the result might be incorrect otherwise.
ApplyDependencies(); ApplyDependencies();
// Ancestry cannot be known if unapplied dependencies remain. // Ancestry cannot be known if unapplied dependencies remain.
Assume(m_deps_to_add.empty()); Assume(m_clusterset.m_deps_to_add.empty());
// Find the Cluster the argument is in, and return the empty vector if it isn't in any. // Find the Cluster the argument is in, and return the empty vector if it isn't in any.
auto cluster = m_entries[GetRefIndex(arg)].m_locator.cluster; auto cluster = m_entries[GetRefIndex(arg)].m_locator.cluster;
if (cluster == nullptr) return {}; if (cluster == nullptr) return {};
@ -1190,7 +1200,7 @@ std::vector<TxGraph::Ref*> TxGraphImpl::GetCluster(const Ref& arg) noexcept
// Apply all removals and dependencies, as the result might be incorrect otherwise. // Apply all removals and dependencies, as the result might be incorrect otherwise.
ApplyDependencies(); ApplyDependencies();
// Cluster linearization cannot be known if unapplied dependencies remain. // Cluster linearization cannot be known if unapplied dependencies remain.
Assume(m_deps_to_add.empty()); Assume(m_clusterset.m_deps_to_add.empty());
// Find the Cluster the argument is in, and return the empty vector if it isn't in any. // Find the Cluster the argument is in, and return the empty vector if it isn't in any.
auto cluster = m_entries[GetRefIndex(arg)].m_locator.cluster; auto cluster = m_entries[GetRefIndex(arg)].m_locator.cluster;
if (cluster == nullptr) return {}; if (cluster == nullptr) return {};
@ -1202,7 +1212,7 @@ std::vector<TxGraph::Ref*> TxGraphImpl::GetCluster(const Ref& arg) noexcept
TxGraph::GraphIndex TxGraphImpl::GetTransactionCount() noexcept TxGraph::GraphIndex TxGraphImpl::GetTransactionCount() noexcept
{ {
ApplyRemovals(); ApplyRemovals();
return m_txcount; return m_clusterset.m_txcount;
} }
FeePerWeight TxGraphImpl::GetIndividualFeerate(const Ref& arg) noexcept FeePerWeight TxGraphImpl::GetIndividualFeerate(const Ref& arg) noexcept
@ -1227,7 +1237,7 @@ FeePerWeight TxGraphImpl::GetChunkFeerate(const Ref& arg) noexcept
// Apply all removals and dependencies, as the result might be inaccurate otherwise. // Apply all removals and dependencies, as the result might be inaccurate otherwise.
ApplyDependencies(); ApplyDependencies();
// Chunk feerates cannot be accurately known if unapplied dependencies remain. // Chunk feerates cannot be accurately known if unapplied dependencies remain.
Assume(m_deps_to_add.empty()); Assume(m_clusterset.m_deps_to_add.empty());
// Find the cluster the argument is in, and return the empty FeePerWeight if it isn't in any. // Find the cluster the argument is in, and return the empty FeePerWeight if it isn't in any.
auto cluster = m_entries[GetRefIndex(arg)].m_locator.cluster; auto cluster = m_entries[GetRefIndex(arg)].m_locator.cluster;
if (cluster == nullptr) return {}; if (cluster == nullptr) return {};
@ -1243,8 +1253,8 @@ bool TxGraphImpl::IsOversized() noexcept
// Find which Clusters will need to be merged together, as that is where the oversize // Find which Clusters will need to be merged together, as that is where the oversize
// property is assessed. // property is assessed.
GroupClusters(); GroupClusters();
Assume(m_group_data.has_value()); Assume(m_clusterset.m_group_data.has_value());
return m_group_data->m_group_oversized; return m_clusterset.m_group_data->m_group_oversized;
} }
void Cluster::SetFee(TxGraphImpl& graph, DepGraphIndex idx, int64_t fee) noexcept void Cluster::SetFee(TxGraphImpl& graph, DepGraphIndex idx, int64_t fee) noexcept
@ -1346,11 +1356,12 @@ void TxGraphImpl::SanityCheck() const
} }
auto& clusterset = m_clusterset;
std::set<const Cluster*> actual_clusters; std::set<const Cluster*> actual_clusters;
// For all quality levels... // For all quality levels...
for (int qual = 0; qual < int(QualityLevel::NONE); ++qual) { for (int qual = 0; qual < int(QualityLevel::NONE); ++qual) {
QualityLevel quality{qual}; QualityLevel quality{qual};
const auto& quality_clusters = m_clusters[qual]; const auto& quality_clusters = clusterset.m_clusters[qual];
// ... for all clusters in them ... // ... for all clusters in them ...
for (ClusterSetIndex setindex = 0; setindex < quality_clusters.size(); ++setindex) { for (ClusterSetIndex setindex = 0; setindex < quality_clusters.size(); ++setindex) {
const auto& cluster = *quality_clusters[setindex]; const auto& cluster = *quality_clusters[setindex];
@ -1368,13 +1379,13 @@ void TxGraphImpl::SanityCheck() const
} }
// Verify that all to-be-removed transactions have valid identifiers, and aren't removed yet. // Verify that all to-be-removed transactions have valid identifiers, and aren't removed yet.
for (GraphIndex idx : m_to_remove) { for (GraphIndex idx : m_clusterset.m_to_remove) {
assert(idx < m_entries.size()); assert(idx < m_entries.size());
assert(m_entries[idx].m_locator.IsPresent()); assert(m_entries[idx].m_locator.IsPresent());
} }
// Verify that all to-be-added dependencies have valid identifiers. // Verify that all to-be-added dependencies have valid identifiers.
for (auto [par_idx, chl_idx] : m_deps_to_add) { for (auto [par_idx, chl_idx] : m_clusterset.m_deps_to_add) {
assert(par_idx != chl_idx); assert(par_idx != chl_idx);
assert(par_idx < m_entries.size()); assert(par_idx < m_entries.size());
assert(chl_idx < m_entries.size()); assert(chl_idx < m_entries.size());
@ -1389,7 +1400,9 @@ void TxGraphImpl::SanityCheck() const
// If no to-be-removed transactions, or to-be-added dependencies remain, m_unlinked must be // If no to-be-removed transactions, or to-be-added dependencies remain, m_unlinked must be
// empty (to prevent memory leaks due to an ever-growing m_entries vector). // empty (to prevent memory leaks due to an ever-growing m_entries vector).
if (m_to_remove.empty() && m_deps_to_add.empty()) assert(actual_unlinked.empty()); if (clusterset.m_to_remove.empty() && clusterset.m_deps_to_add.empty()) {
assert(actual_unlinked.empty());
}
} }
} // namespace } // namespace