1use alloy_evm::{
2 eth::EthEvmContext, precompiles::PrecompilesMap, Database, Evm, EvmEnv, EvmFactory,
3};
4use alloy_primitives::{Address, Bytes, B256, U256};
5use arb_precompiles::register_arb_precompiles;
6use arb_stylus::{
7 config::StylusConfig, ink::Gas as StylusGas, meter::MeteredMachine, run::RunProgram,
8 StylusEvmApi,
9};
10use arbos::programs::types::EvmData;
11use core::fmt::Debug;
12use revm::{
13 context::{
14 result::{EVMError, ExecutionResult, InvalidTransaction},
15 ContextSetters, Evm as RevmEvm, FrameStack,
16 },
17 context_interface::{
18 host::LoadError,
19 result::{HaltReason, ResultAndState},
20 ContextTr, JournalTr,
21 },
22 handler::{
23 instructions::EthInstructions, EthFrame, EvmTr, FrameResult, Handler, ItemOrResult,
24 MainnetHandler, PrecompileProvider,
25 },
26 inspector::{InspectorHandler, NoOpInspector},
27 interpreter::{
28 interpreter::EthInterpreter,
29 interpreter_action::FrameInit,
30 interpreter_types::{InputsTr, ReturnData, RuntimeFlag, StackTr},
31 CallInput, CallInputs, CallOutcome, CallScheme, FrameInput, Gas as EvmGas, Host,
32 InstructionContext, InstructionResult, InterpreterResult, InterpreterTypes,
33 },
34 primitives::hardfork::SpecId,
35 ExecuteEvm, InspectEvm, Inspector, SystemCallEvm,
36};
37
38use crate::transaction::ArbTransaction;
39
40const BLOBBASEFEE_OPCODE: u8 = 0x4a;
42
43const SELFDESTRUCT_OPCODE: u8 = 0xff;
45
46const NUMBER_OPCODE: u8 = 0x43;
48
49const BLOCKHASH_OPCODE: u8 = 0x40;
51
52const BALANCE_OPCODE: u8 = 0x31;
54
55fn arb_number<WIRE: InterpreterTypes, H: Host + ?Sized>(ctx: InstructionContext<'_, H, WIRE>) {
57 let l1_block = arb_precompiles::get_l1_block_number_for_evm();
58 if !ctx.interpreter.stack.push(U256::from(l1_block)) {
59 ctx.interpreter.halt(InstructionResult::StackOverflow);
60 }
61}
62
63fn arb_blockhash<WIRE: InterpreterTypes, H: Host + ?Sized>(ctx: InstructionContext<'_, H, WIRE>) {
70 use revm::interpreter::InstructionResult;
71
72 let requested = match ctx.interpreter.stack.pop() {
73 Some(v) => v,
74 None => {
75 ctx.interpreter.halt(InstructionResult::StackUnderflow);
76 return;
77 }
78 };
79
80 let l1_block_number = U256::from(arb_precompiles::get_l1_block_number_for_evm());
81
82 let Some(diff) = l1_block_number.checked_sub(requested) else {
83 if !ctx.interpreter.stack.push(U256::ZERO) {
84 ctx.interpreter.halt(InstructionResult::StackOverflow);
85 }
86 return;
87 };
88
89 let diff_u64: u64 = diff.try_into().unwrap_or(u64::MAX);
90 if diff_u64 == 0 || diff_u64 > 256 {
91 if !ctx.interpreter.stack.push(U256::ZERO) {
92 ctx.interpreter.halt(InstructionResult::StackOverflow);
93 }
94 return;
95 }
96
97 let requested_u64: u64 = requested.try_into().unwrap_or(u64::MAX);
98 match ctx.host.block_hash(requested_u64) {
99 Some(hash) => {
100 if !ctx.interpreter.stack.push(U256::from_be_bytes(hash.0)) {
101 ctx.interpreter.halt(InstructionResult::StackOverflow);
102 }
103 }
104 None => {
105 ctx.interpreter.halt_fatal();
106 }
107 }
108}
109
110fn arb_balance<WIRE: InterpreterTypes, H: Host + ?Sized>(ctx: InstructionContext<'_, H, WIRE>) {
116 let addr_u256 = match ctx.interpreter.stack.pop() {
118 Some(v) => v,
119 None => {
120 ctx.interpreter
121 .halt(revm::interpreter::InstructionResult::StackUnderflow);
122 return;
123 }
124 };
125
126 let addr = alloy_primitives::Address::from_word(alloy_primitives::B256::from(
127 addr_u256.to_be_bytes::<32>(),
128 ));
129
130 let spec_id = ctx.interpreter.runtime_flag.spec_id();
132 if spec_id.is_enabled_in(revm::primitives::hardfork::SpecId::BERLIN) {
133 let Some(state_load) = ctx.host.balance(addr) else {
135 ctx.interpreter.halt_fatal();
136 return;
137 };
138 let gas_cost = if state_load.is_cold { 2600u64 } else { 100u64 };
140 if !ctx.interpreter.gas.record_cost(gas_cost) {
141 ctx.interpreter
142 .halt(revm::interpreter::InstructionResult::OutOfGas);
143 return;
144 }
145
146 let balance = if addr == arb_precompiles::get_current_tx_sender() {
148 state_load
149 .data
150 .saturating_sub(arb_precompiles::get_poster_balance_correction())
151 } else {
152 state_load.data
153 };
154
155 if !ctx.interpreter.stack.push(balance) {
156 ctx.interpreter
157 .halt(revm::interpreter::InstructionResult::StackOverflow);
158 }
159 } else {
160 let Some(state_load) = ctx.host.balance(addr) else {
162 ctx.interpreter.halt_fatal();
163 return;
164 };
165
166 let balance = if addr == arb_precompiles::get_current_tx_sender() {
167 state_load
168 .data
169 .saturating_sub(arb_precompiles::get_poster_balance_correction())
170 } else {
171 state_load.data
172 };
173
174 if !ctx.interpreter.stack.push(balance) {
175 ctx.interpreter
176 .halt(revm::interpreter::InstructionResult::StackOverflow);
177 }
178 }
179}
180
181const SELFBALANCE_OPCODE: u8 = 0x47;
183
184fn arb_selfbalance<WIRE: InterpreterTypes, H: Host + ?Sized>(ctx: InstructionContext<'_, H, WIRE>) {
187 let target = ctx.interpreter.input.target_address();
188
189 let Some(state_load) = ctx.host.balance(target) else {
190 ctx.interpreter.halt_fatal();
191 return;
192 };
193
194 let balance = if target == arb_precompiles::get_current_tx_sender() {
196 state_load
197 .data
198 .saturating_sub(arb_precompiles::get_poster_balance_correction())
199 } else {
200 state_load.data
201 };
202
203 if !ctx.interpreter.stack.push(balance) {
204 ctx.interpreter
205 .halt(revm::interpreter::InstructionResult::StackOverflow);
206 }
207}
208
209fn arb_blob_basefee<WIRE: InterpreterTypes, H: Host + ?Sized>(
211 ctx: InstructionContext<'_, H, WIRE>,
212) {
213 ctx.interpreter.halt(InstructionResult::OpcodeNotFound);
214}
215
216fn arb_selfdestruct<WIRE: InterpreterTypes, H: Host + ?Sized>(
219 ctx: InstructionContext<'_, H, WIRE>,
220) {
221 if ctx.interpreter.runtime_flag.is_static() {
222 ctx.interpreter
223 .halt(InstructionResult::StateChangeDuringStaticCall);
224 return;
225 }
226
227 let acting_addr = ctx.interpreter.input.target_address();
229 match ctx.host.load_account_code(acting_addr) {
230 Some(code_load) => {
231 if arb_stylus::is_stylus_program(&code_load.data) {
232 ctx.interpreter.halt(InstructionResult::Revert);
233 return;
234 }
235 }
236 None => {
237 ctx.interpreter.halt_fatal();
238 return;
239 }
240 }
241
242 let Some(raw) = ctx.interpreter.stack.pop() else {
246 ctx.interpreter.halt(InstructionResult::StackUnderflow);
247 return;
248 };
249 let target = Address::from_word(alloy_primitives::B256::from(raw.to_be_bytes()));
250
251 let spec = ctx.interpreter.runtime_flag.spec_id();
252 let cold_load_gas = ctx.host.gas_params().selfdestruct_cold_cost();
253 let skip_cold_load = ctx.interpreter.gas.remaining() < cold_load_gas;
254
255 let res = match ctx.host.selfdestruct(acting_addr, target, skip_cold_load) {
256 Ok(res) => res,
257 Err(LoadError::ColdLoadSkipped) => {
258 ctx.interpreter.halt_oog();
259 return;
260 }
261 Err(LoadError::DBError) => {
262 ctx.interpreter.halt_fatal();
263 return;
264 }
265 };
266
267 let should_charge_topup = if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
269 res.had_value && !res.target_exists
270 } else {
271 !res.target_exists
272 };
273
274 let gas_cost = ctx
275 .host
276 .gas_params()
277 .selfdestruct_cost(should_charge_topup, res.is_cold);
278 if !ctx.interpreter.gas.record_cost(gas_cost) {
279 ctx.interpreter.halt_oog();
280 return;
281 }
282
283 if !res.previously_destroyed {
284 ctx.interpreter
285 .gas
286 .record_refund(ctx.host.gas_params().selfdestruct_refund());
287 }
288
289 ctx.interpreter.halt(InstructionResult::SelfDestruct);
290}
291
292pub use arb_stylus::pages::{
298 add_stylus_pages, get_stylus_pages, get_stylus_program_count, pop_stylus_program,
299 push_stylus_program, reset_stylus_pages, set_stylus_pages_open,
300};
301
302use arb_precompiles::storage_slot::{
305 derive_subspace_key, map_slot, map_slot_b256, ARBOS_STATE_ADDRESS, PROGRAMS_DATA_KEY,
306 PROGRAMS_PARAMS_KEY, PROGRAMS_SUBSPACE, ROOT_STORAGE_KEY,
307};
308use arbos::programs::{memory::MemoryModel, params::StylusParams, Program};
309
310fn sload_arbos<DB: Database>(journal: &mut revm::Journal<DB>, slot: U256) -> Option<U256> {
312 let _ = journal
313 .inner
314 .load_account(&mut journal.database, ARBOS_STATE_ADDRESS)
315 .ok()?;
316 let result = journal
317 .inner
318 .sload(&mut journal.database, ARBOS_STATE_ADDRESS, slot, false)
319 .ok()?;
320 Some(result.data)
321}
322
323fn read_params_word<DB: Database>(journal: &mut revm::Journal<DB>) -> Option<[u8; 32]> {
325 let programs_key = derive_subspace_key(ROOT_STORAGE_KEY, PROGRAMS_SUBSPACE);
326 let params_key = derive_subspace_key(programs_key.as_slice(), PROGRAMS_PARAMS_KEY);
327 let slot = map_slot(params_key.as_slice(), 0);
328 sload_arbos(journal, slot).map(|v| v.to_be_bytes::<32>())
329}
330
331fn read_program_word<DB: Database>(
333 journal: &mut revm::Journal<DB>,
334 code_hash: B256,
335) -> Option<B256> {
336 let programs_key = derive_subspace_key(ROOT_STORAGE_KEY, PROGRAMS_SUBSPACE);
337 let data_key = derive_subspace_key(programs_key.as_slice(), PROGRAMS_DATA_KEY);
338 let slot = map_slot_b256(data_key.as_slice(), &code_hash);
339 sload_arbos(journal, slot).map(|v| B256::from(v.to_be_bytes::<32>()))
340}
341
342fn read_module_hash<DB: Database>(
345 journal: &mut revm::Journal<DB>,
346 code_hash: B256,
347) -> Option<B256> {
348 let programs_key = derive_subspace_key(ROOT_STORAGE_KEY, PROGRAMS_SUBSPACE);
349 let module_hashes_key = derive_subspace_key(programs_key.as_slice(), &[2]);
350 let slot = map_slot_b256(module_hashes_key.as_slice(), &code_hash);
351 sload_arbos(journal, slot).map(|v| B256::from(v.to_be_bytes::<32>()))
352}
353
354fn parse_stylus_params(word: &[u8; 32], arbos_version: u64) -> StylusParams {
356 StylusParams {
357 arbos_version,
358 version: u16::from_be_bytes([word[0], word[1]]),
359 ink_price: (word[2] as u32) << 16 | (word[3] as u32) << 8 | word[4] as u32,
360 max_stack_depth: u32::from_be_bytes([word[5], word[6], word[7], word[8]]),
361 free_pages: u16::from_be_bytes([word[9], word[10]]),
362 page_gas: u16::from_be_bytes([word[11], word[12]]),
363 page_ramp: arbos::programs::params::INITIAL_PAGE_RAMP,
364 page_limit: u16::from_be_bytes([word[13], word[14]]),
365 min_init_gas: word[15],
366 min_cached_init_gas: word[16],
367 init_cost_scalar: word[17],
368 cached_cost_scalar: word[18],
369 expiry_days: u16::from_be_bytes([word[19], word[20]]),
370 keepalive_days: u16::from_be_bytes([word[21], word[22]]),
371 block_cache_size: u16::from_be_bytes([word[23], word[24]]),
372 max_wasm_size: 0,
374 max_fragment_count: 0,
375 }
376}
377
378fn stylus_call_gas_cost(
380 params: &StylusParams,
381 program: &Program,
382 pages_open: u16,
383 pages_ever: u16,
384) -> u64 {
385 let model = MemoryModel::new(params.free_pages, params.page_gas);
386 let mut cost = model.gas_cost(program.footprint, pages_open, pages_ever);
387
388 let cached = program.cached;
389 if cached || program.version > 1 {
390 cost = cost.saturating_add(program.cached_gas(params));
391 }
392 if !cached {
393 cost = cost.saturating_add(program.init_gas(params));
394 }
395 cost
396}
397
398use arb_stylus::evm_api_impl::{SubCallResult, SubCreateResult};
401
402fn stylus_call_trampoline<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
408 ctx: *mut (),
409 call_type: u8,
410 contract: Address,
411 caller: Address,
412 storage_addr: Address,
413 input: &[u8],
414 gas: u64,
415 value: U256,
416) -> SubCallResult
417where
418 BlockEnv: revm::context::Block,
419 TxEnv: revm::context::Transaction,
420 CfgEnv: revm::context::Cfg,
421 DB: Database,
422{
423 let context = unsafe {
424 &mut *(ctx as *mut revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>)
425 };
426
427 let is_static = call_type == 2;
428 let is_delegate = call_type == 1;
429
430 let checkpoint = context.journaled_state.inner.checkpoint();
432
433 if !is_delegate && !value.is_zero() {
435 let transfer_result = context.journaled_state.inner.transfer(
436 &mut context.journaled_state.database,
437 caller,
438 contract,
439 value,
440 );
441 if transfer_result.is_err() {
442 context.journaled_state.inner.checkpoint_revert(checkpoint);
443 return SubCallResult {
444 output: Vec::new(),
445 gas_cost: 0,
446 success: false,
447 refund: 0,
448 };
449 }
450 }
451
452 let code_address = contract;
453 let target_address = storage_addr;
454 let _ = is_delegate;
455
456 let call_scheme = match call_type {
457 0 => CallScheme::Call,
458 1 => CallScheme::DelegateCall,
459 2 => CallScheme::StaticCall,
460 _ => CallScheme::Call,
461 };
462
463 let call_value = if is_delegate {
464 revm::interpreter::CallValue::Apparent(value)
465 } else {
466 revm::interpreter::CallValue::Transfer(value)
467 };
468
469 let sub_inputs = CallInputs {
470 input: CallInput::Bytes(input.to_vec().into()),
471 gas_limit: gas,
472 target_address,
473 bytecode_address: code_address,
474 caller,
475 value: call_value,
476 scheme: call_scheme,
477 is_static,
478 return_memory_offset: 0..0,
479 known_bytecode: None,
480 };
481
482 {
483 arb_precompiles::set_evm_depth(context.journaled_state.inner.depth);
484 let spec: revm::primitives::hardfork::SpecId = context.cfg.spec().into();
485 let mut precompiles =
486 alloy_evm::precompiles::PrecompilesMap::from(revm::handler::EthPrecompiles::new(spec));
487 register_arb_precompiles(&mut precompiles, arb_precompiles::get_arbos_version());
488 let mut arb_map = ArbPrecompilesMap(precompiles);
489 let dispatch_result = <ArbPrecompilesMap as PrecompileProvider<
490 revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
491 >>::run(&mut arb_map, context, &sub_inputs);
492
493 match dispatch_result {
494 Ok(Some(result)) => {
495 let success = result.result.is_ok();
496 let output = result.output.to_vec();
497 let gas_used = if success || matches!(result.result, InstructionResult::Revert) {
503 gas.saturating_sub(result.gas.remaining())
504 } else {
505 gas
506 };
507 let refund = if success { result.gas.refunded() } else { 0 };
508 if success {
509 context.journaled_state.inner.checkpoint_commit();
510 } else {
511 context.journaled_state.inner.checkpoint_revert(checkpoint);
512 }
513 return SubCallResult {
514 output,
515 gas_cost: gas_used,
516 success,
517 refund,
518 };
519 }
520 Ok(None) => {}
521 Err(_) => {
522 context.journaled_state.inner.checkpoint_revert(checkpoint);
523 return SubCallResult {
524 output: Vec::new(),
525 gas_cost: gas,
526 success: false,
527 refund: 0,
528 };
529 }
530 }
531 }
532
533 let bytecode = match context
534 .journaled_state
535 .inner
536 .load_code(&mut context.journaled_state.database, code_address)
537 {
538 Ok(acc) => acc
539 .data
540 .info
541 .code
542 .as_ref()
543 .map(|c| c.original_bytes())
544 .unwrap_or_default(),
545 Err(_) => {
546 context.journaled_state.inner.checkpoint_revert(checkpoint);
547 return SubCallResult {
548 output: Vec::new(),
549 gas_cost: 0,
550 success: false,
551 refund: 0,
552 };
553 }
554 };
555
556 if bytecode.is_empty() {
557 context.journaled_state.inner.checkpoint_commit();
558 return SubCallResult {
559 output: Vec::new(),
560 gas_cost: 0,
561 success: true,
562 refund: 0,
563 };
564 }
565
566 if arb_precompiles::get_arbos_version() >= arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS
569 && arb_stylus::is_stylus_program(&bytecode)
570 {
571 let result = execute_stylus_program(context, &sub_inputs, &bytecode);
572 let success = result.result.is_ok();
573 let output = result.output.to_vec();
574 let gas_used = gas.saturating_sub(result.gas.remaining());
575 let refund = result.gas.refunded();
576 if success {
577 context.journaled_state.inner.checkpoint_commit();
578 } else {
579 context.journaled_state.inner.checkpoint_revert(checkpoint);
580 }
581 return SubCallResult {
582 output,
583 gas_cost: gas_used,
584 success,
585 refund,
586 };
587 }
588
589 let result = run_evm_bytecode(context, &sub_inputs, &bytecode, gas);
590 let success = result.result.is_ok();
591 let output = result.output.to_vec();
592 let gas_used = gas.saturating_sub(result.gas.remaining());
593 let refund = result.gas.refunded();
594 if success {
595 context.journaled_state.inner.checkpoint_commit();
596 } else {
597 context.journaled_state.inner.checkpoint_revert(checkpoint);
598 }
599 SubCallResult {
600 output,
601 gas_cost: gas_used,
602 success,
603 refund,
604 }
605}
606
607fn stylus_create_trampoline<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
609 ctx: *mut (),
610 caller: Address,
611 code: &[u8],
612 gas: u64,
613 endowment: U256,
614 salt: Option<B256>,
615) -> SubCreateResult
616where
617 BlockEnv: revm::context::Block,
618 TxEnv: revm::context::Transaction,
619 CfgEnv: revm::context::Cfg,
620 DB: Database,
621{
622 let context = unsafe {
623 &mut *(ctx as *mut revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>)
624 };
625
626 let checkpoint = context.journaled_state.inner.checkpoint();
627
628 let caller_nonce = {
630 let acc = context
631 .journaled_state
632 .inner
633 .load_account(&mut context.journaled_state.database, caller);
634 acc.map(|a| a.data.info.nonce).unwrap_or(0)
635 };
636
637 let created_address = if let Some(salt) = salt {
638 let code_hash = alloy_primitives::keccak256(code);
640 let mut buf = Vec::with_capacity(1 + 20 + 32 + 32);
641 buf.push(0xff);
642 buf.extend_from_slice(caller.as_slice());
643 buf.extend_from_slice(salt.as_slice());
644 buf.extend_from_slice(code_hash.as_slice());
645 Address::from_slice(&alloy_primitives::keccak256(&buf)[12..])
646 } else {
647 use alloy_rlp::Encodable;
649 let mut rlp_buf = Vec::with_capacity(64);
650 alloy_rlp::Header {
651 list: true,
652 payload_length: caller.length() + caller_nonce.length(),
653 }
654 .encode(&mut rlp_buf);
655 caller.encode(&mut rlp_buf);
656 caller_nonce.encode(&mut rlp_buf);
657 Address::from_slice(&alloy_primitives::keccak256(&rlp_buf)[12..])
658 };
659
660 let _ = context
662 .journaled_state
663 .inner
664 .load_account(&mut context.journaled_state.database, caller);
665 if let Some(acc) = context.journaled_state.inner.state.get_mut(&caller) {
666 acc.info.nonce += 1;
667 context
668 .journaled_state
669 .inner
670 .nonce_bump_journal_entry(caller);
671 }
672
673 if !endowment.is_zero()
675 && context
676 .journaled_state
677 .inner
678 .transfer(
679 &mut context.journaled_state.database,
680 caller,
681 created_address,
682 endowment,
683 )
684 .is_err()
685 {
686 context.journaled_state.inner.checkpoint_revert(checkpoint);
687 return SubCreateResult {
688 address: None,
689 output: Vec::new(),
690 gas_cost: gas,
691 };
692 }
693
694 let init_inputs = CallInputs {
696 input: CallInput::Bytes(code.to_vec().into()),
697 gas_limit: gas,
698 target_address: created_address,
699 bytecode_address: created_address,
700 caller,
701 value: revm::interpreter::CallValue::Transfer(endowment),
702 scheme: CallScheme::Call,
703 is_static: false,
704 return_memory_offset: 0..0,
705 known_bytecode: None,
706 };
707
708 let result = run_evm_bytecode(context, &init_inputs, code, gas);
709 let success = result.result.is_ok();
710 let gas_used = gas.saturating_sub(result.gas.remaining());
711
712 if success {
713 let deployed_code = result.output.to_vec();
715 let code_hash = alloy_primitives::keccak256(&deployed_code);
716 let bytecode = revm::bytecode::Bytecode::new_raw(deployed_code.into());
717 let _ = context
719 .journaled_state
720 .inner
721 .load_account(&mut context.journaled_state.database, created_address);
722 context
723 .journaled_state
724 .inner
725 .set_code_with_hash(created_address, bytecode, code_hash);
726 context.journaled_state.inner.checkpoint_commit();
727 SubCreateResult {
728 address: Some(created_address),
729 output: Vec::new(), gas_cost: gas_used,
731 }
732 } else {
733 let output = result.output.to_vec();
734 context.journaled_state.inner.checkpoint_revert(checkpoint);
735 SubCreateResult {
736 address: None,
737 output, gas_cost: gas_used,
739 }
740 }
741}
742
743fn run_evm_bytecode<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
749 context: &mut revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
750 inputs: &CallInputs,
751 bytecode: &[u8],
752 gas_limit: u64,
753) -> InterpreterResult
754where
755 BlockEnv: revm::context::Block,
756 TxEnv: revm::context::Transaction,
757 CfgEnv: revm::context::Cfg,
758 DB: Database,
759{
760 use revm::{
761 bytecode::Bytecode,
762 interpreter::{
763 interpreter::{ExtBytecode, InputsImpl},
764 FrameInput, InterpreterAction, SharedMemory,
765 },
766 };
767
768 let code = Bytecode::new_raw(bytecode.to_vec().into());
769 let ext_bytecode = ExtBytecode::new(code);
770
771 let call_value = inputs.value.get();
772 let interp_input = InputsImpl {
773 target_address: inputs.target_address,
774 bytecode_address: Some(inputs.bytecode_address),
775 caller_address: inputs.caller,
776 input: inputs.input.clone(),
777 call_value,
778 };
779
780 let spec = context.cfg.spec();
781
782 let mut interpreter = revm::interpreter::Interpreter::new(
783 SharedMemory::new(),
784 ext_bytecode,
785 interp_input,
786 inputs.is_static,
787 spec.clone().into(),
788 gas_limit,
789 );
790
791 type Ctx<B, T, C, D, Ch> = revm::Context<B, T, C, D, revm::Journal<D>, Ch>;
796 let mut instructions = EthInstructions::<
797 EthInterpreter,
798 Ctx<BlockEnv, TxEnv, CfgEnv, DB, Chain>,
799 >::new_mainnet_with_spec(spec.into());
800 instructions.insert_instruction(
801 BLOBBASEFEE_OPCODE,
802 revm::interpreter::Instruction::new(arb_blob_basefee, 2),
803 );
804 instructions.insert_instruction(
805 SELFDESTRUCT_OPCODE,
806 revm::interpreter::Instruction::new(arb_selfdestruct, 5000),
807 );
808 instructions.insert_instruction(
809 NUMBER_OPCODE,
810 revm::interpreter::Instruction::new(arb_number, 2),
811 );
812 instructions.insert_instruction(
813 BLOCKHASH_OPCODE,
814 revm::interpreter::Instruction::new(arb_blockhash, 20),
815 );
816 instructions.insert_instruction(
817 BALANCE_OPCODE,
818 revm::interpreter::Instruction::new(arb_balance, 0),
819 );
820 instructions.insert_instruction(
821 SELFBALANCE_OPCODE,
822 revm::interpreter::Instruction::new(arb_selfbalance, 5),
823 );
824
825 loop {
827 let action = interpreter.run_plain(&instructions.instruction_table, context);
828
829 match action {
830 InterpreterAction::Return(result) => {
831 return result;
832 }
833 InterpreterAction::NewFrame(FrameInput::Call(sub_call)) => {
834 let resolved_input: Bytes = match &sub_call.input {
847 revm::interpreter::CallInput::Bytes(b) => b.clone(),
848 revm::interpreter::CallInput::SharedBuffer(range) => {
849 Bytes::from(
853 interpreter
854 .memory
855 .global_slice_range(range.clone())
856 .to_vec(),
857 )
858 }
859 };
860 let bytecode_address = sub_call.bytecode_address;
861 let sub_result = stylus_call_trampoline::<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
862 context as *mut _ as *mut (),
863 match sub_call.scheme {
864 CallScheme::Call | CallScheme::CallCode => 0,
865 CallScheme::DelegateCall => 1,
866 CallScheme::StaticCall => 2,
867 },
868 bytecode_address,
869 sub_call.caller,
870 sub_call.target_address,
871 &resolved_input,
872 sub_call.gas_limit,
873 sub_call.value.get(),
874 );
875
876 let gas_remaining = sub_call.gas_limit.saturating_sub(sub_result.gas_cost);
878 let ins_result = if sub_result.success {
879 InstructionResult::Return
880 } else {
881 InstructionResult::Revert
882 };
883
884 let output: Bytes = sub_result.output.into();
885 let returned_len = output.len();
886 let mem_start = sub_call.return_memory_offset.start;
887 let mem_length = sub_call.return_memory_offset.len();
888 let target_len = mem_length.min(returned_len);
889
890 interpreter.return_data.set_buffer(output);
891
892 let item = if ins_result.is_ok() {
893 U256::from(1)
894 } else {
895 U256::ZERO
896 };
897 let _ = interpreter.stack.push(item);
898
899 if ins_result.is_ok_or_revert() {
900 interpreter.gas.erase_cost(gas_remaining);
901 if target_len > 0 {
902 interpreter
903 .memory
904 .set(mem_start, &interpreter.return_data.buffer()[..target_len]);
905 }
906 }
907
908 if ins_result.is_ok() {
909 interpreter.gas.record_refund(sub_result.refund);
918 }
919 }
920 InterpreterAction::NewFrame(FrameInput::Create(sub_create)) => {
921 let salt = match sub_create.scheme() {
923 revm::interpreter::CreateScheme::Create2 { salt } => {
924 Some(B256::from(salt.to_be_bytes()))
925 }
926 _ => None,
927 };
928
929 let sub_result = stylus_create_trampoline::<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
930 context as *mut _ as *mut (),
931 sub_create.caller(),
932 sub_create.init_code(),
933 sub_create.gas_limit(),
934 sub_create.value(),
935 salt,
936 );
937
938 let gas_remaining = sub_create.gas_limit().saturating_sub(sub_result.gas_cost);
939 let created_addr = sub_result.address;
940
941 let ins_result = if created_addr.is_some() {
942 InstructionResult::Return
943 } else if !sub_result.output.is_empty() {
944 InstructionResult::Revert
945 } else {
946 InstructionResult::CreateInitCodeStartingEF00
947 };
948
949 let output: Bytes = sub_result.output.into();
950 interpreter.return_data.set_buffer(output);
951
952 let item = match created_addr {
954 Some(addr) => addr.into_word().into(),
955 None => U256::ZERO,
956 };
957 let _ = interpreter.stack.push(item);
958
959 if ins_result.is_ok_or_revert() {
960 interpreter.gas.erase_cost(gas_remaining);
961 }
962 }
963 InterpreterAction::NewFrame(FrameInput::Empty) => {
964 return InterpreterResult::new(
966 InstructionResult::Revert,
967 Bytes::new(),
968 EvmGas::new(0),
969 );
970 }
971 }
972 }
973}
974
975fn execute_stylus_program<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
982 context: &mut revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
983 inputs: &CallInputs,
984 bytecode: &[u8],
985) -> InterpreterResult
986where
987 BlockEnv: revm::context::Block,
988 TxEnv: revm::context::Transaction,
989 CfgEnv: revm::context::Cfg,
990 DB: Database,
991{
992 use arbos::programs::types::UserOutcome;
993
994 let zero_gas = || EvmGas::new(0);
995
996 let code_hash = alloy_primitives::keccak256(bytecode);
997 let arbos_version = arb_precompiles::get_arbos_version();
998 let block_timestamp = arb_precompiles::get_block_timestamp();
999
1000 let params_word = match read_params_word(&mut context.journaled_state) {
1002 Some(w) => w,
1003 None => {
1004 tracing::warn!(target: "stylus", "failed to read StylusParams from storage");
1005 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1006 }
1007 };
1008 let params = parse_stylus_params(¶ms_word, arbos_version);
1009
1010 let program_word = match read_program_word(&mut context.journaled_state, code_hash) {
1011 Some(w) => w,
1012 None => {
1013 tracing::warn!(target: "stylus", codehash = %code_hash, "failed to read program data");
1014 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1015 }
1016 };
1017 let program = Program::from_storage(program_word, block_timestamp);
1018
1019 if program.version == 0 || program.version != params.version {
1021 tracing::warn!(target: "stylus", codehash = %code_hash, program_ver = program.version, params_ver = params.version, "program version mismatch");
1022 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1023 }
1024 let expiry_seconds = (params.expiry_days as u64) * 24 * 3600;
1025 if program.age_seconds > expiry_seconds {
1026 tracing::warn!(target: "stylus", codehash = %code_hash, "program expired");
1027 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1028 }
1029
1030 let (pages_open, pages_ever) = get_stylus_pages();
1032 let recent_wasms_hit = if arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_60 {
1034 arb_precompiles::insert_recent_wasm(code_hash)
1035 } else {
1036 false
1037 };
1038 let effective_cached = program.cached || recent_wasms_hit;
1039 let effective_program = if effective_cached != program.cached {
1040 let mut p = program;
1041 p.cached = effective_cached;
1042 p
1043 } else {
1044 program
1045 };
1046 let upfront_cost = stylus_call_gas_cost(¶ms, &effective_program, pages_open, pages_ever);
1047 let total_gas = inputs.gas_limit;
1048
1049 if total_gas < upfront_cost {
1050 return InterpreterResult::new(InstructionResult::OutOfGas, Bytes::new(), zero_gas());
1051 }
1052 let gas_for_wasm = total_gas - upfront_cost;
1053
1054 let stylus_config = StylusConfig::new(params.version, params.max_stack_depth, params.ink_price);
1055
1056 let target_addr = inputs.target_address;
1058 let is_delegate = matches!(
1059 inputs.scheme,
1060 CallScheme::DelegateCall | CallScheme::CallCode
1061 );
1062 let reentrant = if !is_delegate {
1066 push_stylus_program(target_addr)
1067 } else {
1068 get_stylus_program_count(target_addr) > 1
1069 };
1070
1071 let module_hash =
1075 read_module_hash(&mut context.journaled_state, code_hash).unwrap_or(code_hash);
1076
1077 let mut evm_data = build_evm_data(context, inputs);
1079 evm_data.reentrant = reentrant as u32;
1080 evm_data.cached = effective_program.cached;
1081 evm_data.module_hash = module_hash;
1082
1083 let (prev_open, _prev_ever) = add_stylus_pages(program.footprint);
1085
1086 let journal_ptr = &mut context.journaled_state as *mut revm::Journal<DB>;
1088 let is_static = inputs.is_static || matches!(inputs.scheme, CallScheme::StaticCall);
1089 let ctx_ptr = context as *mut _ as *mut ();
1090 let caller = inputs.caller;
1091 let call_value = inputs.value.get();
1092 let evm_api = unsafe {
1093 StylusEvmApi::new(
1094 journal_ptr,
1095 target_addr,
1096 caller,
1097 call_value,
1098 is_static,
1099 params.free_pages,
1100 params.page_gas,
1101 arbos_version,
1102 ctx_ptr,
1103 Some(stylus_call_trampoline::<BlockEnv, TxEnv, CfgEnv, DB, Chain>),
1104 Some(stylus_create_trampoline::<BlockEnv, TxEnv, CfgEnv, DB, Chain>),
1105 )
1106 };
1107
1108 let long_term_tag = if program.cached { 1u32 } else { 0u32 };
1110 let mut instance = if let Some((module, store)) =
1111 arb_stylus::cache::InitCache::get(code_hash, params.version, long_term_tag, false)
1112 {
1113 let compile = arb_stylus::CompileConfig::version(params.version, false);
1114 let env = arb_stylus::env::WasmEnv::new(compile, Some(stylus_config), evm_api, evm_data);
1115 match arb_stylus::NativeInstance::from_module(module, store, env) {
1116 Ok(inst) => inst,
1117 Err(e) => {
1118 tracing::warn!(target: "stylus", codehash = %code_hash, err = %e, "failed from cached module");
1119 set_stylus_pages_open(prev_open);
1120 if !is_delegate {
1121 pop_stylus_program(target_addr);
1122 }
1123 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1124 }
1125 }
1126 } else {
1127 let decompressed = match arb_stylus::decompress_wasm(bytecode) {
1128 Ok(w) => w,
1129 Err(e) => {
1130 tracing::warn!(target: "stylus", codehash = %code_hash, err = %e, "WASM decompression failed");
1131 set_stylus_pages_open(prev_open);
1132 if !is_delegate {
1133 pop_stylus_program(target_addr);
1134 }
1135 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1136 }
1137 };
1138 let compile = arb_stylus::CompileConfig::version(params.version, false);
1139 match arb_stylus::NativeInstance::from_bytes(
1140 &decompressed,
1141 evm_api,
1142 evm_data,
1143 &compile,
1144 stylus_config,
1145 ) {
1146 Ok(inst) => inst,
1147 Err(e) => {
1148 tracing::warn!(target: "stylus", codehash = %code_hash, err = %e, "failed to compile WASM");
1149 set_stylus_pages_open(prev_open);
1150 if !is_delegate {
1151 pop_stylus_program(target_addr);
1152 }
1153 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1154 }
1155 }
1156 };
1157
1158 let ink = stylus_config.pricing.gas_to_ink(StylusGas(gas_for_wasm));
1160
1161 let calldata_owned: Bytes = inputs.input.bytes(context);
1164 let calldata: &[u8] = &calldata_owned;
1165 let outcome = match instance.run_main(calldata, stylus_config, ink) {
1166 Ok(outcome) => outcome,
1167 Err(e) => {
1168 tracing::warn!(target: "stylus", codehash = %code_hash, err = %e, "WASM execution failed");
1169 set_stylus_pages_open(prev_open);
1170 if !is_delegate {
1171 pop_stylus_program(target_addr);
1172 }
1173 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1174 }
1175 };
1176
1177 set_stylus_pages_open(prev_open);
1179 if !is_delegate {
1180 pop_stylus_program(target_addr);
1181 }
1182
1183 let ink_left = match instance.ink_left() {
1185 arb_stylus::MachineMeter::Ready(ink_val) => ink_val,
1186 arb_stylus::MachineMeter::Exhausted => arb_stylus::Ink(0),
1187 };
1188 let gas_left = stylus_config.pricing.ink_to_gas(ink_left).0;
1189
1190 let output: Bytes = instance.env().outs.clone().into();
1192 let gas_left = if !output.is_empty()
1193 && arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS_FIXES
1194 {
1195 let evm_cost = arbos::programs::types::evm_memory_cost(output.len() as u64);
1196 if total_gas < evm_cost {
1197 0
1198 } else {
1199 gas_left.min(total_gas - evm_cost)
1200 }
1201 } else {
1202 gas_left
1203 };
1204
1205 let mut gas_result = EvmGas::new(gas_left);
1206 let sstore_refund = instance.env().evm_api.sstore_refund();
1208 if sstore_refund != 0 {
1209 gas_result.record_refund(sstore_refund);
1210 }
1211
1212 match outcome {
1214 UserOutcome::Success => {
1215 InterpreterResult::new(InstructionResult::Return, output, gas_result)
1216 }
1217 UserOutcome::Revert => {
1218 InterpreterResult::new(InstructionResult::Revert, output, gas_result)
1219 }
1220 UserOutcome::OutOfInk => {
1221 InterpreterResult::new(InstructionResult::OutOfGas, Bytes::new(), zero_gas())
1222 }
1223 UserOutcome::OutOfStack => {
1224 InterpreterResult::new(InstructionResult::CallTooDeep, Bytes::new(), zero_gas())
1225 }
1226 UserOutcome::Failure => {
1227 InterpreterResult::new(InstructionResult::Revert, Bytes::new(), gas_result)
1228 }
1229 }
1230}
1231
1232fn build_evm_data<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
1234 context: &revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1235 inputs: &CallInputs,
1236) -> EvmData
1237where
1238 BlockEnv: revm::context::Block,
1239 TxEnv: revm::context::Transaction,
1240 CfgEnv: revm::context::Cfg,
1241 DB: Database,
1242{
1243 let basefee = U256::from(context.block.basefee());
1244 let gas_price = U256::from(context.tx.gas_price());
1245 let value = inputs.value.get();
1246
1247 let l1_block_number = arb_precompiles::get_l1_block_number_for_evm();
1249
1250 EvmData {
1251 arbos_version: arb_precompiles::get_arbos_version(),
1252 block_basefee: B256::from(basefee.to_be_bytes()),
1253 chain_id: context.cfg.chain_id(),
1254 block_coinbase: context.block.beneficiary(),
1255 block_gas_limit: context.block.gas_limit(),
1256 block_number: l1_block_number,
1257 block_timestamp: context.block.timestamp().saturating_to(),
1258 contract_address: inputs.target_address,
1259 module_hash: alloy_primitives::keccak256(b""),
1260 msg_sender: inputs.caller,
1261 msg_value: B256::from(value.to_be_bytes()),
1262 tx_gas_price: B256::from(gas_price.to_be_bytes()),
1263 tx_origin: context.tx.caller(),
1264 reentrant: 0,
1265 cached: false,
1266 tracing: false,
1267 }
1268}
1269
1270fn is_stylus_call(frame_init: &FrameInit) -> Option<Bytes> {
1274 if arb_precompiles::get_arbos_version() < arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS {
1275 return None;
1276 }
1277 if let FrameInput::Call(ref inputs) = frame_init.frame_input {
1278 if let Some((_, ref code)) = inputs.known_bytecode {
1279 let raw = code.original_bytes();
1280 if arb_stylus::is_stylus_program(&raw) {
1281 return Some(raw);
1282 }
1283 }
1284 }
1285 None
1286}
1287
1288fn execute_stylus_call_concrete<DB: Database>(
1291 ctx: &mut EthEvmContext<DB>,
1292 inputs: &CallInputs,
1293 bytecode: &[u8],
1294 checkpoint: revm::context_interface::journaled_state::JournalCheckpoint,
1295) -> FrameResult {
1296 if let revm::interpreter::CallValue::Transfer(value) = inputs.value {
1298 if let Some(i) =
1299 ctx.journal_mut()
1300 .transfer_loaded(inputs.caller, inputs.target_address, value)
1301 {
1302 let gas = EvmGas::new(inputs.gas_limit);
1303 ctx.journaled_state.inner.checkpoint_revert(checkpoint);
1304 return FrameResult::Call(CallOutcome {
1305 result: InterpreterResult::new(i.into(), Bytes::new(), gas),
1306 memory_offset: inputs.return_memory_offset.clone(),
1307 was_precompile_called: false,
1308 precompile_call_logs: Vec::new(),
1309 });
1310 }
1311 }
1312
1313 let result = execute_stylus_program(ctx, inputs, bytecode);
1314
1315 if result.result.is_ok() {
1316 ctx.journaled_state.inner.checkpoint_commit();
1317 } else {
1318 ctx.journaled_state.inner.checkpoint_revert(checkpoint);
1319 }
1320 FrameResult::Call(CallOutcome {
1321 result,
1322 memory_offset: inputs.return_memory_offset.clone(),
1323 was_precompile_called: false,
1324 precompile_call_logs: Vec::new(),
1325 })
1326}
1327
1328#[derive(Clone, Debug)]
1334pub struct ArbPrecompilesMap(pub PrecompilesMap);
1335
1336impl<BlockEnv, TxEnv, CfgEnv, DB, Chain>
1337 PrecompileProvider<revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>>
1338 for ArbPrecompilesMap
1339where
1340 BlockEnv: revm::context::Block,
1341 TxEnv: revm::context::Transaction,
1342 CfgEnv: revm::context::Cfg,
1343 DB: Database,
1344{
1345 type Output = InterpreterResult;
1346
1347 fn set_spec(&mut self, spec: CfgEnv::Spec) -> bool {
1348 <PrecompilesMap as PrecompileProvider<
1349 revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1350 >>::set_spec(&mut self.0, spec)
1351 }
1352
1353 fn run(
1354 &mut self,
1355 context: &mut revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1356 inputs: &CallInputs,
1357 ) -> Result<Option<Self::Output>, String> {
1358 arb_precompiles::set_evm_depth(context.journaled_state.inner.depth);
1360
1361 if let result @ Some(_) = <PrecompilesMap as PrecompileProvider<
1363 revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1364 >>::run(&mut self.0, context, inputs)?
1365 {
1366 return Ok(result);
1367 }
1368
1369 let arbos_version = arb_precompiles::get_arbos_version();
1371 if arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS {
1372 let bytecode = inputs
1375 .known_bytecode
1376 .as_ref()
1377 .map(|(_, code)| code.original_bytes())
1378 .or_else(|| {
1379 context
1380 .journaled_state
1381 .inner
1382 .load_code(
1383 &mut context.journaled_state.database,
1384 inputs.bytecode_address,
1385 )
1386 .ok()
1387 .and_then(|acc| acc.data.info.code.as_ref().map(|c| c.original_bytes()))
1388 });
1389
1390 if let Some(bytecode) = bytecode {
1391 if arb_stylus::is_stylus_program(&bytecode) {
1392 return Ok(Some(execute_stylus_program(context, inputs, &bytecode)));
1393 }
1394 }
1395 }
1396
1397 Ok(None)
1398 }
1399
1400 fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
1401 <PrecompilesMap as PrecompileProvider<
1402 revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1403 >>::warm_addresses(&self.0)
1404 }
1405
1406 fn contains(&self, address: &Address) -> bool {
1407 <PrecompilesMap as PrecompileProvider<
1408 revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1409 >>::contains(&self.0, address)
1410 }
1411}
1412
1413type InnerRevmEvm<DB, I> = RevmEvm<
1416 EthEvmContext<DB>,
1417 I,
1418 EthInstructions<EthInterpreter, EthEvmContext<DB>>,
1419 ArbPrecompilesMap,
1420 EthFrame,
1421>;
1422
1423pub struct ArbEvm<DB: Database, I> {
1428 inner: InnerRevmEvm<DB, I>,
1429 inspect: bool,
1430}
1431
1432impl<DB, I> ArbEvm<DB, I>
1433where
1434 DB: Database,
1435{
1436 pub fn new(inner: InnerRevmEvm<DB, I>, inspect: bool) -> Self {
1437 Self { inner, inspect }
1438 }
1439
1440 pub fn into_inner(self) -> InnerRevmEvm<DB, I> {
1441 self.inner
1442 }
1443
1444 pub fn ctx(&self) -> &EthEvmContext<DB> {
1445 &self.inner.ctx
1446 }
1447
1448 pub fn ctx_mut(&mut self) -> &mut EthEvmContext<DB> {
1449 &mut self.inner.ctx
1450 }
1451
1452 pub fn precompiles_mut(&mut self) -> &mut ArbPrecompilesMap {
1453 &mut self.inner.precompiles
1454 }
1455}
1456
1457impl<DB: Database, I> core::ops::Deref for ArbEvm<DB, I> {
1458 type Target = EthEvmContext<DB>;
1459 fn deref(&self) -> &Self::Target {
1460 &self.inner.ctx
1461 }
1462}
1463
1464impl<DB: Database, I> core::ops::DerefMut for ArbEvm<DB, I> {
1465 fn deref_mut(&mut self) -> &mut Self::Target {
1466 &mut self.inner.ctx
1467 }
1468}
1469
1470impl<DB, I> EvmTr for ArbEvm<DB, I>
1473where
1474 DB: Database,
1475 I: Inspector<EthEvmContext<DB>, EthInterpreter>,
1476{
1477 type Context = EthEvmContext<DB>;
1478 type Instructions = EthInstructions<EthInterpreter, EthEvmContext<DB>>;
1479 type Precompiles = ArbPrecompilesMap;
1480 type Frame = EthFrame<EthInterpreter>;
1481
1482 #[inline]
1483 fn all(
1484 &self,
1485 ) -> (
1486 &Self::Context,
1487 &Self::Instructions,
1488 &Self::Precompiles,
1489 &FrameStack<Self::Frame>,
1490 ) {
1491 self.inner.all()
1492 }
1493
1494 #[inline]
1495 fn all_mut(
1496 &mut self,
1497 ) -> (
1498 &mut Self::Context,
1499 &mut Self::Instructions,
1500 &mut Self::Precompiles,
1501 &mut FrameStack<Self::Frame>,
1502 ) {
1503 self.inner.all_mut()
1504 }
1505
1506 #[inline]
1507 fn frame_init(
1508 &mut self,
1509 frame_input: FrameInit,
1510 ) -> Result<
1511 ItemOrResult<&mut Self::Frame, FrameResult>,
1512 revm::handler::evm::ContextDbError<Self::Context>,
1513 > {
1514 let pushed_caller = match &frame_input.frame_input {
1516 FrameInput::Call(inputs) => {
1517 arb_precompiles::push_caller_frame(inputs.caller);
1518 true
1519 }
1520 FrameInput::Create(inputs) => {
1521 arb_precompiles::push_caller_frame(inputs.caller());
1522 true
1523 }
1524 _ => false,
1525 };
1526
1527 if let Some(bytecode) = is_stylus_call(&frame_input) {
1529 if let FrameInput::Call(ref inputs) = frame_input.frame_input {
1530 if frame_input.depth > revm::primitives::constants::CALL_STACK_LIMIT as usize {
1531 let gas = EvmGas::new(inputs.gas_limit);
1532 if pushed_caller {
1533 arb_precompiles::pop_caller_frame();
1534 }
1535 return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
1536 result: InterpreterResult::new(
1537 InstructionResult::CallTooDeep,
1538 Bytes::new(),
1539 gas,
1540 ),
1541 memory_offset: inputs.return_memory_offset.clone(),
1542 was_precompile_called: false,
1543 precompile_call_logs: Vec::new(),
1544 })));
1545 }
1546 let checkpoint = self.inner.ctx.journal_mut().checkpoint();
1547 let result = execute_stylus_call_concrete(
1548 &mut self.inner.ctx,
1549 inputs,
1550 &bytecode,
1551 checkpoint,
1552 );
1553 if pushed_caller {
1554 arb_precompiles::pop_caller_frame();
1555 }
1556 return Ok(ItemOrResult::Result(result));
1557 }
1558 }
1559
1560 self.inner.frame_init(frame_input)
1562 }
1563
1564 #[inline]
1565 fn frame_run(
1566 &mut self,
1567 ) -> Result<
1568 ItemOrResult<FrameInit, FrameResult>,
1569 revm::handler::evm::ContextDbError<Self::Context>,
1570 > {
1571 self.inner.frame_run()
1572 }
1573
1574 #[inline]
1575 fn frame_return_result(
1576 &mut self,
1577 result: FrameResult,
1578 ) -> Result<Option<FrameResult>, revm::handler::evm::ContextDbError<Self::Context>> {
1579 arb_precompiles::pop_caller_frame();
1581 self.inner.frame_return_result(result)
1582 }
1583}
1584
1585impl<DB, I> ExecuteEvm for ArbEvm<DB, I>
1588where
1589 DB: Database,
1590 I: Inspector<EthEvmContext<DB>, EthInterpreter>,
1591{
1592 type ExecutionResult = ExecutionResult<HaltReason>;
1593 type State = revm::state::EvmState;
1594 type Error = EVMError<<DB as revm::Database>::Error, InvalidTransaction>;
1595 type Tx = revm::context::TxEnv;
1596 type Block = revm::context::BlockEnv;
1597
1598 #[inline]
1599 fn transact_one(&mut self, tx: Self::Tx) -> Result<Self::ExecutionResult, Self::Error> {
1600 self.inner.ctx.set_tx(tx);
1601 MainnetHandler::default().run(self)
1602 }
1603
1604 #[inline]
1605 fn finalize(&mut self) -> Self::State {
1606 self.inner.ctx.journal_mut().finalize()
1607 }
1608
1609 #[inline]
1610 fn set_block(&mut self, block: Self::Block) {
1611 self.inner.ctx.set_block(block);
1612 }
1613
1614 #[inline]
1615 fn replay(&mut self) -> Result<ResultAndState<HaltReason>, Self::Error> {
1616 MainnetHandler::default().run(self).map(|result| {
1617 let state = self.finalize();
1618 ResultAndState::new(result, state)
1619 })
1620 }
1621}
1622
1623impl<DB, I> SystemCallEvm for ArbEvm<DB, I>
1626where
1627 DB: Database,
1628 I: Inspector<EthEvmContext<DB>, EthInterpreter>,
1629{
1630 fn system_call_one_with_caller(
1631 &mut self,
1632 caller: Address,
1633 system_contract_address: Address,
1634 data: Bytes,
1635 ) -> Result<Self::ExecutionResult, Self::Error> {
1636 use revm::handler::system_call::SystemCallTx;
1637 self.inner
1638 .ctx
1639 .set_tx(revm::context::TxEnv::new_system_tx_with_caller(
1640 caller,
1641 system_contract_address,
1642 data,
1643 ));
1644 MainnetHandler::default().run_system_call(self)
1645 }
1646}
1647
1648impl<DB, I> revm::inspector::InspectorEvmTr for ArbEvm<DB, I>
1651where
1652 DB: Database,
1653 I: Inspector<EthEvmContext<DB>, EthInterpreter>,
1654 revm::Journal<DB>: revm::inspector::JournalExt,
1655{
1656 type Inspector = I;
1657
1658 fn all_inspector(
1659 &self,
1660 ) -> (
1661 &Self::Context,
1662 &Self::Instructions,
1663 &Self::Precompiles,
1664 &FrameStack<Self::Frame>,
1665 &Self::Inspector,
1666 ) {
1667 let (ctx, inst, pre, fs) = self.inner.all();
1668 (ctx, inst, pre, fs, &self.inner.inspector)
1669 }
1670
1671 fn all_mut_inspector(
1672 &mut self,
1673 ) -> (
1674 &mut Self::Context,
1675 &mut Self::Instructions,
1676 &mut Self::Precompiles,
1677 &mut FrameStack<Self::Frame>,
1678 &mut Self::Inspector,
1679 ) {
1680 (
1681 &mut self.inner.ctx,
1682 &mut self.inner.instruction,
1683 &mut self.inner.precompiles,
1684 &mut self.inner.frame_stack,
1685 &mut self.inner.inspector,
1686 )
1687 }
1688}
1689
1690impl<DB, I> InspectEvm for ArbEvm<DB, I>
1693where
1694 DB: Database,
1695 I: Inspector<EthEvmContext<DB>, EthInterpreter>,
1696 revm::Journal<DB>: revm::inspector::JournalExt,
1697{
1698 type Inspector = I;
1699
1700 fn set_inspector(&mut self, inspector: Self::Inspector) {
1701 self.inner.inspector = inspector;
1702 }
1703
1704 fn inspect_one_tx(&mut self, tx: Self::Tx) -> Result<Self::ExecutionResult, Self::Error> {
1705 self.inner.ctx.set_tx(tx);
1706 MainnetHandler::default().inspect_run(self)
1707 }
1708}
1709
1710impl<DB, I> Evm for ArbEvm<DB, I>
1713where
1714 DB: Database,
1715 I: Inspector<EthEvmContext<DB>, EthInterpreter>,
1716 revm::Journal<DB>: revm::inspector::JournalExt,
1717{
1718 type DB = DB;
1719 type Tx = ArbTransaction;
1720 type Error = EVMError<<DB as revm::Database>::Error>;
1721 type HaltReason = HaltReason;
1722 type Spec = SpecId;
1723 type Precompiles = PrecompilesMap;
1724 type Inspector = I;
1725 type BlockEnv = revm::context::BlockEnv;
1726
1727 fn block(&self) -> &revm::context::BlockEnv {
1728 &self.inner.ctx.block
1729 }
1730
1731 fn chain_id(&self) -> u64 {
1732 self.inner.ctx.cfg.chain_id
1733 }
1734
1735 fn transact_raw(
1736 &mut self,
1737 tx: Self::Tx,
1738 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
1739 if self.inspect {
1740 InspectEvm::inspect_tx(self, tx.into_inner())
1741 } else {
1742 ExecuteEvm::transact(self, tx.into_inner())
1743 }
1744 }
1745
1746 fn transact_system_call(
1747 &mut self,
1748 caller: Address,
1749 contract: Address,
1750 data: Bytes,
1751 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
1752 SystemCallEvm::system_call_with_caller(self, caller, contract, data)
1753 }
1754
1755 fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>) {
1756 let revm::Context {
1757 block: block_env,
1758 cfg: cfg_env,
1759 journaled_state,
1760 ..
1761 } = self.inner.ctx;
1762 (journaled_state.database, EvmEnv { block_env, cfg_env })
1763 }
1764
1765 fn set_inspector_enabled(&mut self, enabled: bool) {
1766 self.inspect = enabled;
1767 }
1768
1769 fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) {
1770 (
1771 &self.inner.ctx.journaled_state.database,
1772 &self.inner.inspector,
1773 &self.inner.precompiles.0,
1774 )
1775 }
1776
1777 fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) {
1778 (
1779 &mut self.inner.ctx.journaled_state.database,
1780 &mut self.inner.inspector,
1781 &mut self.inner.precompiles.0,
1782 )
1783 }
1784}
1785
1786#[derive(Default, Debug, Clone, Copy)]
1790pub struct ArbEvmFactory(pub alloy_evm::EthEvmFactory);
1791
1792impl ArbEvmFactory {
1793 pub fn new() -> Self {
1794 Self::default()
1795 }
1796}
1797
1798fn build_arb_evm<DB: Database, I>(
1799 inner: RevmEvm<
1800 EthEvmContext<DB>,
1801 I,
1802 EthInstructions<EthInterpreter, EthEvmContext<DB>>,
1803 PrecompilesMap,
1804 EthFrame,
1805 >,
1806 inspect: bool,
1807) -> ArbEvm<DB, I> {
1808 let RevmEvm {
1809 ctx,
1810 inspector,
1811 mut instruction,
1812 mut precompiles,
1813 frame_stack: _,
1814 } = inner;
1815
1816 instruction.insert_instruction(
1817 BLOBBASEFEE_OPCODE,
1818 revm::interpreter::Instruction::new(arb_blob_basefee, 2),
1819 );
1820 instruction.insert_instruction(
1821 SELFDESTRUCT_OPCODE,
1822 revm::interpreter::Instruction::new(arb_selfdestruct, 5000),
1823 );
1824 instruction.insert_instruction(
1825 NUMBER_OPCODE,
1826 revm::interpreter::Instruction::new(arb_number, 2),
1827 );
1828 instruction.insert_instruction(
1829 BLOCKHASH_OPCODE,
1830 revm::interpreter::Instruction::new(arb_blockhash, 20),
1831 );
1832 instruction.insert_instruction(
1833 BALANCE_OPCODE,
1834 revm::interpreter::Instruction::new(arb_balance, 0),
1835 );
1836 instruction.insert_instruction(
1837 SELFBALANCE_OPCODE,
1838 revm::interpreter::Instruction::new(arb_selfbalance, 5),
1839 );
1840 register_arb_precompiles(&mut precompiles, arb_precompiles::get_arbos_version());
1841 let arb_precompiles = ArbPrecompilesMap(precompiles);
1842
1843 let revm_evm = RevmEvm::new_with_inspector(ctx, inspector, instruction, arb_precompiles);
1844 ArbEvm::new(revm_evm, inspect)
1845}
1846
1847impl EvmFactory for ArbEvmFactory {
1848 type Evm<DB: Database, I: Inspector<EthEvmContext<DB>, EthInterpreter>> = ArbEvm<DB, I>;
1849 type Context<DB: Database> = EthEvmContext<DB>;
1850 type Tx = ArbTransaction;
1851 type Error<DBError: core::error::Error + Send + Sync + 'static> = EVMError<DBError>;
1852 type HaltReason = HaltReason;
1853 type Spec = SpecId;
1854 type Precompiles = PrecompilesMap;
1855 type BlockEnv = revm::context::BlockEnv;
1856
1857 fn create_evm<DB: Database>(
1858 &self,
1859 db: DB,
1860 input: EvmEnv<Self::Spec>,
1861 ) -> Self::Evm<DB, NoOpInspector> {
1862 let eth_evm = self.0.create_evm(db, input);
1863 build_arb_evm(eth_evm.into_inner(), false)
1864 }
1865
1866 fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>, EthInterpreter>>(
1867 &self,
1868 db: DB,
1869 input: EvmEnv<Self::Spec>,
1870 inspector: I,
1871 ) -> Self::Evm<DB, I> {
1872 let eth_evm = self.0.create_evm_with_inspector(db, input, inspector);
1873 build_arb_evm(eth_evm.into_inner(), true)
1874 }
1875}