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 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<<Self::Database as Database>::Error>> {
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<
310 StateLoad<
311 revm::context::journaled_state::account::JournaledAccount<'_, Self::JournalEntry>,
312 >,
313 <Self::Database as Database>::Error,
314 > {
315 self.journaled_state
316 .load_account_mut_optional_code(address, load_code)
317 }
318}
319
320impl JournalExt for Backend {
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}