diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 5ad4ebb..f5e7ed8 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 e938d71..2388e43 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 4970212..6b4f752 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 afd6801..a5af78e 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 cc8827a..cb46402 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 4450d28..4925dbd 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 6fc4b00..c0d4520 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 5c9def6..0f02978 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 f92dc43..b9001fa 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 f512a74..e14c6db 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 48dfb59..c265d0f 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) },