reth_rpc_eth_api/helpers/
pending_block.rs1use super::SpawnBlocking;
5use crate::{EthApiTypes, FromEthApiError, FromEvmError, RpcNodeCore};
6use alloy_consensus::{BlockHeader, Transaction};
7use alloy_eips::{eip7840::BlobParams, BlockId};
8use alloy_primitives::B256;
9use alloy_rpc_types_eth::BlockOverrides;
10use futures::Future;
11use reth_chain_state::ExecutedBlock;
12use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
13use reth_errors::{BlockExecutionError, BlockValidationError, ProviderError, RethError};
14use reth_evm::{
15 database::StateProviderDatabase,
16 execute::{BlockBuilder, BlockBuilderOutcome, BlockExecutionOutput},
17 ConfigureEvm, EvmEnv, EvmEnvFor, NextBlockEnvAttributes,
18};
19use reth_primitives_traits::{
20 transaction::error::InvalidTransactionError, HeaderTy, NodePrimitives, SealedHeader,
21};
22use reth_rpc_convert::RpcConvert;
23use reth_rpc_eth_types::{
24 block::BlockAndReceipts, builder::config::PendingBlockKind, EthApiError, PendingBlock,
25 PendingBlockEnv, PendingBlockEnvOrigin,
26};
27use reth_storage_api::{
28 noop::NoopProvider, BlockReader, BlockReaderIdExt, ProviderHeader, ProviderTx, StateProvider,
29 StateProviderBox, StateProviderFactory,
30};
31use reth_tasks::cancel::is_cancelled;
32use reth_transaction_pool::{
33 error::InvalidPoolTransactionError, BestTransactions, BestTransactionsAttributes,
34 PoolTransaction, TransactionPool,
35};
36use reth_trie_common::SortedTrieData;
37use std::{
38 sync::Arc,
39 time::{Duration, Instant},
40};
41use tokio::sync::Mutex;
42use tracing::debug;
43
44pub trait LoadPendingBlock:
48 EthApiTypes<
49 Error: FromEvmError<Self::Evm>,
50 RpcConvert: RpcConvert<Network = Self::NetworkTypes>,
51 > + RpcNodeCore
52{
53 fn pending_block(&self) -> &Mutex<Option<PendingBlock<Self::Primitives>>>;
57
58 fn pending_env_builder(&self) -> &dyn PendingEnvBuilder<Self::Evm>;
60
61 fn pending_block_kind(&self) -> PendingBlockKind {
63 self.eth_api_settings().pending_block_kind
64 }
65
66 fn pending_block_env_and_cfg(&self) -> Result<PendingBlockEnv<Self::Evm>, Self::Error> {
71 if let Some(pending) =
72 self.provider().pending_block_and_receipts().map_err(Self::Error::from_eth_err)?
73 {
74 let (block, output) = pending.into_parts();
75
76 let evm_env = self
80 .evm_config()
81 .evm_env(block.header())
82 .map_err(RethError::other)
83 .map_err(Self::Error::from_eth_err)?;
84
85 return Ok(PendingBlockEnv::new(
86 evm_env,
87 PendingBlockEnvOrigin::ActualPending(block, output.into()),
88 ));
89 }
90
91 let latest = self
94 .provider()
95 .latest_header()
96 .map_err(Self::Error::from_eth_err)?
97 .ok_or(EthApiError::HeaderNotFound(BlockId::latest()))?;
98
99 let evm_env = self
100 .evm_config()
101 .next_evm_env(&latest, &self.next_env_attributes(&latest)?)
102 .map_err(RethError::other)
103 .map_err(Self::Error::from_eth_err)?;
104
105 Ok(PendingBlockEnv::new(evm_env, PendingBlockEnvOrigin::DerivedFromLatest(latest)))
106 }
107
108 fn next_env_attributes(
110 &self,
111 parent: &SealedHeader<ProviderHeader<Self::Provider>>,
112 ) -> Result<<Self::Evm as ConfigureEvm>::NextBlockEnvCtx, Self::Error> {
113 Ok(self.pending_env_builder().pending_env_attributes(parent, None)?)
114 }
115
116 fn local_pending_state(
122 &self,
123 ) -> impl Future<Output = Result<Option<StateProviderBox>, Self::Error>> + Send
124 where
125 Self: SpawnBlocking,
126 {
127 async move {
128 let Some(pending_block) = self.pool_pending_block().await? else {
129 return Ok(None);
130 };
131
132 self.spawn_blocking_io(move |this| {
133 Ok(Some(
134 this.provider()
135 .state_with_block_appended(
136 pending_block.block().parent_hash(),
137 pending_block.executed_block,
138 )
139 .map_err(Self::Error::from_eth_err)?,
140 ))
141 })
142 .await
143 }
144 }
145
146 fn local_pending_block_or_state(
153 &self,
154 ) -> impl Future<Output = Result<Option<PendingStateSource<Self::Primitives>>, Self::Error>> + Send
155 where
156 Self: SpawnBlocking,
157 {
158 async move { Ok(self.local_pending_state().await?.map(PendingStateSource::State)) }
159 }
160
161 fn pool_pending_block(
167 &self,
168 ) -> impl Future<Output = Result<Option<PendingBlock<Self::Primitives>>, Self::Error>> + Send
169 where
170 Self: SpawnBlocking,
171 {
172 async move {
173 if self.pending_block_kind().is_none() {
174 return Ok(None);
175 }
176 let pending = self.pending_block_env_and_cfg()?;
177 let parent = match pending.origin {
178 PendingBlockEnvOrigin::ActualPending(..) => return Ok(None),
179 PendingBlockEnvOrigin::DerivedFromLatest(parent) => parent,
180 };
181
182 self.build_pool_pending_block(parent, pending.evm_env).await
183 }
184 }
185
186 fn build_pool_pending_block(
191 &self,
192 parent: SealedHeader<ProviderHeader<Self::Provider>>,
193 evm_env: EvmEnvFor<Self::Evm>,
194 ) -> impl Future<Output = Result<Option<PendingBlock<Self::Primitives>>, Self::Error>> + Send
195 where
196 Self: SpawnBlocking,
197 {
198 async move {
199 let mut lock = self.pending_block().lock().await;
201
202 let now = Instant::now();
203
204 if let Some(pending_block) = lock.as_ref() {
206 if evm_env.block_env().number.to::<u64>() == pending_block.block().number() &&
208 parent.hash() == pending_block.block().parent_hash() &&
209 now <= pending_block.expires_at
210 {
211 return Ok(Some(pending_block.clone()));
212 }
213 }
214
215 let executed_block = match self
216 .spawn_blocking_io(move |this| {
217 this.build_block(&parent)
219 })
220 .await
221 {
222 Ok(block) => block,
223 Err(err) => {
224 debug!(target: "rpc", "Failed to build pending block: {:?}", err);
225 return Ok(None)
226 }
227 };
228
229 let pending = PendingBlock::with_executed_block(
230 Instant::now() + Duration::from_secs(1),
231 executed_block,
232 );
233
234 *lock = Some(pending.clone());
235
236 Ok(Some(pending))
237 }
238 }
239
240 fn local_pending_block(
242 &self,
243 ) -> impl Future<Output = Result<Option<BlockAndReceipts<Self::Primitives>>, Self::Error>> + Send
244 where
245 Self: SpawnBlocking,
246 Self::Pool:
247 TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>>,
248 {
249 async move {
250 if self.pending_block_kind().is_none() {
251 return Ok(None);
252 }
253
254 let pending = self.pending_block_env_and_cfg()?;
255
256 Ok(match pending.origin {
257 PendingBlockEnvOrigin::ActualPending(block, receipts) => {
258 Some(BlockAndReceipts { block, receipts })
259 }
260 PendingBlockEnvOrigin::DerivedFromLatest(parent) => self
261 .build_pool_pending_block(parent, pending.evm_env)
262 .await?
263 .map(PendingBlock::into_block_and_receipts),
264 })
265 }
266 }
267
268 fn build_block(
276 &self,
277 parent: &SealedHeader<ProviderHeader<Self::Provider>>,
278 ) -> Result<ExecutedBlock<Self::Primitives>, Self::Error>
279 where
280 Self::Pool:
281 TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>>,
282 EthApiError: From<ProviderError>,
283 {
284 let state_provider = self
285 .provider()
286 .history_by_block_hash(parent.hash())
287 .map_err(Self::Error::from_eth_err)?;
288 let state = StateProviderDatabase::new(state_provider.into_evm_state_provider());
289
290 let mut builder = self
291 .evm_config()
292 .builder_for_next_block(state, parent, self.next_env_attributes(parent)?)
293 .map_err(RethError::other)
294 .map_err(Self::Error::from_eth_err)?;
295
296 builder.apply_pre_execution_changes().map_err(Self::Error::from_eth_err)?;
297
298 let block_gas_limit = builder.evm_env().block_env().gas_limit.to::<u64>();
299 let is_amsterdam = builder.evm_env().uses_separate_block_gas();
300 let basefee = builder.evm_env().block_base_fee();
301 let blob_gasprice = self
302 .provider()
303 .chain_spec()
304 .is_cancun_active_at_timestamp(builder.evm_env().block_env().timestamp.to())
305 .then(|| builder.evm_env().block_blob_base_fee());
306
307 let blob_params = self
308 .provider()
309 .chain_spec()
310 .blob_params_at_timestamp(parent.timestamp())
311 .unwrap_or_else(BlobParams::cancun);
312 let mut cumulative_tx_gas_used = 0;
313 let mut block_regular_gas_used = 0;
314 let mut block_state_gas_used = 0;
315 let mut sum_blob_gas_used = 0;
316 let tx_gas_limit_cap = builder.evm_env().regular_gas_limit_cap();
317
318 if !self.pending_block_kind().is_empty() {
320 let mut best_txs = self
321 .pool()
322 .best_transactions_with_attributes(BestTransactionsAttributes::new(
323 basefee,
324 blob_gasprice,
325 ))
326 .without_updates();
328
329 while let Some(pool_tx) = best_txs.next() {
330 if is_cancelled() {
333 return Err(Self::Error::from_eth_err(EthApiError::InternalEthError))
334 }
335
336 let exceeds_gas_limit = if is_amsterdam {
338 let regular_available_gas =
339 block_gas_limit.saturating_sub(block_regular_gas_used);
340 let state_available_gas = block_gas_limit.saturating_sub(block_state_gas_used);
341 let regular_tx_gas_limit = pool_tx.gas_limit().min(tx_gas_limit_cap);
342
343 if regular_tx_gas_limit > regular_available_gas {
344 Some((regular_tx_gas_limit, regular_available_gas))
345 } else if pool_tx.gas_limit() > state_available_gas {
346 Some((pool_tx.gas_limit(), state_available_gas))
347 } else {
348 None
349 }
350 } else {
351 let block_available_gas =
352 block_gas_limit.saturating_sub(cumulative_tx_gas_used);
353 (pool_tx.gas_limit() > block_available_gas)
354 .then_some((pool_tx.gas_limit(), block_available_gas))
355 };
356
357 if let Some((transaction_gas_limit, block_available_gas)) = exceeds_gas_limit {
358 best_txs.mark_invalid(
362 &pool_tx,
363 InvalidPoolTransactionError::ExceedsGasLimit(
364 transaction_gas_limit,
365 block_available_gas,
366 ),
367 );
368 continue
369 }
370
371 if pool_tx.origin.is_private() {
372 best_txs.mark_invalid(
376 &pool_tx,
377 InvalidPoolTransactionError::Consensus(
378 InvalidTransactionError::TxTypeNotSupported,
379 ),
380 );
381 continue
382 }
383
384 let tx = pool_tx.to_consensus();
386
387 let tx_blob_gas = tx.blob_gas_used();
390 if let Some(tx_blob_gas) = tx_blob_gas &&
391 sum_blob_gas_used + tx_blob_gas > blob_params.max_blob_gas_per_block()
392 {
393 best_txs.mark_invalid(
398 &pool_tx,
399 InvalidPoolTransactionError::ExceedsGasLimit(
400 tx_blob_gas,
401 blob_params.max_blob_gas_per_block(),
402 ),
403 );
404 continue
405 }
406
407 let gas_output = match builder.execute_transaction(tx) {
408 Ok(gas_output) => gas_output,
409 Err(BlockExecutionError::Validation(BlockValidationError::InvalidTx {
410 error,
411 ..
412 })) => {
413 if error.is_nonce_too_low() {
414 } else {
416 best_txs.mark_invalid(
419 &pool_tx,
420 InvalidPoolTransactionError::Consensus(
421 InvalidTransactionError::TxTypeNotSupported,
422 ),
423 );
424 }
425 continue
426 }
427 Err(BlockExecutionError::Validation(
428 BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
429 transaction_gas_limit,
430 block_available_gas,
431 },
432 )) => {
433 best_txs.mark_invalid(
434 &pool_tx,
435 InvalidPoolTransactionError::ExceedsGasLimit(
436 transaction_gas_limit,
437 block_available_gas,
438 ),
439 );
440 continue
441 }
442 Err(err) => return Err(Self::Error::from_eth_err(err)),
444 };
445
446 if let Some(tx_blob_gas) = tx_blob_gas {
448 sum_blob_gas_used += tx_blob_gas;
449
450 if sum_blob_gas_used == blob_params.max_blob_gas_per_block() {
452 best_txs.skip_blobs();
453 }
454 }
455
456 let gas_used = gas_output.tx_gas_used();
458 cumulative_tx_gas_used += gas_used;
459 block_regular_gas_used += gas_output.regular_gas_used();
460 block_state_gas_used += gas_output.state_gas_used();
461 }
462 }
463
464 let BlockBuilderOutcome {
465 execution_result,
466 execution_state,
467 block,
468 hashed_state,
469 trie_updates,
470 ..
471 } = builder.finish(NoopProvider::default(), None).map_err(Self::Error::from_eth_err)?;
472
473 let execution_outcome =
474 BlockExecutionOutput { state: execution_state, result: execution_result };
475
476 Ok(ExecutedBlock::new(
477 block.into(),
478 Arc::new(execution_outcome),
479 SortedTrieData::new(
480 Arc::new(hashed_state.into_sorted()),
481 Arc::new(trie_updates.into_sorted()),
482 ),
483 ))
484 }
485}
486
487pub enum PendingStateSource<N: NodePrimitives> {
489 Block(PendingBlock<N>),
491 State(StateProviderBox),
493}
494
495impl<N: NodePrimitives> std::fmt::Debug for PendingStateSource<N> {
496 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
497 match self {
498 Self::Block(_) => f.write_str("PendingStateSource::Block(..)"),
499 Self::State(_) => f.write_str("PendingStateSource::State(..)"),
500 }
501 }
502}
503
504pub trait PendingEnvBuilder<Evm: ConfigureEvm>: Send + Sync + Unpin + 'static {
506 fn pending_env_attributes(
512 &self,
513 parent: &SealedHeader<HeaderTy<Evm::Primitives>>,
514 block_overrides: Option<&BlockOverrides>,
515 ) -> Result<Evm::NextBlockEnvCtx, EthApiError>;
516}
517
518pub trait BuildPendingEnv<Header> {
524 fn build_pending_env(
530 parent: &SealedHeader<Header>,
531 block_overrides: Option<&BlockOverrides>,
532 ) -> Self;
533}
534
535impl<Evm> PendingEnvBuilder<Evm> for ()
536where
537 Evm: ConfigureEvm<NextBlockEnvCtx: BuildPendingEnv<HeaderTy<Evm::Primitives>>>,
538{
539 fn pending_env_attributes(
540 &self,
541 parent: &SealedHeader<HeaderTy<Evm::Primitives>>,
542 block_overrides: Option<&BlockOverrides>,
543 ) -> Result<Evm::NextBlockEnvCtx, EthApiError> {
544 Ok(Evm::NextBlockEnvCtx::build_pending_env(parent, block_overrides))
545 }
546}
547
548impl<H: BlockHeader> BuildPendingEnv<H> for NextBlockEnvAttributes {
549 fn build_pending_env(
550 parent: &SealedHeader<H>,
551 block_overrides: Option<&BlockOverrides>,
552 ) -> Self {
553 let mut attributes = Self {
554 timestamp: block_overrides
557 .and_then(|overrides| overrides.time)
558 .unwrap_or_else(|| parent.timestamp().saturating_add(12)),
559 suggested_fee_recipient: parent.beneficiary(),
560 prev_randao: B256::random(),
561 gas_limit: parent.gas_limit(),
562 parent_beacon_block_root: parent.parent_beacon_block_root().map(|_| B256::ZERO),
563 withdrawals: parent.withdrawals_root().map(|_| Default::default()),
564 extra_data: parent.extra_data().clone(),
565 slot_number: parent.slot_number().map(|slot| slot.saturating_add(1)),
566 };
567
568 if attributes.parent_beacon_block_root.is_some() &&
569 let Some(beacon_root) = block_overrides.and_then(|overrides| overrides.beacon_root)
570 {
571 attributes.parent_beacon_block_root = Some(beacon_root);
572 }
573
574 attributes
575 }
576}
577
578#[cfg(test)]
579mod tests {
580 use super::*;
581 use alloy_consensus::Header;
582 use alloy_primitives::B256;
583 use reth_primitives_traits::SealedHeader;
584
585 #[test]
586 fn pending_env_defaults_parent_beacon_root() {
587 let mut header = Header::default();
588 let beacon_root = B256::repeat_byte(0x42);
589 header.parent_beacon_block_root = Some(beacon_root);
590 let sealed = SealedHeader::new(header, B256::ZERO);
591
592 let attrs = NextBlockEnvAttributes::build_pending_env(&sealed, None);
593
594 assert_eq!(attrs.parent_beacon_block_root, Some(B256::ZERO));
595 }
596
597 #[test]
598 fn pending_env_applies_parent_beacon_root_override() {
599 let header = Header { parent_beacon_block_root: Some(B256::ZERO), ..Default::default() };
600 let sealed = SealedHeader::new(header, B256::ZERO);
601 let beacon_root = B256::repeat_byte(0x42);
602 let block_overrides =
603 BlockOverrides { beacon_root: Some(beacon_root), ..Default::default() };
604
605 let attrs = NextBlockEnvAttributes::build_pending_env(&sealed, Some(&block_overrides));
606
607 assert_eq!(attrs.parent_beacon_block_root, Some(beacon_root));
608 }
609
610 #[test]
611 fn pending_env_ignores_parent_beacon_root_override_before_fork() {
612 let sealed = SealedHeader::new(Header::default(), B256::ZERO);
613 let beacon_root = B256::repeat_byte(0x42);
614 let block_overrides =
615 BlockOverrides { beacon_root: Some(beacon_root), ..Default::default() };
616
617 let attrs = NextBlockEnvAttributes::build_pending_env(&sealed, Some(&block_overrides));
618
619 assert_eq!(attrs.parent_beacon_block_root, None);
620 }
621
622 #[test]
623 fn pending_env_increments_parent_slot_number() {
624 let header = Header { slot_number: Some(7), ..Default::default() };
625 let sealed = SealedHeader::new(header, B256::ZERO);
626
627 let attrs = NextBlockEnvAttributes::build_pending_env(&sealed, None);
628
629 assert_eq!(attrs.slot_number, Some(8));
630 }
631}