Skip to main content

kimchi/
proof.rs

1//! This module implements the data structures of a proof.
2
3use crate::circuits::{
4    berkeley_columns::Column,
5    gate::GateType,
6    lookup::lookups::LookupPattern,
7    wires::{COLUMNS, PERMUTS},
8};
9use ark_ec::AffineRepr;
10use ark_ff::{FftField, One, Zero};
11use ark_poly::univariate::DensePolynomial;
12use core::array;
13use o1_utils::ExtendedDensePolynomial;
14use poly_commitment::{
15    commitment::{b_poly, b_poly_coefficients, CommitmentCurve, PolyComm},
16    OpenProof,
17};
18use serde::{Deserialize, Serialize};
19use serde_with::serde_as;
20
21//~ spec:startcode
22/// Evaluations of a polynomial at 2 points
23#[serde_as]
24#[derive(Copy, Clone, Serialize, Deserialize, Default, Debug, PartialEq)]
25#[cfg_attr(
26    feature = "ocaml_types",
27    derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)
28)]
29#[serde(bound(
30    serialize = "Vec<o1_utils::serialization::SerdeAs>: serde_with::SerializeAs<Evals>",
31    deserialize = "Vec<o1_utils::serialization::SerdeAs>: serde_with::DeserializeAs<'de, Evals>"
32))]
33pub struct PointEvaluations<Evals> {
34    /// Evaluation at the challenge point zeta.
35    #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
36    pub zeta: Evals,
37    /// Evaluation at `zeta . omega`, the product of the challenge point and the group generator.
38    #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
39    pub zeta_omega: Evals,
40}
41
42// TODO: this should really be vectors here, perhaps create another type for chunked evaluations?
43/// Polynomial evaluations contained in a `ProverProof`.
44/// - **Chunked evaluations** `Field` is instantiated with vectors with a length
45/// that equals the length of the chunk
46/// - **Non chunked evaluations** `Field` is instantiated with a field, so they
47/// are single-sized#[serde_as]
48#[serde_as]
49#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
50pub struct ProofEvaluations<Evals> {
51    /// public input polynomials
52    pub public: Option<Evals>,
53    /// witness polynomials
54    pub w: [Evals; COLUMNS],
55    /// permutation polynomial
56    pub z: Evals,
57    /// permutation polynomials
58    /// (PERMUTS-1 evaluations because the last permutation is only used in
59    /// commitment form)
60    pub s: [Evals; PERMUTS - 1],
61    /// coefficient polynomials
62    pub coefficients: [Evals; COLUMNS],
63    /// evaluation of the generic selector polynomial
64    pub generic_selector: Evals,
65    /// evaluation of the poseidon selector polynomial
66    pub poseidon_selector: Evals,
67    /// evaluation of the elliptic curve addition selector polynomial
68    pub complete_add_selector: Evals,
69    /// evaluation of the elliptic curve variable base scalar multiplication
70    /// selector polynomial
71    pub mul_selector: Evals,
72    /// evaluation of the endoscalar multiplication selector polynomial
73    pub emul_selector: Evals,
74    /// evaluation of the endoscalar multiplication scalar computation selector
75    /// polynomial
76    pub endomul_scalar_selector: Evals,
77
78    // Optional gates
79    /// evaluation of the RangeCheck0 selector polynomial
80    pub range_check0_selector: Option<Evals>,
81    /// evaluation of the RangeCheck1 selector polynomial
82    pub range_check1_selector: Option<Evals>,
83    /// evaluation of the ForeignFieldAdd selector polynomial
84    pub foreign_field_add_selector: Option<Evals>,
85    /// evaluation of the ForeignFieldMul selector polynomial
86    pub foreign_field_mul_selector: Option<Evals>,
87    /// evaluation of the Xor selector polynomial
88    pub xor_selector: Option<Evals>,
89    /// evaluation of the Rot selector polynomial
90    pub rot_selector: Option<Evals>,
91
92    // lookup-related evaluations
93    /// evaluation of lookup aggregation polynomial
94    pub lookup_aggregation: Option<Evals>,
95    /// evaluation of lookup table polynomial
96    pub lookup_table: Option<Evals>,
97    /// evaluation of lookup sorted polynomials
98    pub lookup_sorted: [Option<Evals>; 5],
99    /// evaluation of runtime lookup table polynomial
100    pub runtime_lookup_table: Option<Evals>,
101
102    // lookup selectors
103    /// evaluation of the runtime lookup table selector polynomial
104    pub runtime_lookup_table_selector: Option<Evals>,
105    /// evaluation of the Xor range check pattern selector polynomial
106    pub xor_lookup_selector: Option<Evals>,
107    /// evaluation of the Lookup range check pattern selector polynomial
108    pub lookup_gate_lookup_selector: Option<Evals>,
109    /// evaluation of the RangeCheck range check pattern selector polynomial
110    pub range_check_lookup_selector: Option<Evals>,
111    /// evaluation of the ForeignFieldMul range check pattern selector
112    /// polynomial
113    pub foreign_field_mul_lookup_selector: Option<Evals>,
114}
115
116/// Commitments linked to the lookup feature
117#[serde_as]
118#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
119#[serde(bound = "G: ark_serialize::CanonicalDeserialize + ark_serialize::CanonicalSerialize")]
120pub struct LookupCommitments<G: AffineRepr> {
121    /// Commitments to the sorted lookup table polynomial (may have chunks)
122    pub sorted: Vec<PolyComm<G>>,
123    /// Commitment to the lookup aggregation polynomial
124    pub aggreg: PolyComm<G>,
125    /// Optional commitment to concatenated runtime tables
126    pub runtime: Option<PolyComm<G>>,
127}
128
129/// All the commitments that the prover creates as part of the proof.
130#[serde_as]
131#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
132#[serde(bound = "G: ark_serialize::CanonicalDeserialize + ark_serialize::CanonicalSerialize")]
133pub struct ProverCommitments<G: AffineRepr> {
134    /// The commitments to the witness (execution trace)
135    pub w_comm: [PolyComm<G>; COLUMNS],
136    /// The commitment to the permutation polynomial
137    pub z_comm: PolyComm<G>,
138    /// The commitment to the quotient polynomial
139    pub t_comm: PolyComm<G>,
140    /// Commitments related to the lookup argument
141    pub lookup: Option<LookupCommitments<G>>,
142}
143
144/// The proof that the prover creates from a
145/// [ProverIndex](super::prover_index::ProverIndex) and a `witness`.
146#[serde_as]
147#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
148#[serde(bound = "G: ark_serialize::CanonicalDeserialize + ark_serialize::CanonicalSerialize")]
149pub struct ProverProof<G, OpeningProof, const FULL_ROUNDS: usize>
150where
151    G: CommitmentCurve,
152    OpeningProof: OpenProof<G, FULL_ROUNDS>,
153{
154    /// All the polynomial commitments required in the proof
155    pub commitments: ProverCommitments<G>,
156
157    /// batched commitment opening proof
158    #[serde(bound(
159        serialize = "OpeningProof: Serialize",
160        deserialize = "OpeningProof: Deserialize<'de>"
161    ))]
162    pub proof: OpeningProof,
163
164    /// Two evaluations over a number of committed polynomials
165    pub evals: ProofEvaluations<PointEvaluations<Vec<G::ScalarField>>>,
166
167    /// Required evaluation for [Maller's
168    /// optimization](https://o1-labs.github.io/proof-systems/kimchi/maller_15.html#the-evaluation-of-l)
169    #[serde_as(as = "o1_utils::serialization::SerdeAs")]
170    pub ft_eval1: G::ScalarField,
171
172    /// Accumulators from previously verified proofs in the recursion chain.
173    ///
174    /// Each [`RecursionChallenge`] stores the IPA folding challenges and accumulated
175    /// commitment from verifying a previous proof. Instead of checking the IPA
176    /// immediately (which requires an expensive MSM `<s, G>` where `s` has `2^k`
177    /// elements), we defer this check by storing the accumulator.
178    ///
179    /// During verification, these accumulators are processed as follows:
180    /// 1. The commitments are absorbed into the Fiat-Shamir sponge
181    /// 2. The challenges are used to compute evaluations of `b(X)` at `zeta` and
182    ///    `zeta * omega` (see [`RecursionChallenge::evals`])
183    /// 3. These evaluations are paired with the commitments and included in the
184    ///    batched polynomial commitment check
185    ///
186    /// The actual MSM verification happens in [`SRS::verify`](poly_commitment::ipa::SRS::verify)
187    /// (see `poly-commitment/src/ipa.rs`), where `b_poly_coefficients` computes
188    /// the `2^k` coefficients and they are batched into a single large MSM with
189    /// all other verification checks.
190    ///
191    /// This design enables efficient recursive proof composition as described in
192    /// Section 3.2 of the [Halo paper](https://eprint.iacr.org/2019/1021.pdf).
193    pub prev_challenges: Vec<RecursionChallenge<G>>,
194}
195
196/// Stores the accumulator from a previously verified IPA (Inner Product Argument).
197///
198/// In recursive proof composition, when we verify a proof, the IPA verification
199/// produces an accumulator that can be "deferred" rather than checked immediately.
200/// This accumulator consists of:
201///
202/// - **`chals`**: The folding challenges `u_1, ..., u_k` sampled during the
203///   `k = log_2(n)` rounds of the IPA. These challenges define the
204///   **challenge polynomial** (also called `b(X)` or `h(X)`):
205///   ```text
206///   b(X) = prod_{i=0}^{k-1} (1 + u_{k-i} * X^{2^i})
207///   ```
208///   This polynomial was introduced in Section 3.2 of the
209///   [Halo paper](https://eprint.iacr.org/2019/1021.pdf) as a way to efficiently
210///   represent the folded evaluation point.
211///
212/// - **`comm`**: The accumulated commitment `U = <h, G>` where `h` is the vector
213///   of coefficients of `b(X)` and `G` is the commitment basis. This is the
214///   "deferred" part of IPA verification.
215///
216/// The accumulator satisfies the relation `R_Acc`: anyone can verify it in `O(n)`
217/// time by recomputing `<h, G>`.
218///
219/// See the [accumulation documentation](https://o1-labs.github.io/proof-systems/pickles/accumulation.html)
220/// for a complete description of how these accumulators are used in Pickles.
221#[serde_as]
222#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
223#[serde(bound = "G: ark_serialize::CanonicalDeserialize + ark_serialize::CanonicalSerialize")]
224pub struct RecursionChallenge<G>
225where
226    G: AffineRepr,
227{
228    /// The IPA folding challenges `[u_1, ..., u_k]` that define the challenge
229    /// polynomial `b(X)`. See [`b_poly`].
230    #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
231    pub chals: Vec<G::ScalarField>,
232    /// The accumulated commitment from IPA verification.
233    ///
234    /// This commitment is used in two places:
235    /// 1. Absorbed into the Fq-sponge for Fiat-Shamir (see `prover.rs` and
236    ///    `verifier.rs` where commitments of previous challenges are absorbed).
237    /// 2. Included in the batched polynomial commitment verification, paired
238    ///    with evaluations of `b(X)` at `zeta` and `zeta * omega` (see
239    ///    `verifier.rs` where `polys` is constructed from `prev_challenges`).
240    pub comm: PolyComm<G>,
241}
242
243//~ spec:endcode
244
245impl<Evals> PointEvaluations<Evals> {
246    pub fn map<Evals2, FN: Fn(Evals) -> Evals2>(self, f: &FN) -> PointEvaluations<Evals2> {
247        let PointEvaluations { zeta, zeta_omega } = self;
248        PointEvaluations {
249            zeta: f(zeta),
250            zeta_omega: f(zeta_omega),
251        }
252    }
253
254    pub fn map_ref<Evals2, FN: Fn(&Evals) -> Evals2>(&self, f: &FN) -> PointEvaluations<Evals2> {
255        let PointEvaluations { zeta, zeta_omega } = self;
256        PointEvaluations {
257            zeta: f(zeta),
258            zeta_omega: f(zeta_omega),
259        }
260    }
261}
262
263impl<Eval> ProofEvaluations<Eval> {
264    pub fn map<Eval2, FN: Fn(Eval) -> Eval2>(self, f: &FN) -> ProofEvaluations<Eval2> {
265        let ProofEvaluations {
266            public,
267            w,
268            z,
269            s,
270            coefficients,
271            generic_selector,
272            poseidon_selector,
273            complete_add_selector,
274            mul_selector,
275            emul_selector,
276            endomul_scalar_selector,
277            range_check0_selector,
278            range_check1_selector,
279            foreign_field_add_selector,
280            foreign_field_mul_selector,
281            xor_selector,
282            rot_selector,
283            lookup_aggregation,
284            lookup_table,
285            lookup_sorted,
286            runtime_lookup_table,
287            runtime_lookup_table_selector,
288            xor_lookup_selector,
289            lookup_gate_lookup_selector,
290            range_check_lookup_selector,
291            foreign_field_mul_lookup_selector,
292        } = self;
293        ProofEvaluations {
294            public: public.map(f),
295            w: w.map(f),
296            z: f(z),
297            s: s.map(f),
298            coefficients: coefficients.map(f),
299            generic_selector: f(generic_selector),
300            poseidon_selector: f(poseidon_selector),
301            complete_add_selector: f(complete_add_selector),
302            mul_selector: f(mul_selector),
303            emul_selector: f(emul_selector),
304            endomul_scalar_selector: f(endomul_scalar_selector),
305            range_check0_selector: range_check0_selector.map(f),
306            range_check1_selector: range_check1_selector.map(f),
307            foreign_field_add_selector: foreign_field_add_selector.map(f),
308            foreign_field_mul_selector: foreign_field_mul_selector.map(f),
309            xor_selector: xor_selector.map(f),
310            rot_selector: rot_selector.map(f),
311            lookup_aggregation: lookup_aggregation.map(f),
312            lookup_table: lookup_table.map(f),
313            lookup_sorted: lookup_sorted.map(|x| x.map(f)),
314            runtime_lookup_table: runtime_lookup_table.map(f),
315            runtime_lookup_table_selector: runtime_lookup_table_selector.map(f),
316            xor_lookup_selector: xor_lookup_selector.map(f),
317            lookup_gate_lookup_selector: lookup_gate_lookup_selector.map(f),
318            range_check_lookup_selector: range_check_lookup_selector.map(f),
319            foreign_field_mul_lookup_selector: foreign_field_mul_lookup_selector.map(f),
320        }
321    }
322
323    pub fn map_ref<Eval2, FN: Fn(&Eval) -> Eval2>(&self, f: &FN) -> ProofEvaluations<Eval2> {
324        let ProofEvaluations {
325            public,
326            w: [w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14],
327            z,
328            s: [s0, s1, s2, s3, s4, s5],
329            coefficients: [c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14],
330            generic_selector,
331            poseidon_selector,
332            complete_add_selector,
333            mul_selector,
334            emul_selector,
335            endomul_scalar_selector,
336            range_check0_selector,
337            range_check1_selector,
338            foreign_field_add_selector,
339            foreign_field_mul_selector,
340            xor_selector,
341            rot_selector,
342            lookup_aggregation,
343            lookup_table,
344            lookup_sorted,
345            runtime_lookup_table,
346            runtime_lookup_table_selector,
347            xor_lookup_selector,
348            lookup_gate_lookup_selector,
349            range_check_lookup_selector,
350            foreign_field_mul_lookup_selector,
351        } = self;
352        ProofEvaluations {
353            public: public.as_ref().map(f),
354            w: [
355                f(w0),
356                f(w1),
357                f(w2),
358                f(w3),
359                f(w4),
360                f(w5),
361                f(w6),
362                f(w7),
363                f(w8),
364                f(w9),
365                f(w10),
366                f(w11),
367                f(w12),
368                f(w13),
369                f(w14),
370            ],
371            z: f(z),
372            s: [f(s0), f(s1), f(s2), f(s3), f(s4), f(s5)],
373            coefficients: [
374                f(c0),
375                f(c1),
376                f(c2),
377                f(c3),
378                f(c4),
379                f(c5),
380                f(c6),
381                f(c7),
382                f(c8),
383                f(c9),
384                f(c10),
385                f(c11),
386                f(c12),
387                f(c13),
388                f(c14),
389            ],
390            generic_selector: f(generic_selector),
391            poseidon_selector: f(poseidon_selector),
392            complete_add_selector: f(complete_add_selector),
393            mul_selector: f(mul_selector),
394            emul_selector: f(emul_selector),
395            endomul_scalar_selector: f(endomul_scalar_selector),
396            range_check0_selector: range_check0_selector.as_ref().map(f),
397            range_check1_selector: range_check1_selector.as_ref().map(f),
398            foreign_field_add_selector: foreign_field_add_selector.as_ref().map(f),
399            foreign_field_mul_selector: foreign_field_mul_selector.as_ref().map(f),
400            xor_selector: xor_selector.as_ref().map(f),
401            rot_selector: rot_selector.as_ref().map(f),
402            lookup_aggregation: lookup_aggregation.as_ref().map(f),
403            lookup_table: lookup_table.as_ref().map(f),
404            lookup_sorted: array::from_fn(|i| lookup_sorted[i].as_ref().map(f)),
405            runtime_lookup_table: runtime_lookup_table.as_ref().map(f),
406            runtime_lookup_table_selector: runtime_lookup_table_selector.as_ref().map(f),
407            xor_lookup_selector: xor_lookup_selector.as_ref().map(f),
408            lookup_gate_lookup_selector: lookup_gate_lookup_selector.as_ref().map(f),
409            range_check_lookup_selector: range_check_lookup_selector.as_ref().map(f),
410            foreign_field_mul_lookup_selector: foreign_field_mul_lookup_selector.as_ref().map(f),
411        }
412    }
413}
414
415impl<G: AffineRepr> RecursionChallenge<G> {
416    pub fn new(chals: Vec<G::ScalarField>, comm: PolyComm<G>) -> RecursionChallenge<G> {
417        RecursionChallenge { chals, comm }
418    }
419
420    /// Computes evaluations of the challenge polynomial `b(X)` at the given points.
421    ///
422    /// The challenge polynomial is defined by the IPA challenges as:
423    /// ```text
424    /// b(X) = prod_{i=0}^{k-1} (1 + u_{k-i} * X^{2^i})
425    /// ```
426    /// where `u_1, ..., u_k` are the challenges sampled during the `k` rounds of
427    /// the IPA protocol (stored in `self.chals`).
428    ///
429    /// This method evaluates `b(X)` at `evaluation_points` (typically `zeta` and
430    /// `zeta * omega`). If the polynomial degree exceeds `max_poly_size`, the
431    /// evaluations are "chunked" to handle polynomial splitting.
432    ///
433    /// These evaluations are paired with [`Self::comm`] and included in the
434    /// batched polynomial commitment verification (see `verifier.rs`).
435    ///
436    /// The MSM has size `2^k` where `k` is the number of IPA rounds (e.g., `k = 15` for
437    /// a domain of size `2^15`, giving an MSM of 32768 points). Computing this in-circuit
438    /// would require EC scalar multiplication: using [`VarBaseMul`](crate::circuits::polynomials::varbasemul)
439    /// costs 2 rows per 5 bits (~104 rows for a 256-bit scalar). For an MSM of 32768 points,
440    /// the constraint count would be higher than the accepted circuit size. By deferring to
441    /// the out-of-circuit verifier, we avoid this cost entirely.
442    ///
443    /// # Arguments
444    /// * `max_poly_size` - Maximum polynomial size for chunking
445    /// * `evaluation_points` - Points at which to evaluate (typically `[zeta, zeta * omega]`)
446    /// * `powers_of_eval_points_for_chunks` - Powers used for recombining chunks
447    ///
448    /// # Returns
449    /// A vector of evaluation vectors, one per evaluation point. Each inner vector
450    /// contains the chunked evaluations (or a single evaluation if no chunking needed).
451    ///
452    /// # References
453    /// - [Halo paper, Section 3.2](https://eprint.iacr.org/2019/1021.pdf)
454    pub fn evals(
455        &self,
456        max_poly_size: usize,
457        evaluation_points: &[G::ScalarField],
458        powers_of_eval_points_for_chunks: &[G::ScalarField],
459    ) -> Vec<Vec<G::ScalarField>> {
460        let RecursionChallenge { chals, comm: _ } = self;
461        // No need to check the correctness of poly explicitly. Its correctness is assured by the
462        // checking of the inner product argument.
463        let b_len = 1 << chals.len();
464        let mut b: Option<Vec<G::ScalarField>> = None;
465
466        (0..2)
467            .map(|i| {
468                let full = b_poly(chals, evaluation_points[i]);
469                if max_poly_size == b_len {
470                    return vec![full];
471                }
472                let mut betaacc = G::ScalarField::one();
473                let diff = (max_poly_size..b_len)
474                    .map(|j| {
475                        let b_j = match &b {
476                            None => {
477                                let t = b_poly_coefficients(chals);
478                                let res = t[j];
479                                b = Some(t);
480                                res
481                            }
482                            Some(b) => b[j],
483                        };
484
485                        let ret = betaacc * b_j;
486                        betaacc *= &evaluation_points[i];
487                        ret
488                    })
489                    .fold(G::ScalarField::zero(), |x, y| x + y);
490                vec![full - (diff * powers_of_eval_points_for_chunks[i]), diff]
491            })
492            .collect()
493    }
494}
495
496impl<F: Zero + Copy> ProofEvaluations<PointEvaluations<F>> {
497    pub fn dummy_with_witness_evaluations(
498        curr: [F; COLUMNS],
499        next: [F; COLUMNS],
500    ) -> ProofEvaluations<PointEvaluations<F>> {
501        let pt = |curr, next| PointEvaluations {
502            zeta: curr,
503            zeta_omega: next,
504        };
505        ProofEvaluations {
506            public: Some(pt(F::zero(), F::zero())),
507            w: array::from_fn(|i| pt(curr[i], next[i])),
508            z: pt(F::zero(), F::zero()),
509            s: array::from_fn(|_| pt(F::zero(), F::zero())),
510            coefficients: array::from_fn(|_| pt(F::zero(), F::zero())),
511            generic_selector: pt(F::zero(), F::zero()),
512            poseidon_selector: pt(F::zero(), F::zero()),
513            complete_add_selector: pt(F::zero(), F::zero()),
514            mul_selector: pt(F::zero(), F::zero()),
515            emul_selector: pt(F::zero(), F::zero()),
516            endomul_scalar_selector: pt(F::zero(), F::zero()),
517            range_check0_selector: None,
518            range_check1_selector: None,
519            foreign_field_add_selector: None,
520            foreign_field_mul_selector: None,
521            xor_selector: None,
522            rot_selector: None,
523            lookup_aggregation: None,
524            lookup_table: None,
525            lookup_sorted: array::from_fn(|_| None),
526            runtime_lookup_table: None,
527            runtime_lookup_table_selector: None,
528            xor_lookup_selector: None,
529            lookup_gate_lookup_selector: None,
530            range_check_lookup_selector: None,
531            foreign_field_mul_lookup_selector: None,
532        }
533    }
534}
535
536impl<F: FftField> ProofEvaluations<PointEvaluations<Vec<F>>> {
537    pub fn combine(&self, pt: &PointEvaluations<F>) -> ProofEvaluations<PointEvaluations<F>> {
538        self.map_ref(&|evals| PointEvaluations {
539            zeta: DensePolynomial::eval_polynomial(&evals.zeta, pt.zeta),
540            zeta_omega: DensePolynomial::eval_polynomial(&evals.zeta_omega, pt.zeta_omega),
541        })
542    }
543}
544
545impl<F> ProofEvaluations<F> {
546    pub fn get_column(&self, col: Column) -> Option<&F> {
547        match col {
548            Column::Witness(i) => Some(&self.w[i]),
549            Column::Z => Some(&self.z),
550            Column::LookupSorted(i) => self.lookup_sorted[i].as_ref(),
551            Column::LookupAggreg => self.lookup_aggregation.as_ref(),
552            Column::LookupTable => self.lookup_table.as_ref(),
553            Column::LookupKindIndex(LookupPattern::Xor) => self.xor_lookup_selector.as_ref(),
554            Column::LookupKindIndex(LookupPattern::Lookup) => {
555                self.lookup_gate_lookup_selector.as_ref()
556            }
557            Column::LookupKindIndex(LookupPattern::RangeCheck) => {
558                self.range_check_lookup_selector.as_ref()
559            }
560            Column::LookupKindIndex(LookupPattern::ForeignFieldMul) => {
561                self.foreign_field_mul_lookup_selector.as_ref()
562            }
563            Column::LookupRuntimeSelector => self.runtime_lookup_table_selector.as_ref(),
564            Column::LookupRuntimeTable => self.runtime_lookup_table.as_ref(),
565            Column::Index(GateType::Generic) => Some(&self.generic_selector),
566            Column::Index(GateType::Poseidon) => Some(&self.poseidon_selector),
567            Column::Index(GateType::CompleteAdd) => Some(&self.complete_add_selector),
568            Column::Index(GateType::VarBaseMul) => Some(&self.mul_selector),
569            Column::Index(GateType::EndoMul) => Some(&self.emul_selector),
570            Column::Index(GateType::EndoMulScalar) => Some(&self.endomul_scalar_selector),
571            Column::Index(GateType::RangeCheck0) => self.range_check0_selector.as_ref(),
572            Column::Index(GateType::RangeCheck1) => self.range_check1_selector.as_ref(),
573            Column::Index(GateType::ForeignFieldAdd) => self.foreign_field_add_selector.as_ref(),
574            Column::Index(GateType::ForeignFieldMul) => self.foreign_field_mul_selector.as_ref(),
575            Column::Index(GateType::Xor16) => self.xor_selector.as_ref(),
576            Column::Index(GateType::Rot64) => self.rot_selector.as_ref(),
577            Column::Index(_) => None,
578            Column::Coefficient(i) => Some(&self.coefficients[i]),
579            Column::Permutation(i) => Some(&self.s[i]),
580        }
581    }
582}
583
584//
585// OCaml types
586//
587
588#[cfg(feature = "ocaml_types")]
589pub mod caml {
590    use super::*;
591    use poly_commitment::commitment::caml::CamlPolyComm;
592
593    //
594    // CamlRecursionChallenge<CamlG, CamlF>
595    //
596
597    #[derive(Clone, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
598    pub struct CamlRecursionChallenge<CamlG, CamlF> {
599        pub chals: Vec<CamlF>,
600        pub comm: CamlPolyComm<CamlG>,
601    }
602
603    //
604    // CamlRecursionChallenge<CamlG, CamlF> <-> RecursionChallenge<G>
605    //
606
607    impl<G, CamlG, CamlF> From<RecursionChallenge<G>> for CamlRecursionChallenge<CamlG, CamlF>
608    where
609        G: AffineRepr,
610        CamlG: From<G>,
611        CamlF: From<G::ScalarField>,
612    {
613        fn from(ch: RecursionChallenge<G>) -> Self {
614            Self {
615                chals: ch.chals.into_iter().map(Into::into).collect(),
616                comm: ch.comm.into(),
617            }
618        }
619    }
620
621    impl<G, CamlG, CamlF> From<CamlRecursionChallenge<CamlG, CamlF>> for RecursionChallenge<G>
622    where
623        G: AffineRepr + From<CamlG>,
624        G::ScalarField: From<CamlF>,
625    {
626        fn from(caml_ch: CamlRecursionChallenge<CamlG, CamlF>) -> RecursionChallenge<G> {
627            RecursionChallenge {
628                chals: caml_ch.chals.into_iter().map(Into::into).collect(),
629                comm: caml_ch.comm.into(),
630            }
631        }
632    }
633
634    //
635    // CamlProofEvaluations<CamlF>
636    //
637
638    #[allow(clippy::type_complexity)]
639    #[derive(Clone, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
640    pub struct CamlProofEvaluations<CamlF> {
641        pub w: (
642            PointEvaluations<Vec<CamlF>>,
643            PointEvaluations<Vec<CamlF>>,
644            PointEvaluations<Vec<CamlF>>,
645            PointEvaluations<Vec<CamlF>>,
646            PointEvaluations<Vec<CamlF>>,
647            PointEvaluations<Vec<CamlF>>,
648            PointEvaluations<Vec<CamlF>>,
649            PointEvaluations<Vec<CamlF>>,
650            PointEvaluations<Vec<CamlF>>,
651            PointEvaluations<Vec<CamlF>>,
652            PointEvaluations<Vec<CamlF>>,
653            PointEvaluations<Vec<CamlF>>,
654            PointEvaluations<Vec<CamlF>>,
655            PointEvaluations<Vec<CamlF>>,
656            PointEvaluations<Vec<CamlF>>,
657        ),
658        pub z: PointEvaluations<Vec<CamlF>>,
659        pub s: (
660            PointEvaluations<Vec<CamlF>>,
661            PointEvaluations<Vec<CamlF>>,
662            PointEvaluations<Vec<CamlF>>,
663            PointEvaluations<Vec<CamlF>>,
664            PointEvaluations<Vec<CamlF>>,
665            PointEvaluations<Vec<CamlF>>,
666        ),
667        pub coefficients: (
668            PointEvaluations<Vec<CamlF>>,
669            PointEvaluations<Vec<CamlF>>,
670            PointEvaluations<Vec<CamlF>>,
671            PointEvaluations<Vec<CamlF>>,
672            PointEvaluations<Vec<CamlF>>,
673            PointEvaluations<Vec<CamlF>>,
674            PointEvaluations<Vec<CamlF>>,
675            PointEvaluations<Vec<CamlF>>,
676            PointEvaluations<Vec<CamlF>>,
677            PointEvaluations<Vec<CamlF>>,
678            PointEvaluations<Vec<CamlF>>,
679            PointEvaluations<Vec<CamlF>>,
680            PointEvaluations<Vec<CamlF>>,
681            PointEvaluations<Vec<CamlF>>,
682            PointEvaluations<Vec<CamlF>>,
683        ),
684
685        pub generic_selector: PointEvaluations<Vec<CamlF>>,
686        pub poseidon_selector: PointEvaluations<Vec<CamlF>>,
687        pub complete_add_selector: PointEvaluations<Vec<CamlF>>,
688        pub mul_selector: PointEvaluations<Vec<CamlF>>,
689        pub emul_selector: PointEvaluations<Vec<CamlF>>,
690        pub endomul_scalar_selector: PointEvaluations<Vec<CamlF>>,
691
692        pub range_check0_selector: Option<PointEvaluations<Vec<CamlF>>>,
693        pub range_check1_selector: Option<PointEvaluations<Vec<CamlF>>>,
694        pub foreign_field_add_selector: Option<PointEvaluations<Vec<CamlF>>>,
695        pub foreign_field_mul_selector: Option<PointEvaluations<Vec<CamlF>>>,
696        pub xor_selector: Option<PointEvaluations<Vec<CamlF>>>,
697        pub rot_selector: Option<PointEvaluations<Vec<CamlF>>>,
698        pub lookup_aggregation: Option<PointEvaluations<Vec<CamlF>>>,
699        pub lookup_table: Option<PointEvaluations<Vec<CamlF>>>,
700        pub lookup_sorted: Vec<Option<PointEvaluations<Vec<CamlF>>>>,
701        pub runtime_lookup_table: Option<PointEvaluations<Vec<CamlF>>>,
702
703        pub runtime_lookup_table_selector: Option<PointEvaluations<Vec<CamlF>>>,
704        pub xor_lookup_selector: Option<PointEvaluations<Vec<CamlF>>>,
705        pub lookup_gate_lookup_selector: Option<PointEvaluations<Vec<CamlF>>>,
706        pub range_check_lookup_selector: Option<PointEvaluations<Vec<CamlF>>>,
707        pub foreign_field_mul_lookup_selector: Option<PointEvaluations<Vec<CamlF>>>,
708    }
709
710    //
711    // ProofEvaluations<Vec<F>> <-> CamlProofEvaluations<CamlF>
712    //
713
714    impl<F, CamlF> From<ProofEvaluations<PointEvaluations<Vec<F>>>>
715        for (
716            Option<PointEvaluations<Vec<CamlF>>>,
717            CamlProofEvaluations<CamlF>,
718        )
719    where
720        F: Clone,
721        CamlF: From<F>,
722    {
723        fn from(pe: ProofEvaluations<PointEvaluations<Vec<F>>>) -> Self {
724            let first = pe.public.map(|x: PointEvaluations<Vec<F>>| {
725                // map both fields of each evaluation.
726                x.map(&|x: Vec<F>| {
727                    let y: Vec<CamlF> = x.into_iter().map(Into::into).collect();
728                    y
729                })
730            });
731            let w = (
732                pe.w[0]
733                    .clone()
734                    .map(&|x| x.into_iter().map(Into::into).collect()),
735                pe.w[1]
736                    .clone()
737                    .map(&|x| x.into_iter().map(Into::into).collect()),
738                pe.w[2]
739                    .clone()
740                    .map(&|x| x.into_iter().map(Into::into).collect()),
741                pe.w[3]
742                    .clone()
743                    .map(&|x| x.into_iter().map(Into::into).collect()),
744                pe.w[4]
745                    .clone()
746                    .map(&|x| x.into_iter().map(Into::into).collect()),
747                pe.w[5]
748                    .clone()
749                    .map(&|x| x.into_iter().map(Into::into).collect()),
750                pe.w[6]
751                    .clone()
752                    .map(&|x| x.into_iter().map(Into::into).collect()),
753                pe.w[7]
754                    .clone()
755                    .map(&|x| x.into_iter().map(Into::into).collect()),
756                pe.w[8]
757                    .clone()
758                    .map(&|x| x.into_iter().map(Into::into).collect()),
759                pe.w[9]
760                    .clone()
761                    .map(&|x| x.into_iter().map(Into::into).collect()),
762                pe.w[10]
763                    .clone()
764                    .map(&|x| x.into_iter().map(Into::into).collect()),
765                pe.w[11]
766                    .clone()
767                    .map(&|x| x.into_iter().map(Into::into).collect()),
768                pe.w[12]
769                    .clone()
770                    .map(&|x| x.into_iter().map(Into::into).collect()),
771                pe.w[13]
772                    .clone()
773                    .map(&|x| x.into_iter().map(Into::into).collect()),
774                pe.w[14]
775                    .clone()
776                    .map(&|x| x.into_iter().map(Into::into).collect()),
777            );
778            let coefficients = (
779                pe.coefficients[0]
780                    .clone()
781                    .map(&|x| x.into_iter().map(Into::into).collect()),
782                pe.coefficients[1]
783                    .clone()
784                    .map(&|x| x.into_iter().map(Into::into).collect()),
785                pe.coefficients[2]
786                    .clone()
787                    .map(&|x| x.into_iter().map(Into::into).collect()),
788                pe.coefficients[3]
789                    .clone()
790                    .map(&|x| x.into_iter().map(Into::into).collect()),
791                pe.coefficients[4]
792                    .clone()
793                    .map(&|x| x.into_iter().map(Into::into).collect()),
794                pe.coefficients[5]
795                    .clone()
796                    .map(&|x| x.into_iter().map(Into::into).collect()),
797                pe.coefficients[6]
798                    .clone()
799                    .map(&|x| x.into_iter().map(Into::into).collect()),
800                pe.coefficients[7]
801                    .clone()
802                    .map(&|x| x.into_iter().map(Into::into).collect()),
803                pe.coefficients[8]
804                    .clone()
805                    .map(&|x| x.into_iter().map(Into::into).collect()),
806                pe.coefficients[9]
807                    .clone()
808                    .map(&|x| x.into_iter().map(Into::into).collect()),
809                pe.coefficients[10]
810                    .clone()
811                    .map(&|x| x.into_iter().map(Into::into).collect()),
812                pe.coefficients[11]
813                    .clone()
814                    .map(&|x| x.into_iter().map(Into::into).collect()),
815                pe.coefficients[12]
816                    .clone()
817                    .map(&|x| x.into_iter().map(Into::into).collect()),
818                pe.coefficients[13]
819                    .clone()
820                    .map(&|x| x.into_iter().map(Into::into).collect()),
821                pe.coefficients[14]
822                    .clone()
823                    .map(&|x| x.into_iter().map(Into::into).collect()),
824            );
825            let s = (
826                pe.s[0]
827                    .clone()
828                    .map(&|x| x.into_iter().map(Into::into).collect()),
829                pe.s[1]
830                    .clone()
831                    .map(&|x| x.into_iter().map(Into::into).collect()),
832                pe.s[2]
833                    .clone()
834                    .map(&|x| x.into_iter().map(Into::into).collect()),
835                pe.s[3]
836                    .clone()
837                    .map(&|x| x.into_iter().map(Into::into).collect()),
838                pe.s[4]
839                    .clone()
840                    .map(&|x| x.into_iter().map(Into::into).collect()),
841                pe.s[5]
842                    .clone()
843                    .map(&|x| x.into_iter().map(Into::into).collect()),
844            );
845
846            let second = CamlProofEvaluations {
847                w,
848                coefficients,
849                z: pe.z.map(&|x| x.into_iter().map(Into::into).collect()),
850                s,
851                generic_selector: pe
852                    .generic_selector
853                    .map(&|x| x.into_iter().map(Into::into).collect()),
854                poseidon_selector: pe
855                    .poseidon_selector
856                    .map(&|x| x.into_iter().map(Into::into).collect()),
857                complete_add_selector: pe
858                    .complete_add_selector
859                    .map(&|x| x.into_iter().map(Into::into).collect()),
860                mul_selector: pe
861                    .mul_selector
862                    .map(&|x| x.into_iter().map(Into::into).collect()),
863                emul_selector: pe
864                    .emul_selector
865                    .map(&|x| x.into_iter().map(Into::into).collect()),
866                endomul_scalar_selector: pe
867                    .endomul_scalar_selector
868                    .map(&|x| x.into_iter().map(Into::into).collect()),
869                range_check0_selector: pe
870                    .range_check0_selector
871                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
872                range_check1_selector: pe
873                    .range_check1_selector
874                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
875                foreign_field_add_selector: pe
876                    .foreign_field_add_selector
877                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
878                foreign_field_mul_selector: pe
879                    .foreign_field_mul_selector
880                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
881                xor_selector: pe
882                    .xor_selector
883                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
884                rot_selector: pe
885                    .rot_selector
886                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
887                lookup_aggregation: pe
888                    .lookup_aggregation
889                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
890                lookup_table: pe
891                    .lookup_table
892                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
893                lookup_sorted: pe
894                    .lookup_sorted
895                    .iter()
896                    .map(|x| {
897                        x.as_ref().map(|x| {
898                            x.map_ref(&|x| x.clone().into_iter().map(Into::into).collect())
899                        })
900                    })
901                    .collect::<Vec<_>>(),
902                runtime_lookup_table: pe
903                    .runtime_lookup_table
904                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
905                runtime_lookup_table_selector: pe
906                    .runtime_lookup_table_selector
907                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
908                xor_lookup_selector: pe
909                    .xor_lookup_selector
910                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
911                lookup_gate_lookup_selector: pe
912                    .lookup_gate_lookup_selector
913                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
914                range_check_lookup_selector: pe
915                    .range_check_lookup_selector
916                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
917                foreign_field_mul_lookup_selector: pe
918                    .foreign_field_mul_lookup_selector
919                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
920            };
921
922            (first, second)
923        }
924    }
925
926    impl<F, CamlF>
927        From<(
928            Option<PointEvaluations<Vec<CamlF>>>,
929            CamlProofEvaluations<CamlF>,
930        )> for ProofEvaluations<PointEvaluations<Vec<F>>>
931    where
932        F: Clone,
933        CamlF: Clone,
934        F: From<CamlF>,
935    {
936        fn from(
937            (public, cpe): (
938                Option<PointEvaluations<Vec<CamlF>>>,
939                CamlProofEvaluations<CamlF>,
940            ),
941        ) -> Self {
942            let w = [
943                cpe.w.0.map(&|x| x.into_iter().map(Into::into).collect()),
944                cpe.w.1.map(&|x| x.into_iter().map(Into::into).collect()),
945                cpe.w.2.map(&|x| x.into_iter().map(Into::into).collect()),
946                cpe.w.3.map(&|x| x.into_iter().map(Into::into).collect()),
947                cpe.w.4.map(&|x| x.into_iter().map(Into::into).collect()),
948                cpe.w.5.map(&|x| x.into_iter().map(Into::into).collect()),
949                cpe.w.6.map(&|x| x.into_iter().map(Into::into).collect()),
950                cpe.w.7.map(&|x| x.into_iter().map(Into::into).collect()),
951                cpe.w.8.map(&|x| x.into_iter().map(Into::into).collect()),
952                cpe.w.9.map(&|x| x.into_iter().map(Into::into).collect()),
953                cpe.w.10.map(&|x| x.into_iter().map(Into::into).collect()),
954                cpe.w.11.map(&|x| x.into_iter().map(Into::into).collect()),
955                cpe.w.12.map(&|x| x.into_iter().map(Into::into).collect()),
956                cpe.w.13.map(&|x| x.into_iter().map(Into::into).collect()),
957                cpe.w.14.map(&|x| x.into_iter().map(Into::into).collect()),
958            ];
959            let coefficients = [
960                cpe.coefficients
961                    .0
962                    .map(&|x| x.into_iter().map(Into::into).collect()),
963                cpe.coefficients
964                    .1
965                    .map(&|x| x.into_iter().map(Into::into).collect()),
966                cpe.coefficients
967                    .2
968                    .map(&|x| x.into_iter().map(Into::into).collect()),
969                cpe.coefficients
970                    .3
971                    .map(&|x| x.into_iter().map(Into::into).collect()),
972                cpe.coefficients
973                    .4
974                    .map(&|x| x.into_iter().map(Into::into).collect()),
975                cpe.coefficients
976                    .5
977                    .map(&|x| x.into_iter().map(Into::into).collect()),
978                cpe.coefficients
979                    .6
980                    .map(&|x| x.into_iter().map(Into::into).collect()),
981                cpe.coefficients
982                    .7
983                    .map(&|x| x.into_iter().map(Into::into).collect()),
984                cpe.coefficients
985                    .8
986                    .map(&|x| x.into_iter().map(Into::into).collect()),
987                cpe.coefficients
988                    .9
989                    .map(&|x| x.into_iter().map(Into::into).collect()),
990                cpe.coefficients
991                    .10
992                    .map(&|x| x.into_iter().map(Into::into).collect()),
993                cpe.coefficients
994                    .11
995                    .map(&|x| x.into_iter().map(Into::into).collect()),
996                cpe.coefficients
997                    .12
998                    .map(&|x| x.into_iter().map(Into::into).collect()),
999                cpe.coefficients
1000                    .13
1001                    .map(&|x| x.into_iter().map(Into::into).collect()),
1002                cpe.coefficients
1003                    .14
1004                    .map(&|x| x.into_iter().map(Into::into).collect()),
1005            ];
1006            let s = [
1007                cpe.s.0.map(&|x| x.into_iter().map(Into::into).collect()),
1008                cpe.s.1.map(&|x| x.into_iter().map(Into::into).collect()),
1009                cpe.s.2.map(&|x| x.into_iter().map(Into::into).collect()),
1010                cpe.s.3.map(&|x| x.into_iter().map(Into::into).collect()),
1011                cpe.s.4.map(&|x| x.into_iter().map(Into::into).collect()),
1012                cpe.s.5.map(&|x| x.into_iter().map(Into::into).collect()),
1013            ];
1014
1015            Self {
1016                public: public.map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1017                w,
1018                coefficients,
1019                z: cpe.z.map(&|x| x.into_iter().map(Into::into).collect()),
1020                s,
1021                generic_selector: cpe
1022                    .generic_selector
1023                    .map(&|x| x.into_iter().map(Into::into).collect()),
1024                poseidon_selector: cpe
1025                    .poseidon_selector
1026                    .map(&|x| x.into_iter().map(Into::into).collect()),
1027                complete_add_selector: cpe
1028                    .complete_add_selector
1029                    .map(&|x| x.into_iter().map(Into::into).collect()),
1030                mul_selector: cpe
1031                    .mul_selector
1032                    .map(&|x| x.into_iter().map(Into::into).collect()),
1033                emul_selector: cpe
1034                    .emul_selector
1035                    .map(&|x| x.into_iter().map(Into::into).collect()),
1036                endomul_scalar_selector: cpe
1037                    .endomul_scalar_selector
1038                    .map(&|x| x.into_iter().map(Into::into).collect()),
1039                range_check0_selector: cpe
1040                    .range_check0_selector
1041                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1042                range_check1_selector: cpe
1043                    .range_check1_selector
1044                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1045                foreign_field_add_selector: cpe
1046                    .foreign_field_add_selector
1047                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1048                foreign_field_mul_selector: cpe
1049                    .foreign_field_mul_selector
1050                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1051                xor_selector: cpe
1052                    .xor_selector
1053                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1054                rot_selector: cpe
1055                    .rot_selector
1056                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1057                lookup_aggregation: cpe
1058                    .lookup_aggregation
1059                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1060                lookup_table: cpe
1061                    .lookup_table
1062                    .map(|x| x.map(&|x| x.into_iter().map(Into::into).collect())),
1063                lookup_sorted: {
1064                    assert_eq!(cpe.lookup_sorted.len(), 5); // Invalid proof
1065                    array::from_fn(|i| {
1066                        cpe.lookup_sorted[i]
1067                            .as_ref()
1068                            .map(|x| x.clone().map(&|x| x.into_iter().map(Into::into).collect()))
1069                    })
1070                },
1071                runtime_lookup_table: cpe
1072                    .runtime_lookup_table
1073                    .map(|x| x.map(&|x| x.iter().map(|x| x.clone().into()).collect())),
1074                runtime_lookup_table_selector: cpe
1075                    .runtime_lookup_table_selector
1076                    .map(|x| x.map(&|x| x.iter().map(|x| x.clone().into()).collect())),
1077                xor_lookup_selector: cpe
1078                    .xor_lookup_selector
1079                    .map(|x| x.map(&|x| x.iter().map(|x| x.clone().into()).collect())),
1080                lookup_gate_lookup_selector: cpe
1081                    .lookup_gate_lookup_selector
1082                    .map(|x| x.map(&|x| x.iter().map(|x| x.clone().into()).collect())),
1083                range_check_lookup_selector: cpe
1084                    .range_check_lookup_selector
1085                    .map(|x| x.map(&|x| x.iter().map(|x| x.clone().into()).collect())),
1086                foreign_field_mul_lookup_selector: cpe
1087                    .foreign_field_mul_lookup_selector
1088                    .map(|x| x.map(&|x| x.iter().map(|x| x.clone().into()).collect())),
1089            }
1090        }
1091    }
1092}
1093
1094#[cfg(test)]
1095mod tests {
1096    use super::*;
1097    use ark_ec::{CurveGroup, VariableBaseMSM};
1098    use ark_ff::Field;
1099    use mina_curves::pasta::{Fp, Fq, Vesta, VestaParameters};
1100    use poly_commitment::PolyComm;
1101    use std::str::FromStr;
1102
1103    type VestaGroup = ark_ec::short_weierstrass::Projective<VestaParameters>;
1104
1105    /// Regression test for RecursionChallenge::evals.
1106    ///
1107    /// This test verifies that the challenge polynomial evaluation remains
1108    /// consistent. The challenge polynomial b(X) is defined as:
1109    /// ```text
1110    /// b(X) = prod_{i=0}^{k-1} (1 + u_{k-i} * X^{2^i})
1111    /// ```
1112    /// See Section 3.2 of the [Halo paper](https://eprint.iacr.org/2019/1021.pdf).
1113    #[test]
1114    fn test_recursion_challenge_evals_regression() {
1115        // 15 challenges (typical for domain size 2^15)
1116        let chals: Vec<Fp> = (2..=16).map(|i| Fp::from(i as u64)).collect();
1117
1118        let comm = PolyComm::<Vesta> {
1119            chunks: vec![Vesta::generator()],
1120        };
1121
1122        let rec_chal = RecursionChallenge::<Vesta>::new(chals.clone(), comm);
1123
1124        let zeta = Fp::from(17u64);
1125        let zeta_omega = Fp::from(19u64);
1126        let evaluation_points = [zeta, zeta_omega];
1127
1128        let max_poly_size = 1 << 15;
1129        let powers_of_eval_points_for_chunks = [
1130            zeta.pow([max_poly_size as u64]),
1131            zeta_omega.pow([max_poly_size as u64]),
1132        ];
1133
1134        let evals = rec_chal.evals(
1135            max_poly_size,
1136            &evaluation_points,
1137            &powers_of_eval_points_for_chunks,
1138        );
1139
1140        // Expected values (computed and verified)
1141        let expected_eval_zeta = Fp::from_str(
1142            "21115683812642620361045381629886583866877919362491419134086003378733605776328",
1143        )
1144        .unwrap();
1145        let expected_eval_zeta_omega = Fp::from_str(
1146            "2298325069360593860729719174291433577456794311517767070156020442825391962511",
1147        )
1148        .unwrap();
1149
1150        assert_eq!(evals[0].len(), 1, "Should have single chunk");
1151        assert_eq!(evals[1].len(), 1, "Should have single chunk");
1152        assert_eq!(evals[0][0], expected_eval_zeta, "b(zeta) mismatch");
1153        assert_eq!(
1154            evals[1][0], expected_eval_zeta_omega,
1155            "b(zeta*omega) mismatch"
1156        );
1157
1158        // Verify consistency with b_poly
1159        assert_eq!(evals[0][0], b_poly(&chals, zeta));
1160        assert_eq!(evals[1][0], b_poly(&chals, zeta_omega));
1161    }
1162
1163    /// Regression test for accumulated commitment computation.
1164    ///
1165    /// The accumulated commitment U = <h, G> where h are the coefficients of
1166    /// b(X) and G is the SRS basis. This test uses a small example (4 challenges)
1167    /// to verify the MSM computation.
1168    #[test]
1169    fn test_recursion_challenge_commitment_regression() {
1170        // Use 4 challenges for a manageable test (2^4 = 16 coefficients)
1171        let chals: Vec<Fp> = vec![
1172            Fp::from(2u64),
1173            Fp::from(3u64),
1174            Fp::from(5u64),
1175            Fp::from(7u64),
1176        ];
1177
1178        // Compute b(X) coefficients
1179        let coeffs = b_poly_coefficients(&chals);
1180        assert_eq!(coeffs.len(), 16);
1181
1182        // Create a simple SRS-like basis (for testing, use multiples of generator)
1183        let g = Vesta::generator();
1184        let basis: Vec<Vesta> = (1..=16)
1185            .map(|i| (g * Fp::from(i as u64)).into_affine())
1186            .collect();
1187
1188        // Compute accumulated commitment U = <coeffs, basis>
1189        let accumulated_comm = VestaGroup::msm(&basis, &coeffs).unwrap().into_affine();
1190
1191        // Expected commitment coordinates (computed and verified)
1192        let expected_x = Fq::from_str(
1193            "3756288960823668761746459900985719106126835112055076922409498125279524024429",
1194        )
1195        .unwrap();
1196        let expected_y = Fq::from_str(
1197            "7540929664328976141648477194277016811781677917189411360504995258251130097840",
1198        )
1199        .unwrap();
1200
1201        assert_eq!(accumulated_comm.x, expected_x, "commitment x mismatch");
1202        assert_eq!(accumulated_comm.y, expected_y, "commitment y mismatch");
1203    }
1204}