Allow MSMs to be queried and not just raw commitments.

This allows us to avoid some interstitial arithmetic in the vanishing argument.
This commit is contained in:
Sean Bowe 2021-02-26 18:48:02 -07:00
parent f7ef626858
commit fd91b6b42c
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
6 changed files with 173 additions and 147 deletions

View File

@ -168,43 +168,43 @@ impl<C: CurveAffine> Evaluated<C> {
))
}
pub(in crate::plonk) fn queries<'a>(
&'a self,
vk: &'a VerifyingKey<C>,
pub(in crate::plonk) fn queries<'r, 'params: 'r>(
&'r self,
vk: &'r VerifyingKey<C>,
x: ChallengeX<C>,
) -> impl Iterator<Item = VerifierQuery<'a, C>> + Clone {
) -> impl Iterator<Item = VerifierQuery<'r, 'params, C>> + Clone {
let x_inv = vk.domain.rotate_omega(*x, Rotation(-1));
iter::empty()
// Open lookup product commitments at x
.chain(Some(VerifierQuery {
point: *x,
commitment: &self.committed.product_commitment,
eval: self.product_eval,
}))
.chain(Some(VerifierQuery::new_commitment(
&self.committed.product_commitment,
*x,
self.product_eval,
)))
// Open lookup input commitments at x
.chain(Some(VerifierQuery {
point: *x,
commitment: &self.committed.permuted.permuted_input_commitment,
eval: self.permuted_input_eval,
}))
.chain(Some(VerifierQuery::new_commitment(
&self.committed.permuted.permuted_input_commitment,
*x,
self.permuted_input_eval,
)))
// Open lookup table commitments at x
.chain(Some(VerifierQuery {
point: *x,
commitment: &self.committed.permuted.permuted_table_commitment,
eval: self.permuted_table_eval,
}))
.chain(Some(VerifierQuery::new_commitment(
&self.committed.permuted.permuted_table_commitment,
*x,
self.permuted_table_eval,
)))
// Open lookup input commitments at \omega^{-1} x
.chain(Some(VerifierQuery {
point: x_inv,
commitment: &self.committed.permuted.permuted_input_commitment,
eval: self.permuted_input_inv_eval,
}))
.chain(Some(VerifierQuery::new_commitment(
&self.committed.permuted.permuted_input_commitment,
x_inv,
self.permuted_input_inv_eval,
)))
// Open lookup product commitments at \omega^{-1} x
.chain(Some(VerifierQuery {
point: x_inv,
commitment: &self.committed.product_commitment,
eval: self.product_inv_eval,
}))
.chain(Some(VerifierQuery::new_commitment(
&self.committed.product_commitment,
x_inv,
self.product_inv_eval,
)))
}
}

View File

@ -128,35 +128,33 @@ impl<C: CurveAffine> Evaluated<C> {
}))
}
pub(in crate::plonk) fn queries<'a>(
&'a self,
vk: &'a plonk::VerifyingKey<C>,
vkey: &'a VerifyingKey<C>,
pub(in crate::plonk) fn queries<'r, 'params: 'r>(
&'r self,
vk: &'r plonk::VerifyingKey<C>,
vkey: &'r VerifyingKey<C>,
x: ChallengeX<C>,
) -> impl Iterator<Item = VerifierQuery<'a, C>> + Clone {
) -> impl Iterator<Item = VerifierQuery<'r, 'params, C>> + Clone {
let x_inv = vk.domain.rotate_omega(*x, Rotation(-1));
iter::empty()
// Open permutation product commitments at x and \omega^{-1} x
.chain(Some(VerifierQuery {
point: *x,
commitment: &self.permutation_product_commitment,
eval: self.permutation_product_eval,
}))
.chain(Some(VerifierQuery {
point: x_inv,
commitment: &self.permutation_product_commitment,
eval: self.permutation_product_inv_eval,
}))
.chain(Some(VerifierQuery::new_commitment(
&self.permutation_product_commitment,
*x,
self.permutation_product_eval,
)))
.chain(Some(VerifierQuery::new_commitment(
&self.permutation_product_commitment,
x_inv,
self.permutation_product_inv_eval,
)))
// Open permutation commitments for each permutation argument at x
.chain(
vkey.commitments
.iter()
.zip(self.permutation_evals.iter())
.map(move |(commitment, &eval)| VerifierQuery {
point: *x,
commitment,
eval,
.map(move |(commitment, &eval)| {
VerifierQuery::new_commitment(commitment, *x, eval)
}),
)
}

View File

@ -1,10 +1,12 @@
use ff::Field;
use group::{Curve, Group};
use crate::{
arithmetic::CurveAffine,
plonk::{Error, VerifyingKey},
poly::multiopen::VerifierQuery,
poly::{
commitment::{Params, MSM},
multiopen::VerifierQuery,
},
transcript::{read_n_points, EncodedChallenge, TranscriptRead},
};
@ -15,8 +17,8 @@ pub struct Committed<C: CurveAffine> {
h_commitments: Vec<C>,
}
pub struct Evaluated<C: CurveAffine> {
h_commitment: C,
pub struct Evaluated<'params, C: CurveAffine> {
h_commitment: MSM<'params, C>,
expected_h_eval: C::Scalar,
}
@ -34,23 +36,25 @@ impl<C: CurveAffine> Argument<C> {
}
impl<C: CurveAffine> Committed<C> {
pub(in crate::plonk) fn verify(
pub(in crate::plonk) fn verify<'params>(
self,
params: &'params Params<C>,
expressions: impl Iterator<Item = C::Scalar>,
y: ChallengeY<C>,
xn: C::Scalar,
) -> Result<Evaluated<C>, Error> {
) -> Result<Evaluated<'params, C>, Error> {
let expected_h_eval = expressions.fold(C::Scalar::zero(), |h_eval, v| h_eval * &*y + &v);
let expected_h_eval = expected_h_eval * ((xn - C::Scalar::one()).invert().unwrap());
let h_commitment = self
.h_commitments
.iter()
.rev()
.fold(C::CurveExt::identity(), |acc, eval| {
acc * xn + eval.to_curve()
})
.to_affine();
let h_commitment =
self.h_commitments
.iter()
.rev()
.fold(params.empty_msm(), |mut acc, commitment| {
acc.scale(xn);
acc.append_term(C::Scalar::one(), *commitment);
acc
});
Ok(Evaluated {
expected_h_eval,
@ -59,16 +63,19 @@ impl<C: CurveAffine> Committed<C> {
}
}
impl<C: CurveAffine> Evaluated<C> {
pub(in crate::plonk) fn queries(
&self,
impl<'params, C: CurveAffine> Evaluated<'params, C> {
pub(in crate::plonk) fn queries<'r>(
&'r self,
x: ChallengeX<C>,
) -> impl Iterator<Item = VerifierQuery<'_, C>> + Clone {
Some(VerifierQuery {
point: *x,
commitment: &self.h_commitment,
eval: self.expected_h_eval,
})
) -> impl Iterator<Item = VerifierQuery<'r, 'params, C>> + Clone
where
'params: 'r,
{
Some(VerifierQuery::new_msm(
&self.h_commitment,
*x,
self.expected_h_eval,
))
.into_iter()
}
}

View File

@ -13,13 +13,13 @@ use crate::poly::{
use crate::transcript::{read_n_points, read_n_scalars, EncodedChallenge, TranscriptRead};
/// Returns a boolean indicating whether or not the proof is valid
pub fn verify_proof<'a, C: CurveAffine, E: EncodedChallenge<C>, T: TranscriptRead<C, E>>(
params: &'a Params<C>,
pub fn verify_proof<'params, C: CurveAffine, E: EncodedChallenge<C>, T: TranscriptRead<C, E>>(
params: &'params Params<C>,
vk: &VerifyingKey<C>,
msm: MSM<'a, C>,
msm: MSM<'params, C>,
instance_commitments: &[&[C]],
transcript: &mut T,
) -> Result<Guard<'a, C, E>, Error> {
) -> Result<Guard<'params, C, E>, Error> {
// Check that instance_commitments matches the expected number of instance columns
for instance_commitments in instance_commitments.iter() {
if instance_commitments.len() != vk.cs.num_instance_columns {
@ -217,7 +217,7 @@ pub fn verify_proof<'a, C: CurveAffine, E: EncodedChallenge<C>, T: TranscriptRea
},
);
vanishing.verify(expressions, y, xn)?
vanishing.verify(params, expressions, y, xn)?
};
let queries = instance_commitments
@ -237,17 +237,21 @@ pub fn verify_proof<'a, C: CurveAffine, E: EncodedChallenge<C>, T: TranscriptRea
)| {
iter::empty()
.chain(vk.cs.instance_queries.iter().enumerate().map(
move |(query_index, &(column, at))| VerifierQuery {
point: vk.domain.rotate_omega(*x, at),
commitment: &instance_commitments[column.index()],
eval: instance_evals[query_index],
move |(query_index, &(column, at))| {
VerifierQuery::new_commitment(
&instance_commitments[column.index()],
vk.domain.rotate_omega(*x, at),
instance_evals[query_index],
)
},
))
.chain(vk.cs.advice_queries.iter().enumerate().map(
move |(query_index, &(column, at))| VerifierQuery {
point: vk.domain.rotate_omega(*x, at),
commitment: &advice_commitments[column.index()],
eval: advice_evals[query_index],
move |(query_index, &(column, at))| {
VerifierQuery::new_commitment(
&advice_commitments[column.index()],
vk.domain.rotate_omega(*x, at),
advice_evals[query_index],
)
},
))
.chain(
@ -270,10 +274,12 @@ pub fn verify_proof<'a, C: CurveAffine, E: EncodedChallenge<C>, T: TranscriptRea
.fixed_queries
.iter()
.enumerate()
.map(|(query_index, &(column, at))| VerifierQuery {
point: vk.domain.rotate_omega(*x, at),
commitment: &vk.fixed_commitments[column.index()],
eval: fixed_evals[query_index],
.map(|(query_index, &(column, at))| {
VerifierQuery::new_commitment(
&vk.fixed_commitments[column.index()],
vk.domain.rotate_omega(*x, at),
fixed_evals[query_index],
)
}),
)
.chain(vanishing.queries(x));

View File

@ -51,13 +51,55 @@ pub struct ProverQuery<'a, C: CurveAffine> {
/// A polynomial query at a point
#[derive(Debug, Clone)]
pub struct VerifierQuery<'a, C: CurveAffine> {
pub struct VerifierQuery<'r, 'params: 'r, C: CurveAffine> {
/// point at which polynomial is queried
pub point: C::Scalar,
point: C::Scalar,
/// commitment to polynomial
pub commitment: &'a C,
commitment: CommitmentReference<'r, 'params, C>,
/// evaluation of polynomial at query point
pub eval: C::Scalar,
eval: C::Scalar,
}
impl<'r, 'params: 'r, C: CurveAffine> VerifierQuery<'r, 'params, C> {
/// Create a new verifier query based on a commitment
pub fn new_commitment(commitment: &'r C, point: C::Scalar, eval: C::Scalar) -> Self {
VerifierQuery {
point,
eval,
commitment: CommitmentReference::Commitment(commitment),
}
}
/// Create a new verifier query based on a linear combination of commitments
pub fn new_msm(
msm: &'r commitment::MSM<'params, C>,
point: C::Scalar,
eval: C::Scalar,
) -> Self {
VerifierQuery {
point,
eval,
commitment: CommitmentReference::MSM(msm),
}
}
}
#[derive(Copy, Clone, Debug)]
enum CommitmentReference<'r, 'params: 'r, C: CurveAffine> {
Commitment(&'r C),
MSM(&'r commitment::MSM<'params, C>),
}
impl<'r, 'params: 'r, C: CurveAffine> PartialEq for CommitmentReference<'r, 'params, C> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(&CommitmentReference::Commitment(a), &CommitmentReference::Commitment(b)) => {
std::ptr::eq(a, b)
}
(&CommitmentReference::MSM(a), &CommitmentReference::MSM(b)) => std::ptr::eq(a, b),
_ => false,
}
}
}
struct CommitmentData<F, T: PartialEq> {
@ -279,21 +321,9 @@ fn test_roundtrip() {
&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,
})),
.chain(Some(VerifierQuery::new_commitment(&a, x, avx)))
.chain(Some(VerifierQuery::new_commitment(&b, x, avx))) // NB: wrong!
.chain(Some(VerifierQuery::new_commitment(&c, y, cvy))),
msm,
)
.unwrap();
@ -313,21 +343,9 @@ fn test_roundtrip() {
&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,
})),
.chain(Some(VerifierQuery::new_commitment(&a, x, avx)))
.chain(Some(VerifierQuery::new_commitment(&b, x, bvx)))
.chain(Some(VerifierQuery::new_commitment(&c, y, cvy))),
msm,
)
.unwrap();

View File

@ -5,8 +5,8 @@ use super::super::{
Error,
};
use super::{
construct_intermediate_sets, ChallengeX1, ChallengeX2, ChallengeX3, ChallengeX4, Query,
VerifierQuery,
construct_intermediate_sets, ChallengeX1, ChallengeX2, ChallengeX3, ChallengeX4,
CommitmentReference, Query, VerifierQuery,
};
use crate::arithmetic::{eval_polynomial, lagrange_interpolate, CurveAffine, FieldExt};
use crate::transcript::{EncodedChallenge, TranscriptRead};
@ -19,20 +19,20 @@ struct CommitmentData<C: CurveAffine> {
/// Verify a multi-opening proof
pub fn verify_proof<
'b,
'a: 'b,
'r,
'params: 'r,
I,
C: CurveAffine,
E: EncodedChallenge<C>,
T: TranscriptRead<C, E>,
>(
params: &'a Params<C>,
params: &'params Params<C>,
transcript: &mut T,
queries: I,
mut msm: MSM<'a, C>,
) -> Result<Guard<'a, C, E>, Error>
mut msm: MSM<'params, C>,
) -> Result<Guard<'params, C, E>, Error>
where
I: IntoIterator<Item = VerifierQuery<'b, C>> + Clone,
I: IntoIterator<Item = VerifierQuery<'r, 'params, C>> + Clone,
{
// Scale the MSM by a random factor to ensure that if the existing MSM
// has is_zero() == false then this argument won't be able to interfere
@ -61,7 +61,14 @@ where
{
let mut accumulate = |set_idx: usize, new_commitment, evals: Vec<C::Scalar>| {
q_commitments[set_idx].scale(*x_1);
q_commitments[set_idx].append_term(C::Scalar::one(), new_commitment);
match new_commitment {
CommitmentReference::Commitment(c) => {
q_commitments[set_idx].append_term(C::Scalar::one(), *c);
}
CommitmentReference::MSM(msm) => {
q_commitments[set_idx].add_msm(msm);
}
}
for (eval, set_eval) in evals.iter().zip(q_eval_sets[set_idx].iter_mut()) {
*set_eval *= &(*x_1);
*set_eval += eval;
@ -72,9 +79,9 @@ where
// For each set, we collapse each commitment's evals pointwise.
for commitment_data in commitment_map.into_iter() {
accumulate(
commitment_data.set_index, // set_idx,
*commitment_data.commitment.0, // commitment,
commitment_data.evals, // evals
commitment_data.set_index, // set_idx,
commitment_data.commitment, // commitment,
commitment_data.evals, // evals
);
}
}
@ -128,18 +135,8 @@ where
super::commitment::verify_proof(params, msm, transcript, *x_3, msm_eval)
}
#[doc(hidden)]
#[derive(Copy, Clone)]
pub struct CommitmentPointer<'a, C>(&'a C);
impl<'a, C> PartialEq for CommitmentPointer<'a, C> {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self.0, other.0)
}
}
impl<'a, C: CurveAffine> Query<C::Scalar> for VerifierQuery<'a, C> {
type Commitment = CommitmentPointer<'a, C>;
impl<'a, 'b, C: CurveAffine> Query<C::Scalar> for VerifierQuery<'a, 'b, C> {
type Commitment = CommitmentReference<'a, 'b, C>;
type Eval = C::Scalar;
fn get_point(&self) -> C::Scalar {
@ -149,6 +146,6 @@ impl<'a, C: CurveAffine> Query<C::Scalar> for VerifierQuery<'a, C> {
self.eval
}
fn get_commitment(&self) -> Self::Commitment {
CommitmentPointer(self.commitment)
self.commitment
}
}