use super::{
errors::{KZG10Errors, PolyCommitSchemeError},
AggregateProof, Commitment, Proof,
};
use crate::{fft::Polynomial, transcript::TranscriptProtocol, util};
use dusk_bls12_381::{
multiscalar_mul::msm_variable_base, G1Affine, G1Projective, G2Affine, G2Prepared, Scalar,
};
use failure::Error;
use merlin::Transcript;
#[derive(Clone, Debug)]
pub struct OpeningKey {
pub g: G1Affine,
pub h: G2Affine,
pub beta_h: G2Affine,
pub prepared_h: G2Prepared,
pub prepared_beta_h: G2Prepared,
}
#[derive(Debug)]
pub struct CommitKey {
pub powers_of_g: Vec<G1Affine>,
}
impl CommitKey {
pub fn max_degree(&self) -> usize {
self.powers_of_g.len() - 1
}
pub fn truncate(&self, mut truncated_degree: usize) -> Result<CommitKey, Error> {
if truncated_degree == 1 {
truncated_degree += 1;
}
if truncated_degree == 0 {
return Err(PolyCommitSchemeError(KZG10Errors::TruncatedDegreeIsZero.into()).into());
}
if truncated_degree > self.max_degree() {
return Err(PolyCommitSchemeError(KZG10Errors::TruncatedDegreeTooLarge.into()).into());
}
let truncated_powers = Self {
powers_of_g: self.powers_of_g[..=truncated_degree].to_vec(),
};
Ok(truncated_powers)
}
fn check_commit_degree_is_within_bounds(&self, poly_degree: usize) -> Result<(), Error> {
check_degree_is_within_bounds(self.max_degree(), poly_degree)
}
pub fn commit(&self, polynomial: &Polynomial) -> Result<Commitment, Error> {
self.check_commit_degree_is_within_bounds(polynomial.degree())?;
let commitment = msm_variable_base(&self.powers_of_g, &polynomial.coeffs);
Ok(Commitment::from_projective(commitment))
}
pub fn compute_single_witness(&self, polynomial: &Polynomial, point: &Scalar) -> Polynomial {
polynomial.ruffini(*point)
}
pub(crate) fn compute_aggregate_witness(
&self,
polynomials: &[Polynomial],
point: &Scalar,
transcript: &mut Transcript,
) -> Polynomial {
let challenge = transcript.challenge_scalar(b"aggregate_witness");
let powers = util::powers_of(&challenge, polynomials.len() - 1);
assert_eq!(powers.len(), polynomials.len());
let numerator: Polynomial = polynomials
.iter()
.zip(powers.iter())
.map(|(poly, challenge)| poly * challenge)
.sum();
numerator.ruffini(*point)
}
pub fn open_single(
&self,
polynomial: &Polynomial,
value: &Scalar,
point: &Scalar,
) -> Result<Proof, Error> {
let witness_poly = self.compute_single_witness(polynomial, point);
Ok(Proof {
commitment_to_witness: self.commit(&witness_poly)?,
evaluated_point: *value,
commitment_to_polynomial: self.commit(polynomial)?,
})
}
pub fn open_multiple(
&self,
polynomials: &[Polynomial],
evaluations: Vec<Scalar>,
point: &Scalar,
transcript: &mut Transcript,
) -> Result<AggregateProof, Error> {
let mut polynomial_commitments = Vec::with_capacity(polynomials.len());
for poly in polynomials.iter() {
polynomial_commitments.push(self.commit(poly)?)
}
let witness_poly = self.compute_aggregate_witness(polynomials, point, transcript);
let witness_commitment = self.commit(&witness_poly)?;
let aggregate_proof = AggregateProof {
commitment_to_witness: witness_commitment,
evaluated_points: evaluations,
commitments_to_polynomials: polynomial_commitments,
};
Ok(aggregate_proof)
}
}
impl OpeningKey {
pub fn check(&self, point: Scalar, proof: Proof) -> bool {
let inner_a: G1Affine =
(proof.commitment_to_polynomial.0 - (self.g * proof.evaluated_point)).into();
let inner_b: G2Affine = (self.beta_h - (self.h * point)).into();
let prepared_inner_b = G2Prepared::from(-inner_b);
let pairing = dusk_bls12_381::multi_miller_loop(&[
(&inner_a, &self.prepared_h),
(&proof.commitment_to_witness.0, &prepared_inner_b),
])
.final_exponentiation();
pairing == dusk_bls12_381::Gt::identity()
}
pub fn batch_check(
&self,
points: &[Scalar],
proofs: &[Proof],
transcript: &mut Transcript,
) -> Result<(), Error> {
let mut total_c = G1Projective::identity();
let mut total_w = G1Projective::identity();
let challenge = transcript.challenge_scalar(b"batch");
let powers = util::powers_of(&challenge, proofs.len() - 1);
let mut g_multiplier = Scalar::zero();
for ((proof, challenge), point) in proofs.iter().zip(powers).zip(points) {
let mut c = G1Projective::from(proof.commitment_to_polynomial.0);
let w = proof.commitment_to_witness.0;
c += w * point;
g_multiplier += challenge * proof.evaluated_point;
total_c += c * challenge;
total_w += w * challenge;
}
total_c -= self.g * g_multiplier;
let affine_total_w = G1Affine::from(-total_w);
let affine_total_c = G1Affine::from(total_c);
let pairing = dusk_bls12_381::multi_miller_loop(&[
(&affine_total_w, &self.prepared_beta_h),
(&affine_total_c, &self.prepared_h),
])
.final_exponentiation();
if pairing != dusk_bls12_381::Gt::identity() {
return Err(PolyCommitSchemeError(KZG10Errors::PairingCheckFailure.into()).into());
};
Ok(())
}
}
fn check_degree_is_within_bounds(max_degree: usize, poly_degree: usize) -> Result<(), Error> {
if poly_degree == 0 {
return Err(PolyCommitSchemeError(KZG10Errors::PolynomialDegreeIsZero.into()).into());
}
if poly_degree > max_degree {
return Err(PolyCommitSchemeError(KZG10Errors::PolynomialDegreeTooLarge.into()).into());
}
Ok(())
}
#[cfg(test)]
mod test {
use super::super::srs::*;
use super::*;
use merlin::Transcript;
fn setup_test(degree: usize) -> (CommitKey, OpeningKey) {
let srs = PublicParameters::setup(degree, &mut rand::thread_rng()).unwrap();
srs.trim(degree).unwrap()
}
#[test]
fn test_basic_commit() {
let degree = 25;
let (proving_key, opening_key) = setup_test(degree);
let point = Scalar::from(10);
let poly = Polynomial::rand(degree, &mut rand::thread_rng());
let value = poly.evaluate(&point);
let proof = proving_key.open_single(&poly, &value, &point).unwrap();
let ok = opening_key.check(point, proof);
assert!(ok);
}
#[test]
fn test_batch_verification() {
let degree = 25;
let (proving_key, vk) = setup_test(degree);
let point_a = Scalar::from(10);
let point_b = Scalar::from(11);
let poly_a = Polynomial::rand(degree, &mut rand::thread_rng());
let value_a = poly_a.evaluate(&point_a);
let proof_a = proving_key
.open_single(&poly_a, &value_a, &point_a)
.unwrap();
assert!(vk.check(point_a, proof_a));
let poly_b = Polynomial::rand(degree, &mut rand::thread_rng());
let value_b = poly_b.evaluate(&point_b);
let proof_b = proving_key
.open_single(&poly_b, &value_b, &point_b)
.unwrap();
assert!(vk.check(point_b, proof_b));
assert!(vk
.batch_check(
&[point_a, point_b],
&[proof_a, proof_b],
&mut Transcript::new(b""),
)
.is_ok());
}
#[test]
fn test_aggregate_witness() {
let max_degree = 27;
let (proving_key, opening_key) = setup_test(max_degree);
let point = Scalar::from(10);
let aggregated_proof = {
let poly_a = Polynomial::rand(25, &mut rand::thread_rng());
let poly_a_eval = poly_a.evaluate(&point);
let poly_b = Polynomial::rand(26 + 1, &mut rand::thread_rng());
let poly_b_eval = poly_b.evaluate(&point);
let poly_c = Polynomial::rand(27, &mut rand::thread_rng());
let poly_c_eval = poly_c.evaluate(&point);
proving_key
.open_multiple(
&[poly_a, poly_b, poly_c],
vec![poly_a_eval, poly_b_eval, poly_c_eval],
&point,
&mut Transcript::new(b"agg_flatten"),
)
.unwrap()
};
let ok = {
let flattened_proof = aggregated_proof.flatten(&mut Transcript::new(b"agg_flatten"));
opening_key.check(point, flattened_proof)
};
assert!(ok);
}
#[test]
fn test_batch_with_aggregation() {
let max_degree = 28;
let (proving_key, opening_key) = setup_test(max_degree);
let point_a = Scalar::from(10);
let point_b = Scalar::from(11);
let (aggregated_proof, single_proof) = {
let poly_a = Polynomial::rand(25, &mut rand::thread_rng());
let poly_a_eval = poly_a.evaluate(&point_a);
let poly_b = Polynomial::rand(26, &mut rand::thread_rng());
let poly_b_eval = poly_b.evaluate(&point_a);
let poly_c = Polynomial::rand(27, &mut rand::thread_rng());
let poly_c_eval = poly_c.evaluate(&point_a);
let poly_d = Polynomial::rand(28, &mut rand::thread_rng());
let poly_d_eval = poly_d.evaluate(&point_b);
let aggregated_proof = proving_key
.open_multiple(
&[poly_a, poly_b, poly_c],
vec![poly_a_eval, poly_b_eval, poly_c_eval],
&point_a,
&mut Transcript::new(b"agg_batch"),
)
.unwrap();
let single_proof = proving_key
.open_single(&poly_d, &poly_d_eval, &point_b)
.unwrap();
(aggregated_proof, single_proof)
};
let ok = {
let mut transcript = Transcript::new(b"agg_batch");
let flattened_proof = aggregated_proof.flatten(&mut transcript);
opening_key.batch_check(
&[point_a, point_b],
&[flattened_proof, single_proof],
&mut transcript,
)
};
assert!(ok.is_ok());
}
}