1use crate::{
2 eth::{
3 backend::{db::Db, validate::TransactionValidator},
4 error::InvalidTransactionError,
5 pool::transactions::PoolTransaction,
6 },
7 inject_precompiles,
8 mem::inspector::Inspector,
9 PrecompileFactory,
10};
11use alloy_consensus::{constants::EMPTY_WITHDRAWALS, Receipt, ReceiptWithBloom};
12use alloy_eips::{eip2718::Encodable2718, eip7685::EMPTY_REQUESTS_HASH};
13use alloy_primitives::{Bloom, BloomInput, Log, B256};
14use anvil_core::eth::{
15 block::{Block, BlockInfo, PartialHeader},
16 transaction::{
17 DepositReceipt, PendingTransaction, TransactionInfo, TypedReceipt, TypedTransaction,
18 },
19 trie,
20};
21use foundry_evm::{
22 backend::DatabaseError,
23 revm::{
24 interpreter::InstructionResult,
25 primitives::{
26 BlockEnv, CfgEnvWithHandlerCfg, EVMError, EnvWithHandlerCfg, ExecutionResult, Output,
27 SpecId,
28 },
29 },
30 traces::CallTraceNode,
31 utils::odyssey_handler_register,
32};
33use revm::db::WrapDatabaseRef;
34use std::sync::Arc;
35
36#[derive(Debug)]
38pub struct ExecutedTransaction {
39 transaction: Arc<PoolTransaction>,
40 exit_reason: InstructionResult,
41 out: Option<Output>,
42 gas_used: u64,
43 logs: Vec<Log>,
44 traces: Vec<CallTraceNode>,
45 nonce: u64,
46}
47
48impl ExecutedTransaction {
51 fn create_receipt(&self, cumulative_gas_used: &mut u64) -> TypedReceipt {
53 let logs = self.logs.clone();
54 *cumulative_gas_used = cumulative_gas_used.saturating_add(self.gas_used);
55
56 let status_code = u8::from(self.exit_reason as u8 <= InstructionResult::SelfDestruct as u8);
58 let receipt_with_bloom: ReceiptWithBloom = Receipt {
59 status: (status_code == 1).into(),
60 cumulative_gas_used: *cumulative_gas_used,
61 logs,
62 }
63 .into();
64
65 match &self.transaction.pending_transaction.transaction.transaction {
66 TypedTransaction::Legacy(_) => TypedReceipt::Legacy(receipt_with_bloom),
67 TypedTransaction::EIP2930(_) => TypedReceipt::EIP2930(receipt_with_bloom),
68 TypedTransaction::EIP1559(_) => TypedReceipt::EIP1559(receipt_with_bloom),
69 TypedTransaction::EIP4844(_) => TypedReceipt::EIP4844(receipt_with_bloom),
70 TypedTransaction::EIP7702(_) => TypedReceipt::EIP7702(receipt_with_bloom),
71 TypedTransaction::Deposit(tx) => TypedReceipt::Deposit(DepositReceipt {
72 inner: receipt_with_bloom,
73 deposit_nonce: Some(tx.nonce),
74 deposit_receipt_version: Some(1),
75 }),
76 }
77 }
78}
79
80#[derive(Clone, Debug)]
82pub struct ExecutedTransactions {
83 pub block: BlockInfo,
85 pub included: Vec<Arc<PoolTransaction>>,
87 pub invalid: Vec<Arc<PoolTransaction>>,
90}
91
92pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> {
94 pub db: &'a mut Db,
96 pub validator: &'a V,
98 pub pending: std::vec::IntoIter<Arc<PoolTransaction>>,
100 pub block_env: BlockEnv,
101 pub cfg_env: CfgEnvWithHandlerCfg,
103 pub parent_hash: B256,
104 pub gas_used: u64,
106 pub blob_gas_used: u64,
108 pub enable_steps_tracing: bool,
109 pub odyssey: bool,
110 pub print_logs: bool,
111 pub print_traces: bool,
112 pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
114}
115
116impl<DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'_, DB, V> {
117 pub fn execute(mut self) -> ExecutedTransactions {
119 let mut transactions = Vec::new();
120 let mut transaction_infos = Vec::new();
121 let mut receipts = Vec::new();
122 let mut bloom = Bloom::default();
123 let mut cumulative_gas_used = 0u64;
124 let mut invalid = Vec::new();
125 let mut included = Vec::new();
126 let gas_limit = self.block_env.gas_limit.to::<u64>();
127 let parent_hash = self.parent_hash;
128 let block_number = self.block_env.number.to::<u64>();
129 let difficulty = self.block_env.difficulty;
130 let beneficiary = self.block_env.coinbase;
131 let timestamp = self.block_env.timestamp.to::<u64>();
132 let base_fee = if self.cfg_env.handler_cfg.spec_id.is_enabled_in(SpecId::LONDON) {
133 Some(self.block_env.basefee.to::<u64>())
134 } else {
135 None
136 };
137
138 let is_shanghai = self.cfg_env.handler_cfg.spec_id >= SpecId::SHANGHAI;
139 let is_cancun = self.cfg_env.handler_cfg.spec_id >= SpecId::CANCUN;
140 let is_prague = self.cfg_env.handler_cfg.spec_id >= SpecId::PRAGUE;
141 let excess_blob_gas = if is_cancun { self.block_env.get_blob_excess_gas() } else { None };
142 let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None };
143
144 for tx in self.into_iter() {
145 let tx = match tx {
146 TransactionExecutionOutcome::Executed(tx) => {
147 included.push(tx.transaction.clone());
148 tx
149 }
150 TransactionExecutionOutcome::Exhausted(tx) => {
151 trace!(target: "backend", tx_gas_limit = %tx.pending_transaction.transaction.gas_limit(), ?tx, "block gas limit exhausting, skipping transaction");
152 continue
153 }
154 TransactionExecutionOutcome::BlobGasExhausted(tx) => {
155 trace!(target: "backend", blob_gas = %tx.pending_transaction.transaction.blob_gas().unwrap_or_default(), ?tx, "block blob gas limit exhausting, skipping transaction");
156 continue
157 }
158 TransactionExecutionOutcome::Invalid(tx, _) => {
159 trace!(target: "backend", ?tx, "skipping invalid transaction");
160 invalid.push(tx);
161 continue
162 }
163 TransactionExecutionOutcome::DatabaseError(_, err) => {
164 trace!(target: "backend", ?err, "Failed to execute transaction due to database error");
167 continue
168 }
169 };
170 if is_cancun {
171 let tx_blob_gas = tx
172 .transaction
173 .pending_transaction
174 .transaction
175 .transaction
176 .blob_gas()
177 .unwrap_or(0);
178 cumulative_blob_gas_used =
179 Some(cumulative_blob_gas_used.unwrap_or(0u64).saturating_add(tx_blob_gas));
180 }
181 let receipt = tx.create_receipt(&mut cumulative_gas_used);
182
183 let ExecutedTransaction { transaction, logs, out, traces, exit_reason: exit, .. } = tx;
184 build_logs_bloom(logs.clone(), &mut bloom);
185
186 let contract_address = out.as_ref().and_then(|out| {
187 if let Output::Create(_, contract_address) = out {
188 trace!(target: "backend", "New contract deployed: at {:?}", contract_address);
189 *contract_address
190 } else {
191 None
192 }
193 });
194
195 let transaction_index = transaction_infos.len() as u64;
196 let info = TransactionInfo {
197 transaction_hash: transaction.hash(),
198 transaction_index,
199 from: *transaction.pending_transaction.sender(),
200 to: transaction.pending_transaction.transaction.to(),
201 contract_address,
202 traces,
203 exit,
204 out: out.map(Output::into_data),
205 nonce: tx.nonce,
206 gas_used: tx.gas_used,
207 };
208
209 transaction_infos.push(info);
210 receipts.push(receipt);
211 transactions.push(transaction.pending_transaction.transaction.clone());
212 }
213
214 let receipts_root =
215 trie::ordered_trie_root(receipts.iter().map(Encodable2718::encoded_2718));
216
217 let partial_header = PartialHeader {
218 parent_hash,
219 beneficiary,
220 state_root: self.db.maybe_state_root().unwrap_or_default(),
221 receipts_root,
222 logs_bloom: bloom,
223 difficulty,
224 number: block_number,
225 gas_limit,
226 gas_used: cumulative_gas_used,
227 timestamp,
228 extra_data: Default::default(),
229 mix_hash: Default::default(),
230 nonce: Default::default(),
231 base_fee,
232 parent_beacon_block_root: is_cancun.then_some(Default::default()),
233 blob_gas_used: cumulative_blob_gas_used,
234 excess_blob_gas,
235 withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
236 requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
237 };
238
239 let block = Block::new(partial_header, transactions.clone());
240 let block = BlockInfo { block, transactions: transaction_infos, receipts };
241 ExecutedTransactions { block, included, invalid }
242 }
243
244 fn env_for(&self, tx: &PendingTransaction) -> EnvWithHandlerCfg {
245 let mut tx_env = tx.to_revm_tx_env();
246 if self.cfg_env.handler_cfg.is_optimism {
247 tx_env.optimism.enveloped_tx =
248 Some(alloy_rlp::encode(&tx.transaction.transaction).into());
249 }
250
251 EnvWithHandlerCfg::new_with_cfg_env(self.cfg_env.clone(), self.block_env.clone(), tx_env)
252 }
253}
254
255#[derive(Debug)]
257pub enum TransactionExecutionOutcome {
258 Executed(ExecutedTransaction),
260 Invalid(Arc<PoolTransaction>, InvalidTransactionError),
262 Exhausted(Arc<PoolTransaction>),
264 BlobGasExhausted(Arc<PoolTransaction>),
266 DatabaseError(Arc<PoolTransaction>, DatabaseError),
268}
269
270impl<DB: Db + ?Sized, V: TransactionValidator> Iterator for &mut TransactionExecutor<'_, DB, V> {
271 type Item = TransactionExecutionOutcome;
272
273 fn next(&mut self) -> Option<Self::Item> {
274 let transaction = self.pending.next()?;
275 let sender = *transaction.pending_transaction.sender();
276 let account = match self.db.basic(sender).map(|acc| acc.unwrap_or_default()) {
277 Ok(account) => account,
278 Err(err) => return Some(TransactionExecutionOutcome::DatabaseError(transaction, err)),
279 };
280 let env = self.env_for(&transaction.pending_transaction);
281
282 let max_gas = self.gas_used.saturating_add(env.tx.gas_limit);
284 if !env.cfg.disable_block_gas_limit && max_gas > env.block.gas_limit.to::<u64>() {
285 return Some(TransactionExecutionOutcome::Exhausted(transaction))
286 }
287
288 let max_blob_gas = self.blob_gas_used.saturating_add(
290 transaction.pending_transaction.transaction.transaction.blob_gas().unwrap_or(0),
291 );
292 if max_blob_gas > alloy_eips::eip4844::MAX_DATA_GAS_PER_BLOCK {
293 return Some(TransactionExecutionOutcome::BlobGasExhausted(transaction))
294 }
295
296 if let Err(err) = self.validator.validate_pool_transaction_for(
298 &transaction.pending_transaction,
299 &account,
300 &env,
301 ) {
302 warn!(target: "backend", "Skipping invalid tx execution [{:?}] {}", transaction.hash(), err);
303 return Some(TransactionExecutionOutcome::Invalid(transaction, err))
304 }
305
306 let nonce = account.nonce;
307
308 let mut inspector = Inspector::default().with_tracing();
310 if self.enable_steps_tracing {
311 inspector = inspector.with_steps_tracing();
312 }
313 if self.print_logs {
314 inspector = inspector.with_log_collector();
315 }
316 if self.print_traces {
317 inspector = inspector.with_trace_printer();
318 }
319
320 let exec_result = {
321 let mut evm = new_evm_with_inspector(&mut *self.db, env, &mut inspector, self.odyssey);
322 if let Some(factory) = &self.precompile_factory {
323 inject_precompiles(&mut evm, factory.precompiles());
324 }
325
326 trace!(target: "backend", "[{:?}] executing", transaction.hash());
327 match evm.transact_commit() {
329 Ok(exec_result) => exec_result,
330 Err(err) => {
331 warn!(target: "backend", "[{:?}] failed to execute: {:?}", transaction.hash(), err);
332 match err {
333 EVMError::Database(err) => {
334 return Some(TransactionExecutionOutcome::DatabaseError(
335 transaction,
336 err,
337 ))
338 }
339 EVMError::Transaction(err) => {
340 return Some(TransactionExecutionOutcome::Invalid(
341 transaction,
342 err.into(),
343 ))
344 }
345 e => panic!("failed to execute transaction: {e}"),
348 }
349 }
350 }
351 };
352
353 if self.print_traces {
354 inspector.print_traces();
355 }
356 inspector.print_logs();
357
358 let (exit_reason, gas_used, out, logs) = match exec_result {
359 ExecutionResult::Success { reason, gas_used, logs, output, .. } => {
360 (reason.into(), gas_used, Some(output), Some(logs))
361 }
362 ExecutionResult::Revert { gas_used, output } => {
363 (InstructionResult::Revert, gas_used, Some(Output::Call(output)), None)
364 }
365 ExecutionResult::Halt { reason, gas_used } => (reason.into(), gas_used, None, None),
366 };
367
368 if exit_reason == InstructionResult::OutOfGas {
369 warn!(target: "backend", "[{:?}] executed with out of gas", transaction.hash())
371 }
372
373 trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", transaction.hash(), out);
374
375 self.gas_used = self.gas_used.saturating_add(gas_used);
377
378 if let Some(blob_gas) = transaction.pending_transaction.transaction.transaction.blob_gas() {
380 self.blob_gas_used = self.blob_gas_used.saturating_add(blob_gas);
381 }
382
383 trace!(target: "backend::executor", "transacted [{:?}], result: {:?} gas {}", transaction.hash(), exit_reason, gas_used);
384
385 let tx = ExecutedTransaction {
386 transaction,
387 exit_reason,
388 out,
389 gas_used,
390 logs: logs.unwrap_or_default(),
391 traces: inspector.tracer.map(|t| t.into_traces().into_nodes()).unwrap_or_default(),
392 nonce,
393 };
394
395 Some(TransactionExecutionOutcome::Executed(tx))
396 }
397}
398
399fn build_logs_bloom(logs: Vec<Log>, bloom: &mut Bloom) {
401 for log in logs {
402 bloom.accrue(BloomInput::Raw(&log.address[..]));
403 for topic in log.topics() {
404 bloom.accrue(BloomInput::Raw(&topic[..]));
405 }
406 }
407}
408
409pub fn new_evm_with_inspector<DB: revm::Database>(
411 db: DB,
412 env: EnvWithHandlerCfg,
413 inspector: &mut dyn revm::Inspector<DB>,
414 odyssey: bool,
415) -> revm::Evm<'_, &mut dyn revm::Inspector<DB>, DB> {
416 let EnvWithHandlerCfg { env, handler_cfg } = env;
417
418 let mut handler = revm::Handler::new(handler_cfg);
419
420 handler.append_handler_register_plain(revm::inspector_handle_register);
421 if odyssey {
422 handler.append_handler_register_plain(odyssey_handler_register);
423 }
424
425 let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector);
426
427 revm::Evm::new(context, handler)
428}
429
430pub fn new_evm_with_inspector_ref<'a, DB>(
432 db: DB,
433 env: EnvWithHandlerCfg,
434 inspector: &mut dyn revm::Inspector<WrapDatabaseRef<DB>>,
435 odyssey: bool,
436) -> revm::Evm<'a, &mut dyn revm::Inspector<WrapDatabaseRef<DB>>, WrapDatabaseRef<DB>>
437where
438 DB: revm::DatabaseRef,
439{
440 new_evm_with_inspector(WrapDatabaseRef(db), env, inspector, odyssey)
441}