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 {}