Skip to content
Merged
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 Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ futures = "0.3.31"
hex = "0.4.3"
http-body-util = "=0.1.0"
indicatif = "0.17.9"
keccak = "0.1.5"
lazy_static = "1.5.0"
num-bigint = "0.4.6"
num-integer = "0.1.46"
num-traits = "0.2.19"
rand = "0.8"
reqwest = "0.12.9"
Expand All @@ -53,8 +56,6 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
utoipa = { version = "5.3.1", features = ["axum_extras"] }
utoipa-swagger-ui = { version = "9", features = ["axum"] }
version-compare = "=0.0.11"
num-integer = "0.1.46"
keccak = "0.1.5"

dry_hint_processor = { path = "crates/dry_hint_processor" }
eth_essentials_cairo_vm_hints = { path = "packages/eth_essentials/cairo_vm_hints" }
Expand Down
24 changes: 16 additions & 8 deletions crates/dry_hint_processor/src/syscall_handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ use hints::vars;
use serde::{Deserialize, Serialize};
use starknet::CallContractHandler as StarknetCallContractHandler;
use syscall_handler::{
felt_from_ptr, keccak::KeccakHandler, run_handler, traits, SyscallExecutionError, SyscallResult, SyscallSelector, WriteResponseResult,
call_contract, call_contract::debug::DebugCallContractHandler, felt_from_ptr, keccak::KeccakHandler, run_handler, traits,
SyscallExecutionError, SyscallResult, SyscallSelector, WriteResponseResult,
};
use tokio::{sync::RwLock, task};
use types::{
Expand Down Expand Up @@ -80,6 +81,8 @@ impl SyscallHandlerWrapper {
pub struct CallContractHandlerRelay {
pub evm_call_contract_handler: EvmCallContractHandler,
pub starknet_call_contract_handler: StarknetCallContractHandler,
#[serde(skip)]
pub debug_call_contract_handler: DebugCallContractHandler,
}

impl traits::SyscallHandler for CallContractHandlerRelay {
Expand All @@ -93,13 +96,18 @@ impl traits::SyscallHandler for CallContractHandlerRelay {
}

async fn execute(&mut self, request: Self::Request, vm: &mut VirtualMachine) -> SyscallResult<Self::Response> {
let chain_id = <Felt252 as TryInto<u128>>::try_into(*vm.get_integer((request.calldata_start + 2)?)?)
.map_err(|e| SyscallExecutionError::InternalError(e.to_string().into()))?;

match chain_id {
ETHEREUM_MAINNET_CHAIN_ID | ETHEREUM_TESTNET_CHAIN_ID => self.evm_call_contract_handler.execute(request, vm).await,
STARKNET_MAINNET_CHAIN_ID | STARKNET_TESTNET_CHAIN_ID => self.starknet_call_contract_handler.execute(request, vm).await,
_ => Err(SyscallExecutionError::InternalError(Box::from("Unknown chain id"))),
match request.contract_address {
v if v == call_contract::debug::CONTRACT_ADDRESS => self.debug_call_contract_handler.execute(request, vm).await,
_ => {
let chain_id = <Felt252 as TryInto<u128>>::try_into(*vm.get_integer((request.calldata_start + 2)?)?)
.map_err(|e| SyscallExecutionError::InternalError(e.to_string().into()))?;

match chain_id {
ETHEREUM_MAINNET_CHAIN_ID | ETHEREUM_TESTNET_CHAIN_ID => self.evm_call_contract_handler.execute(request, vm).await,
STARKNET_MAINNET_CHAIN_ID | STARKNET_TESTNET_CHAIN_ID => self.starknet_call_contract_handler.execute(request, vm).await,
_ => Err(SyscallExecutionError::InternalError(Box::from("Unknown chain id"))),
}
}
}
}

Expand Down
25 changes: 17 additions & 8 deletions crates/sound_hint_processor/src/syscall_handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ use cairo_vm::{
use hints::vars;
use serde::{Deserialize, Serialize};
use syscall_handler::{
felt_from_ptr, keccak::KeccakHandler, run_handler, traits, SyscallExecutionError, SyscallResult, SyscallSelector, WriteResponseResult,
call_contract, call_contract::debug::DebugCallContractHandler, felt_from_ptr, keccak::KeccakHandler, run_handler, traits,
SyscallExecutionError, SyscallResult, SyscallSelector, WriteResponseResult,
};
use tokio::{sync::RwLock, task};
use types::{
Expand Down Expand Up @@ -85,13 +86,15 @@ impl CairoType for Memorizer {
pub struct CallContractHandlerRelay {
pub evm_call_contract_handler: EvmCallContractHandler,
pub starknet_call_contract_handler: StarknetCallContractHandler,
pub debug_call_contract_handler: DebugCallContractHandler,
}

impl CallContractHandlerRelay {
pub fn new(dict_manager: Rc<RefCell<DictManager>>) -> Self {
Self {
evm_call_contract_handler: EvmCallContractHandler::new(dict_manager.clone()),
starknet_call_contract_handler: StarknetCallContractHandler::new(dict_manager),
debug_call_contract_handler: DebugCallContractHandler,
}
}
}
Expand All @@ -107,13 +110,18 @@ impl traits::SyscallHandler for CallContractHandlerRelay {
}

async fn execute(&mut self, request: Self::Request, vm: &mut VirtualMachine) -> SyscallResult<Self::Response> {
let chain_id = <Felt252 as TryInto<u128>>::try_into(*vm.get_integer((request.calldata_start + 2)?)?)
.map_err(|e| SyscallExecutionError::InternalError(e.to_string().into()))?;

match chain_id {
ETHEREUM_MAINNET_CHAIN_ID | ETHEREUM_TESTNET_CHAIN_ID => self.evm_call_contract_handler.execute(request, vm).await,
STARKNET_MAINNET_CHAIN_ID | STARKNET_TESTNET_CHAIN_ID => self.starknet_call_contract_handler.execute(request, vm).await,
_ => Err(SyscallExecutionError::InternalError(Box::from("Unknown chain id"))),
match request.contract_address {
v if v == call_contract::debug::CONTRACT_ADDRESS => self.debug_call_contract_handler.execute(request, vm).await,
_ => {
let chain_id = <Felt252 as TryInto<u128>>::try_into(*vm.get_integer((request.calldata_start + 2)?)?)
.map_err(|e| SyscallExecutionError::InternalError(e.to_string().into()))?;

match chain_id {
ETHEREUM_MAINNET_CHAIN_ID | ETHEREUM_TESTNET_CHAIN_ID => self.evm_call_contract_handler.execute(request, vm).await,
STARKNET_MAINNET_CHAIN_ID | STARKNET_TESTNET_CHAIN_ID => self.starknet_call_contract_handler.execute(request, vm).await,
_ => Err(SyscallExecutionError::InternalError(Box::from("Unknown chain id"))),
}
}
}
}

Expand All @@ -129,6 +137,7 @@ pub struct SyscallHandler {
#[serde(skip)]
pub syscall_ptr: Option<Relocatable>,
pub call_contract_handler: CallContractHandlerRelay,
// pub debug_handler: DebugHandler,
#[serde(skip)]
pub keccak_handler: KeccakHandler,
}
Expand Down
9 changes: 5 additions & 4 deletions crates/syscall_handler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ edition = "2021"

[dependencies]
cairo-vm.workspace = true
thiserror.workspace = true
types.workspace = true
num-integer.workspace = true
keccak.workspace = true
num-bigint.workspace = true
num-integer.workspace = true
num-traits.workspace = true
serde.workspace = true
serde.workspace = true
strum_macros.workspace = true
thiserror.workspace = true
types.workspace = true
141 changes: 141 additions & 0 deletions crates/syscall_handler/src/call_contract/debug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use cairo_vm::{types::relocatable::Relocatable, vm::vm_core::VirtualMachine, Felt252};
use serde::{Deserialize, Serialize};
use strum_macros::FromRepr;
use types::cairo::{
new_syscalls::{CallContractRequest, CallContractResponse},
traits::CairoType,
};

use crate::{traits, SyscallExecutionError, SyscallResult, WriteResponseResult};

pub const CONTRACT_ADDRESS: Felt252 = Felt252::from_hex_unchecked("0x6465627567"); // 'debug' in hex

#[derive(FromRepr)]
pub enum CallHandlerId {
Print = 0,
PrintArray = 1,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct DebugCallContractHandler;

impl traits::SyscallHandler for DebugCallContractHandler {
type Request = CallContractRequest;
type Response = CallContractResponse;

fn read_request(&mut self, vm: &VirtualMachine, ptr: &mut Relocatable) -> SyscallResult<Self::Request> {
let ret = Self::Request::from_memory(vm, *ptr)?;
*ptr = (*ptr + Self::Request::cairo_size())?;
Ok(ret)
}

async fn execute(&mut self, request: Self::Request, vm: &mut VirtualMachine) -> SyscallResult<Self::Response> {
let call_handler_id = CallHandlerId::try_from(request.selector)?;
match call_handler_id {
CallHandlerId::Print => {
let field_len = (request.calldata_end - request.calldata_start)?;
let fields = vm
.get_integer_range(request.calldata_start, field_len)?
.into_iter()
.map(|f| (*f.as_ref()))
.collect::<Vec<Felt252>>();

let str = decode_byte_array_felts(fields);
println!("{}", str);
Ok(Self::Response {
retdata_start: request.calldata_end,
retdata_end: request.calldata_end,
})
}
CallHandlerId::PrintArray => {
let field_len = (request.calldata_end - request.calldata_start)?;
let fields = vm
.get_integer_range(request.calldata_start, field_len)?
.into_iter()
.map(|f| (*f.as_ref()))
.collect::<Vec<Felt252>>();

println!("{:?}", fields);

Ok(Self::Response {
retdata_start: request.calldata_end,
retdata_end: request.calldata_end,
})
}
}
}

fn write_response(&mut self, _response: Self::Response, _vm: &mut VirtualMachine, _ptr: &mut Relocatable) -> WriteResponseResult {
Ok(())
}
}

// Decodes a serialized byte array of felts into a ascii string
fn decode_byte_array_felts(felts: Vec<Felt252>) -> String {
// 1) Parse how many full 31-byte chunks we have.
let n_full: usize = felts[0].try_into().expect("n_full not convertible");

// 2) Read each 31-byte chunk in big-endian order.
let mut bytes = Vec::new();
for i in 0..n_full {
let chunk = &felts[1 + i];
let chunk_be: Vec<u8> = chunk.to_bytes_be().to_vec();

// Convert if chain to match
match chunk_be.len().cmp(&31) {
std::cmp::Ordering::Less => {
// Prepend leading zeros if needed
let mut padded = vec![0u8; 31 - chunk_be.len()];
padded.extend_from_slice(&chunk_be);
bytes.extend_from_slice(&padded);
}
std::cmp::Ordering::Greater => {
// If somehow bigger, take the last 31 bytes
bytes.extend_from_slice(&chunk_be[chunk_be.len() - 31..]);
}
std::cmp::Ordering::Equal => {
bytes.extend_from_slice(&chunk_be);
}
}
}

// 3) The next felt is the pending word, followed by the pending length.
let pending_word = &felts[1 + n_full];
let pending_len: usize = felts[1 + n_full + 1].try_into().unwrap();

if pending_len > 0 {
let pending_be: Vec<u8> = pending_word.to_bytes_be().to_vec();
// Convert if chain to match
match pending_be.len().cmp(&pending_len) {
std::cmp::Ordering::Less => {
// Again pad if needed
let mut padded = vec![0u8; pending_len - pending_be.len()];
padded.extend_from_slice(&pending_be);
bytes.extend_from_slice(&padded);
}
std::cmp::Ordering::Greater => {
bytes.extend_from_slice(&pending_be[pending_be.len() - pending_len..]);
}
std::cmp::Ordering::Equal => {
bytes.extend_from_slice(&pending_be);
}
}
}

// 4) Convert raw bytes to a UTF-8 string (or ASCII if you know it is ASCII).
String::from_utf8(bytes).expect("Invalid UTF-8")
}

impl TryFrom<Felt252> for CallHandlerId {
type Error = SyscallExecutionError;
fn try_from(value: Felt252) -> Result<Self, Self::Error> {
Self::from_repr(value.try_into().map_err(|e| Self::Error::InvalidSyscallInput {
input: value,
info: format!("{}", e),
})?)
.ok_or(Self::Error::InvalidSyscallInput {
input: value,
info: "Invalid function identifier".to_string(),
})
}
}
1 change: 1 addition & 0 deletions crates/syscall_handler/src/call_contract/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod debug;
1 change: 1 addition & 0 deletions crates/syscall_handler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![warn(unused_crate_dependencies)]
#![forbid(unsafe_code)]

pub mod call_contract;
pub mod keccak;
pub mod traits;

Expand Down
2 changes: 2 additions & 0 deletions hdp_cairo/src/debug.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod printer;
pub use printer::{print, print_array};
25 changes: 25 additions & 0 deletions hdp_cairo/src/debug/printer.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use starknet::syscalls::call_contract_syscall;
use starknet::SyscallResultTrait;
use core::fmt::Display;


const DEBUG_CONTRACT_ADDRESS: felt252 = 'debug';
const PRINT: felt252 = 0;
const PRINT_ARRAY: felt252 = 1;

pub fn print<T, +Display<T>, +Drop<T>>(value: T) {
let msg: ByteArray = format!("{}", value);
let mut output_array = array![];
msg.serialize(ref output_array);
call_contract_syscall(DEBUG_CONTRACT_ADDRESS.try_into().unwrap(), PRINT, output_array.span())
.unwrap_syscall();
}

pub fn print_array(array: Array<felt252>) {
call_contract_syscall(
DEBUG_CONTRACT_ADDRESS.try_into().unwrap(),
PRINT_ARRAY,
array.span()
)
.unwrap_syscall();
}
1 change: 1 addition & 0 deletions hdp_cairo/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod evm;
pub mod starknet;
pub mod debug;

#[derive(Serde, Drop)]
pub struct HDP {
Expand Down
5 changes: 5 additions & 0 deletions src/contract_bootloader/execute_syscalls.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ func execute_call_contract{
let state_access_type = request.contract_address;
let field = request.selector;

// Debug Contract does not need to be executed
if (request.contract_address == 'debug') {
return ();
}

let layout = chain_id_to_layout(request.calldata_start[2]);
let output_ptr = response.retdata_start;

Expand Down
1 change: 1 addition & 0 deletions tests/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod evm;
pub mod starknet;
pub mod utils;
3 changes: 3 additions & 0 deletions tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ pub mod evm;
#[cfg(test)]
pub mod starknet;

#[cfg(test)]
pub mod utils;

#[cfg(test)]
mod test_utils {
use std::{env, path::PathBuf};
Expand Down
1 change: 1 addition & 0 deletions tests/src/utils.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod debug;
Loading