Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(blockifier): add NativeSyscallHandler #621

Open
wants to merge 6 commits into
base: native/add-native-execution-engine
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/blockifier/src/execution/native.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod syscall_handler;
pub mod utils;
323 changes: 323 additions & 0 deletions crates/blockifier/src/execution/native/syscall_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
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::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 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(),
}
}

pub 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.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);

let retdata = call_info.execution.retdata.clone();

self.inner_calls.push(call_info);

Ok(retdata)
}

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.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.");
}
}
37 changes: 37 additions & 0 deletions crates/blockifier/src/execution/native/utils.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,46 @@
use cairo_lang_starknet_classes::contract_class::ContractEntryPoint;
use itertools::Itertools;
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 {
EntryPointSelector(Felt::from(&entrypoint.selector))
}

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)
}
}
}
Loading
Loading