diff --git a/Cargo.toml b/Cargo.toml index 75dd24a9..e64e53ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ark-poly-commit" -version = "0.3.0" +version = "0.3.1" authors = [ "Alessandro Chiesa ", "Mary Maller ", @@ -21,28 +21,29 @@ license = "MIT/Apache-2.0" edition = "2018" [dependencies] -ark-serialize = { version = "^0.3.0", default-features = false, features = [ "derive" ] } -ark-ff = { version = "^0.3.0", default-features = false } -ark-ec = { version = "^0.3.0", default-features = false } -ark-poly = {version = "^0.3.0", default-features = false } -ark-sponge = {version = "^0.3.0", default-features = false} +ark-serialize = { version = "0.3", default-features = false, features = [ "derive" ] } +ark-ff = { version = "0.3", default-features = false } +ark-ec = { version = "0.3", default-features = false } +ark-poly = {version = "0.3", default-features = false } +ark-sponge = {version = "0.3", default-features = false} -ark-std = { version = "^0.3.0", default-features = false } -ark-relations = { version = "^0.3.0", default-features = false, optional = true } -ark-r1cs-std = { version = "^0.3.0", default-features = false, optional = true } -ark-nonnative-field = { version = "^0.3.0", default-features = false, optional = true } +ark-std = { version = "0.3", default-features = false } +ark-relations = { version = "0.3", default-features = false, optional = true } +ark-r1cs-std = { version = "0.3", default-features = false, optional = true } +ark-nonnative-field = { version = "0.3", default-features = false, optional = true } hashbrown = { version = "0.9", optional = true } digest = "0.9" rayon = { version = "1", optional = true } derivative = { version = "2", features = [ "use_core" ] } +tracing = { version = "0.1", default-features = false, features = [ "attributes" ] } [dev-dependencies] -ark-ed-on-bls12-381 = { version = "^0.3.0", default-features = false } -ark-bls12-381 = { version = "^0.3.0", default-features = false, features = [ "curve" ] } -ark-bls12-377 = { version = "^0.3.0", default-features = false, features = [ "curve" ] } +ark-ed-on-bls12-381 = { version = "0.3", default-features = false } +ark-bls12-381 = { version = "0.3", default-features = false, features = [ "curve" ] } +ark-bls12-377 = { version = "0.3", default-features = false, features = [ "curve" ] } blake2 = { version = "0.9", default-features = false } -rand_chacha = { version = "0.3.0", default-features = false } +rand_chacha = { version = "0.3", default-features = false } [profile.release] opt-level = 3 @@ -56,16 +57,6 @@ debug-assertions = true incremental = true debug = true -# To be removed in the new release. -[patch.crates-io] -ark-std = { git = "https://github.com/arkworks-rs/std" } -ark-ec = { git = "https://github.com/arkworks-rs/algebra" } -ark-ff = { git = "https://github.com/arkworks-rs/algebra" } -ark-serialize = { git = "https://github.com/arkworks-rs/algebra" } -ark-bls12-381 = { git = "https://github.com/arkworks-rs/curves" } -ark-bls12-377 = { git = "https://github.com/arkworks-rs/curves" } -ark-ed-on-bls12-381 = { git = "https://github.com/arkworks-rs/curves" } - [features] default = [ "std", "parallel" ] std = [ "ark-ff/std", "ark-ec/std", "ark-nonnative-field/std", "ark-poly/std", "ark-std/std", "ark-relations/std", "ark-serialize/std", "ark-sponge/std"] diff --git a/src/data_structures.rs b/src/data_structures.rs index ccf75874..6d155101 100644 --- a/src/data_structures.rs +++ b/src/data_structures.rs @@ -108,6 +108,7 @@ pub struct BatchLCProof>` should be `Vec<_>` pub evals: Option>, } diff --git a/src/ipa_pc/mod.rs b/src/ipa_pc/mod.rs index aabf2add..2d27d83f 100644 --- a/src/ipa_pc/mod.rs +++ b/src/ipa_pc/mod.rs @@ -3,7 +3,7 @@ use crate::{BatchLCProof, Error, Evaluations, QuerySet, UVPolynomial}; use crate::{LabeledCommitment, LabeledPolynomial, LinearCombination}; use crate::{PCCommitterKey, PCRandomness, PCUniversalParams, PolynomialCommitment}; -use ark_ec::{msm::VariableBase, AffineCurve, ProjectiveCurve}; +use ark_ec::{msm::VariableBaseMSM, AffineCurve, ProjectiveCurve}; use ark_ff::{to_bytes, Field, One, PrimeField, UniformRand, Zero}; use ark_std::rand::RngCore; use ark_std::{convert::TryInto, format, marker::PhantomData, vec}; @@ -65,7 +65,7 @@ where .map(|s| s.into_repr()) .collect::>(); - let mut comm = VariableBase::msm(comm_key, &scalars_bigint); + let mut comm = VariableBaseMSM::multi_scalar_mul(comm_key, &scalars_bigint); if randomizer.is_some() { assert!(hiding_generator.is_some()); @@ -701,14 +701,14 @@ where }) } - fn check<'a>( + fn check<'a, R: RngCore>( vk: &Self::VerifierKey, commitments: impl IntoIterator>, point: &'a P::Point, values: impl IntoIterator, proof: &Self::Proof, opening_challenges: &mut ChallengeGenerator, - _rng: Option<&mut dyn RngCore>, + _rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, @@ -759,11 +759,12 @@ where values: &Evaluations, proof: &Self::BatchProof, opening_challenges: &mut ChallengeGenerator, - rng: &mut R, + rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, { + let rng = &mut crate::optional_rng::OptionalRng(rng); let commitments: BTreeMap<_, _> = commitments.into_iter().map(|c| (c.label(), c)).collect(); let mut query_to_labels_map = BTreeMap::new(); @@ -956,7 +957,7 @@ where eqn_evaluations: &Evaluations, proof: &BatchLCProof, opening_challenges: &mut ChallengeGenerator, - rng: &mut R, + rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, diff --git a/src/kzg10/mod.rs b/src/kzg10/mod.rs index 2f5a5a50..608fcf69 100644 --- a/src/kzg10/mod.rs +++ b/src/kzg10/mod.rs @@ -6,7 +6,7 @@ //! This construction achieves extractability in the algebraic group model (AGM). use crate::{BTreeMap, Error, LabeledPolynomial, PCRandomness, ToString, Vec}; -use ark_ec::msm::{FixedBase, VariableBase}; +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; @@ -58,17 +58,21 @@ where cur *= β } - let window_size = FixedBase::get_mul_window_size(max_degree + 1); + let window_size = FixedBaseMSM::get_mul_window_size(max_degree + 1); let scalar_bits = E::Fr::size_in_bits(); let g_time = start_timer!(|| "Generating powers of G"); - let g_table = FixedBase::get_window_table(scalar_bits, window_size, g); - let powers_of_g = - FixedBase::msm::(scalar_bits, window_size, &g_table, &powers_of_beta); + let g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, g); + let powers_of_g = FixedBaseMSM::multi_scalar_mul::( + scalar_bits, + window_size, + &g_table, + &powers_of_beta, + ); end_timer!(g_time); let gamma_g_time = start_timer!(|| "Generating powers of gamma * G"); - let gamma_g_table = FixedBase::get_window_table(scalar_bits, window_size, gamma_g); - let mut powers_of_gamma_g = FixedBase::msm::( + let gamma_g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, gamma_g); + let mut powers_of_gamma_g = FixedBaseMSM::multi_scalar_mul::( scalar_bits, window_size, &gamma_g_table, @@ -95,8 +99,8 @@ where cur /= β } - let neg_h_table = FixedBase::get_window_table(scalar_bits, window_size, h); - let neg_powers_of_h = FixedBase::msm::( + let neg_h_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, h); + let neg_powers_of_h = FixedBaseMSM::multi_scalar_mul::( scalar_bits, window_size, &neg_h_table, @@ -152,8 +156,10 @@ where skip_leading_zeros_and_convert_to_bigints(polynomial); let msm_time = start_timer!(|| "MSM to compute commitment to plaintext poly"); - let mut commitment = - VariableBase::msm(&powers.powers_of_g[num_leading_zeros..], &plain_coeffs); + let mut commitment = VariableBaseMSM::multi_scalar_mul( + &powers.powers_of_g[num_leading_zeros..], + &plain_coeffs, + ); end_timer!(msm_time); let mut randomness = Randomness::::empty(); @@ -175,7 +181,8 @@ where let random_ints = convert_to_bigints(&randomness.blinding_polynomial.coeffs()); let msm_time = start_timer!(|| "MSM to compute commitment to random poly"); let random_commitment = - VariableBase::msm(&powers.powers_of_gamma_g, random_ints.as_slice()).into_affine(); + VariableBaseMSM::multi_scalar_mul(&powers.powers_of_gamma_g, random_ints.as_slice()) + .into_affine(); end_timer!(msm_time); commitment.add_assign_mixed(&random_commitment); @@ -226,7 +233,10 @@ where skip_leading_zeros_and_convert_to_bigints(witness_polynomial); let witness_comm_time = start_timer!(|| "Computing commitment to witness polynomial"); - let mut w = VariableBase::msm(&powers.powers_of_g[num_leading_zeros..], &witness_coeffs); + let mut w = VariableBaseMSM::multi_scalar_mul( + &powers.powers_of_g[num_leading_zeros..], + &witness_coeffs, + ); end_timer!(witness_comm_time); let random_v = if let Some(hiding_witness_polynomial) = hiding_witness_polynomial { @@ -238,7 +248,10 @@ where let random_witness_coeffs = convert_to_bigints(&hiding_witness_polynomial.coeffs()); let witness_comm_time = start_timer!(|| "Computing commitment to random witness polynomial"); - w += &VariableBase::msm(&powers.powers_of_gamma_g, &random_witness_coeffs); + w += &VariableBaseMSM::multi_scalar_mul( + &powers.powers_of_gamma_g, + &random_witness_coeffs, + ); end_timer!(witness_comm_time); Some(blinding_evaluation) } else { @@ -308,11 +321,12 @@ where points: &[E::Fr], values: &[E::Fr], proofs: &[Proof], - rng: &mut R, + rng: Option<&mut R>, ) -> Result { let check_time = start_timer!(|| format!("Checking {} evaluation proofs", commitments.len())); + let rng = &mut crate::optional_rng::OptionalRng(rng); let mut total_c = ::zero(); let mut total_w = ::zero(); @@ -603,7 +617,12 @@ mod tests { proofs.push(proof); } assert!(KZG10::::batch_check( - &vk, &comms, &points, &values, &proofs, rng + &vk, + &comms, + &points, + &values, + &proofs, + Some(rng) )?); } Ok(()) diff --git a/src/lib.rs b/src/lib.rs index a6dc40bd..1e87404d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -222,14 +222,14 @@ pub trait PolynomialCommitment, S: Cryptographic Self::Commitment: 'a; /// check but with individual challenges - fn check<'a>( + fn check<'a, R: RngCore>( vk: &Self::VerifierKey, commitments: impl IntoIterator>, point: &'a P::Point, values: impl IntoIterator, proof: &Self::Proof, challenge_generator: &mut ChallengeGenerator, - rng: Option<&mut dyn RngCore>, + rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a; @@ -242,7 +242,7 @@ pub trait PolynomialCommitment, S: Cryptographic evaluations: &Evaluations, proof: &Self::BatchProof, challenge_generator: &mut ChallengeGenerator, - rng: &mut R, + mut rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, @@ -289,7 +289,7 @@ pub trait PolynomialCommitment, S: Cryptographic values, &proof, challenge_generator, - Some(rng), + rng.as_mut(), )?; end_timer!(proof_time); } @@ -341,7 +341,7 @@ pub trait PolynomialCommitment, S: Cryptographic eqn_evaluations: &Evaluations, proof: &BatchLCProof, challenge_generator: &mut ChallengeGenerator, - rng: &mut R, + rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, @@ -638,7 +638,7 @@ pub mod tests { &values, &proof, &mut (challenge_gen.clone()), - rng, + Some(rng), )?; assert!(result, "proof was incorrect, Query set: {:#?}", query_set); } @@ -774,7 +774,7 @@ pub mod tests { &values, &proof, &mut (challenge_gen.clone()), - rng, + Some(rng), )?; if !result { println!( @@ -955,7 +955,7 @@ pub mod tests { &values, &proof, &mut (challenge_gen.clone()), - rng, + Some(rng), )?; if !result { println!( diff --git a/src/marlin/marlin_pc/constraints.rs b/src/marlin/marlin_pc/constraints.rs new file mode 100644 index 00000000..9d0dff50 --- /dev/null +++ b/src/marlin/marlin_pc/constraints.rs @@ -0,0 +1,1522 @@ +use crate::{ + constraints::{EvaluationsVar, LabeledPointVar, PCCheckRandomDataVar, PCCheckVar, QuerySetVar}, + data_structures::LabeledCommitment, + kzg10::{Proof, VerifierKey as KZG10VerifierKey}, + marlin_pc::{ + data_structures::{Commitment, VerifierKey}, + MarlinKZG10, PreparedCommitment, PreparedVerifierKey, + }, + BTreeMap, BTreeSet, BatchLCProof, LinearCombinationCoeffVar, LinearCombinationVar, + PrepareGadget, String, ToString, Vec, +}; +use ark_ec::{AffineCurve, CurveCycle, PairingEngine, PairingFriendlyCycle}; +use ark_ff::{PrimeField, ToConstraintField}; +use ark_nonnative_field::{NonNativeFieldMulResultVar, NonNativeFieldVar}; +use ark_poly::UVPolynomial; +use ark_r1cs_std::{fields::fp::FpVar, prelude::*, ToConstraintFieldGadget}; +use ark_relations::r1cs::{ConstraintSystemRef, Namespace, Result as R1CSResult, SynthesisError}; +use ark_sponge::CryptographicSponge; +use ark_std::{borrow::Borrow, convert::TryInto, marker::PhantomData, ops::Div, vec}; + +type E2Fr = <::E2 as AffineCurve>::ScalarField; +type E2Fq = <::E2 as AffineCurve>::BaseField; + +/// High level variable representing the verification key of the `MarlinKZG10` polynomial commitment +/// scheme. +#[derive(Derivative)] +#[derivative(Clone(bound = "E: PairingFriendlyCycle"))] +pub struct VerifierKeyVar>> +where + E2Fq: PrimeField, +{ + /// Generator of G1. + pub g: PG::G1Var, + /// Generator of G2. + pub h: PG::G2Var, + /// Generator of G1, times first monomial. + pub beta_h: PG::G2Var, + /// Used for the shift powers associated with different degree bounds. + pub degree_bounds_and_shift_powers: Option>, PG::G1Var)>>, +} + +impl VerifierKeyVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, +{ + /// Find the appropriate shift for the degree bound. + #[tracing::instrument(target = "r1cs", skip(self, cs))] + pub fn get_shift_power( + &self, + cs: impl Into>>, + bound: &FpVar>, + ) -> Option { + // Search the bound using PIR + if self.degree_bounds_and_shift_powers.is_none() { + None + } else { + let ns = cs.into(); + let cs = ns.cs(); + + let degree_bounds_and_shift_powers = + self.degree_bounds_and_shift_powers.clone().unwrap(); + + let mut pir_vector = vec![false; degree_bounds_and_shift_powers.len()]; + + let desired_bound_value = bound.value().unwrap_or_default(); + + for (i, (_, this_bound, _)) in degree_bounds_and_shift_powers.iter().enumerate() { + if this_bound + .value() + .unwrap_or_default() + .eq(&desired_bound_value) + { + pir_vector[i] = true; + break; + } + } + + let mut pir_vector_gadgets = Vec::new(); + for bit in pir_vector.iter() { + pir_vector_gadgets.push( + Boolean::new_witness(ark_relations::ns!(cs, "alloc_pir"), || Ok(bit)).unwrap(), + ); + } + + // Sum of the PIR values are equal to one + let mut sum = FpVar::>::zero(); + let one = FpVar::>::one(); + for pir_gadget in pir_vector_gadgets.iter() { + sum += &FpVar::>::from(pir_gadget.clone()); + } + sum.enforce_equal(&one).unwrap(); + + // PIR the value + let mut found_bound = FpVar::>::zero(); + + let mut found_shift_power = PG::G1Var::zero(); + + for (pir_gadget, (_, degree, shift_power)) in pir_vector_gadgets + .iter() + .zip(degree_bounds_and_shift_powers.iter()) + { + found_bound = + FpVar::>::conditionally_select(pir_gadget, degree, &found_bound) + .unwrap(); + + found_shift_power = + PG::G1Var::conditionally_select(pir_gadget, shift_power, &found_shift_power) + .unwrap(); + } + + found_bound.enforce_equal(&bound).unwrap(); + + Some(found_shift_power) + } + } +} + +impl AllocVar, E2Fq> for VerifierKeyVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, +{ + #[tracing::instrument(target = "r1cs", skip(cs, val))] + fn new_variable( + cs: impl Into>>, + val: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> R1CSResult + where + T: Borrow>, + { + let vk_orig = val()?.borrow().clone(); + + let ns = cs.into(); + let cs = ns.cs(); + + let VerifierKey { + vk, + degree_bounds_and_shift_powers, + .. + } = vk_orig; + + let degree_bounds_and_shift_powers = degree_bounds_and_shift_powers.map(|vec| { + vec.iter() + .map(|(s, g)| { + ( + *s, + FpVar::>::new_variable( + ark_relations::ns!(cs, "degree bound"), + || Ok( as From>::from(*s as u128)), + mode, + ) + .unwrap(), + PG::G1Var::new_variable(ark_relations::ns!(cs, "pow"), || Ok(*g), mode) + .unwrap(), + ) + }) + .collect() + }); + + let KZG10VerifierKey { g, h, beta_h, .. } = vk; + + let g = PG::G1Var::new_variable(ark_relations::ns!(cs, "g"), || Ok(g), mode)?; + let h = PG::G2Var::new_variable(ark_relations::ns!(cs, "h"), || Ok(h), mode)?; + let beta_h = + PG::G2Var::new_variable(ark_relations::ns!(cs, "beta_h"), || Ok(beta_h), mode)?; + + Ok(Self { + g, + h, + beta_h, + degree_bounds_and_shift_powers, + }) + } +} + +impl ToBytesGadget> for VerifierKeyVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, +{ + #[tracing::instrument(target = "r1cs", skip(self))] + fn to_bytes(&self) -> R1CSResult>>> { + let mut bytes = Vec::new(); + + bytes.extend_from_slice(&self.g.to_bytes()?); + bytes.extend_from_slice(&self.h.to_bytes()?); + bytes.extend_from_slice(&self.beta_h.to_bytes()?); + + if self.degree_bounds_and_shift_powers.is_some() { + let degree_bounds_and_shift_powers = + self.degree_bounds_and_shift_powers.as_ref().unwrap(); + for (_, degree_bound, shift_power) in degree_bounds_and_shift_powers.iter() { + bytes.extend_from_slice(°ree_bound.to_bytes()?); + bytes.extend_from_slice(&shift_power.to_bytes()?); + } + } + + Ok(bytes) + } +} + +impl ToConstraintFieldGadget> for VerifierKeyVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, + PG::G1Var: ToConstraintFieldGadget>, + PG::G2Var: ToConstraintFieldGadget>, +{ + #[tracing::instrument(target = "r1cs", skip(self))] + fn to_constraint_field(&self) -> R1CSResult>>> { + let mut res = Vec::new(); + + let mut g_gadget = self.g.to_constraint_field()?; + let mut h_gadget = self.h.to_constraint_field()?; + let mut beta_h_gadget = self.beta_h.to_constraint_field()?; + + res.append(&mut g_gadget); + res.append(&mut h_gadget); + res.append(&mut beta_h_gadget); + + if self.degree_bounds_and_shift_powers.as_ref().is_some() { + let list = self.degree_bounds_and_shift_powers.as_ref().unwrap(); + for (_, d_gadget, shift_power) in list.iter() { + let mut d_elems = d_gadget.to_constraint_field()?; + let mut shift_power_elems = shift_power.to_constraint_field()?; + + res.append(&mut d_elems); + res.append(&mut shift_power_elems); + } + } + + Ok(res) + } +} + +/// High level variable representing the verification key of the `MarlinKZG10` polynomial commitment +/// scheme, prepared for use in arithmetic. +#[allow(clippy::type_complexity)] +#[derive(Derivative)] +#[derivative(Clone(bound = "E: PairingFriendlyCycle"))] +pub struct PreparedVerifierKeyVar>> +where + E2Fq: PrimeField, +{ + /// Generator of G1. + pub prepared_g: Vec, + /// Generator of G2. + pub prepared_h: PG::G2PreparedVar, + /// Generator of G1, times first monomial. + pub prepared_beta_h: PG::G2PreparedVar, + /// Used for the shift powers associated with different degree bounds. + pub prepared_degree_bounds_and_shift_powers: + Option>, Vec)>>, + /// Indicate whether or not it is a constant allocation (which decides whether or not shift + /// powers are precomputed). + pub constant_allocation: bool, + /// If not a constant allocation, the original vk is attached (for computing the shift power + /// series). + pub origin_vk: Option>, +} + +impl PreparedVerifierKeyVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, +{ + /// Find the appropriate shift for the degree bound. + pub fn get_shift_power( + &self, + cs: impl Into>>, + bound: &FpVar>, + ) -> Option> { + if self.constant_allocation { + if self.prepared_degree_bounds_and_shift_powers.is_none() { + None + } else { + let prepared_degree_bounds_and_shift_powers = self + .prepared_degree_bounds_and_shift_powers + .as_ref() + .unwrap(); + let bound_value = bound.value().unwrap_or_default(); + + for (_, bound, prepared_shift_powers) in + prepared_degree_bounds_and_shift_powers.iter() + { + if bound.value().unwrap_or_default() == bound_value { + return Some((*prepared_shift_powers).clone()); + } + } + + None + } + } else { + let shift_power = self.origin_vk.as_ref().unwrap().get_shift_power(cs, bound); + + if let Some(shift_power) = shift_power { + let mut prepared_shift_gadgets = Vec::::new(); + + let supported_bits = E2Fr::::size_in_bits(); + + let mut cur: PG::G1Var = shift_power; + for _ in 0..supported_bits { + prepared_shift_gadgets.push(cur.clone()); + cur.double_in_place().unwrap(); + } + + Some(prepared_shift_gadgets) + } else { + None + } + } + } +} + +impl PrepareGadget, E2Fq> for PreparedVerifierKeyVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, +{ + #[tracing::instrument(target = "r1cs", skip(unprepared))] + fn prepare(unprepared: &VerifierKeyVar) -> R1CSResult { + let supported_bits = as PrimeField>::size_in_bits(); + let mut prepared_g = Vec::::new(); + + let mut g: PG::G1Var = unprepared.g.clone(); + for _ in 0..supported_bits { + prepared_g.push(g.clone()); + g.double_in_place()?; + } + + let prepared_h = PG::prepare_g2(&unprepared.h)?; + let prepared_beta_h = PG::prepare_g2(&unprepared.beta_h)?; + + let prepared_degree_bounds_and_shift_powers = + if unprepared.degree_bounds_and_shift_powers.is_some() { + let mut res = Vec::<(usize, FpVar>, Vec)>::new(); + + for (d, d_gadget, shift_power) in unprepared + .degree_bounds_and_shift_powers + .as_ref() + .unwrap() + .iter() + { + res.push((*d, (*d_gadget).clone(), vec![shift_power.clone()])); + } + + Some(res) + } else { + None + }; + + Ok(Self { + prepared_g, + prepared_h, + prepared_beta_h, + prepared_degree_bounds_and_shift_powers, + constant_allocation: false, + origin_vk: Some(unprepared.clone()), + }) + } +} + +impl AllocVar, E2Fq> for PreparedVerifierKeyVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, +{ + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable( + cs: impl Into>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> R1CSResult + where + T: Borrow>, + { + let t = f()?; + let obj = t.borrow(); + + let ns = cs.into(); + let cs = ns.cs(); + + let mut prepared_g = Vec::::new(); + for g in obj.prepared_vk.prepared_g.iter() { + prepared_g.push(::G1Affine, + E2Fq, + >>::new_variable( + ark_relations::ns!(cs, "g"), || Ok(*g), mode + )?); + } + + let prepared_h = PG::G2PreparedVar::new_variable( + ark_relations::ns!(cs, "h"), + || Ok(&obj.prepared_vk.prepared_h), + mode, + )?; + let prepared_beta_h = PG::G2PreparedVar::new_variable( + ark_relations::ns!(cs, "beta_h"), + || Ok(&obj.prepared_vk.prepared_beta_h), + mode, + )?; + + let prepared_degree_bounds_and_shift_powers = + if obj.prepared_degree_bounds_and_shift_powers.is_some() { + let mut res = Vec::<(usize, FpVar>, Vec)>::new(); + + for (d, shift_power_elems) in obj + .prepared_degree_bounds_and_shift_powers + .as_ref() + .unwrap() + .iter() + { + let mut gadgets = Vec::::new(); + for shift_power_elem in shift_power_elems.iter() { + gadgets.push(::G1Affine, + E2Fq, + >>::new_variable( + cs.clone(), || Ok(shift_power_elem), mode + )?); + } + + let d_gadget = FpVar::>::new_variable( + cs.clone(), + || Ok( as From>::from(*d as u128)), + mode, + )?; + + res.push((*d, d_gadget, gadgets)); + } + Some(res) + } else { + None + }; + + Ok(Self { + prepared_g, + prepared_h, + prepared_beta_h, + prepared_degree_bounds_and_shift_powers, + constant_allocation: true, + origin_vk: None, + }) + } +} + +/// High level variable representing a commitment in the `MarlinKZG10` polynomial commitment scheme. +#[derive(Derivative)] +#[derivative(Clone(bound = "E: PairingFriendlyCycle"))] +pub struct CommitmentVar>> +where + E2Fq: PrimeField, +{ + comm: PG::G1Var, + shifted_comm: Option, +} + +impl AllocVar, E2Fq> for CommitmentVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, +{ + #[tracing::instrument(target = "r1cs", skip(cs, value_gen))] + fn new_variable( + cs: impl Into>>, + value_gen: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> R1CSResult + where + T: Borrow>, + { + value_gen().and_then(|commitment| { + let ns = cs.into(); + let cs = ns.cs(); + + let commitment = *commitment.borrow(); + let comm = commitment.comm; + let comm_gadget = PG::G1Var::new_variable(cs.clone(), || Ok(comm.0), mode)?; + + let shifted_comm = commitment.shifted_comm; + let shifted_comm_gadget = if let Some(shifted_comm) = shifted_comm { + Some(PG::G1Var::new_variable(cs, || Ok(shifted_comm.0), mode)?) + } else { + None + }; + + Ok(Self { + comm: comm_gadget, + shifted_comm: shifted_comm_gadget, + }) + }) + } +} + +impl ToConstraintFieldGadget> for CommitmentVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + E::E2: ToConstraintField>, + PG: PairingVar>, + PG::G1Var: ToConstraintFieldGadget>, +{ + #[tracing::instrument(target = "r1cs", skip(self))] + fn to_constraint_field(&self) -> R1CSResult>>> { + let mut res = Vec::new(); + + let mut comm_gadget = self.comm.to_constraint_field()?; + + res.append(&mut comm_gadget); + + if self.shifted_comm.as_ref().is_some() { + let mut shifted_comm_gadget = + self.shifted_comm.as_ref().unwrap().to_constraint_field()?; + res.append(&mut shifted_comm_gadget); + } + + Ok(res) + } +} + +impl ToBytesGadget> for CommitmentVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, +{ + #[tracing::instrument(target = "r1cs", skip(self))] + fn to_bytes(&self) -> R1CSResult>>> { + let zero_shifted_comm = PG::G1Var::zero(); + + let mut bytes = Vec::new(); + bytes.extend_from_slice(&self.comm.to_bytes()?); + + let shifted_comm = self.shifted_comm.clone().unwrap_or(zero_shifted_comm); + bytes.extend_from_slice(&shifted_comm.to_bytes()?); + Ok(bytes) + } +} + +/// High level variable for a `MarlinKZG10` polynomial commitment, prepared for use in arirthmetic. +/// (`shifted_comm` is not prepared, due to the specific use case.) +#[derive(Derivative)] +#[derivative(Clone(bound = "E: PairingFriendlyCycle"))] +pub struct PreparedCommitmentVar>> { + prepared_comm: Vec, + shifted_comm: Option, +} + +impl PrepareGadget, E2Fq> for PreparedCommitmentVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, +{ + #[tracing::instrument(target = "r1cs", skip(unprepared))] + fn prepare(unprepared: &CommitmentVar) -> R1CSResult { + let mut prepared_comm = Vec::::new(); + let supported_bits = as PrimeField>::size_in_bits(); + + let mut cur: PG::G1Var = unprepared.comm.clone(); + for _ in 0..supported_bits { + prepared_comm.push(cur.clone()); + cur.double_in_place()?; + } + + Ok(Self { + prepared_comm, + shifted_comm: unprepared.shifted_comm.clone(), + }) + } +} + +impl AllocVar, E2Fq> for PreparedCommitmentVar +where + E: PairingFriendlyCycle, + PG: PairingVar>, +{ + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_variable( + cs: impl Into>>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> R1CSResult + where + T: Borrow>, + { + let t = f()?; + let obj = t.borrow(); + + let ns = cs.into(); + let cs = ns.cs(); + + let mut prepared_comm = Vec::::new(); + + for comm_elem in obj.prepared_comm.0.iter() { + prepared_comm.push(::G1Projective, + E2Fq, + >>::new_variable( + ark_relations::ns!(cs, "comm_elem"), + || { + Ok(<::G1Projective as From< + ::G1Affine, + >>::from(*comm_elem)) + }, + mode, + )?); + } + + let shifted_comm = if obj.shifted_comm.is_some() { + Some(::G1Projective, + E2Fq, + >>::new_variable( + ark_relations::ns!(cs, "shifted_comm"), + || { + Ok(<::G1Projective as From< + ::G1Affine, + >>::from(obj.shifted_comm.unwrap().0)) + }, + mode, + )?) + } else { + None + }; + + Ok(Self { + prepared_comm, + shifted_comm, + }) + } +} + +/// High level variable for a `MarlinKZG10` polynomial commitment, along with a string label and a +/// degree bound. +#[derive(Derivative)] +#[derivative(Clone(bound = "E: PairingFriendlyCycle"))] +pub struct LabeledCommitmentVar>> +where + E2Fq: PrimeField, +{ + /// A text label for the commitment. + pub label: String, + /// The plain commitment. + pub commitment: CommitmentVar, + /// Optionally, a bound on the polynomial degree. + pub degree_bound: Option>>, +} + +impl AllocVar>, E2Fq> + for LabeledCommitmentVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, +{ + #[tracing::instrument(target = "r1cs", skip(cs, value_gen))] + fn new_variable( + cs: impl Into>>, + value_gen: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> R1CSResult + where + T: Borrow>>, + { + value_gen().and_then(|labeled_commitment| { + let ns = cs.into(); + let cs = ns.cs(); + + let labeled_commitment = labeled_commitment.borrow().clone(); + let label = labeled_commitment.label().to_string(); + let commitment = labeled_commitment.commitment(); + let degree_bound = labeled_commitment.degree_bound(); + + let commitment = CommitmentVar::new_variable( + ark_relations::ns!(cs, "commitment"), + || Ok(commitment), + mode, + )?; + + let degree_bound = if let Some(degree_bound) = degree_bound { + FpVar::>::new_variable( + ark_relations::ns!(cs, "degree_bound"), + || Ok( as From>::from(degree_bound as u128)), + mode, + ) + .ok() + } else { + None + }; + + Ok(Self { + label, + commitment, + degree_bound, + }) + }) + } +} + +/// High level variable for a `MarlinKZG10` polynomial commitment, along with a string label and a +/// degree bound, prepared for use in arithmetic. +#[derive(Derivative)] +#[derivative(Clone(bound = "E: PairingFriendlyCycle"))] +pub struct PreparedLabeledCommitmentVar< + E: PairingFriendlyCycle, + PG: PairingVar>, +> where + E2Fq: PrimeField, +{ + /// A text label for the commitment. + pub label: String, + /// The plain commitment. + pub prepared_commitment: PreparedCommitmentVar, + /// Optionally, a bound on the polynomial degree. + pub degree_bound: Option>>, +} + +impl PrepareGadget, E2Fq> + for PreparedLabeledCommitmentVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, +{ + #[tracing::instrument(target = "r1cs", skip(unprepared))] + fn prepare(unprepared: &LabeledCommitmentVar) -> R1CSResult { + let prepared_commitment = PreparedCommitmentVar::prepare(&unprepared.commitment)?; + + Ok(Self { + label: unprepared.label.clone(), + prepared_commitment, + degree_bound: unprepared.degree_bound.clone(), + }) + } +} + +/// High level variable for a `MarlinKZG10` opening proof. +#[allow(clippy::type_complexity)] +#[derive(Derivative)] +#[derivative(Clone(bound = "E: PairingFriendlyCycle"))] +pub struct ProofVar>> +where + E2Fq: PrimeField, +{ + /// The commitment to the witness polynomial. + pub w: PG::G1Var, + /// The evaluation of the random hiding polynomial. + pub random_v: Option, E2Fq>>, +} + +impl AllocVar, E2Fq> for ProofVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + PG: PairingVar>, +{ + #[tracing::instrument(target = "r1cs", skip(cs, value_gen))] + fn new_variable( + cs: impl Into>>, + value_gen: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> R1CSResult + where + T: Borrow>, + { + value_gen().and_then(|proof| { + let ns = cs.into(); + let cs = ns.cs(); + + let Proof { w, random_v } = *proof.borrow(); + let w = PG::G1Var::new_variable(ark_relations::ns!(cs, "w"), || Ok(w), mode)?; + + let random_v = match random_v { + None => None, + Some(random_v_inner) => Some(NonNativeFieldVar::new_variable( + ark_relations::ns!(cs, "random_v"), + || Ok(random_v_inner), + mode, + )?), + }; + + Ok(Self { w, random_v }) + }) + } +} + +/// High level variable for a batched `MarlinKZG10` proof, for the opening of a linear combination +/// of polynomials. +#[allow(clippy::type_complexity)] +#[derive(Derivative)] +#[derivative(Clone(bound = "E: PairingFriendlyCycle"))] +pub struct BatchLCProofVar< + E: PairingFriendlyCycle, + P: UVPolynomial, Point = E2Fr>, + PG: PairingVar>, + S: CryptographicSponge, +> where + E2Fq: PrimeField, +{ + /// Evaluation proofs. + pub proofs: Vec>, + /// Evaluations required to verify the proof. + pub evals: Option, E2Fq>>>, + #[doc(hidden)] + pub polynomial: PhantomData

, + _sponge: PhantomData, +} + +impl + AllocVar, Vec::Engine2>>>, E2Fq> + for BatchLCProofVar +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + P: UVPolynomial, Point = E2Fr>, + for<'a, 'b> &'a P: Div<&'b P, Output = P>, + PG: PairingVar>, + S: CryptographicSponge, +{ + #[tracing::instrument(target = "r1cs", skip(cs, value_gen))] + fn new_variable( + cs: impl Into>>, + value_gen: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> R1CSResult + where + T: Borrow, Vec::Engine2>>>>, + { + value_gen().map(|proof| { + let ns = cs.into(); + let cs = ns.cs(); + + let BatchLCProof { proof, evals } = proof.borrow().clone(); + + let proofs: Vec> = proof + .iter() + .map(|p| { + ProofVar::new_variable(ark_relations::ns!(cs, "proof"), || Ok(p), mode).unwrap() + }) + .collect(); + + #[allow(clippy::type_complexity)] + let evals: Option, E2Fq>>> = match evals { + None => None, + Some(evals_inner) => Some( + evals_inner + .iter() + .map(|e| { + NonNativeFieldVar::new_variable( + ark_relations::ns!(cs, "evaluation"), + || Ok(e), + mode, + ) + .unwrap() + }) + .collect(), + ), + }; + + Self { + proofs, + evals, + polynomial: PhantomData, + _sponge: PhantomData, + } + }) + } +} + +/// Helper struct for information about one member of a linear combination. +#[derive(Derivative)] +#[derivative(Clone(bound = "E: PairingFriendlyCycle"))] +pub struct LCItem +where + E: PairingFriendlyCycle, + PG: PairingVar>, + E2Fq: PrimeField, +{ + coeff: Option, E2Fq>>, + degree_bound: Option>>, + comm: PreparedCommitmentVar, + negate: bool, +} + +/// Gadget for the `MarlinKZG10` polynomial commitment verifier. +#[derive(Derivative)] +#[derivative(Clone(bound = "E: PairingFriendlyCycle"))] +pub struct MarlinKZG10Gadget +where + E: PairingFriendlyCycle, + P: UVPolynomial, Point = E2Fr>, + PG: PairingVar>, + S: CryptographicSponge, +{ + _cycle_engine: PhantomData, + _pairing_gadget: PhantomData, + _polynomial: PhantomData

, + _sponge: PhantomData, +} + +impl MarlinKZG10Gadget +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + P: UVPolynomial, Point = E2Fr>, + for<'a, 'b> &'a P: Div<&'b P, Output = P>, + PG: PairingVar>, + S: CryptographicSponge, +{ + #[allow(clippy::type_complexity, clippy::too_many_arguments)] + #[tracing::instrument( + target = "r1cs", + skip(prepared_verification_key, lc_info, query_set, evaluations, proofs) + )] + // rustfmt bug with generics struct + #[rustfmt::skip] + fn prepared_batch_check_evaluations( + cs: ConstraintSystemRef>, + prepared_verification_key: &, + P, + MarlinKZG10, + E2Fq, + S, + >>::PreparedVerifierKeyVar, + lc_info: &[(String, Vec>)], + query_set: &QuerySetVar, E2Fq>, + evaluations: &EvaluationsVar, E2Fq>, + proofs: &[, + P, + MarlinKZG10, + E2Fq, + S, + >>::ProofVar], + opening_challenges: &[NonNativeFieldVar, E2Fq>], + opening_challenges_bits: &[Vec>>], + batching_rands: &[NonNativeFieldVar, E2Fq>], + batching_rands_bits: &[Vec>>], + ) -> R1CSResult>> { + let mut batching_rands = batching_rands.to_vec(); + let mut batching_rands_bits = batching_rands_bits.to_vec(); + + let commitment_lcs: BTreeMap>)> = + lc_info.iter().map(|c| (c.0.clone(), c.clone())).collect(); + + let mut query_to_labels_map = BTreeMap::new(); + + for (label, point) in query_set.0.iter() { + let labels = query_to_labels_map + .entry(point.name.clone()) + .or_insert((point.value.clone(), BTreeSet::new())); + labels.1.insert(label); + } + + // Accumulate commitments and evaluations for each query. + let mut combined_queries = Vec::new(); + let mut combined_comms = Vec::new(); + let mut combined_evals = Vec::new(); + for (_, (point, labels)) in query_to_labels_map.into_iter() { + let mut comms_to_combine = Vec::>>::new(); + let mut values_to_combine = Vec::new(); + for label in labels.into_iter() { + let commitment_lc = commitment_lcs.get(label).unwrap().clone(); + + let v_i = evaluations + .0 + .get(&LabeledPointVar { + name: label.clone(), + value: point.clone(), + }) + .unwrap(); + + comms_to_combine.push(commitment_lc.1.clone()); + values_to_combine.push(v_i.clone()); + } + + // Accumulate the commitments and evaluations corresponding to `query`. + let mut combined_comm = PG::G1Var::zero(); + let mut combined_eval = NonNativeFieldMulResultVar::, E2Fq>::zero(); + + let mut opening_challenges_counter = 0; + + for (commitment_lcs, value) in comms_to_combine.into_iter().zip(values_to_combine) { + let challenge = opening_challenges[opening_challenges_counter].clone(); + + let challenge_bits = opening_challenges_bits[opening_challenges_counter].clone(); + opening_challenges_counter += 1; + + for lc_info in commitment_lcs.iter() { + let LCItem:: { + coeff, + degree_bound, + comm, + negate, + } = lc_info; + let PreparedCommitmentVar { shifted_comm, .. } = comm; + + if coeff.is_none() { + // To combine the commitments, we multiply each by one of the random challenges, and sum. + let mut comm_times_challenge = PG::G1Var::zero(); + { + for (bit, base_power) in + challenge_bits.iter().zip(comm.prepared_comm.iter()) + { + let mut new_encoded = base_power.clone(); + new_encoded += comm_times_challenge.clone(); + comm_times_challenge = PG::G1Var::conditionally_select( + bit, + &new_encoded, + &comm_times_challenge, + )?; + } + } + + if negate.eq(&true) { + comm_times_challenge = comm_times_challenge.negate()?; + } + + combined_comm += comm_times_challenge.clone(); + + // If the degree bound is specified, we include the adjusted degree-shifted commitment + // (that is, c_i' - v_i beta^{D - d_i} G), where d_i is the specific degree bound and + // v_i is the evaluation, in the combined commitment, + if let Some(degree_bound) = degree_bound { + let challenge_shifted_bits = + opening_challenges_bits[opening_challenges_counter].clone(); + opening_challenges_counter += 1; + + let mut shifted_comm = shifted_comm.clone().unwrap(); + + if negate.eq(&true) { + shifted_comm = shifted_comm.negate()?; + } + + let value_bits = value.to_bits_le()?; + let shift_power = prepared_verification_key + .get_shift_power(cs.clone(), degree_bound) + .unwrap(); + + let mut shift_power_times_value = PG::G1Var::zero(); + { + for (bit, base_power) in value_bits.iter().zip(&shift_power) { + let mut new_encoded = base_power.clone(); + new_encoded += shift_power_times_value.clone(); + shift_power_times_value = PG::G1Var::conditionally_select( + bit, + &new_encoded, + &shift_power_times_value, + )?; + } + } + let mut adjusted_comm = shifted_comm; + adjusted_comm -= shift_power_times_value; + let adjusted_comm_times_challenge = + adjusted_comm.scalar_mul_le(challenge_shifted_bits.iter())?; + combined_comm += adjusted_comm_times_challenge; + } + } else { + assert!(degree_bound.is_none()); + + let mut comm_times_challenge = PG::G1Var::zero(); + let coeff = coeff.clone().unwrap(); + + let challenge_times_coeff = &challenge * &coeff; + let challenge_times_coeff_bits = challenge_times_coeff.to_bits_le()?; + + { + for (bit, base_power) in + challenge_times_coeff_bits.iter().zip(&comm.prepared_comm) + { + let mut new_encoded = comm_times_challenge.clone(); + new_encoded += base_power.clone(); + comm_times_challenge = PG::G1Var::conditionally_select( + bit, + &new_encoded, + &comm_times_challenge, + )?; + } + } + + if negate.eq(&true) { + comm_times_challenge = comm_times_challenge.negate()?; + } + + combined_comm += comm_times_challenge; + } + } + // Similarly, we add up the evaluations, multiplied with random challenges. + let value_times_challenge_unreduced = value.mul_without_reduce(&challenge)?; + + combined_eval += &value_times_challenge_unreduced; + } + + let combined_eval_reduced = combined_eval.reduce()?; + + combined_queries.push(point.clone()); + combined_comms.push(combined_comm); + combined_evals.push(combined_eval_reduced); + } + + // Perform the batch check. + { + let mut total_c = PG::G1Var::zero(); + let mut total_w = PG::G1Var::zero(); + + let mut g_multiplier = NonNativeFieldMulResultVar::, E2Fq>::zero(); + let mut g_multiplier_reduced = NonNativeFieldVar::, E2Fq>::zero(); + for (i, (((c, z), v), proof)) in combined_comms + .iter() + .zip(combined_queries) + .zip(combined_evals) + .zip(proofs) + .enumerate() + { + let z_bits = z.to_bits_le()?; + + let w_times_z = proof.w.scalar_mul_le(z_bits.iter())?; + + let mut c_plus_w_times_z = c.clone(); + c_plus_w_times_z += w_times_z; + + if i != 0 { + let randomizer = batching_rands.remove(0); + let randomizer_bits = batching_rands_bits.remove(0); + + let randomizer_times_v = randomizer.mul_without_reduce(&v)?; + + g_multiplier += &randomizer_times_v; + + let c_times_randomizer = + c_plus_w_times_z.scalar_mul_le(randomizer_bits.iter())?; + let w_times_randomizer = proof.w.scalar_mul_le(randomizer_bits.iter())?; + total_c += c_times_randomizer; + total_w += w_times_randomizer; + } else { + g_multiplier_reduced += v; + total_c += c_plus_w_times_z; + total_w += proof.w.clone(); + } + } + + // Prepare each input to the pairing. + let (prepared_total_w, prepared_beta_h, prepared_total_c, prepared_h) = { + let g_multiplier_reduced = g_multiplier.reduce()? + &g_multiplier_reduced; + let g_multiplier_bits = g_multiplier_reduced.to_bits_le()?; + + let mut g_times_mul = PG::G1Var::zero(); + { + for (bit, base_power) in g_multiplier_bits + .iter() + .zip(&prepared_verification_key.prepared_g) + { + let mut new_encoded = g_times_mul.clone(); + new_encoded += base_power.clone(); + g_times_mul = + PG::G1Var::conditionally_select(bit, &new_encoded, &g_times_mul)?; + } + } + total_c -= g_times_mul; + total_w = total_w.negate()?; + + let prepared_total_w = PG::prepare_g1(&total_w)?; + let prepared_beta_h = prepared_verification_key.prepared_beta_h.clone(); + let prepared_total_c = PG::prepare_g1(&total_c)?; + let prepared_h = prepared_verification_key.prepared_h.clone(); + + ( + prepared_total_w, + prepared_beta_h, + prepared_total_c, + prepared_h, + ) + }; + + let lhs = PG::product_of_pairings( + &[prepared_total_w, prepared_total_c], + &[prepared_beta_h, prepared_h], + )?; + + let rhs = &PG::GTVar::one(); + lhs.is_eq(&rhs) + } + } +} + +impl PCCheckVar, P, MarlinKZG10, E2Fq, S> + for MarlinKZG10Gadget +where + E: PairingFriendlyCycle, + E2Fq: PrimeField, + P: UVPolynomial, Point = E2Fr>, + for<'a, 'b> &'a P: Div<&'b P, Output = P>, + PG: PairingVar>, + S: CryptographicSponge, +{ + type VerifierKeyVar = VerifierKeyVar; + type PreparedVerifierKeyVar = PreparedVerifierKeyVar; + type CommitmentVar = CommitmentVar; + type PreparedCommitmentVar = PreparedCommitmentVar; + type LabeledCommitmentVar = LabeledCommitmentVar; + type PreparedLabeledCommitmentVar = PreparedLabeledCommitmentVar; + type ProofVar = ProofVar; + type BatchLCProofVar = BatchLCProofVar; + + #[allow(clippy::type_complexity)] + #[tracing::instrument( + target = "r1cs", + skip(verification_key, commitments, query_set, evaluations, proofs) + )] + fn batch_check_evaluations( + _cs: ConstraintSystemRef>, + verification_key: &Self::VerifierKeyVar, + commitments: &[Self::LabeledCommitmentVar], + query_set: &QuerySetVar, E2Fq>, + evaluations: &EvaluationsVar, E2Fq>, + proofs: &[Self::ProofVar], + rand_data: &PCCheckRandomDataVar, E2Fq>, + ) -> R1CSResult>> { + let mut batching_rands = rand_data.batching_rands.to_vec(); + let mut batching_rands_bits = rand_data.batching_rands_bits.to_vec(); + + let commitments: BTreeMap<_, _> = + commitments.iter().map(|c| (c.label.clone(), c)).collect(); + let mut query_to_labels_map = BTreeMap::new(); + + for (label, point) in query_set.0.iter() { + let labels = query_to_labels_map + .entry(point.name.clone()) + .or_insert((point.value.clone(), BTreeSet::new())); + labels.1.insert(label); + } + + // Accumulate commitments and evaluations for each query. + let mut combined_queries = Vec::new(); + let mut combined_comms = Vec::new(); + let mut combined_evals = Vec::new(); + for (_, (point, labels)) in query_to_labels_map.into_iter() { + let mut comms_to_combine: Vec = Vec::new(); + let mut values_to_combine = Vec::new(); + for label in labels.into_iter() { + let commitment = &(*commitments.get(label).unwrap()).clone(); + let degree_bound = commitment.degree_bound.clone(); + assert_eq!( + degree_bound.is_some(), + commitment.commitment.shifted_comm.is_some() + ); + + let v_i = evaluations + .0 + .get(&LabeledPointVar { + name: label.clone(), + value: point.clone(), + }) + .unwrap(); + + comms_to_combine.push(commitment.clone()); + values_to_combine.push(v_i.clone()); + } + + // Accumulate the commitments and evaluations corresponding to `query`. + let mut combined_comm = PG::G1Var::zero(); + let mut combined_eval = NonNativeFieldMulResultVar::, E2Fq>::zero(); + + let mut opening_challenges_counter = 0; + + for (labeled_commitment, value) in + comms_to_combine.into_iter().zip(values_to_combine.iter()) + { + let challenge = rand_data.opening_challenges[opening_challenges_counter].clone(); + let challenge_bits = + rand_data.opening_challenges_bits[opening_challenges_counter].clone(); + opening_challenges_counter += 1; + + let LabeledCommitmentVar { + commitment, + degree_bound, + .. + } = labeled_commitment; + let CommitmentVar { shifted_comm, .. } = commitment; + + // To combine the commitments, we multiply each by one of the random challenges, and sum. + combined_comm += commitment.comm.scalar_mul_le(challenge_bits.iter())?; + + // Similarly, we add up the evaluations, multiplied with random challenges. + let value_times_challenge_unreduced = value.mul_without_reduce(&challenge)?; + combined_eval += &value_times_challenge_unreduced; + + // If the degree bound is specified, we include the adjusted degree-shifted commitment + // (that is, c_i' - v_i beta^{D - d_i} G), where d_i is the specific degree bound and + // v_i is the evaluation, in the cocmbined commitment, + if let Some(degree_bound) = degree_bound { + let challenge_shifted_bits = + rand_data.opening_challenges_bits[opening_challenges_counter].clone(); + opening_challenges_counter += 1; + + let shifted_comm = shifted_comm.as_ref().unwrap().clone(); + + let value_bits = value.to_bits_le()?; + let shift_power = verification_key + .get_shift_power(verification_key.g.cs(), °ree_bound) + .unwrap(); + + let shift_power_times_value = shift_power.scalar_mul_le(value_bits.iter())?; + let mut adjusted_comm = shifted_comm; + adjusted_comm -= shift_power_times_value; + + let adjusted_comm_times_challenge = + adjusted_comm.scalar_mul_le(challenge_shifted_bits.iter())?; + + combined_comm += adjusted_comm_times_challenge; + } + } + + combined_queries.push(point.clone()); + combined_comms.push(combined_comm); + combined_evals.push(combined_eval); + } + + // Perform the batch check. + { + let mut total_c = PG::G1Var::zero(); + let mut total_w = PG::G1Var::zero(); + + let mut g_multiplier = NonNativeFieldMulResultVar::, E2Fq>::zero(); + for (((c, z), v), proof) in combined_comms + .iter() + .zip(combined_queries) + .zip(combined_evals) + .zip(proofs) + { + let z_bits = z.to_bits_le()?; + + let w_times_z = proof.w.scalar_mul_le(z_bits.iter())?; + let mut c_plus_w_times_z = c.clone(); + c_plus_w_times_z += w_times_z; + + let randomizer = batching_rands.remove(0); + let randomizer_bits = batching_rands_bits.remove(0); + + let v_reduced = v.reduce()?; + let randomizer_times_v = randomizer.mul_without_reduce(&v_reduced)?; + + g_multiplier += randomizer_times_v; + + let c_times_randomizer = c_plus_w_times_z.scalar_mul_le(randomizer_bits.iter())?; + let w_times_randomizer = proof.w.scalar_mul_le(randomizer_bits.iter())?; + total_c += c_times_randomizer; + total_w += w_times_randomizer; + } + + // Prepare each input to the pairing. + let (prepared_total_w, prepared_beta_h, prepared_total_c, prepared_h) = { + let g_multiplier_reduced = g_multiplier.reduce()?; + let g_multiplier_bits = g_multiplier_reduced.to_bits_le()?; + + let g_times_mul = verification_key.g.scalar_mul_le(g_multiplier_bits.iter())?; + total_c -= g_times_mul; + total_w = total_w.negate()?; + + let prepared_total_w = PG::prepare_g1(&total_w)?; + let prepared_beta_h = PG::prepare_g2(&verification_key.beta_h)?; + let prepared_total_c = PG::prepare_g1(&total_c)?; + let prepared_h = PG::prepare_g2(&verification_key.h)?; + + ( + prepared_total_w, + prepared_beta_h, + prepared_total_c, + prepared_h, + ) + }; + + let lhs = PG::product_of_pairings( + &[prepared_total_w, prepared_total_c], + &[prepared_beta_h, prepared_h], + )?; + + let rhs = &PG::GTVar::one(); + + lhs.is_eq(rhs) + } + } + + #[allow(clippy::type_complexity)] + #[tracing::instrument( + target = "r1cs", + skip( + prepared_verification_key, + linear_combinations, + prepared_commitments, + query_set, + proof, + evaluations + ) + )] + fn prepared_check_combinations( + cs: ConstraintSystemRef>, + prepared_verification_key: &Self::PreparedVerifierKeyVar, + linear_combinations: &[LinearCombinationVar, E2Fq>], + prepared_commitments: &[Self::PreparedLabeledCommitmentVar], + query_set: &QuerySetVar, E2Fq>, + evaluations: &EvaluationsVar, E2Fq>, + proof: &Self::BatchLCProofVar, + rand_data: &PCCheckRandomDataVar, E2Fq>, + ) -> R1CSResult>> { + let BatchLCProofVar { proofs, .. } = proof; + + let label_comm_map = prepared_commitments + .iter() + .map(|c| (c.label.clone(), c)) + .collect::>(); + + let mut lc_info = Vec::new(); + let mut evaluations = evaluations.clone(); + + // For each linear combination, we sum up the relevant commitments, multiplied + // with their corresponding coefficients; these combined commitments are then + // the inputs to the normal batch check. + for lc in linear_combinations.iter() { + let lc_label = lc.label.clone(); + let num_polys = lc.terms.len(); + + let mut coeffs_and_comms = Vec::new(); + + for (coeff, label) in lc.terms.iter() { + if label.is_one() { + for (label, ref mut eval) in evaluations.0.iter_mut() { + if label.name == lc_label { + match coeff.clone() { + LinearCombinationCoeffVar::One => { + **eval = (**eval).clone() - &NonNativeFieldVar::one() + } + LinearCombinationCoeffVar::MinusOne => { + **eval = (**eval).clone() + &NonNativeFieldVar::one() + } + LinearCombinationCoeffVar::Var(variable) => { + **eval = (**eval).clone() - &variable + } + }; + } + } + } else { + let label: &String = label.try_into().unwrap(); + let &cur_comm = label_comm_map.get(label).unwrap(); + let negate = match coeff { + LinearCombinationCoeffVar::One | LinearCombinationCoeffVar::Var(_) => false, + LinearCombinationCoeffVar::MinusOne => true, + }; + + if num_polys == 1 && cur_comm.degree_bound.is_some() { + assert!(!negate); + } + + let coeff = match coeff { + LinearCombinationCoeffVar::One => None, + LinearCombinationCoeffVar::MinusOne => None, + LinearCombinationCoeffVar::Var(variable) => Some(variable.clone()), + }; + + coeffs_and_comms.push(LCItem { + coeff, + degree_bound: cur_comm.degree_bound.clone(), + comm: cur_comm.prepared_commitment.clone(), + negate, + }); + } + } + + lc_info.push((lc_label, coeffs_and_comms)); + } + + Self::prepared_batch_check_evaluations( + cs, + prepared_verification_key, + lc_info.as_slice(), + &query_set, + &evaluations, + proofs, + &rand_data.opening_challenges, + &rand_data.opening_challenges_bits, + &rand_data.batching_rands, + &rand_data.batching_rands_bits, + ) + } + + fn create_labeled_commitment( + label: String, + commitment: Self::CommitmentVar, + degree_bound: Option>>, + ) -> Self::LabeledCommitmentVar { + Self::LabeledCommitmentVar { + label, + commitment, + degree_bound, + } + } + + fn create_prepared_labeled_commitment( + label: String, + prepared_commitment: Self::PreparedCommitmentVar, + degree_bound: Option>>, + ) -> Self::PreparedLabeledCommitmentVar { + Self::PreparedLabeledCommitmentVar { + label, + prepared_commitment, + degree_bound, + } + } +} diff --git a/src/marlin/marlin_pc/data_structures.rs b/src/marlin/marlin_pc/data_structures.rs index 37e075db..00eacbc9 100644 --- a/src/marlin/marlin_pc/data_structures.rs +++ b/src/marlin/marlin_pc/data_structures.rs @@ -44,10 +44,10 @@ pub struct CommitterKey { impl CommitterKey { /// Obtain powers for the underlying KZG10 construction - pub fn powers<'a>(&'a self) -> kzg10::Powers<'a, E> { + pub fn powers<'a>(&self) -> kzg10::Powers<'a, E> { kzg10::Powers { - powers_of_g: self.powers.as_slice().into(), - powers_of_gamma_g: self.powers_of_gamma_g.as_slice().into(), + powers_of_g: ark_std::borrow::Cow::Owned(self.powers.clone()), + powers_of_gamma_g: ark_std::borrow::Cow::Owned(self.powers_of_gamma_g.clone()), } } diff --git a/src/marlin/marlin_pc/mod.rs b/src/marlin/marlin_pc/mod.rs index 535fde40..37f568e7 100644 --- a/src/marlin/marlin_pc/mod.rs +++ b/src/marlin/marlin_pc/mod.rs @@ -6,6 +6,7 @@ use crate::{PCRandomness, PCUniversalParams, PolynomialCommitment}; use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; use ark_ff::Zero; use ark_poly::UVPolynomial; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; use ark_std::rand::RngCore; use ark_std::{marker::PhantomData, ops::Div, vec}; @@ -14,6 +15,12 @@ use crate::challenge::ChallengeGenerator; use ark_sponge::CryptographicSponge; pub use data_structures::*; +/// R1CS constraints for the commitment scheme. +#[cfg(feature = "r1cs")] +mod constraints; +#[cfg(feature = "r1cs")] +pub use constraints::*; + /// Polynomial commitment based on [[KZG10]][kzg], with degree enforcement, batching, /// and (optional) hiding property taken from [[CHMMVW20, “Marlin”]][marlin]. /// @@ -26,6 +33,7 @@ pub use data_structures::*; /// /// [kzg]: http://cacr.uwaterloo.ca/techreports/2010/cacr2010-10.pdf /// [marlin]: https://eprint.iacr.org/2019/104 +#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] pub struct MarlinKZG10, S: CryptographicSponge> { _engine: PhantomData, _poly: PhantomData

, @@ -341,14 +349,14 @@ where /// 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>, point: &'a P::Point, values: impl IntoIterator, proof: &Self::Proof, opening_challenges: &mut ChallengeGenerator, - _rng: Option<&mut dyn RngCore>, + _rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, @@ -374,7 +382,7 @@ where values: &Evaluations, proof: &Self::BatchProof, opening_challenges: &mut ChallengeGenerator, - rng: &mut R, + rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, @@ -438,7 +446,7 @@ where evaluations: &Evaluations, proof: &BatchLCProof, opening_challenges: &mut ChallengeGenerator, - rng: &mut R, + rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, diff --git a/src/marlin/marlin_pst13_pc/mod.rs b/src/marlin/marlin_pst13_pc/mod.rs index 944066cc..10773621 100644 --- a/src/marlin/marlin_pst13_pc/mod.rs +++ b/src/marlin/marlin_pst13_pc/mod.rs @@ -8,7 +8,7 @@ use crate::{LabeledCommitment, LabeledPolynomial, LinearCombination}; use crate::{PCRandomness, PCUniversalParams, PolynomialCommitment}; use crate::{ToString, Vec}; use ark_ec::{ - msm::{FixedBase, VariableBase}, + msm::{FixedBaseMSM, VariableBaseMSM}, AffineCurve, PairingEngine, ProjectiveCurve, }; use ark_ff::{One, PrimeField, UniformRand, Zero}; @@ -215,17 +215,21 @@ where let scalar_bits = E::Fr::size_in_bits(); let g_time = start_timer!(|| "Generating powers of G"); - let window_size = FixedBase::get_mul_window_size(max_degree + 1); - let g_table = FixedBase::get_window_table(scalar_bits, window_size, g); - let mut powers_of_g = - FixedBase::msm::(scalar_bits, window_size, &g_table, &powers_of_beta); + let window_size = FixedBaseMSM::get_mul_window_size(max_degree + 1); + let g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, g); + let mut powers_of_g = FixedBaseMSM::multi_scalar_mul::( + scalar_bits, + window_size, + &g_table, + &powers_of_beta, + ); powers_of_g.push(g); powers_of_beta_terms.push(P::Term::new(vec![])); end_timer!(g_time); let gamma_g_time = start_timer!(|| "Generating powers of gamma * G"); - let window_size = FixedBase::get_mul_window_size(max_degree + 2); - let gamma_g_table = FixedBase::get_window_table(scalar_bits, window_size, gamma_g); + let window_size = FixedBaseMSM::get_mul_window_size(max_degree + 2); + let gamma_g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, gamma_g); // Each element `i` of `powers_of_gamma_g` is a vector of length `max_degree+1` // containing `betas[i]^j \gamma G` for `j` from 1 to `max_degree+1` to support // up to `max_degree` queries @@ -239,7 +243,7 @@ where cur *= &betas[i]; powers_of_beta.push(cur); } - *v = FixedBase::msm::( + *v = FixedBaseMSM::multi_scalar_mul::( scalar_bits, window_size, &gamma_g_table, @@ -383,7 +387,7 @@ where end_timer!(to_bigint_time); let msm_time = start_timer!(|| "MSM to compute commitment to plaintext poly"); - let mut commitment = VariableBase::msm(&powers_of_g, &plain_ints); + let mut commitment = VariableBaseMSM::multi_scalar_mul(&powers_of_g, &plain_ints); end_timer!(msm_time); // Sample random polynomial @@ -419,7 +423,7 @@ where let msm_time = start_timer!(|| "MSM to compute commitment to random poly"); let random_commitment = - VariableBase::msm(&powers_of_gamma_g, &random_ints).into_affine(); + VariableBaseMSM::multi_scalar_mul(&powers_of_gamma_g, &random_ints).into_affine(); end_timer!(msm_time); // Mask commitment with random poly @@ -487,7 +491,7 @@ where // Convert coefficients to BigInt let witness_ints = Self::convert_to_bigints(&w); // Compute MSM - VariableBase::msm(&powers_of_g, &witness_ints) + VariableBaseMSM::multi_scalar_mul(&powers_of_g, &witness_ints) }) .collect::>(); end_timer!(witness_comm_time); @@ -517,7 +521,10 @@ where // Convert coefficients to BigInt let hiding_witness_ints = Self::convert_to_bigints(hiding_witness); // Compute MSM and add result to witness - *witness += &VariableBase::msm(&powers_of_gamma_g, &hiding_witness_ints); + *witness += &VariableBaseMSM::multi_scalar_mul( + &powers_of_gamma_g, + &hiding_witness_ints, + ); }); end_timer!(witness_comm_time); Some(r.blinding_polynomial.evaluate(point)) @@ -533,14 +540,14 @@ where /// 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>, point: &'a P::Point, values: impl IntoIterator, proof: &Self::Proof, opening_challenges: &mut ChallengeGenerator, - _rng: Option<&mut dyn RngCore>, + _rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, @@ -583,11 +590,12 @@ where values: &Evaluations, proof: &Self::BatchProof, opening_challenges: &mut ChallengeGenerator, - rng: &mut R, + rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, { + let rng = &mut crate::optional_rng::OptionalRng(rng); let (combined_comms, combined_queries, combined_evals) = Marlin::::combine_and_normalize( commitments, @@ -689,7 +697,7 @@ where eqn_evaluations: &Evaluations, proof: &BatchLCProof, opening_challenges: &mut ChallengeGenerator, - rng: &mut R, + rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, diff --git a/src/marlin/mod.rs b/src/marlin/mod.rs index 05ba0570..381f5757 100644 --- a/src/marlin/mod.rs +++ b/src/marlin/mod.rs @@ -328,7 +328,7 @@ where evaluations: &Evaluations, proof: &BatchLCProof, opening_challenges: &mut ChallengeGenerator, - rng: &mut R, + rng: Option<&mut R>, ) -> Result where R: RngCore, diff --git a/src/multilinear_pc/mod.rs b/src/multilinear_pc/mod.rs index 81dafd1b..8cf5b253 100644 --- a/src/multilinear_pc/mod.rs +++ b/src/multilinear_pc/mod.rs @@ -1,7 +1,7 @@ use crate::multilinear_pc::data_structures::{ Commitment, CommitterKey, Proof, UniversalParams, VerifierKey, }; -use ark_ec::msm::{FixedBase, VariableBase}; +use ark_ec::msm::{FixedBaseMSM, VariableBaseMSM}; use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; use ark_ff::{Field, PrimeField}; use ark_ff::{One, Zero}; @@ -59,22 +59,16 @@ impl MultilinearPC { pp_powers.extend(pp_k_powers); total_scalars += 1 << (num_vars - i); } - let window_size = FixedBase::get_mul_window_size(total_scalars); - let g_table = FixedBase::get_window_table(scalar_bits, window_size, g.into_projective()); - let h_table = FixedBase::get_window_table(scalar_bits, window_size, h.into_projective()); - - let pp_g = E::G1Projective::batch_normalization_into_affine(&FixedBase::msm( - scalar_bits, - window_size, - &g_table, - &pp_powers, - )); - let pp_h = E::G2Projective::batch_normalization_into_affine(&FixedBase::msm( - scalar_bits, - window_size, - &h_table, - &pp_powers, - )); + let window_size = FixedBaseMSM::get_mul_window_size(total_scalars); + let g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, g.into_projective()); + let h_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, h.into_projective()); + + let pp_g = E::G1Projective::batch_normalization_into_affine( + &FixedBaseMSM::multi_scalar_mul(scalar_bits, window_size, &g_table, &pp_powers), + ); + let pp_h = E::G2Projective::batch_normalization_into_affine( + &FixedBaseMSM::multi_scalar_mul(scalar_bits, window_size, &h_table, &pp_powers), + ); let mut start = 0; for i in 0..num_vars { let size = 1 << (num_vars - i); @@ -88,10 +82,10 @@ impl MultilinearPC { // uncomment to measure the time for calculating vp // let vp_generation_timer = start_timer!(|| "VP generation"); let g_mask = { - let window_size = FixedBase::get_mul_window_size(num_vars); + let window_size = FixedBaseMSM::get_mul_window_size(num_vars); let g_table = - FixedBase::get_window_table(scalar_bits, window_size, g.into_projective()); - E::G1Projective::batch_normalization_into_affine(&FixedBase::msm( + FixedBaseMSM::get_window_table(scalar_bits, window_size, g.into_projective()); + E::G1Projective::batch_normalization_into_affine(&FixedBaseMSM::multi_scalar_mul( scalar_bits, window_size, &g_table, @@ -146,7 +140,8 @@ impl MultilinearPC { .into_iter() .map(|x| x.into_repr()) .collect(); - let g_product = VariableBase::msm(&ck.powers_of_g[0], scalars.as_slice()).into_affine(); + let g_product = + VariableBaseMSM::multi_scalar_mul(&ck.powers_of_g[0], scalars.as_slice()).into_affine(); Commitment { nv, g_product } } @@ -178,7 +173,8 @@ impl MultilinearPC { .map(|x| q[k][x >> 1].into_repr()) // fine .collect(); - let pi_h = VariableBase::msm(&ck.powers_of_h[i], &scalars).into_affine(); // no need to move outside and partition + let pi_h = + VariableBaseMSM::multi_scalar_mul(&ck.powers_of_h[i], &scalars).into_affine(); // no need to move outside and partition proofs.push(pi_h); } @@ -200,10 +196,12 @@ impl MultilinearPC { ); let scalar_size = E::Fr::size_in_bits(); - let window_size = FixedBase::get_mul_window_size(vk.nv); + let window_size = FixedBaseMSM::get_mul_window_size(vk.nv); - let g_table = FixedBase::get_window_table(scalar_size, window_size, vk.g.into_projective()); - let g_mul: Vec = FixedBase::msm(scalar_size, window_size, &g_table, point); + let g_table = + FixedBaseMSM::get_window_table(scalar_size, window_size, vk.g.into_projective()); + let g_mul: Vec = + FixedBaseMSM::multi_scalar_mul(scalar_size, window_size, &g_table, point); let pairing_lefts: Vec<_> = (0..vk.nv) .map(|i| vk.g_mask_random[i].into_projective() - &g_mul[i]) diff --git a/src/sonic_pc/mod.rs b/src/sonic_pc/mod.rs index 1ddd1f67..3dd4fdd8 100644 --- a/src/sonic_pc/mod.rs +++ b/src/sonic_pc/mod.rs @@ -385,14 +385,14 @@ where Ok(proof) } - fn check<'a>( + fn check<'a, R: RngCore>( vk: &Self::VerifierKey, commitments: impl IntoIterator>, point: &'a P::Point, values: impl IntoIterator, proof: &Self::Proof, opening_challenges: &mut ChallengeGenerator, - _rng: Option<&mut dyn RngCore>, + _rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, @@ -432,11 +432,12 @@ where values: &Evaluations, proof: &Self::BatchProof, opening_challenges: &mut ChallengeGenerator, - rng: &mut R, + rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a, { + let rng = &mut crate::optional_rng::OptionalRng(rng); let commitments: BTreeMap<_, _> = commitments.into_iter().map(|c| (c.label(), c)).collect(); let mut query_to_labels_map = BTreeMap::new(); @@ -600,7 +601,7 @@ where eqn_evaluations: &Evaluations, proof: &BatchLCProof, opening_challenges: &mut ChallengeGenerator, - rng: &mut R, + rng: Option<&mut R>, ) -> Result where Self::Commitment: 'a,