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.
This commit is contained in:
Sean Bowe 2021-01-13 17:22:32 -07:00
parent ccca639591
commit ec2d8db8cb
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
7 changed files with 211 additions and 149 deletions

View File

@ -55,11 +55,6 @@ pub(in crate::plonk) struct Constructed<C: CurveAffine> {
pub(in crate::plonk) struct Evaluated<C: CurveAffine> { pub(in crate::plonk) struct Evaluated<C: CurveAffine> {
constructed: Constructed<C>, constructed: Constructed<C>,
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 { impl Argument {
@ -465,14 +460,7 @@ impl<C: CurveAffine> Constructed<C> {
.map_err(|_| Error::TranscriptError)?; .map_err(|_| Error::TranscriptError)?;
} }
Ok(Evaluated { Ok(Evaluated { constructed: self })
constructed: self,
product_eval,
product_inv_eval,
permuted_input_eval,
permuted_input_inv_eval,
permuted_table_eval,
})
} }
} }
@ -490,35 +478,30 @@ impl<C: CurveAffine> Evaluated<C> {
point: *x, point: *x,
poly: &self.constructed.product_poly, poly: &self.constructed.product_poly,
blind: self.constructed.product_blind, blind: self.constructed.product_blind,
eval: self.product_eval,
})) }))
// Open lookup input commitments at x // Open lookup input commitments at x
.chain(Some(ProverQuery { .chain(Some(ProverQuery {
point: *x, point: *x,
poly: &self.constructed.permuted_input_poly, poly: &self.constructed.permuted_input_poly,
blind: self.constructed.permuted_input_blind, blind: self.constructed.permuted_input_blind,
eval: self.permuted_input_eval,
})) }))
// Open lookup table commitments at x // Open lookup table commitments at x
.chain(Some(ProverQuery { .chain(Some(ProverQuery {
point: *x, point: *x,
poly: &self.constructed.permuted_table_poly, poly: &self.constructed.permuted_table_poly,
blind: self.constructed.permuted_table_blind, blind: self.constructed.permuted_table_blind,
eval: self.permuted_table_eval,
})) }))
// Open lookup input commitments at x_inv // Open lookup input commitments at x_inv
.chain(Some(ProverQuery { .chain(Some(ProverQuery {
point: x_inv, point: x_inv,
poly: &self.constructed.permuted_input_poly, poly: &self.constructed.permuted_input_poly,
blind: self.constructed.permuted_input_blind, blind: self.constructed.permuted_input_blind,
eval: self.permuted_input_inv_eval,
})) }))
// Open lookup product commitments at x_inv // Open lookup product commitments at x_inv
.chain(Some(ProverQuery { .chain(Some(ProverQuery {
point: x_inv, point: x_inv,
poly: &self.constructed.product_poly, poly: &self.constructed.product_poly,
blind: self.constructed.product_blind, blind: self.constructed.product_blind,
eval: self.product_inv_eval,
})) }))
} }
} }

View File

@ -27,9 +27,6 @@ pub(crate) struct Constructed<C: CurveAffine> {
pub(crate) struct Evaluated<C: CurveAffine> { pub(crate) struct Evaluated<C: CurveAffine> {
constructed: Constructed<C>, constructed: Constructed<C>,
permutation_product_eval: C::Scalar,
permutation_product_inv_eval: C::Scalar,
permutation_evals: Vec<C::Scalar>,
} }
impl Argument { impl Argument {
@ -218,20 +215,12 @@ impl<C: CurveAffine> super::ProvingKey<C> {
.collect() .collect()
} }
fn open<'a>( fn open<'a>(&'a self, x: ChallengeX<C>) -> impl Iterator<Item = ProverQuery<'a, C>> + Clone {
&'a self, self.polys.iter().map(move |poly| ProverQuery {
evals: &'a [C::Scalar], point: *x,
x: ChallengeX<C>, poly,
) -> impl Iterator<Item = ProverQuery<'a, C>> + Clone { blind: Blind::default(),
self.polys })
.iter()
.zip(evals.iter())
.map(move |(poly, eval)| ProverQuery {
point: *x,
poly,
blind: Blind::default(),
eval: *eval,
})
} }
} }
@ -265,12 +254,7 @@ impl<C: CurveAffine> Constructed<C> {
.map_err(|_| Error::TranscriptError)?; .map_err(|_| Error::TranscriptError)?;
} }
Ok(Evaluated { Ok(Evaluated { constructed: self })
constructed: self,
permutation_product_eval,
permutation_product_inv_eval,
permutation_evals,
})
} }
} }
@ -289,15 +273,13 @@ impl<C: CurveAffine> Evaluated<C> {
point: *x, point: *x,
poly: &self.constructed.permutation_product_poly, poly: &self.constructed.permutation_product_poly,
blind: self.constructed.permutation_product_blind, blind: self.constructed.permutation_product_blind,
eval: self.permutation_product_eval,
})) }))
.chain(Some(ProverQuery { .chain(Some(ProverQuery {
point: x_inv, point: x_inv,
poly: &self.constructed.permutation_product_poly, poly: &self.constructed.permutation_product_poly,
blind: self.constructed.permutation_product_blind, blind: self.constructed.permutation_product_blind,
eval: self.permutation_product_inv_eval,
})) }))
// Open permutation polynomial commitments at x // Open permutation polynomial commitments at x
.chain(pkey.open(&self.permutation_evals, x)) .chain(pkey.open(x))
} }
} }

View File

@ -304,53 +304,51 @@ pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circ
.map(|p| p.evaluate(pk, x, transcript)) .map(|p| p.evaluate(pk, x, transcript))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let instances = let instances = iter::empty()
iter::empty() .chain(
.chain(pk.vk.cs.advice_queries.iter().enumerate().map( pk.vk
|(query_index, &(column, at))| ProverQuery { .cs
.advice_queries
.iter()
.map(|&(column, at)| ProverQuery {
point: domain.rotate_omega(*x, at), point: domain.rotate_omega(*x, at),
poly: &advice_polys[column.index()], poly: &advice_polys[column.index()],
blind: advice_blinds[column.index()], blind: advice_blinds[column.index()],
eval: advice_evals[query_index], }),
}, )
)) .chain(
.chain( pk.vk
pk.vk .cs
.cs .aux_queries
.aux_queries .iter()
.iter() .map(|&(column, at)| ProverQuery {
.enumerate() point: domain.rotate_omega(*x, at),
.map(|(query_index, &(column, at))| ProverQuery { poly: &aux_polys[column.index()],
point: domain.rotate_omega(*x, at), blind: Blind::default(),
poly: &aux_polys[column.index()], }),
blind: Blind::default(), )
eval: aux_evals[query_index], .chain(
}), pk.vk
) .cs
.chain( .fixed_queries
pk.vk .iter()
.cs .map(|&(column, at)| ProverQuery {
.fixed_queries point: domain.rotate_omega(*x, at),
.iter() poly: &pk.fixed_polys[column.index()],
.enumerate() blind: Blind::default(),
.map(|(query_index, &(column, at))| ProverQuery { }),
point: domain.rotate_omega(*x, at), )
poly: &pk.fixed_polys[column.index()], // We query the h(X) polynomial at x
blind: Blind::default(), .chain(vanishing.open(x))
eval: fixed_evals[query_index], .chain(
}), permutations
) .iter()
// We query the h(X) polynomial at x .zip(pk.permutations.iter())
.chain(vanishing.open(x)) .map(|(p, pkey)| p.open(pk, pkey, x))
.chain( .into_iter()
permutations .flatten(),
.iter() )
.zip(pk.permutations.iter()) .chain(lookups.iter().map(|p| p.open(pk, x)).into_iter().flatten());
.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) multiopen::create_proof(params, transcript, instances).map_err(|_| Error::OpeningError)
} }

View File

@ -17,7 +17,6 @@ pub(in crate::plonk) struct Constructed<C: CurveAffine> {
pub(in crate::plonk) struct Evaluated<C: CurveAffine> { pub(in crate::plonk) struct Evaluated<C: CurveAffine> {
constructed: Constructed<C>, constructed: Constructed<C>,
h_evals: Vec<C::Scalar>,
} }
impl<C: CurveAffine> Argument<C> { impl<C: CurveAffine> Argument<C> {
@ -85,10 +84,7 @@ impl<C: CurveAffine> Constructed<C> {
.map_err(|_| Error::TranscriptError)?; .map_err(|_| Error::TranscriptError)?;
} }
Ok(Evaluated { Ok(Evaluated { constructed: self })
constructed: self,
h_evals,
})
} }
} }
@ -101,12 +97,10 @@ impl<C: CurveAffine> Evaluated<C> {
.h_pieces .h_pieces
.iter() .iter()
.zip(self.constructed.h_blinds.iter()) .zip(self.constructed.h_blinds.iter())
.zip(self.h_evals.iter()) .map(move |(h_poly, h_blind)| ProverQuery {
.map(move |((h_poly, h_blind), h_eval)| ProverQuery {
point: *x, point: *x,
poly: h_poly, poly: h_poly,
blind: *h_blind, blind: *h_blind,
eval: *h_eval,
}) })
} }
} }

View File

@ -3,7 +3,6 @@
//! //!
//! [halo]: https://eprint.iacr.org/2019/1021 //! [halo]: https://eprint.iacr.org/2019/1021
use ff::Field;
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
use super::*; use super::*;
@ -48,8 +47,6 @@ pub struct ProverQuery<'a, C: CurveAffine> {
pub poly: &'a Polynomial<C::Scalar, Coeff>, pub poly: &'a Polynomial<C::Scalar, Coeff>,
/// blinding factor of polynomial /// blinding factor of polynomial
pub blind: commitment::Blind<C::Scalar>, pub blind: commitment::Blind<C::Scalar>,
/// evaluation of polynomial at query point
pub eval: C::Scalar,
} }
/// A polynomial query at a point /// A polynomial query at a point
@ -63,14 +60,14 @@ pub struct VerifierQuery<'a, C: CurveAffine> {
pub eval: C::Scalar, pub eval: C::Scalar,
} }
struct CommitmentData<F: Field, T: PartialEq> { struct CommitmentData<F, T: PartialEq> {
commitment: T, commitment: T,
set_index: usize, set_index: usize,
point_indices: Vec<usize>, point_indices: Vec<usize>,
evals: Vec<F>, evals: Vec<F>,
} }
impl<F: Field, T: PartialEq> CommitmentData<F, T> { impl<F, T: PartialEq> CommitmentData<F, T> {
fn new(commitment: T) -> Self { fn new(commitment: T) -> Self {
CommitmentData { CommitmentData {
commitment, commitment,
@ -83,21 +80,22 @@ impl<F: Field, T: PartialEq> CommitmentData<F, T> {
trait Query<F>: Sized { trait Query<F>: Sized {
type Commitment: PartialEq + Copy; type Commitment: PartialEq + Copy;
type Eval: Clone + Default;
fn get_point(&self) -> F; fn get_point(&self) -> F;
fn get_eval(&self) -> F; fn get_eval(&self) -> Self::Eval;
fn get_commitment(&self) -> Self::Commitment; fn get_commitment(&self) -> Self::Commitment;
} }
fn construct_intermediate_sets<F: FieldExt, I, Q: Query<F>>( fn construct_intermediate_sets<F: FieldExt, I, Q: Query<F>>(
queries: I, queries: I,
) -> (Vec<CommitmentData<F, Q::Commitment>>, Vec<Vec<F>>) ) -> (Vec<CommitmentData<Q::Eval, Q::Commitment>>, Vec<Vec<F>>)
where where
I: IntoIterator<Item = Q> + Clone, I: IntoIterator<Item = Q> + Clone,
{ {
// Construct sets of unique commitments and corresponding information about // Construct sets of unique commitments and corresponding information about
// their queries. // their queries.
let mut commitment_map: Vec<CommitmentData<F, Q::Commitment>> = vec![]; let mut commitment_map: Vec<CommitmentData<Q::Eval, Q::Commitment>> = vec![];
// Also construct mapping from a unique point to a point_index. This defines // Also construct mapping from a unique point to a point_index. This defines
// an ordering on the points. // an ordering on the points.
@ -151,7 +149,7 @@ where
// Initialise empty evals vec for each unique commitment // Initialise empty evals vec for each unique commitment
for commitment_data in commitment_map.iter_mut() { for commitment_data in commitment_map.iter_mut() {
let len = commitment_data.point_indices.len(); 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 // Populate set_index, evals and points for each commitment using point_idx_sets
@ -203,6 +201,134 @@ where
(commitment_map, point_sets) (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<EqAffine> = 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(
&params,
&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(
&params,
&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(
&params,
&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)] #[cfg(test)]
mod tests { mod tests {
use super::{construct_intermediate_sets, Query}; use super::{construct_intermediate_sets, Query};
@ -216,14 +342,15 @@ mod tests {
eval: F, eval: F,
} }
impl<F: Copy> Query<F> for MyQuery<F> { impl<F: Clone + Default> Query<F> for MyQuery<F> {
type Commitment = usize; type Commitment = usize;
type Eval = F;
fn get_point(&self) -> F { fn get_point(&self) -> F {
self.point self.point.clone()
} }
fn get_eval(&self) -> F { fn get_eval(&self) -> Self::Eval {
self.eval self.eval.clone()
} }
fn get_commitment(&self) -> Self::Commitment { fn get_commitment(&self) -> Self::Commitment {
self.commitment self.commitment

View File

@ -7,9 +7,7 @@ use super::{
Query, Query,
}; };
use crate::arithmetic::{ use crate::arithmetic::{eval_polynomial, kate_division, Curve, CurveAffine, FieldExt};
eval_polynomial, kate_division, lagrange_interpolate, Curve, CurveAffine, FieldExt,
};
use crate::transcript::TranscriptWrite; use crate::transcript::TranscriptWrite;
use ff::Field; use ff::Field;
@ -43,56 +41,36 @@ where
let mut q_polys: Vec<Option<Polynomial<C::Scalar, Coeff>>> = vec![None; point_sets.len()]; let mut q_polys: Vec<Option<Polynomial<C::Scalar, Coeff>>> = vec![None; point_sets.len()];
let mut q_blinds = vec![Blind(C::Scalar::zero()); 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, let mut accumulate =
new_poly: &Polynomial<C::Scalar, Coeff>, |set_idx: usize, new_poly: &Polynomial<C::Scalar, Coeff>, blind: Blind<C::Scalar>| {
blind: Blind<C::Scalar>, if let Some(poly) = &q_polys[set_idx] {
evals: Vec<C::Scalar>| { q_polys[set_idx] = Some(poly.clone() * *x_1 + new_poly);
if let Some(poly) = &q_polys[set_idx] { } else {
q_polys[set_idx] = Some(poly.clone() * *x_1 + new_poly); q_polys[set_idx] = Some(new_poly.clone());
} else { }
q_polys[set_idx] = Some(new_poly.clone()); q_blinds[set_idx] *= *x_1;
} q_blinds[set_idx] += blind;
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() { for commitment_data in poly_map.into_iter() {
accumulate( accumulate(
commitment_data.set_index, // set_idx, commitment_data.set_index, // set_idx,
commitment_data.commitment.poly, // poly, commitment_data.commitment.poly, // poly,
commitment_data.commitment.blind, // blind, commitment_data.commitment.blind, // blind,
commitment_data.evals, // evals
); );
} }
} }
let f_poly = point_sets let f_poly = point_sets
.iter() .iter()
.zip(q_eval_sets.iter())
.zip(q_polys.iter()) .zip(q_polys.iter())
.fold(None, |f_poly, ((points, evals), poly)| { .fold(None, |f_poly, (points, 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 let mut poly = points
.iter() .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()); poly.resize(params.n as usize, C::Scalar::zero());
let poly = Polynomial { let poly = Polynomial {
values: poly, values: poly,
@ -153,13 +131,12 @@ impl<'a, C: CurveAffine> PartialEq for PolynomialPointer<'a, C> {
impl<'a, C: CurveAffine> Query<C::Scalar> for ProverQuery<'a, C> { impl<'a, C: CurveAffine> Query<C::Scalar> for ProverQuery<'a, C> {
type Commitment = PolynomialPointer<'a, C>; type Commitment = PolynomialPointer<'a, C>;
type Eval = ();
fn get_point(&self) -> C::Scalar { fn get_point(&self) -> C::Scalar {
self.point self.point
} }
fn get_eval(&self) -> C::Scalar { fn get_eval(&self) -> () {}
self.eval
}
fn get_commitment(&self) -> Self::Commitment { fn get_commitment(&self) -> Self::Commitment {
PolynomialPointer { PolynomialPointer {
poly: self.poly, poly: self.poly,

View File

@ -133,6 +133,7 @@ impl<'a, C> PartialEq for CommitmentPointer<'a, C> {
impl<'a, C: CurveAffine> Query<C::Scalar> for VerifierQuery<'a, C> { impl<'a, C: CurveAffine> Query<C::Scalar> for VerifierQuery<'a, C> {
type Commitment = CommitmentPointer<'a, C>; type Commitment = CommitmentPointer<'a, C>;
type Eval = C::Scalar;
fn get_point(&self) -> C::Scalar { fn get_point(&self) -> C::Scalar {
self.point self.point