From 3f28e7c4142a90175613cf41b09d6cf56b67982f Mon Sep 17 00:00:00 2001 From: Noam Spiegelstein Date: Mon, 16 Dec 2024 17:51:07 +0200 Subject: [PATCH] feat(starknet_state_sync): implement state sync get compiled class --- .../starknet_gateway/src/sync_state_reader.rs | 16 ++++++- crates/starknet_state_sync/src/lib.rs | 44 ++++++++++++++++++- .../src/communication.rs | 42 +++++++++++++++++- .../starknet_state_sync_types/src/errors.rs | 17 +++++-- 4 files changed, 111 insertions(+), 8 deletions(-) diff --git a/crates/starknet_gateway/src/sync_state_reader.rs b/crates/starknet_gateway/src/sync_state_reader.rs index 3fefe2ad8c..2ffcd21774 100644 --- a/crates/starknet_gateway/src/sync_state_reader.rs +++ b/crates/starknet_gateway/src/sync_state_reader.rs @@ -3,6 +3,7 @@ use blockifier::state::errors::StateError; use blockifier::state::state_api::{StateReader as BlockifierStateReader, StateResult}; use futures::executor::block_on; use starknet_api::block::{BlockInfo, BlockNumber}; +use starknet_api::contract_class::ContractClass; use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce}; use starknet_api::state::StorageKey; use starknet_state_sync_types::communication::SharedStateSyncClient; @@ -55,8 +56,19 @@ impl BlockifierStateReader for SyncStateReader { Ok(res) } - fn get_compiled_class(&self, _class_hash: ClassHash) -> StateResult { - todo!() + fn get_compiled_class(&self, class_hash: ClassHash) -> StateResult { + let (contract_class, _sierra_version) = + block_on(self.state_sync_client.get_compiled_class(self.block_number, class_hash)) + .map_err(|e| StateError::StateReadError(e.to_string()))?; + + match contract_class { + ContractClass::V1(casm_contract_class) => { + Ok(RunnableCompiledClass::V1(casm_contract_class.try_into()?)) + } + ContractClass::V0(deprecated_contract_class) => { + Ok(RunnableCompiledClass::V0(deprecated_contract_class.try_into()?)) + } + } } fn get_class_hash_at(&self, _contract_address: ContractAddress) -> StateResult { diff --git a/crates/starknet_state_sync/src/lib.rs b/crates/starknet_state_sync/src/lib.rs index 40a664001d..01d6dd8560 100644 --- a/crates/starknet_state_sync/src/lib.rs +++ b/crates/starknet_state_sync/src/lib.rs @@ -3,11 +3,13 @@ pub mod runner; use async_trait::async_trait; use papyrus_storage::body::BodyStorageReader; +use papyrus_storage::compiled_class::CasmStorageReader; use papyrus_storage::db::TransactionKind; use papyrus_storage::state::StateStorageReader; use papyrus_storage::{StorageReader, StorageTxn}; use starknet_api::block::BlockNumber; -use starknet_api::core::{ContractAddress, Nonce, BLOCK_HASH_TABLE_ADDRESS}; +use starknet_api::contract_class::{ContractClass, SierraVersion}; +use starknet_api::core::{ClassHash, ContractAddress, Nonce, BLOCK_HASH_TABLE_ADDRESS}; use starknet_api::state::{StateNumber, StorageKey}; use starknet_sequencer_infra::component_definitions::{ComponentRequestHandler, ComponentStarter}; use starknet_sequencer_infra::component_server::{LocalComponentServer, RemoteComponentServer}; @@ -54,6 +56,11 @@ impl ComponentRequestHandler for StateSync StateSyncRequest::GetNonceAt(block_number, contract_address) => { StateSyncResponse::GetNonceAt(self.get_nonce_at(block_number, contract_address)) } + StateSyncRequest::GetCompiledClass(block_number, class_hash) => { + StateSyncResponse::GetCompiledClass( + self.get_compiled_class(block_number, class_hash), + ) + } } } } @@ -117,6 +124,41 @@ impl StateSync { Ok(res) } + + fn get_compiled_class( + &self, + block_number: BlockNumber, + class_hash: ClassHash, + ) -> StateSyncResult<(ContractClass, SierraVersion)> { + let txn = self.storage_reader.begin_ro_txn()?; + verify_synced_up_to(&txn, block_number)?; + + let state_reader = txn.get_state_reader()?; + // Check if this class exists in the Cairo1 classes table. + if let Some(class_definition_block_number) = + state_reader.get_class_definition_block_number(&class_hash)? + { + if class_definition_block_number > block_number { + return Err(StateSyncError::ClassHashNotFound(class_hash)); + } + let (option_casm, option_sierra) = txn.get_casm_and_sierra(&class_hash)?; + + // Check if both options are `Some`. + let (casm, sierra) = option_casm + .zip(option_sierra) + .ok_or(StateSyncError::ClassHashNotFound(class_hash))?; + let sierra_version = SierraVersion::extract_from_program(&sierra.sierra_program)?; + return Ok((ContractClass::V1(casm), sierra_version)); + } + + // Check if this class exists in the Cairo0 classes table. + let state_number = StateNumber::unchecked_right_after_block(block_number); + let deprecated_compiled_contract_class = state_reader + .get_deprecated_class_definition_at(state_number, &class_hash)? + .ok_or(StateSyncError::ClassHashNotFound(class_hash))?; + + Ok((ContractClass::V0(deprecated_compiled_contract_class), SierraVersion::DEPRECATED)) + } } fn verify_synced_up_to( diff --git a/crates/starknet_state_sync_types/src/communication.rs b/crates/starknet_state_sync_types/src/communication.rs index 794b63a6a4..d894c29c3d 100644 --- a/crates/starknet_state_sync_types/src/communication.rs +++ b/crates/starknet_state_sync_types/src/communication.rs @@ -4,7 +4,8 @@ use async_trait::async_trait; use papyrus_proc_macros::handle_response_variants; use serde::{Deserialize, Serialize}; use starknet_api::block::BlockNumber; -use starknet_api::core::{ContractAddress, Nonce}; +use starknet_api::contract_class::{ContractClass, SierraVersion}; +use starknet_api::core::{ClassHash, ContractAddress, Nonce}; use starknet_api::state::StorageKey; use starknet_sequencer_infra::component_client::{ ClientError, @@ -50,7 +51,12 @@ pub trait StateSyncClient: Send + Sync { contract_address: ContractAddress, ) -> StateSyncClientResult; - // TODO: Add get_compiled_class for StateSyncReader + async fn get_compiled_class( + &self, + block_number: BlockNumber, + class_hash: ClassHash, + ) -> StateSyncClientResult<(ContractClass, SierraVersion)>; + // TODO: Add get_class_hash_at for StateSyncReader // TODO: Add get_compiled_class_hash for StateSyncReader // TODO: Add get_block_info for StateSyncReader @@ -79,6 +85,7 @@ pub enum StateSyncRequest { AddNewBlock(BlockNumber, SyncBlock), GetStorageAt(BlockNumber, ContractAddress, StorageKey), GetNonceAt(BlockNumber, ContractAddress), + GetCompiledClass(BlockNumber, ClassHash), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -87,6 +94,7 @@ pub enum StateSyncResponse { AddNewBlock(StateSyncResult<()>), GetStorageAt(StateSyncResult), GetNonceAt(StateSyncResult), + GetCompiledClass(StateSyncResult<(ContractClass, SierraVersion)>), } #[async_trait] @@ -145,6 +153,21 @@ impl StateSyncClient for LocalStateSyncClient { StateSyncError ) } + + async fn get_compiled_class( + &self, + block_number: BlockNumber, + class_hash: ClassHash, + ) -> StateSyncClientResult<(ContractClass, SierraVersion)> { + let request = StateSyncRequest::GetCompiledClass(block_number, class_hash); + let response = self.send(request).await; + handle_response_variants!( + StateSyncResponse, + GetCompiledClass, + StateSyncClientError, + StateSyncError + ) + } } #[async_trait] @@ -203,4 +226,19 @@ impl StateSyncClient for RemoteStateSyncClient { StateSyncError ) } + + async fn get_compiled_class( + &self, + block_number: BlockNumber, + class_hash: ClassHash, + ) -> StateSyncClientResult<(ContractClass, SierraVersion)> { + let request = StateSyncRequest::GetCompiledClass(block_number, class_hash); + let response = self.send(request).await; + handle_response_variants!( + StateSyncResponse, + GetCompiledClass, + StateSyncClientError, + StateSyncError + ) + } } diff --git a/crates/starknet_state_sync_types/src/errors.rs b/crates/starknet_state_sync_types/src/errors.rs index 7fd0c09f0e..67978b8f6c 100644 --- a/crates/starknet_state_sync_types/src/errors.rs +++ b/crates/starknet_state_sync_types/src/errors.rs @@ -1,7 +1,8 @@ use papyrus_storage::StorageError; use serde::{Deserialize, Serialize}; use starknet_api::block::BlockNumber; -use starknet_api::core::ContractAddress; +use starknet_api::core::{ClassHash, ContractAddress}; +use starknet_api::StarknetApiError; use thiserror::Error; #[derive(Debug, Error, Serialize, Deserialize, Clone)] @@ -12,10 +13,14 @@ pub enum StateSyncError { BlockNotFound(BlockNumber), #[error("Contract address {0} was not found")] ContractNotFound(ContractAddress), - // StorageError does not derive Serialize, Deserialize and Clone Traits. - // We put the string of the error instead. + #[error("Class hash {0} was not found")] + ClassHashNotFound(ClassHash), + // StorageError and StarknetApiError do not derive Serialize, Deserialize and Clone Traits. + // We put the string of the errors instead. #[error("Unexpected storage error: {0}")] StorageError(String), + #[error("Unexpected starknet api error: {0}")] + StarknetApiError(String), } impl From for StateSyncError { @@ -23,3 +28,9 @@ impl From for StateSyncError { StateSyncError::StorageError(error.to_string()) } } + +impl From for StateSyncError { + fn from(error: StarknetApiError) -> Self { + StateSyncError::StarknetApiError(error.to_string()) + } +}