diff --git a/crates/blockifier/src/execution/native/syscall_handler.rs b/crates/blockifier/src/execution/native/syscall_handler.rs index 34f34887168..12f9394b6a7 100644 --- a/crates/blockifier/src/execution/native/syscall_handler.rs +++ b/crates/blockifier/src/execution/native/syscall_handler.rs @@ -14,7 +14,14 @@ use cairo_native::starknet::{ U256, }; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; -use starknet_api::core::{calculate_contract_address, ClassHash, ContractAddress, EthAddress}; +use starknet_api::contract_class::EntryPointType; +use starknet_api::core::{ + calculate_contract_address, + ClassHash, + ContractAddress, + EntryPointSelector, + EthAddress, +}; use starknet_api::state::StorageKey; use starknet_api::transaction::fields::{Calldata, ContractAddressSalt}; use starknet_api::transaction::L2ToL1Payload; @@ -30,6 +37,7 @@ use crate::execution::call_info::{ use crate::execution::common_hints::ExecutionMode; use crate::execution::entry_point::{ CallEntryPoint, + CallType, ConstructorContext, EntryPointExecutionContext, }; @@ -83,7 +91,6 @@ impl<'state> NativeSyscallHandler<'state> { } } - #[allow(dead_code)] fn execute_inner_call( &mut self, entry_point: CallEntryPoint, @@ -321,12 +328,32 @@ impl<'state> StarknetSyscallHandler for &mut NativeSyscallHandler<'state> { fn library_call( &mut self, - _class_hash: Felt, - _function_selector: Felt, - _calldata: &[Felt], - _remaining_gas: &mut u128, + class_hash: Felt, + function_selector: Felt, + calldata: &[Felt], + remaining_gas: &mut u128, ) -> SyscallResult> { - todo!("Implement library_call syscall."); + self.pre_execute_syscall(remaining_gas, self.context.gas_costs().library_call_gas_cost)?; + + let class_hash = ClassHash(class_hash); + + let wrapper_calldata = Calldata(Arc::new(calldata.to_vec())); + + let entry_point = CallEntryPoint { + class_hash: Some(class_hash), + code_address: None, + entry_point_type: EntryPointType::External, + entry_point_selector: EntryPointSelector(function_selector), + calldata: wrapper_calldata, + // The call context remains the same in a library call. + storage_address: self.call.storage_address, + caller_address: self.call.caller_address, + call_type: CallType::Delegate, + initial_gas: u64::try_from(*remaining_gas) + .expect("Failed to convert gas (u128 -> u64)"), + }; + + Ok(self.execute_inner_call(entry_point, remaining_gas)?.0) } fn call_contract( diff --git a/crates/blockifier/src/execution/syscalls/syscall_tests/library_call.rs b/crates/blockifier/src/execution/syscalls/syscall_tests/library_call.rs index 0d71757d2f5..7c0c88e7fab 100644 --- a/crates/blockifier/src/execution/syscalls/syscall_tests/library_call.rs +++ b/crates/blockifier/src/execution/syscalls/syscall_tests/library_call.rs @@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet}; use cairo_vm::types::builtin_name::BuiltinName; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use pretty_assertions::assert_eq; +use starknet_api::execution_resources::GasAmount; use starknet_api::execution_utils::format_panic_data; use starknet_api::transaction::fields::GasVectorComputationMode; use starknet_api::{calldata, felt, storage_key}; @@ -28,6 +29,10 @@ use crate::test_utils::{ }; use crate::versioned_constants::VersionedConstants; +#[cfg_attr( + feature = "cairo_native", + test_case(FeatureContract::TestContract(CairoVersion::Native), 186610; "Native") +)] #[test_case(FeatureContract::TestContract(CairoVersion::Cairo1), REQUIRED_GAS_LIBRARY_CALL_TEST; "VM")] fn test_library_call(test_contract: FeatureContract, expected_gas: u64) { let chain_info = &ChainInfo::create_for_testing(); @@ -59,6 +64,10 @@ fn test_library_call(test_contract: FeatureContract, expected_gas: u64) { ); } +#[cfg_attr( + feature = "cairo_native", + test_case(FeatureContract::TestContract(CairoVersion::Native); "Native") +)] #[test_case(FeatureContract::TestContract(CairoVersion::Cairo1); "VM")] fn test_library_call_assert_fails(test_contract: FeatureContract) { let chain_info = &ChainInfo::create_for_testing(); @@ -80,14 +89,45 @@ fn test_library_call_assert_fails(test_contract: FeatureContract) { let call_info = entry_point_call.execute_directly(&mut state).unwrap(); assert!(call_info.execution.failed); - assert_eq!( - format_panic_data(&call_info.execution.retdata.0), - "(0x7820213d2079 ('x != y'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'))" - ); + + let expected_err = match test_contract.cairo_version() { + CairoVersion::Cairo0 | CairoVersion::Cairo1 => { + "(0x7820213d2079 ('x != y'), 0x454e545259504f494e545f4641494c4544 \ + ('ENTRYPOINT_FAILED'))" + } + #[cfg(feature = "cairo_native")] + CairoVersion::Native => "0x7820213d2079 ('x != y')", + }; + assert_eq!(format_panic_data(&call_info.execution.retdata.0), expected_err); } +#[cfg_attr( + feature = "cairo_native", + test_case(FeatureContract::TestContract(CairoVersion::Native), 512510; "Native") +)] #[test_case(FeatureContract::TestContract(CairoVersion::Cairo1), 475110; "VM")] fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { + // Todo(pwhite) 2024/10/28: Execution resources from the VM & Native are mesaured differently + // helper function to change the expected resource values from both of executions + // When gas is changed to be the same between VM and Native this should be removed. + #[cfg_attr(not(feature = "cairo_native"), allow(unused_variables))] + fn if_native(test_contract: &FeatureContract) -> impl Fn(T, T) -> T + '_ { + move |native: T, non_native: T| { + #[cfg(feature = "cairo_native")] + { + if matches!(test_contract, FeatureContract::TestContract(CairoVersion::Native)) { + native + } else { + non_native + } + } + #[cfg(not(feature = "cairo_native"))] + { + non_native + } + } + } + let chain_info = &ChainInfo::create_for_testing(); let mut state = test_state(chain_info, BALANCE, &[(test_contract, 1)]); @@ -108,7 +148,7 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { entry_point_selector: selector_from_name("test_nested_library_call"), calldata: main_entry_point_calldata, class_hash: Some(test_class_hash), - initial_gas: 9999906600, + initial_gas: if_native(&test_contract)(9999584180, 9999292440), ..trivial_external_entry_point_new(test_contract) }; let nested_storage_entry_point = CallEntryPoint { @@ -117,7 +157,7 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { class_hash: Some(test_class_hash), code_address: None, call_type: CallType::Delegate, - initial_gas: 9999600120, + initial_gas: if_native(&test_contract)(9999258160, 9998985960), ..trivial_external_entry_point_new(test_contract) }; let library_entry_point = CallEntryPoint { @@ -132,20 +172,33 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { class_hash: Some(test_class_hash), code_address: None, call_type: CallType::Delegate, - initial_gas: 9999751100, + initial_gas: if_native(&test_contract)(9999418850, 9999136940), ..trivial_external_entry_point_new(test_contract) }; let storage_entry_point = CallEntryPoint { calldata: calldata![felt!(key), felt!(value)], - initial_gas: 9999448480, + initial_gas: if_native(&test_contract)(9999097590, 9998834320), ..nested_storage_entry_point }; - let storage_entry_point_resources = ExecutionResources { - n_steps: 244, - n_memory_holes: 0, - builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 7)]), - }; + let first_storage_entry_point_resources = if_native(&test_contract)( + ChargedResources { + vm_resources: ExecutionResources::default(), + gas_for_fee: GasAmount(25920), + }, + ChargedResources::from_execution_resources(ExecutionResources { + n_steps: 244, + n_memory_holes: 0, + builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 7)]), + }), + ); + let storage_entry_point_resources = if_native(&test_contract)( + ChargedResources { + vm_resources: ExecutionResources::default(), + gas_for_fee: GasAmount(25920), + }, + first_storage_entry_point_resources.clone(), + ); // The default VersionedConstants is used in the execute_directly call bellow. let tracked_resource = test_contract.get_runnable_class().tracked_resource( @@ -157,33 +210,39 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { call: nested_storage_entry_point, execution: CallExecution { retdata: retdata![felt!(value + 1)], - gas_consumed: REQUIRED_GAS_STORAGE_READ_WRITE_TEST, + gas_consumed: if_native(&test_contract)(25920, REQUIRED_GAS_STORAGE_READ_WRITE_TEST), ..CallExecution::default() }, - charged_resources: ChargedResources::from_execution_resources( - storage_entry_point_resources.clone(), - ), + charged_resources: first_storage_entry_point_resources, tracked_resource, storage_read_values: vec![felt!(value + 1)], accessed_storage_keys: HashSet::from([storage_key!(key + 1)]), ..Default::default() }; - let library_call_resources = &get_syscall_resources(SyscallSelector::LibraryCall) - + &ExecutionResources { - n_steps: 377, - n_memory_holes: 0, - builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 15)]), - }; + let library_call_resources = if_native(&test_contract)( + ChargedResources { + vm_resources: ExecutionResources::default(), + gas_for_fee: GasAmount(186610), + }, + ChargedResources::from_execution_resources( + &get_syscall_resources(SyscallSelector::LibraryCall) + + &ExecutionResources { + n_steps: 377, + n_memory_holes: 0, + builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 15)]), + }, + ), + ); let library_call_info = CallInfo { call: library_entry_point, execution: CallExecution { retdata: retdata![felt!(value + 1)], - gas_consumed: REQUIRED_GAS_LIBRARY_CALL_TEST, + gas_consumed: if_native(&test_contract)(186610, REQUIRED_GAS_LIBRARY_CALL_TEST), ..CallExecution::default() }, - charged_resources: ChargedResources::from_execution_resources(library_call_resources), + charged_resources: library_call_resources, inner_calls: vec![nested_storage_call_info], tracked_resource, ..Default::default() @@ -193,24 +252,31 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { call: storage_entry_point, execution: CallExecution { retdata: retdata![felt!(value)], - gas_consumed: REQUIRED_GAS_STORAGE_READ_WRITE_TEST, + gas_consumed: if_native(&test_contract)(25920, REQUIRED_GAS_STORAGE_READ_WRITE_TEST), ..CallExecution::default() }, - charged_resources: ChargedResources::from_execution_resources( - storage_entry_point_resources, - ), + charged_resources: storage_entry_point_resources, storage_read_values: vec![felt!(value)], accessed_storage_keys: HashSet::from([storage_key!(key)]), tracked_resource, ..Default::default() }; - let main_call_resources = &(&get_syscall_resources(SyscallSelector::LibraryCall) * 3) - + &ExecutionResources { - n_steps: 727, - n_memory_holes: 2, - builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 27)]), - }; + let main_call_resources = if_native(&test_contract)( + ChargedResources { + vm_resources: ExecutionResources::default(), + gas_for_fee: GasAmount(512510), + }, + ChargedResources::from_execution_resources( + &(&get_syscall_resources(SyscallSelector::LibraryCall) * 3) + + &ExecutionResources { + n_steps: 727, + n_memory_holes: 2, + builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 27)]), + }, + ), + ); + let expected_call_info = CallInfo { call: main_entry_point.clone(), execution: CallExecution { @@ -218,7 +284,7 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { gas_consumed: expected_gas, ..CallExecution::default() }, - charged_resources: ChargedResources::from_execution_resources(main_call_resources), + charged_resources: main_call_resources, inner_calls: vec![library_call_info, storage_call_info], tracked_resource, ..Default::default()