evm_gasometer/
costs.rs

1use crate::consts::*;
2use crate::Config;
3use evm_core::ExitError;
4use primitive_types::{H256, U256};
5
6pub fn call_extra_check(gas: U256, after_gas: u64, config: &Config) -> Result<(), ExitError> {
7	if config.err_on_call_with_more_gas && U256::from(after_gas) < gas {
8		Err(ExitError::OutOfGas)
9	} else {
10		Ok(())
11	}
12}
13
14pub fn suicide_refund(already_removed: bool) -> i64 {
15	if already_removed {
16		0
17	} else {
18		R_SUICIDE
19	}
20}
21
22#[allow(clippy::collapsible_else_if)]
23pub fn sstore_refund(original: H256, current: H256, new: H256, config: &Config) -> i64 {
24	if config.sstore_gas_metering {
25		if current == new {
26			0
27		} else {
28			if original == current && new == H256::default() {
29				config.refund_sstore_clears
30			} else {
31				let mut refund = 0;
32
33				if original != H256::default() {
34					if current == H256::default() {
35						refund -= config.refund_sstore_clears;
36					} else if new == H256::default() {
37						refund += config.refund_sstore_clears;
38					}
39				}
40
41				if original == new {
42					if original == H256::default() {
43						refund += (config.gas_sstore_set - config.gas_sload) as i64;
44					} else {
45						refund += (config.gas_sstore_reset - config.gas_sload) as i64;
46					}
47				}
48
49				refund
50			}
51		}
52	} else {
53		if current != H256::default() && new == H256::default() {
54			config.refund_sstore_clears
55		} else {
56			0
57		}
58	}
59}
60
61pub fn create2_cost(len: U256) -> Result<u64, ExitError> {
62	let base = U256::from(G_CREATE);
63	// ceil(len / 32.0)
64	let sha_addup_base = len / U256::from(32)
65		+ if len % U256::from(32) == U256::zero() {
66			U256::zero()
67		} else {
68			U256::one()
69		};
70	let sha_addup = U256::from(G_SHA3WORD)
71		.checked_mul(sha_addup_base)
72		.ok_or(ExitError::OutOfGas)?;
73	let gas = base.checked_add(sha_addup).ok_or(ExitError::OutOfGas)?;
74
75	if gas > U256::from(u64::MAX) {
76		return Err(ExitError::OutOfGas);
77	}
78
79	Ok(gas.as_u64())
80}
81
82pub fn exp_cost(power: U256, config: &Config) -> Result<u64, ExitError> {
83	if power == U256::zero() {
84		Ok(G_EXP)
85	} else {
86		let gas = U256::from(G_EXP)
87			.checked_add(
88				U256::from(config.gas_expbyte)
89					.checked_mul(U256::from(crate::utils::log2floor(power) / 8 + 1))
90					.ok_or(ExitError::OutOfGas)?,
91			)
92			.ok_or(ExitError::OutOfGas)?;
93
94		if gas > U256::from(u64::MAX) {
95			return Err(ExitError::OutOfGas);
96		}
97
98		Ok(gas.as_u64())
99	}
100}
101
102pub fn verylowcopy_cost(len: U256) -> Result<u64, ExitError> {
103	let wordd = len / U256::from(32);
104	let wordr = len % U256::from(32);
105
106	let gas = U256::from(G_VERYLOW)
107		.checked_add(
108			U256::from(G_COPY)
109				.checked_mul(if wordr == U256::zero() {
110					wordd
111				} else {
112					wordd + U256::one()
113				})
114				.ok_or(ExitError::OutOfGas)?,
115		)
116		.ok_or(ExitError::OutOfGas)?;
117
118	if gas > U256::from(u64::MAX) {
119		return Err(ExitError::OutOfGas);
120	}
121
122	Ok(gas.as_u64())
123}
124
125pub fn extcodecopy_cost(len: U256, is_cold: bool, config: &Config) -> Result<u64, ExitError> {
126	let wordd = len / U256::from(32);
127	let wordr = len % U256::from(32);
128	let gas = U256::from(address_access_cost(is_cold, config.gas_ext_code, config))
129		.checked_add(
130			U256::from(G_COPY)
131				.checked_mul(if wordr == U256::zero() {
132					wordd
133				} else {
134					wordd + U256::one()
135				})
136				.ok_or(ExitError::OutOfGas)?,
137		)
138		.ok_or(ExitError::OutOfGas)?;
139
140	if gas > U256::from(u64::MAX) {
141		return Err(ExitError::OutOfGas);
142	}
143
144	Ok(gas.as_u64())
145}
146
147pub fn log_cost(n: u8, len: U256) -> Result<u64, ExitError> {
148	let gas = U256::from(G_LOG)
149		.checked_add(
150			U256::from(G_LOGDATA)
151				.checked_mul(len)
152				.ok_or(ExitError::OutOfGas)?,
153		)
154		.ok_or(ExitError::OutOfGas)?
155		.checked_add(U256::from(G_LOGTOPIC * n as u64))
156		.ok_or(ExitError::OutOfGas)?;
157
158	if gas > U256::from(u64::MAX) {
159		return Err(ExitError::OutOfGas);
160	}
161
162	Ok(gas.as_u64())
163}
164
165pub fn sha3_cost(len: U256) -> Result<u64, ExitError> {
166	let wordd = len / U256::from(32);
167	let wordr = len % U256::from(32);
168
169	let gas = U256::from(G_SHA3)
170		.checked_add(
171			U256::from(G_SHA3WORD)
172				.checked_mul(if wordr == U256::zero() {
173					wordd
174				} else {
175					wordd + U256::one()
176				})
177				.ok_or(ExitError::OutOfGas)?,
178		)
179		.ok_or(ExitError::OutOfGas)?;
180
181	if gas > U256::from(u64::MAX) {
182		return Err(ExitError::OutOfGas);
183	}
184
185	Ok(gas.as_u64())
186}
187
188pub fn sload_cost(is_cold: bool, config: &Config) -> u64 {
189	if config.increase_state_access_gas {
190		if is_cold {
191			config.gas_sload_cold
192		} else {
193			config.gas_storage_read_warm
194		}
195	} else {
196		config.gas_sload
197	}
198}
199
200#[allow(clippy::collapsible_else_if)]
201pub fn sstore_cost(
202	original: H256,
203	current: H256,
204	new: H256,
205	gas: u64,
206	is_cold: bool,
207	config: &Config,
208) -> Result<u64, ExitError> {
209	let gas_cost = if config.estimate {
210		config.gas_sstore_set
211	} else {
212		if config.sstore_gas_metering {
213			if config.sstore_revert_under_stipend && gas <= config.call_stipend {
214				return Err(ExitError::OutOfGas);
215			}
216
217			if new == current {
218				config.gas_sload
219			} else {
220				if original == current {
221					if original == H256::zero() {
222						config.gas_sstore_set
223					} else {
224						config.gas_sstore_reset
225					}
226				} else {
227					config.gas_sload
228				}
229			}
230		} else {
231			if current == H256::zero() && new != H256::zero() {
232				config.gas_sstore_set
233			} else {
234				config.gas_sstore_reset
235			}
236		}
237	};
238	Ok(
239		// In EIP-2929 we charge extra if the slot has not been used yet in this transaction
240		if is_cold {
241			gas_cost + config.gas_sload_cold
242		} else {
243			gas_cost
244		},
245	)
246}
247
248pub fn tload_cost(config: &Config) -> Result<u64, ExitError> {
249	Ok(config.gas_storage_read_warm)
250}
251
252pub fn tstore_cost(config: &Config) -> Result<u64, ExitError> {
253	Ok(config.gas_storage_read_warm)
254}
255
256pub fn suicide_cost(value: U256, is_cold: bool, target_exists: bool, config: &Config) -> u64 {
257	let eip161 = !config.empty_considered_exists;
258	let should_charge_topup = if eip161 {
259		value != U256::zero() && !target_exists
260	} else {
261		!target_exists
262	};
263
264	let suicide_gas_topup = if should_charge_topup {
265		config.gas_suicide_new_account
266	} else {
267		0
268	};
269
270	let mut gas = config.gas_suicide + suicide_gas_topup;
271	if config.increase_state_access_gas && is_cold {
272		gas += config.gas_account_access_cold
273	}
274	gas
275}
276
277pub fn call_cost(
278	value: U256,
279	is_cold: bool,
280	is_call_or_callcode: bool,
281	is_call_or_staticcall: bool,
282	new_account: bool,
283	config: &Config,
284) -> u64 {
285	let transfers_value = value != U256::default();
286	address_access_cost(is_cold, config.gas_call, config)
287		+ xfer_cost(is_call_or_callcode, transfers_value)
288		+ new_cost(is_call_or_staticcall, new_account, transfers_value, config)
289}
290
291pub fn address_access_cost(is_cold: bool, regular_value: u64, config: &Config) -> u64 {
292	if config.increase_state_access_gas {
293		if is_cold {
294			config.gas_account_access_cold
295		} else {
296			config.gas_storage_read_warm
297		}
298	} else {
299		regular_value
300	}
301}
302
303fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 {
304	if is_call_or_callcode && transfers_value {
305		G_CALLVALUE
306	} else {
307		0
308	}
309}
310
311fn new_cost(
312	is_call_or_staticcall: bool,
313	new_account: bool,
314	transfers_value: bool,
315	config: &Config,
316) -> u64 {
317	let eip161 = !config.empty_considered_exists;
318	if is_call_or_staticcall {
319		if eip161 {
320			if transfers_value && new_account {
321				G_NEWACCOUNT
322			} else {
323				0
324			}
325		} else if new_account {
326			G_NEWACCOUNT
327		} else {
328			0
329		}
330	} else {
331		0
332	}
333}