mina_tree/proofs/
wrap.rs

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