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> {
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 {
@ -465,14 +460,7 @@ impl<C: CurveAffine> Constructed<C> {
.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<C: CurveAffine> Evaluated<C> {
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,
}))
}
}

View File

@ -27,9 +27,6 @@ pub(crate) struct Constructed<C: CurveAffine> {
pub(crate) struct Evaluated<C: CurveAffine> {
constructed: Constructed<C>,
permutation_product_eval: C::Scalar,
permutation_product_inv_eval: C::Scalar,
permutation_evals: Vec<C::Scalar>,
}
impl Argument {
@ -218,20 +215,12 @@ impl<C: CurveAffine> super::ProvingKey<C> {
.collect()
}
fn open<'a>(
&'a self,
evals: &'a [C::Scalar],
x: ChallengeX<C>,
) -> impl Iterator<Item = ProverQuery<'a, C>> + 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<C>) -> impl Iterator<Item = ProverQuery<'a, C>> + Clone {
self.polys.iter().map(move |poly| ProverQuery {
point: *x,
poly,
blind: Blind::default(),
})
}
}
@ -265,12 +254,7 @@ impl<C: CurveAffine> Constructed<C> {
.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<C: CurveAffine> Evaluated<C> {
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))
}
}

View File

@ -304,53 +304,51 @@ pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circ
.map(|p| p.evaluate(pk, x, transcript))
.collect::<Result<Vec<_>, _>>()?;
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)
}

View File

@ -17,7 +17,6 @@ pub(in crate::plonk) struct Constructed<C: CurveAffine> {
pub(in crate::plonk) struct Evaluated<C: CurveAffine> {
constructed: Constructed<C>,
h_evals: Vec<C::Scalar>,
}
impl<C: CurveAffine> Argument<C> {
@ -85,10 +84,7 @@ impl<C: CurveAffine> Constructed<C> {
.map_err(|_| Error::TranscriptError)?;
}
Ok(Evaluated {
constructed: self,
h_evals,
})
Ok(Evaluated { constructed: self })
}
}
@ -101,12 +97,10 @@ impl<C: CurveAffine> Evaluated<C> {
.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,
})
}
}

View File

@ -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<C::Scalar, Coeff>,
/// blinding factor of polynomial
pub blind: commitment::Blind<C::Scalar>,
/// 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<F: Field, T: PartialEq> {
struct CommitmentData<F, T: PartialEq> {
commitment: T,
set_index: usize,
point_indices: Vec<usize>,
evals: Vec<F>,
}
impl<F: Field, T: PartialEq> CommitmentData<F, T> {
impl<F, T: PartialEq> CommitmentData<F, T> {
fn new(commitment: T) -> Self {
CommitmentData {
commitment,
@ -83,21 +80,22 @@ impl<F: Field, T: PartialEq> CommitmentData<F, T> {
trait Query<F>: 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<F: FieldExt, I, Q: Query<F>>(
queries: I,
) -> (Vec<CommitmentData<F, Q::Commitment>>, Vec<Vec<F>>)
) -> (Vec<CommitmentData<Q::Eval, Q::Commitment>>, Vec<Vec<F>>)
where
I: IntoIterator<Item = Q> + Clone,
{
// Construct sets of unique commitments and corresponding information about
// 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
// 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<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)]
mod tests {
use super::{construct_intermediate_sets, Query};
@ -216,14 +342,15 @@ mod tests {
eval: F,
}
impl<F: Copy> Query<F> for MyQuery<F> {
impl<F: Clone + Default> Query<F> for MyQuery<F> {
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

View File

@ -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<Option<Polynomial<C::Scalar, Coeff>>> = 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<C::Scalar, Coeff>,
blind: Blind<C::Scalar>,
evals: Vec<C::Scalar>| {
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<C::Scalar, Coeff>, blind: Blind<C::Scalar>| {
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<C::Scalar> 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,

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> {
type Commitment = CommitmentPointer<'a, C>;
type Eval = C::Scalar;
fn get_point(&self) -> C::Scalar {
self.point