mirror of https://github.com/zcash/halo2.git
Modification of the polynomial commitment scheme to compensate for Transcript API changes.
This commit is contained in:
parent
fb232ddec0
commit
d30c6b62e4
|
@ -5,9 +5,10 @@
|
|||
|
||||
use super::{Coeff, LagrangeCoeff, Polynomial};
|
||||
use crate::arithmetic::{best_fft, best_multiexp, parallelize, Curve, CurveAffine, FieldExt};
|
||||
use crate::transcript::Hasher;
|
||||
use crate::transcript::{Transcript, TranscriptWrite};
|
||||
|
||||
use ff::{Field, PrimeField};
|
||||
use std::io;
|
||||
use std::ops::{Add, AddAssign, Mul, MulAssign};
|
||||
|
||||
mod msm;
|
||||
|
@ -15,7 +16,8 @@ mod prover;
|
|||
mod verifier;
|
||||
|
||||
pub use msm::MSM;
|
||||
pub use verifier::{Accumulator, Guard};
|
||||
pub use prover::create_proof;
|
||||
pub use verifier::{verify_proof, Accumulator, Guard};
|
||||
|
||||
/// These are the public parameters for the polynomial commitment scheme.
|
||||
#[derive(Debug)]
|
||||
|
@ -25,21 +27,13 @@ pub struct Params<C: CurveAffine> {
|
|||
pub(crate) g: Vec<C>,
|
||||
pub(crate) g_lagrange: Vec<C>,
|
||||
pub(crate) h: C,
|
||||
}
|
||||
|
||||
/// This is a proof object for the polynomial commitment scheme opening.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Proof<C: CurveAffine> {
|
||||
rounds: Vec<(C, C)>,
|
||||
delta: C,
|
||||
z1: C::Scalar,
|
||||
z2: C::Scalar,
|
||||
pub(crate) u: C,
|
||||
}
|
||||
|
||||
impl<C: CurveAffine> Params<C> {
|
||||
/// Initializes parameters for the curve, given a random oracle to draw
|
||||
/// points from.
|
||||
pub fn new<H: Hasher<C::Base>>(k: u32) -> Self {
|
||||
pub fn new<T: TranscriptWrite<io::Sink, C> + Sync>(k: u32) -> Self {
|
||||
// This is usually a limitation on the curve, but we also want 32-bit
|
||||
// architectures to be supported.
|
||||
assert!(k < 32);
|
||||
|
@ -49,19 +43,20 @@ impl<C: CurveAffine> Params<C> {
|
|||
let n: u64 = 1 << k;
|
||||
|
||||
let g = {
|
||||
let hasher = &H::init(C::Base::zero());
|
||||
let hasher = &T::init(io::sink(), C::Base::one());
|
||||
|
||||
let mut g = Vec::with_capacity(n as usize);
|
||||
g.resize(n as usize, C::zero());
|
||||
|
||||
parallelize(&mut g, move |g, start| {
|
||||
let mut cur_value = C::Base::from(start as u64);
|
||||
let mut cur_value = C::Scalar::from(start as u64);
|
||||
for g in g.iter_mut() {
|
||||
let mut hasher = hasher.clone();
|
||||
hasher.absorb(cur_value);
|
||||
cur_value += &C::Base::one();
|
||||
let mut hasher = hasher.fork();
|
||||
hasher.write_scalar(cur_value).unwrap();
|
||||
cur_value += &C::Scalar::one();
|
||||
loop {
|
||||
let x = hasher.squeeze().to_bytes();
|
||||
let x: C::Base = hasher.squeeze_challenge();
|
||||
let x = x.to_bytes();
|
||||
let p = C::from_bytes(&x);
|
||||
if bool::from(p.is_some()) {
|
||||
*g = p.unwrap();
|
||||
|
@ -102,8 +97,15 @@ impl<C: CurveAffine> Params<C> {
|
|||
};
|
||||
|
||||
let h = {
|
||||
let mut hasher = H::init(C::Base::zero());
|
||||
let x = hasher.squeeze().to_bytes();
|
||||
let mut hasher = T::init(io::sink(), C::Base::from_u64(2));
|
||||
let x = hasher.squeeze_challenge().to_bytes();
|
||||
let p = C::from_bytes(&x);
|
||||
p.unwrap()
|
||||
};
|
||||
|
||||
let u = {
|
||||
let mut hasher = T::init(io::sink(), C::Base::from_u64(3));
|
||||
let x = hasher.squeeze_challenge().to_bytes();
|
||||
let p = C::from_bytes(&x);
|
||||
p.unwrap()
|
||||
};
|
||||
|
@ -114,6 +116,7 @@ impl<C: CurveAffine> Params<C> {
|
|||
g,
|
||||
g_lagrange,
|
||||
h,
|
||||
u,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,12 +225,12 @@ impl<F: FieldExt> MulAssign<F> for Blind<F> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_commit_lagrange() {
|
||||
fn test_commit_lagrange_epaffine() {
|
||||
const K: u32 = 6;
|
||||
|
||||
use crate::pasta::{EpAffine, Fp, Fq};
|
||||
use crate::transcript::DummyHash;
|
||||
let params = Params::<EpAffine>::new::<DummyHash<Fp>>(K);
|
||||
use crate::pasta::{EpAffine, Fq};
|
||||
use crate::transcript::DummyHashWriter;
|
||||
let params = Params::<EpAffine>::new::<DummyHashWriter<std::io::Sink, _>>(K);
|
||||
let domain = super::EvaluationDomain::new(1, K);
|
||||
|
||||
let mut a = domain.empty_lagrange();
|
||||
|
@ -243,6 +246,28 @@ fn test_commit_lagrange() {
|
|||
assert_eq!(params.commit(&b, alpha), params.commit_lagrange(&a, alpha));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_commit_lagrange_eqaffine() {
|
||||
const K: u32 = 6;
|
||||
|
||||
use crate::pasta::{EqAffine, Fp};
|
||||
use crate::transcript::DummyHashWriter;
|
||||
let params = Params::<EqAffine>::new::<DummyHashWriter<std::io::Sink, _>>(K);
|
||||
let domain = super::EvaluationDomain::new(1, K);
|
||||
|
||||
let mut a = domain.empty_lagrange();
|
||||
|
||||
for (i, a) in a.iter_mut().enumerate() {
|
||||
*a = Fp::from(i as u64);
|
||||
}
|
||||
|
||||
let b = domain.lagrange_to_coeff(a.clone());
|
||||
|
||||
let alpha = Blind(Fp::rand());
|
||||
|
||||
assert_eq!(params.commit(&b, alpha), params.commit_lagrange(&a, alpha));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opening_proof() {
|
||||
const K: u32 = 6;
|
||||
|
@ -254,10 +279,13 @@ fn test_opening_proof() {
|
|||
EvaluationDomain,
|
||||
};
|
||||
use crate::arithmetic::{eval_polynomial, Curve, FieldExt};
|
||||
use crate::pasta::{EpAffine, Fp, Fq};
|
||||
use crate::transcript::{ChallengeScalar, DummyHash, Transcript};
|
||||
use crate::pasta::{EpAffine, Fq};
|
||||
use crate::transcript::{
|
||||
ChallengeScalar, DummyHashReader, DummyHashWriter, Transcript, TranscriptRead,
|
||||
TranscriptWrite,
|
||||
};
|
||||
|
||||
let params = Params::<EpAffine>::new::<DummyHash<Fp>>(K);
|
||||
let params = Params::<EpAffine>::new::<DummyHashWriter<std::io::Sink, _>>(K);
|
||||
let domain = EvaluationDomain::new(1, K);
|
||||
|
||||
let mut px = domain.empty_coeff();
|
||||
|
@ -270,32 +298,41 @@ fn test_opening_proof() {
|
|||
|
||||
let p = params.commit(&px, blind).to_affine();
|
||||
|
||||
let mut transcript = Transcript::<_, DummyHash<_>, DummyHash<_>>::new();
|
||||
transcript.absorb_point(&p).unwrap();
|
||||
let mut transcript = DummyHashWriter::<Vec<u8>, EpAffine>::init(vec![], Field::zero());
|
||||
transcript.write_point(p).unwrap();
|
||||
let x = ChallengeScalar::<_, ()>::get(&mut transcript);
|
||||
// Evaluate the polynomial
|
||||
let v = eval_polynomial(&px, *x);
|
||||
transcript.write_scalar(v).unwrap();
|
||||
|
||||
transcript.absorb_base(Fp::from_bytes(&v.to_bytes()).unwrap()); // unlikely to fail since p ~ q
|
||||
let (proof, ch_prover) = {
|
||||
create_proof(¶ms, &mut transcript, &px, blind, *x).unwrap();
|
||||
let ch_prover = transcript.squeeze_challenge();
|
||||
(transcript.finalize(), ch_prover)
|
||||
};
|
||||
|
||||
loop {
|
||||
let mut transcript_dup = transcript.clone();
|
||||
|
||||
let opening_proof = Proof::create(¶ms, &mut transcript, &px, blind, *x);
|
||||
if let Ok(opening_proof) = opening_proof {
|
||||
// Verify the opening proof
|
||||
let mut transcript = DummyHashReader::<&[u8], EpAffine>::init(&proof[..], Field::zero());
|
||||
let p_prime = transcript.read_point().unwrap();
|
||||
assert_eq!(p, p_prime);
|
||||
let x_prime = ChallengeScalar::<_, ()>::get(&mut transcript);
|
||||
assert_eq!(*x, *x_prime);
|
||||
let v_prime = transcript.read_scalar().unwrap();
|
||||
assert_eq!(v, v_prime);
|
||||
|
||||
let mut commitment_msm = params.empty_msm();
|
||||
commitment_msm.append_term(Field::one(), p);
|
||||
let guard = opening_proof
|
||||
.verify(
|
||||
let guard = verify_proof(
|
||||
¶ms,
|
||||
params.empty_msm(),
|
||||
&mut transcript_dup,
|
||||
&mut transcript,
|
||||
*x,
|
||||
commitment_msm,
|
||||
v,
|
||||
)
|
||||
.unwrap();
|
||||
let ch_verifier = transcript.squeeze_challenge();
|
||||
assert_eq!(ch_prover, ch_verifier);
|
||||
|
||||
// Test guard behavior prior to checking another proof
|
||||
{
|
||||
|
@ -307,12 +344,5 @@ fn test_opening_proof() {
|
|||
let g = guard.compute_g();
|
||||
let (msm_g, _accumulator) = guard.clone().use_g(g);
|
||||
assert!(msm_g.eval());
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
transcript = transcript_dup;
|
||||
transcript.absorb_base(Field::one());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use super::Params;
|
||||
use crate::arithmetic::{best_multiexp, parallelize, Curve, CurveAffine};
|
||||
use ff::Field;
|
||||
|
||||
/// A multiscalar multiplication in the polynomial commitment scheme
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -7,6 +8,7 @@ pub struct MSM<'a, C: CurveAffine> {
|
|||
pub(crate) params: &'a Params<C>,
|
||||
g_scalars: Option<Vec<C::Scalar>>,
|
||||
h_scalar: Option<C::Scalar>,
|
||||
u_scalar: Option<C::Scalar>,
|
||||
other_scalars: Vec<C::Scalar>,
|
||||
other_bases: Vec<C>,
|
||||
}
|
||||
|
@ -16,6 +18,7 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
|
|||
pub fn new(params: &'a Params<C>) -> Self {
|
||||
let g_scalars = None;
|
||||
let h_scalar = None;
|
||||
let u_scalar = None;
|
||||
let other_scalars = vec![];
|
||||
let other_bases = vec![];
|
||||
|
||||
|
@ -23,6 +26,7 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
|
|||
params,
|
||||
g_scalars,
|
||||
h_scalar,
|
||||
u_scalar,
|
||||
other_scalars,
|
||||
other_bases,
|
||||
}
|
||||
|
@ -40,6 +44,10 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
|
|||
if let Some(h_scalar) = &other.h_scalar {
|
||||
self.add_to_h_scalar(*h_scalar);
|
||||
}
|
||||
|
||||
if let Some(u_scalar) = &other.u_scalar {
|
||||
self.add_to_u_scalar(*u_scalar);
|
||||
}
|
||||
}
|
||||
|
||||
/// Add arbitrary term (the scalar and the point)
|
||||
|
@ -48,6 +56,17 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
|
|||
self.other_bases.push(point);
|
||||
}
|
||||
|
||||
/// Add a value to the first entry of `g_scalars`.
|
||||
pub fn add_constant_term(&mut self, constant: C::Scalar) {
|
||||
if let Some(g_scalars) = self.g_scalars.as_mut() {
|
||||
g_scalars[0] += &constant;
|
||||
} else {
|
||||
let mut g_scalars = vec![C::Scalar::zero(); self.params.n as usize];
|
||||
g_scalars[0] += &constant;
|
||||
self.g_scalars = Some(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`.
|
||||
pub fn add_to_g_scalars(&mut self, scalars: &[C::Scalar]) {
|
||||
|
@ -68,6 +87,11 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
|
|||
self.h_scalar = self.h_scalar.map_or(Some(scalar), |a| Some(a + &scalar));
|
||||
}
|
||||
|
||||
/// Add to `u_scalar`
|
||||
pub fn add_to_u_scalar(&mut self, scalar: C::Scalar) {
|
||||
self.u_scalar = self.u_scalar.map_or(Some(scalar), |a| Some(a + &scalar));
|
||||
}
|
||||
|
||||
/// Scale all scalars in the MSM by some scaling factor
|
||||
pub fn scale(&mut self, factor: C::Scalar) {
|
||||
if let Some(g_scalars) = &mut self.g_scalars {
|
||||
|
@ -87,12 +111,14 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
|
|||
}
|
||||
|
||||
self.h_scalar = self.h_scalar.map(|a| a * &factor);
|
||||
self.u_scalar = self.u_scalar.map(|a| a * &factor);
|
||||
}
|
||||
|
||||
/// Perform multiexp and check that it results in zero
|
||||
pub fn eval(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.u_scalar.map(|_| 1).unwrap_or(0)
|
||||
+ self.other_scalars.len();
|
||||
let mut scalars: Vec<C::Scalar> = Vec::with_capacity(len);
|
||||
let mut bases: Vec<C> = Vec::with_capacity(len);
|
||||
|
@ -105,6 +131,11 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
|
|||
bases.push(self.params.h);
|
||||
}
|
||||
|
||||
if let Some(u_scalar) = self.u_scalar {
|
||||
scalars.push(u_scalar);
|
||||
bases.push(self.params.u);
|
||||
}
|
||||
|
||||
if let Some(g_scalars) = &self.g_scalars {
|
||||
scalars.extend(g_scalars);
|
||||
bases.extend(self.params.g.iter());
|
||||
|
|
|
@ -1,57 +1,76 @@
|
|||
use ff::Field;
|
||||
|
||||
use super::super::{Coeff, Error, Polynomial};
|
||||
use super::{Blind, Params, Proof};
|
||||
use super::super::{Coeff, Polynomial};
|
||||
use super::{Blind, Params};
|
||||
use crate::arithmetic::{
|
||||
best_multiexp, compute_inner_product, parallelize, small_multiexp, Curve, CurveAffine, FieldExt,
|
||||
best_multiexp, compute_inner_product, eval_polynomial, parallelize, small_multiexp, Curve,
|
||||
CurveAffine, FieldExt,
|
||||
};
|
||||
use crate::transcript::{Challenge, ChallengeScalar, Hasher, Transcript};
|
||||
use crate::transcript::{Challenge, ChallengeScalar, TranscriptWrite};
|
||||
use std::io::{self, Write};
|
||||
|
||||
impl<C: CurveAffine> Proof<C> {
|
||||
/// Create a polynomial commitment opening proof for the polynomial defined
|
||||
/// by the coefficients `px`, the blinding factor `blind` used for the
|
||||
/// polynomial commitment, and the point `x` that the polynomial is
|
||||
/// evaluated at.
|
||||
///
|
||||
/// This function will panic if the provided polynomial is too large with
|
||||
/// respect to the polynomial commitment parameters.
|
||||
///
|
||||
/// **Important:** This function assumes that the provided `transcript` has
|
||||
/// already seen the common inputs: the polynomial commitment P, the claimed
|
||||
/// 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<HBase, HScalar>(
|
||||
/// Create a polynomial commitment opening proof for the polynomial defined
|
||||
/// by the coefficients `px`, the blinding factor `blind` used for the
|
||||
/// polynomial commitment, and the point `x` that the polynomial is
|
||||
/// evaluated at.
|
||||
///
|
||||
/// This function will panic if the provided polynomial is too large with
|
||||
/// respect to the polynomial commitment parameters.
|
||||
///
|
||||
/// **Important:** This function assumes that the provided `transcript` has
|
||||
/// already seen the common inputs: the polynomial commitment P, the claimed
|
||||
/// 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_proof<C: CurveAffine, W: Write, T: TranscriptWrite<W, C>>(
|
||||
params: &Params<C>,
|
||||
transcript: &mut Transcript<C, HBase, HScalar>,
|
||||
transcript: &mut T,
|
||||
px: &Polynomial<C::Scalar, Coeff>,
|
||||
blind: Blind<C::Scalar>,
|
||||
x: C::Scalar,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
HBase: Hasher<C::Base>,
|
||||
HScalar: Hasher<C::Scalar>,
|
||||
{
|
||||
let mut blind = blind.0;
|
||||
|
||||
) -> io::Result<()> {
|
||||
// We're limited to polynomials of degree n - 1.
|
||||
assert!(px.len() <= params.n as usize);
|
||||
|
||||
// Compute U
|
||||
let u = {
|
||||
let u_x = transcript.squeeze();
|
||||
// y^2 = x^3 + B
|
||||
let u_y2 = u_x.square() * &u_x + &C::b();
|
||||
if let Some(u_y) = u_y2.deterministic_sqrt() {
|
||||
C::from_xy(u_x, u_y).unwrap()
|
||||
} else {
|
||||
return Err(Error::SamplingError);
|
||||
// Sample a random polynomial (of same degree) that has a root at x, first
|
||||
// by setting all coefficients to random values.
|
||||
let mut s_poly = (*px).clone();
|
||||
for coeff in s_poly.iter_mut() {
|
||||
*coeff = C::Scalar::rand();
|
||||
}
|
||||
};
|
||||
// Evaluate the random polynomial at x
|
||||
let v_prime = eval_polynomial(&s_poly[..], x);
|
||||
// Subtract constant coefficient to get a random polynomial with a root at x
|
||||
s_poly[0] = s_poly[0] - &v_prime;
|
||||
// And sample a random blind
|
||||
let s_poly_blind = Blind(C::Scalar::rand());
|
||||
|
||||
// Write a commitment to the random polynomial to the transcript
|
||||
let s_poly_commitment = params.commit(&s_poly, s_poly_blind).to_affine();
|
||||
transcript.write_point(s_poly_commitment)?;
|
||||
|
||||
// Challenge that will ensure that the prover cannot change P but can only
|
||||
// witness a random polynomial commitment that agrees with P at x, with high
|
||||
// probability.
|
||||
let iota = *ChallengeScalar::<C, ()>::get(transcript);
|
||||
|
||||
// Challenge that ensures that the prover did not interfere with the U term
|
||||
// in their commitments.
|
||||
let z = *ChallengeScalar::<C, ()>::get(transcript);
|
||||
|
||||
// We'll be opening `s_poly_commitment * iota + P - [v] G_0` to ensure it
|
||||
// has a root at zero.
|
||||
let mut final_poly = s_poly * iota + px;
|
||||
let v = eval_polynomial(&final_poly, x);
|
||||
final_poly[0] = final_poly[0] - &v;
|
||||
drop(px);
|
||||
let blind = s_poly_blind * Blind(iota) + blind;
|
||||
let mut blind = blind.0;
|
||||
drop(s_poly_blind);
|
||||
|
||||
// Initialize the vector `a` as the coefficients of the polynomial,
|
||||
// rounding up to the parameters.
|
||||
let mut a = px.to_vec();
|
||||
let mut a = final_poly.values;
|
||||
a.resize(params.n as usize, C::Scalar::zero());
|
||||
|
||||
// Initialize the vector `b` as the powers of `x`. The inner product of
|
||||
|
@ -71,7 +90,6 @@ impl<C: CurveAffine> Proof<C> {
|
|||
let mut g = params.g.clone();
|
||||
|
||||
// Perform the inner product argument, round by round.
|
||||
let mut rounds = Vec::with_capacity(params.k as usize);
|
||||
for k in (1..=params.k).rev() {
|
||||
let half = 1 << (k - 1); // half the length of `a`, `b`, `G`
|
||||
|
||||
|
@ -87,28 +105,24 @@ impl<C: CurveAffine> Proof<C> {
|
|||
let mut 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, l_randomness], &[u, params.h]);
|
||||
let r = r + &best_multiexp(&[value_r, r_randomness], &[u, params.h]);
|
||||
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 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.clone();
|
||||
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
|
||||
.absorb_point(&l)
|
||||
.map_err(|_| Error::SamplingError)?;
|
||||
transcript
|
||||
.absorb_point(&r)
|
||||
.map_err(|_| Error::SamplingError)?;
|
||||
transcript.write_point(l)?;
|
||||
transcript.write_point(r)?;
|
||||
|
||||
// ... and get the squared challenge.
|
||||
let challenge_sq_packed = Challenge::get(&mut transcript);
|
||||
let challenge_sq: C::Scalar = *ChallengeScalar::<_, ()>::from(challenge_sq_packed);
|
||||
let challenge_sq = *ChallengeScalar::<C, ()>::from(challenge_sq_packed);
|
||||
|
||||
// There might be no square root, in which case we'll fork the
|
||||
// transcript.
|
||||
|
@ -128,12 +142,8 @@ impl<C: CurveAffine> Proof<C> {
|
|||
let challenge_sq = challenge.square();
|
||||
|
||||
// Feed L and R into the real transcript
|
||||
transcript
|
||||
.absorb_point(&l)
|
||||
.map_err(|_| Error::SamplingError)?;
|
||||
transcript
|
||||
.absorb_point(&r)
|
||||
.map_err(|_| Error::SamplingError)?;
|
||||
transcript.write_point(l)?;
|
||||
transcript.write_point(r)?;
|
||||
|
||||
// And obtain the challenge, even though we already have it, since
|
||||
// squeezing affects the transcript.
|
||||
|
@ -142,9 +152,6 @@ impl<C: CurveAffine> Proof<C> {
|
|||
assert_eq!(challenge_sq, *challenge_sq_expected);
|
||||
}
|
||||
|
||||
// Done with this round.
|
||||
rounds.push((l, r));
|
||||
|
||||
// Collapse `a` and `b`.
|
||||
// TODO: parallelize
|
||||
for i in 0..half {
|
||||
|
@ -166,37 +173,11 @@ impl<C: CurveAffine> Proof<C> {
|
|||
// We have fully collapsed `a`, `b`, `G`
|
||||
assert_eq!(a.len(), 1);
|
||||
let a = a[0];
|
||||
assert_eq!(b.len(), 1);
|
||||
let b = b[0];
|
||||
assert_eq!(g.len(), 1);
|
||||
let g = g[0];
|
||||
|
||||
// Random nonces for the zero-knowledge opening
|
||||
let d = C::Scalar::rand();
|
||||
let s = C::Scalar::rand();
|
||||
transcript.write_scalar(a)?;
|
||||
transcript.write_scalar(blind)?;
|
||||
|
||||
metrics::increment_counter!("multiexp", "val" => "delta", "size" => "3");
|
||||
let delta = best_multiexp(&[d, d * &b, s], &[g, u, params.h]).to_affine();
|
||||
|
||||
// Feed delta into the transcript
|
||||
transcript
|
||||
.absorb_point(&delta)
|
||||
.map_err(|_| Error::SamplingError)?;
|
||||
|
||||
// Obtain the challenge c.
|
||||
let c = ChallengeScalar::<C::Scalar, ()>::get(transcript);
|
||||
|
||||
// Compute z1 and z2 as described in the Halo paper.
|
||||
let z1 = a * &*c + &d;
|
||||
let z2 = *c * &blind + &s;
|
||||
|
||||
Ok(Proof {
|
||||
rounds,
|
||||
delta,
|
||||
z1,
|
||||
z2,
|
||||
})
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parallel_generator_collapse<C: CurveAffine>(
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
use ff::Field;
|
||||
|
||||
use super::super::Error;
|
||||
use super::{Params, Proof, MSM};
|
||||
use crate::transcript::{Challenge, ChallengeScalar, Hasher, Transcript};
|
||||
use super::{Params, MSM};
|
||||
use crate::transcript::{Challenge, ChallengeScalar, TranscriptRead};
|
||||
|
||||
use crate::arithmetic::{best_multiexp, Curve, CurveAffine, FieldExt};
|
||||
|
||||
use std::io::Read;
|
||||
|
||||
/// A guard returned by the verifier
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Guard<'a, C: CurveAffine> {
|
||||
msm: MSM<'a, C>,
|
||||
neg_z1: C::Scalar,
|
||||
neg_a: C::Scalar,
|
||||
allinv: C::Scalar,
|
||||
challenges_sq: Vec<C::Scalar>,
|
||||
challenges_sq_packed: Vec<Challenge>,
|
||||
|
@ -31,17 +33,17 @@ 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);
|
||||
let s = compute_s(&self.challenges_sq, self.allinv * &self.neg_a);
|
||||
self.msm.add_to_g_scalars(&s);
|
||||
self.msm.add_to_h_scalar(self.neg_z1);
|
||||
self.msm.add_to_h_scalar(self.neg_a);
|
||||
|
||||
self.msm
|
||||
}
|
||||
|
||||
/// Lets caller supply the purported G point and simply appends
|
||||
/// [-z1] G to return an updated MSM.
|
||||
/// [-a] G to return an updated MSM.
|
||||
pub fn use_g(mut self, g: C) -> (MSM<'a, C>, Accumulator<C>) {
|
||||
self.msm.append_term(self.neg_z1, g);
|
||||
self.msm.append_term(self.neg_a, g);
|
||||
|
||||
let accumulator = Accumulator {
|
||||
g,
|
||||
|
@ -62,64 +64,44 @@ impl<'a, C: CurveAffine> Guard<'a, C> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<C: CurveAffine> Proof<C> {
|
||||
/// 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>(
|
||||
&self,
|
||||
/// 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`. The provided `commitment_msm` should evaluate to the commitment `P`
|
||||
/// being opened.
|
||||
pub fn verify_proof<'a, C: CurveAffine, R: Read, T: TranscriptRead<R, C>>(
|
||||
params: &'a Params<C>,
|
||||
mut msm: MSM<'a, C>,
|
||||
transcript: &mut Transcript<C, HBase, HScalar>,
|
||||
transcript: &mut T,
|
||||
x: C::Scalar,
|
||||
mut commitment_msm: MSM<'a, C>,
|
||||
v: C::Scalar,
|
||||
) -> Result<Guard<'a, C>, Error>
|
||||
where
|
||||
HBase: Hasher<C::Base>,
|
||||
HScalar: Hasher<C::Scalar>,
|
||||
{
|
||||
// Check for well-formedness
|
||||
if self.rounds.len() != params.k as usize {
|
||||
return Err(Error::OpeningError);
|
||||
}
|
||||
) -> Result<Guard<'a, C>, Error> {
|
||||
let k = params.k as usize;
|
||||
|
||||
// Compute U
|
||||
let u = {
|
||||
let u_x = transcript.squeeze();
|
||||
// y^2 = x^3 + B
|
||||
let u_y2 = u_x.square() * &u_x + &C::b();
|
||||
let u_y = u_y2.deterministic_sqrt();
|
||||
if u_y.is_none() {
|
||||
return Err(Error::OpeningError);
|
||||
}
|
||||
let u_y = u_y.unwrap();
|
||||
// P - [v] G_0 + S * iota
|
||||
// + \sum(L_i * u_i^2) + \sum(R_i * u_i^-2)
|
||||
commitment_msm.add_constant_term(-v);
|
||||
let s_poly_commitment = transcript.read_point().map_err(|_| Error::OpeningError)?;
|
||||
|
||||
C::from_xy(u_x, u_y).unwrap()
|
||||
};
|
||||
let iota = *ChallengeScalar::<C, ()>::get(transcript);
|
||||
commitment_msm.append_term(iota, s_poly_commitment);
|
||||
|
||||
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);
|
||||
let z = *ChallengeScalar::<C, ()>::get(transcript);
|
||||
|
||||
// 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 challenges_sq_packed: Vec<Challenge> = Vec::with_capacity(self.rounds.len());
|
||||
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<Challenge> = Vec::with_capacity(k);
|
||||
let mut allinv = C::Scalar::one();
|
||||
|
||||
for round in &self.rounds {
|
||||
// Feed L and R into the transcript.
|
||||
let l = round.0;
|
||||
let r = round.1;
|
||||
transcript
|
||||
.absorb_point(&l)
|
||||
.map_err(|_| Error::OpeningError)?;
|
||||
transcript
|
||||
.absorb_point(&r)
|
||||
.map_err(|_| Error::OpeningError)?;
|
||||
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: C::Scalar = *ChallengeScalar::<_, ()>::from(challenge_sq_packed);
|
||||
let challenge_sq = *ChallengeScalar::<C, ()>::from(challenge_sq_packed);
|
||||
|
||||
let challenge = challenge_sq.deterministic_sqrt();
|
||||
if challenge.is_none() {
|
||||
|
@ -139,10 +121,8 @@ impl<C: CurveAffine> Proof<C> {
|
|||
|
||||
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);
|
||||
commitment_msm.append_term(challenge_sq, l);
|
||||
commitment_msm.append_term(challenge_sq_inv, r);
|
||||
|
||||
challenges.push(challenge);
|
||||
challenges_inv.push(challenge_inv);
|
||||
|
@ -150,56 +130,38 @@ impl<C: CurveAffine> Proof<C> {
|
|||
challenges_sq_packed.push(challenge_sq_packed);
|
||||
}
|
||||
|
||||
// Feed delta into the transcript
|
||||
transcript
|
||||
.absorb_point(&self.delta)
|
||||
.map_err(|_| Error::OpeningError)?;
|
||||
|
||||
// Get the challenge `c`
|
||||
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
|
||||
// = [z1] (G + H)
|
||||
// The computation of [z1] (G + H) happens in either Guard::use_challenges()
|
||||
// or Guard::use_g().
|
||||
// Our goal is to open
|
||||
// commitment_msm - [v] G_0 + random_poly_commitment * iota
|
||||
// + \sum(L_i * u_i^2) + \sum(R_i * u_i^-2)
|
||||
// at x to 0, by asking the prover to supply (a, h) such that it equals
|
||||
// = [a] (G + [b * z] U) + [h] H
|
||||
// except that we wish for the prover to supply G as Commit(g(X); 1) so
|
||||
// we must substitute to get
|
||||
// = [a] ((G - H) + [b * z] U) + [h] H
|
||||
// = [a] G + [-a] H + [abz] U + [h] H
|
||||
// = [a] G + [abz] U + [h - a] H
|
||||
// but subtracting to get the desired equality
|
||||
// ... + [-a] G + [-abz] U + [a - h] H = 0
|
||||
|
||||
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 neg_z1 = -self.z1;
|
||||
commitment_msm.add_to_u_scalar(neg_a * &b * &z);
|
||||
commitment_msm.add_to_h_scalar(a - &h);
|
||||
|
||||
// [c] P
|
||||
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);
|
||||
}
|
||||
|
||||
for (scalar, base) in extra_scalars.iter().zip(extra_bases.iter()) {
|
||||
msm.append_term(*scalar, *base);
|
||||
}
|
||||
|
||||
// [c * v] U - [z1 * b] U
|
||||
msm.append_term((*c * &v) + &(neg_z1 * &b), u);
|
||||
|
||||
// delta
|
||||
msm.append_term(Field::one(), self.delta);
|
||||
|
||||
// + [z1 - z2] H
|
||||
msm.add_to_h_scalar(self.z1 - &self.z2);
|
||||
|
||||
let guard = Guard {
|
||||
msm,
|
||||
neg_z1,
|
||||
neg_a,
|
||||
allinv,
|
||||
challenges_sq,
|
||||
challenges_sq_packed,
|
||||
};
|
||||
|
||||
Ok(guard)
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_b<F: Field>(x: F, challenges: &[F], challenges_inv: &[F]) -> F {
|
||||
|
|
Loading…
Reference in New Issue