reth_rpc_eth_api/helpers/
block.rs
1use 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
21pub type BlockReceiptsResult<N, E> = Result<Option<Vec<RpcReceipt<N>>>, E>;
23pub 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
32pub trait EthBlocks: LoadBlock {
35 #[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 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 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 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 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 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 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 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 #[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 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 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
199pub trait LoadBlock: LoadPendingBlock + SpawnBlocking + RpcNodeCoreExt {
203 #[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 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 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}