1use crate::{
4 circuits::{
5 argument::{Argument, ArgumentType},
6 berkeley_columns::{BerkeleyChallenges, Environment, LookupEnvironment},
7 constraints::zk_rows_strict_lower_bound,
8 expr::{self, l0_1, Constants},
9 gate::GateType,
10 lookup::{self, runtime_tables::RuntimeTable, tables::combine_table_entry},
11 polynomials::{
12 complete_add::CompleteAdd,
13 endomul_scalar::EndomulScalar,
14 endosclmul::EndosclMul,
15 foreign_field_add::circuitgates::ForeignFieldAdd,
16 foreign_field_mul::{self, circuitgates::ForeignFieldMul},
17 generic, permutation,
18 poseidon::Poseidon,
19 range_check::circuitgates::{RangeCheck0, RangeCheck1},
20 rot::Rot64,
21 varbasemul::VarbaseMul,
22 xor::Xor16,
23 },
24 wires::{COLUMNS, PERMUTS},
25 },
26 collections::HashMap,
27 curve::KimchiCurve,
28 error::ProverError,
29 lagrange_basis_evaluations::LagrangeBasisEvaluations,
30 plonk_sponge::FrSponge,
31 proof::{
32 LookupCommitments, PointEvaluations, ProofEvaluations, ProverCommitments, ProverProof,
33 RecursionChallenge,
34 },
35 prover_index::ProverIndex,
36 verifier_index::VerifierIndex,
37};
38use ark_ff::{FftField, Field, One, PrimeField, UniformRand, Zero};
39use ark_poly::{
40 univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Evaluations, Polynomial,
41 Radix2EvaluationDomain as D,
42};
43use core::array;
44use itertools::Itertools;
45use mina_poseidon::{poseidon::ArithmeticSpongeParams, sponge::ScalarChallenge, FqSponge};
46use o1_utils::ExtendedDensePolynomial as _;
47use poly_commitment::{
48 commitment::{
49 absorb_commitment, b_poly_coefficients, BlindedCommitment, CommitmentCurve, PolyComm,
50 },
51 utils::DensePolynomialOrEvaluations,
52 OpenProof, SRS as _,
53};
54use rand_core::{CryptoRng, RngCore};
55use rayon::prelude::*;
56
57type Result<T> = core::result::Result<T, ProverError>;
59
60macro_rules! check_constraint {
62 ($index:expr, $evaluation:expr) => {{
63 check_constraint!($index, stringify!($evaluation), $evaluation);
64 }};
65 ($index:expr, $label:expr, $evaluation:expr) => {{
66 if cfg!(debug_assertions) {
67 let (_, res) = $evaluation
68 .interpolate_by_ref()
69 .divide_by_vanishing_poly($index.cs.domain.d1);
70 if !res.is_zero() {
71 panic!("couldn't divide by vanishing polynomial: {}", $label);
72 }
73 }
74 }};
75}
76
77fn blinder<F: UniformRand, RNG: RngCore + CryptoRng>(
85 num_chunks: usize,
86 rng: &mut RNG,
87) -> PolyComm<F> {
88 PolyComm::new((0..num_chunks).map(|_| F::rand(rng)).collect())
89}
90
91#[derive(Default)]
93struct LookupContext<G, F>
94where
95 G: CommitmentCurve,
96 F: FftField,
97{
98 joint_combiner: Option<F>,
100
101 table_id_combiner: Option<F>,
104
105 dummy_lookup_value: Option<F>,
107
108 joint_lookup_table: Option<DensePolynomial<F>>,
110 joint_lookup_table_d8: Option<Evaluations<F, D<F>>>,
111
112 sorted: Option<Vec<Evaluations<F, D<F>>>>,
114 sorted_coeffs: Option<Vec<DensePolynomial<F>>>,
115 sorted_comms: Option<Vec<BlindedCommitment<G>>>,
116 sorted8: Option<Vec<Evaluations<F, D<F>>>>,
117
118 aggreg_coeffs: Option<DensePolynomial<F>>,
120 aggreg_comm: Option<BlindedCommitment<G>>,
121 aggreg8: Option<Evaluations<F, D<F>>>,
122
123 pub lookup_aggregation_eval: Option<PointEvaluations<Vec<F>>>,
126 pub lookup_table_eval: Option<PointEvaluations<Vec<F>>>,
128 pub lookup_sorted_eval: [Option<PointEvaluations<Vec<F>>>; 5],
130 pub runtime_lookup_table_eval: Option<PointEvaluations<Vec<F>>>,
132
133 runtime_table: Option<DensePolynomial<F>>,
135 runtime_table_d8: Option<Evaluations<F, D<F>>>,
136 runtime_table_comm: Option<BlindedCommitment<G>>,
137}
138
139impl<G, OpeningProof, const FULL_ROUNDS: usize> ProverProof<G, OpeningProof, FULL_ROUNDS>
140where
141 G: KimchiCurve<FULL_ROUNDS>,
142 G::BaseField: PrimeField,
143 OpeningProof: OpenProof<G, FULL_ROUNDS>,
144{
145 pub fn create<EFqSponge, EFrSponge, RNG>(
151 groupmap: &G::Map,
152 witness: [Vec<G::ScalarField>; COLUMNS],
153 runtime_tables: &[RuntimeTable<G::ScalarField>],
154 index: &ProverIndex<FULL_ROUNDS, G, OpeningProof::SRS>,
155 rng: &mut RNG,
156 ) -> Result<Self>
157 where
158 EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField, FULL_ROUNDS>,
159 EFrSponge: FrSponge<G::ScalarField>,
160 EFrSponge: From<&'static ArithmeticSpongeParams<G::ScalarField, FULL_ROUNDS>>,
161 RNG: RngCore + CryptoRng,
162 VerifierIndex<FULL_ROUNDS, G, OpeningProof::SRS>: Clone,
163 {
164 Self::create_recursive::<EFqSponge, EFrSponge, RNG>(
165 groupmap,
166 witness,
167 runtime_tables,
168 index,
169 Vec::new(),
170 None,
171 rng,
172 )
173 }
174
175 pub fn create_recursive<EFqSponge, EFrSponge, RNG>(
187 group_map: &G::Map,
188 mut witness: [Vec<G::ScalarField>; COLUMNS],
189 runtime_tables: &[RuntimeTable<G::ScalarField>],
190 index: &ProverIndex<FULL_ROUNDS, G, OpeningProof::SRS>,
191 prev_challenges: Vec<RecursionChallenge<G>>,
192 blinders: Option<[Option<PolyComm<G::ScalarField>>; COLUMNS]>,
193 rng: &mut RNG,
194 ) -> Result<Self>
195 where
196 EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField, FULL_ROUNDS>,
197 EFrSponge: FrSponge<G::ScalarField>,
198 EFrSponge: From<&'static ArithmeticSpongeParams<G::ScalarField, FULL_ROUNDS>>,
199 RNG: RngCore + CryptoRng,
200 VerifierIndex<FULL_ROUNDS, G, OpeningProof::SRS>: Clone,
201 {
202 internal_tracing::checkpoint!(internal_traces; create_recursive);
203 let d1_size = index.cs.domain.d1.size();
204
205 let (_, endo_r) = G::endos();
206
207 let num_chunks = if d1_size < index.max_poly_size {
208 1
209 } else {
210 d1_size / index.max_poly_size
211 };
212
213 if cfg!(debug_assertions) && !index.cs.disable_gates_checks {
216 let public = witness[0][0..index.cs.public].to_vec();
217 index.verify(&witness, &public).expect("incorrect witness");
218 }
219
220 let length_witness = witness[0].len();
226 let length_padding = d1_size
227 .checked_sub(length_witness)
228 .ok_or(ProverError::NoRoomForZkInWitness)?;
229
230 let zero_knowledge_limit = zk_rows_strict_lower_bound(num_chunks);
231 if (index.cs.zk_rows as usize) <= zero_knowledge_limit {
240 return Err(ProverError::NotZeroKnowledge(
241 zero_knowledge_limit + 1,
242 index.cs.zk_rows as usize,
243 ));
244 }
245
246 if length_padding < index.cs.zk_rows as usize {
247 return Err(ProverError::NoRoomForZkInWitness);
248 }
249
250 internal_tracing::checkpoint!(internal_traces; pad_witness);
253 for w in &mut witness {
254 if w.len() != length_witness {
255 return Err(ProverError::WitnessCsInconsistent);
256 }
257
258 w.extend(std::iter::repeat_n(G::ScalarField::zero(), length_padding));
260
261 for row in w.iter_mut().rev().take(index.cs.zk_rows as usize) {
263 *row = <G::ScalarField as UniformRand>::rand(rng);
264 }
265 }
266
267 internal_tracing::checkpoint!(internal_traces; set_up_fq_sponge);
269 let mut fq_sponge = EFqSponge::new(G::other_curve_sponge_params());
270
271 let verifier_index_digest = index.verifier_index_digest::<EFqSponge>();
273 fq_sponge.absorb_fq(&[verifier_index_digest]);
274
275 for RecursionChallenge { comm, .. } in &prev_challenges {
277 absorb_commitment(&mut fq_sponge, comm)
278 }
279
280 let public = witness[0][0..index.cs.public].to_vec();
284 let public_poly = -Evaluations::<G::ScalarField, D<G::ScalarField>>::from_vec_and_domain(
285 public,
286 index.cs.domain.d1,
287 )
288 .interpolate();
289
290 let public_comm = index.srs.commit_non_hiding(&public_poly, num_chunks);
292 let public_comm = {
293 index
294 .srs
295 .mask_custom(
296 public_comm.clone(),
297 &public_comm.map(|_| G::ScalarField::one()),
298 )
299 .unwrap()
300 .commitment
301 };
302
303 absorb_commitment(&mut fq_sponge, &public_comm);
309
310 internal_tracing::checkpoint!(internal_traces; commit_to_witness_columns);
315 let blinders_final: Vec<PolyComm<G::ScalarField>> = match blinders {
317 None => (0..COLUMNS)
318 .map(|_| blinder::<G::ScalarField, _>(num_chunks, rng))
319 .collect(),
320 Some(blinders_arr) => blinders_arr
321 .into_iter()
322 .map(|blinder_el| match blinder_el {
323 None => blinder::<G::ScalarField, _>(num_chunks, rng),
324 Some(blinder_el_some) => blinder_el_some,
325 })
326 .collect(),
327 };
328 let w_comm_opt_res: Vec<Result<_>> = witness
329 .clone()
330 .into_par_iter()
331 .zip(blinders_final.into_par_iter())
332 .map(|(witness, blinder)| {
333 let witness_eval =
334 Evaluations::<G::ScalarField, D<G::ScalarField>>::from_vec_and_domain(
335 witness,
336 index.cs.domain.d1,
337 );
338
339 let witness_com = index
341 .srs
342 .commit_evaluations_non_hiding(index.cs.domain.d1, &witness_eval);
343 let com = index
344 .srs
345 .mask_custom(witness_com, &blinder)
346 .map_err(ProverError::WrongBlinders)?;
347
348 Ok(com)
349 })
350 .collect();
351
352 let w_comm_res: Result<Vec<BlindedCommitment<G>>> = w_comm_opt_res.into_iter().collect();
353
354 let w_comm = w_comm_res?;
355
356 let w_comm: [BlindedCommitment<G>; COLUMNS] = w_comm
357 .try_into()
358 .expect("previous loop is of the correct length");
359
360 w_comm
362 .iter()
363 .for_each(|c| absorb_commitment(&mut fq_sponge, &c.commitment));
364
365 let witness_poly: [DensePolynomial<G::ScalarField>; COLUMNS] = (0..COLUMNS)
370 .into_par_iter()
371 .map(|i| {
372 Evaluations::<G::ScalarField, D<G::ScalarField>>::from_vec_and_domain(
373 witness[i].clone(),
374 index.cs.domain.d1,
375 )
376 .interpolate()
377 })
378 .collect::<Vec<_>>()
379 .try_into()
380 .unwrap();
381
382 let mut lookup_context = LookupContext::default();
383
384 let lookup_constraint_system = index
386 .cs
387 .lookup_constraint_system
388 .try_get_or_err()
389 .map_err(ProverError::from)?;
390 if let Some(lcs) = lookup_constraint_system {
391 internal_tracing::checkpoint!(internal_traces; use_lookup, {
392 "uses_lookup": true,
393 "uses_runtime_tables": lcs.runtime_tables.is_some(),
394 });
395 let runtime_second_col_d8 = if let Some(cfg_runtime_tables) = &lcs.runtime_tables {
397 let expected_runtime: Vec<_> = cfg_runtime_tables
400 .iter()
401 .map(|rt| (rt.id, rt.len))
402 .collect();
403 let runtime: Vec<_> = runtime_tables
404 .iter()
405 .map(|rt| (rt.id, rt.data.len()))
406 .collect();
407 if expected_runtime != runtime {
408 return Err(ProverError::RuntimeTablesInconsistent);
409 }
410
411 let (runtime_table_contribution, runtime_table_contribution_d8) = {
414 let mut offset = lcs
415 .runtime_table_offset
416 .expect("runtime configuration missing offset");
417
418 let mut evals = vec![G::ScalarField::zero(); d1_size];
419 for rt in runtime_tables {
420 let range = offset..(offset + rt.data.len());
421 evals[range].copy_from_slice(&rt.data);
422 offset += rt.data.len();
423 }
424
425 for e in evals.iter_mut().rev().take(index.cs.zk_rows as usize) {
427 *e = <G::ScalarField as UniformRand>::rand(rng);
428 }
429
430 let runtime_table_contribution =
432 Evaluations::from_vec_and_domain(evals, index.cs.domain.d1).interpolate();
433
434 let runtime_table_contribution_d8 =
435 runtime_table_contribution.evaluate_over_domain_by_ref(index.cs.domain.d8);
436
437 (runtime_table_contribution, runtime_table_contribution_d8)
438 };
439
440 let runtime_table_comm =
443 index
444 .srs
445 .commit(&runtime_table_contribution, num_chunks, rng);
446
447 absorb_commitment(&mut fq_sponge, &runtime_table_comm.commitment);
449
450 let mut second_column_d8 = runtime_table_contribution_d8.clone();
452 second_column_d8
453 .evals
454 .par_iter_mut()
455 .enumerate()
456 .for_each(|(row, e)| {
457 *e += lcs.lookup_table8[1][row];
458 });
459
460 lookup_context.runtime_table = Some(runtime_table_contribution);
461 lookup_context.runtime_table_d8 = Some(runtime_table_contribution_d8);
462 lookup_context.runtime_table_comm = Some(runtime_table_comm);
463 Some(second_column_d8)
464 } else {
465 None
466 };
467
468 let joint_combiner = if lcs.configuration.lookup_info.features.joint_lookup_used {
472 fq_sponge.challenge()
473 } else {
474 G::ScalarField::zero()
475 };
476
477 let joint_combiner: G::ScalarField =
479 ScalarChallenge::new(joint_combiner).to_field(endo_r);
480
481 let table_id_combiner: G::ScalarField = if lcs.table_ids8.as_ref().is_some() {
485 joint_combiner.pow([lcs.configuration.lookup_info.max_joint_size as u64])
486 } else {
487 G::ScalarField::zero()
489 };
490 lookup_context.table_id_combiner = Some(table_id_combiner);
491
492 let dummy_lookup_value = lcs
495 .configuration
496 .dummy_lookup
497 .evaluate(&joint_combiner, &table_id_combiner);
498 lookup_context.dummy_lookup_value = Some(dummy_lookup_value);
499
500 let joint_lookup_table_d8 = {
502 let evals = (0..(d1_size * 8))
505 .into_par_iter()
506 .map(|idx| {
507 let table_id = match lcs.table_ids8.as_ref() {
508 Some(table_ids8) => table_ids8.evals[idx],
509 None =>
510 {
513 G::ScalarField::zero()
514 }
515 };
516
517 if !lcs.configuration.lookup_info.features.uses_runtime_tables {
518 let table_row = lcs.lookup_table8.iter().map(|e| &e.evals[idx]);
519
520 combine_table_entry(
521 &joint_combiner,
522 &table_id_combiner,
523 table_row,
524 &table_id,
525 )
526 } else {
527 let second_col = runtime_second_col_d8.as_ref().unwrap();
529
530 let table_row = lcs.lookup_table8.iter().enumerate().map(|(col, e)| {
531 if col == 1 {
532 &second_col.evals[idx]
533 } else {
534 &e.evals[idx]
535 }
536 });
537
538 combine_table_entry(
539 &joint_combiner,
540 &table_id_combiner,
541 table_row,
542 &table_id,
543 )
544 }
545 })
546 .collect();
547
548 Evaluations::from_vec_and_domain(evals, index.cs.domain.d8)
549 };
550
551 let joint_lookup_table = {
562 let d1_evals: Vec<G::ScalarField> = joint_lookup_table_d8
563 .evals
564 .iter()
565 .step_by(8)
566 .copied()
567 .collect();
568 Evaluations::from_vec_and_domain(d1_evals, index.cs.domain.d1).interpolate()
569 };
570
571 let sorted: Vec<_> = lookup::constraints::sorted(
576 dummy_lookup_value,
577 &joint_lookup_table_d8,
578 index.cs.domain.d1,
579 &index.cs.gates,
580 &witness,
581 joint_combiner,
582 table_id_combiner,
583 &lcs.configuration.lookup_info,
584 index.cs.zk_rows as usize,
585 )?;
586
587 let sorted: Vec<_> = sorted
590 .into_iter()
591 .map(|chunk| {
592 lookup::constraints::zk_patch(
593 chunk,
594 index.cs.domain.d1,
595 index.cs.zk_rows as usize,
596 rng,
597 )
598 })
599 .collect();
600
601 let sorted_comms: Vec<_> = sorted
603 .iter()
604 .map(|v| index.srs.commit_evaluations(index.cs.domain.d1, v, rng))
605 .collect();
606
607 sorted_comms
609 .iter()
610 .for_each(|c| absorb_commitment(&mut fq_sponge, &c.commitment));
611
612 let sorted_coeffs: Vec<_> =
615 sorted.par_iter().map(|e| e.clone().interpolate()).collect();
616 let sorted8: Vec<_> = sorted_coeffs
617 .par_iter()
618 .map(|v| v.evaluate_over_domain_by_ref(index.cs.domain.d8))
619 .collect();
620
621 lookup_context.joint_combiner = Some(joint_combiner);
622 lookup_context.sorted = Some(sorted);
623 lookup_context.sorted_coeffs = Some(sorted_coeffs);
624 lookup_context.sorted_comms = Some(sorted_comms);
625 lookup_context.sorted8 = Some(sorted8);
626 lookup_context.joint_lookup_table_d8 = Some(joint_lookup_table_d8);
627 lookup_context.joint_lookup_table = Some(joint_lookup_table);
628 }
629
630 let beta = fq_sponge.challenge();
632
633 let gamma = fq_sponge.challenge();
635
636 if let Some(lcs) = lookup_constraint_system {
638 let joint_lookup_table_d8 = lookup_context.joint_lookup_table_d8.as_ref().unwrap();
640 let sorted = lookup_context.sorted.take().unwrap();
641
642 let aggreg = lookup::constraints::aggregation::<_, G::ScalarField>(
643 lookup_context.dummy_lookup_value.unwrap(),
644 joint_lookup_table_d8,
645 index.cs.domain.d1,
646 &index.cs.gates,
647 &witness,
648 &lookup_context.joint_combiner.unwrap(),
649 &lookup_context.table_id_combiner.unwrap(),
650 beta,
651 gamma,
652 &sorted,
653 rng,
654 &lcs.configuration.lookup_info,
655 index.cs.zk_rows as usize,
656 )?;
657
658 let aggreg_comm = index
660 .srs
661 .commit_evaluations(index.cs.domain.d1, &aggreg, rng);
662
663 absorb_commitment(&mut fq_sponge, &aggreg_comm.commitment);
665
666 let aggreg_coeffs = aggreg.interpolate();
668 let aggreg8 = aggreg_coeffs.evaluate_over_domain_by_ref(index.cs.domain.d8);
671
672 lookup_context.aggreg_comm = Some(aggreg_comm);
673 lookup_context.aggreg_coeffs = Some(aggreg_coeffs);
674 lookup_context.aggreg8 = Some(aggreg8);
675 }
676
677 let column_evaluations = index.column_evaluations.get();
678
679 internal_tracing::checkpoint!(internal_traces; z_permutation_aggregation_polynomial);
681 let z_poly = index.perm_aggreg(&witness, &beta, &gamma, rng)?;
682 drop(witness);
683
684 let z_comm = index.srs.commit(&z_poly, num_chunks, rng);
686
687 absorb_commitment(&mut fq_sponge, &z_comm.commitment);
689
690 let alpha_chal = ScalarChallenge::new(fq_sponge.challenge());
692
693 let alpha: G::ScalarField = alpha_chal.to_field(endo_r);
695
696 let mut all_alphas = index.powers_of_alpha.clone();
698 all_alphas.instantiate(alpha);
699
700 let (t_comm, zeta, zeta_omega, chunked_evals, ft, blinding_ft) = {
709 let joint_lookup_table_d8 = lookup_context.joint_lookup_table_d8.take();
710 let sorted8 = lookup_context.sorted8.take();
711 let aggreg8 = lookup_context.aggreg8.take();
712 let runtime_table_d8 = lookup_context.runtime_table_d8.take();
713
714 let lookup_env = if let Some(lcs) = lookup_constraint_system {
715 Some(LookupEnvironment {
716 aggreg: aggreg8.as_ref().unwrap(),
717 sorted: sorted8.as_ref().unwrap(),
718 selectors: &lcs.lookup_selectors,
719 table: joint_lookup_table_d8.as_ref().unwrap(),
720 runtime_selector: lcs.runtime_selector.as_ref(),
721 runtime_table: runtime_table_d8.as_ref(),
722 })
723 } else {
724 None
725 };
726
727 internal_tracing::checkpoint!(internal_traces; eval_witness_polynomials_over_domains);
728 let lagrange = index.cs.evaluate(&witness_poly, &z_poly);
729 internal_tracing::checkpoint!(internal_traces; compute_index_evals);
730 let env = {
731 let mut index_evals = HashMap::new();
732 use GateType::*;
733 index_evals.insert(Generic, &column_evaluations.generic_selector4);
734 index_evals.insert(Poseidon, &column_evaluations.poseidon_selector8);
735 index_evals.insert(CompleteAdd, &column_evaluations.complete_add_selector4);
736 index_evals.insert(VarBaseMul, &column_evaluations.mul_selector8);
737 index_evals.insert(EndoMul, &column_evaluations.emul_selector8);
738 index_evals.insert(EndoMulScalar, &column_evaluations.endomul_scalar_selector8);
739
740 if let Some(selector) = &column_evaluations.range_check0_selector8 {
741 index_evals.insert(GateType::RangeCheck0, selector);
742 }
743
744 if let Some(selector) = &column_evaluations.range_check1_selector8 {
745 index_evals.insert(GateType::RangeCheck1, selector);
746 }
747
748 if let Some(selector) = &column_evaluations.foreign_field_add_selector8 {
749 index_evals.insert(GateType::ForeignFieldAdd, selector);
750 }
751
752 if let Some(selector) = &column_evaluations.foreign_field_mul_selector8 {
753 index_evals.extend(
754 foreign_field_mul::gadget::circuit_gates()
755 .iter()
756 .map(|gate_type| (*gate_type, selector)),
757 );
758 }
759
760 if let Some(selector) = &column_evaluations.xor_selector8 {
761 index_evals.insert(GateType::Xor16, selector);
762 }
763
764 if let Some(selector) = &column_evaluations.rot_selector8 {
765 index_evals.insert(GateType::Rot64, selector);
766 }
767
768 let mds = &G::sponge_params().mds;
769 Environment {
770 constants: Constants {
771 endo_coefficient: index.cs.endo,
772 mds,
773 zk_rows: index.cs.zk_rows,
774 },
775 challenges: BerkeleyChallenges {
776 alpha,
777 beta,
778 gamma,
779 joint_combiner: lookup_context
780 .joint_combiner
781 .unwrap_or(G::ScalarField::zero()),
782 },
783 witness: &lagrange.this.w,
784 coefficient: &column_evaluations.coefficients8,
785 vanishes_on_zero_knowledge_and_previous_rows: &index
786 .cs
787 .precomputations()
788 .vanishes_on_zero_knowledge_and_previous_rows,
789 z: &lagrange.this.z,
790 l0_1: l0_1(index.cs.domain.d1),
791 domain: index.cs.domain,
792 index: index_evals,
793 lookup: lookup_env,
794 }
795 };
796
797 let mut cache = expr::Cache::default();
798
799 internal_tracing::checkpoint!(internal_traces; compute_quotient_poly);
800
801 let quotient_poly = {
802 let mut t4 = {
804 let generic_constraint =
805 generic::Generic::combined_constraints(&all_alphas, &mut cache);
806 let generic4 = generic_constraint.evaluations(&env);
807
808 if cfg!(debug_assertions) {
809 let p4 = public_poly.evaluate_over_domain_by_ref(index.cs.domain.d4);
810 let gen_minus_pub = &generic4 + &p4;
811
812 check_constraint!(index, gen_minus_pub);
813 }
814
815 generic4
816 };
817
818 let (mut t8, bnd) = {
820 let alphas =
821 all_alphas.get_alphas(ArgumentType::Permutation, permutation::CONSTRAINTS);
822 let (perm, bnd) = index.perm_quot(&lagrange, beta, gamma, &z_poly, alphas)?;
823
824 check_constraint!(index, perm);
825
826 (perm, bnd)
827 };
828
829 {
830 use crate::circuits::argument::DynArgument;
831
832 let range_check0_enabled = column_evaluations.range_check0_selector8.is_some();
833 let range_check1_enabled = column_evaluations.range_check1_selector8.is_some();
834 let foreign_field_addition_enabled =
835 column_evaluations.foreign_field_add_selector8.is_some();
836 let foreign_field_multiplication_enabled =
837 column_evaluations.foreign_field_mul_selector8.is_some();
838 let xor_enabled = column_evaluations.xor_selector8.is_some();
839 let rot_enabled = column_evaluations.rot_selector8.is_some();
840
841 for gate in [
842 (
843 (&CompleteAdd::default() as &dyn DynArgument<G::ScalarField>),
844 true,
845 ),
846 (&VarbaseMul::default(), true),
847 (&EndosclMul::default(), true),
848 (&EndomulScalar::default(), true),
849 (&Poseidon::default(), true),
850 (&RangeCheck0::default(), range_check0_enabled),
852 (&RangeCheck1::default(), range_check1_enabled),
853 (&ForeignFieldAdd::default(), foreign_field_addition_enabled),
855 (
857 &ForeignFieldMul::default(),
858 foreign_field_multiplication_enabled,
859 ),
860 (&Xor16::default(), xor_enabled),
862 (&Rot64::default(), rot_enabled),
864 ]
865 .into_iter()
866 .filter_map(|(gate, is_enabled)| if is_enabled { Some(gate) } else { None })
867 {
868 let constraint = gate.combined_constraints(&all_alphas, &mut cache);
869 let eval = constraint.evaluations(&env);
870 if eval.domain().size == t4.domain().size {
871 t4 += &eval;
872 } else if eval.domain().size == t8.domain().size {
873 t8 += &eval;
874 } else {
875 panic!("Bad evaluation")
876 }
877 check_constraint!(index, format!("{:?}", gate.argument_type()), eval);
878 }
879 };
880
881 {
883 if let Some(lcs) = lookup_constraint_system {
884 let constraints =
885 lookup::constraints::constraints(&lcs.configuration, false);
886 let constraints_len = u32::try_from(constraints.len())
887 .expect("not expecting a large amount of constraints");
888 let lookup_alphas =
889 all_alphas.get_alphas(ArgumentType::Lookup, constraints_len);
890
891 for (ii, (constraint, alpha_pow)) in
894 constraints.into_iter().zip_eq(lookup_alphas).enumerate()
895 {
896 let mut eval = constraint.evaluations(&env);
897 eval.evals.par_iter_mut().for_each(|x| *x *= alpha_pow);
898
899 if eval.domain().size == t4.domain().size {
900 t4 += &eval;
901 } else if eval.domain().size == t8.domain().size {
902 t8 += &eval;
903 } else if eval.evals.iter().all(|x| x.is_zero()) {
904 } else {
906 panic!("Bad evaluation")
907 }
908
909 check_constraint!(index, format!("lookup constraint #{ii}"), eval);
910 }
911 }
912 }
913
914 let mut f = t4.interpolate() + t8.interpolate();
916 f += &public_poly;
917
918 let (mut quotient, res) = f.divide_by_vanishing_poly(index.cs.domain.d1);
920 if !res.is_zero() {
921 return Err(ProverError::Prover(
922 "rest of division by vanishing polynomial",
923 ));
924 }
925
926 quotient += &bnd; quotient
928 };
929
930 let t_comm = { index.srs.commit("ient_poly, 7 * num_chunks, rng) };
932
933 absorb_commitment(&mut fq_sponge, &t_comm.commitment);
935
936 let zeta_chal = ScalarChallenge::new(fq_sponge.challenge());
938
939 let zeta = zeta_chal.to_field(endo_r);
941
942 let omega = index.cs.domain.d1.group_gen;
943 let zeta_omega = zeta * omega;
944
945 if lookup_constraint_system.is_some() {
947 let aggreg = lookup_context
949 .aggreg_coeffs
950 .as_ref()
951 .unwrap()
952 .to_chunked_polynomial(num_chunks, index.max_poly_size);
953
954 let sorted = lookup_context
956 .sorted_coeffs
957 .as_ref()
958 .unwrap()
959 .iter()
960 .map(|c| c.to_chunked_polynomial(num_chunks, index.max_poly_size))
961 .collect::<Vec<_>>();
962
963 let joint_table = lookup_context.joint_lookup_table.as_ref().unwrap();
965 let joint_table =
966 joint_table.to_chunked_polynomial(num_chunks, index.max_poly_size);
967
968 lookup_context.lookup_aggregation_eval = Some(PointEvaluations {
969 zeta: aggreg.evaluate_chunks(zeta),
970 zeta_omega: aggreg.evaluate_chunks(zeta_omega),
971 });
972 lookup_context.lookup_table_eval = Some(PointEvaluations {
973 zeta: joint_table.evaluate_chunks(zeta),
974 zeta_omega: joint_table.evaluate_chunks(zeta_omega),
975 });
976 lookup_context.lookup_sorted_eval = array::from_fn(|i| {
977 if i < sorted.len() {
978 let sorted = &sorted[i];
979 Some(PointEvaluations {
980 zeta: sorted.evaluate_chunks(zeta),
981 zeta_omega: sorted.evaluate_chunks(zeta_omega),
982 })
983 } else {
984 None
985 }
986 });
987 lookup_context.runtime_lookup_table_eval =
988 lookup_context.runtime_table.as_ref().map(|runtime_table| {
989 let runtime_table =
990 runtime_table.to_chunked_polynomial(num_chunks, index.max_poly_size);
991 PointEvaluations {
992 zeta: runtime_table.evaluate_chunks(zeta),
993 zeta_omega: runtime_table.evaluate_chunks(zeta_omega),
994 }
995 });
996 }
997
998 internal_tracing::checkpoint!(internal_traces; lagrange_basis_eval_zeta_poly);
1018 let zeta_evals =
1019 LagrangeBasisEvaluations::new(index.max_poly_size, index.cs.domain.d1, zeta);
1020 internal_tracing::checkpoint!(internal_traces; lagrange_basis_eval_zeta_omega_poly);
1021 let zeta_omega_evals =
1022 LagrangeBasisEvaluations::new(index.max_poly_size, index.cs.domain.d1, zeta_omega);
1023
1024 let chunked_evals_for_selector =
1025 |p: &Evaluations<G::ScalarField, D<G::ScalarField>>| PointEvaluations {
1026 zeta: zeta_evals.evaluate_boolean(p),
1027 zeta_omega: zeta_omega_evals.evaluate_boolean(p),
1028 };
1029
1030 let chunked_evals_for_evaluations =
1031 |p: &Evaluations<G::ScalarField, D<G::ScalarField>>| PointEvaluations {
1032 zeta: zeta_evals.evaluate(p),
1033 zeta_omega: zeta_omega_evals.evaluate(p),
1034 };
1035
1036 internal_tracing::checkpoint!(internal_traces; chunk_eval_zeta_omega_poly);
1037 let chunked_evals = ProofEvaluations::<PointEvaluations<Vec<G::ScalarField>>> {
1038 public: {
1039 let chunked =
1040 public_poly.to_chunked_polynomial(num_chunks, index.max_poly_size);
1041 Some(PointEvaluations {
1042 zeta: chunked.evaluate_chunks(zeta),
1043 zeta_omega: chunked.evaluate_chunks(zeta_omega),
1044 })
1045 },
1046 s: array::from_fn(|i| {
1047 chunked_evals_for_evaluations(&column_evaluations.permutation_coefficients8[i])
1048 }),
1049 coefficients: array::from_fn(|i| {
1050 chunked_evals_for_evaluations(&column_evaluations.coefficients8[i])
1051 }),
1052 w: array::from_fn(|i| {
1053 let chunked =
1054 witness_poly[i].to_chunked_polynomial(num_chunks, index.max_poly_size);
1055 PointEvaluations {
1056 zeta: chunked.evaluate_chunks(zeta),
1057 zeta_omega: chunked.evaluate_chunks(zeta_omega),
1058 }
1059 }),
1060
1061 z: {
1062 let chunked = z_poly.to_chunked_polynomial(num_chunks, index.max_poly_size);
1063 PointEvaluations {
1064 zeta: chunked.evaluate_chunks(zeta),
1065 zeta_omega: chunked.evaluate_chunks(zeta_omega),
1066 }
1067 },
1068
1069 lookup_aggregation: lookup_context.lookup_aggregation_eval.take(),
1070 lookup_table: lookup_context.lookup_table_eval.take(),
1071 lookup_sorted: array::from_fn(|i| lookup_context.lookup_sorted_eval[i].take()),
1072 runtime_lookup_table: lookup_context.runtime_lookup_table_eval.take(),
1073 generic_selector: chunked_evals_for_selector(&column_evaluations.generic_selector4),
1074 poseidon_selector: chunked_evals_for_selector(
1075 &column_evaluations.poseidon_selector8,
1076 ),
1077 complete_add_selector: chunked_evals_for_selector(
1078 &column_evaluations.complete_add_selector4,
1079 ),
1080 mul_selector: chunked_evals_for_selector(&column_evaluations.mul_selector8),
1081 emul_selector: chunked_evals_for_selector(&column_evaluations.emul_selector8),
1082 endomul_scalar_selector: chunked_evals_for_selector(
1083 &column_evaluations.endomul_scalar_selector8,
1084 ),
1085
1086 range_check0_selector: column_evaluations
1087 .range_check0_selector8
1088 .as_ref()
1089 .map(chunked_evals_for_selector),
1090 range_check1_selector: column_evaluations
1091 .range_check1_selector8
1092 .as_ref()
1093 .map(chunked_evals_for_selector),
1094 foreign_field_add_selector: column_evaluations
1095 .foreign_field_add_selector8
1096 .as_ref()
1097 .map(chunked_evals_for_selector),
1098 foreign_field_mul_selector: column_evaluations
1099 .foreign_field_mul_selector8
1100 .as_ref()
1101 .map(chunked_evals_for_selector),
1102 xor_selector: column_evaluations
1103 .xor_selector8
1104 .as_ref()
1105 .map(chunked_evals_for_selector),
1106 rot_selector: column_evaluations
1107 .rot_selector8
1108 .as_ref()
1109 .map(chunked_evals_for_selector),
1110
1111 runtime_lookup_table_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1112 lcs.runtime_selector
1113 .as_ref()
1114 .map(chunked_evals_for_selector)
1115 }),
1116 xor_lookup_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1117 lcs.lookup_selectors
1118 .xor
1119 .as_ref()
1120 .map(chunked_evals_for_selector)
1121 }),
1122 lookup_gate_lookup_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1123 lcs.lookup_selectors
1124 .lookup
1125 .as_ref()
1126 .map(chunked_evals_for_selector)
1127 }),
1128 range_check_lookup_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1129 lcs.lookup_selectors
1130 .range_check
1131 .as_ref()
1132 .map(chunked_evals_for_selector)
1133 }),
1134 foreign_field_mul_lookup_selector: lookup_constraint_system.as_ref().and_then(
1135 |lcs| {
1136 lcs.lookup_selectors
1137 .ffmul
1138 .as_ref()
1139 .map(chunked_evals_for_selector)
1140 },
1141 ),
1142 };
1143
1144 let zeta_to_srs_len = zeta.pow([index.max_poly_size as u64]);
1145 let zeta_omega_to_srs_len = zeta_omega.pow([index.max_poly_size as u64]);
1146 let zeta_to_domain_size = zeta.pow([d1_size as u64]);
1147
1148 let evals: ProofEvaluations<PointEvaluations<G::ScalarField>> = {
1151 let powers_of_eval_points_for_chunks = PointEvaluations {
1152 zeta: zeta_to_srs_len,
1153 zeta_omega: zeta_omega_to_srs_len,
1154 };
1155 chunked_evals.combine(&powers_of_eval_points_for_chunks)
1156 };
1157
1158 internal_tracing::checkpoint!(internal_traces; compute_ft_poly);
1161 let ft: DensePolynomial<G::ScalarField> = {
1162 let f_chunked = {
1163 let alphas =
1169 all_alphas.get_alphas(ArgumentType::Permutation, permutation::CONSTRAINTS);
1170 let f = index.perm_lnrz(&evals, zeta, beta, gamma, alphas);
1171
1172 let f = {
1174 let (_lin_constant, mut lin) =
1175 index.linearization.to_polynomial(&env, zeta, &evals);
1176 lin += &f;
1177 lin.interpolate()
1178 };
1179
1180 f.to_chunked_polynomial(num_chunks, index.max_poly_size)
1182 .linearize(zeta_to_srs_len)
1183 };
1184
1185 let t_chunked = quotient_poly
1186 .to_chunked_polynomial(7 * num_chunks, index.max_poly_size)
1187 .linearize(zeta_to_srs_len);
1188
1189 &f_chunked - &t_chunked.scale(zeta_to_domain_size - G::ScalarField::one())
1190 };
1191
1192 let blinding_ft = {
1195 let blinding_t = t_comm.blinders.chunk_blinding(zeta_to_srs_len);
1196 let blinding_f = G::ScalarField::zero();
1197
1198 PolyComm {
1199 chunks: vec![
1201 blinding_f - (zeta_to_domain_size - G::ScalarField::one()) * blinding_t,
1202 ],
1203 }
1204 };
1205
1206 (t_comm, zeta, zeta_omega, chunked_evals, ft, blinding_ft)
1207 };
1208
1209 internal_tracing::checkpoint!(internal_traces; ft_eval_zeta_omega);
1211 let ft_eval1 = ft.evaluate(&zeta_omega);
1212
1213 let fq_sponge_before_evaluations = fq_sponge.clone();
1215 let mut fr_sponge = EFrSponge::from(G::sponge_params());
1216
1217 fr_sponge.absorb(&fq_sponge.digest());
1219
1220 let prev_challenge_digest = {
1222 let mut fr_sponge = EFrSponge::from(G::sponge_params());
1225 for RecursionChallenge { chals, .. } in &prev_challenges {
1226 fr_sponge.absorb_multiple(chals);
1227 }
1228 fr_sponge.digest()
1229 };
1230 fr_sponge.absorb(&prev_challenge_digest);
1231
1232 internal_tracing::checkpoint!(internal_traces; build_polynomials);
1234 let polys = prev_challenges
1235 .iter()
1236 .map(|RecursionChallenge { chals, comm }| {
1237 (
1238 DensePolynomial::from_coefficients_vec(b_poly_coefficients(chals)),
1239 comm.len(),
1240 )
1241 })
1242 .collect::<Vec<_>>();
1243
1244 fr_sponge.absorb(&ft_eval1);
1246
1247 fr_sponge.absorb_multiple(&chunked_evals.public.as_ref().unwrap().zeta);
1255 fr_sponge.absorb_multiple(&chunked_evals.public.as_ref().unwrap().zeta_omega);
1256 fr_sponge.absorb_evaluations(&chunked_evals);
1257
1258 let v_chal = fr_sponge.challenge();
1260
1261 let v = v_chal.to_field(endo_r);
1263
1264 let u_chal = fr_sponge.challenge();
1266
1267 let u = u_chal.to_field(endo_r);
1269
1270 let non_hiding = |n_chunks: usize| PolyComm {
1274 chunks: vec![G::ScalarField::zero(); n_chunks],
1275 };
1276
1277 let fixed_hiding = |n_chunks: usize| PolyComm {
1278 chunks: vec![G::ScalarField::one(); n_chunks],
1279 };
1280
1281 let coefficients_form = DensePolynomialOrEvaluations::DensePolynomial;
1282 let evaluations_form = |e| DensePolynomialOrEvaluations::Evaluations(e, index.cs.domain.d1);
1283
1284 let mut polynomials = polys
1285 .iter()
1286 .map(|(p, n_chunks)| (coefficients_form(p), non_hiding(*n_chunks)))
1287 .collect::<Vec<_>>();
1288
1289 polynomials.push((coefficients_form(&public_poly), fixed_hiding(num_chunks)));
1298 polynomials.push((coefficients_form(&ft), blinding_ft));
1299 polynomials.push((coefficients_form(&z_poly), z_comm.blinders));
1300 polynomials.push((
1301 evaluations_form(&column_evaluations.generic_selector4),
1302 fixed_hiding(num_chunks),
1303 ));
1304 polynomials.push((
1305 evaluations_form(&column_evaluations.poseidon_selector8),
1306 fixed_hiding(num_chunks),
1307 ));
1308 polynomials.push((
1309 evaluations_form(&column_evaluations.complete_add_selector4),
1310 fixed_hiding(num_chunks),
1311 ));
1312 polynomials.push((
1313 evaluations_form(&column_evaluations.mul_selector8),
1314 fixed_hiding(num_chunks),
1315 ));
1316 polynomials.push((
1317 evaluations_form(&column_evaluations.emul_selector8),
1318 fixed_hiding(num_chunks),
1319 ));
1320 polynomials.push((
1321 evaluations_form(&column_evaluations.endomul_scalar_selector8),
1322 fixed_hiding(num_chunks),
1323 ));
1324 polynomials.extend(
1325 witness_poly
1326 .iter()
1327 .zip(w_comm.iter())
1328 .map(|(w, c)| (coefficients_form(w), c.blinders.clone()))
1329 .collect::<Vec<_>>(),
1330 );
1331 polynomials.extend(
1332 column_evaluations
1333 .coefficients8
1334 .iter()
1335 .map(|coefficientm| (evaluations_form(coefficientm), non_hiding(num_chunks)))
1336 .collect::<Vec<_>>(),
1337 );
1338 polynomials.extend(
1339 column_evaluations.permutation_coefficients8[0..PERMUTS - 1]
1340 .iter()
1341 .map(|w| (evaluations_form(w), non_hiding(num_chunks)))
1342 .collect::<Vec<_>>(),
1343 );
1344
1345 if let Some(range_check0_selector8) = &column_evaluations.range_check0_selector8 {
1347 polynomials.push((
1348 evaluations_form(range_check0_selector8),
1349 non_hiding(num_chunks),
1350 ));
1351 }
1352 if let Some(range_check1_selector8) = &column_evaluations.range_check1_selector8 {
1353 polynomials.push((
1354 evaluations_form(range_check1_selector8),
1355 non_hiding(num_chunks),
1356 ));
1357 }
1358 if let Some(foreign_field_add_selector8) = &column_evaluations.foreign_field_add_selector8 {
1359 polynomials.push((
1360 evaluations_form(foreign_field_add_selector8),
1361 non_hiding(num_chunks),
1362 ));
1363 }
1364 if let Some(foreign_field_mul_selector8) = &column_evaluations.foreign_field_mul_selector8 {
1365 polynomials.push((
1366 evaluations_form(foreign_field_mul_selector8),
1367 non_hiding(num_chunks),
1368 ));
1369 }
1370 if let Some(xor_selector8) = &column_evaluations.xor_selector8 {
1371 polynomials.push((evaluations_form(xor_selector8), non_hiding(num_chunks)));
1372 }
1373 if let Some(rot_selector8) = &column_evaluations.rot_selector8 {
1374 polynomials.push((evaluations_form(rot_selector8), non_hiding(num_chunks)));
1375 }
1376
1377 if let Some(lcs) = lookup_constraint_system {
1380 let sorted_poly = lookup_context.sorted_coeffs.as_ref().unwrap();
1382 let sorted_comms = lookup_context.sorted_comms.as_ref().unwrap();
1383
1384 for (poly, comm) in sorted_poly.iter().zip(sorted_comms) {
1385 polynomials.push((coefficients_form(poly), comm.blinders.clone()));
1386 }
1387
1388 let aggreg_poly = lookup_context.aggreg_coeffs.as_ref().unwrap();
1390 let aggreg_comm = lookup_context.aggreg_comm.as_ref().unwrap();
1391 polynomials.push((coefficients_form(aggreg_poly), aggreg_comm.blinders.clone()));
1392
1393 let table_blinding = {
1395 let joint_combiner = lookup_context.joint_combiner.as_ref().unwrap();
1396 let table_id_combiner = lookup_context.table_id_combiner.as_ref().unwrap();
1397 let max_fixed_lookup_table_size = {
1398 lcs.lookup_table8.len()
1406 };
1407 let base_blinding = {
1408 let fixed_table_blinding = if max_fixed_lookup_table_size == 0 {
1409 G::ScalarField::zero()
1410 } else {
1411 (1..max_fixed_lookup_table_size).fold(G::ScalarField::one(), |acc, _| {
1412 G::ScalarField::one() + *joint_combiner * acc
1413 })
1414 };
1415 fixed_table_blinding + *table_id_combiner
1416 };
1417 if lcs.runtime_selector.is_some() {
1418 let runtime_comm = lookup_context.runtime_table_comm.as_ref().unwrap();
1419
1420 let chunks = runtime_comm
1421 .blinders
1422 .into_iter()
1423 .map(|blinding| *joint_combiner * *blinding + base_blinding)
1424 .collect();
1425
1426 PolyComm::new(chunks)
1427 } else {
1428 let chunks = vec![base_blinding; num_chunks];
1429 PolyComm::new(chunks)
1430 }
1431 };
1432
1433 let joint_lookup_table = lookup_context.joint_lookup_table.as_ref().unwrap();
1434
1435 polynomials.push((coefficients_form(joint_lookup_table), table_blinding));
1436
1437 if lcs.runtime_selector.is_some() {
1439 let runtime_table_comm = lookup_context.runtime_table_comm.as_ref().unwrap();
1440 let runtime_table = lookup_context.runtime_table.as_ref().unwrap();
1441
1442 polynomials.push((
1443 coefficients_form(runtime_table),
1444 runtime_table_comm.blinders.clone(),
1445 ));
1446 }
1447
1448 if let Some(runtime_lookup_table_selector) = &lcs.runtime_selector {
1451 polynomials.push((
1452 evaluations_form(runtime_lookup_table_selector),
1453 non_hiding(num_chunks),
1454 ))
1455 }
1456 if let Some(xor_lookup_selector) = &lcs.lookup_selectors.xor {
1457 polynomials.push((
1458 evaluations_form(xor_lookup_selector),
1459 non_hiding(num_chunks),
1460 ))
1461 }
1462 if let Some(lookup_gate_selector) = &lcs.lookup_selectors.lookup {
1463 polynomials.push((
1464 evaluations_form(lookup_gate_selector),
1465 non_hiding(num_chunks),
1466 ))
1467 }
1468 if let Some(range_check_lookup_selector) = &lcs.lookup_selectors.range_check {
1469 polynomials.push((
1470 evaluations_form(range_check_lookup_selector),
1471 non_hiding(num_chunks),
1472 ))
1473 }
1474 if let Some(foreign_field_mul_lookup_selector) = &lcs.lookup_selectors.ffmul {
1475 polynomials.push((
1476 evaluations_form(foreign_field_mul_lookup_selector),
1477 non_hiding(num_chunks),
1478 ))
1479 }
1480 }
1481
1482 internal_tracing::checkpoint!(internal_traces; create_aggregated_ipa);
1484 let proof = OpenProof::open(
1485 &*index.srs,
1486 group_map,
1487 &polynomials,
1488 &[zeta, zeta_omega],
1489 v,
1490 u,
1491 fq_sponge_before_evaluations,
1492 rng,
1493 );
1494
1495 let lookup = lookup_context
1496 .aggreg_comm
1497 .zip(lookup_context.sorted_comms)
1498 .map(|(a, s)| LookupCommitments {
1499 aggreg: a.commitment,
1500 sorted: s.iter().map(|c| c.commitment.clone()).collect(),
1501 runtime: lookup_context.runtime_table_comm.map(|x| x.commitment),
1502 });
1503
1504 let proof = Self {
1505 commitments: ProverCommitments {
1506 w_comm: array::from_fn(|i| w_comm[i].commitment.clone()),
1507 z_comm: z_comm.commitment,
1508 t_comm: t_comm.commitment,
1509 lookup,
1510 },
1511 proof,
1512 evals: chunked_evals,
1513 ft_eval1,
1514 prev_challenges,
1515 };
1516
1517 internal_tracing::checkpoint!(internal_traces; create_recursive_done);
1518
1519 Ok(proof)
1520 }
1521}
1522
1523internal_tracing::decl_traces!(internal_traces;
1524 pasta_fp_plonk_proof_create,
1525 pasta_fq_plonk_proof_create,
1526 create_recursive,
1527 pad_witness,
1528 set_up_fq_sponge,
1529 commit_to_witness_columns,
1530 use_lookup,
1531 z_permutation_aggregation_polynomial,
1532 eval_witness_polynomials_over_domains,
1533 compute_index_evals,
1534 compute_quotient_poly,
1535 lagrange_basis_eval_zeta_poly,
1536 lagrange_basis_eval_zeta_omega_poly,
1537 chunk_eval_zeta_omega_poly,
1538 compute_ft_poly,
1539 ft_eval_zeta_omega,
1540 build_polynomials,
1541 create_aggregated_ipa,
1542 create_recursive_done);
1543
1544#[cfg(feature = "ocaml_types")]
1545pub mod caml {
1546 use super::*;
1547 use crate::proof::caml::{CamlProofEvaluations, CamlRecursionChallenge};
1548 use ark_ec::AffineRepr;
1549 use poly_commitment::{
1550 commitment::caml::CamlPolyComm,
1551 ipa::{caml::CamlOpeningProof, OpeningProof},
1552 };
1553
1554 #[cfg(feature = "internal_tracing")]
1555 pub use internal_traces::caml::CamlTraces as CamlProverTraces;
1556
1557 #[derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
1558 pub struct CamlProofWithPublic<CamlG, CamlF> {
1559 pub public_evals: Option<PointEvaluations<Vec<CamlF>>>,
1560 pub proof: CamlProverProof<CamlG, CamlF>,
1561 }
1562
1563 #[derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
1568 pub struct CamlProverProof<CamlG, CamlF> {
1569 pub commitments: CamlProverCommitments<CamlG>,
1570 pub proof: CamlOpeningProof<CamlG, CamlF>,
1571 pub evals: CamlProofEvaluations<CamlF>,
1573 pub ft_eval1: CamlF,
1574 pub public: Vec<CamlF>,
1575 pub prev_challenges: Vec<CamlRecursionChallenge<CamlG, CamlF>>,
1577 }
1578
1579 #[derive(Clone, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
1584 pub struct CamlLookupCommitments<CamlG> {
1585 pub sorted: Vec<CamlPolyComm<CamlG>>,
1586 pub aggreg: CamlPolyComm<CamlG>,
1587 pub runtime: Option<CamlPolyComm<CamlG>>,
1588 }
1589
1590 #[allow(clippy::type_complexity)]
1591 #[derive(Clone, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
1592 pub struct CamlProverCommitments<CamlG> {
1593 pub w_comm: (
1595 CamlPolyComm<CamlG>,
1596 CamlPolyComm<CamlG>,
1597 CamlPolyComm<CamlG>,
1598 CamlPolyComm<CamlG>,
1599 CamlPolyComm<CamlG>,
1600 CamlPolyComm<CamlG>,
1601 CamlPolyComm<CamlG>,
1602 CamlPolyComm<CamlG>,
1603 CamlPolyComm<CamlG>,
1604 CamlPolyComm<CamlG>,
1605 CamlPolyComm<CamlG>,
1606 CamlPolyComm<CamlG>,
1607 CamlPolyComm<CamlG>,
1608 CamlPolyComm<CamlG>,
1609 CamlPolyComm<CamlG>,
1610 ),
1611 pub z_comm: CamlPolyComm<CamlG>,
1612 pub t_comm: CamlPolyComm<CamlG>,
1613 pub lookup: Option<CamlLookupCommitments<CamlG>>,
1614 }
1615
1616 impl<G, CamlG> From<LookupCommitments<G>> for CamlLookupCommitments<CamlG>
1638 where
1639 G: AffineRepr,
1640 CamlPolyComm<CamlG>: From<PolyComm<G>>,
1641 {
1642 fn from(
1643 LookupCommitments {
1644 aggreg,
1645 sorted,
1646 runtime,
1647 }: LookupCommitments<G>,
1648 ) -> Self {
1649 Self {
1650 aggreg: aggreg.into(),
1651 sorted: sorted.into_iter().map(Into::into).collect(),
1652 runtime: runtime.map(Into::into),
1653 }
1654 }
1655 }
1656
1657 impl<G, CamlG> From<CamlLookupCommitments<CamlG>> for LookupCommitments<G>
1658 where
1659 G: AffineRepr,
1660 PolyComm<G>: From<CamlPolyComm<CamlG>>,
1661 {
1662 fn from(
1663 CamlLookupCommitments {
1664 aggreg,
1665 sorted,
1666 runtime,
1667 }: CamlLookupCommitments<CamlG>,
1668 ) -> LookupCommitments<G> {
1669 LookupCommitments {
1670 aggreg: aggreg.into(),
1671 sorted: sorted.into_iter().map(Into::into).collect(),
1672 runtime: runtime.map(Into::into),
1673 }
1674 }
1675 }
1676
1677 impl<G, CamlG> From<ProverCommitments<G>> for CamlProverCommitments<CamlG>
1682 where
1683 G: AffineRepr,
1684 CamlPolyComm<CamlG>: From<PolyComm<G>>,
1685 {
1686 fn from(prover_comm: ProverCommitments<G>) -> Self {
1687 let [w_comm0, w_comm1, w_comm2, w_comm3, w_comm4, w_comm5, w_comm6, w_comm7, w_comm8, w_comm9, w_comm10, w_comm11, w_comm12, w_comm13, w_comm14] =
1688 prover_comm.w_comm;
1689 Self {
1690 w_comm: (
1691 w_comm0.into(),
1692 w_comm1.into(),
1693 w_comm2.into(),
1694 w_comm3.into(),
1695 w_comm4.into(),
1696 w_comm5.into(),
1697 w_comm6.into(),
1698 w_comm7.into(),
1699 w_comm8.into(),
1700 w_comm9.into(),
1701 w_comm10.into(),
1702 w_comm11.into(),
1703 w_comm12.into(),
1704 w_comm13.into(),
1705 w_comm14.into(),
1706 ),
1707 z_comm: prover_comm.z_comm.into(),
1708 t_comm: prover_comm.t_comm.into(),
1709 lookup: prover_comm.lookup.map(Into::into),
1710 }
1711 }
1712 }
1713
1714 impl<G, CamlG> From<CamlProverCommitments<CamlG>> for ProverCommitments<G>
1715 where
1716 G: AffineRepr,
1717 PolyComm<G>: From<CamlPolyComm<CamlG>>,
1718 {
1719 fn from(caml_prover_comm: CamlProverCommitments<CamlG>) -> ProverCommitments<G> {
1720 let (
1721 w_comm0,
1722 w_comm1,
1723 w_comm2,
1724 w_comm3,
1725 w_comm4,
1726 w_comm5,
1727 w_comm6,
1728 w_comm7,
1729 w_comm8,
1730 w_comm9,
1731 w_comm10,
1732 w_comm11,
1733 w_comm12,
1734 w_comm13,
1735 w_comm14,
1736 ) = caml_prover_comm.w_comm;
1737 ProverCommitments {
1738 w_comm: [
1739 w_comm0.into(),
1740 w_comm1.into(),
1741 w_comm2.into(),
1742 w_comm3.into(),
1743 w_comm4.into(),
1744 w_comm5.into(),
1745 w_comm6.into(),
1746 w_comm7.into(),
1747 w_comm8.into(),
1748 w_comm9.into(),
1749 w_comm10.into(),
1750 w_comm11.into(),
1751 w_comm12.into(),
1752 w_comm13.into(),
1753 w_comm14.into(),
1754 ],
1755 z_comm: caml_prover_comm.z_comm.into(),
1756 t_comm: caml_prover_comm.t_comm.into(),
1757 lookup: caml_prover_comm.lookup.map(Into::into),
1758 }
1759 }
1760 }
1761
1762 #[allow(type_alias_bounds)]
1769 type ProofTuple<const FULL_ROUNDS: usize, G: AffineRepr> = (
1770 ProverProof<G, OpeningProof<G, FULL_ROUNDS>, FULL_ROUNDS>,
1771 Vec<<G as AffineRepr>::ScalarField>,
1772 );
1773
1774 impl<const FULL_ROUNDS: usize, G, CamlG, CamlF> From<ProofTuple<FULL_ROUNDS, G>>
1775 for CamlProofWithPublic<CamlG, CamlF>
1776 where
1777 G: AffineRepr,
1778 G::BaseField: PrimeField,
1779 G: poly_commitment::commitment::EndoCurve,
1780 CamlG: From<G>,
1781 CamlF: From<G::ScalarField>,
1782 {
1783 fn from(pp: ProofTuple<FULL_ROUNDS, G>) -> Self {
1784 let (public_evals, evals) = pp.0.evals.into();
1785 CamlProofWithPublic {
1786 public_evals,
1787 proof: CamlProverProof {
1788 commitments: pp.0.commitments.into(),
1789 proof: pp.0.proof.into(),
1790 evals,
1791 ft_eval1: pp.0.ft_eval1.into(),
1792 public: pp.1.into_iter().map(Into::into).collect(),
1793 prev_challenges: pp.0.prev_challenges.into_iter().map(Into::into).collect(),
1794 },
1795 }
1796 }
1797 }
1798
1799 impl<const FULL_ROUNDS: usize, G, CamlG, CamlF> From<CamlProofWithPublic<CamlG, CamlF>>
1800 for ProofTuple<FULL_ROUNDS, G>
1801 where
1802 CamlF: Clone,
1803 G: AffineRepr + From<CamlG>,
1804 G::BaseField: PrimeField,
1805 G: poly_commitment::commitment::EndoCurve,
1806 G::ScalarField: From<CamlF>,
1807 {
1808 fn from(caml_pp: CamlProofWithPublic<CamlG, CamlF>) -> Self {
1809 let CamlProofWithPublic {
1810 public_evals,
1811 proof: caml_pp,
1812 } = caml_pp;
1813 let proof = ProverProof {
1814 commitments: caml_pp.commitments.into(),
1815 proof: caml_pp.proof.into(),
1816 evals: (public_evals, caml_pp.evals).into(),
1817 ft_eval1: caml_pp.ft_eval1.into(),
1818 prev_challenges: caml_pp
1819 .prev_challenges
1820 .into_iter()
1821 .map(Into::into)
1822 .collect(),
1823 };
1824
1825 (proof, caml_pp.public.into_iter().map(Into::into).collect())
1826 }
1827 }
1828}
1829
1830#[cfg(test)]
1831mod tests {
1832 use super::*;
1833 use mina_curves::pasta::Fp;
1834
1835 #[test]
1839 fn blinders_are_drawn_per_chunk() {
1840 let mut rng = o1_utils::tests::make_test_rng(None);
1841 let blinder = blinder::<Fp, _>(4, &mut rng);
1842
1843 assert_eq!(blinder.chunks.len(), 4);
1844 for (i, a) in blinder.chunks.iter().enumerate() {
1845 for b in blinder.chunks.iter().skip(i + 1) {
1846 assert_ne!(a, b, "two chunks share a blinder");
1847 }
1848 }
1849 }
1850}