From 5aa0110feb3092ec012ef4e41c94416e9569f4b7 Mon Sep 17 00:00:00 2001 From: Avi Cohen Date: Sun, 17 May 2026 15:57:52 +0300 Subject: [PATCH 1/7] blockifier,apollo_compile_to_native: adopt cairo_native::ContractExecutor + libfunc profiling Co-Authored-By: Claude Opus 4.7 (1M context) --- Cargo.lock | 107 +++++++++++++++++- Cargo.toml | 6 +- crates/apollo_compile_to_native/Cargo.toml | 6 + .../apollo_compile_to_native/src/compiler.rs | 25 ++++ crates/blockifier/Cargo.toml | 12 ++ crates/blockifier/src/execution/native.rs | 3 + .../src/execution/native/contract_class.rs | 33 +++++- .../execution/native/entry_point_execution.rs | 6 +- .../src/execution/native/profiling.rs | 68 +++++++++++ .../src/execution/native/run_dispatch.rs | 45 ++++++++ .../src/state/native_class_manager.rs | 13 ++- 11 files changed, 312 insertions(+), 12 deletions(-) create mode 100644 crates/blockifier/src/execution/native/profiling.rs create mode 100644 crates/blockifier/src/execution/native/run_dispatch.rs diff --git a/Cargo.lock b/Cargo.lock index be2d55eb69c..c76358fdc41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4851,6 +4851,35 @@ dependencies = [ "xshell", ] +[[package]] +name = "cairo-lang-test-plugin" +version = "2.19.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bb3a904befddc475d7f4d70a6c58f42a0ce49497d71b04b8fa5aa766953c2b" +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=a21f3f57128d2a91067fa31bff92bd00bdc85ba5#a21f3f57128d2a91067fa31bff92bd00bdc85ba5" 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", @@ -4926,6 +4955,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", + "sierra-emu", "starknet-curve 0.6.0", "starknet-types-core", "tempfile", @@ -4960,6 +4990,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=a21f3f57128d2a91067fa31bff92bd00bdc85ba5#a21f3f57128d2a91067fa31bff92bd00bdc85ba5" +dependencies = [ + "serde", + "starknet-types-core", +] + [[package]] name = "cairo-vm" version = "3.2.0" @@ -6219,6 +6258,7 @@ dependencies = [ "ff", "generic-array", "group", + "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", @@ -8186,6 +8226,7 @@ dependencies = [ "once_cell", "serdect", "sha2 0.10.9", + "signature", ] [[package]] @@ -9957,6 +9998,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 +10510,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 +12347,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.7" +source = "git+https://github.com/starkware-libs/cairo_native.git?rev=a21f3f57128d2a91067fa31bff92bd00bdc85ba5#a21f3f57128d2a91067fa31bff92bd00bdc85ba5" +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", + "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" diff --git a/Cargo.toml b/Cargo.toml index 84f460a6e55..119b07f9ad1 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 +# `ContractExecutor`, `EmuContractInfo`, `AotWithProgram`, and `run_with_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 = "7107710f05801b63f3f40d863dd8ed38680cc6f6" } 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..c133cc7076c 100644 --- a/crates/apollo_compile_to_native/Cargo.toml +++ b/crates/apollo_compile_to_native/Cargo.toml @@ -9,6 +9,12 @@ 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. +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..44fbd2667ee 100644 --- a/crates/blockifier/Cargo.toml +++ b/crates/blockifier/Cargo.toml @@ -23,6 +23,9 @@ node_api = [] only-native = ["cairo_native"] os_input = [] reexecution = ["transaction_serde"] +# Enables the sierra-emu execution path of `cairo_native::ContractExecutor`. Pulls in +# cairo-native's `sierra-emu` feature which compiles `SierraEmuSyscallBridge`. +sierra-emu = ["cairo-native/sierra-emu", "cairo_native"] testing = [ "blockifier_test_utils", "expect-test", @@ -33,6 +36,15 @@ testing = [ ] tracing = [] transaction_serde = [] +# Enables libfunc-level profiling for native execution. Pulls in cairo-native's +# `with-libfunc-profiling` so `ContractExecutor::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..c1c5bad93e1 100644 --- a/crates/blockifier/src/execution/native/contract_class.rs +++ b/crates/blockifier/src/execution/native/contract_class.rs @@ -2,7 +2,11 @@ use std::borrow::Cow; use std::ops::Deref; use std::sync::Arc; -use cairo_native::executor::AotContractExecutor; +#[cfg(feature = "with-libfunc-profiling")] +use cairo_native::executor::AotWithProgram; +#[cfg(feature = "sierra-emu")] +use cairo_native::executor::EmuContractInfo; +use cairo_native::executor::{AotContractExecutor, ContractExecutor}; use starknet_api::contract_class::compiled_class_hash::HashableCompiledClass; use starknet_api::core::EntryPointSelector; use starknet_types_core::felt::Felt; @@ -30,7 +34,28 @@ impl NativeCompiledClassV1 { /// 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 { - let contract = NativeCompiledClassV1Inner::new(executor, casm); + let contract = NativeCompiledClassV1Inner::new(executor.into(), casm); + + Self(Arc::new(contract)) + } + + /// Initialize a compiled class backed by the sierra-emu interpreter instead of the AOT + /// executor. Used by benchmarking / replay tooling that wants to execute through the emu + /// VM while reusing the rest of the blockifier pipeline. + #[cfg(feature = "sierra-emu")] + pub fn new_from_emu(info: EmuContractInfo, casm: CompiledClassV1) -> NativeCompiledClassV1 { + let contract = NativeCompiledClassV1Inner::new(info.into(), casm); + + Self(Arc::new(contract)) + } + + /// Like [`Self::new`], but also stores the Sierra `Program` (via the cairo-native + /// [`AotWithProgram`] pairing) so [`cairo_native::ContractExecutor::run_with_profile`] + /// can resolve libfunc samples. Only callable when the `with-libfunc-profiling` + /// feature is enabled. + #[cfg(feature = "with-libfunc-profiling")] + pub fn new_with_program(info: AotWithProgram, casm: CompiledClassV1) -> NativeCompiledClassV1 { + let contract = NativeCompiledClassV1Inner::new(info.into(), casm); Self(Arc::new(contract)) } @@ -71,12 +96,12 @@ 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 { + fn new(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 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..04cff2a213a --- /dev/null +++ b/crates/blockifier/src/execution/native/profiling.rs @@ -0,0 +1,68 @@ +//! Blockifier-side storage for libfunc profiles collected by +//! [`cairo_native::ContractExecutor::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::{Arc, LazyLock, Mutex}; + +use cairo_native::executor::Program; +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: Arc, +} + +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, Arc)` 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 +/// [`cairo_native::ContractExecutor::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 +/// `ContractExecutor::run_with_profile`, which itself holds a `&mut` to the handler. +pub fn record_profile_for( + syscall_handler: &NativeSyscallHandler<'_>, + selector: Felt, +) -> impl FnOnce(Profile, Arc) + '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..d3ddd4cfc81 --- /dev/null +++ b/crates/blockifier/src/execution/native/run_dispatch.rs @@ -0,0 +1,45 @@ +//! Dispatches a [`ContractExecutor`] entry-point call, transparently routing through +//! [`ContractExecutor::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::executor::ContractExecutor; +use cairo_native::utils::BuiltinCosts; +use starknet_types_core::felt::Felt; + +use crate::execution::native::syscall_handler::NativeSyscallHandler; + +/// Runs an entry point on `executor`. Always available. +/// +/// When `with-libfunc-profiling` is enabled the call is routed through +/// [`ContractExecutor::run_with_profile`]; for the `AotWithProgram` variant cairo-native +/// invokes our callback with the captured profile and the program it was built from, which +/// we record into [`crate::execution::native::profiling::LIBFUNC_PROFILES_MAP`] keyed by +/// the current transaction hash. For other variants cairo-native falls through to +/// [`ContractExecutor::run`] and the callback is never invoked. +pub fn run_native_executor( + executor: &ContractExecutor, + selector: Felt, + calldata: &[Felt], + call_initial_gas: u64, + builtin_costs: BuiltinCosts, + syscall_handler: &mut NativeSyscallHandler<'_>, +) -> Result { + #[cfg(feature = "with-libfunc-profiling")] + { + 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(not(feature = "with-libfunc-profiling"))] + 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..07defb94847 100644 --- a/crates/blockifier/src/state/native_class_manager.rs +++ b/crates/blockifier/src/state/native_class_manager.rs @@ -283,8 +283,16 @@ fn process_compilation_request( return Ok(()); } let sierra_for_compilation = into_contract_class_for_compilation(sierra.as_ref()); + let start = Instant::now(); - let compilation_result = compiler.compile(sierra_for_compilation); + #[cfg(feature = "with-libfunc-profiling")] + let compilation_result = compiler + .compile_with_program(sierra_for_compilation) + .map(|info| NativeCompiledClassV1::new_with_program(info, casm.clone())); + #[cfg(not(feature = "with-libfunc-profiling"))] + let compilation_result = compiler + .compile(sierra_for_compilation) + .map(|executor| NativeCompiledClassV1::new(executor, casm.clone())); let duration = start.elapsed(); log::info!( "Compiling to native contract with class hash: {:#066x}. Duration: {:.3} seconds", @@ -292,8 +300,7 @@ fn process_compilation_request( duration.as_secs_f32() ); match compilation_result { - Ok(executor) => { - let native_compiled_class = NativeCompiledClassV1::new(executor, casm); + Ok(native_compiled_class) => { class_cache.set( class_hash, CompiledClasses::V1Native(CachedCairoNative::Compiled(native_compiled_class)), From 03d674691a96776cb7f047c2cd0d40eee07cdd03 Mon Sep 17 00:00:00 2001 From: Avi Cohen Date: Mon, 18 May 2026 18:01:12 +0300 Subject: [PATCH 2/7] workspace: declare cairo-native version on git dep for version sync test Cargo accepts both git+rev and a version field together; the field is a SemVer constraint on the package at the git rev. The rev's Cargo.toml already pins 0.9.0-rc.6, matching native_compiler_version.txt, so the constants test required_cairo_native_version_test parses the version and succeeds. Co-Authored-By: Claude Opus 4.7 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 119b07f9ad1..1dce54c0913 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -264,7 +264,7 @@ cairo-lang-utils = "2.19.0-rc.3" # (starkware-libs/cairo_native#1610 → #1611 → #1612 → #1613) containing # `ContractExecutor`, `EmuContractInfo`, `AotWithProgram`, and `run_with_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 = "7107710f05801b63f3f40d863dd8ed38680cc6f6" } +cairo-native = { git = "https://github.com/starkware-libs/cairo_native.git", rev = "7107710f05801b63f3f40d863dd8ed38680cc6f6", version = "0.9.0-rc.7" } cairo-program-runner-lib = "1.1.0" cairo-vm = "3.2.0" camelpaste = "0.1.0" From d70c4b7bde1e93a5c340ff0c63cbc4845b8cdebe Mon Sep 17 00:00:00 2001 From: Avi Cohen Date: Tue, 19 May 2026 15:40:06 +0300 Subject: [PATCH 3/7] blockifier: address review feedback on native compilation path - Drop unused `NativeCompiledClassV1::new_from_emu`; it had no in-tree callers, and the sierra-emu construction path will be re-added when the benchmarking/replay tool that needs it lands. - Revert `process_compilation_request`'s `.map(...)` shape to a direct `match`, moving `casm` into `NativeCompiledClassV1::new(_with_program)` in the `Ok` arm at zero cost instead of cloning it. - Bump pinned cairo-native rev to pick up `cargo fmt` fix on starkware-libs/cairo_native#1613. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/execution/native/contract_class.rs | 12 ------------ .../blockifier/src/state/native_class_manager.rs | 14 +++++++------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/crates/blockifier/src/execution/native/contract_class.rs b/crates/blockifier/src/execution/native/contract_class.rs index c1c5bad93e1..7dc2fb6bfda 100644 --- a/crates/blockifier/src/execution/native/contract_class.rs +++ b/crates/blockifier/src/execution/native/contract_class.rs @@ -4,8 +4,6 @@ use std::sync::Arc; #[cfg(feature = "with-libfunc-profiling")] use cairo_native::executor::AotWithProgram; -#[cfg(feature = "sierra-emu")] -use cairo_native::executor::EmuContractInfo; use cairo_native::executor::{AotContractExecutor, ContractExecutor}; use starknet_api::contract_class::compiled_class_hash::HashableCompiledClass; use starknet_api::core::EntryPointSelector; @@ -39,16 +37,6 @@ impl NativeCompiledClassV1 { Self(Arc::new(contract)) } - /// Initialize a compiled class backed by the sierra-emu interpreter instead of the AOT - /// executor. Used by benchmarking / replay tooling that wants to execute through the emu - /// VM while reusing the rest of the blockifier pipeline. - #[cfg(feature = "sierra-emu")] - pub fn new_from_emu(info: EmuContractInfo, casm: CompiledClassV1) -> NativeCompiledClassV1 { - let contract = NativeCompiledClassV1Inner::new(info.into(), casm); - - Self(Arc::new(contract)) - } - /// Like [`Self::new`], but also stores the Sierra `Program` (via the cairo-native /// [`AotWithProgram`] pairing) so [`cairo_native::ContractExecutor::run_with_profile`] /// can resolve libfunc samples. Only callable when the `with-libfunc-profiling` diff --git a/crates/blockifier/src/state/native_class_manager.rs b/crates/blockifier/src/state/native_class_manager.rs index 07defb94847..847ca6b8e31 100644 --- a/crates/blockifier/src/state/native_class_manager.rs +++ b/crates/blockifier/src/state/native_class_manager.rs @@ -286,13 +286,9 @@ fn process_compilation_request( let start = Instant::now(); #[cfg(feature = "with-libfunc-profiling")] - let compilation_result = compiler - .compile_with_program(sierra_for_compilation) - .map(|info| NativeCompiledClassV1::new_with_program(info, casm.clone())); + let compilation_result = compiler.compile_with_program(sierra_for_compilation); #[cfg(not(feature = "with-libfunc-profiling"))] - let compilation_result = compiler - .compile(sierra_for_compilation) - .map(|executor| NativeCompiledClassV1::new(executor, casm.clone())); + let compilation_result = compiler.compile(sierra_for_compilation); let duration = start.elapsed(); log::info!( "Compiling to native contract with class hash: {:#066x}. Duration: {:.3} seconds", @@ -300,7 +296,11 @@ fn process_compilation_request( duration.as_secs_f32() ); match compilation_result { - Ok(native_compiled_class) => { + Ok(compiled) => { + #[cfg(feature = "with-libfunc-profiling")] + let native_compiled_class = NativeCompiledClassV1::new_with_program(compiled, casm); + #[cfg(not(feature = "with-libfunc-profiling"))] + let native_compiled_class = NativeCompiledClassV1::new(compiled, casm); class_cache.set( class_hash, CompiledClasses::V1Native(CachedCairoNative::Compiled(native_compiled_class)), From f3f3cefc2d4c49c3159ad80c6e50ae7743a132b3 Mon Sep 17 00:00:00 2001 From: Avi Cohen Date: Wed, 20 May 2026 13:07:03 +0300 Subject: [PATCH 4/7] blockifier: re-add new_from_emu for out-of-tree benchmarking branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `new_from_emu` was deleted in 88b68af9 after Cursor flagged it as unused. Restore it (with a doc comment naming the planned consumer) so the benchmarking / replay tooling — which will live as a feature branch and is not expected to merge here — can reach it without patching this file. No `#[expect(dead_code)]` needed: `dead_code` doesn't fire on `pub fn` in a library crate. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/execution/native/contract_class.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/blockifier/src/execution/native/contract_class.rs b/crates/blockifier/src/execution/native/contract_class.rs index 7dc2fb6bfda..a61e865f7cd 100644 --- a/crates/blockifier/src/execution/native/contract_class.rs +++ b/crates/blockifier/src/execution/native/contract_class.rs @@ -4,6 +4,8 @@ use std::sync::Arc; #[cfg(feature = "with-libfunc-profiling")] use cairo_native::executor::AotWithProgram; +#[cfg(feature = "sierra-emu")] +use cairo_native::executor::EmuContractInfo; use cairo_native::executor::{AotContractExecutor, ContractExecutor}; use starknet_api::contract_class::compiled_class_hash::HashableCompiledClass; use starknet_api::core::EntryPointSelector; @@ -37,6 +39,17 @@ impl NativeCompiledClassV1 { Self(Arc::new(contract)) } + /// Initialize a compiled class backed by the sierra-emu interpreter instead of the AOT + /// executor. Intended for the out-of-tree benchmarking / replay feature branch — those + /// tools are not expected to merge into this repo and will consume this constructor + /// from a fork. `pub` so a fork can reach it without patching this file. + #[cfg(feature = "sierra-emu")] + pub fn new_from_emu(info: EmuContractInfo, casm: CompiledClassV1) -> NativeCompiledClassV1 { + let contract = NativeCompiledClassV1Inner::new(info.into(), casm); + + Self(Arc::new(contract)) + } + /// Like [`Self::new`], but also stores the Sierra `Program` (via the cairo-native /// [`AotWithProgram`] pairing) so [`cairo_native::ContractExecutor::run_with_profile`] /// can resolve libfunc samples. Only callable when the `with-libfunc-profiling` From 2e4845260524f874202ff7dbb4dbb8b996260fcf Mon Sep 17 00:00:00 2001 From: Avi Cohen Date: Mon, 1 Jun 2026 16:43:58 +0300 Subject: [PATCH 5/7] apollo_compile_to_native: document instrumented-binary requirement for libfunc profiling The with-libfunc-profiling feature only enables profiling in the linked cairo-native library; the profiler symbol that run_with_libfunc_profile resolves is emitted by the compiler binary. Document that starknet-native-compile must itself be built with with-libfunc-profiling (and that compiler_binary_path can point at such a binary), otherwise every profiled native call fails on a missing symbol. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/apollo_compile_to_native/Cargo.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/apollo_compile_to_native/Cargo.toml b/crates/apollo_compile_to_native/Cargo.toml index c133cc7076c..69b828bd27e 100644 --- a/crates/apollo_compile_to_native/Cargo.toml +++ b/crates/apollo_compile_to_native/Cargo.toml @@ -13,6 +13,13 @@ workspace = true # 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] From 536db2384ba3049b7a7a7ce1cea32d65134d5a9c Mon Sep 17 00:00:00 2001 From: Avi Cohen Date: Mon, 6 Jul 2026 15:15:07 +0300 Subject: [PATCH 6/7] blockifier: replace ContractExecutor enum with cfg-resolved NativeContractExecutor The enum dispatched between executor backends that are always selected at compile time, never at runtime. NativeCompiledClassV1 now stores a NativeContractExecutor type alias resolved per build: AotContractExecutor in production, EmuContractExecutor under sierra-emu (which now skips native compilation in the class manager and runs the interpreter), AotWithProgram under with-libfunc-profiling. All three expose the same run() shape, so call sites are unchanged across builds; sierra-emu takes precedence over profiling in the resolution. Pulls the reworked cairo-native stack (no ContractExecutor enum; EmuContractExecutor + AotWithProgram with inherent run methods). Co-Authored-By: Claude Fable 5 --- Cargo.lock | 12 ++--- Cargo.toml | 4 +- crates/blockifier/Cargo.toml | 7 +-- .../src/execution/native/contract_class.rs | 54 +++++++++---------- .../src/execution/native/profiling.rs | 16 +++--- .../src/execution/native/run_dispatch.rs | 25 ++++----- .../src/state/native_class_manager.rs | 52 +++++++++++++++--- .../blockifier/src/test_utils/struct_impls.rs | 22 +++++++- 8 files changed, 122 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c76358fdc41..622806577d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4853,9 +4853,9 @@ dependencies = [ [[package]] name = "cairo-lang-test-plugin" -version = "2.19.0-rc.0" +version = "2.19.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bb3a904befddc475d7f4d70a6c58f42a0ce49497d71b04b8fa5aa766953c2b" +checksum = "eaceca861261038c5162af5da78152dea4cc59a1197c32c742bee98e8d41b574" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -4919,7 +4919,7 @@ dependencies = [ [[package]] name = "cairo-native" version = "0.9.0-rc.7" -source = "git+https://github.com/starkware-libs/cairo_native.git?rev=a21f3f57128d2a91067fa31bff92bd00bdc85ba5#a21f3f57128d2a91067fa31bff92bd00bdc85ba5" +source = "git+https://github.com/starkware-libs/cairo_native.git?rev=6184f7a635110bb84fae58ffa082fb9b2d286ada#6184f7a635110bb84fae58ffa082fb9b2d286ada" dependencies = [ "aquamarine", "ark-ec 0.5.0", @@ -4951,7 +4951,6 @@ dependencies = [ "num-bigint", "num-integer", "num-traits", - "rand 0.9.2", "serde", "serde_json", "sha2 0.10.9", @@ -4993,7 +4992,7 @@ dependencies = [ [[package]] name = "cairo-starknet-syscalls" version = "0.9.0-rc.7" -source = "git+https://github.com/starkware-libs/cairo_native.git?rev=a21f3f57128d2a91067fa31bff92bd00bdc85ba5#a21f3f57128d2a91067fa31bff92bd00bdc85ba5" +source = "git+https://github.com/starkware-libs/cairo_native.git?rev=6184f7a635110bb84fae58ffa082fb9b2d286ada#6184f7a635110bb84fae58ffa082fb9b2d286ada" dependencies = [ "serde", "starknet-types-core", @@ -12350,7 +12349,7 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "sierra-emu" version = "0.9.0-rc.7" -source = "git+https://github.com/starkware-libs/cairo_native.git?rev=a21f3f57128d2a91067fa31bff92bd00bdc85ba5#a21f3f57128d2a91067fa31bff92bd00bdc85ba5" +source = "git+https://github.com/starkware-libs/cairo_native.git?rev=6184f7a635110bb84fae58ffa082fb9b2d286ada#6184f7a635110bb84fae58ffa082fb9b2d286ada" dependencies = [ "cairo-lang-compiler", "cairo-lang-filesystem", @@ -12373,7 +12372,6 @@ dependencies = [ "num-integer", "num-traits", "p256", - "rand 0.9.2", "rayon", "sec1", "serde", diff --git a/Cargo.toml b/Cargo.toml index 1dce54c0913..d6b9ece4b82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -262,9 +262,9 @@ cairo-lang-starknet-classes = "2.19.0-rc.3" cairo-lang-utils = "2.19.0-rc.3" # TEMP: git dep pinned to the tip of the unreleased PR stack # (starkware-libs/cairo_native#1610 → #1611 → #1612 → #1613) containing -# `ContractExecutor`, `EmuContractInfo`, `AotWithProgram`, and `run_with_profile`. +# `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 = "7107710f05801b63f3f40d863dd8ed38680cc6f6", version = "0.9.0-rc.7" } +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/blockifier/Cargo.toml b/crates/blockifier/Cargo.toml index 44fbd2667ee..5e09c563612 100644 --- a/crates/blockifier/Cargo.toml +++ b/crates/blockifier/Cargo.toml @@ -23,8 +23,9 @@ node_api = [] only-native = ["cairo_native"] os_input = [] reexecution = ["transaction_serde"] -# Enables the sierra-emu execution path of `cairo_native::ContractExecutor`. Pulls in -# cairo-native's `sierra-emu` feature which compiles `SierraEmuSyscallBridge`. +# 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", @@ -37,7 +38,7 @@ testing = [ tracing = [] transaction_serde = [] # Enables libfunc-level profiling for native execution. Pulls in cairo-native's -# `with-libfunc-profiling` so `ContractExecutor::run_with_profile` is available, +# `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 = [ diff --git a/crates/blockifier/src/execution/native/contract_class.rs b/crates/blockifier/src/execution/native/contract_class.rs index a61e865f7cd..9d3798e95c3 100644 --- a/crates/blockifier/src/execution/native/contract_class.rs +++ b/crates/blockifier/src/execution/native/contract_class.rs @@ -2,11 +2,12 @@ use std::borrow::Cow; use std::ops::Deref; use std::sync::Arc; -#[cfg(feature = "with-libfunc-profiling")] +#[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::EmuContractInfo; -use cairo_native::executor::{AotContractExecutor, ContractExecutor}; +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; @@ -14,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 { @@ -32,31 +47,10 @@ 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 { - let contract = NativeCompiledClassV1Inner::new(executor.into(), casm); - - Self(Arc::new(contract)) - } - - /// Initialize a compiled class backed by the sierra-emu interpreter instead of the AOT - /// executor. Intended for the out-of-tree benchmarking / replay feature branch — those - /// tools are not expected to merge into this repo and will consume this constructor - /// from a fork. `pub` so a fork can reach it without patching this file. - #[cfg(feature = "sierra-emu")] - pub fn new_from_emu(info: EmuContractInfo, casm: CompiledClassV1) -> NativeCompiledClassV1 { - let contract = NativeCompiledClassV1Inner::new(info.into(), casm); - - Self(Arc::new(contract)) - } - - /// Like [`Self::new`], but also stores the Sierra `Program` (via the cairo-native - /// [`AotWithProgram`] pairing) so [`cairo_native::ContractExecutor::run_with_profile`] - /// can resolve libfunc samples. Only callable when the `with-libfunc-profiling` - /// feature is enabled. - #[cfg(feature = "with-libfunc-profiling")] - pub fn new_with_program(info: AotWithProgram, casm: CompiledClassV1) -> NativeCompiledClassV1 { - let contract = NativeCompiledClassV1Inner::new(info.into(), casm); + /// 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)) } @@ -97,12 +91,12 @@ impl HashableCompiledClass for NativeCompiledCla #[derive(Debug)] pub struct NativeCompiledClassV1Inner { - pub executor: ContractExecutor, + pub executor: NativeContractExecutor, casm: CompiledClassV1, } impl NativeCompiledClassV1Inner { - fn new(executor: ContractExecutor, casm: CompiledClassV1) -> Self { + fn new(executor: NativeContractExecutor, casm: CompiledClassV1) -> Self { NativeCompiledClassV1Inner { executor, casm } } } diff --git a/crates/blockifier/src/execution/native/profiling.rs b/crates/blockifier/src/execution/native/profiling.rs index 04cff2a213a..0767f9cafb1 100644 --- a/crates/blockifier/src/execution/native/profiling.rs +++ b/crates/blockifier/src/execution/native/profiling.rs @@ -1,5 +1,5 @@ //! Blockifier-side storage for libfunc profiles collected by -//! [`cairo_native::ContractExecutor::run_with_profile`]. +//! `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 @@ -9,9 +9,9 @@ //! externally between runs. use std::collections::HashMap; -use std::sync::{Arc, LazyLock, Mutex}; +use std::sync::{LazyLock, Mutex}; -use cairo_native::executor::Program; +use cairo_native::executor::ArcProgram; use cairo_native::metadata::profiler::Profile; use starknet_types_core::felt::Felt; @@ -21,7 +21,7 @@ pub struct EntrypointProfile { pub class_hash: Felt, pub selector: Felt, pub profile: Profile, - pub program: Arc, + pub program: ArcProgram, } pub struct TransactionProfile { @@ -35,18 +35,18 @@ type ProfilesByBlockTx = HashMap; pub static LIBFUNC_PROFILES_MAP: LazyLock> = LazyLock::new(|| Mutex::new(HashMap::new())); -/// Builds an `FnOnce(Profile, Arc)` that, when invoked, records the captured +/// 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 -/// [`cairo_native::ContractExecutor::run_with_profile`] callback alongside the profile. +/// `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 -/// `ContractExecutor::run_with_profile`, which itself holds a `&mut` to the handler. +/// `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, Arc) + 'static { +) -> 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(); diff --git a/crates/blockifier/src/execution/native/run_dispatch.rs b/crates/blockifier/src/execution/native/run_dispatch.rs index d3ddd4cfc81..f76353d9f56 100644 --- a/crates/blockifier/src/execution/native/run_dispatch.rs +++ b/crates/blockifier/src/execution/native/run_dispatch.rs @@ -1,33 +1,34 @@ -//! Dispatches a [`ContractExecutor`] entry-point call, transparently routing through -//! [`ContractExecutor::run_with_profile`] when libfunc profiling is enabled. +//! 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::executor::ContractExecutor; 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. /// -/// When `with-libfunc-profiling` is enabled the call is routed through -/// [`ContractExecutor::run_with_profile`]; for the `AotWithProgram` variant cairo-native -/// invokes our callback with the captured profile and the program it was built from, which -/// we record into [`crate::execution::native::profiling::LIBFUNC_PROFILES_MAP`] keyed by -/// the current transaction hash. For other variants cairo-native falls through to -/// [`ContractExecutor::run`] and the callback is never invoked. +/// 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: &ContractExecutor, + executor: &NativeContractExecutor, selector: Felt, calldata: &[Felt], call_initial_gas: u64, builtin_costs: BuiltinCosts, syscall_handler: &mut NativeSyscallHandler<'_>, ) -> Result { - #[cfg(feature = "with-libfunc-profiling")] + #[cfg(all(feature = "with-libfunc-profiling", not(feature = "sierra-emu")))] { let on_profile = crate::execution::native::profiling::record_profile_for(syscall_handler, selector); @@ -40,6 +41,6 @@ pub fn run_native_executor( on_profile, ) } - #[cfg(not(feature = "with-libfunc-profiling"))] + #[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 847ca6b8e31..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> { @@ -285,9 +294,14 @@ fn process_compilation_request( let sierra_for_compilation = into_contract_class_for_compilation(sierra.as_ref()); let start = Instant::now(); - #[cfg(feature = "with-libfunc-profiling")] + // 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(feature = "with-libfunc-profiling"))] + #[cfg(not(any(feature = "with-libfunc-profiling", feature = "sierra-emu")))] let compilation_result = compiler.compile(sierra_for_compilation); let duration = start.elapsed(); log::info!( @@ -297,9 +311,6 @@ fn process_compilation_request( ); match compilation_result { Ok(compiled) => { - #[cfg(feature = "with-libfunc-profiling")] - let native_compiled_class = NativeCompiledClassV1::new_with_program(compiled, casm); - #[cfg(not(feature = "with-libfunc-profiling"))] let native_compiled_class = NativeCompiledClassV1::new(compiled, casm); class_cache.set( class_hash, @@ -325,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, From bc128443ad741559a6a450cf5ccb01abad83f00e Mon Sep 17 00:00:00 2001 From: Avi Cohen Date: Thu, 9 Jul 2026 14:14:26 +0300 Subject: [PATCH 7/7] ci: retrigger checks The previous head SHA carried a permanently-completed check-run (failure) from before that job was removed from main's workflow. merge-gatekeeper-new no longer ignores that context, so it polls forever waiting on a check that will never update, until it times out. A new SHA has no such stale check-run. Co-Authored-By: Claude Fable 5