c_kzg/bindings/
mod.rs

1#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
2
3#[cfg(feature = "serde")]
4mod serde;
5#[cfg(test)]
6mod test_formats;
7
8include!("./generated.rs");
9
10use alloc::string::String;
11use alloc::vec::Vec;
12use core::ffi::CStr;
13use core::fmt;
14use core::mem::MaybeUninit;
15use core::ops::{Deref, DerefMut};
16
17#[cfg(feature = "std")]
18use alloc::ffi::CString;
19#[cfg(feature = "std")]
20use std::path::Path;
21
22pub const BYTES_PER_G1_POINT: usize = 48;
23pub const BYTES_PER_G2_POINT: usize = 96;
24
25/// Number of G1 points required for the kzg trusted setup.
26pub const NUM_G1_POINTS: usize = 4096;
27
28/// Number of G2 points required for the kzg trusted setup.
29/// 65 is fixed and is used for providing multiproofs up to 64 field elements.
30pub const NUM_G2_POINTS: usize = 65;
31
32/// A trusted (valid) KZG commitment.
33// NOTE: this is a type alias to the struct Bytes48, same as [`KZGProof`] in the C header files. To
34//       facilitate type safety: proofs and commitments should not be interchangeable, we use a
35//       custom implementation.
36#[repr(C)]
37pub struct KZGCommitment {
38    bytes: [u8; BYTES_PER_COMMITMENT],
39}
40
41/// A trusted (valid) KZG proof.
42// NOTE: this is a type alias to the struct Bytes48, same as [`KZGCommitment`] in the C header
43//       files. To facilitate type safety: proofs and commitments should not be interchangeable, we
44//       use a custom implementation.
45#[repr(C)]
46pub struct KZGProof {
47    bytes: [u8; BYTES_PER_PROOF],
48}
49
50#[derive(Debug)]
51pub enum Error {
52    /// Wrong number of bytes.
53    InvalidBytesLength(String),
54    /// The hex string is invalid.
55    InvalidHexFormat(String),
56    /// The KZG proof is invalid.
57    InvalidKzgProof(String),
58    /// The KZG commitment is invalid.
59    InvalidKzgCommitment(String),
60    /// The provided trusted setup is invalid.
61    InvalidTrustedSetup(String),
62    /// Paired arguments have different lengths.
63    MismatchLength(String),
64    /// Loading the trusted setup failed.
65    LoadingTrustedSetupFailed(KzgErrors),
66    /// The underlying c-kzg library returned an error.
67    CError(C_KZG_RET),
68}
69
70#[cfg(feature = "std")]
71impl std::error::Error for Error {}
72
73impl fmt::Display for Error {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        match self {
76            Self::InvalidBytesLength(s)
77            | Self::InvalidHexFormat(s)
78            | Self::InvalidKzgProof(s)
79            | Self::InvalidKzgCommitment(s)
80            | Self::InvalidTrustedSetup(s)
81            | Self::MismatchLength(s) => f.write_str(s),
82            Self::LoadingTrustedSetupFailed(s) => write!(f, "KzgErrors: {:?}", s),
83            Self::CError(s) => fmt::Debug::fmt(s, f),
84        }
85    }
86}
87
88impl From<KzgErrors> for Error {
89    fn from(e: KzgErrors) -> Self {
90        Error::LoadingTrustedSetupFailed(e)
91    }
92}
93
94#[derive(Debug)]
95pub enum KzgErrors {
96    /// Failed to get current directory.
97    FailedCurrentDirectory,
98    /// The specified path does not exist.
99    PathNotExists,
100    /// Problems related to I/O.
101    IOError,
102    /// Not a valid file.
103    NotValidFile,
104    /// File is not properly formatted.
105    FileFormatError,
106    /// Not able to parse to usize.
107    ParseError,
108    /// Number of points does not match what is expected.
109    MismatchedNumberOfPoints,
110}
111
112/// Converts a hex string (with or without the 0x prefix) to bytes.
113pub fn hex_to_bytes(hex_str: &str) -> Result<Vec<u8>, Error> {
114    let trimmed_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
115    hex::decode(trimmed_str)
116        .map_err(|e| Error::InvalidHexFormat(format!("Failed to decode hex: {}", e)))
117}
118
119/// Holds the parameters of a kzg trusted setup ceremony.
120impl KZGSettings {
121    /// Initializes a trusted setup from `FIELD_ELEMENTS_PER_BLOB` g1 points
122    /// and 65 g2 points in byte format.
123    pub fn load_trusted_setup(
124        g1_bytes: &[[u8; BYTES_PER_G1_POINT]],
125        g2_bytes: &[[u8; BYTES_PER_G2_POINT]],
126    ) -> Result<Self, Error> {
127        if g1_bytes.len() != FIELD_ELEMENTS_PER_BLOB {
128            return Err(Error::InvalidTrustedSetup(format!(
129                "Invalid number of g1 points in trusted setup. Expected {} got {}",
130                FIELD_ELEMENTS_PER_BLOB,
131                g1_bytes.len()
132            )));
133        }
134        if g2_bytes.len() != NUM_G2_POINTS {
135            return Err(Error::InvalidTrustedSetup(format!(
136                "Invalid number of g2 points in trusted setup. Expected {} got {}",
137                NUM_G2_POINTS,
138                g2_bytes.len()
139            )));
140        }
141        let mut kzg_settings = MaybeUninit::<KZGSettings>::uninit();
142        unsafe {
143            let res = load_trusted_setup(
144                kzg_settings.as_mut_ptr(),
145                g1_bytes.as_ptr().cast(),
146                g1_bytes.len(),
147                g2_bytes.as_ptr().cast(),
148                g2_bytes.len(),
149            );
150            if let C_KZG_RET::C_KZG_OK = res {
151                Ok(kzg_settings.assume_init())
152            } else {
153                Err(Error::InvalidTrustedSetup(format!(
154                    "Invalid trusted setup: {res:?}",
155                )))
156            }
157        }
158    }
159
160    /// Loads the trusted setup parameters from a file. The file format is as follows:
161    ///
162    /// FIELD_ELEMENTS_PER_BLOB
163    /// 65 # This is fixed and is used for providing multiproofs up to 64 field elements.
164    /// FIELD_ELEMENT_PER_BLOB g1 byte values
165    /// 65 g2 byte values
166    #[cfg(feature = "std")]
167    pub fn load_trusted_setup_file(file_path: &Path) -> Result<Self, Error> {
168        #[cfg(unix)]
169        let file_path_bytes = {
170            use std::os::unix::prelude::OsStrExt;
171            file_path.as_os_str().as_bytes()
172        };
173
174        #[cfg(windows)]
175        let file_path_bytes = file_path
176            .as_os_str()
177            .to_str()
178            .ok_or_else(|| Error::InvalidTrustedSetup("Unsupported non unicode file path".into()))?
179            .as_bytes();
180
181        let file_path = CString::new(file_path_bytes)
182            .map_err(|e| Error::InvalidTrustedSetup(format!("Invalid trusted setup file: {e}")))?;
183
184        Self::load_trusted_setup_file_inner(&file_path)
185    }
186
187    /// Parses the contents of a KZG trusted setup file into a KzgSettings.
188    pub fn parse_kzg_trusted_setup(trusted_setup: &str) -> Result<Self, Error> {
189        let mut lines = trusted_setup.lines();
190
191        // load number of points
192        let n_g1 = lines
193            .next()
194            .ok_or(KzgErrors::FileFormatError)?
195            .parse::<usize>()
196            .map_err(|_| KzgErrors::ParseError)?;
197        let n_g2 = lines
198            .next()
199            .ok_or(KzgErrors::FileFormatError)?
200            .parse::<usize>()
201            .map_err(|_| KzgErrors::ParseError)?;
202
203        if n_g1 != NUM_G1_POINTS {
204            return Err(KzgErrors::MismatchedNumberOfPoints.into());
205        }
206
207        if n_g2 != NUM_G2_POINTS {
208            return Err(KzgErrors::MismatchedNumberOfPoints.into());
209        }
210
211        // load g1 points
212        let mut g1_points = alloc::boxed::Box::new([[0; BYTES_PER_G1_POINT]; NUM_G1_POINTS]);
213        for bytes in g1_points.iter_mut() {
214            let line = lines.next().ok_or(KzgErrors::FileFormatError)?;
215            hex::decode_to_slice(line, bytes).map_err(|_| KzgErrors::ParseError)?;
216        }
217
218        // load g2 points
219        let mut g2_points = alloc::boxed::Box::new([[0; BYTES_PER_G2_POINT]; NUM_G2_POINTS]);
220        for bytes in g2_points.iter_mut() {
221            let line = lines.next().ok_or(KzgErrors::FileFormatError)?;
222            hex::decode_to_slice(line, bytes).map_err(|_| KzgErrors::ParseError)?;
223        }
224
225        if lines.next().is_some() {
226            return Err(KzgErrors::FileFormatError.into());
227        }
228
229        Self::load_trusted_setup(g1_points.as_ref(), g2_points.as_ref())
230    }
231
232    /// Loads the trusted setup parameters from a file. The file format is as follows:
233    ///
234    /// FIELD_ELEMENTS_PER_BLOB
235    /// 65 # This is fixed and is used for providing multiproofs up to 64 field elements.
236    /// FIELD_ELEMENT_PER_BLOB g1 byte values
237    /// 65 g2 byte values
238    #[cfg(not(feature = "std"))]
239    pub fn load_trusted_setup_file(file_path: &CStr) -> Result<Self, Error> {
240        Self::load_trusted_setup_file_inner(file_path)
241    }
242
243    /// Loads the trusted setup parameters from a file.
244    ///
245    /// Same as [`load_trusted_setup_file`](Self::load_trusted_setup_file)
246    #[cfg_attr(not(feature = "std"), doc = ", but takes a `CStr` instead of a `Path`")]
247    /// .
248    pub fn load_trusted_setup_file_inner(file_path: &CStr) -> Result<Self, Error> {
249        // SAFETY: `b"r\0"` is a valid null-terminated string.
250        const MODE: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"r\0") };
251
252        // SAFETY:
253        // - .as_ptr(): pointer is not dangling because file_path has not been dropped.
254        //    Usage or ptr: File will not be written to it by the c code.
255        let file_ptr = unsafe { libc::fopen(file_path.as_ptr(), MODE.as_ptr()) };
256        if file_ptr.is_null() {
257            #[cfg(not(feature = "std"))]
258            return Err(Error::InvalidTrustedSetup(format!(
259                "Failed to open trusted setup file {file_path:?}"
260            )));
261
262            #[cfg(feature = "std")]
263            return Err(Error::InvalidTrustedSetup(format!(
264                "Failed to open trusted setup file {file_path:?}: {}",
265                std::io::Error::last_os_error()
266            )));
267        }
268        let mut kzg_settings = MaybeUninit::<KZGSettings>::uninit();
269        let result = unsafe {
270            let res = load_trusted_setup_file(kzg_settings.as_mut_ptr(), file_ptr);
271            let _unchecked_close_result = libc::fclose(file_ptr);
272
273            if let C_KZG_RET::C_KZG_OK = res {
274                Ok(kzg_settings.assume_init())
275            } else {
276                Err(Error::InvalidTrustedSetup(format!(
277                    "Invalid trusted setup: {res:?}"
278                )))
279            }
280        };
281
282        result
283    }
284}
285
286impl Drop for KZGSettings {
287    fn drop(&mut self) {
288        unsafe { free_trusted_setup(self) }
289    }
290}
291
292impl Blob {
293    /// Creates a new blob from a byte array.
294    pub const fn new(bytes: [u8; BYTES_PER_BLOB]) -> Self {
295        Self { bytes }
296    }
297
298    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
299        if bytes.len() != BYTES_PER_BLOB {
300            return Err(Error::InvalidBytesLength(format!(
301                "Invalid byte length. Expected {} got {}",
302                BYTES_PER_BLOB,
303                bytes.len(),
304            )));
305        }
306        let mut new_bytes = [0; BYTES_PER_BLOB];
307        new_bytes.copy_from_slice(bytes);
308        Ok(Self::new(new_bytes))
309    }
310
311    pub fn from_hex(hex_str: &str) -> Result<Self, Error> {
312        Self::from_bytes(&hex_to_bytes(hex_str)?)
313    }
314}
315
316impl AsRef<[u8]> for Blob {
317    fn as_ref(&self) -> &[u8] {
318        &self.bytes
319    }
320}
321
322impl Bytes32 {
323    /// Creates a new instance from a byte array.
324    pub const fn new(bytes: [u8; 32]) -> Self {
325        Self { bytes }
326    }
327
328    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
329        if bytes.len() != 32 {
330            return Err(Error::InvalidBytesLength(format!(
331                "Invalid byte length. Expected {} got {}",
332                32,
333                bytes.len(),
334            )));
335        }
336        let mut new_bytes = [0; 32];
337        new_bytes.copy_from_slice(bytes);
338        Ok(Self::new(new_bytes))
339    }
340
341    pub fn from_hex(hex_str: &str) -> Result<Self, Error> {
342        Self::from_bytes(&hex_to_bytes(hex_str)?)
343    }
344}
345
346impl Bytes48 {
347    /// Creates a new instance from a byte array.
348    pub const fn new(bytes: [u8; 48]) -> Self {
349        Self { bytes }
350    }
351
352    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
353        if bytes.len() != 48 {
354            return Err(Error::InvalidBytesLength(format!(
355                "Invalid byte length. Expected {} got {}",
356                48,
357                bytes.len(),
358            )));
359        }
360        let mut new_bytes = [0; 48];
361        new_bytes.copy_from_slice(bytes);
362        Ok(Self::new(new_bytes))
363    }
364
365    pub fn from_hex(hex_str: &str) -> Result<Self, Error> {
366        Self::from_bytes(&hex_to_bytes(hex_str)?)
367    }
368
369    pub fn into_inner(self) -> [u8; 48] {
370        self.bytes
371    }
372}
373
374impl KZGProof {
375    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
376        if bytes.len() != BYTES_PER_PROOF {
377            return Err(Error::InvalidKzgProof(format!(
378                "Invalid byte length. Expected {} got {}",
379                BYTES_PER_PROOF,
380                bytes.len(),
381            )));
382        }
383        let mut proof_bytes = [0; BYTES_PER_PROOF];
384        proof_bytes.copy_from_slice(bytes);
385        Ok(Self { bytes: proof_bytes })
386    }
387
388    pub fn to_bytes(&self) -> Bytes48 {
389        Bytes48 { bytes: self.bytes }
390    }
391
392    pub fn as_hex_string(&self) -> String {
393        hex::encode(self.bytes)
394    }
395
396    pub fn compute_kzg_proof(
397        blob: &Blob,
398        z_bytes: &Bytes32,
399        kzg_settings: &KZGSettings,
400    ) -> Result<(Self, Bytes32), Error> {
401        let mut kzg_proof = MaybeUninit::<KZGProof>::uninit();
402        let mut y_out = MaybeUninit::<Bytes32>::uninit();
403        unsafe {
404            let res = compute_kzg_proof(
405                kzg_proof.as_mut_ptr(),
406                y_out.as_mut_ptr(),
407                blob,
408                z_bytes,
409                kzg_settings,
410            );
411            if let C_KZG_RET::C_KZG_OK = res {
412                Ok((kzg_proof.assume_init(), y_out.assume_init()))
413            } else {
414                Err(Error::CError(res))
415            }
416        }
417    }
418
419    pub fn compute_blob_kzg_proof(
420        blob: &Blob,
421        commitment_bytes: &Bytes48,
422        kzg_settings: &KZGSettings,
423    ) -> Result<Self, Error> {
424        let mut kzg_proof = MaybeUninit::<KZGProof>::uninit();
425        unsafe {
426            let res = compute_blob_kzg_proof(
427                kzg_proof.as_mut_ptr(),
428                blob,
429                commitment_bytes,
430                kzg_settings,
431            );
432            if let C_KZG_RET::C_KZG_OK = res {
433                Ok(kzg_proof.assume_init())
434            } else {
435                Err(Error::CError(res))
436            }
437        }
438    }
439
440    pub fn verify_kzg_proof(
441        commitment_bytes: &Bytes48,
442        z_bytes: &Bytes32,
443        y_bytes: &Bytes32,
444        proof_bytes: &Bytes48,
445        kzg_settings: &KZGSettings,
446    ) -> Result<bool, Error> {
447        let mut verified: MaybeUninit<bool> = MaybeUninit::uninit();
448        unsafe {
449            let res = verify_kzg_proof(
450                verified.as_mut_ptr(),
451                commitment_bytes,
452                z_bytes,
453                y_bytes,
454                proof_bytes,
455                kzg_settings,
456            );
457            if let C_KZG_RET::C_KZG_OK = res {
458                Ok(verified.assume_init())
459            } else {
460                Err(Error::CError(res))
461            }
462        }
463    }
464
465    pub fn verify_blob_kzg_proof(
466        blob: &Blob,
467        commitment_bytes: &Bytes48,
468        proof_bytes: &Bytes48,
469        kzg_settings: &KZGSettings,
470    ) -> Result<bool, Error> {
471        let mut verified: MaybeUninit<bool> = MaybeUninit::uninit();
472        unsafe {
473            let res = verify_blob_kzg_proof(
474                verified.as_mut_ptr(),
475                blob,
476                commitment_bytes,
477                proof_bytes,
478                kzg_settings,
479            );
480            if let C_KZG_RET::C_KZG_OK = res {
481                Ok(verified.assume_init())
482            } else {
483                Err(Error::CError(res))
484            }
485        }
486    }
487
488    pub fn verify_blob_kzg_proof_batch(
489        blobs: &[Blob],
490        commitments_bytes: &[Bytes48],
491        proofs_bytes: &[Bytes48],
492        kzg_settings: &KZGSettings,
493    ) -> Result<bool, Error> {
494        if blobs.len() != commitments_bytes.len() {
495            return Err(Error::MismatchLength(format!(
496                "There are {} blobs and {} commitments",
497                blobs.len(),
498                commitments_bytes.len()
499            )));
500        }
501        if blobs.len() != proofs_bytes.len() {
502            return Err(Error::MismatchLength(format!(
503                "There are {} blobs and {} proofs",
504                blobs.len(),
505                proofs_bytes.len()
506            )));
507        }
508        let mut verified: MaybeUninit<bool> = MaybeUninit::uninit();
509        unsafe {
510            let res = verify_blob_kzg_proof_batch(
511                verified.as_mut_ptr(),
512                blobs.as_ptr(),
513                commitments_bytes.as_ptr(),
514                proofs_bytes.as_ptr(),
515                blobs.len(),
516                kzg_settings,
517            );
518            if let C_KZG_RET::C_KZG_OK = res {
519                Ok(verified.assume_init())
520            } else {
521                Err(Error::CError(res))
522            }
523        }
524    }
525}
526
527impl KZGCommitment {
528    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
529        if bytes.len() != BYTES_PER_COMMITMENT {
530            return Err(Error::InvalidKzgCommitment(format!(
531                "Invalid byte length. Expected {} got {}",
532                BYTES_PER_PROOF,
533                bytes.len(),
534            )));
535        }
536        let mut commitment = [0; BYTES_PER_COMMITMENT];
537        commitment.copy_from_slice(bytes);
538        Ok(Self { bytes: commitment })
539    }
540
541    pub fn to_bytes(&self) -> Bytes48 {
542        Bytes48 { bytes: self.bytes }
543    }
544
545    pub fn as_hex_string(&self) -> String {
546        hex::encode(self.bytes)
547    }
548
549    pub fn blob_to_kzg_commitment(blob: &Blob, kzg_settings: &KZGSettings) -> Result<Self, Error> {
550        let mut kzg_commitment: MaybeUninit<KZGCommitment> = MaybeUninit::uninit();
551        unsafe {
552            let res = blob_to_kzg_commitment(kzg_commitment.as_mut_ptr(), blob, kzg_settings);
553            if let C_KZG_RET::C_KZG_OK = res {
554                Ok(kzg_commitment.assume_init())
555            } else {
556                Err(Error::CError(res))
557            }
558        }
559    }
560}
561
562impl From<[u8; BYTES_PER_COMMITMENT]> for KZGCommitment {
563    fn from(value: [u8; BYTES_PER_COMMITMENT]) -> Self {
564        Self { bytes: value }
565    }
566}
567
568impl From<[u8; BYTES_PER_PROOF]> for KZGProof {
569    fn from(value: [u8; BYTES_PER_PROOF]) -> Self {
570        Self { bytes: value }
571    }
572}
573
574impl From<[u8; BYTES_PER_BLOB]> for Blob {
575    fn from(value: [u8; BYTES_PER_BLOB]) -> Self {
576        Self { bytes: value }
577    }
578}
579
580impl From<[u8; 32]> for Bytes32 {
581    fn from(value: [u8; 32]) -> Self {
582        Self { bytes: value }
583    }
584}
585
586impl AsRef<[u8; 32]> for Bytes32 {
587    fn as_ref(&self) -> &[u8; 32] {
588        &self.bytes
589    }
590}
591
592impl From<[u8; 48]> for Bytes48 {
593    fn from(value: [u8; 48]) -> Self {
594        Self { bytes: value }
595    }
596}
597
598impl AsRef<[u8; 48]> for Bytes48 {
599    fn as_ref(&self) -> &[u8; 48] {
600        &self.bytes
601    }
602}
603
604impl Deref for Bytes32 {
605    type Target = [u8; 32];
606    fn deref(&self) -> &Self::Target {
607        &self.bytes
608    }
609}
610
611impl Deref for Bytes48 {
612    type Target = [u8; 48];
613    fn deref(&self) -> &Self::Target {
614        &self.bytes
615    }
616}
617
618impl DerefMut for Bytes48 {
619    fn deref_mut(&mut self) -> &mut Self::Target {
620        &mut self.bytes
621    }
622}
623
624impl Deref for Blob {
625    type Target = [u8; BYTES_PER_BLOB];
626    fn deref(&self) -> &Self::Target {
627        &self.bytes
628    }
629}
630
631impl DerefMut for Blob {
632    fn deref_mut(&mut self) -> &mut Self::Target {
633        &mut self.bytes
634    }
635}
636
637impl Clone for Blob {
638    fn clone(&self) -> Self {
639        Blob { bytes: self.bytes }
640    }
641}
642
643impl Deref for KZGProof {
644    type Target = [u8; BYTES_PER_PROOF];
645    fn deref(&self) -> &Self::Target {
646        &self.bytes
647    }
648}
649
650impl Deref for KZGCommitment {
651    type Target = [u8; BYTES_PER_COMMITMENT];
652    fn deref(&self) -> &Self::Target {
653        &self.bytes
654    }
655}
656
657/// Safety: The memory for `roots_of_unity` and `g1_values` and `g2_values` are only freed on
658/// calling `free_trusted_setup` which only happens when we drop the struct.
659unsafe impl Sync for KZGSettings {}
660unsafe impl Send for KZGSettings {}
661
662#[cfg(test)]
663#[allow(unused_imports, dead_code)]
664mod tests {
665    use super::*;
666    use rand::{rngs::ThreadRng, Rng};
667    use std::{fs, path::PathBuf};
668    use test_formats::{
669        blob_to_kzg_commitment_test, compute_blob_kzg_proof, compute_kzg_proof,
670        verify_blob_kzg_proof, verify_blob_kzg_proof_batch, verify_kzg_proof,
671    };
672
673    fn generate_random_blob(rng: &mut ThreadRng) -> Blob {
674        let mut arr = [0u8; BYTES_PER_BLOB];
675        rng.fill(&mut arr[..]);
676        // Ensure that the blob is canonical by ensuring that
677        // each field element contained in the blob is < BLS_MODULUS
678        for i in 0..FIELD_ELEMENTS_PER_BLOB {
679            arr[i * BYTES_PER_FIELD_ELEMENT] = 0;
680        }
681        arr.into()
682    }
683
684    fn test_simple(trusted_setup_file: &Path) {
685        let mut rng = rand::thread_rng();
686        assert!(trusted_setup_file.exists());
687        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap();
688
689        let num_blobs: usize = rng.gen_range(1..16);
690        let mut blobs: Vec<Blob> = (0..num_blobs)
691            .map(|_| generate_random_blob(&mut rng))
692            .collect();
693
694        let commitments: Vec<Bytes48> = blobs
695            .iter()
696            .map(|blob| KZGCommitment::blob_to_kzg_commitment(blob, &kzg_settings).unwrap())
697            .map(|commitment| commitment.to_bytes())
698            .collect();
699
700        let proofs: Vec<Bytes48> = blobs
701            .iter()
702            .zip(commitments.iter())
703            .map(|(blob, commitment)| {
704                KZGProof::compute_blob_kzg_proof(blob, commitment, &kzg_settings).unwrap()
705            })
706            .map(|proof| proof.to_bytes())
707            .collect();
708
709        assert!(KZGProof::verify_blob_kzg_proof_batch(
710            &blobs,
711            &commitments,
712            &proofs,
713            &kzg_settings
714        )
715        .unwrap());
716
717        blobs.pop();
718
719        let error =
720            KZGProof::verify_blob_kzg_proof_batch(&blobs, &commitments, &proofs, &kzg_settings)
721                .unwrap_err();
722        assert!(matches!(error, Error::MismatchLength(_)));
723
724        let incorrect_blob = generate_random_blob(&mut rng);
725        blobs.push(incorrect_blob);
726
727        assert!(!KZGProof::verify_blob_kzg_proof_batch(
728            &blobs,
729            &commitments,
730            &proofs,
731            &kzg_settings
732        )
733        .unwrap());
734    }
735
736    #[test]
737    fn test_end_to_end() {
738        let trusted_setup_file = Path::new("src/trusted_setup.txt");
739        test_simple(trusted_setup_file);
740    }
741
742    const BLOB_TO_KZG_COMMITMENT_TESTS: &str = "tests/blob_to_kzg_commitment/*/*/*";
743    const COMPUTE_KZG_PROOF_TESTS: &str = "tests/compute_kzg_proof/*/*/*";
744    const COMPUTE_BLOB_KZG_PROOF_TESTS: &str = "tests/compute_blob_kzg_proof/*/*/*";
745    const VERIFY_KZG_PROOF_TESTS: &str = "tests/verify_kzg_proof/*/*/*";
746    const VERIFY_BLOB_KZG_PROOF_TESTS: &str = "tests/verify_blob_kzg_proof/*/*/*";
747    const VERIFY_BLOB_KZG_PROOF_BATCH_TESTS: &str = "tests/verify_blob_kzg_proof_batch/*/*/*";
748
749    #[test]
750    fn test_blob_to_kzg_commitment() {
751        let trusted_setup_file = Path::new("src/trusted_setup.txt");
752        assert!(trusted_setup_file.exists());
753        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap();
754        let test_files: Vec<PathBuf> = glob::glob(BLOB_TO_KZG_COMMITMENT_TESTS)
755            .unwrap()
756            .map(Result::unwrap)
757            .collect();
758        assert!(!test_files.is_empty());
759
760        for test_file in test_files {
761            let yaml_data = fs::read_to_string(test_file).unwrap();
762            let test: blob_to_kzg_commitment_test::Test = serde_yaml::from_str(&yaml_data).unwrap();
763            let Ok(blob) = test.input.get_blob() else {
764                assert!(test.get_output().is_none());
765                continue;
766            };
767
768            match KZGCommitment::blob_to_kzg_commitment(&blob, &kzg_settings) {
769                Ok(res) => assert_eq!(res.bytes, test.get_output().unwrap().bytes),
770                _ => assert!(test.get_output().is_none()),
771            }
772        }
773    }
774
775    #[test]
776    fn test_parse_kzg_trusted_setup() {
777        let trusted_setup_file = Path::new("src/trusted_setup.txt");
778        assert!(trusted_setup_file.exists());
779        let trusted_setup = fs::read_to_string(trusted_setup_file).unwrap();
780        let _ = KZGSettings::parse_kzg_trusted_setup(&trusted_setup).unwrap();
781    }
782
783    #[test]
784    fn test_compute_kzg_proof() {
785        let trusted_setup_file = Path::new("src/trusted_setup.txt");
786        assert!(trusted_setup_file.exists());
787        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap();
788        let test_files: Vec<PathBuf> = glob::glob(COMPUTE_KZG_PROOF_TESTS)
789            .unwrap()
790            .map(Result::unwrap)
791            .collect();
792        assert!(!test_files.is_empty());
793
794        for test_file in test_files {
795            let yaml_data = fs::read_to_string(test_file).unwrap();
796            let test: compute_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap();
797            let (Ok(blob), Ok(z)) = (test.input.get_blob(), test.input.get_z()) else {
798                assert!(test.get_output().is_none());
799                continue;
800            };
801
802            match KZGProof::compute_kzg_proof(&blob, &z, &kzg_settings) {
803                Ok((proof, y)) => {
804                    assert_eq!(proof.bytes, test.get_output().unwrap().0.bytes);
805                    assert_eq!(y.bytes, test.get_output().unwrap().1.bytes);
806                }
807                _ => assert!(test.get_output().is_none()),
808            }
809        }
810    }
811
812    #[test]
813    fn test_compute_blob_kzg_proof() {
814        let trusted_setup_file = Path::new("src/trusted_setup.txt");
815        assert!(trusted_setup_file.exists());
816        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap();
817        let test_files: Vec<PathBuf> = glob::glob(COMPUTE_BLOB_KZG_PROOF_TESTS)
818            .unwrap()
819            .map(Result::unwrap)
820            .collect();
821        assert!(!test_files.is_empty());
822
823        for test_file in test_files {
824            let yaml_data = fs::read_to_string(test_file).unwrap();
825            let test: compute_blob_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap();
826            let (Ok(blob), Ok(commitment)) = (test.input.get_blob(), test.input.get_commitment())
827            else {
828                assert!(test.get_output().is_none());
829                continue;
830            };
831
832            match KZGProof::compute_blob_kzg_proof(&blob, &commitment, &kzg_settings) {
833                Ok(res) => assert_eq!(res.bytes, test.get_output().unwrap().bytes),
834                _ => assert!(test.get_output().is_none()),
835            }
836        }
837    }
838
839    #[test]
840    fn test_verify_kzg_proof() {
841        let trusted_setup_file = Path::new("src/trusted_setup.txt");
842        assert!(trusted_setup_file.exists());
843        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap();
844        let test_files: Vec<PathBuf> = glob::glob(VERIFY_KZG_PROOF_TESTS)
845            .unwrap()
846            .map(Result::unwrap)
847            .collect();
848        assert!(!test_files.is_empty());
849
850        for test_file in test_files {
851            let yaml_data = fs::read_to_string(test_file).unwrap();
852            let test: verify_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap();
853            let (Ok(commitment), Ok(z), Ok(y), Ok(proof)) = (
854                test.input.get_commitment(),
855                test.input.get_z(),
856                test.input.get_y(),
857                test.input.get_proof(),
858            ) else {
859                assert!(test.get_output().is_none());
860                continue;
861            };
862
863            match KZGProof::verify_kzg_proof(&commitment, &z, &y, &proof, &kzg_settings) {
864                Ok(res) => assert_eq!(res, test.get_output().unwrap()),
865                _ => assert!(test.get_output().is_none()),
866            }
867        }
868    }
869
870    #[test]
871    fn test_verify_blob_kzg_proof() {
872        let trusted_setup_file = Path::new("src/trusted_setup.txt");
873        assert!(trusted_setup_file.exists());
874        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap();
875        let test_files: Vec<PathBuf> = glob::glob(VERIFY_BLOB_KZG_PROOF_TESTS)
876            .unwrap()
877            .map(Result::unwrap)
878            .collect();
879        assert!(!test_files.is_empty());
880
881        for test_file in test_files {
882            let yaml_data = fs::read_to_string(test_file).unwrap();
883            let test: verify_blob_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap();
884            let (Ok(blob), Ok(commitment), Ok(proof)) = (
885                test.input.get_blob(),
886                test.input.get_commitment(),
887                test.input.get_proof(),
888            ) else {
889                assert!(test.get_output().is_none());
890                continue;
891            };
892
893            match KZGProof::verify_blob_kzg_proof(&blob, &commitment, &proof, &kzg_settings) {
894                Ok(res) => assert_eq!(res, test.get_output().unwrap()),
895                _ => assert!(test.get_output().is_none()),
896            }
897        }
898    }
899
900    #[test]
901    fn test_verify_blob_kzg_proof_batch() {
902        let trusted_setup_file = Path::new("src/trusted_setup.txt");
903        assert!(trusted_setup_file.exists());
904        let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap();
905        let test_files: Vec<PathBuf> = glob::glob(VERIFY_BLOB_KZG_PROOF_BATCH_TESTS)
906            .unwrap()
907            .map(Result::unwrap)
908            .collect();
909        assert!(!test_files.is_empty());
910
911        for test_file in test_files {
912            let yaml_data = fs::read_to_string(test_file).unwrap();
913            let test: verify_blob_kzg_proof_batch::Test = serde_yaml::from_str(&yaml_data).unwrap();
914            let (Ok(blobs), Ok(commitments), Ok(proofs)) = (
915                test.input.get_blobs(),
916                test.input.get_commitments(),
917                test.input.get_proofs(),
918            ) else {
919                assert!(test.get_output().is_none());
920                continue;
921            };
922
923            match KZGProof::verify_blob_kzg_proof_batch(
924                &blobs,
925                &commitments,
926                &proofs,
927                &kzg_settings,
928            ) {
929                Ok(res) => assert_eq!(res, test.get_output().unwrap()),
930                _ => assert!(test.get_output().is_none()),
931            }
932        }
933    }
934}