fix(chain): TxDescendants performs a BFS

This commit also changes test_descendants_no_repeat to check
the order of the transactions returned
This commit is contained in:
Daniela Brozzoni 2023-09-29 18:42:49 +02:00
parent 486e0e1437
commit 2f26eca607
No known key found for this signature in database
GPG Key ID: 7DE4F1FDCED0AB87
2 changed files with 15 additions and 20 deletions

View File

@ -1145,7 +1145,7 @@ impl<A> AsRef<TxGraph<A>> for TxGraph<A> {
pub struct TxDescendants<'g, A, F> { pub struct TxDescendants<'g, A, F> {
graph: &'g TxGraph<A>, graph: &'g TxGraph<A>,
visited: HashSet<Txid>, visited: HashSet<Txid>,
stack: Vec<(usize, Txid)>, queue: VecDeque<(usize, Txid)>,
filter_map: F, filter_map: F,
} }
@ -1156,7 +1156,7 @@ impl<'g, A, F> TxDescendants<'g, A, F> {
Self { Self {
graph, graph,
visited: Default::default(), visited: Default::default(),
stack: [(0, txid)].into(), queue: [(0, txid)].into(),
filter_map, filter_map,
} }
} }
@ -1166,10 +1166,10 @@ impl<'g, A, F> TxDescendants<'g, A, F> {
let mut descendants = Self { let mut descendants = Self {
graph, graph,
visited: Default::default(), visited: Default::default(),
stack: Default::default(), queue: Default::default(),
filter_map, filter_map,
}; };
descendants.populate_stack(1, txid); descendants.populate_queue(1, txid);
descendants descendants
} }
@ -1186,7 +1186,7 @@ impl<'g, A, F> TxDescendants<'g, A, F> {
Self { Self {
graph, graph,
visited: Default::default(), visited: Default::default(),
stack: txids.into_iter().map(|txid| (0, txid)).collect(), queue: txids.into_iter().map(|txid| (0, txid)).collect(),
filter_map, filter_map,
} }
} }
@ -1205,25 +1205,25 @@ impl<'g, A, F> TxDescendants<'g, A, F> {
let mut descendants = Self { let mut descendants = Self {
graph, graph,
visited: Default::default(), visited: Default::default(),
stack: Default::default(), queue: Default::default(),
filter_map, filter_map,
}; };
for txid in txids { for txid in txids {
descendants.populate_stack(1, txid); descendants.populate_queue(1, txid);
} }
descendants descendants
} }
} }
impl<'g, A, F> TxDescendants<'g, A, F> { impl<'g, A, F> TxDescendants<'g, A, F> {
fn populate_stack(&mut self, depth: usize, txid: Txid) { fn populate_queue(&mut self, depth: usize, txid: Txid) {
let spend_paths = self let spend_paths = self
.graph .graph
.spends .spends
.range(tx_outpoint_range(txid)) .range(tx_outpoint_range(txid))
.flat_map(|(_, spends)| spends) .flat_map(|(_, spends)| spends)
.map(|&txid| (depth, txid)); .map(|&txid| (depth, txid));
self.stack.extend(spend_paths); self.queue.extend(spend_paths);
} }
} }
@ -1235,8 +1235,8 @@ where
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let (op_spends, txid, item) = loop { let (op_spends, txid, item) = loop {
// we have exhausted all paths when stack is empty // we have exhausted all paths when queue is empty
let (op_spends, txid) = self.stack.pop()?; let (op_spends, txid) = self.queue.pop_front()?;
// we do not want to visit the same transaction twice // we do not want to visit the same transaction twice
if self.visited.insert(txid) { if self.visited.insert(txid) {
// ignore paths when user filters them out // ignore paths when user filters them out
@ -1246,7 +1246,7 @@ where
} }
}; };
self.populate_stack(op_spends + 1, txid); self.populate_queue(op_spends + 1, txid);
Some(item) Some(item)
} }
} }

View File

@ -610,7 +610,7 @@ fn test_descendants_no_repeat() {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut graph = TxGraph::<()>::default(); let mut graph = TxGraph::<()>::default();
let mut expected_txids = BTreeSet::new(); let mut expected_txids = Vec::new();
// these are NOT descendants of `tx_a` // these are NOT descendants of `tx_a`
for tx in txs_not_connected { for tx in txs_not_connected {
@ -625,19 +625,14 @@ fn test_descendants_no_repeat() {
.chain(core::iter::once(&tx_e)) .chain(core::iter::once(&tx_e))
{ {
let _ = graph.insert_tx(tx.clone()); let _ = graph.insert_tx(tx.clone());
assert!(expected_txids.insert(tx.txid())); expected_txids.push(tx.txid());
} }
let descendants = graph let descendants = graph
.walk_descendants(tx_a.txid(), |_, txid| Some(txid)) .walk_descendants(tx_a.txid(), |_, txid| Some(txid))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
assert_eq!(descendants.len(), expected_txids.len()); assert_eq!(descendants, expected_txids);
for txid in descendants {
assert!(expected_txids.remove(&txid));
}
assert!(expected_txids.is_empty());
} }
#[test] #[test]