revm_interpreter/
gas.rs

1//! EVM gas calculation utilities.
2
3mod calc;
4mod constants;
5
6pub use calc::*;
7pub use constants::*;
8
9/// Represents the state of gas during execution.
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct Gas {
13    /// The initial gas limit. This is constant throughout execution.
14    limit: u64,
15    /// The remaining gas.
16    remaining: u64,
17    /// Refunded gas. This is used only at the end of execution.
18    refunded: i64,
19    /// Memoisation of values for memory expansion cost.
20    memory: MemoryGas,
21}
22
23impl Gas {
24    /// Creates a new `Gas` struct with the given gas limit.
25    #[inline]
26    pub const fn new(limit: u64) -> Self {
27        Self {
28            limit,
29            remaining: limit,
30            refunded: 0,
31            memory: MemoryGas::new(),
32        }
33    }
34
35    /// Creates a new `Gas` struct with the given gas limit, but without any gas remaining.
36    #[inline]
37    pub const fn new_spent(limit: u64) -> Self {
38        Self {
39            limit,
40            remaining: 0,
41            refunded: 0,
42            memory: MemoryGas::new(),
43        }
44    }
45
46    /// Returns the gas limit.
47    #[inline]
48    pub const fn limit(&self) -> u64 {
49        self.limit
50    }
51
52    /// Returns the **last** memory expansion cost.
53    #[inline]
54    #[deprecated = "memory expansion cost is not tracked anymore; \
55                    calculate it using `SharedMemory::current_expansion_cost` instead"]
56    #[doc(hidden)]
57    pub const fn memory(&self) -> u64 {
58        0
59    }
60
61    /// Returns the total amount of gas that was refunded.
62    #[inline]
63    pub const fn refunded(&self) -> i64 {
64        self.refunded
65    }
66
67    /// Returns the total amount of gas spent.
68    #[inline]
69    pub const fn spent(&self) -> u64 {
70        self.limit - self.remaining
71    }
72
73    /// Returns the total amount of gas spent, minus the refunded gas.
74    #[inline]
75    pub const fn spent_sub_refunded(&self) -> u64 {
76        self.spent().saturating_sub(self.refunded as u64)
77    }
78
79    /// Returns the amount of gas remaining.
80    #[inline]
81    pub const fn remaining(&self) -> u64 {
82        self.remaining
83    }
84
85    /// Return remaining gas after subtracting 63/64 parts.
86    pub const fn remaining_63_of_64_parts(&self) -> u64 {
87        self.remaining - self.remaining / 64
88    }
89
90    /// Erases a gas cost from the totals.
91    #[inline]
92    pub fn erase_cost(&mut self, returned: u64) {
93        self.remaining += returned;
94    }
95
96    /// Spends all remaining gas.
97    #[inline]
98    pub fn spend_all(&mut self) {
99        self.remaining = 0;
100    }
101
102    /// Records a refund value.
103    ///
104    /// `refund` can be negative but `self.refunded` should always be positive
105    /// at the end of transact.
106    #[inline]
107    pub fn record_refund(&mut self, refund: i64) {
108        self.refunded += refund;
109    }
110
111    /// Set a refund value for final refund.
112    ///
113    /// Max refund value is limited to Nth part (depending of fork) of gas spend.
114    ///
115    /// Related to EIP-3529: Reduction in refunds
116    #[inline]
117    pub fn set_final_refund(&mut self, is_london: bool) {
118        let max_refund_quotient = if is_london { 5 } else { 2 };
119        self.refunded = (self.refunded() as u64).min(self.spent() / max_refund_quotient) as i64;
120    }
121
122    /// Set a refund value. This overrides the current refund value.
123    #[inline]
124    pub fn set_refund(&mut self, refund: i64) {
125        self.refunded = refund;
126    }
127
128    /// Set a spent value. This overrides the current spent value.
129    #[inline]
130    pub fn set_spent(&mut self, spent: u64) {
131        self.remaining = self.limit.saturating_sub(spent);
132    }
133
134    /// Records an explicit cost.
135    ///
136    /// Returns `false` if the gas limit is exceeded.
137    #[inline]
138    #[must_use = "prefer using `gas!` instead to return an out-of-gas error on failure"]
139    pub fn record_cost(&mut self, cost: u64) -> bool {
140        if let Some(new_remaining) = self.remaining.checked_sub(cost) {
141            self.remaining = new_remaining;
142            return true;
143        }
144        false
145    }
146
147    /// Record memory expansion
148    #[inline]
149    #[must_use = "internally uses record_cost that flags out of gas error"]
150    pub fn record_memory_expansion(&mut self, new_len: usize) -> MemoryExtensionResult {
151        let Some(additional_cost) = self.memory.record_new_len(new_len) else {
152            return MemoryExtensionResult::Same;
153        };
154
155        if !self.record_cost(additional_cost) {
156            return MemoryExtensionResult::OutOfGas;
157        }
158
159        MemoryExtensionResult::Extended
160    }
161}
162
163pub enum MemoryExtensionResult {
164    /// Memory was extended.
165    Extended,
166    /// Memory size stayed the same.
167    Same,
168    /// Not enough gas to extend memory.s
169    OutOfGas,
170}
171
172/// Utility struct that speeds up calculation of memory expansion
173/// It contains the current memory length and its memory expansion cost.
174///
175/// It allows us to split gas accounting from memory structure.
176#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
177#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
178pub struct MemoryGas {
179    /// Current memory length
180    pub words_num: usize,
181    /// Current memory expansion cost
182    pub expansion_cost: u64,
183}
184
185impl MemoryGas {
186    pub const fn new() -> Self {
187        Self {
188            words_num: 0,
189            expansion_cost: 0,
190        }
191    }
192
193    #[inline]
194    pub fn record_new_len(&mut self, new_num: usize) -> Option<u64> {
195        if new_num <= self.words_num {
196            return None;
197        }
198        self.words_num = new_num;
199        let mut cost = crate::gas::calc::memory_gas(new_num);
200        core::mem::swap(&mut self.expansion_cost, &mut cost);
201        // Safe to subtract because we know that new_len > length
202        // Notice the swap above.
203        Some(self.expansion_cost - cost)
204    }
205}