1#[cfg(not(feature = "std"))]
4pub mod prelude {
5 pub use alloc::vec::Vec;
6}
7
8#[cfg(feature = "std")]
9pub mod prelude {
10 pub use std::vec::Vec;
11}
12
13#[cfg(feature = "tracing")]
14pub mod tracing;
15
16#[cfg(feature = "tracing")]
17macro_rules! event {
18 ($x:expr) => {
19 use self::tracing::Event::*;
20 self::tracing::with(|listener| listener.event($x));
21 };
22}
23#[cfg(feature = "force-debug")]
24macro_rules! log_gas {
25 ($self:expr, $($arg:tt)*) => (
26 log::trace!(target: "evm", "Gasometer {} [Gas used: {}, Gas left: {}]", format_args!($($arg)*),
27 $self.total_used_gas(), $self.gas());
28 #[cfg(feature = "print-debug")]
29 println!("\t# {} [{} | {}]", format_args!($($arg)*), $self.total_used_gas(), $self.gas());
30 );
31}
32
33#[cfg(not(feature = "force-debug"))]
34macro_rules! log_gas {
35 ($self:expr, $($arg:tt)*) => {};
36}
37
38#[cfg(not(feature = "tracing"))]
39macro_rules! event {
40 ($x:expr) => {};
41}
42
43mod consts;
44mod costs;
45mod memory;
46mod utils;
47
48use crate::core::utils::U256_ZERO;
49use crate::core::{ExitError, Opcode, Stack};
50use crate::prelude::*;
51use crate::runtime::{Config, Handler};
52use core::cmp::max;
53use primitive_types::{H160, H256, U256};
54
55macro_rules! try_or_fail {
56 ( $inner:expr, $e:expr ) => {
57 match $e {
58 Ok(value) => value,
59 Err(e) => {
60 $inner = Err(e.clone());
61 return Err(e);
62 }
63 }
64 };
65}
66
67#[cfg(feature = "tracing")]
68#[derive(Debug, Copy, Clone)]
69pub struct Snapshot {
70 pub gas_limit: u64,
71 pub memory_gas: u64,
72 pub used_gas: u64,
73 pub refunded_gas: i64,
74}
75
76#[cfg(feature = "tracing")]
77impl Snapshot {
78 #[must_use]
79 const fn new<'config>(gas_limit: u64, inner: &'config Inner<'config>) -> Self {
80 Self {
81 gas_limit,
82 memory_gas: inner.memory_gas,
83 used_gas: inner.used_gas,
84 refunded_gas: inner.refunded_gas,
85 }
86 }
87}
88
89#[derive(Clone, Debug)]
91pub struct Gasometer<'config> {
92 gas_limit: u64,
93 config: &'config Config,
94 inner: Result<Inner<'config>, ExitError>,
95}
96
97impl<'config> Gasometer<'config> {
98 #[must_use]
100 pub const fn new(gas_limit: u64, config: &'config Config) -> Self {
101 Self {
102 gas_limit,
103 config,
104 inner: Ok(Inner {
105 memory_gas: 0,
106 used_gas: 0,
107 refunded_gas: 0,
108 floor_gas: 0,
109 config,
110 }),
111 }
112 }
113
114 #[inline]
119 pub fn gas_cost(&self, cost: GasCost, gas: u64) -> Result<u64, ExitError> {
120 match self.inner.as_ref() {
121 Ok(inner) => inner.gas_cost(cost, gas),
122 Err(e) => Err(e.clone()),
123 }
124 }
125
126 #[inline]
127 fn inner_mut(&mut self) -> Result<&mut Inner<'config>, ExitError> {
128 self.inner.as_mut().map_err(|e| e.clone())
129 }
130
131 #[inline]
133 #[must_use]
134 pub const fn config(&self) -> &'config Config {
135 self.config
136 }
137
138 #[inline]
140 #[must_use]
141 pub const fn gas_limit(&self) -> u64 {
142 self.gas_limit
143 }
144
145 #[inline]
147 #[must_use]
148 pub fn floor_gas(&self) -> u64 {
149 self.inner.as_ref().map_or(0, |inner| inner.floor_gas)
150 }
151
152 #[inline]
154 #[must_use]
155 pub fn gas(&self) -> u64 {
156 self.inner.as_ref().map_or(0, |inner| {
157 self.gas_limit - inner.used_gas - inner.memory_gas
158 })
159 }
160
161 #[inline]
163 #[must_use]
164 pub const fn total_used_gas(&self) -> u64 {
165 match self.inner.as_ref() {
166 Ok(inner) => inner.used_gas + inner.memory_gas,
167 Err(_) => self.gas_limit,
168 }
169 }
170
171 #[inline]
173 #[must_use]
174 pub fn refunded_gas(&self) -> i64 {
175 self.inner.as_ref().map_or(0, |inner| inner.refunded_gas)
176 }
177
178 pub fn fail(&mut self) -> ExitError {
180 self.inner = Err(ExitError::OutOfGas);
181 ExitError::OutOfGas
182 }
183
184 #[inline]
189 pub fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> {
190 event!(RecordCost {
191 cost,
192 snapshot: self.snapshot(),
193 });
194
195 let all_gas_cost = self.total_used_gas() + cost;
196 if self.gas_limit < all_gas_cost {
197 self.inner = Err(ExitError::OutOfGas);
198 return Err(ExitError::OutOfGas);
199 }
200
201 self.inner_mut()?.used_gas += cost;
202 log_gas!(self, "record_cost: {}", cost);
203 Ok(())
204 }
205
206 #[inline]
207 pub fn record_refund(&mut self, refund: i64) -> Result<(), ExitError> {
212 event!(RecordRefund {
213 refund,
214 snapshot: self.snapshot(),
215 });
216 log_gas!(self, "record_refund: -{}", refund);
217
218 self.inner_mut()?.refunded_gas += refund;
219 Ok(())
220 }
221
222 pub fn record_authority_refund(&mut self, refunded_accounts: u64) -> Result<(), ExitError> {
228 let refund = i64::try_from(
229 refunded_accounts
230 * (self.config.gas_per_empty_account_cost - self.config.gas_per_auth_base_cost),
231 )
232 .unwrap_or(i64::MAX);
233 self.record_refund(refund)
234 }
235
236 #[allow(clippy::as_conversions)]
242 #[inline]
243 pub fn record_deposit(&mut self, len: usize) -> Result<(), ExitError> {
244 let cost = len as u64 * u64::from(consts::G_CODEDEPOSIT);
245 self.record_cost(cost)
246 }
247
248 pub fn record_dynamic_cost(
253 &mut self,
254 cost: GasCost,
255 memory: Option<MemoryCost>,
256 ) -> Result<(), ExitError> {
257 let gas = self.gas();
258 let inner_mut = match &mut self.inner {
261 Ok(inner) => inner,
262 Err(err) => return Err(err.clone()),
263 };
264
265 let memory_gas = match memory {
266 Some(memory) => try_or_fail!(self.inner, inner_mut.memory_gas(memory)),
267 None => inner_mut.memory_gas,
268 };
269 let gas_cost = try_or_fail!(self.inner, inner_mut.gas_cost(cost, gas));
270 let gas_refund = inner_mut.gas_refund(cost);
271 let used_gas = inner_mut.used_gas;
272
273 #[cfg(feature = "tracing")]
274 let gas_limit = self.gas_limit;
275 event!(RecordDynamicCost {
276 gas_cost,
277 memory_gas,
278 gas_refund,
279 snapshot: Some(Snapshot::new(gas_limit, inner_mut)),
280 });
281
282 let all_gas_cost = memory_gas
283 .checked_add(used_gas.saturating_add(gas_cost))
284 .ok_or(ExitError::OutOfGas)?;
285 if self.gas_limit < all_gas_cost {
286 self.inner = Err(ExitError::OutOfGas);
287 return Err(ExitError::OutOfGas);
288 }
289
290 let after_gas = self.gas_limit - all_gas_cost;
291 try_or_fail!(self.inner, inner_mut.extra_check(cost, after_gas));
292
293 inner_mut.used_gas += gas_cost;
294 inner_mut.memory_gas = memory_gas;
295 inner_mut.refunded_gas += gas_refund;
296
297 log_gas!(
299 self,
300 "record_dynamic_cost: {gas_cost} - {memory_gas} - {gas_refund}"
301 );
302
303 Ok(())
304 }
305
306 #[inline]
311 pub fn record_stipend(&mut self, stipend: u64) -> Result<(), ExitError> {
312 event!(RecordStipend {
313 stipend,
314 snapshot: self.snapshot(),
315 });
316
317 self.inner_mut()?.used_gas -= stipend;
318 log_gas!(self, "record_stipent: {}", stipend);
319 Ok(())
320 }
321
322 pub fn record_transaction(&mut self, cost: TransactionCost) -> Result<(), ExitError> {
330 let gas_cost = match cost {
331 #[allow(clippy::as_conversions)]
333 TransactionCost::Call {
334 zero_data_len,
335 non_zero_data_len,
336 access_list_address_len,
337 access_list_storage_len,
338 authorization_list_len,
339 } => {
340 #[deny(clippy::let_and_return)]
341 let cost = self.config.gas_transaction_call
342 + zero_data_len as u64 * self.config.gas_transaction_zero_data
343 + non_zero_data_len as u64 * self.config.gas_transaction_non_zero_data
344 + access_list_address_len as u64 * self.config.gas_access_list_address
345 + access_list_storage_len as u64 * self.config.gas_access_list_storage_key
346 + authorization_list_len as u64 * self.config.gas_per_empty_account_cost;
347
348 if self.config.has_floor_gas {
349 let tokens_in_calldata = (zero_data_len + non_zero_data_len * 4) as u64;
352 self.inner_mut()?.floor_gas = tokens_in_calldata
353 * self.config.total_cost_floor_per_token
354 + self.config.gas_transaction_call;
355 }
356
357 log_gas!(
358 self,
359 "Record Call {} [gas_transaction_call: {}, zero_data_len: {}, non_zero_data_len: {}, access_list_address_len: {}, access_list_storage_len: {}, authorization_list_len: {}]",
360 cost,
361 self.config.gas_transaction_call,
362 zero_data_len,
363 non_zero_data_len,
364 access_list_address_len,
365 access_list_storage_len,
366 authorization_list_len
367 );
368
369 cost
370 }
371 #[allow(clippy::as_conversions)]
373 TransactionCost::Create {
374 zero_data_len,
375 non_zero_data_len,
376 access_list_address_len,
377 access_list_storage_len,
378 initcode_cost,
379 } => {
380 let mut cost = self.config.gas_transaction_create
381 + zero_data_len as u64 * self.config.gas_transaction_zero_data
382 + non_zero_data_len as u64 * self.config.gas_transaction_non_zero_data
383 + access_list_address_len as u64 * self.config.gas_access_list_address
384 + access_list_storage_len as u64 * self.config.gas_access_list_storage_key;
385 if self.config.max_initcode_size.is_some() {
386 cost += initcode_cost;
387 }
388
389 if self.config.has_floor_gas {
390 let tokens_in_calldata = (zero_data_len + non_zero_data_len * 4) as u64;
393 self.inner_mut()?.floor_gas = tokens_in_calldata
394 * self.config.total_cost_floor_per_token
395 + self.config.gas_transaction_call;
396 }
397
398 log_gas!(
399 self,
400 "Record Create {} [gas_transaction_create: {}, zero_data_len: {}, non_zero_data_len: {}, access_list_address_len: {}, access_list_storage_len: {}, initcode_cost: {}]",
401 cost,
402 self.config.gas_transaction_create,
403 zero_data_len,
404 non_zero_data_len,
405 access_list_address_len,
406 access_list_storage_len,
407 initcode_cost
408 );
409 cost
410 }
411 };
412
413 event!(RecordTransaction {
414 cost: gas_cost,
415 snapshot: self.snapshot(),
416 });
417
418 if self.gas() < gas_cost {
419 self.inner = Err(ExitError::OutOfGas);
420 return Err(ExitError::OutOfGas);
421 }
422
423 self.inner_mut()?.used_gas += gas_cost;
424 Ok(())
425 }
426
427 #[cfg(feature = "tracing")]
428 #[must_use]
429 pub fn snapshot(&self) -> Option<Snapshot> {
430 self.inner
431 .as_ref()
432 .ok()
433 .map(|inner| Snapshot::new(self.gas_limit, inner))
434 }
435}
436
437#[allow(clippy::naive_bytecount)]
439#[must_use]
440pub fn call_transaction_cost(
441 data: &[u8],
442 access_list: &[(H160, Vec<H256>)],
443 authorization_list_len: usize,
444) -> TransactionCost {
445 let zero_data_len = data.iter().filter(|v| **v == 0).count();
446 let non_zero_data_len = data.len() - zero_data_len;
447 let (access_list_address_len, access_list_storage_len) = count_access_list(access_list);
448
449 TransactionCost::Call {
450 zero_data_len,
451 non_zero_data_len,
452 access_list_address_len,
453 access_list_storage_len,
454 authorization_list_len,
455 }
456}
457
458#[allow(clippy::naive_bytecount)]
460#[must_use]
461pub fn create_transaction_cost(data: &[u8], access_list: &[(H160, Vec<H256>)]) -> TransactionCost {
462 let zero_data_len = data.iter().filter(|v| **v == 0).count();
463 let non_zero_data_len = data.len() - zero_data_len;
464 let (access_list_address_len, access_list_storage_len) = count_access_list(access_list);
465 let initcode_cost = init_code_cost(data);
466
467 TransactionCost::Create {
468 zero_data_len,
469 non_zero_data_len,
470 access_list_address_len,
471 access_list_storage_len,
472 initcode_cost,
473 }
474}
475
476#[allow(clippy::as_conversions)]
479#[must_use]
480pub const fn init_code_cost(data: &[u8]) -> u64 {
481 2 * (data.len() as u64).div_ceil(32)
485}
486
487fn count_access_list(access_list: &[(H160, Vec<H256>)]) -> (usize, usize) {
489 let access_list_address_len = access_list.len();
490 let access_list_storage_len = access_list.iter().map(|(_, keys)| keys.len()).sum();
491
492 (access_list_address_len, access_list_storage_len)
493}
494
495#[allow(clippy::too_many_lines)]
496#[inline]
497#[must_use]
498pub fn static_opcode_cost(opcode: Opcode) -> Option<u32> {
499 static TABLE: [Option<u32>; 256] = {
500 let mut table = [None; 256];
501
502 table[Opcode::STOP.as_usize()] = Some(consts::G_ZERO);
503 table[Opcode::CALLDATASIZE.as_usize()] = Some(consts::G_BASE);
504 table[Opcode::CODESIZE.as_usize()] = Some(consts::G_BASE);
505 table[Opcode::POP.as_usize()] = Some(consts::G_BASE);
506 table[Opcode::PC.as_usize()] = Some(consts::G_BASE);
507 table[Opcode::MSIZE.as_usize()] = Some(consts::G_BASE);
508
509 table[Opcode::ADDRESS.as_usize()] = Some(consts::G_BASE);
510 table[Opcode::ORIGIN.as_usize()] = Some(consts::G_BASE);
511 table[Opcode::CALLER.as_usize()] = Some(consts::G_BASE);
512 table[Opcode::CALLVALUE.as_usize()] = Some(consts::G_BASE);
513 table[Opcode::COINBASE.as_usize()] = Some(consts::G_BASE);
514 table[Opcode::TIMESTAMP.as_usize()] = Some(consts::G_BASE);
515 table[Opcode::NUMBER.as_usize()] = Some(consts::G_BASE);
516 table[Opcode::PREVRANDAO.as_usize()] = Some(consts::G_BASE);
517 table[Opcode::GASLIMIT.as_usize()] = Some(consts::G_BASE);
518 table[Opcode::GASPRICE.as_usize()] = Some(consts::G_BASE);
519 table[Opcode::GAS.as_usize()] = Some(consts::G_BASE);
520
521 table[Opcode::ADD.as_usize()] = Some(consts::G_VERYLOW);
522 table[Opcode::SUB.as_usize()] = Some(consts::G_VERYLOW);
523 table[Opcode::NOT.as_usize()] = Some(consts::G_VERYLOW);
524 table[Opcode::LT.as_usize()] = Some(consts::G_VERYLOW);
525 table[Opcode::GT.as_usize()] = Some(consts::G_VERYLOW);
526 table[Opcode::SLT.as_usize()] = Some(consts::G_VERYLOW);
527 table[Opcode::SGT.as_usize()] = Some(consts::G_VERYLOW);
528 table[Opcode::EQ.as_usize()] = Some(consts::G_VERYLOW);
529 table[Opcode::ISZERO.as_usize()] = Some(consts::G_VERYLOW);
530 table[Opcode::AND.as_usize()] = Some(consts::G_VERYLOW);
531 table[Opcode::OR.as_usize()] = Some(consts::G_VERYLOW);
532 table[Opcode::XOR.as_usize()] = Some(consts::G_VERYLOW);
533 table[Opcode::BYTE.as_usize()] = Some(consts::G_VERYLOW);
534 table[Opcode::CALLDATALOAD.as_usize()] = Some(consts::G_VERYLOW);
535 table[Opcode::PUSH1.as_usize()] = Some(consts::G_VERYLOW);
536 table[Opcode::PUSH2.as_usize()] = Some(consts::G_VERYLOW);
537 table[Opcode::PUSH3.as_usize()] = Some(consts::G_VERYLOW);
538 table[Opcode::PUSH4.as_usize()] = Some(consts::G_VERYLOW);
539 table[Opcode::PUSH5.as_usize()] = Some(consts::G_VERYLOW);
540 table[Opcode::PUSH6.as_usize()] = Some(consts::G_VERYLOW);
541 table[Opcode::PUSH7.as_usize()] = Some(consts::G_VERYLOW);
542 table[Opcode::PUSH8.as_usize()] = Some(consts::G_VERYLOW);
543 table[Opcode::PUSH9.as_usize()] = Some(consts::G_VERYLOW);
544 table[Opcode::PUSH10.as_usize()] = Some(consts::G_VERYLOW);
545 table[Opcode::PUSH11.as_usize()] = Some(consts::G_VERYLOW);
546 table[Opcode::PUSH12.as_usize()] = Some(consts::G_VERYLOW);
547 table[Opcode::PUSH13.as_usize()] = Some(consts::G_VERYLOW);
548 table[Opcode::PUSH14.as_usize()] = Some(consts::G_VERYLOW);
549 table[Opcode::PUSH15.as_usize()] = Some(consts::G_VERYLOW);
550 table[Opcode::PUSH16.as_usize()] = Some(consts::G_VERYLOW);
551 table[Opcode::PUSH17.as_usize()] = Some(consts::G_VERYLOW);
552 table[Opcode::PUSH18.as_usize()] = Some(consts::G_VERYLOW);
553 table[Opcode::PUSH19.as_usize()] = Some(consts::G_VERYLOW);
554 table[Opcode::PUSH20.as_usize()] = Some(consts::G_VERYLOW);
555 table[Opcode::PUSH21.as_usize()] = Some(consts::G_VERYLOW);
556 table[Opcode::PUSH22.as_usize()] = Some(consts::G_VERYLOW);
557 table[Opcode::PUSH23.as_usize()] = Some(consts::G_VERYLOW);
558 table[Opcode::PUSH24.as_usize()] = Some(consts::G_VERYLOW);
559 table[Opcode::PUSH25.as_usize()] = Some(consts::G_VERYLOW);
560 table[Opcode::PUSH26.as_usize()] = Some(consts::G_VERYLOW);
561 table[Opcode::PUSH27.as_usize()] = Some(consts::G_VERYLOW);
562 table[Opcode::PUSH28.as_usize()] = Some(consts::G_VERYLOW);
563 table[Opcode::PUSH29.as_usize()] = Some(consts::G_VERYLOW);
564 table[Opcode::PUSH30.as_usize()] = Some(consts::G_VERYLOW);
565 table[Opcode::PUSH31.as_usize()] = Some(consts::G_VERYLOW);
566 table[Opcode::PUSH32.as_usize()] = Some(consts::G_VERYLOW);
567 table[Opcode::DUP1.as_usize()] = Some(consts::G_VERYLOW);
568 table[Opcode::DUP2.as_usize()] = Some(consts::G_VERYLOW);
569 table[Opcode::DUP3.as_usize()] = Some(consts::G_VERYLOW);
570 table[Opcode::DUP4.as_usize()] = Some(consts::G_VERYLOW);
571 table[Opcode::DUP5.as_usize()] = Some(consts::G_VERYLOW);
572 table[Opcode::DUP6.as_usize()] = Some(consts::G_VERYLOW);
573 table[Opcode::DUP7.as_usize()] = Some(consts::G_VERYLOW);
574 table[Opcode::DUP8.as_usize()] = Some(consts::G_VERYLOW);
575 table[Opcode::DUP9.as_usize()] = Some(consts::G_VERYLOW);
576 table[Opcode::DUP10.as_usize()] = Some(consts::G_VERYLOW);
577 table[Opcode::DUP11.as_usize()] = Some(consts::G_VERYLOW);
578 table[Opcode::DUP12.as_usize()] = Some(consts::G_VERYLOW);
579 table[Opcode::DUP13.as_usize()] = Some(consts::G_VERYLOW);
580 table[Opcode::DUP14.as_usize()] = Some(consts::G_VERYLOW);
581 table[Opcode::DUP15.as_usize()] = Some(consts::G_VERYLOW);
582 table[Opcode::DUP16.as_usize()] = Some(consts::G_VERYLOW);
583 table[Opcode::SWAP1.as_usize()] = Some(consts::G_VERYLOW);
584 table[Opcode::SWAP2.as_usize()] = Some(consts::G_VERYLOW);
585 table[Opcode::SWAP3.as_usize()] = Some(consts::G_VERYLOW);
586 table[Opcode::SWAP4.as_usize()] = Some(consts::G_VERYLOW);
587 table[Opcode::SWAP5.as_usize()] = Some(consts::G_VERYLOW);
588 table[Opcode::SWAP6.as_usize()] = Some(consts::G_VERYLOW);
589 table[Opcode::SWAP7.as_usize()] = Some(consts::G_VERYLOW);
590 table[Opcode::SWAP8.as_usize()] = Some(consts::G_VERYLOW);
591 table[Opcode::SWAP9.as_usize()] = Some(consts::G_VERYLOW);
592 table[Opcode::SWAP10.as_usize()] = Some(consts::G_VERYLOW);
593 table[Opcode::SWAP11.as_usize()] = Some(consts::G_VERYLOW);
594 table[Opcode::SWAP12.as_usize()] = Some(consts::G_VERYLOW);
595 table[Opcode::SWAP13.as_usize()] = Some(consts::G_VERYLOW);
596 table[Opcode::SWAP14.as_usize()] = Some(consts::G_VERYLOW);
597 table[Opcode::SWAP15.as_usize()] = Some(consts::G_VERYLOW);
598 table[Opcode::SWAP16.as_usize()] = Some(consts::G_VERYLOW);
599
600 table[Opcode::MUL.as_usize()] = Some(consts::G_LOW);
601 table[Opcode::DIV.as_usize()] = Some(consts::G_LOW);
602 table[Opcode::SDIV.as_usize()] = Some(consts::G_LOW);
603 table[Opcode::MOD.as_usize()] = Some(consts::G_LOW);
604 table[Opcode::SMOD.as_usize()] = Some(consts::G_LOW);
605 table[Opcode::SIGNEXTEND.as_usize()] = Some(consts::G_LOW);
606
607 table[Opcode::ADDMOD.as_usize()] = Some(consts::G_MID);
608 table[Opcode::MULMOD.as_usize()] = Some(consts::G_MID);
609 table[Opcode::JUMP.as_usize()] = Some(consts::G_MID);
610
611 table[Opcode::JUMPI.as_usize()] = Some(consts::G_HIGH);
612 table[Opcode::JUMPDEST.as_usize()] = Some(consts::G_JUMPDEST);
613
614 table
615 };
616
617 TABLE[opcode.as_usize()]
618}
619
620fn get_and_set_warm<H: Handler>(handler: &mut H, target: H160) -> (bool, Option<bool>) {
622 let delegated_designator_is_cold =
623 handler
624 .get_authority_target(target)
625 .map(|authority_target| {
626 if handler.is_cold(authority_target, None) {
627 handler.warm_target((authority_target, None));
628 true
629 } else {
630 false
631 }
632 });
633 let target_is_cold = handler.is_cold(target, None);
634 if target_is_cold {
635 handler.warm_target((target, None));
636 }
637 (target_is_cold, delegated_designator_is_cold)
638}
639
640fn get_and_set_non_delegated_warm<H: Handler>(handler: &mut H, target: H160) -> bool {
643 let target_is_cold = handler.is_cold(target, None);
644 if target_is_cold {
645 handler.warm_target((target, None));
646 }
647 target_is_cold
648}
649
650#[allow(
655 clippy::nonminimal_bool,
656 clippy::cognitive_complexity,
657 clippy::too_many_lines,
658 clippy::match_same_arms
659)]
660pub fn dynamic_opcode_cost<H: Handler>(
661 address: H160,
662 opcode: Opcode,
663 stack: &Stack,
664 is_static: bool,
665 config: &Config,
666 handler: &mut H,
667) -> Result<(GasCost, Option<MemoryCost>), ExitError> {
668 let gas_cost = match opcode {
669 Opcode::RETURN => GasCost::Zero,
670
671 Opcode::MLOAD | Opcode::MSTORE | Opcode::MSTORE8 => GasCost::VeryLow,
672
673 Opcode::REVERT if config.has_revert => GasCost::Zero,
674 Opcode::REVERT => GasCost::Invalid(opcode),
675
676 Opcode::CHAINID if config.has_chain_id => GasCost::Base,
677 Opcode::CHAINID => GasCost::Invalid(opcode),
678
679 Opcode::SHL | Opcode::SHR | Opcode::SAR if config.has_bitwise_shifting => GasCost::VeryLow,
680 Opcode::SHL | Opcode::SHR | Opcode::SAR => GasCost::Invalid(opcode),
681
682 Opcode::SELFBALANCE if config.has_self_balance => GasCost::Low,
683 Opcode::SELFBALANCE => GasCost::Invalid(opcode),
684
685 Opcode::BASEFEE if config.has_base_fee => GasCost::Base,
686 Opcode::BASEFEE => GasCost::Invalid(opcode),
687
688 Opcode::BLOBBASEFEE if config.has_blob_base_fee => GasCost::Base,
689 Opcode::BLOBBASEFEE => GasCost::Invalid(opcode),
690
691 Opcode::BLOBHASH if config.has_shard_blob_transactions => GasCost::VeryLow,
692 Opcode::BLOBHASH => GasCost::Invalid(opcode),
693
694 Opcode::TLOAD if config.has_transient_storage => GasCost::WarmStorageRead,
695 Opcode::TLOAD => GasCost::Invalid(opcode),
696
697 Opcode::TSTORE if !is_static && config.has_transient_storage => GasCost::WarmStorageRead,
698 Opcode::TSTORE => GasCost::Invalid(opcode),
699
700 Opcode::MCOPY if config.has_mcopy => GasCost::VeryLowCopy {
701 len: stack.peek(2)?,
702 },
703 Opcode::MCOPY => GasCost::Invalid(opcode),
704
705 Opcode::EXTCODESIZE => {
706 let target = stack.peek_h256(0)?.into();
707 let target_is_cold = get_and_set_non_delegated_warm(handler, target);
708 GasCost::ExtCodeSize { target_is_cold }
709 }
710 Opcode::BALANCE => {
711 let target = stack.peek_h256(0)?.into();
712 let target_is_cold = handler.is_cold(target, None);
713 if target_is_cold {
714 handler.warm_target((target, None));
715 }
716 GasCost::Balance { target_is_cold }
717 }
718 Opcode::BLOCKHASH => GasCost::BlockHash,
719
720 Opcode::EXTCODEHASH if config.has_ext_code_hash => {
721 let target = stack.peek_h256(0)?.into();
722 let target_is_cold = get_and_set_non_delegated_warm(handler, target);
723 GasCost::ExtCodeHash { target_is_cold }
724 }
725 Opcode::EXTCODEHASH => GasCost::Invalid(opcode),
726
727 Opcode::CALLCODE => {
728 let target = stack.peek_h256(1)?.into();
729 let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target);
730 GasCost::CallCode {
731 value: stack.peek(2)?,
732 gas: stack.peek(0)?,
733 target_is_cold,
734 delegated_designator_is_cold,
735 target_exists: {
736 handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
737 handler.exists(target)
738 },
739 }
740 }
741 Opcode::STATICCALL => {
742 let target = stack.peek_h256(1)?.into();
743 let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target);
744 GasCost::StaticCall {
745 gas: stack.peek(0)?,
746 target_is_cold,
747 delegated_designator_is_cold,
748 target_exists: {
749 handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
750 handler.exists(target)
751 },
752 }
753 }
754 Opcode::SHA3 => GasCost::Sha3 {
755 len: stack.peek(1)?,
756 },
757 Opcode::EXTCODECOPY => {
758 let target = stack.peek_h256(0)?.into();
759 let target_is_cold = get_and_set_non_delegated_warm(handler, target);
760 GasCost::ExtCodeCopy {
761 target_is_cold,
762 len: stack.peek(3)?,
763 }
764 }
765 Opcode::CALLDATACOPY | Opcode::CODECOPY => GasCost::VeryLowCopy {
766 len: stack.peek(2)?,
767 },
768 Opcode::EXP => GasCost::Exp {
769 power: stack.peek(1)?,
770 },
771 Opcode::SLOAD => {
772 let index = stack.peek_h256(0)?;
773 let target_is_cold = handler.is_cold(address, Some(index));
774 if target_is_cold {
775 handler.warm_target((address, Some(index)));
776 }
777 GasCost::SLoad { target_is_cold }
778 }
779
780 Opcode::DELEGATECALL if config.has_delegate_call => {
781 let target = stack.peek_h256(1)?.into();
782 let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target);
783 GasCost::DelegateCall {
784 gas: stack.peek(0)?,
785 target_is_cold,
786 delegated_designator_is_cold,
787 target_exists: {
788 handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
789 handler.exists(target)
790 },
791 }
792 }
793 Opcode::DELEGATECALL => GasCost::Invalid(opcode),
794
795 Opcode::RETURNDATASIZE if config.has_return_data => GasCost::Base,
796 Opcode::RETURNDATACOPY if config.has_return_data => GasCost::VeryLowCopy {
797 len: stack.peek(2)?,
798 },
799 Opcode::RETURNDATASIZE | Opcode::RETURNDATACOPY => GasCost::Invalid(opcode),
800
801 Opcode::SSTORE if !is_static => {
802 let index = stack.peek_h256(0)?;
803 let value = stack.peek_h256(1)?;
804 let target_is_cold = handler.is_cold(address, Some(index));
805 if target_is_cold {
806 handler.warm_target((address, Some(index)));
807 }
808 GasCost::SStore {
809 original: handler.original_storage(address, index),
810 current: handler.storage(address, index),
811 new: value,
812 target_is_cold,
813 }
814 }
815 Opcode::LOG0 if !is_static => GasCost::Log {
816 n: 0,
817 len: stack.peek(1)?,
818 },
819 Opcode::LOG1 if !is_static => GasCost::Log {
820 n: 1,
821 len: stack.peek(1)?,
822 },
823 Opcode::LOG2 if !is_static => GasCost::Log {
824 n: 2,
825 len: stack.peek(1)?,
826 },
827 Opcode::LOG3 if !is_static => GasCost::Log {
828 n: 3,
829 len: stack.peek(1)?,
830 },
831 Opcode::LOG4 if !is_static => GasCost::Log {
832 n: 4,
833 len: stack.peek(1)?,
834 },
835 Opcode::CREATE if !is_static => GasCost::Create,
836 Opcode::CREATE2 if !is_static && config.has_create2 => GasCost::Create2 {
837 len: stack.peek(2)?,
838 },
839 Opcode::SELFDESTRUCT if !is_static => {
840 let target = stack.peek_h256(0)?.into();
841 let target_is_cold = handler.is_cold(target, None);
842 if target_is_cold {
843 handler.warm_target((target, None));
844 }
845 GasCost::Suicide {
846 value: handler.balance(address),
847 target_is_cold,
848 target_exists: {
849 handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
850 handler.exists(target)
851 },
852 already_removed: handler.deleted(address),
853 }
854 }
855 Opcode::CALL if !is_static || (is_static && stack.peek(2)? == U256_ZERO) => {
856 let target = stack.peek_h256(1)?.into();
857 let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target);
858 GasCost::Call {
859 value: stack.peek(2)?,
860 gas: stack.peek(0)?,
861 target_is_cold,
862 delegated_designator_is_cold,
863 target_exists: {
864 handler.record_external_operation(crate::core::ExternalOperation::IsEmpty)?;
865 handler.exists(target)
866 },
867 }
868 }
869
870 Opcode::PUSH0 if config.has_push0 => GasCost::Base,
871
872 _ => GasCost::Invalid(opcode),
873 };
874
875 let memory_cost = match opcode {
876 Opcode::SHA3
877 | Opcode::RETURN
878 | Opcode::REVERT
879 | Opcode::LOG0
880 | Opcode::LOG1
881 | Opcode::LOG2
882 | Opcode::LOG3
883 | Opcode::LOG4 => Some(peek_memory_cost(stack, 0, 1)?),
884
885 Opcode::CODECOPY | Opcode::CALLDATACOPY | Opcode::RETURNDATACOPY => {
886 Some(peek_memory_cost(stack, 0, 2)?)
887 }
888
889 Opcode::EXTCODECOPY => Some(peek_memory_cost(stack, 1, 3)?),
890
891 Opcode::MLOAD | Opcode::MSTORE => Some(MemoryCost {
892 offset: stack.peek_usize(0)?,
893 len: 32,
894 }),
895
896 Opcode::MCOPY => {
897 let len = stack.peek_usize(2)?;
898 if len == 0 {
899 None
900 } else {
901 Some(MemoryCost {
902 offset: {
903 let src = stack.peek_usize(0)?;
904 let dst = stack.peek_usize(1)?;
905 max(src, dst)
906 },
907 len,
908 })
909 }
910 }
911
912 Opcode::MSTORE8 => Some(MemoryCost {
913 offset: stack.peek_usize(0)?,
914 len: 1,
915 }),
916
917 Opcode::CREATE | Opcode::CREATE2 => Some(peek_memory_cost(stack, 1, 2)?),
918
919 Opcode::CALL | Opcode::CALLCODE => {
920 Some(peek_memory_cost(stack, 3, 4)?.join(peek_memory_cost(stack, 5, 6)?))
921 }
922
923 Opcode::DELEGATECALL | Opcode::STATICCALL => {
924 Some(peek_memory_cost(stack, 2, 3)?.join(peek_memory_cost(stack, 4, 5)?))
925 }
926
927 _ => None,
928 };
929
930 Ok((gas_cost, memory_cost))
931}
932
933fn peek_memory_cost(
934 stack: &Stack,
935 offset_index: usize,
936 len_index: usize,
937) -> Result<MemoryCost, ExitError> {
938 let len = stack.peek_usize(len_index)?;
939
940 if len == 0 {
941 return Ok(MemoryCost {
942 offset: usize::MAX,
943 len,
944 });
945 }
946
947 let offset = stack.peek_usize(offset_index)?;
948 Ok(MemoryCost { offset, len })
949}
950
951#[derive(Clone, Debug)]
953struct Inner<'config> {
954 memory_gas: u64,
955 used_gas: u64,
956 refunded_gas: i64,
957 config: &'config Config,
958 floor_gas: u64,
959}
960
961impl Inner<'_> {
962 fn memory_gas(&self, memory: MemoryCost) -> Result<u64, ExitError> {
963 let from = memory.offset;
964 let len = memory.len;
965
966 if len == 0 {
967 return Ok(self.memory_gas);
968 }
969
970 let end = from.checked_add(len).ok_or(ExitError::OutOfGas)?;
971
972 let rem = end % 32;
973 let new = if rem == 0 { end / 32 } else { end / 32 + 1 };
974
975 Ok(max(self.memory_gas, memory::memory_gas(new)?))
976 }
977
978 fn extra_check(&self, cost: GasCost, after_gas: u64) -> Result<(), ExitError> {
979 match cost {
980 GasCost::Call { gas, .. }
981 | GasCost::CallCode { gas, .. }
982 | GasCost::DelegateCall { gas, .. }
983 | GasCost::StaticCall { gas, .. } => {
984 costs::call_extra_check(gas, after_gas, self.config)
985 }
986 _ => Ok(()),
987 }
988 }
989
990 #[allow(clippy::too_many_lines)]
992 fn gas_cost(&self, cost: GasCost, gas: u64) -> Result<u64, ExitError> {
993 Ok(match cost {
994 GasCost::Call {
995 value,
996 target_is_cold,
997 delegated_designator_is_cold,
998 target_exists,
999 ..
1000 } => costs::call_cost(
1001 value,
1002 target_is_cold,
1003 delegated_designator_is_cold,
1004 true,
1005 true,
1006 !target_exists,
1007 self.config,
1008 ),
1009 GasCost::CallCode {
1010 value,
1011 target_is_cold,
1012 delegated_designator_is_cold,
1013 target_exists,
1014 ..
1015 } => costs::call_cost(
1016 value,
1017 target_is_cold,
1018 delegated_designator_is_cold,
1019 true,
1020 false,
1021 !target_exists,
1022 self.config,
1023 ),
1024 GasCost::DelegateCall {
1025 target_is_cold,
1026 delegated_designator_is_cold,
1027 target_exists,
1028 ..
1029 } => costs::call_cost(
1030 U256_ZERO,
1031 target_is_cold,
1032 delegated_designator_is_cold,
1033 false,
1034 false,
1035 !target_exists,
1036 self.config,
1037 ),
1038 GasCost::StaticCall {
1039 target_is_cold,
1040 delegated_designator_is_cold,
1041 target_exists,
1042 ..
1043 } => costs::call_cost(
1044 U256_ZERO,
1045 target_is_cold,
1046 delegated_designator_is_cold,
1047 false,
1048 true,
1049 !target_exists,
1050 self.config,
1051 ),
1052
1053 GasCost::Suicide {
1054 value,
1055 target_is_cold,
1056 target_exists,
1057 ..
1058 } => costs::suicide_cost(value, target_is_cold, target_exists, self.config),
1059 GasCost::SStore {
1060 original,
1061 current,
1062 new,
1063 target_is_cold,
1064 } => costs::sstore_cost(original, current, new, gas, target_is_cold, self.config)?,
1065
1066 GasCost::Sha3 { len } => costs::sha3_cost(len)?,
1067 GasCost::Log { n, len } => costs::log_cost(n, len)?,
1068 GasCost::VeryLowCopy { len } => costs::verylowcopy_cost(len)?,
1069 GasCost::Exp { power } => costs::exp_cost(power, self.config)?,
1070 GasCost::Create => u64::from(consts::G_CREATE),
1071 GasCost::Create2 { len } => costs::create2_cost(len)?,
1072 GasCost::SLoad { target_is_cold } => costs::sload_cost(target_is_cold, self.config),
1073
1074 GasCost::Zero => u64::from(consts::G_ZERO),
1075 GasCost::Base => u64::from(consts::G_BASE),
1076 GasCost::VeryLow => u64::from(consts::G_VERYLOW),
1077 GasCost::Low => u64::from(consts::G_LOW),
1078 GasCost::Invalid(opcode) => return Err(ExitError::InvalidCode(opcode)),
1079
1080 GasCost::ExtCodeSize { target_is_cold } => costs::non_delegated_access_cost(
1081 target_is_cold,
1082 self.config.gas_ext_code,
1083 self.config,
1084 ),
1085 GasCost::ExtCodeCopy {
1086 target_is_cold,
1087 len,
1088 } => costs::ext_codecopy_cost(len, target_is_cold, self.config)?,
1089 GasCost::ExtCodeHash { target_is_cold } => costs::non_delegated_access_cost(
1090 target_is_cold,
1091 self.config.gas_ext_code_hash,
1092 self.config,
1093 ),
1094
1095 GasCost::Balance { target_is_cold } => costs::non_delegated_access_cost(
1096 target_is_cold,
1097 self.config.gas_balance,
1098 self.config,
1099 ),
1100 GasCost::BlockHash => u64::from(consts::G_BLOCKHASH),
1101 GasCost::WarmStorageRead => costs::storage_read_warm(self.config),
1102 })
1103 }
1104
1105 fn gas_refund(&self, cost: GasCost) -> i64 {
1106 match cost {
1107 _ if self.config.estimate => 0,
1108
1109 GasCost::SStore {
1110 original,
1111 current,
1112 new,
1113 ..
1114 } => costs::sstore_refund(original, current, new, self.config),
1115 GasCost::Suicide {
1116 already_removed, ..
1117 } if !self.config.decrease_clears_refund => costs::suicide_refund(already_removed),
1118 _ => 0,
1119 }
1120 }
1121}
1122
1123#[derive(Debug, Clone, Copy)]
1125pub enum GasCost {
1126 Zero,
1128 Base,
1130 VeryLow,
1132 Low,
1134 Invalid(Opcode),
1136
1137 ExtCodeSize {
1139 target_is_cold: bool,
1141 },
1142 Balance {
1144 target_is_cold: bool,
1146 },
1147 BlockHash,
1149 ExtCodeHash {
1151 target_is_cold: bool,
1153 },
1154
1155 Call {
1157 value: U256,
1159 gas: U256,
1161 target_is_cold: bool,
1163 delegated_designator_is_cold: Option<bool>,
1165 target_exists: bool,
1167 },
1168 CallCode {
1170 value: U256,
1172 gas: U256,
1174 target_is_cold: bool,
1176 delegated_designator_is_cold: Option<bool>,
1178 target_exists: bool,
1180 },
1181 DelegateCall {
1183 gas: U256,
1185 target_is_cold: bool,
1187 delegated_designator_is_cold: Option<bool>,
1189 target_exists: bool,
1191 },
1192 StaticCall {
1194 gas: U256,
1196 target_is_cold: bool,
1198 delegated_designator_is_cold: Option<bool>,
1200 target_exists: bool,
1202 },
1203 Suicide {
1205 value: U256,
1207 target_is_cold: bool,
1209 target_exists: bool,
1211 already_removed: bool,
1213 },
1214 SStore {
1216 original: H256,
1218 current: H256,
1220 new: H256,
1222 target_is_cold: bool,
1224 },
1225 Sha3 {
1227 len: U256,
1229 },
1230 Log {
1232 n: u8,
1234 len: U256,
1236 },
1237 ExtCodeCopy {
1239 target_is_cold: bool,
1241 len: U256,
1243 },
1244 VeryLowCopy {
1246 len: U256,
1248 },
1249 Exp {
1251 power: U256,
1253 },
1254 Create,
1256 Create2 {
1258 len: U256,
1260 },
1261 SLoad {
1263 target_is_cold: bool,
1265 },
1266 WarmStorageRead,
1267}
1268
1269#[derive(Debug, Clone, Copy)]
1271pub enum StorageTarget {
1272 None,
1274 Address(H160),
1276 Slot(H160, H256),
1278}
1279
1280#[derive(Debug, Clone, Copy)]
1282pub struct MemoryCost {
1283 pub offset: usize,
1285 pub len: usize,
1287}
1288
1289#[derive(Debug, Clone, Copy)]
1291pub enum TransactionCost {
1292 Call {
1294 zero_data_len: usize,
1296 non_zero_data_len: usize,
1298 access_list_address_len: usize,
1300 access_list_storage_len: usize,
1302 authorization_list_len: usize,
1304 },
1305 Create {
1307 zero_data_len: usize,
1309 non_zero_data_len: usize,
1311 access_list_address_len: usize,
1313 access_list_storage_len: usize,
1315 initcode_cost: u64,
1317 },
1318}
1319
1320impl MemoryCost {
1321 #[must_use]
1323 pub const fn join(self, other: Self) -> Self {
1324 if self.len == 0 {
1325 return other;
1326 }
1327
1328 if other.len == 0 {
1329 return self;
1330 }
1331
1332 let self_end = self.offset.saturating_add(self.len);
1333 let other_end = other.offset.saturating_add(other.len);
1334
1335 if self_end >= other_end {
1336 self
1337 } else {
1338 other
1339 }
1340 }
1341}