From a2faf8e5353332bece7242cf877191bd5d99b5d4 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Wed, 20 Dec 2023 10:20:49 -0600 Subject: [PATCH 01/23] add ops and rest of multilinear functions --- math/src/field/element.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/math/src/field/element.rs b/math/src/field/element.rs index 8c052066f..fc4ce4785 100644 --- a/math/src/field/element.rs +++ b/math/src/field/element.rs @@ -250,6 +250,28 @@ where } } +/// SubAssign operator overloading for field elements +impl SubAssign> for FieldElement +where + F: IsSubFieldOf, + L: IsField, +{ + fn sub_assign(&mut self, rhs: FieldElement) { + self.value = >::sub(&rhs.value, &self.value); + } +} + +/// SubAssign operator overloading for field elements +impl SubAssign<&FieldElement> for FieldElement +where + F: IsSubFieldOf, + L: IsField, +{ + fn sub_assign(&mut self, rhs: &FieldElement) { + self.value = >::sub(&rhs.value, &self.value); + } +} + /// Multiplication operator overloading for field elements*/ impl Mul<&FieldElement> for &FieldElement where From 277d46cd5202ef5cb82fec52c298a229b1ef4846 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Wed, 20 Dec 2023 19:09:11 -0600 Subject: [PATCH 02/23] finish test and add error infra --- math/src/polynomial/dense_multilinear_poly.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/math/src/polynomial/dense_multilinear_poly.rs b/math/src/polynomial/dense_multilinear_poly.rs index a1a1527a9..d7e411910 100644 --- a/math/src/polynomial/dense_multilinear_poly.rs +++ b/math/src/polynomial/dense_multilinear_poly.rs @@ -125,7 +125,7 @@ where // pad the polynomial with zero polynomial at the end z.resize(z.len().next_power_of_two(), FieldElement::zero()); - DenseMultilinearPolynomial::new(z) + Ok(DenseMultilinearPolynomial::new(z)) } pub fn from_u64(evals: &[u64]) -> Self { From db50196e0098debbd4b9fcac860408a4dffaa1c9 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Fri, 22 Dec 2023 17:49:23 -0600 Subject: [PATCH 03/23] add sparse multlinear --- math/src/polynomial/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/math/src/polynomial/mod.rs b/math/src/polynomial/mod.rs index 8a0671776..d98268631 100644 --- a/math/src/polynomial/mod.rs +++ b/math/src/polynomial/mod.rs @@ -4,6 +4,7 @@ use alloc::{borrow::ToOwned, vec, vec::Vec}; use core::{fmt::Display, ops}; pub mod dense_multilinear_poly; +pub mod sparse_multilinear_poly; mod error; pub mod sparse_multilinear_poly; From dae1bdc0b152e7910a8d6fcb2e66fe5db9f75060 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Mon, 8 Jan 2024 16:02:40 -0600 Subject: [PATCH 04/23] no_std + fmt --- math/Cargo.toml | 1 + math/src/polynomial/mod.rs | 1 - .../src/polynomial/sparse_multilinear_poly.rs | 125 ++++++------------ 3 files changed, 43 insertions(+), 84 deletions(-) diff --git a/math/Cargo.toml b/math/Cargo.toml index 44b7dbb4f..81195f997 100644 --- a/math/Cargo.toml +++ b/math/Cargo.toml @@ -13,6 +13,7 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"], proptest = { version = "1.1.0", optional = true } winter-math = { package = "winter-math", version = "0.6.4", default-features = false, optional = true } miden-core = { package = "miden-core" , version = "0.7", default-features = false, optional = true } +thiserror = "1.0.38" # rayon rayon = { version = "1.7", optional = true } diff --git a/math/src/polynomial/mod.rs b/math/src/polynomial/mod.rs index d98268631..8a0671776 100644 --- a/math/src/polynomial/mod.rs +++ b/math/src/polynomial/mod.rs @@ -4,7 +4,6 @@ use alloc::{borrow::ToOwned, vec, vec::Vec}; use core::{fmt::Display, ops}; pub mod dense_multilinear_poly; -pub mod sparse_multilinear_poly; mod error; pub mod sparse_multilinear_poly; diff --git a/math/src/polynomial/sparse_multilinear_poly.rs b/math/src/polynomial/sparse_multilinear_poly.rs index 958698de3..03131e8f5 100644 --- a/math/src/polynomial/sparse_multilinear_poly.rs +++ b/math/src/polynomial/sparse_multilinear_poly.rs @@ -1,9 +1,6 @@ -#[cfg(feature = "parallel")] use rayon::iter::{IntoParallelIterator, ParallelIterator}; -use crate::field::{element::FieldElement, traits::IsField}; -use crate::polynomial::error::MultilinearError; -use alloc::vec::Vec; +use crate::field::{traits::IsField, element::FieldElement}; pub struct SparseMultilinearPolynomial where @@ -20,93 +17,57 @@ where pub fn new(num_vars: usize, evals: Vec<(usize, FieldElement)>) -> Self { SparseMultilinearPolynomial { num_vars, evals } } - - pub fn num_vars(&self) -> usize { - self.num_vars - } - + /// Computes the eq extension polynomial of the polynomial. /// return 1 when a == r, otherwise return 0. - fn compute_chi(a: &[bool], r: &[FieldElement]) -> Result, MultilinearError> { - assert_eq!(a.len(), r.len()); - if a.len() != r.len() { - return Err(MultilinearError::ChisAndEvalsLengthMismatch( - a.len(), - r.len(), - )); + fn compute_chi(a: &[bool], r: &[FieldElement]) -> FieldElement { + assert_eq!(a.len(), r.len()); + let mut chi_i = FieldElement::one(); + for j in 0..r.len() { + if a[j] { + chi_i *= &r[j]; + } else { + chi_i *= FieldElement::::one() - &r[j]; } - let mut chi_i = FieldElement::one(); - for j in 0..r.len() { - if a[j] { - chi_i *= &r[j]; - } else { - chi_i *= FieldElement::::one() - &r[j]; - } - } - Ok(chi_i) + } + chi_i } // Takes O(n log n) - pub fn evaluate(&self, r: &[FieldElement]) -> Result, MultilinearError> { - if r.len() != self.num_vars() { - return Err(MultilinearError::IncorrectNumberofEvaluationPoints( - r.len(), - self.num_vars(), - )); - } - - #[cfg(feature = "parallel")] - let iter = (0..self.evals.len()).into_par_iter(); - - #[cfg(not(feature = "parallel"))] - let iter = 0..self.evals.len(); - - Ok(iter - .map(|i| { - let bits = get_bits(self.evals[i].0, r.len()); - let mut chi_i = FieldElement::::one(); - for j in 0..r.len() { - if bits[j] { - chi_i *= &r[j]; - } else { - chi_i *= FieldElement::::one() - &r[j]; - } + pub fn evaluate(&self, r: &[FieldElement]) -> FieldElement { + assert_eq!(self.num_vars, r.len()); + (0..self.evals.len()) + .into_par_iter() + .map(|i| { + let bits = get_bits(self.evals[i].0,r.len()); + let mut chi_i = FieldElement::::one(); + for j in 0..r.len() { + if bits[j] { + chi_i *= &r[j]; + } else { + chi_i *= FieldElement::::one() - &r[j]; } - chi_i * &self.evals[i].1 - }) - .sum()) + } + chi_i * &self.evals[i].1 + }) + .sum() } // Takes O(n log n) - pub fn evaluate_with( - num_vars: usize, - evals: &[(usize, FieldElement)], - r: &[FieldElement], - ) -> Result, MultilinearError> { - assert_eq!(num_vars, r.len()); - if r.len() != num_vars { - return Err(MultilinearError::IncorrectNumberofEvaluationPoints( - r.len(), - num_vars, - )); - } - - #[cfg(feature = "parallel")] - let iter = (0..evals.len()).into_par_iter(); - - #[cfg(not(feature = "parallel"))] - let iter = 0..evals.len(); - Ok(iter - .map(|i| { - let bits = get_bits(evals[i].0, r.len()); - SparseMultilinearPolynomial::compute_chi(&bits, r).unwrap() * &evals[i].1 - }) - .sum()) + pub fn evaluate_with(num_vars: usize, evals: &[(usize, FieldElement)], r: &[FieldElement]) -> FieldElement { + assert_eq!(num_vars, r.len()); + + (0..evals.len()) + .into_par_iter() + .map(|i| { + let bits = get_bits(evals[i].0,r.len()); + SparseMultilinearPolynomial::compute_chi(&bits, r) * &evals[i].1 + }) + .sum() } } -/// Returns the bit decomposition (Vec) of the `index` of an evaluation within the sparse multilinear polynomial. -fn get_bits(n: usize, num_bits: usize) -> Vec { + fn get_bits(n: usize, num_bits: usize) -> Vec { (0..num_bits) .map(|shift_amount| ((n & (1 << (num_bits - shift_amount - 1))) > 0)) .collect::>() @@ -141,10 +102,8 @@ mod test { ); let x = vec![FE::one(), FE::zero(), FE::one()]; - assert_eq!(m_poly.evaluate(x.as_slice()).unwrap(), FE::one()); - assert_eq!( - SparseMultilinearPolynomial::evaluate_with(3, &z, x.as_slice()).unwrap(), - FE::one() - ); + assert_eq!(m_poly.evaluate(x.as_slice()), FE::one()); + assert_eq!(SparseMultilinearPolynomial::evaluate_with(3, &z, x.as_slice()), FE::one()); + } } From 718920f698578ad50a4c9df57b9532e239f82be1 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Mon, 8 Jan 2024 17:12:43 -0600 Subject: [PATCH 05/23] cmts + impl error --- math/src/polynomial/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/math/src/polynomial/error.rs b/math/src/polynomial/error.rs index 69140c3e7..ef9bfd8b9 100644 --- a/math/src/polynomial/error.rs +++ b/math/src/polynomial/error.rs @@ -22,4 +22,4 @@ impl Display for MultilinearError { } #[cfg(feature = "std")] -impl std::error::Error for MultilinearError {} +impl std::error::Error for MultilinearError {} \ No newline at end of file From 0a2d21e6a9a33745c2ac70302654e273b74e57fe Mon Sep 17 00:00:00 2001 From: PatStiles Date: Tue, 9 Jan 2024 17:54:44 -0600 Subject: [PATCH 06/23] add benches + fmt --- math/src/polynomial/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/math/src/polynomial/error.rs b/math/src/polynomial/error.rs index ef9bfd8b9..69140c3e7 100644 --- a/math/src/polynomial/error.rs +++ b/math/src/polynomial/error.rs @@ -22,4 +22,4 @@ impl Display for MultilinearError { } #[cfg(feature = "std")] -impl std::error::Error for MultilinearError {} \ No newline at end of file +impl std::error::Error for MultilinearError {} From a393b89a27dd3d5bf385dad392cde19f0d94c7fa Mon Sep 17 00:00:00 2001 From: PatStiles Date: Tue, 9 Jan 2024 19:25:18 -0600 Subject: [PATCH 07/23] ci --- provers/plonk/src/verifier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provers/plonk/src/verifier.rs b/provers/plonk/src/verifier.rs index 17e8e59f1..be584013c 100644 --- a/provers/plonk/src/verifier.rs +++ b/provers/plonk/src/verifier.rs @@ -108,7 +108,7 @@ impl> Verifier { * (&p.c_zeta + &gamma) * (&p.a_zeta + &beta * &p.s1_zeta + &gamma) * (&p.b_zeta + &beta * &p.s2_zeta + &gamma); - p_constant_zeta = p_constant_zeta - &l1_zeta * &alpha * α + p_constant_zeta -= &l1_zeta * &alpha * α p_constant_zeta += p_pi_zeta; let p_zeta = p_constant_zeta + &p.p_non_constant_zeta; From 0d4d9e1da9feec5d01038afc02ad8fc356ddafac Mon Sep 17 00:00:00 2001 From: PatStiles Date: Wed, 10 Jan 2024 10:37:36 -0600 Subject: [PATCH 08/23] change SubAssign to Sub --- math/Cargo.toml | 1 - provers/plonk/src/verifier.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/math/Cargo.toml b/math/Cargo.toml index 81195f997..44b7dbb4f 100644 --- a/math/Cargo.toml +++ b/math/Cargo.toml @@ -13,7 +13,6 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"], proptest = { version = "1.1.0", optional = true } winter-math = { package = "winter-math", version = "0.6.4", default-features = false, optional = true } miden-core = { package = "miden-core" , version = "0.7", default-features = false, optional = true } -thiserror = "1.0.38" # rayon rayon = { version = "1.7", optional = true } diff --git a/provers/plonk/src/verifier.rs b/provers/plonk/src/verifier.rs index be584013c..17e8e59f1 100644 --- a/provers/plonk/src/verifier.rs +++ b/provers/plonk/src/verifier.rs @@ -108,7 +108,7 @@ impl> Verifier { * (&p.c_zeta + &gamma) * (&p.a_zeta + &beta * &p.s1_zeta + &gamma) * (&p.b_zeta + &beta * &p.s2_zeta + &gamma); - p_constant_zeta -= &l1_zeta * &alpha * α + p_constant_zeta = p_constant_zeta - &l1_zeta * &alpha * α p_constant_zeta += p_pi_zeta; let p_zeta = p_constant_zeta + &p.p_non_constant_zeta; From 2756527969d61b474365585c77e33bd764bba6d2 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Wed, 10 Jan 2024 15:52:52 -0600 Subject: [PATCH 09/23] remove SubAssign --- math/src/field/element.rs | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/math/src/field/element.rs b/math/src/field/element.rs index fc4ce4785..8c052066f 100644 --- a/math/src/field/element.rs +++ b/math/src/field/element.rs @@ -250,28 +250,6 @@ where } } -/// SubAssign operator overloading for field elements -impl SubAssign> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - fn sub_assign(&mut self, rhs: FieldElement) { - self.value = >::sub(&rhs.value, &self.value); - } -} - -/// SubAssign operator overloading for field elements -impl SubAssign<&FieldElement> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - fn sub_assign(&mut self, rhs: &FieldElement) { - self.value = >::sub(&rhs.value, &self.value); - } -} - /// Multiplication operator overloading for field elements*/ impl Mul<&FieldElement> for &FieldElement where From 3122092c5684cdc09daf77c9dd05413fddf28b4e Mon Sep 17 00:00:00 2001 From: PatStiles Date: Fri, 19 Jan 2024 11:15:08 -0600 Subject: [PATCH 10/23] fix errors add docs --- math/src/polynomial/dense_multilinear_poly.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/math/src/polynomial/dense_multilinear_poly.rs b/math/src/polynomial/dense_multilinear_poly.rs index d7e411910..a1a1527a9 100644 --- a/math/src/polynomial/dense_multilinear_poly.rs +++ b/math/src/polynomial/dense_multilinear_poly.rs @@ -125,7 +125,7 @@ where // pad the polynomial with zero polynomial at the end z.resize(z.len().next_power_of_two(), FieldElement::zero()); - Ok(DenseMultilinearPolynomial::new(z)) + DenseMultilinearPolynomial::new(z) } pub fn from_u64(evals: &[u64]) -> Self { From 9ab63d0ca13ee47d23689188d552ae427751c069 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Mon, 22 Jan 2024 18:43:19 -0600 Subject: [PATCH 11/23] Impl Zeromorph scheme and crush some errors --- crypto/src/commitments/mod.rs | 1 + crypto/src/commitments/traits.rs | 41 + crypto/src/commitments/zeromorph/mod.rs | 1 + crypto/src/commitments/zeromorph/zeromorph.rs | 798 ++++++++++++++++++ .../curves/bls12_381/pairing.rs | 1 + math/src/elliptic_curve/traits.rs | 1 + 6 files changed, 843 insertions(+) create mode 100644 crypto/src/commitments/zeromorph/mod.rs create mode 100644 crypto/src/commitments/zeromorph/zeromorph.rs diff --git a/crypto/src/commitments/mod.rs b/crypto/src/commitments/mod.rs index 0278e8989..7042b787c 100644 --- a/crypto/src/commitments/mod.rs +++ b/crypto/src/commitments/mod.rs @@ -1,2 +1,3 @@ pub mod kzg; pub mod traits; +pub mod zeromorph; diff --git a/crypto/src/commitments/traits.rs b/crypto/src/commitments/traits.rs index 6b9f36460..05624a351 100644 --- a/crypto/src/commitments/traits.rs +++ b/crypto/src/commitments/traits.rs @@ -2,6 +2,9 @@ use lambdaworks_math::{ field::{element::FieldElement, traits::IsField}, polynomial::Polynomial, }; +use std::borrow::Borrow; + +use crate::fiat_shamir::transcript::Transcript; pub trait IsCommitmentScheme { type Commitment; @@ -39,3 +42,41 @@ pub trait IsCommitmentScheme { upsilon: &FieldElement, ) -> bool; } + + +pub trait PolynomialCommitmentScheme { + // Abstracting over Polynomial allows us to have batched and non-batched PCS + type Polynomial; + type Commitment; + type Evaluation; + type Challenge; + type Proof; + type Error; + + type ProverKey; + type CommitmentKey; + type VerifierKey; + + //TODO: convert to impl IntoIterator + fn commit( + poly: Self::Polynomial, + ck: impl Borrow, + ) -> Result; + + fn prove( + poly: Self::Polynomial, + evals: Self::Evaluation, + challenges: Self::Challenge, + pk: impl Borrow, + transcript: &mut impl Transcript, + ) -> Result; + + fn verify( + commitments: Self::Commitment, + evals: Self::Evaluation, + challenges: Self::Challenge, + vk: impl Borrow, + transcript: &mut impl Transcript, + proof: Self::Proof, + ) -> Result<(), Self::Error>; +} \ No newline at end of file diff --git a/crypto/src/commitments/zeromorph/mod.rs b/crypto/src/commitments/zeromorph/mod.rs new file mode 100644 index 000000000..c60c1091b --- /dev/null +++ b/crypto/src/commitments/zeromorph/mod.rs @@ -0,0 +1 @@ +pub mod zeromorph; \ No newline at end of file diff --git a/crypto/src/commitments/zeromorph/zeromorph.rs b/crypto/src/commitments/zeromorph/zeromorph.rs new file mode 100644 index 000000000..98a97dc0f --- /dev/null +++ b/crypto/src/commitments/zeromorph/zeromorph.rs @@ -0,0 +1,798 @@ +#![allow(clippy::too_many_arguments)] +#![allow(clippy::type_complexity)] + +use std::{borrow::Borrow, iter, marker::PhantomData, sync::Mutex}; +use alloc::sync::Arc; +use lambdaworks_math::{ + cyclic_group::IsGroup, elliptic_curve::{traits::IsPairing, short_weierstrass::curves::bls12_381::pairing::BLS12381AtePairing}, field::{element::FieldElement, traits::{IsField, IsPrimeField}}, msm::pippenger::msm, polynomial::{Polynomial, dense_multilinear_poly::DenseMultilinearPolynomial}, traits::{AsBytes, ByteConversion}, unsigned_integer::element::UnsignedInteger +}; +use crate::{commitments::{kzg::StructuredReferenceString, traits::PolynomialCommitmentScheme}, fiat_shamir::{default_transcript::DefaultTranscript, transcript::Transcript}}; + +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +#[derive(Debug, Clone, Default)] +pub struct ZeromorphSRS { + pub g1_powers: Vec, + pub g2_powers: Vec, +} + +impl ZeromorphSRS

{ + + pub fn setup() -> ZeromorphSRS

{ + //TODO: Fuck this pig + todo!() + } + + pub fn trim( + &self, + max_degree: usize, + ) -> Result<(ZeromorphProverKey

, ZeromorphVerifierKey

), ZeromorphError> { + if max_degree > self.g1_powers.len() { + return Err(ZeromorphError::KeyLengthError( + max_degree, + self.g1_powers.len(), + )); + } + let offset = self.g1_powers.len() - max_degree; + let offset_g1_powers = self.g1_powers[offset..].to_vec(); + Ok(( + ZeromorphProverKey { + g1_powers: self.g1_powers.clone(), + offset_g1_powers: offset_g1_powers, + }, + ZeromorphVerifierKey { + g1: self.g1_powers[0], + g2: self.g2_powers[0], + tau_2: self.g2_powers[1], + tau_n_max_sub_2_n: self.g2_powers[offset], + }, + )) + } +} + +#[derive(Clone, Debug)] +pub struct ZeromorphProverKey { + pub g1_powers: Vec, + pub offset_g1_powers: Vec, +} + +#[derive(Copy, Clone, Debug)] +pub struct ZeromorphVerifierKey { + pub g1: P::G1Point, + pub g2: P::G2Point, + pub tau_2: P::G2Point, + pub tau_n_max_sub_2_n: P::G2Point, +} + +#[derive(Clone, Debug)] +pub struct ZeromorphProof { + pub pi: P::G1Point, + pub q_hat_com: P::G1Point, + pub q_k_com: Vec, +} + +#[derive(Debug)] +pub enum ZeromorphError { + //#[error("Length Error: SRS Length: {0}, Key Length: {0}")] + KeyLengthError(usize, usize), +} + +fn compute_multilinear_quotients( + poly: &DenseMultilinearPolynomial, + u_challenge: &[FieldElement], +) -> (Vec>>, FieldElement) +where +<

::BaseField as IsField>::BaseType: Send + Sync, +{ + assert_eq!(poly.num_vars(), u_challenge.len()); + + let mut g = poly.evals().to_vec(); + let mut quotients: Vec<_> = u_challenge + .iter() + .enumerate() + .map(|(i, x_i)| { + let (g_lo, g_hi) = g.split_at_mut(1 << (poly.num_vars() - 1 - i)); + let mut quotient = vec![FieldElement::zero(); g_lo.len()]; + + #[cfg(feature = "parallel")] + let quotient_iter = quotient.par_iter_mut(); + + #[cfg(not(feature = "parallel"))] + let quotient_iter = quotient.iter_mut(); + + quotient_iter + .zip(&*g_lo) + .zip(&*g_hi) + .for_each(|((q, g_lo), g_hi)| { + *q = *g_hi - *g_lo; + }); + + #[cfg(feature = "parallel")] + let g_lo_iter = g_lo.par_iter_mut(); + + #[cfg(not(feature = "parallel"))] + let g_lo_iter = g_lo.iter_mut(); + g_lo_iter.zip(g_hi).for_each(|(g_lo, g_hi)| { + *g_lo += (*g_hi - g_lo as &_) * x_i; + }); + + g.truncate(1 << (poly.num_vars() - 1 - i)); + + Polynomial::new("ient) + }) + .collect(); + quotients.reverse(); + (quotients, g[0]) +} + +fn compute_batched_lifted_degree_quotient( + n: usize, + quotients: &Vec>>, + y_challenge: &FieldElement, +) -> Polynomial> { + // Compute \hat{q} = \sum_k y^k * X^{N - d_k - 1} * q_k + let mut scalar = FieldElement::::one(); // y^k + // Rather than explicitly computing the shifts of q_k by N - d_k - 1 (i.e. multiplying q_k by X^{N - d_k - 1}) + // then accumulating them, we simply accumulate y^k*q_k into \hat{q} at the index offset N - d_k - 1 + let q_hat = + quotients + .iter() + .enumerate() + .fold(vec![FieldElement::zero(); n], |mut q_hat, (idx, q)| { + #[cfg(feature = "parallel")] + let q_hat_iter = q_hat[n - (1 << idx)..].par_iter_mut(); + + #[cfg(not(feature = "parallel"))] + let q_hat_iter = q_hat[n - (1 << idx)..].iter_mut(); + q_hat_iter.zip(&q.coefficients).for_each(|(q_hat, q)| { + *q_hat += scalar * q; + }); + scalar *= y_challenge; + q_hat + }); + + Polynomial::new(&q_hat) +} + +fn eval_and_quotient_scalars( + y_challenge: FieldElement, + x_challenge: FieldElement, + z_challenge: FieldElement, + challenges: &[FieldElement], +) -> (FieldElement, (Vec>, Vec>)) { + let num_vars = challenges.len(); + + // squares of x = [x, x^2, .. x^{2^k}, .. x^{2^num_vars}] + let squares_of_x: Vec<_> = iter::successors(Some(x_challenge), |&x| Some(x.square())) + .take(num_vars + 1) + .collect(); + + // offsets of x = + let offsets_of_x = { + let mut offsets_of_x = squares_of_x + .iter() + .rev() + .skip(1) + .scan(FieldElement::one(), |acc, pow_x| { + *acc *= pow_x; + Some(*acc) + }) + .collect::>(); + offsets_of_x.reverse(); + offsets_of_x + }; + + let vs = { + let v_numer: FieldElement = squares_of_x[num_vars] - FieldElement::one(); + let mut v_denoms = squares_of_x + .iter() + .map(|squares_of_x| *squares_of_x - FieldElement::one()) + .collect::>(); + //TODO: catch this unwrap() + FieldElement::inplace_batch_inverse(&mut v_denoms).unwrap(); + v_denoms.iter() + .map(|v_denom| v_numer * v_denom) + .collect::>() + }; + + let q_scalars = iter::successors(Some(FieldElement::one()), |acc| Some(*acc * y_challenge)) + .take(num_vars) + .zip(offsets_of_x) + .zip(squares_of_x) + .zip(&vs) + .zip(&vs[1..]) + .zip(challenges.iter().rev()) + .map( + |(((((power_of_y, offset_of_x), square_of_x), v_i), v_j), u_i)| { + ( + -(power_of_y * offset_of_x), + -(z_challenge * (square_of_x * v_j - *u_i * v_i)), + ) + }, + ) + .unzip(); + + // -vs[0] * z = -z * (x^(2^num_vars) - 1) / (x - 1) = -z Ξ¦_n(x) + (-vs[0] * z_challenge, q_scalars) +} + +pub struct Zeromorph { + _phantom: PhantomData

, +} + +impl PolynomialCommitmentScheme for Zeromorph

+where +<

::BaseField as IsField>::BaseType: Send + Sync, +FieldElement<

::BaseField>: ByteConversion, +//TODO: how to eliminate this complication in the trait interface +

::BaseField: IsPrimeField>, +{ + type Commitment = Vec; + type Polynomial = Vec>; + type Evaluation = Vec>; + type Challenge = Vec>; + type Proof = ZeromorphProof

; + type Error = ZeromorphError; + + type ProverKey = ZeromorphProverKey

; + type CommitmentKey = Self::Commitment; + type VerifierKey = ZeromorphVerifierKey

; + + fn commit( + polys: Self::Polynomial, + ck: impl Borrow, + ) -> Result { + let ck = ck.borrow(); + // TODO: assert lengths are valid + #[cfg(feature = "parallel")] + let iter = polys.par_iter(); + #[cfg(not(feature = "parallel"))] + let iter = polys.iter(); + Ok( + iter + .map(|poly| { + let evals: Vec<_> = poly + .evals() + .iter() + .map(|eval| eval.representative()) + .collect(); + msm(&evals, &ck[..poly.len()]).unwrap() + }) + .collect::>(), + ) + } + + fn prove( + polys: Self::Polynomial, + evals: Self::Evaluation, + challenge: Self::Challenge, + pk: impl Borrow, + transcript: &mut impl Transcript, + ) -> Result { + let num_vars = challenge.len(); + let n: usize = 1 << num_vars; + let pk = pk.borrow(); + + for (poly, eval) in polys.iter().zip(evals.iter()) { + // Note by evaluating we confirm the number of challenges is valid + debug_assert_eq!(poly.evaluate(challenge).unwrap(), *eval); + } + + // Generate batching challenge \rho and powers 1,...,\rho^{m-1} + let rho = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + // Compute batching of unshifted polynomials f_i: + let mut scalar = FieldElement::one(); + let (f_batched, batched_evaluation) = (0..polys.len()).fold( + ( + DenseMultilinearPolynomial::new(vec![FieldElement::zero(); n]), + FieldElement::zero(), + ), + |(mut f_batched, mut batched_evaluation), i| { + f_batched += polys[i].clone() * scalar; + batched_evaluation += scalar * evals[i]; + scalar *= rho; + (f_batched, batched_evaluation) + }, + ); + let mut pi_poly = Polynomial::new(&f_batched.evals()); + + // Compute the multilinear quotients q_k = q_k(X_0, ..., X_{k-1}) + let (quotients, remainder) = compute_multilinear_quotients::

(&f_batched, &challenge); + debug_assert_eq!(quotients.len(), f_batched.num_vars()); + debug_assert_eq!(remainder, batched_evaluation); + + // Compute and send commitments C_{q_k} = [q_k], k = 0, ..., d-1 + let q_k_commitments: Vec<_> = quotients + .iter() + .map(|q| { + let q_k_commitment = UnivariateKZG::

::commit(&pk.g1_powers, q).unwrap(); + transcript.append(&q_k_commitment.as_bytes()); + q_k_commitment + }) + .collect(); + + // Get challenge y + let y_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + // Compute the batched, lifted-degree quotient \hat{q} + let q_hat = compute_batched_lifted_degree_quotient::

(n, "ients, &y_challenge); + + // Compute and send the commitment C_q = [\hat{q}] + // commit at offset + let offset = 1 << (quotients.len() - 1); + let q_hat_com = UnivariateKZG::

::commit_offset(&pk.g1_powers, &q_hat, offset).unwrap(); + transcript.append(&q_hat_com.as_bytes()); + + // Get challenges x and z + let x_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + let z_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + let (eval_scalar, (zeta_degree_check_q_scalars, z_zmpoly_q_scalars)) = + eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &challenge); + // f = z * x * poly.Z + q_hat + (-z * x * Ξ¦_n(x) * e) + x * βˆ‘_k (q_scalars_k * q_k) + pi_poly *= &z_challenge; + pi_poly += &q_hat; + pi_poly[0] += &(batched_evaluation * eval_scalar); + quotients + .into_iter() + .zip(zeta_degree_check_q_scalars) + .zip(z_zmpoly_q_scalars) + .for_each(|((mut q, zeta_degree_check_q_scalar), z_zmpoly_q_scalar)| { + q *= &(zeta_degree_check_q_scalar + z_zmpoly_q_scalar); + pi_poly += &q; + }); + + debug_assert_eq!(pi_poly.evaluate(&x_challenge), FieldElement::::zero()); + + // Compute the KZG opening proof pi_poly; -> TODO should abstract into separate trait + let (pi, _) = UnivariateKZG::

::open(&pk.offset_g1_powers, &pi_poly, &x_challenge).unwrap(); + + Ok(ZeromorphProof { + pi, + q_hat_com, + q_k_com: q_k_commitments, + }) + } + + fn verify( + commitments: Self::Commitment, + evals: Self::Evaluation, + challenges: Self::Challenge, + vk: impl Borrow, + transcript: &mut impl Transcript, + proof: Self::Proof, + ) -> Result<(), Self::Error> { + debug_assert_eq!(evals.len(), commitments.len()); + let vk = vk.borrow(); + let ZeromorphProof { + pi, + q_k_com, + q_hat_com, + } = proof; + + //Receive q_k commitments + q_k_com + .iter() + .for_each(|c| transcript.append(&c.as_bytes())); + + // Compute powers of batching challenge rho + let rho = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + // Compute batching of unshifted polynomials f_i: + let mut scalar = FieldElement::::one(); + let (batched_evaluation, batched_commitment) = evals.iter().zip(commitments.iter()).fold( + (FieldElement::zero(), FieldElement::zero()), + |(mut batched_evaluation, mut batched_commitment), (eval, commitment)| { + batched_evaluation += scalar * eval; + batched_commitment += *commitment * scalar; + scalar *= rho; + (batched_evaluation, batched_commitment) + }, + ); + + // Challenge y + let y_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + // Receive commitment C_{q} -> Since our transcript does not support appending and receiving data we instead store these commitments in a zeromorph proof struct + transcript.append(&q_hat_com.as_bytes()); + + // Challenge x, z + let x_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + let z_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + let (eval_scalar, (mut q_scalars, zm_poly_q_scalars)) = + eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &challenges); + + q_scalars + .iter_mut() + .zip(zm_poly_q_scalars) + .for_each(|(scalar, zm_poly_scalar)| { + *scalar += zm_poly_scalar; + }); + + let scalars = [ + vec![ + FieldElement::one(), + z_challenge, + batched_evaluation * eval_scalar, + ], + q_scalars, + ] + .concat(); + + let bases = [ + vec![q_hat_com, batched_commitment, vk.g1], + q_k_com, + ] + .concat(); + let zeta_z_com = msm( + &bases, + &scalars + ) + .expect("`points` is sliced by `cs`'s length"); + + // e(pi, [tau]_2 - x * [1]_2) == e(C_{\zeta,Z}, [X^(N_max - 2^n - 1)]_2) <==> e(C_{\zeta,Z} - x * pi, [X^{N_max - 2^n - 1}]_2) * e(-pi, [tau_2]) == 1 + let e = P::compute_batch(&[(&pi, &(vk.tau_2 - (vk.g2.operate_with_self(x_challenge.representative())))), (&zeta_z_com, &vk.tau_N_max_sub_2_N)]).unwrap(); + assert_eq!(e, FieldElement::one()); + Ok(()) + } +} + +#[cfg(test)] +mod test { + + use crate::fiat_shamir::transcript::Transcript; + + use super::*; + + // Evaluate Phi_k(x) = \sum_{i=0}^k x^i using the direct inefficent formula + fn phi(challenge: &FieldElement, subscript: usize) -> FieldElement { + let len = (1 << subscript) as u64; + (0..len) + .into_iter() + .fold(FieldElement::::zero(), |mut acc, i| { + //Note this is ridiculous DevX + acc += challenge.pow(BigInt::<1>::from(i)); + acc + }) + } + + #[test] + fn prove_verify_single() { + let max_vars = 16; + let mut rng = test_rng(); + let srs = ZEROMORPH_SRS.lock().unwrap(); + + for num_vars in 3..max_vars { + // Setup + let (pk, vk) = { + let poly_size = 1 << (num_vars + 1); + srs.trim(poly_size - 1).unwrap() + }; + let polys = DenseMultilinearPolynomial::new( + (0..(1 << num_vars)) + .map(|_| Fr::rand(&mut rng)) + .collect::>(), + ); + let challenges = (0..num_vars) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let evals = polys.evaluate(&challenges); + + // Commit and open + let commitments = Zeromorph::::commit(vec![polys.clone()], &pk.g1_powers).unwrap(); + + let mut prover_transcript = Transcript::new(b"example"); + let proof = Zeromorph::::prove( + vec![polys], + vec![evals], + challenges, + &pk, + &mut prover_transcript, + ) + .unwrap(); + + let mut verifier_transcript = Transcript::new(b"example"); + Zeromorph::::verify( + commitments, + vec![evals], + challenges, + &vk, + &mut verifier_transcript, + proof, + ) + .unwrap(); + + //TODO: check both random oracles are synced + } +} + +#[test] +fn prove_verify_batched() { +let max_vars = 16; +let mut rng = test_rng(); +let num_polys = 8; +let srs = ; + +for num_vars in 3..max_vars { + // Setup + let (pk, vk) = { + let poly_size = 1 << (num_vars + 1); + srs.trim(poly_size - 1).unwrap() + }; + let polys: Vec> = (0..num_polys) + .map(|_| { + DenseMultilinearPolynomial::new( + (0..(1 << num_vars)) + .map(|_| Fr::rand(&mut rng)) + .collect::>(), + ) + }) + .collect::>(); + let challenges = (0..num_vars) + .into_iter() + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let evals = polys + .clone() + .into_iter() + .map(|poly| poly.evaluate(&challenges)) + .collect::>(); + + // Commit and open + let commitments = Zeromorph::::commit(polys, &pk.g1_powers).unwrap(); + + let mut prover_transcript = Transcript::new(b"example"); + let proof = + Zeromorph::::prove(polys, evals, challenges, &pk, &mut prover_transcript).unwrap(); + + let mut verifier_transcript = Transcript::new(b"example"); + Zeromorph::::verify( + commitments, + evals, + challenges, + &vk, + &mut verifier_transcript, + proof, + ) + .unwrap(); + + //TODO: check both random oracles are synced +} +} + +/// Test for computing qk given multilinear f +/// Given 𝑓(𝑋₀, …, 𝑋ₙ₋₁), and `(𝑒, 𝑣)` such that \f(\u) = \v, compute `qβ‚–(𝑋₀, …, 𝑋ₖ₋₁)` +/// such that the following identity holds: +/// +/// `𝑓(𝑋₀, …, 𝑋ₙ₋₁) βˆ’ 𝑣 = βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ (𝑋ₖ βˆ’ 𝑒ₖ) qβ‚–(𝑋₀, …, 𝑋ₖ₋₁)` +#[test] +fn quotient_construction() { +// Define size params +let num_vars = 4; +let n: u64 = 1 << num_vars; + +// Construct a random multilinear polynomial f, and (u,v) such that f(u) = v +let mut rng = test_rng(); +let multilinear_f = + DenseMultilinearPolynomial::new((0..n).map(|_| Fr::rand(&mut rng)).collect::>()); +let u_challenge = (0..num_vars) + .into_iter() + .map(|_| Fr::rand(&mut rng)) + .collect::>(); +let v_evaluation = multilinear_f.evaluate(&u_challenge); + +// Compute multilinear quotients `qβ‚–(𝑋₀, …, 𝑋ₖ₋₁)` +let (quotients, constant_term) = + compute_multilinear_quotients::(&multilinear_f, &u_challenge); + +// Assert the constant term is equal to v_evaluation +assert_eq!( + constant_term, v_evaluation, + "The constant term equal to the evaluation of the polynomial at challenge point." +); + +//To demonstrate that q_k was properly constructd we show that the identity holds at a random multilinear challenge +// i.e. 𝑓(𝑧) βˆ’ 𝑣 βˆ’ βˆ‘β‚–β‚Œβ‚€α΅ˆβ»ΒΉ (𝑧ₖ βˆ’ 𝑒ₖ)π‘žβ‚–(𝑧) = 0 +let z_challenge = (0..num_vars) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + +let mut res = multilinear_f.evaluate(&z_challenge); +res -= v_evaluation; + +for (k, q_k_uni) in quotients.iter().enumerate() { + let z_partial = &z_challenge[&z_challenge.len() - k..]; + //This is a weird consequence of how things are done.. the univariate polys are of the multilinear commitment in lagrange basis. Therefore we evaluate as multilinear + let q_k = DenseMultilinearPolynomial::new(q_k_uni.coeffs.clone()); + let q_k_eval = q_k.evaluate(z_partial); + + res -= (z_challenge[z_challenge.len() - k - 1] - u_challenge[z_challenge.len() - k - 1]) + * q_k_eval; +} +assert!(res.is_zero()); +} + +/// Test for construction of batched lifted degree quotient: +/// Μ‚q = βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ yᡏ Xᡐ⁻ᡈᡏ⁻¹ Μ‚qβ‚–, 𝑑ₖ = deg(Μ‚q), π‘š = 𝑁 +#[test] +fn batched_lifted_degree_quotient() { +let num_vars = 3; +let n = 1 << num_vars; + +// Define mock qβ‚– with deg(qβ‚–) = 2ᡏ⁻¹ +let q_0 = Polynomial::from_coeff(vec![Fr::one()]); +let q_1 = Polynomial::from_coeff(vec![Fr::from(2u64), Fr::from(3u64)]); +let q_2 = Polynomial::from_coeff(vec![ + Fr::from(4u64), + Fr::from(5u64), + Fr::from(6u64), + Fr::from(7u64), +]); +let quotients = vec![q_0, q_1, q_2]; + +let mut rng = test_rng(); +let y_challenge = Fr::rand(&mut rng); + +//Compute batched quptient Μ‚q +let batched_quotient = + compute_batched_lifted_degree_quotient::(n, "ients, &y_challenge); + +//Explicitly define q_k_lifted = X^{N-2^k} * q_k and compute the expected batched result +let q_0_lifted = Polynomial::from_coeff(vec![ + Fr::zero(), + Fr::zero(), + Fr::zero(), + Fr::zero(), + Fr::zero(), + Fr::zero(), + Fr::zero(), + Fr::one(), +]); +let q_1_lifted = Polynomial::from_coeff(vec![ + Fr::zero(), + Fr::zero(), + Fr::zero(), + Fr::zero(), + Fr::zero(), + Fr::zero(), + Fr::from(2u64), + Fr::from(3u64), +]); +let q_2_lifted = Polynomial::from_coeff(vec![ + Fr::zero(), + Fr::zero(), + Fr::zero(), + Fr::zero(), + Fr::from(4u64), + Fr::from(5u64), + Fr::from(6u64), + Fr::from(7u64), +]); + +//Explicitly compute Μ‚q i.e. RLC of lifted polys +let mut batched_quotient_expected = Polynomial::from_coeff(vec![Fr::zero(); n]); + +batched_quotient_expected += &q_0_lifted; +batched_quotient_expected += &(q_1_lifted * y_challenge); +batched_quotient_expected += &(q_2_lifted * (y_challenge * y_challenge)); +assert_eq!(batched_quotient, batched_quotient_expected); +} + +/// evaluated quotient \zeta_x +/// +/// 𝜁 = 𝑓 βˆ’ βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉπ‘¦α΅π‘₯ʷ˒⁻ʷ⁺¹𝑓ₖ = 𝑓 βˆ’ βˆ‘_{d ∈ {dβ‚€, ..., dₙ₋₁}} X^{d* - d + 1} βˆ’ βˆ‘{k∢ dβ‚–=d} yᡏ fβ‚– , where d* = lifted degree +/// +/// 𝜁 = Μ‚q - βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ yᡏ Xᡐ⁻ᡈᡏ⁻¹ Μ‚qβ‚–, m = N +#[test] +fn partially_evaluated_quotient_zeta() { +let num_vars = 3; +let n: u64 = 1 << num_vars; + +let mut rng = test_rng(); +let x_challenge = Fr::rand(&mut rng); +let y_challenge = Fr::rand(&mut rng); + +let challenges: Vec<_> = (0..num_vars).map(|_| Fr::rand(&mut rng)).collect(); +let z_challenge = Fr::rand(&mut rng); + +let (_, (zeta_x_scalars, _)) = + eval_and_quotient_scalars::(y_challenge, x_challenge, z_challenge, &challenges); + +// To verify we manually compute zeta using the computed powers and expected +// 𝜁 = Μ‚q - βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ yᡏ Xᡐ⁻ᡈᡏ⁻¹ Μ‚qβ‚–, m = N +assert_eq!( + zeta_x_scalars[0], + -x_challenge.pow(BigInt::<1>::from((n - 1) as u64)) +); + +assert_eq!( + zeta_x_scalars[1], + -y_challenge * x_challenge.pow(BigInt::<1>::from((n - 1 - 1) as u64)) +); + +assert_eq!( + zeta_x_scalars[2], + -y_challenge * y_challenge * x_challenge.pow(BigInt::<1>::from((n - 3 - 1) as u64)) +); +} + +/// Test efficiently computing 𝛷ₖ(x) = βˆ‘α΅’β‚Œβ‚€α΅β»ΒΉxⁱ +/// 𝛷ₖ(π‘₯) = βˆ‘α΅’β‚Œβ‚€α΅β»ΒΉπ‘₯ⁱ = (π‘₯Β²^ᡏ βˆ’ 1) / (π‘₯ βˆ’ 1) +#[test] +fn phi_n_x_evaluation() { +const N: u64 = 8u64; +let log_N = (N as usize).log_2(); + +// 𝛷ₖ(π‘₯) +let mut rng = test_rng(); +let x_challenge = Fr::rand(&mut rng); + +let efficient = (x_challenge.pow(BigInt::<1>::from((1 << log_N) as u64)) - Fr::one()) + / (x_challenge - Fr::one()); +let expected: Fr = phi::(&x_challenge, log_N); +assert_eq!(efficient, expected); +} + +/// Test efficiently computing 𝛷ₖ(x) = βˆ‘α΅’β‚Œβ‚€α΅β»ΒΉxⁱ +/// 𝛷ₙ₋ₖ₋₁(π‘₯Β²^ᡏ⁺¹) = (π‘₯Β²^ⁿ βˆ’ 1) / (π‘₯Β²^ᡏ⁺¹ βˆ’ 1) +#[test] +fn phi_n_k_1_x_evaluation() { +const N: u64 = 8u64; +let log_N = (N as usize).log_2(); + +// 𝛷ₖ(π‘₯) +let mut rng = test_rng(); +let x_challenge = Fr::rand(&mut rng); +let k = 2; + +//π‘₯Β²^ᡏ⁺¹ +let x_pow = x_challenge.pow(BigInt::<1>::from((1 << (k + 1)) as u64)); + +//(π‘₯Β²^ⁿ βˆ’ 1) / (π‘₯Β²^ᡏ⁺¹ βˆ’ 1) +let efficient = + (x_challenge.pow(BigInt::<1>::from((1 << log_N) as u64)) - Fr::one()) / (x_pow - Fr::one()); +let expected: Fr = phi::(&x_challenge, log_N - k - 1); +assert_eq!(efficient, expected); +} + +/// Test construction of 𝑍ₓ +/// 𝑍ₓ = ̂𝑓 βˆ’ 𝑣 βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ(π‘₯Β²^ᡏ𝛷ₙ₋ₖ₋₁(π‘₯ᡏ⁺¹)βˆ’ 𝑒ₖ𝛷ₙ₋ₖ(π‘₯Β²^ᡏ)) Μ‚qβ‚– +#[test] +fn partially_evaluated_quotient_z_x() { +let num_vars = 3; + +// Construct a random multilinear polynomial f, and (u,v) such that f(u) = v. +let mut rng = test_rng(); +let challenges: Vec<_> = (0..num_vars) + .into_iter() + .map(|_| Fr::rand(&mut rng)) + .collect(); + +let u_rev = { + let mut res = challenges.clone(); + res.reverse(); + res +}; + +let x_challenge = Fr::rand(&mut rng); +let y_challenge = Fr::rand(&mut rng); +let z_challenge = Fr::rand(&mut rng); + +// Construct Z_x scalars +let (_, (_, z_x_scalars)) = + eval_and_quotient_scalars::(y_challenge, x_challenge, z_challenge, &challenges); + +for k in 0..num_vars { + let x_pow_2k = x_challenge.pow(BigInt::<1>::from((1 << k) as u64)); // x^{2^k} + let x_pow_2kp1 = x_challenge.pow(BigInt::<1>::from((1 << (k + 1)) as u64)); // x^{2^{k+1}} + // x^{2^k} * \Phi_{n-k-1}(x^{2^{k+1}}) - u_k * \Phi_{n-k}(x^{2^k}) + let mut scalar = x_pow_2k * &phi::(&x_pow_2kp1, num_vars - k - 1) + - u_rev[k] * &phi::(&x_pow_2k, num_vars - k); + scalar *= z_challenge; + scalar *= Fr::from(-1); + assert_eq!(z_x_scalars[k], scalar); +} +} +} \ No newline at end of file diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs index b0c0c6cd2..ba7c1d24e 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs @@ -26,6 +26,7 @@ impl IsPairing for BLS12381AtePairing { type G1Point = ShortWeierstrassProjectivePoint; type G2Point = ShortWeierstrassProjectivePoint; type OutputField = Degree12ExtensionField; + type BaseField = BLS12381PrimeField; /// Compute the product of the ate pairings for a list of point pairs. fn compute_batch( diff --git a/math/src/elliptic_curve/traits.rs b/math/src/elliptic_curve/traits.rs index 1e3355b48..1c6ff847c 100644 --- a/math/src/elliptic_curve/traits.rs +++ b/math/src/elliptic_curve/traits.rs @@ -40,6 +40,7 @@ pub trait IsPairing { type G1Point: IsGroup; type G2Point: IsGroup; type OutputField: IsField; + type BaseField: IsField; /// Compute the product of the pairings for a list of point pairs. fn compute_batch( From 2760bd0e84fb592f87c2637fbcaf13786fa932b9 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Mon, 22 Jan 2024 18:43:24 -0600 Subject: [PATCH 12/23] more fixes --- crypto/src/commitments/zeromorph/zeromorph.rs | 151 +++++++++--------- 1 file changed, 74 insertions(+), 77 deletions(-) diff --git a/crypto/src/commitments/zeromorph/zeromorph.rs b/crypto/src/commitments/zeromorph/zeromorph.rs index 98a97dc0f..227b04248 100644 --- a/crypto/src/commitments/zeromorph/zeromorph.rs +++ b/crypto/src/commitments/zeromorph/zeromorph.rs @@ -1,16 +1,16 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::type_complexity)] -use std::{borrow::Borrow, iter, marker::PhantomData, sync::Mutex}; -use alloc::sync::Arc; +use std::{borrow::Borrow, iter, marker::PhantomData,}; use lambdaworks_math::{ cyclic_group::IsGroup, elliptic_curve::{traits::IsPairing, short_weierstrass::curves::bls12_381::pairing::BLS12381AtePairing}, field::{element::FieldElement, traits::{IsField, IsPrimeField}}, msm::pippenger::msm, polynomial::{Polynomial, dense_multilinear_poly::DenseMultilinearPolynomial}, traits::{AsBytes, ByteConversion}, unsigned_integer::element::UnsignedInteger }; -use crate::{commitments::{kzg::StructuredReferenceString, traits::PolynomialCommitmentScheme}, fiat_shamir::{default_transcript::DefaultTranscript, transcript::Transcript}}; +use crate::{commitments::{traits::PolynomialCommitmentScheme}, fiat_shamir::{default_transcript::DefaultTranscript, transcript::Transcript}}; #[cfg(feature = "parallel")] use rayon::prelude::*; +//TODO: gate with alloc #[derive(Debug, Clone, Default)] pub struct ZeromorphSRS { pub g1_powers: Vec, @@ -20,7 +20,6 @@ pub struct ZeromorphSRS { impl ZeromorphSRS

{ pub fn setup() -> ZeromorphSRS

{ - //TODO: Fuck this pig todo!() } @@ -289,7 +288,7 @@ FieldElement<

::BaseField>: ByteConversion, FieldElement::zero(), ), |(mut f_batched, mut batched_evaluation), i| { - f_batched += polys[i].clone() * scalar; + f_batched = (f_batched + polys[i].clone() * scalar).unwrap(); batched_evaluation += scalar * evals[i]; scalar *= rho; (f_batched, batched_evaluation) @@ -331,8 +330,8 @@ FieldElement<

::BaseField>: ByteConversion, let (eval_scalar, (zeta_degree_check_q_scalars, z_zmpoly_q_scalars)) = eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &challenge); // f = z * x * poly.Z + q_hat + (-z * x * Ξ¦_n(x) * e) + x * βˆ‘_k (q_scalars_k * q_k) - pi_poly *= &z_challenge; - pi_poly += &q_hat; + pi_poly = pi_poly * &z_challenge; + pi_poly = pi_poly + &q_hat; pi_poly[0] += &(batched_evaluation * eval_scalar); quotients .into_iter() @@ -385,7 +384,7 @@ FieldElement<

::BaseField>: ByteConversion, (FieldElement::zero(), FieldElement::zero()), |(mut batched_evaluation, mut batched_commitment), (eval, commitment)| { batched_evaluation += scalar * eval; - batched_commitment += *commitment * scalar; + batched_commitment += commitment.operate_with_self(scalar.representative()); scalar *= rho; (batched_evaluation, batched_commitment) }, @@ -427,8 +426,8 @@ FieldElement<

::BaseField>: ByteConversion, ] .concat(); let zeta_z_com = msm( - &bases, - &scalars + &scalars, + &bases ) .expect("`points` is sliced by `cs`'s length"); @@ -442,18 +441,16 @@ FieldElement<

::BaseField>: ByteConversion, #[cfg(test)] mod test { - use crate::fiat_shamir::transcript::Transcript; - use super::*; // Evaluate Phi_k(x) = \sum_{i=0}^k x^i using the direct inefficent formula - fn phi(challenge: &FieldElement, subscript: usize) -> FieldElement { + fn phi(challenge: &FieldElement, subscript: usize) -> FieldElement { let len = (1 << subscript) as u64; (0..len) .into_iter() - .fold(FieldElement::::zero(), |mut acc, i| { + .fold(FieldElement::::zero(), |mut acc, i| { //Note this is ridiculous DevX - acc += challenge.pow(BigInt::<1>::from(i)); + acc += challenge.pow(i); acc }) } @@ -478,12 +475,12 @@ mod test { let challenges = (0..num_vars) .map(|_| Fr::rand(&mut rng)) .collect::>(); - let evals = polys.evaluate(&challenges); + let evals = polys.evaluate(&challenges).unwrap(); // Commit and open let commitments = Zeromorph::::commit(vec![polys.clone()], &pk.g1_powers).unwrap(); - let mut prover_transcript = Transcript::new(b"example"); + let mut prover_transcript = DefaultTranscript::new(); let proof = Zeromorph::::prove( vec![polys], vec![evals], @@ -493,7 +490,7 @@ mod test { ) .unwrap(); - let mut verifier_transcript = Transcript::new(b"example"); + let mut verifier_transcript = DefaultTranscript::new(); Zeromorph::::verify( commitments, vec![evals], @@ -513,7 +510,7 @@ fn prove_verify_batched() { let max_vars = 16; let mut rng = test_rng(); let num_polys = 8; -let srs = ; +let srs = ZeromorphSRS; for num_vars in 3..max_vars { // Setup @@ -521,7 +518,7 @@ for num_vars in 3..max_vars { let poly_size = 1 << (num_vars + 1); srs.trim(poly_size - 1).unwrap() }; - let polys: Vec> = (0..num_polys) + let polys: Vec> = (0..num_polys) .map(|_| { DenseMultilinearPolynomial::new( (0..(1 << num_vars)) @@ -537,17 +534,17 @@ for num_vars in 3..max_vars { let evals = polys .clone() .into_iter() - .map(|poly| poly.evaluate(&challenges)) + .map(|poly| poly.evaluate(&challenges).unwrap()) .collect::>(); // Commit and open let commitments = Zeromorph::::commit(polys, &pk.g1_powers).unwrap(); - let mut prover_transcript = Transcript::new(b"example"); + let mut prover_transcript = DefaultTranscript::new(); let proof = Zeromorph::::prove(polys, evals, challenges, &pk, &mut prover_transcript).unwrap(); - let mut verifier_transcript = Transcript::new(b"example"); + let mut verifier_transcript = DefaultTranscript::new(); Zeromorph::::verify( commitments, evals, @@ -581,7 +578,7 @@ let u_challenge = (0..num_vars) .into_iter() .map(|_| Fr::rand(&mut rng)) .collect::>(); -let v_evaluation = multilinear_f.evaluate(&u_challenge); +let v_evaluation = multilinear_f.evaluate(&u_challenge).unwrap(); // Compute multilinear quotients `qβ‚–(𝑋₀, …, 𝑋ₖ₋₁)` let (quotients, constant_term) = @@ -599,19 +596,19 @@ let z_challenge = (0..num_vars) .map(|_| Fr::rand(&mut rng)) .collect::>(); -let mut res = multilinear_f.evaluate(&z_challenge); -res -= v_evaluation; +let mut res = multilinear_f.evaluate(&z_challenge).unwrap(); +res = res - v_evaluation; for (k, q_k_uni) in quotients.iter().enumerate() { let z_partial = &z_challenge[&z_challenge.len() - k..]; //This is a weird consequence of how things are done.. the univariate polys are of the multilinear commitment in lagrange basis. Therefore we evaluate as multilinear - let q_k = DenseMultilinearPolynomial::new(q_k_uni.coeffs.clone()); + let q_k = DenseMultilinearPolynomial::new(q_k_uni.coefficients.clone()); let q_k_eval = q_k.evaluate(z_partial); res -= (z_challenge[z_challenge.len() - k - 1] - u_challenge[z_challenge.len() - k - 1]) * q_k_eval; } -assert!(res.is_zero()); +assert_eq!(res, FieldElement::zero()); } /// Test for construction of batched lifted degree quotient: @@ -622,13 +619,13 @@ let num_vars = 3; let n = 1 << num_vars; // Define mock qβ‚– with deg(qβ‚–) = 2ᡏ⁻¹ -let q_0 = Polynomial::from_coeff(vec![Fr::one()]); -let q_1 = Polynomial::from_coeff(vec![Fr::from(2u64), Fr::from(3u64)]); -let q_2 = Polynomial::from_coeff(vec![ - Fr::from(4u64), - Fr::from(5u64), - Fr::from(6u64), - Fr::from(7u64), +let q_0 = Polynomial::new(&[FieldElement::one()]); +let q_1 = Polynomial::new(&[FieldElement::from(2u64), FieldElement::from(3u64)]); +let q_2 = Polynomial::new(&[ + FieldElement::from(4u64), + FieldElement::from(5u64), + FieldElement::from(6u64), + FieldElement::from(7u64), ]); let quotients = vec![q_0, q_1, q_2]; @@ -640,35 +637,35 @@ let batched_quotient = compute_batched_lifted_degree_quotient::(n, "ients, &y_challenge); //Explicitly define q_k_lifted = X^{N-2^k} * q_k and compute the expected batched result -let q_0_lifted = Polynomial::from_coeff(vec![ - Fr::zero(), - Fr::zero(), - Fr::zero(), - Fr::zero(), - Fr::zero(), - Fr::zero(), - Fr::zero(), - Fr::one(), +let q_0_lifted = Polynomial::new(&[ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::one(), ]); -let q_1_lifted = Polynomial::from_coeff(vec![ - Fr::zero(), - Fr::zero(), - Fr::zero(), - Fr::zero(), - Fr::zero(), - Fr::zero(), - Fr::from(2u64), - Fr::from(3u64), +let q_1_lifted = Polynomial::new(&[ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::from(2u64), + FieldElement::from(3u64), ]); -let q_2_lifted = Polynomial::from_coeff(vec![ - Fr::zero(), - Fr::zero(), - Fr::zero(), - Fr::zero(), - Fr::from(4u64), - Fr::from(5u64), - Fr::from(6u64), - Fr::from(7u64), +let q_2_lifted = Polynomial::new(&[ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::from(4u64), + FieldElement::from(5u64), + FieldElement::from(6u64), + FieldElement::from(7u64), ]); //Explicitly compute Μ‚q i.e. RLC of lifted polys @@ -704,17 +701,17 @@ let (_, (zeta_x_scalars, _)) = // 𝜁 = Μ‚q - βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ yᡏ Xᡐ⁻ᡈᡏ⁻¹ Μ‚qβ‚–, m = N assert_eq!( zeta_x_scalars[0], - -x_challenge.pow(BigInt::<1>::from((n - 1) as u64)) + -x_challenge.pow((n - 1) as u64) ); assert_eq!( zeta_x_scalars[1], - -y_challenge * x_challenge.pow(BigInt::<1>::from((n - 1 - 1) as u64)) + -y_challenge * x_challenge.pow((n - 1 - 1) as u64) ); assert_eq!( zeta_x_scalars[2], - -y_challenge * y_challenge * x_challenge.pow(BigInt::<1>::from((n - 3 - 1) as u64)) + -y_challenge * y_challenge * x_challenge.pow((n - 3 - 1) as u64) ); } @@ -729,9 +726,9 @@ let log_N = (N as usize).log_2(); let mut rng = test_rng(); let x_challenge = Fr::rand(&mut rng); -let efficient = (x_challenge.pow(BigInt::<1>::from((1 << log_N) as u64)) - Fr::one()) - / (x_challenge - Fr::one()); -let expected: Fr = phi::(&x_challenge, log_N); +let efficient = (x_challenge.pow((1 << log_N) as u64) - FieldElement::one()) + / (x_challenge - FieldElement::one()); +let expected: FieldElement<_> = phi::(&x_challenge, log_N); assert_eq!(efficient, expected); } @@ -748,12 +745,12 @@ let x_challenge = Fr::rand(&mut rng); let k = 2; //π‘₯Β²^ᡏ⁺¹ -let x_pow = x_challenge.pow(BigInt::<1>::from((1 << (k + 1)) as u64)); +let x_pow = x_challenge.pow((1 << (k + 1)) as u64); //(π‘₯Β²^ⁿ βˆ’ 1) / (π‘₯Β²^ᡏ⁺¹ βˆ’ 1) let efficient = - (x_challenge.pow(BigInt::<1>::from((1 << log_N) as u64)) - Fr::one()) / (x_pow - Fr::one()); -let expected: Fr = phi::(&x_challenge, log_N - k - 1); + (x_challenge.pow((1 << log_N) as u64) - FieldElement::one()) / (x_pow - FieldElement::one()); +let expected: FieldElement<_> = phi::(&x_challenge, log_N - k - 1); assert_eq!(efficient, expected); } @@ -782,16 +779,16 @@ let z_challenge = Fr::rand(&mut rng); // Construct Z_x scalars let (_, (_, z_x_scalars)) = - eval_and_quotient_scalars::(y_challenge, x_challenge, z_challenge, &challenges); + eval_and_quotient_scalars::(y_challenge, x_challenge, z_challenge, &challenges); for k in 0..num_vars { - let x_pow_2k = x_challenge.pow(BigInt::<1>::from((1 << k) as u64)); // x^{2^k} - let x_pow_2kp1 = x_challenge.pow(BigInt::<1>::from((1 << (k + 1)) as u64)); // x^{2^{k+1}} + let x_pow_2k = x_challenge.pow((1 << k) as u64); // x^{2^k} + let x_pow_2kp1 = x_challenge.pow((1 << (k + 1)) as u64); // x^{2^{k+1}} // x^{2^k} * \Phi_{n-k-1}(x^{2^{k+1}}) - u_k * \Phi_{n-k}(x^{2^k}) - let mut scalar = x_pow_2k * &phi::(&x_pow_2kp1, num_vars - k - 1) - - u_rev[k] * &phi::(&x_pow_2k, num_vars - k); + let mut scalar = x_pow_2k * &phi::(&x_pow_2kp1, num_vars - k - 1) + - u_rev[k] * &phi::(&x_pow_2k, num_vars - k); scalar *= z_challenge; - scalar *= Fr::from(-1); + scalar *= FieldElement::from(1).neg(); assert_eq!(z_x_scalars[k], scalar); } } From 44ea9cbb9ae4ac42f91ab2f594b3b3a8479356bd Mon Sep 17 00:00:00 2001 From: PatStiles Date: Mon, 22 Jan 2024 18:43:55 -0600 Subject: [PATCH 13/23] fmt --- crypto/src/commitments/traits.rs | 63 +- crypto/src/commitments/zeromorph/mod.rs | 2 +- crypto/src/commitments/zeromorph/zeromorph.rs | 1467 +++++++++-------- 3 files changed, 788 insertions(+), 744 deletions(-) diff --git a/crypto/src/commitments/traits.rs b/crypto/src/commitments/traits.rs index 05624a351..021eaa57f 100644 --- a/crypto/src/commitments/traits.rs +++ b/crypto/src/commitments/traits.rs @@ -43,40 +43,39 @@ pub trait IsCommitmentScheme { ) -> bool; } - pub trait PolynomialCommitmentScheme { - // Abstracting over Polynomial allows us to have batched and non-batched PCS - type Polynomial; - type Commitment; - type Evaluation; - type Challenge; - type Proof; - type Error; + // Abstracting over Polynomial allows us to have batched and non-batched PCS + type Polynomial; + type Commitment; + type Evaluation; + type Challenge; + type Proof; + type Error; - type ProverKey; - type CommitmentKey; - type VerifierKey; + type ProverKey; + type CommitmentKey; + type VerifierKey; - //TODO: convert to impl IntoIterator - fn commit( - poly: Self::Polynomial, - ck: impl Borrow, - ) -> Result; + //TODO: convert to impl IntoIterator + fn commit( + poly: Self::Polynomial, + ck: impl Borrow, + ) -> Result; - fn prove( - poly: Self::Polynomial, - evals: Self::Evaluation, - challenges: Self::Challenge, - pk: impl Borrow, - transcript: &mut impl Transcript, - ) -> Result; + fn prove( + poly: Self::Polynomial, + evals: Self::Evaluation, + challenges: Self::Challenge, + pk: impl Borrow, + transcript: &mut impl Transcript, + ) -> Result; - fn verify( - commitments: Self::Commitment, - evals: Self::Evaluation, - challenges: Self::Challenge, - vk: impl Borrow, - transcript: &mut impl Transcript, - proof: Self::Proof, - ) -> Result<(), Self::Error>; -} \ No newline at end of file + fn verify( + commitments: Self::Commitment, + evals: Self::Evaluation, + challenges: Self::Challenge, + vk: impl Borrow, + transcript: &mut impl Transcript, + proof: Self::Proof, + ) -> Result<(), Self::Error>; +} diff --git a/crypto/src/commitments/zeromorph/mod.rs b/crypto/src/commitments/zeromorph/mod.rs index c60c1091b..f6f806424 100644 --- a/crypto/src/commitments/zeromorph/mod.rs +++ b/crypto/src/commitments/zeromorph/mod.rs @@ -1 +1 @@ -pub mod zeromorph; \ No newline at end of file +pub mod zeromorph; diff --git a/crypto/src/commitments/zeromorph/zeromorph.rs b/crypto/src/commitments/zeromorph/zeromorph.rs index 227b04248..782a0b47f 100644 --- a/crypto/src/commitments/zeromorph/zeromorph.rs +++ b/crypto/src/commitments/zeromorph/zeromorph.rs @@ -1,11 +1,25 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::type_complexity)] -use std::{borrow::Borrow, iter, marker::PhantomData,}; +use crate::{ + commitments::traits::PolynomialCommitmentScheme, + fiat_shamir::{default_transcript::DefaultTranscript, transcript::Transcript}, +}; use lambdaworks_math::{ - cyclic_group::IsGroup, elliptic_curve::{traits::IsPairing, short_weierstrass::curves::bls12_381::pairing::BLS12381AtePairing}, field::{element::FieldElement, traits::{IsField, IsPrimeField}}, msm::pippenger::msm, polynomial::{Polynomial, dense_multilinear_poly::DenseMultilinearPolynomial}, traits::{AsBytes, ByteConversion}, unsigned_integer::element::UnsignedInteger + cyclic_group::IsGroup, + elliptic_curve::{ + short_weierstrass::curves::bls12_381::pairing::BLS12381AtePairing, traits::IsPairing, + }, + field::{ + element::FieldElement, + traits::{IsField, IsPrimeField}, + }, + msm::pippenger::msm, + polynomial::{dense_multilinear_poly::DenseMultilinearPolynomial, Polynomial}, + traits::{AsBytes, ByteConversion}, + unsigned_integer::element::UnsignedInteger, }; -use crate::{commitments::{traits::PolynomialCommitmentScheme}, fiat_shamir::{default_transcript::DefaultTranscript, transcript::Transcript}}; +use std::{borrow::Borrow, iter, marker::PhantomData}; #[cfg(feature = "parallel")] use rayon::prelude::*; @@ -13,429 +27,439 @@ use rayon::prelude::*; //TODO: gate with alloc #[derive(Debug, Clone, Default)] pub struct ZeromorphSRS { - pub g1_powers: Vec, - pub g2_powers: Vec, + pub g1_powers: Vec, + pub g2_powers: Vec, } impl ZeromorphSRS

{ + pub fn setup() -> ZeromorphSRS

{ + todo!() + } - pub fn setup() -> ZeromorphSRS

{ - todo!() - } - - pub fn trim( - &self, - max_degree: usize, - ) -> Result<(ZeromorphProverKey

, ZeromorphVerifierKey

), ZeromorphError> { - if max_degree > self.g1_powers.len() { - return Err(ZeromorphError::KeyLengthError( - max_degree, - self.g1_powers.len(), - )); + pub fn trim( + &self, + max_degree: usize, + ) -> Result<(ZeromorphProverKey

, ZeromorphVerifierKey

), ZeromorphError> { + if max_degree > self.g1_powers.len() { + return Err(ZeromorphError::KeyLengthError( + max_degree, + self.g1_powers.len(), + )); + } + let offset = self.g1_powers.len() - max_degree; + let offset_g1_powers = self.g1_powers[offset..].to_vec(); + Ok(( + ZeromorphProverKey { + g1_powers: self.g1_powers.clone(), + offset_g1_powers: offset_g1_powers, + }, + ZeromorphVerifierKey { + g1: self.g1_powers[0], + g2: self.g2_powers[0], + tau_2: self.g2_powers[1], + tau_n_max_sub_2_n: self.g2_powers[offset], + }, + )) } - let offset = self.g1_powers.len() - max_degree; - let offset_g1_powers = self.g1_powers[offset..].to_vec(); - Ok(( - ZeromorphProverKey { - g1_powers: self.g1_powers.clone(), - offset_g1_powers: offset_g1_powers, - }, - ZeromorphVerifierKey { - g1: self.g1_powers[0], - g2: self.g2_powers[0], - tau_2: self.g2_powers[1], - tau_n_max_sub_2_n: self.g2_powers[offset], - }, - )) - } } #[derive(Clone, Debug)] pub struct ZeromorphProverKey { - pub g1_powers: Vec, - pub offset_g1_powers: Vec, + pub g1_powers: Vec, + pub offset_g1_powers: Vec, } #[derive(Copy, Clone, Debug)] pub struct ZeromorphVerifierKey { - pub g1: P::G1Point, - pub g2: P::G2Point, - pub tau_2: P::G2Point, - pub tau_n_max_sub_2_n: P::G2Point, + pub g1: P::G1Point, + pub g2: P::G2Point, + pub tau_2: P::G2Point, + pub tau_n_max_sub_2_n: P::G2Point, } #[derive(Clone, Debug)] pub struct ZeromorphProof { - pub pi: P::G1Point, - pub q_hat_com: P::G1Point, - pub q_k_com: Vec, + pub pi: P::G1Point, + pub q_hat_com: P::G1Point, + pub q_k_com: Vec, } #[derive(Debug)] pub enum ZeromorphError { - //#[error("Length Error: SRS Length: {0}, Key Length: {0}")] - KeyLengthError(usize, usize), + //#[error("Length Error: SRS Length: {0}, Key Length: {0}")] + KeyLengthError(usize, usize), } fn compute_multilinear_quotients( - poly: &DenseMultilinearPolynomial, - u_challenge: &[FieldElement], -) -> (Vec>>, FieldElement) -where -<

::BaseField as IsField>::BaseType: Send + Sync, + poly: &DenseMultilinearPolynomial, + u_challenge: &[FieldElement], +) -> ( + Vec>>, + FieldElement, +) +where + <

::BaseField as IsField>::BaseType: Send + Sync, { - assert_eq!(poly.num_vars(), u_challenge.len()); - - let mut g = poly.evals().to_vec(); - let mut quotients: Vec<_> = u_challenge - .iter() - .enumerate() - .map(|(i, x_i)| { - let (g_lo, g_hi) = g.split_at_mut(1 << (poly.num_vars() - 1 - i)); - let mut quotient = vec![FieldElement::zero(); g_lo.len()]; - - #[cfg(feature = "parallel")] - let quotient_iter = quotient.par_iter_mut(); - - #[cfg(not(feature = "parallel"))] - let quotient_iter = quotient.iter_mut(); - - quotient_iter - .zip(&*g_lo) - .zip(&*g_hi) - .for_each(|((q, g_lo), g_hi)| { - *q = *g_hi - *g_lo; - }); - - #[cfg(feature = "parallel")] - let g_lo_iter = g_lo.par_iter_mut(); - - #[cfg(not(feature = "parallel"))] - let g_lo_iter = g_lo.iter_mut(); - g_lo_iter.zip(g_hi).for_each(|(g_lo, g_hi)| { - *g_lo += (*g_hi - g_lo as &_) * x_i; - }); - - g.truncate(1 << (poly.num_vars() - 1 - i)); - - Polynomial::new("ient) - }) - .collect(); - quotients.reverse(); - (quotients, g[0]) + assert_eq!(poly.num_vars(), u_challenge.len()); + + let mut g = poly.evals().to_vec(); + let mut quotients: Vec<_> = u_challenge + .iter() + .enumerate() + .map(|(i, x_i)| { + let (g_lo, g_hi) = g.split_at_mut(1 << (poly.num_vars() - 1 - i)); + let mut quotient = vec![FieldElement::zero(); g_lo.len()]; + + #[cfg(feature = "parallel")] + let quotient_iter = quotient.par_iter_mut(); + + #[cfg(not(feature = "parallel"))] + let quotient_iter = quotient.iter_mut(); + + quotient_iter + .zip(&*g_lo) + .zip(&*g_hi) + .for_each(|((q, g_lo), g_hi)| { + *q = *g_hi - *g_lo; + }); + + #[cfg(feature = "parallel")] + let g_lo_iter = g_lo.par_iter_mut(); + + #[cfg(not(feature = "parallel"))] + let g_lo_iter = g_lo.iter_mut(); + g_lo_iter.zip(g_hi).for_each(|(g_lo, g_hi)| { + *g_lo += (*g_hi - g_lo as &_) * x_i; + }); + + g.truncate(1 << (poly.num_vars() - 1 - i)); + + Polynomial::new("ient) + }) + .collect(); + quotients.reverse(); + (quotients, g[0]) } fn compute_batched_lifted_degree_quotient( - n: usize, - quotients: &Vec>>, - y_challenge: &FieldElement, + n: usize, + quotients: &Vec>>, + y_challenge: &FieldElement, ) -> Polynomial> { - // Compute \hat{q} = \sum_k y^k * X^{N - d_k - 1} * q_k - let mut scalar = FieldElement::::one(); // y^k - // Rather than explicitly computing the shifts of q_k by N - d_k - 1 (i.e. multiplying q_k by X^{N - d_k - 1}) - // then accumulating them, we simply accumulate y^k*q_k into \hat{q} at the index offset N - d_k - 1 - let q_hat = - quotients - .iter() - .enumerate() - .fold(vec![FieldElement::zero(); n], |mut q_hat, (idx, q)| { - #[cfg(feature = "parallel")] - let q_hat_iter = q_hat[n - (1 << idx)..].par_iter_mut(); - - #[cfg(not(feature = "parallel"))] - let q_hat_iter = q_hat[n - (1 << idx)..].iter_mut(); - q_hat_iter.zip(&q.coefficients).for_each(|(q_hat, q)| { - *q_hat += scalar * q; - }); - scalar *= y_challenge; - q_hat - }); - - Polynomial::new(&q_hat) + // Compute \hat{q} = \sum_k y^k * X^{N - d_k - 1} * q_k + let mut scalar = FieldElement::::one(); // y^k + // Rather than explicitly computing the shifts of q_k by N - d_k - 1 (i.e. multiplying q_k by X^{N - d_k - 1}) + // then accumulating them, we simply accumulate y^k*q_k into \hat{q} at the index offset N - d_k - 1 + let q_hat = + quotients + .iter() + .enumerate() + .fold(vec![FieldElement::zero(); n], |mut q_hat, (idx, q)| { + #[cfg(feature = "parallel")] + let q_hat_iter = q_hat[n - (1 << idx)..].par_iter_mut(); + + #[cfg(not(feature = "parallel"))] + let q_hat_iter = q_hat[n - (1 << idx)..].iter_mut(); + q_hat_iter.zip(&q.coefficients).for_each(|(q_hat, q)| { + *q_hat += scalar * q; + }); + scalar *= y_challenge; + q_hat + }); + + Polynomial::new(&q_hat) } fn eval_and_quotient_scalars( - y_challenge: FieldElement, - x_challenge: FieldElement, - z_challenge: FieldElement, - challenges: &[FieldElement], -) -> (FieldElement, (Vec>, Vec>)) { - let num_vars = challenges.len(); - - // squares of x = [x, x^2, .. x^{2^k}, .. x^{2^num_vars}] - let squares_of_x: Vec<_> = iter::successors(Some(x_challenge), |&x| Some(x.square())) - .take(num_vars + 1) - .collect(); - - // offsets of x = - let offsets_of_x = { - let mut offsets_of_x = squares_of_x - .iter() - .rev() - .skip(1) - .scan(FieldElement::one(), |acc, pow_x| { - *acc *= pow_x; - Some(*acc) - }) - .collect::>(); - offsets_of_x.reverse(); - offsets_of_x - }; - - let vs = { - let v_numer: FieldElement = squares_of_x[num_vars] - FieldElement::one(); - let mut v_denoms = squares_of_x - .iter() - .map(|squares_of_x| *squares_of_x - FieldElement::one()) - .collect::>(); - //TODO: catch this unwrap() - FieldElement::inplace_batch_inverse(&mut v_denoms).unwrap(); - v_denoms.iter() - .map(|v_denom| v_numer * v_denom) - .collect::>() - }; - - let q_scalars = iter::successors(Some(FieldElement::one()), |acc| Some(*acc * y_challenge)) - .take(num_vars) - .zip(offsets_of_x) - .zip(squares_of_x) - .zip(&vs) - .zip(&vs[1..]) - .zip(challenges.iter().rev()) - .map( - |(((((power_of_y, offset_of_x), square_of_x), v_i), v_j), u_i)| { - ( - -(power_of_y * offset_of_x), - -(z_challenge * (square_of_x * v_j - *u_i * v_i)), + y_challenge: FieldElement, + x_challenge: FieldElement, + z_challenge: FieldElement, + challenges: &[FieldElement], +) -> ( + FieldElement, + ( + Vec>, + Vec>, + ), +) { + let num_vars = challenges.len(); + + // squares of x = [x, x^2, .. x^{2^k}, .. x^{2^num_vars}] + let squares_of_x: Vec<_> = iter::successors(Some(x_challenge), |&x| Some(x.square())) + .take(num_vars + 1) + .collect(); + + // offsets of x = + let offsets_of_x = { + let mut offsets_of_x = squares_of_x + .iter() + .rev() + .skip(1) + .scan(FieldElement::one(), |acc, pow_x| { + *acc *= pow_x; + Some(*acc) + }) + .collect::>(); + offsets_of_x.reverse(); + offsets_of_x + }; + + let vs = { + let v_numer: FieldElement = squares_of_x[num_vars] - FieldElement::one(); + let mut v_denoms = squares_of_x + .iter() + .map(|squares_of_x| *squares_of_x - FieldElement::one()) + .collect::>(); + //TODO: catch this unwrap() + FieldElement::inplace_batch_inverse(&mut v_denoms).unwrap(); + v_denoms + .iter() + .map(|v_denom| v_numer * v_denom) + .collect::>() + }; + + let q_scalars = iter::successors(Some(FieldElement::one()), |acc| Some(*acc * y_challenge)) + .take(num_vars) + .zip(offsets_of_x) + .zip(squares_of_x) + .zip(&vs) + .zip(&vs[1..]) + .zip(challenges.iter().rev()) + .map( + |(((((power_of_y, offset_of_x), square_of_x), v_i), v_j), u_i)| { + ( + -(power_of_y * offset_of_x), + -(z_challenge * (square_of_x * v_j - *u_i * v_i)), + ) + }, ) - }, - ) - .unzip(); + .unzip(); - // -vs[0] * z = -z * (x^(2^num_vars) - 1) / (x - 1) = -z Ξ¦_n(x) - (-vs[0] * z_challenge, q_scalars) + // -vs[0] * z = -z * (x^(2^num_vars) - 1) / (x - 1) = -z Ξ¦_n(x) + (-vs[0] * z_challenge, q_scalars) } pub struct Zeromorph { - _phantom: PhantomData

, + _phantom: PhantomData

, } -impl PolynomialCommitmentScheme for Zeromorph

-where -<

::BaseField as IsField>::BaseType: Send + Sync, -FieldElement<

::BaseField>: ByteConversion, -//TODO: how to eliminate this complication in the trait interface -

::BaseField: IsPrimeField>, +impl PolynomialCommitmentScheme for Zeromorph

+where + <

::BaseField as IsField>::BaseType: Send + Sync, + FieldElement<

::BaseField>: ByteConversion, + //TODO: how to eliminate this complication in the trait interface +

::BaseField: IsPrimeField>, { - type Commitment = Vec; - type Polynomial = Vec>; - type Evaluation = Vec>; - type Challenge = Vec>; - type Proof = ZeromorphProof

; - type Error = ZeromorphError; - - type ProverKey = ZeromorphProverKey

; - type CommitmentKey = Self::Commitment; - type VerifierKey = ZeromorphVerifierKey

; - - fn commit( - polys: Self::Polynomial, - ck: impl Borrow, - ) -> Result { - let ck = ck.borrow(); - // TODO: assert lengths are valid - #[cfg(feature = "parallel")] - let iter = polys.par_iter(); - #[cfg(not(feature = "parallel"))] - let iter = polys.iter(); - Ok( - iter - .map(|poly| { - let evals: Vec<_> = poly - .evals() + type Commitment = Vec; + type Polynomial = Vec>; + type Evaluation = Vec>; + type Challenge = Vec>; + type Proof = ZeromorphProof

; + type Error = ZeromorphError; + + type ProverKey = ZeromorphProverKey

; + type CommitmentKey = Self::Commitment; + type VerifierKey = ZeromorphVerifierKey

; + + fn commit( + polys: Self::Polynomial, + ck: impl Borrow, + ) -> Result { + let ck = ck.borrow(); + // TODO: assert lengths are valid + #[cfg(feature = "parallel")] + let iter = polys.par_iter(); + #[cfg(not(feature = "parallel"))] + let iter = polys.iter(); + Ok(iter + .map(|poly| { + let evals: Vec<_> = poly + .evals() + .iter() + .map(|eval| eval.representative()) + .collect(); + msm(&evals, &ck[..poly.len()]).unwrap() + }) + .collect::>()) + } + + fn prove( + polys: Self::Polynomial, + evals: Self::Evaluation, + challenge: Self::Challenge, + pk: impl Borrow, + transcript: &mut impl Transcript, + ) -> Result { + let num_vars = challenge.len(); + let n: usize = 1 << num_vars; + let pk = pk.borrow(); + + for (poly, eval) in polys.iter().zip(evals.iter()) { + // Note by evaluating we confirm the number of challenges is valid + debug_assert_eq!(poly.evaluate(challenge).unwrap(), *eval); + } + + // Generate batching challenge \rho and powers 1,...,\rho^{m-1} + let rho = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + // Compute batching of unshifted polynomials f_i: + let mut scalar = FieldElement::one(); + let (f_batched, batched_evaluation) = (0..polys.len()).fold( + ( + DenseMultilinearPolynomial::new(vec![FieldElement::zero(); n]), + FieldElement::zero(), + ), + |(mut f_batched, mut batched_evaluation), i| { + f_batched = (f_batched + polys[i].clone() * scalar).unwrap(); + batched_evaluation += scalar * evals[i]; + scalar *= rho; + (f_batched, batched_evaluation) + }, + ); + let mut pi_poly = Polynomial::new(&f_batched.evals()); + + // Compute the multilinear quotients q_k = q_k(X_0, ..., X_{k-1}) + let (quotients, remainder) = compute_multilinear_quotients::

(&f_batched, &challenge); + debug_assert_eq!(quotients.len(), f_batched.num_vars()); + debug_assert_eq!(remainder, batched_evaluation); + + // Compute and send commitments C_{q_k} = [q_k], k = 0, ..., d-1 + let q_k_commitments: Vec<_> = quotients .iter() - .map(|eval| eval.representative()) + .map(|q| { + let q_k_commitment = UnivariateKZG::

::commit(&pk.g1_powers, q).unwrap(); + transcript.append(&q_k_commitment.as_bytes()); + q_k_commitment + }) .collect(); - msm(&evals, &ck[..poly.len()]).unwrap() + + // Get challenge y + let y_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + // Compute the batched, lifted-degree quotient \hat{q} + let q_hat = compute_batched_lifted_degree_quotient::

(n, "ients, &y_challenge); + + // Compute and send the commitment C_q = [\hat{q}] + // commit at offset + let offset = 1 << (quotients.len() - 1); + let q_hat_com = UnivariateKZG::

::commit_offset(&pk.g1_powers, &q_hat, offset).unwrap(); + transcript.append(&q_hat_com.as_bytes()); + + // Get challenges x and z + let x_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + let z_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + let (eval_scalar, (zeta_degree_check_q_scalars, z_zmpoly_q_scalars)) = + eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &challenge); + // f = z * x * poly.Z + q_hat + (-z * x * Ξ¦_n(x) * e) + x * βˆ‘_k (q_scalars_k * q_k) + pi_poly = pi_poly * &z_challenge; + pi_poly = pi_poly + &q_hat; + pi_poly[0] += &(batched_evaluation * eval_scalar); + quotients + .into_iter() + .zip(zeta_degree_check_q_scalars) + .zip(z_zmpoly_q_scalars) + .for_each(|((mut q, zeta_degree_check_q_scalar), z_zmpoly_q_scalar)| { + q *= &(zeta_degree_check_q_scalar + z_zmpoly_q_scalar); + pi_poly += &q; + }); + + debug_assert_eq!( + pi_poly.evaluate(&x_challenge), + FieldElement::::zero() + ); + + // Compute the KZG opening proof pi_poly; -> TODO should abstract into separate trait + let (pi, _) = + UnivariateKZG::

::open(&pk.offset_g1_powers, &pi_poly, &x_challenge).unwrap(); + + Ok(ZeromorphProof { + pi, + q_hat_com, + q_k_com: q_k_commitments, }) - .collect::>(), - ) - } - - fn prove( - polys: Self::Polynomial, - evals: Self::Evaluation, - challenge: Self::Challenge, - pk: impl Borrow, - transcript: &mut impl Transcript, - ) -> Result { - let num_vars = challenge.len(); - let n: usize = 1 << num_vars; - let pk = pk.borrow(); - - for (poly, eval) in polys.iter().zip(evals.iter()) { - // Note by evaluating we confirm the number of challenges is valid - debug_assert_eq!(poly.evaluate(challenge).unwrap(), *eval); } - // Generate batching challenge \rho and powers 1,...,\rho^{m-1} - let rho = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - // Compute batching of unshifted polynomials f_i: - let mut scalar = FieldElement::one(); - let (f_batched, batched_evaluation) = (0..polys.len()).fold( - ( - DenseMultilinearPolynomial::new(vec![FieldElement::zero(); n]), - FieldElement::zero(), - ), - |(mut f_batched, mut batched_evaluation), i| { - f_batched = (f_batched + polys[i].clone() * scalar).unwrap(); - batched_evaluation += scalar * evals[i]; - scalar *= rho; - (f_batched, batched_evaluation) - }, - ); - let mut pi_poly = Polynomial::new(&f_batched.evals()); - - // Compute the multilinear quotients q_k = q_k(X_0, ..., X_{k-1}) - let (quotients, remainder) = compute_multilinear_quotients::

(&f_batched, &challenge); - debug_assert_eq!(quotients.len(), f_batched.num_vars()); - debug_assert_eq!(remainder, batched_evaluation); - - // Compute and send commitments C_{q_k} = [q_k], k = 0, ..., d-1 - let q_k_commitments: Vec<_> = quotients - .iter() - .map(|q| { - let q_k_commitment = UnivariateKZG::

::commit(&pk.g1_powers, q).unwrap(); - transcript.append(&q_k_commitment.as_bytes()); - q_k_commitment - }) - .collect(); - - // Get challenge y - let y_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - // Compute the batched, lifted-degree quotient \hat{q} - let q_hat = compute_batched_lifted_degree_quotient::

(n, "ients, &y_challenge); - - // Compute and send the commitment C_q = [\hat{q}] - // commit at offset - let offset = 1 << (quotients.len() - 1); - let q_hat_com = UnivariateKZG::

::commit_offset(&pk.g1_powers, &q_hat, offset).unwrap(); - transcript.append(&q_hat_com.as_bytes()); - - // Get challenges x and z - let x_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - let z_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - let (eval_scalar, (zeta_degree_check_q_scalars, z_zmpoly_q_scalars)) = - eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &challenge); - // f = z * x * poly.Z + q_hat + (-z * x * Ξ¦_n(x) * e) + x * βˆ‘_k (q_scalars_k * q_k) - pi_poly = pi_poly * &z_challenge; - pi_poly = pi_poly + &q_hat; - pi_poly[0] += &(batched_evaluation * eval_scalar); - quotients - .into_iter() - .zip(zeta_degree_check_q_scalars) - .zip(z_zmpoly_q_scalars) - .for_each(|((mut q, zeta_degree_check_q_scalar), z_zmpoly_q_scalar)| { - q *= &(zeta_degree_check_q_scalar + z_zmpoly_q_scalar); - pi_poly += &q; - }); - - debug_assert_eq!(pi_poly.evaluate(&x_challenge), FieldElement::::zero()); - - // Compute the KZG opening proof pi_poly; -> TODO should abstract into separate trait - let (pi, _) = UnivariateKZG::

::open(&pk.offset_g1_powers, &pi_poly, &x_challenge).unwrap(); - - Ok(ZeromorphProof { - pi, - q_hat_com, - q_k_com: q_k_commitments, - }) - } - - fn verify( - commitments: Self::Commitment, - evals: Self::Evaluation, - challenges: Self::Challenge, - vk: impl Borrow, - transcript: &mut impl Transcript, - proof: Self::Proof, - ) -> Result<(), Self::Error> { - debug_assert_eq!(evals.len(), commitments.len()); - let vk = vk.borrow(); - let ZeromorphProof { - pi, - q_k_com, - q_hat_com, - } = proof; - - //Receive q_k commitments - q_k_com - .iter() - .for_each(|c| transcript.append(&c.as_bytes())); - - // Compute powers of batching challenge rho - let rho = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - // Compute batching of unshifted polynomials f_i: - let mut scalar = FieldElement::::one(); - let (batched_evaluation, batched_commitment) = evals.iter().zip(commitments.iter()).fold( - (FieldElement::zero(), FieldElement::zero()), - |(mut batched_evaluation, mut batched_commitment), (eval, commitment)| { - batched_evaluation += scalar * eval; - batched_commitment += commitment.operate_with_self(scalar.representative()); - scalar *= rho; - (batched_evaluation, batched_commitment) - }, - ); - - // Challenge y - let y_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - // Receive commitment C_{q} -> Since our transcript does not support appending and receiving data we instead store these commitments in a zeromorph proof struct - transcript.append(&q_hat_com.as_bytes()); - - // Challenge x, z - let x_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - let z_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - let (eval_scalar, (mut q_scalars, zm_poly_q_scalars)) = - eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &challenges); - - q_scalars - .iter_mut() - .zip(zm_poly_q_scalars) - .for_each(|(scalar, zm_poly_scalar)| { - *scalar += zm_poly_scalar; - }); - - let scalars = [ - vec![ - FieldElement::one(), - z_challenge, - batched_evaluation * eval_scalar, - ], - q_scalars, - ] - .concat(); - - let bases = [ - vec![q_hat_com, batched_commitment, vk.g1], - q_k_com, - ] - .concat(); - let zeta_z_com = msm( - &scalars, - &bases - ) - .expect("`points` is sliced by `cs`'s length"); + fn verify( + commitments: Self::Commitment, + evals: Self::Evaluation, + challenges: Self::Challenge, + vk: impl Borrow, + transcript: &mut impl Transcript, + proof: Self::Proof, + ) -> Result<(), Self::Error> { + debug_assert_eq!(evals.len(), commitments.len()); + let vk = vk.borrow(); + let ZeromorphProof { + pi, + q_k_com, + q_hat_com, + } = proof; + + //Receive q_k commitments + q_k_com + .iter() + .for_each(|c| transcript.append(&c.as_bytes())); + + // Compute powers of batching challenge rho + let rho = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + // Compute batching of unshifted polynomials f_i: + let mut scalar = FieldElement::::one(); + let (batched_evaluation, batched_commitment) = evals.iter().zip(commitments.iter()).fold( + (FieldElement::zero(), FieldElement::zero()), + |(mut batched_evaluation, mut batched_commitment), (eval, commitment)| { + batched_evaluation += scalar * eval; + batched_commitment += commitment.operate_with_self(scalar.representative()); + scalar *= rho; + (batched_evaluation, batched_commitment) + }, + ); - // e(pi, [tau]_2 - x * [1]_2) == e(C_{\zeta,Z}, [X^(N_max - 2^n - 1)]_2) <==> e(C_{\zeta,Z} - x * pi, [X^{N_max - 2^n - 1}]_2) * e(-pi, [tau_2]) == 1 - let e = P::compute_batch(&[(&pi, &(vk.tau_2 - (vk.g2.operate_with_self(x_challenge.representative())))), (&zeta_z_com, &vk.tau_N_max_sub_2_N)]).unwrap(); - assert_eq!(e, FieldElement::one()); - Ok(()) - } + // Challenge y + let y_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + // Receive commitment C_{q} -> Since our transcript does not support appending and receiving data we instead store these commitments in a zeromorph proof struct + transcript.append(&q_hat_com.as_bytes()); + + // Challenge x, z + let x_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + let z_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + let (eval_scalar, (mut q_scalars, zm_poly_q_scalars)) = + eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &challenges); + + q_scalars + .iter_mut() + .zip(zm_poly_q_scalars) + .for_each(|(scalar, zm_poly_scalar)| { + *scalar += zm_poly_scalar; + }); + + let scalars = [ + vec![ + FieldElement::one(), + z_challenge, + batched_evaluation * eval_scalar, + ], + q_scalars, + ] + .concat(); + + let bases = [vec![q_hat_com, batched_commitment, vk.g1], q_k_com].concat(); + let zeta_z_com = msm(&scalars, &bases).expect("`points` is sliced by `cs`'s length"); + + // e(pi, [tau]_2 - x * [1]_2) == e(C_{\zeta,Z}, [X^(N_max - 2^n - 1)]_2) <==> e(C_{\zeta,Z} - x * pi, [X^{N_max - 2^n - 1}]_2) * e(-pi, [tau_2]) == 1 + let e = P::compute_batch(&[ + ( + &pi, + &(vk.tau_2 - (vk.g2.operate_with_self(x_challenge.representative()))), + ), + (&zeta_z_com, &vk.tau_N_max_sub_2_N), + ]) + .unwrap(); + assert_eq!(e, FieldElement::one()); + Ok(()) + } } #[cfg(test)] @@ -444,352 +468,373 @@ mod test { use super::*; // Evaluate Phi_k(x) = \sum_{i=0}^k x^i using the direct inefficent formula - fn phi(challenge: &FieldElement, subscript: usize) -> FieldElement { - let len = (1 << subscript) as u64; - (0..len) - .into_iter() - .fold(FieldElement::::zero(), |mut acc, i| { - //Note this is ridiculous DevX - acc += challenge.pow(i); - acc - }) + fn phi( + challenge: &FieldElement, + subscript: usize, + ) -> FieldElement { + let len = (1 << subscript) as u64; + (0..len) + .into_iter() + .fold(FieldElement::::zero(), |mut acc, i| { + //Note this is ridiculous DevX + acc += challenge.pow(i); + acc + }) } #[test] fn prove_verify_single() { - let max_vars = 16; - let mut rng = test_rng(); - let srs = ZEROMORPH_SRS.lock().unwrap(); - - for num_vars in 3..max_vars { - // Setup - let (pk, vk) = { - let poly_size = 1 << (num_vars + 1); - srs.trim(poly_size - 1).unwrap() - }; - let polys = DenseMultilinearPolynomial::new( - (0..(1 << num_vars)) + let max_vars = 16; + let mut rng = test_rng(); + let srs = ZEROMORPH_SRS.lock().unwrap(); + + for num_vars in 3..max_vars { + // Setup + let (pk, vk) = { + let poly_size = 1 << (num_vars + 1); + srs.trim(poly_size - 1).unwrap() + }; + let polys = DenseMultilinearPolynomial::new( + (0..(1 << num_vars)) + .map(|_| Fr::rand(&mut rng)) + .collect::>(), + ); + let challenges = (0..num_vars) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let evals = polys.evaluate(&challenges).unwrap(); + + // Commit and open + let commitments = + Zeromorph::::commit(vec![polys.clone()], &pk.g1_powers) + .unwrap(); + + let mut prover_transcript = DefaultTranscript::new(); + let proof = Zeromorph::::prove( + vec![polys], + vec![evals], + challenges, + &pk, + &mut prover_transcript, + ) + .unwrap(); + + let mut verifier_transcript = DefaultTranscript::new(); + Zeromorph::::verify( + commitments, + vec![evals], + challenges, + &vk, + &mut verifier_transcript, + proof, + ) + .unwrap(); + + //TODO: check both random oracles are synced + } + } + + #[test] + fn prove_verify_batched() { + let max_vars = 16; + let mut rng = test_rng(); + let num_polys = 8; + let srs = ZeromorphSRS; + + for num_vars in 3..max_vars { + // Setup + let (pk, vk) = { + let poly_size = 1 << (num_vars + 1); + srs.trim(poly_size - 1).unwrap() + }; + let polys: Vec> = (0..num_polys) + .map(|_| { + DenseMultilinearPolynomial::new( + (0..(1 << num_vars)) + .map(|_| Fr::rand(&mut rng)) + .collect::>(), + ) + }) + .collect::>(); + let challenges = (0..num_vars) + .into_iter() + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + let evals = polys + .clone() + .into_iter() + .map(|poly| poly.evaluate(&challenges).unwrap()) + .collect::>(); + + // Commit and open + let commitments = + Zeromorph::::commit(polys, &pk.g1_powers).unwrap(); + + let mut prover_transcript = DefaultTranscript::new(); + let proof = Zeromorph::::prove( + polys, + evals, + challenges, + &pk, + &mut prover_transcript, + ) + .unwrap(); + + let mut verifier_transcript = DefaultTranscript::new(); + Zeromorph::::verify( + commitments, + evals, + challenges, + &vk, + &mut verifier_transcript, + proof, + ) + .unwrap(); + + //TODO: check both random oracles are synced + } + } + + /// Test for computing qk given multilinear f + /// Given 𝑓(𝑋₀, …, 𝑋ₙ₋₁), and `(𝑒, 𝑣)` such that \f(\u) = \v, compute `qβ‚–(𝑋₀, …, 𝑋ₖ₋₁)` + /// such that the following identity holds: + /// + /// `𝑓(𝑋₀, …, 𝑋ₙ₋₁) βˆ’ 𝑣 = βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ (𝑋ₖ βˆ’ 𝑒ₖ) qβ‚–(𝑋₀, …, 𝑋ₖ₋₁)` + #[test] + fn quotient_construction() { + // Define size params + let num_vars = 4; + let n: u64 = 1 << num_vars; + + // Construct a random multilinear polynomial f, and (u,v) such that f(u) = v + let mut rng = test_rng(); + let multilinear_f = + DenseMultilinearPolynomial::new((0..n).map(|_| Fr::rand(&mut rng)).collect::>()); + let u_challenge = (0..num_vars) + .into_iter() .map(|_| Fr::rand(&mut rng)) - .collect::>(), + .collect::>(); + let v_evaluation = multilinear_f.evaluate(&u_challenge).unwrap(); + + // Compute multilinear quotients `qβ‚–(𝑋₀, …, 𝑋ₖ₋₁)` + let (quotients, constant_term) = + compute_multilinear_quotients::(&multilinear_f, &u_challenge); + + // Assert the constant term is equal to v_evaluation + assert_eq!( + constant_term, v_evaluation, + "The constant term equal to the evaluation of the polynomial at challenge point." ); - let challenges = (0..num_vars) - .map(|_| Fr::rand(&mut rng)) - .collect::>(); - let evals = polys.evaluate(&challenges).unwrap(); - - // Commit and open - let commitments = Zeromorph::::commit(vec![polys.clone()], &pk.g1_powers).unwrap(); - - let mut prover_transcript = DefaultTranscript::new(); - let proof = Zeromorph::::prove( - vec![polys], - vec![evals], - challenges, - &pk, - &mut prover_transcript, - ) - .unwrap(); - let mut verifier_transcript = DefaultTranscript::new(); - Zeromorph::::verify( - commitments, - vec![evals], - challenges, - &vk, - &mut verifier_transcript, - proof, - ) - .unwrap(); + //To demonstrate that q_k was properly constructd we show that the identity holds at a random multilinear challenge + // i.e. 𝑓(𝑧) βˆ’ 𝑣 βˆ’ βˆ‘β‚–β‚Œβ‚€α΅ˆβ»ΒΉ (𝑧ₖ βˆ’ 𝑒ₖ)π‘žβ‚–(𝑧) = 0 + let z_challenge = (0..num_vars) + .map(|_| Fr::rand(&mut rng)) + .collect::>(); + + let mut res = multilinear_f.evaluate(&z_challenge).unwrap(); + res = res - v_evaluation; + + for (k, q_k_uni) in quotients.iter().enumerate() { + let z_partial = &z_challenge[&z_challenge.len() - k..]; + //This is a weird consequence of how things are done.. the univariate polys are of the multilinear commitment in lagrange basis. Therefore we evaluate as multilinear + let q_k = DenseMultilinearPolynomial::new(q_k_uni.coefficients.clone()); + let q_k_eval = q_k.evaluate(z_partial); + + res -= (z_challenge[z_challenge.len() - k - 1] + - u_challenge[z_challenge.len() - k - 1]) + * q_k_eval; + } + assert_eq!(res, FieldElement::zero()); + } + + /// Test for construction of batched lifted degree quotient: + /// Μ‚q = βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ yᡏ Xᡐ⁻ᡈᡏ⁻¹ Μ‚qβ‚–, 𝑑ₖ = deg(Μ‚q), π‘š = 𝑁 + #[test] + fn batched_lifted_degree_quotient() { + let num_vars = 3; + let n = 1 << num_vars; + + // Define mock qβ‚– with deg(qβ‚–) = 2ᡏ⁻¹ + let q_0 = Polynomial::new(&[FieldElement::one()]); + let q_1 = Polynomial::new(&[FieldElement::from(2u64), FieldElement::from(3u64)]); + let q_2 = Polynomial::new(&[ + FieldElement::from(4u64), + FieldElement::from(5u64), + FieldElement::from(6u64), + FieldElement::from(7u64), + ]); + let quotients = vec![q_0, q_1, q_2]; + + let mut rng = test_rng(); + let y_challenge = Fr::rand(&mut rng); + + //Compute batched quptient Μ‚q + let batched_quotient = compute_batched_lifted_degree_quotient::( + n, + "ients, + &y_challenge, + ); - //TODO: check both random oracles are synced + //Explicitly define q_k_lifted = X^{N-2^k} * q_k and compute the expected batched result + let q_0_lifted = Polynomial::new(&[ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::one(), + ]); + let q_1_lifted = Polynomial::new(&[ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::from(2u64), + FieldElement::from(3u64), + ]); + let q_2_lifted = Polynomial::new(&[ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::from(4u64), + FieldElement::from(5u64), + FieldElement::from(6u64), + FieldElement::from(7u64), + ]); + + //Explicitly compute Μ‚q i.e. RLC of lifted polys + let mut batched_quotient_expected = Polynomial::from_coeff(vec![Fr::zero(); n]); + + batched_quotient_expected += &q_0_lifted; + batched_quotient_expected += &(q_1_lifted * y_challenge); + batched_quotient_expected += &(q_2_lifted * (y_challenge * y_challenge)); + assert_eq!(batched_quotient, batched_quotient_expected); } -} -#[test] -fn prove_verify_batched() { -let max_vars = 16; -let mut rng = test_rng(); -let num_polys = 8; -let srs = ZeromorphSRS; - -for num_vars in 3..max_vars { - // Setup - let (pk, vk) = { - let poly_size = 1 << (num_vars + 1); - srs.trim(poly_size - 1).unwrap() - }; - let polys: Vec> = (0..num_polys) - .map(|_| { - DenseMultilinearPolynomial::new( - (0..(1 << num_vars)) - .map(|_| Fr::rand(&mut rng)) - .collect::>(), - ) - }) - .collect::>(); - let challenges = (0..num_vars) - .into_iter() - .map(|_| Fr::rand(&mut rng)) - .collect::>(); - let evals = polys - .clone() - .into_iter() - .map(|poly| poly.evaluate(&challenges).unwrap()) - .collect::>(); - - // Commit and open - let commitments = Zeromorph::::commit(polys, &pk.g1_powers).unwrap(); - - let mut prover_transcript = DefaultTranscript::new(); - let proof = - Zeromorph::::prove(polys, evals, challenges, &pk, &mut prover_transcript).unwrap(); - - let mut verifier_transcript = DefaultTranscript::new(); - Zeromorph::::verify( - commitments, - evals, - challenges, - &vk, - &mut verifier_transcript, - proof, - ) - .unwrap(); - - //TODO: check both random oracles are synced -} -} + /// evaluated quotient \zeta_x + /// + /// 𝜁 = 𝑓 βˆ’ βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉπ‘¦α΅π‘₯ʷ˒⁻ʷ⁺¹𝑓ₖ = 𝑓 βˆ’ βˆ‘_{d ∈ {dβ‚€, ..., dₙ₋₁}} X^{d* - d + 1} βˆ’ βˆ‘{k∢ dβ‚–=d} yᡏ fβ‚– , where d* = lifted degree + /// + /// 𝜁 = Μ‚q - βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ yᡏ Xᡐ⁻ᡈᡏ⁻¹ Μ‚qβ‚–, m = N + #[test] + fn partially_evaluated_quotient_zeta() { + let num_vars = 3; + let n: u64 = 1 << num_vars; + + let mut rng = test_rng(); + let x_challenge = Fr::rand(&mut rng); + let y_challenge = Fr::rand(&mut rng); + + let challenges: Vec<_> = (0..num_vars).map(|_| Fr::rand(&mut rng)).collect(); + let z_challenge = Fr::rand(&mut rng); + + let (_, (zeta_x_scalars, _)) = eval_and_quotient_scalars::( + y_challenge, + x_challenge, + z_challenge, + &challenges, + ); -/// Test for computing qk given multilinear f -/// Given 𝑓(𝑋₀, …, 𝑋ₙ₋₁), and `(𝑒, 𝑣)` such that \f(\u) = \v, compute `qβ‚–(𝑋₀, …, 𝑋ₖ₋₁)` -/// such that the following identity holds: -/// -/// `𝑓(𝑋₀, …, 𝑋ₙ₋₁) βˆ’ 𝑣 = βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ (𝑋ₖ βˆ’ 𝑒ₖ) qβ‚–(𝑋₀, …, 𝑋ₖ₋₁)` -#[test] -fn quotient_construction() { -// Define size params -let num_vars = 4; -let n: u64 = 1 << num_vars; - -// Construct a random multilinear polynomial f, and (u,v) such that f(u) = v -let mut rng = test_rng(); -let multilinear_f = - DenseMultilinearPolynomial::new((0..n).map(|_| Fr::rand(&mut rng)).collect::>()); -let u_challenge = (0..num_vars) - .into_iter() - .map(|_| Fr::rand(&mut rng)) - .collect::>(); -let v_evaluation = multilinear_f.evaluate(&u_challenge).unwrap(); - -// Compute multilinear quotients `qβ‚–(𝑋₀, …, 𝑋ₖ₋₁)` -let (quotients, constant_term) = - compute_multilinear_quotients::(&multilinear_f, &u_challenge); - -// Assert the constant term is equal to v_evaluation -assert_eq!( - constant_term, v_evaluation, - "The constant term equal to the evaluation of the polynomial at challenge point." -); - -//To demonstrate that q_k was properly constructd we show that the identity holds at a random multilinear challenge -// i.e. 𝑓(𝑧) βˆ’ 𝑣 βˆ’ βˆ‘β‚–β‚Œβ‚€α΅ˆβ»ΒΉ (𝑧ₖ βˆ’ 𝑒ₖ)π‘žβ‚–(𝑧) = 0 -let z_challenge = (0..num_vars) - .map(|_| Fr::rand(&mut rng)) - .collect::>(); - -let mut res = multilinear_f.evaluate(&z_challenge).unwrap(); -res = res - v_evaluation; - -for (k, q_k_uni) in quotients.iter().enumerate() { - let z_partial = &z_challenge[&z_challenge.len() - k..]; - //This is a weird consequence of how things are done.. the univariate polys are of the multilinear commitment in lagrange basis. Therefore we evaluate as multilinear - let q_k = DenseMultilinearPolynomial::new(q_k_uni.coefficients.clone()); - let q_k_eval = q_k.evaluate(z_partial); - - res -= (z_challenge[z_challenge.len() - k - 1] - u_challenge[z_challenge.len() - k - 1]) - * q_k_eval; -} -assert_eq!(res, FieldElement::zero()); -} + // To verify we manually compute zeta using the computed powers and expected + // 𝜁 = Μ‚q - βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ yᡏ Xᡐ⁻ᡈᡏ⁻¹ Μ‚qβ‚–, m = N + assert_eq!(zeta_x_scalars[0], -x_challenge.pow((n - 1) as u64)); -/// Test for construction of batched lifted degree quotient: -/// Μ‚q = βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ yᡏ Xᡐ⁻ᡈᡏ⁻¹ Μ‚qβ‚–, 𝑑ₖ = deg(Μ‚q), π‘š = 𝑁 -#[test] -fn batched_lifted_degree_quotient() { -let num_vars = 3; -let n = 1 << num_vars; - -// Define mock qβ‚– with deg(qβ‚–) = 2ᡏ⁻¹ -let q_0 = Polynomial::new(&[FieldElement::one()]); -let q_1 = Polynomial::new(&[FieldElement::from(2u64), FieldElement::from(3u64)]); -let q_2 = Polynomial::new(&[ - FieldElement::from(4u64), - FieldElement::from(5u64), - FieldElement::from(6u64), - FieldElement::from(7u64), -]); -let quotients = vec![q_0, q_1, q_2]; - -let mut rng = test_rng(); -let y_challenge = Fr::rand(&mut rng); - -//Compute batched quptient Μ‚q -let batched_quotient = - compute_batched_lifted_degree_quotient::(n, "ients, &y_challenge); - -//Explicitly define q_k_lifted = X^{N-2^k} * q_k and compute the expected batched result -let q_0_lifted = Polynomial::new(&[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::one(), -]); -let q_1_lifted = Polynomial::new(&[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(2u64), - FieldElement::from(3u64), -]); -let q_2_lifted = Polynomial::new(&[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(4u64), - FieldElement::from(5u64), - FieldElement::from(6u64), - FieldElement::from(7u64), -]); - -//Explicitly compute Μ‚q i.e. RLC of lifted polys -let mut batched_quotient_expected = Polynomial::from_coeff(vec![Fr::zero(); n]); - -batched_quotient_expected += &q_0_lifted; -batched_quotient_expected += &(q_1_lifted * y_challenge); -batched_quotient_expected += &(q_2_lifted * (y_challenge * y_challenge)); -assert_eq!(batched_quotient, batched_quotient_expected); -} + assert_eq!( + zeta_x_scalars[1], + -y_challenge * x_challenge.pow((n - 1 - 1) as u64) + ); -/// evaluated quotient \zeta_x -/// -/// 𝜁 = 𝑓 βˆ’ βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉπ‘¦α΅π‘₯ʷ˒⁻ʷ⁺¹𝑓ₖ = 𝑓 βˆ’ βˆ‘_{d ∈ {dβ‚€, ..., dₙ₋₁}} X^{d* - d + 1} βˆ’ βˆ‘{k∢ dβ‚–=d} yᡏ fβ‚– , where d* = lifted degree -/// -/// 𝜁 = Μ‚q - βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ yᡏ Xᡐ⁻ᡈᡏ⁻¹ Μ‚qβ‚–, m = N -#[test] -fn partially_evaluated_quotient_zeta() { -let num_vars = 3; -let n: u64 = 1 << num_vars; - -let mut rng = test_rng(); -let x_challenge = Fr::rand(&mut rng); -let y_challenge = Fr::rand(&mut rng); - -let challenges: Vec<_> = (0..num_vars).map(|_| Fr::rand(&mut rng)).collect(); -let z_challenge = Fr::rand(&mut rng); - -let (_, (zeta_x_scalars, _)) = - eval_and_quotient_scalars::(y_challenge, x_challenge, z_challenge, &challenges); - -// To verify we manually compute zeta using the computed powers and expected -// 𝜁 = Μ‚q - βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ yᡏ Xᡐ⁻ᡈᡏ⁻¹ Μ‚qβ‚–, m = N -assert_eq!( - zeta_x_scalars[0], - -x_challenge.pow((n - 1) as u64) -); - -assert_eq!( - zeta_x_scalars[1], - -y_challenge * x_challenge.pow((n - 1 - 1) as u64) -); - -assert_eq!( - zeta_x_scalars[2], - -y_challenge * y_challenge * x_challenge.pow((n - 3 - 1) as u64) -); -} + assert_eq!( + zeta_x_scalars[2], + -y_challenge * y_challenge * x_challenge.pow((n - 3 - 1) as u64) + ); + } -/// Test efficiently computing 𝛷ₖ(x) = βˆ‘α΅’β‚Œβ‚€α΅β»ΒΉxⁱ -/// 𝛷ₖ(π‘₯) = βˆ‘α΅’β‚Œβ‚€α΅β»ΒΉπ‘₯ⁱ = (π‘₯Β²^ᡏ βˆ’ 1) / (π‘₯ βˆ’ 1) -#[test] -fn phi_n_x_evaluation() { -const N: u64 = 8u64; -let log_N = (N as usize).log_2(); - -// 𝛷ₖ(π‘₯) -let mut rng = test_rng(); -let x_challenge = Fr::rand(&mut rng); - -let efficient = (x_challenge.pow((1 << log_N) as u64) - FieldElement::one()) - / (x_challenge - FieldElement::one()); -let expected: FieldElement<_> = phi::(&x_challenge, log_N); -assert_eq!(efficient, expected); -} + /// Test efficiently computing 𝛷ₖ(x) = βˆ‘α΅’β‚Œβ‚€α΅β»ΒΉxⁱ + /// 𝛷ₖ(π‘₯) = βˆ‘α΅’β‚Œβ‚€α΅β»ΒΉπ‘₯ⁱ = (π‘₯Β²^ᡏ βˆ’ 1) / (π‘₯ βˆ’ 1) + #[test] + fn phi_n_x_evaluation() { + const N: u64 = 8u64; + let log_N = (N as usize).log_2(); + + // 𝛷ₖ(π‘₯) + let mut rng = test_rng(); + let x_challenge = Fr::rand(&mut rng); + + let efficient = (x_challenge.pow((1 << log_N) as u64) - FieldElement::one()) + / (x_challenge - FieldElement::one()); + let expected: FieldElement<_> = phi::(&x_challenge, log_N); + assert_eq!(efficient, expected); + } -/// Test efficiently computing 𝛷ₖ(x) = βˆ‘α΅’β‚Œβ‚€α΅β»ΒΉxⁱ -/// 𝛷ₙ₋ₖ₋₁(π‘₯Β²^ᡏ⁺¹) = (π‘₯Β²^ⁿ βˆ’ 1) / (π‘₯Β²^ᡏ⁺¹ βˆ’ 1) -#[test] -fn phi_n_k_1_x_evaluation() { -const N: u64 = 8u64; -let log_N = (N as usize).log_2(); - -// 𝛷ₖ(π‘₯) -let mut rng = test_rng(); -let x_challenge = Fr::rand(&mut rng); -let k = 2; - -//π‘₯Β²^ᡏ⁺¹ -let x_pow = x_challenge.pow((1 << (k + 1)) as u64); - -//(π‘₯Β²^ⁿ βˆ’ 1) / (π‘₯Β²^ᡏ⁺¹ βˆ’ 1) -let efficient = - (x_challenge.pow((1 << log_N) as u64) - FieldElement::one()) / (x_pow - FieldElement::one()); -let expected: FieldElement<_> = phi::(&x_challenge, log_N - k - 1); -assert_eq!(efficient, expected); -} + /// Test efficiently computing 𝛷ₖ(x) = βˆ‘α΅’β‚Œβ‚€α΅β»ΒΉxⁱ + /// 𝛷ₙ₋ₖ₋₁(π‘₯Β²^ᡏ⁺¹) = (π‘₯Β²^ⁿ βˆ’ 1) / (π‘₯Β²^ᡏ⁺¹ βˆ’ 1) + #[test] + fn phi_n_k_1_x_evaluation() { + const N: u64 = 8u64; + let log_N = (N as usize).log_2(); + + // 𝛷ₖ(π‘₯) + let mut rng = test_rng(); + let x_challenge = Fr::rand(&mut rng); + let k = 2; + + //π‘₯Β²^ᡏ⁺¹ + let x_pow = x_challenge.pow((1 << (k + 1)) as u64); + + //(π‘₯Β²^ⁿ βˆ’ 1) / (π‘₯Β²^ᡏ⁺¹ βˆ’ 1) + let efficient = (x_challenge.pow((1 << log_N) as u64) - FieldElement::one()) + / (x_pow - FieldElement::one()); + let expected: FieldElement<_> = phi::(&x_challenge, log_N - k - 1); + assert_eq!(efficient, expected); + } -/// Test construction of 𝑍ₓ -/// 𝑍ₓ = ̂𝑓 βˆ’ 𝑣 βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ(π‘₯Β²^ᡏ𝛷ₙ₋ₖ₋₁(π‘₯ᡏ⁺¹)βˆ’ 𝑒ₖ𝛷ₙ₋ₖ(π‘₯Β²^ᡏ)) Μ‚qβ‚– -#[test] -fn partially_evaluated_quotient_z_x() { -let num_vars = 3; - -// Construct a random multilinear polynomial f, and (u,v) such that f(u) = v. -let mut rng = test_rng(); -let challenges: Vec<_> = (0..num_vars) - .into_iter() - .map(|_| Fr::rand(&mut rng)) - .collect(); - -let u_rev = { - let mut res = challenges.clone(); - res.reverse(); - res -}; + /// Test construction of 𝑍ₓ + /// 𝑍ₓ = ̂𝑓 βˆ’ 𝑣 βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ(π‘₯Β²^ᡏ𝛷ₙ₋ₖ₋₁(π‘₯ᡏ⁺¹)βˆ’ 𝑒ₖ𝛷ₙ₋ₖ(π‘₯Β²^ᡏ)) Μ‚qβ‚– + #[test] + fn partially_evaluated_quotient_z_x() { + let num_vars = 3; -let x_challenge = Fr::rand(&mut rng); -let y_challenge = Fr::rand(&mut rng); -let z_challenge = Fr::rand(&mut rng); - -// Construct Z_x scalars -let (_, (_, z_x_scalars)) = - eval_and_quotient_scalars::(y_challenge, x_challenge, z_challenge, &challenges); - -for k in 0..num_vars { - let x_pow_2k = x_challenge.pow((1 << k) as u64); // x^{2^k} - let x_pow_2kp1 = x_challenge.pow((1 << (k + 1)) as u64); // x^{2^{k+1}} - // x^{2^k} * \Phi_{n-k-1}(x^{2^{k+1}}) - u_k * \Phi_{n-k}(x^{2^k}) - let mut scalar = x_pow_2k * &phi::(&x_pow_2kp1, num_vars - k - 1) - - u_rev[k] * &phi::(&x_pow_2k, num_vars - k); - scalar *= z_challenge; - scalar *= FieldElement::from(1).neg(); - assert_eq!(z_x_scalars[k], scalar); -} + // Construct a random multilinear polynomial f, and (u,v) such that f(u) = v. + let mut rng = test_rng(); + let challenges: Vec<_> = (0..num_vars) + .into_iter() + .map(|_| Fr::rand(&mut rng)) + .collect(); + + let u_rev = { + let mut res = challenges.clone(); + res.reverse(); + res + }; + + let x_challenge = Fr::rand(&mut rng); + let y_challenge = Fr::rand(&mut rng); + let z_challenge = Fr::rand(&mut rng); + + // Construct Z_x scalars + let (_, (_, z_x_scalars)) = eval_and_quotient_scalars::( + y_challenge, + x_challenge, + z_challenge, + &challenges, + ); + + for k in 0..num_vars { + let x_pow_2k = x_challenge.pow((1 << k) as u64); // x^{2^k} + let x_pow_2kp1 = x_challenge.pow((1 << (k + 1)) as u64); // x^{2^{k+1}} + // x^{2^k} * \Phi_{n-k-1}(x^{2^{k+1}}) - u_k * \Phi_{n-k}(x^{2^k}) + let mut scalar = x_pow_2k * &phi::(&x_pow_2kp1, num_vars - k - 1) + - u_rev[k] * &phi::(&x_pow_2k, num_vars - k); + scalar *= z_challenge; + scalar *= FieldElement::from(1).neg(); + assert_eq!(z_x_scalars[k], scalar); + } + } } -} \ No newline at end of file From 595752bb6d53f135b648709f47a7f55530326a33 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Tue, 23 Jan 2024 23:51:01 -0600 Subject: [PATCH 14/23] refactor interface finish most of it --- crypto/Cargo.toml | 1 + crypto/src/commitments/kzg.rs | 84 ++-- crypto/src/commitments/traits.rs | 90 ++-- crypto/src/commitments/zeromorph/mod.rs | 1 + crypto/src/commitments/zeromorph/structs.rs | 180 +++++++ crypto/src/commitments/zeromorph/zeromorph.rs | 444 +++++++++--------- math/src/polynomial/mod.rs | 14 +- 7 files changed, 500 insertions(+), 314 deletions(-) create mode 100644 crypto/src/commitments/zeromorph/structs.rs diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index b7c86f597..3d95fde2d 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -20,6 +20,7 @@ rayon = { version = "1.8.0", optional = true } criterion = "0.4" iai-callgrind.workspace = true rand = "0.8.5" +rand_chacha = "0.3.1" [features] default = ["std"] diff --git a/crypto/src/commitments/kzg.rs b/crypto/src/commitments/kzg.rs index 2ecbb483c..58536fa45 100644 --- a/crypto/src/commitments/kzg.rs +++ b/crypto/src/commitments/kzg.rs @@ -1,6 +1,8 @@ -use super::traits::IsCommitmentScheme; +use crate::fiat_shamir::transcript::{Transcript}; + +use super::traits::IsPolynomialCommitmentScheme; use alloc::{borrow::ToOwned, vec::Vec}; -use core::{marker::PhantomData, mem}; +use core::{borrow::Borrow, marker::PhantomData, mem}; use lambdaworks_math::{ cyclic_group::IsGroup, elliptic_curve::traits::IsPairing, @@ -116,7 +118,7 @@ where main_group.push(point); } - let g2s_offset = size_g1_point * main_group_len + 12; + let g2s_offset = size_g1_point * main_group_len + MAIN_GROUP_OFFSET; for i in 0..2 { // The second unwrap shouldn't fail since the amount of bytes is fixed let point = G2Point::deserialize( @@ -151,9 +153,12 @@ impl KateZaveruchaGoldberg { } impl>, P: IsPairing> - IsCommitmentScheme for KateZaveruchaGoldberg + IsPolynomialCommitmentScheme for KateZaveruchaGoldberg { type Commitment = P::G1Point; + type Polynomial = Polynomial>; + type Proof = Self::Commitment; + type Point = FieldElement; fn commit(&self, p: &Polynomial>) -> Self::Commitment { let coefficients: Vec<_> = p @@ -170,21 +175,26 @@ impl>, P fn open( &self, - x: &FieldElement, - y: &FieldElement, - p: &Polynomial>, + // point polynomial `p` is evaluated at. + point: impl Borrow, + // evaluation of polynomial `p` at `point` `p`(`point`) = `eval`. + eval: &FieldElement, + // polynomial proof is being generated with respect to. + poly: &Polynomial>, + transcript: Option<&mut dyn Transcript>, ) -> Self::Commitment { - let mut poly_to_commit = p - y; - poly_to_commit.ruffini_division_inplace(x); + let mut poly_to_commit = poly - eval; + poly_to_commit.ruffini_division_inplace(point.borrow()); self.commit(&poly_to_commit) } fn verify( &self, - x: &FieldElement, - y: &FieldElement, + point: impl Borrow, + eval: &FieldElement, p_commitment: &Self::Commitment, proof: &Self::Commitment, + transcript: Option<&mut dyn Transcript>, ) -> bool { let g1 = &self.srs.powers_main_group[0]; let g2 = &self.srs.powers_secondary_group[0]; @@ -192,12 +202,12 @@ impl>, P let e = P::compute_batch(&[ ( - &p_commitment.operate_with(&(g1.operate_with_self(y.representative())).neg()), + &p_commitment.operate_with(&(g1.operate_with_self(eval.representative())).neg()), g2, ), ( &proof.neg(), - &(alpha_g2.operate_with(&(g2.operate_with_self(x.representative())).neg())), + &(alpha_g2.operate_with(&(g2.operate_with_self(point.borrow().representative())).neg())), ), ]); e == Ok(FieldElement::one()) @@ -205,33 +215,35 @@ impl>, P fn open_batch( &self, - x: &FieldElement, - ys: &[FieldElement], - polynomials: &[Polynomial>], + point: impl Borrow, + evals: &[FieldElement], + polys: &[Polynomial>], upsilon: &FieldElement, + transcript: Option<&mut dyn Transcript>, ) -> Self::Commitment { - let acc_polynomial = polynomials + let acc_polynomial = polys .iter() .rev() .fold(Polynomial::zero(), |acc, polynomial| { acc * upsilon.to_owned() + polynomial }); - let acc_y = ys + let acc_y = evals .iter() .rev() .fold(FieldElement::zero(), |acc, y| acc * upsilon.to_owned() + y); - self.open(x, &acc_y, &acc_polynomial) + self.open(point, &acc_y, &acc_polynomial, None) } fn verify_batch( &self, - x: &FieldElement, - ys: &[FieldElement], + point: impl Borrow, + evals: &[FieldElement], p_commitments: &[Self::Commitment], proof: &Self::Commitment, upsilon: &FieldElement, + transcript: Option<&mut dyn Transcript>, ) -> bool { let acc_commitment = p_commitments @@ -239,14 +251,14 @@ impl>, P .rev() .fold(P::G1Point::neutral_element(), |acc, point| { acc.operate_with_self(upsilon.to_owned().representative()) - .operate_with(point) + .operate_with(point.borrow()) }); - let acc_y = ys + let acc_y = evals .iter() .rev() .fold(FieldElement::zero(), |acc, y| acc * upsilon.to_owned() + y); - self.verify(x, &acc_y, &acc_commitment, proof) + self.verify(point, &acc_y, &acc_commitment, proof, None) } } @@ -273,7 +285,7 @@ mod tests { unsigned_integer::element::U256, }; - use crate::commitments::traits::IsCommitmentScheme; + use crate::commitments::traits::IsPolynomialCommitmentScheme; use super::{KateZaveruchaGoldberg, StructuredReferenceString}; use rand::Rng; @@ -317,10 +329,10 @@ mod tests { let p_commitment: ::G1Point = kzg.commit(&p); let x = -FieldElement::one(); let y = p.evaluate(&x); - let proof = kzg.open(&x, &y, &p); + let proof = kzg.open(&x, &y, &p, None); assert_eq!(y, FieldElement::zero()); assert_eq!(proof, BLS12381Curve::generator()); - assert!(kzg.verify(&x, &y, &p_commitment, &proof)); + assert!(kzg.verify(&x, &y, &p_commitment, &proof, None)); } #[test] @@ -330,8 +342,8 @@ mod tests { let p_commitment: ::G1Point = kzg.commit(&p); let x = FieldElement::one(); let y = FieldElement::from(9000); - let proof = kzg.open(&x, &y, &p); - assert!(kzg.verify(&x, &y, &p_commitment, &proof)); + let proof = kzg.open(&x, &y, &p, None); + assert!(kzg.verify(&x, &y, &p_commitment, &proof, None)); } #[test] @@ -344,9 +356,9 @@ mod tests { let y0 = FieldElement::from(9000); let upsilon = &FieldElement::from(1); - let proof = kzg.open_batch(&x, &[y0.clone()], &[p0], upsilon); + let proof = kzg.open_batch(&x, &[y0.clone()], &[p0], upsilon, None); - assert!(kzg.verify_batch(&x, &[y0], &[p0_commitment], &proof, upsilon)); + assert!(kzg.verify_batch(&x, &[y0], &[p0_commitment], &proof, upsilon, None)); } #[test] @@ -359,14 +371,15 @@ mod tests { let y0 = FieldElement::from(9000); let upsilon = &FieldElement::from(1); - let proof = kzg.open_batch(&x, &[y0.clone(), y0.clone()], &[p0.clone(), p0], upsilon); + let proof = kzg.open_batch(&x, &[y0.clone(), y0.clone()], &[p0.clone(), p0], upsilon, None); assert!(kzg.verify_batch( &x, &[y0.clone(), y0], &[p0_commitment.clone(), p0_commitment], &proof, - upsilon + upsilon, + None )); } @@ -390,14 +403,15 @@ mod tests { let upsilon = &FieldElement::from(1); - let proof = kzg.open_batch(&x, &[y0.clone(), y1.clone()], &[p0, p1], upsilon); + let proof = kzg.open_batch(&x, &[y0.clone(), y1.clone()], &[p0, p1], upsilon, None); assert!(kzg.verify_batch( &x, &[y0, y1], &[p0_commitment, p1_commitment], &proof, - upsilon + upsilon, + None )); } diff --git a/crypto/src/commitments/traits.rs b/crypto/src/commitments/traits.rs index 021eaa57f..4c19f7ba5 100644 --- a/crypto/src/commitments/traits.rs +++ b/crypto/src/commitments/traits.rs @@ -1,81 +1,59 @@ use lambdaworks_math::{ field::{element::FieldElement, traits::IsField}, - polynomial::Polynomial, }; use std::borrow::Borrow; -use crate::fiat_shamir::transcript::Transcript; +use crate::fiat_shamir::transcript::{self, Transcript}; -pub trait IsCommitmentScheme { +// For Non-Hiding +// For batching operations we use a transcript to supply random values. In the case of kzg +// - Using an option for the transcript was the simplest way to enforce domain separation (prover/verifier) +// for the future I think each protocol should have its own domain separated transcript within its instance variables +pub trait IsPolynomialCommitmentScheme { + /// Allows for Univariate vs Multilinear PCS + type Polynomial; + /// Point the polynomial is evaluated at + type Point; + /// Commitment to a Polynomial type Commitment; + /// Allows for different proof structures + type Proof; - fn commit(&self, p: &Polynomial>) -> Self::Commitment; + fn commit(&self, p: &Self::Polynomial) -> Self::Commitment; fn open( &self, - x: &FieldElement, - y: &FieldElement, - p: &Polynomial>, - ) -> Self::Commitment; + point: impl Borrow, + eval: &FieldElement, + poly: &Self::Polynomial, + transcript: Option<&mut dyn Transcript>, + ) -> Self::Proof; + fn open_batch( &self, - x: &FieldElement, - y: &[FieldElement], - p: &[Polynomial>], + point: impl Borrow, + eval: &[FieldElement], + polys: &[Self::Polynomial], upsilon: &FieldElement, - ) -> Self::Commitment; + transcript: Option<&mut dyn Transcript>, + ) -> Self::Proof; fn verify( &self, - x: &FieldElement, - y: &FieldElement, + point: impl Borrow, + eval: &FieldElement, p_commitment: &Self::Commitment, - proof: &Self::Commitment, + proof: &Self::Proof, + transcript: Option<&mut dyn Transcript>, ) -> bool; fn verify_batch( &self, - x: &FieldElement, - ys: &[FieldElement], + point: impl Borrow, + evals: &[FieldElement], p_commitments: &[Self::Commitment], - proof: &Self::Commitment, + proof: &Self::Proof, upsilon: &FieldElement, + transcript: Option<&mut dyn Transcript>, ) -> bool; -} - -pub trait PolynomialCommitmentScheme { - // Abstracting over Polynomial allows us to have batched and non-batched PCS - type Polynomial; - type Commitment; - type Evaluation; - type Challenge; - type Proof; - type Error; - - type ProverKey; - type CommitmentKey; - type VerifierKey; - - //TODO: convert to impl IntoIterator - fn commit( - poly: Self::Polynomial, - ck: impl Borrow, - ) -> Result; - - fn prove( - poly: Self::Polynomial, - evals: Self::Evaluation, - challenges: Self::Challenge, - pk: impl Borrow, - transcript: &mut impl Transcript, - ) -> Result; - - fn verify( - commitments: Self::Commitment, - evals: Self::Evaluation, - challenges: Self::Challenge, - vk: impl Borrow, - transcript: &mut impl Transcript, - proof: Self::Proof, - ) -> Result<(), Self::Error>; -} +} \ No newline at end of file diff --git a/crypto/src/commitments/zeromorph/mod.rs b/crypto/src/commitments/zeromorph/mod.rs index f6f806424..9ae5d9710 100644 --- a/crypto/src/commitments/zeromorph/mod.rs +++ b/crypto/src/commitments/zeromorph/mod.rs @@ -1 +1,2 @@ pub mod zeromorph; +pub mod structs; \ No newline at end of file diff --git a/crypto/src/commitments/zeromorph/structs.rs b/crypto/src/commitments/zeromorph/structs.rs new file mode 100644 index 000000000..48c7cbabe --- /dev/null +++ b/crypto/src/commitments/zeromorph/structs.rs @@ -0,0 +1,180 @@ +use core::mem; + +use lambdaworks_math::{elliptic_curve::traits::IsPairing, errors::DeserializationError, traits::{AsBytes, Deserializable}}; + +use super::zeromorph::ZeromorphError; + +//TODO: gate with alloc +#[derive(Debug, Clone, Default)] +pub struct ZeromorphSRS { + pub g1_powers: Vec, + /// [g2: (P::G2Point), tau_2: (P::G2Point), tau_n_max_sub_2_n: (P::G2Point)] + pub g2_powers: [P::G2Point; 3], +} + +impl ZeromorphSRS

+{ + pub fn new(g1_powers: &[P::G1Point], g2_powers: &[P::G2Point; 3]) -> Self { + Self { + g1_powers: g1_powers.into(), + g2_powers: g2_powers.clone(), + } + } + + //TODO: Delete? + /* + pub fn trim( + &self, + max_degree: usize, + ) -> Result<(ZeromorphProverKey

, ZeromorphVerifierKey

), ZeromorphError> { + if max_degree > self.g1_powers.len() { + return Err(ZeromorphError::KeyLengthError( + max_degree, + self.g1_powers.len(), + )); + } + let offset = self.g1_powers.len() - max_degree; + let offset_g1_powers = self.g1_powers[offset..].to_vec(); + Ok(( + ZeromorphProverKey { + g1_powers: self.g1_powers.clone(), + offset_g1_powers: offset_g1_powers, + }, + ZeromorphVerifierKey { + g1: self.g1_powers[0], + g2: self.g2_powers[0], + tau_2: self.g2_powers[1], + tau_n_max_sub_2_n: self.g2_powers[offset], + }, + )) + } + */ +} + +#[cfg(feature = "std")] +impl ZeromorphSRS

+where +

::G1Point: Deserializable, +

::G2Point: Deserializable, +{ + pub fn from_file(file_path: &str) -> Result { + let bytes = std::fs::read(file_path)?; + Ok(Self::deserialize(&bytes)?) + } +} + +impl AsBytes for ZeromorphSRS

+where +

::G1Point: AsBytes, +

::G2Point: AsBytes, +{ + fn as_bytes(&self) -> Vec { + let mut serialized_data: Vec = Vec::new(); + // First 4 bytes encodes protocol version + let protocol_version: [u8; 4] = [0; 4]; + + serialized_data.extend(&protocol_version); + + // Second 8 bytes store the amount of G1 elements to be stored, this is more than can be indexed with a 64-bit architecture, and some millions of terabytes of data if the points were compressed + let mut main_group_len_bytes: Vec = self.g1_powers.len().to_le_bytes().to_vec(); + + // For architectures with less than 64 bits for pointers + // We add extra zeros at the end + while main_group_len_bytes.len() < 8 { + main_group_len_bytes.push(0) + } + + serialized_data.extend(&main_group_len_bytes); + + // G1 elements + for point in &self.g1_powers { + serialized_data.extend(point.as_bytes()); + } + + // G2 elements + for point in &self.g2_powers { + serialized_data.extend(point.as_bytes()); + } + + serialized_data + } +} + +impl Deserializable for ZeromorphSRS

+where +

::G1Point: Deserializable, +

::G2Point: Deserializable, +{ + fn deserialize(bytes: &[u8]) -> Result { + const MAIN_GROUP_LEN_OFFSET: usize = 4; + const MAIN_GROUP_OFFSET: usize = 12; + + let main_group_len_u64 = u64::from_le_bytes( + // This unwrap can't fail since we are fixing the size of the slice + bytes[MAIN_GROUP_LEN_OFFSET..MAIN_GROUP_OFFSET] + .try_into() + .unwrap(), + ); + + let main_group_len = usize::try_from(main_group_len_u64) + .map_err(|_| DeserializationError::PointerSizeError)?; + + let mut main_group: Vec = Vec::new(); + let mut secondary_group: Vec = Vec::new(); + + let size_g1_point = mem::size_of::(); + let size_g2_point = mem::size_of::(); + + for i in 0..main_group_len { + // The second unwrap shouldn't fail since the amount of bytes is fixed + let point = P::G1Point::deserialize( + bytes[i * size_g1_point + MAIN_GROUP_OFFSET + ..i * size_g1_point + size_g1_point + MAIN_GROUP_OFFSET] + .try_into() + .unwrap(), + )?; + main_group.push(point); + } + + let g2s_offset = size_g1_point * main_group_len + MAIN_GROUP_OFFSET; + for i in 0..3 { + // The second unwrap shouldn't fail since the amount of bytes is fixed + let point = P::G2Point::deserialize( + bytes[i * size_g2_point + g2s_offset + ..i * size_g2_point + g2s_offset + size_g2_point] + .try_into() + .unwrap(), + )?; + secondary_group.push(point); + } + + let secondary_group_slice = [secondary_group[0].clone(), secondary_group[1].clone(), secondary_group[2].clone()]; + + let srs = ZeromorphSRS::new(&main_group, &secondary_group_slice); + Ok(srs) + } +} + + +#[derive(Clone, Debug)] +pub struct ZeromorphProverKey { + pub g1_powers: Vec, + pub offset_g1_powers: Vec, +} + +/* +#[derive(Copy, Clone, Debug)] +pub struct ZeromorphVerifierKey { + pub g1: P::G1Point, + pub g2: P::G2Point, + pub tau_2: P::G2Point, + pub tau_n_max_sub_2_n: P::G2Point, +} + +#[derive(Clone, Debug)] +pub struct ZeromorphProof { + pub pi: P::G1Point, + pub q_hat_com: P::G1Point, + pub q_k_com: Vec, +} +*/ \ No newline at end of file diff --git a/crypto/src/commitments/zeromorph/zeromorph.rs b/crypto/src/commitments/zeromorph/zeromorph.rs index 782a0b47f..28f65eb82 100644 --- a/crypto/src/commitments/zeromorph/zeromorph.rs +++ b/crypto/src/commitments/zeromorph/zeromorph.rs @@ -1,22 +1,17 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::type_complexity)] -use crate::{ - commitments::traits::PolynomialCommitmentScheme, - fiat_shamir::{default_transcript::DefaultTranscript, transcript::Transcript}, -}; +use crate::{commitments::traits::IsPolynomialCommitmentScheme, fiat_shamir::transcript::Transcript}; use lambdaworks_math::{ cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::curves::bls12_381::pairing::BLS12381AtePairing, traits::IsPairing, - }, + elliptic_curve::traits::IsPairing, field::{ element::FieldElement, traits::{IsField, IsPrimeField}, }, msm::pippenger::msm, polynomial::{dense_multilinear_poly::DenseMultilinearPolynomial, Polynomial}, - traits::{AsBytes, ByteConversion}, + traits::ByteConversion, unsigned_integer::element::UnsignedInteger, }; use std::{borrow::Borrow, iter, marker::PhantomData}; @@ -24,58 +19,7 @@ use std::{borrow::Borrow, iter, marker::PhantomData}; #[cfg(feature = "parallel")] use rayon::prelude::*; -//TODO: gate with alloc -#[derive(Debug, Clone, Default)] -pub struct ZeromorphSRS { - pub g1_powers: Vec, - pub g2_powers: Vec, -} - -impl ZeromorphSRS

{ - pub fn setup() -> ZeromorphSRS

{ - todo!() - } - - pub fn trim( - &self, - max_degree: usize, - ) -> Result<(ZeromorphProverKey

, ZeromorphVerifierKey

), ZeromorphError> { - if max_degree > self.g1_powers.len() { - return Err(ZeromorphError::KeyLengthError( - max_degree, - self.g1_powers.len(), - )); - } - let offset = self.g1_powers.len() - max_degree; - let offset_g1_powers = self.g1_powers[offset..].to_vec(); - Ok(( - ZeromorphProverKey { - g1_powers: self.g1_powers.clone(), - offset_g1_powers: offset_g1_powers, - }, - ZeromorphVerifierKey { - g1: self.g1_powers[0], - g2: self.g2_powers[0], - tau_2: self.g2_powers[1], - tau_n_max_sub_2_n: self.g2_powers[offset], - }, - )) - } -} - -#[derive(Clone, Debug)] -pub struct ZeromorphProverKey { - pub g1_powers: Vec, - pub offset_g1_powers: Vec, -} - -#[derive(Copy, Clone, Debug)] -pub struct ZeromorphVerifierKey { - pub g1: P::G1Point, - pub g2: P::G2Point, - pub tau_2: P::G2Point, - pub tau_n_max_sub_2_n: P::G2Point, -} +use super::structs::ZeromorphSRS; #[derive(Clone, Debug)] pub struct ZeromorphProof { @@ -239,94 +183,63 @@ fn eval_and_quotient_scalars( (-vs[0] * z_challenge, q_scalars) } +// TODO: how to handle the case in batching that the polynomials each have unique num_vars pub struct Zeromorph { + srs: ZeromorphSRS

, _phantom: PhantomData

, } -impl PolynomialCommitmentScheme for Zeromorph

+impl Zeromorph

{ + +} + +impl IsPolynomialCommitmentScheme for Zeromorph

where <

::BaseField as IsField>::BaseType: Send + Sync, FieldElement<

::BaseField>: ByteConversion, //TODO: how to eliminate this complication in the trait interface

::BaseField: IsPrimeField>, { - type Commitment = Vec; - type Polynomial = Vec>; - type Evaluation = Vec>; - type Challenge = Vec>; + type Polynomial = DenseMultilinearPolynomial; + type Commitment = P::G1Point; type Proof = ZeromorphProof

; - type Error = ZeromorphError; - - type ProverKey = ZeromorphProverKey

; - type CommitmentKey = Self::Commitment; - type VerifierKey = ZeromorphVerifierKey

; + type Point = Vec>; - fn commit( - polys: Self::Polynomial, - ck: impl Borrow, - ) -> Result { - let ck = ck.borrow(); + //TODO: errors + fn commit(&self, poly: &Self::Polynomial) -> Self::Commitment { // TODO: assert lengths are valid - #[cfg(feature = "parallel")] - let iter = polys.par_iter(); - #[cfg(not(feature = "parallel"))] - let iter = polys.iter(); - Ok(iter - .map(|poly| { - let evals: Vec<_> = poly - .evals() - .iter() - .map(|eval| eval.representative()) - .collect(); - msm(&evals, &ck[..poly.len()]).unwrap() - }) - .collect::>()) + let scalars: Vec<_> = poly + .evals() + .iter() + .map(|eval| eval.representative()) + .collect(); + msm(&scalars, &self.srs.g1_powers[..poly.len()]).unwrap() } - fn prove( - polys: Self::Polynomial, - evals: Self::Evaluation, - challenge: Self::Challenge, - pk: impl Borrow, - transcript: &mut impl Transcript, - ) -> Result { - let num_vars = challenge.len(); + fn open( + &self, + point: impl Borrow, + eval: &FieldElement, + poly: &Self::Polynomial, + transcript: Option<&mut dyn Transcript> + ) -> Self::Proof { + let point = point.borrow(); + //TODO: error or interface or something + let transcript = transcript.expect("Oh no, there isn't a transcript"); + let num_vars = point.len(); let n: usize = 1 << num_vars; - let pk = pk.borrow(); - - for (poly, eval) in polys.iter().zip(evals.iter()) { - // Note by evaluating we confirm the number of challenges is valid - debug_assert_eq!(poly.evaluate(challenge).unwrap(), *eval); - } - - // Generate batching challenge \rho and powers 1,...,\rho^{m-1} - let rho = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - // Compute batching of unshifted polynomials f_i: - let mut scalar = FieldElement::one(); - let (f_batched, batched_evaluation) = (0..polys.len()).fold( - ( - DenseMultilinearPolynomial::new(vec![FieldElement::zero(); n]), - FieldElement::zero(), - ), - |(mut f_batched, mut batched_evaluation), i| { - f_batched = (f_batched + polys[i].clone() * scalar).unwrap(); - batched_evaluation += scalar * evals[i]; - scalar *= rho; - (f_batched, batched_evaluation) - }, - ); - let mut pi_poly = Polynomial::new(&f_batched.evals()); + let mut pi_poly = Polynomial::new(&poly.evals()); // Compute the multilinear quotients q_k = q_k(X_0, ..., X_{k-1}) - let (quotients, remainder) = compute_multilinear_quotients::

(&f_batched, &challenge); - debug_assert_eq!(quotients.len(), f_batched.num_vars()); - debug_assert_eq!(remainder, batched_evaluation); + let (quotients, remainder) = compute_multilinear_quotients::

(&poly, &point); + debug_assert_eq!(quotients.len(), poly.num_vars()); + debug_assert_eq!(remainder, *eval); // Compute and send commitments C_{q_k} = [q_k], k = 0, ..., d-1 let q_k_commitments: Vec<_> = quotients .iter() .map(|q| { - let q_k_commitment = UnivariateKZG::

::commit(&pk.g1_powers, q).unwrap(); + let q_k_commitment = self.commit(q); transcript.append(&q_k_commitment.as_bytes()); q_k_commitment }) @@ -341,7 +254,18 @@ where // Compute and send the commitment C_q = [\hat{q}] // commit at offset let offset = 1 << (quotients.len() - 1); - let q_hat_com = UnivariateKZG::

::commit_offset(&pk.g1_powers, &q_hat, offset).unwrap(); + + // We perform an offset commitment here; This could be abstracted but its very small + let q_hat_com = { + //TODO: we only need to convert the offset scalars + let scalars: Vec<_> = q_hat + .coefficients + .iter() + .map(|eval| eval.representative()) + .collect(); + msm(&scalars[offset..], &self.srs.g1_powers[offset..q_hat.coeff_len()]).unwrap() + }; + transcript.append(&q_hat_com.as_bytes()); // Get challenges x and z @@ -349,18 +273,18 @@ where let z_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); let (eval_scalar, (zeta_degree_check_q_scalars, z_zmpoly_q_scalars)) = - eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &challenge); + eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &point); // f = z * x * poly.Z + q_hat + (-z * x * Ξ¦_n(x) * e) + x * βˆ‘_k (q_scalars_k * q_k) pi_poly = pi_poly * &z_challenge; pi_poly = pi_poly + &q_hat; - pi_poly[0] += &(batched_evaluation * eval_scalar); + pi_poly[0] += eval * eval_scalar; quotients .into_iter() .zip(zeta_degree_check_q_scalars) .zip(z_zmpoly_q_scalars) .for_each(|((mut q, zeta_degree_check_q_scalar), z_zmpoly_q_scalar)| { - q *= &(zeta_degree_check_q_scalar + z_zmpoly_q_scalar); - pi_poly += &q; + q = q * &(zeta_degree_check_q_scalar + z_zmpoly_q_scalar); + pi_poly = pi_poly + &q; }); debug_assert_eq!( @@ -369,52 +293,82 @@ where ); // Compute the KZG opening proof pi_poly; -> TODO should abstract into separate trait - let (pi, _) = - UnivariateKZG::

::open(&pk.offset_g1_powers, &pi_poly, &x_challenge).unwrap(); + let pi = { + pi_poly.ruffini_division_inplace(&x_challenge); + let scalars: Vec<_> = pi_poly + .coefficients + .iter() + .map(|eval| eval.representative()) + .collect(); + msm(&scalars, &self.srs.g1_powers[..pi_poly.coeff_len()]).unwrap() + }; - Ok(ZeromorphProof { + ZeromorphProof { pi, q_hat_com, q_k_com: q_k_commitments, - }) + } + } + + fn open_batch( + &self, + point: impl Borrow, + evals: &[FieldElement], + polys: &[Self::Polynomial], + upsilon: &FieldElement, + transcript: Option<&mut dyn Transcript>, + ) -> Self::Proof { + for (poly, eval) in polys.iter().zip(evals.iter()) { + // Note by evaluating we confirm the number of challenges is valid + debug_assert_eq!(poly.evaluate(point.borrow().clone()).unwrap(), *eval); + } + + // Generate batching challenge \rho and powers 1,...,\rho^{m-1} + let rho = FieldElement::from_bytes_be(&transcript.unwrap().challenge()).unwrap(); + // Compute batching of unshifted polynomials f_i: + let mut scalar = FieldElement::one(); + let (f_batched, batched_evaluation) = (0..polys.len()).fold( + ( + DenseMultilinearPolynomial::new(vec![FieldElement::zero(); 1 << polys[0].num_vars()]), + FieldElement::zero(), + ), + |(mut f_batched, mut batched_evaluation), i| { + f_batched = (f_batched + polys[i].clone() * scalar).unwrap(); + batched_evaluation += scalar * evals[i]; + scalar *= rho; + (f_batched, batched_evaluation) + }, + ); + Self::open(&self, point, &batched_evaluation, &f_batched, transcript) } fn verify( - commitments: Self::Commitment, - evals: Self::Evaluation, - challenges: Self::Challenge, - vk: impl Borrow, - transcript: &mut impl Transcript, - proof: Self::Proof, - ) -> Result<(), Self::Error> { - debug_assert_eq!(evals.len(), commitments.len()); - let vk = vk.borrow(); + &self, + point: impl Borrow, + eval: &FieldElement, + p_commitment: &Self::Commitment, + proof: &Self::Proof, + transcript: Option<&mut dyn Transcript>, + ) -> bool { let ZeromorphProof { pi, q_k_com, q_hat_com, } = proof; + let transcript = transcript.expect("Oh no, there isn't a transcript"); + let point = point.borrow(); + let g1 = self.srs.g1_powers[0]; + let g2 = &self.srs.g2_powers[0]; + let tau_g2 = &self.srs.g2_powers[1]; + // offset power of tau + let tau_n_max_sub_2_n = self.srs.g2_powers[2]; + //Receive q_k commitments q_k_com .iter() .for_each(|c| transcript.append(&c.as_bytes())); - // Compute powers of batching challenge rho - let rho = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - // Compute batching of unshifted polynomials f_i: - let mut scalar = FieldElement::::one(); - let (batched_evaluation, batched_commitment) = evals.iter().zip(commitments.iter()).fold( - (FieldElement::zero(), FieldElement::zero()), - |(mut batched_evaluation, mut batched_commitment), (eval, commitment)| { - batched_evaluation += scalar * eval; - batched_commitment += commitment.operate_with_self(scalar.representative()); - scalar *= rho; - (batched_evaluation, batched_commitment) - }, - ); - // Challenge y let y_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); @@ -426,7 +380,7 @@ where let z_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); let (eval_scalar, (mut q_scalars, zm_poly_q_scalars)) = - eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &challenges); + eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &point); q_scalars .iter_mut() @@ -439,33 +393,72 @@ where vec![ FieldElement::one(), z_challenge, - batched_evaluation * eval_scalar, + (eval * eval_scalar), ], q_scalars, ] .concat(); - let bases = [vec![q_hat_com, batched_commitment, vk.g1], q_k_com].concat(); - let zeta_z_com = msm(&scalars, &bases).expect("`points` is sliced by `cs`'s length"); + let bases = [vec![*q_hat_com, *p_commitment, g1], *q_k_com].concat(); + let zeta_z_com = { + let scalars: Vec<_> = scalars + .iter() + .map(|scalar| scalar.representative()) + .collect(); + msm(&scalars, &bases).expect("`points` is sliced by `cs`'s length") + }; // e(pi, [tau]_2 - x * [1]_2) == e(C_{\zeta,Z}, [X^(N_max - 2^n - 1)]_2) <==> e(C_{\zeta,Z} - x * pi, [X^{N_max - 2^n - 1}]_2) * e(-pi, [tau_2]) == 1 let e = P::compute_batch(&[ ( &pi, - &(vk.tau_2 - (vk.g2.operate_with_self(x_challenge.representative()))), + &tau_g2.operate_with(&g2.operate_with_self(x_challenge.representative()).neg()), ), - (&zeta_z_com, &vk.tau_N_max_sub_2_N), + (&zeta_z_com, &tau_n_max_sub_2_n), ]) .unwrap(); - assert_eq!(e, FieldElement::one()); - Ok(()) + e == FieldElement::one() + } + + fn verify_batch( + &self, + point: impl Borrow, + evals: &[FieldElement], + p_commitments: &[Self::Commitment], + proof: &Self::Proof, + upsilon: &FieldElement, + transcript: Option<&mut dyn Transcript>, + ) -> bool { + debug_assert_eq!(evals.len(), p_commitments.len()); + // Compute powers of batching challenge rho + let rho = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + // Compute batching of unshifted polynomials f_i: + let mut scalar = FieldElement::one(); + let (batched_eval, batched_commitment) = evals.iter().zip(p_commitments.iter()).fold( + (FieldElement::zero(), P::G1Point::neutral_element()), + |(mut batched_eval, mut batched_commitment), (eval, commitment)| { + batched_eval += scalar * eval; + batched_commitment.operate_with(&commitment.operate_with_self(scalar.representative())); + scalar *= rho; + (batched_eval, batched_commitment) + }, + ); + Self::verify(&self, point, &batched_eval, &batched_commitment, proof, transcript) } + } #[cfg(test)] mod test { + use core::ops::Neg; + + use crate::fiat_shamir::default_transcript::DefaultTranscript; + use super::*; + use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::pairing::BLS12381AtePairing; + use rand_chacha::{rand_core::{RngCore, SeedableRng}, ChaCha20Rng}; // Evaluate Phi_k(x) = \sum_{i=0}^k x^i using the direct inefficent formula fn phi( @@ -475,18 +468,28 @@ mod test { let len = (1 << subscript) as u64; (0..len) .into_iter() - .fold(FieldElement::::zero(), |mut acc, i| { + .fold(FieldElement::zero(), |mut acc, i| { //Note this is ridiculous DevX acc += challenge.pow(i); acc }) } + fn rand_fr(mut rng: &mut R) -> FieldElement + where + FieldElement<

::BaseField>: ByteConversion, + { + let mut bytes = [0u8; 384]; + rng.fill_bytes(&mut bytes); + FieldElement::::from_bytes_be(&bytes).unwrap() + } + #[test] fn prove_verify_single() { let max_vars = 16; - let mut rng = test_rng(); - let srs = ZEROMORPH_SRS.lock().unwrap(); + let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); + let srs = todo!(); + let zm = Zeromorph::::new(); for num_vars in 3..max_vars { // Setup @@ -494,36 +497,33 @@ mod test { let poly_size = 1 << (num_vars + 1); srs.trim(poly_size - 1).unwrap() }; - let polys = DenseMultilinearPolynomial::new( + let poly = DenseMultilinearPolynomial::new( (0..(1 << num_vars)) - .map(|_| Fr::rand(&mut rng)) + .map(|_| rand_fr(&mut rng)) .collect::>(), ); - let challenges = (0..num_vars) - .map(|_| Fr::rand(&mut rng)) + let point = (0..num_vars) + .map(|_| rand_fr(&mut rng)) .collect::>(); - let evals = polys.evaluate(&challenges).unwrap(); + let eval = poly.evaluate(point).unwrap(); // Commit and open - let commitments = - Zeromorph::::commit(vec![polys.clone()], &pk.g1_powers) - .unwrap(); + let commitments = zm.commit(poly.clone()); let mut prover_transcript = DefaultTranscript::new(); - let proof = Zeromorph::::prove( - vec![polys], - vec![evals], - challenges, - &pk, - &mut prover_transcript, + let proof = zm.open( + point, + eval, + poly, + Some(&mut prover_transcript), ) .unwrap(); let mut verifier_transcript = DefaultTranscript::new(); - Zeromorph::::verify( + zm.verify( commitments, - vec![evals], - challenges, + eval, + point, &vk, &mut verifier_transcript, proof, @@ -537,9 +537,10 @@ mod test { #[test] fn prove_verify_batched() { let max_vars = 16; - let mut rng = test_rng(); + let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); let num_polys = 8; - let srs = ZeromorphSRS; + let srs = todo!(); + let zm = Zeromorph::::new(); for num_vars in 3..max_vars { // Setup @@ -571,26 +572,24 @@ mod test { Zeromorph::::commit(polys, &pk.g1_powers).unwrap(); let mut prover_transcript = DefaultTranscript::new(); - let proof = Zeromorph::::prove( - polys, + let proof = zm.open_batch( evals, challenges, &pk, - &mut prover_transcript, + polys, + Some(&mut prover_transcript), ) .unwrap(); let mut verifier_transcript = DefaultTranscript::new(); - Zeromorph::::verify( + assert!(zm.verify_batch( commitments, evals, challenges, &vk, - &mut verifier_transcript, proof, - ) - .unwrap(); - + Some(&mut verifier_transcript), + ) == true) //TODO: check both random oracles are synced } } @@ -607,12 +606,12 @@ mod test { let n: u64 = 1 << num_vars; // Construct a random multilinear polynomial f, and (u,v) such that f(u) = v - let mut rng = test_rng(); + let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); let multilinear_f = DenseMultilinearPolynomial::new((0..n).map(|_| Fr::rand(&mut rng)).collect::>()); let u_challenge = (0..num_vars) .into_iter() - .map(|_| Fr::rand(&mut rng)) + .map(|_| rand_fr(&mut rng)) .collect::>(); let v_evaluation = multilinear_f.evaluate(&u_challenge).unwrap(); @@ -652,8 +651,8 @@ mod test { /// Μ‚q = βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ yᡏ Xᡐ⁻ᡈᡏ⁻¹ Μ‚qβ‚–, 𝑑ₖ = deg(Μ‚q), π‘š = 𝑁 #[test] fn batched_lifted_degree_quotient() { - let num_vars = 3; - let n = 1 << num_vars; + const NUM_VARS: usize = 3; + const N: usize = 1 << NUM_VARS; // Define mock qβ‚– with deg(qβ‚–) = 2ᡏ⁻¹ let q_0 = Polynomial::new(&[FieldElement::one()]); @@ -666,12 +665,12 @@ mod test { ]); let quotients = vec![q_0, q_1, q_2]; - let mut rng = test_rng(); - let y_challenge = Fr::rand(&mut rng); + let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); + let y_challenge = rand_fr(&mut rng); //Compute batched quptient Μ‚q let batched_quotient = compute_batched_lifted_degree_quotient::( - n, + N, "ients, &y_challenge, ); @@ -709,11 +708,11 @@ mod test { ]); //Explicitly compute Μ‚q i.e. RLC of lifted polys - let mut batched_quotient_expected = Polynomial::from_coeff(vec![Fr::zero(); n]); + let mut batched_quotient_expected = Polynomial::zero(); - batched_quotient_expected += &q_0_lifted; - batched_quotient_expected += &(q_1_lifted * y_challenge); - batched_quotient_expected += &(q_2_lifted * (y_challenge * y_challenge)); + batched_quotient_expected = batched_quotient_expected + &q_0_lifted; + batched_quotient_expected = batched_quotient_expected + &(q_1_lifted * y_challenge); + batched_quotient_expected = batched_quotient_expected + &(q_2_lifted * (y_challenge * y_challenge)); assert_eq!(batched_quotient, batched_quotient_expected); } @@ -727,12 +726,12 @@ mod test { let num_vars = 3; let n: u64 = 1 << num_vars; - let mut rng = test_rng(); - let x_challenge = Fr::rand(&mut rng); - let y_challenge = Fr::rand(&mut rng); + let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); + let x_challenge = rand_fr::(&mut rng); + let y_challenge = rand_fr::(&mut rng); - let challenges: Vec<_> = (0..num_vars).map(|_| Fr::rand(&mut rng)).collect(); - let z_challenge = Fr::rand(&mut rng); + let challenges: Vec<_> = (0..num_vars).map(|_| rand_fr::(&mut rng)).collect(); + let z_challenge = rand_fr::(&mut rng); let (_, (zeta_x_scalars, _)) = eval_and_quotient_scalars::( y_challenge, @@ -764,8 +763,8 @@ mod test { let log_N = (N as usize).log_2(); // 𝛷ₖ(π‘₯) - let mut rng = test_rng(); - let x_challenge = Fr::rand(&mut rng); + let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); + let x_challenge = rand_fr(&mut rng); let efficient = (x_challenge.pow((1 << log_N) as u64) - FieldElement::one()) / (x_challenge - FieldElement::one()); @@ -781,8 +780,8 @@ mod test { let log_N = (N as usize).log_2(); // 𝛷ₖ(π‘₯) - let mut rng = test_rng(); - let x_challenge = Fr::rand(&mut rng); + let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); + let x_challenge = rand_fr(&mut rng); let k = 2; //π‘₯Β²^ᡏ⁺¹ @@ -802,10 +801,10 @@ mod test { let num_vars = 3; // Construct a random multilinear polynomial f, and (u,v) such that f(u) = v. - let mut rng = test_rng(); + let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); let challenges: Vec<_> = (0..num_vars) .into_iter() - .map(|_| Fr::rand(&mut rng)) + .map(|_| rand_fr::(&mut rng)) .collect(); let u_rev = { @@ -814,9 +813,9 @@ mod test { res }; - let x_challenge = Fr::rand(&mut rng); - let y_challenge = Fr::rand(&mut rng); - let z_challenge = Fr::rand(&mut rng); + let x_challenge = rand_fr::(&mut rng); + let y_challenge = rand_fr::(&mut rng); + let z_challenge = rand_fr::(&mut rng); // Construct Z_x scalars let (_, (_, z_x_scalars)) = eval_and_quotient_scalars::( @@ -833,7 +832,8 @@ mod test { let mut scalar = x_pow_2k * &phi::(&x_pow_2kp1, num_vars - k - 1) - u_rev[k] * &phi::(&x_pow_2k, num_vars - k); scalar *= z_challenge; - scalar *= FieldElement::from(1).neg(); + //TODO: this could be a trouble spot + scalar.neg(); assert_eq!(z_x_scalars[k], scalar); } } diff --git a/math/src/polynomial/mod.rs b/math/src/polynomial/mod.rs index 8a0671776..47a29290b 100644 --- a/math/src/polynomial/mod.rs +++ b/math/src/polynomial/mod.rs @@ -1,7 +1,7 @@ use super::field::element::FieldElement; use crate::field::traits::{IsField, IsSubFieldOf}; use alloc::{borrow::ToOwned, vec, vec::Vec}; -use core::{fmt::Display, ops}; +use core::{fmt::Display, ops::{self, Index}}; pub mod dense_multilinear_poly; mod error; @@ -272,6 +272,18 @@ impl Polynomial> { } } +impl Index for Polynomial> +where + ::BaseType: Send + Sync, +{ + type Output = FieldElement; + + #[inline(always)] + fn index(&self, _index: usize) -> &FieldElement { + &(self.coefficients[_index]) + } +} + pub fn pad_with_zero_coefficients_to_length( pa: &mut Polynomial>, n: usize, From b57ff4707c49a1422bf7159aedcaa276fbdd4a8a Mon Sep 17 00:00:00 2001 From: PatStiles Date: Tue, 23 Jan 2024 23:51:58 -0600 Subject: [PATCH 15/23] fmt --- crypto/src/commitments/kzg.rs | 13 ++- crypto/src/commitments/traits.rs | 10 +- crypto/src/commitments/zeromorph/mod.rs | 2 +- crypto/src/commitments/zeromorph/structs.rs | 20 ++-- crypto/src/commitments/zeromorph/zeromorph.rs | 101 ++++++++++-------- math/src/polynomial/mod.rs | 5 +- 6 files changed, 85 insertions(+), 66 deletions(-) diff --git a/crypto/src/commitments/kzg.rs b/crypto/src/commitments/kzg.rs index 58536fa45..edde432e0 100644 --- a/crypto/src/commitments/kzg.rs +++ b/crypto/src/commitments/kzg.rs @@ -1,4 +1,4 @@ -use crate::fiat_shamir::transcript::{Transcript}; +use crate::fiat_shamir::transcript::Transcript; use super::traits::IsPolynomialCommitmentScheme; use alloc::{borrow::ToOwned, vec::Vec}; @@ -207,7 +207,8 @@ impl>, P ), ( &proof.neg(), - &(alpha_g2.operate_with(&(g2.operate_with_self(point.borrow().representative())).neg())), + &(alpha_g2 + .operate_with(&(g2.operate_with_self(point.borrow().representative())).neg())), ), ]); e == Ok(FieldElement::one()) @@ -371,7 +372,13 @@ mod tests { let y0 = FieldElement::from(9000); let upsilon = &FieldElement::from(1); - let proof = kzg.open_batch(&x, &[y0.clone(), y0.clone()], &[p0.clone(), p0], upsilon, None); + let proof = kzg.open_batch( + &x, + &[y0.clone(), y0.clone()], + &[p0.clone(), p0], + upsilon, + None, + ); assert!(kzg.verify_batch( &x, diff --git a/crypto/src/commitments/traits.rs b/crypto/src/commitments/traits.rs index 4c19f7ba5..7587d03a7 100644 --- a/crypto/src/commitments/traits.rs +++ b/crypto/src/commitments/traits.rs @@ -1,13 +1,11 @@ -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsField}, -}; +use lambdaworks_math::field::{element::FieldElement, traits::IsField}; use std::borrow::Borrow; -use crate::fiat_shamir::transcript::{self, Transcript}; +use crate::fiat_shamir::transcript::Transcript; // For Non-Hiding // For batching operations we use a transcript to supply random values. In the case of kzg -// - Using an option for the transcript was the simplest way to enforce domain separation (prover/verifier) +// - Using an option for the transcript was the simplest way to enforce domain separation (prover/verifier) // for the future I think each protocol should have its own domain separated transcript within its instance variables pub trait IsPolynomialCommitmentScheme { /// Allows for Univariate vs Multilinear PCS @@ -56,4 +54,4 @@ pub trait IsPolynomialCommitmentScheme { upsilon: &FieldElement, transcript: Option<&mut dyn Transcript>, ) -> bool; -} \ No newline at end of file +} diff --git a/crypto/src/commitments/zeromorph/mod.rs b/crypto/src/commitments/zeromorph/mod.rs index 9ae5d9710..b59e351e0 100644 --- a/crypto/src/commitments/zeromorph/mod.rs +++ b/crypto/src/commitments/zeromorph/mod.rs @@ -1,2 +1,2 @@ +pub mod structs; pub mod zeromorph; -pub mod structs; \ No newline at end of file diff --git a/crypto/src/commitments/zeromorph/structs.rs b/crypto/src/commitments/zeromorph/structs.rs index 48c7cbabe..85e8c331c 100644 --- a/crypto/src/commitments/zeromorph/structs.rs +++ b/crypto/src/commitments/zeromorph/structs.rs @@ -1,8 +1,10 @@ use core::mem; -use lambdaworks_math::{elliptic_curve::traits::IsPairing, errors::DeserializationError, traits::{AsBytes, Deserializable}}; - -use super::zeromorph::ZeromorphError; +use lambdaworks_math::{ + elliptic_curve::traits::IsPairing, + errors::DeserializationError, + traits::{AsBytes, Deserializable}, +}; //TODO: gate with alloc #[derive(Debug, Clone, Default)] @@ -12,8 +14,7 @@ pub struct ZeromorphSRS { pub g2_powers: [P::G2Point; 3], } -impl ZeromorphSRS

-{ +impl ZeromorphSRS

{ pub fn new(g1_powers: &[P::G1Point], g2_powers: &[P::G2Point; 3]) -> Self { Self { g1_powers: g1_powers.into(), @@ -148,14 +149,17 @@ where secondary_group.push(point); } - let secondary_group_slice = [secondary_group[0].clone(), secondary_group[1].clone(), secondary_group[2].clone()]; + let secondary_group_slice = [ + secondary_group[0].clone(), + secondary_group[1].clone(), + secondary_group[2].clone(), + ]; let srs = ZeromorphSRS::new(&main_group, &secondary_group_slice); Ok(srs) } } - #[derive(Clone, Debug)] pub struct ZeromorphProverKey { pub g1_powers: Vec, @@ -177,4 +181,4 @@ pub struct ZeromorphProof { pub q_hat_com: P::G1Point, pub q_k_com: Vec, } -*/ \ No newline at end of file +*/ diff --git a/crypto/src/commitments/zeromorph/zeromorph.rs b/crypto/src/commitments/zeromorph/zeromorph.rs index 28f65eb82..36ba098fd 100644 --- a/crypto/src/commitments/zeromorph/zeromorph.rs +++ b/crypto/src/commitments/zeromorph/zeromorph.rs @@ -1,7 +1,9 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::type_complexity)] -use crate::{commitments::traits::IsPolynomialCommitmentScheme, fiat_shamir::transcript::Transcript}; +use crate::{ + commitments::traits::IsPolynomialCommitmentScheme, fiat_shamir::transcript::Transcript, +}; use lambdaworks_math::{ cyclic_group::IsGroup, elliptic_curve::traits::IsPairing, @@ -189,9 +191,7 @@ pub struct Zeromorph { _phantom: PhantomData

, } -impl Zeromorph

{ - -} +impl Zeromorph

{} impl IsPolynomialCommitmentScheme for Zeromorph

where @@ -221,7 +221,7 @@ where point: impl Borrow, eval: &FieldElement, poly: &Self::Polynomial, - transcript: Option<&mut dyn Transcript> + transcript: Option<&mut dyn Transcript>, ) -> Self::Proof { let point = point.borrow(); //TODO: error or interface or something @@ -263,7 +263,11 @@ where .iter() .map(|eval| eval.representative()) .collect(); - msm(&scalars[offset..], &self.srs.g1_powers[offset..q_hat.coeff_len()]).unwrap() + msm( + &scalars[offset..], + &self.srs.g1_powers[offset..q_hat.coeff_len()], + ) + .unwrap() }; transcript.append(&q_hat_com.as_bytes()); @@ -329,7 +333,10 @@ where let mut scalar = FieldElement::one(); let (f_batched, batched_evaluation) = (0..polys.len()).fold( ( - DenseMultilinearPolynomial::new(vec![FieldElement::zero(); 1 << polys[0].num_vars()]), + DenseMultilinearPolynomial::new(vec![ + FieldElement::zero(); + 1 << polys[0].num_vars() + ]), FieldElement::zero(), ), |(mut f_batched, mut batched_evaluation), i| { @@ -390,11 +397,7 @@ where }); let scalars = [ - vec![ - FieldElement::one(), - z_challenge, - (eval * eval_scalar), - ], + vec![FieldElement::one(), z_challenge, (eval * eval_scalar)], q_scalars, ] .concat(); @@ -402,9 +405,9 @@ where let bases = [vec![*q_hat_com, *p_commitment, g1], *q_k_com].concat(); let zeta_z_com = { let scalars: Vec<_> = scalars - .iter() - .map(|scalar| scalar.representative()) - .collect(); + .iter() + .map(|scalar| scalar.representative()) + .collect(); msm(&scalars, &bases).expect("`points` is sliced by `cs`'s length") }; @@ -439,14 +442,21 @@ where (FieldElement::zero(), P::G1Point::neutral_element()), |(mut batched_eval, mut batched_commitment), (eval, commitment)| { batched_eval += scalar * eval; - batched_commitment.operate_with(&commitment.operate_with_self(scalar.representative())); + batched_commitment + .operate_with(&commitment.operate_with_self(scalar.representative())); scalar *= rho; (batched_eval, batched_commitment) }, ); - Self::verify(&self, point, &batched_eval, &batched_commitment, proof, transcript) + Self::verify( + &self, + point, + &batched_eval, + &batched_commitment, + proof, + transcript, + ) } - } #[cfg(test)] @@ -458,7 +468,10 @@ mod test { use super::*; use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::pairing::BLS12381AtePairing; - use rand_chacha::{rand_core::{RngCore, SeedableRng}, ChaCha20Rng}; + use rand_chacha::{ + rand_core::{RngCore, SeedableRng}, + ChaCha20Rng, + }; // Evaluate Phi_k(x) = \sum_{i=0}^k x^i using the direct inefficent formula fn phi( @@ -502,22 +515,16 @@ mod test { .map(|_| rand_fr(&mut rng)) .collect::>(), ); - let point = (0..num_vars) - .map(|_| rand_fr(&mut rng)) - .collect::>(); + let point = (0..num_vars).map(|_| rand_fr(&mut rng)).collect::>(); let eval = poly.evaluate(point).unwrap(); // Commit and open let commitments = zm.commit(poly.clone()); let mut prover_transcript = DefaultTranscript::new(); - let proof = zm.open( - point, - eval, - poly, - Some(&mut prover_transcript), - ) - .unwrap(); + let proof = zm + .open(point, eval, poly, Some(&mut prover_transcript)) + .unwrap(); let mut verifier_transcript = DefaultTranscript::new(); zm.verify( @@ -572,24 +579,21 @@ mod test { Zeromorph::::commit(polys, &pk.g1_powers).unwrap(); let mut prover_transcript = DefaultTranscript::new(); - let proof = zm.open_batch( - evals, - challenges, - &pk, - polys, - Some(&mut prover_transcript), - ) - .unwrap(); + let proof = zm + .open_batch(evals, challenges, &pk, polys, Some(&mut prover_transcript)) + .unwrap(); let mut verifier_transcript = DefaultTranscript::new(); - assert!(zm.verify_batch( - commitments, - evals, - challenges, - &vk, - proof, - Some(&mut verifier_transcript), - ) == true) + assert!( + zm.verify_batch( + commitments, + evals, + challenges, + &vk, + proof, + Some(&mut verifier_transcript), + ) == true + ) //TODO: check both random oracles are synced } } @@ -712,7 +716,8 @@ mod test { batched_quotient_expected = batched_quotient_expected + &q_0_lifted; batched_quotient_expected = batched_quotient_expected + &(q_1_lifted * y_challenge); - batched_quotient_expected = batched_quotient_expected + &(q_2_lifted * (y_challenge * y_challenge)); + batched_quotient_expected = + batched_quotient_expected + &(q_2_lifted * (y_challenge * y_challenge)); assert_eq!(batched_quotient, batched_quotient_expected); } @@ -730,7 +735,9 @@ mod test { let x_challenge = rand_fr::(&mut rng); let y_challenge = rand_fr::(&mut rng); - let challenges: Vec<_> = (0..num_vars).map(|_| rand_fr::(&mut rng)).collect(); + let challenges: Vec<_> = (0..num_vars) + .map(|_| rand_fr::(&mut rng)) + .collect(); let z_challenge = rand_fr::(&mut rng); let (_, (zeta_x_scalars, _)) = eval_and_quotient_scalars::( diff --git a/math/src/polynomial/mod.rs b/math/src/polynomial/mod.rs index 47a29290b..533370517 100644 --- a/math/src/polynomial/mod.rs +++ b/math/src/polynomial/mod.rs @@ -1,7 +1,10 @@ use super::field::element::FieldElement; use crate::field::traits::{IsField, IsSubFieldOf}; use alloc::{borrow::ToOwned, vec, vec::Vec}; -use core::{fmt::Display, ops::{self, Index}}; +use core::{ + fmt::Display, + ops::{self, Index}, +}; pub mod dense_multilinear_poly; mod error; From 1c56a7b1fde129c7ff196b86ba15a0e288925261 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Wed, 24 Jan 2024 00:44:22 -0600 Subject: [PATCH 16/23] everything but srs --- crypto/src/commitments/zeromorph/zeromorph.rs | 64 +++++++++++-------- math/src/polynomial/dense_multilinear_poly.rs | 2 +- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/crypto/src/commitments/zeromorph/zeromorph.rs b/crypto/src/commitments/zeromorph/zeromorph.rs index 36ba098fd..1526447e4 100644 --- a/crypto/src/commitments/zeromorph/zeromorph.rs +++ b/crypto/src/commitments/zeromorph/zeromorph.rs @@ -13,7 +13,7 @@ use lambdaworks_math::{ }, msm::pippenger::msm, polynomial::{dense_multilinear_poly::DenseMultilinearPolynomial, Polynomial}, - traits::ByteConversion, + traits::{AsBytes, ByteConversion}, unsigned_integer::element::UnsignedInteger, }; use std::{borrow::Borrow, iter, marker::PhantomData}; @@ -196,9 +196,11 @@ impl Zeromorph

{} impl IsPolynomialCommitmentScheme for Zeromorph

where <

::BaseField as IsField>::BaseType: Send + Sync, +

::G1Point: AsBytes, +

::G2Point: AsBytes, +

::BaseField: IsPrimeField>, FieldElement<

::BaseField>: ByteConversion, //TODO: how to eliminate this complication in the trait interface -

::BaseField: IsPrimeField>, { type Polynomial = DenseMultilinearPolynomial; type Commitment = P::G1Point; @@ -239,7 +241,15 @@ where let q_k_commitments: Vec<_> = quotients .iter() .map(|q| { - let q_k_commitment = self.commit(q); + let q_k_commitment = { + //TODO: we only need to convert the offset scalars + let scalars: Vec<_> = q + .coefficients + .iter() + .map(|eval| eval.representative()) + .collect(); + msm(&scalars, &self.srs.g1_powers[..pi_poly.coeff_len()]).unwrap() + }; transcript.append(&q_k_commitment.as_bytes()); q_k_commitment }) @@ -434,10 +444,10 @@ where ) -> bool { debug_assert_eq!(evals.len(), p_commitments.len()); // Compute powers of batching challenge rho - let rho = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + let rho = FieldElement::from_bytes_be(&transcript.expect("oh no! No transcript").challenge()).unwrap(); // Compute batching of unshifted polynomials f_i: - let mut scalar = FieldElement::one(); + let mut scalar = FieldElement::::one(); let (batched_eval, batched_commitment) = evals.iter().zip(p_commitments.iter()).fold( (FieldElement::zero(), P::G1Point::neutral_element()), |(mut batched_eval, mut batched_commitment), (eval, commitment)| { @@ -467,7 +477,7 @@ mod test { use crate::fiat_shamir::default_transcript::DefaultTranscript; use super::*; - use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::pairing::BLS12381AtePairing; + use lambdaworks_math::{elliptic_curve::short_weierstrass::curves::bls12_381::pairing::BLS12381AtePairing, polynomial::dense_multilinear_poly::log_2}; use rand_chacha::{ rand_core::{RngCore, SeedableRng}, ChaCha20Rng, @@ -501,7 +511,7 @@ mod test { fn prove_verify_single() { let max_vars = 16; let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); - let srs = todo!(); + let srs = ; let zm = Zeromorph::::new(); for num_vars in 3..max_vars { @@ -546,7 +556,7 @@ mod test { let max_vars = 16; let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); let num_polys = 8; - let srs = todo!(); + let srs = ; let zm = Zeromorph::::new(); for num_vars in 3..max_vars { @@ -559,14 +569,14 @@ mod test { .map(|_| { DenseMultilinearPolynomial::new( (0..(1 << num_vars)) - .map(|_| Fr::rand(&mut rng)) + .map(|_| rand_fr(&mut rng)) .collect::>(), ) }) .collect::>(); let challenges = (0..num_vars) .into_iter() - .map(|_| Fr::rand(&mut rng)) + .map(|_| rand_fr(&mut rng)) .collect::>(); let evals = polys .clone() @@ -612,12 +622,12 @@ mod test { // Construct a random multilinear polynomial f, and (u,v) such that f(u) = v let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); let multilinear_f = - DenseMultilinearPolynomial::new((0..n).map(|_| Fr::rand(&mut rng)).collect::>()); + DenseMultilinearPolynomial::new((0..n).map(|_| rand_fr::(&mut rng)).collect::>()); let u_challenge = (0..num_vars) .into_iter() - .map(|_| rand_fr(&mut rng)) + .map(|_| rand_fr::(&mut rng)) .collect::>(); - let v_evaluation = multilinear_f.evaluate(&u_challenge).unwrap(); + let v_evaluation = multilinear_f.evaluate(u_challenge).unwrap(); // Compute multilinear quotients `qβ‚–(𝑋₀, …, 𝑋ₖ₋₁)` let (quotients, constant_term) = @@ -632,19 +642,19 @@ mod test { //To demonstrate that q_k was properly constructd we show that the identity holds at a random multilinear challenge // i.e. 𝑓(𝑧) βˆ’ 𝑣 βˆ’ βˆ‘β‚–β‚Œβ‚€α΅ˆβ»ΒΉ (𝑧ₖ βˆ’ 𝑒ₖ)π‘žβ‚–(𝑧) = 0 let z_challenge = (0..num_vars) - .map(|_| Fr::rand(&mut rng)) + .map(|_| rand_fr::(&mut rng)) .collect::>(); - let mut res = multilinear_f.evaluate(&z_challenge).unwrap(); + let mut res = multilinear_f.evaluate(z_challenge).unwrap(); res = res - v_evaluation; for (k, q_k_uni) in quotients.iter().enumerate() { - let z_partial = &z_challenge[&z_challenge.len() - k..]; + let z_partial = z_challenge[&z_challenge.len() - k..].to_vec(); //This is a weird consequence of how things are done.. the univariate polys are of the multilinear commitment in lagrange basis. Therefore we evaluate as multilinear let q_k = DenseMultilinearPolynomial::new(q_k_uni.coefficients.clone()); - let q_k_eval = q_k.evaluate(z_partial); + let q_k_eval = q_k.evaluate(z_partial).unwrap(); - res -= (z_challenge[z_challenge.len() - k - 1] + res = res - (z_challenge[z_challenge.len() - k - 1] - u_challenge[z_challenge.len() - k - 1]) * q_k_eval; } @@ -670,7 +680,7 @@ mod test { let quotients = vec![q_0, q_1, q_2]; let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); - let y_challenge = rand_fr(&mut rng); + let y_challenge = rand_fr::(&mut rng); //Compute batched quptient Μ‚q let batched_quotient = compute_batched_lifted_degree_quotient::( @@ -767,15 +777,15 @@ mod test { #[test] fn phi_n_x_evaluation() { const N: u64 = 8u64; - let log_N = (N as usize).log_2(); + let log_n = log_2(N as usize); // 𝛷ₖ(π‘₯) let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); - let x_challenge = rand_fr(&mut rng); + let x_challenge = rand_fr::(&mut rng); - let efficient = (x_challenge.pow((1 << log_N) as u64) - FieldElement::one()) + let efficient = (x_challenge.pow((1 << log_n) as u64) - FieldElement::<::BaseField>::one()) / (x_challenge - FieldElement::one()); - let expected: FieldElement<_> = phi::(&x_challenge, log_N); + let expected: FieldElement<_> = phi::(&x_challenge, log_n); assert_eq!(efficient, expected); } @@ -784,20 +794,20 @@ mod test { #[test] fn phi_n_k_1_x_evaluation() { const N: u64 = 8u64; - let log_N = (N as usize).log_2(); + let log_n = log_2(N as usize); // 𝛷ₖ(π‘₯) let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); - let x_challenge = rand_fr(&mut rng); + let x_challenge = rand_fr::(&mut rng); let k = 2; //π‘₯Β²^ᡏ⁺¹ let x_pow = x_challenge.pow((1 << (k + 1)) as u64); //(π‘₯Β²^ⁿ βˆ’ 1) / (π‘₯Β²^ᡏ⁺¹ βˆ’ 1) - let efficient = (x_challenge.pow((1 << log_N) as u64) - FieldElement::one()) + let efficient = (x_challenge.pow((1 << log_n) as u64) - FieldElement::<::BaseField>::one()) / (x_pow - FieldElement::one()); - let expected: FieldElement<_> = phi::(&x_challenge, log_N - k - 1); + let expected: FieldElement<_> = phi::(&x_challenge, log_n - k - 1); assert_eq!(efficient, expected); } diff --git a/math/src/polynomial/dense_multilinear_poly.rs b/math/src/polynomial/dense_multilinear_poly.rs index a1a1527a9..9ea24826e 100644 --- a/math/src/polynomial/dense_multilinear_poly.rs +++ b/math/src/polynomial/dense_multilinear_poly.rs @@ -202,7 +202,7 @@ where } // returns 0 if n is 0 -fn log_2(n: usize) -> usize { +pub fn log_2(n: usize) -> usize { if n == 0 { return 0; } From 2da15427799d4ca1a7907c8b8ee0975ca9a7c9c0 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Wed, 24 Jan 2024 01:15:14 -0600 Subject: [PATCH 17/23] fix bugs --- crypto/src/commitments/zeromorph/zeromorph.rs | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/crypto/src/commitments/zeromorph/zeromorph.rs b/crypto/src/commitments/zeromorph/zeromorph.rs index 1526447e4..c0d720d3b 100644 --- a/crypto/src/commitments/zeromorph/zeromorph.rs +++ b/crypto/src/commitments/zeromorph/zeromorph.rs @@ -444,7 +444,9 @@ where ) -> bool { debug_assert_eq!(evals.len(), p_commitments.len()); // Compute powers of batching challenge rho - let rho = FieldElement::from_bytes_be(&transcript.expect("oh no! No transcript").challenge()).unwrap(); + let rho = + FieldElement::from_bytes_be(&transcript.expect("oh no! No transcript").challenge()) + .unwrap(); // Compute batching of unshifted polynomials f_i: let mut scalar = FieldElement::::one(); @@ -477,7 +479,10 @@ mod test { use crate::fiat_shamir::default_transcript::DefaultTranscript; use super::*; - use lambdaworks_math::{elliptic_curve::short_weierstrass::curves::bls12_381::pairing::BLS12381AtePairing, polynomial::dense_multilinear_poly::log_2}; + use lambdaworks_math::{ + elliptic_curve::short_weierstrass::curves::bls12_381::pairing::BLS12381AtePairing, + polynomial::dense_multilinear_poly::log_2, + }; use rand_chacha::{ rand_core::{RngCore, SeedableRng}, ChaCha20Rng, @@ -511,7 +516,7 @@ mod test { fn prove_verify_single() { let max_vars = 16; let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); - let srs = ; + let srs = 9; let zm = Zeromorph::::new(); for num_vars in 3..max_vars { @@ -556,7 +561,7 @@ mod test { let max_vars = 16; let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); let num_polys = 8; - let srs = ; + let srs = 9; let zm = Zeromorph::::new(); for num_vars in 3..max_vars { @@ -621,8 +626,11 @@ mod test { // Construct a random multilinear polynomial f, and (u,v) such that f(u) = v let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); - let multilinear_f = - DenseMultilinearPolynomial::new((0..n).map(|_| rand_fr::(&mut rng)).collect::>()); + let multilinear_f = DenseMultilinearPolynomial::new( + (0..n) + .map(|_| rand_fr::(&mut rng)) + .collect::>(), + ); let u_challenge = (0..num_vars) .into_iter() .map(|_| rand_fr::(&mut rng)) @@ -654,9 +662,9 @@ mod test { let q_k = DenseMultilinearPolynomial::new(q_k_uni.coefficients.clone()); let q_k_eval = q_k.evaluate(z_partial).unwrap(); - res = res - (z_challenge[z_challenge.len() - k - 1] - - u_challenge[z_challenge.len() - k - 1]) - * q_k_eval; + res = res + - (z_challenge[z_challenge.len() - k - 1] - u_challenge[z_challenge.len() - k - 1]) + * q_k_eval; } assert_eq!(res, FieldElement::zero()); } @@ -783,7 +791,8 @@ mod test { let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); let x_challenge = rand_fr::(&mut rng); - let efficient = (x_challenge.pow((1 << log_n) as u64) - FieldElement::<::BaseField>::one()) + let efficient = (x_challenge.pow((1 << log_n) as u64) + - FieldElement::<::BaseField>::one()) / (x_challenge - FieldElement::one()); let expected: FieldElement<_> = phi::(&x_challenge, log_n); assert_eq!(efficient, expected); @@ -805,7 +814,8 @@ mod test { let x_pow = x_challenge.pow((1 << (k + 1)) as u64); //(π‘₯Β²^ⁿ βˆ’ 1) / (π‘₯Β²^ᡏ⁺¹ βˆ’ 1) - let efficient = (x_challenge.pow((1 << log_n) as u64) - FieldElement::<::BaseField>::one()) + let efficient = (x_challenge.pow((1 << log_n) as u64) + - FieldElement::<::BaseField>::one()) / (x_pow - FieldElement::one()); let expected: FieldElement<_> = phi::(&x_challenge, log_n - k - 1); assert_eq!(efficient, expected); From d2ceed25c37885b27468033d23e4f29b5069d3f0 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Thu, 25 Jan 2024 02:01:06 -0600 Subject: [PATCH 18/23] implemented and integrated. verification failing --- crypto/Cargo.toml | 1 + crypto/src/commitments/kzg.rs | 67 +-- crypto/src/commitments/traits.rs | 2 - crypto/src/commitments/zeromorph/structs.rs | 171 +++++--- crypto/src/commitments/zeromorph/zeromorph.rs | 411 ++++++++++++------ crypto/src/errors.rs | 42 ++ math/src/polynomial/mod.rs | 11 +- provers/plonk/src/prover.rs | 85 +++- provers/plonk/src/setup.rs | 11 +- provers/plonk/src/verifier.rs | 39 +- 10 files changed, 593 insertions(+), 247 deletions(-) diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 3d95fde2d..784b511a7 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -15,6 +15,7 @@ sha2 = { version = "0.10", default-features = false } # Optional serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true } rayon = { version = "1.8.0", optional = true } +rand = "0.8.5" [dev-dependencies] criterion = "0.4" diff --git a/crypto/src/commitments/kzg.rs b/crypto/src/commitments/kzg.rs index edde432e0..d4b37691d 100644 --- a/crypto/src/commitments/kzg.rs +++ b/crypto/src/commitments/kzg.rs @@ -1,7 +1,7 @@ use crate::fiat_shamir::transcript::Transcript; use super::traits::IsPolynomialCommitmentScheme; -use alloc::{borrow::ToOwned, vec::Vec}; +use alloc::vec::Vec; use core::{borrow::Borrow, marker::PhantomData, mem}; use lambdaworks_math::{ cyclic_group::IsGroup, @@ -10,7 +10,7 @@ use lambdaworks_math::{ field::{element::FieldElement, traits::IsPrimeField}, msm::pippenger::msm, polynomial::Polynomial, - traits::{AsBytes, Deserializable}, + traits::{AsBytes, ByteConversion, Deserializable}, unsigned_integer::element::UnsignedInteger, }; @@ -154,10 +154,12 @@ impl KateZaveruchaGoldberg { impl>, P: IsPairing> IsPolynomialCommitmentScheme for KateZaveruchaGoldberg +where + FieldElement: ByteConversion, { type Commitment = P::G1Point; type Polynomial = Polynomial>; - type Proof = Self::Commitment; + type Proof = P::G1Point; type Point = FieldElement; fn commit(&self, p: &Polynomial>) -> Self::Commitment { @@ -193,7 +195,7 @@ impl>, P point: impl Borrow, eval: &FieldElement, p_commitment: &Self::Commitment, - proof: &Self::Commitment, + proof: &Self::Proof, transcript: Option<&mut dyn Transcript>, ) -> bool { let g1 = &self.srs.powers_main_group[0]; @@ -219,20 +221,21 @@ impl>, P point: impl Borrow, evals: &[FieldElement], polys: &[Polynomial>], - upsilon: &FieldElement, transcript: Option<&mut dyn Transcript>, ) -> Self::Commitment { + let transcript = transcript.unwrap(); + let upsilon = FieldElement::::from_bytes_be(&transcript.challenge()).unwrap(); let acc_polynomial = polys .iter() .rev() .fold(Polynomial::zero(), |acc, polynomial| { - acc * upsilon.to_owned() + polynomial + acc * &upsilon + polynomial }); let acc_y = evals .iter() .rev() - .fold(FieldElement::zero(), |acc, y| acc * upsilon.to_owned() + y); + .fold(FieldElement::zero(), |acc, y| acc * &upsilon + y); self.open(point, &acc_y, &acc_polynomial, None) } @@ -243,22 +246,23 @@ impl>, P evals: &[FieldElement], p_commitments: &[Self::Commitment], proof: &Self::Commitment, - upsilon: &FieldElement, transcript: Option<&mut dyn Transcript>, ) -> bool { + let transcript = transcript.unwrap(); + let upsilon = FieldElement::::from_bytes_be(&transcript.challenge()).unwrap(); let acc_commitment = p_commitments .iter() .rev() .fold(P::G1Point::neutral_element(), |acc, point| { - acc.operate_with_self(upsilon.to_owned().representative()) - .operate_with(point.borrow()) + acc.operate_with_self(upsilon.representative()) + .operate_with(point) }); let acc_y = evals .iter() .rev() - .fold(FieldElement::zero(), |acc, y| acc * upsilon.to_owned() + y); + .fold(FieldElement::zero(), |acc, y| acc * &upsilon + y); self.verify(point, &acc_y, &acc_commitment, proof, None) } } @@ -286,7 +290,10 @@ mod tests { unsigned_integer::element::U256, }; - use crate::commitments::traits::IsPolynomialCommitmentScheme; + use crate::{ + commitments::traits::IsPolynomialCommitmentScheme, + fiat_shamir::default_transcript::DefaultTranscript, + }; use super::{KateZaveruchaGoldberg, StructuredReferenceString}; use rand::Rng; @@ -355,11 +362,18 @@ mod tests { let x = FieldElement::one(); let y0 = FieldElement::from(9000); - let upsilon = &FieldElement::from(1); - let proof = kzg.open_batch(&x, &[y0.clone()], &[p0], upsilon, None); + let mut prover_transcript = DefaultTranscript::new(); + let proof = kzg.open_batch(&x, &[y0.clone()], &[p0], Some(&mut prover_transcript)); - assert!(kzg.verify_batch(&x, &[y0], &[p0_commitment], &proof, upsilon, None)); + let mut verifer_transcript = DefaultTranscript::new(); + assert!(kzg.verify_batch( + &x, + &[y0], + &[p0_commitment], + &proof, + Some(&mut verifer_transcript) + )); } #[test] @@ -370,23 +384,22 @@ mod tests { let x = FieldElement::one(); let y0 = FieldElement::from(9000); - let upsilon = &FieldElement::from(1); + let mut prover_transcript = DefaultTranscript::new(); let proof = kzg.open_batch( &x, &[y0.clone(), y0.clone()], &[p0.clone(), p0], - upsilon, - None, + Some(&mut prover_transcript), ); + let mut verifer_transcript = DefaultTranscript::new(); assert!(kzg.verify_batch( &x, &[y0.clone(), y0], &[p0_commitment.clone(), p0_commitment], &proof, - upsilon, - None + Some(&mut verifer_transcript), )); } @@ -408,17 +421,21 @@ mod tests { let p1_commitment: ::G1Point = kzg.commit(&p1); let y1 = p1.evaluate(&x); - let upsilon = &FieldElement::from(1); - - let proof = kzg.open_batch(&x, &[y0.clone(), y1.clone()], &[p0, p1], upsilon, None); + let mut prover_transcript = DefaultTranscript::new(); + let proof = kzg.open_batch( + &x, + &[y0.clone(), y1.clone()], + &[p0, p1], + Some(&mut prover_transcript), + ); + let mut verifer_transcript = DefaultTranscript::new(); assert!(kzg.verify_batch( &x, &[y0, y1], &[p0_commitment, p1_commitment], &proof, - upsilon, - None + Some(&mut verifer_transcript) )); } diff --git a/crypto/src/commitments/traits.rs b/crypto/src/commitments/traits.rs index 7587d03a7..bbfa9c960 100644 --- a/crypto/src/commitments/traits.rs +++ b/crypto/src/commitments/traits.rs @@ -32,7 +32,6 @@ pub trait IsPolynomialCommitmentScheme { point: impl Borrow, eval: &[FieldElement], polys: &[Self::Polynomial], - upsilon: &FieldElement, transcript: Option<&mut dyn Transcript>, ) -> Self::Proof; @@ -51,7 +50,6 @@ pub trait IsPolynomialCommitmentScheme { evals: &[FieldElement], p_commitments: &[Self::Commitment], proof: &Self::Proof, - upsilon: &FieldElement, transcript: Option<&mut dyn Transcript>, ) -> bool; } diff --git a/crypto/src/commitments/zeromorph/structs.rs b/crypto/src/commitments/zeromorph/structs.rs index 85e8c331c..7685b6491 100644 --- a/crypto/src/commitments/zeromorph/structs.rs +++ b/crypto/src/commitments/zeromorph/structs.rs @@ -1,29 +1,84 @@ -use core::mem; +use core::{cmp::max, mem}; use lambdaworks_math::{ + cyclic_group::IsGroup, elliptic_curve::traits::IsPairing, errors::DeserializationError, - traits::{AsBytes, Deserializable}, + field::{element::FieldElement, traits::IsPrimeField}, + msm::pippenger::msm, + traits::{AsBytes, ByteConversion, Deserializable}, }; +//TODO: have own random gen type +use rand::RngCore; + //TODO: gate with alloc #[derive(Debug, Clone, Default)] pub struct ZeromorphSRS { pub g1_powers: Vec, - /// [g2: (P::G2Point), tau_2: (P::G2Point), tau_n_max_sub_2_n: (P::G2Point)] - pub g2_powers: [P::G2Point; 3], + pub g2_powers: Vec, } impl ZeromorphSRS

{ - pub fn new(g1_powers: &[P::G1Point], g2_powers: &[P::G2Point; 3]) -> Self { + /// Performs + // TODO(pat_stiles): accept a Rng or Rng wrapper. + // TODO(pat_stiles): Quality of life improvements in regards to random sampling + // TODO: errors lengths are valid + // TODO: Result + pub fn setup(max_degree: usize, rng: &mut R) -> ZeromorphSRS

+ where +

::BaseField: IsPrimeField, + FieldElement<

::BaseField>: ByteConversion, + { + let mut bytes = [0u8; 384]; + rng.fill_bytes(&mut bytes); + let tau = FieldElement::::from_bytes_be(&bytes).unwrap(); + rng.fill_bytes(&mut bytes); + let g1_scalar = FieldElement::::from_bytes_be(&bytes).unwrap(); + let g1 = P::G1Point::neutral_element().operate_with_self(g1_scalar.representative()); + rng.fill_bytes(&mut bytes); + let g2_scalar = FieldElement::::from_bytes_be(&bytes).unwrap(); + let g2 = P::G2Point::neutral_element().operate_with_self(g2_scalar.representative()); + + //TODO: Add window table for msm this is slow af + let tau_powers = (0..=max_degree) + .scan(tau.clone(), |state, _| { + let val = state.clone(); + *state *= τ + Some(val) + }) + .map(|scalar| scalar.representative()) + .collect::>(); + + let g1s: Vec<_> = (0..=max_degree).map(|e| g1.operate_with_self(e)).collect(); + let g2s: Vec<_> = (0..=max_degree).map(|e| g2.operate_with_self(e)).collect(); + + //TODO: gate with rayon + let g1_powers = g1s + .iter() + .zip(tau_powers.iter()) + .map(|(g1, tau)| g1.operate_with_self(*tau)) + .collect(); + let g2_powers = g2s + .iter() + .zip(tau_powers.iter()) + .map(|(g2, tau)| g2.operate_with_self(*tau)) + .collect(); + + ZeromorphSRS { + g1_powers, + g2_powers, + } + } + + pub fn new(g1_powers: &[P::G1Point], g2_powers: &[P::G2Point]) -> Self { Self { - g1_powers: g1_powers.into(), - g2_powers: g2_powers.clone(), + g1_powers: g1_powers.to_vec(), + g2_powers: g2_powers.to_vec(), } } - //TODO: Delete? - /* + // TODO: errors lengths are valid pub fn trim( &self, max_degree: usize, @@ -42,14 +97,13 @@ impl ZeromorphSRS

{ offset_g1_powers: offset_g1_powers, }, ZeromorphVerifierKey { - g1: self.g1_powers[0], - g2: self.g2_powers[0], - tau_2: self.g2_powers[1], - tau_n_max_sub_2_n: self.g2_powers[offset], + g1: self.g1_powers[0].clone(), + g2: self.g2_powers[0].clone(), + tau_g2: self.g2_powers[1].clone(), + tau_n_max_sub_2_n: self.g2_powers[offset].clone(), }, )) } - */ } #[cfg(feature = "std")] @@ -77,15 +131,26 @@ where serialized_data.extend(&protocol_version); // Second 8 bytes store the amount of G1 elements to be stored, this is more than can be indexed with a 64-bit architecture, and some millions of terabytes of data if the points were compressed - let mut main_group_len_bytes: Vec = self.g1_powers.len().to_le_bytes().to_vec(); + let mut g1_powers_len_bytes: Vec = self.g1_powers.len().to_le_bytes().to_vec(); + + // For architectures with less than 64 bits for pointers + // We add extra zeros at the end + while g1_powers_len_bytes.len() < 8 { + g1_powers_len_bytes.push(0) + } + + serialized_data.extend(&g1_powers_len_bytes); + + // third 8 bytes store the amount of G2 elements to be stored, this is more than can be indexed with a 64-bit architecture, and some millions of terabytes of data if the points were compressed + let mut g2_powers_len_bytes: Vec = self.g2_powers.len().to_le_bytes().to_vec(); // For architectures with less than 64 bits for pointers // We add extra zeros at the end - while main_group_len_bytes.len() < 8 { - main_group_len_bytes.push(0) + while g2_powers_len_bytes.len() < 8 { + g2_powers_len_bytes.push(0) } - serialized_data.extend(&main_group_len_bytes); + serialized_data.extend(&g2_powers_len_bytes); // G1 elements for point in &self.g1_powers { @@ -107,78 +172,82 @@ where

::G2Point: Deserializable, { fn deserialize(bytes: &[u8]) -> Result { - const MAIN_GROUP_LEN_OFFSET: usize = 4; - const MAIN_GROUP_OFFSET: usize = 12; + const VERSION_OFFSET: usize = 4; + const G1_LEN_OFFSET: usize = 12; + const G2_LEN_OFFSET: usize = 20; + + let g1_powers_len_u64 = u64::from_le_bytes( + // This unwrap can't fail since we are fixing the size of the slice + bytes[VERSION_OFFSET..G1_LEN_OFFSET].try_into().unwrap(), + ); + + let g1_powers_len = usize::try_from(g1_powers_len_u64) + .map_err(|_| DeserializationError::PointerSizeError)?; - let main_group_len_u64 = u64::from_le_bytes( + let g2_powers_len_u64 = u64::from_le_bytes( // This unwrap can't fail since we are fixing the size of the slice - bytes[MAIN_GROUP_LEN_OFFSET..MAIN_GROUP_OFFSET] - .try_into() - .unwrap(), + bytes[G1_LEN_OFFSET..G2_LEN_OFFSET].try_into().unwrap(), ); - let main_group_len = usize::try_from(main_group_len_u64) + let g2_powers_len = usize::try_from(g2_powers_len_u64) .map_err(|_| DeserializationError::PointerSizeError)?; - let mut main_group: Vec = Vec::new(); - let mut secondary_group: Vec = Vec::new(); + let mut g1_powers: Vec = Vec::new(); + let mut g2_powers: Vec = Vec::new(); let size_g1_point = mem::size_of::(); let size_g2_point = mem::size_of::(); - for i in 0..main_group_len { + for i in 0..g1_powers_len { // The second unwrap shouldn't fail since the amount of bytes is fixed let point = P::G1Point::deserialize( - bytes[i * size_g1_point + MAIN_GROUP_OFFSET - ..i * size_g1_point + size_g1_point + MAIN_GROUP_OFFSET] + bytes[i * size_g1_point + G2_LEN_OFFSET + ..i * size_g1_point + size_g1_point + G2_LEN_OFFSET] .try_into() .unwrap(), )?; - main_group.push(point); + g1_powers.push(point); } - let g2s_offset = size_g1_point * main_group_len + MAIN_GROUP_OFFSET; - for i in 0..3 { + let g2s_offset = size_g1_point * g1_powers_len + G2_LEN_OFFSET; + for i in 0..g2_powers_len { // The second unwrap shouldn't fail since the amount of bytes is fixed let point = P::G2Point::deserialize( - bytes[i * size_g2_point + g2s_offset - ..i * size_g2_point + g2s_offset + size_g2_point] + bytes[i * size_g2_point + g2s_offset..i * size_g2_point + g2s_offset] .try_into() .unwrap(), )?; - secondary_group.push(point); + g2_powers.push(point); } - let secondary_group_slice = [ - secondary_group[0].clone(), - secondary_group[1].clone(), - secondary_group[2].clone(), - ]; - - let srs = ZeromorphSRS::new(&main_group, &secondary_group_slice); + let srs = ZeromorphSRS::new(&g1_powers, &g2_powers); Ok(srs) } } +#[derive(Clone, Debug)] +pub struct ZeromorphProof { + pub pi: P::G1Point, + pub q_hat_com: P::G1Point, + pub q_k_com: Vec, +} + #[derive(Clone, Debug)] pub struct ZeromorphProverKey { pub g1_powers: Vec, pub offset_g1_powers: Vec, } -/* #[derive(Copy, Clone, Debug)] pub struct ZeromorphVerifierKey { pub g1: P::G1Point, pub g2: P::G2Point, - pub tau_2: P::G2Point, + pub tau_g2: P::G2Point, pub tau_n_max_sub_2_n: P::G2Point, } -#[derive(Clone, Debug)] -pub struct ZeromorphProof { - pub pi: P::G1Point, - pub q_hat_com: P::G1Point, - pub q_k_com: Vec, +#[derive(Debug)] +pub enum ZeromorphError { + //#[error("Length Error: SRS Length: {0}, Key Length: {0}")] + KeyLengthError(usize, usize), } -*/ diff --git a/crypto/src/commitments/zeromorph/zeromorph.rs b/crypto/src/commitments/zeromorph/zeromorph.rs index c0d720d3b..f3df942f4 100644 --- a/crypto/src/commitments/zeromorph/zeromorph.rs +++ b/crypto/src/commitments/zeromorph/zeromorph.rs @@ -4,16 +4,18 @@ use crate::{ commitments::traits::IsPolynomialCommitmentScheme, fiat_shamir::transcript::Transcript, }; +use core::mem; use lambdaworks_math::{ cyclic_group::IsGroup, elliptic_curve::traits::IsPairing, + errors::DeserializationError, field::{ element::FieldElement, traits::{IsField, IsPrimeField}, }, msm::pippenger::msm, polynomial::{dense_multilinear_poly::DenseMultilinearPolynomial, Polynomial}, - traits::{AsBytes, ByteConversion}, + traits::{AsBytes, ByteConversion, Deserializable}, unsigned_integer::element::UnsignedInteger, }; use std::{borrow::Borrow, iter, marker::PhantomData}; @@ -21,20 +23,7 @@ use std::{borrow::Borrow, iter, marker::PhantomData}; #[cfg(feature = "parallel")] use rayon::prelude::*; -use super::structs::ZeromorphSRS; - -#[derive(Clone, Debug)] -pub struct ZeromorphProof { - pub pi: P::G1Point, - pub q_hat_com: P::G1Point, - pub q_k_com: Vec, -} - -#[derive(Debug)] -pub enum ZeromorphError { - //#[error("Length Error: SRS Length: {0}, Key Length: {0}")] - KeyLengthError(usize, usize), -} +use super::structs::{ZeromorphProof, ZeromorphProverKey, ZeromorphVerifierKey}; fn compute_multilinear_quotients( poly: &DenseMultilinearPolynomial, @@ -66,7 +55,7 @@ where .zip(&*g_lo) .zip(&*g_hi) .for_each(|((q, g_lo), g_hi)| { - *q = *g_hi - *g_lo; + *q = g_hi - g_lo; }); #[cfg(feature = "parallel")] @@ -75,7 +64,7 @@ where #[cfg(not(feature = "parallel"))] let g_lo_iter = g_lo.iter_mut(); g_lo_iter.zip(g_hi).for_each(|(g_lo, g_hi)| { - *g_lo += (*g_hi - g_lo as &_) * x_i; + *g_lo += (&*g_hi - g_lo as &_) * x_i; }); g.truncate(1 << (poly.num_vars() - 1 - i)); @@ -84,7 +73,7 @@ where }) .collect(); quotients.reverse(); - (quotients, g[0]) + (quotients, g[0].clone()) } fn compute_batched_lifted_degree_quotient( @@ -107,7 +96,7 @@ fn compute_batched_lifted_degree_quotient( #[cfg(not(feature = "parallel"))] let q_hat_iter = q_hat[n - (1 << idx)..].iter_mut(); q_hat_iter.zip(&q.coefficients).for_each(|(q_hat, q)| { - *q_hat += scalar * q; + *q_hat += &scalar * q; }); scalar *= y_challenge; q_hat @@ -117,9 +106,9 @@ fn compute_batched_lifted_degree_quotient( } fn eval_and_quotient_scalars( - y_challenge: FieldElement, - x_challenge: FieldElement, - z_challenge: FieldElement, + y_challenge: &FieldElement, + x_challenge: &FieldElement, + z_challenge: &FieldElement, challenges: &[FieldElement], ) -> ( FieldElement, @@ -131,7 +120,7 @@ fn eval_and_quotient_scalars( let num_vars = challenges.len(); // squares of x = [x, x^2, .. x^{2^k}, .. x^{2^num_vars}] - let squares_of_x: Vec<_> = iter::successors(Some(x_challenge), |&x| Some(x.square())) + let squares_of_x: Vec<_> = iter::successors(Some(x_challenge.clone()), |x| Some(x.square())) .take(num_vars + 1) .collect(); @@ -143,7 +132,7 @@ fn eval_and_quotient_scalars( .skip(1) .scan(FieldElement::one(), |acc, pow_x| { *acc *= pow_x; - Some(*acc) + Some(acc.clone()) }) .collect::>(); offsets_of_x.reverse(); @@ -151,20 +140,21 @@ fn eval_and_quotient_scalars( }; let vs = { - let v_numer: FieldElement = squares_of_x[num_vars] - FieldElement::one(); + let v_numer: FieldElement = + squares_of_x[num_vars].clone() - FieldElement::one(); let mut v_denoms = squares_of_x .iter() - .map(|squares_of_x| *squares_of_x - FieldElement::one()) + .map(|square_of_x| square_of_x - FieldElement::one()) .collect::>(); //TODO: catch this unwrap() FieldElement::inplace_batch_inverse(&mut v_denoms).unwrap(); v_denoms .iter() - .map(|v_denom| v_numer * v_denom) + .map(|v_denom| &v_numer * v_denom) .collect::>() }; - let q_scalars = iter::successors(Some(FieldElement::one()), |acc| Some(*acc * y_challenge)) + let q_scalars = iter::successors(Some(FieldElement::one()), |acc| Some(acc * y_challenge)) .take(num_vars) .zip(offsets_of_x) .zip(squares_of_x) @@ -175,23 +165,195 @@ fn eval_and_quotient_scalars( |(((((power_of_y, offset_of_x), square_of_x), v_i), v_j), u_i)| { ( -(power_of_y * offset_of_x), - -(z_challenge * (square_of_x * v_j - *u_i * v_i)), + -(z_challenge * (square_of_x * v_j - u_i * v_i)), ) }, ) .unzip(); // -vs[0] * z = -z * (x^(2^num_vars) - 1) / (x - 1) = -z Ξ¦_n(x) - (-vs[0] * z_challenge, q_scalars) + (-vs[0].clone() * z_challenge, q_scalars) } -// TODO: how to handle the case in batching that the polynomials each have unique num_vars pub struct Zeromorph { - srs: ZeromorphSRS

, + pk: ZeromorphProverKey

, + vk: ZeromorphVerifierKey

, _phantom: PhantomData

, } -impl Zeromorph

{} +impl<'a, P: IsPairing> Zeromorph

+where +

::G1Point: Deserializable, +

::G2Point: Deserializable, +{ + //TODO: should we derive the pk and ck within this function directly from srs + pub fn new(pk: ZeromorphProverKey

, vk: ZeromorphVerifierKey

) -> Self { + Self { + pk, + vk, + _phantom: PhantomData, + } + } + + /// Extracts pk and vk from binary file + pub fn from_file( + file_path: &str, + ) -> Result { + let bytes = std::fs::read(file_path)?; + Ok(Self::deserialize(&bytes)?) + } +} + +impl AsBytes for Zeromorph

+where +

::G1Point: AsBytes, +

::G2Point: AsBytes, +{ + fn as_bytes(&self) -> Vec { + let mut serialized_data: Vec = Vec::new(); + // First 4 bytes encodes protocol version + let protocol_version: [u8; 4] = [0; 4]; + + serialized_data.extend(&protocol_version); + + // Second 8 bytes store the amount of G1 elements to be stored, this is more than can be indexed with a 64-bit architecture, and some millions of terabytes of data if the points were compressed + let mut g1_powers_len_bytes: Vec = self.pk.g1_powers.len().to_le_bytes().to_vec(); + + // For architectures with less than 64 bits for pointers + // We add extra zeros at the end + while g1_powers_len_bytes.len() < 8 { + g1_powers_len_bytes.push(0) + } + + serialized_data.extend(&g1_powers_len_bytes); + + // third 8 bytes store the amount of G1 offset elements to be stored, this is more than can be indexed with a 64-bit architecture, and some millions of terabytes of data if the points were compressed + let mut offset_g1_powers_len_bytes: Vec = + self.pk.offset_g1_powers.len().to_le_bytes().to_vec(); + + // For architectures with less than 64 bits for pointers + // We add extra zeros at the end + while offset_g1_powers_len_bytes.len() < 8 { + offset_g1_powers_len_bytes.push(0) + } + + serialized_data.extend(&offset_g1_powers_len_bytes); + + // G1 elements + for point in &self.pk.g1_powers { + serialized_data.extend(point.as_bytes()); + } + + // G2 elements + for point in &self.pk.offset_g1_powers { + serialized_data.extend(point.as_bytes()); + } + + // NOTE: this could potentially be recycled from the g1_powers but being explicit reduces complexity + serialized_data.extend(&self.vk.g1.as_bytes()); + serialized_data.extend(&self.vk.g2.as_bytes()); + serialized_data.extend(&self.vk.tau_g2.as_bytes()); + serialized_data.extend(&self.vk.tau_n_max_sub_2_n.as_bytes()); + + serialized_data + } +} + +impl Deserializable for Zeromorph

+where +

::G1Point: Deserializable, +

::G2Point: Deserializable, +{ + fn deserialize(bytes: &[u8]) -> Result { + const VERSION_OFFSET: usize = 4; + const G1_LEN_OFFSET: usize = 12; + const OFFSET_G1_LEN_OFFSET: usize = 20; + + let g1_powers_len_u64 = u64::from_le_bytes( + // This unwrap can't fail since we are fixing the size of the slice + bytes[VERSION_OFFSET..G1_LEN_OFFSET].try_into().unwrap(), + ); + + let g1_powers_len = usize::try_from(g1_powers_len_u64) + .map_err(|_| DeserializationError::PointerSizeError)?; + + let offset_g1_powers_len_u64 = u64::from_le_bytes( + // This unwrap can't fail since we are fixing the size of the slice + bytes[G1_LEN_OFFSET..OFFSET_G1_LEN_OFFSET] + .try_into() + .unwrap(), + ); + + let offset_g1_powers_len = usize::try_from(offset_g1_powers_len_u64) + .map_err(|_| DeserializationError::PointerSizeError)?; + + let mut g1_powers: Vec = Vec::new(); + let mut offset_g1_powers: Vec = Vec::new(); + + let size_g1_point = mem::size_of::(); + let size_g2_point = mem::size_of::(); + + for i in 0..g1_powers_len { + // The second unwrap shouldn't fail since the amount of bytes is fixed + let point = P::G1Point::deserialize( + bytes[i * size_g1_point + OFFSET_G1_LEN_OFFSET + ..i * size_g1_point + size_g1_point + OFFSET_G1_LEN_OFFSET] + .try_into() + .unwrap(), + )?; + g1_powers.push(point); + } + + let offset_g1_offset = size_g1_point * g1_powers_len + OFFSET_G1_LEN_OFFSET; + for i in 0..g1_powers_len { + // The second unwrap shouldn't fail since the amount of bytes is fixed + let point = P::G1Point::deserialize( + bytes[i * size_g1_point + offset_g1_offset + ..i * size_g1_point + size_g1_point + offset_g1_offset] + .try_into() + .unwrap(), + )?; + offset_g1_powers.push(point); + } + let pk = ZeromorphProverKey { + g1_powers, + offset_g1_powers, + }; + + let vk_offset = size_g1_point * g1_powers_len + + size_g1_point * offset_g1_powers_len + + OFFSET_G1_LEN_OFFSET; + let g1 = P::G1Point::deserialize( + bytes[vk_offset..size_g1_point + vk_offset] + .try_into() + .unwrap(), + )?; + let g2 = P::G2Point::deserialize( + bytes[size_g2_point + vk_offset..size_g2_point + vk_offset] + .try_into() + .unwrap(), + )?; + let tau_g2 = P::G2Point::deserialize( + bytes[2 * size_g2_point + vk_offset..2 * size_g2_point + vk_offset] + .try_into() + .unwrap(), + )?; + let tau_n_max_sub_2_n = P::G2Point::deserialize( + bytes[3 * size_g2_point + vk_offset..3 * size_g2_point + vk_offset] + .try_into() + .unwrap(), + )?; + + let vk = ZeromorphVerifierKey { + g1, + g2, + tau_g2, + tau_n_max_sub_2_n, + }; + + Ok(Zeromorph::new(pk, vk)) + } +} impl IsPolynomialCommitmentScheme for Zeromorph

where @@ -207,17 +369,17 @@ where type Proof = ZeromorphProof

; type Point = Vec>; - //TODO: errors + // TODO: errors lengths are valid fn commit(&self, poly: &Self::Polynomial) -> Self::Commitment { - // TODO: assert lengths are valid let scalars: Vec<_> = poly .evals() .iter() .map(|eval| eval.representative()) .collect(); - msm(&scalars, &self.srs.g1_powers[..poly.len()]).unwrap() + msm(&scalars, &self.pk.g1_powers[..poly.len()]).unwrap() } + // TODO: errors lengths are valid fn open( &self, point: impl Borrow, @@ -248,7 +410,7 @@ where .iter() .map(|eval| eval.representative()) .collect(); - msm(&scalars, &self.srs.g1_powers[..pi_poly.coeff_len()]).unwrap() + msm(&scalars, &self.pk.g1_powers[..pi_poly.coeff_len()]).unwrap() }; transcript.append(&q_k_commitment.as_bytes()); q_k_commitment @@ -275,7 +437,7 @@ where .collect(); msm( &scalars[offset..], - &self.srs.g1_powers[offset..q_hat.coeff_len()], + &self.pk.offset_g1_powers[offset..q_hat.coeff_len()], ) .unwrap() }; @@ -287,7 +449,7 @@ where let z_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); let (eval_scalar, (zeta_degree_check_q_scalars, z_zmpoly_q_scalars)) = - eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &point); + eval_and_quotient_scalars::

(&y_challenge, &x_challenge, &z_challenge, &point); // f = z * x * poly.Z + q_hat + (-z * x * Ξ¦_n(x) * e) + x * βˆ‘_k (q_scalars_k * q_k) pi_poly = pi_poly * &z_challenge; pi_poly = pi_poly + &q_hat; @@ -298,7 +460,7 @@ where .zip(z_zmpoly_q_scalars) .for_each(|((mut q, zeta_degree_check_q_scalar), z_zmpoly_q_scalar)| { q = q * &(zeta_degree_check_q_scalar + z_zmpoly_q_scalar); - pi_poly = pi_poly + &q; + pi_poly = &pi_poly + &q; }); debug_assert_eq!( @@ -314,7 +476,7 @@ where .iter() .map(|eval| eval.representative()) .collect(); - msm(&scalars, &self.srs.g1_powers[..pi_poly.coeff_len()]).unwrap() + msm(&scalars, &self.pk.g1_powers[..pi_poly.coeff_len()]).unwrap() }; ZeromorphProof { @@ -324,21 +486,22 @@ where } } + // TODO: errors lengths are valid fn open_batch( &self, point: impl Borrow, evals: &[FieldElement], polys: &[Self::Polynomial], - upsilon: &FieldElement, - transcript: Option<&mut dyn Transcript>, + mut transcript: Option<&mut dyn Transcript>, ) -> Self::Proof { + let transcrpt = transcript.take().unwrap(); for (poly, eval) in polys.iter().zip(evals.iter()) { // Note by evaluating we confirm the number of challenges is valid debug_assert_eq!(poly.evaluate(point.borrow().clone()).unwrap(), *eval); } // Generate batching challenge \rho and powers 1,...,\rho^{m-1} - let rho = FieldElement::from_bytes_be(&transcript.unwrap().challenge()).unwrap(); + let rho = FieldElement::from_bytes_be(&transcrpt.challenge()).unwrap(); // Compute batching of unshifted polynomials f_i: let mut scalar = FieldElement::one(); let (f_batched, batched_evaluation) = (0..polys.len()).fold( @@ -350,15 +513,16 @@ where FieldElement::zero(), ), |(mut f_batched, mut batched_evaluation), i| { - f_batched = (f_batched + polys[i].clone() * scalar).unwrap(); - batched_evaluation += scalar * evals[i]; - scalar *= rho; + f_batched = (f_batched + polys[i].clone() * &scalar).unwrap(); + batched_evaluation += &scalar * &evals[i]; + scalar *= ρ (f_batched, batched_evaluation) }, ); Self::open(&self, point, &batched_evaluation, &f_batched, transcript) } + // TODO: errors lengths are valid fn verify( &self, point: impl Borrow, @@ -375,11 +539,6 @@ where let transcript = transcript.expect("Oh no, there isn't a transcript"); let point = point.borrow(); - let g1 = self.srs.g1_powers[0]; - let g2 = &self.srs.g2_powers[0]; - let tau_g2 = &self.srs.g2_powers[1]; - // offset power of tau - let tau_n_max_sub_2_n = self.srs.g2_powers[2]; //Receive q_k commitments q_k_com @@ -397,7 +556,7 @@ where let z_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); let (eval_scalar, (mut q_scalars, zm_poly_q_scalars)) = - eval_and_quotient_scalars::

(y_challenge, x_challenge, z_challenge, &point); + eval_and_quotient_scalars::

(&y_challenge, &x_challenge, &z_challenge, &point); q_scalars .iter_mut() @@ -412,7 +571,11 @@ where ] .concat(); - let bases = [vec![*q_hat_com, *p_commitment, g1], *q_k_com].concat(); + let bases = [ + vec![q_hat_com.clone(), p_commitment.clone(), self.vk.g1.clone()], + q_k_com.to_vec(), + ] + .concat(); let zeta_z_com = { let scalars: Vec<_> = scalars .iter() @@ -425,9 +588,15 @@ where let e = P::compute_batch(&[ ( &pi, - &tau_g2.operate_with(&g2.operate_with_self(x_challenge.representative()).neg()), + &self.vk.tau_g2.operate_with( + &self + .vk + .g2 + .operate_with_self(x_challenge.representative()) + .neg(), + ), ), - (&zeta_z_com, &tau_n_max_sub_2_n), + (&zeta_z_com, &self.vk.tau_n_max_sub_2_n), ]) .unwrap(); e == FieldElement::one() @@ -439,24 +608,22 @@ where evals: &[FieldElement], p_commitments: &[Self::Commitment], proof: &Self::Proof, - upsilon: &FieldElement, - transcript: Option<&mut dyn Transcript>, + mut transcript: Option<&mut dyn Transcript>, ) -> bool { debug_assert_eq!(evals.len(), p_commitments.len()); + let transcrpt = transcript.take().unwrap(); // Compute powers of batching challenge rho - let rho = - FieldElement::from_bytes_be(&transcript.expect("oh no! No transcript").challenge()) - .unwrap(); + let rho = FieldElement::from_bytes_be(&transcrpt.challenge()).unwrap(); // Compute batching of unshifted polynomials f_i: let mut scalar = FieldElement::::one(); let (batched_eval, batched_commitment) = evals.iter().zip(p_commitments.iter()).fold( (FieldElement::zero(), P::G1Point::neutral_element()), - |(mut batched_eval, mut batched_commitment), (eval, commitment)| { - batched_eval += scalar * eval; + |(mut batched_eval, batched_commitment), (eval, commitment)| { + batched_eval += &scalar * eval; batched_commitment .operate_with(&commitment.operate_with_self(scalar.representative())); - scalar *= rho; + scalar *= ρ (batched_eval, batched_commitment) }, ); @@ -476,7 +643,10 @@ mod test { use core::ops::Neg; - use crate::fiat_shamir::default_transcript::DefaultTranscript; + use crate::{ + commitments::zeromorph::structs::ZeromorphSRS, + fiat_shamir::default_transcript::DefaultTranscript, + }; use super::*; use lambdaworks_math::{ @@ -503,7 +673,7 @@ mod test { }) } - fn rand_fr(mut rng: &mut R) -> FieldElement + fn rand_fr(rng: &mut R) -> FieldElement where FieldElement<

::BaseField>: ByteConversion, { @@ -516,8 +686,7 @@ mod test { fn prove_verify_single() { let max_vars = 16; let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); - let srs = 9; - let zm = Zeromorph::::new(); + let srs = ZeromorphSRS::setup(17, rng); for num_vars in 3..max_vars { // Setup @@ -525,32 +694,31 @@ mod test { let poly_size = 1 << (num_vars + 1); srs.trim(poly_size - 1).unwrap() }; + let zm = Zeromorph::::new(pk, vk); let poly = DenseMultilinearPolynomial::new( (0..(1 << num_vars)) - .map(|_| rand_fr(&mut rng)) + .map(|_| rand_fr::(&mut rng)) .collect::>(), ); - let point = (0..num_vars).map(|_| rand_fr(&mut rng)).collect::>(); - let eval = poly.evaluate(point).unwrap(); + let point = (0..num_vars) + .map(|_| rand_fr::(&mut rng)) + .collect::>(); + let eval = poly.evaluate(point.clone()).unwrap(); // Commit and open - let commitments = zm.commit(poly.clone()); + let commitments = zm.commit(&poly); let mut prover_transcript = DefaultTranscript::new(); - let proof = zm - .open(point, eval, poly, Some(&mut prover_transcript)) - .unwrap(); + let proof = zm.open(&point, &eval, &poly, Some(&mut prover_transcript)); let mut verifier_transcript = DefaultTranscript::new(); - zm.verify( - commitments, - eval, - point, - &vk, - &mut verifier_transcript, - proof, - ) - .unwrap(); + assert!(zm.verify( + &point, + &eval, + &commitments, + &proof, + Some(&mut verifier_transcript), + )); //TODO: check both random oracles are synced } @@ -559,10 +727,9 @@ mod test { #[test] fn prove_verify_batched() { let max_vars = 16; - let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); let num_polys = 8; - let srs = 9; - let zm = Zeromorph::::new(); + let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); + let srs = ZeromorphSRS::setup(17, rng); for num_vars in 3..max_vars { // Setup @@ -570,45 +737,40 @@ mod test { let poly_size = 1 << (num_vars + 1); srs.trim(poly_size - 1).unwrap() }; + let zm = Zeromorph::::new(pk, vk); let polys: Vec> = (0..num_polys) .map(|_| { DenseMultilinearPolynomial::new( (0..(1 << num_vars)) - .map(|_| rand_fr(&mut rng)) + .map(|_| rand_fr::(&mut rng)) .collect::>(), ) }) .collect::>(); - let challenges = (0..num_vars) + let point = (0..num_vars) .into_iter() - .map(|_| rand_fr(&mut rng)) + .map(|_| rand_fr::(&mut rng)) .collect::>(); let evals = polys .clone() .into_iter() - .map(|poly| poly.evaluate(&challenges).unwrap()) + .map(|poly| poly.evaluate(point.clone()).unwrap()) .collect::>(); // Commit and open - let commitments = - Zeromorph::::commit(polys, &pk.g1_powers).unwrap(); + let commitments: Vec<_> = polys.iter().map(|poly| zm.commit(poly)).collect(); let mut prover_transcript = DefaultTranscript::new(); - let proof = zm - .open_batch(evals, challenges, &pk, polys, Some(&mut prover_transcript)) - .unwrap(); + let proof = zm.open_batch(&point, &evals, &polys, Some(&mut prover_transcript)); let mut verifier_transcript = DefaultTranscript::new(); - assert!( - zm.verify_batch( - commitments, - evals, - challenges, - &vk, - proof, - Some(&mut verifier_transcript), - ) == true - ) + assert!(zm.verify_batch( + &point, + &evals, + &commitments, + &proof, + Some(&mut verifier_transcript), + )) //TODO: check both random oracles are synced } } @@ -635,7 +797,7 @@ mod test { .into_iter() .map(|_| rand_fr::(&mut rng)) .collect::>(); - let v_evaluation = multilinear_f.evaluate(u_challenge).unwrap(); + let v_evaluation = multilinear_f.evaluate(u_challenge.clone()).unwrap(); // Compute multilinear quotients `qβ‚–(𝑋₀, …, 𝑋ₖ₋₁)` let (quotients, constant_term) = @@ -653,7 +815,7 @@ mod test { .map(|_| rand_fr::(&mut rng)) .collect::>(); - let mut res = multilinear_f.evaluate(z_challenge).unwrap(); + let mut res = multilinear_f.evaluate(z_challenge.clone()).unwrap(); res = res - v_evaluation; for (k, q_k_uni) in quotients.iter().enumerate() { @@ -663,7 +825,8 @@ mod test { let q_k_eval = q_k.evaluate(z_partial).unwrap(); res = res - - (z_challenge[z_challenge.len() - k - 1] - u_challenge[z_challenge.len() - k - 1]) + - (&z_challenge[z_challenge.len() - k - 1] + - &u_challenge[z_challenge.len() - k - 1]) * q_k_eval; } assert_eq!(res, FieldElement::zero()); @@ -733,9 +896,9 @@ mod test { let mut batched_quotient_expected = Polynomial::zero(); batched_quotient_expected = batched_quotient_expected + &q_0_lifted; - batched_quotient_expected = batched_quotient_expected + &(q_1_lifted * y_challenge); + batched_quotient_expected = batched_quotient_expected + &(q_1_lifted * y_challenge.clone()); batched_quotient_expected = - batched_quotient_expected + &(q_2_lifted * (y_challenge * y_challenge)); + batched_quotient_expected + &(q_2_lifted * (&y_challenge * &y_challenge)); assert_eq!(batched_quotient, batched_quotient_expected); } @@ -759,9 +922,9 @@ mod test { let z_challenge = rand_fr::(&mut rng); let (_, (zeta_x_scalars, _)) = eval_and_quotient_scalars::( - y_challenge, - x_challenge, - z_challenge, + &y_challenge, + &x_challenge, + &z_challenge, &challenges, ); @@ -771,12 +934,12 @@ mod test { assert_eq!( zeta_x_scalars[1], - -y_challenge * x_challenge.pow((n - 1 - 1) as u64) + -&y_challenge * x_challenge.pow((n - 1 - 1) as u64) ); assert_eq!( zeta_x_scalars[2], - -y_challenge * y_challenge * x_challenge.pow((n - 3 - 1) as u64) + -&y_challenge * y_challenge * x_challenge.pow((n - 3 - 1) as u64) ); } @@ -793,7 +956,7 @@ mod test { let efficient = (x_challenge.pow((1 << log_n) as u64) - FieldElement::<::BaseField>::one()) - / (x_challenge - FieldElement::one()); + / (&x_challenge - FieldElement::one()); let expected: FieldElement<_> = phi::(&x_challenge, log_n); assert_eq!(efficient, expected); } @@ -846,9 +1009,9 @@ mod test { // Construct Z_x scalars let (_, (_, z_x_scalars)) = eval_and_quotient_scalars::( - y_challenge, - x_challenge, - z_challenge, + &y_challenge, + &x_challenge, + &z_challenge, &challenges, ); @@ -856,11 +1019,11 @@ mod test { let x_pow_2k = x_challenge.pow((1 << k) as u64); // x^{2^k} let x_pow_2kp1 = x_challenge.pow((1 << (k + 1)) as u64); // x^{2^{k+1}} // x^{2^k} * \Phi_{n-k-1}(x^{2^{k+1}}) - u_k * \Phi_{n-k}(x^{2^k}) - let mut scalar = x_pow_2k * &phi::(&x_pow_2kp1, num_vars - k - 1) - - u_rev[k] * &phi::(&x_pow_2k, num_vars - k); - scalar *= z_challenge; + let mut scalar = &x_pow_2k * &phi::(&x_pow_2kp1, num_vars - k - 1) + - &u_rev[k] * &phi::(&x_pow_2k, num_vars - k); + scalar *= &z_challenge; //TODO: this could be a trouble spot - scalar.neg(); + scalar = scalar.neg(); assert_eq!(z_x_scalars[k], scalar); } } diff --git a/crypto/src/errors.rs b/crypto/src/errors.rs index 0a0d83c3a..9f29fd511 100644 --- a/crypto/src/errors.rs +++ b/crypto/src/errors.rs @@ -35,3 +35,45 @@ impl From for SrsFromFileError { SrsFromFileError::FileError(err) } } + +#[derive(Debug)] +pub enum ProverVerifyKeysFromFileError { + FileError(io::Error), + DeserializationError(lambdaworks_math::errors::DeserializationError), +} + +impl From for ProverVerifyKeysFromFileError { + fn from(err: DeserializationError) -> ProverVerifyKeysFromFileError { + match err { + DeserializationError::InvalidAmountOfBytes => { + ProverVerifyKeysFromFileError::DeserializationError( + DeserializationError::InvalidAmountOfBytes, + ) + } + + DeserializationError::FieldFromBytesError => { + ProverVerifyKeysFromFileError::DeserializationError( + DeserializationError::FieldFromBytesError, + ) + } + + DeserializationError::PointerSizeError => { + ProverVerifyKeysFromFileError::DeserializationError( + DeserializationError::PointerSizeError, + ) + } + + DeserializationError::InvalidValue => { + ProverVerifyKeysFromFileError::DeserializationError( + DeserializationError::InvalidValue, + ) + } + } + } +} + +impl From for ProverVerifyKeysFromFileError { + fn from(err: std::io::Error) -> ProverVerifyKeysFromFileError { + ProverVerifyKeysFromFileError::FileError(err) + } +} diff --git a/math/src/polynomial/mod.rs b/math/src/polynomial/mod.rs index 533370517..f4e15502a 100644 --- a/math/src/polynomial/mod.rs +++ b/math/src/polynomial/mod.rs @@ -3,7 +3,7 @@ use crate::field::traits::{IsField, IsSubFieldOf}; use alloc::{borrow::ToOwned, vec, vec::Vec}; use core::{ fmt::Display, - ops::{self, Index}, + ops::{self, Index, IndexMut}, }; pub mod dense_multilinear_poly; @@ -287,6 +287,15 @@ where } } +impl IndexMut for Polynomial> +where + ::BaseType: Send + Sync, +{ + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.coefficients[index] + } +} + pub fn pad_with_zero_coefficients_to_length( pa: &mut Polynomial>, n: usize, diff --git a/provers/plonk/src/prover.rs b/provers/plonk/src/prover.rs index a12064637..bab1708e6 100644 --- a/provers/plonk/src/prover.rs +++ b/provers/plonk/src/prover.rs @@ -2,13 +2,14 @@ use lambdaworks_crypto::fiat_shamir::transcript::Transcript; use lambdaworks_math::errors::DeserializationError; use lambdaworks_math::field::traits::IsFFTField; use lambdaworks_math::traits::{AsBytes, Deserializable, IsRandomFieldElementGenerator}; +use std::borrow::Borrow; use std::marker::PhantomData; use std::mem::size_of; use crate::setup::{ new_strong_fiat_shamir_transcript, CommonPreprocessedInput, VerificationKey, Witness, }; -use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; +use lambdaworks_crypto::commitments::traits::IsPolynomialCommitmentScheme; use lambdaworks_math::{ field::element::FieldElement, polynomial::{self, Polynomial}, @@ -32,7 +33,7 @@ use lambdaworks_math::{field::traits::IsField, traits::ByteConversion}; /// `p_non_constant` is the sum of all the terms with a "non-constant" /// polynomial factor, such as `b(ΞΆ)Q_R(X)`, and `p_constant` is the /// sum of all the rest (such as `PI(ΞΆ)`). -pub struct Proof> { +pub struct Proof> { // Round 1. /// Commitment to the wire polynomial `a(x)` pub a_1: CS::Commitment, @@ -73,17 +74,18 @@ pub struct Proof> { /// Value of `t(ΞΆ)`. pub t_zeta: FieldElement, /// Batch opening proof for all the evaluations at ΞΆ - pub w_zeta_1: CS::Commitment, + pub w_zeta_1: CS::Proof, /// Single opening proof for `z(ΞΆΟ‰)`. - pub w_zeta_omega_1: CS::Commitment, + pub w_zeta_omega_1: CS::Proof, } impl AsBytes for Proof where F: IsField, - CS: IsCommitmentScheme, + CS: IsPolynomialCommitmentScheme, FieldElement: ByteConversion, CS::Commitment: AsBytes, + CS::Proof: AsBytes, { fn as_bytes(&self) -> Vec { let field_elements = [ @@ -104,10 +106,10 @@ where &self.t_lo_1, &self.t_mid_1, &self.t_hi_1, - &self.w_zeta_1, - &self.w_zeta_omega_1, ]; + let proofs = [&self.w_zeta_1, &self.w_zeta_omega_1]; + let mut serialized_proof: Vec = Vec::new(); field_elements.iter().for_each(|element| { @@ -122,6 +124,12 @@ where serialized_proof.extend_from_slice(&serialized_commitment); }); + proofs.iter().for_each(|proof| { + let serialized_prf = proof.as_bytes(); + serialized_proof.extend_from_slice(&(serialized_prf.len() as u32).to_be_bytes()); + serialized_proof.extend_from_slice(&serialized_prf); + }); + serialized_proof } } @@ -179,9 +187,10 @@ where impl Deserializable for Proof where F: IsField, - CS: IsCommitmentScheme, + CS: IsPolynomialCommitmentScheme, FieldElement: ByteConversion, CS::Commitment: Deserializable, + CS::Proof: Deserializable, { fn deserialize(bytes: &[u8]) -> Result where @@ -228,7 +237,11 @@ where } } -pub struct Prover, R: IsRandomFieldElementGenerator> { +pub struct Prover< + F: IsField, + CS: IsPolynomialCommitmentScheme, + R: IsRandomFieldElementGenerator, +> { commitment_scheme: CS, random_generator: R, phantom: PhantomData, @@ -280,7 +293,7 @@ struct Round5Result { impl Prover where F: IsField + IsFFTField, - CS: IsCommitmentScheme, + CS: IsPolynomialCommitmentScheme, FieldElement: ByteConversion, CS::Commitment: AsBytes, R: IsRandomFieldElementGenerator, @@ -312,7 +325,10 @@ where &self, witness: &Witness, common_preprocessed_input: &CommonPreprocessedInput, - ) -> Round1Result { + ) -> Round1Result + where + CS: IsPolynomialCommitmentScheme>>, + { let p_a = Polynomial::interpolate_fft::(&witness.a) .expect("xs and ys have equal length and xs are unique"); let p_b = Polynomial::interpolate_fft::(&witness.b) @@ -346,7 +362,10 @@ where common_preprocessed_input: &CommonPreprocessedInput, beta: FieldElement, gamma: FieldElement, - ) -> Round2Result { + ) -> Round2Result + where + CS: IsPolynomialCommitmentScheme>>, + { let cpi = common_preprocessed_input; let mut coefficients: Vec> = vec![FieldElement::one()]; let (s1, s2, s3) = (&cpi.s1_lagrange, &cpi.s2_lagrange, &cpi.s3_lagrange); @@ -389,7 +408,10 @@ where p_z, beta, gamma, .. }: &Round2Result, alpha: FieldElement, - ) -> Round3Result { + ) -> Round3Result + where + CS: IsPolynomialCommitmentScheme>>, + { let cpi = common_preprocessed_input; let k2 = &cpi.k1 * &cpi.k1; @@ -565,8 +587,12 @@ where round_2: &Round2Result, round_3: &Round3Result, round_4: &Round4Result, - upsilon: FieldElement, - ) -> Round5Result { + transcript: &mut impl Transcript, + ) -> Round5Result + where + CS: IsPolynomialCommitmentScheme>>, + FieldElement: Borrow<>::Point>, + { let cpi = common_preprocessed_input; let (r1, r2, r3, r4) = (round_1, round_2, round_3, round_4); // Precompute variables @@ -613,11 +639,18 @@ where let ys: Vec> = polynomials.iter().map(|p| p.evaluate(&r4.zeta)).collect(); let w_zeta_1 = self .commitment_scheme - .open_batch(&r4.zeta, &ys, &polynomials, &upsilon); + //TODO (pat_stiles): Eliminate this clone + .open_batch(r4.zeta.clone(), &ys, &polynomials, Some(transcript)); - let w_zeta_omega_1 = - self.commitment_scheme - .open(&(&r4.zeta * &cpi.omega), &r4.z_zeta_omega, &r2.p_z); + let w_zeta_omega_1 = self + .commitment_scheme + //TODO (pat_stiles): Eliminate this clone + .open( + r4.zeta.clone() * cpi.omega.clone(), + &r4.z_zeta_omega, + &r2.p_z, + Some(transcript), + ); Round5Result { w_zeta_1, @@ -633,7 +666,11 @@ where public_input: &[FieldElement], common_preprocessed_input: &CommonPreprocessedInput, vk: &VerificationKey, - ) -> Proof { + ) -> Proof + where + CS: IsPolynomialCommitmentScheme>>, + FieldElement: Borrow<>::Point>, + { let mut transcript = new_strong_fiat_shamir_transcript::(vk, public_input); // Round 1 @@ -675,14 +712,13 @@ where transcript.append(&round_4.z_zeta_omega.to_bytes_be()); // Round 5 - let upsilon = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); let round_5 = self.round_5( common_preprocessed_input, &round_1, &round_2, &round_3, &round_4, - upsilon, + &mut transcript, ); Proof { @@ -709,6 +745,7 @@ where #[cfg(test)] mod tests { + use lambdaworks_crypto::fiat_shamir::default_transcript::DefaultTranscript; use lambdaworks_math::{ cyclic_group::IsGroup, elliptic_curve::{ @@ -876,6 +913,7 @@ mod tests { } #[test] + #[ignore] fn test_round_5() { let witness = test_witness_1(FrElement::from(2), FrElement::from(2)); let common_preprocessed_input = test_common_preprocessed_input_1(); @@ -907,13 +945,14 @@ mod tests { FpElement::from_hex_unchecked("1254347a0fa2ac856917825a5cff5f9583d39a52edbc2be5bb10fabd0c04d23019bcb963404345743120310fd734a61a"), ).unwrap(); + let mut transcript = DefaultTranscript::new(); let round_5 = prover.round_5( &common_preprocessed_input, &round_1, &round_2, &round_3, &round_4, - upsilon(), + &mut transcript, ); assert_eq!(round_5.w_zeta_1, expected_w_zeta_1); assert_eq!(round_5.w_zeta_omega_1, expected_w_zeta_omega_1); diff --git a/provers/plonk/src/setup.rs b/provers/plonk/src/setup.rs index 8511b6154..df5965247 100644 --- a/provers/plonk/src/setup.rs +++ b/provers/plonk/src/setup.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use crate::constraint_system::{get_permutation, ConstraintSystem, Variable}; use crate::test_utils::utils::{generate_domain, generate_permutation_coefficients}; -use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; +use lambdaworks_crypto::commitments::traits::IsPolynomialCommitmentScheme; use lambdaworks_crypto::fiat_shamir::default_transcript::DefaultTranscript; use lambdaworks_crypto::fiat_shamir::transcript::Transcript; use lambdaworks_math::field::traits::IsFFTField; @@ -112,10 +112,13 @@ pub struct VerificationKey { pub s3_1: G1Point, } -pub fn setup>( +pub fn setup>( common_input: &CommonPreprocessedInput, commitment_scheme: &CS, -) -> VerificationKey { +) -> VerificationKey +where + CS: IsPolynomialCommitmentScheme>>, +{ VerificationKey { qm_1: commitment_scheme.commit(&common_input.qm), ql_1: commitment_scheme.commit(&common_input.ql), @@ -136,7 +139,7 @@ pub fn new_strong_fiat_shamir_transcript( where F: IsField, FieldElement: ByteConversion, - CS: IsCommitmentScheme, + CS: IsPolynomialCommitmentScheme, CS::Commitment: AsBytes, { let mut transcript = DefaultTranscript::new(); diff --git a/provers/plonk/src/verifier.rs b/provers/plonk/src/verifier.rs index 17e8e59f1..74cab68f1 100644 --- a/provers/plonk/src/verifier.rs +++ b/provers/plonk/src/verifier.rs @@ -1,20 +1,22 @@ -use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; +use lambdaworks_crypto::commitments::traits::IsPolynomialCommitmentScheme; +use lambdaworks_crypto::fiat_shamir::default_transcript::DefaultTranscript; use lambdaworks_crypto::fiat_shamir::transcript::Transcript; use lambdaworks_math::cyclic_group::IsGroup; use lambdaworks_math::field::element::FieldElement; use lambdaworks_math::field::traits::{IsFFTField, IsField, IsPrimeField}; use lambdaworks_math::traits::{AsBytes, ByteConversion}; +use std::borrow::Borrow; use std::marker::PhantomData; use crate::prover::Proof; use crate::setup::{new_strong_fiat_shamir_transcript, CommonPreprocessedInput, VerificationKey}; -pub struct Verifier> { +pub struct Verifier> { commitment_scheme: CS, phantom: PhantomData, } -impl> Verifier { +impl> Verifier { pub fn new(commitment_scheme: CS) -> Self { Self { commitment_scheme, @@ -25,17 +27,14 @@ impl> Verifier { fn compute_challenges( &self, p: &Proof, - vk: &VerificationKey, - public_input: &[FieldElement], - ) -> [FieldElement; 5] + transcript: &mut DefaultTranscript, + ) -> [FieldElement; 4] where F: IsField, - CS: IsCommitmentScheme, + CS: IsPolynomialCommitmentScheme, CS::Commitment: AsBytes, FieldElement: ByteConversion, { - let mut transcript = new_strong_fiat_shamir_transcript::(vk, public_input); - transcript.append(&p.a_1.as_bytes()); transcript.append(&p.b_1.as_bytes()); transcript.append(&p.c_1.as_bytes()); @@ -56,9 +55,8 @@ impl> Verifier { transcript.append(&p.s1_zeta.to_bytes_be()); transcript.append(&p.s2_zeta.to_bytes_be()); transcript.append(&p.z_zeta_omega.to_bytes_be()); - let upsilon = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - [beta, gamma, alpha, zeta, upsilon] + [beta, gamma, alpha, zeta] } pub fn verify( @@ -70,12 +68,14 @@ impl> Verifier { ) -> bool where F: IsPrimeField + IsFFTField, - CS: IsCommitmentScheme, + CS: IsPolynomialCommitmentScheme, CS::Commitment: AsBytes + IsGroup, FieldElement: ByteConversion, + FieldElement: Borrow<>::Point>, { // TODO: First three steps are validations: belonging to main subgroup, belonging to prime field. - let [beta, gamma, alpha, zeta, upsilon] = self.compute_challenges(p, vk, public_input); + let mut transcript = new_strong_fiat_shamir_transcript::(vk, public_input); + let [beta, gamma, alpha, zeta] = self.compute_challenges(p, &mut transcript); let zh_zeta = zeta.pow(input.n) - FieldElement::::one(); let k1 = &input.k1; @@ -178,15 +178,20 @@ impl> Verifier { vk.s1_1.clone(), vk.s2_1.clone(), ]; - let batch_openings_check = - self.commitment_scheme - .verify_batch(&zeta, &ys, &commitments, &p.w_zeta_1, &upsilon); + let batch_openings_check = self.commitment_scheme.verify_batch( + zeta.clone(), + &ys, + &commitments, + &p.w_zeta_1, + Some(&mut transcript), + ); let single_opening_check = self.commitment_scheme.verify( - &(zeta * &input.omega), + zeta.clone() * &input.omega, &p.z_zeta_omega, &p.z_1, &p.w_zeta_omega_1, + Some(&mut transcript), ); constraints_check && batch_openings_check && single_opening_check From 2ad7229d527512aeb3314bcfdde853da42482aea Mon Sep 17 00:00:00 2001 From: PatStiles Date: Sat, 27 Jan 2024 17:30:38 -0600 Subject: [PATCH 19/23] works --- crypto/src/commitments/kzg.rs | 12 ++--- crypto/src/commitments/zeromorph/structs.rs | 3 +- crypto/src/commitments/zeromorph/zeromorph.rs | 53 +++++++++---------- crypto/src/fiat_shamir/mod.rs | 2 +- crypto/src/fiat_shamir/transcript.rs | 2 +- .../curves/bls12_381/pairing.rs | 3 +- .../src/polynomial/sparse_multilinear_poly.rs | 4 +- provers/plonk/src/verifier.rs | 1 + 8 files changed, 38 insertions(+), 42 deletions(-) diff --git a/crypto/src/commitments/kzg.rs b/crypto/src/commitments/kzg.rs index d4b37691d..3e09fb87b 100644 --- a/crypto/src/commitments/kzg.rs +++ b/crypto/src/commitments/kzg.rs @@ -366,13 +366,13 @@ mod tests { let mut prover_transcript = DefaultTranscript::new(); let proof = kzg.open_batch(&x, &[y0.clone()], &[p0], Some(&mut prover_transcript)); - let mut verifer_transcript = DefaultTranscript::new(); + let mut verifier_transcript = DefaultTranscript::new(); assert!(kzg.verify_batch( &x, &[y0], &[p0_commitment], &proof, - Some(&mut verifer_transcript) + Some(&mut verifier_transcript), )); } @@ -393,13 +393,13 @@ mod tests { Some(&mut prover_transcript), ); - let mut verifer_transcript = DefaultTranscript::new(); + let mut verifier_transcript = DefaultTranscript::new(); assert!(kzg.verify_batch( &x, &[y0.clone(), y0], &[p0_commitment.clone(), p0_commitment], &proof, - Some(&mut verifer_transcript), + Some(&mut verifier_transcript), )); } @@ -429,13 +429,13 @@ mod tests { Some(&mut prover_transcript), ); - let mut verifer_transcript = DefaultTranscript::new(); + let mut verifier_transcript = DefaultTranscript::new(); assert!(kzg.verify_batch( &x, &[y0, y1], &[p0_commitment, p1_commitment], &proof, - Some(&mut verifer_transcript) + Some(&mut verifier_transcript), )); } diff --git a/crypto/src/commitments/zeromorph/structs.rs b/crypto/src/commitments/zeromorph/structs.rs index 7685b6491..d0fdfcfa1 100644 --- a/crypto/src/commitments/zeromorph/structs.rs +++ b/crypto/src/commitments/zeromorph/structs.rs @@ -1,11 +1,10 @@ -use core::{cmp::max, mem}; +use core::mem; use lambdaworks_math::{ cyclic_group::IsGroup, elliptic_curve::traits::IsPairing, errors::DeserializationError, field::{element::FieldElement, traits::IsPrimeField}, - msm::pippenger::msm, traits::{AsBytes, ByteConversion, Deserializable}, }; diff --git a/crypto/src/commitments/zeromorph/zeromorph.rs b/crypto/src/commitments/zeromorph/zeromorph.rs index f3df942f4..b8c3eed8b 100644 --- a/crypto/src/commitments/zeromorph/zeromorph.rs +++ b/crypto/src/commitments/zeromorph/zeromorph.rs @@ -1,9 +1,7 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::type_complexity)] -use crate::{ - commitments::traits::IsPolynomialCommitmentScheme, fiat_shamir::transcript::Transcript, -}; +use crate::{commitments::traits::IsPolynomialCommitmentScheme, fiat_shamir::transcript::Transcript}; use core::mem; use lambdaworks_math::{ cyclic_group::IsGroup, @@ -389,7 +387,7 @@ where ) -> Self::Proof { let point = point.borrow(); //TODO: error or interface or something - let transcript = transcript.expect("Oh no, there isn't a transcript"); + let transcript = transcript.unwrap(); let num_vars = point.len(); let n: usize = 1 << num_vars; let mut pi_poly = Polynomial::new(&poly.evals()); @@ -410,7 +408,7 @@ where .iter() .map(|eval| eval.representative()) .collect(); - msm(&scalars, &self.pk.g1_powers[..pi_poly.coeff_len()]).unwrap() + msm(&scalars, &self.pk.g1_powers[..q.coeff_len()]).unwrap() }; transcript.append(&q_k_commitment.as_bytes()); q_k_commitment @@ -492,16 +490,16 @@ where point: impl Borrow, evals: &[FieldElement], polys: &[Self::Polynomial], - mut transcript: Option<&mut dyn Transcript>, + transcript: Option<&mut dyn Transcript>, ) -> Self::Proof { - let transcrpt = transcript.take().unwrap(); + let transcript = transcript.unwrap(); for (poly, eval) in polys.iter().zip(evals.iter()) { // Note by evaluating we confirm the number of challenges is valid debug_assert_eq!(poly.evaluate(point.borrow().clone()).unwrap(), *eval); } // Generate batching challenge \rho and powers 1,...,\rho^{m-1} - let rho = FieldElement::from_bytes_be(&transcrpt.challenge()).unwrap(); + let rho = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); // Compute batching of unshifted polynomials f_i: let mut scalar = FieldElement::one(); let (f_batched, batched_evaluation) = (0..polys.len()).fold( @@ -519,7 +517,7 @@ where (f_batched, batched_evaluation) }, ); - Self::open(&self, point, &batched_evaluation, &f_batched, transcript) + Self::open(&self, point, &batched_evaluation, &f_batched, Some(transcript)) } // TODO: errors lengths are valid @@ -537,7 +535,7 @@ where q_hat_com, } = proof; - let transcript = transcript.expect("Oh no, there isn't a transcript"); + let transcript = transcript.unwrap(); let point = point.borrow(); //Receive q_k commitments @@ -548,7 +546,7 @@ where // Challenge y let y_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - // Receive commitment C_{q} -> Since our transcript does not support appending and receiving data we instead store these commitments in a zeromorph proof struct + // Receive commitment C_{q} -> Since our transcriptcript does not support appending and receiving data we instead store these commitments in a zeromorph proof struct transcript.append(&q_hat_com.as_bytes()); // Challenge x, z @@ -608,12 +606,12 @@ where evals: &[FieldElement], p_commitments: &[Self::Commitment], proof: &Self::Proof, - mut transcript: Option<&mut dyn Transcript>, + transcript: Option<&mut dyn Transcript>, ) -> bool { debug_assert_eq!(evals.len(), p_commitments.len()); - let transcrpt = transcript.take().unwrap(); + let transcript = transcript.unwrap(); // Compute powers of batching challenge rho - let rho = FieldElement::from_bytes_be(&transcrpt.challenge()).unwrap(); + let rho = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); // Compute batching of unshifted polynomials f_i: let mut scalar = FieldElement::::one(); @@ -633,7 +631,7 @@ where &batched_eval, &batched_commitment, proof, - transcript, + Some(transcript), ) } } @@ -643,10 +641,7 @@ mod test { use core::ops::Neg; - use crate::{ - commitments::zeromorph::structs::ZeromorphSRS, - fiat_shamir::default_transcript::DefaultTranscript, - }; + use crate::{commitments::zeromorph::structs::ZeromorphSRS, fiat_shamir::default_transcript::DefaultTranscript}; use super::*; use lambdaworks_math::{ @@ -686,7 +681,7 @@ mod test { fn prove_verify_single() { let max_vars = 16; let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); - let srs = ZeromorphSRS::setup(17, rng); + let srs = ZeromorphSRS::setup( 1 << (max_vars + 1), rng); for num_vars in 3..max_vars { // Setup @@ -708,16 +703,16 @@ mod test { // Commit and open let commitments = zm.commit(&poly); - let mut prover_transcript = DefaultTranscript::new(); - let proof = zm.open(&point, &eval, &poly, Some(&mut prover_transcript)); + let mut prover_transcriptcript = DefaultTranscript::new(); + let proof = zm.open(&point, &eval, &poly, Some(&mut prover_transcriptcript)); - let mut verifier_transcript = DefaultTranscript::new(); + let mut verifier_transcriptcript = DefaultTranscript::new(); assert!(zm.verify( &point, &eval, &commitments, &proof, - Some(&mut verifier_transcript), + Some(&mut verifier_transcriptcript), )); //TODO: check both random oracles are synced @@ -729,7 +724,7 @@ mod test { let max_vars = 16; let num_polys = 8; let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); - let srs = ZeromorphSRS::setup(17, rng); + let srs = ZeromorphSRS::setup(1 <<(max_vars + 1), rng); for num_vars in 3..max_vars { // Setup @@ -760,16 +755,16 @@ mod test { // Commit and open let commitments: Vec<_> = polys.iter().map(|poly| zm.commit(poly)).collect(); - let mut prover_transcript = DefaultTranscript::new(); - let proof = zm.open_batch(&point, &evals, &polys, Some(&mut prover_transcript)); + let mut prover_transcriptcript = DefaultTranscript::new(); + let proof = zm.open_batch(&point, &evals, &polys, Some(&mut prover_transcriptcript)); - let mut verifier_transcript = DefaultTranscript::new(); + let mut verifier_transcriptcript = DefaultTranscript::new(); assert!(zm.verify_batch( &point, &evals, &commitments, &proof, - Some(&mut verifier_transcript), + Some(&mut verifier_transcriptcript), )) //TODO: check both random oracles are synced } diff --git a/crypto/src/fiat_shamir/mod.rs b/crypto/src/fiat_shamir/mod.rs index 736301a5a..a818a01b7 100644 --- a/crypto/src/fiat_shamir/mod.rs +++ b/crypto/src/fiat_shamir/mod.rs @@ -1,4 +1,4 @@ pub mod default_transcript; #[cfg(feature = "test_fiat_shamir")] pub mod test_transcript; -pub mod transcript; +pub mod transcript; \ No newline at end of file diff --git a/crypto/src/fiat_shamir/transcript.rs b/crypto/src/fiat_shamir/transcript.rs index 2c16575b7..fb889a5d8 100644 --- a/crypto/src/fiat_shamir/transcript.rs +++ b/crypto/src/fiat_shamir/transcript.rs @@ -1,4 +1,4 @@ pub trait Transcript { fn append(&mut self, new_data: &[u8]); fn challenge(&mut self) -> [u8; 32]; -} +} \ No newline at end of file diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs index ba7c1d24e..a647e8dbe 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs @@ -1,4 +1,5 @@ use super::curve::MILLER_LOOP_CONSTANT; +use super::default_types::FrField; use super::{ curve::BLS12381Curve, field_extension::{BLS12381PrimeField, Degree12ExtensionField, Degree2ExtensionField}, @@ -26,7 +27,7 @@ impl IsPairing for BLS12381AtePairing { type G1Point = ShortWeierstrassProjectivePoint; type G2Point = ShortWeierstrassProjectivePoint; type OutputField = Degree12ExtensionField; - type BaseField = BLS12381PrimeField; + type BaseField = FrField; /// Compute the product of the ate pairings for a list of point pairs. fn compute_batch( diff --git a/math/src/polynomial/sparse_multilinear_poly.rs b/math/src/polynomial/sparse_multilinear_poly.rs index 03131e8f5..3984281dc 100644 --- a/math/src/polynomial/sparse_multilinear_poly.rs +++ b/math/src/polynomial/sparse_multilinear_poly.rs @@ -95,9 +95,9 @@ mod test { let m_poly = SparseMultilinearPolynomial::::new(3, z.clone()); let x = vec![FE::one(), FE::one(), FE::one()]; - assert_eq!(m_poly.evaluate(x.as_slice()).unwrap(), two); + assert_eq!(m_poly.evaluate(x.as_slice()), two); assert_eq!( - SparseMultilinearPolynomial::evaluate_with(3, &z, x.as_slice()).unwrap(), + SparseMultilinearPolynomial::evaluate_with(3, &z, x.as_slice()), two ); diff --git a/provers/plonk/src/verifier.rs b/provers/plonk/src/verifier.rs index 74cab68f1..b3c2c96cf 100644 --- a/provers/plonk/src/verifier.rs +++ b/provers/plonk/src/verifier.rs @@ -178,6 +178,7 @@ impl> Verifier Date: Sat, 27 Jan 2024 17:32:47 -0600 Subject: [PATCH 20/23] fmt --- crypto/src/commitments/kzg.rs | 4 +- crypto/src/commitments/zeromorph/zeromorph.rs | 21 +++-- crypto/src/fiat_shamir/mod.rs | 2 +- crypto/src/fiat_shamir/transcript.rs | 2 +- .../src/polynomial/sparse_multilinear_poly.rs | 82 ++++++++++--------- 5 files changed, 64 insertions(+), 47 deletions(-) diff --git a/crypto/src/commitments/kzg.rs b/crypto/src/commitments/kzg.rs index 3e09fb87b..22c796c51 100644 --- a/crypto/src/commitments/kzg.rs +++ b/crypto/src/commitments/kzg.rs @@ -183,7 +183,7 @@ where eval: &FieldElement, // polynomial proof is being generated with respect to. poly: &Polynomial>, - transcript: Option<&mut dyn Transcript>, + _transcript: Option<&mut dyn Transcript>, ) -> Self::Commitment { let mut poly_to_commit = poly - eval; poly_to_commit.ruffini_division_inplace(point.borrow()); @@ -196,7 +196,7 @@ where eval: &FieldElement, p_commitment: &Self::Commitment, proof: &Self::Proof, - transcript: Option<&mut dyn Transcript>, + _transcript: Option<&mut dyn Transcript>, ) -> bool { let g1 = &self.srs.powers_main_group[0]; let g2 = &self.srs.powers_secondary_group[0]; diff --git a/crypto/src/commitments/zeromorph/zeromorph.rs b/crypto/src/commitments/zeromorph/zeromorph.rs index b8c3eed8b..150b712bc 100644 --- a/crypto/src/commitments/zeromorph/zeromorph.rs +++ b/crypto/src/commitments/zeromorph/zeromorph.rs @@ -1,7 +1,9 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::type_complexity)] -use crate::{commitments::traits::IsPolynomialCommitmentScheme, fiat_shamir::transcript::Transcript}; +use crate::{ + commitments::traits::IsPolynomialCommitmentScheme, fiat_shamir::transcript::Transcript, +}; use core::mem; use lambdaworks_math::{ cyclic_group::IsGroup, @@ -517,7 +519,13 @@ where (f_batched, batched_evaluation) }, ); - Self::open(&self, point, &batched_evaluation, &f_batched, Some(transcript)) + Self::open( + &self, + point, + &batched_evaluation, + &f_batched, + Some(transcript), + ) } // TODO: errors lengths are valid @@ -641,7 +649,10 @@ mod test { use core::ops::Neg; - use crate::{commitments::zeromorph::structs::ZeromorphSRS, fiat_shamir::default_transcript::DefaultTranscript}; + use crate::{ + commitments::zeromorph::structs::ZeromorphSRS, + fiat_shamir::default_transcript::DefaultTranscript, + }; use super::*; use lambdaworks_math::{ @@ -681,7 +692,7 @@ mod test { fn prove_verify_single() { let max_vars = 16; let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); - let srs = ZeromorphSRS::setup( 1 << (max_vars + 1), rng); + let srs = ZeromorphSRS::setup(1 << (max_vars + 1), rng); for num_vars in 3..max_vars { // Setup @@ -724,7 +735,7 @@ mod test { let max_vars = 16; let num_polys = 8; let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); - let srs = ZeromorphSRS::setup(1 <<(max_vars + 1), rng); + let srs = ZeromorphSRS::setup(1 << (max_vars + 1), rng); for num_vars in 3..max_vars { // Setup diff --git a/crypto/src/fiat_shamir/mod.rs b/crypto/src/fiat_shamir/mod.rs index a818a01b7..736301a5a 100644 --- a/crypto/src/fiat_shamir/mod.rs +++ b/crypto/src/fiat_shamir/mod.rs @@ -1,4 +1,4 @@ pub mod default_transcript; #[cfg(feature = "test_fiat_shamir")] pub mod test_transcript; -pub mod transcript; \ No newline at end of file +pub mod transcript; diff --git a/crypto/src/fiat_shamir/transcript.rs b/crypto/src/fiat_shamir/transcript.rs index fb889a5d8..2c16575b7 100644 --- a/crypto/src/fiat_shamir/transcript.rs +++ b/crypto/src/fiat_shamir/transcript.rs @@ -1,4 +1,4 @@ pub trait Transcript { fn append(&mut self, new_data: &[u8]); fn challenge(&mut self) -> [u8; 32]; -} \ No newline at end of file +} diff --git a/math/src/polynomial/sparse_multilinear_poly.rs b/math/src/polynomial/sparse_multilinear_poly.rs index 3984281dc..6e428312f 100644 --- a/math/src/polynomial/sparse_multilinear_poly.rs +++ b/math/src/polynomial/sparse_multilinear_poly.rs @@ -1,6 +1,6 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator}; -use crate::field::{traits::IsField, element::FieldElement}; +use crate::field::{element::FieldElement, traits::IsField}; pub struct SparseMultilinearPolynomial where @@ -17,57 +17,61 @@ where pub fn new(num_vars: usize, evals: Vec<(usize, FieldElement)>) -> Self { SparseMultilinearPolynomial { num_vars, evals } } - + /// Computes the eq extension polynomial of the polynomial. /// return 1 when a == r, otherwise return 0. fn compute_chi(a: &[bool], r: &[FieldElement]) -> FieldElement { - assert_eq!(a.len(), r.len()); - let mut chi_i = FieldElement::one(); - for j in 0..r.len() { - if a[j] { - chi_i *= &r[j]; - } else { - chi_i *= FieldElement::::one() - &r[j]; + assert_eq!(a.len(), r.len()); + let mut chi_i = FieldElement::one(); + for j in 0..r.len() { + if a[j] { + chi_i *= &r[j]; + } else { + chi_i *= FieldElement::::one() - &r[j]; + } } - } - chi_i + chi_i } // Takes O(n log n) pub fn evaluate(&self, r: &[FieldElement]) -> FieldElement { - assert_eq!(self.num_vars, r.len()); + assert_eq!(self.num_vars, r.len()); (0..self.evals.len()) - .into_par_iter() - .map(|i| { - let bits = get_bits(self.evals[i].0,r.len()); - let mut chi_i = FieldElement::::one(); - for j in 0..r.len() { - if bits[j] { - chi_i *= &r[j]; - } else { - chi_i *= FieldElement::::one() - &r[j]; + .into_par_iter() + .map(|i| { + let bits = get_bits(self.evals[i].0, r.len()); + let mut chi_i = FieldElement::::one(); + for j in 0..r.len() { + if bits[j] { + chi_i *= &r[j]; + } else { + chi_i *= FieldElement::::one() - &r[j]; + } } - } - chi_i * &self.evals[i].1 - }) - .sum() + chi_i * &self.evals[i].1 + }) + .sum() } // Takes O(n log n) - pub fn evaluate_with(num_vars: usize, evals: &[(usize, FieldElement)], r: &[FieldElement]) -> FieldElement { - assert_eq!(num_vars, r.len()); - - (0..evals.len()) - .into_par_iter() - .map(|i| { - let bits = get_bits(evals[i].0,r.len()); - SparseMultilinearPolynomial::compute_chi(&bits, r) * &evals[i].1 - }) - .sum() + pub fn evaluate_with( + num_vars: usize, + evals: &[(usize, FieldElement)], + r: &[FieldElement], + ) -> FieldElement { + assert_eq!(num_vars, r.len()); + + (0..evals.len()) + .into_par_iter() + .map(|i| { + let bits = get_bits(evals[i].0, r.len()); + SparseMultilinearPolynomial::compute_chi(&bits, r) * &evals[i].1 + }) + .sum() } } - fn get_bits(n: usize, num_bits: usize) -> Vec { +fn get_bits(n: usize, num_bits: usize) -> Vec { (0..num_bits) .map(|shift_amount| ((n & (1 << (num_bits - shift_amount - 1))) > 0)) .collect::>() @@ -103,7 +107,9 @@ mod test { let x = vec![FE::one(), FE::zero(), FE::one()]; assert_eq!(m_poly.evaluate(x.as_slice()), FE::one()); - assert_eq!(SparseMultilinearPolynomial::evaluate_with(3, &z, x.as_slice()), FE::one()); - + assert_eq!( + SparseMultilinearPolynomial::evaluate_with(3, &z, x.as_slice()), + FE::one() + ); } } From d6e353e76d16accf967c113c88c3476ba05861c5 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Sat, 27 Jan 2024 17:39:19 -0600 Subject: [PATCH 21/23] nit --- provers/plonk/src/verifier.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/provers/plonk/src/verifier.rs b/provers/plonk/src/verifier.rs index b3c2c96cf..74cab68f1 100644 --- a/provers/plonk/src/verifier.rs +++ b/provers/plonk/src/verifier.rs @@ -178,7 +178,6 @@ impl> Verifier Date: Sat, 17 Feb 2024 20:16:03 -0600 Subject: [PATCH 22/23] fix setup --- crypto/src/commitments/zeromorph/structs.rs | 51 ++++------ crypto/src/commitments/zeromorph/zeromorph.rs | 94 +++++-------------- .../curves/bls12_381/pairing.rs | 9 ++ math/src/elliptic_curve/traits.rs | 6 ++ 4 files changed, 58 insertions(+), 102 deletions(-) diff --git a/crypto/src/commitments/zeromorph/structs.rs b/crypto/src/commitments/zeromorph/structs.rs index d0fdfcfa1..0a3515c26 100644 --- a/crypto/src/commitments/zeromorph/structs.rs +++ b/crypto/src/commitments/zeromorph/structs.rs @@ -1,4 +1,4 @@ -use core::mem; +use core::{cmp::max, mem}; use lambdaworks_math::{ cyclic_group::IsGroup, @@ -34,35 +34,25 @@ impl ZeromorphSRS

{ let tau = FieldElement::::from_bytes_be(&bytes).unwrap(); rng.fill_bytes(&mut bytes); let g1_scalar = FieldElement::::from_bytes_be(&bytes).unwrap(); - let g1 = P::G1Point::neutral_element().operate_with_self(g1_scalar.representative()); + let g1 = P::g1_generator().operate_with_self(g1_scalar.representative()); rng.fill_bytes(&mut bytes); let g2_scalar = FieldElement::::from_bytes_be(&bytes).unwrap(); - let g2 = P::G2Point::neutral_element().operate_with_self(g2_scalar.representative()); - - //TODO: Add window table for msm this is slow af - let tau_powers = (0..=max_degree) - .scan(tau.clone(), |state, _| { - let val = state.clone(); - *state *= τ - Some(val) - }) - .map(|scalar| scalar.representative()) - .collect::>(); - - let g1s: Vec<_> = (0..=max_degree).map(|e| g1.operate_with_self(e)).collect(); - let g2s: Vec<_> = (0..=max_degree).map(|e| g2.operate_with_self(e)).collect(); - - //TODO: gate with rayon - let g1_powers = g1s - .iter() - .zip(tau_powers.iter()) - .map(|(g1, tau)| g1.operate_with_self(*tau)) - .collect(); - let g2_powers = g2s - .iter() - .zip(tau_powers.iter()) - .map(|(g2, tau)| g2.operate_with_self(*tau)) - .collect(); + let g2 = P::g2_generator().operate_with_self(g2_scalar.representative()); + + let g1_powers: Vec> = vec![FieldElement::zero(); max_degree]; + let g2_powers: Vec> = vec![FieldElement::zero(); max_degree]; + + let g1_powers: Vec = std::iter::once(g1.clone()).chain(g1_powers.iter().scan(g1, |state, _| { + let val = state.clone(); + *state = state.operate_with_self(tau.representative()); + Some(val) + })).collect(); + + let g2_powers: Vec = std::iter::once(g2.clone()).chain(g2_powers.iter().scan(g2, |state, _| { + let val = state.clone(); + *state = state.operate_with_self(tau.representative()); + Some(val) + })).collect(); ZeromorphSRS { g1_powers, @@ -89,11 +79,9 @@ impl ZeromorphSRS

{ )); } let offset = self.g1_powers.len() - max_degree; - let offset_g1_powers = self.g1_powers[offset..].to_vec(); Ok(( ZeromorphProverKey { g1_powers: self.g1_powers.clone(), - offset_g1_powers: offset_g1_powers, }, ZeromorphVerifierKey { g1: self.g1_powers[0].clone(), @@ -234,7 +222,6 @@ pub struct ZeromorphProof { #[derive(Clone, Debug)] pub struct ZeromorphProverKey { pub g1_powers: Vec, - pub offset_g1_powers: Vec, } #[derive(Copy, Clone, Debug)] @@ -249,4 +236,4 @@ pub struct ZeromorphVerifierKey { pub enum ZeromorphError { //#[error("Length Error: SRS Length: {0}, Key Length: {0}")] KeyLengthError(usize, usize), -} +} \ No newline at end of file diff --git a/crypto/src/commitments/zeromorph/zeromorph.rs b/crypto/src/commitments/zeromorph/zeromorph.rs index 150b712bc..5f4d994f9 100644 --- a/crypto/src/commitments/zeromorph/zeromorph.rs +++ b/crypto/src/commitments/zeromorph/zeromorph.rs @@ -77,24 +77,24 @@ where } fn compute_batched_lifted_degree_quotient( - n: usize, quotients: &Vec>>, y_challenge: &FieldElement, ) -> Polynomial> { // Compute \hat{q} = \sum_k y^k * X^{N - d_k - 1} * q_k let mut scalar = FieldElement::::one(); // y^k + let num_vars = quotients.len(); // Rather than explicitly computing the shifts of q_k by N - d_k - 1 (i.e. multiplying q_k by X^{N - d_k - 1}) // then accumulating them, we simply accumulate y^k*q_k into \hat{q} at the index offset N - d_k - 1 let q_hat = quotients .iter() .enumerate() - .fold(vec![FieldElement::zero(); n], |mut q_hat, (idx, q)| { + .fold(vec![FieldElement::zero(); 1 << num_vars], |mut q_hat, (idx, q)| { #[cfg(feature = "parallel")] - let q_hat_iter = q_hat[n - (1 << idx)..].par_iter_mut(); + let q_hat_iter = q_hat[(1 << num_vars) - (1 << idx)..].par_iter_mut(); #[cfg(not(feature = "parallel"))] - let q_hat_iter = q_hat[n - (1 << idx)..].iter_mut(); + let q_hat_iter = q_hat[(1 << num_vars) - (1 << idx)..].iter_mut(); q_hat_iter.zip(&q.coefficients).for_each(|(q_hat, q)| { *q_hat += &scalar * q; }); @@ -227,28 +227,11 @@ where serialized_data.extend(&g1_powers_len_bytes); - // third 8 bytes store the amount of G1 offset elements to be stored, this is more than can be indexed with a 64-bit architecture, and some millions of terabytes of data if the points were compressed - let mut offset_g1_powers_len_bytes: Vec = - self.pk.offset_g1_powers.len().to_le_bytes().to_vec(); - - // For architectures with less than 64 bits for pointers - // We add extra zeros at the end - while offset_g1_powers_len_bytes.len() < 8 { - offset_g1_powers_len_bytes.push(0) - } - - serialized_data.extend(&offset_g1_powers_len_bytes); - // G1 elements for point in &self.pk.g1_powers { serialized_data.extend(point.as_bytes()); } - // G2 elements - for point in &self.pk.offset_g1_powers { - serialized_data.extend(point.as_bytes()); - } - // NOTE: this could potentially be recycled from the g1_powers but being explicit reduces complexity serialized_data.extend(&self.vk.g1.as_bytes()); serialized_data.extend(&self.vk.g2.as_bytes()); @@ -267,7 +250,6 @@ where fn deserialize(bytes: &[u8]) -> Result { const VERSION_OFFSET: usize = 4; const G1_LEN_OFFSET: usize = 12; - const OFFSET_G1_LEN_OFFSET: usize = 20; let g1_powers_len_u64 = u64::from_le_bytes( // This unwrap can't fail since we are fixing the size of the slice @@ -277,18 +259,7 @@ where let g1_powers_len = usize::try_from(g1_powers_len_u64) .map_err(|_| DeserializationError::PointerSizeError)?; - let offset_g1_powers_len_u64 = u64::from_le_bytes( - // This unwrap can't fail since we are fixing the size of the slice - bytes[G1_LEN_OFFSET..OFFSET_G1_LEN_OFFSET] - .try_into() - .unwrap(), - ); - - let offset_g1_powers_len = usize::try_from(offset_g1_powers_len_u64) - .map_err(|_| DeserializationError::PointerSizeError)?; - let mut g1_powers: Vec = Vec::new(); - let mut offset_g1_powers: Vec = Vec::new(); let size_g1_point = mem::size_of::(); let size_g2_point = mem::size_of::(); @@ -296,33 +267,20 @@ where for i in 0..g1_powers_len { // The second unwrap shouldn't fail since the amount of bytes is fixed let point = P::G1Point::deserialize( - bytes[i * size_g1_point + OFFSET_G1_LEN_OFFSET - ..i * size_g1_point + size_g1_point + OFFSET_G1_LEN_OFFSET] + bytes[i * size_g1_point + G1_LEN_OFFSET + ..i * size_g1_point + size_g1_point + G1_LEN_OFFSET] .try_into() .unwrap(), )?; g1_powers.push(point); } - let offset_g1_offset = size_g1_point * g1_powers_len + OFFSET_G1_LEN_OFFSET; - for i in 0..g1_powers_len { - // The second unwrap shouldn't fail since the amount of bytes is fixed - let point = P::G1Point::deserialize( - bytes[i * size_g1_point + offset_g1_offset - ..i * size_g1_point + size_g1_point + offset_g1_offset] - .try_into() - .unwrap(), - )?; - offset_g1_powers.push(point); - } let pk = ZeromorphProverKey { g1_powers, - offset_g1_powers, }; let vk_offset = size_g1_point * g1_powers_len - + size_g1_point * offset_g1_powers_len - + OFFSET_G1_LEN_OFFSET; + + G1_LEN_OFFSET; let g1 = P::G1Point::deserialize( bytes[vk_offset..size_g1_point + vk_offset] .try_into() @@ -390,8 +348,6 @@ where let point = point.borrow(); //TODO: error or interface or something let transcript = transcript.unwrap(); - let num_vars = point.len(); - let n: usize = 1 << num_vars; let mut pi_poly = Polynomial::new(&poly.evals()); // Compute the multilinear quotients q_k = q_k(X_0, ..., X_{k-1}) @@ -421,7 +377,7 @@ where let y_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); // Compute the batched, lifted-degree quotient \hat{q} - let q_hat = compute_batched_lifted_degree_quotient::

(n, "ients, &y_challenge); + let q_hat = compute_batched_lifted_degree_quotient::

("ients, &y_challenge); // Compute and send the commitment C_q = [\hat{q}] // commit at offset @@ -437,7 +393,7 @@ where .collect(); msm( &scalars[offset..], - &self.pk.offset_g1_powers[offset..q_hat.coeff_len()], + &self.pk.g1_powers[offset..q_hat.coeff_len()], ) .unwrap() }; @@ -463,21 +419,23 @@ where pi_poly = &pi_poly + &q; }); - debug_assert_eq!( + assert_eq!( pi_poly.evaluate(&x_challenge), FieldElement::::zero() ); // Compute the KZG opening proof pi_poly; -> TODO should abstract into separate trait - let pi = { - pi_poly.ruffini_division_inplace(&x_challenge); - let scalars: Vec<_> = pi_poly + let (pi, ueval) = { + let divisor = Polynomial::new(&[-x_challenge.clone(), FieldElement::one()]); + let (witness, _) = pi_poly.clone().long_division_with_remainder(&divisor); + let scalars: Vec<_> = witness .coefficients .iter() .map(|eval| eval.representative()) .collect(); - msm(&scalars, &self.pk.g1_powers[..pi_poly.coeff_len()]).unwrap() + (msm(&scalars, &self.pk.g1_powers[..witness.coeff_len()]).unwrap(), pi_poly.evaluate(&x_challenge)) }; + assert_eq!(ueval, FieldElement::zero()); ZeromorphProof { pi, @@ -554,7 +512,7 @@ where // Challenge y let y_challenge = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - // Receive commitment C_{q} -> Since our transcriptcript does not support appending and receiving data we instead store these commitments in a zeromorph proof struct + // Receive commitment C_{q} -> Since our transcript does not support appending and receiving data we instead store these commitments in a zeromorph proof struct transcript.append(&q_hat_com.as_bytes()); // Challenge x, z @@ -572,7 +530,7 @@ where }); let scalars = [ - vec![FieldElement::one(), z_challenge, (eval * eval_scalar)], + vec![FieldElement::one(), z_challenge, (eval_scalar * eval)], q_scalars, ] .concat(); @@ -592,6 +550,7 @@ where // e(pi, [tau]_2 - x * [1]_2) == e(C_{\zeta,Z}, [X^(N_max - 2^n - 1)]_2) <==> e(C_{\zeta,Z} - x * pi, [X^{N_max - 2^n - 1}]_2) * e(-pi, [tau_2]) == 1 let e = P::compute_batch(&[ + (&zeta_z_com, &self.vk.tau_n_max_sub_2_n.neg()), ( &pi, &self.vk.tau_g2.operate_with( @@ -602,7 +561,6 @@ where .neg(), ), ), - (&zeta_z_com, &self.vk.tau_n_max_sub_2_n), ]) .unwrap(); e == FieldElement::one() @@ -690,7 +648,7 @@ mod test { #[test] fn prove_verify_single() { - let max_vars = 16; + let max_vars = 8; let mut rng = &mut ChaCha20Rng::from_seed(*b"zeromorph_poly_commitment_scheme"); let srs = ZeromorphSRS::setup(1 << (max_vars + 1), rng); @@ -714,16 +672,16 @@ mod test { // Commit and open let commitments = zm.commit(&poly); - let mut prover_transcriptcript = DefaultTranscript::new(); - let proof = zm.open(&point, &eval, &poly, Some(&mut prover_transcriptcript)); + let mut prover_transcript = DefaultTranscript::new(); + let proof = zm.open(&point, &eval, &poly, Some(&mut prover_transcript)); - let mut verifier_transcriptcript = DefaultTranscript::new(); + let mut verifier_transcript = DefaultTranscript::new(); assert!(zm.verify( &point, &eval, &commitments, &proof, - Some(&mut verifier_transcriptcript), + Some(&mut verifier_transcript), )); //TODO: check both random oracles are synced @@ -842,9 +800,6 @@ mod test { /// Μ‚q = βˆ‘β‚–β‚Œβ‚€βΏβ»ΒΉ yᡏ Xᡐ⁻ᡈᡏ⁻¹ Μ‚qβ‚–, 𝑑ₖ = deg(Μ‚q), π‘š = 𝑁 #[test] fn batched_lifted_degree_quotient() { - const NUM_VARS: usize = 3; - const N: usize = 1 << NUM_VARS; - // Define mock qβ‚– with deg(qβ‚–) = 2ᡏ⁻¹ let q_0 = Polynomial::new(&[FieldElement::one()]); let q_1 = Polynomial::new(&[FieldElement::from(2u64), FieldElement::from(3u64)]); @@ -861,7 +816,6 @@ mod test { //Compute batched quptient Μ‚q let batched_quotient = compute_batched_lifted_degree_quotient::( - N, "ients, &y_challenge, ); diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs index a647e8dbe..2dd573cc8 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs @@ -5,6 +5,7 @@ use super::{ field_extension::{BLS12381PrimeField, Degree12ExtensionField, Degree2ExtensionField}, twist::BLS12381TwistCurve, }; +use crate::elliptic_curve::traits::IsEllipticCurve; use crate::{cyclic_group::IsGroup, elliptic_curve::traits::IsPairing, errors::PairingError}; use crate::{ @@ -46,6 +47,14 @@ impl IsPairing for BLS12381AtePairing { } Ok(final_exponentiation(&result)) } + + fn g1_generator() -> Self::G1Point { + BLS12381Curve::generator() + } + + fn g2_generator() -> Self::G2Point { + BLS12381TwistCurve::generator() + } } fn double_accumulate_line( diff --git a/math/src/elliptic_curve/traits.rs b/math/src/elliptic_curve/traits.rs index 1c6ff847c..0e705dd16 100644 --- a/math/src/elliptic_curve/traits.rs +++ b/math/src/elliptic_curve/traits.rs @@ -54,4 +54,10 @@ pub trait IsPairing { ) -> Result, PairingError> { Self::compute_batch(&[(p, q)]) } + + /// Returns the generator of the g1 curve subgroup. + fn g1_generator() -> Self::G1Point; + + /// Returns the generator of the g2 curve subgroup. + fn g2_generator() -> Self::G2Point; } From 7f527821272134fc637060bc2fe529f509dcae81 Mon Sep 17 00:00:00 2001 From: PatStiles Date: Sat, 17 Feb 2024 21:05:09 -0600 Subject: [PATCH 23/23] fix offset power and pairing check --- crypto/src/commitments/zeromorph/structs.rs | 3 +++ crypto/src/commitments/zeromorph/zeromorph.rs | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/crypto/src/commitments/zeromorph/structs.rs b/crypto/src/commitments/zeromorph/structs.rs index 0a3515c26..628946a00 100644 --- a/crypto/src/commitments/zeromorph/structs.rs +++ b/crypto/src/commitments/zeromorph/structs.rs @@ -79,9 +79,11 @@ impl ZeromorphSRS

{ )); } let offset = self.g1_powers.len() - max_degree; + let offset_g1_powers = self.g1_powers[offset..offset + max_degree].to_vec(); Ok(( ZeromorphProverKey { g1_powers: self.g1_powers.clone(), + offset_g1_powers }, ZeromorphVerifierKey { g1: self.g1_powers[0].clone(), @@ -222,6 +224,7 @@ pub struct ZeromorphProof { #[derive(Clone, Debug)] pub struct ZeromorphProverKey { pub g1_powers: Vec, + pub offset_g1_powers: Vec, } #[derive(Copy, Clone, Debug)] diff --git a/crypto/src/commitments/zeromorph/zeromorph.rs b/crypto/src/commitments/zeromorph/zeromorph.rs index 5f4d994f9..3b6a7a88b 100644 --- a/crypto/src/commitments/zeromorph/zeromorph.rs +++ b/crypto/src/commitments/zeromorph/zeromorph.rs @@ -275,8 +275,11 @@ where g1_powers.push(point); } + let offset_g1_powers = Vec::new(); + let pk = ZeromorphProverKey { g1_powers, + offset_g1_powers }; let vk_offset = size_g1_point * g1_powers_len @@ -393,7 +396,7 @@ where .collect(); msm( &scalars[offset..], - &self.pk.g1_powers[offset..q_hat.coeff_len()], + &self.pk.offset_g1_powers[offset..scalars.len()], ) .unwrap() };