@@ -104,6 +104,7 @@ use crate::special_contracts::{
104104 V1_BOUND_CAIRO1_CONTRACT_SIERRA ,
105105} ;
106106use crate :: test_manager:: {
107+ block_context_for_flow_tests,
107108 TestManager ,
108109 TestParameters ,
109110 FUNDED_ACCOUNT_ADDRESS ,
@@ -2852,3 +2853,110 @@ async fn test_empty_multi_block() {
28522853 ) ;
28532854 test_output. expect_hint_coverage ( "test_empty_multi_block" ) ;
28542855}
2856+
2857+ /// Validates the migration flow for both declaring contract with casm hash v2, and migrating a
2858+ /// contract that was already declared with casm hash v1. This test covers:
2859+ /// 1. Declaring a contract with casm hash v2 and using it in the same block where it was declared.
2860+ /// 2. Using a contract that was previously declared with casm hash v1 (a migration should be
2861+ /// triggered in this case).
2862+ /// 3. Using the migrated contract in a block after the migration.
2863+ #[ rstest]
2864+ #[ tokio:: test]
2865+ async fn test_compiled_class_hash_migration ( ) {
2866+ let ( mut test_manager, _) =
2867+ TestManager :: < DictStateReader > :: new_with_default_initial_state ( [ ] ) . await ;
2868+
2869+ // Declare two contracts, with V1 and V2 hashes.
2870+ let test_contract = FeatureContract :: TestContract ( CairoVersion :: Cairo1 ( RunnableCairo1 :: Casm ) ) ;
2871+ let empty_contract = FeatureContract :: Empty ( CairoVersion :: Cairo1 ( RunnableCairo1 :: Casm ) ) ;
2872+ let test_contract_sierra = test_contract. get_sierra ( ) ;
2873+ let empty_contract_sierra = empty_contract. get_sierra ( ) ;
2874+ let test_class_hash = test_contract_sierra. calculate_class_hash ( ) ;
2875+ let empty_class_hash = empty_contract_sierra. calculate_class_hash ( ) ;
2876+ let compiled_test_class_hash_v1 = test_contract. get_compiled_class_hash ( & HashVersion :: V1 ) ;
2877+ let compiled_empty_class_hash_v2 = empty_contract. get_compiled_class_hash ( & HashVersion :: V2 ) ;
2878+ let declare_tx_args_v1 = declare_tx_args ! {
2879+ sender_address: * FUNDED_ACCOUNT_ADDRESS ,
2880+ class_hash: test_class_hash,
2881+ compiled_class_hash: compiled_test_class_hash_v1,
2882+ resource_bounds: * NON_TRIVIAL_RESOURCE_BOUNDS ,
2883+ nonce: test_manager. next_nonce( * FUNDED_ACCOUNT_ADDRESS ) ,
2884+ } ;
2885+ let account_declare_tx_v1 = declare_tx ( declare_tx_args_v1) ;
2886+ let class_info = get_class_info_of_feature_contract ( test_contract) ;
2887+ let tx_v1 =
2888+ DeclareTransaction :: create ( account_declare_tx_v1, class_info, & CHAIN_ID_FOR_TESTS ) . unwrap ( ) ;
2889+ test_manager. add_cairo1_declare_tx ( tx_v1, & test_contract_sierra) ;
2890+
2891+ // Move to the next block.
2892+ test_manager. move_to_next_block ( ) ;
2893+
2894+ // Declare the contract with V2 hash.
2895+ let declare_tx_args_v2 = declare_tx_args ! {
2896+ sender_address: * FUNDED_ACCOUNT_ADDRESS ,
2897+ class_hash: empty_class_hash,
2898+ compiled_class_hash: compiled_empty_class_hash_v2,
2899+ resource_bounds: * NON_TRIVIAL_RESOURCE_BOUNDS ,
2900+ nonce: test_manager. next_nonce( * FUNDED_ACCOUNT_ADDRESS ) ,
2901+ } ;
2902+ let account_declare_tx_v2 = declare_tx ( declare_tx_args_v2) ;
2903+ let class_info = get_class_info_of_feature_contract ( empty_contract) ;
2904+ let tx_v2 =
2905+ DeclareTransaction :: create ( account_declare_tx_v2, class_info, & CHAIN_ID_FOR_TESTS ) . unwrap ( ) ;
2906+ test_manager. add_cairo1_declare_tx ( tx_v2, & empty_contract_sierra) ;
2907+
2908+ // Move to the next block.
2909+ test_manager. move_to_next_block ( ) ;
2910+
2911+ // Deploy the contract with V2 hash.
2912+ let ( deploy_tx_v2, _address_v2) = get_deploy_contract_tx_and_address_with_salt (
2913+ empty_class_hash,
2914+ calldata ! [ ] ,
2915+ test_manager. next_nonce ( * FUNDED_ACCOUNT_ADDRESS ) ,
2916+ * NON_TRIVIAL_RESOURCE_BOUNDS ,
2917+ ContractAddressSalt ( Felt :: ZERO ) ,
2918+ ) ;
2919+ test_manager. add_invoke_tx ( deploy_tx_v2, None ) ;
2920+
2921+ // Deploy the V1 contract.
2922+ let ( deploy_tx_v1, address_v1) = get_deploy_contract_tx_and_address_with_salt (
2923+ test_class_hash,
2924+ calldata ! [ Felt :: ONE , Felt :: TWO ] ,
2925+ test_manager. next_nonce ( * FUNDED_ACCOUNT_ADDRESS ) ,
2926+ * NON_TRIVIAL_RESOURCE_BOUNDS ,
2927+ ContractAddressSalt ( Felt :: ZERO ) ,
2928+ ) ;
2929+ test_manager. add_invoke_tx ( deploy_tx_v1, None ) ;
2930+
2931+ // Invoke some function on the V1 contract.
2932+ let calldata = create_calldata ( address_v1, "return_result" , & [ Felt :: from ( 3 ) ] ) ;
2933+ test_manager. add_funded_account_invoke ( invoke_tx_args ! { calldata } ) ;
2934+
2935+ // Create custom block contexts for the two blocks.
2936+ assert_eq ! ( test_manager. n_blocks( ) , 3 ) ;
2937+ let first_block_number = test_manager. initial_state . next_block_number . 0 ;
2938+ let use_kzg_da = true ;
2939+ let block_contexts = [ 0 , 1 , 2 ]
2940+ . iter ( )
2941+ . map ( |i| {
2942+ let mut block_ctx = block_context_for_flow_tests (
2943+ BlockNumber ( first_block_number + * i as u64 ) ,
2944+ use_kzg_da,
2945+ ) ;
2946+ // Migration is enabled, and V1-declare should be enabled only for the first block.
2947+ block_ctx. versioned_constants . enable_casm_hash_migration = true ;
2948+ block_ctx. versioned_constants . block_casm_hash_v1_declares = * i > 0 ;
2949+ block_ctx
2950+ } )
2951+ . collect ( ) ;
2952+
2953+ // Run the test and verify the storage changes.
2954+ let test_output = test_manager
2955+ . execute_test_with_block_contexts (
2956+ block_contexts,
2957+ & TestParameters { use_kzg_da, ..Default :: default ( ) } ,
2958+ )
2959+ . await ;
2960+ test_output. perform_default_validations ( ) ;
2961+ test_output. expect_hint_coverage ( "test_compiled_class_hash_migration" ) ;
2962+ }
0 commit comments