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().next_block_excess_blob_gas_osaka(
257 parent_excess_blob_gas,
258 parent_blob_gas_used,
259 0,
261 )
262}
263
264#[inline]
269pub const fn calc_blob_gasprice(excess_blob_gas: u64) -> u128 {
270 eip7840::BlobParams::cancun().calc_blob_fee(excess_blob_gas)
271}
272
273#[inline]
284pub const fn fake_exponential(factor: u128, numerator: u128, denominator: u128) -> u128 {
285 assert!(denominator != 0, "attempt to divide by zero");
286
287 let mut i = 1;
288 let mut output = 0;
289 let mut numerator_accum = factor * denominator;
290 while numerator_accum > 0 {
291 output += numerator_accum;
292
293 let Some(val) = numerator_accum.checked_mul(numerator) else {
295 break;
296 };
297
298 numerator_accum = val / (denominator * i);
300 i += 1;
301 }
302 output / denominator
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 #[test]
311 fn test_calc_excess_blob_gas() {
312 for t @ &(excess, blobs, expected) in &[
313 (0, 0, 0),
316 (0, 1, 0),
317 (0, TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB, 0),
318 (0, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB),
321 (1, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB + 1),
322 (
323 1,
324 (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 2,
325 2 * DATA_GAS_PER_BLOB + 1,
326 ),
327 (
330 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
331 TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB,
332 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
333 ),
334 (
335 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
336 (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 1,
337 TARGET_DATA_GAS_PER_BLOCK_DENCUN - DATA_GAS_PER_BLOB,
338 ),
339 (
340 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
341 (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 2,
342 TARGET_DATA_GAS_PER_BLOCK_DENCUN - (2 * DATA_GAS_PER_BLOB),
343 ),
344 (DATA_GAS_PER_BLOB - 1, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 1, 0),
345 ] {
346 let actual = calc_excess_blob_gas(excess, blobs * DATA_GAS_PER_BLOB);
347 assert_eq!(actual, expected, "test: {t:?}");
348 }
349 }
350
351 #[test]
353 fn test_calc_blob_fee() {
354 let blob_fee_vectors = &[
355 (0, 1),
356 (2314057, 1),
357 (2314058, 2),
358 (10 * 1024 * 1024, 23),
359 (148099578, 18446739238971471609), (148099579, 18446744762204311910), (161087488, 902580055246494526580),
368 ];
369
370 for &(excess, expected) in blob_fee_vectors {
371 let actual = calc_blob_gasprice(excess);
372 assert_eq!(actual, expected, "test: {excess}");
373 }
374 }
375
376 #[test]
378 fn fake_exp() {
379 for t @ &(factor, numerator, denominator, expected) in &[
380 (1u64, 0u64, 1u64, 1u128),
381 (38493, 0, 1000, 38493),
382 (0, 1234, 2345, 0),
383 (1, 2, 1, 6), (1, 4, 2, 6),
385 (1, 3, 1, 16), (1, 6, 2, 18),
387 (1, 4, 1, 49), (1, 8, 2, 50),
389 (10, 8, 2, 542), (11, 8, 2, 596), (1, 5, 1, 136), (1, 5, 2, 11), (2, 5, 2, 23), (1, 50000000, 2225652, 5709098764),
395 (1, 380928, BLOB_GASPRICE_UPDATE_FRACTION.try_into().unwrap(), 1),
396 ] {
397 let actual = fake_exponential(factor as u128, numerator as u128, denominator as u128);
398 assert_eq!(actual, expected, "test: {t:?}");
399 }
400 }
401
402 #[test]
403 #[cfg(feature = "serde")]
404 fn serde_heap_blob() {
405 let blob = HeapBlob::repeat_byte(0x42);
406 let serialized = serde_json::to_string(&blob).unwrap();
407
408 let deserialized: HeapBlob = serde_json::from_str(&serialized).unwrap();
409 assert_eq!(blob, deserialized);
410 }
411
412 #[test]
413 fn fake_exp_handles_overflow() {
414 let factor = 1u128; let numerator = u64::MAX as u128; let denominator = 5007716u128; let result = fake_exponential(factor, numerator, denominator);
421
422 assert!(result > 0);
424
425 let prague_params = crate::eip7840::BlobParams::prague();
427 let _blob_fee = prague_params.calc_blob_fee(u64::MAX);
429 }
430}