|
1 | 1 | //! Contains RPC handler implementations specific to tracing. |
2 | 2 |
|
3 | | -use reth_evm::ConfigureEvm; |
4 | | -use reth_primitives::Header; |
5 | | -use reth_rpc_eth_api::helpers::{LoadState, Trace}; |
6 | | - |
7 | 3 | use crate::EthApi; |
| 4 | +use alloy_eips::BlockId; |
| 5 | +use alloy_rpc_types_eth::TransactionInfo; |
| 6 | +use reth_bsc_primitives::system_contracts::is_system_transaction; |
| 7 | +use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks}; |
| 8 | +use reth_evm::{system_calls::SystemCaller, ConfigureEvm}; |
| 9 | +use reth_primitives::{Header, SealedBlockWithSenders}; |
| 10 | +use reth_revm::database::StateProviderDatabase; |
| 11 | +use reth_rpc_eth_api::helpers::{LoadBlock, LoadState, SpawnBlocking, Trace}; |
| 12 | +use reth_rpc_eth_types::{ |
| 13 | + cache::db::{StateCacheDbRefMutWrapper, StateProviderTraitObjWrapper}, |
| 14 | + EthApiError, StateCacheDb, |
| 15 | +}; |
| 16 | +use revm::{db::CacheDB, Inspector}; |
| 17 | +use revm_primitives::{ |
| 18 | + db::DatabaseCommit, EnvWithHandlerCfg, EvmState, ExecutionResult, ResultAndState, |
| 19 | +}; |
| 20 | +use std::{future::Future, sync::Arc}; |
8 | 21 |
|
9 | | -impl<Provider, Pool, Network, EvmConfig> Trace for EthApi<Provider, Pool, Network, EvmConfig> where |
10 | | - Self: LoadState<Evm: ConfigureEvm<Header = Header>> |
| 22 | +impl<Provider, Pool, Network, EvmConfig> Trace for EthApi<Provider, Pool, Network, EvmConfig> |
| 23 | +where |
| 24 | + Self: LoadState<Evm: ConfigureEvm<Header = Header>>, |
| 25 | + Provider: ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks>, |
| 26 | + EvmConfig: ConfigureEvm<Header = Header>, |
11 | 27 | { |
| 28 | + fn trace_block_until_with_inspector<Setup, Insp, F, R>( |
| 29 | + &self, |
| 30 | + block_id: BlockId, |
| 31 | + block: Option<Arc<SealedBlockWithSenders>>, |
| 32 | + highest_index: Option<u64>, |
| 33 | + mut inspector_setup: Setup, |
| 34 | + f: F, |
| 35 | + ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send |
| 36 | + where |
| 37 | + Self: LoadBlock, |
| 38 | + F: Fn( |
| 39 | + TransactionInfo, |
| 40 | + u64, // tx gas limit |
| 41 | + Insp, |
| 42 | + ExecutionResult, |
| 43 | + &EvmState, |
| 44 | + &StateCacheDb<'_>, |
| 45 | + ) -> Result<R, Self::Error> |
| 46 | + + Send |
| 47 | + + 'static, |
| 48 | + Setup: FnMut() -> Insp + Send + 'static, |
| 49 | + Insp: for<'a, 'b> Inspector<StateCacheDbRefMutWrapper<'a, 'b>> + Send + 'static, |
| 50 | + R: Send + 'static, |
| 51 | + { |
| 52 | + async move { |
| 53 | + let block = async { |
| 54 | + if block.is_some() { |
| 55 | + return Ok(block) |
| 56 | + } |
| 57 | + self.block_with_senders(block_id).await |
| 58 | + }; |
| 59 | + |
| 60 | + let ((cfg, block_env, _), block) = |
| 61 | + futures::try_join!(self.evm_env_at(block_id), block)?; |
| 62 | + |
| 63 | + let Some(block) = block else { return Ok(None) }; |
| 64 | + |
| 65 | + if block.body.transactions.is_empty() { |
| 66 | + // nothing to trace |
| 67 | + return Ok(Some(Vec::new())) |
| 68 | + } |
| 69 | + |
| 70 | + let parent_timestamp = self |
| 71 | + .block_with_senders(block.parent_hash.into()) |
| 72 | + .await? |
| 73 | + .ok_or(EthApiError::HeaderNotFound(block.parent_hash.into()))? |
| 74 | + .timestamp; |
| 75 | + |
| 76 | + // replay all transactions of the block |
| 77 | + self.spawn_tracing(move |this| { |
| 78 | + // we need to get the state of the parent block because we're replaying this block |
| 79 | + // on top of its parent block's state |
| 80 | + let state_at = block.parent_hash; |
| 81 | + let block_hash = block.hash(); |
| 82 | + |
| 83 | + let block_number = block_env.number.saturating_to::<u64>(); |
| 84 | + let base_fee = block_env.basefee.saturating_to::<u128>(); |
| 85 | + |
| 86 | + // now get the state |
| 87 | + let state = this.state_at_block_id(state_at.into())?; |
| 88 | + let mut db = |
| 89 | + CacheDB::new(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state))); |
| 90 | + |
| 91 | + // apply relevant system calls |
| 92 | + SystemCaller::new(this.evm_config().clone(), this.provider().chain_spec()) |
| 93 | + .pre_block_beacon_root_contract_call( |
| 94 | + &mut db, |
| 95 | + &cfg, |
| 96 | + &block_env, |
| 97 | + block.header().parent_beacon_block_root, |
| 98 | + ) |
| 99 | + .map_err(|_| { |
| 100 | + EthApiError::EvmCustom("failed to apply 4788 system call".to_string()) |
| 101 | + })?; |
| 102 | + |
| 103 | + // prepare transactions, we do everything upfront to reduce time spent with open |
| 104 | + // state |
| 105 | + let max_transactions = |
| 106 | + highest_index.map_or(block.body.transactions.len(), |highest| { |
| 107 | + // we need + 1 because the index is 0-based |
| 108 | + highest as usize + 1 |
| 109 | + }); |
| 110 | + let mut results = Vec::with_capacity(max_transactions); |
| 111 | + |
| 112 | + let mut transactions = block |
| 113 | + .transactions_with_sender() |
| 114 | + .take(max_transactions) |
| 115 | + .enumerate() |
| 116 | + .map(|(idx, (signer, tx))| { |
| 117 | + let tx_info = TransactionInfo { |
| 118 | + hash: Some(tx.hash()), |
| 119 | + index: Some(idx as u64), |
| 120 | + block_hash: Some(block_hash), |
| 121 | + block_number: Some(block_number), |
| 122 | + base_fee: Some(base_fee), |
| 123 | + }; |
| 124 | + (tx_info, signer, tx) |
| 125 | + }) |
| 126 | + .peekable(); |
| 127 | + |
| 128 | + let is_bsc = this.bsc_trace_helper.is_some(); |
| 129 | + let mut before_system_tx = is_bsc; |
| 130 | + |
| 131 | + // try to upgrade system contracts for bsc before all txs if feynman is not active |
| 132 | + if is_bsc { |
| 133 | + if let Some(trace_helper) = this.bsc_trace_helper.as_ref() { |
| 134 | + trace_helper |
| 135 | + .upgrade_system_contracts(&mut db, &block_env, parent_timestamp, true) |
| 136 | + .map_err(|e| e.into())?; |
| 137 | + } |
| 138 | + } |
| 139 | + |
| 140 | + while let Some((tx_info, sender, tx)) = transactions.next() { |
| 141 | + // check if the transaction is a system transaction |
| 142 | + // this should be done before return |
| 143 | + if is_bsc && |
| 144 | + before_system_tx && |
| 145 | + is_system_transaction(tx, *sender, block_env.coinbase) |
| 146 | + { |
| 147 | + if let Some(trace_helper) = this.bsc_trace_helper.as_ref() { |
| 148 | + // move block reward from the system address to the coinbase |
| 149 | + trace_helper |
| 150 | + .add_block_reward(&mut db, &block_env) |
| 151 | + .map_err(|e| e.into())?; |
| 152 | + |
| 153 | + // try to upgrade system contracts between normal txs and system txs |
| 154 | + // if feynman is active |
| 155 | + trace_helper |
| 156 | + .upgrade_system_contracts( |
| 157 | + &mut db, |
| 158 | + &block_env, |
| 159 | + parent_timestamp, |
| 160 | + false, |
| 161 | + ) |
| 162 | + .map_err(|e| e.into())?; |
| 163 | + } |
| 164 | + |
| 165 | + before_system_tx = false; |
| 166 | + } |
| 167 | + |
| 168 | + let tx_env = this.evm_config().tx_env(tx, *sender); |
| 169 | + #[cfg(feature = "bsc")] |
| 170 | + let tx_env = { |
| 171 | + let mut tx_env = tx_env; |
| 172 | + if !before_system_tx { |
| 173 | + tx_env.bsc.is_system_transaction = Some(true); |
| 174 | + }; |
| 175 | + tx_env |
| 176 | + }; |
| 177 | + |
| 178 | + let tx_gas_limit = tx_env.gas_limit; |
| 179 | + let env = |
| 180 | + EnvWithHandlerCfg::new_with_cfg_env(cfg.clone(), block_env.clone(), tx_env); |
| 181 | + |
| 182 | + let mut inspector = inspector_setup(); |
| 183 | + let (res, _) = |
| 184 | + this.inspect(StateCacheDbRefMutWrapper(&mut db), env, &mut inspector)?; |
| 185 | + let ResultAndState { result, state } = res; |
| 186 | + results.push(f(tx_info, tx_gas_limit, inspector, result, &state, &db)?); |
| 187 | + |
| 188 | + // need to apply the state changes of this transaction before executing the |
| 189 | + // next transaction, but only if there's a next transaction |
| 190 | + if transactions.peek().is_some() { |
| 191 | + // commit the state changes to the DB |
| 192 | + db.commit(state) |
| 193 | + } |
| 194 | + } |
| 195 | + |
| 196 | + Ok(Some(results)) |
| 197 | + }) |
| 198 | + .await |
| 199 | + } |
| 200 | + } |
12 | 201 | } |
0 commit comments