1
1
use crate :: collections:: { HashMap , HashSet , VecDeque } ;
2
- use crate :: tx_graph:: { TxAncestors , TxDescendants } ;
2
+ use crate :: tx_graph:: { TxAncestors , TxDescendants , TxNode } ;
3
3
use crate :: { Anchor , ChainOracle , TxGraph } ;
4
4
use alloc:: boxed:: Box ;
5
5
use alloc:: collections:: BTreeSet ;
@@ -36,6 +36,9 @@ pub struct CanonicalIter<'g, A, C> {
36
36
canonical : CanonicalMap < A > ,
37
37
not_canonical : NotCanonicalSet ,
38
38
39
+ canonical_ancestors : HashMap < Txid , Vec < Txid > > ,
40
+ canonical_roots : VecDeque < Txid > ,
41
+
39
42
queue : VecDeque < Txid > ,
40
43
}
41
44
@@ -75,6 +78,8 @@ impl<'g, A: Anchor, C: ChainOracle> CanonicalIter<'g, A, C> {
75
78
unprocessed_leftover_txs : VecDeque :: new ( ) ,
76
79
canonical : HashMap :: new ( ) ,
77
80
not_canonical : HashSet :: new ( ) ,
81
+ canonical_ancestors : HashMap :: new ( ) ,
82
+ canonical_roots : VecDeque :: new ( ) ,
78
83
queue : VecDeque :: new ( ) ,
79
84
}
80
85
}
@@ -160,7 +165,7 @@ impl<'g, A: Anchor, C: ChainOracle> CanonicalIter<'g, A, C> {
160
165
161
166
// Any conflicts with a canonical tx can be added to `not_canonical`. Descendants
162
167
// of `not_canonical` txs can also be added to `not_canonical`.
163
- for ( _, conflict_txid) in self . tx_graph . direct_conflicts ( & tx) {
168
+ for ( _, conflict_txid) in self . tx_graph . direct_conflicts ( & tx. clone ( ) ) {
164
169
TxDescendants :: new_include_root (
165
170
self . tx_graph ,
166
171
conflict_txid,
@@ -181,6 +186,18 @@ impl<'g, A: Anchor, C: ChainOracle> CanonicalIter<'g, A, C> {
181
186
detected_self_double_spend = true ;
182
187
return None ;
183
188
}
189
+
190
+ // Calculates all the existing ancestors for the given Txid
191
+ self . canonical_ancestors . insert (
192
+ this_txid,
193
+ tx. clone ( )
194
+ . input
195
+ . iter ( )
196
+ . filter ( |txin| self . tx_graph . get_tx ( txin. previous_output . txid ) . is_some ( ) )
197
+ . map ( |txin| txin. previous_output . txid )
198
+ . collect ( ) ,
199
+ ) ;
200
+
184
201
canonical_entry. insert ( ( tx, this_reason) ) ;
185
202
Some ( this_txid)
186
203
} ,
@@ -190,12 +207,29 @@ impl<'g, A: Anchor, C: ChainOracle> CanonicalIter<'g, A, C> {
190
207
if detected_self_double_spend {
191
208
for txid in staged_queue {
192
209
self . canonical . remove ( & txid) ;
210
+ self . canonical_ancestors . remove ( & txid) ;
193
211
}
194
212
for txid in undo_not_canonical {
195
213
self . not_canonical . remove ( & txid) ;
196
214
}
197
215
} else {
198
- self . queue . extend ( staged_queue) ;
216
+ // TODO: (@oleonardolima) Can this be optimized somehow ?
217
+ // Can we just do a simple lookup on the `canonical_ancestors` field ?
218
+ for txid in staged_queue {
219
+ let tx = self . tx_graph . get_tx ( txid) . expect ( "tx must exist" ) ;
220
+ let ancestors = tx
221
+ . input
222
+ . iter ( )
223
+ . map ( |txin| txin. previous_output . txid )
224
+ . filter_map ( |prev_txid| self . tx_graph . get_tx ( prev_txid) )
225
+ . collect :: < Vec < _ > > ( ) ;
226
+
227
+ // check if it's a root: it's either a coinbase transaction or has not known
228
+ // ancestors in the tx_graph
229
+ if tx. is_coinbase ( ) || ancestors. is_empty ( ) {
230
+ self . canonical_roots . push_back ( txid) ;
231
+ }
232
+ }
199
233
}
200
234
}
201
235
}
@@ -205,31 +239,21 @@ impl<A: Anchor, C: ChainOracle> Iterator for CanonicalIter<'_, A, C> {
205
239
206
240
fn next ( & mut self ) -> Option < Self :: Item > {
207
241
loop {
208
- if let Some ( txid) = self . queue . pop_front ( ) {
209
- let ( tx, reason) = self
210
- . canonical
211
- . get ( & txid)
212
- . cloned ( )
213
- . expect ( "reason must exist" ) ;
214
- return Some ( Ok ( ( txid, tx, reason) ) ) ;
215
- }
216
-
217
- if let Some ( ( txid, tx) ) = self . unprocessed_assumed_txs . next ( ) {
242
+ while let Some ( ( txid, tx) ) = self . unprocessed_assumed_txs . next ( ) {
218
243
if !self . is_canonicalized ( txid) {
219
244
self . mark_canonical ( txid, tx, CanonicalReason :: assumed ( ) ) ;
220
245
}
221
246
}
222
247
223
- if let Some ( ( txid, tx, anchors) ) = self . unprocessed_anchored_txs . next ( ) {
248
+ while let Some ( ( txid, tx, anchors) ) = self . unprocessed_anchored_txs . next ( ) {
224
249
if !self . is_canonicalized ( txid) {
225
250
if let Err ( err) = self . scan_anchors ( txid, tx, anchors) {
226
251
return Some ( Err ( err) ) ;
227
252
}
228
253
}
229
- continue ;
230
254
}
231
255
232
- if let Some ( ( txid, tx, last_seen) ) = self . unprocessed_seen_txs . next ( ) {
256
+ while let Some ( ( txid, tx, last_seen) ) = self . unprocessed_seen_txs . next ( ) {
233
257
debug_assert ! (
234
258
!tx. is_coinbase( ) ,
235
259
"Coinbase txs must not have `last_seen` (in mempool) value"
@@ -238,15 +262,34 @@ impl<A: Anchor, C: ChainOracle> Iterator for CanonicalIter<'_, A, C> {
238
262
let observed_in = ObservedIn :: Mempool ( last_seen) ;
239
263
self . mark_canonical ( txid, tx, CanonicalReason :: from_observed_in ( observed_in) ) ;
240
264
}
241
- continue ;
242
265
}
243
266
244
- if let Some ( ( txid, tx, height) ) = self . unprocessed_leftover_txs . pop_front ( ) {
267
+ while let Some ( ( txid, tx, height) ) = self . unprocessed_leftover_txs . pop_front ( ) {
245
268
if !self . is_canonicalized ( txid) && !tx. is_coinbase ( ) {
246
269
let observed_in = ObservedIn :: Block ( height) ;
247
270
self . mark_canonical ( txid, tx, CanonicalReason :: from_observed_in ( observed_in) ) ;
248
271
}
249
- continue ;
272
+ }
273
+
274
+ if !self . canonical_roots . is_empty ( ) {
275
+ let topological_iter = TopologicalIteratorWithLevels :: new (
276
+ self . tx_graph ,
277
+ self . chain ,
278
+ self . chain_tip ,
279
+ & self . canonical_ancestors ,
280
+ self . canonical_roots . drain ( ..) . collect ( ) ,
281
+ ) ;
282
+
283
+ self . queue . extend ( topological_iter) ;
284
+ }
285
+
286
+ if let Some ( txid) = self . queue . pop_front ( ) {
287
+ let ( tx, reason) = self
288
+ . canonical
289
+ . get ( & txid)
290
+ . cloned ( )
291
+ . expect ( "canonical reason must exist" ) ;
292
+ return Some ( Ok ( ( txid, tx, reason) ) ) ;
250
293
}
251
294
252
295
return None ;
@@ -342,3 +385,129 @@ impl<A: Clone> CanonicalReason<A> {
342
385
}
343
386
}
344
387
}
388
+
389
+ struct TopologicalIteratorWithLevels < ' a , A , C > {
390
+ tx_graph : & ' a TxGraph < A > ,
391
+ chain : & ' a C ,
392
+ chain_tip : BlockId ,
393
+
394
+ current_level : Vec < Txid > ,
395
+ next_level : Vec < Txid > ,
396
+
397
+ adj_list : HashMap < Txid , Vec < Txid > > ,
398
+ parent_count : HashMap < Txid , usize > ,
399
+
400
+ current_index : usize ,
401
+ }
402
+
403
+ impl < ' a , A : Anchor , C : ChainOracle > TopologicalIteratorWithLevels < ' a , A , C > {
404
+ fn new (
405
+ tx_graph : & ' a TxGraph < A > ,
406
+ chain : & ' a C ,
407
+ chain_tip : BlockId ,
408
+ ancestors_by_txid : & HashMap < Txid , Vec < Txid > > ,
409
+ roots : Vec < Txid > ,
410
+ ) -> Self {
411
+ let mut parent_count = HashMap :: new ( ) ;
412
+ let mut adj_list: HashMap < Txid , Vec < Txid > > = HashMap :: new ( ) ;
413
+
414
+ for ( txid, ancestors) in ancestors_by_txid {
415
+ for ancestor in ancestors {
416
+ adj_list. entry ( * ancestor) . or_default ( ) . push ( * txid) ;
417
+ * parent_count. entry ( * txid) . or_insert ( 0 ) += 1 ;
418
+ }
419
+ }
420
+
421
+ let mut current_level: Vec < Txid > = roots. to_vec ( ) ;
422
+
423
+ // Sort the initial level by confirmation height
424
+ current_level. sort_by_key ( |& txid| {
425
+ let tx_node = tx_graph. get_tx_node ( txid) . expect ( "tx should exist" ) ;
426
+ Self :: find_direct_anchor ( & tx_node, chain, chain_tip)
427
+ . expect ( "should not fail" )
428
+ . map ( |anchor| anchor. confirmation_height_upper_bound ( ) )
429
+ . unwrap_or ( u32:: MAX )
430
+ } ) ;
431
+
432
+ Self {
433
+ current_level,
434
+ next_level : Vec :: new ( ) ,
435
+ adj_list,
436
+ parent_count,
437
+ current_index : 0 ,
438
+ tx_graph,
439
+ chain,
440
+ chain_tip,
441
+ }
442
+ }
443
+
444
+ fn find_direct_anchor (
445
+ tx_node : & TxNode < ' _ , Arc < Transaction > , A > ,
446
+ chain : & C ,
447
+ chain_tip : BlockId ,
448
+ ) -> Result < Option < A > , C :: Error > {
449
+ tx_node
450
+ . anchors
451
+ . iter ( )
452
+ . find_map ( |a| -> Option < Result < A , C :: Error > > {
453
+ match chain. is_block_in_chain ( a. anchor_block ( ) , chain_tip) {
454
+ Ok ( Some ( true ) ) => Some ( Ok ( a. clone ( ) ) ) ,
455
+ Ok ( Some ( false ) ) | Ok ( None ) => None ,
456
+ Err ( err) => Some ( Err ( err) ) ,
457
+ }
458
+ } )
459
+ . transpose ( )
460
+ }
461
+
462
+ fn advance_to_next_level ( & mut self ) {
463
+ self . current_level = core:: mem:: take ( & mut self . next_level ) ;
464
+
465
+ // Sort by confirmation height
466
+ self . current_level . sort_by_key ( |& txid| {
467
+ let tx_node = self . tx_graph . get_tx_node ( txid) . expect ( "tx should exist" ) ;
468
+
469
+ Self :: find_direct_anchor ( & tx_node, self . chain , self . chain_tip )
470
+ . expect ( "should not fail" )
471
+ . map ( |anchor| anchor. confirmation_height_upper_bound ( ) )
472
+ . unwrap_or ( u32:: MAX )
473
+ } ) ;
474
+
475
+ self . current_index = 0 ;
476
+ }
477
+ }
478
+
479
+ impl < ' a , A : Anchor , C : ChainOracle > Iterator for TopologicalIteratorWithLevels < ' a , A , C > {
480
+ type Item = Txid ;
481
+
482
+ fn next ( & mut self ) -> Option < Self :: Item > {
483
+ // If we've exhausted the current level, move to next
484
+ if self . current_index >= self . current_level . len ( ) {
485
+ if self . next_level . is_empty ( ) {
486
+ return None ;
487
+ }
488
+ self . advance_to_next_level ( ) ;
489
+ }
490
+
491
+ let current = self . current_level [ self . current_index ] ;
492
+ self . current_index += 1 ;
493
+
494
+ // If this is the last item in current level, prepare dependents for next level
495
+ if self . current_index == self . current_level . len ( ) {
496
+ // Process all dependents of all transactions in current level
497
+ for & tx in & self . current_level {
498
+ if let Some ( dependents) = self . adj_list . get ( & tx) {
499
+ for & dependent in dependents {
500
+ if let Some ( degree) = self . parent_count . get_mut ( & dependent) {
501
+ * degree -= 1 ;
502
+ if * degree == 0 {
503
+ self . next_level . push ( dependent) ;
504
+ }
505
+ }
506
+ }
507
+ }
508
+ }
509
+ }
510
+
511
+ Some ( current)
512
+ }
513
+ }
0 commit comments