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() {
79 if let Some(pending) = self.local_pending_block().await? {
80 return Ok(Some(pending.block.body().transaction_count()));
81 }
82 return Ok(self
84 .provider()
85 .pending_block()
86 .map_err(Self::Error::from_eth_err)?
87 .map(|block| block.body().transaction_count()));
88 }
89
90 let block_hash = match self
91 .provider()
92 .block_hash_for_id(block_id)
93 .map_err(Self::Error::from_eth_err)?
94 {
95 Some(block_hash) => block_hash,
96 None => return Ok(None),
97 };
98
99 Ok(self
100 .cache()
101 .get_recovered_block(block_hash)
102 .await
103 .map_err(Self::Error::from_eth_err)?
104 .map(|b| b.body().transaction_count()))
105 }
106 }
107
108 fn block_receipts(
112 &self,
113 block_id: BlockId,
114 ) -> impl Future<Output = BlockReceiptsResult<Self::NetworkTypes, Self::Error>> + Send
115 where
116 Self: LoadReceipt,
117 {
118 async move {
119 if let Some((block, receipts)) = self.load_block_and_receipts(block_id).await? {
120 let block_number = block.number();
121 let base_fee = block.base_fee_per_gas();
122 let block_hash = block.hash();
123 let excess_blob_gas = block.excess_blob_gas();
124 let timestamp = block.timestamp();
125 let mut gas_used = 0;
126 let mut next_log_index = 0;
127
128 let inputs = block
129 .transactions_recovered()
130 .zip(Arc::unwrap_or_clone(receipts))
131 .enumerate()
132 .map(|(idx, (tx, receipt))| {
133 let meta = TransactionMeta {
134 tx_hash: *tx.tx_hash(),
135 index: idx as u64,
136 block_hash,
137 block_number,
138 base_fee,
139 excess_blob_gas,
140 timestamp,
141 };
142
143 let cumulative_gas_used = receipt.cumulative_gas_used();
144 let logs_len = receipt.logs().len();
145
146 let input = ConvertReceiptInput {
147 tx,
148 gas_used: cumulative_gas_used - gas_used,
149 next_log_index,
150 meta,
151 receipt,
152 };
153
154 gas_used = cumulative_gas_used;
155 next_log_index += logs_len;
156
157 input
158 })
159 .collect::<Vec<_>>();
160
161 return Ok(self
162 .converter()
163 .convert_receipts_with_block(inputs, block.sealed_block())
164 .map(Some)?)
165 }
166
167 Ok(None)
168 }
169 }
170
171 fn load_block_and_receipts(
173 &self,
174 block_id: BlockId,
175 ) -> impl Future<Output = BlockAndReceiptsResult<Self>> + Send
176 where
177 Self: LoadReceipt,
178 Self::Pool:
179 TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>>,
180 {
181 async move {
182 if block_id.is_pending() {
183 if let Some((block, receipts)) = self
186 .provider()
187 .pending_block_and_receipts()
188 .map_err(Self::Error::from_eth_err)?
189 {
190 return Ok(Some((Arc::new(block), Arc::new(receipts))));
191 }
192
193 if let Some(pending) = self.local_pending_block().await? {
195 return Ok(Some((pending.block, pending.receipts)));
196 }
197 }
198
199 if let Some(block_hash) =
200 self.provider().block_hash_for_id(block_id).map_err(Self::Error::from_eth_err)? &&
201 let Some((block, receipts)) = self
202 .cache()
203 .get_block_and_receipts(block_hash)
204 .await
205 .map_err(Self::Error::from_eth_err)?
206 {
207 return Ok(Some((block, receipts)));
208 }
209
210 Ok(None)
211 }
212 }
213
214 #[expect(clippy::type_complexity)]
218 fn ommers(
219 &self,
220 block_id: BlockId,
221 ) -> impl Future<Output = Result<Option<Vec<ProviderHeader<Self::Provider>>>, Self::Error>> + Send
222 {
223 async move {
224 if let Some(block) = self.recovered_block(block_id).await? {
225 Ok(block.body().ommers().map(|o| o.to_vec()))
226 } else {
227 Ok(None)
228 }
229 }
230 }
231
232 fn ommer_by_block_and_index(
236 &self,
237 block_id: BlockId,
238 index: Index,
239 ) -> impl Future<Output = Result<Option<RpcBlock<Self::NetworkTypes>>, Self::Error>> + Send
240 {
241 async move {
242 let uncles = if block_id.is_pending() {
243 self.provider()
245 .pending_block()
246 .map_err(Self::Error::from_eth_err)?
247 .and_then(|block| block.body().ommers().map(|o| o.to_vec()))
248 } else {
249 self.recovered_block(block_id)
250 .await?
251 .map(|block| block.body().ommers().map(|o| o.to_vec()).unwrap_or_default())
252 }
253 .unwrap_or_default();
254
255 uncles
256 .into_iter()
257 .nth(index.into())
258 .map(|header| {
259 let block =
260 alloy_consensus::Block::<alloy_consensus::TxEnvelope, _>::uncle(header);
261 let size = block.length();
262 let header = self
263 .converter()
264 .convert_header(SealedHeader::new_unhashed(block.header), size)?;
265 Ok(Block {
266 uncles: vec![],
267 header,
268 transactions: BlockTransactions::Uncle,
269 withdrawals: None,
270 })
271 })
272 .transpose()
273 }
274 }
275}
276
277pub trait LoadBlock: LoadPendingBlock + SpawnBlocking + RpcNodeCoreExt {
281 #[expect(clippy::type_complexity)]
283 fn recovered_block(
284 &self,
285 block_id: BlockId,
286 ) -> impl Future<
287 Output = Result<
288 Option<Arc<RecoveredBlock<<Self::Provider as BlockReader>::Block>>>,
289 Self::Error,
290 >,
291 > + Send {
292 async move {
293 if block_id.is_pending() {
294 if let Some(pending_block) =
296 self.provider().pending_block().map_err(Self::Error::from_eth_err)?
297 {
298 return Ok(Some(Arc::new(pending_block)));
299 }
300
301 return match self.local_pending_block().await? {
303 Some(pending) => Ok(Some(pending.block)),
304 None => Ok(None),
305 };
306 }
307
308 let block_hash = match self
309 .provider()
310 .block_hash_for_id(block_id)
311 .map_err(Self::Error::from_eth_err)?
312 {
313 Some(block_hash) => block_hash,
314 None => return Ok(None),
315 };
316
317 self.cache().get_recovered_block(block_hash).await.map_err(Self::Error::from_eth_err)
318 }
319 }
320}