testudo/src/r1csproof.rs
src/r1csproof.rsRaw
1#![allow(clippy::too_many_arguments)]
2use super::dense_mlpoly::{DensePolynomial, EqPolynomial, PolyCommitmentGens};
3use super::errors::ProofVerifyError;
4use crate::constraints::{R1CSVerificationCircuit, SumcheckVerificationCircuit, VerifierConfig};
5use crate::math::Math;
6use crate::mipp::MippProof;
7use crate::poseidon_transcript::PoseidonTranscript;
8use crate::sqrt_pst::Polynomial;
9use crate::sumcheck::SumcheckInstanceProof;
10use crate::transcript::Transcript;
11use crate::unipoly::UniPoly;
12use ark_crypto_primitives::sponge::poseidon::PoseidonConfig;
13use ark_crypto_primitives::sponge::Absorb;
14use ark_ec::pairing::Pairing;
15
16use ark_poly_commit::multilinear_pc::data_structures::{Commitment, Proof};
17use itertools::Itertools;
18
19use super::r1csinstance::R1CSInstance;
20
21use super::sparse_mlpoly::{SparsePolyEntry, SparsePolynomial};
22use super::timer::Timer;
23use ark_snark::{CircuitSpecificSetupSNARK, SNARK};
24
25use crate::ark_std::UniformRand;
26use ark_groth16::{Groth16, ProvingKey, VerifyingKey};
27
28use ark_serialize::*;
29use ark_std::{One, Zero};
30
31#[derive(CanonicalSerialize, CanonicalDeserialize, Debug)]
32pub struct R1CSProof<E: Pairing> {
33 // The PST commitment to the multilinear extension of the witness.
34 pub comm: Commitment<E>,
35 sc_proof_phase1: SumcheckInstanceProof<E::ScalarField>,
36 claims_phase2: (
37 E::ScalarField,
38 E::ScalarField,
39 E::ScalarField,
40 E::ScalarField,
41 ),
42 sc_proof_phase2: SumcheckInstanceProof<E::ScalarField>,
43 pub eval_vars_at_ry: E::ScalarField,
44 pub proof_eval_vars_at_ry: Proof<E>,
45 rx: Vec<E::ScalarField>,
46 ry: Vec<E::ScalarField>,
47 // The transcript state after the satisfiability proof was computed.
48 pub transcript_sat_state: E::ScalarField,
49 pub initial_state: E::ScalarField,
50 pub t: E::TargetField,
51 pub mipp_proof: MippProof<E>,
52}
53
54#[derive(Debug, CanonicalSerialize, CanonicalDeserialize, Clone)]
55pub struct R1CSVerifierProof<E: Pairing> {
56 comm: Commitment<E>,
57 circuit_proof: ark_groth16::Proof<E>,
58 initial_state: E::ScalarField,
59 transcript_sat_state: E::ScalarField,
60 eval_vars_at_ry: E::ScalarField,
61 proof_eval_vars_at_ry: Proof<E>,
62 t: E::TargetField,
63 mipp_proof: MippProof<E>,
64}
65
66#[derive(Clone)]
67pub struct CircuitGens<E: Pairing> {
68 pk: ProvingKey<E>,
69 vk: VerifyingKey<E>,
70}
71
72impl<E> CircuitGens<E>
73where
74 E: Pairing,
75{
76 // Performs the circuit-specific setup required by Groth16 for the sumcheck
77 // circuit. This is done by filling the struct with dummy elements, ensuring
78 // the sizes are correct so the setup matches the circuit that will be proved.
79 pub fn setup(
80 num_cons: usize,
81 num_vars: usize,
82 num_inputs: usize,
83 poseidon: PoseidonConfig<E::ScalarField>,
84 ) -> Self {
85 let mut rng = rand::thread_rng();
86
87 let uni_polys_round1 = (0..num_cons.log_2())
88 .map(|_i| {
89 UniPoly::<E::ScalarField>::from_evals(&[
90 E::ScalarField::rand(&mut rng),
91 E::ScalarField::rand(&mut rng),
92 E::ScalarField::rand(&mut rng),
93 E::ScalarField::rand(&mut rng),
94 ])
95 })
96 .collect::<Vec<UniPoly<E::ScalarField>>>();
97
98 let uni_polys_round2 = (0..num_vars.log_2() + 1)
99 .map(|_i| {
100 UniPoly::<E::ScalarField>::from_evals(&[
101 E::ScalarField::rand(&mut rng),
102 E::ScalarField::rand(&mut rng),
103 E::ScalarField::rand(&mut rng),
104 ])
105 })
106 .collect::<Vec<UniPoly<E::ScalarField>>>();
107
108 let circuit = R1CSVerificationCircuit {
109 num_vars: num_vars,
110 num_cons: num_cons,
111 input: (0..num_inputs)
112 .map(|_i| E::ScalarField::rand(&mut rng))
113 .collect_vec(),
114 input_as_sparse_poly: SparsePolynomial::new(
115 num_vars.log_2(),
116 (0..num_inputs + 1)
117 .map(|i| SparsePolyEntry::new(i, E::ScalarField::rand(&mut rng)))
118 .collect::<Vec<SparsePolyEntry<E::ScalarField>>>(),
119 ),
120 evals: (
121 E::ScalarField::zero(),
122 E::ScalarField::zero(),
123 E::ScalarField::zero(),
124 ),
125 params: poseidon,
126 prev_challenge: E::ScalarField::zero(),
127 claims_phase2: (
128 E::ScalarField::zero(),
129 E::ScalarField::zero(),
130 E::ScalarField::zero(),
131 E::ScalarField::zero(),
132 ),
133 eval_vars_at_ry: E::ScalarField::zero(),
134 sc_phase1: SumcheckVerificationCircuit {
135 polys: uni_polys_round1,
136 },
137 sc_phase2: SumcheckVerificationCircuit {
138 polys: uni_polys_round2,
139 },
140 claimed_rx: (0..num_cons.log_2())
141 .map(|_i| E::ScalarField::rand(&mut rng))
142 .collect_vec(),
143 claimed_ry: (0..num_vars.log_2() + 1)
144 .map(|_i| E::ScalarField::rand(&mut rng))
145 .collect_vec(),
146 claimed_transcript_sat_state: E::ScalarField::zero(),
147 };
148 let (pk, vk) = Groth16::<E>::setup(circuit.clone(), &mut rng).unwrap();
149 CircuitGens { pk, vk }
150 }
151}
152
153#[derive(Clone)]
154pub struct R1CSGens<E: Pairing> {
155 gens_pc: PolyCommitmentGens<E>,
156 gens_gc: CircuitGens<E>,
157}
158
159impl<E: Pairing> R1CSGens<E> {
160 // Performs the setup for the polynomial commitment PST and for Groth16.
161 pub fn setup(
162 label: &'static [u8],
163 num_cons: usize,
164 num_vars: usize,
165 num_inputs: usize,
166 poseidon: PoseidonConfig<E::ScalarField>,
167 ) -> Self {
168 let num_poly_vars = num_vars.log_2();
169 let gens_pc = PolyCommitmentGens::setup(num_poly_vars, label);
170 let gens_gc = CircuitGens::setup(num_cons, num_vars, num_inputs, poseidon);
171 R1CSGens { gens_pc, gens_gc }
172 }
173}
174
175impl<E> R1CSProof<E>
176where
177 E: Pairing,
178 E::ScalarField: Absorb,
179{
180 fn prove_phase_one(
181 num_rounds: usize,
182 evals_tau: &mut DensePolynomial<E::ScalarField>,
183 evals_Az: &mut DensePolynomial<E::ScalarField>,
184 evals_Bz: &mut DensePolynomial<E::ScalarField>,
185 evals_Cz: &mut DensePolynomial<E::ScalarField>,
186 transcript: &mut PoseidonTranscript<E::ScalarField>,
187 ) -> (
188 SumcheckInstanceProof<E::ScalarField>,
189 Vec<E::ScalarField>,
190 Vec<E::ScalarField>,
191 ) {
192 let comb_func =
193 |poly_tau_comp: &E::ScalarField,
194 poly_A_comp: &E::ScalarField,
195 poly_B_comp: &E::ScalarField,
196 poly_C_comp: &E::ScalarField|
197 -> E::ScalarField { (*poly_tau_comp) * ((*poly_A_comp) * poly_B_comp - poly_C_comp) };
198
199 let (sc_proof_phase_one, r, claims) = SumcheckInstanceProof::prove_cubic_with_additive_term(
200 &E::ScalarField::zero(), // claim is zero
201 num_rounds,
202 evals_tau,
203 evals_Az,
204 evals_Bz,
205 evals_Cz,
206 comb_func,
207 transcript,
208 );
209
210 (sc_proof_phase_one, r, claims)
211 }
212
213 fn prove_phase_two(
214 num_rounds: usize,
215 claim: &E::ScalarField,
216 evals_z: &mut DensePolynomial<E::ScalarField>,
217 evals_ABC: &mut DensePolynomial<E::ScalarField>,
218 transcript: &mut PoseidonTranscript<E::ScalarField>,
219 ) -> (
220 SumcheckInstanceProof<E::ScalarField>,
221 Vec<E::ScalarField>,
222 Vec<E::ScalarField>,
223 ) {
224 let comb_func = |poly_A_comp: &E::ScalarField,
225 poly_B_comp: &E::ScalarField|
226 -> E::ScalarField { (*poly_A_comp) * poly_B_comp };
227 let (sc_proof_phase_two, r, claims) = SumcheckInstanceProof::prove_quad(
228 claim, num_rounds, evals_z, evals_ABC, comb_func, transcript,
229 );
230
231 (sc_proof_phase_two, r, claims)
232 }
233
234 // Proves the R1CS instance inst is satisfiable given the assignment
235 // vars.
236 pub fn prove(
237 inst: &R1CSInstance<E::ScalarField>,
238 vars: Vec<E::ScalarField>,
239 input: &[E::ScalarField],
240 gens: &R1CSGens<E>,
241 transcript: &mut PoseidonTranscript<E::ScalarField>,
242 ) -> (Self, Vec<E::ScalarField>, Vec<E::ScalarField>) {
243 let timer_prove = Timer::new("R1CSProof::prove");
244 // we currently require the number of |inputs| + 1 to be at most number of vars
245 assert!(input.len() < vars.len());
246
247 // create the multilinear witness polynomial from the satisfying assiment
248 // expressed as the list of sqrt-sized polynomials
249 let mut pl = Polynomial::from_evaluations(&vars.clone());
250
251 let timer_commit = Timer::new("polycommit");
252
253 // commitment list to the satisfying witness polynomial list
254 let (comm_list, t) = pl.commit(&gens.gens_pc.ck);
255
256 transcript.append_gt::<E>(b"", &t);
257
258 timer_commit.stop();
259
260 let initial_state = transcript.challenge_scalar(b"");
261 transcript.new_from_state(&initial_state);
262
263 transcript.append_scalar_vector(b"", &input);
264
265 let timer_sc_proof_phase1 = Timer::new("prove_sc_phase_one");
266
267 // append input to variables to create a single vector z
268 let z = {
269 let num_inputs = input.len();
270 let num_vars = vars.len();
271 let mut z = vars;
272 z.extend(&vec![E::ScalarField::one()]); // add constant term in z
273 z.extend(input);
274 z.extend(&vec![E::ScalarField::zero(); num_vars - num_inputs - 1]); // we will pad with zeros
275 z
276 };
277
278 // derive the verifier's challenge tau
279 let (num_rounds_x, num_rounds_y) = (inst.get_num_cons().log_2(), z.len().log_2());
280 let tau = transcript.challenge_scalar_vec(b"", num_rounds_x);
281 // compute the initial evaluation table for R(\tau, x)
282 let mut poly_tau = DensePolynomial::new(EqPolynomial::new(tau).evals());
283 let (mut poly_Az, mut poly_Bz, mut poly_Cz) =
284 inst.multiply_vec(inst.get_num_cons(), z.len(), &z);
285
286 let (sc_proof_phase1, rx, _claims_phase1) = R1CSProof::<E>::prove_phase_one(
287 num_rounds_x,
288 &mut poly_tau,
289 &mut poly_Az,
290 &mut poly_Bz,
291 &mut poly_Cz,
292 transcript,
293 );
294 assert_eq!(poly_tau.len(), 1);
295 assert_eq!(poly_Az.len(), 1);
296 assert_eq!(poly_Bz.len(), 1);
297 assert_eq!(poly_Cz.len(), 1);
298 timer_sc_proof_phase1.stop();
299
300 let (tau_claim, Az_claim, Bz_claim, Cz_claim) =
301 (&poly_tau[0], &poly_Az[0], &poly_Bz[0], &poly_Cz[0]);
302 let prod_Az_Bz_claims = (*Az_claim) * Bz_claim;
303
304 // prove the final step of sum-check #1
305 let taus_bound_rx = tau_claim;
306 let _claim_post_phase1 = ((*Az_claim) * Bz_claim - Cz_claim) * taus_bound_rx;
307
308 let timer_sc_proof_phase2 = Timer::new("prove_sc_phase_two");
309 // combine the three claims into a single claim
310 let r_A: E::ScalarField = transcript.challenge_scalar(b"");
311 let r_B: E::ScalarField = transcript.challenge_scalar(b"");
312 let r_C: E::ScalarField = transcript.challenge_scalar(b"");
313 let claim_phase2 = r_A * Az_claim + r_B * Bz_claim + r_C * Cz_claim;
314
315 let evals_ABC = {
316 // compute the initial evaluation table for R(\tau, x)
317 let evals_rx = EqPolynomial::new(rx.clone()).evals();
318 let (evals_A, evals_B, evals_C) =
319 inst.compute_eval_table_sparse(inst.get_num_cons(), z.len(), &evals_rx);
320
321 assert_eq!(evals_A.len(), evals_B.len());
322 assert_eq!(evals_A.len(), evals_C.len());
323 (0..evals_A.len())
324 .map(|i| r_A * evals_A[i] + r_B * evals_B[i] + r_C * evals_C[i])
325 .collect::<Vec<_>>()
326 };
327
328 // another instance of the sum-check protocol
329 let (sc_proof_phase2, ry, _claims_phase2) = R1CSProof::<E>::prove_phase_two(
330 num_rounds_y,
331 &claim_phase2,
332 &mut DensePolynomial::new(z),
333 &mut DensePolynomial::new(evals_ABC),
334 transcript,
335 );
336 timer_sc_proof_phase2.stop();
337 let transcript_sat_state = transcript.challenge_scalar(b"");
338 transcript.new_from_state(&transcript_sat_state);
339
340 let timmer_opening = Timer::new("polyopening");
341
342 let (comm, proof_eval_vars_at_ry, mipp_proof) =
343 pl.open(transcript, comm_list, &gens.gens_pc.ck, &ry[1..], &t);
344
345 timmer_opening.stop();
346
347 let timer_polyeval = Timer::new("polyeval");
348 let eval_vars_at_ry = pl.eval(&ry[1..]);
349 timer_polyeval.stop();
350 timer_prove.stop();
351 (
352 R1CSProof {
353 comm,
354 initial_state,
355 sc_proof_phase1,
356 claims_phase2: (*Az_claim, *Bz_claim, *Cz_claim, prod_Az_Bz_claims),
357 sc_proof_phase2,
358 eval_vars_at_ry,
359 proof_eval_vars_at_ry,
360 rx: rx.clone(),
361 ry: ry.clone(),
362 transcript_sat_state,
363 t,
364 mipp_proof,
365 },
366 rx,
367 ry,
368 )
369 }
370
371 // Creates a Groth16 proof for the verification of sumcheck, expressed
372 // as a circuit.
373 pub fn prove_verifier(
374 &self,
375 num_vars: usize,
376 num_cons: usize,
377 input: &[E::ScalarField],
378 evals: &(E::ScalarField, E::ScalarField, E::ScalarField),
379 transcript: &mut PoseidonTranscript<E::ScalarField>,
380 gens: &R1CSGens<E>,
381 poseidon: PoseidonConfig<E::ScalarField>,
382 ) -> Result<R1CSVerifierProof<E>, ProofVerifyError> {
383 // serialise and add the IPP commitment to the transcript
384 transcript.append_gt::<E>(b"", &self.t);
385
386 let initial_state = transcript.challenge_scalar(b"");
387 transcript.new_from_state(&initial_state);
388
389 let mut input_as_sparse_poly_entries = vec![SparsePolyEntry::new(0, E::ScalarField::one())];
390 //remaining inputs
391 input_as_sparse_poly_entries.extend(
392 (0..input.len())
393 .map(|i| SparsePolyEntry::new(i + 1, input[i]))
394 .collect::<Vec<SparsePolyEntry<E::ScalarField>>>(),
395 );
396 let input_as_sparse_poly =
397 SparsePolynomial::new(num_vars.log_2() as usize, input_as_sparse_poly_entries);
398
399 let config = VerifierConfig {
400 num_vars,
401 num_cons,
402 input: input.to_vec(),
403 evals: *evals,
404 params: poseidon,
405 prev_challenge: initial_state,
406 claims_phase2: self.claims_phase2,
407 polys_sc1: self.sc_proof_phase1.polys.clone(),
408 polys_sc2: self.sc_proof_phase2.polys.clone(),
409 eval_vars_at_ry: self.eval_vars_at_ry,
410 input_as_sparse_poly,
411 comm: self.comm.clone(),
412 rx: self.rx.clone(),
413 ry: self.ry.clone(),
414 transcript_sat_state: self.transcript_sat_state,
415 };
416
417 let circuit = R1CSVerificationCircuit::new(&config);
418
419 let circuit_prover_timer = Timer::new("provecircuit");
420 let proof = Groth16::<E>::prove(&gens.gens_gc.pk, circuit, &mut rand::thread_rng()).unwrap();
421 circuit_prover_timer.stop();
422
423 Ok(R1CSVerifierProof {
424 comm: self.comm.clone(),
425 circuit_proof: proof,
426 initial_state: self.initial_state,
427 transcript_sat_state: self.transcript_sat_state,
428 eval_vars_at_ry: self.eval_vars_at_ry,
429 proof_eval_vars_at_ry: self.proof_eval_vars_at_ry.clone(),
430 t: self.t,
431 mipp_proof: self.mipp_proof.clone(),
432 })
433 }
434}
435
436impl<E: Pairing> R1CSVerifierProof<E>
437where
438 <E as Pairing>::ScalarField: Absorb,
439{
440 // Verifier the Groth16 proof for the sumcheck circuit and the PST polynomial
441 // commitment opening.
442 pub fn verify(
443 &self,
444 r: (Vec<E::ScalarField>, Vec<E::ScalarField>),
445 input: &[E::ScalarField],
446 evals: &(E::ScalarField, E::ScalarField, E::ScalarField),
447 transcript: &mut PoseidonTranscript<E::ScalarField>,
448 gens: &R1CSGens<E>,
449 ) -> Result<bool, ProofVerifyError> {
450 let (rx, ry) = &r;
451 let (Ar, Br, Cr) = evals;
452 let mut pubs = vec![self.initial_state];
453 pubs.extend(input.clone());
454 pubs.extend(rx.clone());
455 pubs.extend(ry.clone());
456 pubs.extend(vec![
457 self.eval_vars_at_ry,
458 *Ar,
459 *Br,
460 *Cr,
461 self.transcript_sat_state,
462 ]);
463 transcript.new_from_state(&self.transcript_sat_state);
464 par! {
465 // verifies the Groth16 proof for the spartan verifier
466 let is_verified = Groth16::<E>::verify(&gens.gens_gc.vk, &pubs, &self.circuit_proof).unwrap(),
467
468 // verifies the proof of opening against the result of evaluating the
469 // witness polynomial at point ry
470 let res = Polynomial::verify(
471 transcript,
472 &gens.gens_pc.vk,
473 &self.comm,
474 &ry[1..],
475 self.eval_vars_at_ry,
476 &self.proof_eval_vars_at_ry,
477 &self.mipp_proof,
478 &self.t,
479 )
480 };
481 assert!(is_verified == true);
482 assert!(res == true);
483 Ok(is_verified && res)
484 }
485}
486
487#[cfg(test)]
488mod tests {
489
490 use super::*;
491
492 use ark_ff::PrimeField;
493 use ark_std::UniformRand;
494 type F = ark_bls12_377::Fr;
495
496 fn produce_tiny_r1cs() -> (R1CSInstance<F>, Vec<F>, Vec<F>) {
497 // three constraints over five variables Z1, Z2, Z3, Z4, and Z5
498 // rounded to the nearest power of two
499 let num_cons = 128;
500 let num_vars = 256;
501 let num_inputs = 2;
502
503 // encode the above constraints into three matrices
504 let mut A: Vec<(usize, usize, F)> = Vec::new();
505 let mut B: Vec<(usize, usize, F)> = Vec::new();
506 let mut C: Vec<(usize, usize, F)> = Vec::new();
507
508 let one = F::one();
509 // constraint 0 entries
510 // (Z1 + Z2) * I0 - Z3 = 0;
511 A.push((0, 0, one));
512 A.push((0, 1, one));
513 B.push((0, num_vars + 1, one));
514 C.push((0, 2, one));
515
516 // constraint 1 entries
517 // (Z1 + I1) * (Z3) - Z4 = 0
518 A.push((1, 0, one));
519 A.push((1, num_vars + 2, one));
520 B.push((1, 2, one));
521 C.push((1, 3, one));
522 // constraint 3 entries
523 // Z5 * 1 - 0 = 0
524 A.push((2, 4, one));
525 B.push((2, num_vars, one));
526
527 let inst = R1CSInstance::new(num_cons, num_vars, num_inputs, &A, &B, &C);
528
529 // compute a satisfying assignment
530 let mut rng = ark_std::rand::thread_rng();
531 let i0 = F::rand(&mut rng);
532 let i1 = F::rand(&mut rng);
533 let z1 = F::rand(&mut rng);
534 let z2 = F::rand(&mut rng);
535 let z3 = (z1 + z2) * i0; // constraint 1: (Z1 + Z2) * I0 - Z3 = 0;
536 let z4 = (z1 + i1) * z3; // constraint 2: (Z1 + I1) * (Z3) - Z4 = 0
537 let z5 = F::zero(); //constraint 3
538
539 let mut vars = vec![F::zero(); num_vars];
540 vars[0] = z1;
541 vars[1] = z2;
542 vars[2] = z3;
543 vars[3] = z4;
544 vars[4] = z5;
545
546 let mut input = vec![F::zero(); num_inputs];
547 input[0] = i0;
548 input[1] = i1;
549
550 (inst, vars, input)
551 }
552
553 #[test]
554 fn test_tiny_r1cs() {
555 let (inst, vars, input) = tests::produce_tiny_r1cs();
556 let is_sat = inst.is_sat(&vars, &input);
557 assert!(is_sat);
558 }
559
560 #[test]
561 fn test_synthetic_r1cs() {
562 type F = ark_bls12_377::Fr;
563 let (inst, vars, input) = R1CSInstance::<F>::produce_synthetic_r1cs(1024, 1024, 10);
564 let is_sat = inst.is_sat(&vars, &input);
565 assert!(is_sat);
566 }
567
568 use crate::parameters::PoseidonConfiguration;
569 #[test]
570 fn check_r1cs_proof_ark_blst() {
571 let params = ark_blst::Scalar::poseidon_params();
572 check_r1cs_proof::<ark_blst::Bls12>(params);
573 }
574 #[test]
575 fn check_r1cs_proof_bls12_377() {
576 let params = ark_bls12_377::Fr::poseidon_params();
577 check_r1cs_proof::<ark_bls12_377::Bls12_377>(params);
578 }
579
580 #[test]
581 fn check_r1cs_proof_bls12_381() {
582 let params = ark_bls12_381::Fr::poseidon_params();
583 check_r1cs_proof::<ark_bls12_381::Bls12_381>(params);
584 }
585 fn check_r1cs_proof<P>(params: PoseidonConfig<P::ScalarField>)
586 where
587 P: Pairing,
588 P::ScalarField: PrimeField,
589 P::ScalarField: Absorb,
590 {
591 let num_vars = 1024;
592 let num_cons = num_vars;
593 let num_inputs = 3;
594 let (inst, vars, input) =
595 R1CSInstance::<P::ScalarField>::produce_synthetic_r1cs(num_cons, num_vars, num_inputs);
596
597 let gens = R1CSGens::<P>::setup(b"test-m", num_cons, num_vars, num_inputs, params.clone());
598
599 //let params = poseidon_params();
600 // let mut random_tape = RandomTape::new(b"proof");
601
602 let mut prover_transcript = PoseidonTranscript::new(¶ms.clone());
603 let c = prover_transcript.challenge_scalar::<P::ScalarField>(b"");
604 prover_transcript.new_from_state(&c);
605 let (proof, rx, ry) = R1CSProof::prove(&inst, vars, &input, &gens, &mut prover_transcript);
606
607 let inst_evals = inst.evaluate(&rx, &ry);
608
609 prover_transcript.new_from_state(&c);
610 let verifier_proof = proof
611 .prove_verifier(
612 num_vars,
613 num_cons,
614 &input,
615 &inst_evals,
616 &mut prover_transcript,
617 &gens,
618 params.clone(),
619 )
620 .unwrap();
621
622 let mut verifier_transcript = PoseidonTranscript::new(¶ms.clone());
623 assert!(verifier_proof
624 .verify(
625 (rx, ry),
626 &input,
627 &inst_evals,
628 &mut verifier_transcript,
629 &gens
630 )
631 .is_ok());
632 }
633}