aurora_evm/runtime/
mod.rs

1//! Runtime layer for EVM.
2
3#[cfg(not(feature = "std"))]
4pub mod prelude {
5    pub use alloc::{rc::Rc, vec::Vec};
6}
7#[cfg(feature = "std")]
8pub mod prelude {
9    pub use std::{rc::Rc, vec::Vec};
10}
11
12#[cfg(feature = "tracing")]
13pub mod tracing;
14
15#[cfg(feature = "tracing")]
16macro_rules! event {
17    ($x:expr) => {
18        use crate::runtime::tracing::Event::*;
19        crate::runtime::tracing::with(|listener| listener.event($x));
20    };
21}
22
23#[cfg(not(feature = "tracing"))]
24macro_rules! event {
25    ($x:expr) => {};
26}
27
28mod context;
29mod eval;
30mod handler;
31mod interrupt;
32
33pub use crate::core::*;
34
35pub use self::context::{CallScheme, Context, CreateScheme};
36pub use self::handler::{Handler, Transfer};
37pub use self::interrupt::{Resolve, ResolveCall, ResolveCreate};
38
39use prelude::*;
40use primitive_types::H160;
41
42macro_rules! step {
43	( $self:expr, $handler:expr, $return:tt $($err:path)?; $($ok:path)? ) => ({
44		let result = $self.machine.step($handler, &$self.context.address);
45		match result {
46			Ok(()) => $($ok)?(()),
47			Err(Capture::Exit(e)) => {
48				$self.status = Err(e.clone());
49				#[allow(unused_parens)]
50				$return $($err)*(Capture::Exit(e))
51			},
52			Err(Capture::Trap(opcode)) => {
53				match eval::eval($self, opcode, $handler) {
54					eval::Control::Continue => $($ok)?(()),
55					eval::Control::CallInterrupt(interrupt) => {
56						let resolve = ResolveCall::new($self);
57						#[allow(unused_parens)]
58						$return $($err)*(Capture::Trap(Resolve::Call(interrupt, resolve)))
59					},
60					eval::Control::CreateInterrupt(interrupt) => {
61						let resolve = ResolveCreate::new($self);
62						#[allow(unused_parens)]
63						$return $($err)*(Capture::Trap(Resolve::Create(interrupt, resolve)))
64					},
65					eval::Control::Exit(exit) => {
66						$self.machine.exit(exit.clone().into());
67						$self.status = Err(exit.clone());
68						#[allow(unused_parens)]
69						$return $($err)*(Capture::Exit(exit))
70					},
71				}
72			},
73		}
74	});
75}
76
77/// EVM runtime.
78///
79/// The runtime wraps an EVM `Machine` with support of return data and context.
80pub struct Runtime {
81    machine: Machine,
82    status: Result<(), ExitReason>,
83    return_data_buffer: Vec<u8>,
84    return_data_len: usize,
85    return_data_offset: usize,
86    context: Context,
87}
88
89impl Runtime {
90    /// Create a new runtime with given code and data.
91    #[must_use]
92    pub fn new(
93        code: Rc<Vec<u8>>,
94        data: Rc<Vec<u8>>,
95        context: Context,
96        stack_limit: usize,
97        memory_limit: usize,
98    ) -> Self {
99        Self {
100            machine: Machine::new(code, data, stack_limit, memory_limit),
101            status: Ok(()),
102            return_data_buffer: Vec::new(),
103            return_data_len: 0,
104            return_data_offset: 0,
105            context,
106        }
107    }
108
109    /// Get a reference to the machine.
110    #[must_use]
111    pub const fn machine(&self) -> &Machine {
112        &self.machine
113    }
114
115    /// Get a reference to the execution context.
116    #[must_use]
117    pub const fn context(&self) -> &Context {
118        &self.context
119    }
120
121    /// Step the runtime.
122    ///
123    /// # Errors
124    /// Return `ExitReason`, `Resolve`
125    pub fn step<H: Handler + InterpreterHandler>(
126        &mut self,
127        handler: &mut H,
128    ) -> Result<(), Capture<ExitReason, Resolve<H>>> {
129        step!(self, handler, return Err; Ok)
130    }
131
132    /// Loop stepping the runtime until it stops.
133    pub fn run<H: Handler + InterpreterHandler>(
134        &mut self,
135        handler: &mut H,
136    ) -> Capture<ExitReason, Resolve<H>> {
137        loop {
138            step!(self, handler, return;);
139        }
140    }
141
142    /// # Errors
143    /// Return `ExitReason`
144    pub fn finish_create(
145        &mut self,
146        reason: ExitReason,
147        address: Option<H160>,
148        return_data: Vec<u8>,
149    ) -> Result<(), ExitReason> {
150        eval::finish_create(self, reason, address, return_data)
151    }
152
153    /// # Errors
154    /// Return `ExitReason`
155    pub fn finish_call(
156        &mut self,
157        reason: ExitReason,
158        return_data: Vec<u8>,
159    ) -> Result<(), ExitReason> {
160        eval::finish_call(
161            self,
162            self.return_data_len,
163            self.return_data_offset,
164            reason,
165            return_data,
166        )
167    }
168}
169
170/// Runtime configuration.
171#[allow(clippy::struct_excessive_bools)]
172#[derive(Clone, Debug)]
173pub struct Config {
174    /// Gas paid for extcode.
175    pub gas_ext_code: u64,
176    /// Gas paid for extcodehash.
177    pub gas_ext_code_hash: u64,
178    /// Gas paid for sstore set.
179    pub gas_sstore_set: u64,
180    /// Gas paid for sstore reset.
181    pub gas_sstore_reset: u64,
182    /// Gas paid for sstore refund.
183    pub refund_sstore_clears: i64,
184    /// EIP-3529
185    pub max_refund_quotient: u64,
186    /// Gas paid for BALANCE opcode.
187    pub gas_balance: u64,
188    /// Gas paid for SLOAD opcode.
189    pub gas_sload: u64,
190    /// Gas paid for cold SLOAD opcode.
191    pub gas_sload_cold: u64,
192    /// Gas paid for SUICIDE opcode.
193    pub gas_suicide: u64,
194    /// Gas paid for SUICIDE opcode when it hits a new account.
195    pub gas_suicide_new_account: u64,
196    /// Gas paid for CALL opcode.
197    pub gas_call: u64,
198    /// Gas paid for EXP opcode for every byte.
199    pub gas_expbyte: u64,
200    /// Gas paid for a contract creation transaction.
201    pub gas_transaction_create: u64,
202    /// Gas paid for a message call transaction.
203    pub gas_transaction_call: u64,
204    /// Gas paid for zero data in a transaction.
205    pub gas_transaction_zero_data: u64,
206    /// Gas paid for non-zero data in a transaction.
207    pub gas_transaction_non_zero_data: u64,
208    /// Gas paid per address in transaction access list (see EIP-2930).
209    pub gas_access_list_address: u64,
210    /// Gas paid per storage key in transaction access list (see EIP-2930).
211    pub gas_access_list_storage_key: u64,
212    /// Gas paid for accessing cold account.
213    pub gas_account_access_cold: u64,
214    /// Gas paid for accessing ready storage.
215    pub gas_storage_read_warm: u64,
216    /// EIP-1283.
217    pub sstore_gas_metering: bool,
218    /// EIP-1706.
219    pub sstore_revert_under_stipend: bool,
220    /// EIP-2929
221    pub increase_state_access_gas: bool,
222    /// EIP-3529
223    pub decrease_clears_refund: bool,
224    /// EIP-3541
225    pub disallow_executable_format: bool,
226    /// EIP-3651
227    pub warm_coinbase_address: bool,
228    /// Whether to throw out of gas error when
229    /// CALL/CALLCODE/DELEGATECALL requires more than maximum amount
230    /// of gas.
231    pub err_on_call_with_more_gas: bool,
232    /// Take l64 for callcreate after gas.
233    pub call_l64_after_gas: bool,
234    /// Whether empty account is considered exists.
235    pub empty_considered_exists: bool,
236    /// Whether create transactions and create opcode increases nonce by one.
237    pub create_increase_nonce: bool,
238    /// Stack limit.
239    pub stack_limit: usize,
240    /// Memory limit.
241    pub memory_limit: usize,
242    /// Call limit.
243    pub call_stack_limit: usize,
244    /// Create contract limit.
245    pub create_contract_limit: Option<usize>,
246    /// EIP-3860, maximum size limit of `init_code`.
247    pub max_initcode_size: Option<usize>,
248    /// Call stipend.
249    pub call_stipend: u64,
250    /// Has delegate call.
251    pub has_delegate_call: bool,
252    /// Has create2.
253    pub has_create2: bool,
254    /// Has revert.
255    pub has_revert: bool,
256    /// Has return data.
257    pub has_return_data: bool,
258    /// Has bitwise shifting.
259    pub has_bitwise_shifting: bool,
260    /// Has chain ID.
261    pub has_chain_id: bool,
262    /// Has self balance.
263    pub has_self_balance: bool,
264    /// Has ext code hash.
265    pub has_ext_code_hash: bool,
266    /// Has ext block fee. See [EIP-3198](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3198.md)
267    pub has_base_fee: bool,
268    /// Has PUSH0 opcode. See [EIP-3855](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3855.md)
269    pub has_push0: bool,
270    /// Whether the gasometer is running in estimate mode.
271    pub estimate: bool,
272    /// Has BLOBBASEFEE. See [EIP-7516](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7516.md)
273    pub has_blob_base_fee: bool,
274    /// Has Shard Blob Transactions. See [EIP-4844](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md)
275    pub has_shard_blob_transactions: bool,
276    /// Has Transient storage. See [EIP-1153](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1153.md)
277    pub has_transient_storage: bool,
278    /// Has MCOPY - Memory copying instruction. See [EIP-5656](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-5656.md)
279    pub has_mcopy: bool,
280    /// SELFDESTRUCT restriction: EIP-6780
281    pub has_restricted_selfdestruct: bool,
282    /// EIP-7702
283    pub has_authorization_list: bool,
284    /// EIP-7702
285    pub gas_per_empty_account_cost: u64,
286    /// EIP-7702
287    pub gas_per_auth_base_cost: u64,
288    /// EIP-7623
289    pub has_floor_gas: bool,
290    /// EIP-7623
291    pub total_cost_floor_per_token: u64,
292}
293
294impl Config {
295    /// Frontier hard fork configuration.
296    #[must_use]
297    pub const fn frontier() -> Self {
298        Self {
299            gas_ext_code: 20,
300            gas_ext_code_hash: 20,
301            gas_balance: 20,
302            gas_sload: 50,
303            gas_sload_cold: 0,
304            gas_sstore_set: 20000,
305            gas_sstore_reset: 5000,
306            refund_sstore_clears: 15000,
307            max_refund_quotient: 2,
308            gas_suicide: 0,
309            gas_suicide_new_account: 0,
310            gas_call: 40,
311            gas_expbyte: 10,
312            gas_transaction_create: 21000,
313            gas_transaction_call: 21000,
314            gas_transaction_zero_data: 4,
315            gas_transaction_non_zero_data: 68,
316            gas_access_list_address: 0,
317            gas_access_list_storage_key: 0,
318            gas_account_access_cold: 0,
319            gas_storage_read_warm: 0,
320            sstore_gas_metering: false,
321            sstore_revert_under_stipend: false,
322            increase_state_access_gas: false,
323            decrease_clears_refund: false,
324            disallow_executable_format: false,
325            warm_coinbase_address: false,
326            err_on_call_with_more_gas: true,
327            empty_considered_exists: true,
328            create_increase_nonce: false,
329            call_l64_after_gas: false,
330            stack_limit: 1024,
331            memory_limit: usize::MAX,
332            call_stack_limit: 1024,
333            create_contract_limit: None,
334            max_initcode_size: None,
335            call_stipend: 2300,
336            has_delegate_call: false,
337            has_create2: false,
338            has_revert: false,
339            has_return_data: false,
340            has_bitwise_shifting: false,
341            has_chain_id: false,
342            has_self_balance: false,
343            has_ext_code_hash: false,
344            has_base_fee: false,
345            has_push0: false,
346            estimate: false,
347            has_blob_base_fee: false,
348            has_shard_blob_transactions: false,
349            has_transient_storage: false,
350            has_mcopy: false,
351            has_restricted_selfdestruct: false,
352            has_authorization_list: false,
353            gas_per_empty_account_cost: 0,
354            gas_per_auth_base_cost: 0,
355            has_floor_gas: false,
356            total_cost_floor_per_token: 0,
357        }
358    }
359
360    /// Istanbul hard fork configuration.
361    #[must_use]
362    pub const fn istanbul() -> Self {
363        Self {
364            gas_ext_code: 700,
365            gas_ext_code_hash: 700,
366            gas_balance: 700,
367            gas_sload: 800,
368            gas_sload_cold: 0,
369            gas_sstore_set: 20000,
370            gas_sstore_reset: 5000,
371            refund_sstore_clears: 15000,
372            max_refund_quotient: 2,
373            gas_suicide: 5000,
374            gas_suicide_new_account: 25000,
375            gas_call: 700,
376            gas_expbyte: 50,
377            gas_transaction_create: 53000,
378            gas_transaction_call: 21000,
379            gas_transaction_zero_data: 4,
380            gas_transaction_non_zero_data: 16,
381            gas_access_list_address: 0,
382            gas_access_list_storage_key: 0,
383            gas_account_access_cold: 0,
384            gas_storage_read_warm: 0,
385            sstore_gas_metering: true,
386            sstore_revert_under_stipend: true,
387            increase_state_access_gas: false,
388            decrease_clears_refund: false,
389            disallow_executable_format: false,
390            warm_coinbase_address: false,
391            err_on_call_with_more_gas: false,
392            empty_considered_exists: false,
393            create_increase_nonce: true,
394            call_l64_after_gas: true,
395            stack_limit: 1024,
396            memory_limit: usize::MAX,
397            call_stack_limit: 1024,
398            create_contract_limit: Some(0x6000),
399            max_initcode_size: None,
400            call_stipend: 2300,
401            has_delegate_call: true,
402            has_create2: true,
403            has_revert: true,
404            has_return_data: true,
405            has_bitwise_shifting: true,
406            has_chain_id: true,
407            has_self_balance: true,
408            has_ext_code_hash: true,
409            has_base_fee: false,
410            has_push0: false,
411            estimate: false,
412            has_blob_base_fee: false,
413            has_shard_blob_transactions: false,
414            has_transient_storage: false,
415            has_mcopy: false,
416            has_restricted_selfdestruct: false,
417            has_authorization_list: false,
418            gas_per_auth_base_cost: 0,
419            gas_per_empty_account_cost: 0,
420            has_floor_gas: false,
421            total_cost_floor_per_token: 0,
422        }
423    }
424
425    /// Berlin hard fork configuration.
426    #[must_use]
427    pub const fn berlin() -> Self {
428        Self::config_with_derived_values(DerivedConfigInputs::berlin())
429    }
430
431    /// london hard fork configuration.
432    #[must_use]
433    pub const fn london() -> Self {
434        Self::config_with_derived_values(DerivedConfigInputs::london())
435    }
436
437    /// The Merge (Paris) hard fork configuration.
438    #[must_use]
439    pub const fn merge() -> Self {
440        Self::config_with_derived_values(DerivedConfigInputs::merge())
441    }
442
443    /// Shanghai hard fork configuration.
444    #[must_use]
445    pub const fn shanghai() -> Self {
446        Self::config_with_derived_values(DerivedConfigInputs::shanghai())
447    }
448
449    /// Cancun hard fork configuration.
450    #[must_use]
451    pub const fn cancun() -> Self {
452        Self::config_with_derived_values(DerivedConfigInputs::cancun())
453    }
454
455    /// Prague hard fork configuration.
456    #[must_use]
457    pub const fn prague() -> Self {
458        Self::config_with_derived_values(DerivedConfigInputs::prague())
459    }
460
461    const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Self {
462        let DerivedConfigInputs {
463            gas_storage_read_warm,
464            gas_sload_cold,
465            gas_access_list_storage_key,
466            decrease_clears_refund,
467            has_base_fee,
468            has_push0,
469            disallow_executable_format,
470            warm_coinbase_address,
471            max_initcode_size,
472            has_blob_base_fee,
473            has_shard_blob_transactions,
474            has_transient_storage,
475            has_mcopy,
476            has_restricted_selfdestruct,
477            has_authorization_list,
478            gas_per_empty_account_cost,
479            gas_per_auth_base_cost,
480            has_floor_gas,
481            total_cost_floor_per_token,
482        } = inputs;
483
484        // See https://eips.ethereum.org/EIPS/eip-2929
485        let gas_sload = gas_storage_read_warm;
486        let gas_sstore_reset = 5000 - gas_sload_cold;
487
488        // In that particular case allow unsigned casting to signed as it can't be more than `i64::MAX`.
489        #[allow(clippy::as_conversions, clippy::cast_possible_wrap)]
490        // See https://eips.ethereum.org/EIPS/eip-3529
491        let refund_sstore_clears = if decrease_clears_refund {
492            (gas_sstore_reset + gas_access_list_storage_key) as i64
493        } else {
494            15000
495        };
496        let max_refund_quotient = if decrease_clears_refund { 5 } else { 2 };
497
498        Self {
499            gas_ext_code: 0,
500            gas_ext_code_hash: 0,
501            gas_balance: 0,
502            gas_sload,
503            gas_sload_cold,
504            gas_sstore_set: 20000,
505            gas_sstore_reset,
506            refund_sstore_clears,
507            max_refund_quotient,
508            gas_suicide: 5000,
509            gas_suicide_new_account: 25000,
510            gas_call: 0,
511            gas_expbyte: 50,
512            gas_transaction_create: 53000,
513            gas_transaction_call: 21000,
514            gas_transaction_zero_data: 4,
515            gas_transaction_non_zero_data: 16,
516            gas_access_list_address: 2400,
517            gas_access_list_storage_key,
518            gas_account_access_cold: 2600,
519            gas_storage_read_warm,
520            sstore_gas_metering: true,
521            sstore_revert_under_stipend: true,
522            increase_state_access_gas: true,
523            decrease_clears_refund,
524            disallow_executable_format,
525            warm_coinbase_address,
526            err_on_call_with_more_gas: false,
527            empty_considered_exists: false,
528            create_increase_nonce: true,
529            call_l64_after_gas: true,
530            stack_limit: 1024,
531            memory_limit: usize::MAX,
532            call_stack_limit: 1024,
533            create_contract_limit: Some(0x6000),
534            max_initcode_size,
535            call_stipend: 2300,
536            has_delegate_call: true,
537            has_create2: true,
538            has_revert: true,
539            has_return_data: true,
540            has_bitwise_shifting: true,
541            has_chain_id: true,
542            has_self_balance: true,
543            has_ext_code_hash: true,
544            has_base_fee,
545            has_push0,
546            estimate: false,
547            has_blob_base_fee,
548            has_shard_blob_transactions,
549            has_transient_storage,
550            has_mcopy,
551            has_restricted_selfdestruct,
552            has_authorization_list,
553            gas_per_empty_account_cost,
554            gas_per_auth_base_cost,
555            has_floor_gas,
556            total_cost_floor_per_token,
557        }
558    }
559}
560
561/// Independent inputs that are used to derive other config values.
562/// See `Config::config_with_derived_values` implementation for details.
563#[allow(clippy::struct_excessive_bools)]
564#[derive(Debug, Copy, Clone, Eq, PartialEq)]
565struct DerivedConfigInputs {
566    gas_storage_read_warm: u64,
567    gas_sload_cold: u64,
568    gas_access_list_storage_key: u64,
569    decrease_clears_refund: bool,
570    has_base_fee: bool,
571    has_push0: bool,
572    disallow_executable_format: bool,
573    warm_coinbase_address: bool,
574    max_initcode_size: Option<usize>,
575    has_blob_base_fee: bool,
576    has_shard_blob_transactions: bool,
577    has_transient_storage: bool,
578    has_mcopy: bool,
579    has_restricted_selfdestruct: bool,
580    has_authorization_list: bool,
581    gas_per_empty_account_cost: u64,
582    gas_per_auth_base_cost: u64,
583    has_floor_gas: bool,
584    total_cost_floor_per_token: u64,
585}
586
587impl DerivedConfigInputs {
588    const fn berlin() -> Self {
589        Self {
590            gas_storage_read_warm: 100,
591            gas_sload_cold: 2100,
592            gas_access_list_storage_key: 1900,
593            decrease_clears_refund: false,
594            has_base_fee: false,
595            has_push0: false,
596            disallow_executable_format: false,
597            warm_coinbase_address: false,
598            max_initcode_size: None,
599            has_blob_base_fee: false,
600            has_shard_blob_transactions: false,
601            has_transient_storage: false,
602            has_mcopy: false,
603            has_restricted_selfdestruct: false,
604            has_authorization_list: false,
605            gas_per_auth_base_cost: 0,
606            gas_per_empty_account_cost: 0,
607            has_floor_gas: false,
608            total_cost_floor_per_token: 0,
609        }
610    }
611
612    const fn london() -> Self {
613        Self {
614            gas_storage_read_warm: 100,
615            gas_sload_cold: 2100,
616            gas_access_list_storage_key: 1900,
617            decrease_clears_refund: true,
618            has_base_fee: true,
619            has_push0: false,
620            disallow_executable_format: true,
621            warm_coinbase_address: false,
622            max_initcode_size: None,
623            has_blob_base_fee: false,
624            has_shard_blob_transactions: false,
625            has_transient_storage: false,
626            has_mcopy: false,
627            has_restricted_selfdestruct: false,
628            has_authorization_list: false,
629            gas_per_auth_base_cost: 0,
630            gas_per_empty_account_cost: 0,
631            has_floor_gas: false,
632            total_cost_floor_per_token: 0,
633        }
634    }
635
636    const fn merge() -> Self {
637        Self {
638            gas_storage_read_warm: 100,
639            gas_sload_cold: 2100,
640            gas_access_list_storage_key: 1900,
641            decrease_clears_refund: true,
642            has_base_fee: true,
643            has_push0: false,
644            disallow_executable_format: true,
645            warm_coinbase_address: false,
646            max_initcode_size: None,
647            has_blob_base_fee: false,
648            has_shard_blob_transactions: false,
649            has_transient_storage: false,
650            has_mcopy: false,
651            has_restricted_selfdestruct: false,
652            has_authorization_list: false,
653            gas_per_auth_base_cost: 0,
654            gas_per_empty_account_cost: 0,
655            has_floor_gas: false,
656            total_cost_floor_per_token: 0,
657        }
658    }
659
660    const fn shanghai() -> Self {
661        Self {
662            gas_storage_read_warm: 100,
663            gas_sload_cold: 2100,
664            gas_access_list_storage_key: 1900,
665            decrease_clears_refund: true,
666            has_base_fee: true,
667            has_push0: true,
668            disallow_executable_format: true,
669            warm_coinbase_address: true,
670            // 2 * 24576 as per EIP-3860
671            max_initcode_size: Some(0xC000),
672            has_blob_base_fee: false,
673            has_shard_blob_transactions: false,
674            has_transient_storage: false,
675            has_mcopy: false,
676            has_restricted_selfdestruct: false,
677            has_authorization_list: false,
678            gas_per_auth_base_cost: 0,
679            gas_per_empty_account_cost: 0,
680            has_floor_gas: false,
681            total_cost_floor_per_token: 0,
682        }
683    }
684
685    const fn cancun() -> Self {
686        let mut config = Self::shanghai();
687        config.has_blob_base_fee = true;
688        config.has_shard_blob_transactions = true;
689        config.has_transient_storage = true;
690        config.has_mcopy = true;
691        config.has_restricted_selfdestruct = true;
692        config
693    }
694
695    const fn prague() -> Self {
696        let mut config = Self::cancun();
697        config.has_authorization_list = true;
698        config.gas_per_empty_account_cost = 25000;
699        config.gas_per_auth_base_cost = 12500;
700        config.has_floor_gas = true;
701        config.total_cost_floor_per_token = 10;
702        config
703    }
704}