From 8360b94f8924b00cd043ad9de3633b1e62fa2e46 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 2 Dec 2020 23:16:37 +0800 Subject: [PATCH] Extract plonk::vanishing::{Argument, Proof} from prover and verifier Co-authored-by: Jack Grigg --- src/plonk.rs | 5 +- src/plonk/prover.rs | 71 ++++--------------- src/plonk/vanishing.rs | 17 +++++ src/plonk/vanishing/prover.rs | 122 ++++++++++++++++++++++++++++++++ src/plonk/vanishing/verifier.rs | 76 ++++++++++++++++++++ src/plonk/verifier.rs | 44 ++---------- 6 files changed, 237 insertions(+), 98 deletions(-) create mode 100644 src/plonk/vanishing.rs create mode 100644 src/plonk/vanishing/prover.rs create mode 100644 src/plonk/vanishing/verifier.rs diff --git a/src/plonk.rs b/src/plonk.rs index a3feeed..a5fbae0 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -15,6 +15,8 @@ mod circuit; mod keygen; mod lookup; mod permutation; +mod vanishing; + mod prover; mod verifier; @@ -51,13 +53,12 @@ pub struct ProvingKey { #[derive(Debug, Clone)] pub struct Proof { advice_commitments: Vec, - h_commitments: Vec, permutations: Option>, lookups: Vec>, advice_evals: Vec, aux_evals: Vec, fixed_evals: Vec, - h_evals: Vec, + vanishing: vanishing::Proof, multiopening: multiopen::Proof, } diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 8b28990..4784499 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -3,8 +3,8 @@ use std::iter; use super::{ circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed}, - permutation, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, - Proof, ProvingKey, + permutation, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, + Error, Proof, ProvingKey, }; use crate::arithmetic::{eval_polynomial, Curve, CurveAffine, FieldExt}; use crate::poly::{ @@ -222,7 +222,7 @@ impl Proof { .collect::, _>>()?; // Obtain challenge for keeping all separate gates linearly independent - let y = ChallengeY::::get(&mut transcript); + let y = ChallengeY::get(&mut transcript); // Evaluate the h(X) polynomial's constraint system expressions for the permutation constraints, if any. let (permutations, permutation_expressions) = permutations @@ -242,7 +242,7 @@ impl Proof { }; // Evaluate the h(X) polynomial's constraint system expressions for the constraints provided - let h_poly = iter::empty() + let expressions = iter::empty() // Custom constraints .chain(meta.gates.iter().map(|poly| { poly.evaluate( @@ -257,40 +257,11 @@ impl Proof { // Permutation constraints, if any. .chain(permutation_expressions.into_iter().flatten()) // Lookup constraints, if any. - .chain(lookup_expressions.into_iter().flatten()) - .fold(domain.empty_extended(), |h_poly, v| h_poly * *y + &v); + .chain(lookup_expressions.into_iter().flatten()); - // Divide by t(X) = X^{params.n} - 1. - let h_poly = domain.divide_by_vanishing_poly(h_poly); - - // Obtain final h(X) polynomial - let h_poly = domain.extended_to_coeff(h_poly); - - // Split h(X) up into pieces - let h_pieces = h_poly - .chunks_exact(params.n as usize) - .map(|v| domain.coeff_from_vec(v.to_vec())) - .collect::>(); - drop(h_poly); - let h_blinds: Vec<_> = h_pieces.iter().map(|_| Blind(C::Scalar::rand())).collect(); - - // Compute commitments to each h(X) piece - let h_commitments_projective: Vec<_> = h_pieces - .iter() - .zip(h_blinds.iter()) - .map(|(h_piece, blind)| params.commit(&h_piece, *blind)) - .collect(); - let mut h_commitments = vec![C::zero(); h_commitments_projective.len()]; - C::Projective::batch_to_affine(&h_commitments_projective, &mut h_commitments); - let h_commitments = h_commitments; - drop(h_commitments_projective); - - // Hash each h(X) piece - for c in h_commitments.iter() { - transcript - .absorb_point(c) - .map_err(|_| Error::TranscriptError)?; - } + // Construct the vanishing argument + let vanishing = + vanishing::Argument::construct(params, domain, expressions, y, &mut transcript)?; let x = ChallengeX::get(&mut transcript); @@ -319,21 +290,17 @@ impl Proof { }) .collect(); - let h_evals: Vec<_> = h_pieces - .iter() - .map(|poly| eval_polynomial(poly, *x)) - .collect(); - - // Hash each advice evaluation + // Hash each column evaluation for eval in advice_evals .iter() .chain(aux_evals.iter()) .chain(fixed_evals.iter()) - .chain(h_evals.iter()) { transcript.absorb_scalar(*eval); } + let vanishing = vanishing.evaluate(x, &mut transcript); + // Evaluate the permutations, if any, at omega^i x. let permutations = permutations.map(|p| p.evaluate(pk, x, &mut transcript)); @@ -370,18 +337,7 @@ impl Proof { }, )) // We query the h(X) polynomial at x - .chain( - h_pieces - .iter() - .zip(h_blinds.iter()) - .zip(h_evals.iter()) - .map(|((h_poly, h_blind), h_eval)| ProverQuery { - point: *x, - poly: h_poly, - blind: *h_blind, - eval: *h_eval, - }), - ); + .chain(vanishing.open(x)); let multiopening = multiopen::Proof::create( params, @@ -400,13 +356,12 @@ impl Proof { Ok(Proof { advice_commitments, - h_commitments, permutations: permutations.map(|p| p.build()), lookups: lookups.into_iter().map(|p| p.build()).collect(), advice_evals, fixed_evals, aux_evals, - h_evals, + vanishing: vanishing.build(), multiopening, }) } diff --git a/src/plonk/vanishing.rs b/src/plonk/vanishing.rs new file mode 100644 index 0000000..62695ba --- /dev/null +++ b/src/plonk/vanishing.rs @@ -0,0 +1,17 @@ +use std::marker::PhantomData; + +use crate::arithmetic::CurveAffine; + +mod prover; +mod verifier; + +/// A vanishing argument. +pub(crate) struct Argument { + _marker: PhantomData, +} + +#[derive(Debug, Clone)] +pub(crate) struct Proof { + h_commitments: Vec, + h_evals: Vec, +} diff --git a/src/plonk/vanishing/prover.rs b/src/plonk/vanishing/prover.rs new file mode 100644 index 0000000..df6a00a --- /dev/null +++ b/src/plonk/vanishing/prover.rs @@ -0,0 +1,122 @@ +use super::{Argument, Proof}; +use crate::{ + arithmetic::{eval_polynomial, Curve, CurveAffine, FieldExt}, + plonk::{ChallengeX, ChallengeY, Error}, + poly::{ + commitment::{Blind, Params}, + multiopen::ProverQuery, + Coeff, EvaluationDomain, ExtendedLagrangeCoeff, Polynomial, + }, + transcript::{Hasher, Transcript}, +}; + +pub(in crate::plonk) struct Constructed { + h_pieces: Vec>, + h_blinds: Vec>, + h_commitments: Vec, +} + +pub(in crate::plonk) struct Evaluated { + constructed: Constructed, + h_evals: Vec, +} + +impl Argument { + pub(in crate::plonk) fn construct, HScalar: Hasher>( + params: &Params, + domain: &EvaluationDomain, + expressions: impl Iterator>, + y: ChallengeY, + transcript: &mut Transcript, + ) -> Result, Error> { + // Evaluate the h(X) polynomial's constraint system expressions for the constraints provided + let h_poly = expressions.fold(domain.empty_extended(), |h_poly, v| h_poly * *y + &v); + + // Divide by t(X) = X^{params.n} - 1. + let h_poly = domain.divide_by_vanishing_poly(h_poly); + + // Obtain final h(X) polynomial + let h_poly = domain.extended_to_coeff(h_poly); + + // Split h(X) up into pieces + let h_pieces = h_poly + .chunks_exact(params.n as usize) + .map(|v| domain.coeff_from_vec(v.to_vec())) + .collect::>(); + drop(h_poly); + let h_blinds: Vec<_> = h_pieces.iter().map(|_| Blind(C::Scalar::rand())).collect(); + + // Compute commitments to each h(X) piece + let h_commitments_projective: Vec<_> = h_pieces + .iter() + .zip(h_blinds.iter()) + .map(|(h_piece, blind)| params.commit(&h_piece, *blind)) + .collect(); + let mut h_commitments = vec![C::zero(); h_commitments_projective.len()]; + C::Projective::batch_to_affine(&h_commitments_projective, &mut h_commitments); + let h_commitments = h_commitments; + + // Hash each h(X) piece + for c in h_commitments.iter() { + transcript + .absorb_point(c) + .map_err(|_| Error::TranscriptError)?; + } + + Ok(Constructed { + h_pieces, + h_blinds, + h_commitments, + }) + } +} + +impl Constructed { + pub(in crate::plonk) fn evaluate, HScalar: Hasher>( + self, + x: ChallengeX, + transcript: &mut Transcript, + ) -> Evaluated { + let h_evals: Vec<_> = self + .h_pieces + .iter() + .map(|poly| eval_polynomial(poly, *x)) + .collect(); + + // Hash each advice evaluation + for eval in &h_evals { + transcript.absorb_scalar(*eval); + } + + Evaluated { + constructed: self, + h_evals, + } + } +} + +impl Evaluated { + pub(in crate::plonk) fn open<'a>( + &'a self, + x: ChallengeX, + ) -> impl Iterator> + Clone { + self.constructed + .h_pieces + .iter() + .zip(self.constructed.h_blinds.iter()) + .zip(self.h_evals.iter()) + .map(move |((h_poly, h_blind), h_eval)| ProverQuery { + point: *x, + poly: h_poly, + blind: *h_blind, + eval: *h_eval, + }) + } + + pub(in crate::plonk) fn build(self) -> Proof { + Proof { + h_commitments: self.constructed.h_commitments, + h_evals: self.h_evals, + } + } +} diff --git a/src/plonk/vanishing/verifier.rs b/src/plonk/vanishing/verifier.rs new file mode 100644 index 0000000..e86cb62 --- /dev/null +++ b/src/plonk/vanishing/verifier.rs @@ -0,0 +1,76 @@ +use ff::Field; + +use super::Proof; +use crate::{ + arithmetic::CurveAffine, + plonk::{ChallengeX, ChallengeY, Error, VerifyingKey}, + poly::multiopen::VerifierQuery, + transcript::{Hasher, Transcript}, +}; + +impl Proof { + pub(in crate::plonk) fn check_lengths(&self, _vk: &VerifyingKey) -> Result<(), Error> { + // TODO: check h_evals + + // TODO: check h_commitments + + Ok(()) + } + + pub(in crate::plonk) fn absorb_commitments< + HBase: Hasher, + HScalar: Hasher, + >( + &self, + transcript: &mut Transcript, + ) -> Result<(), Error> { + // Obtain a commitment to h(X) in the form of multiple pieces of degree n - 1 + for c in &self.h_commitments { + transcript + .absorb_point(c) + .map_err(|_| Error::TranscriptError)?; + } + Ok(()) + } + + pub(in crate::plonk) fn verify( + &self, + expressions: impl Iterator, + y: ChallengeY, + xn: C::Scalar, + ) -> Result<(), Error> { + let expected_h_eval = expressions.fold(C::Scalar::zero(), |h_eval, v| h_eval * &y + &v); + + // Compute h(x) from the prover + let h_eval = self + .h_evals + .iter() + .rev() + .fold(C::Scalar::zero(), |acc, eval| acc * &xn + eval); + + // Did the prover commit to the correct polynomial? + if expected_h_eval != (h_eval * &(xn - &C::Scalar::one())) { + return Err(Error::ConstraintSystemFailure); + } + + Ok(()) + } + + pub(in crate::plonk) fn evals(&self) -> impl Iterator { + self.h_evals.iter() + } + + pub(in crate::plonk) fn queries<'a>( + &'a self, + x: ChallengeX, + ) -> impl Iterator> + Clone { + self.h_commitments + .iter() + .zip(self.h_evals.iter()) + .map(move |(commitment, &eval)| VerifierQuery { + point: *x, + commitment, + eval, + }) + } +} diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 5db3310..bc21231 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -75,12 +75,7 @@ impl<'a, C: CurveAffine> Proof { // Sample y challenge, which keeps the gates linearly independent. let y = ChallengeY::get(&mut transcript); - // Obtain a commitment to h(X) in the form of multiple pieces of degree n - 1 - for c in &self.h_commitments { - transcript - .absorb_point(c) - .map_err(|_| Error::TranscriptError)?; - } + self.vanishing.absorb_commitments(&mut transcript)?; // Sample x challenge, which is used to ensure the circuit is // satisfied with high probability. @@ -95,7 +90,7 @@ impl<'a, C: CurveAffine> Proof { .iter() .chain(self.aux_evals.iter()) .chain(self.fixed_evals.iter()) - .chain(self.h_evals.iter()) + .chain(self.vanishing.evals()) .chain( self.permutations .as_ref() @@ -135,17 +130,7 @@ impl<'a, C: CurveAffine> Proof { eval: self.fixed_evals[query_index], }, )) - .chain( - self.h_commitments - .iter() - .enumerate() - .zip(self.h_evals.iter()) - .map(|((idx, _), &eval)| VerifierQuery { - point: *x, - commitment: &self.h_commitments[idx], - eval, - }), - ); + .chain(self.vanishing.queries(x)); // We are now convinced the circuit is satisfied so long as the // polynomial commitments open to the correct values. @@ -184,8 +169,6 @@ impl<'a, C: CurveAffine> Proof { return Err(Error::IncompatibleParams); } - // TODO: check h_evals - if self.fixed_evals.len() != vk.cs.fixed_queries.len() { return Err(Error::IncompatibleParams); } @@ -203,8 +186,6 @@ impl<'a, C: CurveAffine> Proof { return Err(Error::IncompatibleParams); } - // TODO: check h_commitments - if self.advice_commitments.len() != vk.cs.num_advice_columns { return Err(Error::IncompatibleParams); } @@ -234,7 +215,7 @@ impl<'a, C: CurveAffine> Proof { * &vk.domain.get_barycentric_weight(); // l_0(x) // Compute the expected value of h(x) - let expected_h_eval = std::iter::empty() + let expressions = std::iter::empty() // Evaluate the circuit using the custom gates provided .chain(vk.cs.gates.iter().map(|poly| { poly.evaluate( @@ -272,21 +253,8 @@ impl<'a, C: CurveAffine> Proof { }) .into_iter() .flatten(), - ) - .fold(C::Scalar::zero(), |h_eval, v| h_eval * &y + &v); + ); - // Compute h(x) from the prover - let h_eval = self - .h_evals - .iter() - .rev() - .fold(C::Scalar::zero(), |acc, eval| acc * &xn + eval); - - // Did the prover commit to the correct polynomial? - if expected_h_eval != (h_eval * &(xn - &C::Scalar::one())) { - return Err(Error::ConstraintSystemFailure); - } - - Ok(()) + self.vanishing.verify(expressions, y, xn) } }