Skip to main content

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    /// Calculate intrinsic gas and gas floor based on transaction data.
323    /// Returns intrinsic gas cost and gas floor.
324    #[must_use]
325    pub fn calculate_intrinsic_gas_and_gas_floor(
326        data: &[u8],
327        access_list: &[(H160, Vec<H256>)],
328        authorization_list_len: usize,
329        config: &Config,
330        is_contract_creation: bool,
331    ) -> (u64, u64) {
332        let cost = if is_contract_creation {
333            create_transaction_cost(data, access_list)
334        } else {
335            call_transaction_cost(data, access_list, authorization_list_len)
336        };
337        Self::intrinsic_gas_and_gas_floor(cost, config)
338    }
339
340    /// Calculate intrinsic gas and gas floor based on `TransactionCost` type and config.
341    /// Returns intrinsic gas cost and gas floor.
342    /// Related EIPs:
343    /// - [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028)
344    /// - [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623)
345    #[must_use]
346    pub const fn intrinsic_gas_and_gas_floor(cost: TransactionCost, config: &Config) -> (u64, u64) {
347        match cost {
348            // NOTE: in that context usize->u64 `as_conversions` is safe
349            #[allow(clippy::as_conversions)]
350            TransactionCost::Call {
351                zero_data_len,
352                non_zero_data_len,
353                access_list_address_len,
354                access_list_storage_len,
355                authorization_list_len,
356            } => {
357                #[deny(clippy::let_and_return)]
358                let cost = config.gas_transaction_call
359                    + zero_data_len as u64 * config.gas_transaction_zero_data
360                    + non_zero_data_len as u64 * config.gas_transaction_non_zero_data
361                    + access_list_address_len as u64 * config.gas_access_list_address
362                    + access_list_storage_len as u64 * config.gas_access_list_storage_key
363                    + authorization_list_len as u64 * config.gas_per_empty_account_cost;
364
365                let floor_gas = if config.has_floor_gas {
366                    // According to EIP-2028: non-zero byte = 16, zero-byte = 4
367                    // According to EIP-7623: tokens_in_calldata = zero_bytes_in_calldata + nonzero_bytes_in_calldata * 4
368                    let tokens_in_calldata = (zero_data_len + non_zero_data_len * 4) as u64;
369                    tokens_in_calldata * config.total_cost_floor_per_token
370                        + config.gas_transaction_call
371                } else {
372                    0
373                };
374
375                (cost, floor_gas)
376            }
377            // NOTE: in that context usize->u64 `as_conversions` is safe
378            #[allow(clippy::as_conversions)]
379            TransactionCost::Create {
380                zero_data_len,
381                non_zero_data_len,
382                access_list_address_len,
383                access_list_storage_len,
384                initcode_cost,
385            } => {
386                let mut cost = config.gas_transaction_create
387                    + zero_data_len as u64 * config.gas_transaction_zero_data
388                    + non_zero_data_len as u64 * config.gas_transaction_non_zero_data
389                    + access_list_address_len as u64 * config.gas_access_list_address
390                    + access_list_storage_len as u64 * config.gas_access_list_storage_key;
391                if config.max_initcode_size.is_some() {
392                    cost += initcode_cost;
393                }
394
395                let floor_gas = if config.has_floor_gas {
396                    // According to EIP-2028: non-zero byte = 16, zero-byte = 4
397                    // According to EIP-7623: tokens_in_calldata = zero_bytes_in_calldata + nonzero_bytes_in_calldata * 4
398                    let tokens_in_calldata = (zero_data_len + non_zero_data_len * 4) as u64;
399                    tokens_in_calldata * config.total_cost_floor_per_token
400                        + config.gas_transaction_call
401                } else {
402                    0
403                };
404
405                (cost, floor_gas)
406            }
407        }
408    }
409
410    /// Verify transaction cost against gas limit and gas floor based on `TransactionCost` type.
411    /// Returns intrinsic gas cost.
412    /// For `force-debug` feature, it will log the transaction cost details.
413    /// Related EIPs:
414    /// - [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028)
415    /// - [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623)
416    ///
417    /// ## Errors
418    /// Return `ExitError` if gas limit is less than intrinsic gas or gas floor.
419    pub fn verify_transaction(&mut self, cost: TransactionCost) -> Result<(u64, u64), ExitError> {
420        let (gas_cost, floor_gas) = Self::intrinsic_gas_and_gas_floor(cost, self.config);
421        #[cfg(feature = "force-debug")]
422        match cost {
423            TransactionCost::Call {
424                zero_data_len,
425                non_zero_data_len,
426                access_list_address_len,
427                access_list_storage_len,
428                authorization_list_len,
429            } => {
430                log_gas!(
431					self,
432					"Record Call {} [gas_transaction_call: {}, zero_data_len: {}, non_zero_data_len: {}, access_list_address_len: {}, access_list_storage_len: {}, authorization_list_len: {}]",
433					cost,
434					self.config.gas_transaction_call,
435					zero_data_len,
436					non_zero_data_len,
437					access_list_address_len,
438					access_list_storage_len,
439					authorization_list_len
440				);
441            }
442            TransactionCost::Create {
443                zero_data_len,
444                non_zero_data_len,
445                access_list_address_len,
446                access_list_storage_len,
447                initcode_cost,
448            } => {
449                log_gas!(
450					self,
451					"Record Create {} [gas_transaction_create: {}, zero_data_len: {}, non_zero_data_len: {}, access_list_address_len: {}, access_list_storage_len: {}, initcode_cost: {}]",
452					cost,
453					self.config.gas_transaction_create,
454					zero_data_len,
455					non_zero_data_len,
456					access_list_address_len,
457					access_list_storage_len,
458					initcode_cost
459				);
460            }
461        }
462        if self.gas() < gas_cost {
463            self.inner = Err(ExitError::OutOfGas);
464            return Err(ExitError::OutOfGas);
465        }
466        // EIP-7623 gas floor check for gas_limit
467        // It's equivalent to checking: max(cas_cost, floor_gas). But as we need to check
468        // `config.has_floor_gas` anyway, we can do it this way to avoid an extra max() call.
469        if self.config.has_floor_gas && self.gas_limit() < floor_gas {
470            self.inner = Err(ExitError::OutOfGas);
471            return Err(ExitError::OutOfGas);
472        }
473
474        Ok((gas_cost, floor_gas))
475    }
476
477    /// Record transaction cost.
478    /// Related EIPs:
479    /// - [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028)
480    /// - [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623)
481    ///
482    /// # Errors
483    /// Return `ExitError`
484    pub fn record_transaction(&mut self, cost: TransactionCost) -> Result<(), ExitError> {
485        let (gas_cost, floor_gas) = self.verify_transaction(cost)?;
486
487        event!(RecordTransaction {
488            cost: gas_cost,
489            snapshot: self.snapshot(),
490        });
491
492        // EIP-7623 gas floor update
493        if self.config.has_floor_gas {
494            self.inner_mut()?.floor_gas = floor_gas;
495        }
496
497        self.inner_mut()?.used_gas += gas_cost;
498        Ok(())
499    }
500
501    #[cfg(feature = "tracing")]
502    #[must_use]
503    pub fn snapshot(&self) -> Option<Snapshot> {
504        self.inner
505            .as_ref()
506            .ok()
507            .map(|inner| Snapshot::new(self.gas_limit, inner))
508    }
509}
510
511/// Calculate the call transaction cost.
512#[allow(clippy::naive_bytecount)]
513#[must_use]
514pub fn call_transaction_cost(
515    data: &[u8],
516    access_list: &[(H160, Vec<H256>)],
517    authorization_list_len: usize,
518) -> TransactionCost {
519    let zero_data_len = data.iter().filter(|v| **v == 0).count();
520    let non_zero_data_len = data.len() - zero_data_len;
521    let (access_list_address_len, access_list_storage_len) = count_access_list(access_list);
522
523    TransactionCost::Call {
524        zero_data_len,
525        non_zero_data_len,
526        access_list_address_len,
527        access_list_storage_len,
528        authorization_list_len,
529    }
530}
531
532/// Calculate the create transaction cost.
533#[allow(clippy::naive_bytecount)]
534#[must_use]
535pub fn create_transaction_cost(data: &[u8], access_list: &[(H160, Vec<H256>)]) -> TransactionCost {
536    let zero_data_len = data.iter().filter(|v| **v == 0).count();
537    let non_zero_data_len = data.len() - zero_data_len;
538    let (access_list_address_len, access_list_storage_len) = count_access_list(access_list);
539    let initcode_cost = init_code_cost(data);
540
541    TransactionCost::Create {
542        zero_data_len,
543        non_zero_data_len,
544        access_list_address_len,
545        access_list_storage_len,
546        initcode_cost,
547    }
548}
549
550/// Init code cost, related to `EIP-3860`
551/// NOTE: in that context `as-conversion` is safe for `usize->u64`
552#[allow(clippy::as_conversions)]
553#[must_use]
554pub const fn init_code_cost(data: &[u8]) -> u64 {
555    // As per EIP-3860:
556    // > We define initcode_cost(initcode) to equal INITCODE_WORD_COST * ceil(len(initcode) / 32).
557    // where INITCODE_WORD_COST is 2.
558    2 * (data.len() as u64).div_ceil(32)
559}
560
561/// Counts the number of addresses and storage keys in the access list
562fn count_access_list(access_list: &[(H160, Vec<H256>)]) -> (usize, usize) {
563    let access_list_address_len = access_list.len();
564    let access_list_storage_len = access_list.iter().map(|(_, keys)| keys.len()).sum();
565
566    (access_list_address_len, access_list_storage_len)
567}
568
569#[allow(clippy::too_many_lines)]
570#[inline]
571#[must_use]
572pub fn static_opcode_cost(opcode: Opcode) -> Option<u32> {
573    static TABLE: [Option<u32>; 256] = {
574        let mut table = [None; 256];
575
576        table[Opcode::STOP.as_usize()] = Some(consts::G_ZERO);
577        table[Opcode::CALLDATASIZE.as_usize()] = Some(consts::G_BASE);
578        table[Opcode::CODESIZE.as_usize()] = Some(consts::G_BASE);
579        table[Opcode::POP.as_usize()] = Some(consts::G_BASE);
580        table[Opcode::PC.as_usize()] = Some(consts::G_BASE);
581        table[Opcode::MSIZE.as_usize()] = Some(consts::G_BASE);
582
583        table[Opcode::ADDRESS.as_usize()] = Some(consts::G_BASE);
584        table[Opcode::ORIGIN.as_usize()] = Some(consts::G_BASE);
585        table[Opcode::CALLER.as_usize()] = Some(consts::G_BASE);
586        table[Opcode::CALLVALUE.as_usize()] = Some(consts::G_BASE);
587        table[Opcode::COINBASE.as_usize()] = Some(consts::G_BASE);
588        table[Opcode::TIMESTAMP.as_usize()] = Some(consts::G_BASE);
589        table[Opcode::NUMBER.as_usize()] = Some(consts::G_BASE);
590        table[Opcode::PREVRANDAO.as_usize()] = Some(consts::G_BASE);
591        table[Opcode::GASLIMIT.as_usize()] = Some(consts::G_BASE);
592        table[Opcode::GASPRICE.as_usize()] = Some(consts::G_BASE);
593        table[Opcode::GAS.as_usize()] = Some(consts::G_BASE);
594
595        table[Opcode::ADD.as_usize()] = Some(consts::G_VERYLOW);
596        table[Opcode::SUB.as_usize()] = Some(consts::G_VERYLOW);
597        table[Opcode::NOT.as_usize()] = Some(consts::G_VERYLOW);
598        table[Opcode::LT.as_usize()] = Some(consts::G_VERYLOW);
599        table[Opcode::GT.as_usize()] = Some(consts::G_VERYLOW);
600        table[Opcode::SLT.as_usize()] = Some(consts::G_VERYLOW);
601        table[Opcode::SGT.as_usize()] = Some(consts::G_VERYLOW);
602        table[Opcode::EQ.as_usize()] = Some(consts::G_VERYLOW);
603        table[Opcode::ISZERO.as_usize()] = Some(consts::G_VERYLOW);
604        table[Opcode::AND.as_usize()] = Some(consts::G_VERYLOW);
605        table[Opcode::OR.as_usize()] = Some(consts::G_VERYLOW);
606        table[Opcode::XOR.as_usize()] = Some(consts::G_VERYLOW);
607        table[Opcode::BYTE.as_usize()] = Some(consts::G_VERYLOW);
608        table[Opcode::CALLDATALOAD.as_usize()] = Some(consts::G_VERYLOW);
609        table[Opcode::PUSH1.as_usize()] = Some(consts::G_VERYLOW);
610        table[Opcode::PUSH2.as_usize()] = Some(consts::G_VERYLOW);
611        table[Opcode::PUSH3.as_usize()] = Some(consts::G_VERYLOW);
612        table[Opcode::PUSH4.as_usize()] = Some(consts::G_VERYLOW);
613        table[Opcode::PUSH5.as_usize()] = Some(consts::G_VERYLOW);
614        table[Opcode::PUSH6.as_usize()] = Some(consts::G_VERYLOW);
615        table[Opcode::PUSH7.as_usize()] = Some(consts::G_VERYLOW);
616        table[Opcode::PUSH8.as_usize()] = Some(consts::G_VERYLOW);
617        table[Opcode::PUSH9.as_usize()] = Some(consts::G_VERYLOW);
618        table[Opcode::PUSH10.as_usize()] = Some(consts::G_VERYLOW);
619        table[Opcode::PUSH11.as_usize()] = Some(consts::G_VERYLOW);
620        table[Opcode::PUSH12.as_usize()] = Some(consts::G_VERYLOW);
621        table[Opcode::PUSH13.as_usize()] = Some(consts::G_VERYLOW);
622        table[Opcode::PUSH14.as_usize()] = Some(consts::G_VERYLOW);
623        table[Opcode::PUSH15.as_usize()] = Some(consts::G_VERYLOW);
624        table[Opcode::PUSH16.as_usize()] = Some(consts::G_VERYLOW);
625        table[Opcode::PUSH17.as_usize()] = Some(consts::G_VERYLOW);
626        table[Opcode::PUSH18.as_usize()] = Some(consts::G_VERYLOW);
627        table[Opcode::PUSH19.as_usize()] = Some(consts::G_VERYLOW);
628        table[Opcode::PUSH20.as_usize()] = Some(consts::G_VERYLOW);
629        table[Opcode::PUSH21.as_usize()] = Some(consts::G_VERYLOW);
630        table[Opcode::PUSH22.as_usize()] = Some(consts::G_VERYLOW);
631        table[Opcode::PUSH23.as_usize()] = Some(consts::G_VERYLOW);
632        table[Opcode::PUSH24.as_usize()] = Some(consts::G_VERYLOW);
633        table[Opcode::PUSH25.as_usize()] = Some(consts::G_VERYLOW);
634        table[Opcode::PUSH26.as_usize()] = Some(consts::G_VERYLOW);
635        table[Opcode::PUSH27.as_usize()] = Some(consts::G_VERYLOW);
636        table[Opcode::PUSH28.as_usize()] = Some(consts::G_VERYLOW);
637        table[Opcode::PUSH29.as_usize()] = Some(consts::G_VERYLOW);
638        table[Opcode::PUSH30.as_usize()] = Some(consts::G_VERYLOW);
639        table[Opcode::PUSH31.as_usize()] = Some(consts::G_VERYLOW);
640        table[Opcode::PUSH32.as_usize()] = Some(consts::G_VERYLOW);
641        table[Opcode::DUP1.as_usize()] = Some(consts::G_VERYLOW);
642        table[Opcode::DUP2.as_usize()] = Some(consts::G_VERYLOW);
643        table[Opcode::DUP3.as_usize()] = Some(consts::G_VERYLOW);
644        table[Opcode::DUP4.as_usize()] = Some(consts::G_VERYLOW);
645        table[Opcode::DUP5.as_usize()] = Some(consts::G_VERYLOW);
646        table[Opcode::DUP6.as_usize()] = Some(consts::G_VERYLOW);
647        table[Opcode::DUP7.as_usize()] = Some(consts::G_VERYLOW);
648        table[Opcode::DUP8.as_usize()] = Some(consts::G_VERYLOW);
649        table[Opcode::DUP9.as_usize()] = Some(consts::G_VERYLOW);
650        table[Opcode::DUP10.as_usize()] = Some(consts::G_VERYLOW);
651        table[Opcode::DUP11.as_usize()] = Some(consts::G_VERYLOW);
652        table[Opcode::DUP12.as_usize()] = Some(consts::G_VERYLOW);
653        table[Opcode::DUP13.as_usize()] = Some(consts::G_VERYLOW);
654        table[Opcode::DUP14.as_usize()] = Some(consts::G_VERYLOW);
655        table[Opcode::DUP15.as_usize()] = Some(consts::G_VERYLOW);
656        table[Opcode::DUP16.as_usize()] = Some(consts::G_VERYLOW);
657        table[Opcode::SWAP1.as_usize()] = Some(consts::G_VERYLOW);
658        table[Opcode::SWAP2.as_usize()] = Some(consts::G_VERYLOW);
659        table[Opcode::SWAP3.as_usize()] = Some(consts::G_VERYLOW);
660        table[Opcode::SWAP4.as_usize()] = Some(consts::G_VERYLOW);
661        table[Opcode::SWAP5.as_usize()] = Some(consts::G_VERYLOW);
662        table[Opcode::SWAP6.as_usize()] = Some(consts::G_VERYLOW);
663        table[Opcode::SWAP7.as_usize()] = Some(consts::G_VERYLOW);
664        table[Opcode::SWAP8.as_usize()] = Some(consts::G_VERYLOW);
665        table[Opcode::SWAP9.as_usize()] = Some(consts::G_VERYLOW);
666        table[Opcode::SWAP10.as_usize()] = Some(consts::G_VERYLOW);
667        table[Opcode::SWAP11.as_usize()] = Some(consts::G_VERYLOW);
668        table[Opcode::SWAP12.as_usize()] = Some(consts::G_VERYLOW);
669        table[Opcode::SWAP13.as_usize()] = Some(consts::G_VERYLOW);
670        table[Opcode::SWAP14.as_usize()] = Some(consts::G_VERYLOW);
671        table[Opcode::SWAP15.as_usize()] = Some(consts::G_VERYLOW);
672        table[Opcode::SWAP16.as_usize()] = Some(consts::G_VERYLOW);
673
674        table[Opcode::MUL.as_usize()] = Some(consts::G_LOW);
675        table[Opcode::DIV.as_usize()] = Some(consts::G_LOW);
676        table[Opcode::SDIV.as_usize()] = Some(consts::G_LOW);
677        table[Opcode::MOD.as_usize()] = Some(consts::G_LOW);
678        table[Opcode::SMOD.as_usize()] = Some(consts::G_LOW);
679        table[Opcode::SIGNEXTEND.as_usize()] = Some(consts::G_LOW);
680
681        table[Opcode::ADDMOD.as_usize()] = Some(consts::G_MID);
682        table[Opcode::MULMOD.as_usize()] = Some(consts::G_MID);
683        table[Opcode::JUMP.as_usize()] = Some(consts::G_MID);
684
685        table[Opcode::JUMPI.as_usize()] = Some(consts::G_HIGH);
686        table[Opcode::JUMPDEST.as_usize()] = Some(consts::G_JUMPDEST);
687
688        table
689    };
690
691    TABLE[opcode.as_usize()]
692}
693
694/// Get and set warm address if it's not warmed.
695fn get_and_set_warm<H: Handler>(handler: &mut H, target: H160) -> (bool, Option<bool>) {
696    let delegated_designator_is_cold =
697        handler
698            .get_authority_target(target)
699            .map(|authority_target| {
700                if handler.is_cold(authority_target, None) {
701                    handler.warm_target((authority_target, None));
702                    true
703                } else {
704                    false
705                }
706            });
707    let target_is_cold = handler.is_cold(target, None);
708    if target_is_cold {
709        handler.warm_target((target, None));
710    }
711    (target_is_cold, delegated_designator_is_cold)
712}
713
714/// Get and set warm address if it's not warmed for non-delegated opcodes like `EXT*`.
715/// NOTE: Related to EIP-7702
716fn get_and_set_non_delegated_warm<H: Handler>(handler: &mut H, target: H160) -> bool {
717    let target_is_cold = handler.is_cold(target, None);
718    if target_is_cold {
719        handler.warm_target((target, None));
720    }
721    target_is_cold
722}
723
724/// Calculate the opcode cost.
725///
726/// # Errors
727/// Return `ExitError`
728#[allow(
729    clippy::nonminimal_bool,
730    clippy::cognitive_complexity,
731    clippy::too_many_lines,
732    clippy::match_same_arms
733)]
734pub fn dynamic_opcode_cost<H: Handler>(
735    address: H160,
736    opcode: Opcode,
737    stack: &Stack,
738    is_static: bool,
739    config: &Config,
740    handler: &mut H,
741) -> Result<(GasCost, Option<MemoryCost>), ExitError> {
742    let gas_cost = match opcode {
743        Opcode::RETURN => GasCost::Zero,
744
745        Opcode::MLOAD | Opcode::MSTORE | Opcode::MSTORE8 => GasCost::VeryLow,
746
747        Opcode::REVERT if config.has_revert => GasCost::Zero,
748        Opcode::REVERT => GasCost::Invalid(opcode),
749
750        Opcode::CHAINID if config.has_chain_id => GasCost::Base,
751        Opcode::CHAINID => GasCost::Invalid(opcode),
752
753        Opcode::SHL | Opcode::SHR | Opcode::SAR if config.has_bitwise_shifting => GasCost::VeryLow,
754        Opcode::SHL | Opcode::SHR | Opcode::SAR => GasCost::Invalid(opcode),
755
756        Opcode::SELFBALANCE if config.has_self_balance => GasCost::Low,
757        Opcode::SELFBALANCE => GasCost::Invalid(opcode),
758
759        Opcode::BASEFEE if config.has_base_fee => GasCost::Base,
760        Opcode::BASEFEE => GasCost::Invalid(opcode),
761
762        Opcode::BLOBBASEFEE if config.has_blob_base_fee => GasCost::Base,
763        Opcode::BLOBBASEFEE => GasCost::Invalid(opcode),
764
765        Opcode::BLOBHASH if config.has_shard_blob_transactions => GasCost::VeryLow,
766        Opcode::BLOBHASH => GasCost::Invalid(opcode),
767
768        Opcode::TLOAD if config.has_transient_storage => GasCost::WarmStorageRead,
769        Opcode::TLOAD => GasCost::Invalid(opcode),
770
771        Opcode::TSTORE if !is_static && config.has_transient_storage => GasCost::WarmStorageRead,
772        Opcode::TSTORE => GasCost::Invalid(opcode),
773
774        Opcode::MCOPY if config.has_mcopy => GasCost::VeryLowCopy {
775            len: stack.peek(2)?,
776        },
777        Opcode::MCOPY => GasCost::Invalid(opcode),
778
779        Opcode::EXTCODESIZE => {
780            let target = stack.peek_h256(0)?.into();
781            let target_is_cold = get_and_set_non_delegated_warm(handler, target);
782            GasCost::ExtCodeSize { target_is_cold }
783        }
784        Opcode::BALANCE => {
785            let target = stack.peek_h256(0)?.into();
786            let target_is_cold = handler.is_cold(target, None);
787            if target_is_cold {
788                handler.warm_target((target, None));
789            }
790            GasCost::Balance { target_is_cold }
791        }
792        Opcode::BLOCKHASH => GasCost::BlockHash,
793
794        Opcode::EXTCODEHASH if config.has_ext_code_hash => {
795            let target = stack.peek_h256(0)?.into();
796            let target_is_cold = get_and_set_non_delegated_warm(handler, target);
797            GasCost::ExtCodeHash { target_is_cold }
798        }
799        Opcode::EXTCODEHASH => GasCost::Invalid(opcode),
800
801        Opcode::CALLCODE => {
802            let target = stack.peek_h256(1)?.into();
803            let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target);
804            GasCost::CallCode {
805                value: stack.peek(2)?,
806                gas: stack.peek(0)?,
807                target_is_cold,
808                delegated_designator_is_cold,
809                target_exists: {
810                    handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
811                    handler.exists(target)
812                },
813            }
814        }
815        Opcode::STATICCALL => {
816            let target = stack.peek_h256(1)?.into();
817            let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target);
818            GasCost::StaticCall {
819                gas: stack.peek(0)?,
820                target_is_cold,
821                delegated_designator_is_cold,
822                target_exists: {
823                    handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
824                    handler.exists(target)
825                },
826            }
827        }
828        Opcode::SHA3 => GasCost::Sha3 {
829            len: stack.peek(1)?,
830        },
831        Opcode::EXTCODECOPY => {
832            let target = stack.peek_h256(0)?.into();
833            let target_is_cold = get_and_set_non_delegated_warm(handler, target);
834            GasCost::ExtCodeCopy {
835                target_is_cold,
836                len: stack.peek(3)?,
837            }
838        }
839        Opcode::CALLDATACOPY | Opcode::CODECOPY => GasCost::VeryLowCopy {
840            len: stack.peek(2)?,
841        },
842        Opcode::EXP => GasCost::Exp {
843            power: stack.peek(1)?,
844        },
845        Opcode::SLOAD => {
846            let index = stack.peek_h256(0)?;
847            let target_is_cold = handler.is_cold(address, Some(index));
848            if target_is_cold {
849                handler.warm_target((address, Some(index)));
850            }
851            GasCost::SLoad { target_is_cold }
852        }
853
854        Opcode::DELEGATECALL if config.has_delegate_call => {
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::DelegateCall {
858                gas: stack.peek(0)?,
859                target_is_cold,
860                delegated_designator_is_cold,
861                target_exists: {
862                    handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
863                    handler.exists(target)
864                },
865            }
866        }
867        Opcode::DELEGATECALL => GasCost::Invalid(opcode),
868
869        Opcode::RETURNDATASIZE if config.has_return_data => GasCost::Base,
870        Opcode::RETURNDATACOPY if config.has_return_data => GasCost::VeryLowCopy {
871            len: stack.peek(2)?,
872        },
873        Opcode::RETURNDATASIZE | Opcode::RETURNDATACOPY => GasCost::Invalid(opcode),
874
875        Opcode::SSTORE if !is_static => {
876            let index = stack.peek_h256(0)?;
877            let value = stack.peek_h256(1)?;
878            let target_is_cold = handler.is_cold(address, Some(index));
879            if target_is_cold {
880                handler.warm_target((address, Some(index)));
881            }
882            GasCost::SStore {
883                original: handler.original_storage(address, index),
884                current: handler.storage(address, index),
885                new: value,
886                target_is_cold,
887            }
888        }
889        Opcode::LOG0 if !is_static => GasCost::Log {
890            n: 0,
891            len: stack.peek(1)?,
892        },
893        Opcode::LOG1 if !is_static => GasCost::Log {
894            n: 1,
895            len: stack.peek(1)?,
896        },
897        Opcode::LOG2 if !is_static => GasCost::Log {
898            n: 2,
899            len: stack.peek(1)?,
900        },
901        Opcode::LOG3 if !is_static => GasCost::Log {
902            n: 3,
903            len: stack.peek(1)?,
904        },
905        Opcode::LOG4 if !is_static => GasCost::Log {
906            n: 4,
907            len: stack.peek(1)?,
908        },
909        Opcode::CREATE if !is_static => GasCost::Create,
910        Opcode::CREATE2 if !is_static && config.has_create2 => GasCost::Create2 {
911            len: stack.peek(2)?,
912        },
913        Opcode::SELFDESTRUCT if !is_static => {
914            let target = stack.peek_h256(0)?.into();
915            let target_is_cold = handler.is_cold(target, None);
916            if target_is_cold {
917                handler.warm_target((target, None));
918            }
919            GasCost::Suicide {
920                value: handler.balance(address),
921                target_is_cold,
922                target_exists: {
923                    handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
924                    handler.exists(target)
925                },
926                already_removed: handler.deleted(address),
927            }
928        }
929        Opcode::CALL if !is_static || (is_static && stack.peek(2)? == U256_ZERO) => {
930            let target = stack.peek_h256(1)?.into();
931            let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target);
932            GasCost::Call {
933                value: stack.peek(2)?,
934                gas: stack.peek(0)?,
935                target_is_cold,
936                delegated_designator_is_cold,
937                target_exists: {
938                    handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
939                    handler.exists(target)
940                },
941            }
942        }
943
944        Opcode::PUSH0 if config.has_push0 => GasCost::Base,
945
946        _ => GasCost::Invalid(opcode),
947    };
948
949    let memory_cost = match opcode {
950        Opcode::SHA3
951        | Opcode::RETURN
952        | Opcode::REVERT
953        | Opcode::LOG0
954        | Opcode::LOG1
955        | Opcode::LOG2
956        | Opcode::LOG3
957        | Opcode::LOG4 => Some(peek_memory_cost(stack, 0, 1)?),
958
959        Opcode::CODECOPY | Opcode::CALLDATACOPY | Opcode::RETURNDATACOPY => {
960            Some(peek_memory_cost(stack, 0, 2)?)
961        }
962
963        Opcode::EXTCODECOPY => Some(peek_memory_cost(stack, 1, 3)?),
964
965        Opcode::MLOAD | Opcode::MSTORE => Some(MemoryCost {
966            offset: stack.peek_usize(0)?,
967            len: 32,
968        }),
969
970        Opcode::MCOPY => {
971            let len = stack.peek_usize(2)?;
972            if len == 0 {
973                None
974            } else {
975                Some(MemoryCost {
976                    offset: {
977                        let src = stack.peek_usize(0)?;
978                        let dst = stack.peek_usize(1)?;
979                        max(src, dst)
980                    },
981                    len,
982                })
983            }
984        }
985
986        Opcode::MSTORE8 => Some(MemoryCost {
987            offset: stack.peek_usize(0)?,
988            len: 1,
989        }),
990
991        Opcode::CREATE | Opcode::CREATE2 => Some(peek_memory_cost(stack, 1, 2)?),
992
993        Opcode::CALL | Opcode::CALLCODE => {
994            Some(peek_memory_cost(stack, 3, 4)?.join(peek_memory_cost(stack, 5, 6)?))
995        }
996
997        Opcode::DELEGATECALL | Opcode::STATICCALL => {
998            Some(peek_memory_cost(stack, 2, 3)?.join(peek_memory_cost(stack, 4, 5)?))
999        }
1000
1001        _ => None,
1002    };
1003
1004    Ok((gas_cost, memory_cost))
1005}
1006
1007fn peek_memory_cost(
1008    stack: &Stack,
1009    offset_index: usize,
1010    len_index: usize,
1011) -> Result<MemoryCost, ExitError> {
1012    let len = stack.peek_usize(len_index)?;
1013
1014    if len == 0 {
1015        return Ok(MemoryCost {
1016            offset: usize::MAX,
1017            len,
1018        });
1019    }
1020
1021    let offset = stack.peek_usize(offset_index)?;
1022    Ok(MemoryCost { offset, len })
1023}
1024
1025/// Holds the gas consumption for a Gasometer instance.
1026#[derive(Clone, Debug)]
1027struct Inner<'config> {
1028    memory_gas: u64,
1029    used_gas: u64,
1030    refunded_gas: i64,
1031    config: &'config Config,
1032    floor_gas: u64,
1033}
1034
1035impl Inner<'_> {
1036    fn memory_gas(&self, memory: MemoryCost) -> Result<u64, ExitError> {
1037        let from = memory.offset;
1038        let len = memory.len;
1039
1040        if len == 0 {
1041            return Ok(self.memory_gas);
1042        }
1043
1044        let end = from.checked_add(len).ok_or(ExitError::OutOfGas)?;
1045
1046        let rem = end % 32;
1047        let new = if rem == 0 { end / 32 } else { end / 32 + 1 };
1048
1049        Ok(max(self.memory_gas, memory::memory_gas(new)?))
1050    }
1051
1052    fn extra_check(&self, cost: GasCost, after_gas: u64) -> Result<(), ExitError> {
1053        match cost {
1054            GasCost::Call { gas, .. }
1055            | GasCost::CallCode { gas, .. }
1056            | GasCost::DelegateCall { gas, .. }
1057            | GasCost::StaticCall { gas, .. } => {
1058                costs::call_extra_check(gas, after_gas, self.config)
1059            }
1060            _ => Ok(()),
1061        }
1062    }
1063
1064    /// Returns the gas cost numerical value.
1065    #[allow(clippy::too_many_lines)]
1066    fn gas_cost(&self, cost: GasCost, gas: u64) -> Result<u64, ExitError> {
1067        Ok(match cost {
1068            GasCost::Call {
1069                value,
1070                target_is_cold,
1071                delegated_designator_is_cold,
1072                target_exists,
1073                ..
1074            } => costs::call_cost(
1075                value,
1076                target_is_cold,
1077                delegated_designator_is_cold,
1078                true,
1079                true,
1080                !target_exists,
1081                self.config,
1082            ),
1083            GasCost::CallCode {
1084                value,
1085                target_is_cold,
1086                delegated_designator_is_cold,
1087                target_exists,
1088                ..
1089            } => costs::call_cost(
1090                value,
1091                target_is_cold,
1092                delegated_designator_is_cold,
1093                true,
1094                false,
1095                !target_exists,
1096                self.config,
1097            ),
1098            GasCost::DelegateCall {
1099                target_is_cold,
1100                delegated_designator_is_cold,
1101                target_exists,
1102                ..
1103            } => costs::call_cost(
1104                U256_ZERO,
1105                target_is_cold,
1106                delegated_designator_is_cold,
1107                false,
1108                false,
1109                !target_exists,
1110                self.config,
1111            ),
1112            GasCost::StaticCall {
1113                target_is_cold,
1114                delegated_designator_is_cold,
1115                target_exists,
1116                ..
1117            } => costs::call_cost(
1118                U256_ZERO,
1119                target_is_cold,
1120                delegated_designator_is_cold,
1121                false,
1122                true,
1123                !target_exists,
1124                self.config,
1125            ),
1126
1127            GasCost::Suicide {
1128                value,
1129                target_is_cold,
1130                target_exists,
1131                ..
1132            } => costs::suicide_cost(value, target_is_cold, target_exists, self.config),
1133            GasCost::SStore {
1134                original,
1135                current,
1136                new,
1137                target_is_cold,
1138            } => costs::sstore_cost(original, current, new, gas, target_is_cold, self.config)?,
1139
1140            GasCost::Sha3 { len } => costs::sha3_cost(len)?,
1141            GasCost::Log { n, len } => costs::log_cost(n, len)?,
1142            GasCost::VeryLowCopy { len } => costs::verylowcopy_cost(len)?,
1143            GasCost::Exp { power } => costs::exp_cost(power, self.config)?,
1144            GasCost::Create => u64::from(consts::G_CREATE),
1145            GasCost::Create2 { len } => costs::create2_cost(len)?,
1146            GasCost::SLoad { target_is_cold } => costs::sload_cost(target_is_cold, self.config),
1147
1148            GasCost::Zero => u64::from(consts::G_ZERO),
1149            GasCost::Base => u64::from(consts::G_BASE),
1150            GasCost::VeryLow => u64::from(consts::G_VERYLOW),
1151            GasCost::Low => u64::from(consts::G_LOW),
1152            GasCost::Invalid(opcode) => return Err(ExitError::InvalidCode(opcode)),
1153
1154            GasCost::ExtCodeSize { target_is_cold } => costs::non_delegated_access_cost(
1155                target_is_cold,
1156                self.config.gas_ext_code,
1157                self.config,
1158            ),
1159            GasCost::ExtCodeCopy {
1160                target_is_cold,
1161                len,
1162            } => costs::ext_codecopy_cost(len, target_is_cold, self.config)?,
1163            GasCost::ExtCodeHash { target_is_cold } => costs::non_delegated_access_cost(
1164                target_is_cold,
1165                self.config.gas_ext_code_hash,
1166                self.config,
1167            ),
1168
1169            GasCost::Balance { target_is_cold } => costs::non_delegated_access_cost(
1170                target_is_cold,
1171                self.config.gas_balance,
1172                self.config,
1173            ),
1174            GasCost::BlockHash => u64::from(consts::G_BLOCKHASH),
1175            GasCost::WarmStorageRead => costs::storage_read_warm(self.config),
1176        })
1177    }
1178
1179    fn gas_refund(&self, cost: GasCost) -> i64 {
1180        match cost {
1181            _ if self.config.estimate => 0,
1182
1183            GasCost::SStore {
1184                original,
1185                current,
1186                new,
1187                ..
1188            } => costs::sstore_refund(original, current, new, self.config),
1189            GasCost::Suicide {
1190                already_removed, ..
1191            } if !self.config.decrease_clears_refund => costs::suicide_refund(already_removed),
1192            _ => 0,
1193        }
1194    }
1195}
1196
1197/// Gas cost.
1198#[derive(Debug, Clone, Copy)]
1199pub enum GasCost {
1200    /// Zero gas cost.
1201    Zero,
1202    /// Base gas cost.
1203    Base,
1204    /// Very low gas cost.
1205    VeryLow,
1206    /// Low gas cost.
1207    Low,
1208    /// Fail the gasometer.
1209    Invalid(Opcode),
1210
1211    /// Gas cost for `EXTCODESIZE`.
1212    ExtCodeSize {
1213        /// True if address has not been previously accessed in this transaction
1214        target_is_cold: bool,
1215    },
1216    /// Gas cost for `BALANCE`.
1217    Balance {
1218        /// True if address has not been previously accessed in this transaction
1219        target_is_cold: bool,
1220    },
1221    /// Gas cost for `BLOCKHASH`.
1222    BlockHash,
1223    /// Gas cost for `EXTBLOCKHASH`.
1224    ExtCodeHash {
1225        /// True if address has not been previously accessed in this transaction
1226        target_is_cold: bool,
1227    },
1228
1229    /// Gas cost for `CALL`.
1230    Call {
1231        /// Call value.
1232        value: U256,
1233        /// Call gas.
1234        gas: U256,
1235        /// True if target has not been previously accessed in this transaction
1236        target_is_cold: bool,
1237        /// True if delegated designator of authority has not been previously accessed in this transaction (EIP-7702)
1238        delegated_designator_is_cold: Option<bool>,
1239        /// Whether the target exists.
1240        target_exists: bool,
1241    },
1242    /// Gas cost for `CALLCODE`.
1243    CallCode {
1244        /// Call value.
1245        value: U256,
1246        /// Call gas.
1247        gas: U256,
1248        /// True if target has not been previously accessed in this transaction
1249        target_is_cold: bool,
1250        /// True if delegated designator of authority has not been previously accessed in this transaction (EIP-7702)
1251        delegated_designator_is_cold: Option<bool>,
1252        /// Whether the target exists.
1253        target_exists: bool,
1254    },
1255    /// Gas cost for `DELEGATECALL`.
1256    DelegateCall {
1257        /// Call gas.
1258        gas: U256,
1259        /// True if target has not been previously accessed in this transaction
1260        target_is_cold: bool,
1261        /// True if delegated designator of authority has not been previously accessed in this transaction (EIP-7702)
1262        delegated_designator_is_cold: Option<bool>,
1263        /// Whether the target exists.
1264        target_exists: bool,
1265    },
1266    /// Gas cost for `STATICCALL`.
1267    StaticCall {
1268        /// Call gas.
1269        gas: U256,
1270        /// True if target has not been previously accessed in this transaction
1271        target_is_cold: bool,
1272        /// True if delegated designator of authority has not been previously accessed in this transaction (EIP-7702)
1273        delegated_designator_is_cold: Option<bool>,
1274        /// Whether the target exists.
1275        target_exists: bool,
1276    },
1277    /// Gas cost for `SUICIDE`.
1278    Suicide {
1279        /// Value.
1280        value: U256,
1281        /// True if target has not been previously accessed in this transaction
1282        target_is_cold: bool,
1283        /// Whether the target exists.
1284        target_exists: bool,
1285        /// Whether the target has already been removed.
1286        already_removed: bool,
1287    },
1288    /// Gas cost for `SSTORE`.
1289    SStore {
1290        /// Original value.
1291        original: H256,
1292        /// Current value.
1293        current: H256,
1294        /// New value.
1295        new: H256,
1296        /// True if target has not been previously accessed in this transaction
1297        target_is_cold: bool,
1298    },
1299    /// Gas cost for `SHA3`.
1300    Sha3 {
1301        /// Length of the data.
1302        len: U256,
1303    },
1304    /// Gas cost for `LOG`.
1305    Log {
1306        /// Topic length.
1307        n: u8,
1308        /// Data length.
1309        len: U256,
1310    },
1311    /// Gas cost for `EXTCODECOPY`.
1312    ExtCodeCopy {
1313        /// True if target has not been previously accessed in this transaction
1314        target_is_cold: bool,
1315        /// Length.
1316        len: U256,
1317    },
1318    /// Gas cost for some copy opcodes that is documented as `VERYLOW`.
1319    VeryLowCopy {
1320        /// Length.
1321        len: U256,
1322    },
1323    /// Gas cost for `EXP`.
1324    Exp {
1325        /// Power of `EXP`.
1326        power: U256,
1327    },
1328    /// Gas cost for `CREATE`.
1329    Create,
1330    /// Gas cost for `CREATE2`.
1331    Create2 {
1332        /// Length.
1333        len: U256,
1334    },
1335    /// Gas cost for `SLOAD`.
1336    SLoad {
1337        /// True if target has not been previously accessed in this transaction
1338        target_is_cold: bool,
1339    },
1340    WarmStorageRead,
1341}
1342
1343/// Storage opcode will access. Used for tracking accessed storage (EIP-2929).
1344#[derive(Debug, Clone, Copy)]
1345pub enum StorageTarget {
1346    /// No storage access
1347    None,
1348    /// Accessing address
1349    Address(H160),
1350    /// Accessing storage slot within an address
1351    Slot(H160, H256),
1352}
1353
1354/// Memory cost.
1355#[derive(Debug, Clone, Copy)]
1356pub struct MemoryCost {
1357    /// Affected memory offset.
1358    pub offset: usize,
1359    /// Affected length.
1360    pub len: usize,
1361}
1362
1363/// Transaction cost.
1364#[derive(Debug, Clone, Copy)]
1365pub enum TransactionCost {
1366    /// Call transaction cost.
1367    Call {
1368        /// Length of zeros in transaction data.
1369        zero_data_len: usize,
1370        /// Length of non-zeros in transaction data.
1371        non_zero_data_len: usize,
1372        /// Number of addresses in transaction access list (see EIP-2930)
1373        access_list_address_len: usize,
1374        /// Total number of storage keys in transaction access list (see EIP-2930)
1375        access_list_storage_len: usize,
1376        /// Number of authorities in transaction authorization list (see EIP-7702)
1377        authorization_list_len: usize,
1378    },
1379    /// Create transaction cost.
1380    Create {
1381        /// Length of zeros in transaction data.
1382        zero_data_len: usize,
1383        /// Length of non-zeros in transaction data.
1384        non_zero_data_len: usize,
1385        /// Number of addresses in transaction access list (see EIP-2930)
1386        access_list_address_len: usize,
1387        /// Total number of storage keys in transaction access list (see EIP-2930)
1388        access_list_storage_len: usize,
1389        /// Cost of initcode = 2 * ceil(len(initcode) / 32) (see EIP-3860)
1390        initcode_cost: u64,
1391    },
1392}
1393
1394impl MemoryCost {
1395    /// Join two memory cost together.
1396    #[must_use]
1397    pub const fn join(self, other: Self) -> Self {
1398        if self.len == 0 {
1399            return other;
1400        }
1401
1402        if other.len == 0 {
1403            return self;
1404        }
1405
1406        let self_end = self.offset.saturating_add(self.len);
1407        let other_end = other.offset.saturating_add(other.len);
1408
1409        if self_end >= other_end {
1410            self
1411        } else {
1412            other
1413        }
1414    }
1415}