Skip to main content

c_kzg/bindings/
mod.rs

1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4#![allow(dead_code)]
5
6#[cfg(feature = "serde")]
7mod serde;
8#[cfg(test)]
9mod test_formats;
10
11include!("./generated.rs");
12
13use alloc::boxed::Box;
14use alloc::string::String;
15use alloc::vec::Vec;
16use core::ffi::CStr;
17use core::fmt;
18use core::mem::MaybeUninit;
19use core::ops::{Deref, DerefMut};
20use core::ptr;
21
22#[cfg(feature = "std")]
23use alloc::ffi::CString;
24#[cfg(feature = "std")]
25use std::path::Path;
26
27const BYTES_PER_G1_POINT: usize = 48;
28const BYTES_PER_G2_POINT: usize = 96;
29
30/// Number of G1 points required for the kzg trusted setup.
31const NUM_G1_POINTS: usize = 4096;
32
33/// Number of G2 points required for the kzg trusted setup.
34/// 65 is fixed and is used for providing multiproofs up to 64 field elements.
35const NUM_G2_POINTS: usize = 65;
36
37/// A trusted (valid) KZG commitment.
38// NOTE: this is a type alias to the struct Bytes48, same as [`KZGProof`] in the C header files. To
39//       facilitate type safety: proofs and commitments should not be interchangeable, we use a
40//       custom implementation.
41#[repr(C)]
42#[derive(Debug, Copy, Clone)]
43pub struct KZGCommitment {
44    bytes: [u8; BYTES_PER_COMMITMENT],
45}
46
47/// A trusted (valid) KZG proof.
48// NOTE: this is a type alias to the struct Bytes48, same as [`KZGCommitment`] in the C header
49//       files. To facilitate type safety: proofs and commitments should not be interchangeable, we
50//       use a custom implementation.
51#[repr(C)]
52#[derive(Debug, Copy, Clone)]
53pub struct KZGProof {
54    bytes: [u8; BYTES_PER_PROOF],
55}
56
57#[derive(Debug)]
58pub enum Error {
59    /// Wrong number of bytes.
60    InvalidBytesLength(String),
61    /// The hex string is invalid.
62    InvalidHexFormat(String),
63    /// The KZG proof is invalid.
64    InvalidKzgProof(String),
65    /// The KZG commitment is invalid.
66    InvalidKzgCommitment(String),
67    /// The provided trusted setup is invalid.
68    InvalidTrustedSetup(String),
69    /// Paired arguments have different lengths.
70    MismatchLength(String),
71    /// Loading the trusted setup failed.
72    LoadingTrustedSetupFailed(KzgErrors),
73    /// The underlying c-kzg library returned an error.
74    CError(C_KZG_RET),
75}
76
77#[cfg(feature = "std")]
78impl std::error::Error for Error {}
79
80impl fmt::Display for Error {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        match self {
83            Self::InvalidBytesLength(s)
84            | Self::InvalidHexFormat(s)
85            | Self::InvalidKzgProof(s)
86            | Self::InvalidKzgCommitment(s)
87            | Self::InvalidTrustedSetup(s)
88            | Self::MismatchLength(s) => f.write_str(s),
89            Self::LoadingTrustedSetupFailed(s) => write!(f, "KzgErrors: {s:?}"),
90            Self::CError(s) => fmt::Debug::fmt(s, f),
91        }
92    }
93}
94
95impl From<KzgErrors> for Error {
96    fn from(e: KzgErrors) -> Self {
97        Error::LoadingTrustedSetupFailed(e)
98    }
99}
100
101pub type CellsPerExtBlob = [Cell; CELLS_PER_EXT_BLOB];
102pub type ProofsPerExtBlob = [KZGProof; CELLS_PER_EXT_BLOB];
103
104#[derive(Debug)]
105pub enum KzgErrors {
106    /// Failed to get current directory.
107    FailedCurrentDirectory,
108    /// The specified path does not exist.
109    PathNotExists,
110    /// Problems related to I/O.
111    IOError,
112    /// Not a valid file.
113    NotValidFile,
114    /// File is not properly formatted.
115    FileFormatError,
116    /// Not able to parse to usize.
117    ParseError,
118    /// Number of points does not match what is expected.
119    MismatchedNumberOfPoints,
120}
121
122/// Converts a hex string (with or without the 0x prefix) to bytes.
123pub fn hex_to_bytes(hex_str: &str) -> Result<Vec<u8>, Error> {
124    let trimmed_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
125    hex::decode(trimmed_str)
126        .map_err(|e| Error::InvalidHexFormat(format!("Failed to decode hex: {e}")))
127}
128
129/// Holds the parameters of a kzg trusted setup ceremony.
130impl KZGSettings {
131    /// Initializes a trusted setup from a flat array of `FIELD_ELEMENTS_PER_BLOB` G1 points in monomial form, a flat
132    /// array of `FIELD_ELEMENTS_PER_BLOB` G1 points in Lagrange form, and a flat array of 65 G2 points in monomial
133    /// form.
134    pub fn load_trusted_setup(
135        g1_monomial_bytes: &[u8],
136        g1_lagrange_bytes: &[u8],
137        g2_monomial_bytes: &[u8],
138        precompute: u64,
139    ) -> Result<Self, Error> {
140        let mut kzg_settings = MaybeUninit::<KZGSettings>::uninit();
141        unsafe {
142            let res = load_trusted_setup(
143                kzg_settings.as_mut_ptr(),
144                g1_monomial_bytes.as_ptr().cast(),
145                g1_monomial_bytes.len() as u64,
146                g1_lagrange_bytes.as_ptr().cast(),
147                g1_lagrange_bytes.len() as u64,
148                g2_monomial_bytes.as_ptr().cast(),
149                g2_monomial_bytes.len() as u64,
150                precompute,
151            );
152            if let C_KZG_RET::C_KZG_OK = res {
153                Ok(kzg_settings.assume_init())
154            } else {
155                Err(Error::InvalidTrustedSetup(format!(
156                    "Invalid trusted setup: {res:?}",
157                )))
158            }
159        }
160    }
161
162    /// Loads the trusted setup parameters from a file. The file format is as follows:
163    ///
164    /// FIELD_ELEMENTS_PER_BLOB
165    /// 65 # This is fixed and is used for providing multiproofs up to 64 field elements.
166    /// FIELD_ELEMENT_PER_BLOB g1 byte values in Lagrange form
167    /// 65 g2 byte values in monomial form
168    /// FIELD_ELEMENT_PER_BLOB g1 byte values in monomial form
169    #[cfg(feature = "std")]
170    pub fn load_trusted_setup_file(file_path: &Path, precompute: u64) -> Result<Self, Error> {
171        #[cfg(unix)]
172        let file_path_bytes = {
173            use std::os::unix::prelude::OsStrExt;
174            file_path.as_os_str().as_bytes()
175        };
176
177        #[cfg(windows)]
178        let file_path_bytes = file_path
179            .as_os_str()
180            .to_str()
181            .ok_or_else(|| Error::InvalidTrustedSetup("Unsupported non unicode file path".into()))?
182            .as_bytes();
183
184        let file_path = CString::new(file_path_bytes)
185            .map_err(|e| Error::InvalidTrustedSetup(format!("Invalid trusted setup file: {e}")))?;
186
187        Self::load_trusted_setup_file_inner(&file_path, precompute)
188    }
189
190    /// Parses the contents of a KZG trusted setup file into a KzgSettings.
191    pub fn parse_kzg_trusted_setup(trusted_setup: &str, precompute: u64) -> Result<Self, Error> {
192        let mut lines = trusted_setup.lines();
193
194        // Load number of g1 points
195        let n_g1 = lines
196            .next()
197            .ok_or(KzgErrors::FileFormatError)?
198            .parse::<usize>()
199            .map_err(|_| KzgErrors::ParseError)?;
200        if n_g1 != NUM_G1_POINTS {
201            return Err(KzgErrors::MismatchedNumberOfPoints.into());
202        }
203
204        // Load number of g2 points
205        let n_g2 = lines
206            .next()
207            .ok_or(KzgErrors::FileFormatError)?
208            .parse::<usize>()
209            .map_err(|_| KzgErrors::ParseError)?;
210        if n_g2 != NUM_G2_POINTS {
211            return Err(KzgErrors::MismatchedNumberOfPoints.into());
212        }
213
214        let mut g1_lagrange_bytes = alloc::boxed::Box::new([0; BYTES_PER_G1_POINT * NUM_G1_POINTS]);
215        let mut g2_monomial_bytes = alloc::boxed::Box::new([0; BYTES_PER_G2_POINT * NUM_G2_POINTS]);
216        let mut g1_monomial_bytes = alloc::boxed::Box::new([0; BYTES_PER_G1_POINT * NUM_G1_POINTS]);
217
218        // Load g1 Lagrange bytes
219        g1_lagrange_bytes
220            .chunks_mut(BYTES_PER_G1_POINT)
221            .try_for_each(|chunk| {
222                let line = lines.next().ok_or(KzgErrors::FileFormatError)?;
223                hex::decode_to_slice(line, chunk).map_err(|_| KzgErrors::ParseError)
224            })?;
225
226        // Load g2 monomial bytes
227        g2_monomial_bytes
228            .chunks_mut(BYTES_PER_G2_POINT)
229            .try_for_each(|chunk| {
230                let line = lines.next().ok_or(KzgErrors::FileFormatError)?;
231                hex::decode_to_slice(line, chunk).map_err(|_| KzgErrors::ParseError)
232            })?;
233
234        // Load g1 monomial bytes
235        g1_monomial_bytes
236            .chunks_mut(BYTES_PER_G1_POINT)
237            .try_for_each(|chunk| {
238                let line = lines.next().ok_or(KzgErrors::FileFormatError)?;
239                hex::decode_to_slice(line, chunk).map_err(|_| KzgErrors::ParseError)
240            })?;
241
242        if lines.next().is_some() {
243            return Err(KzgErrors::FileFormatError.into());
244        }
245
246        Self::load_trusted_setup(
247            g1_monomial_bytes.as_ref(),
248            g1_lagrange_bytes.as_ref(),
249            g2_monomial_bytes.as_ref(),
250            precompute,
251        )
252    }
253
254    /// Loads the trusted setup parameters from a file. The file format is as follows:
255    ///
256    /// FIELD_ELEMENTS_PER_BLOB
257    /// 65 # This is fixed and is used for providing multiproofs up to 64 field elements.
258    /// FIELD_ELEMENT_PER_BLOB g1 byte values in Lagrange form
259    /// 65 g2 byte values in monomial form
260    /// FIELD_ELEMENT_PER_BLOB g1 byte values in monomial form
261    #[cfg(not(feature = "std"))]
262    pub fn load_trusted_setup_file(file_path: &CStr, precompute: u64) -> Result<Self, Error> {
263        Self::load_trusted_setup_file_inner(file_path, precompute)
264    }
265
266    /// Loads the trusted setup parameters from a file.
267    ///
268    /// Same as [`load_trusted_setup_file`](Self::load_trusted_setup_file)
269    #[cfg_attr(not(feature = "std"), doc = ", but takes a `CStr` instead of a `Path`")]
270    pub fn load_trusted_setup_file_inner(file_path: &CStr, precompute: u64) -> Result<Self, Error> {
271        // SAFETY: `b"r\0"` is a valid null-terminated string.
272        const MODE: &CStr = c"r";
273
274        // SAFETY:
275        // - .as_ptr(): pointer is not dangling because file_path has not been dropped.
276        //    Usage or ptr: File will not be written to it by the c code.
277        let file_ptr = unsafe { libc::fopen(file_path.as_ptr(), MODE.as_ptr()) };
278        if file_ptr.is_null() {
279            #[cfg(not(feature = "std"))]
280            return Err(Error::InvalidTrustedSetup(format!(
281                "Failed to open trusted setup file {file_path:?}"
282            )));
283
284            #[cfg(feature = "std")]
285            return Err(Error::InvalidTrustedSetup(format!(
286                "Failed to open trusted setup file {file_path:?}: {}",
287                std::io::Error::last_os_error()
288            )));
289        }
290        let mut kzg_settings = MaybeUninit::<KZGSettings>::uninit();
291
292        unsafe {
293            let res = load_trusted_setup_file(kzg_settings.as_mut_ptr(), file_ptr, precompute);
294            let _unchecked_close_result = libc::fclose(file_ptr);
295
296            if let C_KZG_RET::C_KZG_OK = res {
297                Ok(kzg_settings.assume_init())
298            } else {
299                Err(Error::InvalidTrustedSetup(format!(
300                    "Invalid trusted setup: {res:?}"
301                )))
302            }
303        }
304    }
305
306    pub fn blob_to_kzg_commitment(&self, blob: &Blob) -> Result<KZGCommitment, Error> {
307        let mut kzg_commitment: MaybeUninit<KZGCommitment> = MaybeUninit::uninit();
308        unsafe {
309            let res = blob_to_kzg_commitment(kzg_commitment.as_mut_ptr(), blob, self);
310            if let C_KZG_RET::C_KZG_OK = res {
311                Ok(kzg_commitment.assume_init())
312            } else {
313                Err(Error::CError(res))
314            }
315        }
316    }
317
318    pub fn compute_kzg_proof(
319        &self,
320        blob: &Blob,
321        z_bytes: &Bytes32,
322    ) -> Result<(KZGProof, Bytes32), Error> {
323        let mut kzg_proof = MaybeUninit::<KZGProof>::uninit();
324        let mut y_out = MaybeUninit::<Bytes32>::uninit();
325        unsafe {
326            let res = compute_kzg_proof(
327                kzg_proof.as_mut_ptr(),
328                y_out.as_mut_ptr(),
329                blob,
330                z_bytes,
331                self,
332            );
333            if let C_KZG_RET::C_KZG_OK = res {
334                Ok((kzg_proof.assume_init(), y_out.assume_init()))
335            } else {
336                Err(Error::CError(res))
337            }
338        }
339    }
340
341    pub fn compute_blob_kzg_proof(
342        &self,
343        blob: &Blob,
344        commitment_bytes: &Bytes48,
345    ) -> Result<KZGProof, Error> {
346        let mut kzg_proof = MaybeUninit::<KZGProof>::uninit();
347        unsafe {
348            let res = compute_blob_kzg_proof(kzg_proof.as_mut_ptr(), blob, commitment_bytes, self);
349            if let C_KZG_RET::C_KZG_OK = res {
350                Ok(kzg_proof.assume_init())
351            } else {
352                Err(Error::CError(res))
353            }
354        }
355    }
356
357    pub fn verify_kzg_proof(
358        &self,
359        commitment_bytes: &Bytes48,
360        z_bytes: &Bytes32,
361        y_bytes: &Bytes32,
362        proof_bytes: &Bytes48,
363    ) -> Result<bool, Error> {
364        let mut verified: MaybeUninit<bool> = MaybeUninit::uninit();
365        unsafe {
366            let res = verify_kzg_proof(
367                verified.as_mut_ptr(),
368                commitment_bytes,
369                z_bytes,
370                y_bytes,
371                proof_bytes,
372                self,
373            );
374            if let C_KZG_RET::C_KZG_OK = res {
375                Ok(verified.assume_init())
376            } else {
377                Err(Error::CError(res))
378            }
379        }
380    }
381
382    pub fn verify_blob_kzg_proof(
383        &self,
384        blob: &Blob,
385        commitment_bytes: &Bytes48,
386        proof_bytes: &Bytes48,
387    ) -> Result<bool, Error> {
388        let mut verified: MaybeUninit<bool> = MaybeUninit::uninit();
389        unsafe {
390            let res = verify_blob_kzg_proof(
391                verified.as_mut_ptr(),
392                blob,
393                commitment_bytes,
394                proof_bytes,
395                self,
396            );
397            if let C_KZG_RET::C_KZG_OK = res {
398                Ok(verified.assume_init())
399            } else {
400                Err(Error::CError(res))
401            }
402        }
403    }
404
405    pub fn verify_blob_kzg_proof_batch(
406        &self,
407        blobs: &[Blob],
408        commitments_bytes: &[Bytes48],
409        proofs_bytes: &[Bytes48],
410    ) -> Result<bool, Error> {
411        if blobs.len() != commitments_bytes.len() {
412            return Err(Error::MismatchLength(format!(
413                "There are {} blobs and {} commitments",
414                blobs.len(),
415                commitments_bytes.len()
416            )));
417        }
418        if blobs.len() != proofs_bytes.len() {
419            return Err(Error::MismatchLength(format!(
420                "There are {} blobs and {} proofs",
421                blobs.len(),
422                proofs_bytes.len()
423            )));
424        }
425        let mut verified: MaybeUninit<bool> = MaybeUninit::uninit();
426        unsafe {
427            let res = verify_blob_kzg_proof_batch(
428                verified.as_mut_ptr(),
429                blobs.as_ptr(),
430                commitments_bytes.as_ptr(),
431                proofs_bytes.as_ptr(),
432                blobs.len() as u64,
433                self,
434            );
435            if let C_KZG_RET::C_KZG_OK = res {
436                Ok(verified.assume_init())
437            } else {
438                Err(Error::CError(res))
439            }
440        }
441    }
442
443    pub fn compute_cells(&self, blob: &Blob) -> Result<Box<CellsPerExtBlob>, Error> {
444        let mut cells: Box<[Cell; CELLS_PER_EXT_BLOB]> = vec![Cell::default(); CELLS_PER_EXT_BLOB]
445            .into_boxed_slice()
446            .try_into()
447            .unwrap();
448        unsafe {
449            let res = compute_cells_and_kzg_proofs(cells.as_mut_ptr(), ptr::null_mut(), blob, self);
450            if let C_KZG_RET::C_KZG_OK = res {
451                Ok(cells)
452            } else {
453                Err(Error::CError(res))
454            }
455        }
456    }
457
458    pub fn compute_cells_and_kzg_proofs(
459        &self,
460        blob: &Blob,
461    ) -> Result<(Box<CellsPerExtBlob>, Box<ProofsPerExtBlob>), Error> {
462        let mut cells: Box<[Cell; CELLS_PER_EXT_BLOB]> = vec![Cell::default(); CELLS_PER_EXT_BLOB]
463            .into_boxed_slice()
464            .try_into()
465            .unwrap();
466        let mut proofs: Box<[KZGProof; CELLS_PER_EXT_BLOB]> =
467            vec![KZGProof::default(); CELLS_PER_EXT_BLOB]
468                .into_boxed_slice()
469                .try_into()
470                .unwrap();
471        unsafe {
472            let res =
473                compute_cells_and_kzg_proofs(cells.as_mut_ptr(), proofs.as_mut_ptr(), blob, self);
474            if let C_KZG_RET::C_KZG_OK = res {
475                Ok((cells, proofs))
476            } else {
477                Err(Error::CError(res))
478            }
479        }
480    }
481
482    pub fn recover_cells_and_kzg_proofs(
483        &self,
484        cell_indices: &[u64],
485        cells: &[Cell],
486    ) -> Result<(Box<CellsPerExtBlob>, Box<ProofsPerExtBlob>), Error> {
487        if cell_indices.len() != cells.len() {
488            return Err(Error::MismatchLength(format!(
489                "There are {} cell indices and {} cells",
490                cell_indices.len(),
491                cells.len()
492            )));
493        }
494        let mut recovered_cells: Box<[Cell; CELLS_PER_EXT_BLOB]> =
495            vec![Cell::default(); CELLS_PER_EXT_BLOB]
496                .into_boxed_slice()
497                .try_into()
498                .unwrap();
499        let mut recovered_proofs: Box<[KZGProof; CELLS_PER_EXT_BLOB]> =
500            vec![KZGProof::default(); CELLS_PER_EXT_BLOB]
501                .into_boxed_slice()
502                .try_into()
503                .unwrap();
504        unsafe {
505            let res = recover_cells_and_kzg_proofs(
506                recovered_cells.as_mut_ptr(),
507                recovered_proofs.as_mut_ptr(),
508                cell_indices.as_ptr(),
509                cells.as_ptr(),
510                cells.len() as u64,
511                self,
512            );
513            if let C_KZG_RET::C_KZG_OK = res {
514                Ok((recovered_cells, recovered_proofs))
515            } else {
516                Err(Error::CError(res))
517            }
518        }
519    }
520
521    pub fn verify_cell_kzg_proof_batch(
522        &self,
523        commitments_bytes: &[Bytes48],
524        cell_indices: &[u64],
525        cells: &[Cell],
526        proofs_bytes: &[Bytes48],
527    ) -> Result<bool, Error> {
528        if cells.len() != commitments_bytes.len() {
529            return Err(Error::MismatchLength(format!(
530                "There are {} cells and {} commitments",
531                cells.len(),
532                commitments_bytes.len()
533            )));
534        }
535        if cells.len() != cell_indices.len() {
536            return Err(Error::MismatchLength(format!(
537                "There are {} cells and {} column indices",
538                cells.len(),
539                cell_indices.len()
540            )));
541        }
542        if cells.len() != proofs_bytes.len() {
543            return Err(Error::MismatchLength(format!(
544                "There are {} cells and {} proofs",
545                cells.len(),
546                proofs_bytes.len()
547            )));
548        }
549        let mut verified: MaybeUninit<bool> = MaybeUninit::uninit();
550        unsafe {
551            let res = verify_cell_kzg_proof_batch(
552                verified.as_mut_ptr(),
553                commitments_bytes.as_ptr(),
554                cell_indices.as_ptr(),
555                cells.as_ptr(),
556                proofs_bytes.as_ptr(),
557                cells.len() as u64,
558                self,
559            );
560            if let C_KZG_RET::C_KZG_OK = res {
561                Ok(verified.assume_init())
562            } else {
563                Err(Error::CError(res))
564            }
565        }
566    }
567}
568
569impl Drop for KZGSettings {
570    fn drop(&mut self) {
571        unsafe { free_trusted_setup(self) }
572    }
573}
574
575impl Blob {
576    /// Creates a new blob from a byte array.
577    pub const fn new(bytes: [u8; BYTES_PER_BLOB]) -> Self {
578        Self { bytes }
579    }
580
581    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
582        if bytes.len() != BYTES_PER_BLOB {
583            return Err(Error::InvalidBytesLength(format!(
584                "Invalid byte length. Expected {} got {}",
585                BYTES_PER_BLOB,
586                bytes.len(),
587            )));
588        }
589        let mut new_bytes = [0; BYTES_PER_BLOB];
590        new_bytes.copy_from_slice(bytes);
591        Ok(Self::new(new_bytes))
592    }
593
594    pub fn from_hex(hex_str: &str) -> Result<Self, Error> {
595        Self::from_bytes(&hex_to_bytes(hex_str)?)
596    }
597
598    pub fn into_inner(self) -> [u8; BYTES_PER_BLOB] {
599        self.bytes
600    }
601}
602
603impl AsRef<[u8]> for Blob {
604    fn as_ref(&self) -> &[u8] {
605        &self.bytes
606    }
607}
608
609impl Bytes32 {
610    /// Creates a new instance from a byte array.
611    pub const fn new(bytes: [u8; 32]) -> Self {
612        Self { bytes }
613    }
614
615    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
616        if bytes.len() != 32 {
617            return Err(Error::InvalidBytesLength(format!(
618                "Invalid byte length. Expected {} got {}",
619                32,
620                bytes.len(),
621            )));
622        }
623        let mut new_bytes = [0; 32];
624        new_bytes.copy_from_slice(bytes);
625        Ok(Self::new(new_bytes))
626    }
627
628    pub fn from_hex(hex_str: &str) -> Result<Self, Error> {
629        Self::from_bytes(&hex_to_bytes(hex_str)?)
630    }
631}
632
633impl Bytes48 {
634    /// Creates a new instance from a byte array.
635    pub const fn new(bytes: [u8; 48]) -> Self {
636        Self { bytes }
637    }
638
639    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
640        if bytes.len() != 48 {
641            return Err(Error::InvalidBytesLength(format!(
642                "Invalid byte length. Expected {} got {}",
643                48,
644                bytes.len(),
645            )));
646        }
647        let mut new_bytes = [0; 48];
648        new_bytes.copy_from_slice(bytes);
649        Ok(Self::new(new_bytes))
650    }
651
652    pub fn from_hex(hex_str: &str) -> Result<Self, Error> {
653        Self::from_bytes(&hex_to_bytes(hex_str)?)
654    }
655
656    pub fn into_inner(self) -> [u8; 48] {
657        self.bytes
658    }
659}
660
661impl KZGProof {
662    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
663        if bytes.len() != BYTES_PER_PROOF {
664            return Err(Error::InvalidKzgProof(format!(
665                "Invalid byte length. Expected {} got {}",
666                BYTES_PER_PROOF,
667                bytes.len(),
668            )));
669        }
670        let mut proof_bytes = [0; BYTES_PER_PROOF];
671        proof_bytes.copy_from_slice(bytes);
672        Ok(Self { bytes: proof_bytes })
673    }
674
675    pub fn to_bytes(&self) -> Bytes48 {
676        Bytes48 { bytes: self.bytes }
677    }
678
679    pub fn as_hex_string(&self) -> String {
680        hex::encode(self.bytes)
681    }
682}
683
684impl KZGCommitment {
685    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
686        if bytes.len() != BYTES_PER_COMMITMENT {
687            return Err(Error::InvalidKzgCommitment(format!(
688                "Invalid byte length. Expected {} got {}",
689                BYTES_PER_COMMITMENT,
690                bytes.len(),
691            )));
692        }
693        let mut commitment = [0; BYTES_PER_COMMITMENT];
694        commitment.copy_from_slice(bytes);
695        Ok(Self { bytes: commitment })
696    }
697
698    pub fn to_bytes(&self) -> Bytes48 {
699        Bytes48 { bytes: self.bytes }
700    }
701
702    pub fn as_hex_string(&self) -> String {
703        hex::encode(self.bytes)
704    }
705}
706
707impl Cell {
708    pub const fn new(bytes: [u8; BYTES_PER_CELL]) -> Self {
709        Self { bytes }
710    }
711
712    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
713        if bytes.len() != BYTES_PER_CELL {
714            return Err(Error::InvalidBytesLength(format!(
715                "Invalid byte length. Expected {} got {}",
716                BYTES_PER_CELL,
717                bytes.len(),
718            )));
719        }
720        let mut new_bytes = [0; BYTES_PER_CELL];
721        new_bytes.copy_from_slice(bytes);
722        Ok(Self::new(new_bytes))
723    }
724
725    pub fn to_bytes(&self) -> [u8; BYTES_PER_CELL] {
726        self.bytes
727    }
728
729    pub fn from_hex(hex_str: &str) -> Result<Self, Error> {
730        Self::from_bytes(&hex_to_bytes(hex_str)?)
731    }
732}
733
734impl From<[u8; BYTES_PER_COMMITMENT]> for KZGCommitment {
735    fn from(value: [u8; BYTES_PER_COMMITMENT]) -> Self {
736        Self { bytes: value }
737    }
738}
739
740impl From<[u8; BYTES_PER_PROOF]> for KZGProof {
741    fn from(value: [u8; BYTES_PER_PROOF]) -> Self {
742        Self { bytes: value }
743    }
744}
745
746impl From<[u8; BYTES_PER_BLOB]> for Blob {
747    fn from(value: [u8; BYTES_PER_BLOB]) -> Self {
748        Self { bytes: value }
749    }
750}
751
752impl From<[u8; 32]> for Bytes32 {
753    fn from(value: [u8; 32]) -> Self {
754        Self { bytes: value }
755    }
756}
757
758impl AsRef<[u8; 32]> for Bytes32 {
759    fn as_ref(&self) -> &[u8; 32] {
760        &self.bytes
761    }
762}
763
764impl From<[u8; 48]> for Bytes48 {
765    fn from(value: [u8; 48]) -> Self {
766        Self { bytes: value }
767    }
768}
769
770impl AsRef<[u8; 48]> for Bytes48 {
771    fn as_ref(&self) -> &[u8; 48] {
772        &self.bytes
773    }
774}
775
776impl Deref for Bytes32 {
777    type Target = [u8; 32];
778    fn deref(&self) -> &Self::Target {
779        &self.bytes
780    }
781}
782
783impl Deref for Bytes48 {
784    type Target = [u8; 48];
785    fn deref(&self) -> &Self::Target {
786        &self.bytes
787    }
788}
789
790impl DerefMut for Bytes48 {
791    fn deref_mut(&mut self) -> &mut Self::Target {
792        &mut self.bytes
793    }
794}
795
796impl Deref for Blob {
797    type Target = [u8; BYTES_PER_BLOB];
798    fn deref(&self) -> &Self::Target {
799        &self.bytes
800    }
801}
802
803impl DerefMut for Blob {
804    fn deref_mut(&mut self) -> &mut Self::Target {
805        &mut self.bytes
806    }
807}
808
809impl Clone for Blob {
810    fn clone(&self) -> Self {
811        Blob { bytes: self.bytes }
812    }
813}
814
815impl Deref for KZGProof {
816    type Target = [u8; BYTES_PER_PROOF];
817    fn deref(&self) -> &Self::Target {
818        &self.bytes
819    }
820}
821
822impl Deref for KZGCommitment {
823    type Target = [u8; BYTES_PER_COMMITMENT];
824    fn deref(&self) -> &Self::Target {
825        &self.bytes
826    }
827}
828
829#[allow(clippy::derivable_impls)]
830impl Default for Bytes32 {
831    fn default() -> Self {
832        Bytes32 { bytes: [0; 32] }
833    }
834}
835
836impl Default for Bytes48 {
837    fn default() -> Self {
838        Bytes48 { bytes: [0; 48] }
839    }
840}
841
842impl Default for KZGCommitment {
843    fn default() -> Self {
844        KZGCommitment {
845            bytes: [0; BYTES_PER_COMMITMENT],
846        }
847    }
848}
849
850impl Default for KZGProof {
851    fn default() -> Self {
852        KZGProof {
853            bytes: [0; BYTES_PER_PROOF],
854        }
855    }
856}
857
858impl Default for Blob {
859    fn default() -> Self {
860        Blob {
861            bytes: [0; BYTES_PER_BLOB],
862        }
863    }
864}
865
866impl Default for Cell {
867    fn default() -> Self {
868        Cell {
869            bytes: [0; BYTES_PER_CELL],
870        }
871    }
872}
873
874#[cfg(feature = "arbitrary")]
875impl arbitrary::Arbitrary<'_> for Bytes32 {
876    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
877        let mut bytes = [0u8; 32];
878        u.fill_buffer(&mut bytes)?;
879        Ok(Bytes32::from(bytes))
880    }
881}
882
883#[cfg(feature = "arbitrary")]
884impl arbitrary::Arbitrary<'_> for Bytes48 {
885    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
886        let mut bytes = [0u8; 48];
887        u.fill_buffer(&mut bytes)?;
888        Ok(Bytes48::from(bytes))
889    }
890}
891
892#[cfg(feature = "arbitrary")]
893impl arbitrary::Arbitrary<'_> for Blob {
894    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
895        let mut bytes = [0u8; BYTES_PER_BLOB];
896        u.fill_buffer(&mut bytes)?;
897        Ok(Blob::from(bytes))
898    }
899}
900
901#[cfg(feature = "arbitrary")]
902impl arbitrary::Arbitrary<'_> for Cell {
903    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
904        let mut bytes = [0u8; BYTES_PER_CELL];
905        u.fill_buffer(&mut bytes)?;
906        Ok(Cell::new(bytes))
907    }
908}
909
910/// Safety: The memory for `roots_of_unity` and `g1_values` and `g2_values` are only freed on
911/// calling `free_trusted_setup` which only happens when we drop the struct.
912unsafe impl Sync for KZGSettings {}
913unsafe impl Send for KZGSettings {}
914
915#[cfg(test)]
916#[allow(unused_imports, dead_code)]
917mod tests {
918    use super::*;
919    use rand::{rngs::ThreadRng, Rng};
920    use std::{fs, path::PathBuf};
921    use test_formats::{
922        blob_to_kzg_commitment_test, compute_blob_kzg_proof, compute_cells,
923        compute_cells_and_kzg_proofs, compute_kzg_proof, recover_cells_and_kzg_proofs,
924        verify_blob_kzg_proof, verify_blob_kzg_proof_batch, verify_cell_kzg_proof_batch,
925        verify_kzg_proof,
926    };
927
928    fn generate_random_blob(rng: &mut ThreadRng) -> Blob {
929        let mut arr = [0u8; BYTES_PER_BLOB];
930        rng.fill(&mut arr[..]);
931        // Ensure that the blob is canonical by ensuring that
932        // each field element contained in the blob is < BLS_MODULUS
933        for i in 0..FIELD_ELEMENTS_PER_BLOB {
934            arr[i * BYTES_PER_FIELD_ELEMENT] = 0;
935        }
936        arr.into()
937    }
938
939    fn test_simple(trusted_setup_file: &Path) {
940        let mut rng = rand::rng();
941        assert!(trusted_setup_file.exists());
942        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file, 0).unwrap();
943
944        let num_blobs: usize = rng.random_range(1..16);
945        let mut blobs: Vec<Blob> = (0..num_blobs)
946            .map(|_| generate_random_blob(&mut rng))
947            .collect();
948
949        let commitments: Vec<Bytes48> = blobs
950            .iter()
951            .map(|blob| kzg_settings.blob_to_kzg_commitment(blob).unwrap())
952            .map(|commitment| commitment.to_bytes())
953            .collect();
954
955        let proofs: Vec<Bytes48> = blobs
956            .iter()
957            .zip(commitments.iter())
958            .map(|(blob, commitment)| {
959                kzg_settings
960                    .compute_blob_kzg_proof(blob, commitment)
961                    .unwrap()
962            })
963            .map(|proof| proof.to_bytes())
964            .collect();
965
966        assert!(kzg_settings
967            .verify_blob_kzg_proof_batch(&blobs, &commitments, &proofs)
968            .unwrap());
969
970        blobs.pop();
971
972        let error = kzg_settings
973            .verify_blob_kzg_proof_batch(&blobs, &commitments, &proofs)
974            .unwrap_err();
975        assert!(matches!(error, Error::MismatchLength(_)));
976
977        let incorrect_blob = generate_random_blob(&mut rng);
978        blobs.push(incorrect_blob);
979
980        assert!(!kzg_settings
981            .verify_blob_kzg_proof_batch(&blobs, &commitments, &proofs)
982            .unwrap());
983    }
984
985    #[test]
986    fn test_end_to_end() {
987        let trusted_setup_file = Path::new("src/trusted_setup.txt");
988        test_simple(trusted_setup_file);
989    }
990
991    const BLOB_TO_KZG_COMMITMENT_TESTS: &str = "tests/blob_to_kzg_commitment/*/*/*";
992    const COMPUTE_KZG_PROOF_TESTS: &str = "tests/compute_kzg_proof/*/*/*";
993    const COMPUTE_BLOB_KZG_PROOF_TESTS: &str = "tests/compute_blob_kzg_proof/*/*/*";
994    const VERIFY_KZG_PROOF_TESTS: &str = "tests/verify_kzg_proof/*/*/*";
995    const VERIFY_BLOB_KZG_PROOF_TESTS: &str = "tests/verify_blob_kzg_proof/*/*/*";
996    const VERIFY_BLOB_KZG_PROOF_BATCH_TESTS: &str = "tests/verify_blob_kzg_proof_batch/*/*/*";
997
998    const COMPUTE_CELLS_TESTS: &str = "tests/compute_cells/*/*/*";
999    const COMPUTE_CELLS_AND_KZG_PROOFS_TESTS: &str = "tests/compute_cells_and_kzg_proofs/*/*/*";
1000    const RECOVER_CELLS_AND_KZG_PROOFS_TESTS: &str = "tests/recover_cells_and_kzg_proofs/*/*/*";
1001    const VERIFY_CELL_KZG_PROOF_BATCH_TESTS: &str = "tests/verify_cell_kzg_proof_batch/*/*/*";
1002
1003    #[test]
1004    fn test_blob_to_kzg_commitment() {
1005        let trusted_setup_file = Path::new("src/trusted_setup.txt");
1006        assert!(trusted_setup_file.exists());
1007        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file, 0).unwrap();
1008        let test_files: Vec<PathBuf> = glob::glob(BLOB_TO_KZG_COMMITMENT_TESTS)
1009            .unwrap()
1010            .map(Result::unwrap)
1011            .collect();
1012        assert!(!test_files.is_empty());
1013
1014        #[allow(unused_variables)]
1015        for (index, test_file) in test_files.iter().enumerate() {
1016            let yaml_data = fs::read_to_string(test_file).unwrap();
1017            let test: blob_to_kzg_commitment_test::Test = serde_yaml::from_str(&yaml_data).unwrap();
1018            let Ok(blob) = test.input.get_blob() else {
1019                assert!(test.get_output().is_none());
1020                continue;
1021            };
1022
1023            #[cfg(feature = "generate-fuzz-corpus")]
1024            {
1025                use std::{env, fs::File, io::Write};
1026                let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
1027                let dir_path = root_dir
1028                    .join("fuzz")
1029                    .join("corpus")
1030                    .join("fuzz_blob_to_kzg_commitment");
1031                fs::create_dir_all(&dir_path).unwrap();
1032                let file_path = dir_path.join(format!("data_{index}.bin"));
1033                let mut file = File::create(&file_path).unwrap();
1034                file.write_all(&blob.bytes).unwrap();
1035            }
1036
1037            match kzg_settings.blob_to_kzg_commitment(&blob) {
1038                Ok(res) => assert_eq!(res.bytes, test.get_output().unwrap().bytes),
1039                _ => assert!(test.get_output().is_none()),
1040            }
1041        }
1042    }
1043
1044    #[test]
1045    fn test_parse_kzg_trusted_setup() {
1046        let trusted_setup_file = Path::new("src/trusted_setup.txt");
1047        assert!(trusted_setup_file.exists());
1048        let trusted_setup = fs::read_to_string(trusted_setup_file).unwrap();
1049        let _ = KZGSettings::parse_kzg_trusted_setup(&trusted_setup, 0).unwrap();
1050    }
1051
1052    #[test]
1053    fn test_compute_kzg_proof() {
1054        let trusted_setup_file = Path::new("src/trusted_setup.txt");
1055        assert!(trusted_setup_file.exists());
1056        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file, 0).unwrap();
1057        let test_files: Vec<PathBuf> = glob::glob(COMPUTE_KZG_PROOF_TESTS)
1058            .unwrap()
1059            .map(Result::unwrap)
1060            .collect();
1061        assert!(!test_files.is_empty());
1062
1063        #[allow(unused_variables)]
1064        for (index, test_file) in test_files.iter().enumerate() {
1065            let yaml_data = fs::read_to_string(test_file).unwrap();
1066            let test: compute_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap();
1067            let (Ok(blob), Ok(z)) = (test.input.get_blob(), test.input.get_z()) else {
1068                assert!(test.get_output().is_none());
1069                continue;
1070            };
1071
1072            #[cfg(feature = "generate-fuzz-corpus")]
1073            {
1074                use std::{env, fs::File, io::Write};
1075                let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
1076                let dir_path = root_dir
1077                    .join("fuzz")
1078                    .join("corpus")
1079                    .join("fuzz_compute_kzg_proof");
1080                fs::create_dir_all(&dir_path).unwrap();
1081                let file_path = dir_path.join(format!("data_{index}.bin"));
1082                let mut file = File::create(&file_path).unwrap();
1083                file.write_all(&blob.bytes).unwrap();
1084                file.write_all(&z.bytes).unwrap();
1085            }
1086
1087            match kzg_settings.compute_kzg_proof(&blob, &z) {
1088                Ok((proof, y)) => {
1089                    assert_eq!(proof.bytes, test.get_output().unwrap().0.bytes);
1090                    assert_eq!(y.bytes, test.get_output().unwrap().1.bytes);
1091                }
1092                _ => assert!(test.get_output().is_none()),
1093            }
1094        }
1095    }
1096
1097    #[test]
1098    fn test_compute_blob_kzg_proof() {
1099        let trusted_setup_file = Path::new("src/trusted_setup.txt");
1100        assert!(trusted_setup_file.exists());
1101        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file, 0).unwrap();
1102        let test_files: Vec<PathBuf> = glob::glob(COMPUTE_BLOB_KZG_PROOF_TESTS)
1103            .unwrap()
1104            .map(Result::unwrap)
1105            .collect();
1106        assert!(!test_files.is_empty());
1107
1108        #[allow(unused_variables)]
1109        for (index, test_file) in test_files.iter().enumerate() {
1110            let yaml_data = fs::read_to_string(test_file).unwrap();
1111            let test: compute_blob_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap();
1112            let (Ok(blob), Ok(commitment)) = (test.input.get_blob(), test.input.get_commitment())
1113            else {
1114                assert!(test.get_output().is_none());
1115                continue;
1116            };
1117
1118            #[cfg(feature = "generate-fuzz-corpus")]
1119            {
1120                use std::{env, fs::File, io::Write};
1121                let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
1122                let dir_path = root_dir
1123                    .join("fuzz")
1124                    .join("corpus")
1125                    .join("fuzz_compute_blob_kzg_proof");
1126                fs::create_dir_all(&dir_path).unwrap();
1127                let file_path = dir_path.join(format!("data_{index}.bin"));
1128                let mut file = File::create(&file_path).unwrap();
1129                file.write_all(&blob.bytes).unwrap();
1130                file.write_all(&commitment.bytes).unwrap();
1131            }
1132
1133            match kzg_settings.compute_blob_kzg_proof(&blob, &commitment) {
1134                Ok(res) => assert_eq!(res.bytes, test.get_output().unwrap().bytes),
1135                _ => assert!(test.get_output().is_none()),
1136            }
1137        }
1138    }
1139
1140    #[test]
1141    fn test_verify_kzg_proof() {
1142        let trusted_setup_file = Path::new("src/trusted_setup.txt");
1143        assert!(trusted_setup_file.exists());
1144        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file, 0).unwrap();
1145        let test_files: Vec<PathBuf> = glob::glob(VERIFY_KZG_PROOF_TESTS)
1146            .unwrap()
1147            .map(Result::unwrap)
1148            .collect();
1149        assert!(!test_files.is_empty());
1150
1151        #[allow(unused_variables)]
1152        for (index, test_file) in test_files.iter().enumerate() {
1153            let yaml_data = fs::read_to_string(test_file).unwrap();
1154            let test: verify_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap();
1155            let (Ok(commitment), Ok(z), Ok(y), Ok(proof)) = (
1156                test.input.get_commitment(),
1157                test.input.get_z(),
1158                test.input.get_y(),
1159                test.input.get_proof(),
1160            ) else {
1161                assert!(test.get_output().is_none());
1162                continue;
1163            };
1164
1165            #[cfg(feature = "generate-fuzz-corpus")]
1166            {
1167                use std::{env, fs::File, io::Write};
1168                let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
1169                let dir_path = root_dir
1170                    .join("fuzz")
1171                    .join("corpus")
1172                    .join("fuzz_verify_kzg_proof");
1173                fs::create_dir_all(&dir_path).unwrap();
1174                let file_path = dir_path.join(format!("data_{index}.bin"));
1175                let mut file = File::create(&file_path).unwrap();
1176                file.write_all(&commitment.bytes).unwrap();
1177                file.write_all(&z.bytes).unwrap();
1178                file.write_all(&y.bytes).unwrap();
1179                file.write_all(&proof.bytes).unwrap();
1180            }
1181
1182            match kzg_settings.verify_kzg_proof(&commitment, &z, &y, &proof) {
1183                Ok(res) => assert_eq!(res, test.get_output().unwrap()),
1184                _ => assert!(test.get_output().is_none()),
1185            }
1186        }
1187    }
1188
1189    #[test]
1190    fn test_verify_blob_kzg_proof() {
1191        let trusted_setup_file = Path::new("src/trusted_setup.txt");
1192        assert!(trusted_setup_file.exists());
1193        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file, 0).unwrap();
1194        let test_files: Vec<PathBuf> = glob::glob(VERIFY_BLOB_KZG_PROOF_TESTS)
1195            .unwrap()
1196            .map(Result::unwrap)
1197            .collect();
1198        assert!(!test_files.is_empty());
1199
1200        #[allow(unused_variables)]
1201        for (index, test_file) in test_files.iter().enumerate() {
1202            let yaml_data = fs::read_to_string(test_file).unwrap();
1203            let test: verify_blob_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap();
1204            let (Ok(blob), Ok(commitment), Ok(proof)) = (
1205                test.input.get_blob(),
1206                test.input.get_commitment(),
1207                test.input.get_proof(),
1208            ) else {
1209                assert!(test.get_output().is_none());
1210                continue;
1211            };
1212
1213            #[cfg(feature = "generate-fuzz-corpus")]
1214            {
1215                use std::{env, fs::File, io::Write};
1216                let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
1217                let dir_path = root_dir
1218                    .join("fuzz")
1219                    .join("corpus")
1220                    .join("fuzz_verify_blob_kzg_proof");
1221                fs::create_dir_all(&dir_path).unwrap();
1222                let file_path = dir_path.join(format!("data_{index}.bin"));
1223                let mut file = File::create(&file_path).unwrap();
1224                file.write_all(&blob.bytes).unwrap();
1225                file.write_all(&commitment.bytes).unwrap();
1226                file.write_all(&proof.bytes).unwrap();
1227            }
1228
1229            match kzg_settings.verify_blob_kzg_proof(&blob, &commitment, &proof) {
1230                Ok(res) => assert_eq!(res, test.get_output().unwrap()),
1231                _ => assert!(test.get_output().is_none()),
1232            }
1233        }
1234    }
1235
1236    #[test]
1237    fn test_verify_blob_kzg_proof_batch() {
1238        let trusted_setup_file = Path::new("src/trusted_setup.txt");
1239        assert!(trusted_setup_file.exists());
1240        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file, 0).unwrap();
1241        let test_files: Vec<PathBuf> = glob::glob(VERIFY_BLOB_KZG_PROOF_BATCH_TESTS)
1242            .unwrap()
1243            .map(Result::unwrap)
1244            .collect();
1245        assert!(!test_files.is_empty());
1246
1247        #[allow(unused_variables)]
1248        for (index, test_file) in test_files.iter().enumerate() {
1249            let yaml_data = fs::read_to_string(test_file).unwrap();
1250            let test: verify_blob_kzg_proof_batch::Test = serde_yaml::from_str(&yaml_data).unwrap();
1251            let (Ok(blobs), Ok(commitments), Ok(proofs)) = (
1252                test.input.get_blobs(),
1253                test.input.get_commitments(),
1254                test.input.get_proofs(),
1255            ) else {
1256                assert!(test.get_output().is_none());
1257                continue;
1258            };
1259
1260            #[cfg(feature = "generate-fuzz-corpus")]
1261            {
1262                use std::{env, fs::File, io::Write};
1263                let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
1264                let dir_path = root_dir
1265                    .join("fuzz")
1266                    .join("corpus")
1267                    .join("fuzz_verify_blob_kzg_proof_batch");
1268                fs::create_dir_all(&dir_path).unwrap();
1269                let file_path = dir_path.join(format!("data_{index}.bin"));
1270                let mut file = File::create(&file_path).unwrap();
1271                for blob in &blobs {
1272                    file.write_all(&blob.bytes).unwrap();
1273                }
1274                for commitment in &commitments {
1275                    file.write_all(&commitment.bytes).unwrap();
1276                }
1277                for proof in &proofs {
1278                    file.write_all(&proof.bytes).unwrap();
1279                }
1280            }
1281
1282            match kzg_settings.verify_blob_kzg_proof_batch(&blobs, &commitments, &proofs) {
1283                Ok(res) => assert_eq!(res, test.get_output().unwrap()),
1284                _ => assert!(test.get_output().is_none()),
1285            }
1286        }
1287    }
1288
1289    #[test]
1290    fn test_compute_cells() {
1291        let trusted_setup_file = Path::new("src/trusted_setup.txt");
1292        assert!(trusted_setup_file.exists());
1293        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file, 0).unwrap();
1294        let test_files: Vec<PathBuf> = glob::glob(COMPUTE_CELLS_TESTS)
1295            .unwrap()
1296            .map(Result::unwrap)
1297            .collect();
1298        assert!(!test_files.is_empty());
1299
1300        #[allow(unused_variables)]
1301        for (index, test_file) in test_files.iter().enumerate() {
1302            let yaml_data = fs::read_to_string(test_file).unwrap();
1303            let test: compute_cells::Test = serde_yaml::from_str(&yaml_data).unwrap();
1304            let Ok(blob) = test.input.get_blob() else {
1305                assert!(test.get_output().is_none());
1306                continue;
1307            };
1308
1309            #[cfg(feature = "generate-fuzz-corpus")]
1310            {
1311                use std::{env, fs::File, io::Write};
1312                let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
1313                let dir_path = root_dir
1314                    .join("fuzz")
1315                    .join("corpus")
1316                    .join("fuzz_compute_cells");
1317                fs::create_dir_all(&dir_path).unwrap();
1318                let file_path = dir_path.join(format!("data_{index}.bin"));
1319                let mut file = File::create(&file_path).unwrap();
1320                file.write_all(&blob.bytes).unwrap();
1321            }
1322
1323            match kzg_settings.compute_cells(&blob) {
1324                Ok(res) => assert_eq!(res.as_slice(), test.get_output().unwrap()),
1325                _ => assert!(test.get_output().is_none()),
1326            }
1327        }
1328    }
1329
1330    #[test]
1331    fn test_compute_cells_and_kzg_proofs() {
1332        let trusted_setup_file = Path::new("src/trusted_setup.txt");
1333        assert!(trusted_setup_file.exists());
1334        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file, 0).unwrap();
1335        let test_files: Vec<PathBuf> = glob::glob(COMPUTE_CELLS_AND_KZG_PROOFS_TESTS)
1336            .unwrap()
1337            .map(Result::unwrap)
1338            .collect();
1339        assert!(!test_files.is_empty());
1340
1341        #[allow(unused_variables)]
1342        for (index, test_file) in test_files.iter().enumerate() {
1343            let yaml_data = fs::read_to_string(test_file).unwrap();
1344            let test: compute_cells_and_kzg_proofs::Test =
1345                serde_yaml::from_str(&yaml_data).unwrap();
1346            let Ok(blob) = test.input.get_blob() else {
1347                assert!(test.get_output().is_none());
1348                continue;
1349            };
1350
1351            #[cfg(feature = "generate-fuzz-corpus")]
1352            {
1353                use std::{env, fs::File, io::Write};
1354                let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
1355                let dir_path = root_dir
1356                    .join("fuzz")
1357                    .join("corpus")
1358                    .join("fuzz_compute_cells_and_kzg_proofs");
1359                fs::create_dir_all(&dir_path).unwrap();
1360                let file_path = dir_path.join(format!("data_{index}.bin"));
1361                let mut file = File::create(&file_path).unwrap();
1362                file.write_all(&blob.bytes).unwrap();
1363            }
1364
1365            match kzg_settings.compute_cells_and_kzg_proofs(&blob) {
1366                Ok((cells, proofs)) => {
1367                    let (expected_cells, expected_proofs) = test.get_output().unwrap();
1368                    assert_eq!(cells.as_slice(), expected_cells);
1369                    let proofs_as_bytes: Vec<Bytes48> =
1370                        proofs.iter().map(|p| p.to_bytes()).collect();
1371                    assert_eq!(proofs_as_bytes, expected_proofs);
1372                }
1373                _ => assert!(test.get_output().is_none()),
1374            }
1375        }
1376    }
1377
1378    #[test]
1379    fn test_recover_cells_and_kzg_proofs() {
1380        let trusted_setup_file = Path::new("src/trusted_setup.txt");
1381        assert!(trusted_setup_file.exists());
1382        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file, 0).unwrap();
1383        let test_files: Vec<PathBuf> = glob::glob(RECOVER_CELLS_AND_KZG_PROOFS_TESTS)
1384            .unwrap()
1385            .map(Result::unwrap)
1386            .collect();
1387        assert!(!test_files.is_empty());
1388
1389        #[allow(unused_variables)]
1390        for (index, test_file) in test_files.iter().enumerate() {
1391            let yaml_data = fs::read_to_string(test_file).unwrap();
1392            let test: recover_cells_and_kzg_proofs::Test =
1393                serde_yaml::from_str(&yaml_data).unwrap();
1394            let (Ok(cell_indices), Ok(cells)) =
1395                (test.input.get_cell_indices(), test.input.get_cells())
1396            else {
1397                assert!(test.get_output().is_none());
1398                continue;
1399            };
1400
1401            #[cfg(feature = "generate-fuzz-corpus")]
1402            {
1403                use std::{env, fs::File, io::Write};
1404                let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
1405                let dir_path = root_dir
1406                    .join("fuzz")
1407                    .join("corpus")
1408                    .join("fuzz_recover_cells_and_kzg_proofs");
1409                fs::create_dir_all(&dir_path).unwrap();
1410                let file_path = dir_path.join(format!("data_{index}.bin"));
1411                let mut file = File::create(&file_path).unwrap();
1412                for cell_index in &cell_indices {
1413                    file.write_all(&cell_index.to_le_bytes()).unwrap();
1414                }
1415                for cell in &cells {
1416                    file.write_all(&cell.bytes).unwrap();
1417                }
1418            }
1419
1420            match kzg_settings.recover_cells_and_kzg_proofs(&cell_indices, &cells) {
1421                Ok((recovered_cells, recovered_proofs)) => {
1422                    let (expected_cells, expected_proofs) = test.get_output().unwrap();
1423                    assert_eq!(recovered_cells.as_slice(), expected_cells);
1424                    let proofs_as_bytes: Vec<Bytes48> =
1425                        recovered_proofs.iter().map(|p| p.to_bytes()).collect();
1426                    assert_eq!(proofs_as_bytes, expected_proofs);
1427                }
1428                _ => assert!(test.get_output().is_none()),
1429            }
1430        }
1431    }
1432
1433    #[test]
1434    fn test_verify_cell_kzg_proof_batch() {
1435        let trusted_setup_file = Path::new("src/trusted_setup.txt");
1436        assert!(trusted_setup_file.exists());
1437        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file, 0).unwrap();
1438        let test_files: Vec<PathBuf> = glob::glob(VERIFY_CELL_KZG_PROOF_BATCH_TESTS)
1439            .unwrap()
1440            .map(Result::unwrap)
1441            .collect();
1442        assert!(!test_files.is_empty());
1443
1444        #[allow(unused_variables)]
1445        for (index, test_file) in test_files.iter().enumerate() {
1446            let yaml_data = fs::read_to_string(test_file).unwrap();
1447            let test: verify_cell_kzg_proof_batch::Test = serde_yaml::from_str(&yaml_data).unwrap();
1448            let (Ok(commitments), Ok(cell_indices), Ok(cells), Ok(proofs)) = (
1449                test.input.get_commitments(),
1450                test.input.get_cell_indices(),
1451                test.input.get_cells(),
1452                test.input.get_proofs(),
1453            ) else {
1454                assert!(test.get_output().is_none());
1455                continue;
1456            };
1457
1458            #[cfg(feature = "generate-fuzz-corpus")]
1459            {
1460                use std::{env, fs::File, io::Write};
1461                let root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
1462                let dir_path = root_dir
1463                    .join("fuzz")
1464                    .join("corpus")
1465                    .join("fuzz_verify_cell_kzg_proof_batch");
1466                fs::create_dir_all(&dir_path).unwrap();
1467                let file_path = dir_path.join(format!("data_{index}.bin"));
1468                let mut file = File::create(&file_path).unwrap();
1469                for commitment in &commitments {
1470                    file.write_all(&commitment.bytes).unwrap();
1471                }
1472                for cell_index in &cell_indices {
1473                    file.write_all(&cell_index.to_le_bytes()).unwrap();
1474                }
1475                for cell in &cells {
1476                    file.write_all(&cell.bytes).unwrap();
1477                }
1478                for proof in &proofs {
1479                    file.write_all(&proof.bytes).unwrap();
1480                }
1481            }
1482
1483            match kzg_settings.verify_cell_kzg_proof_batch(
1484                &commitments,
1485                &cell_indices,
1486                &cells,
1487                &proofs,
1488            ) {
1489                Ok(res) => assert_eq!(res, test.get_output().unwrap()),
1490                _ => assert!(test.get_output().is_none()),
1491            }
1492        }
1493    }
1494}