foundry_evm_core/
evm.rs

1use std::ops::{Deref, DerefMut};
2
3use crate::{
4    backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH, Env, InspectorExt,
5};
6use alloy_consensus::constants::KECCAK_EMPTY;
7use alloy_evm::{
8    eth::EthEvmContext,
9    precompiles::{DynPrecompile, PrecompilesMap},
10    Evm, EvmEnv,
11};
12use alloy_primitives::{Address, Bytes, U256};
13use foundry_fork_db::DatabaseError;
14use revm::{
15    context::{
16        result::{EVMError, HaltReason, ResultAndState},
17        BlockEnv, CfgEnv, ContextTr, CreateScheme, Evm as RevmEvm, JournalTr, LocalContext, TxEnv,
18    },
19    handler::{
20        instructions::EthInstructions, EthFrame, EthPrecompiles, FrameInitOrResult, FrameResult,
21        Handler, ItemOrResult, MainnetHandler,
22    },
23    inspector::InspectorHandler,
24    interpreter::{
25        interpreter::EthInterpreter, return_ok, CallInput, CallInputs, CallOutcome, CallScheme,
26        CallValue, CreateInputs, CreateOutcome, FrameInput, Gas, InstructionResult,
27        InterpreterResult,
28    },
29    precompile::{secp256r1::P256VERIFY, PrecompileSpecId, Precompiles},
30    primitives::hardfork::SpecId,
31    Context, ExecuteEvm, Journal,
32};
33
34pub fn new_evm_with_inspector<'i, 'db, I: InspectorExt + ?Sized>(
35    db: &'db mut dyn DatabaseExt,
36    env: Env,
37    inspector: &'i mut I,
38) -> FoundryEvm<'db, &'i mut I> {
39    let ctx = EthEvmContext {
40        journaled_state: {
41            let mut journal = Journal::new(db);
42            journal.set_spec_id(env.evm_env.cfg_env.spec);
43            journal
44        },
45        block: env.evm_env.block_env,
46        cfg: env.evm_env.cfg_env,
47        tx: env.tx,
48        chain: (),
49        local: LocalContext::default(),
50        error: Ok(()),
51    };
52    let spec = ctx.cfg.spec;
53
54    let mut evm = FoundryEvm {
55        inner: RevmEvm::new_with_inspector(
56            ctx,
57            inspector,
58            EthInstructions::default(),
59            get_precompiles(spec),
60        ),
61    };
62
63    inject_precompiles(&mut evm);
64
65    evm
66}
67
68pub fn new_evm_with_existing_context<'a>(
69    ctx: EthEvmContext<&'a mut dyn DatabaseExt>,
70    inspector: &'a mut dyn InspectorExt,
71) -> FoundryEvm<'a, &'a mut dyn InspectorExt> {
72    let spec = ctx.cfg.spec;
73
74    let mut evm = FoundryEvm {
75        inner: RevmEvm::new_with_inspector(
76            ctx,
77            inspector,
78            EthInstructions::default(),
79            get_precompiles(spec),
80        ),
81    };
82
83    inject_precompiles(&mut evm);
84
85    evm
86}
87
88/// Conditionally inject additional precompiles into the EVM context.
89fn inject_precompiles(evm: &mut FoundryEvm<'_, impl InspectorExt>) {
90    if evm.inspector().is_odyssey() {
91        evm.precompiles_mut().apply_precompile(P256VERIFY.address(), |_| {
92            Some(DynPrecompile::from(P256VERIFY.precompile()))
93        });
94    }
95}
96
97/// Get the precompiles for the given spec.
98fn get_precompiles(spec: SpecId) -> PrecompilesMap {
99    PrecompilesMap::from_static(
100        EthPrecompiles {
101            precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
102            spec,
103        }
104        .precompiles,
105    )
106}
107
108/// Get the call inputs for the CREATE2 factory.
109fn get_create2_factory_call_inputs(
110    salt: U256,
111    inputs: &CreateInputs,
112    deployer: Address,
113) -> CallInputs {
114    let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code[..]].concat();
115    CallInputs {
116        caller: inputs.caller,
117        bytecode_address: deployer,
118        target_address: deployer,
119        scheme: CallScheme::Call,
120        value: CallValue::Transfer(inputs.value),
121        input: CallInput::Bytes(calldata.into()),
122        gas_limit: inputs.gas_limit,
123        is_static: false,
124        return_memory_offset: 0..0,
125        is_eof: false,
126    }
127}
128
129pub struct FoundryEvm<'db, I: InspectorExt> {
130    #[allow(clippy::type_complexity)]
131    pub inner: RevmEvm<
132        EthEvmContext<&'db mut dyn DatabaseExt>,
133        I,
134        EthInstructions<EthInterpreter, EthEvmContext<&'db mut dyn DatabaseExt>>,
135        PrecompilesMap,
136    >,
137}
138
139impl<I: InspectorExt> FoundryEvm<'_, I> {
140    pub fn run_execution(
141        &mut self,
142        frame: FrameInput,
143    ) -> Result<FrameResult, EVMError<DatabaseError>> {
144        let mut handler = FoundryHandler::<_>::default();
145
146        // Create first frame action
147        let frame = handler.inspect_first_frame_init(&mut self.inner, frame)?;
148        let frame_result = match frame {
149            ItemOrResult::Item(frame) => handler.inspect_run_exec_loop(&mut self.inner, frame)?,
150            ItemOrResult::Result(result) => result,
151        };
152
153        Ok(frame_result)
154    }
155}
156
157impl<'db, I: InspectorExt> Evm for FoundryEvm<'db, I> {
158    type Precompiles = PrecompilesMap;
159    type Inspector = I;
160    type DB = &'db mut dyn DatabaseExt;
161    type Error = EVMError<DatabaseError>;
162    type HaltReason = HaltReason;
163    type Spec = SpecId;
164    type Tx = TxEnv;
165
166    fn chain_id(&self) -> u64 {
167        self.inner.ctx.cfg.chain_id
168    }
169
170    fn block(&self) -> &BlockEnv {
171        &self.inner.block
172    }
173
174    fn db_mut(&mut self) -> &mut Self::DB {
175        self.inner.db()
176    }
177
178    fn precompiles(&self) -> &Self::Precompiles {
179        &self.inner.precompiles
180    }
181
182    fn precompiles_mut(&mut self) -> &mut Self::Precompiles {
183        &mut self.inner.precompiles
184    }
185
186    fn inspector(&self) -> &Self::Inspector {
187        &self.inner.inspector
188    }
189
190    fn inspector_mut(&mut self) -> &mut Self::Inspector {
191        &mut self.inner.inspector
192    }
193
194    fn set_inspector_enabled(&mut self, _enabled: bool) {
195        unimplemented!("FoundryEvm is always inspecting")
196    }
197
198    fn transact_raw(
199        &mut self,
200        tx: Self::Tx,
201    ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
202        let mut handler = FoundryHandler::<_>::default();
203        self.inner.set_tx(tx);
204        handler.inspect_run(&mut self.inner)
205    }
206
207    fn transact_system_call(
208        &mut self,
209        _caller: Address,
210        _contract: Address,
211        _data: Bytes,
212    ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
213        unimplemented!()
214    }
215
216    fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>)
217    where
218        Self: Sized,
219    {
220        let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.ctx;
221
222        (journaled_state.database, EvmEnv { block_env, cfg_env })
223    }
224}
225
226impl<'db, I: InspectorExt> Deref for FoundryEvm<'db, I> {
227    type Target = Context<BlockEnv, TxEnv, CfgEnv, &'db mut dyn DatabaseExt>;
228
229    fn deref(&self) -> &Self::Target {
230        &self.inner.ctx
231    }
232}
233
234impl<I: InspectorExt> DerefMut for FoundryEvm<'_, I> {
235    fn deref_mut(&mut self) -> &mut Self::Target {
236        &mut self.inner.ctx
237    }
238}
239
240pub struct FoundryHandler<'db, I: InspectorExt> {
241    #[allow(clippy::type_complexity)]
242    inner: MainnetHandler<
243        RevmEvm<
244            EthEvmContext<&'db mut dyn DatabaseExt>,
245            I,
246            EthInstructions<EthInterpreter, EthEvmContext<&'db mut dyn DatabaseExt>>,
247            PrecompilesMap,
248        >,
249        EVMError<DatabaseError>,
250        EthFrame<
251            RevmEvm<
252                EthEvmContext<&'db mut dyn DatabaseExt>,
253                I,
254                EthInstructions<EthInterpreter, EthEvmContext<&'db mut dyn DatabaseExt>>,
255                PrecompilesMap,
256            >,
257            EVMError<DatabaseError>,
258            EthInterpreter,
259        >,
260    >,
261    create2_overrides: Vec<(usize, CallInputs)>,
262}
263
264impl<I: InspectorExt> Default for FoundryHandler<'_, I> {
265    fn default() -> Self {
266        Self { inner: MainnetHandler::default(), create2_overrides: Vec::new() }
267    }
268}
269
270impl<'db, I: InspectorExt> Handler for FoundryHandler<'db, I> {
271    type Evm = RevmEvm<
272        EthEvmContext<&'db mut dyn DatabaseExt>,
273        I,
274        EthInstructions<EthInterpreter, EthEvmContext<&'db mut dyn DatabaseExt>>,
275        PrecompilesMap,
276    >;
277    type Error = EVMError<DatabaseError>;
278    type Frame = EthFrame<
279        RevmEvm<
280            EthEvmContext<&'db mut dyn DatabaseExt>,
281            I,
282            EthInstructions<EthInterpreter, EthEvmContext<&'db mut dyn DatabaseExt>>,
283            PrecompilesMap,
284        >,
285        EVMError<DatabaseError>,
286        EthInterpreter,
287    >;
288    type HaltReason = HaltReason;
289
290    fn frame_return_result(
291        &mut self,
292        frame: &mut Self::Frame,
293        evm: &mut Self::Evm,
294        result: <Self::Frame as revm::handler::Frame>::FrameResult,
295    ) -> Result<(), Self::Error> {
296        let result = if self
297            .create2_overrides
298            .last()
299            .is_some_and(|(depth, _)| *depth == evm.journal().depth)
300        {
301            let (_, call_inputs) = self.create2_overrides.pop().unwrap();
302            let FrameResult::Call(mut result) = result else {
303                unreachable!("create2 override should be a call frame");
304            };
305
306            // Decode address from output.
307            let address = match result.instruction_result() {
308                return_ok!() => Address::try_from(result.output().as_ref())
309                    .map_err(|_| {
310                        result.result = InterpreterResult {
311                            result: InstructionResult::Revert,
312                            output: "invalid CREATE2 factory output".into(),
313                            gas: Gas::new(call_inputs.gas_limit),
314                        };
315                    })
316                    .ok(),
317                _ => None,
318            };
319
320            FrameResult::Create(CreateOutcome { result: result.result, address })
321        } else {
322            result
323        };
324
325        self.inner.frame_return_result(frame, evm, result)
326    }
327}
328
329impl<I: InspectorExt> InspectorHandler for FoundryHandler<'_, I> {
330    type IT = EthInterpreter;
331
332    fn inspect_frame_call(
333        &mut self,
334        frame: &mut Self::Frame,
335        evm: &mut Self::Evm,
336    ) -> Result<FrameInitOrResult<Self::Frame>, Self::Error> {
337        let frame_or_result = self.inner.inspect_frame_call(frame, evm)?;
338
339        let ItemOrResult::Item(FrameInput::Create(inputs)) = &frame_or_result else {
340            return Ok(frame_or_result)
341        };
342
343        let CreateScheme::Create2 { salt } = inputs.scheme else { return Ok(frame_or_result) };
344
345        if !evm.inspector.should_use_create2_factory(&mut evm.ctx, inputs) {
346            return Ok(frame_or_result)
347        }
348
349        let gas_limit = inputs.gas_limit;
350
351        // Get CREATE2 deployer.
352        let create2_deployer = evm.inspector.create2_deployer();
353
354        // Generate call inputs for CREATE2 factory.
355        let call_inputs = get_create2_factory_call_inputs(salt, inputs, create2_deployer);
356
357        // Push data about current override to the stack.
358        self.create2_overrides.push((evm.journal().depth(), call_inputs.clone()));
359
360        // Sanity check that CREATE2 deployer exists.
361        let code_hash = evm.journal().load_account(create2_deployer)?.info.code_hash;
362        if code_hash == KECCAK_EMPTY {
363            return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
364                result: InterpreterResult {
365                    result: InstructionResult::Revert,
366                    output: Bytes::copy_from_slice(
367                        format!("missing CREATE2 deployer: {create2_deployer}").as_bytes(),
368                    ),
369                    gas: Gas::new(gas_limit),
370                },
371                memory_offset: 0..0,
372            })))
373        } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH {
374            return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
375                result: InterpreterResult {
376                    result: InstructionResult::Revert,
377                    output: "invalid CREATE2 deployer bytecode".into(),
378                    gas: Gas::new(gas_limit),
379                },
380                memory_offset: 0..0,
381            })))
382        }
383
384        // Return the created CALL frame instead
385        Ok(ItemOrResult::Item(FrameInput::Call(Box::new(call_inputs))))
386    }
387}