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