diff --git a/crates/blockifier/src/execution/native/syscall_handler.rs b/crates/blockifier/src/execution/native/syscall_handler.rs index 3de786bc26..68983b824e 100644 --- a/crates/blockifier/src/execution/native/syscall_handler.rs +++ b/crates/blockifier/src/execution/native/syscall_handler.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; use std::hash::RandomState; +use std::sync::Arc; use cairo_native::starknet::{ ExecutionInfo, @@ -12,11 +13,15 @@ use cairo_native::starknet::{ }; use cairo_native::starknet_stub::encode_str_as_felts; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; +use starknet_api::contract_class::EntryPointType; +use starknet_api::core::{ContractAddress, EntryPointSelector}; use starknet_api::state::StorageKey; +use starknet_api::transaction::fields::Calldata; use starknet_types_core::felt::Felt; use crate::execution::call_info::{CallInfo, OrderedEvent, OrderedL2ToL1Message, Retdata}; -use crate::execution::entry_point::{CallEntryPoint, EntryPointExecutionContext}; +use crate::execution::common_hints::ExecutionMode; +use crate::execution::entry_point::{CallEntryPoint, CallType, EntryPointExecutionContext}; use crate::execution::syscalls::hint_processor::{SyscallExecutionError, OUT_OF_GAS_ERROR}; use crate::state::state_api::State; @@ -57,7 +62,6 @@ impl<'state> NativeSyscallHandler<'state> { } } - #[allow(dead_code)] fn execute_inner_call( &mut self, entry_point: CallEntryPoint, @@ -168,12 +172,41 @@ impl<'state> StarknetSyscallHandler for &mut NativeSyscallHandler<'state> { fn call_contract( &mut self, - _address: Felt, - _entry_point_selector: Felt, - _calldata: &[Felt], - _remaining_gas: &mut u128, + address: Felt, + entry_point_selector: Felt, + calldata: &[Felt], + remaining_gas: &mut u128, ) -> SyscallResult> { - todo!("Implement call_contract syscall."); + self.pre_execute_syscall(remaining_gas, self.context.gas_costs().call_contract_gas_cost)?; + + let contract_address = ContractAddress::try_from(address) + .map_err(|error| self.handle_error(remaining_gas, error.into()))?; + if self.context.execution_mode == ExecutionMode::Validate + && self.call.storage_address != contract_address + { + let err = SyscallExecutionError::InvalidSyscallInExecutionMode { + syscall_name: "call_contract".to_string(), + execution_mode: self.context.execution_mode, + }; + return Err(self.handle_error(remaining_gas, err)); + } + + let wrapper_calldata = Calldata(Arc::new(calldata.to_vec())); + + let entry_point = CallEntryPoint { + class_hash: None, + code_address: Some(contract_address), + entry_point_type: EntryPointType::External, + entry_point_selector: EntryPointSelector(entry_point_selector), + calldata: wrapper_calldata, + storage_address: contract_address, + caller_address: self.call.caller_address, + call_type: CallType::Call, + initial_gas: u64::try_from(*remaining_gas) + .expect("Failed to convert gas from u128 to u64."), + }; + + Ok(self.execute_inner_call(entry_point, remaining_gas)?.0) } fn storage_read( diff --git a/crates/blockifier/src/execution/syscalls/syscall_tests/call_contract.rs b/crates/blockifier/src/execution/syscalls/syscall_tests/call_contract.rs index d200b61244..6de8d679b3 100644 --- a/crates/blockifier/src/execution/syscalls/syscall_tests/call_contract.rs +++ b/crates/blockifier/src/execution/syscalls/syscall_tests/call_contract.rs @@ -67,6 +67,15 @@ fn test_call_contract_that_panics() { ); } +#[cfg_attr( + feature = "cairo_native", + test_case( + FeatureContract::TestContract(CairoVersion::Native), + FeatureContract::TestContract(CairoVersion::Native), + 191870; + "Call Contract between two contracts using Native" + ) +)] #[test_case( FeatureContract::TestContract(CairoVersion::Cairo1), FeatureContract::TestContract(CairoVersion::Cairo1), @@ -106,7 +115,30 @@ fn test_call_contract( ); } -/// Cairo0 / Old Cairo1 / Cairo1 calls to Cairo0 / Old Cairo1/ Cairo1. +/// Cairo0 / Old Cairo1 / Cairo1 / Native calls to Cairo0 / Old Cairo1 / Cairo1 / Native. +#[cfg(feature = "cairo_native")] +#[rstest] +fn test_tracked_resources( + #[values( + CompilerBasedVersion::CairoVersion(CairoVersion::Cairo0), + CompilerBasedVersion::OldCairo1, + CompilerBasedVersion::CairoVersion(CairoVersion::Cairo1), + CompilerBasedVersion::CairoVersion(CairoVersion::Native) + )] + outer_version: CompilerBasedVersion, + #[values( + CompilerBasedVersion::CairoVersion(CairoVersion::Cairo0), + CompilerBasedVersion::OldCairo1, + CompilerBasedVersion::CairoVersion(CairoVersion::Cairo1), + CompilerBasedVersion::CairoVersion(CairoVersion::Native) + )] + inner_version: CompilerBasedVersion, +) { + test_tracked_resources_fn(outer_version, inner_version); +} + +/// Cairo0 / Old Cairo1 / Cairo1 calls to Cairo0 / Old Cairo1 / Cairo1. +#[cfg(not(feature = "cairo_native"))] #[rstest] fn test_tracked_resources( #[values( @@ -121,6 +153,13 @@ fn test_tracked_resources( CompilerBasedVersion::CairoVersion(CairoVersion::Cairo1) )] inner_version: CompilerBasedVersion, +) { + test_tracked_resources_fn(outer_version, inner_version); +} + +fn test_tracked_resources_fn( + outer_version: CompilerBasedVersion, + inner_version: CompilerBasedVersion, ) { let outer_contract = outer_version.get_test_contract(); let inner_contract = inner_version.get_test_contract(); @@ -139,12 +178,15 @@ fn test_tracked_resources( let expected_outer_resource = outer_version.own_tracked_resource(); assert_eq!(execution.tracked_resource, expected_outer_resource); + // If the outer call uses CairoSteps, then use it for inner. + // See execute_entry_point_call_wrapper in crates/blockifier/src/execution/execution_utils.rs let expected_inner_resource = if expected_outer_resource == inner_version.own_tracked_resource() { expected_outer_resource } else { TrackedResource::CairoSteps }; + assert_eq!(execution.inner_calls.first().unwrap().tracked_resource, expected_inner_resource); }