reth_rpc_eth_api/helpers/
block.rs

1//! Database access for `eth_` block RPC methods. Loads block and receipt data w.r.t. network.
2
3use super::{LoadPendingBlock, LoadReceipt, SpawnBlocking};
4use crate::{
5    node::RpcNodeCoreExt, EthApiTypes, FromEthApiError, FullEthApiTypes, RpcBlock, RpcNodeCore,
6    RpcReceipt,
7};
8use alloy_eips::BlockId;
9use alloy_primitives::{Sealable, U256};
10use alloy_rlp::Encodable;
11use alloy_rpc_types_eth::{Block, BlockTransactions, Header, Index};
12use futures::Future;
13use reth_node_api::BlockBody;
14use reth_primitives_traits::{RecoveredBlock, SealedBlock};
15use reth_provider::{
16    BlockIdReader, BlockReader, BlockReaderIdExt, ProviderHeader, ProviderReceipt,
17};
18use reth_rpc_types_compat::block::from_block;
19use std::sync::Arc;
20
21/// Result type of the fetched block receipts.
22pub type BlockReceiptsResult<N, E> = Result<Option<Vec<RpcReceipt<N>>>, E>;
23/// Result type of the fetched block and its receipts.
24pub type BlockAndReceiptsResult<Eth> = Result<
25    Option<(
26        SealedBlock<<<Eth as RpcNodeCore>::Provider as BlockReader>::Block>,
27        Arc<Vec<ProviderReceipt<<Eth as RpcNodeCore>::Provider>>>,
28    )>,
29    <Eth as EthApiTypes>::Error,
30>;
31
32/// Block related functions for the [`EthApiServer`](crate::EthApiServer) trait in the
33/// `eth_` namespace.
34pub trait EthBlocks: LoadBlock {
35    /// Returns the block header for the given block id.
36    #[expect(clippy::type_complexity)]
37    fn rpc_block_header(
38        &self,
39        block_id: BlockId,
40    ) -> impl Future<Output = Result<Option<Header<ProviderHeader<Self::Provider>>>, Self::Error>> + Send
41    where
42        Self: FullEthApiTypes,
43    {
44        async move { Ok(self.rpc_block(block_id, false).await?.map(|block| block.header)) }
45    }
46
47    /// Returns the populated rpc block object for the given block id.
48    ///
49    /// If `full` is true, the block object will contain all transaction objects, otherwise it will
50    /// only contain the transaction hashes.
51    fn rpc_block(
52        &self,
53        block_id: BlockId,
54        full: bool,
55    ) -> impl Future<Output = Result<Option<RpcBlock<Self::NetworkTypes>>, Self::Error>> + Send
56    where
57        Self: FullEthApiTypes,
58    {
59        async move {
60            let Some(block) = self.recovered_block(block_id).await? else { return Ok(None) };
61
62            let block = from_block((*block).clone(), full.into(), self.tx_resp_builder())?;
63            Ok(Some(block))
64        }
65    }
66
67    /// Returns the number transactions in the given block.
68    ///
69    /// Returns `None` if the block does not exist
70    fn block_transaction_count(
71        &self,
72        block_id: BlockId,
73    ) -> impl Future<Output = Result<Option<usize>, Self::Error>> + Send {
74        async move {
75            if block_id.is_pending() {
76                // Pending block can be fetched directly without need for caching
77                return Ok(self
78                    .provider()
79                    .pending_block()
80                    .map_err(Self::Error::from_eth_err)?
81                    .map(|block| block.body().transactions().len()))
82            }
83
84            let block_hash = match self
85                .provider()
86                .block_hash_for_id(block_id)
87                .map_err(Self::Error::from_eth_err)?
88            {
89                Some(block_hash) => block_hash,
90                None => return Ok(None),
91            };
92
93            Ok(self
94                .cache()
95                .get_recovered_block(block_hash)
96                .await
97                .map_err(Self::Error::from_eth_err)?
98                .map(|b| b.body().transaction_count()))
99        }
100    }
101
102    /// Helper function for `eth_getBlockReceipts`.
103    ///
104    /// Returns all transaction receipts in block, or `None` if block wasn't found.
105    fn block_receipts(
106        &self,
107        block_id: BlockId,
108    ) -> impl Future<Output = BlockReceiptsResult<Self::NetworkTypes, Self::Error>> + Send
109    where
110        Self: LoadReceipt;
111
112    /// Helper method that loads a block and all its receipts.
113    fn load_block_and_receipts(
114        &self,
115        block_id: BlockId,
116    ) -> impl Future<Output = BlockAndReceiptsResult<Self>> + Send
117    where
118        Self: LoadReceipt,
119    {
120        async move {
121            if block_id.is_pending() {
122                // First, try to get the pending block from the provider, in case we already
123                // received the actual pending block from the CL.
124                if let Some((block, receipts)) = self
125                    .provider()
126                    .pending_block_and_receipts()
127                    .map_err(Self::Error::from_eth_err)?
128                {
129                    return Ok(Some((block, Arc::new(receipts))));
130                }
131
132                // If no pending block from provider, build the pending block locally.
133                if let Some((block, receipts)) = self.local_pending_block().await? {
134                    return Ok(Some((block.into_sealed_block(), Arc::new(receipts))));
135                }
136            }
137
138            if let Some(block_hash) =
139                self.provider().block_hash_for_id(block_id).map_err(Self::Error::from_eth_err)?
140            {
141                return self
142                    .cache()
143                    .get_block_and_receipts(block_hash)
144                    .await
145                    .map_err(Self::Error::from_eth_err)
146                    .map(|b| b.map(|(b, r)| (b.clone_sealed_block(), r)))
147            }
148
149            Ok(None)
150        }
151    }
152
153    /// Returns uncle headers of given block.
154    ///
155    /// Returns an empty vec if there are none.
156    #[expect(clippy::type_complexity)]
157    fn ommers(
158        &self,
159        block_id: BlockId,
160    ) -> Result<Option<Vec<ProviderHeader<Self::Provider>>>, Self::Error> {
161        self.provider().ommers_by_id(block_id).map_err(Self::Error::from_eth_err)
162    }
163
164    /// Returns uncle block at given index in given block.
165    ///
166    /// Returns `None` if index out of range.
167    fn ommer_by_block_and_index(
168        &self,
169        block_id: BlockId,
170        index: Index,
171    ) -> impl Future<Output = Result<Option<RpcBlock<Self::NetworkTypes>>, Self::Error>> + Send
172    {
173        async move {
174            let uncles = if block_id.is_pending() {
175                // Pending block can be fetched directly without need for caching
176                self.provider()
177                    .pending_block()
178                    .map_err(Self::Error::from_eth_err)?
179                    .and_then(|block| block.body().ommers().map(|o| o.to_vec()))
180            } else {
181                self.provider().ommers_by_id(block_id).map_err(Self::Error::from_eth_err)?
182            }
183            .unwrap_or_default();
184
185            Ok(uncles.into_iter().nth(index.into()).map(|header| {
186                let block = alloy_consensus::Block::<alloy_consensus::TxEnvelope, _>::uncle(header);
187                let size = U256::from(block.length());
188                Block {
189                    uncles: vec![],
190                    header: Header::from_consensus(block.header.seal_slow(), None, Some(size)),
191                    transactions: BlockTransactions::Uncle,
192                    withdrawals: None,
193                }
194            }))
195        }
196    }
197}
198
199/// Loads a block from database.
200///
201/// Behaviour shared by several `eth_` RPC methods, not exclusive to `eth_` blocks RPC methods.
202pub trait LoadBlock: LoadPendingBlock + SpawnBlocking + RpcNodeCoreExt {
203    /// Returns the block object for the given block id.
204    #[expect(clippy::type_complexity)]
205    fn recovered_block(
206        &self,
207        block_id: BlockId,
208    ) -> impl Future<
209        Output = Result<
210            Option<Arc<RecoveredBlock<<Self::Provider as BlockReader>::Block>>>,
211            Self::Error,
212        >,
213    > + Send {
214        async move {
215            if block_id.is_pending() {
216                // Pending block can be fetched directly without need for caching
217                if let Some(pending_block) = self
218                    .provider()
219                    .pending_block_with_senders()
220                    .map_err(Self::Error::from_eth_err)?
221                {
222                    return Ok(Some(Arc::new(pending_block)));
223                }
224
225                // If no pending block from provider, try to get local pending block
226                return match self.local_pending_block().await? {
227                    Some((block, _)) => Ok(Some(Arc::new(block))),
228                    None => Ok(None),
229                };
230            }
231
232            let block_hash = match self
233                .provider()
234                .block_hash_for_id(block_id)
235                .map_err(Self::Error::from_eth_err)?
236            {
237                Some(block_hash) => block_hash,
238                None => return Ok(None),
239            };
240
241            self.cache().get_recovered_block(block_hash).await.map_err(Self::Error::from_eth_err)
242        }
243    }
244}