-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(blockifier): add
NativeSyscallHandler
(#1240)
* feat: add native syscall handler * fix: compilation error * refactor: native syscall handler * refactor: move update_remainng_gas out of SyscallHandler impl * fix: restore concurrency in main workflow * refactor: unwrap -> expect in native gas withdraw * chore: restore rebase unintended changes
- Loading branch information
1 parent
5a21cba
commit ac479c5
Showing
4 changed files
with
415 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
pub mod syscall_handler; | ||
pub mod utils; | ||
|
||
#[cfg(test)] | ||
pub mod utils_test; |
328 changes: 328 additions & 0 deletions
328
crates/blockifier/src/execution/native/syscall_handler.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,328 @@ | ||
use std::collections::HashSet; | ||
use std::hash::RandomState; | ||
|
||
use cairo_native::starknet::{ | ||
ExecutionInfo, | ||
ExecutionInfoV2, | ||
Secp256k1Point, | ||
Secp256r1Point, | ||
StarknetSyscallHandler, | ||
SyscallResult, | ||
U256, | ||
}; | ||
use cairo_vm::vm::runners::cairo_runner::ExecutionResources; | ||
use starknet_api::core::{ContractAddress, EntryPointSelector}; | ||
use starknet_api::state::StorageKey; | ||
use starknet_types_core::felt::Felt; | ||
|
||
use crate::execution::call_info::{CallInfo, OrderedEvent, OrderedL2ToL1Message, Retdata}; | ||
use crate::execution::entry_point::{CallEntryPoint, EntryPointExecutionContext}; | ||
use crate::execution::execution_utils::update_remaining_gas; | ||
use crate::execution::native::utils::encode_str_as_felts; | ||
use crate::execution::syscalls::hint_processor::OUT_OF_GAS_ERROR; | ||
use crate::state::state_api::State; | ||
|
||
pub struct NativeSyscallHandler<'state> { | ||
// Input for execution. | ||
pub state: &'state mut dyn State, | ||
pub resources: &'state mut ExecutionResources, | ||
pub context: &'state mut EntryPointExecutionContext, | ||
|
||
// Call information. | ||
pub caller_address: ContractAddress, | ||
pub contract_address: ContractAddress, | ||
pub entry_point_selector: Felt, | ||
|
||
// Execution results. | ||
pub events: Vec<OrderedEvent>, | ||
pub l2_to_l1_messages: Vec<OrderedL2ToL1Message>, | ||
pub inner_calls: Vec<CallInfo>, | ||
|
||
// Additional information gathered during execution. | ||
pub read_values: Vec<Felt>, | ||
pub accessed_keys: HashSet<StorageKey, RandomState>, | ||
} | ||
|
||
impl<'state> NativeSyscallHandler<'state> { | ||
pub fn new( | ||
state: &'state mut dyn State, | ||
caller_address: ContractAddress, | ||
contract_address: ContractAddress, | ||
entry_point_selector: EntryPointSelector, | ||
resources: &'state mut ExecutionResources, | ||
context: &'state mut EntryPointExecutionContext, | ||
) -> NativeSyscallHandler<'state> { | ||
NativeSyscallHandler { | ||
state, | ||
caller_address, | ||
contract_address, | ||
entry_point_selector: entry_point_selector.0, | ||
resources, | ||
context, | ||
events: Vec::new(), | ||
l2_to_l1_messages: Vec::new(), | ||
inner_calls: Vec::new(), | ||
read_values: Vec::new(), | ||
accessed_keys: HashSet::new(), | ||
} | ||
} | ||
|
||
#[allow(dead_code)] | ||
fn execute_inner_call( | ||
&mut self, | ||
entry_point: CallEntryPoint, | ||
remaining_gas: &mut u128, | ||
) -> SyscallResult<Retdata> { | ||
let call_info = entry_point | ||
.execute(self.state, self.resources, self.context) | ||
.map_err(|e| encode_str_as_felts(&e.to_string()))?; | ||
let retdata = call_info.execution.retdata.clone(); | ||
|
||
if call_info.execution.failed { | ||
// In VM it's wrapped into `SyscallExecutionError::SyscallError`. | ||
return Err(retdata.0.clone()); | ||
} | ||
|
||
native_update_remaining_gas(remaining_gas, &call_info); | ||
|
||
self.inner_calls.push(call_info); | ||
|
||
Ok(retdata) | ||
} | ||
|
||
// Handles gas related logic when executing a syscall. Required because Native calls the | ||
// syscalls directly unlike the VM where the `execute_syscall` method perform this operation | ||
// first. | ||
#[allow(dead_code)] | ||
fn substract_syscall_gas_cost( | ||
&mut self, | ||
remaining_gas: &mut u128, | ||
syscall_gas_cost: u64, | ||
) -> SyscallResult<()> { | ||
// Refund `SYSCALL_BASE_GAS_COST` as it was pre-charged. | ||
let required_gas = | ||
u128::from(syscall_gas_cost - self.context.gas_costs().syscall_base_gas_cost); | ||
|
||
if *remaining_gas < required_gas { | ||
// Out of gas failure. | ||
return Err(vec![ | ||
Felt::from_hex(OUT_OF_GAS_ERROR) | ||
.expect("Failed to parse OUT_OF_GAS_ERROR hex string"), | ||
]); | ||
} | ||
|
||
*remaining_gas -= required_gas; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl<'state> StarknetSyscallHandler for &mut NativeSyscallHandler<'state> { | ||
fn get_block_hash( | ||
&mut self, | ||
_block_number: u64, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<Felt> { | ||
todo!("Implement get_block_hash syscall."); | ||
} | ||
|
||
fn get_execution_info(&mut self, _remaining_gas: &mut u128) -> SyscallResult<ExecutionInfo> { | ||
todo!("Implement get_execution_info syscall."); | ||
} | ||
|
||
fn get_execution_info_v2( | ||
&mut self, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<ExecutionInfoV2> { | ||
todo!("Implement get_execution_info_v2 syscall."); | ||
} | ||
|
||
fn deploy( | ||
&mut self, | ||
_class_hash: Felt, | ||
_contract_address_salt: Felt, | ||
_calldata: &[Felt], | ||
_deploy_from_zero: bool, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<(Felt, Vec<Felt>)> { | ||
todo!("Implement deploy syscall."); | ||
} | ||
|
||
fn replace_class(&mut self, _class_hash: Felt, _remaining_gas: &mut u128) -> SyscallResult<()> { | ||
todo!("Implement replace_class syscall."); | ||
} | ||
|
||
fn library_call( | ||
&mut self, | ||
_class_hash: Felt, | ||
_function_selector: Felt, | ||
_calldata: &[Felt], | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<Vec<Felt>> { | ||
todo!("Implement library_call syscall."); | ||
} | ||
|
||
fn call_contract( | ||
&mut self, | ||
_address: Felt, | ||
_entry_point_selector: Felt, | ||
_calldata: &[Felt], | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<Vec<Felt>> { | ||
todo!("Implement call_contract syscall."); | ||
} | ||
|
||
fn storage_read( | ||
&mut self, | ||
_address_domain: u32, | ||
_address: Felt, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<Felt> { | ||
todo!("Implement storage_read syscall."); | ||
} | ||
|
||
fn storage_write( | ||
&mut self, | ||
_address_domain: u32, | ||
_address: Felt, | ||
_value: Felt, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<()> { | ||
todo!("Implement storage_write syscall."); | ||
} | ||
|
||
fn emit_event( | ||
&mut self, | ||
_keys: &[Felt], | ||
_data: &[Felt], | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<()> { | ||
todo!("Implement emit_event syscall."); | ||
} | ||
|
||
fn send_message_to_l1( | ||
&mut self, | ||
_to_address: Felt, | ||
_payload: &[Felt], | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<()> { | ||
todo!("Implement send_message_to_l1 syscall."); | ||
} | ||
|
||
fn keccak(&mut self, _input: &[u64], _remaining_gas: &mut u128) -> SyscallResult<U256> { | ||
todo!("Implement keccak syscall."); | ||
} | ||
|
||
fn secp256k1_new( | ||
&mut self, | ||
_x: U256, | ||
_y: U256, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<Option<Secp256k1Point>> { | ||
todo!("Implement secp256k1_new syscall."); | ||
} | ||
|
||
fn secp256k1_add( | ||
&mut self, | ||
_p0: Secp256k1Point, | ||
_p1: Secp256k1Point, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<Secp256k1Point> { | ||
todo!("Implement secp256k1_add syscall."); | ||
} | ||
|
||
fn secp256k1_mul( | ||
&mut self, | ||
_p: Secp256k1Point, | ||
_m: U256, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<Secp256k1Point> { | ||
todo!("Implement secp256k1_mul syscall."); | ||
} | ||
|
||
fn secp256k1_get_point_from_x( | ||
&mut self, | ||
_x: U256, | ||
_y_parity: bool, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<Option<Secp256k1Point>> { | ||
todo!("Implement secp256k1_get_point_from_x syscall."); | ||
} | ||
|
||
fn secp256k1_get_xy( | ||
&mut self, | ||
_p: Secp256k1Point, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<(U256, U256)> { | ||
todo!("Implement secp256k1_get_xy syscall."); | ||
} | ||
|
||
fn secp256r1_new( | ||
&mut self, | ||
_x: U256, | ||
_y: U256, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<Option<Secp256r1Point>> { | ||
todo!("Implement secp256r1_new syscall."); | ||
} | ||
|
||
fn secp256r1_add( | ||
&mut self, | ||
_p0: Secp256r1Point, | ||
_p1: Secp256r1Point, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<Secp256r1Point> { | ||
todo!("Implement secp256r1_add syscall."); | ||
} | ||
|
||
fn secp256r1_mul( | ||
&mut self, | ||
_p: Secp256r1Point, | ||
_m: U256, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<Secp256r1Point> { | ||
todo!("Implement secp256r1_mul syscall."); | ||
} | ||
|
||
fn secp256r1_get_point_from_x( | ||
&mut self, | ||
_x: U256, | ||
_y_parity: bool, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<Option<Secp256r1Point>> { | ||
todo!("Implement secp256r1_get_point_from_x syscall."); | ||
} | ||
|
||
fn secp256r1_get_xy( | ||
&mut self, | ||
_p: Secp256r1Point, | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<(U256, U256)> { | ||
todo!("Implement secp256r1_get_xy syscall."); | ||
} | ||
|
||
fn sha256_process_block( | ||
&mut self, | ||
_prev_state: &[u32; 8], | ||
_current_block: &[u32; 16], | ||
_remaining_gas: &mut u128, | ||
) -> SyscallResult<[u32; 8]> { | ||
todo!("Implement sha256_process_block syscall."); | ||
} | ||
} | ||
|
||
/// Wrapper function around [update_remaining_gas] which takes a u128 as an input, converts it to | ||
/// u64 and uses [update_remaining_gas] to withdraw the right amount. Finally, updates the value | ||
/// to which `remaining_gas` points to. | ||
#[allow(dead_code)] | ||
fn native_update_remaining_gas(remaining_gas: &mut u128, call_info: &CallInfo) { | ||
// Create a new variable with converted type. | ||
let mut remaining_gas_u64 = | ||
u64::try_from(*remaining_gas).expect("Failed to convert gas to u64."); | ||
|
||
// Pass the reference to the function. | ||
update_remaining_gas(&mut remaining_gas_u64, call_info); | ||
|
||
// Change the remaining gas value. | ||
*remaining_gas = u128::from(remaining_gas_u64); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.