From 4a3b8301658c029d30d3e381074d1f1c6e0cf459 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 25 Nov 2020 00:49:52 +0000 Subject: [PATCH 01/12] Extract permutation argument into a submodule --- src/plonk.rs | 6 +- src/plonk/permutation.rs | 14 ++ src/plonk/permutation/prover.rs | 371 ++++++++++++++++++++++++++++++ src/plonk/permutation/verifier.rs | 159 +++++++++++++ src/plonk/prover.rs | 315 ++++--------------------- src/plonk/verifier.rs | 143 ++---------- 6 files changed, 619 insertions(+), 389 deletions(-) create mode 100644 src/plonk/permutation.rs create mode 100644 src/plonk/permutation/prover.rs create mode 100644 src/plonk/permutation/verifier.rs diff --git a/src/plonk.rs b/src/plonk.rs index 7953cba0..e938d711 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -12,6 +12,7 @@ use crate::poly::{ mod circuit; mod keygen; +mod permutation; mod prover; mod verifier; @@ -50,10 +51,7 @@ pub struct ProvingKey { pub struct Proof { advice_commitments: Vec, h_commitments: Vec, - permutation_product_commitments: Vec, - permutation_product_evals: Vec, - permutation_product_inv_evals: Vec, - permutation_evals: Vec>, + permutations: Option>, advice_evals: Vec, aux_evals: Vec, fixed_evals: Vec, diff --git a/src/plonk/permutation.rs b/src/plonk/permutation.rs new file mode 100644 index 00000000..74a47141 --- /dev/null +++ b/src/plonk/permutation.rs @@ -0,0 +1,14 @@ +//! Implementation of a PLONK permutation argument. + +use crate::arithmetic::CurveAffine; + +mod prover; +mod verifier; + +#[derive(Debug, Clone)] +pub(crate) struct Proof { + permutation_product_commitments: Vec, + permutation_product_evals: Vec, + permutation_product_inv_evals: Vec, + permutation_evals: Vec>, +} diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs new file mode 100644 index 00000000..4970212e --- /dev/null +++ b/src/plonk/permutation/prover.rs @@ -0,0 +1,371 @@ +use ff::Field; +use std::iter; + +use super::Proof; +use crate::{ + arithmetic::{eval_polynomial, parallelize, BatchInvert, Curve, CurveAffine, FieldExt}, + plonk::{Error, ProvingKey}, + poly::{ + commitment::{Blind, Params}, + multiopen::ProverQuery, + Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation, + }, + transcript::{Hasher, Transcript}, +}; + +#[derive(Clone)] +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, +} + +pub(crate) struct Constructed { + permutation_product_polys: Vec>, + permutation_product_blinds: Vec>, + permutation_product_commitments: Vec, +} + +pub(crate) struct Evaluated { + constructed: Constructed, + permutation_product_evals: Vec, + permutation_product_inv_evals: Vec, + permutation_evals: Vec>, +} + +impl Proof { + pub(crate) fn commit, HScalar: Hasher>( + params: &Params, + pk: &ProvingKey, + advice: &[Polynomial], + x_0: C::Scalar, + x_1: C::Scalar, + transcript: &mut Transcript, + ) -> 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![]; + + // 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(|(columns, permuted_values)| { + 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 columns.iter().zip(permuted_values.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 *= + &(x_0 * permuted_advice_value + &x_1 + 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 (columns, 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 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 * &x_0 + &x_1 + 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)?; + } + + Ok(Committed { + permutation_product_polys, + permutation_product_cosets, + permutation_product_cosets_inv, + permutation_product_blinds, + permutation_product_commitments, + }) + } +} + +impl Committed { + pub(crate) fn construct<'a>( + self, + pk: &'a ProvingKey, + advice_cosets: &'a [Polynomial], + x_0: C::Scalar, + x_1: C::Scalar, + ) -> Result< + ( + Constructed, + impl Iterator> + 'a, + ), + Error, + > { + let domain = &pk.vk.domain; + let permutation_product_cosets_owned = self.permutation_product_cosets.clone(); + let permutation_product_cosets = self.permutation_product_cosets; + let permutation_product_cosets_inv = self.permutation_product_cosets_inv; + + 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), + ) + // 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.iter().enumerate().map( + move |(permutation_index, columns)| { + let mut left = permutation_product_cosets[permutation_index].clone(); + for (advice, permutation) in columns + .iter() + .map(|&column| &advice_cosets[pk.vk.cs.get_advice_query_index(column, 0)]) + .zip(pk.permutation_cosets[permutation_index].iter()) + { + parallelize(&mut left, |left, start| { + for ((left, advice), permutation) in left + .iter_mut() + .zip(advice[start..].iter()) + .zip(permutation[start..].iter()) + { + *left *= &(*advice + &(x_0 * permutation) + &x_1); + } + }); + } + + let mut right = permutation_product_cosets_inv[permutation_index].clone(); + let mut current_delta = x_0 * &C::Scalar::ZETA; + let step = domain.get_extended_omega(); + for advice in 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 + &x_1); + beta_term *= &step; + } + }); + current_delta *= &C::Scalar::DELTA; + } + + 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, + }, + expressions, + )) + } +} + +impl Constructed { + pub(crate) fn evaluate, HScalar: Hasher>( + self, + pk: &ProvingKey, + x_3: C::Scalar, + 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_3)) + .collect(); + + let permutation_product_inv_evals: Vec = self + .permutation_product_polys + .iter() + .map(|poly| eval_polynomial(poly, domain.rotate_omega(x_3, Rotation(-1)))) + .collect(); + + let permutation_evals: Vec> = pk + .permutation_polys + .iter() + .map(|polys| { + polys + .iter() + .map(|poly| eval_polynomial(poly, x_3)) + .collect() + }) + .collect(); + + // 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())) + { + transcript.absorb_scalar(*eval); + } + + Evaluated { + constructed: self, + permutation_product_evals, + permutation_product_inv_evals, + permutation_evals, + } + } +} + +impl Evaluated { + pub fn open<'a>( + &'a self, + pk: &'a ProvingKey, + x_3: C::Scalar, + ) -> impl Iterator> + Clone { + let x_3_inv = pk.vk.domain.rotate_omega(x_3, Rotation(-1)); + + iter::empty() + // Open permutation product commitments at x_3 + .chain( + self.constructed + .permutation_product_polys + .iter() + .zip(self.constructed.permutation_product_blinds.iter()) + .zip(self.permutation_product_evals.iter()) + .map(move |((poly, blind), eval)| ProverQuery { + point: x_3, + poly, + blind: *blind, + eval: *eval, + }), + ) + // Open permutation polynomial commitments at x_3 + .chain( + pk.permutation_polys + .iter() + .zip(self.permutation_evals.iter()) + .flat_map(|(polys, evals)| polys.iter().zip(evals.iter())) + .map(move |(poly, eval)| ProverQuery { + point: x_3, + poly, + blind: Blind::default(), + eval: *eval, + }), + ) + // Open permutation product commitments at \omega^{-1} x_3 + .chain( + self.constructed + .permutation_product_polys + .iter() + .zip(self.constructed.permutation_product_blinds.iter()) + .zip(self.permutation_product_inv_evals.iter()) + .map(move |((poly, blind), eval)| ProverQuery { + point: x_3_inv, + poly, + blind: *blind, + eval: *eval, + }), + ) + } + + 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_evals: self.permutation_evals, + } + } +} diff --git a/src/plonk/permutation/verifier.rs b/src/plonk/permutation/verifier.rs new file mode 100644 index 00000000..afd68010 --- /dev/null +++ b/src/plonk/permutation/verifier.rs @@ -0,0 +1,159 @@ +use ff::Field; +use std::iter; + +use super::Proof; +use crate::{ + arithmetic::{CurveAffine, FieldExt}, + plonk::{Error, VerifyingKey}, + 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, permutation) in + self.permutation_evals.iter().zip(vk.cs.permutations.iter()) + { + if permutation_evals.len() != permutation.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() { + return Err(Error::IncompatibleParams); + } + + Ok(()) + } + + pub(crate) fn absorb_commitments, HScalar: Hasher>( + &self, + transcript: &mut Transcript, + ) -> Result<(), Error> { + for c in &self.permutation_product_commitments { + transcript + .absorb_point(c) + .map_err(|_| Error::TranscriptError)?; + } + Ok(()) + } + + pub(crate) fn expressions<'a>( + &'a self, + vk: &'a VerifyingKey, + advice_evals: &'a [C::Scalar], + l_0: C::Scalar, + x_0: C::Scalar, + x_1: C::Scalar, + x_3: C::Scalar, + ) -> 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)), + ) + // 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 + .iter() + .zip(self.permutation_evals.iter()) + .zip(self.permutation_product_evals.iter()) + .zip(self.permutation_product_inv_evals.iter()) + .map( + move |(((columns, permutation_evals), product_eval), product_inv_eval)| { + let mut left = *product_eval; + for (advice_eval, permutation_eval) in columns + .iter() + .map(|&column| { + advice_evals[vk.cs.get_advice_query_index(column, 0)] + }) + .zip(permutation_evals.iter()) + { + left *= &(advice_eval + &(x_0 * permutation_eval) + &x_1); + } + + let mut right = *product_inv_eval; + let mut current_delta = x_0 * &x_3; + for advice_eval in columns.iter().map(|&column| { + advice_evals[vk.cs.get_advice_query_index(column, 0)] + }) { + right *= &(advice_eval + ¤t_delta + &x_1); + current_delta *= &C::Scalar::DELTA; + } + + 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())) + } + + pub(crate) fn queries<'a>( + &'a self, + vk: &'a VerifyingKey, + x_3: C::Scalar, + ) -> impl Iterator> + Clone { + let x_3_inv = vk.domain.rotate_omega(x_3, Rotation(-1)); + + iter::empty() + // Open permutation product commitments at x_3 + .chain( + self.permutation_product_commitments + .iter() + .enumerate() + .zip(self.permutation_product_evals.iter()) + .map(move |((idx, _), &eval)| VerifierQuery { + point: x_3, + commitment: &self.permutation_product_commitments[idx], + eval, + }), + ) + // Open permutation commitments for each permutation argument at x_3 + .chain( + (0..vk.permutation_commitments.len()) + .map(move |outer_idx| { + let inner_len = vk.permutation_commitments[outer_idx].len(); + (0..inner_len).map(move |inner_idx| VerifierQuery { + point: x_3, + commitment: &vk.permutation_commitments[outer_idx][inner_idx], + eval: self.permutation_evals[outer_idx][inner_idx], + }) + }) + .flatten(), + ) + // Open permutation product commitments at \omega^{-1} x_3 + .chain( + self.permutation_product_commitments + .iter() + .enumerate() + .zip(self.permutation_product_inv_evals.iter()) + .map(move |((idx, _), &eval)| VerifierQuery { + point: x_3_inv, + commitment: &self.permutation_product_commitments[idx], + eval, + }), + ) + } +} diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 8d75168c..cc8827aa 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -3,16 +3,15 @@ use std::iter; use super::{ circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed}, - Error, Proof, ProvingKey, + permutation, Error, Proof, ProvingKey, }; use crate::arithmetic::{ - eval_polynomial, get_challenge_scalar, parallelize, BatchInvert, Challenge, Curve, CurveAffine, - FieldExt, + eval_polynomial, get_challenge_scalar, Challenge, Curve, CurveAffine, FieldExt, }; use crate::poly::{ commitment::{Blind, Params}, multiopen::{self, ProverQuery}, - LagrangeCoeff, Polynomial, Rotation, + LagrangeCoeff, Polynomial, }; use crate::transcript::{Hasher, Transcript}; @@ -177,197 +176,46 @@ impl Proof { // Sample x_1 challenge let x_1: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - // 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![]; - - // 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(|(columns, permuted_values)| { - 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 columns.iter().zip(permuted_values.iter()) - { - parallelize(&mut modified_advice, |modified_advice, start| { - for ((modified_advice, advice_value), permuted_advice_value) in - modified_advice - .iter_mut() - .zip(witness.advice[column.index()][start..].iter()) - .zip(permuted_column_values[start..].iter()) - { - *modified_advice *= - &(x_0 * permuted_advice_value + &x_1 + 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 (columns, 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 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(witness.advice[column.index()][start..].iter()) - { - // Multiply by p_j(\omega^i) + \delta^j \omega^i \beta - *modified_advice *= &(deltaomega * &x_0 + &x_1 + 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)?; - } + // Commit to permutations, if any. + let permutations = if !pk.vk.cs.permutations.is_empty() { + Some(permutation::Proof::commit( + params, + pk, + &witness.advice, + x_0, + x_1, + &mut transcript, + )?) + } else { + None + }; // Obtain challenge for keeping all separate gates linearly independent let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + // 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, x_0, x_1)) + .transpose()? + .map(|(p, expressions)| (Some(p), Some(expressions))) + .unwrap_or_default(); + // Evaluate the h(X) polynomial's constraint system expressions for the constraints provided - let h_poly = - 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, - ) - })) - // l_0(X) * (1 - z(X)) = 0 - .chain( - permutation_product_cosets - .iter() - .cloned() - .map(|coset| Polynomial::one_minus(coset) * &pk.l0), + let h_poly = 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, ) - // 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.iter().enumerate().map( - |(permutation_index, columns)| { - let mut left = permutation_product_cosets[permutation_index].clone(); - for (advice, permutation) in columns - .iter() - .map(|&column| { - &advice_cosets[pk.vk.cs.get_advice_query_index(column, 0)] - }) - .zip(pk.permutation_cosets[permutation_index].iter()) - { - parallelize(&mut left, |left, start| { - for ((left, advice), permutation) in left - .iter_mut() - .zip(advice[start..].iter()) - .zip(permutation[start..].iter()) - { - *left *= &(*advice + &(x_0 * permutation) + &x_1); - } - }); - } - - let mut right = permutation_product_cosets_inv[permutation_index].clone(); - let mut current_delta = x_0 * &C::Scalar::ZETA; - let step = domain.get_extended_omega(); - for advice in 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 + &x_1); - beta_term *= &step; - } - }); - current_delta *= &C::Scalar::DELTA; - } - - left - &right - }, - )) - .fold(domain.empty_extended(), |h_poly, v| h_poly * x_2 + &v); + })) + // Permutation constraints, if any. + .chain(permutation_expressions.into_iter().flatten()) + .fold(domain.empty_extended(), |h_poly, v| h_poly * x_2 + &v); // Divide by t(X) = X^{params.n} - 1. let h_poly = domain.divide_by_vanishing_poly(h_poly); @@ -402,7 +250,6 @@ impl Proof { } let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - let x_3_inv = domain.rotate_omega(x_3, Rotation(-1)); // Evaluate polynomials at omega^i x_3 let advice_evals: Vec<_> = meta @@ -432,27 +279,6 @@ impl Proof { }) .collect(); - let permutation_product_evals: Vec = permutation_product_polys - .iter() - .map(|poly| eval_polynomial(poly, x_3)) - .collect(); - - let permutation_product_inv_evals: Vec = permutation_product_polys - .iter() - .map(|poly| eval_polynomial(poly, domain.rotate_omega(x_3, Rotation(-1)))) - .collect(); - - let permutation_evals: Vec> = pk - .permutation_polys - .iter() - .map(|polys| { - polys - .iter() - .map(|poly| eval_polynomial(poly, x_3)) - .collect() - }) - .collect(); - let h_evals: Vec<_> = h_pieces .iter() .map(|poly| eval_polynomial(poly, x_3)) @@ -464,13 +290,13 @@ impl Proof { .chain(aux_evals.iter()) .chain(fixed_evals.iter()) .chain(h_evals.iter()) - .chain(permutation_product_evals.iter()) - .chain(permutation_product_inv_evals.iter()) - .chain(permutation_evals.iter().flat_map(|evals| evals.iter())) { transcript.absorb_scalar(*eval); } + // Evaluate the permutations, if any, at omega^i x_3. + let permutations = permutations.map(|p| p.evaluate(pk, x_3, &mut transcript)); + let instances = iter::empty() .chain(pk.vk.cs.advice_queries.iter().enumerate().map( @@ -511,68 +337,23 @@ impl Proof { }), ); - // Handle permutation arguments, if any exist - let permutation_instances = if !pk.vk.cs.permutations.is_empty() { - Some( - iter::empty() - // Open permutation product commitments at x_3 - .chain( - permutation_product_polys - .iter() - .zip(permutation_product_blinds.iter()) - .zip(permutation_product_evals.iter()) - .map(|((poly, blind), eval)| ProverQuery { - point: x_3, - poly, - blind: *blind, - eval: *eval, - }), - ) - // Open permutation polynomial commitments at x_3 - .chain( - pk.permutation_polys - .iter() - .zip(permutation_evals.iter()) - .flat_map(|(polys, evals)| polys.iter().zip(evals.iter())) - .map(|(poly, eval)| ProverQuery { - point: x_3, - poly, - blind: Blind::default(), - eval: *eval, - }), - ) - // Open permutation product commitments at \omega^{-1} x_3 - .chain( - permutation_product_polys - .iter() - .zip(permutation_product_blinds.iter()) - .zip(permutation_product_inv_evals.iter()) - .map(|((poly, blind), eval)| ProverQuery { - point: x_3_inv, - poly, - blind: *blind, - eval: *eval, - }), - ), - ) - } else { - None - }; - let multiopening = multiopen::Proof::create( params, &mut transcript, - instances.chain(permutation_instances.into_iter().flatten()), + instances.chain( + permutations + .as_ref() + .map(|p| p.open(pk, x_3)) + .into_iter() + .flatten(), + ), ) .map_err(|_| Error::OpeningError)?; Ok(Proof { advice_commitments, h_commitments, - permutation_product_commitments, - permutation_product_evals, - permutation_product_inv_evals, - permutation_evals, + permutations: permutations.map(|p| p.build()), advice_evals, fixed_evals, aux_evals, diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index ce5eb3c9..4450d285 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -6,7 +6,6 @@ use crate::arithmetic::{get_challenge_scalar, Challenge, CurveAffine, FieldExt}; use crate::poly::{ commitment::{Guard, Params, MSM}, multiopen::VerifierQuery, - Rotation, }; use crate::transcript::{Hasher, Transcript}; @@ -53,10 +52,8 @@ impl<'a, C: CurveAffine> Proof { let x_1: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); // Hash each permutation product commitment - for c in &self.permutation_product_commitments { - transcript - .absorb_point(c) - .map_err(|_| Error::TranscriptError)?; + if let Some(p) = &self.permutations { + p.absorb_commitments(&mut transcript)?; } // Sample x_2 challenge, which keeps the gates linearly independent. @@ -72,7 +69,6 @@ impl<'a, C: CurveAffine> Proof { // Sample x_3 challenge, which is used to ensure the circuit is // satisfied with high probability. let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - let x_3_inv = vk.domain.rotate_omega(x_3, Rotation(-1)); // This check ensures the circuit is satisfied so long as the polynomial // commitments open to the correct values. @@ -84,9 +80,13 @@ impl<'a, C: CurveAffine> Proof { .chain(self.aux_evals.iter()) .chain(self.fixed_evals.iter()) .chain(self.h_evals.iter()) - .chain(self.permutation_product_evals.iter()) - .chain(self.permutation_product_inv_evals.iter()) - .chain(self.permutation_evals.iter().flat_map(|evals| evals.iter())) + .chain( + self.permutations + .as_ref() + .map(|p| p.evals()) + .into_iter() + .flatten(), + ) { transcript.absorb_scalar(*eval); } @@ -130,59 +130,19 @@ impl<'a, C: CurveAffine> Proof { }), ); - // Handle permutation arguments, if any exist - let permutation_queries = if !vk.cs.permutations.is_empty() { - Some( - iter::empty() - // Open permutation product commitments at x_3 - .chain( - self.permutation_product_commitments - .iter() - .enumerate() - .zip(self.permutation_product_evals.iter()) - .map(|((idx, _), &eval)| VerifierQuery { - point: x_3, - commitment: &self.permutation_product_commitments[idx], - eval, - }), - ) - // Open permutation commitments for each permutation argument at x_3 - .chain( - (0..vk.permutation_commitments.len()) - .map(|outer_idx| { - let inner_len = vk.permutation_commitments[outer_idx].len(); - (0..inner_len).map(move |inner_idx| VerifierQuery { - point: x_3, - commitment: &vk.permutation_commitments[outer_idx][inner_idx], - eval: self.permutation_evals[outer_idx][inner_idx], - }) - }) - .flatten(), - ) - // Open permutation product commitments at \omega^{-1} x_3 - .chain( - self.permutation_product_commitments - .iter() - .enumerate() - .zip(self.permutation_product_inv_evals.iter()) - .map(|((idx, _), &eval)| VerifierQuery { - point: x_3_inv, - commitment: &self.permutation_product_commitments[idx], - eval, - }), - ), - ) - } else { - None - }; - // We are now convinced the circuit is satisfied so long as the // polynomial commitments open to the correct values. self.multiopening .verify( params, &mut transcript, - queries.chain(permutation_queries.into_iter().flatten()), + queries.chain( + self.permutations + .as_ref() + .map(|p| p.queries(vk, x_3)) + .into_iter() + .flatten(), + ), msm, ) .map_err(|_| Error::OpeningError) @@ -209,29 +169,10 @@ impl<'a, C: CurveAffine> Proof { return Err(Error::IncompatibleParams); } - if self.permutation_evals.len() != vk.cs.permutations.len() { - return Err(Error::IncompatibleParams); - } - - for (permutation_evals, permutation) in - self.permutation_evals.iter().zip(vk.cs.permutations.iter()) - { - if permutation_evals.len() != permutation.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() { - return Err(Error::IncompatibleParams); - } + self.permutations + .as_ref() + .map(|p| p.check_lengths(vk)) + .transpose()?; // TODO: check h_commitments @@ -275,46 +216,12 @@ impl<'a, C: CurveAffine> Proof { &|a, scalar| a * &scalar, ) })) - // l_0(X) * (1 - z(X)) = 0 .chain( - self.permutation_product_evals - .iter() - .map(|product_eval| l_0 * &(C::Scalar::one() - 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 - .iter() - .zip(self.permutation_evals.iter()) - .zip(self.permutation_product_evals.iter()) - .zip(self.permutation_product_inv_evals.iter()) - .map( - |(((columns, permutation_evals), product_eval), product_inv_eval)| { - let mut left = *product_eval; - for (advice_eval, permutation_eval) in columns - .iter() - .map(|&column| { - self.advice_evals[vk.cs.get_advice_query_index(column, 0)] - }) - .zip(permutation_evals.iter()) - { - left *= &(advice_eval + &(x_0 * permutation_eval) + &x_1); - } - - let mut right = *product_inv_eval; - let mut current_delta = x_0 * &x_3; - for advice_eval in columns.iter().map(|&column| { - self.advice_evals[vk.cs.get_advice_query_index(column, 0)] - }) { - right *= &(advice_eval + ¤t_delta + &x_1); - current_delta *= &C::Scalar::DELTA; - } - - left - &right - }, - ), + self.permutations + .as_ref() + .map(|p| p.expressions(vk, &self.advice_evals, l_0, x_0, x_1, x_3)) + .into_iter() + .flatten(), ) .fold(C::Scalar::zero(), |h_eval, v| h_eval * &x_2 + &v); From 63e3bc1e155add0ce49f866d997b0d40c9581bfe Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 25 Nov 2020 19:13:45 +0000 Subject: [PATCH 02/12] Remove unnecessary Transcript::init_with_hashers constructor --- src/poly/commitment.rs | 14 +++++--------- src/transcript.rs | 12 ------------ 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 8faa3baf..6fc4b00d 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -257,7 +257,7 @@ fn test_opening_proof() { use crate::arithmetic::{ eval_polynomial, get_challenge_scalar, Challenge, Curve, CurveAffine, FieldExt, }; - use crate::transcript::{DummyHash, Hasher, Transcript}; + use crate::transcript::{DummyHash, Transcript}; use crate::tweedle::{EpAffine, Fp, Fq}; let params = Params::::new::>(K); @@ -273,18 +273,14 @@ fn test_opening_proof() { let p = params.commit(&px, blind).to_affine(); - let mut hasher = DummyHash::init(Field::one()); - let (p_x, p_y) = p.get_xy().unwrap(); - hasher.absorb(p_x); - hasher.absorb(p_y); - let x_packed = hasher.squeeze().get_lower_128(); + let mut transcript = Transcript::<_, DummyHash<_>, DummyHash<_>>::new(); + transcript.absorb_point(&p).unwrap(); + let x_packed = transcript.squeeze().get_lower_128(); let x: Fq = get_challenge_scalar(Challenge(x_packed)); // Evaluate the polynomial let v = eval_polynomial(&px, x); - hasher.absorb(Fp::from_bytes(&v.to_bytes()).unwrap()); // unlikely to fail since p ~ q - let scalar_hasher = DummyHash::init(Fq::one()); - let mut transcript = Transcript::init_with_hashers(&hasher, &scalar_hasher); + transcript.absorb_base(Fp::from_bytes(&v.to_bytes()).unwrap()); // unlikely to fail since p ~ q loop { let mut transcript_dup = transcript.clone(); diff --git a/src/transcript.rs b/src/transcript.rs index 254bf323..3cf27c9c 100644 --- a/src/transcript.rs +++ b/src/transcript.rs @@ -81,18 +81,6 @@ impl, HScalar: Hasher> } } - /// Initialise a new transcript with some given base_hasher and - /// scalar_hasher - #[cfg(test)] - pub(crate) fn init_with_hashers(base_hasher: &HBase, scalar_hasher: &HScalar) -> Self { - Transcript { - base_hasher: base_hasher.clone(), - scalar_hasher: scalar_hasher.clone(), - scalar_needs_squeezing: false, - _marker: PhantomData, - } - } - fn conditional_scalar_squeeze(&mut self) { if self.scalar_needs_squeezing { let transcript_scalar_point = From f63f3ff2af41f002299e33e589c07bb5c63c0828 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 25 Nov 2020 19:26:31 +0000 Subject: [PATCH 03/12] Introduce typed challenge scalars This also centralises the challenge generation logic in Challenge::get, ensuring it is consistent across the codebase. --- src/arithmetic.rs | 21 ------- src/plonk.rs | 19 +++++- src/plonk/permutation/prover.rs | 49 +++++++-------- src/plonk/permutation/verifier.rs | 30 ++++----- src/plonk/prover.rs | 55 ++++++++-------- src/plonk/verifier.rs | 64 +++++++++---------- src/poly/commitment.rs | 100 +++++++++++++++++++++++++----- src/poly/commitment/prover.rs | 23 +++---- src/poly/commitment/verifier.rs | 27 ++++---- src/poly/multiopen/prover.rs | 25 ++++---- src/poly/multiopen/verifier.rs | 24 ++++--- 11 files changed, 241 insertions(+), 196 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 5ad4ebbb..f5e7ed84 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -68,27 +68,6 @@ where } } -/// This is a 128-bit verifier challenge. -#[derive(Copy, Clone, Debug)] -pub struct Challenge(pub(crate) u128); - -/// This algorithm applies the mapping of Algorithm 1 from the -/// [Halo](https://eprint.iacr.org/2019/1021) paper. -pub fn get_challenge_scalar(challenge: Challenge) -> F { - let mut acc = (F::ZETA + F::one()).double(); - - for i in (0..64).rev() { - let should_negate = ((challenge.0 >> ((i << 1) + 1)) & 1) == 1; - let should_endo = ((challenge.0 >> (i << 1)) & 1) == 1; - - let q = if should_negate { -F::one() } else { F::one() }; - let q = if should_endo { q * F::ZETA } else { q }; - acc = acc + q + acc; - } - - acc -} - fn multiexp_serial(coeffs: &[C::Scalar], bases: &[C], acc: &mut C::Projective) { let coeffs: Vec<[u8; 32]> = coeffs.iter().map(|a| a.to_bytes()).collect(); diff --git a/src/plonk.rs b/src/plonk.rs index e938d711..2388e436 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -7,7 +7,8 @@ use crate::arithmetic::CurveAffine; use crate::poly::{ - multiopen, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, + commitment::ChallengeScalar, multiopen, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, + LagrangeCoeff, Polynomial, }; mod circuit; @@ -93,6 +94,22 @@ impl VerifyingKey { } } +#[derive(Clone, Copy, Debug)] +pub(crate) struct Beta; +pub(crate) type ChallengeBeta = ChallengeScalar; + +#[derive(Clone, Copy, Debug)] +pub(crate) struct Gamma; +pub(crate) type ChallengeGamma = ChallengeScalar; + +#[derive(Clone, Copy, Debug)] +pub(crate) struct Y; +pub(crate) type ChallengeY = ChallengeScalar; + +#[derive(Clone, Copy, Debug)] +pub(crate) struct X; +pub(crate) type ChallengeX = ChallengeScalar; + #[test] fn test_proving() { use crate::arithmetic::{Curve, FieldExt}; diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs index 4970212e..6b4f7524 100644 --- a/src/plonk/permutation/prover.rs +++ b/src/plonk/permutation/prover.rs @@ -4,7 +4,7 @@ use std::iter; use super::Proof; use crate::{ arithmetic::{eval_polynomial, parallelize, BatchInvert, Curve, CurveAffine, FieldExt}, - plonk::{Error, ProvingKey}, + plonk::{ChallengeBeta, ChallengeGamma, ChallengeX, Error, ProvingKey}, poly::{ commitment::{Blind, Params}, multiopen::ProverQuery, @@ -40,8 +40,8 @@ impl Proof { params: &Params, pk: &ProvingKey, advice: &[Polynomial], - x_0: C::Scalar, - x_1: C::Scalar, + beta: ChallengeBeta, + gamma: ChallengeGamma, transcript: &mut Transcript, ) -> Result, Error> { let domain = &pk.vk.domain; @@ -81,7 +81,7 @@ impl Proof { .zip(permuted_column_values[start..].iter()) { *modified_advice *= - &(x_0 * permuted_advice_value + &x_1 + advice_value); + &(*beta * permuted_advice_value + &gamma + advice_value); } }); } @@ -116,7 +116,7 @@ impl Proof { .zip(advice[column.index()][start..].iter()) { // Multiply by p_j(\omega^i) + \delta^j \omega^i \beta - *modified_advice *= &(deltaomega * &x_0 + &x_1 + advice_value); + *modified_advice *= &(deltaomega * &beta + &gamma + advice_value); deltaomega *= ω } }); @@ -184,8 +184,8 @@ impl Committed { self, pk: &'a ProvingKey, advice_cosets: &'a [Polynomial], - x_0: C::Scalar, - x_1: C::Scalar, + beta: ChallengeBeta, + gamma: ChallengeGamma, ) -> Result< ( Constructed, @@ -220,13 +220,13 @@ impl Committed { .zip(advice[start..].iter()) .zip(permutation[start..].iter()) { - *left *= &(*advice + &(x_0 * permutation) + &x_1); + *left *= &(*advice + &(*beta * permutation) + &gamma); } }); } let mut right = permutation_product_cosets_inv[permutation_index].clone(); - let mut current_delta = x_0 * &C::Scalar::ZETA; + let mut current_delta = *beta * &C::Scalar::ZETA; let step = domain.get_extended_omega(); for advice in columns .iter() @@ -236,7 +236,7 @@ impl Committed { 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 + &x_1); + *right *= &(*advice + &beta_term + &gamma); beta_term *= &step; } }); @@ -262,7 +262,7 @@ impl Constructed { pub(crate) fn evaluate, HScalar: Hasher>( self, pk: &ProvingKey, - x_3: C::Scalar, + x: ChallengeX, transcript: &mut Transcript, ) -> Evaluated { let domain = &pk.vk.domain; @@ -270,24 +270,19 @@ impl Constructed { let permutation_product_evals: Vec = self .permutation_product_polys .iter() - .map(|poly| eval_polynomial(poly, x_3)) + .map(|poly| eval_polynomial(poly, *x)) .collect(); let permutation_product_inv_evals: Vec = self .permutation_product_polys .iter() - .map(|poly| eval_polynomial(poly, domain.rotate_omega(x_3, Rotation(-1)))) + .map(|poly| eval_polynomial(poly, domain.rotate_omega(*x, Rotation(-1)))) .collect(); let permutation_evals: Vec> = pk .permutation_polys .iter() - .map(|polys| { - polys - .iter() - .map(|poly| eval_polynomial(poly, x_3)) - .collect() - }) + .map(|polys| polys.iter().map(|poly| eval_polynomial(poly, *x)).collect()) .collect(); // Hash each advice evaluation @@ -312,12 +307,12 @@ impl Evaluated { pub fn open<'a>( &'a self, pk: &'a ProvingKey, - x_3: C::Scalar, + x: ChallengeX, ) -> impl Iterator> + Clone { - let x_3_inv = pk.vk.domain.rotate_omega(x_3, Rotation(-1)); + let x_inv = pk.vk.domain.rotate_omega(*x, Rotation(-1)); iter::empty() - // Open permutation product commitments at x_3 + // Open permutation product commitments at x .chain( self.constructed .permutation_product_polys @@ -325,26 +320,26 @@ impl Evaluated { .zip(self.constructed.permutation_product_blinds.iter()) .zip(self.permutation_product_evals.iter()) .map(move |((poly, blind), eval)| ProverQuery { - point: x_3, + point: *x, poly, blind: *blind, eval: *eval, }), ) - // Open permutation polynomial commitments at x_3 + // Open permutation polynomial commitments at x .chain( pk.permutation_polys .iter() .zip(self.permutation_evals.iter()) .flat_map(|(polys, evals)| polys.iter().zip(evals.iter())) .map(move |(poly, eval)| ProverQuery { - point: x_3, + point: *x, poly, blind: Blind::default(), eval: *eval, }), ) - // Open permutation product commitments at \omega^{-1} x_3 + // Open permutation product commitments at \omega^{-1} x .chain( self.constructed .permutation_product_polys @@ -352,7 +347,7 @@ impl Evaluated { .zip(self.constructed.permutation_product_blinds.iter()) .zip(self.permutation_product_inv_evals.iter()) .map(move |((poly, blind), eval)| ProverQuery { - point: x_3_inv, + point: x_inv, poly, blind: *blind, eval: *eval, diff --git a/src/plonk/permutation/verifier.rs b/src/plonk/permutation/verifier.rs index afd68010..a5af78e4 100644 --- a/src/plonk/permutation/verifier.rs +++ b/src/plonk/permutation/verifier.rs @@ -4,7 +4,7 @@ use std::iter; use super::Proof; use crate::{ arithmetic::{CurveAffine, FieldExt}, - plonk::{Error, VerifyingKey}, + plonk::{ChallengeBeta, ChallengeGamma, ChallengeX, Error, VerifyingKey}, poly::{multiopen::VerifierQuery, Rotation}, transcript::{Hasher, Transcript}, }; @@ -55,9 +55,9 @@ impl Proof { vk: &'a VerifyingKey, advice_evals: &'a [C::Scalar], l_0: C::Scalar, - x_0: C::Scalar, - x_1: C::Scalar, - x_3: C::Scalar, + beta: ChallengeBeta, + gamma: ChallengeGamma, + x: ChallengeX, ) -> impl Iterator + 'a { iter::empty() // l_0(X) * (1 - z(X)) = 0 @@ -85,15 +85,15 @@ impl Proof { }) .zip(permutation_evals.iter()) { - left *= &(advice_eval + &(x_0 * permutation_eval) + &x_1); + left *= &(advice_eval + &(*beta * permutation_eval) + &gamma); } let mut right = *product_inv_eval; - let mut current_delta = x_0 * &x_3; + let mut current_delta = *beta * &x; for advice_eval in columns.iter().map(|&column| { advice_evals[vk.cs.get_advice_query_index(column, 0)] }) { - right *= &(advice_eval + ¤t_delta + &x_1); + right *= &(advice_eval + ¤t_delta + &gamma); current_delta *= &C::Scalar::DELTA; } @@ -113,44 +113,44 @@ impl Proof { pub(crate) fn queries<'a>( &'a self, vk: &'a VerifyingKey, - x_3: C::Scalar, + x: ChallengeX, ) -> impl Iterator> + Clone { - let x_3_inv = vk.domain.rotate_omega(x_3, Rotation(-1)); + let x_inv = vk.domain.rotate_omega(*x, Rotation(-1)); iter::empty() - // Open permutation product commitments at x_3 + // Open permutation product commitments at x .chain( self.permutation_product_commitments .iter() .enumerate() .zip(self.permutation_product_evals.iter()) .map(move |((idx, _), &eval)| VerifierQuery { - point: x_3, + point: *x, commitment: &self.permutation_product_commitments[idx], eval, }), ) - // Open permutation commitments for each permutation argument at x_3 + // Open permutation commitments for each permutation argument at x .chain( (0..vk.permutation_commitments.len()) .map(move |outer_idx| { let inner_len = vk.permutation_commitments[outer_idx].len(); (0..inner_len).map(move |inner_idx| VerifierQuery { - point: x_3, + point: *x, commitment: &vk.permutation_commitments[outer_idx][inner_idx], eval: self.permutation_evals[outer_idx][inner_idx], }) }) .flatten(), ) - // Open permutation product commitments at \omega^{-1} x_3 + // Open permutation product commitments at \omega^{-1} x .chain( self.permutation_product_commitments .iter() .enumerate() .zip(self.permutation_product_inv_evals.iter()) .map(move |((idx, _), &eval)| VerifierQuery { - point: x_3_inv, + point: x_inv, commitment: &self.permutation_product_commitments[idx], eval, }), diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index cc8827aa..cb46402d 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -3,11 +3,9 @@ use std::iter; use super::{ circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed}, - permutation, Error, Proof, ProvingKey, -}; -use crate::arithmetic::{ - eval_polynomial, get_challenge_scalar, Challenge, Curve, CurveAffine, FieldExt, + permutation, ChallengeBeta, ChallengeGamma, ChallengeX, ChallengeY, Error, Proof, ProvingKey, }; +use crate::arithmetic::{eval_polynomial, Curve, CurveAffine, FieldExt}; use crate::poly::{ commitment::{Blind, Params}, multiopen::{self, ProverQuery}, @@ -170,11 +168,11 @@ impl Proof { }) .collect(); - // Sample x_0 challenge - let x_0: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + // Sample beta challenge + let beta = ChallengeBeta::get(&mut transcript); - // Sample x_1 challenge - let x_1: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + // Sample gamma challenge + let gamma = ChallengeGamma::get(&mut transcript); // Commit to permutations, if any. let permutations = if !pk.vk.cs.permutations.is_empty() { @@ -182,8 +180,8 @@ impl Proof { params, pk, &witness.advice, - x_0, - x_1, + beta, + gamma, &mut transcript, )?) } else { @@ -191,11 +189,11 @@ impl Proof { }; // Obtain challenge for keeping all separate gates linearly independent - let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + 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, x_0, x_1)) + .map(|p| p.construct(pk, &advice_cosets, beta, gamma)) .transpose()? .map(|(p, expressions)| (Some(p), Some(expressions))) .unwrap_or_default(); @@ -215,7 +213,7 @@ impl Proof { })) // Permutation constraints, if any. .chain(permutation_expressions.into_iter().flatten()) - .fold(domain.empty_extended(), |h_poly, v| h_poly * x_2 + &v); + .fold(domain.empty_extended(), |h_poly, v| h_poly * *y + &v); // Divide by t(X) = X^{params.n} - 1. let h_poly = domain.divide_by_vanishing_poly(h_poly); @@ -249,14 +247,14 @@ impl Proof { .map_err(|_| Error::TranscriptError)?; } - let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x = ChallengeX::get(&mut transcript); - // Evaluate polynomials at omega^i x_3 + // 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_3, at)) + eval_polynomial(&advice_polys[column.index()], domain.rotate_omega(*x, at)) }) .collect(); @@ -264,7 +262,7 @@ impl Proof { .aux_queries .iter() .map(|&(column, at)| { - eval_polynomial(&aux_polys[column.index()], domain.rotate_omega(x_3, at)) + eval_polynomial(&aux_polys[column.index()], domain.rotate_omega(*x, at)) }) .collect(); @@ -272,16 +270,13 @@ impl Proof { .fixed_queries .iter() .map(|&(column, at)| { - eval_polynomial( - &pk.fixed_polys[column.index()], - domain.rotate_omega(x_3, at), - ) + eval_polynomial(&pk.fixed_polys[column.index()], domain.rotate_omega(*x, at)) }) .collect(); let h_evals: Vec<_> = h_pieces .iter() - .map(|poly| eval_polynomial(poly, x_3)) + .map(|poly| eval_polynomial(poly, *x)) .collect(); // Hash each advice evaluation @@ -294,14 +289,14 @@ impl Proof { transcript.absorb_scalar(*eval); } - // Evaluate the permutations, if any, at omega^i x_3. - let permutations = permutations.map(|p| p.evaluate(pk, x_3, &mut transcript)); + // Evaluate the permutations, if any, at omega^i x. + let permutations = permutations.map(|p| p.evaluate(pk, x, &mut transcript)); let instances = iter::empty() .chain(pk.vk.cs.advice_queries.iter().enumerate().map( |(query_index, &(column, at))| ProverQuery { - point: domain.rotate_omega(x_3, at), + point: domain.rotate_omega(*x, at), poly: &advice_polys[column.index()], blind: advice_blinds[column.index()], eval: advice_evals[query_index], @@ -309,7 +304,7 @@ impl Proof { )) .chain(pk.vk.cs.aux_queries.iter().enumerate().map( |(query_index, &(column, at))| ProverQuery { - point: domain.rotate_omega(x_3, at), + point: domain.rotate_omega(*x, at), poly: &aux_polys[column.index()], blind: Blind::default(), eval: aux_evals[query_index], @@ -317,20 +312,20 @@ impl Proof { )) .chain(pk.vk.cs.fixed_queries.iter().enumerate().map( |(query_index, &(column, at))| ProverQuery { - point: domain.rotate_omega(x_3, at), + point: domain.rotate_omega(*x, at), poly: &pk.fixed_polys[column.index()], blind: Blind::default(), eval: fixed_evals[query_index], }, )) - // We query the h(X) polynomial at x_3 + // We query the h(X) polynomial at x .chain( h_pieces .iter() .zip(h_blinds.iter()) .zip(h_evals.iter()) .map(|((h_poly, h_blind), h_eval)| ProverQuery { - point: x_3, + point: *x, poly: h_poly, blind: *h_blind, eval: *h_eval, @@ -343,7 +338,7 @@ impl Proof { instances.chain( permutations .as_ref() - .map(|p| p.open(pk, x_3)) + .map(|p| p.open(pk, x)) .into_iter() .flatten(), ), diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 4450d285..4925dbd0 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -1,8 +1,8 @@ use ff::Field; use std::iter; -use super::{Error, Proof, VerifyingKey}; -use crate::arithmetic::{get_challenge_scalar, Challenge, CurveAffine, FieldExt}; +use super::{ChallengeBeta, ChallengeGamma, ChallengeX, ChallengeY, Error, Proof, VerifyingKey}; +use crate::arithmetic::{CurveAffine, FieldExt}; use crate::poly::{ commitment::{Guard, Params, MSM}, multiopen::VerifierQuery, @@ -45,19 +45,19 @@ impl<'a, C: CurveAffine> Proof { .map_err(|_| Error::TranscriptError)?; } - // Sample x_0 challenge - let x_0: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + // Sample beta challenge + let beta = ChallengeBeta::get(&mut transcript); - // Sample x_1 challenge - let x_1: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + // Sample gamma challenge + let gamma = ChallengeGamma::get(&mut transcript); // Hash each permutation product commitment if let Some(p) = &self.permutations { p.absorb_commitments(&mut transcript)?; } - // Sample x_2 challenge, which keeps the gates linearly independent. - let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + // Sample y challenge, which keeps the gates linearly independent. + let y = ChallengeY::get(&mut transcript); // Obtain a commitment to h(X) in the form of multiple pieces of degree n - 1 for c in &self.h_commitments { @@ -66,13 +66,13 @@ impl<'a, C: CurveAffine> Proof { .map_err(|_| Error::TranscriptError)?; } - // Sample x_3 challenge, which is used to ensure the circuit is + // Sample x challenge, which is used to ensure the circuit is // satisfied with high probability. - let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x = ChallengeX::get(&mut transcript); // This check ensures the circuit is satisfied so long as the polynomial // commitments open to the correct values. - self.check_hx(params, vk, x_0, x_1, x_2, x_3)?; + self.check_hx(params, vk, beta, gamma, y, x)?; for eval in self .advice_evals @@ -95,7 +95,7 @@ impl<'a, C: CurveAffine> Proof { iter::empty() .chain(vk.cs.advice_queries.iter().enumerate().map( |(query_index, &(column, at))| VerifierQuery { - point: vk.domain.rotate_omega(x_3, at), + point: vk.domain.rotate_omega(*x, at), commitment: &self.advice_commitments[column.index()], eval: self.advice_evals[query_index], }, @@ -106,14 +106,14 @@ impl<'a, C: CurveAffine> Proof { .iter() .enumerate() .map(|(query_index, &(column, at))| VerifierQuery { - point: vk.domain.rotate_omega(x_3, at), + point: vk.domain.rotate_omega(*x, at), commitment: &aux_commitments[column.index()], eval: self.aux_evals[query_index], }), ) .chain(vk.cs.fixed_queries.iter().enumerate().map( |(query_index, &(column, at))| VerifierQuery { - point: vk.domain.rotate_omega(x_3, at), + point: vk.domain.rotate_omega(*x, at), commitment: &vk.fixed_commitments[column.index()], eval: self.fixed_evals[query_index], }, @@ -124,7 +124,7 @@ impl<'a, C: CurveAffine> Proof { .enumerate() .zip(self.h_evals.iter()) .map(|((idx, _), &eval)| VerifierQuery { - point: x_3, + point: *x, commitment: &self.h_commitments[idx], eval, }), @@ -139,7 +139,7 @@ impl<'a, C: CurveAffine> Proof { queries.chain( self.permutations .as_ref() - .map(|p| p.queries(vk, x_3)) + .map(|p| p.queries(vk, x)) .into_iter() .flatten(), ), @@ -189,21 +189,21 @@ impl<'a, C: CurveAffine> Proof { &self, params: &'a Params, vk: &VerifyingKey, - x_0: C::Scalar, - x_1: C::Scalar, - x_2: C::Scalar, - x_3: C::Scalar, + beta: ChallengeBeta, + gamma: ChallengeGamma, + y: ChallengeY, + x: ChallengeX, ) -> Result<(), Error> { - // x_3^n - let x_3n = x_3.pow(&[params.n as u64, 0, 0, 0]); + // x^n + let xn = x.pow(&[params.n as u64, 0, 0, 0]); // TODO: bubble this error up - // l_0(x_3) - let l_0 = (x_3 - &C::Scalar::one()).invert().unwrap() // 1 / (x_3 - 1) - * &(x_3n - &C::Scalar::one()) // (x_3^n - 1) / (x_3 - 1) - * &vk.domain.get_barycentric_weight(); // l_0(x_3) + // l_0(x) + let l_0 = (*x - &C::Scalar::one()).invert().unwrap() // 1 / (x - 1) + * &(xn - &C::Scalar::one()) // (x^n - 1) / (x - 1) + * &vk.domain.get_barycentric_weight(); // l_0(x) - // Compute the expected value of h(x_3) + // Compute the expected value of h(x) let expected_h_eval = std::iter::empty() // Evaluate the circuit using the custom gates provided .chain(vk.cs.gates.iter().map(|poly| { @@ -219,21 +219,21 @@ impl<'a, C: CurveAffine> Proof { .chain( self.permutations .as_ref() - .map(|p| p.expressions(vk, &self.advice_evals, l_0, x_0, x_1, x_3)) + .map(|p| p.expressions(vk, &self.advice_evals, l_0, beta, gamma, x)) .into_iter() .flatten(), ) - .fold(C::Scalar::zero(), |h_eval, v| h_eval * &x_2 + &v); + .fold(C::Scalar::zero(), |h_eval, v| h_eval * &y + &v); - // Compute h(x_3) from the prover + // Compute h(x) from the prover let h_eval = self .h_evals .iter() .rev() - .fold(C::Scalar::zero(), |acc, eval| acc * &x_3n + eval); + .fold(C::Scalar::zero(), |acc, eval| acc * &xn + eval); // Did the prover commit to the correct polynomial? - if expected_h_eval != (h_eval * &(x_3n - &C::Scalar::one())) { + if expected_h_eval != (h_eval * &(xn - &C::Scalar::one())) { return Err(Error::ConstraintSystemFailure); } diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 6fc4b00d..c0d4520a 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -5,10 +5,11 @@ use super::{Coeff, LagrangeCoeff, Polynomial}; use crate::arithmetic::{best_fft, best_multiexp, parallelize, Curve, CurveAffine, FieldExt}; -use crate::transcript::Hasher; +use crate::transcript::{Hasher, Transcript}; use ff::{Field, PrimeField}; -use std::ops::{Add, AddAssign, Mul, MulAssign}; +use std::marker::PhantomData; +use std::ops::{Add, AddAssign, Deref, Mul, MulAssign}; mod msm; mod prover; @@ -17,6 +18,78 @@ mod verifier; pub use msm::MSM; pub use verifier::{Accumulator, Guard}; +/// This is a 128-bit verifier challenge. +#[derive(Copy, Clone, Debug)] +pub struct Challenge(pub(crate) u128); + +impl Challenge { + /// Obtains a new challenge from the transcript. + pub fn get(transcript: &mut Transcript) -> Challenge + where + C: CurveAffine, + HBase: Hasher, + HScalar: Hasher, + { + Challenge(transcript.squeeze().get_lower_128()) + } +} + +/// The scalar representation of a verifier challenge. +/// +/// The `T` type can be used to scope the challenge to a specific context, or set to `()` +/// if no context is required. +#[derive(Copy, Clone, Debug)] +pub struct ChallengeScalar { + inner: F, + _marker: PhantomData, +} + +impl From for ChallengeScalar { + /// This algorithm applies the mapping of Algorithm 1 from the + /// [Halo](https://eprint.iacr.org/2019/1021) paper. + fn from(challenge: Challenge) -> Self { + let mut acc = (F::ZETA + F::one()).double(); + + for i in (0..64).rev() { + let should_negate = ((challenge.0 >> ((i << 1) + 1)) & 1) == 1; + let should_endo = ((challenge.0 >> (i << 1)) & 1) == 1; + + let q = if should_negate { -F::one() } else { F::one() }; + let q = if should_endo { q * F::ZETA } else { q }; + acc = acc + q + acc; + } + + ChallengeScalar { + inner: acc, + _marker: PhantomData::default(), + } + } +} + +impl ChallengeScalar { + /// Obtains a new challenge from the transcript. + pub fn get(transcript: &mut Transcript) -> Self + where + C: CurveAffine, + HBase: Hasher, + HScalar: Hasher, + { + Challenge::get(transcript).into() + } +} + +impl Deref for ChallengeScalar { + type Target = F; + + fn deref(&self) -> &F { + &self.inner + } +} + +#[derive(Clone, Copy, Debug)] +pub(crate) struct X6 {} +pub(crate) type ChallengeX6 = ChallengeScalar; + /// These are the public parameters for the polynomial commitment scheme. #[derive(Debug)] pub struct Params { @@ -176,13 +249,13 @@ impl Params { #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub struct Blind(pub F); -impl Default for Blind { +impl Default for Blind { fn default() -> Self { Blind(F::one()) } } -impl Add for Blind { +impl Add for Blind { type Output = Self; fn add(self, rhs: Blind) -> Self { @@ -190,7 +263,7 @@ impl Add for Blind { } } -impl Mul for Blind { +impl Mul for Blind { type Output = Self; fn mul(self, rhs: Blind) -> Self { @@ -198,25 +271,25 @@ impl Mul for Blind { } } -impl AddAssign for Blind { +impl AddAssign for Blind { fn add_assign(&mut self, rhs: Blind) { self.0 += rhs.0; } } -impl MulAssign for Blind { +impl MulAssign for Blind { fn mul_assign(&mut self, rhs: Blind) { self.0 *= rhs.0; } } -impl AddAssign for Blind { +impl AddAssign for Blind { fn add_assign(&mut self, rhs: F) { self.0 += rhs; } } -impl MulAssign for Blind { +impl MulAssign for Blind { fn mul_assign(&mut self, rhs: F) { self.0 *= rhs; } @@ -254,9 +327,7 @@ fn test_opening_proof() { commitment::{Blind, Params}, EvaluationDomain, }; - use crate::arithmetic::{ - eval_polynomial, get_challenge_scalar, Challenge, Curve, CurveAffine, FieldExt, - }; + use crate::arithmetic::{eval_polynomial, Curve, FieldExt}; use crate::transcript::{DummyHash, Transcript}; use crate::tweedle::{EpAffine, Fp, Fq}; @@ -275,10 +346,9 @@ fn test_opening_proof() { let mut transcript = Transcript::<_, DummyHash<_>, DummyHash<_>>::new(); transcript.absorb_point(&p).unwrap(); - let x_packed = transcript.squeeze().get_lower_128(); - let x: Fq = get_challenge_scalar(Challenge(x_packed)); + let x = ChallengeX6::get(&mut transcript); // Evaluate the polynomial - let v = eval_polynomial(&px, x); + let v = eval_polynomial(&px, *x); transcript.absorb_base(Fp::from_bytes(&v.to_bytes()).unwrap()); // unlikely to fail since p ~ q diff --git a/src/poly/commitment/prover.rs b/src/poly/commitment/prover.rs index 5c9def6d..0f029782 100644 --- a/src/poly/commitment/prover.rs +++ b/src/poly/commitment/prover.rs @@ -1,10 +1,9 @@ use ff::Field; use super::super::{Coeff, Error, Polynomial}; -use super::{Blind, Params, Proof}; +use super::{Blind, Challenge, ChallengeScalar, ChallengeX6, Params, Proof}; use crate::arithmetic::{ - best_multiexp, compute_inner_product, get_challenge_scalar, parallelize, small_multiexp, - Challenge, Curve, CurveAffine, FieldExt, + best_multiexp, compute_inner_product, parallelize, small_multiexp, Curve, CurveAffine, FieldExt, }; use crate::transcript::{Hasher, Transcript}; @@ -22,12 +21,12 @@ impl Proof { /// opening v, and the point x. It's probably also nice for the transcript /// to have seen the elliptic curve description and the SRS, if you want to /// be rigorous. - pub fn create( + pub(crate) fn create( params: &Params, transcript: &mut Transcript, px: &Polynomial, blind: Blind, - x: C::Scalar, + x: ChallengeX6, ) -> Result where HBase: Hasher, @@ -108,8 +107,8 @@ impl Proof { .map_err(|_| Error::SamplingError)?; // ... and get the squared challenge. - let challenge_sq_packed = transcript.squeeze().get_lower_128(); - let challenge_sq: C::Scalar = get_challenge_scalar(Challenge(challenge_sq_packed)); + let challenge_sq_packed = Challenge::get(&mut transcript); + let challenge_sq: C::Scalar = *ChallengeScalar::<_, ()>::from(challenge_sq_packed); // There might be no square root, in which case we'll fork the // transcript. @@ -139,9 +138,8 @@ impl Proof { // And obtain the challenge, even though we already have it, since // squeezing affects the transcript. { - let challenge_sq_packed = transcript.squeeze().get_lower_128(); - let challenge_sq_expected = get_challenge_scalar(Challenge(challenge_sq_packed)); - assert_eq!(challenge_sq, challenge_sq_expected); + let challenge_sq_expected = ChallengeScalar::<_, ()>::get(transcript); + assert_eq!(challenge_sq, *challenge_sq_expected); } // Done with this round. @@ -186,12 +184,11 @@ impl Proof { .map_err(|_| Error::SamplingError)?; // Obtain the challenge c. - let c_packed = transcript.squeeze().get_lower_128(); - let c: C::Scalar = get_challenge_scalar(Challenge(c_packed)); + let c = ChallengeScalar::<_, ()>::get(transcript); // Compute z1 and z2 as described in the Halo paper. let z1 = a * &c + &d; - let z2 = c * &blind + &s; + let z2 = *c * &blind + &s; Ok(Proof { rounds, diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index f92dc433..b9001fab 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -1,12 +1,10 @@ use ff::Field; use super::super::Error; -use super::{Params, Proof, MSM}; +use super::{Challenge, ChallengeScalar, ChallengeX6, Params, Proof, MSM}; use crate::transcript::{Hasher, Transcript}; -use crate::arithmetic::{ - best_multiexp, get_challenge_scalar, Challenge, Curve, CurveAffine, FieldExt, -}; +use crate::arithmetic::{best_multiexp, Curve, CurveAffine, FieldExt}; /// A guard returned by the verifier #[derive(Debug, Clone)] @@ -68,12 +66,12 @@ impl Proof { /// Checks to see if an [`Proof`] is valid given the current `transcript`, /// and a point `x` that the polynomial commitment `p` opens purportedly to /// the value `v`. - pub fn verify<'a, HBase, HScalar>( + pub(crate) fn verify<'a, HBase, HScalar>( &self, params: &'a Params, mut msm: MSM<'a, C>, transcript: &mut Transcript, - x: C::Scalar, + x: ChallengeX6, mut commitment_msm: MSM<'a, C>, v: C::Scalar, ) -> Result, Error> @@ -120,8 +118,8 @@ impl Proof { transcript .absorb_point(&r) .map_err(|_| Error::OpeningError)?; - let challenge_sq_packed = transcript.squeeze().get_lower_128(); - let challenge_sq: C::Scalar = get_challenge_scalar(Challenge(challenge_sq_packed)); + let challenge_sq_packed = Challenge::get(transcript); + let challenge_sq: C::Scalar = *ChallengeScalar::<_, ()>::from(challenge_sq_packed); let challenge = challenge_sq.deterministic_sqrt(); if challenge.is_none() { @@ -149,7 +147,7 @@ impl Proof { challenges.push(challenge); challenges_inv.push(challenge_inv); challenges_sq.push(challenge_sq); - challenges_sq_packed.push(Challenge(challenge_sq_packed)); + challenges_sq_packed.push(challenge_sq_packed); } // Feed delta into the transcript @@ -158,8 +156,7 @@ impl Proof { .map_err(|_| Error::OpeningError)?; // Get the challenge `c` - let c_packed = transcript.squeeze().get_lower_128(); - let c: C::Scalar = get_challenge_scalar(Challenge(c_packed)); + let c = ChallengeScalar::<_, ()>::get(transcript); // Construct // [c] P + [c * v] U + [c] sum(L_i * u_i^2) + [c] sum(R_i * u_i^-2) + delta - [z1 * b] U + [z1 - z2] H @@ -167,17 +164,17 @@ impl Proof { // The computation of [z1] (G + H) happens in either Guard::use_challenges() // or Guard::use_g(). - let b = compute_b(x, &challenges, &challenges_inv); + let b = compute_b(*x, &challenges, &challenges_inv); let neg_z1 = -self.z1; // [c] P - commitment_msm.scale(c); + commitment_msm.scale(*c); msm.add_msm(&commitment_msm); // [c] sum(L_i * u_i^2) + [c] sum(R_i * u_i^-2) for scalar in &mut extra_scalars { - *scalar *= &c; + *scalar *= &(*c); } for (scalar, base) in extra_scalars.iter().zip(extra_bases.iter()) { @@ -185,7 +182,7 @@ impl Proof { } // [c * v] U - [z1 * b] U - msm.append_term((c * &v) + &(neg_z1 * &b), u); + msm.append_term((*c * &v) + &(neg_z1 * &b), u); // delta msm.append_term(Field::one(), self.delta); diff --git a/src/poly/multiopen/prover.rs b/src/poly/multiopen/prover.rs index f512a740..e14c6db9 100644 --- a/src/poly/multiopen/prover.rs +++ b/src/poly/multiopen/prover.rs @@ -1,12 +1,11 @@ use super::super::{ - commitment::{self, Blind, Params}, + commitment::{self, Blind, ChallengeScalar, ChallengeX6, Params}, Coeff, Error, Polynomial, }; use super::{construct_intermediate_sets, Proof, ProverQuery, Query}; use crate::arithmetic::{ - eval_polynomial, get_challenge_scalar, kate_division, lagrange_interpolate, Challenge, Curve, - CurveAffine, FieldExt, + eval_polynomial, kate_division, lagrange_interpolate, Curve, CurveAffine, FieldExt, }; use crate::transcript::{Hasher, Transcript}; @@ -31,8 +30,8 @@ impl Proof { where I: IntoIterator> + Clone, { - let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x_4 = ChallengeScalar::<_, ()>::get(transcript); + let x_5 = ChallengeScalar::<_, ()>::get(transcript); let (poly_map, point_sets) = construct_intermediate_sets(queries); @@ -54,11 +53,11 @@ impl Proof { blind: Blind, evals: Vec| { if let Some(poly) = &q_polys[set_idx] { - q_polys[set_idx] = Some(poly.clone() * x_4 + new_poly); + q_polys[set_idx] = Some(poly.clone() * *x_4 + new_poly); } else { q_polys[set_idx] = Some(new_poly.clone()); } - q_blinds[set_idx] *= x_4; + q_blinds[set_idx] *= *x_4; q_blinds[set_idx] += blind; // Each polynomial is evaluated at a set of points. For each set, // we collapse each polynomial's evals pointwise. @@ -100,7 +99,7 @@ impl Proof { if f_poly.is_none() { Some(poly) } else { - f_poly.map(|f_poly| f_poly * x_5 + &poly) + f_poly.map(|f_poly| f_poly * *x_5 + &poly) } }) .unwrap(); @@ -114,26 +113,24 @@ impl Proof { .absorb_point(&f_commitment) .map_err(|_| Error::SamplingError)?; - let x_6: C::Scalar = - get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x_6 = ChallengeX6::get(&mut transcript); let q_evals: Vec = q_polys .iter() - .map(|poly| eval_polynomial(poly.as_ref().unwrap(), x_6)) + .map(|poly| eval_polynomial(poly.as_ref().unwrap(), *x_6)) .collect(); for eval in q_evals.iter() { transcript.absorb_scalar(*eval); } - let x_7: C::Scalar = - get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x_7 = ChallengeScalar::<_, ()>::get(&mut transcript); let (f_poly, f_blind_try) = q_polys.iter().zip(q_blinds.iter()).fold( (f_poly.clone(), f_blind), |(f_poly, f_blind), (poly, blind)| { ( - f_poly * x_7 + poly.as_ref().unwrap(), + f_poly * *x_7 + poly.as_ref().unwrap(), Blind((f_blind.0 * &x_7) + &blind.0), ) }, diff --git a/src/poly/multiopen/verifier.rs b/src/poly/multiopen/verifier.rs index 48dfb59a..c265d0f6 100644 --- a/src/poly/multiopen/verifier.rs +++ b/src/poly/multiopen/verifier.rs @@ -1,13 +1,11 @@ use ff::Field; use super::super::{ - commitment::{Guard, Params, MSM}, + commitment::{ChallengeScalar, ChallengeX6, Guard, Params, MSM}, Error, }; use super::{construct_intermediate_sets, Proof, Query, VerifierQuery}; -use crate::arithmetic::{ - eval_polynomial, get_challenge_scalar, lagrange_interpolate, Challenge, CurveAffine, FieldExt, -}; +use crate::arithmetic::{eval_polynomial, lagrange_interpolate, CurveAffine, FieldExt}; use crate::transcript::{Hasher, Transcript}; #[derive(Debug, Clone)] @@ -35,15 +33,15 @@ impl Proof { msm.scale(C::Scalar::rand()); // Sample x_4 for compressing openings at the same point sets together - let x_4: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x_4 = ChallengeScalar::<_, ()>::get(transcript); // Sample a challenge x_5 for keeping the multi-point quotient // polynomial terms linearly independent. - let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x_5 = ChallengeScalar::<_, ()>::get(transcript); let (commitment_map, point_sets) = construct_intermediate_sets(queries); - // Compress the commitments and expected evaluations at x_3 together. + // Compress the commitments and expected evaluations at x together. // using the challenge x_4 let mut q_commitments: Vec<_> = vec![params.empty_msm(); point_sets.len()]; @@ -55,7 +53,7 @@ impl Proof { } { let mut accumulate = |set_idx: usize, new_commitment, evals: Vec| { - q_commitments[set_idx].scale(x_4); + q_commitments[set_idx].scale(*x_4); q_commitments[set_idx].append_term(C::Scalar::one(), new_commitment); for (eval, set_eval) in evals.iter().zip(q_eval_sets[set_idx].iter_mut()) { *set_eval *= &x_4; @@ -81,7 +79,7 @@ impl Proof { // Sample a challenge x_6 for checking that f(X) was committed to // correctly. - let x_6: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x_6 = ChallengeX6::get(transcript); for eval in self.q_evals.iter() { transcript.absorb_scalar(*eval); @@ -97,9 +95,9 @@ impl Proof { C::Scalar::zero(), |msm_eval, ((points, evals), proof_eval)| { let r_poly = lagrange_interpolate(points, evals); - let r_eval = eval_polynomial(&r_poly, x_6); + let r_eval = eval_polynomial(&r_poly, *x_6); let eval = points.iter().fold(*proof_eval - &r_eval, |eval, point| { - eval * &(x_6 - point).invert().unwrap() + eval * &(*x_6 - point).invert().unwrap() }); msm_eval * &x_5 + &eval }, @@ -107,7 +105,7 @@ impl Proof { // Sample a challenge x_7 that we will use to collapse the openings of // the various remaining polynomials at x_6 together. - let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let x_7 = ChallengeScalar::<_, ()>::get(transcript); // Compute the final commitment that has to be opened let mut commitment_msm = params.empty_msm(); @@ -115,7 +113,7 @@ impl Proof { let (commitment_msm, msm_eval) = q_commitments.into_iter().zip(self.q_evals.iter()).fold( (commitment_msm, msm_eval), |(mut commitment_msm, msm_eval), (q_commitment, q_eval)| { - commitment_msm.scale(x_7); + commitment_msm.scale(*x_7); commitment_msm.add_msm(&q_commitment); (commitment_msm, msm_eval * &x_7 + q_eval) }, From 66240800a3be54c17891d4f60780dfd545316c46 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 1 Dec 2020 02:09:03 +0000 Subject: [PATCH 04/12] Move permutation keygen into plonk::permutation::keygen --- src/plonk.rs | 8 +- src/plonk/circuit.rs | 7 +- src/plonk/keygen.rs | 153 +++---------------------- src/plonk/permutation.rs | 40 ++++++- src/plonk/permutation/keygen.rs | 179 ++++++++++++++++++++++++++++++ src/plonk/permutation/prover.rs | 140 +++++++++++++---------- src/plonk/permutation/verifier.rs | 19 ++-- 7 files changed, 332 insertions(+), 214 deletions(-) create mode 100644 src/plonk/permutation/keygen.rs diff --git a/src/plonk.rs b/src/plonk.rs index 2388e436..4d67c7d7 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -8,7 +8,7 @@ use crate::arithmetic::CurveAffine; use crate::poly::{ commitment::ChallengeScalar, multiopen, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, - LagrangeCoeff, Polynomial, + Polynomial, }; mod circuit; @@ -28,7 +28,7 @@ pub use verifier::*; pub struct VerifyingKey { domain: EvaluationDomain, fixed_commitments: Vec, - permutation_commitments: Vec>, + permutations: Vec>, cs: ConstraintSystem, } @@ -41,9 +41,7 @@ pub struct ProvingKey { l0: Polynomial, fixed_polys: Vec>, fixed_cosets: Vec>, - permutations: Vec>>, - permutation_polys: Vec>>, - permutation_cosets: Vec>>, + permutations: Vec>, } /// This is an object which represents a (Turbo)PLONK proof. diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index c5447c52..151ac67a 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -4,7 +4,7 @@ use ff::Field; use std::collections::BTreeMap; use std::convert::TryFrom; -use super::Error; +use super::{permutation, Error}; use crate::poly::Rotation; /// A column type @@ -312,7 +312,7 @@ pub struct ConstraintSystem { // Vector of permutation arguments, where each corresponds to a sequence of columns // that are involved in a permutation argument. - pub(crate) permutations: Vec>>, + pub(crate) permutations: Vec, } impl Default for ConstraintSystem { @@ -347,7 +347,8 @@ impl ConstraintSystem { for column in columns { self.query_advice_index(*column, 0); } - self.permutations.push(columns.to_vec()); + self.permutations + .push(permutation::Argument::new(columns.to_vec())); index } diff --git a/src/plonk/keygen.rs b/src/plonk/keygen.rs index 5b7af796..1b726255 100644 --- a/src/plonk/keygen.rs +++ b/src/plonk/keygen.rs @@ -2,9 +2,9 @@ use ff::Field; use super::{ circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed}, - Error, ProvingKey, VerifyingKey, + permutation, Error, ProvingKey, VerifyingKey, }; -use crate::arithmetic::{Curve, CurveAffine, FieldExt}; +use crate::arithmetic::{Curve, CurveAffine}; use crate::poly::{ commitment::{Blind, Params}, EvaluationDomain, LagrangeCoeff, Polynomial, Rotation, @@ -21,9 +21,7 @@ where { struct Assembly { fixed: Vec>, - mapping: Vec>>, - aux: Vec>>, - sizes: Vec>>, + permutations: permutation::keygen::Assembly, _marker: std::marker::PhantomData, } @@ -61,62 +59,22 @@ where right_column: usize, right_row: usize, ) -> Result<(), Error> { - // Check bounds first - if permutation >= self.mapping.len() - || left_column >= self.mapping[permutation].len() - || left_row >= self.mapping[permutation][left_column].len() - || right_column >= self.mapping[permutation].len() - || right_row >= self.mapping[permutation][right_column].len() - { - return Err(Error::BoundsFailure); - } - - let mut left_cycle = self.aux[permutation][left_column][left_row]; - let mut right_cycle = self.aux[permutation][right_column][right_row]; - - if left_cycle == right_cycle { - return Ok(()); - } - - if self.sizes[permutation][left_cycle.0][left_cycle.1] - < self.sizes[permutation][right_cycle.0][right_cycle.1] - { - std::mem::swap(&mut left_cycle, &mut right_cycle); - } - - self.sizes[permutation][left_cycle.0][left_cycle.1] += - self.sizes[permutation][right_cycle.0][right_cycle.1]; - let mut i = right_cycle; - loop { - self.aux[permutation][i.0][i.1] = left_cycle; - i = self.mapping[permutation][i.0][i.1]; - if i == right_cycle { - break; - } - } - - let tmp = self.mapping[permutation][left_column][left_row]; - self.mapping[permutation][left_column][left_row] = - self.mapping[permutation][right_column][right_row]; - self.mapping[permutation][right_column][right_row] = tmp; - - Ok(()) + self.permutations + .copy(permutation, left_column, left_row, right_column, right_row) } } let mut cs = ConstraintSystem::default(); let config = ConcreteCircuit::configure(&mut cs); - // Get the largest permutation argument length in terms of the number of - // advice columns involved. - let mut largest_permutation_length = 0; - for permutation in &cs.permutations { - largest_permutation_length = std::cmp::max(permutation.len(), largest_permutation_length); - } - // The permutation argument will serve alongside the gates, so must be // accounted for. - let mut degree = largest_permutation_length + 1; + let mut degree = cs + .permutations + .iter() + .map(|p| p.required_degree()) + .max() + .unwrap_or(1); // Account for each gate to ensure our quotient polynomial is the // correct degree and that our extended domain is the right size. @@ -126,95 +84,16 @@ where let domain = EvaluationDomain::new(degree as u32, params.k); - // Compute [omega^0, omega^1, ..., omega^{params.n - 1}] - let mut omega_powers = Vec::with_capacity(params.n as usize); - { - let mut cur = C::Scalar::one(); - for _ in 0..params.n { - omega_powers.push(cur); - cur *= &domain.get_omega(); - } - } - - // Compute [omega_powers * \delta^0, omega_powers * \delta^1, ..., omega_powers * \delta^m] - let mut deltaomega = Vec::with_capacity(largest_permutation_length); - { - let mut cur = C::Scalar::one(); - for _ in 0..largest_permutation_length { - let mut omega_powers = omega_powers.clone(); - for o in &mut omega_powers { - *o *= &cur; - } - - deltaomega.push(omega_powers); - - cur *= &C::Scalar::DELTA; - } - } - let mut assembly: Assembly = Assembly { fixed: vec![domain.empty_lagrange(); cs.num_fixed_columns], - mapping: vec![], - aux: vec![], - sizes: vec![], + permutations: permutation::keygen::Assembly::new(params, &cs), _marker: std::marker::PhantomData, }; - // Initialize the copy vector to keep track of copy constraints in all - // the permutation arguments. - for permutation in &cs.permutations { - let mut columns = vec![]; - for i in 0..permutation.len() { - // Computes [(i, 0), (i, 1), ..., (i, n - 1)] - columns.push((0..params.n).map(|j| (i, j as usize)).collect()); - } - assembly.mapping.push(columns.clone()); - assembly.aux.push(columns); - assembly - .sizes - .push(vec![vec![1usize; params.n as usize]; permutation.len()]); - } - // Synthesize the circuit to obtain SRS circuit.synthesize(&mut assembly, config)?; - // Compute permutation polynomials, convert to coset form and - // pre-compute commitments for the SRS. - let mut permutation_commitments = vec![]; - let mut permutations = vec![]; - let mut permutation_polys = vec![]; - let mut permutation_cosets = vec![]; - for (permutation_index, permutation) in cs.permutations.iter().enumerate() { - let mut commitments = vec![]; - let mut inner_permutations = vec![]; - let mut polys = vec![]; - let mut cosets = vec![]; - for i in 0..permutation.len() { - // Computes the permutation polynomial based on the permutation - // description in the assembly. - let mut permutation_poly = domain.empty_lagrange(); - for (j, p) in permutation_poly.iter_mut().enumerate() { - let (permuted_i, permuted_j) = assembly.mapping[permutation_index][i][j]; - *p = deltaomega[permuted_i][permuted_j]; - } - - // Compute commitment to permutation polynomial - commitments.push( - params - .commit_lagrange(&permutation_poly, Blind::default()) - .to_affine(), - ); - // Store permutation polynomial and precompute its coset evaluation - inner_permutations.push(permutation_poly.clone()); - let poly = domain.lagrange_to_coeff(permutation_poly); - polys.push(poly.clone()); - cosets.push(domain.coeff_to_extended(poly, Rotation::default())); - } - permutation_commitments.push(commitments); - permutations.push(inner_permutations); - permutation_polys.push(polys); - permutation_cosets.push(cosets); - } + let (permutation_pks, permutation_vks) = assembly.permutations.build_keys(params, &cs, &domain); let fixed_commitments = assembly .fixed @@ -248,14 +127,12 @@ where vk: VerifyingKey { domain, fixed_commitments, - permutation_commitments, + permutations: permutation_vks, cs, }, l0, fixed_polys, fixed_cosets, - permutations, - permutation_polys, - permutation_cosets, + permutations: permutation_pks, }) } diff --git a/src/plonk/permutation.rs b/src/plonk/permutation.rs index 74a47141..deda02bd 100644 --- a/src/plonk/permutation.rs +++ b/src/plonk/permutation.rs @@ -1,10 +1,48 @@ //! Implementation of a PLONK permutation argument. -use crate::arithmetic::CurveAffine; +use super::circuit::{Advice, Column}; +use crate::{ + arithmetic::CurveAffine, + poly::{Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial}, +}; +pub(crate) mod keygen; mod prover; mod verifier; +/// A permutation argument. +#[derive(Debug, Clone)] +pub(crate) struct Argument { + /// A sequence of columns involved in the argument. + columns: Vec>, +} + +impl Argument { + pub(crate) fn new(columns: Vec>) -> Self { + Argument { columns } + } + + pub(crate) fn required_degree(&self) -> usize { + // The permutation argument will serve alongside the gates, so must be + // accounted for. + self.columns.len() + 1 + } +} + +/// The verifying key for a single permutation argument. +#[derive(Debug)] +pub(crate) struct VerifyingKey { + commitments: Vec, +} + +/// The proving key for a single permutation argument. +#[derive(Debug)] +pub(crate) struct ProvingKey { + permutations: Vec>, + polys: Vec>, + cosets: Vec>, +} + #[derive(Debug, Clone)] pub(crate) struct Proof { permutation_product_commitments: Vec, diff --git a/src/plonk/permutation/keygen.rs b/src/plonk/permutation/keygen.rs new file mode 100644 index 00000000..960cb04f --- /dev/null +++ b/src/plonk/permutation/keygen.rs @@ -0,0 +1,179 @@ +use ff::Field; + +use super::{ProvingKey, VerifyingKey}; +use crate::{ + arithmetic::{Curve, CurveAffine, FieldExt}, + plonk::{circuit::ConstraintSystem, Error}, + poly::{ + commitment::{Blind, Params}, + EvaluationDomain, Rotation, + }, +}; + +pub(crate) struct Assembly { + mapping: Vec>>, + aux: Vec>>, + sizes: Vec>>, +} + +impl Assembly { + pub(crate) fn new( + params: &Params, + cs: &ConstraintSystem, + ) -> Self { + let mut assembly = Assembly { + mapping: vec![], + aux: vec![], + sizes: vec![], + }; + + // Initialize the copy vector to keep track of copy constraints in all + // the permutation arguments. + for p in &cs.permutations { + let mut columns = vec![]; + for i in 0..p.columns.len() { + // Computes [(i, 0), (i, 1), ..., (i, n - 1)] + columns.push((0..params.n).map(|j| (i, j as usize)).collect()); + } + assembly.mapping.push(columns.clone()); + assembly.aux.push(columns); + assembly + .sizes + .push(vec![vec![1usize; params.n as usize]; p.columns.len()]); + } + + assembly + } + + pub(crate) fn copy( + &mut self, + permutation: usize, + left_column: usize, + left_row: usize, + right_column: usize, + right_row: usize, + ) -> Result<(), Error> { + // Check bounds first + if permutation >= self.mapping.len() + || left_column >= self.mapping[permutation].len() + || left_row >= self.mapping[permutation][left_column].len() + || right_column >= self.mapping[permutation].len() + || right_row >= self.mapping[permutation][right_column].len() + { + return Err(Error::BoundsFailure); + } + + let mut left_cycle = self.aux[permutation][left_column][left_row]; + let mut right_cycle = self.aux[permutation][right_column][right_row]; + + if left_cycle == right_cycle { + return Ok(()); + } + + if self.sizes[permutation][left_cycle.0][left_cycle.1] + < self.sizes[permutation][right_cycle.0][right_cycle.1] + { + std::mem::swap(&mut left_cycle, &mut right_cycle); + } + + self.sizes[permutation][left_cycle.0][left_cycle.1] += + self.sizes[permutation][right_cycle.0][right_cycle.1]; + let mut i = right_cycle; + loop { + self.aux[permutation][i.0][i.1] = left_cycle; + i = self.mapping[permutation][i.0][i.1]; + if i == right_cycle { + break; + } + } + + let tmp = self.mapping[permutation][left_column][left_row]; + self.mapping[permutation][left_column][left_row] = + self.mapping[permutation][right_column][right_row]; + self.mapping[permutation][right_column][right_row] = tmp; + + Ok(()) + } + + pub(crate) fn build_keys( + self, + params: &Params, + cs: &ConstraintSystem, + domain: &EvaluationDomain, + ) -> (Vec>, Vec>) { + // Get the largest permutation argument length in terms of the number of + // advice columns involved. + let largest_permutation_length = cs + .permutations + .iter() + .map(|p| p.columns.len()) + .max() + .unwrap_or_default(); + + // Compute [omega^0, omega^1, ..., omega^{params.n - 1}] + let mut omega_powers = Vec::with_capacity(params.n as usize); + { + let mut cur = C::Scalar::one(); + for _ in 0..params.n { + omega_powers.push(cur); + cur *= &domain.get_omega(); + } + } + + // Compute [omega_powers * \delta^0, omega_powers * \delta^1, ..., omega_powers * \delta^m] + let mut deltaomega = Vec::with_capacity(largest_permutation_length); + { + let mut cur = C::Scalar::one(); + for _ in 0..largest_permutation_length { + let mut omega_powers = omega_powers.clone(); + for o in &mut omega_powers { + *o *= &cur; + } + + deltaomega.push(omega_powers); + + cur *= &C::Scalar::DELTA; + } + } + + // Compute permutation polynomials, convert to coset form and + // pre-compute commitments for the SRS. + let mut pks = vec![]; + let mut vks = vec![]; + for (p, mapping) in cs.permutations.iter().zip(self.mapping.iter()) { + let mut commitments = vec![]; + let mut permutations = vec![]; + let mut polys = vec![]; + let mut cosets = vec![]; + for i in 0..p.columns.len() { + // Computes the permutation polynomial based on the permutation + // description in the assembly. + let mut permutation_poly = domain.empty_lagrange(); + for (j, p) in permutation_poly.iter_mut().enumerate() { + let (permuted_i, permuted_j) = mapping[i][j]; + *p = deltaomega[permuted_i][permuted_j]; + } + + // Compute commitment to permutation polynomial + commitments.push( + params + .commit_lagrange(&permutation_poly, Blind::default()) + .to_affine(), + ); + // Store permutation polynomial and precompute its coset evaluation + permutations.push(permutation_poly.clone()); + let poly = domain.lagrange_to_coeff(permutation_poly); + polys.push(poly.clone()); + cosets.push(domain.coeff_to_extended(poly, Rotation::default())); + } + vks.push(VerifyingKey { commitments }); + pks.push(ProvingKey { + permutations, + polys, + cosets, + }); + } + + (pks, vks) + } +} diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs index 6b4f7524..bd74a49e 100644 --- a/src/plonk/permutation/prover.rs +++ b/src/plonk/permutation/prover.rs @@ -67,11 +67,12 @@ impl Proof { // // where p_j(X) is the jth advice column in this permutation, // and i is the ith row of the column. - .map(|(columns, permuted_values)| { + .map(|(p, pkey)| { 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 columns.iter().zip(permuted_values.iter()) + for (&column, permuted_column_values) in + p.columns.iter().zip(pkey.permutations.iter()) { parallelize(&mut modified_advice, |modified_advice, start| { for ((modified_advice, advice_value), permuted_advice_value) in @@ -97,7 +98,7 @@ impl Proof { .flat_map(|v| v.iter_mut()) .batch_invert(); - for (columns, mut modified_advice) in pk + for (p, mut modified_advice) in pk .vk .cs .permutations @@ -107,7 +108,7 @@ impl Proof { // 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 columns.iter() { + 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]); @@ -195,8 +196,8 @@ impl Committed { > { let domain = &pk.vk.domain; let permutation_product_cosets_owned = self.permutation_product_cosets.clone(); - let permutation_product_cosets = self.permutation_product_cosets; - let permutation_product_cosets_inv = self.permutation_product_cosets_inv; + 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 @@ -206,46 +207,56 @@ impl Committed { .map(move |coset| Polynomial::one_minus(coset) * &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.iter().enumerate().map( - move |(permutation_index, columns)| { - let mut left = permutation_product_cosets[permutation_index].clone(); - for (advice, permutation) in columns - .iter() - .map(|&column| &advice_cosets[pk.vk.cs.get_advice_query_index(column, 0)]) - .zip(pk.permutation_cosets[permutation_index].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); - } - }); - } + .chain( + pk.vk + .cs + .permutations + .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()) + { + 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); + } + }); + } - let mut right = permutation_product_cosets_inv[permutation_index].clone(); - let mut current_delta = *beta * &C::Scalar::ZETA; - let step = domain.get_extended_omega(); - for advice in 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 = 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; + } - left - &right - }, - )); + left - &right + }), + ); Ok(( Constructed { @@ -258,6 +269,31 @@ impl Committed { } } +impl super::ProvingKey { + fn evaluate(&self, x: ChallengeX) -> Vec { + self.polys + .iter() + .map(|poly| eval_polynomial(poly, *x)) + .collect() + } + + fn open<'a>( + &'a self, + evals: &'a [C::Scalar], + x: ChallengeX, + ) -> impl Iterator> + Clone { + self.polys + .iter() + .zip(evals.iter()) + .map(move |(poly, eval)| ProverQuery { + point: *x, + poly, + blind: Blind::default(), + eval: *eval, + }) + } +} + impl Constructed { pub(crate) fn evaluate, HScalar: Hasher>( self, @@ -279,11 +315,7 @@ impl Constructed { .map(|poly| eval_polynomial(poly, domain.rotate_omega(*x, Rotation(-1)))) .collect(); - let permutation_evals: Vec> = pk - .permutation_polys - .iter() - .map(|polys| polys.iter().map(|poly| eval_polynomial(poly, *x)).collect()) - .collect(); + let permutation_evals: Vec<_> = pk.permutations.iter().map(|p| p.evaluate(x)).collect(); // Hash each advice evaluation for eval in permutation_product_evals @@ -328,16 +360,10 @@ impl Evaluated { ) // Open permutation polynomial commitments at x .chain( - pk.permutation_polys + pk.permutations .iter() .zip(self.permutation_evals.iter()) - .flat_map(|(polys, evals)| polys.iter().zip(evals.iter())) - .map(move |(poly, eval)| ProverQuery { - point: *x, - poly, - blind: Blind::default(), - eval: *eval, - }), + .flat_map(move |(permutation, evals)| permutation.open(evals, x)), ) // Open permutation product commitments at \omega^{-1} x .chain( diff --git a/src/plonk/permutation/verifier.rs b/src/plonk/permutation/verifier.rs index a5af78e4..63fb4398 100644 --- a/src/plonk/permutation/verifier.rs +++ b/src/plonk/permutation/verifier.rs @@ -15,10 +15,8 @@ impl Proof { return Err(Error::IncompatibleParams); } - for (permutation_evals, permutation) in - self.permutation_evals.iter().zip(vk.cs.permutations.iter()) - { - if permutation_evals.len() != permutation.len() { + 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); } } @@ -76,9 +74,10 @@ impl Proof { .zip(self.permutation_product_evals.iter()) .zip(self.permutation_product_inv_evals.iter()) .map( - move |(((columns, permutation_evals), product_eval), product_inv_eval)| { + move |(((p, permutation_evals), product_eval), product_inv_eval)| { let mut left = *product_eval; - for (advice_eval, permutation_eval) in columns + for (advice_eval, permutation_eval) in p + .columns .iter() .map(|&column| { advice_evals[vk.cs.get_advice_query_index(column, 0)] @@ -90,7 +89,7 @@ impl Proof { let mut right = *product_inv_eval; let mut current_delta = *beta * &x; - for advice_eval in columns.iter().map(|&column| { + 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); @@ -132,12 +131,12 @@ impl Proof { ) // Open permutation commitments for each permutation argument at x .chain( - (0..vk.permutation_commitments.len()) + (0..vk.permutations.len()) .map(move |outer_idx| { - let inner_len = vk.permutation_commitments[outer_idx].len(); + let inner_len = vk.permutations[outer_idx].commitments.len(); (0..inner_len).map(move |inner_idx| VerifierQuery { point: *x, - commitment: &vk.permutation_commitments[outer_idx][inner_idx], + commitment: &vk.permutations[outer_idx].commitments[inner_idx], eval: self.permutation_evals[outer_idx][inner_idx], }) }) From 7422efca727fa6cab5dac3d528f87ed50fd6161e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 1 Dec 2020 14:16:31 +0000 Subject: [PATCH 05/12] s/permutation::Proof::commit/permutation::Argument::commit Once we refactor the permutation argument implementation to be integrated as Vec, we can change this again to just map from the Vec inside ConstraintSystem. --- src/plonk/permutation/prover.rs | 6 +++--- src/plonk/prover.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs index bd74a49e..b0ce871b 100644 --- a/src/plonk/permutation/prover.rs +++ b/src/plonk/permutation/prover.rs @@ -1,7 +1,7 @@ use ff::Field; use std::iter; -use super::Proof; +use super::{Argument, Proof}; use crate::{ arithmetic::{eval_polynomial, parallelize, BatchInvert, Curve, CurveAffine, FieldExt}, plonk::{ChallengeBeta, ChallengeGamma, ChallengeX, Error, ProvingKey}, @@ -35,8 +35,8 @@ pub(crate) struct Evaluated { permutation_evals: Vec>, } -impl Proof { - pub(crate) fn commit, HScalar: Hasher>( +impl Argument { + pub(crate) fn commit, HScalar: Hasher>( params: &Params, pk: &ProvingKey, advice: &[Polynomial], diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index cb46402d..bce29c17 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -176,7 +176,7 @@ impl Proof { // Commit to permutations, if any. let permutations = if !pk.vk.cs.permutations.is_empty() { - Some(permutation::Proof::commit( + Some(permutation::Argument::commit( params, pk, &witness.advice, From a63e6e25d858af9d89c80af1b309f6a4eddf0108 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 1 Dec 2020 21:14:14 +0000 Subject: [PATCH 06/12] Restrict visibility of PLONK challenges to plonk module --- src/plonk.rs | 16 ++++++++-------- src/plonk/permutation/prover.rs | 12 ++++++++---- src/plonk/permutation/verifier.rs | 4 ++-- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index 4d67c7d7..a644f7eb 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -93,20 +93,20 @@ impl VerifyingKey { } #[derive(Clone, Copy, Debug)] -pub(crate) struct Beta; -pub(crate) type ChallengeBeta = ChallengeScalar; +struct Beta; +type ChallengeBeta = ChallengeScalar; #[derive(Clone, Copy, Debug)] -pub(crate) struct Gamma; -pub(crate) type ChallengeGamma = ChallengeScalar; +struct Gamma; +type ChallengeGamma = ChallengeScalar; #[derive(Clone, Copy, Debug)] -pub(crate) struct Y; -pub(crate) type ChallengeY = ChallengeScalar; +struct Y; +type ChallengeY = ChallengeScalar; #[derive(Clone, Copy, Debug)] -pub(crate) struct X; -pub(crate) type ChallengeX = ChallengeScalar; +struct X; +type ChallengeX = ChallengeScalar; #[test] fn test_proving() { diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs index b0ce871b..d975ccc9 100644 --- a/src/plonk/permutation/prover.rs +++ b/src/plonk/permutation/prover.rs @@ -36,7 +36,11 @@ pub(crate) struct Evaluated { } impl Argument { - pub(crate) fn commit, HScalar: Hasher>( + pub(in crate::plonk) fn commit< + C: CurveAffine, + HBase: Hasher, + HScalar: Hasher, + >( params: &Params, pk: &ProvingKey, advice: &[Polynomial], @@ -181,7 +185,7 @@ impl Argument { } impl Committed { - pub(crate) fn construct<'a>( + pub(in crate::plonk) fn construct<'a>( self, pk: &'a ProvingKey, advice_cosets: &'a [Polynomial], @@ -295,7 +299,7 @@ impl super::ProvingKey { } impl Constructed { - pub(crate) fn evaluate, HScalar: Hasher>( + pub(in crate::plonk) fn evaluate, HScalar: Hasher>( self, pk: &ProvingKey, x: ChallengeX, @@ -336,7 +340,7 @@ impl Constructed { } impl Evaluated { - pub fn open<'a>( + pub(in crate::plonk) fn open<'a>( &'a self, pk: &'a ProvingKey, x: ChallengeX, diff --git a/src/plonk/permutation/verifier.rs b/src/plonk/permutation/verifier.rs index 63fb4398..cea9d69a 100644 --- a/src/plonk/permutation/verifier.rs +++ b/src/plonk/permutation/verifier.rs @@ -48,7 +48,7 @@ impl Proof { Ok(()) } - pub(crate) fn expressions<'a>( + pub(in crate::plonk) fn expressions<'a>( &'a self, vk: &'a VerifyingKey, advice_evals: &'a [C::Scalar], @@ -109,7 +109,7 @@ impl Proof { .chain(self.permutation_evals.iter().flat_map(|evals| evals.iter())) } - pub(crate) fn queries<'a>( + pub(in crate::plonk) fn queries<'a>( &'a self, vk: &'a VerifyingKey, x: ChallengeX, From eb7ce442f9feab8a01b053eeeb25c19692a447b9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 1 Dec 2020 21:23:05 +0000 Subject: [PATCH 07/12] Rename ChallengeX6 to ChallengeZ --- src/poly/commitment.rs | 12 ++++++------ src/poly/commitment/prover.rs | 6 +++--- src/poly/commitment/verifier.rs | 6 +++--- src/poly/multiopen/prover.rs | 8 ++++---- src/poly/multiopen/verifier.rs | 16 ++++++++-------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index c0d4520a..787bd8bf 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -87,8 +87,8 @@ impl Deref for ChallengeScalar { } #[derive(Clone, Copy, Debug)] -pub(crate) struct X6 {} -pub(crate) type ChallengeX6 = ChallengeScalar; +pub(crate) struct Z {} +pub(crate) type ChallengeZ = ChallengeScalar; /// These are the public parameters for the polynomial commitment scheme. #[derive(Debug)] @@ -346,16 +346,16 @@ fn test_opening_proof() { let mut transcript = Transcript::<_, DummyHash<_>, DummyHash<_>>::new(); transcript.absorb_point(&p).unwrap(); - let x = ChallengeX6::get(&mut transcript); + let z = ChallengeZ::get(&mut transcript); // Evaluate the polynomial - let v = eval_polynomial(&px, *x); + let v = eval_polynomial(&px, *z); transcript.absorb_base(Fp::from_bytes(&v.to_bytes()).unwrap()); // unlikely to fail since p ~ q loop { let mut transcript_dup = transcript.clone(); - let opening_proof = Proof::create(¶ms, &mut transcript, &px, blind, x); + let opening_proof = Proof::create(¶ms, &mut transcript, &px, blind, z); if let Ok(opening_proof) = opening_proof { // Verify the opening proof let mut commitment_msm = params.empty_msm(); @@ -365,7 +365,7 @@ fn test_opening_proof() { ¶ms, params.empty_msm(), &mut transcript_dup, - x, + z, commitment_msm, v, ) diff --git a/src/poly/commitment/prover.rs b/src/poly/commitment/prover.rs index 0f029782..22d8956e 100644 --- a/src/poly/commitment/prover.rs +++ b/src/poly/commitment/prover.rs @@ -1,7 +1,7 @@ use ff::Field; use super::super::{Coeff, Error, Polynomial}; -use super::{Blind, Challenge, ChallengeScalar, ChallengeX6, Params, Proof}; +use super::{Blind, Challenge, ChallengeScalar, ChallengeZ, Params, Proof}; use crate::arithmetic::{ best_multiexp, compute_inner_product, parallelize, small_multiexp, Curve, CurveAffine, FieldExt, }; @@ -26,7 +26,7 @@ impl Proof { transcript: &mut Transcript, px: &Polynomial, blind: Blind, - x: ChallengeX6, + z: ChallengeZ, ) -> Result where HBase: Hasher, @@ -61,7 +61,7 @@ impl Proof { let mut cur = C::Scalar::one(); for _ in 0..(1 << params.k) { b.push(cur); - cur *= &x; + cur *= &z; } } diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index b9001fab..3c349286 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -1,7 +1,7 @@ use ff::Field; use super::super::Error; -use super::{Challenge, ChallengeScalar, ChallengeX6, Params, Proof, MSM}; +use super::{Challenge, ChallengeScalar, ChallengeZ, Params, Proof, MSM}; use crate::transcript::{Hasher, Transcript}; use crate::arithmetic::{best_multiexp, Curve, CurveAffine, FieldExt}; @@ -71,7 +71,7 @@ impl Proof { params: &'a Params, mut msm: MSM<'a, C>, transcript: &mut Transcript, - x: ChallengeX6, + z: ChallengeZ, mut commitment_msm: MSM<'a, C>, v: C::Scalar, ) -> Result, Error> @@ -164,7 +164,7 @@ impl Proof { // The computation of [z1] (G + H) happens in either Guard::use_challenges() // or Guard::use_g(). - let b = compute_b(*x, &challenges, &challenges_inv); + let b = compute_b(*z, &challenges, &challenges_inv); let neg_z1 = -self.z1; diff --git a/src/poly/multiopen/prover.rs b/src/poly/multiopen/prover.rs index e14c6db9..890ea213 100644 --- a/src/poly/multiopen/prover.rs +++ b/src/poly/multiopen/prover.rs @@ -1,5 +1,5 @@ use super::super::{ - commitment::{self, Blind, ChallengeScalar, ChallengeX6, Params}, + commitment::{self, Blind, ChallengeScalar, ChallengeZ, Params}, Coeff, Error, Polynomial, }; use super::{construct_intermediate_sets, Proof, ProverQuery, Query}; @@ -113,11 +113,11 @@ impl Proof { .absorb_point(&f_commitment) .map_err(|_| Error::SamplingError)?; - let x_6 = ChallengeX6::get(&mut transcript); + let z = ChallengeZ::get(&mut transcript); let q_evals: Vec = q_polys .iter() - .map(|poly| eval_polynomial(poly.as_ref().unwrap(), *x_6)) + .map(|poly| eval_polynomial(poly.as_ref().unwrap(), *z)) .collect(); for eval in q_evals.iter() { @@ -137,7 +137,7 @@ impl Proof { ); if let Ok(opening) = - commitment::Proof::create(¶ms, &mut transcript, &f_poly, f_blind_try, x_6) + commitment::Proof::create(¶ms, &mut transcript, &f_poly, f_blind_try, z) { break (opening, q_evals); } else { diff --git a/src/poly/multiopen/verifier.rs b/src/poly/multiopen/verifier.rs index c265d0f6..858216f6 100644 --- a/src/poly/multiopen/verifier.rs +++ b/src/poly/multiopen/verifier.rs @@ -1,7 +1,7 @@ use ff::Field; use super::super::{ - commitment::{ChallengeScalar, ChallengeX6, Guard, Params, MSM}, + commitment::{ChallengeScalar, ChallengeZ, Guard, Params, MSM}, Error, }; use super::{construct_intermediate_sets, Proof, Query, VerifierQuery}; @@ -77,15 +77,15 @@ impl Proof { .absorb_point(&self.f_commitment) .map_err(|_| Error::SamplingError)?; - // Sample a challenge x_6 for checking that f(X) was committed to + // Sample a challenge z for checking that f(X) was committed to // correctly. - let x_6 = ChallengeX6::get(transcript); + let z = ChallengeZ::get(transcript); for eval in self.q_evals.iter() { transcript.absorb_scalar(*eval); } - // We can compute the expected msm_eval at x_6 using the q_evals provided + // We can compute the expected msm_eval at z using the q_evals provided // by the prover and from x_5 let msm_eval = point_sets .iter() @@ -95,16 +95,16 @@ impl Proof { C::Scalar::zero(), |msm_eval, ((points, evals), proof_eval)| { let r_poly = lagrange_interpolate(points, evals); - let r_eval = eval_polynomial(&r_poly, *x_6); + let r_eval = eval_polynomial(&r_poly, *z); let eval = points.iter().fold(*proof_eval - &r_eval, |eval, point| { - eval * &(*x_6 - point).invert().unwrap() + eval * &(*z - point).invert().unwrap() }); msm_eval * &x_5 + &eval }, ); // Sample a challenge x_7 that we will use to collapse the openings of - // the various remaining polynomials at x_6 together. + // the various remaining polynomials at z together. let x_7 = ChallengeScalar::<_, ()>::get(transcript); // Compute the final commitment that has to be opened @@ -121,7 +121,7 @@ impl Proof { // Verify the opening proof self.opening - .verify(params, msm, transcript, x_6, commitment_msm, msm_eval) + .verify(params, msm, transcript, z, commitment_msm, msm_eval) } } From f0723dbbccef140fb52f004992509b4227dece86 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 1 Dec 2020 21:42:32 +0000 Subject: [PATCH 08/12] multiopen: Rename [x_4, x_5] challenges to [x_1, x_2] Also added types for these challenges, even though it's not technically necessary yet because we don't pass these around anywhere. --- src/poly/multiopen.rs | 10 ++++++++++ src/poly/multiopen/prover.rs | 16 ++++++++-------- src/poly/multiopen/verifier.rs | 20 ++++++++++---------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/poly/multiopen.rs b/src/poly/multiopen.rs index da3f58d1..82859a7f 100644 --- a/src/poly/multiopen.rs +++ b/src/poly/multiopen.rs @@ -12,6 +12,16 @@ use crate::arithmetic::{CurveAffine, FieldExt}; mod prover; mod verifier; +#[derive(Clone, Copy, Debug)] +struct X1 {} +/// Challenge for compressing openings at the same point sets together. +type ChallengeX1 = commitment::ChallengeScalar; + +#[derive(Clone, Copy, Debug)] +struct X2 {} +/// Challenge for keeping the multi-point quotient polynomial terms linearly independent. +type ChallengeX2 = commitment::ChallengeScalar; + /// This is a multi-point opening proof used in the polynomial commitment scheme opening. #[derive(Debug, Clone)] pub struct Proof { diff --git a/src/poly/multiopen/prover.rs b/src/poly/multiopen/prover.rs index 890ea213..63cfa1c9 100644 --- a/src/poly/multiopen/prover.rs +++ b/src/poly/multiopen/prover.rs @@ -2,7 +2,7 @@ use super::super::{ commitment::{self, Blind, ChallengeScalar, ChallengeZ, Params}, Coeff, Error, Polynomial, }; -use super::{construct_intermediate_sets, Proof, ProverQuery, Query}; +use super::{construct_intermediate_sets, ChallengeX1, ChallengeX2, Proof, ProverQuery, Query}; use crate::arithmetic::{ eval_polynomial, kate_division, lagrange_interpolate, Curve, CurveAffine, FieldExt, @@ -30,13 +30,13 @@ impl Proof { where I: IntoIterator> + Clone, { - let x_4 = ChallengeScalar::<_, ()>::get(transcript); - let x_5 = ChallengeScalar::<_, ()>::get(transcript); + let x_1 = ChallengeX1::get(transcript); + let x_2 = ChallengeX2::get(transcript); let (poly_map, point_sets) = construct_intermediate_sets(queries); // Collapse openings at same point sets together into single openings using - // x_4 challenge. + // x_1 challenge. let mut q_polys: Vec>> = vec![None; point_sets.len()]; let mut q_blinds = vec![Blind(C::Scalar::zero()); point_sets.len()]; @@ -53,16 +53,16 @@ impl Proof { blind: Blind, evals: Vec| { if let Some(poly) = &q_polys[set_idx] { - q_polys[set_idx] = Some(poly.clone() * *x_4 + new_poly); + q_polys[set_idx] = Some(poly.clone() * *x_1 + new_poly); } else { q_polys[set_idx] = Some(new_poly.clone()); } - q_blinds[set_idx] *= *x_4; + q_blinds[set_idx] *= *x_1; q_blinds[set_idx] += blind; // Each polynomial is evaluated at a set of points. For each set, // we collapse each polynomial's evals pointwise. for (eval, set_eval) in evals.iter().zip(q_eval_sets[set_idx].iter_mut()) { - *set_eval *= &x_4; + *set_eval *= &x_1; *set_eval += eval; } }; @@ -99,7 +99,7 @@ impl Proof { if f_poly.is_none() { Some(poly) } else { - f_poly.map(|f_poly| f_poly * *x_5 + &poly) + f_poly.map(|f_poly| f_poly * *x_2 + &poly) } }) .unwrap(); diff --git a/src/poly/multiopen/verifier.rs b/src/poly/multiopen/verifier.rs index 858216f6..53699b61 100644 --- a/src/poly/multiopen/verifier.rs +++ b/src/poly/multiopen/verifier.rs @@ -4,7 +4,7 @@ use super::super::{ commitment::{ChallengeScalar, ChallengeZ, Guard, Params, MSM}, Error, }; -use super::{construct_intermediate_sets, Proof, Query, VerifierQuery}; +use super::{construct_intermediate_sets, ChallengeX1, ChallengeX2, Proof, Query, VerifierQuery}; use crate::arithmetic::{eval_polynomial, lagrange_interpolate, CurveAffine, FieldExt}; use crate::transcript::{Hasher, Transcript}; @@ -32,17 +32,17 @@ impl Proof { // with it to make it true, with high probability. msm.scale(C::Scalar::rand()); - // Sample x_4 for compressing openings at the same point sets together - let x_4 = ChallengeScalar::<_, ()>::get(transcript); + // Sample x_1 for compressing openings at the same point sets together + let x_1 = ChallengeX1::get(transcript); - // Sample a challenge x_5 for keeping the multi-point quotient + // Sample a challenge x_2 for keeping the multi-point quotient // polynomial terms linearly independent. - let x_5 = ChallengeScalar::<_, ()>::get(transcript); + let x_2 = ChallengeX2::get(transcript); let (commitment_map, point_sets) = construct_intermediate_sets(queries); // Compress the commitments and expected evaluations at x together. - // using the challenge x_4 + // using the challenge x_1 let mut q_commitments: Vec<_> = vec![params.empty_msm(); point_sets.len()]; // A vec of vecs of evals. The outer vec corresponds to the point set, @@ -53,10 +53,10 @@ impl Proof { } { let mut accumulate = |set_idx: usize, new_commitment, evals: Vec| { - q_commitments[set_idx].scale(*x_4); + q_commitments[set_idx].scale(*x_1); q_commitments[set_idx].append_term(C::Scalar::one(), new_commitment); for (eval, set_eval) in evals.iter().zip(q_eval_sets[set_idx].iter_mut()) { - *set_eval *= &x_4; + *set_eval *= &x_1; *set_eval += eval; } }; @@ -86,7 +86,7 @@ impl Proof { } // We can compute the expected msm_eval at z using the q_evals provided - // by the prover and from x_5 + // by the prover and from x_2 let msm_eval = point_sets .iter() .zip(q_eval_sets.iter()) @@ -99,7 +99,7 @@ impl Proof { let eval = points.iter().fold(*proof_eval - &r_eval, |eval, point| { eval * &(*z - point).invert().unwrap() }); - msm_eval * &x_5 + &eval + msm_eval * &x_2 + &eval }, ); From dd3d1dd68b61b989e5b5f3a11bc0cc772da3bace Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 1 Dec 2020 21:49:07 +0000 Subject: [PATCH 09/12] Small type annotation cleanups --- src/plonk/permutation/prover.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs index d975ccc9..322d59b6 100644 --- a/src/plonk/permutation/prover.rs +++ b/src/plonk/permutation/prover.rs @@ -307,13 +307,13 @@ impl Constructed { ) -> Evaluated { let domain = &pk.vk.domain; - let permutation_product_evals: Vec = self + let permutation_product_evals: Vec<_> = self .permutation_product_polys .iter() .map(|poly| eval_polynomial(poly, *x)) .collect(); - let permutation_product_inv_evals: Vec = self + let permutation_product_inv_evals: Vec<_> = self .permutation_product_polys .iter() .map(|poly| eval_polynomial(poly, domain.rotate_omega(*x, Rotation(-1)))) From 3d6afd7b8e381bc06dc93ec4af4f2db36b6ca856 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 1 Dec 2020 22:09:50 +0000 Subject: [PATCH 10/12] permutation: Clean up opening chains --- src/plonk/permutation/prover.rs | 36 ++++++++++++++----------------- src/plonk/permutation/verifier.rs | 31 ++++++++++++-------------- 2 files changed, 30 insertions(+), 37 deletions(-) diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs index 322d59b6..47d6bdf7 100644 --- a/src/plonk/permutation/prover.rs +++ b/src/plonk/permutation/prover.rs @@ -348,18 +348,28 @@ impl Evaluated { let x_inv = pk.vk.domain.rotate_omega(*x, Rotation(-1)); iter::empty() - // Open permutation product commitments at x + // 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()) - .map(move |((poly, blind), eval)| ProverQuery { - point: *x, - poly, - blind: *blind, - eval: *eval, + .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, + })) }), ) // Open permutation polynomial commitments at x @@ -369,20 +379,6 @@ impl Evaluated { .zip(self.permutation_evals.iter()) .flat_map(move |(permutation, evals)| permutation.open(evals, x)), ) - // Open permutation product commitments at \omega^{-1} x - .chain( - self.constructed - .permutation_product_polys - .iter() - .zip(self.constructed.permutation_product_blinds.iter()) - .zip(self.permutation_product_inv_evals.iter()) - .map(move |((poly, blind), eval)| ProverQuery { - point: x_inv, - poly, - blind: *blind, - eval: *eval, - }), - ) } pub(crate) fn build(self) -> Proof { diff --git a/src/plonk/permutation/verifier.rs b/src/plonk/permutation/verifier.rs index cea9d69a..ce09aee0 100644 --- a/src/plonk/permutation/verifier.rs +++ b/src/plonk/permutation/verifier.rs @@ -117,16 +117,25 @@ impl Proof { let x_inv = vk.domain.rotate_omega(*x, Rotation(-1)); iter::empty() - // Open permutation product commitments at x + // Open permutation product commitments at x and \omega^{-1} x .chain( self.permutation_product_commitments .iter() .enumerate() .zip(self.permutation_product_evals.iter()) - .map(move |((idx, _), &eval)| VerifierQuery { - point: *x, - commitment: &self.permutation_product_commitments[idx], - eval, + .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, + })) }), ) // Open permutation commitments for each permutation argument at x @@ -142,17 +151,5 @@ impl Proof { }) .flatten(), ) - // Open permutation product commitments at \omega^{-1} x - .chain( - self.permutation_product_commitments - .iter() - .enumerate() - .zip(self.permutation_product_inv_evals.iter()) - .map(move |((idx, _), &eval)| VerifierQuery { - point: x_inv, - commitment: &self.permutation_product_commitments[idx], - eval, - }), - ) } } From 2e6ca274a456e6f3f2f77c7b7dddddd248b73677 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 1 Dec 2020 22:34:18 +0000 Subject: [PATCH 11/12] Fix challenge types in poly::multiopen and poly::commitment The argument to the poly::commitment prover and verifier was mistakenly represented as a challenge, when in fact the commitments may be opened at any scalar (which just happens to be a challenge within poly::multiopen). The poly::commitment APIs are now public again. --- src/poly/commitment.rs | 12 ++++-------- src/poly/commitment/prover.rs | 8 ++++---- src/poly/commitment/verifier.rs | 8 ++++---- src/poly/multiopen.rs | 11 +++++++++++ src/poly/multiopen/prover.rs | 19 +++++++++++-------- src/poly/multiopen/verifier.rs | 29 ++++++++++++++++------------- 6 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 787bd8bf..3de7d216 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -86,10 +86,6 @@ impl Deref for ChallengeScalar { } } -#[derive(Clone, Copy, Debug)] -pub(crate) struct Z {} -pub(crate) type ChallengeZ = ChallengeScalar; - /// These are the public parameters for the polynomial commitment scheme. #[derive(Debug)] pub struct Params { @@ -346,16 +342,16 @@ fn test_opening_proof() { let mut transcript = Transcript::<_, DummyHash<_>, DummyHash<_>>::new(); transcript.absorb_point(&p).unwrap(); - let z = ChallengeZ::get(&mut transcript); + let x = ChallengeScalar::<_, ()>::get(&mut transcript); // Evaluate the polynomial - let v = eval_polynomial(&px, *z); + let v = eval_polynomial(&px, *x); transcript.absorb_base(Fp::from_bytes(&v.to_bytes()).unwrap()); // unlikely to fail since p ~ q loop { let mut transcript_dup = transcript.clone(); - let opening_proof = Proof::create(¶ms, &mut transcript, &px, blind, z); + let opening_proof = Proof::create(¶ms, &mut transcript, &px, blind, *x); if let Ok(opening_proof) = opening_proof { // Verify the opening proof let mut commitment_msm = params.empty_msm(); @@ -365,7 +361,7 @@ fn test_opening_proof() { ¶ms, params.empty_msm(), &mut transcript_dup, - z, + *x, commitment_msm, v, ) diff --git a/src/poly/commitment/prover.rs b/src/poly/commitment/prover.rs index 22d8956e..1245daf7 100644 --- a/src/poly/commitment/prover.rs +++ b/src/poly/commitment/prover.rs @@ -1,7 +1,7 @@ use ff::Field; use super::super::{Coeff, Error, Polynomial}; -use super::{Blind, Challenge, ChallengeScalar, ChallengeZ, Params, Proof}; +use super::{Blind, Challenge, ChallengeScalar, Params, Proof}; use crate::arithmetic::{ best_multiexp, compute_inner_product, parallelize, small_multiexp, Curve, CurveAffine, FieldExt, }; @@ -21,12 +21,12 @@ impl Proof { /// opening v, and the point x. It's probably also nice for the transcript /// to have seen the elliptic curve description and the SRS, if you want to /// be rigorous. - pub(crate) fn create( + pub fn create( params: &Params, transcript: &mut Transcript, px: &Polynomial, blind: Blind, - z: ChallengeZ, + x: C::Scalar, ) -> Result where HBase: Hasher, @@ -61,7 +61,7 @@ impl Proof { let mut cur = C::Scalar::one(); for _ in 0..(1 << params.k) { b.push(cur); - cur *= &z; + cur *= &x; } } diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index 3c349286..eee7d05a 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -1,7 +1,7 @@ use ff::Field; use super::super::Error; -use super::{Challenge, ChallengeScalar, ChallengeZ, Params, Proof, MSM}; +use super::{Challenge, ChallengeScalar, Params, Proof, MSM}; use crate::transcript::{Hasher, Transcript}; use crate::arithmetic::{best_multiexp, Curve, CurveAffine, FieldExt}; @@ -66,12 +66,12 @@ impl Proof { /// Checks to see if an [`Proof`] is valid given the current `transcript`, /// and a point `x` that the polynomial commitment `p` opens purportedly to /// the value `v`. - pub(crate) fn verify<'a, HBase, HScalar>( + pub fn verify<'a, HBase, HScalar>( &self, params: &'a Params, mut msm: MSM<'a, C>, transcript: &mut Transcript, - z: ChallengeZ, + x: C::Scalar, mut commitment_msm: MSM<'a, C>, v: C::Scalar, ) -> Result, Error> @@ -164,7 +164,7 @@ impl Proof { // The computation of [z1] (G + H) happens in either Guard::use_challenges() // or Guard::use_g(). - let b = compute_b(*z, &challenges, &challenges_inv); + let b = compute_b(x, &challenges, &challenges_inv); let neg_z1 = -self.z1; diff --git a/src/poly/multiopen.rs b/src/poly/multiopen.rs index 82859a7f..abaa7360 100644 --- a/src/poly/multiopen.rs +++ b/src/poly/multiopen.rs @@ -22,6 +22,17 @@ struct X2 {} /// Challenge for keeping the multi-point quotient polynomial terms linearly independent. type ChallengeX2 = commitment::ChallengeScalar; +#[derive(Clone, Copy, Debug)] +struct X3 {} +/// Challenge point at which the commitments are opened. +type ChallengeX3 = commitment::ChallengeScalar; + +#[derive(Clone, Copy, Debug)] +struct X4 {} +/// Challenge for collapsing the openings of the various remaining polynomials at x_3 +/// together. +type ChallengeX4 = commitment::ChallengeScalar; + /// This is a multi-point opening proof used in the polynomial commitment scheme opening. #[derive(Debug, Clone)] pub struct Proof { diff --git a/src/poly/multiopen/prover.rs b/src/poly/multiopen/prover.rs index 63cfa1c9..6e6fa477 100644 --- a/src/poly/multiopen/prover.rs +++ b/src/poly/multiopen/prover.rs @@ -1,8 +1,11 @@ use super::super::{ - commitment::{self, Blind, ChallengeScalar, ChallengeZ, Params}, + commitment::{self, Blind, Params}, Coeff, Error, Polynomial, }; -use super::{construct_intermediate_sets, ChallengeX1, ChallengeX2, Proof, ProverQuery, Query}; +use super::{ + construct_intermediate_sets, ChallengeX1, ChallengeX2, ChallengeX3, ChallengeX4, Proof, + ProverQuery, Query, +}; use crate::arithmetic::{ eval_polynomial, kate_division, lagrange_interpolate, Curve, CurveAffine, FieldExt, @@ -113,31 +116,31 @@ impl Proof { .absorb_point(&f_commitment) .map_err(|_| Error::SamplingError)?; - let z = ChallengeZ::get(&mut transcript); + let x_3 = ChallengeX3::get(&mut transcript); let q_evals: Vec = q_polys .iter() - .map(|poly| eval_polynomial(poly.as_ref().unwrap(), *z)) + .map(|poly| eval_polynomial(poly.as_ref().unwrap(), *x_3)) .collect(); for eval in q_evals.iter() { transcript.absorb_scalar(*eval); } - let x_7 = ChallengeScalar::<_, ()>::get(&mut transcript); + let x_4 = ChallengeX4::get(&mut transcript); let (f_poly, f_blind_try) = q_polys.iter().zip(q_blinds.iter()).fold( (f_poly.clone(), f_blind), |(f_poly, f_blind), (poly, blind)| { ( - f_poly * *x_7 + poly.as_ref().unwrap(), - Blind((f_blind.0 * &x_7) + &blind.0), + f_poly * *x_4 + poly.as_ref().unwrap(), + Blind((f_blind.0 * &x_4) + &blind.0), ) }, ); if let Ok(opening) = - commitment::Proof::create(¶ms, &mut transcript, &f_poly, f_blind_try, z) + commitment::Proof::create(¶ms, &mut transcript, &f_poly, f_blind_try, *x_3) { break (opening, q_evals); } else { diff --git a/src/poly/multiopen/verifier.rs b/src/poly/multiopen/verifier.rs index 53699b61..e47e1d09 100644 --- a/src/poly/multiopen/verifier.rs +++ b/src/poly/multiopen/verifier.rs @@ -1,10 +1,13 @@ use ff::Field; use super::super::{ - commitment::{ChallengeScalar, ChallengeZ, Guard, Params, MSM}, + commitment::{Guard, Params, MSM}, Error, }; -use super::{construct_intermediate_sets, ChallengeX1, ChallengeX2, Proof, Query, VerifierQuery}; +use super::{ + construct_intermediate_sets, ChallengeX1, ChallengeX2, ChallengeX3, ChallengeX4, Proof, Query, + VerifierQuery, +}; use crate::arithmetic::{eval_polynomial, lagrange_interpolate, CurveAffine, FieldExt}; use crate::transcript::{Hasher, Transcript}; @@ -77,15 +80,15 @@ impl Proof { .absorb_point(&self.f_commitment) .map_err(|_| Error::SamplingError)?; - // Sample a challenge z for checking that f(X) was committed to + // Sample a challenge x_3 for checking that f(X) was committed to // correctly. - let z = ChallengeZ::get(transcript); + let x_3 = ChallengeX3::get(transcript); for eval in self.q_evals.iter() { transcript.absorb_scalar(*eval); } - // We can compute the expected msm_eval at z using the q_evals provided + // We can compute the expected msm_eval at x_3 using the q_evals provided // by the prover and from x_2 let msm_eval = point_sets .iter() @@ -95,17 +98,17 @@ impl Proof { C::Scalar::zero(), |msm_eval, ((points, evals), proof_eval)| { let r_poly = lagrange_interpolate(points, evals); - let r_eval = eval_polynomial(&r_poly, *z); + let r_eval = eval_polynomial(&r_poly, *x_3); let eval = points.iter().fold(*proof_eval - &r_eval, |eval, point| { - eval * &(*z - point).invert().unwrap() + eval * &(*x_3 - point).invert().unwrap() }); msm_eval * &x_2 + &eval }, ); - // Sample a challenge x_7 that we will use to collapse the openings of - // the various remaining polynomials at z together. - let x_7 = ChallengeScalar::<_, ()>::get(transcript); + // Sample a challenge x_4 that we will use to collapse the openings of + // the various remaining polynomials at x_3 together. + let x_4 = ChallengeX4::get(transcript); // Compute the final commitment that has to be opened let mut commitment_msm = params.empty_msm(); @@ -113,15 +116,15 @@ impl Proof { let (commitment_msm, msm_eval) = q_commitments.into_iter().zip(self.q_evals.iter()).fold( (commitment_msm, msm_eval), |(mut commitment_msm, msm_eval), (q_commitment, q_eval)| { - commitment_msm.scale(*x_7); + commitment_msm.scale(*x_4); commitment_msm.add_msm(&q_commitment); - (commitment_msm, msm_eval * &x_7 + q_eval) + (commitment_msm, msm_eval * &x_4 + q_eval) }, ); // Verify the opening proof self.opening - .verify(params, msm, transcript, z, commitment_msm, msm_eval) + .verify(params, msm, transcript, *x_3, commitment_msm, msm_eval) } } From 4d4c79be5819002e508a43d07304cdf27e49b317 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 1 Dec 2020 22:40:54 +0000 Subject: [PATCH 12/12] Move Challenge and ChallengeScalar into the transcript module --- src/plonk.rs | 6 +-- src/poly/commitment.rs | 75 ++------------------------------- src/poly/commitment/prover.rs | 4 +- src/poly/commitment/verifier.rs | 4 +- src/poly/multiopen.rs | 13 +++--- src/transcript.rs | 69 ++++++++++++++++++++++++++++++ 6 files changed, 86 insertions(+), 85 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index a644f7eb..ae31166f 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -6,10 +6,8 @@ //! [plonk]: https://eprint.iacr.org/2019/953 use crate::arithmetic::CurveAffine; -use crate::poly::{ - commitment::ChallengeScalar, multiopen, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, - Polynomial, -}; +use crate::poly::{multiopen, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, Polynomial}; +use crate::transcript::ChallengeScalar; mod circuit; mod keygen; diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 3de7d216..69ae7b02 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -5,11 +5,10 @@ use super::{Coeff, LagrangeCoeff, Polynomial}; use crate::arithmetic::{best_fft, best_multiexp, parallelize, Curve, CurveAffine, FieldExt}; -use crate::transcript::{Hasher, Transcript}; +use crate::transcript::Hasher; use ff::{Field, PrimeField}; -use std::marker::PhantomData; -use std::ops::{Add, AddAssign, Deref, Mul, MulAssign}; +use std::ops::{Add, AddAssign, Mul, MulAssign}; mod msm; mod prover; @@ -18,74 +17,6 @@ mod verifier; pub use msm::MSM; pub use verifier::{Accumulator, Guard}; -/// This is a 128-bit verifier challenge. -#[derive(Copy, Clone, Debug)] -pub struct Challenge(pub(crate) u128); - -impl Challenge { - /// Obtains a new challenge from the transcript. - pub fn get(transcript: &mut Transcript) -> Challenge - where - C: CurveAffine, - HBase: Hasher, - HScalar: Hasher, - { - Challenge(transcript.squeeze().get_lower_128()) - } -} - -/// The scalar representation of a verifier challenge. -/// -/// The `T` type can be used to scope the challenge to a specific context, or set to `()` -/// if no context is required. -#[derive(Copy, Clone, Debug)] -pub struct ChallengeScalar { - inner: F, - _marker: PhantomData, -} - -impl From for ChallengeScalar { - /// This algorithm applies the mapping of Algorithm 1 from the - /// [Halo](https://eprint.iacr.org/2019/1021) paper. - fn from(challenge: Challenge) -> Self { - let mut acc = (F::ZETA + F::one()).double(); - - for i in (0..64).rev() { - let should_negate = ((challenge.0 >> ((i << 1) + 1)) & 1) == 1; - let should_endo = ((challenge.0 >> (i << 1)) & 1) == 1; - - let q = if should_negate { -F::one() } else { F::one() }; - let q = if should_endo { q * F::ZETA } else { q }; - acc = acc + q + acc; - } - - ChallengeScalar { - inner: acc, - _marker: PhantomData::default(), - } - } -} - -impl ChallengeScalar { - /// Obtains a new challenge from the transcript. - pub fn get(transcript: &mut Transcript) -> Self - where - C: CurveAffine, - HBase: Hasher, - HScalar: Hasher, - { - Challenge::get(transcript).into() - } -} - -impl Deref for ChallengeScalar { - type Target = F; - - fn deref(&self) -> &F { - &self.inner - } -} - /// These are the public parameters for the polynomial commitment scheme. #[derive(Debug)] pub struct Params { @@ -324,7 +255,7 @@ fn test_opening_proof() { EvaluationDomain, }; use crate::arithmetic::{eval_polynomial, Curve, FieldExt}; - use crate::transcript::{DummyHash, Transcript}; + use crate::transcript::{ChallengeScalar, DummyHash, Transcript}; use crate::tweedle::{EpAffine, Fp, Fq}; let params = Params::::new::>(K); diff --git a/src/poly/commitment/prover.rs b/src/poly/commitment/prover.rs index 1245daf7..999367ba 100644 --- a/src/poly/commitment/prover.rs +++ b/src/poly/commitment/prover.rs @@ -1,11 +1,11 @@ use ff::Field; use super::super::{Coeff, Error, Polynomial}; -use super::{Blind, Challenge, ChallengeScalar, Params, Proof}; +use super::{Blind, Params, Proof}; use crate::arithmetic::{ best_multiexp, compute_inner_product, parallelize, small_multiexp, Curve, CurveAffine, FieldExt, }; -use crate::transcript::{Hasher, Transcript}; +use crate::transcript::{Challenge, ChallengeScalar, Hasher, Transcript}; impl Proof { /// Create a polynomial commitment opening proof for the polynomial defined diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index eee7d05a..3770db4a 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -1,8 +1,8 @@ use ff::Field; use super::super::Error; -use super::{Challenge, ChallengeScalar, Params, Proof, MSM}; -use crate::transcript::{Hasher, Transcript}; +use super::{Params, Proof, MSM}; +use crate::transcript::{Challenge, ChallengeScalar, Hasher, Transcript}; use crate::arithmetic::{best_multiexp, Curve, CurveAffine, FieldExt}; diff --git a/src/poly/multiopen.rs b/src/poly/multiopen.rs index abaa7360..bcae0e6d 100644 --- a/src/poly/multiopen.rs +++ b/src/poly/multiopen.rs @@ -7,7 +7,10 @@ use ff::Field; use std::collections::{BTreeMap, BTreeSet}; use super::*; -use crate::arithmetic::{CurveAffine, FieldExt}; +use crate::{ + arithmetic::{CurveAffine, FieldExt}, + transcript::ChallengeScalar, +}; mod prover; mod verifier; @@ -15,23 +18,23 @@ mod verifier; #[derive(Clone, Copy, Debug)] struct X1 {} /// Challenge for compressing openings at the same point sets together. -type ChallengeX1 = commitment::ChallengeScalar; +type ChallengeX1 = ChallengeScalar; #[derive(Clone, Copy, Debug)] struct X2 {} /// Challenge for keeping the multi-point quotient polynomial terms linearly independent. -type ChallengeX2 = commitment::ChallengeScalar; +type ChallengeX2 = ChallengeScalar; #[derive(Clone, Copy, Debug)] struct X3 {} /// Challenge point at which the commitments are opened. -type ChallengeX3 = commitment::ChallengeScalar; +type ChallengeX3 = ChallengeScalar; #[derive(Clone, Copy, Debug)] struct X4 {} /// Challenge for collapsing the openings of the various remaining polynomials at x_3 /// together. -type ChallengeX4 = commitment::ChallengeScalar; +type ChallengeX4 = ChallengeScalar; /// This is a multi-point opening proof used in the polynomial commitment scheme opening. #[derive(Debug, Clone)] diff --git a/src/transcript.rs b/src/transcript.rs index 3cf27c9c..1937c8c7 100644 --- a/src/transcript.rs +++ b/src/transcript.rs @@ -3,6 +3,7 @@ use ff::Field; use std::marker::PhantomData; +use std::ops::Deref; use crate::arithmetic::{CurveAffine, FieldExt}; @@ -122,3 +123,71 @@ impl, HScalar: Hasher> self.base_hasher.squeeze() } } + +/// This is a 128-bit verifier challenge. +#[derive(Copy, Clone, Debug)] +pub struct Challenge(pub(crate) u128); + +impl Challenge { + /// Obtains a new challenge from the transcript. + pub fn get(transcript: &mut Transcript) -> Challenge + where + C: CurveAffine, + HBase: Hasher, + HScalar: Hasher, + { + Challenge(transcript.squeeze().get_lower_128()) + } +} + +/// The scalar representation of a verifier challenge. +/// +/// The `T` type can be used to scope the challenge to a specific context, or set to `()` +/// if no context is required. +#[derive(Copy, Clone, Debug)] +pub struct ChallengeScalar { + inner: F, + _marker: PhantomData, +} + +impl From for ChallengeScalar { + /// This algorithm applies the mapping of Algorithm 1 from the + /// [Halo](https://eprint.iacr.org/2019/1021) paper. + fn from(challenge: Challenge) -> Self { + let mut acc = (F::ZETA + F::one()).double(); + + for i in (0..64).rev() { + let should_negate = ((challenge.0 >> ((i << 1) + 1)) & 1) == 1; + let should_endo = ((challenge.0 >> (i << 1)) & 1) == 1; + + let q = if should_negate { -F::one() } else { F::one() }; + let q = if should_endo { q * F::ZETA } else { q }; + acc = acc + q + acc; + } + + ChallengeScalar { + inner: acc, + _marker: PhantomData::default(), + } + } +} + +impl ChallengeScalar { + /// Obtains a new challenge from the transcript. + pub fn get(transcript: &mut Transcript) -> Self + where + C: CurveAffine, + HBase: Hasher, + HScalar: Hasher, + { + Challenge::get(transcript).into() + } +} + +impl Deref for ChallengeScalar { + type Target = F; + + fn deref(&self) -> &F { + &self.inner + } +}