From 9035202bd2635733103b1a5558c113336cbbd6d8 Mon Sep 17 00:00:00 2001 From: Edgar Date: Thu, 11 Jan 2024 13:15:17 +0100 Subject: [PATCH 01/15] start implementing sandboxing --- Cargo.lock | 60 ++++++++++++++++++++------------------- Cargo.toml | 5 ++++ src/bin/cairo-executor.rs | 15 ++++++++++ src/execution_result.rs | 3 +- src/lib.rs | 1 + src/sandbox.rs | 27 ++++++++++++++++++ src/values.rs | 3 +- 7 files changed, 83 insertions(+), 31 deletions(-) create mode 100644 src/bin/cairo-executor.rs create mode 100644 src/sandbox.rs diff --git a/Cargo.lock b/Cargo.lock index 162d10d43..fff022626 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]] @@ -853,6 +853,7 @@ dependencies = [ name = "cairo-native" version = "0.1.0" dependencies = [ + "bincode 1.3.3", "bumpalo", "cairo-felt", "cairo-lang-compiler", @@ -885,6 +886,7 @@ dependencies = [ "num-traits 0.2.17", "pretty_assertions_sorted", "proptest", + "serde", "serde_json", "starknet-types-core", "tempfile", @@ -1037,7 +1039,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1417,7 +1419,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1458,7 +1460,7 @@ checksum = "05aa0010e0e391b9a82dc87bfc4dbee3cb8260eec12ad9d83c12788299fa9093" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -1540,7 +1542,7 @@ checksum = "d4cf186fea4af17825116f72932fe52cce9a13bae39ff63b4dc0cfdb3fb4bde1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -2030,7 +2032,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.43", + "syn 2.0.48", "tblgen", "unindent", ] @@ -2475,7 +2477,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 +2516,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 +2560,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", ] @@ -2866,22 +2868,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 +3069,7 @@ checksum = "af6527b845423542c8a16e060ea1bc43f67229848e7cd4c4d80be994a84220ce" dependencies = [ "starknet-curve 0.4.0", "starknet-ff", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3153,9 +3155,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 +3257,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3267,7 +3269,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "test-case-core", ] @@ -3288,7 +3290,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3433,7 +3435,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3602,7 +3604,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "wasm-bindgen-shared", ] @@ -3624,7 +3626,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3825,7 +3827,7 @@ checksum = "255c4596d41e6916ced49cfafea18727b24d67878fa180ddfd69b9df34fd1726" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.48", ] [[package]] @@ -3845,5 +3847,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..a00772e5f 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,8 @@ libloading = "0.8.1" tracing-subscriber = { version = "0.3", features = [ "env-filter", ], optional = true } +bincode = "1.3.3" +serde = { version = "1.0.195", features = ["derive"] } [dev-dependencies] cairo-felt = "0.8.5" diff --git a/src/bin/cairo-executor.rs b/src/bin/cairo-executor.rs new file mode 100644 index 000000000..3e845a08b --- /dev/null +++ b/src/bin/cairo-executor.rs @@ -0,0 +1,15 @@ +use std::io::Read; + +pub fn main() -> Result<(), std::io::Error> { + let mut stdin = std::io::stdin().lock(); + let mut stdout = std::io::stdout().lock(); + + let mut buffer = Vec::new(); + loop { + stdin.read_to_end(&mut buffer)?; + + // handle message + + // send result + } +} diff --git a/src/execution_result.rs b/src/execution_result.rs index 61c2731f1..96108e643 100644 --- a/src/execution_result.rs +++ b/src/execution_result.rs @@ -2,10 +2,11 @@ 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, Eq, PartialEq, Serialize, Deserialize)] pub struct ExecutionResult { pub remaining_gas: Option, pub return_value: JitValue, 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..179694e67 --- /dev/null +++ b/src/sandbox.rs @@ -0,0 +1,27 @@ +use std::os::unix::process::CommandExt; + +use cairo_lang_sierra::program::Program; +use serde::{Deserialize, Serialize}; + +use crate::{execution_result::ExecutionResult, values::JitValue}; + +#[derive(Debug, Serialize, Deserialize)] +pub enum Message { + ExecuteJIT { + program: Program, + inputs: Vec, + }, + ExecutionResult(ExecutionResult), +} + +pub struct IsolatedExecutor {} + +impl IsolatedExecutor { + pub fn start(&self) -> Result<(), std::io::Error> { + let path = "target/debug/cairo-executor"; + let mut cmd = std::process::Command::new(path); + let proc = cmd.spawn()?; + + Ok(()) + } +} 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), From 2308d10579a33dcf58378d348ccbd000689595a6 Mon Sep 17 00:00:00 2001 From: Edgar Date: Thu, 11 Jan 2024 13:20:08 +0100 Subject: [PATCH 02/15] new --- src/sandbox.rs | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/sandbox.rs b/src/sandbox.rs index 179694e67..3dc55cff3 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -1,4 +1,9 @@ -use std::os::unix::process::CommandExt; +use std::{ + io::Stdout, + os::unix::process::CommandExt, + path::Path, + process::{Child, ChildStdin, ChildStdout, Stdio}, +}; use cairo_lang_sierra::program::Program; use serde::{Deserialize, Serialize}; @@ -14,14 +19,25 @@ pub enum Message { ExecutionResult(ExecutionResult), } -pub struct IsolatedExecutor {} +pub struct IsolatedExecutor { + proc: Child, + stdin: ChildStdin, + stdout: ChildStdout, +} impl IsolatedExecutor { - pub fn start(&self) -> Result<(), std::io::Error> { - let path = "target/debug/cairo-executor"; - let mut cmd = std::process::Command::new(path); - let proc = cmd.spawn()?; + // "target/debug/cairo-executor" + pub fn new(executor_path: &Path) -> Result { + let mut cmd = std::process::Command::new(executor_path); + cmd.stdin(Stdio::piped()).stdout(Stdio::piped()); + let mut proc = cmd.spawn()?; + let stdin = proc.stdin.take().unwrap(); + let stdout = proc.stdout.take().unwrap(); - Ok(()) + Ok(Self { + proc, + stdin, + stdout, + }) } } From 82670dd51745c18447f4ad151bc30428c95ac4c9 Mon Sep 17 00:00:00 2001 From: Edgar Date: Thu, 11 Jan 2024 15:56:32 +0100 Subject: [PATCH 03/15] progress --- src/bin/cairo-executor.rs | 21 ++++++++++++++++++++- src/sandbox.rs | 3 +++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/bin/cairo-executor.rs b/src/bin/cairo-executor.rs index 3e845a08b..5aa599552 100644 --- a/src/bin/cairo-executor.rs +++ b/src/bin/cairo-executor.rs @@ -1,13 +1,32 @@ -use std::io::Read; +use std::io::{Read, Write}; + +use cairo_native::sandbox::Message; pub fn main() -> Result<(), std::io::Error> { let mut stdin = std::io::stdin().lock(); let mut stdout = std::io::stdout().lock(); + let mut send_msg = |msg: Message| { + let bytes = bincode::serialize(&msg).expect("failed to serialize"); + stdout.write_all(&bytes).unwrap(); + }; + let mut buffer = Vec::new(); loop { stdin.read_to_end(&mut buffer)?; + let message: Message = bincode::deserialize(&buffer).expect("failed"); + + match message { + Message::ExecuteJIT { program, inputs } => { + send_msg(Message::Ack); + } + Message::ExecutionResult(_) => todo!(), + Message::Ping => send_msg(Message::Ack), + Message::Ack => {} + } + buffer.clear(); + // handle message // send result diff --git a/src/sandbox.rs b/src/sandbox.rs index 3dc55cff3..9cef03281 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -17,8 +17,11 @@ pub enum Message { inputs: Vec, }, ExecutionResult(ExecutionResult), + Ping, + Ack, } +#[derive(Debug)] pub struct IsolatedExecutor { proc: Child, stdin: ChildStdin, From 4c8c25f01f969f4b8313c036b14450d9f1b79d7e Mon Sep 17 00:00:00 2001 From: Edgar Date: Thu, 11 Jan 2024 17:12:27 +0100 Subject: [PATCH 04/15] prog --- Cargo.lock | 12 ++++- Cargo.toml | 3 +- examples/sandbox.rs | 29 ++++++++++++ src/bin/cairo-executor.rs | 63 ++++++++++++++++++++----- src/sandbox.rs | 99 +++++++++++++++++++++++++++++++++++---- 5 files changed, 183 insertions(+), 23 deletions(-) create mode 100644 examples/sandbox.rs diff --git a/Cargo.lock b/Cargo.lock index fff022626..eb5e3d832 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -853,7 +853,6 @@ dependencies = [ name = "cairo-native" version = "0.1.0" dependencies = [ - "bincode 1.3.3", "bumpalo", "cairo-felt", "cairo-lang-compiler", @@ -894,6 +893,7 @@ dependencies = [ "thiserror", "tracing", "tracing-subscriber", + "uuid", "walkdir", ] @@ -3546,6 +3546,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" diff --git a/Cargo.toml b/Cargo.toml index a00772e5f..5e5a1b211 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,8 +66,9 @@ libloading = "0.8.1" tracing-subscriber = { version = "0.3", features = [ "env-filter", ], optional = true } -bincode = "1.3.3" serde = { version = "1.0.195", features = ["derive"] } +uuid = { version = "1.6.1", features = ["serde", "v4"] } +serde_json = "1.0" [dev-dependencies] cairo-felt = "0.8.5" diff --git a/examples/sandbox.rs b/examples/sandbox.rs new file mode 100644 index 000000000..5befb73f4 --- /dev/null +++ b/examples/sandbox.rs @@ -0,0 +1,29 @@ +use cairo_native::{sandbox::IsolatedExecutor, values::JitValue}; +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 program_path = Path::new("programs/echo.cairo"); + + // Compile the cairo program to sierra. + let sierra_program = (*cairo_native::utils::cairo_to_sierra(program_path)).clone(); + + let mut sandbox = IsolatedExecutor::new(Path::new("/Users/edgar/Documents/cairo_sierra_to_mlir/target/debug/cairo-executor")).unwrap(); + + let result = sandbox + .run_program( + sierra_program, + vec![JitValue::Felt252(1.into())], + "echo::echo::main".to_string(), + ) + .unwrap(); + dbg!(result); +} diff --git a/src/bin/cairo-executor.rs b/src/bin/cairo-executor.rs index 5aa599552..49cccd3a3 100644 --- a/src/bin/cairo-executor.rs +++ b/src/bin/cairo-executor.rs @@ -1,34 +1,71 @@ use std::io::{Read, Write}; -use cairo_native::sandbox::Message; +use cairo_native::{ + context::NativeContext, executor::JitNativeExecutor, sandbox::Message, utils::find_entry_point, +}; + +pub fn main() -> Result<(), Box> { + let pid = std::process::id(); + + let mut log_file = std::fs::File::create(format!("cairo-executor.{pid}.log"))?; -pub fn main() -> Result<(), std::io::Error> { let mut stdin = std::io::stdin().lock(); let mut stdout = std::io::stdout().lock(); let mut send_msg = |msg: Message| { - let bytes = bincode::serialize(&msg).expect("failed to serialize"); + let bytes = serde_json::to_vec(&msg).unwrap(); stdout.write_all(&bytes).unwrap(); }; + let native_context = NativeContext::new(); + writeln!(log_file, "initialized native context")?; + log_file.flush()?; + let mut buffer = Vec::new(); loop { + writeln!(log_file, "waiting fo message")?; + log_file.flush()?; stdin.read_to_end(&mut buffer)?; - let message: Message = bincode::deserialize(&buffer).expect("failed"); + let message: Message = serde_json::from_slice(&buffer)?; + writeln!(log_file, "got message: {:?}", message)?; + log_file.flush()?; match message { - Message::ExecuteJIT { program, inputs } => { - send_msg(Message::Ack); + Message::ExecuteJIT { + id, + program, + inputs, + entry_point, + } => { + send_msg(Message::Ack(id)); + writeln!(log_file, "sent ack: {:?}", id)?; + log_file.flush()?; + let program = program.into_v1()?.program; + let native_program = native_context.compile(&program)?; + + // Call the echo function from the contract using the generated wrapper. + + let entry_point_fn = find_entry_point(&program, &entry_point).unwrap(); + + let fn_id = &entry_point_fn.id; + + let native_executor = + JitNativeExecutor::from_native_module(native_program, Default::default()); + + let result = native_executor.invoke_dynamic(fn_id, &inputs, None, None)?; + + writeln!(log_file, "invoked with result: {:?}", result)?; + log_file.flush()?; + + send_msg(Message::ExecutionResult { id, result }); + + writeln!(log_file, "sent result msg")?; + log_file.flush()?; } - Message::ExecutionResult(_) => todo!(), - Message::Ping => send_msg(Message::Ack), - Message::Ack => {} + Message::ExecutionResult { .. } => {} + Message::Ack(_) => {} } buffer.clear(); - - // handle message - - // send result } } diff --git a/src/sandbox.rs b/src/sandbox.rs index 9cef03281..14bae9cdf 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -1,36 +1,43 @@ use std::{ - io::Stdout, - os::unix::process::CommandExt, + io::{Read, Write}, path::Path, process::{Child, ChildStdin, ChildStdout, Stdio}, }; -use cairo_lang_sierra::program::Program; +use cairo_lang_sierra::program::{Program, VersionedProgram}; use serde::{Deserialize, Serialize}; +use uuid::Uuid; use crate::{execution_result::ExecutionResult, values::JitValue}; #[derive(Debug, Serialize, Deserialize)] pub enum Message { ExecuteJIT { - program: Program, + id: Uuid, + program: VersionedProgram, inputs: Vec, + entry_point: String, + }, + ExecutionResult { + id: Uuid, + result: ExecutionResult, }, - ExecutionResult(ExecutionResult), - Ping, - Ack, + Ack(Uuid), } -#[derive(Debug)] +pub type OnResult = Box; + pub struct IsolatedExecutor { proc: Child, stdin: ChildStdin, stdout: ChildStdout, + read_buffer: [u8; 512], } impl IsolatedExecutor { // "target/debug/cairo-executor" pub fn new(executor_path: &Path) -> Result { + println!("creating executor with: {:?}", executor_path); let mut cmd = std::process::Command::new(executor_path); cmd.stdin(Stdio::piped()).stdout(Stdio::piped()); let mut proc = cmd.spawn()?; @@ -41,6 +48,82 @@ impl IsolatedExecutor { proc, stdin, stdout, + read_buffer: [0; 512] }) } + + pub fn run_program( + &mut self, + program: Program, + inputs: Vec, + entry_point: String, + ) -> Result> { + println!("running program"); + let id = Uuid::new_v4(); + + let msg = Message::ExecuteJIT { + id, + program: program.into_artifact(), + inputs, + entry_point + }; + self.send_msg(msg)?; + + let ack = self.read_msg()?; + + if let Message::Ack(recv_id) = ack { + assert_eq!(recv_id, id, "id mismatch"); + } else { + // should match + panic!("id mismatch"); + } + + let result = self.read_msg()?; + + if let Message::ExecutionResult { + id: recv_id, + result, + } = result + { + assert_eq!(recv_id, id, "id mismatch"); + Ok(result) + } else { + panic!("wrong msg"); + } + } + + fn read_msg(&mut self) -> Result> { + let mut msg = Vec::new(); + // check if we have a partial msg before + for x in self.read_buffer.iter_mut() { + if *x != b'\0' { + msg.push(x); + } + *x = b'\0'; + } + let n = self.stdout.read(&mut self.read_buffer)?; + + for x in &mut self.read_buffer[0..n] { + if *x != b'\0' { + msg.push(x); + } + } + + let msg = serde_json::from_slice(&buf)?; + Ok(msg) + } + + fn send_msg(&mut self, msg: Message) -> Result<(), Box> { + let msg = serde_json::to_vec(&msg).unwrap(); + self.stdin.write_all(&msg)?; + self.stdin.write_all(&[b'\0'])?; + self.stdin.flush()?; + Ok(()) + } +} + +impl Drop for IsolatedExecutor { + fn drop(&mut self) { + let _ = self.proc.kill(); + } } From b958664c4837f2a38e9205c8fd126722ef4a2431 Mon Sep 17 00:00:00 2001 From: Edgar Date: Thu, 11 Jan 2024 17:12:34 +0100 Subject: [PATCH 05/15] prog --- src/sandbox.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sandbox.rs b/src/sandbox.rs index 14bae9cdf..130842685 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -106,6 +106,8 @@ impl IsolatedExecutor { for x in &mut self.read_buffer[0..n] { if *x != b'\0' { msg.push(x); + } else { + } } From 2714b720ab01b946bf186dd2866de0314e4b3f88 Mon Sep 17 00:00:00 2001 From: Edgar Luque Date: Fri, 12 Jan 2024 14:38:54 +0100 Subject: [PATCH 06/15] progress --- Cargo.lock | 64 +++++++++++++++++++++-- Cargo.toml | 1 + examples/sandbox.rs | 5 +- src/bin/cairo-executor.rs | 52 ++++++++++++------- src/sandbox.rs | 106 +++++++++++++++++++++----------------- 5 files changed, 155 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb5e3d832..f63743e63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -873,6 +873,7 @@ dependencies = [ "criterion", "educe", "id-arena", + "ipc-channel", "itertools 0.12.0", "lambdaworks-math 0.1.3", "lazy_static", @@ -1180,6 +1181,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" @@ -1206,12 +1216,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" @@ -1761,6 +1768,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" @@ -2086,6 +2113,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" @@ -2829,6 +2868,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" @@ -3700,6 +3745,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" diff --git a/Cargo.toml b/Cargo.toml index 5e5a1b211..7b5e5617c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,7 @@ tracing-subscriber = { version = "0.3", features = [ 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"] } [dev-dependencies] cairo-felt = "0.8.5" diff --git a/examples/sandbox.rs b/examples/sandbox.rs index 5befb73f4..933e81bc3 100644 --- a/examples/sandbox.rs +++ b/examples/sandbox.rs @@ -16,7 +16,10 @@ fn main() { // Compile the cairo program to sierra. let sierra_program = (*cairo_native::utils::cairo_to_sierra(program_path)).clone(); - let mut sandbox = IsolatedExecutor::new(Path::new("/Users/edgar/Documents/cairo_sierra_to_mlir/target/debug/cairo-executor")).unwrap(); + let sandbox = IsolatedExecutor::new(Path::new( + "/data2/edgar/work/native/target/debug/cairo-executor", + )) + .unwrap(); let result = sandbox .run_program( diff --git a/src/bin/cairo-executor.rs b/src/bin/cairo-executor.rs index 49cccd3a3..e12a28901 100644 --- a/src/bin/cairo-executor.rs +++ b/src/bin/cairo-executor.rs @@ -1,35 +1,42 @@ -use std::io::{Read, Write}; +use std::io::Write; use cairo_native::{ - context::NativeContext, executor::JitNativeExecutor, sandbox::Message, utils::find_entry_point, + context::NativeContext, + executor::JitNativeExecutor, + sandbox::{Message, WrappedMessage}, + utils::find_entry_point, }; +use ipc_channel::ipc::{IpcOneShotServer, IpcSender}; pub fn main() -> Result<(), Box> { - let pid = std::process::id(); + let mut args = std::env::args(); - let mut log_file = std::fs::File::create(format!("cairo-executor.{pid}.log"))?; + if args.len() < 2 { + panic!("missing server ipc name"); + } - let mut stdin = std::io::stdin().lock(); - let mut stdout = std::io::stdout().lock(); + let pid = std::process::id(); + let mut log_file = std::fs::File::create(format!("cairo-executor.{pid}.log"))?; - let mut send_msg = |msg: Message| { - let bytes = serde_json::to_vec(&msg).unwrap(); - stdout.write_all(&bytes).unwrap(); - }; + 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()?)?; + writeln!(log_file, "connected to {server:?}")?; + let (receiver, msg) = sv.accept()?; + writeln!(log_file, "accepted {receiver:?}")?; + assert_eq!(msg, Message::Ping.wrap()?); let native_context = NativeContext::new(); writeln!(log_file, "initialized native context")?; log_file.flush()?; - let mut buffer = Vec::new(); loop { - writeln!(log_file, "waiting fo message")?; - log_file.flush()?; - stdin.read_to_end(&mut buffer)?; + writeln!(log_file, "waiting for message")?; - let message: Message = serde_json::from_slice(&buffer)?; + let message: Message = receiver.recv()?.to_msg()?; writeln!(log_file, "got message: {:?}", message)?; - log_file.flush()?; match message { Message::ExecuteJIT { @@ -38,7 +45,7 @@ pub fn main() -> Result<(), Box> { inputs, entry_point, } => { - send_msg(Message::Ack(id)); + sender.send(Message::Ack(id).wrap()?)?; writeln!(log_file, "sent ack: {:?}", id)?; log_file.flush()?; let program = program.into_v1()?.program; @@ -58,14 +65,21 @@ pub fn main() -> Result<(), Box> { writeln!(log_file, "invoked with result: {:?}", result)?; log_file.flush()?; - send_msg(Message::ExecutionResult { id, result }); + sender.send(Message::ExecutionResult { id, result }.wrap()?)?; writeln!(log_file, "sent result msg")?; log_file.flush()?; } Message::ExecutionResult { .. } => {} Message::Ack(_) => {} + Message::Ping => { + sender.send(Message::Ping.wrap()?)?; + } + Message::Kill => { + break; + } } - buffer.clear(); } + + Ok(()) } diff --git a/src/sandbox.rs b/src/sandbox.rs index 130842685..9e93a00cc 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -1,16 +1,17 @@ use std::{ - io::{Read, Write}, + io::{BufRead, BufReader}, path::Path, - process::{Child, ChildStdin, ChildStdout, Stdio}, + process::{Child, Stdio}, }; use cairo_lang_sierra::program::{Program, VersionedProgram}; +use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender}; use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::{execution_result::ExecutionResult, values::JitValue}; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, PartialEq)] pub enum Message { ExecuteJIT { id: Uuid, @@ -23,37 +24,76 @@ pub enum Message { result: ExecutionResult, }, Ack(Uuid), + Ping, + Kill, +} + +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 type OnResult = Box; pub struct IsolatedExecutor { proc: Child, - stdin: ChildStdin, - stdout: ChildStdout, - read_buffer: [u8; 512], + sender: IpcSender, + receiver: IpcReceiver, } impl IsolatedExecutor { // "target/debug/cairo-executor" pub fn new(executor_path: &Path) -> Result { + let (server, server_name) = IpcOneShotServer::new().unwrap(); println!("creating executor with: {:?}", executor_path); + let mut cmd = std::process::Command::new(executor_path); - cmd.stdin(Stdio::piped()).stdout(Stdio::piped()); - let mut proc = cmd.spawn()?; - let stdin = proc.stdin.take().unwrap(); + 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"); + println!("accepted receiver {receiver:?} wit msg {msg:?}"); + // then we connect + println!("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, - stdin, - stdout, - read_buffer: [0; 512] + sender, + receiver, }) } pub fn run_program( - &mut self, + &self, program: Program, inputs: Vec, entry_point: String, @@ -65,11 +105,11 @@ impl IsolatedExecutor { id, program: program.into_artifact(), inputs, - entry_point + entry_point, }; - self.send_msg(msg)?; + self.sender.send(msg.wrap()?)?; - let ack = self.read_msg()?; + let ack = self.receiver.recv()?.to_msg()?; if let Message::Ack(recv_id) = ack { assert_eq!(recv_id, id, "id mismatch"); @@ -78,7 +118,7 @@ impl IsolatedExecutor { panic!("id mismatch"); } - let result = self.read_msg()?; + let result = self.receiver.recv()?.to_msg()?; if let Message::ExecutionResult { id: recv_id, @@ -91,41 +131,11 @@ impl IsolatedExecutor { panic!("wrong msg"); } } - - fn read_msg(&mut self) -> Result> { - let mut msg = Vec::new(); - // check if we have a partial msg before - for x in self.read_buffer.iter_mut() { - if *x != b'\0' { - msg.push(x); - } - *x = b'\0'; - } - let n = self.stdout.read(&mut self.read_buffer)?; - - for x in &mut self.read_buffer[0..n] { - if *x != b'\0' { - msg.push(x); - } else { - - } - } - - let msg = serde_json::from_slice(&buf)?; - Ok(msg) - } - - fn send_msg(&mut self, msg: Message) -> Result<(), Box> { - let msg = serde_json::to_vec(&msg).unwrap(); - self.stdin.write_all(&msg)?; - self.stdin.write_all(&[b'\0'])?; - self.stdin.flush()?; - Ok(()) - } } impl Drop for IsolatedExecutor { fn drop(&mut self) { + let _ = self.sender.send(Message::Kill.wrap().unwrap()); let _ = self.proc.kill(); } } From fa82452f182e8afe779618e2e96925c895dbace5 Mon Sep 17 00:00:00 2001 From: Edgar Date: Thu, 18 Jan 2024 14:55:11 +0100 Subject: [PATCH 07/15] path --- cairo-executor.82073.log | 9 +++++++++ examples/sandbox.rs | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 cairo-executor.82073.log diff --git a/cairo-executor.82073.log b/cairo-executor.82073.log new file mode 100644 index 000000000..bd159ec2c --- /dev/null +++ b/cairo-executor.82073.log @@ -0,0 +1,9 @@ +connected to "org.rust-lang.ipc-channel.2890018780051221184" +accepted IpcReceiver { os_receiver: OsIpcReceiver { port: Cell { value: 7171 } }, phantom: PhantomData } +initialized native context +waiting for message +got message: ExecuteJIT { id: b74b973c-d356-4cbd-b80a-ec326cfa9a71, program: V1 { version: Version, program: ProgramArtifact { program: Program { type_declarations: [TypeDeclaration { id: ConcreteTypeId { id: 0, debug_name: Some("felt252") }, long_id: ConcreteTypeLongId { generic_id: GenericTypeId("felt252"), generic_args: [] }, declared_type_info: Some(DeclaredTypeInfo { storable: true, droppable: true, duplicatable: true, zero_sized: false }) }, TypeDeclaration { id: ConcreteTypeId { id: 1, debug_name: Some("echo::echo::MyStruct") }, long_id: ConcreteTypeLongId { generic_id: GenericTypeId("Struct"), generic_args: [UserType(UserTypeId { id: 1516066238726593793190034952329559266494809698773362064597592853217008243939, debug_name: Some("echo::echo::MyStruct") }), Type(ConcreteTypeId { id: 0, debug_name: Some("felt252") })] }, declared_type_info: Some(DeclaredTypeInfo { storable: true, droppable: true, duplicatable: true, zero_sized: false }) }, TypeDeclaration { id: ConcreteTypeId { id: 2, debug_name: Some("u64") }, long_id: ConcreteTypeLongId { generic_id: GenericTypeId("u64"), generic_args: [] }, declared_type_info: Some(DeclaredTypeInfo { storable: true, droppable: true, duplicatable: true, zero_sized: false }) }, TypeDeclaration { id: ConcreteTypeId { id: 3, debug_name: Some("echo::echo::MyEnum") }, long_id: ConcreteTypeLongId { generic_id: GenericTypeId("Enum"), generic_args: [UserType(UserTypeId { id: 1581246286119682515464603913030972296498348532698076640711464977133251153513, debug_name: Some("echo::echo::MyEnum") }), Type(ConcreteTypeId { id: 1, debug_name: Some("echo::echo::MyStruct") }), Type(ConcreteTypeId { id: 2, debug_name: Some("u64") })] }, declared_type_info: Some(DeclaredTypeInfo { storable: true, droppable: true, duplicatable: true, zero_sized: false }) }], libfunc_declarations: [LibfuncDeclaration { id: ConcreteLibfuncId { id: 1, debug_name: Some("struct_construct") }, long_id: ConcreteLibfuncLongId { generic_id: GenericLibfuncId("struct_construct"), generic_args: [Type(ConcreteTypeId { id: 1, debug_name: Some("echo::echo::MyStruct") })] } }, LibfuncDeclaration { id: ConcreteLibfuncId { id: 0, debug_name: Some("enum_init") }, long_id: ConcreteLibfuncLongId { generic_id: GenericLibfuncId("enum_init"), generic_args: [Type(ConcreteTypeId { id: 3, debug_name: Some("echo::echo::MyEnum") }), Value(0)] } }, LibfuncDeclaration { id: ConcreteLibfuncId { id: 2, debug_name: Some("store_temp") }, long_id: ConcreteLibfuncLongId { generic_id: GenericLibfuncId("store_temp"), generic_args: [Type(ConcreteTypeId { id: 3, debug_name: Some("echo::echo::MyEnum") })] } }], statements: [Invocation(GenInvocation { libfunc_id: ConcreteLibfuncId { id: 1, debug_name: Some("struct_construct") }, args: [VarId { id: 0, debug_name: None }], branches: [GenBranchInfo { target: Fallthrough, results: [VarId { id: 1, debug_name: None }] }] }), Invocation(GenInvocation { libfunc_id: ConcreteLibfuncId { id: 0, debug_name: Some("enum_init") }, args: [VarId { id: 1, debug_name: None }], branches: [GenBranchInfo { target: Fallthrough, results: [VarId { id: 2, debug_name: None }] }] }), Invocation(GenInvocation { libfunc_id: ConcreteLibfuncId { id: 2, debug_name: Some("store_temp") }, args: [VarId { id: 2, debug_name: None }], branches: [GenBranchInfo { target: Fallthrough, results: [VarId { id: 2, debug_name: None }] }] }), Return([VarId { id: 2, debug_name: None }])], funcs: [GenFunction { id: FunctionId { id: 0, debug_name: Some("echo::echo::main") }, signature: FunctionSignature { param_types: [ConcreteTypeId { id: 0, debug_name: Some("felt252") }], ret_types: [ConcreteTypeId { id: 3, debug_name: Some("echo::echo::MyEnum") }] }, params: [Param { id: VarId { id: 0, debug_name: None }, ty: ConcreteTypeId { id: 0, debug_name: Some("felt252") } }], entry_point: StatementIdx(0) }] }, debug_info: None } }, inputs: [Felt252(1)], entry_point: "echo::echo::main" } +sent ack: b74b973c-d356-4cbd-b80a-ec326cfa9a71 +invoked with result: ExecutionResult { remaining_gas: None, return_value: Enum { tag: 0, value: Struct { fields: [Felt252(1)], debug_name: Some("echo::echo::MyStruct") }, debug_name: Some("echo::echo::MyEnum") } } +sent result msg +waiting for message diff --git a/examples/sandbox.rs b/examples/sandbox.rs index 933e81bc3..aae5737fd 100644 --- a/examples/sandbox.rs +++ b/examples/sandbox.rs @@ -16,10 +16,10 @@ fn main() { // Compile the cairo program to sierra. let sierra_program = (*cairo_native::utils::cairo_to_sierra(program_path)).clone(); - let sandbox = IsolatedExecutor::new(Path::new( - "/data2/edgar/work/native/target/debug/cairo-executor", - )) - .unwrap(); + let path = std::env::current_dir() + .unwrap() + .join("target/debug/cairo-executor"); + let sandbox = IsolatedExecutor::new(&path).unwrap(); let result = sandbox .run_program( From 05f9a7f235ee4940d8c1a440d91cb0ba2b932190 Mon Sep 17 00:00:00 2001 From: Edgar Date: Thu, 18 Jan 2024 15:24:10 +0100 Subject: [PATCH 08/15] better logging --- .gitignore | 1 + Cargo.lock | 21 ++++++++++++++++---- Cargo.toml | 1 + cairo-executor.82073.log | 9 --------- examples/sandbox.rs | 13 +++++++++++-- src/bin/cairo-executor.rs | 41 +++++++++++++++++++++++---------------- src/sandbox.rs | 8 ++++---- 7 files changed, 58 insertions(+), 36 deletions(-) delete mode 100644 cairo-executor.82073.log 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 f63743e63..a55d7fbe4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -893,6 +893,7 @@ dependencies = [ "test-case", "thiserror", "tracing", + "tracing-appender", "tracing-subscriber", "uuid", "walkdir", @@ -3472,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" @@ -3495,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", @@ -3506,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", diff --git a/Cargo.toml b/Cargo.toml index 7b5e5617c..84d729678 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ 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/cairo-executor.82073.log b/cairo-executor.82073.log deleted file mode 100644 index bd159ec2c..000000000 --- a/cairo-executor.82073.log +++ /dev/null @@ -1,9 +0,0 @@ -connected to "org.rust-lang.ipc-channel.2890018780051221184" -accepted IpcReceiver { os_receiver: OsIpcReceiver { port: Cell { value: 7171 } }, phantom: PhantomData } -initialized native context -waiting for message -got message: ExecuteJIT { id: b74b973c-d356-4cbd-b80a-ec326cfa9a71, program: V1 { version: Version, program: ProgramArtifact { program: Program { type_declarations: [TypeDeclaration { id: ConcreteTypeId { id: 0, debug_name: Some("felt252") }, long_id: ConcreteTypeLongId { generic_id: GenericTypeId("felt252"), generic_args: [] }, declared_type_info: Some(DeclaredTypeInfo { storable: true, droppable: true, duplicatable: true, zero_sized: false }) }, TypeDeclaration { id: ConcreteTypeId { id: 1, debug_name: Some("echo::echo::MyStruct") }, long_id: ConcreteTypeLongId { generic_id: GenericTypeId("Struct"), generic_args: [UserType(UserTypeId { id: 1516066238726593793190034952329559266494809698773362064597592853217008243939, debug_name: Some("echo::echo::MyStruct") }), Type(ConcreteTypeId { id: 0, debug_name: Some("felt252") })] }, declared_type_info: Some(DeclaredTypeInfo { storable: true, droppable: true, duplicatable: true, zero_sized: false }) }, TypeDeclaration { id: ConcreteTypeId { id: 2, debug_name: Some("u64") }, long_id: ConcreteTypeLongId { generic_id: GenericTypeId("u64"), generic_args: [] }, declared_type_info: Some(DeclaredTypeInfo { storable: true, droppable: true, duplicatable: true, zero_sized: false }) }, TypeDeclaration { id: ConcreteTypeId { id: 3, debug_name: Some("echo::echo::MyEnum") }, long_id: ConcreteTypeLongId { generic_id: GenericTypeId("Enum"), generic_args: [UserType(UserTypeId { id: 1581246286119682515464603913030972296498348532698076640711464977133251153513, debug_name: Some("echo::echo::MyEnum") }), Type(ConcreteTypeId { id: 1, debug_name: Some("echo::echo::MyStruct") }), Type(ConcreteTypeId { id: 2, debug_name: Some("u64") })] }, declared_type_info: Some(DeclaredTypeInfo { storable: true, droppable: true, duplicatable: true, zero_sized: false }) }], libfunc_declarations: [LibfuncDeclaration { id: ConcreteLibfuncId { id: 1, debug_name: Some("struct_construct") }, long_id: ConcreteLibfuncLongId { generic_id: GenericLibfuncId("struct_construct"), generic_args: [Type(ConcreteTypeId { id: 1, debug_name: Some("echo::echo::MyStruct") })] } }, LibfuncDeclaration { id: ConcreteLibfuncId { id: 0, debug_name: Some("enum_init") }, long_id: ConcreteLibfuncLongId { generic_id: GenericLibfuncId("enum_init"), generic_args: [Type(ConcreteTypeId { id: 3, debug_name: Some("echo::echo::MyEnum") }), Value(0)] } }, LibfuncDeclaration { id: ConcreteLibfuncId { id: 2, debug_name: Some("store_temp") }, long_id: ConcreteLibfuncLongId { generic_id: GenericLibfuncId("store_temp"), generic_args: [Type(ConcreteTypeId { id: 3, debug_name: Some("echo::echo::MyEnum") })] } }], statements: [Invocation(GenInvocation { libfunc_id: ConcreteLibfuncId { id: 1, debug_name: Some("struct_construct") }, args: [VarId { id: 0, debug_name: None }], branches: [GenBranchInfo { target: Fallthrough, results: [VarId { id: 1, debug_name: None }] }] }), Invocation(GenInvocation { libfunc_id: ConcreteLibfuncId { id: 0, debug_name: Some("enum_init") }, args: [VarId { id: 1, debug_name: None }], branches: [GenBranchInfo { target: Fallthrough, results: [VarId { id: 2, debug_name: None }] }] }), Invocation(GenInvocation { libfunc_id: ConcreteLibfuncId { id: 2, debug_name: Some("store_temp") }, args: [VarId { id: 2, debug_name: None }], branches: [GenBranchInfo { target: Fallthrough, results: [VarId { id: 2, debug_name: None }] }] }), Return([VarId { id: 2, debug_name: None }])], funcs: [GenFunction { id: FunctionId { id: 0, debug_name: Some("echo::echo::main") }, signature: FunctionSignature { param_types: [ConcreteTypeId { id: 0, debug_name: Some("felt252") }], ret_types: [ConcreteTypeId { id: 3, debug_name: Some("echo::echo::MyEnum") }] }, params: [Param { id: VarId { id: 0, debug_name: None }, ty: ConcreteTypeId { id: 0, debug_name: Some("felt252") } }], entry_point: StatementIdx(0) }] }, debug_info: None } }, inputs: [Felt252(1)], entry_point: "echo::echo::main" } -sent ack: b74b973c-d356-4cbd-b80a-ec326cfa9a71 -invoked with result: ExecutionResult { remaining_gas: None, return_value: Enum { tag: 0, value: Struct { fields: [Felt252(1)], debug_name: Some("echo::echo::MyStruct") }, debug_name: Some("echo::echo::MyEnum") } } -sent result msg -waiting for message diff --git a/examples/sandbox.rs b/examples/sandbox.rs index aae5737fd..9e25474a7 100644 --- a/examples/sandbox.rs +++ b/examples/sandbox.rs @@ -23,10 +23,19 @@ fn main() { let result = sandbox .run_program( - sierra_program, + sierra_program.clone(), vec![JitValue::Felt252(1.into())], "echo::echo::main".to_string(), ) .unwrap(); - dbg!(result); + println!("{:#?}", result); + + let result = sandbox + .run_program( + sierra_program, + vec![JitValue::Felt252(2.into())], + "echo::echo::main".to_string(), + ) + .unwrap(); + println!("{:#?}", result); } diff --git a/src/bin/cairo-executor.rs b/src/bin/cairo-executor.rs index e12a28901..e08391fdf 100644 --- a/src/bin/cairo-executor.rs +++ b/src/bin/cairo-executor.rs @@ -1,4 +1,4 @@ -use std::io::Write; +use std::path::PathBuf; use cairo_native::{ context::NativeContext, @@ -11,32 +11,42 @@ use ipc_channel::ipc::{IpcOneShotServer, IpcSender}; 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 { - panic!("missing server ipc name"); + tracing::error!("missing server ipc name"); + std::process::exit(1); } - let pid = std::process::id(); - let mut log_file = std::fs::File::create(format!("cairo-executor.{pid}.log"))?; - 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()?)?; - writeln!(log_file, "connected to {server:?}")?; + tracing::info!("connected to {server:?}"); let (receiver, msg) = sv.accept()?; - writeln!(log_file, "accepted {receiver:?}")?; + tracing::info!("accepted {receiver:?}"); assert_eq!(msg, Message::Ping.wrap()?); let native_context = NativeContext::new(); - writeln!(log_file, "initialized native context")?; - log_file.flush()?; + tracing::info!("initialized native context"); loop { - writeln!(log_file, "waiting for message")?; + tracing::info!("waiting for message"); let message: Message = receiver.recv()?.to_msg()?; - writeln!(log_file, "got message: {:?}", message)?; + tracing::info!("got message: {:?}", message); match message { Message::ExecuteJIT { @@ -46,8 +56,7 @@ pub fn main() -> Result<(), Box> { entry_point, } => { sender.send(Message::Ack(id).wrap()?)?; - writeln!(log_file, "sent ack: {:?}", id)?; - log_file.flush()?; + tracing::info!("sent ack: {:?}", id); let program = program.into_v1()?.program; let native_program = native_context.compile(&program)?; @@ -62,13 +71,11 @@ pub fn main() -> Result<(), Box> { let result = native_executor.invoke_dynamic(fn_id, &inputs, None, None)?; - writeln!(log_file, "invoked with result: {:?}", result)?; - log_file.flush()?; + tracing::info!("invoked with result: {:?}", result); sender.send(Message::ExecutionResult { id, result }.wrap()?)?; - writeln!(log_file, "sent result msg")?; - log_file.flush()?; + tracing::info!("sent result msg"); } Message::ExecutionResult { .. } => {} Message::Ack(_) => {} diff --git a/src/sandbox.rs b/src/sandbox.rs index 9e93a00cc..073e3b699 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -67,7 +67,7 @@ impl IsolatedExecutor { // "target/debug/cairo-executor" pub fn new(executor_path: &Path) -> Result { let (server, server_name) = IpcOneShotServer::new().unwrap(); - println!("creating executor with: {:?}", executor_path); + tracing::debug!("creating executor with: {:?}", executor_path); let mut cmd = std::process::Command::new(executor_path); cmd.stdout(Stdio::piped()); @@ -79,9 +79,9 @@ impl IsolatedExecutor { // first we accept the connection let (receiver, msg) = server.accept().expect("failed to accept receiver"); - println!("accepted receiver {receiver:?} wit msg {msg:?}"); + tracing::debug!("accepted receiver {receiver:?} wit msg {msg:?}"); // then we connect - println!("connecting to {client_name}"); + 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(); @@ -98,7 +98,7 @@ impl IsolatedExecutor { inputs: Vec, entry_point: String, ) -> Result> { - println!("running program"); + tracing::debug!("running program"); let id = Uuid::new_v4(); let msg = Message::ExecuteJIT { From 3f51bb618d103197de3f240ac8e8a3956781f139 Mon Sep 17 00:00:00 2001 From: Edgar Date: Thu, 18 Jan 2024 15:25:23 +0100 Subject: [PATCH 09/15] better --- src/bin/cairo-executor.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bin/cairo-executor.rs b/src/bin/cairo-executor.rs index e08391fdf..d7999e19d 100644 --- a/src/bin/cairo-executor.rs +++ b/src/bin/cairo-executor.rs @@ -60,8 +60,6 @@ pub fn main() -> Result<(), Box> { let program = program.into_v1()?.program; let native_program = native_context.compile(&program)?; - // Call the echo function from the contract using the generated wrapper. - let entry_point_fn = find_entry_point(&program, &entry_point).unwrap(); let fn_id = &entry_point_fn.id; From be8323709a63715d441125fc1adfb2ef49e2f88c Mon Sep 17 00:00:00 2001 From: Edgar Luque Date: Fri, 19 Jan 2024 14:50:09 +0100 Subject: [PATCH 10/15] progress --- examples/sandbox.rs | 2 + src/bin/cairo-executor.rs | 342 +++++++++++++++++++++++++++++++++++++- src/execution_result.rs | 4 +- src/sandbox.rs | 47 +++++- src/starknet.rs | 9 +- 5 files changed, 382 insertions(+), 22 deletions(-) diff --git a/examples/sandbox.rs b/examples/sandbox.rs index 9e25474a7..bfc4b9a9c 100644 --- a/examples/sandbox.rs +++ b/examples/sandbox.rs @@ -11,6 +11,7 @@ fn main() { ) .unwrap(); + /* let program_path = Path::new("programs/echo.cairo"); // Compile the cairo program to sierra. @@ -38,4 +39,5 @@ fn main() { ) .unwrap(); println!("{:#?}", result); + */ } diff --git a/src/bin/cairo-executor.rs b/src/bin/cairo-executor.rs index d7999e19d..6c80d9af7 100644 --- a/src/bin/cairo-executor.rs +++ b/src/bin/cairo-executor.rs @@ -1,12 +1,324 @@ -use std::path::PathBuf; +use std::{cell::RefCell, path::PathBuf, rc::Rc, sync::Arc}; +use cairo_lang_utils::ResultHelper; use cairo_native::{ context::NativeContext, executor::JitNativeExecutor, - sandbox::{Message, WrappedMessage}, - utils::find_entry_point, + metadata::syscall_handler::SyscallHandlerMeta, + sandbox::{Message, SyscallAnswer, SyscallRequest, WrappedMessage}, + starknet::{ExecutionInfo, StarkNetSyscallHandler, SyscallResult, U256}, + utils::{find_entry_point, find_entry_point_by_idx}, }; -use ipc_channel::ipc::{IpcOneShotServer, IpcSender}; +use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender}; +use starknet_types_core::felt::Felt; +use tracing::{instrument, 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; + Ok(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 { + info, + remaining_gas, + }) = result + { + *gas = remaining_gas; + Ok(info) + } 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)> { + 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, + _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(); @@ -36,16 +348,22 @@ pub fn main() -> Result<(), Box> { 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.recv()?.to_msg()?; + let message: Message = receiver.borrow().recv()?.to_msg()?; tracing::info!("got message: {:?}", message); match message { @@ -53,21 +371,27 @@ pub fn main() -> Result<(), Box> { id, program, inputs, - entry_point, + function_idx, + gas, } => { 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(&program, &entry_point).unwrap(); + 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_dynamic(fn_id, &inputs, None, None)?; + let result = native_executor.invoke_contract_dynamic( + fn_id, + &inputs, + gas, + Some(&SyscallHandlerMeta::new(&mut syscall_handler)), + )?; tracing::info!("invoked with result: {:?}", result); @@ -83,6 +407,8 @@ pub fn main() -> Result<(), Box> { Message::Kill => { break; } + Message::SyscallRequest(_) => todo!(), + Message::SyscallAnswer(_) => todo!(), } } diff --git a/src/execution_result.rs b/src/execution_result.rs index 96108e643..5b6fe73a4 100644 --- a/src/execution_result.rs +++ b/src/execution_result.rs @@ -6,14 +6,14 @@ use serde::{Deserialize, Serialize}; use starknet_types_core::felt::Felt; /// The result of the JIT execution. -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[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/sandbox.rs b/src/sandbox.rs index 073e3b699..7e4d27278 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -7,25 +7,52 @@ use std::{ 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::ExecutionResult, values::JitValue}; +use crate::{ + execution_result::{ContractExecutionResult, ExecutionResult}, + starknet::ExecutionInfo, +}; -#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[allow(clippy::large_enum_variant)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum Message { ExecuteJIT { id: Uuid, program: VersionedProgram, - inputs: Vec, - entry_point: String, + inputs: Vec, + function_idx: usize, + gas: Option, }, ExecutionResult { id: Uuid, - result: ExecutionResult, + 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 }, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum SyscallAnswer { + GetBlockHash { + result: Felt, + remaining_gas: u128, + }, + GetExecutionInfo { + info: ExecutionInfo, + remaining_gas: u128, + }, } impl Message { @@ -95,9 +122,10 @@ impl IsolatedExecutor { pub fn run_program( &self, program: Program, - inputs: Vec, - entry_point: String, - ) -> Result> { + inputs: Vec, + gas: Option, + function_idx: usize, + ) -> Result> { tracing::debug!("running program"); let id = Uuid::new_v4(); @@ -105,7 +133,8 @@ impl IsolatedExecutor { id, program: program.into_artifact(), inputs, - entry_point, + gas, + function_idx, }; self.sender.send(msg.wrap()?)?; diff --git a/src/starknet.rs b/src/starknet.rs index 05f122d1e..544038439 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)] #[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, From f4ee87c7e5dcf000065b40f110bf0bce1e1006df Mon Sep 17 00:00:00 2001 From: Edgar Date: Mon, 22 Jan 2024 10:14:55 -0300 Subject: [PATCH 11/15] progress --- src/bin/cairo-executor.rs | 12 +++---- src/sandbox.rs | 74 +++++++++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/src/bin/cairo-executor.rs b/src/bin/cairo-executor.rs index 6c80d9af7..bb6070fc3 100644 --- a/src/bin/cairo-executor.rs +++ b/src/bin/cairo-executor.rs @@ -6,12 +6,12 @@ use cairo_native::{ executor::JitNativeExecutor, metadata::syscall_handler::SyscallHandlerMeta, sandbox::{Message, SyscallAnswer, SyscallRequest, WrappedMessage}, - starknet::{ExecutionInfo, StarkNetSyscallHandler, SyscallResult, U256}, - utils::{find_entry_point, find_entry_point_by_idx}, + starknet::{StarkNetSyscallHandler, SyscallResult, U256}, + utils::find_entry_point_by_idx, }; use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender}; use starknet_types_core::felt::Felt; -use tracing::{instrument, Instrument}; +use tracing::instrument; #[derive(Debug)] struct SyscallHandler { @@ -47,7 +47,7 @@ impl StarkNetSyscallHandler for SyscallHandler { }) = result { *gas = remaining_gas; - Ok(result) + result } else { tracing::error!("wrong message received: {:#?}", result); panic!(); @@ -76,12 +76,12 @@ impl StarkNetSyscallHandler for SyscallHandler { .unwrap(); if let Message::SyscallAnswer(SyscallAnswer::GetExecutionInfo { - info, + result, remaining_gas, }) = result { *gas = remaining_gas; - Ok(info) + result } else { tracing::error!("wrong message received: {:#?}", result); panic!(); diff --git a/src/sandbox.rs b/src/sandbox.rs index 7e4d27278..e3e2fa970 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -11,8 +11,8 @@ use starknet_types_core::felt::Felt; use uuid::Uuid; use crate::{ - execution_result::{ContractExecutionResult, ExecutionResult}, - starknet::ExecutionInfo, + execution_result::ContractExecutionResult, + starknet::{ExecutionInfo, StarkNetSyscallHandler, SyscallResult}, }; #[allow(clippy::large_enum_variant)] @@ -46,11 +46,11 @@ pub enum SyscallRequest { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum SyscallAnswer { GetBlockHash { - result: Felt, + result: SyscallResult, remaining_gas: u128, }, GetExecutionInfo { - info: ExecutionInfo, + result: SyscallResult, remaining_gas: u128, }, } @@ -82,8 +82,6 @@ impl WrappedMessage { } } -pub type OnResult = Box; - pub struct IsolatedExecutor { proc: Child, sender: IpcSender, @@ -125,6 +123,7 @@ impl IsolatedExecutor { inputs: Vec, gas: Option, function_idx: usize, + handler: &mut impl StarkNetSyscallHandler, ) -> Result> { tracing::debug!("running program"); let id = Uuid::new_v4(); @@ -138,26 +137,49 @@ impl IsolatedExecutor { }; self.sender.send(msg.wrap()?)?; - let ack = self.receiver.recv()?.to_msg()?; - - if let Message::Ack(recv_id) = ack { - assert_eq!(recv_id, id, "id mismatch"); - } else { - // should match - panic!("id mismatch"); - } - - let result = self.receiver.recv()?.to_msg()?; - - if let Message::ExecutionResult { - id: recv_id, - result, - } = result - { - assert_eq!(recv_id, id, "id mismatch"); - Ok(result) - } else { - panic!("wrong msg"); + 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()?, + )?; + } + }, + Message::SyscallAnswer(_) => unreachable!(), + } } } } From f0051a4ad1d51fa7306d0962722a5e5b11e3897a Mon Sep 17 00:00:00 2001 From: Edgar Date: Mon, 22 Jan 2024 10:43:36 -0300 Subject: [PATCH 12/15] sandbox starknet syscall handler --- examples/sandbox.rs | 257 +++++++++++++++++++++++++++++++++++--- src/bin/cairo-executor.rs | 77 ++++++++++-- src/sandbox.rs | 60 ++++++++- 3 files changed, 366 insertions(+), 28 deletions(-) diff --git a/examples/sandbox.rs b/examples/sandbox.rs index bfc4b9a9c..bfdac4a84 100644 --- a/examples/sandbox.rs +++ b/examples/sandbox.rs @@ -1,4 +1,10 @@ -use cairo_native::{sandbox::IsolatedExecutor, values::JitValue}; +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}; @@ -11,33 +17,250 @@ fn main() { ) .unwrap(); - /* - let program_path = Path::new("programs/echo.cairo"); + let path = Path::new("programs/examples/hello_starknet.cairo"); - // Compile the cairo program to sierra. - let sierra_program = (*cairo_native::utils::cairo_to_sierra(program_path)).clone(); + 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 result = sandbox - .run_program( - sierra_program.clone(), - vec![JitValue::Felt252(1.into())], - "echo::echo::main".to_string(), - ) - .unwrap(); - println!("{:#?}", result); + let entry_point = contract.entry_points_by_type.external.get(0).unwrap(); + + let mut handler = SyscallHandler; let result = sandbox .run_program( - sierra_program, - vec![JitValue::Felt252(2.into())], - "echo::echo::main".to_string(), + 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 index bb6070fc3..fb36aadcc 100644 --- a/src/bin/cairo-executor.rs +++ b/src/bin/cairo-executor.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, path::PathBuf, rc::Rc, sync::Arc}; +use std::{cell::RefCell, path::PathBuf, rc::Rc}; use cairo_lang_utils::ResultHelper; use cairo_native::{ @@ -138,10 +138,39 @@ impl StarkNetSyscallHandler for SyscallHandler { &mut self, address_domain: u32, address: Felt, - _gas: &mut u128, + gas: &mut u128, ) -> SyscallResult { - println!("Called `storage_read({address_domain}, {address})` from MLIR."); - Ok(address * Felt::from(3)) + 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( @@ -149,10 +178,40 @@ impl StarkNetSyscallHandler for SyscallHandler { address_domain: u32, address: Felt, value: Felt, - _gas: &mut u128, + gas: &mut u128, ) -> SyscallResult<()> { - println!("Called `storage_write({address_domain}, {address}, {value})` from MLIR."); - Ok(()) + 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<()> { @@ -364,7 +423,6 @@ pub fn main() -> Result<(), Box> { tracing::info!("waiting for message"); let message: Message = receiver.borrow().recv()?.to_msg()?; - tracing::info!("got message: {:?}", message); match message { Message::ExecuteJIT { @@ -374,6 +432,7 @@ pub fn main() -> Result<(), Box> { function_idx, gas, } => { + tracing::info!("Message: ExecuteJIT"); sender.send(Message::Ack(id).wrap()?)?; tracing::info!("sent ack: {:?}", id); let program = program.into_v1()?.program; @@ -402,9 +461,11 @@ pub fn main() -> Result<(), Box> { 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!(), diff --git a/src/sandbox.rs b/src/sandbox.rs index e3e2fa970..2c531a543 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -38,8 +38,24 @@ pub enum Message { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum SyscallRequest { - GetBlockHash { block_number: u64, gas: u128 }, - GetExecutionInfo { gas: u128 }, + GetBlockHash { + block_number: u64, + gas: u128, + }, + GetExecutionInfo { + 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)] @@ -53,6 +69,14 @@ pub enum SyscallAnswer { result: SyscallResult, remaining_gas: u128, }, + StorageRead { + result: SyscallResult, + remaining_gas: u128, + }, + StorageWrite { + result: SyscallResult<()>, + remaining_gas: u128, + }, } impl Message { @@ -104,7 +128,7 @@ impl IsolatedExecutor { // first we accept the connection let (receiver, msg) = server.accept().expect("failed to accept receiver"); - tracing::debug!("accepted receiver {receiver:?} wit msg {msg:?}"); + 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"); @@ -177,6 +201,36 @@ impl IsolatedExecutor { .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()?, + )?; + } }, Message::SyscallAnswer(_) => unreachable!(), } From 49e5162d248037e0e5a736eab41a8761c6a1e83b Mon Sep 17 00:00:00 2001 From: Edgar Date: Mon, 22 Jan 2024 11:02:42 -0300 Subject: [PATCH 13/15] progress --- src/sandbox.rs | 110 +++++++++++++++++++++++++++++++++++++++++++++++- src/starknet.rs | 2 +- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/src/sandbox.rs b/src/sandbox.rs index 2c531a543..9a41951f3 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -6,13 +6,14 @@ use std::{ use cairo_lang_sierra::program::{Program, VersionedProgram}; use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender}; +use lambdaworks_math::unsigned_integer::element::U256; use serde::{Deserialize, Serialize}; use starknet_types_core::felt::Felt; use uuid::Uuid; use crate::{ execution_result::ContractExecutionResult, - starknet::{ExecutionInfo, StarkNetSyscallHandler, SyscallResult}, + starknet::{ExecutionInfo, StarkNetSyscallHandler, SyscallResult, U256}, }; #[allow(clippy::large_enum_variant)] @@ -45,6 +46,43 @@ pub enum SyscallRequest { 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, @@ -69,6 +107,22 @@ pub enum SyscallAnswer { 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, @@ -77,6 +131,18 @@ pub enum SyscallAnswer { 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 { @@ -231,6 +297,48 @@ impl IsolatedExecutor { .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, gas } => todo!(), + SyscallRequest::LibraryCall { + class_hash, + function_selector, + calldata, + gas, + } => todo!(), + SyscallRequest::CallContract { + address, + entry_point_selector, + calldata, + gas, + } => todo!(), + SyscallRequest::EmitEvent { keys, data, gas } => todo!(), + SyscallRequest::SendMessageToL1 { + to_address, + payload, + gas, + } => todo!(), + SyscallRequest::Keccak { input, gas } => todo!(), }, Message::SyscallAnswer(_) => unreachable!(), } diff --git a/src/starknet.rs b/src/starknet.rs index 544038439..20dd9473e 100644 --- a/src/starknet.rs +++ b/src/starknet.rs @@ -15,7 +15,7 @@ pub type SyscallResult = std::result::Result>; pub struct Felt252Abi(pub [u8; 32]); /// Binary representation of a `u256` (in MLIR). // TODO: This shouldn't need to be public. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[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]); From 5e85be6501d78d90ec327733e9ece7abe16d61b0 Mon Sep 17 00:00:00 2001 From: Edgar Date: Mon, 22 Jan 2024 11:17:00 -0300 Subject: [PATCH 14/15] host --- src/sandbox.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 11 deletions(-) diff --git a/src/sandbox.rs b/src/sandbox.rs index 9a41951f3..042c638d0 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -6,14 +6,13 @@ use std::{ use cairo_lang_sierra::program::{Program, VersionedProgram}; use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender}; -use lambdaworks_math::unsigned_integer::element::U256; use serde::{Deserialize, Serialize}; use starknet_types_core::felt::Felt; use uuid::Uuid; use crate::{ execution_result::ContractExecutionResult, - starknet::{ExecutionInfo, StarkNetSyscallHandler, SyscallResult, U256}, + starknet::{ExecutionInfo, StarkNetSyscallHandler, SyscallResult}, }; #[allow(clippy::large_enum_variant)] @@ -319,26 +318,97 @@ impl IsolatedExecutor { .wrap()?, )?; } - SyscallRequest::ReplaceClass { class_hash, gas } => todo!(), + 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, - gas, - } => todo!(), + 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, - gas, - } => todo!(), - SyscallRequest::EmitEvent { keys, data, gas } => todo!(), + 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, - gas, - } => todo!(), - SyscallRequest::Keccak { input, gas } => todo!(), + 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!(), } From cb8f8b97a2e1cd44a5d6edc2ab6874022f9554c7 Mon Sep 17 00:00:00 2001 From: Edgar Date: Mon, 22 Jan 2024 11:25:10 -0300 Subject: [PATCH 15/15] executor --- src/bin/cairo-executor.rs | 256 +++++++++++++++++++++++++++++++++----- 1 file changed, 227 insertions(+), 29 deletions(-) diff --git a/src/bin/cairo-executor.rs b/src/bin/cairo-executor.rs index fb36aadcc..b84d65a84 100644 --- a/src/bin/cairo-executor.rs +++ b/src/bin/cairo-executor.rs @@ -6,7 +6,7 @@ use cairo_native::{ executor::JitNativeExecutor, metadata::syscall_handler::SyscallHandlerMeta, sandbox::{Message, SyscallAnswer, SyscallRequest, WrappedMessage}, - starknet::{StarkNetSyscallHandler, SyscallResult, U256}, + starknet::{StarkNetSyscallHandler, SyscallResult}, utils::find_entry_point_by_idx, }; use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender}; @@ -94,18 +94,74 @@ impl StarkNetSyscallHandler for SyscallHandler { contract_address_salt: Felt, calldata: &[Felt], deploy_from_zero: bool, - _gas: &mut u128, + 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(), - )) + 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<()> { - println!("Called `replace_class({class_hash})` from MLIR."); - Ok(()) + 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( @@ -113,12 +169,40 @@ impl StarkNetSyscallHandler for SyscallHandler { class_hash: Felt, function_selector: Felt, calldata: &[Felt], - _gas: &mut u128, + 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()) + 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( @@ -126,12 +210,40 @@ impl StarkNetSyscallHandler for SyscallHandler { address: Felt, entry_point_selector: Felt, calldata: &[Felt], - _gas: &mut u128, + 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()) + 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( @@ -214,28 +326,114 @@ impl StarkNetSyscallHandler for SyscallHandler { } } - fn emit_event(&mut self, keys: &[Felt], data: &[Felt], _gas: &mut u128) -> SyscallResult<()> { - println!("Called `emit_event({keys:?}, {data:?})` from MLIR."); - Ok(()) + 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, + gas: &mut u128, ) -> SyscallResult<()> { - println!("Called `send_message_to_l1({to_address}, {payload:?})` from MLIR."); - Ok(()) + 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, + gas: &mut u128, ) -> SyscallResult { - println!("Called `keccak({input:?})` from MLIR."); - Ok(U256(Felt::from(1234567890).to_bytes_le())) + 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(