1use super::{EthStateCacheConfig, MultiConsumerLruCache};
4use crate::block::CachedTransaction;
5use alloy_consensus::transaction::TxHashRef;
6use alloy_eip7928::bal::DecodedBal;
7use alloy_eips::BlockHashOrNumber;
8use alloy_primitives::{Address, Bytes, TxHash, B256};
9use futures::{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 CachedBalResponseSender = oneshot::Sender<Option<CachedRevmBal>>;
52
53type CachedBlockAndReceiptsResponseSender<B, R> =
54 oneshot::Sender<(Option<Arc<RecoveredBlock<B>>>, Option<Arc<Vec<R>>>)>;
55
56type TransactionHashResponseSender<B, R> = oneshot::Sender<Option<CachedTransaction<B, R>>>;
58
59type BalResponseSender = oneshot::Sender<ProviderResult<Option<CachedRevmBal>>>;
61
62type BlockLruCache<B, L> =
63 MultiConsumerLruCache<B256, Arc<RecoveredBlock<B>>, L, BlockWithSendersResponseSender<B>>;
64
65type ReceiptsLruCache<R, L> =
66 MultiConsumerLruCache<B256, Arc<Vec<R>>, L, ReceiptsResponseSender<R>>;
67
68type BalLruCache<L> = MultiConsumerLruCache<B256, CachedRevmBal, L, BalResponseSender>;
69
70#[derive(Debug)]
75pub struct EthStateCache<N: NodePrimitives> {
76 to_service: UnboundedSender<CacheAction<N::Block, N::Receipt>>,
77}
78
79impl<N: NodePrimitives> Clone for EthStateCache<N> {
80 fn clone(&self) -> Self {
81 Self { to_service: self.to_service.clone() }
82 }
83}
84
85impl<N: NodePrimitives> EthStateCache<N> {
86 fn create<Provider>(
88 provider: Provider,
89 action_task_spawner: Runtime,
90 config: EthStateCacheConfig,
91 ) -> (Self, EthStateCacheService<Provider, Runtime>)
92 where
93 Provider: BlockReader<Block = N::Block, Receipt = N::Receipt> + BalProvider,
94 {
95 let EthStateCacheConfig {
96 max_blocks,
97 max_receipts,
98 max_bals,
99 max_concurrent_db_requests,
100 max_cached_tx_hashes,
101 } = config;
102 let (to_service, rx) = unbounded_channel();
103
104 let service = EthStateCacheService {
105 provider,
106 full_block_cache: BlockLruCache::new(max_blocks, "blocks"),
107 receipts_cache: ReceiptsLruCache::new(max_receipts, "receipts"),
108 bal_cache: BalLruCache::new(max_bals, "bals"),
109 action_tx: to_service.clone(),
110 action_rx: UnboundedReceiverStream::new(rx),
111 action_task_spawner,
112 rate_limiter: Arc::new(Semaphore::new(max_concurrent_db_requests)),
113 tx_hash_index: LruMap::new(ByLength::new(max_cached_tx_hashes)),
114 };
115 let cache = Self { to_service };
116 (cache, service)
117 }
118
119 pub fn spawn_with<Provider>(
124 provider: Provider,
125 config: EthStateCacheConfig,
126 executor: Runtime,
127 ) -> Self
128 where
129 Provider: BlockReader<Block = N::Block, Receipt = N::Receipt>
130 + BalProvider
131 + Clone
132 + Unpin
133 + 'static,
134 {
135 let (this, service) = Self::create(provider, executor.clone(), config);
136 executor.spawn_critical_task("eth state cache", service);
137 this
138 }
139
140 pub async fn get_recovered_block(
144 &self,
145 block_hash: B256,
146 ) -> ProviderResult<Option<Arc<RecoveredBlock<N::Block>>>> {
147 let (response_tx, rx) = oneshot::channel();
148 let _ = self.to_service.send(CacheAction::GetBlockWithSenders { block_hash, response_tx });
149 rx.await.map_err(|_| CacheServiceUnavailable)?
150 }
151
152 pub async fn get_maybe_block(
154 &self,
155 block_hash: B256,
156 ) -> ProviderResult<Option<Arc<RecoveredBlock<N::Block>>>> {
157 let (response_tx, rx) = oneshot::channel();
158 let _ = self.to_service.send(CacheAction::GetCachedBlock { block_hash, response_tx });
159 rx.await.map_err(|_| CacheServiceUnavailable.into())
160 }
161
162 pub async fn get_receipts(
166 &self,
167 block_hash: B256,
168 ) -> ProviderResult<Option<Arc<Vec<N::Receipt>>>> {
169 let (response_tx, rx) = oneshot::channel();
170 let _ = self.to_service.send(CacheAction::GetReceipts { block_hash, response_tx });
171 rx.await.map_err(|_| CacheServiceUnavailable)?
172 }
173
174 pub async fn get_block_and_receipts(
176 &self,
177 block_hash: B256,
178 ) -> ProviderResult<Option<(Arc<RecoveredBlock<N::Block>>, Arc<Vec<N::Receipt>>)>> {
179 let block = self.get_recovered_block(block_hash);
180 let receipts = self.get_receipts(block_hash);
181
182 let (block, receipts) = futures::try_join!(block, receipts)?;
183
184 Ok(block.zip(receipts))
185 }
186
187 pub async fn get_recovered_block_and_maybe_bal(
191 &self,
192 block_hash: B256,
193 ) -> ProviderResult<
194 Option<(Arc<RecoveredBlock<N::Block>>, Option<Arc<DecodedBal<Arc<RevmBal>>>>)>,
195 > {
196 let (response_tx, rx) = oneshot::channel();
197 let _ = self.to_service.send(CacheAction::GetCachedBal { block_hash, response_tx });
198
199 let block = self.get_recovered_block(block_hash);
200 let (block, bal) = futures::join!(block, rx);
201
202 let bal = bal.map_err(|_| CacheServiceUnavailable)?.map(|cached| cached.0);
203 Ok(block?.map(|block| (block, bal)))
204 }
205
206 pub async fn get_receipts_and_maybe_block(
208 &self,
209 block_hash: B256,
210 ) -> ProviderResult<Option<(Arc<Vec<N::Receipt>>, Option<Arc<RecoveredBlock<N::Block>>>)>> {
211 let (response_tx, rx) = oneshot::channel();
212 let _ = self.to_service.send(CacheAction::GetCachedBlock { block_hash, response_tx });
213
214 let receipts = self.get_receipts(block_hash);
215
216 let (receipts, block) = futures::join!(receipts, rx);
217
218 let block = block.map_err(|_| CacheServiceUnavailable)?;
219 Ok(receipts?.map(|r| (r, block)))
220 }
221
222 pub async fn maybe_cached_block_and_receipts(
224 &self,
225 block_hash: B256,
226 ) -> ProviderResult<(Option<Arc<RecoveredBlock<N::Block>>>, Option<Arc<Vec<N::Receipt>>>)> {
227 let (response_tx, rx) = oneshot::channel();
228 let _ = self
229 .to_service
230 .send(CacheAction::GetCachedBlockAndReceipts { block_hash, response_tx });
231 rx.await.map_err(|_| CacheServiceUnavailable.into())
232 }
233
234 pub async fn get_transaction_by_hash(
239 &self,
240 tx_hash: TxHash,
241 ) -> Option<CachedTransaction<N::Block, N::Receipt>> {
242 let (response_tx, rx) = oneshot::channel();
243 let _ = self.to_service.send(CacheAction::GetTransactionByHash { tx_hash, response_tx });
244 rx.await.ok()?
245 }
246
247 pub async fn get_bal(
251 &self,
252 block_hash: B256,
253 ) -> ProviderResult<Option<Arc<DecodedBal<Arc<RevmBal>>>>> {
254 let (response_tx, rx) = oneshot::channel();
255 let _ = self.to_service.send(CacheAction::GetBal { block_hash, response_tx });
256 rx.await
257 .map_err(|_| CacheServiceUnavailable)?
258 .map(|maybe_bal| maybe_bal.map(|cached| cached.0))
259 }
260}
261#[derive(Debug, thiserror::Error)]
263#[error("cache service task stopped")]
264pub struct CacheServiceUnavailable;
265
266impl From<CacheServiceUnavailable> for ProviderError {
267 fn from(err: CacheServiceUnavailable) -> Self {
268 Self::other(err)
269 }
270}
271
272#[must_use = "Type does nothing unless spawned"]
289pub(crate) struct EthStateCacheService<
290 Provider,
291 Tasks,
292 LimitBlocks = ByLength,
293 LimitReceipts = ByLength,
294 LimitBals = ByLength,
295> where
296 Provider: BlockReader + BalProvider,
297 LimitBlocks: Limiter<B256, Arc<RecoveredBlock<Provider::Block>>>,
298 LimitReceipts: Limiter<B256, Arc<Vec<Provider::Receipt>>>,
299 LimitBals: Limiter<B256, CachedRevmBal>,
300{
301 provider: Provider,
303 full_block_cache: BlockLruCache<Provider::Block, LimitBlocks>,
305 receipts_cache: ReceiptsLruCache<Provider::Receipt, LimitReceipts>,
307 bal_cache: BalLruCache<LimitBals>,
309 action_tx: UnboundedSender<CacheAction<Provider::Block, Provider::Receipt>>,
311 action_rx: UnboundedReceiverStream<CacheAction<Provider::Block, Provider::Receipt>>,
313 action_task_spawner: Tasks,
315 rate_limiter: Arc<Semaphore>,
319 tx_hash_index: LruMap<TxHash, (B256, usize), ByLength>,
321}
322
323impl<Provider> EthStateCacheService<Provider, Runtime>
324where
325 Provider: BlockReader + BalProvider + Clone + Unpin + 'static,
326{
327 fn index_block_transactions(&mut self, block: &RecoveredBlock<Provider::Block>) {
329 let block_hash = block.hash();
330 for (tx_idx, tx) in block.body().transactions().iter().enumerate() {
331 self.tx_hash_index.insert(*tx.tx_hash(), (block_hash, tx_idx));
332 }
333 }
334
335 fn remove_block_transactions(&mut self, block: &RecoveredBlock<Provider::Block>) {
337 for tx in block.body().transactions() {
338 self.tx_hash_index.remove(tx.tx_hash());
339 }
340 }
341
342 fn on_new_block(
343 &mut self,
344 block_hash: B256,
345 res: ProviderResult<Option<Arc<RecoveredBlock<Provider::Block>>>>,
346 ) {
347 if let Some(queued) = self.full_block_cache.remove(&block_hash) {
348 for tx in queued {
350 let _ = tx.send(res.clone());
351 }
352 }
353
354 if let Ok(Some(block)) = res {
356 self.full_block_cache.insert(block_hash, block);
357 }
358 }
359
360 fn on_new_receipts(
361 &mut self,
362 block_hash: B256,
363 res: ProviderResult<Option<Arc<Vec<Provider::Receipt>>>>,
364 ) {
365 if let Some(queued) = self.receipts_cache.remove(&block_hash) {
366 for tx in queued {
368 let _ = tx.send(res.clone());
369 }
370 }
371
372 if let Ok(Some(receipts)) = res {
374 self.receipts_cache.insert(block_hash, receipts);
375 }
376 }
377
378 fn on_new_bal(&mut self, block_hash: B256, res: ProviderResult<Option<CachedRevmBal>>) {
379 if let Some(queued) = self.bal_cache.remove(&block_hash) {
380 for tx in queued {
381 let _ = tx.send(res.clone());
382 }
383 }
384
385 if let Ok(Some(bal)) = res {
386 self.bal_cache.insert(block_hash, bal);
387 }
388 }
389
390 fn on_reorg_block(
391 &mut self,
392 block_hash: B256,
393 res: ProviderResult<Option<Arc<RecoveredBlock<Provider::Block>>>>,
394 ) {
395 if let Some(queued) = self.full_block_cache.remove(&block_hash) {
396 for tx in queued {
398 let _ = tx.send(res.clone());
399 }
400 }
401 }
402
403 fn on_reorg_receipts(
404 &mut self,
405 block_hash: B256,
406 res: ProviderResult<Option<Arc<Vec<Provider::Receipt>>>>,
407 ) {
408 if let Some(queued) = self.receipts_cache.remove(&block_hash) {
409 for tx in queued {
411 let _ = tx.send(res.clone());
412 }
413 }
414 }
415
416 fn on_reorg_bal(&mut self, block_hash: B256, res: ProviderResult<Option<CachedRevmBal>>) {
417 if let Some(queued) = self.bal_cache.remove(&block_hash) {
418 for tx in queued {
419 let _ = tx.send(res.clone());
420 }
421 }
422 }
423
424 fn shrink_queues(&mut self) {
426 let min_capacity = 2;
427 self.full_block_cache.shrink_to(min_capacity);
428 self.receipts_cache.shrink_to(min_capacity);
429 self.bal_cache.shrink_to(min_capacity);
430 }
431
432 fn update_cached_metrics(&mut self) {
433 self.full_block_cache.update_cached_metrics();
434 self.receipts_cache.update_cached_metrics();
435 self.bal_cache.update_cached_metrics();
436 }
437}
438
439impl<Provider> Future for EthStateCacheService<Provider, Runtime>
440where
441 Provider: BlockReader + BalProvider + Clone + Unpin + 'static,
442{
443 type Output = ();
444
445 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
446 let this = self.get_mut();
447
448 loop {
449 let Poll::Ready(action) = this.action_rx.poll_next_unpin(cx) else {
450 this.shrink_queues();
452 this.update_cached_metrics();
455 return Poll::Pending;
456 };
457
458 match action {
459 None => {
460 unreachable!("can't close")
461 }
462 Some(action) => {
463 match action {
464 CacheAction::GetCachedBlock { block_hash, response_tx } => {
465 let _ =
466 response_tx.send(this.full_block_cache.get(&block_hash).cloned());
467 }
468 CacheAction::GetCachedBal { block_hash, response_tx } => {
469 let _ = response_tx.send(this.bal_cache.get(&block_hash).cloned());
470 }
471 CacheAction::GetCachedBlockAndReceipts { block_hash, response_tx } => {
472 let block = this.full_block_cache.get(&block_hash).cloned();
473 let receipts = this.receipts_cache.get(&block_hash).cloned();
474 let _ = response_tx.send((block, receipts));
475 }
476 CacheAction::GetBlockWithSenders { block_hash, response_tx } => {
477 if let Some(block) = this.full_block_cache.get(&block_hash).cloned() {
478 let _ = response_tx.send(Ok(Some(block)));
479 continue
480 }
481
482 if this.full_block_cache.queue(block_hash, response_tx) {
484 let provider = this.provider.clone();
485 let action_tx = this.action_tx.clone();
486 let rate_limiter = this.rate_limiter.clone();
487 let mut action_sender =
488 ActionSender::new(CacheKind::Block, block_hash, action_tx);
489 this.action_task_spawner.spawn_blocking_task(async move {
490 let _permit = rate_limiter.acquire().await;
492 let block_sender = provider
495 .sealed_block_with_senders(
496 BlockHashOrNumber::Hash(block_hash),
497 TransactionVariant::WithHash,
498 )
499 .map(|maybe_block| maybe_block.map(Arc::new));
500 action_sender.send_block(block_sender);
501 });
502 }
503 }
504 CacheAction::GetReceipts { block_hash, response_tx } => {
505 if let Some(receipts) = this.receipts_cache.get(&block_hash).cloned() {
507 let _ = response_tx.send(Ok(Some(receipts)));
508 continue
509 }
510
511 if this.receipts_cache.queue(block_hash, response_tx) {
513 let provider = this.provider.clone();
514 let action_tx = this.action_tx.clone();
515 let rate_limiter = this.rate_limiter.clone();
516 let mut action_sender =
517 ActionSender::new(CacheKind::Receipt, block_hash, action_tx);
518 this.action_task_spawner.spawn_blocking_task(async move {
519 let _permit = rate_limiter.acquire().await;
521 let res = provider
522 .receipts_by_block(block_hash.into())
523 .map(|maybe_receipts| maybe_receipts.map(Arc::new));
524
525 action_sender.send_receipts(res);
526 });
527 }
528 }
529 CacheAction::GetBal { block_hash, response_tx } => {
530 if let Some(bal) = this.bal_cache.get(&block_hash).cloned() {
531 let _ = response_tx.send(Ok(Some(bal)));
532 continue
533 }
534
535 if this.bal_cache.queue(block_hash, response_tx) {
536 let provider = this.provider.clone();
537 let action_tx = this.action_tx.clone();
538 let rate_limiter = this.rate_limiter.clone();
539 let mut action_sender =
540 ActionSender::new(CacheKind::Bal, block_hash, action_tx);
541 this.action_task_spawner.spawn_blocking_task(async move {
542 let _permit = rate_limiter.acquire().await;
543 let res = provider.get_bal_by_hash(block_hash).and_then(
544 |maybe_bal| {
545 maybe_bal.map(CachedRevmBal::try_from_raw).transpose()
546 },
547 );
548 action_sender.send_bal(res);
549 });
550 }
551 }
552 CacheAction::ReceiptsResult { block_hash, res } => {
553 this.on_new_receipts(block_hash, res);
554 }
555 CacheAction::BalResult { block_hash, res } => {
556 this.on_new_bal(block_hash, res);
557 }
558 CacheAction::BlockWithSendersResult { block_hash, res } => match res {
559 Ok(Some(block_with_senders)) => {
560 this.on_new_block(block_hash, Ok(Some(block_with_senders)));
561 }
562 Ok(None) => {
563 this.on_new_block(block_hash, Ok(None));
564 }
565 Err(e) => {
566 this.on_new_block(block_hash, Err(e));
567 }
568 },
569 CacheAction::CacheNewCanonicalChain { chain_change } => {
570 for block in chain_change.blocks {
571 this.index_block_transactions(&block);
573 this.on_new_block(block.hash(), Ok(Some(block)));
574 }
575
576 for block_receipts in chain_change.receipts {
577 this.on_new_receipts(
578 block_receipts.block_hash,
579 Ok(Some(block_receipts.receipts)),
580 );
581 }
582 }
583 CacheAction::RemoveReorgedChain { chain_change } => {
584 for block in chain_change.blocks {
585 let block_hash = block.hash();
586 this.remove_block_transactions(&block);
588 this.on_reorg_block(block_hash, Ok(Some(block)));
589 this.on_reorg_bal(block_hash, Ok(None));
590 }
591
592 for block_receipts in chain_change.receipts {
593 this.on_reorg_receipts(
594 block_receipts.block_hash,
595 Ok(Some(block_receipts.receipts)),
596 );
597 }
598 }
599 CacheAction::GetTransactionByHash { tx_hash, response_tx } => {
600 let result =
601 this.tx_hash_index.get(&tx_hash).and_then(|(block_hash, idx)| {
602 let block = this.full_block_cache.get(block_hash).cloned()?;
603 let receipts = this.receipts_cache.get(block_hash).cloned();
604 Some(CachedTransaction::new(block, *idx, receipts))
605 });
606 let _ = response_tx.send(result);
607 }
608 };
609 }
610 }
611 }
612 }
613}
614
615enum CacheAction<B: Block, R> {
617 GetBlockWithSenders {
618 block_hash: B256,
619 response_tx: BlockWithSendersResponseSender<B>,
620 },
621 GetReceipts {
622 block_hash: B256,
623 response_tx: ReceiptsResponseSender<R>,
624 },
625 GetBal {
626 block_hash: B256,
627 response_tx: BalResponseSender,
628 },
629 GetCachedBlock {
630 block_hash: B256,
631 response_tx: CachedBlockResponseSender<B>,
632 },
633 GetCachedBal {
634 block_hash: B256,
635 response_tx: CachedBalResponseSender,
636 },
637 GetCachedBlockAndReceipts {
638 block_hash: B256,
639 response_tx: CachedBlockAndReceiptsResponseSender<B, R>,
640 },
641 BlockWithSendersResult {
642 block_hash: B256,
643 res: ProviderResult<Option<Arc<RecoveredBlock<B>>>>,
644 },
645 ReceiptsResult {
646 block_hash: B256,
647 res: ProviderResult<Option<Arc<Vec<R>>>>,
648 },
649 BalResult {
650 block_hash: B256,
651 res: ProviderResult<Option<CachedRevmBal>>,
652 },
653 CacheNewCanonicalChain {
654 chain_change: ChainChange<B, R>,
655 },
656 RemoveReorgedChain {
657 chain_change: ChainChange<B, R>,
658 },
659 GetTransactionByHash {
661 tx_hash: TxHash,
662 response_tx: TransactionHashResponseSender<B, R>,
663 },
664}
665
666struct BlockReceipts<R> {
667 block_hash: B256,
668 receipts: Arc<Vec<R>>,
669}
670
671struct ChainChange<B: Block, R> {
673 blocks: Vec<Arc<RecoveredBlock<B>>>,
674 receipts: Vec<BlockReceipts<R>>,
675}
676
677impl<B: Block, R: Clone> ChainChange<B, R> {
678 fn new<N>(chain: Arc<Chain<N>>) -> Self
679 where
680 N: NodePrimitives<Block = B, Receipt = R>,
681 {
682 let (blocks, receipts): (Vec<_>, Vec<_>) = chain
683 .blocks_and_receipts()
684 .map(|(block, receipts)| {
685 let block_receipts = BlockReceipts {
686 block_hash: block.hash(),
687 receipts: Arc::new(receipts.clone()),
688 };
689 (Arc::clone(block), block_receipts)
690 })
691 .unzip();
692 Self { blocks, receipts }
693 }
694}
695
696#[derive(Copy, Clone, Debug)]
698enum CacheKind {
699 Block,
700 Receipt,
701 Bal,
702}
703
704#[derive(Debug)]
709struct ActionSender<B: Block, R: Send + Sync> {
710 kind: CacheKind,
711 blockhash: B256,
712 tx: Option<UnboundedSender<CacheAction<B, R>>>,
713}
714
715impl<R: Send + Sync, B: Block> ActionSender<B, R> {
716 const fn new(kind: CacheKind, blockhash: B256, tx: UnboundedSender<CacheAction<B, R>>) -> Self {
717 Self { kind, blockhash, tx: Some(tx) }
718 }
719
720 fn send_block(&mut self, block_sender: Result<Option<Arc<RecoveredBlock<B>>>, ProviderError>) {
721 if let Some(tx) = self.tx.take() {
722 let _ = tx.send(CacheAction::BlockWithSendersResult {
723 block_hash: self.blockhash,
724 res: block_sender,
725 });
726 }
727 }
728
729 fn send_receipts(&mut self, receipts: Result<Option<Arc<Vec<R>>>, ProviderError>) {
730 if let Some(tx) = self.tx.take() {
731 let _ =
732 tx.send(CacheAction::ReceiptsResult { block_hash: self.blockhash, res: receipts });
733 }
734 }
735
736 fn send_bal(&mut self, bal: Result<Option<CachedRevmBal>, ProviderError>) {
737 if let Some(tx) = self.tx.take() {
738 let _ = tx.send(CacheAction::BalResult { block_hash: self.blockhash, res: bal });
739 }
740 }
741}
742impl<R: Send + Sync, B: Block> Drop for ActionSender<B, R> {
743 fn drop(&mut self) {
744 if let Some(tx) = self.tx.take() {
745 let msg = match self.kind {
746 CacheKind::Block => CacheAction::BlockWithSendersResult {
747 block_hash: self.blockhash,
748 res: Err(CacheServiceUnavailable.into()),
749 },
750 CacheKind::Receipt => CacheAction::ReceiptsResult {
751 block_hash: self.blockhash,
752 res: Err(CacheServiceUnavailable.into()),
753 },
754 CacheKind::Bal => CacheAction::BalResult {
755 block_hash: self.blockhash,
756 res: Err(CacheServiceUnavailable.into()),
757 },
758 };
759 let _ = tx.send(msg);
760 }
761 }
762}
763
764pub async fn cache_new_blocks_task<St, N: NodePrimitives>(
769 eth_state_cache: EthStateCache<N>,
770 mut events: St,
771) where
772 St: Stream<Item = CanonStateNotification<N>> + Unpin + 'static,
773{
774 while let Some(event) = events.next().await {
775 if let Some(reverted) = event.reverted() {
776 let chain_change = ChainChange::new(reverted);
777
778 let _ =
779 eth_state_cache.to_service.send(CacheAction::RemoveReorgedChain { chain_change });
780 }
781
782 let chain_change = ChainChange::new(event.committed());
783
784 let _ =
785 eth_state_cache.to_service.send(CacheAction::CacheNewCanonicalChain { chain_change });
786 }
787}
788
789#[derive(Clone, Debug)]
791pub(crate) struct CachedRevmBal(Arc<DecodedBal<Arc<RevmBal>>>);
792
793impl CachedRevmBal {
794 #[inline]
796 fn new(bal: DecodedBal<Arc<RevmBal>>) -> Self {
797 Self(Arc::new(bal))
798 }
799
800 fn try_from_raw(raw: Bytes) -> ProviderResult<Self> {
802 DecodedBal::from_rlp_bytes(raw)
803 .map_err(Into::into)
804 .and_then(|decoded| {
805 decoded.try_map(|bal| {
806 RevmBal::try_from(Vec::from(bal)).map(Arc::new).map_err(ProviderError::other)
807 })
808 })
809 .map(Self::new)
810 }
811}
812
813impl InMemorySize for CachedRevmBal {
814 fn size(&self) -> usize {
815 core::mem::size_of::<Self>() + decoded_revm_bal_size(&self.0)
816 }
817}
818
819fn decoded_revm_bal_size(bal: &DecodedBal<Arc<RevmBal>>) -> usize {
820 core::mem::size_of::<DecodedBal<Arc<RevmBal>>>() +
821 bal.as_raw().len() +
822 revm_bal_size(bal.as_bal())
823}
824
825fn revm_bal_size(bal: &Arc<RevmBal>) -> usize {
826 core::mem::size_of::<RevmBal>() +
827 bal.accounts.capacity() * core::mem::size_of::<(Address, RevmAccountBal)>() +
828 bal.accounts.values().map(revm_account_bal_heap_size).sum::<usize>()
829}
830
831fn revm_account_bal_heap_size(account: &RevmAccountBal) -> usize {
832 revm_account_info_bal_heap_size(&account.account_info) +
833 revm_storage_bal_heap_size(&account.storage)
834}
835
836fn revm_account_info_bal_heap_size(account_info: &RevmAccountInfoBal) -> usize {
837 revm_bal_writes_heap_size(&account_info.nonce, |_| 0) +
838 revm_bal_writes_heap_size(&account_info.balance, |_| 0) +
839 revm_bal_writes_heap_size(&account_info.code, revm_code_write_heap_size)
840}
841
842fn revm_storage_bal_heap_size(storage: &RevmStorageBal) -> usize {
843 storage.storage.len() * core::mem::size_of::<(StorageKey, RevmBalWrites<StorageValue>)>() +
844 storage
845 .storage
846 .values()
847 .map(|writes| revm_bal_writes_heap_size(writes, |_| 0))
848 .sum::<usize>()
849}
850
851fn revm_bal_writes_heap_size<T, F>(writes: &RevmBalWrites<T>, mut item_heap_size: F) -> usize
852where
853 T: PartialEq + Clone,
854 F: FnMut(&T) -> usize,
855{
856 writes.writes.capacity() * core::mem::size_of::<(u64, T)>() +
857 writes.writes.iter().map(|(_, item)| item_heap_size(item)).sum::<usize>()
858}
859
860fn revm_code_write_heap_size((_, bytecode): &(B256, Bytecode)) -> usize {
861 bytecode.bytes_ref().len()
862}
863
864#[cfg(test)]
865mod tests {
866 use super::*;
867 use alloy_consensus::{transaction::TransactionMeta, Header};
868 use alloy_eip7928::BlockAccessIndex;
869 use alloy_eips::{BlockHashOrNumber, NumHash};
870 use alloy_primitives::{Address, BlockHash, BlockNumber, Bytes, Signature, TxHash, TxNumber};
871 use core::ops::{RangeBounds, RangeInclusive};
872 use reth_db_models::StoredBlockBodyIndices;
873 use reth_ethereum_primitives::{
874 Block, BlockBody, EthPrimitives, Receipt, Transaction, TransactionSigned,
875 };
876 use reth_primitives_traits::{RecoveredBlock, SealedHeader};
877 use reth_storage_api::{
878 noop::NoopProvider, BalProvider, BalStore, BalStoreHandle, BlockBodyIndicesProvider,
879 BlockHashReader, BlockNumReader, BlockReader, BlockSource, HeaderProvider, ReceiptProvider,
880 TransactionVariant, TransactionsProvider,
881 };
882 use std::sync::atomic::{AtomicUsize, Ordering};
883
884 fn test_service() -> EthStateCacheService<NoopProvider, Runtime> {
885 let (_cache, service) = EthStateCache::<EthPrimitives>::create(
886 NoopProvider::default(),
887 Runtime::test(),
888 EthStateCacheConfig {
889 max_blocks: 4,
890 max_receipts: 4,
891 max_bals: 4,
892 max_concurrent_db_requests: 1,
893 max_cached_tx_hashes: 16,
894 },
895 );
896 service
897 }
898
899 fn test_decoded_revm_bal() -> DecodedBal<Arc<RevmBal>> {
900 DecodedBal::new(Arc::new(RevmBal::default()), Bytes::from_static(&[0xc0]))
901 }
902
903 fn test_block() -> RecoveredBlock<Block> {
904 RecoveredBlock::new_unhashed(
905 Block {
906 header: Header { number: 1, ..Default::default() },
907 body: BlockBody {
908 transactions: vec![TransactionSigned::new_unhashed(
909 Transaction::Legacy(Default::default()),
910 Signature::test_signature(),
911 )],
912 ..Default::default()
913 },
914 },
915 vec![Address::ZERO],
916 )
917 }
918
919 #[test]
920 fn reorg_removes_tx_hash_index_entries_unconditionally() {
921 let mut service = test_service();
922 let block = test_block();
923 let tx_hash = *block.body().transactions().next().expect("test transaction").tx_hash();
924
925 service.tx_hash_index.insert(tx_hash, (B256::repeat_byte(0x33), 0));
926
927 service.remove_block_transactions(&block);
928
929 assert!(service.tx_hash_index.get(&tx_hash).is_none());
930 }
931
932 #[test]
933 fn reorg_evicts_cached_bal() {
934 let mut service = test_service();
935 let block_hash = B256::repeat_byte(0x44);
936
937 assert!(service.bal_cache.insert(block_hash, CachedRevmBal::new(test_decoded_revm_bal())));
938 assert!(service.bal_cache.get(&block_hash).is_some());
939
940 service.on_reorg_bal(block_hash, Ok(None));
941
942 assert!(service.bal_cache.get(&block_hash).is_none());
943 }
944
945 #[test]
946 fn reorg_forwards_bal_to_queued_requests() {
947 let mut service = test_service();
948 let block_hash = B256::repeat_byte(0x55);
949 let (response_tx, mut response_rx) = oneshot::channel();
950 let bal = CachedRevmBal::new(test_decoded_revm_bal());
951
952 assert!(service.bal_cache.queue(block_hash, response_tx));
953
954 service.on_reorg_bal(block_hash, Ok(Some(bal)));
955
956 let bal = response_rx.try_recv().expect("queued BAL response").expect("BAL result");
957
958 assert!(bal.is_some());
959 }
960
961 #[test]
962 fn cached_revm_bal_size_accounts_for_nested_allocations() {
963 let mut account = RevmAccountBal::default();
964 account.account_info.nonce.writes.push((BlockAccessIndex::new(1), 1));
965 account
966 .account_info
967 .balance
968 .writes
969 .push((BlockAccessIndex::new(2), StorageValue::from(1u64)));
970 account.account_info.code.writes.push((
971 BlockAccessIndex::new(3),
972 (B256::repeat_byte(0xaa), Bytecode::new_raw(Bytes::from_static(&[0x60, 0x00]))),
973 ));
974 account.storage.storage.insert(
975 StorageKey::from(1u64),
976 RevmBalWrites::new(vec![(BlockAccessIndex::new(4), StorageValue::from(2u64))]),
977 );
978
979 let mut bal = RevmBal::default();
980 bal.accounts.insert(Address::ZERO, account);
981
982 let raw = Bytes::from_static(&[0xc0, 0x01, 0x02]);
983 let previous_estimate = core::mem::size_of::<CachedRevmBal>() +
984 core::mem::size_of::<DecodedBal<Arc<RevmBal>>>() +
985 raw.len() +
986 core::mem::size_of::<RevmBal>();
987 assert!(CachedRevmBal::new(DecodedBal::new(Arc::new(bal), raw)).size() > previous_estimate);
988 }
989
990 #[tokio::test]
991 async fn get_bal_uses_cached_revm_bal() {
992 let fetches = Arc::new(AtomicUsize::default());
993 let provider = TestBalProvider::new(fetches.clone());
994 let cache = EthStateCache::<EthPrimitives>::spawn_with(
995 provider,
996 EthStateCacheConfig {
997 max_blocks: 0,
998 max_receipts: 0,
999 max_bals: 4,
1000 max_concurrent_db_requests: 1,
1001 max_cached_tx_hashes: 0,
1002 },
1003 Runtime::test(),
1004 );
1005 let block_hash = B256::repeat_byte(0x66);
1006
1007 assert!(cache.get_bal(block_hash).await.unwrap().is_some());
1008 assert!(cache.get_bal(block_hash).await.unwrap().is_some());
1009
1010 assert_eq!(fetches.load(Ordering::SeqCst), 1);
1011 }
1012
1013 #[tokio::test]
1014 async fn get_recovered_block_and_maybe_bal_does_not_fetch_bal() {
1015 let bal_fetches = Arc::new(AtomicUsize::default());
1016 let block = test_block();
1017 let block_hash = block.hash();
1018 let provider = TestBalProvider::new(bal_fetches.clone()).with_block(block);
1019 let cache = EthStateCache::<EthPrimitives>::spawn_with(
1020 provider,
1021 EthStateCacheConfig {
1022 max_blocks: 4,
1023 max_receipts: 0,
1024 max_bals: 4,
1025 max_concurrent_db_requests: 1,
1026 max_cached_tx_hashes: 0,
1027 },
1028 Runtime::test(),
1029 );
1030
1031 let (returned_block, bal) = cache
1032 .get_recovered_block_and_maybe_bal(block_hash)
1033 .await
1034 .unwrap()
1035 .expect("block exists");
1036 assert_eq!(returned_block.hash(), block_hash);
1037 assert!(bal.is_none());
1038 assert_eq!(bal_fetches.load(Ordering::SeqCst), 0);
1039
1040 assert!(cache.get_bal(block_hash).await.unwrap().is_some());
1041
1042 let (_, bal) = cache
1043 .get_recovered_block_and_maybe_bal(block_hash)
1044 .await
1045 .unwrap()
1046 .expect("block exists");
1047 assert!(bal.is_some());
1048 assert_eq!(bal_fetches.load(Ordering::SeqCst), 1);
1049 }
1050
1051 #[tokio::test]
1052 async fn concurrent_get_bal_requests_share_fetch() {
1053 let fetches = Arc::new(AtomicUsize::default());
1054 let provider = TestBalProvider::new(fetches.clone());
1055 let cache = EthStateCache::<EthPrimitives>::spawn_with(
1056 provider,
1057 EthStateCacheConfig {
1058 max_blocks: 0,
1059 max_receipts: 0,
1060 max_bals: 4,
1061 max_concurrent_db_requests: 1,
1062 max_cached_tx_hashes: 0,
1063 },
1064 Runtime::test(),
1065 );
1066 let block_hash = B256::repeat_byte(0x77);
1067
1068 let (first, second) = tokio::join!(cache.get_bal(block_hash), cache.get_bal(block_hash));
1069
1070 assert!(first.unwrap().is_some());
1071 assert!(second.unwrap().is_some());
1072 assert_eq!(fetches.load(Ordering::SeqCst), 1);
1073 }
1074
1075 #[derive(Clone, Debug, Default)]
1076 struct TestBalProvider {
1077 bal_store: BalStoreHandle,
1078 block: Option<RecoveredBlock<Block>>,
1079 }
1080
1081 impl TestBalProvider {
1082 fn new(fetches: Arc<AtomicUsize>) -> Self {
1083 Self { bal_store: BalStoreHandle::new(TestBalStore { fetches }), block: None }
1084 }
1085
1086 fn with_block(mut self, block: RecoveredBlock<Block>) -> Self {
1087 self.block = Some(block);
1088 self
1089 }
1090 }
1091
1092 impl BalProvider for TestBalProvider {
1093 fn bal_store(&self) -> &BalStoreHandle {
1094 &self.bal_store
1095 }
1096 }
1097
1098 #[derive(Debug)]
1099 struct TestBalStore {
1100 fetches: Arc<AtomicUsize>,
1101 }
1102
1103 impl BalStore for TestBalStore {
1104 fn insert(&self, _num_hash: NumHash, _bal: reth_storage_api::RawBal) -> ProviderResult<()> {
1105 Ok(())
1106 }
1107
1108 fn prune(&self, _tip: BlockNumber) -> ProviderResult<usize> {
1109 Ok(0)
1110 }
1111
1112 fn get_by_hashes(&self, block_hashes: &[BlockHash]) -> ProviderResult<Vec<Option<Bytes>>> {
1113 self.fetches.fetch_add(1, Ordering::SeqCst);
1114 Ok(block_hashes.iter().map(|_| Some(Bytes::from_static(&[0xc0]))).collect())
1115 }
1116
1117 fn bal_stream(&self) -> reth_storage_api::BalNotificationStream {
1118 reth_storage_api::NoopBalStore.bal_stream()
1119 }
1120 }
1121
1122 impl BlockHashReader for TestBalProvider {
1123 fn block_hash(&self, _number: BlockNumber) -> ProviderResult<Option<B256>> {
1124 Ok(None)
1125 }
1126
1127 fn canonical_hashes_range(
1128 &self,
1129 _start: BlockNumber,
1130 _end: BlockNumber,
1131 ) -> ProviderResult<Vec<B256>> {
1132 Ok(Vec::new())
1133 }
1134 }
1135
1136 impl BlockNumReader for TestBalProvider {
1137 fn chain_info(&self) -> ProviderResult<reth_chainspec::ChainInfo> {
1138 Ok(reth_chainspec::ChainInfo::default())
1139 }
1140
1141 fn best_block_number(&self) -> ProviderResult<BlockNumber> {
1142 Ok(0)
1143 }
1144
1145 fn last_block_number(&self) -> ProviderResult<BlockNumber> {
1146 Ok(0)
1147 }
1148
1149 fn block_number(&self, _hash: B256) -> ProviderResult<Option<BlockNumber>> {
1150 Ok(None)
1151 }
1152 }
1153
1154 impl HeaderProvider for TestBalProvider {
1155 type Header = Header;
1156
1157 fn header(&self, _block_hash: BlockHash) -> ProviderResult<Option<Self::Header>> {
1158 Ok(None)
1159 }
1160
1161 fn header_by_number(&self, _num: u64) -> ProviderResult<Option<Self::Header>> {
1162 Ok(None)
1163 }
1164
1165 fn headers_range(
1166 &self,
1167 _range: impl RangeBounds<BlockNumber>,
1168 ) -> ProviderResult<Vec<Self::Header>> {
1169 Ok(Vec::new())
1170 }
1171
1172 fn sealed_header(
1173 &self,
1174 _number: BlockNumber,
1175 ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
1176 Ok(None)
1177 }
1178
1179 fn sealed_headers_while(
1180 &self,
1181 _range: impl RangeBounds<BlockNumber>,
1182 _predicate: impl FnMut(&SealedHeader<Self::Header>) -> bool,
1183 ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
1184 Ok(Vec::new())
1185 }
1186 }
1187
1188 impl BlockBodyIndicesProvider for TestBalProvider {
1189 fn block_body_indices(&self, _num: u64) -> ProviderResult<Option<StoredBlockBodyIndices>> {
1190 Ok(None)
1191 }
1192
1193 fn block_body_indices_range(
1194 &self,
1195 _range: RangeInclusive<BlockNumber>,
1196 ) -> ProviderResult<Vec<StoredBlockBodyIndices>> {
1197 Ok(Vec::new())
1198 }
1199 }
1200
1201 impl TransactionsProvider for TestBalProvider {
1202 type Transaction = TransactionSigned;
1203
1204 fn transaction_id(&self, _tx_hash: TxHash) -> ProviderResult<Option<TxNumber>> {
1205 Ok(None)
1206 }
1207
1208 fn transaction_by_id(&self, _id: TxNumber) -> ProviderResult<Option<Self::Transaction>> {
1209 Ok(None)
1210 }
1211
1212 fn transaction_by_id_unhashed(
1213 &self,
1214 _id: TxNumber,
1215 ) -> ProviderResult<Option<Self::Transaction>> {
1216 Ok(None)
1217 }
1218
1219 fn transaction_by_hash(&self, _hash: TxHash) -> ProviderResult<Option<Self::Transaction>> {
1220 Ok(None)
1221 }
1222
1223 fn transaction_by_hash_with_meta(
1224 &self,
1225 _hash: TxHash,
1226 ) -> ProviderResult<Option<(Self::Transaction, TransactionMeta)>> {
1227 Ok(None)
1228 }
1229
1230 fn transactions_by_block(
1231 &self,
1232 _block: BlockHashOrNumber,
1233 ) -> ProviderResult<Option<Vec<Self::Transaction>>> {
1234 Ok(None)
1235 }
1236
1237 fn transactions_by_block_range(
1238 &self,
1239 _range: impl RangeBounds<BlockNumber>,
1240 ) -> ProviderResult<Vec<Vec<Self::Transaction>>> {
1241 Ok(Vec::new())
1242 }
1243
1244 fn transactions_by_tx_range(
1245 &self,
1246 _range: impl RangeBounds<TxNumber>,
1247 ) -> ProviderResult<Vec<Self::Transaction>> {
1248 Ok(Vec::new())
1249 }
1250
1251 fn senders_by_tx_range(
1252 &self,
1253 _range: impl RangeBounds<TxNumber>,
1254 ) -> ProviderResult<Vec<Address>> {
1255 Ok(Vec::new())
1256 }
1257
1258 fn transaction_sender(&self, _id: TxNumber) -> ProviderResult<Option<Address>> {
1259 Ok(None)
1260 }
1261 }
1262
1263 impl ReceiptProvider for TestBalProvider {
1264 type Receipt = Receipt;
1265
1266 fn receipt(&self, _id: TxNumber) -> ProviderResult<Option<Self::Receipt>> {
1267 Ok(None)
1268 }
1269
1270 fn receipt_by_hash(&self, _hash: TxHash) -> ProviderResult<Option<Self::Receipt>> {
1271 Ok(None)
1272 }
1273
1274 fn receipts_by_block(
1275 &self,
1276 _block: BlockHashOrNumber,
1277 ) -> ProviderResult<Option<Vec<Self::Receipt>>> {
1278 Ok(None)
1279 }
1280
1281 fn receipts_by_tx_range(
1282 &self,
1283 _range: impl RangeBounds<TxNumber>,
1284 ) -> ProviderResult<Vec<Self::Receipt>> {
1285 Ok(Vec::new())
1286 }
1287
1288 fn receipts_by_block_range(
1289 &self,
1290 _block_range: RangeInclusive<BlockNumber>,
1291 ) -> ProviderResult<Vec<Vec<Self::Receipt>>> {
1292 Ok(Vec::new())
1293 }
1294 }
1295
1296 impl BlockReader for TestBalProvider {
1297 type Block = Block;
1298
1299 fn find_block_by_hash(
1300 &self,
1301 _hash: B256,
1302 _source: BlockSource,
1303 ) -> ProviderResult<Option<Self::Block>> {
1304 Ok(None)
1305 }
1306
1307 fn block(&self, _id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
1308 Ok(None)
1309 }
1310
1311 fn pending_block(&self) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
1312 Ok(None)
1313 }
1314
1315 fn pending_block_and_receipts(
1316 &self,
1317 ) -> ProviderResult<Option<(RecoveredBlock<Self::Block>, Vec<Self::Receipt>)>> {
1318 Ok(None)
1319 }
1320
1321 fn recovered_block(
1322 &self,
1323 _id: BlockHashOrNumber,
1324 _transaction_kind: TransactionVariant,
1325 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
1326 Ok(None)
1327 }
1328
1329 fn sealed_block_with_senders(
1330 &self,
1331 _id: BlockHashOrNumber,
1332 _transaction_kind: TransactionVariant,
1333 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
1334 Ok(self.block.clone())
1335 }
1336
1337 fn block_range(
1338 &self,
1339 _range: RangeInclusive<BlockNumber>,
1340 ) -> ProviderResult<Vec<Self::Block>> {
1341 Ok(Vec::new())
1342 }
1343
1344 fn block_with_senders_range(
1345 &self,
1346 _range: RangeInclusive<BlockNumber>,
1347 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
1348 Ok(Vec::new())
1349 }
1350
1351 fn recovered_block_range(
1352 &self,
1353 _range: RangeInclusive<BlockNumber>,
1354 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
1355 Ok(Vec::new())
1356 }
1357
1358 fn block_by_transaction_id(&self, _id: TxNumber) -> ProviderResult<Option<BlockNumber>> {
1359 Ok(None)
1360 }
1361 }
1362}