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