mina_tree/proofs/
wrap.rs

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