Skip to main content

reth_rpc_eth_types/cache/
mod.rs

1//! Async caching support for eth RPC
2
3use super::{EthStateCacheConfig, MultiConsumerLruCache};
4use crate::block::CachedTransaction;
5use alloy_consensus::{transaction::TxHashRef, BlockHeader};
6use alloy_eip7928::bal::DecodedBal;
7use alloy_eips::BlockHashOrNumber;
8use alloy_primitives::{Address, Bytes, TxHash, B256};
9use futures::{stream::FuturesOrdered, Stream, StreamExt};
10use reth_chain_state::CanonStateNotification;
11use reth_errors::{ProviderError, ProviderResult};
12use reth_execution_types::Chain;
13use reth_primitives_traits::{Block, BlockBody, InMemorySize, NodePrimitives, RecoveredBlock};
14use reth_revm::{
15    bytecode::Bytecode,
16    primitives::{StorageKey, StorageValue},
17    state::bal::{
18        AccountBal as RevmAccountBal, AccountInfoBal as RevmAccountInfoBal, Bal as RevmBal,
19        BalWrites as RevmBalWrites, StorageBal as RevmStorageBal,
20    },
21};
22use reth_storage_api::{BalProvider, BlockReader, TransactionVariant};
23use reth_tasks::Runtime;
24use schnellru::{ByLength, Limiter, LruMap};
25use std::{
26    future::Future,
27    pin::Pin,
28    sync::Arc,
29    task::{Context, Poll},
30};
31use tokio::sync::{
32    mpsc::{unbounded_channel, UnboundedSender},
33    oneshot, Semaphore,
34};
35use tokio_stream::wrappers::UnboundedReceiverStream;
36
37pub mod config;
38pub mod db;
39pub mod metrics;
40pub mod multi_consumer;
41
42/// The type that can send the response to a requested [`RecoveredBlock`]
43type BlockWithSendersResponseSender<B> =
44    oneshot::Sender<ProviderResult<Option<Arc<RecoveredBlock<B>>>>>;
45
46/// The type that can send the response to the requested receipts of a block.
47type ReceiptsResponseSender<R> = oneshot::Sender<ProviderResult<Option<Arc<Vec<R>>>>>;
48
49type CachedBlockResponseSender<B> = oneshot::Sender<Option<Arc<RecoveredBlock<B>>>>;
50
51type CachedBlockAndReceiptsResponseSender<B, R> =
52    oneshot::Sender<(Option<Arc<RecoveredBlock<B>>>, Option<Arc<Vec<R>>>)>;
53
54/// The type that can send the response to a requested header
55type HeaderResponseSender<H> = oneshot::Sender<ProviderResult<H>>;
56
57/// The type that can send the response with a chain of cached blocks
58type CachedParentBlocksResponseSender<B> = oneshot::Sender<Vec<Arc<RecoveredBlock<B>>>>;
59
60/// The type that can send the response for a transaction hash lookup
61type TransactionHashResponseSender<B, R> = oneshot::Sender<Option<CachedTransaction<B, R>>>;
62
63/// The type that can send the response to a requested revm BAL.
64type BalResponseSender = oneshot::Sender<ProviderResult<Option<CachedRevmBal>>>;
65
66type BlockLruCache<B, L> =
67    MultiConsumerLruCache<B256, Arc<RecoveredBlock<B>>, L, BlockWithSendersResponseSender<B>>;
68
69type ReceiptsLruCache<R, L> =
70    MultiConsumerLruCache<B256, Arc<Vec<R>>, L, ReceiptsResponseSender<R>>;
71
72type HeaderLruCache<H, L> = MultiConsumerLruCache<B256, H, L, HeaderResponseSender<H>>;
73
74type BalLruCache<L> = MultiConsumerLruCache<B256, CachedRevmBal, L, BalResponseSender>;
75
76/// Provides async access to cached eth data
77///
78/// This is the frontend for the async caching service which manages cached data on a different
79/// task.
80#[derive(Debug)]
81pub struct EthStateCache<N: NodePrimitives> {
82    to_service: UnboundedSender<CacheAction<N::Block, N::Receipt>>,
83}
84
85impl<N: NodePrimitives> Clone for EthStateCache<N> {
86    fn clone(&self) -> Self {
87        Self { to_service: self.to_service.clone() }
88    }
89}
90
91impl<N: NodePrimitives> EthStateCache<N> {
92    /// Creates and returns both [`EthStateCache`] frontend and the memory bound service.
93    fn create<Provider>(
94        provider: Provider,
95        action_task_spawner: Runtime,
96        config: EthStateCacheConfig,
97    ) -> (Self, EthStateCacheService<Provider, Runtime>)
98    where
99        Provider: BlockReader<Block = N::Block, Receipt = N::Receipt> + BalProvider,
100    {
101        let EthStateCacheConfig {
102            max_blocks,
103            max_receipts,
104            max_headers,
105            max_bals,
106            max_concurrent_db_requests,
107            max_cached_tx_hashes,
108        } = config;
109        let (to_service, rx) = unbounded_channel();
110
111        let service = EthStateCacheService {
112            provider,
113            full_block_cache: BlockLruCache::new(max_blocks, "blocks"),
114            receipts_cache: ReceiptsLruCache::new(max_receipts, "receipts"),
115            headers_cache: HeaderLruCache::new(max_headers, "headers"),
116            bal_cache: BalLruCache::new(max_bals, "bals"),
117            action_tx: to_service.clone(),
118            action_rx: UnboundedReceiverStream::new(rx),
119            action_task_spawner,
120            rate_limiter: Arc::new(Semaphore::new(max_concurrent_db_requests)),
121            tx_hash_index: LruMap::new(ByLength::new(max_cached_tx_hashes)),
122        };
123        let cache = Self { to_service };
124        (cache, service)
125    }
126
127    /// Creates a new async LRU backed cache service task and spawns it to a new task via the given
128    /// spawner.
129    ///
130    /// The cache is memory limited by the given max bytes values.
131    pub fn spawn_with<Provider>(
132        provider: Provider,
133        config: EthStateCacheConfig,
134        executor: Runtime,
135    ) -> Self
136    where
137        Provider: BlockReader<Block = N::Block, Receipt = N::Receipt>
138            + BalProvider
139            + Clone
140            + Unpin
141            + 'static,
142    {
143        let (this, service) = Self::create(provider, executor.clone(), config);
144        executor.spawn_critical_task("eth state cache", service);
145        this
146    }
147
148    /// Requests the  [`RecoveredBlock`] for the block hash
149    ///
150    /// Returns `None` if the block does not exist.
151    pub async fn get_recovered_block(
152        &self,
153        block_hash: B256,
154    ) -> ProviderResult<Option<Arc<RecoveredBlock<N::Block>>>> {
155        let (response_tx, rx) = oneshot::channel();
156        let _ = self.to_service.send(CacheAction::GetBlockWithSenders { block_hash, response_tx });
157        rx.await.map_err(|_| CacheServiceUnavailable)?
158    }
159
160    /// Requests the block for the given block hash if it is cached.
161    pub async fn get_maybe_block(
162        &self,
163        block_hash: B256,
164    ) -> ProviderResult<Option<Arc<RecoveredBlock<N::Block>>>> {
165        let (response_tx, rx) = oneshot::channel();
166        let _ = self.to_service.send(CacheAction::GetCachedBlock { block_hash, response_tx });
167        rx.await.map_err(|_| CacheServiceUnavailable.into())
168    }
169
170    /// Requests the receipts for the block hash
171    ///
172    /// Returns `None` if the block was not found.
173    pub async fn get_receipts(
174        &self,
175        block_hash: B256,
176    ) -> ProviderResult<Option<Arc<Vec<N::Receipt>>>> {
177        let (response_tx, rx) = oneshot::channel();
178        let _ = self.to_service.send(CacheAction::GetReceipts { block_hash, response_tx });
179        rx.await.map_err(|_| CacheServiceUnavailable)?
180    }
181
182    /// Fetches both receipts and block for the given block hash.
183    pub async fn get_block_and_receipts(
184        &self,
185        block_hash: B256,
186    ) -> ProviderResult<Option<(Arc<RecoveredBlock<N::Block>>, Arc<Vec<N::Receipt>>)>> {
187        let block = self.get_recovered_block(block_hash);
188        let receipts = self.get_receipts(block_hash);
189
190        let (block, receipts) = futures::try_join!(block, receipts)?;
191
192        Ok(block.zip(receipts))
193    }
194
195    /// Retrieves receipts and blocks from cache if block is in the cache, otherwise only receipts.
196    pub async fn get_receipts_and_maybe_block(
197        &self,
198        block_hash: B256,
199    ) -> ProviderResult<Option<(Arc<Vec<N::Receipt>>, Option<Arc<RecoveredBlock<N::Block>>>)>> {
200        let (response_tx, rx) = oneshot::channel();
201        let _ = self.to_service.send(CacheAction::GetCachedBlock { block_hash, response_tx });
202
203        let receipts = self.get_receipts(block_hash);
204
205        let (receipts, block) = futures::join!(receipts, rx);
206
207        let block = block.map_err(|_| CacheServiceUnavailable)?;
208        Ok(receipts?.map(|r| (r, block)))
209    }
210
211    /// Retrieves both block and receipts from cache if available.
212    pub async fn maybe_cached_block_and_receipts(
213        &self,
214        block_hash: B256,
215    ) -> ProviderResult<(Option<Arc<RecoveredBlock<N::Block>>>, Option<Arc<Vec<N::Receipt>>>)> {
216        let (response_tx, rx) = oneshot::channel();
217        let _ = self
218            .to_service
219            .send(CacheAction::GetCachedBlockAndReceipts { block_hash, response_tx });
220        rx.await.map_err(|_| CacheServiceUnavailable.into())
221    }
222
223    /// Streams cached receipts and blocks for a list of block hashes, preserving input order.
224    #[expect(clippy::type_complexity)]
225    pub fn get_receipts_and_maybe_block_stream<'a>(
226        &'a self,
227        hashes: Vec<B256>,
228    ) -> impl Stream<
229        Item = ProviderResult<
230            Option<(Arc<Vec<N::Receipt>>, Option<Arc<RecoveredBlock<N::Block>>>)>,
231        >,
232    > + 'a {
233        let futures = hashes.into_iter().map(move |hash| self.get_receipts_and_maybe_block(hash));
234
235        futures.collect::<FuturesOrdered<_>>()
236    }
237
238    /// Requests the header for the given hash.
239    ///
240    /// Returns an error if the header is not found.
241    pub async fn get_header(&self, block_hash: B256) -> ProviderResult<N::BlockHeader> {
242        let (response_tx, rx) = oneshot::channel();
243        let _ = self.to_service.send(CacheAction::GetHeader { block_hash, response_tx });
244        rx.await.map_err(|_| CacheServiceUnavailable)?
245    }
246
247    /// Retrieves a chain of connected blocks from the cache, starting from the given block hash
248    /// and traversing down through parent hashes. Returns blocks in descending order (newest
249    /// first).
250    /// This is useful for efficiently retrieving a sequence of blocks that might already be in
251    /// cache without making separate database requests.
252    /// Returns `None` if no blocks are found in the cache, otherwise returns `Some(Vec<...>)`
253    /// with at least one block.
254    pub async fn get_cached_parent_blocks(
255        &self,
256        block_hash: B256,
257        max_blocks: usize,
258    ) -> Option<Vec<Arc<RecoveredBlock<N::Block>>>> {
259        let (response_tx, rx) = oneshot::channel();
260        let _ = self.to_service.send(CacheAction::GetCachedParentBlocks {
261            block_hash,
262            max_blocks,
263            response_tx,
264        });
265
266        let blocks = rx.await.unwrap_or_default();
267        if blocks.is_empty() {
268            None
269        } else {
270            Some(blocks)
271        }
272    }
273
274    /// Looks up a transaction by its hash in the cache index.
275    ///
276    /// Returns the cached block, transaction index, and optionally receipts if the transaction
277    /// is in a cached block.
278    pub async fn get_transaction_by_hash(
279        &self,
280        tx_hash: TxHash,
281    ) -> Option<CachedTransaction<N::Block, N::Receipt>> {
282        let (response_tx, rx) = oneshot::channel();
283        let _ = self.to_service.send(CacheAction::GetTransactionByHash { tx_hash, response_tx });
284        rx.await.ok()?
285    }
286
287    /// Requests the revm BAL for the block hash.
288    ///
289    /// Returns `None` if the BAL does not exist.
290    pub async fn get_bal(
291        &self,
292        block_hash: B256,
293    ) -> ProviderResult<Option<Arc<DecodedBal<Arc<RevmBal>>>>> {
294        let (response_tx, rx) = oneshot::channel();
295        let _ = self.to_service.send(CacheAction::GetBal { block_hash, response_tx });
296        rx.await
297            .map_err(|_| CacheServiceUnavailable)?
298            .map(|maybe_bal| maybe_bal.map(|cached| cached.0))
299    }
300}
301/// Thrown when the cache service task dropped.
302#[derive(Debug, thiserror::Error)]
303#[error("cache service task stopped")]
304pub struct CacheServiceUnavailable;
305
306impl From<CacheServiceUnavailable> for ProviderError {
307    fn from(err: CacheServiceUnavailable) -> Self {
308        Self::other(err)
309    }
310}
311
312/// A task that manages caches for data required by the `eth` rpc implementation.
313///
314/// It provides a caching layer on top of the given
315/// [`StateProvider`](reth_storage_api::StateProvider) and keeps data fetched via the provider in
316/// memory in an LRU cache. If the requested data is missing in the cache it is fetched and inserted
317/// into the cache afterwards. While fetching data from disk is sync, this service is async since
318/// requests and data is shared via channels.
319///
320/// This type is an endless future that listens for incoming messages from the user facing
321/// [`EthStateCache`] via a channel. If the requested data is not cached then it spawns a new task
322/// that does the IO and sends the result back to it. This way the caching service only
323/// handles messages and does LRU lookups and never blocking IO.
324///
325/// Caution: The channel for the data is _unbounded_ it is assumed that this is mainly used by the
326/// `reth_rpc::EthApi` which is typically invoked by the RPC server, which already uses
327/// permits to limit concurrent requests.
328#[must_use = "Type does nothing unless spawned"]
329pub(crate) struct EthStateCacheService<
330    Provider,
331    Tasks,
332    LimitBlocks = ByLength,
333    LimitReceipts = ByLength,
334    LimitHeaders = ByLength,
335    LimitBals = ByLength,
336> where
337    Provider: BlockReader + BalProvider,
338    LimitBlocks: Limiter<B256, Arc<RecoveredBlock<Provider::Block>>>,
339    LimitReceipts: Limiter<B256, Arc<Vec<Provider::Receipt>>>,
340    LimitHeaders: Limiter<B256, Provider::Header>,
341    LimitBals: Limiter<B256, CachedRevmBal>,
342{
343    /// The type used to lookup data from disk
344    provider: Provider,
345    /// The LRU cache for full blocks grouped by their block hash.
346    full_block_cache: BlockLruCache<Provider::Block, LimitBlocks>,
347    /// The LRU cache for block receipts grouped by the block hash.
348    receipts_cache: ReceiptsLruCache<Provider::Receipt, LimitReceipts>,
349    /// The LRU cache for headers.
350    ///
351    /// Headers are cached because they are required to populate the environment for execution
352    /// (evm).
353    headers_cache: HeaderLruCache<Provider::Header, LimitHeaders>,
354    /// The LRU cache for revm BALs grouped by the block hash.
355    bal_cache: BalLruCache<LimitBals>,
356    /// Sender half of the action channel.
357    action_tx: UnboundedSender<CacheAction<Provider::Block, Provider::Receipt>>,
358    /// Receiver half of the action channel.
359    action_rx: UnboundedReceiverStream<CacheAction<Provider::Block, Provider::Receipt>>,
360    /// The type that's used to spawn tasks that do the actual work
361    action_task_spawner: Tasks,
362    /// Rate limiter for spawned fetch tasks.
363    ///
364    /// This restricts the max concurrent fetch tasks at the same time.
365    rate_limiter: Arc<Semaphore>,
366    /// LRU index mapping transaction hashes to their block hash and index within the block.
367    tx_hash_index: LruMap<TxHash, (B256, usize), ByLength>,
368}
369
370impl<Provider> EthStateCacheService<Provider, Runtime>
371where
372    Provider: BlockReader + BalProvider + Clone + Unpin + 'static,
373{
374    /// Indexes all transactions in a block by transaction hash.
375    fn index_block_transactions(&mut self, block: &RecoveredBlock<Provider::Block>) {
376        let block_hash = block.hash();
377        for (tx_idx, tx) in block.body().transactions().iter().enumerate() {
378            self.tx_hash_index.insert(*tx.tx_hash(), (block_hash, tx_idx));
379        }
380    }
381
382    /// Removes transaction index entries for a reorged block.
383    fn remove_block_transactions(&mut self, block: &RecoveredBlock<Provider::Block>) {
384        for tx in block.body().transactions() {
385            self.tx_hash_index.remove(tx.tx_hash());
386        }
387    }
388
389    fn on_new_block(
390        &mut self,
391        block_hash: B256,
392        res: ProviderResult<Option<Arc<RecoveredBlock<Provider::Block>>>>,
393    ) {
394        if let Some(queued) = self.full_block_cache.remove(&block_hash) {
395            // send the response to queued senders
396            for tx in queued {
397                let _ = tx.send(res.clone());
398            }
399        }
400
401        // cache good block
402        if let Ok(Some(block)) = res {
403            self.full_block_cache.insert(block_hash, block);
404        }
405    }
406
407    fn on_new_receipts(
408        &mut self,
409        block_hash: B256,
410        res: ProviderResult<Option<Arc<Vec<Provider::Receipt>>>>,
411    ) {
412        if let Some(queued) = self.receipts_cache.remove(&block_hash) {
413            // send the response to queued senders
414            for tx in queued {
415                let _ = tx.send(res.clone());
416            }
417        }
418
419        // cache good receipts
420        if let Ok(Some(receipts)) = res {
421            self.receipts_cache.insert(block_hash, receipts);
422        }
423    }
424
425    fn on_new_bal(&mut self, block_hash: B256, res: ProviderResult<Option<CachedRevmBal>>) {
426        if let Some(queued) = self.bal_cache.remove(&block_hash) {
427            for tx in queued {
428                let _ = tx.send(res.clone());
429            }
430        }
431
432        if let Ok(Some(bal)) = res {
433            self.bal_cache.insert(block_hash, bal);
434        }
435    }
436
437    fn on_reorg_block(
438        &mut self,
439        block_hash: B256,
440        res: ProviderResult<Option<Arc<RecoveredBlock<Provider::Block>>>>,
441    ) {
442        if let Some(queued) = self.full_block_cache.remove(&block_hash) {
443            // send the response to queued senders
444            for tx in queued {
445                let _ = tx.send(res.clone());
446            }
447        }
448    }
449
450    fn on_reorg_receipts(
451        &mut self,
452        block_hash: B256,
453        res: ProviderResult<Option<Arc<Vec<Provider::Receipt>>>>,
454    ) {
455        if let Some(queued) = self.receipts_cache.remove(&block_hash) {
456            // send the response to queued senders
457            for tx in queued {
458                let _ = tx.send(res.clone());
459            }
460        }
461    }
462
463    fn on_reorg_header(&mut self, block_hash: B256, res: ProviderResult<Provider::Header>) {
464        if let Some(queued) = self.headers_cache.remove(&block_hash) {
465            // send the response to queued senders
466            for tx in queued {
467                let _ = tx.send(res.clone());
468            }
469        }
470    }
471
472    fn on_reorg_bal(&mut self, block_hash: B256, res: ProviderResult<Option<CachedRevmBal>>) {
473        if let Some(queued) = self.bal_cache.remove(&block_hash) {
474            for tx in queued {
475                let _ = tx.send(res.clone());
476            }
477        }
478    }
479
480    /// Shrinks the queues but leaves some space for the next requests
481    fn shrink_queues(&mut self) {
482        let min_capacity = 2;
483        self.full_block_cache.shrink_to(min_capacity);
484        self.receipts_cache.shrink_to(min_capacity);
485        self.headers_cache.shrink_to(min_capacity);
486        self.bal_cache.shrink_to(min_capacity);
487    }
488
489    fn update_cached_metrics(&self) {
490        self.full_block_cache.update_cached_metrics();
491        self.receipts_cache.update_cached_metrics();
492        self.headers_cache.update_cached_metrics();
493        self.bal_cache.update_cached_metrics();
494    }
495}
496
497impl<Provider> Future for EthStateCacheService<Provider, Runtime>
498where
499    Provider: BlockReader + BalProvider + Clone + Unpin + 'static,
500{
501    type Output = ();
502
503    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
504        let this = self.get_mut();
505
506        loop {
507            let Poll::Ready(action) = this.action_rx.poll_next_unpin(cx) else {
508                // shrink queues if we don't have any work to do
509                this.shrink_queues();
510                return Poll::Pending;
511            };
512
513            match action {
514                None => {
515                    unreachable!("can't close")
516                }
517                Some(action) => {
518                    match action {
519                        CacheAction::GetCachedBlock { block_hash, response_tx } => {
520                            let _ =
521                                response_tx.send(this.full_block_cache.get(&block_hash).cloned());
522                        }
523                        CacheAction::GetCachedBlockAndReceipts { block_hash, response_tx } => {
524                            let block = this.full_block_cache.get(&block_hash).cloned();
525                            let receipts = this.receipts_cache.get(&block_hash).cloned();
526                            let _ = response_tx.send((block, receipts));
527                        }
528                        CacheAction::GetBlockWithSenders { block_hash, response_tx } => {
529                            if let Some(block) = this.full_block_cache.get(&block_hash).cloned() {
530                                let _ = response_tx.send(Ok(Some(block)));
531                                continue
532                            }
533
534                            // block is not in the cache, request it if this is the first consumer
535                            if this.full_block_cache.queue(block_hash, response_tx) {
536                                let provider = this.provider.clone();
537                                let action_tx = this.action_tx.clone();
538                                let rate_limiter = this.rate_limiter.clone();
539                                let mut action_sender =
540                                    ActionSender::new(CacheKind::Block, block_hash, action_tx);
541                                this.action_task_spawner.spawn_blocking_task(async move {
542                                    // Acquire permit
543                                    let _permit = rate_limiter.acquire().await;
544                                    // Only look in the database to prevent situations where we
545                                    // looking up the tree is blocking
546                                    let block_sender = provider
547                                        .sealed_block_with_senders(
548                                            BlockHashOrNumber::Hash(block_hash),
549                                            TransactionVariant::WithHash,
550                                        )
551                                        .map(|maybe_block| maybe_block.map(Arc::new));
552                                    action_sender.send_block(block_sender);
553                                });
554                            }
555                        }
556                        CacheAction::GetReceipts { block_hash, response_tx } => {
557                            // check if block is cached
558                            if let Some(receipts) = this.receipts_cache.get(&block_hash).cloned() {
559                                let _ = response_tx.send(Ok(Some(receipts)));
560                                continue
561                            }
562
563                            // block is not in the cache, request it if this is the first consumer
564                            if this.receipts_cache.queue(block_hash, response_tx) {
565                                let provider = this.provider.clone();
566                                let action_tx = this.action_tx.clone();
567                                let rate_limiter = this.rate_limiter.clone();
568                                let mut action_sender =
569                                    ActionSender::new(CacheKind::Receipt, block_hash, action_tx);
570                                this.action_task_spawner.spawn_blocking_task(async move {
571                                    // Acquire permit
572                                    let _permit = rate_limiter.acquire().await;
573                                    let res = provider
574                                        .receipts_by_block(block_hash.into())
575                                        .map(|maybe_receipts| maybe_receipts.map(Arc::new));
576
577                                    action_sender.send_receipts(res);
578                                });
579                            }
580                        }
581                        CacheAction::GetHeader { block_hash, response_tx } => {
582                            // check if the header is cached
583                            if let Some(header) = this.headers_cache.get(&block_hash).cloned() {
584                                let _ = response_tx.send(Ok(header));
585                                continue
586                            }
587
588                            // it's possible we have the entire block cached
589                            if let Some(block) = this.full_block_cache.get(&block_hash) {
590                                let _ = response_tx.send(Ok(block.clone_header()));
591                                continue
592                            }
593
594                            // header is not in the cache, request it if this is the first
595                            // consumer
596                            if this.headers_cache.queue(block_hash, response_tx) {
597                                let provider = this.provider.clone();
598                                let action_tx = this.action_tx.clone();
599                                let rate_limiter = this.rate_limiter.clone();
600                                let mut action_sender =
601                                    ActionSender::new(CacheKind::Header, block_hash, action_tx);
602                                this.action_task_spawner.spawn_blocking_task(async move {
603                                    // Acquire permit
604                                    let _permit = rate_limiter.acquire().await;
605                                    let header = provider.header(block_hash).and_then(|header| {
606                                        header.ok_or_else(|| {
607                                            ProviderError::HeaderNotFound(block_hash.into())
608                                        })
609                                    });
610                                    action_sender.send_header(header);
611                                });
612                            }
613                        }
614                        CacheAction::GetBal { block_hash, response_tx } => {
615                            if let Some(bal) = this.bal_cache.get(&block_hash).cloned() {
616                                let _ = response_tx.send(Ok(Some(bal)));
617                                continue
618                            }
619
620                            if this.bal_cache.queue(block_hash, response_tx) {
621                                let provider = this.provider.clone();
622                                let action_tx = this.action_tx.clone();
623                                let rate_limiter = this.rate_limiter.clone();
624                                let mut action_sender =
625                                    ActionSender::new(CacheKind::Bal, block_hash, action_tx);
626                                this.action_task_spawner.spawn_blocking_task(async move {
627                                    let _permit = rate_limiter.acquire().await;
628                                    let res = provider.get_bal_by_hash(block_hash).and_then(
629                                        |maybe_bal| {
630                                            maybe_bal.map(CachedRevmBal::try_from_raw).transpose()
631                                        },
632                                    );
633                                    action_sender.send_bal(res);
634                                });
635                            }
636                        }
637                        CacheAction::ReceiptsResult { block_hash, res } => {
638                            this.on_new_receipts(block_hash, res);
639                        }
640                        CacheAction::BalResult { block_hash, res } => {
641                            this.on_new_bal(block_hash, res);
642                        }
643                        CacheAction::BlockWithSendersResult { block_hash, res } => match res {
644                            Ok(Some(block_with_senders)) => {
645                                this.on_new_block(block_hash, Ok(Some(block_with_senders)));
646                            }
647                            Ok(None) => {
648                                this.on_new_block(block_hash, Ok(None));
649                            }
650                            Err(e) => {
651                                this.on_new_block(block_hash, Err(e));
652                            }
653                        },
654                        CacheAction::HeaderResult { block_hash, res } => {
655                            let res = *res;
656                            if let Some(queued) = this.headers_cache.remove(&block_hash) {
657                                // send the response to queued senders
658                                for tx in queued {
659                                    let _ = tx.send(res.clone());
660                                }
661                            }
662
663                            // cache good header
664                            if let Ok(data) = res {
665                                this.headers_cache.insert(block_hash, data);
666                            }
667                        }
668                        CacheAction::CacheNewCanonicalChain { chain_change } => {
669                            for block in chain_change.blocks {
670                                // Index transactions before caching the block
671                                this.index_block_transactions(&block);
672                                this.on_new_block(block.hash(), Ok(Some(block)));
673                            }
674
675                            for block_receipts in chain_change.receipts {
676                                this.on_new_receipts(
677                                    block_receipts.block_hash,
678                                    Ok(Some(block_receipts.receipts)),
679                                );
680                            }
681                        }
682                        CacheAction::RemoveReorgedChain { chain_change } => {
683                            for block in chain_change.blocks {
684                                let block_hash = block.hash();
685                                let header = block.clone_header();
686                                // Remove transaction index entries for reorged blocks
687                                this.remove_block_transactions(&block);
688                                this.on_reorg_block(block_hash, Ok(Some(block)));
689                                this.on_reorg_header(block_hash, Ok(header));
690                                this.on_reorg_bal(block_hash, Ok(None));
691                            }
692
693                            for block_receipts in chain_change.receipts {
694                                this.on_reorg_receipts(
695                                    block_receipts.block_hash,
696                                    Ok(Some(block_receipts.receipts)),
697                                );
698                            }
699                        }
700                        CacheAction::GetCachedParentBlocks {
701                            block_hash,
702                            max_blocks,
703                            response_tx,
704                        } => {
705                            let mut blocks = Vec::new();
706                            let mut current_hash = block_hash;
707
708                            // Start with the requested block
709                            while blocks.len() < max_blocks {
710                                if let Some(block) =
711                                    this.full_block_cache.get(&current_hash).cloned()
712                                {
713                                    // Get the parent hash for the next iteration
714                                    current_hash = block.header().parent_hash();
715                                    blocks.push(block);
716                                } else {
717                                    // Break the loop if we can't find the current block
718                                    break;
719                                }
720                            }
721
722                            let _ = response_tx.send(blocks);
723                        }
724                        CacheAction::GetTransactionByHash { tx_hash, response_tx } => {
725                            let result =
726                                this.tx_hash_index.get(&tx_hash).and_then(|(block_hash, idx)| {
727                                    let block = this.full_block_cache.get(block_hash).cloned()?;
728                                    let receipts = this.receipts_cache.get(block_hash).cloned();
729                                    Some(CachedTransaction::new(block, *idx, receipts))
730                                });
731                            let _ = response_tx.send(result);
732                        }
733                    };
734                    this.update_cached_metrics();
735                }
736            }
737        }
738    }
739}
740
741/// All message variants sent through the channel
742enum CacheAction<B: Block, R> {
743    GetBlockWithSenders {
744        block_hash: B256,
745        response_tx: BlockWithSendersResponseSender<B>,
746    },
747    GetHeader {
748        block_hash: B256,
749        response_tx: HeaderResponseSender<B::Header>,
750    },
751    GetReceipts {
752        block_hash: B256,
753        response_tx: ReceiptsResponseSender<R>,
754    },
755    GetBal {
756        block_hash: B256,
757        response_tx: BalResponseSender,
758    },
759    GetCachedBlock {
760        block_hash: B256,
761        response_tx: CachedBlockResponseSender<B>,
762    },
763    GetCachedBlockAndReceipts {
764        block_hash: B256,
765        response_tx: CachedBlockAndReceiptsResponseSender<B, R>,
766    },
767    BlockWithSendersResult {
768        block_hash: B256,
769        res: ProviderResult<Option<Arc<RecoveredBlock<B>>>>,
770    },
771    ReceiptsResult {
772        block_hash: B256,
773        res: ProviderResult<Option<Arc<Vec<R>>>>,
774    },
775    HeaderResult {
776        block_hash: B256,
777        res: Box<ProviderResult<B::Header>>,
778    },
779    BalResult {
780        block_hash: B256,
781        res: ProviderResult<Option<CachedRevmBal>>,
782    },
783    CacheNewCanonicalChain {
784        chain_change: ChainChange<B, R>,
785    },
786    RemoveReorgedChain {
787        chain_change: ChainChange<B, R>,
788    },
789    GetCachedParentBlocks {
790        block_hash: B256,
791        max_blocks: usize,
792        response_tx: CachedParentBlocksResponseSender<B>,
793    },
794    /// Look up a transaction's cached data by its hash
795    GetTransactionByHash {
796        tx_hash: TxHash,
797        response_tx: TransactionHashResponseSender<B, R>,
798    },
799}
800
801struct BlockReceipts<R> {
802    block_hash: B256,
803    receipts: Arc<Vec<R>>,
804}
805
806/// A change of the canonical chain
807struct ChainChange<B: Block, R> {
808    blocks: Vec<Arc<RecoveredBlock<B>>>,
809    receipts: Vec<BlockReceipts<R>>,
810}
811
812impl<B: Block, R: Clone> ChainChange<B, R> {
813    fn new<N>(chain: Arc<Chain<N>>) -> Self
814    where
815        N: NodePrimitives<Block = B, Receipt = R>,
816    {
817        let (blocks, receipts): (Vec<_>, Vec<_>) = chain
818            .blocks_and_receipts()
819            .map(|(block, receipts)| {
820                let block_receipts = BlockReceipts {
821                    block_hash: block.hash(),
822                    receipts: Arc::new(receipts.clone()),
823                };
824                (Arc::clone(block), block_receipts)
825            })
826            .unzip();
827        Self { blocks, receipts }
828    }
829}
830
831/// Identifier for the caches.
832#[derive(Copy, Clone, Debug)]
833enum CacheKind {
834    Block,
835    Receipt,
836    Header,
837    Bal,
838}
839
840/// Drop aware sender struct that ensures a response is always emitted even if the db task panics
841/// before a result could be sent.
842///
843/// This type wraps a sender and in case the sender is still present on drop emit an error response.
844#[derive(Debug)]
845struct ActionSender<B: Block, R: Send + Sync> {
846    kind: CacheKind,
847    blockhash: B256,
848    tx: Option<UnboundedSender<CacheAction<B, R>>>,
849}
850
851impl<R: Send + Sync, B: Block> ActionSender<B, R> {
852    const fn new(kind: CacheKind, blockhash: B256, tx: UnboundedSender<CacheAction<B, R>>) -> Self {
853        Self { kind, blockhash, tx: Some(tx) }
854    }
855
856    fn send_block(&mut self, block_sender: Result<Option<Arc<RecoveredBlock<B>>>, ProviderError>) {
857        if let Some(tx) = self.tx.take() {
858            let _ = tx.send(CacheAction::BlockWithSendersResult {
859                block_hash: self.blockhash,
860                res: block_sender,
861            });
862        }
863    }
864
865    fn send_receipts(&mut self, receipts: Result<Option<Arc<Vec<R>>>, ProviderError>) {
866        if let Some(tx) = self.tx.take() {
867            let _ =
868                tx.send(CacheAction::ReceiptsResult { block_hash: self.blockhash, res: receipts });
869        }
870    }
871
872    fn send_header(&mut self, header: Result<<B as Block>::Header, ProviderError>) {
873        if let Some(tx) = self.tx.take() {
874            let _ = tx.send(CacheAction::HeaderResult {
875                block_hash: self.blockhash,
876                res: Box::new(header),
877            });
878        }
879    }
880
881    fn send_bal(&mut self, bal: Result<Option<CachedRevmBal>, ProviderError>) {
882        if let Some(tx) = self.tx.take() {
883            let _ = tx.send(CacheAction::BalResult { block_hash: self.blockhash, res: bal });
884        }
885    }
886}
887impl<R: Send + Sync, B: Block> Drop for ActionSender<B, R> {
888    fn drop(&mut self) {
889        if let Some(tx) = self.tx.take() {
890            let msg = match self.kind {
891                CacheKind::Block => CacheAction::BlockWithSendersResult {
892                    block_hash: self.blockhash,
893                    res: Err(CacheServiceUnavailable.into()),
894                },
895                CacheKind::Receipt => CacheAction::ReceiptsResult {
896                    block_hash: self.blockhash,
897                    res: Err(CacheServiceUnavailable.into()),
898                },
899                CacheKind::Header => CacheAction::HeaderResult {
900                    block_hash: self.blockhash,
901                    res: Box::new(Err(CacheServiceUnavailable.into())),
902                },
903                CacheKind::Bal => CacheAction::BalResult {
904                    block_hash: self.blockhash,
905                    res: Err(CacheServiceUnavailable.into()),
906                },
907            };
908            let _ = tx.send(msg);
909        }
910    }
911}
912
913/// Awaits for new chain events and directly inserts them into the cache so they're available
914/// immediately before they need to be fetched from disk.
915///
916/// Reorged blocks are removed from the cache.
917pub async fn cache_new_blocks_task<St, N: NodePrimitives>(
918    eth_state_cache: EthStateCache<N>,
919    mut events: St,
920) where
921    St: Stream<Item = CanonStateNotification<N>> + Unpin + 'static,
922{
923    while let Some(event) = events.next().await {
924        if let Some(reverted) = event.reverted() {
925            let chain_change = ChainChange::new(reverted);
926
927            let _ =
928                eth_state_cache.to_service.send(CacheAction::RemoveReorgedChain { chain_change });
929        }
930
931        let chain_change = ChainChange::new(event.committed());
932
933        let _ =
934            eth_state_cache.to_service.send(CacheAction::CacheNewCanonicalChain { chain_change });
935    }
936}
937
938/// Cached decoded revm BAL.
939#[derive(Clone, Debug)]
940pub(crate) struct CachedRevmBal(Arc<DecodedBal<Arc<RevmBal>>>);
941
942impl CachedRevmBal {
943    /// Creates a cached revm BAL from an owned decoded BAL.
944    #[inline]
945    fn new(bal: DecodedBal<Arc<RevmBal>>) -> Self {
946        Self(Arc::new(bal))
947    }
948
949    /// Decodes raw BAL bytes into the representation used by revm.
950    fn try_from_raw(raw: Bytes) -> ProviderResult<Self> {
951        DecodedBal::from_rlp_bytes(raw)
952            .map_err(Into::into)
953            .and_then(|decoded| {
954                decoded.try_map(|bal| {
955                    RevmBal::try_from(Vec::from(bal)).map(Arc::new).map_err(ProviderError::other)
956                })
957            })
958            .map(Self::new)
959    }
960}
961
962impl InMemorySize for CachedRevmBal {
963    fn size(&self) -> usize {
964        core::mem::size_of::<Self>() + decoded_revm_bal_size(&self.0)
965    }
966}
967
968fn decoded_revm_bal_size(bal: &DecodedBal<Arc<RevmBal>>) -> usize {
969    core::mem::size_of::<DecodedBal<Arc<RevmBal>>>() +
970        bal.as_raw().len() +
971        revm_bal_size(bal.as_bal())
972}
973
974fn revm_bal_size(bal: &Arc<RevmBal>) -> usize {
975    core::mem::size_of::<RevmBal>() +
976        bal.accounts.capacity() * core::mem::size_of::<(Address, RevmAccountBal)>() +
977        bal.accounts.values().map(revm_account_bal_heap_size).sum::<usize>()
978}
979
980fn revm_account_bal_heap_size(account: &RevmAccountBal) -> usize {
981    revm_account_info_bal_heap_size(&account.account_info) +
982        revm_storage_bal_heap_size(&account.storage)
983}
984
985fn revm_account_info_bal_heap_size(account_info: &RevmAccountInfoBal) -> usize {
986    revm_bal_writes_heap_size(&account_info.nonce, |_| 0) +
987        revm_bal_writes_heap_size(&account_info.balance, |_| 0) +
988        revm_bal_writes_heap_size(&account_info.code, revm_code_write_heap_size)
989}
990
991fn revm_storage_bal_heap_size(storage: &RevmStorageBal) -> usize {
992    storage.storage.len() * core::mem::size_of::<(StorageKey, RevmBalWrites<StorageValue>)>() +
993        storage
994            .storage
995            .values()
996            .map(|writes| revm_bal_writes_heap_size(writes, |_| 0))
997            .sum::<usize>()
998}
999
1000fn revm_bal_writes_heap_size<T, F>(writes: &RevmBalWrites<T>, mut item_heap_size: F) -> usize
1001where
1002    T: PartialEq + Clone,
1003    F: FnMut(&T) -> usize,
1004{
1005    writes.writes.capacity() * core::mem::size_of::<(u64, T)>() +
1006        writes.writes.iter().map(|(_, item)| item_heap_size(item)).sum::<usize>()
1007}
1008
1009fn revm_code_write_heap_size((_, bytecode): &(B256, Bytecode)) -> usize {
1010    bytecode.bytes_ref().len()
1011}
1012
1013#[cfg(test)]
1014mod tests {
1015    use super::*;
1016    use alloy_consensus::{transaction::TransactionMeta, Header};
1017    use alloy_eip7928::BlockAccessIndex;
1018    use alloy_eips::{BlockHashOrNumber, NumHash};
1019    use alloy_primitives::{Address, BlockHash, BlockNumber, Bytes, Signature, TxHash, TxNumber};
1020    use core::ops::{RangeBounds, RangeInclusive};
1021    use reth_db_models::StoredBlockBodyIndices;
1022    use reth_ethereum_primitives::{
1023        Block, BlockBody, EthPrimitives, Receipt, Transaction, TransactionSigned,
1024    };
1025    use reth_primitives_traits::{RecoveredBlock, SealedHeader};
1026    use reth_storage_api::{
1027        noop::NoopProvider, BalProvider, BalStore, BalStoreHandle, BlockBodyIndicesProvider,
1028        BlockHashReader, BlockNumReader, BlockReader, BlockSource, HeaderProvider, ReceiptProvider,
1029        TransactionVariant, TransactionsProvider,
1030    };
1031    use std::sync::atomic::{AtomicUsize, Ordering};
1032
1033    fn test_service() -> EthStateCacheService<NoopProvider, Runtime> {
1034        let (_cache, service) = EthStateCache::<EthPrimitives>::create(
1035            NoopProvider::default(),
1036            Runtime::test(),
1037            EthStateCacheConfig {
1038                max_blocks: 4,
1039                max_receipts: 4,
1040                max_headers: 4,
1041                max_bals: 4,
1042                max_concurrent_db_requests: 1,
1043                max_cached_tx_hashes: 16,
1044            },
1045        );
1046        service
1047    }
1048
1049    fn test_decoded_revm_bal() -> DecodedBal<Arc<RevmBal>> {
1050        DecodedBal::new(Arc::new(RevmBal::default()), Bytes::from_static(&[0xc0]))
1051    }
1052
1053    fn test_block() -> RecoveredBlock<Block> {
1054        RecoveredBlock::new_unhashed(
1055            Block {
1056                header: Header { number: 1, ..Default::default() },
1057                body: BlockBody {
1058                    transactions: vec![TransactionSigned::new_unhashed(
1059                        Transaction::Legacy(Default::default()),
1060                        Signature::test_signature(),
1061                    )],
1062                    ..Default::default()
1063                },
1064            },
1065            vec![Address::ZERO],
1066        )
1067    }
1068
1069    #[test]
1070    fn reorg_evicts_cached_headers() {
1071        let mut service = test_service();
1072        let block_hash = B256::repeat_byte(0x11);
1073
1074        assert!(service
1075            .headers_cache
1076            .insert(block_hash, Header { number: 42, ..Default::default() }));
1077        assert!(service.headers_cache.get(&block_hash).is_some());
1078
1079        service.on_reorg_header(block_hash, Ok(Header { number: 7, ..Default::default() }));
1080
1081        assert!(service.headers_cache.get(&block_hash).is_none());
1082    }
1083
1084    #[test]
1085    fn reorg_forwards_header_to_queued_requests() {
1086        let mut service = test_service();
1087        let block_hash = B256::repeat_byte(0x22);
1088        let (response_tx, mut response_rx) = oneshot::channel();
1089        let header = Header { number: 7, ..Default::default() };
1090
1091        assert!(service.headers_cache.queue(block_hash, response_tx));
1092
1093        service.on_reorg_header(block_hash, Ok(header));
1094
1095        let header =
1096            response_rx.try_recv().expect("queued header response").expect("header result");
1097
1098        assert_eq!(header.number, 7);
1099    }
1100
1101    #[test]
1102    fn reorg_removes_tx_hash_index_entries_unconditionally() {
1103        let mut service = test_service();
1104        let block = test_block();
1105        let tx_hash = *block.body().transactions().next().expect("test transaction").tx_hash();
1106
1107        service.tx_hash_index.insert(tx_hash, (B256::repeat_byte(0x33), 0));
1108
1109        service.remove_block_transactions(&block);
1110
1111        assert!(service.tx_hash_index.get(&tx_hash).is_none());
1112    }
1113
1114    #[test]
1115    fn reorg_evicts_cached_bal() {
1116        let mut service = test_service();
1117        let block_hash = B256::repeat_byte(0x44);
1118
1119        assert!(service.bal_cache.insert(block_hash, CachedRevmBal::new(test_decoded_revm_bal())));
1120        assert!(service.bal_cache.get(&block_hash).is_some());
1121
1122        service.on_reorg_bal(block_hash, Ok(None));
1123
1124        assert!(service.bal_cache.get(&block_hash).is_none());
1125    }
1126
1127    #[test]
1128    fn reorg_forwards_bal_to_queued_requests() {
1129        let mut service = test_service();
1130        let block_hash = B256::repeat_byte(0x55);
1131        let (response_tx, mut response_rx) = oneshot::channel();
1132        let bal = CachedRevmBal::new(test_decoded_revm_bal());
1133
1134        assert!(service.bal_cache.queue(block_hash, response_tx));
1135
1136        service.on_reorg_bal(block_hash, Ok(Some(bal)));
1137
1138        let bal = response_rx.try_recv().expect("queued BAL response").expect("BAL result");
1139
1140        assert!(bal.is_some());
1141    }
1142
1143    #[test]
1144    fn cached_revm_bal_size_accounts_for_nested_allocations() {
1145        let mut account = RevmAccountBal::default();
1146        account.account_info.nonce.writes.push((BlockAccessIndex::new(1), 1));
1147        account
1148            .account_info
1149            .balance
1150            .writes
1151            .push((BlockAccessIndex::new(2), StorageValue::from(1u64)));
1152        account.account_info.code.writes.push((
1153            BlockAccessIndex::new(3),
1154            (B256::repeat_byte(0xaa), Bytecode::new_raw(Bytes::from_static(&[0x60, 0x00]))),
1155        ));
1156        account.storage.storage.insert(
1157            StorageKey::from(1u64),
1158            RevmBalWrites::new(vec![(BlockAccessIndex::new(4), StorageValue::from(2u64))]),
1159        );
1160
1161        let mut bal = RevmBal::default();
1162        bal.accounts.insert(Address::ZERO, account);
1163
1164        let raw = Bytes::from_static(&[0xc0, 0x01, 0x02]);
1165        let previous_estimate = core::mem::size_of::<CachedRevmBal>() +
1166            core::mem::size_of::<DecodedBal<Arc<RevmBal>>>() +
1167            raw.len() +
1168            core::mem::size_of::<RevmBal>();
1169        assert!(CachedRevmBal::new(DecodedBal::new(Arc::new(bal), raw)).size() > previous_estimate);
1170    }
1171
1172    #[tokio::test]
1173    async fn get_bal_uses_cached_revm_bal() {
1174        let fetches = Arc::new(AtomicUsize::default());
1175        let provider = TestBalProvider::new(fetches.clone());
1176        let cache = EthStateCache::<EthPrimitives>::spawn_with(
1177            provider,
1178            EthStateCacheConfig {
1179                max_blocks: 0,
1180                max_receipts: 0,
1181                max_headers: 0,
1182                max_bals: 4,
1183                max_concurrent_db_requests: 1,
1184                max_cached_tx_hashes: 0,
1185            },
1186            Runtime::test(),
1187        );
1188        let block_hash = B256::repeat_byte(0x66);
1189
1190        assert!(cache.get_bal(block_hash).await.unwrap().is_some());
1191        assert!(cache.get_bal(block_hash).await.unwrap().is_some());
1192
1193        assert_eq!(fetches.load(Ordering::SeqCst), 1);
1194    }
1195
1196    #[tokio::test]
1197    async fn concurrent_get_bal_requests_share_fetch() {
1198        let fetches = Arc::new(AtomicUsize::default());
1199        let provider = TestBalProvider::new(fetches.clone());
1200        let cache = EthStateCache::<EthPrimitives>::spawn_with(
1201            provider,
1202            EthStateCacheConfig {
1203                max_blocks: 0,
1204                max_receipts: 0,
1205                max_headers: 0,
1206                max_bals: 4,
1207                max_concurrent_db_requests: 1,
1208                max_cached_tx_hashes: 0,
1209            },
1210            Runtime::test(),
1211        );
1212        let block_hash = B256::repeat_byte(0x77);
1213
1214        let (first, second) = tokio::join!(cache.get_bal(block_hash), cache.get_bal(block_hash));
1215
1216        assert!(first.unwrap().is_some());
1217        assert!(second.unwrap().is_some());
1218        assert_eq!(fetches.load(Ordering::SeqCst), 1);
1219    }
1220
1221    #[derive(Clone, Debug, Default)]
1222    struct TestBalProvider {
1223        bal_store: BalStoreHandle,
1224    }
1225
1226    impl TestBalProvider {
1227        fn new(fetches: Arc<AtomicUsize>) -> Self {
1228            Self { bal_store: BalStoreHandle::new(TestBalStore { fetches }) }
1229        }
1230    }
1231
1232    impl BalProvider for TestBalProvider {
1233        fn bal_store(&self) -> &BalStoreHandle {
1234            &self.bal_store
1235        }
1236    }
1237
1238    #[derive(Debug)]
1239    struct TestBalStore {
1240        fetches: Arc<AtomicUsize>,
1241    }
1242
1243    impl BalStore for TestBalStore {
1244        fn insert(&self, _num_hash: NumHash, _bal: reth_storage_api::RawBal) -> ProviderResult<()> {
1245            Ok(())
1246        }
1247
1248        fn prune(&self, _tip: BlockNumber) -> ProviderResult<usize> {
1249            Ok(0)
1250        }
1251
1252        fn get_by_hashes(&self, block_hashes: &[BlockHash]) -> ProviderResult<Vec<Option<Bytes>>> {
1253            self.fetches.fetch_add(1, Ordering::SeqCst);
1254            Ok(block_hashes.iter().map(|_| Some(Bytes::from_static(&[0xc0]))).collect())
1255        }
1256
1257        fn bal_stream(&self) -> reth_storage_api::BalNotificationStream {
1258            reth_storage_api::NoopBalStore.bal_stream()
1259        }
1260    }
1261
1262    impl BlockHashReader for TestBalProvider {
1263        fn block_hash(&self, _number: BlockNumber) -> ProviderResult<Option<B256>> {
1264            Ok(None)
1265        }
1266
1267        fn canonical_hashes_range(
1268            &self,
1269            _start: BlockNumber,
1270            _end: BlockNumber,
1271        ) -> ProviderResult<Vec<B256>> {
1272            Ok(Vec::new())
1273        }
1274    }
1275
1276    impl BlockNumReader for TestBalProvider {
1277        fn chain_info(&self) -> ProviderResult<reth_chainspec::ChainInfo> {
1278            Ok(reth_chainspec::ChainInfo::default())
1279        }
1280
1281        fn best_block_number(&self) -> ProviderResult<BlockNumber> {
1282            Ok(0)
1283        }
1284
1285        fn last_block_number(&self) -> ProviderResult<BlockNumber> {
1286            Ok(0)
1287        }
1288
1289        fn block_number(&self, _hash: B256) -> ProviderResult<Option<BlockNumber>> {
1290            Ok(None)
1291        }
1292    }
1293
1294    impl HeaderProvider for TestBalProvider {
1295        type Header = Header;
1296
1297        fn header(&self, _block_hash: BlockHash) -> ProviderResult<Option<Self::Header>> {
1298            Ok(None)
1299        }
1300
1301        fn header_by_number(&self, _num: u64) -> ProviderResult<Option<Self::Header>> {
1302            Ok(None)
1303        }
1304
1305        fn headers_range(
1306            &self,
1307            _range: impl RangeBounds<BlockNumber>,
1308        ) -> ProviderResult<Vec<Self::Header>> {
1309            Ok(Vec::new())
1310        }
1311
1312        fn sealed_header(
1313            &self,
1314            _number: BlockNumber,
1315        ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
1316            Ok(None)
1317        }
1318
1319        fn sealed_headers_while(
1320            &self,
1321            _range: impl RangeBounds<BlockNumber>,
1322            _predicate: impl FnMut(&SealedHeader<Self::Header>) -> bool,
1323        ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
1324            Ok(Vec::new())
1325        }
1326    }
1327
1328    impl BlockBodyIndicesProvider for TestBalProvider {
1329        fn block_body_indices(&self, _num: u64) -> ProviderResult<Option<StoredBlockBodyIndices>> {
1330            Ok(None)
1331        }
1332
1333        fn block_body_indices_range(
1334            &self,
1335            _range: RangeInclusive<BlockNumber>,
1336        ) -> ProviderResult<Vec<StoredBlockBodyIndices>> {
1337            Ok(Vec::new())
1338        }
1339    }
1340
1341    impl TransactionsProvider for TestBalProvider {
1342        type Transaction = TransactionSigned;
1343
1344        fn transaction_id(&self, _tx_hash: TxHash) -> ProviderResult<Option<TxNumber>> {
1345            Ok(None)
1346        }
1347
1348        fn transaction_by_id(&self, _id: TxNumber) -> ProviderResult<Option<Self::Transaction>> {
1349            Ok(None)
1350        }
1351
1352        fn transaction_by_id_unhashed(
1353            &self,
1354            _id: TxNumber,
1355        ) -> ProviderResult<Option<Self::Transaction>> {
1356            Ok(None)
1357        }
1358
1359        fn transaction_by_hash(&self, _hash: TxHash) -> ProviderResult<Option<Self::Transaction>> {
1360            Ok(None)
1361        }
1362
1363        fn transaction_by_hash_with_meta(
1364            &self,
1365            _hash: TxHash,
1366        ) -> ProviderResult<Option<(Self::Transaction, TransactionMeta)>> {
1367            Ok(None)
1368        }
1369
1370        fn transactions_by_block(
1371            &self,
1372            _block: BlockHashOrNumber,
1373        ) -> ProviderResult<Option<Vec<Self::Transaction>>> {
1374            Ok(None)
1375        }
1376
1377        fn transactions_by_block_range(
1378            &self,
1379            _range: impl RangeBounds<BlockNumber>,
1380        ) -> ProviderResult<Vec<Vec<Self::Transaction>>> {
1381            Ok(Vec::new())
1382        }
1383
1384        fn transactions_by_tx_range(
1385            &self,
1386            _range: impl RangeBounds<TxNumber>,
1387        ) -> ProviderResult<Vec<Self::Transaction>> {
1388            Ok(Vec::new())
1389        }
1390
1391        fn senders_by_tx_range(
1392            &self,
1393            _range: impl RangeBounds<TxNumber>,
1394        ) -> ProviderResult<Vec<Address>> {
1395            Ok(Vec::new())
1396        }
1397
1398        fn transaction_sender(&self, _id: TxNumber) -> ProviderResult<Option<Address>> {
1399            Ok(None)
1400        }
1401    }
1402
1403    impl ReceiptProvider for TestBalProvider {
1404        type Receipt = Receipt;
1405
1406        fn receipt(&self, _id: TxNumber) -> ProviderResult<Option<Self::Receipt>> {
1407            Ok(None)
1408        }
1409
1410        fn receipt_by_hash(&self, _hash: TxHash) -> ProviderResult<Option<Self::Receipt>> {
1411            Ok(None)
1412        }
1413
1414        fn receipts_by_block(
1415            &self,
1416            _block: BlockHashOrNumber,
1417        ) -> ProviderResult<Option<Vec<Self::Receipt>>> {
1418            Ok(None)
1419        }
1420
1421        fn receipts_by_tx_range(
1422            &self,
1423            _range: impl RangeBounds<TxNumber>,
1424        ) -> ProviderResult<Vec<Self::Receipt>> {
1425            Ok(Vec::new())
1426        }
1427
1428        fn receipts_by_block_range(
1429            &self,
1430            _block_range: RangeInclusive<BlockNumber>,
1431        ) -> ProviderResult<Vec<Vec<Self::Receipt>>> {
1432            Ok(Vec::new())
1433        }
1434    }
1435
1436    impl BlockReader for TestBalProvider {
1437        type Block = Block;
1438
1439        fn find_block_by_hash(
1440            &self,
1441            _hash: B256,
1442            _source: BlockSource,
1443        ) -> ProviderResult<Option<Self::Block>> {
1444            Ok(None)
1445        }
1446
1447        fn block(&self, _id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
1448            Ok(None)
1449        }
1450
1451        fn pending_block(&self) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
1452            Ok(None)
1453        }
1454
1455        fn pending_block_and_receipts(
1456            &self,
1457        ) -> ProviderResult<Option<(RecoveredBlock<Self::Block>, Vec<Self::Receipt>)>> {
1458            Ok(None)
1459        }
1460
1461        fn recovered_block(
1462            &self,
1463            _id: BlockHashOrNumber,
1464            _transaction_kind: TransactionVariant,
1465        ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
1466            Ok(None)
1467        }
1468
1469        fn sealed_block_with_senders(
1470            &self,
1471            _id: BlockHashOrNumber,
1472            _transaction_kind: TransactionVariant,
1473        ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
1474            Ok(None)
1475        }
1476
1477        fn block_range(
1478            &self,
1479            _range: RangeInclusive<BlockNumber>,
1480        ) -> ProviderResult<Vec<Self::Block>> {
1481            Ok(Vec::new())
1482        }
1483
1484        fn block_with_senders_range(
1485            &self,
1486            _range: RangeInclusive<BlockNumber>,
1487        ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
1488            Ok(Vec::new())
1489        }
1490
1491        fn recovered_block_range(
1492            &self,
1493            _range: RangeInclusive<BlockNumber>,
1494        ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
1495            Ok(Vec::new())
1496        }
1497
1498        fn block_by_transaction_id(&self, _id: TxNumber) -> ProviderResult<Option<BlockNumber>> {
1499            Ok(None)
1500        }
1501    }
1502}