mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 23:09:44 -04:00
txgraph: Add ability to configure maximum cluster size/weight (feature)
This is integrated with the oversized property: the graph is oversized when any connected component within it contains more than the cluster count limit many transactions, or when their combined size/weight exceeds the cluster size limit. It becomes disallowed to call AddTransaction with a size larger than this limit. In addition, SetTransactionFeeRate becomes SetTransactionFee, so that we do not need to deal with the case that a call to this function might affect the oversizedness.
This commit is contained in:
parent
a0becaaa9c
commit
7e869040fe
3 changed files with 55 additions and 17 deletions
|
@ -56,9 +56,12 @@ struct SimTxGraph
|
||||||
/** Which transactions have been modified in the graph since creation, either directly or by
|
/** Which transactions have been modified in the graph since creation, either directly or by
|
||||||
* being in a cluster which includes modifications. Only relevant for the staging graph. */
|
* being in a cluster which includes modifications. Only relevant for the staging graph. */
|
||||||
SetType modified;
|
SetType modified;
|
||||||
|
/** The configured maximum total size of transactions per cluster. */
|
||||||
|
uint64_t max_cluster_size;
|
||||||
|
|
||||||
/** Construct a new SimTxGraph with the specified maximum cluster count. */
|
/** Construct a new SimTxGraph with the specified maximum cluster count. */
|
||||||
explicit SimTxGraph(DepGraphIndex max_cluster) : max_cluster_count(max_cluster) {}
|
explicit SimTxGraph(DepGraphIndex max_cluster, uint64_t max_size) :
|
||||||
|
max_cluster_count(max_cluster), max_cluster_size(max_size) {}
|
||||||
|
|
||||||
// Permit copying and moving.
|
// Permit copying and moving.
|
||||||
SimTxGraph(const SimTxGraph&) noexcept = default;
|
SimTxGraph(const SimTxGraph&) noexcept = default;
|
||||||
|
@ -78,6 +81,9 @@ struct SimTxGraph
|
||||||
while (todo.Any()) {
|
while (todo.Any()) {
|
||||||
auto component = graph.FindConnectedComponent(todo);
|
auto component = graph.FindConnectedComponent(todo);
|
||||||
if (component.Count() > max_cluster_count) oversized = true;
|
if (component.Count() > max_cluster_count) oversized = true;
|
||||||
|
uint64_t component_size{0};
|
||||||
|
for (auto i : component) component_size += graph.FeeRate(i).size;
|
||||||
|
if (component_size > max_cluster_size) oversized = true;
|
||||||
todo -= component;
|
todo -= component;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,12 +268,16 @@ FUZZ_TARGET(txgraph)
|
||||||
|
|
||||||
// Decide the maximum number of transactions per cluster we will use in this simulation.
|
// Decide the maximum number of transactions per cluster we will use in this simulation.
|
||||||
auto max_count = provider.ConsumeIntegralInRange<DepGraphIndex>(1, MAX_CLUSTER_COUNT_LIMIT);
|
auto max_count = provider.ConsumeIntegralInRange<DepGraphIndex>(1, MAX_CLUSTER_COUNT_LIMIT);
|
||||||
|
// And the maximum combined size of transactions per cluster.
|
||||||
|
auto max_size = provider.ConsumeIntegralInRange<uint64_t>(1, 0x3fffff * MAX_CLUSTER_COUNT_LIMIT);
|
||||||
|
// Maximum individual transaction size.
|
||||||
|
auto max_tx_size = std::min<uint64_t>(0x3fffff, max_size);
|
||||||
|
|
||||||
// Construct a real graph, and a vector of simulated graphs (main, and possibly staging).
|
// Construct a real graph, and a vector of simulated graphs (main, and possibly staging).
|
||||||
auto real = MakeTxGraph(max_count);
|
auto real = MakeTxGraph(max_count, max_size);
|
||||||
std::vector<SimTxGraph> sims;
|
std::vector<SimTxGraph> sims;
|
||||||
sims.reserve(2);
|
sims.reserve(2);
|
||||||
sims.emplace_back(max_count);
|
sims.emplace_back(max_count, max_size);
|
||||||
|
|
||||||
/** Struct encapsulating information about a BlockBuilder that's currently live. */
|
/** Struct encapsulating information about a BlockBuilder that's currently live. */
|
||||||
struct BlockBuilderData
|
struct BlockBuilderData
|
||||||
|
@ -391,12 +401,12 @@ FUZZ_TARGET(txgraph)
|
||||||
if (alt) {
|
if (alt) {
|
||||||
// If alt is true, pick fee and size from the entire range.
|
// If alt is true, pick fee and size from the entire range.
|
||||||
fee = provider.ConsumeIntegralInRange<int64_t>(-0x8000000000000, 0x7ffffffffffff);
|
fee = provider.ConsumeIntegralInRange<int64_t>(-0x8000000000000, 0x7ffffffffffff);
|
||||||
size = provider.ConsumeIntegralInRange<int32_t>(1, 0x3fffff);
|
size = provider.ConsumeIntegralInRange<int32_t>(1, max_tx_size);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, use smaller range which consume fewer fuzz input bytes, as just
|
// Otherwise, use smaller range which consume fewer fuzz input bytes, as just
|
||||||
// these are likely sufficient to trigger all interesting code paths already.
|
// these are likely sufficient to trigger all interesting code paths already.
|
||||||
fee = provider.ConsumeIntegral<uint8_t>();
|
fee = provider.ConsumeIntegral<uint8_t>();
|
||||||
size = provider.ConsumeIntegral<uint8_t>() + 1;
|
size = provider.ConsumeIntegralInRange<uint32_t>(1, std::min<uint32_t>(0xff, max_tx_size));
|
||||||
}
|
}
|
||||||
FeePerWeight feerate{fee, size};
|
FeePerWeight feerate{fee, size};
|
||||||
// Create a real TxGraph::Ref.
|
// Create a real TxGraph::Ref.
|
||||||
|
@ -570,13 +580,17 @@ FUZZ_TARGET(txgraph)
|
||||||
assert(result.size() <= max_count);
|
assert(result.size() <= max_count);
|
||||||
// Require the result to be topologically valid and not contain duplicates.
|
// Require the result to be topologically valid and not contain duplicates.
|
||||||
auto left = sel_sim.graph.Positions();
|
auto left = sel_sim.graph.Positions();
|
||||||
|
uint64_t total_size{0};
|
||||||
for (auto refptr : result) {
|
for (auto refptr : result) {
|
||||||
auto simpos = sel_sim.Find(refptr);
|
auto simpos = sel_sim.Find(refptr);
|
||||||
|
total_size += sel_sim.graph.FeeRate(simpos).size;
|
||||||
assert(simpos != SimTxGraph::MISSING);
|
assert(simpos != SimTxGraph::MISSING);
|
||||||
assert(left[simpos]);
|
assert(left[simpos]);
|
||||||
left.Reset(simpos);
|
left.Reset(simpos);
|
||||||
assert(!sel_sim.graph.Ancestors(simpos).Overlaps(left));
|
assert(!sel_sim.graph.Ancestors(simpos).Overlaps(left));
|
||||||
}
|
}
|
||||||
|
// Check cluster size limit.
|
||||||
|
assert(total_size <= max_size);
|
||||||
// Require the set to be connected.
|
// Require the set to be connected.
|
||||||
auto result_set = sel_sim.MakeSet(result);
|
auto result_set = sel_sim.MakeSet(result);
|
||||||
assert(sel_sim.graph.IsConnected(result_set));
|
assert(sel_sim.graph.IsConnected(result_set));
|
||||||
|
@ -956,13 +970,17 @@ FUZZ_TARGET(txgraph)
|
||||||
// linearization).
|
// linearization).
|
||||||
std::vector<DepGraphIndex> simlin;
|
std::vector<DepGraphIndex> simlin;
|
||||||
SimTxGraph::SetType done;
|
SimTxGraph::SetType done;
|
||||||
|
uint64_t total_size{0};
|
||||||
for (TxGraph::Ref* ptr : cluster) {
|
for (TxGraph::Ref* ptr : cluster) {
|
||||||
auto simpos = sim.Find(ptr);
|
auto simpos = sim.Find(ptr);
|
||||||
assert(sim.graph.Descendants(simpos).IsSubsetOf(component - done));
|
assert(sim.graph.Descendants(simpos).IsSubsetOf(component - done));
|
||||||
done.Set(simpos);
|
done.Set(simpos);
|
||||||
assert(sim.graph.Ancestors(simpos).IsSubsetOf(done));
|
assert(sim.graph.Ancestors(simpos).IsSubsetOf(done));
|
||||||
simlin.push_back(simpos);
|
simlin.push_back(simpos);
|
||||||
|
total_size += sim.graph.FeeRate(simpos).size;
|
||||||
}
|
}
|
||||||
|
// Check cluster size.
|
||||||
|
assert(total_size <= max_size);
|
||||||
// Construct a chunking object for the simulated graph, using the reported cluster
|
// Construct a chunking object for the simulated graph, using the reported cluster
|
||||||
// linearization as ordering, and compare it against the reported chunk feerates.
|
// linearization as ordering, and compare it against the reported chunk feerates.
|
||||||
if (sims.size() == 1 || main_only) {
|
if (sims.size() == 1 || main_only) {
|
||||||
|
|
|
@ -109,6 +109,8 @@ public:
|
||||||
}
|
}
|
||||||
/** Get the number of transactions in this Cluster. */
|
/** Get the number of transactions in this Cluster. */
|
||||||
LinearizationIndex GetTxCount() const noexcept { return m_linearization.size(); }
|
LinearizationIndex GetTxCount() const noexcept { return m_linearization.size(); }
|
||||||
|
/** Get the total size of the transactions in this Cluster. */
|
||||||
|
uint64_t GetTxSize() const noexcept;
|
||||||
/** Given a DepGraphIndex into this Cluster, find the corresponding GraphIndex. */
|
/** Given a DepGraphIndex into this Cluster, find the corresponding GraphIndex. */
|
||||||
GraphIndex GetClusterEntry(DepGraphIndex index) const noexcept { return m_mapping[index]; }
|
GraphIndex GetClusterEntry(DepGraphIndex index) const noexcept { return m_mapping[index]; }
|
||||||
/** Only called by Graph::SwapIndexes. */
|
/** Only called by Graph::SwapIndexes. */
|
||||||
|
@ -199,6 +201,8 @@ private:
|
||||||
FastRandomContext m_rng;
|
FastRandomContext m_rng;
|
||||||
/** This TxGraphImpl's maximum cluster count limit. */
|
/** This TxGraphImpl's maximum cluster count limit. */
|
||||||
const DepGraphIndex m_max_cluster_count;
|
const DepGraphIndex m_max_cluster_count;
|
||||||
|
/** This TxGraphImpl's maximum cluster size limit. */
|
||||||
|
const uint64_t m_max_cluster_size;
|
||||||
|
|
||||||
/** Information about one group of Clusters to be merged. */
|
/** Information about one group of Clusters to be merged. */
|
||||||
struct GroupEntry
|
struct GroupEntry
|
||||||
|
@ -391,9 +395,10 @@ private:
|
||||||
std::vector<GraphIndex> m_unlinked;
|
std::vector<GraphIndex> m_unlinked;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Construct a new TxGraphImpl with the specified maximum cluster count. */
|
/** Construct a new TxGraphImpl with the specified limits. */
|
||||||
explicit TxGraphImpl(DepGraphIndex max_cluster_count) noexcept :
|
explicit TxGraphImpl(DepGraphIndex max_cluster_count, uint64_t max_cluster_size) noexcept :
|
||||||
m_max_cluster_count(max_cluster_count),
|
m_max_cluster_count(max_cluster_count),
|
||||||
|
m_max_cluster_size(max_cluster_size),
|
||||||
m_main_chunkindex(ChunkOrder(this))
|
m_main_chunkindex(ChunkOrder(this))
|
||||||
{
|
{
|
||||||
Assume(max_cluster_count >= 1);
|
Assume(max_cluster_count >= 1);
|
||||||
|
@ -624,6 +629,15 @@ void TxGraphImpl::CreateChunkData(GraphIndex idx, LinearizationIndex chunk_count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t Cluster::GetTxSize() const noexcept
|
||||||
|
{
|
||||||
|
uint64_t ret{0};
|
||||||
|
for (auto i : m_linearization) {
|
||||||
|
ret += m_depgraph.FeeRate(i).size;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void TxGraphImpl::ClearLocator(int level, GraphIndex idx) noexcept
|
void TxGraphImpl::ClearLocator(int level, GraphIndex idx) noexcept
|
||||||
{
|
{
|
||||||
auto& entry = m_entries[idx];
|
auto& entry = m_entries[idx];
|
||||||
|
@ -1428,10 +1442,12 @@ void TxGraphImpl::GroupClusters(int level) noexcept
|
||||||
new_entry.m_deps_offset = clusterset.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};
|
||||||
|
uint64_t total_size{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) {
|
||||||
clusterset.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();
|
||||||
|
total_size += an_clusters_it->first->GetTxSize();
|
||||||
++an_clusters_it;
|
++an_clusters_it;
|
||||||
++new_entry.m_cluster_count;
|
++new_entry.m_cluster_count;
|
||||||
}
|
}
|
||||||
|
@ -1442,7 +1458,7 @@ void TxGraphImpl::GroupClusters(int level) noexcept
|
||||||
++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 || total_size > m_max_cluster_size) {
|
||||||
clusterset.m_group_data->m_group_oversized = true;
|
clusterset.m_group_data->m_group_oversized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1576,6 +1592,7 @@ Cluster::Cluster(uint64_t sequence, TxGraphImpl& graph, const FeePerWeight& feer
|
||||||
TxGraph::Ref TxGraphImpl::AddTransaction(const FeePerWeight& feerate) noexcept
|
TxGraph::Ref TxGraphImpl::AddTransaction(const FeePerWeight& feerate) noexcept
|
||||||
{
|
{
|
||||||
Assume(m_main_chunkindex_observers == 0 || GetTopLevel() != 0);
|
Assume(m_main_chunkindex_observers == 0 || GetTopLevel() != 0);
|
||||||
|
Assume(feerate.size > 0 && uint64_t(feerate.size) <= m_max_cluster_size);
|
||||||
// Construct a new Ref.
|
// Construct a new Ref.
|
||||||
Ref ret;
|
Ref ret;
|
||||||
// Construct a new Entry, and link it with the Ref.
|
// Construct a new Entry, and link it with the Ref.
|
||||||
|
@ -2121,6 +2138,8 @@ void Cluster::SanityCheck(const TxGraphImpl& graph, int level) const
|
||||||
assert(m_linearization.size() <= graph.m_max_cluster_count);
|
assert(m_linearization.size() <= graph.m_max_cluster_count);
|
||||||
// The level must match the level the Cluster occurs in.
|
// The level must match the level the Cluster occurs in.
|
||||||
assert(m_level == level);
|
assert(m_level == level);
|
||||||
|
// The sum of their sizes cannot exceed m_max_cluster_size.
|
||||||
|
assert(GetTxSize() <= graph.m_max_cluster_size);
|
||||||
// m_quality and m_setindex are checked in TxGraphImpl::SanityCheck.
|
// m_quality and m_setindex are checked in TxGraphImpl::SanityCheck.
|
||||||
|
|
||||||
// Compute the chunking of m_linearization.
|
// Compute the chunking of m_linearization.
|
||||||
|
@ -2498,7 +2517,7 @@ TxGraph::Ref::Ref(Ref&& other) noexcept
|
||||||
std::swap(m_index, other.m_index);
|
std::swap(m_index, other.m_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<TxGraph> MakeTxGraph(unsigned max_cluster_count) noexcept
|
std::unique_ptr<TxGraph> MakeTxGraph(unsigned max_cluster_count, uint64_t max_cluster_size) noexcept
|
||||||
{
|
{
|
||||||
return std::make_unique<TxGraphImpl>(max_cluster_count);
|
return std::make_unique<TxGraphImpl>(max_cluster_count, max_cluster_size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,10 +63,10 @@ public:
|
||||||
/** Virtual destructor, so inheriting is safe. */
|
/** Virtual destructor, so inheriting is safe. */
|
||||||
virtual ~TxGraph() = default;
|
virtual ~TxGraph() = default;
|
||||||
/** Construct a new transaction with the specified feerate, and return a Ref to it.
|
/** 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
|
* If a staging graph exists, the new transaction is only created there. feerate.size cannot
|
||||||
* further calls, only Refs created by AddTransaction() are allowed to be passed to this
|
* exceed the graph's max cluster size. In all further calls, only Refs created by
|
||||||
* TxGraph object (or empty Ref objects). Ref objects may outlive the TxGraph they were
|
* AddTransaction() are allowed to be passed to this TxGraph object (or empty Ref objects).
|
||||||
* created for. */
|
* Ref objects may outlive the TxGraph they were created for. */
|
||||||
[[nodiscard]] virtual Ref AddTransaction(const FeePerWeight& feerate) noexcept = 0;
|
[[nodiscard]] virtual Ref AddTransaction(const FeePerWeight& feerate) noexcept = 0;
|
||||||
/** Remove the specified transaction. If a staging graph exists, the removal only happens
|
/** 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.
|
* there. This is a no-op if the transaction was already removed.
|
||||||
|
@ -240,8 +240,9 @@ public:
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Construct a new TxGraph with the specified limit on transactions within a cluster. That
|
/** Construct a new TxGraph with the specified limit on transactions within a cluster, and the
|
||||||
* number cannot exceed MAX_CLUSTER_COUNT_LIMIT. */
|
* specified limit on the sum of transaction sizes within a cluster. max_cluster_count cannot
|
||||||
std::unique_ptr<TxGraph> MakeTxGraph(unsigned max_cluster_count) noexcept;
|
* exceed MAX_CLUSTER_COUNT_LIMIT. */
|
||||||
|
std::unique_ptr<TxGraph> MakeTxGraph(unsigned max_cluster_count, uint64_t max_cluster_size) noexcept;
|
||||||
|
|
||||||
#endif // BITCOIN_TXGRAPH_H
|
#endif // BITCOIN_TXGRAPH_H
|
||||||
|
|
Loading…
Add table
Reference in a new issue