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};
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 eip7840::BlobParams::cancun().next_block_excess_blob_gas_osaka(
467 parent_excess_blob_gas,
468 parent_blob_gas_used,
469 0,
471 )
472}
473
474#[inline]
479pub const fn calc_blob_gasprice(excess_blob_gas: u64) -> u128 {
480 eip7840::BlobParams::cancun().calc_blob_fee(excess_blob_gas)
481}
482
483#[inline]
494pub const fn fake_exponential(factor: u128, numerator: u128, denominator: u128) -> u128 {
495 assert!(denominator != 0, "attempt to divide by zero");
496
497 let mut i = 1;
498 let mut output = 0;
499 let mut numerator_accum = factor * denominator;
500 while numerator_accum > 0 {
501 output += numerator_accum;
502
503 let Some(val) = numerator_accum.checked_mul(numerator) else {
505 break;
506 };
507
508 numerator_accum = val / (denominator * i);
510 i += 1;
511 }
512 output / denominator
513}
514
515#[cfg(test)]
516mod tests {
517 use super::*;
518
519 #[test]
521 fn test_calc_excess_blob_gas() {
522 for t @ &(excess, blobs, expected) in &[
523 (0, 0, 0),
526 (0, 1, 0),
527 (0, TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB, 0),
528 (0, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB),
531 (1, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB + 1),
532 (
533 1,
534 (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 2,
535 2 * DATA_GAS_PER_BLOB + 1,
536 ),
537 (
540 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
541 TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB,
542 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
543 ),
544 (
545 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
546 (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 1,
547 TARGET_DATA_GAS_PER_BLOCK_DENCUN - DATA_GAS_PER_BLOB,
548 ),
549 (
550 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
551 (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 2,
552 TARGET_DATA_GAS_PER_BLOCK_DENCUN - (2 * DATA_GAS_PER_BLOB),
553 ),
554 (DATA_GAS_PER_BLOB - 1, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 1, 0),
555 ] {
556 let actual = calc_excess_blob_gas(excess, blobs * DATA_GAS_PER_BLOB);
557 assert_eq!(actual, expected, "test: {t:?}");
558 }
559 }
560
561 #[test]
563 fn test_calc_blob_fee() {
564 let blob_fee_vectors = &[
565 (0, 1),
566 (2314057, 1),
567 (2314058, 2),
568 (10 * 1024 * 1024, 23),
569 (148099578, 18446739238971471609), (148099579, 18446744762204311910), (161087488, 902580055246494526580),
578 ];
579
580 for &(excess, expected) in blob_fee_vectors {
581 let actual = calc_blob_gasprice(excess);
582 assert_eq!(actual, expected, "test: {excess}");
583 }
584 }
585
586 #[test]
588 fn fake_exp() {
589 for t @ &(factor, numerator, denominator, expected) in &[
590 (1u64, 0u64, 1u64, 1u128),
591 (38493, 0, 1000, 38493),
592 (0, 1234, 2345, 0),
593 (1, 2, 1, 6), (1, 4, 2, 6),
595 (1, 3, 1, 16), (1, 6, 2, 18),
597 (1, 4, 1, 49), (1, 8, 2, 50),
599 (10, 8, 2, 542), (11, 8, 2, 596), (1, 5, 1, 136), (1, 5, 2, 11), (2, 5, 2, 23), (1, 50000000, 2225652, 5709098764),
605 (1, 380928, BLOB_GASPRICE_UPDATE_FRACTION.try_into().unwrap(), 1),
606 ] {
607 let actual = fake_exponential(factor as u128, numerator as u128, denominator as u128);
608 assert_eq!(actual, expected, "test: {t:?}");
609 }
610 }
611
612 #[test]
613 #[cfg(feature = "serde")]
614 fn serde_heap_blob() {
615 let blob = HeapBlob::repeat_byte(0x42);
616 let serialized = serde_json::to_string(&blob).unwrap();
617
618 let deserialized: HeapBlob = serde_json::from_str(&serialized).unwrap();
619 assert_eq!(blob, deserialized);
620 }
621
622 #[test]
623 fn fake_exp_handles_overflow() {
624 let factor = 1u128; let numerator = u64::MAX as u128; let denominator = 5007716u128; let result = fake_exponential(factor, numerator, denominator);
631
632 assert!(result > 0);
634
635 let prague_params = crate::eip7840::BlobParams::prague();
637 let _blob_fee = prague_params.calc_blob_fee(u64::MAX);
639 }
640}