testudo/src/r1csproof.rs
src/r1csproof.rsRaw
1#![allow(clippy::too_many_arguments)]
2use crate::group::CompressedGroup;
3
4use super::commitments::{Commitments, MultiCommitGens};
5use super::dense_mlpoly::{
6 DensePolynomial, EqPolynomial, PolyCommitment, PolyCommitmentGens, PolyEvalProof,
7};
8use super::errors::ProofVerifyError;
9use super::group::{GroupElement, VartimeMultiscalarMul, CompressGroupElement, DecompressGroupElement};
10use super::nizk::{EqualityProof, KnowledgeProof, ProductProof};
11use super::r1csinstance::R1CSInstance;
12use super::random::RandomTape;
13use super::scalar::Scalar;
14use super::sparse_mlpoly::{SparsePolyEntry, SparsePolynomial};
15use super::sumcheck::ZKSumcheckInstanceProof;
16use super::timer::Timer;
17use super::transcript::{AppendToTranscript, ProofTranscript};
18use core::iter;
19use ark_ff::PrimeField;
20use merlin::Transcript;
21use ark_serialize::*;
22use ark_std::{Zero, One};
23use ark_ec::{ProjectiveCurve};
24
25#[derive(CanonicalSerialize, CanonicalDeserialize, Debug)]
26pub struct R1CSProof {
27 comm_vars: PolyCommitment,
28 sc_proof_phase1: ZKSumcheckInstanceProof,
29 claims_phase2: (
30 CompressedGroup,
31 CompressedGroup,
32 CompressedGroup,
33 CompressedGroup,
34 ),
35 pok_claims_phase2: (KnowledgeProof, ProductProof),
36 proof_eq_sc_phase1: EqualityProof,
37 sc_proof_phase2: ZKSumcheckInstanceProof,
38 comm_vars_at_ry: CompressedGroup,
39 proof_eval_vars_at_ry: PolyEvalProof,
40 proof_eq_sc_phase2: EqualityProof,
41}
42
43pub struct R1CSSumcheckGens {
44 gens_1: MultiCommitGens,
45 gens_3: MultiCommitGens,
46 gens_4: MultiCommitGens,
47}
48
49// TODO: fix passing gens_1_ref
50impl R1CSSumcheckGens {
51 pub fn new(label: &'static [u8], gens_1_ref: &MultiCommitGens) -> Self {
52 let gens_1 = gens_1_ref.clone();
53 let gens_3 = MultiCommitGens::new(3, label);
54 let gens_4 = MultiCommitGens::new(4, label);
55
56 R1CSSumcheckGens {
57 gens_1,
58 gens_3,
59 gens_4,
60 }
61 }
62}
63
64pub struct R1CSGens {
65 gens_sc: R1CSSumcheckGens,
66 gens_pc: PolyCommitmentGens,
67}
68
69impl R1CSGens {
70 pub fn new(label: &'static [u8], _num_cons: usize, num_vars: usize) -> Self {
71 let num_poly_vars = num_vars.log2() as usize;
72 let gens_pc = PolyCommitmentGens::new(num_poly_vars, label);
73 let gens_sc = R1CSSumcheckGens::new(label, &gens_pc.gens.gens_1);
74 R1CSGens { gens_sc, gens_pc }
75 }
76}
77
78impl R1CSProof {
79 fn prove_phase_one(
80 num_rounds: usize,
81 evals_tau: &mut DensePolynomial,
82 evals_Az: &mut DensePolynomial,
83 evals_Bz: &mut DensePolynomial,
84 evals_Cz: &mut DensePolynomial,
85 gens: &R1CSSumcheckGens,
86 transcript: &mut Transcript,
87 random_tape: &mut RandomTape,
88 ) -> (ZKSumcheckInstanceProof, Vec<Scalar>, Vec<Scalar>, Scalar) {
89 let comb_func = |poly_A_comp: &Scalar,
90 poly_B_comp: &Scalar,
91 poly_C_comp: &Scalar,
92 poly_D_comp: &Scalar|
93 -> Scalar { (*poly_A_comp) * ((*poly_B_comp) * poly_C_comp - poly_D_comp) };
94
95 let (sc_proof_phase_one, r, claims, blind_claim_postsc) =
96 ZKSumcheckInstanceProof::prove_cubic_with_additive_term(
97 &Scalar::zero(), // claim is zero
98 &Scalar::zero(), // blind for claim is also zero
99 num_rounds,
100 evals_tau,
101 evals_Az,
102 evals_Bz,
103 evals_Cz,
104 comb_func,
105 &gens.gens_1,
106 &gens.gens_4,
107 transcript,
108 random_tape,
109 );
110
111 (sc_proof_phase_one, r, claims, blind_claim_postsc)
112 }
113
114 fn prove_phase_two(
115 num_rounds: usize,
116 claim: &Scalar,
117 blind_claim: &Scalar,
118 evals_z: &mut DensePolynomial,
119 evals_ABC: &mut DensePolynomial,
120 gens: &R1CSSumcheckGens,
121 transcript: &mut Transcript,
122 random_tape: &mut RandomTape,
123 ) -> (ZKSumcheckInstanceProof, Vec<Scalar>, Vec<Scalar>, Scalar) {
124 let comb_func =
125 |poly_A_comp: &Scalar, poly_B_comp: &Scalar| -> Scalar { (*poly_A_comp) * poly_B_comp };
126 let (sc_proof_phase_two, r, claims, blind_claim_postsc) = ZKSumcheckInstanceProof::prove_quad(
127 claim,
128 blind_claim,
129 num_rounds,
130 evals_z,
131 evals_ABC,
132 comb_func,
133 &gens.gens_1,
134 &gens.gens_3,
135 transcript,
136 random_tape,
137 );
138
139 (sc_proof_phase_two, r, claims, blind_claim_postsc)
140 }
141
142 fn protocol_name() -> &'static [u8] {
143 b"R1CS proof"
144 }
145
146 pub fn prove(
147 inst: &R1CSInstance,
148 vars: Vec<Scalar>,
149 input: &[Scalar],
150 gens: &R1CSGens,
151 transcript: &mut Transcript,
152 random_tape: &mut RandomTape,
153 ) -> (R1CSProof, Vec<Scalar>, Vec<Scalar>) {
154 let timer_prove = Timer::new("R1CSProof::prove");
155 transcript.append_protocol_name(R1CSProof::protocol_name());
156
157 // we currently require the number of |inputs| + 1 to be at most number of vars
158 assert!(input.len() < vars.len());
159
160 input.append_to_transcript(b"input", transcript);
161
162 let timer_commit = Timer::new("polycommit");
163 let (poly_vars, comm_vars, blinds_vars) = {
164 // create a multilinear polynomial using the supplied assignment for variables
165 let poly_vars = DensePolynomial::new(vars.clone());
166
167 // produce a commitment to the satisfying assignment
168 let (comm_vars, blinds_vars) = poly_vars.commit(&gens.gens_pc, Some(random_tape));
169
170 // add the commitment to the prover's transcript
171 comm_vars.append_to_transcript(b"poly_commitment", transcript);
172 (poly_vars, comm_vars, blinds_vars)
173 };
174 timer_commit.stop();
175
176 let timer_sc_proof_phase1 = Timer::new("prove_sc_phase_one");
177
178 // append input to variables to create a single vector z
179 let z = {
180 let num_inputs = input.len();
181 let num_vars = vars.len();
182 let mut z = vars;
183 z.extend(&vec![Scalar::one()]); // add constant term in z
184 z.extend(input);
185 z.extend(&vec![Scalar::zero(); num_vars - num_inputs - 1]); // we will pad with zeros
186 z
187 };
188
189 // derive the verifier's challenge tau
190 let (num_rounds_x, num_rounds_y) =
191 (inst.get_num_cons().log2() as usize, z.len().log2() as usize);
192 let tau = transcript.challenge_vector(b"challenge_tau", num_rounds_x);
193 // compute the initial evaluation table for R(\tau, x)
194 let mut poly_tau = DensePolynomial::new(EqPolynomial::new(tau).evals());
195 let (mut poly_Az, mut poly_Bz, mut poly_Cz) =
196 inst.multiply_vec(inst.get_num_cons(), z.len(), &z);
197
198 let (sc_proof_phase1, rx, _claims_phase1, blind_claim_postsc1) = R1CSProof::prove_phase_one(
199 num_rounds_x,
200 &mut poly_tau,
201 &mut poly_Az,
202 &mut poly_Bz,
203 &mut poly_Cz,
204 &gens.gens_sc,
205 transcript,
206 random_tape,
207 );
208 assert_eq!(poly_tau.len(), 1);
209 assert_eq!(poly_Az.len(), 1);
210 assert_eq!(poly_Bz.len(), 1);
211 assert_eq!(poly_Cz.len(), 1);
212 timer_sc_proof_phase1.stop();
213
214 let (tau_claim, Az_claim, Bz_claim, Cz_claim) =
215 (&poly_tau[0], &poly_Az[0], &poly_Bz[0], &poly_Cz[0]);
216 let (Az_blind, Bz_blind, Cz_blind, prod_Az_Bz_blind) = (
217 random_tape.random_scalar(b"Az_blind"),
218 random_tape.random_scalar(b"Bz_blind"),
219 random_tape.random_scalar(b"Cz_blind"),
220 random_tape.random_scalar(b"prod_Az_Bz_blind"),
221 );
222
223 let (pok_Cz_claim, comm_Cz_claim) = {
224 KnowledgeProof::prove(
225 &gens.gens_sc.gens_1,
226 transcript,
227 random_tape,
228 Cz_claim,
229 &Cz_blind,
230 )
231 };
232
233 let (proof_prod, comm_Az_claim, comm_Bz_claim, comm_prod_Az_Bz_claims) = {
234 let prod = (*Az_claim) * Bz_claim;
235 ProductProof::prove(
236 &gens.gens_sc.gens_1,
237 transcript,
238 random_tape,
239 Az_claim,
240 &Az_blind,
241 Bz_claim,
242 &Bz_blind,
243 &prod,
244 &prod_Az_Bz_blind,
245 )
246 };
247
248 comm_Az_claim.append_to_transcript(b"comm_Az_claim", transcript);
249 comm_Bz_claim.append_to_transcript(b"comm_Bz_claim", transcript);
250 comm_Cz_claim.append_to_transcript(b"comm_Cz_claim", transcript);
251 comm_prod_Az_Bz_claims.append_to_transcript(b"comm_prod_Az_Bz_claims", transcript);
252
253 // prove the final step of sum-check #1
254 let taus_bound_rx = tau_claim;
255 let blind_expected_claim_postsc1 = (prod_Az_Bz_blind - Cz_blind) * taus_bound_rx;
256 let claim_post_phase1 = ((*Az_claim) * Bz_claim - Cz_claim) * taus_bound_rx;
257 let (proof_eq_sc_phase1, _C1, _C2) = EqualityProof::prove(
258 &gens.gens_sc.gens_1,
259 transcript,
260 random_tape,
261 &claim_post_phase1,
262 &blind_expected_claim_postsc1,
263 &claim_post_phase1,
264 &blind_claim_postsc1,
265 );
266
267 let timer_sc_proof_phase2 = Timer::new("prove_sc_phase_two");
268 // combine the three claims into a single claim
269 let r_A = transcript.challenge_scalar(b"challenege_Az");
270 let r_B = transcript.challenge_scalar(b"challenege_Bz");
271 let r_C = transcript.challenge_scalar(b"challenege_Cz");
272 let claim_phase2 = r_A * Az_claim + r_B * Bz_claim + r_C * Cz_claim;
273 let blind_claim_phase2 = r_A * Az_blind + r_B * Bz_blind + r_C * Cz_blind;
274
275 let evals_ABC = {
276 // compute the initial evaluation table for R(\tau, x)
277 let evals_rx = EqPolynomial::new(rx.clone()).evals();
278 let (evals_A, evals_B, evals_C) =
279 inst.compute_eval_table_sparse(inst.get_num_cons(), z.len(), &evals_rx);
280
281 assert_eq!(evals_A.len(), evals_B.len());
282 assert_eq!(evals_A.len(), evals_C.len());
283 (0..evals_A.len())
284 .map(|i| r_A * evals_A[i] + r_B * evals_B[i] + r_C * evals_C[i])
285 .collect::<Vec<Scalar>>()
286 };
287
288 // another instance of the sum-check protocol
289 let (sc_proof_phase2, ry, claims_phase2, blind_claim_postsc2) = R1CSProof::prove_phase_two(
290 num_rounds_y,
291 &claim_phase2,
292 &blind_claim_phase2,
293 &mut DensePolynomial::new(z),
294 &mut DensePolynomial::new(evals_ABC),
295 &gens.gens_sc,
296 transcript,
297 random_tape,
298 );
299 timer_sc_proof_phase2.stop();
300
301 let timer_polyeval = Timer::new("polyeval");
302 let eval_vars_at_ry = poly_vars.evaluate(&ry[1..].to_vec());
303 let blind_eval = random_tape.random_scalar(b"blind_eval");
304 let (proof_eval_vars_at_ry, comm_vars_at_ry) = PolyEvalProof::prove(
305 &poly_vars,
306 Some(&blinds_vars),
307 &ry[1..].to_vec(),
308 &eval_vars_at_ry,
309 Some(&blind_eval),
310 &gens.gens_pc,
311 transcript,
312 random_tape,
313 );
314 timer_polyeval.stop();
315
316 // prove the final step of sum-check #2
317 let blind_eval_Z_at_ry = (Scalar::one() - ry[0]) * blind_eval;
318 let blind_expected_claim_postsc2 = claims_phase2[1] * blind_eval_Z_at_ry;
319 let claim_post_phase2 = claims_phase2[0] * claims_phase2[1];
320 let (proof_eq_sc_phase2, _C1, _C2) = EqualityProof::prove(
321 &gens.gens_pc.gens.gens_1,
322 transcript,
323 random_tape,
324 &claim_post_phase2,
325 &blind_expected_claim_postsc2,
326 &claim_post_phase2,
327 &blind_claim_postsc2,
328 );
329
330 timer_prove.stop();
331
332 (
333 R1CSProof {
334 comm_vars,
335 sc_proof_phase1,
336 claims_phase2: (
337 comm_Az_claim,
338 comm_Bz_claim,
339 comm_Cz_claim,
340 comm_prod_Az_Bz_claims,
341 ),
342 pok_claims_phase2: (pok_Cz_claim, proof_prod),
343 proof_eq_sc_phase1,
344 sc_proof_phase2,
345 comm_vars_at_ry,
346 proof_eval_vars_at_ry,
347 proof_eq_sc_phase2,
348 },
349 rx,
350 ry,
351 )
352 }
353
354 pub fn verify(
355 &self,
356 num_vars: usize,
357 num_cons: usize,
358 input: &[Scalar],
359 evals: &(Scalar, Scalar, Scalar),
360 transcript: &mut Transcript,
361 gens: &R1CSGens,
362 ) -> Result<(Vec<Scalar>, Vec<Scalar>), ProofVerifyError> {
363 transcript.append_protocol_name(R1CSProof::protocol_name());
364
365 input.append_to_transcript(b"input", transcript);
366
367 let n = num_vars;
368 // add the commitment to the verifier's transcript
369 self
370 .comm_vars
371 .append_to_transcript(b"poly_commitment", transcript);
372
373 let (num_rounds_x, num_rounds_y) = (num_cons.log2() as usize, (2 * num_vars).log2() as usize);
374
375 // derive the verifier's challenge tau
376 let tau = transcript.challenge_vector(b"challenge_tau", num_rounds_x);
377
378 // verify the first sum-check instance
379 let claim_phase1 = Scalar::zero()
380 .commit(&Scalar::zero(), &gens.gens_sc.gens_1)
381 .compress();
382 let (comm_claim_post_phase1, rx) = self.sc_proof_phase1.verify(
383 &claim_phase1,
384 num_rounds_x,
385 3,
386 &gens.gens_sc.gens_1,
387 &gens.gens_sc.gens_4,
388 transcript,
389 )?;
390 // perform the intermediate sum-check test with claimed Az, Bz, and Cz
391 let (comm_Az_claim, comm_Bz_claim, comm_Cz_claim, comm_prod_Az_Bz_claims) = &self.claims_phase2;
392 let (pok_Cz_claim, proof_prod) = &self.pok_claims_phase2;
393
394 pok_Cz_claim.verify(&gens.gens_sc.gens_1, transcript, comm_Cz_claim)?;
395 proof_prod.verify(
396 &gens.gens_sc.gens_1,
397 transcript,
398 comm_Az_claim,
399 comm_Bz_claim,
400 comm_prod_Az_Bz_claims,
401 )?;
402
403 comm_Az_claim.append_to_transcript(b"comm_Az_claim", transcript);
404 comm_Bz_claim.append_to_transcript(b"comm_Bz_claim", transcript);
405 comm_Cz_claim.append_to_transcript(b"comm_Cz_claim", transcript);
406 comm_prod_Az_Bz_claims.append_to_transcript(b"comm_prod_Az_Bz_claims", transcript);
407
408 let taus_bound_rx: Scalar = (0..rx.len())
409 .map(|i| rx[i] * tau[i] + (Scalar::one() - rx[i]) * (Scalar::one() - tau[i]))
410 .product();
411 let expected_claim_post_phase1 = (GroupElement::decompress(comm_prod_Az_Bz_claims).unwrap() - GroupElement::decompress(comm_Cz_claim).unwrap()).mul(taus_bound_rx.into_repr())
412 .compress();
413
414 // verify proof that expected_claim_post_phase1 == claim_post_phase1
415 self.proof_eq_sc_phase1.verify(
416 &gens.gens_sc.gens_1,
417 transcript,
418 &expected_claim_post_phase1,
419 &comm_claim_post_phase1,
420 )?;
421
422 // derive three public challenges and then derive a joint claim
423 let r_A = transcript.challenge_scalar(b"challenege_Az");
424 let r_B = transcript.challenge_scalar(b"challenege_Bz");
425 let r_C = transcript.challenge_scalar(b"challenege_Cz");
426
427 // r_A * comm_Az_claim + r_B * comm_Bz_claim + r_C * comm_Cz_claim;
428 let comm_claim_phase2 = GroupElement::vartime_multiscalar_mul(
429 iter::once(&r_A)
430 .chain(iter::once(&r_B))
431 .chain(iter::once(&r_C)).map(|s| (*s)).collect::<Vec<Scalar>>().as_slice(),
432 iter::once(&comm_Az_claim)
433 .chain(iter::once(&comm_Bz_claim))
434 .chain(iter::once(&comm_Cz_claim))
435 .map(|pt| GroupElement::decompress(pt).unwrap())
436 .collect::<Vec<GroupElement>>().as_slice(),
437 )
438 .compress();
439
440 // verify the joint claim with a sum-check protocol
441 let (comm_claim_post_phase2, ry) = self.sc_proof_phase2.verify(
442 &comm_claim_phase2,
443 num_rounds_y,
444 2,
445 &gens.gens_sc.gens_1,
446 &gens.gens_sc.gens_3,
447 transcript,
448 )?;
449
450 // verify Z(ry) proof against the initial commitment
451 self.proof_eval_vars_at_ry.verify(
452 &gens.gens_pc,
453 transcript,
454 &ry[1..].to_vec(),
455 &self.comm_vars_at_ry,
456 &self.comm_vars,
457 )?;
458
459 let poly_input_eval = {
460 // constant term
461 let mut input_as_sparse_poly_entries = vec![SparsePolyEntry::new(0, Scalar::one())];
462 //remaining inputs
463 input_as_sparse_poly_entries.extend(
464 (0..input.len())
465 .map(|i| SparsePolyEntry::new(i + 1, input[i]))
466 .collect::<Vec<SparsePolyEntry>>(),
467 );
468 SparsePolynomial::new(n.log2() as usize, input_as_sparse_poly_entries)
469 .evaluate(&ry[1..].to_vec())
470 };
471
472 // compute commitment to eval_Z_at_ry = (Scalar::one() - ry[0]) * self.eval_vars_at_ry + ry[0] * poly_input_eval
473 let comm_eval_Z_at_ry = GroupElement::vartime_multiscalar_mul(
474 iter::once(Scalar::one() - ry[0]).chain(iter::once(ry[0])).collect::<Vec<Scalar>>().as_slice(),
475 iter::once(GroupElement::decompress(&self.comm_vars_at_ry).unwrap()).chain(iter::once(
476 poly_input_eval.commit(&Scalar::zero(), &gens.gens_pc.gens.gens_1),
477 )).collect::<Vec<GroupElement>>().as_slice(),
478 );
479
480 // perform the final check in the second sum-check protocol
481 let (eval_A_r, eval_B_r, eval_C_r) = evals;
482 let scalar = r_A * eval_A_r + r_B * eval_B_r + r_C * eval_C_r;
483 let expected_claim_post_phase2 =
484 comm_eval_Z_at_ry.mul(scalar.into_repr()).compress();
485 // verify proof that expected_claim_post_phase1 == claim_post_phase1
486 self.proof_eq_sc_phase2.verify(
487 &gens.gens_sc.gens_1,
488 transcript,
489 &expected_claim_post_phase2,
490 &comm_claim_post_phase2,
491 )?;
492
493 Ok((rx, ry))
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500use ark_std::{UniformRand};
501
502 fn produce_tiny_r1cs() -> (R1CSInstance, Vec<Scalar>, Vec<Scalar>) {
503 // three constraints over five variables Z1, Z2, Z3, Z4, and Z5
504 // rounded to the nearest power of two
505 let num_cons = 128;
506 let num_vars = 256;
507 let num_inputs = 2;
508
509 // encode the above constraints into three matrices
510 let mut A: Vec<(usize, usize, Scalar)> = Vec::new();
511 let mut B: Vec<(usize, usize, Scalar)> = Vec::new();
512 let mut C: Vec<(usize, usize, Scalar)> = Vec::new();
513
514 let one = Scalar::one();
515 // constraint 0 entries
516 // (Z1 + Z2) * I0 - Z3 = 0;
517 A.push((0, 0, one));
518 A.push((0, 1, one));
519 B.push((0, num_vars + 1, one));
520 C.push((0, 2, one));
521
522 // constraint 1 entries
523 // (Z1 + I1) * (Z3) - Z4 = 0
524 A.push((1, 0, one));
525 A.push((1, num_vars + 2, one));
526 B.push((1, 2, one));
527 C.push((1, 3, one));
528 // constraint 3 entries
529 // Z5 * 1 - 0 = 0
530 A.push((2, 4, one));
531 B.push((2, num_vars, one));
532
533 let inst = R1CSInstance::new(num_cons, num_vars, num_inputs, &A, &B, &C);
534
535 // compute a satisfying assignment
536 let mut rng = ark_std::rand::thread_rng();
537 let i0 = Scalar::rand(&mut rng);
538 let i1 = Scalar::rand(&mut rng);
539 let z1 = Scalar::rand(&mut rng);
540 let z2 = Scalar::rand(&mut rng);
541 let z3 = (z1 + z2) * i0; // constraint 1: (Z1 + Z2) * I0 - Z3 = 0;
542 let z4 = (z1 + i1) * z3; // constraint 2: (Z1 + I1) * (Z3) - Z4 = 0
543 let z5 = Scalar::zero(); //constraint 3
544
545 let mut vars = vec![Scalar::zero(); num_vars];
546 vars[0] = z1;
547 vars[1] = z2;
548 vars[2] = z3;
549 vars[3] = z4;
550 vars[4] = z5;
551
552 let mut input = vec![Scalar::zero(); num_inputs];
553 input[0] = i0;
554 input[1] = i1;
555
556 (inst, vars, input)
557 }
558
559 #[test]
560 fn test_tiny_r1cs() {
561 let (inst, vars, input) = tests::produce_tiny_r1cs();
562 let is_sat = inst.is_sat(&vars, &input);
563 assert!(is_sat);
564 }
565
566 #[test]
567 fn test_synthetic_r1cs() {
568 let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(1024, 1024, 10);
569 let is_sat = inst.is_sat(&vars, &input);
570 assert!(is_sat);
571 }
572
573 #[test]
574 pub fn check_r1cs_proof() {
575 let num_vars = 1024;
576 let num_cons = num_vars;
577 let num_inputs = 10;
578 let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs);
579
580 let gens = R1CSGens::new(b"test-m", num_cons, num_vars);
581
582 let mut random_tape = RandomTape::new(b"proof");
583 let mut prover_transcript = Transcript::new(b"example");
584 let (proof, rx, ry) = R1CSProof::prove(
585 &inst,
586 vars,
587 &input,
588 &gens,
589 &mut prover_transcript,
590 &mut random_tape,
591 );
592
593 let inst_evals = inst.evaluate(&rx, &ry);
594
595 let mut verifier_transcript = Transcript::new(b"example");
596 assert!(proof
597 .verify(
598 inst.get_num_vars(),
599 inst.get_num_cons(),
600 &input,
601 &inst_evals,
602 &mut verifier_transcript,
603 &gens,
604 )
605 .is_ok());
606 }
607}