Merge pull request #143 from zcash/multiproof

Multi-proof prover
This commit is contained in:
ebfull 2021-02-01 09:35:38 -07:00 committed by GitHub
commit a05f48be8f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 572 additions and 372 deletions

View file

@ -20,6 +20,7 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) {
// Initialize the polynomial commitment parameters // Initialize the polynomial commitment parameters
let params: Params<EqAffine> = Params::new(k); let params: Params<EqAffine> = Params::new(k);
#[derive(Copy, Clone)]
struct PLONKConfig { struct PLONKConfig {
a: Column<Advice>, a: Column<Advice>,
b: Column<Advice>, b: Column<Advice>,
@ -43,6 +44,7 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) {
fn copy(&mut self, a: Variable, b: Variable) -> Result<(), Error>; fn copy(&mut self, a: Variable, b: Variable) -> Result<(), Error>;
} }
#[derive(Clone)]
struct MyCircuit<F: FieldExt> { struct MyCircuit<F: FieldExt> {
a: Option<F>, a: Option<F>,
k: u32, k: u32,
@ -241,7 +243,7 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) {
// Create a proof // Create a proof
let mut transcript = DummyHashWrite::init(vec![], Fq::one()); let mut transcript = DummyHashWrite::init(vec![], Fq::one());
create_proof(&params, &pk, &circuit, &[], &mut transcript) create_proof(&params, &pk, &[circuit], &[], &mut transcript)
.expect("proof generation should not fail") .expect("proof generation should not fail")
}); });
}); });
@ -253,7 +255,7 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) {
// Create a proof // Create a proof
let mut transcript = DummyHashWrite::init(vec![], Fq::one()); let mut transcript = DummyHashWrite::init(vec![], Fq::one());
create_proof(&params, &pk, &circuit, &[], &mut transcript) create_proof(&params, &pk, &[circuit], &[], &mut transcript)
.expect("proof generation should not fail"); .expect("proof generation should not fail");
let proof = transcript.finalize(); let proof = transcript.finalize();

View file

@ -16,6 +16,7 @@ use std::marker::PhantomData;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct Variable(Column<Advice>, usize); pub struct Variable(Column<Advice>, usize);
#[derive(Copy, Clone)]
struct PLONKConfig { struct PLONKConfig {
a: Column<Advice>, a: Column<Advice>,
b: Column<Advice>, b: Column<Advice>,
@ -43,6 +44,7 @@ trait StandardCS<FF: FieldExt> {
F: FnOnce() -> Result<FF, Error>; F: FnOnce() -> Result<FF, Error>;
} }
#[derive(Clone)]
struct MyCircuit<F: FieldExt> { struct MyCircuit<F: FieldExt> {
a: Option<F>, a: Option<F>,
k: u32, k: u32,
@ -278,7 +280,7 @@ fn main() {
// Create a proof // Create a proof
let mut transcript = DummyHashWrite::init(vec![], Fq::one()); let mut transcript = DummyHashWrite::init(vec![], Fq::one());
create_proof(&params, &pk, &circuit, &[pubinputs], &mut transcript) create_proof(&params, &pk, &[circuit], &[&[pubinputs]], &mut transcript)
.expect("proof generation should not fail"); .expect("proof generation should not fail");
let proof: Vec<u8> = transcript.finalize(); let proof: Vec<u8> = transcript.finalize();
@ -288,7 +290,14 @@ fn main() {
let pubinput_slice = &[pubinput]; let pubinput_slice = &[pubinput];
let msm = params.empty_msm(); let msm = params.empty_msm();
let mut transcript = DummyHashRead::init(&proof[..], Fq::one()); let mut transcript = DummyHashRead::init(&proof[..], Fq::one());
let guard = verify_proof(&params, pk.get_vk(), msm, pubinput_slice, &mut transcript).unwrap(); let guard = verify_proof(
&params,
pk.get_vk(),
msm,
&[pubinput_slice],
&mut transcript,
)
.unwrap();
let msm = guard.clone().use_challenges(); let msm = guard.clone().use_challenges();
assert!(msm.eval()); assert!(msm.eval());

View file

@ -62,12 +62,14 @@ pub enum VerifyFailure {
/// }; /// };
/// const K: u32 = 5; /// const K: u32 = 5;
/// ///
/// #[derive(Copy, Clone)]
/// struct MyConfig { /// struct MyConfig {
/// a: Column<Advice>, /// a: Column<Advice>,
/// b: Column<Advice>, /// b: Column<Advice>,
/// c: Column<Advice>, /// c: Column<Advice>,
/// } /// }
/// ///
/// #[derive(Clone)]
/// struct MyCircuit { /// struct MyCircuit {
/// a: Option<u64>, /// a: Option<u64>,
/// b: Option<u64>, /// b: Option<u64>,

View file

@ -164,6 +164,7 @@ fn test_proving() {
// Initialize the polynomial commitment parameters // Initialize the polynomial commitment parameters
let params: Params<EqAffine> = Params::new(K); let params: Params<EqAffine> = Params::new(K);
#[derive(Copy, Clone)]
struct PLONKConfig { struct PLONKConfig {
a: Column<Advice>, a: Column<Advice>,
b: Column<Advice>, b: Column<Advice>,
@ -197,6 +198,7 @@ fn test_proving() {
fn lookup_table(&mut self, values: &[Vec<FF>]) -> Result<(), Error>; fn lookup_table(&mut self, values: &[Vec<FF>]) -> Result<(), Error>;
} }
#[derive(Clone)]
struct MyCircuit<F: FieldExt> { struct MyCircuit<F: FieldExt> {
a: Option<F>, a: Option<F>,
lookup_tables: Vec<Vec<F>>, lookup_tables: Vec<Vec<F>>,
@ -507,18 +509,25 @@ fn test_proving() {
create_proof( create_proof(
&params, &params,
&pk, &pk,
&circuit, &[circuit.clone(), circuit.clone()],
&[pubinputs.clone()], &[&[pubinputs.clone()], &[pubinputs.clone()]],
&mut transcript, &mut transcript,
) )
.expect("proof generation should not fail"); .expect("proof generation should not fail");
let proof: Vec<u8> = transcript.finalize(); let proof: Vec<u8> = transcript.finalize();
let pubinput_slice = &[pubinput]; let pubinput_slice = &[pubinput];
let pubinput_slice_copy = &[pubinput];
let msm = params.empty_msm(); let msm = params.empty_msm();
let mut transcript = DummyHashRead::init(&proof[..], Fq::one()); let mut transcript = DummyHashRead::init(&proof[..], Fq::one());
let guard = let guard = verify_proof(
verify_proof(&params, pk.get_vk(), msm, pubinput_slice, &mut transcript).unwrap(); &params,
pk.get_vk(),
msm,
&[pubinput_slice, pubinput_slice_copy],
&mut transcript,
)
.unwrap();
{ {
let msm = guard.clone().use_challenges(); let msm = guard.clone().use_challenges();
assert!(msm.eval()); assert!(msm.eval());
@ -535,7 +544,14 @@ fn test_proving() {
pk.get_vk().write(&mut vk_buffer).unwrap(); pk.get_vk().write(&mut vk_buffer).unwrap();
let vk = VerifyingKey::<EqAffine>::read::<_, MyCircuit<Fp>>(&mut &vk_buffer[..], &params) let vk = VerifyingKey::<EqAffine>::read::<_, MyCircuit<Fp>>(&mut &vk_buffer[..], &params)
.unwrap(); .unwrap();
let guard = verify_proof(&params, &vk, msm, pubinput_slice, &mut transcript).unwrap(); let guard = verify_proof(
&params,
&vk,
msm,
&[pubinput_slice, pubinput_slice_copy],
&mut transcript,
)
.unwrap();
{ {
let msm = guard.clone().use_challenges(); let msm = guard.clone().use_challenges();
assert!(msm.eval()); assert!(msm.eval());

View file

@ -158,7 +158,7 @@ pub trait Assignment<F: Field> {
/// [`ConstraintSystem`] implementation. /// [`ConstraintSystem`] implementation.
pub trait Circuit<F: Field> { pub trait Circuit<F: Field> {
/// This is a configuration object that stores things like columns. /// This is a configuration object that stores things like columns.
type Config; type Config: Copy;
/// The circuit is given an opportunity to describe the exact gate /// The circuit is given an opportunity to describe the exact gate
/// arrangement, column arrangement, etc. /// arrangement, column arrangement, etc.

View file

@ -1,7 +1,7 @@
use super::circuit::{Any, Column}; use super::circuit::{Any, Column};
mod prover; pub(crate) mod prover;
mod verifier; pub(crate) mod verifier;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct Argument { pub(crate) struct Argument {

View file

@ -7,8 +7,8 @@ use crate::{
}; };
pub(crate) mod keygen; pub(crate) mod keygen;
mod prover; pub(crate) mod prover;
mod verifier; pub(crate) mod verifier;
use std::io; use std::io;

View file

@ -3,14 +3,14 @@ use std::iter;
use super::{ use super::{
circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed}, circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed},
vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, lookup, permutation, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX,
ProvingKey, ChallengeY, Error, ProvingKey,
}; };
use crate::arithmetic::{eval_polynomial, Curve, CurveAffine, FieldExt}; use crate::arithmetic::{eval_polynomial, Curve, CurveAffine, FieldExt};
use crate::poly::{ use crate::poly::{
commitment::{Blind, Params}, commitment::{Blind, Params},
multiopen::{self, ProverQuery}, multiopen::{self, ProverQuery},
LagrangeCoeff, Polynomial, Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial,
}; };
use crate::transcript::TranscriptWrite; use crate::transcript::TranscriptWrite;
@ -20,50 +20,13 @@ use crate::transcript::TranscriptWrite;
pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circuit<C::Scalar>>( pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circuit<C::Scalar>>(
params: &Params<C>, params: &Params<C>,
pk: &ProvingKey<C>, pk: &ProvingKey<C>,
circuit: &ConcreteCircuit, circuits: &[ConcreteCircuit],
aux: &[Polynomial<C::Scalar, LagrangeCoeff>], auxs: &[&[Polynomial<C::Scalar, LagrangeCoeff>]],
transcript: &mut T, transcript: &mut T,
) -> Result<(), Error> { ) -> Result<(), Error> {
if aux.len() != pk.vk.cs.num_aux_columns { for aux in auxs.iter() {
return Err(Error::IncompatibleParams); if aux.len() != pk.vk.cs.num_aux_columns {
} return Err(Error::IncompatibleParams);
struct WitnessCollection<F: Field> {
advice: Vec<Polynomial<F, LagrangeCoeff>>,
_marker: std::marker::PhantomData<F>,
}
impl<F: Field> Assignment<F> for WitnessCollection<F> {
fn assign_advice(
&mut self,
column: Column<Advice>,
row: usize,
to: impl FnOnce() -> Result<F, Error>,
) -> Result<(), Error> {
*self
.advice
.get_mut(column.index())
.and_then(|v| v.get_mut(row))
.ok_or(Error::BoundsFailure)? = to()?;
Ok(())
}
fn assign_fixed(
&mut self,
_: Column<Fixed>,
_: usize,
_: impl FnOnce() -> Result<F, Error>,
) -> Result<(), Error> {
// We only care about advice columns here
Ok(())
}
fn copy(&mut self, _: usize, _: usize, _: usize, _: usize, _: usize) -> Result<(), Error> {
// We only care about advice columns here
Ok(())
} }
} }
@ -71,113 +34,199 @@ pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circ
let mut meta = ConstraintSystem::default(); let mut meta = ConstraintSystem::default();
let config = ConcreteCircuit::configure(&mut meta); let config = ConcreteCircuit::configure(&mut meta);
let mut witness = WitnessCollection { struct AuxSingle<'a, C: CurveAffine> {
advice: vec![domain.empty_lagrange(); meta.num_advice_columns], pub aux_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
_marker: std::marker::PhantomData, pub aux_polys: Vec<Polynomial<C::Scalar, Coeff>>,
}; pub aux_cosets: Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
// Synthesize the circuit to obtain the witness and other information.
circuit.synthesize(&mut witness, config)?;
let witness = witness;
// Compute commitments to aux column polynomials
let aux_commitments_projective: Vec<_> = aux
.iter()
.map(|poly| params.commit_lagrange(poly, Blind::default()))
.collect();
let mut aux_commitments = vec![C::zero(); aux_commitments_projective.len()];
C::Projective::batch_to_affine(&aux_commitments_projective, &mut aux_commitments);
let aux_commitments = aux_commitments;
drop(aux_commitments_projective);
metrics::counter!("aux_commitments", aux_commitments.len() as u64);
for commitment in &aux_commitments {
transcript
.common_point(*commitment)
.map_err(|_| Error::TranscriptError)?;
} }
let aux_polys: Vec<_> = aux let aux: Vec<AuxSingle<C>> = auxs
.iter() .iter()
.map(|poly| { .map(|aux| -> Result<AuxSingle<C>, Error> {
let lagrange_vec = domain.lagrange_from_vec(poly.to_vec()); let aux_commitments_projective: Vec<_> = aux
domain.lagrange_to_coeff(lagrange_vec) .iter()
.map(|poly| params.commit_lagrange(poly, Blind::default()))
.collect();
let mut aux_commitments = vec![C::zero(); aux_commitments_projective.len()];
C::Projective::batch_to_affine(&aux_commitments_projective, &mut aux_commitments);
let aux_commitments = aux_commitments;
drop(aux_commitments_projective);
metrics::counter!("aux_commitments", aux_commitments.len() as u64);
for commitment in &aux_commitments {
transcript
.common_point(*commitment)
.map_err(|_| Error::TranscriptError)?;
}
let aux_polys: Vec<_> = aux
.iter()
.map(|poly| {
let lagrange_vec = domain.lagrange_from_vec(poly.to_vec());
domain.lagrange_to_coeff(lagrange_vec)
})
.collect();
let aux_cosets: Vec<_> = meta
.aux_queries
.iter()
.map(|&(column, at)| {
let poly = aux_polys[column.index()].clone();
domain.coeff_to_extended(poly, at)
})
.collect();
Ok(AuxSingle {
aux_values: *aux,
aux_polys,
aux_cosets,
})
}) })
.collect(); .collect::<Result<Vec<_>, _>>()?;
let aux_cosets: Vec<_> = meta struct AdviceSingle<C: CurveAffine> {
.aux_queries pub advice_values: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
.iter() pub advice_polys: Vec<Polynomial<C::Scalar, Coeff>>,
.map(|&(column, at)| { pub advice_cosets: Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
let poly = aux_polys[column.index()].clone(); pub advice_blinds: Vec<Blind<C::Scalar>>,
domain.coeff_to_extended(poly, at)
})
.collect();
// Compute commitments to advice column polynomials
let advice_blinds: Vec<_> = witness
.advice
.iter()
.map(|_| Blind(C::Scalar::rand()))
.collect();
let advice_commitments_projective: Vec<_> = witness
.advice
.iter()
.zip(advice_blinds.iter())
.map(|(poly, blind)| params.commit_lagrange(poly, *blind))
.collect();
let mut advice_commitments = vec![C::zero(); advice_commitments_projective.len()];
C::Projective::batch_to_affine(&advice_commitments_projective, &mut advice_commitments);
let advice_commitments = advice_commitments;
drop(advice_commitments_projective);
metrics::counter!("advice_commitments", advice_commitments.len() as u64);
for commitment in &advice_commitments {
transcript
.write_point(*commitment)
.map_err(|_| Error::TranscriptError)?;
} }
let advice_polys: Vec<_> = witness let advice: Vec<AdviceSingle<C>> = circuits
.advice
.clone()
.into_iter()
.map(|poly| domain.lagrange_to_coeff(poly))
.collect();
let advice_cosets: Vec<_> = meta
.advice_queries
.iter() .iter()
.map(|&(column, at)| { .map(|circuit| -> Result<AdviceSingle<C>, Error> {
let poly = advice_polys[column.index()].clone(); struct WitnessCollection<F: Field> {
domain.coeff_to_extended(poly, at) pub advice: Vec<Polynomial<F, LagrangeCoeff>>,
_marker: std::marker::PhantomData<F>,
}
impl<F: Field> Assignment<F> for WitnessCollection<F> {
fn assign_advice(
&mut self,
column: Column<Advice>,
row: usize,
to: impl FnOnce() -> Result<F, Error>,
) -> Result<(), Error> {
*self
.advice
.get_mut(column.index())
.and_then(|v| v.get_mut(row))
.ok_or(Error::BoundsFailure)? = to()?;
Ok(())
}
fn assign_fixed(
&mut self,
_: Column<Fixed>,
_: usize,
_: impl FnOnce() -> Result<F, Error>,
) -> Result<(), Error> {
// We only care about advice columns here
Ok(())
}
fn copy(
&mut self,
_: usize,
_: usize,
_: usize,
_: usize,
_: usize,
) -> Result<(), Error> {
// We only care about advice columns here
Ok(())
}
}
let mut witness = WitnessCollection {
advice: vec![domain.empty_lagrange(); meta.num_advice_columns],
_marker: std::marker::PhantomData,
};
// Synthesize the circuit to obtain the witness and other information.
circuit.synthesize(&mut witness, config)?;
let witness = witness;
// Compute commitments to advice column polynomials
let advice_blinds: Vec<_> = witness
.advice
.iter()
.map(|_| Blind(C::Scalar::rand()))
.collect();
let advice_commitments_projective: Vec<_> = witness
.advice
.iter()
.zip(advice_blinds.iter())
.map(|(poly, blind)| params.commit_lagrange(poly, *blind))
.collect();
let mut advice_commitments = vec![C::zero(); advice_commitments_projective.len()];
C::Projective::batch_to_affine(&advice_commitments_projective, &mut advice_commitments);
let advice_commitments = advice_commitments;
drop(advice_commitments_projective);
metrics::counter!("advice_commitments", advice_commitments.len() as u64);
for commitment in &advice_commitments {
transcript
.write_point(*commitment)
.map_err(|_| Error::TranscriptError)?;
}
let advice_polys: Vec<_> = witness
.advice
.clone()
.into_iter()
.map(|poly| domain.lagrange_to_coeff(poly))
.collect();
let advice_cosets: Vec<_> = meta
.advice_queries
.iter()
.map(|&(column, at)| {
let poly = advice_polys[column.index()].clone();
domain.coeff_to_extended(poly, at)
})
.collect();
Ok(AdviceSingle {
advice_values: witness.advice,
advice_polys,
advice_cosets,
advice_blinds,
})
}) })
.collect(); .collect::<Result<Vec<_>, _>>()?;
// Sample theta challenge for keeping lookup columns linearly independent // Sample theta challenge for keeping lookup columns linearly independent
let theta = ChallengeTheta::get(transcript); let theta = ChallengeTheta::get(transcript);
// Construct and commit to permuted values for each lookup let lookups: Vec<Vec<lookup::prover::Permuted<'_, C>>> = aux
let lookups = pk
.vk
.cs
.lookups
.iter() .iter()
.map(|lookup| { .zip(advice.iter())
lookup.commit_permuted( .map(|(aux, advice)| -> Result<Vec<_>, Error> {
&pk, // Construct and commit to permuted values for each lookup
&params, pk.vk
&domain, .cs
theta, .lookups
&witness.advice, .iter()
&pk.fixed_values, .map(|lookup| {
&aux, lookup.commit_permuted(
&advice_cosets, &pk,
&pk.fixed_cosets, &params,
&aux_cosets, &domain,
transcript, theta,
) &advice.advice_values,
&pk.fixed_values,
&aux.aux_values,
&advice.advice_cosets,
&pk.fixed_cosets,
&aux.aux_cosets,
transcript,
)
})
.collect()
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
@ -187,89 +236,148 @@ pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circ
// Sample gamma challenge // Sample gamma challenge
let gamma = ChallengeGamma::get(transcript); let gamma = ChallengeGamma::get(transcript);
// Commit to permutations, if any. let permutations: Vec<Vec<permutation::prover::Committed<C>>> = advice
let permutations = pk
.vk
.cs
.permutations
.iter() .iter()
.zip(pk.permutations.iter()) .map(|advice| -> Result<Vec<_>, Error> {
.map(|(p, pkey)| p.commit(params, pk, pkey, &witness.advice, beta, gamma, transcript)) // Commit to permutations, if any.
pk.vk
.cs
.permutations
.iter()
.zip(pk.permutations.iter())
.map(|(p, pkey)| {
p.commit(
params,
pk,
pkey,
&advice.advice_values,
beta,
gamma,
transcript,
)
})
.collect()
})
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
// Construct and commit to products for each lookup let lookups: Vec<Vec<lookup::prover::Committed<'_, C>>> = lookups
let lookups = lookups
.into_iter() .into_iter()
.map(|lookup| lookup.commit_product(&pk, &params, theta, beta, gamma, transcript)) .map(|lookups| -> Result<Vec<_>, _> {
// Construct and commit to products for each lookup
lookups
.into_iter()
.map(|lookup| lookup.commit_product(&pk, &params, theta, beta, gamma, transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
// Obtain challenge for keeping all separate gates linearly independent // Obtain challenge for keeping all separate gates linearly independent
let y = ChallengeY::get(transcript); let y = ChallengeY::get(transcript);
// Evaluate the h(X) polynomial's constraint system expressions for the permutation constraints, if any. let (permutations, permutation_expressions): (Vec<Vec<_>>, Vec<Vec<_>>) = permutations
let (permutations, permutation_expressions): (Vec<_>, Vec<_>) = { .into_iter()
let tmp: Vec<_> = permutations .zip(advice.iter())
.into_iter() .map(|(permutations, advice)| {
.zip(pk.vk.cs.permutations.iter()) // Evaluate the h(X) polynomial's constraint system expressions for the permutation constraints, if any.
.zip(pk.permutations.iter()) let tmp: Vec<_> = permutations
.map(|((p, argument), pkey)| { .into_iter()
p.construct(pk, argument, pkey, &advice_cosets, beta, gamma) .zip(pk.vk.cs.permutations.iter())
}) .zip(pk.permutations.iter())
.collect(); .map(|((p, argument), pkey)| {
p.construct(pk, argument, pkey, &advice.advice_cosets, beta, gamma)
})
.collect();
tmp.into_iter().unzip() tmp.into_iter().unzip()
}; })
.unzip();
// Evaluate the h(X) polynomial's constraint system expressions for the lookup constraints, if any. let (lookups, lookup_expressions): (Vec<Vec<_>>, Vec<Vec<_>>) = lookups
let (lookups, lookup_expressions): (Vec<_>, Vec<_>) = { .into_iter()
let tmp: Vec<_> = lookups .map(|lookups| {
.into_iter() // Evaluate the h(X) polynomial's constraint system expressions for the lookup constraints, if any.
.map(|p| p.construct(pk, theta, beta, gamma)) let tmp: Vec<_> = lookups
.collect(); .into_iter()
.map(|p| p.construct(pk, theta, beta, gamma))
.collect();
tmp.into_iter().unzip() tmp.into_iter().unzip()
}; })
.unzip();
// Evaluate the h(X) polynomial's constraint system expressions for the constraints provided let expressions = advice
let expressions = iter::empty() .iter()
// Custom constraints .zip(aux.iter())
.chain(meta.gates.iter().map(|poly| { .zip(permutation_expressions.into_iter())
poly.evaluate( .zip(lookup_expressions.into_iter())
&|index| pk.fixed_cosets[index].clone(), .flat_map(
&|index| advice_cosets[index].clone(), |(((advice, aux), permutation_expressions), lookup_expressions)| {
&|index| aux_cosets[index].clone(), iter::empty()
&|a, b| a + &b, // Custom constraints
&|a, b| a * &b, .chain(meta.gates.iter().map(move |poly| {
&|a, scalar| a * scalar, poly.evaluate(
) &|index| pk.fixed_cosets[index].clone(),
})) &|index| advice.advice_cosets[index].clone(),
// Permutation constraints, if any. &|index| aux.aux_cosets[index].clone(),
.chain(permutation_expressions.into_iter().flatten()) &|a, b| a + &b,
// Lookup constraints, if any. &|a, b| a * &b,
.chain(lookup_expressions.into_iter().flatten()); &|a, scalar| a * scalar,
)
}))
// Permutation constraints, if any.
.chain(permutation_expressions.into_iter().flatten())
// Lookup constraints, if any.
.chain(lookup_expressions.into_iter().flatten())
},
);
// Construct the vanishing argument // Construct the vanishing argument
let vanishing = vanishing::Argument::construct(params, domain, expressions, y, transcript)?; let vanishing = vanishing::Argument::construct(params, domain, expressions, y, transcript)?;
let x = ChallengeX::get(transcript); let x = ChallengeX::get(transcript);
// Evaluate polynomials at omega^i x // Compute and hash aux evals for each circuit instance
let advice_evals: Vec<_> = meta for aux in aux.iter() {
.advice_queries // Evaluate polynomials at omega^i x
.iter() let aux_evals: Vec<_> = meta
.map(|&(column, at)| { .aux_queries
eval_polynomial(&advice_polys[column.index()], domain.rotate_omega(*x, at)) .iter()
}) .map(|&(column, at)| {
.collect(); eval_polynomial(&aux.aux_polys[column.index()], domain.rotate_omega(*x, at))
})
.collect();
let aux_evals: Vec<_> = meta // Hash each aux column evaluation
.aux_queries for eval in aux_evals.iter() {
.iter() transcript
.map(|&(column, at)| { .write_scalar(*eval)
eval_polynomial(&aux_polys[column.index()], domain.rotate_omega(*x, at)) .map_err(|_| Error::TranscriptError)?;
}) }
.collect(); }
// Compute and hash advice evals for each circuit instance
for advice in advice.iter() {
// Evaluate polynomials at omega^i x
let advice_evals: Vec<_> = meta
.advice_queries
.iter()
.map(|&(column, at)| {
eval_polynomial(
&advice.advice_polys[column.index()],
domain.rotate_omega(*x, at),
)
})
.collect();
// Hash each advice column evaluation
for eval in advice_evals.iter() {
transcript
.write_scalar(*eval)
.map_err(|_| Error::TranscriptError)?;
}
}
// Compute and hash fixed evals (shared across all circuit instances)
let fixed_evals: Vec<_> = meta let fixed_evals: Vec<_> = meta
.fixed_queries .fixed_queries
.iter() .iter()
@ -278,12 +386,8 @@ pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circ
}) })
.collect(); .collect();
// Hash each column evaluation // Hash each fixed column evaluation
for eval in advice_evals for eval in fixed_evals.iter() {
.iter()
.chain(aux_evals.iter())
.chain(fixed_evals.iter())
{
transcript transcript
.write_scalar(*eval) .write_scalar(*eval)
.map_err(|_| Error::TranscriptError)?; .map_err(|_| Error::TranscriptError)?;
@ -292,41 +396,66 @@ pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circ
let vanishing = vanishing.evaluate(x, transcript)?; let vanishing = vanishing.evaluate(x, transcript)?;
// Evaluate the permutations, if any, at omega^i x. // Evaluate the permutations, if any, at omega^i x.
let permutations = permutations let permutations: Vec<Vec<permutation::prover::Evaluated<C>>> = permutations
.into_iter() .into_iter()
.zip(pk.permutations.iter()) .map(|permutations| -> Result<Vec<_>, _> {
.map(|(p, pkey)| p.evaluate(pk, pkey, x, transcript)) permutations
.into_iter()
.zip(pk.permutations.iter())
.map(|(p, pkey)| p.evaluate(pk, pkey, x, transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
// Evaluate the lookups, if any, at omega^i x. // Evaluate the lookups, if any, at omega^i x.
let lookups = lookups let lookups: Vec<Vec<lookup::prover::Evaluated<C>>> = lookups
.into_iter() .into_iter()
.map(|p| p.evaluate(pk, x, transcript)) .map(|lookups| -> Result<Vec<_>, _> {
lookups
.into_iter()
.map(|p| p.evaluate(pk, x, transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let instances = iter::empty() let instances = aux
.chain( .iter()
pk.vk .zip(advice.iter())
.cs .zip(permutations.iter())
.advice_queries .zip(lookups.iter())
.iter() .flat_map(|(((aux, advice), permutations), lookups)| {
.map(|&(column, at)| ProverQuery { iter::empty()
point: domain.rotate_omega(*x, at), .chain(
poly: &advice_polys[column.index()], pk.vk
blind: advice_blinds[column.index()], .cs
}), .aux_queries
) .iter()
.chain( .map(move |&(column, at)| ProverQuery {
pk.vk point: domain.rotate_omega(*x, at),
.cs poly: &aux.aux_polys[column.index()],
.aux_queries blind: Blind::default(),
.iter() }),
.map(|&(column, at)| ProverQuery { )
point: domain.rotate_omega(*x, at), .chain(
poly: &aux_polys[column.index()], pk.vk
blind: Blind::default(), .cs
}), .advice_queries
) .iter()
.map(move |&(column, at)| ProverQuery {
point: domain.rotate_omega(*x, at),
poly: &advice.advice_polys[column.index()],
blind: advice.advice_blinds[column.index()],
}),
)
.chain(
permutations
.iter()
.zip(pk.permutations.iter())
.flat_map(move |(p, pkey)| p.open(pk, pkey, x))
.into_iter(),
)
.chain(lookups.iter().flat_map(move |p| p.open(pk, x)).into_iter())
})
.chain( .chain(
pk.vk pk.vk
.cs .cs
@ -339,16 +468,7 @@ pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circ
}), }),
) )
// We query the h(X) polynomial at x // We query the h(X) polynomial at x
.chain(vanishing.open(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) multiopen::create_proof(params, transcript, instances).map_err(|_| Error::OpeningError)
} }

View file

@ -17,34 +17,46 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead<C>>(
params: &'a Params<C>, params: &'a Params<C>,
vk: &VerifyingKey<C>, vk: &VerifyingKey<C>,
msm: MSM<'a, C>, msm: MSM<'a, C>,
aux_commitments: &[C], aux_commitments: &[&[C]],
transcript: &mut T, transcript: &mut T,
) -> Result<Guard<'a, C>, Error> { ) -> Result<Guard<'a, C>, Error> {
// Check that aux_commitments matches the expected number of aux columns // Check that aux_commitments matches the expected number of aux columns
if aux_commitments.len() != vk.cs.num_aux_columns { for aux_commitments in aux_commitments.iter() {
return Err(Error::IncompatibleParams); if aux_commitments.len() != vk.cs.num_aux_columns {
return Err(Error::IncompatibleParams);
}
} }
// Hash the aux (external) commitments into the transcript let num_proofs = aux_commitments.len();
for commitment in aux_commitments {
transcript for aux_commitments in aux_commitments.iter() {
.common_point(*commitment) // Hash the aux (external) commitments into the transcript
.map_err(|_| Error::TranscriptError)? for commitment in *aux_commitments {
transcript
.common_point(*commitment)
.map_err(|_| Error::TranscriptError)?
}
} }
// Hash the prover's advice commitments into the transcript let advice_commitments = (0..num_proofs)
let advice_commitments = .map(|_| -> Result<Vec<_>, _> {
read_n_points(transcript, vk.cs.num_advice_columns).map_err(|_| Error::TranscriptError)?; // Hash the prover's advice commitments into the transcript
read_n_points(transcript, vk.cs.num_advice_columns).map_err(|_| Error::TranscriptError)
})
.collect::<Result<Vec<_>, _>>()?;
// Sample theta challenge for keeping lookup columns linearly independent // Sample theta challenge for keeping lookup columns linearly independent
let theta = ChallengeTheta::get(transcript); let theta = ChallengeTheta::get(transcript);
// Hash each lookup permuted commitment let lookups_permuted = (0..num_proofs)
let lookups = vk .map(|_| -> Result<Vec<_>, _> {
.cs // Hash each lookup permuted commitment
.lookups vk.cs
.iter() .lookups
.map(|argument| argument.read_permuted_commitments(transcript)) .iter()
.map(|argument| argument.read_permuted_commitments(transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
// Sample beta challenge // Sample beta challenge
@ -53,18 +65,26 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead<C>>(
// Sample gamma challenge // Sample gamma challenge
let gamma = ChallengeGamma::get(transcript); let gamma = ChallengeGamma::get(transcript);
// Hash each permutation product commitment let permutations_committed = (0..num_proofs)
let permutations = vk .map(|_| -> Result<Vec<_>, _> {
.cs // Hash each permutation product commitment
.permutations vk.cs
.iter() .permutations
.map(|argument| argument.read_product_commitment(transcript)) .iter()
.map(|argument| argument.read_product_commitment(transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
// Hash each lookup product commitment let lookups_committed = lookups_permuted
let lookups = lookups
.into_iter() .into_iter()
.map(|lookup| lookup.read_product_commitment(transcript)) .map(|lookups| {
// Hash each lookup product commitment
lookups
.into_iter()
.map(|lookup| lookup.read_product_commitment(transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
// Sample y challenge, which keeps the gates linearly independent. // Sample y challenge, which keeps the gates linearly independent.
@ -76,24 +96,43 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead<C>>(
// satisfied with high probability. // satisfied with high probability.
let x = ChallengeX::get(transcript); let x = ChallengeX::get(transcript);
let advice_evals = read_n_scalars(transcript, vk.cs.advice_queries.len()) let aux_evals = (0..num_proofs)
.map_err(|_| Error::TranscriptError)?; .map(|_| -> Result<Vec<_>, _> {
let aux_evals = read_n_scalars(transcript, vk.cs.aux_queries.len()).map_err(|_| Error::TranscriptError)
read_n_scalars(transcript, vk.cs.aux_queries.len()).map_err(|_| Error::TranscriptError)?; })
.collect::<Result<Vec<_>, _>>()?;
let advice_evals = (0..num_proofs)
.map(|_| -> Result<Vec<_>, _> {
read_n_scalars(transcript, vk.cs.advice_queries.len())
.map_err(|_| Error::TranscriptError)
})
.collect::<Result<Vec<_>, _>>()?;
let fixed_evals = read_n_scalars(transcript, vk.cs.fixed_queries.len()) let fixed_evals = read_n_scalars(transcript, vk.cs.fixed_queries.len())
.map_err(|_| Error::TranscriptError)?; .map_err(|_| Error::TranscriptError)?;
let vanishing = vanishing.evaluate(transcript)?; let vanishing = vanishing.evaluate(transcript)?;
let permutations = permutations let permutations_evaluated = permutations_committed
.into_iter() .into_iter()
.zip(vk.permutations.iter()) .map(|permutations| -> Result<Vec<_>, _> {
.map(|(permutation, vkey)| permutation.evaluate(vkey, transcript)) permutations
.into_iter()
.zip(vk.permutations.iter())
.map(|(permutation, vkey)| permutation.evaluate(vkey, transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let lookups = lookups let lookups_evaluated = lookups_committed
.into_iter() .into_iter()
.map(|lookup| lookup.evaluate(transcript)) .map(|lookups| -> Result<Vec<_>, _> {
lookups
.into_iter()
.map(|lookup| lookup.evaluate(transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
// This check ensures the circuit is satisfied so long as the polynomial // This check ensures the circuit is satisfied so long as the polynomial
@ -109,74 +148,101 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead<C>>(
* &vk.domain.get_barycentric_weight(); // l_0(x) * &vk.domain.get_barycentric_weight(); // l_0(x)
// Compute the expected value of h(x) // Compute the expected value of h(x)
let expressions = std::iter::empty() let expressions = advice_evals
// Evaluate the circuit using the custom gates provided .iter()
.chain(vk.cs.gates.iter().map(|poly| { .zip(aux_evals.iter())
poly.evaluate( .zip(permutations_evaluated.iter())
&|index| fixed_evals[index], .zip(lookups_evaluated.iter())
&|index| advice_evals[index], .flat_map(|(((advice_evals, aux_evals), permutations), lookups)| {
&|index| aux_evals[index], let fixed_evals = fixed_evals.clone();
&|a, b| a + &b, let fixed_evals_copy = fixed_evals.clone();
&|a, b| a * &b,
&|a, scalar| a * &scalar, std::iter::empty()
) // Evaluate the circuit using the custom gates provided
})) .chain(vk.cs.gates.iter().map(move |poly| {
.chain( poly.evaluate(
permutations &|index| fixed_evals[index],
.iter() &|index| advice_evals[index],
.zip(vk.cs.permutations.iter()) &|index| aux_evals[index],
.map(|(p, argument)| { &|a, b| a + &b,
p.expressions(vk, argument, &advice_evals, l_0, beta, gamma, x) &|a, b| a * &b,
}) &|a, scalar| a * &scalar,
.into_iter()
.flatten(),
)
.chain(
lookups
.iter()
.zip(vk.cs.lookups.iter())
.map(|(p, argument)| {
p.expressions(
vk,
l_0,
argument,
theta,
beta,
gamma,
&advice_evals,
&fixed_evals,
&aux_evals,
) )
}) }))
.into_iter() .chain(
.flatten(), permutations
); .iter()
.zip(vk.cs.permutations.iter())
.flat_map(move |(p, argument)| {
p.expressions(vk, argument, &advice_evals, l_0, beta, gamma, x)
})
.into_iter(),
)
.chain(
lookups
.iter()
.zip(vk.cs.lookups.iter())
.flat_map(move |(p, argument)| {
p.expressions(
vk,
l_0,
argument,
theta,
beta,
gamma,
&advice_evals,
&fixed_evals_copy,
&aux_evals,
)
})
.into_iter(),
)
});
vanishing.verify(expressions, y, xn)?; vanishing.verify(expressions, y, xn)?;
} }
let queries = iter::empty() let queries = aux_commitments
.chain( .iter()
vk.cs .zip(aux_evals.iter())
.advice_queries .zip(advice_commitments.iter())
.iter() .zip(advice_evals.iter())
.enumerate() .zip(permutations_evaluated.iter())
.map(|(query_index, &(column, at))| VerifierQuery { .zip(lookups_evaluated.iter())
point: vk.domain.rotate_omega(*x, at), .flat_map(
commitment: &advice_commitments[column.index()], |(
eval: advice_evals[query_index], ((((aux_commitments, aux_evals), advice_commitments), advice_evals), permutations),
}), lookups,
) )| {
.chain( iter::empty()
vk.cs .chain(vk.cs.aux_queries.iter().enumerate().map(
.aux_queries move |(query_index, &(column, at))| VerifierQuery {
.iter() point: vk.domain.rotate_omega(*x, at),
.enumerate() commitment: &aux_commitments[column.index()],
.map(|(query_index, &(column, at))| VerifierQuery { eval: aux_evals[query_index],
point: vk.domain.rotate_omega(*x, at), },
commitment: &aux_commitments[column.index()], ))
eval: aux_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],
},
))
.chain(
permutations
.iter()
.zip(vk.permutations.iter())
.flat_map(move |(p, vkey)| p.queries(vk, vkey, x))
.into_iter(),
)
.chain(
lookups
.iter()
.flat_map(move |p| p.queries(vk, x))
.into_iter(),
)
},
) )
.chain( .chain(
vk.cs vk.cs
@ -189,22 +255,7 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead<C>>(
eval: fixed_evals[query_index], eval: fixed_evals[query_index],
}), }),
) )
.chain(vanishing.queries(x)) .chain(vanishing.queries(x));
.chain(
permutations
.iter()
.zip(vk.permutations.iter())
.map(|(p, vkey)| p.queries(vk, vkey, x))
.into_iter()
.flatten(),
)
.chain(
lookups
.iter()
.map(|p| p.queries(vk, x))
.into_iter()
.flatten(),
);
// We are now convinced the circuit is satisfied so long as the // We are now convinced the circuit is satisfied so long as the
// polynomial commitments open to the correct values. // polynomial commitments open to the correct values.