diff --git a/src/poly/commitment/prover.rs b/src/poly/commitment/prover.rs index 12afe08f..ef51f108 100644 --- a/src/poly/commitment/prover.rs +++ b/src/poly/commitment/prover.rs @@ -3,8 +3,8 @@ use ff::Field; use super::super::{Coeff, Polynomial}; use super::{Blind, Params}; use crate::arithmetic::{ - best_multiexp, compute_inner_product, eval_polynomial, parallelize, small_multiexp, Curve, - CurveAffine, FieldExt, + best_multiexp, compute_inner_product, eval_polynomial, parallelize, Curve, CurveAffine, + FieldExt, }; use crate::transcript::{Challenge, ChallengeScalar, TranscriptWrite}; use std::io; @@ -84,9 +84,8 @@ pub fn create_proof>( } } - // Initialize the vector `G` from the SRS. We'll be progressively - // collapsing this vector into smaller and smaller vectors until it is - // of length 1. + // Initialize the vector `G` from the SRS. We'll be progressively collapsing + // this vector into smaller and smaller vectors until it is of length 1. let mut g = params.g.clone(); // Perform the inner product argument, round by round. @@ -98,76 +97,42 @@ pub fn create_proof>( // TODO: If we modify multiexp to take "extra" bases, we could speed // this piece up a bit by combining the multiexps. metrics::counter!("multiexp", 2, "val" => "l/r", "size" => format!("{}", half)); - let l = best_multiexp(&a[0..half], &g[half..]); - let r = best_multiexp(&a[half..], &g[0..half]); - let value_l = compute_inner_product(&a[0..half], &b[half..]); - let value_r = compute_inner_product(&a[half..], &b[0..half]); - let mut l_randomness = C::Scalar::rand(); + let l = best_multiexp(&a[half..], &g[0..half]); + let r = best_multiexp(&a[0..half], &g[half..]); + let value_l = compute_inner_product(&a[half..], &b[0..half]); + let value_r = compute_inner_product(&a[0..half], &b[half..]); + let l_randomness = C::Scalar::rand(); let r_randomness = C::Scalar::rand(); metrics::counter!("multiexp", 2, "val" => "l/r", "size" => "2"); let l = l + &best_multiexp(&[value_l * &z, l_randomness], &[params.u, params.h]); let r = r + &best_multiexp(&[value_r * &z, r_randomness], &[params.u, params.h]); - let mut l = l.to_affine(); + let l = l.to_affine(); let r = r.to_affine(); - let challenge = loop { - // We'll fork the transcript and adjust our randomness - // until the challenge is a square. - let mut transcript = transcript.fork(); - - // Feed L and R into the cloned transcript. - // We expect these to not be points at infinity due to the randomness. - transcript.write_point(l)?; - transcript.write_point(r)?; - - // ... and get the squared challenge. - let challenge_sq_packed = Challenge::get(&mut transcript); - let challenge_sq = *ChallengeScalar::::from(challenge_sq_packed); - - // There might be no square root, in which case we'll fork the - // transcript. - let challenge = challenge_sq.deterministic_sqrt(); - if let Some(challenge) = challenge { - break challenge; - } else { - // Try again, with slightly different randomness - l = (l + params.h).to_affine(); - l_randomness += &C::Scalar::one(); - } - }; - - // Challenge is unlikely to be zero. - let challenge_inv = challenge.invert().unwrap(); - let challenge_sq_inv = challenge_inv.square(); - let challenge_sq = challenge.square(); - // Feed L and R into the real transcript transcript.write_point(l)?; transcript.write_point(r)?; - // And obtain the challenge, even though we already have it, since - // squeezing affects the transcript. - { - let challenge_sq_expected = ChallengeScalar::<_, ()>::get(transcript); - assert_eq!(challenge_sq, *challenge_sq_expected); - } + let challenge_packed = Challenge::get(transcript); + let challenge = *ChallengeScalar::::from(challenge_packed); + let challenge_inv = challenge.invert().unwrap(); // TODO, bubble this up // Collapse `a` and `b`. // TODO: parallelize for i in 0..half { - a[i] = (a[i] * &challenge) + &(a[i + half] * &challenge_inv); - b[i] = (b[i] * &challenge_inv) + &(b[i + half] * &challenge); + a[i] = a[i] + &(a[i + half] * &challenge_inv); + b[i] = b[i] + &(b[i + half] * &challenge); } a.truncate(half); b.truncate(half); // Collapse `G` - parallel_generator_collapse(&mut g, challenge, challenge_inv); + parallel_generator_collapse(&mut g, challenge); g.truncate(half); // Update randomness (the synthetic blinding factor at the end) - blind += &(l_randomness * &challenge_sq); - blind += &(r_randomness * &challenge_sq_inv); + blind += &(l_randomness * &challenge_inv); + blind += &(r_randomness * &challenge); } // We have fully collapsed `a`, `b`, `G` @@ -180,20 +145,16 @@ pub fn create_proof>( Ok(()) } -fn parallel_generator_collapse( - g: &mut [C], - challenge: C::Scalar, - challenge_inv: C::Scalar, -) { +fn parallel_generator_collapse(g: &mut [C], challenge: C::Scalar) { let len = g.len() / 2; let (mut g_lo, g_hi) = g.split_at_mut(len); - metrics::counter!("multiexp", len as u64, "size" => "2", "fn" => "parallel_generator_collapse"); + metrics::counter!("scalar_multiplication", len as u64, "fn" => "parallel_generator_collapse"); parallelize(&mut g_lo, |g_lo, start| { let g_hi = &g_hi[start..]; let mut tmp = Vec::with_capacity(g_lo.len()); for (g_lo, g_hi) in g_lo.iter().zip(g_hi.iter()) { - tmp.push(small_multiexp(&[challenge_inv, challenge], &[*g_lo, *g_hi])); + tmp.push(g_lo.to_projective() + &(*g_hi * challenge)); } C::Projective::batch_to_affine(&tmp, g_lo); }); diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index 0adf9ba8..1d956c64 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -4,16 +4,15 @@ use super::super::Error; use super::{Params, MSM}; use crate::transcript::{Challenge, ChallengeScalar, TranscriptRead}; -use crate::arithmetic::{best_multiexp, Curve, CurveAffine, FieldExt}; +use crate::arithmetic::{best_multiexp, BatchInvert, Curve, CurveAffine}; /// A guard returned by the verifier #[derive(Debug, Clone)] pub struct Guard<'a, C: CurveAffine> { msm: MSM<'a, C>, neg_a: C::Scalar, - allinv: C::Scalar, - challenges_sq: Vec, - challenges_sq_packed: Vec, + challenges: Vec, + challenges_packed: Vec, } /// An accumulator instance consisting of an evaluation claim and a proof. @@ -24,14 +23,14 @@ pub struct Accumulator { /// A vector of 128-bit challenges sampled by the verifier, to be used in /// computing g. - pub challenges_sq_packed: Vec, + pub challenges_packed: Vec, } impl<'a, C: CurveAffine> Guard<'a, C> { /// Lets caller supply the challenges and obtain an MSM with updated /// scalars and points. pub fn use_challenges(mut self) -> MSM<'a, C> { - let s = compute_s(&self.challenges_sq, self.allinv * &self.neg_a); + let s = compute_s(&self.challenges, self.neg_a); self.msm.add_to_g_scalars(&s); self.msm.add_to_h_scalar(self.neg_a); @@ -45,7 +44,7 @@ impl<'a, C: CurveAffine> Guard<'a, C> { let accumulator = Accumulator { g, - challenges_sq_packed: self.challenges_sq_packed, + challenges_packed: self.challenges_packed, }; (self.msm, accumulator) @@ -53,7 +52,7 @@ impl<'a, C: CurveAffine> Guard<'a, C> { /// Computes G + H, where G = ⟨s, params.g⟩ and H is used for blinding pub fn compute_g(&self) -> C { - let s = compute_s(&self.challenges_sq, self.allinv); + let s = compute_s(&self.challenges, C::Scalar::one()); metrics::increment_counter!("multiexp", "size" => format!("{}", s.len()), "fn" => "compute_g"); let mut tmp = best_multiexp(&s, &self.msm.params.g); @@ -84,46 +83,37 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead>( let z = *ChallengeScalar::::get(transcript); - // Data about the challenges from each of the rounds. - let mut challenges = Vec::with_capacity(k); - let mut challenges_inv = Vec::with_capacity(k); - let mut challenges_sq = Vec::with_capacity(k); - let mut challenges_sq_packed: Vec = Vec::with_capacity(k); - let mut allinv = C::Scalar::one(); - + let mut rounds = vec![]; for _ in 0..k { // Read L and R from the proof and write them to the transcript let l = transcript.read_point().map_err(|_| Error::OpeningError)?; let r = transcript.read_point().map_err(|_| Error::OpeningError)?; - let challenge_sq_packed = Challenge::get(transcript); - let challenge_sq = *ChallengeScalar::::from(challenge_sq_packed); + let challenge_packed = Challenge::get(transcript); + let challenge = *ChallengeScalar::::from(challenge_packed); - let challenge = challenge_sq.deterministic_sqrt(); - if challenge.is_none() { - // We didn't sample a square. - return Err(Error::OpeningError); - } - let challenge = challenge.unwrap(); + rounds.push(( + l, + r, + challenge, + /* to be inverted */ challenge, + challenge_packed, + )); + } - let challenge_inv = challenge.invert(); - if bool::from(challenge_inv.is_none()) { - // We sampled zero for some reason, unlikely to happen by - // chance. - return Err(Error::OpeningError); - } - let challenge_inv = challenge_inv.unwrap(); - allinv *= &challenge_inv; + rounds + .iter_mut() + .map(|&mut (_, _, _, ref mut challenge, _)| challenge) + .batch_invert(); - let challenge_sq_inv = challenge_inv.square(); - - msm.append_term(challenge_sq, l); - msm.append_term(challenge_sq_inv, r); + let mut challenges = Vec::with_capacity(k); + let mut challenges_packed: Vec = Vec::with_capacity(k); + for (l, r, challenge, challenge_inv, challenge_packed) in rounds { + msm.append_term(challenge_inv, l); + msm.append_term(challenge, r); challenges.push(challenge); - challenges_inv.push(challenge_inv); - challenges_sq.push(challenge_sq); - challenges_sq_packed.push(challenge_sq_packed); + challenges_packed.push(challenge_packed); } // Our goal is to open @@ -142,7 +132,7 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead>( let a = transcript.read_scalar().map_err(|_| Error::SamplingError)?; let neg_a = -a; let h = transcript.read_scalar().map_err(|_| Error::SamplingError)?; - let b = compute_b(x, &challenges, &challenges_inv); + let b = compute_b(x, &challenges); msm.add_to_u_scalar(neg_a * &b * &z); msm.add_to_h_scalar(a - &h); @@ -150,42 +140,43 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead>( let guard = Guard { msm, neg_a, - allinv, - challenges_sq, - challenges_sq_packed, + challenges, + challenges_packed, }; Ok(guard) } -fn compute_b(x: F, challenges: &[F], challenges_inv: &[F]) -> F { - assert!(!challenges.is_empty()); - assert_eq!(challenges.len(), challenges_inv.len()); - if challenges.len() == 1 { - *challenges_inv.last().unwrap() + *challenges.last().unwrap() * x - } else { - (*challenges_inv.last().unwrap() + *challenges.last().unwrap() * x) - * compute_b( - x.square(), - &challenges[0..(challenges.len() - 1)], - &challenges_inv[0..(challenges.len() - 1)], - ) +/// Computes $\prod\limits_{i=0}^{k-1} (1 + u_i x^{2^i})$. +fn compute_b(x: F, challenges: &[F]) -> F { + let mut tmp = F::one(); + let mut cur = x; + for challenge in challenges.iter().rev() { + tmp *= F::one() + &(*challenge * &cur); + cur *= cur; } + tmp } -// TODO: parallelize -fn compute_s(challenges_sq: &[F], allinv: F) -> Vec { - let lg_n = challenges_sq.len(); - let n = 1 << lg_n; +/// Computes the coefficients of $g(X) = \prod\limits_{i=0}^{k-1} (1 + u_i X^{2^i})$. +fn compute_s(challenges: &[F], init: F) -> Vec { + assert!(challenges.len() > 0); + let mut v = vec![F::zero(); 1 << challenges.len()]; + v[0] = init; - let mut s = Vec::with_capacity(n); - s.push(allinv); - for i in 1..n { - let lg_i = (32 - 1 - (i as u32).leading_zeros()) as usize; - let k = 1 << lg_i; - let u_lg_i_sq = challenges_sq[(lg_n - 1) - lg_i]; - s.push(s[i - k] * u_lg_i_sq); + for (len, challenge) in challenges + .iter() + .rev() + .enumerate() + .map(|(i, challenge)| (1 << i, challenge)) + { + let (left, right) = v.split_at_mut(len); + let right = &mut right[0..len]; + right.copy_from_slice(&left); + for v in right { + *v *= challenge; + } } - s + v }