1
+ use crate :: runtime_extensions:: call_to_blockifier_runtime_extension:: execution:: entry_point:: execute_call_entry_point;
1
2
use crate :: state:: CheatnetState ;
3
+ use blockifier:: execution:: call_info:: { CallInfo , Retdata } ;
2
4
use blockifier:: execution:: common_hints:: ExecutionMode ;
3
5
use blockifier:: execution:: entry_point:: { CallEntryPoint , CallType } ;
6
+ use blockifier:: execution:: errors:: EntryPointExecutionError ;
7
+ use blockifier:: execution:: execution_utils:: update_remaining_gas;
4
8
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 ;
6
13
use blockifier:: execution:: syscalls:: vm_syscall_utils:: {
7
- SyscallExecutorBaseError , SyscallSelector ,
14
+ SelfOrRevert , SyscallExecutorBaseError , SyscallSelector , TryExtractRevert ,
8
15
} ;
9
16
use cairo_native:: starknet:: {
10
17
BlockInfo , ExecutionInfo , ExecutionInfoV2 , ResourceBounds , Secp256k1Point , Secp256r1Point ,
@@ -23,6 +30,63 @@ pub struct CheatableNativeSyscallHandler<'a> {
23
30
pub cheatnet_state : & ' a mut CheatnetState ,
24
31
}
25
32
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
+
26
90
impl CheatableNativeSyscallHandler < ' _ > {
27
91
// TODO consider modifying this so it doesn't use take
28
92
pub fn unrecoverable_error ( & mut self ) -> Option < SyscallExecutionError > {
@@ -88,6 +152,73 @@ impl CheatableNativeSyscallHandler<'_> {
88
152
89
153
Ok ( ( ) )
90
154
}
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
+ }
91
222
}
92
223
93
224
impl StarknetSyscallHandler for & mut CheatableNativeSyscallHandler < ' _ > {
@@ -283,12 +414,72 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> {
283
414
calldata : & [ Felt ] ,
284
415
remaining_gas : & mut u64 ,
285
416
) -> SyscallResult < Vec < Felt > > {
286
- self . native_syscall_handler . call_contract (
287
- address,
288
- entry_point_selector,
289
- calldata,
417
+ self . pre_execute_syscall (
290
418
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 )
292
483
}
293
484
294
485
fn storage_read (
0 commit comments