Skip to content

Commit 72107e8

Browse files
Merge pull request #5814 from starkware-libs/aner/global_execute_syscall_fn
refactor(blockifier): global fn execute_syscall_from_selector
2 parents 3c01a27 + ec851fb commit 72107e8

2 files changed

Lines changed: 176 additions & 169 deletions

File tree

crates/blockifier/src/execution/syscalls/hint_processor.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@ use crate::execution::syscalls::secp::{
6161
SecpNewResponse,
6262
};
6363
use crate::execution::syscalls::syscall_base::SyscallHandlerBase;
64-
use crate::execution::syscalls::syscall_executor::SyscallExecutor;
64+
use crate::execution::syscalls::syscall_executor::{
65+
execute_syscall_from_selector,
66+
SyscallExecutor,
67+
};
6568
use crate::execution::syscalls::{
6669
CallContractRequest,
6770
CallContractResponse,
@@ -338,7 +341,7 @@ impl<'a> SyscallHintProcessor<'a> {
338341
self.increment_syscall_count(&selector);
339342
}
340343

341-
self.execute_syscall_from_selector(vm, selector)
344+
execute_syscall_from_selector(self, vm, selector)
342345
}
343346

344347
pub fn get_or_allocate_execution_info_segment(

crates/blockifier/src/execution/syscalls/syscall_executor.rs

Lines changed: 171 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -57,91 +57,7 @@ use crate::execution::syscalls::{
5757
};
5858
use crate::utils::u64_from_usize;
5959

60-
#[allow(dead_code)]
6160
pub trait SyscallExecutor {
62-
fn execute_syscall_from_selector(
63-
&mut self,
64-
vm: &mut VirtualMachine,
65-
selector: SyscallSelector,
66-
) -> HintExecutionResult {
67-
match selector {
68-
SyscallSelector::CallContract => {
69-
self.execute_syscall(vm, selector, Self::call_contract)
70-
}
71-
SyscallSelector::Deploy => self.execute_syscall(vm, selector, Self::deploy),
72-
SyscallSelector::EmitEvent => self.execute_syscall(vm, selector, Self::emit_event),
73-
SyscallSelector::GetBlockHash => {
74-
self.execute_syscall(vm, selector, Self::get_block_hash)
75-
}
76-
SyscallSelector::GetClassHashAt => {
77-
self.execute_syscall(vm, selector, Self::get_class_hash_at)
78-
}
79-
SyscallSelector::GetExecutionInfo => {
80-
self.execute_syscall(vm, selector, Self::get_execution_info)
81-
}
82-
SyscallSelector::Keccak => self.execute_syscall(vm, selector, Self::keccak),
83-
SyscallSelector::Sha256ProcessBlock => {
84-
self.execute_syscall(vm, selector, Self::sha256_process_block)
85-
}
86-
SyscallSelector::LibraryCall => self.execute_syscall(vm, selector, Self::library_call),
87-
SyscallSelector::MetaTxV0 => self.execute_syscall(vm, selector, Self::meta_tx_v0),
88-
SyscallSelector::ReplaceClass => {
89-
self.execute_syscall(vm, selector, Self::replace_class)
90-
}
91-
SyscallSelector::Secp256k1Add => {
92-
self.execute_syscall(vm, selector, Self::secp256k1_add)
93-
}
94-
SyscallSelector::Secp256k1GetPointFromX => {
95-
self.execute_syscall(vm, selector, Self::secp256k1_get_point_from_x)
96-
}
97-
SyscallSelector::Secp256k1GetXy => {
98-
self.execute_syscall(vm, selector, Self::secp256k1_get_xy)
99-
}
100-
SyscallSelector::Secp256k1Mul => {
101-
self.execute_syscall(vm, selector, Self::secp256k1_mul)
102-
}
103-
SyscallSelector::Secp256k1New => {
104-
self.execute_syscall(vm, selector, Self::secp256k1_new)
105-
}
106-
SyscallSelector::Secp256r1Add => {
107-
self.execute_syscall(vm, selector, Self::secp256r1_add)
108-
}
109-
SyscallSelector::Secp256r1GetPointFromX => {
110-
self.execute_syscall(vm, selector, Self::secp256r1_get_point_from_x)
111-
}
112-
SyscallSelector::Secp256r1GetXy => {
113-
self.execute_syscall(vm, selector, Self::secp256r1_get_xy)
114-
}
115-
SyscallSelector::Secp256r1Mul => {
116-
self.execute_syscall(vm, selector, Self::secp256r1_mul)
117-
}
118-
SyscallSelector::Secp256r1New => {
119-
self.execute_syscall(vm, selector, Self::secp256r1_new)
120-
}
121-
SyscallSelector::SendMessageToL1 => {
122-
self.execute_syscall(vm, selector, Self::send_message_to_l1)
123-
}
124-
SyscallSelector::StorageRead => self.execute_syscall(vm, selector, Self::storage_read),
125-
SyscallSelector::StorageWrite => {
126-
self.execute_syscall(vm, selector, Self::storage_write)
127-
}
128-
// Explicitly list unsupported syscalls, so compiler can catch if a syscall is missing.
129-
SyscallSelector::DelegateCall
130-
| SyscallSelector::DelegateL1Handler
131-
| SyscallSelector::GetBlockNumber
132-
| SyscallSelector::GetBlockTimestamp
133-
| SyscallSelector::GetCallerAddress
134-
| SyscallSelector::GetContractAddress
135-
| SyscallSelector::GetSequencerAddress
136-
| SyscallSelector::GetTxInfo
137-
| SyscallSelector::GetTxSignature
138-
| SyscallSelector::KeccakRound
139-
| SyscallSelector::LibraryCallL1Handler => Err(HintError::UnknownHint(
140-
format!("Unsupported syscall selector {selector:?}.").into(),
141-
)),
142-
}
143-
}
144-
14561
fn get_gas_cost_from_selector(
14662
&self,
14763
selector: &SyscallSelector,
@@ -153,89 +69,6 @@ pub trait SyscallExecutor {
15369

15470
fn update_revert_gas_with_next_remaining_gas(&mut self, next_remaining_gas: GasAmount);
15571

156-
fn execute_syscall<Request, Response, ExecuteCallback>(
157-
&mut self,
158-
vm: &mut VirtualMachine,
159-
selector: SyscallSelector,
160-
execute_callback: ExecuteCallback,
161-
) -> HintExecutionResult
162-
where
163-
Request: SyscallRequest + std::fmt::Debug,
164-
Response: SyscallResponse + std::fmt::Debug,
165-
ExecuteCallback: FnOnce(
166-
Request,
167-
&mut VirtualMachine,
168-
&mut Self,
169-
&mut u64, // Remaining gas.
170-
) -> SyscallResult<Response>,
171-
{
172-
let syscall_gas_cost = self.get_gas_cost_from_selector(&selector).map_err(|e| {
173-
HintError::CustomHint(
174-
format!("Failed to get gas cost for syscall selector {selector:?}. Error: {e:?}")
175-
.into(),
176-
)
177-
})?;
178-
179-
let SyscallRequestWrapper { gas_counter, request } =
180-
SyscallRequestWrapper::<Request>::read(vm, self.get_mut_syscall_ptr())?;
181-
182-
let syscall_gas_cost =
183-
syscall_gas_cost.get_syscall_cost(u64_from_usize(request.get_linear_factor_length()));
184-
let syscall_base_cost = self.get_syscall_base_gas_cost();
185-
186-
// Sanity check for preventing underflow.
187-
assert!(
188-
syscall_gas_cost >= syscall_base_cost,
189-
"Syscall gas cost must be greater than base syscall gas cost"
190-
);
191-
192-
// Refund `SYSCALL_BASE_GAS_COST` as it was pre-charged.
193-
let required_gas = syscall_gas_cost - syscall_base_cost;
194-
195-
if gas_counter < required_gas {
196-
// Out of gas failure.
197-
let out_of_gas_error =
198-
Felt::from_hex(OUT_OF_GAS_ERROR).map_err(SyscallExecutionError::from)?;
199-
let response: SyscallResponseWrapper<Response> =
200-
SyscallResponseWrapper::Failure { gas_counter, error_data: vec![out_of_gas_error] };
201-
response.write(vm, self.get_mut_syscall_ptr())?;
202-
203-
return Ok(());
204-
}
205-
206-
// Execute.
207-
let mut remaining_gas = gas_counter - required_gas;
208-
209-
// To support sierra gas charge for blockifier revert flow, we track the remaining gas left
210-
// before executing a syscall if the current tracked resource is gas.
211-
// 1. If the syscall does not run Cairo code (i.e. not library call, not call contract, and
212-
// not a deploy), any failure will not run in the OS, so no need to charge - the value
213-
// before entering the callback is good enough to charge.
214-
// 2. If the syscall runs Cairo code, but the tracked resource is steps (and not gas), the
215-
// additional charge of reverted cairo steps will cover the inner cost, and the outer
216-
// cost we track here will be the additional reverted gas.
217-
// 3. If the syscall runs Cairo code and the tracked resource is gas, either the inner
218-
// failure will be a Cairo1 revert (and the gas consumed on the call info will override
219-
// the current tracked value), or we will pass through another syscall before failing -
220-
// and by induction (we will reach this point again), the gas will be charged correctly.
221-
self.update_revert_gas_with_next_remaining_gas(GasAmount(remaining_gas));
222-
223-
let original_response = execute_callback(request, vm, self, &mut remaining_gas);
224-
let response = match original_response {
225-
Ok(response) => {
226-
SyscallResponseWrapper::Success { gas_counter: remaining_gas, response }
227-
}
228-
Err(SyscallExecutionError::Revert { error_data: data }) => {
229-
SyscallResponseWrapper::Failure { gas_counter: remaining_gas, error_data: data }
230-
}
231-
Err(error) => return Err(error.into()),
232-
};
233-
234-
response.write(vm, self.get_mut_syscall_ptr())?;
235-
236-
Ok(())
237-
}
238-
23972
fn call_contract(
24073
request: CallContractRequest,
24174
vm: &mut VirtualMachine,
@@ -404,3 +237,174 @@ pub trait SyscallExecutor {
404237
remaining_gas: &mut u64,
405238
) -> SyscallResult<StorageWriteResponse>;
406239
}
240+
241+
pub(crate) fn execute_syscall_from_selector<T: SyscallExecutor>(
242+
syscall_executor: &mut T,
243+
vm: &mut VirtualMachine,
244+
selector: SyscallSelector,
245+
) -> HintExecutionResult {
246+
match selector {
247+
SyscallSelector::CallContract => {
248+
execute_syscall(syscall_executor, vm, selector, T::call_contract)
249+
}
250+
SyscallSelector::Deploy => execute_syscall(syscall_executor, vm, selector, T::deploy),
251+
SyscallSelector::EmitEvent => {
252+
execute_syscall(syscall_executor, vm, selector, T::emit_event)
253+
}
254+
SyscallSelector::GetBlockHash => {
255+
execute_syscall(syscall_executor, vm, selector, T::get_block_hash)
256+
}
257+
SyscallSelector::GetClassHashAt => {
258+
execute_syscall(syscall_executor, vm, selector, T::get_class_hash_at)
259+
}
260+
SyscallSelector::GetExecutionInfo => {
261+
execute_syscall(syscall_executor, vm, selector, T::get_execution_info)
262+
}
263+
SyscallSelector::Keccak => execute_syscall(syscall_executor, vm, selector, T::keccak),
264+
SyscallSelector::Sha256ProcessBlock => {
265+
execute_syscall(syscall_executor, vm, selector, T::sha256_process_block)
266+
}
267+
SyscallSelector::LibraryCall => {
268+
execute_syscall(syscall_executor, vm, selector, T::library_call)
269+
}
270+
SyscallSelector::MetaTxV0 => execute_syscall(syscall_executor, vm, selector, T::meta_tx_v0),
271+
SyscallSelector::ReplaceClass => {
272+
execute_syscall(syscall_executor, vm, selector, T::replace_class)
273+
}
274+
SyscallSelector::Secp256k1Add => {
275+
execute_syscall(syscall_executor, vm, selector, T::secp256k1_add)
276+
}
277+
SyscallSelector::Secp256k1GetPointFromX => {
278+
execute_syscall(syscall_executor, vm, selector, T::secp256k1_get_point_from_x)
279+
}
280+
SyscallSelector::Secp256k1GetXy => {
281+
execute_syscall(syscall_executor, vm, selector, T::secp256k1_get_xy)
282+
}
283+
SyscallSelector::Secp256k1Mul => {
284+
execute_syscall(syscall_executor, vm, selector, T::secp256k1_mul)
285+
}
286+
SyscallSelector::Secp256k1New => {
287+
execute_syscall(syscall_executor, vm, selector, T::secp256k1_new)
288+
}
289+
SyscallSelector::Secp256r1Add => {
290+
execute_syscall(syscall_executor, vm, selector, T::secp256r1_add)
291+
}
292+
SyscallSelector::Secp256r1GetPointFromX => {
293+
execute_syscall(syscall_executor, vm, selector, T::secp256r1_get_point_from_x)
294+
}
295+
SyscallSelector::Secp256r1GetXy => {
296+
execute_syscall(syscall_executor, vm, selector, T::secp256r1_get_xy)
297+
}
298+
SyscallSelector::Secp256r1Mul => {
299+
execute_syscall(syscall_executor, vm, selector, T::secp256r1_mul)
300+
}
301+
SyscallSelector::Secp256r1New => {
302+
execute_syscall(syscall_executor, vm, selector, T::secp256r1_new)
303+
}
304+
SyscallSelector::SendMessageToL1 => {
305+
execute_syscall(syscall_executor, vm, selector, T::send_message_to_l1)
306+
}
307+
SyscallSelector::StorageRead => {
308+
execute_syscall(syscall_executor, vm, selector, T::storage_read)
309+
}
310+
SyscallSelector::StorageWrite => {
311+
execute_syscall(syscall_executor, vm, selector, T::storage_write)
312+
}
313+
// Explicitly list unsupported syscalls, so compiler can catch if a syscall is missing.
314+
SyscallSelector::DelegateCall
315+
| SyscallSelector::DelegateL1Handler
316+
| SyscallSelector::GetBlockNumber
317+
| SyscallSelector::GetBlockTimestamp
318+
| SyscallSelector::GetCallerAddress
319+
| SyscallSelector::GetContractAddress
320+
| SyscallSelector::GetSequencerAddress
321+
| SyscallSelector::GetTxInfo
322+
| SyscallSelector::GetTxSignature
323+
| SyscallSelector::KeccakRound
324+
| SyscallSelector::LibraryCallL1Handler => Err(HintError::UnknownHint(
325+
format!("Unsupported syscall selector {selector:?}.").into(),
326+
)),
327+
}
328+
}
329+
330+
fn execute_syscall<Request, Response, ExecuteCallback, Executor>(
331+
syscall_executor: &mut Executor,
332+
vm: &mut VirtualMachine,
333+
selector: SyscallSelector,
334+
execute_callback: ExecuteCallback,
335+
) -> HintExecutionResult
336+
where
337+
Executor: SyscallExecutor,
338+
Request: SyscallRequest + std::fmt::Debug,
339+
Response: SyscallResponse + std::fmt::Debug,
340+
ExecuteCallback: FnOnce(
341+
Request,
342+
&mut VirtualMachine,
343+
&mut Executor,
344+
&mut u64, // Remaining gas.
345+
) -> SyscallResult<Response>,
346+
{
347+
let syscall_gas_cost = syscall_executor.get_gas_cost_from_selector(&selector).map_err(|e| {
348+
HintError::CustomHint(
349+
format!("Failed to get gas cost for syscall selector {selector:?}. Error: {e:?}")
350+
.into(),
351+
)
352+
})?;
353+
354+
let SyscallRequestWrapper { gas_counter, request } =
355+
SyscallRequestWrapper::<Request>::read(vm, syscall_executor.get_mut_syscall_ptr())?;
356+
357+
let syscall_gas_cost =
358+
syscall_gas_cost.get_syscall_cost(u64_from_usize(request.get_linear_factor_length()));
359+
let syscall_base_cost = syscall_executor.get_syscall_base_gas_cost();
360+
361+
// Sanity check for preventing underflow.
362+
assert!(
363+
syscall_gas_cost >= syscall_base_cost,
364+
"Syscall gas cost must be greater than base syscall gas cost"
365+
);
366+
367+
// Refund `SYSCALL_BASE_GAS_COST` as it was pre-charged.
368+
let required_gas = syscall_gas_cost - syscall_base_cost;
369+
370+
if gas_counter < required_gas {
371+
// Out of gas failure.
372+
let out_of_gas_error =
373+
Felt::from_hex(OUT_OF_GAS_ERROR).map_err(SyscallExecutionError::from)?;
374+
let response: SyscallResponseWrapper<Response> =
375+
SyscallResponseWrapper::Failure { gas_counter, error_data: vec![out_of_gas_error] };
376+
response.write(vm, syscall_executor.get_mut_syscall_ptr())?;
377+
378+
return Ok(());
379+
}
380+
381+
// Execute.
382+
let mut remaining_gas = gas_counter - required_gas;
383+
384+
// To support sierra gas charge for blockifier revert flow, we track the remaining gas left
385+
// before executing a syscall if the current tracked resource is gas.
386+
// 1. If the syscall does not run Cairo code (i.e. not library call, not call contract, and not
387+
// a deploy), any failure will not run in the OS, so no need to charge - the value before
388+
// entering the callback is good enough to charge.
389+
// 2. If the syscall runs Cairo code, but the tracked resource is steps (and not gas), the
390+
// additional charge of reverted cairo steps will cover the inner cost, and the outer cost we
391+
// track here will be the additional reverted gas.
392+
// 3. If the syscall runs Cairo code and the tracked resource is gas, either the inner failure
393+
// will be a Cairo1 revert (and the gas consumed on the call info will override the current
394+
// tracked value), or we will pass through another syscall before failing - and by induction
395+
// (we will reach this point again), the gas will be charged correctly.
396+
syscall_executor.update_revert_gas_with_next_remaining_gas(GasAmount(remaining_gas));
397+
398+
let original_response = execute_callback(request, vm, syscall_executor, &mut remaining_gas);
399+
let response = match original_response {
400+
Ok(response) => SyscallResponseWrapper::Success { gas_counter: remaining_gas, response },
401+
Err(SyscallExecutionError::Revert { error_data: data }) => {
402+
SyscallResponseWrapper::Failure { gas_counter: remaining_gas, error_data: data }
403+
}
404+
Err(error) => return Err(error.into()),
405+
};
406+
407+
response.write(vm, syscall_executor.get_mut_syscall_ptr())?;
408+
409+
Ok(())
410+
}

0 commit comments

Comments
 (0)