-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
398 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,2 @@ | ||
pub mod syscall_handler; | ||
pub mod utils; |
318 changes: 318 additions & 0 deletions
318
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,318 @@ | ||
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}; | ||
use crate::execution::entry_point::{CallEntryPoint, EntryPointExecutionContext}; | ||
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; | ||
use crate::transaction::transaction_utils::update_remaining_gas; | ||
|
||
pub struct NativeSyscallHandler<'state> { | ||
// Input for execution. | ||
pub state: &'state mut dyn State, | ||
pub execution_resources: &'state mut ExecutionResources, | ||
pub execution_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 execution result info. | ||
pub storage_read_values: Vec<Felt>, | ||
pub accessed_storage_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, | ||
execution_resources: &'state mut ExecutionResources, | ||
execution_context: &'state mut EntryPointExecutionContext, | ||
) -> NativeSyscallHandler<'state> { | ||
NativeSyscallHandler { | ||
state, | ||
caller_address, | ||
contract_address, | ||
entry_point_selector: entry_point_selector.0, | ||
execution_resources, | ||
execution_context, | ||
events: Vec::new(), | ||
l2_to_l1_messages: Vec::new(), | ||
inner_calls: Vec::new(), | ||
storage_read_values: Vec::new(), | ||
accessed_storage_keys: HashSet::new(), | ||
} | ||
} | ||
|
||
pub fn execute_inner_call( | ||
&mut self, | ||
entry_point: CallEntryPoint, | ||
remaining_gas: &mut u128, | ||
) -> SyscallResult<CallInfo> { | ||
let call_info = entry_point | ||
.execute(self.state, self.execution_resources, self.execution_context) | ||
.map_err(|e| encode_str_as_felts(&e.to_string()))?; | ||
let retdata = call_info.execution.retdata.0.clone(); | ||
|
||
if call_info.execution.failed { | ||
// In VM it's wrapped into `SyscallExecutionError::SyscallError`. | ||
return Err(retdata); | ||
} | ||
|
||
self.update_remaining_gas(remaining_gas, &call_info); | ||
|
||
self.inner_calls.push(call_info.clone()); | ||
|
||
Ok(call_info) | ||
} | ||
|
||
pub fn update_remaining_gas(&mut self, remaining_gas: &mut u128, call_info: &CallInfo) { | ||
// Create a new variable with converted type. | ||
let mut remaining_gas_u64 = u64::try_from(*remaining_gas).unwrap(); | ||
|
||
// 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); | ||
} | ||
|
||
// 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. | ||
pub 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.execution_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).unwrap()]); | ||
} | ||
|
||
*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."); | ||
} | ||
} |
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,11 +1,48 @@ | ||
use cairo_lang_starknet_classes::contract_class::ContractEntryPoint; | ||
use itertools::Itertools; | ||
use num_traits::ToBytes; | ||
use starknet_api::core::EntryPointSelector; | ||
use starknet_types_core::felt::Felt; | ||
|
||
#[cfg(test)] | ||
#[path = "utils_test.rs"] | ||
pub mod test; | ||
|
||
pub fn contract_entrypoint_to_entrypoint_selector( | ||
entrypoint: &ContractEntryPoint, | ||
) -> EntryPointSelector { | ||
let selector_felt = Felt::from_bytes_be_slice(&entrypoint.selector.to_be_bytes()); | ||
EntryPointSelector(selector_felt) | ||
} | ||
|
||
pub fn encode_str_as_felts(msg: &str) -> Vec<Felt> { | ||
const CHUNK_SIZE: usize = 32; | ||
|
||
let data = msg.as_bytes().chunks(CHUNK_SIZE - 1); | ||
let mut encoding = vec![Felt::default(); data.len()]; | ||
for (i, data_chunk) in data.enumerate() { | ||
let mut chunk = [0_u8; CHUNK_SIZE]; | ||
chunk[1..data_chunk.len() + 1].copy_from_slice(data_chunk); | ||
encoding[i] = Felt::from_bytes_be(&chunk); | ||
} | ||
encoding | ||
} | ||
|
||
pub fn decode_felts_as_str(encoding: &[Felt]) -> String { | ||
let bytes_err: Vec<_> = | ||
encoding.iter().flat_map(|felt| felt.to_bytes_be()[1..32].to_vec()).collect(); | ||
|
||
match String::from_utf8(bytes_err) { | ||
Ok(s) => s.trim_matches('\0').to_owned(), | ||
Err(_) => { | ||
let err_msgs = encoding | ||
.iter() | ||
.map(|felt| match String::from_utf8(felt.to_bytes_be()[1..32].to_vec()) { | ||
Ok(s) => format!("{} ({})", s.trim_matches('\0'), felt), | ||
Err(_) => felt.to_string(), | ||
}) | ||
.join(", "); | ||
format!("[{}]", err_msgs) | ||
} | ||
} | ||
} |
Oops, something went wrong.