diff --git a/.gitignore b/.gitignore index e2331392b..cd055e0fb 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ cairo-*.tar /cairo2 *.dylib *.so +executor_logs/ diff --git a/Cargo.lock b/Cargo.lock index 162d10d43..a55d7fbe4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -313,7 +313,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.43", + "syn 2.0.48", "which", ] @@ -336,7 +336,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.43", + "syn 2.0.48", "which", ] @@ -594,7 +594,7 @@ checksum = "ad0bfcae383df402157f70087e8e6d3e13c012356d57a2f60662b1c21e6cfabf" dependencies = [ "cairo-lang-debug", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -873,6 +873,7 @@ dependencies = [ "criterion", "educe", "id-arena", + "ipc-channel", "itertools 0.12.0", "lambdaworks-math 0.1.3", "lazy_static", @@ -885,13 +886,16 @@ dependencies = [ "num-traits 0.2.17", "pretty_assertions_sorted", "proptest", + "serde", "serde_json", "starknet-types-core", "tempfile", "test-case", "thiserror", "tracing", + "tracing-appender", "tracing-subscriber", + "uuid", "walkdir", ] @@ -1037,7 +1041,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1178,6 +1182,15 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" +[[package]] +name = "crossbeam-channel" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.3" @@ -1204,12 +1217,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crunchy" @@ -1417,7 +1427,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1458,7 +1468,7 @@ checksum = "05aa0010e0e391b9a82dc87bfc4dbee3cb8260eec12ad9d83c12788299fa9093" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1540,7 +1550,7 @@ checksum = "d4cf186fea4af17825116f72932fe52cce9a13bae39ff63b4dc0cfdb3fb4bde1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1759,6 +1769,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "ipc-channel" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab3a34c91b7e84a72643bd75d1bac3afd241f78f9859fe0b5e5b2a6a75732c2" +dependencies = [ + "bincode 1.3.3", + "crossbeam-channel", + "fnv", + "lazy_static", + "libc", + "mio", + "rand", + "sc", + "serde", + "tempfile", + "uuid", + "windows", +] + [[package]] name = "is-terminal" version = "0.4.9" @@ -2030,7 +2060,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.43", + "syn 2.0.48", "tblgen", "unindent", ] @@ -2084,6 +2114,18 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + [[package]] name = "mlir-sys" version = "0.2.1" @@ -2475,7 +2517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2514,9 +2556,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -2558,9 +2600,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -2827,6 +2869,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010e18bd3bfd1d45a7e666b236c78720df0d9a7698ebaa9c1c559961eb60a38b" + [[package]] name = "schemars" version = "0.8.15" @@ -2866,22 +2914,22 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3067,7 +3115,7 @@ checksum = "af6527b845423542c8a16e060ea1bc43f67229848e7cd4c4d80be994a84220ce" dependencies = [ "starknet-curve 0.4.0", "starknet-ff", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3153,9 +3201,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.43" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -3255,7 +3303,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3267,7 +3315,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "test-case-core", ] @@ -3288,7 +3336,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3425,6 +3473,18 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.27" @@ -3433,7 +3493,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3448,9 +3508,9 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", @@ -3459,9 +3519,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", @@ -3544,6 +3604,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "valuable" version = "0.1.0" @@ -3602,7 +3672,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "wasm-bindgen-shared", ] @@ -3624,7 +3694,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3688,6 +3758,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3825,7 +3904,7 @@ checksum = "255c4596d41e6916ced49cfafea18727b24d67878fa180ddfd69b9df34fd1726" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3845,5 +3924,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] diff --git a/Cargo.toml b/Cargo.toml index eb0a5435b..84d729678 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,9 @@ required-features = ["build-cli"] name = "cairo-native-run" required-features = ["build-cli"] +[[bin]] +name = "cairo-executor" + [features] default = ["build-cli", "with-runtime"] build-cli = ["clap", "tracing-subscriber"] @@ -63,6 +66,11 @@ libloading = "0.8.1" tracing-subscriber = { version = "0.3", features = [ "env-filter", ], optional = true } +serde = { version = "1.0.195", features = ["derive"] } +uuid = { version = "1.6.1", features = ["serde", "v4"] } +serde_json = "1.0" +ipc-channel = { version = "0.18.0", features = ["memfd"] } +tracing-appender = "0.2.3" [dev-dependencies] cairo-felt = "0.8.5" diff --git a/examples/sandbox.rs b/examples/sandbox.rs new file mode 100644 index 000000000..bfdac4a84 --- /dev/null +++ b/examples/sandbox.rs @@ -0,0 +1,266 @@ +use cairo_lang_compiler::CompilerConfig; +use cairo_lang_starknet::contract_class::compile_path; +use cairo_native::{ + sandbox::IsolatedExecutor, + starknet::{BlockInfo, ExecutionInfo, StarkNetSyscallHandler, SyscallResult, TxInfo, U256}, +}; +use starknet_types_core::felt::Felt; +use std::path::Path; +use tracing_subscriber::{EnvFilter, FmtSubscriber}; + +fn main() { + // Configure logging and error handling. + tracing::subscriber::set_global_default( + FmtSubscriber::builder() + .with_env_filter(EnvFilter::from_default_env()) + .finish(), + ) + .unwrap(); + + let path = Path::new("programs/examples/hello_starknet.cairo"); + + let contract = compile_path( + path, + None, + CompilerConfig { + replace_ids: true, + ..Default::default() + }, + ) + .unwrap(); + + let path = std::env::current_dir() + .unwrap() + .join("target/debug/cairo-executor"); + let sandbox = IsolatedExecutor::new(&path).unwrap(); + + let entry_point = contract.entry_points_by_type.external.get(0).unwrap(); + + let mut handler = SyscallHandler; + + let result = sandbox + .run_program( + contract.extract_sierra_program().unwrap(), + vec![1.into()], + Some(u128::MAX), + entry_point.function_idx, + &mut handler, + ) + .unwrap(); + println!("{:#?}", result); +} + +#[derive(Debug)] +struct SyscallHandler; + +impl StarkNetSyscallHandler for SyscallHandler { + fn get_block_hash(&mut self, block_number: u64, _gas: &mut u128) -> SyscallResult { + println!("Called `get_block_hash({block_number})` from MLIR."); + Ok(Felt::from_bytes_be_slice(b"get_block_hash ok")) + } + + fn get_execution_info( + &mut self, + _gas: &mut u128, + ) -> SyscallResult { + println!("Called `get_execution_info()` from MLIR."); + Ok(ExecutionInfo { + block_info: BlockInfo { + block_number: 1234, + block_timestamp: 2345, + sequencer_address: 3456.into(), + }, + tx_info: TxInfo { + version: 4567.into(), + account_contract_address: 5678.into(), + max_fee: 6789, + signature: vec![1248.into(), 2486.into()], + transaction_hash: 9876.into(), + chain_id: 8765.into(), + nonce: 7654.into(), + }, + caller_address: 6543.into(), + contract_address: 5432.into(), + entry_point_selector: 4321.into(), + }) + } + + fn deploy( + &mut self, + class_hash: Felt, + contract_address_salt: Felt, + calldata: &[Felt], + deploy_from_zero: bool, + _gas: &mut u128, + ) -> SyscallResult<(Felt, Vec)> { + println!("Called `deploy({class_hash}, {contract_address_salt}, {calldata:?}, {deploy_from_zero})` from MLIR."); + Ok(( + class_hash + contract_address_salt, + calldata.iter().map(|x| x + Felt::from(1)).collect(), + )) + } + + fn replace_class(&mut self, class_hash: Felt, _gas: &mut u128) -> SyscallResult<()> { + println!("Called `replace_class({class_hash})` from MLIR."); + Ok(()) + } + + fn library_call( + &mut self, + class_hash: Felt, + function_selector: Felt, + calldata: &[Felt], + _gas: &mut u128, + ) -> SyscallResult> { + println!( + "Called `library_call({class_hash}, {function_selector}, {calldata:?})` from MLIR." + ); + Ok(calldata.iter().map(|x| x * Felt::from(3)).collect()) + } + + fn call_contract( + &mut self, + address: Felt, + entry_point_selector: Felt, + calldata: &[Felt], + _gas: &mut u128, + ) -> SyscallResult> { + println!( + "Called `call_contract({address}, {entry_point_selector}, {calldata:?})` from MLIR." + ); + Ok(calldata.iter().map(|x| x * Felt::from(3)).collect()) + } + + fn storage_read( + &mut self, + address_domain: u32, + address: Felt, + _gas: &mut u128, + ) -> SyscallResult { + println!("Called `storage_read({address_domain}, {address})` from MLIR."); + Ok(address * Felt::from(3)) + } + + fn storage_write( + &mut self, + address_domain: u32, + address: Felt, + value: Felt, + _gas: &mut u128, + ) -> SyscallResult<()> { + println!("Called `storage_write({address_domain}, {address}, {value})` from MLIR."); + Ok(()) + } + + fn emit_event(&mut self, keys: &[Felt], data: &[Felt], _gas: &mut u128) -> SyscallResult<()> { + println!("Called `emit_event({keys:?}, {data:?})` from MLIR."); + Ok(()) + } + + fn send_message_to_l1( + &mut self, + to_address: Felt, + payload: &[Felt], + _gas: &mut u128, + ) -> SyscallResult<()> { + println!("Called `send_message_to_l1({to_address}, {payload:?})` from MLIR."); + Ok(()) + } + + fn keccak( + &mut self, + input: &[u64], + _gas: &mut u128, + ) -> SyscallResult { + println!("Called `keccak({input:?})` from MLIR."); + Ok(U256(Felt::from(1234567890).to_bytes_le())) + } + + fn secp256k1_add( + &mut self, + _p0: cairo_native::starknet::Secp256k1Point, + _p1: cairo_native::starknet::Secp256k1Point, + _remaining_gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256k1_get_point_from_x( + &self, + _x: U256, + _y_parity: bool, + _remaining_gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256k1_get_xy( + &self, + _p: cairo_native::starknet::Secp256k1Point, + _remaining_gas: &mut u128, + ) -> SyscallResult<(U256, U256)> { + todo!() + } + + fn secp256k1_mul( + &self, + _p: cairo_native::starknet::Secp256k1Point, + _m: U256, + _remaining_gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256k1_new( + &self, + _x: U256, + _y: U256, + _remaining_gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256r1_add( + &self, + _p0: cairo_native::starknet::Secp256k1Point, + _p1: cairo_native::starknet::Secp256k1Point, + _remaining_gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256r1_get_point_from_x( + &self, + _x: U256, + _y_parity: bool, + _remaining_gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256r1_get_xy( + &self, + _p: cairo_native::starknet::Secp256k1Point, + _remaining_gas: &mut u128, + ) -> SyscallResult<(U256, U256)> { + todo!() + } + + fn secp256r1_mul( + &self, + _p: cairo_native::starknet::Secp256k1Point, + _m: U256, + _remaining_gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256r1_new( + &mut self, + _x: U256, + _y: U256, + _remaining_gas: &mut u128, + ) -> SyscallResult> { + todo!() + } +} diff --git a/src/bin/cairo-executor.rs b/src/bin/cairo-executor.rs new file mode 100644 index 000000000..b84d65a84 --- /dev/null +++ b/src/bin/cairo-executor.rs @@ -0,0 +1,675 @@ +use std::{cell::RefCell, path::PathBuf, rc::Rc}; + +use cairo_lang_utils::ResultHelper; +use cairo_native::{ + context::NativeContext, + executor::JitNativeExecutor, + metadata::syscall_handler::SyscallHandlerMeta, + sandbox::{Message, SyscallAnswer, SyscallRequest, WrappedMessage}, + starknet::{StarkNetSyscallHandler, SyscallResult}, + utils::find_entry_point_by_idx, +}; +use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender}; +use starknet_types_core::felt::Felt; +use tracing::instrument; + +#[derive(Debug)] +struct SyscallHandler { + sender: IpcSender, + receiver: Rc>>, +} + +impl StarkNetSyscallHandler for SyscallHandler { + #[instrument(skip(self))] + fn get_block_hash(&mut self, block_number: u64, gas: &mut u128) -> SyscallResult { + self.sender + .send( + Message::SyscallRequest(SyscallRequest::GetBlockHash { + block_number, + gas: *gas, + }) + .wrap() + .unwrap(), + ) + .expect("failed to send"); + let result = self + .receiver + .borrow() + .recv() + .on_err(|e| tracing::error!("error receiving: {:?}", e)) + .unwrap() + .to_msg() + .unwrap(); + + if let Message::SyscallAnswer(SyscallAnswer::GetBlockHash { + result, + remaining_gas, + }) = result + { + *gas = remaining_gas; + result + } else { + tracing::error!("wrong message received: {:#?}", result); + panic!(); + } + } + + #[instrument(skip(self))] + fn get_execution_info( + &mut self, + gas: &mut u128, + ) -> SyscallResult { + self.sender + .send( + Message::SyscallRequest(SyscallRequest::GetExecutionInfo { gas: *gas }) + .wrap() + .unwrap(), + ) + .expect("failed to send"); + let result = self + .receiver + .borrow() + .recv() + .on_err(|e| tracing::error!("error receiving: {:?}", e)) + .unwrap() + .to_msg() + .unwrap(); + + if let Message::SyscallAnswer(SyscallAnswer::GetExecutionInfo { + result, + remaining_gas, + }) = result + { + *gas = remaining_gas; + result + } else { + tracing::error!("wrong message received: {:#?}", result); + panic!(); + } + } + + fn deploy( + &mut self, + class_hash: Felt, + contract_address_salt: Felt, + calldata: &[Felt], + deploy_from_zero: bool, + gas: &mut u128, + ) -> SyscallResult<(Felt, Vec)> { + self.sender + .send( + Message::SyscallRequest(SyscallRequest::Deploy { + class_hash, + contract_address_salt, + calldata: calldata.to_vec(), + deploy_from_zero, + gas: *gas, + }) + .wrap() + .unwrap(), + ) + .expect("failed to send"); + let result = self + .receiver + .borrow() + .recv() + .on_err(|e| tracing::error!("error receiving: {:?}", e)) + .unwrap() + .to_msg() + .unwrap(); + + if let Message::SyscallAnswer(SyscallAnswer::Deploy { + result, + remaining_gas, + }) = result + { + *gas = remaining_gas; + result + } else { + tracing::error!("wrong message received: {:#?}", result); + panic!(); + } + } + + fn replace_class(&mut self, class_hash: Felt, gas: &mut u128) -> SyscallResult<()> { + self.sender + .send( + Message::SyscallRequest(SyscallRequest::ReplaceClass { + class_hash, + gas: *gas, + }) + .wrap() + .unwrap(), + ) + .expect("failed to send"); + let result = self + .receiver + .borrow() + .recv() + .on_err(|e| tracing::error!("error receiving: {:?}", e)) + .unwrap() + .to_msg() + .unwrap(); + + if let Message::SyscallAnswer(SyscallAnswer::ReplaceClass { + result, + remaining_gas, + }) = result + { + *gas = remaining_gas; + result + } else { + tracing::error!("wrong message received: {:#?}", result); + panic!(); + } + } + + fn library_call( + &mut self, + class_hash: Felt, + function_selector: Felt, + calldata: &[Felt], + gas: &mut u128, + ) -> SyscallResult> { + self.sender + .send( + Message::SyscallRequest(SyscallRequest::LibraryCall { + class_hash, + function_selector, + calldata: calldata.to_vec(), + gas: *gas, + }) + .wrap() + .unwrap(), + ) + .expect("failed to send"); + let result = self + .receiver + .borrow() + .recv() + .on_err(|e| tracing::error!("error receiving: {:?}", e)) + .unwrap() + .to_msg() + .unwrap(); + + if let Message::SyscallAnswer(SyscallAnswer::LibraryCall { + result, + remaining_gas, + }) = result + { + *gas = remaining_gas; + result + } else { + tracing::error!("wrong message received: {:#?}", result); + panic!(); + } + } + + fn call_contract( + &mut self, + address: Felt, + entry_point_selector: Felt, + calldata: &[Felt], + gas: &mut u128, + ) -> SyscallResult> { + self.sender + .send( + Message::SyscallRequest(SyscallRequest::CallContract { + address, + entry_point_selector, + calldata: calldata.to_vec(), + gas: *gas, + }) + .wrap() + .unwrap(), + ) + .expect("failed to send"); + let result = self + .receiver + .borrow() + .recv() + .on_err(|e| tracing::error!("error receiving: {:?}", e)) + .unwrap() + .to_msg() + .unwrap(); + + if let Message::SyscallAnswer(SyscallAnswer::CallContract { + result, + remaining_gas, + }) = result + { + *gas = remaining_gas; + result + } else { + tracing::error!("wrong message received: {:#?}", result); + panic!(); + } + } + + fn storage_read( + &mut self, + address_domain: u32, + address: Felt, + gas: &mut u128, + ) -> SyscallResult { + self.sender + .send( + Message::SyscallRequest(SyscallRequest::StorageRead { + address_domain, + address, + gas: *gas, + }) + .wrap() + .unwrap(), + ) + .expect("failed to send"); + let result = self + .receiver + .borrow() + .recv() + .on_err(|e| tracing::error!("error receiving: {:?}", e)) + .unwrap() + .to_msg() + .unwrap(); + + if let Message::SyscallAnswer(SyscallAnswer::StorageRead { + result, + remaining_gas, + }) = result + { + *gas = remaining_gas; + result + } else { + tracing::error!("wrong message received: {:#?}", result); + panic!(); + } + } + + fn storage_write( + &mut self, + address_domain: u32, + address: Felt, + value: Felt, + gas: &mut u128, + ) -> SyscallResult<()> { + self.sender + .send( + Message::SyscallRequest(SyscallRequest::StorageWrite { + address_domain, + address, + value, + gas: *gas, + }) + .wrap() + .unwrap(), + ) + .expect("failed to send"); + let result = self + .receiver + .borrow() + .recv() + .on_err(|e| tracing::error!("error receiving: {:?}", e)) + .unwrap() + .to_msg() + .unwrap(); + + if let Message::SyscallAnswer(SyscallAnswer::StorageWrite { + result, + remaining_gas, + }) = result + { + *gas = remaining_gas; + result + } else { + tracing::error!("wrong message received: {:#?}", result); + panic!(); + } + } + + fn emit_event(&mut self, keys: &[Felt], data: &[Felt], gas: &mut u128) -> SyscallResult<()> { + self.sender + .send( + Message::SyscallRequest(SyscallRequest::EmitEvent { + keys: keys.to_vec(), + data: data.to_vec(), + gas: *gas, + }) + .wrap() + .unwrap(), + ) + .expect("failed to send"); + let result = self + .receiver + .borrow() + .recv() + .on_err(|e| tracing::error!("error receiving: {:?}", e)) + .unwrap() + .to_msg() + .unwrap(); + + if let Message::SyscallAnswer(SyscallAnswer::EmitEvent { + result, + remaining_gas, + }) = result + { + *gas = remaining_gas; + result + } else { + tracing::error!("wrong message received: {:#?}", result); + panic!(); + } + } + + fn send_message_to_l1( + &mut self, + to_address: Felt, + payload: &[Felt], + gas: &mut u128, + ) -> SyscallResult<()> { + self.sender + .send( + Message::SyscallRequest(SyscallRequest::SendMessageToL1 { + to_address, + payload: payload.to_vec(), + gas: *gas, + }) + .wrap() + .unwrap(), + ) + .expect("failed to send"); + let result = self + .receiver + .borrow() + .recv() + .on_err(|e| tracing::error!("error receiving: {:?}", e)) + .unwrap() + .to_msg() + .unwrap(); + + if let Message::SyscallAnswer(SyscallAnswer::SendMessageToL1 { + result, + remaining_gas, + }) = result + { + *gas = remaining_gas; + result + } else { + tracing::error!("wrong message received: {:#?}", result); + panic!(); + } + } + + fn keccak( + &mut self, + input: &[u64], + gas: &mut u128, + ) -> SyscallResult { + self.sender + .send( + Message::SyscallRequest(SyscallRequest::Keccak { + input: input.to_vec(), + gas: *gas, + }) + .wrap() + .unwrap(), + ) + .expect("failed to send"); + let result = self + .receiver + .borrow() + .recv() + .on_err(|e| tracing::error!("error receiving: {:?}", e)) + .unwrap() + .to_msg() + .unwrap(); + + if let Message::SyscallAnswer(SyscallAnswer::Keccak { + result, + remaining_gas, + }) = result + { + *gas = remaining_gas; + result + } else { + tracing::error!("wrong message received: {:#?}", result); + panic!(); + } + } + + fn secp256k1_add( + &mut self, + _p0: cairo_native::starknet::Secp256k1Point, + _p1: cairo_native::starknet::Secp256k1Point, + _gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256k1_get_point_from_x( + &self, + _x: cairo_native::starknet::U256, + _y_parity: bool, + _gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256k1_get_xy( + &self, + _p: cairo_native::starknet::Secp256k1Point, + _gas: &mut u128, + ) -> SyscallResult<(cairo_native::starknet::U256, cairo_native::starknet::U256)> { + todo!() + } + + fn secp256k1_mul( + &self, + _p: cairo_native::starknet::Secp256k1Point, + _m: cairo_native::starknet::U256, + _gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256k1_new( + &self, + _x: cairo_native::starknet::U256, + _y: cairo_native::starknet::U256, + _gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256r1_add( + &self, + _p0: cairo_native::starknet::Secp256k1Point, + _p1: cairo_native::starknet::Secp256k1Point, + _gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256r1_get_point_from_x( + &self, + _x: cairo_native::starknet::U256, + _y_parity: bool, + _gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256r1_get_xy( + &self, + _p: cairo_native::starknet::Secp256k1Point, + _gas: &mut u128, + ) -> SyscallResult<(cairo_native::starknet::U256, cairo_native::starknet::U256)> { + todo!() + } + + fn secp256r1_mul( + &self, + _p: cairo_native::starknet::Secp256k1Point, + _m: cairo_native::starknet::U256, + _gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn secp256r1_new( + &mut self, + _x: cairo_native::starknet::U256, + _y: cairo_native::starknet::U256, + _gas: &mut u128, + ) -> SyscallResult> { + todo!() + } + + fn pop_log(&mut self) { + todo!() + } + + fn set_account_contract_address(&mut self, _contract_address: Felt) { + todo!() + } + + fn set_block_number(&mut self, _block_number: u64) { + todo!() + } + + fn set_block_timestamp(&mut self, _block_timestamp: u64) { + todo!() + } + + fn set_caller_address(&mut self, _address: Felt) { + todo!() + } + + fn set_chain_id(&mut self, _chain_id: Felt) { + todo!() + } + + fn set_contract_address(&mut self, _address: Felt) { + todo!() + } + + fn set_max_fee(&mut self, _max_fee: u128) { + todo!() + } + + fn set_nonce(&mut self, _nonce: Felt) { + todo!() + } + + fn set_sequencer_address(&mut self, _address: Felt) { + todo!() + } + + fn set_signature(&mut self, _signature: &[Felt]) { + todo!() + } + + fn set_transaction_hash(&mut self, _transaction_hash: Felt) { + todo!() + } + + fn set_version(&mut self, _version: Felt) { + todo!() + } +} + +pub fn main() -> Result<(), Box> { + let mut args = std::env::args(); + + let pid = std::process::id(); + + let log_dir = PathBuf::from( + std::env::var("CAIRO_EXECUTOR_LOGDIR").unwrap_or("executor_logs/".to_string()), + ); + let file_appender = + tracing_appender::rolling::daily(log_dir, format!("cairo-executor.{pid}.log")); + + tracing_subscriber::fmt() + .with_writer(file_appender) + .with_ansi(false) + .init(); + + if args.len() < 2 { + tracing::error!("missing server ipc name"); + std::process::exit(1); + } + + let server = args.nth(1).unwrap(); + let (sv, name) = IpcOneShotServer::::new()?; + println!("{name}"); // print to let know + let sender = IpcSender::connect(server.clone())?; + sender.send(Message::Ping.wrap()?)?; + tracing::info!("connected to {server:?}"); + let (receiver, msg) = sv.accept()?; + let receiver = Rc::new(RefCell::new(receiver)); + tracing::info!("accepted {receiver:?}"); + assert_eq!(msg, Message::Ping.wrap()?); + + let native_context = NativeContext::new(); + tracing::info!("initialized native context"); + + let mut syscall_handler = SyscallHandler { + sender: sender.clone(), + receiver: receiver.clone(), + }; + + loop { + tracing::info!("waiting for message"); + + let message: Message = receiver.borrow().recv()?.to_msg()?; + + match message { + Message::ExecuteJIT { + id, + program, + inputs, + function_idx, + gas, + } => { + tracing::info!("Message: ExecuteJIT"); + sender.send(Message::Ack(id).wrap()?)?; + tracing::info!("sent ack: {:?}", id); + let program = program.into_v1()?.program; + let native_program = native_context.compile(&program)?; + + let entry_point_fn = find_entry_point_by_idx(&program, function_idx).unwrap(); + + let fn_id = &entry_point_fn.id; + + let native_executor = + JitNativeExecutor::from_native_module(native_program, Default::default()); + + let result = native_executor.invoke_contract_dynamic( + fn_id, + &inputs, + gas, + Some(&SyscallHandlerMeta::new(&mut syscall_handler)), + )?; + + tracing::info!("invoked with result: {:?}", result); + + sender.send(Message::ExecutionResult { id, result }.wrap()?)?; + + tracing::info!("sent result msg"); + } + Message::ExecutionResult { .. } => {} + Message::Ack(_) => {} + Message::Ping => { + tracing::info!("Message: Ping"); + sender.send(Message::Ping.wrap()?)?; + } + Message::Kill => { + tracing::info!("Message: KILL"); + break; + } + Message::SyscallRequest(_) => todo!(), + Message::SyscallAnswer(_) => todo!(), + } + } + + Ok(()) +} diff --git a/src/execution_result.rs b/src/execution_result.rs index 61c2731f1..5b6fe73a4 100644 --- a/src/execution_result.rs +++ b/src/execution_result.rs @@ -2,17 +2,18 @@ use crate::{ error::{jit_engine::ErrorImpl, JitRunnerError}, values::JitValue, }; +use serde::{Deserialize, Serialize}; use starknet_types_core::felt::Felt; /// The result of the JIT execution. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ExecutionResult { pub remaining_gas: Option, pub return_value: JitValue, } /// Starknet contract execution result. -#[derive(Debug, Default)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct ContractExecutionResult { pub remaining_gas: u128, pub failure_flag: bool, diff --git a/src/lib.rs b/src/lib.rs index 849b7e9a9..4cd3fa1e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,6 +83,7 @@ mod ffi; pub mod libfuncs; pub mod metadata; pub mod module; +pub mod sandbox; pub mod starknet; pub mod types; pub mod utils; diff --git a/src/sandbox.rs b/src/sandbox.rs new file mode 100644 index 000000000..042c638d0 --- /dev/null +++ b/src/sandbox.rs @@ -0,0 +1,424 @@ +use std::{ + io::{BufRead, BufReader}, + path::Path, + process::{Child, Stdio}, +}; + +use cairo_lang_sierra::program::{Program, VersionedProgram}; +use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender}; +use serde::{Deserialize, Serialize}; +use starknet_types_core::felt::Felt; +use uuid::Uuid; + +use crate::{ + execution_result::ContractExecutionResult, + starknet::{ExecutionInfo, StarkNetSyscallHandler, SyscallResult}, +}; + +#[allow(clippy::large_enum_variant)] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Message { + ExecuteJIT { + id: Uuid, + program: VersionedProgram, + inputs: Vec, + function_idx: usize, + gas: Option, + }, + ExecutionResult { + id: Uuid, + result: ContractExecutionResult, + }, + Ack(Uuid), + Ping, + Kill, + SyscallRequest(SyscallRequest), + SyscallAnswer(SyscallAnswer), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum SyscallRequest { + GetBlockHash { + block_number: u64, + gas: u128, + }, + GetExecutionInfo { + gas: u128, + }, + Deploy { + class_hash: Felt, + contract_address_salt: Felt, + calldata: Vec, + deploy_from_zero: bool, + gas: u128, + }, + ReplaceClass { + class_hash: Felt, + gas: u128, + }, + LibraryCall { + class_hash: Felt, + function_selector: Felt, + calldata: Vec, + gas: u128, + }, + CallContract { + address: Felt, + entry_point_selector: Felt, + calldata: Vec, + gas: u128, + }, + EmitEvent { + keys: Vec, + data: Vec, + gas: u128, + }, + SendMessageToL1 { + to_address: Felt, + payload: Vec, + gas: u128, + }, + Keccak { + input: Vec, + gas: u128, + }, + StorageRead { + address_domain: u32, + address: Felt, + gas: u128, + }, + StorageWrite { + address_domain: u32, + address: Felt, + value: Felt, + gas: u128, + }, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum SyscallAnswer { + GetBlockHash { + result: SyscallResult, + remaining_gas: u128, + }, + GetExecutionInfo { + result: SyscallResult, + remaining_gas: u128, + }, + Deploy { + result: SyscallResult<(Felt, Vec)>, + remaining_gas: u128, + }, + ReplaceClass { + result: SyscallResult<()>, + remaining_gas: u128, + }, + LibraryCall { + result: SyscallResult>, + remaining_gas: u128, + }, + CallContract { + result: SyscallResult>, + remaining_gas: u128, + }, + StorageRead { + result: SyscallResult, + remaining_gas: u128, + }, + StorageWrite { + result: SyscallResult<()>, + remaining_gas: u128, + }, + EmitEvent { + result: SyscallResult<()>, + remaining_gas: u128, + }, + SendMessageToL1 { + result: SyscallResult<()>, + remaining_gas: u128, + }, + Keccak { + result: SyscallResult, + remaining_gas: u128, + }, +} + +impl Message { + pub fn serialize(&self) -> Result { + serde_json::to_string(self) + } + + pub fn deserialize(value: &str) -> Result { + serde_json::from_str(value) + } + + pub fn wrap(&self) -> Result { + Ok(WrappedMessage::Message(self.serialize()?)) + } +} + +#[derive(Debug, Serialize, Deserialize, PartialEq)] +pub enum WrappedMessage { + Message(String), // ipc-channel uses bincode and doesnt support serializing Vecs +} + +impl WrappedMessage { + pub fn to_msg(self) -> Result { + match self { + WrappedMessage::Message(msg) => Message::deserialize(&msg), + } + } +} + +pub struct IsolatedExecutor { + proc: Child, + sender: IpcSender, + receiver: IpcReceiver, +} + +impl IsolatedExecutor { + // "target/debug/cairo-executor" + pub fn new(executor_path: &Path) -> Result { + let (server, server_name) = IpcOneShotServer::new().unwrap(); + tracing::debug!("creating executor with: {:?}", executor_path); + + let mut cmd = std::process::Command::new(executor_path); + cmd.stdout(Stdio::piped()); + let mut proc = cmd.arg(server_name).spawn()?; + let stdout = proc.stdout.take().unwrap(); + let mut stdout = BufReader::new(stdout); + let mut client_name = String::new(); + stdout.read_line(&mut client_name)?; + + // first we accept the connection + let (receiver, msg) = server.accept().expect("failed to accept receiver"); + tracing::debug!("accepted receiver {receiver:?} with msg {msg:?}"); + // then we connect + tracing::debug!("connecting to {client_name}"); + let sender = IpcSender::connect(client_name.trim().to_string()).expect("failed to connect"); + sender.send(Message::Ping.wrap()?).unwrap(); + + Ok(Self { + proc, + sender, + receiver, + }) + } + + pub fn run_program( + &self, + program: Program, + inputs: Vec, + gas: Option, + function_idx: usize, + handler: &mut impl StarkNetSyscallHandler, + ) -> Result> { + tracing::debug!("running program"); + let id = Uuid::new_v4(); + + let msg = Message::ExecuteJIT { + id, + program: program.into_artifact(), + inputs, + gas, + function_idx, + }; + self.sender.send(msg.wrap()?)?; + + loop { + let msg = self.receiver.recv()?.to_msg()?; + match msg { + Message::ExecuteJIT { .. } => unreachable!(), + Message::ExecutionResult { + id: recv_id, + result, + } => { + assert_eq!(recv_id, id, "id mismatch"); + return Ok(result); + } + Message::Ack(recv_id) => { + assert_eq!(recv_id, id, "id mismatch"); + } + Message::Ping => unreachable!(), + Message::Kill => todo!(), + Message::SyscallRequest(request) => match request { + SyscallRequest::GetBlockHash { + block_number, + mut gas, + } => { + let result = handler.get_block_hash(block_number, &mut gas); + self.sender.send( + Message::SyscallAnswer(SyscallAnswer::GetBlockHash { + result, + remaining_gas: gas, + }) + .wrap()?, + )?; + } + SyscallRequest::GetExecutionInfo { mut gas } => { + let result = handler.get_execution_info(&mut gas); + self.sender.send( + Message::SyscallAnswer(SyscallAnswer::GetExecutionInfo { + result, + remaining_gas: gas, + }) + .wrap()?, + )?; + } + SyscallRequest::StorageRead { + address_domain, + address, + mut gas, + } => { + let result = handler.storage_read(address_domain, address, &mut gas); + self.sender.send( + Message::SyscallAnswer(SyscallAnswer::StorageRead { + result, + remaining_gas: gas, + }) + .wrap()?, + )?; + } + SyscallRequest::StorageWrite { + address_domain, + address, + value, + mut gas, + } => { + let result = + handler.storage_write(address_domain, address, value, &mut gas); + self.sender.send( + Message::SyscallAnswer(SyscallAnswer::StorageWrite { + result, + remaining_gas: gas, + }) + .wrap()?, + )?; + } + SyscallRequest::Deploy { + class_hash, + contract_address_salt, + calldata, + deploy_from_zero, + mut gas, + } => { + let result = handler.deploy( + class_hash, + contract_address_salt, + &calldata, + deploy_from_zero, + &mut gas, + ); + self.sender.send( + Message::SyscallAnswer(SyscallAnswer::Deploy { + result, + remaining_gas: gas, + }) + .wrap()?, + )?; + } + SyscallRequest::ReplaceClass { + class_hash, + mut gas, + } => { + let result = handler.replace_class(class_hash, &mut gas); + self.sender.send( + Message::SyscallAnswer(SyscallAnswer::ReplaceClass { + result, + remaining_gas: gas, + }) + .wrap()?, + )?; + } + SyscallRequest::LibraryCall { + class_hash, + function_selector, + calldata, + mut gas, + } => { + let result = handler.library_call( + class_hash, + function_selector, + &calldata, + &mut gas, + ); + self.sender.send( + Message::SyscallAnswer(SyscallAnswer::LibraryCall { + result, + remaining_gas: gas, + }) + .wrap()?, + )?; + } + SyscallRequest::CallContract { + address, + entry_point_selector, + calldata, + mut gas, + } => { + let result = handler.call_contract( + address, + entry_point_selector, + &calldata, + &mut gas, + ); + self.sender.send( + Message::SyscallAnswer(SyscallAnswer::CallContract { + result, + remaining_gas: gas, + }) + .wrap()?, + )?; + } + SyscallRequest::EmitEvent { + keys, + data, + mut gas, + } => { + let result = handler.emit_event(&keys, &data, &mut gas); + self.sender.send( + Message::SyscallAnswer(SyscallAnswer::EmitEvent { + result, + remaining_gas: gas, + }) + .wrap()?, + )?; + } + SyscallRequest::SendMessageToL1 { + to_address, + payload, + mut gas, + } => { + let result = handler.send_message_to_l1(to_address, &payload, &mut gas); + self.sender.send( + Message::SyscallAnswer(SyscallAnswer::SendMessageToL1 { + result, + remaining_gas: gas, + }) + .wrap()?, + )?; + } + SyscallRequest::Keccak { input, mut gas } => { + let result = handler.keccak(&input, &mut gas); + self.sender.send( + Message::SyscallAnswer(SyscallAnswer::Keccak { + result, + remaining_gas: gas, + }) + .wrap()?, + )?; + } + }, + Message::SyscallAnswer(_) => unreachable!(), + } + } + } +} + +impl Drop for IsolatedExecutor { + fn drop(&mut self) { + let _ = self.sender.send(Message::Kill.wrap().unwrap()); + let _ = self.proc.kill(); + } +} diff --git a/src/starknet.rs b/src/starknet.rs index 05f122d1e..20dd9473e 100644 --- a/src/starknet.rs +++ b/src/starknet.rs @@ -3,22 +3,24 @@ #![allow(clippy::type_complexity)] #![allow(dead_code)] +use serde::{Deserialize, Serialize}; use starknet_types_core::felt::Felt; pub type SyscallResult = std::result::Result>; /// Binary representation of a `Felt` (in MLIR). -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(target_arch = "x86_64", repr(C, align(8)))] #[cfg_attr(not(target_arch = "x86_64"), repr(C, align(16)))] pub struct Felt252Abi(pub [u8; 32]); /// Binary representation of a `u256` (in MLIR). // TODO: This shouldn't need to be public. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[cfg_attr(target_arch = "x86_64", repr(C, align(8)))] #[cfg_attr(not(target_arch = "x86_64"), repr(C, align(16)))] pub struct U256(pub [u8; 32]); +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct ExecutionInfo { pub block_info: BlockInfo, pub tx_info: TxInfo, @@ -26,13 +28,14 @@ pub struct ExecutionInfo { pub contract_address: Felt, pub entry_point_selector: Felt, } - +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct BlockInfo { pub block_number: u64, pub block_timestamp: u64, pub sequencer_address: Felt, } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct TxInfo { pub version: Felt, pub account_contract_address: Felt, diff --git a/src/values.rs b/src/values.rs index 08260356c..43d0da26c 100644 --- a/src/values.rs +++ b/src/values.rs @@ -18,6 +18,7 @@ use cairo_lang_sierra::{ }; use educe::Educe; use num_bigint::{BigInt, Sign}; +use serde::{Deserialize, Serialize}; use starknet_types_core::felt::Felt; use std::{alloc::Layout, collections::HashMap, ops::Neg, ptr::NonNull}; @@ -28,7 +29,7 @@ use std::{alloc::Layout, collections::HashMap, ops::Neg, ptr::NonNull}; /// The debug_name field on some variants is `Some` when receiving a [`JitValue`] as a result. /// /// A Boxed value or a non-null Nullable value is returned with it's inner value. -#[derive(Clone, Educe)] +#[derive(Clone, Educe, Serialize, Deserialize)] #[educe(Debug, Eq, PartialEq)] pub enum JitValue { Felt252(#[educe(Debug(method(std::fmt::Display::fmt)))] Felt),