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 memory gas.
53    #[inline]
54    pub fn memory(&self) -> &MemoryGas {
55        &self.memory
56    }
57
58    /// Returns the memory gas.
59    #[inline]
60    pub fn memory_mut(&mut self) -> &mut MemoryGas {
61        &mut self.memory
62    }
63
64    /// Returns the total amount of gas that was refunded.
65    #[inline]
66    pub const fn refunded(&self) -> i64 {
67        self.refunded
68    }
69
70    /// Returns the total amount of gas spent.
71    #[inline]
72    pub const fn spent(&self) -> u64 {
73        self.limit - self.remaining
74    }
75
76    /// Returns the total amount of gas spent, minus the refunded gas.
77    #[inline]
78    pub const fn spent_sub_refunded(&self) -> u64 {
79        self.spent().saturating_sub(self.refunded as u64)
80    }
81
82    /// Returns the amount of gas remaining.
83    #[inline]
84    pub const fn remaining(&self) -> u64 {
85        self.remaining
86    }
87
88    /// Return remaining gas after subtracting 63/64 parts.
89    pub const fn remaining_63_of_64_parts(&self) -> u64 {
90        self.remaining - self.remaining / 64
91    }
92
93    /// Erases a gas cost from the totals.
94    #[inline]
95    pub fn erase_cost(&mut self, returned: u64) {
96        self.remaining += returned;
97    }
98
99    /// Spends all remaining gas.
100    #[inline]
101    pub fn spend_all(&mut self) {
102        self.remaining = 0;
103    }
104
105    /// Records a refund value.
106    ///
107    /// `refund` can be negative but `self.refunded` should always be positive
108    /// at the end of transact.
109    #[inline]
110    pub fn record_refund(&mut self, refund: i64) {
111        self.refunded += refund;
112    }
113
114    /// Set a refund value for final refund.
115    ///
116    /// Max refund value is limited to Nth part (depending of fork) of gas spend.
117    ///
118    /// Related to EIP-3529: Reduction in refunds
119    #[inline]
120    pub fn set_final_refund(&mut self, is_london: bool) {
121        let max_refund_quotient = if is_london { 5 } else { 2 };
122        self.refunded = (self.refunded() as u64).min(self.spent() / max_refund_quotient) as i64;
123    }
124
125    /// Set a refund value. This overrides the current refund value.
126    #[inline]
127    pub fn set_refund(&mut self, refund: i64) {
128        self.refunded = refund;
129    }
130
131    /// Set a spent value. This overrides the current spent value.
132    #[inline]
133    pub fn set_spent(&mut self, spent: u64) {
134        self.remaining = self.limit.saturating_sub(spent);
135    }
136
137    /// Records an explicit cost.
138    ///
139    /// Returns `false` if the gas limit is exceeded.
140    #[inline]
141    #[must_use = "prefer using `gas!` instead to return an out-of-gas error on failure"]
142    pub fn record_cost(&mut self, cost: u64) -> bool {
143        if let Some(new_remaining) = self.remaining.checked_sub(cost) {
144            self.remaining = new_remaining;
145            return true;
146        }
147        false
148    }
149
150    /// Record memory expansion
151    #[inline]
152    #[must_use = "internally uses record_cost that flags out of gas error"]
153    pub fn record_memory_expansion(&mut self, new_len: usize) -> MemoryExtensionResult {
154        let Some(additional_cost) = self.memory.record_new_len(new_len) else {
155            return MemoryExtensionResult::Same;
156        };
157
158        if !self.record_cost(additional_cost) {
159            return MemoryExtensionResult::OutOfGas;
160        }
161
162        MemoryExtensionResult::Extended
163    }
164}
165
166pub enum MemoryExtensionResult {
167    /// Memory was extended.
168    Extended,
169    /// Memory size stayed the same.
170    Same,
171    /// Not enough gas to extend memory.
172    OutOfGas,
173}
174
175/// Utility struct that speeds up calculation of memory expansion
176/// It contains the current memory length and its memory expansion cost.
177///
178/// It allows us to split gas accounting from memory structure.
179#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
180#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
181pub struct MemoryGas {
182    /// Current memory length
183    pub words_num: usize,
184    /// Current memory expansion cost
185    pub expansion_cost: u64,
186}
187
188impl MemoryGas {
189    pub const fn new() -> Self {
190        Self {
191            words_num: 0,
192            expansion_cost: 0,
193        }
194    }
195
196    #[inline]
197    pub fn record_new_len(&mut self, new_num: usize) -> Option<u64> {
198        if new_num <= self.words_num {
199            return None;
200        }
201        self.words_num = new_num;
202        let mut cost = crate::gas::calc::memory_gas(new_num);
203        core::mem::swap(&mut self.expansion_cost, &mut cost);
204        // Safe to subtract because we know that new_len > length
205        // Notice the swap above.
206        Some(self.expansion_cost - cost)
207    }
208}