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