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
12pub struct EvmBuilder<'a, BuilderStage, EXT, DB: Database> {
16 context: Context<EXT, DB>,
17 handler: Handler<'a, Context<EXT, DB>, EXT, DB>,
19 phantom: PhantomData<BuilderStage>,
21}
22
23pub struct SetGenericStage;
26
27pub 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 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 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 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 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 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 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 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 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 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 #[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 #[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 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 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 #[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 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 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 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 fn handler(handler_cfg: HandlerCfg) -> Handler<'a, Context<EXT, DB>, EXT, DB> {
273 Handler::new(handler_cfg)
274 }
275
276 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 pub fn build(self) -> Evm<'a, EXT, DB> {
305 Evm::new(self.context, self.handler)
306 }
307
308 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 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 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 pub fn modify_db(mut self, f: impl FnOnce(&mut DB)) -> Self {
363 f(&mut self.context.evm.db);
364 self
365 }
366
367 pub fn modify_external_context(mut self, f: impl FnOnce(&mut EXT)) -> Self {
369 f(&mut self.context.external);
370 self
371 }
372
373 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 pub fn with_env(mut self, env: Box<Env>) -> Self {
381 self.context.evm.env = env;
382 self
383 }
384
385 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 pub fn with_tx_env(mut self, tx_env: TxEnv) -> Self {
393 self.context.evm.env.tx = tx_env;
394 self
395 }
396
397 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 pub fn with_block_env(mut self, block_env: BlockEnv) -> Self {
405 self.context.evm.env.block = block_env;
406 self
407 }
408
409 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 pub fn with_clear_env(mut self) -> Self {
417 self.context.evm.env.clear();
418 self
419 }
420
421 pub fn with_clear_tx_env(mut self) -> Self {
423 self.context.evm.env.tx.clear();
424 self
425 }
426 pub fn with_clear_block_env(mut self) -> Self {
428 self.context.evm.env.block.clear();
429 self
430 }
431
432 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 #[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 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 .append_handler_register_box(Box::new(move |handler| {
481 let custom_context = to_capture.clone();
482
483 let custom_instruction = Box::new(
485 move |_interp: &mut Interpreter, _host: &mut Context<(), InMemoryDB>| {
486 let mut inner = custom_context.inner.borrow_mut();
488 *inner += 1;
489 },
490 );
491
492 handler
495 .instruction_table
496 .insert_boxed(0xED, custom_instruction);
497 }))
498 .build();
499
500 let _result_and_state = evm.transact().unwrap();
501
502 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 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 Evm::builder().build();
540 Evm::builder().with_empty_db().build();
542 Evm::builder().with_db(EmptyDB::default()).build();
544 Evm::builder().with_empty_db().build();
546 Evm::builder()
548 .with_empty_db()
549 .with_external_context(())
550 .build();
551 Evm::builder()
553 .with_empty_db()
554 .with_spec_id(SpecId::HOMESTEAD)
555 .build();
556
557 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 Evm::builder()
574 .with_empty_db()
575 .with_external_context(NoOpInspector)
576 .append_handler_register(inspector_handle_register)
577 .build();
578
579 let evm = Evm::builder()
581 .with_db(EmptyDB::default())
582 .with_external_context(NoOpInspector)
583 .append_handler_register(inspector_handle_register)
584 .build();
587
588 let Context { external: _, .. } = evm.into_context();
589 }
590
591 #[test]
592 fn build_modify_build() {
593 let evm = Evm::builder()
595 .with_empty_db()
596 .with_spec_id(SpecId::HOMESTEAD)
597 .build();
598
599 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}