arnaucube
arnaucube/testudoPublic Self-hosted repository
testudo/src/constraints.rs
src/constraints.rsRaw
  1use std::{borrow::Borrow, vec};
  2
  3use super::scalar::Scalar;
  4use crate::{
  5  group::Fq,
  6  math::Math,
  7  sparse_mlpoly::{SparsePolyEntry, SparsePolynomial},
  8  unipoly::UniPoly,
  9};
 10use ark_bls12_377::{constraints::PairingVar as IV, Bls12_377 as I, Fr};
 11use ark_crypto_primitives::{
 12  snark::BooleanInputVar, CircuitSpecificSetupSNARK, SNARKGadget, SNARK,
 13};
 14
 15use ark_ff::{BitIteratorLE, PrimeField, Zero};
 16use ark_groth16::{
 17  constraints::{Groth16VerifierGadget, PreparedVerifyingKeyVar, ProofVar},
 18  Groth16, PreparedVerifyingKey, Proof as GrothProof,
 19};
 20
 21use ark_r1cs_std::{
 22  alloc::{AllocVar, AllocationMode},
 23  fields::fp::FpVar,
 24  prelude::{Boolean, EqGadget, FieldVar},
 25};
 26use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
 27use ark_sponge::{
 28  constraints::CryptographicSpongeVar,
 29  poseidon::{constraints::PoseidonSpongeVar, PoseidonParameters},
 30};
 31use rand::{CryptoRng, Rng};
 32
 33pub struct PoseidonTranscripVar {
 34  pub cs: ConstraintSystemRef<Fr>,
 35  pub sponge: PoseidonSpongeVar<Fr>,
 36  pub params: PoseidonParameters<Fr>,
 37}
 38
 39impl PoseidonTranscripVar {
 40  fn new(
 41    cs: ConstraintSystemRef<Fr>,
 42    params: &PoseidonParameters<Fr>,
 43    challenge: Option<Fr>,
 44  ) -> Self {
 45    let mut sponge = PoseidonSpongeVar::new(cs.clone(), params);
 46
 47    if let Some(c) = challenge {
 48      let c_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(c)).unwrap();
 49      sponge.absorb(&c_var).unwrap();
 50    }
 51
 52    Self {
 53      cs,
 54      sponge,
 55      params: params.clone(),
 56    }
 57  }
 58
 59  fn append(&mut self, input: &FpVar<Fr>) -> Result<(), SynthesisError> {
 60    self.sponge.absorb(&input)
 61  }
 62
 63  fn append_vector(&mut self, input_vec: &[FpVar<Fr>]) -> Result<(), SynthesisError> {
 64    for input in input_vec.iter() {
 65      self.append(input)?;
 66    }
 67    Ok(())
 68  }
 69
 70  fn challenge(&mut self) -> Result<FpVar<Fr>, SynthesisError> {
 71    let c_var = self.sponge.squeeze_field_elements(1).unwrap().remove(0);
 72
 73    Ok(c_var)
 74  }
 75
 76  fn challenge_vector(&mut self, len: usize) -> Result<Vec<FpVar<Fr>>, SynthesisError> {
 77    let c_vars = self.sponge.squeeze_field_elements(len).unwrap();
 78
 79    Ok(c_vars)
 80  }
 81}
 82
 83#[derive(Clone)]
 84pub struct UniPolyVar {
 85  pub coeffs: Vec<FpVar<Fr>>,
 86}
 87
 88impl AllocVar<UniPoly, Fr> for UniPolyVar {
 89  fn new_variable<T: Borrow<UniPoly>>(
 90    cs: impl Into<Namespace<Fr>>,
 91    f: impl FnOnce() -> Result<T, SynthesisError>,
 92    mode: AllocationMode,
 93  ) -> Result<Self, SynthesisError> {
 94    f().and_then(|c| {
 95      let cs = cs.into();
 96      let cp: &UniPoly = c.borrow();
 97      let mut coeffs_var = Vec::new();
 98      for coeff in cp.coeffs.iter() {
 99        let coeff_var = FpVar::<Fr>::new_variable(cs.clone(), || Ok(coeff), mode)?;
100        coeffs_var.push(coeff_var);
101      }
102      Ok(Self { coeffs: coeffs_var })
103    })
104  }
105}
106
107impl UniPolyVar {
108  pub fn eval_at_zero(&self) -> FpVar<Fr> {
109    self.coeffs[0].clone()
110  }
111
112  pub fn eval_at_one(&self) -> FpVar<Fr> {
113    let mut res = self.coeffs[0].clone();
114    for i in 1..self.coeffs.len() {
115      res = &res + &self.coeffs[i];
116    }
117    res
118  }
119
120  // mul without reduce
121  pub fn evaluate(&self, r: &FpVar<Fr>) -> FpVar<Fr> {
122    let mut eval = self.coeffs[0].clone();
123    let mut power = r.clone();
124
125    for i in 1..self.coeffs.len() {
126      eval += &power * &self.coeffs[i];
127      power *= r;
128    }
129    eval
130  }
131}
132
133#[derive(Clone)]
134pub struct SumcheckVerificationCircuit {
135  pub polys: Vec<UniPoly>,
136}
137
138impl SumcheckVerificationCircuit {
139  fn verifiy_sumcheck(
140    &self,
141    poly_vars: &[UniPolyVar],
142    claim_var: &FpVar<Fr>,
143    transcript_var: &mut PoseidonTranscripVar,
144  ) -> Result<(FpVar<Fr>, Vec<FpVar<Fr>>), SynthesisError> {
145    let mut e_var = claim_var.clone();
146    let mut r_vars: Vec<FpVar<Fr>> = Vec::new();
147
148    for (poly_var, _poly) in poly_vars.iter().zip(self.polys.iter()) {
149      let res = poly_var.eval_at_one() + poly_var.eval_at_zero();
150      res.enforce_equal(&e_var)?;
151      transcript_var.append_vector(&poly_var.coeffs)?;
152      let r_i_var = transcript_var.challenge()?;
153      r_vars.push(r_i_var.clone());
154      e_var = poly_var.evaluate(&r_i_var.clone());
155    }
156
157    Ok((e_var, r_vars))
158  }
159}
160
161#[derive(Clone)]
162pub struct SparsePolyEntryVar {
163  idx: usize,
164  val_var: FpVar<Fr>,
165}
166
167impl AllocVar<SparsePolyEntry, Fr> for SparsePolyEntryVar {
168  fn new_variable<T: Borrow<SparsePolyEntry>>(
169    cs: impl Into<Namespace<Fr>>,
170    f: impl FnOnce() -> Result<T, SynthesisError>,
171    _mode: AllocationMode,
172  ) -> Result<Self, SynthesisError> {
173    f().and_then(|s| {
174      let cs = cs.into();
175      let spe: &SparsePolyEntry = s.borrow();
176      let val_var = FpVar::<Fr>::new_witness(cs, || Ok(spe.val))?;
177      Ok(Self {
178        idx: spe.idx,
179        val_var,
180      })
181    })
182  }
183}
184
185#[derive(Clone)]
186pub struct SparsePolynomialVar {
187  num_vars: usize,
188  Z_var: Vec<SparsePolyEntryVar>,
189}
190
191impl AllocVar<SparsePolynomial, Fr> for SparsePolynomialVar {
192  fn new_variable<T: Borrow<SparsePolynomial>>(
193    cs: impl Into<Namespace<Fr>>,
194    f: impl FnOnce() -> Result<T, SynthesisError>,
195    mode: AllocationMode,
196  ) -> Result<Self, SynthesisError> {
197    f().and_then(|s| {
198      let cs = cs.into();
199      let sp: &SparsePolynomial = s.borrow();
200      let mut Z_var = Vec::new();
201      for spe in sp.Z.iter() {
202        let spe_var = SparsePolyEntryVar::new_variable(cs.clone(), || Ok(spe), mode)?;
203        Z_var.push(spe_var);
204      }
205      Ok(Self {
206        num_vars: sp.num_vars,
207        Z_var,
208      })
209    })
210  }
211}
212
213impl SparsePolynomialVar {
214  fn compute_chi(a: &[bool], r_vars: &[FpVar<Fr>]) -> FpVar<Fr> {
215    let mut chi_i_var = FpVar::<Fr>::one();
216    let one = FpVar::<Fr>::one();
217    for (i, r_var) in r_vars.iter().enumerate() {
218      if a[i] {
219        chi_i_var *= r_var;
220      } else {
221        chi_i_var *= &one - r_var;
222      }
223    }
224    chi_i_var
225  }
226
227  pub fn evaluate(&self, r_var: &[FpVar<Fr>]) -> FpVar<Fr> {
228    let mut sum = FpVar::<Fr>::zero();
229    for spe_var in self.Z_var.iter() {
230      // potential problem
231      let bits = &spe_var.idx.get_bits(r_var.len());
232      sum += SparsePolynomialVar::compute_chi(bits, r_var) * &spe_var.val_var;
233    }
234    sum
235  }
236}
237
238#[derive(Clone)]
239pub struct R1CSVerificationCircuit {
240  pub num_vars: usize,
241  pub num_cons: usize,
242  pub input: Vec<Fr>,
243  pub input_as_sparse_poly: SparsePolynomial,
244  pub evals: (Fr, Fr, Fr),
245  pub params: PoseidonParameters<Fr>,
246  pub prev_challenge: Fr,
247  pub claims_phase2: (Scalar, Scalar, Scalar, Scalar),
248  pub eval_vars_at_ry: Fr,
249  pub sc_phase1: SumcheckVerificationCircuit,
250  pub sc_phase2: SumcheckVerificationCircuit,
251  // The point on which the polynomial was evaluated by the prover.
252  pub claimed_ry: Vec<Scalar>,
253  pub claimed_transcript_sat_state: Scalar,
254}
255
256impl R1CSVerificationCircuit {
257  fn new(config: &VerifierConfig) -> Self {
258    Self {
259      num_vars: config.num_vars,
260      num_cons: config.num_cons,
261      input: config.input.clone(),
262      input_as_sparse_poly: config.input_as_sparse_poly.clone(),
263      evals: config.evals,
264      params: config.params.clone(),
265      prev_challenge: config.prev_challenge,
266      claims_phase2: config.claims_phase2,
267      eval_vars_at_ry: config.eval_vars_at_ry,
268      sc_phase1: SumcheckVerificationCircuit {
269        polys: config.polys_sc1.clone(),
270      },
271      sc_phase2: SumcheckVerificationCircuit {
272        polys: config.polys_sc2.clone(),
273      },
274      claimed_ry: config.ry.clone(),
275      claimed_transcript_sat_state: config.transcript_sat_state,
276    }
277  }
278}
279
280impl ConstraintSynthesizer<Fr> for R1CSVerificationCircuit {
281  fn generate_constraints(self, cs: ConstraintSystemRef<Fr>) -> ark_relations::r1cs::Result<()> {
282    let mut transcript_var =
283      PoseidonTranscripVar::new(cs.clone(), &self.params, Some(self.prev_challenge));
284
285    let poly_sc1_vars = self
286      .sc_phase1
287      .polys
288      .iter()
289      .map(|p| UniPolyVar::new_variable(cs.clone(), || Ok(p), AllocationMode::Witness).unwrap())
290      .collect::<Vec<UniPolyVar>>();
291
292    let poly_sc2_vars = self
293      .sc_phase2
294      .polys
295      .iter()
296      .map(|p| UniPolyVar::new_variable(cs.clone(), || Ok(p), AllocationMode::Witness).unwrap())
297      .collect::<Vec<UniPolyVar>>();
298
299    let input_vars = self
300      .input
301      .iter()
302      .map(|i| FpVar::<Fr>::new_variable(cs.clone(), || Ok(i), AllocationMode::Witness).unwrap())
303      .collect::<Vec<FpVar<Fr>>>();
304
305    let claimed_ry_vars = self
306      .claimed_ry
307      .iter()
308      .map(|r| FpVar::<Fr>::new_variable(cs.clone(), || Ok(r), AllocationMode::Input).unwrap())
309      .collect::<Vec<FpVar<Fr>>>();
310
311    transcript_var.append_vector(&input_vars)?;
312
313    let num_rounds_x = self.num_cons.log_2();
314    let _num_rounds_y = (2 * self.num_vars).log_2();
315
316    let tau_vars = transcript_var.challenge_vector(num_rounds_x)?;
317
318    let claim_phase1_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(Fr::zero()))?;
319
320    let (claim_post_phase1_var, rx_var) =
321      self
322        .sc_phase1
323        .verifiy_sumcheck(&poly_sc1_vars, &claim_phase1_var, &mut transcript_var)?;
324
325    let (Az_claim, Bz_claim, Cz_claim, prod_Az_Bz_claims) = &self.claims_phase2;
326
327    let Az_claim_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(Az_claim))?;
328    let Bz_claim_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(Bz_claim))?;
329    let Cz_claim_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(Cz_claim))?;
330    let prod_Az_Bz_claim_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(prod_Az_Bz_claims))?;
331    let one = FpVar::<Fr>::one();
332    let prod_vars: Vec<FpVar<Fr>> = (0..rx_var.len())
333      .map(|i| (&rx_var[i] * &tau_vars[i]) + (&one - &rx_var[i]) * (&one - &tau_vars[i]))
334      .collect();
335    let mut taus_bound_rx_var = FpVar::<Fr>::one();
336
337    for p_var in prod_vars.iter() {
338      taus_bound_rx_var *= p_var;
339    }
340
341    let expected_claim_post_phase1_var =
342      (&prod_Az_Bz_claim_var - &Cz_claim_var) * &taus_bound_rx_var;
343
344    claim_post_phase1_var.enforce_equal(&expected_claim_post_phase1_var)?;
345
346    let r_A_var = transcript_var.challenge()?;
347    let r_B_var = transcript_var.challenge()?;
348    let r_C_var = transcript_var.challenge()?;
349
350    let claim_phase2_var =
351      &r_A_var * &Az_claim_var + &r_B_var * &Bz_claim_var + &r_C_var * &Cz_claim_var;
352
353    let (claim_post_phase2_var, ry_var) =
354      self
355        .sc_phase2
356        .verifiy_sumcheck(&poly_sc2_vars, &claim_phase2_var, &mut transcript_var)?;
357
358    //  Because the verifier checks the commitment opening on point ry outside
359    //  the circuit, the prover needs to send ry to the verifier (making the
360    //  proof size O(log n)). As this point is normally obtained by the verifier
361    //  from the second round of sumcheck, the circuit needs to ensure the
362    //  claimed point, coming from the prover, is actually the point derived
363    //  inside the circuit. These additional checks will be removed
364    //  when the commitment verification is done inside the circuit.
365    for (i, r) in claimed_ry_vars.iter().enumerate() {
366      ry_var[i].enforce_equal(r)?;
367    }
368
369    let input_as_sparse_poly_var = SparsePolynomialVar::new_variable(
370      cs.clone(),
371      || Ok(&self.input_as_sparse_poly),
372      AllocationMode::Witness,
373    )?;
374
375    let poly_input_eval_var = input_as_sparse_poly_var.evaluate(&ry_var[1..]);
376
377    let eval_vars_at_ry_var = FpVar::<Fr>::new_input(cs.clone(), || Ok(&self.eval_vars_at_ry))?;
378
379    let eval_Z_at_ry_var =
380      (FpVar::<Fr>::one() - &ry_var[0]) * &eval_vars_at_ry_var + &ry_var[0] * &poly_input_eval_var;
381
382    let (eval_A_r, eval_B_r, eval_C_r) = self.evals;
383
384    let eval_A_r_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(eval_A_r))?;
385    let eval_B_r_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(eval_B_r))?;
386    let eval_C_r_var = FpVar::<Fr>::new_witness(cs.clone(), || Ok(eval_C_r))?;
387
388    let scalar_var = &r_A_var * &eval_A_r_var + &r_B_var * &eval_B_r_var + &r_C_var * &eval_C_r_var;
389
390    let expected_claim_post_phase2_var = eval_Z_at_ry_var * scalar_var;
391    claim_post_phase2_var.enforce_equal(&expected_claim_post_phase2_var)?;
392
393    let expected_transcript_state_var = transcript_var.challenge()?;
394    let claimed_transcript_state_var =
395      FpVar::<Fr>::new_input(cs, || Ok(self.claimed_transcript_sat_state))?;
396
397    // Ensure that the prover and verifier transcipt views are consistent at
398    // the end of the satisfiability proof.
399    expected_transcript_state_var.enforce_equal(&claimed_transcript_state_var)?;
400
401    Ok(())
402  }
403}
404
405#[derive(Clone)]
406pub struct VerifierConfig {
407  pub num_vars: usize,
408  pub num_cons: usize,
409  pub input: Vec<Fr>,
410  pub input_as_sparse_poly: SparsePolynomial,
411  pub evals: (Fr, Fr, Fr),
412  pub params: PoseidonParameters<Fr>,
413  pub prev_challenge: Fr,
414  pub claims_phase2: (Fr, Fr, Fr, Fr),
415  pub eval_vars_at_ry: Fr,
416  pub polys_sc1: Vec<UniPoly>,
417  pub polys_sc2: Vec<UniPoly>,
418  pub ry: Vec<Scalar>,
419  pub transcript_sat_state: Scalar,
420}
421#[derive(Clone)]
422pub struct VerifierCircuit {
423  pub inner_circuit: R1CSVerificationCircuit,
424  pub inner_proof: GrothProof<I>,
425  pub inner_vk: PreparedVerifyingKey<I>,
426  pub eval_vars_at_ry: Fr,
427  pub claims_phase2: (Fr, Fr, Fr, Fr),
428  pub ry: Vec<Fr>,
429  pub transcript_sat_state: Scalar,
430}
431
432impl VerifierCircuit {
433  pub fn new<R: Rng + CryptoRng>(
434    config: &VerifierConfig,
435    mut rng: &mut R,
436  ) -> Result<Self, SynthesisError> {
437    let inner_circuit = R1CSVerificationCircuit::new(config);
438    let (pk, vk) = Groth16::<I>::setup(inner_circuit.clone(), &mut rng).unwrap();
439    let proof = Groth16::<I>::prove(&pk, inner_circuit.clone(), &mut rng)?;
440    let pvk = Groth16::<I>::process_vk(&vk).unwrap();
441    Ok(Self {
442      inner_circuit,
443      inner_proof: proof,
444      inner_vk: pvk,
445      eval_vars_at_ry: config.eval_vars_at_ry,
446      claims_phase2: config.claims_phase2,
447      ry: config.ry.clone(),
448      transcript_sat_state: config.transcript_sat_state,
449    })
450  }
451}
452
453impl ConstraintSynthesizer<Fq> for VerifierCircuit {
454  fn generate_constraints(self, cs: ConstraintSystemRef<Fq>) -> ark_relations::r1cs::Result<()> {
455    let proof_var = ProofVar::<I, IV>::new_witness(cs.clone(), || Ok(self.inner_proof.clone()))?;
456    let (v_A, v_B, v_C, v_AB) = self.claims_phase2;
457    let mut pubs = vec![];
458    pubs.extend(self.ry);
459    pubs.extend(vec![v_A, v_B, v_C, v_AB]);
460    pubs.extend(vec![self.eval_vars_at_ry, self.transcript_sat_state]);
461
462    let bits = pubs
463      .iter()
464      .map(|c| {
465        let bits: Vec<bool> = BitIteratorLE::new(c.into_repr().as_ref().to_vec()).collect();
466        Vec::new_witness(cs.clone(), || Ok(bits))
467      })
468      .collect::<Result<Vec<_>, _>>()?;
469    let input_var = BooleanInputVar::<Fr, Fq>::new(bits);
470
471    let vk_var = PreparedVerifyingKeyVar::new_witness(cs, || Ok(self.inner_vk.clone()))?;
472    Groth16VerifierGadget::verify_with_processed_vk(&vk_var, &input_var, &proof_var)?
473      .enforce_equal(&Boolean::constant(true))?;
474    Ok(())
475  }
476}