reth_rpc_eth_api/helpers/
block.rs1use super::{LoadPendingBlock, LoadReceipt, SpawnBlocking};
4use crate::{
5 node::RpcNodeCoreExt, EthApiTypes, FromEthApiError, FullEthApiTypes, RpcBlock, RpcNodeCore,
6 RpcReceipt,
7};
8use alloy_consensus::{transaction::TxHashRef, TxReceipt};
9use alloy_eips::BlockId;
10use alloy_rlp::Encodable;
11use alloy_rpc_types_eth::{Block, BlockTransactions, Index};
12use futures::Future;
13use reth_node_api::BlockBody;
14use reth_primitives_traits::{AlloyBlockHeader, RecoveredBlock, SealedHeader, TransactionMeta};
15use reth_rpc_convert::{transaction::ConvertReceiptInput, RpcConvert, RpcHeader};
16use reth_storage_api::{BlockIdReader, BlockReader, ProviderHeader, ProviderReceipt, ProviderTx};
17use reth_transaction_pool::{PoolTransaction, TransactionPool};
18use std::sync::Arc;
19
20pub type BlockReceiptsResult<N, E> = Result<Option<Vec<RpcReceipt<N>>>, E>;
22pub type BlockAndReceiptsResult<Eth> = Result<
24 Option<(
25 Arc<RecoveredBlock<<<Eth as RpcNodeCore>::Provider as BlockReader>::Block>>,
26 Arc<Vec<ProviderReceipt<<Eth as RpcNodeCore>::Provider>>>,
27 )>,
28 <Eth as EthApiTypes>::Error,
29>;
30
31pub trait EthBlocks: LoadBlock<RpcConvert: RpcConvert<Primitives = Self::Primitives>> {
34 fn rpc_block_header(
36 &self,
37 block_id: BlockId,
38 ) -> impl Future<Output = Result<Option<RpcHeader<Self::NetworkTypes>>, Self::Error>> + Send
39 where
40 Self: FullEthApiTypes,
41 {
42 async move { Ok(self.rpc_block(block_id, false).await?.map(|block| block.header)) }
43 }
44
45 fn rpc_block(
50 &self,
51 block_id: BlockId,
52 full: bool,
53 ) -> impl Future<Output = Result<Option<RpcBlock<Self::NetworkTypes>>, Self::Error>> + Send
54 where
55 Self: FullEthApiTypes,
56 {
57 async move {
58 let Some(block) = self.recovered_block(block_id).await? else { return Ok(None) };
59
60 let block = block.clone_into_rpc_block(
61 full.into(),
62 |tx, tx_info| self.converter().fill(tx, tx_info),
63 |header, size| self.converter().convert_header(header, size),
64 )?;
65 Ok(Some(block))
66 }
67 }
68
69 fn block_transaction_count(
73 &self,
74 block_id: BlockId,
75 ) -> impl Future<Output = Result<Option<usize>, Self::Error>> + Send {
76 async move {
77 if block_id.is_pending() {
78 if self.pending_block_kind().is_none() {
79 return Ok(None);
80 }
81
82 if let Some(pending) = self.local_pending_block().await? {
84 return Ok(Some(pending.block.body().transaction_count()));
85 }
86 return Ok(self
88 .provider()
89 .pending_block()
90 .map_err(Self::Error::from_eth_err)?
91 .map(|block| block.body().transaction_count()));
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_recovered_block(block_hash)
106 .await
107 .map_err(Self::Error::from_eth_err)?
108 .map(|b| b.body().transaction_count()))
109 }
110 }
111
112 fn block_receipts(
116 &self,
117 block_id: BlockId,
118 ) -> impl Future<Output = BlockReceiptsResult<Self::NetworkTypes, Self::Error>> + Send
119 where
120 Self: LoadReceipt,
121 {
122 async move {
123 if let Some((block, receipts)) = self.load_block_and_receipts(block_id).await? {
124 let block_number = block.number();
125 let base_fee = block.base_fee_per_gas();
126 let block_hash = block.hash();
127 let excess_blob_gas = block.excess_blob_gas();
128 let timestamp = block.timestamp();
129 let mut gas_used = 0;
130 let mut next_log_index = 0;
131
132 let inputs = block
133 .transactions_recovered()
134 .zip(Arc::unwrap_or_clone(receipts))
135 .enumerate()
136 .map(|(idx, (tx, receipt))| {
137 let meta = TransactionMeta {
138 tx_hash: *tx.tx_hash(),
139 index: idx as u64,
140 block_hash,
141 block_number,
142 base_fee,
143 excess_blob_gas,
144 timestamp,
145 };
146
147 let cumulative_gas_used = receipt.cumulative_gas_used();
148 let logs_len = receipt.logs().len();
149
150 let input = ConvertReceiptInput {
151 tx,
152 gas_used: cumulative_gas_used - gas_used,
153 next_log_index,
154 meta,
155 receipt,
156 };
157
158 gas_used = cumulative_gas_used;
159 next_log_index += logs_len;
160
161 input
162 })
163 .collect::<Vec<_>>();
164
165 return Ok(self
166 .converter()
167 .convert_receipts_with_block(inputs, block.sealed_block())
168 .map(Some)?)
169 }
170
171 Ok(None)
172 }
173 }
174
175 fn load_block_and_receipts(
177 &self,
178 block_id: BlockId,
179 ) -> impl Future<Output = BlockAndReceiptsResult<Self>> + Send
180 where
181 Self: LoadReceipt,
182 Self::Pool:
183 TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>>,
184 {
185 async move {
186 if block_id.is_pending() {
187 if self.pending_block_kind().is_none() {
188 return Ok(None);
189 }
190
191 if let Some((block, receipts)) = self
194 .provider()
195 .pending_block_and_receipts()
196 .map_err(Self::Error::from_eth_err)?
197 {
198 return Ok(Some((Arc::new(block), Arc::new(receipts))));
199 }
200
201 if let Some(pending) = self.local_pending_block().await? {
203 return Ok(Some((pending.block, pending.receipts)));
204 }
205 }
206
207 if let Some(block_hash) =
208 self.provider().block_hash_for_id(block_id).map_err(Self::Error::from_eth_err)? &&
209 let Some((block, receipts)) = self
210 .cache()
211 .get_block_and_receipts(block_hash)
212 .await
213 .map_err(Self::Error::from_eth_err)?
214 {
215 return Ok(Some((block, receipts)));
216 }
217
218 Ok(None)
219 }
220 }
221
222 #[expect(clippy::type_complexity)]
226 fn ommers(
227 &self,
228 block_id: BlockId,
229 ) -> impl Future<Output = Result<Option<Vec<ProviderHeader<Self::Provider>>>, Self::Error>> + Send
230 {
231 async move {
232 if let Some(block) = self.recovered_block(block_id).await? {
233 Ok(block.body().ommers().map(|o| o.to_vec()))
234 } else {
235 Ok(None)
236 }
237 }
238 }
239
240 fn ommer_by_block_and_index(
244 &self,
245 block_id: BlockId,
246 index: Index,
247 ) -> impl Future<Output = Result<Option<RpcBlock<Self::NetworkTypes>>, Self::Error>> + Send
248 {
249 async move {
250 let uncles = self
251 .recovered_block(block_id)
252 .await?
253 .map(|block| block.body().ommers().map(|o| o.to_vec()).unwrap_or_default())
254 .unwrap_or_default();
255
256 uncles
257 .into_iter()
258 .nth(index.into())
259 .map(|header| {
260 let block =
261 alloy_consensus::Block::<alloy_consensus::TxEnvelope, _>::uncle(header);
262 let size = block.length();
263 let header = self
264 .converter()
265 .convert_header(SealedHeader::new_unhashed(block.header), size)?;
266 Ok(Block {
267 uncles: vec![],
268 header,
269 transactions: BlockTransactions::Uncle,
270 withdrawals: None,
271 })
272 })
273 .transpose()
274 }
275 }
276}
277
278pub trait LoadBlock: LoadPendingBlock + SpawnBlocking + RpcNodeCoreExt {
282 #[expect(clippy::type_complexity)]
284 fn recovered_block(
285 &self,
286 block_id: BlockId,
287 ) -> impl Future<
288 Output = Result<
289 Option<Arc<RecoveredBlock<<Self::Provider as BlockReader>::Block>>>,
290 Self::Error,
291 >,
292 > + Send {
293 async move {
294 if block_id.is_pending() {
295 if self.pending_block_kind().is_none() {
296 return Ok(None);
297 }
298
299 if let Some(pending_block) =
301 self.provider().pending_block().map_err(Self::Error::from_eth_err)?
302 {
303 return Ok(Some(Arc::new(pending_block)));
304 }
305
306 return match self.local_pending_block().await? {
308 Some(pending) => Ok(Some(pending.block)),
309 None => Ok(None),
310 };
311 }
312
313 let block_hash = match self
314 .provider()
315 .block_hash_for_id(block_id)
316 .map_err(Self::Error::from_eth_err)?
317 {
318 Some(block_hash) => block_hash,
319 None => return Ok(None),
320 };
321
322 self.cache().get_recovered_block(block_hash).await.map_err(Self::Error::from_eth_err)
323 }
324 }
325}