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
63pub 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
84pub 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 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.clone()
275}
276
277fn 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
301pub 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 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)); to_fp(messages_for_next_step_proof_hash).to_field_elements(&mut fields);
506
507 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
544pub 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 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])); 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 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 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() };
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 #[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 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 }
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 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() };
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!() }
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
1075pub 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 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 field::mul(zk_polynomial, zeta - omega_to_zk_minus_1, w),
1288 |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
1347pub const PERMUTS_MINUS_1_ADD_N1: usize = 6;
1349
1350const 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 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 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 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 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 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]); };
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!()
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 let sg_evals2 = sg_evals(zetaw);
1631 let sg_evals1 = sg_evals(plonk.zeta);
1632 (sg_evals1, sg_evals2)
1633 };
1634
1635 {
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 .expect("combine_split_evaluations: empty")
1753 };
1754
1755 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 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.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 if domains.len() == 1 {
1923 (CircuitVar::Constant(a), b)
1924 } else {
1925 (CircuitVar::Var(a), b)
1926 }
1927 }
1928
1929 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 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 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, ¶ms, 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() };
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); 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); 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 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); 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 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 {
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 {
2711 packed.push(Packed::PackedBits(
2712 var(four_u64_to_field(sponge_digest_before_evaluations)?),
2713 255,
2714 ));
2715 }
2716
2717 {
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 {
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)), );
2735
2736 {
2738 packed.push(Packed::PackedBits(var(Fq::from(*should_finalize)), 1));
2739 }
2740
2741 }
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 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 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}