From 1325a1ee143962d54f50c808348581c6ae8d6b23 Mon Sep 17 00:00:00 2001 From: William Lin <31808623+Will-Lin4@users.noreply.github.com> Date: Sat, 6 Jun 2020 12:32:03 -0700 Subject: [PATCH] PC Scheme based on Inner Product Arguments (#23) Co-Authored-By: Pratyush Mishra <pratyushmishra@berkeley.edu> --- Cargo.toml | 4 +- src/data_structures.rs | 3 +- src/error.rs | 6 +- src/ipa_pc/data_structures.rs | 257 ++++ src/ipa_pc/mod.rs | 1088 +++++++++++++++++ src/kzg10/data_structures.rs | 5 +- src/kzg10/mod.rs | 8 +- src/kzg10/optional_rng.rs | 31 - src/lib.rs | 169 ++- .../data_structures.rs | 2 +- src/{marlin_kzg10 => marlin_pc}/mod.rs | 87 +- src/optional_rng.rs | 52 + .../data_structures.rs | 0 src/{sonic_kzg10 => sonic_pc}/mod.rs | 82 +- 14 files changed, 1670 insertions(+), 124 deletions(-) create mode 100644 src/ipa_pc/data_structures.rs create mode 100644 src/ipa_pc/mod.rs delete mode 100644 src/kzg10/optional_rng.rs rename src/{marlin_kzg10 => marlin_pc}/data_structures.rs (98%) rename src/{marlin_kzg10 => marlin_pc}/mod.rs (92%) create mode 100644 src/optional_rng.rs rename src/{sonic_kzg10 => sonic_pc}/data_structures.rs (100%) rename src/{sonic_kzg10 => sonic_pc}/mod.rs (92%) diff --git a/Cargo.toml b/Cargo.toml index 8d8aee4..d78d02e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,12 +24,14 @@ algebra-core = { git = "https://github.com/scipr-lab/zexe/", default-features = ff-fft = { git = "https://github.com/scipr-lab/zexe/", default-features = false } bench-utils = { git = "https://github.com/scipr-lab/zexe/" } rand_core = { version = "0.5", default-features = false } +digest = "0.8" rayon = { version = "1", optional = true } derivative = { version = "2", features = [ "use_core" ] } [dev-dependencies] rand = { version = "0.7", default-features = false } -algebra = { git = "https://github.com/scipr-lab/zexe/", default-features = false, features = ["full"] } +algebra = { git = "https://github.com/scipr-lab/zexe/", default-features = false, features = ["jubjub", "bls12_381", "bls12_377"] } +blake2 = { version = "0.8", default-features = false } [profile.release] opt-level = 3 diff --git a/src/data_structures.rs b/src/data_structures.rs index 108bcc9..df25cd1 100644 --- a/src/data_structures.rs +++ b/src/data_structures.rs @@ -101,6 +101,7 @@ impl<'a, F: Field> LabeledPolynomial<'a, F> { label, polynomial: Cow::Owned(polynomial), degree_bound, + hiding_bound, } } @@ -174,7 +175,7 @@ impl<C: PCCommitment> LabeledCommitment<C> { &self.label } - /// Retrieve the polynomial from `self`. + /// Retrieve the commitment from `self`. pub fn commitment(&self) -> &C { &self.commitment } diff --git a/src/error.rs b/src/error.rs index 2232c7b..5bef28f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -79,6 +79,9 @@ pub enum Error { /// The inputs to `commit`, `open` or `verify` had incorrect lengths. IncorrectInputLength(String), + + /// The commitment was generated incorrectly, tampered with, or doesn't support the polynomial. + MalformedCommitment(String), } impl core::fmt::Display for Error { @@ -151,7 +154,8 @@ impl core::fmt::Display for Error { supported degree ({:?})", degree_bound, label, poly_degree, supported_degree ), - Error::IncorrectInputLength(err) => write!(f, "{}", err) + Error::IncorrectInputLength(err) => write!(f, "{}", err), + Error::MalformedCommitment(err) => write!(f, "{}", err) } } } diff --git a/src/ipa_pc/data_structures.rs b/src/ipa_pc/data_structures.rs new file mode 100644 index 0000000..d041124 --- /dev/null +++ b/src/ipa_pc/data_structures.rs @@ -0,0 +1,257 @@ +use crate::*; +use crate::{PCCommitterKey, PCVerifierKey, Vec}; +use algebra_core::{AffineCurve, Field, ToBytes, UniformRand, Zero}; +use rand_core::RngCore; + +/// `UniversalParams` are the universal parameters for the inner product arg scheme. +#[derive(Derivative)] +#[derivative(Default(bound = ""), Clone(bound = ""), Debug(bound = ""))] +pub struct UniversalParams<G: AffineCurve> { + /// The key used to commit to polynomials. + pub comm_key: Vec<G>, + + /// Some group generator. + pub h: G, + + /// Some group generator specifically used for hiding. + pub s: G, +} + +impl<G: AffineCurve> PCUniversalParams for UniversalParams<G> { + fn max_degree(&self) -> usize { + self.comm_key.len() - 1 + } +} + +/// `CommitterKey` is used to commit to, and create evaluation proofs for, a given +/// polynomial. +#[derive(Derivative)] +#[derivative( + Default(bound = ""), + Hash(bound = ""), + Clone(bound = ""), + Debug(bound = "") +)] +pub struct CommitterKey<G: AffineCurve> { + /// The key used to commit to polynomials. + pub comm_key: Vec<G>, + + /// A random group generator. + pub h: G, + + /// A random group generator that is to be used to make + /// a commitment hiding. + pub s: G, + + /// The maximum degree supported by the parameters + /// this key was derived from. + pub max_degree: usize, +} + +impl<G: AffineCurve> PCCommitterKey for CommitterKey<G> { + fn max_degree(&self) -> usize { + self.max_degree + } + fn supported_degree(&self) -> usize { + self.comm_key.len() - 1 + } +} + +/// `VerifierKey` is used to check evaluation proofs for a given commitment. +pub type VerifierKey<G> = CommitterKey<G>; + +impl<G: AffineCurve> PCVerifierKey for VerifierKey<G> { + fn max_degree(&self) -> usize { + self.max_degree + } + + fn supported_degree(&self) -> usize { + self.comm_key.len() - 1 + } +} + +/// Commitment to a polynomial that optionally enforces a degree bound. +#[derive(Derivative)] +#[derivative( + Default(bound = ""), + Hash(bound = ""), + Clone(bound = ""), + Copy(bound = ""), + Debug(bound = ""), + PartialEq(bound = ""), + Eq(bound = "") +)] +pub struct Commitment<G: AffineCurve> { + /// A Pedersen commitment to the polynomial. + pub comm: G, + + /// A Pedersen commitment to the shifted polynomial. + /// This is `none` if the committed polynomial does not + /// enforce a strict degree bound. + pub shifted_comm: Option<G>, +} + +impl<G: AffineCurve> PCCommitment for Commitment<G> { + #[inline] + fn empty() -> Self { + Commitment { + comm: G::zero(), + shifted_comm: None, + } + } + + fn has_degree_bound(&self) -> bool { + false + } + + fn size_in_bytes(&self) -> usize { + algebra_core::to_bytes![G::zero()].unwrap().len() / 2 + } +} + +impl<G: AffineCurve> ToBytes for Commitment<G> { + #[inline] + fn write<W: algebra_core::io::Write>(&self, mut writer: W) -> algebra_core::io::Result<()> { + self.comm.write(&mut writer)?; + let shifted_exists = self.shifted_comm.is_some(); + shifted_exists.write(&mut writer)?; + self.shifted_comm + .as_ref() + .unwrap_or(&G::zero()) + .write(&mut writer) + } +} + +/// `Randomness` hides the polynomial inside a commitment and is outputted by `InnerProductArg::commit`. +#[derive(Derivative)] +#[derivative( + Default(bound = ""), + Hash(bound = ""), + Clone(bound = ""), + Debug(bound = ""), + PartialEq(bound = ""), + Eq(bound = "") +)] +pub struct Randomness<G: AffineCurve> { + /// Randomness is some scalar field element. + pub rand: G::ScalarField, + + /// Randomness applied to the shifted commitment is some scalar field element. + pub shifted_rand: Option<G::ScalarField>, +} + +impl<G: AffineCurve> PCRandomness for Randomness<G> { + fn empty() -> Self { + Self { + rand: G::ScalarField::zero(), + shifted_rand: None, + } + } + + fn rand<R: RngCore>(_hiding_bound: usize, has_degree_bound: bool, rng: &mut R) -> Self { + let rand = G::ScalarField::rand(rng); + let shifted_rand = if has_degree_bound { + Some(G::ScalarField::rand(rng)) + } else { + None + }; + + Self { rand, shifted_rand } + } +} + +/// `Proof` is an evaluation proof that is output by `InnerProductArg::open`. +#[derive(Derivative)] +#[derivative( + Default(bound = ""), + Hash(bound = ""), + Clone(bound = ""), + Debug(bound = "") +)] +pub struct Proof<G: AffineCurve> { + /// Vector of left elements for each of the log_d iterations in `open` + pub l_vec: Vec<G>, + + /// Vector of right elements for each of the log_d iterations within `open` + pub r_vec: Vec<G>, + + /// Committer key from the last iteration within `open` + pub final_comm_key: G, + + /// Coefficient from the last iteration within withinopen` + pub c: G::ScalarField, + + /// Commitment to the blinding polynomial. + pub hiding_comm: Option<G>, + + /// Linear combination of all the randomness used for commitments + /// to the opened polynomials, along with the randomness used for the + /// commitment to the hiding polynomial. + pub rand: Option<G::ScalarField>, +} + +impl<G: AffineCurve> PCProof for Proof<G> { + fn size_in_bytes(&self) -> usize { + algebra_core::to_bytes![self].unwrap().len() + } +} + +impl<G: AffineCurve> ToBytes for Proof<G> { + #[inline] + fn write<W: algebra_core::io::Write>(&self, mut writer: W) -> algebra_core::io::Result<()> { + self.l_vec.write(&mut writer)?; + self.r_vec.write(&mut writer)?; + self.final_comm_key.write(&mut writer)?; + self.c.write(&mut writer)?; + self.hiding_comm + .as_ref() + .unwrap_or(&G::zero()) + .write(&mut writer)?; + self.rand + .as_ref() + .unwrap_or(&G::ScalarField::zero()) + .write(&mut writer) + } +} + +/// `SuccinctCheckPolynomial` is a succinctly-representated polynomial +/// generated from the `log_d` random oracle challenges generated in `open`. +/// It has the special property that can be evaluated in `O(log_d)` time. +pub struct SuccinctCheckPolynomial<F: Field>(pub Vec<F>); + +impl<F: Field> SuccinctCheckPolynomial<F> { + /// Computes the coefficients of the underlying degree `d` polynomial. + pub fn compute_coeffs(&self) -> Vec<F> { + let challenges = &self.0; + let log_d = challenges.len(); + + let mut coeffs = vec![F::one(); 1 << log_d]; + for (i, challenge) in challenges.iter().enumerate() { + let i = i + 1; + let elem_degree = 1 << (log_d - i); + for start in (elem_degree..coeffs.len()).step_by(elem_degree * 2) { + for offset in 0..elem_degree { + coeffs[start + offset] *= challenge; + } + } + } + + coeffs + } + + /// Evaluate `self` at `point` in time `O(log_d)`. + pub fn evaluate(&self, point: F) -> F { + let challenges = &self.0; + let log_d = challenges.len(); + + let mut product = F::one(); + for (i, challenge) in challenges.iter().enumerate() { + let i = i + 1; + let elem_degree: u64 = (1 << (log_d - i)) as u64; + let elem = point.pow([elem_degree]); + product *= &(F::one() + &(elem * challenge)); + } + + product + } +} diff --git a/src/ipa_pc/mod.rs b/src/ipa_pc/mod.rs new file mode 100644 index 0000000..8d4f20a --- /dev/null +++ b/src/ipa_pc/mod.rs @@ -0,0 +1,1088 @@ +use crate::{BTreeMap, BTreeSet, String, ToString, Vec}; +use crate::{BatchLCProof, Error, Evaluations, QuerySet}; +use crate::{LabeledCommitment, LabeledPolynomial, LinearCombination}; +use crate::{PCCommitterKey, PCRandomness, PCUniversalParams, Polynomial, PolynomialCommitment}; + +use algebra_core::{ + to_bytes, AffineCurve, Field, One, PrimeField, ProjectiveCurve, ToBytes, UniformRand, + VariableBaseMSM, Zero, +}; +use core::{convert::TryInto, marker::PhantomData}; +use rand_core::RngCore; + +mod data_structures; +pub use data_structures::*; + +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +use digest::Digest; + +/// A polynomial commitment scheme based on the hardness of the +/// discrete logarithm problem in prime-order groups. +/// The construction is described in detail in [[BCMS20]][pcdas]. +/// +/// Degree bound enforcement requires that (at least one of) the points at +/// which a committed polynomial is evaluated are from a distribution that is +/// random conditioned on the polynomial. This is because degree bound +/// enforcement relies on checking a polynomial identity at this point. +/// More formally, the points must be sampled from an admissible query sampler, +/// as detailed in [[CHMMVW20]][marlin]. +/// +/// [pcdas]: https://eprint.iacr.org/2020/499 +/// [marlin]: https://eprint.iacr.org/2019/104 +pub struct InnerProductArgPC<G: AffineCurve, D: Digest> { + _projective: PhantomData<G>, + _digest: PhantomData<D>, +} + +impl<G: AffineCurve, D: Digest> InnerProductArgPC<G, D> { + /// `PROTOCOL_NAME` is used as a seed for the setup function. + pub const PROTOCOL_NAME: &'static [u8] = b"PC-DL-2020"; + + /// Create a Pedersen commitment to `scalars` using the commitment key `comm_key`. + /// Optionally, randomize the commitment using `hiding_generator` and `randomizer`. + fn cm_commit( + comm_key: &[G], + scalars: &[G::ScalarField], + hiding_generator: Option<G>, + randomizer: Option<G::ScalarField>, + ) -> G::Projective { + let scalars_bigint = ff_fft::cfg_iter!(scalars) + .map(|s| s.into_repr()) + .collect::<Vec<_>>(); + + let mut comm = VariableBaseMSM::multi_scalar_mul(comm_key, &scalars_bigint); + + if randomizer.is_some() { + assert!(hiding_generator.is_some()); + comm += &hiding_generator.unwrap().mul(randomizer.unwrap()); + } + + comm + } + + fn compute_random_oracle_challenge(bytes: &[u8]) -> G::ScalarField { + let mut i = 0u64; + let mut challenge = None; + while challenge.is_none() { + let hash_input = algebra_core::to_bytes![bytes, i].unwrap(); + let hash = D::digest(&hash_input); + challenge = <G::ScalarField as Field>::from_random_bytes(&hash); + + i += 1; + } + + challenge.unwrap() + } + + #[inline] + fn inner_product(l: &[G::ScalarField], r: &[G::ScalarField]) -> G::ScalarField { + ff_fft::cfg_iter!(l).zip(r).map(|(li, ri)| *li * ri).sum() + } + + /// The succinct portion of `PC::check`. This algorithm runs in time + /// O(log d), where d is the degree of the committed polynomials. + fn succinct_check<'a>( + vk: &VerifierKey<G>, + commitments: impl IntoIterator<Item = &'a LabeledCommitment<Commitment<G>>>, + point: G::ScalarField, + values: impl IntoIterator<Item = G::ScalarField>, + proof: &Proof<G>, + opening_challenge: G::ScalarField, + ) -> Option<SuccinctCheckPolynomial<G::ScalarField>> { + let check_time = start_timer!(|| "Succinct checking"); + + let d = vk.supported_degree(); + + // `log_d` is ceil(log2 (d + 1)), which is the number of steps to compute all of the challenges + let log_d = algebra_core::log2(d + 1) as usize; + + let mut combined_commitment_proj = G::Projective::zero(); + let mut combined_v = G::ScalarField::zero(); + + let mut cur_challenge = opening_challenge; + let labeled_commitments = commitments.into_iter(); + let values = values.into_iter(); + + for (labeled_commitment, value) in labeled_commitments.zip(values) { + let commitment = labeled_commitment.commitment(); + combined_v += &(cur_challenge * &value); + combined_commitment_proj += &labeled_commitment.commitment().comm.mul(cur_challenge); + cur_challenge *= &opening_challenge; + + let degree_bound = labeled_commitment.degree_bound(); + assert_eq!(degree_bound.is_some(), commitment.shifted_comm.is_some()); + + if let Some(degree_bound) = degree_bound { + let shift = point.pow([(vk.supported_degree() - degree_bound) as u64]); + combined_v += &(cur_challenge * &value * &shift); + combined_commitment_proj += &commitment.shifted_comm.unwrap().mul(cur_challenge); + } + + cur_challenge *= &opening_challenge; + } + + let mut combined_commitment = combined_commitment_proj.into_affine(); + + assert_eq!(proof.hiding_comm.is_some(), proof.rand.is_some()); + if proof.hiding_comm.is_some() { + let hiding_comm = proof.hiding_comm.unwrap(); + let rand = proof.rand.unwrap(); + + let hiding_challenge = Self::compute_random_oracle_challenge( + &algebra_core::to_bytes![combined_commitment, point, combined_v, hiding_comm] + .unwrap(), + ); + combined_commitment_proj += &(hiding_comm.mul(hiding_challenge) - &vk.s.mul(rand)); + combined_commitment = combined_commitment_proj.into_affine(); + } + + // Challenge for each round + let mut round_challenges = Vec::with_capacity(log_d); + let mut round_challenge = Self::compute_random_oracle_challenge( + &algebra_core::to_bytes![combined_commitment, point, combined_v].unwrap(), + ); + + let h_prime = vk.h.mul(round_challenge); + + let mut round_commitment_proj = combined_commitment_proj + &h_prime.mul(combined_v); + + let l_iter = proof.l_vec.iter(); + let r_iter = proof.r_vec.iter(); + + for (l, r) in l_iter.zip(r_iter) { + round_challenge = Self::compute_random_oracle_challenge( + &algebra_core::to_bytes![round_challenge, l, r].unwrap(), + ); + round_challenges.push(round_challenge); + round_commitment_proj += + &(l.mul(round_challenge.inverse().unwrap()) + &r.mul(round_challenge)); + } + + let check_poly = SuccinctCheckPolynomial::<G::ScalarField>(round_challenges); + let v_prime = check_poly.evaluate(point) * &proof.c; + let h_prime = h_prime.into_affine(); + + let check_commitment_elem: G::Projective = Self::cm_commit( + &[proof.final_comm_key.clone(), h_prime], + &[proof.c.clone(), v_prime], + None, + None, + ); + + if !(round_commitment_proj - &check_commitment_elem).is_zero() { + end_timer!(check_time); + return None; + } + + end_timer!(check_time); + Some(check_poly) + } + + fn check_degrees_and_bounds( + supported_degree: usize, + p: &LabeledPolynomial<G::ScalarField>, + ) -> Result<(), Error> { + if p.degree() < 1 { + return Err(Error::DegreeIsZero); + } else if p.degree() > supported_degree { + return Err(Error::TooManyCoefficients { + num_coefficients: p.degree() + 1, + num_powers: supported_degree + 1, + }); + } + + if let Some(bound) = p.degree_bound() { + if bound < p.degree() || bound > supported_degree { + return Err(Error::IncorrectDegreeBound { + poly_degree: p.degree(), + degree_bound: bound, + supported_degree, + label: p.label().to_string(), + }); + } + } + + Ok(()) + } + + fn shift_polynomial( + ck: &CommitterKey<G>, + p: &Polynomial<G::ScalarField>, + degree_bound: usize, + ) -> Polynomial<G::ScalarField> { + if p.is_zero() { + Polynomial::zero() + } else { + let mut shifted_polynomial_coeffs = + vec![G::ScalarField::zero(); ck.supported_degree() - degree_bound]; + shifted_polynomial_coeffs.extend_from_slice(&p.coeffs); + Polynomial::from_coefficients_vec(shifted_polynomial_coeffs) + } + } + + fn combine_shifted_rand( + combined_rand: Option<G::ScalarField>, + new_rand: Option<G::ScalarField>, + coeff: G::ScalarField, + ) -> Option<G::ScalarField> { + if let Some(new_rand) = new_rand { + let coeff_new_rand = new_rand * &coeff; + return Some(combined_rand.map_or(coeff_new_rand, |r| r + &coeff_new_rand)); + }; + + combined_rand + } + + fn combine_shifted_comm( + combined_comm: Option<G::Projective>, + new_comm: Option<G>, + coeff: G::ScalarField, + ) -> Option<G::Projective> { + if let Some(new_comm) = new_comm { + let coeff_new_comm = new_comm.mul(coeff); + return Some(combined_comm.map_or(coeff_new_comm, |c| c + &coeff_new_comm)); + }; + + combined_comm + } + + fn construct_labeled_commitments( + lc_info: &[(String, Option<usize>)], + elements: &[G::Projective], + ) -> Vec<LabeledCommitment<Commitment<G>>> { + let comms = G::Projective::batch_normalization_into_affine(elements); + let mut commitments = Vec::new(); + + let mut i = 0; + for info in lc_info.into_iter() { + let commitment; + let label = info.0.clone(); + let degree_bound = info.1; + + if degree_bound.is_some() { + commitment = Commitment { + comm: comms[i].clone(), + shifted_comm: Some(comms[i + 1].clone()), + }; + + i += 2; + } else { + commitment = Commitment { + comm: comms[i].clone(), + shifted_comm: None, + }; + + i += 1; + } + + commitments.push(LabeledCommitment::new(label, commitment, degree_bound)); + } + + return commitments; + } + + fn sample_generators(num_generators: usize) -> Vec<G> { + let generators: Vec<_> = ff_fft::cfg_into_iter!(0..num_generators) + .map(|i| { + let i = i as u64; + let mut hash = D::digest(&to_bytes![&Self::PROTOCOL_NAME, i].unwrap()); + let mut g = G::from_random_bytes(&hash); + let mut j = 0u64; + while g.is_none() { + hash = D::digest(&to_bytes![&Self::PROTOCOL_NAME, i, j].unwrap()); + g = G::from_random_bytes(&hash); + j += 1; + } + let generator = g.unwrap(); + generator.mul_by_cofactor_to_projective() + }) + .collect(); + + G::Projective::batch_normalization_into_affine(&generators) + } +} + +impl<G: AffineCurve, D: Digest> PolynomialCommitment<G::ScalarField> for InnerProductArgPC<G, D> { + type UniversalParams = UniversalParams<G>; + type CommitterKey = CommitterKey<G>; + type VerifierKey = VerifierKey<G>; + type Commitment = Commitment<G>; + type Randomness = Randomness<G>; + type Proof = Proof<G>; + type BatchProof = Vec<Self::Proof>; + type Error = Error; + + fn setup<R: RngCore>( + max_degree: usize, + _rng: &mut R, + ) -> Result<Self::UniversalParams, Self::Error> { + // Ensure that max_degree + 1 is a power of 2 + let max_degree = (max_degree + 1).next_power_of_two() - 1; + + let setup_time = start_timer!(|| format!("Sampling {} generators", max_degree + 3)); + let mut generators = Self::sample_generators(max_degree + 3); + end_timer!(setup_time); + + let h = generators.pop().unwrap(); + let s = generators.pop().unwrap(); + + let pp = UniversalParams { + comm_key: generators, + h, + s, + }; + + Ok(pp) + } + + fn trim( + pp: &Self::UniversalParams, + supported_degree: usize, + _enforced_degree_bounds: Option<&[usize]>, + ) -> Result<(Self::CommitterKey, Self::VerifierKey), Self::Error> { + // Ensure that supported_degree + 1 is a power of two + let supported_degree = (supported_degree + 1).next_power_of_two() - 1; + if supported_degree > pp.max_degree() { + return Err(Error::TrimmingDegreeTooLarge); + } + + let trim_time = + start_timer!(|| format!("Trimming to supported degree of {}", supported_degree)); + + let ck = CommitterKey { + comm_key: pp.comm_key[0..(supported_degree + 1)].to_vec(), + h: pp.h.clone(), + s: pp.s.clone(), + max_degree: pp.max_degree(), + }; + + let vk = VerifierKey { + comm_key: pp.comm_key[0..(supported_degree + 1)].to_vec(), + h: pp.h.clone(), + s: pp.s.clone(), + max_degree: pp.max_degree(), + }; + + end_timer!(trim_time); + + Ok((ck, vk)) + } + + /// Outputs a commitment to `polynomial`. + fn commit<'a>( + ck: &Self::CommitterKey, + polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<'a, G::ScalarField>>, + rng: Option<&mut dyn RngCore>, + ) -> Result< + ( + Vec<LabeledCommitment<Self::Commitment>>, + Vec<Self::Randomness>, + ), + Self::Error, + > { + let rng = &mut crate::optional_rng::OptionalRng(rng); + let mut comms = Vec::new(); + let mut rands = Vec::new(); + + let commit_time = start_timer!(|| "Committing to polynomials"); + for labeled_polynomial in polynomials { + Self::check_degrees_and_bounds(ck.supported_degree(), labeled_polynomial)?; + + let polynomial = labeled_polynomial.polynomial(); + let label = labeled_polynomial.label(); + let hiding_bound = labeled_polynomial.hiding_bound(); + let degree_bound = labeled_polynomial.degree_bound(); + + let commit_time = start_timer!(|| format!( + "Polynomial {} of degree {}, degree bound {:?}, and hiding bound {:?}", + label, + polynomial.degree(), + degree_bound, + hiding_bound, + )); + + let randomness = if hiding_bound.is_some() { + Randomness::rand(hiding_bound.unwrap(), degree_bound.is_some(), rng) + } else { + Randomness::empty() + }; + + let comm = Self::cm_commit( + &ck.comm_key[..(polynomial.degree() + 1)], + &polynomial.coeffs, + Some(ck.s), + Some(randomness.rand), + ) + .into(); + + let shifted_comm = degree_bound.map(|d| { + Self::cm_commit( + &ck.comm_key[(ck.supported_degree() - d)..], + &polynomial.coeffs, + Some(ck.s), + randomness.shifted_rand, + ) + .into() + }); + + let commitment = Commitment { comm, shifted_comm }; + let labeled_comm = LabeledCommitment::new(label.to_string(), commitment, degree_bound); + + comms.push(labeled_comm); + rands.push(randomness); + + end_timer!(commit_time); + } + + end_timer!(commit_time); + Ok((comms, rands)) + } + + fn open<'a>( + ck: &Self::CommitterKey, + labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<'a, G::ScalarField>>, + commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, + point: G::ScalarField, + opening_challenge: G::ScalarField, + rands: impl IntoIterator<Item = &'a Self::Randomness>, + rng: Option<&mut dyn RngCore>, + ) -> Result<Self::Proof, Self::Error> + where + Self::Commitment: 'a, + Self::Randomness: 'a, + { + let mut combined_polynomial = Polynomial::zero(); + let mut combined_rand = G::ScalarField::zero(); + let mut combined_commitment_proj = G::Projective::zero(); + + let mut has_hiding = false; + + let polys_iter = labeled_polynomials.into_iter(); + let rands_iter = rands.into_iter(); + let comms_iter = commitments.into_iter(); + + let combine_time = start_timer!(|| "Combining polynomials, randomness, and commitments."); + let mut cur_challenge = opening_challenge; + for (labeled_polynomial, (labeled_commitment, randomness)) in + polys_iter.zip(comms_iter.zip(rands_iter)) + { + Self::check_degrees_and_bounds(ck.supported_degree(), labeled_polynomial)?; + + let polynomial = labeled_polynomial.polynomial(); + let degree_bound = labeled_polynomial.degree_bound(); + let hiding_bound = labeled_polynomial.hiding_bound(); + let commitment = labeled_commitment.commitment(); + + combined_polynomial += (cur_challenge, polynomial); + combined_commitment_proj += &commitment.comm.mul(cur_challenge); + + if hiding_bound.is_some() { + has_hiding = true; + combined_rand += &(cur_challenge * &randomness.rand); + } + + cur_challenge *= &opening_challenge; + + assert_eq!(degree_bound.is_some(), randomness.shifted_rand.is_some()); + assert_eq!(degree_bound.is_some(), commitment.shifted_comm.is_some()); + + if let Some(degree_bound) = labeled_polynomial.degree_bound() { + assert!(labeled_commitment.degree_bound().is_some()); + assert_eq!(labeled_commitment.degree_bound().unwrap(), degree_bound); + + let shifted_polynomial = Self::shift_polynomial(ck, polynomial, degree_bound); + combined_polynomial += (cur_challenge, &shifted_polynomial); + combined_commitment_proj += &commitment.shifted_comm.unwrap().mul(cur_challenge); + + if hiding_bound.is_some() { + combined_rand += &(cur_challenge * &randomness.shifted_rand.unwrap()); + } + } + + cur_challenge *= &opening_challenge; + } + + end_timer!(combine_time); + + let combined_v = combined_polynomial.evaluate(point); + + // Pad the coefficients to the appropriate vector size + let d = ck.supported_degree(); + + // `log_d` is ceil(log2 (d + 1)), which is the number of steps to compute all of the challenges + let log_d = algebra_core::log2(d + 1) as usize; + + let mut combined_commitment; + let mut hiding_commitment = None; + + if has_hiding { + let mut rng = rng.expect("hiding commitments require randomness"); + let hiding_time = start_timer!(|| "Applying hiding."); + let mut hiding_polynomial = Polynomial::rand(d, &mut rng); + hiding_polynomial -= + &Polynomial::from_coefficients_slice(&[hiding_polynomial.evaluate(point)]); + + let hiding_rand = G::ScalarField::rand(rng); + let hiding_commitment_proj = Self::cm_commit( + ck.comm_key.as_slice(), + hiding_polynomial.coeffs.as_slice(), + Some(ck.s), + Some(hiding_rand), + ); + + let mut batch = G::Projective::batch_normalization_into_affine(&[ + combined_commitment_proj, + hiding_commitment_proj, + ]); + hiding_commitment = Some(batch.pop().unwrap()); + combined_commitment = batch.pop().unwrap(); + + let hiding_challenge = Self::compute_random_oracle_challenge( + &algebra_core::to_bytes![ + combined_commitment, + point, + combined_v, + hiding_commitment.unwrap() + ] + .unwrap(), + ); + combined_polynomial += (hiding_challenge, &hiding_polynomial); + combined_rand += &(hiding_challenge * &hiding_rand); + combined_commitment_proj += + &(hiding_commitment_proj.mul(hiding_challenge) - &ck.s.mul(combined_rand)); + + end_timer!(hiding_time); + } + + let combined_rand = if has_hiding { + Some(combined_rand) + } else { + None + }; + + let proof_time = + start_timer!(|| format!("Generating proof for degree {} combined polynomial", d + 1)); + + combined_commitment = combined_commitment_proj.into_affine(); + + // ith challenge + let mut round_challenge = Self::compute_random_oracle_challenge( + &algebra_core::to_bytes![combined_commitment, point, combined_v].unwrap(), + ); + + let h_prime = ck.h.mul(round_challenge).into_affine(); + + // Pads the coefficients with zeroes to get the number of coeff to be d+1 + let mut coeffs = combined_polynomial.coeffs; + if coeffs.len() < d + 1 { + for _ in coeffs.len()..(d + 1) { + coeffs.push(G::ScalarField::zero()); + } + } + let mut coeffs = coeffs.as_mut_slice(); + + // Powers of z + let mut z: Vec<G::ScalarField> = Vec::with_capacity(d + 1); + let mut cur_z: G::ScalarField = G::ScalarField::one(); + for _ in 0..(d + 1) { + z.push(cur_z); + cur_z *= &point; + } + let mut z = z.as_mut_slice(); + + // This will be used for transforming the key in each step + let mut key_proj: Vec<G::Projective> = ck.comm_key.iter().map(|x| (*x).into()).collect(); + let mut key_proj = key_proj.as_mut_slice(); + + let mut temp; + + // Key for MSM + // We initialize this to capacity 0 initially because we want to use the key slice first + let mut comm_key = &ck.comm_key; + + let mut l_vec = Vec::with_capacity(log_d); + let mut r_vec = Vec::with_capacity(log_d); + + let mut n = d + 1; + while n > 1 { + let (coeffs_l, coeffs_r) = coeffs.split_at_mut(n / 2); + let (z_l, z_r) = z.split_at_mut(n / 2); + let (key_l, key_r) = comm_key.split_at(n / 2); + let (key_proj_l, _) = key_proj.split_at_mut(n / 2); + + let l = Self::cm_commit(key_l, coeffs_r, None, None) + + &h_prime.mul(Self::inner_product(coeffs_r, z_l)); + + let r = Self::cm_commit(key_r, coeffs_l, None, None) + + &h_prime.mul(Self::inner_product(coeffs_l, z_r)); + + let lr = G::Projective::batch_normalization_into_affine(&[l, r]); + l_vec.push(lr[0]); + r_vec.push(lr[1]); + + round_challenge = Self::compute_random_oracle_challenge( + &algebra_core::to_bytes![round_challenge, lr[0], lr[1]].unwrap(), + ); + let round_challenge_inv = round_challenge.inverse().unwrap(); + + ff_fft::cfg_iter_mut!(coeffs_l) + .zip(coeffs_r) + .for_each(|(c_l, c_r)| *c_l += &(round_challenge_inv * &c_r)); + + ff_fft::cfg_iter_mut!(z_l) + .zip(z_r) + .for_each(|(z_l, z_r)| *z_l += &(round_challenge * &z_r)); + + ff_fft::cfg_iter_mut!(key_proj_l) + .zip(key_r) + .for_each(|(k_l, k_r)| *k_l += &(k_r.mul(round_challenge))); + + coeffs = coeffs_l; + z = z_l; + + key_proj = key_proj_l; + temp = G::Projective::batch_normalization_into_affine(key_proj); + comm_key = &temp; + + n /= 2; + } + + end_timer!(proof_time); + + Ok(Proof { + l_vec, + r_vec, + final_comm_key: comm_key[0], + c: coeffs[0], + hiding_comm: hiding_commitment, + rand: combined_rand, + }) + } + + fn check<'a, R: RngCore>( + vk: &Self::VerifierKey, + commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, + point: G::ScalarField, + values: impl IntoIterator<Item = G::ScalarField>, + proof: &Self::Proof, + opening_challenge: G::ScalarField, + _rng: &mut R, + ) -> Result<bool, Self::Error> + where + Self::Commitment: 'a, + { + let check_time = start_timer!(|| "Checking evaluations"); + let d = vk.supported_degree(); + + // `log_d` is ceil(log2 (d + 1)), which is the number of steps to compute all of the challenges + let log_d = algebra_core::log2(d + 1) as usize; + + if proof.l_vec.len() != proof.r_vec.len() || proof.l_vec.len() != log_d { + return Err(Error::IncorrectInputLength( + format!( + "Expected proof vectors to be {:}. Instead, l_vec size is {:} and r_vec size is {:}", + log_d, + proof.l_vec.len(), + proof.r_vec.len() + ) + )); + } + + let check_poly = + Self::succinct_check(vk, commitments, point, values, proof, opening_challenge); + + if check_poly.is_none() { + return Ok(false); + } + + let check_poly_coeffs = check_poly.unwrap().compute_coeffs(); + let final_key = Self::cm_commit( + vk.comm_key.as_slice(), + check_poly_coeffs.as_slice(), + None, + None, + ); + if !(final_key - &proof.final_comm_key.into()).is_zero() { + return Ok(false); + } + + end_timer!(check_time); + Ok(true) + } + + fn batch_check<'a, R: RngCore>( + vk: &Self::VerifierKey, + commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, + query_set: &QuerySet<G::ScalarField>, + values: &Evaluations<G::ScalarField>, + proof: &Self::BatchProof, + opening_challenge: G::ScalarField, + rng: &mut R, + ) -> Result<bool, Self::Error> + where + Self::Commitment: 'a, + { + let commitments: BTreeMap<_, _> = commitments.into_iter().map(|c| (c.label(), c)).collect(); + let mut query_to_labels_map = BTreeMap::new(); + + for (label, point) in query_set.iter() { + let labels = query_to_labels_map.entry(point).or_insert(BTreeSet::new()); + labels.insert(label); + } + + assert_eq!(proof.len(), query_to_labels_map.len()); + + let mut randomizer = G::ScalarField::one(); + + let mut combined_check_poly = Polynomial::zero(); + let mut combined_final_key = G::Projective::zero(); + + for ((query, labels), p) in query_to_labels_map.into_iter().zip(proof) { + let lc_time = + start_timer!(|| format!("Randomly combining {} commitments", labels.len())); + let mut comms: Vec<&'_ LabeledCommitment<_>> = Vec::new(); + let mut vals = Vec::new(); + for label in labels.into_iter() { + let commitment = commitments.get(label).ok_or(Error::MissingPolynomial { + label: label.to_string(), + })?; + + let v_i = values + .get(&(label.clone(), *query)) + .ok_or(Error::MissingEvaluation { + label: label.to_string(), + })?; + + comms.push(commitment); + vals.push(*v_i); + } + + let check_poly = Self::succinct_check( + vk, + comms.into_iter(), + *query, + vals.into_iter(), + p, + opening_challenge, + ); + + if check_poly.is_none() { + return Ok(false); + } + + let check_poly = + Polynomial::from_coefficients_vec(check_poly.unwrap().compute_coeffs()); + combined_check_poly += (randomizer, &check_poly); + combined_final_key += &p.final_comm_key.into_projective().mul(randomizer); + + randomizer = u128::rand(rng).into(); + end_timer!(lc_time); + } + + let proof_time = start_timer!(|| "Checking batched proof"); + let final_key = Self::cm_commit( + vk.comm_key.as_slice(), + combined_check_poly.coeffs.as_slice(), + None, + None, + ); + if !(final_key - &combined_final_key).is_zero() { + return Ok(false); + } + + end_timer!(proof_time); + + Ok(true) + } + + fn open_combinations<'a>( + ck: &Self::CommitterKey, + lc_s: impl IntoIterator<Item = &'a LinearCombination<G::ScalarField>>, + polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<'a, G::ScalarField>>, + commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, + query_set: &QuerySet<G::ScalarField>, + opening_challenge: G::ScalarField, + rands: impl IntoIterator<Item = &'a Self::Randomness>, + rng: Option<&mut dyn RngCore>, + ) -> Result<BatchLCProof<G::ScalarField, Self>, Self::Error> + where + Self::Randomness: 'a, + Self::Commitment: 'a, + { + let label_poly_map = polynomials + .into_iter() + .zip(rands) + .zip(commitments) + .map(|((p, r), c)| (p.label(), (p, r, c))) + .collect::<BTreeMap<_, _>>(); + + let mut lc_polynomials = Vec::new(); + let mut lc_randomness = Vec::new(); + let mut lc_commitments = Vec::new(); + let mut lc_info = Vec::new(); + + for lc in lc_s { + let lc_label = lc.label().clone(); + let mut poly = Polynomial::zero(); + let mut degree_bound = None; + let mut hiding_bound = None; + + let mut combined_comm = G::Projective::zero(); + let mut combined_shifted_comm: Option<G::Projective> = None; + + let mut combined_rand = G::ScalarField::zero(); + let mut combined_shifted_rand: Option<G::ScalarField> = None; + + let num_polys = lc.len(); + for (coeff, label) in lc.iter().filter(|(_, l)| !l.is_one()) { + let label: &String = label.try_into().expect("cannot be one!"); + let &(cur_poly, cur_rand, cur_comm) = + label_poly_map.get(label).ok_or(Error::MissingPolynomial { + label: label.to_string(), + })?; + + if num_polys == 1 && cur_poly.degree_bound().is_some() { + assert!( + coeff.is_one(), + "Coefficient must be one for degree-bounded equations" + ); + degree_bound = cur_poly.degree_bound(); + } else if cur_poly.degree_bound().is_some() { + eprintln!("Degree bound when number of equations is non-zero"); + return Err(Self::Error::EquationHasDegreeBounds(lc_label)); + } + + // Some(_) > None, always. + hiding_bound = core::cmp::max(hiding_bound, cur_poly.hiding_bound()); + poly += (*coeff, cur_poly.polynomial()); + + combined_rand += &(cur_rand.rand * coeff); + combined_shifted_rand = Self::combine_shifted_rand( + combined_shifted_rand, + cur_rand.shifted_rand, + *coeff, + ); + + let commitment = cur_comm.commitment(); + combined_comm += &commitment.comm.mul(*coeff); + combined_shifted_comm = Self::combine_shifted_comm( + combined_shifted_comm, + commitment.shifted_comm, + *coeff, + ); + } + + let lc_poly = + LabeledPolynomial::new_owned(lc_label.clone(), poly, degree_bound, hiding_bound); + lc_polynomials.push(lc_poly); + lc_randomness.push(Randomness { + rand: combined_rand, + shifted_rand: combined_shifted_rand, + }); + + lc_commitments.push(combined_comm); + if let Some(combined_shifted_comm) = combined_shifted_comm { + lc_commitments.push(combined_shifted_comm); + } + + lc_info.push((lc_label, degree_bound)); + } + + let lc_commitments = Self::construct_labeled_commitments(&lc_info, &lc_commitments); + + let proof = Self::batch_open( + ck, + lc_polynomials.iter(), + lc_commitments.iter(), + &query_set, + opening_challenge, + lc_randomness.iter(), + rng, + )?; + Ok(BatchLCProof { proof, evals: None }) + } + + /// Checks that `values` are the true evaluations at `query_set` of the polynomials + /// committed in `labeled_commitments`. + fn check_combinations<'a, R: RngCore>( + vk: &Self::VerifierKey, + lc_s: impl IntoIterator<Item = &'a LinearCombination<G::ScalarField>>, + commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, + query_set: &QuerySet<G::ScalarField>, + evaluations: &Evaluations<G::ScalarField>, + proof: &BatchLCProof<G::ScalarField, Self>, + opening_challenge: G::ScalarField, + rng: &mut R, + ) -> Result<bool, Self::Error> + where + Self::Commitment: 'a, + { + let BatchLCProof { proof, .. } = proof; + let label_comm_map = commitments + .into_iter() + .map(|c| (c.label(), c)) + .collect::<BTreeMap<_, _>>(); + + let mut lc_commitments = Vec::new(); + let mut lc_info = Vec::new(); + let mut evaluations = evaluations.clone(); + for lc in lc_s { + let lc_label = lc.label().clone(); + let num_polys = lc.len(); + + let mut degree_bound = None; + let mut combined_comm = G::Projective::zero(); + let mut combined_shifted_comm: Option<G::Projective> = None; + + for (coeff, label) in lc.iter() { + if label.is_one() { + for (&(ref label, _), ref mut eval) in evaluations.iter_mut() { + if label == &lc_label { + **eval -= coeff; + } + } + } else { + let label: &String = label.try_into().unwrap(); + let &cur_comm = label_comm_map.get(label).ok_or(Error::MissingPolynomial { + label: label.to_string(), + })?; + + if num_polys == 1 && cur_comm.degree_bound().is_some() { + assert!( + coeff.is_one(), + "Coefficient must be one for degree-bounded equations" + ); + degree_bound = cur_comm.degree_bound(); + } else if cur_comm.degree_bound().is_some() { + return Err(Self::Error::EquationHasDegreeBounds(lc_label)); + } + + let commitment = cur_comm.commitment(); + combined_comm += &commitment.comm.mul(*coeff); + combined_shifted_comm = Self::combine_shifted_comm( + combined_shifted_comm, + commitment.shifted_comm, + *coeff, + ); + } + } + + lc_commitments.push(combined_comm); + + if let Some(combined_shifted_comm) = combined_shifted_comm { + lc_commitments.push(combined_shifted_comm); + } + + lc_info.push((lc_label, degree_bound)); + } + + let lc_commitments = Self::construct_labeled_commitments(&lc_info, &lc_commitments); + + Self::batch_check( + vk, + &lc_commitments, + &query_set, + &evaluations, + proof, + opening_challenge, + rng, + ) + } +} + +#[cfg(test)] +mod tests { + #![allow(non_camel_case_types)] + + use super::InnerProductArgPC; + + use algebra::jubjub::JubJubAffine; + use blake2::Blake2s; + + type PC<E, D> = InnerProductArgPC<E, D>; + type PC_JJB2S = PC<JubJubAffine, Blake2s>; + + #[test] + fn single_poly_test() { + use crate::tests::*; + single_poly_test::<_, PC_JJB2S>().expect("test failed for jubjub-blake2s"); + } + + #[test] + fn quadratic_poly_degree_bound_multiple_queries_test() { + use crate::tests::*; + quadratic_poly_degree_bound_multiple_queries_test::<_, PC_JJB2S>() + .expect("test failed for jubjub-blake2s"); + } + + #[test] + fn linear_poly_degree_bound_test() { + use crate::tests::*; + linear_poly_degree_bound_test::<_, PC_JJB2S>().expect("test failed for jubjub-blake2s"); + } + + #[test] + fn single_poly_degree_bound_test() { + use crate::tests::*; + single_poly_degree_bound_test::<_, PC_JJB2S>().expect("test failed for jubjub-blake2s"); + } + + #[test] + fn single_poly_degree_bound_multiple_queries_test() { + use crate::tests::*; + single_poly_degree_bound_multiple_queries_test::<_, PC_JJB2S>() + .expect("test failed for jubjub-blake2s"); + } + + #[test] + fn two_polys_degree_bound_single_query_test() { + use crate::tests::*; + two_polys_degree_bound_single_query_test::<_, PC_JJB2S>() + .expect("test failed for jubjub-blake2s"); + } + + #[test] + fn full_end_to_end_test() { + use crate::tests::*; + full_end_to_end_test::<_, PC_JJB2S>().expect("test failed for jubjub-blake2s"); + println!("Finished jubjub-blake2s"); + } + + #[test] + fn single_equation_test() { + use crate::tests::*; + single_equation_test::<_, PC_JJB2S>().expect("test failed for jubjub-blake2s"); + println!("Finished jubjub-blake2s"); + } + + #[test] + fn two_equation_test() { + use crate::tests::*; + two_equation_test::<_, PC_JJB2S>().expect("test failed for jubjub-blake2s"); + println!("Finished jubjub-blake2s"); + } + + #[test] + fn two_equation_degree_bound_test() { + use crate::tests::*; + two_equation_degree_bound_test::<_, PC_JJB2S>().expect("test failed for jubjub-blake2s"); + println!("Finished jubjub-blake2s"); + } + + #[test] + fn full_end_to_end_equation_test() { + use crate::tests::*; + full_end_to_end_equation_test::<_, PC_JJB2S>().expect("test failed for jubjub-blake2s"); + println!("Finished jubjub-blake2s"); + } + + #[test] + #[should_panic] + fn bad_degree_bound_test() { + use crate::tests::*; + bad_degree_bound_test::<_, PC_JJB2S>().expect("test failed for jubjub-blake2s"); + println!("Finished jubjub-blake2s"); + } +} diff --git a/src/kzg10/data_structures.rs b/src/kzg10/data_structures.rs index 02dca76..9f88c5f 100644 --- a/src/kzg10/data_structures.rs +++ b/src/kzg10/data_structures.rs @@ -233,6 +233,9 @@ impl<E: PairingEngine> ToBytes for Proof<E> { #[inline] fn write<W: algebra_core::io::Write>(&self, mut writer: W) -> algebra_core::io::Result<()> { self.w.write(&mut writer)?; - self.random_v.write(&mut writer) + self.random_v + .as_ref() + .unwrap_or(&E::Fr::zero()) + .write(&mut writer) } } diff --git a/src/kzg10/mod.rs b/src/kzg10/mod.rs index edd2329..1fa2a8b 100644 --- a/src/kzg10/mod.rs +++ b/src/kzg10/mod.rs @@ -2,7 +2,7 @@ //! single polynomial `p`, and then later provide an evaluation proof that //! convinces verifiers that a claimed value `v` is the true evaluation of `p` //! at a chosen point `x`. Our construction follows the template of the construction -//! proposed by Kate, Zaverucha, and Goldberg ([KZG10](http://cacr.uwaterloo.ca/techreports/2010/cacr2010-10.pdf)). +//! proposed by Kate, Zaverucha, and Goldberg ([KZG11](http://cacr.uwaterloo.ca/techreports/2010/cacr2010-10.pdf)). //! This construction achieves extractability in the algebraic group model (AGM). use crate::{Error, LabeledPolynomial, PCRandomness, Polynomial, ToString, Vec}; @@ -19,8 +19,6 @@ use core::marker::PhantomData; mod data_structures; pub use data_structures::*; -pub(crate) mod optional_rng; - /// `KZG10` is an implementation of the polynomial commitment scheme of /// [Kate, Zaverucha and Goldbgerg][kzg10] /// @@ -465,7 +463,6 @@ mod tests { use algebra::test_rng; use algebra::Bls12_377; use algebra::Bls12_381; - use algebra::SW6; type KZG_Bls12_381 = KZG10<Bls12_381>; @@ -612,19 +609,16 @@ mod tests { fn end_to_end_test() { end_to_end_test_template::<Bls12_377>().expect("test failed for bls12-377"); end_to_end_test_template::<Bls12_381>().expect("test failed for bls12-381"); - end_to_end_test_template::<SW6>().expect("test failed for SW6"); } #[test] fn linear_polynomial_test() { linear_polynomial_test_template::<Bls12_377>().expect("test failed for bls12-377"); linear_polynomial_test_template::<Bls12_381>().expect("test failed for bls12-381"); - linear_polynomial_test_template::<SW6>().expect("test failed for SW6"); } #[test] fn batch_check_test() { batch_check_test_template::<Bls12_377>().expect("test failed for bls12-377"); batch_check_test_template::<Bls12_381>().expect("test failed for bls12-381"); - batch_check_test_template::<SW6>().expect("test failed for SW6"); } } diff --git a/src/kzg10/optional_rng.rs b/src/kzg10/optional_rng.rs deleted file mode 100644 index f31609a..0000000 --- a/src/kzg10/optional_rng.rs +++ /dev/null @@ -1,31 +0,0 @@ -use rand_core::RngCore; - -// This trick is necessary because `Option<&mut R>` is not implicitly reborrowed -// like `&mut R` is. As a result, we define a dummy rng here that should be used -// when `commit` gets `rng = None` -// -// Basically, we define a "dummy rng" that does nothing -// (corresponding to the case that `rng = None`). -pub(crate) struct OptionalRng<R>(pub(crate) Option<R>); - -impl<R: RngCore> RngCore for OptionalRng<R> { - #[inline] - fn next_u32(&mut self) -> u32 { - (&mut self.0).as_mut().map_or(0, |r| r.next_u32()) - } - - #[inline] - fn next_u64(&mut self) -> u64 { - (&mut self.0).as_mut().map_or(0, |r| r.next_u64()) - } - - #[inline] - fn fill_bytes(&mut self, dest: &mut [u8]) { - (&mut self.0).as_mut().map_or((), |r| r.fill_bytes(dest)) - } - - #[inline] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { - Ok(self.fill_bytes(dest)) - } -} diff --git a/src/lib.rs b/src/lib.rs index 8155d32..3fa9b38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,8 @@ #![deny(unused_import_braces, unused_qualifications, trivial_casts)] #![deny(trivial_numeric_casts, private_in_public, variant_size_differences)] #![deny(stable_features, unreachable_pub, non_shorthand_field_patterns)] -#![deny(unused_attributes, unused_mut, missing_docs)] +#![deny(unused_attributes, unused_mut)] +#![deny(missing_docs)] #![deny(unused_imports)] #![deny(renamed_and_removed_lints, stable_features, unused_allocation)] #![deny(unused_comparisons, bare_trait_objects, unused_must_use, const_err)] @@ -47,6 +48,10 @@ pub use data_structures::*; pub mod error; pub use error::*; +/// A random number generator that bypasses some limitations of the Rust borrow +/// checker. +pub mod optional_rng; + #[cfg(not(feature = "std"))] macro_rules! eprintln { () => {}; @@ -63,7 +68,7 @@ pub mod kzg10; /// /// [kzg]: http://cacr.uwaterloo.ca/techreports/2010/cacr2010-10.pdf /// [marlin]: https://eprint.iacr.org/2019/1047 -pub mod marlin_kzg10; +pub mod marlin_pc; /// Polynomial commitment scheme based on the construction in [[KZG10]][kzg], /// modified to obtain batching and to enforce strict @@ -75,7 +80,14 @@ pub mod marlin_kzg10; /// [sonic]: https://eprint.iacr.org/2019/099 /// [al]: https://eprint.iacr.org/2019/601 /// [marlin]: https://eprint.iacr.org/2019/1047 -pub mod sonic_kzg10; +pub mod sonic_pc; + +/// A polynomial commitment scheme based on the hardness of the +/// discrete logarithm problem in prime-order groups. +/// The construction is detailed in [[BCMS20]][pcdas]. +/// +/// [pcdas]: https://eprint.iacr.org/2020/499 +pub mod ipa_pc; /// `QuerySet` is the set of queries that are to be made to a set of labeled polynomials/equations /// `p` that have previously been committed to. Each element of a `QuerySet` is a `(label, query)` @@ -162,34 +174,42 @@ pub trait PolynomialCommitment<F: Field>: Sized { fn open<'a>( ck: &Self::CommitterKey, labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<'a, F>>, + commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, point: F, opening_challenge: F, rands: impl IntoIterator<Item = &'a Self::Randomness>, + rng: Option<&mut dyn RngCore>, ) -> Result<Self::Proof, Self::Error> where - Self::Randomness: 'a; + Self::Randomness: 'a, + Self::Commitment: 'a; /// On input a list of labeled polynomials and a query set, `open` outputs a proof of evaluation /// of the polynomials at the points in the query set. fn batch_open<'a>( ck: &Self::CommitterKey, labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<'a, F>>, + commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, query_set: &QuerySet<F>, opening_challenge: F, rands: impl IntoIterator<Item = &'a Self::Randomness>, + rng: Option<&mut dyn RngCore>, ) -> Result<Self::BatchProof, Self::Error> where Self::Randomness: 'a, + Self::Commitment: 'a, { - let polynomials_with_rands: BTreeMap<_, _> = labeled_polynomials + let rng = &mut crate::optional_rng::OptionalRng(rng); + let poly_rand_comm: BTreeMap<_, _> = labeled_polynomials .into_iter() .zip(rands) - .map(|(poly, r)| (poly.label(), (poly, r))) + .zip(commitments.into_iter()) + .map(|((poly, r), comm)| (poly.label(), (poly, r, comm))) .collect(); let open_time = start_timer!(|| format!( "Opening {} polynomials at query set of size {}", - polynomials_with_rands.len(), + poly_rand_comm.len(), query_set.len(), )); @@ -204,18 +224,30 @@ pub trait PolynomialCommitment<F: Field>: Sized { for (query, labels) in query_to_labels_map.into_iter() { let mut query_polys: Vec<&'a LabeledPolynomial<'a, _>> = Vec::new(); let mut query_rands: Vec<&'a Self::Randomness> = Vec::new(); + let mut query_comms: Vec<&'a LabeledCommitment<Self::Commitment>> = Vec::new(); + for label in labels { - let (polynomial, rand) = - polynomials_with_rands - .get(label) - .ok_or(Error::MissingPolynomial { - label: label.to_string(), - })?; + let (polynomial, rand, comm) = + poly_rand_comm.get(label).ok_or(Error::MissingPolynomial { + label: label.to_string(), + })?; + query_polys.push(polynomial); query_rands.push(rand); + query_comms.push(comm); } + let proof_time = start_timer!(|| "Creating proof"); - let proof = Self::open(ck, query_polys, *query, opening_challenge, query_rands)?; + let proof = Self::open( + ck, + query_polys, + query_comms, + *query, + opening_challenge, + query_rands, + Some(rng), + )?; + end_timer!(proof_time); proofs.push(proof); @@ -227,13 +259,14 @@ pub trait PolynomialCommitment<F: Field>: Sized { /// Verifies that `values` are the evaluations at `point` of the polynomials /// committed inside `commitments`. - fn check<'a>( + fn check<'a, R: RngCore>( vk: &Self::VerifierKey, commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, point: F, values: impl IntoIterator<Item = F>, proof: &Self::Proof, opening_challenge: F, + rng: &mut R, ) -> Result<bool, Self::Error> where Self::Commitment: 'a; @@ -247,7 +280,7 @@ pub trait PolynomialCommitment<F: Field>: Sized { evaluations: &Evaluations<F>, proof: &Self::BatchProof, opening_challenge: F, - _rng: &mut R, + rng: &mut R, ) -> Result<bool, Self::Error> where Self::Commitment: 'a, @@ -285,7 +318,7 @@ pub trait PolynomialCommitment<F: Field>: Sized { } let proof_time = start_timer!(|| "Checking per-query proof"); - result &= Self::check(vk, comms, *query, values, &proof, opening_challenge)?; + result &= Self::check(vk, comms, *query, values, &proof, opening_challenge, rng)?; end_timer!(proof_time); } Ok(result) @@ -298,19 +331,30 @@ pub trait PolynomialCommitment<F: Field>: Sized { ck: &Self::CommitterKey, linear_combinations: impl IntoIterator<Item = &'a LinearCombination<F>>, polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<'a, F>>, + commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, query_set: &QuerySet<F>, opening_challenge: F, rands: impl IntoIterator<Item = &'a Self::Randomness>, + rng: Option<&mut dyn RngCore>, ) -> Result<BatchLCProof<F, Self>, Self::Error> where Self::Randomness: 'a, + Self::Commitment: 'a, { let linear_combinations: Vec<_> = linear_combinations.into_iter().collect(); let polynomials: Vec<_> = polynomials.into_iter().collect(); let poly_query_set = lc_query_set_to_poly_query_set(linear_combinations.iter().copied(), query_set); let poly_evals = evaluate_query_set(polynomials.iter().copied(), &poly_query_set); - let proof = Self::batch_open(ck, polynomials, &poly_query_set, opening_challenge, rands)?; + let proof = Self::batch_open( + ck, + polynomials, + commitments, + &poly_query_set, + opening_challenge, + rands, + rng, + )?; Ok(BatchLCProof { proof, evals: Some(poly_evals.values().copied().collect()), @@ -438,6 +482,83 @@ pub mod tests { num_equations: Option<usize>, } + pub fn bad_degree_bound_test<F, PC>() -> Result<(), PC::Error> + where + F: Field, + PC: PolynomialCommitment<F>, + { + let rng = &mut test_rng(); + let max_degree = 100; + let pp = PC::setup(max_degree, rng)?; + + for _ in 0..10 { + let supported_degree = rand::distributions::Uniform::from(1..=max_degree).sample(rng); + assert!( + max_degree >= supported_degree, + "max_degree < supported_degree" + ); + + let mut labels = Vec::new(); + let mut polynomials = Vec::new(); + let mut degree_bounds = Vec::new(); + + for i in 0..10 { + let label = format!("Test{}", i); + labels.push(label.clone()); + let poly = Polynomial::rand(supported_degree, rng); + + let degree_bound = 1usize; + let hiding_bound = Some(1); + degree_bounds.push(degree_bound); + + polynomials.push(LabeledPolynomial::new_owned( + label, + poly, + Some(degree_bound), + hiding_bound, + )) + } + + println!("supported degree: {:?}", supported_degree); + let (ck, vk) = PC::trim(&pp, supported_degree, Some(degree_bounds.as_slice()))?; + println!("Trimmed"); + + let (comms, rands) = PC::commit(&ck, &polynomials, Some(rng))?; + + let mut query_set = QuerySet::new(); + let mut values = Evaluations::new(); + let point = F::rand(rng); + for (i, label) in labels.iter().enumerate() { + query_set.insert((label.clone(), point)); + let value = polynomials[i].evaluate(point); + values.insert((label.clone(), point), value); + } + println!("Generated query set"); + + let opening_challenge = F::rand(rng); + let proof = PC::batch_open( + &ck, + &polynomials, + &comms, + &query_set, + opening_challenge, + &rands, + Some(rng), + )?; + let result = PC::batch_check( + &vk, + &comms, + &query_set, + &values, + &proof, + opening_challenge, + rng, + )?; + assert!(result, "proof was incorrect, Query set: {:#?}", query_set); + } + Ok(()) + } + fn test_template<F, PC>(info: TestInfo) -> Result<(), PC::Error> where F: Field, @@ -533,7 +654,15 @@ pub mod tests { println!("Generated query set"); let opening_challenge = F::rand(rng); - let proof = PC::batch_open(&ck, &polynomials, &query_set, opening_challenge, &rands)?; + let proof = PC::batch_open( + &ck, + &polynomials, + &comms, + &query_set, + opening_challenge, + &rands, + Some(rng), + )?; let result = PC::batch_check( &vk, &comms, @@ -694,9 +823,11 @@ pub mod tests { &ck, &linear_combinations, &polynomials, + &comms, &query_set, opening_challenge, &rands, + Some(rng), )?; println!("Generated proof"); let result = PC::check_combinations( diff --git a/src/marlin_kzg10/data_structures.rs b/src/marlin_pc/data_structures.rs similarity index 98% rename from src/marlin_kzg10/data_structures.rs rename to src/marlin_pc/data_structures.rs index 91d9dc4..12bd6a9 100644 --- a/src/marlin_kzg10/data_structures.rs +++ b/src/marlin_pc/data_structures.rs @@ -7,7 +7,7 @@ use crate::kzg10; /// `UniversalParams` are the universal parameters for the KZG10 scheme. pub type UniversalParams<E> = kzg10::UniversalParams<E>; -/// `ComitterKey` is used to commit to and create evaluation proofs for a given +/// `CommitterKey` is used to commit to and create evaluation proofs for a given /// polynomial. #[derive(Derivative)] #[derivative( diff --git a/src/marlin_kzg10/mod.rs b/src/marlin_pc/mod.rs similarity index 92% rename from src/marlin_kzg10/mod.rs rename to src/marlin_pc/mod.rs index 0f94fc3..c24332b 100644 --- a/src/marlin_kzg10/mod.rs +++ b/src/marlin_pc/mod.rs @@ -14,6 +14,13 @@ pub use data_structures::*; /// Polynomial commitment based on [[KZG10]][kzg], with degree enforcement, batching, /// and (optional) hiding property taken from [[CHMMVW20, “Marlinâ€]][marlin]. /// +/// Degree bound enforcement requires that (at least one of) the points at +/// which a committed polynomial is evaluated are from a distribution that is +/// random conditioned on the polynomial. This is because degree bound +/// enforcement relies on checking a polynomial identity at this point. +/// More formally, the points must be sampled from an admissible query sampler, +/// as detailed in [[CHMMVW20]][marlin]. +/// /// [kzg]: http://cacr.uwaterloo.ca/techreports/2010/cacr2010-10.pdf /// [marlin]: https://eprint.iacr.org/2019/104 pub struct MarlinKZG10<E: PairingEngine> { @@ -260,12 +267,12 @@ impl<E: PairingEngine> PolynomialCommitment<E::Fr> for MarlinKZG10<E> { ), Self::Error, > { + let rng = &mut crate::optional_rng::OptionalRng(rng); let commit_time = start_timer!(|| "Committing to polynomials"); let mut commitments = Vec::new(); let mut randomness = Vec::new(); - let rng = &mut kzg10::optional_rng::OptionalRng(rng); for p in polynomials { let label = p.label(); let degree_bound = p.degree_bound(); @@ -322,12 +329,15 @@ impl<E: PairingEngine> PolynomialCommitment<E::Fr> for MarlinKZG10<E> { fn open<'a>( ck: &Self::CommitterKey, labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<'a, E::Fr>>, + _commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, point: E::Fr, opening_challenge: E::Fr, rands: impl IntoIterator<Item = &'a Self::Randomness>, + _rng: Option<&mut dyn RngCore>, ) -> Result<Self::Proof, Self::Error> where Self::Randomness: 'a, + Self::Commitment: 'a, { let mut p = Polynomial::zero(); let mut r = kzg10::Randomness::empty(); @@ -408,13 +418,14 @@ impl<E: PairingEngine> PolynomialCommitment<E::Fr> for MarlinKZG10<E> { /// Verifies that `value` is the evaluation at `x` of the polynomial /// committed inside `comm`. - fn check<'a>( + fn check<'a, R: RngCore>( vk: &Self::VerifierKey, commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, point: E::Fr, values: impl IntoIterator<Item = E::Fr>, proof: &Self::Proof, opening_challenge: E::Fr, + _rng: &mut R, ) -> Result<bool, Self::Error> where Self::Commitment: 'a, @@ -511,38 +522,46 @@ impl<E: PairingEngine> PolynomialCommitment<E::Fr> for MarlinKZG10<E> { ck: &Self::CommitterKey, lc_s: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>, polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<'a, E::Fr>>, + commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, query_set: &QuerySet<E::Fr>, opening_challenge: E::Fr, rands: impl IntoIterator<Item = &'a Self::Randomness>, + rng: Option<&mut dyn RngCore>, ) -> Result<BatchLCProof<E::Fr, Self>, Self::Error> where Self::Randomness: 'a, + Self::Commitment: 'a, { - let label_poly_rand_map = polynomials + let label_map = polynomials .into_iter() .zip(rands) - .map(|(p, r)| (p.label(), (p, r))) + .zip(commitments) + .map(|((p, r), c)| (p.label(), (p, r, c))) .collect::<BTreeMap<_, _>>(); let mut lc_polynomials = Vec::new(); let mut lc_randomness = Vec::new(); + let mut lc_commitments = Vec::new(); + let mut lc_info = Vec::new(); + for lc in lc_s { let lc_label = lc.label().clone(); let mut poly = Polynomial::zero(); let mut degree_bound = None; let mut hiding_bound = None; + let mut randomness = Self::Randomness::empty(); assert!(randomness.shifted_rand.is_none()); + let mut coeffs_and_comms = Vec::new(); + let num_polys = lc.len(); for (coeff, label) in lc.iter().filter(|(_, l)| !l.is_one()) { let label: &String = label.try_into().expect("cannot be one!"); - let &(cur_poly, cur_rand) = - label_poly_rand_map - .get(label) - .ok_or(Error::MissingPolynomial { - label: label.to_string(), - })?; + let &(cur_poly, cur_rand, cur_comm) = + label_map.get(label).ok_or(Error::MissingPolynomial { + label: label.to_string(), + })?; if num_polys == 1 && cur_poly.degree_bound().is_some() { assert!( @@ -559,23 +578,38 @@ impl<E: PairingEngine> PolynomialCommitment<E::Fr> for MarlinKZG10<E> { hiding_bound = core::cmp::max(hiding_bound, cur_poly.hiding_bound()); poly += (*coeff, cur_poly.polynomial()); randomness += (*coeff, cur_rand); + coeffs_and_comms.push((*coeff, cur_comm.commitment())); if degree_bound.is_none() { assert!(randomness.shifted_rand.is_none()); } } - let lc_poly = LabeledPolynomial::new_owned(lc_label, poly, degree_bound, hiding_bound); + + let lc_poly = + LabeledPolynomial::new_owned(lc_label.clone(), poly, degree_bound, hiding_bound); lc_polynomials.push(lc_poly); lc_randomness.push(randomness); + lc_commitments.push(Self::combine_commitments(coeffs_and_comms)); + lc_info.push((lc_label, degree_bound)); } + let comms = Self::normalize_commitments(lc_commitments); + let lc_commitments = lc_info + .into_iter() + .zip(comms) + .map(|((label, d), c)| LabeledCommitment::new(label, c, d)) + .collect::<Vec<_>>(); + let proof = Self::batch_open( ck, lc_polynomials.iter(), + lc_commitments.iter(), &query_set, opening_challenge, lc_randomness.iter(), + rng, )?; + Ok(BatchLCProof { proof, evals: None }) } @@ -669,22 +703,19 @@ impl<E: PairingEngine> PolynomialCommitment<E::Fr> for MarlinKZG10<E> { mod tests { #![allow(non_camel_case_types)] - use crate::marlin_kzg10::MarlinKZG10; + use super::MarlinKZG10; use algebra::Bls12_377; use algebra::Bls12_381; - use algebra::SW6; type PC<E> = MarlinKZG10<E>; type PC_Bls12_381 = PC<Bls12_381>; type PC_Bls12_377 = PC<Bls12_377>; - type PC_SW6 = PC<SW6>; #[test] fn single_poly_test() { use crate::tests::*; single_poly_test::<_, PC_Bls12_377>().expect("test failed for bls12-377"); single_poly_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); - single_poly_test::<_, PC_SW6>().expect("test failed for SW6"); } #[test] @@ -694,8 +725,6 @@ mod tests { .expect("test failed for bls12-377"); quadratic_poly_degree_bound_multiple_queries_test::<_, PC_Bls12_381>() .expect("test failed for bls12-381"); - quadratic_poly_degree_bound_multiple_queries_test::<_, PC_SW6>() - .expect("test failed for SW6"); } #[test] @@ -703,7 +732,6 @@ mod tests { use crate::tests::*; linear_poly_degree_bound_test::<_, PC_Bls12_377>().expect("test failed for bls12-377"); linear_poly_degree_bound_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); - linear_poly_degree_bound_test::<_, PC_SW6>().expect("test failed for SW6"); } #[test] @@ -711,7 +739,6 @@ mod tests { use crate::tests::*; single_poly_degree_bound_test::<_, PC_Bls12_377>().expect("test failed for bls12-377"); single_poly_degree_bound_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); - single_poly_degree_bound_test::<_, PC_SW6>().expect("test failed for SW6"); } #[test] @@ -721,7 +748,6 @@ mod tests { .expect("test failed for bls12-377"); single_poly_degree_bound_multiple_queries_test::<_, PC_Bls12_381>() .expect("test failed for bls12-381"); - single_poly_degree_bound_multiple_queries_test::<_, PC_SW6>().expect("test failed for SW6"); } #[test] @@ -731,7 +757,6 @@ mod tests { .expect("test failed for bls12-377"); two_polys_degree_bound_single_query_test::<_, PC_Bls12_381>() .expect("test failed for bls12-381"); - two_polys_degree_bound_single_query_test::<_, PC_SW6>().expect("test failed for SW6"); } #[test] @@ -741,8 +766,6 @@ mod tests { println!("Finished bls12-377"); full_end_to_end_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); println!("Finished bls12-381"); - full_end_to_end_test::<_, PC_SW6>().expect("test failed for SW6"); - println!("Finished sw6"); } #[test] @@ -752,8 +775,6 @@ mod tests { println!("Finished bls12-377"); single_equation_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); println!("Finished bls12-381"); - single_equation_test::<_, PC_SW6>().expect("test failed for SW6"); - println!("Finished sw6"); } #[test] @@ -763,8 +784,6 @@ mod tests { println!("Finished bls12-377"); two_equation_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); println!("Finished bls12-381"); - two_equation_test::<_, PC_SW6>().expect("test failed for SW6"); - println!("Finished sw6"); } #[test] @@ -774,8 +793,6 @@ mod tests { println!("Finished bls12-377"); two_equation_degree_bound_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); println!("Finished bls12-381"); - two_equation_degree_bound_test::<_, PC_SW6>().expect("test failed for SW6"); - println!("Finished sw6"); } #[test] @@ -785,7 +802,15 @@ mod tests { println!("Finished bls12-377"); full_end_to_end_equation_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); println!("Finished bls12-381"); - full_end_to_end_equation_test::<_, PC_SW6>().expect("test failed for SW6"); - println!("Finished sw6"); + } + + #[test] + #[should_panic] + fn bad_degree_bound_test() { + use crate::tests::*; + bad_degree_bound_test::<_, PC_Bls12_377>().expect("test failed for bls12-377"); + println!("Finished bls12-377"); + bad_degree_bound_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); + println!("Finished bls12-381"); } } diff --git a/src/optional_rng.rs b/src/optional_rng.rs new file mode 100644 index 0000000..62f8ecf --- /dev/null +++ b/src/optional_rng.rs @@ -0,0 +1,52 @@ +use core::num::NonZeroU32; +use rand_core::RngCore; + +/// `OptionalRng` is a hack that is necessary because `Option<&mut R>` is not implicitly reborrowed +/// like `&mut R` is. This causes problems when a variable of type `Option<&mut R>` +/// is moved (eg, in a loop). +/// +/// To overcome this, we define the wrapper `OptionalRng` here that can be borrowed +/// mutably, without fear of being moved. +pub struct OptionalRng<R>(pub Option<R>); + +impl<R: RngCore> RngCore for OptionalRng<R> { + #[inline] + fn next_u32(&mut self) -> u32 { + (&mut self.0) + .as_mut() + .map(|r| r.next_u32()) + .expect("Rng was invoked in a non-hiding context") + } + + #[inline] + fn next_u64(&mut self) -> u64 { + (&mut self.0) + .as_mut() + .map(|r| r.next_u64()) + .expect("Rng was invoked in a non-hiding context") + } + + #[inline] + fn fill_bytes(&mut self, dest: &mut [u8]) { + (&mut self.0) + .as_mut() + .map(|r| r.fill_bytes(dest)) + .expect("Rng was invoked in a non-hiding context") + } + + #[inline] + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + match &mut self.0 { + Some(r) => r.try_fill_bytes(dest), + None => Err(NonZeroU32::new(rand_core::Error::CUSTOM_START) + .unwrap() + .into()), + } + } +} + +impl<R: RngCore> From<R> for OptionalRng<R> { + fn from(other: R) -> Self { + Self(Some(other)) + } +} diff --git a/src/sonic_kzg10/data_structures.rs b/src/sonic_pc/data_structures.rs similarity index 100% rename from src/sonic_kzg10/data_structures.rs rename to src/sonic_pc/data_structures.rs diff --git a/src/sonic_kzg10/mod.rs b/src/sonic_pc/mod.rs similarity index 92% rename from src/sonic_kzg10/mod.rs rename to src/sonic_pc/mod.rs index 1ccc809..0ef9f2a 100644 --- a/src/sonic_kzg10/mod.rs +++ b/src/sonic_pc/mod.rs @@ -259,10 +259,10 @@ impl<E: PairingEngine> PolynomialCommitment<E::Fr> for SonicKZG10<E> { ), Self::Error, > { + let rng = &mut crate::optional_rng::OptionalRng(rng); let commit_time = start_timer!(|| "Committing to polynomials"); let mut labeled_comms: Vec<LabeledCommitment<Self::Commitment>> = Vec::new(); let mut randomness: Vec<Self::Randomness> = Vec::new(); - let rng = &mut kzg10::optional_rng::OptionalRng(rng); for labeled_polynomial in polynomials { let enforced_degree_bounds: Option<&[usize]> = ck @@ -314,12 +314,15 @@ impl<E: PairingEngine> PolynomialCommitment<E::Fr> for SonicKZG10<E> { fn open<'a>( ck: &Self::CommitterKey, labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<'a, E::Fr>>, + _commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, point: E::Fr, opening_challenge: E::Fr, rands: impl IntoIterator<Item = &'a Self::Randomness>, + _rng: Option<&mut dyn RngCore>, ) -> Result<Self::Proof, Self::Error> where Self::Randomness: 'a, + Self::Commitment: 'a, { let mut combined_polynomial = Polynomial::zero(); let mut combined_rand = kzg10::Randomness::empty(); @@ -350,13 +353,14 @@ impl<E: PairingEngine> PolynomialCommitment<E::Fr> for SonicKZG10<E> { Ok(proof) } - fn check<'a>( + fn check<'a, R: RngCore>( vk: &Self::VerifierKey, commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, point: E::Fr, values: impl IntoIterator<Item = E::Fr>, proof: &Self::Proof, opening_challenge: E::Fr, + _rng: &mut R, ) -> Result<bool, Self::Error> where Self::Commitment: 'a, @@ -463,37 +467,43 @@ impl<E: PairingEngine> PolynomialCommitment<E::Fr> for SonicKZG10<E> { ck: &Self::CommitterKey, lc_s: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>, polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<'a, E::Fr>>, + commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>, query_set: &QuerySet<E::Fr>, opening_challenge: E::Fr, rands: impl IntoIterator<Item = &'a Self::Randomness>, + rng: Option<&mut dyn RngCore>, ) -> Result<BatchLCProof<E::Fr, Self>, Self::Error> where Self::Randomness: 'a, + Self::Commitment: 'a, { - let label_poly_rand_map = polynomials + let label_map = polynomials .into_iter() .zip(rands) - .map(|(p, r)| (p.label(), (p, r))) + .zip(commitments) + .map(|((p, r), c)| (p.label(), (p, r, c))) .collect::<BTreeMap<_, _>>(); let mut lc_polynomials = Vec::new(); let mut lc_randomness = Vec::new(); + let mut lc_commitments = Vec::new(); + let mut lc_info = Vec::new(); + for lc in lc_s { let lc_label = lc.label().clone(); let mut poly = Polynomial::zero(); let mut degree_bound = None; let mut hiding_bound = None; let mut randomness = Self::Randomness::empty(); + let mut comm = E::G1Projective::zero(); let num_polys = lc.len(); for (coeff, label) in lc.iter().filter(|(_, l)| !l.is_one()) { let label: &String = label.try_into().expect("cannot be one!"); - let &(cur_poly, cur_rand) = - label_poly_rand_map - .get(label) - .ok_or(Error::MissingPolynomial { - label: label.to_string(), - })?; + let &(cur_poly, cur_rand, curr_comm) = + label_map.get(label).ok_or(Error::MissingPolynomial { + label: label.to_string(), + })?; if num_polys == 1 && cur_poly.degree_bound().is_some() { assert!( @@ -510,18 +520,37 @@ impl<E: PairingEngine> PolynomialCommitment<E::Fr> for SonicKZG10<E> { hiding_bound = core::cmp::max(hiding_bound, cur_poly.hiding_bound()); poly += (*coeff, cur_poly.polynomial()); randomness += (*coeff, cur_rand); + comm += &curr_comm.commitment().0.into_projective().mul(*coeff); } - let lc_poly = LabeledPolynomial::new_owned(lc_label, poly, degree_bound, hiding_bound); + + let lc_poly = + LabeledPolynomial::new_owned(lc_label.clone(), poly, degree_bound, hiding_bound); lc_polynomials.push(lc_poly); lc_randomness.push(randomness); + lc_commitments.push(comm); + lc_info.push((lc_label, degree_bound)); } + let comms: Vec<Self::Commitment> = + E::G1Projective::batch_normalization_into_affine(&lc_commitments) + .into_iter() + .map(|c| kzg10::Commitment::<E>(c)) + .collect(); + + let lc_commitments = lc_info + .into_iter() + .zip(comms) + .map(|((label, d), c)| LabeledCommitment::new(label, c, d)) + .collect::<Vec<_>>(); + let proof = Self::batch_open( ck, lc_polynomials.iter(), + lc_commitments.iter(), &query_set, opening_challenge, lc_randomness.iter(), + rng, )?; Ok(BatchLCProof { proof, evals: None }) } @@ -615,22 +644,19 @@ impl<E: PairingEngine> PolynomialCommitment<E::Fr> for SonicKZG10<E> { mod tests { #![allow(non_camel_case_types)] - use crate::sonic_kzg10::SonicKZG10; + use super::SonicKZG10; use algebra::Bls12_377; use algebra::Bls12_381; - use algebra::SW6; type PC<E> = SonicKZG10<E>; type PC_Bls12_377 = PC<Bls12_377>; type PC_Bls12_381 = PC<Bls12_381>; - type PC_SW6 = PC<SW6>; #[test] fn single_poly_test() { use crate::tests::*; single_poly_test::<_, PC_Bls12_377>().expect("test failed for bls12-377"); single_poly_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); - single_poly_test::<_, PC_SW6>().expect("test failed for SW6"); } #[test] @@ -640,8 +666,6 @@ mod tests { .expect("test failed for bls12-377"); quadratic_poly_degree_bound_multiple_queries_test::<_, PC_Bls12_381>() .expect("test failed for bls12-381"); - quadratic_poly_degree_bound_multiple_queries_test::<_, PC_SW6>() - .expect("test failed for SW6"); } #[test] @@ -649,7 +673,6 @@ mod tests { use crate::tests::*; linear_poly_degree_bound_test::<_, PC_Bls12_377>().expect("test failed for bls12-377"); linear_poly_degree_bound_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); - linear_poly_degree_bound_test::<_, PC_SW6>().expect("test failed for SW6"); } #[test] @@ -657,7 +680,6 @@ mod tests { use crate::tests::*; single_poly_degree_bound_test::<_, PC_Bls12_377>().expect("test failed for bls12-377"); single_poly_degree_bound_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); - single_poly_degree_bound_test::<_, PC_SW6>().expect("test failed for SW6"); } #[test] @@ -667,7 +689,6 @@ mod tests { .expect("test failed for bls12-377"); single_poly_degree_bound_multiple_queries_test::<_, PC_Bls12_381>() .expect("test failed for bls12-381"); - single_poly_degree_bound_multiple_queries_test::<_, PC_SW6>().expect("test failed for SW6"); } #[test] @@ -677,7 +698,6 @@ mod tests { .expect("test failed for bls12-377"); two_polys_degree_bound_single_query_test::<_, PC_Bls12_381>() .expect("test failed for bls12-381"); - two_polys_degree_bound_single_query_test::<_, PC_SW6>().expect("test failed for SW6"); } #[test] @@ -687,8 +707,6 @@ mod tests { println!("Finished bls12-377"); full_end_to_end_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); println!("Finished bls12-381"); - full_end_to_end_test::<_, PC_SW6>().expect("test failed for SW6"); - println!("Finished sw6"); } #[test] @@ -698,8 +716,6 @@ mod tests { println!("Finished bls12-377"); single_equation_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); println!("Finished bls12-381"); - single_equation_test::<_, PC_SW6>().expect("test failed for SW6"); - println!("Finished sw6"); } #[test] @@ -709,8 +725,6 @@ mod tests { println!("Finished bls12-377"); two_equation_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); println!("Finished bls12-381"); - two_equation_test::<_, PC_SW6>().expect("test failed for SW6"); - println!("Finished sw6"); } #[test] @@ -720,8 +734,6 @@ mod tests { println!("Finished bls12-377"); two_equation_degree_bound_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); println!("Finished bls12-381"); - two_equation_degree_bound_test::<_, PC_SW6>().expect("test failed for SW6"); - println!("Finished sw6"); } #[test] @@ -731,7 +743,15 @@ mod tests { println!("Finished bls12-377"); full_end_to_end_equation_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); println!("Finished bls12-381"); - full_end_to_end_equation_test::<_, PC_SW6>().expect("test failed for SW6"); - println!("Finished sw6"); + } + + #[test] + #[should_panic] + fn bad_degree_bound_test() { + use crate::tests::*; + bad_degree_bound_test::<_, PC_Bls12_377>().expect("test failed for bls12-377"); + println!("Finished bls12-377"); + bad_degree_bound_test::<_, PC_Bls12_381>().expect("test failed for bls12-381"); + println!("Finished bls12-381"); } } -- GitLab