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