|
3 | 3 | #[macro_use] |
4 | 4 | mod common; |
5 | 5 | use bdk_chain::tx_graph::TxAncestors; |
6 | | -use bdk_chain::{collections::*, BlockId, CanonicalizationParams, ConfirmationBlockTime}; |
| 6 | +use bdk_chain::{collections::*, Balance, BlockId, CanonicalizationParams, ConfirmationBlockTime}; |
7 | 7 | use bdk_chain::{ |
8 | 8 | local_chain::LocalChain, |
9 | 9 | tx_graph::{self, CalculateFeeError}, |
10 | 10 | tx_graph::{ChangeSet, TxGraph}, |
11 | 11 | Anchor, ChainOracle, ChainPosition, Merge, |
12 | 12 | }; |
| 13 | +use bdk_testenv::local_chain; |
13 | 14 | use bdk_testenv::{block_id, hash, utils::new_tx}; |
14 | 15 | use bitcoin::hex::FromHex; |
15 | 16 | use bitcoin::Witness; |
@@ -1550,92 +1551,189 @@ fn test_get_first_seen_of_a_tx() { |
1550 | 1551 |
|
1551 | 1552 | #[test] |
1552 | 1553 | 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 | | - |
1565 | 1554 | // 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")) |
1590 | 1563 | ); |
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() |
| 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: HashSet::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 | + |
| 1606 | + for scenario in scenarios { |
| 1607 | + let env = init_graph(scenario.tx_templates.iter()); |
| 1608 | + |
| 1609 | + let txs = env |
| 1610 | + .tx_graph |
| 1611 | + .list_canonical_txs(&local_chain, chain_tip, env.canonicalization_params.clone()) |
| 1612 | + .map(|tx| tx.tx_node.txid) |
| 1613 | + .collect::<Vec<_>>(); |
| 1614 | + println!("{:?}", env.txid_to_name); |
| 1615 | + println!("{:?}", txs); |
| 1616 | + println!("{:?}", txs); |
| 1617 | + let exp_txs: Vec<Txid> = scenario |
| 1618 | + .exp_chain_txs |
| 1619 | + .iter() |
1608 | 1620 | .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); |
| 1621 | + .iter() |
| 1622 | + .map(|name| *env.txid_to_name.get(*name).expect("txid must exist")) |
| 1623 | + .collect(); |
| 1624 | + assert_eq!( |
| 1625 | + txs, exp_txs, |
| 1626 | + "\n[{}] 'list_canonical_txs' failed", |
| 1627 | + scenario.name |
| 1628 | + ); |
1631 | 1629 | } |
1632 | 1630 |
|
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 | | - } |
| 1631 | + // assert |
| 1632 | +} |
| 1633 | + |
| 1634 | +struct Scenario<'a> { |
| 1635 | + /// Name of the test scenario |
| 1636 | + name: &'a str, |
| 1637 | + /// Transaction templates |
| 1638 | + tx_templates: &'a [TxTemplate<'a, BlockId>], |
| 1639 | + /// Names of txs that must exist in the output of `list_canonical_txs` |
| 1640 | + exp_chain_txs: HashSet<&'a str>, |
| 1641 | + /// Outpoints that must exist in the output of `filter_chain_txouts` |
| 1642 | + exp_chain_txouts: HashSet<(&'a str, u32)>, |
| 1643 | + /// Outpoints of UTXOs that must exist in the output of `filter_chain_unspents` |
| 1644 | + exp_unspents: HashSet<(&'a str, u32)>, |
| 1645 | + /// Expected balances |
| 1646 | + exp_balance: Balance, |
1637 | 1647 | } |
1638 | 1648 |
|
| 1649 | +// #[test] |
| 1650 | +// fn test_list_canonical_txs_topological_order() { |
| 1651 | +// let txs = vec![new_tx(0), new_tx(1), new_tx(2)]; |
| 1652 | +// let txids: Vec<Txid> = txs.iter().map(Transaction::compute_txid).collect(); |
| 1653 | + |
| 1654 | +// // graph |
| 1655 | +// let mut graph = TxGraph::<BlockId>::new(txs); |
| 1656 | + |
| 1657 | +// let full_txs: Vec<_> = graph.full_txs().collect(); |
| 1658 | +// assert_eq!(full_txs.len(), 3); |
| 1659 | + |
| 1660 | +// let unseen_txs: Vec<_> = graph.txs_with_no_anchor_or_last_seen().collect(); |
| 1661 | +// assert_eq!(unseen_txs.len(), 3); |
| 1662 | + |
| 1663 | +// // chain |
| 1664 | +// let blocks: BTreeMap<u32, BlockHash> = [(0, hash!("g")), (1, hash!("A")), (2, hash!("B"))] |
| 1665 | +// .into_iter() |
| 1666 | +// .collect(); |
| 1667 | +// let chain = LocalChain::from_blocks(blocks).unwrap(); |
| 1668 | +// let canonical_txs: Vec<_> = graph |
| 1669 | +// .list_canonical_txs( |
| 1670 | +// &chain, |
| 1671 | +// chain.tip().block_id(), |
| 1672 | +// CanonicalizationParams::default(), |
| 1673 | +// ) |
| 1674 | +// .collect(); |
| 1675 | +// assert!(canonical_txs.is_empty()); |
| 1676 | +// drop(canonical_txs); |
| 1677 | + |
| 1678 | +// // tx0 with seen_at should be returned by canonical txs |
| 1679 | +// let _ = graph.insert_seen_at(txids[0], 2); |
| 1680 | +// let mut canonical_txs = graph.list_canonical_txs( |
| 1681 | +// &chain, |
| 1682 | +// chain.tip().block_id(), |
| 1683 | +// CanonicalizationParams::default(), |
| 1684 | +// ); |
| 1685 | +// assert_eq!( |
| 1686 | +// canonical_txs.next().map(|tx| tx.tx_node.txid).unwrap(), |
| 1687 | +// txids[0] |
| 1688 | +// ); |
| 1689 | +// drop(canonical_txs); |
| 1690 | + |
| 1691 | +// // tx1 with anchor should be returned by canonical txs |
| 1692 | +// let _ = graph.insert_anchor(txids[1], block_id!(2, "B")); |
| 1693 | +// let canonical_txids: Vec<_> = graph |
| 1694 | +// .list_canonical_txs( |
| 1695 | +// &chain, |
| 1696 | +// chain.tip().block_id(), |
| 1697 | +// CanonicalizationParams::default(), |
| 1698 | +// ) |
| 1699 | +// .map(|tx| tx.tx_node.txid) |
| 1700 | +// .collect(); |
| 1701 | + |
| 1702 | +// assert!(canonical_txids.contains(&txids[1])); |
| 1703 | +// assert_eq!( |
| 1704 | +// graph |
| 1705 | +// .txs_with_no_anchor_or_last_seen() |
| 1706 | +// .collect::<Vec<_>>() |
| 1707 | +// .len(), |
| 1708 | +// 1 |
| 1709 | +// ); |
| 1710 | + |
| 1711 | +// // tx2 with seen_at should be returned by canonical txs |
| 1712 | +// let _ = graph.insert_seen_at(txids[2], 1); |
| 1713 | +// let canonical_txids: Vec<_> = graph |
| 1714 | +// .list_canonical_txs( |
| 1715 | +// &chain, |
| 1716 | +// chain.tip().block_id(), |
| 1717 | +// CanonicalizationParams::default(), |
| 1718 | +// ) |
| 1719 | +// .map(|tx| tx.tx_node.txid) |
| 1720 | +// .collect(); |
| 1721 | + |
| 1722 | +// assert!(canonical_txids.contains(&txids[2])); |
| 1723 | +// assert!(graph.txs_with_no_anchor_or_last_seen().next().is_none()); |
| 1724 | + |
| 1725 | +// println!("{:?}", canonical_txids); |
| 1726 | +// for txid in &canonical_txids { |
| 1727 | +// let tx_node = graph.get_tx_node(*txid); |
| 1728 | +// println!("{:?}", tx_node); |
| 1729 | +// } |
| 1730 | + |
| 1731 | +// let expected_txids = [txids[0], txids[2], txids[1]]; |
| 1732 | +// for (idx, txid) in canonical_txids.iter().enumerate() { |
| 1733 | +// assert_eq!(expected_txids[idx], *txid); |
| 1734 | +// } |
| 1735 | +// } |
| 1736 | + |
1639 | 1737 | #[test] |
1640 | 1738 | fn test_canonical_txs_topological_order() { |
1641 | 1739 | let previous_output = OutPoint::new(hash!("op"), 2); |
|
0 commit comments