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