diff --git a/benches/plonk.rs b/benches/plonk.rs index 06f6317..cdc9e6c 100644 --- a/benches/plonk.rs +++ b/benches/plonk.rs @@ -20,6 +20,7 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) { // Initialize the polynomial commitment parameters let params: Params = Params::new(k); + #[derive(Copy, Clone)] struct PLONKConfig { a: Column, b: Column, @@ -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>; } + #[derive(Clone)] struct MyCircuit { a: Option, k: u32, @@ -241,7 +243,7 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) { // Create a proof let mut transcript = DummyHashWrite::init(vec![], Fq::one()); - create_proof(¶ms, &pk, &circuit, &[], &mut transcript) + create_proof(¶ms, &pk, &[circuit], &[], &mut transcript) .expect("proof generation should not fail") }); }); @@ -253,7 +255,7 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) { // Create a proof let mut transcript = DummyHashWrite::init(vec![], Fq::one()); - create_proof(¶ms, &pk, &circuit, &[], &mut transcript) + create_proof(¶ms, &pk, &[circuit], &[], &mut transcript) .expect("proof generation should not fail"); let proof = transcript.finalize(); diff --git a/examples/performance_model.rs b/examples/performance_model.rs index 15be99b..c19246b 100644 --- a/examples/performance_model.rs +++ b/examples/performance_model.rs @@ -16,6 +16,7 @@ use std::marker::PhantomData; #[derive(Copy, Clone, Debug)] pub struct Variable(Column, usize); +#[derive(Copy, Clone)] struct PLONKConfig { a: Column, b: Column, @@ -43,6 +44,7 @@ trait StandardCS { F: FnOnce() -> Result; } +#[derive(Clone)] struct MyCircuit { a: Option, k: u32, @@ -278,7 +280,7 @@ fn main() { // Create a proof let mut transcript = DummyHashWrite::init(vec![], Fq::one()); - create_proof(¶ms, &pk, &circuit, &[pubinputs], &mut transcript) + create_proof(¶ms, &pk, &[circuit], &[&[pubinputs]], &mut transcript) .expect("proof generation should not fail"); let proof: Vec = transcript.finalize(); @@ -288,7 +290,14 @@ fn main() { let pubinput_slice = &[pubinput]; let msm = params.empty_msm(); let mut transcript = DummyHashRead::init(&proof[..], Fq::one()); - let guard = verify_proof(¶ms, pk.get_vk(), msm, pubinput_slice, &mut transcript).unwrap(); + let guard = verify_proof( + ¶ms, + pk.get_vk(), + msm, + &[pubinput_slice], + &mut transcript, + ) + .unwrap(); let msm = guard.clone().use_challenges(); assert!(msm.eval()); diff --git a/src/dev.rs b/src/dev.rs index 9e63e7a..17b5b10 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -62,12 +62,14 @@ pub enum VerifyFailure { /// }; /// const K: u32 = 5; /// +/// #[derive(Copy, Clone)] /// struct MyConfig { /// a: Column, /// b: Column, /// c: Column, /// } /// +/// #[derive(Clone)] /// struct MyCircuit { /// a: Option, /// b: Option, diff --git a/src/plonk.rs b/src/plonk.rs index 284e9cf..239d362 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -164,6 +164,7 @@ fn test_proving() { // Initialize the polynomial commitment parameters let params: Params = Params::new(K); + #[derive(Copy, Clone)] struct PLONKConfig { a: Column, b: Column, @@ -197,6 +198,7 @@ fn test_proving() { fn lookup_table(&mut self, values: &[Vec]) -> Result<(), Error>; } + #[derive(Clone)] struct MyCircuit { a: Option, lookup_tables: Vec>, @@ -507,18 +509,25 @@ fn test_proving() { create_proof( ¶ms, &pk, - &circuit, - &[pubinputs.clone()], + &[circuit.clone(), circuit.clone()], + &[&[pubinputs.clone()], &[pubinputs.clone()]], &mut transcript, ) .expect("proof generation should not fail"); let proof: Vec = transcript.finalize(); let pubinput_slice = &[pubinput]; + let pubinput_slice_copy = &[pubinput]; let msm = params.empty_msm(); let mut transcript = DummyHashRead::init(&proof[..], Fq::one()); - let guard = - verify_proof(¶ms, pk.get_vk(), msm, pubinput_slice, &mut transcript).unwrap(); + let guard = verify_proof( + ¶ms, + pk.get_vk(), + msm, + &[pubinput_slice, pubinput_slice_copy], + &mut transcript, + ) + .unwrap(); { let msm = guard.clone().use_challenges(); assert!(msm.eval()); @@ -535,7 +544,14 @@ fn test_proving() { pk.get_vk().write(&mut vk_buffer).unwrap(); let vk = VerifyingKey::::read::<_, MyCircuit>(&mut &vk_buffer[..], ¶ms) .unwrap(); - let guard = verify_proof(¶ms, &vk, msm, pubinput_slice, &mut transcript).unwrap(); + let guard = verify_proof( + ¶ms, + &vk, + msm, + &[pubinput_slice, pubinput_slice_copy], + &mut transcript, + ) + .unwrap(); { let msm = guard.clone().use_challenges(); assert!(msm.eval()); diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 33feaf7..25d97c1 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -158,7 +158,7 @@ pub trait Assignment { /// [`ConstraintSystem`] implementation. pub trait Circuit { /// 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 /// arrangement, column arrangement, etc. diff --git a/src/plonk/lookup.rs b/src/plonk/lookup.rs index 1f05569..acb76ff 100644 --- a/src/plonk/lookup.rs +++ b/src/plonk/lookup.rs @@ -1,7 +1,7 @@ use super::circuit::{Any, Column}; -mod prover; -mod verifier; +pub(crate) mod prover; +pub(crate) mod verifier; #[derive(Clone, Debug)] pub(crate) struct Argument { diff --git a/src/plonk/permutation.rs b/src/plonk/permutation.rs index 86cf917..89f178d 100644 --- a/src/plonk/permutation.rs +++ b/src/plonk/permutation.rs @@ -7,8 +7,8 @@ use crate::{ }; pub(crate) mod keygen; -mod prover; -mod verifier; +pub(crate) mod prover; +pub(crate) mod verifier; use std::io; diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index a7f1118..4bc6ce2 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -3,14 +3,14 @@ use std::iter; use super::{ circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed}, - vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, - ProvingKey, + lookup, permutation, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, + ChallengeY, Error, ProvingKey, }; use crate::arithmetic::{eval_polynomial, Curve, CurveAffine, FieldExt}; use crate::poly::{ commitment::{Blind, Params}, multiopen::{self, ProverQuery}, - LagrangeCoeff, Polynomial, + Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, }; use crate::transcript::TranscriptWrite; @@ -20,50 +20,13 @@ use crate::transcript::TranscriptWrite; pub fn create_proof, ConcreteCircuit: Circuit>( params: &Params, pk: &ProvingKey, - circuit: &ConcreteCircuit, - aux: &[Polynomial], + circuits: &[ConcreteCircuit], + auxs: &[&[Polynomial]], transcript: &mut T, ) -> Result<(), Error> { - if aux.len() != pk.vk.cs.num_aux_columns { - return Err(Error::IncompatibleParams); - } - - struct WitnessCollection { - advice: Vec>, - _marker: std::marker::PhantomData, - } - - impl Assignment for WitnessCollection { - fn assign_advice( - &mut self, - column: Column, - row: usize, - to: impl FnOnce() -> Result, - ) -> 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, - _: usize, - _: impl FnOnce() -> Result, - ) -> 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(()) + for aux in auxs.iter() { + if aux.len() != pk.vk.cs.num_aux_columns { + return Err(Error::IncompatibleParams); } } @@ -71,113 +34,199 @@ pub fn create_proof, ConcreteCircuit: Circ let mut meta = ConstraintSystem::default(); let config = ConcreteCircuit::configure(&mut meta); - 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 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)?; + struct AuxSingle<'a, C: CurveAffine> { + pub aux_values: &'a [Polynomial], + pub aux_polys: Vec>, + pub aux_cosets: Vec>, } - let aux_polys: Vec<_> = aux + let aux: Vec> = auxs .iter() - .map(|poly| { - let lagrange_vec = domain.lagrange_from_vec(poly.to_vec()); - domain.lagrange_to_coeff(lagrange_vec) + .map(|aux| -> Result, Error> { + 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 + .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::, _>>()?; - 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(); - - // 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)?; + struct AdviceSingle { + pub advice_values: Vec>, + pub advice_polys: Vec>, + pub advice_cosets: Vec>, + pub advice_blinds: Vec>, } - let advice_polys: Vec<_> = witness - .advice - .clone() - .into_iter() - .map(|poly| domain.lagrange_to_coeff(poly)) - .collect(); - - let advice_cosets: Vec<_> = meta - .advice_queries + let advice: Vec> = circuits .iter() - .map(|&(column, at)| { - let poly = advice_polys[column.index()].clone(); - domain.coeff_to_extended(poly, at) + .map(|circuit| -> Result, Error> { + struct WitnessCollection { + pub advice: Vec>, + _marker: std::marker::PhantomData, + } + + impl Assignment for WitnessCollection { + fn assign_advice( + &mut self, + column: Column, + row: usize, + to: impl FnOnce() -> Result, + ) -> 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, + _: usize, + _: impl FnOnce() -> Result, + ) -> 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::, _>>()?; // Sample theta challenge for keeping lookup columns linearly independent let theta = ChallengeTheta::get(transcript); - // Construct and commit to permuted values for each lookup - let lookups = pk - .vk - .cs - .lookups + let lookups: Vec>> = aux .iter() - .map(|lookup| { - lookup.commit_permuted( - &pk, - ¶ms, - &domain, - theta, - &witness.advice, - &pk.fixed_values, - &aux, - &advice_cosets, - &pk.fixed_cosets, - &aux_cosets, - transcript, - ) + .zip(advice.iter()) + .map(|(aux, advice)| -> Result, Error> { + // Construct and commit to permuted values for each lookup + pk.vk + .cs + .lookups + .iter() + .map(|lookup| { + lookup.commit_permuted( + &pk, + ¶ms, + &domain, + theta, + &advice.advice_values, + &pk.fixed_values, + &aux.aux_values, + &advice.advice_cosets, + &pk.fixed_cosets, + &aux.aux_cosets, + transcript, + ) + }) + .collect() }) .collect::, _>>()?; @@ -187,89 +236,148 @@ pub fn create_proof, ConcreteCircuit: Circ // Sample gamma challenge let gamma = ChallengeGamma::get(transcript); - // Commit to permutations, if any. - let permutations = pk - .vk - .cs - .permutations + let permutations: Vec>> = advice .iter() - .zip(pk.permutations.iter()) - .map(|(p, pkey)| p.commit(params, pk, pkey, &witness.advice, beta, gamma, transcript)) + .map(|advice| -> Result, Error> { + // 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::, _>>()?; - // Construct and commit to products for each lookup - let lookups = lookups + let lookups: Vec>> = lookups .into_iter() - .map(|lookup| lookup.commit_product(&pk, ¶ms, theta, beta, gamma, transcript)) + .map(|lookups| -> Result, _> { + // Construct and commit to products for each lookup + lookups + .into_iter() + .map(|lookup| lookup.commit_product(&pk, ¶ms, theta, beta, gamma, transcript)) + .collect::, _>>() + }) .collect::, _>>()?; // Obtain challenge for keeping all separate gates linearly independent 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<_>) = { - let tmp: Vec<_> = permutations - .into_iter() - .zip(pk.vk.cs.permutations.iter()) - .zip(pk.permutations.iter()) - .map(|((p, argument), pkey)| { - p.construct(pk, argument, pkey, &advice_cosets, beta, gamma) - }) - .collect(); + let (permutations, permutation_expressions): (Vec>, Vec>) = permutations + .into_iter() + .zip(advice.iter()) + .map(|(permutations, advice)| { + // Evaluate the h(X) polynomial's constraint system expressions for the permutation constraints, if any. + let tmp: Vec<_> = permutations + .into_iter() + .zip(pk.vk.cs.permutations.iter()) + .zip(pk.permutations.iter()) + .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<_>) = { - let tmp: Vec<_> = lookups - .into_iter() - .map(|p| p.construct(pk, theta, beta, gamma)) - .collect(); + let (lookups, lookup_expressions): (Vec>, Vec>) = lookups + .into_iter() + .map(|lookups| { + // Evaluate the h(X) polynomial's constraint system expressions for the lookup constraints, if any. + let tmp: Vec<_> = lookups + .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 = iter::empty() - // Custom constraints - .chain(meta.gates.iter().map(|poly| { - poly.evaluate( - &|index| pk.fixed_cosets[index].clone(), - &|index| advice_cosets[index].clone(), - &|index| aux_cosets[index].clone(), - &|a, b| a + &b, - &|a, b| a * &b, - &|a, scalar| a * scalar, - ) - })) - // Permutation constraints, if any. - .chain(permutation_expressions.into_iter().flatten()) - // Lookup constraints, if any. - .chain(lookup_expressions.into_iter().flatten()); + let expressions = advice + .iter() + .zip(aux.iter()) + .zip(permutation_expressions.into_iter()) + .zip(lookup_expressions.into_iter()) + .flat_map( + |(((advice, aux), permutation_expressions), lookup_expressions)| { + iter::empty() + // Custom constraints + .chain(meta.gates.iter().map(move |poly| { + poly.evaluate( + &|index| pk.fixed_cosets[index].clone(), + &|index| advice.advice_cosets[index].clone(), + &|index| aux.aux_cosets[index].clone(), + &|a, b| a + &b, + &|a, b| a * &b, + &|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 let vanishing = vanishing::Argument::construct(params, domain, expressions, y, transcript)?; let x = ChallengeX::get(transcript); - // Evaluate polynomials at omega^i x - let advice_evals: Vec<_> = meta - .advice_queries - .iter() - .map(|&(column, at)| { - eval_polynomial(&advice_polys[column.index()], domain.rotate_omega(*x, at)) - }) - .collect(); + // Compute and hash aux evals for each circuit instance + for aux in aux.iter() { + // Evaluate polynomials at omega^i x + let aux_evals: Vec<_> = meta + .aux_queries + .iter() + .map(|&(column, at)| { + eval_polynomial(&aux.aux_polys[column.index()], domain.rotate_omega(*x, at)) + }) + .collect(); - let aux_evals: Vec<_> = meta - .aux_queries - .iter() - .map(|&(column, at)| { - eval_polynomial(&aux_polys[column.index()], domain.rotate_omega(*x, at)) - }) - .collect(); + // Hash each aux column evaluation + for eval in aux_evals.iter() { + transcript + .write_scalar(*eval) + .map_err(|_| Error::TranscriptError)?; + } + } + // 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 .fixed_queries .iter() @@ -278,12 +386,8 @@ pub fn create_proof, ConcreteCircuit: Circ }) .collect(); - // Hash each column evaluation - for eval in advice_evals - .iter() - .chain(aux_evals.iter()) - .chain(fixed_evals.iter()) - { + // Hash each fixed column evaluation + for eval in fixed_evals.iter() { transcript .write_scalar(*eval) .map_err(|_| Error::TranscriptError)?; @@ -292,41 +396,66 @@ pub fn create_proof, ConcreteCircuit: Circ let vanishing = vanishing.evaluate(x, transcript)?; // Evaluate the permutations, if any, at omega^i x. - let permutations = permutations + let permutations: Vec>> = permutations .into_iter() - .zip(pk.permutations.iter()) - .map(|(p, pkey)| p.evaluate(pk, pkey, x, transcript)) + .map(|permutations| -> Result, _> { + permutations + .into_iter() + .zip(pk.permutations.iter()) + .map(|(p, pkey)| p.evaluate(pk, pkey, x, transcript)) + .collect::, _>>() + }) .collect::, _>>()?; // Evaluate the lookups, if any, at omega^i x. - let lookups = lookups + let lookups: Vec>> = lookups .into_iter() - .map(|p| p.evaluate(pk, x, transcript)) + .map(|lookups| -> Result, _> { + lookups + .into_iter() + .map(|p| p.evaluate(pk, x, transcript)) + .collect::, _>>() + }) .collect::, _>>()?; - 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()], - }), - ) - .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(), - }), - ) + let instances = aux + .iter() + .zip(advice.iter()) + .zip(permutations.iter()) + .zip(lookups.iter()) + .flat_map(|(((aux, advice), permutations), lookups)| { + iter::empty() + .chain( + pk.vk + .cs + .aux_queries + .iter() + .map(move |&(column, at)| ProverQuery { + point: domain.rotate_omega(*x, at), + poly: &aux.aux_polys[column.index()], + blind: Blind::default(), + }), + ) + .chain( + pk.vk + .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( pk.vk .cs @@ -339,16 +468,7 @@ pub fn create_proof, ConcreteCircuit: Circ }), ) // 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(vanishing.open(x)); multiopen::create_proof(params, transcript, instances).map_err(|_| Error::OpeningError) } diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 85c34c1..7525439 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -17,34 +17,46 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead>( params: &'a Params, vk: &VerifyingKey, msm: MSM<'a, C>, - aux_commitments: &[C], + aux_commitments: &[&[C]], transcript: &mut T, ) -> Result, Error> { // Check that aux_commitments matches the expected number of aux columns - if aux_commitments.len() != vk.cs.num_aux_columns { - return Err(Error::IncompatibleParams); + for aux_commitments in aux_commitments.iter() { + if aux_commitments.len() != vk.cs.num_aux_columns { + return Err(Error::IncompatibleParams); + } } - // Hash the aux (external) commitments into the transcript - for commitment in aux_commitments { - transcript - .common_point(*commitment) - .map_err(|_| Error::TranscriptError)? + let num_proofs = aux_commitments.len(); + + for aux_commitments in aux_commitments.iter() { + // Hash the aux (external) commitments into the transcript + 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 = - read_n_points(transcript, vk.cs.num_advice_columns).map_err(|_| Error::TranscriptError)?; + let advice_commitments = (0..num_proofs) + .map(|_| -> Result, _> { + // Hash the prover's advice commitments into the transcript + read_n_points(transcript, vk.cs.num_advice_columns).map_err(|_| Error::TranscriptError) + }) + .collect::, _>>()?; // Sample theta challenge for keeping lookup columns linearly independent let theta = ChallengeTheta::get(transcript); - // Hash each lookup permuted commitment - let lookups = vk - .cs - .lookups - .iter() - .map(|argument| argument.read_permuted_commitments(transcript)) + let lookups_permuted = (0..num_proofs) + .map(|_| -> Result, _> { + // Hash each lookup permuted commitment + vk.cs + .lookups + .iter() + .map(|argument| argument.read_permuted_commitments(transcript)) + .collect::, _>>() + }) .collect::, _>>()?; // Sample beta challenge @@ -53,18 +65,26 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead>( // Sample gamma challenge let gamma = ChallengeGamma::get(transcript); - // Hash each permutation product commitment - let permutations = vk - .cs - .permutations - .iter() - .map(|argument| argument.read_product_commitment(transcript)) + let permutations_committed = (0..num_proofs) + .map(|_| -> Result, _> { + // Hash each permutation product commitment + vk.cs + .permutations + .iter() + .map(|argument| argument.read_product_commitment(transcript)) + .collect::, _>>() + }) .collect::, _>>()?; - // Hash each lookup product commitment - let lookups = lookups + let lookups_committed = lookups_permuted .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::, _>>() + }) .collect::, _>>()?; // Sample y challenge, which keeps the gates linearly independent. @@ -76,24 +96,43 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead>( // satisfied with high probability. let x = ChallengeX::get(transcript); - let advice_evals = read_n_scalars(transcript, vk.cs.advice_queries.len()) - .map_err(|_| Error::TranscriptError)?; - let aux_evals = - read_n_scalars(transcript, vk.cs.aux_queries.len()).map_err(|_| Error::TranscriptError)?; + let aux_evals = (0..num_proofs) + .map(|_| -> Result, _> { + read_n_scalars(transcript, vk.cs.aux_queries.len()).map_err(|_| Error::TranscriptError) + }) + .collect::, _>>()?; + + let advice_evals = (0..num_proofs) + .map(|_| -> Result, _> { + read_n_scalars(transcript, vk.cs.advice_queries.len()) + .map_err(|_| Error::TranscriptError) + }) + .collect::, _>>()?; + let fixed_evals = read_n_scalars(transcript, vk.cs.fixed_queries.len()) .map_err(|_| Error::TranscriptError)?; let vanishing = vanishing.evaluate(transcript)?; - let permutations = permutations + let permutations_evaluated = permutations_committed .into_iter() - .zip(vk.permutations.iter()) - .map(|(permutation, vkey)| permutation.evaluate(vkey, transcript)) + .map(|permutations| -> Result, _> { + permutations + .into_iter() + .zip(vk.permutations.iter()) + .map(|(permutation, vkey)| permutation.evaluate(vkey, transcript)) + .collect::, _>>() + }) .collect::, _>>()?; - let lookups = lookups + let lookups_evaluated = lookups_committed .into_iter() - .map(|lookup| lookup.evaluate(transcript)) + .map(|lookups| -> Result, _> { + lookups + .into_iter() + .map(|lookup| lookup.evaluate(transcript)) + .collect::, _>>() + }) .collect::, _>>()?; // 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>( * &vk.domain.get_barycentric_weight(); // l_0(x) // Compute the expected value of h(x) - let expressions = std::iter::empty() - // Evaluate the circuit using the custom gates provided - .chain(vk.cs.gates.iter().map(|poly| { - poly.evaluate( - &|index| fixed_evals[index], - &|index| advice_evals[index], - &|index| aux_evals[index], - &|a, b| a + &b, - &|a, b| a * &b, - &|a, scalar| a * &scalar, - ) - })) - .chain( - permutations - .iter() - .zip(vk.cs.permutations.iter()) - .map(|(p, argument)| { - p.expressions(vk, argument, &advice_evals, l_0, beta, gamma, x) - }) - .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, + let expressions = advice_evals + .iter() + .zip(aux_evals.iter()) + .zip(permutations_evaluated.iter()) + .zip(lookups_evaluated.iter()) + .flat_map(|(((advice_evals, aux_evals), permutations), lookups)| { + let fixed_evals = fixed_evals.clone(); + let fixed_evals_copy = fixed_evals.clone(); + + std::iter::empty() + // Evaluate the circuit using the custom gates provided + .chain(vk.cs.gates.iter().map(move |poly| { + poly.evaluate( + &|index| fixed_evals[index], + &|index| advice_evals[index], + &|index| aux_evals[index], + &|a, b| a + &b, + &|a, b| a * &b, + &|a, scalar| a * &scalar, ) - }) - .into_iter() - .flatten(), - ); + })) + .chain( + 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)?; } - let queries = iter::empty() - .chain( - vk.cs - .advice_queries - .iter() - .enumerate() - .map(|(query_index, &(column, at))| VerifierQuery { - point: vk.domain.rotate_omega(*x, at), - commitment: &advice_commitments[column.index()], - eval: advice_evals[query_index], - }), - ) - .chain( - vk.cs - .aux_queries - .iter() - .enumerate() - .map(|(query_index, &(column, at))| VerifierQuery { - point: vk.domain.rotate_omega(*x, at), - commitment: &aux_commitments[column.index()], - eval: aux_evals[query_index], - }), + let queries = aux_commitments + .iter() + .zip(aux_evals.iter()) + .zip(advice_commitments.iter()) + .zip(advice_evals.iter()) + .zip(permutations_evaluated.iter()) + .zip(lookups_evaluated.iter()) + .flat_map( + |( + ((((aux_commitments, aux_evals), advice_commitments), advice_evals), permutations), + lookups, + )| { + iter::empty() + .chain(vk.cs.aux_queries.iter().enumerate().map( + move |(query_index, &(column, at))| VerifierQuery { + 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( vk.cs @@ -189,22 +255,7 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead>( eval: fixed_evals[query_index], }), ) - .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(), - ); + .chain(vanishing.queries(x)); // We are now convinced the circuit is satisfied so long as the // polynomial commitments open to the correct values.