From dc87fbdcb5f4b1174de45cc4fd9800f2e6b0f7dc Mon Sep 17 00:00:00 2001 From: Avi Cohen Date: Sun, 7 Jun 2026 15:17:15 +0300 Subject: [PATCH] blockifier_reexecution: compile Sierra to Casm in-process via library call Co-Authored-By: Claude Opus 4.8 (1M context) --- Cargo.lock | 4 +- crates/blockifier_reexecution/Cargo.toml | 6 +- crates/blockifier_reexecution/src/compile.rs | 64 +++++++++++-------- .../src/compile_test.rs | 14 ++++ 4 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 crates/blockifier_reexecution/src/compile_test.rs diff --git a/Cargo.lock b/Cargo.lock index bc550a944ef..00f04956984 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3849,8 +3849,7 @@ dependencies = [ name = "blockifier_reexecution" version = "0.19.0-rc.0" dependencies = [ - "apollo_compile_to_casm", - "apollo_compile_to_casm_types", + "apollo_compilation_utils", "apollo_config", "apollo_rpc_execution", "apollo_sierra_compilation_config", @@ -3861,6 +3860,7 @@ dependencies = [ "flate2", "google-cloud-storage", "indexmap 2.14.0", + "mempool_test_utils", "mockito", "pretty_assertions", "reqwest 0.12.24", diff --git a/crates/blockifier_reexecution/Cargo.toml b/crates/blockifier_reexecution/Cargo.toml index 897a27e46eb..a77fab95865 100644 --- a/crates/blockifier_reexecution/Cargo.toml +++ b/crates/blockifier_reexecution/Cargo.toml @@ -11,13 +11,13 @@ blockifier_regression_https_testing = [] cairo_native = ["blockifier/cairo_native"] [dependencies] -apollo_compile_to_casm.workspace = true -apollo_compile_to_casm_types.workspace = true +apollo_compilation_utils.workspace = true apollo_config.workspace = true apollo_rpc_execution.workspace = true apollo_sierra_compilation_config.workspace = true assert_matches.workspace = true blockifier = { workspace = true, features = ["reexecution"] } +cairo-lang-starknet-classes.workspace = true clap = { workspace = true, features = ["cargo", "derive"] } flate2.workspace = true google-cloud-storage.workspace = true @@ -37,7 +37,7 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] } validator.workspace = true [dev-dependencies] -cairo-lang-starknet-classes.workspace = true +mempool_test_utils.workspace = true mockito.workspace = true rstest.workspace = true starknet_api = { workspace = true, features = ["testing"] } diff --git a/crates/blockifier_reexecution/src/compile.rs b/crates/blockifier_reexecution/src/compile.rs index 3033c929132..0bb96ed2353 100644 --- a/crates/blockifier_reexecution/src/compile.rs +++ b/crates/blockifier_reexecution/src/compile.rs @@ -4,15 +4,13 @@ use std::collections::HashMap; use std::io::{self, Read}; -use std::sync::LazyLock; -use apollo_compile_to_casm::{create_sierra_compiler, SierraCompiler}; -use apollo_compile_to_casm_types::RawClass; -use apollo_sierra_compilation_config::config::{ - SierraCompilationConfig, - DEFAULT_MAX_BYTECODE_SIZE, -}; +use apollo_compilation_utils::class_utils::into_contract_class_for_compilation; +use apollo_sierra_compilation_config::config::DEFAULT_MAX_BYTECODE_SIZE; +use blockifier::state::errors::StateError; use blockifier::state::state_api::StateResult; +use cairo_lang_starknet_classes::allowed_libfuncs::ListSelector; +use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; use flate2::bufread; use starknet_api::contract_class::{ContractClass, EntryPointType, SierraVersion}; use starknet_api::core::EntryPointSelector; @@ -30,13 +28,9 @@ use starknet_core::types::{ LegacyEntryPointsByType, }; -static SIERRA_COMPILER: LazyLock = LazyLock::new(|| { - create_sierra_compiler(SierraCompilationConfig { - max_bytecode_size: 10 * DEFAULT_MAX_BYTECODE_SIZE, - audited_libfuncs_only: false, - ..Default::default() - }) -}); +#[cfg(test)] +#[path = "compile_test.rs"] +pub mod test; /// Maps `LegacyEntryPointsByType` to a `HashMap` where each `EntryPointType` /// is associated with a vector of `EntryPoint`. Converts selectors and offsets @@ -76,22 +70,42 @@ pub fn decode_reader(bytes: Vec) -> io::Result { Ok(s) } -/// Compile a SierraContractClass to a versioned ContractClass V1 (casm) using -/// apollo_compile_to_casm. +/// Compile a SierraContractClass to a versioned ContractClass V1 (casm) in-process, using the +/// Sierra→Casm compiler as a library. The classes compiled here are fetched from the chain, so +/// unlike the sequencer gateway (which compiles user-submitted classes in a resource-limited +/// subprocess), no process isolation is needed. pub fn sierra_to_versioned_contract_class_v1( sierra_contract: SierraContractClass, ) -> StateResult<(ContractClass, SierraVersion)> { let sierra_version = SierraVersion::extract_from_program(&sierra_contract.sierra_program) - .unwrap_or_else(|err| panic!("Failed to extract Sierra version: {err}")); - let raw_class = RawClass::try_from(sierra_contract) - .unwrap_or_else(|err| panic!("Failed to convert SierraContractClass into RawClass: {err}")); - let (raw_executable_class, _) = SIERRA_COMPILER - .compile(raw_class) - .unwrap_or_else(|err| panic!("Failed to compile Sierra to Casm: {err}")); - let contract_class: ContractClass = serde_json::from_value(raw_executable_class.into_value()) - .unwrap_or_else(|err| panic!("Failed to deserialize ContractClass: {err}")); + .map_err(|err| { + StateError::StateReadError(format!("Failed to extract Sierra version: {err}")) + })?; + let contract_class_for_compilation = into_contract_class_for_compilation(&sierra_contract); + let extracted_sierra_program = + contract_class_for_compilation.extract_sierra_program(false).map_err(|err| { + StateError::StateReadError(format!("Failed to extract the Sierra program: {err}")) + })?; + // Re-execution must accept any class the network accepted; do not restrict to the audited + // libfuncs list. + extracted_sierra_program + .validate_version_compatible(ListSelector::ListName("all".to_string())) + .map_err(|err| { + StateError::StateReadError(format!("Sierra program version validation failed: {err}")) + })?; + let casm_contract_class = CasmContractClass::from_contract_class( + contract_class_for_compilation, + extracted_sierra_program, + // Add pythonic hints, as the sequencer does when compiling declared classes. + true, + // Generous bound; declared classes already passed the network's bytecode-size limit. + 10 * DEFAULT_MAX_BYTECODE_SIZE, + ) + .map_err(|err| { + StateError::StateReadError(format!("Failed to compile Sierra to Casm: {err}")) + })?; - Ok((contract_class, sierra_version)) + Ok((ContractClass::V1((casm_contract_class, sierra_version.clone())), sierra_version)) } /// Compile a CompressedLegacyContractClass to a ContractClass V0 using cairo_lang_starknet_classes. diff --git a/crates/blockifier_reexecution/src/compile_test.rs b/crates/blockifier_reexecution/src/compile_test.rs new file mode 100644 index 00000000000..ca136738fee --- /dev/null +++ b/crates/blockifier_reexecution/src/compile_test.rs @@ -0,0 +1,14 @@ +use mempool_test_utils::starknet_api_test_utils::{contract_class, COMPILED_CLASS_HASH}; + +use crate::compile::sierra_to_versioned_contract_class_v1; + +/// Pins the in-process compilation output to the fixture's known compiled class hash — the same +/// hash the gateway's compilation flow produces for this class — guarding against output +/// divergence from the compiler used elsewhere in the system. +#[test] +fn compiled_fixture_class_matches_expected_compiled_class_hash() { + let (contract_class, _sierra_version) = + sierra_to_versioned_contract_class_v1(contract_class()).unwrap(); + + assert_eq!(contract_class.compiled_class_hash(), *COMPILED_CLASS_HASH); +}