@@ -70,12 +70,16 @@ class Cluster
70
70
ClusterSetIndex m_setindex{ClusterSetIndex (-1 )};
71
71
/* * Which level this Cluster is at in the graph (-1=not inserted, 0=main, 1=staging). */
72
72
int m_level{-1 };
73
+ /* * Sequence number for this Cluster (for tie-breaking comparison between equal-chunk-feerate
74
+ transactions in distinct clusters). */
75
+ uint64_t m_sequence;
73
76
74
77
public:
78
+ Cluster () noexcept = delete ;
75
79
/* * Construct an empty Cluster. */
76
- Cluster () noexcept = default ;
80
+ explicit Cluster (uint64_t sequence ) noexcept ;
77
81
/* * Construct a singleton Cluster. */
78
- explicit Cluster (TxGraphImpl& graph, const FeePerWeight& feerate, GraphIndex graph_index) noexcept ;
82
+ explicit Cluster (uint64_t sequence, TxGraphImpl& graph, const FeePerWeight& feerate, GraphIndex graph_index) noexcept ;
79
83
80
84
// Cannot move or copy (would invalidate Cluster* in Locator and ClusterSet). */
81
85
Cluster (const Cluster&) = delete ;
@@ -157,6 +161,7 @@ class Cluster
157
161
void SanityCheck (const TxGraphImpl& graph, int level) const ;
158
162
};
159
163
164
+
160
165
/* * The transaction graph, including staged changes.
161
166
*
162
167
* The overall design of the data structure consists of 3 interlinked representations:
@@ -244,6 +249,8 @@ class TxGraphImpl final : public TxGraph
244
249
ClusterSet m_main_clusterset;
245
250
/* * The staging ClusterSet, if any. */
246
251
std::optional<ClusterSet> m_staging_clusterset;
252
+ /* * Next sequence number to assign to created Clusters. */
253
+ uint64_t m_next_sequence_counter{0 };
247
254
248
255
/* * A Locator that describes whether, where, and in which Cluster an Entry appears.
249
256
* Every Entry has MAX_LEVELS locators, as it may appear in one Cluster per level.
@@ -315,6 +322,18 @@ class TxGraphImpl final : public TxGraph
315
322
/* * Set of Entries which have no linked Ref anymore. */
316
323
std::vector<GraphIndex> m_unlinked;
317
324
325
+ /* * Compare two Cluster* by their m_sequence value (while supporting nullptr). */
326
+ static std::strong_ordering CompareClusters (Cluster* a, Cluster* b) noexcept
327
+ {
328
+ // The nullptr pointer compares before everything else.
329
+ if (a == nullptr || b == nullptr ) {
330
+ return (a != nullptr ) <=> (b != nullptr );
331
+ }
332
+ // If neither pointer is nullptr, compare the Clusters' sequence numbers.
333
+ Assume (a == b || a->m_sequence != b->m_sequence );
334
+ return a->m_sequence <=> b->m_sequence ;
335
+ }
336
+
318
337
public:
319
338
/* * Construct a new TxGraphImpl with the specified maximum cluster count. */
320
339
explicit TxGraphImpl (DepGraphIndex max_cluster_count) noexcept :
@@ -569,15 +588,15 @@ std::vector<Cluster*> TxGraphImpl::GetConflicts() const noexcept
569
588
}
570
589
}
571
590
// Deduplicate the result (the same Cluster may appear multiple times).
572
- std::sort (ret.begin (), ret.end ());
591
+ std::sort (ret.begin (), ret.end (), [](Cluster* a, Cluster* b) noexcept { return CompareClusters (a, b) < 0 ; } );
573
592
ret.erase (std::unique (ret.begin (), ret.end ()), ret.end ());
574
593
return ret;
575
594
}
576
595
577
596
Cluster* Cluster::CopyToStaging (TxGraphImpl& graph) const noexcept
578
597
{
579
598
// Construct an empty Cluster.
580
- auto ret = std::make_unique<Cluster>();
599
+ auto ret = std::make_unique<Cluster>(graph. m_next_sequence_counter ++ );
581
600
auto ptr = ret.get ();
582
601
// Copy depgraph, mapping, and linearization/
583
602
ptr->m_depgraph = m_depgraph;
@@ -710,7 +729,7 @@ bool Cluster::Split(TxGraphImpl& graph) noexcept
710
729
}
711
730
first = false ;
712
731
// Construct a new Cluster to hold the found component.
713
- auto new_cluster = std::make_unique<Cluster>();
732
+ auto new_cluster = std::make_unique<Cluster>(graph. m_next_sequence_counter ++ );
714
733
new_clusters.push_back (new_cluster.get ());
715
734
// Remember that all the component's transactions go to this new Cluster. The positions
716
735
// will be determined below, so use -1 for now.
@@ -956,9 +975,11 @@ void TxGraphImpl::ApplyRemovals(int up_to_level) noexcept
956
975
if (cluster != nullptr ) PullIn (cluster);
957
976
}
958
977
}
959
- // Group the set of to-be-removed entries by Cluster* .
978
+ // Group the set of to-be-removed entries by Cluster::m_sequence .
960
979
std::sort (to_remove.begin (), to_remove.end (), [&](GraphIndex a, GraphIndex b) noexcept {
961
- return std::less{}(m_entries[a].m_locator [level].cluster , m_entries[b].m_locator [level].cluster );
980
+ Cluster* cluster_a = m_entries[a].m_locator [level].cluster ;
981
+ Cluster* cluster_b = m_entries[b].m_locator [level].cluster ;
982
+ return CompareClusters (cluster_a, cluster_b) < 0 ;
962
983
});
963
984
// Process per Cluster.
964
985
std::span to_remove_span{to_remove};
@@ -1103,16 +1124,16 @@ void TxGraphImpl::GroupClusters(int level) noexcept
1103
1124
}
1104
1125
// Sort and deduplicate an_clusters, so we end up with a sorted list of all involved Clusters
1105
1126
// to which dependencies apply.
1106
- std::sort (an_clusters.begin (), an_clusters.end ());
1127
+ std::sort (an_clusters.begin (), an_clusters.end (), []( auto & a, auto & b) noexcept { return CompareClusters (a. first , b. first ) < 0 ; } );
1107
1128
an_clusters.erase (std::unique (an_clusters.begin (), an_clusters.end ()), an_clusters.end ());
1108
1129
1109
- // Sort the dependencies by child Cluster.
1130
+ // Sort the dependencies by child Cluster::m_sequence .
1110
1131
std::sort (clusterset.m_deps_to_add .begin (), clusterset.m_deps_to_add .end (), [&](auto & a, auto & b) noexcept {
1111
1132
auto [_a_par, a_chl] = a;
1112
1133
auto [_b_par, b_chl] = b;
1113
1134
auto a_chl_cluster = FindCluster (a_chl, level);
1114
1135
auto b_chl_cluster = FindCluster (b_chl, level);
1115
- return std::less{} (a_chl_cluster, b_chl_cluster);
1136
+ return CompareClusters (a_chl_cluster, b_chl_cluster) < 0 ;
1116
1137
});
1117
1138
1118
1139
// Run the union-find algorithm to to find partitions of the input Clusters which need to be
@@ -1132,13 +1153,13 @@ void TxGraphImpl::GroupClusters(int level) noexcept
1132
1153
* tree for this partition. */
1133
1154
unsigned rank;
1134
1155
};
1135
- /* * Information about each input Cluster. Sorted by Cluster* pointer . */
1156
+ /* * Information about each input Cluster. Sorted by Cluster::m_sequence . */
1136
1157
std::vector<PartitionData> partition_data;
1137
1158
1138
1159
/* * Given a Cluster, find its corresponding PartitionData. */
1139
1160
auto locate_fn = [&](Cluster* arg) noexcept -> PartitionData* {
1140
1161
auto it = std::lower_bound (partition_data.begin (), partition_data.end (), arg,
1141
- [](auto & a, Cluster* ptr) noexcept { return a.cluster < ptr ; });
1162
+ [](auto & a, Cluster* ptr) noexcept { return CompareClusters ( a.cluster , ptr) < 0 ; });
1142
1163
Assume (it != partition_data.end ());
1143
1164
Assume (it->cluster == arg);
1144
1165
return &*it;
@@ -1219,7 +1240,7 @@ void TxGraphImpl::GroupClusters(int level) noexcept
1219
1240
while (deps_it != clusterset.m_deps_to_add .end ()) {
1220
1241
auto [par, chl] = *deps_it;
1221
1242
auto chl_cluster = FindCluster (chl, level);
1222
- if (std::greater{} (chl_cluster, data.cluster )) break ;
1243
+ if (CompareClusters (chl_cluster, data.cluster ) > 0 ) break ;
1223
1244
// Skip dependencies that apply to earlier Clusters (those necessary are for
1224
1245
// deleted transactions, as otherwise we'd have processed them already).
1225
1246
if (chl_cluster == data.cluster ) {
@@ -1234,8 +1255,8 @@ void TxGraphImpl::GroupClusters(int level) noexcept
1234
1255
1235
1256
// Sort both an_clusters and an_deps by representative of the partition they are in, grouping
1236
1257
// all those applying to the same partition together.
1237
- std::sort (an_deps.begin (), an_deps.end (), [](auto & a, auto & b) noexcept { return a.second < b.second ; });
1238
- std::sort (an_clusters.begin (), an_clusters.end (), [](auto & a, auto & b) noexcept { return a.second < b.second ; });
1258
+ std::sort (an_deps.begin (), an_deps.end (), [](auto & a, auto & b) noexcept { return CompareClusters ( a.second , b.second ) < 0 ; });
1259
+ std::sort (an_clusters.begin (), an_clusters.end (), [](auto & a, auto & b) noexcept { return CompareClusters ( a.second , b.second ) < 0 ; });
1239
1260
1240
1261
// Translate the resulting cluster groups to the m_group_data structure, and the dependencies
1241
1262
// back to m_deps_to_add.
@@ -1390,7 +1411,10 @@ void TxGraphImpl::MakeAllAcceptable(int level) noexcept
1390
1411
}
1391
1412
}
1392
1413
1393
- Cluster::Cluster (TxGraphImpl& graph, const FeePerWeight& feerate, GraphIndex graph_index) noexcept
1414
+ Cluster::Cluster (uint64_t sequence) noexcept : m_sequence{sequence} {}
1415
+
1416
+ Cluster::Cluster (uint64_t sequence, TxGraphImpl& graph, const FeePerWeight& feerate, GraphIndex graph_index) noexcept :
1417
+ m_sequence{sequence}
1394
1418
{
1395
1419
// Create a new transaction in the DepGraph, and remember its position in m_mapping.
1396
1420
auto cluster_idx = m_depgraph.AddTransaction (feerate);
@@ -1410,7 +1434,7 @@ TxGraph::Ref TxGraphImpl::AddTransaction(const FeePerWeight& feerate) noexcept
1410
1434
GetRefGraph (ret) = this ;
1411
1435
GetRefIndex (ret) = idx;
1412
1436
// Construct a new singleton Cluster (which is necessarily optimally linearized).
1413
- auto cluster = std::make_unique<Cluster>(*this , feerate, idx);
1437
+ auto cluster = std::make_unique<Cluster>(m_next_sequence_counter++, *this , feerate, idx);
1414
1438
auto cluster_ptr = cluster.get ();
1415
1439
int level = GetTopLevel ();
1416
1440
auto & clusterset = GetClusterSet (level);
@@ -1606,7 +1630,7 @@ std::vector<TxGraph::Ref*> TxGraphImpl::GetAncestorsUnion(std::span<const Ref* c
1606
1630
matches.emplace_back (cluster, m_entries[GetRefIndex (*arg)].m_locator [cluster->m_level ].index );
1607
1631
}
1608
1632
// Group by Cluster.
1609
- std::sort (matches.begin (), matches.end (), [](auto & a, auto & b) noexcept { return std::less{} (a.first , b.first ); });
1633
+ std::sort (matches.begin (), matches.end (), [](auto & a, auto & b) noexcept { return CompareClusters (a.first , b.first ) < 0 ; });
1610
1634
// Dispatch to the Clusters.
1611
1635
std::span match_span (matches);
1612
1636
std::vector<TxGraph::Ref*> ret;
@@ -1639,7 +1663,7 @@ std::vector<TxGraph::Ref*> TxGraphImpl::GetDescendantsUnion(std::span<const Ref*
1639
1663
matches.emplace_back (cluster, m_entries[GetRefIndex (*arg)].m_locator [cluster->m_level ].index );
1640
1664
}
1641
1665
// Group by Cluster.
1642
- std::sort (matches.begin (), matches.end (), [](auto & a, auto & b) noexcept { return std::less{} (a.first , b.first ); });
1666
+ std::sort (matches.begin (), matches.end (), [](auto & a, auto & b) noexcept { return CompareClusters (a.first , b.first ) < 0 ; });
1643
1667
// Dispatch to the Clusters.
1644
1668
std::span match_span (matches);
1645
1669
std::vector<TxGraph::Ref*> ret;
@@ -1867,7 +1891,9 @@ std::strong_ordering TxGraphImpl::CompareMainOrder(const Ref& a, const Ref& b) n
1867
1891
if (feerate_cmp < 0 ) return std::strong_ordering::less;
1868
1892
if (feerate_cmp > 0 ) return std::strong_ordering::greater;
1869
1893
// Compare Cluster* as tie-break for equal chunk feerates.
1870
- if (locator_a.cluster != locator_b.cluster ) return locator_a.cluster <=> locator_b.cluster ;
1894
+ if (locator_a.cluster != locator_b.cluster ) {
1895
+ return CompareClusters (locator_a.cluster , locator_b.cluster );
1896
+ }
1871
1897
// As final tie-break, compare position within cluster linearization.
1872
1898
return entry_a.m_main_lin_index <=> entry_b.m_main_lin_index ;
1873
1899
}
@@ -1889,7 +1915,7 @@ TxGraph::GraphIndex TxGraphImpl::CountDistinctClusters(std::span<const Ref* cons
1889
1915
if (cluster != nullptr ) clusters.push_back (cluster);
1890
1916
}
1891
1917
// Count the number of distinct elements in clusters.
1892
- std::sort (clusters.begin (), clusters.end ());
1918
+ std::sort (clusters.begin (), clusters.end (), [](Cluster* a, Cluster* b) noexcept { return CompareClusters (a, b) < 0 ; } );
1893
1919
Cluster* last{nullptr };
1894
1920
GraphIndex ret{0 };
1895
1921
for (Cluster* cluster : clusters) {
@@ -1951,6 +1977,8 @@ void TxGraphImpl::SanityCheck() const
1951
1977
std::set<const Cluster*> expected_clusters[MAX_LEVELS];
1952
1978
/* * Which GraphIndexes ought to occur in ClusterSet::m_removed, based on m_entries. */
1953
1979
std::set<GraphIndex> expected_removed[MAX_LEVELS];
1980
+ /* * Which Cluster::m_sequence values have been encountered. */
1981
+ std::set<uint64_t > sequences;
1954
1982
/* * Whether compaction is possible in the current state. */
1955
1983
bool compact_possible{true };
1956
1984
@@ -2004,6 +2032,10 @@ void TxGraphImpl::SanityCheck() const
2004
2032
// ... for all clusters in them ...
2005
2033
for (ClusterSetIndex setindex = 0 ; setindex < quality_clusters.size (); ++setindex) {
2006
2034
const auto & cluster = *quality_clusters[setindex];
2035
+ // Check the sequence number.
2036
+ assert (cluster.m_sequence < m_next_sequence_counter);
2037
+ assert (sequences.count (cluster.m_sequence ) == 0 );
2038
+ sequences.insert (cluster.m_sequence );
2007
2039
// Remember we saw this Cluster (only if it is non-empty; empty Clusters aren't
2008
2040
// expected to be referenced by the Entry vector).
2009
2041
if (cluster.GetTxCount () != 0 ) {
0 commit comments