From 5a92f33353fe9eae292c5ad98bf9167deae4e946 Mon Sep 17 00:00:00 2001 From: joe bebel Date: Tue, 4 May 2021 03:49:48 -0700 Subject: [PATCH 01/11] fast amortized kzg commitments --- src/error.rs | 5 + src/kzg10/data_structures.rs | 37 ++++ src/kzg10/fastpoly.rs | 372 +++++++++++++++++++++++++++++++++++ src/kzg10/mod.rs | 262 +++++++++++++++++++++++- 4 files changed, 673 insertions(+), 3 deletions(-) create mode 100644 src/kzg10/fastpoly.rs diff --git a/src/error.rs b/src/error.rs index de7091eb..72b03851 100644 --- a/src/error.rs +++ b/src/error.rs @@ -93,6 +93,9 @@ pub enum Error { /// Index of the offending polynomial. label: String, }, + + /// Attempt to open_amortized on too large a domain + AmortizedOpeningTooLarge(usize), } impl core::fmt::Display for Error { @@ -179,6 +182,8 @@ impl core::fmt::Display for Error { support up to degree ({:?})", label, poly_degree, supported_degree ), Error::IncorrectInputLength(err) => write!(f, "{}", err), + Error::AmortizedOpeningTooLarge(s) => write!(f, "tried to open_amortized on too large domain of size {:?}", s), + } } } diff --git a/src/kzg10/data_structures.rs b/src/kzg10/data_structures.rs index 4bb95430..1037ec94 100644 --- a/src/kzg10/data_structures.rs +++ b/src/kzg10/data_structures.rs @@ -21,6 +21,8 @@ pub struct UniversalParams { pub h: E::G2Affine, /// \beta times the above generator of G2. pub beta_h: E::G2Affine, + /// Group elements of the form `{ \beta^i H }`, where `i` ranges from 0 to `degree`. + pub powers_of_h: Vec, /// Group elements of the form `{ \beta^i G2 }`, where `i` ranges from `0` to `-degree`. pub neg_powers_of_h: BTreeMap, /// The generator of G2, prepared for use in pairings. @@ -95,6 +97,7 @@ impl CanonicalDeserialize for UniversalParams { powers_of_gamma_g, h, beta_h, + powers_of_h: vec![], neg_powers_of_h, prepared_h, prepared_beta_h, @@ -118,6 +121,7 @@ impl CanonicalDeserialize for UniversalParams { powers_of_gamma_g, h, beta_h, + powers_of_h: vec![], neg_powers_of_h, prepared_h, prepared_beta_h, @@ -139,6 +143,7 @@ impl CanonicalDeserialize for UniversalParams { powers_of_gamma_g, h, beta_h, + powers_of_h: vec![], neg_powers_of_h, prepared_h, prepared_beta_h, @@ -557,3 +562,35 @@ impl ToBytes for Proof { .write(&mut writer) } } + +/// Opening proofs of a commitment on a large domain +pub struct AmortizedProof { + /// This is a vector of commitments to the witness polynomials + /// over a domain 1, omega, omega^2, ..., omega^{n-1} + /// where omega is a primitive n'th root of unity + pub w: Vec, + /// Scale factor whose multiplication is deferred until the proofs are combined + pub scale: E::Fr, +} + +impl AmortizedProof { + /// Combine opening proofs onto a subset of the domain + pub fn combine_at_evals( + &self, + range: &std::ops::Range, // Domain is omega^{start}, ..., omega^{end-1} + evals: &[E::Fr], // Evaluation of polynomial at domain + s: &super::fastpoly::SubproductDomain, // SubproductDomain of the domain + ) -> Proof { + let lagrange_coeff = s.fast_inverse_lagrange_coefficients(); + let mut total = E::G1Projective::zero(); + for (c_i, point) in lagrange_coeff.iter().zip(self.w[range.clone()].iter()) { + total += point.into_affine().mul::(c_i.inverse().unwrap()); + } + Proof { + w: total.into(), + // NOTE: if the ifft had not multiplied by domain_size_inv + //w: total.into_affine().mul::(self.scale).into(), + random_v: None, + } + } +} diff --git a/src/kzg10/fastpoly.rs b/src/kzg10/fastpoly.rs new file mode 100644 index 00000000..50bd8ea9 --- /dev/null +++ b/src/kzg10/fastpoly.rs @@ -0,0 +1,372 @@ +use crate::Error; +use ark_ec::PairingEngine; +use ark_ff::{Field, One, Zero}; +use ark_poly::UVPolynomial; + +use ark_poly::polynomial::univariate::DensePolynomial; + +type P = DensePolynomial; + +/// Computes the inverse of f mod x^l +pub fn inverse_mod_xl(f: &P, l: usize) -> Option> { + //use std::ops::Mul; + //let r = + // std::mem::size_of::() * 8 - (l as u64).leading_zeros() as usize; // ceil(log_2(l)) + + //assert_eq!((l as f64).log2().ceil() as usize, r); + let r = (l as f64).log2().ceil() as usize; //TODO: rounding problems?? + let mut g = DensePolynomial:: { + coeffs: vec![f.coeffs[0].inverse().unwrap()], //todo unwrap + }; + let mut i = 2usize; + for _ in 0..r { + g = &(&g + &g) - &(f * &(&g * &g)); //todo: g*2? + g.coeffs.resize(i, E::Fr::zero()); + i *= 2; + } + Some(g) +} + +/// Computes the rev_m(f) function in place +pub fn rev(f: &mut P, m: usize) { + assert!(f.coeffs.len() - 1 <= m); + for _ in 0..(m - (f.coeffs.len() - 1)) { + f.coeffs.push(E::Fr::zero()); + } + f.reverse(); +} + +/// Divide f by g in nearly linear time +pub fn fast_divide_monic(f: &P, g: &P) -> (P, P) { + if f.coeffs().len() < g.coeffs().len() { + return ( + P:: { + coeffs: vec![E::Fr::zero()], + }, + f.clone(), + ); + } + let m = f.coeffs().len() - g.coeffs().len(); + + let mut rev_f = f.clone(); + let mut rev_g = g.clone(); + rev_f.reverse(); + rev_g.reverse(); + + let mut q = &rev_f * &inverse_mod_xl::(&rev_g, m + 1).unwrap(); + q.coeffs.resize(m + 1, E::Fr::zero()); + rev::(&mut q, m); + let r = f - &(g * &q); + (q, r) +} + +/// The subproduct tree of a polynomial m over a domain u +#[derive(Debug, Clone)] +pub struct SubproductDomain { + /// Domain values + pub u: Vec, + /// Subproduct tree over domain u + pub t: SubproductTree, + /// Derivative of the subproduct polynomial + pub prime: P, // Derivative +} + +impl SubproductDomain { + /// Create a subproduct tree domain + pub fn new(u: Vec) -> SubproductDomain { + let t = SubproductTree::new(&u); + let prime = derivative::(&t.m); + SubproductDomain { u, t, prime } + } + /// evaluate a polynomial over the domain + pub fn fast_evaluate(&self, f: &P) -> Vec { + let mut evals = vec![E::Fr::zero(); self.u.len()]; + self.t.fast_evaluate(f, &self.u, &mut evals); + evals + } + /// interpolate a polynomial over the domain + pub fn fast_interpolate(&self, v: &[E::Fr]) -> P { + self.t.fast_interpolate(&self.u, v) + } + /// compute the inverse of the lagrange coefficients fast + pub fn fast_inverse_lagrange_coefficients(&self) -> Vec { + self.t.fast_inverse_lagrange_coefficients(&self.u) + } + /// compute a linear coefficient of lagrange factors times c_i + pub fn fast_linear_combine(&self, c: &[E::Fr]) -> P { + self.t.fast_linear_combine(&self.u, &c) + } +} + +/// A subproduct tree of the subproduct domain +#[derive(Debug, Clone)] +pub struct SubproductTree { + /// The left child + pub left: Option>>, + /// The right child + pub right: Option>>, + /// The polynomial for this subdomain + pub m: P, +} + +impl SubproductTree { + /// Compute the subproduct tree of m = (x - u_0)*...*(x-u_{n-1}) + pub fn new(u: &[E::Fr]) -> SubproductTree { + if u.len() == 1 { + SubproductTree { + left: None, + right: None, + m: P:: { + coeffs: vec![-u[0], E::Fr::one()], + }, + } + } else { + let n = u.len() / 2; + let (u_0, u_1) = u.split_at(n); + let left = Box::new(SubproductTree::new(u_0)); + let right = Box::new(SubproductTree::new(u_1)); + let m = &left.m * &right.m; + SubproductTree { + left: Some(left), + right: Some(right), + m, + } + } + } + /// Fast evaluate f over this subproduct tree + pub fn fast_evaluate(&self, f: &P, u: &[E::Fr], t: &mut [E::Fr]) { + //todo: assert degree < u.len() + if u.len() == 1 { + t[0] = f.coeffs[0]; + return; + } + + let left = self.left.as_ref().unwrap(); + let right = self.right.as_ref().unwrap(); + + let (q_0, r_0) = fast_divide_monic::(f, &left.m); + let (_, r_1) = fast_divide_monic::(f, &right.m); + + let n = u.len() / 2; + let (u_0, u_1) = u.split_at(n); + let (t_0, t_1) = t.split_at_mut(n); + + left.fast_evaluate(&r_0, u_0, t_0); + right.fast_evaluate(&r_1, u_1, t_1); + } + /// Fast interpolate over this subproduct tree + pub fn fast_interpolate(&self, u: &[E::Fr], v: &[E::Fr]) -> P { + let mut lagrange_coeff = self.fast_inverse_lagrange_coefficients(u); + + for (s_i, v_i) in lagrange_coeff.iter_mut().zip(v.iter()) { + *s_i = s_i.inverse().unwrap() * *v_i; + } + + self.fast_linear_combine(u, &lagrange_coeff) + } + /// Fast compute lagrange coefficients over this subproduct tree + pub fn fast_inverse_lagrange_coefficients(&self, u: &[E::Fr]) -> Vec { + //assert u.len() == degree of s.m + if u.len() == 1 { + return vec![E::Fr::one()]; + } + let mut evals = vec![E::Fr::zero(); u.len()]; + let m_prime = derivative::(&self.m); + self.fast_evaluate(&m_prime, u, &mut evals); + evals + } + /// Fast linear combine over this subproduct tree + pub fn fast_linear_combine(&self, u: &[E::Fr], c: &[E::Fr]) -> P { + if u.len() == 1 { + return P:: { coeffs: vec![c[0]] }; + } + let n = u.len() / 2; + let (u_0, u_1) = u.split_at(n); + let (c_0, c_1) = c.split_at(n); + + let left = self.left.as_ref().unwrap(); + let right = self.right.as_ref().unwrap(); + let r_0 = left.fast_linear_combine(u_0, c_0); + let r_1 = right.fast_linear_combine(u_1, c_1); + + &(&right.m * &r_0) + &(&left.m * &r_1) + } +} +/// compute the derivative of polynomial p +pub fn derivative(p: &P) -> P { + let mut coeffs = Vec::with_capacity(p.coeffs().len() - 1); + for (i, c) in p.coeffs.iter().enumerate().skip(1) { + coeffs.push(E::Fr::from(i as u64) * c); + } + P:: { coeffs } +} + +/// Build a vector representation of the circulant matrix of polynomial p +pub fn build_circulant(polynomial: &P, size: usize) -> Vec { + let mut circulant = vec![E::Fr::zero(); 2 * size]; + let coeffs = polynomial.coeffs(); + if size == coeffs.len() - 1 { + circulant[0] = *coeffs.last().unwrap(); + circulant[size] = *coeffs.last().unwrap(); + circulant[size + 1..size + 1 + coeffs.len() - 2] + .copy_from_slice(&coeffs[1..coeffs.len() - 1]); + } else { + circulant[size + 1..size + 1 + coeffs.len() - 1].copy_from_slice(&coeffs[1..]); + } + circulant +} +/// Computes the Toeplitz matrix of polynomial times the vector v +pub fn toeplitz_mul( + polynomial: &P, + v: &[E::G1Affine], + size: usize, +) -> Result<(Vec, E::Fr), Error> { + use ark_ec::AffineCurve; + use ark_poly::EvaluationDomain; + let m = polynomial.coeffs().len() - 1; + let size = ark_std::cmp::max(size, m); + + let domain = ark_poly::Radix2EvaluationDomain::::new(2 * size) + .ok_or(Error::AmortizedOpeningTooLarge(size))?; + + let size = domain.size() / 2; + let mut circulant = build_circulant::(polynomial, size); + + let mut tmp: Vec = Vec::with_capacity(domain.size()); + + for _ in 0..(size - v.len()) { + tmp.push(E::G1Projective::zero()); + } + + for i in v.iter().rev() { + tmp.push(i.into_projective()); + } + + tmp.resize(domain.size(), E::G1Projective::zero()); + domain.fft_in_place(&mut tmp); + domain.fft_in_place(&mut circulant); + + for (i, j) in tmp.iter_mut().zip(circulant.iter()) { + *i *= *j; + } + + // NOTE: it is possible to avoid scaling by domain_size_inv, and defer this to later + domain.ifft_in_place(&mut tmp); + + Ok(( + tmp[..size].to_vec(), + E::Fr::from(domain.size() as u64).inverse().unwrap(), + )) +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_ec::PairingEngine; + use ark_ff::{One, Zero}; + use ark_poly::polynomial::univariate::DensePolynomial; + use ark_poly::Polynomial; + use ark_poly::UVPolynomial; + use ark_std::UniformRand; + + type E = ark_bls12_381::Bls12_381; + type Fr = ::Fr; + #[test] + fn test_inverse() { + let rng = &mut ark_std::test_rng(); + + let degree = 100; + let l = 101; + for _ in 0..100 { + let p = DensePolynomial::::rand(degree, rng); + let p_inv = inverse_mod_xl::(&p, l).unwrap(); + let mut t = &p * &p_inv; + t.coeffs.resize(l, Fr::zero()); + assert_eq!(t.coeffs[0], Fr::one()); + for i in t.iter().skip(1) { + assert_eq!(*i, Fr::zero()); + } + } + } + + #[test] + fn test_divide() { + let rng = &mut ark_std::test_rng(); + + let degree = 100; + let l = 101; + for g_deg in 1..100 { + let f = DensePolynomial::::rand(degree, rng); + let mut g = DensePolynomial::::rand(g_deg, rng); + *g.last_mut().unwrap() = Fr::one(); //monic + + let (q, r) = fast_divide_monic::(&f, &g); + + let t = &(&q * &g) + &r; + + for (i, j) in t.coeffs.iter().zip(f.coeffs.iter()) { + assert_eq!(*i, *j); + } + } + } + + #[test] + fn test_interpolate() { + let rng = &mut ark_std::test_rng(); + for d in 1..100 { + let mut points = vec![]; + let mut evals = vec![]; + for _ in 0..d { + points.push(Fr::rand(rng)); + evals.push(Fr::rand(rng)); + } + + let s = SubproductDomain::::new(points); + let p = s.fast_interpolate(&evals); + + for (x, y) in s.u.iter().zip(evals.iter()) { + assert_eq!(p.evaluate(x), *y) + } + } + } + + #[test] + fn test_linear_combine() { + let rng = &mut ark_std::test_rng(); + for d in 1..100 { + let mut u = vec![]; + let mut c = vec![]; + for _ in 0..d { + u.push(Fr::rand(rng)); + c.push(Fr::rand(rng)); + } + let s = SubproductDomain::::new(u); + let f = s.fast_linear_combine(&c); + + let r = Fr::rand(rng); + let m = s.t.m.evaluate(&r); + let mut total = Fr::zero(); + for (u_i, c_i) in s.u.iter().zip(c.iter()) { + total += m * *c_i / (r - u_i); + } + assert_eq!(f.evaluate(&r), total); + } + } + + #[test] + fn test_inv_lagrange() { + let rng = &mut ark_std::test_rng(); + for d in 1..100 { + let mut u = vec![]; + for _ in 0..d { + u.push(Fr::rand(rng)); + } + let s = SubproductDomain::::new(u); + let f = s.fast_inverse_lagrange_coefficients(); + + for (a, (i, j)) in s.u.iter().zip(f.iter()).enumerate() { + assert_eq!(s.prime.evaluate(i), *j); + } + } + } +} diff --git a/src/kzg10/mod.rs b/src/kzg10/mod.rs index 66b26884..646fef04 100644 --- a/src/kzg10/mod.rs +++ b/src/kzg10/mod.rs @@ -9,7 +9,9 @@ use crate::{BTreeMap, Error, LabeledPolynomial, PCRandomness, ToString, Vec}; use ark_ec::msm::{FixedBaseMSM, VariableBaseMSM}; use ark_ec::{group::Group, AffineCurve, PairingEngine, ProjectiveCurve}; use ark_ff::{One, PrimeField, UniformRand, Zero}; -use ark_poly::UVPolynomial; +use ark_poly::polynomial::univariate::DensePolynomial; +use ark_poly::EvaluationDomain; +use ark_poly::{Polynomial, UVPolynomial}; use ark_std::{format, marker::PhantomData, ops::Div, vec}; use ark_std::rand::RngCore; @@ -18,6 +20,8 @@ use rayon::prelude::*; mod data_structures; pub use data_structures::*; +mod fastpoly; +pub use fastpoly::*; /// `KZG10` is an implementation of the polynomial commitment scheme of /// [Kate, Zaverucha and Goldbgerg][kzg10] @@ -41,6 +45,8 @@ where produce_g2_powers: bool, rng: &mut R, ) -> Result, Error> { + let produce_h_powers = true; + if max_degree < 1 { return Err(Error::DegreeIsZero); } @@ -119,16 +125,37 @@ where end_timer!(neg_powers_of_h_time); + let powers_of_h_time = start_timer!(|| "Generating powers of h in G2"); + let powers_of_h = if produce_h_powers { + let h_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, h); + + let powers_of_h = FixedBaseMSM::multi_scalar_mul::( + scalar_bits, + window_size, + &h_table, + &powers_of_beta, + ); + + E::G2Projective::batch_normalization_into_affine(&powers_of_h) + } else { + let h = h.into_affine(); + let beta_h = h.mul(beta).into_affine(); + vec![h, beta_h] + }; + end_timer!(powers_of_h_time); + let h = h.into_affine(); let beta_h = h.mul(beta).into_affine(); + let prepared_h = h.into(); - let prepared_beta_h = beta_h.into(); + let prepared_beta_h = powers_of_h[1].into(); let pp = UniversalParams { powers_of_g, powers_of_gamma_g, h, beta_h, + powers_of_h, neg_powers_of_h, prepared_h, prepared_beta_h, @@ -191,6 +218,30 @@ where Ok((Commitment(commitment.into()), randomness)) } + /// Outputs a commitment to `polynomial` in G2 + pub fn G2_commit( + powers: &UniversalParams, + polynomial: &DensePolynomial, + ) -> Result { + Self::check_degree_is_too_large(polynomial.degree(), powers.powers_of_h.len())?; + + let commit_time = start_timer!(|| format!( + "Committing to polynomial of degree {} in G2", + polynomial.degree(), + )); + + let (num_leading_zeros, plain_coeffs) = + skip_leading_zeros_and_convert_to_bigints(polynomial); + + let msm_time = start_timer!(|| "MSM to compute commitment to plaintext poly in G2"); + let commitment = VariableBaseMSM::multi_scalar_mul( + &powers.powers_of_h[num_leading_zeros..], + &plain_coeffs, + ); + end_timer!(msm_time); + end_timer!(commit_time); + Ok(commitment) + } /// Compute witness polynomial. /// /// The witness polynomial w(x) the quotient of the division (p(x) - p(z)) / (x - z) @@ -265,7 +316,7 @@ where } /// On input a polynomial `p` and a point `point`, outputs a proof for the same. - pub(crate) fn open<'a>( + pub fn open<'a>( powers: &Powers, p: &P, point: P::Point, @@ -290,6 +341,32 @@ where proof } + /// Open a polynomial at an entire evaluation domain simultaneously + pub fn open_amortized<'a>( + pp: &UniversalParams, + p: &DensePolynomial, + domain: &ark_poly::domain::Radix2EvaluationDomain, + ) -> Result, Error> { + Self::check_degree_is_too_large(p.degree(), pp.powers_of_g.len())?; + let open_time = + start_timer!(|| format!("Amortized opening polynomial of degree {}", p.degree())); + + let m = p.coeffs().len() - 1; + let powers_of_g = pp.powers_of_g[0..m].to_vec(); + + let toeplitz_time = start_timer!(|| "Computing Toeplitz product"); + let (mut h, scale) = toeplitz_mul::(p, &powers_of_g, domain.size())?; + + end_timer!(toeplitz_time); + + domain.fft_in_place(&mut h); + + let proofs = AmortizedProof { w: h, scale }; + + end_timer!(open_time); + Ok(proofs) + } + /// Verifies that `value` is the evaluation at `point` of the polynomial /// committed inside `comm`. pub fn check( @@ -427,6 +504,60 @@ where Ok(()) } } + + /// Verifies that `value` is the evaluation at `point` of the polynomial + /// committed inside `comm`. + pub fn check_amortized( + pp: &UniversalParams, + ck: &Powers, + comm: &Commitment, + proof: &Proof, + evals: &[E::Fr], // Evaluations at the SubproductDomain + s: &SubproductDomain, // SubproductDomain of the evaluation domain + ) -> Result { + let evaluation_interpolation_time = start_timer!(|| "Constructing evaluation polynomial"); + + let evaluation_polynomial = s.fast_interpolate(evals); + end_timer!(evaluation_interpolation_time); + let evaluation_commit_time = + start_timer!(|| "Constructing commitment to evaluation polynomial in G1"); + + let evaluation_polynomial_commitment = + KZG10::>::commit(&ck, &evaluation_polynomial, None, None)?; + + end_timer!(evaluation_commit_time); + + let s_commitment_time = + start_timer!(|| "Constructing commitment to SubproductDomain in G2"); + let s_commitment = Self::G2_commit(pp, &s.t.m)?; + end_timer!(s_commitment_time); + Self::check_amortized_with_commitments( + pp, + comm, + proof, + &s_commitment, + &evaluation_polynomial_commitment.0, + ) + } + + /// Check amortized proof with precomputed commitments + pub fn check_amortized_with_commitments( + ck: &UniversalParams, + comm: &Commitment, + proof: &Proof, + s_commitment: &E::G2Projective, // G2 Commitment to the SubproductDomain + evaluation_polynomial_commitment: &Commitment, + ) -> Result { + let check_time = start_timer!(|| "Checking evaluation"); + let inner = comm.0.into_projective() - evaluation_polynomial_commitment.0.into_projective(); + + let lhs = E::pairing(inner, ck.powers_of_h[0]); + + let rhs = E::pairing(proof.w, *s_commitment); + + end_timer!(check_time, || format!("Result: {}", lhs == rhs)); + Ok(lhs == rhs) + } } fn skip_leading_zeros_and_convert_to_bigints>( @@ -653,4 +784,129 @@ mod tests { assert!(p.degree() > max_degree); assert!(KZG_Bls12_381::check_degree_is_too_large(p.degree(), powers.size()).is_err()); } + #[test] + fn test_toeplitz() { + type G1Projective = ::G1Projective; + type G1Affine = ::G1Affine; + + let rng = &mut test_rng(); + for total_weight in 15..30 { + let degree = 2 * total_weight / 3; + + let pp = KZG_Bls12_381::setup(degree, false, rng).unwrap(); + let (ck, vk) = KZG_Bls12_381::trim(&pp, degree).unwrap(); + + for _ in 0..4 { + let polynomial = DensePolynomial::::rand(degree, rng); + + let coeffs = polynomial.coeffs(); + let m = polynomial.coeffs.len() - 1; + + let domain = ark_poly::Radix2EvaluationDomain::::new(total_weight).unwrap(); + let p = ck.powers_of_g[0..m].to_vec(); + let (h, scale): (Vec, Fr) = + toeplitz_mul::(&polynomial, &p, domain.size()).unwrap(); + + let h = h + .iter() + .map( + |p: &G1Projective| (*p).into_affine(), /*. mul(scale).into_affine()*/ + ) + .collect::>(); + + for i in 1..=m { + let mut total = G1Projective::zero(); + for j in 0..=m - i { + total = total + (p[j].mul(coeffs[i + j])); + } + assert_eq!(G1Affine::from(total), h[i - 1]); + } + } + } + } + #[test] + fn test_check_amortized() { + let rng = &mut test_rng(); + let n = 10; + let eval_domain_size = 1 << n; + + let degree = 1 << (n - 1); + + let pp = KZG_Bls12_381::setup(eval_domain_size, false, rng).unwrap(); + let (ck, vk) = KZG_Bls12_381::trim(&pp, degree).unwrap(); + + let p = DensePoly::::rand(degree, rng); + + let (comm, rand) = KZG_Bls12_381::commit(&ck, &p, None, None).unwrap(); + + let domain = ark_poly::Radix2EvaluationDomain::::new(eval_domain_size).unwrap(); + let openings = KZG_Bls12_381::open_amortized(&pp, &p, &domain).unwrap(); + + let evals = p.evaluate_over_domain_by_ref(domain).evals; + + assert!(KZG_Bls12_381::check( + &vk, + &comm, + Fr::one(), + evals[0], + &Proof { + w: openings.w[0].into_affine(), + /*.mul::(openings.scale) // If the ifft does not multiply by domain_size_inv + .into_affine()*/ + random_v: None, + } + ) + .unwrap()); + + let mut whole_domain = Vec::with_capacity(domain.size()); + let mut t = Fr::one(); + for _ in 0..domain.size() { + whole_domain.push(t); + t *= domain.group_gen; + } + + let subproduct_domains = [ + SubproductDomain::new(whole_domain[0..100].to_vec()), + SubproductDomain::new(whole_domain[100..400].to_vec()), + SubproductDomain::new(whole_domain[400..].to_vec()), + ]; + + let combined = [ + openings.combine_at_evals(&(0..100), &evals[0..100], &subproduct_domains[0]), + openings.combine_at_evals(&(100..400), &evals[100..400], &subproduct_domains[1]), + openings.combine_at_evals( + &(400..eval_domain_size), + &evals[400..], + &subproduct_domains[2], + ), + ]; + + assert!(KZG_Bls12_381::check_amortized( + &pp, + &ck, + &comm, + &combined[0], + &evals[0..100], + &subproduct_domains[0] + ) + .unwrap()); + assert!(KZG_Bls12_381::check_amortized( + &pp, + &ck, + &comm, + &combined[1], + &evals[100..400], + &subproduct_domains[1] + ) + .unwrap()); + assert!(KZG_Bls12_381::check_amortized( + &pp, + &ck, + &comm, + &combined[2], + &evals[400..], + &subproduct_domains[2] + ) + .unwrap()); + } } From dbe472ff58da895c63ad57366c327bf838b57c52 Mon Sep 17 00:00:00 2001 From: joe <55120843+joebebel@users.noreply.github.com> Date: Tue, 4 May 2021 18:13:41 -0700 Subject: [PATCH 02/11] Update src/kzg10/mod.rs Co-authored-by: Pratyush Mishra --- src/kzg10/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kzg10/mod.rs b/src/kzg10/mod.rs index 646fef04..96edb8bc 100644 --- a/src/kzg10/mod.rs +++ b/src/kzg10/mod.rs @@ -219,7 +219,7 @@ where } /// Outputs a commitment to `polynomial` in G2 - pub fn G2_commit( + pub fn commit_g2( powers: &UniversalParams, polynomial: &DensePolynomial, ) -> Result { From b5e9b68429660a231f73fcdfa4559df87ee570ec Mon Sep 17 00:00:00 2001 From: joe bebel Date: Tue, 4 May 2021 19:05:27 -0700 Subject: [PATCH 03/11] cleanup suggestions --- src/kzg10/data_structures.rs | 10 +-- src/kzg10/fastpoly.rs | 119 +++++++++++++++++------------------ src/kzg10/mod.rs | 79 +++++++++++------------ src/marlin/marlin_pc/mod.rs | 6 +- src/sonic_pc/mod.rs | 5 +- 5 files changed, 106 insertions(+), 113 deletions(-) diff --git a/src/kzg10/data_structures.rs b/src/kzg10/data_structures.rs index 1037ec94..43356c18 100644 --- a/src/kzg10/data_structures.rs +++ b/src/kzg10/data_structures.rs @@ -575,15 +575,15 @@ pub struct AmortizedProof { impl AmortizedProof { /// Combine opening proofs onto a subset of the domain - pub fn combine_at_evals( + pub fn combine_at_domain( &self, - range: &std::ops::Range, // Domain is omega^{start}, ..., omega^{end-1} - evals: &[E::Fr], // Evaluation of polynomial at domain - s: &super::fastpoly::SubproductDomain, // SubproductDomain of the domain + start: usize, // Domain is omega^{start}, ..., omega^{end-1} + end: usize, + s: &super::fastpoly::SubproductDomain, // SubproductDomain of the domain ) -> Proof { let lagrange_coeff = s.fast_inverse_lagrange_coefficients(); let mut total = E::G1Projective::zero(); - for (c_i, point) in lagrange_coeff.iter().zip(self.w[range.clone()].iter()) { + for (c_i, point) in lagrange_coeff.iter().zip(self.w[start..end].iter()) { total += point.into_affine().mul::(c_i.inverse().unwrap()); } Proof { diff --git a/src/kzg10/fastpoly.rs b/src/kzg10/fastpoly.rs index 50bd8ea9..1d28b9c7 100644 --- a/src/kzg10/fastpoly.rs +++ b/src/kzg10/fastpoly.rs @@ -1,47 +1,46 @@ use crate::Error; use ark_ec::PairingEngine; -use ark_ff::{Field, One, Zero}; +use ark_ff::{FftField, Field, Zero}; use ark_poly::UVPolynomial; -use ark_poly::polynomial::univariate::DensePolynomial; - -type P = DensePolynomial; +use ark_poly::polynomial::univariate::DensePolynomial as Poly; /// Computes the inverse of f mod x^l -pub fn inverse_mod_xl(f: &P, l: usize) -> Option> { +pub fn inverse_mod_xl(f: &Poly, l: usize) -> Option> { //use std::ops::Mul; //let r = // std::mem::size_of::() * 8 - (l as u64).leading_zeros() as usize; // ceil(log_2(l)) //assert_eq!((l as f64).log2().ceil() as usize, r); - let r = (l as f64).log2().ceil() as usize; //TODO: rounding problems?? - let mut g = DensePolynomial:: { + //let r = (l as f64).log2().ceil() as usize; //TODO: rounding problems?? + let r = ark_std::log2(l); + let mut g = Poly:: { coeffs: vec![f.coeffs[0].inverse().unwrap()], //todo unwrap }; let mut i = 2usize; for _ in 0..r { g = &(&g + &g) - &(f * &(&g * &g)); //todo: g*2? - g.coeffs.resize(i, E::Fr::zero()); + g.coeffs.resize(i, F::zero()); i *= 2; } Some(g) } /// Computes the rev_m(f) function in place -pub fn rev(f: &mut P, m: usize) { +pub fn rev(f: &mut Poly, m: usize) { assert!(f.coeffs.len() - 1 <= m); for _ in 0..(m - (f.coeffs.len() - 1)) { - f.coeffs.push(E::Fr::zero()); + f.coeffs.push(F::zero()); } f.reverse(); } /// Divide f by g in nearly linear time -pub fn fast_divide_monic(f: &P, g: &P) -> (P, P) { +pub fn fast_divide_monic(f: &Poly, g: &Poly) -> (Poly, Poly) { if f.coeffs().len() < g.coeffs().len() { return ( - P:: { - coeffs: vec![E::Fr::zero()], + Poly:: { + coeffs: vec![F::zero()], }, f.clone(), ); @@ -53,71 +52,71 @@ pub fn fast_divide_monic(f: &P, g: &P) -> (P, P) { rev_f.reverse(); rev_g.reverse(); - let mut q = &rev_f * &inverse_mod_xl::(&rev_g, m + 1).unwrap(); - q.coeffs.resize(m + 1, E::Fr::zero()); - rev::(&mut q, m); + let mut q = &rev_f * &inverse_mod_xl::(&rev_g, m + 1).unwrap(); + q.coeffs.resize(m + 1, F::zero()); + rev::(&mut q, m); let r = f - &(g * &q); (q, r) } /// The subproduct tree of a polynomial m over a domain u #[derive(Debug, Clone)] -pub struct SubproductDomain { +pub struct SubproductDomain { /// Domain values - pub u: Vec, + pub u: Vec, /// Subproduct tree over domain u - pub t: SubproductTree, + pub t: SubproductTree, /// Derivative of the subproduct polynomial - pub prime: P, // Derivative + pub prime: Poly, // Derivative } -impl SubproductDomain { +impl SubproductDomain { /// Create a subproduct tree domain - pub fn new(u: Vec) -> SubproductDomain { + pub fn new(u: Vec) -> SubproductDomain { let t = SubproductTree::new(&u); - let prime = derivative::(&t.m); + let prime = derivative::(&t.m); SubproductDomain { u, t, prime } } /// evaluate a polynomial over the domain - pub fn fast_evaluate(&self, f: &P) -> Vec { - let mut evals = vec![E::Fr::zero(); self.u.len()]; + pub fn fast_evaluate(&self, f: &Poly) -> Vec { + let mut evals = vec![F::zero(); self.u.len()]; self.t.fast_evaluate(f, &self.u, &mut evals); evals } /// interpolate a polynomial over the domain - pub fn fast_interpolate(&self, v: &[E::Fr]) -> P { + pub fn fast_interpolate(&self, v: &[F]) -> Poly { self.t.fast_interpolate(&self.u, v) } /// compute the inverse of the lagrange coefficients fast - pub fn fast_inverse_lagrange_coefficients(&self) -> Vec { + pub fn fast_inverse_lagrange_coefficients(&self) -> Vec { self.t.fast_inverse_lagrange_coefficients(&self.u) } /// compute a linear coefficient of lagrange factors times c_i - pub fn fast_linear_combine(&self, c: &[E::Fr]) -> P { + pub fn fast_linear_combine(&self, c: &[F]) -> Poly { self.t.fast_linear_combine(&self.u, &c) } } /// A subproduct tree of the subproduct domain #[derive(Debug, Clone)] -pub struct SubproductTree { +pub struct SubproductTree { /// The left child - pub left: Option>>, + pub left: Option>>, /// The right child - pub right: Option>>, + pub right: Option>>, /// The polynomial for this subdomain - pub m: P, + pub m: Poly, } -impl SubproductTree { +impl SubproductTree { /// Compute the subproduct tree of m = (x - u_0)*...*(x-u_{n-1}) - pub fn new(u: &[E::Fr]) -> SubproductTree { + pub fn new(u: &[F]) -> SubproductTree { if u.len() == 1 { SubproductTree { left: None, right: None, - m: P:: { - coeffs: vec![-u[0], E::Fr::one()], + m: Poly:: { + coeffs: vec![-u[0], F::one()], }, } } else { @@ -134,7 +133,7 @@ impl SubproductTree { } } /// Fast evaluate f over this subproduct tree - pub fn fast_evaluate(&self, f: &P, u: &[E::Fr], t: &mut [E::Fr]) { + pub fn fast_evaluate(&self, f: &Poly, u: &[F], t: &mut [F]) { //todo: assert degree < u.len() if u.len() == 1 { t[0] = f.coeffs[0]; @@ -144,8 +143,8 @@ impl SubproductTree { let left = self.left.as_ref().unwrap(); let right = self.right.as_ref().unwrap(); - let (q_0, r_0) = fast_divide_monic::(f, &left.m); - let (_, r_1) = fast_divide_monic::(f, &right.m); + let (_, r_0) = fast_divide_monic::(f, &left.m); + let (_, r_1) = fast_divide_monic::(f, &right.m); let n = u.len() / 2; let (u_0, u_1) = u.split_at(n); @@ -155,7 +154,7 @@ impl SubproductTree { right.fast_evaluate(&r_1, u_1, t_1); } /// Fast interpolate over this subproduct tree - pub fn fast_interpolate(&self, u: &[E::Fr], v: &[E::Fr]) -> P { + pub fn fast_interpolate(&self, u: &[F], v: &[F]) -> Poly { let mut lagrange_coeff = self.fast_inverse_lagrange_coefficients(u); for (s_i, v_i) in lagrange_coeff.iter_mut().zip(v.iter()) { @@ -165,20 +164,20 @@ impl SubproductTree { self.fast_linear_combine(u, &lagrange_coeff) } /// Fast compute lagrange coefficients over this subproduct tree - pub fn fast_inverse_lagrange_coefficients(&self, u: &[E::Fr]) -> Vec { + pub fn fast_inverse_lagrange_coefficients(&self, u: &[F]) -> Vec { //assert u.len() == degree of s.m if u.len() == 1 { - return vec![E::Fr::one()]; + return vec![F::one()]; } - let mut evals = vec![E::Fr::zero(); u.len()]; - let m_prime = derivative::(&self.m); + let mut evals = vec![F::zero(); u.len()]; + let m_prime = derivative::(&self.m); self.fast_evaluate(&m_prime, u, &mut evals); evals } /// Fast linear combine over this subproduct tree - pub fn fast_linear_combine(&self, u: &[E::Fr], c: &[E::Fr]) -> P { + pub fn fast_linear_combine(&self, u: &[F], c: &[F]) -> Poly { if u.len() == 1 { - return P:: { coeffs: vec![c[0]] }; + return Poly:: { coeffs: vec![c[0]] }; } let n = u.len() / 2; let (u_0, u_1) = u.split_at(n); @@ -193,17 +192,17 @@ impl SubproductTree { } } /// compute the derivative of polynomial p -pub fn derivative(p: &P) -> P { +pub fn derivative(p: &Poly) -> Poly { let mut coeffs = Vec::with_capacity(p.coeffs().len() - 1); for (i, c) in p.coeffs.iter().enumerate().skip(1) { - coeffs.push(E::Fr::from(i as u64) * c); + coeffs.push(F::from(i as u64) * c); } - P:: { coeffs } + Poly:: { coeffs } } /// Build a vector representation of the circulant matrix of polynomial p -pub fn build_circulant(polynomial: &P, size: usize) -> Vec { - let mut circulant = vec![E::Fr::zero(); 2 * size]; +pub fn build_circulant(polynomial: &Poly, size: usize) -> Vec { + let mut circulant = vec![F::zero(); 2 * size]; let coeffs = polynomial.coeffs(); if size == coeffs.len() - 1 { circulant[0] = *coeffs.last().unwrap(); @@ -217,7 +216,7 @@ pub fn build_circulant(polynomial: &P, size: usize) -> Vec< } /// Computes the Toeplitz matrix of polynomial times the vector v pub fn toeplitz_mul( - polynomial: &P, + polynomial: &Poly, v: &[E::G1Affine], size: usize, ) -> Result<(Vec, E::Fr), Error> { @@ -230,7 +229,7 @@ pub fn toeplitz_mul( .ok_or(Error::AmortizedOpeningTooLarge(size))?; let size = domain.size() / 2; - let mut circulant = build_circulant::(polynomial, size); + let mut circulant = build_circulant::(polynomial, size); let mut tmp: Vec = Vec::with_capacity(domain.size()); @@ -269,7 +268,6 @@ mod tests { use ark_poly::UVPolynomial; use ark_std::UniformRand; - type E = ark_bls12_381::Bls12_381; type Fr = ::Fr; #[test] fn test_inverse() { @@ -279,7 +277,7 @@ mod tests { let l = 101; for _ in 0..100 { let p = DensePolynomial::::rand(degree, rng); - let p_inv = inverse_mod_xl::(&p, l).unwrap(); + let p_inv = inverse_mod_xl::(&p, l).unwrap(); let mut t = &p * &p_inv; t.coeffs.resize(l, Fr::zero()); assert_eq!(t.coeffs[0], Fr::one()); @@ -294,13 +292,12 @@ mod tests { let rng = &mut ark_std::test_rng(); let degree = 100; - let l = 101; for g_deg in 1..100 { let f = DensePolynomial::::rand(degree, rng); let mut g = DensePolynomial::::rand(g_deg, rng); *g.last_mut().unwrap() = Fr::one(); //monic - let (q, r) = fast_divide_monic::(&f, &g); + let (q, r) = fast_divide_monic::(&f, &g); let t = &(&q * &g) + &r; @@ -321,7 +318,7 @@ mod tests { evals.push(Fr::rand(rng)); } - let s = SubproductDomain::::new(points); + let s = SubproductDomain::::new(points); let p = s.fast_interpolate(&evals); for (x, y) in s.u.iter().zip(evals.iter()) { @@ -340,7 +337,7 @@ mod tests { u.push(Fr::rand(rng)); c.push(Fr::rand(rng)); } - let s = SubproductDomain::::new(u); + let s = SubproductDomain::::new(u); let f = s.fast_linear_combine(&c); let r = Fr::rand(rng); @@ -361,10 +358,10 @@ mod tests { for _ in 0..d { u.push(Fr::rand(rng)); } - let s = SubproductDomain::::new(u); + let s = SubproductDomain::::new(u); let f = s.fast_inverse_lagrange_coefficients(); - for (a, (i, j)) in s.u.iter().zip(f.iter()).enumerate() { + for (i, j) in s.u.iter().zip(f.iter()) { assert_eq!(s.prime.evaluate(i), *j); } } diff --git a/src/kzg10/mod.rs b/src/kzg10/mod.rs index 96edb8bc..592fde66 100644 --- a/src/kzg10/mod.rs +++ b/src/kzg10/mod.rs @@ -43,10 +43,9 @@ where pub fn setup( max_degree: usize, produce_g2_powers: bool, + produce_h_powers: bool, rng: &mut R, ) -> Result, Error> { - let produce_h_powers = true; - if max_degree < 1 { return Err(Error::DegreeIsZero); } @@ -165,7 +164,7 @@ where } /// Outputs a commitment to `polynomial`. - pub fn commit( + pub fn commit_g1( powers: &Powers, polynomial: &P, hiding_bound: Option, @@ -342,7 +341,7 @@ where } /// Open a polynomial at an entire evaluation domain simultaneously - pub fn open_amortized<'a>( + pub fn open_at_domain<'a>( pp: &UniversalParams, p: &DensePolynomial, domain: &ark_poly::domain::Radix2EvaluationDomain, @@ -505,15 +504,15 @@ where } } - /// Verifies that `value` is the evaluation at `point` of the polynomial - /// committed inside `comm`. - pub fn check_amortized( + /// Verifies that `evals` are the evaluation at `s` of the polynomial + /// committed inside `comm` where `s` is a constructed SubproductDomain + pub fn check_at_domain( pp: &UniversalParams, ck: &Powers, comm: &Commitment, proof: &Proof, - evals: &[E::Fr], // Evaluations at the SubproductDomain - s: &SubproductDomain, // SubproductDomain of the evaluation domain + evals: &[E::Fr], // Evaluations at the SubproductDomain + s: &SubproductDomain, // SubproductDomain of the evaluation domain ) -> Result { let evaluation_interpolation_time = start_timer!(|| "Constructing evaluation polynomial"); @@ -523,15 +522,15 @@ where start_timer!(|| "Constructing commitment to evaluation polynomial in G1"); let evaluation_polynomial_commitment = - KZG10::>::commit(&ck, &evaluation_polynomial, None, None)?; + KZG10::>::commit_g1(&ck, &evaluation_polynomial, None, None)?; end_timer!(evaluation_commit_time); let s_commitment_time = start_timer!(|| "Constructing commitment to SubproductDomain in G2"); - let s_commitment = Self::G2_commit(pp, &s.t.m)?; + let s_commitment = Self::commit_g2(pp, &s.t.m)?; end_timer!(s_commitment_time); - Self::check_amortized_with_commitments( + Self::check_at_domain_with_commitments( pp, comm, proof, @@ -541,7 +540,7 @@ where } /// Check amortized proof with precomputed commitments - pub fn check_amortized_with_commitments( + pub fn check_at_domain_with_commitments( ck: &UniversalParams, comm: &Commitment, proof: &Proof, @@ -643,12 +642,12 @@ mod tests { f_p += (f, &p); let degree = 4; - let pp = KZG_Bls12_381::setup(degree, false, rng).unwrap(); + let pp = KZG_Bls12_381::setup(degree, false, false, rng).unwrap(); let (powers, _) = KZG_Bls12_381::trim(&pp, degree).unwrap(); let hiding_bound = None; - let (comm, _) = KZG10::commit(&powers, &p, hiding_bound, Some(rng)).unwrap(); - let (f_comm, _) = KZG10::commit(&powers, &f_p, hiding_bound, Some(rng)).unwrap(); + let (comm, _) = KZG10::commit_g1(&powers, &p, hiding_bound, Some(rng)).unwrap(); + let (f_comm, _) = KZG10::commit_g1(&powers, &f_p, hiding_bound, Some(rng)).unwrap(); let mut f_comm_2 = Commitment::empty(); f_comm_2 += (f, &comm); @@ -667,11 +666,11 @@ mod tests { while degree <= 1 { degree = usize::rand(rng) % 20; } - let pp = KZG10::::setup(degree, false, rng)?; + let pp = KZG10::::setup(degree, false, false, rng)?; let (ck, vk) = KZG10::::trim(&pp, degree)?; let p = P::rand(degree, rng); let hiding_bound = Some(1); - let (comm, rand) = KZG10::::commit(&ck, &p, hiding_bound, Some(rng))?; + let (comm, rand) = KZG10::::commit_g1(&ck, &p, hiding_bound, Some(rng))?; let point = E::Fr::rand(rng); let value = p.evaluate(&point); let proof = KZG10::::open(&ck, &p, point, &rand)?; @@ -695,11 +694,11 @@ mod tests { let rng = &mut test_rng(); for _ in 0..100 { let degree = 50; - let pp = KZG10::::setup(degree, false, rng)?; + let pp = KZG10::::setup(degree, false, false, rng)?; let (ck, vk) = KZG10::::trim(&pp, 2)?; let p = P::rand(1, rng); let hiding_bound = Some(1); - let (comm, rand) = KZG10::::commit(&ck, &p, hiding_bound, Some(rng))?; + let (comm, rand) = KZG10::::commit_g1(&ck, &p, hiding_bound, Some(rng))?; let point = E::Fr::rand(rng); let value = p.evaluate(&point); let proof = KZG10::::open(&ck, &p, point, &rand)?; @@ -726,7 +725,7 @@ mod tests { while degree <= 1 { degree = usize::rand(rng) % 20; } - let pp = KZG10::::setup(degree, false, rng)?; + let pp = KZG10::::setup(degree, false, false, rng)?; let (ck, vk) = KZG10::::trim(&pp, degree)?; let mut comms = Vec::new(); let mut values = Vec::new(); @@ -735,7 +734,7 @@ mod tests { for _ in 0..10 { let p = P::rand(degree, rng); let hiding_bound = Some(1); - let (comm, rand) = KZG10::::commit(&ck, &p, hiding_bound, Some(rng))?; + let (comm, rand) = KZG10::::commit_g1(&ck, &p, hiding_bound, Some(rng))?; let point = E::Fr::rand(rng); let value = p.evaluate(&point); let proof = KZG10::::open(&ck, &p, point, &rand)?; @@ -777,7 +776,7 @@ mod tests { let rng = &mut test_rng(); let max_degree = 123; - let pp = KZG_Bls12_381::setup(max_degree, false, rng).unwrap(); + let pp = KZG_Bls12_381::setup(max_degree, false, false, rng).unwrap(); let (powers, _) = KZG_Bls12_381::trim(&pp, max_degree).unwrap(); let p = DensePoly::::rand(max_degree + 1, rng); @@ -793,8 +792,8 @@ mod tests { for total_weight in 15..30 { let degree = 2 * total_weight / 3; - let pp = KZG_Bls12_381::setup(degree, false, rng).unwrap(); - let (ck, vk) = KZG_Bls12_381::trim(&pp, degree).unwrap(); + let pp = KZG_Bls12_381::setup(degree, false, false, rng).unwrap(); + let (ck, _) = KZG_Bls12_381::trim(&pp, degree).unwrap(); for _ in 0..4 { let polynomial = DensePolynomial::::rand(degree, rng); @@ -804,20 +803,20 @@ mod tests { let domain = ark_poly::Radix2EvaluationDomain::::new(total_weight).unwrap(); let p = ck.powers_of_g[0..m].to_vec(); - let (h, scale): (Vec, Fr) = + let (h, _scale): (Vec, Fr) = toeplitz_mul::(&polynomial, &p, domain.size()).unwrap(); let h = h .iter() .map( - |p: &G1Projective| (*p).into_affine(), /*. mul(scale).into_affine()*/ + |p: &G1Projective| (*p).into_affine(), /*. mul(_scale).into_affine()*/ ) .collect::>(); for i in 1..=m { let mut total = G1Projective::zero(); for j in 0..=m - i { - total = total + (p[j].mul(coeffs[i + j])); + total += p[j].mul(coeffs[i + j]); } assert_eq!(G1Affine::from(total), h[i - 1]); } @@ -825,22 +824,22 @@ mod tests { } } #[test] - fn test_check_amortized() { + fn test_check_at_domain() { let rng = &mut test_rng(); let n = 10; let eval_domain_size = 1 << n; let degree = 1 << (n - 1); - let pp = KZG_Bls12_381::setup(eval_domain_size, false, rng).unwrap(); + let pp = KZG_Bls12_381::setup(eval_domain_size, false, true, rng).unwrap(); let (ck, vk) = KZG_Bls12_381::trim(&pp, degree).unwrap(); let p = DensePoly::::rand(degree, rng); - let (comm, rand) = KZG_Bls12_381::commit(&ck, &p, None, None).unwrap(); + let (comm, _) = KZG_Bls12_381::commit_g1(&ck, &p, None, None).unwrap(); let domain = ark_poly::Radix2EvaluationDomain::::new(eval_domain_size).unwrap(); - let openings = KZG_Bls12_381::open_amortized(&pp, &p, &domain).unwrap(); + let openings = KZG_Bls12_381::open_at_domain(&pp, &p, &domain).unwrap(); let evals = p.evaluate_over_domain_by_ref(domain).evals; @@ -872,16 +871,12 @@ mod tests { ]; let combined = [ - openings.combine_at_evals(&(0..100), &evals[0..100], &subproduct_domains[0]), - openings.combine_at_evals(&(100..400), &evals[100..400], &subproduct_domains[1]), - openings.combine_at_evals( - &(400..eval_domain_size), - &evals[400..], - &subproduct_domains[2], - ), + openings.combine_at_domain(0, 100, &subproduct_domains[0]), + openings.combine_at_domain(100, 400, &subproduct_domains[1]), + openings.combine_at_domain(400, eval_domain_size, &subproduct_domains[2]), ]; - assert!(KZG_Bls12_381::check_amortized( + assert!(KZG_Bls12_381::check_at_domain( &pp, &ck, &comm, @@ -890,7 +885,7 @@ mod tests { &subproduct_domains[0] ) .unwrap()); - assert!(KZG_Bls12_381::check_amortized( + assert!(KZG_Bls12_381::check_at_domain( &pp, &ck, &comm, @@ -899,7 +894,7 @@ mod tests { &subproduct_domains[1] ) .unwrap()); - assert!(KZG_Bls12_381::check_amortized( + assert!(KZG_Bls12_381::check_at_domain( &pp, &ck, &comm, diff --git a/src/marlin/marlin_pc/mod.rs b/src/marlin/marlin_pc/mod.rs index d101e3ae..218e7dab 100644 --- a/src/marlin/marlin_pc/mod.rs +++ b/src/marlin/marlin_pc/mod.rs @@ -74,7 +74,7 @@ where _num_vars: Option, rng: &mut R, ) -> Result { - kzg10::KZG10::setup(max_degree, false, rng).map_err(Into::into) + kzg10::KZG10::setup(max_degree, false, false, rng).map_err(Into::into) } fn trim( @@ -215,13 +215,13 @@ where )); let (comm, rand) = - kzg10::KZG10::commit(&ck.powers(), polynomial, hiding_bound, Some(rng))?; + kzg10::KZG10::commit_g1(&ck.powers(), polynomial, hiding_bound, Some(rng))?; let (shifted_comm, shifted_rand) = if let Some(degree_bound) = degree_bound { let shifted_powers = ck .shifted_powers(degree_bound) .ok_or(Error::UnsupportedDegreeBound(degree_bound))?; let (shifted_comm, shifted_rand) = - kzg10::KZG10::commit(&shifted_powers, &polynomial, hiding_bound, Some(rng))?; + kzg10::KZG10::commit_g1(&shifted_powers, &polynomial, hiding_bound, Some(rng))?; (Some(shifted_comm), Some(shifted_rand)) } else { (None, None) diff --git a/src/sonic_pc/mod.rs b/src/sonic_pc/mod.rs index 806221ed..3eb189a9 100644 --- a/src/sonic_pc/mod.rs +++ b/src/sonic_pc/mod.rs @@ -151,7 +151,7 @@ where _: Option, rng: &mut R, ) -> Result { - kzg10::KZG10::::setup(max_degree, true, rng).map_err(Into::into) + kzg10::KZG10::::setup(max_degree, true, false, rng).map_err(Into::into) } fn trim( @@ -320,7 +320,8 @@ where ck.powers() }; - let (comm, rand) = kzg10::KZG10::commit(&powers, polynomial, hiding_bound, Some(rng))?; + let (comm, rand) = + kzg10::KZG10::commit_g1(&powers, polynomial, hiding_bound, Some(rng))?; labeled_comms.push(LabeledCommitment::new( label.to_string(), From a8957c0facbee90efea8bdb46d53057ae8db9900 Mon Sep 17 00:00:00 2001 From: joe bebel Date: Tue, 4 May 2021 19:05:29 -0700 Subject: [PATCH 04/11] cleanup suggestions --- src/kzg10/fastpoly.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/kzg10/fastpoly.rs b/src/kzg10/fastpoly.rs index 1d28b9c7..4e1f00b3 100644 --- a/src/kzg10/fastpoly.rs +++ b/src/kzg10/fastpoly.rs @@ -7,12 +7,6 @@ use ark_poly::polynomial::univariate::DensePolynomial as Poly; /// Computes the inverse of f mod x^l pub fn inverse_mod_xl(f: &Poly, l: usize) -> Option> { - //use std::ops::Mul; - //let r = - // std::mem::size_of::() * 8 - (l as u64).leading_zeros() as usize; // ceil(log_2(l)) - - //assert_eq!((l as f64).log2().ceil() as usize, r); - //let r = (l as f64).log2().ceil() as usize; //TODO: rounding problems?? let r = ark_std::log2(l); let mut g = Poly:: { coeffs: vec![f.coeffs[0].inverse().unwrap()], //todo unwrap From 7671ed38c112d76c028c586198d818861b95de8e Mon Sep 17 00:00:00 2001 From: joe bebel Date: Tue, 4 May 2021 19:36:45 -0700 Subject: [PATCH 05/11] rename fast methods --- src/kzg10/data_structures.rs | 4 +- src/kzg10/mod.rs | 6 +-- .../{fastpoly.rs => subproductdomain.rs} | 44 +++++++++---------- 3 files changed, 27 insertions(+), 27 deletions(-) rename src/kzg10/{fastpoly.rs => subproductdomain.rs} (88%) diff --git a/src/kzg10/data_structures.rs b/src/kzg10/data_structures.rs index 43356c18..67e8d440 100644 --- a/src/kzg10/data_structures.rs +++ b/src/kzg10/data_structures.rs @@ -579,9 +579,9 @@ impl AmortizedProof { &self, start: usize, // Domain is omega^{start}, ..., omega^{end-1} end: usize, - s: &super::fastpoly::SubproductDomain, // SubproductDomain of the domain + s: &super::subproductdomain::SubproductDomain, // SubproductDomain of the domain ) -> Proof { - let lagrange_coeff = s.fast_inverse_lagrange_coefficients(); + let lagrange_coeff = s.inverse_lagrange_coefficients(); let mut total = E::G1Projective::zero(); for (c_i, point) in lagrange_coeff.iter().zip(self.w[start..end].iter()) { total += point.into_affine().mul::(c_i.inverse().unwrap()); diff --git a/src/kzg10/mod.rs b/src/kzg10/mod.rs index 592fde66..d2d65646 100644 --- a/src/kzg10/mod.rs +++ b/src/kzg10/mod.rs @@ -20,8 +20,8 @@ use rayon::prelude::*; mod data_structures; pub use data_structures::*; -mod fastpoly; -pub use fastpoly::*; +mod subproductdomain; +pub use subproductdomain::*; /// `KZG10` is an implementation of the polynomial commitment scheme of /// [Kate, Zaverucha and Goldbgerg][kzg10] @@ -516,7 +516,7 @@ where ) -> Result { let evaluation_interpolation_time = start_timer!(|| "Constructing evaluation polynomial"); - let evaluation_polynomial = s.fast_interpolate(evals); + let evaluation_polynomial = s.interpolate(evals); end_timer!(evaluation_interpolation_time); let evaluation_commit_time = start_timer!(|| "Constructing commitment to evaluation polynomial in G1"); diff --git a/src/kzg10/fastpoly.rs b/src/kzg10/subproductdomain.rs similarity index 88% rename from src/kzg10/fastpoly.rs rename to src/kzg10/subproductdomain.rs index 4e1f00b3..6013bc67 100644 --- a/src/kzg10/fastpoly.rs +++ b/src/kzg10/subproductdomain.rs @@ -72,22 +72,22 @@ impl SubproductDomain { SubproductDomain { u, t, prime } } /// evaluate a polynomial over the domain - pub fn fast_evaluate(&self, f: &Poly) -> Vec { + pub fn evaluate(&self, f: &Poly) -> Vec { let mut evals = vec![F::zero(); self.u.len()]; - self.t.fast_evaluate(f, &self.u, &mut evals); + self.t.evaluate(f, &self.u, &mut evals); evals } /// interpolate a polynomial over the domain - pub fn fast_interpolate(&self, v: &[F]) -> Poly { - self.t.fast_interpolate(&self.u, v) + pub fn interpolate(&self, v: &[F]) -> Poly { + self.t.interpolate(&self.u, v) } /// compute the inverse of the lagrange coefficients fast - pub fn fast_inverse_lagrange_coefficients(&self) -> Vec { - self.t.fast_inverse_lagrange_coefficients(&self.u) + pub fn inverse_lagrange_coefficients(&self) -> Vec { + self.t.inverse_lagrange_coefficients(&self.u) } /// compute a linear coefficient of lagrange factors times c_i - pub fn fast_linear_combine(&self, c: &[F]) -> Poly { - self.t.fast_linear_combine(&self.u, &c) + pub fn linear_combine(&self, c: &[F]) -> Poly { + self.t.linear_combine(&self.u, &c) } } @@ -127,7 +127,7 @@ impl SubproductTree { } } /// Fast evaluate f over this subproduct tree - pub fn fast_evaluate(&self, f: &Poly, u: &[F], t: &mut [F]) { + pub fn evaluate(&self, f: &Poly, u: &[F], t: &mut [F]) { //todo: assert degree < u.len() if u.len() == 1 { t[0] = f.coeffs[0]; @@ -144,32 +144,32 @@ impl SubproductTree { let (u_0, u_1) = u.split_at(n); let (t_0, t_1) = t.split_at_mut(n); - left.fast_evaluate(&r_0, u_0, t_0); - right.fast_evaluate(&r_1, u_1, t_1); + left.evaluate(&r_0, u_0, t_0); + right.evaluate(&r_1, u_1, t_1); } /// Fast interpolate over this subproduct tree - pub fn fast_interpolate(&self, u: &[F], v: &[F]) -> Poly { - let mut lagrange_coeff = self.fast_inverse_lagrange_coefficients(u); + pub fn interpolate(&self, u: &[F], v: &[F]) -> Poly { + let mut lagrange_coeff = self.inverse_lagrange_coefficients(u); for (s_i, v_i) in lagrange_coeff.iter_mut().zip(v.iter()) { *s_i = s_i.inverse().unwrap() * *v_i; } - self.fast_linear_combine(u, &lagrange_coeff) + self.linear_combine(u, &lagrange_coeff) } /// Fast compute lagrange coefficients over this subproduct tree - pub fn fast_inverse_lagrange_coefficients(&self, u: &[F]) -> Vec { + pub fn inverse_lagrange_coefficients(&self, u: &[F]) -> Vec { //assert u.len() == degree of s.m if u.len() == 1 { return vec![F::one()]; } let mut evals = vec![F::zero(); u.len()]; let m_prime = derivative::(&self.m); - self.fast_evaluate(&m_prime, u, &mut evals); + self.evaluate(&m_prime, u, &mut evals); evals } /// Fast linear combine over this subproduct tree - pub fn fast_linear_combine(&self, u: &[F], c: &[F]) -> Poly { + pub fn linear_combine(&self, u: &[F], c: &[F]) -> Poly { if u.len() == 1 { return Poly:: { coeffs: vec![c[0]] }; } @@ -179,8 +179,8 @@ impl SubproductTree { let left = self.left.as_ref().unwrap(); let right = self.right.as_ref().unwrap(); - let r_0 = left.fast_linear_combine(u_0, c_0); - let r_1 = right.fast_linear_combine(u_1, c_1); + let r_0 = left.linear_combine(u_0, c_0); + let r_1 = right.linear_combine(u_1, c_1); &(&right.m * &r_0) + &(&left.m * &r_1) } @@ -313,7 +313,7 @@ mod tests { } let s = SubproductDomain::::new(points); - let p = s.fast_interpolate(&evals); + let p = s.interpolate(&evals); for (x, y) in s.u.iter().zip(evals.iter()) { assert_eq!(p.evaluate(x), *y) @@ -332,7 +332,7 @@ mod tests { c.push(Fr::rand(rng)); } let s = SubproductDomain::::new(u); - let f = s.fast_linear_combine(&c); + let f = s.linear_combine(&c); let r = Fr::rand(rng); let m = s.t.m.evaluate(&r); @@ -353,7 +353,7 @@ mod tests { u.push(Fr::rand(rng)); } let s = SubproductDomain::::new(u); - let f = s.fast_inverse_lagrange_coefficients(); + let f = s.inverse_lagrange_coefficients(); for (i, j) in s.u.iter().zip(f.iter()) { assert_eq!(s.prime.evaluate(i), *j); From 420c17d46f2c82992d9e6833f2fd18708939c607 Mon Sep 17 00:00:00 2001 From: joe bebel Date: Tue, 4 May 2021 19:45:10 -0700 Subject: [PATCH 06/11] rename amortizedproof to domainproof --- src/kzg10/data_structures.rs | 5 +++-- src/kzg10/mod.rs | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/kzg10/data_structures.rs b/src/kzg10/data_structures.rs index 67e8d440..88185da0 100644 --- a/src/kzg10/data_structures.rs +++ b/src/kzg10/data_structures.rs @@ -564,7 +564,7 @@ impl ToBytes for Proof { } /// Opening proofs of a commitment on a large domain -pub struct AmortizedProof { +pub struct DomainProof { /// This is a vector of commitments to the witness polynomials /// over a domain 1, omega, omega^2, ..., omega^{n-1} /// where omega is a primitive n'th root of unity @@ -573,8 +573,9 @@ pub struct AmortizedProof { pub scale: E::Fr, } -impl AmortizedProof { +impl DomainProof { /// Combine opening proofs onto a subset of the domain + /// represented by the SubproductDomain s pub fn combine_at_domain( &self, start: usize, // Domain is omega^{start}, ..., omega^{end-1} diff --git a/src/kzg10/mod.rs b/src/kzg10/mod.rs index d2d65646..ba29d881 100644 --- a/src/kzg10/mod.rs +++ b/src/kzg10/mod.rs @@ -345,7 +345,7 @@ where pp: &UniversalParams, p: &DensePolynomial, domain: &ark_poly::domain::Radix2EvaluationDomain, - ) -> Result, Error> { + ) -> Result, Error> { Self::check_degree_is_too_large(p.degree(), pp.powers_of_g.len())?; let open_time = start_timer!(|| format!("Amortized opening polynomial of degree {}", p.degree())); @@ -360,7 +360,7 @@ where domain.fft_in_place(&mut h); - let proofs = AmortizedProof { w: h, scale }; + let proofs = DomainProof { w: h, scale }; end_timer!(open_time); Ok(proofs) @@ -510,8 +510,8 @@ where pp: &UniversalParams, ck: &Powers, comm: &Commitment, - proof: &Proof, - evals: &[E::Fr], // Evaluations at the SubproductDomain + proof: &Proof, // A multi-reveal KZG proof over the SubproductDomain s + evals: &[E::Fr], // Evaluations at the SubproductDomain s: &SubproductDomain, // SubproductDomain of the evaluation domain ) -> Result { let evaluation_interpolation_time = start_timer!(|| "Constructing evaluation polynomial"); @@ -539,7 +539,7 @@ where ) } - /// Check amortized proof with precomputed commitments + /// Check combined domain proof with precomputed commitments pub fn check_at_domain_with_commitments( ck: &UniversalParams, comm: &Commitment, From 0b9e5f45853b14e72a5d15080fda221f19c89acb Mon Sep 17 00:00:00 2001 From: joe bebel Date: Wed, 12 May 2021 17:29:10 -0700 Subject: [PATCH 07/11] add comments --- src/kzg10/subproductdomain.rs | 145 ++++++++++++++++++++++++---------- 1 file changed, 103 insertions(+), 42 deletions(-) diff --git a/src/kzg10/subproductdomain.rs b/src/kzg10/subproductdomain.rs index 6013bc67..9d354016 100644 --- a/src/kzg10/subproductdomain.rs +++ b/src/kzg10/subproductdomain.rs @@ -1,26 +1,43 @@ use crate::Error; use ark_ec::PairingEngine; use ark_ff::{FftField, Field, Zero}; -use ark_poly::UVPolynomial; +use ark_poly::{Polynomial, UVPolynomial}; use ark_poly::polynomial::univariate::DensePolynomial as Poly; +/// Where indicated, algorithms are from Modern Computer Algebra, 3rd edition, by Gathen and Gerhard +/// Abbreviated as GG +/// Let M(n) denote the time to multiply. + +/// GG Algorithm 9.3 /// Computes the inverse of f mod x^l +/// Takes O(M(l)) field arithmetic operations pub fn inverse_mod_xl(f: &Poly, l: usize) -> Option> { - let r = ark_std::log2(l); - let mut g = Poly:: { - coeffs: vec![f.coeffs[0].inverse().unwrap()], //todo unwrap - }; - let mut i = 2usize; - for _ in 0..r { - g = &(&g + &g) - &(f * &(&g * &g)); //todo: g*2? - g.coeffs.resize(i, F::zero()); - i *= 2; + let r = ark_std::log2(l); // Compute ceil(log_2(l)) + if let Some(f_0_inv) = f.coeffs[0].inverse() { + // Invert f(0)^-1 if possible + let mut g = Poly:: { + coeffs: vec![f_0_inv], // Constant polynomial f(0)^-1 + }; + + let mut i = 2usize; + // Use Newton iteration which converges to the inverse mod x^l + for _ in 0..r { + g = &(&g + &g) - &(f * &(&g * &g)); //TODO: is g*2 better than g+g? + g.coeffs + .resize(ark_std::cmp::min(g.coeffs.len(), i), F::zero()); // Take g remainder mod x^{2^i} + i *= 2; + } + Some(g) + } else { + // No inverse exists because f(0)^-1 does not exist + None } - Some(g) } +/// GG chapter 9.1 /// Computes the rev_m(f) function in place +/// rev_m(f) = x^m f(1/x) pub fn rev(f: &mut Poly, m: usize) { assert!(f.coeffs.len() - 1 <= m); for _ in 0..(m - (f.coeffs.len() - 1)) { @@ -29,8 +46,11 @@ pub fn rev(f: &mut Poly, m: usize) { f.reverse(); } +/// GG Algorithm 9.5 /// Divide f by g in nearly linear time pub fn fast_divide_monic(f: &Poly, g: &Poly) -> (Poly, Poly) { + //assert_eq!(g.coeffs.last(), F::one()); //TODO: check monic condition + if f.coeffs().len() < g.coeffs().len() { return ( Poly:: { @@ -53,67 +73,84 @@ pub fn fast_divide_monic(f: &Poly, g: &Poly) -> (Poly, Pol (q, r) } -/// The subproduct tree of a polynomial m over a domain u +/// A subproduct domain is a domain { u_0, ..., u_{n-1} } of scalar values +/// accompanied by a subproduct tree of the polynomial: +/// m = (x - u_0)*...*(x-u_{n-1}) +/// Once the subproduct tree is constructed, operations over the +/// entire subproduct domain can be done in a fast, recursive way. +/// Unlike other fast algorithms, the subproduct domain may be +/// arbitrary points instead of a multiplicative subgroup +/// of roots of unity of the field #[derive(Debug, Clone)] pub struct SubproductDomain { - /// Domain values + /// Domain values u = { u_0, ..., u_{n-1} } pub u: Vec, /// Subproduct tree over domain u pub t: SubproductTree, /// Derivative of the subproduct polynomial - pub prime: Poly, // Derivative + pub prime: Poly, // Derivative of the polynomial m } impl SubproductDomain { - /// Create a subproduct tree domain + /// Create a new subproduct tree domain over the domain { u_0, ..., u_{n-1} } pub fn new(u: Vec) -> SubproductDomain { let t = SubproductTree::new(&u); let prime = derivative::(&t.m); SubproductDomain { u, t, prime } } - /// evaluate a polynomial over the domain + /// Evaluate a polynomial f over the subproduct domain u pub fn evaluate(&self, f: &Poly) -> Vec { let mut evals = vec![F::zero(); self.u.len()]; self.t.evaluate(f, &self.u, &mut evals); evals } - /// interpolate a polynomial over the domain + /// Interpolate a polynomial f over the domain, such that f(u_i) = v_i pub fn interpolate(&self, v: &[F]) -> Poly { self.t.interpolate(&self.u, v) } - /// compute the inverse of the lagrange coefficients fast + /// Compute the inverse of the lagrange coefficients necessary to interpolate over u pub fn inverse_lagrange_coefficients(&self) -> Vec { self.t.inverse_lagrange_coefficients(&self.u) } - /// compute a linear coefficient of lagrange factors times c_i + /// Compute a linear combination of lagrange factors times c_i pub fn linear_combine(&self, c: &[F]) -> Poly { self.t.linear_combine(&self.u, &c) } } /// A subproduct tree of the subproduct domain +/// This type is defined separately from SubproductDomain +/// because the domain u is owned by SubproductDomain, whereas +/// m = (x - u_i)*...*(x-u_j) is owned by the SubproductTree +/// The subdomain { u_i, ..., u_j } is borrowed by SubproductTree for each operation #[derive(Debug, Clone)] pub struct SubproductTree { - /// The left child + /// The left child SubproductTree pub left: Option>>, - /// The right child + /// The right child SubproductTree pub right: Option>>, - /// The polynomial for this subdomain + /// The polynomial m = (x - u_i)*...*(x-u_j) for this subdomain pub m: Poly, } impl SubproductTree { + /// GG Algorithm 10.3 /// Compute the subproduct tree of m = (x - u_0)*...*(x-u_{n-1}) + /// Takes O(M(r) log r) field operations + /// Specialized to assume the leaves are of the form m_i = x-u_i + /// Generalized to arbitrary r, not just powers of 2 pub fn new(u: &[F]) -> SubproductTree { + // A degree 1 polynomial is a leaf of the tree if u.len() == 1 { SubproductTree { left: None, right: None, m: Poly:: { - coeffs: vec![-u[0], F::one()], + coeffs: vec![-u[0], F::one()], // m_0 = x - u_0 }, } } else { + // Split as evenly as possible let n = u.len() / 2; let (u_0, u_1) = u.split_at(n); let left = Box::new(SubproductTree::new(u_0)); @@ -126,20 +163,31 @@ impl SubproductTree { } } } - /// Fast evaluate f over this subproduct tree + /// GG algorithm 9.5 + /// Evaluate f over this subproduct tree + /// deg(f) must be less than the size of the domain + /// self must be the subproduct tree of the slice u + /// The output is stored in the slice t: + /// t_i = f(u_i) + /// Takes O(M(n) log n) field operations pub fn evaluate(&self, f: &Poly, u: &[F], t: &mut [F]) { - //todo: assert degree < u.len() + assert!(f.degree() < u.len()); + if u.len() == 1 { + // By the assertion above, f must be a constant polynomial, so evaluating t[0] = f.coeffs[0]; return; } + // if u.len() > 1, then this SubproductTree must not be a leaf, and it has both children let left = self.left.as_ref().unwrap(); let right = self.right.as_ref().unwrap(); + // if f = q*m + r, then on the domain u where m(u_i) = 0, f(u_i) = r(u_i) let (_, r_0) = fast_divide_monic::(f, &left.m); let (_, r_1) = fast_divide_monic::(f, &right.m); + // divide the domain in the same way that the SubproductTree was constructed let n = u.len() / 2; let (u_0, u_1) = u.split_at(n); let (t_0, t_1) = t.split_at_mut(n); @@ -168,36 +216,46 @@ impl SubproductTree { self.evaluate(&m_prime, u, &mut evals); evals } - /// Fast linear combine over this subproduct tree + /// GG Algorithm 10.9 + /// Fast linear combination of moduli over this subproduct tree + /// On input c = { c_0, ..., c_{n-1} } + /// output sum_i (c_i * m) / (x- u_i) + /// Takes O(M(n) log n) field operations pub fn linear_combine(&self, u: &[F], c: &[F]) -> Poly { if u.len() == 1 { + // Output c_0 * (x-u_0) / (x-u_0) = c_0 return Poly:: { coeffs: vec![c[0]] }; } let n = u.len() / 2; let (u_0, u_1) = u.split_at(n); let (c_0, c_1) = c.split_at(n); + // Linear combination of moduli over both halves of the subproduct tree let left = self.left.as_ref().unwrap(); let right = self.right.as_ref().unwrap(); let r_0 = left.linear_combine(u_0, c_0); let r_1 = right.linear_combine(u_1, c_1); + // r_0 = sum_{i in left} c_i m_left / (x-u_i) + // so m_right * r_0 = sum_{i in left} c_i (m_left*m_right) / (x-u_i) = sum_{i in left} c_i m / (x-u_i) &(&right.m * &r_0) + &(&left.m * &r_1) } } -/// compute the derivative of polynomial p -pub fn derivative(p: &Poly) -> Poly { - let mut coeffs = Vec::with_capacity(p.coeffs().len() - 1); - for (i, c) in p.coeffs.iter().enumerate().skip(1) { +/// compute the derivative of polynomial f +pub fn derivative(f: &Poly) -> Poly { + let mut coeffs = Vec::with_capacity(f.coeffs().len() - 1); + for (i, c) in f.coeffs.iter().enumerate().skip(1) { coeffs.push(F::from(i as u64) * c); } Poly:: { coeffs } } -/// Build a vector representation of the circulant matrix of polynomial p -pub fn build_circulant(polynomial: &Poly, size: usize) -> Vec { +/// Build a vector representation of the (n x n) circulant matrix of polynomial f +/// Based on the blog post: +/// https://alinush.github.io/2020/03/19/multiplying-a-vector-by-a-toeplitz-matrix.html +pub fn build_circulant(f: &Poly, size: usize) -> Vec { let mut circulant = vec![F::zero(); 2 * size]; - let coeffs = polynomial.coeffs(); + let coeffs = f.coeffs(); if size == coeffs.len() - 1 { circulant[0] = *coeffs.last().unwrap(); circulant[size] = *coeffs.last().unwrap(); @@ -267,16 +325,19 @@ mod tests { fn test_inverse() { let rng = &mut ark_std::test_rng(); - let degree = 100; let l = 101; - for _ in 0..100 { - let p = DensePolynomial::::rand(degree, rng); - let p_inv = inverse_mod_xl::(&p, l).unwrap(); - let mut t = &p * &p_inv; - t.coeffs.resize(l, Fr::zero()); - assert_eq!(t.coeffs[0], Fr::one()); - for i in t.iter().skip(1) { - assert_eq!(*i, Fr::zero()); + for l in [1, 2, 3, 5, 19, 25, 101].iter() { + for degree in 0..*l { + for _ in 0..10 { + let p = DensePolynomial::::rand(degree, rng); + let p_inv = inverse_mod_xl::(&p, *l).unwrap(); + let mut t = &p * &p_inv; + t.coeffs.resize(*l, Fr::zero()); + assert_eq!(t.coeffs[0], Fr::one()); + for i in t.iter().skip(1) { + assert_eq!(*i, Fr::zero()); + } + } } } } From 37656a99bd06313b0d3da656e57312223e306957 Mon Sep 17 00:00:00 2001 From: joe <55120843+joebebel@users.noreply.github.com> Date: Fri, 28 May 2021 14:52:30 -0700 Subject: [PATCH 08/11] Update src/kzg10/subproductdomain.rs Co-authored-by: Pratyush Mishra --- src/kzg10/subproductdomain.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/kzg10/subproductdomain.rs b/src/kzg10/subproductdomain.rs index 9d354016..100187fb 100644 --- a/src/kzg10/subproductdomain.rs +++ b/src/kzg10/subproductdomain.rs @@ -53,9 +53,7 @@ pub fn fast_divide_monic(f: &Poly, g: &Poly) -> (Poly, Pol if f.coeffs().len() < g.coeffs().len() { return ( - Poly:: { - coeffs: vec![F::zero()], - }, + Poly::zero(), f.clone(), ); } From b425bef3620a75f5f69f5a665396d023c725f9cb Mon Sep 17 00:00:00 2001 From: joe <55120843+joebebel@users.noreply.github.com> Date: Fri, 28 May 2021 14:52:42 -0700 Subject: [PATCH 09/11] Update src/error.rs Co-authored-by: Pratyush Mishra --- src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index 72b03851..37087058 100644 --- a/src/error.rs +++ b/src/error.rs @@ -94,7 +94,7 @@ pub enum Error { label: String, }, - /// Attempt to open_amortized on too large a domain + /// Attempt to `open_amortized` on too large a domain AmortizedOpeningTooLarge(usize), } From 1237258dfd1f477ed423d17c5e30b52ae05bdc61 Mon Sep 17 00:00:00 2001 From: joe <55120843+joebebel@users.noreply.github.com> Date: Thu, 10 Jun 2021 14:18:11 -0700 Subject: [PATCH 10/11] Update src/kzg10/subproductdomain.rs Co-authored-by: Pratyush Mishra --- src/kzg10/subproductdomain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kzg10/subproductdomain.rs b/src/kzg10/subproductdomain.rs index 100187fb..d319c5ff 100644 --- a/src/kzg10/subproductdomain.rs +++ b/src/kzg10/subproductdomain.rs @@ -49,7 +49,7 @@ pub fn rev(f: &mut Poly, m: usize) { /// GG Algorithm 9.5 /// Divide f by g in nearly linear time pub fn fast_divide_monic(f: &Poly, g: &Poly) -> (Poly, Poly) { - //assert_eq!(g.coeffs.last(), F::one()); //TODO: check monic condition + assert_eq!(g.coeffs.last(), F::one()); // check that `g` is monic if f.coeffs().len() < g.coeffs().len() { return ( From 389646f21d2eab749a85846d1844631557b87f9b Mon Sep 17 00:00:00 2001 From: joe <55120843+joebebel@users.noreply.github.com> Date: Thu, 10 Jun 2021 14:18:26 -0700 Subject: [PATCH 11/11] Update src/kzg10/subproductdomain.rs Co-authored-by: Pratyush Mishra --- src/kzg10/subproductdomain.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/kzg10/subproductdomain.rs b/src/kzg10/subproductdomain.rs index d319c5ff..95761da7 100644 --- a/src/kzg10/subproductdomain.rs +++ b/src/kzg10/subproductdomain.rs @@ -5,9 +5,9 @@ use ark_poly::{Polynomial, UVPolynomial}; use ark_poly::polynomial::univariate::DensePolynomial as Poly; -/// Where indicated, algorithms are from Modern Computer Algebra, 3rd edition, by Gathen and Gerhard -/// Abbreviated as GG -/// Let M(n) denote the time to multiply. +//! Where indicated, algorithms are from Modern Computer Algebra, 3rd edition, by Gathen and Gerhard +//! Abbreviated as GG +//! Let M(n) denote the time to multiply. /// GG Algorithm 9.3 /// Computes the inverse of f mod x^l