diff --git a/Cargo.lock b/Cargo.lock index 8cb0a2d9c5d..1dafb4536fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8034,6 +8034,7 @@ dependencies = [ "assert_matches", "blockifier", "cairo-lang-starknet-classes", + "futures", "indexmap 2.7.0", "papyrus_storage", "rstest", diff --git a/crates/papyrus_state_reader/Cargo.toml b/crates/papyrus_state_reader/Cargo.toml index aaf0067263b..7f71f8a096f 100644 --- a/crates/papyrus_state_reader/Cargo.toml +++ b/crates/papyrus_state_reader/Cargo.toml @@ -14,6 +14,7 @@ workspace = true [dependencies] blockifier.workspace = true cairo-lang-starknet-classes.workspace = true +futures.workspace = true papyrus_storage.workspace = true starknet-types-core.workspace = true starknet_api.workspace = true diff --git a/crates/papyrus_state_reader/src/papyrus_state.rs b/crates/papyrus_state_reader/src/papyrus_state.rs index 49c65d95b06..b762011ed73 100644 --- a/crates/papyrus_state_reader/src/papyrus_state.rs +++ b/crates/papyrus_state_reader/src/papyrus_state.rs @@ -12,12 +12,13 @@ use blockifier::state::global_cache::CachedCairoNative; use blockifier::state::global_cache::CachedCasm; use blockifier::state::state_api::{StateReader, StateResult}; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; +use futures::executor::block_on; use papyrus_storage::compiled_class::CasmStorageReader; use papyrus_storage::db::RO; use papyrus_storage::state::StateStorageReader; use papyrus_storage::StorageReader; use starknet_api::block::BlockNumber; -use starknet_api::contract_class::SierraVersion; +use starknet_api::contract_class::{ContractClass, SierraVersion}; use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce}; use starknet_api::deprecated_contract_class::ContractClass as DeprecatedClass; use starknet_api::state::{SierraContractClass, StateNumber, StorageKey}; @@ -34,7 +35,8 @@ pub struct PapyrusReader { storage_reader: StorageReader, latest_block: BlockNumber, contract_class_manager: ContractClassManager, - _class_reader: SharedClassManagerClient, + // Reader is `None` for reader invoked through `native_blockifier`. + class_reader: Option, } impl PapyrusReader { @@ -43,9 +45,13 @@ impl PapyrusReader { latest_block: BlockNumber, contract_class_manager: ContractClassManager, ) -> Self { - // TODO(Elin): integrate class manager client. - let _class_reader = Arc::new(EmptyClassManagerClient); - Self { storage_reader, latest_block, contract_class_manager, _class_reader } + Self { + storage_reader, + latest_block, + contract_class_manager, + // TODO(Elin): integrate class manager client. + class_reader: None, + } } fn reader(&self) -> StateResult> { @@ -144,28 +150,51 @@ impl PapyrusReader { &self, class_hash: ClassHash, ) -> StateResult<(CasmContractClass, SierraContractClass)> { - let (option_casm, option_sierra) = self - .reader()? - .get_casm_and_sierra(&class_hash) + let Some(class_reader) = &self.class_reader else { + let (option_casm, option_sierra) = self + .reader()? + .get_casm_and_sierra(&class_hash) + .map_err(|err| StateError::StateReadError(err.to_string()))?; + let (casm, sierra) = couple_casm_and_sierra(class_hash, option_casm, option_sierra)? + .expect( + "Should be able to fetch a Casm and Sierra class if its definition exists, + database is inconsistent.", + ); + + return Ok((casm, sierra)); + }; + + let casm = block_on(class_reader.get_executable(class_hash)) + .map_err(|err| StateError::StateReadError(err.to_string()))?; + let ContractClass::V1((casm, _sierra_version)) = casm else { + panic!("Class hash {class_hash} originated from a Cairo 1 contract."); + }; + // TODO(Elin): consider not reading Sierra if compilation is disabled. + let sierra = block_on(class_reader.get_sierra(class_hash)) .map_err(|err| StateError::StateReadError(err.to_string()))?; - let (casm, sierra) = couple_casm_and_sierra(class_hash, option_casm, option_sierra)? - .expect( - "Should be able to fetch a Casm and Sierra class if its definition exists, - database is inconsistent.", - ); Ok((casm, sierra)) } fn read_deprecated_casm(&self, class_hash: ClassHash) -> StateResult> { - let state_number = StateNumber(self.latest_block); - let option_casm = self - .reader()? - .get_state_reader() - .and_then(|sr| sr.get_deprecated_class_definition_at(state_number, &class_hash)) + let Some(class_reader) = &self.class_reader else { + let state_number = StateNumber(self.latest_block); + let option_casm = self + .reader()? + .get_state_reader() + .and_then(|sr| sr.get_deprecated_class_definition_at(state_number, &class_hash)) + .map_err(|err| StateError::StateReadError(err.to_string()))?; + + return Ok(option_casm); + }; + + let casm = block_on(class_reader.get_executable(class_hash)) .map_err(|err| StateError::StateReadError(err.to_string()))?; + let ContractClass::V0(casm) = casm else { + panic!("Class hash {class_hash} originated from a Cairo 0 contract."); + }; - Ok(option_casm) + Ok(Some(casm)) } } diff --git a/crates/starknet_class_manager_types/src/lib.rs b/crates/starknet_class_manager_types/src/lib.rs index c32c2e03d6c..5cd19ca3298 100644 --- a/crates/starknet_class_manager_types/src/lib.rs +++ b/crates/starknet_class_manager_types/src/lib.rs @@ -55,6 +55,7 @@ pub struct ClassHashes { pub trait ClassManagerClient: Send + Sync { async fn add_class(&self, class: Class) -> ClassManagerClientResult; + // TODO(Elin): separate V0 and V1 APIs; remove Sierra version. async fn get_executable(&self, class_id: ClassId) -> ClassManagerClientResult; async fn get_sierra(&self, class_id: ClassId) -> ClassManagerClientResult; @@ -75,6 +76,7 @@ pub enum CachedClassStorageError { Storage(#[from] E), } +// TODO(Elin): express class not found, either as `Option`, or as a dedicated error variant. #[derive(Clone, Debug, Error, Eq, PartialEq, Serialize, Deserialize)] pub enum ClassManagerError { #[error("Internal client error: {0}")]