1#[cfg(feature = "kzg")]
7pub use c_kzg;
8
9#[cfg(feature = "kzg")]
11pub mod env_settings;
12#[cfg(feature = "kzg")]
14pub mod trusted_setup_points;
15
16pub mod builder;
18pub mod utils;
19
20mod engine;
21pub use engine::*;
22
23#[cfg(feature = "kzg-sidecar")]
25mod sidecar;
26#[cfg(feature = "kzg-sidecar")]
27pub use sidecar::*;
28
29use alloy_primitives::{b256, Bytes, FixedBytes, B256, U256, U512};
30
31use crate::eip7840;
32
33pub const BLS_MODULUS_BYTES: B256 =
36 b256!("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001");
37
38pub const BLS_MODULUS: U256 = U256::from_be_bytes(BLS_MODULUS_BYTES.0);
41
42pub const FIELD_ELEMENT_BYTES: u64 = 32;
44
45pub const FIELD_ELEMENT_BYTES_USIZE: usize = FIELD_ELEMENT_BYTES as usize;
47
48pub const FIELD_ELEMENTS_PER_BLOB: u64 = 4096;
50
51pub const USABLE_BITS_PER_FIELD_ELEMENT: usize = 254;
53
54pub const USABLE_BYTES_PER_BLOB: usize =
58 USABLE_BITS_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB as usize / 8;
59
60pub const DATA_GAS_PER_BLOB: u64 = 131_072u64; pub const BYTES_PER_BLOB: usize = 131_072;
66
67pub const MAX_DATA_GAS_PER_BLOCK_DENCUN: u64 = 786_432u64; pub const TARGET_DATA_GAS_PER_BLOCK_DENCUN: u64 = 393_216u64; pub const MAX_BLOBS_PER_BLOCK_DENCUN: usize =
75 (MAX_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) as usize; 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;
85
86pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
88
89pub const BYTES_PER_COMMITMENT: usize = 48;
91
92pub const BYTES_PER_PROOF: usize = 48;
94
95pub type Blob = FixedBytes<BYTES_PER_BLOB>;
97
98#[cfg(feature = "serde")]
100pub fn deserialize_blob<'de, D>(deserializer: D) -> Result<alloc::boxed::Box<Blob>, D::Error>
101where
102 D: serde::de::Deserializer<'de>,
103{
104 use serde::Deserialize;
105 let raw_blob = <alloy_primitives::Bytes>::deserialize(deserializer)?;
106 let blob = alloc::boxed::Box::new(
107 Blob::try_from(raw_blob.as_ref()).map_err(serde::de::Error::custom)?,
108 );
109 Ok(blob)
110}
111
112#[cfg(all(debug_assertions, feature = "serde"))]
114pub fn deserialize_blobs<'de, D>(deserializer: D) -> Result<alloc::vec::Vec<Blob>, D::Error>
115where
116 D: serde::de::Deserializer<'de>,
117{
118 use alloc::vec::Vec;
119 use serde::Deserialize;
120
121 let raw_blobs = Vec::<alloy_primitives::Bytes>::deserialize(deserializer)?;
122 let mut blobs = Vec::with_capacity(raw_blobs.len());
123 for blob in raw_blobs {
124 blobs.push(Blob::try_from(blob.as_ref()).map_err(serde::de::Error::custom)?);
125 }
126 Ok(blobs)
127}
128
129#[cfg(all(not(debug_assertions), feature = "serde"))]
130#[inline(always)]
131pub fn deserialize_blobs<'de, D>(deserializer: D) -> Result<alloc::vec::Vec<Blob>, D::Error>
133where
134 D: serde::de::Deserializer<'de>,
135{
136 serde::Deserialize::deserialize(deserializer)
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, alloy_rlp::RlpEncodableWrapper)]
141pub struct HeapBlob(Bytes);
142
143impl HeapBlob {
144 pub fn new(blob: &[u8]) -> Result<Self, InvalidBlobLength> {
146 if blob.len() != BYTES_PER_BLOB {
147 return Err(InvalidBlobLength(blob.len()));
148 }
149
150 Ok(Self(Bytes::copy_from_slice(blob)))
151 }
152
153 pub fn from_array(blob: [u8; BYTES_PER_BLOB]) -> Self {
155 Self(Bytes::from(blob))
156 }
157
158 pub fn from_bytes(bytes: Bytes) -> Result<Self, InvalidBlobLength> {
160 if bytes.len() != BYTES_PER_BLOB {
161 return Err(InvalidBlobLength(bytes.len()));
162 }
163
164 Ok(Self(bytes))
165 }
166
167 pub fn repeat_byte(byte: u8) -> Self {
169 Self(Bytes::from(vec![byte; BYTES_PER_BLOB]))
170 }
171
172 pub const fn inner(&self) -> &Bytes {
174 &self.0
175 }
176}
177
178impl Default for HeapBlob {
179 fn default() -> Self {
180 Self::repeat_byte(0)
181 }
182}
183
184#[derive(Debug, Clone)]
186pub struct InvalidBlobLength(usize);
187impl core::fmt::Display for InvalidBlobLength {
188 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
189 write!(f, "Invalid blob length: {}, expected: {BYTES_PER_BLOB}", self.0)
190 }
191}
192impl core::error::Error for InvalidBlobLength {}
193
194#[cfg(feature = "serde")]
195impl serde::Serialize for HeapBlob {
196 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
197 where
198 S: serde::Serializer,
199 {
200 self.inner().serialize(serializer)
201 }
202}
203
204#[cfg(any(test, feature = "arbitrary"))]
205impl<'a> arbitrary::Arbitrary<'a> for HeapBlob {
206 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
207 let mut blob = vec![0u8; BYTES_PER_BLOB];
208 u.fill_buffer(&mut blob)?;
209 Ok(Self(Bytes::from(blob)))
210 }
211}
212
213#[cfg(feature = "serde")]
214impl<'de> serde::Deserialize<'de> for HeapBlob {
215 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
216 where
217 D: serde::de::Deserializer<'de>,
218 {
219 let inner = <Bytes>::deserialize(deserializer)?;
220
221 Self::from_bytes(inner).map_err(serde::de::Error::custom)
222 }
223}
224
225impl alloy_rlp::Decodable for HeapBlob {
226 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
227 let bytes = <Bytes>::decode(buf)?;
228
229 Self::from_bytes(bytes).map_err(|_| alloy_rlp::Error::Custom("invalid blob length"))
230 }
231}
232
233pub type Bytes48 = FixedBytes<48>;
235
236#[cfg(feature = "kzg")]
238pub trait AsCkzg: Sized {
239 type Ckzg;
241
242 fn as_ckzg(&self) -> &Self::Ckzg;
244
245 fn as_ckzg_mut(&mut self) -> &mut Self::Ckzg;
247
248 fn from_ckzg(value: Self::Ckzg) -> Self;
250
251 fn slice_as_ckzg(slice: &[Self]) -> &[Self::Ckzg];
253
254 fn slice_as_ckzg_mut(slice: &mut [Self]) -> &mut [Self::Ckzg];
256
257 fn vec_as_ckzg(vec: alloc::vec::Vec<Self>) -> alloc::vec::Vec<Self::Ckzg>;
259
260 fn vec_from_ckzg(vec: alloc::vec::Vec<Self::Ckzg>) -> alloc::vec::Vec<Self>;
262}
263
264#[cfg(feature = "kzg")]
266pub trait AsAlloy: Sized {
267 type Alloy;
269
270 fn as_alloy(&self) -> &Self::Alloy;
272
273 fn as_alloy_mut(&mut self) -> &mut Self::Alloy;
275
276 fn into_alloy(self) -> Self::Alloy;
278
279 fn slice_as_alloy(slice: &[Self]) -> &[Self::Alloy];
281
282 fn slice_as_alloy_mut(slice: &mut [Self]) -> &mut [Self::Alloy];
284
285 fn vec_as_alloy(vec: alloc::vec::Vec<Self>) -> alloc::vec::Vec<Self::Alloy>;
287
288 fn boxed_slice_as_alloy(boxed: alloc::boxed::Box<[Self]>) -> alloc::boxed::Box<[Self::Alloy]>;
290
291 fn vec_from_alloy(vec: alloc::vec::Vec<Self::Alloy>) -> alloc::vec::Vec<Self>;
293}
294
295#[cfg(feature = "kzg")]
296macro_rules! impl_ckzg_conversions {
297 ($alloy:ty, $ckzg:ty) => {
298 impl AsCkzg for $alloy {
299 type Ckzg = $ckzg;
300
301 #[inline]
302 fn as_ckzg(&self) -> &Self::Ckzg {
303 unsafe { core::mem::transmute(self) }
306 }
307
308 #[inline]
309 fn as_ckzg_mut(&mut self) -> &mut Self::Ckzg {
310 unsafe { core::mem::transmute(self) }
312 }
313
314 #[inline]
315 fn from_ckzg(value: Self::Ckzg) -> Self {
316 unsafe { core::mem::transmute(value) }
318 }
319
320 #[inline]
321 fn slice_as_ckzg(slice: &[Self]) -> &[Self::Ckzg] {
322 unsafe { core::mem::transmute(slice) }
324 }
325
326 #[inline]
327 fn slice_as_ckzg_mut(slice: &mut [Self]) -> &mut [Self::Ckzg] {
328 unsafe { core::mem::transmute(slice) }
330 }
331
332 #[inline]
333 fn vec_as_ckzg(vec: alloc::vec::Vec<Self>) -> alloc::vec::Vec<Self::Ckzg> {
334 unsafe { core::mem::transmute(vec) }
336 }
337
338 #[inline]
339 fn vec_from_ckzg(vec: alloc::vec::Vec<Self::Ckzg>) -> alloc::vec::Vec<Self> {
340 unsafe { core::mem::transmute(vec) }
342 }
343 }
344
345 impl_ckzg_conversions!(reverse $alloy, $ckzg);
346 };
347 (reverse $alloy:ty, $ckzg:ty) => {
348 impl AsAlloy for $ckzg {
349 type Alloy = $alloy;
350
351 #[inline]
352 fn as_alloy(&self) -> &Self::Alloy {
353 unsafe { core::mem::transmute(self) }
356 }
357
358 #[inline]
359 fn as_alloy_mut(&mut self) -> &mut Self::Alloy {
360 unsafe { core::mem::transmute(self) }
362 }
363
364 #[inline]
365 fn into_alloy(self) -> Self::Alloy {
366 unsafe { core::mem::transmute(self) }
368 }
369
370 #[inline]
371 fn slice_as_alloy(slice: &[Self]) -> &[Self::Alloy] {
372 unsafe { core::mem::transmute(slice) }
374 }
375
376 #[inline]
377 fn slice_as_alloy_mut(slice: &mut [Self]) -> &mut [Self::Alloy] {
378 unsafe { core::mem::transmute(slice) }
380 }
381
382 #[inline]
383 fn vec_as_alloy(vec: alloc::vec::Vec<Self>) -> alloc::vec::Vec<Self::Alloy> {
384 unsafe { core::mem::transmute(vec) }
386 }
387
388 #[inline]
389 fn boxed_slice_as_alloy(
390 boxed: alloc::boxed::Box<[Self]>,
391 ) -> alloc::boxed::Box<[Self::Alloy]> {
392 unsafe {
394 core::mem::transmute::<
395 alloc::boxed::Box<[Self]>,
396 alloc::boxed::Box<[Self::Alloy]>,
397 >(boxed)
398 }
399 }
400
401 #[inline]
402 fn vec_from_alloy(vec: alloc::vec::Vec<Self::Alloy>) -> alloc::vec::Vec<Self> {
403 unsafe { core::mem::transmute(vec) }
405 }
406 }
407 };
408}
409
410#[cfg(feature = "kzg")]
411impl_ckzg_conversions!(Blob, c_kzg::Blob);
412#[cfg(feature = "kzg")]
413impl_ckzg_conversions!(Bytes48, c_kzg::Bytes48);
414#[cfg(feature = "kzg")]
415impl_ckzg_conversions!(reverse Bytes48, c_kzg::KzgProof);
416#[cfg(feature = "kzg")]
417impl_ckzg_conversions!(reverse crate::eip7594::Cell, c_kzg::Cell);
418
419#[cfg(feature = "kzg")]
421#[deprecated(note = "use `Blob::slice_as_ckzg` via the `AsCkzg` trait instead")]
422#[inline]
423pub fn blobs_as_ckzg(blobs: &[Blob]) -> &[c_kzg::Blob] {
424 Blob::slice_as_ckzg(blobs)
425}
426
427#[cfg(feature = "kzg")]
429#[deprecated(note = "use `Bytes48::slice_as_ckzg` via the `AsCkzg` trait instead")]
430#[inline]
431pub fn bytes48_as_ckzg(bytes: &[Bytes48]) -> &[c_kzg::Bytes48] {
432 Bytes48::slice_as_ckzg(bytes)
433}
434
435#[cfg(feature = "kzg")]
437#[deprecated(note = "use `Bytes48::from_ckzg` via the `AsCkzg` trait instead")]
438#[inline]
439pub fn bytes48_from_ckzg(bytes: c_kzg::Bytes48) -> Bytes48 {
440 Bytes48::from_ckzg(bytes)
441}
442
443#[cfg(feature = "sha2")]
451pub fn kzg_to_versioned_hash(commitment: &[u8]) -> B256 {
452 use sha2::Digest;
453
454 debug_assert_eq!(commitment.len(), 48, "commitment length is not 48");
455 let mut res = sha2::Sha256::digest(commitment);
456 res[0] = VERSIONED_HASH_VERSION_KZG;
457 B256::new(res.into())
458}
459
460#[inline]
465pub const fn calc_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 {
466 let next_excess_blob_gas = parent_excess_blob_gas + parent_blob_gas_used;
467 next_excess_blob_gas.saturating_sub(TARGET_DATA_GAS_PER_BLOCK_DENCUN)
468}
469
470#[inline]
475pub fn calc_blob_gasprice(excess_blob_gas: u64) -> u128 {
476 eip7840::BlobParams::cancun().calc_blob_fee(excess_blob_gas)
477}
478
479#[inline]
490pub fn fake_exponential(factor: u128, numerator: u128, denominator: u128) -> u128 {
491 assert!(denominator != 0, "attempt to divide by zero");
492
493 let mut i = U512::from(1);
494 let denominator = U512::from(denominator);
495 let numerator = U512::from(numerator);
496 let mut output = U512::ZERO;
497 let mut numerator_accum = U512::from(factor) * denominator;
498
499 while numerator_accum > U512::ZERO {
500 let Some(next_output) = output.checked_add(numerator_accum) else {
501 return u128::MAX;
502 };
503 output = next_output;
504
505 let Some(next_accum) = numerator_accum.checked_mul(numerator) else {
507 return u128::MAX;
508 };
509 numerator_accum = next_accum / (denominator * i);
510
511 i += U512::from(1);
512 }
513
514 (output / denominator).try_into().unwrap_or(u128::MAX)
515}
516
517#[cfg(test)]
518mod tests {
519 use super::*;
520
521 #[test]
523 fn test_calc_excess_blob_gas() {
524 const ZERO_EXCESS: u64 = calc_excess_blob_gas(0, 0);
525 assert_eq!(ZERO_EXCESS, 0);
526
527 for t @ &(excess, blobs, expected) in &[
528 (0, 0, 0),
531 (0, 1, 0),
532 (0, TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB, 0),
533 (0, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB),
536 (1, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB + 1),
537 (
538 1,
539 (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 2,
540 2 * DATA_GAS_PER_BLOB + 1,
541 ),
542 (
545 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
546 TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB,
547 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
548 ),
549 (
550 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
551 (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 1,
552 TARGET_DATA_GAS_PER_BLOCK_DENCUN - DATA_GAS_PER_BLOB,
553 ),
554 (
555 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
556 (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 2,
557 TARGET_DATA_GAS_PER_BLOCK_DENCUN - (2 * DATA_GAS_PER_BLOB),
558 ),
559 (DATA_GAS_PER_BLOB - 1, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 1, 0),
560 ] {
561 let actual = calc_excess_blob_gas(excess, blobs * DATA_GAS_PER_BLOB);
562 assert_eq!(actual, expected, "test: {t:?}");
563
564 let osaka = eip7840::BlobParams::cancun().next_block_excess_blob_gas_osaka(
567 excess,
568 blobs * DATA_GAS_PER_BLOB,
569 0,
570 );
571 assert_eq!(actual, osaka, "osaka consistency: {t:?}");
572 }
573 }
574
575 #[test]
577 fn test_calc_blob_fee() {
578 let blob_fee_vectors = &[
579 (0, 1),
580 (2314057, 1),
581 (2314058, 2),
582 (10 * 1024 * 1024, 23),
583 (148099578, 18446739238971471609), (148099579, 18446744762204311910), (161087488, 902580055246494526580),
592 ];
593
594 for &(excess, expected) in blob_fee_vectors {
595 let actual = calc_blob_gasprice(excess);
596 assert_eq!(actual, expected, "test: {excess}");
597 }
598 }
599
600 #[test]
601 fn test_calc_blob_fee_pectra_large_input() {
602 let actual = crate::eip7691::calc_blob_gasprice(299453931);
603 assert_eq!(actual, 93359993185840258978230108);
604 }
605
606 #[test]
608 fn fake_exp() {
609 for t @ &(factor, numerator, denominator, expected) in &[
610 (1u64, 0u64, 1u64, 1u128),
611 (38493, 0, 1000, 38493),
612 (0, 1234, 2345, 0),
613 (1, 2, 1, 6), (1, 4, 2, 6),
615 (1, 3, 1, 16), (1, 6, 2, 18),
617 (1, 4, 1, 49), (1, 8, 2, 50),
619 (10, 8, 2, 542), (11, 8, 2, 596), (1, 5, 1, 136), (1, 5, 2, 11), (2, 5, 2, 23), (1, 50000000, 2225652, 5709098764),
625 (1, 380928, BLOB_GASPRICE_UPDATE_FRACTION.try_into().unwrap(), 1),
626 (0, 89, 1, 0),
627 (1, 299453931, 5007716, 93359993185840258978230108),
628 (1, 88, 1, 165162653699637111792770913913821835905),
629 ] {
630 let actual = fake_exponential(factor as u128, numerator as u128, denominator as u128);
631 assert_eq!(actual, expected, "test: {t:?}");
632 }
633 }
634
635 #[test]
636 #[cfg(feature = "serde")]
637 fn serde_heap_blob() {
638 let blob = HeapBlob::repeat_byte(0x42);
639 let serialized = serde_json::to_string(&blob).unwrap();
640
641 let deserialized: HeapBlob = serde_json::from_str(&serialized).unwrap();
642 assert_eq!(blob, deserialized);
643 }
644
645 #[test]
646 fn fake_exp_saturates_when_result_overflows_u128() {
647 assert_eq!(fake_exponential(100, 88, 1), u128::MAX);
648 assert_eq!(fake_exponential(1, u64::MAX as u128, 5007716), u128::MAX);
649 }
650
651 #[test]
652 fn fake_exp_no_spurious_saturation_for_large_factor() {
653 assert_eq!(
656 fake_exponential(u128::MAX - 1, 0, BLOB_GASPRICE_UPDATE_FRACTION),
657 u128::MAX - 1
658 );
659 }
660
661 #[test]
662 fn osaka_excess_blob_gas_handles_large_blob_fee_comparison() {
663 let params = crate::eip7840::BlobParams::osaka();
664 let excess_blob_gas = crate::eip7691::BLOB_GASPRICE_UPDATE_FRACTION_PECTRA as u64 * 88;
665
666 let actual = params.next_block_excess_blob_gas_osaka(excess_blob_gas, 0, 1);
667
668 assert_eq!(actual, excess_blob_gas - params.target_blob_gas_per_block());
669 }
670}