diff --git a/crates/blockifier/src/state/contract_class_manager.rs b/crates/blockifier/src/state/contract_class_manager.rs index fbb697f3b9..4091450545 100644 --- a/crates/blockifier/src/state/contract_class_manager.rs +++ b/crates/blockifier/src/state/contract_class_manager.rs @@ -153,6 +153,16 @@ impl ContractClassManager { self.contract_caches.set_casm(class_hash, compiled_class); } + #[cfg(feature = "cairo_native")] + pub fn run_cairo_native(&self) -> bool { + self.config.run_cairo_native + } + + #[cfg(feature = "cairo_native")] + pub fn wait_on_native_compilation(&self) -> bool { + self.config.wait_on_native_compilation + } + /// Clear the contract caches. pub fn clear(&mut self) { self.contract_caches.clear(); diff --git a/crates/blockifier/src/state/global_cache.rs b/crates/blockifier/src/state/global_cache.rs index 6bd63b1582..dd24ca008d 100644 --- a/crates/blockifier/src/state/global_cache.rs +++ b/crates/blockifier/src/state/global_cache.rs @@ -21,6 +21,13 @@ pub enum CachedCasm { WithoutSierra(RunnableCompiledClass), WithSierra(RunnableCompiledClass, Arc), } +impl CachedCasm { + pub fn casm(&self) -> RunnableCompiledClass { + match self { + CachedCasm::WithoutSierra(casm) | CachedCasm::WithSierra(casm, _) => casm.clone(), + } + } +} #[cfg(feature = "cairo_native")] #[derive(Debug, Clone)] diff --git a/crates/native_blockifier/Cargo.toml b/crates/native_blockifier/Cargo.toml index e8227d110b..e423eea5e8 100644 --- a/crates/native_blockifier/Cargo.toml +++ b/crates/native_blockifier/Cargo.toml @@ -7,7 +7,7 @@ license-file.workspace = true description = "A Bridge between the Rust blockifier crate and Python." [features] -cairo_native = ["blockifier/cairo_native"] +cairo_native = ["blockifier/cairo_native", "papyrus_state_reader/cairo_native"] # Required for `cargo test` to work with Pyo3. # On Python, make sure to compile this with the extension-module feature enabled. # https://pyo3.rs/v0.19.1/faq#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror diff --git a/crates/papyrus_state_reader/Cargo.toml b/crates/papyrus_state_reader/Cargo.toml index 80d5d7cabb..5b279784cd 100644 --- a/crates/papyrus_state_reader/Cargo.toml +++ b/crates/papyrus_state_reader/Cargo.toml @@ -5,6 +5,9 @@ edition.workspace = true repository.workspace = true license.workspace = true +[features] +cairo_native = ["blockifier/cairo_native"] + [lints] workspace = true diff --git a/crates/papyrus_state_reader/src/papyrus_state.rs b/crates/papyrus_state_reader/src/papyrus_state.rs index d7b2450e95..5f57e1db1d 100644 --- a/crates/papyrus_state_reader/src/papyrus_state.rs +++ b/crates/papyrus_state_reader/src/papyrus_state.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use blockifier::execution::contract_class::{ CompiledClassV0, CompiledClassV1, @@ -5,6 +7,8 @@ use blockifier::execution::contract_class::{ }; use blockifier::state::contract_class_manager::ContractClassManager; use blockifier::state::errors::{couple_casm_and_sierra, StateError}; +#[cfg(feature = "cairo_native")] +use blockifier::state::global_cache::CachedCairoNative; use blockifier::state::global_cache::CachedCasm; use blockifier::state::state_api::{StateReader, StateResult}; use papyrus_storage::compiled_class::CasmStorageReader; @@ -22,6 +26,7 @@ use starknet_types_core::felt::Felt; mod test; type RawPapyrusReader<'env> = papyrus_storage::StorageTxn<'env, RO>; + pub struct PapyrusReader { storage_reader: StorageReader, latest_block: BlockNumber, @@ -45,10 +50,7 @@ impl PapyrusReader { /// Returns a V1 contract if found, or a V0 contract if a V1 contract is not /// found, or an `Error` otherwise. - fn get_compiled_class_inner( - &self, - class_hash: ClassHash, - ) -> StateResult { + fn get_compiled_class_inner(&self, class_hash: ClassHash) -> StateResult { let state_number = StateNumber(self.latest_block); let class_declaration_block_number = self .reader()? @@ -69,11 +71,11 @@ impl PapyrusReader { database is inconsistent.", ); let sierra_version = SierraVersion::extract_from_program(&sierra.sierra_program)?; - let runnable_compiled = RunnableCompiledClass::V1(CompiledClassV1::try_from(( + let runnable_casm = RunnableCompiledClass::V1(CompiledClassV1::try_from(( casm_compiled_class, sierra_version, ))?); - return Ok(runnable_compiled); + return Ok(CachedCasm::WithSierra(runnable_casm, Arc::new(sierra))); } let v0_compiled_class = self @@ -83,12 +85,52 @@ impl PapyrusReader { .map_err(|err| StateError::StateReadError(err.to_string()))?; match v0_compiled_class { - Some(starknet_api_contract_class) => Ok(RunnableCompiledClass::V0( - CompiledClassV0::try_from(starknet_api_contract_class)?, - )), + Some(starknet_api_contract_class) => { + let runnable_casm = RunnableCompiledClass::V0(CompiledClassV0::try_from( + starknet_api_contract_class, + )?); + Ok(CachedCasm::WithoutSierra(runnable_casm)) + } None => Err(StateError::UndeclaredClassHash(class_hash)), } } + + fn get_compiled_class_non_native_flow( + &self, + class_hash: ClassHash, + ) -> StateResult { + let versioned_contract_class = self.contract_class_manager.get_casm(&class_hash); + match versioned_contract_class { + Some(contract_class) => Ok(contract_class.casm()), + None => { + let contract_class_from_db = self.get_compiled_class_inner(class_hash)?.casm(); + self.contract_class_manager.set_casm( + class_hash, + CachedCasm::WithoutSierra(contract_class_from_db.clone()), + ); + Ok(contract_class_from_db) + } + } + } + + #[cfg(feature = "cairo_native")] + fn extract_contract_in_sync_compilation( + &self, + class_hash: ClassHash, + casm: RunnableCompiledClass, + ) -> RunnableCompiledClass { + assert!(self.contract_class_manager.wait_on_native_compilation()); + let cached_native = self + .contract_class_manager + .get_native(&class_hash) + .expect("Should have native in cache in sync compilation flow."); + match cached_native { + CachedCairoNative::Compiled(compiled_native) => { + RunnableCompiledClass::from(compiled_native) + } + CachedCairoNative::CompilationFailed => casm, + } + } } // Currently unused - will soon replace the same `impl` for `PapyrusStateReader`. @@ -133,20 +175,56 @@ impl StateReader for PapyrusReader { fn get_compiled_class(&self, class_hash: ClassHash) -> StateResult { // Assumption: the global cache is cleared upon reverted blocks. - let cached_casm = self.contract_class_manager.get_casm(&class_hash); - match cached_casm { - None => { - let compiled_class_from_db = self.get_compiled_class_inner(class_hash)?; - self.contract_class_manager.set_casm( - class_hash, - CachedCasm::WithoutSierra(compiled_class_from_db.clone()), - ); - Ok(compiled_class_from_db) + #[cfg(not(feature = "cairo_native"))] + return self.get_compiled_class_non_native_flow(class_hash); + + #[cfg(feature = "cairo_native")] + { + // If we turned off the cairo native compilation, we use the non cairo native flow. + if !self.contract_class_manager.run_cairo_native() { + return self.get_compiled_class_non_native_flow(class_hash); } - Some(CachedCasm::WithoutSierra(casm)) => Ok(casm), - Some(CachedCasm::WithSierra(_, _)) => { - todo!("Add this flow when Sierra to Native compilation is added to PapyrusReader.") + + // We have the Native in cache. + if let Some(cached_native) = self.contract_class_manager.get_native(&class_hash) { + match cached_native { + CachedCairoNative::Compiled(compiled_native) => { + return Ok(RunnableCompiledClass::from(compiled_native)); + } + // for some reason the compilation failed, we use the non cairo native flow. + CachedCairoNative::CompilationFailed => { + return self.get_compiled_class_non_native_flow(class_hash); + } + } + }; + + let cached_casm = match self.contract_class_manager.get_casm(&class_hash) { + // Casm is in cache. + Some(cached_casm) => cached_casm, + None => { + let cached_casm = self.get_compiled_class_inner(class_hash)?; + self.contract_class_manager.set_casm(class_hash, cached_casm.clone()); + cached_casm + } + }; + + match cached_casm { + CachedCasm::WithSierra(runnable_casm, sierra) => { + if let RunnableCompiledClass::V1(casm_v1) = runnable_casm.clone() { + self.contract_class_manager.send_compilation_request(( + class_hash, + sierra.clone(), + casm_v1.clone(), + )); + if self.contract_class_manager.wait_on_native_compilation() { + return Ok(self + .extract_contract_in_sync_compilation(class_hash, runnable_casm)); + } + } + Ok(runnable_casm) + } + CachedCasm::WithoutSierra(runnable_casm) => Ok(runnable_casm), } } }