revm/
builder.rs

1use crate::{
2    db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef},
3    handler::register,
4    primitives::{
5        BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, Env, EnvWithHandlerCfg, HandlerCfg, SpecId, TxEnv,
6    },
7    Context, ContextWithHandlerCfg, Evm, Handler,
8};
9use core::marker::PhantomData;
10use std::boxed::Box;
11
12/// Evm Builder allows building or modifying EVM.
13/// Note that some of the methods that changes underlying structures
14/// will reset the registered handler to default mainnet.
15pub struct EvmBuilder<'a, BuilderStage, EXT, DB: Database> {
16    context: Context<EXT, DB>,
17    /// Handler that will be used by EVM. It contains handle registers
18    handler: Handler<'a, Context<EXT, DB>, EXT, DB>,
19    /// Phantom data to mark the stage of the builder.
20    phantom: PhantomData<BuilderStage>,
21}
22
23/// First stage of the builder allows setting generic variables.
24/// Generic variables are database and external context.
25pub struct SetGenericStage;
26
27/// Second stage of the builder allows appending handler registers.
28/// Requires the database and external context to be set.
29pub struct HandlerStage;
30
31impl<'a> Default for EvmBuilder<'a, SetGenericStage, (), EmptyDB> {
32    fn default() -> Self {
33        cfg_if::cfg_if! {
34            if #[cfg(all(feature = "optimism-default-handler",
35                not(feature = "negate-optimism-default-handler")))] {
36                    let mut handler_cfg = HandlerCfg::new(SpecId::LATEST);
37                    // set is_optimism to true by default.
38                    handler_cfg.is_optimism = true;
39
40            } else {
41                let handler_cfg = HandlerCfg::new(SpecId::LATEST);
42            }
43        }
44
45        Self {
46            context: Context::default(),
47            handler: EvmBuilder::<'a, SetGenericStage, (), EmptyDB>::handler(handler_cfg),
48            phantom: PhantomData,
49        }
50    }
51}
52
53impl<'a, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, EXT, DB> {
54    /// Sets the [`EmptyDB`] as the [`Database`] that will be used by [`Evm`].
55    pub fn with_empty_db(self) -> EvmBuilder<'a, SetGenericStage, EXT, EmptyDB> {
56        EvmBuilder {
57            context: Context::new(
58                self.context.evm.with_db(EmptyDB::default()),
59                self.context.external,
60            ),
61            handler: EvmBuilder::<'a, SetGenericStage, EXT, EmptyDB>::handler(self.handler.cfg()),
62            phantom: PhantomData,
63        }
64    }
65    /// Sets the [`Database`] that will be used by [`Evm`].
66    pub fn with_db<ODB: Database>(self, db: ODB) -> EvmBuilder<'a, SetGenericStage, EXT, ODB> {
67        EvmBuilder {
68            context: Context::new(self.context.evm.with_db(db), self.context.external),
69            handler: EvmBuilder::<'a, SetGenericStage, EXT, ODB>::handler(self.handler.cfg()),
70            phantom: PhantomData,
71        }
72    }
73    /// Sets the [`DatabaseRef`] that will be used by [`Evm`].
74    pub fn with_ref_db<ODB: DatabaseRef>(
75        self,
76        db: ODB,
77    ) -> EvmBuilder<'a, SetGenericStage, EXT, WrapDatabaseRef<ODB>> {
78        EvmBuilder {
79            context: Context::new(
80                self.context.evm.with_db(WrapDatabaseRef(db)),
81                self.context.external,
82            ),
83            handler: EvmBuilder::<'a, SetGenericStage, EXT, WrapDatabaseRef<ODB>>::handler(
84                self.handler.cfg(),
85            ),
86            phantom: PhantomData,
87        }
88    }
89
90    /// Sets the external context that will be used by [`Evm`].
91    pub fn with_external_context<OEXT>(
92        self,
93        external: OEXT,
94    ) -> EvmBuilder<'a, SetGenericStage, OEXT, DB> {
95        EvmBuilder {
96            context: Context::new(self.context.evm, external),
97            handler: EvmBuilder::<'a, SetGenericStage, OEXT, DB>::handler(self.handler.cfg()),
98            phantom: PhantomData,
99        }
100    }
101
102    /// Sets Builder with [`EnvWithHandlerCfg`].
103    pub fn with_env_with_handler_cfg(
104        mut self,
105        env_with_handler_cfg: EnvWithHandlerCfg,
106    ) -> EvmBuilder<'a, HandlerStage, EXT, DB> {
107        let EnvWithHandlerCfg { env, handler_cfg } = env_with_handler_cfg;
108        self.context.evm.env = env;
109        EvmBuilder {
110            context: self.context,
111            handler: EvmBuilder::<'a, HandlerStage, EXT, DB>::handler(handler_cfg),
112            phantom: PhantomData,
113        }
114    }
115
116    /// Sets Builder with [`ContextWithHandlerCfg`].
117    pub fn with_context_with_handler_cfg<OEXT, ODB: Database>(
118        self,
119        context_with_handler_cfg: ContextWithHandlerCfg<OEXT, ODB>,
120    ) -> EvmBuilder<'a, HandlerStage, OEXT, ODB> {
121        EvmBuilder {
122            context: context_with_handler_cfg.context,
123            handler: EvmBuilder::<'a, HandlerStage, OEXT, ODB>::handler(
124                context_with_handler_cfg.cfg,
125            ),
126            phantom: PhantomData,
127        }
128    }
129
130    /// Sets Builder with [`CfgEnvWithHandlerCfg`].
131    pub fn with_cfg_env_with_handler_cfg(
132        mut self,
133        cfg_env_and_spec_id: CfgEnvWithHandlerCfg,
134    ) -> EvmBuilder<'a, HandlerStage, EXT, DB> {
135        self.context.evm.env.cfg = cfg_env_and_spec_id.cfg_env;
136
137        EvmBuilder {
138            context: self.context,
139            handler: EvmBuilder::<'a, HandlerStage, EXT, DB>::handler(
140                cfg_env_and_spec_id.handler_cfg,
141            ),
142            phantom: PhantomData,
143        }
144    }
145
146    /// Sets Builder with [`HandlerCfg`]
147    pub fn with_handler_cfg(
148        self,
149        handler_cfg: HandlerCfg,
150    ) -> EvmBuilder<'a, HandlerStage, EXT, DB> {
151        EvmBuilder {
152            context: self.context,
153            handler: EvmBuilder::<'a, HandlerStage, EXT, DB>::handler(handler_cfg),
154            phantom: PhantomData,
155        }
156    }
157
158    /// Sets the Optimism handler with latest spec.
159    ///
160    /// If `optimism-default-handler` feature is enabled this is not needed.
161    #[cfg(feature = "optimism")]
162    pub fn optimism(mut self) -> EvmBuilder<'a, HandlerStage, EXT, DB> {
163        self.handler = Handler::optimism_with_spec(self.handler.cfg.spec_id);
164        EvmBuilder {
165            context: self.context,
166            handler: self.handler,
167            phantom: PhantomData,
168        }
169    }
170
171    /// Sets the mainnet handler with latest spec.
172    ///
173    /// Enabled only with `optimism-default-handler` feature.
174    #[cfg(feature = "optimism-default-handler")]
175    pub fn mainnet(mut self) -> EvmBuilder<'a, HandlerStage, EXT, DB> {
176        self.handler = Handler::mainnet_with_spec(self.handler.cfg.spec_id);
177        EvmBuilder {
178            context: self.context,
179            handler: self.handler,
180            phantom: PhantomData,
181        }
182    }
183}
184
185impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> {
186    /// Creates new builder from Evm, Evm is consumed and all field are moved to Builder.
187    /// It will preserve set handler and context.
188    ///
189    /// Builder is in HandlerStage and both database and external are set.
190    pub fn new(evm: Evm<'a, EXT, DB>) -> Self {
191        Self {
192            context: evm.context,
193            handler: evm.handler,
194            phantom: PhantomData,
195        }
196    }
197
198    /// Sets the [`EmptyDB`] and resets the [`Handler`] to default mainnet.
199    pub fn reset_handler_with_empty_db(self) -> EvmBuilder<'a, HandlerStage, EXT, EmptyDB> {
200        EvmBuilder {
201            context: Context::new(
202                self.context.evm.with_db(EmptyDB::default()),
203                self.context.external,
204            ),
205            handler: EvmBuilder::<'a, HandlerStage, EXT, EmptyDB>::handler(self.handler.cfg()),
206            phantom: PhantomData,
207        }
208    }
209
210    /// Resets the [`Handler`] and sets base mainnet handler.
211    ///
212    /// Enabled only with `optimism-default-handler` feature.
213    #[cfg(feature = "optimism-default-handler")]
214    pub fn reset_handler_with_mainnet(mut self) -> EvmBuilder<'a, HandlerStage, EXT, DB> {
215        self.handler = Handler::mainnet_with_spec(self.handler.cfg.spec_id);
216        EvmBuilder {
217            context: self.context,
218            handler: self.handler,
219            phantom: PhantomData,
220        }
221    }
222
223    /// Sets the [`Database`] that will be used by [`Evm`]
224    /// and resets the [`Handler`] to default mainnet.
225    pub fn reset_handler_with_db<ODB: Database>(
226        self,
227        db: ODB,
228    ) -> EvmBuilder<'a, SetGenericStage, EXT, ODB> {
229        EvmBuilder {
230            context: Context::new(self.context.evm.with_db(db), self.context.external),
231            handler: EvmBuilder::<'a, SetGenericStage, EXT, ODB>::handler(self.handler.cfg()),
232            phantom: PhantomData,
233        }
234    }
235
236    /// Resets [`Handler`] and sets the [`DatabaseRef`] that will be used by [`Evm`]
237    /// and resets the [`Handler`] to default mainnet.
238    pub fn reset_handler_with_ref_db<ODB: DatabaseRef>(
239        self,
240        db: ODB,
241    ) -> EvmBuilder<'a, SetGenericStage, EXT, WrapDatabaseRef<ODB>> {
242        EvmBuilder {
243            context: Context::new(
244                self.context.evm.with_db(WrapDatabaseRef(db)),
245                self.context.external,
246            ),
247            handler: EvmBuilder::<'a, SetGenericStage, EXT, WrapDatabaseRef<ODB>>::handler(
248                self.handler.cfg(),
249            ),
250            phantom: PhantomData,
251        }
252    }
253
254    /// Resets [`Handler`] and sets new `ExternalContext` type.
255    ///  and resets the [`Handler`] to default mainnet.
256    pub fn reset_handler_with_external_context<OEXT>(
257        self,
258        external: OEXT,
259    ) -> EvmBuilder<'a, SetGenericStage, OEXT, DB> {
260        EvmBuilder {
261            context: Context::new(self.context.evm, external),
262            handler: EvmBuilder::<'a, SetGenericStage, OEXT, DB>::handler(self.handler.cfg()),
263            phantom: PhantomData,
264        }
265    }
266}
267
268impl<'a, BuilderStage, EXT, DB: Database> EvmBuilder<'a, BuilderStage, EXT, DB> {
269    /// Creates the default handler.
270    ///
271    /// This is useful for adding optimism handle register.
272    fn handler(handler_cfg: HandlerCfg) -> Handler<'a, Context<EXT, DB>, EXT, DB> {
273        Handler::new(handler_cfg)
274    }
275
276    /// This modifies the [EvmBuilder] to make it easy to construct an [`Evm`] with a _specific_
277    /// handler.
278    ///
279    /// # Example
280    /// ```rust
281    /// use revm::{EvmBuilder, Handler, primitives::{SpecId, HandlerCfg}};
282    /// use revm_interpreter::primitives::CancunSpec;
283    /// let builder = EvmBuilder::default();
284    ///
285    /// // get the desired handler
286    /// let mainnet = Handler::mainnet::<CancunSpec>();
287    /// let builder = builder.with_handler(mainnet);
288    ///
289    /// // build the EVM
290    /// let evm = builder.build();
291    /// ```
292    pub fn with_handler(
293        self,
294        handler: Handler<'a, Context<EXT, DB>, EXT, DB>,
295    ) -> EvmBuilder<'a, BuilderStage, EXT, DB> {
296        EvmBuilder {
297            context: self.context,
298            handler,
299            phantom: PhantomData,
300        }
301    }
302
303    /// Builds the [`Evm`].
304    pub fn build(self) -> Evm<'a, EXT, DB> {
305        Evm::new(self.context, self.handler)
306    }
307
308    /// Register Handler that modifies the behavior of EVM.
309    /// Check [`Handler`] for more information.
310    ///
311    /// When called, EvmBuilder will transition from SetGenericStage to HandlerStage.
312    pub fn append_handler_register(
313        mut self,
314        handle_register: register::HandleRegister<EXT, DB>,
315    ) -> EvmBuilder<'a, HandlerStage, EXT, DB> {
316        self.handler
317            .append_handler_register(register::HandleRegisters::Plain(handle_register));
318        EvmBuilder {
319            context: self.context,
320            handler: self.handler,
321
322            phantom: PhantomData,
323        }
324    }
325
326    /// Register Handler that modifies the behavior of EVM.
327    /// Check [`Handler`] for more information.
328    ///
329    /// When called, EvmBuilder will transition from SetGenericStage to HandlerStage.
330    pub fn append_handler_register_box(
331        mut self,
332        handle_register: register::HandleRegisterBox<'a, EXT, DB>,
333    ) -> EvmBuilder<'a, HandlerStage, EXT, DB> {
334        self.handler
335            .append_handler_register(register::HandleRegisters::Box(handle_register));
336        EvmBuilder {
337            context: self.context,
338            handler: self.handler,
339
340            phantom: PhantomData,
341        }
342    }
343
344    /// Sets specification Id , that will mark the version of EVM.
345    /// It represent the hard fork of ethereum.
346    ///
347    /// # Note
348    ///
349    /// When changed it will reapply all handle registers, this can be
350    /// expensive operation depending on registers.
351    pub fn with_spec_id(mut self, spec_id: SpecId) -> Self {
352        self.handler.modify_spec_id(spec_id);
353        EvmBuilder {
354            context: self.context,
355            handler: self.handler,
356
357            phantom: PhantomData,
358        }
359    }
360
361    /// Allows modification of Evm Database.
362    pub fn modify_db(mut self, f: impl FnOnce(&mut DB)) -> Self {
363        f(&mut self.context.evm.db);
364        self
365    }
366
367    /// Allows modification of external context.
368    pub fn modify_external_context(mut self, f: impl FnOnce(&mut EXT)) -> Self {
369        f(&mut self.context.external);
370        self
371    }
372
373    /// Allows modification of Evm Environment.
374    pub fn modify_env(mut self, f: impl FnOnce(&mut Box<Env>)) -> Self {
375        f(&mut self.context.evm.env);
376        self
377    }
378
379    /// Sets Evm Environment.
380    pub fn with_env(mut self, env: Box<Env>) -> Self {
381        self.context.evm.env = env;
382        self
383    }
384
385    /// Allows modification of Evm's Transaction Environment.
386    pub fn modify_tx_env(mut self, f: impl FnOnce(&mut TxEnv)) -> Self {
387        f(&mut self.context.evm.env.tx);
388        self
389    }
390
391    /// Sets Evm's Transaction Environment.
392    pub fn with_tx_env(mut self, tx_env: TxEnv) -> Self {
393        self.context.evm.env.tx = tx_env;
394        self
395    }
396
397    /// Allows modification of Evm's Block Environment.
398    pub fn modify_block_env(mut self, f: impl FnOnce(&mut BlockEnv)) -> Self {
399        f(&mut self.context.evm.env.block);
400        self
401    }
402
403    /// Sets Evm's Block Environment.
404    pub fn with_block_env(mut self, block_env: BlockEnv) -> Self {
405        self.context.evm.env.block = block_env;
406        self
407    }
408
409    /// Allows modification of Evm's Config Environment.
410    pub fn modify_cfg_env(mut self, f: impl FnOnce(&mut CfgEnv)) -> Self {
411        f(&mut self.context.evm.env.cfg);
412        self
413    }
414
415    /// Clears Environment of EVM.
416    pub fn with_clear_env(mut self) -> Self {
417        self.context.evm.env.clear();
418        self
419    }
420
421    /// Clears Transaction environment of EVM.
422    pub fn with_clear_tx_env(mut self) -> Self {
423        self.context.evm.env.tx.clear();
424        self
425    }
426    /// Clears Block environment of EVM.
427    pub fn with_clear_block_env(mut self) -> Self {
428        self.context.evm.env.block.clear();
429        self
430    }
431
432    /// Resets [`Handler`] to default mainnet.
433    pub fn reset_handler(mut self) -> Self {
434        self.handler = Self::handler(self.handler.cfg());
435        self
436    }
437}
438
439#[cfg(test)]
440mod test {
441    use super::SpecId;
442    use crate::{
443        db::EmptyDB,
444        inspector::inspector_handle_register,
445        inspectors::NoOpInspector,
446        primitives::{
447            address, AccountInfo, Address, Bytecode, Bytes, PrecompileResult, TxKind, U256,
448        },
449        Context, ContextPrecompile, ContextStatefulPrecompile, Evm, InMemoryDB, InnerEvmContext,
450    };
451    use revm_interpreter::{gas, Host, Interpreter};
452    use revm_precompile::PrecompileOutput;
453    use std::{cell::RefCell, rc::Rc, sync::Arc};
454
455    /// Custom evm context
456    #[derive(Default, Clone, Debug)]
457    pub(crate) struct CustomContext {
458        pub(crate) inner: Rc<RefCell<u8>>,
459    }
460
461    #[test]
462    fn simple_add_stateful_instruction() {
463        let code = Bytecode::new_raw([0xED, 0x00].into());
464        let code_hash = code.hash_slow();
465        let to_addr = address!("ffffffffffffffffffffffffffffffffffffffff");
466
467        // initialize the custom context and make sure it's zero
468        let custom_context = CustomContext::default();
469        assert_eq!(*custom_context.inner.borrow(), 0);
470
471        let to_capture = custom_context.clone();
472        let mut evm = Evm::builder()
473            .with_db(InMemoryDB::default())
474            .modify_db(|db| {
475                db.insert_account_info(to_addr, AccountInfo::new(U256::ZERO, 0, code_hash, code))
476            })
477            .modify_tx_env(|tx| tx.transact_to = TxKind::Call(to_addr))
478            // we need to use handle register box to capture the custom context in the handle
479            // register
480            .append_handler_register_box(Box::new(move |handler| {
481                let custom_context = to_capture.clone();
482
483                // we need to use a box to capture the custom context in the instruction
484                let custom_instruction = Box::new(
485                    move |_interp: &mut Interpreter, _host: &mut Context<(), InMemoryDB>| {
486                        // modify the value
487                        let mut inner = custom_context.inner.borrow_mut();
488                        *inner += 1;
489                    },
490                );
491
492                // need to  ensure the instruction table is a boxed instruction table so that we
493                // can insert the custom instruction as a boxed instruction
494                handler
495                    .instruction_table
496                    .insert_boxed(0xED, custom_instruction);
497            }))
498            .build();
499
500        let _result_and_state = evm.transact().unwrap();
501
502        // ensure the custom context was modified
503        assert_eq!(*custom_context.inner.borrow(), 1);
504    }
505
506    #[test]
507    fn simple_add_instruction() {
508        const CUSTOM_INSTRUCTION_COST: u64 = 133;
509        const INITIAL_TX_GAS: u64 = 21000;
510        const EXPECTED_RESULT_GAS: u64 = INITIAL_TX_GAS + CUSTOM_INSTRUCTION_COST;
511
512        fn custom_instruction(interp: &mut Interpreter, _host: &mut impl Host) {
513            // just spend some gas
514            gas!(interp, CUSTOM_INSTRUCTION_COST);
515        }
516
517        let code = Bytecode::new_raw([0xED, 0x00].into());
518        let code_hash = code.hash_slow();
519        let to_addr = address!("ffffffffffffffffffffffffffffffffffffffff");
520
521        let mut evm = Evm::builder()
522            .with_db(InMemoryDB::default())
523            .modify_db(|db| {
524                db.insert_account_info(to_addr, AccountInfo::new(U256::ZERO, 0, code_hash, code))
525            })
526            .modify_tx_env(|tx| tx.transact_to = TxKind::Call(to_addr))
527            .append_handler_register(|handler| {
528                handler.instruction_table.insert(0xED, custom_instruction)
529            })
530            .build();
531
532        let result_and_state = evm.transact().unwrap();
533        assert_eq!(result_and_state.result.gas_used(), EXPECTED_RESULT_GAS);
534    }
535
536    #[test]
537    fn simple_build() {
538        // build without external with latest spec
539        Evm::builder().build();
540        // build with empty db
541        Evm::builder().with_empty_db().build();
542        // build with_db
543        Evm::builder().with_db(EmptyDB::default()).build();
544        // build with empty external
545        Evm::builder().with_empty_db().build();
546        // build with some external
547        Evm::builder()
548            .with_empty_db()
549            .with_external_context(())
550            .build();
551        // build with spec
552        Evm::builder()
553            .with_empty_db()
554            .with_spec_id(SpecId::HOMESTEAD)
555            .build();
556
557        // with with Env change in multiple places
558        Evm::builder()
559            .with_empty_db()
560            .modify_tx_env(|tx| tx.gas_limit = 10)
561            .build();
562        Evm::builder().modify_tx_env(|tx| tx.gas_limit = 10).build();
563        Evm::builder()
564            .with_empty_db()
565            .modify_tx_env(|tx| tx.gas_limit = 10)
566            .build();
567        Evm::builder()
568            .with_empty_db()
569            .modify_tx_env(|tx| tx.gas_limit = 10)
570            .build();
571
572        // with inspector handle
573        Evm::builder()
574            .with_empty_db()
575            .with_external_context(NoOpInspector)
576            .append_handler_register(inspector_handle_register)
577            .build();
578
579        // create the builder
580        let evm = Evm::builder()
581            .with_db(EmptyDB::default())
582            .with_external_context(NoOpInspector)
583            .append_handler_register(inspector_handle_register)
584            // this would not compile
585            // .with_db(..)
586            .build();
587
588        let Context { external: _, .. } = evm.into_context();
589    }
590
591    #[test]
592    fn build_modify_build() {
593        // build evm
594        let evm = Evm::builder()
595            .with_empty_db()
596            .with_spec_id(SpecId::HOMESTEAD)
597            .build();
598
599        // modify evm
600        let evm = evm.modify().with_spec_id(SpecId::FRONTIER).build();
601        let _ = evm
602            .modify()
603            .modify_tx_env(|tx| tx.chain_id = Some(2))
604            .build();
605    }
606
607    #[test]
608    fn build_custom_precompile() {
609        struct CustomPrecompile;
610
611        impl ContextStatefulPrecompile<EmptyDB> for CustomPrecompile {
612            fn call(
613                &self,
614                _input: &Bytes,
615                _gas_limit: u64,
616                _context: &mut InnerEvmContext<EmptyDB>,
617            ) -> PrecompileResult {
618                Ok(PrecompileOutput::new(10, Bytes::new()))
619            }
620        }
621
622        let mut evm = Evm::builder()
623            .with_empty_db()
624            .with_spec_id(SpecId::HOMESTEAD)
625            .append_handler_register(|handler| {
626                let precompiles = handler.pre_execution.load_precompiles();
627                handler.pre_execution.load_precompiles = Arc::new(move || {
628                    let mut precompiles = precompiles.clone();
629                    precompiles.extend([(
630                        Address::ZERO,
631                        ContextPrecompile::ContextStateful(Arc::new(CustomPrecompile)),
632                    )]);
633                    precompiles
634                });
635            })
636            .build();
637
638        evm.transact().unwrap();
639    }
640}