alloy_evm/
evm.rs

1//! Abstraction over EVM.
2
3use crate::{env::BlockEnvironment, tracing::TxTracer, EvmEnv, EvmError, IntoTxEnv};
4use alloy_consensus::transaction::TxHashRef;
5use alloy_primitives::{Address, Bytes, B256};
6use core::{error::Error, fmt::Debug, hash::Hash};
7use revm::{
8    context::result::ExecutionResult,
9    context_interface::{
10        result::{HaltReasonTr, ResultAndState},
11        ContextTr,
12    },
13    inspector::{JournalExt, NoOpInspector},
14    DatabaseCommit, Inspector,
15};
16
17/// Helper trait to bound [`revm::Database::Error`] with common requirements.
18pub trait Database: revm::Database<Error: Error + Send + Sync + 'static> + Debug {}
19impl<T> Database for T where T: revm::Database<Error: Error + Send + Sync + 'static> + Debug {}
20
21/// An instance of an ethereum virtual machine.
22///
23/// An EVM is commonly initialized with the corresponding block context and state and it's only
24/// purpose is to execute transactions.
25///
26/// Executing a transaction will return the outcome of the transaction.
27pub trait Evm {
28    /// Database type held by the EVM.
29    type DB;
30    /// The transaction object that the EVM will execute.
31    ///
32    /// This type represents the transaction environment that the EVM operates on internally.
33    /// Typically this is [`revm::context::TxEnv`], which contains all necessary transaction
34    /// data like sender, gas limits, value, and calldata.
35    ///
36    /// The EVM accepts flexible transaction inputs through the [`IntoTxEnv`] trait. This means
37    /// that while the EVM internally works with `Self::Tx` (usually `TxEnv`), users can pass
38    /// various transaction formats to [`Evm::transact`], including:
39    /// - Direct [`TxEnv`](revm::context::TxEnv) instances
40    /// - [`Recovered<T>`](alloy_consensus::transaction::Recovered) where `T` implements
41    ///   [`crate::FromRecoveredTx`]
42    /// - [`WithEncoded<Recovered<T>>`](alloy_eips::eip2718::WithEncoded) where `T` implements
43    ///   [`crate::FromTxWithEncoded`]
44    ///
45    /// This design allows the EVM to accept recovered consensus transactions seamlessly.
46    type Tx: IntoTxEnv<Self::Tx>;
47    /// Error type returned by EVM. Contains either errors related to invalid transactions or
48    /// internal irrecoverable execution errors.
49    type Error: EvmError;
50    /// Halt reason. Enum over all possible reasons for halting the execution. When execution halts,
51    /// it means that transaction is valid, however, it's execution was interrupted (e.g because of
52    /// running out of gas or overflowing stack).
53    type HaltReason: HaltReasonTr + Send + Sync + 'static;
54    /// Identifier of the EVM specification. EVM is expected to use this identifier to determine
55    /// which features are enabled.
56    type Spec: Debug + Copy + Hash + Eq + Send + Sync + Default + 'static;
57    /// Block environment used by the EVM.
58    type BlockEnv: BlockEnvironment;
59    /// Precompiles used by the EVM.
60    type Precompiles;
61    /// Evm inspector.
62    type Inspector;
63
64    /// Reference to [`Evm::BlockEnv`].
65    fn block(&self) -> &Self::BlockEnv;
66
67    /// Returns the chain ID of the environment.
68    fn chain_id(&self) -> u64;
69
70    /// Executes a transaction and returns the outcome.
71    fn transact_raw(
72        &mut self,
73        tx: Self::Tx,
74    ) -> Result<ResultAndState<Self::HaltReason>, Self::Error>;
75
76    /// Same as [`Evm::transact_raw`], but takes any type implementing [`IntoTxEnv`].
77    ///
78    /// This is the primary method for executing transactions. It accepts flexible input types
79    /// that can be converted to the EVM's transaction environment, including:
80    /// - [`TxEnv`](revm::context::TxEnv) - Direct transaction environment
81    /// - [`Recovered<T>`](alloy_consensus::transaction::Recovered) - Consensus transaction with
82    ///   recovered sender
83    /// - [`WithEncoded<Recovered<T>>`](alloy_eips::eip2718::WithEncoded) - Transaction with sender
84    ///   and encoded bytes
85    ///
86    /// The conversion happens automatically through the [`IntoTxEnv`] trait.
87    fn transact(
88        &mut self,
89        tx: impl IntoTxEnv<Self::Tx>,
90    ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
91        self.transact_raw(tx.into_tx_env())
92    }
93
94    /// Executes a system call.
95    ///
96    /// Note: this will only keep the target `contract` in the state. This is done because revm is
97    /// loading [`revm::context::Block::beneficiary`] into state by default, and we need to avoid it
98    /// by also covering edge cases when beneficiary is set to the system contract address.
99    fn transact_system_call(
100        &mut self,
101        caller: Address,
102        contract: Address,
103        data: Bytes,
104    ) -> Result<ResultAndState<Self::HaltReason>, Self::Error>;
105
106    /// Returns an immutable reference to the underlying database.
107    fn db(&self) -> &Self::DB {
108        self.components().0
109    }
110
111    /// Returns a mutable reference to the underlying database.
112    fn db_mut(&mut self) -> &mut Self::DB {
113        self.components_mut().0
114    }
115
116    /// Executes a transaction and commits the state changes to the underlying database.
117    fn transact_commit(
118        &mut self,
119        tx: impl IntoTxEnv<Self::Tx>,
120    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error>
121    where
122        Self::DB: DatabaseCommit,
123    {
124        let ResultAndState { result, state } = self.transact(tx)?;
125        self.db_mut().commit(state);
126
127        Ok(result)
128    }
129
130    /// Consumes the EVM and returns the inner [`EvmEnv`].
131    fn finish(self) -> (Self::DB, EvmEnv<Self::Spec, Self::BlockEnv>)
132    where
133        Self: Sized;
134
135    /// Consumes the EVM and returns the inner database.
136    fn into_db(self) -> Self::DB
137    where
138        Self: Sized,
139    {
140        self.finish().0
141    }
142
143    /// Consumes the EVM and returns the inner [`EvmEnv`].
144    fn into_env(self) -> EvmEnv<Self::Spec, Self::BlockEnv>
145    where
146        Self: Sized,
147    {
148        self.finish().1
149    }
150
151    /// Determines whether additional transactions should be inspected or not.
152    ///
153    /// See also [`EvmFactory::create_evm_with_inspector`].
154    fn set_inspector_enabled(&mut self, enabled: bool);
155
156    /// Enables the configured inspector.
157    ///
158    /// All additional transactions will be inspected if enabled.
159    fn enable_inspector(&mut self) {
160        self.set_inspector_enabled(true)
161    }
162
163    /// Disables the configured inspector.
164    ///
165    /// Transactions will no longer be inspected.
166    fn disable_inspector(&mut self) {
167        self.set_inspector_enabled(false)
168    }
169
170    /// Getter of precompiles.
171    fn precompiles(&self) -> &Self::Precompiles {
172        self.components().2
173    }
174
175    /// Mutable getter of precompiles.
176    fn precompiles_mut(&mut self) -> &mut Self::Precompiles {
177        self.components_mut().2
178    }
179
180    /// Getter of inspector.
181    fn inspector(&self) -> &Self::Inspector {
182        self.components().1
183    }
184
185    /// Mutable getter of inspector.
186    fn inspector_mut(&mut self) -> &mut Self::Inspector {
187        self.components_mut().1
188    }
189
190    /// Provides immutable references to the database, inspector and precompiles.
191    fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles);
192
193    /// Provides mutable references to the database, inspector and precompiles.
194    fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles);
195}
196
197/// An extension trait for [`Evm`] providing additional functionality.
198pub trait EvmExt: Evm {
199    /// Replays all the transactions until the target transaction is found.
200    ///
201    /// This stops before transacting the target hash and commits all previous changes.
202    ///
203    /// Returns the index of the target transaction in the iterator.
204    fn replay_transactions_until<I, T>(
205        &mut self,
206        transactions: I,
207        target_tx_hash: B256,
208    ) -> Result<usize, Self::Error>
209    where
210        Self::DB: DatabaseCommit,
211        I: IntoIterator<Item = T>,
212        T: IntoTxEnv<Self::Tx> + TxHashRef,
213    {
214        let mut index = 0;
215        for tx in transactions {
216            if *tx.tx_hash() == target_tx_hash {
217                // reached the target transaction
218                break;
219            }
220            self.transact_commit(tx)?;
221            index += 1;
222        }
223        Ok(index)
224    }
225
226    /// Replays all the previous transactions and returns the [`ResultAndState`] of the target
227    /// transaction.
228    ///
229    /// Returns `None` if the target transaction was not found.
230    fn replay_transaction<I, T>(
231        &mut self,
232        transactions: I,
233        target_tx_hash: B256,
234    ) -> Result<Option<ResultAndState<Self::HaltReason>>, Self::Error>
235    where
236        Self::DB: DatabaseCommit,
237        I: IntoIterator<Item = T>,
238        T: IntoTxEnv<Self::Tx> + TxHashRef,
239    {
240        for tx in transactions {
241            if *tx.tx_hash() == target_tx_hash {
242                // reached the target transaction
243                return self.transact(tx).map(Some);
244            } else {
245                self.transact_commit(tx)?;
246            }
247        }
248        Ok(None)
249    }
250}
251
252/// Automatic implementation of [`EvmExt`] for all types that implement [`Evm`].
253impl<T: Evm> EvmExt for T {}
254
255/// A type responsible for creating instances of an ethereum virtual machine given a certain input.
256pub trait EvmFactory {
257    /// The EVM type that this factory creates.
258    type Evm<DB: Database, I: Inspector<Self::Context<DB>>>: Evm<
259        DB = DB,
260        Tx = Self::Tx,
261        HaltReason = Self::HaltReason,
262        Error = Self::Error<DB::Error>,
263        Spec = Self::Spec,
264        BlockEnv = Self::BlockEnv,
265        Precompiles = Self::Precompiles,
266        Inspector = I,
267    >;
268
269    /// The EVM context for inspectors
270    type Context<DB: Database>: ContextTr<Db = DB, Journal: JournalExt>;
271    /// Transaction environment.
272    type Tx: IntoTxEnv<Self::Tx>;
273    /// EVM error. See [`Evm::Error`].
274    type Error<DBError: Error + Send + Sync + 'static>: EvmError;
275    /// Halt reason. See [`Evm::HaltReason`].
276    type HaltReason: HaltReasonTr + Send + Sync + 'static;
277    /// The EVM specification identifier, see [`Evm::Spec`].
278    type Spec: Debug + Copy + Hash + Eq + Send + Sync + Default + 'static;
279    /// Block environment used by the EVM. See [`Evm::BlockEnv`].
280    type BlockEnv: BlockEnvironment;
281    /// Precompiles used by the EVM.
282    type Precompiles;
283
284    /// Creates a new instance of an EVM.
285    fn create_evm<DB: Database>(
286        &self,
287        db: DB,
288        evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
289    ) -> Self::Evm<DB, NoOpInspector>;
290
291    /// Creates a new instance of an EVM with an inspector.
292    ///
293    /// Note: It is expected that the [`Inspector`] is usually provided as `&mut Inspector` so that
294    /// it remains owned by the call site when [`Evm::transact`] is invoked.
295    fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>>>(
296        &self,
297        db: DB,
298        input: EvmEnv<Self::Spec, Self::BlockEnv>,
299        inspector: I,
300    ) -> Self::Evm<DB, I>;
301}
302
303/// An extension trait for [`EvmFactory`] providing useful non-overridable methods.
304pub trait EvmFactoryExt: EvmFactory {
305    /// Creates a new [`TxTracer`] instance with the given database, input and fused inspector.
306    fn create_tracer<DB, I>(
307        &self,
308        db: DB,
309        input: EvmEnv<Self::Spec, Self::BlockEnv>,
310        fused_inspector: I,
311    ) -> TxTracer<Self::Evm<DB, I>>
312    where
313        DB: Database + DatabaseCommit,
314        I: Inspector<Self::Context<DB>> + Clone,
315    {
316        TxTracer::new(self.create_evm_with_inspector(db, input, fused_inspector))
317    }
318}
319
320impl<T: EvmFactory> EvmFactoryExt for T {}