1#![cfg_attr(not(test), warn(unused_crate_dependencies))]
8
9use revm::{
10 context::{
11 journaled_state::{account::JournaledAccount, AccountInfoLoad, JournalLoadError},
12 result::InvalidTransaction,
13 BlockEnv, Cfg, CfgEnv, ContextTr, Evm, LocalContext, TxEnv,
14 },
15 context_interface::{
16 journaled_state::{AccountLoad, JournalCheckpoint, TransferError},
17 result::EVMError,
18 Block, JournalTr, Transaction,
19 },
20 database::InMemoryDB,
21 handler::{
22 instructions::{EthInstructions, InstructionProvider},
23 EthPrecompiles, PrecompileProvider,
24 },
25 inspector::{inspectors::TracerEip3155, JournalExt},
26 interpreter::{
27 interpreter::EthInterpreter, CallInputs, CallOutcome, InterpreterResult, SStoreResult,
28 SelfDestructResult, StateLoad,
29 },
30 primitives::{hardfork::SpecId, Address, HashSet, Log, StorageKey, StorageValue, B256, U256},
31 state::{Account, Bytecode, EvmState},
32 Context, Database, DatabaseCommit, InspectEvm, Inspector, Journal, JournalEntry,
33};
34use std::{convert::Infallible, fmt::Debug};
35
36#[derive(Clone, Debug)]
40struct Backend {
41 journaled_state: Journal<InMemoryDB>,
43 method_with_inspector_counter: usize,
45 method_without_inspector_counter: usize,
46}
47
48impl Backend {
49 fn new(spec: SpecId, db: InMemoryDB) -> Self {
50 let mut journaled_state = Journal::new(db);
51 journaled_state.set_spec_id(spec);
52 Self {
53 journaled_state,
54 method_with_inspector_counter: 0,
55 method_without_inspector_counter: 0,
56 }
57 }
58}
59
60impl JournalTr for Backend {
61 type Database = InMemoryDB;
62 type State = EvmState;
63 type JournaledAccount<'a> = JournaledAccount<'a, InMemoryDB, JournalEntry>;
64
65 fn new(database: InMemoryDB) -> Self {
66 Self::new(SpecId::default(), database)
67 }
68
69 fn db(&self) -> &Self::Database {
70 self.journaled_state.db()
71 }
72
73 fn db_mut(&mut self) -> &mut Self::Database {
74 self.journaled_state.db_mut()
75 }
76
77 fn sload(
78 &mut self,
79 address: Address,
80 key: StorageKey,
81 ) -> Result<StateLoad<StorageValue>, <Self::Database as Database>::Error> {
82 self.journaled_state.sload(address, key)
83 }
84
85 fn sstore(
86 &mut self,
87 address: Address,
88 key: StorageKey,
89 value: StorageValue,
90 ) -> Result<StateLoad<SStoreResult>, <Self::Database as Database>::Error> {
91 self.journaled_state.sstore(address, key, value)
92 }
93
94 fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
95 self.journaled_state.tload(address, key)
96 }
97
98 fn tstore(&mut self, address: Address, key: StorageKey, value: StorageValue) {
99 self.journaled_state.tstore(address, key, value)
100 }
101
102 fn log(&mut self, log: Log) {
103 self.journaled_state.log(log)
104 }
105
106 fn logs(&self) -> &[Log] {
107 self.journaled_state.logs()
108 }
109
110 fn selfdestruct(
111 &mut self,
112 address: Address,
113 target: Address,
114 skip_cold_load: bool,
115 ) -> Result<StateLoad<SelfDestructResult>, JournalLoadError<Infallible>> {
116 self.journaled_state
117 .selfdestruct(address, target, skip_cold_load)
118 }
119
120 fn warm_access_list(
121 &mut self,
122 access_list: revm::primitives::HashMap<Address, HashSet<StorageKey>>,
123 ) {
124 self.journaled_state.warm_access_list(access_list);
125 }
126
127 fn warm_coinbase_account(&mut self, address: Address) {
128 self.journaled_state.warm_coinbase_account(address)
129 }
130
131 fn warm_precompiles(&mut self, addresses: HashSet<Address>) {
132 self.journaled_state.warm_precompiles(addresses)
133 }
134
135 fn precompile_addresses(&self) -> &HashSet<Address> {
136 self.journaled_state.precompile_addresses()
137 }
138
139 fn set_spec_id(&mut self, spec_id: SpecId) {
140 self.journaled_state.set_spec_id(spec_id);
141 }
142
143 fn touch_account(&mut self, address: Address) {
144 self.journaled_state.touch_account(address);
145 }
146
147 fn transfer(
148 &mut self,
149 from: Address,
150 to: Address,
151 balance: U256,
152 ) -> Result<Option<TransferError>, Infallible> {
153 self.journaled_state.transfer(from, to, balance)
154 }
155
156 fn transfer_loaded(
157 &mut self,
158 from: Address,
159 to: Address,
160 balance: U256,
161 ) -> Option<TransferError> {
162 self.journaled_state.transfer_loaded(from, to, balance)
163 }
164
165 fn load_account(&mut self, address: Address) -> Result<StateLoad<&Account>, Infallible> {
166 self.journaled_state.load_account(address)
167 }
168
169 fn load_account_with_code(
170 &mut self,
171 address: Address,
172 ) -> Result<StateLoad<&Account>, Infallible> {
173 self.journaled_state.load_account_with_code(address)
174 }
175
176 fn load_account_delegated(
177 &mut self,
178 address: Address,
179 ) -> Result<StateLoad<AccountLoad>, Infallible> {
180 self.journaled_state.load_account_delegated(address)
181 }
182
183 fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
184 self.journaled_state.set_code_with_hash(address, code, hash);
185 }
186
187 fn code(
188 &mut self,
189 address: Address,
190 ) -> Result<StateLoad<revm::primitives::Bytes>, <Self::Database as Database>::Error> {
191 self.journaled_state.code(address)
192 }
193
194 fn code_hash(
195 &mut self,
196 address: Address,
197 ) -> Result<StateLoad<B256>, <Self::Database as Database>::Error> {
198 self.journaled_state.code_hash(address)
199 }
200
201 fn clear(&mut self) {
202 self.journaled_state.clear();
203 }
204
205 fn checkpoint(&mut self) -> JournalCheckpoint {
206 self.journaled_state.checkpoint()
207 }
208
209 fn checkpoint_commit(&mut self) {
210 self.journaled_state.checkpoint_commit()
211 }
212
213 fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
214 self.journaled_state.checkpoint_revert(checkpoint)
215 }
216
217 fn create_account_checkpoint(
218 &mut self,
219 caller: Address,
220 address: Address,
221 balance: U256,
222 spec_id: SpecId,
223 ) -> Result<JournalCheckpoint, TransferError> {
224 self.journaled_state
225 .create_account_checkpoint(caller, address, balance, spec_id)
226 }
227
228 #[inline]
230 fn depth(&self) -> usize {
231 self.journaled_state.depth()
232 }
233
234 fn finalize(&mut self) -> Self::State {
235 self.journaled_state.finalize()
236 }
237
238 fn caller_accounting_journal_entry(
239 &mut self,
240 address: Address,
241 old_balance: U256,
242 bump_nonce: bool,
243 ) {
244 self.journaled_state
245 .caller_accounting_journal_entry(address, old_balance, bump_nonce)
246 }
247
248 fn balance_incr(
249 &mut self,
250 address: Address,
251 balance: U256,
252 ) -> Result<(), <Self::Database as Database>::Error> {
253 self.journaled_state.balance_incr(address, balance)
254 }
255
256 fn nonce_bump_journal_entry(&mut self, address: Address) {
257 self.journaled_state.nonce_bump_journal_entry(address)
258 }
259
260 fn take_logs(&mut self) -> Vec<Log> {
261 self.journaled_state.take_logs()
262 }
263
264 fn commit_tx(&mut self) {
265 self.journaled_state.commit_tx()
266 }
267
268 fn discard_tx(&mut self) {
269 self.journaled_state.discard_tx()
270 }
271
272 fn sload_skip_cold_load(
273 &mut self,
274 address: Address,
275 key: StorageKey,
276 skip_cold_load: bool,
277 ) -> Result<StateLoad<StorageValue>, JournalLoadError<<Self::Database as Database>::Error>>
278 {
279 self.journaled_state
280 .sload_skip_cold_load(address, key, skip_cold_load)
281 }
282
283 fn sstore_skip_cold_load(
284 &mut self,
285 address: Address,
286 key: StorageKey,
287 value: StorageValue,
288 skip_cold_load: bool,
289 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<<Self::Database as Database>::Error>>
290 {
291 self.journaled_state
292 .sstore_skip_cold_load(address, key, value, skip_cold_load)
293 }
294
295 fn load_account_info_skip_cold_load(
296 &mut self,
297 address: Address,
298 load_code: bool,
299 skip_cold_load: bool,
300 ) -> Result<AccountInfoLoad<'_>, JournalLoadError<Infallible>> {
301 self.journaled_state
302 .load_account_info_skip_cold_load(address, load_code, skip_cold_load)
303 }
304
305 fn load_account_mut_optional_code(
306 &mut self,
307 address: Address,
308 load_code: bool,
309 ) -> Result<StateLoad<Self::JournaledAccount<'_>>, Infallible> {
310 self.journaled_state
311 .load_account_mut_optional_code(address, load_code)
312 }
313}
314
315impl JournalExt for Backend {
316 fn journal(&self) -> &[JournalEntry] {
317 self.journaled_state.journal()
318 }
319
320 fn evm_state(&self) -> &EvmState {
321 self.journaled_state.evm_state()
322 }
323
324 fn evm_state_mut(&mut self) -> &mut EvmState {
325 self.journaled_state.evm_state_mut()
326 }
327}
328
329trait DatabaseExt: JournalTr {
332 fn method_that_takes_inspector_as_argument<
335 InspectorT,
336 BlockT,
337 TxT,
338 CfgT,
339 InstructionProviderT,
340 PrecompileT,
341 >(
342 &mut self,
343 env: Env<BlockT, TxT, CfgT>,
344 inspector: InspectorT,
345 ) -> anyhow::Result<()>
346 where
347 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
348 BlockT: Block,
349 TxT: Transaction + Clone,
350 CfgT: Cfg,
351 InstructionProviderT: InstructionProvider<
352 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
353 InterpreterTypes = EthInterpreter,
354 > + Default,
355 PrecompileT: PrecompileProvider<
356 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
357 Output = InterpreterResult,
358 > + Default;
359
360 fn method_that_constructs_inspector<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
362 &mut self,
363 env: Env<BlockT, TxT, CfgT>,
364 ) -> anyhow::Result<()>
365 where
366 BlockT: Block,
367 TxT: Transaction + Clone,
368 CfgT: Cfg,
369 InstructionProviderT: InstructionProvider<
370 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
371 InterpreterTypes = EthInterpreter,
372 > + Default,
373 PrecompileT: PrecompileProvider<
374 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
375 Output = InterpreterResult,
376 > + Default;
377}
378
379impl DatabaseExt for Backend {
380 fn method_that_takes_inspector_as_argument<
381 InspectorT,
382 BlockT,
383 TxT,
384 CfgT,
385 InstructionProviderT,
386 PrecompileT,
387 >(
388 &mut self,
389 env: Env<BlockT, TxT, CfgT>,
390 inspector: InspectorT,
391 ) -> anyhow::Result<()>
392 where
393 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
394 BlockT: Block,
395 TxT: Transaction + Clone,
396 CfgT: Cfg,
397 InstructionProviderT: InstructionProvider<
398 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
399 InterpreterTypes = EthInterpreter,
400 > + Default,
401 PrecompileT: PrecompileProvider<
402 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
403 Output = InterpreterResult,
404 > + Default,
405 {
406 commit_transaction::<InspectorT, BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
407 self, env, inspector,
408 )?;
409 self.method_with_inspector_counter += 1;
410 Ok(())
411 }
412
413 fn method_that_constructs_inspector<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
414 &mut self,
415 env: Env<BlockT, TxT, CfgT>,
416 ) -> anyhow::Result<()>
417 where
418 BlockT: Block,
419 TxT: Transaction + Clone,
420 CfgT: Cfg,
421 InstructionProviderT: InstructionProvider<
422 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
423 InterpreterTypes = EthInterpreter,
424 > + Default,
425 PrecompileT: PrecompileProvider<
426 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
427 Output = InterpreterResult,
428 > + Default,
429 {
430 let inspector = TracerEip3155::new(Box::new(std::io::sink()));
431 commit_transaction::<
432 TracerEip3155,
434 BlockT,
435 TxT,
436 CfgT,
437 InstructionProviderT,
438 PrecompileT,
439 >(self, env, inspector)?;
440
441 self.method_without_inspector_counter += 1;
442 Ok(())
443 }
444}
445
446#[derive(Clone, Default)]
449struct Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT> {
450 call_count: usize,
451 phantom: core::marker::PhantomData<(BlockT, TxT, CfgT, InstructionProviderT, PrecompileT)>,
452}
453
454impl<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
455 Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
456where
457 BlockT: Block + Clone,
458 TxT: Transaction + Clone,
459 CfgT: Cfg + Clone,
460 InstructionProviderT: InstructionProvider<
461 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
462 InterpreterTypes = EthInterpreter,
463 > + Default,
464 PrecompileT: PrecompileProvider<
465 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
466 Output = InterpreterResult,
467 > + Default,
468{
469 fn apply_cheatcode(
470 &mut self,
471 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
472 ) -> anyhow::Result<()> {
473 let block = context.block.clone();
475 let tx = context.tx.clone();
476 let cfg = context.cfg.clone();
477
478 context
480 .journal_mut()
481 .method_that_takes_inspector_as_argument::<_, _, _, _, InstructionProviderT, PrecompileT>(
482 Env {
483 block: block.clone(),
484 tx: tx.clone(),
485 cfg: cfg.clone(),
486 },
487 self,
488 )?;
489
490 context
492 .journal_mut()
493 .method_that_constructs_inspector::<_, _, _, InstructionProviderT, PrecompileT>(
494 Env { block, tx, cfg },
495 )?;
496 Ok(())
497 }
498}
499
500impl<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
501 Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>>
502 for Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
503where
504 BlockT: Block + Clone,
505 TxT: Transaction + Clone,
506 CfgT: Cfg + Clone,
507 InstructionProviderT: InstructionProvider<
508 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
509 InterpreterTypes = EthInterpreter,
510 > + Default,
511 PrecompileT: PrecompileProvider<
512 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
513 Output = InterpreterResult,
514 > + Default,
515{
516 fn call(
518 &mut self,
519 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
520 _inputs: &mut CallInputs,
521 ) -> Option<CallOutcome> {
522 self.call_count += 1;
523 if self.call_count == 1 {
525 self.apply_cheatcode(context).unwrap();
528 }
529 None
530 }
531}
532
533#[derive(Clone, Debug)]
535struct Env<BlockT, TxT, CfgT> {
536 block: BlockT,
537 tx: TxT,
538 cfg: CfgT,
539}
540
541impl Env<BlockEnv, TxEnv, CfgEnv> {
542 fn mainnet() -> Self {
543 let mut cfg = CfgEnv::default();
545 cfg.disable_nonce_check = true;
546
547 Self {
548 block: BlockEnv::default(),
549 tx: TxEnv::default(),
550 cfg,
551 }
552 }
553}
554
555fn commit_transaction<InspectorT, BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
558 backend: &mut Backend,
559 env: Env<BlockT, TxT, CfgT>,
560 inspector: InspectorT,
561) -> Result<(), EVMError<Infallible, InvalidTransaction>>
562where
563 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
564 BlockT: Block,
565 TxT: Transaction + Clone,
566 CfgT: Cfg,
567 InstructionProviderT: InstructionProvider<
568 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
569 InterpreterTypes = EthInterpreter,
570 > + Default,
571 PrecompileT: PrecompileProvider<
572 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
573 Output = InterpreterResult,
574 > + Default,
575{
576 let new_backend = backend.clone();
581 let tx = env.tx.clone();
582
583 let context = Context {
584 tx: env.tx,
585 block: env.block,
586 cfg: env.cfg,
587 journaled_state: new_backend,
588 chain: (),
589 local: LocalContext::default(),
590 error: Ok(()),
591 };
592
593 let mut evm = Evm::new_with_inspector(
594 context,
595 inspector,
596 InstructionProviderT::default(),
597 PrecompileT::default(),
598 );
599
600 let state = evm.inspect_tx(tx)?.state;
601
602 backend.journaled_state.database.commit(state);
604 update_state(
605 &mut backend.journaled_state.inner.state,
606 &mut backend.journaled_state.database,
607 )?;
608
609 Ok(())
610}
611
612fn update_state<DB: Database>(state: &mut EvmState, db: &mut DB) -> Result<(), DB::Error> {
615 for (addr, acc) in state.iter_mut() {
616 acc.info = db.basic(*addr)?.unwrap_or_default();
617 for (key, val) in acc.storage.iter_mut() {
618 val.present_value = db.storage(*addr, *key)?;
619 }
620 }
621
622 Ok(())
623}
624
625fn main() -> anyhow::Result<()> {
626 let backend = Backend::new(SpecId::default(), InMemoryDB::default());
627 let mut inspector = Cheatcodes::<
628 BlockEnv,
629 TxEnv,
630 CfgEnv,
631 EthInstructions<EthInterpreter, Context<BlockEnv, TxEnv, CfgEnv, InMemoryDB, Backend>>,
632 EthPrecompiles,
633 >::default();
634 let env = Env::mainnet();
635 let tx = env.tx.clone();
636
637 let context = Context {
638 tx: env.tx,
639 block: env.block,
640 cfg: env.cfg,
641 journaled_state: backend,
642 chain: (),
643 local: LocalContext::default(),
644 error: Ok(()),
645 };
646
647 let mut evm = Evm::new_with_inspector(
648 context,
649 &mut inspector,
650 EthInstructions::default(),
651 EthPrecompiles::default(),
652 );
653 evm.inspect_tx(tx)?;
654
655 assert_eq!(evm.inspector.call_count, 2);
657 assert_eq!(evm.journaled_state.method_with_inspector_counter, 1);
658 assert_eq!(evm.journaled_state.method_without_inspector_counter, 1);
659
660 Ok(())
661}