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_coinbase_account(&mut self, address: Address) {
121 self.journaled_state.warm_coinbase_account(address)
122 }
123
124 fn warm_precompiles(&mut self, addresses: HashSet<Address>) {
125 self.journaled_state.warm_precompiles(addresses)
126 }
127
128 fn precompile_addresses(&self) -> &HashSet<Address> {
129 self.journaled_state.precompile_addresses()
130 }
131
132 fn set_spec_id(&mut self, spec_id: SpecId) {
133 self.journaled_state.set_spec_id(spec_id);
134 }
135
136 fn touch_account(&mut self, address: Address) {
137 self.journaled_state.touch_account(address);
138 }
139
140 fn transfer(
141 &mut self,
142 from: Address,
143 to: Address,
144 balance: U256,
145 ) -> Result<Option<TransferError>, Infallible> {
146 self.journaled_state.transfer(from, to, balance)
147 }
148
149 fn load_account(&mut self, address: Address) -> Result<StateLoad<&mut Account>, Infallible> {
150 self.journaled_state.load_account(address)
151 }
152
153 fn load_account_code(
154 &mut self,
155 address: Address,
156 ) -> Result<StateLoad<&mut Account>, Infallible> {
157 self.journaled_state.load_account_code(address)
158 }
159
160 fn load_account_delegated(
161 &mut self,
162 address: Address,
163 ) -> Result<StateLoad<AccountLoad>, Infallible> {
164 self.journaled_state.load_account_delegated(address)
165 }
166
167 fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
168 self.journaled_state.set_code_with_hash(address, code, hash);
169 }
170
171 fn code(
172 &mut self,
173 address: Address,
174 ) -> Result<StateLoad<revm::primitives::Bytes>, <Self::Database as Database>::Error> {
175 self.journaled_state.code(address)
176 }
177
178 fn code_hash(
179 &mut self,
180 address: Address,
181 ) -> Result<StateLoad<B256>, <Self::Database as Database>::Error> {
182 self.journaled_state.code_hash(address)
183 }
184
185 fn clear(&mut self) {
186 self.journaled_state.clear();
187 }
188
189 fn checkpoint(&mut self) -> JournalCheckpoint {
190 self.journaled_state.checkpoint()
191 }
192
193 fn checkpoint_commit(&mut self) {
194 self.journaled_state.checkpoint_commit()
195 }
196
197 fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
198 self.journaled_state.checkpoint_revert(checkpoint)
199 }
200
201 fn create_account_checkpoint(
202 &mut self,
203 caller: Address,
204 address: Address,
205 balance: U256,
206 spec_id: SpecId,
207 ) -> Result<JournalCheckpoint, TransferError> {
208 self.journaled_state
209 .create_account_checkpoint(caller, address, balance, spec_id)
210 }
211
212 #[inline]
214 fn depth(&self) -> usize {
215 self.journaled_state.depth()
216 }
217
218 fn finalize(&mut self) -> Self::State {
219 self.journaled_state.finalize()
220 }
221
222 fn caller_accounting_journal_entry(
223 &mut self,
224 address: Address,
225 old_balance: U256,
226 bump_nonce: bool,
227 ) {
228 self.journaled_state
229 .caller_accounting_journal_entry(address, old_balance, bump_nonce)
230 }
231
232 fn balance_incr(
233 &mut self,
234 address: Address,
235 balance: U256,
236 ) -> Result<(), <Self::Database as Database>::Error> {
237 self.journaled_state.balance_incr(address, balance)
238 }
239
240 fn nonce_bump_journal_entry(&mut self, address: Address) {
241 self.journaled_state.nonce_bump_journal_entry(address)
242 }
243
244 fn take_logs(&mut self) -> Vec<Log> {
245 self.journaled_state.take_logs()
246 }
247
248 fn commit_tx(&mut self) {
249 self.journaled_state.commit_tx()
250 }
251
252 fn discard_tx(&mut self) {
253 self.journaled_state.discard_tx()
254 }
255}
256
257impl JournalExt for Backend {
258 fn logs(&self) -> &[Log] {
259 self.journaled_state.logs()
260 }
261
262 fn journal(&self) -> &[JournalEntry] {
263 self.journaled_state.journal()
264 }
265
266 fn evm_state(&self) -> &EvmState {
267 self.journaled_state.evm_state()
268 }
269
270 fn evm_state_mut(&mut self) -> &mut EvmState {
271 self.journaled_state.evm_state_mut()
272 }
273}
274
275trait DatabaseExt: JournalTr {
278 fn method_that_takes_inspector_as_argument<
281 InspectorT,
282 BlockT,
283 TxT,
284 CfgT,
285 InstructionProviderT,
286 PrecompileT,
287 >(
288 &mut self,
289 env: Env<BlockT, TxT, CfgT>,
290 inspector: InspectorT,
291 ) -> anyhow::Result<()>
292 where
293 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
294 BlockT: Block,
295 TxT: Transaction + Clone,
296 CfgT: Cfg,
297 InstructionProviderT: InstructionProvider<
298 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
299 InterpreterTypes = EthInterpreter,
300 > + Default,
301 PrecompileT: PrecompileProvider<
302 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
303 Output = InterpreterResult,
304 > + Default;
305
306 fn method_that_constructs_inspector<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
308 &mut self,
309 env: Env<BlockT, TxT, CfgT>,
310 ) -> anyhow::Result<()>
311 where
312 BlockT: Block,
313 TxT: Transaction + Clone,
314 CfgT: Cfg,
315 InstructionProviderT: InstructionProvider<
316 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
317 InterpreterTypes = EthInterpreter,
318 > + Default,
319 PrecompileT: PrecompileProvider<
320 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
321 Output = InterpreterResult,
322 > + Default;
323}
324
325impl DatabaseExt for Backend {
326 fn method_that_takes_inspector_as_argument<
327 InspectorT,
328 BlockT,
329 TxT,
330 CfgT,
331 InstructionProviderT,
332 PrecompileT,
333 >(
334 &mut self,
335 env: Env<BlockT, TxT, CfgT>,
336 inspector: InspectorT,
337 ) -> anyhow::Result<()>
338 where
339 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
340 BlockT: Block,
341 TxT: Transaction + Clone,
342 CfgT: Cfg,
343 InstructionProviderT: InstructionProvider<
344 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
345 InterpreterTypes = EthInterpreter,
346 > + Default,
347 PrecompileT: PrecompileProvider<
348 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
349 Output = InterpreterResult,
350 > + Default,
351 {
352 commit_transaction::<InspectorT, BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
353 self, env, inspector,
354 )?;
355 self.method_with_inspector_counter += 1;
356 Ok(())
357 }
358
359 fn method_that_constructs_inspector<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
360 &mut self,
361 env: Env<BlockT, TxT, CfgT>,
362 ) -> anyhow::Result<()>
363 where
364 BlockT: Block,
365 TxT: Transaction + Clone,
366 CfgT: Cfg,
367 InstructionProviderT: InstructionProvider<
368 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
369 InterpreterTypes = EthInterpreter,
370 > + Default,
371 PrecompileT: PrecompileProvider<
372 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
373 Output = InterpreterResult,
374 > + Default,
375 {
376 let inspector = TracerEip3155::new(Box::new(std::io::sink()));
377 commit_transaction::<
378 TracerEip3155,
380 BlockT,
381 TxT,
382 CfgT,
383 InstructionProviderT,
384 PrecompileT,
385 >(self, env, inspector)?;
386
387 self.method_without_inspector_counter += 1;
388 Ok(())
389 }
390}
391
392#[derive(Clone, Default)]
395struct Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT> {
396 call_count: usize,
397 phantom: core::marker::PhantomData<(BlockT, TxT, CfgT, InstructionProviderT, PrecompileT)>,
398}
399
400impl<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
401 Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
402where
403 BlockT: Block + Clone,
404 TxT: Transaction + Clone,
405 CfgT: Cfg + Clone,
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 fn apply_cheatcode(
416 &mut self,
417 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
418 ) -> anyhow::Result<()> {
419 let block = context.block.clone();
421 let tx = context.tx.clone();
422 let cfg = context.cfg.clone();
423
424 context
426 .journal_mut()
427 .method_that_takes_inspector_as_argument::<_, _, _, _, InstructionProviderT, PrecompileT>(
428 Env {
429 block: block.clone(),
430 tx: tx.clone(),
431 cfg: cfg.clone(),
432 },
433 self,
434 )?;
435
436 context
438 .journal_mut()
439 .method_that_constructs_inspector::<_, _, _, InstructionProviderT, PrecompileT>(
440 Env { block, tx, cfg },
441 )?;
442 Ok(())
443 }
444}
445
446impl<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
447 Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>>
448 for Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
449where
450 BlockT: Block + Clone,
451 TxT: Transaction + Clone,
452 CfgT: Cfg + Clone,
453 InstructionProviderT: InstructionProvider<
454 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
455 InterpreterTypes = EthInterpreter,
456 > + Default,
457 PrecompileT: PrecompileProvider<
458 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
459 Output = InterpreterResult,
460 > + Default,
461{
462 fn call(
464 &mut self,
465 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
466 _inputs: &mut CallInputs,
467 ) -> Option<CallOutcome> {
468 self.call_count += 1;
469 if self.call_count == 1 {
471 self.apply_cheatcode(context).unwrap();
474 }
475 None
476 }
477}
478
479#[derive(Clone, Debug)]
481struct Env<BlockT, TxT, CfgT> {
482 block: BlockT,
483 tx: TxT,
484 cfg: CfgT,
485}
486
487impl Env<BlockEnv, TxEnv, CfgEnv> {
488 fn mainnet() -> Self {
489 let mut cfg = CfgEnv::default();
491 cfg.disable_nonce_check = true;
492
493 Self {
494 block: BlockEnv::default(),
495 tx: TxEnv::default(),
496 cfg,
497 }
498 }
499}
500
501fn commit_transaction<InspectorT, BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
504 backend: &mut Backend,
505 env: Env<BlockT, TxT, CfgT>,
506 inspector: InspectorT,
507) -> Result<(), EVMError<Infallible, InvalidTransaction>>
508where
509 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
510 BlockT: Block,
511 TxT: Transaction + Clone,
512 CfgT: Cfg,
513 InstructionProviderT: InstructionProvider<
514 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
515 InterpreterTypes = EthInterpreter,
516 > + Default,
517 PrecompileT: PrecompileProvider<
518 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
519 Output = InterpreterResult,
520 > + Default,
521{
522 let new_backend = backend.clone();
527 let tx = env.tx.clone();
528
529 let context = Context {
530 tx: env.tx,
531 block: env.block,
532 cfg: env.cfg,
533 journaled_state: new_backend,
534 chain: (),
535 local: LocalContext::default(),
536 error: Ok(()),
537 };
538
539 let mut evm = Evm::new_with_inspector(
540 context,
541 inspector,
542 InstructionProviderT::default(),
543 PrecompileT::default(),
544 );
545
546 let state = evm.inspect_tx(tx)?.state;
547
548 backend.journaled_state.database.commit(state);
550 update_state(
551 &mut backend.journaled_state.inner.state,
552 &mut backend.journaled_state.database,
553 )?;
554
555 Ok(())
556}
557
558fn update_state<DB: Database>(state: &mut EvmState, db: &mut DB) -> Result<(), DB::Error> {
561 for (addr, acc) in state.iter_mut() {
562 acc.info = db.basic(*addr)?.unwrap_or_default();
563 for (key, val) in acc.storage.iter_mut() {
564 val.present_value = db.storage(*addr, *key)?;
565 }
566 }
567
568 Ok(())
569}
570
571fn main() -> anyhow::Result<()> {
572 let backend = Backend::new(SpecId::default(), InMemoryDB::default());
573 let mut inspector = Cheatcodes::<
574 BlockEnv,
575 TxEnv,
576 CfgEnv,
577 EthInstructions<EthInterpreter, Context<BlockEnv, TxEnv, CfgEnv, InMemoryDB, Backend>>,
578 EthPrecompiles,
579 >::default();
580 let env = Env::mainnet();
581 let tx = env.tx.clone();
582
583 let context = Context {
584 tx: env.tx,
585 block: env.block,
586 cfg: env.cfg,
587 journaled_state: backend,
588 chain: (),
589 local: LocalContext::default(),
590 error: Ok(()),
591 };
592
593 let mut evm = Evm::new_with_inspector(
594 context,
595 &mut inspector,
596 EthInstructions::default(),
597 EthPrecompiles::default(),
598 );
599 evm.inspect_tx(tx)?;
600
601 assert_eq!(evm.inspector.call_count, 2);
603 assert_eq!(evm.journaled_state.method_with_inspector_counter, 1);
604 assert_eq!(evm.journaled_state.method_without_inspector_counter, 1);
605
606 Ok(())
607}