revm_interpreter/gas/
calc.rs1use super::constants::*;
2use crate::num_words;
3use context_interface::{transaction::AccessListItemTr as _, Transaction, TransactionType};
4use primitives::{eip7702, hardfork::SpecId, U256};
5
6#[inline]
7pub(crate) const fn log2floor(value: U256) -> u64 {
8 let mut l: u64 = 256;
9 let mut i = 3;
10 loop {
11 if value.as_limbs()[i] == 0u64 {
12 l -= 64;
13 } else {
14 l -= value.as_limbs()[i].leading_zeros() as u64;
15 if l == 0 {
16 return l;
17 } else {
18 return l - 1;
19 }
20 }
21 if i == 0 {
22 break;
23 }
24 i -= 1;
25 }
26 l
27}
28
29#[inline]
31pub const fn cost_per_word(len: usize, multiple: u64) -> Option<u64> {
32 multiple.checked_mul(num_words(len) as u64)
33}
34
35#[inline]
41pub const fn initcode_cost(len: usize) -> u64 {
42 let Some(cost) = cost_per_word(len, INITCODE_WORD_COST) else {
43 panic!("initcode cost overflow")
44 };
45 cost
46}
47
48#[inline]
50pub const fn memory_gas(num_words: usize, linear_cost: u64, quadratic_cost: u64) -> u64 {
51 let num_words = num_words as u64;
52 linear_cost
53 .saturating_mul(num_words)
54 .saturating_add(num_words.saturating_mul(num_words) / quadratic_cost)
55}
56
57#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
59#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60pub struct InitialAndFloorGas {
61 pub initial_gas: u64,
63 pub floor_gas: u64,
66}
67
68impl InitialAndFloorGas {
69 #[inline]
71 pub const fn new(initial_gas: u64, floor_gas: u64) -> Self {
72 Self {
73 initial_gas,
74 floor_gas,
75 }
76 }
77}
78
79pub fn calculate_initial_tx_gas(
87 spec_id: SpecId,
88 input: &[u8],
89 is_create: bool,
90 access_list_accounts: u64,
91 access_list_storages: u64,
92 authorization_list_num: u64,
93) -> InitialAndFloorGas {
94 let mut gas = InitialAndFloorGas::default();
95
96 let tokens_in_calldata = get_tokens_in_calldata(input, spec_id.is_enabled_in(SpecId::ISTANBUL));
98
99 gas.initial_gas += tokens_in_calldata * STANDARD_TOKEN_COST;
100
101 gas.initial_gas += access_list_accounts * ACCESS_LIST_ADDRESS;
103 gas.initial_gas += access_list_storages * ACCESS_LIST_STORAGE_KEY;
104
105 gas.initial_gas += if is_create {
107 if spec_id.is_enabled_in(SpecId::HOMESTEAD) {
108 53000
110 } else {
111 21000
112 }
113 } else {
114 21000
115 };
116
117 if spec_id.is_enabled_in(SpecId::SHANGHAI) && is_create {
120 gas.initial_gas += initcode_cost(input.len())
121 }
122
123 if spec_id.is_enabled_in(SpecId::PRAGUE) {
125 gas.initial_gas += authorization_list_num * eip7702::PER_EMPTY_ACCOUNT_COST;
126
127 gas.floor_gas = calc_tx_floor_cost(tokens_in_calldata);
129 }
130
131 gas
132}
133
134pub fn calculate_initial_tx_gas_for_tx(tx: impl Transaction, spec: SpecId) -> InitialAndFloorGas {
142 let mut accounts = 0;
143 let mut storages = 0;
144 if tx.tx_type() != TransactionType::Legacy {
146 (accounts, storages) = tx
147 .access_list()
148 .map(|al| {
149 al.fold((0, 0), |(mut num_accounts, mut num_storage_slots), item| {
150 num_accounts += 1;
151 num_storage_slots += item.storage_slots().count();
152
153 (num_accounts, num_storage_slots)
154 })
155 })
156 .unwrap_or_default();
157 }
158
159 calculate_initial_tx_gas(
160 spec,
161 tx.input(),
162 tx.kind().is_create(),
163 accounts as u64,
164 storages as u64,
165 tx.authorization_list_len() as u64,
166 )
167}
168
169#[inline]
171pub fn get_tokens_in_calldata(input: &[u8], is_istanbul: bool) -> u64 {
172 let zero_data_len = input.iter().filter(|v| **v == 0).count() as u64;
173 let non_zero_data_len = input.len() as u64 - zero_data_len;
174 let non_zero_data_multiplier = if is_istanbul {
175 NON_ZERO_BYTE_MULTIPLIER_ISTANBUL
177 } else {
178 NON_ZERO_BYTE_MULTIPLIER
179 };
180 zero_data_len + non_zero_data_len * non_zero_data_multiplier
181}
182
183#[inline]
185pub fn calc_tx_floor_cost(tokens_in_calldata: u64) -> u64 {
186 tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN + 21_000
187}