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_mut_skip_cold_load(
296 &mut self,
297 address: Address,
298 skip_cold_load: bool,
299 ) -> Result<StateLoad<Self::JournaledAccount<'_>>, Infallible> {
300 self.journaled_state
301 .load_account_mut_skip_cold_load(address, skip_cold_load)
302 }
303
304 fn load_account_info_skip_cold_load(
305 &mut self,
306 address: Address,
307 load_code: bool,
308 skip_cold_load: bool,
309 ) -> Result<AccountInfoLoad<'_>, JournalLoadError<Infallible>> {
310 self.journaled_state
311 .load_account_info_skip_cold_load(address, load_code, skip_cold_load)
312 }
313
314 fn load_account_mut_optional_code(
315 &mut self,
316 address: Address,
317 load_code: bool,
318 ) -> Result<StateLoad<Self::JournaledAccount<'_>>, Infallible> {
319 self.journaled_state
320 .load_account_mut_optional_code(address, load_code)
321 }
322}
323
324impl JournalExt for Backend {
325 fn journal(&self) -> &[JournalEntry] {
326 self.journaled_state.journal()
327 }
328
329 fn evm_state(&self) -> &EvmState {
330 self.journaled_state.evm_state()
331 }
332
333 fn evm_state_mut(&mut self) -> &mut EvmState {
334 self.journaled_state.evm_state_mut()
335 }
336}
337
338trait DatabaseExt: JournalTr {
341 fn method_that_takes_inspector_as_argument<
344 InspectorT,
345 BlockT,
346 TxT,
347 CfgT,
348 InstructionProviderT,
349 PrecompileT,
350 >(
351 &mut self,
352 env: Env<BlockT, TxT, CfgT>,
353 inspector: InspectorT,
354 ) -> anyhow::Result<()>
355 where
356 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
357 BlockT: Block,
358 TxT: Transaction + Clone,
359 CfgT: Cfg,
360 InstructionProviderT: InstructionProvider<
361 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
362 InterpreterTypes = EthInterpreter,
363 > + Default,
364 PrecompileT: PrecompileProvider<
365 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
366 Output = InterpreterResult,
367 > + Default;
368
369 fn method_that_constructs_inspector<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
371 &mut self,
372 env: Env<BlockT, TxT, CfgT>,
373 ) -> anyhow::Result<()>
374 where
375 BlockT: Block,
376 TxT: Transaction + Clone,
377 CfgT: Cfg,
378 InstructionProviderT: InstructionProvider<
379 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
380 InterpreterTypes = EthInterpreter,
381 > + Default,
382 PrecompileT: PrecompileProvider<
383 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
384 Output = InterpreterResult,
385 > + Default;
386}
387
388impl DatabaseExt for Backend {
389 fn method_that_takes_inspector_as_argument<
390 InspectorT,
391 BlockT,
392 TxT,
393 CfgT,
394 InstructionProviderT,
395 PrecompileT,
396 >(
397 &mut self,
398 env: Env<BlockT, TxT, CfgT>,
399 inspector: InspectorT,
400 ) -> anyhow::Result<()>
401 where
402 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
403 BlockT: Block,
404 TxT: Transaction + Clone,
405 CfgT: Cfg,
406 InstructionProviderT: InstructionProvider<
407 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
408 InterpreterTypes = EthInterpreter,
409 > + Default,
410 PrecompileT: PrecompileProvider<
411 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
412 Output = InterpreterResult,
413 > + Default,
414 {
415 commit_transaction::<InspectorT, BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
416 self, env, inspector,
417 )?;
418 self.method_with_inspector_counter += 1;
419 Ok(())
420 }
421
422 fn method_that_constructs_inspector<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
423 &mut self,
424 env: Env<BlockT, TxT, CfgT>,
425 ) -> anyhow::Result<()>
426 where
427 BlockT: Block,
428 TxT: Transaction + Clone,
429 CfgT: Cfg,
430 InstructionProviderT: InstructionProvider<
431 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
432 InterpreterTypes = EthInterpreter,
433 > + Default,
434 PrecompileT: PrecompileProvider<
435 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
436 Output = InterpreterResult,
437 > + Default,
438 {
439 let inspector = TracerEip3155::new(Box::new(std::io::sink()));
440 commit_transaction::<
441 TracerEip3155,
443 BlockT,
444 TxT,
445 CfgT,
446 InstructionProviderT,
447 PrecompileT,
448 >(self, env, inspector)?;
449
450 self.method_without_inspector_counter += 1;
451 Ok(())
452 }
453}
454
455#[derive(Clone, Default)]
458struct Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT> {
459 call_count: usize,
460 phantom: core::marker::PhantomData<(BlockT, TxT, CfgT, InstructionProviderT, PrecompileT)>,
461}
462
463impl<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
464 Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
465where
466 BlockT: Block + Clone,
467 TxT: Transaction + Clone,
468 CfgT: Cfg + Clone,
469 InstructionProviderT: InstructionProvider<
470 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
471 InterpreterTypes = EthInterpreter,
472 > + Default,
473 PrecompileT: PrecompileProvider<
474 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
475 Output = InterpreterResult,
476 > + Default,
477{
478 fn apply_cheatcode(
479 &mut self,
480 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
481 ) -> anyhow::Result<()> {
482 let block = context.block.clone();
484 let tx = context.tx.clone();
485 let cfg = context.cfg.clone();
486
487 context
489 .journal_mut()
490 .method_that_takes_inspector_as_argument::<_, _, _, _, InstructionProviderT, PrecompileT>(
491 Env {
492 block: block.clone(),
493 tx: tx.clone(),
494 cfg: cfg.clone(),
495 },
496 self,
497 )?;
498
499 context
501 .journal_mut()
502 .method_that_constructs_inspector::<_, _, _, InstructionProviderT, PrecompileT>(
503 Env { block, tx, cfg },
504 )?;
505 Ok(())
506 }
507}
508
509impl<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
510 Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>>
511 for Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
512where
513 BlockT: Block + Clone,
514 TxT: Transaction + Clone,
515 CfgT: Cfg + Clone,
516 InstructionProviderT: InstructionProvider<
517 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
518 InterpreterTypes = EthInterpreter,
519 > + Default,
520 PrecompileT: PrecompileProvider<
521 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
522 Output = InterpreterResult,
523 > + Default,
524{
525 fn call(
527 &mut self,
528 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
529 _inputs: &mut CallInputs,
530 ) -> Option<CallOutcome> {
531 self.call_count += 1;
532 if self.call_count == 1 {
534 self.apply_cheatcode(context).unwrap();
537 }
538 None
539 }
540}
541
542#[derive(Clone, Debug)]
544struct Env<BlockT, TxT, CfgT> {
545 block: BlockT,
546 tx: TxT,
547 cfg: CfgT,
548}
549
550impl Env<BlockEnv, TxEnv, CfgEnv> {
551 fn mainnet() -> Self {
552 let mut cfg = CfgEnv::default();
554 cfg.disable_nonce_check = true;
555
556 Self {
557 block: BlockEnv::default(),
558 tx: TxEnv::default(),
559 cfg,
560 }
561 }
562}
563
564fn commit_transaction<InspectorT, BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
567 backend: &mut Backend,
568 env: Env<BlockT, TxT, CfgT>,
569 inspector: InspectorT,
570) -> Result<(), EVMError<Infallible, InvalidTransaction>>
571where
572 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
573 BlockT: Block,
574 TxT: Transaction + Clone,
575 CfgT: Cfg,
576 InstructionProviderT: InstructionProvider<
577 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
578 InterpreterTypes = EthInterpreter,
579 > + Default,
580 PrecompileT: PrecompileProvider<
581 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
582 Output = InterpreterResult,
583 > + Default,
584{
585 let new_backend = backend.clone();
590 let tx = env.tx.clone();
591
592 let context = Context {
593 tx: env.tx,
594 block: env.block,
595 cfg: env.cfg,
596 journaled_state: new_backend,
597 chain: (),
598 local: LocalContext::default(),
599 error: Ok(()),
600 };
601
602 let mut evm = Evm::new_with_inspector(
603 context,
604 inspector,
605 InstructionProviderT::default(),
606 PrecompileT::default(),
607 );
608
609 let state = evm.inspect_tx(tx)?.state;
610
611 backend.journaled_state.database.commit(state);
613 update_state(
614 &mut backend.journaled_state.inner.state,
615 &mut backend.journaled_state.database,
616 )?;
617
618 Ok(())
619}
620
621fn update_state<DB: Database>(state: &mut EvmState, db: &mut DB) -> Result<(), DB::Error> {
624 for (addr, acc) in state.iter_mut() {
625 acc.info = db.basic(*addr)?.unwrap_or_default();
626 for (key, val) in acc.storage.iter_mut() {
627 val.present_value = db.storage(*addr, *key)?;
628 }
629 }
630
631 Ok(())
632}
633
634fn main() -> anyhow::Result<()> {
635 let backend = Backend::new(SpecId::default(), InMemoryDB::default());
636 let mut inspector = Cheatcodes::<
637 BlockEnv,
638 TxEnv,
639 CfgEnv,
640 EthInstructions<EthInterpreter, Context<BlockEnv, TxEnv, CfgEnv, InMemoryDB, Backend>>,
641 EthPrecompiles,
642 >::default();
643 let env = Env::mainnet();
644 let tx = env.tx.clone();
645
646 let context = Context {
647 tx: env.tx,
648 block: env.block,
649 cfg: env.cfg,
650 journaled_state: backend,
651 chain: (),
652 local: LocalContext::default(),
653 error: Ok(()),
654 };
655
656 let mut evm = Evm::new_with_inspector(
657 context,
658 &mut inspector,
659 EthInstructions::default(),
660 EthPrecompiles::default(),
661 );
662 evm.inspect_tx(tx)?;
663
664 assert_eq!(evm.inspector.call_count, 2);
666 assert_eq!(evm.journaled_state.method_with_inspector_counter, 1);
667 assert_eq!(evm.journaled_state.method_without_inspector_counter, 1);
668
669 Ok(())
670}