revmc_builtins/
gas.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! Gas calculation utilities.

use revm_primitives::{SpecId, U256};

pub use revm_interpreter::gas::*;

/// `const` Option `?`.
#[allow(unused_macros)]
macro_rules! tri {
    ($e:expr) => {
        match $e {
            Some(v) => v,
            None => return None,
        }
    };
}

// These are overridden to only account for the dynamic cost.

/// `EXP` opcode cost calculation.
#[inline]
pub fn dyn_exp_cost(spec_id: SpecId, power: U256) -> Option<u64> {
    #[inline]
    const fn log2floor(value: U256) -> u64 {
        let mut l: u64 = 256;
        let mut i = 3;
        loop {
            if value.as_limbs()[i] == 0u64 {
                l -= 64;
            } else {
                l -= value.as_limbs()[i].leading_zeros() as u64;
                if l == 0 {
                    return l;
                } else {
                    return l - 1;
                }
            }
            if i == 0 {
                break;
            }
            i -= 1;
        }
        l
    }

    if power == U256::ZERO {
        Some(0)
    } else {
        // EIP-160: EXP cost increase
        let gas_byte =
            U256::from(if spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) { 50 } else { 10 });
        let gas = gas_byte.checked_mul(U256::from(log2floor(power) / 8 + 1))?;
        u64::try_from(gas).ok()
    }
}

/// `LOG` opcode cost calculation.
#[inline]
pub const fn dyn_log_cost(len: u64) -> Option<u64> {
    LOGDATA.checked_mul(len)
}

/// `KECCAK256` opcode cost calculation.
#[inline]
pub const fn dyn_keccak256_cost(len: u64) -> Option<u64> {
    cost_per_word(len, KECCAK256WORD)
}

/// `*COPY` opcodes cost calculation.
#[inline]
pub const fn dyn_verylowcopy_cost(len: u64) -> Option<u64> {
    cost_per_word(len, COPY)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn exp_cost() {
        for (spec_id, power) in [
            (SpecId::CANCUN, U256::from(0)),
            (SpecId::CANCUN, U256::from(1)),
            (SpecId::CANCUN, U256::from(69)),
        ] {
            assert_eq!(
                super::exp_cost(spec_id, power).unwrap(),
                EXP + dyn_exp_cost(spec_id, power).unwrap(),
            );
        }
    }

    #[test]
    fn log_cost() {
        for n_topics in [0, 1, 2] {
            for len in [0, 1, 69] {
                assert_eq!(
                    super::log_cost(n_topics, len).unwrap(),
                    LOG + (LOGTOPIC * n_topics as u64) + dyn_log_cost(len).unwrap(),
                );
            }
        }
    }

    #[test]
    fn keccak256_cost() {
        for len in [0, 1, 69] {
            assert_eq!(
                super::keccak256_cost(len).unwrap(),
                KECCAK256 + dyn_keccak256_cost(len).unwrap(),
            );
        }
    }

    #[test]
    fn verylowcopy_cost() {
        for len in [0, 1, 69] {
            assert_eq!(
                super::verylowcopy_cost(len).unwrap(),
                VERYLOW + dyn_verylowcopy_cost(len).unwrap(),
            );
        }
    }
}