revm_context_interface/block/blob.rs
1//! Blob (EIP-4844) related functions and types. [`BlobExcessGasAndPrice`] is struct that helps with
2//! calculating blob gas price and excess blob gas.
3//!
4//! See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers).
5//!
6//! [`calc_blob_gasprice`] and [`calc_excess_blob_gas`] are used to calculate the blob gas price and
7//! excess blob gas.
8//!
9//! [`BlobExcessGasAndPrice`] is used to store the blob gas price and excess blob gas.s
10use primitives::eip4844::MIN_BLOB_GASPRICE;
11
12/// Structure holding block blob excess gas and it calculates blob fee
13///
14/// Incorporated as part of the Cancun upgrade via [EIP-4844].
15///
16/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
17#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19pub struct BlobExcessGasAndPrice {
20 /// The excess blob gas of the block
21 pub excess_blob_gas: u64,
22 /// The calculated blob gas price based on the `excess_blob_gas`
23 ///
24 /// See [calc_blob_gasprice]
25 pub blob_gasprice: u128,
26}
27
28impl BlobExcessGasAndPrice {
29 /// Creates a new instance by calculating the blob gas price with [`calc_blob_gasprice`].
30 pub fn new(excess_blob_gas: u64, blob_base_fee_update_fraction: u64) -> Self {
31 let blob_gasprice = calc_blob_gasprice(excess_blob_gas, blob_base_fee_update_fraction);
32 Self {
33 excess_blob_gas,
34 blob_gasprice,
35 }
36 }
37
38 /// Calculate this block excess gas and price from the parent excess gas and gas used
39 /// and the target blob gas per block.
40 ///
41 /// This fields will be used to calculate `excess_blob_gas` with [`calc_excess_blob_gas`] func.
42 pub fn from_parent_and_target(
43 parent_excess_blob_gas: u64,
44 parent_blob_gas_used: u64,
45 parent_target_blob_gas_per_block: u64,
46 blob_base_fee_update_fraction: u64,
47 ) -> Self {
48 Self::new(
49 calc_excess_blob_gas(
50 parent_excess_blob_gas,
51 parent_blob_gas_used,
52 parent_target_blob_gas_per_block,
53 ),
54 blob_base_fee_update_fraction,
55 )
56 }
57}
58
59/// Calculates the `excess_blob_gas` from the parent header's `blob_gas_used` and `excess_blob_gas`.
60///
61/// See also [the EIP-4844 helpers]<https://eips.ethereum.org/EIPS/eip-4844#helpers>
62/// (`calc_excess_blob_gas`).
63#[inline]
64pub fn calc_excess_blob_gas(
65 parent_excess_blob_gas: u64,
66 parent_blob_gas_used: u64,
67 parent_target_blob_gas_per_block: u64,
68) -> u64 {
69 (parent_excess_blob_gas + parent_blob_gas_used).saturating_sub(parent_target_blob_gas_per_block)
70}
71
72/// Calculates the blob gas price from the header's excess blob gas field.
73///
74/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers)
75/// (`get_blob_gasprice`).
76#[inline]
77pub fn calc_blob_gasprice(excess_blob_gas: u64, blob_base_fee_update_fraction: u64) -> u128 {
78 fake_exponential(
79 MIN_BLOB_GASPRICE,
80 excess_blob_gas,
81 blob_base_fee_update_fraction,
82 )
83}
84
85/// Approximates `factor * e ** (numerator / denominator)` using Taylor expansion.
86///
87/// This is used to calculate the blob price.
88///
89/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers)
90/// (`fake_exponential`).
91///
92/// # Panics
93///
94/// This function panics if `denominator` is zero.
95#[inline]
96pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 {
97 assert_ne!(denominator, 0, "attempt to divide by zero");
98 let factor = factor as u128;
99 let numerator = numerator as u128;
100 let denominator = denominator as u128;
101
102 let mut i = 1;
103 let mut output = 0;
104 let mut numerator_accum = factor * denominator;
105 while numerator_accum > 0 {
106 output += numerator_accum;
107
108 // Denominator is asserted as not zero at the start of the function.
109 numerator_accum = (numerator_accum * numerator) / (denominator * i);
110 i += 1;
111 }
112 output / denominator
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use primitives::eip4844::{
119 self, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, GAS_PER_BLOB,
120 TARGET_BLOB_GAS_PER_BLOCK_CANCUN as TARGET_BLOB_GAS_PER_BLOCK,
121 };
122
123 // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L27
124 #[test]
125 fn test_calc_excess_blob_gas() {
126 for t @ &(excess, blobs, expected) in &[
127 // The excess blob gas should not increase from zero if the used blob
128 // slots are below - or equal - to the target.
129 (0, 0, 0),
130 (0, 1, 0),
131 (0, TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB, 0),
132 // If the target blob gas is exceeded, the excessBlobGas should increase
133 // by however much it was overshot
134 (
135 0,
136 (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 1,
137 GAS_PER_BLOB,
138 ),
139 (
140 1,
141 (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 1,
142 GAS_PER_BLOB + 1,
143 ),
144 (
145 1,
146 (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 2,
147 2 * GAS_PER_BLOB + 1,
148 ),
149 // The excess blob gas should decrease by however much the target was
150 // under-shot, capped at zero.
151 (
152 TARGET_BLOB_GAS_PER_BLOCK,
153 TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB,
154 TARGET_BLOB_GAS_PER_BLOCK,
155 ),
156 (
157 TARGET_BLOB_GAS_PER_BLOCK,
158 (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 1,
159 TARGET_BLOB_GAS_PER_BLOCK - GAS_PER_BLOB,
160 ),
161 (
162 TARGET_BLOB_GAS_PER_BLOCK,
163 (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 2,
164 TARGET_BLOB_GAS_PER_BLOCK - (2 * GAS_PER_BLOB),
165 ),
166 (
167 GAS_PER_BLOB - 1,
168 (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 1,
169 0,
170 ),
171 ] {
172 let actual = calc_excess_blob_gas(
173 excess,
174 blobs * GAS_PER_BLOB,
175 eip4844::TARGET_BLOB_GAS_PER_BLOCK_CANCUN,
176 );
177 assert_eq!(actual, expected, "test: {t:?}");
178 }
179 }
180
181 // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L60
182 #[test]
183 fn test_calc_blob_fee_cancun() {
184 let blob_fee_vectors = &[
185 (0, 1),
186 (2314057, 1),
187 (2314058, 2),
188 (10 * 1024 * 1024, 23),
189 // `calc_blob_gasprice` approximates `e ** (excess_blob_gas / BLOB_BASE_FEE_UPDATE_FRACTION)` using Taylor expansion
190 //
191 // to roughly find where boundaries will be hit:
192 // 2 ** bits = e ** (excess_blob_gas / BLOB_BASE_FEE_UPDATE_FRACTION)
193 // excess_blob_gas = ln(2 ** bits) * BLOB_BASE_FEE_UPDATE_FRACTION
194 (148099578, 18446739238971471609), // output is just below the overflow
195 (148099579, 18446744762204311910), // output is just after the overflow
196 (161087488, 902580055246494526580),
197 ];
198
199 for &(excess, expected) in blob_fee_vectors {
200 let actual = calc_blob_gasprice(excess, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN);
201 assert_eq!(actual, expected, "test: {excess}");
202 }
203 }
204
205 // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L78
206 #[test]
207 fn fake_exp() {
208 for t @ &(factor, numerator, denominator, expected) in &[
209 (1u64, 0u64, 1u64, 1u128),
210 (38493, 0, 1000, 38493),
211 (0, 1234, 2345, 0),
212 (1, 2, 1, 6), // approximate 7.389
213 (1, 4, 2, 6),
214 (1, 3, 1, 16), // approximate 20.09
215 (1, 6, 2, 18),
216 (1, 4, 1, 49), // approximate 54.60
217 (1, 8, 2, 50),
218 (10, 8, 2, 542), // approximate 540.598
219 (11, 8, 2, 596), // approximate 600.58
220 (1, 5, 1, 136), // approximate 148.4
221 (1, 5, 2, 11), // approximate 12.18
222 (2, 5, 2, 23), // approximate 24.36
223 (1, 50000000, 2225652, 5709098764),
224 (1, 380928, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, 1),
225 ] {
226 let actual = fake_exponential(factor, numerator, denominator);
227 assert_eq!(actual, expected, "test: {t:?}");
228 }
229 }
230}