From 998d02449d6da6282eb07e9f8084f4257cf0b8b0 Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii Date: Thu, 24 Oct 2024 13:38:54 +0300 Subject: [PATCH] feat(blockifier): add `deploy` syscall implementation --- crates/blockifier/src/execution/errors.rs | 2 + .../execution/native/entry_point_execution.rs | 15 +++- .../src/execution/native/syscall_handler.rs | 79 +++++++++++++++++-- .../syscalls/syscall_tests/deploy.rs | 16 ++++ 4 files changed, 101 insertions(+), 11 deletions(-) diff --git a/crates/blockifier/src/execution/errors.rs b/crates/blockifier/src/execution/errors.rs index 3c7d79d4de..74f73b099e 100644 --- a/crates/blockifier/src/execution/errors.rs +++ b/crates/blockifier/src/execution/errors.rs @@ -91,6 +91,8 @@ pub enum EntryPointExecutionError { #[cfg(feature = "cairo_native")] #[error(transparent)] NativeUnexpectedError(#[from] NativeError), + #[error("Native execution error: {info}")] + NativeExecutionError { info: String }, #[error(transparent)] PostExecutionError(#[from] PostExecutionError), #[error(transparent)] diff --git a/crates/blockifier/src/execution/native/entry_point_execution.rs b/crates/blockifier/src/execution/native/entry_point_execution.rs index 2b1d22b19d..e12290dadf 100644 --- a/crates/blockifier/src/execution/native/entry_point_execution.rs +++ b/crates/blockifier/src/execution/native/entry_point_execution.rs @@ -1,4 +1,5 @@ use cairo_native::execution_result::ContractExecutionResult; +use cairo_native::starknet_stub::decode_felts_as_str; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use num_traits::ToPrimitive; use starknet_api::execution_resources::GasAmount; @@ -27,14 +28,22 @@ pub fn execute_entry_point_call( let mut syscall_handler: NativeSyscallHandler<'_> = NativeSyscallHandler::new(call, state, resources, context); - let execution_result = contract_class.executor.invoke_contract_dynamic( + let call_result = match contract_class.executor.invoke_contract_dynamic( &function_id, &syscall_handler.call.calldata.0.clone(), Some(syscall_handler.call.initial_gas.into()), &mut syscall_handler, - ); + ) { + Ok(res) if res.failure_flag => Err(EntryPointExecutionError::NativeExecutionError { + info: match res.return_values.is_empty() { + false => decode_felts_as_str(&res.return_values), + true => String::from("Unknown error"), + }, + }), + Err(runner_err) => Err(EntryPointExecutionError::NativeUnexpectedError(runner_err)), + Ok(res) => Ok(res), + }?; - let call_result = execution_result.map_err(EntryPointExecutionError::NativeUnexpectedError)?; create_callinfo(call_result, syscall_handler) } diff --git a/crates/blockifier/src/execution/native/syscall_handler.rs b/crates/blockifier/src/execution/native/syscall_handler.rs index 3de786bc26..2b9e23ce51 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,18 @@ use cairo_native::starknet::{ }; use cairo_native::starknet_stub::encode_str_as_felts; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; +use starknet_api::core::{calculate_contract_address, ClassHash, ContractAddress}; use starknet_api::state::StorageKey; +use starknet_api::transaction::fields::{Calldata, ContractAddressSalt}; 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::entry_point::{ + CallEntryPoint, + ConstructorContext, + EntryPointExecutionContext, +}; +use crate::execution::execution_utils::execute_deployment; use crate::execution::syscalls::hint_processor::{SyscallExecutionError, OUT_OF_GAS_ERROR}; use crate::state::state_api::State; @@ -143,15 +151,70 @@ impl<'state> StarknetSyscallHandler for &mut NativeSyscallHandler<'state> { fn deploy( &mut self, - _class_hash: Felt, - _contract_address_salt: Felt, - _calldata: &[Felt], - _deploy_from_zero: bool, - _remaining_gas: &mut u128, + class_hash: Felt, + contract_address_salt: Felt, + calldata: &[Felt], + deploy_from_zero: bool, + remaining_gas: &mut u128, ) -> SyscallResult<(Felt, Vec)> { - todo!("Implement deploy syscall."); - } + self.pre_execute_syscall(remaining_gas, self.context.gas_costs().deploy_gas_cost)?; + + let deployer_address = self.call.storage_address; + let deployer_address_for_calculation = + if deploy_from_zero { ContractAddress::default() } else { deployer_address }; + + let class_hash = ClassHash(class_hash); + let calldata = Calldata(Arc::new(calldata.to_vec())); + + let deployed_contract_address = calculate_contract_address( + ContractAddressSalt(contract_address_salt), + class_hash, + &calldata, + deployer_address_for_calculation, + ) + .map_err(|err| self.handle_error(remaining_gas, err.into()))?; + + let ctor_context = ConstructorContext { + class_hash, + code_address: Some(deployed_contract_address), + storage_address: deployed_contract_address, + caller_address: deployer_address, + }; + let mut remaining_gas_u64 = + u64::try_from(*remaining_gas).expect("Failed to convert gas to u64."); + + let call_info = execute_deployment( + self.state, + self.resources, + self.context, + ctor_context, + calldata, + // Warning: converting of reference would create a new reference to different data, + // example: + // let mut a: u128 = 1; + // let a_ref: &mut u128 = &mut a; + // + // let mut b: u64 = u64::try_from(*a_ref).unwrap(); + // + // assert_eq!(b, 1); + // + // b += 1; + // + // assert_eq!(b, 2); + // assert_eq!(a, 1); + &mut remaining_gas_u64, + ) + .map_err(|err| self.handle_error(remaining_gas, err.into()))?; + + *remaining_gas = u128::from(remaining_gas_u64); + + let constructor_retdata = call_info.execution.retdata.0[..].to_vec(); + + self.inner_calls.push(call_info); + + Ok((Felt::from(deployed_contract_address), constructor_retdata)) + } fn replace_class(&mut self, _class_hash: Felt, _remaining_gas: &mut u128) -> SyscallResult<()> { todo!("Implement replace_class syscall."); } diff --git a/crates/blockifier/src/execution/syscalls/syscall_tests/deploy.rs b/crates/blockifier/src/execution/syscalls/syscall_tests/deploy.rs index c5a477c61e..67b35308af 100644 --- a/crates/blockifier/src/execution/syscalls/syscall_tests/deploy.rs +++ b/crates/blockifier/src/execution/syscalls/syscall_tests/deploy.rs @@ -15,6 +15,10 @@ use crate::test_utils::initial_test_state::test_state; use crate::test_utils::{calldata_for_deploy_test, trivial_external_entry_point_new, CairoVersion}; #[test_case(FeatureContract::TestContract(CairoVersion::Cairo1), 205200;"VM")] +#[cfg_attr( + feature = "cairo_native", + test_case(FeatureContract::TestContract(CairoVersion::Native), 216800;"Native") +)] fn no_constructor(deployer_contract: FeatureContract, expected_gas: u64) { // TODO(Yoni): share the init code of the tests in this file. @@ -63,6 +67,10 @@ fn no_constructor(deployer_contract: FeatureContract, expected_gas: u64) { } #[test_case(FeatureContract::TestContract(CairoVersion::Cairo1);"VM")] +#[cfg_attr( + feature = "cairo_native", + test_case(FeatureContract::TestContract(CairoVersion::Native);"Native") +)] fn no_constructor_nonempty_calldata(deployer_contract: FeatureContract) { let empty_contract = FeatureContract::Empty(CairoVersion::Cairo1); let class_hash = empty_contract.get_class_hash(); @@ -89,6 +97,10 @@ fn no_constructor_nonempty_calldata(deployer_contract: FeatureContract) { } #[test_case(FeatureContract::TestContract(CairoVersion::Cairo1),214550, 4610;"VM")] +#[cfg_attr( + feature = "cairo_native", + test_case(FeatureContract::TestContract(CairoVersion::Native),236750, 15210;"Native") +)] fn with_constructor( deployer_contract: FeatureContract, expected_gas: u64, @@ -151,6 +163,10 @@ fn with_constructor( } #[test_case(FeatureContract::TestContract(CairoVersion::Cairo1);"VM")] +#[cfg_attr( + feature = "cairo_native", + test_case(FeatureContract::TestContract(CairoVersion::Native);"Native") +)] fn to_unavailable_address(deployer_contract: FeatureContract) { let empty_contract = FeatureContract::Empty(CairoVersion::Cairo1); let mut state = test_state(