1use 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
42type BlockWithSendersResponseSender<B> =
44 oneshot::Sender<ProviderResult<Option<Arc<RecoveredBlock<B>>>>>;
45
46type 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
54type HeaderResponseSender<H> = oneshot::Sender<ProviderResult<H>>;
56
57type CachedParentBlocksResponseSender<B> = oneshot::Sender<Vec<Arc<RecoveredBlock<B>>>>;
59
60type TransactionHashResponseSender<B, R> = oneshot::Sender<Option<CachedTransaction<B, R>>>;
62
63type 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#[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 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 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 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 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 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 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 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 #[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 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 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 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 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#[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#[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 provider: Provider,
335 full_block_cache: BlockLruCache<Provider::Block, LimitBlocks>,
337 receipts_cache: ReceiptsLruCache<Provider::Receipt, LimitReceipts>,
339 headers_cache: HeaderLruCache<Provider::Header, LimitHeaders>,
344 bal_cache: BalLruCache<LimitBals>,
346 action_tx: UnboundedSender<CacheAction<Provider::Block, Provider::Receipt>>,
348 action_rx: UnboundedReceiverStream<CacheAction<Provider::Block, Provider::Receipt>>,
350 action_task_spawner: Tasks,
352 rate_limiter: Arc<Semaphore>,
356 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 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 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 for tx in queued {
387 let _ = tx.send(res.clone());
388 }
389 }
390
391 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 for tx in queued {
405 let _ = tx.send(res.clone());
406 }
407 }
408
409 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 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 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 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 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 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 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 let _permit = rate_limiter.acquire().await;
534 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 if let Some(receipts) = this.receipts_cache.get(&block_hash).cloned() {
549 let _ = response_tx.send(Ok(Some(receipts)));
550 continue
551 }
552
553 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 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 if let Some(header) = this.headers_cache.get(&block_hash).cloned() {
574 let _ = response_tx.send(Ok(header));
575 continue
576 }
577
578 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 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 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 for tx in queued {
648 let _ = tx.send(res.clone());
649 }
650 }
651
652 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 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 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 while blocks.len() < max_blocks {
699 if let Some(block) =
700 this.full_block_cache.get(¤t_hash).cloned()
701 {
702 current_hash = block.header().parent_hash();
704 blocks.push(block);
705 } else {
706 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
730enum 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 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
795struct 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#[derive(Copy, Clone, Debug)]
822enum CacheKind {
823 Block,
824 Receipt,
825 Header,
826 Bal,
827}
828
829#[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
902pub 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#[derive(Clone, Debug)]
929pub(crate) struct CachedRevmBal(Arc<DecodedBal<Arc<RevmBal>>>);
930
931impl CachedRevmBal {
932 #[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}