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