1use crate::{
4 compiled::{CompileCache, CompiledContracts},
5 merkle_trie::compute_test_roots,
6};
7use revm::{
8 context::{block::BlockEnv, cfg::CfgEnv, tx::TxEnv},
9 context_interface::result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction},
10 database::{self, bal::EvmDatabaseError},
11 database_interface::{DatabaseCommit, EmptyDB},
12 handler::Handler,
13 inspector::{inspectors::TracerEip3155, InspectCommitEvm},
14 primitives::{hardfork::SpecId, Bytes, B256},
15 Context, ExecuteCommitEvm, MainBuilder, MainContext,
16};
17use std::{convert::Infallible, io::stderr};
18
19type ExecResult =
20 Result<ExecutionResult<HaltReason>, EVMError<EvmDatabaseError<Infallible>, InvalidTransaction>>;
21
22#[derive(Debug)]
24pub struct ExecutionSnapshot {
25 pub status: ExecStatus,
26 pub output: Option<Bytes>,
27 pub gas_used: u64,
28 pub state_root: B256,
29 pub logs_root: B256,
30 pub post_state_dump: String,
31}
32
33#[derive(Debug)]
35pub enum ExecStatus {
36 Success(String),
37 Revert,
38 Halt(String),
39 Error(String),
40}
41
42impl std::fmt::Display for ExecStatus {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 match self {
45 Self::Success(r) => write!(f, "Success({r})"),
46 Self::Revert => write!(f, "Revert"),
47 Self::Halt(r) => write!(f, "Halt({r})"),
48 Self::Error(e) => write!(f, "Error({e})"),
49 }
50 }
51}
52
53fn snapshot_from_result(
54 exec_result: &ExecResult,
55 db: &database::State<EmptyDB>,
56) -> ExecutionSnapshot {
57 let validation = compute_test_roots(exec_result, db);
58
59 let (status, output, gas_used) = match exec_result {
60 Ok(result) => {
61 let status = match result {
62 ExecutionResult::Success { reason, .. } => {
63 ExecStatus::Success(format!("{reason:?}"))
64 }
65 ExecutionResult::Revert { .. } => ExecStatus::Revert,
66 ExecutionResult::Halt { reason, .. } => ExecStatus::Halt(format!("{reason:?}")),
67 };
68 (status, result.output().cloned(), result.gas_used())
69 }
70 Err(e) => (ExecStatus::Error(e.to_string()), None, 0),
71 };
72
73 ExecutionSnapshot {
74 status,
75 output,
76 gas_used,
77 state_root: validation.state_root,
78 logs_root: validation.logs_root,
79 post_state_dump: db.cache.pretty_print(),
80 }
81}
82
83pub fn run_interpreter(
85 cfg: &CfgEnv,
86 block: &BlockEnv,
87 tx: &TxEnv,
88 cache_state: &database::CacheState,
89) -> ExecutionSnapshot {
90 let prestate = cache_state.clone();
91 let mut state =
92 database::State::builder().with_cached_prestate(prestate).with_bundle_update().build();
93
94 let mut evm = Context::mainnet()
95 .with_block(block)
96 .with_tx(tx)
97 .with_cfg(cfg.clone())
98 .with_db(&mut state)
99 .build_mainnet();
100 let exec_result = evm.transact_commit(tx);
101 let db = evm.ctx.journaled_state.database;
102
103 snapshot_from_result(&exec_result, db)
104}
105
106pub fn run_jit(
108 compiled: &CompiledContracts,
109 cache: &CompileCache,
110 spec_id: SpecId,
111 cfg: &CfgEnv,
112 block: &BlockEnv,
113 tx: &TxEnv,
114 cache_state: &database::CacheState,
115) -> ExecutionSnapshot {
116 let prestate = cache_state.clone();
117 let mut state =
118 database::State::builder().with_cached_prestate(prestate).with_bundle_update().build();
119
120 let exec_result = unsafe {
124 let db_ref = &mut *(&mut state as *mut database::State<EmptyDB>);
125 let evm_context = Context::mainnet()
126 .with_block(block.clone())
127 .with_tx(tx.clone())
128 .with_cfg(cfg.clone())
129 .with_db(db_ref);
130 let mut handler = crate::compiled::CompiledHandler { compiled, cache, spec_id };
131 let mut evm = evm_context.build_mainnet();
132 let result = handler.run(&mut evm);
133 if result.is_ok() {
134 let s = evm.ctx.journaled_state.finalize();
135 DatabaseCommit::commit(&mut evm.ctx.journaled_state.database, s);
136 }
137 result
138 };
139 let db = &state;
140
141 snapshot_from_result(&exec_result, db)
142}
143
144pub fn trace_interpreter(
146 cfg: &CfgEnv,
147 block: &BlockEnv,
148 tx: &TxEnv,
149 cache_state: &database::CacheState,
150) {
151 let prestate = cache_state.clone();
152 let mut state =
153 database::State::builder().with_cached_prestate(prestate).with_bundle_update().build();
154
155 let mut evm = Context::mainnet()
156 .with_db(&mut state)
157 .with_block(block)
158 .with_tx(tx)
159 .with_cfg(cfg.clone())
160 .build_mainnet_with_inspector(TracerEip3155::buffered(stderr()).without_summary());
161 let exec_result = evm.inspect_tx_commit(tx);
162
163 eprintln!("\nExecution result: {exec_result:#?}");
164 eprintln!("\nState after:\n{}", evm.ctx.journaled_state.database.cache.pretty_print());
165}
166
167pub fn compare(interp: &ExecutionSnapshot, jit: &ExecutionSnapshot) -> Vec<Mismatch> {
169 let mut mismatches = Vec::new();
170
171 let interp_status = format!("{}", interp.status);
172 let jit_status = format!("{}", jit.status);
173 if interp_status != jit_status {
174 mismatches.push(Mismatch { field: "status", interpreter: interp_status, jit: jit_status });
175 }
176
177 if interp.output != jit.output {
178 mismatches.push(Mismatch {
179 field: "output",
180 interpreter: format!("{:?}", interp.output),
181 jit: format!("{:?}", jit.output),
182 });
183 }
184
185 if interp.gas_used != jit.gas_used {
186 mismatches.push(Mismatch {
187 field: "gas_used",
188 interpreter: interp.gas_used.to_string(),
189 jit: jit.gas_used.to_string(),
190 });
191 }
192
193 if interp.state_root != jit.state_root {
194 mismatches.push(Mismatch {
195 field: "state_root",
196 interpreter: format!("{}", interp.state_root),
197 jit: format!("{}", jit.state_root),
198 });
199 }
200
201 if interp.logs_root != jit.logs_root {
202 mismatches.push(Mismatch {
203 field: "logs_root",
204 interpreter: format!("{}", interp.logs_root),
205 jit: format!("{}", jit.logs_root),
206 });
207 }
208
209 mismatches
210}
211
212pub struct Mismatch {
214 pub field: &'static str,
215 pub interpreter: String,
216 pub jit: String,
217}
218
219impl std::fmt::Display for Mismatch {
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 write!(f, " {}: interpreter={}, jit={}", self.field, self.interpreter, self.jit)
222 }
223}