@@ -254,6 +254,71 @@ class TxGraphImpl final : public TxGraph
254
254
/* * Next sequence number to assign to created Clusters. */
255
255
uint64_t m_next_sequence_counter{0 };
256
256
257
+ /* * Information about a chunk in the main graph. */
258
+ struct ChunkData
259
+ {
260
+ /* * The Entry which is the last transaction of the chunk. */
261
+ mutable GraphIndex m_graph_index;
262
+ /* * How many transactions the chunk contains. */
263
+ LinearizationIndex m_chunk_count;
264
+
265
+ ChunkData (GraphIndex graph_index, LinearizationIndex chunk_count) noexcept :
266
+ m_graph_index{graph_index}, m_chunk_count{chunk_count} {}
267
+ };
268
+
269
+ /* * Compare two Cluster* by their m_sequence value (while supporting nullptr). */
270
+ static std::strong_ordering CompareClusters (Cluster* a, Cluster* b) noexcept
271
+ {
272
+ // The nullptr pointer compares before everything else.
273
+ if (a == nullptr || b == nullptr ) {
274
+ return (a != nullptr ) <=> (b != nullptr );
275
+ }
276
+ // If neither pointer is nullptr, compare the Clusters' sequence numbers.
277
+ Assume (a == b || a->m_sequence != b->m_sequence );
278
+ return a->m_sequence <=> b->m_sequence ;
279
+ }
280
+
281
+ /* * Compare two entries (which must both exist within the main graph). */
282
+ std::strong_ordering CompareMainTransactions (GraphIndex a, GraphIndex b) const noexcept
283
+ {
284
+ Assume (a < m_entries.size () && b < m_entries.size ());
285
+ const auto & entry_a = m_entries[a];
286
+ const auto & entry_b = m_entries[b];
287
+ // Compare chunk feerates, and return result if it differs.
288
+ auto feerate_cmp = FeeRateCompare (entry_b.m_main_chunk_feerate , entry_a.m_main_chunk_feerate );
289
+ if (feerate_cmp < 0 ) return std::strong_ordering::less;
290
+ if (feerate_cmp > 0 ) return std::strong_ordering::greater;
291
+ // Compare Cluster m_sequence as tie-break for equal chunk feerates.
292
+ const auto & locator_a = entry_a.m_locator [0 ];
293
+ const auto & locator_b = entry_b.m_locator [0 ];
294
+ Assume (locator_a.IsPresent () && locator_b.IsPresent ());
295
+ if (locator_a.cluster != locator_b.cluster ) {
296
+ return CompareClusters (locator_a.cluster , locator_b.cluster );
297
+ }
298
+ // As final tie-break, compare position within cluster linearization.
299
+ return entry_a.m_main_lin_index <=> entry_b.m_main_lin_index ;
300
+ }
301
+
302
+ /* * Comparator for ChunkData objects in mining order. */
303
+ class ChunkOrder
304
+ {
305
+ const TxGraphImpl* const m_graph;
306
+ public:
307
+ explicit ChunkOrder (const TxGraphImpl* graph) : m_graph(graph) {}
308
+
309
+ bool operator ()(const ChunkData& a, const ChunkData& b) const noexcept
310
+ {
311
+ return m_graph->CompareMainTransactions (a.m_graph_index , b.m_graph_index ) < 0 ;
312
+ }
313
+ };
314
+
315
+ /* * Definition for the mining index type. */
316
+ using ChunkIndex = std::set<ChunkData, ChunkOrder>;
317
+
318
+ /* * Index of ChunkData objects, indexing the last transaction in each chunk in the main
319
+ * graph. */
320
+ ChunkIndex m_main_chunkindex;
321
+
257
322
/* * A Locator that describes whether, where, and in which Cluster an Entry appears.
258
323
* Every Entry has MAX_LEVELS locators, as it may appear in one Cluster per level.
259
324
*
@@ -310,6 +375,9 @@ class TxGraphImpl final : public TxGraph
310
375
{
311
376
/* * Pointer to the corresponding Ref object if any, or nullptr if unlinked. */
312
377
Ref* m_ref{nullptr };
378
+ /* * Iterator to the corresponding ChunkData, if any, and m_main_chunkindex.end() otherwise.
379
+ * This is initialized on construction of the Entry, in AddTransaction. */
380
+ ChunkIndex::iterator m_main_chunkindex_iterator;
313
381
/* * Which Cluster and position therein this Entry appears in. ([0] = main, [1] = staged). */
314
382
Locator m_locator[MAX_LEVELS];
315
383
/* * The chunk feerate of this transaction in main (if present in m_locator[0]). */
@@ -324,43 +392,11 @@ class TxGraphImpl final : public TxGraph
324
392
/* * Set of Entries which have no linked Ref anymore. */
325
393
std::vector<GraphIndex> m_unlinked;
326
394
327
- /* * Compare two Cluster* by their m_sequence value (while supporting nullptr). */
328
- static std::strong_ordering CompareClusters (Cluster* a, Cluster* b) noexcept
329
- {
330
- // The nullptr pointer compares before everything else.
331
- if (a == nullptr || b == nullptr ) {
332
- return (a != nullptr ) <=> (b != nullptr );
333
- }
334
- // If neither pointer is nullptr, compare the Clusters' sequence numbers.
335
- Assume (a == b || a->m_sequence != b->m_sequence );
336
- return a->m_sequence <=> b->m_sequence ;
337
- }
338
-
339
- /* * Compare two entries (which must both exist within the main graph). */
340
- std::strong_ordering CompareMainTransactions (GraphIndex a, GraphIndex b) const noexcept
341
- {
342
- Assume (a < m_entries.size () && b < m_entries.size ());
343
- const auto & entry_a = m_entries[a];
344
- const auto & entry_b = m_entries[b];
345
- // Compare chunk feerates, and return result if it differs.
346
- auto feerate_cmp = FeeRateCompare (entry_b.m_main_chunk_feerate , entry_a.m_main_chunk_feerate );
347
- if (feerate_cmp < 0 ) return std::strong_ordering::less;
348
- if (feerate_cmp > 0 ) return std::strong_ordering::greater;
349
- // Compare Cluster m_sequence as tie-break for equal chunk feerates.
350
- const auto & locator_a = entry_a.m_locator [0 ];
351
- const auto & locator_b = entry_b.m_locator [0 ];
352
- Assume (locator_a.IsPresent () && locator_b.IsPresent ());
353
- if (locator_a.cluster != locator_b.cluster ) {
354
- return CompareClusters (locator_a.cluster , locator_b.cluster );
355
- }
356
- // As final tie-break, compare position within cluster linearization.
357
- return entry_a.m_main_lin_index <=> entry_b.m_main_lin_index ;
358
- }
359
-
360
395
public:
361
396
/* * Construct a new TxGraphImpl with the specified maximum cluster count. */
362
397
explicit TxGraphImpl (DepGraphIndex max_cluster_count) noexcept :
363
- m_max_cluster_count(max_cluster_count)
398
+ m_max_cluster_count(max_cluster_count),
399
+ m_main_chunkindex(ChunkOrder(this ))
364
400
{
365
401
Assume (max_cluster_count >= 1 );
366
402
Assume (max_cluster_count <= MAX_CLUSTER_COUNT_LIMIT);
@@ -544,13 +580,23 @@ void TxGraphImpl::ClearLocator(int level, GraphIndex idx) noexcept
544
580
--m_staging_clusterset->m_txcount ;
545
581
}
546
582
}
583
+ if (level == 0 && entry.m_main_chunkindex_iterator != m_main_chunkindex.end ()) {
584
+ m_main_chunkindex.erase (entry.m_main_chunkindex_iterator );
585
+ entry.m_main_chunkindex_iterator = m_main_chunkindex.end ();
586
+ }
547
587
}
548
588
549
589
void Cluster::Updated (TxGraphImpl& graph) noexcept
550
590
{
551
591
// Update all the Locators for this Cluster's Entry objects.
552
592
for (DepGraphIndex idx : m_linearization) {
553
593
auto & entry = graph.m_entries [m_mapping[idx]];
594
+ if (m_level == 0 && entry.m_main_chunkindex_iterator != graph.m_main_chunkindex .end ()) {
595
+ // Destroy any potential ChunkData prior to modifying the Cluster (as that could
596
+ // invalidate its ordering).
597
+ graph.m_main_chunkindex .erase (entry.m_main_chunkindex_iterator );
598
+ entry.m_main_chunkindex_iterator = graph.m_main_chunkindex .end ();
599
+ }
554
600
entry.m_locator [m_level].SetPresent (this , idx);
555
601
}
556
602
// If this is for the main graph (level = 0), and the Cluster's quality is ACCEPTABLE or
@@ -559,22 +605,30 @@ void Cluster::Updated(TxGraphImpl& graph) noexcept
559
605
// ACCEPTABLE, so it is pointless to compute these if we haven't reached that quality level
560
606
// yet.
561
607
if (m_level == 0 && IsAcceptable ()) {
562
- LinearizationChunking chunking (m_depgraph, m_linearization);
608
+ const LinearizationChunking chunking (m_depgraph, m_linearization);
563
609
LinearizationIndex lin_idx{0 };
564
610
// Iterate over the chunks.
565
611
for (unsigned chunk_idx = 0 ; chunk_idx < chunking.NumChunksLeft (); ++chunk_idx) {
566
612
auto chunk = chunking.GetChunk (chunk_idx);
567
- Assume (chunk.transactions .Any ());
613
+ const auto chunk_count = chunk.transactions .Count ();
614
+ Assume (chunk_count > 0 );
568
615
// Iterate over the transactions in the linearization, which must match those in chunk.
569
- do {
616
+ while ( true ) {
570
617
DepGraphIndex idx = m_linearization[lin_idx];
571
618
GraphIndex graph_idx = m_mapping[idx];
572
619
auto & entry = graph.m_entries [graph_idx];
573
620
entry.m_main_lin_index = lin_idx++;
574
621
entry.m_main_chunk_feerate = FeePerWeight::FromFeeFrac (chunk.feerate );
575
622
Assume (chunk.transactions [idx]);
576
623
chunk.transactions .Reset (idx);
577
- } while (chunk.transactions .Any ());
624
+ if (chunk.transactions .None ()) {
625
+ // Last transaction in the chunk.
626
+ auto [it, inserted] = graph.m_main_chunkindex .emplace (graph_idx, chunk_count);
627
+ Assume (inserted);
628
+ entry.m_main_chunkindex_iterator = it;
629
+ break ;
630
+ }
631
+ }
578
632
}
579
633
}
580
634
}
@@ -829,7 +883,14 @@ void Cluster::Merge(TxGraphImpl& graph, Cluster& other) noexcept
829
883
// Update the transaction's Locator. There is no need to call Updated() to update chunk
830
884
// feerates, as Updated() will be invoked by Cluster::ApplyDependencies on the resulting
831
885
// merged Cluster later anyway).
832
- graph.m_entries [idx].m_locator [m_level].SetPresent (this , new_pos);
886
+ auto & entry = graph.m_entries [idx];
887
+ if (m_level == 0 && entry.m_main_chunkindex_iterator != graph.m_main_chunkindex .end ()) {
888
+ // Destroy any potential ChunkData prior to modifying the Cluster (as that could
889
+ // invalidate its ordering).
890
+ graph.m_main_chunkindex .erase (entry.m_main_chunkindex_iterator );
891
+ entry.m_main_chunkindex_iterator = graph.m_main_chunkindex .end ();
892
+ }
893
+ entry.m_locator [m_level].SetPresent (this , new_pos);
833
894
}
834
895
// Purge the other Cluster, now that everything has been moved.
835
896
other.m_depgraph = DepGraph<SetType>{};
@@ -1043,6 +1104,10 @@ void TxGraphImpl::SwapIndexes(GraphIndex a, GraphIndex b) noexcept
1043
1104
Entry& entry = m_entries[idx];
1044
1105
// Update linked Ref, if any exists.
1045
1106
if (entry.m_ref ) GetRefIndex (*entry.m_ref ) = idx;
1107
+ // Update linked chunk index entries, if any exist.
1108
+ if (entry.m_main_chunkindex_iterator != m_main_chunkindex.end ()) {
1109
+ entry.m_main_chunkindex_iterator ->m_graph_index = idx;
1110
+ }
1046
1111
// Update the locators for both levels. The rest of the Entry information will not change,
1047
1112
// so no need to invoke Cluster::Updated().
1048
1113
for (int level = 0 ; level < MAX_LEVELS; ++level) {
@@ -1452,6 +1517,7 @@ TxGraph::Ref TxGraphImpl::AddTransaction(const FeePerWeight& feerate) noexcept
1452
1517
auto idx = m_entries.size ();
1453
1518
m_entries.emplace_back ();
1454
1519
auto & entry = m_entries.back ();
1520
+ entry.m_main_chunkindex_iterator = m_main_chunkindex.end ();
1455
1521
entry.m_ref = &ret;
1456
1522
GetRefGraph (ret) = this ;
1457
1523
GetRefIndex (ret) = idx;
@@ -1983,6 +2049,7 @@ void Cluster::SanityCheck(const TxGraphImpl& graph, int level) const
1983
2049
// Verify m_linearization.
1984
2050
SetType m_done;
1985
2051
LinearizationIndex linindex{0 };
2052
+ DepGraphIndex chunk_pos{0 }; // !< position within the current chunk
1986
2053
assert (m_depgraph.IsAcyclic ());
1987
2054
for (auto lin_pos : m_linearization) {
1988
2055
assert (lin_pos < m_mapping.size ());
@@ -1999,8 +2066,17 @@ void Cluster::SanityCheck(const TxGraphImpl& graph, int level) const
1999
2066
++linindex;
2000
2067
if (!linchunking.GetChunk (0 ).transactions [lin_pos]) {
2001
2068
linchunking.MarkDone (linchunking.GetChunk (0 ).transactions );
2069
+ chunk_pos = 0 ;
2002
2070
}
2003
2071
assert (entry.m_main_chunk_feerate == linchunking.GetChunk (0 ).feerate );
2072
+ // Verify that an entry in the chunk index exists for every chunk-ending transaction.
2073
+ ++chunk_pos;
2074
+ bool is_chunk_end = (chunk_pos == linchunking.GetChunk (0 ).transactions .Count ());
2075
+ assert ((entry.m_main_chunkindex_iterator != graph.m_main_chunkindex .end ()) == is_chunk_end);
2076
+ if (is_chunk_end) {
2077
+ auto & chunk_data = *entry.m_main_chunkindex_iterator ;
2078
+ assert (chunk_data.m_chunk_count == chunk_pos);
2079
+ }
2004
2080
// If this Cluster has an acceptable quality level, its chunks must be connected.
2005
2081
assert (m_depgraph.IsConnected (linchunking.GetChunk (0 ).transactions ));
2006
2082
}
@@ -2019,6 +2095,8 @@ void TxGraphImpl::SanityCheck() const
2019
2095
std::set<GraphIndex> expected_removed[MAX_LEVELS];
2020
2096
/* * Which Cluster::m_sequence values have been encountered. */
2021
2097
std::set<uint64_t > sequences;
2098
+ /* * Which GraphIndexes ought to occur in m_main_chunkindex, based on m_entries. */
2099
+ std::set<GraphIndex> expected_chunkindex;
2022
2100
/* * Whether compaction is possible in the current state. */
2023
2101
bool compact_possible{true };
2024
2102
@@ -2033,6 +2111,11 @@ void TxGraphImpl::SanityCheck() const
2033
2111
assert (GetRefGraph (*entry.m_ref ) == this );
2034
2112
assert (GetRefIndex (*entry.m_ref ) == idx);
2035
2113
}
2114
+ if (entry.m_main_chunkindex_iterator != m_main_chunkindex.end ()) {
2115
+ // Remember which entries we see a chunkindex entry for.
2116
+ assert (entry.m_locator [0 ].IsPresent ());
2117
+ expected_chunkindex.insert (idx);
2118
+ }
2036
2119
// Verify the Entry m_locators.
2037
2120
bool was_present{false }, was_removed{false };
2038
2121
for (int level = 0 ; level < MAX_LEVELS; ++level) {
@@ -2145,6 +2228,20 @@ void TxGraphImpl::SanityCheck() const
2145
2228
if (compact_possible) {
2146
2229
assert (actual_unlinked.empty ());
2147
2230
}
2231
+
2232
+ // Finally, check the chunk index.
2233
+ std::set<GraphIndex> actual_chunkindex;
2234
+ FeeFrac last_chunk_feerate;
2235
+ for (const auto & chunk : m_main_chunkindex) {
2236
+ GraphIndex idx = chunk.m_graph_index ;
2237
+ actual_chunkindex.insert (idx);
2238
+ auto chunk_feerate = m_entries[idx].m_main_chunk_feerate ;
2239
+ if (!last_chunk_feerate.IsEmpty ()) {
2240
+ assert (FeeRateCompare (last_chunk_feerate, chunk_feerate) >= 0 );
2241
+ }
2242
+ last_chunk_feerate = chunk_feerate;
2243
+ }
2244
+ assert (actual_chunkindex == expected_chunkindex);
2148
2245
}
2149
2246
2150
2247
void TxGraphImpl::DoWork () noexcept
0 commit comments