1use crate::runtime::{JitBackend, LookupDecision, LookupRequest, RuntimeCacheKey};
8use alloy_primitives::{Address, Bytes};
9use revm_context::{Host, JournalEntry};
10use revm_context_interface::{
11 Cfg, ContextSetters, ContextTr, Database,
12 journaled_state::JournalTr,
13 result::{EVMError, HaltReason, InvalidTransaction, ResultAndState},
14};
15use revm_database_interface::DatabaseCommit;
16use revm_handler::{
17 EthFrame, EvmTr, ExecuteCommitEvm, ExecuteEvm, FrameInitOrResult, FrameTr, Handler,
18 ItemOrResult, MainnetHandler, PrecompileProvider, SystemCallCommitEvm, SystemCallEvm,
19 SystemCallTx, evm::ContextDbError,
20};
21use revm_inspector::{
22 InspectCommitEvm, InspectEvm, InspectSystemCallEvm, Inspector, InspectorEvmTr, InspectorFrame,
23 InspectorHandler, JournalExt,
24};
25use revm_interpreter::{InstructionResult, InterpreterAction, InterpreterResult, InterpreterTypes};
26use revm_primitives::{B256Map, hardfork::SpecId};
27use revm_state::EvmState;
28use std::sync::Arc;
29
30#[derive(derive_more::Debug)]
38pub struct JitEvm<EVM> {
39 inner: EVM,
40 backend: JitBackend,
41 #[debug(skip)]
44 lookup_cache: B256Map<LookupDecision>,
45 lookup_cache_spec_id: SpecId,
47}
48
49impl<EVM> JitEvm<EVM> {
50 pub const fn inner(&self) -> &EVM {
52 &self.inner
53 }
54
55 pub fn inner_mut(&mut self) -> &mut EVM {
57 &mut self.inner
58 }
59
60 pub fn into_inner(self) -> EVM {
62 self.inner
63 }
64
65 pub const fn backend(&self) -> &JitBackend {
67 &self.backend
68 }
69
70 pub fn backend_mut(&mut self) -> &mut JitBackend {
72 &mut self.backend
73 }
74
75 pub fn set_backend(&mut self, backend: JitBackend) {
77 self.backend = backend;
78 self.lookup_cache.clear();
79 }
80}
81
82impl<EVM: EvmTr> JitEvm<EVM>
83where
84 EVM::Context: ContextTr,
85{
86 pub fn disabled(inner: EVM) -> Self {
90 Self::new(inner, JitBackend::disabled())
91 }
92
93 pub fn new(inner: EVM, backend: JitBackend) -> Self {
95 let spec_id: SpecId = inner.ctx_ref().cfg().spec().into();
96 Self {
97 inner,
98 backend,
99 lookup_cache: B256Map::with_capacity_and_hasher(16, Default::default()),
100 lookup_cache_spec_id: spec_id,
101 }
102 }
103
104 fn invalidate_cache(&mut self) {
105 let spec_id: SpecId = self.inner.ctx_ref().cfg().spec().into();
106 if spec_id != self.lookup_cache_spec_id {
107 self.lookup_cache.clear();
108 self.lookup_cache_spec_id = spec_id;
109 }
112 }
113}
114
115impl<EVM> core::ops::Deref for JitEvm<EVM> {
116 type Target = EVM;
117
118 #[inline]
119 fn deref(&self) -> &EVM {
120 &self.inner
121 }
122}
123
124impl<EVM> core::ops::DerefMut for JitEvm<EVM> {
125 #[inline]
126 fn deref_mut(&mut self) -> &mut EVM {
127 &mut self.inner
128 }
129}
130
131impl<EVM> EvmTr for JitEvm<EVM>
132where
133 EVM: EvmTr<
134 Frame = EthFrame,
135 Precompiles: PrecompileProvider<EVM::Context, Output = InterpreterResult>,
136 >,
137{
138 type Context = EVM::Context;
139 type Instructions = EVM::Instructions;
140 type Precompiles = EVM::Precompiles;
141 type Frame = EVM::Frame;
142
143 #[inline]
144 fn all(
145 &self,
146 ) -> (
147 &Self::Context,
148 &Self::Instructions,
149 &Self::Precompiles,
150 &revm_context_interface::FrameStack<Self::Frame>,
151 ) {
152 self.inner.all()
153 }
154
155 #[inline]
156 fn all_mut(
157 &mut self,
158 ) -> (
159 &mut Self::Context,
160 &mut Self::Instructions,
161 &mut Self::Precompiles,
162 &mut revm_context_interface::FrameStack<Self::Frame>,
163 ) {
164 self.inner.all_mut()
165 }
166
167 #[inline]
168 fn frame_init(
169 &mut self,
170 frame_input: <Self::Frame as FrameTr>::FrameInit,
171 ) -> Result<
172 ItemOrResult<&mut Self::Frame, <Self::Frame as FrameTr>::FrameResult>,
173 ContextDbError<Self::Context>,
174 > {
175 self.inner.frame_init(frame_input)
176 }
177
178 #[inline]
179 fn frame_run(
180 &mut self,
181 ) -> Result<FrameInitOrResult<Self::Frame>, ContextDbError<Self::Context>> {
182 let spec_id: SpecId = self.inner.ctx_ref().cfg().spec().into();
183 let frame = self.inner.frame_stack().get();
184 let code_hash = frame.interpreter.bytecode.get_or_calculate_hash();
185
186 let decision = self.lookup_cache.entry(code_hash).or_insert_with(|| {
187 let code = frame.interpreter.bytecode.original_bytes();
188 self.backend.lookup(LookupRequest { key: RuntimeCacheKey { code_hash, spec_id }, code })
189 });
190
191 Ok(match decision {
192 LookupDecision::Compiled(program) => {
193 let (ctx, _, _, frame_stack) = self.inner.all_mut();
194 let frame = frame_stack.get();
195 let action =
196 unsafe { program.func.call_with_interpreter(&mut frame.interpreter, ctx) };
197 frame.process_next_action::<_, ContextDbError<Self::Context>>(ctx, action).inspect(
198 |i| {
199 if i.is_result() {
200 frame.set_finished(true);
201 }
202 },
203 )?
204 }
205 LookupDecision::Interpret(_) => self.inner.frame_run()?,
206 })
207 }
208
209 #[inline]
210 fn frame_return_result(
211 &mut self,
212 result: <Self::Frame as FrameTr>::FrameResult,
213 ) -> Result<Option<<Self::Frame as FrameTr>::FrameResult>, ContextDbError<Self::Context>> {
214 self.inner.frame_return_result(result)
215 }
216}
217
218impl<EVM> ExecuteEvm for JitEvm<EVM>
219where
220 EVM: EvmTr<
221 Frame = EthFrame,
222 Context: ContextTr<Journal: JournalTr<State = EvmState>> + ContextSetters,
223 Precompiles: PrecompileProvider<EVM::Context, Output = InterpreterResult>,
224 >,
225{
226 type ExecutionResult = revm_context_interface::result::ExecutionResult<HaltReason>;
227 type State = EvmState;
228 type Error = EVMError<<<EVM::Context as ContextTr>::Db as Database>::Error, InvalidTransaction>;
229 type Tx = <EVM::Context as ContextTr>::Tx;
230 type Block = <EVM::Context as ContextTr>::Block;
231
232 #[inline]
233 fn transact_one(&mut self, tx: Self::Tx) -> Result<Self::ExecutionResult, Self::Error> {
234 self.ctx_mut().set_tx(tx);
235 self.invalidate_cache();
236 MainnetHandler::default().run(self)
237 }
238
239 #[inline]
240 fn finalize(&mut self) -> Self::State {
241 self.ctx_mut().journal_mut().finalize()
242 }
243
244 #[inline]
245 fn set_block(&mut self, block: Self::Block) {
246 self.ctx_mut().set_block(block);
247 }
248
249 #[inline]
250 fn replay(&mut self) -> Result<ResultAndState<HaltReason>, Self::Error> {
251 self.invalidate_cache();
252 MainnetHandler::default().run(self).map(|result| {
253 let state = self.ctx_mut().journal_mut().finalize();
254 ResultAndState::new(result, state)
255 })
256 }
257}
258
259impl<EVM> ExecuteCommitEvm for JitEvm<EVM>
260where
261 Self: ExecuteEvm<State = EvmState>,
262 EVM: EvmTr<Context: ContextTr<Db: DatabaseCommit>>,
263{
264 #[inline]
265 fn commit(&mut self, state: Self::State) {
266 self.ctx_mut().db_mut().commit(state);
267 }
268}
269
270impl<EVM> SystemCallEvm for JitEvm<EVM>
271where
272 EVM: EvmTr<
273 Frame = EthFrame,
274 Context: ContextTr<Journal: JournalTr<State = EvmState>, Tx: SystemCallTx>
275 + ContextSetters,
276 Precompiles: PrecompileProvider<EVM::Context, Output = InterpreterResult>,
277 >,
278{
279 #[inline]
280 fn system_call_one_with_caller(
281 &mut self,
282 caller: Address,
283 system_contract_address: Address,
284 data: Bytes,
285 ) -> Result<Self::ExecutionResult, Self::Error> {
286 self.ctx_mut().set_tx(
287 <<EVM::Context as ContextTr>::Tx as SystemCallTx>::new_system_tx_with_caller(
288 caller,
289 system_contract_address,
290 data,
291 ),
292 );
293 self.invalidate_cache();
294 MainnetHandler::default().run_system_call(self)
295 }
296}
297
298impl<EVM> SystemCallCommitEvm for JitEvm<EVM>
299where
300 Self: SystemCallEvm<State = EvmState> + ExecuteCommitEvm,
301 EVM: EvmTr<Context: ContextTr<Db: DatabaseCommit>>,
302{
303 #[inline]
304 fn system_call_with_caller_commit(
305 &mut self,
306 caller: Address,
307 system_contract_address: Address,
308 data: Bytes,
309 ) -> Result<Self::ExecutionResult, Self::Error> {
310 self.system_call_with_caller(caller, system_contract_address, data).map(|output| {
311 self.ctx_mut().db_mut().commit(output.state);
312 output.result
313 })
314 }
315}
316
317impl<EVM> InspectorEvmTr for JitEvm<EVM>
318where
319 EVM: EvmTr<
320 Frame = EthFrame,
321 Precompiles: PrecompileProvider<EVM::Context, Output = InterpreterResult>,
322 > + InspectorEvmTr,
323{
324 type Inspector = <EVM as InspectorEvmTr>::Inspector;
325
326 #[inline]
327 fn all_inspector(
328 &self,
329 ) -> (
330 &Self::Context,
331 &Self::Instructions,
332 &Self::Precompiles,
333 &revm_context_interface::FrameStack<Self::Frame>,
334 &Self::Inspector,
335 ) {
336 self.inner.all_inspector()
337 }
338
339 #[inline]
340 fn all_mut_inspector(
341 &mut self,
342 ) -> (
343 &mut Self::Context,
344 &mut Self::Instructions,
345 &mut Self::Precompiles,
346 &mut revm_context_interface::FrameStack<Self::Frame>,
347 &mut Self::Inspector,
348 ) {
349 self.inner.all_mut_inspector()
350 }
351
352 #[inline]
353 fn inspect_frame_run(
354 &mut self,
355 ) -> Result<FrameInitOrResult<Self::Frame>, ContextDbError<Self::Context>> {
356 let spec_id: SpecId = self.inner.ctx_ref().cfg().spec().into();
357 let frame = self.inner.frame_stack().get();
358 let code_hash = frame.interpreter.bytecode.get_or_calculate_hash();
359
360 let decision = self.lookup_cache.entry(code_hash).or_insert_with(|| {
361 let code = frame.interpreter.bytecode.original_bytes();
362 self.backend.lookup(LookupRequest { key: RuntimeCacheKey { code_hash, spec_id }, code })
363 });
364
365 let program = match decision {
366 LookupDecision::Compiled(program) => Arc::clone(program),
367 LookupDecision::Interpret(_) => return self.inner.inspect_frame_run(),
368 };
369
370 let (ctx, inspector) = self.ctx_inspector();
377 let ctx_ptr: *mut EVM::Context = ctx;
378 let inspector_ptr: *mut <EVM as InspectorEvmTr>::Inspector = inspector;
379 let mut on_log = move |log: &revm_primitives::Log| unsafe {
380 (*inspector_ptr).log(&mut *ctx_ptr, log.clone());
381 };
382
383 let (ctx, _, _, frame_stack) = self.inner.all_mut();
384 let frame = frame_stack.get();
385 let action = unsafe {
386 program.func.call_with_interpreter_with(&mut frame.interpreter, ctx, |ecx| {
387 ecx.on_log = Some(core::mem::transmute::<
391 &mut dyn FnMut(&revm_primitives::Log),
392 &mut (dyn FnMut(&revm_primitives::Log) + '_),
393 >(&mut on_log));
394 })
395 };
396
397 let (ctx, inspector) = self.ctx_inspector();
399 if let InterpreterAction::Return(result) = &action
400 && result.result == InstructionResult::SelfDestruct
401 {
402 inspect_selfdestruct(ctx, inspector);
403 }
404
405 let (ctx, _, _, frame_stack) = self.inner.all_mut();
406 let frame = frame_stack.get();
407 let mut result = frame.process_next_action::<_, ContextDbError<Self::Context>>(ctx, action);
408
409 if let Ok(ItemOrResult::Result(frame_result)) = &mut result {
410 let (ctx, inspector, frame) = self.ctx_inspector_frame();
411 if let Some(frame) = frame.eth_frame() {
412 revm_inspector::handler::frame_end(ctx, inspector, &frame.input, frame_result);
413 frame.set_finished(true);
414 }
415 }
416 result
417 }
418}
419
420impl<EVM> InspectEvm for JitEvm<EVM>
421where
422 EVM: EvmTr<
423 Frame = EthFrame,
424 Context: ContextTr<Journal: JournalTr<State = EvmState> + JournalExt> + ContextSetters,
425 Precompiles: PrecompileProvider<EVM::Context, Output = InterpreterResult>,
426 > + InspectorEvmTr,
427{
428 type Inspector = <EVM as InspectorEvmTr>::Inspector;
429
430 #[inline]
431 fn set_inspector(&mut self, inspector: Self::Inspector) {
432 *self.inner.inspector() = inspector;
433 }
434
435 #[inline]
436 fn inspect_one_tx(&mut self, tx: Self::Tx) -> Result<Self::ExecutionResult, Self::Error> {
437 self.inner.ctx_mut().set_tx(tx);
438 self.invalidate_cache();
439 MainnetHandler::default().inspect_run(self)
440 }
441}
442
443impl<EVM> InspectCommitEvm for JitEvm<EVM> where Self: InspectEvm + ExecuteCommitEvm {}
444
445impl<EVM> InspectSystemCallEvm for JitEvm<EVM>
446where
447 EVM: EvmTr<
448 Frame = EthFrame,
449 Context: ContextTr<
450 Journal: JournalTr<State = EvmState> + JournalExt,
451 Tx: SystemCallTx,
452 > + ContextSetters,
453 Precompiles: PrecompileProvider<EVM::Context, Output = InterpreterResult>,
454 > + InspectorEvmTr,
455{
456 #[inline]
457 fn inspect_one_system_call_with_caller(
458 &mut self,
459 caller: Address,
460 system_contract_address: Address,
461 data: Bytes,
462 ) -> Result<Self::ExecutionResult, Self::Error> {
463 self.inner.ctx_mut().set_tx(
464 <<EVM::Context as ContextTr>::Tx as SystemCallTx>::new_system_tx_with_caller(
465 caller,
466 system_contract_address,
467 data,
468 ),
469 );
470 self.invalidate_cache();
471 MainnetHandler::default().inspect_run_system_call(self)
472 }
473}
474
475#[inline(never)]
476#[cold]
477fn inspect_selfdestruct<CTX, IT>(context: &mut CTX, inspector: &mut impl Inspector<CTX, IT>)
478where
479 CTX: ContextTr<Journal: JournalExt> + Host,
480 IT: InterpreterTypes,
481{
482 if let Some(
483 JournalEntry::AccountDestroyed {
484 address: contract, target: to, had_balance: balance, ..
485 }
486 | JournalEntry::BalanceTransfer { from: contract, to, balance, .. },
487 ) = context.journal_mut().journal().last()
488 {
489 inspector.selfdestruct(*contract, *to, *balance);
490 }
491}
492
493#[cfg(test)]
494#[cfg(feature = "llvm")]
495mod tests {
496 use super::*;
497 use crate::runtime::{JitBackend, RuntimeConfig};
498 use alloy_primitives::{Address, Bytes, TxKind, U256};
499 use revm_bytecode::opcode as op;
500 use revm_context::TxEnv;
501 use revm_context_interface::result::{ExecutionResult, Output};
502 use revm_database::{CacheDB, EmptyDB};
503 use revm_handler::MainBuilder;
504
505 fn blocking_backend() -> JitBackend {
506 JitBackend::new(RuntimeConfig { blocking: true, ..Default::default() }).unwrap()
507 }
508
509 type TestInnerEvm = revm_handler::MainnetEvm<
510 revm_context::Context<
511 revm_context::BlockEnv,
512 TxEnv,
513 revm_context::CfgEnv,
514 CacheDB<EmptyDB>,
515 >,
516 >;
517
518 fn test_jit_evm_with_spec(backend: JitBackend, spec_id: SpecId) -> JitEvm<TestInnerEvm> {
519 let inner = revm_context::Context::new(CacheDB::default(), spec_id).build_mainnet();
520 JitEvm::new(inner, backend)
521 }
522
523 fn test_jit_evm(backend: JitBackend) -> JitEvm<TestInnerEvm> {
524 test_jit_evm_with_spec(backend, SpecId::CANCUN)
525 }
526
527 fn deploy_contract_with_nonce(
528 evm: &mut JitEvm<TestInnerEvm>,
529 bytecode: &[u8],
530 nonce: u64,
531 ) -> Address {
532 let len = bytecode.len();
533 assert!(len <= 255);
534 let offset = 10u8;
535 let mut deploy_code = vec![
536 op::PUSH1,
537 len as u8,
538 op::PUSH1,
539 offset,
540 op::PUSH0,
541 op::CODECOPY,
542 op::PUSH1,
543 len as u8,
544 op::PUSH0,
545 op::RETURN,
546 ];
547 deploy_code.extend_from_slice(bytecode);
548
549 let tx = TxEnv {
550 kind: TxKind::Create,
551 nonce,
552 data: Bytes::copy_from_slice(&deploy_code),
553 gas_limit: 1_000_000,
554 ..Default::default()
555 };
556
557 let result = evm.transact_commit(tx).unwrap();
558 match result {
559 ExecutionResult::Success { output, .. } => match output {
560 Output::Create(_, Some(addr)) => addr,
561 other => panic!("expected Create output, got: {other:?}"),
562 },
563 other => panic!("expected Success, got: {other:?}"),
564 }
565 }
566
567 fn deploy_contract(evm: &mut JitEvm<TestInnerEvm>, bytecode: &[u8]) -> Address {
568 deploy_contract_with_nonce(evm, bytecode, 0)
569 }
570
571 fn call_contract(backend: JitBackend, spec_id: SpecId, bytecode: &[u8]) -> ExecutionResult {
572 let mut evm = test_jit_evm_with_spec(backend, spec_id);
573 let contract_addr = deploy_contract(&mut evm, bytecode);
574 let tx = TxEnv {
575 kind: TxKind::Call(contract_addr),
576 nonce: 1,
577 gas_limit: 1_000_000,
578 ..Default::default()
579 };
580 evm.transact(tx).unwrap().result
581 }
582
583 #[test]
584 fn jit_evm_simple_return() {
585 let backend = blocking_backend();
586
587 let runtime_code: &[u8] = &[0x60, 0x42, 0x5f, 0x52, 0x60, 0x20, 0x5f, 0xf3];
589
590 let mut evm = test_jit_evm(backend.clone());
591 let contract_addr = deploy_contract(&mut evm, runtime_code);
592
593 let tx = TxEnv {
594 kind: TxKind::Call(contract_addr),
595 nonce: 1,
596 gas_limit: 1_000_000,
597 ..Default::default()
598 };
599
600 let result = evm.transact(tx).unwrap();
601 match result.result {
602 ExecutionResult::Success { output, .. } => {
603 let data = match &output {
604 Output::Call(bytes) => bytes,
605 other => panic!("expected Call output, got: {other:?}"),
606 };
607 assert_eq!(U256::from_be_slice(data), U256::from(0x42));
608 }
609 other => panic!("expected Success, got: {other:?}"),
610 }
611
612 let stats = backend.stats();
613 assert!(stats.compilations_succeeded > 0, "expected compilations, got: {stats:?}");
614 assert!(stats.resident_entries > 0, "expected resident entries, got: {stats:?}");
615 }
616
617 #[test]
618 fn jit_evm_add() {
619 let backend = blocking_backend();
620
621 let runtime_code: &[u8] =
623 &[0x60, 0x01, 0x60, 0x02, 0x01, 0x5f, 0x52, 0x60, 0x20, 0x5f, 0xf3];
624
625 let mut evm = test_jit_evm(backend.clone());
626 let contract_addr = deploy_contract(&mut evm, runtime_code);
627
628 let tx = TxEnv {
629 kind: TxKind::Call(contract_addr),
630 nonce: 1,
631 gas_limit: 1_000_000,
632 ..Default::default()
633 };
634
635 let result = evm.transact(tx).unwrap();
636 match result.result {
637 ExecutionResult::Success { output, .. } => {
638 let data = match &output {
639 Output::Call(bytes) => bytes,
640 other => panic!("expected Call output, got: {other:?}"),
641 };
642 assert_eq!(U256::from_be_slice(data), U256::from(3));
643 }
644 other => panic!("expected Success, got: {other:?}"),
645 }
646
647 let stats = backend.stats();
648 assert!(stats.compilations_succeeded > 0, "expected compilations, got: {stats:?}");
649 assert!(stats.resident_entries > 0, "expected resident entries, got: {stats:?}");
650 }
651
652 #[test]
653 fn jit_evm_fallback_empty_code() {
654 let backend = blocking_backend();
655 let mut evm = test_jit_evm(backend);
656
657 let tx = TxEnv {
658 kind: TxKind::Call(Address::with_last_byte(0xEE)),
659 gas_limit: 1_000_000,
660 ..Default::default()
661 };
662
663 let result = evm.transact(tx).unwrap();
664 assert!(
665 matches!(result.result, ExecutionResult::Success { .. }),
666 "expected Success for empty-code call, got: {:?}",
667 result.result,
668 );
669 }
670
671 #[test]
672 fn jit_evm_amsterdam_state_gas_failure_matches_interpreter_gas() {
673 let bytecode = &[op::PUSH1, 0x01, op::PUSH1, 0xea, op::SSTORE, op::ADD];
679
680 let interpreter = call_contract(JitBackend::disabled(), SpecId::AMSTERDAM, bytecode);
681 let jit = call_contract(blocking_backend(), SpecId::AMSTERDAM, bytecode);
682
683 let (
684 ExecutionResult::Halt { gas: jit_gas, .. },
685 ExecutionResult::Halt { gas: interpreter_gas, .. },
686 ) = (&jit, &interpreter)
687 else {
688 panic!("expected halt results: jit={jit:?}, interpreter={interpreter:?}");
689 };
690 assert_eq!(jit_gas, interpreter_gas);
691 }
692
693 #[test]
695 fn jit_evm_non_blocking() {
696 use crate::runtime::RuntimeTuning;
697
698 let config = RuntimeConfig {
699 enabled: true,
700 tuning: RuntimeTuning {
701 jit_hot_threshold: 1,
702 jit_worker_count: 1,
703 ..Default::default()
704 },
705 ..Default::default()
706 };
707 let backend = JitBackend::new(config).unwrap();
708
709 let runtime_code: &[u8] = &[0x60, 0x42, 0x5f, 0x52, 0x60, 0x20, 0x5f, 0xf3];
710
711 let mut evm = test_jit_evm(backend.clone());
712 let contract_addr = deploy_contract(&mut evm, runtime_code);
713
714 let tx = TxEnv {
716 kind: TxKind::Call(contract_addr),
717 nonce: 1,
718 gas_limit: 1_000_000,
719 ..Default::default()
720 };
721 let result = evm.transact(tx).unwrap();
722 assert!(matches!(result.result, ExecutionResult::Success { .. }));
723
724 let code_hash = alloy_primitives::keccak256(runtime_code);
726 let deadline = std::time::Instant::now() + std::time::Duration::from_secs(30);
727 loop {
728 if backend.get_compiled(code_hash, SpecId::CANCUN).is_some() {
729 break;
730 }
731 assert!(std::time::Instant::now() < deadline, "timed out waiting for JIT");
732 std::thread::sleep(std::time::Duration::from_millis(50));
733 }
734
735 let mut evm = test_jit_evm(backend);
737 let contract_addr = deploy_contract(&mut evm, runtime_code);
738
739 let tx = TxEnv {
740 kind: TxKind::Call(contract_addr),
741 nonce: 1,
742 gas_limit: 1_000_000,
743 ..Default::default()
744 };
745 let result = evm.transact(tx).unwrap();
746 match result.result {
747 ExecutionResult::Success { output, .. } => {
748 let data = match &output {
749 Output::Call(bytes) => bytes,
750 other => panic!("expected Call output, got: {other:?}"),
751 };
752 assert_eq!(U256::from_be_slice(data), U256::from(0x42));
753 }
754 other => panic!("expected Success, got: {other:?}"),
755 }
756 }
757
758 #[test]
760 fn jit_evm_nested_call() {
761 let backend = blocking_backend();
762 let mut evm = test_jit_evm(backend);
763
764 let inner_code: &[u8] = &[0x60, 0x42, 0x5f, 0x52, 0x60, 0x20, 0x5f, 0xf3];
766 let inner_addr = deploy_contract_with_nonce(&mut evm, inner_code, 0);
767
768 let mut outer_code = vec![
770 op::PUSH0, op::PUSH0, op::PUSH0, op::PUSH0, op::PUSH0, ];
776 outer_code.push(op::PUSH20);
778 outer_code.extend_from_slice(inner_addr.as_slice());
779 outer_code.extend_from_slice(&[
780 op::PUSH2,
781 0xFF,
782 0xFF, op::CALL,
784 op::RETURNDATASIZE,
785 op::PUSH0,
786 op::PUSH0,
787 op::RETURNDATACOPY,
788 op::RETURNDATASIZE,
789 op::PUSH0,
790 op::RETURN,
791 ]);
792 let outer_addr = deploy_contract_with_nonce(&mut evm, &outer_code, 1);
793
794 let tx = TxEnv {
795 kind: TxKind::Call(outer_addr),
796 nonce: 2,
797 gas_limit: 1_000_000,
798 ..Default::default()
799 };
800 let result = evm.transact(tx).unwrap();
801 match result.result {
802 ExecutionResult::Success { output, .. } => {
803 let data = match &output {
804 Output::Call(bytes) => bytes,
805 other => panic!("expected Call output, got: {other:?}"),
806 };
807 assert_eq!(U256::from_be_slice(data), U256::from(0x42));
808 }
809 other => panic!("expected Success, got: {other:?}"),
810 }
811 }
812
813 #[test]
816 fn jit_evm_create2_factory() {
817 let backend = blocking_backend();
818 let mut evm = test_jit_evm(backend.clone());
819
820 let runtime_code: &[u8] = &[0x60, 0xAB, 0x5f, 0x52, 0x60, 0x20, 0x5f, 0xf3];
822
823 let rt_len = runtime_code.len();
825 let init_offset = 10u8;
826 let mut init_code = vec![
827 op::PUSH1,
828 rt_len as u8,
829 op::PUSH1,
830 init_offset,
831 op::PUSH0,
832 op::CODECOPY,
833 op::PUSH1,
834 rt_len as u8,
835 op::PUSH0,
836 op::RETURN,
837 ];
838 init_code.extend_from_slice(runtime_code);
839 let init_len = init_code.len();
840
841 let mut factory_code = vec![
844 op::PUSH1,
845 0, op::PUSH1,
847 0, op::PUSH0,
849 op::CODECOPY, op::PUSH0,
851 op::CALLDATALOAD, op::PUSH1,
853 0, op::PUSH0, op::PUSH0, op::CREATE2,
857 op::PUSH0,
858 op::MSTORE,
859 op::PUSH1,
860 0x20,
861 op::PUSH0,
862 op::RETURN,
863 ];
864 let code_offset = factory_code.len() as u8;
865 factory_code[1] = init_len as u8;
866 factory_code[3] = code_offset;
867 factory_code[9] = init_len as u8;
868 factory_code.extend_from_slice(&init_code);
869
870 let factory_addr = deploy_contract(&mut evm, &factory_code);
871
872 let mut deployed_addrs = Vec::new();
874 for (i, salt) in (1u64..=3).enumerate() {
875 let mut calldata = [0u8; 32];
876 calldata[24..].copy_from_slice(&salt.to_be_bytes());
877
878 let tx = TxEnv {
879 kind: TxKind::Call(factory_addr),
880 nonce: 1 + i as u64,
881 data: Bytes::copy_from_slice(&calldata),
882 gas_limit: 1_000_000,
883 ..Default::default()
884 };
885 let result = evm.transact_commit(tx).unwrap();
886 match result {
887 ExecutionResult::Success { output, .. } => {
888 let data = match &output {
889 Output::Call(bytes) => bytes,
890 other => panic!("expected Call output, got: {other:?}"),
891 };
892 let addr = Address::from_slice(&data[12..32]);
893 assert_ne!(addr, Address::ZERO, "CREATE2 should return a non-zero address");
894 deployed_addrs.push(addr);
895 }
896 other => panic!("expected Success, got: {other:?}"),
897 }
898 }
899
900 deployed_addrs.sort();
902 deployed_addrs.dedup();
903 assert_eq!(
904 deployed_addrs.len(),
905 3,
906 "CREATE2 with different salts should yield different addresses"
907 );
908
909 for (i, &addr) in deployed_addrs.iter().enumerate() {
911 let tx = TxEnv {
912 kind: TxKind::Call(addr),
913 nonce: 4 + i as u64,
914 gas_limit: 1_000_000,
915 ..Default::default()
916 };
917 let result = evm.transact_commit(tx).unwrap();
918 match result {
919 ExecutionResult::Success { output, .. } => {
920 let data = match &output {
921 Output::Call(bytes) => bytes,
922 other => panic!("expected Call output, got: {other:?}"),
923 };
924 assert_eq!(U256::from_be_slice(data), U256::from(0xABu64));
925 }
926 other => panic!("expected Success, got: {other:?}"),
927 }
928 }
929
930 let stats = backend.stats();
931 assert!(stats.compilations_succeeded > 0, "expected compilations, got: {stats:?}");
932 }
933
934 #[test]
936 fn jit_evm_create_and_call() {
937 let backend = blocking_backend();
938 let mut evm = test_jit_evm(backend);
939
940 let runtime_code: &[u8] = &[0x60, 0x99, 0x5f, 0x52, 0x60, 0x20, 0x5f, 0xf3];
942
943 let contract_addr = deploy_contract(&mut evm, runtime_code);
945
946 let tx = TxEnv {
948 kind: TxKind::Call(contract_addr),
949 nonce: 1,
950 gas_limit: 1_000_000,
951 ..Default::default()
952 };
953 let result = evm.transact(tx).unwrap();
954 match result.result {
955 ExecutionResult::Success { output, .. } => {
956 let data = match &output {
957 Output::Call(bytes) => bytes,
958 other => panic!("expected Call output, got: {other:?}"),
959 };
960 assert_eq!(U256::from_be_slice(data), U256::from(0x99u64));
961 }
962 other => panic!("expected Success, got: {other:?}"),
963 }
964 }
965}