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 context::{CallScheme, Context, CreateScheme};
36pub use handler::{Handler, Transfer};
37pub use 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, 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}
283
284impl Config {
285    /// Frontier hard fork configuration.
286    #[must_use]
287    pub const fn frontier() -> Self {
288        Self {
289            gas_ext_code: 20,
290            gas_ext_code_hash: 20,
291            gas_balance: 20,
292            gas_sload: 50,
293            gas_sload_cold: 0,
294            gas_sstore_set: 20000,
295            gas_sstore_reset: 5000,
296            refund_sstore_clears: 15000,
297            max_refund_quotient: 2,
298            gas_suicide: 0,
299            gas_suicide_new_account: 0,
300            gas_call: 40,
301            gas_expbyte: 10,
302            gas_transaction_create: 21000,
303            gas_transaction_call: 21000,
304            gas_transaction_zero_data: 4,
305            gas_transaction_non_zero_data: 68,
306            gas_access_list_address: 0,
307            gas_access_list_storage_key: 0,
308            gas_account_access_cold: 0,
309            gas_storage_read_warm: 0,
310            sstore_gas_metering: false,
311            sstore_revert_under_stipend: false,
312            increase_state_access_gas: false,
313            decrease_clears_refund: false,
314            disallow_executable_format: false,
315            warm_coinbase_address: false,
316            err_on_call_with_more_gas: true,
317            empty_considered_exists: true,
318            create_increase_nonce: false,
319            call_l64_after_gas: false,
320            stack_limit: 1024,
321            memory_limit: usize::MAX,
322            call_stack_limit: 1024,
323            create_contract_limit: None,
324            max_initcode_size: None,
325            call_stipend: 2300,
326            has_delegate_call: false,
327            has_create2: false,
328            has_revert: false,
329            has_return_data: false,
330            has_bitwise_shifting: false,
331            has_chain_id: false,
332            has_self_balance: false,
333            has_ext_code_hash: false,
334            has_base_fee: false,
335            has_push0: false,
336            estimate: false,
337            has_blob_base_fee: false,
338            has_shard_blob_transactions: false,
339            has_transient_storage: false,
340            has_mcopy: false,
341            has_restricted_selfdestruct: false,
342        }
343    }
344
345    /// Istanbul hard fork configuration.
346    #[must_use]
347    pub const fn istanbul() -> Self {
348        Self {
349            gas_ext_code: 700,
350            gas_ext_code_hash: 700,
351            gas_balance: 700,
352            gas_sload: 800,
353            gas_sload_cold: 0,
354            gas_sstore_set: 20000,
355            gas_sstore_reset: 5000,
356            refund_sstore_clears: 15000,
357            max_refund_quotient: 2,
358            gas_suicide: 5000,
359            gas_suicide_new_account: 25000,
360            gas_call: 700,
361            gas_expbyte: 50,
362            gas_transaction_create: 53000,
363            gas_transaction_call: 21000,
364            gas_transaction_zero_data: 4,
365            gas_transaction_non_zero_data: 16,
366            gas_access_list_address: 0,
367            gas_access_list_storage_key: 0,
368            gas_account_access_cold: 0,
369            gas_storage_read_warm: 0,
370            sstore_gas_metering: true,
371            sstore_revert_under_stipend: true,
372            increase_state_access_gas: false,
373            decrease_clears_refund: false,
374            disallow_executable_format: false,
375            warm_coinbase_address: false,
376            err_on_call_with_more_gas: false,
377            empty_considered_exists: false,
378            create_increase_nonce: true,
379            call_l64_after_gas: true,
380            stack_limit: 1024,
381            memory_limit: usize::MAX,
382            call_stack_limit: 1024,
383            create_contract_limit: Some(0x6000),
384            max_initcode_size: None,
385            call_stipend: 2300,
386            has_delegate_call: true,
387            has_create2: true,
388            has_revert: true,
389            has_return_data: true,
390            has_bitwise_shifting: true,
391            has_chain_id: true,
392            has_self_balance: true,
393            has_ext_code_hash: true,
394            has_base_fee: false,
395            has_push0: false,
396            estimate: false,
397            has_blob_base_fee: false,
398            has_shard_blob_transactions: false,
399            has_transient_storage: false,
400            has_mcopy: false,
401            has_restricted_selfdestruct: false,
402        }
403    }
404
405    /// Berlin hard fork configuration.
406    #[must_use]
407    pub const fn berlin() -> Self {
408        Self::config_with_derived_values(DerivedConfigInputs::berlin())
409    }
410
411    /// london hard fork configuration.
412    #[must_use]
413    pub const fn london() -> Self {
414        Self::config_with_derived_values(DerivedConfigInputs::london())
415    }
416
417    /// The Merge (Paris) hard fork configuration.
418    #[must_use]
419    pub const fn merge() -> Self {
420        Self::config_with_derived_values(DerivedConfigInputs::merge())
421    }
422
423    /// Shanghai hard fork configuration.
424    #[must_use]
425    pub const fn shanghai() -> Self {
426        Self::config_with_derived_values(DerivedConfigInputs::shanghai())
427    }
428
429    /// Cancun hard fork configuration.
430    #[must_use]
431    pub const fn cancun() -> Self {
432        Self::config_with_derived_values(DerivedConfigInputs::cancun())
433    }
434
435    const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Self {
436        let DerivedConfigInputs {
437            gas_storage_read_warm,
438            gas_sload_cold,
439            gas_access_list_storage_key,
440            decrease_clears_refund,
441            has_base_fee,
442            has_push0,
443            disallow_executable_format,
444            warm_coinbase_address,
445            max_initcode_size,
446            has_blob_base_fee,
447            has_shard_blob_transactions,
448            has_transient_storage,
449            has_mcopy,
450            has_restricted_selfdestruct,
451        } = inputs;
452
453        // See https://eips.ethereum.org/EIPS/eip-2929
454        let gas_sload = gas_storage_read_warm;
455        let gas_sstore_reset = 5000 - gas_sload_cold;
456
457        // In that particular case allow unsigned casting to signed as it can't be more than `i64::MAX`.
458        #[allow(clippy::as_conversions, clippy::cast_possible_wrap)]
459        // See https://eips.ethereum.org/EIPS/eip-3529
460        let refund_sstore_clears = if decrease_clears_refund {
461            (gas_sstore_reset + gas_access_list_storage_key) as i64
462        } else {
463            15000
464        };
465        let max_refund_quotient = if decrease_clears_refund { 5 } else { 2 };
466
467        Self {
468            gas_ext_code: 0,
469            gas_ext_code_hash: 0,
470            gas_balance: 0,
471            gas_sload,
472            gas_sload_cold,
473            gas_sstore_set: 20000,
474            gas_sstore_reset,
475            refund_sstore_clears,
476            max_refund_quotient,
477            gas_suicide: 5000,
478            gas_suicide_new_account: 25000,
479            gas_call: 0,
480            gas_expbyte: 50,
481            gas_transaction_create: 53000,
482            gas_transaction_call: 21000,
483            gas_transaction_zero_data: 4,
484            gas_transaction_non_zero_data: 16,
485            gas_access_list_address: 2400,
486            gas_access_list_storage_key,
487            gas_account_access_cold: 2600,
488            gas_storage_read_warm,
489            sstore_gas_metering: true,
490            sstore_revert_under_stipend: true,
491            increase_state_access_gas: true,
492            decrease_clears_refund,
493            disallow_executable_format,
494            warm_coinbase_address,
495            err_on_call_with_more_gas: false,
496            empty_considered_exists: false,
497            create_increase_nonce: true,
498            call_l64_after_gas: true,
499            stack_limit: 1024,
500            memory_limit: usize::MAX,
501            call_stack_limit: 1024,
502            create_contract_limit: Some(0x6000),
503            max_initcode_size,
504            call_stipend: 2300,
505            has_delegate_call: true,
506            has_create2: true,
507            has_revert: true,
508            has_return_data: true,
509            has_bitwise_shifting: true,
510            has_chain_id: true,
511            has_self_balance: true,
512            has_ext_code_hash: true,
513            has_base_fee,
514            has_push0,
515            estimate: false,
516            has_blob_base_fee,
517            has_shard_blob_transactions,
518            has_transient_storage,
519            has_mcopy,
520            has_restricted_selfdestruct,
521        }
522    }
523}
524
525/// Independent inputs that are used to derive other config values.
526/// See `Config::config_with_derived_values` implementation for details.
527#[allow(clippy::struct_excessive_bools)]
528#[derive(Debug, Copy, Clone, Eq, PartialEq)]
529struct DerivedConfigInputs {
530    gas_storage_read_warm: u64,
531    gas_sload_cold: u64,
532    gas_access_list_storage_key: u64,
533    decrease_clears_refund: bool,
534    has_base_fee: bool,
535    has_push0: bool,
536    disallow_executable_format: bool,
537    warm_coinbase_address: bool,
538    max_initcode_size: Option<usize>,
539    has_blob_base_fee: bool,
540    has_shard_blob_transactions: bool,
541    has_transient_storage: bool,
542    has_mcopy: bool,
543    has_restricted_selfdestruct: bool,
544}
545
546impl DerivedConfigInputs {
547    const fn berlin() -> Self {
548        Self {
549            gas_storage_read_warm: 100,
550            gas_sload_cold: 2100,
551            gas_access_list_storage_key: 1900,
552            decrease_clears_refund: false,
553            has_base_fee: false,
554            has_push0: false,
555            disallow_executable_format: false,
556            warm_coinbase_address: false,
557            max_initcode_size: None,
558            has_blob_base_fee: false,
559            has_shard_blob_transactions: false,
560            has_transient_storage: false,
561            has_mcopy: false,
562            has_restricted_selfdestruct: false,
563        }
564    }
565
566    const fn london() -> Self {
567        Self {
568            gas_storage_read_warm: 100,
569            gas_sload_cold: 2100,
570            gas_access_list_storage_key: 1900,
571            decrease_clears_refund: true,
572            has_base_fee: true,
573            has_push0: false,
574            disallow_executable_format: true,
575            warm_coinbase_address: false,
576            max_initcode_size: None,
577            has_blob_base_fee: false,
578            has_shard_blob_transactions: false,
579            has_transient_storage: false,
580            has_mcopy: false,
581            has_restricted_selfdestruct: false,
582        }
583    }
584
585    const fn merge() -> Self {
586        Self {
587            gas_storage_read_warm: 100,
588            gas_sload_cold: 2100,
589            gas_access_list_storage_key: 1900,
590            decrease_clears_refund: true,
591            has_base_fee: true,
592            has_push0: false,
593            disallow_executable_format: true,
594            warm_coinbase_address: false,
595            max_initcode_size: None,
596            has_blob_base_fee: false,
597            has_shard_blob_transactions: false,
598            has_transient_storage: false,
599            has_mcopy: false,
600            has_restricted_selfdestruct: false,
601        }
602    }
603
604    const fn shanghai() -> Self {
605        Self {
606            gas_storage_read_warm: 100,
607            gas_sload_cold: 2100,
608            gas_access_list_storage_key: 1900,
609            decrease_clears_refund: true,
610            has_base_fee: true,
611            has_push0: true,
612            disallow_executable_format: true,
613            warm_coinbase_address: true,
614            // 2 * 24576 as per EIP-3860
615            max_initcode_size: Some(0xC000),
616            has_blob_base_fee: false,
617            has_shard_blob_transactions: false,
618            has_transient_storage: false,
619            has_mcopy: false,
620            has_restricted_selfdestruct: false,
621        }
622    }
623
624    const fn cancun() -> Self {
625        Self {
626            gas_storage_read_warm: 100,
627            gas_sload_cold: 2100,
628            gas_access_list_storage_key: 1900,
629            decrease_clears_refund: true,
630            has_base_fee: true,
631            has_push0: true,
632            disallow_executable_format: true,
633            warm_coinbase_address: true,
634            // 2 * 24576 as per EIP-3860
635            max_initcode_size: Some(0xC000),
636            has_blob_base_fee: true,
637            has_shard_blob_transactions: true,
638            has_transient_storage: true,
639            has_mcopy: true,
640            has_restricted_selfdestruct: true,
641        }
642    }
643}