use core::borrow::Borrow;
use snarkvm_curves::PairingEngine;
use snarkvm_gadgets::{
nonnative::NonNativeFieldVar,
traits::{alloc::AllocGadget, curves::PairingGadget},
};
use snarkvm_r1cs::{ConstraintSystem, SynthesisError};
use crate::{
kzg10::Proof,
marlin_pc::{proof::ProofVar, MarlinKZG10},
BatchLCProof,
Vec,
};
#[allow(clippy::type_complexity)]
pub struct BatchLCProofVar<
TargetCurve: PairingEngine,
BaseCurve: PairingEngine,
PG: PairingGadget<TargetCurve, <BaseCurve as PairingEngine>::Fr>,
> {
pub proofs: Vec<ProofVar<TargetCurve, BaseCurve, PG>>,
pub evals: Option<Vec<NonNativeFieldVar<<TargetCurve as PairingEngine>::Fr, <BaseCurve as PairingEngine>::Fr>>>,
}
impl<TargetCurve, BaseCurve, PG> Clone for BatchLCProofVar<TargetCurve, BaseCurve, PG>
where
TargetCurve: PairingEngine,
BaseCurve: PairingEngine,
PG: PairingGadget<TargetCurve, <BaseCurve as PairingEngine>::Fr>,
{
fn clone(&self) -> Self {
Self {
proofs: self.proofs.clone(),
evals: self.evals.clone(),
}
}
}
impl<TargetCurve, BaseCurve, PG>
AllocGadget<
BatchLCProof<<TargetCurve as PairingEngine>::Fr, <TargetCurve as PairingEngine>::Fq, MarlinKZG10<TargetCurve>>,
<BaseCurve as PairingEngine>::Fr,
> for BatchLCProofVar<TargetCurve, BaseCurve, PG>
where
TargetCurve: PairingEngine,
BaseCurve: PairingEngine,
PG: PairingGadget<TargetCurve, <BaseCurve as PairingEngine>::Fr>,
{
fn alloc_constant<
Fn: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<
BatchLCProof<
<TargetCurve as PairingEngine>::Fr,
<TargetCurve as PairingEngine>::Fq,
MarlinKZG10<TargetCurve>,
>,
>,
CS: ConstraintSystem<<BaseCurve as PairingEngine>::Fr>,
>(
mut cs: CS,
value_gen: Fn,
) -> Result<Self, SynthesisError> {
value_gen().map(|proof| {
let BatchLCProof { proof, evaluations } = proof.borrow().clone();
let proofs: Vec<Proof<_>> = proof.to_vec();
let proofs: Vec<ProofVar<TargetCurve, BaseCurve, PG>> = proofs
.iter()
.enumerate()
.map(|(i, p)| ProofVar::alloc_constant(cs.ns(|| format!("proof_{}", i)), || Ok(p)).unwrap())
.collect();
let evals: Option<
Vec<NonNativeFieldVar<<TargetCurve as PairingEngine>::Fr, <BaseCurve as PairingEngine>::Fr>>,
> = evaluations.map(|evals_inner| {
evals_inner
.iter()
.enumerate()
.map(|(i, e)| {
NonNativeFieldVar::alloc_constant(cs.ns(|| format!("evaluation_{}", i)), || Ok(e)).unwrap()
})
.collect()
});
Self { proofs, evals }
})
}
fn alloc<
Fn: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<
BatchLCProof<
<TargetCurve as PairingEngine>::Fr,
<TargetCurve as PairingEngine>::Fq,
MarlinKZG10<TargetCurve>,
>,
>,
CS: ConstraintSystem<<BaseCurve as PairingEngine>::Fr>,
>(
mut cs: CS,
value_gen: Fn,
) -> Result<Self, SynthesisError> {
value_gen().map(|proof| {
let BatchLCProof { proof, evaluations } = proof.borrow().clone();
let proofs: Vec<Proof<_>> = proof.to_vec();
let proofs: Vec<ProofVar<TargetCurve, BaseCurve, PG>> = proofs
.iter()
.enumerate()
.map(|(i, p)| ProofVar::alloc(cs.ns(|| format!("proof_{}", i)), || Ok(p)).unwrap())
.collect();
let evals: Option<
Vec<NonNativeFieldVar<<TargetCurve as PairingEngine>::Fr, <BaseCurve as PairingEngine>::Fr>>,
> = evaluations.map(|evals_inner| {
evals_inner
.iter()
.enumerate()
.map(|(i, e)| NonNativeFieldVar::alloc(cs.ns(|| format!("evaluation_{}", i)), || Ok(e)).unwrap())
.collect()
});
Self { proofs, evals }
})
}
fn alloc_input<
Fn: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<
BatchLCProof<
<TargetCurve as PairingEngine>::Fr,
<TargetCurve as PairingEngine>::Fq,
MarlinKZG10<TargetCurve>,
>,
>,
CS: ConstraintSystem<<BaseCurve as PairingEngine>::Fr>,
>(
mut cs: CS,
value_gen: Fn,
) -> Result<Self, SynthesisError> {
value_gen().map(|proof| {
let BatchLCProof { proof, evaluations } = proof.borrow().clone();
let proofs: Vec<Proof<_>> = proof.to_vec();
let proofs: Vec<ProofVar<TargetCurve, BaseCurve, PG>> = proofs
.iter()
.enumerate()
.map(|(i, p)| ProofVar::alloc_input(cs.ns(|| format!("proof_{}", i)), || Ok(p)).unwrap())
.collect();
let evals: Option<
Vec<NonNativeFieldVar<<TargetCurve as PairingEngine>::Fr, <BaseCurve as PairingEngine>::Fr>>,
> = evaluations.map(|evals_inner| {
evals_inner
.iter()
.enumerate()
.map(|(i, e)| {
NonNativeFieldVar::alloc_input(cs.ns(|| format!("evaluation_{}", i)), || Ok(e)).unwrap()
})
.collect()
});
Self { proofs, evals }
})
}
}
#[cfg(test)]
mod tests {
use snarkvm_algorithms::fft::DensePolynomial;
use snarkvm_curves::{
bls12_377::{Bls12_377, Fq, Fr},
bw6_761::BW6_761,
AffineCurve,
};
use snarkvm_fields::One;
use snarkvm_gadgets::{curves::bls12_377::PairingGadget as Bls12_377PairingGadget, traits::eq::EqGadget};
use snarkvm_r1cs::TestConstraintSystem;
use snarkvm_utilities::rand::{test_rng, UniformRand};
use crate::{marlin_pc::MarlinKZG10, LabeledPolynomial, LinearCombination, PolynomialCommitment, QuerySet};
use super::*;
type PC = MarlinKZG10<Bls12_377>;
type PG = Bls12_377PairingGadget;
type BaseCurve = BW6_761;
const MAX_DEGREE: usize = 383;
const SUPPORTED_DEGREE: usize = 300;
const SUPPORTED_HIDING_BOUND: usize = 1;
#[test]
fn test_alloc() {
let rng = &mut test_rng();
let cs = &mut TestConstraintSystem::<Fq>::new();
let pp = PC::setup(MAX_DEGREE, rng).unwrap();
let (committer_key, _vk) = PC::trim(&pp, SUPPORTED_DEGREE, SUPPORTED_HIDING_BOUND, None).unwrap();
let label = "TEST".to_string();
let random_polynomial = DensePolynomial::<Fr>::rand(SUPPORTED_DEGREE - 1, rng);
let labeled_polynomial = LabeledPolynomial::new(label.clone(), random_polynomial, None, None);
let labeled_polynomials = vec![&labeled_polynomial];
let (commitments, randomness) = PC::commit(&committer_key, labeled_polynomials.clone(), Some(rng)).unwrap();
let random_point = Fr::rand(rng);
let mut lc_s = Vec::new();
let mut query_set = QuerySet::new();
let mut lc = LinearCombination::empty(label.clone());
lc.push((Fr::one(), label.to_string().into()));
lc_s.push(lc);
query_set.insert((label, ("rand".into(), random_point)));
let challenge = Fr::rand(rng);
let batch_lc_proof = PC::open_combinations(
&committer_key,
&lc_s,
labeled_polynomials,
&commitments,
&query_set,
challenge,
&randomness,
Some(rng),
)
.unwrap();
let batch_lc_proof_gadget =
BatchLCProofVar::<_, BaseCurve, PG>::alloc(cs.ns(|| "alloc_batch_lc_proof"), || Ok(batch_lc_proof.clone()))
.unwrap();
for (i, (proof, proof_gadget)) in batch_lc_proof
.proof
.iter()
.zip(batch_lc_proof_gadget.proofs)
.enumerate()
{
let expected_w_gadget =
<PG as PairingGadget<_, _>>::G1Gadget::alloc(cs.ns(|| format!("proof_w_{}", i)), || {
Ok(proof.w.into_projective())
})
.unwrap();
expected_w_gadget
.enforce_equal(cs.ns(|| format!("enforce_equals_w_{}", i)), &proof_gadget.w)
.unwrap();
assert_eq!(proof.random_v.is_some(), proof_gadget.random_v.is_some());
if let (Some(random_v), Some(random_v_gadget)) = (proof.random_v, proof_gadget.random_v) {
let expected_random_v =
NonNativeFieldVar::alloc(cs.ns(|| format!("expected_random_v_{}", i)), || Ok(random_v)).unwrap();
expected_random_v
.enforce_equal(cs.ns(|| format!("enforce_equal_random_v_{}", i)), &random_v_gadget)
.unwrap();
}
}
assert_eq!(
batch_lc_proof.evaluations.is_some(),
batch_lc_proof_gadget.evals.is_some()
);
if let (Some(random_vs), Some(random_v_gadgets)) = (batch_lc_proof.evaluations, batch_lc_proof_gadget.evals) {
for (i, (random_v, random_v_gadget)) in random_vs.iter().zip(random_v_gadgets).enumerate() {
let expected_random_v =
NonNativeFieldVar::alloc(cs.ns(|| format!("expected_random_v_{}", i)), || Ok(random_v)).unwrap();
expected_random_v
.enforce_equal(cs.ns(|| format!("enforce_equal_random_v_{}", i)), &random_v_gadget)
.unwrap();
}
}
assert!(cs.is_satisfied());
}
}