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
63pub const MAX_DATA_GAS_PER_BLOCK: u64 = 786_432u64; pub const TARGET_DATA_GAS_PER_BLOCK: u64 = 393_216u64; pub const MAX_BLOBS_PER_BLOCK: usize = (MAX_DATA_GAS_PER_BLOCK / DATA_GAS_PER_BLOB) as usize; pub const TARGET_BLOBS_PER_BLOCK: u64 = TARGET_DATA_GAS_PER_BLOCK / DATA_GAS_PER_BLOB; pub const BLOB_GASPRICE_UPDATE_FRACTION: u128 = 3_338_477u128; pub const BLOB_TX_MIN_BLOB_GASPRICE: u128 = 1u128;
80
81pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
83
84pub const BYTES_PER_COMMITMENT: usize = 48;
86
87pub const BYTES_PER_PROOF: usize = 48;
89
90pub type Blob = FixedBytes<BYTES_PER_BLOB>;
92
93#[cfg(feature = "serde")]
95pub fn deserialize_blob<'de, D>(deserializer: D) -> Result<alloc::boxed::Box<Blob>, D::Error>
96where
97 D: serde::de::Deserializer<'de>,
98{
99 use serde::Deserialize;
100 let raw_blob = <alloy_primitives::Bytes>::deserialize(deserializer)?;
101 let blob = alloc::boxed::Box::new(
102 Blob::try_from(raw_blob.as_ref()).map_err(serde::de::Error::custom)?,
103 );
104 Ok(blob)
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, alloy_rlp::RlpEncodableWrapper)]
109pub struct HeapBlob(Bytes);
110
111impl HeapBlob {
112 pub fn new(blob: &[u8]) -> Result<Self, InvalidBlobLength> {
114 if blob.len() != BYTES_PER_BLOB {
115 return Err(InvalidBlobLength(blob.len()));
116 }
117
118 Ok(Self(Bytes::copy_from_slice(blob)))
119 }
120
121 pub fn from_array(blob: [u8; BYTES_PER_BLOB]) -> Self {
123 Self(Bytes::from(blob))
124 }
125
126 pub fn from_bytes(bytes: Bytes) -> Result<Self, InvalidBlobLength> {
128 if bytes.len() != BYTES_PER_BLOB {
129 return Err(InvalidBlobLength(bytes.len()));
130 }
131
132 Ok(Self(bytes))
133 }
134
135 pub fn repeat_byte(byte: u8) -> Self {
137 Self(Bytes::from(vec![byte; BYTES_PER_BLOB]))
138 }
139
140 pub fn inner(&self) -> &Bytes {
142 &self.0
143 }
144}
145
146impl Default for HeapBlob {
147 fn default() -> Self {
148 Self::repeat_byte(0)
149 }
150}
151
152#[derive(Debug, Clone)]
154pub struct InvalidBlobLength(usize);
155impl core::fmt::Display for InvalidBlobLength {
156 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
157 write!(f, "Invalid blob length: {}, expected: {BYTES_PER_BLOB}", self.0)
158 }
159}
160impl core::error::Error for InvalidBlobLength {}
161
162#[cfg(feature = "serde")]
163impl serde::Serialize for HeapBlob {
164 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
165 where
166 S: serde::Serializer,
167 {
168 self.inner().serialize(serializer)
169 }
170}
171
172#[cfg(any(test, feature = "arbitrary"))]
173impl<'a> arbitrary::Arbitrary<'a> for HeapBlob {
174 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
175 let mut blob = vec![0u8; BYTES_PER_BLOB];
176 u.fill_buffer(&mut blob)?;
177 Ok(Self(Bytes::from(blob)))
178 }
179}
180
181#[cfg(feature = "serde")]
182impl<'de> serde::Deserialize<'de> for HeapBlob {
183 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
184 where
185 D: serde::de::Deserializer<'de>,
186 {
187 let inner = <Bytes>::deserialize(deserializer)?;
188
189 Self::from_bytes(inner).map_err(serde::de::Error::custom)
190 }
191}
192
193impl alloy_rlp::Decodable for HeapBlob {
194 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
195 let bytes = <Bytes>::decode(buf)?;
196
197 Self::from_bytes(bytes).map_err(|_| alloy_rlp::Error::Custom("invalid blob length"))
198 }
199}
200
201pub type Bytes48 = FixedBytes<48>;
203
204#[cfg(feature = "sha2")]
212pub fn kzg_to_versioned_hash(commitment: &[u8]) -> B256 {
213 use sha2::Digest;
214
215 debug_assert_eq!(commitment.len(), 48, "commitment length is not 48");
216 let mut res = sha2::Sha256::digest(commitment);
217 res[0] = VERSIONED_HASH_VERSION_KZG;
218 B256::new(res.into())
219}
220
221#[inline]
226pub const fn calc_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 {
227 eip7840::BlobParams::cancun()
228 .next_block_excess_blob_gas(parent_excess_blob_gas, parent_blob_gas_used)
229}
230
231#[inline]
236pub const fn calc_blob_gasprice(excess_blob_gas: u64) -> u128 {
237 eip7840::BlobParams::cancun().calc_blob_fee(excess_blob_gas)
238}
239
240#[inline]
251pub const fn fake_exponential(factor: u128, numerator: u128, denominator: u128) -> u128 {
252 assert!(denominator != 0, "attempt to divide by zero");
253
254 let mut i = 1;
255 let mut output = 0;
256 let mut numerator_accum = factor * denominator;
257 while numerator_accum > 0 {
258 output += numerator_accum;
259
260 numerator_accum = (numerator_accum * numerator) / (denominator * i);
262 i += 1;
263 }
264 output / denominator
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 #[test]
273 fn test_calc_excess_blob_gas() {
274 for t @ &(excess, blobs, expected) in &[
275 (0, 0, 0),
278 (0, 1, 0),
279 (0, TARGET_DATA_GAS_PER_BLOCK / DATA_GAS_PER_BLOB, 0),
280 (0, (TARGET_DATA_GAS_PER_BLOCK / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB),
283 (1, (TARGET_DATA_GAS_PER_BLOCK / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB + 1),
284 (1, (TARGET_DATA_GAS_PER_BLOCK / DATA_GAS_PER_BLOB) + 2, 2 * DATA_GAS_PER_BLOB + 1),
285 (
288 TARGET_DATA_GAS_PER_BLOCK,
289 TARGET_DATA_GAS_PER_BLOCK / DATA_GAS_PER_BLOB,
290 TARGET_DATA_GAS_PER_BLOCK,
291 ),
292 (
293 TARGET_DATA_GAS_PER_BLOCK,
294 (TARGET_DATA_GAS_PER_BLOCK / DATA_GAS_PER_BLOB) - 1,
295 TARGET_DATA_GAS_PER_BLOCK - DATA_GAS_PER_BLOB,
296 ),
297 (
298 TARGET_DATA_GAS_PER_BLOCK,
299 (TARGET_DATA_GAS_PER_BLOCK / DATA_GAS_PER_BLOB) - 2,
300 TARGET_DATA_GAS_PER_BLOCK - (2 * DATA_GAS_PER_BLOB),
301 ),
302 (DATA_GAS_PER_BLOB - 1, (TARGET_DATA_GAS_PER_BLOCK / DATA_GAS_PER_BLOB) - 1, 0),
303 ] {
304 let actual = calc_excess_blob_gas(excess, blobs * DATA_GAS_PER_BLOB);
305 assert_eq!(actual, expected, "test: {t:?}");
306 }
307 }
308
309 #[test]
311 fn test_calc_blob_fee() {
312 let blob_fee_vectors = &[
313 (0, 1),
314 (2314057, 1),
315 (2314058, 2),
316 (10 * 1024 * 1024, 23),
317 (148099578, 18446739238971471609), (148099579, 18446744762204311910), (161087488, 902580055246494526580),
326 ];
327
328 for &(excess, expected) in blob_fee_vectors {
329 let actual = calc_blob_gasprice(excess);
330 assert_eq!(actual, expected, "test: {excess}");
331 }
332 }
333
334 #[test]
336 fn fake_exp() {
337 for t @ &(factor, numerator, denominator, expected) in &[
338 (1u64, 0u64, 1u64, 1u128),
339 (38493, 0, 1000, 38493),
340 (0, 1234, 2345, 0),
341 (1, 2, 1, 6), (1, 4, 2, 6),
343 (1, 3, 1, 16), (1, 6, 2, 18),
345 (1, 4, 1, 49), (1, 8, 2, 50),
347 (10, 8, 2, 542), (11, 8, 2, 596), (1, 5, 1, 136), (1, 5, 2, 11), (2, 5, 2, 23), (1, 50000000, 2225652, 5709098764),
353 (1, 380928, BLOB_GASPRICE_UPDATE_FRACTION.try_into().unwrap(), 1),
354 ] {
355 let actual = fake_exponential(factor as u128, numerator as u128, denominator as u128);
356 assert_eq!(actual, expected, "test: {t:?}");
357 }
358 }
359
360 #[test]
361 #[cfg(feature = "serde")]
362 fn serde_heap_blob() {
363 let blob = HeapBlob::repeat_byte(0x42);
364 let serialized = serde_json::to_string(&blob).unwrap();
365
366 let deserialized: HeapBlob = serde_json::from_str(&serialized).unwrap();
367 assert_eq!(blob, deserialized);
368 }
369}