1use crate::{
4 circuits::{
5 argument::ArgumentType,
6 berkeley_columns::{BerkeleyChallenges, Column},
7 constraints::ConstraintSystem,
8 expr::{Constants, PolishToken},
9 gate::GateType,
10 lookup::{lookups::LookupPattern, tables::combine_table},
11 polynomials::permutation,
12 scalars::RandomOracles,
13 wires::{COLUMNS, PERMUTS},
14 },
15 curve::KimchiCurve,
16 error::VerifyError,
17 oracles::OraclesResult,
18 plonk_sponge::FrSponge,
19 proof::{PointEvaluations, ProofEvaluations, ProverProof, RecursionChallenge},
20 verifier_index::VerifierIndex,
21};
22use ark_ec::AffineRepr;
23use ark_ff::{Field, One, PrimeField, Zero};
24use ark_poly::{univariate::DensePolynomial, EvaluationDomain, Polynomial};
25use mina_poseidon::{sponge::ScalarChallenge, FqSponge};
26use o1_utils::ExtendedDensePolynomial;
27use poly_commitment::{
28 commitment::{
29 absorb_commitment, combined_inner_product, BatchEvaluationProof, Evaluation, PolyComm,
30 },
31 OpenProof, SRS as _,
32};
33use rand::thread_rng;
34
35pub type Result<T> = core::result::Result<T, VerifyError>;
37
38#[derive(Debug)]
39pub struct Context<'a, G: KimchiCurve, OpeningProof: OpenProof<G>> {
40 pub verifier_index: &'a VerifierIndex<G, OpeningProof>,
42
43 pub proof: &'a ProverProof<G, OpeningProof>,
45
46 pub public_input: &'a [G::ScalarField],
48}
49
50impl<'a, G: KimchiCurve, OpeningProof: OpenProof<G>> Context<'a, G, OpeningProof> {
51 pub fn get_column(&self, col: Column) -> Option<&'a PolyComm<G>> {
52 use Column::*;
53 match col {
54 Witness(i) => Some(&self.proof.commitments.w_comm[i]),
55 Coefficient(i) => Some(&self.verifier_index.coefficients_comm[i]),
56 Permutation(i) => Some(&self.verifier_index.sigma_comm[i]),
57 Z => Some(&self.proof.commitments.z_comm),
58 LookupSorted(i) => Some(&self.proof.commitments.lookup.as_ref()?.sorted[i]),
59 LookupAggreg => Some(&self.proof.commitments.lookup.as_ref()?.aggreg),
60 LookupKindIndex(i) => {
61 Some(self.verifier_index.lookup_index.as_ref()?.lookup_selectors[i].as_ref()?)
62 }
63 LookupTable => None,
64 LookupRuntimeSelector => Some(
65 self.verifier_index
66 .lookup_index
67 .as_ref()?
68 .runtime_tables_selector
69 .as_ref()?,
70 ),
71 LookupRuntimeTable => self.proof.commitments.lookup.as_ref()?.runtime.as_ref(),
72 Index(t) => {
73 use GateType::*;
74 match t {
75 Zero => None,
76 Generic => Some(&self.verifier_index.generic_comm),
77 Lookup => None,
78 CompleteAdd => Some(&self.verifier_index.complete_add_comm),
79 VarBaseMul => Some(&self.verifier_index.mul_comm),
80 EndoMul => Some(&self.verifier_index.emul_comm),
81 EndoMulScalar => Some(&self.verifier_index.endomul_scalar_comm),
82 Poseidon => Some(&self.verifier_index.psm_comm),
83 CairoClaim | CairoInstruction | CairoFlags | CairoTransition => None,
84 RangeCheck0 => Some(self.verifier_index.range_check0_comm.as_ref()?),
85 RangeCheck1 => Some(self.verifier_index.range_check1_comm.as_ref()?),
86 ForeignFieldAdd => Some(self.verifier_index.foreign_field_add_comm.as_ref()?),
87 ForeignFieldMul => Some(self.verifier_index.foreign_field_mul_comm.as_ref()?),
88 Xor16 => Some(self.verifier_index.xor_comm.as_ref()?),
89 Rot64 => Some(self.verifier_index.rot_comm.as_ref()?),
90 }
91 }
92 }
93 }
94}
95
96impl<G: KimchiCurve, OpeningProof: OpenProof<G>> ProverProof<G, OpeningProof>
97where
98 G::BaseField: PrimeField,
99{
100 pub fn oracles<
110 EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField>,
111 EFrSponge: FrSponge<G::ScalarField>,
112 >(
113 &self,
114 index: &VerifierIndex<G, OpeningProof>,
115 public_comm: &PolyComm<G>,
116 public_input: Option<&[G::ScalarField]>,
117 ) -> Result<OraclesResult<G, EFqSponge>> {
118 let n = index.domain.size;
124 let (_, endo_r) = G::endos();
125
126 let chunk_size = {
127 let d1_size = index.domain.size();
128 if d1_size < index.max_poly_size {
129 1
130 } else {
131 d1_size / index.max_poly_size
132 }
133 };
134
135 let zk_rows = index.zk_rows;
136
137 let mut fq_sponge = EFqSponge::new(G::other_curve_sponge_params());
141
142 let verifier_index_digest = index.digest::<EFqSponge>();
144 fq_sponge.absorb_fq(&[verifier_index_digest]);
145
146 for RecursionChallenge { comm, .. } in &self.prev_challenges {
148 absorb_commitment(&mut fq_sponge, comm);
149 }
150
151 absorb_commitment(&mut fq_sponge, public_comm);
153
154 self.commitments
156 .w_comm
157 .iter()
158 .for_each(|c| absorb_commitment(&mut fq_sponge, c));
159
160 if let Some(l) = &index.lookup_index {
162 let lookup_commits = self
163 .commitments
164 .lookup
165 .as_ref()
166 .ok_or(VerifyError::LookupCommitmentMissing)?;
167
168 if l.runtime_tables_selector.is_some() {
170 let runtime_commit = lookup_commits
171 .runtime
172 .as_ref()
173 .ok_or(VerifyError::IncorrectRuntimeProof)?;
174 absorb_commitment(&mut fq_sponge, runtime_commit);
175 }
176 }
177
178 let joint_combiner = if let Some(l) = &index.lookup_index {
179 let joint_combiner = if l.joint_lookup_used {
183 fq_sponge.challenge()
184 } else {
185 G::ScalarField::zero()
186 };
187
188 let joint_combiner = ScalarChallenge(joint_combiner);
191 let joint_combiner_field = joint_combiner.to_field(endo_r);
192 let joint_combiner = (joint_combiner, joint_combiner_field);
193
194 Some(joint_combiner)
195 } else {
196 None
197 };
198
199 if index.lookup_index.is_some() {
200 let lookup_commits = self
201 .commitments
202 .lookup
203 .as_ref()
204 .ok_or(VerifyError::LookupCommitmentMissing)?;
205
206 for com in &lookup_commits.sorted {
208 absorb_commitment(&mut fq_sponge, com);
209 }
210 }
211
212 let beta = fq_sponge.challenge();
215
216 let gamma = fq_sponge.challenge();
218
219 if index.lookup_index.is_some() {
221 let lookup_commits = self
223 .commitments
224 .lookup
225 .as_ref()
226 .ok_or(VerifyError::LookupCommitmentMissing)?;
227 absorb_commitment(&mut fq_sponge, &lookup_commits.aggreg);
228 }
229
230 absorb_commitment(&mut fq_sponge, &self.commitments.z_comm);
232
233 let alpha_chal = ScalarChallenge(fq_sponge.challenge());
236
237 let alpha = alpha_chal.to_field(endo_r);
239
240 if self.commitments.t_comm.len() > chunk_size * 7 {
242 return Err(VerifyError::IncorrectCommitmentLength(
243 "t",
244 chunk_size * 7,
245 self.commitments.t_comm.len(),
246 ));
247 }
248
249 absorb_commitment(&mut fq_sponge, &self.commitments.t_comm);
251
252 let zeta_chal = ScalarChallenge(fq_sponge.challenge());
255
256 let zeta = zeta_chal.to_field(endo_r);
258
259 let digest = fq_sponge.clone().digest();
265 let mut fr_sponge = EFrSponge::new(G::sponge_params());
266
267 fr_sponge.absorb(&digest);
269
270 let prev_challenge_digest = {
272 let mut fr_sponge = EFrSponge::new(G::sponge_params());
275 for RecursionChallenge { chals, .. } in &self.prev_challenges {
276 fr_sponge.absorb_multiple(chals);
277 }
278 fr_sponge.digest()
279 };
280 fr_sponge.absorb(&prev_challenge_digest);
281
282 let zeta1 = zeta.pow([n]);
284 let zetaw = zeta * index.domain.group_gen;
285 let evaluation_points = [zeta, zetaw];
286 let powers_of_eval_points_for_chunks = PointEvaluations {
287 zeta: zeta.pow([index.max_poly_size as u64]),
288 zeta_omega: zetaw.pow([index.max_poly_size as u64]),
289 };
290
291 let polys: Vec<(PolyComm<G>, _)> = self
293 .prev_challenges
294 .iter()
295 .map(|challenge| {
296 let evals = challenge.evals(
297 index.max_poly_size,
298 &evaluation_points,
299 &[
300 powers_of_eval_points_for_chunks.zeta,
301 powers_of_eval_points_for_chunks.zeta_omega,
302 ],
303 );
304 let RecursionChallenge { chals: _, comm } = challenge;
305 (comm.clone(), evals)
306 })
307 .collect();
308
309 let mut all_alphas = index.powers_of_alpha.clone();
311 all_alphas.instantiate(alpha);
312
313 let public_evals = if let Some(public_evals) = &self.evals.public {
314 [public_evals.zeta.clone(), public_evals.zeta_omega.clone()]
315 } else if chunk_size > 1 {
316 return Err(VerifyError::MissingPublicInputEvaluation);
317 } else if let Some(public_input) = public_input {
318 let w: Vec<_> = index.domain.elements().take(public_input.len()).collect();
320
321 let mut zeta_minus_x: Vec<_> = w.iter().map(|w| zeta - w).collect();
322
323 w.iter()
324 .take(public_input.len())
325 .for_each(|w| zeta_minus_x.push(zetaw - w));
326
327 ark_ff::fields::batch_inversion::<G::ScalarField>(&mut zeta_minus_x);
328
329 if public_input.is_empty() {
333 [vec![G::ScalarField::zero()], vec![G::ScalarField::zero()]]
334 } else {
335 [
336 vec![
337 (public_input
338 .iter()
339 .zip(zeta_minus_x.iter())
340 .zip(index.domain.elements())
341 .map(|((p, l), w)| -*l * p * w)
342 .fold(G::ScalarField::zero(), |x, y| x + y))
343 * (zeta1 - G::ScalarField::one())
344 * index.domain.size_inv,
345 ],
346 vec![
347 (public_input
348 .iter()
349 .zip(zeta_minus_x[public_input.len()..].iter())
350 .zip(index.domain.elements())
351 .map(|((p, l), w)| -*l * p * w)
352 .fold(G::ScalarField::zero(), |x, y| x + y))
353 * index.domain.size_inv
354 * (zetaw.pow([n]) - G::ScalarField::one()),
355 ],
356 ]
357 }
358 } else {
359 return Err(VerifyError::MissingPublicInputEvaluation);
360 };
361
362 fr_sponge.absorb(&self.ft_eval1);
364
365 fr_sponge.absorb_multiple(&public_evals[0]);
373 fr_sponge.absorb_multiple(&public_evals[1]);
374 fr_sponge.absorb_evaluations(&self.evals);
375
376 let v_chal = fr_sponge.challenge();
378
379 let v = v_chal.to_field(endo_r);
381
382 let u_chal = fr_sponge.challenge();
384
385 let u = u_chal.to_field(endo_r);
387
388 let evals = self.evals.combine(&powers_of_eval_points_for_chunks);
391
392 let ft_eval0 = {
394 let permutation_vanishing_polynomial =
395 index.permutation_vanishing_polynomial_m().evaluate(&zeta);
396 let zeta1m1 = zeta1 - G::ScalarField::one();
397
398 let mut alpha_powers =
399 all_alphas.get_alphas(ArgumentType::Permutation, permutation::CONSTRAINTS);
400 let alpha0 = alpha_powers
401 .next()
402 .expect("missing power of alpha for permutation");
403 let alpha1 = alpha_powers
404 .next()
405 .expect("missing power of alpha for permutation");
406 let alpha2 = alpha_powers
407 .next()
408 .expect("missing power of alpha for permutation");
409
410 let init = (evals.w[PERMUTS - 1].zeta + gamma)
411 * evals.z.zeta_omega
412 * alpha0
413 * permutation_vanishing_polynomial;
414 let mut ft_eval0 = evals
415 .w
416 .iter()
417 .zip(evals.s.iter())
418 .map(|(w, s)| (beta * s.zeta) + w.zeta + gamma)
419 .fold(init, |x, y| x * y);
420
421 ft_eval0 -= DensePolynomial::eval_polynomial(
422 &public_evals[0],
423 powers_of_eval_points_for_chunks.zeta,
424 );
425
426 ft_eval0 -= evals
427 .w
428 .iter()
429 .zip(index.shift.iter())
430 .map(|(w, s)| gamma + (beta * zeta * s) + w.zeta)
431 .fold(
432 alpha0 * permutation_vanishing_polynomial * evals.z.zeta,
433 |x, y| x * y,
434 );
435
436 let numerator = ((zeta1m1 * alpha1 * (zeta - index.w()))
437 + (zeta1m1 * alpha2 * (zeta - G::ScalarField::one())))
438 * (G::ScalarField::one() - evals.z.zeta);
439
440 let denominator = (zeta - index.w()) * (zeta - G::ScalarField::one());
441 let denominator = denominator.inverse().expect("negligible probability");
442
443 ft_eval0 += numerator * denominator;
444
445 let constants = Constants {
446 endo_coefficient: index.endo,
447 mds: &G::sponge_params().mds,
448 zk_rows,
449 };
450 let challenges = BerkeleyChallenges {
451 alpha,
452 beta,
453 gamma,
454 joint_combiner: joint_combiner
455 .as_ref()
456 .map(|j| j.1)
457 .unwrap_or(G::ScalarField::zero()),
458 };
459
460 ft_eval0 -= PolishToken::evaluate(
461 &index.linearization.constant_term,
462 index.domain,
463 zeta,
464 &evals,
465 &constants,
466 &challenges,
467 )
468 .unwrap();
469
470 ft_eval0
471 };
472
473 let combined_inner_product =
474 {
475 let ft_eval0 = vec![ft_eval0];
476 let ft_eval1 = vec![self.ft_eval1];
477
478 #[allow(clippy::type_complexity)]
479 let mut es: Vec<Vec<Vec<G::ScalarField>>> =
480 polys.iter().map(|(_, e)| e.clone()).collect();
481 es.push(public_evals.to_vec());
482 es.push(vec![ft_eval0, ft_eval1]);
483 for col in [
484 Column::Z,
485 Column::Index(GateType::Generic),
486 Column::Index(GateType::Poseidon),
487 Column::Index(GateType::CompleteAdd),
488 Column::Index(GateType::VarBaseMul),
489 Column::Index(GateType::EndoMul),
490 Column::Index(GateType::EndoMulScalar),
491 ]
492 .into_iter()
493 .chain((0..COLUMNS).map(Column::Witness))
494 .chain((0..COLUMNS).map(Column::Coefficient))
495 .chain((0..PERMUTS - 1).map(Column::Permutation))
496 .chain(
497 index
498 .range_check0_comm
499 .as_ref()
500 .map(|_| Column::Index(GateType::RangeCheck0)),
501 )
502 .chain(
503 index
504 .range_check1_comm
505 .as_ref()
506 .map(|_| Column::Index(GateType::RangeCheck1)),
507 )
508 .chain(
509 index
510 .foreign_field_add_comm
511 .as_ref()
512 .map(|_| Column::Index(GateType::ForeignFieldAdd)),
513 )
514 .chain(
515 index
516 .foreign_field_mul_comm
517 .as_ref()
518 .map(|_| Column::Index(GateType::ForeignFieldMul)),
519 )
520 .chain(
521 index
522 .xor_comm
523 .as_ref()
524 .map(|_| Column::Index(GateType::Xor16)),
525 )
526 .chain(
527 index
528 .rot_comm
529 .as_ref()
530 .map(|_| Column::Index(GateType::Rot64)),
531 )
532 .chain(
533 index
534 .lookup_index
535 .as_ref()
536 .map(|li| {
537 (0..li.lookup_info.max_per_row + 1)
538 .map(Column::LookupSorted)
539 .chain([Column::LookupAggreg, Column::LookupTable].into_iter())
540 .chain(
541 li.runtime_tables_selector
542 .as_ref()
543 .map(|_| [Column::LookupRuntimeTable].into_iter())
544 .into_iter()
545 .flatten(),
546 )
547 .chain(
548 self.evals
549 .runtime_lookup_table_selector
550 .as_ref()
551 .map(|_| Column::LookupRuntimeSelector),
552 )
553 .chain(
554 self.evals
555 .xor_lookup_selector
556 .as_ref()
557 .map(|_| Column::LookupKindIndex(LookupPattern::Xor)),
558 )
559 .chain(
560 self.evals
561 .lookup_gate_lookup_selector
562 .as_ref()
563 .map(|_| Column::LookupKindIndex(LookupPattern::Lookup)),
564 )
565 .chain(
566 self.evals.range_check_lookup_selector.as_ref().map(|_| {
567 Column::LookupKindIndex(LookupPattern::RangeCheck)
568 }),
569 )
570 .chain(self.evals.foreign_field_mul_lookup_selector.as_ref().map(
571 |_| Column::LookupKindIndex(LookupPattern::ForeignFieldMul),
572 ))
573 })
574 .into_iter()
575 .flatten(),
576 ) {
577 es.push({
578 let evals = self
579 .evals
580 .get_column(col)
581 .ok_or(VerifyError::MissingEvaluation(col))?;
582 vec![evals.zeta.clone(), evals.zeta_omega.clone()]
583 })
584 }
585
586 combined_inner_product(&v, &u, &es)
587 };
588
589 let oracles = RandomOracles {
590 joint_combiner,
591 beta,
592 gamma,
593 alpha_chal,
594 alpha,
595 zeta,
596 v,
597 u,
598 zeta_chal,
599 v_chal,
600 u_chal,
601 };
602
603 Ok(OraclesResult {
604 fq_sponge,
605 digest,
606 oracles,
607 all_alphas,
608 public_evals,
609 powers_of_eval_points_for_chunks,
610 polys,
611 zeta1,
612 ft_eval0,
613 combined_inner_product,
614 })
615 }
616}
617
618fn check_proof_evals_len<G, OpeningProof>(
622 proof: &ProverProof<G, OpeningProof>,
623 expected_size: usize,
624) -> Result<()>
625where
626 G: KimchiCurve,
627 G::BaseField: PrimeField,
628{
629 let ProofEvaluations {
630 public,
631 w,
632 z,
633 s,
634 coefficients,
635 generic_selector,
636 poseidon_selector,
637 complete_add_selector,
638 mul_selector,
639 emul_selector,
640 endomul_scalar_selector,
641 range_check0_selector,
642 range_check1_selector,
643 foreign_field_add_selector,
644 foreign_field_mul_selector,
645 xor_selector,
646 rot_selector,
647 lookup_aggregation,
648 lookup_table,
649 lookup_sorted,
650 runtime_lookup_table,
651 runtime_lookup_table_selector,
652 xor_lookup_selector,
653 lookup_gate_lookup_selector,
654 range_check_lookup_selector,
655 foreign_field_mul_lookup_selector,
656 } = &proof.evals;
657
658 let check_eval_len = |eval: &PointEvaluations<Vec<_>>, str: &'static str| -> Result<()> {
659 if eval.zeta.len() != expected_size {
660 Err(VerifyError::IncorrectEvaluationsLength(
661 expected_size,
662 eval.zeta.len(),
663 str,
664 ))
665 } else if eval.zeta_omega.len() != expected_size {
666 Err(VerifyError::IncorrectEvaluationsLength(
667 expected_size,
668 eval.zeta_omega.len(),
669 str,
670 ))
671 } else {
672 Ok(())
673 }
674 };
675
676 if let Some(public) = public {
677 check_eval_len(public, "public input")?;
678 }
679
680 for w_i in w {
681 check_eval_len(w_i, "witness")?;
682 }
683 check_eval_len(z, "permutation accumulator")?;
684 for s_i in s {
685 check_eval_len(s_i, "permutation shifts")?;
686 }
687 for coeff in coefficients {
688 check_eval_len(coeff, "coefficients")?;
689 }
690
691 for sorted in lookup_sorted.iter().flatten() {
693 check_eval_len(sorted, "lookup sorted")?
694 }
695
696 if let Some(lookup_aggregation) = lookup_aggregation {
697 check_eval_len(lookup_aggregation, "lookup aggregation")?;
698 }
699 if let Some(lookup_table) = lookup_table {
700 check_eval_len(lookup_table, "lookup table")?;
701 }
702 if let Some(runtime_lookup_table) = runtime_lookup_table {
703 check_eval_len(runtime_lookup_table, "runtime lookup table")?;
704 }
705
706 check_eval_len(generic_selector, "generic selector")?;
707 check_eval_len(poseidon_selector, "poseidon selector")?;
708 check_eval_len(complete_add_selector, "complete add selector")?;
709 check_eval_len(mul_selector, "mul selector")?;
710 check_eval_len(emul_selector, "endomul selector")?;
711 check_eval_len(endomul_scalar_selector, "endomul scalar selector")?;
712
713 if let Some(range_check0_selector) = range_check0_selector {
716 check_eval_len(range_check0_selector, "range check 0 selector")?
717 }
718 if let Some(range_check1_selector) = range_check1_selector {
719 check_eval_len(range_check1_selector, "range check 1 selector")?
720 }
721 if let Some(foreign_field_add_selector) = foreign_field_add_selector {
722 check_eval_len(foreign_field_add_selector, "foreign field add selector")?
723 }
724 if let Some(foreign_field_mul_selector) = foreign_field_mul_selector {
725 check_eval_len(foreign_field_mul_selector, "foreign field mul selector")?
726 }
727 if let Some(xor_selector) = xor_selector {
728 check_eval_len(xor_selector, "xor selector")?
729 }
730 if let Some(rot_selector) = rot_selector {
731 check_eval_len(rot_selector, "rot selector")?
732 }
733
734 if let Some(runtime_lookup_table_selector) = runtime_lookup_table_selector {
737 check_eval_len(
738 runtime_lookup_table_selector,
739 "runtime lookup table selector",
740 )?
741 }
742 if let Some(xor_lookup_selector) = xor_lookup_selector {
743 check_eval_len(xor_lookup_selector, "xor lookup selector")?
744 }
745 if let Some(lookup_gate_lookup_selector) = lookup_gate_lookup_selector {
746 check_eval_len(lookup_gate_lookup_selector, "lookup gate lookup selector")?
747 }
748 if let Some(range_check_lookup_selector) = range_check_lookup_selector {
749 check_eval_len(range_check_lookup_selector, "range check lookup selector")?
750 }
751 if let Some(foreign_field_mul_lookup_selector) = foreign_field_mul_lookup_selector {
752 check_eval_len(
753 foreign_field_mul_lookup_selector,
754 "foreign field mul lookup selector",
755 )?
756 }
757
758 Ok(())
759}
760
761fn to_batch<'a, G, EFqSponge, EFrSponge, OpeningProof: OpenProof<G>>(
762 verifier_index: &VerifierIndex<G, OpeningProof>,
763 proof: &'a ProverProof<G, OpeningProof>,
764 public_input: &'a [<G as AffineRepr>::ScalarField],
765) -> Result<BatchEvaluationProof<'a, G, EFqSponge, OpeningProof>>
766where
767 G: KimchiCurve,
768 G::BaseField: PrimeField,
769 EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField>,
770 EFrSponge: FrSponge<G::ScalarField>,
771{
772 let zk_rows = verifier_index.zk_rows;
781
782 if proof.prev_challenges.len() != verifier_index.prev_challenges {
783 return Err(VerifyError::IncorrectPrevChallengesLength(
784 verifier_index.prev_challenges,
785 proof.prev_challenges.len(),
786 ));
787 }
788 if public_input.len() != verifier_index.public {
789 return Err(VerifyError::IncorrectPubicInputLength(
790 verifier_index.public,
791 ));
792 }
793
794 let chunk_size = {
796 let d1_size = verifier_index.domain.size();
797 if d1_size < verifier_index.max_poly_size {
798 1
799 } else {
800 d1_size / verifier_index.max_poly_size
801 }
802 };
803 check_proof_evals_len(proof, chunk_size)?;
804
805 let public_comm = {
807 if public_input.len() != verifier_index.public {
808 return Err(VerifyError::IncorrectPubicInputLength(
809 verifier_index.public,
810 ));
811 }
812 let lgr_comm = verifier_index
813 .srs()
814 .get_lagrange_basis(verifier_index.domain);
815 let com: Vec<_> = lgr_comm.iter().take(verifier_index.public).collect();
816 if public_input.is_empty() {
817 PolyComm::new(vec![verifier_index.srs().blinding_commitment(); chunk_size])
818 } else {
819 let elm: Vec<_> = public_input.iter().map(|s| -*s).collect();
820 let public_comm = PolyComm::<G>::multi_scalar_mul(&com, &elm);
821 verifier_index
822 .srs()
823 .mask_custom(
824 public_comm.clone(),
825 &public_comm.map(|_| G::ScalarField::one()),
826 )
827 .unwrap()
828 .commitment
829 }
830 };
831
832 let OraclesResult {
834 fq_sponge,
835 oracles,
836 all_alphas,
837 public_evals,
838 powers_of_eval_points_for_chunks,
839 polys,
840 zeta1: zeta_to_domain_size,
841 ft_eval0,
842 combined_inner_product,
843 ..
844 } = proof.oracles::<EFqSponge, EFrSponge>(verifier_index, &public_comm, Some(public_input))?;
845
846 let evals = proof.evals.combine(&powers_of_eval_points_for_chunks);
850
851 let context = Context {
852 verifier_index,
853 proof,
854 public_input,
855 };
856
857 let f_comm = {
866 let permutation_vanishing_polynomial = verifier_index
868 .permutation_vanishing_polynomial_m()
869 .evaluate(&oracles.zeta);
870
871 let alphas = all_alphas.get_alphas(ArgumentType::Permutation, permutation::CONSTRAINTS);
872
873 let mut commitments = vec![&verifier_index.sigma_comm[PERMUTS - 1]];
874 let mut scalars = vec![ConstraintSystem::<G::ScalarField>::perm_scalars(
875 &evals,
876 oracles.beta,
877 oracles.gamma,
878 alphas,
879 permutation_vanishing_polynomial,
880 )];
881
882 {
884 let constants = Constants {
886 endo_coefficient: verifier_index.endo,
887 mds: &G::sponge_params().mds,
888 zk_rows,
889 };
890 let challenges = BerkeleyChallenges {
891 alpha: oracles.alpha,
892 beta: oracles.beta,
893 gamma: oracles.gamma,
894 joint_combiner: oracles
895 .joint_combiner
896 .as_ref()
897 .map(|j| j.1)
898 .unwrap_or(G::ScalarField::zero()),
899 };
900
901 for (col, tokens) in &verifier_index.linearization.index_terms {
902 let scalar = PolishToken::evaluate(
903 tokens,
904 verifier_index.domain,
905 oracles.zeta,
906 &evals,
907 &constants,
908 &challenges,
909 )
910 .expect("should evaluate");
911
912 let col = *col;
913 scalars.push(scalar);
914 commitments.push(
915 context
916 .get_column(col)
917 .ok_or(VerifyError::MissingCommitment(col))?,
918 );
919 }
920 }
921
922 PolyComm::multi_scalar_mul(&commitments, &scalars)
924 };
925
926 let ft_comm = {
929 let zeta_to_srs_len = oracles.zeta.pow([verifier_index.max_poly_size as u64]);
930 let chunked_f_comm = f_comm.chunk_commitment(zeta_to_srs_len);
931 let chunked_t_comm = &proof.commitments.t_comm.chunk_commitment(zeta_to_srs_len);
932 &chunked_f_comm - &chunked_t_comm.scale(zeta_to_domain_size - G::ScalarField::one())
933 };
934
935 let mut evaluations = vec![];
938
939 evaluations.extend(polys.into_iter().map(|(c, e)| Evaluation {
941 commitment: c,
942 evaluations: e,
943 }));
944
945 evaluations.push(Evaluation {
947 commitment: public_comm,
948 evaluations: public_evals.to_vec(),
949 });
950
951 evaluations.push(Evaluation {
953 commitment: ft_comm,
954 evaluations: vec![vec![ft_eval0], vec![proof.ft_eval1]],
955 });
956
957 for col in [
958 Column::Z,
960 Column::Index(GateType::Generic),
962 Column::Index(GateType::Poseidon),
963 Column::Index(GateType::CompleteAdd),
964 Column::Index(GateType::VarBaseMul),
965 Column::Index(GateType::EndoMul),
966 Column::Index(GateType::EndoMulScalar),
967 ]
968 .into_iter()
969 .chain((0..COLUMNS).map(Column::Witness))
971 .chain((0..COLUMNS).map(Column::Coefficient))
973 .chain((0..PERMUTS - 1).map(Column::Permutation))
975 .chain(
977 verifier_index
978 .range_check0_comm
979 .as_ref()
980 .map(|_| Column::Index(GateType::RangeCheck0)),
981 )
982 .chain(
983 verifier_index
984 .range_check1_comm
985 .as_ref()
986 .map(|_| Column::Index(GateType::RangeCheck1)),
987 )
988 .chain(
989 verifier_index
990 .foreign_field_add_comm
991 .as_ref()
992 .map(|_| Column::Index(GateType::ForeignFieldAdd)),
993 )
994 .chain(
995 verifier_index
996 .foreign_field_mul_comm
997 .as_ref()
998 .map(|_| Column::Index(GateType::ForeignFieldMul)),
999 )
1000 .chain(
1001 verifier_index
1002 .xor_comm
1003 .as_ref()
1004 .map(|_| Column::Index(GateType::Xor16)),
1005 )
1006 .chain(
1007 verifier_index
1008 .rot_comm
1009 .as_ref()
1010 .map(|_| Column::Index(GateType::Rot64)),
1011 )
1012 .chain(
1015 verifier_index
1016 .lookup_index
1017 .as_ref()
1018 .map(|li| {
1019 (0..li.lookup_info.max_per_row + 1)
1021 .map(Column::LookupSorted)
1022 .chain([Column::LookupAggreg].into_iter())
1024 })
1025 .into_iter()
1026 .flatten(),
1027 ) {
1028 let evals = proof
1029 .evals
1030 .get_column(col)
1031 .ok_or(VerifyError::MissingEvaluation(col))?;
1032 evaluations.push(Evaluation {
1033 commitment: context
1034 .get_column(col)
1035 .ok_or(VerifyError::MissingCommitment(col))?
1036 .clone(),
1037 evaluations: vec![evals.zeta.clone(), evals.zeta_omega.clone()],
1038 });
1039 }
1040
1041 if let Some(li) = &verifier_index.lookup_index {
1042 let lookup_comms = proof
1043 .commitments
1044 .lookup
1045 .as_ref()
1046 .ok_or(VerifyError::LookupCommitmentMissing)?;
1047
1048 let lookup_table = proof
1049 .evals
1050 .lookup_table
1051 .as_ref()
1052 .ok_or(VerifyError::LookupEvalsMissing)?;
1053 let runtime_lookup_table = proof.evals.runtime_lookup_table.as_ref();
1054
1055 let table_comm = {
1057 let joint_combiner = oracles
1058 .joint_combiner
1059 .expect("joint_combiner should be present if lookups are used");
1060 let table_id_combiner = joint_combiner
1064 .1
1065 .pow([u64::from(li.lookup_info.max_joint_size)]);
1066 let lookup_table: Vec<_> = li.lookup_table.iter().collect();
1067 let runtime = lookup_comms.runtime.as_ref();
1068
1069 combine_table(
1070 &lookup_table,
1071 joint_combiner.1,
1072 table_id_combiner,
1073 li.table_ids.as_ref(),
1074 runtime,
1075 )
1076 };
1077
1078 evaluations.push(Evaluation {
1080 commitment: table_comm,
1081 evaluations: vec![lookup_table.zeta.clone(), lookup_table.zeta_omega.clone()],
1082 });
1083
1084 if li.runtime_tables_selector.is_some() {
1086 let runtime = lookup_comms
1087 .runtime
1088 .as_ref()
1089 .ok_or(VerifyError::IncorrectRuntimeProof)?;
1090 let runtime_eval = runtime_lookup_table
1091 .as_ref()
1092 .map(|x| x.map_ref(&|x| x.clone()))
1093 .ok_or(VerifyError::IncorrectRuntimeProof)?;
1094
1095 evaluations.push(Evaluation {
1096 commitment: runtime.clone(),
1097 evaluations: vec![runtime_eval.zeta, runtime_eval.zeta_omega],
1098 });
1099 }
1100 }
1101
1102 for col in verifier_index
1103 .lookup_index
1104 .as_ref()
1105 .map(|li| {
1106 (li.runtime_tables_selector
1107 .as_ref()
1108 .map(|_| Column::LookupRuntimeSelector))
1109 .into_iter()
1110 .chain(
1111 li.lookup_selectors
1112 .xor
1113 .as_ref()
1114 .map(|_| Column::LookupKindIndex(LookupPattern::Xor)),
1115 )
1116 .chain(
1117 li.lookup_selectors
1118 .lookup
1119 .as_ref()
1120 .map(|_| Column::LookupKindIndex(LookupPattern::Lookup)),
1121 )
1122 .chain(
1123 li.lookup_selectors
1124 .range_check
1125 .as_ref()
1126 .map(|_| Column::LookupKindIndex(LookupPattern::RangeCheck)),
1127 )
1128 .chain(
1129 li.lookup_selectors
1130 .ffmul
1131 .as_ref()
1132 .map(|_| Column::LookupKindIndex(LookupPattern::ForeignFieldMul)),
1133 )
1134 })
1135 .into_iter()
1136 .flatten()
1137 {
1138 let evals = proof
1139 .evals
1140 .get_column(col)
1141 .ok_or(VerifyError::MissingEvaluation(col))?;
1142 evaluations.push(Evaluation {
1143 commitment: context
1144 .get_column(col)
1145 .ok_or(VerifyError::MissingCommitment(col))?
1146 .clone(),
1147 evaluations: vec![evals.zeta.clone(), evals.zeta_omega.clone()],
1148 });
1149 }
1150
1151 let evaluation_points = vec![oracles.zeta, oracles.zeta * verifier_index.domain.group_gen];
1153 Ok(BatchEvaluationProof {
1154 sponge: fq_sponge,
1155 evaluations,
1156 evaluation_points,
1157 polyscale: oracles.v,
1158 evalscale: oracles.u,
1159 opening: &proof.proof,
1160 combined_inner_product,
1161 })
1162}
1163
1164pub fn verify<G, EFqSponge, EFrSponge, OpeningProof: OpenProof<G>>(
1170 group_map: &G::Map,
1171 verifier_index: &VerifierIndex<G, OpeningProof>,
1172 proof: &ProverProof<G, OpeningProof>,
1173 public_input: &[G::ScalarField],
1174) -> Result<()>
1175where
1176 G: KimchiCurve,
1177 G::BaseField: PrimeField,
1178 EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField>,
1179 EFrSponge: FrSponge<G::ScalarField>,
1180{
1181 let proofs = vec![Context {
1182 verifier_index,
1183 proof,
1184 public_input,
1185 }];
1186 batch_verify::<G, EFqSponge, EFrSponge, OpeningProof>(group_map, &proofs)
1187}
1188
1189pub fn batch_verify<G, EFqSponge, EFrSponge, OpeningProof: OpenProof<G>>(
1197 group_map: &G::Map,
1198 proofs: &[Context<G, OpeningProof>],
1199) -> Result<()>
1200where
1201 G: KimchiCurve,
1202 G::BaseField: PrimeField,
1203 EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField>,
1204 EFrSponge: FrSponge<G::ScalarField>,
1205{
1206 if proofs.is_empty() {
1215 return Ok(());
1216 }
1217
1218 let srs = proofs[0].verifier_index.srs();
1221 for &Context { verifier_index, .. } in proofs {
1222 if verifier_index.srs().max_poly_size() != srs.max_poly_size() {
1223 return Err(VerifyError::DifferentSRS);
1224 }
1225 }
1226
1227 let mut batch = vec![];
1229 for &Context {
1230 verifier_index,
1231 proof,
1232 public_input,
1233 } in proofs
1234 {
1235 batch.push(to_batch::<G, EFqSponge, EFrSponge, OpeningProof>(
1236 verifier_index,
1237 proof,
1238 public_input,
1239 )?);
1240 }
1241
1242 if OpeningProof::verify(srs, group_map, &mut batch, &mut thread_rng()) {
1244 Ok(())
1245 } else {
1246 Err(VerifyError::OpenProof)
1247 }
1248}