Skip to content

Commit a28cfdf

Browse files
committed
wip
1 parent 9e062a5 commit a28cfdf

File tree

3 files changed

+241
-86
lines changed

3 files changed

+241
-86
lines changed

crates/chain/tests/common/tx_template.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ impl TxOutTemplate {
5656
pub struct TxTemplateEnv<'a, A> {
5757
pub tx_graph: TxGraph<A>,
5858
pub indexer: SpkTxOutIndex<u32>,
59-
pub txid_to_name: HashMap<&'a str, Txid>,
59+
pub tx_name_to_txid: HashMap<&'a str, Txid>,
6060
pub canonicalization_params: CanonicalizationParams,
6161
}
6262

@@ -153,7 +153,7 @@ pub fn init_graph<'a, A: Anchor + Clone + 'a>(
153153
TxTemplateEnv {
154154
tx_graph,
155155
indexer,
156-
txid_to_name,
156+
tx_name_to_txid: txid_to_name,
157157
canonicalization_params,
158158
}
159159
}

crates/chain/tests/test_tx_graph.rs

Lines changed: 236 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
#[macro_use]
44
mod common;
55
use bdk_chain::tx_graph::TxAncestors;
6-
use bdk_chain::{collections::*, BlockId, CanonicalizationParams, ConfirmationBlockTime};
6+
use bdk_chain::{collections::*, Balance, BlockId, CanonicalizationParams, ConfirmationBlockTime};
77
use bdk_chain::{
88
local_chain::LocalChain,
99
tx_graph::{self, CalculateFeeError},
1010
tx_graph::{ChangeSet, TxGraph},
1111
Anchor, ChainOracle, ChainPosition, Merge,
1212
};
13+
use bdk_testenv::local_chain;
1314
use bdk_testenv::{block_id, hash, utils::new_tx};
1415
use bitcoin::hex::FromHex;
1516
use bitcoin::Witness;
@@ -1550,92 +1551,246 @@ fn test_get_first_seen_of_a_tx() {
15501551

15511552
#[test]
15521553
fn test_list_canonical_txs_topological_order() {
1553-
let txs = vec![new_tx(0), new_tx(1), new_tx(2)];
1554-
let txids: Vec<Txid> = txs.iter().map(Transaction::compute_txid).collect();
1555-
1556-
// graph
1557-
let mut graph = TxGraph::<BlockId>::new(txs);
1558-
1559-
let full_txs: Vec<_> = graph.full_txs().collect();
1560-
assert_eq!(full_txs.len(), 3);
1561-
1562-
let unseen_txs: Vec<_> = graph.txs_with_no_anchor_or_last_seen().collect();
1563-
assert_eq!(unseen_txs.len(), 3);
1564-
15651554
// chain
1566-
let blocks: BTreeMap<u32, BlockHash> = [(0, hash!("g")), (1, hash!("A")), (2, hash!("B"))]
1567-
.into_iter()
1568-
.collect();
1569-
let chain = LocalChain::from_blocks(blocks).unwrap();
1570-
let canonical_txs: Vec<_> = graph
1571-
.list_canonical_txs(
1572-
&chain,
1573-
chain.tip().block_id(),
1574-
CanonicalizationParams::default(),
1575-
)
1576-
.collect();
1577-
assert!(canonical_txs.is_empty());
1578-
drop(canonical_txs);
1579-
1580-
// tx0 with seen_at should be returned by canonical txs
1581-
let _ = graph.insert_seen_at(txids[0], 2);
1582-
let mut canonical_txs = graph.list_canonical_txs(
1583-
&chain,
1584-
chain.tip().block_id(),
1585-
CanonicalizationParams::default(),
1586-
);
1587-
assert_eq!(
1588-
canonical_txs.next().map(|tx| tx.tx_node.txid).unwrap(),
1589-
txids[0]
1555+
let local_chain = local_chain!(
1556+
(0, hash!("A")),
1557+
(1, hash!("B")),
1558+
(2, hash!("C")),
1559+
(3, hash!("D")),
1560+
(4, hash!("E")),
1561+
(5, hash!("F")),
1562+
(6, hash!("G"))
15901563
);
1591-
drop(canonical_txs);
1592-
1593-
// tx1 with anchor should be returned by canonical txs
1594-
let _ = graph.insert_anchor(txids[1], block_id!(2, "B"));
1595-
let canonical_txids: Vec<_> = graph
1596-
.list_canonical_txs(
1597-
&chain,
1598-
chain.tip().block_id(),
1599-
CanonicalizationParams::default(),
1600-
)
1601-
.map(|tx| tx.tx_node.txid)
1602-
.collect();
1603-
1604-
assert!(canonical_txids.contains(&txids[1]));
1605-
assert_eq!(
1606-
graph
1607-
.txs_with_no_anchor_or_last_seen()
1608-
.collect::<Vec<_>>()
1609-
.len(),
1610-
1
1611-
);
1612-
1613-
// tx2 with seen_at should be returned by canonical txs
1614-
let _ = graph.insert_seen_at(txids[2], 1);
1615-
let canonical_txids: Vec<_> = graph
1616-
.list_canonical_txs(
1617-
&chain,
1618-
chain.tip().block_id(),
1619-
CanonicalizationParams::default(),
1620-
)
1621-
.map(|tx| tx.tx_node.txid)
1622-
.collect();
1623-
1624-
assert!(canonical_txids.contains(&txids[2]));
1625-
assert!(graph.txs_with_no_anchor_or_last_seen().next().is_none());
1626-
1627-
println!("{:?}", canonical_txids);
1628-
for txid in &canonical_txids {
1629-
let tx_node = graph.get_tx_node(*txid);
1630-
println!("{:?}", tx_node);
1564+
let chain_tip = local_chain.tip().block_id();
1565+
1566+
// scenarios
1567+
let scenarios = [Scenario {
1568+
name: "C spend A, B spend A, and A is in the best chain",
1569+
tx_templates: &[
1570+
TxTemplate {
1571+
tx_name: "A",
1572+
inputs: &[TxInTemplate::Bogus],
1573+
outputs: &[TxOutTemplate::new(10000, Some(0))],
1574+
anchors: &[block_id!(1, "B")],
1575+
last_seen: None,
1576+
assume_canonical: false,
1577+
},
1578+
TxTemplate {
1579+
tx_name: "B",
1580+
inputs: &[TxInTemplate::PrevTx("A", 0), TxInTemplate::Bogus],
1581+
outputs: &[TxOutTemplate::new(5000, Some(0))],
1582+
anchors: &[block_id!(1, "B")],
1583+
last_seen: None,
1584+
assume_canonical: false,
1585+
},
1586+
TxTemplate {
1587+
tx_name: "C",
1588+
inputs: &[TxInTemplate::PrevTx("B", 0), TxInTemplate::Bogus],
1589+
outputs: &[TxOutTemplate::new(2500, Some(0))],
1590+
anchors: &[block_id!(1, "B")],
1591+
last_seen: None,
1592+
assume_canonical: false,
1593+
},
1594+
],
1595+
exp_chain_txs: Vec::from(["A", "B", "C"]),
1596+
exp_chain_txouts: HashSet::from([("A", 0), ("B", 0), ("C", 0)]),
1597+
exp_unspents: HashSet::from([("C", 0)]),
1598+
exp_balance: Balance {
1599+
immature: Amount::ZERO,
1600+
trusted_pending: Amount::ZERO,
1601+
untrusted_pending: Amount::ZERO,
1602+
confirmed: Amount::from_sat(2500),
1603+
},
1604+
},
1605+
Scenario {
1606+
name: "c0 spend b0, b0 spend a0, d0 spends both b0 and c1, c1 spend b1, b1 spend a0, and a0 is in the best chain",
1607+
tx_templates: &[TxTemplate {
1608+
tx_name: "a0",
1609+
inputs: &[TxInTemplate::Bogus],
1610+
outputs: &[TxOutTemplate::new(10000, Some(0)), TxOutTemplate::new(10000, Some(1))],
1611+
anchors: &[block_id!(1, "B")],
1612+
last_seen: None,
1613+
assume_canonical: false,
1614+
}, TxTemplate {
1615+
tx_name: "b0",
1616+
inputs: &[TxInTemplate::PrevTx("a0", 0), TxInTemplate::Bogus],
1617+
outputs: &[TxOutTemplate::new(10000, Some(0)), TxOutTemplate::new(10000, Some(1))],
1618+
anchors: &[block_id!(1, "B")],
1619+
last_seen: None,
1620+
assume_canonical: false,
1621+
},
1622+
TxTemplate {
1623+
tx_name: "c0",
1624+
inputs: &[TxInTemplate::PrevTx("b0", 0), TxInTemplate::Bogus],
1625+
outputs: &[TxOutTemplate::new(5000, Some(0))],
1626+
anchors: &[block_id!(1, "B")],
1627+
last_seen: None,
1628+
assume_canonical: false,
1629+
},
1630+
TxTemplate {
1631+
tx_name: "b1",
1632+
inputs: &[TxInTemplate::PrevTx("a0", 1), TxInTemplate::Bogus],
1633+
outputs: &[TxOutTemplate::new(10000, Some(0))],
1634+
anchors: &[block_id!(1, "B")],
1635+
last_seen: None,
1636+
assume_canonical: false,
1637+
},
1638+
TxTemplate {
1639+
tx_name: "c1",
1640+
inputs: &[TxInTemplate::PrevTx("b1", 0), TxInTemplate::Bogus],
1641+
outputs: &[TxOutTemplate::new(10000, Some(0))],
1642+
anchors: &[block_id!(1, "B")],
1643+
last_seen: None,
1644+
assume_canonical: false,
1645+
},
1646+
TxTemplate {
1647+
tx_name: "d0",
1648+
inputs: &[TxInTemplate::PrevTx("b0", 1), TxInTemplate::PrevTx("c1", 0), TxInTemplate::Bogus],
1649+
outputs: &[TxOutTemplate::new(10000, Some(0))],
1650+
anchors: &[block_id!(1, "B")],
1651+
last_seen: None,
1652+
assume_canonical: false,
1653+
}],
1654+
exp_chain_txs: Vec::from(["a0", "b0", "c0", "b1", "c1", "d0"]),
1655+
exp_chain_txouts: HashSet::from([("a0", 0),("b0", 0),("b1", 0),("c0", 0),("c1", 0), ("d0", 0)]),
1656+
exp_unspents: HashSet::from([("c0", 0), ("d0", 0)]),
1657+
exp_balance: Default::default(),
1658+
}];
1659+
1660+
for scenario in scenarios {
1661+
let env = init_graph(scenario.tx_templates.iter());
1662+
1663+
let canonical_txs = env
1664+
.tx_graph
1665+
.list_canonical_txs(&local_chain, chain_tip, env.canonicalization_params.clone())
1666+
.map(|tx| tx.tx_node.txid)
1667+
.collect::<Vec<_>>();
1668+
1669+
println!("{:?}", env.tx_name_to_txid);
1670+
println!("{:?}", scenario.exp_chain_txs);
1671+
let mut exp_txs: Vec<_> = scenario.exp_chain_txs.iter().collect::<Vec<_>>();
1672+
// exp_txs.sort();
1673+
let exp_txs: Vec<Txid> = exp_txs
1674+
.iter()
1675+
.map(|tx_name| {
1676+
println!("#{:?}", tx_name);
1677+
*env.tx_name_to_txid.get(*tx_name).expect("txid must exist")
1678+
})
1679+
.collect();
1680+
println!("{:?}", exp_txs);
1681+
assert_eq!(
1682+
canonical_txs, exp_txs,
1683+
"\n[{}] 'list_canonical_txs' failed to output the txs in topological order",
1684+
scenario.name
1685+
);
16311686
}
16321687

1633-
let expected_txids = [txids[0], txids[2], txids[1]];
1634-
for (idx, txid) in canonical_txids.iter().enumerate() {
1635-
assert_eq!(expected_txids[idx], *txid);
1636-
}
1688+
// assert
16371689
}
16381690

1691+
struct Scenario<'a> {
1692+
/// Name of the test scenario
1693+
name: &'a str,
1694+
/// Transaction templates
1695+
tx_templates: &'a [TxTemplate<'a, BlockId>],
1696+
/// Names of txs that must exist in the output of `list_canonical_txs`
1697+
exp_chain_txs: Vec<&'a str>,
1698+
/// Outpoints that must exist in the output of `filter_chain_txouts`
1699+
exp_chain_txouts: HashSet<(&'a str, u32)>,
1700+
/// Outpoints of UTXOs that must exist in the output of `filter_chain_unspents`
1701+
exp_unspents: HashSet<(&'a str, u32)>,
1702+
/// Expected balances
1703+
exp_balance: Balance,
1704+
}
1705+
1706+
// #[test]
1707+
// fn test_list_canonical_txs_topological_order() {
1708+
// let txs = vec![new_tx(0), new_tx(1), new_tx(2)];
1709+
// let txids: Vec<Txid> = txs.iter().map(Transaction::compute_txid).collect();
1710+
1711+
// // graph
1712+
// let mut graph = TxGraph::<BlockId>::new(txs);
1713+
1714+
// let full_txs: Vec<_> = graph.full_txs().collect();
1715+
// assert_eq!(full_txs.len(), 3);
1716+
1717+
// let unseen_txs: Vec<_> = graph.txs_with_no_anchor_or_last_seen().collect();
1718+
// assert_eq!(unseen_txs.len(), 3);
1719+
1720+
// // chain
1721+
// let blocks: BTreeMap<u32, BlockHash> = [(0, hash!("g")), (1, hash!("A")), (2, hash!("B"))]
1722+
// .into_iter()
1723+
// .collect();
1724+
// let chain = LocalChain::from_blocks(blocks).unwrap();
1725+
// let canonical_txs: Vec<_> = graph
1726+
// .list_canonical_txs(
1727+
// &chain,
1728+
// chain.tip().block_id(),
1729+
// CanonicalizationParams::default(),
1730+
// )
1731+
// .collect();
1732+
// assert!(canonical_txs.is_empty());
1733+
// drop(canonical_txs);
1734+
1735+
// // tx0 with seen_at should be returned by canonical txs
1736+
// let _ = graph.insert_seen_at(txids[0], 2);
1737+
// let mut canonical_txs = graph.list_canonical_txs(
1738+
// &chain,
1739+
// chain.tip().block_id(),
1740+
// CanonicalizationParams::default(),
1741+
// );
1742+
// assert_eq!(
1743+
// canonical_txs.next().map(|tx| tx.tx_node.txid).unwrap(),
1744+
// txids[0]
1745+
// );
1746+
// drop(canonical_txs);
1747+
1748+
// // tx1 with anchor should be returned by canonical txs
1749+
// let _ = graph.insert_anchor(txids[1], block_id!(2, "B"));
1750+
// let canonical_txids: Vec<_> = graph
1751+
// .list_canonical_txs(
1752+
// &chain,
1753+
// chain.tip().block_id(),
1754+
// CanonicalizationParams::default(),
1755+
// )
1756+
// .map(|tx| tx.tx_node.txid)
1757+
// .collect();
1758+
1759+
// assert!(canonical_txids.contains(&txids[1]));
1760+
// assert_eq!(
1761+
// graph
1762+
// .txs_with_no_anchor_or_last_seen()
1763+
// .collect::<Vec<_>>()
1764+
// .len(),
1765+
// 1
1766+
// );
1767+
1768+
// // tx2 with seen_at should be returned by canonical txs
1769+
// let _ = graph.insert_seen_at(txids[2], 1);
1770+
// let canonical_txids: Vec<_> = graph
1771+
// .list_canonical_txs(
1772+
// &chain,
1773+
// chain.tip().block_id(),
1774+
// CanonicalizationParams::default(),
1775+
// )
1776+
// .map(|tx| tx.tx_node.txid)
1777+
// .collect();
1778+
1779+
// assert!(canonical_txids.contains(&txids[2]));
1780+
// assert!(graph.txs_with_no_anchor_or_last_seen().next().is_none());
1781+
1782+
// println!("{:?}", canonical_txids);
1783+
// for txid in &canonical_txids {
1784+
// let tx_node = graph.get_tx_node(*txid);
1785+
// println!("{:?}", tx_node);
1786+
// }
1787+
1788+
// let expected_txids = [txids[0], txids[2], txids[1]];
1789+
// for (idx, txid) in canonical_txids.iter().enumerate() {
1790+
// assert_eq!(expected_txids[idx], *txid);
1791+
// }
1792+
// }
1793+
16391794
#[test]
16401795
fn test_canonical_txs_topological_order() {
16411796
let previous_output = OutPoint::new(hash!("op"), 2);

crates/chain/tests/test_tx_graph_conflicts.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,7 @@ fn test_tx_conflict_handling() {
978978
let exp_txs = scenario
979979
.exp_chain_txs
980980
.iter()
981-
.map(|txid| *env.txid_to_name.get(txid).expect("txid must exist"))
981+
.map(|txid| *env.tx_name_to_txid.get(txid).expect("txid must exist"))
982982
.collect::<BTreeSet<_>>();
983983
assert_eq!(
984984
txs, exp_txs,
@@ -1000,7 +1000,7 @@ fn test_tx_conflict_handling() {
10001000
.exp_chain_txouts
10011001
.iter()
10021002
.map(|(txid, vout)| OutPoint {
1003-
txid: *env.txid_to_name.get(txid).expect("txid must exist"),
1003+
txid: *env.tx_name_to_txid.get(txid).expect("txid must exist"),
10041004
vout: *vout,
10051005
})
10061006
.collect::<BTreeSet<_>>();
@@ -1024,7 +1024,7 @@ fn test_tx_conflict_handling() {
10241024
.exp_unspents
10251025
.iter()
10261026
.map(|(txid, vout)| OutPoint {
1027-
txid: *env.txid_to_name.get(txid).expect("txid must exist"),
1027+
txid: *env.tx_name_to_txid.get(txid).expect("txid must exist"),
10281028
vout: *vout,
10291029
})
10301030
.collect::<BTreeSet<_>>();

0 commit comments

Comments
 (0)