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