From cd8ec587039e9451a846bcc311679f8e702337fc Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Wed, 11 Feb 2026 20:30:24 -0500 Subject: [PATCH] refactor(engine): move CachedStateProvider prewarm to const generic (#22106) Co-authored-by: Amp --- crates/engine/tree/src/tree/cached_state.rs | 74 ++++++++----------- .../tree/src/tree/payload_processor/mod.rs | 2 +- .../src/tree/payload_processor/prewarm.rs | 13 ++-- .../engine/tree/src/tree/payload_validator.rs | 7 +- 4 files changed, 45 insertions(+), 51 deletions(-) diff --git a/crates/engine/tree/src/tree/cached_state.rs b/crates/engine/tree/src/tree/cached_state.rs index 02bd2b4e0f..182a278f93 100644 --- a/crates/engine/tree/src/tree/cached_state.rs +++ b/crates/engine/tree/src/tree/cached_state.rs @@ -76,8 +76,16 @@ impl CacheConfig for EpochCacheConfig { type FixedCache = fixed_cache::Cache; /// A wrapper of a state provider and a shared cache. +/// +/// The const generic `PREWARM` controls whether every cache miss is populated. This is only +/// relevant for pre-warm transaction execution with the intention to pre-populate the cache with +/// data for regular block execution. During regular block execution the cache doesn't need to be +/// populated because the actual EVM database [`State`](revm::database::State) also caches +/// internally during block execution and the cache is then updated after the block with the entire +/// [`BundleState`] output of that block which contains all accessed accounts, code, storage. See +/// also [`ExecutionCache::insert_state`]. #[derive(Debug)] -pub struct CachedStateProvider { +pub struct CachedStateProvider { /// The state provider state_provider: S, @@ -86,15 +94,9 @@ pub struct CachedStateProvider { /// Metrics for the cached state provider metrics: CachedStateMetrics, - - /// If prewarm enabled we populate every cache miss - prewarm: bool, } -impl CachedStateProvider -where - S: StateProvider, -{ +impl CachedStateProvider { /// Creates a new [`CachedStateProvider`] from an [`ExecutionCache`], state provider, and /// [`CachedStateMetrics`]. pub const fn new( @@ -102,27 +104,7 @@ where caches: ExecutionCache, metrics: CachedStateMetrics, ) -> Self { - Self { state_provider, caches, metrics, prewarm: false } - } -} - -impl CachedStateProvider { - /// Enables pre-warm mode so that every cache miss is populated. - /// - /// This is only relevant for pre-warm transaction execution with the intention to pre-populate - /// the cache with data for regular block execution. During regular block execution the - /// cache doesn't need to be populated because the actual EVM database - /// [`State`](revm::database::State) also caches internally during block execution and the cache - /// is then updated after the block with the entire [`BundleState`] output of that block which - /// contains all accessed accounts,code,storage. See also [`ExecutionCache::insert_state`]. - pub const fn prewarm(mut self) -> Self { - self.prewarm = true; - self - } - - /// Returns whether this provider should pre-warm cache misses. - const fn is_prewarm(&self) -> bool { - self.prewarm + Self { state_provider, caches, metrics } } } @@ -307,9 +289,9 @@ impl StatsHandler for CacheStatsHandler { } } -impl AccountReader for CachedStateProvider { +impl AccountReader for CachedStateProvider { fn basic_account(&self, address: &Address) -> ProviderResult> { - if self.is_prewarm() { + if PREWARM { match self.caches.get_or_try_insert_account_with(*address, || { self.state_provider.basic_account(address) })? { @@ -334,13 +316,13 @@ pub enum CachedStatus { Cached(T), } -impl StateProvider for CachedStateProvider { +impl StateProvider for CachedStateProvider { fn storage( &self, account: Address, storage_key: StorageKey, ) -> ProviderResult> { - if self.is_prewarm() { + if PREWARM { match self.caches.get_or_try_insert_storage_with(account, storage_key, || { self.state_provider.storage(account, storage_key).map(Option::unwrap_or_default) })? { @@ -360,9 +342,9 @@ impl StateProvider for CachedStateProvider { } } -impl BytecodeReader for CachedStateProvider { +impl BytecodeReader for CachedStateProvider { fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult> { - if self.is_prewarm() { + if PREWARM { match self.caches.get_or_try_insert_code_with(*code_hash, || { self.state_provider.bytecode_by_hash(code_hash) })? { @@ -378,7 +360,9 @@ impl BytecodeReader for CachedStateProvider { } } -impl StateRootProvider for CachedStateProvider { +impl StateRootProvider + for CachedStateProvider +{ fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult { self.state_provider.state_root(hashed_state) } @@ -402,7 +386,9 @@ impl StateRootProvider for CachedStateProvider { } } -impl StateProofProvider for CachedStateProvider { +impl StateProofProvider + for CachedStateProvider +{ fn proof( &self, input: TrieInput, @@ -429,7 +415,9 @@ impl StateProofProvider for CachedStateProvider { } } -impl StorageRootProvider for CachedStateProvider { +impl StorageRootProvider + for CachedStateProvider +{ fn storage_root( &self, address: Address, @@ -457,7 +445,7 @@ impl StorageRootProvider for CachedStateProvider { } } -impl BlockHashReader for CachedStateProvider { +impl BlockHashReader for CachedStateProvider { fn block_hash(&self, number: alloy_primitives::BlockNumber) -> ProviderResult> { self.state_provider.block_hash(number) } @@ -471,7 +459,9 @@ impl BlockHashReader for CachedStateProvider { } } -impl HashedPostStateProvider for CachedStateProvider { +impl HashedPostStateProvider + for CachedStateProvider +{ fn hashed_post_state(&self, bundle_state: &reth_revm::db::BundleState) -> HashedPostState { self.state_provider.hashed_post_state(bundle_state) } @@ -868,7 +858,7 @@ mod tests { let caches = ExecutionCache::new(1000); let state_provider = - CachedStateProvider::new(provider, caches, CachedStateMetrics::zeroed()); + CachedStateProvider::<_, false>::new(provider, caches, CachedStateMetrics::zeroed()); let res = state_provider.storage(address, storage_key); assert!(res.is_ok()); @@ -888,7 +878,7 @@ mod tests { let caches = ExecutionCache::new(1000); let state_provider = - CachedStateProvider::new(provider, caches, CachedStateMetrics::zeroed()); + CachedStateProvider::<_, false>::new(provider, caches, CachedStateMetrics::zeroed()); let res = state_provider.storage(address, storage_key); assert!(res.is_ok()); diff --git a/crates/engine/tree/src/tree/payload_processor/mod.rs b/crates/engine/tree/src/tree/payload_processor/mod.rs index 5d25f67383..92b45698c3 100644 --- a/crates/engine/tree/src/tree/payload_processor/mod.rs +++ b/crates/engine/tree/src/tree/payload_processor/mod.rs @@ -297,7 +297,7 @@ where let provider = provider_builder.build().expect("failed to build provider"); let provider = if let Some(saved_cache) = saved_cache { let (cache, metrics, _disable_metrics) = saved_cache.split(); - Box::new(CachedStateProvider::new(provider, cache, metrics)) + Box::new(CachedStateProvider::<_, false>::new(provider, cache, metrics)) as Box } else { Box::new(provider) diff --git a/crates/engine/tree/src/tree/payload_processor/prewarm.rs b/crates/engine/tree/src/tree/payload_processor/prewarm.rs index 8e64721f2e..4b1834694e 100644 --- a/crates/engine/tree/src/tree/payload_processor/prewarm.rs +++ b/crates/engine/tree/src/tree/payload_processor/prewarm.rs @@ -535,11 +535,11 @@ where if let Some(saved_cache) = saved_cache { let caches = saved_cache.cache().clone(); let cache_metrics = saved_cache.metrics().clone(); - state_provider = Box::new( - CachedStateProvider::new(state_provider, caches, cache_metrics) - // ensure we pre-warm the cache - .prewarm(), - ); + state_provider = Box::new(CachedStateProvider::<_, true>::new( + state_provider, + caches, + cache_metrics, + )); } let state_provider = StateProviderDatabase::new(state_provider); @@ -749,7 +749,8 @@ where let saved_cache = saved_cache.expect("BAL prewarm should only run with cache"); let caches = saved_cache.cache().clone(); let cache_metrics = saved_cache.metrics().clone(); - let state_provider = CachedStateProvider::new(state_provider, caches, cache_metrics); + let state_provider = + CachedStateProvider::<_, false>::new(state_provider, caches, cache_metrics); let start = Instant::now(); diff --git a/crates/engine/tree/src/tree/payload_validator.rs b/crates/engine/tree/src/tree/payload_validator.rs index 72f4a56292..e9464dbc15 100644 --- a/crates/engine/tree/src/tree/payload_validator.rs +++ b/crates/engine/tree/src/tree/payload_validator.rs @@ -443,8 +443,11 @@ where // Use cached state provider before executing, used in execution after prewarming threads // complete if let Some((caches, cache_metrics)) = handle.caches().zip(handle.cache_metrics()) { - state_provider = - Box::new(CachedStateProvider::new(state_provider, caches, cache_metrics)); + state_provider = Box::new(CachedStateProvider::<_, false>::new( + state_provider, + caches, + cache_metrics, + )); }; if self.config.state_provider_metrics() {