kimchi/
prover.rs

1//! This module implements prover's zk-proof primitive.
2
3use 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    curve::KimchiCurve,
27    error::ProverError,
28    lagrange_basis_evaluations::LagrangeBasisEvaluations,
29    plonk_sponge::FrSponge,
30    proof::{
31        LookupCommitments, PointEvaluations, ProofEvaluations, ProverCommitments, ProverProof,
32        RecursionChallenge,
33    },
34    prover_index::ProverIndex,
35    verifier_index::VerifierIndex,
36};
37use ark_ff::{FftField, Field, One, PrimeField, UniformRand, Zero};
38use ark_poly::{
39    univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Evaluations, Polynomial,
40    Radix2EvaluationDomain as D,
41};
42use core::array;
43use itertools::Itertools;
44use mina_poseidon::{poseidon::ArithmeticSpongeParams, sponge::ScalarChallenge, FqSponge};
45use o1_utils::ExtendedDensePolynomial as _;
46use poly_commitment::{
47    commitment::{
48        absorb_commitment, b_poly_coefficients, BlindedCommitment, CommitmentCurve, PolyComm,
49    },
50    utils::DensePolynomialOrEvaluations,
51    OpenProof, SRS as _,
52};
53use rand_core::{CryptoRng, RngCore};
54use rayon::prelude::*;
55use std::collections::HashMap;
56
57/// The result of a proof creation or verification.
58type Result<T> = core::result::Result<T, ProverError>;
59
60/// Helper to quickly test if a witness satisfies a constraint
61macro_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
77/// Contains variables needed for lookup in the prover algorithm.
78#[derive(Default)]
79struct LookupContext<G, F>
80where
81    G: CommitmentCurve,
82    F: FftField,
83{
84    /// The joint combiner used to join the columns of lookup tables
85    joint_combiner: Option<F>,
86
87    /// The power of the joint_combiner that can be used to add a table_id column
88    /// to the concatenated lookup tables.
89    table_id_combiner: Option<F>,
90
91    /// The combined lookup entry that can be used as dummy value
92    dummy_lookup_value: Option<F>,
93
94    /// The combined lookup table
95    joint_lookup_table: Option<DensePolynomial<F>>,
96    joint_lookup_table_d8: Option<Evaluations<F, D<F>>>,
97
98    /// The sorted polynomials `s` in different forms
99    sorted: Option<Vec<Evaluations<F, D<F>>>>,
100    sorted_coeffs: Option<Vec<DensePolynomial<F>>>,
101    sorted_comms: Option<Vec<BlindedCommitment<G>>>,
102    sorted8: Option<Vec<Evaluations<F, D<F>>>>,
103
104    /// The aggregation polynomial in different forms
105    aggreg_coeffs: Option<DensePolynomial<F>>,
106    aggreg_comm: Option<BlindedCommitment<G>>,
107    aggreg8: Option<Evaluations<F, D<F>>>,
108
109    // lookup-related evaluations
110    /// evaluation of lookup aggregation polynomial
111    pub lookup_aggregation_eval: Option<PointEvaluations<Vec<F>>>,
112    /// evaluation of lookup table polynomial
113    pub lookup_table_eval: Option<PointEvaluations<Vec<F>>>,
114    /// evaluation of lookup sorted polynomials
115    pub lookup_sorted_eval: [Option<PointEvaluations<Vec<F>>>; 5],
116    /// evaluation of runtime lookup table polynomial
117    pub runtime_lookup_table_eval: Option<PointEvaluations<Vec<F>>>,
118
119    /// Runtime table
120    runtime_table: Option<DensePolynomial<F>>,
121    runtime_table_d8: Option<Evaluations<F, D<F>>>,
122    runtime_table_comm: Option<BlindedCommitment<G>>,
123    runtime_second_col_d8: Option<Evaluations<F, D<F>>>,
124}
125
126impl<G, OpeningProof, const FULL_ROUNDS: usize> ProverProof<G, OpeningProof, FULL_ROUNDS>
127where
128    G: KimchiCurve<FULL_ROUNDS>,
129    G::BaseField: PrimeField,
130    OpeningProof: OpenProof<G, FULL_ROUNDS>,
131{
132    /// This function constructs prover's zk-proof from the witness & the `ProverIndex` against SRS instance
133    ///
134    /// # Errors
135    ///
136    /// Will give error if `create_recursive` process fails.
137    pub fn create<EFqSponge, EFrSponge, RNG>(
138        groupmap: &G::Map,
139        witness: [Vec<G::ScalarField>; COLUMNS],
140        runtime_tables: &[RuntimeTable<G::ScalarField>],
141        index: &ProverIndex<FULL_ROUNDS, G, OpeningProof::SRS>,
142        rng: &mut RNG,
143    ) -> Result<Self>
144    where
145        EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField, FULL_ROUNDS>,
146        EFrSponge: FrSponge<G::ScalarField>,
147        EFrSponge: From<&'static ArithmeticSpongeParams<G::ScalarField, FULL_ROUNDS>>,
148        RNG: RngCore + CryptoRng,
149        VerifierIndex<FULL_ROUNDS, G, OpeningProof::SRS>: Clone,
150    {
151        Self::create_recursive::<EFqSponge, EFrSponge, RNG>(
152            groupmap,
153            witness,
154            runtime_tables,
155            index,
156            Vec::new(),
157            None,
158            rng,
159        )
160    }
161
162    /// This function constructs prover's recursive zk-proof from the witness &
163    /// the `ProverIndex` against SRS instance
164    ///
165    /// # Errors
166    ///
167    /// Will give error if inputs(like `lookup_context.joint_lookup_table_d8`)
168    /// are None.
169    ///
170    /// # Panics
171    ///
172    /// Will panic if `lookup_context.joint_lookup_table_d8` is None.
173    pub fn create_recursive<EFqSponge, EFrSponge, RNG>(
174        group_map: &G::Map,
175        mut witness: [Vec<G::ScalarField>; COLUMNS],
176        runtime_tables: &[RuntimeTable<G::ScalarField>],
177        index: &ProverIndex<FULL_ROUNDS, G, OpeningProof::SRS>,
178        prev_challenges: Vec<RecursionChallenge<G>>,
179        blinders: Option<[Option<PolyComm<G::ScalarField>>; COLUMNS]>,
180        rng: &mut RNG,
181    ) -> Result<Self>
182    where
183        EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField, FULL_ROUNDS>,
184        EFrSponge: FrSponge<G::ScalarField>,
185        EFrSponge: From<&'static ArithmeticSpongeParams<G::ScalarField, FULL_ROUNDS>>,
186        RNG: RngCore + CryptoRng,
187        VerifierIndex<FULL_ROUNDS, G, OpeningProof::SRS>: Clone,
188    {
189        internal_tracing::checkpoint!(internal_traces; create_recursive);
190        let d1_size = index.cs.domain.d1.size();
191
192        let (_, endo_r) = G::endos();
193
194        let num_chunks = if d1_size < index.max_poly_size {
195            1
196        } else {
197            d1_size / index.max_poly_size
198        };
199
200        // Verify the circuit satisfiability by the computed witness (baring plookup constraints)
201        // Catch mistakes before proof generation.
202        if cfg!(debug_assertions) && !index.cs.disable_gates_checks {
203            let public = witness[0][0..index.cs.public].to_vec();
204            index.verify(&witness, &public).expect("incorrect witness");
205        }
206
207        //~ 1. Ensure we have room in the witness for the zero-knowledge rows.
208        //~    We currently expect the witness not to be of the same length as the domain,
209        //~    but instead be of the length of the (smaller) circuit.
210        //~    If we cannot add `zk_rows` rows to the columns of the witness before reaching
211        //~    the size of the domain, abort.
212        let length_witness = witness[0].len();
213        let length_padding = d1_size
214            .checked_sub(length_witness)
215            .ok_or(ProverError::NoRoomForZkInWitness)?;
216
217        let zero_knowledge_limit = zk_rows_strict_lower_bound(num_chunks);
218        // Because the lower bound is strict, the result of the function above
219        // is not a sufficient number of zero knowledge rows, so the error must
220        // be raised anytime the number of zero knowledge rows is not greater
221        // than the strict lower bound.
222        // Example:
223        //   for 1 chunk, `zero_knowledge_limit` is 2, and we need at least 3,
224        //   thus the error should be raised and the message should say that the
225        //   expected number of zero knowledge rows is 3 (hence the + 1).
226        if (index.cs.zk_rows as usize) <= zero_knowledge_limit {
227            return Err(ProverError::NotZeroKnowledge(
228                zero_knowledge_limit + 1,
229                index.cs.zk_rows as usize,
230            ));
231        }
232
233        if length_padding < index.cs.zk_rows as usize {
234            return Err(ProverError::NoRoomForZkInWitness);
235        }
236
237        //~ 1. Pad the witness columns with Zero gates to make them the same length as the domain.
238        //~    Then, randomize the last `zk_rows` of each columns.
239        internal_tracing::checkpoint!(internal_traces; pad_witness);
240        for w in &mut witness {
241            if w.len() != length_witness {
242                return Err(ProverError::WitnessCsInconsistent);
243            }
244
245            // padding
246            w.extend(std::iter::repeat_n(G::ScalarField::zero(), length_padding));
247
248            // zk-rows
249            for row in w.iter_mut().rev().take(index.cs.zk_rows as usize) {
250                *row = <G::ScalarField as UniformRand>::rand(rng);
251            }
252        }
253
254        //~ 1. Setup the Fq-Sponge.
255        internal_tracing::checkpoint!(internal_traces; set_up_fq_sponge);
256        let mut fq_sponge = EFqSponge::new(G::other_curve_sponge_params());
257
258        //~ 1. Absorb the digest of the VerifierIndex.
259        let verifier_index_digest = index.verifier_index_digest::<EFqSponge>();
260        fq_sponge.absorb_fq(&[verifier_index_digest]);
261
262        //~ 1. Absorb the commitments of the previous challenges with the Fq-sponge.
263        for RecursionChallenge { comm, .. } in &prev_challenges {
264            absorb_commitment(&mut fq_sponge, comm)
265        }
266
267        //~ 1. Compute the negated public input polynomial as
268        //~    the polynomial that evaluates to $-p_i$ for the first `public_input_size` values of the domain,
269        //~    and $0$ for the rest.
270        let public = witness[0][0..index.cs.public].to_vec();
271        let public_poly = -Evaluations::<G::ScalarField, D<G::ScalarField>>::from_vec_and_domain(
272            public,
273            index.cs.domain.d1,
274        )
275        .interpolate();
276
277        //~ 1. Commit (non-hiding) to the negated public input polynomial.
278        let public_comm = index.srs.commit_non_hiding(&public_poly, num_chunks);
279        let public_comm = {
280            index
281                .srs
282                .mask_custom(
283                    public_comm.clone(),
284                    &public_comm.map(|_| G::ScalarField::one()),
285                )
286                .unwrap()
287                .commitment
288        };
289
290        //~ 1. Absorb the commitment to the public polynomial with the Fq-Sponge.
291        //~
292        //~    Note: unlike the original PLONK protocol,
293        //~    the prover also provides evaluations of the public polynomial to help the verifier circuit.
294        //~    This is why we need to absorb the commitment to the public polynomial at this point.
295        absorb_commitment(&mut fq_sponge, &public_comm);
296
297        //~ 1. Commit to the witness columns by creating `COLUMNS` hiding commitments.
298        //~
299        //~    Note: since the witness is in evaluation form,
300        //~    we can use the `commit_evaluation` optimization.
301        internal_tracing::checkpoint!(internal_traces; commit_to_witness_columns);
302        // generate blinders if not given externally
303        let blinders_final: Vec<PolyComm<G::ScalarField>> = match blinders {
304            None => (0..COLUMNS)
305                .map(|_| PolyComm::new(vec![UniformRand::rand(rng); num_chunks]))
306                .collect(),
307            Some(blinders_arr) => blinders_arr
308                .into_iter()
309                .map(|blinder_el| match blinder_el {
310                    None => PolyComm::new(vec![UniformRand::rand(rng); num_chunks]),
311                    Some(blinder_el_some) => blinder_el_some,
312                })
313                .collect(),
314        };
315        let w_comm_opt_res: Vec<Result<_>> = witness
316            .clone()
317            .into_par_iter()
318            .zip(blinders_final.into_par_iter())
319            .map(|(witness, blinder)| {
320                let witness_eval =
321                    Evaluations::<G::ScalarField, D<G::ScalarField>>::from_vec_and_domain(
322                        witness,
323                        index.cs.domain.d1,
324                    );
325
326                // TODO: make this a function rather no? mask_with_custom()
327                let witness_com = index
328                    .srs
329                    .commit_evaluations_non_hiding(index.cs.domain.d1, &witness_eval);
330                let com = index
331                    .srs
332                    .mask_custom(witness_com, &blinder)
333                    .map_err(ProverError::WrongBlinders)?;
334
335                Ok(com)
336            })
337            .collect();
338
339        let w_comm_res: Result<Vec<BlindedCommitment<G>>> = w_comm_opt_res.into_iter().collect();
340
341        let w_comm = w_comm_res?;
342
343        let w_comm: [BlindedCommitment<G>; COLUMNS] = w_comm
344            .try_into()
345            .expect("previous loop is of the correct length");
346
347        //~ 1. Absorb the witness commitments with the Fq-Sponge.
348        w_comm
349            .iter()
350            .for_each(|c| absorb_commitment(&mut fq_sponge, &c.commitment));
351
352        //~ 1. Compute the witness polynomials by interpolating each `COLUMNS` of the witness.
353        //~    As mentioned above, we commit using the evaluations form rather than the coefficients
354        //~    form so we can take advantage of the sparsity of the evaluations (i.e., there are many
355        //~    0 entries and entries that have less-than-full-size field elemnts.)
356        let witness_poly: [DensePolynomial<G::ScalarField>; COLUMNS] = (0..COLUMNS)
357            .into_par_iter()
358            .map(|i| {
359                Evaluations::<G::ScalarField, D<G::ScalarField>>::from_vec_and_domain(
360                    witness[i].clone(),
361                    index.cs.domain.d1,
362                )
363                .interpolate()
364            })
365            .collect::<Vec<_>>()
366            .try_into()
367            .unwrap();
368
369        let mut lookup_context = LookupContext::default();
370
371        //~ 1. If using lookup:
372        let lookup_constraint_system = index
373            .cs
374            .lookup_constraint_system
375            .try_get_or_err()
376            .map_err(ProverError::from)?;
377        if let Some(lcs) = lookup_constraint_system {
378            internal_tracing::checkpoint!(internal_traces; use_lookup, {
379                "uses_lookup": true,
380                "uses_runtime_tables": lcs.runtime_tables.is_some(),
381            });
382            //~~ * if using runtime table:
383            if let Some(cfg_runtime_tables) = &lcs.runtime_tables {
384                //~~~ * check that all the provided runtime tables have length and IDs that match the runtime table configuration of the index
385                //~~~   we expect the given runtime tables to be sorted as configured, this makes it easier afterwards
386                let expected_runtime: Vec<_> = cfg_runtime_tables
387                    .iter()
388                    .map(|rt| (rt.id, rt.len))
389                    .collect();
390                let runtime: Vec<_> = runtime_tables
391                    .iter()
392                    .map(|rt| (rt.id, rt.data.len()))
393                    .collect();
394                if expected_runtime != runtime {
395                    return Err(ProverError::RuntimeTablesInconsistent);
396                }
397
398                //~~~ * calculate the contribution to the second column of the lookup table
399                //~~~   (the runtime vector)
400                let (runtime_table_contribution, runtime_table_contribution_d8) = {
401                    let mut offset = lcs
402                        .runtime_table_offset
403                        .expect("runtime configuration missing offset");
404
405                    let mut evals = vec![G::ScalarField::zero(); d1_size];
406                    for rt in runtime_tables {
407                        let range = offset..(offset + rt.data.len());
408                        evals[range].copy_from_slice(&rt.data);
409                        offset += rt.data.len();
410                    }
411
412                    // zero-knowledge
413                    for e in evals.iter_mut().rev().take(index.cs.zk_rows as usize) {
414                        *e = <G::ScalarField as UniformRand>::rand(rng);
415                    }
416
417                    // get coeff and evaluation form
418                    let runtime_table_contribution =
419                        Evaluations::from_vec_and_domain(evals, index.cs.domain.d1).interpolate();
420
421                    let runtime_table_contribution_d8 =
422                        runtime_table_contribution.evaluate_over_domain_by_ref(index.cs.domain.d8);
423
424                    (runtime_table_contribution, runtime_table_contribution_d8)
425                };
426
427                // commit the runtime polynomial
428                // (and save it to the proof)
429                let runtime_table_comm =
430                    index
431                        .srs
432                        .commit(&runtime_table_contribution, num_chunks, rng);
433
434                // absorb the commitment
435                absorb_commitment(&mut fq_sponge, &runtime_table_comm.commitment);
436
437                // pre-compute the updated second column of the lookup table
438                let mut second_column_d8 = runtime_table_contribution_d8.clone();
439                second_column_d8
440                    .evals
441                    .par_iter_mut()
442                    .enumerate()
443                    .for_each(|(row, e)| {
444                        *e += lcs.lookup_table8[1][row];
445                    });
446
447                lookup_context.runtime_table = Some(runtime_table_contribution);
448                lookup_context.runtime_table_d8 = Some(runtime_table_contribution_d8);
449                lookup_context.runtime_table_comm = Some(runtime_table_comm);
450                lookup_context.runtime_second_col_d8 = Some(second_column_d8);
451            }
452
453            //~~ * If queries involve a lookup table with multiple columns
454            //~~   then squeeze the Fq-Sponge to obtain the joint combiner challenge $j'$,
455            //~~   otherwise set the joint combiner challenge $j'$ to $0$.
456            let joint_combiner = if lcs.configuration.lookup_info.features.joint_lookup_used {
457                fq_sponge.challenge()
458            } else {
459                G::ScalarField::zero()
460            };
461
462            //~~ * Derive the scalar joint combiner $j$ from $j'$ using the endomorphism (TODO: specify)
463            let joint_combiner: G::ScalarField = ScalarChallenge(joint_combiner).to_field(endo_r);
464
465            //~~ * If multiple lookup tables are involved,
466            //~~   set the `table_id_combiner` as the $j^i$ with $i$ the maximum width of any used table.
467            //~~   Essentially, this is to add a last column of table ids to the concatenated lookup tables.
468            let table_id_combiner: G::ScalarField = if lcs.table_ids8.as_ref().is_some() {
469                joint_combiner.pow([lcs.configuration.lookup_info.max_joint_size as u64])
470            } else {
471                // TODO: just set this to None in case multiple tables are not used
472                G::ScalarField::zero()
473            };
474            lookup_context.table_id_combiner = Some(table_id_combiner);
475
476            //~~ * Compute the dummy lookup value as the combination of the last entry of the XOR table (so `(0, 0, 0)`).
477            //~~   Warning: This assumes that we always use the XOR table when using lookups.
478            let dummy_lookup_value = lcs
479                .configuration
480                .dummy_lookup
481                .evaluate(&joint_combiner, &table_id_combiner);
482            lookup_context.dummy_lookup_value = Some(dummy_lookup_value);
483
484            //~~ * Compute the lookup table values as the combination of the lookup table entries.
485            let joint_lookup_table_d8 = {
486                let mut evals = Vec::with_capacity(d1_size);
487
488                for idx in 0..(d1_size * 8) {
489                    let table_id = match lcs.table_ids8.as_ref() {
490                        Some(table_ids8) => table_ids8.evals[idx],
491                        None =>
492                        // If there is no `table_ids8` in the constraint system,
493                        // every table ID is identically 0.
494                        {
495                            G::ScalarField::zero()
496                        }
497                    };
498
499                    let combined_entry =
500                        if !lcs.configuration.lookup_info.features.uses_runtime_tables {
501                            let table_row = lcs.lookup_table8.iter().map(|e| &e.evals[idx]);
502
503                            combine_table_entry(
504                                &joint_combiner,
505                                &table_id_combiner,
506                                table_row,
507                                &table_id,
508                            )
509                        } else {
510                            // if runtime table are used, the second row is modified
511                            let second_col = lookup_context.runtime_second_col_d8.as_ref().unwrap();
512
513                            let table_row = lcs.lookup_table8.iter().enumerate().map(|(col, e)| {
514                                if col == 1 {
515                                    &second_col.evals[idx]
516                                } else {
517                                    &e.evals[idx]
518                                }
519                            });
520
521                            combine_table_entry(
522                                &joint_combiner,
523                                &table_id_combiner,
524                                table_row,
525                                &table_id,
526                            )
527                        };
528                    evals.push(combined_entry);
529                }
530
531                Evaluations::from_vec_and_domain(evals, index.cs.domain.d8)
532            };
533
534            // TODO: This interpolation is avoidable.
535            let joint_lookup_table = joint_lookup_table_d8.interpolate_by_ref();
536
537            //~~ * Compute the sorted evaluations.
538            // TODO: Once we switch to committing using lagrange commitments,
539            // `witness` will be consumed when we interpolate, so interpolation will
540            // have to moved below this.
541            let sorted: Vec<_> = lookup::constraints::sorted(
542                dummy_lookup_value,
543                &joint_lookup_table_d8,
544                index.cs.domain.d1,
545                &index.cs.gates,
546                &witness,
547                joint_combiner,
548                table_id_combiner,
549                &lcs.configuration.lookup_info,
550                index.cs.zk_rows as usize,
551            )?;
552
553            //~~ * Randomize the last `EVALS` rows in each of the sorted polynomials
554            //~~   in order to add zero-knowledge to the protocol.
555            let sorted: Vec<_> = sorted
556                .into_iter()
557                .map(|chunk| {
558                    lookup::constraints::zk_patch(
559                        chunk,
560                        index.cs.domain.d1,
561                        index.cs.zk_rows as usize,
562                        rng,
563                    )
564                })
565                .collect();
566
567            //~~ * Commit each of the sorted polynomials.
568            let sorted_comms: Vec<_> = sorted
569                .iter()
570                .map(|v| index.srs.commit_evaluations(index.cs.domain.d1, v, rng))
571                .collect();
572
573            //~~ * Absorb each commitments to the sorted polynomials.
574            sorted_comms
575                .iter()
576                .for_each(|c| absorb_commitment(&mut fq_sponge, &c.commitment));
577
578            // precompute different forms of the sorted polynomials for later
579            // TODO: We can avoid storing these coefficients.
580            let sorted_coeffs: Vec<_> = sorted.iter().map(|e| e.clone().interpolate()).collect();
581            let sorted8: Vec<_> = sorted_coeffs
582                .iter()
583                .map(|v| v.evaluate_over_domain_by_ref(index.cs.domain.d8))
584                .collect();
585
586            lookup_context.joint_combiner = Some(joint_combiner);
587            lookup_context.sorted = Some(sorted);
588            lookup_context.sorted_coeffs = Some(sorted_coeffs);
589            lookup_context.sorted_comms = Some(sorted_comms);
590            lookup_context.sorted8 = Some(sorted8);
591            lookup_context.joint_lookup_table_d8 = Some(joint_lookup_table_d8);
592            lookup_context.joint_lookup_table = Some(joint_lookup_table);
593        }
594
595        //~ 1. Sample $\beta$ with the Fq-Sponge.
596        let beta = fq_sponge.challenge();
597
598        //~ 1. Sample $\gamma$ with the Fq-Sponge.
599        let gamma = fq_sponge.challenge();
600
601        //~ 1. If using lookup:
602        if let Some(lcs) = lookup_constraint_system {
603            //~~ * Compute the lookup aggregation polynomial.
604            let joint_lookup_table_d8 = lookup_context.joint_lookup_table_d8.as_ref().unwrap();
605
606            let aggreg = lookup::constraints::aggregation::<_, G::ScalarField>(
607                lookup_context.dummy_lookup_value.unwrap(),
608                joint_lookup_table_d8,
609                index.cs.domain.d1,
610                &index.cs.gates,
611                &witness,
612                &lookup_context.joint_combiner.unwrap(),
613                &lookup_context.table_id_combiner.unwrap(),
614                beta,
615                gamma,
616                lookup_context.sorted.as_ref().unwrap(),
617                rng,
618                &lcs.configuration.lookup_info,
619                index.cs.zk_rows as usize,
620            )?;
621
622            //~~ * Commit to the aggregation polynomial.
623            let aggreg_comm = index
624                .srs
625                .commit_evaluations(index.cs.domain.d1, &aggreg, rng);
626
627            //~~ * Absorb the commitment to the aggregation polynomial with the Fq-Sponge.
628            absorb_commitment(&mut fq_sponge, &aggreg_comm.commitment);
629
630            // precompute different forms of the aggregation polynomial for later
631            let aggreg_coeffs = aggreg.interpolate();
632            // TODO: There's probably a clever way to expand the domain without
633            // interpolating
634            let aggreg8 = aggreg_coeffs.evaluate_over_domain_by_ref(index.cs.domain.d8);
635
636            lookup_context.aggreg_comm = Some(aggreg_comm);
637            lookup_context.aggreg_coeffs = Some(aggreg_coeffs);
638            lookup_context.aggreg8 = Some(aggreg8);
639        }
640
641        let column_evaluations = index.column_evaluations.get();
642
643        //~ 1. Compute the permutation aggregation polynomial $z$.
644        internal_tracing::checkpoint!(internal_traces; z_permutation_aggregation_polynomial);
645        let z_poly = index.perm_aggreg(&witness, &beta, &gamma, rng)?;
646
647        //~ 1. Commit (hiding) to the permutation aggregation polynomial $z$.
648        let z_comm = index.srs.commit(&z_poly, num_chunks, rng);
649
650        //~ 1. Absorb the permutation aggregation polynomial $z$ with the Fq-Sponge.
651        absorb_commitment(&mut fq_sponge, &z_comm.commitment);
652
653        //~ 1. Sample $\alpha'$ with the Fq-Sponge.
654        let alpha_chal = ScalarChallenge(fq_sponge.challenge());
655
656        //~ 1. Derive $\alpha$ from $\alpha'$ using the endomorphism (TODO: details)
657        let alpha: G::ScalarField = alpha_chal.to_field(endo_r);
658
659        //~ 1. TODO: instantiate alpha?
660        let mut all_alphas = index.powers_of_alpha.clone();
661        all_alphas.instantiate(alpha);
662
663        //~ 1. Compute the quotient polynomial (the $t$ in $f = Z_H \cdot t$).
664        //~    The quotient polynomial is computed by adding all these polynomials together:
665        //~~ * the combined constraints for all the gates
666        //~~ * the combined constraints for the permutation
667        //~~ * TODO: lookup
668        //~~ * the negated public polynomial
669        //~    and by then dividing the resulting polynomial with the vanishing polynomial $Z_H$.
670        //~    TODO: specify the split of the permutation polynomial into perm and bnd?
671        let lookup_env = if let Some(lcs) = lookup_constraint_system {
672            let joint_lookup_table_d8 = lookup_context.joint_lookup_table_d8.as_ref().unwrap();
673
674            Some(LookupEnvironment {
675                aggreg: lookup_context.aggreg8.as_ref().unwrap(),
676                sorted: lookup_context.sorted8.as_ref().unwrap(),
677                selectors: &lcs.lookup_selectors,
678                table: joint_lookup_table_d8,
679                runtime_selector: lcs.runtime_selector.as_ref(),
680                runtime_table: lookup_context.runtime_table_d8.as_ref(),
681            })
682        } else {
683            None
684        };
685
686        internal_tracing::checkpoint!(internal_traces; eval_witness_polynomials_over_domains);
687        let lagrange = index.cs.evaluate(&witness_poly, &z_poly);
688        internal_tracing::checkpoint!(internal_traces; compute_index_evals);
689        let env = {
690            let mut index_evals = HashMap::new();
691            use GateType::*;
692            index_evals.insert(Generic, &column_evaluations.generic_selector4);
693            index_evals.insert(Poseidon, &column_evaluations.poseidon_selector8);
694            index_evals.insert(CompleteAdd, &column_evaluations.complete_add_selector4);
695            index_evals.insert(VarBaseMul, &column_evaluations.mul_selector8);
696            index_evals.insert(EndoMul, &column_evaluations.emul_selector8);
697            index_evals.insert(EndoMulScalar, &column_evaluations.endomul_scalar_selector8);
698
699            if let Some(selector) = &column_evaluations.range_check0_selector8 {
700                index_evals.insert(GateType::RangeCheck0, selector);
701            }
702
703            if let Some(selector) = &column_evaluations.range_check1_selector8 {
704                index_evals.insert(GateType::RangeCheck1, selector);
705            }
706
707            if let Some(selector) = &column_evaluations.foreign_field_add_selector8 {
708                index_evals.insert(GateType::ForeignFieldAdd, selector);
709            }
710
711            if let Some(selector) = &column_evaluations.foreign_field_mul_selector8 {
712                index_evals.extend(
713                    foreign_field_mul::gadget::circuit_gates()
714                        .iter()
715                        .map(|gate_type| (*gate_type, selector)),
716                );
717            }
718
719            if let Some(selector) = &column_evaluations.xor_selector8 {
720                index_evals.insert(GateType::Xor16, selector);
721            }
722
723            if let Some(selector) = &column_evaluations.rot_selector8 {
724                index_evals.insert(GateType::Rot64, selector);
725            }
726
727            let mds = &G::sponge_params().mds;
728            Environment {
729                constants: Constants {
730                    endo_coefficient: index.cs.endo,
731                    mds,
732                    zk_rows: index.cs.zk_rows,
733                },
734                challenges: BerkeleyChallenges {
735                    alpha,
736                    beta,
737                    gamma,
738                    joint_combiner: lookup_context
739                        .joint_combiner
740                        .unwrap_or(G::ScalarField::zero()),
741                },
742                witness: &lagrange.d8.this.w,
743                coefficient: &column_evaluations.coefficients8,
744                vanishes_on_zero_knowledge_and_previous_rows: &index
745                    .cs
746                    .precomputations()
747                    .vanishes_on_zero_knowledge_and_previous_rows,
748                z: &lagrange.d8.this.z,
749                l0_1: l0_1(index.cs.domain.d1),
750                domain: index.cs.domain,
751                index: index_evals,
752                lookup: lookup_env,
753            }
754        };
755
756        let mut cache = expr::Cache::default();
757
758        internal_tracing::checkpoint!(internal_traces; compute_quotient_poly);
759
760        let quotient_poly = {
761            // generic
762            let mut t4 = {
763                let generic_constraint =
764                    generic::Generic::combined_constraints(&all_alphas, &mut cache);
765                let generic4 = generic_constraint.evaluations(&env);
766
767                if cfg!(debug_assertions) {
768                    let p4 = public_poly.evaluate_over_domain_by_ref(index.cs.domain.d4);
769                    let gen_minus_pub = &generic4 + &p4;
770
771                    check_constraint!(index, gen_minus_pub);
772                }
773
774                generic4
775            };
776
777            // permutation
778            let (mut t8, bnd) = {
779                let alphas =
780                    all_alphas.get_alphas(ArgumentType::Permutation, permutation::CONSTRAINTS);
781                let (perm, bnd) = index.perm_quot(&lagrange, beta, gamma, &z_poly, alphas)?;
782
783                check_constraint!(index, perm);
784
785                (perm, bnd)
786            };
787
788            {
789                use crate::circuits::argument::DynArgument;
790
791                let range_check0_enabled = column_evaluations.range_check0_selector8.is_some();
792                let range_check1_enabled = column_evaluations.range_check1_selector8.is_some();
793                let foreign_field_addition_enabled =
794                    column_evaluations.foreign_field_add_selector8.is_some();
795                let foreign_field_multiplication_enabled =
796                    column_evaluations.foreign_field_mul_selector8.is_some();
797                let xor_enabled = column_evaluations.xor_selector8.is_some();
798                let rot_enabled = column_evaluations.rot_selector8.is_some();
799
800                for gate in [
801                    (
802                        (&CompleteAdd::default() as &dyn DynArgument<G::ScalarField>),
803                        true,
804                    ),
805                    (&VarbaseMul::default(), true),
806                    (&EndosclMul::default(), true),
807                    (&EndomulScalar::default(), true),
808                    (&Poseidon::default(), true),
809                    // Range check gates
810                    (&RangeCheck0::default(), range_check0_enabled),
811                    (&RangeCheck1::default(), range_check1_enabled),
812                    // Foreign field addition gate
813                    (&ForeignFieldAdd::default(), foreign_field_addition_enabled),
814                    // Foreign field multiplication gate
815                    (
816                        &ForeignFieldMul::default(),
817                        foreign_field_multiplication_enabled,
818                    ),
819                    // Xor gate
820                    (&Xor16::default(), xor_enabled),
821                    // Rot gate
822                    (&Rot64::default(), rot_enabled),
823                ]
824                .into_iter()
825                .filter_map(|(gate, is_enabled)| if is_enabled { Some(gate) } else { None })
826                {
827                    let constraint = gate.combined_constraints(&all_alphas, &mut cache);
828                    let eval = constraint.evaluations(&env);
829                    if eval.domain().size == t4.domain().size {
830                        t4 += &eval;
831                    } else if eval.domain().size == t8.domain().size {
832                        t8 += &eval;
833                    } else {
834                        panic!("Bad evaluation")
835                    }
836                    check_constraint!(index, format!("{:?}", gate.argument_type()), eval);
837                }
838            };
839
840            // lookup
841            {
842                if let Some(lcs) = lookup_constraint_system {
843                    let constraints = lookup::constraints::constraints(&lcs.configuration, false);
844                    let constraints_len = u32::try_from(constraints.len())
845                        .expect("not expecting a large amount of constraints");
846                    let lookup_alphas =
847                        all_alphas.get_alphas(ArgumentType::Lookup, constraints_len);
848
849                    // as lookup constraints are computed with the expression framework,
850                    // each of them can result in Evaluations of different domains
851                    for (ii, (constraint, alpha_pow)) in
852                        constraints.into_iter().zip_eq(lookup_alphas).enumerate()
853                    {
854                        let mut eval = constraint.evaluations(&env);
855                        eval.evals.par_iter_mut().for_each(|x| *x *= alpha_pow);
856
857                        if eval.domain().size == t4.domain().size {
858                            t4 += &eval;
859                        } else if eval.domain().size == t8.domain().size {
860                            t8 += &eval;
861                        } else if eval.evals.iter().all(|x| x.is_zero()) {
862                            // Skip any 0-valued evaluations
863                        } else {
864                            panic!("Bad evaluation")
865                        }
866
867                        check_constraint!(index, format!("lookup constraint #{ii}"), eval);
868                    }
869                }
870            }
871
872            // public polynomial
873            let mut f = t4.interpolate() + t8.interpolate();
874            f += &public_poly;
875
876            // divide contributions with vanishing polynomial
877            let (mut quotient, res) = f.divide_by_vanishing_poly(index.cs.domain.d1);
878            if !res.is_zero() {
879                return Err(ProverError::Prover(
880                    "rest of division by vanishing polynomial",
881                ));
882            }
883
884            quotient += &bnd; // already divided by Z_H
885            quotient
886        };
887
888        //~ 1. commit (hiding) to the quotient polynomial $t$
889        let t_comm = { index.srs.commit(&quotient_poly, 7 * num_chunks, rng) };
890
891        //~ 1. Absorb the commitment of the quotient polynomial with the Fq-Sponge.
892        absorb_commitment(&mut fq_sponge, &t_comm.commitment);
893
894        //~ 1. Sample $\zeta'$ with the Fq-Sponge.
895        let zeta_chal = ScalarChallenge(fq_sponge.challenge());
896
897        //~ 1. Derive $\zeta$ from $\zeta'$ using the endomorphism (TODO: specify)
898        let zeta = zeta_chal.to_field(endo_r);
899
900        let omega = index.cs.domain.d1.group_gen;
901        let zeta_omega = zeta * omega;
902
903        //~ 1. If lookup is used, evaluate the following polynomials at $\zeta$ and $\zeta \omega$:
904        if lookup_constraint_system.is_some() {
905            //~~ * the aggregation polynomial
906            let aggreg = lookup_context
907                .aggreg_coeffs
908                .as_ref()
909                .unwrap()
910                .to_chunked_polynomial(num_chunks, index.max_poly_size);
911
912            //~~ * the sorted polynomials
913            let sorted = lookup_context
914                .sorted_coeffs
915                .as_ref()
916                .unwrap()
917                .iter()
918                .map(|c| c.to_chunked_polynomial(num_chunks, index.max_poly_size))
919                .collect::<Vec<_>>();
920
921            //~~ * the table polynonial
922            let joint_table = lookup_context.joint_lookup_table.as_ref().unwrap();
923            let joint_table = joint_table.to_chunked_polynomial(num_chunks, index.max_poly_size);
924
925            lookup_context.lookup_aggregation_eval = Some(PointEvaluations {
926                zeta: aggreg.evaluate_chunks(zeta),
927                zeta_omega: aggreg.evaluate_chunks(zeta_omega),
928            });
929            lookup_context.lookup_table_eval = Some(PointEvaluations {
930                zeta: joint_table.evaluate_chunks(zeta),
931                zeta_omega: joint_table.evaluate_chunks(zeta_omega),
932            });
933            lookup_context.lookup_sorted_eval = array::from_fn(|i| {
934                if i < sorted.len() {
935                    let sorted = &sorted[i];
936                    Some(PointEvaluations {
937                        zeta: sorted.evaluate_chunks(zeta),
938                        zeta_omega: sorted.evaluate_chunks(zeta_omega),
939                    })
940                } else {
941                    None
942                }
943            });
944            lookup_context.runtime_lookup_table_eval =
945                lookup_context.runtime_table.as_ref().map(|runtime_table| {
946                    let runtime_table =
947                        runtime_table.to_chunked_polynomial(num_chunks, index.max_poly_size);
948                    PointEvaluations {
949                        zeta: runtime_table.evaluate_chunks(zeta),
950                        zeta_omega: runtime_table.evaluate_chunks(zeta_omega),
951                    }
952                });
953        }
954
955        //~ 1. Chunk evaluate the following polynomials at both $\zeta$ and $\zeta \omega$:
956        //~~ * $s_i$
957        //~~ * $w_i$
958        //~~ * $z$
959        //~~ * lookup (TODO, see [this issue](https://github.com/MinaProtocol/mina/issues/13886))
960        //~~ * generic selector
961        //~~ * poseidon selector
962        //~
963        //~    By "chunk evaluate" we mean that the evaluation of each polynomial can potentially be a vector of values.
964        //~    This is because the index's `max_poly_size` parameter dictates the maximum size of a polynomial in the protocol.
965        //~    If a polynomial $f$ exceeds this size, it must be split into several polynomials like so:
966        //~    $$f(x) = f_0(x) + x^n f_1(x) + x^{2n} f_2(x) + \cdots$$
967        //~
968        //~    And the evaluation of such a polynomial is the following list for $x \in {\zeta, \zeta\omega}$:
969        //~
970        //~    $$(f_0(x), f_1(x), f_2(x), \ldots)$$
971        //~
972        //~    TODO: do we want to specify more on that? It seems unnecessary except for the t polynomial (or if for some reason someone sets that to a low value)
973
974        internal_tracing::checkpoint!(internal_traces; lagrange_basis_eval_zeta_poly);
975        let zeta_evals =
976            LagrangeBasisEvaluations::new(index.max_poly_size, index.cs.domain.d1, zeta);
977        internal_tracing::checkpoint!(internal_traces; lagrange_basis_eval_zeta_omega_poly);
978        let zeta_omega_evals =
979            LagrangeBasisEvaluations::new(index.max_poly_size, index.cs.domain.d1, zeta_omega);
980
981        let chunked_evals_for_selector =
982            |p: &Evaluations<G::ScalarField, D<G::ScalarField>>| PointEvaluations {
983                zeta: zeta_evals.evaluate_boolean(p),
984                zeta_omega: zeta_omega_evals.evaluate_boolean(p),
985            };
986
987        let chunked_evals_for_evaluations =
988            |p: &Evaluations<G::ScalarField, D<G::ScalarField>>| PointEvaluations {
989                zeta: zeta_evals.evaluate(p),
990                zeta_omega: zeta_omega_evals.evaluate(p),
991            };
992
993        internal_tracing::checkpoint!(internal_traces; chunk_eval_zeta_omega_poly);
994        let chunked_evals = ProofEvaluations::<PointEvaluations<Vec<G::ScalarField>>> {
995            public: {
996                let chunked = public_poly.to_chunked_polynomial(num_chunks, index.max_poly_size);
997                Some(PointEvaluations {
998                    zeta: chunked.evaluate_chunks(zeta),
999                    zeta_omega: chunked.evaluate_chunks(zeta_omega),
1000                })
1001            },
1002            s: array::from_fn(|i| {
1003                chunked_evals_for_evaluations(&column_evaluations.permutation_coefficients8[i])
1004            }),
1005            coefficients: array::from_fn(|i| {
1006                chunked_evals_for_evaluations(&column_evaluations.coefficients8[i])
1007            }),
1008            w: array::from_fn(|i| {
1009                let chunked =
1010                    witness_poly[i].to_chunked_polynomial(num_chunks, index.max_poly_size);
1011                PointEvaluations {
1012                    zeta: chunked.evaluate_chunks(zeta),
1013                    zeta_omega: chunked.evaluate_chunks(zeta_omega),
1014                }
1015            }),
1016
1017            z: {
1018                let chunked = z_poly.to_chunked_polynomial(num_chunks, index.max_poly_size);
1019                PointEvaluations {
1020                    zeta: chunked.evaluate_chunks(zeta),
1021                    zeta_omega: chunked.evaluate_chunks(zeta_omega),
1022                }
1023            },
1024
1025            lookup_aggregation: lookup_context.lookup_aggregation_eval.take(),
1026            lookup_table: lookup_context.lookup_table_eval.take(),
1027            lookup_sorted: array::from_fn(|i| lookup_context.lookup_sorted_eval[i].take()),
1028            runtime_lookup_table: lookup_context.runtime_lookup_table_eval.take(),
1029            generic_selector: chunked_evals_for_selector(&column_evaluations.generic_selector4),
1030            poseidon_selector: chunked_evals_for_selector(&column_evaluations.poseidon_selector8),
1031            complete_add_selector: chunked_evals_for_selector(
1032                &column_evaluations.complete_add_selector4,
1033            ),
1034            mul_selector: chunked_evals_for_selector(&column_evaluations.mul_selector8),
1035            emul_selector: chunked_evals_for_selector(&column_evaluations.emul_selector8),
1036            endomul_scalar_selector: chunked_evals_for_selector(
1037                &column_evaluations.endomul_scalar_selector8,
1038            ),
1039
1040            range_check0_selector: column_evaluations
1041                .range_check0_selector8
1042                .as_ref()
1043                .map(chunked_evals_for_selector),
1044            range_check1_selector: column_evaluations
1045                .range_check1_selector8
1046                .as_ref()
1047                .map(chunked_evals_for_selector),
1048            foreign_field_add_selector: column_evaluations
1049                .foreign_field_add_selector8
1050                .as_ref()
1051                .map(chunked_evals_for_selector),
1052            foreign_field_mul_selector: column_evaluations
1053                .foreign_field_mul_selector8
1054                .as_ref()
1055                .map(chunked_evals_for_selector),
1056            xor_selector: column_evaluations
1057                .xor_selector8
1058                .as_ref()
1059                .map(chunked_evals_for_selector),
1060            rot_selector: column_evaluations
1061                .rot_selector8
1062                .as_ref()
1063                .map(chunked_evals_for_selector),
1064
1065            runtime_lookup_table_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1066                lcs.runtime_selector
1067                    .as_ref()
1068                    .map(chunked_evals_for_selector)
1069            }),
1070            xor_lookup_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1071                lcs.lookup_selectors
1072                    .xor
1073                    .as_ref()
1074                    .map(chunked_evals_for_selector)
1075            }),
1076            lookup_gate_lookup_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1077                lcs.lookup_selectors
1078                    .lookup
1079                    .as_ref()
1080                    .map(chunked_evals_for_selector)
1081            }),
1082            range_check_lookup_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1083                lcs.lookup_selectors
1084                    .range_check
1085                    .as_ref()
1086                    .map(chunked_evals_for_selector)
1087            }),
1088            foreign_field_mul_lookup_selector: lookup_constraint_system.as_ref().and_then(|lcs| {
1089                lcs.lookup_selectors
1090                    .ffmul
1091                    .as_ref()
1092                    .map(chunked_evals_for_selector)
1093            }),
1094        };
1095
1096        let zeta_to_srs_len = zeta.pow([index.max_poly_size as u64]);
1097        let zeta_omega_to_srs_len = zeta_omega.pow([index.max_poly_size as u64]);
1098        let zeta_to_domain_size = zeta.pow([d1_size as u64]);
1099
1100        //~ 1. Evaluate the same polynomials without chunking them
1101        //~    (so that each polynomial should correspond to a single value this time).
1102        let evals: ProofEvaluations<PointEvaluations<G::ScalarField>> = {
1103            let powers_of_eval_points_for_chunks = PointEvaluations {
1104                zeta: zeta_to_srs_len,
1105                zeta_omega: zeta_omega_to_srs_len,
1106            };
1107            chunked_evals.combine(&powers_of_eval_points_for_chunks)
1108        };
1109
1110        //~ 1. Compute the ft polynomial.
1111        //~    This is to implement [Maller's optimization](https://o1-labs.github.io/proof-systems/kimchi/maller_15.html).
1112        internal_tracing::checkpoint!(internal_traces; compute_ft_poly);
1113        let ft: DensePolynomial<G::ScalarField> = {
1114            let f_chunked = {
1115                // TODO: compute the linearization polynomial in evaluation form so
1116                // that we can drop the coefficient forms of the index polynomials from
1117                // the constraint system struct
1118
1119                // permutation (not part of linearization yet)
1120                let alphas =
1121                    all_alphas.get_alphas(ArgumentType::Permutation, permutation::CONSTRAINTS);
1122                let f = index.perm_lnrz(&evals, zeta, beta, gamma, alphas);
1123
1124                // the circuit polynomial
1125                let f = {
1126                    let (_lin_constant, mut lin) =
1127                        index.linearization.to_polynomial(&env, zeta, &evals);
1128                    lin += &f;
1129                    lin.interpolate()
1130                };
1131
1132                drop(env);
1133
1134                // see https://o1-labs.github.io/proof-systems/kimchi/maller_15.html#the-prover-side
1135                f.to_chunked_polynomial(num_chunks, index.max_poly_size)
1136                    .linearize(zeta_to_srs_len)
1137            };
1138
1139            let t_chunked = quotient_poly
1140                .to_chunked_polynomial(7 * num_chunks, index.max_poly_size)
1141                .linearize(zeta_to_srs_len);
1142
1143            &f_chunked - &t_chunked.scale(zeta_to_domain_size - G::ScalarField::one())
1144        };
1145
1146        //~ 1. construct the blinding part of the ft polynomial commitment
1147        //~    [see this section](https://o1-labs.github.io/proof-systems/kimchi/maller_15.html#evaluation-proof-and-blinding-factors)
1148        let blinding_ft = {
1149            let blinding_t = t_comm.blinders.chunk_blinding(zeta_to_srs_len);
1150            let blinding_f = G::ScalarField::zero();
1151
1152            PolyComm {
1153                // blinding_f - Z_H(zeta) * blinding_t
1154                chunks: vec![
1155                    blinding_f - (zeta_to_domain_size - G::ScalarField::one()) * blinding_t,
1156                ],
1157            }
1158        };
1159
1160        //~ 1. Evaluate the ft polynomial at $\zeta\omega$ only.
1161        internal_tracing::checkpoint!(internal_traces; ft_eval_zeta_omega);
1162        let ft_eval1 = ft.evaluate(&zeta_omega);
1163
1164        //~ 1. Setup the Fr-Sponge
1165        let fq_sponge_before_evaluations = fq_sponge.clone();
1166        let mut fr_sponge = EFrSponge::from(G::sponge_params());
1167
1168        //~ 1. Squeeze the Fq-sponge and absorb the result with the Fr-Sponge.
1169        fr_sponge.absorb(&fq_sponge.digest());
1170
1171        //~ 1. Absorb the previous recursion challenges.
1172        let prev_challenge_digest = {
1173            // Note: we absorb in a new sponge here to limit the scope in which we need the
1174            // more-expensive 'optional sponge'.
1175            let mut fr_sponge = EFrSponge::from(G::sponge_params());
1176            for RecursionChallenge { chals, .. } in &prev_challenges {
1177                fr_sponge.absorb_multiple(chals);
1178            }
1179            fr_sponge.digest()
1180        };
1181        fr_sponge.absorb(&prev_challenge_digest);
1182
1183        //~ 1. Compute evaluations for the previous recursion challenges.
1184        internal_tracing::checkpoint!(internal_traces; build_polynomials);
1185        let polys = prev_challenges
1186            .iter()
1187            .map(|RecursionChallenge { chals, comm }| {
1188                (
1189                    DensePolynomial::from_coefficients_vec(b_poly_coefficients(chals)),
1190                    comm.len(),
1191                )
1192            })
1193            .collect::<Vec<_>>();
1194
1195        //~ 1. Absorb the unique evaluation of ft: $ft(\zeta\omega)$.
1196        fr_sponge.absorb(&ft_eval1);
1197
1198        //~ 1. Absorb all the polynomial evaluations in $\zeta$ and $\zeta\omega$:
1199        //~~ * the public polynomial
1200        //~~ * z
1201        //~~ * generic selector
1202        //~~ * poseidon selector
1203        //~~ * the 15 register/witness
1204        //~~ * 6 sigmas evaluations (the last one is not evaluated)
1205        fr_sponge.absorb_multiple(&chunked_evals.public.as_ref().unwrap().zeta);
1206        fr_sponge.absorb_multiple(&chunked_evals.public.as_ref().unwrap().zeta_omega);
1207        fr_sponge.absorb_evaluations(&chunked_evals);
1208
1209        //~ 1. Sample $v'$ with the Fr-Sponge
1210        let v_chal = fr_sponge.challenge();
1211
1212        //~ 1. Derive $v$ from $v'$ using the endomorphism (TODO: specify)
1213        let v = v_chal.to_field(endo_r);
1214
1215        //~ 1. Sample $u'$ with the Fr-Sponge
1216        let u_chal = fr_sponge.challenge();
1217
1218        //~ 1. Derive $u$ from $u'$ using the endomorphism (TODO: specify)
1219        let u = u_chal.to_field(endo_r);
1220
1221        //~ 1. Create a list of all polynomials that will require evaluations
1222        //~    (and evaluation proofs) in the protocol.
1223        //~    First, include the previous challenges, in case we are in a recursive prover.
1224        let non_hiding = |n_chunks: usize| PolyComm {
1225            chunks: vec![G::ScalarField::zero(); n_chunks],
1226        };
1227
1228        let fixed_hiding = |n_chunks: usize| PolyComm {
1229            chunks: vec![G::ScalarField::one(); n_chunks],
1230        };
1231
1232        let coefficients_form = DensePolynomialOrEvaluations::DensePolynomial;
1233        let evaluations_form = |e| DensePolynomialOrEvaluations::Evaluations(e, index.cs.domain.d1);
1234
1235        let mut polynomials = polys
1236            .iter()
1237            .map(|(p, n_chunks)| (coefficients_form(p), non_hiding(*n_chunks)))
1238            .collect::<Vec<_>>();
1239
1240        //~ 1. Then, include:
1241        //~~ * the negated public polynomial
1242        //~~ * the ft polynomial
1243        //~~ * the permutation aggregation polynomial z polynomial
1244        //~~ * the generic selector
1245        //~~ * the poseidon selector
1246        //~~ * the 15 registers/witness columns
1247        //~~ * the 6 sigmas
1248        polynomials.push((coefficients_form(&public_poly), fixed_hiding(num_chunks)));
1249        polynomials.push((coefficients_form(&ft), blinding_ft));
1250        polynomials.push((coefficients_form(&z_poly), z_comm.blinders));
1251        polynomials.push((
1252            evaluations_form(&column_evaluations.generic_selector4),
1253            fixed_hiding(num_chunks),
1254        ));
1255        polynomials.push((
1256            evaluations_form(&column_evaluations.poseidon_selector8),
1257            fixed_hiding(num_chunks),
1258        ));
1259        polynomials.push((
1260            evaluations_form(&column_evaluations.complete_add_selector4),
1261            fixed_hiding(num_chunks),
1262        ));
1263        polynomials.push((
1264            evaluations_form(&column_evaluations.mul_selector8),
1265            fixed_hiding(num_chunks),
1266        ));
1267        polynomials.push((
1268            evaluations_form(&column_evaluations.emul_selector8),
1269            fixed_hiding(num_chunks),
1270        ));
1271        polynomials.push((
1272            evaluations_form(&column_evaluations.endomul_scalar_selector8),
1273            fixed_hiding(num_chunks),
1274        ));
1275        polynomials.extend(
1276            witness_poly
1277                .iter()
1278                .zip(w_comm.iter())
1279                .map(|(w, c)| (coefficients_form(w), c.blinders.clone()))
1280                .collect::<Vec<_>>(),
1281        );
1282        polynomials.extend(
1283            column_evaluations
1284                .coefficients8
1285                .iter()
1286                .map(|coefficientm| (evaluations_form(coefficientm), non_hiding(num_chunks)))
1287                .collect::<Vec<_>>(),
1288        );
1289        polynomials.extend(
1290            column_evaluations.permutation_coefficients8[0..PERMUTS - 1]
1291                .iter()
1292                .map(|w| (evaluations_form(w), non_hiding(num_chunks)))
1293                .collect::<Vec<_>>(),
1294        );
1295
1296        //~~ * the optional gates
1297        if let Some(range_check0_selector8) = &column_evaluations.range_check0_selector8 {
1298            polynomials.push((
1299                evaluations_form(range_check0_selector8),
1300                non_hiding(num_chunks),
1301            ));
1302        }
1303        if let Some(range_check1_selector8) = &column_evaluations.range_check1_selector8 {
1304            polynomials.push((
1305                evaluations_form(range_check1_selector8),
1306                non_hiding(num_chunks),
1307            ));
1308        }
1309        if let Some(foreign_field_add_selector8) = &column_evaluations.foreign_field_add_selector8 {
1310            polynomials.push((
1311                evaluations_form(foreign_field_add_selector8),
1312                non_hiding(num_chunks),
1313            ));
1314        }
1315        if let Some(foreign_field_mul_selector8) = &column_evaluations.foreign_field_mul_selector8 {
1316            polynomials.push((
1317                evaluations_form(foreign_field_mul_selector8),
1318                non_hiding(num_chunks),
1319            ));
1320        }
1321        if let Some(xor_selector8) = &column_evaluations.xor_selector8 {
1322            polynomials.push((evaluations_form(xor_selector8), non_hiding(num_chunks)));
1323        }
1324        if let Some(rot_selector8) = &column_evaluations.rot_selector8 {
1325            polynomials.push((evaluations_form(rot_selector8), non_hiding(num_chunks)));
1326        }
1327
1328        //~~ * optionally, the runtime table
1329        //~ 1. if using lookup:
1330        if let Some(lcs) = lookup_constraint_system {
1331            //~~ * add the lookup sorted polynomials
1332            let sorted_poly = lookup_context.sorted_coeffs.as_ref().unwrap();
1333            let sorted_comms = lookup_context.sorted_comms.as_ref().unwrap();
1334
1335            for (poly, comm) in sorted_poly.iter().zip(sorted_comms) {
1336                polynomials.push((coefficients_form(poly), comm.blinders.clone()));
1337            }
1338
1339            //~~ * add the lookup aggreg polynomial
1340            let aggreg_poly = lookup_context.aggreg_coeffs.as_ref().unwrap();
1341            let aggreg_comm = lookup_context.aggreg_comm.as_ref().unwrap();
1342            polynomials.push((coefficients_form(aggreg_poly), aggreg_comm.blinders.clone()));
1343
1344            //~~ * add the combined table polynomial
1345            let table_blinding = {
1346                let joint_combiner = lookup_context.joint_combiner.as_ref().unwrap();
1347                let table_id_combiner = lookup_context.table_id_combiner.as_ref().unwrap();
1348                let max_fixed_lookup_table_size = {
1349                    // CAUTION: This is not `lcs.configuration.lookup_info.max_joint_size` because
1350                    // the lookup table may be strictly narrower, and as such will not contribute
1351                    // the associated blinders.
1352                    // For example, using a runtime table with the lookup gate (width 2), but only
1353                    // width-1 fixed tables (e.g. range check), it would be incorrect to use the
1354                    // wider width (2) because there are no such contributing commitments!
1355                    // Note that lookup_table8 is a list of polynomials
1356                    lcs.lookup_table8.len()
1357                };
1358                let base_blinding = {
1359                    let fixed_table_blinding = if max_fixed_lookup_table_size == 0 {
1360                        G::ScalarField::zero()
1361                    } else {
1362                        (1..max_fixed_lookup_table_size).fold(G::ScalarField::one(), |acc, _| {
1363                            G::ScalarField::one() + *joint_combiner * acc
1364                        })
1365                    };
1366                    fixed_table_blinding + *table_id_combiner
1367                };
1368                if lcs.runtime_selector.is_some() {
1369                    let runtime_comm = lookup_context.runtime_table_comm.as_ref().unwrap();
1370
1371                    let chunks = runtime_comm
1372                        .blinders
1373                        .into_iter()
1374                        .map(|blinding| *joint_combiner * *blinding + base_blinding)
1375                        .collect();
1376
1377                    PolyComm::new(chunks)
1378                } else {
1379                    let chunks = vec![base_blinding; num_chunks];
1380                    PolyComm::new(chunks)
1381                }
1382            };
1383
1384            let joint_lookup_table = lookup_context.joint_lookup_table.as_ref().unwrap();
1385
1386            polynomials.push((coefficients_form(joint_lookup_table), table_blinding));
1387
1388            //~~ * if present, add the runtime table polynomial
1389            if lcs.runtime_selector.is_some() {
1390                let runtime_table_comm = lookup_context.runtime_table_comm.as_ref().unwrap();
1391                let runtime_table = lookup_context.runtime_table.as_ref().unwrap();
1392
1393                polynomials.push((
1394                    coefficients_form(runtime_table),
1395                    runtime_table_comm.blinders.clone(),
1396                ));
1397            }
1398
1399            //~~ * the lookup selectors
1400
1401            if let Some(runtime_lookup_table_selector) = &lcs.runtime_selector {
1402                polynomials.push((
1403                    evaluations_form(runtime_lookup_table_selector),
1404                    non_hiding(1),
1405                ))
1406            }
1407            if let Some(xor_lookup_selector) = &lcs.lookup_selectors.xor {
1408                polynomials.push((evaluations_form(xor_lookup_selector), non_hiding(1)))
1409            }
1410            if let Some(lookup_gate_selector) = &lcs.lookup_selectors.lookup {
1411                polynomials.push((evaluations_form(lookup_gate_selector), non_hiding(1)))
1412            }
1413            if let Some(range_check_lookup_selector) = &lcs.lookup_selectors.range_check {
1414                polynomials.push((evaluations_form(range_check_lookup_selector), non_hiding(1)))
1415            }
1416            if let Some(foreign_field_mul_lookup_selector) = &lcs.lookup_selectors.ffmul {
1417                polynomials.push((
1418                    evaluations_form(foreign_field_mul_lookup_selector),
1419                    non_hiding(1),
1420                ))
1421            }
1422        }
1423
1424        //~ 1. Create an aggregated evaluation proof for all of these polynomials at $\zeta$ and $\zeta\omega$ using $u$ and $v$.
1425        internal_tracing::checkpoint!(internal_traces; create_aggregated_ipa);
1426        let proof = OpenProof::open(
1427            &*index.srs,
1428            group_map,
1429            &polynomials,
1430            &[zeta, zeta_omega],
1431            v,
1432            u,
1433            fq_sponge_before_evaluations,
1434            rng,
1435        );
1436
1437        let lookup = lookup_context
1438            .aggreg_comm
1439            .zip(lookup_context.sorted_comms)
1440            .map(|(a, s)| LookupCommitments {
1441                aggreg: a.commitment,
1442                sorted: s.iter().map(|c| c.commitment.clone()).collect(),
1443                runtime: lookup_context.runtime_table_comm.map(|x| x.commitment),
1444            });
1445
1446        let proof = Self {
1447            commitments: ProverCommitments {
1448                w_comm: array::from_fn(|i| w_comm[i].commitment.clone()),
1449                z_comm: z_comm.commitment,
1450                t_comm: t_comm.commitment,
1451                lookup,
1452            },
1453            proof,
1454            evals: chunked_evals,
1455            ft_eval1,
1456            prev_challenges,
1457        };
1458
1459        internal_tracing::checkpoint!(internal_traces; create_recursive_done);
1460
1461        Ok(proof)
1462    }
1463}
1464
1465internal_tracing::decl_traces!(internal_traces;
1466    pasta_fp_plonk_proof_create,
1467    pasta_fq_plonk_proof_create,
1468    create_recursive,
1469    pad_witness,
1470    set_up_fq_sponge,
1471    commit_to_witness_columns,
1472    use_lookup,
1473    z_permutation_aggregation_polynomial,
1474    eval_witness_polynomials_over_domains,
1475    compute_index_evals,
1476    compute_quotient_poly,
1477    lagrange_basis_eval_zeta_poly,
1478    lagrange_basis_eval_zeta_omega_poly,
1479    chunk_eval_zeta_omega_poly,
1480    compute_ft_poly,
1481    ft_eval_zeta_omega,
1482    build_polynomials,
1483    create_aggregated_ipa,
1484    create_recursive_done);
1485
1486#[cfg(feature = "ocaml_types")]
1487pub mod caml {
1488    use super::*;
1489    use crate::proof::caml::{CamlProofEvaluations, CamlRecursionChallenge};
1490    use ark_ec::AffineRepr;
1491    use poly_commitment::{
1492        commitment::caml::CamlPolyComm,
1493        ipa::{caml::CamlOpeningProof, OpeningProof},
1494    };
1495
1496    #[cfg(feature = "internal_tracing")]
1497    pub use internal_traces::caml::CamlTraces as CamlProverTraces;
1498
1499    #[derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
1500    pub struct CamlProofWithPublic<CamlG, CamlF> {
1501        pub public_evals: Option<PointEvaluations<Vec<CamlF>>>,
1502        pub proof: CamlProverProof<CamlG, CamlF>,
1503    }
1504
1505    //
1506    // CamlProverProof<CamlG, CamlF>
1507    //
1508
1509    #[derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
1510    pub struct CamlProverProof<CamlG, CamlF> {
1511        pub commitments: CamlProverCommitments<CamlG>,
1512        pub proof: CamlOpeningProof<CamlG, CamlF>,
1513        // OCaml doesn't have sized arrays, so we have to convert to a tuple..
1514        pub evals: CamlProofEvaluations<CamlF>,
1515        pub ft_eval1: CamlF,
1516        pub public: Vec<CamlF>,
1517        //Vec<(Vec<CamlF>, CamlPolyComm<CamlG>)>,
1518        pub prev_challenges: Vec<CamlRecursionChallenge<CamlG, CamlF>>,
1519    }
1520
1521    //
1522    // CamlProverCommitments<CamlG>
1523    //
1524
1525    #[derive(Clone, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
1526    pub struct CamlLookupCommitments<CamlG> {
1527        pub sorted: Vec<CamlPolyComm<CamlG>>,
1528        pub aggreg: CamlPolyComm<CamlG>,
1529        pub runtime: Option<CamlPolyComm<CamlG>>,
1530    }
1531
1532    #[allow(clippy::type_complexity)]
1533    #[derive(Clone, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
1534    pub struct CamlProverCommitments<CamlG> {
1535        // polynomial commitments
1536        pub w_comm: (
1537            CamlPolyComm<CamlG>,
1538            CamlPolyComm<CamlG>,
1539            CamlPolyComm<CamlG>,
1540            CamlPolyComm<CamlG>,
1541            CamlPolyComm<CamlG>,
1542            CamlPolyComm<CamlG>,
1543            CamlPolyComm<CamlG>,
1544            CamlPolyComm<CamlG>,
1545            CamlPolyComm<CamlG>,
1546            CamlPolyComm<CamlG>,
1547            CamlPolyComm<CamlG>,
1548            CamlPolyComm<CamlG>,
1549            CamlPolyComm<CamlG>,
1550            CamlPolyComm<CamlG>,
1551            CamlPolyComm<CamlG>,
1552        ),
1553        pub z_comm: CamlPolyComm<CamlG>,
1554        pub t_comm: CamlPolyComm<CamlG>,
1555        pub lookup: Option<CamlLookupCommitments<CamlG>>,
1556    }
1557
1558    // These implementations are handy for conversions such as:
1559    // InternalType <-> Ocaml::Value
1560    //
1561    // It does this by hiding the required middle conversion step:
1562    // InternalType <-> CamlInternalType <-> Ocaml::Value
1563    //
1564    // Note that some conversions are not always possible to shorten,
1565    // because we don't always know how to convert the types.
1566    // For example, to implement the conversion
1567    // ProverCommitments<G> -> CamlProverCommitments<CamlG>
1568    // we need to know how to convert G to CamlG.
1569    // we don't know that information, unless we implemented some trait (e.g. ToCaml)
1570    // we can do that, but instead we implemented the From trait for the reverse
1571    // operations (From<G> for CamlG).
1572    // it reduces the complexity, but forces us to do the conversion in two
1573    // phases instead of one.
1574
1575    //
1576    // CamlLookupCommitments<CamlG> <-> LookupCommitments<G>
1577    //
1578
1579    impl<G, CamlG> From<LookupCommitments<G>> for CamlLookupCommitments<CamlG>
1580    where
1581        G: AffineRepr,
1582        CamlPolyComm<CamlG>: From<PolyComm<G>>,
1583    {
1584        fn from(
1585            LookupCommitments {
1586                aggreg,
1587                sorted,
1588                runtime,
1589            }: LookupCommitments<G>,
1590        ) -> Self {
1591            Self {
1592                aggreg: aggreg.into(),
1593                sorted: sorted.into_iter().map(Into::into).collect(),
1594                runtime: runtime.map(Into::into),
1595            }
1596        }
1597    }
1598
1599    impl<G, CamlG> From<CamlLookupCommitments<CamlG>> for LookupCommitments<G>
1600    where
1601        G: AffineRepr,
1602        PolyComm<G>: From<CamlPolyComm<CamlG>>,
1603    {
1604        fn from(
1605            CamlLookupCommitments {
1606                aggreg,
1607                sorted,
1608                runtime,
1609            }: CamlLookupCommitments<CamlG>,
1610        ) -> LookupCommitments<G> {
1611            LookupCommitments {
1612                aggreg: aggreg.into(),
1613                sorted: sorted.into_iter().map(Into::into).collect(),
1614                runtime: runtime.map(Into::into),
1615            }
1616        }
1617    }
1618
1619    //
1620    // CamlProverCommitments<CamlG> <-> ProverCommitments<G>
1621    //
1622
1623    impl<G, CamlG> From<ProverCommitments<G>> for CamlProverCommitments<CamlG>
1624    where
1625        G: AffineRepr,
1626        CamlPolyComm<CamlG>: From<PolyComm<G>>,
1627    {
1628        fn from(prover_comm: ProverCommitments<G>) -> Self {
1629            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] =
1630                prover_comm.w_comm;
1631            Self {
1632                w_comm: (
1633                    w_comm0.into(),
1634                    w_comm1.into(),
1635                    w_comm2.into(),
1636                    w_comm3.into(),
1637                    w_comm4.into(),
1638                    w_comm5.into(),
1639                    w_comm6.into(),
1640                    w_comm7.into(),
1641                    w_comm8.into(),
1642                    w_comm9.into(),
1643                    w_comm10.into(),
1644                    w_comm11.into(),
1645                    w_comm12.into(),
1646                    w_comm13.into(),
1647                    w_comm14.into(),
1648                ),
1649                z_comm: prover_comm.z_comm.into(),
1650                t_comm: prover_comm.t_comm.into(),
1651                lookup: prover_comm.lookup.map(Into::into),
1652            }
1653        }
1654    }
1655
1656    impl<G, CamlG> From<CamlProverCommitments<CamlG>> for ProverCommitments<G>
1657    where
1658        G: AffineRepr,
1659        PolyComm<G>: From<CamlPolyComm<CamlG>>,
1660    {
1661        fn from(caml_prover_comm: CamlProverCommitments<CamlG>) -> ProverCommitments<G> {
1662            let (
1663                w_comm0,
1664                w_comm1,
1665                w_comm2,
1666                w_comm3,
1667                w_comm4,
1668                w_comm5,
1669                w_comm6,
1670                w_comm7,
1671                w_comm8,
1672                w_comm9,
1673                w_comm10,
1674                w_comm11,
1675                w_comm12,
1676                w_comm13,
1677                w_comm14,
1678            ) = caml_prover_comm.w_comm;
1679            ProverCommitments {
1680                w_comm: [
1681                    w_comm0.into(),
1682                    w_comm1.into(),
1683                    w_comm2.into(),
1684                    w_comm3.into(),
1685                    w_comm4.into(),
1686                    w_comm5.into(),
1687                    w_comm6.into(),
1688                    w_comm7.into(),
1689                    w_comm8.into(),
1690                    w_comm9.into(),
1691                    w_comm10.into(),
1692                    w_comm11.into(),
1693                    w_comm12.into(),
1694                    w_comm13.into(),
1695                    w_comm14.into(),
1696                ],
1697                z_comm: caml_prover_comm.z_comm.into(),
1698                t_comm: caml_prover_comm.t_comm.into(),
1699                lookup: caml_prover_comm.lookup.map(Into::into),
1700            }
1701        }
1702    }
1703
1704    //
1705    // ProverProof<G> <-> CamlProofWithPublic<CamlG, CamlF>
1706    //
1707
1708    // this is just used to simplify the type signatures below
1709    // the bound does not need to be enforced
1710    #[allow(type_alias_bounds)]
1711    type ProofTuple<const FULL_ROUNDS: usize, G: AffineRepr> = (
1712        ProverProof<G, OpeningProof<G, FULL_ROUNDS>, FULL_ROUNDS>,
1713        Vec<<G as AffineRepr>::ScalarField>,
1714    );
1715
1716    impl<const FULL_ROUNDS: usize, G, CamlG, CamlF> From<ProofTuple<FULL_ROUNDS, G>>
1717        for CamlProofWithPublic<CamlG, CamlF>
1718    where
1719        G: AffineRepr,
1720        G::BaseField: PrimeField,
1721        G: poly_commitment::commitment::EndoCurve,
1722        CamlG: From<G>,
1723        CamlF: From<G::ScalarField>,
1724    {
1725        fn from(pp: ProofTuple<FULL_ROUNDS, G>) -> Self {
1726            let (public_evals, evals) = pp.0.evals.into();
1727            CamlProofWithPublic {
1728                public_evals,
1729                proof: CamlProverProof {
1730                    commitments: pp.0.commitments.into(),
1731                    proof: pp.0.proof.into(),
1732                    evals,
1733                    ft_eval1: pp.0.ft_eval1.into(),
1734                    public: pp.1.into_iter().map(Into::into).collect(),
1735                    prev_challenges: pp.0.prev_challenges.into_iter().map(Into::into).collect(),
1736                },
1737            }
1738        }
1739    }
1740
1741    impl<const FULL_ROUNDS: usize, G, CamlG, CamlF> From<CamlProofWithPublic<CamlG, CamlF>>
1742        for ProofTuple<FULL_ROUNDS, G>
1743    where
1744        CamlF: Clone,
1745        G: AffineRepr + From<CamlG>,
1746        G::BaseField: PrimeField,
1747        G: poly_commitment::commitment::EndoCurve,
1748        G::ScalarField: From<CamlF>,
1749    {
1750        fn from(caml_pp: CamlProofWithPublic<CamlG, CamlF>) -> Self {
1751            let CamlProofWithPublic {
1752                public_evals,
1753                proof: caml_pp,
1754            } = caml_pp;
1755            let proof = ProverProof {
1756                commitments: caml_pp.commitments.into(),
1757                proof: caml_pp.proof.into(),
1758                evals: (public_evals, caml_pp.evals).into(),
1759                ft_eval1: caml_pp.ft_eval1.into(),
1760                prev_challenges: caml_pp
1761                    .prev_challenges
1762                    .into_iter()
1763                    .map(Into::into)
1764                    .collect(),
1765            };
1766
1767            (proof, caml_pp.public.into_iter().map(Into::into).collect())
1768        }
1769    }
1770}