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}