From 5be7d9525d638f9df898c8a52db097682587bb4f Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 21 Dec 2020 15:59:41 -0700 Subject: [PATCH] Update multiopen APIs to reflect changes made to Transcript APIs --- src/poly/multiopen.rs | 16 +-- src/poly/multiopen/prover.rs | 241 +++++++++++++++------------------ src/poly/multiopen/verifier.rs | 199 ++++++++++++++------------- 3 files changed, 212 insertions(+), 244 deletions(-) diff --git a/src/poly/multiopen.rs b/src/poly/multiopen.rs index 0d4cc935..4dbb1987 100644 --- a/src/poly/multiopen.rs +++ b/src/poly/multiopen.rs @@ -15,6 +15,9 @@ use crate::{ mod prover; mod verifier; +pub use prover::create_proof; +pub use verifier::verify_proof; + #[derive(Clone, Copy, Debug)] struct X1 {} /// Challenge for compressing openings at the same point sets together. @@ -36,19 +39,6 @@ struct X4 {} /// together. type ChallengeX4 = ChallengeScalar; -/// This is a multi-point opening proof used in the polynomial commitment scheme opening. -#[derive(Debug, Clone)] -pub struct Proof { - // A vector of evaluations at each set of query points - q_evals: Vec, - - // Commitment to final polynomial - f_commitment: C, - - // Commitment proof - opening: commitment::Proof, -} - /// A polynomial query at a point #[derive(Debug, Clone)] pub struct ProverQuery<'a, C: CurveAffine> { diff --git a/src/poly/multiopen/prover.rs b/src/poly/multiopen/prover.rs index 15b5fc80..6244e203 100644 --- a/src/poly/multiopen/prover.rs +++ b/src/poly/multiopen/prover.rs @@ -1,18 +1,19 @@ use super::super::{ commitment::{self, Blind, Params}, - Coeff, Error, Polynomial, + Coeff, Polynomial, }; use super::{ - construct_intermediate_sets, ChallengeX1, ChallengeX2, ChallengeX3, ChallengeX4, Proof, - ProverQuery, Query, + construct_intermediate_sets, ChallengeX1, ChallengeX2, ChallengeX3, ChallengeX4, ProverQuery, + Query, }; use crate::arithmetic::{ eval_polynomial, kate_division, lagrange_interpolate, Curve, CurveAffine, FieldExt, }; -use crate::transcript::{Hasher, Transcript}; +use crate::transcript::TranscriptWrite; use ff::Field; +use std::io::{self, Write}; use std::marker::PhantomData; #[derive(Debug, Clone)] @@ -23,138 +24,118 @@ struct CommitmentData { evals: Vec, } -impl Proof { - /// Create a multi-opening proof - pub fn create<'a, I, HBase: Hasher, HScalar: Hasher>( - params: &Params, - transcript: &mut Transcript, - queries: I, - ) -> Result - where - I: IntoIterator> + Clone, +/// Create a multi-opening proof +pub fn create_proof<'a, I, C: CurveAffine, W: Write, T: TranscriptWrite>( + params: &Params, + transcript: &mut T, + queries: I, +) -> io::Result<()> +where + I: IntoIterator> + Clone, +{ + let x_1 = ChallengeX1::get(transcript); + let x_2 = ChallengeX2::get(transcript); + + let (poly_map, point_sets) = construct_intermediate_sets(queries); + + // Collapse openings at same point sets together into single openings using + // x_1 challenge. + let mut q_polys: Vec>> = vec![None; point_sets.len()]; + let mut q_blinds = vec![Blind(C::Scalar::zero()); point_sets.len()]; + + // A vec of vecs of evals. The outer vec corresponds to the point set, + // while the inner vec corresponds to the points in a particular set. + let mut q_eval_sets = Vec::with_capacity(point_sets.len()); + for point_set in point_sets.iter() { + q_eval_sets.push(vec![C::Scalar::zero(); point_set.len()]); + } + { - let x_1 = ChallengeX1::get(transcript); - let x_2 = ChallengeX2::get(transcript); - - let (poly_map, point_sets) = construct_intermediate_sets(queries); - - // Collapse openings at same point sets together into single openings using - // x_1 challenge. - let mut q_polys: Vec>> = vec![None; point_sets.len()]; - let mut q_blinds = vec![Blind(C::Scalar::zero()); point_sets.len()]; - - // A vec of vecs of evals. The outer vec corresponds to the point set, - // while the inner vec corresponds to the points in a particular set. - let mut q_eval_sets = Vec::with_capacity(point_sets.len()); - for point_set in point_sets.iter() { - q_eval_sets.push(vec![C::Scalar::zero(); point_set.len()]); - } - - { - let mut accumulate = |set_idx: usize, - new_poly: &Polynomial, - blind: Blind, - evals: Vec| { - if let Some(poly) = &q_polys[set_idx] { - q_polys[set_idx] = Some(poly.clone() * *x_1 + new_poly); - } else { - q_polys[set_idx] = Some(new_poly.clone()); - } - q_blinds[set_idx] *= *x_1; - q_blinds[set_idx] += blind; - // Each polynomial is evaluated at a set of points. For each set, - // we collapse each polynomial's evals pointwise. - for (eval, set_eval) in evals.iter().zip(q_eval_sets[set_idx].iter_mut()) { - *set_eval *= &*x_1; - *set_eval += eval; - } - }; - - for commitment_data in poly_map.into_iter() { - accumulate( - commitment_data.set_index, // set_idx, - commitment_data.commitment.poly, // poly, - commitment_data.commitment.blind, // blind, - commitment_data.evals, // evals - ); - } - } - - let f_poly = point_sets - .iter() - .zip(q_eval_sets.iter()) - .zip(q_polys.iter()) - .fold(None, |f_poly, ((points, evals), poly)| { - let mut poly = poly.clone().unwrap().values; - // TODO: makes implicit asssumption that poly degree is smaller than interpolation poly degree - for (p, r) in poly.iter_mut().zip(lagrange_interpolate(points, evals)) { - *p -= &r; - } - let mut poly = points - .iter() - .fold(poly, |poly, point| kate_division(&poly, *point)); - poly.resize(params.n as usize, C::Scalar::zero()); - let poly = Polynomial { - values: poly, - _marker: PhantomData, - }; - - if f_poly.is_none() { - Some(poly) - } else { - f_poly.map(|f_poly| f_poly * *x_2 + &poly) - } - }) - .unwrap(); - - let mut f_blind = Blind(C::Scalar::rand()); - let mut f_commitment = params.commit(&f_poly, f_blind).to_affine(); - - let (opening, q_evals) = loop { - let mut transcript = transcript.clone(); - transcript - .absorb_point(&f_commitment) - .map_err(|_| Error::SamplingError)?; - - let x_3 = ChallengeX3::get(&mut transcript); - - let q_evals: Vec = q_polys - .iter() - .map(|poly| eval_polynomial(poly.as_ref().unwrap(), *x_3)) - .collect(); - - for eval in q_evals.iter() { - transcript.absorb_scalar(*eval); - } - - let x_4 = ChallengeX4::get(&mut transcript); - - let (f_poly, f_blind_try) = q_polys.iter().zip(q_blinds.iter()).fold( - (f_poly.clone(), f_blind), - |(f_poly, f_blind), (poly, blind)| { - ( - f_poly * *x_4 + poly.as_ref().unwrap(), - Blind((f_blind.0 * &*x_4) + &blind.0), - ) - }, - ); - - if let Ok(opening) = - commitment::Proof::create(¶ms, &mut transcript, &f_poly, f_blind_try, *x_3) - { - break (opening, q_evals); + let mut accumulate = |set_idx: usize, + new_poly: &Polynomial, + blind: Blind, + evals: Vec| { + if let Some(poly) = &q_polys[set_idx] { + q_polys[set_idx] = Some(poly.clone() * *x_1 + new_poly); } else { - f_blind += C::Scalar::one(); - f_commitment = (f_commitment + params.h).to_affine(); + q_polys[set_idx] = Some(new_poly.clone()); + } + q_blinds[set_idx] *= *x_1; + q_blinds[set_idx] += blind; + // Each polynomial is evaluated at a set of points. For each set, + // we collapse each polynomial's evals pointwise. + for (eval, set_eval) in evals.iter().zip(q_eval_sets[set_idx].iter_mut()) { + *set_eval *= &x_1; + *set_eval += eval; } }; - Ok(Proof { - q_evals, - f_commitment, - opening, - }) + for commitment_data in poly_map.into_iter() { + accumulate( + commitment_data.set_index, // set_idx, + commitment_data.commitment.poly, // poly, + commitment_data.commitment.blind, // blind, + commitment_data.evals, // evals + ); + } } + + let f_poly = point_sets + .iter() + .zip(q_eval_sets.iter()) + .zip(q_polys.iter()) + .fold(None, |f_poly, ((points, evals), poly)| { + let mut poly = poly.clone().unwrap().values; + // TODO: makes implicit asssumption that poly degree is smaller than interpolation poly degree + for (p, r) in poly.iter_mut().zip(lagrange_interpolate(points, evals)) { + *p -= &r; + } + let mut poly = points + .iter() + .fold(poly, |poly, point| kate_division(&poly, *point)); + poly.resize(params.n as usize, C::Scalar::zero()); + let poly = Polynomial { + values: poly, + _marker: PhantomData, + }; + + if f_poly.is_none() { + Some(poly) + } else { + f_poly.map(|f_poly| f_poly * *x_2 + &poly) + } + }) + .unwrap(); + + let f_blind = Blind(C::Scalar::rand()); + let f_commitment = params.commit(&f_poly, f_blind).to_affine(); + + transcript.write_point(f_commitment)?; + + let x_3 = ChallengeX3::get(transcript); + + let q_evals: Vec = q_polys + .iter() + .map(|poly| eval_polynomial(poly.as_ref().unwrap(), *x_3)) + .collect(); + + for eval in q_evals.iter() { + transcript.write_scalar(*eval)?; + } + + let x_4 = ChallengeX4::get(transcript); + + let (f_poly, f_blind_try) = q_polys.iter().zip(q_blinds.iter()).fold( + (f_poly.clone(), f_blind), + |(f_poly, f_blind), (poly, blind)| { + ( + f_poly * *x_4 + poly.as_ref().unwrap(), + Blind((f_blind.0 * &x_4) + &blind.0), + ) + }, + ); + + commitment::create_proof(¶ms, transcript, &f_poly, f_blind_try, *x_3) } #[doc(hidden)] diff --git a/src/poly/multiopen/verifier.rs b/src/poly/multiopen/verifier.rs index 2e1d8322..dc2e8b9b 100644 --- a/src/poly/multiopen/verifier.rs +++ b/src/poly/multiopen/verifier.rs @@ -5,11 +5,13 @@ use super::super::{ Error, }; use super::{ - construct_intermediate_sets, ChallengeX1, ChallengeX2, ChallengeX3, ChallengeX4, Proof, Query, + construct_intermediate_sets, ChallengeX1, ChallengeX2, ChallengeX3, ChallengeX4, Query, VerifierQuery, }; use crate::arithmetic::{eval_polynomial, lagrange_interpolate, CurveAffine, FieldExt}; -use crate::transcript::{Hasher, Transcript}; +use crate::transcript::TranscriptRead; + +use std::io::Read; #[derive(Debug, Clone)] struct CommitmentData { @@ -18,114 +20,109 @@ struct CommitmentData { evals: Vec, } -impl Proof { - /// Verify a multi-opening proof - pub fn verify<'a, I, HBase: Hasher, HScalar: Hasher>( - &self, - params: &'a Params, - transcript: &mut Transcript, - queries: I, - mut msm: MSM<'a, C>, - ) -> Result, Error> - where - I: IntoIterator> + Clone, +/// Verify a multi-opening proof +pub fn verify_proof<'a, I, C: CurveAffine, R: Read, T: TranscriptRead>( + params: &'a Params, + transcript: &mut T, + queries: I, + mut msm: MSM<'a, C>, +) -> Result, Error> +where + I: IntoIterator> + Clone, +{ + // Scale the MSM by a random factor to ensure that if the existing MSM + // has is_zero() == false then this argument won't be able to interfere + // with it to make it true, with high probability. + msm.scale(C::Scalar::rand()); + + // Sample x_1 for compressing openings at the same point sets together + let x_1 = ChallengeX1::get(transcript); + + // Sample a challenge x_2 for keeping the multi-point quotient + // polynomial terms linearly independent. + let x_2 = ChallengeX2::get(transcript); + + let (commitment_map, point_sets) = construct_intermediate_sets(queries); + + // Compress the commitments and expected evaluations at x together. + // using the challenge x_1 + let mut q_commitments: Vec<_> = vec![params.empty_msm(); point_sets.len()]; + + // A vec of vecs of evals. The outer vec corresponds to the point set, + // while the inner vec corresponds to the points in a particular set. + let mut q_eval_sets = Vec::with_capacity(point_sets.len()); + for point_set in point_sets.iter() { + q_eval_sets.push(vec![C::Scalar::zero(); point_set.len()]); + } { - // Scale the MSM by a random factor to ensure that if the existing MSM - // has is_zero() == false then this argument won't be able to interfere - // with it to make it true, with high probability. - msm.scale(C::Scalar::rand()); - - // Sample x_1 for compressing openings at the same point sets together - let x_1 = ChallengeX1::get(transcript); - - // Sample a challenge x_2 for keeping the multi-point quotient - // polynomial terms linearly independent. - let x_2 = ChallengeX2::::get(transcript); - - let (commitment_map, point_sets) = construct_intermediate_sets(queries); - - // Compress the commitments and expected evaluations at x together. - // using the challenge x_1 - let mut q_commitments: Vec<_> = vec![params.empty_msm(); point_sets.len()]; - - // A vec of vecs of evals. The outer vec corresponds to the point set, - // while the inner vec corresponds to the points in a particular set. - let mut q_eval_sets = Vec::with_capacity(point_sets.len()); - for point_set in point_sets.iter() { - q_eval_sets.push(vec![C::Scalar::zero(); point_set.len()]); - } - { - let mut accumulate = |set_idx: usize, new_commitment, evals: Vec| { - q_commitments[set_idx].scale(*x_1); - q_commitments[set_idx].append_term(C::Scalar::one(), new_commitment); - for (eval, set_eval) in evals.iter().zip(q_eval_sets[set_idx].iter_mut()) { - *set_eval *= &*x_1; - *set_eval += eval; - } - }; - - // Each commitment corresponds to evaluations at a set of points. - // For each set, we collapse each commitment's evals pointwise. - for commitment_data in commitment_map.into_iter() { - accumulate( - commitment_data.set_index, // set_idx, - *commitment_data.commitment.0, // commitment, - commitment_data.evals, // evals - ); + let mut accumulate = |set_idx: usize, new_commitment, evals: Vec| { + q_commitments[set_idx].scale(*x_1); + q_commitments[set_idx].append_term(C::Scalar::one(), new_commitment); + for (eval, set_eval) in evals.iter().zip(q_eval_sets[set_idx].iter_mut()) { + *set_eval *= &x_1; + *set_eval += eval; } - } + }; - // Obtain the commitment to the multi-point quotient polynomial f(X). - transcript - .absorb_point(&self.f_commitment) - .map_err(|_| Error::SamplingError)?; - - // Sample a challenge x_3 for checking that f(X) was committed to - // correctly. - let x_3 = ChallengeX3::get(transcript); - - for eval in self.q_evals.iter() { - transcript.absorb_scalar(*eval); - } - - // We can compute the expected msm_eval at x_3 using the q_evals provided - // by the prover and from x_2 - let msm_eval = point_sets - .iter() - .zip(q_eval_sets.iter()) - .zip(self.q_evals.iter()) - .fold( - C::Scalar::zero(), - |msm_eval, ((points, evals), proof_eval)| { - let r_poly = lagrange_interpolate(points, evals); - let r_eval = eval_polynomial(&r_poly, *x_3); - let eval = points.iter().fold(*proof_eval - &r_eval, |eval, point| { - eval * &(*x_3 - point).invert().unwrap() - }); - msm_eval * &*x_2 + &eval - }, + // Each commitment corresponds to evaluations at a set of points. + // For each set, we collapse each commitment's evals pointwise. + for commitment_data in commitment_map.into_iter() { + accumulate( + commitment_data.set_index, // set_idx, + *commitment_data.commitment.0, // commitment, + commitment_data.evals, // evals ); + } + } - // Sample a challenge x_4 that we will use to collapse the openings of - // the various remaining polynomials at x_3 together. - let x_4 = ChallengeX4::get(transcript); + // Obtain the commitment to the multi-point quotient polynomial f(X). + let f_commitment = transcript.read_point().map_err(|_| Error::SamplingError)?; - // Compute the final commitment that has to be opened - let mut commitment_msm = params.empty_msm(); - commitment_msm.append_term(C::Scalar::one(), self.f_commitment); - let (commitment_msm, msm_eval) = q_commitments.into_iter().zip(self.q_evals.iter()).fold( - (commitment_msm, msm_eval), - |(mut commitment_msm, msm_eval), (q_commitment, q_eval)| { - commitment_msm.scale(*x_4); - commitment_msm.add_msm(&q_commitment); - (commitment_msm, msm_eval * &*x_4 + q_eval) + // Sample a challenge x_3 for checking that f(X) was committed to + // correctly. + let x_3 = ChallengeX3::get(transcript); + + let mut q_evals = Vec::with_capacity(q_eval_sets.len()); + for _ in 0..q_eval_sets.len() { + q_evals.push(transcript.read_scalar().map_err(|_| Error::SamplingError)?); + } + + // We can compute the expected msm_eval at x_3 using the q_evals provided + // by the prover and from x_2 + let msm_eval = point_sets + .iter() + .zip(q_eval_sets.iter()) + .zip(q_evals.iter()) + .fold( + C::Scalar::zero(), + |msm_eval, ((points, evals), proof_eval)| { + let r_poly = lagrange_interpolate(points, evals); + let r_eval = eval_polynomial(&r_poly, *x_3); + let eval = points.iter().fold(*proof_eval - &r_eval, |eval, point| { + eval * &(*x_3 - point).invert().unwrap() + }); + msm_eval * &x_2 + &eval }, ); - // Verify the opening proof - self.opening - .verify(params, msm, transcript, *x_3, commitment_msm, msm_eval) - } + // Sample a challenge x_4 that we will use to collapse the openings of + // the various remaining polynomials at x_3 together. + let x_4 = ChallengeX4::get(transcript); + + // Compute the final commitment that has to be opened + let mut commitment_msm = params.empty_msm(); + commitment_msm.append_term(C::Scalar::one(), f_commitment); + let (commitment_msm, msm_eval) = q_commitments.into_iter().zip(q_evals.iter()).fold( + (commitment_msm, msm_eval), + |(mut commitment_msm, msm_eval), (q_commitment, q_eval)| { + commitment_msm.scale(*x_4); + commitment_msm.add_msm(&q_commitment); + (commitment_msm, msm_eval * &x_4 + q_eval) + }, + ); + + // Verify the opening proof + super::commitment::verify_proof(params, msm, transcript, *x_3, commitment_msm, msm_eval) } #[doc(hidden)]