use crate::{
kzg10,
BTreeMap,
BTreeSet,
BatchLCProof,
Error,
Evaluations,
LabeledCommitment,
LabeledPolynomial,
LinearCombination,
PCCommitterKey,
PCRandomness,
PCUniversalParams,
Polynomial,
PolynomialCommitment,
QuerySet,
String,
ToOwned,
ToString,
Vec,
};
use snarkvm_curves::traits::{AffineCurve, PairingCurve, PairingEngine, ProjectiveCurve};
use snarkvm_fields::{One, Zero};
use snarkvm_utilities::rand::UniformRand;
use core::{
convert::TryInto,
marker::PhantomData,
ops::Mul,
sync::atomic::{AtomicBool, Ordering},
};
use rand_core::{RngCore, SeedableRng};
mod data_structures;
pub use data_structures::*;
mod gadgets;
pub use gadgets::*;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SonicKZG10<E: PairingEngine> {
_engine: PhantomData<E>,
}
impl<E: PairingEngine> SonicKZG10<E> {
fn combine_commitments<'a>(
coeffs_and_comms: impl IntoIterator<Item = (E::Fr, &'a Commitment<E>)>,
) -> E::G1Projective {
let mut combined_comm = E::G1Projective::zero();
for (coeff, comm) in coeffs_and_comms {
if coeff.is_one() {
combined_comm.add_assign_mixed(&comm.0);
} else {
combined_comm += &comm.0.mul(coeff).into();
}
}
combined_comm
}
fn normalize_commitments(commitments: Vec<E::G1Projective>) -> impl Iterator<Item = Commitment<E>> {
let mut comms = Vec::with_capacity(commitments.len());
for comm in commitments {
comms.push(comm);
}
let comms = E::G1Projective::batch_normalization_into_affine(comms);
comms.into_iter().map(|c| kzg10::Commitment(c))
}
}
impl<E: PairingEngine> PolynomialCommitment<E::Fr, E::Fq> for SonicKZG10<E> {
type BatchProof = Vec<Self::Proof>;
type Commitment = Commitment<E>;
type CommitterKey = CommitterKey<E>;
type PreparedCommitment = PreparedCommitment<E>;
type PreparedVerifierKey = PreparedVerifierKey<E>;
type Proof = kzg10::Proof<E>;
type Randomness = Randomness<E>;
type UniversalParams = UniversalParams<E>;
type VerifierKey = VerifierKey<E>;
fn setup<R: RngCore>(max_degree: usize, rng: &mut R) -> Result<Self::UniversalParams, Error> {
kzg10::KZG10::setup(max_degree, &kzg10::KZG10DegreeBoundsConfig::MARLIN, true, rng).map_err(Into::into)
}
fn trim(
pp: &Self::UniversalParams,
supported_degree: usize,
supported_hiding_bound: usize,
enforced_degree_bounds: Option<&[usize]>,
) -> Result<(Self::CommitterKey, Self::VerifierKey), Error> {
let trim_time = start_timer!(|| "Trimming public parameters");
let max_degree = pp.max_degree();
if supported_degree > max_degree {
return Err(Error::TrimmingDegreeTooLarge);
}
let enforced_degree_bounds = enforced_degree_bounds.map(|bounds| {
let mut v = bounds.to_vec();
v.sort_unstable();
v.dedup();
v
});
let (shifted_powers_of_g, shifted_powers_of_gamma_g) = if let Some(enforced_degree_bounds) =
enforced_degree_bounds.as_ref()
{
if enforced_degree_bounds.is_empty() {
(None, None)
} else {
let highest_enforced_degree_bound = *enforced_degree_bounds.last().unwrap();
if highest_enforced_degree_bound > supported_degree {
return Err(Error::UnsupportedDegreeBound(highest_enforced_degree_bound));
}
let lowest_shift_degree = max_degree - highest_enforced_degree_bound;
let shifted_ck_time = start_timer!(|| format!(
"Constructing `shifted_powers` of size {}",
max_degree - lowest_shift_degree + 1
));
let shifted_powers_of_g = pp.powers_of_g[lowest_shift_degree..].to_vec();
let mut shifted_powers_of_gamma_g = BTreeMap::new();
let _max_gamma_g = pp.powers_of_gamma_g.keys().last().unwrap();
for degree_bound in enforced_degree_bounds {
let shift_degree = max_degree - degree_bound;
let mut powers_for_degree_bound = Vec::with_capacity((max_degree + 2).saturating_sub(shift_degree));
for i in 0..=supported_hiding_bound + 1 {
if shift_degree + i < max_degree + 2 {
powers_for_degree_bound.push(pp.powers_of_gamma_g[&(shift_degree + i)]);
}
}
shifted_powers_of_gamma_g.insert(*degree_bound, powers_for_degree_bound);
}
end_timer!(shifted_ck_time);
(Some(shifted_powers_of_g), Some(shifted_powers_of_gamma_g))
}
} else {
(None, None)
};
let powers_of_g = pp.powers_of_g[..=supported_degree].to_vec();
let powers_of_gamma_g = (0..=supported_hiding_bound + 1)
.map(|i| pp.powers_of_gamma_g[&i])
.collect();
let ck = CommitterKey {
powers: powers_of_g,
powers_of_gamma_g,
shifted_powers: shifted_powers_of_g,
shifted_powers_of_gamma_g,
enforced_degree_bounds,
max_degree,
};
let g = pp.powers_of_g[0];
let h = pp.h;
let beta_h = pp.beta_h;
let gamma_g = pp.powers_of_gamma_g[&0];
let prepared_h = (&pp.prepared_h).clone();
let prepared_beta_h = (&pp.prepared_beta_h).clone();
let degree_bounds_and_neg_powers_of_h = if pp.inverse_neg_powers_of_h.is_empty() {
None
} else {
Some(
pp.inverse_neg_powers_of_h
.iter()
.map(|(d, affine)| (*d, *affine))
.collect::<Vec<(usize, E::G2Affine)>>(),
)
};
let degree_bounds_and_prepared_neg_powers_of_h =
degree_bounds_and_neg_powers_of_h
.as_ref()
.map(|degree_bounds_and_neg_powers_of_h| {
degree_bounds_and_neg_powers_of_h
.iter()
.map(|(d, affine)| (*d, affine.prepare()))
.collect::<Vec<(usize, <E::G2Affine as PairingCurve>::Prepared)>>()
});
let kzg10_vk = kzg10::VerifierKey::<E> {
g,
gamma_g,
h,
beta_h,
prepared_h,
prepared_beta_h,
};
let vk = VerifierKey {
vk: kzg10_vk,
degree_bounds_and_neg_powers_of_h,
degree_bounds_and_prepared_neg_powers_of_h,
supported_degree,
max_degree,
};
end_timer!(trim_time);
Ok((ck, vk))
}
#[allow(clippy::type_complexity)]
fn commit_with_terminator<'a>(
ck: &Self::CommitterKey,
polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::Fr>>,
terminator: &AtomicBool,
rng: Option<&mut dyn RngCore>,
) -> Result<(Vec<LabeledCommitment<Self::Commitment>>, Vec<Self::Randomness>), 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 mut pool = snarkvm_utilities::ExecutionPool::<Result<_, _>>::new();
let enforced_degree_bounds: Option<&[usize]> = ck.enforced_degree_bounds.as_deref();
for labeled_polynomial in polynomials {
if terminator.load(Ordering::Relaxed) {
return Err(Error::Terminated);
}
let seed = rng.0.as_mut().map(|r| {
let mut seed = [0u8; 32];
r.fill_bytes(&mut seed);
seed
});
kzg10::KZG10::<E>::check_degrees_and_bounds(
ck.supported_degree(),
ck.max_degree,
enforced_degree_bounds,
labeled_polynomial,
)?;
let polynomial = labeled_polynomial.polynomial();
let degree_bound = labeled_polynomial.degree_bound();
let hiding_bound = labeled_polynomial.hiding_bound();
let label = labeled_polynomial.label().clone();
pool.add_job(move || {
let mut rng = seed.map(rand::rngs::StdRng::from_seed);
let commit_time = start_timer!(|| format!(
"Polynomial {} of degree {}, degree bound {:?}, and hiding bound {:?}",
label,
polynomial.degree(),
degree_bound,
hiding_bound,
));
let powers = if let Some(degree_bound) = degree_bound {
ck.shifted_powers(degree_bound).unwrap()
} else {
ck.powers()
};
let (comm, rand) = kzg10::KZG10::commit(
&powers,
polynomial,
hiding_bound,
terminator,
rng.as_mut().map(|s| s as _),
)?;
end_timer!(commit_time);
Ok((LabeledCommitment::new(label.to_string(), comm, degree_bound), rand))
});
}
let results: Vec<Result<_, _>> = pool.execute_all();
for result in results {
let (comm, rand) = result?;
labeled_comms.push(comm);
randomness.push(rand);
}
end_timer!(commit_time);
Ok((labeled_comms, randomness))
}
fn open<'a>(
ck: &Self::CommitterKey,
labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<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, Error>
where
Self::Randomness: 'a,
Self::Commitment: 'a,
{
let mut combined_polynomial = Polynomial::zero();
let mut combined_rand = kzg10::Randomness::empty();
let mut curr_challenge = opening_challenge;
for (polynomial, rand) in labeled_polynomials.into_iter().zip(rands) {
let enforced_degree_bounds: Option<&[usize]> = ck.enforced_degree_bounds.as_deref();
kzg10::KZG10::<E>::check_degrees_and_bounds(
ck.supported_degree(),
ck.max_degree,
enforced_degree_bounds,
polynomial,
)?;
combined_polynomial += (curr_challenge, polynomial.polynomial());
combined_rand += (curr_challenge, rand);
curr_challenge *= &opening_challenge;
}
let proof_time = start_timer!(|| "Creating proof for polynomials");
let proof = kzg10::KZG10::open(&ck.powers(), &combined_polynomial, point, &combined_rand)?;
end_timer!(proof_time);
Ok(proof)
}
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, Error>
where
Self::Commitment: 'a,
{
let check_time = start_timer!(|| "Checking evaluations");
let mut combined_comms: BTreeMap<Option<usize>, E::G1Projective> = BTreeMap::new();
let mut combined_witness: E::G1Projective = E::G1Projective::zero();
let mut combined_adjusted_witness: E::G1Projective = E::G1Projective::zero();
Self::accumulate_elems(
&mut combined_comms,
&mut combined_witness,
&mut combined_adjusted_witness,
vk,
commitments,
point,
values,
proof,
opening_challenge,
None,
);
let res = Self::check_elems(combined_comms, combined_witness, combined_adjusted_witness, vk);
end_timer!(check_time);
res
}
fn batch_check<'a, R: RngCore>(
vk: &Self::VerifierKey,
commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
query_set: &QuerySet<E::Fr>,
values: &Evaluations<E::Fr>,
proof: &Self::BatchProof,
opening_challenge: E::Fr,
rng: &mut R,
) -> Result<bool, Error>
where
Self::Commitment: 'a,
{
let commitments: BTreeMap<_, _> = commitments.into_iter().map(|c| (c.label().to_owned(), c)).collect();
let mut query_to_labels_map = BTreeMap::new();
for (label, (point_name, point)) in query_set.iter() {
let labels = query_to_labels_map
.entry(point_name)
.or_insert((point, BTreeSet::new()));
labels.1.insert(label);
}
assert_eq!(proof.len(), query_to_labels_map.len());
let mut randomizer = E::Fr::one();
let mut combined_comms: BTreeMap<Option<usize>, E::G1Projective> = BTreeMap::new();
let mut combined_witness: E::G1Projective = E::G1Projective::zero();
let mut combined_adjusted_witness: E::G1Projective = E::G1Projective::zero();
for ((_query_name, (query, labels)), p) in query_to_labels_map.into_iter().zip(proof) {
let mut comms_to_combine: Vec<&'_ LabeledCommitment<_>> = Vec::new();
let mut values_to_combine = 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_to_combine.push(commitment);
values_to_combine.push(*v_i);
}
Self::accumulate_elems(
&mut combined_comms,
&mut combined_witness,
&mut combined_adjusted_witness,
vk,
comms_to_combine.into_iter(),
*query,
values_to_combine.into_iter(),
p,
opening_challenge,
Some(randomizer),
);
randomizer = u128::rand(rng).into();
}
Self::check_elems(combined_comms, combined_witness, combined_adjusted_witness, vk)
}
fn open_combinations<'a>(
ck: &Self::CommitterKey,
lc_s: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<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, E::Fq, Self>, Error>
where
Self::Randomness: 'a,
Self::Commitment: 'a,
{
let label_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 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, 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!(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(Error::EquationHasDegreeBounds(lc_label));
}
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(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(kzg10::Commitment::<E>)
.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,
evaluations: None,
})
}
fn check_combinations<'a, R: RngCore>(
vk: &Self::VerifierKey,
lc_s: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
query_set: &QuerySet<E::Fr>,
evaluations: &Evaluations<E::Fr>,
proof: &BatchLCProof<E::Fr, E::Fq, Self>,
opening_challenge: E::Fr,
rng: &mut R,
) -> Result<bool, Error>
where
Self::Commitment: 'a,
{
let BatchLCProof { proof, .. } = proof;
let label_comm_map = commitments
.into_iter()
.map(|c| (c.label().to_owned(), 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 = E::G1Projective::zero();
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.to_owned().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(Error::EquationHasDegreeBounds(lc_label));
}
combined_comm += &cur_comm.commitment().0.mul(*coeff).into();
}
}
lc_commitments.push(combined_comm);
lc_info.push((lc_label, degree_bound));
}
let comms = E::G1Projective::batch_normalization_into_affine(lc_commitments)
.into_iter()
.map(kzg10::Commitment);
let lc_commitments: Vec<_> = lc_info
.into_iter()
.zip(comms)
.map(|((label, d), c)| LabeledCommitment::new(label, c, d))
.collect();
Self::batch_check(
vk,
&lc_commitments,
query_set,
&evaluations,
proof,
opening_challenge,
rng,
)
}
fn open_combinations_individual_opening_challenges<'a>(
ck: &Self::CommitterKey,
linear_combinations: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::Fr>>,
commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
query_set: &QuerySet<E::Fr>,
opening_challenges: &dyn Fn(u64) -> E::Fr,
rands: impl IntoIterator<Item = &'a Self::Randomness>,
) -> Result<BatchLCProof<E::Fr, E::Fq, Self>, Error>
where
Self::Randomness: 'a,
Self::Commitment: 'a,
{
let label_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 linear_combinations {
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 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, 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!(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() {
return Err(Error::EquationHasDegreeBounds(lc_label));
}
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()));
}
let lc_poly = LabeledPolynomial::new(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_individual_opening_challenges(
ck,
lc_polynomials.iter(),
lc_commitments.iter(),
query_set,
opening_challenges,
lc_randomness.iter(),
)?;
Ok(BatchLCProof {
proof,
evaluations: None,
})
}
fn check_combinations_individual_opening_challenges<'a, R: RngCore>(
vk: &Self::VerifierKey,
linear_combinations: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
query_set: &QuerySet<E::Fr>,
evaluations: &Evaluations<E::Fr>,
proof: &BatchLCProof<E::Fr, E::Fq, Self>,
opening_challenges: &dyn Fn(u64) -> E::Fr,
rng: &mut R,
) -> Result<bool, 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();
let lc_processing_time = start_timer!(|| "Combining commitments");
for lc in linear_combinations {
let lc_label = lc.label().clone();
let num_polys = lc.len();
let mut degree_bound = None;
let mut coeffs_and_comms = Vec::new();
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(Error::EquationHasDegreeBounds(lc_label));
}
coeffs_and_comms.push((*coeff, cur_comm.commitment()));
}
}
let lc_time = start_timer!(|| format!("Combining {} commitments for {}", num_polys, lc_label));
lc_commitments.push(Self::combine_commitments(coeffs_and_comms));
end_timer!(lc_time);
lc_info.push((lc_label, degree_bound));
}
end_timer!(lc_processing_time);
let combined_comms_norm_time = start_timer!(|| "Normalizing commitments");
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<_>>();
end_timer!(combined_comms_norm_time);
Self::batch_check_individual_opening_challenges(
vk,
&lc_commitments,
query_set,
&evaluations,
proof,
opening_challenges,
rng,
)
}
}
impl<E: PairingEngine> SonicKZG10<E> {
fn open_individual_opening_challenges<'a>(
ck: &<Self as PolynomialCommitment<E::Fr, E::Fq>>::CommitterKey,
labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::Fr>>,
_commitments: impl IntoIterator<
Item = &'a LabeledCommitment<<Self as PolynomialCommitment<E::Fr, E::Fq>>::Commitment>,
>,
point: &'a E::Fr,
opening_challenges: &dyn Fn(u64) -> E::Fr,
rands: impl IntoIterator<Item = &'a <Self as PolynomialCommitment<E::Fr, E::Fq>>::Randomness>,
) -> Result<<Self as PolynomialCommitment<E::Fr, E::Fq>>::Proof, Error>
where
<Self as PolynomialCommitment<E::Fr, E::Fq>>::Randomness: 'a,
<Self as PolynomialCommitment<E::Fr, E::Fq>>::Commitment: 'a,
{
let mut combined_polynomial = Polynomial::zero();
let mut combined_rand = kzg10::Randomness::empty();
for (opening_challenge_counter, (polynomial, rand)) in labeled_polynomials.into_iter().zip(rands).enumerate() {
let enforced_degree_bounds: Option<&[usize]> = ck.enforced_degree_bounds.as_deref();
kzg10::KZG10::<E>::check_degrees_and_bounds(
ck.supported_degree(),
ck.max_degree,
enforced_degree_bounds,
polynomial,
)?;
let curr_challenge = opening_challenges(opening_challenge_counter as u64);
combined_polynomial += (curr_challenge, polynomial.polynomial());
combined_rand += (curr_challenge, rand);
}
let proof_time = start_timer!(|| "Creating proof for polynomials");
let proof = kzg10::KZG10::open(&ck.powers(), &combined_polynomial, *point, &combined_rand)?;
end_timer!(proof_time);
Ok(proof)
}
fn batch_open_individual_opening_challenges<'a>(
ck: &<Self as PolynomialCommitment<E::Fr, E::Fq>>::CommitterKey,
labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::Fr>>,
commitments: impl IntoIterator<
Item = &'a LabeledCommitment<<Self as PolynomialCommitment<E::Fr, E::Fq>>::Commitment>,
>,
query_set: &QuerySet<E::Fr>,
opening_challenges: &dyn Fn(u64) -> E::Fr,
rands: impl IntoIterator<Item = &'a <Self as PolynomialCommitment<E::Fr, E::Fq>>::Randomness>,
) -> Result<<Self as PolynomialCommitment<E::Fr, E::Fq>>::BatchProof, Error>
where
<Self as PolynomialCommitment<E::Fr, E::Fq>>::Randomness: 'a,
<Self as PolynomialCommitment<E::Fr, E::Fq>>::Commitment: 'a,
{
let poly_rand_comm: BTreeMap<_, _> = labeled_polynomials
.into_iter()
.zip(rands)
.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 {}",
poly_rand_comm.len(),
query_set.len(),
));
let mut query_to_labels_map = BTreeMap::new();
for (label, (point_name, point)) in query_set.iter() {
let labels = query_to_labels_map
.entry(point_name)
.or_insert((point, BTreeSet::new()));
labels.1.insert(label);
}
let mut proofs = Vec::new();
for (_query_name, (query, labels)) in query_to_labels_map.into_iter() {
let mut query_polys: Vec<&'a LabeledPolynomial<_>> = Vec::new();
let mut query_rands: Vec<&'a <Self as PolynomialCommitment<E::Fr, E::Fq>>::Randomness> = Vec::new();
let mut query_comms: Vec<&'a LabeledCommitment<<Self as PolynomialCommitment<E::Fr, E::Fq>>::Commitment>> =
Vec::new();
for label in labels {
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_individual_opening_challenges(
ck,
query_polys,
query_comms,
query,
opening_challenges,
query_rands,
)?;
end_timer!(proof_time);
proofs.push(proof);
}
end_timer!(open_time);
Ok(proofs)
}
fn check_individual_opening_challenges<'a>(
vk: &<Self as PolynomialCommitment<E::Fr, E::Fq>>::VerifierKey,
commitments: impl IntoIterator<
Item = &'a LabeledCommitment<<Self as PolynomialCommitment<E::Fr, E::Fq>>::Commitment>,
>,
point: &'a E::Fr,
values: impl IntoIterator<Item = E::Fr>,
proof: &<Self as PolynomialCommitment<E::Fr, E::Fq>>::Proof,
opening_challenges: &dyn Fn(u64) -> E::Fr,
) -> Result<bool, Error>
where
<Self as PolynomialCommitment<E::Fr, E::Fq>>::Commitment: 'a,
{
let check_time = start_timer!(|| "Checking evaluations");
let mut combined_comms: BTreeMap<Option<usize>, E::G1Projective> = BTreeMap::new();
let mut combined_witness: E::G1Projective = E::G1Projective::zero();
let mut combined_adjusted_witness: E::G1Projective = E::G1Projective::zero();
Self::accumulate_elems_individual_opening_challenges(
&mut combined_comms,
&mut combined_witness,
&mut combined_adjusted_witness,
vk,
commitments,
*point,
values,
proof,
opening_challenges,
None,
);
let res = Self::check_elems(combined_comms, combined_witness, combined_adjusted_witness, vk);
end_timer!(check_time);
res
}
fn batch_check_individual_opening_challenges<'a, R: RngCore>(
vk: &<Self as PolynomialCommitment<E::Fr, E::Fq>>::VerifierKey,
commitments: impl IntoIterator<
Item = &'a LabeledCommitment<<Self as PolynomialCommitment<E::Fr, E::Fq>>::Commitment>,
>,
query_set: &QuerySet<E::Fr>,
evaluations: &Evaluations<E::Fr>,
proof: &<Self as PolynomialCommitment<E::Fr, E::Fq>>::BatchProof,
opening_challenges: &dyn Fn(u64) -> E::Fr,
_rng: &mut R,
) -> Result<bool, Error>
where
<Self as PolynomialCommitment<E::Fr, E::Fq>>::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_name, point)) in query_set.iter() {
let labels = query_to_labels_map
.entry(point_name)
.or_insert((point, BTreeSet::new()));
labels.1.insert(label);
}
let proofs: Vec<_> = proof.clone();
assert_eq!(proofs.len(), query_to_labels_map.len());
let mut result = true;
for ((_point_name, (point, labels)), proof) in query_to_labels_map.into_iter().zip(proofs) {
let mut comms: Vec<&'_ LabeledCommitment<_>> = Vec::new();
let mut values = Vec::new();
for label in labels {
let commitment = commitments.get(label).ok_or(Error::MissingPolynomial {
label: label.to_string(),
})?;
let v_i = evaluations
.get(&(label.clone(), *point))
.ok_or(Error::MissingEvaluation {
label: label.to_string(),
})?;
comms.push(commitment);
values.push(*v_i);
}
let proof_time = start_timer!(|| "Checking per-query proof");
result &= Self::check_individual_opening_challenges(vk, comms, point, values, &proof, opening_challenges)?;
end_timer!(proof_time);
}
Ok(result)
}
}
impl<E: PairingEngine> SonicKZG10<E> {
#[allow(clippy::too_many_arguments)]
fn accumulate_elems<'a>(
combined_comms: &mut BTreeMap<Option<usize>, E::G1Projective>,
combined_witness: &mut E::G1Projective,
combined_adjusted_witness: &mut E::G1Projective,
vk: &VerifierKey<E>,
commitments: impl IntoIterator<Item = &'a LabeledCommitment<Commitment<E>>>,
point: E::Fr,
values: impl IntoIterator<Item = E::Fr>,
proof: &kzg10::Proof<E>,
opening_challenge: E::Fr,
randomizer: Option<E::Fr>,
) {
let acc_time = start_timer!(|| "Accumulating elements");
let mut curr_challenge = opening_challenge;
let mut combined_values = E::Fr::zero();
for (labeled_comm, value) in commitments.into_iter().zip(values) {
combined_values += &(value * curr_challenge);
let comm = labeled_comm.commitment();
let degree_bound = labeled_comm.degree_bound();
let mut comm_with_challenge: E::G1Projective = comm.0.mul(curr_challenge).into();
if let Some(randomizer) = randomizer {
comm_with_challenge = comm_with_challenge.mul(randomizer);
}
*combined_comms.entry(degree_bound).or_insert_with(E::G1Projective::zero) += &comm_with_challenge;
curr_challenge *= &opening_challenge;
}
let mut witness: E::G1Projective = proof.w.into_projective();
let mut adjusted_witness = vk.vk.g.mul(combined_values) - proof.w.mul(point);
if let Some(random_v) = proof.random_v {
adjusted_witness += &vk.vk.gamma_g.mul(random_v);
}
if let Some(randomizer) = randomizer {
witness = witness.mul(randomizer);
adjusted_witness = adjusted_witness.mul(randomizer);
}
*combined_witness += &witness;
*combined_adjusted_witness += &adjusted_witness.into();
end_timer!(acc_time);
}
fn accumulate_elems_individual_opening_challenges<'a>(
combined_comms: &mut BTreeMap<Option<usize>, E::G1Projective>,
combined_witness: &mut E::G1Projective,
combined_adjusted_witness: &mut E::G1Projective,
vk: &VerifierKey<E>,
commitments: impl IntoIterator<Item = &'a LabeledCommitment<Commitment<E>>>,
point: E::Fr,
values: impl IntoIterator<Item = E::Fr>,
proof: &kzg10::Proof<E>,
opening_challenges: &dyn Fn(u64) -> E::Fr,
randomizer: Option<E::Fr>,
) {
let acc_time = start_timer!(|| "Accumulating elements");
let mut combined_values = E::Fr::zero();
for (opening_challenge_counter, (labeled_commitment, value)) in commitments.into_iter().zip(values).enumerate()
{
let current_challenge = opening_challenges(opening_challenge_counter as u64);
combined_values += &(value * current_challenge);
let commitment = labeled_commitment.commitment();
let degree_bound = labeled_commitment.degree_bound();
let mut commitment_with_challenge: E::G1Projective = commitment.0.mul(current_challenge).into();
if let Some(randomizer) = randomizer {
commitment_with_challenge = commitment_with_challenge.mul(randomizer);
}
*combined_comms.entry(degree_bound).or_insert_with(E::G1Projective::zero) += &commitment_with_challenge;
}
let mut witness: E::G1Projective = proof.w.into_projective();
let mut adjusted_witness = vk.vk.g.mul(combined_values) - proof.w.mul(point);
if let Some(random_v) = proof.random_v {
adjusted_witness += &vk.vk.gamma_g.mul(random_v);
}
if let Some(randomizer) = randomizer {
witness = proof.w.into_projective().mul(randomizer);
adjusted_witness = adjusted_witness.mul(randomizer);
}
*combined_witness += &witness;
*combined_adjusted_witness += &adjusted_witness.into();
end_timer!(acc_time);
}
#[allow(clippy::type_complexity)]
fn check_elems(
combined_comms: BTreeMap<Option<usize>, E::G1Projective>,
combined_witness: E::G1Projective,
combined_adjusted_witness: E::G1Projective,
vk: &VerifierKey<E>,
) -> Result<bool, Error> {
let check_time = start_timer!(|| "Checking elems");
let mut g1_projective_elems = Vec::with_capacity(combined_comms.len() + 2);
let mut g2_prepared_elems = Vec::with_capacity(combined_comms.len() + 2);
for (degree_bound, comm) in combined_comms.into_iter() {
let shift_power = if let Some(degree_bound) = degree_bound {
vk.get_prepared_shift_power(degree_bound)
.ok_or(Error::UnsupportedDegreeBound(degree_bound))?
} else {
vk.vk.prepared_h.clone()
};
g1_projective_elems.push(comm);
g2_prepared_elems.push(shift_power);
}
g1_projective_elems.push(-combined_adjusted_witness);
g2_prepared_elems.push(vk.vk.prepared_h.clone());
g1_projective_elems.push(-combined_witness);
g2_prepared_elems.push(vk.vk.prepared_beta_h.clone());
let g1_prepared_elems_iter = E::G1Projective::batch_normalization_into_affine(g1_projective_elems)
.into_iter()
.map(|a| a.prepare())
.collect::<Vec<_>>();
let g1_g2_prepared = g1_prepared_elems_iter.iter().zip(g2_prepared_elems.iter());
let is_one: bool = E::product_of_pairings(g1_g2_prepared).is_one();
end_timer!(check_time);
Ok(is_one)
}
}
#[cfg(test)]
mod tests {
#![allow(non_camel_case_types)]
use super::{CommitterKey, PolynomialCommitment, SonicKZG10};
use snarkvm_curves::bls12_377::Bls12_377;
use rand::distributions::Distribution;
use snarkvm_utilities::{rand::test_rng, FromBytes, ToBytes};
type PC<E> = SonicKZG10<E>;
type PC_Bls12_377 = PC<Bls12_377>;
#[test]
fn committer_key_serialization_test() {
let rng = &mut test_rng();
let max_degree = rand::distributions::Uniform::from(8..=64).sample(rng);
let supported_degree = rand::distributions::Uniform::from(1..=max_degree).sample(rng);
let pp = PC_Bls12_377::setup(max_degree, rng).unwrap();
let (ck, _vk) = PC_Bls12_377::trim(&pp, supported_degree, 0, None).unwrap();
let ck_bytes = ck.to_bytes_le().unwrap();
let ck_recovered: CommitterKey<Bls12_377> = FromBytes::read_le(&ck_bytes[..]).unwrap();
let ck_recovered_bytes = ck_recovered.to_bytes_le().unwrap();
assert_eq!(&ck_bytes, &ck_recovered_bytes);
}
#[test]
fn single_poly_test() {
use crate::tests::*;
single_poly_test::<_, _, PC_Bls12_377>().expect("test failed for bls12-377");
}
#[test]
fn quadratic_poly_degree_bound_multiple_queries_test() {
use crate::tests::*;
quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_377>().expect("test failed for bls12-377");
}
#[test]
fn linear_poly_degree_bound_test() {
use crate::tests::*;
linear_poly_degree_bound_test::<_, _, PC_Bls12_377>().expect("test failed for bls12-377");
}
#[test]
fn single_poly_degree_bound_test() {
use crate::tests::*;
single_poly_degree_bound_test::<_, _, PC_Bls12_377>().expect("test failed for bls12-377");
}
#[test]
fn single_poly_degree_bound_multiple_queries_test() {
use crate::tests::*;
single_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_377>().expect("test failed for bls12-377");
}
#[test]
fn two_polys_degree_bound_single_query_test() {
use crate::tests::*;
two_polys_degree_bound_single_query_test::<_, _, PC_Bls12_377>().expect("test failed for bls12-377");
}
#[test]
fn full_end_to_end_test() {
use crate::tests::*;
full_end_to_end_test::<_, _, PC_Bls12_377>().expect("test failed for bls12-377");
println!("Finished bls12-377");
}
#[test]
fn single_equation_test() {
use crate::tests::*;
single_equation_test::<_, _, PC_Bls12_377>().expect("test failed for bls12-377");
println!("Finished bls12-377");
}
#[test]
fn two_equation_test() {
use crate::tests::*;
two_equation_test::<_, _, PC_Bls12_377>().expect("test failed for bls12-377");
println!("Finished bls12-377");
}
#[test]
fn two_equation_degree_bound_test() {
use crate::tests::*;
two_equation_degree_bound_test::<_, _, PC_Bls12_377>().expect("test failed for bls12-377");
println!("Finished bls12-377");
}
#[test]
fn full_end_to_end_equation_test() {
use crate::tests::*;
full_end_to_end_equation_test::<_, _, PC_Bls12_377>().expect("test failed for bls12-377");
println!("Finished bls12-377");
}
#[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");
}
}