diff --git a/plonkish_backend/src/pcs/multilinear.rs b/plonkish_backend/src/pcs/multilinear.rs index f98345b0..54574879 100644 --- a/plonkish_backend/src/pcs/multilinear.rs +++ b/plonkish_backend/src/pcs/multilinear.rs @@ -12,14 +12,14 @@ mod kzg; mod zeromorph; pub use brakedown::{ - MultilinearBrakedown, MultilinearBrakedownCommitment, MultilinearBrakedownParams, + MultilinearBrakedown, MultilinearBrakedownCommitment, MultilinearBrakedownParam, }; pub use gemini::Gemini; -pub use hyrax::{MultilinearHyrax, MultilinearHyraxCommitment, MultilinearHyraxParams}; -pub use ipa::{MultilinearIpa, MultilinearIpaCommitment, MultilinearIpaParams}; +pub use hyrax::{MultilinearHyrax, MultilinearHyraxCommitment, MultilinearHyraxParam}; +pub use ipa::{MultilinearIpa, MultilinearIpaCommitment, MultilinearIpaParam}; pub use kzg::{ - MultilinearKzg, MultilinearKzgCommitment, MultilinearKzgParams, MultilinearKzgProverParams, - MultilinearKzgVerifierParams, + MultilinearKzg, MultilinearKzgCommitment, MultilinearKzgParam, MultilinearKzgProverParam, + MultilinearKzgVerifierParam, }; pub use zeromorph::{Zeromorph, ZeromorphKzgProverParam, ZeromorphKzgVerifierParam}; diff --git a/plonkish_backend/src/pcs/multilinear/brakedown.rs b/plonkish_backend/src/pcs/multilinear/brakedown.rs index 8bb73e8a..39f961c1 100644 --- a/plonkish_backend/src/pcs/multilinear/brakedown.rs +++ b/plonkish_backend/src/pcs/multilinear/brakedown.rs @@ -31,13 +31,13 @@ impl Clone for MultilinearBrakedown { +pub struct MultilinearBrakedownParam { num_vars: usize, num_rows: usize, brakedown: Brakedown, } -impl MultilinearBrakedownParams { +impl MultilinearBrakedownParam { pub fn num_vars(&self) -> usize { self.num_vars } @@ -92,9 +92,9 @@ where H: Hash, S: BrakedownSpec, { - type Param = MultilinearBrakedownParams; - type ProverParam = MultilinearBrakedownParams; - type VerifierParam = MultilinearBrakedownParams; + type Param = MultilinearBrakedownParam; + type ProverParam = MultilinearBrakedownParam; + type VerifierParam = MultilinearBrakedownParam; type Polynomial = MultilinearPolynomial; type Commitment = MultilinearBrakedownCommitment; type CommitmentChunk = Output; @@ -103,7 +103,7 @@ where assert!(poly_size.is_power_of_two()); let num_vars = poly_size.ilog2() as usize; let brakedown = Brakedown::new_multilinear::(num_vars, 20.min((1 << num_vars) - 1), rng); - Ok(MultilinearBrakedownParams { + Ok(MultilinearBrakedownParam { num_vars, num_rows: (1 << num_vars) / brakedown.row_len(), brakedown, @@ -120,7 +120,7 @@ where Ok((param.clone(), param.clone())) } else { Err(Error::InvalidPcsParam( - "Can't trim MultilinearBrakedownParams into different poly_size".to_string(), + "Can't trim MultilinearBrakedownParam into different poly_size".to_string(), )) } } diff --git a/plonkish_backend/src/pcs/multilinear/gemini.rs b/plonkish_backend/src/pcs/multilinear/gemini.rs index 3894c9b1..49af3e1a 100644 --- a/plonkish_backend/src/pcs/multilinear/gemini.rs +++ b/plonkish_backend/src/pcs/multilinear/gemini.rs @@ -4,7 +4,7 @@ use crate::{ pcs::{ multilinear::additive, - univariate::{UnivariateKzg, UnivariateKzgCommitment}, + univariate::{err_too_large_deree, UnivariateKzg, UnivariateKzgCommitment}, Evaluation, Point, PolynomialCommitmentScheme, }, poly::{ @@ -54,11 +54,8 @@ where fn commit(pp: &Self::ProverParam, poly: &Self::Polynomial) -> Result { if pp.degree() + 1 < poly.evals().len() { - return Err(Error::InvalidPcsParam(format!( - "Too large degree of poly to commit (param supports degree up to {} but got {})", - pp.degree(), - poly.evals().len() - ))); + let got = poly.evals().len() - 1; + return Err(err_too_large_deree("commit", pp.degree(), got)); } Ok(UnivariateKzg::commit_monomial(pp, poly.evals())) @@ -84,11 +81,8 @@ where ) -> Result<(), Error> { let num_vars = point.len(); if pp.degree() + 1 < poly.evals().len() { - return Err(Error::InvalidPcsParam(format!( - "Too large degree of poly to open (param supports degree up to {} but got {})", - pp.degree(), - poly.evals().len() - ))); + let got = poly.evals().len() - 1; + return Err(err_too_large_deree("open", pp.degree(), got)); } if cfg!(feature = "sanity-check") { diff --git a/plonkish_backend/src/pcs/multilinear/hyrax.rs b/plonkish_backend/src/pcs/multilinear/hyrax.rs index eefbbb4a..45e3ecb0 100644 --- a/plonkish_backend/src/pcs/multilinear/hyrax.rs +++ b/plonkish_backend/src/pcs/multilinear/hyrax.rs @@ -2,7 +2,7 @@ use crate::{ pcs::{ multilinear::{ additive, err_too_many_variates, - ipa::{MultilinearIpa, MultilinearIpaCommitment, MultilinearIpaParams}, + ipa::{MultilinearIpa, MultilinearIpaCommitment, MultilinearIpaParam}, validate_input, }, Additive, Evaluation, Point, PolynomialCommitmentScheme, @@ -16,7 +16,6 @@ use crate::{ }, Error, }; - use rand::RngCore; use std::{borrow::Cow, iter, marker::PhantomData}; @@ -24,14 +23,14 @@ use std::{borrow::Cow, iter, marker::PhantomData}; pub struct MultilinearHyrax(PhantomData); #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct MultilinearHyraxParams { +pub struct MultilinearHyraxParam { num_vars: usize, batch_num_vars: usize, row_num_vars: usize, - ipa: MultilinearIpaParams, + ipa: MultilinearIpaParam, } -impl MultilinearHyraxParams { +impl MultilinearHyraxParam { pub fn num_vars(&self) -> usize { self.num_vars } @@ -108,9 +107,9 @@ where C: CurveAffine + Serialize + DeserializeOwned, C::ScalarExt: Serialize + DeserializeOwned, { - type Param = MultilinearHyraxParams; - type ProverParam = MultilinearHyraxParams; - type VerifierParam = MultilinearHyraxParams; + type Param = MultilinearHyraxParam; + type ProverParam = MultilinearHyraxParam; + type VerifierParam = MultilinearHyraxParam; type Polynomial = MultilinearPolynomial; type Commitment = MultilinearHyraxCommitment; type CommitmentChunk = C; @@ -171,8 +170,8 @@ where let comm = { let mut comm = vec![C::CurveExt::identity(); pp.num_chunks()]; parallelize(&mut comm, |(comm, start)| { - for (comm, start) in comm.iter_mut().zip((start * row_len..).step_by(row_len)) { - *comm = variable_base_msm(&scalars[start..start + row_len], pp.g()); + for (comm, offset) in comm.iter_mut().zip((start * row_len..).step_by(row_len)) { + *comm = variable_base_msm(&scalars[offset..offset + row_len], pp.g()); } }); batch_projective_to_affine(&comm) @@ -198,8 +197,8 @@ where let comms = { let mut comms = vec![C::CurveExt::identity(); scalars.len()]; parallelize(&mut comms, |(comms, start)| { - for (comm, scalars) in comms.iter_mut().zip(&scalars[start..]) { - *comm = variable_base_msm(*scalars, pp.g()); + for (comm, row) in comms.iter_mut().zip(&scalars[start..]) { + *comm = variable_base_msm(*row, pp.g()); } }); batch_projective_to_affine(&comms) @@ -268,14 +267,13 @@ where num_polys: usize, transcript: &mut impl TranscriptRead, ) -> Result, Error> { - let comms = iter::repeat_with(|| { + iter::repeat_with(|| { transcript .read_commitments(vp.num_chunks()) .map(MultilinearHyraxCommitment) }) .take(num_polys) - .try_collect()?; - Ok(comms) + .collect() } fn verify( @@ -297,7 +295,6 @@ where variable_base_msm(&scalars, &comm.0).into() }) }; - MultilinearIpa::verify(&vp.ipa, &comm, &lo.to_vec(), eval, transcript) } diff --git a/plonkish_backend/src/pcs/multilinear/ipa.rs b/plonkish_backend/src/pcs/multilinear/ipa.rs index deabd2e4..a62c58b6 100644 --- a/plonkish_backend/src/pcs/multilinear/ipa.rs +++ b/plonkish_backend/src/pcs/multilinear/ipa.rs @@ -1,36 +1,34 @@ use crate::{ pcs::{ multilinear::{additive, err_too_many_variates, validate_input}, + univariate::ipa::{prove_bulletproof_reduction, verify_bulletproof_reduction}, Additive, Evaluation, Point, PolynomialCommitmentScheme, }, poly::multilinear::MultilinearPolynomial, util::{ arithmetic::{ - batch_projective_to_affine, inner_product, variable_base_msm, Curve, CurveAffine, - CurveExt, Field, Group, + batch_projective_to_affine, variable_base_msm, Curve, CurveAffine, CurveExt, Group, }, - chain, parallel::parallelize, transcript::{TranscriptRead, TranscriptWrite}, - Deserialize, DeserializeOwned, Itertools, Serialize, + Deserialize, DeserializeOwned, Either, Itertools, Serialize, }, Error, }; -use halo2_curves::group::ff::BatchInvert; use rand::RngCore; -use std::{iter, marker::PhantomData, slice}; +use std::{marker::PhantomData, slice}; #[derive(Clone, Debug)] pub struct MultilinearIpa(PhantomData); #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct MultilinearIpaParams { +pub struct MultilinearIpaParam { num_vars: usize, g: Vec, h: C, } -impl MultilinearIpaParams { +impl MultilinearIpaParam { pub fn num_vars(&self) -> usize { self.num_vars } @@ -87,9 +85,9 @@ where C: CurveAffine + Serialize + DeserializeOwned, C::ScalarExt: Serialize + DeserializeOwned, { - type Param = MultilinearIpaParams; - type ProverParam = MultilinearIpaParams; - type VerifierParam = MultilinearIpaParams; + type Param = MultilinearIpaParam; + type ProverParam = MultilinearIpaParam; + type VerifierParam = MultilinearIpaParam; type Polynomial = MultilinearPolynomial; type Commitment = MultilinearIpaCommitment; type CommitmentChunk = C; @@ -173,56 +171,10 @@ where assert_eq!(poly.evaluate(point), *eval); } - let xi_0 = transcript.squeeze_challenge(); - let h_prime = (pp.h * xi_0).to_affine(); - - let mut bases = pp.g().to_vec(); - let mut coeffs = poly.evals().to_vec(); - let mut zs = MultilinearPolynomial::eq_xy(point).into_evals(); - - for i in 0..pp.num_vars() { - let mid = 1 << (pp.num_vars() - i - 1); - - let (bases_l, bases_r) = bases.split_at(mid); - let (coeffs_l, coeffs_r) = coeffs.split_at(mid); - let (zs_l, zs_r) = zs.split_at(mid); - let (c_l, c_r) = (inner_product(coeffs_r, zs_l), inner_product(coeffs_l, zs_r)); - let l_i = variable_base_msm(chain![coeffs_r, [&c_l]], chain![bases_l, [&h_prime]]); - let r_i = variable_base_msm(chain![coeffs_l, [&c_r]], chain![bases_r, [&h_prime]]); - transcript.write_commitment(&l_i.to_affine())?; - transcript.write_commitment(&r_i.to_affine())?; - - let xi_i = transcript.squeeze_challenge(); - let xi_i_inv = xi_i.invert().unwrap(); - - let (bases_l, bases_r) = bases.split_at_mut(mid); - let (coeffs_l, coeffs_r) = coeffs.split_at_mut(mid); - let (zs_l, zs_r) = zs.split_at_mut(mid); - parallelize(bases_l, |(bases_l, start)| { - let mut tmp = Vec::with_capacity(bases_l.len()); - for (lhs, rhs) in bases_l.iter().zip(bases_r[start..].iter()) { - tmp.push(lhs.to_curve() + *rhs * xi_i); - } - C::Curve::batch_normalize(&tmp, bases_l); - }); - parallelize(coeffs_l, |(coeffs_l, start)| { - for (lhs, rhs) in coeffs_l.iter_mut().zip(coeffs_r[start..].iter()) { - *lhs += xi_i_inv * rhs; - } - }); - parallelize(zs_l, |(zs_l, start)| { - for (lhs, rhs) in zs_l.iter_mut().zip(zs_r[start..].iter()) { - *lhs += xi_i * rhs; - } - }); - bases.truncate(mid); - coeffs.truncate(mid); - zs.truncate(mid); - } - - transcript.write_field_element(&coeffs[0])?; - - Ok(()) + let bases = pp.g(); + let coeffs = poly.evals(); + let zs = MultilinearPolynomial::eq_xy(point).into_evals(); + prove_bulletproof_reduction(bases, pp.h(), coeffs, zs, transcript) } fn batch_open<'a>( @@ -258,35 +210,9 @@ where eval: &C::Scalar, transcript: &mut impl TranscriptRead, ) -> Result<(), Error> { - validate_input("verify", vp.num_vars(), [], [point])?; - - let xi_0 = transcript.squeeze_challenge(); - - let (ls, rs, xis) = iter::repeat_with(|| { - Ok(( - transcript.read_commitment()?, - transcript.read_commitment()?, - transcript.squeeze_challenge(), - )) - }) - .take(vp.num_vars()) - .collect::, _>>()? - .into_iter() - .multiunzip::<(Vec<_>, Vec<_>, Vec<_>)>(); - let neg_c = -transcript.read_field_element()?; - - let xi_invs = { - let mut xi_invs = xis.clone(); - xi_invs.batch_invert(); - xi_invs - }; - let neg_c_h = MultilinearPolynomial::new(h_coeffs(neg_c, &xis)); - let u = &(xi_0 * (neg_c_h.evaluate(point) + eval)); - let scalars = chain![&xi_invs, &xis, neg_c_h.evals(), [u]]; - let bases = chain![&ls, &rs, vp.g(), [vp.h()]]; - bool::from((variable_base_msm(scalars, bases) + comm.0).is_identity()) - .then_some(()) - .ok_or_else(|| Error::InvalidPcsOpen("Invalid multilinear IPA open".to_string())) + let bases = vp.g(); + let point = Either::Right(point.as_slice()); + verify_bulletproof_reduction(bases, vp.h(), comm, point, eval, transcript) } fn batch_verify<'a>( @@ -301,26 +227,6 @@ where } } -fn h_coeffs(scalar: F, xi: &[F]) -> Vec { - assert!(!xi.is_empty()); - - let mut coeffs = vec![F::ZERO; 1 << xi.len()]; - coeffs[0] = scalar; - - for (len, xi) in xi.iter().rev().enumerate().map(|(i, xi)| (1 << i, xi)) { - let (left, right) = coeffs.split_at_mut(len); - let right = &mut right[0..len]; - right.copy_from_slice(left); - parallelize(right, |(right, _)| { - for coeff in right { - *coeff *= xi; - } - }); - } - - coeffs -} - #[cfg(test)] mod test { use crate::{ diff --git a/plonkish_backend/src/pcs/multilinear/kzg.rs b/plonkish_backend/src/pcs/multilinear/kzg.rs index cbaf6525..3c375b4c 100644 --- a/plonkish_backend/src/pcs/multilinear/kzg.rs +++ b/plonkish_backend/src/pcs/multilinear/kzg.rs @@ -23,14 +23,14 @@ use std::{iter, marker::PhantomData, ops::Neg, slice}; pub struct MultilinearKzg(PhantomData); #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct MultilinearKzgParams { +pub struct MultilinearKzgParam { g1: M::G1Affine, eqs: Vec>, g2: M::G2Affine, ss: Vec, } -impl MultilinearKzgParams { +impl MultilinearKzgParam { pub fn num_vars(&self) -> usize { self.eqs.len() } @@ -53,12 +53,12 @@ impl MultilinearKzgParams { } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct MultilinearKzgProverParams { +pub struct MultilinearKzgProverParam { g1: M::G1Affine, eqs: Vec>, } -impl MultilinearKzgProverParams { +impl MultilinearKzgProverParam { pub fn num_vars(&self) -> usize { self.eqs.len() - 1 } @@ -77,13 +77,13 @@ impl MultilinearKzgProverParams { } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct MultilinearKzgVerifierParams { +pub struct MultilinearKzgVerifierParam { g1: M::G1Affine, g2: M::G2Affine, ss: Vec, } -impl MultilinearKzgVerifierParams { +impl MultilinearKzgVerifierParam { pub fn num_vars(&self) -> usize { self.ss.len() } @@ -154,15 +154,16 @@ where M::G1Affine: Serialize + DeserializeOwned, M::G2Affine: Serialize + DeserializeOwned, { - type Param = MultilinearKzgParams; - type ProverParam = MultilinearKzgProverParams; - type VerifierParam = MultilinearKzgVerifierParams; + type Param = MultilinearKzgParam; + type ProverParam = MultilinearKzgProverParam; + type VerifierParam = MultilinearKzgVerifierParam; type Polynomial = MultilinearPolynomial; type Commitment = MultilinearKzgCommitment; type CommitmentChunk = M::G1Affine; fn setup(poly_size: usize, _: usize, mut rng: impl RngCore) -> Result { assert!(poly_size.is_power_of_two()); + let num_vars = poly_size.ilog2() as usize; let ss = iter::repeat_with(|| M::Scalar::random(&mut rng)) .take(num_vars) diff --git a/plonkish_backend/src/pcs/multilinear/zeromorph.rs b/plonkish_backend/src/pcs/multilinear/zeromorph.rs index 80f84713..ffd18abc 100644 --- a/plonkish_backend/src/pcs/multilinear/zeromorph.rs +++ b/plonkish_backend/src/pcs/multilinear/zeromorph.rs @@ -1,7 +1,10 @@ use crate::{ pcs::{ multilinear::{additive, quotients}, - univariate::{UnivariateKzg, UnivariateKzgProverParam, UnivariateKzgVerifierParam}, + univariate::{ + err_too_large_deree, UnivariateKzg, UnivariateKzgProverParam, + UnivariateKzgVerifierParam, + }, Evaluation, Point, PolynomialCommitmentScheme, }, poly::{multilinear::MultilinearPolynomial, univariate::UnivariatePolynomial}, @@ -78,6 +81,8 @@ where as PolynomialCommitmentScheme>::CommitmentChunk; fn setup(poly_size: usize, batch_size: usize, rng: impl RngCore) -> Result { + assert!(poly_size.is_power_of_two()); + UnivariateKzg::::setup(poly_size, batch_size, rng) } @@ -86,11 +91,13 @@ where poly_size: usize, batch_size: usize, ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> { + assert!(poly_size.is_power_of_two()); + let (commit_pp, vp) = UnivariateKzg::::trim(param, poly_size, batch_size)?; let offset = param.monomial_g1().len() - poly_size; let open_pp = { let monomial_g1 = param.monomial_g1()[offset..].to_vec(); - UnivariateKzgProverParam::new(monomial_g1, Vec::new()) + UnivariateKzgProverParam::new(poly_size.ilog2() as usize, monomial_g1, Vec::new()) }; let s_offset_g2 = param.powers_of_s_g2()[offset]; @@ -102,11 +109,8 @@ where fn commit(pp: &Self::ProverParam, poly: &Self::Polynomial) -> Result { if pp.degree() + 1 < poly.evals().len() { - return Err(Error::InvalidPcsParam(format!( - "Too large degree of poly to commit (param supports degree up to {} but got {})", - pp.degree(), - poly.evals().len() - ))); + let got = poly.evals().len() - 1; + return Err(err_too_large_deree("commit", pp.degree(), got)); } Ok(UnivariateKzg::commit_monomial(&pp.commit_pp, poly.evals())) @@ -132,11 +136,8 @@ where ) -> Result<(), Error> { let num_vars = poly.num_vars(); if pp.degree() + 1 < poly.evals().len() { - return Err(Error::InvalidPcsParam(format!( - "Too large degree of poly to open (param supports degree up to {} but got {})", - pp.degree(), - poly.evals().len() - ))); + let got = poly.evals().len() - 1; + return Err(err_too_large_deree("open", pp.degree(), got)); } if cfg!(feature = "sanity-check") { diff --git a/plonkish_backend/src/pcs/univariate.rs b/plonkish_backend/src/pcs/univariate.rs index 7fa6da07..c58db3ba 100644 --- a/plonkish_backend/src/pcs/univariate.rs +++ b/plonkish_backend/src/pcs/univariate.rs @@ -2,7 +2,7 @@ use crate::{ poly::univariate::{UnivariateBasis::*, UnivariatePolynomial}, util::{ arithmetic::{ - batch_projective_to_affine, radix2_fft, root_of_unity_inv, CurveAffine, Field, + batch_projective_to_affine, radix2_fft, root_of_unity_inv, squares, CurveAffine, Field, PrimeField, }, parallel::parallelize, @@ -11,21 +11,29 @@ use crate::{ Error, }; +mod hyrax; +pub(super) mod ipa; mod kzg; +pub use hyrax::{ + UnivariateHyrax, UnivariateHyraxCommitment, UnivariateHyraxParam, UnivariateHyraxVerifierParam, +}; +pub use ipa::{ + UnivariateIpa, UnivariateIpaCommitment, UnivariateIpaParam, UnivariateIpaVerifierParam, +}; pub use kzg::{ UnivariateKzg, UnivariateKzgCommitment, UnivariateKzgParam, UnivariateKzgProverParam, UnivariateKzgVerifierParam, }; -fn monomial_g1_to_lagrange_g1(monomial_g1: &[C]) -> Vec { - assert!(monomial_g1.len().is_power_of_two()); +fn monomial_g_to_lagrange_g(monomial_g: &[C]) -> Vec { + assert!(monomial_g.len().is_power_of_two()); - let k = monomial_g1.len().ilog2() as usize; - let n_inv = C::Scalar::TWO_INV.pow_vartime([k as u64]); + let k = monomial_g.len().ilog2() as usize; + let n_inv = squares(C::Scalar::TWO_INV).nth(k).unwrap(); let omega_inv = root_of_unity_inv(k); - let mut lagrange = monomial_g1.iter().map(C::to_curve).collect_vec(); + let mut lagrange = monomial_g.iter().map(C::to_curve).collect_vec(); radix2_fft(&mut lagrange, omega_inv, k); parallelize(&mut lagrange, |(g, _)| { g.iter_mut().for_each(|g| *g *= n_inv) @@ -49,7 +57,7 @@ fn validate_input<'a, F: Field>( } Lagrange => { if param_degree + 1 != poly.coeffs().len() { - return Err(err_invalid_evals_len(param_degree + 1, poly.coeffs().len())); + return Err(err_invalid_evals_len(param_degree, poly.coeffs().len() - 1)); } } } @@ -57,7 +65,7 @@ fn validate_input<'a, F: Field>( Ok(()) } -fn err_too_large_deree(function: &str, upto: usize, got: usize) -> Error { +pub(super) fn err_too_large_deree(function: &str, upto: usize, got: usize) -> Error { Error::InvalidPcsParam(if function == "trim" { format!("Too large degree to {function} (param supports degree up to {upto} but got {got})") } else { diff --git a/plonkish_backend/src/pcs/univariate/hyrax.rs b/plonkish_backend/src/pcs/univariate/hyrax.rs new file mode 100644 index 00000000..ac94473d --- /dev/null +++ b/plonkish_backend/src/pcs/univariate/hyrax.rs @@ -0,0 +1,402 @@ +use crate::{ + pcs::{ + univariate::{ + additive, err_too_large_deree, + ipa::{ + UnivariateIpa, UnivariateIpaCommitment, UnivariateIpaParam, + UnivariateIpaVerifierParam, + }, + validate_input, + }, + Additive, Evaluation, Point, PolynomialCommitmentScheme, + }, + poly::univariate::{UnivariateBasis::*, UnivariatePolynomial}, + util::{ + arithmetic::{ + batch_projective_to_affine, div_ceil, powers, squares, variable_base_msm, CurveAffine, + Field, Group, + }, + chain, izip, + parallel::parallelize, + transcript::{TranscriptRead, TranscriptWrite}, + Deserialize, DeserializeOwned, Itertools, Serialize, + }, + Error, +}; +use rand::RngCore; +use std::{borrow::Cow, iter, marker::PhantomData}; + +#[derive(Clone, Debug)] +pub struct UnivariateHyrax(PhantomData); + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct UnivariateHyraxParam { + k: usize, + batch_k: usize, + row_k: usize, + ipa: UnivariateIpaParam, +} + +impl UnivariateHyraxParam { + pub fn k(&self) -> usize { + self.k + } + + pub fn degree(&self) -> usize { + (1 << self.k) - 1 + } + + pub fn batch_k(&self) -> usize { + self.batch_k + } + + pub fn row_k(&self) -> usize { + self.row_k + } + + pub fn row_len(&self) -> usize { + 1 << self.row_k + } + + pub fn num_chunks(&self) -> usize { + 1 << (self.k - self.row_k) + } + + pub fn monomial(&self) -> &[C] { + self.ipa.monomial() + } + + pub fn lagrange(&self) -> &[C] { + self.ipa.lagrange() + } + + pub fn h(&self) -> &C { + self.ipa.h() + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct UnivariateHyraxVerifierParam { + k: usize, + batch_k: usize, + row_k: usize, + ipa: UnivariateIpaVerifierParam, +} + +impl UnivariateHyraxVerifierParam { + pub fn k(&self) -> usize { + self.k + } + + pub fn row_k(&self) -> usize { + self.row_k + } + + pub fn num_chunks(&self) -> usize { + 1 << (self.k - self.row_k) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct UnivariateHyraxCommitment(pub Vec); + +impl Default for UnivariateHyraxCommitment { + fn default() -> Self { + Self(Vec::new()) + } +} + +impl AsRef<[C]> for UnivariateHyraxCommitment { + fn as_ref(&self) -> &[C] { + &self.0 + } +} + +// TODO: Batch all MSMs into one +impl Additive for UnivariateHyraxCommitment { + fn msm<'a, 'b>( + scalars: impl IntoIterator, + bases: impl IntoIterator, + ) -> Self { + let (scalars, bases) = scalars + .into_iter() + .zip_eq(bases) + .filter_map(|(scalar, bases)| (bases != &Self::default()).then_some((scalar, bases))) + .unzip::<_, _, Vec<_>, Vec<_>>(); + + let num_chunks = bases[0].0.len(); + for bases in bases.iter() { + assert_eq!(bases.0.len(), num_chunks); + } + + let mut output = vec![C::CurveExt::identity(); num_chunks]; + parallelize(&mut output, |(output, start)| { + for (output, idx) in output.iter_mut().zip(start..) { + *output = variable_base_msm(scalars.clone(), bases.iter().map(|base| &base.0[idx])) + } + }); + UnivariateHyraxCommitment(batch_projective_to_affine(&output)) + } +} + +impl PolynomialCommitmentScheme for UnivariateHyrax +where + C: CurveAffine + Serialize + DeserializeOwned, + C::ScalarExt: Serialize + DeserializeOwned, +{ + type Param = UnivariateHyraxParam; + type ProverParam = UnivariateHyraxParam; + type VerifierParam = UnivariateHyraxVerifierParam; + type Polynomial = UnivariatePolynomial; + type Commitment = UnivariateHyraxCommitment; + type CommitmentChunk = C; + + fn setup(poly_size: usize, batch_size: usize, rng: impl RngCore) -> Result { + // TODO: Support arbitrary degree. + assert!(poly_size.is_power_of_two()); + assert!(batch_size > 0 && batch_size <= poly_size); + + let k = poly_size.ilog2() as usize; + let batch_k = (poly_size * batch_size).next_power_of_two().ilog2() as usize; + let row_k = div_ceil(batch_k, 2); + + let ipa = UnivariateIpa::setup(1 << row_k, 0, rng)?; + + Ok(Self::Param { + k, + batch_k, + row_k, + ipa, + }) + } + + fn trim( + param: &Self::Param, + poly_size: usize, + batch_size: usize, + ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> { + assert!(poly_size.is_power_of_two()); + assert!(batch_size > 0 && batch_size <= poly_size); + + let k = poly_size.ilog2() as usize; + let batch_k = (poly_size * batch_size).next_power_of_two().ilog2() as usize; + let row_k = div_ceil(batch_k, 2); + if param.row_k() < row_k { + return Err(err_too_large_deree("trim", param.degree(), poly_size - 1)); + } + + let (ipa_pp, ipa_vp) = UnivariateIpa::trim(¶m.ipa, 1 << row_k, 0)?; + + let pp = Self::ProverParam { + k, + batch_k, + row_k, + ipa: ipa_pp, + }; + let vp = Self::VerifierParam { + k, + batch_k, + row_k, + ipa: ipa_vp, + }; + Ok((pp, vp)) + } + + fn commit(pp: &Self::ProverParam, poly: &Self::Polynomial) -> Result { + validate_input("commit", pp.degree(), [poly])?; + + let bases = match poly.basis() { + Monomial => pp.monomial(), + Lagrange => pp.lagrange(), + }; + + let row_len = pp.row_len(); + let scalars = poly.coeffs(); + let comm = { + let mut comm = vec![C::CurveExt::identity(); pp.num_chunks()]; + parallelize(&mut comm, |(comm, start)| { + for (comm, offset) in comm.iter_mut().zip((start * row_len..).step_by(row_len)) { + let row = &scalars[offset..(offset + row_len).min(scalars.len())]; + *comm = variable_base_msm(row, &bases[..row.len()]); + } + }); + batch_projective_to_affine(&comm) + }; + + Ok(UnivariateHyraxCommitment(comm)) + } + + fn batch_commit<'a>( + pp: &Self::ProverParam, + polys: impl IntoIterator, + ) -> Result, Error> { + let polys = polys.into_iter().collect_vec(); + if polys.is_empty() { + return Ok(Vec::new()); + } + validate_input("batch commit", pp.degree(), polys.iter().copied())?; + + let row_len = pp.row_len(); + let scalars = polys + .iter() + .flat_map(|poly| { + chain![poly.coeffs().chunks(row_len), iter::repeat([].as_slice())] + .take(pp.num_chunks()) + }) + .collect_vec(); + let comms = { + let mut comms = vec![C::CurveExt::identity(); scalars.len()]; + parallelize(&mut comms, |(comms, start)| { + for (comm, row) in comms.iter_mut().zip(&scalars[start..]) { + *comm = variable_base_msm(*row, &pp.monomial()[..row.len()]); + } + }); + batch_projective_to_affine(&comms) + }; + + Ok(comms + .into_iter() + .chunks(pp.num_chunks()) + .into_iter() + .map(|comm| UnivariateHyraxCommitment(comm.collect_vec())) + .collect_vec()) + } + + // TODO: Batch all MSMs into one + fn open( + pp: &Self::ProverParam, + poly: &Self::Polynomial, + comm: &Self::Commitment, + point: &Point, + eval: &C::Scalar, + transcript: &mut impl TranscriptWrite, + ) -> Result<(), Error> { + assert_eq!(poly.basis(), Monomial); + + validate_input("open", pp.degree(), [poly])?; + + if cfg!(feature = "sanity-check") { + assert_eq!(comm.0.len(), pp.num_chunks()); + assert_eq!(Self::commit(pp, poly).unwrap().0, comm.0); + assert_eq!(poly.evaluate(point), *eval); + } + + let row_len = pp.row_len(); + let scalars = powers(squares(*point).nth(pp.row_k()).unwrap()) + .take(pp.num_chunks()) + .collect_vec(); + let poly = if pp.num_chunks() == 1 { + Cow::Borrowed(poly) + } else { + let mut coeffs = vec![C::Scalar::ZERO; row_len]; + if let Some(row) = poly.coeffs().chunks(row_len).next() { + coeffs[..row.len()].copy_from_slice(row); + } + izip!(&scalars, poly.coeffs().chunks(row_len)) + .skip(1) + .for_each(|(scalar, row)| { + parallelize(&mut coeffs, |(coeffs, start)| { + let scalar = *scalar; + izip!(coeffs, &row[start..]).for_each(|(lhs, rhs)| *lhs += scalar * rhs) + }); + }); + Cow::Owned(UnivariatePolynomial::monomial(coeffs)) + }; + let comm = if cfg!(feature = "sanity-check") { + UnivariateIpaCommitment(if pp.num_chunks() == 1 { + comm.0[0] + } else { + variable_base_msm(&scalars, &comm.0).into() + }) + } else { + UnivariateIpaCommitment::default() + }; + + UnivariateIpa::open(&pp.ipa, &poly, &comm, point, eval, transcript) + } + + fn batch_open<'a>( + pp: &Self::ProverParam, + polys: impl IntoIterator, + comms: impl IntoIterator, + points: &[Point], + evals: &[Evaluation], + transcript: &mut impl TranscriptWrite, + ) -> Result<(), Error> { + let polys = polys.into_iter().collect_vec(); + let comms = comms.into_iter().collect_vec(); + additive::batch_open::<_, Self>(pp, polys, comms, points, evals, transcript) + } + + fn read_commitments( + vp: &Self::VerifierParam, + num_polys: usize, + transcript: &mut impl TranscriptRead, + ) -> Result, Error> { + iter::repeat_with(|| { + transcript + .read_commitments(vp.num_chunks()) + .map(UnivariateHyraxCommitment) + }) + .take(num_polys) + .collect() + } + + fn verify( + vp: &Self::VerifierParam, + comm: &Self::Commitment, + point: &Point, + eval: &C::Scalar, + transcript: &mut impl TranscriptRead, + ) -> Result<(), Error> { + assert_eq!(comm.0.len(), vp.num_chunks()); + + let comm = { + UnivariateIpaCommitment(if vp.num_chunks() == 1 { + comm.0[0] + } else { + let scalars = powers(squares(*point).nth(vp.row_k()).unwrap()) + .take(vp.num_chunks()) + .collect_vec(); + variable_base_msm(&scalars, &comm.0).into() + }) + }; + + UnivariateIpa::verify(&vp.ipa, &comm, point, eval, transcript) + } + + fn batch_verify<'a>( + vp: &Self::VerifierParam, + comms: impl IntoIterator, + points: &[Point], + evals: &[Evaluation], + transcript: &mut impl TranscriptRead, + ) -> Result<(), Error> { + let comms = comms.into_iter().collect_vec(); + additive::batch_verify::<_, Self>(vp, comms, points, evals, transcript) + } +} + +#[cfg(test)] +mod test { + use crate::{ + pcs::{ + test::{run_batch_commit_open_verify, run_commit_open_verify}, + univariate::hyrax::UnivariateHyrax, + }, + util::transcript::Keccak256Transcript, + }; + use halo2_curves::pasta::pallas::Affine; + + type Pcs = UnivariateHyrax; + + #[test] + fn commit_open_verify() { + run_commit_open_verify::<_, Pcs, Keccak256Transcript<_>>(); + } + + #[test] + fn batch_commit_open_verify() { + run_batch_commit_open_verify::<_, Pcs, Keccak256Transcript<_>>(); + } +} diff --git a/plonkish_backend/src/pcs/univariate/ipa.rs b/plonkish_backend/src/pcs/univariate/ipa.rs new file mode 100644 index 00000000..c810c12d --- /dev/null +++ b/plonkish_backend/src/pcs/univariate/ipa.rs @@ -0,0 +1,449 @@ +use crate::{ + pcs::{ + univariate::{additive, err_too_large_deree, monomial_g_to_lagrange_g, validate_input}, + Additive, Evaluation, Point, PolynomialCommitmentScheme, + }, + poly::{ + multilinear, + univariate::{UnivariateBasis::*, UnivariatePolynomial}, + }, + util::{ + arithmetic::{ + batch_projective_to_affine, inner_product, powers, squares, variable_base_msm, Curve, + CurveAffine, CurveExt, Field, Group, PrimeField, + }, + chain, izip, + parallel::parallelize, + transcript::{TranscriptRead, TranscriptWrite}, + Deserialize, DeserializeOwned, Either, Itertools, Serialize, + }, + Error, +}; +use halo2_curves::group::ff::BatchInvert; +use rand::RngCore; +use std::{borrow::Cow, iter, marker::PhantomData, slice}; + +#[derive(Clone, Debug)] +pub struct UnivariateIpa(PhantomData); + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct UnivariateIpaParam { + k: usize, + monomial: Vec, + lagrange: Vec, + h: C, +} + +impl UnivariateIpaParam { + pub fn k(&self) -> usize { + self.k + } + + pub fn degree(&self) -> usize { + self.monomial.len() - 1 + } + + pub fn monomial(&self) -> &[C] { + &self.monomial + } + + pub fn lagrange(&self) -> &[C] { + &self.lagrange + } + + pub fn h(&self) -> &C { + &self.h + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct UnivariateIpaVerifierParam { + k: usize, + monomial: Vec, + h: C, +} + +impl UnivariateIpaVerifierParam { + pub fn k(&self) -> usize { + self.k + } + + pub fn h(&self) -> &C { + &self.h + } + + pub fn monomial(&self) -> &[C] { + &self.monomial + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct UnivariateIpaCommitment(pub C); + +impl Default for UnivariateIpaCommitment { + fn default() -> Self { + Self(C::identity()) + } +} + +impl AsRef<[C]> for UnivariateIpaCommitment { + fn as_ref(&self) -> &[C] { + slice::from_ref(&self.0) + } +} + +impl AsRef for UnivariateIpaCommitment { + fn as_ref(&self) -> &C { + &self.0 + } +} + +impl From for UnivariateIpaCommitment { + fn from(comm: C) -> Self { + Self(comm) + } +} + +impl Additive for UnivariateIpaCommitment { + fn msm<'a, 'b>( + scalars: impl IntoIterator, + bases: impl IntoIterator, + ) -> Self { + let scalars = scalars.into_iter().collect_vec(); + let bases = bases.into_iter().map(|base| &base.0).collect_vec(); + UnivariateIpaCommitment(variable_base_msm(scalars, bases).to_affine()) + } +} + +impl PolynomialCommitmentScheme for UnivariateIpa +where + C: CurveAffine + Serialize + DeserializeOwned, + C::ScalarExt: Serialize + DeserializeOwned, +{ + type Param = UnivariateIpaParam; + type ProverParam = UnivariateIpaParam; + type VerifierParam = UnivariateIpaVerifierParam; + type Polynomial = UnivariatePolynomial; + type Commitment = UnivariateIpaCommitment; + type CommitmentChunk = C; + + fn setup(poly_size: usize, _: usize, _: impl RngCore) -> Result { + // TODO: Support arbitrary degree. + assert!(poly_size.is_power_of_two()); + assert!(poly_size.ilog2() <= C::Scalar::S); + + let k = poly_size.ilog2() as usize; + + let monomial = { + let mut g = vec![C::Curve::identity(); poly_size]; + parallelize(&mut g, |(g, start)| { + let hasher = C::CurveExt::hash_to_curve("UnivariateIpa::setup"); + for (g, idx) in g.iter_mut().zip(start as u32..) { + let mut message = [0u8; 5]; + message[1..5].copy_from_slice(&idx.to_le_bytes()); + *g = hasher(&message); + } + }); + batch_projective_to_affine(&g) + }; + + let lagrange = monomial_g_to_lagrange_g(&monomial); + + let hasher = C::CurveExt::hash_to_curve("UnivariateIpa::setup"); + let h = hasher(&[1]).to_affine(); + + Ok(Self::Param { + k, + monomial, + lagrange, + h, + }) + } + + fn trim( + param: &Self::Param, + poly_size: usize, + _: usize, + ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> { + assert!(poly_size.is_power_of_two()); + + let k = poly_size.ilog2() as usize; + + if param.monomial.len() < poly_size { + return Err(err_too_large_deree("trim", param.degree(), poly_size - 1)); + } + + let monomial = param.monomial[..poly_size].to_vec(); + let lagrange = if param.lagrange.len() == poly_size { + param.lagrange.clone() + } else { + monomial_g_to_lagrange_g(&monomial) + }; + + let pp = Self::ProverParam { + k, + monomial: monomial.clone(), + lagrange, + h: param.h, + }; + let vp = Self::VerifierParam { + k, + monomial, + h: param.h, + }; + Ok((pp, vp)) + } + + fn commit(pp: &Self::ProverParam, poly: &Self::Polynomial) -> Result { + validate_input("commit", pp.degree(), [poly])?; + + let coeffs = poly.coeffs(); + let bases = match poly.basis() { + Monomial => pp.monomial(), + Lagrange => pp.lagrange(), + }; + Ok(variable_base_msm(coeffs, &bases[..coeffs.len()]).into()).map(UnivariateIpaCommitment) + } + + fn batch_commit<'a>( + pp: &Self::ProverParam, + polys: impl IntoIterator, + ) -> Result, Error> { + polys + .into_iter() + .map(|poly| Self::commit(pp, poly)) + .collect() + } + + fn open( + pp: &Self::ProverParam, + poly: &Self::Polynomial, + comm: &Self::Commitment, + point: &Point, + eval: &C::Scalar, + transcript: &mut impl TranscriptWrite, + ) -> Result<(), Error> { + assert_eq!(poly.basis(), Monomial); + + validate_input("open", pp.degree(), [poly])?; + + if cfg!(feature = "sanity-check") { + assert_eq!(Self::commit(pp, poly).unwrap().0, comm.0); + assert_eq!(poly.evaluate(point), *eval); + } + + let bases = pp.monomial(); + let coeffs = chain![poly.coeffs().iter().cloned(), iter::repeat(C::Scalar::ZERO)] + .take(bases.len()) + .collect_vec(); + let zs = powers(*point).take(bases.len()).collect_vec(); + prove_bulletproof_reduction(bases, pp.h(), coeffs, zs, transcript) + } + + fn batch_open<'a>( + pp: &Self::ProverParam, + polys: impl IntoIterator, + comms: impl IntoIterator, + points: &[Point], + evals: &[Evaluation], + transcript: &mut impl TranscriptWrite, + ) -> Result<(), Error> { + let polys = polys.into_iter().collect_vec(); + let comms = comms.into_iter().collect_vec(); + additive::batch_open::<_, Self>(pp, polys, comms, points, evals, transcript) + } + + fn read_commitments( + _: &Self::VerifierParam, + num_polys: usize, + transcript: &mut impl TranscriptRead, + ) -> Result, Error> { + let comms = transcript.read_commitments(num_polys)?; + Ok(comms.into_iter().map(UnivariateIpaCommitment).collect()) + } + + fn verify( + vp: &Self::VerifierParam, + comm: &Self::Commitment, + point: &Point, + eval: &C::Scalar, + transcript: &mut impl TranscriptRead, + ) -> Result<(), Error> { + let bases = vp.monomial(); + let point = Either::Left(point); + verify_bulletproof_reduction(bases, vp.h(), comm, point, eval, transcript) + } + + fn batch_verify<'a>( + vp: &Self::VerifierParam, + comms: impl IntoIterator, + points: &[Point], + evals: &[Evaluation], + transcript: &mut impl TranscriptRead, + ) -> Result<(), Error> { + let comms = comms.into_iter().collect_vec(); + additive::batch_verify::<_, Self>(vp, comms, points, evals, transcript) + } +} + +pub(crate) fn prove_bulletproof_reduction<'a, C: CurveAffine>( + bases: impl Into>, + h: &C, + coeffs: impl Into>, + zs: impl Into>, + transcript: &mut impl TranscriptWrite, +) -> Result<(), Error> { + let mut bases = bases.into().into_owned(); + let mut coeffs = coeffs.into().into_owned(); + let mut zs = zs.into().into_owned(); + + assert_eq!(bases.len(), coeffs.len()); + assert_eq!(bases.len(), zs.len()); + assert!(bases.len().is_power_of_two()); + + let xi_0 = transcript.squeeze_challenge(); + let h_prime = (*h * xi_0).to_affine(); + + let k = bases.len().ilog2() as usize; + for i in 0..k { + let mid = 1 << (k - i - 1); + + let (bases_l, bases_r) = bases.split_at(mid); + let (coeffs_l, coeffs_r) = coeffs.split_at(mid); + let (zs_l, zs_r) = zs.split_at(mid); + let (c_l, c_r) = (inner_product(coeffs_r, zs_l), inner_product(coeffs_l, zs_r)); + let l_i = variable_base_msm(chain![coeffs_r, [&c_l]], chain![bases_l, [&h_prime]]); + let r_i = variable_base_msm(chain![coeffs_l, [&c_r]], chain![bases_r, [&h_prime]]); + transcript.write_commitment(&l_i.to_affine())?; + transcript.write_commitment(&r_i.to_affine())?; + + let xi_i = transcript.squeeze_challenge(); + let xi_i_inv = xi_i.invert().unwrap(); + + let (bases_l, bases_r) = bases.split_at_mut(mid); + let (coeffs_l, coeffs_r) = coeffs.split_at_mut(mid); + let (zs_l, zs_r) = zs.split_at_mut(mid); + parallelize(bases_l, |(bases_l, start)| { + let mut tmp = Vec::with_capacity(bases_l.len()); + for (lhs, rhs) in bases_l.iter().zip(bases_r[start..].iter()) { + tmp.push(lhs.to_curve() + *rhs * xi_i); + } + C::Curve::batch_normalize(&tmp, bases_l); + }); + parallelize(coeffs_l, |(coeffs_l, start)| { + for (lhs, rhs) in coeffs_l.iter_mut().zip(coeffs_r[start..].iter()) { + *lhs += xi_i_inv * rhs; + } + }); + parallelize(zs_l, |(zs_l, start)| { + for (lhs, rhs) in zs_l.iter_mut().zip(zs_r[start..].iter()) { + *lhs += xi_i * rhs; + } + }); + bases.truncate(mid); + coeffs.truncate(mid); + zs.truncate(mid); + } + + transcript.write_field_element(&coeffs[0])?; + + Ok(()) +} + +pub(crate) fn verify_bulletproof_reduction( + bases: &[C], + h: &C, + comm: impl AsRef, + point: Either<&C::Scalar, &[C::Scalar]>, + eval: &C::Scalar, + transcript: &mut impl TranscriptRead, +) -> Result<(), Error> { + assert!(bases.len().is_power_of_two()); + if let Either::Right(point) = point { + assert_eq!(1 << point.len(), bases.len()); + } + + let k = bases.len().ilog2() as usize; + + let xi_0 = transcript.squeeze_challenge(); + + let (ls, rs, xis) = iter::repeat_with(|| { + Ok(( + transcript.read_commitment()?, + transcript.read_commitment()?, + transcript.squeeze_challenge(), + )) + }) + .take(k) + .collect::, _>>()? + .into_iter() + .multiunzip::<(Vec<_>, Vec<_>, Vec<_>)>(); + let neg_c = -transcript.read_field_element()?; + + let xi_invs = { + let mut xi_invs = xis.clone(); + xi_invs.batch_invert(); + xi_invs + }; + let neg_c_h = h_coeffs(neg_c, &xis); + let (kind, neg_c_h_eval) = match point { + Either::Left(point) => ("univariate", h_eval(neg_c, &xis, point)), + Either::Right(point) => ("multivariate", multilinear::evaluate(&neg_c_h, point)), + }; + let u = xi_0 * (neg_c_h_eval + eval); + let scalars = chain![&xi_invs, &xis, &neg_c_h, [&u]]; + let bases = chain![&ls, &rs, bases, [h]]; + bool::from((variable_base_msm(scalars, bases) + comm.as_ref()).is_identity()) + .then_some(()) + .ok_or_else(|| Error::InvalidPcsOpen(format!("Invalid {kind} IPA open"))) +} + +pub(crate) fn h_coeffs(init: F, xi: &[F]) -> Vec { + assert!(!xi.is_empty()); + + let mut coeffs = vec![F::ZERO; 1 << xi.len()]; + coeffs[0] = init; + + for (len, xi) in xi.iter().rev().enumerate().map(|(i, xi)| (1 << i, xi)) { + let (left, right) = coeffs.split_at_mut(len); + let right = &mut right[0..len]; + right.copy_from_slice(left); + parallelize(right, |(right, _)| { + for coeff in right { + *coeff *= xi; + } + }); + } + + coeffs +} + +fn h_eval(init: F, xis: &[F], x: &F) -> F { + izip!(squares(*x), xis.iter().rev()) + .map(|(square_of_x, xi)| F::ONE + square_of_x * xi) + .fold(init, |acc, item| acc * item) +} + +#[cfg(test)] +mod test { + use crate::{ + pcs::{ + test::{run_batch_commit_open_verify, run_commit_open_verify}, + univariate::ipa::UnivariateIpa, + }, + util::transcript::Keccak256Transcript, + }; + use halo2_curves::pasta::pallas::Affine; + + type Pcs = UnivariateIpa; + + #[test] + fn commit_open_verify() { + run_commit_open_verify::<_, Pcs, Keccak256Transcript<_>>(); + } + + #[test] + fn batch_commit_open_verify() { + run_batch_commit_open_verify::<_, Pcs, Keccak256Transcript<_>>(); + } +} diff --git a/plonkish_backend/src/pcs/univariate/kzg.rs b/plonkish_backend/src/pcs/univariate/kzg.rs index 374e7182..646cca43 100644 --- a/plonkish_backend/src/pcs/univariate/kzg.rs +++ b/plonkish_backend/src/pcs/univariate/kzg.rs @@ -1,6 +1,6 @@ use crate::{ pcs::{ - univariate::{additive, err_too_large_deree, monomial_g1_to_lagrange_g1, validate_input}, + univariate::{additive, err_too_large_deree, monomial_g_to_lagrange_g, validate_input}, Additive, Evaluation, Point, PolynomialCommitmentScheme, }, poly::univariate::{UnivariateBasis::*, UnivariatePolynomial}, @@ -45,12 +45,17 @@ impl UnivariateKzg { deserialize = "M::G1Affine: DeserializeOwned, M::G2Affine: DeserializeOwned", ))] pub struct UnivariateKzgParam { + k: usize, monomial_g1: Vec, lagrange_g1: Vec, powers_of_s_g2: Vec, } impl UnivariateKzgParam { + pub fn k(&self) -> usize { + self.k + } + pub fn degree(&self) -> usize { self.monomial_g1.len() - 1 } @@ -78,18 +83,28 @@ impl UnivariateKzgParam { deserialize = "M::G1Affine: DeserializeOwned", ))] pub struct UnivariateKzgProverParam { + k: usize, monomial_g1: Vec, lagrange_g1: Vec, } impl UnivariateKzgProverParam { - pub(crate) fn new(monomial_g1: Vec, lagrange_g1: Vec) -> Self { + pub(crate) fn new( + k: usize, + monomial_g1: Vec, + lagrange_g1: Vec, + ) -> Self { Self { + k, monomial_g1, lagrange_g1, } } + pub fn k(&self) -> usize { + self.k + } + pub fn degree(&self) -> usize { self.monomial_g1.len() - 1 } @@ -222,6 +237,7 @@ where }; Ok(Self::Param { + k: poly_size.ilog2() as usize, monomial_g1, lagrange_g1, powers_of_s_g2, @@ -243,9 +259,10 @@ where let lagrange_g1 = if param.lagrange_g1.len() == poly_size { param.lagrange_g1.clone() } else { - monomial_g1_to_lagrange_g1(&monomial_g1) + monomial_g_to_lagrange_g(&monomial_g1) }; - let pp = Self::ProverParam::new(monomial_g1, lagrange_g1); + + let pp = Self::ProverParam::new(poly_size.ilog2() as usize, monomial_g1, lagrange_g1); let vp = Self::VerifierParam { g1: param.g1(), g2: param.g2(), @@ -281,6 +298,8 @@ where eval: &M::Scalar, transcript: &mut impl TranscriptWrite, ) -> Result<(), Error> { + assert_eq!(poly.basis(), Monomial); + validate_input("open", pp.degree(), [poly])?; if cfg!(feature = "sanity-check") { @@ -323,9 +342,8 @@ where num_polys: usize, transcript: &mut impl TranscriptRead, ) -> Result, Error> { - transcript - .read_commitments(num_polys) - .map(|comms| comms.into_iter().map(UnivariateKzgCommitment).collect_vec()) + let comms = transcript.read_commitments(num_polys)?; + Ok(comms.into_iter().map(UnivariateKzgCommitment).collect()) } fn verify( diff --git a/plonkish_backend/src/poly/multilinear.rs b/plonkish_backend/src/poly/multilinear.rs index 3fabda24..a7b24337 100644 --- a/plonkish_backend/src/poly/multilinear.rs +++ b/plonkish_backend/src/poly/multilinear.rs @@ -92,7 +92,7 @@ impl Polynomial for MultilinearPolynomial { } fn evaluate(&self, point: &Self::Point) -> F { - MultilinearPolynomial::evaluate(self, point.as_slice()) + MultilinearPolynomial::evaluate(self, point) } #[cfg(any(test, feature = "benchmark"))] @@ -165,23 +165,7 @@ impl MultilinearPolynomial { pub fn evaluate(&self, x: &[F]) -> F { assert_eq!(x.len(), self.num_vars); - - let mut evals = Cow::Borrowed(self.evals()); - let mut bits = Vec::new(); - let mut buf = Vec::with_capacity(self.evals.len() >> 1); - for x_i in x.iter() { - if x_i == &F::ZERO || x_i == &F::ONE { - bits.push(x_i == &F::ONE); - continue; - } - - let distance = bits.len() + 1; - let skip = usize_from_bits_le(&bits); - merge_in_place(&mut evals, x_i, distance, skip, &mut buf); - bits.clear(); - } - - evals[usize_from_bits_le(&bits)] + evaluate(&self.evals, x) } pub fn fix_last_vars(&self, x: &[F]) -> Self { @@ -457,6 +441,27 @@ impl, P: Borrow>> Sum<(BF, P)> impl_index!(MultilinearPolynomial, evals); +pub(crate) fn evaluate(evals: &[F], x: &[F]) -> F { + assert_eq!(1 << x.len(), evals.len()); + + let mut evals = Cow::Borrowed(evals); + let mut bits = Vec::new(); + let mut buf = Vec::with_capacity(evals.len() >> 1); + for x_i in x.iter() { + if x_i == &F::ZERO || x_i == &F::ONE { + bits.push(x_i == &F::ONE); + continue; + } + + let distance = bits.len() + 1; + let skip = usize_from_bits_le(&bits); + merge_in_place(&mut evals, x_i, distance, skip, &mut buf); + bits.clear(); + } + + evals[usize_from_bits_le(&bits)] +} + pub fn rotation_eval(x: &[F], rotation: Rotation, evals_for_rotation: &[F]) -> F { if rotation == Rotation::cur() { assert!(evals_for_rotation.len() == 1); diff --git a/plonkish_backend/src/util.rs b/plonkish_backend/src/util.rs index 30df4dcb..328b0d0b 100644 --- a/plonkish_backend/src/util.rs +++ b/plonkish_backend/src/util.rs @@ -6,7 +6,7 @@ pub mod parallel; mod timer; pub mod transcript; -pub use itertools::{chain, izip, Itertools}; +pub use itertools::{chain, izip, Either, Itertools}; pub use num_bigint::BigUint; pub use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer}; pub use timer::{end_timer, start_timer, start_unit_timer};