@@ -1554,113 +1554,309 @@ fn tx_outpoint_range(txid: Txid) -> RangeInclusive<OutPoint> {
1554
1554
OutPoint :: new ( txid, u32:: MIN ) ..=OutPoint :: new ( txid, u32:: MAX )
1555
1555
}
1556
1556
1557
- /// Bench
1558
- #[ allow( unused) ]
1559
- #[ allow( missing_docs) ]
1560
- #[ cfg( bdk_bench) ]
1561
- pub mod bench {
1562
- use std:: str:: FromStr ;
1563
-
1557
+ #[ cfg( any( test, bdk_bench) ) ]
1558
+ mod bench_util {
1564
1559
use bdk_core:: { CheckPoint , ConfirmationBlockTime } ;
1565
- use bitcoin:: absolute;
1566
- use bitcoin:: hashes:: Hash ;
1567
- use bitcoin:: transaction;
1568
- use bitcoin:: { Address , BlockHash , Network , TxIn } ;
1569
- use criterion:: Criterion ;
1570
- use miniscript:: Descriptor ;
1571
- use miniscript:: DescriptorPublicKey ;
1560
+ use bitcoin:: {
1561
+ absolute, constants, hashes:: Hash , secp256k1:: Secp256k1 , transaction, BlockHash , Network ,
1562
+ TxIn ,
1563
+ } ;
1564
+ use miniscript:: { Descriptor , DescriptorPublicKey } ;
1572
1565
1573
1566
use super :: * ;
1574
1567
use crate :: keychain_txout:: KeychainTxOutIndex ;
1575
1568
use crate :: local_chain:: LocalChain ;
1576
1569
use crate :: IndexedTxGraph ;
1577
1570
1578
1571
#[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
1579
- enum Keychain {
1572
+ pub enum Keychain {
1580
1573
External ,
1581
1574
}
1582
1575
1583
1576
const EXTERNAL : & str = "tr([ab28dc00/86h/1h/0h]tpubDCdDtzAMZZrkwKBxwNcGCqe4FRydeD9rfMisoi7qLdraG79YohRfPW4YgdKQhpgASdvh612xXNY5xYzoqnyCgPbkpK4LSVcH5Xv4cK7johH/0/*)" ;
1584
1577
1585
- fn get_params ( ) -> (
1586
- IndexedTxGraph < ConfirmationBlockTime , KeychainTxOutIndex < Keychain > > ,
1587
- LocalChain ,
1588
- ) {
1589
- let genesis = bitcoin:: constants:: genesis_block ( Network :: Regtest ) . block_hash ( ) ;
1578
+ pub fn parse_descriptor ( s : & str ) -> Descriptor < DescriptorPublicKey > {
1579
+ <Descriptor < DescriptorPublicKey > >:: parse_descriptor ( & Secp256k1 :: new ( ) , s)
1580
+ . unwrap ( )
1581
+ . 0
1582
+ }
1583
+
1584
+ /// New tx guaranteed to have at least one output
1585
+ pub fn new_tx ( lt : u32 ) -> Transaction {
1586
+ Transaction {
1587
+ version : transaction:: Version :: TWO ,
1588
+ lock_time : absolute:: LockTime :: from_consensus ( lt) ,
1589
+ input : vec ! [ ] ,
1590
+ output : vec ! [ TxOut :: NULL ] ,
1591
+ }
1592
+ }
1593
+
1594
+ pub fn spk_at_index ( txout_index : & KeychainTxOutIndex < Keychain > , index : u32 ) -> ScriptBuf {
1595
+ txout_index
1596
+ . get_descriptor ( Keychain :: External )
1597
+ . unwrap ( )
1598
+ . at_derivation_index ( index)
1599
+ . unwrap ( )
1600
+ . script_pubkey ( )
1601
+ }
1602
+
1603
+ type KeychainTxGraph = IndexedTxGraph < ConfirmationBlockTime , KeychainTxOutIndex < Keychain > > ;
1604
+
1605
+ /// Initialize indexed tx-graph with one keychain and a local chain. Also insert the
1606
+ /// first ancestor tx.
1607
+ pub fn init_graph_chain ( ) -> ( KeychainTxGraph , LocalChain ) {
1608
+ let genesis = constants:: genesis_block ( Network :: Regtest ) . block_hash ( ) ;
1590
1609
let block_0 = BlockId {
1591
1610
height : 0 ,
1592
1611
hash : genesis,
1593
1612
} ;
1613
+ // chain tip 100
1594
1614
let mut cp = CheckPoint :: new ( block_0) ;
1595
- let block_100 = BlockId {
1615
+ let chain_tip = BlockId {
1596
1616
height : 100 ,
1597
1617
hash : BlockHash :: all_zeros ( ) ,
1598
1618
} ;
1599
- cp = cp. push ( block_100 ) . unwrap ( ) ;
1619
+ cp = cp. push ( chain_tip ) . unwrap ( ) ;
1600
1620
let chain = LocalChain :: from_tip ( cp) . unwrap ( ) ;
1601
1621
1602
- let mut graph = IndexedTxGraph :: new ( {
1603
- let mut index = KeychainTxOutIndex :: new ( 10 ) ;
1604
- index
1605
- . insert_descriptor ( Keychain :: External , parse_descriptor ( EXTERNAL ) )
1606
- . unwrap ( ) ;
1607
- index
1608
- } ) ;
1622
+ let desc = parse_descriptor ( EXTERNAL ) ;
1623
+ let mut index = KeychainTxOutIndex :: new ( 10 ) ;
1624
+ index. insert_descriptor ( Keychain :: External , desc) . unwrap ( ) ;
1625
+ let mut graph = IndexedTxGraph :: new ( index) ;
1609
1626
1610
- // insert funding tx (coinbase)
1611
- let addr_0 =
1612
- Address :: from_str ( "bcrt1plhmjhj75nut38qwwm5w7xqysy25xhd4ckuv7zu5tey3nkmcwh3cqvan5mz" )
1613
- . unwrap ( )
1614
- . assume_checked ( ) ;
1615
- let tx_0 = Transaction {
1627
+ // insert funding tx (coinbase) confirmed at chain tip
1628
+ add_ancestor_tx ( & mut graph, chain_tip, 0 ) ;
1629
+
1630
+ ( graph, chain)
1631
+ }
1632
+
1633
+ /// Add ancestor tx confirmed at `block_id` with `locktime` (used for uniqueness).
1634
+ /// The transaction always pays 1 BTC to SPK 0.
1635
+ pub fn add_ancestor_tx ( graph : & mut KeychainTxGraph , block_id : BlockId , locktime : u32 ) {
1636
+ let spk_0 = spk_at_index ( & graph. index , 0 ) ;
1637
+ let tx = Transaction {
1638
+ input : vec ! [ TxIn :: default ( ) ] ,
1616
1639
output : vec ! [ TxOut {
1617
- script_pubkey: addr_0. script_pubkey( ) ,
1618
1640
value: Amount :: ONE_BTC ,
1641
+ script_pubkey: spk_0,
1619
1642
} ] ,
1620
- ..new_tx ( 0 )
1643
+ ..new_tx ( locktime )
1621
1644
} ;
1622
- let txid_0 = tx_0 . compute_txid ( ) ;
1623
- let _ = graph. insert_tx ( tx_0 ) ;
1645
+ let txid = tx . compute_txid ( ) ;
1646
+ let _ = graph. insert_tx ( tx ) ;
1624
1647
let _ = graph. insert_anchor (
1625
- txid_0 ,
1648
+ txid ,
1626
1649
ConfirmationBlockTime {
1627
- block_id : block_100 ,
1650
+ block_id,
1628
1651
confirmation_time : 100 ,
1629
1652
} ,
1630
1653
) ;
1654
+ }
1631
1655
1632
- ( graph, chain)
1656
+ /// Add `n` conflicts to `graph` that spend the given `previous_output`, incrementing
1657
+ /// the tx last-seen each time.
1658
+ pub fn add_conflicts ( n : u32 , graph : & mut KeychainTxGraph , previous_output : OutPoint ) {
1659
+ let spk_1 = spk_at_index ( & graph. index , 1 ) ;
1660
+ for i in 1 ..n + 1 {
1661
+ let tx = Transaction {
1662
+ input : vec ! [ TxIn {
1663
+ previous_output,
1664
+ ..Default :: default ( )
1665
+ } ] ,
1666
+ output : vec ! [ TxOut {
1667
+ value: Amount :: ONE_BTC - Amount :: from_sat( i as u64 * 10 ) ,
1668
+ script_pubkey: spk_1. clone( ) ,
1669
+ } ] ,
1670
+ ..new_tx ( i)
1671
+ } ;
1672
+ let update = TxUpdate {
1673
+ txs : vec ! [ Arc :: new( tx) ] ,
1674
+ ..Default :: default ( )
1675
+ } ;
1676
+ let _ = graph. apply_update_at ( update, Some ( i as u64 ) ) ;
1677
+ }
1633
1678
}
1634
1679
1635
- fn parse_descriptor ( s : & str ) -> miniscript:: Descriptor < DescriptorPublicKey > {
1636
- <Descriptor < DescriptorPublicKey > >:: parse_descriptor (
1637
- & bitcoin:: secp256k1:: Secp256k1 :: new ( ) ,
1638
- s,
1639
- )
1640
- . unwrap ( )
1641
- . 0
1680
+ /// Apply a chain of `n` unconfirmed txs where each subsequent tx spends the output
1681
+ /// of the previous one.
1682
+ pub fn chain_unconfirmed ( n : u32 , graph : & mut KeychainTxGraph , mut previous_output : OutPoint ) {
1683
+ for i in 0 ..n {
1684
+ // create tx
1685
+ let tx = Transaction {
1686
+ input : vec ! [ TxIn {
1687
+ previous_output,
1688
+ ..Default :: default ( )
1689
+ } ] ,
1690
+ ..new_tx ( i)
1691
+ } ;
1692
+ let txid = tx. compute_txid ( ) ;
1693
+ let update = TxUpdate {
1694
+ txs : vec ! [ Arc :: new( tx) ] ,
1695
+ ..Default :: default ( )
1696
+ } ;
1697
+ let _ = graph. apply_update_at ( update, Some ( 21 ) ) ;
1698
+ // store the next prevout
1699
+ previous_output = OutPoint :: new ( txid, 0 ) ;
1700
+ }
1642
1701
}
1643
1702
1644
- fn new_tx ( lt : u32 ) -> Transaction {
1645
- Transaction {
1646
- version : transaction:: Version :: TWO ,
1647
- lock_time : absolute:: LockTime :: from_consensus ( lt) ,
1648
- input : vec ! [ ] ,
1649
- output : vec ! [ ] ,
1703
+ /// Insert `n` txs where
1704
+ /// - half spend ancestor A
1705
+ /// - half spend ancestor B
1706
+ /// - and one spends both
1707
+ pub fn add_nested_conflicts ( n : u32 , graph : & mut KeychainTxGraph , chain : & LocalChain ) {
1708
+ // add ancestor B
1709
+ add_ancestor_tx ( graph, chain. tip ( ) . block_id ( ) , 1 ) ;
1710
+
1711
+ let outpoints: Vec < _ > = graph. index . outpoints ( ) . iter ( ) . map ( |( _, op) | * op) . collect ( ) ;
1712
+ assert ! ( outpoints. len( ) >= 2 ) ;
1713
+ let op_a = outpoints[ 0 ] ;
1714
+ let op_b = outpoints[ 1 ] ;
1715
+
1716
+ for i in 0 ..n {
1717
+ let tx = if i == n / 2 {
1718
+ // tx spends both A, B
1719
+ Transaction {
1720
+ input : vec ! [
1721
+ TxIn {
1722
+ previous_output: op_a,
1723
+ ..Default :: default ( )
1724
+ } ,
1725
+ TxIn {
1726
+ previous_output: op_b,
1727
+ ..Default :: default ( )
1728
+ } ,
1729
+ ] ,
1730
+ ..new_tx ( i)
1731
+ }
1732
+ } else if i % 2 == 1 {
1733
+ // tx spends A
1734
+ Transaction {
1735
+ input : vec ! [ TxIn {
1736
+ previous_output: op_a,
1737
+ ..Default :: default ( )
1738
+ } ] ,
1739
+ ..new_tx ( i)
1740
+ }
1741
+ } else {
1742
+ // tx spends B
1743
+ Transaction {
1744
+ input : vec ! [ TxIn {
1745
+ previous_output: op_b,
1746
+ ..Default :: default ( )
1747
+ } ] ,
1748
+ ..new_tx ( i)
1749
+ }
1750
+ } ;
1751
+
1752
+ let update = TxUpdate {
1753
+ txs : vec ! [ Arc :: new( tx) ] ,
1754
+ ..Default :: default ( )
1755
+ } ;
1756
+ let _ = graph. apply_update_at ( update, Some ( i as u64 ) ) ;
1650
1757
}
1651
1758
}
1652
1759
1760
+ #[ test]
1761
+ fn test_add_conflicts ( ) {
1762
+ let ( mut graph, chain) = init_graph_chain ( ) ;
1763
+ let txouts: Vec < _ > = graph. graph ( ) . all_txouts ( ) . collect ( ) ;
1764
+ assert_eq ! ( txouts. len( ) , 1 ) ;
1765
+ let prevout = txouts. iter ( ) . next ( ) . unwrap ( ) . 0 ;
1766
+ add_conflicts ( 3 , & mut graph, prevout) ;
1767
+
1768
+ let unspent = graph. graph ( ) . filter_chain_unspents (
1769
+ & chain,
1770
+ chain. tip ( ) . block_id ( ) ,
1771
+ graph. index . outpoints ( ) . clone ( ) ,
1772
+ ) ;
1773
+ assert_eq ! ( unspent. count( ) , 1 ) ;
1774
+ }
1775
+
1776
+ #[ test]
1777
+ fn test_chain_unconfirmed ( ) {
1778
+ let ( mut graph, _) = init_graph_chain ( ) ;
1779
+ let ( prevout, _txout) = graph
1780
+ . graph ( )
1781
+ . all_txouts ( )
1782
+ . find ( |( _, txout) | txout. value == Amount :: ONE_BTC )
1783
+ . expect ( "initial graph should have txout" ) ;
1784
+ chain_unconfirmed ( 5 , & mut graph, prevout) ;
1785
+ assert_eq ! ( graph. graph( ) . txs. len( ) , 6 ) ; // 1 onchain + 5 unconfirmed
1786
+ }
1787
+
1788
+ #[ test]
1789
+ #[ rustfmt:: skip]
1790
+ fn test_nested ( ) {
1791
+ let ( mut graph, chain) = init_graph_chain ( ) ;
1792
+ let chain_tip = chain. tip ( ) . block_id ( ) ;
1793
+ let n = 5 ;
1794
+ add_nested_conflicts ( n, & mut graph, & chain) ;
1795
+
1796
+ let op = graph. index . outpoints ( ) . clone ( ) ;
1797
+ assert_eq ! ( graph. graph( ) . full_txs( ) . count( ) as u32 , 2 + n) ; // 2 onchain + n unconfirmed
1798
+ assert_eq ! ( graph. graph( ) . filter_chain_txouts( & chain, chain_tip, op) . count( ) , 2 ) ; // 2 onchain
1799
+ assert_eq ! ( graph. graph( ) . list_canonical_txs( & chain, chain_tip) . count( ) , 2 + 2 ) ; // 2 onchain + 2 unconfirmed
1800
+ }
1801
+ }
1802
+
1803
+ /// Bench
1804
+ #[ allow( missing_docs) ]
1805
+ #[ cfg( bdk_bench) ]
1806
+ pub mod bench {
1807
+ use std:: hint:: black_box;
1808
+
1809
+ use criterion:: Criterion ;
1810
+
1811
+ use super :: bench_util:: * ;
1812
+
1813
+ #[ inline( never) ]
1653
1814
pub fn filter_chain_unspents ( bench : & mut Criterion ) {
1654
- let ( graph, chain) = get_params ( ) ;
1655
- // TODO: insert conflicts
1815
+ let ( mut graph, chain) = black_box ( init_graph_chain ( ) ) ;
1816
+ let prevout = graph. graph ( ) . all_txouts ( ) . next ( ) . unwrap ( ) . 0 ;
1817
+ black_box ( add_conflicts ( 1000 , & mut graph, prevout) ) ;
1818
+
1656
1819
bench. bench_function ( "filter_chain_unspents" , |b| {
1657
1820
b. iter ( || {
1658
- TxGraph :: filter_chain_unspents (
1659
- graph. graph ( ) ,
1821
+ let unspent = graph. graph ( ) . filter_chain_unspents (
1660
1822
& chain,
1661
1823
chain. tip ( ) . block_id ( ) ,
1662
1824
graph. index . outpoints ( ) . clone ( ) ,
1663
- )
1825
+ ) ;
1826
+ assert_eq ! ( unspent. count( ) , 1 ) ;
1827
+ } )
1828
+ } ) ;
1829
+ }
1830
+
1831
+ #[ inline( never) ]
1832
+ pub fn list_canonical_txs ( bench : & mut Criterion ) {
1833
+ let ( mut graph, chain) = black_box ( init_graph_chain ( ) ) ;
1834
+ let prevout = graph. graph ( ) . all_txouts ( ) . next ( ) . unwrap ( ) . 0 ;
1835
+ black_box ( chain_unconfirmed ( 100 , & mut graph, prevout) ) ;
1836
+
1837
+ bench. bench_function ( "list_canonical_txs" , |b| {
1838
+ b. iter ( || {
1839
+ let txs = graph
1840
+ . graph ( )
1841
+ . list_canonical_txs ( & chain, chain. tip ( ) . block_id ( ) ) ;
1842
+ // 1 onchain + 100 unconfirmed
1843
+ assert_eq ! ( txs. count( ) , 1 + 100 ) ;
1844
+ } )
1845
+ } ) ;
1846
+ }
1847
+
1848
+ #[ inline( never) ]
1849
+ pub fn nested_conflicts ( bench : & mut Criterion ) {
1850
+ let ( mut graph, chain) = black_box ( init_graph_chain ( ) ) ;
1851
+ black_box ( add_nested_conflicts ( 2000 , & mut graph, & chain) ) ;
1852
+ let graph = graph. graph ( ) ;
1853
+ let chain_tip = chain. tip ( ) . block_id ( ) ;
1854
+
1855
+ bench. bench_function ( "nested_conflicts" , |b| {
1856
+ b. iter ( || {
1857
+ let txs = graph. list_canonical_txs ( & chain, chain_tip) ;
1858
+ // 2 onchain + 2 unconfirmed
1859
+ assert_eq ! ( txs. count( ) , 4 ) ;
1664
1860
} )
1665
1861
} ) ;
1666
1862
}
0 commit comments