Add MSM and Guard structs in polycommit scheme

This commit is contained in:
therealyingtong 2020-09-09 21:00:36 +08:00
parent 549232234f
commit 5724706a09
No known key found for this signature in database
GPG Key ID: 179F32A1503D607E
5 changed files with 225 additions and 68 deletions

View File

@ -70,6 +70,10 @@ where
} }
/// This is a 128-bit verifier challenge. /// 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)] #[derive(Copy, Clone, Debug)]
pub struct Challenge(pub(crate) u128); pub struct Challenge(pub(crate) u128);

View File

@ -1,6 +1,9 @@
use super::{hash_point, Proof, SRS}; use super::{hash_point, Proof, SRS};
use crate::arithmetic::{get_challenge_scalar, Challenge, Curve, CurveAffine, Field}; 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; use crate::transcript::Hasher;
impl<C: CurveAffine> Proof<C> { impl<C: CurveAffine> Proof<C> {
@ -261,12 +264,20 @@ impl<C: CurveAffine> Proof<C> {
} }
// Verify the opening proof // Verify the opening proof
self.opening.verify( let (challenges, mut guard) = self
params, .opening
&mut transcript, .verify(
x_6, params,
&f_commitment.to_affine(), &mut MSM::default(&params),
f_eval, &mut transcript,
) x_6,
&f_commitment.to_affine(),
f_eval,
)
.unwrap();
let msm: MSM<C> = guard.use_challenges(challenges).unwrap();
msm.is_zero()
} }
} }

View File

@ -13,6 +13,14 @@ mod domain;
pub use 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. /// The basis over which a polynomial is described.
pub trait Basis: Clone + Debug + Send + Sync {} pub trait Basis: Clone + Debug + Send + Sync {}

View File

@ -3,8 +3,11 @@
//! //!
//! [halo]: https://eprint.iacr.org/2019/1021 //! [halo]: https://eprint.iacr.org/2019/1021
use super::{Coeff, LagrangeCoeff, Polynomial}; use super::{Coeff, Error, LagrangeCoeff, Polynomial};
use crate::arithmetic::{best_fft, best_multiexp, parallelize, Curve, CurveAffine, Field}; use crate::arithmetic::{
best_fft, best_multiexp, get_challenge_scalar, parallelize, Challenge, Curve, CurveAffine,
Field,
};
use crate::transcript::Hasher; use crate::transcript::Hasher;
use std::ops::{Add, AddAssign, Mul, MulAssign}; use std::ops::{Add, AddAssign, Mul, MulAssign};
@ -21,6 +24,49 @@ pub struct OpeningProof<C: CurveAffine> {
z2: C::Scalar, z2: C::Scalar,
} }
/// A multiscalar multiplication in the polynomial commitment scheme
#[derive(Debug)]
pub struct MSM<C: CurveAffine> {
/// Scalars in the multiscalar multiplication
pub scalars: Vec<C::Scalar>,
/// Points in the multiscalar multiplication
pub bases: Vec<C>,
}
impl<'a, C: CurveAffine> MSM<C> {
/// Empty MSM
pub fn default(params: &'a Params<C>) -> Self {
let scalars: Vec<C::Scalar> =
Vec::with_capacity(params.k as usize * 2 + 4 + params.n as usize);
let bases: Vec<C> = 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. /// These are the public parameters for the polynomial commitment scheme.
#[derive(Debug)] #[derive(Debug)]
pub struct Params<C: CurveAffine> { pub struct Params<C: CurveAffine> {
@ -154,6 +200,85 @@ impl<C: CurveAffine> Params<C> {
} }
} }
/// 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<C>,
/// Scalars produced by the verifier for multiscalar multiplication
pub scalars: Vec<C::Scalar>,
/// Points produced by the verifier for multiscalar multiplication
pub bases: Vec<C>,
}
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<Challenge>,
) -> Result<MSM<C>, 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<C::Scalar>) -> Result<MSM<C>, 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. /// Wrapper type around a blinding factor.
#[derive(Copy, Clone, Eq, PartialEq, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct Blind<F>(pub F); pub struct Blind<F>(pub F);
@ -273,8 +398,39 @@ fn test_opening_proof() {
transcript.absorb(Field::one()); transcript.absorb(Field::one());
} else { } else {
let opening_proof = opening_proof.unwrap(); let opening_proof = opening_proof.unwrap();
assert!(opening_proof.verify(&params, &mut transcript_dup, x, &p, v)); // Verify the opening proof
let (challenges, mut guard) = opening_proof
.verify(
&params,
&mut MSM::default(&params),
&mut transcript_dup,
x,
&p,
v,
)
.unwrap();
let msm = guard.use_challenges(challenges).unwrap();
assert!(msm.is_zero());
break; break;
} }
} }
} }
// TODO: parallelize
fn compute_s<F: Field>(challenges_sq: &[F], allinv: F) -> Vec<F> {
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
}

View File

@ -1,25 +1,25 @@
use super::{OpeningProof, Params}; use super::super::Error;
use super::{Guard, OpeningProof, Params, MSM};
use crate::transcript::Hasher; use crate::transcript::Hasher;
use crate::arithmetic::{ use crate::arithmetic::{get_challenge_scalar, Challenge, CurveAffine, Field};
best_multiexp, get_challenge_scalar, Challenge, Curve, CurveAffine, Field,
};
impl<C: CurveAffine> OpeningProof<C> { impl<C: CurveAffine> OpeningProof<C> {
/// Checks to see if an [`OpeningProof`] is valid given the current /// Checks to see if an [`OpeningProof`] is valid given the current
/// `transcript`, and a point `x` that the polynomial commitment `p` opens /// `transcript`, and a point `x` that the polynomial commitment `p` opens
/// purportedly to the value `v`. /// purportedly to the value `v`.
pub fn verify<H: Hasher<C::Base>>( pub fn verify<'a, H: Hasher<C::Base>>(
&self, &self,
params: &Params<C>, params: &'a Params<C>,
msm: &mut MSM<C>,
transcript: &mut H, transcript: &mut H,
x: C::Scalar, x: C::Scalar,
p: &C, p: &C,
v: C::Scalar, v: C::Scalar,
) -> bool { ) -> Result<(Vec<Challenge>, Guard<'a, C>), Error> {
// Check for well-formedness // Check for well-formedness
if self.rounds.len() != params.k as usize { if self.rounds.len() != params.k as usize {
return false; return Err(Error::OpeningError);
} }
transcript.absorb(C::Base::from_u64(self.fork as u64)); transcript.absorb(C::Base::from_u64(self.fork as u64));
@ -31,28 +31,25 @@ impl<C: CurveAffine> OpeningProof<C> {
let u_y2 = u_x.square() * &u_x + &C::b(); let u_y2 = u_x.square() * &u_x + &C::b();
let u_y = u_y2.deterministic_sqrt(); let u_y = u_y2.deterministic_sqrt();
if u_y.is_none() { if u_y.is_none() {
return false; return Err(Error::OpeningError);
} }
let u_y = u_y.unwrap(); let u_y = u_y.unwrap();
C::from_xy(u_x, 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. // Data about the challenges from each of the rounds.
let mut challenges = Vec::with_capacity(self.rounds.len()); let mut challenges = Vec::with_capacity(self.rounds.len());
let mut challenges_inv = 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 challenges_sq = Vec::with_capacity(self.rounds.len());
let mut allinv = Field::one(); let mut challenges_sq_packed: Vec<Challenge> = Vec::with_capacity(self.rounds.len());
for round in &self.rounds { for round in &self.rounds {
// Feed L and R into the transcript. // Feed L and R into the transcript.
let l = round.0.get_xy(); let l = round.0.get_xy();
let r = round.1.get_xy(); let r = round.1.get_xy();
if bool::from(l.is_none() | r.is_none()) { if bool::from(l.is_none() | r.is_none()) {
return false; return Err(Error::OpeningError);
} }
let l = l.unwrap(); let l = l.unwrap();
let r = r.unwrap(); let r = r.unwrap();
@ -66,7 +63,7 @@ impl<C: CurveAffine> OpeningProof<C> {
let challenge = challenge_sq.deterministic_sqrt(); let challenge = challenge_sq.deterministic_sqrt();
if challenge.is_none() { if challenge.is_none() {
// We didn't sample a square. // We didn't sample a square.
return false; return Err(Error::OpeningError);
} }
let challenge = challenge.unwrap(); let challenge = challenge.unwrap();
@ -74,26 +71,26 @@ impl<C: CurveAffine> OpeningProof<C> {
if bool::from(challenge_inv.is_none()) { if bool::from(challenge_inv.is_none()) {
// We sampled zero for some reason, unlikely to happen by // We sampled zero for some reason, unlikely to happen by
// chance. // chance.
return false; return Err(Error::OpeningError);
} }
let challenge_inv = challenge_inv.unwrap(); let challenge_inv = challenge_inv.unwrap();
allinv *= challenge_inv;
let challenge_sq_inv = challenge_inv.square(); let challenge_sq_inv = challenge_inv.square();
extra_scalars.push(challenge_sq); msm.scalars.push(challenge_sq);
extra_bases.push(round.0); msm.bases.push(round.0);
extra_scalars.push(challenge_sq_inv); msm.scalars.push(challenge_sq_inv);
extra_bases.push(round.1); msm.bases.push(round.1);
challenges.push(challenge); challenges.push(challenge);
challenges_inv.push(challenge_inv); challenges_inv.push(challenge_inv);
challenges_sq.push(challenge_sq); challenges_sq.push(challenge_sq);
challenges_sq_packed.push(Challenge(challenge_sq_packed));
} }
let delta = self.delta.get_xy(); let delta = self.delta.get_xy();
if bool::from(delta.is_none()) { if bool::from(delta.is_none()) {
return false; return Err(Error::OpeningError);
} }
let delta = delta.unwrap(); let delta = delta.unwrap();
@ -109,7 +106,7 @@ impl<C: CurveAffine> OpeningProof<C> {
// [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 // [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 // = 0
for scalar in &mut extra_scalars { for scalar in &mut msm.scalars {
*scalar *= &c; *scalar *= &c;
} }
@ -118,31 +115,29 @@ impl<C: CurveAffine> OpeningProof<C> {
let neg_z1 = -self.z1; let neg_z1 = -self.z1;
// [c] P // [c] P
extra_bases.push(*p); msm.bases.push(*p);
extra_scalars.push(c); msm.scalars.push(c);
// [c * v] U - [z1 * b] U // [c * v] U - [z1 * b] U
extra_bases.push(u); msm.bases.push(u);
extra_scalars.push((c * &v) + &(neg_z1 * &b)); msm.scalars.push((c * &v) + &(neg_z1 * &b));
// delta // delta
extra_bases.push(self.delta); msm.bases.push(self.delta);
extra_scalars.push(Field::one()); msm.scalars.push(Field::one());
// - [z2] H // - [z2] H
extra_bases.push(params.h); msm.bases.push(params.h);
extra_scalars.push(-self.z2); msm.scalars.push(-self.z2);
// - [z1] G let guard = Guard::<'a, _> {
extra_bases.extend(&params.g); neg_z1,
let mut s = compute_s(&challenges_sq, allinv); params,
// TODO: parallelize scalars: msm.scalars.clone(),
for s in &mut s { bases: msm.bases.clone(),
*s *= &neg_z1; };
}
extra_scalars.extend(s);
bool::from(best_multiexp(&extra_scalars, &extra_bases).is_zero()) Ok((challenges_sq_packed, guard))
} }
} }
@ -160,20 +155,3 @@ fn compute_b<F: Field>(x: F, challenges: &[F], challenges_inv: &[F]) -> F {
) )
} }
} }
// TODO: parallelize
fn compute_s<F: Field>(challenges_sq: &[F], allinv: F) -> Vec<F> {
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
}