From 9de0e667e3777993d421ae702369bfca9024c42f 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 --- .../src/execution/native/syscall_handler.rs | 80 ++++++++++++++++--- .../syscalls/syscall_tests/deploy.rs | 16 ++++ 2 files changed, 87 insertions(+), 9 deletions(-) diff --git a/crates/blockifier/src/execution/native/syscall_handler.rs b/crates/blockifier/src/execution/native/syscall_handler.rs index 48cf8e76d0..34f3488716 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::{ BlockInfo, @@ -13,8 +14,9 @@ use cairo_native::starknet::{ U256, }; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; -use starknet_api::core::EthAddress; +use starknet_api::core::{calculate_contract_address, ClassHash, ContractAddress, EthAddress}; use starknet_api::state::StorageKey; +use starknet_api::transaction::fields::{Calldata, ContractAddressSalt}; use starknet_api::transaction::L2ToL1Payload; use starknet_types_core::felt::Felt; @@ -26,8 +28,13 @@ use crate::execution::call_info::{ Retdata, }; use crate::execution::common_hints::ExecutionMode; -use crate::execution::entry_point::{CallEntryPoint, EntryPointExecutionContext}; +use crate::execution::entry_point::{ + CallEntryPoint, + ConstructorContext, + EntryPointExecutionContext, +}; use crate::execution::errors::EntryPointExecutionError; +use crate::execution::execution_utils::execute_deployment; use crate::execution::syscalls::hint_processor::{ SyscallExecutionError, INVALID_INPUT_LENGTH_ERROR, @@ -244,15 +251,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..bca64ff2de 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), 215310;"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),233890, 13840;"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(