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//!
7//! [`BlobExcessGasAndPrice`] is used to store the blob gas price and excess blob gas.s
8use primitives::{
9    eip4844::{
10        BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE,
11        MIN_BLOB_GASPRICE,
12    },
13    hardfork::SpecId,
14};
15
16/// Structure holding block blob excess gas and it calculates blob fee
17///
18/// Incorporated as part of the Cancun upgrade via [EIP-4844].
19///
20/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
21#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub struct BlobExcessGasAndPrice {
24    /// The excess blob gas of the block
25    pub excess_blob_gas: u64,
26    /// The calculated blob gas price based on the `excess_blob_gas`
27    ///
28    /// See [calc_blob_gasprice]
29    pub blob_gasprice: u128,
30}
31
32impl BlobExcessGasAndPrice {
33    /// Creates a new instance by calculating the blob gas price with [`calc_blob_gasprice`].
34    ///
35    /// `excess_blob_gas` is the excess blob gas of the block, it can be calculated with `calc_excess_blob_gas` function from alloy-eips.
36    pub fn new(excess_blob_gas: u64, blob_base_fee_update_fraction: u64) -> Self {
37        let blob_gasprice = calc_blob_gasprice(excess_blob_gas, blob_base_fee_update_fraction);
38        Self {
39            excess_blob_gas,
40            blob_gasprice,
41        }
42    }
43
44    /// Creates a new instance by calculating the blob gas price based on the spec.
45    pub fn new_with_spec(excess_blob_gas: u64, spec: SpecId) -> Self {
46        Self::new(
47            excess_blob_gas,
48            if spec.is_enabled_in(SpecId::PRAGUE) {
49                BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE
50            } else {
51                BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN
52            },
53        )
54    }
55}
56
57/// Calculates the blob gas price from the header's excess blob gas field.
58///
59/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers)
60/// (`get_blob_gasprice`).
61#[inline]
62pub fn calc_blob_gasprice(excess_blob_gas: u64, blob_base_fee_update_fraction: u64) -> u128 {
63    fake_exponential(
64        MIN_BLOB_GASPRICE,
65        excess_blob_gas,
66        blob_base_fee_update_fraction,
67    )
68}
69
70/// Calculates the base fee per blob gas. Calls [`calc_blob_gasprice`] internally.
71/// Name of the function is aligned with EIP-4844 spec.
72pub fn get_base_fee_per_blob_gas(excess_blob_gas: u64, blob_base_fee_update_fraction: u64) -> u128 {
73    calc_blob_gasprice(excess_blob_gas, blob_base_fee_update_fraction)
74}
75
76/// Approximates `factor * e ** (numerator / denominator)` using Taylor expansion.
77///
78/// This is used to calculate the blob price.
79///
80/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers)
81/// (`fake_exponential`).
82#[inline]
83pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 {
84    assert_ne!(denominator, 0, "attempt to divide by zero");
85    let factor = factor as u128;
86    let numerator = numerator as u128;
87    let denominator = denominator as u128;
88
89    if denominator == 0 {
90        return 0;
91    }
92
93    let mut i = 1;
94    let mut output = 0;
95    let mut numerator_accum = factor * denominator;
96    while numerator_accum > 0 {
97        output += numerator_accum;
98
99        // Denominator is asserted as not zero at the start of the function.
100        numerator_accum = (numerator_accum * numerator) / (denominator * i);
101        i += 1;
102    }
103    output / denominator
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109    use primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN;
110
111    // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L78
112    #[test]
113    fn fake_exp() {
114        for t @ &(factor, numerator, denominator, expected) in &[
115            (1u64, 0u64, 1u64, 1u128),
116            (38493, 0, 1000, 38493),
117            (0, 1234, 2345, 0),
118            (1, 2, 1, 6), // approximate 7.389
119            (1, 4, 2, 6),
120            (1, 3, 1, 16), // approximate 20.09
121            (1, 6, 2, 18),
122            (1, 4, 1, 49), // approximate 54.60
123            (1, 8, 2, 50),
124            (10, 8, 2, 542), // approximate 540.598
125            (11, 8, 2, 596), // approximate 600.58
126            (1, 5, 1, 136),  // approximate 148.4
127            (1, 5, 2, 11),   // approximate 12.18
128            (2, 5, 2, 23),   // approximate 24.36
129            (1, 50000000, 2225652, 5709098764),
130            (1, 380928, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, 1),
131        ] {
132            let actual = fake_exponential(factor, numerator, denominator);
133            assert_eq!(actual, expected, "test: {t:?}");
134        }
135    }
136}