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