1use core::fmt::Debug;
2
3use alloy_evm::{
4 Database, Evm, EvmEnv, EvmFactory, eth::EthEvmContext, precompiles::PrecompilesMap,
5};
6use alloy_primitives::{Address, B256, Bytes, U256};
7use arb_precompiles::register_arb_precompiles;
8use arb_stylus::{
9 StylusEvmApi, config::StylusConfig, ink::Gas as StylusGas, meter::MeteredMachine,
10 run::RunProgram,
11};
12use arbos::programs::types::EvmData;
13use revm::{
14 ExecuteEvm, InspectEvm, Inspector, SystemCallEvm,
15 context::{
16 ContextSetters, Evm as RevmEvm, FrameStack,
17 result::{EVMError, ExecutionResult, InvalidTransaction},
18 },
19 context_interface::{
20 ContextTr, JournalTr,
21 host::LoadError,
22 result::{HaltReason, ResultAndState},
23 },
24 handler::{
25 EthFrame, EvmTr, FrameResult, Handler, ItemOrResult, MainnetHandler, PrecompileProvider,
26 instructions::EthInstructions,
27 },
28 inspector::{InspectorHandler, NoOpInspector},
29 interpreter::{
30 CallInput, CallInputs, CallOutcome, CallScheme, FrameInput, Gas as EvmGas, Host,
31 InstructionContext, InstructionResult, InterpreterResult, InterpreterTypes,
32 interpreter::EthInterpreter,
33 interpreter_action::FrameInit,
34 interpreter_types::{InputsTr, ReturnData, RuntimeFlag, StackTr},
35 },
36 primitives::hardfork::SpecId,
37};
38
39use crate::transaction::ArbTransaction;
40
41const BLOBBASEFEE_OPCODE: u8 = 0x4a;
43
44const SELFDESTRUCT_OPCODE: u8 = 0xff;
46
47const NUMBER_OPCODE: u8 = 0x43;
49
50const BLOCKHASH_OPCODE: u8 = 0x40;
52
53const BALANCE_OPCODE: u8 = 0x31;
55
56fn arb_number<WIRE: InterpreterTypes, H: Host + ?Sized>(ctx: InstructionContext<'_, H, WIRE>) {
62 let l1_block = ctx.host.block_number();
63 if !ctx.interpreter.stack.push(l1_block) {
64 ctx.interpreter.halt(InstructionResult::StackOverflow);
65 }
66}
67
68fn arb_blockhash<WIRE: InterpreterTypes, H: Host + ?Sized>(ctx: InstructionContext<'_, H, WIRE>) {
75 use revm::interpreter::InstructionResult;
76
77 let requested = match ctx.interpreter.stack.pop() {
78 Some(v) => v,
79 None => {
80 ctx.interpreter.halt(InstructionResult::StackUnderflow);
81 return;
82 }
83 };
84
85 let l1_block_number = ctx.host.block_number();
86
87 let Some(diff) = l1_block_number.checked_sub(requested) else {
88 if !ctx.interpreter.stack.push(U256::ZERO) {
89 ctx.interpreter.halt(InstructionResult::StackOverflow);
90 }
91 return;
92 };
93
94 let diff_u64: u64 = diff.try_into().unwrap_or(u64::MAX);
95 if diff_u64 == 0 || diff_u64 > 256 {
96 if !ctx.interpreter.stack.push(U256::ZERO) {
97 ctx.interpreter.halt(InstructionResult::StackOverflow);
98 }
99 return;
100 }
101
102 let requested_u64: u64 = requested.try_into().unwrap_or(u64::MAX);
103 match ctx.host.block_hash(requested_u64) {
104 Some(hash) => {
105 if !ctx.interpreter.stack.push(U256::from_be_bytes(hash.0)) {
106 ctx.interpreter.halt(InstructionResult::StackOverflow);
107 }
108 }
109 None => {
110 ctx.interpreter.halt_fatal();
111 }
112 }
113}
114
115fn arb_balance<WIRE: InterpreterTypes, H: Host + ?Sized>(ctx: InstructionContext<'_, H, WIRE>) {
121 let addr_u256 = match ctx.interpreter.stack.pop() {
122 Some(v) => v,
123 None => {
124 ctx.interpreter
125 .halt(revm::interpreter::InstructionResult::StackUnderflow);
126 return;
127 }
128 };
129
130 let addr = alloy_primitives::Address::from_word(alloy_primitives::B256::from(
131 addr_u256.to_be_bytes::<32>(),
132 ));
133
134 let sender = ctx.host.caller();
135 let correction = poster_balance_correction_word();
136
137 let spec_id = ctx.interpreter.runtime_flag.spec_id();
138 if spec_id.is_enabled_in(revm::primitives::hardfork::SpecId::BERLIN) {
139 let Some(state_load) = ctx.host.balance(addr) else {
140 ctx.interpreter.halt_fatal();
141 return;
142 };
143 let gas_cost = if state_load.is_cold { 2600u64 } else { 100u64 };
144 if !ctx.interpreter.gas.record_cost(gas_cost) {
145 ctx.interpreter
146 .halt(revm::interpreter::InstructionResult::OutOfGas);
147 return;
148 }
149
150 let balance = if addr == sender {
151 state_load.data.saturating_sub(correction)
152 } else {
153 state_load.data
154 };
155
156 if !ctx.interpreter.stack.push(balance) {
157 ctx.interpreter
158 .halt(revm::interpreter::InstructionResult::StackOverflow);
159 }
160 } else {
161 let Some(state_load) = ctx.host.balance(addr) else {
162 ctx.interpreter.halt_fatal();
163 return;
164 };
165
166 let balance = if addr == sender {
167 state_load.data.saturating_sub(correction)
168 } else {
169 state_load.data
170 };
171
172 if !ctx.interpreter.stack.push(balance) {
173 ctx.interpreter
174 .halt(revm::interpreter::InstructionResult::StackOverflow);
175 }
176 }
177}
178
179const SELFBALANCE_OPCODE: u8 = 0x47;
181
182fn arb_selfbalance<WIRE: InterpreterTypes, H: Host + ?Sized>(ctx: InstructionContext<'_, H, WIRE>) {
185 let target = ctx.interpreter.input.target_address();
186
187 let Some(state_load) = ctx.host.balance(target) else {
188 ctx.interpreter.halt_fatal();
189 return;
190 };
191
192 let sender = ctx.host.caller();
193 let balance = if target == sender {
194 state_load
195 .data
196 .saturating_sub(poster_balance_correction_word())
197 } else {
198 state_load.data
199 };
200
201 if !ctx.interpreter.stack.push(balance) {
202 ctx.interpreter
203 .halt(revm::interpreter::InstructionResult::StackOverflow);
204 }
205}
206
207thread_local! {
216 static POSTER_BALANCE_CORRECTION: std::cell::Cell<u128> = const { std::cell::Cell::new(0) };
217}
218
219pub fn set_poster_balance_correction(value: u128) {
222 POSTER_BALANCE_CORRECTION.with(|cell| cell.set(value));
223}
224
225pub fn clear_poster_balance_correction() {
227 POSTER_BALANCE_CORRECTION.with(|cell| cell.set(0));
228}
229
230fn poster_balance_correction_word() -> U256 {
231 POSTER_BALANCE_CORRECTION.with(|cell| U256::from(cell.get()))
232}
233
234fn arb_blob_basefee<WIRE: InterpreterTypes, H: Host + ?Sized>(
236 ctx: InstructionContext<'_, H, WIRE>,
237) {
238 ctx.interpreter.halt(InstructionResult::OpcodeNotFound);
239}
240
241fn arb_selfdestruct<WIRE: InterpreterTypes, H: Host + ?Sized>(
244 ctx: InstructionContext<'_, H, WIRE>,
245) {
246 if ctx.interpreter.runtime_flag.is_static() {
247 ctx.interpreter
248 .halt(InstructionResult::StateChangeDuringStaticCall);
249 return;
250 }
251
252 let acting_addr = ctx.interpreter.input.target_address();
254 match ctx.host.load_account_code(acting_addr) {
255 Some(code_load) => {
256 if arb_stylus::is_stylus_runnable(&code_load.data) {
257 ctx.interpreter.halt(InstructionResult::Revert);
258 return;
259 }
260 }
261 None => {
262 ctx.interpreter.halt_fatal();
263 return;
264 }
265 }
266
267 let Some(raw) = ctx.interpreter.stack.pop() else {
271 ctx.interpreter.halt(InstructionResult::StackUnderflow);
272 return;
273 };
274 let target = Address::from_word(alloy_primitives::B256::from(raw.to_be_bytes()));
275
276 let spec = ctx.interpreter.runtime_flag.spec_id();
277 let cold_load_gas = ctx.host.gas_params().selfdestruct_cold_cost();
278 let skip_cold_load = ctx.interpreter.gas.remaining() < cold_load_gas;
279
280 let res = match ctx.host.selfdestruct(acting_addr, target, skip_cold_load) {
281 Ok(res) => res,
282 Err(LoadError::ColdLoadSkipped) => {
283 ctx.interpreter.halt_oog();
284 return;
285 }
286 Err(LoadError::DBError) => {
287 ctx.interpreter.halt_fatal();
288 return;
289 }
290 };
291
292 let should_charge_topup = if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
294 res.had_value && !res.target_exists
295 } else {
296 !res.target_exists
297 };
298
299 let gas_cost = ctx
300 .host
301 .gas_params()
302 .selfdestruct_cost(should_charge_topup, res.is_cold);
303 if !ctx.interpreter.gas.record_cost(gas_cost) {
304 ctx.interpreter.halt_oog();
305 return;
306 }
307
308 if !res.previously_destroyed {
309 ctx.interpreter
310 .gas
311 .record_refund(ctx.host.gas_params().selfdestruct_refund());
312 }
313
314 ctx.interpreter.halt(InstructionResult::SelfDestruct);
315}
316
317pub fn reset_stylus_pages(ctx: &arb_context::ArbPrecompileCtx) {
319 let mut tx = ctx.tx.lock();
320 tx.stylus_program_counts.clear();
321}
322
323use arb_storage::{
326 ARBOS_STATE_ADDRESS, DatabaseError, DatabaseErrorInfo, Detached, Storage, StorageBackend,
327 StorageError, SystemStateBackend,
328 layout::{
329 PROGRAMS_SUBSPACE, ROOT_STORAGE_KEY, derive_subspace_key, map_slot_b256,
330 programs::{MODULE_HASHES_KEY, PARAMS_KEY, PROGRAM_DATA_KEY},
331 },
332};
333use arbos::programs::{Program, memory::MemoryModel, params::StylusParams};
334
335fn sload_arbos<DB: Database>(journal: &mut revm::Journal<DB>, slot: U256) -> Option<U256> {
337 let _ = journal
338 .inner
339 .load_account(&mut journal.database, ARBOS_STATE_ADDRESS)
340 .ok()?;
341 let result = journal
342 .inner
343 .sload(&mut journal.database, ARBOS_STATE_ADDRESS, slot, false)
344 .ok()?;
345 Some(result.data)
346}
347
348struct JournalBackend<'a, DB: Database> {
353 journal: &'a mut revm::Journal<DB>,
354}
355
356impl<'a, DB: Database> JournalBackend<'a, DB> {
357 fn new(journal: &'a mut revm::Journal<DB>) -> Self {
358 Self { journal }
359 }
360}
361
362impl<DB: Database> SystemStateBackend for JournalBackend<'_, DB> {
363 type Error = StorageError;
364
365 fn sload_system(&mut self, account: Address, slot: U256) -> Result<U256, Self::Error> {
366 let journal = &mut *self.journal;
370 journal
371 .inner
372 .load_account(&mut journal.database, account)
373 .map_err(|e| {
374 StorageError::Database(DatabaseError::Read(DatabaseErrorInfo::new(format!(
375 "{e:?}"
376 ))))
377 })?;
378 let value = journal
379 .inner
380 .sload(&mut journal.database, account, slot, false)
381 .map_err(|e| {
382 StorageError::Database(DatabaseError::Read(DatabaseErrorInfo::new(format!(
383 "{e:?}"
384 ))))
385 })?;
386 Ok(value.data)
387 }
388}
389
390impl<DB: Database> StorageBackend for JournalBackend<'_, DB> {
391 fn sload(&mut self, account: Address, slot: U256) -> Result<U256, StorageError> {
392 let journal = &mut *self.journal;
393 journal
394 .inner
395 .load_account(&mut journal.database, account)
396 .map_err(|e| {
397 StorageError::Database(DatabaseError::Read(DatabaseErrorInfo::new(format!(
398 "{e:?}"
399 ))))
400 })?;
401 let value = journal
402 .inner
403 .sload(&mut journal.database, account, slot, false)
404 .map_err(|e| {
405 StorageError::Database(DatabaseError::Read(DatabaseErrorInfo::new(format!(
406 "{e:?}"
407 ))))
408 })?;
409 Ok(value.data)
410 }
411
412 fn sstore(&mut self, account: Address, slot: U256, value: U256) -> Result<(), StorageError> {
413 let journal = &mut *self.journal;
414 journal
415 .inner
416 .sstore(&mut journal.database, account, slot, value, false)
417 .map_err(|e| {
418 StorageError::Database(DatabaseError::Write(DatabaseErrorInfo::new(format!(
419 "{e:?}"
420 ))))
421 })?;
422 Ok(())
423 }
424}
425
426fn programs_params_storage() -> Storage<'static, Detached> {
431 let programs_key = derive_subspace_key(ROOT_STORAGE_KEY, PROGRAMS_SUBSPACE);
432 let params_key = derive_subspace_key(programs_key.as_slice(), PARAMS_KEY);
433 Storage::detached(ARBOS_STATE_ADDRESS, params_key)
434}
435
436fn read_program_word<DB: Database>(
438 journal: &mut revm::Journal<DB>,
439 code_hash: B256,
440) -> Option<B256> {
441 let programs_key = derive_subspace_key(ROOT_STORAGE_KEY, PROGRAMS_SUBSPACE);
442 let data_key = derive_subspace_key(programs_key.as_slice(), PROGRAM_DATA_KEY);
443 let slot = map_slot_b256(data_key.as_slice(), &code_hash);
444 sload_arbos(journal, slot).map(|v| B256::from(v.to_be_bytes::<32>()))
445}
446
447fn read_module_hash<DB: Database>(
450 journal: &mut revm::Journal<DB>,
451 code_hash: B256,
452) -> Option<B256> {
453 let programs_key = derive_subspace_key(ROOT_STORAGE_KEY, PROGRAMS_SUBSPACE);
454 let module_hashes_key = derive_subspace_key(programs_key.as_slice(), MODULE_HASHES_KEY);
455 let slot = map_slot_b256(module_hashes_key.as_slice(), &code_hash);
456 sload_arbos(journal, slot).map(|v| B256::from(v.to_be_bytes::<32>()))
457}
458
459fn stylus_call_gas_cost(
461 params: &StylusParams,
462 program: &Program,
463 pages_open: u16,
464 pages_ever: u16,
465 arbos_version: u64,
466) -> u64 {
467 let model = MemoryModel::new(params.free_pages, params.page_gas);
468 let mut cost = model.gas_cost(program.footprint, pages_open, pages_ever);
469
470 let cached = program.cached;
471 if cached || program.version > 1 {
472 cost = cost.saturating_add(program.cached_gas(params));
473 }
474 if !cached {
475 cost = cost.saturating_add(program.init_gas(params));
476 }
477 let new_open = pages_open.saturating_add(program.footprint);
478 if arb_stylus::env::page_limit_exceeded(arbos_version, params.page_limit, new_open) {
479 cost = cost.saturating_add(u64::MAX);
480 }
481 cost
482}
483
484use arb_stylus::evm_api_impl::{SubCallResult, SubCreateResult};
487
488fn read_tx_pages(ctx: &arb_context::ArbPrecompileCtx) -> (u16, u16) {
490 let tx = ctx.tx.lock();
491 (tx.stylus_pages_open, tx.stylus_pages_ever)
492}
493
494fn write_tx_pages(ctx: &arb_context::ArbPrecompileCtx, pages: (u16, u16)) {
496 let mut tx = ctx.tx.lock();
497 tx.stylus_pages_open = pages.0;
498 tx.stylus_pages_ever = pages.1;
499}
500
501#[allow(clippy::too_many_arguments)]
503fn stylus_call_trampoline<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
504 ctx: *mut (),
505 precompile_ctx: *const (),
506 call_type: u8,
507 contract: Address,
508 caller: Address,
509 storage_addr: Address,
510 input: &[u8],
511 gas: u64,
512 value: U256,
513 parent_pages: (u16, u16),
514) -> SubCallResult
515where
516 BlockEnv: revm::context::Block,
517 TxEnv: revm::context::Transaction,
518 CfgEnv: revm::context::Cfg,
519 DB: Database,
520{
521 let context = unsafe {
529 &mut *(ctx as *mut revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>)
530 };
531 let pre_ctx = unsafe { &*(precompile_ctx as *const arb_context::ArbPrecompileCtx) };
532
533 write_tx_pages(pre_ctx, parent_pages);
534
535 let is_static = call_type == 2;
536 let is_delegate = call_type == 1;
537 let is_callcode = call_type == 3;
538
539 struct ActorGuard<'a> {
540 ctx: &'a arb_context::ArbPrecompileCtx,
541 addr: Address,
542 pushed: bool,
543 }
544 impl Drop for ActorGuard<'_> {
545 fn drop(&mut self) {
546 if self.pushed {
547 self.ctx.pop_stylus_program(self.addr);
548 }
549 }
550 }
551 let _actor_guard = if !is_delegate && !is_callcode {
552 pre_ctx.push_stylus_program(storage_addr);
553 ActorGuard {
554 ctx: pre_ctx,
555 addr: storage_addr,
556 pushed: true,
557 }
558 } else {
559 ActorGuard {
560 ctx: pre_ctx,
561 addr: storage_addr,
562 pushed: false,
563 }
564 };
565
566 let checkpoint = context.journaled_state.inner.checkpoint();
567
568 if !is_delegate && !value.is_zero() {
569 let transfer_result = context.journaled_state.inner.transfer(
570 &mut context.journaled_state.database,
571 caller,
572 contract,
573 value,
574 );
575 if matches!(transfer_result, Err(_) | Ok(Some(_))) {
576 context.journaled_state.inner.checkpoint_revert(checkpoint);
577 return SubCallResult {
578 output: Vec::new(),
579 gas_cost: 0,
580 success: false,
581 refund: 0,
582 pages: read_tx_pages(pre_ctx),
583 };
584 }
585 }
586
587 let code_address = contract;
588 let target_address = storage_addr;
589 let _ = is_delegate;
590
591 let call_scheme = match call_type {
592 0 => CallScheme::Call,
593 1 => CallScheme::DelegateCall,
594 2 => CallScheme::StaticCall,
595 _ => CallScheme::Call,
596 };
597
598 let call_value = if is_delegate {
599 revm::interpreter::CallValue::Apparent(value)
600 } else {
601 revm::interpreter::CallValue::Transfer(value)
602 };
603
604 let sub_inputs = CallInputs {
605 input: CallInput::Bytes(input.to_vec().into()),
606 gas_limit: gas,
607 target_address,
608 bytecode_address: code_address,
609 caller,
610 value: call_value,
611 scheme: call_scheme,
612 is_static,
613 return_memory_offset: 0..0,
614 known_bytecode: None,
615 };
616
617 {
618 let spec: revm::primitives::hardfork::SpecId = context.cfg.spec().into();
619 let mut precompiles =
620 alloy_evm::precompiles::PrecompilesMap::from(revm::handler::EthPrecompiles::new(spec));
621 let pre_arc = unsafe {
628 let raw = precompile_ctx as *const arb_context::ArbPrecompileCtx;
629 std::sync::Arc::increment_strong_count(raw);
630 std::sync::Arc::from_raw(raw)
631 };
632 register_arb_precompiles(&mut precompiles, pre_arc.clone());
633 let mut arb_map = ArbPrecompilesMap::new(precompiles, pre_arc);
634 let dispatch_result = <ArbPrecompilesMap as PrecompileProvider<
635 revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
636 >>::run(&mut arb_map, context, &sub_inputs);
637
638 match dispatch_result {
639 Ok(Some(result)) => {
640 let success = result.result.is_ok();
641 let output = result.output.to_vec();
642 let gas_used = if success || matches!(result.result, InstructionResult::Revert) {
648 gas.saturating_sub(result.gas.remaining())
649 } else {
650 gas
651 };
652 let refund = if success { result.gas.refunded() } else { 0 };
653 if success {
654 context.journaled_state.inner.checkpoint_commit();
655 } else {
656 context.journaled_state.inner.checkpoint_revert(checkpoint);
657 }
658 return SubCallResult {
659 output,
660 gas_cost: gas_used,
661 success,
662 refund,
663 pages: read_tx_pages(pre_ctx),
664 };
665 }
666 Ok(None) => {}
667 Err(_) => {
668 context.journaled_state.inner.checkpoint_revert(checkpoint);
669 return SubCallResult {
670 output: Vec::new(),
671 gas_cost: gas,
672 success: false,
673 refund: 0,
674 pages: read_tx_pages(pre_ctx),
675 };
676 }
677 }
678 }
679
680 let bytecode = match context
681 .journaled_state
682 .inner
683 .load_code(&mut context.journaled_state.database, code_address)
684 {
685 Ok(acc) => acc
686 .data
687 .info
688 .code
689 .as_ref()
690 .map(|c| c.original_bytes())
691 .unwrap_or_default(),
692 Err(_) => {
693 context.journaled_state.inner.checkpoint_revert(checkpoint);
694 return SubCallResult {
695 output: Vec::new(),
696 gas_cost: 0,
697 success: false,
698 refund: 0,
699 pages: read_tx_pages(pre_ctx),
700 };
701 }
702 };
703
704 let (bytecode, bytecode_addr) = if bytecode.len() == 23
710 && bytecode[0] == 0xef
711 && bytecode[1] == 0x01
712 && bytecode[2] == 0x00
713 {
714 let delegate = Address::from_slice(&bytecode[3..23]);
715 match context
716 .journaled_state
717 .inner
718 .load_code(&mut context.journaled_state.database, delegate)
719 {
720 Ok(acc) => {
721 let code = acc
722 .data
723 .info
724 .code
725 .as_ref()
726 .map(|c| c.original_bytes())
727 .unwrap_or_default();
728 (code, delegate)
729 }
730 Err(_) => {
731 context.journaled_state.inner.checkpoint_revert(checkpoint);
732 return SubCallResult {
733 output: Vec::new(),
734 gas_cost: 0,
735 success: false,
736 refund: 0,
737 pages: read_tx_pages(pre_ctx),
738 };
739 }
740 }
741 } else {
742 (bytecode, code_address)
743 };
744
745 if bytecode.is_empty() {
746 context.journaled_state.inner.checkpoint_commit();
747 return SubCallResult {
748 output: Vec::new(),
749 gas_cost: 0,
750 success: true,
751 refund: 0,
752 pages: read_tx_pages(pre_ctx),
753 };
754 }
755
756 let sub_inputs = CallInputs {
757 bytecode_address: bytecode_addr,
758 ..sub_inputs
759 };
760
761 if pre_ctx.block.arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS
762 && arb_stylus::is_stylus_runnable(&bytecode)
763 {
764 let arc = unsafe {
769 let raw = precompile_ctx as *const arb_context::ArbPrecompileCtx;
770 std::sync::Arc::increment_strong_count(raw);
771 std::sync::Arc::from_raw(raw)
772 };
773 let result = execute_stylus_program(context, &sub_inputs, &bytecode, &arc);
774 let pages = read_tx_pages(pre_ctx);
775 let success = result.result.is_ok();
776 let output = result.output.to_vec();
777 let gas_used = gas.saturating_sub(result.gas.remaining());
778 let refund = result.gas.refunded();
779 if success {
780 context.journaled_state.inner.checkpoint_commit();
781 } else {
782 context.journaled_state.inner.checkpoint_revert(checkpoint);
783 }
784 return SubCallResult {
785 output,
786 gas_cost: gas_used,
787 success,
788 refund,
789 pages,
790 };
791 }
792
793 let result = run_evm_bytecode(context, &sub_inputs, &bytecode, gas, pre_ctx);
794 let pages = read_tx_pages(pre_ctx);
795 let success = result.result.is_ok();
796 let output = result.output.to_vec();
797 let gas_used = if success || matches!(result.result, InstructionResult::Revert) {
798 gas.saturating_sub(result.gas.remaining())
799 } else {
800 gas
801 };
802 let refund = result.gas.refunded();
803 if success {
804 context.journaled_state.inner.checkpoint_commit();
805 } else {
806 context.journaled_state.inner.checkpoint_revert(checkpoint);
807 }
808 SubCallResult {
809 output,
810 gas_cost: gas_used,
811 success,
812 refund,
813 pages,
814 }
815}
816
817#[allow(clippy::too_many_arguments)]
819fn stylus_create_trampoline<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
820 ctx: *mut (),
821 precompile_ctx: *const (),
822 caller: Address,
823 code: &[u8],
824 gas: u64,
825 endowment: U256,
826 salt: Option<B256>,
827 parent_pages: (u16, u16),
828) -> SubCreateResult
829where
830 BlockEnv: revm::context::Block,
831 TxEnv: revm::context::Transaction,
832 CfgEnv: revm::context::Cfg,
833 DB: Database,
834{
835 let pre_ctx = unsafe { &*(precompile_ctx as *const arb_context::ArbPrecompileCtx) };
840 {
841 let mut tx = pre_ctx.tx.lock();
842 tx.stylus_pages_open = parent_pages.0;
843 tx.stylus_pages_ever = parent_pages.1;
844 }
845 let context = unsafe {
846 &mut *(ctx as *mut revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>)
847 };
848
849 let (caller_nonce, caller_balance) = {
850 let acc = context
851 .journaled_state
852 .inner
853 .load_account(&mut context.journaled_state.database, caller);
854 acc.map(|a| (a.data.info.nonce, a.data.info.balance))
855 .unwrap_or((0, U256::ZERO))
856 };
857
858 if caller_balance < endowment {
859 return SubCreateResult {
860 address: None,
861 output: Vec::new(),
862 gas_cost: 0,
863 pages: read_tx_pages(pre_ctx),
864 };
865 }
866
867 let created_address = if let Some(salt) = salt {
868 let code_hash = alloy_primitives::keccak256(code);
869 let mut buf = Vec::with_capacity(1 + 20 + 32 + 32);
870 buf.push(0xff);
871 buf.extend_from_slice(caller.as_slice());
872 buf.extend_from_slice(salt.as_slice());
873 buf.extend_from_slice(code_hash.as_slice());
874 Address::from_slice(&alloy_primitives::keccak256(&buf)[12..])
875 } else {
876 use alloy_rlp::Encodable;
877 let mut rlp_buf = Vec::with_capacity(64);
878 alloy_rlp::Header {
879 list: true,
880 payload_length: caller.length() + caller_nonce.length(),
881 }
882 .encode(&mut rlp_buf);
883 caller.encode(&mut rlp_buf);
884 caller_nonce.encode(&mut rlp_buf);
885 Address::from_slice(&alloy_primitives::keccak256(&rlp_buf)[12..])
886 };
887
888 struct CreateActorGuard<'a> {
889 ctx: &'a arb_context::ArbPrecompileCtx,
890 addr: Address,
891 }
892 impl Drop for CreateActorGuard<'_> {
893 fn drop(&mut self) {
894 self.ctx.pop_stylus_program(self.addr);
895 }
896 }
897 pre_ctx.push_stylus_program(created_address);
898 let _create_guard = CreateActorGuard {
899 ctx: pre_ctx,
900 addr: created_address,
901 };
902
903 {
904 use revm::context_interface::journaled_state::account::JournaledAccountTr;
905 let bumped = match context
906 .journaled_state
907 .inner
908 .load_account_mut(&mut context.journaled_state.database, caller)
909 {
910 Ok(mut caller_acc) => caller_acc.data.bump_nonce(),
911 Err(_) => false,
912 };
913 if !bumped {
914 return SubCreateResult {
915 address: None,
916 output: Vec::new(),
917 gas_cost: gas,
918 pages: read_tx_pages(pre_ctx),
919 };
920 }
921 }
922
923 if context
924 .journaled_state
925 .inner
926 .load_account(&mut context.journaled_state.database, created_address)
927 .is_err()
928 {
929 return SubCreateResult {
930 address: None,
931 output: Vec::new(),
932 gas_cost: gas,
933 pages: read_tx_pages(pre_ctx),
934 };
935 }
936
937 let spec: revm::primitives::hardfork::SpecId = context.cfg.spec().into();
938 let checkpoint = match context.journaled_state.inner.create_account_checkpoint(
939 caller,
940 created_address,
941 endowment,
942 spec,
943 ) {
944 Ok(cp) => cp,
945 Err(_) => {
946 return SubCreateResult {
947 address: None,
948 output: Vec::new(),
949 gas_cost: gas,
950 pages: read_tx_pages(pre_ctx),
951 };
952 }
953 };
954
955 let init_inputs = CallInputs {
956 input: CallInput::Bytes(Bytes::new()),
957 gas_limit: gas,
958 target_address: created_address,
959 bytecode_address: created_address,
960 caller,
961 value: revm::interpreter::CallValue::Transfer(endowment),
962 scheme: CallScheme::Call,
963 is_static: false,
964 return_memory_offset: 0..0,
965 known_bytecode: None,
966 };
967
968 let result = run_evm_bytecode(context, &init_inputs, code, gas, pre_ctx);
969 let pages = read_tx_pages(pre_ctx);
970 let success = result.result.is_ok();
971
972 if success {
973 let deployed_code = result.output.to_vec();
974 let max_code_size = context.cfg.max_code_size();
975 if deployed_code.len() > max_code_size {
976 context.journaled_state.inner.checkpoint_revert(checkpoint);
977 return SubCreateResult {
978 address: None,
979 output: Vec::new(),
980 gas_cost: gas,
981 pages,
982 };
983 }
984 let is_stylus =
985 arb_stylus::is_stylus_component(&deployed_code, pre_ctx.block.arbos_version);
986 if !deployed_code.is_empty() && deployed_code[0] == 0xEF && !is_stylus {
987 context.journaled_state.inner.checkpoint_revert(checkpoint);
988 return SubCreateResult {
989 address: None,
990 output: Vec::new(),
991 gas_cost: gas,
992 pages,
993 };
994 }
995 let deposit_cost = context
996 .cfg
997 .gas_params()
998 .code_deposit_cost(deployed_code.len());
999 let after_deposit_remaining = result.gas.remaining().saturating_sub(deposit_cost);
1000 if result.gas.remaining() < deposit_cost {
1001 context.journaled_state.inner.checkpoint_revert(checkpoint);
1002 return SubCreateResult {
1003 address: None,
1004 output: Vec::new(),
1005 gas_cost: gas,
1006 pages,
1007 };
1008 }
1009 let gas_used = gas.saturating_sub(after_deposit_remaining);
1010 let code_hash = alloy_primitives::keccak256(&deployed_code);
1011 let bytecode = revm::bytecode::Bytecode::new_raw(deployed_code.into());
1012 let _ = context
1013 .journaled_state
1014 .inner
1015 .load_account(&mut context.journaled_state.database, created_address);
1016 context
1017 .journaled_state
1018 .inner
1019 .set_code_with_hash(created_address, bytecode, code_hash);
1020 context.journaled_state.inner.checkpoint_commit();
1021 SubCreateResult {
1022 address: Some(created_address),
1023 output: Vec::new(),
1024 gas_cost: gas_used,
1025 pages,
1026 }
1027 } else {
1028 let output = result.output.to_vec();
1029 let gas_used = gas.saturating_sub(result.gas.remaining());
1030 context.journaled_state.inner.checkpoint_revert(checkpoint);
1031 SubCreateResult {
1032 address: None,
1033 output,
1034 gas_cost: gas_used,
1035 pages,
1036 }
1037 }
1038}
1039
1040fn run_evm_bytecode<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
1046 context: &mut revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1047 inputs: &CallInputs,
1048 bytecode: &[u8],
1049 gas_limit: u64,
1050 pre_ctx: &arb_context::ArbPrecompileCtx,
1051) -> InterpreterResult
1052where
1053 BlockEnv: revm::context::Block,
1054 TxEnv: revm::context::Transaction,
1055 CfgEnv: revm::context::Cfg,
1056 DB: Database,
1057{
1058 use revm::{
1059 bytecode::Bytecode,
1060 interpreter::{
1061 FrameInput, InterpreterAction, SharedMemory,
1062 interpreter::{ExtBytecode, InputsImpl},
1063 },
1064 };
1065
1066 let code = Bytecode::new_raw(bytecode.to_vec().into());
1067 let ext_bytecode = ExtBytecode::new(code);
1068
1069 let call_value = inputs.value.get();
1070 let interp_input = InputsImpl {
1071 target_address: inputs.target_address,
1072 bytecode_address: Some(inputs.bytecode_address),
1073 caller_address: inputs.caller,
1074 input: inputs.input.clone(),
1075 call_value,
1076 };
1077
1078 let spec = context.cfg.spec();
1079
1080 let mut interpreter = revm::interpreter::Interpreter::new(
1081 SharedMemory::new(),
1082 ext_bytecode,
1083 interp_input,
1084 inputs.is_static,
1085 spec.clone().into(),
1086 gas_limit,
1087 );
1088
1089 type Ctx<B, T, C, D, Ch> = revm::Context<B, T, C, D, revm::Journal<D>, Ch>;
1094 let mut instructions = EthInstructions::<
1095 EthInterpreter,
1096 Ctx<BlockEnv, TxEnv, CfgEnv, DB, Chain>,
1097 >::new_mainnet_with_spec(spec.into());
1098 instructions.insert_instruction(
1099 BLOBBASEFEE_OPCODE,
1100 revm::interpreter::Instruction::new(arb_blob_basefee, 2),
1101 );
1102 instructions.insert_instruction(
1103 SELFDESTRUCT_OPCODE,
1104 revm::interpreter::Instruction::new(arb_selfdestruct, 5000),
1105 );
1106 instructions.insert_instruction(
1107 NUMBER_OPCODE,
1108 revm::interpreter::Instruction::new(arb_number, 2),
1109 );
1110 instructions.insert_instruction(
1111 BLOCKHASH_OPCODE,
1112 revm::interpreter::Instruction::new(arb_blockhash, 20),
1113 );
1114 instructions.insert_instruction(
1115 BALANCE_OPCODE,
1116 revm::interpreter::Instruction::new(arb_balance, 0),
1117 );
1118 instructions.insert_instruction(
1119 SELFBALANCE_OPCODE,
1120 revm::interpreter::Instruction::new(arb_selfbalance, 5),
1121 );
1122
1123 let mut multi_gas_inspector = crate::multi_gas::MultiGasInspector::default();
1129 loop {
1130 let action = revm::inspector::inspect_instructions(
1131 context,
1132 &mut interpreter,
1133 &mut multi_gas_inspector,
1134 &instructions.instruction_table,
1135 );
1136
1137 match action {
1138 InterpreterAction::Return(result) => {
1139 pre_ctx.add_stylus_multi_gas(multi_gas_inspector.take_multi_gas());
1140 return result;
1141 }
1142 InterpreterAction::NewFrame(FrameInput::Call(mut sub_call)) => {
1143 revm::Inspector::call(&mut multi_gas_inspector, context, &mut sub_call);
1144 let resolved_input: Bytes = match &sub_call.input {
1157 revm::interpreter::CallInput::Bytes(b) => b.clone(),
1158 revm::interpreter::CallInput::SharedBuffer(range) if range.is_empty() => {
1161 Bytes::new()
1162 }
1163 revm::interpreter::CallInput::SharedBuffer(range) => {
1164 Bytes::from(
1168 interpreter
1169 .memory
1170 .global_slice_range(range.clone())
1171 .to_vec(),
1172 )
1173 }
1174 };
1175 let bytecode_address = sub_call.bytecode_address;
1176 let parent_pages = read_tx_pages(pre_ctx);
1177 let sub_result = stylus_call_trampoline::<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
1178 context as *mut _ as *mut (),
1179 pre_ctx as *const _ as *const (),
1180 match sub_call.scheme {
1181 CallScheme::Call | CallScheme::CallCode => 0,
1182 CallScheme::DelegateCall => 1,
1183 CallScheme::StaticCall => 2,
1184 },
1185 bytecode_address,
1186 sub_call.caller,
1187 sub_call.target_address,
1188 &resolved_input,
1189 sub_call.gas_limit,
1190 sub_call.value.get(),
1191 parent_pages,
1192 );
1193 write_tx_pages(pre_ctx, sub_result.pages);
1194
1195 let gas_remaining = sub_call.gas_limit.saturating_sub(sub_result.gas_cost);
1196 let ins_result = if sub_result.success {
1197 InstructionResult::Return
1198 } else {
1199 InstructionResult::Revert
1200 };
1201
1202 let output: Bytes = sub_result.output.into();
1203 let returned_len = output.len();
1204 let mem_start = sub_call.return_memory_offset.start;
1205 let mem_length = sub_call.return_memory_offset.len();
1206 let target_len = mem_length.min(returned_len);
1207
1208 interpreter.return_data.set_buffer(output);
1209
1210 let item = if ins_result.is_ok() {
1211 U256::from(1)
1212 } else {
1213 U256::ZERO
1214 };
1215 let _ = interpreter.stack.push(item);
1216
1217 if ins_result.is_ok_or_revert() {
1218 interpreter.gas.erase_cost(gas_remaining);
1219 if target_len > 0 {
1220 interpreter
1221 .memory
1222 .set(mem_start, &interpreter.return_data.buffer()[..target_len]);
1223 }
1224 }
1225
1226 if ins_result.is_ok() {
1227 interpreter.gas.record_refund(sub_result.refund);
1236 }
1237 }
1238 InterpreterAction::NewFrame(FrameInput::Create(mut sub_create)) => {
1239 revm::Inspector::create(&mut multi_gas_inspector, context, &mut sub_create);
1240 let salt = match sub_create.scheme() {
1242 revm::interpreter::CreateScheme::Create2 { salt } => {
1243 Some(B256::from(salt.to_be_bytes()))
1244 }
1245 _ => None,
1246 };
1247
1248 let parent_pages = read_tx_pages(pre_ctx);
1249 let sub_result = stylus_create_trampoline::<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
1250 context as *mut _ as *mut (),
1251 pre_ctx as *const _ as *const (),
1252 sub_create.caller(),
1253 sub_create.init_code(),
1254 sub_create.gas_limit(),
1255 sub_create.value(),
1256 salt,
1257 parent_pages,
1258 );
1259 write_tx_pages(pre_ctx, sub_result.pages);
1260
1261 let gas_remaining = sub_create.gas_limit().saturating_sub(sub_result.gas_cost);
1262 let created_addr = sub_result.address;
1263
1264 let ins_result = if created_addr.is_some() {
1265 InstructionResult::Return
1266 } else if !sub_result.output.is_empty() {
1267 InstructionResult::Revert
1268 } else {
1269 InstructionResult::CreateInitCodeStartingEF00
1270 };
1271
1272 let output: Bytes = sub_result.output.into();
1273 interpreter.return_data.set_buffer(output);
1274
1275 let item = match created_addr {
1277 Some(addr) => addr.into_word().into(),
1278 None => U256::ZERO,
1279 };
1280 let _ = interpreter.stack.push(item);
1281
1282 if ins_result.is_ok_or_revert() {
1283 interpreter.gas.erase_cost(gas_remaining);
1284 }
1285 }
1286 InterpreterAction::NewFrame(FrameInput::Empty) => {
1287 pre_ctx.add_stylus_multi_gas(multi_gas_inspector.take_multi_gas());
1288 return InterpreterResult::new(
1289 InstructionResult::Revert,
1290 Bytes::new(),
1291 EvmGas::new(0),
1292 );
1293 }
1294 }
1295 }
1296}
1297
1298struct StylusFrameGuard<'a> {
1302 ctx: &'a std::sync::Arc<arb_context::ArbPrecompileCtx>,
1303}
1304
1305impl Drop for StylusFrameGuard<'_> {
1306 fn drop(&mut self) {
1307 self.ctx.exit_stylus_frame();
1308 }
1309}
1310
1311fn execute_stylus_program<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
1317 context: &mut revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1318 inputs: &CallInputs,
1319 bytecode: &[u8],
1320 ctx: &std::sync::Arc<arb_context::ArbPrecompileCtx>,
1321) -> InterpreterResult
1322where
1323 BlockEnv: revm::context::Block,
1324 TxEnv: revm::context::Transaction,
1325 CfgEnv: revm::context::Cfg,
1326 DB: Database,
1327{
1328 use arbos::programs::types::UserOutcome;
1329
1330 let stylus_frame_depth = ctx.enter_stylus_frame();
1331 let _stylus_frame_guard = StylusFrameGuard { ctx };
1332
1333 let zero_gas = || EvmGas::new(0);
1334 let write_pages = |open: u16, ever: u16| {
1335 let mut tx = ctx.tx.lock();
1336 tx.stylus_pages_open = open;
1337 tx.stylus_pages_ever = ever;
1338 };
1339 let (parent_open, parent_ever) = {
1340 let tx = ctx.tx.lock();
1341 (tx.stylus_pages_open, tx.stylus_pages_ever)
1342 };
1343
1344 let code_hash = alloy_primitives::keccak256(bytecode);
1345 let arbos_version = ctx.block.arbos_version;
1346 let block_timestamp = ctx.block.block_timestamp;
1347
1348 let params_sto = programs_params_storage();
1349 let params = {
1350 let mut backend = JournalBackend::new(&mut context.journaled_state);
1351 match StylusParams::load(arbos_version, ¶ms_sto, &mut backend) {
1352 Ok(p) => p,
1353 Err(e) => {
1354 tracing::warn!(target: "stylus", err = %e, "failed to load StylusParams from storage");
1355 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1356 }
1357 }
1358 };
1359
1360 let program_word = match read_program_word(&mut context.journaled_state, code_hash) {
1361 Some(w) => w,
1362 None => {
1363 tracing::warn!(target: "stylus", codehash = %code_hash, "failed to read program data");
1364 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1365 }
1366 };
1367 let program = Program::from_storage(program_word, block_timestamp);
1368
1369 if program.version == 0 || program.version != params.version {
1370 tracing::warn!(target: "stylus", codehash = %code_hash, program_ver = program.version, params_ver = params.version, "program version mismatch");
1371 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1372 }
1373 let expiry_seconds = (params.expiry_days as u64) * 24 * 3600;
1374 if program.age_seconds > expiry_seconds {
1375 tracing::warn!(target: "stylus", codehash = %code_hash, "program expired");
1376 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1377 }
1378
1379 let recent_wasms_hit = if arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_60 {
1380 ctx.block.insert_recent_wasm(code_hash)
1381 } else {
1382 false
1383 };
1384 let effective_cached = program.cached || recent_wasms_hit;
1385 let effective_program = if effective_cached != program.cached {
1386 let mut p = program;
1387 p.cached = effective_cached;
1388 p
1389 } else {
1390 program
1391 };
1392 let upfront_cost = stylus_call_gas_cost(
1393 ¶ms,
1394 &effective_program,
1395 parent_open,
1396 parent_ever,
1397 arbos_version,
1398 );
1399 let total_gas = inputs.gas_limit;
1400
1401 if total_gas < upfront_cost {
1402 if stylus_frame_depth == 1 {
1405 ctx.add_stylus_upfront_oog_gas(total_gas);
1406 }
1407 return InterpreterResult::new(InstructionResult::OutOfGas, Bytes::new(), zero_gas());
1408 }
1409 let gas_for_wasm = total_gas - upfront_cost;
1410
1411 let stylus_config = StylusConfig::new(params.version, params.max_stack_depth, params.ink_price);
1412
1413 let target_addr = inputs.target_address;
1414 let reentrant = ctx.stylus_program_count(target_addr) > 1;
1415
1416 let module_hash =
1417 read_module_hash(&mut context.journaled_state, code_hash).unwrap_or(code_hash);
1418
1419 let mut evm_data = build_evm_data(context, inputs, ctx);
1420 evm_data.reentrant = reentrant as u32;
1421 evm_data.cached = effective_program.cached;
1422 evm_data.module_hash = module_hash;
1423
1424 let start_open = parent_open.saturating_add(program.footprint);
1425 let start_ever = parent_ever.max(start_open);
1426
1427 let journal_ptr = &mut context.journaled_state as *mut revm::Journal<DB>;
1428 let is_static = inputs.is_static || matches!(inputs.scheme, CallScheme::StaticCall);
1429 let ctx_ptr = context as *mut _ as *mut ();
1430 let precompile_ctx_ptr = std::sync::Arc::as_ptr(ctx) as *const ();
1431 let caller = inputs.caller;
1432 let call_value = inputs.value.get();
1433 let evm_api = unsafe {
1440 StylusEvmApi::new(
1441 journal_ptr,
1442 target_addr,
1443 caller,
1444 call_value,
1445 is_static,
1446 arbos_version,
1447 ctx_ptr,
1448 precompile_ctx_ptr,
1449 Some(stylus_call_trampoline::<BlockEnv, TxEnv, CfgEnv, DB, Chain>),
1450 Some(stylus_create_trampoline::<BlockEnv, TxEnv, CfgEnv, DB, Chain>),
1451 )
1452 };
1453
1454 let long_term_tag = if program.cached { 1u32 } else { 0u32 };
1457 let (module, store) = match arb_stylus::cache::InitCache::get(
1458 module_hash,
1459 params.version,
1460 long_term_tag,
1461 false,
1462 ) {
1463 Some(loaded) => loaded,
1464 None => {
1465 let decompressed_result = if arb_stylus::is_stylus_root(bytecode) {
1469 arb_stylus::get_wasm_from_root(
1470 bytecode,
1471 params.max_wasm_size,
1472 params.max_fragment_count,
1473 false,
1474 |addr| {
1475 let db = &mut context.journaled_state.database;
1476 let info = db
1477 .basic(addr)
1478 .map_err(|e| arb_stylus::StylusError::Backend(format!("{e:?}")))?
1479 .unwrap_or_default();
1480 let code = match info.code {
1481 Some(c) => c,
1482 None => db
1483 .code_by_hash(info.code_hash)
1484 .map_err(|e| arb_stylus::StylusError::Backend(format!("{e:?}")))?,
1485 };
1486 Ok(code.original_bytes().to_vec())
1487 },
1488 )
1489 } else {
1490 arb_stylus::decompress_wasm(bytecode)
1491 };
1492 let decompressed = match decompressed_result {
1493 Ok(w) => w,
1494 Err(arb_stylus::StylusError::Backend(e)) => {
1497 tracing::error!(target: "stylus", codehash = %code_hash, err = %e, "fragment read failed");
1498 write_pages(parent_open, start_ever);
1499 return InterpreterResult::new(
1500 InstructionResult::FatalExternalError,
1501 Bytes::new(),
1502 zero_gas(),
1503 );
1504 }
1505 Err(e) => {
1506 tracing::warn!(target: "stylus", codehash = %code_hash, err = %e, "WASM decompression failed");
1507 write_pages(parent_open, start_ever);
1508 return InterpreterResult::new(
1509 InstructionResult::Revert,
1510 Bytes::new(),
1511 zero_gas(),
1512 );
1513 }
1514 };
1515 let serialized = match arb_stylus::compile_module(&decompressed, params.version, false)
1516 {
1517 Ok(s) => s,
1518 Err(e) => {
1519 tracing::warn!(target: "stylus", codehash = %code_hash, err = %e, "failed to compile WASM");
1520 write_pages(parent_open, start_ever);
1521 return InterpreterResult::new(
1522 InstructionResult::Revert,
1523 Bytes::new(),
1524 zero_gas(),
1525 );
1526 }
1527 };
1528 match arb_stylus::cache::InitCache::insert(
1529 module_hash,
1530 &serialized,
1531 params.version,
1532 long_term_tag,
1533 false,
1534 ) {
1535 Ok(loaded) => loaded,
1536 Err(e) => {
1537 tracing::warn!(target: "stylus", codehash = %code_hash, err = %e, "failed to load compiled module");
1538 write_pages(parent_open, start_ever);
1539 return InterpreterResult::new(
1540 InstructionResult::Revert,
1541 Bytes::new(),
1542 zero_gas(),
1543 );
1544 }
1545 }
1546 }
1547 };
1548
1549 let compile = match arb_stylus::CompileConfig::version(params.version, false) {
1550 Ok(c) => c,
1551 Err(e) => {
1552 tracing::warn!(target: "stylus", codehash = %code_hash, err = %e, "unsupported Stylus version");
1553 write_pages(parent_open, start_ever);
1554 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1555 }
1556 };
1557 let mut env = arb_stylus::env::WasmEnv::new(compile, Some(stylus_config), evm_api, evm_data);
1558 env.set_pages(
1559 start_open,
1560 start_ever,
1561 params.free_pages,
1562 params.page_gas,
1563 params.page_limit,
1564 arbos_version,
1565 );
1566 let mut instance = match arb_stylus::NativeInstance::from_module(module, store, env) {
1567 Ok(inst) => inst,
1568 Err(e) => {
1569 tracing::warn!(target: "stylus", codehash = %code_hash, err = %e, "failed to build instance");
1570 write_pages(parent_open, start_ever);
1571 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1572 }
1573 };
1574
1575 write_pages(start_open, start_ever);
1576
1577 let ink = stylus_config.pricing.gas_to_ink(StylusGas(gas_for_wasm));
1578
1579 let calldata_owned: Bytes = inputs.input.bytes(context);
1580 let calldata: &[u8] = &calldata_owned;
1581 let outcome = match instance.run_main(calldata, stylus_config, ink) {
1582 Ok(outcome) => outcome,
1583 Err(e) => {
1584 tracing::warn!(target: "stylus", codehash = %code_hash, err = %e, "WASM execution failed");
1585 let final_ever = instance.env().pages_ever.max(start_ever);
1586 write_pages(parent_open, final_ever);
1587 return InterpreterResult::new(InstructionResult::Revert, Bytes::new(), zero_gas());
1588 }
1589 };
1590
1591 let final_ever = instance.env().pages_ever.max(start_ever);
1592 write_pages(parent_open, final_ever);
1593
1594 let ink_left = match instance.ink_left() {
1595 arb_stylus::MachineMeter::Ready(ink_val) => ink_val,
1596 arb_stylus::MachineMeter::Exhausted => arb_stylus::Ink(0),
1597 };
1598 let gas_left = stylus_config.pricing.ink_to_gas(ink_left).0;
1599
1600 let output: Bytes = instance.env().outs.clone().into();
1601 let gas_left = if !output.is_empty()
1602 && arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS_FIXES
1603 {
1604 let evm_cost = arbos::programs::types::evm_memory_cost(output.len() as u64);
1605 if total_gas < evm_cost {
1606 0
1607 } else {
1608 gas_left.min(total_gas - evm_cost)
1609 }
1610 } else {
1611 gas_left
1612 };
1613
1614 let mut gas_result = EvmGas::new(gas_left);
1615 let sstore_refund = instance.env().evm_api.sstore_refund();
1616 if sstore_refund != 0 {
1617 gas_result.record_refund(sstore_refund);
1618 }
1619
1620 let consumed_gas_left = match outcome {
1621 UserOutcome::OutOfInk | UserOutcome::OutOfStack => 0,
1622 _ => gas_left,
1623 };
1624 let sub_call_gas = instance.env().evm_api.sub_call_gas();
1627 let mut program_multi_gas = instance.env().evm_api.multi_gas();
1628 arbos::programs::attribute_wasm_computation(
1629 &mut program_multi_gas,
1630 total_gas.saturating_sub(sub_call_gas),
1631 consumed_gas_left,
1632 );
1633 ctx.add_stylus_multi_gas(program_multi_gas);
1634
1635 match outcome {
1636 UserOutcome::Success => {
1637 InterpreterResult::new(InstructionResult::Return, output, gas_result)
1638 }
1639 UserOutcome::Revert => {
1640 InterpreterResult::new(InstructionResult::Revert, output, gas_result)
1641 }
1642 UserOutcome::OutOfInk => {
1643 InterpreterResult::new(InstructionResult::OutOfGas, Bytes::new(), zero_gas())
1644 }
1645 UserOutcome::OutOfStack => {
1646 InterpreterResult::new(InstructionResult::CallTooDeep, Bytes::new(), zero_gas())
1647 }
1648 UserOutcome::Failure => {
1649 InterpreterResult::new(InstructionResult::Revert, Bytes::new(), gas_result)
1650 }
1651 }
1652}
1653
1654fn build_evm_data<BlockEnv, TxEnv, CfgEnv, DB, Chain>(
1656 context: &revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1657 inputs: &CallInputs,
1658 ctx: &arb_context::ArbPrecompileCtx,
1659) -> EvmData
1660where
1661 BlockEnv: revm::context::Block,
1662 TxEnv: revm::context::Transaction,
1663 CfgEnv: revm::context::Cfg,
1664 DB: Database,
1665{
1666 let basefee = U256::from(context.block.basefee());
1667 let stashed = ctx.tx_snapshot().effective_gas_price;
1670 let gas_price = if stashed != 0 {
1671 U256::from(stashed)
1672 } else {
1673 U256::from(context.tx.gas_price())
1674 };
1675 let value = inputs.value.get();
1676
1677 EvmData {
1678 arbos_version: ctx.block.arbos_version,
1679 block_basefee: B256::from(basefee.to_be_bytes()),
1680 chain_id: context.cfg.chain_id(),
1681 block_coinbase: context.block.beneficiary(),
1682 block_gas_limit: context.block.gas_limit(),
1683 block_number: ctx.block.l1_block_number_for_evm,
1684 block_timestamp: context.block.timestamp().saturating_to(),
1685 contract_address: inputs.target_address,
1686 module_hash: alloy_primitives::keccak256(b""),
1687 msg_sender: inputs.caller,
1688 msg_value: B256::from(value.to_be_bytes()),
1689 tx_gas_price: B256::from(gas_price.to_be_bytes()),
1690 tx_origin: context.tx.caller(),
1691 reentrant: 0,
1692 cached: false,
1693 tracing: false,
1694 }
1695}
1696
1697fn is_stylus_call(frame_init: &FrameInit, arbos_version: u64) -> Option<Bytes> {
1701 if arbos_version < arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS {
1702 return None;
1703 }
1704 if let FrameInput::Call(ref inputs) = frame_init.frame_input
1705 && let Some((_, ref code)) = inputs.known_bytecode
1706 {
1707 let raw = code.original_bytes();
1708 if arb_stylus::is_stylus_runnable(&raw) {
1709 return Some(raw);
1710 }
1711 }
1712 None
1713}
1714
1715fn execute_stylus_call_concrete<DB: Database>(
1718 ctx: &mut EthEvmContext<DB>,
1719 inputs: &CallInputs,
1720 bytecode: &[u8],
1721 checkpoint: revm::context_interface::journaled_state::JournalCheckpoint,
1722 pre_ctx: &std::sync::Arc<arb_context::ArbPrecompileCtx>,
1723) -> FrameResult {
1724 if let revm::interpreter::CallValue::Transfer(value) = inputs.value
1726 && let Some(i) =
1727 ctx.journal_mut()
1728 .transfer_loaded(inputs.caller, inputs.target_address, value)
1729 {
1730 let gas = EvmGas::new(inputs.gas_limit);
1731 ctx.journaled_state.inner.checkpoint_revert(checkpoint);
1732 return FrameResult::Call(CallOutcome {
1733 result: InterpreterResult::new(i.into(), Bytes::new(), gas),
1734 memory_offset: inputs.return_memory_offset.clone(),
1735 was_precompile_called: false,
1736 precompile_call_logs: Vec::new(),
1737 });
1738 }
1739
1740 let result = execute_stylus_program(ctx, inputs, bytecode, pre_ctx);
1741
1742 if result.result.is_ok() {
1743 ctx.journaled_state.inner.checkpoint_commit();
1744 } else {
1745 ctx.journaled_state.inner.checkpoint_revert(checkpoint);
1746 }
1747 FrameResult::Call(CallOutcome {
1748 result,
1749 memory_offset: inputs.return_memory_offset.clone(),
1750 was_precompile_called: false,
1751 precompile_call_logs: Vec::new(),
1752 })
1753}
1754
1755#[derive(Clone, Debug)]
1758pub struct ArbPrecompilesMap {
1759 pub inner: PrecompilesMap,
1760 pub ctx: std::sync::Arc<arb_context::ArbPrecompileCtx>,
1763}
1764
1765impl ArbPrecompilesMap {
1766 pub fn new(inner: PrecompilesMap, ctx: std::sync::Arc<arb_context::ArbPrecompileCtx>) -> Self {
1767 Self { inner, ctx }
1768 }
1769}
1770
1771impl<BlockEnv, TxEnv, CfgEnv, DB, Chain>
1772 PrecompileProvider<revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>>
1773 for ArbPrecompilesMap
1774where
1775 BlockEnv: revm::context::Block,
1776 TxEnv: revm::context::Transaction,
1777 CfgEnv: revm::context::Cfg,
1778 DB: Database,
1779{
1780 type Output = InterpreterResult;
1781
1782 fn set_spec(&mut self, spec: CfgEnv::Spec) -> bool {
1783 <PrecompilesMap as PrecompileProvider<
1784 revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1785 >>::set_spec(&mut self.inner, spec)
1786 }
1787
1788 fn run(
1791 &mut self,
1792 context: &mut revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1793 inputs: &CallInputs,
1794 ) -> Result<Option<Self::Output>, String> {
1795 self.ctx.set_evm_depth(context.journaled_state.inner.depth);
1796
1797 if let result @ Some(_) = <PrecompilesMap as PrecompileProvider<
1799 revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1800 >>::run(&mut self.inner, context, inputs)?
1801 {
1802 return Ok(result);
1803 }
1804
1805 let arbos_version = self.ctx.block.arbos_version;
1807 if arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS {
1808 let bytecode = inputs
1811 .known_bytecode
1812 .as_ref()
1813 .map(|(_, code)| code.original_bytes())
1814 .or_else(|| {
1815 context
1816 .journaled_state
1817 .inner
1818 .load_code(
1819 &mut context.journaled_state.database,
1820 inputs.bytecode_address,
1821 )
1822 .ok()
1823 .and_then(|acc| acc.data.info.code.as_ref().map(|c| c.original_bytes()))
1824 });
1825
1826 if let Some(bytecode) = bytecode
1827 && arb_stylus::is_stylus_runnable(&bytecode)
1828 {
1829 return Ok(Some(execute_stylus_program(
1830 context, inputs, &bytecode, &self.ctx,
1831 )));
1832 }
1833 }
1834
1835 Ok(None)
1836 }
1837
1838 fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
1839 <PrecompilesMap as PrecompileProvider<
1840 revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1841 >>::warm_addresses(&self.inner)
1842 }
1843
1844 fn contains(&self, address: &Address) -> bool {
1845 <PrecompilesMap as PrecompileProvider<
1846 revm::Context<BlockEnv, TxEnv, CfgEnv, DB, revm::Journal<DB>, Chain>,
1847 >>::contains(&self.inner, address)
1848 }
1849}
1850
1851type InnerRevmEvm<DB, I> = RevmEvm<
1854 EthEvmContext<DB>,
1855 I,
1856 EthInstructions<EthInterpreter, EthEvmContext<DB>>,
1857 ArbPrecompilesMap,
1858 EthFrame,
1859>;
1860
1861struct CreateFrameCtx {
1862 caller: Address,
1863 checkpoint: revm::context_interface::journaled_state::JournalCheckpoint,
1864}
1865
1866pub struct ArbEvm<DB: Database, I> {
1871 inner: InnerRevmEvm<DB, I>,
1872 inspect: bool,
1873 create_ctx_stack: Vec<CreateFrameCtx>,
1874 actor_stack: Vec<Option<Address>>,
1875}
1876
1877impl<DB, I> ArbEvm<DB, I>
1878where
1879 DB: Database,
1880{
1881 pub fn new(inner: InnerRevmEvm<DB, I>, inspect: bool) -> Self {
1882 Self {
1883 inner,
1884 inspect,
1885 create_ctx_stack: Vec::new(),
1886 actor_stack: Vec::new(),
1887 }
1888 }
1889
1890 pub fn into_inner(self) -> InnerRevmEvm<DB, I> {
1891 self.inner
1892 }
1893
1894 pub fn ctx(&self) -> &EthEvmContext<DB> {
1895 &self.inner.ctx
1896 }
1897
1898 pub fn ctx_mut(&mut self) -> &mut EthEvmContext<DB> {
1899 &mut self.inner.ctx
1900 }
1901
1902 pub fn precompiles_mut(&mut self) -> &mut ArbPrecompilesMap {
1903 &mut self.inner.precompiles
1904 }
1905}
1906
1907pub trait ArbEvmFactoryStaged {
1912 fn staged_precompile_ctx(&self) -> Option<std::sync::Arc<arb_context::ArbPrecompileCtx>>;
1913}
1914
1915impl ArbEvmFactoryStaged for ArbEvmFactory {
1916 fn staged_precompile_ctx(&self) -> Option<std::sync::Arc<arb_context::ArbPrecompileCtx>> {
1917 self.staged()
1918 }
1919}
1920
1921impl<DB: Database, I> core::ops::Deref for ArbEvm<DB, I> {
1922 type Target = EthEvmContext<DB>;
1923 fn deref(&self) -> &Self::Target {
1924 &self.inner.ctx
1925 }
1926}
1927
1928impl<DB: Database, I> core::ops::DerefMut for ArbEvm<DB, I> {
1929 fn deref_mut(&mut self) -> &mut Self::Target {
1930 &mut self.inner.ctx
1931 }
1932}
1933
1934impl<DB, I> EvmTr for ArbEvm<DB, I>
1937where
1938 DB: Database,
1939 I: Inspector<EthEvmContext<DB>, EthInterpreter>,
1940{
1941 type Context = EthEvmContext<DB>;
1942 type Instructions = EthInstructions<EthInterpreter, EthEvmContext<DB>>;
1943 type Precompiles = ArbPrecompilesMap;
1944 type Frame = EthFrame<EthInterpreter>;
1945
1946 #[inline]
1947 fn all(
1948 &self,
1949 ) -> (
1950 &Self::Context,
1951 &Self::Instructions,
1952 &Self::Precompiles,
1953 &FrameStack<Self::Frame>,
1954 ) {
1955 self.inner.all()
1956 }
1957
1958 #[inline]
1959 fn all_mut(
1960 &mut self,
1961 ) -> (
1962 &mut Self::Context,
1963 &mut Self::Instructions,
1964 &mut Self::Precompiles,
1965 &mut FrameStack<Self::Frame>,
1966 ) {
1967 self.inner.all_mut()
1968 }
1969
1970 #[inline]
1971 fn frame_init(
1972 &mut self,
1973 frame_input: FrameInit,
1974 ) -> Result<
1975 ItemOrResult<&mut Self::Frame, FrameResult>,
1976 revm::handler::evm::ContextDbError<Self::Context>,
1977 > {
1978 let pre_ctx = self.inner.precompiles.ctx.clone();
1979 let pushed_caller = match &frame_input.frame_input {
1980 FrameInput::Call(inputs) => {
1981 pre_ctx.push_caller(inputs.caller);
1982 true
1983 }
1984 FrameInput::Create(inputs) => {
1985 pre_ctx.push_caller(inputs.caller());
1986 true
1987 }
1988 _ => false,
1989 };
1990
1991 match &frame_input.frame_input {
1992 FrameInput::Call(inputs)
1993 if !matches!(
1994 inputs.scheme,
1995 CallScheme::DelegateCall | CallScheme::CallCode
1996 ) =>
1997 {
1998 pre_ctx.push_stylus_program(inputs.target_address);
1999 self.actor_stack.push(Some(inputs.target_address));
2000 }
2001 FrameInput::Call(_) => {
2002 self.actor_stack.push(None);
2003 }
2004 FrameInput::Create(_) => {
2005 self.actor_stack.push(None);
2006 }
2007 _ => {}
2008 }
2009
2010 if let FrameInput::Create(inputs) = &frame_input.frame_input {
2011 let cp = self.inner.ctx.journal_mut().checkpoint();
2012 self.create_ctx_stack.push(CreateFrameCtx {
2013 caller: inputs.caller(),
2014 checkpoint: cp,
2015 });
2016 }
2017
2018 if let Some(bytecode) = is_stylus_call(&frame_input, pre_ctx.block.arbos_version)
2019 && let FrameInput::Call(ref inputs) = frame_input.frame_input
2020 {
2021 if frame_input.depth > revm::primitives::constants::CALL_STACK_LIMIT as usize {
2022 let gas = EvmGas::new(inputs.gas_limit);
2023 if pushed_caller {
2024 pre_ctx.pop_caller();
2025 }
2026 return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
2027 result: InterpreterResult::new(
2028 InstructionResult::CallTooDeep,
2029 Bytes::new(),
2030 gas,
2031 ),
2032 memory_offset: inputs.return_memory_offset.clone(),
2033 was_precompile_called: false,
2034 precompile_call_logs: Vec::new(),
2035 })));
2036 }
2037 let checkpoint = self.inner.ctx.journal_mut().checkpoint();
2038 let result = execute_stylus_call_concrete(
2039 &mut self.inner.ctx,
2040 inputs,
2041 &bytecode,
2042 checkpoint,
2043 &pre_ctx,
2044 );
2045 if pushed_caller {
2046 pre_ctx.pop_caller();
2047 }
2048 return Ok(ItemOrResult::Result(result));
2049 }
2050
2051 self.inner.frame_init(frame_input)
2052 }
2053
2054 #[inline]
2055 fn frame_run(
2056 &mut self,
2057 ) -> Result<
2058 ItemOrResult<FrameInit, FrameResult>,
2059 revm::handler::evm::ContextDbError<Self::Context>,
2060 > {
2061 self.inner.frame_run()
2062 }
2063
2064 #[inline]
2065 fn frame_return_result(
2066 &mut self,
2067 mut result: FrameResult,
2068 ) -> Result<Option<FrameResult>, revm::handler::evm::ContextDbError<Self::Context>> {
2069 let pre_ctx = self.inner.precompiles.ctx.clone();
2070 pre_ctx.pop_caller();
2071
2072 if let Some(Some(addr)) = self.actor_stack.pop() {
2073 pre_ctx.pop_stylus_program(addr);
2074 }
2075
2076 if let FrameResult::Create(ref mut outcome) = result {
2080 let create_ctx = self.create_ctx_stack.pop();
2081 if outcome.instruction_result().is_ok()
2082 && let Some(addr) = outcome.address
2083 {
2084 let code_bytes: Vec<u8> = self
2085 .inner
2086 .ctx
2087 .journal_mut()
2088 .code(addr)
2089 .map(|c| c.data.to_vec())
2090 .unwrap_or_default();
2091 let starts_with_ef = code_bytes.first() == Some(&0xEF);
2092 let is_stylus =
2093 arb_stylus::is_stylus_component(&code_bytes, pre_ctx.block.arbos_version);
2094 if starts_with_ef && !is_stylus {
2095 if let Some(create_ctx) = create_ctx {
2096 self.inner
2097 .ctx
2098 .journal_mut()
2099 .checkpoint_revert(create_ctx.checkpoint);
2100 use revm::context_interface::journaled_state::account::JournaledAccountTr;
2101 if let Ok(mut caller_acc) = self
2102 .inner
2103 .ctx
2104 .journal_mut()
2105 .load_account_mut(create_ctx.caller)
2106 {
2107 let _ = caller_acc.data.bump_nonce();
2108 }
2109 }
2110 outcome.address = None;
2111 outcome.result.result = InstructionResult::CreateContractStartingWithEF;
2112 outcome.result.output = Bytes::new();
2113 outcome.result.gas.spend_all();
2114 }
2115 }
2116 }
2117 self.inner.frame_return_result(result)
2118 }
2119}
2120
2121impl<DB, I> ExecuteEvm for ArbEvm<DB, I>
2124where
2125 DB: Database,
2126 I: Inspector<EthEvmContext<DB>, EthInterpreter>,
2127{
2128 type ExecutionResult = ExecutionResult<HaltReason>;
2129 type State = revm::state::EvmState;
2130 type Error = EVMError<<DB as revm::Database>::Error, InvalidTransaction>;
2131 type Tx = revm::context::TxEnv;
2132 type Block = revm::context::BlockEnv;
2133
2134 #[inline]
2135 fn transact_one(&mut self, tx: Self::Tx) -> Result<Self::ExecutionResult, Self::Error> {
2136 self.inner.ctx.set_tx(tx);
2137 MainnetHandler::default().run(self)
2138 }
2139
2140 #[inline]
2141 fn finalize(&mut self) -> Self::State {
2142 self.inner.ctx.journal_mut().finalize()
2143 }
2144
2145 #[inline]
2146 fn set_block(&mut self, block: Self::Block) {
2147 self.inner.ctx.set_block(block);
2148 }
2149
2150 #[inline]
2151 fn replay(&mut self) -> Result<ResultAndState<HaltReason>, Self::Error> {
2152 MainnetHandler::default().run(self).map(|result| {
2153 let state = self.finalize();
2154 ResultAndState::new(result, state)
2155 })
2156 }
2157}
2158
2159impl<DB, I> SystemCallEvm for ArbEvm<DB, I>
2162where
2163 DB: Database,
2164 I: Inspector<EthEvmContext<DB>, EthInterpreter>,
2165{
2166 fn system_call_one_with_caller(
2167 &mut self,
2168 caller: Address,
2169 system_contract_address: Address,
2170 data: Bytes,
2171 ) -> Result<Self::ExecutionResult, Self::Error> {
2172 use revm::handler::system_call::SystemCallTx;
2173 self.inner
2174 .ctx
2175 .set_tx(revm::context::TxEnv::new_system_tx_with_caller(
2176 caller,
2177 system_contract_address,
2178 data,
2179 ));
2180 MainnetHandler::default().run_system_call(self)
2181 }
2182}
2183
2184impl<DB, I> revm::inspector::InspectorEvmTr for ArbEvm<DB, I>
2187where
2188 DB: Database,
2189 I: Inspector<EthEvmContext<DB>, EthInterpreter>,
2190 revm::Journal<DB>: revm::inspector::JournalExt,
2191{
2192 type Inspector = I;
2193
2194 fn all_inspector(
2195 &self,
2196 ) -> (
2197 &Self::Context,
2198 &Self::Instructions,
2199 &Self::Precompiles,
2200 &FrameStack<Self::Frame>,
2201 &Self::Inspector,
2202 ) {
2203 let (ctx, inst, pre, fs) = self.inner.all();
2204 (ctx, inst, pre, fs, &self.inner.inspector)
2205 }
2206
2207 fn all_mut_inspector(
2208 &mut self,
2209 ) -> (
2210 &mut Self::Context,
2211 &mut Self::Instructions,
2212 &mut Self::Precompiles,
2213 &mut FrameStack<Self::Frame>,
2214 &mut Self::Inspector,
2215 ) {
2216 (
2217 &mut self.inner.ctx,
2218 &mut self.inner.instruction,
2219 &mut self.inner.precompiles,
2220 &mut self.inner.frame_stack,
2221 &mut self.inner.inspector,
2222 )
2223 }
2224}
2225
2226impl<DB, I> InspectEvm for ArbEvm<DB, I>
2229where
2230 DB: Database,
2231 I: Inspector<EthEvmContext<DB>, EthInterpreter>,
2232 revm::Journal<DB>: revm::inspector::JournalExt,
2233{
2234 type Inspector = I;
2235
2236 fn set_inspector(&mut self, inspector: Self::Inspector) {
2237 self.inner.inspector = inspector;
2238 }
2239
2240 fn inspect_one_tx(&mut self, tx: Self::Tx) -> Result<Self::ExecutionResult, Self::Error> {
2241 self.inner.ctx.set_tx(tx);
2242 MainnetHandler::default().inspect_run(self)
2243 }
2244}
2245
2246impl<DB, I> Evm for ArbEvm<DB, I>
2249where
2250 DB: Database,
2251 I: Inspector<EthEvmContext<DB>, EthInterpreter>,
2252 revm::Journal<DB>: revm::inspector::JournalExt,
2253{
2254 type DB = DB;
2255 type Tx = ArbTransaction;
2256 type Error = EVMError<<DB as revm::Database>::Error>;
2257 type HaltReason = HaltReason;
2258 type Spec = SpecId;
2259 type Precompiles = PrecompilesMap;
2260 type Inspector = I;
2261 type BlockEnv = revm::context::BlockEnv;
2262
2263 fn block(&self) -> &revm::context::BlockEnv {
2264 &self.inner.ctx.block
2265 }
2266
2267 fn chain_id(&self) -> u64 {
2268 self.inner.ctx.cfg.chain_id
2269 }
2270
2271 fn transact_raw(
2272 &mut self,
2273 tx: Self::Tx,
2274 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
2275 if self.inspect {
2276 InspectEvm::inspect_tx(self, tx.into_inner())
2277 } else {
2278 ExecuteEvm::transact(self, tx.into_inner())
2279 }
2280 }
2281
2282 fn transact_system_call(
2283 &mut self,
2284 caller: Address,
2285 contract: Address,
2286 data: Bytes,
2287 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
2288 SystemCallEvm::system_call_with_caller(self, caller, contract, data)
2289 }
2290
2291 fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>) {
2292 let revm::Context {
2293 block: block_env,
2294 cfg: cfg_env,
2295 journaled_state,
2296 ..
2297 } = self.inner.ctx;
2298 (journaled_state.database, EvmEnv { block_env, cfg_env })
2299 }
2300
2301 fn set_inspector_enabled(&mut self, enabled: bool) {
2302 self.inspect = enabled;
2303 }
2304
2305 fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) {
2306 (
2307 &self.inner.ctx.journaled_state.database,
2308 &self.inner.inspector,
2309 &self.inner.precompiles.inner,
2310 )
2311 }
2312
2313 fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) {
2314 (
2315 &mut self.inner.ctx.journaled_state.database,
2316 &mut self.inner.inspector,
2317 &mut self.inner.precompiles.inner,
2318 )
2319 }
2320}
2321
2322#[derive(Default, Debug)]
2331pub struct ArbEvmFactory {
2332 pub inner: alloy_evm::EthEvmFactory,
2333 staged_ctx:
2334 std::sync::Arc<parking_lot::RwLock<Option<std::sync::Arc<arb_context::ArbPrecompileCtx>>>>,
2335 chain_caches: std::sync::Arc<arb_context::ChainCaches>,
2336 isolate_staging_on_clone: bool,
2341}
2342
2343impl Clone for ArbEvmFactory {
2344 fn clone(&self) -> Self {
2345 let isolate = self.isolate_staging_on_clone;
2346 Self {
2347 inner: self.inner,
2348 staged_ctx: if isolate {
2349 std::sync::Arc::default()
2350 } else {
2351 self.staged_ctx.clone()
2352 },
2353 chain_caches: if isolate {
2354 std::sync::Arc::default()
2355 } else {
2356 self.chain_caches.clone()
2357 },
2358 isolate_staging_on_clone: isolate,
2359 }
2360 }
2361}
2362
2363impl ArbEvmFactory {
2364 pub fn new() -> Self {
2365 Self::default()
2366 }
2367
2368 pub fn isolated() -> Self {
2371 Self {
2372 isolate_staging_on_clone: true,
2373 ..Self::default()
2374 }
2375 }
2376
2377 pub fn stage_ctx(&self, ctx: std::sync::Arc<arb_context::ArbPrecompileCtx>) {
2380 *self.staged_ctx.write() = Some(ctx);
2381 }
2382
2383 pub fn chain_caches(&self) -> &std::sync::Arc<arb_context::ChainCaches> {
2384 &self.chain_caches
2385 }
2386
2387 fn staged(&self) -> Option<std::sync::Arc<arb_context::ArbPrecompileCtx>> {
2388 self.staged_ctx.read().clone()
2389 }
2390}
2391
2392fn build_arb_evm<DB: Database, I>(
2393 inner: RevmEvm<
2394 EthEvmContext<DB>,
2395 I,
2396 EthInstructions<EthInterpreter, EthEvmContext<DB>>,
2397 PrecompilesMap,
2398 EthFrame,
2399 >,
2400 staged: Option<std::sync::Arc<arb_context::ArbPrecompileCtx>>,
2401 inspect: bool,
2402) -> ArbEvm<DB, I> {
2403 let pre_ctx =
2404 staged.unwrap_or_else(|| std::sync::Arc::new(arb_context::ArbPrecompileCtx::default()));
2405 let RevmEvm {
2406 ctx: evm_ctx,
2407 inspector,
2408 mut instruction,
2409 mut precompiles,
2410 frame_stack: _,
2411 } = inner;
2412
2413 instruction.insert_instruction(
2414 BLOBBASEFEE_OPCODE,
2415 revm::interpreter::Instruction::new(arb_blob_basefee, 2),
2416 );
2417 instruction.insert_instruction(
2418 SELFDESTRUCT_OPCODE,
2419 revm::interpreter::Instruction::new(arb_selfdestruct, 5000),
2420 );
2421 instruction.insert_instruction(
2422 NUMBER_OPCODE,
2423 revm::interpreter::Instruction::new(arb_number, 2),
2424 );
2425 instruction.insert_instruction(
2426 BLOCKHASH_OPCODE,
2427 revm::interpreter::Instruction::new(arb_blockhash, 20),
2428 );
2429 instruction.insert_instruction(
2430 BALANCE_OPCODE,
2431 revm::interpreter::Instruction::new(arb_balance, 0),
2432 );
2433 instruction.insert_instruction(
2434 SELFBALANCE_OPCODE,
2435 revm::interpreter::Instruction::new(arb_selfbalance, 5),
2436 );
2437 register_arb_precompiles(&mut precompiles, pre_ctx.clone());
2438 let arb_precompiles = ArbPrecompilesMap::new(precompiles, pre_ctx);
2439
2440 let revm_evm = RevmEvm::new_with_inspector(evm_ctx, inspector, instruction, arb_precompiles);
2441 ArbEvm::new(revm_evm, inspect)
2442}
2443
2444impl EvmFactory for ArbEvmFactory {
2445 type Evm<DB: Database, I: Inspector<EthEvmContext<DB>, EthInterpreter>> = ArbEvm<DB, I>;
2446 type Context<DB: Database> = EthEvmContext<DB>;
2447 type Tx = ArbTransaction;
2448 type Error<DBError: core::error::Error + Send + Sync + 'static> = EVMError<DBError>;
2449 type HaltReason = HaltReason;
2450 type Spec = SpecId;
2451 type Precompiles = PrecompilesMap;
2452 type BlockEnv = revm::context::BlockEnv;
2453
2454 fn create_evm<DB: Database>(
2455 &self,
2456 db: DB,
2457 input: EvmEnv<Self::Spec>,
2458 ) -> Self::Evm<DB, NoOpInspector> {
2459 let eth_evm = self.inner.create_evm(db, input);
2460 build_arb_evm(eth_evm.into_inner(), self.staged(), false)
2461 }
2462
2463 fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>, EthInterpreter>>(
2464 &self,
2465 db: DB,
2466 input: EvmEnv<Self::Spec>,
2467 inspector: I,
2468 ) -> Self::Evm<DB, I> {
2469 let eth_evm = self.inner.create_evm_with_inspector(db, input, inspector);
2470 build_arb_evm(eth_evm.into_inner(), self.staged(), true)
2471 }
2472}
2473
2474#[cfg(test)]
2475mod tests {
2476 use std::sync::Arc;
2477
2478 use super::ArbEvmFactory;
2479
2480 #[test]
2481 fn isolated_clone_has_independent_staging_and_caches() {
2482 let factory = ArbEvmFactory::isolated();
2483 let clone = factory.clone();
2484 factory.stage_ctx(Arc::new(arb_context::ArbPrecompileCtx::default()));
2485 assert!(factory.staged().is_some());
2486 assert!(clone.staged().is_none());
2487 assert!(!Arc::ptr_eq(factory.chain_caches(), clone.chain_caches()));
2488 }
2489
2490 #[test]
2491 fn default_clone_shares_staging_and_caches() {
2492 let factory = ArbEvmFactory::new();
2493 let clone = factory.clone();
2494 factory.stage_ctx(Arc::new(arb_context::ArbPrecompileCtx::default()));
2495 assert!(clone.staged().is_some());
2496 assert!(Arc::ptr_eq(factory.chain_caches(), clone.chain_caches()));
2497 }
2498}