diff --git a/Cargo.toml b/Cargo.toml index 41fc13e..14ce3bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,10 @@ serde_yaml = "0.9.25" rand = "0.8.5" [features] -default = ["rand"] +default = [] rand = ["dep:rand"] [[bench]] name = "kzg" -harness = false \ No newline at end of file +harness = false +required-features = ["rand"] \ No newline at end of file diff --git a/benches/kzg.rs b/benches/kzg.rs index e041753..512dcca 100644 --- a/benches/kzg.rs +++ b/benches/kzg.rs @@ -1,6 +1,6 @@ use kateth::{ blob::Blob, - kzg::{Commitment, Proof, Setup}, + kzg::{Bytes48, Setup}, }; use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput}; @@ -14,17 +14,17 @@ pub fn benchmark(c: &mut Criterion) { let max_batch_size = *batch_sizes.last().unwrap(); let mut rng = thread_rng(); - let blobs: Vec> = (0..max_batch_size) - .map(|_| Blob::random(&mut rng)) + let blobs: Vec> = (0..max_batch_size) + .map(|_| Blob::<4096>::random(&mut rng).to_bytes()) .collect(); - let commitments: Vec = blobs + let commitments: Vec = blobs .iter() - .map(|blob| kzg.blob_to_commitment(blob)) + .map(|blob| kzg.blob_to_commitment(blob).unwrap().serialize()) .collect(); - let proofs: Vec = blobs + let proofs: Vec = blobs .iter() .zip(commitments.iter()) - .map(|(blob, commitment)| kzg.blob_proof(blob, commitment)) + .map(|(blob, commitment)| kzg.blob_proof(blob, commitment).unwrap().serialize()) .collect(); c.bench_function("blob to kzg commitment", |b| { diff --git a/src/blob.rs b/src/blob.rs index 4779f87..3fe8dc8 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -3,6 +3,7 @@ use crate::{ kzg::{Commitment, Polynomial, Proof, Setup}, }; +#[derive(Clone, Copy, Debug)] pub enum Error { InvalidFieldElement, InvalidLen, @@ -35,6 +36,15 @@ impl Blob { Ok(Self { elements }) } + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::with_capacity(Self::BYTES); + for element in self.elements.iter() { + let element = element.to_be_bytes(); + bytes.extend_from_slice(&element); + } + bytes + } + pub(crate) fn commitment(&self, setup: &Setup) -> Commitment { let lincomb = P1::lincomb_pippenger(setup.g1_lagrange_brp.as_slice(), self.elements.as_slice()); diff --git a/src/kzg/mod.rs b/src/kzg/mod.rs index f66e94b..d405993 100644 --- a/src/kzg/mod.rs +++ b/src/kzg/mod.rs @@ -1,15 +1,35 @@ -use crate::bls; +use crate::{blob, bls}; mod poly; mod setup; +#[cfg(test)] +mod spec; + pub type Proof = bls::P1; pub type Commitment = bls::P1; +pub type Bytes32 = [u8; 32]; +pub type Bytes48 = [u8; 48]; + +#[derive(Clone, Copy, Debug)] pub enum Error { + Blob(blob::Error), Bls(bls::Error), } +impl From for Error { + fn from(value: blob::Error) -> Self { + Self::Blob(value) + } +} + +impl From for Error { + fn from(value: bls::Error) -> Self { + Self::Bls(value) + } +} + pub(crate) use poly::Polynomial; pub use setup::Setup; diff --git a/src/kzg/setup.rs b/src/kzg/setup.rs index aec17be..ed44fec 100644 --- a/src/kzg/setup.rs +++ b/src/kzg/setup.rs @@ -4,9 +4,9 @@ use std::{ path::Path, }; -use super::{Commitment, Polynomial, Proof}; +use super::{Bytes32, Bytes48, Commitment, Error, Polynomial, Proof}; use crate::{ - blob::Blob, + blob::{Blob, Error as BlobError}, bls::{self, Decompress, ECGroupError, Error as BlsError, Fr, P1, P2}, math, }; @@ -89,7 +89,7 @@ impl Setup { }) } - pub fn verify_proof( + fn verify_proof_inner( &self, proof: &Proof, commitment: &Commitment, @@ -101,7 +101,26 @@ impl Setup { bls::verify_pairings(pairing1, pairing2) } - pub fn verify_proof_batch( + pub fn verify_proof( + &self, + proof: &Bytes48, + commitment: &Bytes48, + point: &Bytes32, + eval: &Bytes32, + ) -> Result { + let proof = Proof::decompress(proof).map_err(|err| Error::from(BlsError::ECGroup(err)))?; + let commitment = Commitment::decompress(commitment) + .map_err(|err| Error::from(BlsError::ECGroup(err)))?; + let point = + Fr::from_be_slice(point).map_err(|err| Error::from(BlsError::FiniteField(err)))?; + let eval = + Fr::from_be_slice(eval).map_err(|err| Error::from(BlsError::FiniteField(err)))?; + + let verified = self.verify_proof_inner(&proof, &commitment, &point, &eval); + Ok(verified) + } + + fn verify_proof_batch( &self, proofs: impl AsRef<[Proof]>, commitments: impl AsRef<[Commitment]>, @@ -149,15 +168,40 @@ impl Setup { ) } - pub fn blob_to_commitment(&self, blob: &Blob) -> Commitment { + fn blob_to_commitment_inner(&self, blob: &Blob) -> Commitment { blob.commitment(self) } - pub fn blob_proof(&self, blob: &Blob, commitment: &Commitment) -> Proof { + pub fn blob_to_commitment(&self, blob: impl AsRef<[u8]>) -> Result { + let blob = Blob::::from_slice(blob)?; + let commitment = self.blob_to_commitment_inner(&blob); + Ok(commitment) + } + + fn blob_proof_inner(&self, blob: &Blob, commitment: &Commitment) -> Proof { blob.proof(commitment, self) } - pub fn verify_blob_proof( + pub fn blob_proof(&self, blob: impl AsRef<[u8]>, commitment: &Bytes48) -> Result { + let blob: Blob = Blob::from_slice(blob).map_err(Error::from)?; + let commitment = Commitment::decompress(commitment) + .map_err(|err| Error::from(BlsError::ECGroup(err)))?; + let proof = self.blob_proof_inner(&blob, &commitment); + Ok(proof) + } + + pub fn proof(&self, blob: impl AsRef<[u8]>, point: &Bytes32) -> Result<(Proof, Fr), Error> { + let blob: Blob = Blob::from_slice(blob).map_err(Error::from)?; + let point = + Fr::from_be_slice(point).map_err(|err| Error::from(BlsError::FiniteField(err)))?; + + let poly = Polynomial(&blob.elements); + let (eval, proof) = poly.prove(point, self); + + Ok((proof, eval)) + } + + fn verify_blob_proof_inner( &self, blob: &Blob, commitment: &Commitment, @@ -166,10 +210,25 @@ impl Setup { let poly = Polynomial(&blob.elements); let challenge = blob.challenge(commitment); let eval = poly.evaluate(challenge, self); - self.verify_proof(proof, commitment, &challenge, &eval) + self.verify_proof_inner(proof, commitment, &challenge, &eval) } - pub fn verify_blob_proof_batch( + pub fn verify_blob_proof( + &self, + blob: impl AsRef<[u8]>, + commitment: &Bytes48, + proof: &Bytes48, + ) -> Result { + let blob: Blob = Blob::from_slice(blob).map_err(Error::from)?; + let commitment = Commitment::decompress(commitment) + .map_err(|err| Error::from(BlsError::ECGroup(err)))?; + let proof = Proof::decompress(proof).map_err(|err| Error::from(BlsError::ECGroup(err)))?; + + let verified = self.verify_blob_proof_inner(&blob, &commitment, &proof); + Ok(verified) + } + + fn verify_blob_proof_batch_inner( &self, blobs: impl AsRef<[Blob]>, commitments: impl AsRef<[Commitment]>, @@ -192,15 +251,47 @@ impl Setup { self.verify_proof_batch(proofs, commitments, challenges, evaluations) } + + pub fn verify_blob_proof_batch( + &self, + blobs: impl AsRef<[B]>, + commitments: impl AsRef<[Bytes48]>, + proofs: impl AsRef<[Bytes48]>, + ) -> Result + where + B: AsRef<[u8]>, + { + assert_eq!(blobs.as_ref().len(), commitments.as_ref().len()); + assert_eq!(commitments.as_ref().len(), proofs.as_ref().len()); + + let blobs: Result>, _> = + blobs.as_ref().iter().map(Blob::::from_slice).collect(); + let blobs = blobs.map_err(Error::from)?; + + let commitments: Result, _> = commitments + .as_ref() + .iter() + .map(Commitment::decompress) + .collect(); + let commitments = commitments.map_err(|err| Error::from(BlsError::ECGroup(err)))?; + + let proofs: Result, _> = + proofs.as_ref().iter().map(Proof::decompress).collect(); + let proofs = proofs.map_err(|err| Error::from(BlsError::ECGroup(err)))?; + + let verified = self.verify_blob_proof_batch_inner(blobs, commitments, proofs); + Ok(verified) + } } #[cfg(test)] mod tests { - use self::bls::Decompress; - use super::*; - use crate::blob::Blob; + use crate::{bls::Compress, kzg::spec::{ + BlobToCommitment, ComputeBlobProof, ComputeProof, VerifyBlobProof, VerifyBlobProofBatch, + VerifyProof, + }}; use std::{ fs::{self, File}, @@ -209,241 +300,9 @@ mod tests { sync::Arc, }; - use crate::bls::P1; - - use alloy_primitives::{Bytes, FixedBytes}; - const FIELD_ELEMENTS_PER_BLOB: usize = 4096; const SETUP_G2_LEN: usize = 65; - #[derive(serde::Deserialize, serde::Serialize)] - struct ComputeKzgProofInputUnchecked { - pub blob: Bytes, - pub z: Bytes, - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct ComputeKzgProofUnchecked { - input: ComputeKzgProofInputUnchecked, - output: Option<(FixedBytes<{ Proof::BYTES }>, FixedBytes<{ Fr::BYTES }>)>, - } - - struct ComputeKzgProofInput { - pub blob: Blob, - pub z: Fr, - } - - impl ComputeKzgProofInput { - pub fn from_unchecked(unchecked: ComputeKzgProofInputUnchecked) -> Result { - let blob = Blob::from_slice(unchecked.blob).map_err(|_| ())?; - match Fr::from_be_slice(unchecked.z) { - Ok(z) => Ok(ComputeKzgProofInput { blob, z }), - Err(_) => Err(()), - } - } - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct BlobToCommitmentInputUnchecked { - pub blob: Bytes, - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct BlobToCommitmentUnchecked { - input: BlobToCommitmentInputUnchecked, - output: Option>, - } - - struct BlobToCommitmentInput { - pub blob: Blob, - } - - impl BlobToCommitmentInput { - pub fn from_unchecked(unchecked: BlobToCommitmentInputUnchecked) -> Result { - let blob = Blob::from_slice(unchecked.blob).map_err(|_| ())?; - Ok(Self { blob }) - } - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct ComputeBlobKzgProofInputUnchecked { - blob: Bytes, - commitment: Bytes, - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct ComputeBlobKzgProofUnchecked { - input: ComputeBlobKzgProofInputUnchecked, - output: Option>, - } - - struct ComputeBlobKzgProofInput { - blob: Blob, - commitment: Commitment, - } - - impl ComputeBlobKzgProofInput { - pub fn from_unchecked(unchecked: ComputeBlobKzgProofInputUnchecked) -> Result { - let blob = Blob::from_slice(unchecked.blob).map_err(|_| ())?; - if unchecked.commitment.len() != Commitment::BYTES { - return Err(()); - } - let commitment = FixedBytes::<{ Commitment::BYTES }>::from_slice(&unchecked.commitment); - let commitment = Commitment::decompress(commitment).map_err(|_| ())?; - Ok(Self { blob, commitment }) - } - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct VerifyKzgProofUnchecked { - input: VerifyKzgProofInputUnchecked, - output: Option, - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct VerifyKzgProofInputUnchecked { - pub commitment: Bytes, - pub z: Bytes, - pub y: Bytes, - pub proof: Bytes, - } - - struct VerifyKzgProofInput { - pub commitment: Commitment, - pub z: Fr, - pub y: Fr, - pub proof: Proof, - } - - impl VerifyKzgProofInput { - pub fn from_unchecked(unchecked: VerifyKzgProofInputUnchecked) -> Result { - if unchecked.commitment.len() != Commitment::BYTES { - return Err(()); - } - let commitment = FixedBytes::<{ Commitment::BYTES }>::from_slice(&unchecked.commitment); - let commitment = Commitment::decompress(commitment).map_err(|_| ())?; - - let z = Fr::from_be_slice(unchecked.z).map_err(|_| ())?; - let y = Fr::from_be_slice(unchecked.y).map_err(|_| ())?; - - if unchecked.proof.len() != Proof::BYTES { - return Err(()); - } - let proof = FixedBytes::<{ Proof::BYTES }>::from_slice(&unchecked.proof); - let proof = Proof::decompress(proof).map_err(|_| ())?; - - Ok(Self { - commitment, - z, - y, - proof, - }) - } - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct VerifyBlobKzgProofUnchecked { - input: VerifyBlobKzgProofInputUnchecked, - output: Option, - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct VerifyBlobKzgProofInputUnchecked { - pub blob: Bytes, - pub commitment: Bytes, - pub proof: Bytes, - } - - struct VerifyBlobKzgProofInput { - pub blob: Blob, - pub commitment: Commitment, - pub proof: Proof, - } - - impl VerifyBlobKzgProofInput { - pub fn from_unchecked(unchecked: VerifyBlobKzgProofInputUnchecked) -> Result { - let blob = Blob::from_slice(unchecked.blob).map_err(|_| ())?; - if unchecked.commitment.len() != Commitment::BYTES { - return Err(()); - } - let commitment = FixedBytes::<{ Commitment::BYTES }>::from_slice(&unchecked.commitment); - let commitment = Commitment::decompress(commitment).map_err(|_| ())?; - if unchecked.proof.len() != Proof::BYTES { - return Err(()); - } - let proof = FixedBytes::<{ Proof::BYTES }>::from_slice(&unchecked.proof); - let proof = Proof::decompress(proof).map_err(|_| ())?; - Ok(Self { - blob, - commitment, - proof, - }) - } - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct VerifyBlobKzgProofBatchInputUnchecked { - blobs: Vec, - commitments: Vec, - proofs: Vec, - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct VerifyBlobKzgProofBatchUnchecked { - input: VerifyBlobKzgProofBatchInputUnchecked, - output: Option, - } - - struct VerifyBlobKzgProofBatchInput { - blobs: Vec>, - commitments: Vec, - proofs: Vec, - } - - impl VerifyBlobKzgProofBatchInput { - pub fn from_unchecked( - unchecked: VerifyBlobKzgProofBatchInputUnchecked, - ) -> Result { - if unchecked.blobs.len() != unchecked.commitments.len() - || unchecked.blobs.len() != unchecked.proofs.len() - { - return Err(()); - } - - let mut blobs = vec![]; - for blob in unchecked.blobs { - let blob = Blob::from_slice(blob).map_err(|_| ())?; - blobs.push(blob); - } - - let mut commitments = vec![]; - for commitment in unchecked.commitments { - if commitment.len() != Commitment::BYTES { - return Err(()); - } - let commitment = FixedBytes::<{ Commitment::BYTES }>::from_slice(&commitment); - let commitment = Commitment::decompress(commitment).map_err(|_| ())?; - commitments.push(commitment); - } - - let mut proofs = vec![]; - for proof in unchecked.proofs { - if proof.len() != Proof::BYTES { - return Err(()); - } - let proof = FixedBytes::<{ Proof::BYTES }>::from_slice(&proof); - let proof = Proof::decompress(proof).map_err(|_| ())?; - proofs.push(proof); - } - - Ok(Self { - blobs, - commitments, - proofs, - }) - } - } - fn setup() -> Setup { let path = format!("{}/trusted_setup_4096.json", env!("CARGO_MANIFEST_DIR")); let path = PathBuf::from(path); @@ -470,29 +329,24 @@ mod tests { let setup = setup(); let setup = Arc::new(setup); - let files = consensus_spec_test_files("compute_kzg_proof"); - - for file in files { + for file in consensus_spec_test_files("compute_kzg_proof") { let reader = BufReader::new(file); - let case: ComputeKzgProofUnchecked = serde_yaml::from_reader(reader).unwrap(); - - match ComputeKzgProofInput::from_unchecked(case.input) { - Ok(input) => { - let (proof, eval) = case.output.unwrap(); - let expected_eval = Fr::from_be_bytes(eval).unwrap(); - let expected_proof = P1::decompress(proof).unwrap(); - - let poly = Polynomial(&input.blob.elements); - let eval = poly.evaluate(input.z, &setup); - let (_eval, proof) = poly.prove(input.z, &setup); - - assert_eq!(eval, expected_eval); - assert_eq!(proof, expected_proof); - } - Err(_) => { - assert!(case.output.is_none()); - } - } + let case: ComputeProof = serde_yaml::from_reader(reader).unwrap(); + + let expected = case.output(); + let Some((blob, z)) = case.input() else { + assert!(expected.is_none()); + continue; + }; + let Ok((proof, y)) = setup.proof(blob, &z) else { + assert!(expected.is_none()); + continue; + }; + let (expected_proof, expected_y) = expected.unwrap(); + let mut proof_bytes = [0u8; Proof::BYTES]; + proof.compress(&mut proof_bytes).unwrap(); + assert_eq!(proof_bytes, expected_proof); + assert_eq!(y.to_be_bytes(), expected_y); } } @@ -501,26 +355,23 @@ mod tests { let setup = setup(); let setup = Arc::new(setup); - let files = consensus_spec_test_files("compute_blob_kzg_proof"); - - for file in files { + for file in consensus_spec_test_files("compute_blob_kzg_proof") { let reader = BufReader::new(file); - let case: ComputeBlobKzgProofUnchecked = serde_yaml::from_reader(reader).unwrap(); - - match ComputeBlobKzgProofInput::from_unchecked(case.input) { - Ok(input) => { - let proof = case.output.unwrap(); - let proof = P1::decompress(proof).unwrap(); - let expected_proof = Proof::from(proof); - - let proof = setup.blob_proof(&input.blob, &input.commitment); - - assert_eq!(proof, expected_proof); - } - Err(_) => { - assert!(case.output.is_none()); - } - } + let case: ComputeBlobProof = serde_yaml::from_reader(reader).unwrap(); + + let expected = case.output(); + let Some((blob, commitment)) = case.input() else { + assert!(expected.is_none()); + continue; + }; + let Ok(proof) = setup.blob_proof(&blob, &commitment) else { + assert!(expected.is_none()); + continue; + }; + let expected = expected.unwrap(); + let mut proof_bytes = [0u8; Proof::BYTES]; + proof.compress(&mut proof_bytes).unwrap(); + assert_eq!(proof_bytes, expected); } } @@ -530,26 +381,19 @@ mod tests { let setup = setup(); let setup = Arc::new(setup); - let files = consensus_spec_test_files("blob_to_kzg_commitment"); - - for file in files { + for file in consensus_spec_test_files("blob_to_kzg_commitment") { let reader = BufReader::new(file); - let case: BlobToCommitmentUnchecked = serde_yaml::from_reader(reader).unwrap(); - - match BlobToCommitmentInput::from_unchecked(case.input) { - Ok(input) => { - let comm = case.output.unwrap(); - let comm = P1::decompress(comm).unwrap(); - let expected_comm = Commitment::from(comm); - - let comm = input.blob.commitment(&setup); - - assert_eq!(comm, expected_comm); - } - Err(_) => { - assert!(case.output.is_none()); - } - } + let case: BlobToCommitment = serde_yaml::from_reader(reader).unwrap(); + + let expected = case.output(); + let Ok(commitment) = setup.blob_to_commitment(case.input()) else { + assert!(expected.is_none()); + continue; + }; + let expected = expected.unwrap(); + let mut commitment_bytes = [0u8; Commitment::BYTES]; + commitment.compress(&mut commitment_bytes).unwrap(); + assert_eq!(commitment_bytes, expected); } } @@ -561,18 +405,19 @@ mod tests { for file in consensus_spec_test_files("verify_kzg_proof") { let reader = BufReader::new(file); - let case: VerifyKzgProofUnchecked = serde_yaml::from_reader(reader).unwrap(); - - match VerifyKzgProofInput::from_unchecked(case.input) { - Ok(input) => { - let check = - setup.verify_proof(&input.proof, &input.commitment, &input.z, &input.y); - assert_eq!(check, case.output.unwrap()); - } - Err(_) => { - assert!(case.output.is_none()); - } - } + let case: VerifyProof = serde_yaml::from_reader(reader).unwrap(); + + let expected = case.output(); + let Some((commitment, z, y, proof)) = case.input() else { + assert!(expected.is_none()); + continue; + }; + let Ok(verified) = setup.verify_proof(&proof, &commitment, &z, &y) else { + assert!(expected.is_none()); + continue; + }; + let expected = expected.unwrap(); + assert_eq!(verified, expected); } } @@ -584,18 +429,19 @@ mod tests { for file in consensus_spec_test_files("verify_blob_kzg_proof") { let reader = BufReader::new(file); - let case: VerifyBlobKzgProofUnchecked = serde_yaml::from_reader(reader).unwrap(); - - match VerifyBlobKzgProofInput::from_unchecked(case.input) { - Ok(input) => { - let check = - setup.verify_blob_proof(&input.blob, &input.commitment, &input.proof); - assert_eq!(check, case.output.unwrap()); - } - Err(_) => { - assert!(case.output.is_none()); - } - } + let case: VerifyBlobProof = serde_yaml::from_reader(reader).unwrap(); + + let expected = case.output(); + let Some((blob, commitment, proof)) = case.input() else { + assert!(expected.is_none()); + continue; + }; + let Ok(verified) = setup.verify_blob_proof(&blob, &commitment, &proof) else { + assert!(expected.is_none()); + continue; + }; + let expected = expected.unwrap(); + assert_eq!(verified, expected); } } @@ -607,18 +453,19 @@ mod tests { for file in consensus_spec_test_files("verify_blob_kzg_proof_batch") { let reader = BufReader::new(file); - let case: VerifyBlobKzgProofBatchUnchecked = serde_yaml::from_reader(reader).unwrap(); - - match VerifyBlobKzgProofBatchInput::from_unchecked(case.input) { - Ok(input) => { - let check = - setup.verify_blob_proof_batch(input.blobs, input.commitments, input.proofs); - assert_eq!(check, case.output.unwrap()); - } - Err(_) => { - assert!(case.output.is_none()); - } - } + let case: VerifyBlobProofBatch = serde_yaml::from_reader(reader).unwrap(); + + let expected = case.output(); + let Some((blobs, commitments, proofs)) = case.input() else { + assert!(expected.is_none()); + continue; + }; + let Ok(verified) = setup.verify_blob_proof_batch(&blobs, &commitments, &proofs) else { + assert!(expected.is_none()); + continue; + }; + let expected = expected.unwrap(); + assert_eq!(verified, expected); } } } diff --git a/src/kzg/spec.rs b/src/kzg/spec.rs new file mode 100644 index 0000000..c6473f5 --- /dev/null +++ b/src/kzg/spec.rs @@ -0,0 +1,218 @@ +use alloy_primitives::{Bytes, FixedBytes}; +use serde::Deserialize; + +use crate::bls::{Fr, P1}; + +use super::{Bytes32, Bytes48}; + +fn bytes32_from_bytes(bytes: &Bytes) -> Option { + let bytes = FixedBytes::<{ Fr::BYTES }>::try_from(bytes.as_ref()).ok(); + bytes.map(Into::::into) +} + +fn bytes48_from_bytes(bytes: &Bytes) -> Option { + let bytes = FixedBytes::<{ P1::BYTES }>::try_from(bytes.as_ref()).ok()?; + Some(bytes.into()) +} + +#[derive(Deserialize)] +struct BlobToCommitmentInput { + blob: Bytes, +} + +#[derive(Deserialize)] +pub struct BlobToCommitment { + input: BlobToCommitmentInput, + output: Option, +} + +impl BlobToCommitment { + pub fn input(&self) -> Bytes { + self.input.blob.clone() + } + + pub fn output(&self) -> Option { + self.output.as_ref().and_then(bytes48_from_bytes) + } +} + +#[derive(Deserialize)] +struct ComputeBlobProofInput { + blob: Bytes, + commitment: Bytes, +} + +#[derive(Deserialize)] +pub struct ComputeBlobProof { + input: ComputeBlobProofInput, + output: Option, +} + +impl ComputeBlobProof { + fn commitment(&self) -> Option { + bytes48_from_bytes(&self.input.commitment) + } + + pub fn input(&self) -> Option<(Bytes, Bytes48)> { + Some(self.input.blob.clone()).zip(self.commitment()) + } + + pub fn output(&self) -> Option { + self.output.as_ref().and_then(bytes48_from_bytes) + } +} + +#[derive(Deserialize)] +struct ComputeProofInput { + blob: Bytes, + z: Bytes, +} + +#[derive(Deserialize)] +pub struct ComputeProof { + input: ComputeProofInput, + output: Option<(Bytes, Bytes)>, +} + +impl ComputeProof { + fn z(&self) -> Option { + bytes32_from_bytes(&self.input.z) + } + + pub fn input(&self) -> Option<(Bytes, Bytes32)> { + Some(self.input.blob.clone()).zip(self.z()) + } + + pub fn output(&self) -> Option<(Bytes48, Bytes32)> { + self.output.as_ref().and_then(|(proof, y)| { + let proof = bytes48_from_bytes(proof); + let y = bytes32_from_bytes(y); + proof.zip(y) + }) + } +} + +#[derive(Deserialize)] +struct VerifyBlobProofInput { + blob: Bytes, + commitment: Bytes, + proof: Bytes, +} + +#[derive(Deserialize)] +pub struct VerifyBlobProof { + input: VerifyBlobProofInput, + output: Option, +} + +impl VerifyBlobProof { + fn commitment(&self) -> Option { + bytes48_from_bytes(&self.input.commitment) + } + + fn proof(&self) -> Option { + bytes48_from_bytes(&self.input.proof) + } + + pub fn input(&self) -> Option<(Bytes, Bytes48, Bytes48)> { + match (self.commitment(), self.proof()) { + (Some(commitment), Some(proof)) => Some((self.input.blob.clone(), commitment, proof)), + _ => None, + } + } + + pub fn output(&self) -> Option { + self.output + } +} + +#[derive(Deserialize)] +struct VerifyProofInput { + commitment: Bytes, + z: Bytes, + y: Bytes, + proof: Bytes, +} + +#[derive(Deserialize)] +pub struct VerifyProof { + input: VerifyProofInput, + output: Option, +} + +impl VerifyProof { + fn commitment(&self) -> Option { + bytes48_from_bytes(&self.input.commitment) + } + + fn z(&self) -> Option { + bytes32_from_bytes(&self.input.z) + } + + fn y(&self) -> Option { + bytes32_from_bytes(&self.input.y) + } + + fn proof(&self) -> Option { + bytes48_from_bytes(&self.input.proof) + } + + pub fn input(&self) -> Option<(Bytes48, Bytes32, Bytes32, Bytes48)> { + match (self.commitment(), self.z(), self.y(), self.proof()) { + (Some(commitment), Some(z), Some(y), Some(proof)) => Some((commitment, z, y, proof)), + _ => None, + } + } + + pub fn output(&self) -> Option { + self.output + } +} + +#[derive(Deserialize)] +struct VerifyBlobProofBatchInput { + blobs: Vec, + commitments: Vec, + proofs: Vec, +} + +#[derive(Deserialize)] +pub struct VerifyBlobProofBatch { + input: VerifyBlobProofBatchInput, + output: Option, +} + +impl VerifyBlobProofBatch { + fn commitments(&self) -> Option> { + let commitments: Vec = self + .input + .commitments + .iter() + .filter_map(bytes48_from_bytes) + .collect(); + (commitments.len() == self.input.commitments.len()).then_some(commitments) + } + + fn proofs(&self) -> Option> { + let proofs: Vec = self + .input + .proofs + .iter() + .filter_map(bytes48_from_bytes) + .collect(); + (proofs.len() == self.input.proofs.len()).then_some(proofs) + } + + pub fn input(&self) -> Option<(Vec, Vec, Vec)> { + match (self.commitments(), self.proofs()) { + (Some(commitments), Some(proofs)) => (self.input.blobs.len() == commitments.len() + && commitments.len() == proofs.len()) + .then_some((self.input.blobs.clone(), commitments, proofs)), + _ => None, + } + } + + pub fn output(&self) -> Option { + self.output + } +}