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