Skip to content

Commit 95d6326

Browse files
committed
TODO initial support for call_contract
1 parent 7a82606 commit 95d6326

File tree

1 file changed

+198
-7
lines changed

1 file changed

+198
-7
lines changed

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

Lines changed: 198 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1+
use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::execute_call_entry_point;
12
use crate::state::CheatnetState;
3+
use blockifier::execution::call_info::{CallInfo, Retdata};
24
use blockifier::execution::common_hints::ExecutionMode;
35
use blockifier::execution::entry_point::{CallEntryPoint, CallType};
6+
use blockifier::execution::errors::EntryPointExecutionError;
7+
use blockifier::execution::execution_utils::update_remaining_gas;
48
use blockifier::execution::native::syscall_handler::NativeSyscallHandler;
5-
use blockifier::execution::syscalls::hint_processor::{OUT_OF_GAS_ERROR, SyscallExecutionError};
9+
use blockifier::execution::syscalls::hint_processor::{
10+
ENTRYPOINT_FAILED_ERROR, OUT_OF_GAS_ERROR, SyscallExecutionError,
11+
};
12+
use blockifier::execution::syscalls::syscall_base::SyscallHandlerBase;
613
use blockifier::execution::syscalls::vm_syscall_utils::{
7-
SyscallExecutorBaseError, SyscallSelector,
14+
SelfOrRevert, SyscallExecutorBaseError, SyscallSelector, TryExtractRevert,
815
};
916
use cairo_native::starknet::{
1017
BlockInfo, ExecutionInfo, ExecutionInfoV2, ResourceBounds, Secp256k1Point, Secp256r1Point,
@@ -23,6 +30,63 @@ pub struct CheatableNativeSyscallHandler<'a> {
2330
pub cheatnet_state: &'a mut CheatnetState,
2431
}
2532

33+
pub type BaseSyscallResult<T> = Result<T, SyscallExecutionError>;
34+
35+
#[allow(clippy::result_large_err)]
36+
pub fn execute_inner_call(
37+
syscall_handler_base: &mut SyscallHandlerBase,
38+
cheatnet_state: &mut CheatnetState,
39+
call: &mut CallEntryPoint,
40+
remaining_gas: &mut u64,
41+
) -> BaseSyscallResult<Vec<Felt>> {
42+
let revert_idx = syscall_handler_base.context.revert_infos.0.len();
43+
44+
// region: Modified blockifier code
45+
let call_info = execute_call_entry_point(
46+
call,
47+
syscall_handler_base.state,
48+
cheatnet_state,
49+
syscall_handler_base.context,
50+
true,
51+
)?;
52+
// TODO not sure if to keep it
53+
update_remaining_gas(remaining_gas, &call_info);
54+
// endregion
55+
56+
let mut raw_retdata = call_info.execution.retdata.0.clone();
57+
let failed = call_info.execution.failed;
58+
syscall_handler_base.inner_calls.push(call_info);
59+
if failed {
60+
syscall_handler_base
61+
.context
62+
.revert(revert_idx, syscall_handler_base.state)?;
63+
64+
// Delete events and l2_to_l1_messages from the reverted call.
65+
let reverted_call = &mut syscall_handler_base.inner_calls.last_mut().unwrap();
66+
let mut stack: Vec<&mut CallInfo> = vec![reverted_call];
67+
while let Some(call_info) = stack.pop() {
68+
call_info.execution.events.clear();
69+
call_info.execution.l2_to_l1_messages.clear();
70+
// Add inner calls that did not fail to the stack.
71+
// The events and l2_to_l1_messages of the failed calls were already cleared.
72+
stack.extend(
73+
call_info
74+
.inner_calls
75+
.iter_mut()
76+
.filter(|call_info| !call_info.execution.failed),
77+
);
78+
}
79+
80+
raw_retdata
81+
.push(Felt::from_hex(ENTRYPOINT_FAILED_ERROR).map_err(SyscallExecutionError::from)?);
82+
return Err(SyscallExecutionError::Revert {
83+
error_data: raw_retdata,
84+
});
85+
}
86+
87+
Ok(raw_retdata)
88+
}
89+
2690
impl CheatableNativeSyscallHandler<'_> {
2791
// TODO consider modifying this so it doesn't use take
2892
pub fn unrecoverable_error(&mut self) -> Option<SyscallExecutionError> {
@@ -88,6 +152,73 @@ impl CheatableNativeSyscallHandler<'_> {
88152

89153
Ok(())
90154
}
155+
156+
#[allow(clippy::result_large_err)]
157+
fn execute_inner_call(
158+
&mut self,
159+
entry_point: &mut CallEntryPoint,
160+
remaining_gas: &mut u64,
161+
class_hash: ClassHash,
162+
error_wrapper_fn: impl Fn(
163+
SyscallExecutionError,
164+
ClassHash,
165+
ContractAddress,
166+
EntryPointSelector,
167+
) -> SyscallExecutionError,
168+
) -> SyscallResult<Retdata> {
169+
let entry_point_clone = entry_point.clone();
170+
let raw_data = execute_inner_call(
171+
&mut self.native_syscall_handler.base,
172+
&mut self.cheatnet_state,
173+
entry_point,
174+
remaining_gas,
175+
)
176+
.map_err(|e| {
177+
self.handle_error(
178+
remaining_gas,
179+
SyscallExecutionError::from_self_or_revert(e.try_extract_revert().map_original(
180+
|error| {
181+
error_wrapper_fn(
182+
error,
183+
class_hash,
184+
entry_point_clone.storage_address,
185+
entry_point_clone.entry_point_selector,
186+
)
187+
},
188+
)),
189+
)
190+
})?;
191+
Ok(Retdata(raw_data))
192+
}
193+
194+
fn handle_error(&mut self, remaining_gas: &mut u64, error: SyscallExecutionError) -> Vec<Felt> {
195+
// In case of more than one inner call and because each inner call has their own
196+
// syscall handler, if there is an unrecoverable error at call `n` it will create a
197+
// `NativeExecutionError`. When rolling back, each call from `n-1` to `1` will also
198+
// store the result of a previous `NativeExecutionError` in a `NativeExecutionError`
199+
// creating multiple wraps around the same error. This function is meant to prevent that.
200+
fn unwrap_native_error(error: SyscallExecutionError) -> SyscallExecutionError {
201+
match error {
202+
SyscallExecutionError::EntryPointExecutionError(
203+
EntryPointExecutionError::NativeUnrecoverableError(e),
204+
) => *e,
205+
_ => error,
206+
}
207+
}
208+
209+
match error.try_extract_revert() {
210+
SelfOrRevert::Revert(revert_error) => revert_error.error_data,
211+
SelfOrRevert::Original(error) => {
212+
assert!(
213+
self.native_syscall_handler.unrecoverable_error.is_none(),
214+
"Trying to set an unrecoverable error twice in Native Syscall Handler"
215+
);
216+
self.native_syscall_handler.unrecoverable_error = Some(unwrap_native_error(error));
217+
*remaining_gas = 0;
218+
vec![]
219+
}
220+
}
221+
}
91222
}
92223

93224
impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> {
@@ -283,12 +414,72 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> {
283414
calldata: &[Felt],
284415
remaining_gas: &mut u64,
285416
) -> SyscallResult<Vec<Felt>> {
286-
self.native_syscall_handler.call_contract(
287-
address,
288-
entry_point_selector,
289-
calldata,
417+
self.pre_execute_syscall(
290418
remaining_gas,
291-
)
419+
self.native_syscall_handler
420+
.gas_costs()
421+
.syscalls
422+
.call_contract
423+
.base_syscall_cost(),
424+
SyscallSelector::CallContract,
425+
)?;
426+
427+
let contract_address = ContractAddress::try_from(address)
428+
.map_err(|error| self.handle_error(remaining_gas, error.into()))?;
429+
430+
let class_hash = self
431+
.native_syscall_handler
432+
.base
433+
.state
434+
.get_class_hash_at(contract_address)
435+
.map_err(|e| self.handle_error(remaining_gas, e.into()))?;
436+
if self.native_syscall_handler.base.context.execution_mode == ExecutionMode::Validate
437+
&& self.native_syscall_handler.base.call.storage_address != contract_address
438+
{
439+
let err = SyscallExecutorBaseError::InvalidSyscallInExecutionMode {
440+
syscall_name: "call_contract".to_string(),
441+
execution_mode: self.native_syscall_handler.base.context.execution_mode,
442+
};
443+
return Err(self.handle_error(remaining_gas, err.into()));
444+
}
445+
let selector = EntryPointSelector(entry_point_selector);
446+
// TODO restore blocking
447+
// self
448+
// .native_syscall_handler
449+
// .base
450+
// .maybe_block_direct_execute_call(selector)
451+
// .map_err(|e| self.handle_error(remaining_gas, e))?;
452+
453+
let wrapper_calldata = Calldata(Arc::new(calldata.to_vec()));
454+
455+
let mut entry_point = CallEntryPoint {
456+
class_hash: None,
457+
code_address: Some(contract_address),
458+
entry_point_type: EntryPointType::External,
459+
entry_point_selector: selector,
460+
calldata: wrapper_calldata,
461+
storage_address: contract_address,
462+
caller_address: self.native_syscall_handler.base.call.storage_address,
463+
call_type: CallType::Call,
464+
initial_gas: *remaining_gas,
465+
};
466+
467+
let error_wrapper_function =
468+
|e: SyscallExecutionError,
469+
class_hash: ClassHash,
470+
storage_address: ContractAddress,
471+
selector: EntryPointSelector| {
472+
e.as_call_contract_execution_error(class_hash, storage_address, selector)
473+
};
474+
475+
Ok(self
476+
.execute_inner_call(
477+
&mut entry_point,
478+
remaining_gas,
479+
class_hash,
480+
error_wrapper_function,
481+
)?
482+
.0)
292483
}
293484

294485
fn storage_read(

0 commit comments

Comments
 (0)