@@ -7,12 +7,14 @@ use bdk_chain::{
77 indexed_tx_graph:: { self , IndexedTxGraph } ,
88 keychain:: { self , Balance , KeychainTxOutIndex } ,
99 local_chain:: LocalChain ,
10- tx_graph, BlockId , ChainPosition , ConfirmationHeightAnchor ,
10+ tx_graph, BlockId , ChainPosition , ConfirmationHeightAnchor , SpkIterator ,
1111} ;
1212use bitcoin:: {
13- secp256k1:: Secp256k1 , BlockHash , OutPoint , Script , ScriptBuf , Transaction , TxIn , TxOut ,
13+ hashes:: Hash , secp256k1:: Secp256k1 , BlockHash , OutPoint , Script , ScriptBuf , Transaction , TxIn ,
14+ TxOut ,
1415} ;
15- use miniscript:: Descriptor ;
16+ use common:: * ;
17+ use miniscript:: { Descriptor , DescriptorPublicKey } ;
1618
1719/// Ensure [`IndexedTxGraph::insert_relevant_txs`] can successfully index transactions NOT presented
1820/// in topological order.
@@ -471,3 +473,127 @@ fn test_list_owned_txouts() {
471473 ) ;
472474 }
473475}
476+
477+ #[ allow( unused) ]
478+ pub fn single_descriptor_setup ( ) -> (
479+ LocalChain ,
480+ IndexedTxGraph < ConfirmationHeightAnchor , KeychainTxOutIndex < ( ) > > ,
481+ Descriptor < DescriptorPublicKey > ,
482+ ) {
483+ let local_chain = ( 0 ..10 )
484+ . map ( |i| ( i as u32 , BlockHash :: hash ( format ! ( "Block {}" , i) . as_bytes ( ) ) ) )
485+ . collect :: < BTreeMap < u32 , BlockHash > > ( ) ;
486+ let local_chain = LocalChain :: from ( local_chain) ;
487+
488+ let ( desc_1, _) = Descriptor :: parse_descriptor ( & Secp256k1 :: signing_only ( ) , "tr(tprv8ZgxMBicQKsPd3krDUsBAmtnRsK3rb8u5yi1zhQgMhF1tR8MW7xfE4rnrbbsrbPR52e7rKapu6ztw1jXveJSCGHEriUGZV7mCe88duLp5pj/86'/1'/0'/0/*)" ) . unwrap ( ) ;
489+
490+ let mut graph = IndexedTxGraph :: < ConfirmationHeightAnchor , KeychainTxOutIndex < ( ) > > :: default ( ) ;
491+
492+ graph. index . add_keychain ( ( ) , desc_1. clone ( ) ) ;
493+ graph. index . set_lookahead_for_all ( 100 ) ;
494+
495+ ( local_chain, graph, desc_1)
496+ }
497+
498+ #[ allow( unused) ]
499+ pub fn setup_conflicts (
500+ spk_iter : & mut SpkIterator < & Descriptor < DescriptorPublicKey > > ,
501+ ) -> ( Transaction , Transaction , Transaction ) {
502+ let tx1 = Transaction {
503+ output : vec ! [ TxOut {
504+ script_pubkey: spk_iter. next( ) . unwrap( ) . 1 ,
505+ value: 40000 ,
506+ } ] ,
507+ ..new_tx ( 0 )
508+ } ;
509+
510+ let tx_conflict_1 = Transaction {
511+ input : vec ! [ TxIn {
512+ previous_output: OutPoint :: new( tx1. txid( ) , 0 ) ,
513+ ..Default :: default ( )
514+ } ] ,
515+ output : vec ! [ TxOut {
516+ script_pubkey: spk_iter. next( ) . unwrap( ) . 1 ,
517+ value: 20000 ,
518+ } ] ,
519+ ..new_tx ( 0 )
520+ } ;
521+
522+ let tx_conflict_2 = Transaction {
523+ input : vec ! [ TxIn {
524+ previous_output: OutPoint :: new( tx1. txid( ) , 0 ) ,
525+ ..Default :: default ( )
526+ } ] ,
527+ output : vec ! [ TxOut {
528+ script_pubkey: spk_iter. next( ) . unwrap( ) . 1 ,
529+ value: 30000 ,
530+ } ] ,
531+ ..new_tx ( 0 )
532+ } ;
533+
534+ ( tx1, tx_conflict_1, tx_conflict_2)
535+ }
536+
537+ /// Test conflicts for two mempool tx, with same `seen_at` time.
538+ #[ test]
539+ fn test_unconfirmed_conflicts_at_same_last_seen ( ) {
540+ let ( local_chain, mut graph, desc) = single_descriptor_setup ( ) ;
541+ let mut spk_iter = SpkIterator :: new ( & desc) ;
542+ let ( parent_tx, tx_conflict_1, tx_conflict_2) = setup_conflicts ( & mut spk_iter) ;
543+
544+ // Parent confirms at height 2.
545+ let _ = graph. insert_relevant_txs (
546+ [ & parent_tx] . iter ( ) . map ( |tx| {
547+ (
548+ * tx,
549+ [ ConfirmationHeightAnchor {
550+ anchor_block : ( 2 , * local_chain. blocks ( ) . get ( & 2 ) . unwrap ( ) ) . into ( ) ,
551+ confirmation_height : 2 ,
552+ } ] ,
553+ )
554+ } ) ,
555+ None ,
556+ ) ;
557+
558+ // Both conflicts are in mempool at same `seen_at`
559+ let _ = graph. insert_relevant_txs (
560+ [ & tx_conflict_1, & tx_conflict_2]
561+ . iter ( )
562+ . map ( |tx| ( * tx, None ) ) ,
563+ Some ( 100 ) ,
564+ ) ;
565+
566+ let txouts = graph
567+ . graph ( )
568+ . filter_chain_txouts (
569+ & local_chain,
570+ local_chain. tip ( ) . unwrap ( ) . block_id ( ) ,
571+ graph. index . outpoints ( ) . iter ( ) . cloned ( ) ,
572+ )
573+ . collect :: < Vec < _ > > ( ) ;
574+
575+ let utxos = graph
576+ . graph ( )
577+ . filter_chain_unspents (
578+ & local_chain,
579+ local_chain. tip ( ) . unwrap ( ) . block_id ( ) ,
580+ graph. index . outpoints ( ) . iter ( ) . cloned ( ) ,
581+ )
582+ . collect :: < Vec < _ > > ( ) ;
583+
584+ assert_eq ! ( txouts. len( ) , 2 ) ;
585+ assert_eq ! (
586+ txouts
587+ . iter( )
588+ . filter( |( _, txout) | matches!( txout. chain_position, ChainPosition :: Unconfirmed ( 100 ) ) )
589+ . count( ) ,
590+ 1
591+ ) ;
592+ assert_eq ! (
593+ utxos
594+ . iter( )
595+ . filter( |( _, txout) | matches!( txout. chain_position, ChainPosition :: Unconfirmed ( 100 ) ) )
596+ . count( ) ,
597+ 1
598+ ) ;
599+ }
0 commit comments