diff --git a/src/lib.rs b/src/lib.rs index 043a25fb9..cb9bdd7db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -198,6 +198,7 @@ mod test { use std::path::PathBuf; use crate::core::contract_address::{compute_deprecated_class_hash, compute_sierra_class_hash}; + use crate::definitions::constants::INITIAL_GAS_COST; use crate::definitions::{ block_context::StarknetChainId, constants::{ @@ -836,7 +837,7 @@ mod test { tx_type: TransactionType::Declare, validate_entry_point_selector: VALIDATE_DECLARE_ENTRY_POINT_SELECTOR.clone(), version: 1.into(), - max_fee: 2, + max_fee: INITIAL_GAS_COST, signature: vec![], nonce: 0.into(), hash_value: 0.into(), @@ -998,4 +999,18 @@ mod test { [(0, 1224), (0, 0)] ); } + + #[test] + fn test_declare_v2_with_invalid_compiled_class_hash() { + let (block_context, mut state) = create_account_tx_test_state().unwrap(); + let mut declare_v2 = declarev2_tx(); + declare_v2.compiled_class_hash = Felt252::from(1); + let declare_tx = Transaction::DeclareV2(Box::new(declare_v2)); + + let err = declare_tx + .execute(&mut state, &block_context, INITIAL_GAS_COST) + .unwrap_err(); + + assert_eq!(err.to_string(), "Invalid compiled class, expected class hash: \"1948962768849191111780391610229754715773924969841143100991524171924131413970\", but received: \"1\"".to_string()); + } } diff --git a/src/testing/mod.rs b/src/testing/mod.rs index fe208b42a..1e7141d92 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -41,7 +41,7 @@ lazy_static! { pub static ref TEST_CLASS_HASH: Felt252 = felt_str!("272"); pub static ref TEST_EMPTY_CONTRACT_CLASS_HASH: Felt252 = felt_str!("274"); pub static ref TEST_ERC20_CONTRACT_CLASS_HASH: Felt252 = felt_str!("4112"); - pub static ref TEST_FIB_COMPILED_CONTRACT_CLASS_HASH: Felt252 = felt_str!("27727"); + pub static ref TEST_FIB_COMPILED_CONTRACT_CLASS_HASH: Felt252 = felt_str!("1948962768849191111780391610229754715773924969841143100991524171924131413970"); // Storage keys. pub static ref TEST_ERC20_ACCOUNT_BALANCE_KEY: Felt252 = diff --git a/src/transaction/declare_v2.rs b/src/transaction/declare_v2.rs index bb08e8ee5..b1d6c490f 100644 --- a/src/transaction/declare_v2.rs +++ b/src/transaction/declare_v2.rs @@ -1,5 +1,5 @@ use super::{verify_version, Transaction}; -use crate::core::contract_address::compute_sierra_class_hash; +use crate::core::contract_address::{compute_casm_class_hash, compute_sierra_class_hash}; use crate::definitions::constants::QUERY_VERSION_BASE; use crate::services::api::contract_classes::deprecated_contract_class::EntryPointType; @@ -381,6 +381,13 @@ impl DeclareV2 { }) .map_err(|e| TransactionError::SierraCompileError(e.to_string()))?; + let casm_class_hash = compute_casm_class_hash(casm_class)?; + if casm_class_hash != self.compiled_class_hash { + return Err(TransactionError::InvalidCompiledClassHash( + casm_class_hash.to_string(), + self.compiled_class_hash.to_string(), + )); + } state.set_compiled_class_hash(&self.sierra_class_hash, &self.compiled_class_hash)?; state.set_compiled_class(&self.compiled_class_hash, casm_class.clone())?; @@ -453,7 +460,7 @@ mod tests { use std::{collections::HashMap, fs::File, io::BufReader, path::PathBuf}; use super::DeclareV2; - use crate::core::contract_address::compute_sierra_class_hash; + use crate::core::contract_address::{compute_casm_class_hash, compute_sierra_class_hash}; use crate::definitions::constants::QUERY_VERSION_BASE; use crate::services::api::contract_classes::compiled_class::CompiledClass; use crate::state::state_api::StateReader; @@ -487,12 +494,15 @@ mod tests { let sierra_contract_class: cairo_lang_starknet::contract_class::ContractClass = serde_json::from_reader(reader).unwrap(); let sender_address = Address(1.into()); + let casm_class = + CasmContractClass::from_contract_class(sierra_contract_class.clone(), true).unwrap(); + let casm_class_hash = compute_casm_class_hash(&casm_class).unwrap(); // create internal declare v2 let internal_declare = DeclareV2::new_with_tx_hash( &sierra_contract_class, - Felt252::one(), + casm_class_hash, sender_address, 0, version, @@ -553,12 +563,16 @@ mod tests { serde_json::from_reader(reader).unwrap(); let sierra_class_hash = compute_sierra_class_hash(&sierra_contract_class).unwrap(); let sender_address = Address(1.into()); + let casm_class = + CasmContractClass::from_contract_class(sierra_contract_class.clone(), true).unwrap(); + let casm_class_hash = compute_casm_class_hash(&casm_class).unwrap(); // create internal declare v2 - let internal_declare = DeclareV2::new_with_tx_hash( + let internal_declare = DeclareV2::new_with_sierra_class_hash_and_tx_hash( &sierra_contract_class, sierra_class_hash, + casm_class_hash, sender_address, 0, version, diff --git a/src/transaction/error.rs b/src/transaction/error.rs index 0e5bf97c0..6ed79c64d 100644 --- a/src/transaction/error.rs +++ b/src/transaction/error.rs @@ -135,4 +135,6 @@ pub enum TransactionError { CallInfoIsNone, #[error("Unsupported version {0:?}")] UnsupportedVersion(String), + #[error("Invalid compiled class, expected class hash: {0:?}, but received: {1:?}")] + InvalidCompiledClassHash(String, String), } diff --git a/starknet_programs/cairo1/fibonacci_dispatcher.cairo b/starknet_programs/cairo1/fibonacci_dispatcher.cairo index fc70d4e7a..3ffea098c 100644 --- a/starknet_programs/cairo1/fibonacci_dispatcher.cairo +++ b/starknet_programs/cairo1/fibonacci_dispatcher.cairo @@ -42,7 +42,8 @@ mod Dispatcher { class_hash: felt252, selector: felt252, a: felt252, b: felt252, n: felt252 ) -> felt252 { FibonacciLibraryDispatcher { - class_hash: starknet::class_hash_const::<27727>(), + // THIS VALUE IS THE HASH OF THE FIBONACCI CASM CLASS HASH. THE SAME AS THE CONSTANT: TEST_FIB_COMPILED_CONTRACT_CLASS_HASH + class_hash: starknet::class_hash_const::<1948962768849191111780391610229754715773924969841143100991524171924131413970>(), selector }.fib(a, b, n) } diff --git a/starknet_programs/cairo2/fibonacci_dispatcher.cairo b/starknet_programs/cairo2/fibonacci_dispatcher.cairo index 3cbb12b14..5dfe9c6be 100644 --- a/starknet_programs/cairo2/fibonacci_dispatcher.cairo +++ b/starknet_programs/cairo2/fibonacci_dispatcher.cairo @@ -51,7 +51,8 @@ mod Dispatcher { self: @ContractState, class_hash: felt252, selector: felt252, a: felt252, b: felt252, n: felt252 ) -> felt252 { FibonacciLibraryDispatcher { - class_hash: starknet::class_hash_const::<27727>(), + // THIS VALUE IS THE HASH OF THE FIBONACCI CASM CLASS HASH. + class_hash: starknet::class_hash_const::<2889767417435368609058888822622483550637539736178264636938129582300971548553>(), selector }.fib(a, b, n) } diff --git a/tests/internals.rs b/tests/internals.rs index 75bacb1ab..6278deae4 100644 --- a/tests/internals.rs +++ b/tests/internals.rs @@ -12,7 +12,9 @@ use cairo_vm::vm::{ use lazy_static::lazy_static; use num_bigint::BigUint; use num_traits::{FromPrimitive, Num, One, Zero}; -use starknet_in_rust::core::contract_address::compute_sierra_class_hash; +use starknet_in_rust::core::contract_address::{ + compute_casm_class_hash, compute_sierra_class_hash, +}; use starknet_in_rust::core::errors::state_errors::StateError; use starknet_in_rust::definitions::constants::{ DEFAULT_CAIRO_RESOURCE_FEE_WEIGHTS, VALIDATE_ENTRY_POINT_SELECTOR, @@ -74,7 +76,8 @@ lazy_static! { static ref TEST_CLASS_HASH: Felt252 = felt_str!("272"); static ref TEST_EMPTY_CONTRACT_CLASS_HASH: Felt252 = felt_str!("274"); static ref TEST_ERC20_CONTRACT_CLASS_HASH: Felt252 = felt_str!("4112"); - static ref TEST_FIB_COMPILED_CONTRACT_CLASS_HASH: Felt252 = felt_str!("27727"); + static ref TEST_FIB_COMPILED_CONTRACT_CLASS_HASH_CAIRO1: Felt252 = felt_str!("1948962768849191111780391610229754715773924969841143100991524171924131413970"); + static ref TEST_FIB_COMPILED_CONTRACT_CLASS_HASH_CAIRO2: Felt252 = felt_str!("2889767417435368609058888822622483550637539736178264636938129582300971548553"); // Storage keys. // NOTE: this key corresponds to the lower 128 bits of an U256 @@ -699,6 +702,9 @@ fn declarev2_tx() -> DeclareV2 { let program_data = include_bytes!("../starknet_programs/cairo1/fibonacci.sierra"); let sierra_contract_class: SierraContractClass = serde_json::from_slice(program_data).unwrap(); let sierra_class_hash = compute_sierra_class_hash(&sierra_contract_class).unwrap(); + let casm_class = + CasmContractClass::from_contract_class(sierra_contract_class.clone(), true).unwrap(); + let casm_class_hash = compute_casm_class_hash(&casm_class).unwrap(); DeclareV2 { sender_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), @@ -709,10 +715,10 @@ fn declarev2_tx() -> DeclareV2 { signature: vec![], nonce: 0.into(), hash_value: 0.into(), - compiled_class_hash: TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone(), + compiled_class_hash: casm_class_hash, sierra_contract_class, sierra_class_hash, - casm_class: Default::default(), + casm_class: casm_class.into(), skip_execute: false, skip_fee_transfer: false, skip_validate: false, @@ -720,12 +726,21 @@ fn declarev2_tx() -> DeclareV2 { } fn deploy_fib_syscall() -> Deploy { + let contract_hash; + #[cfg(not(feature = "cairo_1_tests"))] + { + contract_hash = felt_to_hash(&TEST_FIB_COMPILED_CONTRACT_CLASS_HASH_CAIRO2.clone()) + } + #[cfg(feature = "cairo_1_tests")] + { + contract_hash = felt_to_hash(&TEST_FIB_COMPILED_CONTRACT_CLASS_HASH_CAIRO1.clone()) + } Deploy { hash_value: 0.into(), version: 1.into(), contract_address: TEST_FIB_CONTRACT_ADDRESS.clone(), contract_address_salt: 0.into(), - contract_hash: felt_to_hash(&TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone()), + contract_hash, constructor_calldata: Vec::new(), tx_type: TransactionType::Deploy, skip_execute: false, @@ -862,6 +877,15 @@ fn test_declarev2_tx() { ]); let fee = calculate_tx_fee(&resources, *GAS_PRICE, &block_context).unwrap(); + let contract_hash; + #[cfg(not(feature = "cairo_1_tests"))] + { + contract_hash = TEST_FIB_COMPILED_CONTRACT_CLASS_HASH_CAIRO2.clone(); + } + #[cfg(feature = "cairo_1_tests")] + { + contract_hash = TEST_FIB_COMPILED_CONTRACT_CLASS_HASH_CAIRO1.clone(); + } let expected_execution_info = TransactionExecutionInfo::new( Some(CallInfo { call_type: Some(CallType::Call), @@ -869,7 +893,7 @@ fn test_declarev2_tx() { class_hash: Some(felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH)), entry_point_selector: Some(VALIDATE_DECLARE_ENTRY_POINT_SELECTOR.clone()), entry_point_type: Some(EntryPointType::External), - calldata: vec![TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone()], + calldata: vec![contract_hash], execution_resources: ExecutionResources { n_steps: 12, ..Default::default() @@ -943,6 +967,15 @@ fn expected_execute_call_info() -> CallInfo { } fn expected_fib_execute_call_info() -> CallInfo { + let contract_hash; + #[cfg(not(feature = "cairo_1_tests"))] + { + contract_hash = felt_to_hash(&TEST_FIB_COMPILED_CONTRACT_CLASS_HASH_CAIRO2.clone()); + } + #[cfg(feature = "cairo_1_tests")] + { + contract_hash = felt_to_hash(&TEST_FIB_COMPILED_CONTRACT_CLASS_HASH_CAIRO1.clone()); + } CallInfo { caller_address: Address(Felt252::zero()), call_type: Some(CallType::Call), @@ -972,7 +1005,7 @@ fn expected_fib_execute_call_info() -> CallInfo { internal_calls: vec![CallInfo { caller_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), call_type: Some(CallType::Call), - class_hash: Some(felt_to_hash(&TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone())), + class_hash: Some(contract_hash), entry_point_selector: Some(Felt252::from_bytes_be(&calculate_sn_keccak(b"fib"))), entry_point_type: Some(EntryPointType::External), calldata: vec![Felt252::from(42), Felt252::from(0), Felt252::from(0)], @@ -1760,9 +1793,18 @@ fn test_library_call_with_declare_v2() { ) }; + let casm_contract_hash; + #[cfg(not(feature = "cairo_1_tests"))] + { + casm_contract_hash = TEST_FIB_COMPILED_CONTRACT_CLASS_HASH_CAIRO2.clone() + } + #[cfg(feature = "cairo_1_tests")] + { + casm_contract_hash = TEST_FIB_COMPILED_CONTRACT_CLASS_HASH_CAIRO1.clone() + } // Create an execution entry point let calldata = vec![ - TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone(), + casm_contract_hash, Felt252::from_bytes_be(&calculate_sn_keccak(b"fib")), 1.into(), 1.into(), @@ -1798,11 +1840,21 @@ fn test_library_call_with_declare_v2() { ) .unwrap(); + let casm_contract_hash; + #[cfg(not(feature = "cairo_1_tests"))] + { + casm_contract_hash = TEST_FIB_COMPILED_CONTRACT_CLASS_HASH_CAIRO2.clone() + } + #[cfg(feature = "cairo_1_tests")] + { + casm_contract_hash = TEST_FIB_COMPILED_CONTRACT_CLASS_HASH_CAIRO1.clone() + } + let expected_internal_call_info = CallInfo { caller_address: Address(0.into()), call_type: Some(CallType::Delegate), contract_address: address.clone(), - class_hash: Some(TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone().to_be_bytes()), + class_hash: Some(casm_contract_hash.to_be_bytes()), entry_point_selector: Some(external_entrypoint_selector.into()), entry_point_type: Some(EntryPointType::External), #[cfg(not(feature = "cairo_1_tests"))]