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::{bal::DecodedBal, 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                let (bal, _) = DecodedBal::from_rlp_bytes(cached_bal.as_raw().clone())
36                    .map_err(RethError::other)
37                    .map_err(Self::Error::from_eth_err)?
38                    .split();
39                return Ok(Some(Vec::from(bal)))
40            }
41
42            self.spawn_blocking_io(move |eth_api| {
43                let state = eth_api
44                    .provider()
45                    .state_by_block_id(block.parent_hash().into())
46                    .map_err(Self::Error::from_eth_err)?;
47
48                let mut db = State::builder()
49                    .with_database(StateProviderDatabase::new(StateProviderTraitObjWrapper(state)))
50                    .with_bal_builder()
51                    .build();
52
53                let block_txs = block.transactions_recovered();
54                let mut executor = RpcNodeCore::evm_config(&eth_api)
55                    .executor_for_block(&mut db, block.sealed_block())
56                    .map_err(RethError::other)
57                    .map_err(Self::Error::from_eth_err)?;
58
59                executor.apply_pre_execution_changes().map_err(Self::Error::from_eth_err)?;
60                executor.evm_mut().db_mut().bump_bal_index();
61
62                // replay all transactions prior to the targeted transaction
63                for block_tx in block_txs {
64                    executor.execute_transaction(block_tx).map_err(Self::Error::from_eth_err)?;
65                    executor.evm_mut().db_mut().bump_bal_index();
66                }
67
68                executor
69                    .apply_post_execution_changes()
70                    .map_err(|err| EthApiError::Internal(err.into()))?;
71
72                let bal = db.take_built_alloy_bal();
73                Ok(bal)
74            })
75            .await
76        }
77    }
78
79    /// Retrieves the raw RLP-encoded block access list for a block.
80    fn get_raw_block_access_list(
81        &self,
82        block_id: BlockId,
83    ) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + Send {
84        async move {
85            let block = self
86                .recovered_block(block_id)
87                .await?
88                .ok_or_else(|| EthApiError::HeaderNotFound(block_id))?;
89
90            if let Some(cached_bal) =
91                self.cache().get_bal(block.hash()).await.map_err(Self::Error::from_eth_err)?
92            {
93                return Ok(Some(cached_bal.as_raw().clone()))
94            }
95
96            Ok(self.get_block_access_list(block_id).await?.map(|bal| alloy_rlp::encode(bal).into()))
97        }
98    }
99}