From f51a2a11f638e2a8f8e24d0efe896c07a4c69cbd Mon Sep 17 00:00:00 2001 From: Esteve Soler Arderiu Date: Thu, 19 Sep 2024 12:01:36 +0200 Subject: [PATCH] Make native trace dump available. --- Cargo.lock | 47 +++++++++++-- Cargo.toml | 4 +- crates/blockifier/Cargo.toml | 5 +- .../execution/native/entry_point_execution.rs | 70 ++++++++++++++++++- .../blockifier/src/execution/native/utils.rs | 39 +++++++++-- 5 files changed, 151 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c9f9b8a37c..b83e5a8fc43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1028,7 +1028,7 @@ dependencies = [ "serde_json", "sha2", "sha3", - "sierra-emu", + "sierra-emu 0.1.0", "starknet-types-core", "starknet_api", "strum 0.25.0", @@ -1686,7 +1686,6 @@ dependencies = [ [[package]] name = "cairo-native" version = "0.2.0" -source = "git+https://github.com/lambdaclass/cairo_native#2be717cba74c63628cb68b619ff2022c70d0cdd2" dependencies = [ "anyhow", "aquamarine", @@ -1732,18 +1731,22 @@ dependencies = [ "thiserror", "tracing", "tracing-subscriber", + "utf8_iter", ] [[package]] name = "cairo-native-runtime" version = "0.2.0" -source = "git+https://github.com/lambdaclass/cairo_native#2be717cba74c63628cb68b619ff2022c70d0cdd2" dependencies = [ + "cairo-lang-sierra", "cairo-lang-sierra-gas", + "cairo-lang-utils", "lazy_static", "libc", + "num-bigint 0.4.6", + "num-traits 0.2.19", "rand 0.8.5", - "starknet-crypto 0.7.1", + "sierra-emu 0.1.0 (git+https://github.com/lambdaclass/sierra-emu)", "starknet-curve 0.5.0", "starknet-types-core", ] @@ -8854,7 +8857,35 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "sierra-emu" version = "0.1.0" -source = "git+https://github.com/lambdaclass/sierra-emu.git#2b612d56db15a1002bb78827aa3fb2f66db4cc81" +dependencies = [ + "cairo-lang-sierra", + "cairo-lang-sierra-ap-change", + "cairo-lang-sierra-gas", + "cairo-lang-utils", + "clap", + "k256", + "keccak", + "num-bigint 0.4.6", + "num-traits 0.2.19", + "p256", + "rand 0.8.5", + "sec1", + "serde", + "serde_json", + "sha2", + "smallvec", + "starknet-crypto 0.7.1", + "starknet-curve 0.5.0", + "starknet-types-core", + "thiserror", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "sierra-emu" +version = "0.1.0" +source = "git+https://github.com/lambdaclass/sierra-emu#2b612d56db15a1002bb78827aa3fb2f66db4cc81" dependencies = [ "cairo-lang-sierra", "cairo-lang-sierra-ap-change", @@ -10546,6 +10577,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index fbbf60b7fdc..6469d9fc459 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,8 +81,7 @@ cairo-lang-sierra-to-casm = "2.8.2" cairo-lang-starknet-classes = "2.8.2" cairo-lang-utils = "2.8.2" # This is a temporary dependency, will be removed once the new version of cairo-native is released to main. -cairo-native = { git = "https://github.com/lambdaclass/cairo_native" } -sierra-emu = { git = "https://github.com/lambdaclass/sierra-emu.git" } +cairo-native = { path = "../cairo_native" } cairo-vm = "1.0.1" camelpaste = "0.1.0" chrono = "0.4.26" @@ -172,6 +171,7 @@ serde_repr = "0.1.19" serde_yaml = "0.9.16" sha2 = "0.10.8" sha3 = "0.10.8" +sierra-emu = { path = "../sierra-emu" } simple_logger = "4.0.0" starknet-core = "0.6.0" starknet-crypto = "0.5.1" diff --git a/crates/blockifier/Cargo.toml b/crates/blockifier/Cargo.toml index 2bcd38b52af..0131d5f596a 100644 --- a/crates/blockifier/Cargo.toml +++ b/crates/blockifier/Cargo.toml @@ -14,6 +14,7 @@ concurrency = [] jemalloc = ["dep:tikv-jemallocator"] testing = ["rand", "rstest"] use-sierra-emu = [] +with-trace-dump = ["cairo-native/with-trace-dump"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -31,11 +32,9 @@ cairo-lang-sierra.workspace = true cairo-lang-starknet-classes.workspace = true cairo-lang-utils.workspace = true cairo-native.workspace = true -sierra-emu.workspace = true cairo-vm.workspace = true derive_more.workspace = true indexmap.workspace = true -tracing.workspace = true itertools.workspace = true keccak.workspace = true log.workspace = true @@ -52,6 +51,7 @@ serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true, features = ["arbitrary_precision"] } sha2.workspace = true sha3.workspace = true +sierra-emu.workspace = true starknet-types-core.workspace = true starknet_api = { workspace = true, features = ["testing"] } strum.workspace = true @@ -60,6 +60,7 @@ tempfile.workspace = true thiserror.workspace = true tikv-jemallocator = { workspace = true, optional = true } toml.workspace = true +tracing.workspace = true [dev-dependencies] assert_matches.workspace = true diff --git a/crates/blockifier/src/execution/native/entry_point_execution.rs b/crates/blockifier/src/execution/native/entry_point_execution.rs index b7ef0c74635..8e798c54433 100644 --- a/crates/blockifier/src/execution/native/entry_point_execution.rs +++ b/crates/blockifier/src/execution/native/entry_point_execution.rs @@ -48,7 +48,75 @@ pub fn execute_entry_point_call( ); run_sierra_emu_executor(vm, function_id, call.clone()) } else { - run_native_executor(&contract_class.executor, function_id, call, syscall_handler) + #[cfg(feature = "with-trace-dump")] + let counter_value = { + use std::collections::HashMap; + use std::sync::atomic::AtomicUsize; + use std::sync::Mutex; + + use cairo_lang_sierra::program_registry::ProgramRegistry; + use cairo_native::runtime::trace_dump::TraceDump; + use cairo_native::types::TypeBuilder; + + // Since the library is statically linked, then dynamically loaded, each instance of + // `TRACE_DUMP` for each contract is separate (probably). That's why we need this + // getter and cannot use `cairo_native::runtime::TRACE_DUMP` directly. + let trace_dump = unsafe { + let fn_ptr = contract_class + .executor + .library + .get:: &'static Mutex>>( + b"get_trace_dump_ptr\0", + ) + .unwrap(); + + fn_ptr() + }; + let mut trace_dump = trace_dump.lock().unwrap(); + + static COUNTER: AtomicUsize = AtomicUsize::new(0); + let counter_value = COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + trace_dump.insert( + u64::try_from(counter_value).unwrap(), + TraceDump::new( + ProgramRegistry::new(&contract_class.program).unwrap(), + |x, registry| x.layout(registry).unwrap(), + ), + ); + + // Set the active trace id. + let trace_id_ref = unsafe { + contract_class + .executor + .library + .get::(b"TRACE_DUMP__TRACE_ID\0") + .unwrap() + .try_as_raw_ptr() + .unwrap() + .cast::() + .as_mut() + .unwrap() + }; + *trace_id_ref = u64::try_from(counter_value).unwrap(); + + println!("Execution started for trace #{counter_value}."); + dbg!(trace_dump.keys().collect::>()); + counter_value + }; + + let x = run_native_executor( + &contract_class.executor, + function_id, + call, + syscall_handler, + #[cfg(feature = "with-trace-dump")] + counter_value, + ); + + #[cfg(feature = "with-trace-dump")] + println!("Execution finished for trace #{counter_value}."); + + x }; let execution_time = pre_execution_instant.elapsed().as_millis(); diff --git a/crates/blockifier/src/execution/native/utils.rs b/crates/blockifier/src/execution/native/utils.rs index a1d97d9ed94..74655afb8a4 100644 --- a/crates/blockifier/src/execution/native/utils.rs +++ b/crates/blockifier/src/execution/native/utils.rs @@ -51,6 +51,7 @@ pub fn run_native_executor( function_id: &FunctionId, call: CallEntryPoint, mut syscall_handler: NativeSyscallHandler<'_>, + #[cfg(feature = "with-trace-dump")] trace_id: usize, ) -> EntryPointExecutionResult { let execution_result = native_executor.invoke_contract_dynamic( function_id, @@ -59,6 +60,33 @@ pub fn run_native_executor( &mut syscall_handler, ); + #[cfg(feature = "with-trace-dump")] + #[allow(warnings)] + { + use std::sync::Mutex; + + use cairo_native::runtime::trace_dump::TraceDump; + + let trace = serde_json::to_string_pretty(&{ + let trace_dump = unsafe { + let fn_ptr = native_executor + .library + .get:: &'static Mutex>>( + b"get_trace_dump_ptr\0", + ) + .unwrap(); + + fn_ptr() + }; + let mut trace_dump = trace_dump.lock().unwrap(); + + trace_dump.remove(&u64::try_from(trace_id).unwrap()).unwrap().trace + }) + .unwrap(); + std::fs::create_dir_all("traces/native/").unwrap(); + std::fs::write(&format!("traces/native/trace_{}.json", trace_id), trace).unwrap(); + } + let run_result = match execution_result { Ok(res) if res.failure_flag => Err(EntryPointExecutionError::NativeExecutionError { info: if !res.return_values.is_empty() { @@ -100,6 +128,9 @@ pub fn run_sierra_emu_executor( std::fs::create_dir_all("traces/emu/").unwrap(); std::fs::write(format!("traces/emu/trace_{}.json", counter_value), trace).unwrap(); + std::fs::write(format!("traces/program_{}.sierra", counter_value), format!("{}", vm.program)) + .unwrap(); + if execution_result.failure_flag { Err(EntryPointExecutionError::NativeExecutionError { info: if !execution_result.return_values.is_empty() { @@ -129,8 +160,8 @@ fn create_callinfo( syscall_handler: NativeSyscallHandler<'_>, ) -> Result { let gas_consumed = { - let low = run_result.remaining_gas as u64; - let high = (run_result.remaining_gas >> 64) as u64; + let low = u64::try_from(run_result.remaining_gas & u128::from(u64::MAX)).unwrap(); + let high = u64::try_from(run_result.remaining_gas >> 64).unwrap(); if high != 0 { return Err(EntryPointExecutionError::NativeExecutionError { info: "Overflow: gas consumed bigger than 64 bit".into(), @@ -169,8 +200,8 @@ pub fn create_callinfo_emu( accessed_storage_keys: HashSet, ) -> Result { let gas_consumed = { - let low = run_result.remaining_gas as u64; - let high = (run_result.remaining_gas >> 64) as u64; + let low = u64::try_from(run_result.remaining_gas & u128::from(u64::MAX)).unwrap(); + let high = u64::try_from(run_result.remaining_gas >> 64).unwrap(); if high != 0 { return Err(EntryPointExecutionError::NativeExecutionError { info: "Overflow: gas consumed bigger than 64 bit".into(),