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