// Copyright (c) 2023 Espresso Systems (espressosys.com) // This file is part of the HyperPlonk library. // You should have received a copy of the MIT License // along with the HyperPlonk library. If not, see . //! Implementing Structured Reference Strings for multilinear polynomial KZG use crate::pcs::{ multilinear_kzg::util::{eq_eval, eq_extension}, prelude::PCSError, StructuredReferenceString, }; use ark_ec::{pairing::Pairing, scalar_mul::fixed_base::FixedBase, AffineRepr, CurveGroup}; use ark_ff::{Field, PrimeField, Zero}; use ark_poly::DenseMultilinearExtension; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{ collections::LinkedList, end_timer, format, rand::Rng, start_timer, string::ToString, vec::Vec, UniformRand, }; use core::iter::FromIterator; /// Evaluations over {0,1}^n for G1 or G2 #[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] pub struct Evaluations { /// The evaluations. pub evals: Vec, } /// Universal Parameter #[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] pub struct MultilinearUniversalParams { /// prover parameters pub prover_param: MultilinearProverParam, /// h^randomness: h^t1, h^t2, ..., **h^{t_nv}** pub h_mask: Vec, } /// Prover Parameters #[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] pub struct MultilinearProverParam { /// number of variables pub num_vars: usize, /// `pp_{0}`, `pp_{1}`, ...,pp_{nu_vars} defined /// by XZZPD19 where pp_{nv-0}=g and /// pp_{nv-i}=g^{eq((t_1,..t_i),(X_1,..X_i))} pub powers_of_g: Vec>, /// generator for G1 pub g: E::G1Affine, /// generator for G2 pub h: E::G2Affine, } /// Verifier Parameters #[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] pub struct MultilinearVerifierParam { /// number of variables pub num_vars: usize, /// generator of G1 pub g: E::G1Affine, /// generator of G2 pub h: E::G2Affine, /// h^randomness: h^t1, h^t2, ..., **h^{t_nv}** pub h_mask: Vec, } impl StructuredReferenceString for MultilinearUniversalParams { type ProverParam = MultilinearProverParam; type VerifierParam = MultilinearVerifierParam; /// Extract the prover parameters from the public parameters. fn extract_prover_param(&self, supported_num_vars: usize) -> Self::ProverParam { let to_reduce = self.prover_param.num_vars - supported_num_vars; Self::ProverParam { powers_of_g: self.prover_param.powers_of_g[to_reduce..].to_vec(), g: self.prover_param.g, h: self.prover_param.h, num_vars: supported_num_vars, } } /// Extract the verifier parameters from the public parameters. fn extract_verifier_param(&self, supported_num_vars: usize) -> Self::VerifierParam { let to_reduce = self.prover_param.num_vars - supported_num_vars; Self::VerifierParam { num_vars: supported_num_vars, g: self.prover_param.g, h: self.prover_param.h, h_mask: self.h_mask[to_reduce..].to_vec(), } } /// Trim the universal parameters to specialize the public parameters /// for multilinear polynomials to the given `supported_num_vars`, and /// returns committer key and verifier key. `supported_num_vars` should /// be in range `1..=params.num_vars` fn trim( &self, supported_num_vars: usize, ) -> Result<(Self::ProverParam, Self::VerifierParam), PCSError> { if supported_num_vars > self.prover_param.num_vars { return Err(PCSError::InvalidParameters(format!( "SRS does not support target number of vars {}", supported_num_vars ))); } let to_reduce = self.prover_param.num_vars - supported_num_vars; let ck = Self::ProverParam { powers_of_g: self.prover_param.powers_of_g[to_reduce..].to_vec(), g: self.prover_param.g, h: self.prover_param.h, num_vars: supported_num_vars, }; let vk = Self::VerifierParam { num_vars: supported_num_vars, g: self.prover_param.g, h: self.prover_param.h, h_mask: self.h_mask[to_reduce..].to_vec(), }; Ok((ck, vk)) } /// Build SRS for testing. /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION. fn gen_srs_for_testing(rng: &mut R, num_vars: usize) -> Result { if num_vars == 0 { return Err(PCSError::InvalidParameters( "constant polynomial not supported".to_string(), )); } let total_timer = start_timer!(|| "SRS generation"); let pp_generation_timer = start_timer!(|| "Prover Param generation"); let g = E::G1::rand(rng); let h = E::G2::rand(rng); let mut powers_of_g = Vec::new(); let t: Vec<_> = (0..num_vars).map(|_| E::ScalarField::rand(rng)).collect(); let scalar_bits = E::ScalarField::MODULUS_BIT_SIZE as usize; let mut eq: LinkedList> = LinkedList::from_iter(eq_extension(&t).into_iter()); let mut eq_arr = LinkedList::new(); let mut base = eq.pop_back().unwrap().evaluations; for i in (0..num_vars).rev() { eq_arr.push_front(remove_dummy_variable(&base, i)?); if i != 0 { let mul = eq.pop_back().unwrap().evaluations; base = base .into_iter() .zip(mul.into_iter()) .map(|(a, b)| a * b) .collect(); } } let mut pp_powers = Vec::new(); let mut total_scalars = 0; for i in 0..num_vars { let eq = eq_arr.pop_front().unwrap(); let pp_k_powers = (0..(1 << (num_vars - i))).map(|x| eq[x]); pp_powers.extend(pp_k_powers); total_scalars += 1 << (num_vars - i); } let window_size = FixedBase::get_mul_window_size(total_scalars); let g_table = FixedBase::get_window_table(scalar_bits, window_size, g); let pp_g = E::G1::normalize_batch(&FixedBase::msm( scalar_bits, window_size, &g_table, &pp_powers, )); let mut start = 0; for i in 0..num_vars { let size = 1 << (num_vars - i); let pp_k_g = Evaluations { evals: pp_g[start..(start + size)].to_vec(), }; // check correctness of pp_k_g let t_eval_0 = eq_eval(&vec![E::ScalarField::zero(); num_vars - i], &t[i..num_vars])?; assert_eq!((g * t_eval_0).into(), pp_k_g.evals[0]); powers_of_g.push(pp_k_g); start += size; } let gg = Evaluations { evals: [g.into_affine()].to_vec(), }; powers_of_g.push(gg); let pp = Self::ProverParam { num_vars, g: g.into_affine(), h: h.into_affine(), powers_of_g, }; end_timer!(pp_generation_timer); let vp_generation_timer = start_timer!(|| "VP generation"); let h_mask = { let window_size = FixedBase::get_mul_window_size(num_vars); let h_table = FixedBase::get_window_table(scalar_bits, window_size, h); E::G2::normalize_batch(&FixedBase::msm(scalar_bits, window_size, &h_table, &t)) }; end_timer!(vp_generation_timer); end_timer!(total_timer); Ok(Self { prover_param: pp, h_mask, }) } } /// fix first `pad` variables of `poly` represented in evaluation form to zero fn remove_dummy_variable(poly: &[F], pad: usize) -> Result, PCSError> { if pad == 0 { return Ok(poly.to_vec()); } if !poly.len().is_power_of_two() { return Err(PCSError::InvalidParameters( "Size of polynomial should be power of two.".to_string(), )); } let nv = ark_std::log2(poly.len()) as usize - pad; Ok((0..(1 << nv)).map(|x| poly[x << pad]).collect()) } #[cfg(test)] mod tests { use super::*; use ark_bls12_381::Bls12_381; use ark_std::test_rng; type E = Bls12_381; #[test] fn test_srs_gen() -> Result<(), PCSError> { let mut rng = test_rng(); for nv in 4..10 { let _ = MultilinearUniversalParams::::gen_srs_for_testing(&mut rng, nv)?; } Ok(()) } }