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;
8use alloy_primitives::{B256, U256};
9use alloy_rpc_types_eth::{BlockNumberOrTag, BlockOverrides};
10use futures::Future;
11use reth_chain_state::{BlockState, 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::{transaction::error::InvalidTransactionError, HeaderTy, SealedHeader};
20use reth_revm::{database::StateProviderDatabase, db::State};
21use reth_rpc_convert::RpcConvert;
22use reth_rpc_eth_types::{
23 block::BlockAndReceipts, builder::config::PendingBlockKind, EthApiError, PendingBlock,
24 PendingBlockEnv, PendingBlockEnvOrigin,
25};
26use reth_storage_api::{
27 noop::NoopProvider, BlockReader, BlockReaderIdExt, ProviderHeader, ProviderTx,
28 StateProviderBox, StateProviderFactory,
29};
30use reth_transaction_pool::{
31 error::InvalidPoolTransactionError, BestTransactions, BestTransactionsAttributes,
32 PoolTransaction, TransactionPool,
33};
34use reth_trie_common::ComputedTrieData;
35use revm::context_interface::{Block, Cfg as _};
36use std::{
37 sync::Arc,
38 time::{Duration, Instant},
39};
40use tokio::sync::Mutex;
41use tracing::debug;
42
43pub trait LoadPendingBlock:
47 EthApiTypes<
48 Error: FromEvmError<Self::Evm>,
49 RpcConvert: RpcConvert<Network = Self::NetworkTypes>,
50 > + RpcNodeCore
51{
52 fn pending_block(&self) -> &Mutex<Option<PendingBlock<Self::Primitives>>>;
56
57 fn pending_env_builder(&self) -> &dyn PendingEnvBuilder<Self::Evm>;
59
60 fn pending_block_kind(&self) -> PendingBlockKind {
62 self.eth_api_settings().pending_block_kind
63 }
64
65 fn pending_block_env_and_cfg(&self) -> Result<PendingBlockEnv<Self::Evm>, Self::Error> {
69 if let Some((block, receipts)) =
70 self.provider().pending_block_and_receipts().map_err(Self::Error::from_eth_err)?
71 {
72 let evm_env = self
76 .evm_config()
77 .evm_env(block.header())
78 .map_err(RethError::other)
79 .map_err(Self::Error::from_eth_err)?;
80
81 return Ok(PendingBlockEnv::new(
82 evm_env,
83 PendingBlockEnvOrigin::ActualPending(Arc::new(block), Arc::new(receipts)),
84 ));
85 }
86
87 let latest = self
90 .provider()
91 .latest_header()
92 .map_err(Self::Error::from_eth_err)?
93 .ok_or(EthApiError::HeaderNotFound(BlockNumberOrTag::Latest.into()))?;
94
95 let evm_env = self
96 .evm_config()
97 .next_evm_env(&latest, &self.next_env_attributes(&latest)?)
98 .map_err(RethError::other)
99 .map_err(Self::Error::from_eth_err)?;
100
101 Ok(PendingBlockEnv::new(evm_env, PendingBlockEnvOrigin::DerivedFromLatest(latest)))
102 }
103
104 fn next_env_attributes(
106 &self,
107 parent: &SealedHeader<ProviderHeader<Self::Provider>>,
108 ) -> Result<<Self::Evm as ConfigureEvm>::NextBlockEnvCtx, Self::Error> {
109 Ok(self.pending_env_builder().pending_env_attributes(parent, None)?)
110 }
111
112 fn local_pending_state(
114 &self,
115 ) -> impl Future<Output = Result<Option<StateProviderBox>, Self::Error>> + Send
116 where
117 Self: SpawnBlocking,
118 {
119 async move {
120 let Some(pending_block) = self.pool_pending_block().await? else {
121 return Ok(None);
122 };
123
124 let latest_historical = self
125 .provider()
126 .history_by_block_hash(pending_block.block().parent_hash())
127 .map_err(Self::Error::from_eth_err)?;
128
129 let state = BlockState::from(pending_block);
130
131 Ok(Some(Box::new(state.state_provider(latest_historical)) as StateProviderBox))
132 }
133 }
134
135 fn pool_pending_block(
137 &self,
138 ) -> impl Future<Output = Result<Option<PendingBlock<Self::Primitives>>, Self::Error>> + Send
139 where
140 Self: SpawnBlocking,
141 {
142 async move {
143 if self.pending_block_kind().is_none() {
144 return Ok(None);
145 }
146 let pending = self.pending_block_env_and_cfg()?;
147 let parent = match pending.origin {
148 PendingBlockEnvOrigin::ActualPending(..) => return Ok(None),
149 PendingBlockEnvOrigin::DerivedFromLatest(parent) => parent,
150 };
151
152 self.build_pool_pending_block(parent, pending.evm_env).await
153 }
154 }
155
156 fn build_pool_pending_block(
161 &self,
162 parent: SealedHeader<ProviderHeader<Self::Provider>>,
163 evm_env: EvmEnvFor<Self::Evm>,
164 ) -> impl Future<Output = Result<Option<PendingBlock<Self::Primitives>>, Self::Error>> + Send
165 where
166 Self: SpawnBlocking,
167 {
168 async move {
169 let mut lock = self.pending_block().lock().await;
171
172 let now = Instant::now();
173
174 if let Some(pending_block) = lock.as_ref() {
176 if evm_env.block_env.number() == U256::from(pending_block.block().number()) &&
178 parent.hash() == pending_block.block().parent_hash() &&
179 now <= pending_block.expires_at
180 {
181 return Ok(Some(pending_block.clone()));
182 }
183 }
184
185 let executed_block = match self
186 .spawn_blocking_io(move |this| {
187 this.build_block(&parent)
189 })
190 .await
191 {
192 Ok(block) => block,
193 Err(err) => {
194 debug!(target: "rpc", "Failed to build pending block: {:?}", err);
195 return Ok(None)
196 }
197 };
198
199 let pending = PendingBlock::with_executed_block(
200 Instant::now() + Duration::from_secs(1),
201 executed_block,
202 );
203
204 *lock = Some(pending.clone());
205
206 Ok(Some(pending))
207 }
208 }
209
210 fn local_pending_block(
212 &self,
213 ) -> impl Future<Output = Result<Option<BlockAndReceipts<Self::Primitives>>, Self::Error>> + Send
214 where
215 Self: SpawnBlocking,
216 Self::Pool:
217 TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>>,
218 {
219 async move {
220 if self.pending_block_kind().is_none() {
221 return Ok(None);
222 }
223
224 let pending = self.pending_block_env_and_cfg()?;
225
226 Ok(match pending.origin {
227 PendingBlockEnvOrigin::ActualPending(block, receipts) => {
228 Some(BlockAndReceipts { block, receipts })
229 }
230 PendingBlockEnvOrigin::DerivedFromLatest(parent) => self
231 .build_pool_pending_block(parent, pending.evm_env)
232 .await?
233 .map(PendingBlock::into_block_and_receipts),
234 })
235 }
236 }
237
238 fn build_block(
246 &self,
247 parent: &SealedHeader<ProviderHeader<Self::Provider>>,
248 ) -> Result<ExecutedBlock<Self::Primitives>, Self::Error>
249 where
250 Self::Pool:
251 TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>>,
252 EthApiError: From<ProviderError>,
253 {
254 let state_provider = self
255 .provider()
256 .history_by_block_hash(parent.hash())
257 .map_err(Self::Error::from_eth_err)?;
258 let state = StateProviderDatabase::new(state_provider);
259 let mut db = State::builder().with_database(state).with_bundle_update().build();
260
261 let mut builder = self
262 .evm_config()
263 .builder_for_next_block(&mut db, parent, self.next_env_attributes(parent)?)
264 .map_err(RethError::other)
265 .map_err(Self::Error::from_eth_err)?;
266
267 builder.apply_pre_execution_changes().map_err(Self::Error::from_eth_err)?;
268
269 let block_gas_limit: u64 = builder.evm().block().gas_limit();
270 let is_amsterdam = self
271 .provider()
272 .chain_spec()
273 .is_amsterdam_active_at_timestamp(builder.evm().block().timestamp().saturating_to());
274 let basefee = builder.evm().block().basefee();
275 let blob_gasprice = builder.evm().block().blob_gasprice().map(|p| p as u64);
276
277 let blob_params = self
278 .provider()
279 .chain_spec()
280 .blob_params_at_timestamp(parent.timestamp())
281 .unwrap_or_else(BlobParams::cancun);
282 let mut cumulative_tx_gas_used = 0;
283 let mut block_regular_gas_used = 0;
284 let mut block_state_gas_used = 0;
285 let mut sum_blob_gas_used = 0;
286 let tx_gas_limit_cap = builder.evm().cfg_env().tx_gas_limit_cap();
287
288 if !self.pending_block_kind().is_empty() {
290 let mut best_txs = self
291 .pool()
292 .best_transactions_with_attributes(BestTransactionsAttributes::new(
293 basefee,
294 blob_gasprice,
295 ))
296 .without_updates();
298
299 while let Some(pool_tx) = best_txs.next() {
300 let exceeds_gas_limit = if is_amsterdam {
302 let regular_available_gas =
303 block_gas_limit.saturating_sub(block_regular_gas_used);
304 let state_available_gas = block_gas_limit.saturating_sub(block_state_gas_used);
305 let regular_tx_gas_limit = pool_tx.gas_limit().min(tx_gas_limit_cap);
306
307 if regular_tx_gas_limit > regular_available_gas {
308 Some((regular_tx_gas_limit, regular_available_gas))
309 } else if pool_tx.gas_limit() > state_available_gas {
310 Some((pool_tx.gas_limit(), state_available_gas))
311 } else {
312 None
313 }
314 } else {
315 let block_available_gas =
316 block_gas_limit.saturating_sub(cumulative_tx_gas_used);
317 (pool_tx.gas_limit() > block_available_gas)
318 .then_some((pool_tx.gas_limit(), block_available_gas))
319 };
320
321 if let Some((transaction_gas_limit, block_available_gas)) = exceeds_gas_limit {
322 best_txs.mark_invalid(
326 &pool_tx,
327 InvalidPoolTransactionError::ExceedsGasLimit(
328 transaction_gas_limit,
329 block_available_gas,
330 ),
331 );
332 continue
333 }
334
335 if pool_tx.origin.is_private() {
336 best_txs.mark_invalid(
340 &pool_tx,
341 InvalidPoolTransactionError::Consensus(
342 InvalidTransactionError::TxTypeNotSupported,
343 ),
344 );
345 continue
346 }
347
348 let tx = pool_tx.to_consensus();
350
351 let tx_blob_gas = tx.blob_gas_used();
354 if let Some(tx_blob_gas) = tx_blob_gas &&
355 sum_blob_gas_used + tx_blob_gas > blob_params.max_blob_gas_per_block()
356 {
357 best_txs.mark_invalid(
362 &pool_tx,
363 InvalidPoolTransactionError::ExceedsGasLimit(
364 tx_blob_gas,
365 blob_params.max_blob_gas_per_block(),
366 ),
367 );
368 continue
369 }
370
371 let mut tx_regular_gas_used = 0;
372 let gas_output =
373 match builder.execute_transaction_with_result_closure(tx, |result| {
374 tx_regular_gas_used = result.result().result.gas().block_regular_gas_used();
375 }) {
376 Ok(gas_output) => gas_output,
377 Err(BlockExecutionError::Validation(BlockValidationError::InvalidTx {
378 error,
379 ..
380 })) => {
381 if error.is_nonce_too_low() {
382 } else {
384 best_txs.mark_invalid(
387 &pool_tx,
388 InvalidPoolTransactionError::Consensus(
389 InvalidTransactionError::TxTypeNotSupported,
390 ),
391 );
392 }
393 continue
394 }
395 Err(BlockExecutionError::Validation(
396 BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
397 transaction_gas_limit,
398 block_available_gas,
399 },
400 )) => {
401 best_txs.mark_invalid(
402 &pool_tx,
403 InvalidPoolTransactionError::ExceedsGasLimit(
404 transaction_gas_limit,
405 block_available_gas,
406 ),
407 );
408 continue
409 }
410 Err(err) => return Err(Self::Error::from_eth_err(err)),
412 };
413
414 if let Some(tx_blob_gas) = tx_blob_gas {
416 sum_blob_gas_used += tx_blob_gas;
417
418 if sum_blob_gas_used == blob_params.max_blob_gas_per_block() {
420 best_txs.skip_blobs();
421 }
422 }
423
424 let gas_used = gas_output.tx_gas_used();
426 cumulative_tx_gas_used += gas_used;
427 block_regular_gas_used += tx_regular_gas_used;
428 block_state_gas_used += gas_output.state_gas_used();
429 }
430 }
431
432 let BlockBuilderOutcome { execution_result, block, hashed_state, trie_updates, .. } =
433 builder.finish(NoopProvider::default(), None).map_err(Self::Error::from_eth_err)?;
434
435 let execution_outcome =
436 BlockExecutionOutput { state: db.take_bundle(), result: execution_result };
437
438 Ok(ExecutedBlock::new(
439 block.into(),
440 Arc::new(execution_outcome),
441 ComputedTrieData::new(
442 Arc::new(hashed_state.into_sorted()),
443 Arc::new(trie_updates.into_sorted()),
444 ),
445 ))
446 }
447}
448
449pub trait PendingEnvBuilder<Evm: ConfigureEvm>: Send + Sync + Unpin + 'static {
451 fn pending_env_attributes(
457 &self,
458 parent: &SealedHeader<HeaderTy<Evm::Primitives>>,
459 block_overrides: Option<&BlockOverrides>,
460 ) -> Result<Evm::NextBlockEnvCtx, EthApiError>;
461}
462
463pub trait BuildPendingEnv<Header> {
469 fn build_pending_env(
475 parent: &SealedHeader<Header>,
476 block_overrides: Option<&BlockOverrides>,
477 ) -> Self;
478}
479
480impl<Evm> PendingEnvBuilder<Evm> for ()
481where
482 Evm: ConfigureEvm<NextBlockEnvCtx: BuildPendingEnv<HeaderTy<Evm::Primitives>>>,
483{
484 fn pending_env_attributes(
485 &self,
486 parent: &SealedHeader<HeaderTy<Evm::Primitives>>,
487 block_overrides: Option<&BlockOverrides>,
488 ) -> Result<Evm::NextBlockEnvCtx, EthApiError> {
489 Ok(Evm::NextBlockEnvCtx::build_pending_env(parent, block_overrides))
490 }
491}
492
493impl<H: BlockHeader> BuildPendingEnv<H> for NextBlockEnvAttributes {
494 fn build_pending_env(
495 parent: &SealedHeader<H>,
496 block_overrides: Option<&BlockOverrides>,
497 ) -> Self {
498 let mut attributes = Self {
499 timestamp: parent.timestamp().saturating_add(12),
500 suggested_fee_recipient: parent.beneficiary(),
501 prev_randao: B256::random(),
502 gas_limit: parent.gas_limit(),
503 parent_beacon_block_root: parent.parent_beacon_block_root().map(|_| B256::ZERO),
504 withdrawals: parent.withdrawals_root().map(|_| Default::default()),
505 extra_data: parent.extra_data().clone(),
506 slot_number: parent.slot_number().map(|slot| slot.saturating_add(1)),
507 };
508
509 if attributes.parent_beacon_block_root.is_some() &&
510 let Some(beacon_root) = block_overrides.and_then(|overrides| overrides.beacon_root)
511 {
512 attributes.parent_beacon_block_root = Some(beacon_root);
513 }
514
515 attributes
516 }
517}
518
519#[cfg(test)]
520mod tests {
521 use super::*;
522 use alloy_consensus::Header;
523 use alloy_primitives::B256;
524 use reth_primitives_traits::SealedHeader;
525
526 #[test]
527 fn pending_env_defaults_parent_beacon_root() {
528 let mut header = Header::default();
529 let beacon_root = B256::repeat_byte(0x42);
530 header.parent_beacon_block_root = Some(beacon_root);
531 let sealed = SealedHeader::new(header, B256::ZERO);
532
533 let attrs = NextBlockEnvAttributes::build_pending_env(&sealed, None);
534
535 assert_eq!(attrs.parent_beacon_block_root, Some(B256::ZERO));
536 }
537
538 #[test]
539 fn pending_env_applies_parent_beacon_root_override() {
540 let header = Header { parent_beacon_block_root: Some(B256::ZERO), ..Default::default() };
541 let sealed = SealedHeader::new(header, B256::ZERO);
542 let beacon_root = B256::repeat_byte(0x42);
543 let block_overrides =
544 BlockOverrides { beacon_root: Some(beacon_root), ..Default::default() };
545
546 let attrs = NextBlockEnvAttributes::build_pending_env(&sealed, Some(&block_overrides));
547
548 assert_eq!(attrs.parent_beacon_block_root, Some(beacon_root));
549 }
550
551 #[test]
552 fn pending_env_ignores_parent_beacon_root_override_before_fork() {
553 let sealed = SealedHeader::new(Header::default(), B256::ZERO);
554 let beacon_root = B256::repeat_byte(0x42);
555 let block_overrides =
556 BlockOverrides { beacon_root: Some(beacon_root), ..Default::default() };
557
558 let attrs = NextBlockEnvAttributes::build_pending_env(&sealed, Some(&block_overrides));
559
560 assert_eq!(attrs.parent_beacon_block_root, None);
561 }
562
563 #[test]
564 fn pending_env_increments_parent_slot_number() {
565 let header = Header { slot_number: Some(7), ..Default::default() };
566 let sealed = SealedHeader::new(header, B256::ZERO);
567
568 let attrs = NextBlockEnvAttributes::build_pending_env(&sealed, None);
569
570 assert_eq!(attrs.slot_number, Some(8));
571 }
572}