1#![deny(warnings)]
4#![forbid(unsafe_code, unused_variables)]
5#![cfg_attr(not(feature = "std"), no_std)]
6
7extern crate alloc;
8
9#[cfg(feature = "tracing")]
10pub mod tracing;
11
12#[cfg(feature = "tracing")]
13macro_rules! event {
14	($x:expr) => {
15		use crate::tracing::Event::*;
16		crate::tracing::with(|listener| listener.event($x));
17	};
18}
19#[cfg(feature = "force-debug")]
20macro_rules! log_gas {
21	($self:expr, $($arg:tt)*) => (log::trace!(target: "evm", "Gasometer {} [Gas used: {}, Gas left: {}]", format_args!($($arg)*),
22	$self.total_used_gas(), $self.gas()));
23}
24
25#[cfg(not(feature = "force-debug"))]
26macro_rules! log_gas {
27	($self:expr, $($arg:tt)*) => {};
28}
29
30#[cfg(not(feature = "tracing"))]
31macro_rules! event {
32	($x:expr) => {};
33}
34
35mod consts;
36mod costs;
37mod memory;
38mod utils;
39
40use alloc::vec::Vec;
41use core::cmp::max;
42use evm_core::{ExitError, Opcode, Stack};
43use evm_runtime::{Config, Handler};
44use primitive_types::{H160, H256, U256};
45
46macro_rules! try_or_fail {
47	( $inner:expr, $e:expr ) => {
48		match $e {
49			Ok(value) => value,
50			Err(e) => {
51				$inner = Err(e.clone());
52				return Err(e);
53			}
54		}
55	};
56}
57
58#[cfg(feature = "tracing")]
59#[derive(Debug, Copy, Clone)]
60pub struct Snapshot {
61	pub gas_limit: u64,
62	pub memory_gas: u64,
63	pub used_gas: u64,
64	pub refunded_gas: i64,
65}
66
67#[derive(Clone, Debug)]
69pub struct Gasometer<'config> {
70	gas_limit: u64,
71	config: &'config Config,
72	inner: Result<Inner<'config>, ExitError>,
73}
74
75impl<'config> Gasometer<'config> {
76	pub fn new(gas_limit: u64, config: &'config Config) -> Self {
78		Self {
79			gas_limit,
80			config,
81			inner: Ok(Inner {
82				memory_gas: 0,
83				used_gas: 0,
84				refunded_gas: 0,
85				config,
86			}),
87		}
88	}
89
90	#[inline]
91	pub fn gas_cost(&self, cost: GasCost, gas: u64) -> Result<u64, ExitError> {
93		match self.inner.as_ref() {
94			Ok(inner) => inner.gas_cost(cost, gas),
95			Err(e) => Err(e.clone()),
96		}
97	}
98
99	#[inline]
100	fn inner_mut(&mut self) -> Result<&mut Inner<'config>, ExitError> {
101		self.inner.as_mut().map_err(|e| e.clone())
102	}
103
104	#[inline]
105	pub fn config(&self) -> &'config Config {
107		self.config
108	}
109
110	#[inline]
111	pub fn gas(&self) -> u64 {
113		match self.inner.as_ref() {
114			Ok(inner) => self.gas_limit - inner.used_gas - inner.memory_gas,
115			Err(_) => 0,
116		}
117	}
118
119	#[inline]
120	pub fn total_used_gas(&self) -> u64 {
122		match self.inner.as_ref() {
123			Ok(inner) => inner.used_gas + inner.memory_gas,
124			Err(_) => self.gas_limit,
125		}
126	}
127
128	#[inline]
129	pub fn refunded_gas(&self) -> i64 {
131		match self.inner.as_ref() {
132			Ok(inner) => inner.refunded_gas,
133			Err(_) => 0,
134		}
135	}
136
137	pub fn fail(&mut self) -> ExitError {
139		self.inner = Err(ExitError::OutOfGas);
140		ExitError::OutOfGas
141	}
142
143	#[inline]
144	pub fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> {
146		event!(RecordCost {
147			cost,
148			snapshot: self.snapshot(),
149		});
150		log_gas!(self, "Record cost {}", cost);
151
152		let all_gas_cost = self.total_used_gas() + cost;
153		if self.gas_limit < all_gas_cost {
154			self.inner = Err(ExitError::OutOfGas);
155			return Err(ExitError::OutOfGas);
156		}
157
158		self.inner_mut()?.used_gas += cost;
159		Ok(())
160	}
161
162	#[inline]
163	pub fn record_refund(&mut self, refund: i64) -> Result<(), ExitError> {
165		event!(RecordRefund {
166			refund,
167			snapshot: self.snapshot(),
168		});
169		log_gas!(self, "Record refund -{}", refund);
170
171		self.inner_mut()?.refunded_gas += refund;
172		Ok(())
173	}
174
175	#[inline]
176	pub fn record_deposit(&mut self, len: usize) -> Result<(), ExitError> {
178		let cost = len as u64 * consts::G_CODEDEPOSIT;
179		self.record_cost(cost)
180	}
181
182	pub fn record_dynamic_cost(
184		&mut self,
185		cost: GasCost,
186		memory: Option<MemoryCost>,
187	) -> Result<(), ExitError> {
188		let gas = self.gas();
189
190		let memory_gas = match memory {
191			Some(memory) => try_or_fail!(self.inner, self.inner_mut()?.memory_gas(memory)),
192			None => self.inner_mut()?.memory_gas,
193		};
194		let gas_cost = try_or_fail!(self.inner, self.inner_mut()?.gas_cost(cost, gas));
195		let gas_refund = self.inner_mut()?.gas_refund(cost);
196		let used_gas = self.inner_mut()?.used_gas;
197
198		event!(RecordDynamicCost {
199			gas_cost,
200			memory_gas,
201			gas_refund,
202			snapshot: self.snapshot(),
203		});
204
205		let all_gas_cost = memory_gas + used_gas + gas_cost;
206		if self.gas_limit < all_gas_cost {
207			self.inner = Err(ExitError::OutOfGas);
208			return Err(ExitError::OutOfGas);
209		}
210
211		log_gas!(
212			self,
213			"Record dynamic cost {} - memory_gas {} - gas_refund {}",
214			gas_cost,
215			memory_gas,
216			gas_refund
217		);
218
219		let after_gas = self.gas_limit - all_gas_cost;
220		try_or_fail!(self.inner, self.inner_mut()?.extra_check(cost, after_gas));
221
222		self.inner_mut()?.used_gas += gas_cost;
223		self.inner_mut()?.memory_gas = memory_gas;
224		self.inner_mut()?.refunded_gas += gas_refund;
225
226		Ok(())
227	}
228
229	#[inline]
230	pub fn record_stipend(&mut self, stipend: u64) -> Result<(), ExitError> {
232		event!(RecordStipend {
233			stipend,
234			snapshot: self.snapshot(),
235		});
236
237		self.inner_mut()?.used_gas -= stipend;
238		log_gas!(self, "Record stipent {}", stipend);
239		Ok(())
240	}
241
242	pub fn record_transaction(&mut self, cost: TransactionCost) -> Result<(), ExitError> {
244		let gas_cost = match cost {
245			TransactionCost::Call {
246				zero_data_len,
247				non_zero_data_len,
248				access_list_address_len,
249				access_list_storage_len,
250				authorization_list_len,
251			} => {
252				#[deny(clippy::let_and_return)]
253				let cost = self.config.gas_transaction_call
254					+ zero_data_len as u64 * self.config.gas_transaction_zero_data
255					+ non_zero_data_len as u64 * self.config.gas_transaction_non_zero_data
256					+ access_list_address_len as u64 * self.config.gas_access_list_address
257					+ access_list_storage_len as u64 * self.config.gas_access_list_storage_key
258					+ authorization_list_len as u64 * self.config.gas_per_empty_account_cost;
259
260				log_gas!(
261					self,
262					"Record Call {} [gas_transaction_call: {}, zero_data_len: {}, non_zero_data_len: {}, access_list_address_len: {}, access_list_storage_len: {}, authorization_list_len: {}]",
263					cost,
264					self.config.gas_transaction_call,
265					zero_data_len,
266					non_zero_data_len,
267					access_list_address_len,
268					access_list_storage_len,
269					authorization_list_len
270				);
271
272				cost
273			}
274			TransactionCost::Create {
275				zero_data_len,
276				non_zero_data_len,
277				access_list_address_len,
278				access_list_storage_len,
279				initcode_cost,
280				authorization_list_len,
281			} => {
282				let mut cost = self.config.gas_transaction_create
283					+ zero_data_len as u64 * self.config.gas_transaction_zero_data
284					+ non_zero_data_len as u64 * self.config.gas_transaction_non_zero_data
285					+ access_list_address_len as u64 * self.config.gas_access_list_address
286					+ access_list_storage_len as u64 * self.config.gas_access_list_storage_key
287					+ authorization_list_len as u64 * self.config.gas_per_empty_account_cost;
288				if self.config.max_initcode_size.is_some() {
289					cost += initcode_cost;
290				}
291
292				log_gas!(
293					self,
294					"Record Create {} [gas_transaction_create: {}, zero_data_len: {}, non_zero_data_len: {}, access_list_address_len: {}, access_list_storage_len: {}, initcode_cost: {}, authorization_list_len: {}]",
295					cost,
296					self.config.gas_transaction_create,
297					zero_data_len,
298					non_zero_data_len,
299					access_list_address_len,
300					access_list_storage_len,
301					initcode_cost,
302					authorization_list_len
303				);
304				cost
305			}
306		};
307
308		event!(RecordTransaction {
309			cost: gas_cost,
310			snapshot: self.snapshot(),
311		});
312
313		if self.gas() < gas_cost {
314			self.inner = Err(ExitError::OutOfGas);
315			return Err(ExitError::OutOfGas);
316		}
317
318		self.inner_mut()?.used_gas += gas_cost;
319		Ok(())
320	}
321
322	#[cfg(feature = "tracing")]
323	pub fn snapshot(&self) -> Option<Snapshot> {
324		self.inner.as_ref().ok().map(|inner| Snapshot {
325			gas_limit: self.gas_limit,
326			memory_gas: inner.memory_gas,
327			used_gas: inner.used_gas,
328			refunded_gas: inner.refunded_gas,
329		})
330	}
331}
332
333#[allow(clippy::naive_bytecount)]
335pub fn call_transaction_cost(
336	data: &[u8],
337	access_list: &[(H160, Vec<H256>)],
338	authorization_list: &[(U256, H160, U256, Option<H160>)],
339) -> TransactionCost {
340	let zero_data_len = data.iter().filter(|v| **v == 0).count();
341	let non_zero_data_len = data.len() - zero_data_len;
342	let (access_list_address_len, access_list_storage_len) = count_access_list(access_list);
343	let authorization_list_len = authorization_list.len();
346
347	TransactionCost::Call {
348		zero_data_len,
349		non_zero_data_len,
350		access_list_address_len,
351		access_list_storage_len,
352		authorization_list_len,
353	}
354}
355
356#[allow(clippy::naive_bytecount)]
358pub fn create_transaction_cost(
359	data: &[u8],
360	access_list: &[(H160, Vec<H256>)],
361	authorization_list: &[(U256, H160, U256, Option<H160>)],
362) -> TransactionCost {
363	let zero_data_len = data.iter().filter(|v| **v == 0).count();
364	let non_zero_data_len = data.len() - zero_data_len;
365	let (access_list_address_len, access_list_storage_len) = count_access_list(access_list);
366	let authorization_list_len = authorization_list.len();
369	let initcode_cost = init_code_cost(data);
370
371	TransactionCost::Create {
372		zero_data_len,
373		non_zero_data_len,
374		access_list_address_len,
375		access_list_storage_len,
376		initcode_cost,
377		authorization_list_len,
378	}
379}
380
381pub fn init_code_cost(data: &[u8]) -> u64 {
382	2 * ((data.len() as u64 + 31) / 32)
386}
387
388fn count_access_list(access_list: &[(H160, Vec<H256>)]) -> (usize, usize) {
390	let access_list_address_len = access_list.len();
391	let access_list_storage_len = access_list.iter().map(|(_, keys)| keys.len()).sum();
392
393	(access_list_address_len, access_list_storage_len)
394}
395
396#[inline]
397pub fn static_opcode_cost(opcode: Opcode) -> Option<u64> {
398	static TABLE: [Option<u64>; 256] = {
399		let mut table = [None; 256];
400
401		table[Opcode::STOP.as_usize()] = Some(consts::G_ZERO);
402		table[Opcode::CALLDATASIZE.as_usize()] = Some(consts::G_BASE);
403		table[Opcode::CODESIZE.as_usize()] = Some(consts::G_BASE);
404		table[Opcode::POP.as_usize()] = Some(consts::G_BASE);
405		table[Opcode::PC.as_usize()] = Some(consts::G_BASE);
406		table[Opcode::MSIZE.as_usize()] = Some(consts::G_BASE);
407
408		table[Opcode::ADDRESS.as_usize()] = Some(consts::G_BASE);
409		table[Opcode::ORIGIN.as_usize()] = Some(consts::G_BASE);
410		table[Opcode::CALLER.as_usize()] = Some(consts::G_BASE);
411		table[Opcode::CALLVALUE.as_usize()] = Some(consts::G_BASE);
412		table[Opcode::COINBASE.as_usize()] = Some(consts::G_BASE);
413		table[Opcode::TIMESTAMP.as_usize()] = Some(consts::G_BASE);
414		table[Opcode::NUMBER.as_usize()] = Some(consts::G_BASE);
415		table[Opcode::DIFFICULTY.as_usize()] = Some(consts::G_BASE);
416		table[Opcode::GASLIMIT.as_usize()] = Some(consts::G_BASE);
417		table[Opcode::GASPRICE.as_usize()] = Some(consts::G_BASE);
418		table[Opcode::GAS.as_usize()] = Some(consts::G_BASE);
419
420		table[Opcode::ADD.as_usize()] = Some(consts::G_VERYLOW);
421		table[Opcode::SUB.as_usize()] = Some(consts::G_VERYLOW);
422		table[Opcode::NOT.as_usize()] = Some(consts::G_VERYLOW);
423		table[Opcode::LT.as_usize()] = Some(consts::G_VERYLOW);
424		table[Opcode::GT.as_usize()] = Some(consts::G_VERYLOW);
425		table[Opcode::SLT.as_usize()] = Some(consts::G_VERYLOW);
426		table[Opcode::SGT.as_usize()] = Some(consts::G_VERYLOW);
427		table[Opcode::EQ.as_usize()] = Some(consts::G_VERYLOW);
428		table[Opcode::ISZERO.as_usize()] = Some(consts::G_VERYLOW);
429		table[Opcode::AND.as_usize()] = Some(consts::G_VERYLOW);
430		table[Opcode::OR.as_usize()] = Some(consts::G_VERYLOW);
431		table[Opcode::XOR.as_usize()] = Some(consts::G_VERYLOW);
432		table[Opcode::BYTE.as_usize()] = Some(consts::G_VERYLOW);
433		table[Opcode::CALLDATALOAD.as_usize()] = Some(consts::G_VERYLOW);
434		table[Opcode::PUSH1.as_usize()] = Some(consts::G_VERYLOW);
435		table[Opcode::PUSH2.as_usize()] = Some(consts::G_VERYLOW);
436		table[Opcode::PUSH3.as_usize()] = Some(consts::G_VERYLOW);
437		table[Opcode::PUSH4.as_usize()] = Some(consts::G_VERYLOW);
438		table[Opcode::PUSH5.as_usize()] = Some(consts::G_VERYLOW);
439		table[Opcode::PUSH6.as_usize()] = Some(consts::G_VERYLOW);
440		table[Opcode::PUSH7.as_usize()] = Some(consts::G_VERYLOW);
441		table[Opcode::PUSH8.as_usize()] = Some(consts::G_VERYLOW);
442		table[Opcode::PUSH9.as_usize()] = Some(consts::G_VERYLOW);
443		table[Opcode::PUSH10.as_usize()] = Some(consts::G_VERYLOW);
444		table[Opcode::PUSH11.as_usize()] = Some(consts::G_VERYLOW);
445		table[Opcode::PUSH12.as_usize()] = Some(consts::G_VERYLOW);
446		table[Opcode::PUSH13.as_usize()] = Some(consts::G_VERYLOW);
447		table[Opcode::PUSH14.as_usize()] = Some(consts::G_VERYLOW);
448		table[Opcode::PUSH15.as_usize()] = Some(consts::G_VERYLOW);
449		table[Opcode::PUSH16.as_usize()] = Some(consts::G_VERYLOW);
450		table[Opcode::PUSH17.as_usize()] = Some(consts::G_VERYLOW);
451		table[Opcode::PUSH18.as_usize()] = Some(consts::G_VERYLOW);
452		table[Opcode::PUSH19.as_usize()] = Some(consts::G_VERYLOW);
453		table[Opcode::PUSH20.as_usize()] = Some(consts::G_VERYLOW);
454		table[Opcode::PUSH21.as_usize()] = Some(consts::G_VERYLOW);
455		table[Opcode::PUSH22.as_usize()] = Some(consts::G_VERYLOW);
456		table[Opcode::PUSH23.as_usize()] = Some(consts::G_VERYLOW);
457		table[Opcode::PUSH24.as_usize()] = Some(consts::G_VERYLOW);
458		table[Opcode::PUSH25.as_usize()] = Some(consts::G_VERYLOW);
459		table[Opcode::PUSH26.as_usize()] = Some(consts::G_VERYLOW);
460		table[Opcode::PUSH27.as_usize()] = Some(consts::G_VERYLOW);
461		table[Opcode::PUSH28.as_usize()] = Some(consts::G_VERYLOW);
462		table[Opcode::PUSH29.as_usize()] = Some(consts::G_VERYLOW);
463		table[Opcode::PUSH30.as_usize()] = Some(consts::G_VERYLOW);
464		table[Opcode::PUSH31.as_usize()] = Some(consts::G_VERYLOW);
465		table[Opcode::PUSH32.as_usize()] = Some(consts::G_VERYLOW);
466		table[Opcode::DUP1.as_usize()] = Some(consts::G_VERYLOW);
467		table[Opcode::DUP2.as_usize()] = Some(consts::G_VERYLOW);
468		table[Opcode::DUP3.as_usize()] = Some(consts::G_VERYLOW);
469		table[Opcode::DUP4.as_usize()] = Some(consts::G_VERYLOW);
470		table[Opcode::DUP5.as_usize()] = Some(consts::G_VERYLOW);
471		table[Opcode::DUP6.as_usize()] = Some(consts::G_VERYLOW);
472		table[Opcode::DUP7.as_usize()] = Some(consts::G_VERYLOW);
473		table[Opcode::DUP8.as_usize()] = Some(consts::G_VERYLOW);
474		table[Opcode::DUP9.as_usize()] = Some(consts::G_VERYLOW);
475		table[Opcode::DUP10.as_usize()] = Some(consts::G_VERYLOW);
476		table[Opcode::DUP11.as_usize()] = Some(consts::G_VERYLOW);
477		table[Opcode::DUP12.as_usize()] = Some(consts::G_VERYLOW);
478		table[Opcode::DUP13.as_usize()] = Some(consts::G_VERYLOW);
479		table[Opcode::DUP14.as_usize()] = Some(consts::G_VERYLOW);
480		table[Opcode::DUP15.as_usize()] = Some(consts::G_VERYLOW);
481		table[Opcode::DUP16.as_usize()] = Some(consts::G_VERYLOW);
482		table[Opcode::SWAP1.as_usize()] = Some(consts::G_VERYLOW);
483		table[Opcode::SWAP2.as_usize()] = Some(consts::G_VERYLOW);
484		table[Opcode::SWAP3.as_usize()] = Some(consts::G_VERYLOW);
485		table[Opcode::SWAP4.as_usize()] = Some(consts::G_VERYLOW);
486		table[Opcode::SWAP5.as_usize()] = Some(consts::G_VERYLOW);
487		table[Opcode::SWAP6.as_usize()] = Some(consts::G_VERYLOW);
488		table[Opcode::SWAP7.as_usize()] = Some(consts::G_VERYLOW);
489		table[Opcode::SWAP8.as_usize()] = Some(consts::G_VERYLOW);
490		table[Opcode::SWAP9.as_usize()] = Some(consts::G_VERYLOW);
491		table[Opcode::SWAP10.as_usize()] = Some(consts::G_VERYLOW);
492		table[Opcode::SWAP11.as_usize()] = Some(consts::G_VERYLOW);
493		table[Opcode::SWAP12.as_usize()] = Some(consts::G_VERYLOW);
494		table[Opcode::SWAP13.as_usize()] = Some(consts::G_VERYLOW);
495		table[Opcode::SWAP14.as_usize()] = Some(consts::G_VERYLOW);
496		table[Opcode::SWAP15.as_usize()] = Some(consts::G_VERYLOW);
497		table[Opcode::SWAP16.as_usize()] = Some(consts::G_VERYLOW);
498
499		table[Opcode::MUL.as_usize()] = Some(consts::G_LOW);
500		table[Opcode::DIV.as_usize()] = Some(consts::G_LOW);
501		table[Opcode::SDIV.as_usize()] = Some(consts::G_LOW);
502		table[Opcode::MOD.as_usize()] = Some(consts::G_LOW);
503		table[Opcode::SMOD.as_usize()] = Some(consts::G_LOW);
504		table[Opcode::SIGNEXTEND.as_usize()] = Some(consts::G_LOW);
505
506		table[Opcode::ADDMOD.as_usize()] = Some(consts::G_MID);
507		table[Opcode::MULMOD.as_usize()] = Some(consts::G_MID);
508		table[Opcode::JUMP.as_usize()] = Some(consts::G_MID);
509
510		table[Opcode::JUMPI.as_usize()] = Some(consts::G_HIGH);
511		table[Opcode::JUMPDEST.as_usize()] = Some(consts::G_JUMPDEST);
512
513		table
514	};
515
516	TABLE[opcode.as_usize()]
517}
518
519#[allow(clippy::nonminimal_bool)]
521pub fn dynamic_opcode_cost<H: Handler>(
522	address: H160,
523	opcode: Opcode,
524	stack: &Stack,
525	is_static: bool,
526	config: &Config,
527	handler: &mut H,
528) -> Result<(GasCost, StorageTarget, Option<MemoryCost>), ExitError> {
529	let mut storage_target = StorageTarget::None;
530	let gas_cost = match opcode {
531		Opcode::RETURN => GasCost::Zero,
532
533		Opcode::MLOAD | Opcode::MSTORE | Opcode::MSTORE8 => GasCost::VeryLow,
534
535		Opcode::REVERT if config.has_revert => GasCost::Zero,
536		Opcode::REVERT => GasCost::Invalid(opcode),
537
538		Opcode::CHAINID if config.has_chain_id => GasCost::Base,
539		Opcode::CHAINID => GasCost::Invalid(opcode),
540
541		Opcode::SHL | Opcode::SHR | Opcode::SAR if config.has_bitwise_shifting => GasCost::VeryLow,
542		Opcode::SHL | Opcode::SHR | Opcode::SAR => GasCost::Invalid(opcode),
543
544		Opcode::SELFBALANCE if config.has_self_balance => GasCost::Low,
545		Opcode::SELFBALANCE => GasCost::Invalid(opcode),
546
547		Opcode::BASEFEE if config.has_base_fee => GasCost::Base,
548		Opcode::BASEFEE => GasCost::Invalid(opcode),
549
550		Opcode::EXTCODESIZE => {
551			let target = stack.peek(0)?.into();
552			storage_target = StorageTarget::Address(target);
553			GasCost::ExtCodeSize {
554				target_is_cold: handler.is_cold(target, None)?,
555			}
556		}
557		Opcode::BALANCE => {
558			let target = stack.peek(0)?.into();
559			storage_target = StorageTarget::Address(target);
560			GasCost::Balance {
561				target_is_cold: handler.is_cold(target, None)?,
562			}
563		}
564		Opcode::BLOCKHASH => GasCost::BlockHash,
565
566		Opcode::EXTCODEHASH if config.has_ext_code_hash => {
567			let target = stack.peek(0)?.into();
568			storage_target = StorageTarget::Address(target);
569			GasCost::ExtCodeHash {
570				target_is_cold: handler.is_cold(target, None)?,
571			}
572		}
573		Opcode::EXTCODEHASH => GasCost::Invalid(opcode),
574
575		Opcode::CALLCODE => {
576			let target = stack.peek(1)?.into();
577			storage_target = StorageTarget::Address(target);
578			GasCost::CallCode {
579				value: U256::from_big_endian(&stack.peek(2)?[..]),
580				gas: U256::from_big_endian(&stack.peek(0)?[..]),
581				target_is_cold: handler.is_cold(target, None)?,
582				target_exists: {
583					handler.record_external_operation(evm_core::ExternalOperation::IsEmpty)?;
584					handler.exists(target)
585				},
586			}
587		}
588		Opcode::STATICCALL => {
589			let target = stack.peek(1)?.into();
590			storage_target = StorageTarget::Address(target);
591			GasCost::StaticCall {
592				gas: U256::from_big_endian(&stack.peek(0)?[..]),
593				target_is_cold: handler.is_cold(target, None)?,
594				target_exists: {
595					handler.record_external_operation(evm_core::ExternalOperation::IsEmpty)?;
596					handler.exists(target)
597				},
598			}
599		}
600		Opcode::SHA3 => GasCost::Sha3 {
601			len: U256::from_big_endian(&stack.peek(1)?[..]),
602		},
603		Opcode::EXTCODECOPY => {
604			let target = stack.peek(0)?.into();
605			storage_target = StorageTarget::Address(target);
606			GasCost::ExtCodeCopy {
607				target_is_cold: handler.is_cold(target, None)?,
608				len: U256::from_big_endian(&stack.peek(3)?[..]),
609			}
610		}
611		Opcode::CALLDATACOPY | Opcode::CODECOPY => GasCost::VeryLowCopy {
612			len: U256::from_big_endian(&stack.peek(2)?[..]),
613		},
614		Opcode::MCOPY if config.has_mcopy => GasCost::VeryLowCopy {
615			len: U256::from_big_endian(&stack.peek(2)?[..]),
616		},
617		Opcode::EXP => GasCost::Exp {
618			power: U256::from_big_endian(&stack.peek(1)?[..]),
619		},
620		Opcode::SLOAD => {
621			let index = stack.peek(0)?;
622			storage_target = StorageTarget::Slot(address, index);
623			GasCost::SLoad {
624				target_is_cold: handler.is_cold(address, Some(index))?,
625			}
626		}
627		Opcode::TLOAD if config.has_tloadstore => GasCost::TLoad,
628
629		Opcode::DELEGATECALL if config.has_delegate_call => {
630			let target = stack.peek(1)?.into();
631			storage_target = StorageTarget::Address(target);
632			GasCost::DelegateCall {
633				gas: U256::from_big_endian(&stack.peek(0)?[..]),
634				target_is_cold: handler.is_cold(target, None)?,
635				target_exists: {
636					handler.record_external_operation(evm_core::ExternalOperation::IsEmpty)?;
637					handler.exists(target)
638				},
639			}
640		}
641		Opcode::DELEGATECALL => GasCost::Invalid(opcode),
642
643		Opcode::RETURNDATASIZE if config.has_return_data => GasCost::Base,
644		Opcode::RETURNDATACOPY if config.has_return_data => GasCost::VeryLowCopy {
645			len: U256::from_big_endian(&stack.peek(2)?[..]),
646		},
647		Opcode::RETURNDATASIZE | Opcode::RETURNDATACOPY => GasCost::Invalid(opcode),
648
649		Opcode::SSTORE if !is_static => {
650			let index = stack.peek(0)?;
651			let value = stack.peek(1)?;
652			storage_target = StorageTarget::Slot(address, index);
653
654			GasCost::SStore {
655				original: handler.original_storage(address, index),
656				current: handler.storage(address, index),
657				new: value,
658				target_is_cold: handler.is_cold(address, Some(index))?,
659			}
660		}
661		Opcode::TSTORE if config.has_tloadstore && !is_static => GasCost::TStore,
662		Opcode::LOG0 if !is_static => GasCost::Log {
663			n: 0,
664			len: U256::from_big_endian(&stack.peek(1)?[..]),
665		},
666		Opcode::LOG1 if !is_static => GasCost::Log {
667			n: 1,
668			len: U256::from_big_endian(&stack.peek(1)?[..]),
669		},
670		Opcode::LOG2 if !is_static => GasCost::Log {
671			n: 2,
672			len: U256::from_big_endian(&stack.peek(1)?[..]),
673		},
674		Opcode::LOG3 if !is_static => GasCost::Log {
675			n: 3,
676			len: U256::from_big_endian(&stack.peek(1)?[..]),
677		},
678		Opcode::LOG4 if !is_static => GasCost::Log {
679			n: 4,
680			len: U256::from_big_endian(&stack.peek(1)?[..]),
681		},
682		Opcode::CREATE if !is_static => GasCost::Create,
683		Opcode::CREATE2 if !is_static && config.has_create2 => GasCost::Create2 {
684			len: U256::from_big_endian(&stack.peek(2)?[..]),
685		},
686		Opcode::SUICIDE if !is_static => {
687			let target = stack.peek(0)?.into();
688			storage_target = StorageTarget::Address(target);
689			GasCost::Suicide {
690				value: handler.balance(address),
691				target_is_cold: handler.is_cold(target, None)?,
692				target_exists: {
693					handler.record_external_operation(evm_core::ExternalOperation::IsEmpty)?;
694					handler.exists(target)
695				},
696				already_removed: handler.deleted(address),
697			}
698		}
699		Opcode::CALL
700			if !is_static
701				|| (is_static && U256::from_big_endian(&stack.peek(2)?[..]) == U256::zero()) =>
702		{
703			let target = stack.peek(1)?.into();
704			storage_target = StorageTarget::Address(target);
705			GasCost::Call {
706				value: U256::from_big_endian(&stack.peek(2)?[..]),
707				gas: U256::from_big_endian(&stack.peek(0)?[..]),
708				target_is_cold: handler.is_cold(target, None)?,
709				target_exists: {
710					handler.record_external_operation(evm_core::ExternalOperation::IsEmpty)?;
711					handler.exists(target)
712				},
713			}
714		}
715
716		Opcode::PUSH0 if config.has_push0 => GasCost::Base,
717
718		_ => GasCost::Invalid(opcode),
719	};
720
721	let memory_cost = match opcode {
722		Opcode::SHA3
723		| Opcode::RETURN
724		| Opcode::REVERT
725		| Opcode::LOG0
726		| Opcode::LOG1
727		| Opcode::LOG2
728		| Opcode::LOG3
729		| Opcode::LOG4 => Some(MemoryCost {
730			offset: U256::from_big_endian(&stack.peek(0)?[..]),
731			len: U256::from_big_endian(&stack.peek(1)?[..]),
732		}),
733
734		Opcode::MCOPY => {
735			let top0 = U256::from_big_endian(&stack.peek(0)?[..]);
736			let top1 = U256::from_big_endian(&stack.peek(1)?[..]);
737			let offset = top0.max(top1);
738			Some(MemoryCost {
739				offset,
740				len: U256::from_big_endian(&stack.peek(2)?[..]),
741			})
742		}
743
744		Opcode::CODECOPY | Opcode::CALLDATACOPY | Opcode::RETURNDATACOPY => Some(MemoryCost {
745			offset: U256::from_big_endian(&stack.peek(0)?[..]),
746			len: U256::from_big_endian(&stack.peek(2)?[..]),
747		}),
748
749		Opcode::EXTCODECOPY => Some(MemoryCost {
750			offset: U256::from_big_endian(&stack.peek(1)?[..]),
751			len: U256::from_big_endian(&stack.peek(3)?[..]),
752		}),
753
754		Opcode::MLOAD | Opcode::MSTORE => Some(MemoryCost {
755			offset: U256::from_big_endian(&stack.peek(0)?[..]),
756			len: U256::from(32),
757		}),
758
759		Opcode::MSTORE8 => Some(MemoryCost {
760			offset: U256::from_big_endian(&stack.peek(0)?[..]),
761			len: U256::from(1),
762		}),
763
764		Opcode::CREATE | Opcode::CREATE2 => Some(MemoryCost {
765			offset: U256::from_big_endian(&stack.peek(1)?[..]),
766			len: U256::from_big_endian(&stack.peek(2)?[..]),
767		}),
768
769		Opcode::CALL | Opcode::CALLCODE => Some(
770			MemoryCost {
771				offset: U256::from_big_endian(&stack.peek(3)?[..]),
772				len: U256::from_big_endian(&stack.peek(4)?[..]),
773			}
774			.join(MemoryCost {
775				offset: U256::from_big_endian(&stack.peek(5)?[..]),
776				len: U256::from_big_endian(&stack.peek(6)?[..]),
777			}),
778		),
779
780		Opcode::DELEGATECALL | Opcode::STATICCALL => Some(
781			MemoryCost {
782				offset: U256::from_big_endian(&stack.peek(2)?[..]),
783				len: U256::from_big_endian(&stack.peek(3)?[..]),
784			}
785			.join(MemoryCost {
786				offset: U256::from_big_endian(&stack.peek(4)?[..]),
787				len: U256::from_big_endian(&stack.peek(5)?[..]),
788			}),
789		),
790
791		_ => None,
792	};
793
794	Ok((gas_cost, storage_target, memory_cost))
795}
796
797#[derive(Clone, Debug)]
799struct Inner<'config> {
800	memory_gas: u64,
801	used_gas: u64,
802	refunded_gas: i64,
803	config: &'config Config,
804}
805
806impl Inner<'_> {
807	fn memory_gas(&self, memory: MemoryCost) -> Result<u64, ExitError> {
808		let from = memory.offset;
809		let len = memory.len;
810
811		if len == U256::zero() {
812			return Ok(self.memory_gas);
813		}
814
815		let end = from.checked_add(len).ok_or(ExitError::OutOfGas)?;
816
817		if end > U256::from(usize::MAX) {
818			return Err(ExitError::OutOfGas);
819		}
820		let end = end.as_usize();
821
822		let rem = end % 32;
823		let new = if rem == 0 { end / 32 } else { end / 32 + 1 };
824
825		Ok(max(self.memory_gas, memory::memory_gas(new)?))
826	}
827
828	fn extra_check(&self, cost: GasCost, after_gas: u64) -> Result<(), ExitError> {
829		match cost {
830			GasCost::Call { gas, .. } => costs::call_extra_check(gas, after_gas, self.config),
831			GasCost::CallCode { gas, .. } => costs::call_extra_check(gas, after_gas, self.config),
832			GasCost::DelegateCall { gas, .. } => {
833				costs::call_extra_check(gas, after_gas, self.config)
834			}
835			GasCost::StaticCall { gas, .. } => costs::call_extra_check(gas, after_gas, self.config),
836			_ => Ok(()),
837		}
838	}
839
840	fn gas_cost(&self, cost: GasCost, gas: u64) -> Result<u64, ExitError> {
842		Ok(match cost {
843			GasCost::Call {
844				value,
845				target_is_cold,
846				target_exists,
847				..
848			} => costs::call_cost(
849				value,
850				target_is_cold,
851				true,
852				true,
853				!target_exists,
854				self.config,
855			),
856			GasCost::CallCode {
857				value,
858				target_is_cold,
859				target_exists,
860				..
861			} => costs::call_cost(
862				value,
863				target_is_cold,
864				true,
865				false,
866				!target_exists,
867				self.config,
868			),
869			GasCost::DelegateCall {
870				target_is_cold,
871				target_exists,
872				..
873			} => costs::call_cost(
874				U256::zero(),
875				target_is_cold,
876				false,
877				false,
878				!target_exists,
879				self.config,
880			),
881			GasCost::StaticCall {
882				target_is_cold,
883				target_exists,
884				..
885			} => costs::call_cost(
886				U256::zero(),
887				target_is_cold,
888				false,
889				true,
890				!target_exists,
891				self.config,
892			),
893
894			GasCost::Suicide {
895				value,
896				target_is_cold,
897				target_exists,
898				..
899			} => costs::suicide_cost(value, target_is_cold, target_exists, self.config),
900			GasCost::SStore {
901				original,
902				current,
903				new,
904				target_is_cold,
905			} => costs::sstore_cost(original, current, new, gas, target_is_cold, self.config)?,
906
907			GasCost::TLoad => costs::tload_cost(self.config)?,
908			GasCost::TStore => costs::tstore_cost(self.config)?,
909
910			GasCost::Sha3 { len } => costs::sha3_cost(len)?,
911			GasCost::Log { n, len } => costs::log_cost(n, len)?,
912			GasCost::VeryLowCopy { len } => costs::verylowcopy_cost(len)?,
913			GasCost::Exp { power } => costs::exp_cost(power, self.config)?,
914			GasCost::Create => consts::G_CREATE,
915			GasCost::Create2 { len } => costs::create2_cost(len)?,
916			GasCost::SLoad { target_is_cold } => costs::sload_cost(target_is_cold, self.config),
917
918			GasCost::Zero => consts::G_ZERO,
919			GasCost::Base => consts::G_BASE,
920			GasCost::VeryLow => consts::G_VERYLOW,
921			GasCost::Low => consts::G_LOW,
922			GasCost::Invalid(opcode) => return Err(ExitError::InvalidCode(opcode)),
923
924			GasCost::ExtCodeSize { target_is_cold } => {
925				costs::address_access_cost(target_is_cold, self.config.gas_ext_code, self.config)
926			}
927			GasCost::ExtCodeCopy {
928				target_is_cold,
929				len,
930			} => costs::extcodecopy_cost(len, target_is_cold, self.config)?,
931			GasCost::Balance { target_is_cold } => {
932				costs::address_access_cost(target_is_cold, self.config.gas_balance, self.config)
933			}
934			GasCost::BlockHash => consts::G_BLOCKHASH,
935			GasCost::ExtCodeHash { target_is_cold } => costs::address_access_cost(
936				target_is_cold,
937				self.config.gas_ext_code_hash,
938				self.config,
939			),
940		})
941	}
942
943	fn gas_refund(&self, cost: GasCost) -> i64 {
944		match cost {
945			_ if self.config.estimate => 0,
946
947			GasCost::SStore {
948				original,
949				current,
950				new,
951				..
952			} => costs::sstore_refund(original, current, new, self.config),
953			GasCost::Suicide {
954				already_removed, ..
955			} if !self.config.decrease_clears_refund => costs::suicide_refund(already_removed),
956			_ => 0,
957		}
958	}
959}
960
961#[derive(Debug, Clone, Copy)]
963pub enum GasCost {
964	Zero,
966	Base,
968	VeryLow,
970	Low,
972	Invalid(Opcode),
974
975	ExtCodeSize {
977		target_is_cold: bool,
979	},
980	Balance {
982		target_is_cold: bool,
984	},
985	BlockHash,
987	ExtCodeHash {
989		target_is_cold: bool,
991	},
992
993	Call {
995		value: U256,
997		gas: U256,
999		target_is_cold: bool,
1001		target_exists: bool,
1003	},
1004	CallCode {
1006		value: U256,
1008		gas: U256,
1010		target_is_cold: bool,
1012		target_exists: bool,
1014	},
1015	DelegateCall {
1017		gas: U256,
1019		target_is_cold: bool,
1021		target_exists: bool,
1023	},
1024	StaticCall {
1026		gas: U256,
1028		target_is_cold: bool,
1030		target_exists: bool,
1032	},
1033	Suicide {
1035		value: U256,
1037		target_is_cold: bool,
1039		target_exists: bool,
1041		already_removed: bool,
1043	},
1044	SStore {
1046		original: H256,
1048		current: H256,
1050		new: H256,
1052		target_is_cold: bool,
1054	},
1055	Sha3 {
1057		len: U256,
1059	},
1060	Log {
1062		n: u8,
1064		len: U256,
1066	},
1067	ExtCodeCopy {
1069		target_is_cold: bool,
1071		len: U256,
1073	},
1074	VeryLowCopy {
1076		len: U256,
1078	},
1079	Exp {
1081		power: U256,
1083	},
1084	Create,
1086	Create2 {
1088		len: U256,
1090	},
1091	SLoad {
1093		target_is_cold: bool,
1095	},
1096	TLoad,
1098	TStore,
1100}
1101
1102#[derive(Debug, Clone, Copy)]
1104pub enum StorageTarget {
1105	None,
1107	Address(H160),
1109	Slot(H160, H256),
1111}
1112
1113#[derive(Debug, Clone, Copy)]
1115pub struct MemoryCost {
1116	pub offset: U256,
1118	pub len: U256,
1120}
1121
1122#[derive(Debug, Clone, Copy)]
1124pub enum TransactionCost {
1125	Call {
1127		zero_data_len: usize,
1129		non_zero_data_len: usize,
1131		access_list_address_len: usize,
1133		access_list_storage_len: usize,
1135		authorization_list_len: usize,
1137	},
1138	Create {
1140		zero_data_len: usize,
1142		non_zero_data_len: usize,
1144		access_list_address_len: usize,
1146		access_list_storage_len: usize,
1148		initcode_cost: u64,
1150		authorization_list_len: usize,
1152	},
1153}
1154
1155impl MemoryCost {
1156	pub fn join(self, other: MemoryCost) -> MemoryCost {
1158		if self.len == U256::zero() {
1159			return other;
1160		}
1161
1162		if other.len == U256::zero() {
1163			return self;
1164		}
1165
1166		let self_end = self.offset.saturating_add(self.len);
1167		let other_end = other.offset.saturating_add(other.len);
1168
1169		if self_end >= other_end {
1170			self
1171		} else {
1172			other
1173		}
1174	}
1175}