From ec2d8db8cbd0f8472ef8593ef8b7819a7201f954 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Wed, 13 Jan 2021 17:22:32 -0700 Subject: [PATCH] Multiopen prover never needed evals to be specified. The Lagrange interpolation we were doing was pointless. kate_division sheds the constant term off each time it is invoked because the quotient polynomial isn't affected by it. This means we were modifying coefficients that end up getting discarded anyway; the quotient polynomial coefficients are already determined exactly by the leading coefficients and the fact that a root exists at each of the points. --- src/plonk/lookup/prover.rs | 19 +--- src/plonk/permutation/prover.rs | 34 ++----- src/plonk/prover.rs | 86 +++++++++--------- src/plonk/vanishing/prover.rs | 10 +-- src/poly/multiopen.rs | 153 +++++++++++++++++++++++++++++--- src/poly/multiopen/prover.rs | 57 ++++-------- src/poly/multiopen/verifier.rs | 1 + 7 files changed, 211 insertions(+), 149 deletions(-) diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 82e7921..f13bcdb 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -55,11 +55,6 @@ pub(in crate::plonk) struct Constructed { pub(in crate::plonk) struct Evaluated { constructed: Constructed, - product_eval: C::Scalar, - product_inv_eval: C::Scalar, - permuted_input_eval: C::Scalar, - permuted_input_inv_eval: C::Scalar, - permuted_table_eval: C::Scalar, } impl Argument { @@ -465,14 +460,7 @@ impl Constructed { .map_err(|_| Error::TranscriptError)?; } - Ok(Evaluated { - constructed: self, - product_eval, - product_inv_eval, - permuted_input_eval, - permuted_input_inv_eval, - permuted_table_eval, - }) + Ok(Evaluated { constructed: self }) } } @@ -490,35 +478,30 @@ impl Evaluated { point: *x, poly: &self.constructed.product_poly, blind: self.constructed.product_blind, - eval: self.product_eval, })) // Open lookup input commitments at x .chain(Some(ProverQuery { point: *x, poly: &self.constructed.permuted_input_poly, blind: self.constructed.permuted_input_blind, - eval: self.permuted_input_eval, })) // Open lookup table commitments at x .chain(Some(ProverQuery { point: *x, poly: &self.constructed.permuted_table_poly, blind: self.constructed.permuted_table_blind, - eval: self.permuted_table_eval, })) // Open lookup input commitments at x_inv .chain(Some(ProverQuery { point: x_inv, poly: &self.constructed.permuted_input_poly, blind: self.constructed.permuted_input_blind, - eval: self.permuted_input_inv_eval, })) // Open lookup product commitments at x_inv .chain(Some(ProverQuery { point: x_inv, poly: &self.constructed.product_poly, blind: self.constructed.product_blind, - eval: self.product_inv_eval, })) } } diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs index 7d5fd7a..5af58e8 100644 --- a/src/plonk/permutation/prover.rs +++ b/src/plonk/permutation/prover.rs @@ -27,9 +27,6 @@ pub(crate) struct Constructed { pub(crate) struct Evaluated { constructed: Constructed, - permutation_product_eval: C::Scalar, - permutation_product_inv_eval: C::Scalar, - permutation_evals: Vec, } impl Argument { @@ -218,20 +215,12 @@ impl super::ProvingKey { .collect() } - fn open<'a>( - &'a self, - evals: &'a [C::Scalar], - x: ChallengeX, - ) -> impl Iterator> + Clone { - self.polys - .iter() - .zip(evals.iter()) - .map(move |(poly, eval)| ProverQuery { - point: *x, - poly, - blind: Blind::default(), - eval: *eval, - }) + fn open<'a>(&'a self, x: ChallengeX) -> impl Iterator> + Clone { + self.polys.iter().map(move |poly| ProverQuery { + point: *x, + poly, + blind: Blind::default(), + }) } } @@ -265,12 +254,7 @@ impl Constructed { .map_err(|_| Error::TranscriptError)?; } - Ok(Evaluated { - constructed: self, - permutation_product_eval, - permutation_product_inv_eval, - permutation_evals, - }) + Ok(Evaluated { constructed: self }) } } @@ -289,15 +273,13 @@ impl Evaluated { point: *x, poly: &self.constructed.permutation_product_poly, blind: self.constructed.permutation_product_blind, - eval: self.permutation_product_eval, })) .chain(Some(ProverQuery { point: x_inv, poly: &self.constructed.permutation_product_poly, blind: self.constructed.permutation_product_blind, - eval: self.permutation_product_inv_eval, })) // Open permutation polynomial commitments at x - .chain(pkey.open(&self.permutation_evals, x)) + .chain(pkey.open(x)) } } diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 365ae55..3f2db38 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -304,53 +304,51 @@ pub fn create_proof, ConcreteCircuit: Circ .map(|p| p.evaluate(pk, x, transcript)) .collect::, _>>()?; - let instances = - iter::empty() - .chain(pk.vk.cs.advice_queries.iter().enumerate().map( - |(query_index, &(column, at))| ProverQuery { + let instances = iter::empty() + .chain( + pk.vk + .cs + .advice_queries + .iter() + .map(|&(column, at)| ProverQuery { point: domain.rotate_omega(*x, at), poly: &advice_polys[column.index()], blind: advice_blinds[column.index()], - eval: advice_evals[query_index], - }, - )) - .chain( - pk.vk - .cs - .aux_queries - .iter() - .enumerate() - .map(|(query_index, &(column, at))| ProverQuery { - point: domain.rotate_omega(*x, at), - poly: &aux_polys[column.index()], - blind: Blind::default(), - eval: aux_evals[query_index], - }), - ) - .chain( - pk.vk - .cs - .fixed_queries - .iter() - .enumerate() - .map(|(query_index, &(column, at))| ProverQuery { - point: domain.rotate_omega(*x, at), - poly: &pk.fixed_polys[column.index()], - blind: Blind::default(), - eval: fixed_evals[query_index], - }), - ) - // We query the h(X) polynomial at x - .chain(vanishing.open(x)) - .chain( - permutations - .iter() - .zip(pk.permutations.iter()) - .map(|(p, pkey)| p.open(pk, pkey, x)) - .into_iter() - .flatten(), - ) - .chain(lookups.iter().map(|p| p.open(pk, x)).into_iter().flatten()); + }), + ) + .chain( + pk.vk + .cs + .aux_queries + .iter() + .map(|&(column, at)| ProverQuery { + point: domain.rotate_omega(*x, at), + poly: &aux_polys[column.index()], + blind: Blind::default(), + }), + ) + .chain( + pk.vk + .cs + .fixed_queries + .iter() + .map(|&(column, at)| ProverQuery { + point: domain.rotate_omega(*x, at), + poly: &pk.fixed_polys[column.index()], + blind: Blind::default(), + }), + ) + // We query the h(X) polynomial at x + .chain(vanishing.open(x)) + .chain( + permutations + .iter() + .zip(pk.permutations.iter()) + .map(|(p, pkey)| p.open(pk, pkey, x)) + .into_iter() + .flatten(), + ) + .chain(lookups.iter().map(|p| p.open(pk, x)).into_iter().flatten()); multiopen::create_proof(params, transcript, instances).map_err(|_| Error::OpeningError) } diff --git a/src/plonk/vanishing/prover.rs b/src/plonk/vanishing/prover.rs index 6f182ac..30f11d1 100644 --- a/src/plonk/vanishing/prover.rs +++ b/src/plonk/vanishing/prover.rs @@ -17,7 +17,6 @@ pub(in crate::plonk) struct Constructed { pub(in crate::plonk) struct Evaluated { constructed: Constructed, - h_evals: Vec, } impl Argument { @@ -85,10 +84,7 @@ impl Constructed { .map_err(|_| Error::TranscriptError)?; } - Ok(Evaluated { - constructed: self, - h_evals, - }) + Ok(Evaluated { constructed: self }) } } @@ -101,12 +97,10 @@ impl Evaluated { .h_pieces .iter() .zip(self.constructed.h_blinds.iter()) - .zip(self.h_evals.iter()) - .map(move |((h_poly, h_blind), h_eval)| ProverQuery { + .map(move |(h_poly, h_blind)| ProverQuery { point: *x, poly: h_poly, blind: *h_blind, - eval: *h_eval, }) } } diff --git a/src/poly/multiopen.rs b/src/poly/multiopen.rs index 4dbb198..a8880e7 100644 --- a/src/poly/multiopen.rs +++ b/src/poly/multiopen.rs @@ -3,7 +3,6 @@ //! //! [halo]: https://eprint.iacr.org/2019/1021 -use ff::Field; use std::collections::{BTreeMap, BTreeSet}; use super::*; @@ -48,8 +47,6 @@ pub struct ProverQuery<'a, C: CurveAffine> { pub poly: &'a Polynomial, /// blinding factor of polynomial pub blind: commitment::Blind, - /// evaluation of polynomial at query point - pub eval: C::Scalar, } /// A polynomial query at a point @@ -63,14 +60,14 @@ pub struct VerifierQuery<'a, C: CurveAffine> { pub eval: C::Scalar, } -struct CommitmentData { +struct CommitmentData { commitment: T, set_index: usize, point_indices: Vec, evals: Vec, } -impl CommitmentData { +impl CommitmentData { fn new(commitment: T) -> Self { CommitmentData { commitment, @@ -83,21 +80,22 @@ impl CommitmentData { trait Query: Sized { type Commitment: PartialEq + Copy; + type Eval: Clone + Default; fn get_point(&self) -> F; - fn get_eval(&self) -> F; + fn get_eval(&self) -> Self::Eval; fn get_commitment(&self) -> Self::Commitment; } fn construct_intermediate_sets>( queries: I, -) -> (Vec>, Vec>) +) -> (Vec>, Vec>) where I: IntoIterator + Clone, { // Construct sets of unique commitments and corresponding information about // their queries. - let mut commitment_map: Vec> = vec![]; + let mut commitment_map: Vec> = vec![]; // Also construct mapping from a unique point to a point_index. This defines // an ordering on the points. @@ -151,7 +149,7 @@ where // Initialise empty evals vec for each unique commitment for commitment_data in commitment_map.iter_mut() { let len = commitment_data.point_indices.len(); - commitment_data.evals = vec![F::zero(); len]; + commitment_data.evals = vec![Q::Eval::default(); len]; } // Populate set_index, evals and points for each commitment using point_idx_sets @@ -203,6 +201,134 @@ where (commitment_map, point_sets) } +#[test] +fn test_roundtrip() { + use super::commitment::{Blind, Params}; + use crate::arithmetic::{eval_polynomial, Curve, FieldExt}; + use crate::pasta::{EqAffine, Fp}; + + const K: u32 = 4; + + let params: Params = Params::new(K); + let domain = EvaluationDomain::new(1, K); + + let mut ax = domain.empty_coeff(); + for (i, a) in ax.iter_mut().enumerate() { + *a = Fp::from(10 + i as u64); + } + + let mut bx = domain.empty_coeff(); + for (i, a) in bx.iter_mut().enumerate() { + *a = Fp::from(100 + i as u64); + } + + let mut cx = domain.empty_coeff(); + for (i, a) in cx.iter_mut().enumerate() { + *a = Fp::from(100 + i as u64); + } + + let blind = Blind(Fp::rand()); + + let a = params.commit(&ax, blind).to_affine(); + let b = params.commit(&bx, blind).to_affine(); + let c = params.commit(&cx, blind).to_affine(); + + let x = Fp::rand(); + let y = Fp::rand(); + let avx = eval_polynomial(&ax, x); + let bvx = eval_polynomial(&bx, x); + let cvy = eval_polynomial(&cx, y); + + let mut transcript = crate::transcript::DummyHashWrite::init(vec![], Field::one()); + create_proof( + ¶ms, + &mut transcript, + std::iter::empty() + .chain(Some(ProverQuery { + point: x, + poly: &ax, + blind, + })) + .chain(Some(ProverQuery { + point: x, + poly: &bx, + blind, + })) + .chain(Some(ProverQuery { + point: y, + poly: &cx, + blind, + })), + ) + .unwrap(); + let proof = transcript.finalize(); + + { + let mut proof = &proof[..]; + let mut transcript = crate::transcript::DummyHashRead::init(&mut proof, Field::one()); + let msm = params.empty_msm(); + + let guard = verify_proof( + ¶ms, + &mut transcript, + std::iter::empty() + .chain(Some(VerifierQuery { + point: x, + commitment: &a, + eval: avx, + })) + .chain(Some(VerifierQuery { + point: x, + commitment: &b, + eval: avx, // NB: wrong! + })) + .chain(Some(VerifierQuery { + point: y, + commitment: &c, + eval: cvy, + })), + msm, + ) + .unwrap(); + + // Should fail. + assert!(!guard.use_challenges().eval()); + } + + { + let mut proof = &proof[..]; + + let mut transcript = crate::transcript::DummyHashRead::init(&mut proof, Field::one()); + let msm = params.empty_msm(); + + let guard = verify_proof( + ¶ms, + &mut transcript, + std::iter::empty() + .chain(Some(VerifierQuery { + point: x, + commitment: &a, + eval: avx, + })) + .chain(Some(VerifierQuery { + point: x, + commitment: &b, + eval: bvx, + })) + .chain(Some(VerifierQuery { + point: y, + commitment: &c, + eval: cvy, + })), + msm, + ) + .unwrap(); + + // Should succeed. + assert!(guard.use_challenges().eval()); + } +} + #[cfg(test)] mod tests { use super::{construct_intermediate_sets, Query}; @@ -216,14 +342,15 @@ mod tests { eval: F, } - impl Query for MyQuery { + impl Query for MyQuery { type Commitment = usize; + type Eval = F; fn get_point(&self) -> F { - self.point + self.point.clone() } - fn get_eval(&self) -> F { - self.eval + fn get_eval(&self) -> Self::Eval { + self.eval.clone() } fn get_commitment(&self) -> Self::Commitment { self.commitment diff --git a/src/poly/multiopen/prover.rs b/src/poly/multiopen/prover.rs index b9f8744..1b9faf4 100644 --- a/src/poly/multiopen/prover.rs +++ b/src/poly/multiopen/prover.rs @@ -7,9 +7,7 @@ use super::{ Query, }; -use crate::arithmetic::{ - eval_polynomial, kate_division, lagrange_interpolate, Curve, CurveAffine, FieldExt, -}; +use crate::arithmetic::{eval_polynomial, kate_division, Curve, CurveAffine, FieldExt}; use crate::transcript::TranscriptWrite; use ff::Field; @@ -43,56 +41,36 @@ where 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; - } - }; + let mut accumulate = + |set_idx: usize, new_poly: &Polynomial, blind: Blind| { + 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; + }; 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; - } + .fold(None, |f_poly, (points, poly)| { let mut poly = points .iter() - .fold(poly, |poly, point| kate_division(&poly, *point)); + .fold(poly.clone().unwrap().values, |poly, point| { + kate_division(&poly, *point) + }); poly.resize(params.n as usize, C::Scalar::zero()); let poly = Polynomial { values: poly, @@ -153,13 +131,12 @@ impl<'a, C: CurveAffine> PartialEq for PolynomialPointer<'a, C> { impl<'a, C: CurveAffine> Query for ProverQuery<'a, C> { type Commitment = PolynomialPointer<'a, C>; + type Eval = (); fn get_point(&self) -> C::Scalar { self.point } - fn get_eval(&self) -> C::Scalar { - self.eval - } + fn get_eval(&self) -> () {} fn get_commitment(&self) -> Self::Commitment { PolynomialPointer { poly: self.poly, diff --git a/src/poly/multiopen/verifier.rs b/src/poly/multiopen/verifier.rs index d34426a..8ab7734 100644 --- a/src/poly/multiopen/verifier.rs +++ b/src/poly/multiopen/verifier.rs @@ -133,6 +133,7 @@ impl<'a, C> PartialEq for CommitmentPointer<'a, C> { impl<'a, C: CurveAffine> Query for VerifierQuery<'a, C> { type Commitment = CommitmentPointer<'a, C>; + type Eval = C::Scalar; fn get_point(&self) -> C::Scalar { self.point