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 runtime_second_col_d8: Option<Evaluations<F, D<F>>>,
138}
139
140impl<G, OpeningProof, const FULL_ROUNDS: usize> ProverProof<G, OpeningProof, FULL_ROUNDS>
141where
142 G: KimchiCurve<FULL_ROUNDS>,
143 G::BaseField: PrimeField,
144 OpeningProof: OpenProof<G, FULL_ROUNDS>,
145{
146 pub fn create<EFqSponge, EFrSponge, RNG>(
152 groupmap: &G::Map,
153 witness: [Vec<G::ScalarField>; COLUMNS],
154 runtime_tables: &[RuntimeTable<G::ScalarField>],
155 index: &ProverIndex<FULL_ROUNDS, G, OpeningProof::SRS>,
156 rng: &mut RNG,
157 ) -> Result<Self>
158 where
159 EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField, FULL_ROUNDS>,
160 EFrSponge: FrSponge<G::ScalarField>,
161 EFrSponge: From<&'static ArithmeticSpongeParams<G::ScalarField, FULL_ROUNDS>>,
162 RNG: RngCore + CryptoRng,
163 VerifierIndex<FULL_ROUNDS, G, OpeningProof::SRS>: Clone,
164 {
165 Self::create_recursive::<EFqSponge, EFrSponge, RNG>(
166 groupmap,
167 witness,
168 runtime_tables,
169 index,
170 Vec::new(),
171 None,
172 rng,
173 )
174 }
175
176 pub fn create_recursive<EFqSponge, EFrSponge, RNG>(
188 group_map: &G::Map,
189 mut witness: [Vec<G::ScalarField>; COLUMNS],
190 runtime_tables: &[RuntimeTable<G::ScalarField>],
191 index: &ProverIndex<FULL_ROUNDS, G, OpeningProof::SRS>,
192 prev_challenges: Vec<RecursionChallenge<G>>,
193 blinders: Option<[Option<PolyComm<G::ScalarField>>; COLUMNS]>,
194 rng: &mut RNG,
195 ) -> Result<Self>
196 where
197 EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField, FULL_ROUNDS>,
198 EFrSponge: FrSponge<G::ScalarField>,
199 EFrSponge: From<&'static ArithmeticSpongeParams<G::ScalarField, FULL_ROUNDS>>,
200 RNG: RngCore + CryptoRng,
201 VerifierIndex<FULL_ROUNDS, G, OpeningProof::SRS>: Clone,
202 {
203 internal_tracing::checkpoint!(internal_traces; create_recursive);
204 let d1_size = index.cs.domain.d1.size();
205
206 let (_, endo_r) = G::endos();
207
208 let num_chunks = if d1_size < index.max_poly_size {
209 1
210 } else {
211 d1_size / index.max_poly_size
212 };
213
214 if cfg!(debug_assertions) && !index.cs.disable_gates_checks {
217 let public = witness[0][0..index.cs.public].to_vec();
218 index.verify(&witness, &public).expect("incorrect witness");
219 }
220
221 let length_witness = witness[0].len();
227 let length_padding = d1_size
228 .checked_sub(length_witness)
229 .ok_or(ProverError::NoRoomForZkInWitness)?;
230
231 let zero_knowledge_limit = zk_rows_strict_lower_bound(num_chunks);
232 if (index.cs.zk_rows as usize) <= zero_knowledge_limit {
241 return Err(ProverError::NotZeroKnowledge(
242 zero_knowledge_limit + 1,
243 index.cs.zk_rows as usize,
244 ));
245 }
246
247 if length_padding < index.cs.zk_rows as usize {
248 return Err(ProverError::NoRoomForZkInWitness);
249 }
250
251 internal_tracing::checkpoint!(internal_traces; pad_witness);
254 for w in &mut witness {
255 if w.len() != length_witness {
256 return Err(ProverError::WitnessCsInconsistent);
257 }
258
259 w.extend(std::iter::repeat_n(G::ScalarField::zero(), length_padding));
261
262 for row in w.iter_mut().rev().take(index.cs.zk_rows as usize) {
264 *row = <G::ScalarField as UniformRand>::rand(rng);
265 }
266 }
267
268 internal_tracing::checkpoint!(internal_traces; set_up_fq_sponge);
270 let mut fq_sponge = EFqSponge::new(G::other_curve_sponge_params());
271
272 let verifier_index_digest = index.verifier_index_digest::<EFqSponge>();
274 fq_sponge.absorb_fq(&[verifier_index_digest]);
275
276 for RecursionChallenge { comm, .. } in &prev_challenges {
278 absorb_commitment(&mut fq_sponge, comm)
279 }
280
281 let public = witness[0][0..index.cs.public].to_vec();
285 let public_poly = -Evaluations::<G::ScalarField, D<G::ScalarField>>::from_vec_and_domain(
286 public,
287 index.cs.domain.d1,
288 )
289 .interpolate();
290
291 let public_comm = index.srs.commit_non_hiding(&public_poly, num_chunks);
293 let public_comm = {
294 index
295 .srs
296 .mask_custom(
297 public_comm.clone(),
298 &public_comm.map(|_| G::ScalarField::one()),
299 )
300 .unwrap()
301 .commitment
302 };
303
304 absorb_commitment(&mut fq_sponge, &public_comm);
310
311 internal_tracing::checkpoint!(internal_traces; commit_to_witness_columns);
316 let blinders_final: Vec<PolyComm<G::ScalarField>> = match blinders {
318 None => (0..COLUMNS)
319 .map(|_| blinder::<G::ScalarField, _>(num_chunks, rng))
320 .collect(),
321 Some(blinders_arr) => blinders_arr
322 .into_iter()
323 .map(|blinder_el| match blinder_el {
324 None => blinder::<G::ScalarField, _>(num_chunks, rng),
325 Some(blinder_el_some) => blinder_el_some,
326 })
327 .collect(),
328 };
329 let w_comm_opt_res: Vec<Result<_>> = witness
330 .clone()
331 .into_par_iter()
332 .zip(blinders_final.into_par_iter())
333 .map(|(witness, blinder)| {
334 let witness_eval =
335 Evaluations::<G::ScalarField, D<G::ScalarField>>::from_vec_and_domain(
336 witness,
337 index.cs.domain.d1,
338 );
339
340 let witness_com = index
342 .srs
343 .commit_evaluations_non_hiding(index.cs.domain.d1, &witness_eval);
344 let com = index
345 .srs
346 .mask_custom(witness_com, &blinder)
347 .map_err(ProverError::WrongBlinders)?;
348
349 Ok(com)
350 })
351 .collect();
352
353 let w_comm_res: Result<Vec<BlindedCommitment<G>>> = w_comm_opt_res.into_iter().collect();
354
355 let w_comm = w_comm_res?;
356
357 let w_comm: [BlindedCommitment<G>; COLUMNS] = w_comm
358 .try_into()
359 .expect("previous loop is of the correct length");
360
361 w_comm
363 .iter()
364 .for_each(|c| absorb_commitment(&mut fq_sponge, &c.commitment));
365
366 let witness_poly: [DensePolynomial<G::ScalarField>; COLUMNS] = (0..COLUMNS)
371 .into_par_iter()
372 .map(|i| {
373 Evaluations::<G::ScalarField, D<G::ScalarField>>::from_vec_and_domain(
374 witness[i].clone(),
375 index.cs.domain.d1,
376 )
377 .interpolate()
378 })
379 .collect::<Vec<_>>()
380 .try_into()
381 .unwrap();
382
383 let mut lookup_context = LookupContext::default();
384
385 let lookup_constraint_system = index
387 .cs
388 .lookup_constraint_system
389 .try_get_or_err()
390 .map_err(ProverError::from)?;
391 if let Some(lcs) = lookup_constraint_system {
392 internal_tracing::checkpoint!(internal_traces; use_lookup, {
393 "uses_lookup": true,
394 "uses_runtime_tables": lcs.runtime_tables.is_some(),
395 });
396 if let Some(cfg_runtime_tables) = &lcs.runtime_tables {
398 let expected_runtime: Vec<_> = cfg_runtime_tables
401 .iter()
402 .map(|rt| (rt.id, rt.len))
403 .collect();
404 let runtime: Vec<_> = runtime_tables
405 .iter()
406 .map(|rt| (rt.id, rt.data.len()))
407 .collect();
408 if expected_runtime != runtime {
409 return Err(ProverError::RuntimeTablesInconsistent);
410 }
411
412 let (runtime_table_contribution, runtime_table_contribution_d8) = {
415 let mut offset = lcs
416 .runtime_table_offset
417 .expect("runtime configuration missing offset");
418
419 let mut evals = vec![G::ScalarField::zero(); d1_size];
420 for rt in runtime_tables {
421 let range = offset..(offset + rt.data.len());
422 evals[range].copy_from_slice(&rt.data);
423 offset += rt.data.len();
424 }
425
426 for e in evals.iter_mut().rev().take(index.cs.zk_rows as usize) {
428 *e = <G::ScalarField as UniformRand>::rand(rng);
429 }
430
431 let runtime_table_contribution =
433 Evaluations::from_vec_and_domain(evals, index.cs.domain.d1).interpolate();
434
435 let runtime_table_contribution_d8 =
436 runtime_table_contribution.evaluate_over_domain_by_ref(index.cs.domain.d8);
437
438 (runtime_table_contribution, runtime_table_contribution_d8)
439 };
440
441 let runtime_table_comm =
444 index
445 .srs
446 .commit(&runtime_table_contribution, num_chunks, rng);
447
448 absorb_commitment(&mut fq_sponge, &runtime_table_comm.commitment);
450
451 let mut second_column_d8 = runtime_table_contribution_d8.clone();
453 second_column_d8
454 .evals
455 .par_iter_mut()
456 .enumerate()
457 .for_each(|(row, e)| {
458 *e += lcs.lookup_table8[1][row];
459 });
460
461 lookup_context.runtime_table = Some(runtime_table_contribution);
462 lookup_context.runtime_table_d8 = Some(runtime_table_contribution_d8);
463 lookup_context.runtime_table_comm = Some(runtime_table_comm);
464 lookup_context.runtime_second_col_d8 = Some(second_column_d8);
465 }
466
467 let joint_combiner = if lcs.configuration.lookup_info.features.joint_lookup_used {
471 fq_sponge.challenge()
472 } else {
473 G::ScalarField::zero()
474 };
475
476 let joint_combiner: G::ScalarField =
478 ScalarChallenge::new(joint_combiner).to_field(endo_r);
479
480 let table_id_combiner: G::ScalarField = if lcs.table_ids8.as_ref().is_some() {
484 joint_combiner.pow([lcs.configuration.lookup_info.max_joint_size as u64])
485 } else {
486 G::ScalarField::zero()
488 };
489 lookup_context.table_id_combiner = Some(table_id_combiner);
490
491 let dummy_lookup_value = lcs
494 .configuration
495 .dummy_lookup
496 .evaluate(&joint_combiner, &table_id_combiner);
497 lookup_context.dummy_lookup_value = Some(dummy_lookup_value);
498
499 let joint_lookup_table_d8 = {
501 let evals = (0..(d1_size * 8))
504 .into_par_iter()
505 .map(|idx| {
506 let table_id = match lcs.table_ids8.as_ref() {
507 Some(table_ids8) => table_ids8.evals[idx],
508 None =>
509 {
512 G::ScalarField::zero()
513 }
514 };
515
516 if !lcs.configuration.lookup_info.features.uses_runtime_tables {
517 let table_row = lcs.lookup_table8.iter().map(|e| &e.evals[idx]);
518
519 combine_table_entry(
520 &joint_combiner,
521 &table_id_combiner,
522 table_row,
523 &table_id,
524 )
525 } else {
526 let second_col = lookup_context.runtime_second_col_d8.as_ref().unwrap();
528
529 let table_row = lcs.lookup_table8.iter().enumerate().map(|(col, e)| {
530 if col == 1 {
531 &second_col.evals[idx]
532 } else {
533 &e.evals[idx]
534 }
535 });
536
537 combine_table_entry(
538 &joint_combiner,
539 &table_id_combiner,
540 table_row,
541 &table_id,
542 )
543 }
544 })
545 .collect();
546
547 Evaluations::from_vec_and_domain(evals, index.cs.domain.d8)
548 };
549
550 let joint_lookup_table = {
561 let d1_evals: Vec<G::ScalarField> = joint_lookup_table_d8
562 .evals
563 .iter()
564 .step_by(8)
565 .copied()
566 .collect();
567 Evaluations::from_vec_and_domain(d1_evals, index.cs.domain.d1).interpolate()
568 };
569
570 let sorted: Vec<_> = lookup::constraints::sorted(
575 dummy_lookup_value,
576 &joint_lookup_table_d8,
577 index.cs.domain.d1,
578 &index.cs.gates,
579 &witness,
580 joint_combiner,
581 table_id_combiner,
582 &lcs.configuration.lookup_info,
583 index.cs.zk_rows as usize,
584 )?;
585
586 let sorted: Vec<_> = sorted
589 .into_iter()
590 .map(|chunk| {
591 lookup::constraints::zk_patch(
592 chunk,
593 index.cs.domain.d1,
594 index.cs.zk_rows as usize,
595 rng,
596 )
597 })
598 .collect();
599
600 let sorted_comms: Vec<_> = sorted
602 .iter()
603 .map(|v| index.srs.commit_evaluations(index.cs.domain.d1, v, rng))
604 .collect();
605
606 sorted_comms
608 .iter()
609 .for_each(|c| absorb_commitment(&mut fq_sponge, &c.commitment));
610
611 let sorted_coeffs: Vec<_> =
614 sorted.par_iter().map(|e| e.clone().interpolate()).collect();
615 let sorted8: Vec<_> = sorted_coeffs
616 .par_iter()
617 .map(|v| v.evaluate_over_domain_by_ref(index.cs.domain.d8))
618 .collect();
619
620 lookup_context.joint_combiner = Some(joint_combiner);
621 lookup_context.sorted = Some(sorted);
622 lookup_context.sorted_coeffs = Some(sorted_coeffs);
623 lookup_context.sorted_comms = Some(sorted_comms);
624 lookup_context.sorted8 = Some(sorted8);
625 lookup_context.joint_lookup_table_d8 = Some(joint_lookup_table_d8);
626 lookup_context.joint_lookup_table = Some(joint_lookup_table);
627 }
628
629 let beta = fq_sponge.challenge();
631
632 let gamma = fq_sponge.challenge();
634
635 if let Some(lcs) = lookup_constraint_system {
637 let joint_lookup_table_d8 = lookup_context.joint_lookup_table_d8.as_ref().unwrap();
639
640 let aggreg = lookup::constraints::aggregation::<_, G::ScalarField>(
641 lookup_context.dummy_lookup_value.unwrap(),
642 joint_lookup_table_d8,
643 index.cs.domain.d1,
644 &index.cs.gates,
645 &witness,
646 &lookup_context.joint_combiner.unwrap(),
647 &lookup_context.table_id_combiner.unwrap(),
648 beta,
649 gamma,
650 lookup_context.sorted.as_ref().unwrap(),
651 rng,
652 &lcs.configuration.lookup_info,
653 index.cs.zk_rows as usize,
654 )?;
655
656 let aggreg_comm = index
658 .srs
659 .commit_evaluations(index.cs.domain.d1, &aggreg, rng);
660
661 absorb_commitment(&mut fq_sponge, &aggreg_comm.commitment);
663
664 let aggreg_coeffs = aggreg.interpolate();
666 let aggreg8 = aggreg_coeffs.evaluate_over_domain_by_ref(index.cs.domain.d8);
669
670 lookup_context.aggreg_comm = Some(aggreg_comm);
671 lookup_context.aggreg_coeffs = Some(aggreg_coeffs);
672 lookup_context.aggreg8 = Some(aggreg8);
673 }
674
675 let column_evaluations = index.column_evaluations.get();
676
677 internal_tracing::checkpoint!(internal_traces; z_permutation_aggregation_polynomial);
679 let z_poly = index.perm_aggreg(&witness, &beta, &gamma, rng)?;
680
681 let z_comm = index.srs.commit(&z_poly, num_chunks, rng);
683
684 absorb_commitment(&mut fq_sponge, &z_comm.commitment);
686
687 let alpha_chal = ScalarChallenge::new(fq_sponge.challenge());
689
690 let alpha: G::ScalarField = alpha_chal.to_field(endo_r);
692
693 let mut all_alphas = index.powers_of_alpha.clone();
695 all_alphas.instantiate(alpha);
696
697 let lookup_env = if let Some(lcs) = lookup_constraint_system {
706 let joint_lookup_table_d8 = lookup_context.joint_lookup_table_d8.as_ref().unwrap();
707
708 Some(LookupEnvironment {
709 aggreg: lookup_context.aggreg8.as_ref().unwrap(),
710 sorted: lookup_context.sorted8.as_ref().unwrap(),
711 selectors: &lcs.lookup_selectors,
712 table: joint_lookup_table_d8,
713 runtime_selector: lcs.runtime_selector.as_ref(),
714 runtime_table: lookup_context.runtime_table_d8.as_ref(),
715 })
716 } else {
717 None
718 };
719
720 internal_tracing::checkpoint!(internal_traces; eval_witness_polynomials_over_domains);
721 let lagrange = index.cs.evaluate(&witness_poly, &z_poly);
722 internal_tracing::checkpoint!(internal_traces; compute_index_evals);
723 let env = {
724 let mut index_evals = HashMap::new();
725 use GateType::*;
726 index_evals.insert(Generic, &column_evaluations.generic_selector4);
727 index_evals.insert(Poseidon, &column_evaluations.poseidon_selector8);
728 index_evals.insert(CompleteAdd, &column_evaluations.complete_add_selector4);
729 index_evals.insert(VarBaseMul, &column_evaluations.mul_selector8);
730 index_evals.insert(EndoMul, &column_evaluations.emul_selector8);
731 index_evals.insert(EndoMulScalar, &column_evaluations.endomul_scalar_selector8);
732
733 if let Some(selector) = &column_evaluations.range_check0_selector8 {
734 index_evals.insert(GateType::RangeCheck0, selector);
735 }
736
737 if let Some(selector) = &column_evaluations.range_check1_selector8 {
738 index_evals.insert(GateType::RangeCheck1, selector);
739 }
740
741 if let Some(selector) = &column_evaluations.foreign_field_add_selector8 {
742 index_evals.insert(GateType::ForeignFieldAdd, selector);
743 }
744
745 if let Some(selector) = &column_evaluations.foreign_field_mul_selector8 {
746 index_evals.extend(
747 foreign_field_mul::gadget::circuit_gates()
748 .iter()
749 .map(|gate_type| (*gate_type, selector)),
750 );
751 }
752
753 if let Some(selector) = &column_evaluations.xor_selector8 {
754 index_evals.insert(GateType::Xor16, selector);
755 }
756
757 if let Some(selector) = &column_evaluations.rot_selector8 {
758 index_evals.insert(GateType::Rot64, selector);
759 }
760
761 let mds = &G::sponge_params().mds;
762 Environment {
763 constants: Constants {
764 endo_coefficient: index.cs.endo,
765 mds,
766 zk_rows: index.cs.zk_rows,
767 },
768 challenges: BerkeleyChallenges {
769 alpha,
770 beta,
771 gamma,
772 joint_combiner: lookup_context
773 .joint_combiner
774 .unwrap_or(G::ScalarField::zero()),
775 },
776 witness: &lagrange.this.w,
777 coefficient: &column_evaluations.coefficients8,
778 vanishes_on_zero_knowledge_and_previous_rows: &index
779 .cs
780 .precomputations()
781 .vanishes_on_zero_knowledge_and_previous_rows,
782 z: &lagrange.this.z,
783 l0_1: l0_1(index.cs.domain.d1),
784 domain: index.cs.domain,
785 index: index_evals,
786 lookup: lookup_env,
787 }
788 };
789
790 let mut cache = expr::Cache::default();
791
792 internal_tracing::checkpoint!(internal_traces; compute_quotient_poly);
793
794 let quotient_poly = {
795 let mut t4 = {
797 let generic_constraint =
798 generic::Generic::combined_constraints(&all_alphas, &mut cache);
799 let generic4 = generic_constraint.evaluations(&env);
800
801 if cfg!(debug_assertions) {
802 let p4 = public_poly.evaluate_over_domain_by_ref(index.cs.domain.d4);
803 let gen_minus_pub = &generic4 + &p4;
804
805 check_constraint!(index, gen_minus_pub);
806 }
807
808 generic4
809 };
810
811 let (mut t8, bnd) = {
813 let alphas =
814 all_alphas.get_alphas(ArgumentType::Permutation, permutation::CONSTRAINTS);
815 let (perm, bnd) = index.perm_quot(&lagrange, beta, gamma, &z_poly, alphas)?;
816
817 check_constraint!(index, perm);
818
819 (perm, bnd)
820 };
821
822 {
823 use crate::circuits::argument::DynArgument;
824
825 let range_check0_enabled = column_evaluations.range_check0_selector8.is_some();
826 let range_check1_enabled = column_evaluations.range_check1_selector8.is_some();
827 let foreign_field_addition_enabled =
828 column_evaluations.foreign_field_add_selector8.is_some();
829 let foreign_field_multiplication_enabled =
830 column_evaluations.foreign_field_mul_selector8.is_some();
831 let xor_enabled = column_evaluations.xor_selector8.is_some();
832 let rot_enabled = column_evaluations.rot_selector8.is_some();
833
834 for gate in [
835 (
836 (&CompleteAdd::default() as &dyn DynArgument<G::ScalarField>),
837 true,
838 ),
839 (&VarbaseMul::default(), true),
840 (&EndosclMul::default(), true),
841 (&EndomulScalar::default(), true),
842 (&Poseidon::default(), true),
843 (&RangeCheck0::default(), range_check0_enabled),
845 (&RangeCheck1::default(), range_check1_enabled),
846 (&ForeignFieldAdd::default(), foreign_field_addition_enabled),
848 (
850 &ForeignFieldMul::default(),
851 foreign_field_multiplication_enabled,
852 ),
853 (&Xor16::default(), xor_enabled),
855 (&Rot64::default(), rot_enabled),
857 ]
858 .into_iter()
859 .filter_map(|(gate, is_enabled)| if is_enabled { Some(gate) } else { None })
860 {
861 let constraint = gate.combined_constraints(&all_alphas, &mut cache);
862 let eval = constraint.evaluations(&env);
863 if eval.domain().size == t4.domain().size {
864 t4 += &eval;
865 } else if eval.domain().size == t8.domain().size {
866 t8 += &eval;
867 } else {
868 panic!("Bad evaluation")
869 }
870 check_constraint!(index, format!("{:?}", gate.argument_type()), eval);
871 }
872 };
873
874 {
876 if let Some(lcs) = lookup_constraint_system {
877 let constraints = lookup::constraints::constraints(&lcs.configuration, false);
878 let constraints_len = u32::try_from(constraints.len())
879 .expect("not expecting a large amount of constraints");
880 let lookup_alphas =
881 all_alphas.get_alphas(ArgumentType::Lookup, constraints_len);
882
883 for (ii, (constraint, alpha_pow)) in
886 constraints.into_iter().zip_eq(lookup_alphas).enumerate()
887 {
888 let mut eval = constraint.evaluations(&env);
889 eval.evals.par_iter_mut().for_each(|x| *x *= alpha_pow);
890
891 if eval.domain().size == t4.domain().size {
892 t4 += &eval;
893 } else if eval.domain().size == t8.domain().size {
894 t8 += &eval;
895 } else if eval.evals.iter().all(|x| x.is_zero()) {
896 } else {
898 panic!("Bad evaluation")
899 }
900
901 check_constraint!(index, format!("lookup constraint #{ii}"), eval);
902 }
903 }
904 }
905
906 let mut f = t4.interpolate() + t8.interpolate();
908 f += &public_poly;
909
910 let (mut quotient, res) = f.divide_by_vanishing_poly(index.cs.domain.d1);
912 if !res.is_zero() {
913 return Err(ProverError::Prover(
914 "rest of division by vanishing polynomial",
915 ));
916 }
917
918 quotient += &bnd; quotient
920 };
921
922 let t_comm = { index.srs.commit("ient_poly, 7 * num_chunks, rng) };
924
925 absorb_commitment(&mut fq_sponge, &t_comm.commitment);
927
928 let zeta_chal = ScalarChallenge::new(fq_sponge.challenge());
930
931 let zeta = zeta_chal.to_field(endo_r);
933
934 let omega = index.cs.domain.d1.group_gen;
935 let zeta_omega = zeta * omega;
936
937 if lookup_constraint_system.is_some() {
939 let aggreg = lookup_context
941 .aggreg_coeffs
942 .as_ref()
943 .unwrap()
944 .to_chunked_polynomial(num_chunks, index.max_poly_size);
945
946 let sorted = lookup_context
948 .sorted_coeffs
949 .as_ref()
950 .unwrap()
951 .iter()
952 .map(|c| c.to_chunked_polynomial(num_chunks, index.max_poly_size))
953 .collect::<Vec<_>>();
954
955 let joint_table = lookup_context.joint_lookup_table.as_ref().unwrap();
957 let joint_table = joint_table.to_chunked_polynomial(num_chunks, index.max_poly_size);
958
959 lookup_context.lookup_aggregation_eval = Some(PointEvaluations {
960 zeta: aggreg.evaluate_chunks(zeta),
961 zeta_omega: aggreg.evaluate_chunks(zeta_omega),
962 });
963 lookup_context.lookup_table_eval = Some(PointEvaluations {
964 zeta: joint_table.evaluate_chunks(zeta),
965 zeta_omega: joint_table.evaluate_chunks(zeta_omega),
966 });
967 lookup_context.lookup_sorted_eval = array::from_fn(|i| {
968 if i < sorted.len() {
969 let sorted = &sorted[i];
970 Some(PointEvaluations {
971 zeta: sorted.evaluate_chunks(zeta),
972 zeta_omega: sorted.evaluate_chunks(zeta_omega),
973 })
974 } else {
975 None
976 }
977 });
978 lookup_context.runtime_lookup_table_eval =
979 lookup_context.runtime_table.as_ref().map(|runtime_table| {
980 let runtime_table =
981 runtime_table.to_chunked_polynomial(num_chunks, index.max_poly_size);
982 PointEvaluations {
983 zeta: runtime_table.evaluate_chunks(zeta),
984 zeta_omega: runtime_table.evaluate_chunks(zeta_omega),
985 }
986 });
987 }
988
989 internal_tracing::checkpoint!(internal_traces; lagrange_basis_eval_zeta_poly);
1009 let zeta_evals =
1010 LagrangeBasisEvaluations::new(index.max_poly_size, index.cs.domain.d1, zeta);
1011 internal_tracing::checkpoint!(internal_traces; lagrange_basis_eval_zeta_omega_poly);
1012 let zeta_omega_evals =
1013 LagrangeBasisEvaluations::new(index.max_poly_size, index.cs.domain.d1, zeta_omega);
1014
1015 let chunked_evals_for_selector =
1016 |p: &Evaluations<G::ScalarField, D<G::ScalarField>>| PointEvaluations {
1017 zeta: zeta_evals.evaluate_boolean(p),
1018 zeta_omega: zeta_omega_evals.evaluate_boolean(p),
1019 };
1020
1021 let chunked_evals_for_evaluations =
1022 |p: &Evaluations<G::ScalarField, D<G::ScalarField>>| PointEvaluations {
1023 zeta: zeta_evals.evaluate(p),
1024 zeta_omega: zeta_omega_evals.evaluate(p),
1025 };
1026
1027 internal_tracing::checkpoint!(internal_traces; chunk_eval_zeta_omega_poly);
1028 let chunked_evals = ProofEvaluations::<PointEvaluations<Vec<G::ScalarField>>> {
1029 public: {
1030 let chunked = public_poly.to_chunked_polynomial(num_chunks, index.max_poly_size);
1031 Some(PointEvaluations {
1032 zeta: chunked.evaluate_chunks(zeta),
1033 zeta_omega: chunked.evaluate_chunks(zeta_omega),
1034 })
1035 },
1036 s: array::from_fn(|i| {
1037 chunked_evals_for_evaluations(&column_evaluations.permutation_coefficients8[i])
1038 }),
1039 coefficients: array::from_fn(|i| {
1040 chunked_evals_for_evaluations(&column_evaluations.coefficients8[i])
1041 }),
1042 w: array::from_fn(|i| {
1043 let chunked =
1044 witness_poly[i].to_chunked_polynomial(num_chunks, index.max_poly_size);
1045 PointEvaluations {
1046 zeta: chunked.evaluate_chunks(zeta),
1047 zeta_omega: chunked.evaluate_chunks(zeta_omega),
1048 }
1049 }),
1050
1051 z: {
1052 let chunked = z_poly.to_chunked_polynomial(num_chunks, index.max_poly_size);
1053 PointEvaluations {
1054 zeta: chunked.evaluate_chunks(zeta),
1055 zeta_omega: chunked.evaluate_chunks(zeta_omega),
1056 }
1057 },
1058
1059 lookup_aggregation: lookup_context.lookup_aggregation_eval.take(),
1060 lookup_table: lookup_context.lookup_table_eval.take(),
1061 lookup_sorted: array::from_fn(|i| lookup_context.lookup_sorted_eval[i].take()),
1062 runtime_lookup_table: lookup_context.runtime_lookup_table_eval.take(),
1063 generic_selector: chunked_evals_for_selector(&column_evaluations.generic_selector4),
1064 poseidon_selector: chunked_evals_for_selector(&column_evaluations.poseidon_selector8),
1065 complete_add_selector: chunked_evals_for_selector(
1066 &column_evaluations.complete_add_selector4,
1067 ),
1068 mul_selector: chunked_evals_for_selector(&column_evaluations.mul_selector8),
1069 emul_selector: chunked_evals_for_selector(&column_evaluations.emul_selector8),
1070 endomul_scalar_selector: chunked_evals_for_selector(
1071 &column_evaluations.endomul_scalar_selector8,
1072 ),
1073
1074 range_check0_selector: column_evaluations
1075 .range_check0_selector8
1076 .as_ref()
1077 .map(chunked_evals_for_selector),
1078 range_check1_selector: column_evaluations
1079 .range_check1_selector8
1080 .as_ref()
1081 .map(chunked_evals_for_selector),
1082 foreign_field_add_selector: column_evaluations
1083 .foreign_field_add_selector8
1084 .as_ref()
1085 .map(chunked_evals_for_selector),
1086 foreign_field_mul_selector: column_evaluations
1087 .foreign_field_mul_selector8
1088 .as_ref()
1089 .map(chunked_evals_for_selector),
1090 xor_selector: column_evaluations
1091 .xor_selector8
1092 .as_ref()
1093 .map(chunked_evals_for_selector),
1094 rot_selector: column_evaluations
1095 .rot_selector8
1096 .as_ref()
1097 .map(chunked_evals_for_selector),
1098
1099 runtime_lookup_table_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1100 lcs.runtime_selector
1101 .as_ref()
1102 .map(chunked_evals_for_selector)
1103 }),
1104 xor_lookup_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1105 lcs.lookup_selectors
1106 .xor
1107 .as_ref()
1108 .map(chunked_evals_for_selector)
1109 }),
1110 lookup_gate_lookup_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1111 lcs.lookup_selectors
1112 .lookup
1113 .as_ref()
1114 .map(chunked_evals_for_selector)
1115 }),
1116 range_check_lookup_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1117 lcs.lookup_selectors
1118 .range_check
1119 .as_ref()
1120 .map(chunked_evals_for_selector)
1121 }),
1122 foreign_field_mul_lookup_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1123 lcs.lookup_selectors
1124 .ffmul
1125 .as_ref()
1126 .map(chunked_evals_for_selector)
1127 }),
1128 };
1129
1130 let zeta_to_srs_len = zeta.pow([index.max_poly_size as u64]);
1131 let zeta_omega_to_srs_len = zeta_omega.pow([index.max_poly_size as u64]);
1132 let zeta_to_domain_size = zeta.pow([d1_size as u64]);
1133
1134 let evals: ProofEvaluations<PointEvaluations<G::ScalarField>> = {
1137 let powers_of_eval_points_for_chunks = PointEvaluations {
1138 zeta: zeta_to_srs_len,
1139 zeta_omega: zeta_omega_to_srs_len,
1140 };
1141 chunked_evals.combine(&powers_of_eval_points_for_chunks)
1142 };
1143
1144 internal_tracing::checkpoint!(internal_traces; compute_ft_poly);
1147 let ft: DensePolynomial<G::ScalarField> = {
1148 let f_chunked = {
1149 let alphas =
1155 all_alphas.get_alphas(ArgumentType::Permutation, permutation::CONSTRAINTS);
1156 let f = index.perm_lnrz(&evals, zeta, beta, gamma, alphas);
1157
1158 let f = {
1160 let (_lin_constant, mut lin) =
1161 index.linearization.to_polynomial(&env, zeta, &evals);
1162 lin += &f;
1163 lin.interpolate()
1164 };
1165
1166 drop(env);
1173 drop(lagrange);
1174 lookup_context.joint_lookup_table_d8 = None;
1175 lookup_context.sorted8 = None;
1176 lookup_context.aggreg8 = None;
1177
1178 f.to_chunked_polynomial(num_chunks, index.max_poly_size)
1180 .linearize(zeta_to_srs_len)
1181 };
1182
1183 let t_chunked = quotient_poly
1184 .to_chunked_polynomial(7 * num_chunks, index.max_poly_size)
1185 .linearize(zeta_to_srs_len);
1186
1187 &f_chunked - &t_chunked.scale(zeta_to_domain_size - G::ScalarField::one())
1188 };
1189
1190 let blinding_ft = {
1193 let blinding_t = t_comm.blinders.chunk_blinding(zeta_to_srs_len);
1194 let blinding_f = G::ScalarField::zero();
1195
1196 PolyComm {
1197 chunks: vec![
1199 blinding_f - (zeta_to_domain_size - G::ScalarField::one()) * blinding_t,
1200 ],
1201 }
1202 };
1203
1204 internal_tracing::checkpoint!(internal_traces; ft_eval_zeta_omega);
1206 let ft_eval1 = ft.evaluate(&zeta_omega);
1207
1208 let fq_sponge_before_evaluations = fq_sponge.clone();
1210 let mut fr_sponge = EFrSponge::from(G::sponge_params());
1211
1212 fr_sponge.absorb(&fq_sponge.digest());
1214
1215 let prev_challenge_digest = {
1217 let mut fr_sponge = EFrSponge::from(G::sponge_params());
1220 for RecursionChallenge { chals, .. } in &prev_challenges {
1221 fr_sponge.absorb_multiple(chals);
1222 }
1223 fr_sponge.digest()
1224 };
1225 fr_sponge.absorb(&prev_challenge_digest);
1226
1227 internal_tracing::checkpoint!(internal_traces; build_polynomials);
1229 let polys = prev_challenges
1230 .iter()
1231 .map(|RecursionChallenge { chals, comm }| {
1232 (
1233 DensePolynomial::from_coefficients_vec(b_poly_coefficients(chals)),
1234 comm.len(),
1235 )
1236 })
1237 .collect::<Vec<_>>();
1238
1239 fr_sponge.absorb(&ft_eval1);
1241
1242 fr_sponge.absorb_multiple(&chunked_evals.public.as_ref().unwrap().zeta);
1250 fr_sponge.absorb_multiple(&chunked_evals.public.as_ref().unwrap().zeta_omega);
1251 fr_sponge.absorb_evaluations(&chunked_evals);
1252
1253 let v_chal = fr_sponge.challenge();
1255
1256 let v = v_chal.to_field(endo_r);
1258
1259 let u_chal = fr_sponge.challenge();
1261
1262 let u = u_chal.to_field(endo_r);
1264
1265 let non_hiding = |n_chunks: usize| PolyComm {
1269 chunks: vec![G::ScalarField::zero(); n_chunks],
1270 };
1271
1272 let fixed_hiding = |n_chunks: usize| PolyComm {
1273 chunks: vec![G::ScalarField::one(); n_chunks],
1274 };
1275
1276 let coefficients_form = DensePolynomialOrEvaluations::DensePolynomial;
1277 let evaluations_form = |e| DensePolynomialOrEvaluations::Evaluations(e, index.cs.domain.d1);
1278
1279 let mut polynomials = polys
1280 .iter()
1281 .map(|(p, n_chunks)| (coefficients_form(p), non_hiding(*n_chunks)))
1282 .collect::<Vec<_>>();
1283
1284 polynomials.push((coefficients_form(&public_poly), fixed_hiding(num_chunks)));
1293 polynomials.push((coefficients_form(&ft), blinding_ft));
1294 polynomials.push((coefficients_form(&z_poly), z_comm.blinders));
1295 polynomials.push((
1296 evaluations_form(&column_evaluations.generic_selector4),
1297 fixed_hiding(num_chunks),
1298 ));
1299 polynomials.push((
1300 evaluations_form(&column_evaluations.poseidon_selector8),
1301 fixed_hiding(num_chunks),
1302 ));
1303 polynomials.push((
1304 evaluations_form(&column_evaluations.complete_add_selector4),
1305 fixed_hiding(num_chunks),
1306 ));
1307 polynomials.push((
1308 evaluations_form(&column_evaluations.mul_selector8),
1309 fixed_hiding(num_chunks),
1310 ));
1311 polynomials.push((
1312 evaluations_form(&column_evaluations.emul_selector8),
1313 fixed_hiding(num_chunks),
1314 ));
1315 polynomials.push((
1316 evaluations_form(&column_evaluations.endomul_scalar_selector8),
1317 fixed_hiding(num_chunks),
1318 ));
1319 polynomials.extend(
1320 witness_poly
1321 .iter()
1322 .zip(w_comm.iter())
1323 .map(|(w, c)| (coefficients_form(w), c.blinders.clone()))
1324 .collect::<Vec<_>>(),
1325 );
1326 polynomials.extend(
1327 column_evaluations
1328 .coefficients8
1329 .iter()
1330 .map(|coefficientm| (evaluations_form(coefficientm), non_hiding(num_chunks)))
1331 .collect::<Vec<_>>(),
1332 );
1333 polynomials.extend(
1334 column_evaluations.permutation_coefficients8[0..PERMUTS - 1]
1335 .iter()
1336 .map(|w| (evaluations_form(w), non_hiding(num_chunks)))
1337 .collect::<Vec<_>>(),
1338 );
1339
1340 if let Some(range_check0_selector8) = &column_evaluations.range_check0_selector8 {
1342 polynomials.push((
1343 evaluations_form(range_check0_selector8),
1344 non_hiding(num_chunks),
1345 ));
1346 }
1347 if let Some(range_check1_selector8) = &column_evaluations.range_check1_selector8 {
1348 polynomials.push((
1349 evaluations_form(range_check1_selector8),
1350 non_hiding(num_chunks),
1351 ));
1352 }
1353 if let Some(foreign_field_add_selector8) = &column_evaluations.foreign_field_add_selector8 {
1354 polynomials.push((
1355 evaluations_form(foreign_field_add_selector8),
1356 non_hiding(num_chunks),
1357 ));
1358 }
1359 if let Some(foreign_field_mul_selector8) = &column_evaluations.foreign_field_mul_selector8 {
1360 polynomials.push((
1361 evaluations_form(foreign_field_mul_selector8),
1362 non_hiding(num_chunks),
1363 ));
1364 }
1365 if let Some(xor_selector8) = &column_evaluations.xor_selector8 {
1366 polynomials.push((evaluations_form(xor_selector8), non_hiding(num_chunks)));
1367 }
1368 if let Some(rot_selector8) = &column_evaluations.rot_selector8 {
1369 polynomials.push((evaluations_form(rot_selector8), non_hiding(num_chunks)));
1370 }
1371
1372 if let Some(lcs) = lookup_constraint_system {
1375 let sorted_poly = lookup_context.sorted_coeffs.as_ref().unwrap();
1377 let sorted_comms = lookup_context.sorted_comms.as_ref().unwrap();
1378
1379 for (poly, comm) in sorted_poly.iter().zip(sorted_comms) {
1380 polynomials.push((coefficients_form(poly), comm.blinders.clone()));
1381 }
1382
1383 let aggreg_poly = lookup_context.aggreg_coeffs.as_ref().unwrap();
1385 let aggreg_comm = lookup_context.aggreg_comm.as_ref().unwrap();
1386 polynomials.push((coefficients_form(aggreg_poly), aggreg_comm.blinders.clone()));
1387
1388 let table_blinding = {
1390 let joint_combiner = lookup_context.joint_combiner.as_ref().unwrap();
1391 let table_id_combiner = lookup_context.table_id_combiner.as_ref().unwrap();
1392 let max_fixed_lookup_table_size = {
1393 lcs.lookup_table8.len()
1401 };
1402 let base_blinding = {
1403 let fixed_table_blinding = if max_fixed_lookup_table_size == 0 {
1404 G::ScalarField::zero()
1405 } else {
1406 (1..max_fixed_lookup_table_size).fold(G::ScalarField::one(), |acc, _| {
1407 G::ScalarField::one() + *joint_combiner * acc
1408 })
1409 };
1410 fixed_table_blinding + *table_id_combiner
1411 };
1412 if lcs.runtime_selector.is_some() {
1413 let runtime_comm = lookup_context.runtime_table_comm.as_ref().unwrap();
1414
1415 let chunks = runtime_comm
1416 .blinders
1417 .into_iter()
1418 .map(|blinding| *joint_combiner * *blinding + base_blinding)
1419 .collect();
1420
1421 PolyComm::new(chunks)
1422 } else {
1423 let chunks = vec![base_blinding; num_chunks];
1424 PolyComm::new(chunks)
1425 }
1426 };
1427
1428 let joint_lookup_table = lookup_context.joint_lookup_table.as_ref().unwrap();
1429
1430 polynomials.push((coefficients_form(joint_lookup_table), table_blinding));
1431
1432 if lcs.runtime_selector.is_some() {
1434 let runtime_table_comm = lookup_context.runtime_table_comm.as_ref().unwrap();
1435 let runtime_table = lookup_context.runtime_table.as_ref().unwrap();
1436
1437 polynomials.push((
1438 coefficients_form(runtime_table),
1439 runtime_table_comm.blinders.clone(),
1440 ));
1441 }
1442
1443 if let Some(runtime_lookup_table_selector) = &lcs.runtime_selector {
1446 polynomials.push((
1447 evaluations_form(runtime_lookup_table_selector),
1448 non_hiding(num_chunks),
1449 ))
1450 }
1451 if let Some(xor_lookup_selector) = &lcs.lookup_selectors.xor {
1452 polynomials.push((
1453 evaluations_form(xor_lookup_selector),
1454 non_hiding(num_chunks),
1455 ))
1456 }
1457 if let Some(lookup_gate_selector) = &lcs.lookup_selectors.lookup {
1458 polynomials.push((
1459 evaluations_form(lookup_gate_selector),
1460 non_hiding(num_chunks),
1461 ))
1462 }
1463 if let Some(range_check_lookup_selector) = &lcs.lookup_selectors.range_check {
1464 polynomials.push((
1465 evaluations_form(range_check_lookup_selector),
1466 non_hiding(num_chunks),
1467 ))
1468 }
1469 if let Some(foreign_field_mul_lookup_selector) = &lcs.lookup_selectors.ffmul {
1470 polynomials.push((
1471 evaluations_form(foreign_field_mul_lookup_selector),
1472 non_hiding(num_chunks),
1473 ))
1474 }
1475 }
1476
1477 internal_tracing::checkpoint!(internal_traces; create_aggregated_ipa);
1479 let proof = OpenProof::open(
1480 &*index.srs,
1481 group_map,
1482 &polynomials,
1483 &[zeta, zeta_omega],
1484 v,
1485 u,
1486 fq_sponge_before_evaluations,
1487 rng,
1488 );
1489
1490 let lookup = lookup_context
1491 .aggreg_comm
1492 .zip(lookup_context.sorted_comms)
1493 .map(|(a, s)| LookupCommitments {
1494 aggreg: a.commitment,
1495 sorted: s.iter().map(|c| c.commitment.clone()).collect(),
1496 runtime: lookup_context.runtime_table_comm.map(|x| x.commitment),
1497 });
1498
1499 let proof = Self {
1500 commitments: ProverCommitments {
1501 w_comm: array::from_fn(|i| w_comm[i].commitment.clone()),
1502 z_comm: z_comm.commitment,
1503 t_comm: t_comm.commitment,
1504 lookup,
1505 },
1506 proof,
1507 evals: chunked_evals,
1508 ft_eval1,
1509 prev_challenges,
1510 };
1511
1512 internal_tracing::checkpoint!(internal_traces; create_recursive_done);
1513
1514 Ok(proof)
1515 }
1516}
1517
1518internal_tracing::decl_traces!(internal_traces;
1519 pasta_fp_plonk_proof_create,
1520 pasta_fq_plonk_proof_create,
1521 create_recursive,
1522 pad_witness,
1523 set_up_fq_sponge,
1524 commit_to_witness_columns,
1525 use_lookup,
1526 z_permutation_aggregation_polynomial,
1527 eval_witness_polynomials_over_domains,
1528 compute_index_evals,
1529 compute_quotient_poly,
1530 lagrange_basis_eval_zeta_poly,
1531 lagrange_basis_eval_zeta_omega_poly,
1532 chunk_eval_zeta_omega_poly,
1533 compute_ft_poly,
1534 ft_eval_zeta_omega,
1535 build_polynomials,
1536 create_aggregated_ipa,
1537 create_recursive_done);
1538
1539#[cfg(feature = "ocaml_types")]
1540pub mod caml {
1541 use super::*;
1542 use crate::proof::caml::{CamlProofEvaluations, CamlRecursionChallenge};
1543 use ark_ec::AffineRepr;
1544 use poly_commitment::{
1545 commitment::caml::CamlPolyComm,
1546 ipa::{caml::CamlOpeningProof, OpeningProof},
1547 };
1548
1549 #[cfg(feature = "internal_tracing")]
1550 pub use internal_traces::caml::CamlTraces as CamlProverTraces;
1551
1552 #[derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
1553 pub struct CamlProofWithPublic<CamlG, CamlF> {
1554 pub public_evals: Option<PointEvaluations<Vec<CamlF>>>,
1555 pub proof: CamlProverProof<CamlG, CamlF>,
1556 }
1557
1558 #[derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
1563 pub struct CamlProverProof<CamlG, CamlF> {
1564 pub commitments: CamlProverCommitments<CamlG>,
1565 pub proof: CamlOpeningProof<CamlG, CamlF>,
1566 pub evals: CamlProofEvaluations<CamlF>,
1568 pub ft_eval1: CamlF,
1569 pub public: Vec<CamlF>,
1570 pub prev_challenges: Vec<CamlRecursionChallenge<CamlG, CamlF>>,
1572 }
1573
1574 #[derive(Clone, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
1579 pub struct CamlLookupCommitments<CamlG> {
1580 pub sorted: Vec<CamlPolyComm<CamlG>>,
1581 pub aggreg: CamlPolyComm<CamlG>,
1582 pub runtime: Option<CamlPolyComm<CamlG>>,
1583 }
1584
1585 #[allow(clippy::type_complexity)]
1586 #[derive(Clone, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
1587 pub struct CamlProverCommitments<CamlG> {
1588 pub w_comm: (
1590 CamlPolyComm<CamlG>,
1591 CamlPolyComm<CamlG>,
1592 CamlPolyComm<CamlG>,
1593 CamlPolyComm<CamlG>,
1594 CamlPolyComm<CamlG>,
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 ),
1606 pub z_comm: CamlPolyComm<CamlG>,
1607 pub t_comm: CamlPolyComm<CamlG>,
1608 pub lookup: Option<CamlLookupCommitments<CamlG>>,
1609 }
1610
1611 impl<G, CamlG> From<LookupCommitments<G>> for CamlLookupCommitments<CamlG>
1633 where
1634 G: AffineRepr,
1635 CamlPolyComm<CamlG>: From<PolyComm<G>>,
1636 {
1637 fn from(
1638 LookupCommitments {
1639 aggreg,
1640 sorted,
1641 runtime,
1642 }: LookupCommitments<G>,
1643 ) -> Self {
1644 Self {
1645 aggreg: aggreg.into(),
1646 sorted: sorted.into_iter().map(Into::into).collect(),
1647 runtime: runtime.map(Into::into),
1648 }
1649 }
1650 }
1651
1652 impl<G, CamlG> From<CamlLookupCommitments<CamlG>> for LookupCommitments<G>
1653 where
1654 G: AffineRepr,
1655 PolyComm<G>: From<CamlPolyComm<CamlG>>,
1656 {
1657 fn from(
1658 CamlLookupCommitments {
1659 aggreg,
1660 sorted,
1661 runtime,
1662 }: CamlLookupCommitments<CamlG>,
1663 ) -> LookupCommitments<G> {
1664 LookupCommitments {
1665 aggreg: aggreg.into(),
1666 sorted: sorted.into_iter().map(Into::into).collect(),
1667 runtime: runtime.map(Into::into),
1668 }
1669 }
1670 }
1671
1672 impl<G, CamlG> From<ProverCommitments<G>> for CamlProverCommitments<CamlG>
1677 where
1678 G: AffineRepr,
1679 CamlPolyComm<CamlG>: From<PolyComm<G>>,
1680 {
1681 fn from(prover_comm: ProverCommitments<G>) -> Self {
1682 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] =
1683 prover_comm.w_comm;
1684 Self {
1685 w_comm: (
1686 w_comm0.into(),
1687 w_comm1.into(),
1688 w_comm2.into(),
1689 w_comm3.into(),
1690 w_comm4.into(),
1691 w_comm5.into(),
1692 w_comm6.into(),
1693 w_comm7.into(),
1694 w_comm8.into(),
1695 w_comm9.into(),
1696 w_comm10.into(),
1697 w_comm11.into(),
1698 w_comm12.into(),
1699 w_comm13.into(),
1700 w_comm14.into(),
1701 ),
1702 z_comm: prover_comm.z_comm.into(),
1703 t_comm: prover_comm.t_comm.into(),
1704 lookup: prover_comm.lookup.map(Into::into),
1705 }
1706 }
1707 }
1708
1709 impl<G, CamlG> From<CamlProverCommitments<CamlG>> for ProverCommitments<G>
1710 where
1711 G: AffineRepr,
1712 PolyComm<G>: From<CamlPolyComm<CamlG>>,
1713 {
1714 fn from(caml_prover_comm: CamlProverCommitments<CamlG>) -> ProverCommitments<G> {
1715 let (
1716 w_comm0,
1717 w_comm1,
1718 w_comm2,
1719 w_comm3,
1720 w_comm4,
1721 w_comm5,
1722 w_comm6,
1723 w_comm7,
1724 w_comm8,
1725 w_comm9,
1726 w_comm10,
1727 w_comm11,
1728 w_comm12,
1729 w_comm13,
1730 w_comm14,
1731 ) = caml_prover_comm.w_comm;
1732 ProverCommitments {
1733 w_comm: [
1734 w_comm0.into(),
1735 w_comm1.into(),
1736 w_comm2.into(),
1737 w_comm3.into(),
1738 w_comm4.into(),
1739 w_comm5.into(),
1740 w_comm6.into(),
1741 w_comm7.into(),
1742 w_comm8.into(),
1743 w_comm9.into(),
1744 w_comm10.into(),
1745 w_comm11.into(),
1746 w_comm12.into(),
1747 w_comm13.into(),
1748 w_comm14.into(),
1749 ],
1750 z_comm: caml_prover_comm.z_comm.into(),
1751 t_comm: caml_prover_comm.t_comm.into(),
1752 lookup: caml_prover_comm.lookup.map(Into::into),
1753 }
1754 }
1755 }
1756
1757 #[allow(type_alias_bounds)]
1764 type ProofTuple<const FULL_ROUNDS: usize, G: AffineRepr> = (
1765 ProverProof<G, OpeningProof<G, FULL_ROUNDS>, FULL_ROUNDS>,
1766 Vec<<G as AffineRepr>::ScalarField>,
1767 );
1768
1769 impl<const FULL_ROUNDS: usize, G, CamlG, CamlF> From<ProofTuple<FULL_ROUNDS, G>>
1770 for CamlProofWithPublic<CamlG, CamlF>
1771 where
1772 G: AffineRepr,
1773 G::BaseField: PrimeField,
1774 G: poly_commitment::commitment::EndoCurve,
1775 CamlG: From<G>,
1776 CamlF: From<G::ScalarField>,
1777 {
1778 fn from(pp: ProofTuple<FULL_ROUNDS, G>) -> Self {
1779 let (public_evals, evals) = pp.0.evals.into();
1780 CamlProofWithPublic {
1781 public_evals,
1782 proof: CamlProverProof {
1783 commitments: pp.0.commitments.into(),
1784 proof: pp.0.proof.into(),
1785 evals,
1786 ft_eval1: pp.0.ft_eval1.into(),
1787 public: pp.1.into_iter().map(Into::into).collect(),
1788 prev_challenges: pp.0.prev_challenges.into_iter().map(Into::into).collect(),
1789 },
1790 }
1791 }
1792 }
1793
1794 impl<const FULL_ROUNDS: usize, G, CamlG, CamlF> From<CamlProofWithPublic<CamlG, CamlF>>
1795 for ProofTuple<FULL_ROUNDS, G>
1796 where
1797 CamlF: Clone,
1798 G: AffineRepr + From<CamlG>,
1799 G::BaseField: PrimeField,
1800 G: poly_commitment::commitment::EndoCurve,
1801 G::ScalarField: From<CamlF>,
1802 {
1803 fn from(caml_pp: CamlProofWithPublic<CamlG, CamlF>) -> Self {
1804 let CamlProofWithPublic {
1805 public_evals,
1806 proof: caml_pp,
1807 } = caml_pp;
1808 let proof = ProverProof {
1809 commitments: caml_pp.commitments.into(),
1810 proof: caml_pp.proof.into(),
1811 evals: (public_evals, caml_pp.evals).into(),
1812 ft_eval1: caml_pp.ft_eval1.into(),
1813 prev_challenges: caml_pp
1814 .prev_challenges
1815 .into_iter()
1816 .map(Into::into)
1817 .collect(),
1818 };
1819
1820 (proof, caml_pp.public.into_iter().map(Into::into).collect())
1821 }
1822 }
1823}
1824
1825#[cfg(test)]
1826mod tests {
1827 use super::*;
1828 use mina_curves::pasta::Fp;
1829
1830 #[test]
1834 fn blinders_are_drawn_per_chunk() {
1835 let mut rng = o1_utils::tests::make_test_rng(None);
1836 let blinder = blinder::<Fp, _>(4, &mut rng);
1837
1838 assert_eq!(blinder.chunks.len(), 4);
1839 for (i, a) in blinder.chunks.iter().enumerate() {
1840 for b in blinder.chunks.iter().skip(i + 1) {
1841 assert_ne!(a, b, "two chunks share a blinder");
1842 }
1843 }
1844 }
1845}