@@ -2826,6 +2826,109 @@ fn get_output_account_info(output_merkle_tree_index: u8) -> OutAccountInfo {
28262826 }
28272827}
28282828
2829+ #[ serial]
2830+ #[ tokio:: test]
2831+ async fn test_duplicate_account_in_inputs_and_read_only ( ) {
2832+ spawn_prover ( ProverConfig :: default ( ) ) . await ;
2833+
2834+ let mut config = ProgramTestConfig :: default_with_batched_trees ( false ) ;
2835+ config. with_prover = false ;
2836+ config. additional_programs = Some ( vec ! [ (
2837+ "create_address_test_program" ,
2838+ create_address_test_program:: ID ,
2839+ ) ] ) ;
2840+ config. v2_state_tree_config = Some ( InitStateTreeAccountsInstructionData :: default ( ) ) ;
2841+
2842+ let mut rpc = LightProgramTest :: new ( config) . await . unwrap ( ) ;
2843+ let env = rpc. test_accounts . clone ( ) ;
2844+ let queue = env. v2_state_trees [ 0 ] . output_queue ;
2845+ let tree = env. v2_state_trees [ 0 ] . merkle_tree ;
2846+
2847+ let payer = rpc. get_payer ( ) . insecure_clone ( ) ;
2848+ let mut test_indexer = TestIndexer :: init_from_acounts ( & payer, & env, 0 ) . await ;
2849+
2850+ // Create a compressed account first
2851+ let output_account = get_compressed_output_account ( true , queue) ;
2852+ local_sdk:: perform_test_transaction (
2853+ & mut rpc,
2854+ & mut test_indexer,
2855+ & payer,
2856+ vec ! [ ] ,
2857+ vec ! [ output_account. clone( ) ] ,
2858+ vec ! [ ] ,
2859+ None , // proof
2860+ None ,
2861+ None ,
2862+ false ,
2863+ false ,
2864+ Vec :: new ( ) ,
2865+ Vec :: new ( ) ,
2866+ queue,
2867+ tree,
2868+ false ,
2869+ None ,
2870+ false ,
2871+ None ,
2872+ None ,
2873+ )
2874+ . await
2875+ . unwrap ( )
2876+ . unwrap ( ) ;
2877+
2878+ // Now try to use the same account as both input and read-only
2879+ let compressed_account = test_indexer. compressed_accounts [ 0 ] . clone ( ) ;
2880+
2881+ let read_only_account = ReadOnlyCompressedAccount {
2882+ account_hash : compressed_account. hash ( ) . unwrap ( ) ,
2883+ merkle_context : MerkleContext {
2884+ merkle_tree_pubkey : tree. into ( ) ,
2885+ queue_pubkey : queue. into ( ) ,
2886+ leaf_index : 0 ,
2887+ prove_by_index : false ,
2888+ tree_type : TreeType :: StateV2 ,
2889+ } ,
2890+ root_index : 0 ,
2891+ } ;
2892+
2893+ // Get validity proof for the input account
2894+ let rpc_result = rpc
2895+ . get_validity_proof ( vec ! [ compressed_account. hash( ) . unwrap( ) ] , Vec :: new ( ) , None )
2896+ . await
2897+ . unwrap ( ) ;
2898+
2899+ // Attempt transaction with duplicate account - should fail
2900+ let result = local_sdk:: perform_test_transaction (
2901+ & mut rpc,
2902+ & mut test_indexer,
2903+ & payer,
2904+ vec ! [ compressed_account] , // input_accounts
2905+ vec ! [ ] , // output_accounts
2906+ vec ! [ ] , // new_addresses
2907+ rpc_result. value . proof . 0 , // proof
2908+ None , // sol_compression_recipient
2909+ None , // account_infos
2910+ false , // small_ix
2911+ false , // with_transaction_hash
2912+ vec ! [ read_only_account] , // read_only_accounts
2913+ Vec :: new ( ) , // read_only_addresses
2914+ queue,
2915+ tree,
2916+ false , // is_compress
2917+ None , // compress_or_decompress_lamports
2918+ false , // invalid_sol_pool
2919+ None , // invalid_fee_recipient
2920+ None , // invalid_cpi_context
2921+ )
2922+ . await ;
2923+
2924+ assert_rpc_error (
2925+ result,
2926+ 0 ,
2927+ SystemProgramError :: DuplicateAccountInInputsAndReadOnly . into ( ) ,
2928+ )
2929+ . unwrap ( ) ;
2930+ }
2931+
28292932pub mod local_sdk {
28302933 use std:: collections:: HashMap ;
28312934
0 commit comments