From 5724706a09b33c1344b185d8c5d0f647c85c8790 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 9 Sep 2020 21:00:36 +0800 Subject: [PATCH 01/16] Add MSM and Guard structs in polycommit scheme --- src/arithmetic.rs | 4 + src/plonk/verifier.rs | 27 ++++-- src/poly.rs | 8 ++ src/poly/commitment.rs | 162 +++++++++++++++++++++++++++++++- src/poly/commitment/verifier.rs | 92 +++++++----------- 5 files changed, 225 insertions(+), 68 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 312cfe5..e74cfee 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -70,6 +70,10 @@ where } /// This is a 128-bit verifier challenge. +/// +/// The verifier samples its challenge here as u^2, i.e. the square of the +/// actual challenge. This is an optimisation that is documented in Section 6.3 +/// of the [Halo](https://eprint.iacr.org/2019/1021) paper. #[derive(Copy, Clone, Debug)] pub struct Challenge(pub(crate) u128); diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 9c3adca..6250397 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -1,6 +1,9 @@ use super::{hash_point, Proof, SRS}; use crate::arithmetic::{get_challenge_scalar, Challenge, Curve, CurveAffine, Field}; -use crate::poly::{commitment::Params, Rotation}; +use crate::poly::{ + commitment::{Params, MSM}, + Rotation, +}; use crate::transcript::Hasher; impl Proof { @@ -261,12 +264,20 @@ impl Proof { } // Verify the opening proof - self.opening.verify( - params, - &mut transcript, - x_6, - &f_commitment.to_affine(), - f_eval, - ) + let (challenges, mut guard) = self + .opening + .verify( + params, + &mut MSM::default(¶ms), + &mut transcript, + x_6, + &f_commitment.to_affine(), + f_eval, + ) + .unwrap(); + + let msm: MSM = guard.use_challenges(challenges).unwrap(); + + msm.is_zero() } } diff --git a/src/poly.rs b/src/poly.rs index eef5356..07f32e3 100644 --- a/src/poly.rs +++ b/src/poly.rs @@ -13,6 +13,14 @@ mod domain; pub use domain::*; +/// This is an error that could occur during proving or circuit synthesis. +// TODO: these errors need to be cleaned up +#[derive(Debug)] +pub enum Error { + /// OpeningProof is not well-formed + OpeningError, +} + /// The basis over which a polynomial is described. pub trait Basis: Clone + Debug + Send + Sync {} diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 1a98548..e9189d0 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -3,8 +3,11 @@ //! //! [halo]: https://eprint.iacr.org/2019/1021 -use super::{Coeff, LagrangeCoeff, Polynomial}; -use crate::arithmetic::{best_fft, best_multiexp, parallelize, Curve, CurveAffine, Field}; +use super::{Coeff, Error, LagrangeCoeff, Polynomial}; +use crate::arithmetic::{ + best_fft, best_multiexp, get_challenge_scalar, parallelize, Challenge, Curve, CurveAffine, + Field, +}; use crate::transcript::Hasher; use std::ops::{Add, AddAssign, Mul, MulAssign}; @@ -21,6 +24,49 @@ pub struct OpeningProof { z2: C::Scalar, } +/// A multiscalar multiplication in the polynomial commitment scheme +#[derive(Debug)] +pub struct MSM { + /// Scalars in the multiscalar multiplication + pub scalars: Vec, + + /// Points in the multiscalar multiplication + pub bases: Vec, +} + +impl<'a, C: CurveAffine> MSM { + /// Empty MSM + pub fn default(params: &'a Params) -> Self { + let scalars: Vec = + Vec::with_capacity(params.k as usize * 2 + 4 + params.n as usize); + let bases: Vec = Vec::with_capacity(params.k as usize * 2 + 4 + params.n as usize); + + MSM { scalars, bases } + } + + /// Add arbitrary term (the scalar and the point) + pub fn add_term(&mut self, scalar: C::Scalar, point: C) { + &self.scalars.push(scalar); + &self.bases.push(point); + } + /// Add term to g + pub fn mutate_g(&mut self, scalar: C::Scalar, point: C) -> Self { + unimplemented!() + } + /// Add term to h + pub fn mutate_h(&mut self, scalar: C::Scalar, point: C) -> Self { + unimplemented!() + } + /// Scale by a random blinding factor + pub fn scale(&self, scalar: C::Scalar) -> Self { + unimplemented!() + } + /// Perform multiexp and check that it results in zero + pub fn is_zero(&self) -> bool { + bool::from(best_multiexp(&self.scalars, &self.bases).is_zero()) + } +} + /// These are the public parameters for the polynomial commitment scheme. #[derive(Debug)] pub struct Params { @@ -154,6 +200,85 @@ impl Params { } } +/// A guard returned by the verifier +#[derive(Debug)] +pub struct Guard<'a, C: CurveAffine> { + /// Negation of z1 value in the OpeningProof + pub neg_z1: C::Scalar, + + /// Params that were used by the verifier + pub params: &'a Params, + + /// Scalars produced by the verifier for multiscalar multiplication + pub scalars: Vec, + + /// Points produced by the verifier for multiscalar multiplication + pub bases: 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, + challenges_sq_packed: Vec, + ) -> Result, Error> { + // - [z1] G + let mut allinv = C::Scalar::one(); + let mut challenges_sq = Vec::with_capacity(self.params.k as usize); + + for challenge_sq_packed in challenges_sq_packed { + let challenge_sq: C::Scalar = get_challenge_scalar(challenge_sq_packed); + challenges_sq.push(challenge_sq); + + let challenge = challenge_sq.deterministic_sqrt(); + if challenge.is_none() { + // We didn't sample a square. + return Err(Error::OpeningError); + } + let challenge = challenge.unwrap(); + + 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; + } + + self.bases.extend(&self.params.g); + let mut s = compute_s(&challenges_sq, allinv); + // TODO: parallelize + for s in &mut s { + *s *= &self.neg_z1; + } + self.scalars.extend(s); + + Ok(MSM { + scalars: self.scalars.clone(), + bases: self.bases.clone(), + }) + } + + /// Lets caller supply the purported G point and simply appends it to + /// return an updated MSM. + pub fn use_s(&mut self, mut s: Vec) -> Result, Error> { + // - [z1] G + self.bases.extend(&self.params.g); + for s in &mut s { + *s *= &self.neg_z1; + } + self.scalars.extend(s); + + Ok(MSM { + scalars: self.scalars.clone(), + bases: self.bases.clone(), + }) + } +} + /// Wrapper type around a blinding factor. #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub struct Blind(pub F); @@ -273,8 +398,39 @@ fn test_opening_proof() { transcript.absorb(Field::one()); } else { let opening_proof = opening_proof.unwrap(); - assert!(opening_proof.verify(¶ms, &mut transcript_dup, x, &p, v)); + // Verify the opening proof + let (challenges, mut guard) = opening_proof + .verify( + ¶ms, + &mut MSM::default(¶ms), + &mut transcript_dup, + x, + &p, + v, + ) + .unwrap(); + + let msm = guard.use_challenges(challenges).unwrap(); + + assert!(msm.is_zero()); break; } } } + +// TODO: parallelize +fn compute_s(challenges_sq: &[F], allinv: F) -> Vec { + let lg_n = challenges_sq.len(); + let n = 1 << lg_n; + + 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); + } + + s +} diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index a3d7e4d..9351c92 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -1,25 +1,25 @@ -use super::{OpeningProof, Params}; +use super::super::Error; +use super::{Guard, OpeningProof, Params, MSM}; use crate::transcript::Hasher; -use crate::arithmetic::{ - best_multiexp, get_challenge_scalar, Challenge, Curve, CurveAffine, Field, -}; +use crate::arithmetic::{get_challenge_scalar, Challenge, CurveAffine, Field}; impl OpeningProof { /// Checks to see if an [`OpeningProof`] is valid given the current /// `transcript`, and a point `x` that the polynomial commitment `p` opens /// purportedly to the value `v`. - pub fn verify>( + pub fn verify<'a, H: Hasher>( &self, - params: &Params, + params: &'a Params, + msm: &mut MSM, transcript: &mut H, x: C::Scalar, p: &C, v: C::Scalar, - ) -> bool { + ) -> Result<(Vec, Guard<'a, C>), Error> { // Check for well-formedness if self.rounds.len() != params.k as usize { - return false; + return Err(Error::OpeningError); } transcript.absorb(C::Base::from_u64(self.fork as u64)); @@ -31,28 +31,25 @@ impl OpeningProof { let u_y2 = u_x.square() * &u_x + &C::b(); let u_y = u_y2.deterministic_sqrt(); if u_y.is_none() { - return false; + return Err(Error::OpeningError); } let u_y = u_y.unwrap(); C::from_xy(u_x, u_y).unwrap() }; - let mut extra_scalars = Vec::with_capacity(self.rounds.len() * 2 + 4 + params.n as usize); - let mut extra_bases = Vec::with_capacity(self.rounds.len() * 2 + 4 + params.n as usize); - // Data about the challenges from each of the rounds. let mut challenges = Vec::with_capacity(self.rounds.len()); let mut challenges_inv = Vec::with_capacity(self.rounds.len()); let mut challenges_sq = Vec::with_capacity(self.rounds.len()); - let mut allinv = Field::one(); + let mut challenges_sq_packed: Vec = Vec::with_capacity(self.rounds.len()); for round in &self.rounds { // Feed L and R into the transcript. let l = round.0.get_xy(); let r = round.1.get_xy(); if bool::from(l.is_none() | r.is_none()) { - return false; + return Err(Error::OpeningError); } let l = l.unwrap(); let r = r.unwrap(); @@ -66,7 +63,7 @@ impl OpeningProof { let challenge = challenge_sq.deterministic_sqrt(); if challenge.is_none() { // We didn't sample a square. - return false; + return Err(Error::OpeningError); } let challenge = challenge.unwrap(); @@ -74,26 +71,26 @@ impl OpeningProof { if bool::from(challenge_inv.is_none()) { // We sampled zero for some reason, unlikely to happen by // chance. - return false; + return Err(Error::OpeningError); } let challenge_inv = challenge_inv.unwrap(); - allinv *= challenge_inv; let challenge_sq_inv = challenge_inv.square(); - extra_scalars.push(challenge_sq); - extra_bases.push(round.0); - extra_scalars.push(challenge_sq_inv); - extra_bases.push(round.1); + msm.scalars.push(challenge_sq); + msm.bases.push(round.0); + msm.scalars.push(challenge_sq_inv); + msm.bases.push(round.1); challenges.push(challenge); challenges_inv.push(challenge_inv); challenges_sq.push(challenge_sq); + challenges_sq_packed.push(Challenge(challenge_sq_packed)); } let delta = self.delta.get_xy(); if bool::from(delta.is_none()) { - return false; + return Err(Error::OpeningError); } let delta = delta.unwrap(); @@ -109,7 +106,7 @@ 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 - for scalar in &mut extra_scalars { + for scalar in &mut msm.scalars { *scalar *= &c; } @@ -118,31 +115,29 @@ impl OpeningProof { let neg_z1 = -self.z1; // [c] P - extra_bases.push(*p); - extra_scalars.push(c); + msm.bases.push(*p); + msm.scalars.push(c); // [c * v] U - [z1 * b] U - extra_bases.push(u); - extra_scalars.push((c * &v) + &(neg_z1 * &b)); + msm.bases.push(u); + msm.scalars.push((c * &v) + &(neg_z1 * &b)); // delta - extra_bases.push(self.delta); - extra_scalars.push(Field::one()); + msm.bases.push(self.delta); + msm.scalars.push(Field::one()); // - [z2] H - extra_bases.push(params.h); - extra_scalars.push(-self.z2); + msm.bases.push(params.h); + msm.scalars.push(-self.z2); - // - [z1] G - extra_bases.extend(¶ms.g); - let mut s = compute_s(&challenges_sq, allinv); - // TODO: parallelize - for s in &mut s { - *s *= &neg_z1; - } - extra_scalars.extend(s); + let guard = Guard::<'a, _> { + neg_z1, + params, + scalars: msm.scalars.clone(), + bases: msm.bases.clone(), + }; - bool::from(best_multiexp(&extra_scalars, &extra_bases).is_zero()) + Ok((challenges_sq_packed, guard)) } } @@ -160,20 +155,3 @@ fn compute_b(x: F, challenges: &[F], challenges_inv: &[F]) -> F { ) } } - -// TODO: parallelize -fn compute_s(challenges_sq: &[F], allinv: F) -> Vec { - let lg_n = challenges_sq.len(); - let n = 1 << lg_n; - - 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); - } - - s -} From 7255e085a4e2fdde68070366afbfb1baddf5eb6d Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 10 Sep 2020 19:50:02 +0800 Subject: [PATCH 02/16] Add more fields and methods functions to MSM struct --- src/poly/commitment.rs | 47 ++++++++++++++++++++++++++------- src/poly/commitment/verifier.rs | 4 ++- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index e9189d0..8fd2ebc 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -27,6 +27,12 @@ pub struct OpeningProof { /// A multiscalar multiplication in the polynomial commitment scheme #[derive(Debug)] pub struct MSM { + /// Vector of random generators + pub g: Vec, + + /// Random generator + pub h: C, + /// Scalars in the multiscalar multiplication pub scalars: Vec, @@ -41,7 +47,12 @@ impl<'a, C: CurveAffine> MSM { Vec::with_capacity(params.k as usize * 2 + 4 + params.n as usize); let bases: Vec = Vec::with_capacity(params.k as usize * 2 + 4 + params.n as usize); - MSM { scalars, bases } + MSM { + g: params.g.clone(), + h: params.h.clone(), + scalars, + bases, + } } /// Add arbitrary term (the scalar and the point) @@ -49,18 +60,24 @@ impl<'a, C: CurveAffine> MSM { &self.scalars.push(scalar); &self.bases.push(point); } + /// Add term to g - pub fn mutate_g(&mut self, scalar: C::Scalar, point: C) -> Self { - unimplemented!() + pub fn add_to_g(&mut self, point: C) { + &self.g.push(point); } + /// Add term to h - pub fn mutate_h(&mut self, scalar: C::Scalar, point: C) -> Self { - unimplemented!() + pub fn add_to_h(&mut self, point: C) { + self.h = self.h.add(point).to_affine(); } + /// Scale by a random blinding factor - pub fn scale(&self, scalar: C::Scalar) -> Self { - unimplemented!() + pub fn scale(&mut self, factor: C::Scalar) { + for scalar in self.scalars.iter_mut() { + *scalar *= &factor; + } } + /// Perform multiexp and check that it results in zero pub fn is_zero(&self) -> bool { bool::from(best_multiexp(&self.scalars, &self.bases).is_zero()) @@ -203,6 +220,12 @@ impl Params { /// A guard returned by the verifier #[derive(Debug)] pub struct Guard<'a, C: CurveAffine> { + /// Vector of random generators + pub g: Vec, + + /// Random generator + pub h: C, + /// Negation of z1 value in the OpeningProof pub neg_z1: C::Scalar, @@ -248,7 +271,7 @@ impl<'a, C: CurveAffine> Guard<'a, C> { allinv *= &challenge_inv; } - self.bases.extend(&self.params.g); + self.bases.extend(&self.g); let mut s = compute_s(&challenges_sq, allinv); // TODO: parallelize for s in &mut s { @@ -257,6 +280,8 @@ impl<'a, C: CurveAffine> Guard<'a, C> { self.scalars.extend(s); Ok(MSM { + g: self.g.clone(), + h: self.h.clone(), scalars: self.scalars.clone(), bases: self.bases.clone(), }) @@ -264,15 +289,17 @@ impl<'a, C: CurveAffine> Guard<'a, C> { /// Lets caller supply the purported G point and simply appends it to /// return an updated MSM. - pub fn use_s(&mut self, mut s: Vec) -> Result, Error> { + pub fn use_s(&mut self, g: Vec, mut s: Vec) -> Result, Error> { // - [z1] G - self.bases.extend(&self.params.g); + self.bases.extend(&g); for s in &mut s { *s *= &self.neg_z1; } self.scalars.extend(s); Ok(MSM { + g: self.g.clone(), + h: self.h.clone(), scalars: self.scalars.clone(), bases: self.bases.clone(), }) diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index 9351c92..43470cc 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -127,10 +127,12 @@ impl OpeningProof { msm.scalars.push(Field::one()); // - [z2] H - msm.bases.push(params.h); + msm.bases.push(msm.h); msm.scalars.push(-self.z2); let guard = Guard::<'a, _> { + g: msm.g.clone(), + h: msm.h.clone(), neg_z1, params, scalars: msm.scalars.clone(), From d41fcf842ba665aadbb765c82d2dd96a965eb65f Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 11 Sep 2020 13:42:11 +0800 Subject: [PATCH 03/16] Modify MSM and Guard structs and methods --- src/arithmetic.rs | 4 - src/plonk/verifier.rs | 9 +- src/poly/commitment.rs | 201 ++++++++++++++++---------------- src/poly/commitment/verifier.rs | 43 ++++--- 4 files changed, 128 insertions(+), 129 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index e74cfee..312cfe5 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -70,10 +70,6 @@ where } /// This is a 128-bit verifier challenge. -/// -/// The verifier samples its challenge here as u^2, i.e. the square of the -/// actual challenge. This is an optimisation that is documented in Section 6.3 -/// of the [Halo](https://eprint.iacr.org/2019/1021) paper. #[derive(Copy, Clone, Debug)] pub struct Challenge(pub(crate) u128); diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 6250397..1b303aa 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -264,11 +264,12 @@ impl Proof { } // Verify the opening proof - let (challenges, mut guard) = self + let default_msm = MSM::default(¶ms); + let (challenges, guard) = self .opening .verify( params, - &mut MSM::default(¶ms), + default_msm, &mut transcript, x_6, &f_commitment.to_affine(), @@ -276,8 +277,8 @@ impl Proof { ) .unwrap(); - let msm: MSM = guard.use_challenges(challenges).unwrap(); + let msm: &MSM = &guard.use_challenges(params, challenges).unwrap(); - msm.is_zero() + msm.is_zero(params) } } diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 8fd2ebc..45fd0d8 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -27,60 +27,89 @@ pub struct OpeningProof { /// A multiscalar multiplication in the polynomial commitment scheme #[derive(Debug)] pub struct MSM { - /// Vector of random generators - pub g: Vec, + /// TODO: documentation + pub g_scalars: Option>, - /// Random generator - pub h: C, + /// TODO: documentation + pub h_scalar: Option, - /// Scalars in the multiscalar multiplication - pub scalars: Vec, + /// TODO: documentation + pub other_scalars: Vec, - /// Points in the multiscalar multiplication - pub bases: Vec, + /// TODO: documentation + pub other_bases: Vec, } impl<'a, C: CurveAffine> MSM { /// Empty MSM - pub fn default(params: &'a Params) -> Self { - let scalars: Vec = - Vec::with_capacity(params.k as usize * 2 + 4 + params.n as usize); - let bases: Vec = Vec::with_capacity(params.k as usize * 2 + 4 + params.n as usize); + pub fn default(params: &Params) -> Self { + let g_scalars = Some(vec![C::Scalar::one(); params.n as usize]); + let h_scalar = Some(C::Scalar::one()); + let other_scalars: Vec = Vec::with_capacity(params.k as usize * 2 + 3); + let other_bases: Vec = Vec::with_capacity(params.k as usize * 2 + 3); MSM { - g: params.g.clone(), - h: params.h.clone(), - scalars, - bases, + g_scalars, + h_scalar, + other_scalars, + other_bases, } } /// Add arbitrary term (the scalar and the point) pub fn add_term(&mut self, scalar: C::Scalar, point: C) { - &self.scalars.push(scalar); - &self.bases.push(point); + &self.other_scalars.push(scalar); + &self.other_bases.push(point); } - /// Add term to g - pub fn add_to_g(&mut self, point: C) { - &self.g.push(point); - } - - /// Add term to h - pub fn add_to_h(&mut self, point: C) { - self.h = self.h.add(point).to_affine(); - } - - /// Scale by a random blinding factor - pub fn scale(&mut self, factor: C::Scalar) { - for scalar in self.scalars.iter_mut() { - *scalar *= &factor; + /// Add a vector of scalars to `g_scalars` + pub fn add_to_g(&mut self, scalars: Vec) { + for (g_scalar, scalar) in self + .g_scalars + .as_mut() + .unwrap() + .iter_mut() + .zip(scalars.iter()) + { + *g_scalar += &scalar; } } + /// Add term to h + pub fn add_to_h(&mut self, scalar: C::Scalar) { + self.h_scalar = Some(self.h_scalar.unwrap() + &scalar); + } + + /// Scale all scalars in the MSM by a random blinding factor + pub fn scale(&mut self, factor: C::Scalar) { + for g_scalar in self.g_scalars.as_mut().unwrap().iter_mut() { + *g_scalar *= &factor; + } + for other_scalar in self.other_scalars.iter_mut() { + *other_scalar *= &factor; + } + self.h_scalar = Some(self.h_scalar.unwrap() * &factor); + } + /// Perform multiexp and check that it results in zero - pub fn is_zero(&self) -> bool { - bool::from(best_multiexp(&self.scalars, &self.bases).is_zero()) + pub fn is_zero(&self, params: &'a Params) -> bool { + let mut scalars: Vec = vec![]; + let mut bases: Vec = vec![]; + + scalars.extend(&self.other_scalars); + bases.extend(&self.other_bases); + + if let Some(h_scalar) = self.h_scalar { + scalars.push(h_scalar); + bases.push(params.h); + } + + if let Some(g_scalars) = &self.g_scalars { + scalars.extend(g_scalars); + bases.extend(params.g.iter()); + } + + bool::from(best_multiexp(&scalars, &bases).is_zero()) } } @@ -219,90 +248,70 @@ impl Params { /// A guard returned by the verifier #[derive(Debug)] -pub struct Guard<'a, C: CurveAffine> { - /// Vector of random generators - pub g: Vec, - - /// Random generator - pub h: C, +pub struct Guard { + /// MSM + msm: MSM, /// Negation of z1 value in the OpeningProof - pub neg_z1: C::Scalar, + neg_z1: C::Scalar, - /// Params that were used by the verifier - pub params: &'a Params, + allinv: C::Scalar, - /// Scalars produced by the verifier for multiscalar multiplication - pub scalars: Vec, - - /// Points produced by the verifier for multiscalar multiplication - pub bases: Vec, + challenges_sq: Vec, } -impl<'a, C: CurveAffine> Guard<'a, C> { +impl Guard { /// Lets caller supply the challenges and obtain an MSM with updated /// scalars and points. pub fn use_challenges( - &mut self, + mut self, + params: &Params, challenges_sq_packed: Vec, ) -> Result, Error> { + let mut scalars: Vec = vec![]; + let mut bases: Vec = vec![]; + + scalars.extend(&self.msm.other_scalars); + bases.extend(&self.msm.other_bases); + + // - [z2] H + if let Some(h_scalar) = self.msm.h_scalar { + scalars.push(h_scalar); + bases.push(params.h); + } + // - [z1] G let mut allinv = C::Scalar::one(); - let mut challenges_sq = Vec::with_capacity(self.params.k as usize); + let mut challenges_sq = Vec::with_capacity(params.k as usize); - for challenge_sq_packed in challenges_sq_packed { - let challenge_sq: C::Scalar = get_challenge_scalar(challenge_sq_packed); + for challenge_sq_packed in challenges_sq_packed.iter() { + let challenge_sq: C::Scalar = get_challenge_scalar(*challenge_sq_packed); challenges_sq.push(challenge_sq); let challenge = challenge_sq.deterministic_sqrt(); - if challenge.is_none() { - // We didn't sample a square. - return Err(Error::OpeningError); - } let challenge = challenge.unwrap(); 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; } - self.bases.extend(&self.g); - let mut s = compute_s(&challenges_sq, allinv); - // TODO: parallelize - for s in &mut s { - *s *= &self.neg_z1; - } - self.scalars.extend(s); + let s = compute_s(&challenges_sq, allinv * &self.neg_z1); + scalars.extend(&s); + bases.extend(¶ms.g); - Ok(MSM { - g: self.g.clone(), - h: self.h.clone(), - scalars: self.scalars.clone(), - bases: self.bases.clone(), - }) + self.msm.g_scalars = Some(s); + + Ok(self.msm) } /// Lets caller supply the purported G point and simply appends it to /// return an updated MSM. - pub fn use_s(&mut self, g: Vec, mut s: Vec) -> Result, Error> { - // - [z1] G - self.bases.extend(&g); - for s in &mut s { - *s *= &self.neg_z1; - } - self.scalars.extend(s); + pub fn use_g(mut self, g: C) -> Result, Error> { + &self.msm.other_scalars.push(self.allinv * &self.neg_z1); + &self.msm.other_bases.push(g); - Ok(MSM { - g: self.g.clone(), - h: self.h.clone(), - scalars: self.scalars.clone(), - bases: self.bases.clone(), - }) + Ok(self.msm) } } @@ -426,20 +435,14 @@ fn test_opening_proof() { } else { let opening_proof = opening_proof.unwrap(); // Verify the opening proof - let (challenges, mut guard) = opening_proof - .verify( - ¶ms, - &mut MSM::default(¶ms), - &mut transcript_dup, - x, - &p, - v, - ) + let msm = MSM::default(¶ms); + let (challenges, guard) = opening_proof + .verify(¶ms, msm, &mut transcript_dup, x, &p, v) .unwrap(); - let msm = guard.use_challenges(challenges).unwrap(); + let msm = guard.use_challenges(¶ms, challenges).unwrap(); - assert!(msm.is_zero()); + assert!(msm.is_zero(¶ms)); break; } } diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index 43470cc..e5f897e 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -11,12 +11,12 @@ impl OpeningProof { pub fn verify<'a, H: Hasher>( &self, params: &'a Params, - msm: &mut MSM, + mut msm: MSM, transcript: &mut H, x: C::Scalar, p: &C, v: C::Scalar, - ) -> Result<(Vec, Guard<'a, C>), Error> { + ) -> Result<(Vec, Guard), Error> { // Check for well-formedness if self.rounds.len() != params.k as usize { return Err(Error::OpeningError); @@ -43,6 +43,7 @@ impl OpeningProof { let mut challenges_inv = Vec::with_capacity(self.rounds.len()); let mut challenges_sq = Vec::with_capacity(self.rounds.len()); let mut challenges_sq_packed: Vec = Vec::with_capacity(self.rounds.len()); + let mut allinv = C::Scalar::one(); for round in &self.rounds { // Feed L and R into the transcript. @@ -74,13 +75,14 @@ impl OpeningProof { return Err(Error::OpeningError); } let challenge_inv = challenge_inv.unwrap(); + allinv *= &challenge_inv; let challenge_sq_inv = challenge_inv.square(); - msm.scalars.push(challenge_sq); - msm.bases.push(round.0); - msm.scalars.push(challenge_sq_inv); - msm.bases.push(round.1); + msm.other_scalars.push(challenge_sq); + msm.other_bases.push(round.0); + msm.other_scalars.push(challenge_sq_inv); + msm.other_bases.push(round.1); challenges.push(challenge); challenges_inv.push(challenge_inv); @@ -106,7 +108,7 @@ 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 - for scalar in &mut msm.scalars { + for scalar in &mut msm.other_scalars { *scalar *= &c; } @@ -115,28 +117,25 @@ impl OpeningProof { let neg_z1 = -self.z1; // [c] P - msm.bases.push(*p); - msm.scalars.push(c); + msm.other_bases.push(*p); + msm.other_scalars.push(c); // [c * v] U - [z1 * b] U - msm.bases.push(u); - msm.scalars.push((c * &v) + &(neg_z1 * &b)); + msm.other_bases.push(u); + msm.other_scalars.push((c * &v) + &(neg_z1 * &b)); // delta - msm.bases.push(self.delta); - msm.scalars.push(Field::one()); + msm.other_bases.push(self.delta); + msm.other_scalars.push(Field::one()); - // - [z2] H - msm.bases.push(msm.h); - msm.scalars.push(-self.z2); + // z2 + msm.h_scalar = Some(-self.z2); - let guard = Guard::<'a, _> { - g: msm.g.clone(), - h: msm.h.clone(), + let guard = Guard { + msm, neg_z1, - params, - scalars: msm.scalars.clone(), - bases: msm.bases.clone(), + allinv, + challenges_sq, }; Ok((challenges_sq_packed, guard)) From 5f1cd6ced2805afa159aad131cd863b532087993 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sun, 13 Sep 2020 00:45:11 +0800 Subject: [PATCH 04/16] Only return Guard from OpeningProof.verify() --- src/plonk/verifier.rs | 4 ++-- src/poly/commitment.rs | 38 ++++++--------------------------- src/poly/commitment/verifier.rs | 5 +++-- 3 files changed, 12 insertions(+), 35 deletions(-) diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 1b303aa..0b83bfd 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -265,7 +265,7 @@ impl Proof { // Verify the opening proof let default_msm = MSM::default(¶ms); - let (challenges, guard) = self + let guard = self .opening .verify( params, @@ -277,7 +277,7 @@ impl Proof { ) .unwrap(); - let msm: &MSM = &guard.use_challenges(params, challenges).unwrap(); + let msm: &MSM = &guard.use_challenges(params).unwrap(); msm.is_zero(params) } diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 45fd0d8..be48817 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -5,8 +5,7 @@ use super::{Coeff, Error, LagrangeCoeff, Polynomial}; use crate::arithmetic::{ - best_fft, best_multiexp, get_challenge_scalar, parallelize, Challenge, Curve, CurveAffine, - Field, + best_fft, best_multiexp, parallelize, Challenge, Curve, CurveAffine, Field, }; use crate::transcript::Hasher; use std::ops::{Add, AddAssign, Mul, MulAssign}; @@ -249,25 +248,17 @@ impl Params { /// A guard returned by the verifier #[derive(Debug)] pub struct Guard { - /// MSM msm: MSM, - - /// Negation of z1 value in the OpeningProof neg_z1: C::Scalar, - allinv: C::Scalar, - challenges_sq: Vec, + challenges_sq_packed: Vec, } impl Guard { /// Lets caller supply the challenges and obtain an MSM with updated /// scalars and points. - pub fn use_challenges( - mut self, - params: &Params, - challenges_sq_packed: Vec, - ) -> Result, Error> { + pub fn use_challenges(mut self, params: &Params) -> Result, Error> { let mut scalars: Vec = vec![]; let mut bases: Vec = vec![]; @@ -281,22 +272,7 @@ impl Guard { } // - [z1] G - let mut allinv = C::Scalar::one(); - let mut challenges_sq = Vec::with_capacity(params.k as usize); - - for challenge_sq_packed in challenges_sq_packed.iter() { - let challenge_sq: C::Scalar = get_challenge_scalar(*challenge_sq_packed); - challenges_sq.push(challenge_sq); - - let challenge = challenge_sq.deterministic_sqrt(); - let challenge = challenge.unwrap(); - - let challenge_inv = challenge.invert(); - let challenge_inv = challenge_inv.unwrap(); - allinv *= &challenge_inv; - } - - let s = compute_s(&challenges_sq, allinv * &self.neg_z1); + let s = compute_s(&self.challenges_sq, self.allinv * &self.neg_z1); scalars.extend(&s); bases.extend(¶ms.g); @@ -308,7 +284,7 @@ impl Guard { /// Lets caller supply the purported G point and simply appends it to /// return an updated MSM. pub fn use_g(mut self, g: C) -> Result, Error> { - &self.msm.other_scalars.push(self.allinv * &self.neg_z1); + &self.msm.other_scalars.push(self.neg_z1); &self.msm.other_bases.push(g); Ok(self.msm) @@ -436,11 +412,11 @@ fn test_opening_proof() { let opening_proof = opening_proof.unwrap(); // Verify the opening proof let msm = MSM::default(¶ms); - let (challenges, guard) = opening_proof + let guard = opening_proof .verify(¶ms, msm, &mut transcript_dup, x, &p, v) .unwrap(); - let msm = guard.use_challenges(¶ms, challenges).unwrap(); + let msm = guard.use_challenges(¶ms).unwrap(); assert!(msm.is_zero(¶ms)); break; diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index e5f897e..f04647a 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -16,7 +16,7 @@ impl OpeningProof { x: C::Scalar, p: &C, v: C::Scalar, - ) -> Result<(Vec, Guard), Error> { + ) -> Result, Error> { // Check for well-formedness if self.rounds.len() != params.k as usize { return Err(Error::OpeningError); @@ -136,9 +136,10 @@ impl OpeningProof { neg_z1, allinv, challenges_sq, + challenges_sq_packed, }; - Ok((challenges_sq_packed, guard)) + Ok(guard) } } From 14d1f41e08c235bbe6e3d57843a87513a434fe79 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sun, 13 Sep 2020 02:55:48 +0800 Subject: [PATCH 05/16] Address review comments --- src/plonk/verifier.rs | 6 +- src/poly/commitment.rs | 132 ++++++++++++++------------------ src/poly/commitment/verifier.rs | 38 +++++---- 3 files changed, 85 insertions(+), 91 deletions(-) diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 0b83bfd..fe390c8 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -264,7 +264,7 @@ impl Proof { } // Verify the opening proof - let default_msm = MSM::default(¶ms); + let default_msm = params.msm(); let guard = self .opening .verify( @@ -277,8 +277,8 @@ impl Proof { ) .unwrap(); - let msm: &MSM = &guard.use_challenges(params).unwrap(); + let msm: &MSM = &guard.use_challenges(); - msm.is_zero(params) + msm.is_zero() } } diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index be48817..4ac51a3 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -3,7 +3,7 @@ //! //! [halo]: https://eprint.iacr.org/2019/1021 -use super::{Coeff, Error, LagrangeCoeff, Polynomial}; +use super::{Coeff, LagrangeCoeff, Polynomial}; use crate::arithmetic::{ best_fft, best_multiexp, parallelize, Challenge, Curve, CurveAffine, Field, }; @@ -25,36 +25,15 @@ pub struct OpeningProof { /// A multiscalar multiplication in the polynomial commitment scheme #[derive(Debug)] -pub struct MSM { - /// TODO: documentation - pub g_scalars: Option>, - - /// TODO: documentation - pub h_scalar: Option, - - /// TODO: documentation - pub other_scalars: Vec, - - /// TODO: documentation - pub other_bases: Vec, +pub struct MSM<'a, C: CurveAffine> { + params: &'a Params, + g_scalars: Option>, + h_scalar: Option, + other_scalars: Vec, + other_bases: Vec, } -impl<'a, C: CurveAffine> MSM { - /// Empty MSM - pub fn default(params: &Params) -> Self { - let g_scalars = Some(vec![C::Scalar::one(); params.n as usize]); - let h_scalar = Some(C::Scalar::one()); - let other_scalars: Vec = Vec::with_capacity(params.k as usize * 2 + 3); - let other_bases: Vec = Vec::with_capacity(params.k as usize * 2 + 3); - - MSM { - g_scalars, - h_scalar, - other_scalars, - other_bases, - } - } - +impl<'a, C: CurveAffine> MSM<'a, C> { /// Add arbitrary term (the scalar and the point) pub fn add_term(&mut self, scalar: C::Scalar, point: C) { &self.other_scalars.push(scalar); @@ -62,50 +41,54 @@ impl<'a, C: CurveAffine> MSM { } /// Add a vector of scalars to `g_scalars` - pub fn add_to_g(&mut self, scalars: Vec) { - for (g_scalar, scalar) in self - .g_scalars - .as_mut() - .unwrap() - .iter_mut() - .zip(scalars.iter()) - { - *g_scalar += &scalar; + pub fn add_to_g(&mut self, scalars: &[C::Scalar]) { + if let Some(g_scalars) = &mut self.g_scalars { + for (g_scalar, scalar) in g_scalars.iter_mut().zip(scalars.iter()) { + *g_scalar += &scalar; + } + } else { + self.g_scalars = Some(scalars.to_vec()); } } /// Add term to h pub fn add_to_h(&mut self, scalar: C::Scalar) { - self.h_scalar = Some(self.h_scalar.unwrap() + &scalar); + self.h_scalar = self.h_scalar.map_or(Some(scalar), |a| Some(a + &scalar)); } /// Scale all scalars in the MSM by a random blinding factor pub fn scale(&mut self, factor: C::Scalar) { - for g_scalar in self.g_scalars.as_mut().unwrap().iter_mut() { - *g_scalar *= &factor; + if let Some(g_scalars) = &mut self.g_scalars { + for g_scalar in g_scalars.iter_mut() { + *g_scalar *= &factor; + } } + for other_scalar in self.other_scalars.iter_mut() { *other_scalar *= &factor; } - self.h_scalar = Some(self.h_scalar.unwrap() * &factor); + self.h_scalar = self.h_scalar.map(|a| a * &factor); } /// Perform multiexp and check that it results in zero - pub fn is_zero(&self, params: &'a Params) -> bool { - let mut scalars: Vec = vec![]; - let mut bases: Vec = vec![]; + pub fn is_zero(&self) -> bool { + let len = self.g_scalars.as_ref().map(|v| v.len()).unwrap_or(0) + + self.h_scalar.map(|_| 1).unwrap_or(0) + + self.other_scalars.len(); + let mut scalars: Vec = Vec::with_capacity(len); + let mut bases: Vec = Vec::with_capacity(len); scalars.extend(&self.other_scalars); bases.extend(&self.other_bases); if let Some(h_scalar) = self.h_scalar { scalars.push(h_scalar); - bases.push(params.h); + bases.push(self.params.h); } if let Some(g_scalars) = &self.g_scalars { scalars.extend(g_scalars); - bases.extend(params.g.iter()); + bases.extend(self.params.g.iter()); } bool::from(best_multiexp(&scalars, &bases).is_zero()) @@ -243,51 +226,52 @@ impl Params { best_multiexp::(&tmp_scalars, &tmp_bases) } + + /// Generates an empty multiscalar multiplication struct using the + /// appropriate params. + pub fn msm(&self) -> MSM { + let g_scalars = None; + let h_scalar = None; + let other_scalars = vec![]; + let other_bases = vec![]; + + MSM { + params: &self, + g_scalars, + h_scalar, + other_scalars, + other_bases, + } + } } /// A guard returned by the verifier #[derive(Debug)] -pub struct Guard { - msm: MSM, +pub struct Guard<'a, C: CurveAffine> { + msm: MSM<'a, C>, neg_z1: C::Scalar, allinv: C::Scalar, challenges_sq: Vec, challenges_sq_packed: Vec, } -impl Guard { +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, params: &Params) -> Result, Error> { - let mut scalars: Vec = vec![]; - let mut bases: Vec = vec![]; - - scalars.extend(&self.msm.other_scalars); - bases.extend(&self.msm.other_bases); - - // - [z2] H - if let Some(h_scalar) = self.msm.h_scalar { - scalars.push(h_scalar); - bases.push(params.h); - } - - // - [z1] G + pub fn use_challenges(mut self) -> MSM<'a, C> { let s = compute_s(&self.challenges_sq, self.allinv * &self.neg_z1); - scalars.extend(&s); - bases.extend(¶ms.g); + self.msm.add_to_g(&s); - self.msm.g_scalars = Some(s); - - Ok(self.msm) + self.msm } /// Lets caller supply the purported G point and simply appends it to /// return an updated MSM. - pub fn use_g(mut self, g: C) -> Result, Error> { + pub fn use_g(mut self, g: C) -> MSM<'a, C> { &self.msm.other_scalars.push(self.neg_z1); &self.msm.other_bases.push(g); - Ok(self.msm) + self.msm } } @@ -411,14 +395,14 @@ fn test_opening_proof() { } else { let opening_proof = opening_proof.unwrap(); // Verify the opening proof - let msm = MSM::default(¶ms); + let msm = params.msm(); let guard = opening_proof .verify(¶ms, msm, &mut transcript_dup, x, &p, v) .unwrap(); - let msm = guard.use_challenges(¶ms).unwrap(); + let msm = guard.use_challenges(); - assert!(msm.is_zero(¶ms)); + assert!(msm.is_zero()); break; } } diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index f04647a..7038554 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -11,12 +11,12 @@ impl OpeningProof { pub fn verify<'a, H: Hasher>( &self, params: &'a Params, - mut msm: MSM, + mut msm: MSM<'a, C>, transcript: &mut H, x: C::Scalar, p: &C, v: C::Scalar, - ) -> Result, Error> { + ) -> Result, Error> { // Check for well-formedness if self.rounds.len() != params.k as usize { return Err(Error::OpeningError); @@ -38,6 +38,9 @@ impl OpeningProof { C::from_xy(u_x, u_y).unwrap() }; + let mut extra_scalars = Vec::with_capacity(self.rounds.len() * 2 + 4 + params.n as usize); + let mut extra_bases = Vec::with_capacity(self.rounds.len() * 2 + 4 + params.n as usize); + // Data about the challenges from each of the rounds. let mut challenges = Vec::with_capacity(self.rounds.len()); let mut challenges_inv = Vec::with_capacity(self.rounds.len()); @@ -79,10 +82,10 @@ impl OpeningProof { let challenge_sq_inv = challenge_inv.square(); - msm.other_scalars.push(challenge_sq); - msm.other_bases.push(round.0); - msm.other_scalars.push(challenge_sq_inv); - msm.other_bases.push(round.1); + extra_scalars.push(challenge_sq); + extra_bases.push(round.0); + extra_scalars.push(challenge_sq_inv); + extra_bases.push(round.1); challenges.push(challenge); challenges_inv.push(challenge_inv); @@ -108,28 +111,35 @@ 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 - for scalar in &mut msm.other_scalars { + // 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()); + + for scalar in &mut extra_scalars { *scalar *= &c; } + for (scalar, base) in extra_scalars.iter().zip(extra_bases.iter()) { + msm.add_term(*scalar, *base); + } + let b = compute_b(x, &challenges, &challenges_inv); let neg_z1 = -self.z1; // [c] P - msm.other_bases.push(*p); - msm.other_scalars.push(c); + msm.add_term(c, *p); // [c * v] U - [z1 * b] U - msm.other_bases.push(u); - msm.other_scalars.push((c * &v) + &(neg_z1 * &b)); + msm.add_term((c * &v) + &(neg_z1 * &b), u); // delta - msm.other_bases.push(self.delta); - msm.other_scalars.push(Field::one()); + msm.add_term(Field::one(), self.delta); // z2 - msm.h_scalar = Some(-self.z2); + msm.add_to_h(-self.z2); let guard = Guard { msm, From 0633086ac19fd2ed2cd0c4753969342ef202e2dc Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sun, 13 Sep 2020 11:33:44 +0800 Subject: [PATCH 06/16] Make MSM and Guard derive Clone --- src/poly/commitment.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 4ac51a3..7f96286 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -24,7 +24,7 @@ pub struct OpeningProof { } /// A multiscalar multiplication in the polynomial commitment scheme -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MSM<'a, C: CurveAffine> { params: &'a Params, g_scalars: Option>, @@ -246,7 +246,7 @@ impl Params { } /// A guard returned by the verifier -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Guard<'a, C: CurveAffine> { msm: MSM<'a, C>, neg_z1: C::Scalar, From ed8130b7bfe8c2e53ba32eaa3ad17af68e758649 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sun, 13 Sep 2020 11:37:10 +0800 Subject: [PATCH 07/16] Introduce Accumulator struct and return it in use_g() --- src/poly/commitment.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 7f96286..9917fbb 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -23,6 +23,16 @@ pub struct OpeningProof { z2: C::Scalar, } +/// TODO: documentation +#[derive(Debug, Clone)] +pub struct Accumulator { + /// TODO: documentation + pub g: C, + + /// TODO: documentation + pub challenges_sq_packed: Vec, +} + /// A multiscalar multiplication in the polynomial commitment scheme #[derive(Debug, Clone)] pub struct MSM<'a, C: CurveAffine> { @@ -267,11 +277,16 @@ impl<'a, C: CurveAffine> Guard<'a, C> { /// Lets caller supply the purported G point and simply appends it to /// return an updated MSM. - pub fn use_g(mut self, g: C) -> MSM<'a, C> { + pub fn use_g(mut self, g: C) -> (MSM<'a, C>, Accumulator) { &self.msm.other_scalars.push(self.neg_z1); &self.msm.other_bases.push(g); - self.msm + let accumulator = Accumulator { + g, + challenges_sq_packed: self.challenges_sq_packed, + }; + + (self.msm, accumulator) } } From 1a52d8f6b8cb64543ff560d8019a396387ba5f24 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sun, 13 Sep 2020 11:39:35 +0800 Subject: [PATCH 08/16] Add MSM to PLONK verifier signature --- src/plonk.rs | 3 ++- src/plonk/verifier.rs | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index b0dc869..b7c0120 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -345,6 +345,7 @@ fn test_proving() { let proof = Proof::create::, DummyHash, _>(¶ms, &srs, &circuit) .expect("proof generation should not fail"); - assert!(proof.verify::, DummyHash>(¶ms, &srs)); + let msm_default = params.msm(); + assert!(proof.verify::, DummyHash>(¶ms, &srs, msm_default)); } } diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index fe390c8..7ba9103 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -12,6 +12,7 @@ impl Proof { &self, params: &Params, srs: &SRS, + msm: MSM, ) -> bool { // Create a transcript for obtaining Fiat-Shamir challenges. let mut transcript = HBase::init(C::Base::one()); @@ -264,12 +265,11 @@ impl Proof { } // Verify the opening proof - let default_msm = params.msm(); let guard = self .opening .verify( params, - default_msm, + msm, &mut transcript, x_6, &f_commitment.to_affine(), @@ -277,8 +277,8 @@ impl Proof { ) .unwrap(); - let msm: &MSM = &guard.use_challenges(); + let msm_challenges = guard.use_challenges(); - msm.is_zero() + msm_challenges.is_zero() } } From 229747e118b06368eb1afaca33d6cb1702ac338c Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sun, 13 Sep 2020 11:47:21 +0800 Subject: [PATCH 09/16] Add compute_g() method on Guard and test use_g() --- src/poly/commitment.rs | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 9917fbb..0fbdc14 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -269,8 +269,8 @@ 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_z1); - self.msm.add_to_g(&s); + let g = self.compute_g(self.neg_z1); + self.msm.add_term(C::Scalar::one(), g); self.msm } @@ -278,8 +278,7 @@ impl<'a, C: CurveAffine> Guard<'a, C> { /// Lets caller supply the purported G point and simply appends it to /// return an updated MSM. pub fn use_g(mut self, g: C) -> (MSM<'a, C>, Accumulator) { - &self.msm.other_scalars.push(self.neg_z1); - &self.msm.other_bases.push(g); + &self.msm.add_term(self.neg_z1, g); let accumulator = Accumulator { g, @@ -288,6 +287,30 @@ impl<'a, C: CurveAffine> Guard<'a, C> { (self.msm, accumulator) } + + /// Computes the g value when given a potential scalar as input. + pub fn compute_g(&self, scalar: C::Scalar) -> C { + let s = compute_s(&self.challenges_sq, self.allinv * &scalar); + let mut g = C::Projective::zero(); + + if let Some(g_scalars) = &self.msm.g_scalars { + for ((g_scalar, g_base), s) in + g_scalars.iter().zip(self.msm.params.g.iter()).zip(s.iter()) + { + // g_base * (g_scalar + s) + let tmp = g_base.mul(*g_scalar + &s); + g = g.add(&tmp); + } + } else { + for (g_base, s) in self.msm.params.g.iter().zip(s.iter()) { + // g_base * (g_scalar + s) + let tmp = g_base.mul(*s); + g = g.add(&tmp); + } + } + + g.to_affine() + } } /// Wrapper type around a blinding factor. @@ -415,9 +438,16 @@ fn test_opening_proof() { .verify(¶ms, msm, &mut transcript_dup, x, &p, v) .unwrap(); - let msm = guard.use_challenges(); + // Test use_challenges() + let msm_challenges = guard.clone().use_challenges(); + assert!(msm_challenges.is_zero()); + + // Test use_g() + let g = guard.compute_g(Field::one()); + let (msm_g, _accumulator) = guard.clone().use_g(g); + + assert!(msm_g.is_zero()); - assert!(msm.is_zero()); break; } } From 417174898e0b7a42cb63bea6a8731f81c33df4b2 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sun, 13 Sep 2020 12:31:29 +0800 Subject: [PATCH 10/16] Update documentation --- src/poly/commitment.rs | 7 ++++--- src/poly/commitment/verifier.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 0fbdc14..650b17f 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -23,13 +23,14 @@ pub struct OpeningProof { z2: C::Scalar, } -/// TODO: documentation +/// An accumulator instance consisting of an evaluation claim and a proof. #[derive(Debug, Clone)] pub struct Accumulator { - /// TODO: documentation + /// The claimed output of the linear-time polycommit opening protocol pub g: C, - /// TODO: documentation + /// A vector of 128-bit challenges sampled by the verifier, to be used in + /// computing g. pub challenges_sq_packed: Vec, } diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index 7038554..2e937f9 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -138,7 +138,7 @@ impl OpeningProof { // delta msm.add_term(Field::one(), self.delta); - // z2 + // - [z2] H msm.add_to_h(-self.z2); let guard = Guard { From c264208a031b37ca7df89ab564d148e11c2c0eb7 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sun, 13 Sep 2020 23:07:05 +0800 Subject: [PATCH 11/16] Rename params.msm() to params.empty_msm() --- src/plonk.rs | 2 +- src/poly/commitment.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index b7c0120..7db922d 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -345,7 +345,7 @@ fn test_proving() { let proof = Proof::create::, DummyHash, _>(¶ms, &srs, &circuit) .expect("proof generation should not fail"); - let msm_default = params.msm(); + let msm_default = params.empty_msm(); assert!(proof.verify::, DummyHash>(¶ms, &srs, msm_default)); } } diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 650b17f..010f7f6 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -240,7 +240,7 @@ impl Params { /// Generates an empty multiscalar multiplication struct using the /// appropriate params. - pub fn msm(&self) -> MSM { + pub fn empty_msm(&self) -> MSM { let g_scalars = None; let h_scalar = None; let other_scalars = vec![]; @@ -434,7 +434,7 @@ fn test_opening_proof() { } else { let opening_proof = opening_proof.unwrap(); // Verify the opening proof - let msm = params.msm(); + let msm = params.empty_msm(); let guard = opening_proof .verify(¶ms, msm, &mut transcript_dup, x, &p, v) .unwrap(); From 1eb2a36086186af9d3b0e72ea7535bbacd9a23c6 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sun, 13 Sep 2020 23:10:06 +0800 Subject: [PATCH 12/16] Return MSM from PLONK verifier --- src/plonk.rs | 5 ++++- src/plonk/verifier.rs | 14 +++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index 7db922d..e62409f 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -346,6 +346,9 @@ fn test_proving() { .expect("proof generation should not fail"); let msm_default = params.empty_msm(); - assert!(proof.verify::, DummyHash>(¶ms, &srs, msm_default)); + let msm = proof + .verify::, DummyHash>(¶ms, &srs, msm_default) + .unwrap(); + assert!(msm.is_zero()) } } diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 7ba9103..5197acd 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -1,4 +1,4 @@ -use super::{hash_point, Proof, SRS}; +use super::{hash_point, Error, Proof, SRS}; use crate::arithmetic::{get_challenge_scalar, Challenge, Curve, CurveAffine, Field}; use crate::poly::{ commitment::{Params, MSM}, @@ -6,14 +6,14 @@ use crate::poly::{ }; use crate::transcript::Hasher; -impl Proof { +impl<'a, C: CurveAffine> Proof { /// Returns a boolean indicating whether or not the proof is valid pub fn verify, HScalar: Hasher>( &self, - params: &Params, + params: &'a Params, srs: &SRS, - msm: MSM, - ) -> bool { + msm: MSM<'a, C>, + ) -> Result, Error> { // Create a transcript for obtaining Fiat-Shamir challenges. let mut transcript = HBase::init(C::Base::one()); @@ -137,7 +137,7 @@ impl Proof { } if h_eval != (expected_h_eval * &(x_3n - &C::Scalar::one())) { - return false; + return Err(Error::ConstraintSystemFailure); } // We are now convinced the circuit is satisfied so long as the @@ -279,6 +279,6 @@ impl Proof { let msm_challenges = guard.use_challenges(); - msm_challenges.is_zero() + Ok(msm_challenges) } } From 19ee27e51afee89f03fea81961027eedcf163f7e Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sun, 13 Sep 2020 23:10:37 +0800 Subject: [PATCH 13/16] Fix bug in compute_g() --- src/poly/commitment.rs | 51 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 010f7f6..06306e4 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -51,8 +51,11 @@ impl<'a, C: CurveAffine> MSM<'a, C> { &self.other_bases.push(point); } - /// Add a vector of scalars to `g_scalars` + /// Add a vector of scalars to `g_scalars`. This function will panic if the + /// caller provides a slice of scalars that is not of length `params.n`. + // TODO: parallelize pub fn add_to_g(&mut self, scalars: &[C::Scalar]) { + assert_eq!(scalars.len(), self.params.n as usize); if let Some(g_scalars) = &mut self.g_scalars { for (g_scalar, scalar) in g_scalars.iter_mut().zip(scalars.iter()) { *g_scalar += &scalar; @@ -68,6 +71,7 @@ impl<'a, C: CurveAffine> MSM<'a, C> { } /// Scale all scalars in the MSM by a random blinding factor + // TODO: parallelize pub fn scale(&mut self, factor: C::Scalar) { if let Some(g_scalars) = &mut self.g_scalars { for g_scalar in g_scalars.iter_mut() { @@ -75,6 +79,7 @@ impl<'a, C: CurveAffine> MSM<'a, C> { } } + // TODO: parallelize for other_scalar in self.other_scalars.iter_mut() { *other_scalar *= &factor; } @@ -292,25 +297,7 @@ impl<'a, C: CurveAffine> Guard<'a, C> { /// Computes the g value when given a potential scalar as input. pub fn compute_g(&self, scalar: C::Scalar) -> C { let s = compute_s(&self.challenges_sq, self.allinv * &scalar); - let mut g = C::Projective::zero(); - - if let Some(g_scalars) = &self.msm.g_scalars { - for ((g_scalar, g_base), s) in - g_scalars.iter().zip(self.msm.params.g.iter()).zip(s.iter()) - { - // g_base * (g_scalar + s) - let tmp = g_base.mul(*g_scalar + &s); - g = g.add(&tmp); - } - } else { - for (g_base, s) in self.msm.params.g.iter().zip(s.iter()) { - // g_base * (g_scalar + s) - let tmp = g_base.mul(*s); - g = g.add(&tmp); - } - } - - g.to_affine() + best_multiexp(&s, &self.msm.params.g).to_affine() } } @@ -425,7 +412,7 @@ fn test_opening_proof() { transcript.absorb(Fp::from_bytes(&v.to_bytes()).unwrap()); // unlikely to fail since p ~ q loop { - let mut transcript_dup = transcript.clone(); + let transcript_dup = transcript.clone(); let opening_proof = OpeningProof::create(¶ms, &mut transcript, &px, blind, x); if opening_proof.is_err() { @@ -434,18 +421,30 @@ fn test_opening_proof() { } else { let opening_proof = opening_proof.unwrap(); // Verify the opening proof - let msm = params.empty_msm(); let guard = opening_proof - .verify(¶ms, msm, &mut transcript_dup, x, &p, v) + .verify( + ¶ms, + params.empty_msm(), + &mut transcript_dup.clone(), + x, + &p, + v, + ) + .unwrap(); + + // Generate a `new_guard` to populate `msm.g_scalars` + let msm = guard.use_challenges(); + let new_guard = opening_proof + .verify(¶ms, msm, &mut transcript_dup.clone(), x, &p, v) .unwrap(); // Test use_challenges() - let msm_challenges = guard.clone().use_challenges(); + let msm_challenges = new_guard.clone().use_challenges(); assert!(msm_challenges.is_zero()); // Test use_g() - let g = guard.compute_g(Field::one()); - let (msm_g, _accumulator) = guard.clone().use_g(g); + let g = new_guard.compute_g(Field::one()); + let (msm_g, _accumulator) = new_guard.clone().use_g(g); assert!(msm_g.is_zero()); From 221e9029f7ed4ab5c488895d346f3b6a5a60c2e8 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sun, 13 Sep 2020 10:14:32 -0600 Subject: [PATCH 14/16] Minor adjustments to MSM and Guard APIs. --- src/poly/commitment.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 06306e4..f5d3ef9 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -87,7 +87,7 @@ impl<'a, C: CurveAffine> MSM<'a, C> { } /// Perform multiexp and check that it results in zero - pub fn is_zero(&self) -> bool { + pub fn is_zero(self) -> bool { let len = self.g_scalars.as_ref().map(|v| v.len()).unwrap_or(0) + self.h_scalar.map(|_| 1).unwrap_or(0) + self.other_scalars.len(); @@ -107,6 +107,8 @@ impl<'a, C: CurveAffine> MSM<'a, C> { bases.extend(self.params.g.iter()); } + assert_eq!(scalars.len(), len); + bool::from(best_multiexp(&scalars, &bases).is_zero()) } } @@ -275,8 +277,8 @@ 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 g = self.compute_g(self.neg_z1); - self.msm.add_term(C::Scalar::one(), g); + let s = compute_s(&self.challenges_sq, self.allinv * &self.neg_z1); + self.msm.add_to_g(&s); self.msm } @@ -295,8 +297,8 @@ impl<'a, C: CurveAffine> Guard<'a, C> { } /// Computes the g value when given a potential scalar as input. - pub fn compute_g(&self, scalar: C::Scalar) -> C { - let s = compute_s(&self.challenges_sq, self.allinv * &scalar); + pub fn compute_g(&self) -> C { + let s = compute_s(&self.challenges_sq, self.allinv); best_multiexp(&s, &self.msm.params.g).to_affine() } } @@ -443,7 +445,7 @@ fn test_opening_proof() { assert!(msm_challenges.is_zero()); // Test use_g() - let g = new_guard.compute_g(Field::one()); + let g = new_guard.compute_g(); let (msm_g, _accumulator) = new_guard.clone().use_g(g); assert!(msm_g.is_zero()); From fd350a28a0240fa6be69276b913bde515e53dfa8 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sun, 13 Sep 2020 10:17:00 -0600 Subject: [PATCH 15/16] Minor adjustments to tests and documentation --- src/poly/commitment.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index f5d3ef9..3eddf7c 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -70,7 +70,7 @@ impl<'a, C: CurveAffine> MSM<'a, C> { self.h_scalar = self.h_scalar.map_or(Some(scalar), |a| Some(a + &scalar)); } - /// Scale all scalars in the MSM by a random blinding factor + /// Scale all scalars in the MSM by some scaling factor // TODO: parallelize pub fn scale(&mut self, factor: C::Scalar) { if let Some(g_scalars) = &mut self.g_scalars { @@ -434,19 +434,30 @@ fn test_opening_proof() { ) .unwrap(); - // Generate a `new_guard` to populate `msm.g_scalars` + // Test guard behavior prior to checking another proof + { + // Test use_challenges() + let msm_challenges = guard.clone().use_challenges(); + assert!(msm_challenges.is_zero()); + + // Test use_g() + let g = guard.compute_g(); + let (msm_g, _accumulator) = guard.clone().use_g(g); + } + + // Check another proof to populate `msm.g_scalars` let msm = guard.use_challenges(); - let new_guard = opening_proof + let guard = opening_proof .verify(¶ms, msm, &mut transcript_dup.clone(), x, &p, v) .unwrap(); // Test use_challenges() - let msm_challenges = new_guard.clone().use_challenges(); + let msm_challenges = guard.clone().use_challenges(); assert!(msm_challenges.is_zero()); // Test use_g() - let g = new_guard.compute_g(); - let (msm_g, _accumulator) = new_guard.clone().use_g(g); + let g = guard.compute_g(); + let (msm_g, _accumulator) = guard.clone().use_g(g); assert!(msm_g.is_zero()); From 5ec820f8fd0788101bab587c76e4cfa37d8d4e3e Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sun, 13 Sep 2020 10:23:04 -0600 Subject: [PATCH 16/16] Fix warning in test. --- src/poly/commitment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index 3eddf7c..eb24af9 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -443,6 +443,7 @@ fn test_opening_proof() { // Test use_g() let g = guard.compute_g(); let (msm_g, _accumulator) = guard.clone().use_g(g); + assert!(msm_g.is_zero()); } // Check another proof to populate `msm.g_scalars` @@ -458,7 +459,6 @@ fn test_opening_proof() { // Test use_g() let g = guard.compute_g(); let (msm_g, _accumulator) = guard.clone().use_g(g); - assert!(msm_g.is_zero()); break;