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