Extract plonk::vanishing::{Argument, Proof} from prover and verifier

Co-authored-by: Jack Grigg <jack@electriccoin.co>
This commit is contained in:
therealyingtong 2020-12-02 23:16:37 +08:00
parent cf734f7875
commit 8360b94f89
6 changed files with 237 additions and 98 deletions

View File

@ -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<C: CurveAffine> {
#[derive(Debug, Clone)]
pub struct Proof<C: CurveAffine> {
advice_commitments: Vec<C>,
h_commitments: Vec<C>,
permutations: Option<permutation::Proof<C>>,
lookups: Vec<lookup::Proof<C>>,
advice_evals: Vec<C::Scalar>,
aux_evals: Vec<C::Scalar>,
fixed_evals: Vec<C::Scalar>,
h_evals: Vec<C::Scalar>,
vanishing: vanishing::Proof<C>,
multiopening: multiopen::Proof<C>,
}

View File

@ -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<C: CurveAffine> Proof<C> {
.collect::<Result<Vec<_>, _>>()?;
// Obtain challenge for keeping all separate gates linearly independent
let y = ChallengeY::<C::Scalar>::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<C: CurveAffine> Proof<C> {
};
// 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<C: CurveAffine> Proof<C> {
// 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::<Vec<_>>();
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<C: CurveAffine> Proof<C> {
})
.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<C: CurveAffine> Proof<C> {
},
))
// 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<C: CurveAffine> Proof<C> {
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,
})
}

17
src/plonk/vanishing.rs Normal file
View File

@ -0,0 +1,17 @@
use std::marker::PhantomData;
use crate::arithmetic::CurveAffine;
mod prover;
mod verifier;
/// A vanishing argument.
pub(crate) struct Argument<C: CurveAffine> {
_marker: PhantomData<C>,
}
#[derive(Debug, Clone)]
pub(crate) struct Proof<C: CurveAffine> {
h_commitments: Vec<C>,
h_evals: Vec<C::Scalar>,
}

View File

@ -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<C: CurveAffine> {
h_pieces: Vec<Polynomial<C::Scalar, Coeff>>,
h_blinds: Vec<Blind<C::Scalar>>,
h_commitments: Vec<C>,
}
pub(in crate::plonk) struct Evaluated<C: CurveAffine> {
constructed: Constructed<C>,
h_evals: Vec<C::Scalar>,
}
impl<C: CurveAffine> Argument<C> {
pub(in crate::plonk) fn construct<HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>(
params: &Params<C>,
domain: &EvaluationDomain<C::Scalar>,
expressions: impl Iterator<Item = Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
y: ChallengeY<C::Scalar>,
transcript: &mut Transcript<C, HBase, HScalar>,
) -> Result<Constructed<C>, 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::<Vec<_>>();
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<C: CurveAffine> Constructed<C> {
pub(in crate::plonk) fn evaluate<HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>(
self,
x: ChallengeX<C::Scalar>,
transcript: &mut Transcript<C, HBase, HScalar>,
) -> Evaluated<C> {
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<C: CurveAffine> Evaluated<C> {
pub(in crate::plonk) fn open<'a>(
&'a self,
x: ChallengeX<C::Scalar>,
) -> impl Iterator<Item = ProverQuery<'a, C>> + 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<C> {
Proof {
h_commitments: self.constructed.h_commitments,
h_evals: self.h_evals,
}
}
}

View File

@ -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<C: CurveAffine> Proof<C> {
pub(in crate::plonk) fn check_lengths(&self, _vk: &VerifyingKey<C>) -> Result<(), Error> {
// TODO: check h_evals
// TODO: check h_commitments
Ok(())
}
pub(in crate::plonk) fn absorb_commitments<
HBase: Hasher<C::Base>,
HScalar: Hasher<C::Scalar>,
>(
&self,
transcript: &mut Transcript<C, HBase, HScalar>,
) -> 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<Item = C::Scalar>,
y: ChallengeY<C::Scalar>,
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<Item = &C::Scalar> {
self.h_evals.iter()
}
pub(in crate::plonk) fn queries<'a>(
&'a self,
x: ChallengeX<C::Scalar>,
) -> impl Iterator<Item = VerifierQuery<'a, C>> + Clone {
self.h_commitments
.iter()
.zip(self.h_evals.iter())
.map(move |(commitment, &eval)| VerifierQuery {
point: *x,
commitment,
eval,
})
}
}

View File

@ -75,12 +75,7 @@ impl<'a, C: CurveAffine> Proof<C> {
// 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<C> {
.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<C> {
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<C> {
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<C> {
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<C> {
* &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<C> {
})
.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)
}
}