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); +}