1#[cfg(feature = "kzg")]
7pub mod env_settings;
8#[cfg(feature = "kzg")]
10pub mod trusted_setup_points;
11
12pub mod builder;
14pub mod utils;
15
16mod engine;
17pub use engine::*;
18
19#[cfg(feature = "kzg-sidecar")]
21mod sidecar;
22#[cfg(feature = "kzg-sidecar")]
23pub use sidecar::*;
24
25use alloy_primitives::{b256, Bytes, FixedBytes, B256, U256};
26
27use crate::eip7840;
28
29pub const BLS_MODULUS_BYTES: B256 =
32    b256!("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001");
33
34pub const BLS_MODULUS: U256 = U256::from_be_bytes(BLS_MODULUS_BYTES.0);
37
38pub const FIELD_ELEMENT_BYTES: u64 = 32;
40
41pub const FIELD_ELEMENT_BYTES_USIZE: usize = FIELD_ELEMENT_BYTES as usize;
43
44pub const FIELD_ELEMENTS_PER_BLOB: u64 = 4096;
46
47pub const USABLE_BITS_PER_FIELD_ELEMENT: usize = 254;
49
50pub const USABLE_BYTES_PER_BLOB: usize =
54    USABLE_BITS_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB as usize / 8;
55
56pub const DATA_GAS_PER_BLOB: u64 = 131_072u64; pub const BYTES_PER_BLOB: usize = 131_072;
62
63#[deprecated(
65    since = "0.15.3",
66    note = "use hardfork specific MAX_DATA_GAS_PER_BLOCK_DENCUN constant or `BlobParams::max_blob_gas_per_block`"
67)]
68pub const MAX_DATA_GAS_PER_BLOCK: u64 = MAX_DATA_GAS_PER_BLOCK_DENCUN;
69
70pub const MAX_DATA_GAS_PER_BLOCK_DENCUN: u64 = 786_432u64; #[deprecated(
75    since = "0.15.3",
76    note = "use hardfork specific TARGET_DATA_GAS_PER_BLOCK_DENCUN constant or `BlobParams::target_blob_gas_per_block`"
77)]
78pub const TARGET_DATA_GAS_PER_BLOCK: u64 = TARGET_DATA_GAS_PER_BLOCK_DENCUN;
79
80pub const TARGET_DATA_GAS_PER_BLOCK_DENCUN: u64 = 393_216u64; #[deprecated(
85    since = "0.15.3",
86    note = "use hardfork specific MAX_BLOBS_PER_BLOCK_DENCUN constant or `BlobParams.max_blob_count`"
87)]
88pub const MAX_BLOBS_PER_BLOCK: usize = MAX_BLOBS_PER_BLOCK_DENCUN; pub const MAX_BLOBS_PER_BLOCK_DENCUN: usize =
92    (MAX_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) as usize; #[deprecated(
96    since = "0.15.3",
97    note = "use hardfork specific TARGET_BLOBS_PER_BLOCK_DENCUN constant or `BlobParams.target_blob_count`"
98)]
99pub const TARGET_BLOBS_PER_BLOCK: u64 = TARGET_BLOBS_PER_BLOCK_DENCUN; pub const TARGET_BLOBS_PER_BLOCK_DENCUN: u64 = TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB; pub const BLOB_GASPRICE_UPDATE_FRACTION: u128 = 3_338_477u128; pub const BLOB_TX_MIN_BLOB_GASPRICE: u128 = 1u128;
109
110pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
112
113pub const BYTES_PER_COMMITMENT: usize = 48;
115
116pub const BYTES_PER_PROOF: usize = 48;
118
119pub type Blob = FixedBytes<BYTES_PER_BLOB>;
121
122#[cfg(feature = "serde")]
124pub fn deserialize_blob<'de, D>(deserializer: D) -> Result<alloc::boxed::Box<Blob>, D::Error>
125where
126    D: serde::de::Deserializer<'de>,
127{
128    use serde::Deserialize;
129    let raw_blob = <alloy_primitives::Bytes>::deserialize(deserializer)?;
130    let blob = alloc::boxed::Box::new(
131        Blob::try_from(raw_blob.as_ref()).map_err(serde::de::Error::custom)?,
132    );
133    Ok(blob)
134}
135
136#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, alloy_rlp::RlpEncodableWrapper)]
138pub struct HeapBlob(Bytes);
139
140impl HeapBlob {
141    pub fn new(blob: &[u8]) -> Result<Self, InvalidBlobLength> {
143        if blob.len() != BYTES_PER_BLOB {
144            return Err(InvalidBlobLength(blob.len()));
145        }
146
147        Ok(Self(Bytes::copy_from_slice(blob)))
148    }
149
150    pub fn from_array(blob: [u8; BYTES_PER_BLOB]) -> Self {
152        Self(Bytes::from(blob))
153    }
154
155    pub fn from_bytes(bytes: Bytes) -> Result<Self, InvalidBlobLength> {
157        if bytes.len() != BYTES_PER_BLOB {
158            return Err(InvalidBlobLength(bytes.len()));
159        }
160
161        Ok(Self(bytes))
162    }
163
164    pub fn repeat_byte(byte: u8) -> Self {
166        Self(Bytes::from(vec![byte; BYTES_PER_BLOB]))
167    }
168
169    pub const fn inner(&self) -> &Bytes {
171        &self.0
172    }
173}
174
175impl Default for HeapBlob {
176    fn default() -> Self {
177        Self::repeat_byte(0)
178    }
179}
180
181#[derive(Debug, Clone)]
183pub struct InvalidBlobLength(usize);
184impl core::fmt::Display for InvalidBlobLength {
185    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
186        write!(f, "Invalid blob length: {}, expected: {BYTES_PER_BLOB}", self.0)
187    }
188}
189impl core::error::Error for InvalidBlobLength {}
190
191#[cfg(feature = "serde")]
192impl serde::Serialize for HeapBlob {
193    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
194    where
195        S: serde::Serializer,
196    {
197        self.inner().serialize(serializer)
198    }
199}
200
201#[cfg(any(test, feature = "arbitrary"))]
202impl<'a> arbitrary::Arbitrary<'a> for HeapBlob {
203    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
204        let mut blob = vec![0u8; BYTES_PER_BLOB];
205        u.fill_buffer(&mut blob)?;
206        Ok(Self(Bytes::from(blob)))
207    }
208}
209
210#[cfg(feature = "serde")]
211impl<'de> serde::Deserialize<'de> for HeapBlob {
212    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
213    where
214        D: serde::de::Deserializer<'de>,
215    {
216        let inner = <Bytes>::deserialize(deserializer)?;
217
218        Self::from_bytes(inner).map_err(serde::de::Error::custom)
219    }
220}
221
222impl alloy_rlp::Decodable for HeapBlob {
223    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
224        let bytes = <Bytes>::decode(buf)?;
225
226        Self::from_bytes(bytes).map_err(|_| alloy_rlp::Error::Custom("invalid blob length"))
227    }
228}
229
230pub type Bytes48 = FixedBytes<48>;
232
233#[cfg(feature = "sha2")]
241pub fn kzg_to_versioned_hash(commitment: &[u8]) -> B256 {
242    use sha2::Digest;
243
244    debug_assert_eq!(commitment.len(), 48, "commitment length is not 48");
245    let mut res = sha2::Sha256::digest(commitment);
246    res[0] = VERSIONED_HASH_VERSION_KZG;
247    B256::new(res.into())
248}
249
250#[inline]
255pub const fn calc_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 {
256    eip7840::BlobParams::cancun()
257        .next_block_excess_blob_gas(parent_excess_blob_gas, parent_blob_gas_used)
258}
259
260#[inline]
265pub const fn calc_blob_gasprice(excess_blob_gas: u64) -> u128 {
266    eip7840::BlobParams::cancun().calc_blob_fee(excess_blob_gas)
267}
268
269#[inline]
280pub const fn fake_exponential(factor: u128, numerator: u128, denominator: u128) -> u128 {
281    assert!(denominator != 0, "attempt to divide by zero");
282
283    let mut i = 1;
284    let mut output = 0;
285    let mut numerator_accum = factor * denominator;
286    while numerator_accum > 0 {
287        output += numerator_accum;
288
289        numerator_accum = (numerator_accum * numerator) / (denominator * i);
291        i += 1;
292    }
293    output / denominator
294}
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299
300    #[test]
302    fn test_calc_excess_blob_gas() {
303        for t @ &(excess, blobs, expected) in &[
304            (0, 0, 0),
307            (0, 1, 0),
308            (0, TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB, 0),
309            (0, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB),
312            (1, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB + 1),
313            (
314                1,
315                (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 2,
316                2 * DATA_GAS_PER_BLOB + 1,
317            ),
318            (
321                TARGET_DATA_GAS_PER_BLOCK_DENCUN,
322                TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB,
323                TARGET_DATA_GAS_PER_BLOCK_DENCUN,
324            ),
325            (
326                TARGET_DATA_GAS_PER_BLOCK_DENCUN,
327                (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 1,
328                TARGET_DATA_GAS_PER_BLOCK_DENCUN - DATA_GAS_PER_BLOB,
329            ),
330            (
331                TARGET_DATA_GAS_PER_BLOCK_DENCUN,
332                (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 2,
333                TARGET_DATA_GAS_PER_BLOCK_DENCUN - (2 * DATA_GAS_PER_BLOB),
334            ),
335            (DATA_GAS_PER_BLOB - 1, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 1, 0),
336        ] {
337            let actual = calc_excess_blob_gas(excess, blobs * DATA_GAS_PER_BLOB);
338            assert_eq!(actual, expected, "test: {t:?}");
339        }
340    }
341
342    #[test]
344    fn test_calc_blob_fee() {
345        let blob_fee_vectors = &[
346            (0, 1),
347            (2314057, 1),
348            (2314058, 2),
349            (10 * 1024 * 1024, 23),
350            (148099578, 18446739238971471609), (148099579, 18446744762204311910), (161087488, 902580055246494526580),
359        ];
360
361        for &(excess, expected) in blob_fee_vectors {
362            let actual = calc_blob_gasprice(excess);
363            assert_eq!(actual, expected, "test: {excess}");
364        }
365    }
366
367    #[test]
369    fn fake_exp() {
370        for t @ &(factor, numerator, denominator, expected) in &[
371            (1u64, 0u64, 1u64, 1u128),
372            (38493, 0, 1000, 38493),
373            (0, 1234, 2345, 0),
374            (1, 2, 1, 6), (1, 4, 2, 6),
376            (1, 3, 1, 16), (1, 6, 2, 18),
378            (1, 4, 1, 49), (1, 8, 2, 50),
380            (10, 8, 2, 542), (11, 8, 2, 596), (1, 5, 1, 136),  (1, 5, 2, 11),   (2, 5, 2, 23),   (1, 50000000, 2225652, 5709098764),
386            (1, 380928, BLOB_GASPRICE_UPDATE_FRACTION.try_into().unwrap(), 1),
387        ] {
388            let actual = fake_exponential(factor as u128, numerator as u128, denominator as u128);
389            assert_eq!(actual, expected, "test: {t:?}");
390        }
391    }
392
393    #[test]
394    #[cfg(feature = "serde")]
395    fn serde_heap_blob() {
396        let blob = HeapBlob::repeat_byte(0x42);
397        let serialized = serde_json::to_string(&blob).unwrap();
398
399        let deserialized: HeapBlob = serde_json::from_str(&serialized).unwrap();
400        assert_eq!(blob, deserialized);
401    }
402}