revm_interpreter/gas/
calc.rs
1use revm_primitives::eip7702;
2
3use super::constants::*;
4use crate::{
5 num_words,
6 primitives::{AccessListItem, SpecId, U256},
7 AccountLoad, Eip7702CodeLoad, SStoreResult, SelfDestructResult, StateLoad,
8};
9
10macro_rules! tri {
12 ($e:expr) => {
13 match $e {
14 Some(v) => v,
15 None => return None,
16 }
17 };
18}
19
20#[allow(clippy::collapsible_else_if)]
22#[inline]
23pub fn sstore_refund(spec_id: SpecId, vals: &SStoreResult) -> i64 {
24 if spec_id.is_enabled_in(SpecId::ISTANBUL) {
25 let sstore_clears_schedule = if spec_id.is_enabled_in(SpecId::LONDON) {
27 (SSTORE_RESET - COLD_SLOAD_COST + ACCESS_LIST_STORAGE_KEY) as i64
28 } else {
29 REFUND_SSTORE_CLEARS
30 };
31 if vals.is_new_eq_present() {
32 0
33 } else {
34 if vals.is_original_eq_present() && vals.is_new_zero() {
35 sstore_clears_schedule
36 } else {
37 let mut refund = 0;
38
39 if !vals.is_original_zero() {
40 if vals.is_present_zero() {
41 refund -= sstore_clears_schedule;
42 } else if vals.is_new_zero() {
43 refund += sstore_clears_schedule;
44 }
45 }
46
47 if vals.is_original_eq_new() {
48 let (gas_sstore_reset, gas_sload) = if spec_id.is_enabled_in(SpecId::BERLIN) {
49 (SSTORE_RESET - COLD_SLOAD_COST, WARM_STORAGE_READ_COST)
50 } else {
51 (SSTORE_RESET, sload_cost(spec_id, false))
52 };
53 if vals.is_original_zero() {
54 refund += (SSTORE_SET - gas_sload) as i64;
55 } else {
56 refund += (gas_sstore_reset - gas_sload) as i64;
57 }
58 }
59
60 refund
61 }
62 }
63 } else {
64 if !vals.is_present_zero() && vals.is_new_zero() {
65 REFUND_SSTORE_CLEARS
66 } else {
67 0
68 }
69 }
70}
71
72#[inline]
74pub const fn create2_cost(len: u64) -> Option<u64> {
75 CREATE.checked_add(tri!(cost_per_word(len, KECCAK256WORD)))
76}
77
78#[inline]
79const fn log2floor(value: U256) -> u64 {
80 let mut l: u64 = 256;
81 let mut i = 3;
82 loop {
83 if value.as_limbs()[i] == 0u64 {
84 l -= 64;
85 } else {
86 l -= value.as_limbs()[i].leading_zeros() as u64;
87 if l == 0 {
88 return l;
89 } else {
90 return l - 1;
91 }
92 }
93 if i == 0 {
94 break;
95 }
96 i -= 1;
97 }
98 l
99}
100
101#[inline]
103pub fn exp_cost(spec_id: SpecId, power: U256) -> Option<u64> {
104 if power.is_zero() {
105 Some(EXP)
106 } else {
107 let gas_byte = U256::from(if spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
109 50
110 } else {
111 10
112 });
113 let gas = U256::from(EXP)
114 .checked_add(gas_byte.checked_mul(U256::from(log2floor(power) / 8 + 1))?)?;
115
116 u64::try_from(gas).ok()
117 }
118}
119
120#[inline]
122pub const fn verylowcopy_cost(len: u64) -> Option<u64> {
123 VERYLOW.checked_add(tri!(cost_per_word(len, COPY)))
124}
125
126#[inline]
128pub const fn extcodecopy_cost(spec_id: SpecId, len: u64, load: Eip7702CodeLoad<()>) -> Option<u64> {
129 let base_gas = if spec_id.is_enabled_in(SpecId::BERLIN) {
130 warm_cold_cost_with_delegation(load)
131 } else if spec_id.is_enabled_in(SpecId::TANGERINE) {
132 700
133 } else {
134 20
135 };
136 base_gas.checked_add(tri!(cost_per_word(len, COPY)))
137}
138
139#[inline]
141pub const fn log_cost(n: u8, len: u64) -> Option<u64> {
142 tri!(LOG.checked_add(tri!(LOGDATA.checked_mul(len)))).checked_add(LOGTOPIC * n as u64)
143}
144
145#[inline]
147pub const fn keccak256_cost(len: u64) -> Option<u64> {
148 KECCAK256.checked_add(tri!(cost_per_word(len, KECCAK256WORD)))
149}
150
151#[inline]
153pub const fn cost_per_word(len: u64, multiple: u64) -> Option<u64> {
154 multiple.checked_mul(num_words(len))
155}
156
157#[inline]
163pub const fn initcode_cost(len: u64) -> u64 {
164 let Some(cost) = cost_per_word(len, INITCODE_WORD_COST) else {
165 panic!("initcode cost overflow")
166 };
167 cost
168}
169
170#[inline]
172pub const fn sload_cost(spec_id: SpecId, is_cold: bool) -> u64 {
173 if spec_id.is_enabled_in(SpecId::BERLIN) {
174 if is_cold {
175 COLD_SLOAD_COST
176 } else {
177 WARM_STORAGE_READ_COST
178 }
179 } else if spec_id.is_enabled_in(SpecId::ISTANBUL) {
180 INSTANBUL_SLOAD_GAS
182 } else if spec_id.is_enabled_in(SpecId::TANGERINE) {
183 200
185 } else {
186 50
187 }
188}
189
190#[inline]
192pub fn sstore_cost(spec_id: SpecId, vals: &SStoreResult, gas: u64, is_cold: bool) -> Option<u64> {
193 if spec_id.is_enabled_in(SpecId::ISTANBUL) && gas <= CALL_STIPEND {
195 return None;
196 }
197
198 if spec_id.is_enabled_in(SpecId::BERLIN) {
199 let mut gas_cost = istanbul_sstore_cost::<WARM_STORAGE_READ_COST, WARM_SSTORE_RESET>(vals);
201
202 if is_cold {
203 gas_cost += COLD_SLOAD_COST;
204 }
205 Some(gas_cost)
206 } else if spec_id.is_enabled_in(SpecId::ISTANBUL) {
207 Some(istanbul_sstore_cost::<INSTANBUL_SLOAD_GAS, SSTORE_RESET>(
209 vals,
210 ))
211 } else {
212 Some(frontier_sstore_cost(vals))
214 }
215}
216
217#[inline]
219fn istanbul_sstore_cost<const SLOAD_GAS: u64, const SSTORE_RESET_GAS: u64>(
220 vals: &SStoreResult,
221) -> u64 {
222 if vals.is_new_eq_present() {
223 SLOAD_GAS
224 } else if vals.is_original_eq_present() && vals.is_original_zero() {
225 SSTORE_SET
226 } else if vals.is_original_eq_present() {
227 SSTORE_RESET_GAS
228 } else {
229 SLOAD_GAS
230 }
231}
232
233#[inline]
235fn frontier_sstore_cost(vals: &SStoreResult) -> u64 {
236 if vals.is_present_zero() && !vals.is_new_zero() {
237 SSTORE_SET
238 } else {
239 SSTORE_RESET
240 }
241}
242
243#[inline]
245pub const fn selfdestruct_cost(spec_id: SpecId, res: StateLoad<SelfDestructResult>) -> u64 {
246 let should_charge_topup = if spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
248 res.data.had_value && !res.data.target_exists
249 } else {
250 !res.data.target_exists
251 };
252
253 let selfdestruct_gas_topup = if spec_id.is_enabled_in(SpecId::TANGERINE) && should_charge_topup
255 {
256 25000
257 } else {
258 0
259 };
260
261 let selfdestruct_gas = if spec_id.is_enabled_in(SpecId::TANGERINE) {
263 5000
264 } else {
265 0
266 };
267
268 let mut gas = selfdestruct_gas + selfdestruct_gas_topup;
269 if spec_id.is_enabled_in(SpecId::BERLIN) && res.is_cold {
270 gas += COLD_ACCOUNT_ACCESS_COST
271 }
272 gas
273}
274
275#[inline]
296pub const fn call_cost(spec_id: SpecId, transfers_value: bool, account_load: AccountLoad) -> u64 {
297 let mut gas = if spec_id.is_enabled_in(SpecId::BERLIN) {
299 warm_cold_cost_with_delegation(account_load.load)
300 } else if spec_id.is_enabled_in(SpecId::TANGERINE) {
301 700
303 } else {
304 40
305 };
306
307 if transfers_value {
309 gas += CALLVALUE;
310 }
311
312 if account_load.is_empty {
314 if spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
316 if transfers_value {
318 gas += NEWACCOUNT;
319 }
320 } else {
321 gas += NEWACCOUNT;
322 }
323 }
324
325 gas
326}
327
328#[inline]
330pub const fn warm_cold_cost(is_cold: bool) -> u64 {
331 if is_cold {
332 COLD_ACCOUNT_ACCESS_COST
333 } else {
334 WARM_STORAGE_READ_COST
335 }
336}
337
338#[inline]
342pub const fn warm_cold_cost_with_delegation(load: Eip7702CodeLoad<()>) -> u64 {
343 let mut gas = warm_cold_cost(load.state_load.is_cold);
344 if let Some(is_cold) = load.is_delegate_account_cold {
345 gas += warm_cold_cost(is_cold);
346 }
347 gas
348}
349
350#[inline]
352pub const fn memory_gas_for_len(len: usize) -> u64 {
353 memory_gas(crate::interpreter::num_words(len as u64))
354}
355
356#[inline]
358pub const fn memory_gas(num_words: u64) -> u64 {
359 MEMORY
360 .saturating_mul(num_words)
361 .saturating_add(num_words.saturating_mul(num_words) / 512)
362}
363
364pub fn validate_initial_tx_gas(
367 spec_id: SpecId,
368 input: &[u8],
369 is_create: bool,
370 access_list: &[AccessListItem],
371 authorization_list_num: u64,
372) -> u64 {
373 let mut initial_gas = 0;
374 let zero_data_len = input.iter().filter(|v| **v == 0).count() as u64;
375 let non_zero_data_len = input.len() as u64 - zero_data_len;
376
377 initial_gas += zero_data_len * TRANSACTION_ZERO_DATA;
379 initial_gas += non_zero_data_len
381 * if spec_id.is_enabled_in(SpecId::ISTANBUL) {
382 16
383 } else {
384 68
385 };
386
387 if spec_id.is_enabled_in(SpecId::BERLIN) {
389 let accessed_slots: usize = access_list.iter().map(|item| item.storage_keys.len()).sum();
390 initial_gas += access_list.len() as u64 * ACCESS_LIST_ADDRESS;
391 initial_gas += accessed_slots as u64 * ACCESS_LIST_STORAGE_KEY;
392 }
393
394 initial_gas += if is_create {
396 if spec_id.is_enabled_in(SpecId::HOMESTEAD) {
397 53000
399 } else {
400 21000
401 }
402 } else {
403 21000
404 };
405
406 if spec_id.is_enabled_in(SpecId::SHANGHAI) && is_create {
409 initial_gas += initcode_cost(input.len() as u64)
410 }
411
412 if spec_id.is_enabled_in(SpecId::PRAGUE) {
414 initial_gas += authorization_list_num * eip7702::PER_EMPTY_ACCOUNT_COST;
415 }
416
417 initial_gas
418}