mirror of
https://github.com/saymrwulf/pasta_curves-source.git
synced 2026-09-04 20:03:39 +00:00
Create multiopen abstraction
This commit is contained in:
parent
a8e0a5565e
commit
c3d0a172a7
7 changed files with 476 additions and 316 deletions
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
use crate::arithmetic::CurveAffine;
|
||||
use crate::poly::{
|
||||
commitment, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial,
|
||||
multiopen, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial,
|
||||
};
|
||||
use crate::transcript::Hasher;
|
||||
|
||||
|
|
@ -59,9 +59,7 @@ pub struct Proof<C: CurveAffine> {
|
|||
aux_evals: Vec<C::Scalar>,
|
||||
fixed_evals: Vec<C::Scalar>,
|
||||
h_evals: Vec<C::Scalar>,
|
||||
f_commitment: C,
|
||||
q_evals: Vec<C::Scalar>,
|
||||
opening: commitment::Proof<C>,
|
||||
multiopening: multiopen::Proof<C>,
|
||||
}
|
||||
|
||||
/// This is an error that could occur during proving or circuit synthesis.
|
||||
|
|
@ -96,7 +94,8 @@ impl<C: CurveAffine> VerifyingKey<C> {
|
|||
}
|
||||
}
|
||||
|
||||
fn hash_point<C: CurveAffine, H: Hasher<C::Base>>(
|
||||
/// Hash a point into transcript
|
||||
pub fn hash_point<C: CurveAffine, H: Hasher<C::Base>>(
|
||||
transcript: &mut H,
|
||||
point: &C,
|
||||
) -> Result<(), Error> {
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ use super::{
|
|||
hash_point, Error, Proof, ProvingKey,
|
||||
};
|
||||
use crate::arithmetic::{
|
||||
eval_polynomial, get_challenge_scalar, kate_division, parallelize, BatchInvert, Challenge,
|
||||
Curve, CurveAffine, Field,
|
||||
eval_polynomial, get_challenge_scalar, parallelize, BatchInvert, Challenge, Curve, CurveAffine,
|
||||
Field,
|
||||
};
|
||||
use crate::poly::{
|
||||
commitment::{self, Blind, Params},
|
||||
Coeff, LagrangeCoeff, Polynomial, Rotation,
|
||||
commitment::{Blind, Params},
|
||||
multiopen, Coeff, LagrangeCoeff, Polynomial, Rotation,
|
||||
};
|
||||
use crate::transcript::Hasher;
|
||||
|
||||
|
|
@ -457,194 +457,100 @@ impl<C: CurveAffine> Proof<C> {
|
|||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||
transcript.absorb(transcript_scalar_point);
|
||||
|
||||
let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
let mut instances: Vec<(
|
||||
usize,
|
||||
Polynomial<C::Scalar, Coeff>,
|
||||
Blind<C::Scalar>,
|
||||
C::Scalar,
|
||||
)> = Vec::with_capacity(
|
||||
meta.advice_queries.len()
|
||||
+ meta.aux_queries.len()
|
||||
+ meta.fixed_queries.len()
|
||||
+ h_pieces.len()
|
||||
+ permutation_product_polys.len()
|
||||
+ permutation_product_polys.len()
|
||||
+ pk.permutation_polys.len(),
|
||||
);
|
||||
|
||||
// Collapse openings at same points together into single openings using
|
||||
// x_4 challenge.
|
||||
let mut q_polys: Vec<Option<Polynomial<C::Scalar, Coeff>>> =
|
||||
vec![None; meta.rotations.len()];
|
||||
let mut q_blinds = vec![Blind(C::Scalar::zero()); meta.rotations.len()];
|
||||
let mut q_evals: Vec<_> = vec![C::Scalar::zero(); meta.rotations.len()];
|
||||
for (query_index, &(wire, ref at)) in meta.advice_queries.iter().enumerate() {
|
||||
let point_index = (*meta.rotations.get(at).unwrap()).0;
|
||||
let poly = advice_polys[wire.0].clone();
|
||||
let blind = advice_blinds[wire.0];
|
||||
let eval = advice_evals[query_index];
|
||||
instances.push((point_index, poly, blind, eval));
|
||||
}
|
||||
|
||||
for (query_index, &(wire, ref at)) in meta.aux_queries.iter().enumerate() {
|
||||
let point_index = (*meta.rotations.get(at).unwrap()).0;
|
||||
let poly = aux_polys[wire.0].clone();
|
||||
let blind = Blind::default();
|
||||
let eval = aux_evals[query_index];
|
||||
instances.push((point_index, poly, blind, eval));
|
||||
}
|
||||
|
||||
for (query_index, &(wire, ref at)) in meta.fixed_queries.iter().enumerate() {
|
||||
let point_index = (*meta.rotations.get(at).unwrap()).0;
|
||||
let poly = pk.fixed_polys[wire.0].clone();
|
||||
let blind = Blind::default();
|
||||
let eval = fixed_evals[query_index];
|
||||
instances.push((point_index, poly, blind, eval));
|
||||
}
|
||||
|
||||
// We query the h(X) polynomial at x_3
|
||||
let current_index = (*meta.rotations.get(&Rotation::default()).unwrap()).0;
|
||||
for ((h_poly, h_blind), h_eval) in h_pieces
|
||||
.into_iter()
|
||||
.zip(h_blinds.iter())
|
||||
.zip(h_evals.iter())
|
||||
{
|
||||
let mut accumulate =
|
||||
|point_index: usize, new_poly: &Polynomial<_, Coeff>, blind, eval| {
|
||||
q_polys[point_index]
|
||||
.as_mut()
|
||||
.map(|poly| {
|
||||
parallelize(poly, |q, start| {
|
||||
for (q, a) in q.iter_mut().zip(new_poly[start..].iter()) {
|
||||
*q *= &x_4;
|
||||
*q += a;
|
||||
}
|
||||
});
|
||||
})
|
||||
.or_else(|| {
|
||||
q_polys[point_index] = Some(new_poly.clone());
|
||||
Some(())
|
||||
});
|
||||
q_blinds[point_index] *= x_4;
|
||||
q_blinds[point_index] += blind;
|
||||
q_evals[point_index] *= &x_4;
|
||||
q_evals[point_index] += &eval;
|
||||
};
|
||||
instances.push((current_index, h_poly.clone(), *h_blind, *h_eval));
|
||||
}
|
||||
|
||||
for (query_index, &(wire, ref at)) in meta.advice_queries.iter().enumerate() {
|
||||
let point_index = (*meta.rotations.get(at).unwrap()).0;
|
||||
|
||||
accumulate(
|
||||
point_index,
|
||||
&advice_polys[wire.0],
|
||||
advice_blinds[wire.0],
|
||||
advice_evals[query_index],
|
||||
);
|
||||
}
|
||||
|
||||
for (query_index, &(wire, ref at)) in meta.aux_queries.iter().enumerate() {
|
||||
let point_index = (*meta.rotations.get(at).unwrap()).0;
|
||||
|
||||
accumulate(
|
||||
point_index,
|
||||
&aux_polys[wire.0],
|
||||
Blind::default(),
|
||||
aux_evals[query_index],
|
||||
);
|
||||
}
|
||||
|
||||
for (query_index, &(wire, ref at)) in meta.fixed_queries.iter().enumerate() {
|
||||
let point_index = (*meta.rotations.get(at).unwrap()).0;
|
||||
|
||||
accumulate(
|
||||
point_index,
|
||||
&pk.fixed_polys[wire.0],
|
||||
Blind::default(),
|
||||
fixed_evals[query_index],
|
||||
);
|
||||
}
|
||||
|
||||
// We query the h(X) polynomial at x_3
|
||||
let current_index = (*meta.rotations.get(&Rotation::default()).unwrap()).0;
|
||||
for ((h_poly, h_blind), h_eval) in h_pieces
|
||||
.into_iter()
|
||||
.zip(h_blinds.iter())
|
||||
.zip(h_evals.iter())
|
||||
// Handle permutation arguments, if any exist
|
||||
if !pk.vk.cs.permutations.is_empty() {
|
||||
// Open permutation product commitments at x_3
|
||||
for ((poly, blind), eval) in permutation_product_polys
|
||||
.iter()
|
||||
.zip(permutation_product_blinds.iter())
|
||||
.zip(permutation_product_evals.iter())
|
||||
{
|
||||
accumulate(current_index, &h_poly, *h_blind, *h_eval);
|
||||
instances.push((current_index, poly.clone(), *blind, *eval));
|
||||
}
|
||||
|
||||
// Handle permutation arguments, if any exist
|
||||
if !pk.vk.cs.permutations.is_empty() {
|
||||
// Open permutation product commitments at x_3
|
||||
for ((poly, blind), eval) in permutation_product_polys
|
||||
.iter()
|
||||
.zip(permutation_product_blinds.iter())
|
||||
.zip(permutation_product_evals.iter())
|
||||
{
|
||||
accumulate(current_index, poly, *blind, *eval);
|
||||
}
|
||||
// Open permutation polynomial commitments at x_3
|
||||
for (poly, eval) in pk
|
||||
.permutation_polys
|
||||
.iter()
|
||||
.zip(permutation_evals.iter())
|
||||
.flat_map(|(polys, evals)| polys.iter().zip(evals.iter()))
|
||||
{
|
||||
instances.push((current_index, poly.clone(), Blind::default(), *eval));
|
||||
}
|
||||
|
||||
// Open permutation polynomial commitments at x_3
|
||||
for (poly, eval) in pk
|
||||
.permutation_polys
|
||||
.iter()
|
||||
.zip(permutation_evals.iter())
|
||||
.flat_map(|(polys, evals)| polys.iter().zip(evals.iter()))
|
||||
{
|
||||
accumulate(current_index, poly, Blind::default(), *eval);
|
||||
}
|
||||
|
||||
let current_index = (*pk.vk.cs.rotations.get(&Rotation(-1)).unwrap()).0;
|
||||
// Open permutation product commitments at \omega^{-1} x_3
|
||||
for ((poly, blind), eval) in permutation_product_polys
|
||||
.iter()
|
||||
.zip(permutation_product_blinds.iter())
|
||||
.zip(permutation_product_inv_evals.iter())
|
||||
{
|
||||
accumulate(current_index, poly, *blind, *eval);
|
||||
}
|
||||
let current_index = (*pk.vk.cs.rotations.get(&Rotation(-1)).unwrap()).0;
|
||||
// Open permutation product commitments at \omega^{-1} x_3
|
||||
for ((poly, blind), eval) in permutation_product_polys
|
||||
.iter()
|
||||
.zip(permutation_product_blinds.iter())
|
||||
.zip(permutation_product_inv_evals.iter())
|
||||
{
|
||||
instances.push((current_index, poly.clone(), *blind, *eval));
|
||||
}
|
||||
}
|
||||
|
||||
let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
let mut f_poly: Option<Polynomial<C::Scalar, Coeff>> = None;
|
||||
let mut points: Vec<C::Scalar> = vec![C::Scalar::zero(); meta.rotations.len()];
|
||||
for (&row, &point_index) in meta.rotations.iter() {
|
||||
let mut poly = q_polys[point_index.0].as_ref().unwrap().clone();
|
||||
let point = domain.rotate_omega(x_3, row);
|
||||
poly[0] -= &q_evals[point_index.0];
|
||||
// TODO: change kate_division interface?
|
||||
let mut poly = kate_division(&poly[..], point);
|
||||
poly.push(C::Scalar::zero());
|
||||
let poly = domain.coeff_from_vec(poly);
|
||||
|
||||
f_poly = f_poly
|
||||
.map(|mut f_poly| {
|
||||
parallelize(&mut f_poly, |q, start| {
|
||||
for (q, a) in q.iter_mut().zip(poly[start..].iter()) {
|
||||
*q *= &x_5;
|
||||
*q += a;
|
||||
}
|
||||
});
|
||||
f_poly
|
||||
})
|
||||
.or_else(|| Some(poly));
|
||||
points[point_index.0] = domain.rotate_omega(x_3, row);
|
||||
}
|
||||
|
||||
let f_poly = f_poly.unwrap();
|
||||
let mut f_blind = Blind(C::Scalar::random());
|
||||
let mut f_commitment = params.commit(&f_poly, f_blind).to_affine();
|
||||
|
||||
let (opening, q_evals) = loop {
|
||||
let mut transcript = transcript.clone();
|
||||
let mut transcript_scalar = transcript_scalar.clone();
|
||||
hash_point(&mut transcript, &f_commitment)?;
|
||||
|
||||
let x_6: C::Scalar =
|
||||
get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
let mut q_evals = vec![C::Scalar::zero(); meta.rotations.len()];
|
||||
|
||||
for (_, &point_index) in meta.rotations.iter() {
|
||||
q_evals[point_index.0] =
|
||||
eval_polynomial(&q_polys[point_index.0].as_ref().unwrap(), x_6);
|
||||
}
|
||||
|
||||
for eval in q_evals.iter() {
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
let transcript_scalar_point =
|
||||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||
transcript.absorb(transcript_scalar_point);
|
||||
|
||||
let x_7: C::Scalar =
|
||||
get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
let mut f_blind_dup = f_blind;
|
||||
let mut f_poly = f_poly.clone();
|
||||
for (_, &point_index) in meta.rotations.iter() {
|
||||
f_blind_dup *= x_7;
|
||||
f_blind_dup += q_blinds[point_index.0];
|
||||
|
||||
parallelize(&mut f_poly, |f, start| {
|
||||
for (f, a) in f
|
||||
.iter_mut()
|
||||
.zip(q_polys[point_index.0].as_ref().unwrap()[start..].iter())
|
||||
{
|
||||
*f *= &x_7;
|
||||
*f += a;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Ok(opening) =
|
||||
commitment::Proof::create(¶ms, &mut transcript, &f_poly, f_blind_dup, x_6)
|
||||
{
|
||||
break (opening, q_evals);
|
||||
} else {
|
||||
f_blind += C::Scalar::one();
|
||||
f_commitment = (f_commitment + params.h).to_affine();
|
||||
}
|
||||
};
|
||||
let multiopening = multiopen::Proof::create(
|
||||
params,
|
||||
&mut transcript,
|
||||
&mut transcript_scalar,
|
||||
points,
|
||||
instances,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Ok(Proof {
|
||||
advice_commitments,
|
||||
|
|
@ -657,9 +563,7 @@ impl<C: CurveAffine> Proof<C> {
|
|||
fixed_evals,
|
||||
aux_evals,
|
||||
h_evals,
|
||||
f_commitment,
|
||||
q_evals,
|
||||
opening,
|
||||
multiopening,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use super::{hash_point, Error, Proof, VerifyingKey};
|
|||
use crate::arithmetic::{get_challenge_scalar, Challenge, CurveAffine, Field};
|
||||
use crate::poly::{
|
||||
commitment::{Guard, Params, MSM},
|
||||
multiopen::VerifierQuery,
|
||||
Rotation,
|
||||
};
|
||||
use crate::transcript::Hasher;
|
||||
|
|
@ -9,18 +10,21 @@ use crate::transcript::Hasher;
|
|||
impl<'a, C: CurveAffine> Proof<C> {
|
||||
/// Returns a boolean indicating whether or not the proof is valid
|
||||
pub fn verify<HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>(
|
||||
&self,
|
||||
&'a self,
|
||||
params: &'a Params<C>,
|
||||
vk: &VerifyingKey<C>,
|
||||
mut msm: MSM<'a, C>,
|
||||
aux_commitments: &[C],
|
||||
vk: &'a VerifyingKey<C>,
|
||||
msm: MSM<'a, C>,
|
||||
aux_commitments: &'a [C],
|
||||
) -> Result<Guard<'a, C>, Error> {
|
||||
self.check_lengths(vk, aux_commitments)?;
|
||||
|
||||
// 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
|
||||
// with it to make it true, with high probability.
|
||||
msm.scale(C::Scalar::random());
|
||||
// Check that aux_commitments matches the expected number of aux_wires
|
||||
// and self.aux_evals
|
||||
if aux_commitments.len() != vk.cs.num_aux_wires
|
||||
|| self.aux_evals.len() != vk.cs.num_aux_wires
|
||||
{
|
||||
return Err(Error::IncompatibleParams);
|
||||
}
|
||||
|
||||
// Create a transcript for obtaining Fiat-Shamir challenges.
|
||||
let mut transcript = HBase::init(C::Base::one());
|
||||
|
|
@ -83,134 +87,106 @@ impl<'a, C: CurveAffine> Proof<C> {
|
|||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||
transcript.absorb(transcript_scalar_point);
|
||||
|
||||
// Sample x_4 for compressing openings at the same points together
|
||||
let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
let mut queries: Vec<VerifierQuery<'a, C>> = Vec::new();
|
||||
|
||||
// Compress the commitments and expected evaluations at x_3 together
|
||||
// using the challenge x_4
|
||||
let mut q_commitments: Vec<_> = vec![params.empty_msm(); vk.cs.rotations.len()];
|
||||
let mut q_evals: Vec<_> = vec![C::Scalar::zero(); vk.cs.rotations.len()];
|
||||
for (query_index, &(wire, at)) in vk.cs.advice_queries.iter().enumerate() {
|
||||
let point = vk.domain.rotate_omega(x_3, at);
|
||||
queries.push(VerifierQuery {
|
||||
point,
|
||||
commitment: &self.advice_commitments[wire.0],
|
||||
eval: self.advice_evals[query_index],
|
||||
});
|
||||
}
|
||||
|
||||
for (query_index, &(wire, at)) in vk.cs.aux_queries.iter().enumerate() {
|
||||
let point = vk.domain.rotate_omega(x_3, at);
|
||||
queries.push(VerifierQuery {
|
||||
point,
|
||||
commitment: &aux_commitments[wire.0],
|
||||
eval: self.aux_evals[query_index],
|
||||
});
|
||||
}
|
||||
|
||||
for (query_index, &(wire, at)) in vk.cs.fixed_queries.iter().enumerate() {
|
||||
let point = vk.domain.rotate_omega(x_3, at);
|
||||
queries.push(VerifierQuery {
|
||||
point,
|
||||
commitment: &vk.fixed_commitments[wire.0],
|
||||
eval: self.fixed_evals[query_index],
|
||||
});
|
||||
}
|
||||
|
||||
for ((idx, _), &eval) in self
|
||||
.h_commitments
|
||||
.iter()
|
||||
.enumerate()
|
||||
.zip(self.h_evals.iter())
|
||||
{
|
||||
let mut accumulate = |point_index: usize, new_commitment, eval| {
|
||||
q_commitments[point_index].scale(x_4);
|
||||
q_commitments[point_index].add_term(C::Scalar::one(), new_commitment);
|
||||
q_evals[point_index] *= &x_4;
|
||||
q_evals[point_index] += &eval;
|
||||
};
|
||||
let commitment = &self.h_commitments[idx];
|
||||
queries.push(VerifierQuery {
|
||||
point: x_3,
|
||||
commitment,
|
||||
eval,
|
||||
});
|
||||
}
|
||||
|
||||
for (query_index, &(wire, ref at)) in vk.cs.advice_queries.iter().enumerate() {
|
||||
let point_index = (*vk.cs.rotations.get(at).unwrap()).0;
|
||||
accumulate(
|
||||
point_index,
|
||||
self.advice_commitments[wire.0],
|
||||
self.advice_evals[query_index],
|
||||
);
|
||||
// Handle permutation arguments, if any exist
|
||||
if !vk.cs.permutations.is_empty() {
|
||||
// Open permutation product commitments at x_3
|
||||
for ((idx, _), &eval) in self
|
||||
.permutation_product_commitments
|
||||
.iter()
|
||||
.enumerate()
|
||||
.zip(self.permutation_product_evals.iter())
|
||||
{
|
||||
let commitment = &self.permutation_product_commitments[idx];
|
||||
queries.push(VerifierQuery {
|
||||
point: x_3,
|
||||
commitment,
|
||||
eval,
|
||||
});
|
||||
}
|
||||
|
||||
for (query_index, &(wire, ref at)) in vk.cs.aux_queries.iter().enumerate() {
|
||||
let point_index = (*vk.cs.rotations.get(at).unwrap()).0;
|
||||
accumulate(
|
||||
point_index,
|
||||
aux_commitments[wire.0],
|
||||
self.aux_evals[query_index],
|
||||
);
|
||||
}
|
||||
|
||||
for (query_index, &(wire, ref at)) in vk.cs.fixed_queries.iter().enumerate() {
|
||||
let point_index = (*vk.cs.rotations.get(at).unwrap()).0;
|
||||
accumulate(
|
||||
point_index,
|
||||
vk.fixed_commitments[wire.0],
|
||||
self.fixed_evals[query_index],
|
||||
);
|
||||
}
|
||||
|
||||
let current_index = (*vk.cs.rotations.get(&Rotation::default()).unwrap()).0;
|
||||
for (commitment, eval) in self.h_commitments.iter().zip(self.h_evals.iter()) {
|
||||
accumulate(current_index, *commitment, *eval);
|
||||
}
|
||||
|
||||
// Handle permutation arguments, if any exist
|
||||
if !vk.cs.permutations.is_empty() {
|
||||
// Open permutation product commitments at x_3
|
||||
for (commitment, eval) in self
|
||||
.permutation_product_commitments
|
||||
.iter()
|
||||
.zip(self.permutation_product_evals.iter())
|
||||
{
|
||||
accumulate(current_index, *commitment, *eval);
|
||||
}
|
||||
// Open permutation commitments for each permutation argument at x_3
|
||||
for (commitment, eval) in vk
|
||||
.permutation_commitments
|
||||
.iter()
|
||||
.zip(self.permutation_evals.iter())
|
||||
.flat_map(|(commitments, evals)| commitments.iter().zip(evals.iter()))
|
||||
{
|
||||
accumulate(current_index, *commitment, *eval);
|
||||
}
|
||||
let current_index = (*vk.cs.rotations.get(&Rotation(-1)).unwrap()).0;
|
||||
// Open permutation product commitments at \omega^{-1} x_3
|
||||
for (commitment, eval) in self
|
||||
.permutation_product_commitments
|
||||
.iter()
|
||||
.zip(self.permutation_product_inv_evals.iter())
|
||||
{
|
||||
accumulate(current_index, *commitment, *eval);
|
||||
// Open permutation commitments for each permutation argument at x_3
|
||||
for outer_idx in 0..vk.permutation_commitments.len() {
|
||||
let inner_len = vk.permutation_commitments[outer_idx].len();
|
||||
for inner_idx in 0..inner_len {
|
||||
let commitment = &vk.permutation_commitments[outer_idx][inner_idx];
|
||||
let eval = self.permutation_evals[outer_idx][inner_idx];
|
||||
queries.push(VerifierQuery {
|
||||
point: x_3,
|
||||
commitment,
|
||||
eval,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Open permutation product commitments at \omega^{-1} x_3
|
||||
let x_3_inv = vk.domain.rotate_omega(x_3, Rotation(-1));
|
||||
for ((idx, _), &eval) in self
|
||||
.permutation_product_commitments
|
||||
.iter()
|
||||
.enumerate()
|
||||
.zip(self.permutation_product_inv_evals.iter())
|
||||
{
|
||||
let commitment = &self.permutation_product_commitments[idx];
|
||||
queries.push(VerifierQuery {
|
||||
point: x_3_inv,
|
||||
commitment,
|
||||
eval,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sample a challenge x_5 for keeping the multi-point quotient
|
||||
// polynomial terms linearly independent.
|
||||
let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Obtain the commitment to the multi-point quotient polynomial f(X).
|
||||
hash_point(&mut transcript, &self.f_commitment)?;
|
||||
|
||||
// Sample a challenge x_6 for checking that f(X) was committed to
|
||||
// correctly.
|
||||
let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
for eval in self.q_evals.iter() {
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
let transcript_scalar_point =
|
||||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||
transcript.absorb(transcript_scalar_point);
|
||||
|
||||
// We can compute the expected msm_eval at x_6 using the q_evals provided
|
||||
// by the prover and from x_5
|
||||
let mut msm_eval = C::Scalar::zero();
|
||||
for (&row, point_index) in vk.cs.rotations.iter() {
|
||||
let mut eval = self.q_evals[point_index.0];
|
||||
|
||||
let point = vk.domain.rotate_omega(x_3, row);
|
||||
eval = eval - &q_evals[point_index.0];
|
||||
eval = eval * &(x_6 - &point).invert().unwrap();
|
||||
|
||||
msm_eval *= &x_5;
|
||||
msm_eval += &eval;
|
||||
}
|
||||
|
||||
// Sample a challenge x_7 that we will use to collapse the openings of
|
||||
// the various remaining polynomials at x_6 together.
|
||||
let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Compute the final commitment that has to be opened
|
||||
let mut commitment_msm = params.empty_msm();
|
||||
commitment_msm.add_term(C::Scalar::one(), self.f_commitment);
|
||||
for (_, &point_index) in vk.cs.rotations.iter() {
|
||||
commitment_msm.scale(x_7);
|
||||
commitment_msm.add_msm(&q_commitments[point_index.0]);
|
||||
msm_eval *= &x_7;
|
||||
msm_eval += &self.q_evals[point_index.0];
|
||||
}
|
||||
|
||||
// Verify the opening proof
|
||||
self.opening
|
||||
.verify(params, msm, &mut transcript, x_6, commitment_msm, msm_eval)
|
||||
// We are now convinced the circuit is satisfied so long as the
|
||||
// polynomial commitments open to the correct values.
|
||||
self.multiopening
|
||||
.verify(
|
||||
params,
|
||||
&mut transcript,
|
||||
&mut transcript_scalar,
|
||||
queries,
|
||||
msm,
|
||||
)
|
||||
.map_err(|_| Error::OpeningError)
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +201,7 @@ impl<'a, C: CurveAffine> Proof<C> {
|
|||
return Err(Error::IncompatibleParams);
|
||||
}
|
||||
|
||||
if self.q_evals.len() != vk.cs.rotations.len() {
|
||||
if self.opening.q_evals.len() != vk.cs.rotations.len() {
|
||||
return Err(Error::IncompatibleParams);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use std::ops::{Add, Deref, DerefMut, Index, IndexMut, Mul, RangeFrom, RangeFull,
|
|||
|
||||
pub mod commitment;
|
||||
mod domain;
|
||||
pub mod multiopen;
|
||||
|
||||
pub use domain::*;
|
||||
|
||||
|
|
|
|||
23
src/poly/multiopen.rs
Normal file
23
src/poly/multiopen.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//! This module contains an implementation of the multipoint opening polynomial
|
||||
//! commitment scheme described in the [Halo][halo] paper.
|
||||
//!
|
||||
//! [halo]: https://eprint.iacr.org/2019/1021
|
||||
|
||||
use super::*;
|
||||
use crate::arithmetic::CurveAffine;
|
||||
|
||||
mod prover;
|
||||
mod verifier;
|
||||
|
||||
/// This is a multi-point opening proof used in the polynomial commitment scheme opening.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Proof<C: CurveAffine> {
|
||||
/// A vector of evaluations at each set of query points
|
||||
pub q_evals: Vec<C::Scalar>,
|
||||
|
||||
/// Commitment to final polynomial
|
||||
pub f_commitment: C,
|
||||
|
||||
/// Commitment proof
|
||||
pub opening: commitment::Proof<C>,
|
||||
}
|
||||
165
src/poly/multiopen/prover.rs
Normal file
165
src/poly/multiopen/prover.rs
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use super::super::{
|
||||
commitment::{self, Blind, Params},
|
||||
Coeff, Error, Polynomial,
|
||||
};
|
||||
use super::Proof;
|
||||
|
||||
use crate::arithmetic::{
|
||||
eval_polynomial, get_challenge_scalar, kate_division, parallelize, Challenge, Curve,
|
||||
CurveAffine, Field,
|
||||
};
|
||||
use crate::plonk::hash_point;
|
||||
use crate::transcript::Hasher;
|
||||
|
||||
impl<C: CurveAffine> Proof<C> {
|
||||
/// Create a multi-opening proof
|
||||
pub fn create<I, HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>(
|
||||
params: &Params<C>,
|
||||
transcript: &mut HBase,
|
||||
transcript_scalar: &mut HScalar,
|
||||
points: Vec<C::Scalar>,
|
||||
instances: I,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
I: IntoIterator<
|
||||
Item = (
|
||||
usize,
|
||||
Polynomial<C::Scalar, Coeff>,
|
||||
Blind<C::Scalar>,
|
||||
C::Scalar,
|
||||
),
|
||||
> + Clone,
|
||||
{
|
||||
let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Collapse openings at same points together into single openings using
|
||||
// x_4 challenge.
|
||||
let mut q_polys: Vec<Option<Polynomial<C::Scalar, Coeff>>> = vec![None; points.len()];
|
||||
let mut q_blinds = vec![Blind(C::Scalar::zero()); points.len()];
|
||||
let mut q_evals: Vec<_> = vec![C::Scalar::zero(); points.len()];
|
||||
{
|
||||
let mut accumulate =
|
||||
|point_index: usize, new_poly: Polynomial<C::Scalar, Coeff>, blind, eval| {
|
||||
q_polys[point_index]
|
||||
.as_mut()
|
||||
.map(|poly| {
|
||||
parallelize(poly, |q, start| {
|
||||
for (q, a) in q.iter_mut().zip(new_poly[start..].iter()) {
|
||||
*q *= &x_4;
|
||||
*q += a;
|
||||
}
|
||||
});
|
||||
})
|
||||
.or_else(|| {
|
||||
q_polys[point_index] = Some(new_poly.clone());
|
||||
Some(())
|
||||
});
|
||||
q_blinds[point_index] *= x_4;
|
||||
q_blinds[point_index] += blind;
|
||||
q_evals[point_index] *= &x_4;
|
||||
q_evals[point_index] += &eval;
|
||||
};
|
||||
|
||||
for instance in instances.clone() {
|
||||
accumulate(
|
||||
instance.0, // point_index,
|
||||
instance.1, // poly,
|
||||
instance.2, // blind,
|
||||
instance.3, // eval
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
let mut f_poly: Option<Polynomial<C::Scalar, Coeff>> = None;
|
||||
for (point_index, &point) in points.iter().enumerate() {
|
||||
let mut poly = q_polys[point_index].as_ref().unwrap().clone();
|
||||
poly[0] -= &q_evals[point_index];
|
||||
// TODO: change kate_division interface?
|
||||
let mut poly = kate_division(&poly[..], point);
|
||||
poly.push(C::Scalar::zero());
|
||||
let poly = Polynomial {
|
||||
values: poly,
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
||||
f_poly = f_poly
|
||||
.map(|mut f_poly| {
|
||||
parallelize(&mut f_poly, |q, start| {
|
||||
for (q, a) in q.iter_mut().zip(poly[start..].iter()) {
|
||||
*q *= &x_5;
|
||||
*q += a;
|
||||
}
|
||||
});
|
||||
f_poly
|
||||
})
|
||||
.or_else(|| Some(poly));
|
||||
}
|
||||
|
||||
let f_poly = f_poly.unwrap();
|
||||
let mut f_blind = Blind(C::Scalar::random());
|
||||
let mut f_commitment = params.commit(&f_poly, f_blind).to_affine();
|
||||
|
||||
let (opening, q_evals) = loop {
|
||||
let mut transcript = transcript.clone();
|
||||
let mut transcript_scalar = transcript_scalar.clone();
|
||||
hash_point(&mut transcript, &f_commitment).unwrap();
|
||||
|
||||
let x_6: C::Scalar =
|
||||
get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
let mut q_evals = vec![C::Scalar::zero(); points.len()];
|
||||
|
||||
for (point_index, _) in points.iter().enumerate() {
|
||||
q_evals[point_index] =
|
||||
eval_polynomial(&q_polys[point_index].as_ref().unwrap(), x_6);
|
||||
}
|
||||
|
||||
for eval in q_evals.iter() {
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
let transcript_scalar_point =
|
||||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||
transcript.absorb(transcript_scalar_point);
|
||||
|
||||
let x_7: C::Scalar =
|
||||
get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
let mut f_blind_dup = f_blind;
|
||||
let mut f_poly = f_poly.clone();
|
||||
for (point_index, _) in points.iter().enumerate() {
|
||||
f_blind_dup *= x_7;
|
||||
f_blind_dup += q_blinds[point_index];
|
||||
|
||||
parallelize(&mut f_poly, |f, start| {
|
||||
for (f, a) in f
|
||||
.iter_mut()
|
||||
.zip(q_polys[point_index].as_ref().unwrap()[start..].iter())
|
||||
{
|
||||
*f *= &x_7;
|
||||
*f += a;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Ok(opening) =
|
||||
commitment::Proof::create(¶ms, &mut transcript, &f_poly, f_blind_dup, x_6)
|
||||
{
|
||||
break (opening, q_evals);
|
||||
} else {
|
||||
f_blind += C::Scalar::one();
|
||||
f_commitment = (f_commitment + params.h).to_affine();
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Proof {
|
||||
q_evals,
|
||||
f_commitment,
|
||||
opening,
|
||||
})
|
||||
}
|
||||
}
|
||||
92
src/poly/multiopen/verifier.rs
Normal file
92
src/poly/multiopen/verifier.rs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
use super::super::commitment::{Params, MSM};
|
||||
use super::Proof;
|
||||
use crate::arithmetic::{get_challenge_scalar, Challenge, CurveAffine, Field};
|
||||
use crate::plonk::hash_point;
|
||||
use crate::transcript::Hasher;
|
||||
|
||||
impl<'a, C: CurveAffine> Proof<C> {
|
||||
/// Verify a multi-opening proof
|
||||
pub fn verify<I, HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>(
|
||||
&self,
|
||||
params: &'a Params<C>,
|
||||
transcript: &mut HBase,
|
||||
transcript_scalar: &mut HScalar,
|
||||
points: Vec<C::Scalar>,
|
||||
instances: I,
|
||||
) -> (C::Scalar, MSM<'a, C>, C::Scalar)
|
||||
where
|
||||
I: IntoIterator<Item = (usize, C, C::Scalar)> + Clone,
|
||||
{
|
||||
// Sample x_4 for compressing openings at the same points together
|
||||
let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Compress the commitments and expected evaluations at x_3 together
|
||||
// using the challenge x_4
|
||||
let mut q_commitments: Vec<_> = vec![params.empty_msm(); points.len()];
|
||||
let mut q_evals: Vec<_> = vec![C::Scalar::zero(); points.len()];
|
||||
{
|
||||
let mut accumulate = |point_index: usize, new_commitment, eval| {
|
||||
q_commitments[point_index].scale(x_4);
|
||||
q_commitments[point_index].add_term(C::Scalar::one(), new_commitment);
|
||||
q_evals[point_index] *= &x_4;
|
||||
q_evals[point_index] += &eval;
|
||||
};
|
||||
|
||||
for instance in instances.clone() {
|
||||
accumulate(
|
||||
instance.0, // point_index,
|
||||
instance.1, // commitment,
|
||||
instance.2, // eval,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Sample a challenge x_5 for keeping the multi-point quotient
|
||||
// polynomial terms linearly independent.
|
||||
let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Obtain the commitment to the multi-point quotient polynomial f(X).
|
||||
hash_point(transcript, &self.f_commitment).unwrap();
|
||||
|
||||
// Sample a challenge x_6 for checking that f(X) was committed to
|
||||
// correctly.
|
||||
let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
for eval in self.q_evals.iter() {
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
let transcript_scalar_point =
|
||||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||
transcript.absorb(transcript_scalar_point);
|
||||
|
||||
// We can compute the expected msm_eval at x_6 using the q_evals provided
|
||||
// by the prover and from x_5
|
||||
let mut msm_eval = C::Scalar::zero();
|
||||
for (point_index, point) in points.iter().enumerate() {
|
||||
let mut eval = self.q_evals[point_index];
|
||||
|
||||
eval = eval - &q_evals[point_index];
|
||||
eval = eval * &(x_6 - &point).invert().unwrap();
|
||||
|
||||
msm_eval *= &x_5;
|
||||
msm_eval += &eval;
|
||||
}
|
||||
|
||||
// Sample a challenge x_7 that we will use to collapse the openings of
|
||||
// the various remaining polynomials at x_6 together.
|
||||
let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Compute the final commitment that has to be opened
|
||||
let mut commitment_msm = params.empty_msm();
|
||||
commitment_msm.add_term(C::Scalar::one(), self.f_commitment);
|
||||
for (point_index, _) in points.iter().enumerate() {
|
||||
commitment_msm.scale(x_7);
|
||||
commitment_msm.add_msm(&q_commitments[point_index]);
|
||||
msm_eval *= &x_7;
|
||||
msm_eval += &self.q_evals[point_index];
|
||||
}
|
||||
|
||||
(x_6, commitment_msm, msm_eval)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue