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