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