reth_rpc_eth_api/helpers/
pending_block.rs1use super::SpawnBlocking;
5use crate::{types::RpcTypes, EthApiTypes, FromEthApiError, FromEvmError, RpcNodeCore};
6use alloy_consensus::{BlockHeader, Transaction};
7use alloy_eips::eip7840::BlobParams;
8use alloy_rpc_types_eth::BlockNumberOrTag;
9use futures::Future;
10use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
11use reth_errors::{BlockExecutionError, BlockValidationError, ProviderError, RethError};
12use reth_evm::{
13 execute::{BlockBuilder, BlockBuilderOutcome},
14 ConfigureEvm, Evm, SpecFor,
15};
16use reth_node_api::NodePrimitives;
17use reth_primitives_traits::{
18 transaction::error::InvalidTransactionError, Receipt, RecoveredBlock, SealedHeader,
19};
20use reth_revm::{database::StateProviderDatabase, db::State};
21use reth_rpc_eth_types::{EthApiError, PendingBlock, PendingBlockEnv, PendingBlockEnvOrigin};
22use reth_storage_api::{
23 BlockReader, BlockReaderIdExt, ProviderBlock, ProviderHeader, ProviderReceipt, ProviderTx,
24 ReceiptProvider, StateProviderFactory,
25};
26use reth_transaction_pool::{
27 error::InvalidPoolTransactionError, BestTransactionsAttributes, PoolTransaction,
28 TransactionPool,
29};
30use revm::context_interface::Block;
31use std::time::{Duration, Instant};
32use tokio::sync::Mutex;
33use tracing::debug;
34
35pub trait LoadPendingBlock:
39 EthApiTypes<
40 NetworkTypes: RpcTypes<
41 Header = alloy_rpc_types_eth::Header<ProviderHeader<Self::Provider>>,
42 >,
43 Error: FromEvmError<Self::Evm>,
44 > + RpcNodeCore<
45 Provider: BlockReaderIdExt<Receipt: Receipt>
46 + ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks>
47 + StateProviderFactory,
48 Evm: ConfigureEvm<
49 Primitives: NodePrimitives<
50 BlockHeader = ProviderHeader<Self::Provider>,
51 SignedTx = ProviderTx<Self::Provider>,
52 Receipt = ProviderReceipt<Self::Provider>,
53 Block = ProviderBlock<Self::Provider>,
54 >,
55 >,
56 >
57{
58 #[expect(clippy::type_complexity)]
62 fn pending_block(
63 &self,
64 ) -> &Mutex<Option<PendingBlock<ProviderBlock<Self::Provider>, ProviderReceipt<Self::Provider>>>>;
65
66 #[expect(clippy::type_complexity)]
70 fn pending_block_env_and_cfg(
71 &self,
72 ) -> Result<
73 PendingBlockEnv<
74 ProviderBlock<Self::Provider>,
75 ProviderReceipt<Self::Provider>,
76 SpecFor<Self::Evm>,
77 >,
78 Self::Error,
79 > {
80 if let Some(block) =
81 self.provider().pending_block_with_senders().map_err(Self::Error::from_eth_err)?
82 {
83 if let Some(receipts) = self
84 .provider()
85 .receipts_by_block(block.hash().into())
86 .map_err(Self::Error::from_eth_err)?
87 {
88 let evm_env = self.evm_config().evm_env(block.header());
92
93 return Ok(PendingBlockEnv::new(
94 evm_env,
95 PendingBlockEnvOrigin::ActualPending(block, receipts),
96 ));
97 }
98 }
99
100 let latest = self
103 .provider()
104 .latest_header()
105 .map_err(Self::Error::from_eth_err)?
106 .ok_or(EthApiError::HeaderNotFound(BlockNumberOrTag::Latest.into()))?;
107
108 let evm_env = self
109 .evm_config()
110 .next_evm_env(&latest, &self.next_env_attributes(&latest)?)
111 .map_err(RethError::other)
112 .map_err(Self::Error::from_eth_err)?;
113
114 Ok(PendingBlockEnv::new(evm_env, PendingBlockEnvOrigin::DerivedFromLatest(latest)))
115 }
116
117 fn next_env_attributes(
119 &self,
120 parent: &SealedHeader<ProviderHeader<Self::Provider>>,
121 ) -> Result<<Self::Evm as ConfigureEvm>::NextBlockEnvCtx, Self::Error>;
122
123 #[expect(clippy::type_complexity)]
125 fn local_pending_block(
126 &self,
127 ) -> impl Future<
128 Output = Result<
129 Option<(
130 RecoveredBlock<<Self::Provider as BlockReader>::Block>,
131 Vec<ProviderReceipt<Self::Provider>>,
132 )>,
133 Self::Error,
134 >,
135 > + Send
136 where
137 Self: SpawnBlocking,
138 Self::Pool:
139 TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>>,
140 {
141 async move {
142 let pending = self.pending_block_env_and_cfg()?;
143 let parent = match pending.origin {
144 PendingBlockEnvOrigin::ActualPending(block, receipts) => {
145 return Ok(Some((block, receipts)));
146 }
147 PendingBlockEnvOrigin::DerivedFromLatest(parent) => parent,
148 };
149
150 let mut lock = self.pending_block().lock().await;
152
153 let now = Instant::now();
154
155 if let Some(pending_block) = lock.as_ref() {
157 if pending.evm_env.block_env.number == pending_block.block.number() &&
159 parent.hash() == pending_block.block.parent_hash() &&
160 now <= pending_block.expires_at
161 {
162 return Ok(Some((pending_block.block.clone(), pending_block.receipts.clone())));
163 }
164 }
165
166 let (sealed_block, receipts) = match self
168 .spawn_blocking_io(move |this| {
169 this.build_block(&parent)
171 })
172 .await
173 {
174 Ok(block) => block,
175 Err(err) => {
176 debug!(target: "rpc", "Failed to build pending block: {:?}", err);
177 return Ok(None)
178 }
179 };
180
181 let now = Instant::now();
182 *lock = Some(PendingBlock::new(
183 now + Duration::from_secs(1),
184 sealed_block.clone(),
185 receipts.clone(),
186 ));
187
188 Ok(Some((sealed_block, receipts)))
189 }
190 }
191
192 #[expect(clippy::type_complexity)]
199 fn build_block(
200 &self,
201 parent: &SealedHeader<ProviderHeader<Self::Provider>>,
202 ) -> Result<
203 (RecoveredBlock<ProviderBlock<Self::Provider>>, Vec<ProviderReceipt<Self::Provider>>),
204 Self::Error,
205 >
206 where
207 Self::Pool:
208 TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>>,
209 EthApiError: From<ProviderError>,
210 {
211 let state_provider = self
212 .provider()
213 .history_by_block_hash(parent.hash())
214 .map_err(Self::Error::from_eth_err)?;
215 let state = StateProviderDatabase::new(&state_provider);
216 let mut db = State::builder().with_database(state).with_bundle_update().build();
217
218 let mut builder = self
219 .evm_config()
220 .builder_for_next_block(&mut db, parent, self.next_env_attributes(parent)?)
221 .map_err(RethError::other)
222 .map_err(Self::Error::from_eth_err)?;
223
224 builder.apply_pre_execution_changes().map_err(Self::Error::from_eth_err)?;
225
226 let block_env = builder.evm_mut().block().clone();
227
228 let blob_params = self
229 .provider()
230 .chain_spec()
231 .blob_params_at_timestamp(parent.timestamp())
232 .unwrap_or_else(BlobParams::cancun);
233 let mut cumulative_gas_used = 0;
234 let mut sum_blob_gas_used = 0;
235 let block_gas_limit: u64 = block_env.gas_limit;
236
237 let mut best_txs =
238 self.pool().best_transactions_with_attributes(BestTransactionsAttributes::new(
239 block_env.basefee,
240 block_env.blob_gasprice().map(|gasprice| gasprice as u64),
241 ));
242
243 while let Some(pool_tx) = best_txs.next() {
244 if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit {
246 best_txs.mark_invalid(
250 &pool_tx,
251 InvalidPoolTransactionError::ExceedsGasLimit(
252 pool_tx.gas_limit(),
253 block_gas_limit,
254 ),
255 );
256 continue
257 }
258
259 if pool_tx.origin.is_private() {
260 best_txs.mark_invalid(
264 &pool_tx,
265 InvalidPoolTransactionError::Consensus(
266 InvalidTransactionError::TxTypeNotSupported,
267 ),
268 );
269 continue
270 }
271
272 let tx = pool_tx.to_consensus();
274
275 if let Some(tx_blob_gas) = tx.blob_gas_used() {
278 if sum_blob_gas_used + tx_blob_gas > blob_params.max_blob_gas_per_block() {
279 best_txs.mark_invalid(
284 &pool_tx,
285 InvalidPoolTransactionError::ExceedsGasLimit(
286 tx_blob_gas,
287 blob_params.max_blob_gas_per_block(),
288 ),
289 );
290 continue
291 }
292 }
293
294 let gas_used = match builder.execute_transaction(tx.clone()) {
295 Ok(gas_used) => gas_used,
296 Err(BlockExecutionError::Validation(BlockValidationError::InvalidTx {
297 error,
298 ..
299 })) => {
300 if error.is_nonce_too_low() {
301 } else {
303 best_txs.mark_invalid(
306 &pool_tx,
307 InvalidPoolTransactionError::Consensus(
308 InvalidTransactionError::TxTypeNotSupported,
309 ),
310 );
311 }
312 continue
313 }
314 Err(err) => return Err(Self::Error::from_eth_err(err)),
316 };
317
318 if let Some(tx_blob_gas) = tx.blob_gas_used() {
320 sum_blob_gas_used += tx_blob_gas;
321
322 if sum_blob_gas_used == blob_params.max_blob_gas_per_block() {
324 best_txs.skip_blobs();
325 }
326 }
327
328 cumulative_gas_used += gas_used;
330 }
331
332 let BlockBuilderOutcome { execution_result, block, .. } =
333 builder.finish(&state_provider).map_err(Self::Error::from_eth_err)?;
334
335 Ok((block, execution_result.receipts))
336 }
337}