Skip to content

Commit

Permalink
feat: add KZG methods that accept raw bytes (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobkaufmann authored Mar 7, 2024
1 parent 58b0a07 commit 2eb5170
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 110 deletions.
14 changes: 7 additions & 7 deletions benches/kzg.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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<Blob<4096>> = (0..max_batch_size)
.map(|_| Blob::random(&mut rng))
let blobs: Vec<Vec<u8>> = (0..max_batch_size)
.map(|_| Blob::<4096>::random(&mut rng).to_bytes())
.collect();
let commitments: Vec<Commitment> = blobs
let commitments: Vec<Bytes48> = blobs
.iter()
.map(|blob| kzg.blob_to_commitment(blob))
.map(|blob| kzg.blob_to_commitment(blob).unwrap().serialize())
.collect();
let proofs: Vec<Proof> = blobs
let proofs: Vec<Bytes48> = 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| {
Expand Down
10 changes: 10 additions & 0 deletions src/blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{
kzg::{Commitment, Polynomial, Proof, Setup},
};

#[derive(Clone, Copy, Debug)]
pub enum Error {
InvalidFieldElement,
InvalidLen,
Expand Down Expand Up @@ -35,6 +36,15 @@ impl<const N: usize> Blob<N> {
Ok(Self { elements })
}

pub fn to_bytes(&self) -> Vec<u8> {
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<const G2: usize>(&self, setup: &Setup<N, G2>) -> Commitment {
let lincomb =
P1::lincomb_pippenger(setup.g1_lagrange_brp.as_slice(), self.elements.as_slice());
Expand Down
6 changes: 3 additions & 3 deletions src/bls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,13 +355,13 @@ impl P1 {
pub const BITS: usize = 384;
pub const BYTES: usize = Self::BITS / 8;

pub fn deserialize(bytes: impl AsRef<[u8; Self::BYTES]>) -> Result<Self, ECGroupError> {
pub fn deserialize(bytes: &[u8; Self::BYTES]) -> Result<Self, ECGroupError> {
let mut affine = MaybeUninit::<blst_p1_affine>::uninit();
let mut out = MaybeUninit::<blst_p1>::uninit();
unsafe {
// NOTE: deserialize performs a curve check but not a subgroup check. if that changes,
// then we should encounter `unreachable` for `BLST_POINT_NOT_IN_GROUP` in tests.
match blst_p1_deserialize(affine.as_mut_ptr(), bytes.as_ref().as_ptr()) {
match blst_p1_deserialize(affine.as_mut_ptr(), bytes.as_ptr()) {
BLST_ERROR::BLST_SUCCESS => {}
BLST_ERROR::BLST_BAD_ENCODING => return Err(ECGroupError::InvalidEncoding),
BLST_ERROR::BLST_POINT_NOT_ON_CURVE => return Err(ECGroupError::NotOnCurve),
Expand Down Expand Up @@ -515,7 +515,7 @@ impl P2 {
pub const BITS: usize = 768;
pub const BYTES: usize = Self::BITS / 8;

pub fn deserialize(bytes: impl AsRef<[u8; Self::BYTES]>) -> Result<Self, ECGroupError> {
pub fn deserialize(bytes: &[u8; Self::BYTES]) -> Result<Self, ECGroupError> {
let mut affine = MaybeUninit::<blst_p2_affine>::uninit();
let mut out = MaybeUninit::<blst_p2>::uninit();
unsafe {
Expand Down
19 changes: 18 additions & 1 deletion src/kzg/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::bls;
use crate::{blob, bls};

mod poly;
mod setup;
Expand All @@ -9,10 +9,27 @@ 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<blob::Error> for Error {
fn from(value: blob::Error) -> Self {
Self::Blob(value)
}
}

impl From<bls::Error> for Error {
fn from(value: bls::Error) -> Self {
Self::Bls(value)
}
}

pub(crate) use poly::Polynomial;

pub use setup::Setup;
149 changes: 126 additions & 23 deletions src/kzg/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, ECGroupError, Error as BlsError, Fr, P1, P2},
math,
};
Expand Down Expand Up @@ -60,7 +60,7 @@ impl<const G1: usize, const G2: usize> Setup<G1, G2> {
// TODO: skip unnecessary allocation
let point = FixedBytes::<48>::from_slice(point);
let point =
P1::deserialize(point).map_err(|err| LoadSetupError::Bls(BlsError::from(err)))?;
P1::deserialize(&point).map_err(|err| LoadSetupError::Bls(BlsError::from(err)))?;
g1_lagrange[i] = point;
}
let g1_lagrange_brp = math::bit_reversal_permutation_boxed_array(g1_lagrange.as_slice());
Expand All @@ -75,7 +75,7 @@ impl<const G1: usize, const G2: usize> Setup<G1, G2> {
// TODO: skip unnecessary allocation
let point = FixedBytes::<96>::from_slice(point);
let point =
P2::deserialize(point).map_err(|err| LoadSetupError::Bls(BlsError::from(err)))?;
P2::deserialize(&point).map_err(|err| LoadSetupError::Bls(BlsError::from(err)))?;
g2_monomial[i] = point;
}

Expand All @@ -89,7 +89,7 @@ impl<const G1: usize, const G2: usize> Setup<G1, G2> {
})
}

pub fn verify_proof(
fn verify_proof_inner(
&self,
proof: &Proof,
commitment: &Commitment,
Expand All @@ -101,7 +101,26 @@ impl<const G1: usize, const G2: usize> Setup<G1, G2> {
bls::verify_pairings(pairing1, pairing2)
}

pub fn verify_proof_batch(
pub fn verify_proof(
&self,
proof: &Bytes48,
commitment: &Bytes48,
point: &Bytes32,
eval: &Bytes32,
) -> Result<bool, Error> {
let proof = Proof::deserialize(proof).map_err(|err| Error::from(BlsError::ECGroup(err)))?;
let commitment = Commitment::deserialize(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]>,
Expand Down Expand Up @@ -149,15 +168,40 @@ impl<const G1: usize, const G2: usize> Setup<G1, G2> {
)
}

pub fn blob_to_commitment(&self, blob: &Blob<G1>) -> Commitment {
fn blob_to_commitment_inner(&self, blob: &Blob<G1>) -> Commitment {
blob.commitment(self)
}

pub fn blob_proof(&self, blob: &Blob<G1>, commitment: &Commitment) -> Proof {
pub fn blob_to_commitment(&self, blob: impl AsRef<[u8]>) -> Result<Commitment, BlobError> {
let blob = Blob::<G1>::from_slice(blob)?;
let commitment = self.blob_to_commitment_inner(&blob);
Ok(commitment)
}

fn blob_proof_inner(&self, blob: &Blob<G1>, commitment: &Commitment) -> Proof {
blob.proof(commitment, self)
}

pub fn verify_blob_proof(
pub fn blob_proof(&self, blob: impl AsRef<[u8]>, commitment: &Bytes48) -> Result<Proof, Error> {
let blob: Blob<G1> = Blob::from_slice(blob).map_err(Error::from)?;
let commitment = Commitment::deserialize(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<G1> = 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<G1>,
commitment: &Commitment,
Expand All @@ -166,10 +210,25 @@ impl<const G1: usize, const G2: usize> Setup<G1, G2> {
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<bool, Error> {
let blob: Blob<G1> = Blob::from_slice(blob).map_err(Error::from)?;
let commitment = Commitment::deserialize(commitment)
.map_err(|err| Error::from(BlsError::ECGroup(err)))?;
let proof = Proof::deserialize(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<G1>]>,
commitments: impl AsRef<[Commitment]>,
Expand All @@ -192,6 +251,37 @@ impl<const G1: usize, const G2: usize> Setup<G1, G2> {

self.verify_proof_batch(proofs, commitments, challenges, evaluations)
}

pub fn verify_blob_proof_batch<B>(
&self,
blobs: impl AsRef<[B]>,
commitments: impl AsRef<[Bytes48]>,
proofs: impl AsRef<[Bytes48]>,
) -> Result<bool, Error>
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<Vec<Blob<G1>>, _> =
blobs.as_ref().iter().map(Blob::<G1>::from_slice).collect();
let blobs = blobs.map_err(Error::from)?;

let commitments: Result<Vec<Commitment>, _> = commitments
.as_ref()
.iter()
.map(Commitment::deserialize)
.collect();
let commitments = commitments.map_err(|err| Error::from(BlsError::ECGroup(err)))?;

let proofs: Result<Vec<Proof>, _> =
proofs.as_ref().iter().map(Proof::deserialize).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)]
Expand Down Expand Up @@ -248,11 +338,13 @@ mod tests {
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 poly = Polynomial(&blob.elements);
let (y, proof) = poly.prove(z, &setup);
assert_eq!(proof, expected_proof);
assert_eq!(y, expected_y);
assert_eq!(proof.serialize(), expected_proof);
assert_eq!(y.to_be_bytes(), expected_y);
}
}

Expand All @@ -270,9 +362,12 @@ mod tests {
assert!(expected.is_none());
continue;
};
let Ok(proof) = setup.blob_proof(&blob, &commitment) else {
assert!(expected.is_none());
continue;
};
let expected = expected.unwrap();
let proof = setup.blob_proof(&blob, &commitment);
assert_eq!(proof, expected);
assert_eq!(proof.serialize(), expected);
}
}

Expand All @@ -287,13 +382,12 @@ mod tests {
let case: BlobToCommitment = serde_yaml::from_reader(reader).unwrap();

let expected = case.output();
let Some(blob) = case.input() else {
let Ok(commitment) = setup.blob_to_commitment(case.input()) else {
assert!(expected.is_none());
continue;
};
let expected = expected.unwrap();
let commitment = setup.blob_to_commitment(&blob);
assert_eq!(commitment, expected);
assert_eq!(commitment.serialize(), expected);
}
}

Expand All @@ -312,8 +406,11 @@ mod tests {
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();
let verified = setup.verify_proof(&proof, &commitment, &z, &y);
assert_eq!(verified, expected);
}
}
Expand All @@ -333,8 +430,11 @@ mod tests {
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();
let verified = setup.verify_blob_proof(&blob, &commitment, &proof);
assert_eq!(verified, expected);
}
}
Expand All @@ -354,8 +454,11 @@ mod tests {
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();
let verified = setup.verify_blob_proof_batch(&blobs, &commitments, &proofs);
assert_eq!(verified, expected);
}
}
Expand Down
Loading

0 comments on commit 2eb5170

Please sign in to comment.