From 433c1c5fb2355c9f8daf4dbcd257c8deddffef17 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 23 Jan 2025 08:19:06 -0500 Subject: [PATCH 01/10] stashing init commit for kzg integration --- Cargo.lock | 1 + poly_commit/Cargo.toml | 1 + poly_commit/src/kzg.rs | 5 ++ poly_commit/src/kzg/structs.rs | 89 ++++++++++++++++++++++++++++++++++ poly_commit/src/kzg/utils.rs | 22 +++++++++ poly_commit/src/lib.rs | 3 ++ 6 files changed, 121 insertions(+) create mode 100644 poly_commit/src/kzg.rs create mode 100644 poly_commit/src/kzg/structs.rs create mode 100644 poly_commit/src/kzg/utils.rs diff --git a/Cargo.lock b/Cargo.lock index a41aa1ef..18da3b5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1833,6 +1833,7 @@ dependencies = [ "gf2", "gf2_128", "gkr_field_config", + "halo2curves", "itertools 0.13.0", "mersenne31", "mpi_config", diff --git a/poly_commit/Cargo.toml b/poly_commit/Cargo.toml index 5638a839..26d17591 100644 --- a/poly_commit/Cargo.toml +++ b/poly_commit/Cargo.toml @@ -17,6 +17,7 @@ ethnum.workspace = true ark-std.workspace = true thiserror.workspace = true itertools.workspace = true +halo2curves.workspace = true [dev-dependencies] gf2_128 = { path = "../arith/gf2_128" } diff --git a/poly_commit/src/kzg.rs b/poly_commit/src/kzg.rs new file mode 100644 index 00000000..9d28755e --- /dev/null +++ b/poly_commit/src/kzg.rs @@ -0,0 +1,5 @@ +mod structs; +pub use structs::*; + +mod utils; +pub use utils::*; diff --git a/poly_commit/src/kzg/structs.rs b/poly_commit/src/kzg/structs.rs new file mode 100644 index 00000000..91932272 --- /dev/null +++ b/poly_commit/src/kzg/structs.rs @@ -0,0 +1,89 @@ +use halo2curves::pairing::Engine; + +/// Structured reference string for Bi-KZG polynomial commitment scheme. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct CoefFormBiKZGSRS { + /// (g_1^{\tau_0^i\tau_1^j})_{i\in [0,N], j\in [0, M]} = \\ + /// ( + /// g_1, g_1^{\tau_0}, g_1^{\tau_0^2}, ..., g_1^{\tau_0^N}, + /// g_1^{\tau_1}, g_1^{\tau_0\tau_1}, g_1^{\tau_0^2\tau_1}, ..., g_1^{\tau_0^N\tau_1}, + /// ..., g_1^{\tau_0^N\tau_1^M} + /// ) + pub powers_of_g: Vec, + /// g in lagrange form over omega_0 and omega_1 + pub powers_of_g_lagrange_over_both_roots: Vec, + /// The generator of G2. + pub h: E::G2Affine, + /// tau_0 times the above generator of G2. + pub tau_0_h: E::G2Affine, + /// tau_1 times the above generator of G2. + pub tau_1_h: E::G2Affine, +} + +/// Structured reference string for Bi-KZG polynomial commitment scheme. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct LagrangeFormBiKZGSRS { + /// The generator of G1 + pub g: E::G1Affine, + /// g in lagrange form over omega_0 + pub powers_of_g_lagrange_over_x: Vec, + /// g in lagrange form over omega_0 and omega_1 + pub powers_of_g_lagrange_over_both_roots: Vec, + /// The generator of G2. + pub h: E::G2Affine, + /// tau_0 times the above generator of G2. + pub tau_0_h: E::G2Affine, + /// tau_1 times the above generator of G2. + pub tau_1_h: E::G2Affine, +} + +/// `UnivariateVerifierParam` is used to check evaluation proofs for a given +/// commitment. +#[derive(Clone, Debug, Eq, PartialEq, Default)] +pub struct BiKZGVerifierParam { + /// The generator of G1. + pub g: E::G1Affine, + /// The generator of G2. + pub h: E::G2Affine, + /// tau_0 times the above generator of G2. + pub tau_0_h: E::G2Affine, + /// tau_1 times the above generator of G2. + pub tau_1_h: E::G2Affine, +} + +/// Commitment Bi-KZG polynomial commitment scheme. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct BiKZGCommitment { + /// the actual commitment is an affine point. + pub com: E::G1Affine, +} + +/// Proof for Bi-KZG polynomial commitment scheme. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct BiKZGProof { + /// the actual proof is a pair of affine points. + pub pi0: E::G1Affine, + pub pi1: E::G1Affine, +} + +impl From<&CoefFormBiKZGSRS> for BiKZGVerifierParam { + fn from(srs: &CoefFormBiKZGSRS) -> Self { + Self { + g: srs.powers_of_g[0], + h: srs.h, + tau_0_h: srs.tau_0_h, + tau_1_h: srs.tau_1_h, + } + } +} + +impl From<&LagrangeFormBiKZGSRS> for BiKZGVerifierParam { + fn from(srs: &LagrangeFormBiKZGSRS) -> Self { + Self { + g: srs.g, + h: srs.h, + tau_0_h: srs.tau_0_h, + tau_1_h: srs.tau_1_h, + } + } +} diff --git a/poly_commit/src/kzg/utils.rs b/poly_commit/src/kzg/utils.rs new file mode 100644 index 00000000..79570a44 --- /dev/null +++ b/poly_commit/src/kzg/utils.rs @@ -0,0 +1,22 @@ +use arith::Field; +use halo2curves::ff::PrimeField; + +pub fn primitive_root_of_unity(group_size: usize) -> F { + let omega = F::ROOT_OF_UNITY; + let omega = omega.pow_vartime([(1 << F::S) / group_size as u64]); + assert!( + omega.pow_vartime([group_size as u64]) == F::ONE, + "omega_0 is not root of unity for supported_n" + ); + omega +} + +pub(crate) fn powers_of_field_elements(x: &F, n: usize) -> Vec { + let mut powers = vec![F::ONE]; + let mut cur = *x; + for _ in 0..n - 1 { + powers.push(cur); + cur *= x; + } + powers +} diff --git a/poly_commit/src/lib.rs b/poly_commit/src/lib.rs index 5184ea08..aecda260 100644 --- a/poly_commit/src/lib.rs +++ b/poly_commit/src/lib.rs @@ -13,3 +13,6 @@ pub use raw::RawExpanderGKR; pub mod orion; pub use orion::*; + +pub mod kzg; +pub use kzg::*; From 4b3797fa4ff2e4b5da7190c7f26fb4c8e3ae15a5 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Mon, 27 Jan 2025 12:17:25 -0500 Subject: [PATCH 02/10] minor in workflow --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f78cb710..9c827473 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,7 +41,7 @@ jobs: run: python3 ./scripts/install.py - name: Set RUSTFLAGS for AVX - if: matrix.feature != 'macos-latest' + if: matrix.os != 'macos-latest' run: echo "RUSTFLAGS=$RUSTFLAGS -C target-feature=+${{ matrix.feature }}" >> $GITHUB_ENV - name: Prepare binary From 0f2f6c130834d43bb3f4e06aac7e592272adf6a1 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Tue, 28 Jan 2025 14:00:44 -0500 Subject: [PATCH 03/10] toy commit - vanilla univariate kzg with coefficient form univariate poly --- poly_commit/src/kzg.rs | 6 ++ poly_commit/src/kzg/structs.rs | 40 +++++++++++ poly_commit/src/kzg/tests.rs | 100 ++++++++++++++++++++++++++++ poly_commit/src/kzg/univariate.rs | 107 ++++++++++++++++++++++++++++++ poly_commit/src/kzg/utils.rs | 23 ++++++- 5 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 poly_commit/src/kzg/tests.rs create mode 100644 poly_commit/src/kzg/univariate.rs diff --git a/poly_commit/src/kzg.rs b/poly_commit/src/kzg.rs index 9d28755e..0482a341 100644 --- a/poly_commit/src/kzg.rs +++ b/poly_commit/src/kzg.rs @@ -3,3 +3,9 @@ pub use structs::*; mod utils; pub use utils::*; + +mod univariate; +pub use univariate::*; + +#[cfg(test)] +mod tests; diff --git a/poly_commit/src/kzg/structs.rs b/poly_commit/src/kzg/structs.rs index 91932272..fa22f477 100644 --- a/poly_commit/src/kzg/structs.rs +++ b/poly_commit/src/kzg/structs.rs @@ -87,3 +87,43 @@ impl From<&LagrangeFormBiKZGSRS> for BiKZGVerifierParam { } } } + +/// Structured reference string for univariate KZG polynomial commitment scheme. +/// The univariate polynomial here is of coefficient form. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct CoefFormUniKZGSRS { + /// power of \tau times the generators of G1, yielding + /// \{ \[\tau^i\]_1 \}_{i \in \[0, 2^n - 1\]} + pub powers_of_tau: Vec, + /// \tau times the generator of G2, [\tau]_2. + pub tau_g2: E::G2Affine, +} + +/// Univariate KZG PCS verifier's params. +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct UniKZGVerifierParams { + /// \tau times the generator of G2, [\tau]_2. + pub tau_g2: E::G2Affine, +} + +/// Univariate KZG commitment. +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct UniKZGCommitment { + /// Univariate KZG commitment, polynomial evaluated at \tau times G1 generator. + pub com: E::G1Affine, +} + +/// Univariate KZG opening. +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct UniKZGOpening { + /// Univariate KZG opening, (f(x) - y) / (x - \alpha) evaluated at \tau times G1 generator. + pub opening: E::G1Affine, +} + +impl From<&CoefFormUniKZGSRS> for UniKZGVerifierParams { + fn from(value: &CoefFormUniKZGSRS) -> Self { + Self { + tau_g2: value.tau_g2, + } + } +} diff --git a/poly_commit/src/kzg/tests.rs b/poly_commit/src/kzg/tests.rs new file mode 100644 index 00000000..d7d7d261 --- /dev/null +++ b/poly_commit/src/kzg/tests.rs @@ -0,0 +1,100 @@ +use halo2curves::{ + bn256::{Bn256, Fr}, + ff::Field, +}; + +use crate::{coeff_form_uni_kzg_verify, univariate_degree_one_quotient, UniKZGVerifierParams}; + +use super::{coeff_form_uni_kzg_commit, coeff_form_uni_kzg_opening, generate_srs_for_testing}; + +#[test] +fn test_univariate_degree_one_quotient() { + { + // x^3 + 1 = (x + 1)(x^2 - x + 1) + let poly = vec![ + Fr::from(1u64), + Fr::from(0u64), + Fr::from(0u64), + Fr::from(1u64), + ]; + let point = -Fr::from(1u64); + let (div, remainder) = univariate_degree_one_quotient(&poly, point); + assert_eq!( + div, + vec![ + Fr::from(1u64), + -Fr::from(1u64), + Fr::from(1u64), + Fr::from(0u64) + ] + ); + assert_eq!(remainder, Fr::ZERO) + } + { + // x^3 - 1 = (x-1)(x^2 + x + 1) + let poly = vec![ + -Fr::from(1u64), + Fr::from(0u64), + Fr::from(0u64), + Fr::from(1u64), + ]; + let point = Fr::from(1u64); + let (div, remainder) = univariate_degree_one_quotient(&poly, point); + assert_eq!( + div, + vec![ + Fr::from(1u64), + Fr::from(1u64), + Fr::from(1u64), + Fr::from(0u64) + ] + ); + assert_eq!(remainder, Fr::ZERO) + } + { + // x^3 + 6x^2 + 11x + 6 = (x + 1)(x + 2)(x + 3) + let poly = vec![ + Fr::from(6u64), + Fr::from(11u64), + Fr::from(6u64), + Fr::from(1u64), + ]; + let point = Fr::from(1u64); + let (div, remainder) = univariate_degree_one_quotient(&poly, point); + assert_eq!( + div, + vec![ + Fr::from(18u64), + Fr::from(7u64), + Fr::from(1u64), + Fr::from(0u64), + ] + ); + assert_eq!(remainder, Fr::from(24u64)) + } +} + +#[test] +fn test_coefficient_form_univariate_kzg_e2e() { + // \prod_{i \in [1, 7]} (x + i) + let poly = vec![ + Fr::from(5040u32), + Fr::from(13068u64), + Fr::from(13132u64), + Fr::from(6769u64), + Fr::from(1960u64), + Fr::from(322u64), + Fr::from(28u64), + Fr::from(1u64), + ]; + + let srs = generate_srs_for_testing::(8); + let vk: UniKZGVerifierParams = From::from(&srs); + let com = coeff_form_uni_kzg_commit(&srs, &poly); + + let alpha = Fr::from(3u64); + let eval = Fr::from(604800u64); + + let opening = coeff_form_uni_kzg_opening(&srs, &poly, alpha, eval); + assert!(coeff_form_uni_kzg_verify(vk, com, alpha, eval, opening)) +} diff --git a/poly_commit/src/kzg/univariate.rs b/poly_commit/src/kzg/univariate.rs new file mode 100644 index 00000000..067e591e --- /dev/null +++ b/poly_commit/src/kzg/univariate.rs @@ -0,0 +1,107 @@ +use ark_std::test_rng; +use halo2curves::{ + ff::Field, + group::{prime::PrimeCurveAffine, Curve, Group}, + msm::best_multiexp, + pairing::{MillerLoopResult, MultiMillerLoop}, + CurveAffine, +}; + +use crate::{powers_of_field_elements, univariate_degree_one_quotient}; + +use super::{CoefFormUniKZGSRS, UniKZGCommitment, UniKZGOpening, UniKZGVerifierParams}; + +pub fn generate_srs_for_testing(length: usize) -> CoefFormUniKZGSRS +where + E::G1Affine: CurveAffine, +{ + assert!(length.is_power_of_two()); + + let mut rng = test_rng(); + let tau = E::Fr::random(&mut rng); + let g1 = E::G1Affine::generator(); + + let tau_geometric_progression = powers_of_field_elements(&tau, length); + + let g1_prog = g1.to_curve(); + let coeff_bases = { + let mut proj_bases = vec![E::G1::identity(); length]; + proj_bases + .iter_mut() + .enumerate() + .for_each(|(i, base)| *base = g1_prog * tau_geometric_progression[i]); + + let mut g_bases = vec![E::G1Affine::default(); length]; + E::G1::batch_normalize(&proj_bases, &mut g_bases); + + drop(proj_bases); + g_bases + }; + + CoefFormUniKZGSRS { + powers_of_tau: coeff_bases, + tau_g2: (E::G2Affine::generator() * tau).into(), + } +} + +pub fn coeff_form_uni_kzg_commit( + srs: &CoefFormUniKZGSRS, + coeffs: &[E::Fr], +) -> UniKZGCommitment +where + E::G1Affine: CurveAffine, +{ + assert_eq!(srs.powers_of_tau.len(), coeffs.len()); + + let com = best_multiexp(coeffs, srs.powers_of_tau.as_slice()); + + UniKZGCommitment { com: com.into() } +} + +pub fn coeff_form_uni_kzg_opening( + srs: &CoefFormUniKZGSRS, + coeffs: &[E::Fr], + alpha: E::Fr, + eval: E::Fr, +) -> UniKZGOpening +where + E::G1Affine: CurveAffine, +{ + assert_eq!(srs.powers_of_tau.len(), coeffs.len()); + + let (div, remainder) = univariate_degree_one_quotient(coeffs, alpha); + assert_eq!(remainder, eval); + + let opening = best_multiexp(&div, srs.powers_of_tau.as_slice()); + + UniKZGOpening { + opening: opening.into(), + } +} + +pub fn coeff_form_uni_kzg_verify( + vk: UniKZGVerifierParams, + comm: UniKZGCommitment, + alpha: E::Fr, + eval: E::Fr, + opening: UniKZGOpening, +) -> bool +where + E::G1Affine: CurveAffine, +{ + let g1_eval: E::G1Affine = (E::G1Affine::generator() * eval).into(); + let g2_alpha: E::G2 = E::G2Affine::generator() * alpha; + + let gt_result = E::multi_miller_loop(&[ + ( + &opening.opening, + &(vk.tau_g2.to_curve() - g2_alpha).to_affine().into(), + ), + ( + &(g1_eval - comm.com).into(), + &E::G2Affine::generator().into(), + ), + ]); + + gt_result.final_exponentiation().is_identity().into() +} diff --git a/poly_commit/src/kzg/utils.rs b/poly_commit/src/kzg/utils.rs index 79570a44..ac827633 100644 --- a/poly_commit/src/kzg/utils.rs +++ b/poly_commit/src/kzg/utils.rs @@ -1,5 +1,4 @@ -use arith::Field; -use halo2curves::ff::PrimeField; +use halo2curves::ff::{Field, PrimeField}; pub fn primitive_root_of_unity(group_size: usize) -> F { let omega = F::ROOT_OF_UNITY; @@ -20,3 +19,23 @@ pub(crate) fn powers_of_field_elements(x: &F, n: usize) -> Vec { } powers } + +/// Given a univariate polynomial of coefficient form f(X) = c0 + c1 X + ... + cn X^n +/// and perform the division f(X) / (X - \alpha). +pub(crate) fn univariate_degree_one_quotient(coeffs: &[F], alpha: F) -> (Vec, F) { + let mut div_coeffs = coeffs.to_vec(); + + for i in (1..coeffs.len()).rev() { + // c X^n = c X^n - c \alpha X^(n - 1) + c \alpha X^(n - 1) + // = c (X - \alpha) X^(n - 1) + c \alpha X^(n - 1) + + let remainder = div_coeffs[i] * alpha; + div_coeffs[i - 1] += remainder; + } + + let final_remainder = div_coeffs[0]; + let mut final_div_coeffs = div_coeffs[1..].to_owned(); + final_div_coeffs.resize(coeffs.len(), F::ZERO); + + (final_div_coeffs, final_remainder) +} From 922ccd874bcd31804102869603608047d1d09da5 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Wed, 29 Jan 2025 21:11:54 -0500 Subject: [PATCH 04/10] stashing changes for now, half way working on hyperkzg --- poly_commit/src/kzg.rs | 3 ++ poly_commit/src/kzg/hyperkzg.rs | 73 +++++++++++++++++++++++++++++++ poly_commit/src/kzg/structs.rs | 14 ------ poly_commit/src/kzg/tests.rs | 23 ++++++++-- poly_commit/src/kzg/univariate.rs | 34 +++++++------- poly_commit/src/kzg/utils.rs | 27 ++++++++++++ 6 files changed, 137 insertions(+), 37 deletions(-) create mode 100644 poly_commit/src/kzg/hyperkzg.rs diff --git a/poly_commit/src/kzg.rs b/poly_commit/src/kzg.rs index 0482a341..a21ceea6 100644 --- a/poly_commit/src/kzg.rs +++ b/poly_commit/src/kzg.rs @@ -7,5 +7,8 @@ pub use utils::*; mod univariate; pub use univariate::*; +mod hyperkzg; +pub use hyperkzg::*; + #[cfg(test)] mod tests; diff --git a/poly_commit/src/kzg/hyperkzg.rs b/poly_commit/src/kzg/hyperkzg.rs new file mode 100644 index 00000000..df7f394e --- /dev/null +++ b/poly_commit/src/kzg/hyperkzg.rs @@ -0,0 +1,73 @@ +use arith::ExtensionField; +use halo2curves::{ff::Field, group::Group, pairing::MultiMillerLoop, CurveAffine}; +use transcript::Transcript; + +use crate::{ + coeff_form_uni_kzg_commit, coeff_form_uni_kzg_open, even_odd_coeffs_separate, merge_coeffs, + powers_of_field_elements, univariate_evaluate, +}; + +use super::CoefFormUniKZGSRS; + +pub fn coeff_form_uni_hyperkzg_open>( + srs: &CoefFormUniKZGSRS, + coeffs: &[E::Fr], + alphas: &[E::Fr], + _eval: E::Fr, + beta: E::Fr, + gamma: E::Fr, + _transcript: &mut T, +) where + E::G1Affine: CurveAffine, + E::Fr: ExtensionField, +{ + let beta2 = beta * beta; + let power_series = powers_of_field_elements(&beta2, coeffs.len() >> 1); + let mut local_coeffs = coeffs.to_vec(); + let mut combine_weight = E::Fr::ONE; + + let mut folded_commitment_agg: E::G1 = E::G1::generator() * E::Fr::ZERO; + let mut odd_commitment_agg: E::G1 = E::G1::generator() * E::Fr::ZERO; + let mut even_commitment_agg: E::G1 = E::G1::generator() * E::Fr::ZERO; + + let mut total_opening_agg: E::G1 = E::G1::generator() * E::Fr::ZERO; + let mut odd_opening_agg: E::G1 = E::G1::generator() * E::Fr::ZERO; + let mut even_opening_agg: E::G1 = E::G1::generator() * E::Fr::ZERO; + + let mut odd_eval_agg: E::Fr = E::Fr::ZERO; + let mut even_eval_agg: E::Fr = E::Fr::ZERO; + + alphas.iter().enumerate().for_each(|(i, alpha)| { + if i != 0 { + let local_com = coeff_form_uni_kzg_commit(srs, &local_coeffs); + folded_commitment_agg += local_com * combine_weight; + } + + let (evens, odds) = even_odd_coeffs_separate(&local_coeffs); + + let local_evens_com = coeff_form_uni_kzg_commit(srs, &evens); + even_commitment_agg += local_evens_com * combine_weight; + + let local_odds_com = coeff_form_uni_kzg_commit(srs, &odds); + odd_commitment_agg += local_odds_com * combine_weight; + + let evens_beta2_eval = univariate_evaluate(&evens, &power_series); + let local_even_opening = coeff_form_uni_kzg_open(srs, &evens, beta2, evens_beta2_eval); + even_opening_agg += local_even_opening * combine_weight; + even_eval_agg += evens_beta2_eval * combine_weight; + + let odds_beta2_eval = univariate_evaluate(&odds, &power_series); + let local_odd_opening = coeff_form_uni_kzg_open(srs, &odds, beta2, odds_beta2_eval); + odd_opening_agg += local_odd_opening * combine_weight; + odd_eval_agg += odds_beta2_eval * combine_weight; + + let current_eval = evens_beta2_eval + beta * odds_beta2_eval; + let local_total_opening = coeff_form_uni_kzg_open(srs, &local_coeffs, beta, current_eval); + total_opening_agg += local_total_opening * combine_weight; + + local_coeffs = merge_coeffs(evens, odds, *alpha); + combine_weight *= gamma; + }); + + todo!() +} diff --git a/poly_commit/src/kzg/structs.rs b/poly_commit/src/kzg/structs.rs index fa22f477..66100fc6 100644 --- a/poly_commit/src/kzg/structs.rs +++ b/poly_commit/src/kzg/structs.rs @@ -106,20 +106,6 @@ pub struct UniKZGVerifierParams { pub tau_g2: E::G2Affine, } -/// Univariate KZG commitment. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub struct UniKZGCommitment { - /// Univariate KZG commitment, polynomial evaluated at \tau times G1 generator. - pub com: E::G1Affine, -} - -/// Univariate KZG opening. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub struct UniKZGOpening { - /// Univariate KZG opening, (f(x) - y) / (x - \alpha) evaluated at \tau times G1 generator. - pub opening: E::G1Affine, -} - impl From<&CoefFormUniKZGSRS> for UniKZGVerifierParams { fn from(value: &CoefFormUniKZGSRS) -> Self { Self { diff --git a/poly_commit/src/kzg/tests.rs b/poly_commit/src/kzg/tests.rs index d7d7d261..6f38cfec 100644 --- a/poly_commit/src/kzg/tests.rs +++ b/poly_commit/src/kzg/tests.rs @@ -5,7 +5,9 @@ use halo2curves::{ use crate::{coeff_form_uni_kzg_verify, univariate_degree_one_quotient, UniKZGVerifierParams}; -use super::{coeff_form_uni_kzg_commit, coeff_form_uni_kzg_opening, generate_srs_for_testing}; +use super::{ + coeff_form_uni_kzg_commit, coeff_form_uni_kzg_open, generate_coef_form_uni_kzg_srs_for_testing, +}; #[test] fn test_univariate_degree_one_quotient() { @@ -87,14 +89,27 @@ fn test_coefficient_form_univariate_kzg_e2e() { Fr::from(28u64), Fr::from(1u64), ]; + let alpha = Fr::from(3u64); + let eval = Fr::from(604800u64); - let srs = generate_srs_for_testing::(8); + let srs = generate_coef_form_uni_kzg_srs_for_testing::(8); let vk: UniKZGVerifierParams = From::from(&srs); let com = coeff_form_uni_kzg_commit(&srs, &poly); + let opening = coeff_form_uni_kzg_open(&srs, &poly, alpha, eval); + assert!(coeff_form_uni_kzg_verify(vk, com, alpha, eval, opening)) +} + +#[test] +fn test_coefficient_form_univariate_kzg_constant_e2e() { + let poly = vec![Fr::from(100u64)]; let alpha = Fr::from(3u64); - let eval = Fr::from(604800u64); + let eval = Fr::from(100u64); + + let srs = generate_coef_form_uni_kzg_srs_for_testing::(8); + let vk: UniKZGVerifierParams = From::from(&srs); + let com = coeff_form_uni_kzg_commit(&srs, &poly); - let opening = coeff_form_uni_kzg_opening(&srs, &poly, alpha, eval); + let opening = coeff_form_uni_kzg_open(&srs, &poly, alpha, eval); assert!(coeff_form_uni_kzg_verify(vk, com, alpha, eval, opening)) } diff --git a/poly_commit/src/kzg/univariate.rs b/poly_commit/src/kzg/univariate.rs index 067e591e..56a375e7 100644 --- a/poly_commit/src/kzg/univariate.rs +++ b/poly_commit/src/kzg/univariate.rs @@ -9,9 +9,11 @@ use halo2curves::{ use crate::{powers_of_field_elements, univariate_degree_one_quotient}; -use super::{CoefFormUniKZGSRS, UniKZGCommitment, UniKZGOpening, UniKZGVerifierParams}; +use super::{CoefFormUniKZGSRS, UniKZGVerifierParams}; -pub fn generate_srs_for_testing(length: usize) -> CoefFormUniKZGSRS +pub fn generate_coef_form_uni_kzg_srs_for_testing( + length: usize, +) -> CoefFormUniKZGSRS where E::G1Affine: CurveAffine, { @@ -47,44 +49,38 @@ where pub fn coeff_form_uni_kzg_commit( srs: &CoefFormUniKZGSRS, coeffs: &[E::Fr], -) -> UniKZGCommitment +) -> E::G1 where E::G1Affine: CurveAffine, { - assert_eq!(srs.powers_of_tau.len(), coeffs.len()); + assert!(srs.powers_of_tau.len() >= coeffs.len()); - let com = best_multiexp(coeffs, srs.powers_of_tau.as_slice()); - - UniKZGCommitment { com: com.into() } + best_multiexp(coeffs, &srs.powers_of_tau[..coeffs.len()]) } -pub fn coeff_form_uni_kzg_opening( +pub fn coeff_form_uni_kzg_open( srs: &CoefFormUniKZGSRS, coeffs: &[E::Fr], alpha: E::Fr, eval: E::Fr, -) -> UniKZGOpening +) -> E::G1 where E::G1Affine: CurveAffine, { - assert_eq!(srs.powers_of_tau.len(), coeffs.len()); + assert!(srs.powers_of_tau.len() >= coeffs.len()); let (div, remainder) = univariate_degree_one_quotient(coeffs, alpha); assert_eq!(remainder, eval); - let opening = best_multiexp(&div, srs.powers_of_tau.as_slice()); - - UniKZGOpening { - opening: opening.into(), - } + best_multiexp(&div, &srs.powers_of_tau[..div.len()]) } pub fn coeff_form_uni_kzg_verify( vk: UniKZGVerifierParams, - comm: UniKZGCommitment, + comm: E::G1, alpha: E::Fr, eval: E::Fr, - opening: UniKZGOpening, + opening: E::G1, ) -> bool where E::G1Affine: CurveAffine, @@ -94,11 +90,11 @@ where let gt_result = E::multi_miller_loop(&[ ( - &opening.opening, + &opening.to_affine(), &(vk.tau_g2.to_curve() - g2_alpha).to_affine().into(), ), ( - &(g1_eval - comm.com).into(), + &(g1_eval - comm.to_affine()).into(), &E::G2Affine::generator().into(), ), ]); diff --git a/poly_commit/src/kzg/utils.rs b/poly_commit/src/kzg/utils.rs index ac827633..d1464532 100644 --- a/poly_commit/src/kzg/utils.rs +++ b/poly_commit/src/kzg/utils.rs @@ -1,4 +1,5 @@ use halo2curves::ff::{Field, PrimeField}; +use itertools::izip; pub fn primitive_root_of_unity(group_size: usize) -> F { let omega = F::ROOT_OF_UNITY; @@ -39,3 +40,29 @@ pub(crate) fn univariate_degree_one_quotient(coeffs: &[F], alpha: F) - (final_div_coeffs, final_remainder) } + +pub(crate) fn univariate_evaluate(coeffs: &[F], power_series: &[F]) -> F { + assert!(coeffs.len() <= power_series.len()); + + izip!(coeffs, power_series).map(|(c, p)| *c * *p).sum() +} + +pub(crate) fn even_odd_coeffs_separate(coeffs: &[F]) -> (Vec, Vec) { + assert!(coeffs.len().is_power_of_two()); + + let mut even = Vec::with_capacity(coeffs.len() >> 1); + let mut odd = Vec::with_capacity(coeffs.len() >> 1); + + coeffs.chunks(2).for_each(|eo: &[F]| { + even.push(eo[0]); + odd.push(eo[1]); + }); + + (even, odd) +} + +pub(crate) fn merge_coeffs(evens: Vec, odds: Vec, alpha: F) -> Vec { + assert_eq!(evens.len(), odds.len()); + + izip!(&evens, &odds).map(|(e, o)| *e + alpha * *o).collect() +} From 2b98eda70f3c04446277b0ddf8071832920a90de Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 30 Jan 2025 22:27:39 -0500 Subject: [PATCH 05/10] sketch of the hyper kzg from univariate kzg --- poly_commit/src/kzg/hyperkzg.rs | 181 ++++++++++++++++++++++++-------- poly_commit/src/kzg/structs.rs | 10 ++ poly_commit/src/kzg/utils.rs | 6 -- 3 files changed, 147 insertions(+), 50 deletions(-) diff --git a/poly_commit/src/kzg/hyperkzg.rs b/poly_commit/src/kzg/hyperkzg.rs index df7f394e..6ffb95ff 100644 --- a/poly_commit/src/kzg/hyperkzg.rs +++ b/poly_commit/src/kzg/hyperkzg.rs @@ -1,73 +1,166 @@ +use std::iter; + use arith::ExtensionField; -use halo2curves::{ff::Field, group::Group, pairing::MultiMillerLoop, CurveAffine}; +use halo2curves::{ff::Field, group::GroupEncoding, pairing::MultiMillerLoop, CurveAffine}; +use itertools::izip; use transcript::Transcript; use crate::{ - coeff_form_uni_kzg_commit, coeff_form_uni_kzg_open, even_odd_coeffs_separate, merge_coeffs, - powers_of_field_elements, univariate_evaluate, + coeff_form_uni_kzg_commit, even_odd_coeffs_separate, powers_of_field_elements, + univariate_degree_one_quotient, univariate_evaluate, }; -use super::CoefFormUniKZGSRS; +use super::{CoefFormUniKZGSRS, HyperKZGOpening}; pub fn coeff_form_uni_hyperkzg_open>( srs: &CoefFormUniKZGSRS, - coeffs: &[E::Fr], + coeffs: &Vec, alphas: &[E::Fr], _eval: E::Fr, - beta: E::Fr, - gamma: E::Fr, - _transcript: &mut T, -) where + fs_transcript: &mut T, +) -> HyperKZGOpening +where E::G1Affine: CurveAffine, E::Fr: ExtensionField, { - let beta2 = beta * beta; - let power_series = powers_of_field_elements(&beta2, coeffs.len() >> 1); let mut local_coeffs = coeffs.to_vec(); - let mut combine_weight = E::Fr::ONE; - let mut folded_commitment_agg: E::G1 = E::G1::generator() * E::Fr::ZERO; - let mut odd_commitment_agg: E::G1 = E::G1::generator() * E::Fr::ZERO; - let mut even_commitment_agg: E::G1 = E::G1::generator() * E::Fr::ZERO; + let (folded_oracle_commits, folded_oracle_coeffs): (Vec, Vec>) = alphas + [..alphas.len() - 1] + .iter() + .map(|alpha| { + let (evens, odds) = even_odd_coeffs_separate(&local_coeffs); + + local_coeffs = izip!(&evens, &odds) + .map(|(e, o)| (E::Fr::ONE - alpha) * e + *alpha * *o) + .collect(); + + let folded_oracle_commit = coeff_form_uni_kzg_commit(srs, &local_coeffs); + + fs_transcript.append_u8_slice(folded_oracle_commit.to_bytes().as_ref()); + + (folded_oracle_commit, local_coeffs.clone()) + }) + .unzip(); + + let beta = fs_transcript.generate_challenge_field_element(); + let beta2 = beta * beta; + let beta_inv = beta.invert().unwrap(); + let two_inv = E::Fr::ONE.double().invert().unwrap(); + let beta_pow_series = powers_of_field_elements(&beta, coeffs.len()); + let neg_beta_pow_series = powers_of_field_elements(&(-beta), coeffs.len()); + + let beta2_eval = { + let beta2_pow_series = powers_of_field_elements(&beta2, coeffs.len()); + univariate_evaluate(coeffs, &beta2_pow_series) + }; - let mut total_opening_agg: E::G1 = E::G1::generator() * E::Fr::ZERO; - let mut odd_opening_agg: E::G1 = E::G1::generator() * E::Fr::ZERO; - let mut even_opening_agg: E::G1 = E::G1::generator() * E::Fr::ZERO; + fs_transcript.append_field_element(&beta2_eval); + let mut beta2_evals = vec![beta2_eval]; - let mut odd_eval_agg: E::Fr = E::Fr::ZERO; - let mut even_eval_agg: E::Fr = E::Fr::ZERO; + let (beta_evals, neg_beta_evals): (Vec, Vec) = izip!( + iter::once(coeffs).chain(folded_oracle_coeffs.iter()), + alphas + ) + .enumerate() + .map(|(i, (cs, alpha))| { + let beta_eval = univariate_evaluate(cs, &beta_pow_series); + let neg_beta_eval = univariate_evaluate(cs, &neg_beta_pow_series); - alphas.iter().enumerate().for_each(|(i, alpha)| { - if i != 0 { - let local_com = coeff_form_uni_kzg_commit(srs, &local_coeffs); - folded_commitment_agg += local_com * combine_weight; + if i < alphas.len() - 1 { + let beta2_eval = (beta_eval + neg_beta_eval) * two_inv * (E::Fr::ONE - alpha) + + (beta_eval - neg_beta_eval) * two_inv * beta_inv * alpha; + + beta2_evals.push(beta2_eval); } - let (evens, odds) = even_odd_coeffs_separate(&local_coeffs); + fs_transcript.append_field_element(&beta_eval); + fs_transcript.append_field_element(&neg_beta_eval); + + (beta_eval, neg_beta_eval) + }) + .unzip(); + + let gamma = fs_transcript.generate_challenge_field_element(); + let gamma_pow_series = powers_of_field_elements(&gamma, alphas.len()); + let v_beta = univariate_evaluate(&beta_evals, &gamma_pow_series); + let v_neg_beta = univariate_evaluate(&neg_beta_evals, &gamma_pow_series); + let v_beta2 = univariate_evaluate(&beta2_evals, &gamma_pow_series); + let f_gamma = { + let mut f = coeffs.clone(); + izip!(&gamma_pow_series[1..], &folded_oracle_coeffs).for_each(|(gamma_i, folded_f)| { + izip!(&mut f, folded_f).for_each(|(f_i, folded_f_i)| { + *f_i += *folded_f_i * *gamma_i; + }); + }); + f + }; + let lagrange_degree2: Vec = { + let l_beta_nom = [-beta2 * beta, -beta2 + beta, E::Fr::ONE]; + let l_beta_denom_inv: E::Fr = ((beta - beta2) * (beta + beta)).invert().unwrap(); + let l_beta: Vec = l_beta_nom.iter().map(|i| *i * l_beta_denom_inv).collect(); + + let l_neg_beta_norm = [beta2 * beta, -beta2 - beta, E::Fr::ONE]; + let l_neg_beta_denom_inv: E::Fr = ((beta2 - beta) * (beta2 + beta)).invert().unwrap(); + let l_neg_beta: Vec = l_neg_beta_norm + .iter() + .map(|i| *i * l_neg_beta_denom_inv) + .collect(); + + let l_beta2_norm = [-beta2, E::Fr::ZERO, E::Fr::ONE]; + let l_beta2_denom_inv: E::Fr = ((beta + beta2) * (beta + beta)).invert().unwrap(); + let l_beta2: Vec = l_beta2_norm + .iter() + .map(|i| *i * l_beta2_denom_inv) + .collect(); + + izip!(l_beta, l_neg_beta, l_beta2) + .map(|(l_beta_i, l_neg_beta_i, l_beta2_i)| { + l_beta_i * v_beta + l_neg_beta_i * v_neg_beta + l_beta2_i * v_beta2 + }) + .collect() + }; + let f_gamma_quotient = { + let mut nom = f_gamma.clone(); + izip!(&mut nom, &lagrange_degree2).for_each(|(n, l)| *n -= l); + + let (nom_1, remainder_1) = univariate_degree_one_quotient(&nom, beta); + assert_eq!(remainder_1, E::Fr::ZERO); + + let (nom_2, remainder_2) = univariate_degree_one_quotient(&nom_1, beta2); + assert_eq!(remainder_2, E::Fr::ZERO); - let local_evens_com = coeff_form_uni_kzg_commit(srs, &evens); - even_commitment_agg += local_evens_com * combine_weight; + let (nom_3, remainder_3) = univariate_degree_one_quotient(&nom_2, -beta); + assert_eq!(remainder_3, E::Fr::ZERO); - let local_odds_com = coeff_form_uni_kzg_commit(srs, &odds); - odd_commitment_agg += local_odds_com * combine_weight; + nom_3 + }; + let f_gamma_quotient_com = coeff_form_uni_kzg_commit(srs, &f_gamma_quotient); + fs_transcript.append_u8_slice(f_gamma_quotient_com.to_bytes().as_ref()); - let evens_beta2_eval = univariate_evaluate(&evens, &power_series); - let local_even_opening = coeff_form_uni_kzg_open(srs, &evens, beta2, evens_beta2_eval); - even_opening_agg += local_even_opening * combine_weight; - even_eval_agg += evens_beta2_eval * combine_weight; + let tau = fs_transcript.generate_challenge_field_element(); + let vanishing_at_tau = { + let f_gamma_denom = (tau - beta) * (tau + beta) * (tau - beta2); + let lagrange_degree2_at_tau = + lagrange_degree2[0] + lagrange_degree2[1] * tau + lagrange_degree2[2] * tau * tau; - let odds_beta2_eval = univariate_evaluate(&odds, &power_series); - let local_odd_opening = coeff_form_uni_kzg_open(srs, &odds, beta2, odds_beta2_eval); - odd_opening_agg += local_odd_opening * combine_weight; - odd_eval_agg += odds_beta2_eval * combine_weight; + let mut poly = f_gamma.clone(); + poly[0] -= lagrange_degree2_at_tau; + izip!(&mut poly, &f_gamma_quotient).for_each(|(p, f)| *p -= *f * f_gamma_denom); - let current_eval = evens_beta2_eval + beta * odds_beta2_eval; - let local_total_opening = coeff_form_uni_kzg_open(srs, &local_coeffs, beta, current_eval); - total_opening_agg += local_total_opening * combine_weight; + let (quotient, remainder) = univariate_degree_one_quotient(&poly, tau); + assert_eq!(remainder, E::Fr::ZERO); - local_coeffs = merge_coeffs(evens, odds, *alpha); - combine_weight *= gamma; - }); + quotient + }; + let vanishing_at_tau_commitment = coeff_form_uni_kzg_commit(srs, &vanishing_at_tau); - todo!() + HyperKZGOpening { + folded_oracle_commitments: folded_oracle_commits, + f_beta2: beta2_eval, + evals_at_beta: beta_evals, + evals_at_neg_beta: neg_beta_evals, + beta_commitment: f_gamma_quotient_com, + tau_vanishing_commitment: vanishing_at_tau_commitment, + } } diff --git a/poly_commit/src/kzg/structs.rs b/poly_commit/src/kzg/structs.rs index 66100fc6..16c705cc 100644 --- a/poly_commit/src/kzg/structs.rs +++ b/poly_commit/src/kzg/structs.rs @@ -113,3 +113,13 @@ impl From<&CoefFormUniKZGSRS> for UniKZGVerifierParams { } } } + +#[derive(Debug, Default)] +pub struct HyperKZGOpening { + pub folded_oracle_commitments: Vec, + pub f_beta2: E::Fr, + pub evals_at_beta: Vec, + pub evals_at_neg_beta: Vec, + pub beta_commitment: E::G1, + pub tau_vanishing_commitment: E::G1, +} diff --git a/poly_commit/src/kzg/utils.rs b/poly_commit/src/kzg/utils.rs index d1464532..13d49ccb 100644 --- a/poly_commit/src/kzg/utils.rs +++ b/poly_commit/src/kzg/utils.rs @@ -60,9 +60,3 @@ pub(crate) fn even_odd_coeffs_separate(coeffs: &[F]) -> (Vec, Vec(evens: Vec, odds: Vec, alpha: F) -> Vec { - assert_eq!(evens.len(), odds.len()); - - izip!(&evens, &odds).map(|(e, o)| *e + alpha * *o).collect() -} From 3e007b87da33bc1917ea4b3a200347ccc4417ba6 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Fri, 31 Jan 2025 13:15:32 -0500 Subject: [PATCH 06/10] hyperkzg univariate prototype --- Cargo.lock | 1 + poly_commit/Cargo.toml | 1 + poly_commit/src/kzg/hyperkzg.rs | 138 +++++++++++++++++++++++++------- poly_commit/src/kzg/tests.rs | 46 ++++++++++- poly_commit/src/kzg/utils.rs | 2 - 5 files changed, 158 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 18da3b5c..c0d5dfb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1830,6 +1830,7 @@ dependencies = [ "ark-std", "criterion", "ethnum", + "field_hashers", "gf2", "gf2_128", "gkr_field_config", diff --git a/poly_commit/Cargo.toml b/poly_commit/Cargo.toml index 26d17591..92f43d47 100644 --- a/poly_commit/Cargo.toml +++ b/poly_commit/Cargo.toml @@ -22,6 +22,7 @@ halo2curves.workspace = true [dev-dependencies] gf2_128 = { path = "../arith/gf2_128" } mersenne31 = { path = "../arith/mersenne31" } +field_hashers = { path = "../arith/field_hashers" } tynm.workspace = true criterion.workspace = true diff --git a/poly_commit/src/kzg/hyperkzg.rs b/poly_commit/src/kzg/hyperkzg.rs index 6ffb95ff..dee29d8a 100644 --- a/poly_commit/src/kzg/hyperkzg.rs +++ b/poly_commit/src/kzg/hyperkzg.rs @@ -10,13 +10,38 @@ use crate::{ univariate_degree_one_quotient, univariate_evaluate, }; -use super::{CoefFormUniKZGSRS, HyperKZGOpening}; +use super::{coeff_form_uni_kzg_verify, CoefFormUniKZGSRS, HyperKZGOpening, UniKZGVerifierParams}; + +fn coeff_form_degree2_lagrange(roots: [F; 3], evals: [F; 3]) -> [F; 3] { + let [r0, r1, r2] = roots; + let [e0, e1, e2] = evals; + + let r0_nom = [r1 * r2, -r1 - r2, F::ONE]; + let r0_denom_inv = ((r0 - r1) * (r0 - r2)).invert().unwrap(); + let r0_weight = r0_denom_inv * e0; + + let r1_nom = [r0 * r2, -r0 - r2, F::ONE]; + let r1_denom_inv = ((r1 - r0) * (r1 - r2)).invert().unwrap(); + let r1_weight = r1_denom_inv * e1; + + let r2_nom = [r0 * r1, -r0 - r1, F::ONE]; + let r2_denom_inv = ((r2 - r0) * (r2 - r1)).invert().unwrap(); + let r2_weight = r2_denom_inv * e2; + + let combine = |a, b, c| a * r0_weight + b * r1_weight + c * r2_weight; + + [ + combine(r0_nom[0], r1_nom[0], r2_nom[0]), + combine(r0_nom[1], r1_nom[1], r2_nom[1]), + combine(r0_nom[2], r1_nom[2], r2_nom[2]), + ] +} pub fn coeff_form_uni_hyperkzg_open>( srs: &CoefFormUniKZGSRS, coeffs: &Vec, alphas: &[E::Fr], - _eval: E::Fr, + eval: E::Fr, fs_transcript: &mut T, ) -> HyperKZGOpening where @@ -43,6 +68,16 @@ where }) .unzip(); + { + let degree_1 = &folded_oracle_coeffs[folded_oracle_coeffs.len() - 1]; + assert_eq!(degree_1.len(), 2); + let last_alpha = alphas[alphas.len() - 1]; + assert_eq!( + degree_1[0] * (E::Fr::ONE - last_alpha) + degree_1[1] * last_alpha, + eval + ); + } + let beta = fs_transcript.generate_challenge_field_element(); let beta2 = beta * beta; let beta_inv = beta.invert().unwrap(); @@ -95,31 +130,9 @@ where }); f }; - let lagrange_degree2: Vec = { - let l_beta_nom = [-beta2 * beta, -beta2 + beta, E::Fr::ONE]; - let l_beta_denom_inv: E::Fr = ((beta - beta2) * (beta + beta)).invert().unwrap(); - let l_beta: Vec = l_beta_nom.iter().map(|i| *i * l_beta_denom_inv).collect(); - - let l_neg_beta_norm = [beta2 * beta, -beta2 - beta, E::Fr::ONE]; - let l_neg_beta_denom_inv: E::Fr = ((beta2 - beta) * (beta2 + beta)).invert().unwrap(); - let l_neg_beta: Vec = l_neg_beta_norm - .iter() - .map(|i| *i * l_neg_beta_denom_inv) - .collect(); - - let l_beta2_norm = [-beta2, E::Fr::ZERO, E::Fr::ONE]; - let l_beta2_denom_inv: E::Fr = ((beta + beta2) * (beta + beta)).invert().unwrap(); - let l_beta2: Vec = l_beta2_norm - .iter() - .map(|i| *i * l_beta2_denom_inv) - .collect(); - - izip!(l_beta, l_neg_beta, l_beta2) - .map(|(l_beta_i, l_neg_beta_i, l_beta2_i)| { - l_beta_i * v_beta + l_neg_beta_i * v_neg_beta + l_beta2_i * v_beta2 - }) - .collect() - }; + + let lagrange_degree2 = + coeff_form_degree2_lagrange([beta, -beta, beta2], [v_beta, v_neg_beta, v_beta2]); let f_gamma_quotient = { let mut nom = f_gamma.clone(); izip!(&mut nom, &lagrange_degree2).for_each(|(n, l)| *n -= l); @@ -164,3 +177,74 @@ where tau_vanishing_commitment: vanishing_at_tau_commitment, } } + +pub fn coeff_form_uni_hyperkzg_verify>( + vk: UniKZGVerifierParams, + comm: E::G1, + alphas: &[E::Fr], + eval: E::Fr, + opening: &HyperKZGOpening, + fs_transcript: &mut T, +) -> bool +where + E::G1Affine: CurveAffine, + E::Fr: ExtensionField, +{ + opening + .folded_oracle_commitments + .iter() + .for_each(|f| fs_transcript.append_u8_slice(f.to_bytes().as_ref())); + + let beta = fs_transcript.generate_challenge_field_element(); + let beta2 = beta * beta; + let beta_inv = beta.invert().unwrap(); + let two_inv = E::Fr::ONE.double().invert().unwrap(); + + fs_transcript.append_field_element(&opening.f_beta2); + let mut beta2_evals = vec![opening.f_beta2]; + izip!(&opening.evals_at_beta, &opening.evals_at_neg_beta, alphas).for_each( + |(beta_eval, neg_beta_eval, alpha)| { + let beta2_eval = (*beta_eval + *neg_beta_eval) * two_inv * (E::Fr::ONE - alpha) + + (*beta_eval - *neg_beta_eval) * two_inv * beta_inv * alpha; + + beta2_evals.push(beta2_eval); + + fs_transcript.append_field_element(beta_eval); + fs_transcript.append_field_element(neg_beta_eval); + }, + ); + + if beta2_evals[beta2_evals.len() - 1] != eval { + return false; + } + + let gamma = fs_transcript.generate_challenge_field_element(); + let gamma_pow_series = powers_of_field_elements(&gamma, alphas.len()); + let v_beta = univariate_evaluate(&opening.evals_at_beta, &gamma_pow_series); + let v_neg_beta = univariate_evaluate(&opening.evals_at_neg_beta, &gamma_pow_series); + let v_beta2 = univariate_evaluate(&beta2_evals, &gamma_pow_series); + let lagrange_degree2 = + coeff_form_degree2_lagrange([beta, -beta, beta2], [v_beta, v_neg_beta, v_beta2]); + + let commitment_agg: E::G1 = izip!( + iter::once(&comm).chain(opening.folded_oracle_commitments.iter()), + gamma_pow_series + ) + .map(|(c, a)| *c * a) + .sum(); + + fs_transcript.append_u8_slice(opening.beta_commitment.to_bytes().as_ref()); + let tau = fs_transcript.generate_challenge_field_element(); + + let q_weight = (tau - beta) * (tau - beta2) * (tau + beta); + let lagrange_eval = + lagrange_degree2[0] + lagrange_degree2[1] * tau + lagrange_degree2[2] * tau * tau; + + coeff_form_uni_kzg_verify( + vk, + commitment_agg - opening.beta_commitment * q_weight, + tau, + lagrange_eval, + opening.tau_vanishing_commitment, + ) +} diff --git a/poly_commit/src/kzg/tests.rs b/poly_commit/src/kzg/tests.rs index 6f38cfec..c1e8e484 100644 --- a/poly_commit/src/kzg/tests.rs +++ b/poly_commit/src/kzg/tests.rs @@ -1,9 +1,16 @@ +use arith::BN254Fr; +use field_hashers::MiMC5FiatShamirHasher; use halo2curves::{ bn256::{Bn256, Fr}, ff::Field, }; +use polynomials::MultiLinearPoly; +use transcript::{FieldHashTranscript, Transcript}; -use crate::{coeff_form_uni_kzg_verify, univariate_degree_one_quotient, UniKZGVerifierParams}; +use crate::{ + coeff_form_uni_hyperkzg_open, coeff_form_uni_hyperkzg_verify, coeff_form_uni_kzg_verify, + univariate_degree_one_quotient, UniKZGVerifierParams, +}; use super::{ coeff_form_uni_kzg_commit, coeff_form_uni_kzg_open, generate_coef_form_uni_kzg_srs_for_testing, @@ -113,3 +120,40 @@ fn test_coefficient_form_univariate_kzg_constant_e2e() { let opening = coeff_form_uni_kzg_open(&srs, &poly, alpha, eval); assert!(coeff_form_uni_kzg_verify(vk, com, alpha, eval, opening)) } + +#[test] +fn test_hyperkzg_e2e() { + let multilinear = MultiLinearPoly::new(vec![ + Fr::from(1u64), + Fr::from(2u64), + Fr::from(3u64), + Fr::from(4u64), + Fr::from(5u64), + Fr::from(6u64), + Fr::from(7u64), + Fr::from(8u64), + ]); + let alphas = vec![Fr::from(2u64), Fr::from(4u64), Fr::from(8u64)]; + let eval = multilinear.evaluate_jolt(&alphas); + + dbg!(eval); + + let srs = generate_coef_form_uni_kzg_srs_for_testing::(8); + let vk: UniKZGVerifierParams = From::from(&srs); + let com = coeff_form_uni_kzg_commit(&srs, &multilinear.coeffs); + let mut fs_transcript = FieldHashTranscript::>::new(); + + let opening = + coeff_form_uni_hyperkzg_open(&srs, &multilinear.coeffs, &alphas, eval, &mut fs_transcript); + + dbg!(&opening); + + assert!(coeff_form_uni_hyperkzg_verify( + vk, + com, + &alphas, + eval, + &opening, + &mut fs_transcript + )) +} diff --git a/poly_commit/src/kzg/utils.rs b/poly_commit/src/kzg/utils.rs index 13d49ccb..ce6242d0 100644 --- a/poly_commit/src/kzg/utils.rs +++ b/poly_commit/src/kzg/utils.rs @@ -42,8 +42,6 @@ pub(crate) fn univariate_degree_one_quotient(coeffs: &[F], alpha: F) - } pub(crate) fn univariate_evaluate(coeffs: &[F], power_series: &[F]) -> F { - assert!(coeffs.len() <= power_series.len()); - izip!(coeffs, power_series).map(|(c, p)| *c * *p).sum() } From 0437a3ee0d58521fa145e31f35ee89a458f68204 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Fri, 31 Jan 2025 22:10:20 -0500 Subject: [PATCH 07/10] minor, update testings --- poly_commit/src/kzg/hyperkzg.rs | 75 ++++++++++----------------------- poly_commit/src/kzg/tests.rs | 53 +++++++++++------------ poly_commit/src/kzg/utils.rs | 34 ++++++++++++++- 3 files changed, 80 insertions(+), 82 deletions(-) diff --git a/poly_commit/src/kzg/hyperkzg.rs b/poly_commit/src/kzg/hyperkzg.rs index dee29d8a..16532406 100644 --- a/poly_commit/src/kzg/hyperkzg.rs +++ b/poly_commit/src/kzg/hyperkzg.rs @@ -6,12 +6,16 @@ use itertools::izip; use transcript::Transcript; use crate::{ - coeff_form_uni_kzg_commit, even_odd_coeffs_separate, powers_of_field_elements, - univariate_degree_one_quotient, univariate_evaluate, + coeff_form_uni_kzg_commit, even_odd_coeffs_separate, polynomial_add, powers_of_field_elements, + univariate_evaluate, }; -use super::{coeff_form_uni_kzg_verify, CoefFormUniKZGSRS, HyperKZGOpening, UniKZGVerifierParams}; +use super::{ + coeff_form_uni_kzg_verify, univariate_roots_quotient, CoefFormUniKZGSRS, HyperKZGOpening, + UniKZGVerifierParams, +}; +#[inline(always)] fn coeff_form_degree2_lagrange(roots: [F; 3], evals: [F; 3]) -> [F; 3] { let [r0, r1, r2] = roots; let [e0, e1, e2] = evals; @@ -41,7 +45,6 @@ pub fn coeff_form_uni_hyperkzg_open>( srs: &CoefFormUniKZGSRS, coeffs: &Vec, alphas: &[E::Fr], - eval: E::Fr, fs_transcript: &mut T, ) -> HyperKZGOpening where @@ -68,16 +71,6 @@ where }) .unzip(); - { - let degree_1 = &folded_oracle_coeffs[folded_oracle_coeffs.len() - 1]; - assert_eq!(degree_1.len(), 2); - let last_alpha = alphas[alphas.len() - 1]; - assert_eq!( - degree_1[0] * (E::Fr::ONE - last_alpha) + degree_1[1] * last_alpha, - eval - ); - } - let beta = fs_transcript.generate_challenge_field_element(); let beta2 = beta * beta; let beta_inv = beta.invert().unwrap(); @@ -97,17 +90,15 @@ where iter::once(coeffs).chain(folded_oracle_coeffs.iter()), alphas ) - .enumerate() - .map(|(i, (cs, alpha))| { + .map(|(cs, alpha)| { let beta_eval = univariate_evaluate(cs, &beta_pow_series); let neg_beta_eval = univariate_evaluate(cs, &neg_beta_pow_series); - if i < alphas.len() - 1 { - let beta2_eval = (beta_eval + neg_beta_eval) * two_inv * (E::Fr::ONE - alpha) - + (beta_eval - neg_beta_eval) * two_inv * beta_inv * alpha; + let beta2_eval = two_inv + * ((beta_eval + neg_beta_eval) * (E::Fr::ONE - alpha) + + (beta_eval - neg_beta_eval) * beta_inv * alpha); - beta2_evals.push(beta2_eval); - } + beta2_evals.push(beta2_eval); fs_transcript.append_field_element(&beta_eval); fs_transcript.append_field_element(&neg_beta_eval); @@ -123,11 +114,8 @@ where let v_beta2 = univariate_evaluate(&beta2_evals, &gamma_pow_series); let f_gamma = { let mut f = coeffs.clone(); - izip!(&gamma_pow_series[1..], &folded_oracle_coeffs).for_each(|(gamma_i, folded_f)| { - izip!(&mut f, folded_f).for_each(|(f_i, folded_f_i)| { - *f_i += *folded_f_i * *gamma_i; - }); - }); + izip!(&gamma_pow_series[1..], &folded_oracle_coeffs) + .for_each(|(gamma_i, folded_f)| polynomial_add(&mut f, *gamma_i, folded_f)); f }; @@ -135,18 +123,8 @@ where coeff_form_degree2_lagrange([beta, -beta, beta2], [v_beta, v_neg_beta, v_beta2]); let f_gamma_quotient = { let mut nom = f_gamma.clone(); - izip!(&mut nom, &lagrange_degree2).for_each(|(n, l)| *n -= l); - - let (nom_1, remainder_1) = univariate_degree_one_quotient(&nom, beta); - assert_eq!(remainder_1, E::Fr::ZERO); - - let (nom_2, remainder_2) = univariate_degree_one_quotient(&nom_1, beta2); - assert_eq!(remainder_2, E::Fr::ZERO); - - let (nom_3, remainder_3) = univariate_degree_one_quotient(&nom_2, -beta); - assert_eq!(remainder_3, E::Fr::ZERO); - - nom_3 + polynomial_add(&mut nom, -E::Fr::ONE, &lagrange_degree2); + univariate_roots_quotient(nom, &[beta, beta2, -beta]) }; let f_gamma_quotient_com = coeff_form_uni_kzg_commit(srs, &f_gamma_quotient); fs_transcript.append_u8_slice(f_gamma_quotient_com.to_bytes().as_ref()); @@ -159,12 +137,8 @@ where let mut poly = f_gamma.clone(); poly[0] -= lagrange_degree2_at_tau; - izip!(&mut poly, &f_gamma_quotient).for_each(|(p, f)| *p -= *f * f_gamma_denom); - - let (quotient, remainder) = univariate_degree_one_quotient(&poly, tau); - assert_eq!(remainder, E::Fr::ZERO); - - quotient + polynomial_add(&mut poly, -f_gamma_denom, &f_gamma_quotient); + univariate_roots_quotient(poly, &[tau]) }; let vanishing_at_tau_commitment = coeff_form_uni_kzg_commit(srs, &vanishing_at_tau); @@ -204,8 +178,9 @@ where let mut beta2_evals = vec![opening.f_beta2]; izip!(&opening.evals_at_beta, &opening.evals_at_neg_beta, alphas).for_each( |(beta_eval, neg_beta_eval, alpha)| { - let beta2_eval = (*beta_eval + *neg_beta_eval) * two_inv * (E::Fr::ONE - alpha) - + (*beta_eval - *neg_beta_eval) * two_inv * beta_inv * alpha; + let beta2_eval = two_inv + * ((*beta_eval + *neg_beta_eval) * (E::Fr::ONE - alpha) + + (*beta_eval - *neg_beta_eval) * beta_inv * alpha); beta2_evals.push(beta2_eval); @@ -226,12 +201,8 @@ where let lagrange_degree2 = coeff_form_degree2_lagrange([beta, -beta, beta2], [v_beta, v_neg_beta, v_beta2]); - let commitment_agg: E::G1 = izip!( - iter::once(&comm).chain(opening.folded_oracle_commitments.iter()), - gamma_pow_series - ) - .map(|(c, a)| *c * a) - .sum(); + let commitment_agg: E::G1 = + comm + univariate_evaluate(&opening.folded_oracle_commitments, &gamma_pow_series[1..]); fs_transcript.append_u8_slice(opening.beta_commitment.to_bytes().as_ref()); let tau = fs_transcript.generate_challenge_field_element(); diff --git a/poly_commit/src/kzg/tests.rs b/poly_commit/src/kzg/tests.rs index c1e8e484..50f9cbbe 100644 --- a/poly_commit/src/kzg/tests.rs +++ b/poly_commit/src/kzg/tests.rs @@ -1,4 +1,5 @@ use arith::BN254Fr; +use ark_std::test_rng; use field_hashers::MiMC5FiatShamirHasher; use halo2curves::{ bn256::{Bn256, Fr}, @@ -123,37 +124,31 @@ fn test_coefficient_form_univariate_kzg_constant_e2e() { #[test] fn test_hyperkzg_e2e() { - let multilinear = MultiLinearPoly::new(vec![ - Fr::from(1u64), - Fr::from(2u64), - Fr::from(3u64), - Fr::from(4u64), - Fr::from(5u64), - Fr::from(6u64), - Fr::from(7u64), - Fr::from(8u64), - ]); - let alphas = vec![Fr::from(2u64), Fr::from(4u64), Fr::from(8u64)]; - let eval = multilinear.evaluate_jolt(&alphas); - - dbg!(eval); + let mut rng = test_rng(); + let max_vars = 15; + let max_length = 1 << max_vars; - let srs = generate_coef_form_uni_kzg_srs_for_testing::(8); - let vk: UniKZGVerifierParams = From::from(&srs); - let com = coeff_form_uni_kzg_commit(&srs, &multilinear.coeffs); - let mut fs_transcript = FieldHashTranscript::>::new(); + let srs = generate_coef_form_uni_kzg_srs_for_testing::(max_length); + (2..max_vars).for_each(|vars| { + let multilinear = MultiLinearPoly::random(vars, &mut rng); + let alphas: Vec = (0..vars).map(|_| Fr::random(&mut rng)).collect(); + let eval = multilinear.evaluate_jolt(&alphas); - let opening = - coeff_form_uni_hyperkzg_open(&srs, &multilinear.coeffs, &alphas, eval, &mut fs_transcript); + let vk: UniKZGVerifierParams = From::from(&srs); + let com = coeff_form_uni_kzg_commit(&srs, &multilinear.coeffs); + let mut fs_transcript = + FieldHashTranscript::>::new(); - dbg!(&opening); + let opening = + coeff_form_uni_hyperkzg_open(&srs, &multilinear.coeffs, &alphas, &mut fs_transcript); - assert!(coeff_form_uni_hyperkzg_verify( - vk, - com, - &alphas, - eval, - &opening, - &mut fs_transcript - )) + assert!(coeff_form_uni_hyperkzg_verify( + vk, + com, + &alphas, + eval, + &opening, + &mut fs_transcript + )) + }); } diff --git a/poly_commit/src/kzg/utils.rs b/poly_commit/src/kzg/utils.rs index ce6242d0..6497fad0 100644 --- a/poly_commit/src/kzg/utils.rs +++ b/poly_commit/src/kzg/utils.rs @@ -1,3 +1,5 @@ +use std::{iter::Sum, ops::Mul}; + use halo2curves::ff::{Field, PrimeField}; use itertools::izip; @@ -11,6 +13,7 @@ pub fn primitive_root_of_unity(group_size: usize) -> F { omega } +#[inline(always)] pub(crate) fn powers_of_field_elements(x: &F, n: usize) -> Vec { let mut powers = vec![F::ONE]; let mut cur = *x; @@ -23,6 +26,7 @@ pub(crate) fn powers_of_field_elements(x: &F, n: usize) -> Vec { /// Given a univariate polynomial of coefficient form f(X) = c0 + c1 X + ... + cn X^n /// and perform the division f(X) / (X - \alpha). +#[inline(always)] pub(crate) fn univariate_degree_one_quotient(coeffs: &[F], alpha: F) -> (Vec, F) { let mut div_coeffs = coeffs.to_vec(); @@ -41,7 +45,28 @@ pub(crate) fn univariate_degree_one_quotient(coeffs: &[F], alpha: F) - (final_div_coeffs, final_remainder) } -pub(crate) fn univariate_evaluate(coeffs: &[F], power_series: &[F]) -> F { +#[inline(always)] +pub(crate) fn univariate_roots_quotient(mut coeffs: Vec, roots: &[F]) -> Vec { + roots.iter().enumerate().for_each(|(ith_root, r)| { + for i in ((1 + ith_root)..coeffs.len()).rev() { + // c X^n = c X^n - c \alpha X^(n - 1) + c \alpha X^(n - 1) + // = c (X - \alpha) X^(n - 1) + c \alpha X^(n - 1) + + let remainder = coeffs[i] * r; + coeffs[i - 1] += remainder; + } + + assert_eq!(coeffs[ith_root], F::ZERO); + }); + + coeffs[roots.len()..].to_owned() +} + +#[inline(always)] +pub(crate) fn univariate_evaluate + Sum + Copy, F1: Field>( + coeffs: &[F], + power_series: &[F1], +) -> F { izip!(coeffs, power_series).map(|(c, p)| *c * *p).sum() } @@ -58,3 +83,10 @@ pub(crate) fn even_odd_coeffs_separate(coeffs: &[F]) -> (Vec, Vec(coeffs: &mut [F], weight: F, another_coeffs: &[F]) { + assert!(coeffs.len() >= another_coeffs.len()); + + izip!(coeffs, another_coeffs).for_each(|(c, a)| *c += weight * *a); +} From 04aa66e82d50797368a87aedb67d89cf9dc0abc0 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Sat, 1 Feb 2025 12:37:42 -0500 Subject: [PATCH 08/10] minor - renaming stuffs --- poly_commit/src/kzg/hyperkzg.rs | 20 ++++++-------------- poly_commit/src/kzg/univariate.rs | 4 ++-- poly_commit/src/kzg/utils.rs | 2 +- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/poly_commit/src/kzg/hyperkzg.rs b/poly_commit/src/kzg/hyperkzg.rs index 16532406..27cb6ae2 100644 --- a/poly_commit/src/kzg/hyperkzg.rs +++ b/poly_commit/src/kzg/hyperkzg.rs @@ -5,15 +5,7 @@ use halo2curves::{ff::Field, group::GroupEncoding, pairing::MultiMillerLoop, Cur use itertools::izip; use transcript::Transcript; -use crate::{ - coeff_form_uni_kzg_commit, even_odd_coeffs_separate, polynomial_add, powers_of_field_elements, - univariate_evaluate, -}; - -use super::{ - coeff_form_uni_kzg_verify, univariate_roots_quotient, CoefFormUniKZGSRS, HyperKZGOpening, - UniKZGVerifierParams, -}; +use crate::*; #[inline(always)] fn coeff_form_degree2_lagrange(roots: [F; 3], evals: [F; 3]) -> [F; 3] { @@ -75,11 +67,11 @@ where let beta2 = beta * beta; let beta_inv = beta.invert().unwrap(); let two_inv = E::Fr::ONE.double().invert().unwrap(); - let beta_pow_series = powers_of_field_elements(&beta, coeffs.len()); - let neg_beta_pow_series = powers_of_field_elements(&(-beta), coeffs.len()); + let beta_pow_series = powers_series(&beta, coeffs.len()); + let neg_beta_pow_series = powers_series(&(-beta), coeffs.len()); let beta2_eval = { - let beta2_pow_series = powers_of_field_elements(&beta2, coeffs.len()); + let beta2_pow_series = powers_series(&beta2, coeffs.len()); univariate_evaluate(coeffs, &beta2_pow_series) }; @@ -108,7 +100,7 @@ where .unzip(); let gamma = fs_transcript.generate_challenge_field_element(); - let gamma_pow_series = powers_of_field_elements(&gamma, alphas.len()); + let gamma_pow_series = powers_series(&gamma, alphas.len()); let v_beta = univariate_evaluate(&beta_evals, &gamma_pow_series); let v_neg_beta = univariate_evaluate(&neg_beta_evals, &gamma_pow_series); let v_beta2 = univariate_evaluate(&beta2_evals, &gamma_pow_series); @@ -194,7 +186,7 @@ where } let gamma = fs_transcript.generate_challenge_field_element(); - let gamma_pow_series = powers_of_field_elements(&gamma, alphas.len()); + let gamma_pow_series = powers_series(&gamma, alphas.len()); let v_beta = univariate_evaluate(&opening.evals_at_beta, &gamma_pow_series); let v_neg_beta = univariate_evaluate(&opening.evals_at_neg_beta, &gamma_pow_series); let v_beta2 = univariate_evaluate(&beta2_evals, &gamma_pow_series); diff --git a/poly_commit/src/kzg/univariate.rs b/poly_commit/src/kzg/univariate.rs index 56a375e7..3d0b79e0 100644 --- a/poly_commit/src/kzg/univariate.rs +++ b/poly_commit/src/kzg/univariate.rs @@ -7,7 +7,7 @@ use halo2curves::{ CurveAffine, }; -use crate::{powers_of_field_elements, univariate_degree_one_quotient}; +use crate::{powers_series, univariate_degree_one_quotient}; use super::{CoefFormUniKZGSRS, UniKZGVerifierParams}; @@ -23,7 +23,7 @@ where let tau = E::Fr::random(&mut rng); let g1 = E::G1Affine::generator(); - let tau_geometric_progression = powers_of_field_elements(&tau, length); + let tau_geometric_progression = powers_series(&tau, length); let g1_prog = g1.to_curve(); let coeff_bases = { diff --git a/poly_commit/src/kzg/utils.rs b/poly_commit/src/kzg/utils.rs index 6497fad0..71c582e1 100644 --- a/poly_commit/src/kzg/utils.rs +++ b/poly_commit/src/kzg/utils.rs @@ -14,7 +14,7 @@ pub fn primitive_root_of_unity(group_size: usize) -> F { } #[inline(always)] -pub(crate) fn powers_of_field_elements(x: &F, n: usize) -> Vec { +pub(crate) fn powers_series(x: &F, n: usize) -> Vec { let mut powers = vec![F::ONE]; let mut cur = *x; for _ in 0..n - 1 { From fee33f9becbf84c3bfabd2c14308baa42bbeb74b Mon Sep 17 00:00:00 2001 From: Hang Su Date: Sun, 2 Feb 2025 23:15:02 -0500 Subject: [PATCH 09/10] minor, stashing current changes --- poly_commit/src/kzg/univariate.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/poly_commit/src/kzg/univariate.rs b/poly_commit/src/kzg/univariate.rs index 3d0b79e0..4c713c25 100644 --- a/poly_commit/src/kzg/univariate.rs +++ b/poly_commit/src/kzg/univariate.rs @@ -11,6 +11,7 @@ use crate::{powers_series, univariate_degree_one_quotient}; use super::{CoefFormUniKZGSRS, UniKZGVerifierParams}; +#[inline(always)] pub fn generate_coef_form_uni_kzg_srs_for_testing( length: usize, ) -> CoefFormUniKZGSRS @@ -46,6 +47,7 @@ where } } +#[inline(always)] pub fn coeff_form_uni_kzg_commit( srs: &CoefFormUniKZGSRS, coeffs: &[E::Fr], @@ -58,6 +60,7 @@ where best_multiexp(coeffs, &srs.powers_of_tau[..coeffs.len()]) } +#[inline(always)] pub fn coeff_form_uni_kzg_open( srs: &CoefFormUniKZGSRS, coeffs: &[E::Fr], @@ -75,6 +78,7 @@ where best_multiexp(&div, &srs.powers_of_tau[..div.len()]) } +#[inline(always)] pub fn coeff_form_uni_kzg_verify( vk: UniKZGVerifierParams, comm: E::G1, From 1652a5c8e1ccb1663ec9e4c39c0a0d33b16c49e4 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 6 Feb 2025 22:07:16 -0500 Subject: [PATCH 10/10] bikzg prototyped --- poly_commit/src/kzg.rs | 3 + poly_commit/src/kzg/bivariate.rs | 141 ++++++++++++++++++++++++++++++ poly_commit/src/kzg/structs.rs | 127 ++++++++------------------- poly_commit/src/kzg/univariate.rs | 22 ++++- 4 files changed, 200 insertions(+), 93 deletions(-) create mode 100644 poly_commit/src/kzg/bivariate.rs diff --git a/poly_commit/src/kzg.rs b/poly_commit/src/kzg.rs index a21ceea6..67d2033f 100644 --- a/poly_commit/src/kzg.rs +++ b/poly_commit/src/kzg.rs @@ -7,6 +7,9 @@ pub use utils::*; mod univariate; pub use univariate::*; +mod bivariate; +pub use bivariate::*; + mod hyperkzg; pub use hyperkzg::*; diff --git a/poly_commit/src/kzg/bivariate.rs b/poly_commit/src/kzg/bivariate.rs new file mode 100644 index 00000000..f917c712 --- /dev/null +++ b/poly_commit/src/kzg/bivariate.rs @@ -0,0 +1,141 @@ +use ark_std::test_rng; +use halo2curves::{ + ff::Field, + group::{prime::PrimeCurveAffine, Curve, Group}, + msm::best_multiexp, + pairing::{MillerLoopResult, MultiMillerLoop}, + CurveAffine, +}; + +use crate::{ + powers_series, univariate_degree_one_quotient, CoefFormBiKZGLocalSRS, CoefFormUniKZGSRS, +}; + +use super::{BiKZGProof, BiKZGVerifierParam}; + +#[inline(always)] +pub fn generate_coef_form_bi_kzg_local_srs_for_testing( + local_length: usize, + distributed_parties: usize, + party_rank: usize, +) -> CoefFormBiKZGLocalSRS +where + E::G1Affine: CurveAffine, +{ + assert!(local_length.is_power_of_two()); + assert!(distributed_parties.is_power_of_two()); + assert!(party_rank < distributed_parties && 0 < party_rank); + + let mut rng = test_rng(); + let tau_x = E::Fr::random(&mut rng); + let tau_y = E::Fr::random(&mut rng); + + let g1 = E::G1Affine::generator(); + + let tau_x_geometric_progression = powers_series(&tau_x, local_length); + let tau_y_geometric_progression = powers_series(&tau_y, distributed_parties); + + let g1_prog = g1.to_curve(); + let x_coeff_bases = { + let mut proj_bases = vec![E::G1::identity(); local_length]; + proj_bases.iter_mut().enumerate().for_each(|(i, base)| { + *base = + g1_prog * tau_y_geometric_progression[party_rank] * tau_x_geometric_progression[i] + }); + + let mut g_bases = vec![E::G1Affine::default(); local_length]; + E::G1::batch_normalize(&proj_bases, &mut g_bases); + + drop(proj_bases); + g_bases + }; + + let tau_x_srs = CoefFormUniKZGSRS:: { + powers_of_tau: x_coeff_bases, + tau_g2: (E::G2Affine::generator() * tau_x).into(), + }; + + let y_coeff_bases = { + let mut proj_bases = vec![E::G1::identity(); distributed_parties]; + proj_bases + .iter_mut() + .enumerate() + .for_each(|(i, base)| *base = g1_prog * tau_y_geometric_progression[i]); + + let mut g_bases = vec![E::G1Affine::default(); distributed_parties]; + E::G1::batch_normalize(&proj_bases, &mut g_bases); + + drop(proj_bases); + g_bases + }; + + let tau_y_srs = CoefFormUniKZGSRS:: { + powers_of_tau: y_coeff_bases, + tau_g2: (E::G2Affine::generator() * tau_y).into(), + }; + + CoefFormBiKZGLocalSRS { + tau_x_srs, + tau_y_srs, + } +} + +#[inline(always)] +pub fn coeff_form_bi_kzg_open_leader( + srs: &CoefFormBiKZGLocalSRS, + evals_and_opens: &[(E::Fr, E::G1)], + beta: E::Fr, + eval: E::Fr, +) -> BiKZGProof +where + E::G1Affine: CurveAffine, +{ + assert_eq!(srs.tau_y_srs.powers_of_tau.len(), evals_and_opens.len()); + + let x_open: E::G1 = evals_and_opens.iter().map(|(_, o)| o).sum(); + let gammas: Vec = evals_and_opens.iter().map(|(e, _)| *e).collect(); + + let (div, remainder) = univariate_degree_one_quotient(&gammas, beta); + assert_eq!(remainder, eval); + + let y_open = best_multiexp(&div, &srs.tau_y_srs.powers_of_tau[..div.len()]); + + BiKZGProof { + quotient_x: x_open.into(), + quotient_y: y_open.into(), + } +} + +#[inline(always)] +pub fn coeff_form_bi_kzg_verify( + vk: BiKZGVerifierParam, + comm: E::G1, + alpha: E::Fr, + beta: E::Fr, + eval: E::Fr, + opening: BiKZGProof, +) -> bool +where + E::G1Affine: CurveAffine, +{ + let g1_eval: E::G1Affine = (E::G1Affine::generator() * eval).into(); + let g2_alpha: E::G2 = E::G2Affine::generator() * alpha; + let g2_beta: E::G2 = E::G2Affine::generator() * beta; + + let gt_result = E::multi_miller_loop(&[ + ( + &opening.quotient_x, + &(vk.tau_x_g2.to_curve() - g2_alpha).to_affine().into(), + ), + ( + &opening.quotient_y, + &(vk.tau_y_g2.to_curve() - g2_beta).to_affine().into(), + ), + ( + &(g1_eval - comm.to_affine()).into(), + &E::G2Affine::generator().into(), + ), + ]); + + gt_result.final_exponentiation().is_identity().into() +} diff --git a/poly_commit/src/kzg/structs.rs b/poly_commit/src/kzg/structs.rs index 16c705cc..ab860a4d 100644 --- a/poly_commit/src/kzg/structs.rs +++ b/poly_commit/src/kzg/structs.rs @@ -1,108 +1,20 @@ use halo2curves::pairing::Engine; -/// Structured reference string for Bi-KZG polynomial commitment scheme. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct CoefFormBiKZGSRS { - /// (g_1^{\tau_0^i\tau_1^j})_{i\in [0,N], j\in [0, M]} = \\ - /// ( - /// g_1, g_1^{\tau_0}, g_1^{\tau_0^2}, ..., g_1^{\tau_0^N}, - /// g_1^{\tau_1}, g_1^{\tau_0\tau_1}, g_1^{\tau_0^2\tau_1}, ..., g_1^{\tau_0^N\tau_1}, - /// ..., g_1^{\tau_0^N\tau_1^M} - /// ) - pub powers_of_g: Vec, - /// g in lagrange form over omega_0 and omega_1 - pub powers_of_g_lagrange_over_both_roots: Vec, - /// The generator of G2. - pub h: E::G2Affine, - /// tau_0 times the above generator of G2. - pub tau_0_h: E::G2Affine, - /// tau_1 times the above generator of G2. - pub tau_1_h: E::G2Affine, -} - -/// Structured reference string for Bi-KZG polynomial commitment scheme. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct LagrangeFormBiKZGSRS { - /// The generator of G1 - pub g: E::G1Affine, - /// g in lagrange form over omega_0 - pub powers_of_g_lagrange_over_x: Vec, - /// g in lagrange form over omega_0 and omega_1 - pub powers_of_g_lagrange_over_both_roots: Vec, - /// The generator of G2. - pub h: E::G2Affine, - /// tau_0 times the above generator of G2. - pub tau_0_h: E::G2Affine, - /// tau_1 times the above generator of G2. - pub tau_1_h: E::G2Affine, -} - -/// `UnivariateVerifierParam` is used to check evaluation proofs for a given -/// commitment. -#[derive(Clone, Debug, Eq, PartialEq, Default)] -pub struct BiKZGVerifierParam { - /// The generator of G1. - pub g: E::G1Affine, - /// The generator of G2. - pub h: E::G2Affine, - /// tau_0 times the above generator of G2. - pub tau_0_h: E::G2Affine, - /// tau_1 times the above generator of G2. - pub tau_1_h: E::G2Affine, -} - -/// Commitment Bi-KZG polynomial commitment scheme. -#[derive(Clone, Debug, Default, PartialEq)] -pub struct BiKZGCommitment { - /// the actual commitment is an affine point. - pub com: E::G1Affine, -} - -/// Proof for Bi-KZG polynomial commitment scheme. -#[derive(Clone, Debug, Default, PartialEq)] -pub struct BiKZGProof { - /// the actual proof is a pair of affine points. - pub pi0: E::G1Affine, - pub pi1: E::G1Affine, -} - -impl From<&CoefFormBiKZGSRS> for BiKZGVerifierParam { - fn from(srs: &CoefFormBiKZGSRS) -> Self { - Self { - g: srs.powers_of_g[0], - h: srs.h, - tau_0_h: srs.tau_0_h, - tau_1_h: srs.tau_1_h, - } - } -} - -impl From<&LagrangeFormBiKZGSRS> for BiKZGVerifierParam { - fn from(srs: &LagrangeFormBiKZGSRS) -> Self { - Self { - g: srs.g, - h: srs.h, - tau_0_h: srs.tau_0_h, - tau_1_h: srs.tau_1_h, - } - } -} - /// Structured reference string for univariate KZG polynomial commitment scheme. /// The univariate polynomial here is of coefficient form. -#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct CoefFormUniKZGSRS { /// power of \tau times the generators of G1, yielding - /// \{ \[\tau^i\]_1 \}_{i \in \[0, 2^n - 1\]} + /// \tau^i over G1 with i ranging in \[ 0, 2^n - 1 \] pub powers_of_tau: Vec, - /// \tau times the generator of G2, [\tau]_2. + /// \tau over G2 pub tau_g2: E::G2Affine, } /// Univariate KZG PCS verifier's params. #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct UniKZGVerifierParams { - /// \tau times the generator of G2, [\tau]_2. + /// \tau over G2 pub tau_g2: E::G2Affine, } @@ -123,3 +35,34 @@ pub struct HyperKZGOpening { pub beta_commitment: E::G1, pub tau_vanishing_commitment: E::G1, } + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CoefFormBiKZGLocalSRS { + pub tau_x_srs: CoefFormUniKZGSRS, + pub tau_y_srs: CoefFormUniKZGSRS, +} + +/// Bivariate KZG PCS verifier's params. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] +pub struct BiKZGVerifierParam { + /// tau_0 over G2. + pub tau_x_g2: E::G2Affine, + /// tau_y over G2. + pub tau_y_g2: E::G2Affine, +} + +impl From<&CoefFormBiKZGLocalSRS> for BiKZGVerifierParam { + fn from(srs: &CoefFormBiKZGLocalSRS) -> Self { + Self { + tau_x_g2: srs.tau_x_srs.tau_g2, + tau_y_g2: srs.tau_y_srs.tau_g2, + } + } +} + +/// Proof for Bi-KZG polynomial commitment scheme. +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct BiKZGProof { + pub quotient_x: E::G1Affine, + pub quotient_y: E::G1Affine, +} diff --git a/poly_commit/src/kzg/univariate.rs b/poly_commit/src/kzg/univariate.rs index 4c713c25..67aeac7b 100644 --- a/poly_commit/src/kzg/univariate.rs +++ b/poly_commit/src/kzg/univariate.rs @@ -7,7 +7,7 @@ use halo2curves::{ CurveAffine, }; -use crate::{powers_series, univariate_degree_one_quotient}; +use crate::{powers_series, univariate_degree_one_quotient, univariate_evaluate}; use super::{CoefFormUniKZGSRS, UniKZGVerifierParams}; @@ -78,6 +78,26 @@ where best_multiexp(&div, &srs.powers_of_tau[..div.len()]) } +#[inline(always)] +pub fn coeff_form_uni_kzg_open_eval( + srs: &CoefFormUniKZGSRS, + coeffs: &[E::Fr], + alpha: E::Fr, +) -> (E::Fr, E::G1) +where + E::G1Affine: CurveAffine, +{ + assert!(srs.powers_of_tau.len() >= coeffs.len()); + + let alpha_geometric_progression = powers_series(&alpha, coeffs.len()); + let eval = univariate_evaluate(coeffs, &alpha_geometric_progression); + + let (div, remainder) = univariate_degree_one_quotient(coeffs, alpha); + assert_eq!(remainder, eval); + + (eval, best_multiexp(&div, &srs.powers_of_tau[..div.len()])) +} + #[inline(always)] pub fn coeff_form_uni_kzg_verify( vk: UniKZGVerifierParams,