diff --git a/book/src/design/protocol.md b/book/src/design/protocol.md index 15e3a07f..20702996 100644 --- a/book/src/design/protocol.md +++ b/book/src/design/protocol.md @@ -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$. diff --git a/halo2_proofs/src/poly/commitment.rs b/halo2_proofs/src/poly/commitment.rs index 14868bc5..f7a3a89e 100644 --- a/halo2_proofs/src/poly/commitment.rs +++ b/halo2_proofs/src/poly/commitment.rs @@ -30,7 +30,7 @@ pub struct Params { pub(crate) n: u64, pub(crate) g: Vec, pub(crate) g_lagrange: Vec, - pub(crate) h: C, + pub(crate) w: C, pub(crate) u: C, } @@ -102,7 +102,7 @@ impl Params { }; 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 Params { n, g, g_lagrange, - h, + w, u, } } @@ -126,7 +126,7 @@ impl Params { tmp_scalars.push(r.0); tmp_bases.extend(self.g.iter()); - tmp_bases.push(self.h); + tmp_bases.push(self.w); best_multiexp::(&tmp_scalars, &tmp_bases) } @@ -146,7 +146,7 @@ impl Params { tmp_scalars.push(r.0); tmp_bases.extend(self.g_lagrange.iter()); - tmp_bases.push(self.h); + tmp_bases.push(self.w); best_multiexp::(&tmp_scalars, &tmp_bases) } @@ -171,7 +171,7 @@ impl Params { 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 Params { let g: Vec<_> = (0..n).map(|_| C::read(reader)).collect::>()?; let g_lagrange: Vec<_> = (0..n).map(|_| C::read(reader)).collect::>()?; - let h = C::read(reader)?; + let w = C::read(reader)?; let u = C::read(reader)?; Ok(Params { @@ -196,7 +196,7 @@ impl Params { n, g, g_lagrange, - h, + w, u, }) } diff --git a/halo2_proofs/src/poly/commitment/msm.rs b/halo2_proofs/src/poly/commitment/msm.rs index 7bfe3e47..7cb27b29 100644 --- a/halo2_proofs/src/poly/commitment/msm.rs +++ b/halo2_proofs/src/poly/commitment/msm.rs @@ -8,7 +8,7 @@ use group::Group; pub struct MSM<'a, C: CurveAffine> { pub(crate) params: &'a Params, g_scalars: Option>, - h_scalar: Option, + w_scalar: Option, u_scalar: Option, other_scalars: Vec, other_bases: Vec, @@ -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) -> 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 = 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 { diff --git a/halo2_proofs/src/poly/commitment/prover.rs b/halo2_proofs/src/poly/commitment/prover.rs index 852709c3..05848352 100644 --- a/halo2_proofs/src/poly/commitment/prover.rs +++ b/halo2_proofs/src/poly/commitment/prover.rs @@ -33,23 +33,23 @@ pub fn create_proof< params: &Params, mut rng: R, transcript: &mut T, - px: &Polynomial, - blind: Blind, + p_poly: &Polynomial, + p_blind: Blind, 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(()) } diff --git a/halo2_proofs/src/poly/commitment/verifier.rs b/halo2_proofs/src/poly/commitment/verifier.rs index a87c6bc0..ebd687c1 100644 --- a/halo2_proofs/src/poly/commitment/verifier.rs +++ b/halo2_proofs/src/poly/commitment/verifier.rs @@ -13,9 +13,9 @@ use crate::arithmetic::{best_multiexp, CurveAffine}; #[derive(Debug, Clone)] pub struct Guard<'a, C: CurveAffine, E: EncodedChallenge> { msm: MSM<'a, C>, - neg_a: C::Scalar, - challenges: Vec, - challenges_packed: Vec, + neg_c: C::Scalar, + u: Vec, + u_packed: Vec, } /// An accumulator instance consisting of an evaluation claim and a proof. @@ -24,18 +24,18 @@ pub struct Accumulator> { /// 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, + /// 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, } impl<'a, C: CurveAffine, E: EncodedChallenge> 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> 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) { - 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, T: TranscriptRea ) -> Result, 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, 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 = 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 = 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(x: F, challenges: &[F]) -> F { +/// Computes $\prod\limits_{i=0}^{k-1} (1 + u_{k - 1 - i} x^{2^i})$. +fn compute_b(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(challenges: &[F], init: F) -> Vec { - 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(u: &[F], init: F) -> Vec { + 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; } } diff --git a/halo2_proofs/src/poly/multiopen/prover.rs b/halo2_proofs/src/poly/multiopen/prover.rs index 94f0267b..245bd5b0 100644 --- a/halo2_proofs/src/poly/multiopen/prover.rs +++ b/halo2_proofs/src/poly/multiopen/prover.rs @@ -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 = 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)] diff --git a/halo2_proofs/src/poly/multiopen/verifier.rs b/halo2_proofs/src/poly/multiopen/verifier.rs index 0cbbf2ce..0505f426 100644 --- a/halo2_proofs/src/poly/multiopen/verifier.rs +++ b/halo2_proofs/src/poly/multiopen/verifier.rs @@ -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 for VerifierQuery<'a, 'b, C> {