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