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#[cfg(all(debug_assertions, feature = "serde"))]
138pub fn deserialize_blobs<'de, D>(deserializer: D) -> Result<alloc::vec::Vec<Blob>, D::Error>
139where
140 D: serde::de::Deserializer<'de>,
141{
142 use alloc::vec::Vec;
143 use serde::Deserialize;
144
145 let raw_blobs = Vec::<alloy_primitives::Bytes>::deserialize(deserializer)?;
146 let mut blobs = Vec::with_capacity(raw_blobs.len());
147 for blob in raw_blobs {
148 blobs.push(Blob::try_from(blob.as_ref()).map_err(serde::de::Error::custom)?);
149 }
150 Ok(blobs)
151}
152
153#[cfg(all(not(debug_assertions), feature = "serde"))]
154#[inline(always)]
155pub fn deserialize_blobs<'de, D>(deserializer: D) -> Result<alloc::vec::Vec<Blob>, D::Error>
156where
157 D: serde::de::Deserializer<'de>,
158{
159 serde::Deserialize::deserialize(deserializer)
160}
161
162#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, alloy_rlp::RlpEncodableWrapper)]
164pub struct HeapBlob(Bytes);
165
166impl HeapBlob {
167 pub fn new(blob: &[u8]) -> Result<Self, InvalidBlobLength> {
169 if blob.len() != BYTES_PER_BLOB {
170 return Err(InvalidBlobLength(blob.len()));
171 }
172
173 Ok(Self(Bytes::copy_from_slice(blob)))
174 }
175
176 pub fn from_array(blob: [u8; BYTES_PER_BLOB]) -> Self {
178 Self(Bytes::from(blob))
179 }
180
181 pub fn from_bytes(bytes: Bytes) -> Result<Self, InvalidBlobLength> {
183 if bytes.len() != BYTES_PER_BLOB {
184 return Err(InvalidBlobLength(bytes.len()));
185 }
186
187 Ok(Self(bytes))
188 }
189
190 pub fn repeat_byte(byte: u8) -> Self {
192 Self(Bytes::from(vec![byte; BYTES_PER_BLOB]))
193 }
194
195 pub const fn inner(&self) -> &Bytes {
197 &self.0
198 }
199}
200
201impl Default for HeapBlob {
202 fn default() -> Self {
203 Self::repeat_byte(0)
204 }
205}
206
207#[derive(Debug, Clone)]
209pub struct InvalidBlobLength(usize);
210impl core::fmt::Display for InvalidBlobLength {
211 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
212 write!(f, "Invalid blob length: {}, expected: {BYTES_PER_BLOB}", self.0)
213 }
214}
215impl core::error::Error for InvalidBlobLength {}
216
217#[cfg(feature = "serde")]
218impl serde::Serialize for HeapBlob {
219 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
220 where
221 S: serde::Serializer,
222 {
223 self.inner().serialize(serializer)
224 }
225}
226
227#[cfg(any(test, feature = "arbitrary"))]
228impl<'a> arbitrary::Arbitrary<'a> for HeapBlob {
229 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
230 let mut blob = vec![0u8; BYTES_PER_BLOB];
231 u.fill_buffer(&mut blob)?;
232 Ok(Self(Bytes::from(blob)))
233 }
234}
235
236#[cfg(feature = "serde")]
237impl<'de> serde::Deserialize<'de> for HeapBlob {
238 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
239 where
240 D: serde::de::Deserializer<'de>,
241 {
242 let inner = <Bytes>::deserialize(deserializer)?;
243
244 Self::from_bytes(inner).map_err(serde::de::Error::custom)
245 }
246}
247
248impl alloy_rlp::Decodable for HeapBlob {
249 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
250 let bytes = <Bytes>::decode(buf)?;
251
252 Self::from_bytes(bytes).map_err(|_| alloy_rlp::Error::Custom("invalid blob length"))
253 }
254}
255
256pub type Bytes48 = FixedBytes<48>;
258
259#[cfg(feature = "sha2")]
267pub fn kzg_to_versioned_hash(commitment: &[u8]) -> B256 {
268 use sha2::Digest;
269
270 debug_assert_eq!(commitment.len(), 48, "commitment length is not 48");
271 let mut res = sha2::Sha256::digest(commitment);
272 res[0] = VERSIONED_HASH_VERSION_KZG;
273 B256::new(res.into())
274}
275
276#[inline]
281pub const fn calc_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 {
282 eip7840::BlobParams::cancun().next_block_excess_blob_gas_osaka(
283 parent_excess_blob_gas,
284 parent_blob_gas_used,
285 0,
287 )
288}
289
290#[inline]
295pub const fn calc_blob_gasprice(excess_blob_gas: u64) -> u128 {
296 eip7840::BlobParams::cancun().calc_blob_fee(excess_blob_gas)
297}
298
299#[inline]
310pub const fn fake_exponential(factor: u128, numerator: u128, denominator: u128) -> u128 {
311 assert!(denominator != 0, "attempt to divide by zero");
312
313 let mut i = 1;
314 let mut output = 0;
315 let mut numerator_accum = factor * denominator;
316 while numerator_accum > 0 {
317 output += numerator_accum;
318
319 let Some(val) = numerator_accum.checked_mul(numerator) else {
321 break;
322 };
323
324 numerator_accum = val / (denominator * i);
326 i += 1;
327 }
328 output / denominator
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 #[test]
337 fn test_calc_excess_blob_gas() {
338 for t @ &(excess, blobs, expected) in &[
339 (0, 0, 0),
342 (0, 1, 0),
343 (0, TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB, 0),
344 (0, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB),
347 (1, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 1, DATA_GAS_PER_BLOB + 1),
348 (
349 1,
350 (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) + 2,
351 2 * DATA_GAS_PER_BLOB + 1,
352 ),
353 (
356 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
357 TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB,
358 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
359 ),
360 (
361 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
362 (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 1,
363 TARGET_DATA_GAS_PER_BLOCK_DENCUN - DATA_GAS_PER_BLOB,
364 ),
365 (
366 TARGET_DATA_GAS_PER_BLOCK_DENCUN,
367 (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 2,
368 TARGET_DATA_GAS_PER_BLOCK_DENCUN - (2 * DATA_GAS_PER_BLOB),
369 ),
370 (DATA_GAS_PER_BLOB - 1, (TARGET_DATA_GAS_PER_BLOCK_DENCUN / DATA_GAS_PER_BLOB) - 1, 0),
371 ] {
372 let actual = calc_excess_blob_gas(excess, blobs * DATA_GAS_PER_BLOB);
373 assert_eq!(actual, expected, "test: {t:?}");
374 }
375 }
376
377 #[test]
379 fn test_calc_blob_fee() {
380 let blob_fee_vectors = &[
381 (0, 1),
382 (2314057, 1),
383 (2314058, 2),
384 (10 * 1024 * 1024, 23),
385 (148099578, 18446739238971471609), (148099579, 18446744762204311910), (161087488, 902580055246494526580),
394 ];
395
396 for &(excess, expected) in blob_fee_vectors {
397 let actual = calc_blob_gasprice(excess);
398 assert_eq!(actual, expected, "test: {excess}");
399 }
400 }
401
402 #[test]
404 fn fake_exp() {
405 for t @ &(factor, numerator, denominator, expected) in &[
406 (1u64, 0u64, 1u64, 1u128),
407 (38493, 0, 1000, 38493),
408 (0, 1234, 2345, 0),
409 (1, 2, 1, 6), (1, 4, 2, 6),
411 (1, 3, 1, 16), (1, 6, 2, 18),
413 (1, 4, 1, 49), (1, 8, 2, 50),
415 (10, 8, 2, 542), (11, 8, 2, 596), (1, 5, 1, 136), (1, 5, 2, 11), (2, 5, 2, 23), (1, 50000000, 2225652, 5709098764),
421 (1, 380928, BLOB_GASPRICE_UPDATE_FRACTION.try_into().unwrap(), 1),
422 ] {
423 let actual = fake_exponential(factor as u128, numerator as u128, denominator as u128);
424 assert_eq!(actual, expected, "test: {t:?}");
425 }
426 }
427
428 #[test]
429 #[cfg(feature = "serde")]
430 fn serde_heap_blob() {
431 let blob = HeapBlob::repeat_byte(0x42);
432 let serialized = serde_json::to_string(&blob).unwrap();
433
434 let deserialized: HeapBlob = serde_json::from_str(&serialized).unwrap();
435 assert_eq!(blob, deserialized);
436 }
437
438 #[test]
439 fn fake_exp_handles_overflow() {
440 let factor = 1u128; let numerator = u64::MAX as u128; let denominator = 5007716u128; let result = fake_exponential(factor, numerator, denominator);
447
448 assert!(result > 0);
450
451 let prague_params = crate::eip7840::BlobParams::prague();
453 let _blob_fee = prague_params.calc_blob_fee(u64::MAX);
455 }
456}