mina_tree/proofs/
step.rs

1use std::rc::Rc;
2
3use crate::{
4    proofs::{
5        prover::make_padded_proof_from_p2p,
6        public_input::{
7            plonk_checks::ShiftingValue,
8            prepared_statement::{DeferredValues, PreparedStatement, ProofState},
9        },
10        unfinalized::dummy_ipa_step_challenges_computed,
11        util::proof_evaluation_to_absorption_sequence,
12        verifiers::wrap_domains,
13        wrap::{
14            create_oracle_with_public_input, dummy_ipa_wrap_sg, wrap_verifier, Domain,
15            COMMON_MAX_DEGREE_WRAP_LOG2,
16        },
17    },
18    verifier::{get_srs, get_srs_mut},
19};
20use anyhow::Context;
21use ark_ff::{BigInteger256, One, Zero};
22use ark_poly::{
23    univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Radix2EvaluationDomain,
24};
25use kimchi::proof::{PointEvaluations, ProverCommitments, RecursionChallenge};
26use mina_curves::pasta::{Fp, Fq, Pallas};
27use mina_p2p_messages::{bigint::InvalidBigInt, v2};
28use poly_commitment::{commitment::b_poly_coefficients, ipa::OpeningProof};
29
30use crate::proofs::{
31    public_input::{
32        plonk_checks::{derive_plonk, InCircuit},
33        prepared_statement::Plonk,
34    },
35    transaction::endos,
36    util::{challenge_polynomial, four_u64_to_field},
37    verification::{make_scalars_env, prev_evals_from_p2p},
38    wrap::{
39        combined_inner_product, evals_of_split_evals, CombinedInnerProductParams,
40        COMMON_MAX_DEGREE_STEP_LOG2,
41    },
42    BACKEND_TICK_ROUNDS_N, BACKEND_TOCK_ROUNDS_N,
43};
44
45use super::{
46    constants::ProofConstants,
47    field::{Boolean, CircuitVar, FieldWitness, GroupAffine},
48    public_input::{messages::MessagesForNextWrapProof, plonk_checks::PlonkMinimal},
49    to_field_elements::{ToFieldElements, ToFieldElementsDebug},
50    transaction::{
51        create_proof, make_group, messages_for_next_wrap_proof_padding,
52        scalar_challenge::to_field_checked, Check, CircuitPlonkVerificationKeyEvals,
53        CreateProofParams, InnerCurve, MessagesForNextStepProof, PlonkVerificationKeyEvals,
54        ProofWithPublic, Prover, ReducedMessagesForNextStepProof, StepStatement,
55    },
56    unfinalized::{evals_from_p2p, AllEvals, EvalsWithPublicInput, Unfinalized},
57    util::{extract_bulletproof, two_u64_to_field},
58    witness::Witness,
59    wrap::Domains,
60    ProverProof, VerifierIndex,
61};
62
63#[derive(Clone)]
64pub struct PreviousProofStatement<'a> {
65    pub public_input: Rc<dyn ToFieldElementsDebug>,
66    pub proof: &'a v2::PicklesProofProofsVerified2ReprStableV2,
67    pub proof_must_verify: CircuitVar<Boolean>,
68}
69
70pub struct InductiveRule<'a, const N_PREVIOUS: usize> {
71    pub previous_proof_statements: [PreviousProofStatement<'a>; N_PREVIOUS],
72    pub public_output: (),
73    pub auxiliary_output: (),
74}
75
76impl InductiveRule<'_, 0> {
77    pub fn empty() -> Self {
78        Self {
79            previous_proof_statements: [],
80            public_output: (),
81            auxiliary_output: (),
82        }
83    }
84}
85
86#[derive(Clone, Copy, Debug)]
87pub enum OptFlag {
88    Yes,
89    No,
90    Maybe,
91}
92
93#[derive(Clone, Debug)]
94pub enum Opt<T> {
95    Some(T),
96    No,
97    Maybe(Boolean, T),
98}
99
100impl<T> Opt<T> {
101    fn map<V>(&self, fun: impl Fn(&T) -> V) -> Opt<V> {
102        match self {
103            Opt::Some(v) => Opt::Some(fun(v)),
104            Opt::No => Opt::No,
105            Opt::Maybe(b, v) => Opt::Maybe(*b, fun(v)),
106        }
107    }
108}
109
110#[derive(Clone, Debug)]
111pub struct FeatureFlags<Bool> {
112    pub range_check0: Bool,
113    pub range_check1: Bool,
114    pub foreign_field_add: Bool,
115    pub foreign_field_mul: Bool,
116    pub xor: Bool,
117    pub rot: Bool,
118    pub lookup: Bool,
119    pub runtime_tables: Bool,
120}
121
122impl FeatureFlags<Boolean> {
123    pub fn empty() -> Self {
124        Self {
125            range_check0: Boolean::False,
126            range_check1: Boolean::False,
127            foreign_field_add: Boolean::False,
128            foreign_field_mul: Boolean::False,
129            xor: Boolean::False,
130            rot: Boolean::False,
131            lookup: Boolean::False,
132            runtime_tables: Boolean::False,
133        }
134    }
135}
136
137impl<F: FieldWitness> ToFieldElements<F> for FeatureFlags<bool> {
138    fn to_field_elements(&self, fields: &mut Vec<F>) {
139        let Self {
140            range_check0,
141            range_check1,
142            foreign_field_add,
143            foreign_field_mul,
144            xor,
145            rot,
146            lookup,
147            runtime_tables,
148        } = self;
149
150        [
151            range_check0,
152            range_check1,
153            foreign_field_add,
154            foreign_field_mul,
155            xor,
156            rot,
157            lookup,
158            runtime_tables,
159        ]
160        .to_field_elements(fields);
161    }
162}
163
164impl FeatureFlags<bool> {
165    pub fn empty_bool() -> Self {
166        Self {
167            range_check0: false,
168            range_check1: false,
169            foreign_field_add: false,
170            foreign_field_mul: false,
171            xor: false,
172            rot: false,
173            lookup: false,
174            runtime_tables: false,
175        }
176    }
177
178    pub fn to_boolean(&self) -> FeatureFlags<Boolean> {
179        use super::field::ToBoolean;
180
181        let Self {
182            range_check0,
183            range_check1,
184            foreign_field_add,
185            foreign_field_mul,
186            xor,
187            rot,
188            lookup,
189            runtime_tables,
190        } = self;
191
192        FeatureFlags {
193            range_check0: range_check0.to_boolean(),
194            range_check1: range_check1.to_boolean(),
195            foreign_field_add: foreign_field_add.to_boolean(),
196            foreign_field_mul: foreign_field_mul.to_boolean(),
197            xor: xor.to_boolean(),
198            rot: rot.to_boolean(),
199            lookup: lookup.to_boolean(),
200            runtime_tables: runtime_tables.to_boolean(),
201        }
202    }
203}
204
205#[derive(Debug)]
206pub struct Basic {
207    pub proof_verifieds: Vec<u64>,
208    // branches: u64,
209    pub wrap_domain: Domains,
210    pub step_domains: Box<[Domains]>,
211    pub feature_flags: FeatureFlags<OptFlag>,
212}
213
214#[derive(Debug)]
215pub enum ForStepKind<T, T2 = ()> {
216    Known(T),
217    SideLoaded(T2),
218}
219
220#[derive(Debug)]
221pub struct ForStep {
222    pub branches: usize,
223    pub max_proofs_verified: usize,
224    pub proof_verifieds: ForStepKind<Vec<Fp>>,
225    pub public_input: (), // Typ
226    pub wrap_key: CircuitPlonkVerificationKeyEvals<Fp>,
227    pub wrap_domain: ForStepKind<Domain, Box<[Boolean]>>,
228    pub step_domains: ForStepKind<Box<[Domains]>>,
229    pub feature_flags: FeatureFlags<OptFlag>,
230    pub num_chunks: u64,
231    pub zk_rows: u64,
232}
233
234pub enum Packed {
235    Field(CircuitVar<Fq>),
236    PackedBits(CircuitVar<Fq>, usize),
237}
238
239impl std::fmt::Debug for Packed {
240    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241        match self {
242            Self::Field(x) => f.write_fmt(format_args!("Field({:?})", x)),
243            Self::PackedBits(a, b) => f.write_fmt(format_args!("PackedBits({:?}, {:?})", a, b)),
244        }
245    }
246}
247
248pub struct StatementDeferredValues {
249    pub plonk: PlonkMinimal<Fp, 2>,
250    pub bulletproof_challenges: [[u64; 2]; 16],
251    pub branch_data: v2::CompositionTypesBranchDataStableV1,
252}
253
254pub struct StatementProofState {
255    pub deferred_values: StatementDeferredValues,
256    pub sponge_digest_before_evaluations: [u64; 4],
257    pub messages_for_next_wrap_proof: MessagesForNextWrapProof,
258}
259
260impl TryFrom<&v2::PicklesProofProofsVerified2ReprStableV2StatementProofState>
261    for StatementProofState
262{
263    type Error = InvalidBigInt;
264
265    fn try_from(
266        value: &v2::PicklesProofProofsVerified2ReprStableV2StatementProofState,
267    ) -> Result<Self, Self::Error> {
268        let v2::PicklesProofProofsVerified2ReprStableV2StatementProofState {
269            deferred_values:
270                v2::PicklesProofProofsVerified2ReprStableV2StatementProofStateDeferredValues {
271                    plonk,
272                    bulletproof_challenges,
273                    branch_data,
274                },
275            sponge_digest_before_evaluations,
276            messages_for_next_wrap_proof:
277                v2::PicklesProofProofsVerified2ReprStableV2MessagesForNextWrapProof {
278                    challenge_polynomial_commitment: (c0, c1),
279                    old_bulletproof_challenges,
280                },
281        } = value;
282
283        Ok(Self {
284            deferred_values: StatementDeferredValues {
285                plonk: plonk.try_into()?,
286                bulletproof_challenges: bulletproof_challenges
287                    .each_ref()
288                    .map(|challenge| challenge.prechallenge.inner.each_ref().map(|v| v.as_u64())),
289                branch_data: branch_data.clone(),
290            },
291            sponge_digest_before_evaluations: sponge_digest_before_evaluations
292                .each_ref()
293                .map(|v| v.as_u64()),
294            messages_for_next_wrap_proof: MessagesForNextWrapProof {
295                challenge_polynomial_commitment: InnerCurve::from((
296                    c0.to_field::<Fq>()?,
297                    c1.to_field()?,
298                )),
299                old_bulletproof_challenges: old_bulletproof_challenges
300                    .iter()
301                    .map(|old_bulletproof_challenge| {
302                        old_bulletproof_challenge.each_ref().map(|chal| {
303                            two_u64_to_field(
304                                &chal.prechallenge.inner.each_ref().map(|v| v.as_u64()),
305                            )
306                        })
307                    })
308                    .collect(),
309            },
310        })
311    }
312}
313
314pub mod step_verifier {
315    use std::ops::Neg;
316
317    use super::*;
318    use crate::proofs::{
319        self,
320        field::{field, ToBoolean},
321        opt_sponge::OptSponge,
322        public_input::plonk_checks::{self, ft_eval0_checked},
323        transaction::{poseidon::Sponge, scalar_challenge},
324        unfinalized,
325        util::{
326            challenge_polynomial_checked, proof_evaluation_to_list_opt, to_absorption_sequence_opt,
327        },
328        wrap::{
329            make_scalars_env_checked, one_hot_vector, ones_vector,
330            pcs_batch::PcsBatch,
331            pseudo::{self, PseudoDomain},
332            wrap_verifier::{
333                actual_evaluation, chunks_needed, lowest_128_bits, split_commitments::Point,
334                Advice, OPS_BITS_PER_CHUNK,
335            },
336            MakeScalarsEnvParams, PERMUTS_MINUS_1_ADD_N1,
337        },
338    };
339    use itertools::Itertools;
340    use kimchi::circuits::wires::PERMUTS;
341    use poly_commitment::{ipa::SRS, PolyComm};
342
343    fn squeeze_challenge(s: &mut Sponge<Fp>, w: &mut Witness<Fp>) -> Fp {
344        lowest_128_bits(s.squeeze(w), true, w)
345    }
346
347    fn squeeze_scalar(s: &mut Sponge<Fp>, w: &mut Witness<Fp>) -> Fp {
348        lowest_128_bits(s.squeeze(w), false, w)
349    }
350
351    fn absorb_curve(c: &Pallas, sponge: &mut Sponge<Fp>, w: &mut Witness<Fp>) {
352        let GroupAffine::<Fp> { x, y, .. } = c;
353        sponge.absorb2(&[*x, *y], w);
354    }
355
356    pub trait PlonkDomain<F: FieldWitness> {
357        fn vanishing_polynomial(&self, x: F, w: &mut Witness<F>) -> F;
358        fn generator(&self) -> F;
359        fn shifts(&self) -> &[F; PERMUTS];
360        fn log2_size(&self) -> u64;
361    }
362
363    impl<F: FieldWitness> PlonkDomain<F> for PseudoDomain<F> {
364        fn vanishing_polynomial(&self, x: F, w: &mut Witness<F>) -> F {
365            self.vanishing_polynomial(x, w)
366        }
367        fn generator(&self) -> F {
368            self.domain.group_gen
369        }
370        fn shifts(&self) -> &[F; PERMUTS] {
371            &self.shifts
372        }
373        fn log2_size(&self) -> u64 {
374            todo!()
375        }
376    }
377
378    impl<F: FieldWitness> PlonkDomain<F> for SideloadedDomain<F> {
379        fn vanishing_polynomial(&self, x: F, w: &mut Witness<F>) -> F {
380            (self.vanishing_polynomial)(x, w)
381        }
382        fn generator(&self) -> F {
383            self.generator
384        }
385        fn shifts(&self) -> &[F; PERMUTS] {
386            &self.shifts
387        }
388        fn log2_size(&self) -> u64 {
389            self.log2_size
390        }
391    }
392
393    struct SideloadedDomain<F: FieldWitness> {
394        generator: F,
395        shifts: Box<[F; PERMUTS]>,
396        vanishing_polynomial: Box<dyn Fn(F, &mut Witness<F>) -> F>,
397        log2_size: u64,
398    }
399
400    fn domain_for_compiled(
401        domains: &[Domains],
402        branch_data: &v2::CompositionTypesBranchDataStableV1,
403        w: &mut Witness<Fp>,
404    ) -> PseudoDomain<Fp> {
405        let unique_domains = domains
406            .iter()
407            .map(|d| d.h)
408            .sorted()
409            .dedup()
410            .collect::<Vec<_>>();
411        let mut which_log2 = unique_domains
412            .iter()
413            .rev()
414            .map(|Domain::Pow2RootsOfUnity(d)| {
415                let d = Fp::from(*d);
416                let domain_log2 = Fp::from(branch_data.domain_log2.as_u8() as u64);
417                field::equal(d, domain_log2, w)
418            })
419            .collect::<Vec<_>>();
420
421        which_log2.reverse();
422
423        pseudo::to_domain::<Fp>(&which_log2, &unique_domains)
424    }
425
426    fn tick_shifts(log2_size: u64) -> Box<[Fp; PERMUTS]> {
427        // caml_pasta_fp_plonk_verifier_index_shifts
428        use kimchi::circuits::polynomials::permutation::Shifts;
429
430        let num_coeff = 1 << log2_size;
431        let domain = Radix2EvaluationDomain::<Fp>::new(num_coeff).unwrap();
432        let shifts = Shifts::new(&domain);
433        Box::from(*shifts.shifts())
434    }
435
436    fn domain_generator(log2_size: u64) -> Fp {
437        // caml_pasta_fp_domain_generator
438        let num_coeff = 1 << log2_size;
439        let domain = Radix2EvaluationDomain::<Fp>::new(num_coeff).unwrap();
440        domain.group_gen
441    }
442
443    fn side_loaded_domain(log2_size: u64, w: &mut Witness<Fp>) -> SideloadedDomain<Fp> {
444        let log2_size_field = Fp::from(log2_size);
445
446        let vanishing_polynomial = |mask: Vec<Boolean>| {
447            Box::new(move |x: Fp, w: &mut Witness<Fp>| {
448                let result = mask.iter().rev().fold(x, |acc, should_square| {
449                    let squared = field::square(acc, w);
450                    w.exists_no_check(match should_square {
451                        Boolean::True => squared,
452                        Boolean::False => acc,
453                    })
454                });
455                result - Fp::one()
456            })
457        };
458
459        let mut domain = |max: u64| {
460            let max_n = max;
461            let mask = ones_vector(log2_size_field, max_n, w);
462
463            let s_max_n = max_n + 1;
464            let log2_sizes = (
465                one_hot_vector::of_index(log2_size_field, s_max_n, w),
466                (0..max_n).collect::<Vec<_>>(),
467            );
468            let shifts = pseudo::shifts(&log2_sizes, tick_shifts);
469            let generator = pseudo::generator(&log2_sizes, domain_generator);
470            let vanishing_polynomial = vanishing_polynomial(mask);
471
472            SideloadedDomain {
473                generator,
474                shifts,
475                vanishing_polynomial,
476                log2_size,
477            }
478        };
479
480        domain(Domains::max().h.log2_size())
481    }
482
483    pub(super) fn proof_verified_to_prefix(
484        p: &v2::PicklesBaseProofsVerifiedStableV1,
485    ) -> [Boolean; 2] {
486        use v2::PicklesBaseProofsVerifiedStableV1::*;
487
488        match p {
489            N0 => [Boolean::False, Boolean::False],
490            N1 => [Boolean::False, Boolean::True],
491            N2 => [Boolean::True, Boolean::True],
492        }
493    }
494
495    // TODO: This assumes that feature flags are never enabled (we make this assumption everywhere else too)
496    fn validate_feature_flags(hack_feature_flags: OptFlag, w: &mut Witness<Fp>) {
497        let no = CircuitVar::Var(Boolean::False);
498
499        let range_check0 = no;
500        let range_check1 = no;
501        let rot = no;
502        let xor = no;
503        let foreign_field_mul = no;
504        let lookup = no;
505
506        if let OptFlag::Maybe = hack_feature_flags {
507            let range_check_lookup = {
508                let first = range_check0.or(&range_check1, w);
509                first.or(&rot, w)
510            };
511            let lookups_per_row_4 = {
512                let first = xor.or(&range_check_lookup, w);
513                first.or(&foreign_field_mul, w)
514            };
515            let _lookups_per_row_3 = lookups_per_row_4.or(&lookup, w);
516        };
517    }
518
519    pub(super) struct FinalizeOtherProofParams<'a> {
520        pub(super) max_proof_verified: usize,
521        pub(super) feature_flags: &'a FeatureFlags<OptFlag>,
522        pub(super) step_domains: &'a ForStepKind<Box<[Domains]>>,
523        pub(super) sponge: Sponge<Fp>,
524        pub(super) prev_challenges: &'a [[Fp; Fp::NROUNDS]],
525        pub(super) deferred_values: &'a DeferredValues<Fp>,
526        pub(super) evals: &'a AllEvals<Fp>,
527        pub(super) hack_feature_flags: OptFlag,
528        pub(super) zk_rows: u64,
529    }
530
531    pub(super) fn finalize_other_proof(
532        params: FinalizeOtherProofParams,
533        w: &mut Witness<Fp>,
534    ) -> anyhow::Result<(Boolean, Vec<Fp>)> {
535        let FinalizeOtherProofParams {
536            max_proof_verified,
537            feature_flags: _,
538            step_domains,
539            mut sponge,
540            prev_challenges,
541            deferred_values,
542            evals,
543            hack_feature_flags,
544            zk_rows,
545        } = params;
546
547        let DeferredValues {
548            plonk,
549            combined_inner_product,
550            b,
551            xi,
552            bulletproof_challenges,
553            branch_data,
554        } = deferred_values;
555
556        validate_feature_flags(hack_feature_flags, w);
557
558        let AllEvals { ft_eval1, evals } = evals;
559
560        let actual_width_mask = &branch_data.proofs_verified;
561        let actual_width_mask = proof_verified_to_prefix(actual_width_mask);
562
563        let (_, endo) = endos::<Fq>();
564        let scalar = |b: &[u64; 2], w: &mut Witness<Fp>| -> Fp {
565            let scalar = two_u64_to_field(b);
566            to_field_checked::<Fp, 128>(scalar, endo, w)
567        };
568
569        let plonk = {
570            let Plonk {
571                alpha,
572                beta,
573                gamma,
574                zeta,
575                zeta_to_srs_length,
576                zeta_to_domain_size,
577                perm,
578                feature_flags,
579                lookup: _,
580            } = plonk;
581
582            // We decompose this way because of OCaml evaluation order
583            // let lookup = lookup.as_ref().map(|lookup| scalar(lookup, w));
584            let lookup = match hack_feature_flags {
585                OptFlag::No => None,
586                OptFlag::Maybe => {
587                    // TODO: Hack
588                    // This assumes that `plonk.lookup` (just above) is [0, 0]
589                    // <https://github.com/MinaProtocol/mina/blob/a51f09d09e6ae83362ea74eaca072c8e40d08b52/src/lib/pickles/composition_types/composition_types.ml#L131>
590                    Some(scalar(&[0, 0], w))
591                }
592                OptFlag::Yes => todo!(),
593            };
594            let zeta = scalar(zeta, w);
595            let alpha = scalar(alpha, w);
596
597            InCircuit {
598                alpha,
599                beta: two_u64_to_field(beta),
600                gamma: two_u64_to_field(gamma),
601                zeta,
602                zeta_to_domain_size: zeta_to_domain_size.clone(),
603                zeta_to_srs_length: zeta_to_srs_length.clone(),
604                perm: perm.clone(),
605                lookup,
606                feature_flags: feature_flags.clone(),
607            }
608        };
609
610        let domain: Rc<dyn PlonkDomain<Fp>> = match step_domains {
611            ForStepKind::Known(ds) => Rc::new(domain_for_compiled(ds, branch_data, w)),
612            ForStepKind::SideLoaded(()) => Rc::new(side_loaded_domain(
613                branch_data.domain_log2.as_u8() as u64,
614                w,
615            )),
616        };
617
618        let zetaw = field::mul(domain.generator(), plonk.zeta, w);
619
620        let sg_olds = prev_challenges
621            .iter()
622            .map(|chals| challenge_polynomial_checked(chals))
623            .collect::<Vec<_>>();
624
625        let (sg_evals1, sg_evals2) = {
626            let sg_evals = |pt: Fp, w: &mut Witness<Fp>| {
627                let ntrim_front = 2 - max_proof_verified;
628
629                let mut sg = sg_olds
630                    .iter()
631                    .zip(&actual_width_mask[ntrim_front..])
632                    .rev()
633                    .map(|(f, keep)| (*keep, f(pt, w)))
634                    .collect::<Vec<_>>();
635                sg.reverse();
636                sg
637            };
638
639            // We decompose this way because of OCaml evaluation order
640            let sg_evals2 = sg_evals(zetaw, w);
641            let sg_evals1 = sg_evals(plonk.zeta, w);
642            (sg_evals1, sg_evals2)
643        };
644
645        // sponge state
646        {
647            let challenge_digest = {
648                let ntrim_front = 2 - max_proof_verified;
649
650                let mut sponge = OptSponge::create();
651                prev_challenges
652                    .iter()
653                    .zip(&actual_width_mask[ntrim_front..])
654                    .for_each(|(chals, keep)| {
655                        let keep = CircuitVar::Var(*keep);
656                        for chal in chals {
657                            sponge.absorb((keep, *chal));
658                        }
659                    });
660                sponge.squeeze(w)
661            };
662
663            sponge.absorb2(&[challenge_digest], w);
664            sponge.absorb(&[*ft_eval1], w);
665            sponge.absorb(&evals.public_input.0, w);
666            sponge.absorb(&evals.public_input.1, w);
667
668            for eval in &to_absorption_sequence_opt(&evals.evals, hack_feature_flags) {
669                match eval {
670                    Opt::No => {}
671                    Opt::Some(PointEvaluations { zeta, zeta_omega }) => {
672                        sponge.absorb(zeta, w);
673                        sponge.absorb(zeta_omega, w);
674                    }
675                    Opt::Maybe(b, PointEvaluations { zeta, zeta_omega }) => {
676                        let sponge_state_before = sponge.sponge_state.clone();
677                        let state_before = sponge.state;
678
679                        sponge.absorb(zeta, w);
680                        sponge.absorb(zeta_omega, w);
681
682                        // TODO: Does it panic in OCaml ?
683                        use ::poseidon::SpongeState::{Absorbed, Squeezed};
684                        match (sponge_state_before, &sponge.sponge_state) {
685                            (Absorbed(x), Absorbed(y)) => assert_eq!(x, *y),
686                            (Squeezed(x), Squeezed(y)) => assert_eq!(x, *y),
687                            (Absorbed(_), Squeezed(_)) => panic!(),
688                            (Squeezed(_), Absorbed(_)) => panic!(),
689                        }
690
691                        sponge
692                            .state
693                            .iter_mut()
694                            .zip(state_before)
695                            .for_each(|(then, else_)| {
696                                *then = w.exists_no_check(match b {
697                                    Boolean::True => *then,
698                                    Boolean::False => else_,
699                                })
700                            });
701                    }
702                }
703            }
704        };
705
706        let xi_actual = lowest_128_bits(sponge.squeeze(w), true, w);
707        let r_actual = lowest_128_bits(sponge.squeeze(w), true, w);
708
709        let xi_correct = field::equal(xi_actual, two_u64_to_field(xi), w);
710
711        let xi = scalar(xi, w);
712        let r = to_field_checked::<Fp, 128>(r_actual, endo, w);
713
714        let to_bytes = |f: Fp| {
715            let bigint: BigInteger256 = f.into();
716            bigint.0
717        };
718
719        let plonk_mininal = PlonkMinimal::<Fp, 4> {
720            alpha: plonk.alpha,
721            beta: plonk.beta,
722            gamma: plonk.gamma,
723            zeta: plonk.zeta,
724            joint_combiner: None,
725            alpha_bytes: to_bytes(plonk.alpha),
726            beta_bytes: to_bytes(plonk.beta),
727            gamma_bytes: to_bytes(plonk.gamma),
728            zeta_bytes: to_bytes(plonk.zeta),
729            joint_combiner_bytes: None,
730            feature_flags: FeatureFlags::empty_bool(), // TODO
731        };
732
733        let combined_evals = {
734            let mut pow2pow =
735                |f: Fp| (0..COMMON_MAX_DEGREE_STEP_LOG2).fold(f, |acc, _| field::square(acc, w));
736
737            let zeta_n = pow2pow(plonk.zeta);
738            let zetaw_n = pow2pow(zetaw);
739
740            evals
741                .evals
742                .map_ref(&|PointEvaluations { zeta, zeta_omega }| {
743                    let zeta = actual_evaluation(zeta, zeta_n);
744                    let zeta_omega = actual_evaluation(zeta_omega, zetaw_n);
745                    PointEvaluations { zeta, zeta_omega }
746                })
747        };
748
749        let srs_length_log2 = COMMON_MAX_DEGREE_STEP_LOG2;
750        let env = make_scalars_env_checked(
751            MakeScalarsEnvParams {
752                minimal: &plonk_mininal,
753                domain,
754                srs_length_log2,
755                hack_feature_flags,
756                zk_rows,
757            },
758            w,
759        );
760        let combined_inner_product_correct = {
761            let p_eval0 = &evals.public_input.0;
762
763            let ft_eval0 = ft_eval0_checked(
764                &env,
765                &combined_evals,
766                &plonk_mininal,
767                plonk.lookup,
768                p_eval0,
769                w,
770            );
771            let a = proof_evaluation_to_list_opt(&evals.evals, hack_feature_flags)
772                .into_iter()
773                .filter_map(|v| match v {
774                    Opt::No => None,
775                    Opt::Some(PointEvaluations { zeta, zeta_omega }) => Some([
776                        zeta.into_iter().map(Opt::Some).collect::<Vec<_>>(),
777                        zeta_omega.into_iter().map(Opt::Some).collect::<Vec<_>>(),
778                    ]),
779                    Opt::Maybe(boolean, PointEvaluations { zeta, zeta_omega }) => Some([
780                        zeta.into_iter()
781                            .map(|a| Opt::Maybe(boolean, a))
782                            .collect::<Vec<_>>(),
783                        zeta_omega
784                            .into_iter()
785                            .map(|a| Opt::Maybe(boolean, a))
786                            .collect::<Vec<_>>(),
787                    ]),
788                })
789                .collect::<Vec<_>>();
790
791            let actual_combined_inner_product = {
792                enum WhichEval {
793                    First,
794                    Second,
795                }
796
797                let combine = |which_eval: WhichEval,
798                               sg_evals: &[(Boolean, Fp)],
799                               ft_eval: Fp,
800                               x_hat: &[Fp],
801                               w: &mut Witness<Fp>| {
802                    let f = |[a, b]: &[Vec<Opt<Fp>>; 2]| match which_eval {
803                        WhichEval::First => a.clone(),
804                        WhichEval::Second => b.clone(),
805                    };
806                    let v = sg_evals
807                        .iter()
808                        .copied()
809                        .map(|(b, v)| Opt::Maybe(b, v))
810                        .chain(x_hat.iter().copied().map(Opt::Some).collect::<Vec<_>>())
811                        .chain([Opt::Some(ft_eval)])
812                        .chain(a.iter().flat_map(f))
813                        .rev()
814                        .collect::<Vec<_>>();
815                    let (init, rest) = v.split_at(1);
816
817                    let init = match init[0] {
818                        Opt::Some(x) => x,
819                        Opt::No => Fp::zero(),
820                        Opt::Maybe(b, x) => field::mul(b.to_field(), x, w),
821                    };
822                    rest.iter().fold(init, |acc: Fp, fx: &Opt<Fp>| match fx {
823                        Opt::No => acc,
824                        Opt::Some(fx) => *fx + field::mul(xi, acc, w),
825                        Opt::Maybe(b, fx) => {
826                            let on_true = *fx + field::mul(xi, acc, w);
827                            w.exists_no_check(match b {
828                                Boolean::True => on_true,
829                                Boolean::False => acc,
830                            })
831                        }
832                    })
833                };
834
835                // We decompose this way because of OCaml evaluation order
836                let b = combine(
837                    WhichEval::Second,
838                    &sg_evals2,
839                    *ft_eval1,
840                    &evals.public_input.1,
841                    w,
842                );
843                let b = field::mul(b, r, w);
844                let a = combine(
845                    WhichEval::First,
846                    &sg_evals1,
847                    ft_eval0,
848                    &evals.public_input.0,
849                    w,
850                );
851                a + b
852            };
853
854            let combined_inner_product =
855                ShiftingValue::<Fp>::shifted_to_field(combined_inner_product);
856            field::equal(combined_inner_product, actual_combined_inner_product, w)
857        };
858
859        let mut bulletproof_challenges = bulletproof_challenges
860            .iter()
861            .rev()
862            .map(|f| to_field_checked::<Fp, 128>(*f, endo, w))
863            .collect::<Vec<_>>();
864        bulletproof_challenges.reverse();
865
866        let b_correct = {
867            let challenge_poly = challenge_polynomial_checked(&bulletproof_challenges);
868
869            // We decompose this way because of OCaml evaluation order
870            let r_zetaw = field::mul(r, challenge_poly(zetaw, w), w);
871            let b_actual = challenge_poly(plonk.zeta, w) + r_zetaw;
872
873            field::equal(b.shifted_to_field(), b_actual, w)
874        };
875
876        let plonk = wrap_verifier::PlonkWithField {
877            alpha: plonk.alpha,
878            beta: plonk.beta,
879            gamma: plonk.gamma,
880            zeta: plonk.zeta,
881            zeta_to_srs_length: plonk.zeta_to_srs_length,
882            zeta_to_domain_size: plonk.zeta_to_domain_size,
883            perm: plonk.perm,
884            lookup: (),
885        };
886        let plonk_checks_passed = plonk_checks::checked(&env, &combined_evals, &plonk, w);
887
888        let finalized = Boolean::all(
889            &[
890                xi_correct,
891                b_correct,
892                combined_inner_product_correct,
893                plonk_checks_passed,
894            ],
895            w,
896        );
897
898        Ok((finalized, bulletproof_challenges))
899    }
900
901    pub(super) fn sponge_after_index(
902        index: &PlonkVerificationKeyEvals<Fp>,
903        w: &mut Witness<Fp>,
904    ) -> Sponge<Fp> {
905        let mut sponge = Sponge::<Fp>::new();
906        let fields = index.to_field_elements_owned();
907        sponge.absorb2(&fields, w);
908        sponge
909    }
910
911    pub(super) fn hash_messages_for_next_step_proof_opt(
912        msg: ReducedMessagesForNextStepProof,
913        sponge: Sponge<Fp>,
914        _widths: &ForStepKind<Vec<Fp>>,
915        _max_width: usize,
916        proofs_verified_mask: &[Boolean],
917        w: &mut Witness<Fp>,
918    ) -> Fp {
919        enum MaybeOpt<T, T2> {
920            Opt(Boolean, T),
921            NotOpt(T2),
922        }
923
924        let ReducedMessagesForNextStepProof {
925            app_state,
926            challenge_polynomial_commitments,
927            old_bulletproof_challenges,
928        } = msg;
929
930        let old_bulletproof_challenges = proofs_verified_mask
931            .iter()
932            .zip(old_bulletproof_challenges)
933            .map(|(b, v)| {
934                let b = *b;
935                v.map(|v| MaybeOpt::Opt(b, v))
936            });
937
938        let challenge_polynomial_commitments = proofs_verified_mask
939            .iter()
940            .zip(challenge_polynomial_commitments)
941            .map(|(b, v)| {
942                let b = *b;
943                let GroupAffine::<Fp> { x, y, .. } = v.to_affine();
944                [MaybeOpt::Opt(b, x), MaybeOpt::Opt(b, y)]
945            });
946
947        let app_state = app_state
948            .to_field_elements_owned()
949            .into_iter()
950            .map(MaybeOpt::NotOpt);
951
952        let both = challenge_polynomial_commitments
953            .zip(old_bulletproof_challenges)
954            .flat_map(|(c, o)| c.into_iter().chain(o));
955
956        let sponge = Box::new(sponge);
957        let res = app_state
958            .chain(both)
959            .fold(MaybeOpt::NotOpt(sponge), |acc, v| match (acc, v) {
960                (MaybeOpt::NotOpt(mut sponge), MaybeOpt::NotOpt(v)) => {
961                    sponge.absorb(&[v], w);
962                    MaybeOpt::NotOpt(sponge)
963                }
964                (MaybeOpt::NotOpt(sponge), MaybeOpt::Opt(b, v)) => {
965                    let mut sponge = Box::new(OptSponge::of_sponge(*sponge, w));
966                    sponge.absorb((CircuitVar::Var(b), v));
967                    MaybeOpt::Opt(Boolean::True, sponge)
968                }
969                (MaybeOpt::Opt(_, mut sponge), MaybeOpt::Opt(b, v)) => {
970                    sponge.absorb((CircuitVar::Var(b), v));
971                    MaybeOpt::Opt(Boolean::True, sponge)
972                }
973                (MaybeOpt::Opt(_, _), MaybeOpt::NotOpt(_)) => panic!(),
974            });
975
976        match res {
977            MaybeOpt::NotOpt(mut sponge) => sponge.squeeze(w),
978            MaybeOpt::Opt(_, mut sponge) => sponge.squeeze(w),
979        }
980    }
981
982    // TODO: Dedup with the one in `wrap_verifier`
983    fn scale_fast2<F: FieldWitness>(
984        g: CircuitVar<GroupAffine<F>>,
985        (s_div_2, s_odd): (F, Boolean),
986        num_bits: usize,
987        w: &mut Witness<F>,
988    ) -> GroupAffine<F> {
989        use crate::proofs::transaction::plonk_curve_ops::scale_fast_unpack;
990        use wrap_verifier::*;
991
992        let s_div_2_bits = num_bits - 1;
993        let chunks_needed = chunks_needed(s_div_2_bits);
994        let actual_bits_used = chunks_needed * OPS_BITS_PER_CHUNK;
995
996        let g_value = *g.value();
997        let shifted = F::Shifting::of_raw(s_div_2);
998        let h = match actual_bits_used {
999            255 => scale_fast_unpack::<F, F, 255>(g_value, shifted, w).0,
1000            130 => scale_fast_unpack::<F, F, 130>(g_value, shifted, w).0,
1001            10 => scale_fast_unpack::<F, F, 10>(g_value, shifted, w).0,
1002            n => todo!("{:?} param_num_bits={:?}", n, num_bits),
1003        };
1004
1005        let on_false = {
1006            let g_neg = g.value().neg();
1007            if let CircuitVar::Var(_) = g {
1008                w.exists(g_neg.y);
1009            } else {
1010                // eprintln!("ignoring {:?}", g_neg.y);
1011            }
1012            w.add_fast(h, g_neg)
1013        };
1014
1015        w.exists_no_check(match s_odd {
1016            Boolean::True => h,
1017            Boolean::False => on_false,
1018        })
1019    }
1020
1021    // TODO: Dedup with the one above and in `wrap_verifier`
1022    fn scale_fast22<F: FieldWitness>(
1023        g: CircuitVar<GroupAffine<F>>,
1024        (s_div_2, s_odd): (F, Boolean),
1025        num_bits: usize,
1026        w: &mut Witness<F>,
1027    ) -> GroupAffine<F> {
1028        use crate::proofs::transaction::plonk_curve_ops::scale_fast_unpack;
1029        use wrap_verifier::*;
1030
1031        let s_div_2_bits = num_bits - 1;
1032        let chunks_needed = chunks_needed(s_div_2_bits);
1033        let actual_bits_used = chunks_needed * OPS_BITS_PER_CHUNK;
1034
1035        let g_value = *g.value();
1036        let shifted = F::Shifting::of_raw(s_div_2);
1037        let h = match actual_bits_used {
1038            255 => scale_fast_unpack::<F, F, 255>(g_value, shifted, w).0,
1039            130 => scale_fast_unpack::<F, F, 130>(g_value, shifted, w).0,
1040            10 => scale_fast_unpack::<F, F, 10>(g_value, shifted, w).0,
1041            n => todo!("{:?} param_num_bits={:?}", n, num_bits),
1042        };
1043
1044        let on_false = {
1045            let g_neg = w.exists_no_check(g.value().neg());
1046            w.add_fast(h, g_neg)
1047        };
1048
1049        w.exists_no_check(match s_odd {
1050            Boolean::True => h,
1051            Boolean::False => on_false,
1052        })
1053    }
1054
1055    // TODO: Dedup with the one in `wrap_verifier`
1056    pub(super) fn scale_fast2_prime<F: FieldWitness, F2: FieldWitness>(
1057        g: CircuitVar<GroupAffine<F>>,
1058        s: F2,
1059        num_bits: usize,
1060        w: &mut Witness<F>,
1061    ) -> GroupAffine<F> {
1062        let s_parts = w.exists({
1063            // TODO: Here `s` is a `F` but needs to be read as a `F::Scalar`
1064            let bigint: BigInteger256 = s.into();
1065            let bigint = bigint.0;
1066            let s_odd = bigint[0] & 1 != 0;
1067            let v = if s_odd { s - F2::one() } else { s };
1068            // TODO: Remove this ugly hack
1069            let v: BigInteger256 = (v / F2::from(2u64)).into();
1070            (F::from(v), s_odd.to_boolean()) // `unwrap` never fail
1071        });
1072
1073        scale_fast2(g, s_parts, num_bits, w)
1074    }
1075
1076    // TODO: Dedup with the one in `wrap_verifier`
1077    pub(super) fn ft_comm<F: FieldWitness, Scale>(
1078        plonk: &Plonk<<F as proofs::field::FieldWitness>::Scalar>,
1079        t_comm: &PolyComm<GroupAffine<F>>,
1080        verification_key: &CircuitPlonkVerificationKeyEvals<F>,
1081        scale: Scale,
1082        w: &mut Witness<F>,
1083    ) -> GroupAffine<F>
1084    where
1085        Scale: Fn(
1086            CircuitVar<GroupAffine<F>>,
1087            <<F as proofs::field::FieldWitness>::Scalar as FieldWitness>::Shifting,
1088            &mut Witness<F>,
1089        ) -> GroupAffine<F>,
1090    {
1091        let m = verification_key;
1092        let [sigma_comm_last] = &m.sigma[PERMUTS_MINUS_1_ADD_N1..] else {
1093            panic!()
1094        };
1095
1096        // We decompose this way because of OCaml evaluation order (reversed)
1097        let f_comm = [scale(*sigma_comm_last, plonk.perm.clone(), w)]
1098            .into_iter()
1099            .rev()
1100            .reduce(|acc, v| w.add_fast(acc, v))
1101            .unwrap();
1102
1103        let chunked_t_comm = t_comm
1104            .chunks
1105            .iter()
1106            .rev()
1107            .copied()
1108            .reduce(|acc, v| {
1109                let scaled = scale(CircuitVar::Var(acc), plonk.zeta_to_srs_length.clone(), w);
1110                w.add_fast(v, scaled)
1111            })
1112            .unwrap();
1113
1114        // We decompose this way because of OCaml evaluation order
1115        let scaled = scale(
1116            CircuitVar::Var(chunked_t_comm),
1117            plonk.zeta_to_domain_size.clone(),
1118            w,
1119        )
1120        .neg();
1121        let v = w.add_fast(f_comm, chunked_t_comm);
1122
1123        // Because of `neg()` above
1124        w.exists_no_check(scaled.y);
1125
1126        w.add_fast(v, scaled)
1127    }
1128
1129    fn public_input_commitment_dynamic(
1130        which: &[Boolean],
1131        srs: &mut poly_commitment::ipa::SRS<Pallas>,
1132        domains: Vec<Domains>,
1133        public_input: Vec<Packed>,
1134        w: &mut Witness<Fp>,
1135    ) -> GroupAffine<Fp> {
1136        let lagrange_commitment =
1137            |d: &Domains, i: usize, srs: &mut poly_commitment::ipa::SRS<Pallas>| {
1138                let d = 2u64.pow(d.h.log2_size() as u32);
1139                let elems = wrap_verifier::lagrange_commitment::<Fp>(srs, d, i).chunks;
1140                assert_eq!(elems.len(), 1);
1141                InnerCurve::<Fp>::of_affine(elems[0])
1142            };
1143
1144        fn select_curve_points(
1145            domains: &[Domains],
1146            which: &[Boolean],
1147            srs: &mut poly_commitment::ipa::SRS<Pallas>,
1148            w: &mut Witness<Fp>,
1149            points_for_domain: impl Fn(
1150                &Domains,
1151                &mut poly_commitment::ipa::SRS<Pallas>,
1152            ) -> Vec<InnerCurve<Fp>>,
1153        ) -> Vec<InnerCurve<Fp>> {
1154            let (d, ds) = domains.split_first().unwrap();
1155            if ds.iter().all(|d2| d2.h == d.h) {
1156                points_for_domain(d, srs)
1157            } else {
1158                let index = which.iter().position(|b| b.as_bool()).unwrap();
1159                let domain = &domains[index];
1160                let points = points_for_domain(domain, srs);
1161                for p in points.iter().rev() {
1162                    let GroupAffine::<Fp> { x, y, .. } = p.to_affine();
1163                    w.exists_no_check([y, x]);
1164                }
1165                points
1166            }
1167        }
1168
1169        let lagrange =
1170            |i: usize, srs: &mut poly_commitment::ipa::SRS<Pallas>, w: &mut Witness<Fp>| {
1171                let vec = select_curve_points(&domains, which, srs, w, |d, srs| {
1172                    vec![lagrange_commitment(d, i, srs)]
1173                });
1174                vec[0].clone()
1175            };
1176
1177        let pow2pow = |x: InnerCurve<Fp>, n: usize| (0..n).fold(x, |acc, _| acc.clone() + acc);
1178
1179        let lagrange_with_correction = |input_length: usize,
1180                                        i: usize,
1181                                        srs: &mut poly_commitment::ipa::SRS<Pallas>,
1182                                        w: &mut Witness<Fp>|
1183         -> Vec<InnerCurve<Fp>> {
1184            let actual_shift = OPS_BITS_PER_CHUNK * chunks_needed(input_length);
1185
1186            select_curve_points(&domains, which, srs, w, |d, srs| {
1187                let g = lagrange_commitment(d, i, srs);
1188                let powed = pow2pow(g.clone(), actual_shift);
1189                let powed = powed.to_affine().neg();
1190                vec![g, InnerCurve::of_affine(powed)]
1191            })
1192        };
1193
1194        let x_hat = {
1195            let (constant_part, non_constant_part): (Vec<_>, Vec<_>) = public_input
1196                .into_iter()
1197                .enumerate()
1198                .partition_map(|(i, t)| {
1199                    use itertools::Either::{Left, Right};
1200                    use CircuitVar::Constant;
1201                    use Packed::{Field, PackedBits};
1202
1203                    match t {
1204                        Field(Constant(c)) | PackedBits(Constant(c), _) => Left(if c.is_zero() {
1205                            None
1206                        } else if c.is_one() {
1207                            Some(lagrange(i, srs, w))
1208                        } else {
1209                            todo!()
1210                        }),
1211                        Field(x) => Right((i, (x, 255))),
1212                        PackedBits(x, n) => Right((i, (x, n))),
1213                    }
1214                });
1215
1216            #[derive(Debug)]
1217            enum CondOrAdd {
1218                CondAdd(CircuitVar<Boolean>, InnerCurve<Fp>),
1219                AddWithCorrection((CircuitVar<Fq>, usize), Vec<InnerCurve<Fp>>),
1220            }
1221
1222            let terms = non_constant_part
1223                .into_iter()
1224                .map(|(i, x)| match x {
1225                    (b, 1) => CondOrAdd::CondAdd(CircuitVar::of_cvar(b), lagrange(i, srs, w)),
1226                    (x, n) => {
1227                        CondOrAdd::AddWithCorrection((x, n), lagrange_with_correction(n, i, srs, w))
1228                    }
1229                })
1230                .collect::<Vec<_>>();
1231
1232            let correction = terms
1233                .iter()
1234                .filter_map(|term| match term {
1235                    CondOrAdd::CondAdd(_, _) => None,
1236                    CondOrAdd::AddWithCorrection(_, v) => {
1237                        let [_, corr] = v.as_slice() else { panic!() };
1238                        Some(corr.to_affine())
1239                    }
1240                })
1241                .reduce(|a, b| w.add_fast(a, b))
1242                .unwrap();
1243
1244            let init =
1245                constant_part
1246                    .iter()
1247                    .filter_map(|term| term.as_ref())
1248                    .fold(correction, |acc, v| {
1249                        let v = v.to_affine();
1250                        w.add_fast(acc, v)
1251                    });
1252
1253            let result = terms.iter().fold(init, |acc, term| match term {
1254                CondOrAdd::CondAdd(b, g) => {
1255                    let on_true = w.add_fast(g.to_affine(), acc);
1256                    w.exists_no_check(match b.value() {
1257                        Boolean::True => on_true,
1258                        Boolean::False => acc,
1259                    })
1260                }
1261                CondOrAdd::AddWithCorrection((x, num_bits), slice) => {
1262                    let g = &slice[0];
1263                    let g = CircuitVar::Var(g.to_affine());
1264                    let acc2 = scale_fast2_prime(g, *x.value(), *num_bits, w);
1265                    w.add_fast(acc, acc2)
1266                }
1267            });
1268            result.neg()
1269        };
1270
1271        x_hat
1272    }
1273
1274    fn multiscale_known(ts: &[(&Packed, InnerCurve<Fp>)], w: &mut Witness<Fp>) -> GroupAffine<Fp> {
1275        let pow2pow = |x: InnerCurve<Fp>, n: usize| (0..n).fold(x, |acc, _| acc.clone() + acc);
1276
1277        let (constant_part, non_constant_part): (Vec<_>, Vec<_>) =
1278            ts.iter().partition_map(|(t, g)| {
1279                use itertools::Either::{Left, Right};
1280                use CircuitVar::Constant;
1281                use Packed::{Field, PackedBits};
1282
1283                match t {
1284                    Field(Constant(c)) | PackedBits(Constant(c), _) => Left(if c.is_zero() {
1285                        None
1286                    } else if c.is_one() {
1287                        Some(g)
1288                    } else {
1289                        todo!()
1290                    }),
1291                    Field(x) => Right((Field(*x), g)),
1292                    PackedBits(x, n) => Right((PackedBits(*x, *n), g)),
1293                }
1294            });
1295
1296        let add_opt = |xo: Option<InnerCurve<Fp>>, y: &InnerCurve<Fp>| match xo {
1297            Some(x) => x + y.clone(),
1298            None => y.clone(),
1299        };
1300
1301        let constant_part = constant_part
1302            .iter()
1303            .filter_map(|c| c.as_ref())
1304            .fold(None, |acc, x| Some(add_opt(acc, x)));
1305
1306        let (correction, acc) = {
1307            non_constant_part
1308                .iter()
1309                .map(|(s, x)| {
1310                    let (rr, n) = match s {
1311                        Packed::PackedBits(s, n) => {
1312                            let x = CircuitVar::Constant(x.to_affine());
1313                            let c = scale_fast2_prime(x, s.as_field(), *n, w);
1314                            (c, *n)
1315                        }
1316                        Packed::Field(s) => {
1317                            let x = CircuitVar::Constant(x.to_affine());
1318                            let c = scale_fast2_prime(x, s.as_field(), 255, w);
1319                            (c, 255)
1320                        }
1321                    };
1322
1323                    let n = wrap_verifier::OPS_BITS_PER_CHUNK * wrap_verifier::chunks_needed(n - 1);
1324                    let cc = pow2pow((*x).clone(), n);
1325                    (cc, rr)
1326                })
1327                .collect::<Vec<_>>() // We need to collect because `w` is borrowed :(
1328                .into_iter()
1329                .reduce(|(a1, b1), (a2, b2)| ((a1 + a2), w.add_fast(b1, b2)))
1330                .unwrap()
1331        };
1332
1333        let correction = InnerCurve::of_affine(correction.to_affine().neg());
1334        w.add_fast(acc, add_opt(constant_part, &correction).to_affine())
1335    }
1336
1337    fn lagrange_commitment<F: FieldWitness>(
1338        srs: &mut SRS<GroupAffine<F>>,
1339        domain: &Domain,
1340        i: usize,
1341    ) -> InnerCurve<F> {
1342        let d = domain.size();
1343        let elems = wrap_verifier::lagrange_commitment::<F>(srs, d, i).chunks;
1344
1345        assert_eq!(elems.len(), 1);
1346        InnerCurve::of_affine(elems[0])
1347    }
1348
1349    fn to_high_low(f: Fq) -> (Fp, Boolean) {
1350        fn of_bits<F: FieldWitness>(bs: &[bool; 254]) -> F {
1351            bs.iter().rev().fold(F::zero(), |acc, b| {
1352                let acc = acc + acc;
1353                if *b {
1354                    acc + F::one()
1355                } else {
1356                    acc
1357                }
1358            })
1359        }
1360        let to_high_low = |fq: Fq| {
1361            let [low, high @ ..] = crate::proofs::transaction::field_to_bits::<Fq, 255>(fq);
1362            (of_bits::<Fp>(&high), low.to_boolean())
1363        };
1364        to_high_low(f)
1365    }
1366
1367    fn scale_for_ft_comm(
1368        g: CircuitVar<GroupAffine<Fp>>,
1369        f: <Fq as FieldWitness>::Shifting,
1370        w: &mut Witness<Fp>,
1371    ) -> GroupAffine<Fp> {
1372        let scalar = to_high_low(f.shifted_raw());
1373        scale_fast2(g, scalar, 255, w)
1374    }
1375
1376    struct CheckBulletProofParams<'a> {
1377        pcs_batch: PcsBatch,
1378        sponge: Sponge<Fp>,
1379        xi: [u64; 2],
1380        advice: &'a Advice<Fp>,
1381        openings_proof: &'a OpeningProof<GroupAffine<Fp>>,
1382        srs: &'a SRS<GroupAffine<Fp>>,
1383        polynomials: (Vec<CircuitVar<GroupAffine<Fp>>>, Vec<()>),
1384    }
1385
1386    fn check_bulletproof(
1387        params: CheckBulletProofParams,
1388        w: &mut Witness<Fp>,
1389    ) -> anyhow::Result<(Boolean, Vec<Fp>)> {
1390        let CheckBulletProofParams {
1391            pcs_batch: _,
1392            mut sponge,
1393            xi,
1394            advice,
1395            openings_proof:
1396                OpeningProof {
1397                    lr,
1398                    delta,
1399                    z1,
1400                    z2,
1401                    sg,
1402                },
1403            srs,
1404            polynomials,
1405        } = params;
1406
1407        let (without_degree_bound, _with_degree_bound) = polynomials;
1408
1409        let (high, low) = to_high_low(advice.combined_inner_product.shifted_raw());
1410        sponge.absorb2(&[high, low.to_field()], w);
1411
1412        let u = {
1413            let t = sponge.squeeze(w);
1414            wrap_verifier::group_map(t, w)
1415        };
1416
1417        let xi_field: Fq = two_u64_to_field(&xi);
1418
1419        let point: Point<Fp> = PcsBatch::combine_split_commitments::<Fp, _, _, _, _>(
1420            |p, _w| p.clone(),
1421            |acc, _xi, p, w| match acc {
1422                Point::MaybeFinite(acc_is_finite, acc) => match p {
1423                    Point::MaybeFinite(p_is_finite, p) => {
1424                        let is_finite = p_is_finite.or(&acc_is_finite, w);
1425                        let xi_acc = scalar_challenge::endo_cvar::<Fp, _, 128>(acc, xi_field, w);
1426
1427                        let on_true = CircuitVar::Var(w.add_fast(*p.value(), xi_acc));
1428
1429                        let v = match acc_is_finite.as_boolean() {
1430                            Boolean::True => on_true,
1431                            Boolean::False => *p,
1432                        };
1433                        if let CircuitVar::Var(_) = acc_is_finite {
1434                            w.exists_no_check(v.value());
1435                        };
1436                        Point::<Fp>::MaybeFinite(is_finite, v)
1437                    }
1438                    Point::Finite(p) => {
1439                        let xi_acc = scalar_challenge::endo_cvar::<Fp, _, 128>(acc, xi_field, w);
1440                        let on_true = CircuitVar::Var(w.add_fast(*p.value(), xi_acc));
1441
1442                        let v = match acc_is_finite.as_boolean() {
1443                            Boolean::True => on_true,
1444                            Boolean::False => *p,
1445                        };
1446                        if let CircuitVar::Var(_) = acc_is_finite {
1447                            w.exists_no_check(v.value());
1448                        };
1449                        Point::<Fp>::Finite(v)
1450                    }
1451                },
1452                Point::Finite(acc) => {
1453                    let xi_acc = scalar_challenge::endo_cvar::<Fp, _, 128>(acc, xi_field, w);
1454
1455                    let v = match p {
1456                        Point::Finite(p) => CircuitVar::Var(w.add_fast(*p.value(), xi_acc)),
1457                        Point::MaybeFinite(p_is_finite, p) => {
1458                            let p = *p;
1459                            let on_true = CircuitVar::Var(w.add_fast(*p.value(), xi_acc));
1460
1461                            let v = match p_is_finite.as_boolean() {
1462                                Boolean::True => on_true,
1463                                Boolean::False => CircuitVar::Var(xi_acc),
1464                            };
1465                            if let CircuitVar::Var(_) = p_is_finite {
1466                                w.exists_no_check(v.value());
1467                            };
1468                            v
1469                        }
1470                    };
1471
1472                    Point::Finite(v)
1473                }
1474            },
1475            xi,
1476            &without_degree_bound
1477                .into_iter()
1478                .map(Point::Finite)
1479                .collect::<Vec<_>>(),
1480            &[],
1481            w,
1482        );
1483
1484        let Point::Finite(combined_polynomial) = point else {
1485            panic!("invalid state");
1486        };
1487        let combined_polynomial = *combined_polynomial.value();
1488
1489        let (lr_prod, challenges) = wrap_verifier::bullet_reduce(&mut sponge, lr, w);
1490
1491        let p_prime = {
1492            w.exists_no_check(&u); // Made by `plonk_curve_ops.seal` in `scale_fast`
1493            let combined_inner_product = to_high_low(advice.combined_inner_product.shifted_raw());
1494            let uc = scale_fast22(CircuitVar::Var(u), combined_inner_product, 255, w);
1495            w.add_fast(combined_polynomial, uc)
1496        };
1497
1498        let q = w.add_fast(p_prime, lr_prod);
1499        absorb_curve(delta, &mut sponge, w);
1500        let c = squeeze_scalar(&mut sponge, w);
1501
1502        let lhs = {
1503            let cq = scalar_challenge::endo::<Fp, Fp, 128>(q, c, w);
1504            w.add_fast(cq, *delta)
1505        };
1506
1507        let rhs = {
1508            let b_u = {
1509                w.exists_no_check(&u); // Made by `plonk_curve_ops.seal` in `scale_fast`
1510                let b = to_high_low(advice.b.shifted_raw());
1511                scale_fast22(CircuitVar::Var(u), b, 255, w)
1512            };
1513
1514            let z_1_g_plus_b_u = {
1515                use plonk_checks::ShiftedValue;
1516
1517                let z1 = to_high_low(ShiftedValue::<Fq>::of_field(*z1).shifted);
1518                scale_fast2(CircuitVar::Var(w.add_fast(*sg, b_u)), z1, 255, w)
1519            };
1520
1521            let z2_h = {
1522                use plonk_checks::ShiftedValue;
1523
1524                let z2 = to_high_low(ShiftedValue::<Fq>::of_field(*z2).shifted);
1525                scale_fast2(CircuitVar::Constant(srs.h), z2, 255, w)
1526            };
1527
1528            w.add_fast(z_1_g_plus_b_u, z2_h)
1529        };
1530
1531        Ok((wrap_verifier::equal_g(lhs, rhs, w), challenges))
1532    }
1533
1534    struct IncrementallyVerifyProofParams<'a> {
1535        pub proofs_verified: usize,
1536        pub srs: &'a mut poly_commitment::ipa::SRS<Pallas>,
1537        pub wrap_domain: &'a ForStepKind<Domain, Box<[Boolean]>>,
1538        pub sponge: Sponge<Fp>,
1539        pub sponge_after_index: Sponge<Fp>,
1540        pub wrap_verification_key: &'a CircuitPlonkVerificationKeyEvals<Fp>,
1541        pub xi: [u64; 2],
1542        pub public_input: Vec<Packed>,
1543        pub sg_old: &'a Vec<GroupAffine<Fp>>,
1544        pub advice: &'a Advice<Fp>,
1545        pub proof: &'a ProverProof<Fq>,
1546        pub plonk: &'a Plonk<Fq>,
1547    }
1548
1549    fn incrementally_verify_proof(
1550        params: IncrementallyVerifyProofParams,
1551        w: &mut Witness<Fp>,
1552    ) -> anyhow::Result<(Fp, (Boolean, Vec<Fp>))> {
1553        let IncrementallyVerifyProofParams {
1554            proofs_verified: _,
1555            srs,
1556            wrap_domain,
1557            mut sponge,
1558            sponge_after_index,
1559            wrap_verification_key,
1560            xi,
1561            public_input,
1562            sg_old,
1563            advice,
1564            proof,
1565            plonk,
1566        } = params;
1567
1568        let ProverProof::<Fq> {
1569            commitments: messages,
1570            proof: openings_proof,
1571            ..
1572            // evals,
1573            // ft_eval1,
1574            // prev_challenges
1575        } = proof;
1576
1577        let sample = squeeze_challenge;
1578        let sample_scalar = squeeze_scalar;
1579
1580        let index_digest = {
1581            let mut index_sponge = sponge_after_index.clone();
1582            index_sponge.squeeze(w)
1583        };
1584
1585        sponge.absorb2(&[index_digest], w);
1586
1587        // Or padding
1588        assert_eq!(sg_old.len(), 2);
1589        for v in sg_old {
1590            absorb_curve(v, &mut sponge, w);
1591        }
1592
1593        let x_hat = match wrap_domain {
1594            ForStepKind::Known(domain) => {
1595                let ts = public_input
1596                    .iter()
1597                    .enumerate()
1598                    .map(|(i, x)| (x, lagrange_commitment::<Fp>(srs, domain, i)))
1599                    .collect::<Vec<_>>();
1600                multiscale_known(&ts, w).neg()
1601            }
1602            ForStepKind::SideLoaded(which) => {
1603                let domains = [0, 1, 2].into_iter().map(wrap_domains).collect();
1604                public_input_commitment_dynamic(which, srs, domains, public_input, w)
1605            }
1606        };
1607
1608        let x_hat = {
1609            w.exists(x_hat.y); // Because of `.neg()` above
1610            w.add_fast(x_hat, srs.h)
1611        };
1612
1613        absorb_curve(&x_hat, &mut sponge, w);
1614
1615        let w_comm = &messages.w_comm;
1616        for g in w_comm.iter().flat_map(|w| &w.chunks) {
1617            absorb_curve(g, &mut sponge, w);
1618        }
1619
1620        let _beta = sample(&mut sponge, w);
1621        let _gamma = sample(&mut sponge, w);
1622
1623        let z_comm = &messages.z_comm;
1624        for z in z_comm.chunks.iter() {
1625            absorb_curve(z, &mut sponge, w);
1626        }
1627
1628        let _alpha = sample_scalar(&mut sponge, w);
1629
1630        let t_comm = &messages.t_comm;
1631        for t in t_comm.chunks.iter() {
1632            absorb_curve(t, &mut sponge, w);
1633        }
1634
1635        let _zeta = sample_scalar(&mut sponge, w);
1636
1637        let sponge_before_evaluations = sponge.clone();
1638        let sponge_digest_before_evaluations = sponge.squeeze(w);
1639
1640        let sigma_comm_init = &wrap_verification_key.sigma[..PERMUTS_MINUS_1_ADD_N1];
1641
1642        let ft_comm = ft_comm(plonk, t_comm, wrap_verification_key, scale_for_ft_comm, w);
1643
1644        let bulletproof_challenges = {
1645            /// Wrap_hack.Padded_length
1646            const WRAP_HACK_PADDED_LENGTH: usize = 2;
1647            const NUM_COMMITMENTS_WITHOUT_DEGREE_BOUND: usize = 45;
1648
1649            let cvar = CircuitVar::Var;
1650
1651            let without_degree_bound = {
1652                let sg_old = sg_old.iter().copied().map(cvar);
1653                let rest = [cvar(x_hat), cvar(ft_comm)]
1654                    .into_iter()
1655                    .chain(z_comm.chunks.iter().cloned().map(cvar))
1656                    .chain([
1657                        wrap_verification_key.generic,
1658                        wrap_verification_key.psm,
1659                        wrap_verification_key.complete_add,
1660                        wrap_verification_key.mul,
1661                        wrap_verification_key.emul,
1662                        wrap_verification_key.endomul_scalar,
1663                    ])
1664                    .chain(
1665                        w_comm
1666                            .iter()
1667                            .flat_map(|w| w.chunks.iter().cloned().map(cvar)),
1668                    )
1669                    .chain(wrap_verification_key.coefficients)
1670                    .chain(sigma_comm_init.iter().cloned());
1671                sg_old.chain(rest).collect::<Vec<_>>()
1672            };
1673
1674            check_bulletproof(
1675                CheckBulletProofParams {
1676                    pcs_batch: PcsBatch::create(
1677                        WRAP_HACK_PADDED_LENGTH + NUM_COMMITMENTS_WITHOUT_DEGREE_BOUND,
1678                    ),
1679                    sponge: sponge_before_evaluations,
1680                    xi,
1681                    advice,
1682                    openings_proof,
1683                    srs,
1684                    polynomials: (without_degree_bound, vec![]),
1685                },
1686                w,
1687            )?
1688        };
1689
1690        Ok((sponge_digest_before_evaluations, bulletproof_challenges))
1691    }
1692
1693    pub(super) struct VerifyParams<'a> {
1694        pub(super) srs: &'a mut poly_commitment::ipa::SRS<Pallas>,
1695        pub(super) feature_flags: &'a FeatureFlags<OptFlag>,
1696        pub(super) lookup_parameters: (),
1697        pub(super) proofs_verified: usize,
1698        pub(super) wrap_domain: &'a ForStepKind<Domain, Box<[Boolean]>>,
1699        pub(super) is_base_case: CircuitVar<Boolean>,
1700        pub(super) sponge_after_index: Sponge<Fp>,
1701        pub(super) sg_old: &'a Vec<GroupAffine<Fp>>,
1702        pub(super) proof: &'a ProverProof<Fq>,
1703        pub(super) wrap_verification_key: &'a CircuitPlonkVerificationKeyEvals<Fp>,
1704        pub(super) statement: &'a PreparedStatement,
1705        pub(super) hack_feature_flags: OptFlag,
1706        pub(super) unfinalized: &'a Unfinalized,
1707    }
1708
1709    pub(super) fn verify(params: VerifyParams, w: &mut Witness<Fp>) -> anyhow::Result<Boolean> {
1710        let VerifyParams {
1711            srs,
1712            feature_flags: _,
1713            lookup_parameters: _,
1714            proofs_verified,
1715            wrap_domain,
1716            is_base_case,
1717            sponge_after_index,
1718            sg_old,
1719            proof,
1720            wrap_verification_key,
1721            statement,
1722            hack_feature_flags,
1723            unfinalized,
1724        } = params;
1725
1726        let public_input = {
1727            let npublic_input = match hack_feature_flags {
1728                OptFlag::No => 39,
1729                OptFlag::Maybe => 40,
1730                OptFlag::Yes => todo!(),
1731            };
1732            statement.to_public_input_cvar(hack_feature_flags, npublic_input)?
1733        };
1734
1735        let unfinalized::DeferredValues {
1736            plonk,
1737            combined_inner_product,
1738            b,
1739            xi,
1740            bulletproof_challenges: _,
1741        } = &unfinalized.deferred_values;
1742
1743        let b = b.clone();
1744        let combined_inner_product = combined_inner_product.clone();
1745        let sponge = Sponge::new();
1746
1747        let (
1748            _sponge_digest_before_evaluations_actual,
1749            (bulletproof_success, bulletproof_challenges_actual),
1750        ) = incrementally_verify_proof(
1751            IncrementallyVerifyProofParams {
1752                proofs_verified,
1753                srs,
1754                wrap_domain,
1755                sponge,
1756                sponge_after_index,
1757                wrap_verification_key,
1758                xi: *xi,
1759                public_input,
1760                sg_old,
1761                advice: &Advice {
1762                    b,
1763                    combined_inner_product,
1764                },
1765                proof,
1766                plonk,
1767            },
1768            w,
1769        )?;
1770
1771        for (c1, c2) in unfinalized
1772            .deferred_values
1773            .bulletproof_challenges
1774            .iter()
1775            .zip(&bulletproof_challenges_actual)
1776        {
1777            let v = match is_base_case.value() {
1778                Boolean::True => two_u64_to_field(c1),
1779                Boolean::False => *c2,
1780            };
1781            if let CircuitVar::Var(_) = is_base_case {
1782                w.exists_no_check(v);
1783            };
1784        }
1785
1786        Ok(bulletproof_success)
1787    }
1788}
1789
1790struct VerifyOneParams<'a> {
1791    srs: &'a mut poly_commitment::ipa::SRS<Pallas>,
1792    proof: &'a PerProofWitness,
1793    data: &'a ForStep,
1794    messages_for_next_wrap_proof: Fp,
1795    unfinalized: &'a Unfinalized,
1796    should_verify: CircuitVar<Boolean>,
1797}
1798
1799fn verify_one(params: VerifyOneParams, w: &mut Witness<Fp>) -> anyhow::Result<(Vec<Fp>, Boolean)> {
1800    let VerifyOneParams {
1801        srs,
1802        proof,
1803        data,
1804        messages_for_next_wrap_proof,
1805        unfinalized,
1806        should_verify,
1807    } = params;
1808
1809    let PerProofWitness {
1810        app_state,
1811        wrap_proof,
1812        proof_state,
1813        prev_proof_evals,
1814        prev_challenges,
1815        prev_challenge_polynomial_commitments,
1816        hack_feature_flags,
1817    } = proof;
1818
1819    let deferred_values = &proof_state.deferred_values;
1820
1821    let (finalized, chals) = {
1822        let sponge_digest = proof_state.sponge_digest_before_evaluations;
1823
1824        let sponge = {
1825            let mut sponge = crate::proofs::transaction::poseidon::Sponge::<Fp>::new();
1826            sponge.absorb2(&[four_u64_to_field(&sponge_digest)?], w);
1827            sponge
1828        };
1829
1830        step_verifier::finalize_other_proof(
1831            step_verifier::FinalizeOtherProofParams {
1832                max_proof_verified: data.max_proofs_verified,
1833                feature_flags: &data.feature_flags,
1834                step_domains: &data.step_domains,
1835                sponge,
1836                prev_challenges,
1837                deferred_values,
1838                evals: prev_proof_evals,
1839                hack_feature_flags: *hack_feature_flags,
1840                zk_rows: data.zk_rows,
1841            },
1842            w,
1843        )?
1844    };
1845
1846    let branch_data = &deferred_values.branch_data;
1847
1848    let sponge_after_index = step_verifier::sponge_after_index(&data.wrap_key.to_non_cvar(), w);
1849
1850    let statement = {
1851        let msg = ReducedMessagesForNextStepProof {
1852            app_state: app_state.clone().unwrap(),
1853            challenge_polynomial_commitments: prev_challenge_polynomial_commitments
1854                .iter()
1855                .map(|g| InnerCurve::of_affine(*g))
1856                .collect(),
1857            old_bulletproof_challenges: prev_challenges.clone(),
1858        };
1859
1860        let ntrim_front = 2 - prev_challenge_polynomial_commitments.len();
1861        let proofs_verified_mask = {
1862            let proofs_verified_mask = &branch_data.proofs_verified;
1863            step_verifier::proof_verified_to_prefix(proofs_verified_mask)
1864        };
1865        let proofs_verified_mask = &proofs_verified_mask[ntrim_front..];
1866
1867        let prev_messages_for_next_step_proof =
1868            step_verifier::hash_messages_for_next_step_proof_opt(
1869                msg,
1870                sponge_after_index.clone(),
1871                &data.proof_verifieds,
1872                data.max_proofs_verified,
1873                proofs_verified_mask,
1874                w,
1875            );
1876
1877        PreparedStatement {
1878            proof_state: ProofState {
1879                messages_for_next_wrap_proof: { to_bytes(messages_for_next_wrap_proof) },
1880                ..proof_state.clone()
1881            },
1882            messages_for_next_step_proof: { to_bytes(prev_messages_for_next_step_proof) },
1883        }
1884    };
1885
1886    let verified = step_verifier::verify(
1887        step_verifier::VerifyParams {
1888            srs,
1889            feature_flags: &data.feature_flags,
1890            lookup_parameters: (),
1891            proofs_verified: data.max_proofs_verified,
1892            wrap_domain: &data.wrap_domain,
1893            is_base_case: should_verify.neg(),
1894            sponge_after_index,
1895            sg_old: prev_challenge_polynomial_commitments,
1896            proof: wrap_proof,
1897            wrap_verification_key: &data.wrap_key,
1898            statement: &statement,
1899            hack_feature_flags: *hack_feature_flags,
1900            unfinalized,
1901        },
1902        w,
1903    )?;
1904
1905    let a = verified.and(&finalized, w);
1906    let b = CircuitVar::Var(a).or(&should_verify.neg(), w);
1907
1908    Ok((chals, b.as_boolean()))
1909}
1910
1911fn to_bytes(f: Fp) -> [u64; 4] {
1912    let bigint: BigInteger256 = f.into();
1913    bigint.0
1914}
1915
1916fn to_4limbs(v: [u64; 2]) -> [u64; 4] {
1917    [v[0], v[1], 0, 0]
1918}
1919
1920pub struct ExpandDeferredParams<'a> {
1921    pub evals: &'a AllEvals<Fp>,
1922    pub old_bulletproof_challenges: &'a Vec<[Fp; 16]>,
1923    pub proof_state: &'a StatementProofState,
1924    pub zk_rows: u64,
1925}
1926
1927pub fn expand_deferred(params: ExpandDeferredParams) -> anyhow::Result<DeferredValues<Fp>> {
1928    let ExpandDeferredParams {
1929        evals,
1930        old_bulletproof_challenges,
1931        proof_state,
1932        zk_rows,
1933    } = params;
1934
1935    use super::public_input::scalar_challenge::ScalarChallenge;
1936
1937    let (_, endo) = endos::<Fq>();
1938
1939    let plonk0 = &proof_state.deferred_values.plonk;
1940
1941    let zeta = ScalarChallenge::limbs_to_field(&plonk0.zeta_bytes);
1942    let alpha = ScalarChallenge::limbs_to_field(&plonk0.alpha_bytes);
1943    let step_domain: u8 = proof_state.deferred_values.branch_data.domain_log2.as_u8();
1944
1945    let Some(num_coeffs) = 1u64.checked_shl(step_domain as u32) else {
1946        return Err(InvalidBigInt)?;
1947    };
1948
1949    let Some(domain) = Radix2EvaluationDomain::<Fp>::new(num_coeffs as usize) else {
1950        return Err(InvalidBigInt)?;
1951    };
1952
1953    let zetaw = zeta * domain.group_gen;
1954
1955    let plonk_minimal = PlonkMinimal::<Fp, 4> {
1956        alpha,
1957        beta: plonk0.beta,
1958        gamma: plonk0.gamma,
1959        zeta,
1960        // joint_combiner: plonk0.joint_combiner,
1961        joint_combiner: plonk0
1962            .joint_combiner_bytes
1963            .as_ref()
1964            .map(ScalarChallenge::limbs_to_field),
1965        alpha_bytes: to_bytes(alpha),
1966        beta_bytes: to_4limbs(plonk0.beta_bytes),
1967        gamma_bytes: to_4limbs(plonk0.gamma_bytes),
1968        zeta_bytes: to_bytes(zeta),
1969        joint_combiner_bytes: plonk0.joint_combiner_bytes.map(to_4limbs),
1970        feature_flags: plonk0.feature_flags.clone(),
1971    };
1972
1973    let combined_evals =
1974        evals_of_split_evals(zeta, zetaw, &evals.evals.evals, BACKEND_TICK_ROUNDS_N);
1975
1976    let srs_length_log2 = COMMON_MAX_DEGREE_STEP_LOG2;
1977    let env = make_scalars_env(&plonk_minimal, step_domain, srs_length_log2, zk_rows);
1978
1979    let plonk = {
1980        let InCircuit {
1981            alpha: _,
1982            beta: _,
1983            gamma: _,
1984            zeta: _,
1985            zeta_to_domain_size,
1986            zeta_to_srs_length,
1987            perm,
1988            lookup,
1989            feature_flags,
1990        } = derive_plonk(&env, &combined_evals, &plonk_minimal);
1991
1992        Plonk {
1993            alpha: plonk0.alpha_bytes,
1994            beta: plonk0.beta_bytes,
1995            gamma: plonk0.gamma_bytes,
1996            zeta: plonk0.zeta_bytes,
1997            zeta_to_srs_length,
1998            zeta_to_domain_size,
1999            perm,
2000            feature_flags,
2001            lookup: lookup.map(|_| plonk0.joint_combiner_bytes.unwrap()),
2002        }
2003    };
2004
2005    let xs = proof_evaluation_to_absorption_sequence(&evals.evals.evals);
2006    let (x1, x2) = &evals.evals.public_input;
2007
2008    let old_bulletproof_challenges: Vec<_> = old_bulletproof_challenges
2009        .iter()
2010        .map(ScalarChallenge::array_to_fields)
2011        .collect();
2012
2013    let challenges_digest = {
2014        let mut sponge = poseidon::Sponge::<Fp>::default();
2015        for old_bulletproof_challenges in &old_bulletproof_challenges {
2016            sponge.absorb(old_bulletproof_challenges);
2017        }
2018        sponge.squeeze()
2019    };
2020
2021    let mut sponge = poseidon::FqSponge::default();
2022    sponge.absorb_fq(&[four_u64_to_field(
2023        &proof_state.sponge_digest_before_evaluations,
2024    )?]);
2025    sponge.absorb_fq(&[challenges_digest]);
2026    sponge.absorb_fq(&[evals.ft_eval1]);
2027    sponge.absorb_fq(x1);
2028    sponge.absorb_fq(x2);
2029    xs.iter().for_each(|PointEvaluations { zeta, zeta_omega }| {
2030        sponge.absorb_fq(zeta);
2031        sponge.absorb_fq(zeta_omega);
2032    });
2033    let xi_chal: [u64; 2] = sponge.squeeze_limbs();
2034    let r_chal: [u64; 2] = sponge.squeeze_limbs();
2035
2036    let xi = ScalarChallenge::from(xi_chal).to_field(&endo);
2037    let r = ScalarChallenge::from(r_chal).to_field(&endo);
2038
2039    let public_input = &evals.evals.public_input;
2040
2041    let es = &evals.evals.evals;
2042    let tick_combined_evals = evals_of_split_evals(zeta, zetaw, es, super::BACKEND_TICK_ROUNDS_N);
2043    let public = PointEvaluations {
2044        zeta: public_input.0.clone(),
2045        zeta_omega: public_input.1.clone(),
2046    };
2047
2048    let combined_inner_product_actual =
2049        combined_inner_product(CombinedInnerProductParams::<_, { Fp::NROUNDS }, 4> {
2050            env: &env,
2051            evals: es,
2052            combined_evals: &tick_combined_evals,
2053            public: &public,
2054            minimal: &plonk_minimal,
2055            ft_eval1: evals.ft_eval1,
2056            r,
2057            old_bulletproof_challenges: &old_bulletproof_challenges,
2058            xi,
2059            zetaw,
2060        });
2061
2062    let bulletproof_challenges: Vec<_> = proof_state
2063        .deferred_values
2064        .bulletproof_challenges
2065        .iter()
2066        .map(ScalarChallenge::limbs_to_field)
2067        .collect();
2068
2069    let b_actual = {
2070        let challenge_polys = challenge_polynomial(&bulletproof_challenges);
2071        challenge_polys(zeta) + (r * challenge_polys(zetaw))
2072    };
2073
2074    let to_shifted = |f: Fp| <Fp as FieldWitness>::Shifting::of_field(f);
2075
2076    Ok(DeferredValues {
2077        plonk,
2078        combined_inner_product: to_shifted(combined_inner_product_actual),
2079        b: to_shifted(b_actual),
2080        xi: xi_chal,
2081        bulletproof_challenges,
2082        branch_data: proof_state.deferred_values.branch_data.clone(),
2083    })
2084}
2085
2086/// Ipa.Wrap.compute_sg
2087fn wrap_compute_sg(challenges: &[[u64; 2]]) -> GroupAffine<Fp> {
2088    use super::public_input::scalar_challenge::ScalarChallenge;
2089
2090    let challenges = challenges
2091        .iter()
2092        .map(ScalarChallenge::limbs_to_field)
2093        .collect::<Vec<_>>();
2094
2095    let coeffs = b_poly_coefficients(&challenges);
2096    let p = DensePolynomial::from_coefficients_vec(coeffs);
2097
2098    let comm = {
2099        use poly_commitment::SRS;
2100
2101        let srs = get_srs::<Fq>();
2102        srs.commit_non_hiding(&p, 1)
2103    };
2104    comm.chunks[0]
2105}
2106
2107struct ExpandProofParams<'a> {
2108    dlog_vk: &'a VerifierIndex<Fq>,
2109    dlog_plonk_index: &'a CircuitPlonkVerificationKeyEvals<Fp>,
2110    app_state: &'a Rc<dyn ToFieldElementsDebug>,
2111    t: &'a v2::PicklesProofProofsVerified2ReprStableV2,
2112    public_input_length: usize,
2113    tag: (),
2114    must_verify: CircuitVar<Boolean>,
2115    hack_feature_flags: OptFlag,
2116    zk_rows: u64,
2117}
2118
2119fn expand_proof(params: ExpandProofParams) -> anyhow::Result<ExpandedProof> {
2120    let ExpandProofParams {
2121        dlog_vk,
2122        dlog_plonk_index,
2123        app_state,
2124        t,
2125        public_input_length,
2126        tag: _,
2127        must_verify,
2128        hack_feature_flags,
2129        zk_rows,
2130    } = params;
2131
2132    use super::public_input::scalar_challenge::ScalarChallenge;
2133
2134    let plonk0: PlonkMinimal<Fp> = (&t.statement.proof_state.deferred_values.plonk).try_into()?;
2135
2136    let plonk = {
2137        let domain: u8 = t
2138            .statement
2139            .proof_state
2140            .deferred_values
2141            .branch_data
2142            .domain_log2
2143            .as_u8();
2144
2145        let alpha = ScalarChallenge::limbs_to_field(&plonk0.alpha_bytes);
2146        let zeta = ScalarChallenge::limbs_to_field(&plonk0.zeta_bytes);
2147        let w: Fp = Radix2EvaluationDomain::new(1 << domain).unwrap().group_gen;
2148        let zetaw = zeta * w;
2149
2150        // dbg!(alpha, zeta, zetaw, dlog_vk.domain.log_size_of_group, domain);
2151
2152        let es = prev_evals_from_p2p(&t.prev_evals.evals.evals)?;
2153        let combined_evals = evals_of_split_evals(zeta, zetaw, &es, BACKEND_TICK_ROUNDS_N);
2154
2155        let plonk_minimal = PlonkMinimal::<Fp, 4> {
2156            alpha,
2157            beta: plonk0.beta,
2158            gamma: plonk0.gamma,
2159            zeta,
2160            joint_combiner: plonk0.joint_combiner,
2161            // joint_combiner: plonk0.joint_combiner_bytes.as_ref().map(ScalarChallenge::limbs_to_field),
2162            alpha_bytes: to_bytes(alpha),
2163            beta_bytes: to_4limbs(plonk0.beta_bytes),
2164            gamma_bytes: to_4limbs(plonk0.gamma_bytes),
2165            zeta_bytes: to_bytes(zeta),
2166            joint_combiner_bytes: plonk0.joint_combiner_bytes.map(to_4limbs),
2167            feature_flags: plonk0.feature_flags.clone(),
2168        };
2169
2170        let srs_length_log2 = COMMON_MAX_DEGREE_STEP_LOG2;
2171        let env = make_scalars_env(&plonk_minimal, domain, srs_length_log2, zk_rows);
2172
2173        derive_plonk(&env, &combined_evals, &plonk_minimal)
2174    };
2175
2176    let statement = &t.statement;
2177
2178    let prev_challenges: Vec<[Fq; BACKEND_TOCK_ROUNDS_N]> = {
2179        let old_bulletproof_challenges = &statement
2180            .proof_state
2181            .messages_for_next_wrap_proof
2182            .old_bulletproof_challenges;
2183        extract_bulletproof(&[
2184            old_bulletproof_challenges.0[0].0.clone(),
2185            old_bulletproof_challenges.0[1].0.clone(),
2186        ])
2187    };
2188
2189    let old_bulletproof_challenges: Vec<[Fp; 16]> = statement
2190        .messages_for_next_step_proof
2191        .old_bulletproof_challenges
2192        .iter()
2193        .map(|v| {
2194            v.0.clone()
2195                .map(|v| two_u64_to_field(&v.prechallenge.inner.0.map(|v| v.as_u64())))
2196        })
2197        .collect();
2198
2199    let deferred_values_computed = {
2200        let evals: AllEvals<Fp> = (&t.prev_evals).try_into()?;
2201        let proof_state: StatementProofState = (&statement.proof_state).try_into()?;
2202
2203        expand_deferred(ExpandDeferredParams {
2204            evals: &evals,
2205            old_bulletproof_challenges: &old_bulletproof_challenges,
2206            proof_state: &proof_state,
2207            zk_rows,
2208        })?
2209    };
2210
2211    let old_bulletproof_challenges: Vec<_> = old_bulletproof_challenges
2212        .iter()
2213        .map(ScalarChallenge::array_to_fields)
2214        .collect();
2215
2216    let messages_for_next_step_proof = MessagesForNextStepProof {
2217        app_state: Rc::clone(app_state),
2218        dlog_plonk_index: &dlog_plonk_index.to_non_cvar(),
2219        challenge_polynomial_commitments: statement
2220            .messages_for_next_step_proof
2221            .challenge_polynomial_commitments
2222            .iter()
2223            .map(|(x, y)| {
2224                Ok(InnerCurve::of_affine(make_group(
2225                    x.to_field::<Fp>()?,
2226                    y.to_field()?,
2227                )))
2228            })
2229            .collect::<Result<_, InvalidBigInt>>()?,
2230        old_bulletproof_challenges: old_bulletproof_challenges.clone(),
2231    }
2232    .hash();
2233
2234    let deferred_values = deferred_values_computed;
2235    let prev_statement_with_hashes = PreparedStatement {
2236        proof_state: ProofState {
2237            deferred_values: DeferredValues {
2238                plonk: Plonk {
2239                    alpha: plonk0.alpha_bytes,
2240                    beta: plonk0.beta_bytes,
2241                    gamma: plonk0.gamma_bytes,
2242                    zeta: plonk0.zeta_bytes,
2243                    zeta_to_srs_length: plonk.zeta_to_srs_length,
2244                    zeta_to_domain_size: plonk.zeta_to_domain_size,
2245                    perm: plonk.perm,
2246                    lookup: match plonk.lookup {
2247                        None => None,
2248                        Some(_) => Some(plonk0.joint_combiner_bytes.unwrap()),
2249                    },
2250                    feature_flags: plonk.feature_flags,
2251                },
2252                combined_inner_product: deferred_values.combined_inner_product,
2253                b: deferred_values.b,
2254                xi: deferred_values.xi,
2255                bulletproof_challenges: statement
2256                    .proof_state
2257                    .deferred_values
2258                    .bulletproof_challenges
2259                    .iter()
2260                    .map(|challenge| {
2261                        two_u64_to_field(
2262                            &challenge.prechallenge.inner.each_ref().map(|v| v.as_u64()),
2263                        )
2264                    })
2265                    .collect(),
2266                branch_data: deferred_values.branch_data,
2267            },
2268            sponge_digest_before_evaluations: statement
2269                .proof_state
2270                .sponge_digest_before_evaluations
2271                .each_ref()
2272                .map(|v| v.as_u64()),
2273            messages_for_next_wrap_proof: MessagesForNextWrapProof {
2274                old_bulletproof_challenges: prev_challenges.clone(),
2275                challenge_polynomial_commitment: {
2276                    let (x, y) = &statement
2277                        .proof_state
2278                        .messages_for_next_wrap_proof
2279                        .challenge_polynomial_commitment;
2280                    InnerCurve::from((x.to_field::<Fq>()?, y.to_field()?))
2281                },
2282            }
2283            .hash(),
2284        },
2285        messages_for_next_step_proof,
2286    };
2287
2288    let mut proof = make_padded_proof_from_p2p(t)?;
2289    let oracle = {
2290        let public_input = prev_statement_with_hashes.to_public_input(public_input_length)?;
2291        create_oracle_with_public_input(dlog_vk, &proof, &public_input)
2292    };
2293
2294    let x_hat = PointEvaluations {
2295        zeta: oracle.p_eval_1(),
2296        zeta_omega: oracle.p_eval_2(),
2297    };
2298
2299    let alpha = oracle.alpha();
2300    let beta = oracle.beta();
2301    let gamma = oracle.gamma();
2302    let zeta = oracle.zeta();
2303
2304    let to_bytes = |f: Fq| {
2305        let bigint: BigInteger256 = f.into();
2306        let [a, b, c, d] = bigint.0;
2307        assert_eq!([c, d], [0, 0]);
2308        [a, b]
2309    };
2310
2311    let plonk0 = PlonkMinimal {
2312        alpha,
2313        beta,
2314        gamma,
2315        zeta,
2316        joint_combiner: None,
2317        alpha_bytes: to_bytes(alpha),
2318        beta_bytes: to_bytes(beta),
2319        gamma_bytes: to_bytes(gamma),
2320        zeta_bytes: to_bytes(zeta),
2321        joint_combiner_bytes: None, // TODO
2322        feature_flags: FeatureFlags::empty_bool(),
2323    };
2324
2325    let xi = oracle.v();
2326    let r = oracle.u();
2327    let sponge_digest_before_evaluations = oracle.digest_before_evaluations;
2328
2329    let (_, endo) = endos::<Fp>();
2330    let to_field = |bytes: [u64; 2]| -> Fq { ScalarChallenge::from(bytes).to_field(&endo) };
2331
2332    let w = dlog_vk.domain.group_gen;
2333
2334    let zeta = to_field(plonk0.zeta_bytes);
2335    let zetaw = { zeta * w };
2336
2337    let (new_bulletproof_challenges, b) = {
2338        let chals = oracle
2339            .opening_prechallenges
2340            .iter()
2341            .map(|v| to_field(to_bytes(*v)))
2342            .collect::<Vec<_>>();
2343
2344        let r = to_field(to_bytes(r.0));
2345        // let zeta = to_field(plonk0.zeta_bytes);
2346        let challenge_poly = challenge_polynomial(&chals);
2347        let b = challenge_poly(zeta) + (r * challenge_poly(zetaw));
2348
2349        let prechals = oracle
2350            .opening_prechallenges
2351            .iter()
2352            .copied()
2353            .map(to_bytes)
2354            .collect::<Vec<_>>();
2355
2356        (prechals, b)
2357    };
2358
2359    let challenge_polynomial_commitment = match must_verify.value() {
2360        Boolean::False => wrap_compute_sg(&new_bulletproof_challenges),
2361        Boolean::True => proof.proof.sg,
2362    };
2363
2364    let witness = PerProofWitness {
2365        app_state: None,
2366        proof_state: prev_statement_with_hashes.proof_state.clone(),
2367        prev_proof_evals: (&t.prev_evals).try_into()?,
2368        prev_challenge_polynomial_commitments: {
2369            let mut challenge_polynomial_commitments = t
2370                .statement
2371                .messages_for_next_step_proof
2372                .challenge_polynomial_commitments
2373                .iter()
2374                .map(|(x, y)| Ok(make_group::<Fp>(x.to_field()?, y.to_field()?)))
2375                .collect::<Result<Vec<_>, InvalidBigInt>>()?;
2376
2377            while challenge_polynomial_commitments.len() < 2 {
2378                challenge_polynomial_commitments.insert(0, dummy_ipa_wrap_sg());
2379            }
2380
2381            challenge_polynomial_commitments
2382        },
2383        prev_challenges: {
2384            let mut prev_challenges = old_bulletproof_challenges.clone();
2385            while prev_challenges.len() < 2 {
2386                prev_challenges.insert(0, dummy_ipa_step_challenges_computed())
2387            }
2388
2389            prev_challenges
2390        },
2391        wrap_proof: {
2392            proof.proof.sg = challenge_polynomial_commitment;
2393            proof.clone()
2394        },
2395        hack_feature_flags,
2396    };
2397
2398    let tock_combined_evals = {
2399        let zeta = to_field(plonk0.zeta_bytes);
2400        evals_of_split_evals(zeta, zetaw, &proof.evals, BACKEND_TOCK_ROUNDS_N)
2401    };
2402
2403    let tock_plonk_minimal = {
2404        let alpha = to_field(plonk0.alpha_bytes);
2405        let zeta = to_field(plonk0.zeta_bytes);
2406
2407        let to_bytes = |f: Fq| {
2408            let bigint: BigInteger256 = f.into();
2409            bigint.0
2410        };
2411
2412        PlonkMinimal {
2413            alpha,
2414            beta,
2415            gamma,
2416            zeta,
2417            joint_combiner: None,
2418            alpha_bytes: to_bytes(alpha),
2419            beta_bytes: to_4limbs(plonk0.beta_bytes),
2420            gamma_bytes: to_4limbs(plonk0.gamma_bytes),
2421            zeta_bytes: to_bytes(alpha),
2422            joint_combiner_bytes: None, // TODO
2423            feature_flags: FeatureFlags::empty_bool(),
2424        }
2425    };
2426
2427    let domain_log2 = dlog_vk.domain.log_size_of_group;
2428    let srs_length_log2 = COMMON_MAX_DEGREE_WRAP_LOG2;
2429    let tock_env = make_scalars_env(
2430        &tock_plonk_minimal,
2431        domain_log2 as u8,
2432        srs_length_log2 as u64,
2433        zk_rows,
2434    );
2435
2436    let public = {
2437        let PointEvaluations { zeta, zeta_omega } = &x_hat;
2438        PointEvaluations {
2439            zeta: vec![*zeta],
2440            zeta_omega: vec![*zeta_omega],
2441        }
2442    };
2443
2444    let combined_inner_product = combined_inner_product(CombinedInnerProductParams {
2445        env: &tock_env,
2446        evals: &proof.evals,
2447        combined_evals: &tock_combined_evals,
2448        public: &public,
2449        minimal: &tock_plonk_minimal,
2450        ft_eval1: proof.ft_eval1,
2451        r: to_field(to_bytes(r.0)),
2452        old_bulletproof_challenges: &prev_challenges,
2453        xi: to_field(to_bytes(xi.0)),
2454        zetaw,
2455    });
2456
2457    let plonk = derive_plonk(&tock_env, &tock_combined_evals, &tock_plonk_minimal);
2458
2459    let shift = |f: Fq| <Fq as FieldWitness>::Shifting::of_field(f);
2460
2461    let unfinalized = Unfinalized {
2462        deferred_values: crate::proofs::unfinalized::DeferredValues {
2463            plonk: Plonk {
2464                alpha: to_bytes(plonk0.alpha),
2465                beta: to_bytes(plonk0.beta),
2466                gamma: to_bytes(plonk0.gamma),
2467                zeta: to_bytes(plonk0.zeta),
2468                zeta_to_srs_length: plonk.zeta_to_srs_length,
2469                zeta_to_domain_size: plonk.zeta_to_domain_size,
2470                perm: plonk.perm,
2471                lookup: None,
2472                feature_flags: plonk.feature_flags,
2473            },
2474            combined_inner_product: shift(combined_inner_product),
2475            b: shift(b),
2476            xi: to_bytes(xi.0),
2477            bulletproof_challenges: new_bulletproof_challenges,
2478        },
2479        should_finalize: must_verify.value().as_bool(),
2480        sponge_digest_before_evaluations: {
2481            let bigint: BigInteger256 = sponge_digest_before_evaluations.into();
2482            bigint.0
2483        },
2484    };
2485
2486    Ok(ExpandedProof {
2487        sg: challenge_polynomial_commitment,
2488        unfinalized,
2489        prev_statement_with_hashes,
2490        x_hat,
2491        witness,
2492        actual_wrap_domain: dlog_vk.domain.log_size_of_group,
2493    })
2494}
2495
2496#[derive(Debug)]
2497struct ExpandedProof {
2498    sg: GroupAffine<Fp>,
2499    unfinalized: Unfinalized,
2500    prev_statement_with_hashes: PreparedStatement,
2501    x_hat: PointEvaluations<Fq>,
2502    witness: PerProofWitness,
2503    actual_wrap_domain: u32,
2504}
2505
2506#[derive(Clone, Debug)]
2507pub struct PerProofWitness {
2508    pub app_state: Option<Rc<dyn ToFieldElementsDebug>>,
2509    // app_state: AppState,
2510    pub wrap_proof: ProverProof<Fq>,
2511    pub proof_state: ProofState,
2512    pub prev_proof_evals: AllEvals<Fp>,
2513    pub prev_challenges: Vec<[Fp; 16]>,
2514    pub prev_challenge_polynomial_commitments: Vec<GroupAffine<Fp>>,
2515    /// Hack until I understand how feature flags are used.
2516    /// So far they are always `OptFlag::No`, except for zkapps using proof authorization, in that
2517    /// case they are `OptFlag::Maybe`.
2518    pub hack_feature_flags: OptFlag,
2519}
2520
2521impl PerProofWitness {
2522    pub fn with_app_state(self, app_state: Rc<dyn ToFieldElementsDebug>) -> PerProofWitness {
2523        let Self {
2524            app_state: old,
2525            wrap_proof,
2526            proof_state,
2527            prev_proof_evals,
2528            prev_challenges,
2529            prev_challenge_polynomial_commitments,
2530            hack_feature_flags,
2531        } = self;
2532        assert!(old.is_none());
2533
2534        PerProofWitness {
2535            app_state: Some(app_state),
2536            wrap_proof,
2537            proof_state,
2538            prev_proof_evals,
2539            prev_challenges,
2540            prev_challenge_polynomial_commitments,
2541            hack_feature_flags,
2542        }
2543    }
2544}
2545
2546impl Check<Fp> for PerProofWitness {
2547    fn check(&self, w: &mut Witness<Fp>) {
2548        let Self {
2549            app_state,
2550            wrap_proof,
2551            proof_state,
2552            prev_proof_evals: _,
2553            prev_challenges: _,
2554            prev_challenge_polynomial_commitments,
2555            hack_feature_flags: _,
2556        } = self;
2557
2558        assert!(app_state.is_none());
2559
2560        let ProverProof::<Fq> {
2561            commitments:
2562                ProverCommitments {
2563                    w_comm,
2564                    z_comm,
2565                    t_comm,
2566                    lookup: _,
2567                },
2568            proof:
2569                OpeningProof {
2570                    lr,
2571                    delta,
2572                    z1,
2573                    z2,
2574                    sg,
2575                },
2576            evals: _,
2577            ft_eval1: _,
2578            prev_challenges: _,
2579        } = wrap_proof;
2580
2581        for poly in w_comm {
2582            poly.chunks.check(w);
2583        }
2584        z_comm.chunks.check(w);
2585        t_comm.chunks.check(w);
2586        lr.check(w);
2587
2588        let shift = |f: Fq| <Fq as FieldWitness>::Shifting::of_field(f);
2589
2590        shift(*z1).check(w);
2591        shift(*z2).check(w);
2592
2593        delta.check(w);
2594        sg.check(w);
2595
2596        let ProofState {
2597            deferred_values:
2598                DeferredValues {
2599                    plonk:
2600                        Plonk {
2601                            alpha: _,
2602                            beta: _,
2603                            gamma: _,
2604                            zeta: _,
2605                            zeta_to_srs_length,
2606                            zeta_to_domain_size,
2607                            perm,
2608                            lookup: _,
2609                            feature_flags: _,
2610                        },
2611                    combined_inner_product,
2612                    b,
2613                    xi,
2614                    bulletproof_challenges,
2615                    branch_data,
2616                },
2617            sponge_digest_before_evaluations: _,
2618            messages_for_next_wrap_proof: _,
2619        } = proof_state;
2620
2621        zeta_to_srs_length.check(w);
2622        zeta_to_domain_size.check(w);
2623        perm.check(w);
2624        combined_inner_product.check(w);
2625        b.check(w);
2626        two_u64_to_field::<Fp, _>(xi).check(w);
2627        bulletproof_challenges.check(w);
2628
2629        {
2630            let v2::CompositionTypesBranchDataStableV1 {
2631                proofs_verified: _,
2632                domain_log2,
2633            } = branch_data;
2634            let domain_log2: u64 = domain_log2.0.as_u8() as u64;
2635
2636            // Assert 16 bits
2637            const NBITS: usize = 16;
2638            let (_, endo) = endos::<Fq>();
2639            to_field_checked::<Fp, NBITS>(Fp::from(domain_log2), endo, w);
2640        }
2641
2642        prev_challenge_polynomial_commitments.check(w);
2643    }
2644}
2645
2646pub fn extract_recursion_challenges<const N: usize>(
2647    proofs: &[&v2::PicklesProofProofsVerified2ReprStableV2; N],
2648) -> anyhow::Result<Vec<RecursionChallenge<GroupAffine<Fq>>>> {
2649    use poly_commitment::PolyComm;
2650
2651    let comms: [(Fq, Fq); N] =
2652        crate::try_array_into_with(proofs, |proof| -> Result<_, InvalidBigInt> {
2653            let (a, b) = &proof
2654                .statement
2655                .proof_state
2656                .messages_for_next_wrap_proof
2657                .challenge_polynomial_commitment;
2658            Ok((a.to_field::<Fq>()?, b.to_field::<Fq>()?))
2659        })?;
2660
2661    let challs = proofs
2662        .iter()
2663        .map(|p| {
2664            p.statement
2665                .proof_state
2666                .deferred_values
2667                .bulletproof_challenges
2668                .clone()
2669        })
2670        .collect::<Vec<_>>();
2671    let challs = extract_bulletproof(&challs);
2672
2673    Ok(challs
2674        .into_iter()
2675        .zip(comms)
2676        .map(|(chals, (x, y))| {
2677            let comm = PolyComm::<mina_curves::pasta::Vesta> {
2678                chunks: vec![make_group(x, y)],
2679            };
2680            RecursionChallenge {
2681                chals: chals.to_vec(),
2682                comm,
2683            }
2684        })
2685        .collect())
2686}
2687
2688/// `N_PREVIOUS`: Number of previous proofs.
2689///  - For block and merge proofs, the number is 2
2690///  - For zkapp with proof authorization, it's 1
2691///  - For other proofs, it's 0
2692pub struct StepParams<'a, const N_PREVIOUS: usize> {
2693    pub app_state: Rc<dyn ToFieldElementsDebug>,
2694    pub rule: InductiveRule<'a, N_PREVIOUS>,
2695    pub for_step_datas: [&'a ForStep; N_PREVIOUS],
2696    pub indexes: [(
2697        &'a VerifierIndex<Fq>,
2698        &'a CircuitPlonkVerificationKeyEvals<Fp>,
2699    ); N_PREVIOUS],
2700    pub prev_challenge_polynomial_commitments: Vec<RecursionChallenge<GroupAffine<Fq>>>,
2701    /// TODO: Remove this. See documentation in `PerProofWitness` struct.
2702    pub hack_feature_flags: OptFlag,
2703    pub step_prover: &'a Prover<Fp>,
2704    pub wrap_prover: &'a Prover<Fq>,
2705    pub only_verify_constraints: bool,
2706}
2707
2708pub struct StepProof {
2709    pub statement: StepStatement,
2710    pub prev_evals: Vec<AllEvals<Fq>>,
2711    pub proof_with_public: ProofWithPublic<Fp>,
2712}
2713
2714pub fn step<C: ProofConstants, const N_PREVIOUS: usize>(
2715    params: StepParams<N_PREVIOUS>,
2716    w: &mut Witness<Fp>,
2717) -> anyhow::Result<StepProof> {
2718    let StepParams {
2719        app_state,
2720        rule,
2721        for_step_datas,
2722        indexes,
2723        prev_challenge_polynomial_commitments,
2724        hack_feature_flags,
2725        step_prover,
2726        wrap_prover,
2727        only_verify_constraints,
2728    } = params;
2729
2730    let dlog_plonk_index = w.exists(super::merge::dlog_plonk_index(wrap_prover));
2731
2732    let expanded_proofs: Box<[ExpandedProof; N_PREVIOUS]> = rule
2733        .previous_proof_statements
2734        .iter()
2735        .zip(indexes)
2736        .zip(for_step_datas)
2737        .map(|((statement, (verifier_index, dlog_plonk_index)), data)| {
2738            let PreviousProofStatement {
2739                public_input,
2740                proof,
2741                proof_must_verify,
2742            } = statement;
2743
2744            expand_proof(ExpandProofParams {
2745                dlog_vk: verifier_index,
2746                dlog_plonk_index,
2747                app_state: public_input,
2748                t: proof,
2749                public_input_length: 40,
2750                tag: (),
2751                must_verify: *proof_must_verify,
2752                hack_feature_flags,
2753                zk_rows: data.zk_rows,
2754            })
2755        })
2756        .collect::<Result<Vec<_>, _>>()
2757        .context("expand_proof")?
2758        .try_into()
2759        .unwrap();
2760
2761    let prevs: [&PerProofWitness; N_PREVIOUS] =
2762        w.exists(expanded_proofs.each_ref().map(|p| &p.witness));
2763    let unfinalized_proofs_unextended: [&Unfinalized; N_PREVIOUS] =
2764        w.exists(expanded_proofs.each_ref().map(|p| &p.unfinalized));
2765
2766    let messages_for_next_wrap_proof: [Fp; N_PREVIOUS] = {
2767        let f = four_u64_to_field::<Fp, _>;
2768
2769        crate::try_array_into_with(&expanded_proofs, |p| {
2770            f(&p.prev_statement_with_hashes
2771                .proof_state
2772                .messages_for_next_wrap_proof)
2773        })?
2774    };
2775
2776    let messages_for_next_wrap_proof_padded: [Fp; 2] = w.exists({
2777        let n_padding = 2 - expanded_proofs.len();
2778        std::array::from_fn(|i| {
2779            if i < n_padding {
2780                messages_for_next_wrap_proof_padding()
2781            } else {
2782                messages_for_next_wrap_proof[i - n_padding]
2783            }
2784        })
2785    });
2786
2787    let actual_wrap_domains: [usize; N_PREVIOUS] = {
2788        let all_possible_domains = wrap_verifier::all_possible_domains();
2789
2790        let actuals_wrap_domain: [u32; N_PREVIOUS] =
2791            expanded_proofs.each_ref().map(|p| p.actual_wrap_domain);
2792
2793        actuals_wrap_domain.map(|domain_size| {
2794            let domain_size = domain_size as u64;
2795            all_possible_domains
2796                .iter()
2797                .position(|Domain::Pow2RootsOfUnity(d)| *d == domain_size)
2798                .unwrap_or(0)
2799        })
2800    };
2801
2802    let prevs: [PerProofWitness; N_PREVIOUS] = rule
2803        .previous_proof_statements
2804        .iter()
2805        .zip(prevs)
2806        .map(|(stmt, proof)| proof.clone().with_app_state(stmt.public_input.clone()))
2807        .collect::<Vec<_>>()
2808        .try_into()
2809        .unwrap();
2810
2811    let srs = get_srs_mut::<Fq>();
2812    let mut srs = srs.lock().unwrap();
2813
2814    let bulletproof_challenges = prevs
2815        .iter()
2816        .zip(messages_for_next_wrap_proof)
2817        .zip(unfinalized_proofs_unextended)
2818        .zip(&rule.previous_proof_statements)
2819        .zip(actual_wrap_domains)
2820        .zip(for_step_datas)
2821        .map(
2822            |(
2823                ((((proof, msg_for_next_wrap_proof), unfinalized), stmt), actual_wrap_domain),
2824                data,
2825            )| {
2826                let PreviousProofStatement {
2827                    proof_must_verify: should_verify,
2828                    ..
2829                } = stmt;
2830
2831                match data.wrap_domain {
2832                    ForStepKind::SideLoaded(_) => {} // Nothing
2833                    ForStepKind::Known(wrap_domain) => {
2834                        let actual_wrap_domain = wrap_domains(actual_wrap_domain);
2835                        assert_eq!(actual_wrap_domain.h, wrap_domain);
2836                    }
2837                }
2838                let (chals, _verified) = verify_one(
2839                    VerifyOneParams {
2840                        srs: &mut srs,
2841                        proof,
2842                        data,
2843                        messages_for_next_wrap_proof: msg_for_next_wrap_proof,
2844                        unfinalized,
2845                        should_verify: *should_verify,
2846                    },
2847                    w,
2848                )?;
2849                Ok(chals.try_into().unwrap()) // Never fail, we know it's 16
2850            },
2851        )
2852        .collect::<Result<Vec<[Fp; 16]>, anyhow::Error>>()
2853        .context("verify_one")?;
2854
2855    std::mem::drop(srs);
2856
2857    let messages_for_next_step_proof = {
2858        let msg = MessagesForNextStepProof {
2859            app_state: Rc::clone(&app_state),
2860            dlog_plonk_index: &dlog_plonk_index,
2861            challenge_polynomial_commitments: prevs
2862                .iter()
2863                .map(|v| InnerCurve::of_affine(v.wrap_proof.proof.sg))
2864                .collect(),
2865            old_bulletproof_challenges: bulletproof_challenges,
2866        };
2867        crate::proofs::transaction::checked_hash2(&msg.to_fields(), w)
2868    };
2869
2870    let unfinalized_proofs = {
2871        let mut unfinalized_proofs: Vec<_> =
2872            unfinalized_proofs_unextended.into_iter().cloned().collect();
2873        while unfinalized_proofs.len() < 2 {
2874            unfinalized_proofs.insert(0, Unfinalized::dummy());
2875        }
2876        unfinalized_proofs
2877    };
2878
2879    let statement = crate::proofs::transaction::StepMainStatement {
2880        proof_state: crate::proofs::transaction::StepMainProofState {
2881            unfinalized_proofs,
2882            messages_for_next_step_proof,
2883        },
2884        messages_for_next_wrap_proof: messages_for_next_wrap_proof_padded.to_vec(),
2885    };
2886
2887    w.primary = statement.to_field_elements_owned();
2888
2889    let proof = create_proof::<C, Fp>(
2890        CreateProofParams {
2891            prover: step_prover,
2892            prev_challenges: prev_challenge_polynomial_commitments,
2893            only_verify_constraints,
2894        },
2895        w,
2896    )
2897    .context("create_proof")?;
2898
2899    let proofs: [&v2::PicklesProofProofsVerified2ReprStableV2; N_PREVIOUS] = rule
2900        .previous_proof_statements
2901        .each_ref()
2902        .map(|stmt| stmt.proof);
2903
2904    let prev_evals = proofs
2905        .iter()
2906        .zip(&*expanded_proofs)
2907        .map(|(p, expanded)| {
2908            let evals = evals_from_p2p(&p.proof.evaluations)?;
2909            let ft_eval1 = p.proof.ft_eval1.to_field()?;
2910            let PointEvaluations { zeta, zeta_omega } = expanded.x_hat;
2911
2912            Ok(AllEvals {
2913                ft_eval1,
2914                evals: EvalsWithPublicInput {
2915                    evals,
2916                    public_input: (vec![zeta], vec![zeta_omega]),
2917                },
2918            })
2919        })
2920        .collect::<anyhow::Result<Vec<_>>>()
2921        .context("prev_evals")?;
2922
2923    let challenge_polynomial_commitments = expanded_proofs
2924        .iter()
2925        .map(|v| InnerCurve::of_affine(v.sg))
2926        .collect();
2927
2928    let (old_bulletproof_challenges, messages_for_next_wrap_proof): (Vec<_>, Vec<_>) = proofs
2929        .iter()
2930        .map(|v| {
2931            let StatementProofState {
2932                deferred_values,
2933                messages_for_next_wrap_proof,
2934                ..
2935            } = (&v.statement.proof_state).try_into()?;
2936            Ok((
2937                deferred_values.bulletproof_challenges,
2938                messages_for_next_wrap_proof,
2939            ))
2940        })
2941        .collect::<Result<Vec<_>, InvalidBigInt>>() // TODO: Refactor when `try_unzip` is stable
2942        .context("unzip proof state")?
2943        .into_iter()
2944        .unzip();
2945
2946    let old_bulletproof_challenges = old_bulletproof_challenges
2947        .into_iter()
2948        .map(|v: [[u64; 2]; 16]| v.each_ref().map(two_u64_to_field))
2949        .collect();
2950
2951    let step_statement = crate::proofs::transaction::StepStatement {
2952        proof_state: crate::proofs::transaction::StepProofState {
2953            unfinalized_proofs: statement.proof_state.unfinalized_proofs,
2954            messages_for_next_step_proof: ReducedMessagesForNextStepProof {
2955                app_state: Rc::clone(&app_state),
2956                challenge_polynomial_commitments,
2957                old_bulletproof_challenges,
2958            },
2959        },
2960        messages_for_next_wrap_proof,
2961    };
2962
2963    Ok(StepProof {
2964        statement: step_statement,
2965        prev_evals,
2966        proof_with_public: proof,
2967    })
2968}