diff --git a/src/plonk.rs b/src/plonk.rs index 2910b65f..ac5ac13e 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -71,6 +71,8 @@ pub enum Error { ConstraintSystemFailure, /// Out of bounds index passed to a backend BoundsFailure, + /// Opening error + OpeningError, } fn hash_point>( @@ -345,10 +347,32 @@ fn test_proving() { let proof = Proof::create::, DummyHash, _>(¶ms, &srs, &circuit) .expect("proof generation should not fail"); - let msm_default = params.empty_msm(); - let msm = proof - .verify::, DummyHash>(¶ms, &srs, msm_default) + let msm = params.empty_msm(); + let guard = proof + .verify::, DummyHash>(¶ms, &srs, msm) .unwrap(); - assert!(msm.is_zero()) + { + let msm = guard.clone().use_challenges(); + assert!(msm.is_zero()); + } + { + let g = guard.compute_g(); + let (msm, _) = guard.clone().use_g(g); + assert!(msm.is_zero()); + } + let msm = guard.clone().use_challenges(); + assert!(msm.clone().is_zero()); + let guard = proof + .verify::, DummyHash>(¶ms, &srs, msm) + .unwrap(); + { + let msm = guard.clone().use_challenges(); + assert!(msm.is_zero()); + } + { + let g = guard.compute_g(); + let (msm, _) = guard.clone().use_g(g); + assert!(msm.is_zero()); + } } } diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 7af545a1..449bfd9f 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -1,7 +1,7 @@ use super::{hash_point, Error, Proof, SRS}; -use crate::arithmetic::{get_challenge_scalar, Challenge, Curve, CurveAffine, Field}; +use crate::arithmetic::{get_challenge_scalar, Challenge, CurveAffine, Field}; use crate::poly::{ - commitment::{Params, MSM}, + commitment::{Guard, Params, MSM}, Rotation, }; use crate::transcript::Hasher; @@ -12,8 +12,13 @@ impl<'a, C: CurveAffine> Proof { &self, params: &'a Params, srs: &SRS, - msm: MSM<'a, C>, - ) -> Result, Error> { + mut msm: MSM<'a, C>, + ) -> Result, Error> { + // Scale the MSM by a random factor to ensure that if the existing MSM + // has is_zero() == false then this argument won't be able to interfere + // with it to make it true, with high probability. + msm.scale(C::Scalar::random()); + // Create a transcript for obtaining Fiat-Shamir challenges. let mut transcript = HBase::init(C::Base::one()); @@ -148,17 +153,12 @@ impl<'a, C: CurveAffine> Proof { // Compress the commitments and expected evaluations at x_3 together // using the challenge x_4 - let mut q_commitments: Vec> = vec![None; srs.cs.rotations.len()]; + let mut q_commitments: Vec<_> = vec![params.empty_msm(); srs.cs.rotations.len()]; let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.cs.rotations.len()]; { let mut accumulate = |point_index: usize, new_commitment, eval| { - q_commitments[point_index] = q_commitments[point_index] - .map(|mut commitment| { - commitment *= x_4; - commitment += new_commitment; - commitment - }) - .or_else(|| Some(new_commitment.to_projective())); + q_commitments[point_index].scale(x_4); + q_commitments[point_index].add_term(C::Scalar::one(), new_commitment); q_evals[point_index] *= &x_4; q_evals[point_index] += &eval; }; @@ -256,29 +256,18 @@ impl<'a, C: CurveAffine> Proof { let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); // Compute the final commitment that has to be opened - let mut f_commitment: C::Projective = self.f_commitment.to_projective(); + let mut commitment_msm = params.empty_msm(); + commitment_msm.add_term(C::Scalar::one(), self.f_commitment); for (_, &point_index) in srs.cs.rotations.iter() { - f_commitment *= x_7; - f_commitment = f_commitment + &q_commitments[point_index.0].as_ref().unwrap(); + commitment_msm.scale(x_7); + commitment_msm.add_msm(&q_commitments[point_index.0]); f_eval *= &x_7; f_eval += &self.q_evals[point_index.0]; } // Verify the opening proof - let guard = self - .opening - .verify( - params, - msm, - &mut transcript, - x_6, - &f_commitment.to_affine(), - f_eval, - ) - .unwrap(); - - let msm_challenges = guard.use_challenges(); - - Ok(msm_challenges) + self.opening + .verify(params, msm, &mut transcript, x_6, commitment_msm, f_eval) + .map_err(|_| Error::OpeningError) } } diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index eb24af95..a1a9f603 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -45,6 +45,20 @@ pub struct MSM<'a, C: CurveAffine> { } impl<'a, C: CurveAffine> MSM<'a, C> { + /// Add another multiexp into this one + pub fn add_msm(&mut self, other: &Self) { + self.other_scalars.extend(other.other_scalars.iter()); + self.other_bases.extend(other.other_bases.iter()); + + if let Some(g_scalars) = &other.g_scalars { + self.add_to_g(&g_scalars); + } + + if let Some(h_scalar) = &other.h_scalar { + self.add_to_h(*h_scalar); + } + } + /// Add arbitrary term (the scalar and the point) pub fn add_term(&mut self, scalar: C::Scalar, point: C) { &self.other_scalars.push(scalar); @@ -423,13 +437,15 @@ fn test_opening_proof() { } else { let opening_proof = opening_proof.unwrap(); // Verify the opening proof + let mut commitment_msm = params.empty_msm(); + commitment_msm.add_term(Field::one(), p); let guard = opening_proof .verify( ¶ms, params.empty_msm(), &mut transcript_dup.clone(), x, - &p, + commitment_msm, v, ) .unwrap(); @@ -448,8 +464,17 @@ fn test_opening_proof() { // Check another proof to populate `msm.g_scalars` let msm = guard.use_challenges(); + let mut commitment_msm = params.empty_msm(); + commitment_msm.add_term(Field::one(), p); let guard = opening_proof - .verify(¶ms, msm, &mut transcript_dup.clone(), x, &p, v) + .verify( + ¶ms, + msm, + &mut transcript_dup.clone(), + x, + commitment_msm, + v, + ) .unwrap(); // Test use_challenges() diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index 2e937f91..c7673acd 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -14,7 +14,7 @@ impl OpeningProof { mut msm: MSM<'a, C>, transcript: &mut H, x: C::Scalar, - p: &C, + mut commitment_msm: MSM<'a, C>, v: C::Scalar, ) -> Result, Error> { // Check for well-formedness @@ -111,11 +111,13 @@ impl OpeningProof { // [c] P + [c * v] U + [c] sum(L_i * u_i^2) + [c] sum(R_i * u_i^-2) + delta - [z1] G - [z1 * b] U - [z2] H // = 0 - // Scale the MSM by a random factor to ensure that if the existing MSM - // has is_zero() == false then this argument won't be able to interfere - // with it to make it true. It's a way of keeping the MSM's linearly - // independent. - msm.scale(C::Scalar::random()); + let b = compute_b(x, &challenges, &challenges_inv); + + let neg_z1 = -self.z1; + + // [c] P + commitment_msm.scale(c); + msm.add_msm(&commitment_msm); for scalar in &mut extra_scalars { *scalar *= &c; @@ -125,13 +127,6 @@ impl OpeningProof { msm.add_term(*scalar, *base); } - let b = compute_b(x, &challenges, &challenges_inv); - - let neg_z1 = -self.z1; - - // [c] P - msm.add_term(c, *p); - // [c * v] U - [z1 * b] U msm.add_term((c * &v) + &(neg_z1 * &b), u);