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::{
31 hardfork::SpecId, Address, AddressMap, AddressSet, HashSet, Log, StorageKey, StorageValue,
32 B256, U256,
33 },
34 state::{Account, Bytecode, EvmState},
35 Context, Database, DatabaseCommit, InspectEvm, Inspector, Journal, JournalEntry,
36};
37use std::{convert::Infallible, fmt::Debug};
38
39#[derive(Clone, Debug)]
43struct Backend {
44 journaled_state: Journal<InMemoryDB>,
46 method_with_inspector_counter: usize,
48 method_without_inspector_counter: usize,
49}
50
51impl Backend {
52 fn new(spec: SpecId, db: InMemoryDB) -> Self {
53 let mut journaled_state = Journal::new(db);
54 journaled_state.set_spec_id(spec);
55 Self {
56 journaled_state,
57 method_with_inspector_counter: 0,
58 method_without_inspector_counter: 0,
59 }
60 }
61}
62
63impl JournalTr for Backend {
64 type Database = InMemoryDB;
65 type State = EvmState;
66 type JournaledAccount<'a> = JournaledAccount<'a, InMemoryDB, JournalEntry>;
67
68 fn new(database: InMemoryDB) -> Self {
69 Self::new(SpecId::default(), database)
70 }
71
72 fn db(&self) -> &Self::Database {
73 self.journaled_state.db()
74 }
75
76 fn db_mut(&mut self) -> &mut Self::Database {
77 self.journaled_state.db_mut()
78 }
79
80 fn sload(
81 &mut self,
82 address: Address,
83 key: StorageKey,
84 ) -> Result<StateLoad<StorageValue>, <Self::Database as Database>::Error> {
85 self.journaled_state.sload(address, key)
86 }
87
88 fn sstore(
89 &mut self,
90 address: Address,
91 key: StorageKey,
92 value: StorageValue,
93 ) -> Result<StateLoad<SStoreResult>, <Self::Database as Database>::Error> {
94 self.journaled_state.sstore(address, key, value)
95 }
96
97 fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
98 self.journaled_state.tload(address, key)
99 }
100
101 fn tstore(&mut self, address: Address, key: StorageKey, value: StorageValue) {
102 self.journaled_state.tstore(address, key, value)
103 }
104
105 fn log(&mut self, log: Log) {
106 self.journaled_state.log(log)
107 }
108
109 fn logs(&self) -> &[Log] {
110 self.journaled_state.logs()
111 }
112
113 fn selfdestruct(
114 &mut self,
115 address: Address,
116 target: Address,
117 skip_cold_load: bool,
118 ) -> Result<StateLoad<SelfDestructResult>, JournalLoadError<Infallible>> {
119 self.journaled_state
120 .selfdestruct(address, target, skip_cold_load)
121 }
122
123 fn warm_access_list(&mut self, access_list: AddressMap<HashSet<StorageKey>>) {
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: AddressSet) {
132 self.journaled_state.warm_precompiles(addresses)
133 }
134
135 fn precompile_addresses(&self) -> &AddressSet {
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 #[allow(deprecated)]
239 fn caller_accounting_journal_entry(
240 &mut self,
241 address: Address,
242 old_balance: U256,
243 bump_nonce: bool,
244 ) {
245 #[allow(deprecated)]
246 self.journaled_state
247 .caller_accounting_journal_entry(address, old_balance, bump_nonce)
248 }
249
250 fn balance_incr(
251 &mut self,
252 address: Address,
253 balance: U256,
254 ) -> Result<(), <Self::Database as Database>::Error> {
255 self.journaled_state.balance_incr(address, balance)
256 }
257
258 #[allow(deprecated)]
259 fn nonce_bump_journal_entry(&mut self, address: Address) {
260 #[allow(deprecated)]
261 self.journaled_state.nonce_bump_journal_entry(address)
262 }
263
264 fn take_logs(&mut self) -> Vec<Log> {
265 self.journaled_state.take_logs()
266 }
267
268 fn commit_tx(&mut self) {
269 self.journaled_state.commit_tx()
270 }
271
272 fn discard_tx(&mut self) {
273 self.journaled_state.discard_tx()
274 }
275
276 fn sload_skip_cold_load(
277 &mut self,
278 address: Address,
279 key: StorageKey,
280 skip_cold_load: bool,
281 ) -> Result<StateLoad<StorageValue>, JournalLoadError<<Self::Database as Database>::Error>>
282 {
283 self.journaled_state
284 .sload_skip_cold_load(address, key, skip_cold_load)
285 }
286
287 fn sstore_skip_cold_load(
288 &mut self,
289 address: Address,
290 key: StorageKey,
291 value: StorageValue,
292 skip_cold_load: bool,
293 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<<Self::Database as Database>::Error>>
294 {
295 self.journaled_state
296 .sstore_skip_cold_load(address, key, value, skip_cold_load)
297 }
298
299 fn load_account_mut_skip_cold_load(
300 &mut self,
301 address: Address,
302 skip_cold_load: bool,
303 ) -> Result<StateLoad<Self::JournaledAccount<'_>>, Infallible> {
304 self.journaled_state
305 .load_account_mut_skip_cold_load(address, skip_cold_load)
306 }
307
308 fn load_account_info_skip_cold_load(
309 &mut self,
310 address: Address,
311 load_code: bool,
312 skip_cold_load: bool,
313 ) -> Result<AccountInfoLoad<'_>, JournalLoadError<Infallible>> {
314 self.journaled_state
315 .load_account_info_skip_cold_load(address, load_code, skip_cold_load)
316 }
317
318 fn load_account_mut_optional_code(
319 &mut self,
320 address: Address,
321 load_code: bool,
322 ) -> Result<StateLoad<Self::JournaledAccount<'_>>, Infallible> {
323 self.journaled_state
324 .load_account_mut_optional_code(address, load_code)
325 }
326}
327
328impl JournalExt for Backend {
329 fn journal(&self) -> &[JournalEntry] {
330 self.journaled_state.journal()
331 }
332
333 fn evm_state(&self) -> &EvmState {
334 self.journaled_state.evm_state()
335 }
336
337 fn evm_state_mut(&mut self) -> &mut EvmState {
338 self.journaled_state.evm_state_mut()
339 }
340}
341
342trait DatabaseExt: JournalTr {
345 fn method_that_takes_inspector_as_argument<
348 InspectorT,
349 BlockT,
350 TxT,
351 CfgT,
352 InstructionProviderT,
353 PrecompileT,
354 >(
355 &mut self,
356 env: Env<BlockT, TxT, CfgT>,
357 inspector: InspectorT,
358 ) -> anyhow::Result<()>
359 where
360 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
361 BlockT: Block,
362 TxT: Transaction + Clone,
363 CfgT: Cfg,
364 InstructionProviderT: InstructionProvider<
365 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
366 InterpreterTypes = EthInterpreter,
367 > + Default,
368 PrecompileT: PrecompileProvider<
369 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
370 Output = InterpreterResult,
371 > + Default;
372
373 fn method_that_constructs_inspector<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
375 &mut self,
376 env: Env<BlockT, TxT, CfgT>,
377 ) -> anyhow::Result<()>
378 where
379 BlockT: Block,
380 TxT: Transaction + Clone,
381 CfgT: Cfg,
382 InstructionProviderT: InstructionProvider<
383 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
384 InterpreterTypes = EthInterpreter,
385 > + Default,
386 PrecompileT: PrecompileProvider<
387 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
388 Output = InterpreterResult,
389 > + Default;
390}
391
392impl DatabaseExt for Backend {
393 fn method_that_takes_inspector_as_argument<
394 InspectorT,
395 BlockT,
396 TxT,
397 CfgT,
398 InstructionProviderT,
399 PrecompileT,
400 >(
401 &mut self,
402 env: Env<BlockT, TxT, CfgT>,
403 inspector: InspectorT,
404 ) -> anyhow::Result<()>
405 where
406 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
407 BlockT: Block,
408 TxT: Transaction + Clone,
409 CfgT: Cfg,
410 InstructionProviderT: InstructionProvider<
411 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
412 InterpreterTypes = EthInterpreter,
413 > + Default,
414 PrecompileT: PrecompileProvider<
415 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
416 Output = InterpreterResult,
417 > + Default,
418 {
419 commit_transaction::<InspectorT, BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
420 self, env, inspector,
421 )?;
422 self.method_with_inspector_counter += 1;
423 Ok(())
424 }
425
426 fn method_that_constructs_inspector<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
427 &mut self,
428 env: Env<BlockT, TxT, CfgT>,
429 ) -> anyhow::Result<()>
430 where
431 BlockT: Block,
432 TxT: Transaction + Clone,
433 CfgT: Cfg,
434 InstructionProviderT: InstructionProvider<
435 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
436 InterpreterTypes = EthInterpreter,
437 > + Default,
438 PrecompileT: PrecompileProvider<
439 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
440 Output = InterpreterResult,
441 > + Default,
442 {
443 let inspector = TracerEip3155::new(Box::new(std::io::sink()));
444 commit_transaction::<
445 TracerEip3155,
447 BlockT,
448 TxT,
449 CfgT,
450 InstructionProviderT,
451 PrecompileT,
452 >(self, env, inspector)?;
453
454 self.method_without_inspector_counter += 1;
455 Ok(())
456 }
457}
458
459#[derive(Clone, Default)]
462struct Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT> {
463 call_count: usize,
464 phantom: core::marker::PhantomData<(BlockT, TxT, CfgT, InstructionProviderT, PrecompileT)>,
465}
466
467impl<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
468 Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
469where
470 BlockT: Block + Clone,
471 TxT: Transaction + Clone,
472 CfgT: Cfg + Clone,
473 InstructionProviderT: InstructionProvider<
474 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
475 InterpreterTypes = EthInterpreter,
476 > + Default,
477 PrecompileT: PrecompileProvider<
478 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
479 Output = InterpreterResult,
480 > + Default,
481{
482 fn apply_cheatcode(
483 &mut self,
484 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
485 ) -> anyhow::Result<()> {
486 let block = context.block.clone();
488 let tx = context.tx.clone();
489 let cfg = context.cfg.clone();
490
491 context
493 .journal_mut()
494 .method_that_takes_inspector_as_argument::<_, _, _, _, InstructionProviderT, PrecompileT>(
495 Env {
496 block: block.clone(),
497 tx: tx.clone(),
498 cfg: cfg.clone(),
499 },
500 self,
501 )?;
502
503 context
505 .journal_mut()
506 .method_that_constructs_inspector::<_, _, _, InstructionProviderT, PrecompileT>(
507 Env { block, tx, cfg },
508 )?;
509 Ok(())
510 }
511}
512
513impl<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
514 Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>>
515 for Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
516where
517 BlockT: Block + Clone,
518 TxT: Transaction + Clone,
519 CfgT: Cfg + Clone,
520 InstructionProviderT: InstructionProvider<
521 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
522 InterpreterTypes = EthInterpreter,
523 > + Default,
524 PrecompileT: PrecompileProvider<
525 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
526 Output = InterpreterResult,
527 > + Default,
528{
529 fn call(
531 &mut self,
532 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
533 _inputs: &mut CallInputs,
534 ) -> Option<CallOutcome> {
535 self.call_count += 1;
536 if self.call_count == 1 {
538 self.apply_cheatcode(context).unwrap();
541 }
542 None
543 }
544}
545
546#[derive(Clone, Debug)]
548struct Env<BlockT, TxT, CfgT> {
549 block: BlockT,
550 tx: TxT,
551 cfg: CfgT,
552}
553
554impl Env<BlockEnv, TxEnv, CfgEnv> {
555 fn mainnet() -> Self {
556 let mut cfg = CfgEnv::default();
558 cfg.disable_nonce_check = true;
559
560 Self {
561 block: BlockEnv::default(),
562 tx: TxEnv::default(),
563 cfg,
564 }
565 }
566}
567
568fn commit_transaction<InspectorT, BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
571 backend: &mut Backend,
572 env: Env<BlockT, TxT, CfgT>,
573 inspector: InspectorT,
574) -> Result<(), EVMError<Infallible, InvalidTransaction>>
575where
576 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
577 BlockT: Block,
578 TxT: Transaction + Clone,
579 CfgT: Cfg,
580 InstructionProviderT: InstructionProvider<
581 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
582 InterpreterTypes = EthInterpreter,
583 > + Default,
584 PrecompileT: PrecompileProvider<
585 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
586 Output = InterpreterResult,
587 > + Default,
588{
589 let new_backend = backend.clone();
594 let tx = env.tx.clone();
595
596 let context = Context {
597 tx: env.tx,
598 block: env.block,
599 cfg: env.cfg,
600 journaled_state: new_backend,
601 chain: (),
602 local: LocalContext::default(),
603 error: Ok(()),
604 };
605
606 let mut evm = Evm::new_with_inspector(
607 context,
608 inspector,
609 InstructionProviderT::default(),
610 PrecompileT::default(),
611 );
612
613 let state = evm.inspect_tx(tx)?.state;
614
615 backend.journaled_state.database.commit(state);
617 update_state(
618 &mut backend.journaled_state.inner.state,
619 &mut backend.journaled_state.database,
620 )?;
621
622 Ok(())
623}
624
625fn update_state<DB: Database>(state: &mut EvmState, db: &mut DB) -> Result<(), DB::Error> {
628 for (addr, acc) in state.iter_mut() {
629 acc.info = db.basic(*addr)?.unwrap_or_default();
630 for (key, val) in acc.storage.iter_mut() {
631 val.present_value = db.storage(*addr, *key)?;
632 }
633 }
634
635 Ok(())
636}
637
638fn main() -> anyhow::Result<()> {
639 let backend = Backend::new(SpecId::default(), InMemoryDB::default());
640 let mut inspector = Cheatcodes::<
641 BlockEnv,
642 TxEnv,
643 CfgEnv,
644 EthInstructions<EthInterpreter, Context<BlockEnv, TxEnv, CfgEnv, InMemoryDB, Backend>>,
645 EthPrecompiles,
646 >::default();
647 let env = Env::mainnet();
648 let tx = env.tx.clone();
649
650 let context = Context {
651 tx: env.tx,
652 block: env.block,
653 cfg: env.cfg,
654 journaled_state: backend,
655 chain: (),
656 local: LocalContext::default(),
657 error: Ok(()),
658 };
659
660 let mut evm = Evm::new_with_inspector(
661 context,
662 &mut inspector,
663 EthInstructions::default(),
664 EthPrecompiles::default(),
665 );
666 evm.inspect_tx(tx)?;
667
668 assert_eq!(evm.inspector.call_count, 2);
670 assert_eq!(evm.journaled_state.method_with_inspector_counter, 1);
671 assert_eq!(evm.journaled_state.method_without_inspector_counter, 1);
672
673 Ok(())
674}