@@ -303,6 +303,8 @@ class TxGraphImpl final : public TxGraph
303
303
Locator m_locator[MAX_LEVELS];
304
304
/* * The chunk feerate of this transaction in main (if present in m_locator[0]). */
305
305
FeePerWeight m_main_chunk_feerate;
306
+ /* * The position this transaction has in the main linearization (if present). */
307
+ LinearizationIndex m_main_lin_index;
306
308
};
307
309
308
310
/* * The set of all transactions (in all levels combined). GraphIndex values index into this. */
@@ -447,6 +449,7 @@ class TxGraphImpl final : public TxGraph
447
449
std::vector<Ref*> GetDescendants (const Ref& arg, bool main_only = false ) noexcept final ;
448
450
GraphIndex GetTransactionCount (bool main_only = false ) noexcept final ;
449
451
bool IsOversized (bool main_only = false ) noexcept final ;
452
+ std::strong_ordering CompareMainOrder (const Ref& a, const Ref& b) noexcept final ;
450
453
451
454
void SanityCheck () const final ;
452
455
};
@@ -499,9 +502,10 @@ void Cluster::Updated(TxGraphImpl& graph) noexcept
499
502
entry.m_locator [m_level].SetPresent (this , idx);
500
503
}
501
504
// If this is for the main graph (level = 0), and the Cluster's quality is ACCEPTABLE or
502
- // OPTIMAL, compute its chunking and store its information in the Entry's m_main_chunk_feerate.
503
- // These fields are only accessed after making the entire graph ACCEPTABLE, so it is pointless
504
- // to compute these if we haven't reached that quality level yet.
505
+ // OPTIMAL, compute its chunking and store its information in the Entry's m_main_lin_index
506
+ // and m_main_chunk_feerate. These fields are only accessed after making the entire graph
507
+ // ACCEPTABLE, so it is pointless to compute these if we haven't reached that quality level
508
+ // yet.
505
509
if (m_level == 0 && IsAcceptable ()) {
506
510
LinearizationChunking chunking (m_depgraph, m_linearization);
507
511
LinearizationIndex lin_idx{0 };
@@ -511,9 +515,10 @@ void Cluster::Updated(TxGraphImpl& graph) noexcept
511
515
Assume (chunk.transactions .Any ());
512
516
// Iterate over the transactions in the linearization, which must match those in chunk.
513
517
do {
514
- DepGraphIndex idx = m_linearization[lin_idx++ ];
518
+ DepGraphIndex idx = m_linearization[lin_idx];
515
519
GraphIndex graph_idx = m_mapping[idx];
516
520
auto & entry = graph.m_entries [graph_idx];
521
+ entry.m_main_lin_index = lin_idx++;
517
522
entry.m_main_chunk_feerate = FeePerWeight::FromFeeFrac (chunk.feerate );
518
523
Assume (chunk.transactions [idx]);
519
524
chunk.transactions .Reset (idx);
@@ -594,6 +599,10 @@ void Cluster::ApplyRemovals(TxGraphImpl& graph, std::span<GraphIndex>& to_remove
594
599
// are just never accessed, but set it to -1 here to increase the ability to detect a bug
595
600
// that causes it to be accessed regardless.
596
601
m_mapping[locator.index ] = GraphIndex (-1 );
602
+ // - Remove its linearization index from the Entry (if in main).
603
+ if (m_level == 0 ) {
604
+ entry.m_main_lin_index = LinearizationIndex (-1 );
605
+ }
597
606
// - Mark it as missing/removed in the Entry's locator.
598
607
graph.ClearLocator (m_level, idx);
599
608
to_remove = to_remove.subspan (1 );
@@ -1730,6 +1739,33 @@ void TxGraphImpl::SetTransactionFee(const Ref& ref, int64_t fee) noexcept
1730
1739
}
1731
1740
}
1732
1741
1742
+ std::strong_ordering TxGraphImpl::CompareMainOrder (const Ref& a, const Ref& b) noexcept
1743
+ {
1744
+ // The references must not be empty.
1745
+ Assume (GetRefGraph (a) == this );
1746
+ Assume (GetRefGraph (b) == this );
1747
+ // Apply dependencies in main.
1748
+ ApplyDependencies (0 );
1749
+ Assume (m_main_clusterset.m_deps_to_add .empty ());
1750
+ // Make both involved Clusters acceptable, so chunk feerates are relevant.
1751
+ const auto & entry_a = m_entries[GetRefIndex (a)];
1752
+ const auto & entry_b = m_entries[GetRefIndex (b)];
1753
+ const auto & locator_a = entry_a.m_locator [0 ];
1754
+ const auto & locator_b = entry_b.m_locator [0 ];
1755
+ Assume (locator_a.IsPresent ());
1756
+ Assume (locator_b.IsPresent ());
1757
+ MakeAcceptable (*locator_a.cluster );
1758
+ MakeAcceptable (*locator_b.cluster );
1759
+ // Compare chunk feerates, and return result if it differs.
1760
+ auto feerate_cmp = FeeRateCompare (entry_b.m_main_chunk_feerate , entry_a.m_main_chunk_feerate );
1761
+ if (feerate_cmp < 0 ) return std::strong_ordering::less;
1762
+ if (feerate_cmp > 0 ) return std::strong_ordering::greater;
1763
+ // Compare Cluster* as tie-break for equal chunk feerates.
1764
+ if (locator_a.cluster != locator_b.cluster ) return locator_a.cluster <=> locator_b.cluster ;
1765
+ // As final tie-break, compare position within cluster linearization.
1766
+ return entry_a.m_main_lin_index <=> entry_b.m_main_lin_index ;
1767
+ }
1768
+
1733
1769
void Cluster::SanityCheck (const TxGraphImpl& graph, int level) const
1734
1770
{
1735
1771
// There must be an m_mapping for each m_depgraph position (including holes).
@@ -1747,6 +1783,7 @@ void Cluster::SanityCheck(const TxGraphImpl& graph, int level) const
1747
1783
1748
1784
// Verify m_linearization.
1749
1785
SetType m_done;
1786
+ LinearizationIndex linindex{0 };
1750
1787
assert (m_depgraph.IsAcyclic ());
1751
1788
for (auto lin_pos : m_linearization) {
1752
1789
assert (lin_pos < m_mapping.size ());
@@ -1759,6 +1796,8 @@ void Cluster::SanityCheck(const TxGraphImpl& graph, int level) const
1759
1796
assert (entry.m_locator [level].index == lin_pos);
1760
1797
// For main-level entries, check linearization position and chunk feerate.
1761
1798
if (level == 0 && IsAcceptable ()) {
1799
+ assert (entry.m_main_lin_index == linindex);
1800
+ ++linindex;
1762
1801
if (!linchunking.GetChunk (0 ).transactions [lin_pos]) {
1763
1802
linchunking.MarkDone (linchunking.GetChunk (0 ).transactions );
1764
1803
}
0 commit comments