From 09de68c6924df70710242c01ced0680046a73975 Mon Sep 17 00:00:00 2001 From: Pearson White Date: Wed, 23 Oct 2024 14:58:36 -0400 Subject: [PATCH] feat(blockifier): add call_contract cairo native syscall --- .../src/execution/native/syscall_handler.rs | 57 ++++++++++++++++--- .../syscalls/syscall_tests/call_contract.rs | 44 +++++++++++++- 2 files changed, 92 insertions(+), 9 deletions(-) diff --git a/crates/blockifier/src/execution/native/syscall_handler.rs b/crates/blockifier/src/execution/native/syscall_handler.rs index 83ce272aecb..1262501ea6a 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, @@ -11,13 +12,21 @@ use cairo_native::starknet::{ 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; @@ -67,7 +76,6 @@ impl<'state> NativeSyscallHandler<'state> { *syscall_count += n } - #[allow(dead_code)] fn execute_inner_call( &mut self, entry_point: CallEntryPoint, @@ -173,12 +181,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 c1dc0f94ab3..71109cbe945 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); }