mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-12-11 13:13:49 +01:00
txgraph: Make max cluster count configurable and "oversize" state (feature)
Instead of leaving the responsibility on higher layers to guarantee that no connected component within TxGraph (a barely exposed concept, except through GetCluster()) exceeds the cluster count limit, move this responsibility to TxGraph itself: * TxGraph retains a cluster count limit, but it becomes configurable at construction time (this primarily helps with testing that it is properly enforced). * It is always allowed to perform mutators on TxGraph, even if they would cause the cluster count limit to be exceeded. Instead, TxGraph exposes an IsOversized() function, which queries whether it is in a special "oversize" state. * During oversize state, many inspectors are unavailable, but mutators remain valid, so the higher layer can "fix" the oversize state before continuing.
This commit is contained in:
@@ -49,7 +49,7 @@ class Cluster
|
||||
{
|
||||
friend class TxGraphImpl;
|
||||
using GraphIndex = TxGraph::GraphIndex;
|
||||
using SetType = BitSet<CLUSTER_COUNT_LIMIT>;
|
||||
using SetType = BitSet<MAX_CLUSTER_COUNT_LIMIT>;
|
||||
/** The DepGraph for this cluster, holding all feerates, and ancestors/descendants. */
|
||||
DepGraph<SetType> m_depgraph;
|
||||
/** m_mapping[i] gives the GraphIndex for the position i transaction in m_depgraph. Values for
|
||||
@@ -160,6 +160,8 @@ class TxGraphImpl final : public TxGraph
|
||||
private:
|
||||
/** Internal RNG. */
|
||||
FastRandomContext m_rng;
|
||||
/** This TxGraphImpl's maximum cluster count limit. */
|
||||
const DepGraphIndex m_max_cluster_count;
|
||||
|
||||
/** Information about one group of Clusters to be merged. */
|
||||
struct GroupEntry
|
||||
@@ -181,6 +183,9 @@ private:
|
||||
std::vector<GroupEntry> m_groups;
|
||||
/** Which clusters are to be merged. GroupEntry::m_cluster_offset indexes into this. */
|
||||
std::vector<Cluster*> m_group_clusters;
|
||||
/** Whether at least one of the groups cannot be applied because it would result in a
|
||||
* Cluster that violates the cluster count limit. */
|
||||
bool m_group_oversized;
|
||||
};
|
||||
|
||||
/** The vectors of clusters, one vector per quality level. ClusterSetIndex indexes into each. */
|
||||
@@ -232,8 +237,13 @@ private:
|
||||
std::vector<GraphIndex> m_unlinked;
|
||||
|
||||
public:
|
||||
/** Construct a new TxGraphImpl. */
|
||||
explicit TxGraphImpl() noexcept {}
|
||||
/** Construct a new TxGraphImpl with the specified maximum cluster count. */
|
||||
explicit TxGraphImpl(DepGraphIndex max_cluster_count) noexcept :
|
||||
m_max_cluster_count(max_cluster_count)
|
||||
{
|
||||
Assume(max_cluster_count >= 1);
|
||||
Assume(max_cluster_count <= MAX_CLUSTER_COUNT_LIMIT);
|
||||
}
|
||||
|
||||
// Cannot move or copy (would invalidate TxGraphImpl* in Ref, MiningOrder, EvictionOrder).
|
||||
TxGraphImpl(const TxGraphImpl&) = delete;
|
||||
@@ -309,6 +319,7 @@ public:
|
||||
std::vector<Ref*> GetAncestors(const Ref& arg) noexcept final;
|
||||
std::vector<Ref*> GetDescendants(const Ref& arg) noexcept final;
|
||||
GraphIndex GetTransactionCount() noexcept final;
|
||||
bool IsOversized() noexcept final;
|
||||
|
||||
void SanityCheck() const final;
|
||||
};
|
||||
@@ -696,7 +707,7 @@ void TxGraphImpl::GroupClusters() noexcept
|
||||
|
||||
// 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
|
||||
// with inefficient Clusters which just end up being split again anyway.
|
||||
// with inefficient and/or oversized Clusters which just end up being split again anyway.
|
||||
SplitAll();
|
||||
|
||||
/** Annotated clusters: an entry for each Cluster, together with the representative for the
|
||||
@@ -833,6 +844,7 @@ void TxGraphImpl::GroupClusters() noexcept
|
||||
// back to m_deps_to_add.
|
||||
m_group_data = GroupData{};
|
||||
m_group_data->m_group_clusters.reserve(an_clusters.size());
|
||||
m_group_data->m_group_oversized = false;
|
||||
m_deps_to_add.clear();
|
||||
m_deps_to_add.reserve(an_deps.size());
|
||||
auto an_deps_it = an_deps.begin();
|
||||
@@ -846,9 +858,11 @@ void TxGraphImpl::GroupClusters() noexcept
|
||||
new_entry.m_cluster_count = 0;
|
||||
new_entry.m_deps_offset = m_deps_to_add.size();
|
||||
new_entry.m_deps_count = 0;
|
||||
uint32_t total_count{0};
|
||||
// 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) {
|
||||
m_group_data->m_group_clusters.push_back(an_clusters_it->first);
|
||||
total_count += an_clusters_it->first->GetTxCount();
|
||||
++an_clusters_it;
|
||||
++new_entry.m_cluster_count;
|
||||
}
|
||||
@@ -858,6 +872,10 @@ void TxGraphImpl::GroupClusters() noexcept
|
||||
++an_deps_it;
|
||||
++new_entry.m_deps_count;
|
||||
}
|
||||
// Detect oversizedness.
|
||||
if (total_count > m_max_cluster_count) {
|
||||
m_group_data->m_group_oversized = true;
|
||||
}
|
||||
}
|
||||
Assume(an_deps_it == an_deps.end());
|
||||
Assume(an_clusters_it == an_clusters.end());
|
||||
@@ -898,6 +916,8 @@ void TxGraphImpl::ApplyDependencies() noexcept
|
||||
Assume(m_group_data.has_value());
|
||||
// Nothing to do if there are no dependencies to be added.
|
||||
if (m_deps_to_add.empty()) return;
|
||||
// Dependencies cannot be applied if it would result in oversized clusters.
|
||||
if (m_group_data->m_group_oversized) return;
|
||||
|
||||
// For each group of to-be-merged Clusters.
|
||||
for (const auto& group_data : m_group_data->m_groups) {
|
||||
@@ -1073,6 +1093,8 @@ std::vector<TxGraph::Ref*> TxGraphImpl::GetAncestors(const Ref& arg) noexcept
|
||||
Assume(GetRefGraph(arg) == this);
|
||||
// Apply all removals and dependencies, as the result might be incorrect otherwise.
|
||||
ApplyDependencies();
|
||||
// Ancestry cannot be known if unapplied dependencies remain.
|
||||
Assume(m_deps_to_add.empty());
|
||||
// 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;
|
||||
if (cluster == nullptr) return {};
|
||||
@@ -1087,6 +1109,8 @@ std::vector<TxGraph::Ref*> TxGraphImpl::GetDescendants(const Ref& arg) noexcept
|
||||
Assume(GetRefGraph(arg) == this);
|
||||
// Apply all removals and dependencies, as the result might be incorrect otherwise.
|
||||
ApplyDependencies();
|
||||
// Ancestry cannot be known if unapplied dependencies remain.
|
||||
Assume(m_deps_to_add.empty());
|
||||
// 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;
|
||||
if (cluster == nullptr) return {};
|
||||
@@ -1101,6 +1125,8 @@ std::vector<TxGraph::Ref*> TxGraphImpl::GetCluster(const Ref& arg) noexcept
|
||||
Assume(GetRefGraph(arg) == this);
|
||||
// Apply all removals and dependencies, as the result might be incorrect otherwise.
|
||||
ApplyDependencies();
|
||||
// Cluster linearization cannot be known if unapplied dependencies remain.
|
||||
Assume(m_deps_to_add.empty());
|
||||
// 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;
|
||||
if (cluster == nullptr) return {};
|
||||
@@ -1136,6 +1162,8 @@ FeePerWeight TxGraphImpl::GetChunkFeerate(const Ref& arg) noexcept
|
||||
Assume(GetRefGraph(arg) == this);
|
||||
// Apply all removals and dependencies, as the result might be inaccurate otherwise.
|
||||
ApplyDependencies();
|
||||
// Chunk feerates cannot be accurately known if unapplied dependencies remain.
|
||||
Assume(m_deps_to_add.empty());
|
||||
// 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;
|
||||
if (cluster == nullptr) return {};
|
||||
@@ -1146,6 +1174,15 @@ FeePerWeight TxGraphImpl::GetChunkFeerate(const Ref& arg) noexcept
|
||||
return entry.m_chunk_feerate;
|
||||
}
|
||||
|
||||
bool TxGraphImpl::IsOversized() noexcept
|
||||
{
|
||||
// Find which Clusters will need to be merged together, as that is where the oversize
|
||||
// property is assessed.
|
||||
GroupClusters();
|
||||
Assume(m_group_data.has_value());
|
||||
return m_group_data->m_group_oversized;
|
||||
}
|
||||
|
||||
void Cluster::SetFee(TxGraphImpl& graph, DepGraphIndex idx, int64_t fee) noexcept
|
||||
{
|
||||
// Make sure the specified DepGraphIndex exists in this Cluster.
|
||||
@@ -1180,6 +1217,8 @@ void Cluster::SanityCheck(const TxGraphImpl& graph) const
|
||||
assert(m_depgraph.PositionRange() == m_mapping.size());
|
||||
// The linearization for this Cluster must contain every transaction once.
|
||||
assert(m_depgraph.TxCount() == m_linearization.size());
|
||||
// The number of transactions in a Cluster cannot exceed m_max_cluster_count.
|
||||
assert(m_linearization.size() <= graph.m_max_cluster_count);
|
||||
// m_quality and m_setindex are checked in TxGraphImpl::SanityCheck.
|
||||
|
||||
// Compute the chunking of m_linearization.
|
||||
@@ -1321,7 +1360,7 @@ TxGraph::Ref::Ref(Ref&& other) noexcept
|
||||
std::swap(m_index, other.m_index);
|
||||
}
|
||||
|
||||
std::unique_ptr<TxGraph> MakeTxGraph() noexcept
|
||||
std::unique_ptr<TxGraph> MakeTxGraph(unsigned max_cluster_count) noexcept
|
||||
{
|
||||
return std::make_unique<TxGraphImpl>();
|
||||
return std::make_unique<TxGraphImpl>(max_cluster_count);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user