Skip to content

Commit ca2fa12

Browse files
committed
Support deploy syscall
1 parent 2e845a1 commit ca2fa12

File tree

2 files changed

+184
-11
lines changed

2 files changed

+184
-11
lines changed

crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/cheated_syscalls.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,7 @@ pub fn deploy_syscall(
8585
request.constructor_calldata.0.len(),
8686
);
8787

88-
// region: Modified blockifier code
89-
let deployer_address = syscall_handler.storage_address();
90-
// endregion
88+
let deployer_address = syscall_handler.base.call.storage_address;
9189
let deployer_address_for_calculation = if request.deploy_from_zero {
9290
ContractAddress::default()
9391
} else {

crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs

Lines changed: 183 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::
22
use crate::state::CheatnetState;
33
use blockifier::execution::call_info::{CallInfo, Retdata};
44
use blockifier::execution::common_hints::ExecutionMode;
5-
use blockifier::execution::entry_point::{CallEntryPoint, CallType};
6-
use blockifier::execution::errors::EntryPointExecutionError;
5+
use blockifier::execution::entry_point::{
6+
CallEntryPoint, CallType, ConstructorContext, ConstructorEntryPointExecutionResult,
7+
EntryPointExecutionContext, handle_empty_constructor,
8+
};
9+
use blockifier::execution::errors::{
10+
ConstructorEntryPointExecutionError, EntryPointExecutionError,
11+
};
712
use blockifier::execution::execution_utils::update_remaining_gas;
813
use blockifier::execution::native::syscall_handler::NativeSyscallHandler;
914
use blockifier::execution::syscalls::hint_processor::{
@@ -13,15 +18,20 @@ use blockifier::execution::syscalls::syscall_base::SyscallHandlerBase;
1318
use blockifier::execution::syscalls::vm_syscall_utils::{
1419
SelfOrRevert, SyscallExecutorBaseError, SyscallSelector, TryExtractRevert,
1520
};
21+
use blockifier::state::errors::StateError;
22+
use blockifier::state::state_api::State;
23+
use blockifier::utils::u64_from_usize;
1624
use cairo_native::starknet::{
1725
BlockInfo, ExecutionInfo, ExecutionInfoV2, ResourceBounds, Secp256k1Point, Secp256r1Point,
1826
StarknetSyscallHandler, SyscallResult, TxV2Info, U256,
1927
};
2028
use num_traits::ToPrimitive;
2129
use starknet_api::contract_class::EntryPointType;
22-
use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector};
30+
use starknet_api::core::{
31+
ClassHash, ContractAddress, EntryPointSelector, calculate_contract_address,
32+
};
2333
use starknet_api::execution_resources::GasAmount;
24-
use starknet_api::transaction::fields::Calldata;
34+
use starknet_api::transaction::fields::{Calldata, ContractAddressSalt};
2535
use starknet_types_core::felt::Felt;
2636
use std::sync::Arc;
2737

@@ -87,6 +97,152 @@ pub fn execute_inner_call(
8797
Ok(raw_retdata)
8898
}
8999

100+
#[allow(clippy::result_large_err)]
101+
pub fn execute_constructor_entry_point(
102+
state: &mut dyn State,
103+
cheatnet_state: &mut CheatnetState,
104+
context: &mut EntryPointExecutionContext,
105+
ctor_context: ConstructorContext,
106+
calldata: Calldata,
107+
remaining_gas: &mut u64,
108+
) -> ConstructorEntryPointExecutionResult<CallInfo> {
109+
// Ensure the class is declared (by reading it).
110+
let compiled_class = state
111+
.get_compiled_class(ctor_context.class_hash)
112+
.map_err(|error| {
113+
ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None)
114+
})?;
115+
let Some(constructor_selector) = compiled_class.constructor_selector() else {
116+
// Contract has no constructor.
117+
return handle_empty_constructor(
118+
compiled_class,
119+
context,
120+
&ctor_context,
121+
calldata,
122+
*remaining_gas,
123+
)
124+
.map_err(|error| ConstructorEntryPointExecutionError::new(error, &ctor_context, None));
125+
};
126+
127+
let mut constructor_call = CallEntryPoint {
128+
class_hash: None,
129+
code_address: ctor_context.code_address,
130+
entry_point_type: EntryPointType::Constructor,
131+
entry_point_selector: constructor_selector,
132+
calldata,
133+
storage_address: ctor_context.storage_address,
134+
caller_address: ctor_context.caller_address,
135+
call_type: CallType::Call,
136+
initial_gas: *remaining_gas,
137+
};
138+
139+
let call_info =
140+
execute_call_entry_point(&mut constructor_call, state, cheatnet_state, context, false)
141+
.map_err(|error| {
142+
ConstructorEntryPointExecutionError::new(
143+
error,
144+
&ctor_context,
145+
Some(constructor_selector),
146+
)
147+
})?;
148+
149+
Ok(call_info)
150+
}
151+
152+
pub fn execute_deployment(
153+
state: &mut dyn State,
154+
cheatnet_state: &mut CheatnetState,
155+
context: &mut EntryPointExecutionContext,
156+
ctor_context: ConstructorContext,
157+
constructor_calldata: Calldata,
158+
remaining_gas: &mut u64,
159+
) -> ConstructorEntryPointExecutionResult<CallInfo> {
160+
// Address allocation in the state is done before calling the constructor, so that it is
161+
// visible from it.
162+
let deployed_contract_address = ctor_context.storage_address;
163+
let current_class_hash =
164+
state
165+
.get_class_hash_at(deployed_contract_address)
166+
.map_err(|error| {
167+
ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None)
168+
})?;
169+
if current_class_hash != ClassHash::default() {
170+
return Err(ConstructorEntryPointExecutionError::new(
171+
StateError::UnavailableContractAddress(deployed_contract_address).into(),
172+
&ctor_context,
173+
None,
174+
));
175+
}
176+
177+
state
178+
.set_class_hash_at(deployed_contract_address, ctor_context.class_hash)
179+
.map_err(|error| {
180+
ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None)
181+
})?;
182+
183+
execute_constructor_entry_point(
184+
state,
185+
cheatnet_state,
186+
context,
187+
ctor_context,
188+
constructor_calldata,
189+
remaining_gas,
190+
)
191+
}
192+
193+
fn deploy(
194+
syscall_handler_base: &mut SyscallHandlerBase,
195+
cheatnet_state: &mut CheatnetState,
196+
class_hash: ClassHash,
197+
contract_address_salt: ContractAddressSalt,
198+
constructor_calldata: Calldata,
199+
deploy_from_zero: bool,
200+
remaining_gas: &mut u64,
201+
) -> BaseSyscallResult<(ContractAddress, CallInfo)> {
202+
syscall_handler_base
203+
.increment_syscall_linear_factor_by(&SyscallSelector::Deploy, constructor_calldata.0.len());
204+
// let versioned_constants = &syscall_handler_base
205+
// .context
206+
// .tx_context
207+
// .block_context
208+
// .versioned_constants;
209+
// TODO support for reject
210+
// if should_reject_deploy(
211+
// versioned_constants.disable_deploy_in_validation_mode,
212+
// syscall_handler_base.context.execution_mode,
213+
// ) {
214+
// syscall_handler_base.reject_syscall_in_validate_mode("deploy")?;
215+
// }
216+
217+
let deployer_address = syscall_handler_base.call.storage_address;
218+
let deployer_address_for_calculation = match deploy_from_zero {
219+
true => ContractAddress::default(),
220+
false => deployer_address,
221+
};
222+
let deployed_contract_address = calculate_contract_address(
223+
contract_address_salt,
224+
class_hash,
225+
&constructor_calldata,
226+
deployer_address_for_calculation,
227+
)?;
228+
229+
let ctor_context = ConstructorContext {
230+
class_hash,
231+
code_address: Some(deployed_contract_address),
232+
storage_address: deployed_contract_address,
233+
caller_address: deployer_address,
234+
};
235+
let call_info = execute_deployment(
236+
syscall_handler_base.state,
237+
cheatnet_state,
238+
syscall_handler_base.context,
239+
ctor_context,
240+
constructor_calldata,
241+
remaining_gas,
242+
)?;
243+
Ok((deployed_contract_address, call_info))
244+
}
245+
90246
impl CheatableNativeSyscallHandler<'_> {
91247
// TODO consider modifying this so it doesn't use take
92248
pub fn unrecoverable_error(&mut self) -> Option<SyscallExecutionError> {
@@ -378,13 +534,32 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> {
378534
deploy_from_zero: bool,
379535
remaining_gas: &mut u64,
380536
) -> SyscallResult<(Felt, Vec<Felt>)> {
381-
self.native_syscall_handler.deploy(
382-
class_hash,
383-
contract_address_salt,
384-
calldata,
537+
// The cost of deploying a contract is the base cost plus the linear cost of the calldata
538+
// len.
539+
let total_gas_cost = self
540+
.native_syscall_handler
541+
.gas_costs()
542+
.syscalls
543+
.deploy
544+
.get_syscall_cost(u64_from_usize(calldata.len()));
545+
546+
self.pre_execute_syscall(remaining_gas, total_gas_cost, SyscallSelector::Deploy)?;
547+
548+
let (deployed_contract_address, call_info) = deploy(
549+
&mut self.native_syscall_handler.base,
550+
&mut self.cheatnet_state,
551+
ClassHash(class_hash),
552+
ContractAddressSalt(contract_address_salt),
553+
Calldata(Arc::new(calldata.to_vec())),
385554
deploy_from_zero,
386555
remaining_gas,
387556
)
557+
.map_err(|err| self.handle_error(remaining_gas, err))?;
558+
559+
let constructor_retdata = call_info.execution.retdata.0[..].to_vec();
560+
self.native_syscall_handler.base.inner_calls.push(call_info);
561+
562+
Ok((Felt::from(deployed_contract_address), constructor_retdata))
388563
}
389564

390565
fn replace_class(&mut self, class_hash: Felt, remaining_gas: &mut u64) -> SyscallResult<()> {

0 commit comments

Comments
 (0)