#[cfg(feature = "kzg")]
pub use c_kzg;
#[cfg(feature = "kzg")]
pub mod env_settings;
#[cfg(feature = "kzg")]
pub mod trusted_setup_points;
pub mod builder;
pub mod utils;
mod engine;
pub use engine::*;
#[cfg(feature = "kzg-sidecar")]
mod sidecar;
#[cfg(feature = "kzg-sidecar")]
pub use sidecar::*;
use alloy_primitives::{b256, Bytes, FixedBytes, B256, U256};
use crate::eip7840;
pub const BLS_MODULUS_BYTES: B256 =
b256!("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001");
pub const BLS_MODULUS: U256 = U256::from_be_bytes(BLS_MODULUS_BYTES.0);
pub const FIELD_ELEMENT_BYTES: u64 = 32;
pub const FIELD_ELEMENT_BYTES_USIZE: usize = FIELD_ELEMENT_BYTES as usize;
pub const FIELD_ELEMENTS_PER_BLOB: u64 = 4096;
pub const USABLE_BITS_PER_FIELD_ELEMENT: usize = 254;
pub const USABLE_BYTES_PER_BLOB: usize =
USABLE_BITS_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB as usize / 8;
pub const DATA_GAS_PER_BLOB: u64 = 131_072u64;
pub const BYTES_PER_BLOB: usize = 131_072;
pub 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 =
(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;
pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
pub const BYTES_PER_COMMITMENT: usize = 48;
pub const BYTES_PER_PROOF: usize = 48;
pub type Blob = FixedBytes<BYTES_PER_BLOB>;
#[cfg(feature = "serde")]
pub fn deserialize_blob<'de, D>(deserializer: D) -> Result<alloc::boxed::Box<Blob>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
use serde::Deserialize;
let raw_blob = <alloy_primitives::Bytes>::deserialize(deserializer)?;
let blob = alloc::boxed::Box::new(
Blob::try_from(raw_blob.as_ref()).map_err(serde::de::Error::custom)?,
);
Ok(blob)
}
#[cfg(all(debug_assertions, feature = "serde"))]
pub fn deserialize_blobs<'de, D>(deserializer: D) -> Result<alloc::vec::Vec<Blob>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
use alloc::vec::Vec;
use serde::Deserialize;
let raw_blobs = Vec::<alloy_primitives::Bytes>::deserialize(deserializer)?;
let mut blobs = Vec::with_capacity(raw_blobs.len());
for blob in raw_blobs {
blobs.push(Blob::try_from(blob.as_ref()).map_err(serde::de::Error::custom)?);
}
Ok(blobs)
}
#[cfg(all(not(debug_assertions), feature = "serde"))]
#[inline(always)]
pub fn deserialize_blobs<'de, D>(deserializer: D) -> Result<alloc::vec::Vec<Blob>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
serde::Deserialize::deserialize(deserializer)
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, alloy_rlp::RlpEncodableWrapper)]
pub struct HeapBlob(Bytes);
impl HeapBlob {
pub fn new(blob: &[u8]) -> Result<Self, InvalidBlobLength> {
if blob.len() != BYTES_PER_BLOB {
return Err(InvalidBlobLength(blob.len()));
}
Ok(Self(Bytes::copy_from_slice(blob)))
}
pub fn from_array(blob: [u8; BYTES_PER_BLOB]) -> Self {
Self(Bytes::from(blob))
}
pub fn from_bytes(bytes: Bytes) -> Result<Self, InvalidBlobLength> {
if bytes.len() != BYTES_PER_BLOB {
return Err(InvalidBlobLength(bytes.len()));
}
Ok(Self(bytes))
}
pub fn repeat_byte(byte: u8) -> Self {
Self(Bytes::from(vec![byte; BYTES_PER_BLOB]))
}
pub const fn inner(&self) -> &Bytes {
&self.0
}
}
impl Default for HeapBlob {
fn default() -> Self {
Self::repeat_byte(0)
}
}
#[derive(Debug, Clone)]
pub struct InvalidBlobLength(usize);
impl core::fmt::Display for InvalidBlobLength {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Invalid blob length: {}, expected: {BYTES_PER_BLOB}", self.0)
}
}
impl core::error::Error for InvalidBlobLength {}
#[cfg(feature = "serde")]
impl serde::Serialize for HeapBlob {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.inner().serialize(serializer)
}
}
#[cfg(any(test, feature = "arbitrary"))]
impl<'a> arbitrary::Arbitrary<'a> for HeapBlob {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let mut blob = vec![0u8; BYTES_PER_BLOB];
u.fill_buffer(&mut blob)?;
Ok(Self(Bytes::from(blob)))
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for HeapBlob {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let inner = <Bytes>::deserialize(deserializer)?;
Self::from_bytes(inner).map_err(serde::de::Error::custom)
}
}
impl alloy_rlp::Decodable for HeapBlob {
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
let bytes = <Bytes>::decode(buf)?;
Self::from_bytes(bytes).map_err(|_| alloy_rlp::Error::Custom("invalid blob length"))
}
}
pub type Bytes48 = FixedBytes<48>;
#[cfg(feature = "sha2")]
pub fn kzg_to_versioned_hash(commitment: &[u8]) -> B256 {
use sha2::Digest;
debug_assert_eq!(commitment.len(), 48, "commitment length is not 48");
let mut res = sha2::Sha256::digest(commitment);
res[0] = VERSIONED_HASH_VERSION_KZG;
B256::new(res.into())
}
#[inline]
pub const fn calc_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 {
eip7840::BlobParams::cancun().next_block_excess_blob_gas_osaka(
parent_excess_blob_gas,
parent_blob_gas_used,
0,
)
}
#[inline]
pub const fn calc_blob_gasprice(excess_blob_gas: u64) -> u128 {
eip7840::BlobParams::cancun().calc_blob_fee(excess_blob_gas)
}
#[inline]
pub const fn fake_exponential(factor: u128, numerator: u128, denominator: u128) -> u128 {
assert!(denominator != 0, "attempt to divide by zero");
let mut i = 1;
let mut output = 0;
let mut numerator_accum = factor * denominator;
while numerator_accum > 0 {
output += numerator_accum;
let Some(val) = numerator_accum.checked_mul(numerator) else {
break;
};
numerator_accum = val / (denominator * i);
i += 1;
}
output / denominator
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calc_excess_blob_gas() {
for t @ &(excess, blobs, expected) in &[
(0, 0, 0),
(0, 1, 0),
(0, TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB, 0),
(0, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB),
(1, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB + 1),
(
1,
(TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 2,
2 * DATA_GAS_PER_BLOB + 1,
),
(
TARGET_DATA_GAS_PER_BLOCK_DENCUN,
TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB,
TARGET_DATA_GAS_PER_BLOCK_DENCUN,
),
(
TARGET_DATA_GAS_PER_BLOCK_DENCUN,
(TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 1,
TARGET_DATA_GAS_PER_BLOCK_DENCUN - DATA_GAS_PER_BLOB,
),
(
TARGET_DATA_GAS_PER_BLOCK_DENCUN,
(TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 2,
TARGET_DATA_GAS_PER_BLOCK_DENCUN - (2 * DATA_GAS_PER_BLOB),
),
(DATA_GAS_PER_BLOB - 1, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 1, 0),
] {
let actual = calc_excess_blob_gas(excess, blobs * DATA_GAS_PER_BLOB);
assert_eq!(actual, expected, "test: {t:?}");
}
}
#[test]
fn test_calc_blob_fee() {
let blob_fee_vectors = &[
(0, 1),
(2314057, 1),
(2314058, 2),
(10 * 1024 * 1024, 23),
(148099578, 18446739238971471609), (148099579, 18446744762204311910), (161087488, 902580055246494526580),
];
for &(excess, expected) in blob_fee_vectors {
let actual = calc_blob_gasprice(excess);
assert_eq!(actual, expected, "test: {excess}");
}
}
#[test]
fn fake_exp() {
for t @ &(factor, numerator, denominator, expected) in &[
(1u64, 0u64, 1u64, 1u128),
(38493, 0, 1000, 38493),
(0, 1234, 2345, 0),
(1, 2, 1, 6), (1, 4, 2, 6),
(1, 3, 1, 16), (1, 6, 2, 18),
(1, 4, 1, 49), (1, 8, 2, 50),
(10, 8, 2, 542), (11, 8, 2, 596), (1, 5, 1, 136), (1, 5, 2, 11), (2, 5, 2, 23), (1, 50000000, 2225652, 5709098764),
(1, 380928, BLOB_GASPRICE_UPDATE_FRACTION.try_into().unwrap(), 1),
] {
let actual = fake_exponential(factor as u128, numerator as u128, denominator as u128);
assert_eq!(actual, expected, "test: {t:?}");
}
}
#[test]
#[cfg(feature = "serde")]
fn serde_heap_blob() {
let blob = HeapBlob::repeat_byte(0x42);
let serialized = serde_json::to_string(&blob).unwrap();
let deserialized: HeapBlob = serde_json::from_str(&serialized).unwrap();
assert_eq!(blob, deserialized);
}
#[test]
fn fake_exp_handles_overflow() {
let factor = 1u128; let numerator = u64::MAX as u128; let denominator = 5007716u128;
let result = fake_exponential(factor, numerator, denominator);
assert!(result > 0);
let prague_params = crate::eip7840::BlobParams::prague();
let _blob_fee = prague_params.calc_blob_fee(u64::MAX);
}
}