aurora_evm/gasometer/
mod.rs

1//! VM gasometer.
2
3#[cfg(not(feature = "std"))]
4pub mod prelude {
5    pub use alloc::vec::Vec;
6}
7
8#[cfg(feature = "std")]
9pub mod prelude {
10    pub use std::vec::Vec;
11}
12
13#[cfg(feature = "tracing")]
14pub mod tracing;
15
16#[cfg(feature = "tracing")]
17macro_rules! event {
18    ($x:expr) => {
19        use self::tracing::Event::*;
20        self::tracing::with(|listener| listener.event($x));
21    };
22}
23#[cfg(feature = "force-debug")]
24macro_rules! log_gas {
25	($self:expr, $($arg:tt)*) => (
26	log::trace!(target: "evm", "Gasometer {} [Gas used: {}, Gas left: {}]", format_args!($($arg)*),
27	$self.total_used_gas(), $self.gas());
28	#[cfg(feature = "print-debug")]
29	println!("\t# {} [{} | {}]", format_args!($($arg)*), $self.total_used_gas(), $self.gas());
30	);
31}
32
33#[cfg(not(feature = "force-debug"))]
34macro_rules! log_gas {
35    ($self:expr, $($arg:tt)*) => {};
36}
37
38#[cfg(not(feature = "tracing"))]
39macro_rules! event {
40    ($x:expr) => {};
41}
42
43mod consts;
44mod costs;
45mod memory;
46mod utils;
47
48use crate::core::{ExitError, Opcode, Stack};
49use crate::prelude::*;
50use crate::runtime::{Config, Handler};
51use core::cmp::max;
52use primitive_types::{H160, H256, U256};
53
54macro_rules! try_or_fail {
55    ( $inner:expr, $e:expr ) => {
56        match $e {
57            Ok(value) => value,
58            Err(e) => {
59                $inner = Err(e.clone());
60                return Err(e);
61            }
62        }
63    };
64}
65
66#[cfg(feature = "tracing")]
67#[derive(Debug, Copy, Clone)]
68pub struct Snapshot {
69    pub gas_limit: u64,
70    pub memory_gas: u64,
71    pub used_gas: u64,
72    pub refunded_gas: i64,
73}
74
75#[cfg(feature = "tracing")]
76impl Snapshot {
77    #[must_use]
78    const fn new<'config>(gas_limit: u64, inner: &'config Inner<'config>) -> Self {
79        Self {
80            gas_limit,
81            memory_gas: inner.memory_gas,
82            used_gas: inner.used_gas,
83            refunded_gas: inner.refunded_gas,
84        }
85    }
86}
87
88/// EVM gasometer.
89#[derive(Clone, Debug)]
90pub struct Gasometer<'config> {
91    gas_limit: u64,
92    config: &'config Config,
93    inner: Result<Inner<'config>, ExitError>,
94}
95
96impl<'config> Gasometer<'config> {
97    /// Create a new gasometer with given gas limit and config.
98    #[must_use]
99    pub const fn new(gas_limit: u64, config: &'config Config) -> Self {
100        Self {
101            gas_limit,
102            config,
103            inner: Ok(Inner {
104                memory_gas: 0,
105                used_gas: 0,
106                refunded_gas: 0,
107                floor_gas: 0,
108                config,
109            }),
110        }
111    }
112
113    /// Returns the numerical gas cost value.
114    ///
115    /// # Errors
116    /// Return `ExitError`
117    #[inline]
118    pub fn gas_cost(&self, cost: GasCost, gas: u64) -> Result<u64, ExitError> {
119        match self.inner.as_ref() {
120            Ok(inner) => inner.gas_cost(cost, gas),
121            Err(e) => Err(e.clone()),
122        }
123    }
124
125    #[inline]
126    fn inner_mut(&mut self) -> Result<&mut Inner<'config>, ExitError> {
127        self.inner.as_mut().map_err(|e| e.clone())
128    }
129
130    /// Reference of the config.
131    #[inline]
132    #[must_use]
133    pub const fn config(&self) -> &'config Config {
134        self.config
135    }
136
137    /// Gas limit.
138    #[inline]
139    #[must_use]
140    pub const fn gas_limit(&self) -> u64 {
141        self.gas_limit
142    }
143
144    /// Get floor gas
145    #[inline]
146    #[must_use]
147    pub fn floor_gas(&self) -> u64 {
148        self.inner.as_ref().map_or(0, |inner| inner.floor_gas)
149    }
150
151    /// Remaining gas.
152    #[inline]
153    #[must_use]
154    pub fn gas(&self) -> u64 {
155        self.inner.as_ref().map_or(0, |inner| {
156            self.gas_limit - inner.used_gas - inner.memory_gas
157        })
158    }
159
160    /// Total used gas.
161    #[inline]
162    #[must_use]
163    pub const fn total_used_gas(&self) -> u64 {
164        match self.inner.as_ref() {
165            Ok(inner) => inner.used_gas + inner.memory_gas,
166            Err(_) => self.gas_limit,
167        }
168    }
169
170    /// Refunded gas.
171    #[inline]
172    #[must_use]
173    pub fn refunded_gas(&self) -> i64 {
174        self.inner.as_ref().map_or(0, |inner| inner.refunded_gas)
175    }
176
177    /// Explicitly fail the gasometer with out of gas. Return `OutOfGas` error.
178    pub fn fail(&mut self) -> ExitError {
179        self.inner = Err(ExitError::OutOfGas);
180        ExitError::OutOfGas
181    }
182
183    /// Record an explicit cost.
184    ///
185    /// # Errors
186    /// Return `ExitError`
187    #[inline]
188    pub fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> {
189        event!(RecordCost {
190            cost,
191            snapshot: self.snapshot(),
192        });
193
194        let all_gas_cost = self.total_used_gas() + cost;
195        if self.gas_limit < all_gas_cost {
196            self.inner = Err(ExitError::OutOfGas);
197            return Err(ExitError::OutOfGas);
198        }
199
200        self.inner_mut()?.used_gas += cost;
201        log_gas!(self, "record_cost: {}", cost);
202        Ok(())
203    }
204
205    #[inline]
206    /// Record an explicit refund.
207    ///
208    /// # Errors
209    /// Return `ExitError` that is thrown by gasometer gas calculation errors.
210    pub fn record_refund(&mut self, refund: i64) -> Result<(), ExitError> {
211        event!(RecordRefund {
212            refund,
213            snapshot: self.snapshot(),
214        });
215        log_gas!(self, "record_refund: -{}", refund);
216
217        self.inner_mut()?.refunded_gas += refund;
218        Ok(())
219    }
220
221    /// Record refund for `authority` - EIP-7702
222    /// `refunded_accounts` represent count of valid `authority`  accounts.
223    ///
224    /// ## Errors
225    /// Return `ExitError` if `record_refund` operation fails.
226    pub fn record_authority_refund(&mut self, refunded_accounts: u64) -> Result<(), ExitError> {
227        let refund = i64::try_from(
228            refunded_accounts
229                * (self.config.gas_per_empty_account_cost - self.config.gas_per_auth_base_cost),
230        )
231        .unwrap_or(i64::MAX);
232        self.record_refund(refund)
233    }
234
235    /// Record `CREATE` code deposit.
236    ///
237    /// # Errors
238    /// Return `ExitError`
239    /// NOTE: in that context usize->u64 `as_conversions` is save
240    #[allow(clippy::as_conversions)]
241    #[inline]
242    pub fn record_deposit(&mut self, len: usize) -> Result<(), ExitError> {
243        let cost = len as u64 * u64::from(consts::G_CODEDEPOSIT);
244        self.record_cost(cost)
245    }
246
247    /// Record opcode gas cost.
248    ///
249    /// # Errors
250    /// Return `ExitError`
251    pub fn record_dynamic_cost(
252        &mut self,
253        cost: GasCost,
254        memory: Option<MemoryCost>,
255    ) -> Result<(), ExitError> {
256        let gas = self.gas();
257        // Extract a mutable reference to `Inner` to avoid checking `Result`
258        // repeatedly. Tuning performance as this function is on the hot path.
259        let inner_mut = match &mut self.inner {
260            Ok(inner) => inner,
261            Err(err) => return Err(err.clone()),
262        };
263
264        let memory_gas = match memory {
265            Some(memory) => try_or_fail!(self.inner, inner_mut.memory_gas(memory)),
266            None => inner_mut.memory_gas,
267        };
268        let gas_cost = try_or_fail!(self.inner, inner_mut.gas_cost(cost, gas));
269        let gas_refund = inner_mut.gas_refund(cost);
270        let used_gas = inner_mut.used_gas;
271
272        #[cfg(feature = "tracing")]
273        let gas_limit = self.gas_limit;
274        event!(RecordDynamicCost {
275            gas_cost,
276            memory_gas,
277            gas_refund,
278            snapshot: Some(Snapshot::new(gas_limit, inner_mut)),
279        });
280
281        let all_gas_cost = memory_gas
282            .checked_add(used_gas.saturating_add(gas_cost))
283            .ok_or(ExitError::OutOfGas)?;
284        if self.gas_limit < all_gas_cost {
285            self.inner = Err(ExitError::OutOfGas);
286            return Err(ExitError::OutOfGas);
287        }
288
289        let after_gas = self.gas_limit - all_gas_cost;
290        try_or_fail!(self.inner, inner_mut.extra_check(cost, after_gas));
291
292        inner_mut.used_gas += gas_cost;
293        inner_mut.memory_gas = memory_gas;
294        inner_mut.refunded_gas += gas_refund;
295
296        // NOTE Extended meesage: "Record dynamic cost {gas_cost} - memory_gas {} - gas_refund {}",
297        log_gas!(
298            self,
299            "record_dynamic_cost: {gas_cost} - {memory_gas} - {gas_refund}"
300        );
301
302        Ok(())
303    }
304
305    /// Record opcode stipend.
306    ///
307    /// # Errors
308    /// Return `ExitError` that is thrown by gasometer gas calculation errors.
309    #[inline]
310    pub fn record_stipend(&mut self, stipend: u64) -> Result<(), ExitError> {
311        event!(RecordStipend {
312            stipend,
313            snapshot: self.snapshot(),
314        });
315
316        self.inner_mut()?.used_gas -= stipend;
317        log_gas!(self, "record_stipent: {}", stipend);
318        Ok(())
319    }
320
321    /// Record transaction cost.
322    /// Related EIPs:
323    /// - [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028)
324    /// - [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623)
325    ///
326    /// # Errors
327    /// Return `ExitError`
328    pub fn record_transaction(&mut self, cost: TransactionCost) -> Result<(), ExitError> {
329        let gas_cost = match cost {
330            // NOTE: in that context usize->u64 `as_conversions` is safe
331            #[allow(clippy::as_conversions)]
332            TransactionCost::Call {
333                zero_data_len,
334                non_zero_data_len,
335                access_list_address_len,
336                access_list_storage_len,
337                authorization_list_len,
338            } => {
339                #[deny(clippy::let_and_return)]
340                let cost = self.config.gas_transaction_call
341                    + zero_data_len as u64 * self.config.gas_transaction_zero_data
342                    + non_zero_data_len as u64 * self.config.gas_transaction_non_zero_data
343                    + access_list_address_len as u64 * self.config.gas_access_list_address
344                    + access_list_storage_len as u64 * self.config.gas_access_list_storage_key
345                    + authorization_list_len as u64 * self.config.gas_per_empty_account_cost;
346
347                if self.config.has_floor_gas {
348                    // According to EIP-2028: non-zero byte = 16, zero-byte = 4
349                    // According to EIP-7623: tokens_in_calldata = zero_bytes_in_calldata + nonzero_bytes_in_calldata * 4
350                    let tokens_in_calldata = (zero_data_len + non_zero_data_len * 4) as u64;
351                    self.inner_mut()?.floor_gas = tokens_in_calldata
352                        * self.config.total_cost_floor_per_token
353                        + self.config.gas_transaction_call;
354                }
355
356                log_gas!(
357					self,
358					"Record Call {} [gas_transaction_call: {}, zero_data_len: {}, non_zero_data_len: {}, access_list_address_len: {}, access_list_storage_len: {}, authorization_list_len: {}]",
359					cost,
360					self.config.gas_transaction_call,
361					zero_data_len,
362					non_zero_data_len,
363					access_list_address_len,
364					access_list_storage_len,
365					authorization_list_len
366				);
367
368                cost
369            }
370            // NOTE: in that context usize->u64 `as_conversions` is safe
371            #[allow(clippy::as_conversions)]
372            TransactionCost::Create {
373                zero_data_len,
374                non_zero_data_len,
375                access_list_address_len,
376                access_list_storage_len,
377                initcode_cost,
378            } => {
379                let mut cost = self.config.gas_transaction_create
380                    + zero_data_len as u64 * self.config.gas_transaction_zero_data
381                    + non_zero_data_len as u64 * self.config.gas_transaction_non_zero_data
382                    + access_list_address_len as u64 * self.config.gas_access_list_address
383                    + access_list_storage_len as u64 * self.config.gas_access_list_storage_key;
384                if self.config.max_initcode_size.is_some() {
385                    cost += initcode_cost;
386                }
387
388                if self.config.has_floor_gas {
389                    // According to EIP-2028: non-zero byte = 16, zero-byte = 4
390                    // According to EIP-7623: tokens_in_calldata = zero_bytes_in_calldata + nonzero_bytes_in_calldata * 4
391                    let tokens_in_calldata = (zero_data_len + non_zero_data_len * 4) as u64;
392                    self.inner_mut()?.floor_gas = tokens_in_calldata
393                        * self.config.total_cost_floor_per_token
394                        + self.config.gas_transaction_call;
395                }
396
397                log_gas!(
398					self,
399					"Record Create {} [gas_transaction_create: {}, zero_data_len: {}, non_zero_data_len: {}, access_list_address_len: {}, access_list_storage_len: {}, initcode_cost: {}]",
400					cost,
401					self.config.gas_transaction_create,
402					zero_data_len,
403					non_zero_data_len,
404					access_list_address_len,
405					access_list_storage_len,
406					initcode_cost
407				);
408                cost
409            }
410        };
411
412        event!(RecordTransaction {
413            cost: gas_cost,
414            snapshot: self.snapshot(),
415        });
416
417        if self.gas() < gas_cost {
418            self.inner = Err(ExitError::OutOfGas);
419            return Err(ExitError::OutOfGas);
420        }
421
422        self.inner_mut()?.used_gas += gas_cost;
423        Ok(())
424    }
425
426    #[cfg(feature = "tracing")]
427    #[must_use]
428    pub fn snapshot(&self) -> Option<Snapshot> {
429        self.inner
430            .as_ref()
431            .ok()
432            .map(|inner| Snapshot::new(self.gas_limit, inner))
433    }
434}
435
436/// Calculate the call transaction cost.
437#[allow(clippy::naive_bytecount)]
438#[must_use]
439pub fn call_transaction_cost(
440    data: &[u8],
441    access_list: &[(H160, Vec<H256>)],
442    authorization_list_len: usize,
443) -> TransactionCost {
444    let zero_data_len = data.iter().filter(|v| **v == 0).count();
445    let non_zero_data_len = data.len() - zero_data_len;
446    let (access_list_address_len, access_list_storage_len) = count_access_list(access_list);
447
448    TransactionCost::Call {
449        zero_data_len,
450        non_zero_data_len,
451        access_list_address_len,
452        access_list_storage_len,
453        authorization_list_len,
454    }
455}
456
457/// Calculate the create transaction cost.
458#[allow(clippy::naive_bytecount)]
459#[must_use]
460pub fn create_transaction_cost(data: &[u8], access_list: &[(H160, Vec<H256>)]) -> TransactionCost {
461    let zero_data_len = data.iter().filter(|v| **v == 0).count();
462    let non_zero_data_len = data.len() - zero_data_len;
463    let (access_list_address_len, access_list_storage_len) = count_access_list(access_list);
464    let initcode_cost = init_code_cost(data);
465
466    TransactionCost::Create {
467        zero_data_len,
468        non_zero_data_len,
469        access_list_address_len,
470        access_list_storage_len,
471        initcode_cost,
472    }
473}
474
475/// Init code cost, related to `EIP-3860`
476/// NOTE: in that context `as-conversion` is safe for `usize->u64`
477#[allow(clippy::as_conversions)]
478#[must_use]
479pub const fn init_code_cost(data: &[u8]) -> u64 {
480    // As per EIP-3860:
481    // > We define initcode_cost(initcode) to equal INITCODE_WORD_COST * ceil(len(initcode) / 32).
482    // where INITCODE_WORD_COST is 2.
483    2 * ((data.len() as u64 + 31) / 32)
484}
485
486/// Counts the number of addresses and storage keys in the access list
487fn count_access_list(access_list: &[(H160, Vec<H256>)]) -> (usize, usize) {
488    let access_list_address_len = access_list.len();
489    let access_list_storage_len = access_list.iter().map(|(_, keys)| keys.len()).sum();
490
491    (access_list_address_len, access_list_storage_len)
492}
493
494#[allow(clippy::too_many_lines)]
495#[inline]
496#[must_use]
497pub fn static_opcode_cost(opcode: Opcode) -> Option<u32> {
498    static TABLE: [Option<u32>; 256] = {
499        let mut table = [None; 256];
500
501        table[Opcode::STOP.as_usize()] = Some(consts::G_ZERO);
502        table[Opcode::CALLDATASIZE.as_usize()] = Some(consts::G_BASE);
503        table[Opcode::CODESIZE.as_usize()] = Some(consts::G_BASE);
504        table[Opcode::POP.as_usize()] = Some(consts::G_BASE);
505        table[Opcode::PC.as_usize()] = Some(consts::G_BASE);
506        table[Opcode::MSIZE.as_usize()] = Some(consts::G_BASE);
507
508        table[Opcode::ADDRESS.as_usize()] = Some(consts::G_BASE);
509        table[Opcode::ORIGIN.as_usize()] = Some(consts::G_BASE);
510        table[Opcode::CALLER.as_usize()] = Some(consts::G_BASE);
511        table[Opcode::CALLVALUE.as_usize()] = Some(consts::G_BASE);
512        table[Opcode::COINBASE.as_usize()] = Some(consts::G_BASE);
513        table[Opcode::TIMESTAMP.as_usize()] = Some(consts::G_BASE);
514        table[Opcode::NUMBER.as_usize()] = Some(consts::G_BASE);
515        table[Opcode::PREVRANDAO.as_usize()] = Some(consts::G_BASE);
516        table[Opcode::GASLIMIT.as_usize()] = Some(consts::G_BASE);
517        table[Opcode::GASPRICE.as_usize()] = Some(consts::G_BASE);
518        table[Opcode::GAS.as_usize()] = Some(consts::G_BASE);
519
520        table[Opcode::ADD.as_usize()] = Some(consts::G_VERYLOW);
521        table[Opcode::SUB.as_usize()] = Some(consts::G_VERYLOW);
522        table[Opcode::NOT.as_usize()] = Some(consts::G_VERYLOW);
523        table[Opcode::LT.as_usize()] = Some(consts::G_VERYLOW);
524        table[Opcode::GT.as_usize()] = Some(consts::G_VERYLOW);
525        table[Opcode::SLT.as_usize()] = Some(consts::G_VERYLOW);
526        table[Opcode::SGT.as_usize()] = Some(consts::G_VERYLOW);
527        table[Opcode::EQ.as_usize()] = Some(consts::G_VERYLOW);
528        table[Opcode::ISZERO.as_usize()] = Some(consts::G_VERYLOW);
529        table[Opcode::AND.as_usize()] = Some(consts::G_VERYLOW);
530        table[Opcode::OR.as_usize()] = Some(consts::G_VERYLOW);
531        table[Opcode::XOR.as_usize()] = Some(consts::G_VERYLOW);
532        table[Opcode::BYTE.as_usize()] = Some(consts::G_VERYLOW);
533        table[Opcode::CALLDATALOAD.as_usize()] = Some(consts::G_VERYLOW);
534        table[Opcode::PUSH1.as_usize()] = Some(consts::G_VERYLOW);
535        table[Opcode::PUSH2.as_usize()] = Some(consts::G_VERYLOW);
536        table[Opcode::PUSH3.as_usize()] = Some(consts::G_VERYLOW);
537        table[Opcode::PUSH4.as_usize()] = Some(consts::G_VERYLOW);
538        table[Opcode::PUSH5.as_usize()] = Some(consts::G_VERYLOW);
539        table[Opcode::PUSH6.as_usize()] = Some(consts::G_VERYLOW);
540        table[Opcode::PUSH7.as_usize()] = Some(consts::G_VERYLOW);
541        table[Opcode::PUSH8.as_usize()] = Some(consts::G_VERYLOW);
542        table[Opcode::PUSH9.as_usize()] = Some(consts::G_VERYLOW);
543        table[Opcode::PUSH10.as_usize()] = Some(consts::G_VERYLOW);
544        table[Opcode::PUSH11.as_usize()] = Some(consts::G_VERYLOW);
545        table[Opcode::PUSH12.as_usize()] = Some(consts::G_VERYLOW);
546        table[Opcode::PUSH13.as_usize()] = Some(consts::G_VERYLOW);
547        table[Opcode::PUSH14.as_usize()] = Some(consts::G_VERYLOW);
548        table[Opcode::PUSH15.as_usize()] = Some(consts::G_VERYLOW);
549        table[Opcode::PUSH16.as_usize()] = Some(consts::G_VERYLOW);
550        table[Opcode::PUSH17.as_usize()] = Some(consts::G_VERYLOW);
551        table[Opcode::PUSH18.as_usize()] = Some(consts::G_VERYLOW);
552        table[Opcode::PUSH19.as_usize()] = Some(consts::G_VERYLOW);
553        table[Opcode::PUSH20.as_usize()] = Some(consts::G_VERYLOW);
554        table[Opcode::PUSH21.as_usize()] = Some(consts::G_VERYLOW);
555        table[Opcode::PUSH22.as_usize()] = Some(consts::G_VERYLOW);
556        table[Opcode::PUSH23.as_usize()] = Some(consts::G_VERYLOW);
557        table[Opcode::PUSH24.as_usize()] = Some(consts::G_VERYLOW);
558        table[Opcode::PUSH25.as_usize()] = Some(consts::G_VERYLOW);
559        table[Opcode::PUSH26.as_usize()] = Some(consts::G_VERYLOW);
560        table[Opcode::PUSH27.as_usize()] = Some(consts::G_VERYLOW);
561        table[Opcode::PUSH28.as_usize()] = Some(consts::G_VERYLOW);
562        table[Opcode::PUSH29.as_usize()] = Some(consts::G_VERYLOW);
563        table[Opcode::PUSH30.as_usize()] = Some(consts::G_VERYLOW);
564        table[Opcode::PUSH31.as_usize()] = Some(consts::G_VERYLOW);
565        table[Opcode::PUSH32.as_usize()] = Some(consts::G_VERYLOW);
566        table[Opcode::DUP1.as_usize()] = Some(consts::G_VERYLOW);
567        table[Opcode::DUP2.as_usize()] = Some(consts::G_VERYLOW);
568        table[Opcode::DUP3.as_usize()] = Some(consts::G_VERYLOW);
569        table[Opcode::DUP4.as_usize()] = Some(consts::G_VERYLOW);
570        table[Opcode::DUP5.as_usize()] = Some(consts::G_VERYLOW);
571        table[Opcode::DUP6.as_usize()] = Some(consts::G_VERYLOW);
572        table[Opcode::DUP7.as_usize()] = Some(consts::G_VERYLOW);
573        table[Opcode::DUP8.as_usize()] = Some(consts::G_VERYLOW);
574        table[Opcode::DUP9.as_usize()] = Some(consts::G_VERYLOW);
575        table[Opcode::DUP10.as_usize()] = Some(consts::G_VERYLOW);
576        table[Opcode::DUP11.as_usize()] = Some(consts::G_VERYLOW);
577        table[Opcode::DUP12.as_usize()] = Some(consts::G_VERYLOW);
578        table[Opcode::DUP13.as_usize()] = Some(consts::G_VERYLOW);
579        table[Opcode::DUP14.as_usize()] = Some(consts::G_VERYLOW);
580        table[Opcode::DUP15.as_usize()] = Some(consts::G_VERYLOW);
581        table[Opcode::DUP16.as_usize()] = Some(consts::G_VERYLOW);
582        table[Opcode::SWAP1.as_usize()] = Some(consts::G_VERYLOW);
583        table[Opcode::SWAP2.as_usize()] = Some(consts::G_VERYLOW);
584        table[Opcode::SWAP3.as_usize()] = Some(consts::G_VERYLOW);
585        table[Opcode::SWAP4.as_usize()] = Some(consts::G_VERYLOW);
586        table[Opcode::SWAP5.as_usize()] = Some(consts::G_VERYLOW);
587        table[Opcode::SWAP6.as_usize()] = Some(consts::G_VERYLOW);
588        table[Opcode::SWAP7.as_usize()] = Some(consts::G_VERYLOW);
589        table[Opcode::SWAP8.as_usize()] = Some(consts::G_VERYLOW);
590        table[Opcode::SWAP9.as_usize()] = Some(consts::G_VERYLOW);
591        table[Opcode::SWAP10.as_usize()] = Some(consts::G_VERYLOW);
592        table[Opcode::SWAP11.as_usize()] = Some(consts::G_VERYLOW);
593        table[Opcode::SWAP12.as_usize()] = Some(consts::G_VERYLOW);
594        table[Opcode::SWAP13.as_usize()] = Some(consts::G_VERYLOW);
595        table[Opcode::SWAP14.as_usize()] = Some(consts::G_VERYLOW);
596        table[Opcode::SWAP15.as_usize()] = Some(consts::G_VERYLOW);
597        table[Opcode::SWAP16.as_usize()] = Some(consts::G_VERYLOW);
598
599        table[Opcode::MUL.as_usize()] = Some(consts::G_LOW);
600        table[Opcode::DIV.as_usize()] = Some(consts::G_LOW);
601        table[Opcode::SDIV.as_usize()] = Some(consts::G_LOW);
602        table[Opcode::MOD.as_usize()] = Some(consts::G_LOW);
603        table[Opcode::SMOD.as_usize()] = Some(consts::G_LOW);
604        table[Opcode::SIGNEXTEND.as_usize()] = Some(consts::G_LOW);
605
606        table[Opcode::ADDMOD.as_usize()] = Some(consts::G_MID);
607        table[Opcode::MULMOD.as_usize()] = Some(consts::G_MID);
608        table[Opcode::JUMP.as_usize()] = Some(consts::G_MID);
609
610        table[Opcode::JUMPI.as_usize()] = Some(consts::G_HIGH);
611        table[Opcode::JUMPDEST.as_usize()] = Some(consts::G_JUMPDEST);
612
613        table
614    };
615
616    TABLE[opcode.as_usize()]
617}
618
619/// Get and set warm address if it's not warmed.
620fn get_and_set_warm<H: Handler>(handler: &mut H, target: H160) -> (bool, Option<bool>) {
621    let delegated_designator_is_cold =
622        handler
623            .get_authority_target(target)
624            .map(|authority_target| {
625                if handler.is_cold(authority_target, None) {
626                    handler.warm_target((authority_target, None));
627                    true
628                } else {
629                    false
630                }
631            });
632    let target_is_cold = handler.is_cold(target, None);
633    if target_is_cold {
634        handler.warm_target((target, None));
635    }
636    (target_is_cold, delegated_designator_is_cold)
637}
638
639/// Get and set warm address if it's not warmed for non-delegated opcodes like `EXT*`.
640/// NOTE: Related to EIP-7702
641fn get_and_set_non_delegated_warm<H: Handler>(handler: &mut H, target: H160) -> bool {
642    let target_is_cold = handler.is_cold(target, None);
643    if target_is_cold {
644        handler.warm_target((target, None));
645    }
646    target_is_cold
647}
648
649/// Calculate the opcode cost.
650///
651/// # Errors
652/// Return `ExitError`
653#[allow(
654    clippy::nonminimal_bool,
655    clippy::cognitive_complexity,
656    clippy::too_many_lines,
657    clippy::match_same_arms
658)]
659pub fn dynamic_opcode_cost<H: Handler>(
660    address: H160,
661    opcode: Opcode,
662    stack: &Stack,
663    is_static: bool,
664    config: &Config,
665    handler: &mut H,
666) -> Result<(GasCost, Option<MemoryCost>), ExitError> {
667    let gas_cost = match opcode {
668        Opcode::RETURN => GasCost::Zero,
669
670        Opcode::MLOAD | Opcode::MSTORE | Opcode::MSTORE8 => GasCost::VeryLow,
671
672        Opcode::REVERT if config.has_revert => GasCost::Zero,
673        Opcode::REVERT => GasCost::Invalid(opcode),
674
675        Opcode::CHAINID if config.has_chain_id => GasCost::Base,
676        Opcode::CHAINID => GasCost::Invalid(opcode),
677
678        Opcode::SHL | Opcode::SHR | Opcode::SAR if config.has_bitwise_shifting => GasCost::VeryLow,
679        Opcode::SHL | Opcode::SHR | Opcode::SAR => GasCost::Invalid(opcode),
680
681        Opcode::SELFBALANCE if config.has_self_balance => GasCost::Low,
682        Opcode::SELFBALANCE => GasCost::Invalid(opcode),
683
684        Opcode::BASEFEE if config.has_base_fee => GasCost::Base,
685        Opcode::BASEFEE => GasCost::Invalid(opcode),
686
687        Opcode::BLOBBASEFEE if config.has_blob_base_fee => GasCost::Base,
688        Opcode::BLOBBASEFEE => GasCost::Invalid(opcode),
689
690        Opcode::BLOBHASH if config.has_shard_blob_transactions => GasCost::VeryLow,
691        Opcode::BLOBHASH => GasCost::Invalid(opcode),
692
693        Opcode::TLOAD if config.has_transient_storage => GasCost::WarmStorageRead,
694        Opcode::TLOAD => GasCost::Invalid(opcode),
695
696        Opcode::TSTORE if !is_static && config.has_transient_storage => GasCost::WarmStorageRead,
697        Opcode::TSTORE => GasCost::Invalid(opcode),
698
699        Opcode::MCOPY if config.has_mcopy => GasCost::VeryLowCopy {
700            len: stack.peek(2)?,
701        },
702        Opcode::MCOPY => GasCost::Invalid(opcode),
703
704        Opcode::EXTCODESIZE => {
705            let target = stack.peek_h256(0)?.into();
706            let target_is_cold = get_and_set_non_delegated_warm(handler, target);
707            GasCost::ExtCodeSize { target_is_cold }
708        }
709        Opcode::BALANCE => {
710            let target = stack.peek_h256(0)?.into();
711            let target_is_cold = handler.is_cold(target, None);
712            if target_is_cold {
713                handler.warm_target((target, None));
714            }
715            GasCost::Balance { target_is_cold }
716        }
717        Opcode::BLOCKHASH => GasCost::BlockHash,
718
719        Opcode::EXTCODEHASH if config.has_ext_code_hash => {
720            let target = stack.peek_h256(0)?.into();
721            let target_is_cold = get_and_set_non_delegated_warm(handler, target);
722            GasCost::ExtCodeHash { target_is_cold }
723        }
724        Opcode::EXTCODEHASH => GasCost::Invalid(opcode),
725
726        Opcode::CALLCODE => {
727            let target = stack.peek_h256(1)?.into();
728            let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target);
729            GasCost::CallCode {
730                value: stack.peek(2)?,
731                gas: stack.peek(0)?,
732                target_is_cold,
733                delegated_designator_is_cold,
734                target_exists: {
735                    handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
736                    handler.exists(target)
737                },
738            }
739        }
740        Opcode::STATICCALL => {
741            let target = stack.peek_h256(1)?.into();
742            let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target);
743            GasCost::StaticCall {
744                gas: stack.peek(0)?,
745                target_is_cold,
746                delegated_designator_is_cold,
747                target_exists: {
748                    handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
749                    handler.exists(target)
750                },
751            }
752        }
753        Opcode::SHA3 => GasCost::Sha3 {
754            len: stack.peek(1)?,
755        },
756        Opcode::EXTCODECOPY => {
757            let target = stack.peek_h256(0)?.into();
758            let target_is_cold = get_and_set_non_delegated_warm(handler, target);
759            GasCost::ExtCodeCopy {
760                target_is_cold,
761                len: stack.peek(3)?,
762            }
763        }
764        Opcode::CALLDATACOPY | Opcode::CODECOPY => GasCost::VeryLowCopy {
765            len: stack.peek(2)?,
766        },
767        Opcode::EXP => GasCost::Exp {
768            power: stack.peek(1)?,
769        },
770        Opcode::SLOAD => {
771            let index = stack.peek_h256(0)?;
772            let target_is_cold = handler.is_cold(address, Some(index));
773            if target_is_cold {
774                handler.warm_target((address, Some(index)));
775            }
776            GasCost::SLoad { target_is_cold }
777        }
778
779        Opcode::DELEGATECALL if config.has_delegate_call => {
780            let target = stack.peek_h256(1)?.into();
781            let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target);
782            GasCost::DelegateCall {
783                gas: stack.peek(0)?,
784                target_is_cold,
785                delegated_designator_is_cold,
786                target_exists: {
787                    handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
788                    handler.exists(target)
789                },
790            }
791        }
792        Opcode::DELEGATECALL => GasCost::Invalid(opcode),
793
794        Opcode::RETURNDATASIZE if config.has_return_data => GasCost::Base,
795        Opcode::RETURNDATACOPY if config.has_return_data => GasCost::VeryLowCopy {
796            len: stack.peek(2)?,
797        },
798        Opcode::RETURNDATASIZE | Opcode::RETURNDATACOPY => GasCost::Invalid(opcode),
799
800        Opcode::SSTORE if !is_static => {
801            let index = stack.peek_h256(0)?;
802            let value = stack.peek_h256(1)?;
803            let target_is_cold = handler.is_cold(address, Some(index));
804            if target_is_cold {
805                handler.warm_target((address, Some(index)));
806            }
807            GasCost::SStore {
808                original: handler.original_storage(address, index),
809                current: handler.storage(address, index),
810                new: value,
811                target_is_cold,
812            }
813        }
814        Opcode::LOG0 if !is_static => GasCost::Log {
815            n: 0,
816            len: stack.peek(1)?,
817        },
818        Opcode::LOG1 if !is_static => GasCost::Log {
819            n: 1,
820            len: stack.peek(1)?,
821        },
822        Opcode::LOG2 if !is_static => GasCost::Log {
823            n: 2,
824            len: stack.peek(1)?,
825        },
826        Opcode::LOG3 if !is_static => GasCost::Log {
827            n: 3,
828            len: stack.peek(1)?,
829        },
830        Opcode::LOG4 if !is_static => GasCost::Log {
831            n: 4,
832            len: stack.peek(1)?,
833        },
834        Opcode::CREATE if !is_static => GasCost::Create,
835        Opcode::CREATE2 if !is_static && config.has_create2 => GasCost::Create2 {
836            len: stack.peek(2)?,
837        },
838        Opcode::SELFDESTRUCT if !is_static => {
839            let target = stack.peek_h256(0)?.into();
840            let target_is_cold = handler.is_cold(target, None);
841            if target_is_cold {
842                handler.warm_target((target, None));
843            }
844            GasCost::Suicide {
845                value: handler.balance(address),
846                target_is_cold,
847                target_exists: {
848                    handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
849                    handler.exists(target)
850                },
851                already_removed: handler.deleted(address),
852            }
853        }
854        Opcode::CALL if !is_static || (is_static && stack.peek(2)? == U256::zero()) => {
855            let target = stack.peek_h256(1)?.into();
856            let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target);
857            GasCost::Call {
858                value: stack.peek(2)?,
859                gas: stack.peek(0)?,
860                target_is_cold,
861                delegated_designator_is_cold,
862                target_exists: {
863                    handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
864                    handler.exists(target)
865                },
866            }
867        }
868
869        Opcode::PUSH0 if config.has_push0 => GasCost::Base,
870
871        _ => GasCost::Invalid(opcode),
872    };
873
874    let memory_cost = match opcode {
875        Opcode::SHA3
876        | Opcode::RETURN
877        | Opcode::REVERT
878        | Opcode::LOG0
879        | Opcode::LOG1
880        | Opcode::LOG2
881        | Opcode::LOG3
882        | Opcode::LOG4 => Some(peek_memory_cost(stack, 0, 1)?),
883
884        Opcode::CODECOPY | Opcode::CALLDATACOPY | Opcode::RETURNDATACOPY => {
885            Some(peek_memory_cost(stack, 0, 2)?)
886        }
887
888        Opcode::EXTCODECOPY => Some(peek_memory_cost(stack, 1, 3)?),
889
890        Opcode::MLOAD | Opcode::MSTORE => Some(MemoryCost {
891            offset: stack.peek_usize(0)?,
892            len: 32,
893        }),
894
895        Opcode::MCOPY => {
896            let len = stack.peek_usize(2)?;
897            if len == 0 {
898                None
899            } else {
900                Some(MemoryCost {
901                    offset: {
902                        let src = stack.peek_usize(0)?;
903                        let dst = stack.peek_usize(1)?;
904                        max(src, dst)
905                    },
906                    len,
907                })
908            }
909        }
910
911        Opcode::MSTORE8 => Some(MemoryCost {
912            offset: stack.peek_usize(0)?,
913            len: 1,
914        }),
915
916        Opcode::CREATE | Opcode::CREATE2 => Some(peek_memory_cost(stack, 1, 2)?),
917
918        Opcode::CALL | Opcode::CALLCODE => {
919            Some(peek_memory_cost(stack, 3, 4)?.join(peek_memory_cost(stack, 5, 6)?))
920        }
921
922        Opcode::DELEGATECALL | Opcode::STATICCALL => {
923            Some(peek_memory_cost(stack, 2, 3)?.join(peek_memory_cost(stack, 4, 5)?))
924        }
925
926        _ => None,
927    };
928
929    Ok((gas_cost, memory_cost))
930}
931
932fn peek_memory_cost(
933    stack: &Stack,
934    offset_index: usize,
935    len_index: usize,
936) -> Result<MemoryCost, ExitError> {
937    let len = stack.peek_usize(len_index)?;
938
939    if len == 0 {
940        return Ok(MemoryCost {
941            offset: usize::MAX,
942            len,
943        });
944    }
945
946    let offset = stack.peek_usize(offset_index)?;
947    Ok(MemoryCost { offset, len })
948}
949
950/// Holds the gas consumption for a Gasometer instance.
951#[derive(Clone, Debug)]
952struct Inner<'config> {
953    memory_gas: u64,
954    used_gas: u64,
955    refunded_gas: i64,
956    config: &'config Config,
957    floor_gas: u64,
958}
959
960impl Inner<'_> {
961    fn memory_gas(&self, memory: MemoryCost) -> Result<u64, ExitError> {
962        let from = memory.offset;
963        let len = memory.len;
964
965        if len == 0 {
966            return Ok(self.memory_gas);
967        }
968
969        let end = from.checked_add(len).ok_or(ExitError::OutOfGas)?;
970
971        let rem = end % 32;
972        let new = if rem == 0 { end / 32 } else { end / 32 + 1 };
973
974        Ok(max(self.memory_gas, memory::memory_gas(new)?))
975    }
976
977    fn extra_check(&self, cost: GasCost, after_gas: u64) -> Result<(), ExitError> {
978        match cost {
979            GasCost::Call { gas, .. }
980            | GasCost::CallCode { gas, .. }
981            | GasCost::DelegateCall { gas, .. }
982            | GasCost::StaticCall { gas, .. } => {
983                costs::call_extra_check(gas, after_gas, self.config)
984            }
985            _ => Ok(()),
986        }
987    }
988
989    /// Returns the gas cost numerical value.
990    #[allow(clippy::too_many_lines)]
991    fn gas_cost(&self, cost: GasCost, gas: u64) -> Result<u64, ExitError> {
992        Ok(match cost {
993            GasCost::Call {
994                value,
995                target_is_cold,
996                delegated_designator_is_cold,
997                target_exists,
998                ..
999            } => costs::call_cost(
1000                value,
1001                target_is_cold,
1002                delegated_designator_is_cold,
1003                true,
1004                true,
1005                !target_exists,
1006                self.config,
1007            ),
1008            GasCost::CallCode {
1009                value,
1010                target_is_cold,
1011                delegated_designator_is_cold,
1012                target_exists,
1013                ..
1014            } => costs::call_cost(
1015                value,
1016                target_is_cold,
1017                delegated_designator_is_cold,
1018                true,
1019                false,
1020                !target_exists,
1021                self.config,
1022            ),
1023            GasCost::DelegateCall {
1024                target_is_cold,
1025                delegated_designator_is_cold,
1026                target_exists,
1027                ..
1028            } => costs::call_cost(
1029                U256::zero(),
1030                target_is_cold,
1031                delegated_designator_is_cold,
1032                false,
1033                false,
1034                !target_exists,
1035                self.config,
1036            ),
1037            GasCost::StaticCall {
1038                target_is_cold,
1039                delegated_designator_is_cold,
1040                target_exists,
1041                ..
1042            } => costs::call_cost(
1043                U256::zero(),
1044                target_is_cold,
1045                delegated_designator_is_cold,
1046                false,
1047                true,
1048                !target_exists,
1049                self.config,
1050            ),
1051
1052            GasCost::Suicide {
1053                value,
1054                target_is_cold,
1055                target_exists,
1056                ..
1057            } => costs::suicide_cost(value, target_is_cold, target_exists, self.config),
1058            GasCost::SStore {
1059                original,
1060                current,
1061                new,
1062                target_is_cold,
1063            } => costs::sstore_cost(original, current, new, gas, target_is_cold, self.config)?,
1064
1065            GasCost::Sha3 { len } => costs::sha3_cost(len)?,
1066            GasCost::Log { n, len } => costs::log_cost(n, len)?,
1067            GasCost::VeryLowCopy { len } => costs::verylowcopy_cost(len)?,
1068            GasCost::Exp { power } => costs::exp_cost(power, self.config)?,
1069            GasCost::Create => u64::from(consts::G_CREATE),
1070            GasCost::Create2 { len } => costs::create2_cost(len)?,
1071            GasCost::SLoad { target_is_cold } => costs::sload_cost(target_is_cold, self.config),
1072
1073            GasCost::Zero => u64::from(consts::G_ZERO),
1074            GasCost::Base => u64::from(consts::G_BASE),
1075            GasCost::VeryLow => u64::from(consts::G_VERYLOW),
1076            GasCost::Low => u64::from(consts::G_LOW),
1077            GasCost::Invalid(opcode) => return Err(ExitError::InvalidCode(opcode)),
1078
1079            GasCost::ExtCodeSize { target_is_cold } => costs::address_access_cost(
1080                target_is_cold,
1081                None,
1082                self.config.gas_ext_code,
1083                self.config,
1084            ),
1085            GasCost::ExtCodeCopy {
1086                target_is_cold,
1087                len,
1088            } => costs::extcodecopy_cost(len, target_is_cold, None, self.config)?,
1089            GasCost::Balance { target_is_cold } => costs::address_access_cost(
1090                target_is_cold,
1091                None,
1092                self.config.gas_balance,
1093                self.config,
1094            ),
1095            GasCost::BlockHash => u64::from(consts::G_BLOCKHASH),
1096            GasCost::ExtCodeHash { target_is_cold } => costs::address_access_cost(
1097                target_is_cold,
1098                None,
1099                self.config.gas_ext_code_hash,
1100                self.config,
1101            ),
1102            GasCost::WarmStorageRead => costs::storage_read_warm(self.config),
1103        })
1104    }
1105
1106    fn gas_refund(&self, cost: GasCost) -> i64 {
1107        match cost {
1108            _ if self.config.estimate => 0,
1109
1110            GasCost::SStore {
1111                original,
1112                current,
1113                new,
1114                ..
1115            } => costs::sstore_refund(original, current, new, self.config),
1116            GasCost::Suicide {
1117                already_removed, ..
1118            } if !self.config.decrease_clears_refund => costs::suicide_refund(already_removed),
1119            _ => 0,
1120        }
1121    }
1122}
1123
1124/// Gas cost.
1125#[derive(Debug, Clone, Copy)]
1126pub enum GasCost {
1127    /// Zero gas cost.
1128    Zero,
1129    /// Base gas cost.
1130    Base,
1131    /// Very low gas cost.
1132    VeryLow,
1133    /// Low gas cost.
1134    Low,
1135    /// Fail the gasometer.
1136    Invalid(Opcode),
1137
1138    /// Gas cost for `EXTCODESIZE`.
1139    ExtCodeSize {
1140        /// True if address has not been previously accessed in this transaction
1141        target_is_cold: bool,
1142    },
1143    /// Gas cost for `BALANCE`.
1144    Balance {
1145        /// True if address has not been previously accessed in this transaction
1146        target_is_cold: bool,
1147    },
1148    /// Gas cost for `BLOCKHASH`.
1149    BlockHash,
1150    /// Gas cost for `EXTBLOCKHASH`.
1151    ExtCodeHash {
1152        /// True if address has not been previously accessed in this transaction
1153        target_is_cold: bool,
1154    },
1155
1156    /// Gas cost for `CALL`.
1157    Call {
1158        /// Call value.
1159        value: U256,
1160        /// Call gas.
1161        gas: U256,
1162        /// True if target has not been previously accessed in this transaction
1163        target_is_cold: bool,
1164        /// True if delegated designator of authority has not been previously accessed in this transaction (EIP-7702)
1165        delegated_designator_is_cold: Option<bool>,
1166        /// Whether the target exists.
1167        target_exists: bool,
1168    },
1169    /// Gas cost for `CALLCODE`.
1170    CallCode {
1171        /// Call value.
1172        value: U256,
1173        /// Call gas.
1174        gas: U256,
1175        /// True if target has not been previously accessed in this transaction
1176        target_is_cold: bool,
1177        /// True if delegated designator of authority has not been previously accessed in this transaction (EIP-7702)
1178        delegated_designator_is_cold: Option<bool>,
1179        /// Whether the target exists.
1180        target_exists: bool,
1181    },
1182    /// Gas cost for `DELEGATECALL`.
1183    DelegateCall {
1184        /// Call gas.
1185        gas: U256,
1186        /// True if target has not been previously accessed in this transaction
1187        target_is_cold: bool,
1188        /// True if delegated designator of authority has not been previously accessed in this transaction (EIP-7702)
1189        delegated_designator_is_cold: Option<bool>,
1190        /// Whether the target exists.
1191        target_exists: bool,
1192    },
1193    /// Gas cost for `STATICCALL`.
1194    StaticCall {
1195        /// Call gas.
1196        gas: U256,
1197        /// True if target has not been previously accessed in this transaction
1198        target_is_cold: bool,
1199        /// True if delegated designator of authority has not been previously accessed in this transaction (EIP-7702)
1200        delegated_designator_is_cold: Option<bool>,
1201        /// Whether the target exists.
1202        target_exists: bool,
1203    },
1204    /// Gas cost for `SUICIDE`.
1205    Suicide {
1206        /// Value.
1207        value: U256,
1208        /// True if target has not been previously accessed in this transaction
1209        target_is_cold: bool,
1210        /// Whether the target exists.
1211        target_exists: bool,
1212        /// Whether the target has already been removed.
1213        already_removed: bool,
1214    },
1215    /// Gas cost for `SSTORE`.
1216    SStore {
1217        /// Original value.
1218        original: H256,
1219        /// Current value.
1220        current: H256,
1221        /// New value.
1222        new: H256,
1223        /// True if target has not been previously accessed in this transaction
1224        target_is_cold: bool,
1225    },
1226    /// Gas cost for `SHA3`.
1227    Sha3 {
1228        /// Length of the data.
1229        len: U256,
1230    },
1231    /// Gas cost for `LOG`.
1232    Log {
1233        /// Topic length.
1234        n: u8,
1235        /// Data length.
1236        len: U256,
1237    },
1238    /// Gas cost for `EXTCODECOPY`.
1239    ExtCodeCopy {
1240        /// True if target has not been previously accessed in this transaction
1241        target_is_cold: bool,
1242        /// Length.
1243        len: U256,
1244    },
1245    /// Gas cost for some copy opcodes that is documented as `VERYLOW`.
1246    VeryLowCopy {
1247        /// Length.
1248        len: U256,
1249    },
1250    /// Gas cost for `EXP`.
1251    Exp {
1252        /// Power of `EXP`.
1253        power: U256,
1254    },
1255    /// Gas cost for `CREATE`.
1256    Create,
1257    /// Gas cost for `CREATE2`.
1258    Create2 {
1259        /// Length.
1260        len: U256,
1261    },
1262    /// Gas cost for `SLOAD`.
1263    SLoad {
1264        /// True if target has not been previously accessed in this transaction
1265        target_is_cold: bool,
1266    },
1267    WarmStorageRead,
1268}
1269
1270/// Storage opcode will access. Used for tracking accessed storage (EIP-2929).
1271#[derive(Debug, Clone, Copy)]
1272pub enum StorageTarget {
1273    /// No storage access
1274    None,
1275    /// Accessing address
1276    Address(H160),
1277    /// Accessing storage slot within an address
1278    Slot(H160, H256),
1279}
1280
1281/// Memory cost.
1282#[derive(Debug, Clone, Copy)]
1283pub struct MemoryCost {
1284    /// Affected memory offset.
1285    pub offset: usize,
1286    /// Affected length.
1287    pub len: usize,
1288}
1289
1290/// Transaction cost.
1291#[derive(Debug, Clone, Copy)]
1292pub enum TransactionCost {
1293    /// Call transaction cost.
1294    Call {
1295        /// Length of zeros in transaction data.
1296        zero_data_len: usize,
1297        /// Length of non-zeros in transaction data.
1298        non_zero_data_len: usize,
1299        /// Number of addresses in transaction access list (see EIP-2930)
1300        access_list_address_len: usize,
1301        /// Total number of storage keys in transaction access list (see EIP-2930)
1302        access_list_storage_len: usize,
1303        /// Number of authorities in transaction authorization list (see EIP-7702)
1304        authorization_list_len: usize,
1305    },
1306    /// Create transaction cost.
1307    Create {
1308        /// Length of zeros in transaction data.
1309        zero_data_len: usize,
1310        /// Length of non-zeros in transaction data.
1311        non_zero_data_len: usize,
1312        /// Number of addresses in transaction access list (see EIP-2930)
1313        access_list_address_len: usize,
1314        /// Total number of storage keys in transaction access list (see EIP-2930)
1315        access_list_storage_len: usize,
1316        /// Cost of initcode = 2 * ceil(len(initcode) / 32) (see EIP-3860)
1317        initcode_cost: u64,
1318    },
1319}
1320
1321impl MemoryCost {
1322    /// Join two memory cost together.
1323    #[must_use]
1324    pub const fn join(self, other: Self) -> Self {
1325        if self.len == 0 {
1326            return other;
1327        }
1328
1329        if other.len == 0 {
1330            return self;
1331        }
1332
1333        let self_end = self.offset.saturating_add(self.len);
1334        let other_end = other.offset.saturating_add(other.len);
1335
1336        if self_end >= other_end {
1337            self
1338        } else {
1339            other
1340        }
1341    }
1342}