diff --git a/crates/blockifier/src/execution/native/syscall_handler.rs b/crates/blockifier/src/execution/native/syscall_handler.rs index d541ff4363..4e551a3e86 100644 --- a/crates/blockifier/src/execution/native/syscall_handler.rs +++ b/crates/blockifier/src/execution/native/syscall_handler.rs @@ -1,18 +1,27 @@ use std::collections::HashSet; use std::hash::RandomState; +use std::sync::Arc; use cairo_native::starknet::{ ExecutionInfo, ExecutionInfoV2, Secp256k1Point, Secp256r1Point, StarknetSyscallHandler, SyscallResult, U256, }; 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::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::native::utils::encode_str_as_felts; -use crate::execution::syscalls::hint_processor::{SyscallCounter, OUT_OF_GAS_ERROR}; +use crate::execution::syscalls::hint_processor::{ + SyscallCounter, + SyscallExecutionError, + OUT_OF_GAS_ERROR, +}; use crate::execution::syscalls::SyscallSelector; use crate::state::state_api::State; @@ -62,7 +71,6 @@ impl<'state> NativeSyscallHandler<'state> { *syscall_count += n } - #[allow(dead_code)] fn execute_inner_call( &mut self, entry_point: CallEntryPoint, @@ -166,12 +174,45 @@ 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.substract_syscall_gas_cost( + remaining_gas, + SyscallSelector::CallContract, + self.context.gas_costs().call_contract_gas_cost, + )?; + + let contract_address = ContractAddress::try_from(address) + .map_err(|error| encode_str_as_felts(&error.to_string()))?; + 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(encode_str_as_felts(&err.to_string())); + } + + 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 c1dc0f94ab..b6a1d71e53 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,26 @@ fn test_tracked_resources( let expected_outer_resource = outer_version.own_tracked_resource(); assert_eq!(execution.tracked_resource, expected_outer_resource); - let expected_inner_resource = if expected_outer_resource == inner_version.own_tracked_resource() - { - expected_outer_resource - } else { - TrackedResource::CairoSteps - }; + // For native, this logic will be modified below. + let expected_inner_resource = + // If outer and inner are the same, we expect the tracked resource to be the same. + if expected_outer_resource == inner_version.own_tracked_resource() { + expected_outer_resource + } else { + // If outer and inner are not the same, and inner is not Native, we expect CairoSteps. + TrackedResource::CairoSteps + }; + + #[cfg(feature = "cairo_native")] + // If inner is Native, we expect inner to use SierraGas. + // This overrides previous logic. + let expected_inner_resource = + if inner_version == CompilerBasedVersion::CairoVersion(CairoVersion::Native) { + TrackedResource::SierraGas + } else { + expected_inner_resource + }; + assert_eq!(execution.inner_calls.first().unwrap().tracked_resource, expected_inner_resource); }