diff --git a/.github/workflows/replay.yml b/.github/workflows/replay.yml new file mode 100644 index 00000000000..02c87832aaa --- /dev/null +++ b/.github/workflows/replay.yml @@ -0,0 +1,46 @@ +name: Replay + +on: + push: + branches: + - main + - main-v[0-9].** + pull_request: + +jobs: + clippy: + runs-on: ubuntu-latest + env: + MLIR_SYS_190_PREFIX: /usr/lib/llvm-19/ + LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ + TABLEGEN_190_PREFIX: /usr/lib/llvm-19/ + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@1.86.0 + with: + components: clippy + - uses: Swatinem/rust-cache@v2 + - name: Add LLVM Debian repository + uses: myci-actions/add-deb-repo@10 + with: + repo: deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main + repo-name: llvm-repo + keys-asc: https://apt.llvm.org/llvm-snapshot.gpg.key + - name: Install lld + run: sudo apt install lld + - name: Install LLVM + run: sudo apt-get install llvm-19 llvm-19-dev llvm-19-runtime clang-19 clang-tools-19 lld-19 libpolly-19-dev libmlir-19-dev mlir-19-tools + - name: Run cargo clippy + run: | + cd crates/blockifier + cargo clippy --all-targets --all-features --no-deps + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2023-10-19 + components: rustfmt + - name: Run cargo fmt + run: cargo +nightly-2023-10-19 fmt --all -- --check diff --git a/Cargo.lock b/Cargo.lock index 6e0cecf305c..f37a120af53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3530,7 +3530,7 @@ dependencies = [ "bitflags 2.10.0", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.12.1", "log", "prettyplease", "proc-macro2", @@ -3550,7 +3550,7 @@ dependencies = [ "bitflags 2.10.0", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.12.1", "log", "prettyplease", "proc-macro2", @@ -3701,6 +3701,7 @@ dependencies = [ "cached", "cairo-lang-casm", "cairo-lang-runner", + "cairo-lang-sierra", "cairo-lang-starknet-classes", "cairo-lang-utils", "cairo-native", @@ -3731,6 +3732,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", + "sierra-emu", "starknet-types-core", "starknet_api", "strum 0.25.0", @@ -4528,6 +4530,35 @@ dependencies = [ "xshell", ] +[[package]] +name = "cairo-lang-test-plugin" +version = "2.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a706d75348dd5dd9bff1389a8c983e9795639640b3f8522d824fd3288cff04" +dependencies = [ + "anyhow", + "cairo-lang-compiler", + "cairo-lang-debug", + "cairo-lang-defs", + "cairo-lang-filesystem", + "cairo-lang-lowering", + "cairo-lang-parser", + "cairo-lang-semantic", + "cairo-lang-sierra", + "cairo-lang-sierra-generator", + "cairo-lang-starknet", + "cairo-lang-starknet-classes", + "cairo-lang-syntax", + "cairo-lang-utils", + "indoc 2.0.7", + "itertools 0.14.0", + "num-bigint", + "num-traits", + "salsa", + "serde", + "starknet-types-core", +] + [[package]] name = "cairo-lang-test-utils" version = "2.16.0" @@ -4567,7 +4598,7 @@ dependencies = [ [[package]] name = "cairo-native" version = "0.9.0-rc.1" -source = "git+https://github.com/lambdaclass/cairo_native?branch=tomer%2Fblake_builtin#51c1118340824d7b998da03e94cba061c1cff0f8" +source = "git+https://github.com/lambdaclass/cairo_native?rev=b3fea26c8a3f3a48d0f1b473d7439435c6389083#b3fea26c8a3f3a48d0f1b473d7439435c6389083" dependencies = [ "aquamarine", "ark-ec", @@ -4602,6 +4633,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", + "sierra-emu", "starknet-curve 0.6.0", "starknet-types-core", "tempfile", @@ -4868,7 +4900,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -4877,7 +4909,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -5686,6 +5718,7 @@ dependencies = [ "ff", "generic-array", "group", + "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", @@ -5844,7 +5877,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -7309,7 +7342,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -7605,6 +7638,7 @@ dependencies = [ "once_cell", "serdect", "sha2 0.10.9", + "signature", ] [[package]] @@ -8575,7 +8609,7 @@ checksum = "7760e0e418d9b7e5777c0374009ca4c93861b9066f18cb334a20ce50ab63aa48" dependencies = [ "assert-json-diff", "bytes", - "colored 3.0.0", + "colored 2.2.0", "futures-util", "http 1.3.1", "http-body 1.0.1", @@ -9339,6 +9373,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + [[package]] name = "page_size" version = "0.6.0" @@ -9818,6 +9864,15 @@ dependencies = [ "syn 2.0.110", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "primitive-types" version = "0.12.2" @@ -9942,7 +9997,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ "bytes", - "heck 0.5.0", + "heck 0.4.1", "itertools 0.12.1", "log", "multimap", @@ -10050,7 +10105,7 @@ dependencies = [ "libc", "memoffset 0.9.1", "num-bigint", - "parking_lot 0.12.5", + "parking_lot 0.11.2", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", @@ -10207,7 +10262,7 @@ dependencies = [ "once_cell", "socket2 0.6.1", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -10934,7 +10989,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -10993,7 +11048,7 @@ dependencies = [ "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs 0.26.11", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -11534,6 +11589,47 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "sierra-emu" +version = "0.9.0-rc.1" +source = "git+https://github.com/lambdaclass/cairo_native?rev=b3fea26c8a3f3a48d0f1b473d7439435c6389083#b3fea26c8a3f3a48d0f1b473d7439435c6389083" +dependencies = [ + "cairo-lang-compiler", + "cairo-lang-filesystem", + "cairo-lang-lowering", + "cairo-lang-runner", + "cairo-lang-sierra", + "cairo-lang-sierra-ap-change", + "cairo-lang-sierra-gas", + "cairo-lang-sierra-generator", + "cairo-lang-sierra-to-casm", + "cairo-lang-sierra-type-size", + "cairo-lang-starknet-classes", + "cairo-lang-test-plugin", + "cairo-lang-utils", + "clap", + "generic-array", + "k256", + "keccak", + "num-bigint", + "num-integer", + "num-traits", + "p256", + "rand 0.9.2", + "rayon", + "sec1", + "serde", + "serde_json", + "sha2 0.10.9", + "smallvec", + "starknet-crypto 0.8.1", + "starknet-curve 0.6.0", + "starknet-types-core", + "thiserror 2.0.17", + "tracing", + "tracing-subscriber", +] + [[package]] name = "signal-hook-registry" version = "1.4.6" @@ -12717,7 +12813,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -13844,7 +13940,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4ddbe1bd3b3..75f8937221e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -252,7 +252,8 @@ cairo-lang-sierra = "2.16.0" cairo-lang-sierra-to-casm = "2.16.0" cairo-lang-starknet-classes = "2.16.0" cairo-lang-utils = "2.16.0" -cairo-native = { git = "https://github.com/lambdaclass/cairo_native", branch = "tomer/blake_builtin" } +cairo-native = { git = "https://github.com/lambdaclass/cairo_native", rev = "b3fea26c8a3f3a48d0f1b473d7439435c6389083" } +sierra-emu = { git = "https://github.com/lambdaclass/cairo_native", rev = "b3fea26c8a3f3a48d0f1b473d7439435c6389083" } cairo-program-runner-lib = "1.1.0" cairo-vm = "3.0.1" camelpaste = "0.1.0" diff --git a/crates/blockifier/Cargo.toml b/crates/blockifier/Cargo.toml index e1ee9a10c3d..4c08f55eac7 100644 --- a/crates/blockifier/Cargo.toml +++ b/crates/blockifier/Cargo.toml @@ -10,12 +10,17 @@ description = "The transaction-executing component in the Starknet sequencer." workspace = true [features] +with-libfunc-profiling = ["cairo_native", "cairo-native/with-libfunc-profiling"] +with-trace-dump = ["cairo_native", "cairo-native/with-trace-dump"] cairo_native = [ "blockifier_test_utils/cairo_native", "dep:apollo_compilation_utils", "dep:apollo_compile_to_native", "dep:cairo-native", + "dep:sierra-emu", + "dep:cairo-lang-sierra", ] +only-native = ["cairo_native"] mocks = [] native_blockifier = [] node_api = [] @@ -44,7 +49,9 @@ cairo-lang-casm = { workspace = true, features = ["parity-scale-codec"] } cairo-lang-runner.workspace = true cairo-lang-starknet-classes.workspace = true cairo-lang-utils.workspace = true +cairo-lang-sierra = { workspace = true, optional = true } cairo-native = { workspace = true, optional = true } +sierra-emu = { workspace = true, optional = true } cairo-vm.workspace = true dashmap.workspace = true derive_more = { workspace = true, features = [ diff --git a/crates/blockifier/src/execution/call_info.rs b/crates/blockifier/src/execution/call_info.rs index 97f7b56c441..c4c72e76594 100644 --- a/crates/blockifier/src/execution/call_info.rs +++ b/crates/blockifier/src/execution/call_info.rs @@ -160,12 +160,14 @@ impl EventSummary { } } +#[cfg_attr(feature = "transaction_serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, Debug, Default, derive_more::AddAssign, PartialEq)] pub struct CallSummary { pub n_calls: u64, pub n_calls_running_native: u64, } +#[cfg_attr(feature = "transaction_serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, Default, PartialEq)] pub struct ExecutionSummary { pub charged_resources: ChargedResources, @@ -432,6 +434,8 @@ pub struct CallInfo { pub tracked_resource: TrackedResource, // Additional information gathered during execution. + pub time: std::time::Duration, + pub call_counter: usize, pub storage_access_tracker: StorageAccessTracker, // Tracks how many times each cairo primitive (builtin or opcode) was called during execution // (excluding inner calls). Used by the bouncer to decide when to close a block. diff --git a/crates/blockifier/src/execution/deprecated_entry_point_execution.rs b/crates/blockifier/src/execution/deprecated_entry_point_execution.rs index 26e925f2620..0f8312c81f0 100644 --- a/crates/blockifier/src/execution/deprecated_entry_point_execution.rs +++ b/crates/blockifier/src/execution/deprecated_entry_point_execution.rs @@ -287,6 +287,8 @@ pub fn finalize_execution( inner_calls: syscall_handler.inner_calls, tracked_resource: TrackedResource::CairoSteps, resources, + time: Default::default(), + call_counter: 0, storage_access_tracker: StorageAccessTracker { storage_read_values: syscall_handler.read_values, accessed_storage_keys: syscall_handler.accessed_keys, diff --git a/crates/blockifier/src/execution/entry_point.rs b/crates/blockifier/src/execution/entry_point.rs index 2d85eb0e6b9..6191a4c2fe6 100644 --- a/crates/blockifier/src/execution/entry_point.rs +++ b/crates/blockifier/src/execution/entry_point.rs @@ -328,6 +328,8 @@ pub struct EntryPointExecutionContext { // Used to support charging for gas consumed in blockifier revert flow. pub sierra_gas_revert_tracker: SierraGasRevertTracker, + + pub call_counter: usize, } impl EntryPointExecutionContext { @@ -348,6 +350,7 @@ impl EntryPointExecutionContext { tracked_resource_stack: vec![], revert_infos: ExecutionRevertInfo(vec![]), sierra_gas_revert_tracker, + call_counter: 0, } } diff --git a/crates/blockifier/src/execution/entry_point_execution.rs b/crates/blockifier/src/execution/entry_point_execution.rs index d21df5c3d0d..a3c90073306 100644 --- a/crates/blockifier/src/execution/entry_point_execution.rs +++ b/crates/blockifier/src/execution/entry_point_execution.rs @@ -485,6 +485,8 @@ pub fn finalize_execution( inner_calls: syscall_handler_base.inner_calls, tracked_resource, resources: extended_resources, + time: Default::default(), + call_counter: 0, storage_access_tracker: syscall_handler_base.storage_access_tracker, builtin_counters: extended_resources_without_inner_calls.prover_cairo_primitives(), syscalls_usage: syscall_handler_base.syscalls_usage, diff --git a/crates/blockifier/src/execution/execution_utils.rs b/crates/blockifier/src/execution/execution_utils.rs index aa00f27317e..ee8641df7d4 100644 --- a/crates/blockifier/src/execution/execution_utils.rs +++ b/crates/blockifier/src/execution/execution_utils.rs @@ -119,7 +119,11 @@ pub fn execute_entry_point_call( state: &mut dyn State, context: &mut EntryPointExecutionContext, ) -> EntryPointExecutionResult { - match compiled_class { + let current_call_counter = context.call_counter; + context.call_counter += 1; + let pre_time = std::time::Instant::now(); + + let mut result = match compiled_class { RunnableCompiledClass::V0(compiled_class) => { deprecated_entry_point_execution::execute_entry_point_call( call, @@ -133,7 +137,9 @@ pub fn execute_entry_point_call( } #[cfg(feature = "cairo_native")] RunnableCompiledClass::V1Native(compiled_class) => { - if context.tracked_resource_stack.last() == Some(&TrackedResource::CairoSteps) { + if context.tracked_resource_stack.last() == Some(&TrackedResource::CairoSteps) + && !cfg!(feature = "only-native") + { // We cannot run native with cairo steps as the tracked resources (it's a vm // resource). entry_point_execution::execute_entry_point_call( @@ -151,7 +157,11 @@ pub fn execute_entry_point_call( ) } } - } + }?; + + result.time = pre_time.elapsed(); + result.call_counter = current_call_counter; + Ok(result) } pub fn update_remaining_gas(remaining_gas: &mut u64, call_info: &CallInfo) { diff --git a/crates/blockifier/src/execution/native.rs b/crates/blockifier/src/execution/native.rs index 8b24ff59652..be1eadd45e7 100644 --- a/crates/blockifier/src/execution/native.rs +++ b/crates/blockifier/src/execution/native.rs @@ -1,5 +1,6 @@ pub mod contract_class; pub mod entry_point_execution; +pub mod executor; pub mod syscall_handler; pub mod utils; diff --git a/crates/blockifier/src/execution/native/contract_class.rs b/crates/blockifier/src/execution/native/contract_class.rs index d7c6999fac9..3a3a20ba3d1 100644 --- a/crates/blockifier/src/execution/native/contract_class.rs +++ b/crates/blockifier/src/execution/native/contract_class.rs @@ -10,6 +10,7 @@ use starknet_types_core::felt::Felt; use crate::execution::contract_class::{CompiledClassV1, EntryPointV1, NestedFeltCounts}; use crate::execution::entry_point::EntryPointTypeAndSelector; use crate::execution::errors::PreExecutionError; +use crate::execution::native::executor::ContractExecutor; #[derive(Clone, Debug, PartialEq, Eq)] pub struct NativeCompiledClassV1(pub Arc); impl Deref for NativeCompiledClassV1 { @@ -35,6 +36,12 @@ impl NativeCompiledClassV1 { Self(Arc::new(contract)) } + pub fn new_2(executor: ContractExecutor, casm: CompiledClassV1) -> NativeCompiledClassV1 { + let contract = NativeCompiledClassV1Inner::new_2(executor, casm); + + Self(Arc::new(contract)) + } + pub fn get_entry_point( &self, entry_point: &EntryPointTypeAndSelector, @@ -71,12 +78,17 @@ impl HashableCompiledClass for NativeCompiledCla #[derive(Debug)] pub struct NativeCompiledClassV1Inner { - pub executor: AotContractExecutor, + pub executor: ContractExecutor, casm: CompiledClassV1, } impl NativeCompiledClassV1Inner { fn new(executor: AotContractExecutor, casm: CompiledClassV1) -> Self { + let executor = executor.into(); + NativeCompiledClassV1Inner { executor, casm } + } + + fn new_2(executor: ContractExecutor, casm: CompiledClassV1) -> Self { NativeCompiledClassV1Inner { executor, casm } } } diff --git a/crates/blockifier/src/execution/native/entry_point_execution.rs b/crates/blockifier/src/execution/native/entry_point_execution.rs index 486b17019cb..8bf5eb1418c 100644 --- a/crates/blockifier/src/execution/native/entry_point_execution.rs +++ b/crates/blockifier/src/execution/native/entry_point_execution.rs @@ -121,6 +121,8 @@ fn create_callinfo( inner_calls: syscall_handler.base.inner_calls, storage_access_tracker: syscall_handler.base.storage_access_tracker, tracked_resource: TrackedResource::SierraGas, + time: Default::default(), + call_counter: 0, builtin_counters: entry_point_primitive_counters, syscalls_usage: syscall_handler.base.syscalls_usage, }) diff --git a/crates/blockifier/src/execution/native/executor.rs b/crates/blockifier/src/execution/native/executor.rs new file mode 100644 index 00000000000..04643added2 --- /dev/null +++ b/crates/blockifier/src/execution/native/executor.rs @@ -0,0 +1,687 @@ +use std::fs::{self, File}; +use std::io::Write; +use std::path::PathBuf; +use std::sync::Arc; +use std::sync::atomic::AtomicU64; + +use cairo_lang_sierra::program::Program; +use cairo_lang_starknet_classes::compiler_version::VersionId; +use cairo_lang_starknet_classes::contract_class::ContractEntryPoints; +use cairo_native::execution_result::ContractExecutionResult; +use cairo_native::executor::AotContractExecutor; +use cairo_native::starknet::StarknetSyscallHandler; +use cairo_native::utils::BuiltinCosts; +use sierra_emu::VirtualMachine; +use starknet_types_core::felt::Felt; +#[cfg(feature = "with-libfunc-profiling")] +use { + cairo_lang_sierra::ids::ConcreteLibfuncId, + cairo_native::metadata::profiler::LibfuncProfileData, + std::collections::HashMap, + std::sync::{LazyLock, Mutex}, +}; + +use super::syscall_handler::NativeSyscallHandler; + +#[cfg(feature = "with-libfunc-profiling")] +pub struct EntrypointProfile { + pub class_hash: Felt, + pub selector: Felt, + pub profile: HashMap, + pub program: Program, +} + +#[cfg(feature = "with-libfunc-profiling")] +pub struct TransactionProfile { + pub block_number: u64, + pub tx_hash: String, + pub entrypoint_profiles: Vec, +} + +#[cfg(feature = "with-libfunc-profiling")] +type ProfilesByBlockTx = HashMap; + +#[cfg(feature = "with-libfunc-profiling")] +pub static LIBFUNC_PROFILES_MAP: LazyLock> = + LazyLock::new(|| Mutex::new(HashMap::new())); + +#[derive(Debug)] +pub enum ContractExecutor { + Aot(AotContractExecutor), + Emu((Arc, ContractEntryPoints, VersionId)), + #[cfg(any(feature = "with-trace-dump", feature = "with-libfunc-profiling"))] + AotWithProgram((AotContractExecutor, Program)), +} + +impl From for ContractExecutor { + fn from(value: AotContractExecutor) -> Self { + Self::Aot(value) + } +} + +impl From<(Arc, ContractEntryPoints, VersionId)> for ContractExecutor { + fn from(value: (Arc, ContractEntryPoints, VersionId)) -> Self { + Self::Emu(value) + } +} + +impl ContractExecutor { + pub fn run( + &self, + selector: Felt, + args: &[Felt], + gas: u64, + builtin_costs: Option, + mut syscall_handler: &mut NativeSyscallHandler<'_>, + ) -> cairo_native::error::Result { + match self { + ContractExecutor::Aot(aot_contract_executor) => { + aot_contract_executor.run(selector, args, gas, builtin_costs, syscall_handler) + } + ContractExecutor::Emu((program, entrypoints, version)) => { + let mut virtual_machine = + VirtualMachine::new_starknet(program.to_owned(), entrypoints, *version); + + let builtin_costs = builtin_costs.map(|builtin_costs| sierra_emu::BuiltinCosts { + r#const: builtin_costs.r#const, + pedersen: builtin_costs.pedersen, + bitwise: builtin_costs.bitwise, + ecop: builtin_costs.ecop, + poseidon: builtin_costs.poseidon, + add_mod: builtin_costs.add_mod, + mul_mod: builtin_costs.mul_mod, + blake: builtin_costs.blake, + }); + + let args = args.to_owned(); + virtual_machine.call_contract(selector, gas, args, builtin_costs); + + let result = if cfg!(feature = "with-trace-dump") { + static COUNTER: AtomicU64 = AtomicU64::new(0); + let counter = COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + + let trace = virtual_machine.run_with_trace(&mut syscall_handler); + + let trace_path = PathBuf::from(format!("traces/emu/{counter}.json")); + let trace_parent_path = trace_path.parent().unwrap(); + fs::create_dir_all(trace_parent_path).unwrap(); + let trace_file = File::create(&trace_path).unwrap(); + serde_json::to_writer_pretty(trace_file, &trace).unwrap(); + + let sierra_path = PathBuf::from(format!("traces/{counter}.sierra")); + let mut sierra_file = File::create(&sierra_path).unwrap(); + write!(sierra_file, "{}", program).unwrap(); + + sierra_emu::ContractExecutionResult::from_trace(&trace).unwrap() + } else { + virtual_machine.run(&mut syscall_handler).unwrap() + }; + + Ok(ContractExecutionResult { + remaining_gas: result.remaining_gas, + failure_flag: result.failure_flag, + return_values: result.return_values, + error_msg: result.error_msg, + builtin_stats: Default::default(), + }) + } + #[cfg(any(feature = "with-trace-dump", feature = "with-libfunc-profiling"))] + ContractExecutor::AotWithProgram((executor, program)) => { + #[cfg(feature = "with-trace-dump")] + use { + cairo_lang_sierra::program_registry::ProgramRegistry, + cairo_native::metadata::trace_dump::TraceBinding, + cairo_native::metadata::trace_dump::trace_dump_runtime::{ + TRACE_DUMP, TraceDump, + }, + }; + #[cfg(feature = "with-libfunc-profiling")] + use { + cairo_native::metadata::profiler::ProfilerBinding, + cairo_native::metadata::profiler::{LIBFUNC_PROFILE, ProfilerImpl}, + }; + + static COUNTER: AtomicU64 = AtomicU64::new(0); + + #[cfg(feature = "with-trace-dump")] + let trace_dump_trace_id: &mut u64; + #[cfg(feature = "with-trace-dump")] + let trace_dump_old_trace_id: u64; + + #[cfg(feature = "with-libfunc-profiling")] + let libfunc_profiling_trace_id: &mut u64; + #[cfg(feature = "with-libfunc-profiling")] + let libfunc_profiling_old_trace_id: u64; + #[cfg(feature = "with-libfunc-profiling")] + let class_hash = *syscall_handler.base.call.class_hash; + #[cfg(feature = "with-libfunc-profiling")] + let tx_hash = syscall_handler + .base + .context + .tx_context + .tx_info + .transaction_hash() + .to_hex_string(); + #[cfg(feature = "with-libfunc-profiling")] + let block_number = + syscall_handler.base.context.tx_context.block_context.block_info.block_number.0; + + let counter = COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + + #[cfg(feature = "with-trace-dump")] + { + TRACE_DUMP + .lock() + .unwrap() + .insert(counter, TraceDump::new(ProgramRegistry::new(program).unwrap())); + + trace_dump_trace_id = unsafe { + let trace_id_ptr = + executor.find_symbol_ptr(TraceBinding::TraceId.symbol()).unwrap(); + trace_id_ptr.cast::().as_mut().unwrap() + }; + + trace_dump_old_trace_id = *trace_dump_trace_id; + *trace_dump_trace_id = counter; + } + + #[cfg(feature = "with-libfunc-profiling")] + { + LIBFUNC_PROFILE.lock().unwrap().insert(counter, ProfilerImpl::new()); + + libfunc_profiling_trace_id = unsafe { + let trace_id_ptr = + executor.find_symbol_ptr(ProfilerBinding::ProfileId.symbol()).unwrap(); + trace_id_ptr.cast::().as_mut().unwrap() + }; + + libfunc_profiling_old_trace_id = *libfunc_profiling_trace_id; + *libfunc_profiling_trace_id = counter; + } + + let result = executor.run(selector, args, gas, builtin_costs, syscall_handler); + + #[cfg(feature = "with-trace-dump")] + { + let trace = TRACE_DUMP.lock().unwrap().remove(&counter).unwrap().trace; + + let trace_path = PathBuf::from(format!("traces/native/{counter}.json")); + let trace_parent_path = trace_path.parent().unwrap(); + fs::create_dir_all(trace_parent_path).unwrap(); + let trace_file = File::create(&trace_path).unwrap(); + serde_json::to_writer_pretty(trace_file, &trace).unwrap(); + + *trace_dump_trace_id = trace_dump_old_trace_id; + } + + #[cfg(feature = "with-libfunc-profiling")] + { + let profile = LIBFUNC_PROFILE.lock().unwrap().remove(&counter).unwrap(); + + let raw_profile = profile.get_profile(program); + + let mut profiles_map = LIBFUNC_PROFILES_MAP.lock().unwrap(); + + let profile = EntrypointProfile { + class_hash, + selector, + profile: raw_profile, + program: program.clone(), + }; + + match profiles_map.get_mut(&tx_hash) { + Some(tx_profile) => { + tx_profile.entrypoint_profiles.push(profile); + } + None => { + let tx_profile = TransactionProfile { + block_number, + tx_hash: tx_hash.clone(), + entrypoint_profiles: vec![profile], + }; + profiles_map.insert(tx_hash, tx_profile); + } + }; + + *libfunc_profiling_trace_id = libfunc_profiling_old_trace_id; + } + + result + } + } + } +} + +// Implement the Sierra Emu StarknetSyscallHandler for NativeSyscallHandler. +// This delegates to the cairo-native StarknetSyscallHandler implementation, +// converting between sierra-emu and cairo-native types where necessary. +impl sierra_emu::starknet::StarknetSyscallHandler for &mut NativeSyscallHandler<'_> { + fn get_block_hash( + &mut self, + block_number: u64, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult { + StarknetSyscallHandler::get_block_hash(self, block_number, remaining_gas) + } + + fn get_execution_info( + &mut self, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult { + StarknetSyscallHandler::get_execution_info(self, remaining_gas).map(convert_execution_info) + } + + fn get_execution_info_v2( + &mut self, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult { + StarknetSyscallHandler::get_execution_info_v2(self, remaining_gas) + .map(convert_execution_info_v2) + } + + fn deploy( + &mut self, + class_hash: Felt, + contract_address_salt: Felt, + calldata: Vec, + deploy_from_zero: bool, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult<(Felt, Vec)> { + StarknetSyscallHandler::deploy( + self, + class_hash, + contract_address_salt, + &calldata, + deploy_from_zero, + remaining_gas, + ) + } + + fn replace_class( + &mut self, + class_hash: Felt, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult<()> { + StarknetSyscallHandler::replace_class(self, class_hash, remaining_gas) + } + + fn library_call( + &mut self, + class_hash: Felt, + function_selector: Felt, + calldata: Vec, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult> { + StarknetSyscallHandler::library_call( + self, + class_hash, + function_selector, + &calldata, + remaining_gas, + ) + } + + fn call_contract( + &mut self, + address: Felt, + entry_point_selector: Felt, + calldata: Vec, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult> { + StarknetSyscallHandler::call_contract( + self, + address, + entry_point_selector, + &calldata, + remaining_gas, + ) + } + + fn storage_read( + &mut self, + address_domain: u32, + address: Felt, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult { + StarknetSyscallHandler::storage_read(self, address_domain, address, remaining_gas) + } + + fn storage_write( + &mut self, + address_domain: u32, + address: Felt, + value: Felt, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult<()> { + StarknetSyscallHandler::storage_write(self, address_domain, address, value, remaining_gas) + } + + fn emit_event( + &mut self, + keys: Vec, + data: Vec, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult<()> { + StarknetSyscallHandler::emit_event(self, &keys, &data, remaining_gas) + } + + fn send_message_to_l1( + &mut self, + to_address: Felt, + payload: Vec, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult<()> { + StarknetSyscallHandler::send_message_to_l1(self, to_address, &payload, remaining_gas) + } + + fn keccak( + &mut self, + input: Vec, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult { + StarknetSyscallHandler::keccak(self, &input, remaining_gas).map(convert_u256) + } + + fn secp256k1_new( + &mut self, + x: sierra_emu::starknet::U256, + y: sierra_emu::starknet::U256, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult> { + StarknetSyscallHandler::secp256k1_new( + self, + convert_from_u256(x), + convert_from_u256(y), + remaining_gas, + ) + .map(|x| x.map(convert_secp_256_k1_point)) + } + + fn secp256k1_add( + &mut self, + p0: sierra_emu::starknet::Secp256k1Point, + p1: sierra_emu::starknet::Secp256k1Point, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult { + StarknetSyscallHandler::secp256k1_add( + self, + convert_from_secp_256_k1_point(p0), + convert_from_secp_256_k1_point(p1), + remaining_gas, + ) + .map(convert_secp_256_k1_point) + } + + fn secp256k1_mul( + &mut self, + p: sierra_emu::starknet::Secp256k1Point, + m: sierra_emu::starknet::U256, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult { + StarknetSyscallHandler::secp256k1_mul( + self, + convert_from_secp_256_k1_point(p), + convert_from_u256(m), + remaining_gas, + ) + .map(convert_secp_256_k1_point) + } + + fn secp256k1_get_point_from_x( + &mut self, + x: sierra_emu::starknet::U256, + y_parity: bool, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult> { + StarknetSyscallHandler::secp256k1_get_point_from_x( + self, + convert_from_u256(x), + y_parity, + remaining_gas, + ) + .map(|x| x.map(convert_secp_256_k1_point)) + } + + fn secp256k1_get_xy( + &mut self, + p: sierra_emu::starknet::Secp256k1Point, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult<(sierra_emu::starknet::U256, sierra_emu::starknet::U256)> + { + StarknetSyscallHandler::secp256k1_get_xy( + self, + convert_from_secp_256_k1_point(p), + remaining_gas, + ) + .map(|(x, y)| (convert_u256(x), convert_u256(y))) + } + + fn secp256r1_new( + &mut self, + x: sierra_emu::starknet::U256, + y: sierra_emu::starknet::U256, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult> { + StarknetSyscallHandler::secp256r1_new( + self, + convert_from_u256(x), + convert_from_u256(y), + remaining_gas, + ) + .map(|x| x.map(convert_secp_256_r1_point)) + } + + fn secp256r1_add( + &mut self, + p0: sierra_emu::starknet::Secp256r1Point, + p1: sierra_emu::starknet::Secp256r1Point, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult { + StarknetSyscallHandler::secp256r1_add( + self, + convert_from_secp_256_r1_point(p0), + convert_from_secp_256_r1_point(p1), + remaining_gas, + ) + .map(convert_secp_256_r1_point) + } + + fn secp256r1_mul( + &mut self, + p: sierra_emu::starknet::Secp256r1Point, + m: sierra_emu::starknet::U256, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult { + StarknetSyscallHandler::secp256r1_mul( + self, + convert_from_secp_256_r1_point(p), + convert_from_u256(m), + remaining_gas, + ) + .map(convert_secp_256_r1_point) + } + + fn secp256r1_get_point_from_x( + &mut self, + x: sierra_emu::starknet::U256, + y_parity: bool, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult> { + StarknetSyscallHandler::secp256r1_get_point_from_x( + self, + convert_from_u256(x), + y_parity, + remaining_gas, + ) + .map(|x| x.map(convert_secp_256_r1_point)) + } + + fn secp256r1_get_xy( + &mut self, + p: sierra_emu::starknet::Secp256r1Point, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult<(sierra_emu::starknet::U256, sierra_emu::starknet::U256)> + { + StarknetSyscallHandler::secp256r1_get_xy( + self, + convert_from_secp_256_r1_point(p), + remaining_gas, + ) + .map(|(x, y)| (convert_u256(x), convert_u256(y))) + } + + fn sha256_process_block( + &mut self, + mut prev_state: [u32; 8], + current_block: [u32; 16], + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult<[u32; 8]> { + StarknetSyscallHandler::sha256_process_block( + self, + &mut prev_state, + ¤t_block, + remaining_gas, + )?; + Ok(prev_state) + } + + fn meta_tx_v0( + &mut self, + address: Felt, + entry_point_selector: Felt, + calldata: Vec, + signature: Vec, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult> { + StarknetSyscallHandler::meta_tx_v0( + self, + address, + entry_point_selector, + &calldata, + &signature, + remaining_gas, + ) + } + + fn get_class_hash_at( + &mut self, + contract_address: Felt, + remaining_gas: &mut u64, + ) -> sierra_emu::starknet::SyscallResult { + StarknetSyscallHandler::get_class_hash_at(self, contract_address, remaining_gas) + } +} + +// Type conversion helpers between sierra-emu and cairo-native types. + +fn convert_u256(x: cairo_native::starknet::U256) -> sierra_emu::starknet::U256 { + sierra_emu::starknet::U256 { lo: x.lo, hi: x.hi } +} + +fn convert_from_u256(x: sierra_emu::starknet::U256) -> cairo_native::starknet::U256 { + cairo_native::starknet::U256 { lo: x.lo, hi: x.hi } +} + +fn convert_secp_256_k1_point( + x: cairo_native::starknet::Secp256k1Point, +) -> sierra_emu::starknet::Secp256k1Point { + sierra_emu::starknet::Secp256k1Point { x: convert_u256(x.x), y: convert_u256(x.y) } +} + +fn convert_from_secp_256_k1_point( + x: sierra_emu::starknet::Secp256k1Point, +) -> cairo_native::starknet::Secp256k1Point { + cairo_native::starknet::Secp256k1Point { + x: convert_from_u256(x.x), + y: convert_from_u256(x.y), + is_infinity: false, + } +} + +fn convert_secp_256_r1_point( + x: cairo_native::starknet::Secp256r1Point, +) -> sierra_emu::starknet::Secp256r1Point { + sierra_emu::starknet::Secp256r1Point { x: convert_u256(x.x), y: convert_u256(x.y) } +} + +fn convert_from_secp_256_r1_point( + x: sierra_emu::starknet::Secp256r1Point, +) -> cairo_native::starknet::Secp256r1Point { + cairo_native::starknet::Secp256r1Point { + x: convert_from_u256(x.x), + y: convert_from_u256(x.y), + is_infinity: false, + } +} + +fn convert_execution_info( + x: cairo_native::starknet::ExecutionInfo, +) -> sierra_emu::starknet::ExecutionInfo { + sierra_emu::starknet::ExecutionInfo { + block_info: convert_block_info(x.block_info), + tx_info: convert_tx_info(x.tx_info), + caller_address: x.caller_address, + contract_address: x.contract_address, + entry_point_selector: x.entry_point_selector, + } +} + +fn convert_tx_info(x: cairo_native::starknet::TxInfo) -> sierra_emu::starknet::TxInfo { + sierra_emu::starknet::TxInfo { + version: x.version, + account_contract_address: x.account_contract_address, + max_fee: x.max_fee, + signature: x.signature, + transaction_hash: x.transaction_hash, + chain_id: x.chain_id, + nonce: x.nonce, + } +} + +fn convert_execution_info_v2( + x: cairo_native::starknet::ExecutionInfoV2, +) -> sierra_emu::starknet::ExecutionInfoV2 { + sierra_emu::starknet::ExecutionInfoV2 { + block_info: convert_block_info(x.block_info), + tx_info: convert_tx_v2_info(x.tx_info), + caller_address: x.caller_address, + contract_address: x.contract_address, + entry_point_selector: x.entry_point_selector, + } +} + +fn convert_tx_v2_info(x: cairo_native::starknet::TxV2Info) -> sierra_emu::starknet::TxV2Info { + sierra_emu::starknet::TxV2Info { + version: x.version, + account_contract_address: x.account_contract_address, + max_fee: x.max_fee, + signature: x.signature, + transaction_hash: x.transaction_hash, + chain_id: x.chain_id, + nonce: x.nonce, + resource_bounds: x.resource_bounds.into_iter().map(convert_resource_bounds).collect(), + tip: x.tip, + paymaster_data: x.paymaster_data, + nonce_data_availability_mode: x.nonce_data_availability_mode, + fee_data_availability_mode: x.fee_data_availability_mode, + account_deployment_data: x.account_deployment_data, + } +} + +fn convert_resource_bounds( + resource_bounds: cairo_native::starknet::ResourceBounds, +) -> sierra_emu::starknet::ResourceBounds { + sierra_emu::starknet::ResourceBounds { + resource: resource_bounds.resource, + max_amount: resource_bounds.max_amount, + max_price_per_unit: resource_bounds.max_price_per_unit, + } +} + +fn convert_block_info(x: cairo_native::starknet::BlockInfo) -> sierra_emu::starknet::BlockInfo { + sierra_emu::starknet::BlockInfo { + block_number: x.block_number, + block_timestamp: x.block_timestamp, + sequencer_address: x.sequencer_address, + } +}