1use eyre::{eyre, Result};
7use revm::{
8 bytecode::Bytecode,
9 context::{BlockEnv, CfgEnv, Context, Journal, TxEnv},
10 context_interface::JournalTr,
11 database::CacheDB,
12 database_interface::EmptyDB,
13 interpreter::{
14 instructions::instruction_table_gas_changes_spec,
15 interpreter::{EthInterpreter, ExtBytecode},
16 interpreter_types::ReturnData,
17 FrameInput, InputsImpl, Interpreter, InterpreterAction, InterpreterResult, SharedMemory,
18 },
19 primitives::{hardfork::SpecId, keccak256, Bytes, TxKind, B256, U256},
20 state::AccountInfo,
21};
22use revm_statetest_types::{SpecName, TestSuite, TestUnit};
23use revmc::{
24 llvm::inkwell::context::Context as LlvmContext, Backend, EvmCompiler, EvmCompilerFn,
25 EvmContext, EvmLlvmBackend, EvmStack, OptimizationLevel,
26};
27use std::{
28 cmp::min,
29 collections::HashMap,
30 env,
31 ops::Range,
32 path::{Path, PathBuf},
33};
34use walkdir::WalkDir;
35
36const DEFAULT_ETHTESTS_PATH: &str = "tests/ethereum-tests";
38
39pub fn get_ethtests_path() -> PathBuf {
41 env::var("ETHTESTS").map(PathBuf::from).unwrap_or_else(|_| PathBuf::from(DEFAULT_ETHTESTS_PATH))
42}
43
44pub fn find_json_tests(path: &Path) -> Vec<PathBuf> {
46 WalkDir::new(path)
47 .into_iter()
48 .filter_map(|e| e.ok())
49 .filter(|e| e.path().extension().is_some_and(|ext| ext == "json"))
50 .map(|e| e.path().to_path_buf())
51 .collect()
52}
53
54pub fn load_test_suite(path: &Path) -> Result<TestSuite> {
56 let content = std::fs::read_to_string(path)?;
57 let suite: TestSuite = serde_json::from_str(&content)?;
58 Ok(suite)
59}
60
61pub fn spec_name_to_spec_id(spec_name: &SpecName) -> Option<SpecId> {
63 match *spec_name {
64 SpecName::Frontier => Some(SpecId::FRONTIER),
65 SpecName::Homestead => Some(SpecId::HOMESTEAD),
66 SpecName::EIP150 => Some(SpecId::TANGERINE),
67 SpecName::EIP158 => Some(SpecId::SPURIOUS_DRAGON),
68 SpecName::Byzantium => Some(SpecId::BYZANTIUM),
69 SpecName::Constantinople => None, SpecName::ConstantinopleFix => Some(SpecId::PETERSBURG),
71 SpecName::Istanbul => Some(SpecId::ISTANBUL),
72 SpecName::Berlin => Some(SpecId::BERLIN),
73 SpecName::London => Some(SpecId::LONDON),
74 SpecName::Paris | SpecName::Merge => Some(SpecId::MERGE),
75 SpecName::Shanghai => Some(SpecId::SHANGHAI),
76 SpecName::Cancun => Some(SpecId::CANCUN),
77 SpecName::Prague => Some(SpecId::PRAGUE),
78 SpecName::Osaka => Some(SpecId::OSAKA),
79 _ => None, }
81}
82
83#[derive(Debug)]
85pub struct TestResult {
86 pub name: String,
87 pub spec: String,
88 pub passed: bool,
89 pub error: Option<String>,
90}
91
92pub struct CompiledContracts {
94 functions: HashMap<B256, EvmCompilerFn>,
95}
96
97impl CompiledContracts {
98 pub fn new() -> Self {
99 Self { functions: HashMap::new() }
100 }
101
102 pub fn get(&self, code_hash: &B256) -> Option<EvmCompilerFn> {
103 self.functions.get(code_hash).copied()
104 }
105
106 pub fn insert(&mut self, code_hash: B256, func: EvmCompilerFn) {
107 self.functions.insert(code_hash, func);
108 }
109}
110
111impl Default for CompiledContracts {
112 fn default() -> Self {
113 Self::new()
114 }
115}
116
117pub fn build_pre_state(unit: &TestUnit) -> CacheDB<EmptyDB> {
119 let mut db = CacheDB::new(EmptyDB::new());
120
121 for (address, info) in &unit.pre {
122 let code_hash = keccak256(&info.code);
123 let bytecode =
124 if info.code.is_empty() { None } else { Some(Bytecode::new_legacy(info.code.clone())) };
125
126 let acc_info = AccountInfo {
127 balance: info.balance,
128 nonce: info.nonce,
129 code_hash,
130 code: bytecode,
131 ..Default::default()
132 };
133
134 db.insert_account_info(*address, acc_info);
135
136 for (key, value) in &info.storage {
137 let _ = db.insert_account_storage(*address, *key, *value);
138 }
139 }
140
141 db
142}
143
144pub fn build_tx_env(unit: &TestUnit, test: &revm_statetest_types::Test) -> Result<TxEnv> {
146 test.tx_env(unit).map_err(|e| eyre!("Failed to build tx env: {:?}", e))
147}
148
149pub fn compile_contracts<'ctx>(
158 unit: &TestUnit,
159 spec_id: SpecId,
160 compiler: &mut EvmCompiler<EvmLlvmBackend<'ctx>>,
161) -> Result<CompiledContracts> {
162 let mut compiled = CompiledContracts::new();
163 let mut func_ids: Vec<(B256, <EvmLlvmBackend<'ctx> as Backend>::FuncId)> = Vec::new();
164
165 for (address, info) in &unit.pre {
167 if info.code.is_empty() {
168 continue;
169 }
170
171 let code_hash = keccak256(&info.code);
172 if func_ids.iter().any(|(hash, _)| hash == &code_hash) {
174 continue;
175 }
176
177 let name = format!("contract_{:x}", address);
178 let func_id = compiler
179 .translate(&name, &info.code[..], spec_id)
180 .map_err(|e| eyre!("Failed to translate contract {:x}: {}", address, e))?;
181
182 func_ids.push((code_hash, func_id));
183 }
184
185 for (code_hash, func_id) in func_ids {
187 let func = unsafe { compiler.jit_function(func_id) }
188 .map_err(|e| eyre!("Failed to JIT function for {:x}: {}", code_hash, e))?;
189
190 compiled.insert(code_hash, func);
191 }
192
193 Ok(compiled)
194}
195
196pub fn run_test_unit(name: &str, unit: &TestUnit, spec_name: &SpecName) -> Result<Vec<TestResult>> {
198 let spec_str = format!("{:?}", spec_name);
199
200 let Some(spec_id) = spec_name_to_spec_id(spec_name) else {
201 return Ok(vec![TestResult {
202 name: name.to_string(),
203 spec: spec_str,
204 passed: true,
205 error: Some("Skipped: unsupported spec".to_string()),
206 }]);
207 };
208
209 let tests = match unit.post.get(spec_name) {
210 Some(tests) => tests,
211 None => return Ok(vec![]),
212 };
213
214 let mut results = Vec::new();
215
216 for (idx, test) in tests.iter().enumerate() {
217 let result = run_single_test(name, unit, test, spec_id, idx);
218 results.push(TestResult {
219 name: format!("{}[{}]", name, idx),
220 spec: spec_str.clone(),
221 passed: result.is_ok(),
222 error: result.err().map(|e| e.to_string()),
223 });
224 }
225
226 Ok(results)
227}
228
229fn run_single_test(
231 name: &str,
232 unit: &TestUnit,
233 test: &revm_statetest_types::Test,
234 spec_id: SpecId,
235 idx: usize,
236) -> Result<()> {
237 let has_code = unit.pre.values().any(|acc| !acc.code.is_empty());
238 if !has_code {
239 return Ok(());
240 }
241
242 if test.indexes.data >= unit.transaction.data.len() {
243 return Err(eyre!(
244 "Test {}[{}]: invalid data index {} >= {}",
245 name,
246 idx,
247 test.indexes.data,
248 unit.transaction.data.len()
249 ));
250 }
251 if test.indexes.gas >= unit.transaction.gas_limit.len() {
252 return Err(eyre!(
253 "Test {}[{}]: invalid gas index {} >= {}",
254 name,
255 idx,
256 test.indexes.gas,
257 unit.transaction.gas_limit.len()
258 ));
259 }
260 if test.indexes.value >= unit.transaction.value.len() {
261 return Err(eyre!(
262 "Test {}[{}]: invalid value index {} >= {}",
263 name,
264 idx,
265 test.indexes.value,
266 unit.transaction.value.len()
267 ));
268 }
269
270 let tx_env = build_tx_env(unit, test)?;
271 let db = build_pre_state(unit);
272
273 let interpreter_result = run_with_interpreter(unit, &tx_env, db.clone(), spec_id)?;
274
275 let context = LlvmContext::create();
276 let backend = EvmLlvmBackend::new(&context, false, OptimizationLevel::Default)?;
277 let mut compiler = EvmCompiler::new(backend);
278 let compiled = compile_contracts(unit, spec_id, &mut compiler)?;
279 let jit_result = run_with_jit(unit, &tx_env, db, spec_id, &compiled)?;
280
281 if interpreter_result.success != jit_result.success {
282 return Err(eyre!(
283 "Test {}[{}]: success mismatch: interpreter={}, jit={}",
284 name,
285 idx,
286 interpreter_result.success,
287 jit_result.success
288 ));
289 }
290
291 if interpreter_result.gas_used != jit_result.gas_used {
292 return Err(eyre!(
293 "Test {}[{}]: gas mismatch: interpreter={}, jit={}",
294 name,
295 idx,
296 interpreter_result.gas_used,
297 jit_result.gas_used
298 ));
299 }
300
301 if interpreter_result.output != jit_result.output {
302 return Err(eyre!("Test {}[{}]: output mismatch", name, idx));
303 }
304
305 Ok(())
306}
307
308#[derive(Debug)]
310pub struct ExecutionResult {
311 pub success: bool,
312 pub gas_used: u64,
313 pub output: Vec<u8>,
314}
315
316fn run_with_interpreter(
321 unit: &TestUnit,
322 tx_env: &TxEnv,
323 db: CacheDB<EmptyDB>,
324 spec_id: SpecId,
325) -> Result<ExecutionResult> {
326 let target = match tx_env.kind {
327 TxKind::Call(addr) => addr,
328 TxKind::Create => {
329 return Ok(ExecutionResult { success: true, gas_used: 0, output: Vec::new() });
330 }
331 };
332
333 let account = match unit.pre.get(&target) {
334 Some(acc) if !acc.code.is_empty() => acc,
335 _ => {
336 return Ok(ExecutionResult { success: true, gas_used: 0, output: Vec::new() });
337 }
338 };
339
340 let gas_limit = tx_env.gas_limit;
341 let bytecode = Bytecode::new_legacy(account.code.clone());
342 let ext_bytecode = ExtBytecode::new(bytecode);
343
344 let input = InputsImpl {
345 target_address: target,
346 bytecode_address: None,
347 caller_address: tx_env.caller,
348 input: revm::interpreter::CallInput::Bytes(tx_env.data.clone()),
349 call_value: tx_env.value,
350 };
351
352 let mut interpreter =
353 Interpreter::new(SharedMemory::new(), ext_bytecode, input, false, spec_id, gas_limit);
354
355 let mut cfg = CfgEnv::default();
356 cfg.spec = spec_id;
357 let block = unit.block_env(&mut cfg);
358 let mut ctx = Context::<BlockEnv, TxEnv, CfgEnv, _, Journal<_>, ()>::new(db, spec_id)
359 .with_block(block)
360 .with_cfg(cfg);
361
362 let _ = ctx.journaled_state.load_account(target);
364 let _ = ctx.journaled_state.load_account(tx_env.caller);
365
366 let table = instruction_table_gas_changes_spec::<EthInterpreter, _>(spec_id);
367 let mut action = interpreter.run_plain(&table, &mut ctx);
368
369 loop {
370 match action {
371 InterpreterAction::Return(result) => {
372 return Ok(ExecutionResult {
373 success: result.result.is_ok(),
374 gas_used: interpreter.gas.spent(),
375 output: result.output.to_vec(),
376 });
377 }
378 InterpreterAction::NewFrame(frame_input) => {
379 let (call_result, return_memory_offset) = match &frame_input {
380 FrameInput::Call(call_inputs) => {
381 let offset = call_inputs.return_memory_offset.clone();
382 let child_memory = interpreter.memory.new_child_context();
384 let result =
385 execute_frame_interpreter(&mut ctx, frame_input, spec_id, child_memory);
386 interpreter.memory.free_child_context();
388 (result, Some(offset))
389 }
390 FrameInput::Create(_) => {
391 let child_memory = interpreter.memory.new_child_context();
392 let result =
393 execute_frame_interpreter(&mut ctx, frame_input, spec_id, child_memory);
394 interpreter.memory.free_child_context();
395 (result, None)
396 }
397 FrameInput::Empty => {
398 return Ok(ExecutionResult {
399 success: false,
400 gas_used: interpreter.gas.spent(),
401 output: Vec::new(),
402 });
403 }
404 };
405
406 insert_call_outcome(&mut interpreter, call_result, return_memory_offset);
407 action = interpreter.run_plain(&table, &mut ctx);
408 }
409 }
410 }
411}
412
413fn insert_call_outcome(
421 interpreter: &mut Interpreter<EthInterpreter>,
422 outcome: InterpreterResult,
423 return_memory_offset: Option<Range<usize>>,
424) {
425 let ins_result = outcome.result;
426 let out_gas = outcome.gas;
427 let returned_len = outcome.output.len();
428
429 interpreter.return_data.set_buffer(outcome.output);
430
431 let success_indicator = if ins_result.is_ok() { U256::from(1) } else { U256::ZERO };
432 let _ = interpreter.stack.push(success_indicator);
433
434 if ins_result.is_ok_or_revert() {
435 interpreter.gas.erase_cost(out_gas.remaining());
436
437 if let Some(mem_range) = return_memory_offset {
438 let target_len = min(mem_range.len(), returned_len);
439 if target_len > 0 {
440 interpreter
441 .memory
442 .set(mem_range.start, &interpreter.return_data.buffer()[..target_len]);
443 }
444 }
445 }
446
447 if ins_result.is_ok() {
448 interpreter.gas.record_refund(out_gas.refunded());
449 }
450}
451
452fn execute_frame_interpreter<DB: revm::Database>(
454 ctx: &mut Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, ()>,
455 frame_input: FrameInput,
456 spec_id: SpecId,
457 memory: SharedMemory,
458) -> InterpreterResult
459where
460 DB::Error: std::fmt::Debug,
461{
462 match frame_input {
463 FrameInput::Call(call_inputs) => {
464 let target = call_inputs.bytecode_address;
465 let code_result = ctx.journaled_state.code(target);
466 let Ok(state_load) = code_result else {
467 return InterpreterResult {
468 result: revm::interpreter::InstructionResult::FatalExternalError,
469 output: Bytes::new(),
470 gas: revm::interpreter::Gas::new(0),
471 };
472 };
473 let code_bytes = state_load.data;
474
475 if code_bytes.is_empty() {
476 return InterpreterResult {
477 result: revm::interpreter::InstructionResult::Stop,
478 output: Bytes::new(),
479 gas: revm::interpreter::Gas::new(call_inputs.gas_limit),
480 };
481 }
482
483 let bytecode = Bytecode::new_legacy(code_bytes);
484 let ext_bytecode = ExtBytecode::new(bytecode);
485
486 let input = InputsImpl {
487 target_address: call_inputs.target_address,
488 bytecode_address: Some(call_inputs.bytecode_address),
489 caller_address: call_inputs.caller,
490 input: call_inputs.input.clone(),
491 call_value: match call_inputs.value {
492 revm::interpreter::CallValue::Transfer(v) => v,
493 revm::interpreter::CallValue::Apparent(v) => v,
494 },
495 };
496
497 let mut nested_interpreter = Interpreter::new(
498 memory,
499 ext_bytecode,
500 input,
501 call_inputs.is_static,
502 spec_id,
503 call_inputs.gas_limit,
504 );
505
506 let table = instruction_table_gas_changes_spec::<EthInterpreter, _>(spec_id);
507 let mut nested_action = nested_interpreter.run_plain(&table, ctx);
508
509 loop {
510 match nested_action {
511 InterpreterAction::Return(result) => {
512 return InterpreterResult {
513 result: result.result,
514 output: result.output,
515 gas: nested_interpreter.gas,
516 };
517 }
518 InterpreterAction::NewFrame(inner_frame) => {
519 let (inner_result, inner_return_offset) = match &inner_frame {
520 FrameInput::Call(inner_call) => {
521 let offset = inner_call.return_memory_offset.clone();
522 let child_memory = nested_interpreter.memory.new_child_context();
523 let result = execute_frame_interpreter(
524 ctx,
525 inner_frame,
526 spec_id,
527 child_memory,
528 );
529 nested_interpreter.memory.free_child_context();
530 (result, Some(offset))
531 }
532 FrameInput::Create(_) => {
533 let child_memory = nested_interpreter.memory.new_child_context();
534 let result = execute_frame_interpreter(
535 ctx,
536 inner_frame,
537 spec_id,
538 child_memory,
539 );
540 nested_interpreter.memory.free_child_context();
541 (result, None)
542 }
543 FrameInput::Empty => {
544 return InterpreterResult {
545 result: revm::interpreter::InstructionResult::Stop,
546 output: Bytes::new(),
547 gas: nested_interpreter.gas,
548 };
549 }
550 };
551 insert_call_outcome(
552 &mut nested_interpreter,
553 inner_result,
554 inner_return_offset,
555 );
556 nested_action = nested_interpreter.run_plain(&table, ctx);
557 }
558 }
559 }
560 }
561 FrameInput::Create(create_inputs) => InterpreterResult {
562 result: revm::interpreter::InstructionResult::CreateInitCodeStartingEF00,
563 output: Bytes::new(),
564 gas: revm::interpreter::Gas::new(create_inputs.gas_limit()),
565 },
566 FrameInput::Empty => InterpreterResult {
567 result: revm::interpreter::InstructionResult::Stop,
568 output: Bytes::new(),
569 gas: revm::interpreter::Gas::new(0),
570 },
571 }
572}
573
574fn run_with_jit(
579 unit: &TestUnit,
580 tx_env: &TxEnv,
581 db: CacheDB<EmptyDB>,
582 spec_id: SpecId,
583 compiled: &CompiledContracts,
584) -> Result<ExecutionResult> {
585 let target = match tx_env.kind {
586 TxKind::Call(addr) => addr,
587 TxKind::Create => {
588 return Ok(ExecutionResult { success: true, gas_used: 0, output: Vec::new() });
589 }
590 };
591
592 let account = match unit.pre.get(&target) {
593 Some(acc) if !acc.code.is_empty() => acc,
594 _ => {
595 return Ok(ExecutionResult { success: true, gas_used: 0, output: Vec::new() });
596 }
597 };
598
599 let code_hash = keccak256(&account.code);
600 let Some(jit_fn) = compiled.get(&code_hash) else {
601 return Err(eyre!("No compiled function for target {:x}", target));
602 };
603
604 let gas_limit = tx_env.gas_limit;
605 let bytecode = Bytecode::new_legacy(account.code.clone());
606 let ext_bytecode = ExtBytecode::new(bytecode);
607
608 let input = InputsImpl {
609 target_address: target,
610 bytecode_address: None,
611 caller_address: tx_env.caller,
612 input: revm::interpreter::CallInput::Bytes(tx_env.data.clone()),
613 call_value: tx_env.value,
614 };
615
616 let mut interpreter: Interpreter<EthInterpreter> =
617 Interpreter::new(SharedMemory::new(), ext_bytecode, input, false, spec_id, gas_limit);
618
619 let mut cfg = CfgEnv::default();
620 cfg.spec = spec_id;
621 let block = unit.block_env(&mut cfg);
622 let mut ctx = Context::<BlockEnv, TxEnv, CfgEnv, _, Journal<_>, ()>::new(db, spec_id)
623 .with_block(block)
624 .with_cfg(cfg);
625
626 let _ = ctx.journaled_state.load_account(target);
628 let _ = ctx.journaled_state.load_account(tx_env.caller);
629
630 let mut resume_at: usize = 0;
632
633 let (result, new_resume_at) =
635 call_jit_with_resume(&mut interpreter, &mut ctx, jit_fn, resume_at);
636 resume_at = new_resume_at;
637
638 let mut last_result = result;
639
640 let mut iteration = 0;
641 loop {
642 iteration += 1;
643 if iteration > 100 {
644 return Err(eyre::eyre!("Too many iterations"));
645 }
646
647 let action = interpreter.bytecode.action.take();
649
650 match action {
651 Some(InterpreterAction::NewFrame(frame_input)) => {
652 let (call_result, return_memory_offset) = match frame_input {
653 FrameInput::Call(ref call_inputs) => {
654 let offset = call_inputs.return_memory_offset.clone();
655 let result = execute_frame(&mut ctx, frame_input, spec_id, compiled);
656 (result, Some(offset))
657 }
658 FrameInput::Create(_) => {
659 let result = execute_frame(&mut ctx, frame_input, spec_id, compiled);
660 (result, None)
661 }
662 FrameInput::Empty => {
663 return Ok(ExecutionResult {
664 success: false,
665 gas_used: interpreter.gas.spent(),
666 output: Vec::new(),
667 });
668 }
669 };
670
671 insert_call_outcome(&mut interpreter, call_result, return_memory_offset);
672
673 let (result, new_resume_at) =
675 call_jit_with_resume(&mut interpreter, &mut ctx, jit_fn, resume_at);
676 resume_at = new_resume_at;
677 last_result = result;
678 }
679 Some(InterpreterAction::Return(ret_result)) => {
680 return Ok(ExecutionResult {
681 success: ret_result.result.is_ok(),
682 gas_used: interpreter.gas.spent(),
683 output: ret_result.output.to_vec(),
684 });
685 }
686 None => {
687 return Ok(ExecutionResult {
688 success: last_result.is_ok(),
689 gas_used: interpreter.gas.spent(),
690 output: Vec::new(),
691 });
692 }
693 }
694 }
695}
696
697fn call_jit_with_resume<DB: revm::Database + 'static>(
699 interpreter: &mut Interpreter<EthInterpreter>,
700 ctx: &mut Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, ()>,
701 jit_fn: EvmCompilerFn,
702 resume_at: usize,
703) -> (revm::interpreter::InstructionResult, usize)
704where
705 DB::Error: std::fmt::Debug,
706{
707 interpreter.bytecode.action = None;
708
709 let (stack, stack_len) = EvmStack::from_interpreter_stack(&mut interpreter.stack);
710 let mut ecx = EvmContext {
711 memory: &mut interpreter.memory,
712 input: &mut interpreter.input,
713 gas: &mut interpreter.gas,
714 host: ctx,
715 next_action: &mut interpreter.bytecode.action,
716 return_data: interpreter.return_data.buffer(),
717 is_static: interpreter.runtime_flag.is_static,
718 resume_at,
719 };
720
721 let result = unsafe { jit_fn.call(Some(stack), Some(stack_len), &mut ecx) };
722
723 if result == revm::interpreter::InstructionResult::OutOfGas {
724 ecx.gas.spend_all();
725 }
726
727 let new_resume_at = ecx.resume_at;
728 (result, new_resume_at)
729}
730
731fn call_jit_with_resume_nested<H: revmc::HostExt>(
734 interpreter: &mut Interpreter<EthInterpreter>,
735 host: &mut H,
736 jit_fn: EvmCompilerFn,
737 resume_at: usize,
738) -> (revm::interpreter::InstructionResult, usize) {
739 interpreter.bytecode.action = None;
740
741 let (stack, stack_len) = EvmStack::from_interpreter_stack(&mut interpreter.stack);
742 let mut ecx = EvmContext {
743 memory: &mut interpreter.memory,
744 input: &mut interpreter.input,
745 gas: &mut interpreter.gas,
746 host,
747 next_action: &mut interpreter.bytecode.action,
748 return_data: interpreter.return_data.buffer(),
749 is_static: interpreter.runtime_flag.is_static,
750 resume_at,
751 };
752
753 let result = unsafe { jit_fn.call(Some(stack), Some(stack_len), &mut ecx) };
754
755 if result == revm::interpreter::InstructionResult::OutOfGas {
756 ecx.gas.spend_all();
757 }
758
759 let new_resume_at = ecx.resume_at;
760 (result, new_resume_at)
761}
762
763fn execute_frame<DB: revm::Database + 'static>(
765 ctx: &mut Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, ()>,
766 frame_input: FrameInput,
767 spec_id: SpecId,
768 compiled: &CompiledContracts,
769) -> InterpreterResult
770where
771 DB::Error: std::fmt::Debug,
772{
773 match frame_input {
774 FrameInput::Call(call_inputs) => {
775 let target = call_inputs.bytecode_address;
776 let code_result = ctx.journaled_state.code(target);
777 let Ok(state_load) = code_result else {
778 return InterpreterResult {
779 result: revm::interpreter::InstructionResult::FatalExternalError,
780 output: Bytes::new(),
781 gas: revm::interpreter::Gas::new(0),
782 };
783 };
784 let code_bytes = state_load.data;
785
786 if code_bytes.is_empty() {
787 return InterpreterResult {
788 result: revm::interpreter::InstructionResult::Stop,
789 output: Bytes::new(),
790 gas: revm::interpreter::Gas::new(call_inputs.gas_limit),
791 };
792 }
793
794 let code_hash = keccak256(&code_bytes);
795
796 if let Some(jit_fn) = compiled.get(&code_hash) {
797 let bytecode = Bytecode::new_legacy(code_bytes);
798 let ext_bytecode = ExtBytecode::new(bytecode);
799 let input = InputsImpl {
800 target_address: call_inputs.target_address,
801 bytecode_address: Some(call_inputs.bytecode_address),
802 caller_address: call_inputs.caller,
803 input: call_inputs.input.clone(),
804 call_value: match call_inputs.value {
805 revm::interpreter::CallValue::Transfer(v) => v,
806 revm::interpreter::CallValue::Apparent(v) => v,
807 },
808 };
809
810 let mut nested_interpreter = Interpreter::new(
811 SharedMemory::new(),
812 ext_bytecode,
813 input,
814 call_inputs.is_static,
815 spec_id,
816 call_inputs.gas_limit,
817 );
818
819 let mut resume_at: usize = 0;
821
822 let (result, new_resume_at) =
825 call_jit_with_resume_nested(&mut nested_interpreter, ctx, jit_fn, resume_at);
826 resume_at = new_resume_at;
827 let mut last_result = result;
828
829 loop {
830 let action = nested_interpreter.bytecode.action.take();
831 match action {
832 Some(InterpreterAction::Return(result)) => {
833 return InterpreterResult {
834 result: result.result,
835 output: result.output,
836 gas: nested_interpreter.gas,
837 };
838 }
839 Some(InterpreterAction::NewFrame(inner_frame)) => {
840 let (inner_result, inner_return_offset) = match inner_frame {
841 FrameInput::Call(ref inner_call) => {
842 let offset = inner_call.return_memory_offset.clone();
843 let result = execute_frame(ctx, inner_frame, spec_id, compiled);
844 (result, Some(offset))
845 }
846 FrameInput::Create(_) => {
847 let result = execute_frame(ctx, inner_frame, spec_id, compiled);
848 (result, None)
849 }
850 FrameInput::Empty => {
851 return InterpreterResult {
852 result: revm::interpreter::InstructionResult::Stop,
853 output: Bytes::new(),
854 gas: nested_interpreter.gas,
855 };
856 }
857 };
858 insert_call_outcome(
859 &mut nested_interpreter,
860 inner_result,
861 inner_return_offset,
862 );
863 let (result, new_resume_at) = call_jit_with_resume_nested(
865 &mut nested_interpreter,
866 ctx,
867 jit_fn,
868 resume_at,
869 );
870 resume_at = new_resume_at;
871 last_result = result;
872 }
873 None => {
874 return InterpreterResult {
876 result: last_result,
877 output: Bytes::new(),
878 gas: nested_interpreter.gas,
879 };
880 }
881 }
882 }
883 } else {
884 InterpreterResult {
885 result: revm::interpreter::InstructionResult::Stop,
886 output: Bytes::new(),
887 gas: revm::interpreter::Gas::new(call_inputs.gas_limit),
888 }
889 }
890 }
891 FrameInput::Create(create_inputs) => InterpreterResult {
892 result: revm::interpreter::InstructionResult::CreateInitCodeStartingEF00,
893 output: Bytes::new(),
894 gas: revm::interpreter::Gas::new(create_inputs.gas_limit()),
895 },
896 FrameInput::Empty => InterpreterResult {
897 result: revm::interpreter::InstructionResult::Stop,
898 output: Bytes::new(),
899 gas: revm::interpreter::Gas::new(0),
900 },
901 }
902}
903
904pub fn run_general_state_tests(path: &Path) -> Result<Vec<TestResult>> {
906 let test_files = find_json_tests(path);
907 let mut all_results = Vec::new();
908
909 for file in test_files {
910 let suite = match load_test_suite(&file) {
911 Ok(s) => s,
912 Err(e) => {
913 eprintln!("Failed to load {}: {}", file.display(), e);
914 continue;
915 }
916 };
917
918 for (name, unit) in suite.0.iter() {
919 for spec_name in unit.post.keys() {
920 match run_test_unit(name, unit, spec_name) {
921 Ok(results) => all_results.extend(results),
922 Err(e) => {
923 all_results.push(TestResult {
924 name: name.clone(),
925 spec: format!("{:?}", spec_name),
926 passed: false,
927 error: Some(e.to_string()),
928 });
929 }
930 }
931 }
932 }
933 }
934
935 Ok(all_results)
936}
937
938#[cfg(test)]
939mod tests {
940 use super::*;
941 use revm::primitives::Address;
942
943 #[test]
944 fn test_spec_name_conversion() {
945 assert_eq!(spec_name_to_spec_id(&SpecName::Cancun), Some(SpecId::CANCUN));
946 assert_eq!(spec_name_to_spec_id(&SpecName::Constantinople), None);
947 }
948
949 #[test]
950 fn test_build_pre_state() {
951 use revm::primitives::U256;
952 use revm_statetest_types::AccountInfo;
953
954 let mut pre = revm::primitives::HashMap::default();
955 let code: Bytes = vec![0x60, 0x01, 0x00].into();
956 let mut storage = revm::primitives::HashMap::default();
957 storage.insert(U256::from(1), U256::from(42));
958
959 pre.insert(
960 Address::repeat_byte(1),
961 AccountInfo { balance: U256::from(1000), nonce: 5, code, storage },
962 );
963
964 let unit = TestUnit {
965 info: None,
966 env: serde_json::from_str(
967 r#"{
968 "currentCoinbase": "0x0000000000000000000000000000000000000000",
969 "currentDifficulty": "0x0",
970 "currentGasLimit": "0x989680",
971 "currentNumber": "0x0",
972 "currentTimestamp": "0x0",
973 "currentBaseFee": "0x7"
974 }"#,
975 )
976 .unwrap(),
977 pre,
978 post: Default::default(),
979 transaction: Default::default(),
980 out: None,
981 };
982
983 let db = build_pre_state(&unit);
984
985 let acc = db.cache.accounts.get(&Address::repeat_byte(1)).unwrap();
986 assert_eq!(acc.info.balance, U256::from(1000));
987 assert_eq!(acc.info.nonce, 5);
988 assert!(acc.info.code.is_some());
989
990 let storage_val = acc.storage.get(&U256::from(1)).unwrap();
991 assert_eq!(*storage_val, U256::from(42));
992 }
993
994 #[test]
995 fn test_simple_jit_execution() {
996 let bytecode: &[u8] = &[
999 0x60, 0x42, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3, ];
1006
1007 let spec_id = SpecId::CANCUN;
1008 let context = LlvmContext::create();
1009 let backend = EvmLlvmBackend::new(&context, false, OptimizationLevel::Default)
1010 .expect("Failed to create backend");
1011 let mut compiler = EvmCompiler::new(backend);
1012
1013 let jit_fn = unsafe { compiler.jit("test_simple", bytecode, spec_id) }
1014 .expect("Failed to JIT compile");
1015
1016 let bytecode_obj = Bytecode::new_legacy(bytecode.to_vec().into());
1018 let ext_bytecode = ExtBytecode::new(bytecode_obj);
1019 let input = InputsImpl {
1020 target_address: Address::ZERO,
1021 bytecode_address: None,
1022 caller_address: Address::ZERO,
1023 input: revm::interpreter::CallInput::Bytes(Default::default()),
1024 call_value: Default::default(),
1025 };
1026
1027 let mut interpreter =
1028 Interpreter::new(SharedMemory::new(), ext_bytecode, input, false, spec_id, 100_000);
1029
1030 let mut host = revm::context_interface::host::DummyHost::new(spec_id);
1031 let action = unsafe { jit_fn.call_with_interpreter(&mut interpreter, &mut host) };
1032
1033 match action {
1034 revm::interpreter::InterpreterAction::Return(result) => {
1035 assert!(result.result.is_ok(), "Expected success, got {:?}", result.result);
1036 assert_eq!(result.output.len(), 32);
1037 assert_eq!(result.output[31], 0x42);
1038 }
1039 other => panic!("Expected Return action, got {:?}", other),
1040 }
1041 }
1042
1043 #[test]
1044 fn test_compile_multiple_bytecodes() {
1045 let code1: &[u8] = &[0x60, 0x01, 0x60, 0x02, 0x01, 0x00]; let code2: &[u8] = &[0x60, 0x03, 0x60, 0x04, 0x02, 0x00]; let context = LlvmContext::create();
1050 let backend = EvmLlvmBackend::new(&context, false, OptimizationLevel::Default)
1051 .expect("Failed to create backend");
1052 let mut compiler = EvmCompiler::new(backend);
1053
1054 let id1 = compiler
1056 .translate("contract1", code1, SpecId::CANCUN)
1057 .expect("Failed to translate contract 1");
1058 let id2 = compiler
1059 .translate("contract2", code2, SpecId::CANCUN)
1060 .expect("Failed to translate contract 2");
1061
1062 let fn1 = unsafe { compiler.jit_function(id1) }.expect("Failed to JIT contract 1");
1064 let fn2 = unsafe { compiler.jit_function(id2) }.expect("Failed to JIT contract 2");
1065
1066 assert_ne!(fn1.into_inner() as usize, fn2.into_inner() as usize);
1068 }
1069
1070 #[test]
1071 #[ignore = "requires ethereum/tests checkout"]
1072 fn test_run_general_state_tests() {
1073 let mut path = get_ethtests_path().join("GeneralStateTests");
1074
1075 if let Ok(subdir) = env::var("SUBDIR") {
1077 path = path.join(subdir);
1078 }
1079
1080 if !path.exists() {
1081 eprintln!("Skipping: {} does not exist", path.display());
1082 return;
1083 }
1084
1085 let results = run_general_state_tests(&path).unwrap();
1086 let passed = results.iter().filter(|r| r.passed).count();
1087 let failed = results.iter().filter(|r| !r.passed).count();
1088 println!("Passed: {}, Failed: {}", passed, failed);
1089
1090 for result in results.iter().filter(|r| !r.passed) {
1091 println!("FAILED: {} ({}): {:?}", result.name, result.spec, result.error);
1092 }
1093 }
1094}