diff --git a/Cargo.lock b/Cargo.lock index be2d55eb69c..622806577d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4851,6 +4851,35 @@ dependencies = [ "xshell", ] +[[package]] +name = "cairo-lang-test-plugin" +version = "2.19.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaceca861261038c5162af5da78152dea4cc59a1197c32c742bee98e8d41b574" +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.19.0-rc.3" @@ -4890,8 +4919,7 @@ dependencies = [ [[package]] name = "cairo-native" version = "0.9.0-rc.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaaf48d22c1c9a0bb5aedf0a66f5b1a37f41dc3df5ce18480ce2abbed9a88657" +source = "git+https://github.com/starkware-libs/cairo_native.git?rev=6184f7a635110bb84fae58ffa082fb9b2d286ada#6184f7a635110bb84fae58ffa082fb9b2d286ada" dependencies = [ "aquamarine", "ark-ec 0.5.0", @@ -4909,6 +4937,7 @@ dependencies = [ "cairo-lang-starknet", "cairo-lang-starknet-classes", "cairo-lang-utils", + "cairo-starknet-syscalls", "educe 0.5.11", "itertools 0.14.0", "keccak 0.1.5", @@ -4922,10 +4951,10 @@ dependencies = [ "num-bigint", "num-integer", "num-traits", - "rand 0.9.2", "serde", "serde_json", "sha2 0.10.9", + "sierra-emu", "starknet-curve 0.6.0", "starknet-types-core", "tempfile", @@ -4960,6 +4989,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "cairo-starknet-syscalls" +version = "0.9.0-rc.7" +source = "git+https://github.com/starkware-libs/cairo_native.git?rev=6184f7a635110bb84fae58ffa082fb9b2d286ada#6184f7a635110bb84fae58ffa082fb9b2d286ada" +dependencies = [ + "serde", + "starknet-types-core", +] + [[package]] name = "cairo-vm" version = "3.2.0" @@ -6219,6 +6257,7 @@ dependencies = [ "ff", "generic-array", "group", + "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", @@ -8186,6 +8225,7 @@ dependencies = [ "once_cell", "serdect", "sha2 0.10.9", + "signature", ] [[package]] @@ -9957,6 +9997,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" @@ -10457,6 +10509,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" @@ -12285,6 +12346,46 @@ 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.7" +source = "git+https://github.com/starkware-libs/cairo_native.git?rev=6184f7a635110bb84fae58ffa082fb9b2d286ada#6184f7a635110bb84fae58ffa082fb9b2d286ada" +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-to-casm", + "cairo-lang-sierra-type-size", + "cairo-lang-starknet-classes", + "cairo-lang-test-plugin", + "cairo-lang-utils", + "cairo-starknet-syscalls", + "clap", + "generic-array", + "k256", + "keccak 0.1.5", + "num-bigint", + "num-integer", + "num-traits", + "p256", + "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" diff --git a/Cargo.toml b/Cargo.toml index 84f460a6e55..d6b9ece4b82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -260,7 +260,11 @@ cairo-lang-sierra = "2.19.0-rc.3" cairo-lang-sierra-to-casm = "2.19.0-rc.3" cairo-lang-starknet-classes = "2.19.0-rc.3" cairo-lang-utils = "2.19.0-rc.3" -cairo-native = "0.9.0-rc.7" +# TEMP: git dep pinned to the tip of the unreleased PR stack +# (starkware-libs/cairo_native#1610 → #1611 → #1612 → #1613) containing +# `EmuContractExecutor`, `AotWithProgram`, and `run_with_libfunc_profile`. +# Switch back to a crates.io version once those land in a published cairo-native release. +cairo-native = { git = "https://github.com/starkware-libs/cairo_native.git", rev = "6184f7a635110bb84fae58ffa082fb9b2d286ada", version = "0.9.0-rc.7" } cairo-program-runner-lib = "1.1.0" cairo-vm = "3.2.0" camelpaste = "0.1.0" diff --git a/crates/apollo_compile_to_native/Cargo.toml b/crates/apollo_compile_to_native/Cargo.toml index 60676c40b38..69b828bd27e 100644 --- a/crates/apollo_compile_to_native/Cargo.toml +++ b/crates/apollo_compile_to_native/Cargo.toml @@ -9,6 +9,19 @@ description = "A utility crate for compiling Sierra code into Cairo native." [lints] workspace = true +[features] +# Enables `SierraToNativeCompiler::compile_with_program`, which returns the +# AOT executor paired with the Sierra program required by cairo-native's +# libfunc profiler. +# +# This only enables profiling in the linked cairo-native library. The profiler +# symbol that `run_with_libfunc_profile` looks up is emitted by the compiler, so +# the `starknet-native-compile` binary must itself be built with its +# `with-libfunc-profiling` feature; a default-built binary yields `.so`s with no +# profiler symbol and every profiled call then fails. Point +# `SierraCompilationConfig.compiler_binary_path` at an instrumented binary. +with-libfunc-profiling = ["cairo-native/with-libfunc-profiling"] + [dependencies] apollo_compilation_utils.workspace = true apollo_compile_to_native_types.workspace = true diff --git a/crates/apollo_compile_to_native/src/compiler.rs b/crates/apollo_compile_to_native/src/compiler.rs index fd582b51e5f..4f92955cee4 100644 --- a/crates/apollo_compile_to_native/src/compiler.rs +++ b/crates/apollo_compile_to_native/src/compiler.rs @@ -1,4 +1,6 @@ use std::path::PathBuf; +#[cfg(feature = "with-libfunc-profiling")] +use std::sync::Arc; use apollo_compilation_utils::build_utils::verify_compiler_binary; use apollo_compilation_utils::compiler_utils::compile_with_args; @@ -8,6 +10,8 @@ use apollo_compilation_utils::resource_limits::ResourceLimits; use apollo_compile_to_native_types::SierraCompilationConfig; use cairo_lang_starknet_classes::contract_class::ContractClass; use cairo_native::executor::AotContractExecutor; +#[cfg(feature = "with-libfunc-profiling")] +use cairo_native::executor::AotWithProgram; use tempfile::NamedTempFile; use crate::constants::{CAIRO_NATIVE_BINARY_NAME, REQUIRED_CAIRO_NATIVE_VERSION}; @@ -59,4 +63,25 @@ impl SierraToNativeCompiler { .map_err(|e| CompilationUtilError::CompilationError(e.to_string()))? .unwrap()) } + + /// Like [`Self::compile`], but also returns the Sierra program so cairo-native's + /// libfunc profiler can resolve runtime libfunc IDs back to declarations. The + /// program is extracted from `contract_class` before compilation so the two are + /// guaranteed to correspond. + #[cfg(feature = "with-libfunc-profiling")] + pub fn compile_with_program( + &self, + contract_class: ContractClass, + ) -> Result { + let program = contract_class + .extract_sierra_program(false) + .map(|extracted| Arc::new(extracted.program)) + .map_err(|err| { + CompilationUtilError::UnexpectedError(format!( + "Failed to extract Sierra program for profiling: {err}" + )) + })?; + let executor = self.compile(contract_class)?; + Ok(AotWithProgram { executor, program }) + } } diff --git a/crates/blockifier/Cargo.toml b/crates/blockifier/Cargo.toml index ff988a45d4a..5e09c563612 100644 --- a/crates/blockifier/Cargo.toml +++ b/crates/blockifier/Cargo.toml @@ -23,6 +23,10 @@ node_api = [] only-native = ["cairo_native"] os_input = [] reexecution = ["transaction_serde"] +# Backs native contract classes with the sierra-emu interpreter instead of the AOT +# executor (testing / benchmarking builds only). Takes precedence over +# `with-libfunc-profiling` in the `NativeContractExecutor` resolution. +sierra-emu = ["cairo-native/sierra-emu", "cairo_native"] testing = [ "blockifier_test_utils", "expect-test", @@ -33,6 +37,15 @@ testing = [ ] tracing = [] transaction_serde = [] +# Enables libfunc-level profiling for native execution. Pulls in cairo-native's +# `with-libfunc-profiling` so `AotWithProgram::run_with_profile` is available, +# and apollo_compile_to_native's matching feature so `SierraToNativeCompiler` can +# pair the compiled executor with its Sierra program in one call. +with-libfunc-profiling = [ + "apollo_compile_to_native/with-libfunc-profiling", + "cairo-native/with-libfunc-profiling", + "cairo_native", +] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/blockifier/src/execution/native.rs b/crates/blockifier/src/execution/native.rs index 8b24ff59652..7fdd8ed3624 100644 --- a/crates/blockifier/src/execution/native.rs +++ b/crates/blockifier/src/execution/native.rs @@ -1,5 +1,8 @@ pub mod contract_class; pub mod entry_point_execution; +#[cfg(feature = "with-libfunc-profiling")] +pub mod profiling; +mod run_dispatch; 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..9d3798e95c3 100644 --- a/crates/blockifier/src/execution/native/contract_class.rs +++ b/crates/blockifier/src/execution/native/contract_class.rs @@ -2,7 +2,12 @@ use std::borrow::Cow; use std::ops::Deref; use std::sync::Arc; +#[cfg(not(any(feature = "sierra-emu", feature = "with-libfunc-profiling")))] use cairo_native::executor::AotContractExecutor; +#[cfg(all(feature = "with-libfunc-profiling", not(feature = "sierra-emu")))] +use cairo_native::executor::AotWithProgram; +#[cfg(feature = "sierra-emu")] +use cairo_native::executor::EmuContractExecutor; use starknet_api::contract_class::compiled_class_hash::HashableCompiledClass; use starknet_api::core::EntryPointSelector; use starknet_types_core::felt::Felt; @@ -10,6 +15,20 @@ 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; + +/// The executor backing native contract classes, resolved per build. All three types +/// expose the same `run` shape, so call sites are identical across builds. +/// +/// Production builds use cairo-native's AOT executor. The `sierra-emu` feature +/// (testing / benchmarking) swaps in the sierra-emu interpreter and takes precedence +/// over `with-libfunc-profiling`, which pairs the AOT executor with its Sierra program +/// so libfunc profiling samples can be resolved. +#[cfg(feature = "sierra-emu")] +pub type NativeContractExecutor = EmuContractExecutor; +#[cfg(all(feature = "with-libfunc-profiling", not(feature = "sierra-emu")))] +pub type NativeContractExecutor = AotWithProgram; +#[cfg(not(any(feature = "sierra-emu", feature = "with-libfunc-profiling")))] +pub type NativeContractExecutor = AotContractExecutor; #[derive(Clone, Debug, PartialEq, Eq)] pub struct NativeCompiledClassV1(pub Arc); impl Deref for NativeCompiledClassV1 { @@ -28,8 +47,9 @@ impl NativeCompiledClassV1 { /// Initialize a compiled class for native. /// /// executor must be derived from sierra_program which in turn must be derived from - /// sierra_contract_class. - pub fn new(executor: AotContractExecutor, casm: CompiledClassV1) -> NativeCompiledClassV1 { + /// sierra_contract_class. The executor's type -- and therefore this constructor's + /// signature -- follows the build's [`NativeContractExecutor`] resolution. + pub fn new(executor: NativeContractExecutor, casm: CompiledClassV1) -> NativeCompiledClassV1 { let contract = NativeCompiledClassV1Inner::new(executor, casm); Self(Arc::new(contract)) @@ -71,12 +91,12 @@ impl HashableCompiledClass for NativeCompiledCla #[derive(Debug)] pub struct NativeCompiledClassV1Inner { - pub executor: AotContractExecutor, + pub executor: NativeContractExecutor, casm: CompiledClassV1, } impl NativeCompiledClassV1Inner { - fn new(executor: AotContractExecutor, casm: CompiledClassV1) -> Self { + fn new(executor: NativeContractExecutor, 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 8b934339330..10f6e500283 100644 --- a/crates/blockifier/src/execution/native/entry_point_execution.rs +++ b/crates/blockifier/src/execution/native/entry_point_execution.rs @@ -14,6 +14,7 @@ use crate::execution::contract_class::TrackedResource; use crate::execution::entry_point::{EntryPointExecutionContext, ExecutableCallEntryPoint}; use crate::execution::errors::{EntryPointExecutionError, PostExecutionError, PreExecutionError}; use crate::execution::native::contract_class::NativeCompiledClassV1; +use crate::execution::native::run_dispatch::run_native_executor; use crate::execution::native::syscall_handler::NativeSyscallHandler; use crate::state::state_api::State; use crate::transaction::objects::ExecutionResourcesTraits; @@ -57,11 +58,12 @@ pub fn execute_entry_point_call( .checked_sub(initial_budget) .ok_or(PreExecutionError::InsufficientEntryPointGas)?; - let execution_result = compiled_class.executor.run( + let execution_result = run_native_executor( + &compiled_class.executor, entry_point.selector.0, &syscall_handler.base.call.calldata.0.clone(), call_initial_gas, - Some(builtin_costs), + builtin_costs, &mut syscall_handler, ); diff --git a/crates/blockifier/src/execution/native/profiling.rs b/crates/blockifier/src/execution/native/profiling.rs new file mode 100644 index 00000000000..0767f9cafb1 --- /dev/null +++ b/crates/blockifier/src/execution/native/profiling.rs @@ -0,0 +1,68 @@ +//! Blockifier-side storage for libfunc profiles collected by +//! `cairo_native::executor::AotWithProgram::run_with_profile`. +//! +//! cairo-native owns the profiling primitive; this module only provides the keying +//! (transaction hash / block number / class hash + selector) that's not visible from +//! cairo-native's layer. +//! +//! Intended for single-tenant benchmarking. The map grows without bound — drain it +//! externally between runs. + +use std::collections::HashMap; +use std::sync::{LazyLock, Mutex}; + +use cairo_native::executor::ArcProgram; +use cairo_native::metadata::profiler::Profile; +use starknet_types_core::felt::Felt; + +use crate::execution::native::syscall_handler::NativeSyscallHandler; + +pub struct EntrypointProfile { + pub class_hash: Felt, + pub selector: Felt, + pub profile: Profile, + pub program: ArcProgram, +} + +pub struct TransactionProfile { + pub block_number: u64, + pub tx_hash: String, + pub entrypoint_profiles: Vec, +} + +type ProfilesByBlockTx = HashMap; + +pub static LIBFUNC_PROFILES_MAP: LazyLock> = + LazyLock::new(|| Mutex::new(HashMap::new())); + +/// Builds an `FnOnce(Profile, ArcProgram)` that, when invoked, records the captured +/// profile in `LIBFUNC_PROFILES_MAP` keyed by the syscall handler's current transaction +/// hash. The program is supplied by cairo-native's +/// `AotWithProgram::run_with_profile` callback alongside the profile. +/// +/// All keying data is extracted up front so the closure doesn't re-borrow +/// `syscall_handler` — required because the closure outlives the call to +/// `AotWithProgram::run_with_profile`, which itself holds a `&mut` to the handler. +pub fn record_profile_for( + syscall_handler: &NativeSyscallHandler<'_>, + selector: Felt, +) -> impl FnOnce(Profile, ArcProgram) + 'static { + let class_hash = *syscall_handler.base.call.class_hash; + let tx_hash = + syscall_handler.base.context.tx_context.tx_info.transaction_hash().to_hex_string(); + let block_number = + syscall_handler.base.context.tx_context.block_context.block_info.block_number.0; + + move |profile, program| { + let entry = EntrypointProfile { class_hash, selector, profile, program }; + let mut map = LIBFUNC_PROFILES_MAP.lock().unwrap(); + map.entry(tx_hash.clone()) + .or_insert_with(|| TransactionProfile { + block_number, + tx_hash: tx_hash.clone(), + entrypoint_profiles: Vec::new(), + }) + .entrypoint_profiles + .push(entry); + } +} diff --git a/crates/blockifier/src/execution/native/run_dispatch.rs b/crates/blockifier/src/execution/native/run_dispatch.rs new file mode 100644 index 00000000000..f76353d9f56 --- /dev/null +++ b/crates/blockifier/src/execution/native/run_dispatch.rs @@ -0,0 +1,46 @@ +//! Dispatches a [`NativeContractExecutor`] entry-point call, transparently routing +//! through `AotWithProgram::run_with_profile` when libfunc profiling is enabled. +//! +//! Keeps the libfunc-profiling cfg-noise out of the main entry-point execution path. + +use cairo_native::error::Result; +use cairo_native::execution_result::ContractExecutionResult; +use cairo_native::utils::BuiltinCosts; +use starknet_types_core::felt::Felt; + +use crate::execution::native::contract_class::NativeContractExecutor; +use crate::execution::native::syscall_handler::NativeSyscallHandler; + +/// Runs an entry point on `executor`. Always available. +/// +/// All [`NativeContractExecutor`] candidates expose the same `run` shape, so the call +/// site is identical across builds. When `with-libfunc-profiling` is enabled (and +/// `sierra-emu` is not -- the interpreter takes precedence and collects no profiles), +/// the call is routed through `AotWithProgram::run_with_profile`, which invokes our +/// callback with the captured profile and the program the executor was built from; we +/// record it into [`crate::execution::native::profiling::LIBFUNC_PROFILES_MAP`] keyed +/// by the current transaction hash. +pub fn run_native_executor( + executor: &NativeContractExecutor, + selector: Felt, + calldata: &[Felt], + call_initial_gas: u64, + builtin_costs: BuiltinCosts, + syscall_handler: &mut NativeSyscallHandler<'_>, +) -> Result { + #[cfg(all(feature = "with-libfunc-profiling", not(feature = "sierra-emu")))] + { + let on_profile = + crate::execution::native::profiling::record_profile_for(syscall_handler, selector); + executor.run_with_profile( + selector, + calldata, + call_initial_gas, + Some(builtin_costs), + syscall_handler, + on_profile, + ) + } + #[cfg(any(not(feature = "with-libfunc-profiling"), feature = "sierra-emu"))] + executor.run(selector, calldata, call_initial_gas, Some(builtin_costs), syscall_handler) +} diff --git a/crates/blockifier/src/state/native_class_manager.rs b/crates/blockifier/src/state/native_class_manager.rs index 64cbf5ca90c..3c895280f8f 100644 --- a/crates/blockifier/src/state/native_class_manager.rs +++ b/crates/blockifier/src/state/native_class_manager.rs @@ -7,6 +7,13 @@ use apollo_compilation_utils::errors::CompilationUtilError; use apollo_compile_to_native::compiler::SierraToNativeCompiler; #[cfg(any(feature = "testing", test))] use cached::Cached; +#[cfg(feature = "sierra-emu")] +use cairo_lang_starknet_classes::contract_class::{ + version_id_from_serialized_sierra_program, + ContractClass as CairoLangContractClass, +}; +#[cfg(feature = "sierra-emu")] +use cairo_native::executor::EmuContractExecutor; use log; use starknet_api::class_cache::GlobalContractCache; use starknet_api::core::{ClassHash, CompiledClassHash}; @@ -273,7 +280,9 @@ fn run_compilation_worker( /// Processes a compilation request and caches the result. fn process_compilation_request( class_cache: RawClassCache, - compiler: Arc, + #[cfg_attr(feature = "sierra-emu", allow(unused_variables))] compiler: Arc< + SierraToNativeCompiler, + >, compilation_request: CompilationRequest, panic_on_compilation_failure: bool, ) -> Result<(), CompilationUtilError> { @@ -283,7 +292,16 @@ fn process_compilation_request( return Ok(()); } let sierra_for_compilation = into_contract_class_for_compilation(sierra.as_ref()); + let start = Instant::now(); + // The construction below matches the build's `NativeContractExecutor` resolution: + // sierra-emu (no native compilation; the interpreter consumes the Sierra program + // directly), AOT paired with its Sierra program (libfunc profiling), or plain AOT. + #[cfg(feature = "sierra-emu")] + let compilation_result = emu_contract_executor(sierra_for_compilation); + #[cfg(all(feature = "with-libfunc-profiling", not(feature = "sierra-emu")))] + let compilation_result = compiler.compile_with_program(sierra_for_compilation); + #[cfg(not(any(feature = "with-libfunc-profiling", feature = "sierra-emu")))] let compilation_result = compiler.compile(sierra_for_compilation); let duration = start.elapsed(); log::info!( @@ -292,8 +310,8 @@ fn process_compilation_request( duration.as_secs_f32() ); match compilation_result { - Ok(executor) => { - let native_compiled_class = NativeCompiledClassV1::new(executor, casm); + Ok(compiled) => { + let native_compiled_class = NativeCompiledClassV1::new(compiled, casm); class_cache.set( class_hash, CompiledClasses::V1Native(CachedCairoNative::Compiled(native_compiled_class)), @@ -318,3 +336,32 @@ fn process_compilation_request( } } } + +/// Builds a sierra-emu-backed executor from the Sierra class. Nothing is compiled -- +/// the interpreter consumes the Sierra program directly. +#[cfg(feature = "sierra-emu")] +fn emu_contract_executor( + contract_class: CairoLangContractClass, +) -> Result { + let (sierra_version, _compiler_version) = version_id_from_serialized_sierra_program( + &contract_class.sierra_program, + ) + .map_err(|err| { + CompilationUtilError::UnexpectedError(format!( + "Failed to extract Sierra version for sierra-emu execution: {err}" + )) + })?; + let program = contract_class + .extract_sierra_program(false) + .map(|extracted| Arc::new(extracted.program)) + .map_err(|err| { + CompilationUtilError::UnexpectedError(format!( + "Failed to extract Sierra program for sierra-emu execution: {err}" + )) + })?; + Ok(EmuContractExecutor { + program, + entry_points: contract_class.entry_points_by_type, + sierra_version, + }) +} diff --git a/crates/blockifier/src/test_utils/struct_impls.rs b/crates/blockifier/src/test_utils/struct_impls.rs index 73a698ea123..1e7223d5067 100644 --- a/crates/blockifier/src/test_utils/struct_impls.rs +++ b/crates/blockifier/src/test_utils/struct_impls.rs @@ -8,8 +8,12 @@ use blockifier_test_utils::contracts::get_raw_contract_class; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; #[cfg(feature = "cairo_native")] use cairo_lang_starknet_classes::contract_class::ContractClass as SierraContractClass; -#[cfg(feature = "cairo_native")] +#[cfg(all(feature = "cairo_native", not(feature = "sierra-emu")))] use cairo_native::executor::AotContractExecutor; +#[cfg(all(feature = "with-libfunc-profiling", not(feature = "sierra-emu")))] +use cairo_native::executor::AotWithProgram; +#[cfg(feature = "sierra-emu")] +use cairo_native::executor::EmuContractExecutor; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use starknet_api::block::{BlockInfo, BlockNumber}; use starknet_api::contract_address; @@ -297,7 +301,9 @@ impl NativeCompiledClassV1 { let sierra_version = SierraVersion::extract_from_program(&sierra_version_values) .expect("Cannot extract sierra version from sierra program"); - let executor = AotContractExecutor::new( + // Executor construction matches the build's `NativeContractExecutor` resolution. + #[cfg(not(feature = "sierra-emu"))] + let aot_executor = AotContractExecutor::new( &extracted.program, &sierra_contract_class.entry_points_by_type, sierra_version.clone().into(), @@ -308,6 +314,18 @@ impl NativeCompiledClassV1 { ) .expect("Cannot compile sierra into native"); + #[cfg(feature = "sierra-emu")] + let executor = EmuContractExecutor { + program: Arc::new(extracted.program.clone()), + entry_points: sierra_contract_class.entry_points_by_type.clone(), + sierra_version: sierra_version.clone().into(), + }; + #[cfg(all(feature = "with-libfunc-profiling", not(feature = "sierra-emu")))] + let executor = + AotWithProgram { executor: aot_executor, program: Arc::new(extracted.program.clone()) }; + #[cfg(not(any(feature = "sierra-emu", feature = "with-libfunc-profiling")))] + let executor = aot_executor; + // Compile the sierra contract class into casm. let casm_contract_class = CasmContractClass::from_contract_class( sierra_contract_class,