@@ -7,12 +7,14 @@ use bdk_chain::{
7
7
indexed_tx_graph:: { self , IndexedTxGraph } ,
8
8
keychain:: { self , Balance , KeychainTxOutIndex } ,
9
9
local_chain:: LocalChain ,
10
- tx_graph, BlockId , ChainPosition , ConfirmationHeightAnchor ,
10
+ tx_graph, BlockId , ChainPosition , ConfirmationHeightAnchor , SpkIterator ,
11
11
} ;
12
12
use bitcoin:: {
13
- secp256k1:: Secp256k1 , BlockHash , OutPoint , Script , ScriptBuf , Transaction , TxIn , TxOut ,
13
+ hashes:: Hash , secp256k1:: Secp256k1 , BlockHash , OutPoint , Script , ScriptBuf , Transaction , TxIn ,
14
+ TxOut ,
14
15
} ;
15
- use miniscript:: Descriptor ;
16
+ use common:: * ;
17
+ use miniscript:: { Descriptor , DescriptorPublicKey } ;
16
18
17
19
/// Ensure [`IndexedTxGraph::insert_relevant_txs`] can successfully index transactions NOT presented
18
20
/// in topological order.
@@ -471,3 +473,127 @@ fn test_list_owned_txouts() {
471
473
) ;
472
474
}
473
475
}
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