revm_primitives/
utilities.rs

1use crate::{
2    b256, B256, BLOB_GASPRICE_UPDATE_FRACTION, MIN_BLOB_GASPRICE, TARGET_BLOB_GAS_PER_BLOCK,
3};
4pub use alloy_primitives::keccak256;
5
6/// The Keccak-256 hash of the empty string `""`.
7pub const KECCAK_EMPTY: B256 =
8    b256!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
9
10/// Calculates the `excess_blob_gas` from the parent header's `blob_gas_used` and `excess_blob_gas`.
11///
12/// See also [the EIP-4844 helpers]<https://eips.ethereum.org/EIPS/eip-4844#helpers>
13/// (`calc_excess_blob_gas`).
14#[inline]
15pub fn calc_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 {
16    (parent_excess_blob_gas + parent_blob_gas_used).saturating_sub(TARGET_BLOB_GAS_PER_BLOCK)
17}
18
19/// Calculates the blob gas price from the header's excess blob gas field.
20///
21/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers)
22/// (`get_blob_gasprice`).
23#[inline]
24pub fn calc_blob_gasprice(excess_blob_gas: u64) -> u128 {
25    fake_exponential(
26        MIN_BLOB_GASPRICE,
27        excess_blob_gas,
28        BLOB_GASPRICE_UPDATE_FRACTION,
29    )
30}
31
32/// Approximates `factor * e ** (numerator / denominator)` using Taylor expansion.
33///
34/// This is used to calculate the blob price.
35///
36/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers)
37/// (`fake_exponential`).
38///
39/// # Panics
40///
41/// This function panics if `denominator` is zero.
42#[inline]
43pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 {
44    assert_ne!(denominator, 0, "attempt to divide by zero");
45    let factor = factor as u128;
46    let numerator = numerator as u128;
47    let denominator = denominator as u128;
48
49    let mut i = 1;
50    let mut output = 0;
51    let mut numerator_accum = factor * denominator;
52    while numerator_accum > 0 {
53        output += numerator_accum;
54
55        // Denominator is asserted as not zero at the start of the function.
56        numerator_accum = (numerator_accum * numerator) / (denominator * i);
57        i += 1;
58    }
59    output / denominator
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65    use crate::GAS_PER_BLOB;
66
67    // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L27
68    #[test]
69    fn test_calc_excess_blob_gas() {
70        for t @ &(excess, blobs, expected) in &[
71            // The excess blob gas should not increase from zero if the used blob
72            // slots are below - or equal - to the target.
73            (0, 0, 0),
74            (0, 1, 0),
75            (0, TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB, 0),
76            // If the target blob gas is exceeded, the excessBlobGas should increase
77            // by however much it was overshot
78            (
79                0,
80                (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 1,
81                GAS_PER_BLOB,
82            ),
83            (
84                1,
85                (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 1,
86                GAS_PER_BLOB + 1,
87            ),
88            (
89                1,
90                (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 2,
91                2 * GAS_PER_BLOB + 1,
92            ),
93            // The excess blob gas should decrease by however much the target was
94            // under-shot, capped at zero.
95            (
96                TARGET_BLOB_GAS_PER_BLOCK,
97                TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB,
98                TARGET_BLOB_GAS_PER_BLOCK,
99            ),
100            (
101                TARGET_BLOB_GAS_PER_BLOCK,
102                (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 1,
103                TARGET_BLOB_GAS_PER_BLOCK - GAS_PER_BLOB,
104            ),
105            (
106                TARGET_BLOB_GAS_PER_BLOCK,
107                (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 2,
108                TARGET_BLOB_GAS_PER_BLOCK - (2 * GAS_PER_BLOB),
109            ),
110            (
111                GAS_PER_BLOB - 1,
112                (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 1,
113                0,
114            ),
115        ] {
116            let actual = calc_excess_blob_gas(excess, blobs * GAS_PER_BLOB);
117            assert_eq!(actual, expected, "test: {t:?}");
118        }
119    }
120
121    // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L60
122    #[test]
123    fn test_calc_blob_fee() {
124        let blob_fee_vectors = &[
125            (0, 1),
126            (2314057, 1),
127            (2314058, 2),
128            (10 * 1024 * 1024, 23),
129            // calc_blob_gasprice approximates `e ** (excess_blob_gas / BLOB_GASPRICE_UPDATE_FRACTION)` using Taylor expansion
130            //
131            // to roughly find where boundaries will be hit:
132            // 2 ** bits = e ** (excess_blob_gas / BLOB_GASPRICE_UPDATE_FRACTION)
133            // excess_blob_gas = ln(2 ** bits) * BLOB_GASPRICE_UPDATE_FRACTION
134            (148099578, 18446739238971471609), // output is just below the overflow
135            (148099579, 18446744762204311910), // output is just after the overflow
136            (161087488, 902580055246494526580),
137        ];
138
139        for &(excess, expected) in blob_fee_vectors {
140            let actual = calc_blob_gasprice(excess);
141            assert_eq!(actual, expected, "test: {excess}");
142        }
143    }
144
145    // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L78
146    #[test]
147    fn fake_exp() {
148        for t @ &(factor, numerator, denominator, expected) in &[
149            (1u64, 0u64, 1u64, 1u128),
150            (38493, 0, 1000, 38493),
151            (0, 1234, 2345, 0),
152            (1, 2, 1, 6), // approximate 7.389
153            (1, 4, 2, 6),
154            (1, 3, 1, 16), // approximate 20.09
155            (1, 6, 2, 18),
156            (1, 4, 1, 49), // approximate 54.60
157            (1, 8, 2, 50),
158            (10, 8, 2, 542), // approximate 540.598
159            (11, 8, 2, 596), // approximate 600.58
160            (1, 5, 1, 136),  // approximate 148.4
161            (1, 5, 2, 11),   // approximate 12.18
162            (2, 5, 2, 23),   // approximate 24.36
163            (1, 50000000, 2225652, 5709098764),
164            (1, 380928, BLOB_GASPRICE_UPDATE_FRACTION, 1),
165        ] {
166            let actual = fake_exponential(factor, numerator, denominator);
167            assert_eq!(actual, expected, "test: {t:?}");
168        }
169    }
170}