diff --git a/src/plonk.rs b/src/plonk.rs index 16e61372..01c2b7bb 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -53,7 +53,7 @@ pub struct ProvingKey { #[derive(Debug, Clone)] pub struct Proof { advice_commitments: Vec, - permutations: Option>, + permutations: Vec>, lookups: Vec>, advice_evals: Vec, aux_evals: Vec, diff --git a/src/plonk/permutation.rs b/src/plonk/permutation.rs index 0de904cf..3bd1eef0 100644 --- a/src/plonk/permutation.rs +++ b/src/plonk/permutation.rs @@ -55,8 +55,8 @@ pub(crate) struct ProvingKey { #[derive(Debug, Clone)] pub(crate) struct Proof { - permutation_product_commitments: Vec, - permutation_product_evals: Vec, - permutation_product_inv_evals: Vec, - permutation_evals: Vec>, + permutation_product_commitment: C, + permutation_product_eval: C::Scalar, + permutation_product_inv_eval: C::Scalar, + permutation_evals: Vec, } diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs index 6296f3b7..c99ec5e3 100644 --- a/src/plonk/permutation/prover.rs +++ b/src/plonk/permutation/prover.rs @@ -1,10 +1,10 @@ use ff::Field; use std::iter; -use super::{Argument, Proof}; +use super::{Argument, Proof, ProvingKey}; use crate::{ arithmetic::{eval_polynomial, parallelize, BatchInvert, Curve, CurveAffine, FieldExt}, - plonk::{ChallengeBeta, ChallengeGamma, ChallengeX, Error, ProvingKey}, + plonk::{self, ChallengeBeta, ChallengeGamma, ChallengeX, Error}, poly::{ commitment::{Blind, Params}, multiopen::ProverQuery, @@ -14,24 +14,24 @@ use crate::{ }; pub(crate) struct Committed { - permutation_product_polys: Vec>, - permutation_product_cosets: Vec>, - permutation_product_cosets_inv: Vec>, - permutation_product_blinds: Vec>, - permutation_product_commitments: Vec, + permutation_product_poly: Polynomial, + permutation_product_coset: Polynomial, + permutation_product_coset_inv: Polynomial, + permutation_product_blind: Blind, + permutation_product_commitment: C, } pub(crate) struct Constructed { - permutation_product_polys: Vec>, - permutation_product_blinds: Vec>, - permutation_product_commitments: Vec, + permutation_product_poly: Polynomial, + permutation_product_blind: Blind, + permutation_product_commitment: C, } pub(crate) struct Evaluated { constructed: Constructed, - permutation_product_evals: Vec, - permutation_product_inv_evals: Vec, - permutation_evals: Vec>, + permutation_product_eval: C::Scalar, + permutation_product_inv_eval: C::Scalar, + permutation_evals: Vec, } impl Argument { @@ -40,8 +40,10 @@ impl Argument { HBase: Hasher, HScalar: Hasher, >( + &self, params: &Params, - pk: &ProvingKey, + pk: &plonk::ProvingKey, + pkey: &ProvingKey, advice: &[Polynomial], beta: ChallengeBeta, gamma: ChallengeGamma, @@ -49,136 +51,93 @@ impl Argument { ) -> Result, Error> { let domain = &pk.vk.domain; - // Compute permutation product polynomial commitment - let mut permutation_product_polys = vec![]; - let mut permutation_product_cosets = vec![]; - let mut permutation_product_cosets_inv = vec![]; - let mut permutation_product_commitments_projective = vec![]; - let mut permutation_product_blinds = vec![]; + // Goal is to compute the products of fractions + // + // (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) / + // (p_j(\omega^i) + \beta s_j(\omega^i) + \gamma) + // + // where p_j(X) is the jth advice column in this permutation, + // and i is the ith row of the column. - // Iterate over each permutation - let mut permutation_modified_advice = pk - .vk - .cs - .permutations - .iter() - .zip(pk.permutations.iter()) - // Goal is to compute the products of fractions - // - // (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) / - // (p_j(\omega^i) + \beta s_j(\omega^i) + \gamma) - // - // where p_j(X) is the jth advice column in this permutation, - // and i is the ith row of the column. - .map(|(p, pkey)| { - let mut modified_advice = vec![C::Scalar::one(); params.n as usize]; + let mut modified_advice = vec![C::Scalar::one(); params.n as usize]; - // Iterate over each column of the permutation - for (&column, permuted_column_values) in - p.columns.iter().zip(pkey.permutations.iter()) + // Iterate over each column of the permutation + for (&column, permuted_column_values) in self.columns.iter().zip(pkey.permutations.iter()) { + parallelize(&mut modified_advice, |modified_advice, start| { + for ((modified_advice, advice_value), permuted_advice_value) in modified_advice + .iter_mut() + .zip(advice[column.index()][start..].iter()) + .zip(permuted_column_values[start..].iter()) { - parallelize(&mut modified_advice, |modified_advice, start| { - for ((modified_advice, advice_value), permuted_advice_value) in - modified_advice - .iter_mut() - .zip(advice[column.index()][start..].iter()) - .zip(permuted_column_values[start..].iter()) - { - *modified_advice *= - &(*beta * permuted_advice_value + &gamma + advice_value); - } - }); + *modified_advice *= &(*beta * permuted_advice_value + &gamma + advice_value); } - - modified_advice - }) - .collect::>(); - - // Batch invert to obtain the denominators for the permutation product - // polynomials - permutation_modified_advice - .iter_mut() - .flat_map(|v| v.iter_mut()) - .batch_invert(); - - for (p, mut modified_advice) in pk - .vk - .cs - .permutations - .iter() - .zip(permutation_modified_advice.into_iter()) - { - // Iterate over each column again, this time finishing the computation - // of the entire fraction by computing the numerators - let mut deltaomega = C::Scalar::one(); - for &column in p.columns.iter() { - let omega = domain.get_omega(); - parallelize(&mut modified_advice, |modified_advice, start| { - let mut deltaomega = deltaomega * &omega.pow_vartime(&[start as u64, 0, 0, 0]); - for (modified_advice, advice_value) in modified_advice - .iter_mut() - .zip(advice[column.index()][start..].iter()) - { - // Multiply by p_j(\omega^i) + \delta^j \omega^i \beta - *modified_advice *= &(deltaomega * &beta + &gamma + advice_value); - deltaomega *= ω - } - }); - deltaomega *= &C::Scalar::DELTA; - } - - // The modified_advice vector is a vector of products of fractions - // of the form - // - // (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) / - // (p_j(\omega^i) + \beta s_j(\omega^i) + \gamma) - // - // where i is the index into modified_advice, for the jth column in - // the permutation - - // Compute the evaluations of the permutation product polynomial - // over our domain, starting with z[0] = 1 - let mut z = vec![C::Scalar::one()]; - for row in 1..(params.n as usize) { - let mut tmp = z[row - 1]; - - tmp *= &modified_advice[row]; - z.push(tmp); - } - let z = domain.lagrange_from_vec(z); - - let blind = Blind(C::Scalar::rand()); - - permutation_product_commitments_projective.push(params.commit_lagrange(&z, blind)); - permutation_product_blinds.push(blind); - let z = domain.lagrange_to_coeff(z); - permutation_product_polys.push(z.clone()); - permutation_product_cosets - .push(domain.coeff_to_extended(z.clone(), Rotation::default())); - permutation_product_cosets_inv.push(domain.coeff_to_extended(z, Rotation(-1))); + }); } - let mut permutation_product_commitments = - vec![C::zero(); permutation_product_commitments_projective.len()]; - C::Projective::batch_to_affine( - &permutation_product_commitments_projective, - &mut permutation_product_commitments, - ); - let permutation_product_commitments = permutation_product_commitments; - drop(permutation_product_commitments_projective); - // Hash each permutation product commitment - for c in &permutation_product_commitments { - transcript - .absorb_point(c) - .map_err(|_| Error::TranscriptError)?; + // Invert to obtain the denominator for the permutation product polynomial + modified_advice.batch_invert(); + + // Iterate over each column again, this time finishing the computation + // of the entire fraction by computing the numerators + let mut deltaomega = C::Scalar::one(); + for &column in self.columns.iter() { + let omega = domain.get_omega(); + parallelize(&mut modified_advice, |modified_advice, start| { + let mut deltaomega = deltaomega * &omega.pow_vartime(&[start as u64, 0, 0, 0]); + for (modified_advice, advice_value) in modified_advice + .iter_mut() + .zip(advice[column.index()][start..].iter()) + { + // Multiply by p_j(\omega^i) + \delta^j \omega^i \beta + *modified_advice *= &(deltaomega * &beta + &gamma + advice_value); + deltaomega *= ω + } + }); + deltaomega *= &C::Scalar::DELTA; } + // The modified_advice vector is a vector of products of fractions + // of the form + // + // (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) / + // (p_j(\omega^i) + \beta s_j(\omega^i) + \gamma) + // + // where i is the index into modified_advice, for the jth column in + // the permutation + + // Compute the evaluations of the permutation product polynomial + // over our domain, starting with z[0] = 1 + let mut z = vec![C::Scalar::one()]; + for row in 1..(params.n as usize) { + let mut tmp = z[row - 1]; + + tmp *= &modified_advice[row]; + z.push(tmp); + } + let z = domain.lagrange_from_vec(z); + + let blind = Blind(C::Scalar::rand()); + + let permutation_product_commitment_projective = params.commit_lagrange(&z, blind); + let permutation_product_blind = blind; + let z = domain.lagrange_to_coeff(z); + let permutation_product_poly = z.clone(); + let permutation_product_coset = domain.coeff_to_extended(z.clone(), Rotation::default()); + let permutation_product_coset_inv = domain.coeff_to_extended(z, Rotation(-1)); + + let permutation_product_commitment = permutation_product_commitment_projective.to_affine(); + + // Hash the permutation product commitment + transcript + .absorb_point(&permutation_product_commitment) + .map_err(|_| Error::TranscriptError)?; + Ok(Committed { - permutation_product_polys, - permutation_product_cosets, - permutation_product_cosets_inv, - permutation_product_blinds, - permutation_product_commitments, + permutation_product_poly, + permutation_product_coset, + permutation_product_coset_inv, + permutation_product_blind, + permutation_product_commitment, }) } } @@ -186,7 +145,9 @@ impl Argument { impl Committed { pub(in crate::plonk) fn construct<'a>( self, - pk: &'a ProvingKey, + pk: &'a plonk::ProvingKey, + p: &'a Argument, + pkey: &'a ProvingKey, advice_cosets: &'a [Polynomial], beta: ChallengeBeta, gamma: ChallengeGamma, @@ -198,74 +159,59 @@ impl Committed { Error, > { let domain = &pk.vk.domain; - let permutation_product_cosets_owned = self.permutation_product_cosets.clone(); - let permutation_product_cosets = self.permutation_product_cosets.clone(); - let permutation_product_cosets_inv = self.permutation_product_cosets_inv.clone(); let expressions = iter::empty() // l_0(X) * (1 - z(X)) = 0 - .chain( - permutation_product_cosets_owned - .into_iter() - .map(move |coset| Polynomial::one_minus(coset) * &pk.l0), - ) + .chain(Some( + Polynomial::one_minus(self.permutation_product_coset.clone()) * &pk.l0, + )) // z(X) \prod (p(X) + \beta s_i(X) + \gamma) - z(omega^{-1} X) \prod (p(X) + \delta^i \beta X + \gamma) - .chain( - pk.vk - .cs - .permutations + .chain(Some({ + let mut left = self.permutation_product_coset.clone(); + for (advice, permutation) in p + .columns .iter() - .zip(pk.permutations.iter()) - .zip(permutation_product_cosets.into_iter()) - .zip(permutation_product_cosets_inv.into_iter()) - .map(move |(((p, pkey), cosets), cosets_inv)| { - let mut left = cosets; - for (advice, permutation) in p - .columns - .iter() - .map(|&column| { - &advice_cosets[pk.vk.cs.get_advice_query_index(column, 0)] - }) - .zip(pkey.cosets.iter()) + .map(|&column| &advice_cosets[pk.vk.cs.get_advice_query_index(column, 0)]) + .zip(pkey.cosets.iter()) + { + parallelize(&mut left, |left, start| { + for ((left, advice), permutation) in left + .iter_mut() + .zip(advice[start..].iter()) + .zip(permutation[start..].iter()) { - parallelize(&mut left, |left, start| { - for ((left, advice), permutation) in left - .iter_mut() - .zip(advice[start..].iter()) - .zip(permutation[start..].iter()) - { - *left *= &(*advice + &(*beta * permutation) + &gamma); - } - }); + *left *= &(*advice + &(*beta * permutation) + &gamma); } + }); + } - let mut right = cosets_inv; - let mut current_delta = *beta * &C::Scalar::ZETA; - let step = domain.get_extended_omega(); - for advice in p.columns.iter().map(|&column| { - &advice_cosets[pk.vk.cs.get_advice_query_index(column, 0)] - }) { - parallelize(&mut right, move |right, start| { - let mut beta_term = - current_delta * &step.pow_vartime(&[start as u64, 0, 0, 0]); - for (right, advice) in right.iter_mut().zip(advice[start..].iter()) - { - *right *= &(*advice + &beta_term + &gamma); - beta_term *= &step; - } - }); - current_delta *= &C::Scalar::DELTA; + let mut right = self.permutation_product_coset_inv.clone(); + let mut current_delta = *beta * &C::Scalar::ZETA; + let step = domain.get_extended_omega(); + for advice in p + .columns + .iter() + .map(|&column| &advice_cosets[pk.vk.cs.get_advice_query_index(column, 0)]) + { + parallelize(&mut right, move |right, start| { + let mut beta_term = + current_delta * &step.pow_vartime(&[start as u64, 0, 0, 0]); + for (right, advice) in right.iter_mut().zip(advice[start..].iter()) { + *right *= &(*advice + &beta_term + &gamma); + beta_term *= &step; } + }); + current_delta *= &C::Scalar::DELTA; + } - left - &right - }), - ); + left - &right + })); Ok(( Constructed { - permutation_product_polys: self.permutation_product_polys, - permutation_product_blinds: self.permutation_product_blinds, - permutation_product_commitments: self.permutation_product_commitments, + permutation_product_poly: self.permutation_product_poly, + permutation_product_blind: self.permutation_product_blind, + permutation_product_commitment: self.permutation_product_commitment, }, expressions, )) @@ -300,39 +246,35 @@ impl super::ProvingKey { impl Constructed { pub(in crate::plonk) fn evaluate, HScalar: Hasher>( self, - pk: &ProvingKey, + pk: &plonk::ProvingKey, + pkey: &ProvingKey, x: ChallengeX, transcript: &mut Transcript, ) -> Evaluated { let domain = &pk.vk.domain; - let permutation_product_evals: Vec<_> = self - .permutation_product_polys - .iter() - .map(|poly| eval_polynomial(poly, *x)) - .collect(); + let permutation_product_eval = eval_polynomial(&self.permutation_product_poly, *x); - let permutation_product_inv_evals: Vec<_> = self - .permutation_product_polys - .iter() - .map(|poly| eval_polynomial(poly, domain.rotate_omega(*x, Rotation(-1)))) - .collect(); + let permutation_product_inv_eval = eval_polynomial( + &self.permutation_product_poly, + domain.rotate_omega(*x, Rotation(-1)), + ); - let permutation_evals: Vec<_> = pk.permutations.iter().map(|p| p.evaluate(x)).collect(); + let permutation_evals = pkey.evaluate(x); // Hash each advice evaluation - for eval in permutation_product_evals - .iter() - .chain(permutation_product_inv_evals.iter()) - .chain(permutation_evals.iter().flat_map(|evals| evals.iter())) + for eval in iter::empty() + .chain(Some(&permutation_product_eval)) + .chain(Some(&permutation_product_inv_eval)) + .chain(permutation_evals.iter()) { transcript.absorb_scalar(*eval); } Evaluated { constructed: self, - permutation_product_evals, - permutation_product_inv_evals, + permutation_product_eval, + permutation_product_inv_eval, permutation_evals, } } @@ -341,50 +283,35 @@ impl Constructed { impl Evaluated { pub(in crate::plonk) fn open<'a>( &'a self, - pk: &'a ProvingKey, + pk: &'a plonk::ProvingKey, + pkey: &'a ProvingKey, x: ChallengeX, ) -> impl Iterator> + Clone { let x_inv = pk.vk.domain.rotate_omega(*x, Rotation(-1)); iter::empty() // Open permutation product commitments at x and \omega^{-1} x - .chain( - self.constructed - .permutation_product_polys - .iter() - .zip(self.constructed.permutation_product_blinds.iter()) - .zip(self.permutation_product_evals.iter()) - .zip(self.permutation_product_inv_evals.iter()) - .flat_map(move |(((poly, blind), eval), inv_eval)| { - iter::empty() - .chain(Some(ProverQuery { - point: *x, - poly, - blind: *blind, - eval: *eval, - })) - .chain(Some(ProverQuery { - point: x_inv, - poly, - blind: *blind, - eval: *inv_eval, - })) - }), - ) + .chain(Some(ProverQuery { + point: *x, + poly: &self.constructed.permutation_product_poly, + blind: self.constructed.permutation_product_blind, + eval: self.permutation_product_eval, + })) + .chain(Some(ProverQuery { + point: x_inv, + poly: &self.constructed.permutation_product_poly, + blind: self.constructed.permutation_product_blind, + eval: self.permutation_product_inv_eval, + })) // Open permutation polynomial commitments at x - .chain( - pk.permutations - .iter() - .zip(self.permutation_evals.iter()) - .flat_map(move |(permutation, evals)| permutation.open(evals, x)), - ) + .chain(pkey.open(&self.permutation_evals, x)) } pub(crate) fn build(self) -> Proof { Proof { - permutation_product_commitments: self.constructed.permutation_product_commitments, - permutation_product_evals: self.permutation_product_evals, - permutation_product_inv_evals: self.permutation_product_inv_evals, + permutation_product_commitment: self.constructed.permutation_product_commitment, + permutation_product_eval: self.permutation_product_eval, + permutation_product_inv_eval: self.permutation_product_inv_eval, permutation_evals: self.permutation_evals, } } diff --git a/src/plonk/permutation/verifier.rs b/src/plonk/permutation/verifier.rs index ce09aee0..65e73a64 100644 --- a/src/plonk/permutation/verifier.rs +++ b/src/plonk/permutation/verifier.rs @@ -1,35 +1,17 @@ use ff::Field; use std::iter; -use super::Proof; +use super::{Argument, Proof, VerifyingKey}; use crate::{ arithmetic::{CurveAffine, FieldExt}, - plonk::{ChallengeBeta, ChallengeGamma, ChallengeX, Error, VerifyingKey}, + plonk::{self, ChallengeBeta, ChallengeGamma, ChallengeX, Error}, poly::{multiopen::VerifierQuery, Rotation}, transcript::{Hasher, Transcript}, }; impl Proof { - pub(crate) fn check_lengths(&self, vk: &VerifyingKey) -> Result<(), Error> { - if self.permutation_evals.len() != vk.cs.permutations.len() { - return Err(Error::IncompatibleParams); - } - - for (permutation_evals, p) in self.permutation_evals.iter().zip(vk.cs.permutations.iter()) { - if permutation_evals.len() != p.columns.len() { - return Err(Error::IncompatibleParams); - } - } - - if self.permutation_product_inv_evals.len() != vk.cs.permutations.len() { - return Err(Error::IncompatibleParams); - } - - if self.permutation_product_evals.len() != vk.cs.permutations.len() { - return Err(Error::IncompatibleParams); - } - - if self.permutation_product_commitments.len() != vk.cs.permutations.len() { + pub(crate) fn check_lengths(&self, p: &Argument) -> Result<(), Error> { + if self.permutation_evals.len() != p.columns.len() { return Err(Error::IncompatibleParams); } @@ -40,17 +22,15 @@ impl Proof { &self, transcript: &mut Transcript, ) -> Result<(), Error> { - for c in &self.permutation_product_commitments { - transcript - .absorb_point(c) - .map_err(|_| Error::TranscriptError)?; - } - Ok(()) + transcript + .absorb_point(&self.permutation_product_commitment) + .map_err(|_| Error::TranscriptError) } pub(in crate::plonk) fn expressions<'a>( &'a self, - vk: &'a VerifyingKey, + vk: &'a plonk::VerifyingKey, + p: &'a Argument, advice_evals: &'a [C::Scalar], l_0: C::Scalar, beta: ChallengeBeta, @@ -59,97 +39,74 @@ impl Proof { ) -> impl Iterator + 'a { iter::empty() // l_0(X) * (1 - z(X)) = 0 - .chain( - self.permutation_product_evals - .iter() - .map(move |product_eval| l_0 * &(C::Scalar::one() - product_eval)), - ) + .chain(Some( + l_0 * &(C::Scalar::one() - &self.permutation_product_eval), + )) // z(X) \prod (p(X) + \beta s_i(X) + \gamma) // - z(omega^{-1} X) \prod (p(X) + \delta^i \beta X + \gamma) - .chain( - vk.cs - .permutations + .chain(Some({ + let mut left = self.permutation_product_eval; + for (advice_eval, permutation_eval) in p + .columns .iter() + .map(|&column| advice_evals[vk.cs.get_advice_query_index(column, 0)]) .zip(self.permutation_evals.iter()) - .zip(self.permutation_product_evals.iter()) - .zip(self.permutation_product_inv_evals.iter()) - .map( - move |(((p, permutation_evals), product_eval), product_inv_eval)| { - let mut left = *product_eval; - for (advice_eval, permutation_eval) in p - .columns - .iter() - .map(|&column| { - advice_evals[vk.cs.get_advice_query_index(column, 0)] - }) - .zip(permutation_evals.iter()) - { - left *= &(advice_eval + &(*beta * permutation_eval) + &gamma); - } + { + left *= &(advice_eval + &(*beta * permutation_eval) + &gamma); + } - let mut right = *product_inv_eval; - let mut current_delta = *beta * &x; - for advice_eval in p.columns.iter().map(|&column| { - advice_evals[vk.cs.get_advice_query_index(column, 0)] - }) { - right *= &(advice_eval + ¤t_delta + &gamma); - current_delta *= &C::Scalar::DELTA; - } + let mut right = self.permutation_product_inv_eval; + let mut current_delta = *beta * &x; + for advice_eval in p + .columns + .iter() + .map(|&column| advice_evals[vk.cs.get_advice_query_index(column, 0)]) + { + right *= &(advice_eval + ¤t_delta + &gamma); + current_delta *= &C::Scalar::DELTA; + } - left - &right - }, - ), - ) + left - &right + })) } pub(crate) fn evals(&self) -> impl Iterator { - self.permutation_product_evals - .iter() - .chain(self.permutation_product_inv_evals.iter()) - .chain(self.permutation_evals.iter().flat_map(|evals| evals.iter())) + iter::empty() + .chain(Some(&self.permutation_product_eval)) + .chain(Some(&self.permutation_product_inv_eval)) + .chain(self.permutation_evals.iter()) } pub(in crate::plonk) fn queries<'a>( &'a self, - vk: &'a VerifyingKey, + vk: &'a plonk::VerifyingKey, + vkey: &'a VerifyingKey, x: ChallengeX, ) -> impl Iterator> + Clone { let x_inv = vk.domain.rotate_omega(*x, Rotation(-1)); iter::empty() // Open permutation product commitments at x and \omega^{-1} x - .chain( - self.permutation_product_commitments - .iter() - .enumerate() - .zip(self.permutation_product_evals.iter()) - .zip(self.permutation_product_inv_evals.iter()) - .flat_map(move |(((idx, _), &eval), &inv_eval)| { - iter::empty() - .chain(Some(VerifierQuery { - point: *x, - commitment: &self.permutation_product_commitments[idx], - eval, - })) - .chain(Some(VerifierQuery { - point: x_inv, - commitment: &self.permutation_product_commitments[idx], - eval: inv_eval, - })) - }), - ) + .chain(Some(VerifierQuery { + point: *x, + commitment: &self.permutation_product_commitment, + eval: self.permutation_product_eval, + })) + .chain(Some(VerifierQuery { + point: x_inv, + commitment: &self.permutation_product_commitment, + eval: self.permutation_product_inv_eval, + })) // Open permutation commitments for each permutation argument at x .chain( - (0..vk.permutations.len()) - .map(move |outer_idx| { - let inner_len = vk.permutations[outer_idx].commitments.len(); - (0..inner_len).map(move |inner_idx| VerifierQuery { - point: *x, - commitment: &vk.permutations[outer_idx].commitments[inner_idx], - eval: self.permutation_evals[outer_idx][inner_idx], - }) - }) - .flatten(), + vkey.commitments + .iter() + .zip(self.permutation_evals.iter()) + .map(move |(commitment, &eval)| VerifierQuery { + point: *x, + commitment, + eval, + }), ) } } diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 47844997..41a003c4 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -3,8 +3,8 @@ use std::iter; use super::{ circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed}, - permutation, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, - Error, Proof, ProvingKey, + vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, Proof, + ProvingKey, }; use crate::arithmetic::{eval_polynomial, Curve, CurveAffine, FieldExt}; use crate::poly::{ @@ -202,18 +202,24 @@ impl Proof { let gamma = ChallengeGamma::get(&mut transcript); // Commit to permutations, if any. - let permutations = if !pk.vk.cs.permutations.is_empty() { - Some(permutation::Argument::commit( - params, - pk, - &witness.advice, - beta, - gamma, - &mut transcript, - )?) - } else { - None - }; + let permutations = pk + .vk + .cs + .permutations + .iter() + .zip(pk.permutations.iter()) + .map(|(p, pkey)| { + p.commit( + params, + pk, + pkey, + &witness.advice, + beta, + gamma, + &mut transcript, + ) + }) + .collect::, _>>()?; // Construct and commit to products for each lookup let lookups = lookups @@ -225,11 +231,18 @@ impl Proof { let y = ChallengeY::get(&mut transcript); // Evaluate the h(X) polynomial's constraint system expressions for the permutation constraints, if any. - let (permutations, permutation_expressions) = permutations - .map(|p| p.construct(pk, &advice_cosets, beta, gamma)) - .transpose()? - .map(|(p, expressions)| (Some(p), Some(expressions))) - .unwrap_or_default(); + let (permutations, permutation_expressions): (Vec<_>, Vec<_>) = { + let tmp = 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::, _>>()?; + + tmp.into_iter().unzip() + }; // Evaluate the h(X) polynomial's constraint system expressions for the lookup constraints, if any. let (lookups, lookup_expressions): (Vec<_>, Vec<_>) = { @@ -302,7 +315,11 @@ impl Proof { let vanishing = vanishing.evaluate(x, &mut transcript); // Evaluate the permutations, if any, at omega^i x. - let permutations = permutations.map(|p| p.evaluate(pk, x, &mut transcript)); + let permutations = permutations + .into_iter() + .zip(pk.permutations.iter()) + .map(|(p, pkey)| p.evaluate(pk, pkey, x, &mut transcript)) + .collect::>(); // Evaluate the lookups, if any, at omega^i x. let lookups = lookups @@ -337,26 +354,23 @@ impl Proof { }, )) // We query the h(X) polynomial at x - .chain(vanishing.open(x)); - - let multiopening = multiopen::Proof::create( - params, - &mut transcript, - instances + .chain(vanishing.open(x)) .chain( permutations - .as_ref() - .map(|p| p.open(pk, x)) + .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()), - ) - .map_err(|_| Error::OpeningError)?; + .chain(lookups.iter().map(|p| p.open(pk, x)).into_iter().flatten()); + + let multiopening = multiopen::Proof::create(params, &mut transcript, instances) + .map_err(|_| Error::OpeningError)?; Ok(Proof { advice_commitments, - permutations: permutations.map(|p| p.build()), + permutations: permutations.into_iter().map(|p| p.build()).collect(), lookups: lookups.into_iter().map(|p| p.build()).collect(), advice_evals, fixed_evals, diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 11de2b87..3ece2a99 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -63,8 +63,8 @@ impl<'a, C: CurveAffine> Proof { let gamma = ChallengeGamma::get(&mut transcript); // Hash each permutation product commitment - if let Some(p) = &self.permutations { - p.absorb_commitments(&mut transcript)?; + for permutation in &self.permutations { + permutation.absorb_commitments(&mut transcript)?; } // Hash each lookup product commitment @@ -93,7 +93,7 @@ impl<'a, C: CurveAffine> Proof { .chain(self.vanishing.evals()) .chain( self.permutations - .as_ref() + .iter() .map(|p| p.evals()) .into_iter() .flatten(), @@ -141,8 +141,9 @@ impl<'a, C: CurveAffine> Proof { queries .chain( self.permutations - .as_ref() - .map(|p| p.queries(vk, x)) + .iter() + .zip(vk.permutations.iter()) + .map(|(p, vkey)| p.queries(vk, vkey, x)) .into_iter() .flatten(), ) @@ -177,10 +178,13 @@ impl<'a, C: CurveAffine> Proof { return Err(Error::IncompatibleParams); } - self.permutations - .as_ref() - .map(|p| p.check_lengths(vk)) - .transpose()?; + if self.permutations.len() != vk.cs.permutations.len() { + return Err(Error::IncompatibleParams); + } + + for (permutation, p) in self.permutations.iter().zip(vk.cs.permutations.iter()) { + permutation.check_lengths(p)?; + } self.vanishing.check_lengths(vk)?; @@ -231,8 +235,11 @@ impl<'a, C: CurveAffine> Proof { })) .chain( self.permutations - .as_ref() - .map(|p| p.expressions(vk, &self.advice_evals, l_0, beta, gamma, x)) + .iter() + .zip(vk.cs.permutations.iter()) + .map(|(p, argument)| { + p.expressions(vk, argument, &self.advice_evals, l_0, beta, gamma, x) + }) .into_iter() .flatten(), )