halo2_proofs: rename variables for consistency

This changes variable names in the multiopen and commitment opening implementations
and the book's protocol description to keep names and indicies consistent with one
another.

Co-Authored-By: Jack Grigg <jack@electriccoin.co>
This commit is contained in:
Sean Bowe 2022-02-09 13:36:49 -07:00
parent b45180273e
commit a4d3c328b9
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
7 changed files with 171 additions and 176 deletions

View File

@ -369,7 +369,7 @@ $$
15. $\verifier$ responds with challenge $x_3$.
16. $\prover$ sends $\mathbf{u} \in \field^{n_q}$ such that $\mathbf{u}_i = q_i(x_3)$ for all $i \in [0, n_q)$.
17. $\verifier$ responds with challenge $x_4$.
18. $\prover$ and $\verifier$ set $P = Q' + x_4 \sum\limits_{i=0}^{n_q - 1} [x_4^i] Q_i$ and $v = $
18. $\verifier$ set $P = Q' + x_4 \sum\limits_{i=0}^{n_q - 1} [x_4^i] Q_i$ and $v = $
$$
\sum\limits_{i=0}^{n_q - 1}
\left(
@ -392,12 +392,12 @@ $$
19. $\prover$ sets $p(X) = q'(X) + [x_4] \sum\limits_{i=0}^{n_q - 1} x_4^i q_i(X)$.
20. $\prover$ samples a random polynomial $s(X)$ of degree $n - 1$ with a root at $x_3$ and sends a commitment $S = \innerprod{\mathbf{s}}{\mathbf{G}} + [\cdot] W$ where $\mathbf{s}$ defines the coefficients of $s(X)$.
21. $\verifier$ responds with challenges $\xi, z$.
22. $\prover$ and $\verifier$ set $P' = P - [v] \mathbf{G}_0 + [\xi] S$.
23. $\prover$ sets $p'(X) = p(X) - v + \xi s(X)$.
22. $\verifier$ sets $P' = P - [v] \mathbf{G}_0 + [\xi] S$.
23. $\prover$ sets $p'(X) = p(X) - p(x_3) + \xi s(X)$ (where $p(x_3)$ should correspond with the verifier's computed value $v$).
24. Initialize $\mathbf{p'}$ as the coefficients of $p'(X)$ and $\mathbf{G'} = \mathbf{G}$ and $\mathbf{b} = (x_3^0, x_3^1, ..., x_3^{n - 1})$. $\prover$ and $\verifier$ will interact in the following $k$ rounds, where in the $j$th round starting in round $j=0$ and ending in round $j=k-1$:
* $\prover$ sends $L_j = \innerprod{\mathbf{p'}_\hi}{\mathbf{G'}_\lo} + [z \innerprod{\mathbf{p'}_\hi}{\mathbf{b}_\lo}] U + [\cdot] W$ and $R_j = \innerprod{\mathbf{p'}_\lo}{\mathbf{G'}_\hi} + [z \innerprod{\mathbf{p'}_\lo}{\mathbf{b}_\hi}] U + [\cdot] W$.
* $\verifier$ responds with challenge $u_j$.
* $\prover$ and $\verifier$ set $\mathbf{G'} := \mathbf{G'}_\lo + u_j \mathbf{G'}_\hi$ and $\mathbf{b} = \mathbf{b}_\lo + u_j \mathbf{b}_\hi$.
* $\prover$ and $\verifier$ set $\mathbf{G'} := \mathbf{G'}_\lo + u_j \mathbf{G'}_\hi$ and $\mathbf{b} := \mathbf{b}_\lo + u_j \mathbf{b}_\hi$.
* $\prover$ sets $\mathbf{p'} := \mathbf{p'}_\lo + u_j^{-1} \mathbf{p'}_\hi$.
25. $\prover$ sends $c = \mathbf{p'}_0$ and synthetic blinding factor $f$.
25. $\prover$ sends $c = \mathbf{p'}_0$ and synthetic blinding factor $f$ computed from the elided blinding factors.
26. $\verifier$ accepts only if $\sum_{j=0}^{k - 1} [u_j^{-1}] L_j + P' + \sum_{j=0}^{k - 1} [u_j] R_j = [c] \mathbf{G'}_0 + [c \mathbf{b}_0 z] U + [f] W$.

View File

@ -30,7 +30,7 @@ pub struct Params<C: CurveAffine> {
pub(crate) n: u64,
pub(crate) g: Vec<C>,
pub(crate) g_lagrange: Vec<C>,
pub(crate) h: C,
pub(crate) w: C,
pub(crate) u: C,
}
@ -102,7 +102,7 @@ impl<C: CurveAffine> Params<C> {
};
let hasher = C::CurveExt::hash_to_curve("Halo2-Parameters");
let h = hasher(&[1]).to_affine();
let w = hasher(&[1]).to_affine();
let u = hasher(&[2]).to_affine();
Params {
@ -110,7 +110,7 @@ impl<C: CurveAffine> Params<C> {
n,
g,
g_lagrange,
h,
w,
u,
}
}
@ -126,7 +126,7 @@ impl<C: CurveAffine> Params<C> {
tmp_scalars.push(r.0);
tmp_bases.extend(self.g.iter());
tmp_bases.push(self.h);
tmp_bases.push(self.w);
best_multiexp::<C>(&tmp_scalars, &tmp_bases)
}
@ -146,7 +146,7 @@ impl<C: CurveAffine> Params<C> {
tmp_scalars.push(r.0);
tmp_bases.extend(self.g_lagrange.iter());
tmp_bases.push(self.h);
tmp_bases.push(self.w);
best_multiexp::<C>(&tmp_scalars, &tmp_bases)
}
@ -171,7 +171,7 @@ impl<C: CurveAffine> Params<C> {
for g_lagrange_element in &self.g_lagrange {
writer.write_all(g_lagrange_element.to_bytes().as_ref())?;
}
writer.write_all(self.h.to_bytes().as_ref())?;
writer.write_all(self.w.to_bytes().as_ref())?;
writer.write_all(self.u.to_bytes().as_ref())?;
Ok(())
@ -188,7 +188,7 @@ impl<C: CurveAffine> Params<C> {
let g: Vec<_> = (0..n).map(|_| C::read(reader)).collect::<Result<_, _>>()?;
let g_lagrange: Vec<_> = (0..n).map(|_| C::read(reader)).collect::<Result<_, _>>()?;
let h = C::read(reader)?;
let w = C::read(reader)?;
let u = C::read(reader)?;
Ok(Params {
@ -196,7 +196,7 @@ impl<C: CurveAffine> Params<C> {
n,
g,
g_lagrange,
h,
w,
u,
})
}

View File

@ -8,7 +8,7 @@ use group::Group;
pub struct MSM<'a, C: CurveAffine> {
pub(crate) params: &'a Params<C>,
g_scalars: Option<Vec<C::Scalar>>,
h_scalar: Option<C::Scalar>,
w_scalar: Option<C::Scalar>,
u_scalar: Option<C::Scalar>,
other_scalars: Vec<C::Scalar>,
other_bases: Vec<C>,
@ -18,7 +18,7 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
/// Create a new, empty MSM using the provided parameters.
pub fn new(params: &'a Params<C>) -> Self {
let g_scalars = None;
let h_scalar = None;
let w_scalar = None;
let u_scalar = None;
let other_scalars = vec![];
let other_bases = vec![];
@ -26,7 +26,7 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
MSM {
params,
g_scalars,
h_scalar,
w_scalar,
u_scalar,
other_scalars,
other_bases,
@ -42,8 +42,8 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
self.add_to_g_scalars(g_scalars);
}
if let Some(h_scalar) = &other.h_scalar {
self.add_to_h_scalar(*h_scalar);
if let Some(w_scalar) = &other.w_scalar {
self.add_to_w_scalar(*w_scalar);
}
if let Some(u_scalar) = &other.u_scalar {
@ -83,9 +83,9 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
}
}
/// Add to `h_scalar`
pub fn add_to_h_scalar(&mut self, scalar: C::Scalar) {
self.h_scalar = self.h_scalar.map_or(Some(scalar), |a| Some(a + &scalar));
/// Add to `w_scalar`
pub fn add_to_w_scalar(&mut self, scalar: C::Scalar) {
self.w_scalar = self.w_scalar.map_or(Some(scalar), |a| Some(a + &scalar));
}
/// Add to `u_scalar`
@ -111,14 +111,14 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
})
}
self.h_scalar = self.h_scalar.map(|a| a * &factor);
self.w_scalar = self.w_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.w_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);
@ -127,9 +127,9 @@ impl<'a, C: CurveAffine> MSM<'a, C> {
scalars.extend(&self.other_scalars);
bases.extend(&self.other_bases);
if let Some(h_scalar) = self.h_scalar {
scalars.push(h_scalar);
bases.push(self.params.h);
if let Some(w_scalar) = self.w_scalar {
scalars.push(w_scalar);
bases.push(self.params.w);
}
if let Some(u_scalar) = self.u_scalar {

View File

@ -33,23 +33,23 @@ pub fn create_proof<
params: &Params<C>,
mut rng: R,
transcript: &mut T,
px: &Polynomial<C::Scalar, Coeff>,
blind: Blind<C::Scalar>,
p_poly: &Polynomial<C::Scalar, Coeff>,
p_blind: Blind<C::Scalar>,
x: C::Scalar,
) -> io::Result<()> {
// We're limited to polynomials of degree n - 1.
assert!(px.len() <= params.n as usize);
assert_eq!(p_poly.len(), params.n as usize);
// 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();
let mut s_poly = (*p_poly).clone();
for coeff in s_poly.iter_mut() {
*coeff = C::Scalar::random(&mut rng);
}
// Evaluate the random polynomial at x
let v_prime = eval_polynomial(&s_poly[..], x);
let s_at_x = 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;
s_poly[0] = s_poly[0] - &s_at_x;
// And sample a random blind
let s_poly_blind = Blind(C::Scalar::random(&mut rng));
@ -60,27 +60,29 @@ pub fn create_proof<
// 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 = *transcript.squeeze_challenge_scalar::<()>();
let xi = *transcript.squeeze_challenge_scalar::<()>();
// Challenge that ensures that the prover did not interfere with the U term
// in their commitments.
let z = *transcript.squeeze_challenge_scalar::<()>();
// 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;
let blind = s_poly_blind * Blind(iota) + blind;
let mut blind = blind.0;
// We'll be opening `P' = P - [v] G_0 + [\xi] S` to ensure it has a root at
// zero.
let mut p_prime_poly = s_poly * xi + p_poly;
let v = eval_polynomial(&p_prime_poly, x);
p_prime_poly[0] = p_prime_poly[0] - &v;
let p_prime_blind = s_poly_blind * Blind(xi) + p_blind;
// Initialize the vector `a` as the coefficients of the polynomial,
// rounding up to the parameters.
let mut a = final_poly.values;
a.resize(params.n as usize, C::Scalar::zero());
// This accumulates the synthetic blinding factor `f` starting
// with the blinding factor for `P'`.
let mut f = p_prime_blind.0;
// Initialize the vector `p_prime` as the coefficients of the polynomial.
let mut p_prime = p_prime_poly.values;
assert_eq!(p_prime.len(), params.n as usize);
// Initialize the vector `b` as the powers of `x`. The inner product of
// `a` and `b` is the evaluation of the polynomial at `x`.
// `p_prime` and `b` is the evaluation of the polynomial at `x`.
let mut b = Vec::with_capacity(1 << params.k);
{
let mut cur = C::Scalar::one();
@ -90,60 +92,60 @@ pub fn create_proof<
}
}
// Initialize the vector `G` from the URS. We'll be progressively collapsing
// Initialize the vector `G'` from the URS. We'll be progressively collapsing
// this vector into smaller and smaller vectors until it is of length 1.
let mut g = params.g.clone();
let mut g_prime = params.g.clone();
// Perform the inner product argument, round by round.
for k in (1..=params.k).rev() {
let half = 1 << (k - 1); // half the length of `a`, `b`, `G`
for j in 0..params.k {
let half = 1 << (params.k - j - 1); // half the length of `p_prime`, `b`, `G'`
// Compute L, R
//
// TODO: If we modify multiexp to take "extra" bases, we could speed
// this piece up a bit by combining the multiexps.
let l = best_multiexp(&a[half..], &g[0..half]);
let r = best_multiexp(&a[0..half], &g[half..]);
let value_l = compute_inner_product(&a[half..], &b[0..half]);
let value_r = compute_inner_product(&a[0..half], &b[half..]);
let l_randomness = C::Scalar::random(&mut rng);
let r_randomness = C::Scalar::random(&mut rng);
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 l = l.to_affine();
let r = r.to_affine();
let l_j = best_multiexp(&p_prime[half..], &g_prime[0..half]);
let r_j = best_multiexp(&p_prime[0..half], &g_prime[half..]);
let value_l_j = compute_inner_product(&p_prime[half..], &b[0..half]);
let value_r_j = compute_inner_product(&p_prime[0..half], &b[half..]);
let l_j_randomness = C::Scalar::random(&mut rng);
let r_j_randomness = C::Scalar::random(&mut rng);
let l_j = l_j + &best_multiexp(&[value_l_j * &z, l_j_randomness], &[params.u, params.w]);
let r_j = r_j + &best_multiexp(&[value_r_j * &z, r_j_randomness], &[params.u, params.w]);
let l_j = l_j.to_affine();
let r_j = r_j.to_affine();
// Feed L and R into the real transcript
transcript.write_point(l)?;
transcript.write_point(r)?;
transcript.write_point(l_j)?;
transcript.write_point(r_j)?;
let challenge = *transcript.squeeze_challenge_scalar::<()>();
let challenge_inv = challenge.invert().unwrap(); // TODO, bubble this up
let u_j = *transcript.squeeze_challenge_scalar::<()>();
let u_j_inv = u_j.invert().unwrap(); // TODO, bubble this up
// Collapse `a` and `b`.
// Collapse `p_prime` and `b`.
// TODO: parallelize
for i in 0..half {
a[i] = a[i] + &(a[i + half] * &challenge_inv);
b[i] = b[i] + &(b[i + half] * &challenge);
p_prime[i] = p_prime[i] + &(p_prime[i + half] * &u_j_inv);
b[i] = b[i] + &(b[i + half] * &u_j);
}
a.truncate(half);
p_prime.truncate(half);
b.truncate(half);
// Collapse `G`
parallel_generator_collapse(&mut g, challenge);
g.truncate(half);
// Collapse `G'`
parallel_generator_collapse(&mut g_prime, u_j);
g_prime.truncate(half);
// Update randomness (the synthetic blinding factor at the end)
blind += &(l_randomness * &challenge_inv);
blind += &(r_randomness * &challenge);
f += &(l_j_randomness * &u_j_inv);
f += &(r_j_randomness * &u_j);
}
// We have fully collapsed `a`, `b`, `G`
assert_eq!(a.len(), 1);
let a = a[0];
// We have fully collapsed `p_prime`, `b`, `G'`
assert_eq!(p_prime.len(), 1);
let c = p_prime[0];
transcript.write_scalar(a)?;
transcript.write_scalar(blind)?; // \xi
transcript.write_scalar(c)?;
transcript.write_scalar(f)?;
Ok(())
}

View File

@ -13,9 +13,9 @@ use crate::arithmetic::{best_multiexp, CurveAffine};
#[derive(Debug, Clone)]
pub struct Guard<'a, C: CurveAffine, E: EncodedChallenge<C>> {
msm: MSM<'a, C>,
neg_a: C::Scalar,
challenges: Vec<C::Scalar>,
challenges_packed: Vec<E>,
neg_c: C::Scalar,
u: Vec<C::Scalar>,
u_packed: Vec<E>,
}
/// An accumulator instance consisting of an evaluation claim and a proof.
@ -24,18 +24,18 @@ pub struct Accumulator<C: CurveAffine, E: EncodedChallenge<C>> {
/// The claimed output of the linear-time polycommit opening protocol
pub g: C,
/// A vector of 128-bit challenges sampled by the verifier, to be used in
/// computing g.
pub challenges_packed: Vec<E>,
/// A vector of 128-bit challenges u_0, ..., u_{k - 1} sampled by the
/// verifier, to be used in computing G'_0.
pub u_packed: Vec<E>,
}
impl<'a, C: CurveAffine, E: EncodedChallenge<C>> Guard<'a, C, E> {
/// 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, self.neg_a);
let s = compute_s(&self.u, self.neg_c);
self.msm.add_to_g_scalars(&s);
self.msm.add_to_h_scalar(self.neg_a);
self.msm.add_to_w_scalar(self.neg_c);
self.msm
}
@ -43,22 +43,22 @@ impl<'a, C: CurveAffine, E: EncodedChallenge<C>> Guard<'a, C, E> {
/// Lets caller supply the purported G point and simply appends
/// [-a] G to return an updated MSM.
pub fn use_g(mut self, g: C) -> (MSM<'a, C>, Accumulator<C, E>) {
self.msm.append_term(self.neg_a, g);
self.msm.append_term(self.neg_c, g);
let accumulator = Accumulator {
g,
challenges_packed: self.challenges_packed,
u_packed: self.u_packed,
};
(self.msm, accumulator)
}
/// Computes G + H, where G = ⟨s, params.g⟩ and H is used for blinding
/// Computes G + W, where G = ⟨s, params.g⟩ and W is used for blinding
pub fn compute_g(&self) -> C {
let s = compute_s(&self.challenges, C::Scalar::one());
let s = compute_s(&self.u, C::Scalar::one());
let mut tmp = best_multiexp(&s, &self.msm.params.g);
tmp += self.msm.params.h;
tmp += self.msm.params.w;
tmp.to_affine()
}
}
@ -75,14 +75,11 @@ pub fn verify_proof<'a, C: CurveAffine, E: EncodedChallenge<C>, T: TranscriptRea
) -> Result<Guard<'a, C, E>, Error> {
let k = params.k as usize;
// P - [v] G_0 + S * iota
// + \sum(L_i * u_i^2) + \sum(R_i * u_i^-2)
msm.add_constant_term(-v);
// P' = P - [v] G_0 + [\xi] S
msm.add_constant_term(-v); // add [-v] G_0
let s_poly_commitment = transcript.read_point().map_err(|_| Error::OpeningError)?;
let iota = *transcript.squeeze_challenge_scalar::<()>();
msm.append_term(iota, s_poly_commitment);
let xi = *transcript.squeeze_challenge_scalar::<()>();
msm.append_term(xi, s_poly_commitment);
let z = *transcript.squeeze_challenge_scalar::<()>();
@ -92,92 +89,89 @@ pub fn verify_proof<'a, C: CurveAffine, E: EncodedChallenge<C>, T: TranscriptRea
let l = transcript.read_point().map_err(|_| Error::OpeningError)?;
let r = transcript.read_point().map_err(|_| Error::OpeningError)?;
let challenge_packed = transcript.squeeze_challenge();
let challenge = *challenge_packed.as_challenge_scalar::<()>();
let u_j_packed = transcript.squeeze_challenge();
let u_j = *u_j_packed.as_challenge_scalar::<()>();
rounds.push((
l,
r,
challenge,
/* to be inverted */ challenge,
challenge_packed,
l, r, u_j, u_j, // to be inverted
u_j_packed,
));
}
rounds
.iter_mut()
.map(|&mut (_, _, _, ref mut challenge, _)| challenge)
.map(|&mut (_, _, _, ref mut u_j, _)| u_j)
.batch_invert();
let mut challenges = Vec::with_capacity(k);
let mut challenges_packed: Vec<E> = Vec::with_capacity(k);
for (l, r, challenge, challenge_inv, challenge_packed) in rounds {
msm.append_term(challenge_inv, l);
msm.append_term(challenge, r);
// This is the left hand side of the verifier equation.
// P' + \sum([u_j^{-1}] L_j) + \sum([u_j] R_j)
let mut u = Vec::with_capacity(k);
let mut u_packed: Vec<E> = Vec::with_capacity(k);
for (l, r, u_j, u_j_inv, u_j_packed) in rounds {
msm.append_term(u_j_inv, l);
msm.append_term(u_j, r);
challenges.push(challenge);
challenges_packed.push(challenge_packed);
u.push(u_j);
u_packed.push(u_j_packed);
}
// Our goal is to open
// 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, \xi) such that it equals
// = [a] (G + [b * z] U) + [\xi] 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) + [\xi] H
// = [a] G + [-a] H + [abz] U + [\xi] H
// = [a] G + [abz] U + [\xi - a] H
// but subtracting to get the desired equality
// ... + [-a] G + [-abz] U + [a - \xi] H = 0
// Our goal is to check that the left hand side of the verifier
// equation
// P' + \sum([u_j^{-1}] L_j) + \sum([u_j] R_j)
// equals (given the prover's values c, f) the right hand side
// = [c] (G'_0 + [b * z] U) + [f] W
// except that we wish for the prover to supply G'_0 as Commit(g(X); 1) so
// we must substitute G'_0 with G'_0 - W to get
// = [c] ((G'_0 - W) + [b * z] U) + [f] W
// = [c] G'_0 + [-c] W + [cbz] U + [f] W
// = [c] G'_0 + [cbz] U + [f - c] W
// and then subtracting the right hand side from both sides
// to get
// P' + \sum([u_j^{-1}] L_j) + \sum([u_j] R_j)
// + [-c] G'_0 + [-cbz] U + [c - f] W
// = 0
let a = transcript.read_scalar().map_err(|_| Error::SamplingError)?;
let neg_a = -a;
let xi = transcript.read_scalar().map_err(|_| Error::SamplingError)?;
let b = compute_b(x, &challenges);
let c = transcript.read_scalar().map_err(|_| Error::SamplingError)?;
let neg_c = -c;
let f = transcript.read_scalar().map_err(|_| Error::SamplingError)?;
let b = compute_b(x, &u);
msm.add_to_u_scalar(neg_a * &b * &z);
msm.add_to_h_scalar(a - &xi);
msm.add_to_u_scalar(neg_c * &b * &z);
msm.add_to_w_scalar(c - &f);
let guard = Guard {
msm,
neg_a,
challenges,
challenges_packed,
neg_c,
u,
u_packed,
};
Ok(guard)
}
/// Computes $\prod\limits_{i=0}^{k-1} (1 + u_i x^{2^i})$.
fn compute_b<F: Field>(x: F, challenges: &[F]) -> F {
/// Computes $\prod\limits_{i=0}^{k-1} (1 + u_{k - 1 - i} x^{2^i})$.
fn compute_b<F: Field>(x: F, u: &[F]) -> F {
let mut tmp = F::one();
let mut cur = x;
for challenge in challenges.iter().rev() {
tmp *= F::one() + &(*challenge * &cur);
for u_j in u.iter().rev() {
tmp *= F::one() + &(*u_j * &cur);
cur *= cur;
}
tmp
}
/// Computes the coefficients of $g(X) = \prod\limits_{i=0}^{k-1} (1 + u_i X^{2^i})$.
fn compute_s<F: Field>(challenges: &[F], init: F) -> Vec<F> {
assert!(!challenges.is_empty());
let mut v = vec![F::zero(); 1 << challenges.len()];
/// Computes the coefficients of $g(X) = \prod\limits_{i=0}^{k-1} (1 + u_{k - 1 - i} X^{2^i})$.
fn compute_s<F: Field>(u: &[F], init: F) -> Vec<F> {
assert!(!u.is_empty());
let mut v = vec![F::zero(); 1 << u.len()];
v[0] = init;
for (len, challenge) in challenges
.iter()
.rev()
.enumerate()
.map(|(i, challenge)| (1 << i, challenge))
{
for (len, u_j) in u.iter().rev().enumerate().map(|(i, u_j)| (1 << i, u_j)) {
let (left, right) = v.split_at_mut(len);
let right = &mut right[0..len];
right.copy_from_slice(left);
for v in right {
*v *= challenge;
*v *= u_j;
}
}

View File

@ -64,10 +64,10 @@ where
}
}
let f_poly = point_sets
let q_prime_poly = point_sets
.iter()
.zip(q_polys.iter())
.fold(None, |f_poly, (points, poly)| {
.fold(None, |q_prime_poly, (points, poly)| {
let mut poly = points
.iter()
.fold(poly.clone().unwrap().values, |poly, point| {
@ -79,43 +79,40 @@ where
_marker: PhantomData,
};
if f_poly.is_none() {
if q_prime_poly.is_none() {
Some(poly)
} else {
f_poly.map(|f_poly| f_poly * *x_2 + &poly)
q_prime_poly.map(|q_prime_poly| q_prime_poly * *x_2 + &poly)
}
})
.unwrap();
let f_blind = Blind(C::Scalar::random(&mut rng));
let f_commitment = params.commit(&f_poly, f_blind).to_affine();
let q_prime_blind = Blind(C::Scalar::random(&mut rng));
let q_prime_commitment = params.commit(&q_prime_poly, q_prime_blind).to_affine();
transcript.write_point(f_commitment)?;
transcript.write_point(q_prime_commitment)?;
let x_3: ChallengeX3<_> = transcript.squeeze_challenge_scalar();
let q_evals: Vec<C::Scalar> = q_polys
.iter()
.map(|poly| eval_polynomial(poly.as_ref().unwrap(), *x_3))
.collect();
for eval in q_evals.iter() {
transcript.write_scalar(*eval)?;
// Prover sends u_i for all i, which correspond to the evaluation
// of each Q polynomial commitment at x_3.
for q_i_poly in &q_polys {
transcript.write_scalar(eval_polynomial(q_i_poly.as_ref().unwrap(), *x_3))?;
}
let x_4: ChallengeX4<_> = transcript.squeeze_challenge_scalar();
let (f_poly, f_blind_try) = q_polys.iter().zip(q_blinds.iter()).fold(
(f_poly, f_blind),
|(f_poly, f_blind), (poly, blind)| {
let (p_poly, p_poly_blind) = q_polys.into_iter().zip(q_blinds.into_iter()).fold(
(q_prime_poly, q_prime_blind),
|(q_prime_poly, q_prime_blind), (poly, blind)| {
(
f_poly * *x_4 + poly.as_ref().unwrap(),
Blind((f_blind.0 * &(*x_4)) + &blind.0),
q_prime_poly * *x_4 + &poly.unwrap(),
Blind((q_prime_blind.0 * &(*x_4)) + &blind.0),
)
},
);
commitment::create_proof(params, rng, transcript, &f_poly, f_blind_try, *x_3)
commitment::create_proof(params, rng, transcript, &p_poly, p_poly_blind, *x_3)
}
#[doc(hidden)]

View File

@ -77,23 +77,25 @@ where
}
// Obtain the commitment to the multi-point quotient polynomial f(X).
let f_commitment = transcript.read_point().map_err(|_| Error::SamplingError)?;
let q_prime_commitment = transcript.read_point().map_err(|_| Error::SamplingError)?;
// Sample a challenge x_3 for checking that f(X) was committed to
// correctly.
let x_3: ChallengeX3<_> = transcript.squeeze_challenge_scalar();
let mut q_evals = Vec::with_capacity(q_eval_sets.len());
// u is a vector containing the evaluations of the Q polynomial
// commitments at x_3
let mut u = Vec::with_capacity(q_eval_sets.len());
for _ in 0..q_eval_sets.len() {
q_evals.push(transcript.read_scalar().map_err(|_| Error::SamplingError)?);
u.push(transcript.read_scalar().map_err(|_| Error::SamplingError)?);
}
// We can compute the expected msm_eval at x_3 using the q_evals provided
// We can compute the expected msm_eval at x_3 using the u provided
// by the prover and from x_2
let msm_eval = point_sets
.iter()
.zip(q_eval_sets.iter())
.zip(q_evals.iter())
.zip(u.iter())
.fold(
C::Scalar::zero(),
|msm_eval, ((points, evals), proof_eval)| {
@ -111,8 +113,8 @@ where
let x_4: ChallengeX4<_> = transcript.squeeze_challenge_scalar();
// Compute the final commitment that has to be opened
msm.append_term(C::Scalar::one(), f_commitment);
let (msm, msm_eval) = q_commitments.into_iter().zip(q_evals.iter()).fold(
msm.append_term(C::Scalar::one(), q_prime_commitment);
let (msm, v) = q_commitments.into_iter().zip(u.iter()).fold(
(msm, msm_eval),
|(mut msm, msm_eval), (q_commitment, q_eval)| {
msm.scale(*x_4);
@ -122,7 +124,7 @@ where
);
// Verify the opening proof
super::commitment::verify_proof(params, msm, transcript, *x_3, msm_eval)
super::commitment::verify_proof(params, msm, transcript, *x_3, v)
}
impl<'a, 'b, C: CurveAffine> Query<C::Scalar> for VerifierQuery<'a, 'b, C> {