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