Skip to content

Commit 48ca95b

Browse files
test(chain): Add test for walk_ancestors
Co-authored-by: Wei Chen <[email protected]>
1 parent 59a2403 commit 48ca95b

File tree

1 file changed

+203
-1
lines changed

1 file changed

+203
-1
lines changed

crates/chain/tests/test_tx_graph.rs

Lines changed: 203 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use bdk_chain::{
55
collections::*,
66
local_chain::LocalChain,
77
tx_graph::{ChangeSet, TxGraph},
8-
Anchor, Append, BlockId, ChainPosition, ConfirmationHeightAnchor,
8+
Anchor, Append, BlockId, ChainOracle, ChainPosition, ConfirmationHeightAnchor,
99
};
1010
use bitcoin::{
1111
absolute, hashes::Hash, BlockHash, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Txid,
@@ -496,6 +496,208 @@ fn test_calculate_fee_on_coinbase() {
496496
assert_eq!(graph.calculate_fee(&tx), Ok(0));
497497
}
498498

499+
// `test_walk_ancestors` uses the following transaction structure:
500+
//
501+
// a0
502+
// / \
503+
// b0 b1 b2
504+
// / \ \ /
505+
// c0 c1 c2 c3
506+
// / \ /
507+
// d0 d1
508+
// \
509+
// e0
510+
//
511+
// where b0 and b1 spend a0, c0 and c1 spend b0, d0 spends c1, etc.
512+
#[test]
513+
fn test_walk_ancestors() {
514+
let local_chain: LocalChain = (0..=20)
515+
.map(|ht| (ht, BlockHash::hash(format!("Block Hash {}", ht).as_bytes())))
516+
.collect::<BTreeMap<u32, BlockHash>>()
517+
.into();
518+
let tip = local_chain.tip().expect("must have tip");
519+
520+
let tx_a0 = Transaction {
521+
input: vec![TxIn {
522+
previous_output: OutPoint::new(h!("op0"), 0),
523+
..TxIn::default()
524+
}],
525+
output: vec![TxOut::default(), TxOut::default()],
526+
..common::new_tx(0)
527+
};
528+
529+
// tx_b0 spends tx_a0
530+
let tx_b0 = Transaction {
531+
input: vec![TxIn {
532+
previous_output: OutPoint::new(tx_a0.txid(), 0),
533+
..TxIn::default()
534+
}],
535+
output: vec![TxOut::default(), TxOut::default()],
536+
..common::new_tx(0)
537+
};
538+
539+
// tx_b1 spends tx_a0
540+
let tx_b1 = Transaction {
541+
input: vec![TxIn {
542+
previous_output: OutPoint::new(tx_a0.txid(), 1),
543+
..TxIn::default()
544+
}],
545+
output: vec![TxOut::default()],
546+
..common::new_tx(0)
547+
};
548+
549+
let tx_b2 = Transaction {
550+
input: vec![TxIn {
551+
previous_output: OutPoint::new(h!("op1"), 0),
552+
..TxIn::default()
553+
}],
554+
output: vec![TxOut::default()],
555+
..common::new_tx(0)
556+
};
557+
558+
// tx_c0 spends tx_b0
559+
let tx_c0 = Transaction {
560+
input: vec![TxIn {
561+
previous_output: OutPoint::new(tx_b0.txid(), 0),
562+
..TxIn::default()
563+
}],
564+
output: vec![TxOut::default()],
565+
..common::new_tx(0)
566+
};
567+
568+
// tx_c1 spends tx_b0
569+
let tx_c1 = Transaction {
570+
input: vec![TxIn {
571+
previous_output: OutPoint::new(tx_b0.txid(), 1),
572+
..TxIn::default()
573+
}],
574+
output: vec![TxOut::default()],
575+
..common::new_tx(0)
576+
};
577+
578+
// tx_c2 spends tx_b1 and tx_b2
579+
let tx_c2 = Transaction {
580+
input: vec![
581+
TxIn {
582+
previous_output: OutPoint::new(tx_b1.txid(), 0),
583+
..TxIn::default()
584+
},
585+
TxIn {
586+
previous_output: OutPoint::new(tx_b2.txid(), 0),
587+
..TxIn::default()
588+
},
589+
],
590+
output: vec![TxOut::default()],
591+
..common::new_tx(0)
592+
};
593+
594+
let tx_c3 = Transaction {
595+
input: vec![TxIn {
596+
previous_output: OutPoint::new(h!("op2"), 0),
597+
..TxIn::default()
598+
}],
599+
output: vec![TxOut::default()],
600+
..common::new_tx(0)
601+
};
602+
603+
// tx_d0 spends tx_c1
604+
let tx_d0 = Transaction {
605+
input: vec![TxIn {
606+
previous_output: OutPoint::new(tx_c1.txid(), 0),
607+
..TxIn::default()
608+
}],
609+
output: vec![TxOut::default()],
610+
..common::new_tx(0)
611+
};
612+
613+
// tx_d1 spends tx_c2 and tx_c3
614+
let tx_d1 = Transaction {
615+
input: vec![
616+
TxIn {
617+
previous_output: OutPoint::new(tx_c2.txid(), 0),
618+
..TxIn::default()
619+
},
620+
TxIn {
621+
previous_output: OutPoint::new(tx_c3.txid(), 0),
622+
..TxIn::default()
623+
},
624+
],
625+
output: vec![TxOut::default()],
626+
..common::new_tx(0)
627+
};
628+
629+
// tx_e0 spends tx_d1
630+
let tx_e0 = Transaction {
631+
input: vec![TxIn {
632+
previous_output: OutPoint::new(tx_d1.txid(), 0),
633+
..TxIn::default()
634+
}],
635+
output: vec![TxOut::default()],
636+
..common::new_tx(0)
637+
};
638+
639+
let mut graph = TxGraph::<BlockId>::new(vec![
640+
tx_a0.clone(),
641+
tx_b0.clone(),
642+
tx_b1.clone(),
643+
tx_b2.clone(),
644+
tx_c0.clone(),
645+
tx_c1.clone(),
646+
tx_c2.clone(),
647+
tx_c3.clone(),
648+
tx_d0.clone(),
649+
tx_d1.clone(),
650+
tx_e0.clone(),
651+
]);
652+
653+
[&tx_a0, &tx_b1].iter().for_each(|&tx| {
654+
let _ = graph.insert_anchor(tx.txid(), tip.block_id());
655+
});
656+
657+
let ancestors = [
658+
graph
659+
.walk_ancestors(&tx_c0, |depth, tx| Some((depth, tx)))
660+
.collect::<Vec<_>>(),
661+
graph
662+
.walk_ancestors(&tx_d0, |depth, tx| Some((depth, tx)))
663+
.collect::<Vec<_>>(),
664+
graph
665+
.walk_ancestors(&tx_e0, |depth, tx| Some((depth, tx)))
666+
.collect::<Vec<_>>(),
667+
// Only traverse unconfirmed ancestors of tx_e0 this time
668+
graph
669+
.walk_ancestors(&tx_e0, |depth, tx| {
670+
let tx_node = graph.get_tx_node(tx.txid())?;
671+
for block in tx_node.anchors {
672+
match local_chain.is_block_in_chain(block.anchor_block(), tip.block_id()) {
673+
Ok(Some(true)) => return None,
674+
_ => continue,
675+
}
676+
}
677+
Some((depth, tx_node.tx))
678+
})
679+
.collect::<Vec<_>>(),
680+
];
681+
682+
let expected_ancestors = [
683+
vec![(1, &tx_b0), (2, &tx_a0)],
684+
vec![(1, &tx_c1), (2, &tx_b0), (3, &tx_a0)],
685+
vec![
686+
(1, &tx_d1),
687+
(2, &tx_c2),
688+
(2, &tx_c3),
689+
(3, &tx_b1),
690+
(3, &tx_b2),
691+
(4, &tx_a0),
692+
],
693+
vec![(1, &tx_d1), (2, &tx_c2), (2, &tx_c3), (3, &tx_b2)],
694+
];
695+
696+
for (txids, expected_txids) in ancestors.iter().zip(expected_ancestors.iter()) {
697+
assert_eq!(txids, expected_txids);
698+
}
699+
}
700+
499701
#[test]
500702
fn test_conflicting_descendants() {
501703
let previous_output = OutPoint::new(h!("op"), 2);

0 commit comments

Comments
 (0)