Skip to main content

reth_rpc_eth_api/helpers/
bal.rs

1//! Helpers for `eth_blockAccessList` RPC method.
2use alloy_consensus::BlockHeader;
3use alloy_eip7928::BlockAccessList;
4use alloy_primitives::Bytes;
5use alloy_rpc_types_eth::BlockId;
6use reth_errors::RethError;
7use reth_evm::{block::BlockExecutor, ConfigureEvm, Evm};
8use reth_revm::{database::StateProviderDatabase, State};
9use reth_rpc_eth_types::{
10    cache::db::StateProviderTraitObjWrapper, error::FromEthApiError, EthApiError,
11};
12use reth_storage_api::StateProviderFactory;
13
14use crate::{
15    helpers::{Call, LoadBlock, Trace},
16    RpcNodeCore, RpcNodeCoreExt,
17};
18
19/// Helper trait for `eth_blockAccessList` RPC method.
20pub trait GetBlockAccessList: Trace + Call + LoadBlock + RpcNodeCoreExt {
21    /// Retrieves the block access list for a block identified by its hash.
22    fn get_block_access_list(
23        &self,
24        block_id: BlockId,
25    ) -> impl Future<Output = Result<Option<BlockAccessList>, Self::Error>> + Send {
26        async move {
27            let block = self
28                .recovered_block(block_id)
29                .await?
30                .ok_or_else(|| EthApiError::HeaderNotFound(block_id))?;
31
32            if let Some(cached_bal) =
33                self.cache().get_bal(block.hash()).await.map_err(Self::Error::from_eth_err)?
34            {
35                return Ok(Some(cached_bal.as_bal().clone().into_alloy_bal()))
36            }
37
38            self.spawn_blocking_io(move |eth_api| {
39                let state = eth_api
40                    .provider()
41                    .state_by_block_id(block.parent_hash().into())
42                    .map_err(Self::Error::from_eth_err)?;
43
44                let mut db = State::builder()
45                    .with_database(StateProviderDatabase::new(StateProviderTraitObjWrapper(state)))
46                    .with_bal_builder()
47                    .build();
48
49                let block_txs = block.transactions_recovered();
50                let mut executor = RpcNodeCore::evm_config(&eth_api)
51                    .executor_for_block(&mut db, block.sealed_block())
52                    .map_err(RethError::other)
53                    .map_err(Self::Error::from_eth_err)?;
54
55                executor.apply_pre_execution_changes().map_err(Self::Error::from_eth_err)?;
56                executor.evm_mut().db_mut().bump_bal_index();
57
58                // replay all transactions prior to the targeted transaction
59                for block_tx in block_txs {
60                    executor.execute_transaction(block_tx).map_err(Self::Error::from_eth_err)?;
61                    executor.evm_mut().db_mut().bump_bal_index();
62                }
63
64                executor
65                    .apply_post_execution_changes()
66                    .map_err(|err| EthApiError::Internal(err.into()))?;
67
68                let bal = db.take_built_alloy_bal();
69                Ok(bal)
70            })
71            .await
72        }
73    }
74
75    /// Retrieves the raw RLP-encoded block access list for a block.
76    fn get_raw_block_access_list(
77        &self,
78        block_id: BlockId,
79    ) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + Send {
80        async move {
81            let block = self
82                .recovered_block(block_id)
83                .await?
84                .ok_or_else(|| EthApiError::HeaderNotFound(block_id))?;
85
86            if let Some(cached_bal) =
87                self.cache().get_bal(block.hash()).await.map_err(Self::Error::from_eth_err)?
88            {
89                return Ok(Some(cached_bal.as_raw().clone()))
90            }
91
92            Ok(self.get_block_access_list(block_id).await?.map(|bal| alloy_rlp::encode(bal).into()))
93        }
94    }
95}