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}