diff --git a/Cargo.lock b/Cargo.lock index 441c09fb2e..3938806179 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8204,7 +8204,6 @@ dependencies = [ "katana-primitives", "katana-provider", "katana-rpc-types", - "katana-trie", "num-traits 0.2.19", "oneshot", "parking_lot 0.12.3", diff --git a/crates/katana/executor/Cargo.toml b/crates/katana/executor/Cargo.toml index 67cb8d7075..00943c68a5 100644 --- a/crates/katana/executor/Cargo.toml +++ b/crates/katana/executor/Cargo.toml @@ -10,7 +10,6 @@ version.workspace = true katana-cairo = { workspace = true, optional = true } katana-primitives.workspace = true katana-provider.workspace = true -katana-trie.workspace = true thiserror.workspace = true tracing.workspace = true diff --git a/crates/katana/executor/benches/execution.rs b/crates/katana/executor/benches/execution.rs index 1ddff343e2..322b88ae6d 100644 --- a/crates/katana/executor/benches/execution.rs +++ b/crates/katana/executor/benches/execution.rs @@ -3,7 +3,8 @@ use std::time::Duration; use blockifier::state::cached_state::CachedState; use criterion::measurement::WallTime; use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkGroup, Criterion}; -use katana_executor::{ExecutionFlags, StateProviderDb}; +use katana_executor::implementation::blockifier::state::StateProviderDb; +use katana_executor::ExecutionFlags; use katana_primitives::env::{BlockEnv, CfgEnv}; use katana_primitives::transaction::ExecutableTxWithHash; use katana_provider::test_utils; @@ -45,7 +46,7 @@ fn blockifier( || { // setup state let state = provider.latest().expect("failed to get latest state"); - let state = CachedState::new(StateProviderDb::new(state)); + let state = CachedState::new(StateProviderDb::new(state, Default::default())); (state, &block_context, execution_flags, tx.clone()) }, diff --git a/crates/katana/executor/src/abstraction/mod.rs b/crates/katana/executor/src/abstraction/mod.rs index 022ab83868..5ef42990f1 100644 --- a/crates/katana/executor/src/abstraction/mod.rs +++ b/crates/katana/executor/src/abstraction/mod.rs @@ -1,21 +1,15 @@ -mod error; mod executor; -pub use error::*; pub use executor::*; -use katana_primitives::class::{ClassHash, CompiledClass, CompiledClassHash, ContractClass}; -use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageValue}; use katana_primitives::receipt::Receipt; use katana_primitives::state::{StateUpdates, StateUpdatesWithClasses}; use katana_primitives::trace::TxExecInfo; use katana_primitives::transaction::TxWithHash; -use katana_primitives::Felt; -use katana_provider::traits::contract::ContractClassProvider; -use katana_provider::traits::state::{StateProofProvider, StateProvider, StateRootProvider}; -use katana_provider::ProviderResult; -use katana_trie::MultiProof; +use katana_primitives::{ContractAddress, Felt}; -pub type ExecutorResult = Result; +pub use crate::error::*; + +pub type ExecutorResult = Result; /// See . #[derive(Debug, Clone, Default)] @@ -169,88 +163,3 @@ pub struct ResultAndStates { pub result: ExecutionResult, pub states: StateUpdates, } - -/// A wrapper around a boxed [StateProvider] for implementing the executor's own state reader -/// traits. -#[derive(Debug)] -pub struct StateProviderDb<'a>(Box); - -impl<'a> StateProviderDb<'a> { - pub fn new(provider: Box) -> Self { - Self(provider) - } -} - -impl<'a> ContractClassProvider for StateProviderDb<'a> { - fn class(&self, hash: ClassHash) -> ProviderResult> { - self.0.class(hash) - } - - fn compiled_class(&self, hash: ClassHash) -> ProviderResult> { - self.0.compiled_class(hash) - } - - fn compiled_class_hash_of_class_hash( - &self, - hash: ClassHash, - ) -> ProviderResult> { - self.0.compiled_class_hash_of_class_hash(hash) - } -} - -impl<'a> StateProvider for StateProviderDb<'a> { - fn class_hash_of_contract( - &self, - address: ContractAddress, - ) -> ProviderResult> { - self.0.class_hash_of_contract(address) - } - - fn nonce(&self, address: ContractAddress) -> ProviderResult> { - self.0.nonce(address) - } - - fn storage( - &self, - address: ContractAddress, - storage_key: StorageKey, - ) -> ProviderResult> { - self.0.storage(address, storage_key) - } -} - -impl<'a> StateProofProvider for StateProviderDb<'a> { - fn class_multiproof(&self, classes: Vec) -> ProviderResult { - self.0.class_multiproof(classes) - } - - fn contract_multiproof(&self, addresses: Vec) -> ProviderResult { - self.0.contract_multiproof(addresses) - } - - fn storage_multiproof( - &self, - address: ContractAddress, - key: Vec, - ) -> ProviderResult { - self.0.storage_multiproof(address, key) - } -} - -impl<'a> StateRootProvider for StateProviderDb<'a> { - fn classes_root(&self) -> ProviderResult { - self.0.classes_root() - } - - fn contracts_root(&self) -> ProviderResult { - self.0.contracts_root() - } - - fn storage_root(&self, contract: ContractAddress) -> ProviderResult> { - self.0.storage_root(contract) - } - - fn state_root(&self) -> ProviderResult { - self.0.state_root() - } -} diff --git a/crates/katana/executor/src/abstraction/error.rs b/crates/katana/executor/src/error.rs similarity index 100% rename from crates/katana/executor/src/abstraction/error.rs rename to crates/katana/executor/src/error.rs diff --git a/crates/katana/executor/src/implementation/blockifier/mod.rs b/crates/katana/executor/src/implementation/blockifier/mod.rs index 453cb66c1c..051f04ebc6 100644 --- a/crates/katana/executor/src/implementation/blockifier/mod.rs +++ b/crates/katana/executor/src/implementation/blockifier/mod.rs @@ -3,31 +3,39 @@ pub use blockifier; use blockifier::bouncer::{Bouncer, BouncerConfig, BouncerWeights}; mod error; -mod state; +pub mod state; pub mod utils; +use std::collections::HashMap; use std::num::NonZeroU128; +use std::sync::{Arc, LazyLock}; use blockifier::blockifier::block::{BlockInfo, GasPrices}; use blockifier::context::BlockContext; +use blockifier::execution::contract_class::ContractClass as BlockifierContractClass; use blockifier::state::cached_state::{self, MutRefState}; use blockifier::state::state_api::StateReader; use katana_cairo::starknet_api::block::{BlockNumber, BlockTimestamp}; use katana_primitives::block::{ExecutableBlock, GasPrices as KatanaGasPrices, PartialHeader}; +use katana_primitives::class::ClassHash; use katana_primitives::env::{BlockEnv, CfgEnv}; use katana_primitives::fee::TxFeeInfo; use katana_primitives::transaction::{ExecutableTx, ExecutableTxWithHash, TxWithHash}; use katana_primitives::Felt; use katana_provider::traits::state::StateProvider; +use parking_lot::Mutex; use tracing::info; use self::state::CachedState; use crate::{ BlockExecutor, BlockLimits, EntryPointCall, ExecutionError, ExecutionFlags, ExecutionOutput, ExecutionResult, ExecutionStats, ExecutorError, ExecutorExt, ExecutorFactory, ExecutorResult, - ResultAndStates, StateProviderDb, + ResultAndStates, }; +pub static COMPILED_CLASS_CACHE: LazyLock>>> = + LazyLock::new(|| Arc::new(Mutex::new(HashMap::default()))); + pub(crate) const LOG_TARGET: &str = "katana::executor::blockifier"; #[derive(Debug)] @@ -79,7 +87,7 @@ impl ExecutorFactory for BlockifierFactory { #[derive(Debug)] pub struct StarknetVMProcessor<'a> { block_context: BlockContext, - state: CachedState>, + state: CachedState<'a>, transactions: Vec<(TxWithHash, ExecutionResult)>, simulation_flags: ExecutionFlags, stats: ExecutionStats, @@ -88,7 +96,7 @@ pub struct StarknetVMProcessor<'a> { impl<'a> StarknetVMProcessor<'a> { pub fn new( - state: Box, + state: impl StateProvider + 'a, block_env: BlockEnv, cfg_env: CfgEnv, simulation_flags: ExecutionFlags, @@ -96,7 +104,7 @@ impl<'a> StarknetVMProcessor<'a> { ) -> Self { let transactions = Vec::new(); let block_context = utils::block_context_from_envs(&block_env, &cfg_env); - let state = state::CachedState::new(StateProviderDb::new(state)); + let state = state::CachedState::new(state, COMPILED_CLASS_CACHE.clone()); let mut block_max_capacity = BouncerWeights::max(); block_max_capacity.n_steps = limits.cairo_steps as usize; @@ -159,7 +167,7 @@ impl<'a> StarknetVMProcessor<'a> { F: FnMut(&mut dyn StateReader, (TxWithHash, ExecutionResult)) -> T, { let block_context = &self.block_context; - let state = &mut self.state.0.lock().inner; + let state = &mut self.state.inner.lock().cached_state; let mut state = cached_state::CachedState::new(MutRefState::new(state)); let mut results = Vec::with_capacity(transactions.len()); @@ -188,7 +196,7 @@ impl<'a> BlockExecutor<'a> for StarknetVMProcessor<'a> { ) -> ExecutorResult<(usize, Option)> { let block_context = &self.block_context; let flags = &self.simulation_flags; - let mut state = self.state.0.lock(); + let mut state = self.state.inner.lock(); let mut total_executed = 0; for exec_tx in transactions { @@ -203,7 +211,7 @@ impl<'a> BlockExecutor<'a> for StarknetVMProcessor<'a> { let tx = TxWithHash::from(&exec_tx); let hash = tx.hash; let result = utils::transact( - &mut state.inner, + &mut state.cached_state, block_context, flags, exec_tx, @@ -318,8 +326,8 @@ impl ExecutorExt for StarknetVMProcessor<'_> { fn call(&self, call: EntryPointCall) -> Result, ExecutionError> { let block_context = &self.block_context; - let mut state = self.state.0.lock(); - let state = MutRefState::new(&mut state.inner); + let mut state = self.state.inner.lock(); + let state = MutRefState::new(&mut state.cached_state); let retdata = utils::call(call, state, block_context, 1_000_000_000)?; Ok(retdata) } diff --git a/crates/katana/executor/src/implementation/blockifier/state.rs b/crates/katana/executor/src/implementation/blockifier/state.rs index be1fc0c2a1..970b4e85d4 100644 --- a/crates/katana/executor/src/implementation/blockifier/state.rs +++ b/crates/katana/executor/src/implementation/blockifier/state.rs @@ -1,147 +1,60 @@ use std::collections::HashMap; +use std::ops::Deref; use std::sync::Arc; +use blockifier::execution::contract_class::ContractClass as BlockifierContractClass; use blockifier::state::cached_state; use blockifier::state::errors::StateError; use blockifier::state::state_api::{StateReader, StateResult}; use katana_cairo::starknet_api::core::{ClassHash, CompiledClassHash, Nonce}; use katana_cairo::starknet_api::state::StorageKey; -use katana_primitives::class::{self, CompiledClass, ContractClass}; +use katana_primitives::class::{self, ContractClass}; use katana_primitives::Felt; use katana_provider::error::ProviderError; -use katana_provider::traits::contract::ContractClassProvider; +use katana_provider::traits::contract::{ContractClassProvider, ContractClassProviderExt}; use katana_provider::traits::state::{StateProofProvider, StateProvider, StateRootProvider}; use katana_provider::ProviderResult; use parking_lot::Mutex; +use tracing::trace; use super::utils::{self}; -use crate::StateProviderDb; -/// A helper trait to enforce that a type must implement both [StateProvider] and [StateReader]. -pub(super) trait StateDb: StateProvider + StateReader {} - -impl StateDb for T where T: StateProvider + StateReader {} - -impl<'a> StateReader for StateProviderDb<'a> { - fn get_class_hash_at( - &self, - contract_address: katana_cairo::starknet_api::core::ContractAddress, - ) -> StateResult { - self.class_hash_of_contract(utils::to_address(contract_address)) - .map(|v| ClassHash(v.unwrap_or_default())) - .map_err(|e| StateError::StateReadError(e.to_string())) - } - - fn get_compiled_class_hash( - &self, - class_hash: katana_cairo::starknet_api::core::ClassHash, - ) -> StateResult { - if let Some(hash) = self - .compiled_class_hash_of_class_hash(class_hash.0) - .map_err(|e| StateError::StateReadError(e.to_string()))? - { - Ok(CompiledClassHash(hash)) - } else { - Err(StateError::UndeclaredClassHash(class_hash)) - } - } - - fn get_compiled_contract_class( - &self, - class_hash: ClassHash, - ) -> StateResult { - // TODO: handle class compilation on the executor level so that we can manage the cache - // ourselves. - if let Some(class) = self - .compiled_class(class_hash.0) - .map_err(|e| StateError::StateReadError(e.to_string()))? - { - let class = - utils::to_class(class).map_err(|e| StateError::StateReadError(e.to_string()))?; - - Ok(class.contract_class()) - } else { - Err(StateError::UndeclaredClassHash(class_hash)) - } - } - - fn get_nonce_at( - &self, - contract_address: katana_cairo::starknet_api::core::ContractAddress, - ) -> StateResult { - self.nonce(utils::to_address(contract_address)) - .map(|n| Nonce(n.unwrap_or_default())) - .map_err(|e| StateError::StateReadError(e.to_string())) - } - - fn get_storage_at( - &self, - contract_address: katana_cairo::starknet_api::core::ContractAddress, - key: katana_cairo::starknet_api::state::StorageKey, - ) -> StateResult { - self.storage(utils::to_address(contract_address), *key.0.key()) - .map(|v| v.unwrap_or_default()) - .map_err(|e| StateError::StateReadError(e.to_string())) - } +#[derive(Debug, Clone)] +pub struct CachedState<'a> { + pub(crate) inner: Arc>>, } #[derive(Debug)] -pub struct CachedState(pub(super) Arc>>); - -impl Clone for CachedState { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } -} - -// type DeclaredClass = (CompiledClass, ContractClass); - -#[derive(Debug)] -pub(super) struct CachedStateInner { - pub(super) inner: cached_state::CachedState, +pub(crate) struct CachedStateInner<'a> { + pub(super) cached_state: cached_state::CachedState>, pub(super) declared_classes: HashMap, - pub(super) compiled_classes: HashMap, } -impl CachedState { - pub(super) fn new(state: S) -> Self { - let declared_classes = HashMap::new(); - let compiled_classes = HashMap::new(); - +impl<'a> CachedState<'a> { + pub(super) fn new( + state: impl StateProvider + 'a, + compiled_class_cache: Arc>>, + ) -> Self { + let state = StateProviderDb::new(Box::new(state), compiled_class_cache); let cached_state = cached_state::CachedState::new(state); - let inner = CachedStateInner { inner: cached_state, declared_classes, compiled_classes }; - Self(Arc::new(Mutex::new(inner))) + let declared_classes = HashMap::new(); + let inner = CachedStateInner { cached_state, declared_classes }; + + Self { inner: Arc::new(Mutex::new(inner)) } } } -impl ContractClassProvider for CachedState { +impl<'a> ContractClassProvider for CachedState<'a> { fn class( &self, hash: katana_primitives::class::ClassHash, ) -> ProviderResult> { - let state = self.0.lock(); + let state = self.inner.lock(); if let Some(class) = state.declared_classes.get(&hash) { Ok(Some(class.clone())) } else { - state.inner.state.class(hash) - } - } - - fn compiled_class( - &self, - hash: katana_primitives::class::ClassHash, - ) -> ProviderResult> { - let mut state = self.0.lock(); - - if let class @ Some(..) = state.compiled_classes.get(&hash) { - Ok(class.cloned()) - } else if let Some(class) = state.declared_classes.get(&hash) { - let compiled = class.clone().compile()?; - state.compiled_classes.insert(hash, compiled.clone()); - Ok(Some(compiled)) - } else { - state.inner.state.compiled_class(hash) + state.cached_state.state.class(hash) } } @@ -149,7 +62,8 @@ impl ContractClassProvider for CachedState { &self, hash: katana_primitives::class::ClassHash, ) -> ProviderResult> { - let Ok(hash) = self.0.lock().inner.get_compiled_class_hash(ClassHash(hash)) else { + let Ok(hash) = self.inner.lock().cached_state.get_compiled_class_hash(ClassHash(hash)) + else { return Ok(None); }; @@ -157,12 +71,14 @@ impl ContractClassProvider for CachedState { } } -impl StateProvider for CachedState { +impl<'a> StateProvider for CachedState<'a> { fn class_hash_of_contract( &self, address: katana_primitives::contract::ContractAddress, ) -> ProviderResult> { - let Ok(hash) = self.0.lock().inner.get_class_hash_at(utils::to_blk_address(address)) else { + let Ok(hash) = + self.inner.lock().cached_state.get_class_hash_at(utils::to_blk_address(address)) + else { return Ok(None); }; @@ -178,7 +94,7 @@ impl StateProvider for CachedState { return Ok(None); } - match self.0.lock().inner.get_nonce_at(utils::to_blk_address(address)) { + match self.inner.lock().cached_state.get_nonce_at(utils::to_blk_address(address)) { Ok(nonce) => Ok(Some(nonce.0)), Err(e) => Err(ProviderError::Other(e.to_string())), } @@ -195,401 +111,112 @@ impl StateProvider for CachedState { } let address = utils::to_blk_address(address); - let key = - StorageKey(storage_key.try_into().expect("storage key is not a valid field element")); + let key = StorageKey(storage_key.try_into().unwrap()); - match self.0.lock().inner.get_storage_at(address, key) { + match self.inner.lock().cached_state.get_storage_at(address, key) { Ok(value) => Ok(Some(value)), Err(e) => Err(ProviderError::Other(e.to_string())), } } } -impl StateReader for CachedState { - fn get_class_hash_at( - &self, - contract_address: katana_cairo::starknet_api::core::ContractAddress, - ) -> StateResult { - self.0.lock().inner.get_class_hash_at(contract_address) - } +impl<'a> StateProofProvider for CachedState<'a> {} +impl<'a> StateRootProvider for CachedState<'a> {} - fn get_compiled_class_hash(&self, class_hash: ClassHash) -> StateResult { - self.0.lock().inner.get_compiled_class_hash(class_hash) - } +#[derive(Debug)] +pub struct StateProviderDb<'a> { + provider: Box, + compiled_class_cache: Arc>>, +} - fn get_compiled_contract_class( - &self, - class_hash: ClassHash, - ) -> StateResult { - self.0.lock().inner.get_compiled_contract_class(class_hash) - } +impl<'a> Deref for StateProviderDb<'a> { + type Target = Box; - fn get_nonce_at( - &self, - contract_address: katana_cairo::starknet_api::core::ContractAddress, - ) -> StateResult { - self.0.lock().inner.get_nonce_at(contract_address) + fn deref(&self) -> &Self::Target { + &self.provider } +} - fn get_storage_at( - &self, - contract_address: katana_cairo::starknet_api::core::ContractAddress, - key: StorageKey, - ) -> StateResult { - self.0.lock().inner.get_storage_at(contract_address, key) +impl<'a> StateProviderDb<'a> { + pub fn new( + provider: Box, + compiled_class_cache: Arc>>, + ) -> Self { + Self { provider, compiled_class_cache } } } -impl StateProofProvider for CachedState { - fn class_multiproof( +impl<'a> StateReader for StateProviderDb<'a> { + fn get_class_hash_at( &self, - classes: Vec, - ) -> ProviderResult { - let _ = classes; - unimplemented!("not supported in executor's state") + contract_address: katana_cairo::starknet_api::core::ContractAddress, + ) -> StateResult { + self.provider + .class_hash_of_contract(utils::to_address(contract_address)) + .map(|v| ClassHash(v.unwrap_or_default())) + .map_err(|e| StateError::StateReadError(e.to_string())) } - fn contract_multiproof( + fn get_compiled_class_hash( &self, - addresses: Vec, - ) -> ProviderResult { - let _ = addresses; - unimplemented!("not supported in executor's state") + class_hash: katana_cairo::starknet_api::core::ClassHash, + ) -> StateResult { + if let Some(hash) = self + .provider + .compiled_class_hash_of_class_hash(class_hash.0) + .map_err(|e| StateError::StateReadError(e.to_string()))? + { + Ok(CompiledClassHash(hash)) + } else { + Err(StateError::UndeclaredClassHash(class_hash)) + } } - fn storage_multiproof( + fn get_compiled_contract_class( &self, - address: katana_primitives::ContractAddress, - key: Vec, - ) -> ProviderResult { - let _ = address; - let _ = key; - unimplemented!("not supported in executor's state") - } -} - -impl StateRootProvider for CachedState { - fn classes_root(&self) -> ProviderResult { - unimplemented!("not supported in executor's state") - } - - fn contracts_root(&self) -> ProviderResult { - unimplemented!("not supported in executor's state") - } - - fn storage_root(&self, _: katana_primitives::ContractAddress) -> ProviderResult> { - unimplemented!("not supported in executor's state") - } -} - -#[cfg(test)] -mod tests { - - use blockifier::state::state_api::{State, StateReader}; - use katana_primitives::class::CompiledClass; - use katana_primitives::contract::ContractAddress; - use katana_primitives::genesis::constant::{ - DEFAULT_ACCOUNT_CLASS, DEFAULT_ACCOUNT_CLASS_CASM, DEFAULT_LEGACY_ERC20_CLASS, - DEFAULT_LEGACY_UDC_CLASS, - }; - use katana_primitives::utils::class::{parse_compiled_class, parse_sierra_class}; - use katana_primitives::{address, Felt}; - use katana_provider::providers::db::DbProvider; - use katana_provider::traits::contract::{ContractClassWriter, ContractClassWriterExt}; - use katana_provider::traits::state::{StateFactoryProvider, StateProvider, StateWriter}; - use starknet::macros::felt; - - use super::{CachedState, *}; - use crate::StateProviderDb; - - fn new_sierra_class() -> (ContractClass, CompiledClass) { - let json = include_str!("../../../../contracts/build/cairo1_contract.json"); - let artifact = serde_json::from_str(json).unwrap(); - let compiled_class = parse_compiled_class(artifact).unwrap(); - let sierra_class = parse_sierra_class(json).unwrap(); - (sierra_class, compiled_class) - } - - fn state_provider() -> Box { - let address = address!("0x67"); - let nonce = felt!("0x7"); - let storage_key = felt!("0x1"); - let storage_value = felt!("0x2"); - let class_hash = felt!("0x123"); - let compiled_hash = felt!("0x456"); - let class = DEFAULT_ACCOUNT_CLASS.clone(); - let compiled_class = DEFAULT_ACCOUNT_CLASS_CASM.clone(); - let legacy_class_hash = felt!("0x445"); - let legacy_class = DEFAULT_LEGACY_ERC20_CLASS.clone(); - - let provider = katana_provider::test_utils::test_provider(); - provider.set_nonce(address, nonce).unwrap(); - provider.set_class_hash_of_contract(address, class_hash).unwrap(); - provider.set_storage(address, storage_key, storage_value).unwrap(); - provider.set_compiled_class_hash_of_class_hash(class_hash, compiled_hash).unwrap(); - provider.set_class(class_hash, class).unwrap(); - provider.set_compiled_class(class_hash, compiled_class).unwrap(); - provider.set_class(legacy_class_hash, legacy_class.clone()).unwrap(); - provider.set_compiled_class(legacy_class_hash, legacy_class.compile().unwrap()).unwrap(); - - provider.latest().unwrap() - } + class_hash: ClassHash, + ) -> StateResult { + let mut class_cache = self.compiled_class_cache.lock(); - #[test] - fn can_fetch_from_inner_state_provider() -> anyhow::Result<()> { - let state = state_provider(); - let cached_state = CachedState::new(StateProviderDb::new(state)); - - let address = address!("0x67"); - let legacy_class_hash = felt!("0x445"); - let storage_key = felt!("0x1"); - - let api_address = utils::to_blk_address(address); - let actual_class_hash = cached_state.get_class_hash_at(api_address)?; - let actual_nonce = cached_state.get_nonce_at(api_address)?; - let actual_storage_value = cached_state - .get_storage_at(api_address, StorageKey(storage_key.try_into().unwrap()))?; - let actual_compiled_hash = cached_state.get_compiled_class_hash(actual_class_hash)?; - let actual_class = cached_state.get_compiled_contract_class(actual_class_hash)?; - let actual_legacy_class = - cached_state.get_compiled_contract_class(ClassHash(legacy_class_hash))?; - - assert_eq!(actual_nonce.0, felt!("0x7")); - assert_eq!(actual_storage_value, felt!("0x2")); - assert_eq!(actual_class_hash.0, felt!("0x123")); - assert_eq!(actual_compiled_hash.0, felt!("0x456")); - assert_eq!( - actual_class, - utils::to_class(DEFAULT_ACCOUNT_CLASS_CASM.clone()).unwrap().contract_class() - ); - assert_eq!( - actual_legacy_class, - utils::to_class(DEFAULT_LEGACY_ERC20_CLASS.clone().compile().expect("can compile")) - .unwrap() - .contract_class() - ); - - Ok(()) - } + if let Some(class) = class_cache.get(&class_hash.0) { + trace!(target: "executor", class = format!("{}", class_hash.to_hex_string()), "Class cache hit"); + return Ok(class.clone()); + } - #[test] - fn can_fetch_as_state_provider() -> anyhow::Result<()> { - let sp = state_provider(); - - // cache_state native data - let new_address = address!("0xdead"); - let new_storage_key = felt!("0xf00"); - let new_storage_value = felt!("0xba"); - let new_legacy_class_hash = felt!("0x1234"); - let new_legacy_class = DEFAULT_LEGACY_UDC_CLASS.clone(); - let new_legacy_compiled_hash = felt!("0x5678"); - let new_class_hash = felt!("0x777"); - let (new_sierra_class, new_compiled_sierra_class) = new_sierra_class(); - let new_compiled_hash = felt!("0xdead"); - - // we're asserting that the underlying state provider doesnt have cache state native data - - let actual_new_nonce = sp.nonce(new_address)?; - let actual_new_class_hash = sp.class_hash_of_contract(new_address)?; - let actual_new_storage_value = sp.storage(new_address, new_storage_key)?; - let actual_new_legacy_class = sp.class(new_legacy_class_hash)?; - let actual_new_legacy_sierra_class = sp.class(new_legacy_class_hash)?; - // let actual_new_sierra_class = sp.sierra_class(new_class_hash)?; - let actual_new_class = sp.class(new_class_hash)?; - let actual_new_compiled_class_hash = - sp.compiled_class_hash_of_class_hash(new_class_hash)?; - let actual_new_legacy_compiled_hash = - sp.compiled_class_hash_of_class_hash(new_legacy_class_hash)?; - - assert_eq!(actual_new_nonce, None, "data shouldn't exist"); - assert_eq!(actual_new_class_hash, None, "data shouldn't exist"); - assert_eq!(actual_new_storage_value, None, "data shouldn't exist"); - assert_eq!(actual_new_legacy_class, None, "data should'nt exist"); - assert_eq!(actual_new_legacy_sierra_class, None, "data shouldn't exist"); - // assert_eq!(actual_new_sierra_class, None, "data shouldn't exist"); - assert_eq!(actual_new_class, None, "data shouldn't exist"); - assert_eq!(actual_new_compiled_class_hash, None, "data shouldn't exist"); - assert_eq!(actual_new_legacy_compiled_hash, None, "data shouldn't exist"); - - let cached_state = CachedState::new(StateProviderDb::new(sp)); - - // insert some data to the cached state + if let Some(class) = self + .provider + .compiled_class(class_hash.0) + .map_err(|e| StateError::StateReadError(e.to_string()))? { - let lock = &mut cached_state.0.lock(); - let blk_state = &mut lock.inner; + trace!(target: "executor", class = format!("{}", class_hash.to_hex_string()), "Class cache miss"); - let address = utils::to_blk_address(new_address); - let storage_key = StorageKey(new_storage_key.try_into().unwrap()); - let storage_value = new_storage_value; - let class_hash = ClassHash(new_class_hash); let class = - utils::to_class(new_compiled_sierra_class.clone()).unwrap().contract_class(); - let compiled_hash = CompiledClassHash(new_compiled_hash); - let legacy_class_hash = ClassHash(new_legacy_class_hash); - let legacy_class = - utils::to_class(DEFAULT_LEGACY_UDC_CLASS.clone().compile().expect("can compile")) - .unwrap() - .contract_class(); - let legacy_compiled_hash = CompiledClassHash(new_legacy_compiled_hash); - - blk_state.increment_nonce(address)?; - blk_state.set_class_hash_at(address, legacy_class_hash)?; - blk_state.set_storage_at(address, storage_key, storage_value)?; - blk_state.set_contract_class(class_hash, class)?; - blk_state.set_compiled_class_hash(class_hash, compiled_hash)?; - blk_state.set_contract_class(legacy_class_hash, legacy_class)?; - blk_state.set_compiled_class_hash(legacy_class_hash, legacy_compiled_hash)?; - - let declared_classes = &mut lock.declared_classes; - declared_classes.insert(new_legacy_class_hash, new_legacy_class.clone()); - declared_classes.insert(new_class_hash, new_sierra_class.clone()); + utils::to_class(class).map_err(|e| StateError::StateReadError(e.to_string()))?; + + class_cache.insert(class_hash.0, class.clone()); + return Ok(class); } - // assert that can fetch data from the underlyign state provider - let sp: Box = Box::new(cached_state); - - let address = address!("0x67"); - let class_hash = felt!("0x123"); - let legacy_class_hash = felt!("0x445"); - - let actual_class_hash = sp.class_hash_of_contract(address)?; - let actual_nonce = sp.nonce(address)?; - let actual_storage_value = sp.storage(address, felt!("0x1"))?; - let actual_class = sp.class(class_hash)?; - let actual_compiled_class = sp.compiled_class(class_hash)?; - let actual_compiled_hash = sp.compiled_class_hash_of_class_hash(class_hash)?; - let actual_legacy_class = sp.class(legacy_class_hash)?; - - assert_eq!(actual_nonce, Some(felt!("0x7"))); - assert_eq!(actual_class_hash, Some(class_hash)); - assert_eq!(actual_storage_value, Some(felt!("0x2"))); - assert_eq!(actual_compiled_hash, Some(felt!("0x456"))); - assert_eq!(actual_compiled_class, Some(DEFAULT_ACCOUNT_CLASS_CASM.clone())); - assert_eq!(actual_class, Some(DEFAULT_ACCOUNT_CLASS.clone())); - assert_eq!(actual_legacy_class, Some(DEFAULT_LEGACY_ERC20_CLASS.clone())); - - // assert that can fetch data native to the cached state from the state provider - - let actual_new_class_hash = sp.class_hash_of_contract(new_address)?; - let actual_new_nonce = sp.nonce(new_address)?; - let actual_new_storage_value = sp.storage(new_address, new_storage_key)?; - let actual_new_class = sp.class(new_class_hash)?; - let actual_new_compiled_class = sp.compiled_class(new_class_hash)?; - let actual_new_compiled_hash = sp.compiled_class_hash_of_class_hash(new_class_hash)?; - let actual_legacy_class = sp.class(new_legacy_class_hash)?; - let actual_legacy_compiled_class = sp.compiled_class(new_legacy_class_hash)?; - let actual_new_legacy_compiled_hash = - sp.compiled_class_hash_of_class_hash(new_legacy_class_hash)?; - - assert_eq!(actual_new_nonce, Some(felt!("0x1")), "data should be in cached state"); - assert_eq!( - actual_new_class_hash, - Some(new_legacy_class_hash), - "data should be in cached state" - ); - assert_eq!( - actual_new_storage_value, - Some(new_storage_value), - "data should be in cached state" - ); - assert_eq!(actual_new_compiled_class, Some(new_compiled_sierra_class)); - assert_eq!(actual_new_class, Some(new_sierra_class)); - assert_eq!(actual_new_compiled_hash, Some(new_compiled_hash)); - assert_eq!(actual_legacy_class, Some(new_legacy_class.clone())); - assert_eq!( - actual_legacy_compiled_class, - Some(new_legacy_class.compile().expect("can compile")) - ); - assert_eq!( - actual_new_legacy_compiled_hash, - Some(new_legacy_compiled_hash), - "data should - be in cached state" - ); - - Ok(()) + Err(StateError::UndeclaredClassHash(class_hash)) + } + + fn get_nonce_at( + &self, + contract_address: katana_cairo::starknet_api::core::ContractAddress, + ) -> StateResult { + self.provider + .nonce(utils::to_address(contract_address)) + .map(|n| Nonce(n.unwrap_or_default())) + .map_err(|e| StateError::StateReadError(e.to_string())) } - #[test] - fn fetch_non_existant_data() -> anyhow::Result<()> { - let db = DbProvider::new_ephemeral(); - - let address = address!("0x1"); - let class_hash = felt!("0x123"); - let storage_key = felt!("0x1"); - - // edge case: the StateProvider::storage impl of CachedState will return - // default value for non-existant storage key of an existant contract. It will - // only return None if the contract does not exist. The intended behaviour for - // StateProvider::storage is to return None if the storage key or contract address - // does not exist. - let edge_address = address!("0x2"); - db.set_class_hash_of_contract(edge_address, class_hash)?; - - let sp = db.latest()?; - - let cached_state = CachedState::new(StateProviderDb::new(sp)); - - let api_address = utils::to_blk_address(address); - let api_storage_key = StorageKey(storage_key.try_into().unwrap()); - let api_class_hash = ClassHash(class_hash); - - let actual_nonce = - cached_state.get_nonce_at(api_address).expect("should return default value"); - let actual_storage_value = cached_state - .get_storage_at(api_address, api_storage_key) - .expect("should return default value"); - let actual_class_hash = - cached_state.get_class_hash_at(api_address).expect("should return default value"); - let actual_compiled_hash = cached_state.get_compiled_class_hash(api_class_hash); - let actual_compiled_class = cached_state.get_compiled_contract_class(api_class_hash); - let actual_edge_storage_value = cached_state - .get_storage_at(utils::to_blk_address(edge_address), api_storage_key) - .expect("should return default value"); - - assert_eq!( - actual_nonce, - Default::default(), - "nonce of nonexistant contract should default to zero" - ); - assert_eq!( - actual_storage_value, - Default::default(), - "value of nonexistant contract and storage key should default to zero" - ); - assert_eq!( - actual_edge_storage_value, - Default::default(), - "value of nonexistant storage key but existant contract should default to zero" - ); - assert_eq!( - actual_class_hash, - ClassHash::default(), - "class hash of nonexistant contract should default to zero" - ); - assert!(actual_compiled_hash.unwrap_err().to_string().contains("is not declared")); - assert!(actual_compiled_class.unwrap_err().to_string().contains("is not declared")); - - let sp: Box = Box::new(cached_state); - - let actual_nonce = sp.nonce(address)?; - let actual_storage_value = sp.storage(address, storage_key)?; - let actual_edge_storage_value = sp.storage(edge_address, storage_key)?; - let actual_class_hash = sp.class_hash_of_contract(address)?; - let actual_compiled_hash = sp.compiled_class_hash_of_class_hash(class_hash)?; - let actual_class = sp.class(class_hash)?; - - assert_eq!(actual_nonce, None, "nonce of nonexistant contract should be None"); - assert_eq!(actual_class_hash, None, "class hash of nonexistant contract should be None"); - assert_eq!(actual_storage_value, None, "value of nonexistant contract should be None"); - assert_eq!( - actual_edge_storage_value, - Some(Felt::ZERO), - "edge case: value of nonexistant storage key but existant contract should return zero" - ); - assert_eq!(actual_compiled_hash, None); - assert_eq!(actual_class, None); - - Ok(()) + fn get_storage_at( + &self, + contract_address: katana_cairo::starknet_api::core::ContractAddress, + key: katana_cairo::starknet_api::state::StorageKey, + ) -> StateResult { + self.storage(utils::to_address(contract_address), *key.0.key()) + .map(|v| v.unwrap_or_default()) + .map_err(|e| StateError::StateReadError(e.to_string())) } } diff --git a/crates/katana/executor/src/implementation/blockifier/utils.rs b/crates/katana/executor/src/implementation/blockifier/utils.rs index 8b0cc8f9ab..4fbba3d13e 100644 --- a/crates/katana/executor/src/implementation/blockifier/utils.rs +++ b/crates/katana/executor/src/implementation/blockifier/utils.rs @@ -55,7 +55,7 @@ use katana_primitives::{class, event, message, trace, Felt}; use katana_provider::traits::contract::ContractClassProvider; use starknet::core::utils::parse_cairo_short_string; -use super::state::{CachedState, StateDb}; +use super::state::CachedState; use crate::abstraction::{EntryPointCall, ExecutionFlags}; use crate::utils::build_receipt; use crate::{ExecutionError, ExecutionResult, ExecutorResult}; @@ -387,7 +387,7 @@ pub fn to_executor_tx(tx: ExecutableTxWithHash) -> Transaction { }; let hash = TransactionHash(hash); - let class = to_class(compiled).unwrap(); + let class = to_class_info(compiled).unwrap(); let tx = DeclareTransaction::new(tx, hash, class).expect("class mismatch"); Transaction::AccountTransaction(AccountTransaction::Declare(tx)) } @@ -458,10 +458,8 @@ pub fn block_context_from_envs(block_env: &BlockEnv, cfg_env: &CfgEnv) -> BlockC BlockContext::new(block_info, chain_info, versioned_constants, BouncerConfig::max()) } -pub(super) fn state_update_from_cached_state( - state: &CachedState, -) -> StateUpdatesWithClasses { - let state_diff = state.0.lock().inner.to_state_diff().unwrap(); +pub(super) fn state_update_from_cached_state(state: &CachedState<'_>) -> StateUpdatesWithClasses { + let state_diff = state.inner.lock().cached_state.to_state_diff().unwrap(); let mut declared_contract_classes: BTreeMap< katana_primitives::class::ClassHash, @@ -583,16 +581,16 @@ pub fn to_blk_chain_id(chain_id: katana_primitives::chain::ChainId) -> ChainId { } } -pub fn to_class(class: class::CompiledClass) -> Result { +pub fn to_class_info(class: class::CompiledClass) -> Result { // TODO: @kariy not sure of the variant that must be used in this case. Should we change the // return type to include this case of error for contract class conversions? match class { - class::CompiledClass::Legacy(class) => { + class::CompiledClass::Legacy(..) => { // For cairo 0, the sierra_program_length must be 0. - Ok(ClassInfo::new(&ContractClass::V0(ContractClassV0::try_from(class)?), 0, 0).unwrap()) + Ok(ClassInfo::new(&to_class(class)?, 0, 0).unwrap()) } - class::CompiledClass::Class(casm) => { + class::CompiledClass::Class(..) => { // NOTE: // // Right now, we're using dummy values for the sierra class info (ie @@ -602,7 +600,7 @@ pub fn to_class(class: class::CompiledClass) -> Result // Make sure these values are the same over on `snos` when it re-executes the // transactions as otherwise the fees would be different. - let class = ContractClass::V1(ContractClassV1::try_from(casm)?); + let class = to_class(class)?; let sierra_program_length = 1; let abi_length = 0; @@ -612,6 +610,18 @@ pub fn to_class(class: class::CompiledClass) -> Result } } +/// Convert katana-primitives compiled class to blockfiier's contract class. +pub fn to_class(class: class::CompiledClass) -> Result { + match class { + class::CompiledClass::Legacy(class) => { + Ok(ContractClass::V0(ContractClassV0::try_from(class)?)) + } + class::CompiledClass::Class(casm) => { + Ok(ContractClass::V1(ContractClassV1::try_from(casm)?)) + } + } +} + /// TODO: remove this function once starknet api 0.8.0 is supported. fn starknet_api_ethaddr_to_felt(value: katana_cairo::starknet_api::core::EthAddress) -> Felt { let mut bytes = [0u8; 32]; diff --git a/crates/katana/executor/src/implementation/noop.rs b/crates/katana/executor/src/implementation/noop.rs index c1fbfce33e..bb4608bb20 100644 --- a/crates/katana/executor/src/implementation/noop.rs +++ b/crates/katana/executor/src/implementation/noop.rs @@ -1,5 +1,5 @@ use katana_primitives::block::ExecutableBlock; -use katana_primitives::class::{ClassHash, CompiledClass, CompiledClassHash, ContractClass}; +use katana_primitives::class::{ClassHash, CompiledClassHash, ContractClass}; use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageValue}; use katana_primitives::env::{BlockEnv, CfgEnv}; use katana_primitives::fee::TxFeeInfo; @@ -131,11 +131,6 @@ impl ContractClassProvider for NoopStateProvider { Ok(None) } - fn compiled_class(&self, hash: ClassHash) -> ProviderResult> { - let _ = hash; - Ok(None) - } - fn compiled_class_hash_of_class_hash( &self, hash: ClassHash, @@ -170,41 +165,5 @@ impl StateProvider for NoopStateProvider { } } -impl StateProofProvider for NoopStateProvider { - fn class_multiproof(&self, classes: Vec) -> ProviderResult { - let _ = classes; - Ok(katana_trie::MultiProof(Default::default())) - } - - fn contract_multiproof( - &self, - addresses: Vec, - ) -> ProviderResult { - let _ = addresses; - Ok(katana_trie::MultiProof(Default::default())) - } - - fn storage_multiproof( - &self, - address: ContractAddress, - key: Vec, - ) -> ProviderResult { - let _ = address; - let _ = key; - Ok(katana_trie::MultiProof(Default::default())) - } -} - -impl StateRootProvider for NoopStateProvider { - fn classes_root(&self) -> ProviderResult { - Ok(Felt::ZERO) - } - - fn contracts_root(&self) -> ProviderResult { - Ok(Felt::ZERO) - } - - fn storage_root(&self, _: ContractAddress) -> ProviderResult> { - Ok(Some(Felt::ZERO)) - } -} +impl StateProofProvider for NoopStateProvider {} +impl StateRootProvider for NoopStateProvider {} diff --git a/crates/katana/executor/src/lib.rs b/crates/katana/executor/src/lib.rs index 6b2175b0d2..d00990eba0 100644 --- a/crates/katana/executor/src/lib.rs +++ b/crates/katana/executor/src/lib.rs @@ -4,4 +4,5 @@ pub mod implementation; mod utils; mod abstraction; +pub mod error; pub use abstraction::*; diff --git a/crates/katana/executor/tests/executor.rs b/crates/katana/executor/tests/executor.rs index 83175f18c4..f1ec0edc4a 100644 --- a/crates/katana/executor/tests/executor.rs +++ b/crates/katana/executor/tests/executor.rs @@ -12,6 +12,7 @@ use katana_primitives::genesis::constant::{ }; use katana_primitives::transaction::TxWithHash; use katana_primitives::{address, Felt}; +use katana_provider::traits::contract::ContractClassProviderExt; use katana_provider::traits::state::StateProvider; use starknet::core::utils::{ get_storage_var_address, get_udc_deployed_address, UdcUniqueSettings, UdcUniqueness, diff --git a/crates/katana/node/Cargo.toml b/crates/katana/node/Cargo.toml index 53963fa56f..1f331dba09 100644 --- a/crates/katana/node/Cargo.toml +++ b/crates/katana/node/Cargo.toml @@ -13,6 +13,7 @@ katana-executor.workspace = true katana-pipeline.workspace = true katana-pool.workspace = true katana-primitives.workspace = true +katana-provider.workspace = true katana-rpc.workspace = true katana-rpc-api.workspace = true katana-stage.workspace = true @@ -40,7 +41,6 @@ strum_macros.workspace = true clap = { workspace = true, optional = true } dojo-utils = { workspace = true, optional = true } katana-feeder-gateway = { workspace = true, optional = true } -katana-provider = { workspace = true, optional = true } tokio = { workspace = true, features = [ "time" ], optional = true } tracing-log = { workspace = true, optional = true } tracing-subscriber = { workspace = true, optional = true } @@ -52,7 +52,7 @@ vergen-gitcl = { version = "1.0.0", features = [ "build", "cargo", "rustc", "si" [features] starknet-messaging = [ "katana-core/starknet-messaging" ] # experimental feature to test katana full node mode -full-node = [ "dep:katana-feeder-gateway", "dep:katana-provider", "dep:tokio" ] +full-node = [ "dep:katana-feeder-gateway", "dep:tokio" ] [[bin]] name = "full-node" diff --git a/crates/katana/pool/src/validation/stateful.rs b/crates/katana/pool/src/validation/stateful.rs index 7e81a5a9ee..2eaa3abd90 100644 --- a/crates/katana/pool/src/validation/stateful.rs +++ b/crates/katana/pool/src/validation/stateful.rs @@ -9,10 +9,12 @@ use katana_executor::implementation::blockifier::blockifier::transaction::errors TransactionExecutionError, TransactionFeeError, TransactionPreValidationError, }; use katana_executor::implementation::blockifier::blockifier::transaction::transaction_execution::Transaction; +use katana_executor::implementation::blockifier::state::StateProviderDb; use katana_executor::implementation::blockifier::utils::{ block_context_from_envs, to_address, to_executor_tx, }; -use katana_executor::{ExecutionFlags, StateProviderDb}; +use katana_executor::implementation::blockifier::COMPILED_CLASS_CACHE; +use katana_executor::ExecutionFlags; use katana_primitives::contract::{ContractAddress, Nonce}; use katana_primitives::env::{BlockEnv, CfgEnv}; use katana_primitives::transaction::{ExecutableTx, ExecutableTxWithHash}; @@ -86,7 +88,8 @@ impl Inner { // for transaction validation. fn prepare(&self) -> StatefulValidator> { let state = Box::new(self.state.clone()); - let cached_state = CachedState::new(StateProviderDb::new(state)); + let cached_state = + CachedState::new(StateProviderDb::new(state, COMPILED_CLASS_CACHE.clone())); let context = block_context_from_envs(&self.block_env, &self.cfg_env); StatefulValidator::create(cached_state, context) } diff --git a/crates/katana/storage/provider/src/error.rs b/crates/katana/storage/provider/src/error.rs index 4445edd795..7ec77fbab8 100644 --- a/crates/katana/storage/provider/src/error.rs +++ b/crates/katana/storage/provider/src/error.rs @@ -96,6 +96,12 @@ pub enum ProviderError { storage_key: StorageKey, }, + #[error("State proof not supported")] + StateProofNotSupported, + + #[error("State root not found")] + StateRootNotFound, + #[error(transparent)] ContractClassCompilation(#[from] ContractClassCompilationError), @@ -110,6 +116,6 @@ pub enum ProviderError { ForkedBackend(#[from] crate::providers::fork::backend::BackendError), /// Any error that is not covered by the other variants. - #[error("soemthing went wrong: {0}")] + #[error("Something went wrong: {0}")] Other(String), } diff --git a/crates/katana/storage/provider/src/providers/db/mod.rs b/crates/katana/storage/provider/src/providers/db/mod.rs index 5dc37e7493..c490774c55 100644 --- a/crates/katana/storage/provider/src/providers/db/mod.rs +++ b/crates/katana/storage/provider/src/providers/db/mod.rs @@ -714,10 +714,7 @@ impl BlockWriter for DbProvider { } for (class_hash, class) in states.classes { - // generate the compiled class - let compiled = class.clone().compile()?; db_tx.put::(class_hash, class)?; - db_tx.put::(class_hash, compiled)?; } // insert storage changes diff --git a/crates/katana/storage/provider/src/providers/db/state.rs b/crates/katana/storage/provider/src/providers/db/state.rs index e7864e3bfa..b16dafd3dc 100644 --- a/crates/katana/storage/provider/src/providers/db/state.rs +++ b/crates/katana/storage/provider/src/providers/db/state.rs @@ -118,10 +118,6 @@ where Ok(self.0.get::(hash)?) } - fn compiled_class(&self, hash: ClassHash) -> ProviderResult> { - Ok(self.0.get::(hash)?) - } - fn compiled_class_hash_of_class_hash( &self, hash: ClassHash, @@ -246,14 +242,6 @@ where } } - fn compiled_class(&self, hash: ClassHash) -> ProviderResult> { - if self.is_class_declared_before_block(hash)? { - Ok(self.tx.get::(hash)?) - } else { - Ok(None) - } - } - fn compiled_class_hash_of_class_hash( &self, hash: ClassHash, diff --git a/crates/katana/storage/provider/src/providers/fork/backend.rs b/crates/katana/storage/provider/src/providers/fork/backend.rs index b6113532b5..8197724033 100644 --- a/crates/katana/storage/provider/src/providers/fork/backend.rs +++ b/crates/katana/storage/provider/src/providers/fork/backend.rs @@ -14,7 +14,7 @@ use futures::future::BoxFuture; use futures::stream::Stream; use futures::{Future, FutureExt}; use katana_primitives::block::BlockHashOrNumber; -use katana_primitives::class::{ClassHash, CompiledClass, CompiledClassHash, ContractClass}; +use katana_primitives::class::{ClassHash, CompiledClassHash, ContractClass}; use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageValue}; use katana_primitives::conversion::rpc::{ compiled_class_hash_from_flattened_sierra_class, legacy_rpc_to_class, @@ -608,38 +608,6 @@ impl ContractClassProvider for SharedStateProvider { Ok(Some(class)) } - fn compiled_class(&self, hash: ClassHash) -> ProviderResult> { - if let compiled @ Some(..) = - self.0.shared_contract_classes.compiled_classes.read().get(&hash) - { - Ok(compiled.cloned()) - } - // If doesn't exist in the cache, try to fetch it from the forked provider. - else { - // The Starknet RPC specs doesn't provide an endpoint for fetching the compiled class. - // If the uncompiled version doesn't yet exist locally, we fetch from the forked - // provider (from the `starknet_getClsas` method) and compile it. - let class = self.0.shared_contract_classes.classes.read().get(&hash).cloned(); - let class = if class.is_some() { class } else { self.class(hash)? }; - let compiled = class.map(|c| c.compile()).transpose().inspect_err(|error|{ - error!(target: LOG_TARGET, hash = %format!("{hash:#x}"), %error, "Failed to compile class."); - })?; - - if let Some(compiled) = compiled { - // Store the compiled class in the cache. - self.0 - .shared_contract_classes - .compiled_classes - .write() - .entry(hash) - .or_insert(compiled.clone()); - Ok(Some(compiled)) - } else { - Ok(None) - } - } - } - fn compiled_class_hash_of_class_hash( &self, hash: ClassHash, diff --git a/crates/katana/storage/provider/src/providers/fork/state.rs b/crates/katana/storage/provider/src/providers/fork/state.rs index 7ab28ca869..c2ff759d7f 100644 --- a/crates/katana/storage/provider/src/providers/fork/state.rs +++ b/crates/katana/storage/provider/src/providers/fork/state.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use katana_primitives::class::{ClassHash, CompiledClass, CompiledClassHash, ContractClass}; +use katana_primitives::class::{ClassHash, CompiledClassHash, ContractClass}; use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageValue}; use super::backend::SharedStateProvider; @@ -84,13 +84,6 @@ impl ContractClassProvider for ForkedStateDb { ContractClassProvider::class(&self.db, hash) } - fn compiled_class(&self, hash: ClassHash) -> ProviderResult> { - if let class @ Some(_) = self.shared_contract_classes.compiled_classes.read().get(&hash) { - return Ok(class.cloned()); - } - ContractClassProvider::compiled_class(&self.db, hash) - } - fn compiled_class_hash_of_class_hash( &self, hash: ClassHash, @@ -102,40 +95,8 @@ impl ContractClassProvider for ForkedStateDb { } } -impl StateProofProvider for ForkedStateDb { - fn class_multiproof(&self, _: Vec) -> ProviderResult { - Ok(katana_trie::MultiProof(Default::default())) - } - - fn contract_multiproof( - &self, - _: Vec, - ) -> ProviderResult { - Ok(katana_trie::MultiProof(Default::default())) - } - - fn storage_multiproof( - &self, - _: ContractAddress, - _: Vec, - ) -> ProviderResult { - Ok(katana_trie::MultiProof(Default::default())) - } -} - -impl StateRootProvider for ForkedStateDb { - fn classes_root(&self) -> ProviderResult { - Ok(katana_primitives::Felt::ZERO) - } - - fn contracts_root(&self) -> ProviderResult { - Ok(katana_primitives::Felt::ZERO) - } - - fn storage_root(&self, _: ContractAddress) -> ProviderResult> { - Ok(Some(katana_primitives::Felt::ZERO)) - } -} +impl StateProofProvider for ForkedStateDb {} +impl StateRootProvider for ForkedStateDb {} #[derive(Debug)] pub(super) struct LatestStateProvider(pub(super) Arc); @@ -166,10 +127,6 @@ impl ContractClassProvider for LatestStateProvider { ContractClassProvider::class(&self.0, hash) } - fn compiled_class(&self, hash: ClassHash) -> ProviderResult> { - ContractClassProvider::compiled_class(&self.0, hash) - } - fn compiled_class_hash_of_class_hash( &self, hash: ClassHash, @@ -178,40 +135,8 @@ impl ContractClassProvider for LatestStateProvider { } } -impl StateProofProvider for LatestStateProvider { - fn class_multiproof(&self, _: Vec) -> ProviderResult { - Ok(katana_trie::MultiProof(Default::default())) - } - - fn contract_multiproof( - &self, - _: Vec, - ) -> ProviderResult { - Ok(katana_trie::MultiProof(Default::default())) - } - - fn storage_multiproof( - &self, - _: ContractAddress, - _: Vec, - ) -> ProviderResult { - Ok(katana_trie::MultiProof(Default::default())) - } -} - -impl StateRootProvider for LatestStateProvider { - fn classes_root(&self) -> ProviderResult { - Ok(katana_primitives::Felt::ZERO) - } - - fn contracts_root(&self) -> ProviderResult { - Ok(katana_primitives::Felt::ZERO) - } - - fn storage_root(&self, _: ContractAddress) -> ProviderResult> { - Ok(Some(katana_primitives::Felt::ZERO)) - } -} +impl StateProofProvider for LatestStateProvider {} +impl StateRootProvider for LatestStateProvider {} impl StateProvider for ForkedSnapshot { fn nonce(&self, address: ContractAddress) -> ProviderResult> { @@ -266,14 +191,6 @@ impl ContractClassProvider for ForkedSnapshot { } } - fn compiled_class(&self, hash: ClassHash) -> ProviderResult> { - if self.inner.compiled_class_hashes.contains_key(&hash) { - Ok(self.classes.compiled_classes.read().get(&hash).cloned()) - } else { - ContractClassProvider::compiled_class(&self.inner.db, hash) - } - } - fn compiled_class_hash_of_class_hash( &self, hash: ClassHash, @@ -285,40 +202,8 @@ impl ContractClassProvider for ForkedSnapshot { } } -impl StateProofProvider for ForkedSnapshot { - fn class_multiproof(&self, _: Vec) -> ProviderResult { - Ok(katana_trie::MultiProof(Default::default())) - } - - fn contract_multiproof( - &self, - _: Vec, - ) -> ProviderResult { - Ok(katana_trie::MultiProof(Default::default())) - } - - fn storage_multiproof( - &self, - _: ContractAddress, - _: Vec, - ) -> ProviderResult { - Ok(katana_trie::MultiProof(Default::default())) - } -} - -impl StateRootProvider for ForkedSnapshot { - fn classes_root(&self) -> ProviderResult { - Ok(katana_primitives::Felt::ZERO) - } - - fn contracts_root(&self) -> ProviderResult { - Ok(katana_primitives::Felt::ZERO) - } - - fn storage_root(&self, _: ContractAddress) -> ProviderResult> { - Ok(Some(katana_primitives::Felt::ZERO)) - } -} +impl StateProofProvider for ForkedSnapshot {} +impl StateRootProvider for ForkedSnapshot {} #[cfg(test)] mod tests { diff --git a/crates/katana/storage/provider/src/providers/in_memory/state.rs b/crates/katana/storage/provider/src/providers/in_memory/state.rs index f6e5b93f03..caf07ff4d3 100644 --- a/crates/katana/storage/provider/src/providers/in_memory/state.rs +++ b/crates/katana/storage/provider/src/providers/in_memory/state.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, VecDeque}; use std::sync::Arc; use katana_primitives::block::BlockNumber; -use katana_primitives::class::{ClassHash, CompiledClass, CompiledClassHash, ContractClass}; +use katana_primitives::class::{ClassHash, CompiledClassHash, ContractClass}; use katana_primitives::contract::{Nonce, StorageKey, StorageValue}; use katana_primitives::{ContractAddress, Felt}; use katana_trie::MultiProof; @@ -144,11 +144,6 @@ impl ContractClassProvider for EmptyStateProvider { Ok(None) } - fn compiled_class(&self, hash: ClassHash) -> ProviderResult> { - let _ = hash; - Ok(None) - } - fn compiled_class_hash_of_class_hash( &self, hash: ClassHash, diff --git a/crates/katana/storage/provider/src/traits/contract.rs b/crates/katana/storage/provider/src/traits/contract.rs index 7a7a7ea08b..7cf5f63fc2 100644 --- a/crates/katana/storage/provider/src/traits/contract.rs +++ b/crates/katana/storage/provider/src/traits/contract.rs @@ -4,19 +4,10 @@ use crate::ProviderResult; /// A provider trait for retrieving contract class related information. #[auto_impl::auto_impl(&, Box, Arc)] -pub trait ContractClassProvider: Send + Sync { +pub trait ContractClassProvider { /// Returns the compiled class definition of a contract class given its class hash. fn class(&self, hash: ClassHash) -> ProviderResult>; - /// Returns the compiled class definition of a contract class given its class hash. - /// - /// It depends on the provider implementation on how to store/manage the compiled classes, be it - /// compiling on demand (default implementation), or storing the compiled class in the database - /// or volatile cache. - fn compiled_class(&self, hash: ClassHash) -> ProviderResult> { - if let Some(class) = self.class(hash)? { Ok(Some(class.compile()?)) } else { Ok(None) } - } - /// Returns the compiled class hash for the given class hash. fn compiled_class_hash_of_class_hash( &self, @@ -25,7 +16,7 @@ pub trait ContractClassProvider: Send + Sync { } #[auto_impl::auto_impl(&, Box, Arc)] -pub trait ContractClassWriter: Send + Sync { +pub trait ContractClassWriter { /// Sets the compiled class hash for the given class hash. fn set_compiled_class_hash_of_class_hash( &self, @@ -42,3 +33,12 @@ pub trait ContractClassWriterExt: ContractClassWriter { /// Set the compiled class for the given class hash. fn set_compiled_class(&self, hash: ClassHash, class: CompiledClass) -> ProviderResult<()>; } + +pub trait ContractClassProviderExt: ContractClassProvider { + /// Returns the compiled class definition of a contract class given its class hash. + fn compiled_class(&self, hash: ClassHash) -> ProviderResult> { + if let Some(class) = self.class(hash)? { Ok(Some(class.compile()?)) } else { Ok(None) } + } +} + +impl ContractClassProviderExt for T {} diff --git a/crates/katana/storage/provider/src/traits/state.rs b/crates/katana/storage/provider/src/traits/state.rs index 36e81d62d0..7df7a01abe 100644 --- a/crates/katana/storage/provider/src/traits/state.rs +++ b/crates/katana/storage/provider/src/traits/state.rs @@ -7,6 +7,7 @@ use starknet::macros::short_string; use starknet_types_core::hash::StarkHash; use super::contract::ContractClassProvider; +use crate::error::ProviderError; use crate::ProviderResult; #[auto_impl::auto_impl(&, Box, Arc)] @@ -22,18 +23,25 @@ pub trait StateRootProvider: Send + Sync { } /// Retrieves the root of the classes trie. - fn classes_root(&self) -> ProviderResult; + fn classes_root(&self) -> ProviderResult { + Err(ProviderError::StateRootNotFound) + } /// Retrieves the root of the contracts trie. - fn contracts_root(&self) -> ProviderResult; + fn contracts_root(&self) -> ProviderResult { + Err(ProviderError::StateRootNotFound) + } /// Retrieves the root of a contract's storage trie. - fn storage_root(&self, contract: ContractAddress) -> ProviderResult>; + fn storage_root(&self, contract: ContractAddress) -> ProviderResult> { + let _ = contract; + Ok(None) + } } #[auto_impl::auto_impl(&, Box, Arc)] pub trait StateProvider: - ContractClassProvider + StateProofProvider + StateRootProvider + Send + Sync + std::fmt::Debug + ContractClassProvider + StateProofProvider + StateRootProvider + std::fmt::Debug { /// Returns the nonce of a contract. fn nonce(&self, address: ContractAddress) -> ProviderResult>; @@ -91,9 +99,19 @@ pub trait StateProofProvider: Send + Sync { &self, address: ContractAddress, key: Vec, - ) -> ProviderResult; + ) -> ProviderResult { + let _ = address; + let _ = key; + Err(ProviderError::StateProofNotSupported) + } - fn contract_multiproof(&self, addresses: Vec) -> ProviderResult; + fn contract_multiproof(&self, addresses: Vec) -> ProviderResult { + let _ = addresses; + Err(ProviderError::StateProofNotSupported) + } - fn class_multiproof(&self, classes: Vec) -> ProviderResult; + fn class_multiproof(&self, classes: Vec) -> ProviderResult { + let _ = classes; + Err(ProviderError::StateProofNotSupported) + } } diff --git a/crates/katana/storage/provider/tests/class.rs b/crates/katana/storage/provider/tests/class.rs index 4e0c75c493..839b2ec098 100644 --- a/crates/katana/storage/provider/tests/class.rs +++ b/crates/katana/storage/provider/tests/class.rs @@ -8,6 +8,7 @@ use katana_primitives::block::{BlockHashOrNumber, BlockNumber}; use katana_primitives::class::{ClassHash, CompiledClassHash, ContractClass}; use katana_primitives::genesis::constant::{DEFAULT_LEGACY_ERC20_CLASS, DEFAULT_LEGACY_UDC_CLASS}; use katana_provider::providers::fork::ForkedProvider; +use katana_provider::traits::contract::ContractClassProviderExt; use katana_provider::traits::state::{StateFactoryProvider, StateProvider}; use katana_provider::BlockchainProvider; use rstest_reuse::{self, *};