@@ -54,6 +54,7 @@ use crate::{
5454 collections:: * , keychain:: Balance , local_chain:: LocalChain , Anchor , Append , BlockId ,
5555 ChainOracle , ChainPosition , FullTxOut ,
5656} ;
57+ use alloc:: collections:: vec_deque:: VecDeque ;
5758use alloc:: vec:: Vec ;
5859use bitcoin:: { OutPoint , Script , Transaction , TxOut , Txid } ;
5960use core:: {
@@ -319,15 +320,39 @@ impl<A> TxGraph<A> {
319320 . map ( |( outpoint, spends) | ( outpoint. vout , spends) )
320321 }
321322
323+ /// Creates an iterator that filters and maps ancestor transactions.
324+ ///
325+ /// The iterator starts with the ancestors of the supplied `tx` (ancestor transactions of `tx`
326+ /// are transactions spent by `tx`). The supplied transaction is excluded from the iterator.
327+ ///
328+ /// The supplied closure takes in two inputs `(depth, ancestor_tx)`:
329+ ///
330+ /// * `depth` is the distance between the starting `Transaction` and the `ancestor_tx`. I.e., if
331+ /// the `Transaction` is spending an output of the `ancestor_tx` then `depth` will be 1.
332+ /// * `ancestor_tx` is the `Transaction`'s ancestor which we are considering to walk.
333+ ///
334+ /// The supplied closure returns an `Option<T>`, allowing the caller to map each `Transaction`
335+ /// it visits and decide whether to visit ancestors.
336+ pub fn walk_ancestors < ' g , F , O > (
337+ & ' g self ,
338+ tx : & ' g Transaction ,
339+ walk_map : F ,
340+ ) -> TxAncestors < ' g , A , F >
341+ where
342+ F : FnMut ( usize , & ' g Transaction ) -> Option < O > + ' g ,
343+ {
344+ TxAncestors :: new_exclude_root ( self , tx, walk_map)
345+ }
346+
322347 /// Creates an iterator that filters and maps descendants from the starting `txid`.
323348 ///
324349 /// The supplied closure takes in two inputs `(depth, descendant_txid)`:
325350 ///
326351 /// * `depth` is the distance between the starting `txid` and the `descendant_txid`. I.e., if the
327- /// descendant is spending an output of the starting `txid`; the `depth` will be 1.
352+ /// descendant is spending an output of the starting `txid` then `depth` will be 1.
328353 /// * `descendant_txid` is the descendant's txid which we are considering to walk.
329354 ///
330- /// The supplied closure returns an `Option<T>`, allowing the caller to map each node it vists
355+ /// The supplied closure returns an `Option<T>`, allowing the caller to map each node it visits
331356 /// and decide whether to visit descendants.
332357 pub fn walk_descendants < ' g , F , O > ( & ' g self , txid : Txid , walk_map : F ) -> TxDescendants < A , F >
333358 where
@@ -356,8 +381,9 @@ impl<A> TxGraph<A> {
356381 /// transaction's inputs (spends). The conflicting txids are returned with the given
357382 /// transaction's vin (in which it conflicts).
358383 ///
359- /// Note that this only returns directly conflicting txids and does not include descendants of
360- /// those txids (which are technically also conflicting).
384+ /// Note that this only returns directly conflicting txids and won't include:
385+ /// - descendants of conflicting transactions (which are technically also conflicting)
386+ /// - transactions conflicting with the given transaction's ancestors
361387 pub fn direct_conflicts_of_tx < ' g > (
362388 & ' g self ,
363389 tx : & ' g Transaction ,
@@ -671,8 +697,9 @@ impl<A: Anchor> TxGraph<A> {
671697 }
672698 }
673699
674- // The tx is not anchored to a block which is in the best chain, let's check whether we can
675- // ignore it by checking conflicts!
700+ // The tx is not anchored to a block which is in the best chain, which means that it
701+ // might be in mempool, or it might have been dropped already.
702+ // Let's check conflicts to find out!
676703 let tx = match tx_node {
677704 TxNodeInternal :: Whole ( tx) => tx,
678705 TxNodeInternal :: Partial ( _) => {
@@ -681,18 +708,71 @@ impl<A: Anchor> TxGraph<A> {
681708 }
682709 } ;
683710
684- // If a conflicting tx is in the best chain, or has `last_seen` higher than this tx, then
685- // this tx cannot exist in the best chain
686- for conflicting_tx in self . walk_conflicts ( tx, |_, txid| self . get_tx_node ( txid) ) {
687- for block in conflicting_tx. anchors . iter ( ) . map ( A :: anchor_block) {
688- if chain. is_block_in_chain ( block, chain_tip) ? == Some ( true ) {
689- // conflicting tx is in best chain, so the current tx cannot be in best chain!
711+ // We want to retrieve all the transactions that conflict with us, plus all the
712+ // transactions that conflict with our unconfirmed ancestors, since they conflict with us
713+ // as well.
714+ // We only traverse unconfirmed ancestors since conflicts of confirmed transactions
715+ // cannot be in the best chain.
716+
717+ // First of all, we retrieve all our ancestors. Since we're using `new_include_root`, the
718+ // resulting array will also include `tx`
719+ let unconfirmed_ancestor_txs =
720+ TxAncestors :: new_include_root ( self , tx, |_, ancestor_tx : & Transaction | {
721+ let tx_node = self . get_tx_node ( ancestor_tx. txid ( ) ) ?;
722+ // We're filtering the ancestors to keep only the unconfirmed ones (= no anchors in
723+ // the best chain)
724+ for block in tx_node. anchors {
725+ match chain. is_block_in_chain ( block. anchor_block ( ) , chain_tip) {
726+ Ok ( Some ( true ) ) => return None ,
727+ Err ( e) => return Some ( Err ( e) ) ,
728+ _ => continue ,
729+ }
730+ }
731+ Some ( Ok ( tx_node) )
732+ } )
733+ . collect :: < Result < Vec < _ > , C :: Error > > ( ) ?;
734+
735+ // We determine our tx's last seen, which is the max between our last seen,
736+ // and our unconf descendants' last seen.
737+ let unconfirmed_descendants_txs =
738+ TxDescendants :: new_include_root ( self , tx. txid ( ) , |_, descendant_txid : Txid | {
739+ let tx_node = self . get_tx_node ( descendant_txid) ?;
740+ // We're filtering the ancestors to keep only the unconfirmed ones (= no anchors in
741+ // the best chain)
742+ for block in tx_node. anchors {
743+ match chain. is_block_in_chain ( block. anchor_block ( ) , chain_tip) {
744+ Ok ( Some ( true ) ) => return None ,
745+ Err ( e) => return Some ( Err ( e) ) ,
746+ _ => continue ,
747+ }
748+ }
749+ Some ( Ok ( tx_node) )
750+ } )
751+ . collect :: < Result < Vec < _ > , C :: Error > > ( ) ?;
752+
753+ let tx_last_seen = unconfirmed_descendants_txs
754+ . iter ( )
755+ . max_by_key ( |tx| tx. last_seen_unconfirmed )
756+ . map ( |tx| tx. last_seen_unconfirmed )
757+ . expect ( "descendants always includes at least one transaction (the root tx" ) ;
758+
759+ // Now we traverse our ancestors and consider all their conflicts
760+ for tx_node in unconfirmed_ancestor_txs {
761+ // We retrieve all the transactions conflicting with this specific ancestor
762+ let conflicting_txs = self . walk_conflicts ( tx_node. tx , |_, txid| self . get_tx_node ( txid) ) ;
763+
764+ // If a conflicting tx is in the best chain, or has `last_seen` higher than this ancestor, then
765+ // this tx cannot exist in the best chain
766+ for conflicting_tx in conflicting_txs {
767+ for block in conflicting_tx. anchors {
768+ if chain. is_block_in_chain ( block. anchor_block ( ) , chain_tip) ? == Some ( true ) {
769+ return Ok ( None ) ;
770+ }
771+ }
772+ if conflicting_tx. last_seen_unconfirmed > tx_last_seen {
690773 return Ok ( None ) ;
691774 }
692775 }
693- if conflicting_tx. last_seen_unconfirmed > * last_seen {
694- return Ok ( None ) ;
695- }
696776 }
697777
698778 Ok ( Some ( ChainPosition :: Unconfirmed ( * last_seen) ) )
@@ -1137,6 +1217,126 @@ impl<A> AsRef<TxGraph<A>> for TxGraph<A> {
11371217 }
11381218}
11391219
1220+ /// An iterator that traverses ancestors of a given root transaction.
1221+ ///
1222+ /// The iterator excludes partial transactions.
1223+ ///
1224+ /// This `struct` is created by the [`walk_ancestors`] method of [`TxGraph`].
1225+ ///
1226+ /// [`walk_ancestors`]: TxGraph::walk_ancestors
1227+ pub struct TxAncestors < ' g , A , F > {
1228+ graph : & ' g TxGraph < A > ,
1229+ visited : HashSet < Txid > ,
1230+ queue : VecDeque < ( usize , & ' g Transaction ) > ,
1231+ filter_map : F ,
1232+ }
1233+
1234+ impl < ' g , A , F > TxAncestors < ' g , A , F > {
1235+ /// Creates a `TxAncestors` that includes the starting `Transaction` when iterating.
1236+ pub ( crate ) fn new_include_root (
1237+ graph : & ' g TxGraph < A > ,
1238+ tx : & ' g Transaction ,
1239+ filter_map : F ,
1240+ ) -> Self {
1241+ Self {
1242+ graph,
1243+ visited : Default :: default ( ) ,
1244+ queue : [ ( 0 , tx) ] . into ( ) ,
1245+ filter_map,
1246+ }
1247+ }
1248+
1249+ /// Creates a `TxAncestors` that excludes the starting `Transaction` when iterating.
1250+ pub ( crate ) fn new_exclude_root (
1251+ graph : & ' g TxGraph < A > ,
1252+ tx : & ' g Transaction ,
1253+ filter_map : F ,
1254+ ) -> Self {
1255+ let mut ancestors = Self {
1256+ graph,
1257+ visited : Default :: default ( ) ,
1258+ queue : Default :: default ( ) ,
1259+ filter_map,
1260+ } ;
1261+ ancestors. populate_queue ( 1 , tx) ;
1262+ ancestors
1263+ }
1264+
1265+ /// Creates a `TxAncestors` from multiple starting `Transaction`s that includes the starting
1266+ /// `Transaction`s when iterating.
1267+ #[ allow( unused) ]
1268+ pub ( crate ) fn from_multiple_include_root < I > (
1269+ graph : & ' g TxGraph < A > ,
1270+ txs : I ,
1271+ filter_map : F ,
1272+ ) -> Self
1273+ where
1274+ I : IntoIterator < Item = & ' g Transaction > ,
1275+ {
1276+ Self {
1277+ graph,
1278+ visited : Default :: default ( ) ,
1279+ queue : txs. into_iter ( ) . map ( |tx| ( 0 , tx) ) . collect ( ) ,
1280+ filter_map,
1281+ }
1282+ }
1283+
1284+ /// Creates a `TxAncestors` from multiple starting `Transaction`s that excludes the starting
1285+ /// `Transaction`s when iterating.
1286+ #[ allow( unused) ]
1287+ pub ( crate ) fn from_multiple_exclude_root < I > (
1288+ graph : & ' g TxGraph < A > ,
1289+ txs : I ,
1290+ filter_map : F ,
1291+ ) -> Self
1292+ where
1293+ I : IntoIterator < Item = & ' g Transaction > ,
1294+ {
1295+ let mut ancestors = Self {
1296+ graph,
1297+ visited : Default :: default ( ) ,
1298+ queue : Default :: default ( ) ,
1299+ filter_map,
1300+ } ;
1301+ for tx in txs {
1302+ ancestors. populate_queue ( 1 , tx) ;
1303+ }
1304+ ancestors
1305+ }
1306+
1307+ fn populate_queue ( & mut self , depth : usize , tx : & ' g Transaction ) {
1308+ let ancestors = tx
1309+ . input
1310+ . iter ( )
1311+ . map ( |txin| txin. previous_output . txid )
1312+ . filter ( |& prev_txid| self . visited . insert ( prev_txid) )
1313+ . filter_map ( |prev_txid| self . graph . get_tx ( prev_txid) )
1314+ . map ( |tx| ( depth, tx) ) ;
1315+ self . queue . extend ( ancestors) ;
1316+ }
1317+ }
1318+
1319+ impl < ' g , A , F , O > Iterator for TxAncestors < ' g , A , F >
1320+ where
1321+ F : FnMut ( usize , & ' g Transaction ) -> Option < O > ,
1322+ {
1323+ type Item = O ;
1324+
1325+ fn next ( & mut self ) -> Option < Self :: Item > {
1326+ loop {
1327+ // we have exhausted all paths when queue is empty
1328+ let ( ancestor_depth, tx) = self . queue . pop_front ( ) ?;
1329+ // ignore paths when user filters them out
1330+ let item = match ( self . filter_map ) ( ancestor_depth, tx) {
1331+ Some ( item) => item,
1332+ None => continue ,
1333+ } ;
1334+ self . populate_queue ( ancestor_depth + 1 , tx) ;
1335+ return Some ( item) ;
1336+ }
1337+ }
1338+ }
1339+
11401340/// An iterator that traverses transaction descendants.
11411341///
11421342/// This `struct` is created by the [`walk_descendants`] method of [`TxGraph`].
@@ -1145,7 +1345,7 @@ impl<A> AsRef<TxGraph<A>> for TxGraph<A> {
11451345pub struct TxDescendants < ' g , A , F > {
11461346 graph : & ' g TxGraph < A > ,
11471347 visited : HashSet < Txid > ,
1148- stack : Vec < ( usize , Txid ) > ,
1348+ queue : VecDeque < ( usize , Txid ) > ,
11491349 filter_map : F ,
11501350}
11511351
@@ -1156,7 +1356,7 @@ impl<'g, A, F> TxDescendants<'g, A, F> {
11561356 Self {
11571357 graph,
11581358 visited : Default :: default ( ) ,
1159- stack : [ ( 0 , txid) ] . into ( ) ,
1359+ queue : [ ( 0 , txid) ] . into ( ) ,
11601360 filter_map,
11611361 }
11621362 }
@@ -1166,14 +1366,14 @@ impl<'g, A, F> TxDescendants<'g, A, F> {
11661366 let mut descendants = Self {
11671367 graph,
11681368 visited : Default :: default ( ) ,
1169- stack : Default :: default ( ) ,
1369+ queue : Default :: default ( ) ,
11701370 filter_map,
11711371 } ;
1172- descendants. populate_stack ( 1 , txid) ;
1372+ descendants. populate_queue ( 1 , txid) ;
11731373 descendants
11741374 }
11751375
1176- /// Creates a `TxDescendants` from multiple starting transactions that include the starting
1376+ /// Creates a `TxDescendants` from multiple starting transactions that includes the starting
11771377 /// `txid`s when iterating.
11781378 pub ( crate ) fn from_multiple_include_root < I > (
11791379 graph : & ' g TxGraph < A > ,
@@ -1186,7 +1386,7 @@ impl<'g, A, F> TxDescendants<'g, A, F> {
11861386 Self {
11871387 graph,
11881388 visited : Default :: default ( ) ,
1189- stack : txids. into_iter ( ) . map ( |txid| ( 0 , txid) ) . collect ( ) ,
1389+ queue : txids. into_iter ( ) . map ( |txid| ( 0 , txid) ) . collect ( ) ,
11901390 filter_map,
11911391 }
11921392 }
@@ -1205,25 +1405,25 @@ impl<'g, A, F> TxDescendants<'g, A, F> {
12051405 let mut descendants = Self {
12061406 graph,
12071407 visited : Default :: default ( ) ,
1208- stack : Default :: default ( ) ,
1408+ queue : Default :: default ( ) ,
12091409 filter_map,
12101410 } ;
12111411 for txid in txids {
1212- descendants. populate_stack ( 1 , txid) ;
1412+ descendants. populate_queue ( 1 , txid) ;
12131413 }
12141414 descendants
12151415 }
12161416}
12171417
12181418impl < ' g , A , F > TxDescendants < ' g , A , F > {
1219- fn populate_stack ( & mut self , depth : usize , txid : Txid ) {
1419+ fn populate_queue ( & mut self , depth : usize , txid : Txid ) {
12201420 let spend_paths = self
12211421 . graph
12221422 . spends
12231423 . range ( tx_outpoint_range ( txid) )
12241424 . flat_map ( |( _, spends) | spends)
12251425 . map ( |& txid| ( depth, txid) ) ;
1226- self . stack . extend ( spend_paths) ;
1426+ self . queue . extend ( spend_paths) ;
12271427 }
12281428}
12291429
@@ -1235,8 +1435,8 @@ where
12351435
12361436 fn next ( & mut self ) -> Option < Self :: Item > {
12371437 let ( op_spends, txid, item) = loop {
1238- // we have exhausted all paths when stack is empty
1239- let ( op_spends, txid) = self . stack . pop ( ) ?;
1438+ // we have exhausted all paths when queue is empty
1439+ let ( op_spends, txid) = self . queue . pop_front ( ) ?;
12401440 // we do not want to visit the same transaction twice
12411441 if self . visited . insert ( txid) {
12421442 // ignore paths when user filters them out
@@ -1246,7 +1446,7 @@ where
12461446 }
12471447 } ;
12481448
1249- self . populate_stack ( op_spends + 1 , txid) ;
1449+ self . populate_queue ( op_spends + 1 , txid) ;
12501450 Some ( item)
12511451 }
12521452}
0 commit comments