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_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_rpc_types_compat::block::from_block;
16use reth_storage_api::{
17 BlockIdReader, BlockReader, BlockReaderIdExt, ProviderHeader, ProviderReceipt, ProviderTx,
18};
19use reth_transaction_pool::{PoolTransaction, TransactionPool};
20use std::sync::Arc;
21
22pub type BlockReceiptsResult<N, E> = Result<Option<Vec<RpcReceipt<N>>>, E>;
24pub type BlockAndReceiptsResult<Eth> = Result<
26 Option<(
27 SealedBlock<<<Eth as RpcNodeCore>::Provider as BlockReader>::Block>,
28 Arc<Vec<ProviderReceipt<<Eth as RpcNodeCore>::Provider>>>,
29 )>,
30 <Eth as EthApiTypes>::Error,
31>;
32
33pub trait EthBlocks: LoadBlock {
36 #[expect(clippy::type_complexity)]
38 fn rpc_block_header(
39 &self,
40 block_id: BlockId,
41 ) -> impl Future<Output = Result<Option<Header<ProviderHeader<Self::Provider>>>, Self::Error>> + Send
42 where
43 Self: FullEthApiTypes,
44 {
45 async move { Ok(self.rpc_block(block_id, false).await?.map(|block| block.header)) }
46 }
47
48 fn rpc_block(
53 &self,
54 block_id: BlockId,
55 full: bool,
56 ) -> impl Future<Output = Result<Option<RpcBlock<Self::NetworkTypes>>, Self::Error>> + Send
57 where
58 Self: FullEthApiTypes,
59 {
60 async move {
61 let Some(block) = self.recovered_block(block_id).await? else { return Ok(None) };
62
63 let block = from_block((*block).clone(), full.into(), self.tx_resp_builder())?;
64 Ok(Some(block))
65 }
66 }
67
68 fn block_transaction_count(
72 &self,
73 block_id: BlockId,
74 ) -> impl Future<Output = Result<Option<usize>, Self::Error>> + Send {
75 async move {
76 if block_id.is_pending() {
77 return Ok(self
79 .provider()
80 .pending_block()
81 .map_err(Self::Error::from_eth_err)?
82 .map(|block| block.body().transactions().len()))
83 }
84
85 let block_hash = match self
86 .provider()
87 .block_hash_for_id(block_id)
88 .map_err(Self::Error::from_eth_err)?
89 {
90 Some(block_hash) => block_hash,
91 None => return Ok(None),
92 };
93
94 Ok(self
95 .cache()
96 .get_recovered_block(block_hash)
97 .await
98 .map_err(Self::Error::from_eth_err)?
99 .map(|b| b.body().transaction_count()))
100 }
101 }
102
103 fn block_receipts(
107 &self,
108 block_id: BlockId,
109 ) -> impl Future<Output = BlockReceiptsResult<Self::NetworkTypes, Self::Error>> + Send
110 where
111 Self: LoadReceipt;
112
113 fn load_block_and_receipts(
115 &self,
116 block_id: BlockId,
117 ) -> impl Future<Output = BlockAndReceiptsResult<Self>> + Send
118 where
119 Self: LoadReceipt,
120 Self::Pool:
121 TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>>,
122 {
123 async move {
124 if block_id.is_pending() {
125 if let Some((block, receipts)) = self
128 .provider()
129 .pending_block_and_receipts()
130 .map_err(Self::Error::from_eth_err)?
131 {
132 return Ok(Some((block, Arc::new(receipts))));
133 }
134
135 if let Some((block, receipts)) = self.local_pending_block().await? {
137 return Ok(Some((block.into_sealed_block(), Arc::new(receipts))));
138 }
139 }
140
141 if let Some(block_hash) =
142 self.provider().block_hash_for_id(block_id).map_err(Self::Error::from_eth_err)?
143 {
144 return self
145 .cache()
146 .get_block_and_receipts(block_hash)
147 .await
148 .map_err(Self::Error::from_eth_err)
149 .map(|b| b.map(|(b, r)| (b.clone_sealed_block(), r)))
150 }
151
152 Ok(None)
153 }
154 }
155
156 #[expect(clippy::type_complexity)]
160 fn ommers(
161 &self,
162 block_id: BlockId,
163 ) -> Result<Option<Vec<ProviderHeader<Self::Provider>>>, Self::Error> {
164 self.provider().ommers_by_id(block_id).map_err(Self::Error::from_eth_err)
165 }
166
167 fn ommer_by_block_and_index(
171 &self,
172 block_id: BlockId,
173 index: Index,
174 ) -> impl Future<Output = Result<Option<RpcBlock<Self::NetworkTypes>>, Self::Error>> + Send
175 {
176 async move {
177 let uncles = if block_id.is_pending() {
178 self.provider()
180 .pending_block()
181 .map_err(Self::Error::from_eth_err)?
182 .and_then(|block| block.body().ommers().map(|o| o.to_vec()))
183 } else {
184 self.provider().ommers_by_id(block_id).map_err(Self::Error::from_eth_err)?
185 }
186 .unwrap_or_default();
187
188 Ok(uncles.into_iter().nth(index.into()).map(|header| {
189 let block = alloy_consensus::Block::<alloy_consensus::TxEnvelope, _>::uncle(header);
190 let size = U256::from(block.length());
191 Block {
192 uncles: vec![],
193 header: Header::from_consensus(block.header.seal_slow(), None, Some(size)),
194 transactions: BlockTransactions::Uncle,
195 withdrawals: None,
196 }
197 }))
198 }
199 }
200}
201
202pub trait LoadBlock:
206 LoadPendingBlock
207 + SpawnBlocking
208 + RpcNodeCoreExt<
209 Pool: TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>>,
210 >
211{
212 #[expect(clippy::type_complexity)]
214 fn recovered_block(
215 &self,
216 block_id: BlockId,
217 ) -> impl Future<
218 Output = Result<
219 Option<Arc<RecoveredBlock<<Self::Provider as BlockReader>::Block>>>,
220 Self::Error,
221 >,
222 > + Send {
223 async move {
224 if block_id.is_pending() {
225 if let Some(pending_block) = self
227 .provider()
228 .pending_block_with_senders()
229 .map_err(Self::Error::from_eth_err)?
230 {
231 return Ok(Some(Arc::new(pending_block)));
232 }
233
234 return match self.local_pending_block().await? {
236 Some((block, _)) => Ok(Some(Arc::new(block))),
237 None => Ok(None),
238 };
239 }
240
241 let block_hash = match self
242 .provider()
243 .block_hash_for_id(block_id)
244 .map_err(Self::Error::from_eth_err)?
245 {
246 Some(block_hash) => block_hash,
247 None => return Ok(None),
248 };
249
250 self.cache().get_recovered_block(block_hash).await.map_err(Self::Error::from_eth_err)
251 }
252 }
253}