@@ -731,3 +731,109 @@ fn no_agreement_point() -> anyhow::Result<()> {
731731
732732 Ok ( ( ) )
733733}
734+
735+ #[ test]
736+ fn test_expect_tx_evicted ( ) -> anyhow:: Result < ( ) > {
737+ use bdk_bitcoind_rpc:: bitcoincore_rpc:: bitcoin;
738+ use bdk_bitcoind_rpc:: bitcoincore_rpc:: bitcoincore_rpc_json:: CreateRawTransactionInput ;
739+ use bdk_chain:: miniscript;
740+ use bdk_chain:: spk_txout:: SpkTxOutIndex ;
741+ use bitcoin:: constants:: genesis_block;
742+ use bitcoin:: secp256k1:: Secp256k1 ;
743+ use bitcoin:: Network ;
744+ use std:: collections:: HashMap ;
745+ let env = TestEnv :: new ( ) ?;
746+
747+ let s = bdk_testenv:: utils:: DESCRIPTORS [ 0 ] ;
748+ let desc = miniscript:: Descriptor :: parse_descriptor ( & Secp256k1 :: new ( ) , s)
749+ . unwrap ( )
750+ . 0 ;
751+ let spk = desc. at_derivation_index ( 0 ) ?. script_pubkey ( ) ;
752+
753+ let mut chain = LocalChain :: from_genesis_hash ( genesis_block ( Network :: Regtest ) . block_hash ( ) ) . 0 ;
754+ let chain_tip = chain. tip ( ) . block_id ( ) ;
755+
756+ let mut index = SpkTxOutIndex :: default ( ) ;
757+ index. insert_spk ( ( ) , spk. clone ( ) ) ;
758+ let mut graph = IndexedTxGraph :: < BlockId , _ > :: new ( index) ;
759+
760+ // Receive tx1.
761+ let _ = env. mine_blocks ( 100 , None ) ?;
762+ let txid_1 = env. send (
763+ & Address :: from_script ( & spk, Network :: Regtest ) ?,
764+ Amount :: ONE_BTC ,
765+ ) ?;
766+
767+ let mut emitter = Emitter :: new ( env. rpc_client ( ) , chain. tip ( ) , 1 , HashSet :: from ( [ txid_1] ) ) ;
768+ while let Some ( emission) = emitter. next_block ( ) ? {
769+ let height = emission. block_height ( ) ;
770+ chain. apply_update ( CheckPoint :: from_header ( & emission. block . header , height) ) ?;
771+ }
772+
773+ let changeset = graph. batch_insert_unconfirmed ( emitter. mempool ( ) ?. new_txs ) ;
774+ assert ! ( changeset
775+ . tx_graph
776+ . txs
777+ . iter( )
778+ . any( |tx| tx. compute_txid( ) == txid_1) ) ;
779+ let seen_at = graph
780+ . graph ( )
781+ . get_tx_node ( txid_1)
782+ . unwrap ( )
783+ . last_seen_unconfirmed
784+ . unwrap ( ) ;
785+
786+ // Double spend tx1.
787+
788+ // Get `prevout` from core.
789+ let core = env. rpc_client ( ) ;
790+ let tx1 = & core. get_raw_transaction ( & txid_1, None ) ?;
791+ let txin = & tx1. input [ 0 ] ;
792+ let op = txin. previous_output ;
793+
794+ // Create `tx1b` using the previous output from tx1.
795+ let utxo = CreateRawTransactionInput {
796+ txid : op. txid ,
797+ vout : op. vout ,
798+ sequence : None ,
799+ } ;
800+ let addr = core. get_new_address ( None , None ) ?. assume_checked ( ) ;
801+ let tx = core. create_raw_transaction (
802+ & [ utxo] ,
803+ & HashMap :: from ( [ ( addr. to_string ( ) , Amount :: from_btc ( 49.99 ) ?) ] ) ,
804+ None ,
805+ None ,
806+ ) ?;
807+ let res = core. sign_raw_transaction_with_wallet ( & tx, None , None ) ?;
808+ let tx1b = res. transaction ( ) ?;
809+
810+ // Send the tx.
811+ let _txid_2 = core. send_raw_transaction ( & tx1b) ?;
812+
813+ // Retrieve the expected unconfirmed txids and spks from the graph.
814+ let exp_spk_txids = graph
815+ . list_expected_spk_txids ( & chain, chain_tip, ..)
816+ . collect :: < Vec < _ > > ( ) ;
817+ assert_eq ! ( exp_spk_txids, vec![ ( spk, txid_1) ] ) ;
818+
819+ // Check that mempool emission contains evicted txid.
820+ let mempool_event = emitter. mempool ( ) ?;
821+ assert ! ( mempool_event. evicted_txids. contains( & txid_1) ) ;
822+
823+ // Update graph with evicted tx.
824+ let exp_txids = exp_spk_txids. into_iter ( ) . map ( |( _, txid) | txid) ;
825+ let evicted_txids = mempool_event. evicted_txids ( exp_txids) ;
826+ for txid in evicted_txids {
827+ let _ = graph. insert_evicted_at ( txid, seen_at) ;
828+ }
829+
830+ let canonical_txids = graph
831+ . graph ( )
832+ . list_canonical_txs ( & chain, chain_tip)
833+ . map ( |tx| tx. tx_node . compute_txid ( ) )
834+ . collect :: < Vec < _ > > ( ) ;
835+ // tx1 should no longer be canonical.
836+ assert ! ( !canonical_txids. contains( & txid_1) ) ;
837+
838+ Ok ( ( ) )
839+ }
0 commit comments