From a56c45d4ccdac26217a494de6787a973a9a890c2 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 7 Nov 2024 05:46:21 +0400 Subject: [PATCH] refactor(rpc): unify system caller invocations --- crates/rpc/rpc-eth-api/src/helpers/trace.rs | 66 ++++++---- crates/rpc/rpc/src/debug.rs | 131 ++++++-------------- 2 files changed, 75 insertions(+), 122 deletions(-) diff --git a/crates/rpc/rpc-eth-api/src/helpers/trace.rs b/crates/rpc/rpc-eth-api/src/helpers/trace.rs index fa70b2df2efa..29bde5199601 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/trace.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/trace.rs @@ -1,6 +1,6 @@ //! Loads a pending block from database. Helper trait for `eth_` call and trace RPC methods. -use std::sync::Arc; +use std::{fmt::Display, sync::Arc}; use crate::{FromEvmError, RpcNodeCore}; use alloy_primitives::B256; @@ -16,7 +16,9 @@ use reth_rpc_eth_types::{ }; use revm::{db::CacheDB, Database, DatabaseCommit, GetInspector, Inspector}; use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig}; -use revm_primitives::{EnvWithHandlerCfg, EvmState, ExecutionResult, ResultAndState}; +use revm_primitives::{ + BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, EvmState, ExecutionResult, ResultAndState, +}; use super::{Call, LoadBlock, LoadPendingBlock, LoadState, LoadTransaction}; @@ -187,26 +189,13 @@ pub trait Trace: LoadState> { // we need to get the state of the parent block because we're essentially replaying the // block the transaction is included in let parent_block = block.parent_hash; - let parent_beacon_block_root = block.parent_beacon_block_root; let this = self.clone(); self.spawn_with_state_at_block(parent_block.into(), move |state| { let mut db = CacheDB::new(StateProviderDatabase::new(state)); let block_txs = block.transactions_with_sender(); - // apply relevant system calls - SystemCaller::new(this.evm_config().clone(), this.provider().chain_spec()) - .pre_block_beacon_root_contract_call( - &mut db, - &cfg, - &block_env, - parent_beacon_block_root, - ) - .map_err(|_| { - EthApiError::EvmCustom( - "failed to apply 4788 beacon root system call".to_string(), - ) - })?; + this.apply_pre_execution_changes(&block, &mut db, &cfg, &block_env)?; // replay all transactions prior to the targeted transaction this.replay_transactions_until( @@ -333,17 +322,7 @@ pub trait Trace: LoadState> { let mut db = CacheDB::new(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state))); - // apply relevant system calls - SystemCaller::new(this.evm_config().clone(), this.provider().chain_spec()) - .pre_block_beacon_root_contract_call( - &mut db, - &cfg, - &block_env, - block.header().parent_beacon_block_root, - ) - .map_err(|_| { - EthApiError::EvmCustom("failed to apply 4788 system call".to_string()) - })?; + this.apply_pre_execution_changes(&block, &mut db, &cfg, &block_env)?; // prepare transactions, we do everything upfront to reduce time spent with open // state @@ -470,4 +449,37 @@ pub trait Trace: LoadState> { { self.trace_block_until_with_inspector(block_id, block, None, insp_setup, f) } + + /// Applies chain-specific state transitions required before executing a block. + /// + /// Note: This should only be called when tracing an entire block vs individual transactions. + /// When tracing transaction on top of an already committed block state, those transitions are + /// already applied. + fn apply_pre_execution_changes + DatabaseCommit>( + &self, + block: &SealedBlockWithSenders, + db: &mut DB, + cfg: &CfgEnvWithHandlerCfg, + block_env: &BlockEnv, + ) -> Result<(), Self::Error> { + let mut system_caller = + SystemCaller::new(self.evm_config().clone(), self.provider().chain_spec()); + // apply relevant system calls + system_caller + .pre_block_beacon_root_contract_call( + db, + cfg, + block_env, + block.header.parent_beacon_block_root, + ) + .map_err(|_| EthApiError::EvmCustom("failed to apply 4788 system call".to_string()))?; + + system_caller + .pre_block_blockhashes_contract_call(db, cfg, block_env, block.header.parent_hash) + .map_err(|_| { + EthApiError::EvmCustom("failed to apply blockhashes system call".to_string()) + })?; + + Ok(()) + } } diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 7e4c8fb8230d..5b7e161691c9 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -16,10 +16,9 @@ use jsonrpsee::core::RpcResult; use reth_chainspec::EthereumHardforks; use reth_evm::{ execute::{BlockExecutorProvider, Executor}, - system_calls::SystemCaller, ConfigureEvmEnv, }; -use reth_primitives::{Block, TransactionSignedEcRecovered}; +use reth_primitives::{Block, SealedBlockWithSenders}; use reth_provider::{ BlockReaderIdExt, ChainSpecProvider, HeaderProvider, StateProofProvider, StateProviderFactory, TransactionVariant, @@ -93,54 +92,30 @@ where /// Trace the entire block asynchronously async fn trace_block( &self, - at: BlockId, - transactions: Vec, + block: Arc, cfg: CfgEnvWithHandlerCfg, block_env: BlockEnv, opts: GethDebugTracingOptions, - parent_beacon_block_root: Option, ) -> Result, Eth::Error> { - if transactions.is_empty() { - // nothing to trace - return Ok(Vec::new()) - } - // replay all transactions of the block let this = self.clone(); self.eth_api() - .spawn_with_state_at_block(at, move |state| { - let block_hash = at.as_block_hash(); - let mut results = Vec::with_capacity(transactions.len()); + .spawn_with_state_at_block(block.parent_hash.into(), move |state| { + let mut results = Vec::with_capacity(block.body.transactions.len()); let mut db = CacheDB::new(StateProviderDatabase::new(state)); - // apply relevant system calls - SystemCaller::new( - this.eth_api().evm_config().clone(), - this.eth_api().provider().chain_spec(), - ) - .pre_block_beacon_root_contract_call( - &mut db, - &cfg, - &block_env, - parent_beacon_block_root, - ) - .map_err(|_| { - EthApiError::EvmCustom( - "failed to apply 4788 beacon root system call".to_string(), - ) - })?; + this.eth_api().apply_pre_execution_changes(&block, &mut db, &cfg, &block_env)?; - let mut transactions = transactions.into_iter().enumerate().peekable(); + let mut transactions = block.transactions_with_sender().enumerate().peekable(); let mut inspector = None; - while let Some((index, tx)) = transactions.next() { + while let Some((index, (signer, tx))) = transactions.next() { let tx_hash = tx.hash; let env = EnvWithHandlerCfg { env: Env::boxed( cfg.cfg_env.clone(), block_env.clone(), - RpcNodeCore::evm_config(this.eth_api()) - .tx_env(tx.as_signed(), tx.signer()), + RpcNodeCore::evm_config(this.eth_api()).tx_env(tx, *signer), ), handler_cfg: cfg.handler_cfg, }; @@ -149,7 +124,7 @@ where env, &mut db, Some(TransactionContext { - block_hash, + block_hash: Some(block.hash()), tx_hash: Some(tx_hash), tx_index: Some(index), }), @@ -186,45 +161,38 @@ where .map_err(Eth::Error::from_eth_err)?; let (cfg, block_env) = self.eth_api().evm_env_for_raw_block(&block.header).await?; - // we trace on top the block's parent block - let parent = block.parent_hash; - - // we need the beacon block root for a system call - let parent_beacon_block_root = block.parent_beacon_block_root; // Depending on EIP-2 we need to recover the transactions differently - let transactions = - if self.inner.provider.chain_spec().is_homestead_active_at_block(block.number) { - block - .body - .transactions - .into_iter() - .map(|tx| { - tx.into_ecrecovered() - .ok_or(EthApiError::InvalidTransactionSignature) - .map_err(Eth::Error::from_eth_err) - }) - .collect::, Eth::Error>>()? - } else { - block - .body - .transactions - .into_iter() - .map(|tx| { - tx.into_ecrecovered_unchecked() - .ok_or(EthApiError::InvalidTransactionSignature) - .map_err(Eth::Error::from_eth_err) - }) - .collect::, Eth::Error>>()? - }; + let senders = if self.inner.provider.chain_spec().is_homestead_active_at_block(block.number) + { + block + .body + .transactions + .iter() + .map(|tx| { + tx.recover_signer() + .ok_or(EthApiError::InvalidTransactionSignature) + .map_err(Eth::Error::from_eth_err) + }) + .collect::, Eth::Error>>()? + } else { + block + .body + .transactions + .iter() + .map(|tx| { + tx.recover_signer_unchecked() + .ok_or(EthApiError::InvalidTransactionSignature) + .map_err(Eth::Error::from_eth_err) + }) + .collect::, Eth::Error>>()? + }; self.trace_block( - parent.into(), - transactions, + Arc::new(block.with_senders_unchecked(senders).seal_slow()), cfg, block_env, opts, - parent_beacon_block_root, ) .await } @@ -248,19 +216,8 @@ where )?; let block = block.ok_or(EthApiError::HeaderNotFound(block_id))?; - // we need to get the state of the parent block because we're replaying this block on top of - // its parent block's state - let state_at = block.parent_hash; - self.trace_block( - state_at.into(), - (*block).clone().into_transactions_ecrecovered().collect(), - cfg, - block_env, - opts, - block.parent_beacon_block_root, - ) - .await + self.trace_block(block, cfg, block_env, opts).await } /// Trace the transaction according to the provided options. @@ -281,7 +238,6 @@ where // block the transaction is included in let state_at: BlockId = block.parent_hash.into(); let block_hash = block.hash(); - let parent_beacon_block_root = block.parent_beacon_block_root; let this = self.clone(); self.eth_api() @@ -293,22 +249,7 @@ where let mut db = CacheDB::new(StateProviderDatabase::new(state)); - // apply relevant system calls - SystemCaller::new( - this.eth_api().evm_config().clone(), - this.eth_api().provider().chain_spec(), - ) - .pre_block_beacon_root_contract_call( - &mut db, - &cfg, - &block_env, - parent_beacon_block_root, - ) - .map_err(|_| { - EthApiError::EvmCustom( - "failed to apply 4788 beacon root system call".to_string(), - ) - })?; + this.eth_api().apply_pre_execution_changes(&block, &mut db, &cfg, &block_env)?; // replay all transactions prior to the targeted transaction let index = this.eth_api().replay_transactions_until(