1use super::*;
2use itertools::Itertools;
3use std::io::Cursor;
4use zinc_piop::{
5 combined_poly_resolver::CombinedPolyResolver,
6 ideal_check::{self, IdealCheckProtocol},
7 lookup::booleanity::{BoolVerifierAncillary, BooleanityChecker, BooleanityProof},
8 multipoint_eval::{self, MultipointEval, MultipointEvalFamilyInputs},
9 projections::{
10 ProjectedScalars, ProjectedTrace, project_scalars, project_scalars_to_field,
11 project_trace_coeffs_row_major,
12 },
13 sumcheck::multi_degree::MultiDegreeSumcheck,
14};
15use zinc_poly::univariate::dynamic::{DynamicPolynomialConfig, HasDynamicPolynomialConfig};
16use zinc_transcript::{
17 Blake3Transcript,
18 traits::{ConstTranscribable, Transcript},
19};
20use zinc_uair::{
21 Uair, UairSignature, UairTrace,
22 constraint_counter::count_constraints,
23 ideal::{Ideal, IdealCheck},
24 ideal_collector::IdealOrZero,
25};
26use zinc_utils::{add, powers, projectable_to_field::ProjectableToField, sub};
27use zip_plus::{
28 pcs::structs::{ZipPlus, ZipPlusParams, ZipTypes},
29 pcs_transcript::PcsVerifierTranscript,
30};
31
32#[derive(Clone, Debug)]
38pub struct VerifierBase<'a, Zt: ZincTypes<D, FD>, const D: usize, const FD: usize> {
39 num_vars: usize,
40 uair_signature: UairSignature<Zt::Fmod>,
41 pcs_transcript: PcsVerifierTranscript,
42 public_trace: &'a UairTrace<'a, Zt::Int, Zt::Int, D, D>,
43
44 vp_bin: &'a ZipPlusParams<Zt::BinaryZt, Zt::BinaryLc>,
46 vp_arb: &'a ZipPlusParams<Zt::ArbitraryZt, Zt::ArbitraryLc>,
47 vp_int: &'a ZipPlusParams<Zt::IntZt, Zt::IntLc>,
48}
49
50#[derive(Clone, Debug)]
56pub struct VerifierTranscriptReconstructed<
57 'a,
58 Zt: ZincTypes<D, FD>,
59 U: Uair,
60 C: BaseFieldConfig,
61 IdealOverF,
62 const D: usize,
63 const FD: usize,
64> {
65 base: VerifierBase<'a, Zt, D, FD>,
66
67 proof_commitments: (ZipPlusCommitment, ZipPlusCommitment, ZipPlusCommitment),
69 proof_ideal_check: IdealCheckProof<Zt::Fmod>,
70 proof_cpr: CombinedPolyResolverProof<Zt::Fmod>,
71 proof_combined_sumcheck: MultiDegreeSumcheckProof<Zt::Fmod>,
72 proof_multipoint_eval: MultipointEvalProof<Zt::Fmod>,
73 proof_witness_lifted_evals: Vec<Vec<DynamicPolynomial<Zt::Fmod>>>,
77 proof_witness_lifted_evals_pp: Option<Vec<DynamicPolynomial<Zt::Fmod>>>,
82 proof_lookup_proof: Option<BatchedLookupProof<Zt::Fmod>>,
83 proof_booleanity: Option<BooleanityProof<Zt::Fmod>>,
84 proof_affine_booleanity: Option<BooleanityProof<Zt::Fmod>>,
85 proof_ideal_checks_fq: Vec<IdealCheckProof<Zt::Fmod>>,
86 proof_cpr_fq: Vec<CombinedPolyResolverProof<Zt::Fmod>>,
87 proof_combined_sumchecks_fq: Vec<MultiDegreeSumcheckProof<Zt::Fmod>>,
88 proof_multipoint_evals_fq: Vec<MultipointEvalProof<Zt::Fmod>>,
89 _phantom: PhantomData<(U, C, IdealOverF)>,
90}
91
92#[derive(Clone, Debug)]
94pub struct VerifierPrimeProjected<
95 'a,
96 Zt: ZincTypes<D, FD>,
97 U: Uair,
98 C: BaseFieldConfig,
99 IdealOverF,
100 const D: usize,
101 const FD: usize,
102> {
103 base: VerifierBase<'a, Zt, D, FD>,
104 field_cfg: C,
105
106 proof_commitments: (ZipPlusCommitment, ZipPlusCommitment, ZipPlusCommitment),
108 proof_ideal_check: IdealCheckProof<C::Element>,
109 proof_cpr: CombinedPolyResolverProof<C::Element>,
110 proof_combined_sumcheck: MultiDegreeSumcheckProof<C::Element>,
111 proof_multipoint_eval: MultipointEvalProof<C::Element>,
112 proof_witness_lifted_evals: Vec<Vec<DynamicPolynomial<C::Element>>>,
115 proof_witness_lifted_evals_pp: Option<Vec<DynamicPolynomial<Zt::Fmod>>>,
118 proof_lookup_proof: Option<BatchedLookupProof<C::Element>>,
119 proof_booleanity: Option<BooleanityProof<C::Element>>,
120 proof_affine_booleanity: Option<BooleanityProof<C::Element>>,
121 proof_ideal_checks_fq: Vec<IdealCheckProof<C::Element>>,
122 proof_cpr_fq: Vec<CombinedPolyResolverProof<C::Element>>,
123 proof_combined_sumchecks_fq: Vec<MultiDegreeSumcheckProof<C::Element>>,
124 proof_multipoint_evals_fq: Vec<MultipointEvalProof<C::Element>>,
125 _phantom: PhantomData<(U, IdealOverF)>,
126}
127
128#[derive(Clone, Debug)]
130pub struct VerifierIdealChecked<
131 'a,
132 Zt: ZincTypes<D, FD>,
133 U: Uair,
134 C: BaseFieldConfig,
135 IdealOverF,
136 const D: usize,
137 const FD: usize,
138> {
139 base: VerifierBase<'a, Zt, D, FD>,
140 field_cfg: C,
141 all_field_cfgs: Vec<C>,
145 q_star_idx: usize,
148 ic_subclaims: Vec<ideal_check::VerifierSubclaim<C::Element>>,
155
156 proof_commitments: (ZipPlusCommitment, ZipPlusCommitment, ZipPlusCommitment),
158 proof_cpr: CombinedPolyResolverProof<C::Element>,
159 proof_combined_sumcheck: MultiDegreeSumcheckProof<C::Element>,
160 proof_multipoint_eval: MultipointEvalProof<C::Element>,
161 proof_witness_lifted_evals: Vec<Vec<DynamicPolynomial<C::Element>>>,
164 proof_witness_lifted_evals_pp: Option<Vec<DynamicPolynomial<Zt::Fmod>>>,
167 proof_lookup_proof: Option<BatchedLookupProof<C::Element>>,
168 proof_booleanity: Option<BooleanityProof<C::Element>>,
169 proof_affine_booleanity: Option<BooleanityProof<C::Element>>,
170 proof_cpr_fq: Vec<CombinedPolyResolverProof<C::Element>>,
172 proof_combined_sumchecks_fq: Vec<MultiDegreeSumcheckProof<C::Element>>,
174 proof_multipoint_evals_fq: Vec<MultipointEvalProof<C::Element>>,
176 _phantom: PhantomData<(U, IdealOverF)>,
177}
178
179#[derive(Clone, Debug)]
181pub struct VerifierEvalProjected<
182 'a,
183 Zt: ZincTypes<D, FD>,
184 U: Uair,
185 C: BaseFieldConfig,
186 IdealOverF,
187 const D: usize,
188 const FD: usize,
189> {
190 base: VerifierBase<'a, Zt, D, FD>,
191 field_cfg: C,
192 all_field_cfgs: Vec<C>,
195 q_star_idx: usize,
198 ic_subclaims: Vec<ideal_check::VerifierSubclaim<C::Element>>,
202 projecting_elements: Vec<C::Element>,
205 projected_scalars_f: ProjectedScalars<U::Scalar, C::Element>,
207 projected_scalars_f_fq: Vec<ProjectedScalars<U::Scalar, C::Element>>,
213
214 proof_commitments: (ZipPlusCommitment, ZipPlusCommitment, ZipPlusCommitment),
216 proof_cpr: CombinedPolyResolverProof<C::Element>,
217 proof_combined_sumcheck: MultiDegreeSumcheckProof<C::Element>,
218 proof_multipoint_eval: MultipointEvalProof<C::Element>,
219 proof_witness_lifted_evals: Vec<Vec<DynamicPolynomial<C::Element>>>,
222 proof_witness_lifted_evals_pp: Option<Vec<DynamicPolynomial<Zt::Fmod>>>,
225 proof_lookup_proof: Option<BatchedLookupProof<C::Element>>,
226 proof_booleanity: Option<BooleanityProof<C::Element>>,
227 proof_affine_booleanity: Option<BooleanityProof<C::Element>>,
228 proof_cpr_fq: Vec<CombinedPolyResolverProof<C::Element>>,
230 proof_combined_sumchecks_fq: Vec<MultiDegreeSumcheckProof<C::Element>>,
233 proof_multipoint_evals_fq: Vec<MultipointEvalProof<C::Element>>,
236 _phantom: PhantomData<(U, IdealOverF)>,
237}
238
239#[derive(Clone, Debug)]
241pub struct VerifierSumchecked<
242 'a,
243 Zt: ZincTypes<D, FD>,
244 C: BaseFieldConfig,
245 IdealOverF,
246 const D: usize,
247 const FD: usize,
248> {
249 base: VerifierBase<'a, Zt, D, FD>,
250 field_cfg: C,
251 all_field_cfgs: Vec<C>,
253 q_star_idx: usize,
255 projecting_elements: Vec<C::Element>,
258 cpr_eval_point: Vec<C::Element>,
260 cpr_up_evals: Vec<C::Element>,
261 cpr_bit_op_evals: Vec<C::Element>,
262 cpr_down_evals: Vec<C::Element>,
263 cpr_eval_points_fq: Vec<Vec<C::Element>>,
267 cpr_up_evals_fq: Vec<Vec<C::Element>>,
269 cpr_bit_op_evals_fq: Vec<Vec<C::Element>>,
272 cpr_down_evals_fq: Vec<Vec<C::Element>>,
274 bool_bit_slice_evals: Option<Vec<C::Element>>,
280 affine_bool_bit_slice_evals: Option<Vec<C::Element>>,
283 alpha_prime_f: Option<C::Element>,
290
291 proof_commitments: (ZipPlusCommitment, ZipPlusCommitment, ZipPlusCommitment),
293 proof_multipoint_eval: MultipointEvalProof<C::Element>,
294 proof_multipoint_evals_fq: Vec<MultipointEvalProof<C::Element>>,
295 proof_witness_lifted_evals: Vec<Vec<DynamicPolynomial<C::Element>>>,
298 proof_witness_lifted_evals_pp: Option<Vec<DynamicPolynomial<Zt::Fmod>>>,
301 proof_lookup_proof: Option<BatchedLookupProof<C::Element>>,
302 _phantom: PhantomData<IdealOverF>,
303}
304
305#[derive(Clone, Debug)]
307pub struct VerifierMultipointEvaled<
308 'a,
309 Zt: ZincTypes<D, FD>,
310 C: BaseFieldConfig,
311 IdealOverF,
312 const D: usize,
313 const FD: usize,
314> {
315 base: VerifierBase<'a, Zt, D, FD>,
316 field_cfg: C,
317 all_field_cfgs: Vec<C>,
319 projecting_elements: Vec<C::Element>,
322 alpha_prime_f: Option<C::Element>,
324 mp_subclaim: multipoint_eval::Subclaim<C::Element>,
325 mp_subclaims_fq: Vec<multipoint_eval::Subclaim<C::Element>>,
329
330 proof_commitments: (ZipPlusCommitment, ZipPlusCommitment, ZipPlusCommitment),
332 proof_witness_lifted_evals: Vec<Vec<DynamicPolynomial<C::Element>>>,
335 proof_witness_lifted_evals_pp: Option<Vec<DynamicPolynomial<Zt::Fmod>>>,
338 proof_lookup_proof: Option<BatchedLookupProof<C::Element>>,
339 _phantom: PhantomData<IdealOverF>,
340}
341
342#[derive(Clone, Debug)]
344pub struct VerifierLiftedEvalsChecked<
345 'a,
346 Zt: ZincTypes<D, FD>,
347 C: BaseFieldConfig,
348 IdealOverF,
349 const D: usize,
350 const FD: usize,
351> {
352 base: VerifierBase<'a, Zt, D, FD>,
353 lifted_evals_pp: Vec<DynamicPolynomial<C::Element>>,
356 q_pp_cfg: C,
358 r_star: Vec<C::Element>,
360
361 proof_commitments: (ZipPlusCommitment, ZipPlusCommitment, ZipPlusCommitment),
363 #[allow(dead_code)]
368 proof_lookup_proof: Option<BatchedLookupProof<C::Element>>,
369 _phantom: PhantomData<IdealOverF>,
370}
371
372#[derive(Clone, Debug)]
375pub struct VerifierPcsVerified<IdealOverF> {
376 pcs_transcript: PcsVerifierTranscript,
377 _phantom: PhantomData<IdealOverF>,
378}
379
380fn prepare_booleanity_verifier_group<C>(
381 transcript: &mut Blake3Transcript,
382 claimed_sums: &[C::Element],
383 group_idx: &mut usize,
384 num_cols: usize,
385 bit_width: usize,
386 num_vars: usize,
387 field_cfg: &C,
388) -> Result<Option<BoolVerifierAncillary<C::Element>>, ProtocolError<C::Element>>
389where
390 C: BaseFieldConfig + ProjectPrimitiveIntegersWithConfig + 'static,
391 C::Integer: ConstTranscribable,
392{
393 if num_cols == 0 {
394 return Ok(None);
395 }
396
397 let claimed_sum = claimed_sums
398 .get(*group_idx)
399 .ok_or(ProtocolError::BooleanityProofMissing)?;
400 let ancillary = BooleanityChecker::<C>::prepare_verifier(
401 transcript,
402 claimed_sum,
403 num_cols,
404 bit_width,
405 num_vars,
406 field_cfg,
407 )
408 .map_err(ProtocolError::Booleanity)?;
409 *group_idx = add!(*group_idx, 1);
410 Ok(Some(ancillary))
411}
412
413fn finalize_booleanity_verifier_group<C>(
414 transcript: &mut Blake3Transcript,
415 proof: Option<BooleanityProof<C::Element>>,
416 shared_point: &[C::Element],
417 expected_evaluations: &[C::Element],
418 group_idx: &mut usize,
419 ancillary: Option<BoolVerifierAncillary<C::Element>>,
420 field_cfg: &C,
421) -> Result<Option<Vec<C::Element>>, ProtocolError<C::Element>>
422where
423 C: BaseFieldConfig + ProjectPrimitiveIntegersWithConfig + 'static,
424 C::Integer: ConstTranscribable,
425{
426 let (proof, ancillary) = match (proof, ancillary) {
427 (None, None) => return Ok(None),
428 (Some(proof), Some(ancillary)) => (proof, ancillary),
429 _ => return Err(ProtocolError::BooleanityProofMissing),
430 };
431 let expected_eval = expected_evaluations
432 .get(*group_idx)
433 .ok_or(ProtocolError::BooleanityProofMissing)?;
434 *group_idx = add!(*group_idx, 1);
435
436 let subclaim = BooleanityChecker::<C>::finalize_verifier(
437 transcript,
438 proof,
439 shared_point.to_vec(),
440 expected_eval,
441 ancillary,
442 field_cfg,
443 )
444 .map_err(ProtocolError::Booleanity)?;
445 Ok(Some(subclaim.bit_slice_evals))
446}
447
448impl<Zt, U, C, const D: usize, const FD: usize> ZincPlusPiop<Zt, U, C, D, FD>
453where
454 Zt: ZincTypes<D, FD>,
455 U: Uair<Prime = Zt::Fmod>,
456 C: BaseFieldConfig<Integer = Zt::Fmod>,
457{
458 #[allow(clippy::type_complexity)]
461 pub fn step0_reconstruct_transcript<'a, IdealOverF>(
462 (vp_bin, vp_arb, vp_int): &'a (
463 ZipPlusParams<Zt::BinaryZt, Zt::BinaryLc>,
464 ZipPlusParams<Zt::ArbitraryZt, Zt::ArbitraryLc>,
465 ZipPlusParams<Zt::IntZt, Zt::IntLc>,
466 ),
467 mut proof: Proof<Zt::Fmod>,
468 public_trace: &'a UairTrace<'a, Zt::Int, Zt::Int, D, D>,
469 num_vars: usize,
470 ) -> Result<
471 VerifierTranscriptReconstructed<'a, Zt, U, C, IdealOverF, D, FD>,
472 ProtocolError<C::Element>,
473 >
474 where
475 IdealOverF: Ideal,
476 {
477 assert!(
478 num_vars > 0,
479 "Attempt to verify a constant: num_vars must be > 0"
480 );
481 let uair_signature = U::signature();
482 let zip_proof = std::mem::take(&mut proof.zip);
483 let mut base = VerifierBase {
484 num_vars,
485 uair_signature,
486 public_trace,
487 pcs_transcript: PcsVerifierTranscript {
488 fs_transcript: Blake3Transcript::default(),
489 stream: Cursor::new(zip_proof),
490 },
491 vp_bin,
492 vp_arb,
493 vp_int,
494 };
495
496 for comm in [
497 &proof.commitments.0,
498 &proof.commitments.1,
499 &proof.commitments.2,
500 ] {
501 base.pcs_transcript.fs_transcript.absorb_bytes(&comm.root);
502 }
503
504 absorb_public_columns(
505 &mut base.pcs_transcript.fs_transcript,
506 &base.public_trace.binary_poly,
507 );
508 absorb_public_columns(
509 &mut base.pcs_transcript.fs_transcript,
510 &base.public_trace.arbitrary_poly,
511 );
512 absorb_public_columns(
513 &mut base.pcs_transcript.fs_transcript,
514 &base.public_trace.int,
515 );
516
517 Ok(VerifierTranscriptReconstructed {
518 base,
519 proof_commitments: proof.commitments,
520 proof_ideal_check: proof.ideal_check,
521 proof_cpr: proof.cpr_proof,
522 proof_combined_sumcheck: proof.combined_sumcheck,
523 proof_multipoint_eval: proof.multipoint_eval,
524 proof_witness_lifted_evals: proof.witness_lifted_evals,
525 proof_witness_lifted_evals_pp: proof.witness_lifted_evals_pp,
526 proof_lookup_proof: proof.lookup_proof,
527 proof_booleanity: proof.booleanity_proof,
528 proof_affine_booleanity: proof.affine_booleanity_proof,
529 proof_ideal_checks_fq: proof.ideal_checks_fq,
530 proof_cpr_fq: proof.cpr_proofs_fq,
531 proof_combined_sumchecks_fq: proof.combined_sumchecks_fq,
532 proof_multipoint_evals_fq: proof.multipoint_evals_fq,
533 _phantom: PhantomData,
534 })
535 }
536}
537
538impl<'a, Zt, U, C, IdealOverF, const D: usize, const FD: usize>
539 VerifierTranscriptReconstructed<'a, Zt, U, C, IdealOverF, D, FD>
540where
541 Zt: ZincTypes<D, FD>,
542 C: BaseFieldConfig<Integer = Zt::Fmod> + Clone + Send + Sync + 'static,
543 U: Uair,
544 IdealOverF: Ideal,
545{
546 #[allow(clippy::type_complexity)]
548 pub fn step1_prime_projection(
549 mut self,
550 ) -> Result<VerifierPrimeProjected<'a, Zt, U, C, IdealOverF, D, FD>, ProtocolError<C::Element>>
551 {
552 let field_cfg = self
553 .base
554 .pcs_transcript
555 .fs_transcript
556 .get_random_field_cfg::<C, Zt::Fmod, Zt::PrimeTest>();
557
558 let all_field_cfgs = build_all_cfgs::<C>(&self.base.uair_signature, field_cfg.clone());
564 macro_rules! project {
565 ($cfg:expr, $section:expr) => {
566 $section.try_map(|int| project_canonical($cfg, int))
567 };
568 }
569 macro_rules! project_fq_vec {
570 ($section:expr) => {
571 $section
572 .iter()
573 .zip(&all_field_cfgs[1..])
574 .map(|(p, cfg)| project!(cfg, p))
575 .try_collect()
576 };
577 }
578 let n_families = all_field_cfgs.len();
579 if self.proof_witness_lifted_evals.len() != n_families {
580 return Err(ProtocolError::FamilyCountMismatch {
581 got: self.proof_witness_lifted_evals.len(),
582 expected: n_families,
583 });
584 }
585 for fq_len in [
586 self.proof_ideal_checks_fq.len(),
587 self.proof_cpr_fq.len(),
588 self.proof_combined_sumchecks_fq.len(),
589 self.proof_multipoint_evals_fq.len(),
590 ] {
591 if fq_len != sub!(n_families, 1) {
592 return Err(ProtocolError::FamilyCountMismatch {
593 got: fq_len,
594 expected: sub!(n_families, 1),
595 });
596 }
597 }
598 let proof_witness_lifted_evals = self
599 .proof_witness_lifted_evals
600 .iter()
601 .zip(&all_field_cfgs)
602 .map(|(polys, cfg)| {
603 polys
604 .iter()
605 .map(|poly| poly.try_map(|int| project_canonical(cfg, int)))
606 .collect::<Result<Vec<_>, _>>()
607 })
608 .collect::<Result<Vec<_>, _>>()?;
609
610 Ok(VerifierPrimeProjected {
611 base: self.base,
612 proof_commitments: self.proof_commitments,
613 proof_ideal_check: project!(&field_cfg, self.proof_ideal_check)?,
614 proof_cpr: project!(&field_cfg, self.proof_cpr)?,
615 proof_combined_sumcheck: project!(&field_cfg, self.proof_combined_sumcheck)?,
616 proof_multipoint_eval: project!(&field_cfg, self.proof_multipoint_eval)?,
617 proof_witness_lifted_evals,
618 proof_witness_lifted_evals_pp: self.proof_witness_lifted_evals_pp,
619 proof_lookup_proof: match &self.proof_lookup_proof {
620 Some(p) => Some(project!(&field_cfg, p)?),
621 None => None,
622 },
623 proof_booleanity: match &self.proof_booleanity {
624 Some(p) => Some(project!(&field_cfg, p)?),
625 None => None,
626 },
627 proof_affine_booleanity: match &self.proof_affine_booleanity {
628 Some(p) => Some(project!(&field_cfg, p)?),
629 None => None,
630 },
631 proof_ideal_checks_fq: project_fq_vec!(self.proof_ideal_checks_fq)?,
632 proof_cpr_fq: project_fq_vec!(self.proof_cpr_fq)?,
633 proof_combined_sumchecks_fq: project_fq_vec!(self.proof_combined_sumchecks_fq)?,
634 proof_multipoint_evals_fq: project_fq_vec!(self.proof_multipoint_evals_fq)?,
635 field_cfg,
636 _phantom: PhantomData,
637 })
638 }
639}
640
641impl<'a, Zt, U, C, IdealOverF, const D: usize, const FD: usize>
642 VerifierPrimeProjected<'a, Zt, U, C, IdealOverF, D, FD>
643where
644 Zt: ZincTypes<D, FD>,
645 Zt::Int: ProjectableToField<C>,
646 <Zt::ArbitraryZt as ZipTypes>::Eval: ProjectableToField<C>,
647 C: BaseFieldConfig<Integer = Zt::Fmod>
648 + ProjectPrimitiveIntegersWithConfig
649 + ProjectElementWithConfig<Zt::Int>
650 + ProjectElementWithConfig<Zt::CombR>
651 + ProjectElementWithConfig<Zt::Chal>
652 + Clone
653 + Send
654 + Sync
655 + 'static,
656 U: Uair + 'static,
657 IdealOverF: Ideal + for<'cfg> IdealCheck<DynamicPolynomialConfig<'cfg, C>>,
658{
659 #[allow(clippy::type_complexity)]
672 pub fn step2_ideal_check(
673 mut self,
674 project_ideal: impl Fn(&IdealOrZero<U::Ideal>, &C) -> IdealOverF,
675 project_fq_ideal: impl Fn(&IdealOrZero<U::FqIdeal>, &C) -> IdealOverF,
676 ) -> Result<VerifierIdealChecked<'a, Zt, U, C, IdealOverF, D, FD>, ProtocolError<C::Element>>
677 {
678 let num_constraints = count_constraints::<U>();
679 let primes = self.base.uair_signature.primes().to_vec();
680
681 if primes.len() != self.proof_ideal_checks_fq.len() {
682 return Err(ProtocolError::FqIdealCheck {
684 prime_idx: self.proof_ideal_checks_fq.len(),
685 q: "<Length mismatch>".to_owned(),
686 source: IdealCheckError::IdealCollectorError(
687 ideal_check::BatchedIdealCheckError::LengthMismatch {
688 num_ideals: primes.len(),
689 provided_values: self.proof_ideal_checks_fq.len(),
690 },
691 ),
692 });
693 }
694
695 let all_field_cfgs = build_all_cfgs::<C>(&self.base.uair_signature, self.field_cfg.clone());
700 let q_star_idx = shared_challenge::compute_q_star_idx::<C>(&all_field_cfgs);
701 let q_star_cfg = &all_field_cfgs[q_star_idx];
702 let shared_eval_points: Vec<Vec<C::Element>> =
703 shared_challenge::sample_shared_field_challenges::<C>(
704 &mut self.base.pcs_transcript.fs_transcript,
705 self.base.num_vars,
706 q_star_cfg,
707 &all_field_cfgs,
708 );
709
710 let mut ic_subclaims: Vec<ideal_check::VerifierSubclaim<C::Element>> =
712 Vec::with_capacity(all_field_cfgs.len());
713 let q_subclaim = IdealCheckProtocol::<U>::verify_as_subprotocol::<_, IdealOverF, _, _>(
714 &mut self.base.pcs_transcript.fs_transcript,
715 self.proof_ideal_check,
716 0,
717 num_constraints.q,
718 &shared_eval_points[0],
719 |ideal| project_ideal(ideal, &self.field_cfg),
720 |_| unreachable!("Q[X] family"),
721 &self.field_cfg,
722 )?;
723 ic_subclaims.push(q_subclaim);
724
725 for (prime_idx, (cfg_q_i, fq_proof)) in all_field_cfgs[1..]
729 .iter()
730 .zip(self.proof_ideal_checks_fq)
731 .enumerate()
732 {
733 let family_idx = add!(prime_idx, 1);
734 let fq_subclaim =
735 IdealCheckProtocol::<U>::verify_as_subprotocol::<_, IdealOverF, _, _>(
736 &mut self.base.pcs_transcript.fs_transcript,
737 fq_proof,
738 family_idx,
739 num_constraints.for_prime(prime_idx),
740 &shared_eval_points[family_idx],
741 |_| unreachable!("F_q[X] family"),
742 |ideal| project_fq_ideal(ideal, cfg_q_i),
743 cfg_q_i,
744 )
745 .map_err(|source| ProtocolError::FqIdealCheck {
746 prime_idx,
747 q: cfg_q_i.modulus().to_string(),
748 source,
749 })?;
750 ic_subclaims.push(fq_subclaim);
751 }
752
753 Ok(VerifierIdealChecked {
754 base: self.base,
755 field_cfg: self.field_cfg,
756 all_field_cfgs,
757 q_star_idx,
758 ic_subclaims,
759 proof_commitments: self.proof_commitments,
760 proof_cpr: self.proof_cpr,
761 proof_combined_sumcheck: self.proof_combined_sumcheck,
762 proof_multipoint_eval: self.proof_multipoint_eval,
763 proof_witness_lifted_evals: self.proof_witness_lifted_evals,
764 proof_witness_lifted_evals_pp: self.proof_witness_lifted_evals_pp,
765 proof_lookup_proof: self.proof_lookup_proof,
766 proof_booleanity: self.proof_booleanity,
767 proof_affine_booleanity: self.proof_affine_booleanity,
768 proof_cpr_fq: self.proof_cpr_fq,
769 proof_combined_sumchecks_fq: self.proof_combined_sumchecks_fq,
770 proof_multipoint_evals_fq: self.proof_multipoint_evals_fq,
771 _phantom: PhantomData,
772 })
773 }
774}
775
776impl<'a, Zt, U, C, IdealOverF, const D: usize, const FD: usize>
777 VerifierIdealChecked<'a, Zt, U, C, IdealOverF, D, FD>
778where
779 Zt: ZincTypes<D, FD>,
780 C: BaseFieldConfig<Integer = Zt::Fmod>
781 + ProjectElementWithConfig<Zt::Chal>
782 + Clone
783 + Send
784 + Sync
785 + 'static,
786 U: Uair<Prime = Zt::Fmod> + 'static,
787 IdealOverF: Ideal,
788{
789 pub fn step3_eval_projection(
803 mut self,
804 project_scalar: impl Fn(&U::Scalar, &C) -> DynamicPolynomial<C::Element>,
805 ) -> Result<VerifierEvalProjected<'a, Zt, U, C, IdealOverF, D, FD>, ProtocolError<C::Element>>
806 {
807 let q_star_cfg = &self.all_field_cfgs[self.q_star_idx];
808 let projecting_elements: Vec<C::Element> =
809 shared_challenge::sample_shared_field_challenge::<C>(
810 &mut self.base.pcs_transcript.fs_transcript,
811 q_star_cfg,
812 &self.all_field_cfgs,
813 );
814
815 let projected_scalars_fx =
817 project_scalars::<C, U>(&self.field_cfg, |s| project_scalar(s, &self.field_cfg));
818 let projected_scalars_f = project_scalars_to_field(
819 &self.field_cfg,
820 projected_scalars_fx,
821 &projecting_elements[0],
822 )
823 .map_err(|(_s, _f, e)| ProtocolError::ScalarProjection(e))?;
824
825 let projected_scalars_f_fq: Result<Vec<ProjectedScalars<U::Scalar, C::Element>>, _> = self
827 .all_field_cfgs
828 .iter()
829 .zip(projecting_elements.iter())
830 .skip(1) .map(|(cfg_i, projecting_element)| {
832 let projected_scalars_fx_i =
833 project_scalars::<C, U>(cfg_i, |s| project_scalar(s, cfg_i));
834 project_scalars_to_field(cfg_i, projected_scalars_fx_i, projecting_element)
835 .map_err(|(_s, _f, e)| ProtocolError::ScalarProjection(e))
836 })
837 .collect();
838 let projected_scalars_f_fq = projected_scalars_f_fq?;
839
840 Ok(VerifierEvalProjected {
841 base: self.base,
842 field_cfg: self.field_cfg,
843 all_field_cfgs: self.all_field_cfgs,
844 q_star_idx: self.q_star_idx,
845 ic_subclaims: self.ic_subclaims,
846 projecting_elements,
847 projected_scalars_f,
848 projected_scalars_f_fq,
849 proof_commitments: self.proof_commitments,
850 proof_cpr: self.proof_cpr,
851 proof_combined_sumcheck: self.proof_combined_sumcheck,
852 proof_multipoint_eval: self.proof_multipoint_eval,
853 proof_witness_lifted_evals: self.proof_witness_lifted_evals,
854 proof_witness_lifted_evals_pp: self.proof_witness_lifted_evals_pp,
855 proof_lookup_proof: self.proof_lookup_proof,
856 proof_booleanity: self.proof_booleanity,
857 proof_affine_booleanity: self.proof_affine_booleanity,
858 proof_cpr_fq: self.proof_cpr_fq,
859 proof_combined_sumchecks_fq: self.proof_combined_sumchecks_fq,
860 proof_multipoint_evals_fq: self.proof_multipoint_evals_fq,
861 _phantom: PhantomData,
862 })
863 }
864}
865
866impl<'a, Zt, U, C, IdealOverF, const D: usize, const FD: usize>
867 VerifierEvalProjected<'a, Zt, U, C, IdealOverF, D, FD>
868where
869 Zt: ZincTypes<D, FD>,
870 Zt::Int: ProjectableToField<C>,
871 <Zt::ArbitraryZt as ZipTypes>::Eval: ProjectableToField<C>,
872 C: BaseFieldConfig<Integer = Zt::Fmod>
873 + ProjectPrimitiveIntegersWithConfig
874 + ProjectElementWithConfig<Zt::Int>
875 + ProjectElementWithConfig<Zt::CombR>
876 + ProjectElementWithConfig<Zt::Chal>
877 + Clone
878 + Send
879 + Sync
880 + 'static,
881 U: Uair<Prime = Zt::Fmod> + 'static,
882 IdealOverF: Ideal,
883{
884 pub fn step4_sumcheck_verify(
888 mut self,
889 ) -> Result<VerifierSumchecked<'a, Zt, C, IdealOverF, D, FD>, ProtocolError<C::Element>> {
890 let num_constraints = count_constraints::<U>();
891
892 let n_fq = self.all_field_cfgs.len().saturating_sub(1);
894 if self.proof_cpr_fq.len() != n_fq || self.proof_combined_sumchecks_fq.len() != n_fq {
895 return Err(ProtocolError::FqIdealCheck {
896 prime_idx: self.proof_cpr_fq.len(),
897 q: "<fq sub-proof length mismatch>".to_owned(),
898 source: IdealCheckError::IdealCollectorError(
899 ideal_check::BatchedIdealCheckError::LengthMismatch {
900 num_ideals: n_fq,
901 provided_values: self.proof_cpr_fq.len(),
902 },
903 ),
904 });
905 }
906
907 let q_star_cfg = &self.all_field_cfgs[self.q_star_idx];
909 let folding_challenges: Vec<C::Element> =
910 shared_challenge::sample_shared_field_challenge::<C>(
911 &mut self.base.pcs_transcript.fs_transcript,
912 q_star_cfg,
913 &self.all_field_cfgs,
914 );
915
916 let q_cpr_verifier_ancillary = CombinedPolyResolver::prepare_verifier::<U>(
918 &self.proof_cpr,
919 self.proof_combined_sumcheck.claimed_sums()[0].clone(),
920 &self.ic_subclaims[0],
921 num_constraints.q,
922 self.base.num_vars,
923 &self.projecting_elements[0],
924 &folding_challenges[0],
925 &self.field_cfg,
926 )?;
927
928 let sig = self.base.uair_signature.clone();
932 let num_pub_bin = sig.public_cols().num_binary_poly_cols();
933 let num_total_bin = sig.total_cols().num_binary_poly_cols();
934 let num_wit_bin = num_total_bin.saturating_sub(num_pub_bin);
935 let num_affine_virtuals = sig.affine_virtual_specs().len();
936 let mut bool_group_idx = 1;
937
938 let bool_verifier_ancillary = prepare_booleanity_verifier_group(
939 &mut self.base.pcs_transcript.fs_transcript,
940 self.proof_combined_sumcheck.claimed_sums(),
941 &mut bool_group_idx,
942 num_wit_bin,
943 D,
944 self.base.num_vars,
945 &self.field_cfg,
946 )?;
947 let affine_bool_verifier_ancillary = prepare_booleanity_verifier_group(
948 &mut self.base.pcs_transcript.fs_transcript,
949 self.proof_combined_sumcheck.claimed_sums(),
950 &mut bool_group_idx,
951 num_affine_virtuals,
952 D,
953 self.base.num_vars,
954 &self.field_cfg,
955 )?;
956
957 let mut fq_cpr_ancillaries: Vec<_> = Vec::with_capacity(n_fq);
959 for prime_idx in 0..n_fq {
960 let family_idx = add!(prime_idx, 1);
961 let cfg_i = &self.all_field_cfgs[family_idx];
962 let anc_i = CombinedPolyResolver::prepare_verifier::<U>(
963 &self.proof_cpr_fq[prime_idx],
964 self.proof_combined_sumchecks_fq[prime_idx].claimed_sums()[0].clone(),
965 &self.ic_subclaims[family_idx],
966 num_constraints.for_prime(prime_idx),
967 self.base.num_vars,
968 &self.projecting_elements[family_idx],
969 &folding_challenges[family_idx],
970 cfg_i,
971 )?;
972 fq_cpr_ancillaries.push(anc_i);
973 }
974
975 let mut family_proofs: Vec<(&MultiDegreeSumcheckProof<C::Element>, &C)> =
979 Vec::with_capacity(add!(n_fq, 1));
980 family_proofs.push((&self.proof_combined_sumcheck, &self.field_cfg));
981 for prime_idx in 0..n_fq {
982 let family_idx = add!(prime_idx, 1);
983 family_proofs.push((
984 &self.proof_combined_sumchecks_fq[prime_idx],
985 &self.all_field_cfgs[family_idx],
986 ));
987 }
988 let all_md_subclaims = MultiDegreeSumcheck::verify_as_subprotocol(
989 &mut self.base.pcs_transcript.fs_transcript,
990 self.base.num_vars,
991 &family_proofs,
992 q_star_cfg,
993 )
994 .map_err(CombinedPolyResolverError::SumcheckError)?;
995
996 let q_md_subclaims = all_md_subclaims
998 .first()
999 .expect("Q[X] family subclaim always present");
1000
1001 let cpr_subclaim = CombinedPolyResolver::finalize_verifier::<U>(
1002 &mut self.base.pcs_transcript.fs_transcript,
1003 self.proof_cpr,
1004 q_md_subclaims.point().to_vec(),
1005 q_md_subclaims.expected_evaluations()[0].clone(),
1006 q_cpr_verifier_ancillary,
1007 &self.projected_scalars_f,
1008 0,
1009 &self.field_cfg,
1010 )?;
1011
1012 let mut bool_group_idx = 1;
1017 let bool_bit_slice_evals = finalize_booleanity_verifier_group(
1018 &mut self.base.pcs_transcript.fs_transcript,
1019 self.proof_booleanity.take(),
1020 q_md_subclaims.point(),
1021 q_md_subclaims.expected_evaluations(),
1022 &mut bool_group_idx,
1023 bool_verifier_ancillary,
1024 &self.field_cfg,
1025 )?;
1026 let affine_bool_bit_slice_evals = finalize_booleanity_verifier_group(
1027 &mut self.base.pcs_transcript.fs_transcript,
1028 self.proof_affine_booleanity.take(),
1029 q_md_subclaims.point(),
1030 q_md_subclaims.expected_evaluations(),
1031 &mut bool_group_idx,
1032 affine_bool_verifier_ancillary,
1033 &self.field_cfg,
1034 )?;
1035
1036 let n_fq = self.all_field_cfgs.len().saturating_sub(1);
1042 let mut cpr_eval_points_fq: Vec<Vec<C::Element>> = Vec::with_capacity(n_fq);
1043 let mut cpr_up_evals_fq: Vec<Vec<C::Element>> = Vec::with_capacity(n_fq);
1044 let mut cpr_bit_op_evals_fq: Vec<Vec<C::Element>> = Vec::with_capacity(n_fq);
1045 let mut cpr_down_evals_fq: Vec<Vec<C::Element>> = Vec::with_capacity(n_fq);
1046 for (prime_idx, ((cpr_proof_i, cpr_ancillary_i), md_subclaims_i)) in self
1047 .proof_cpr_fq
1048 .into_iter()
1049 .zip(fq_cpr_ancillaries)
1050 .zip(all_md_subclaims.iter().skip(1))
1051 .enumerate()
1052 {
1053 let family_idx = add!(prime_idx, 1);
1054 let cfg_i = &self.all_field_cfgs[family_idx];
1055 let cpr_subclaim_i = CombinedPolyResolver::finalize_verifier::<U>(
1056 &mut self.base.pcs_transcript.fs_transcript,
1057 cpr_proof_i,
1058 md_subclaims_i.point().to_vec(),
1059 md_subclaims_i.expected_evaluations()[0].clone(),
1060 cpr_ancillary_i,
1061 &self.projected_scalars_f_fq[prime_idx],
1062 family_idx,
1063 cfg_i,
1064 )?;
1065 cpr_eval_points_fq.push(cpr_subclaim_i.evaluation_point);
1066 cpr_up_evals_fq.push(cpr_subclaim_i.up_evals);
1067 cpr_bit_op_evals_fq.push(cpr_subclaim_i.bit_op_evals);
1068 cpr_down_evals_fq.push(cpr_subclaim_i.down_evals);
1069 }
1070
1071 let alpha_prime_f: Option<C::Element> =
1073 (bool_bit_slice_evals.is_some() || affine_bool_bit_slice_evals.is_some()).then(|| {
1074 self.base
1075 .pcs_transcript
1076 .fs_transcript
1077 .get_field_challenge(&self.field_cfg)
1078 });
1079
1080 let cpr_eval_point = cpr_subclaim.evaluation_point;
1081
1082 assert!(
1083 self.proof_lookup_proof.is_none(),
1084 "Arbitrary lookup argument is not supported yet!"
1085 );
1086
1087 Ok(VerifierSumchecked {
1088 base: self.base,
1089 field_cfg: self.field_cfg,
1090 all_field_cfgs: self.all_field_cfgs,
1091 q_star_idx: self.q_star_idx,
1092 projecting_elements: self.projecting_elements,
1093 cpr_eval_point,
1094 cpr_up_evals: cpr_subclaim.up_evals,
1095 cpr_bit_op_evals: cpr_subclaim.bit_op_evals,
1096 cpr_down_evals: cpr_subclaim.down_evals,
1097 cpr_eval_points_fq,
1098 cpr_up_evals_fq,
1099 cpr_bit_op_evals_fq,
1100 cpr_down_evals_fq,
1101 bool_bit_slice_evals,
1102 affine_bool_bit_slice_evals,
1103 alpha_prime_f,
1104 proof_commitments: self.proof_commitments,
1105 proof_multipoint_eval: self.proof_multipoint_eval,
1106 proof_multipoint_evals_fq: self.proof_multipoint_evals_fq,
1107 proof_witness_lifted_evals: self.proof_witness_lifted_evals,
1108 proof_witness_lifted_evals_pp: self.proof_witness_lifted_evals_pp,
1109 proof_lookup_proof: self.proof_lookup_proof,
1110 _phantom: PhantomData,
1111 })
1112 }
1113}
1114
1115impl<'a, Zt, C, IdealOverF, const D: usize, const FD: usize>
1116 VerifierSumchecked<'a, Zt, C, IdealOverF, D, FD>
1117where
1118 Zt: ZincTypes<D, FD>,
1119 C: BaseFieldConfig<Integer = Zt::Fmod>
1120 + ProjectPrimitiveIntegersWithConfig
1121 + Clone
1122 + Send
1123 + Sync
1124 + 'static,
1125 IdealOverF: Ideal,
1126{
1127 #[allow(clippy::arithmetic_side_effects)]
1137 pub fn step5_multipoint_eval<U: Uair>(
1138 mut self,
1139 ) -> Result<VerifierMultipointEvaled<'a, Zt, C, IdealOverF, D, FD>, ProtocolError<C::Element>>
1140 {
1141 let n_fq = self.cpr_eval_points_fq.len();
1143 if self.proof_multipoint_evals_fq.len() != n_fq {
1144 return Err(ProtocolError::FqIdealCheck {
1145 prime_idx: self.proof_multipoint_evals_fq.len(),
1146 q: "<fq mp-eval sub-proof length mismatch>".to_owned(),
1147 source: IdealCheckError::IdealCollectorError(
1148 ideal_check::BatchedIdealCheckError::LengthMismatch {
1149 num_ideals: n_fq,
1150 provided_values: self.proof_multipoint_evals_fq.len(),
1151 },
1152 ),
1153 });
1154 }
1155
1156 let sig = self.base.uair_signature.clone();
1160 let num_pub_bin = sig.public_cols().num_binary_poly_cols();
1161 let num_total_bin = sig.total_cols().num_binary_poly_cols();
1162 let num_wit_bin = num_total_bin.saturating_sub(num_pub_bin);
1163 let num_affine_virtuals = sig.affine_virtual_specs().len();
1164 let alpha_prime = self.alpha_prime_f.as_ref();
1165
1166 let witness_bridge_evals = if num_wit_bin == 0 {
1167 Vec::new()
1168 } else {
1169 collapse_bit_slice_evals::<C, D>(
1170 self.bool_bit_slice_evals
1171 .as_ref()
1172 .ok_or(ProtocolError::BooleanityProofMissing)?,
1173 num_wit_bin,
1174 alpha_prime.ok_or(ProtocolError::BooleanityProofMissing)?,
1175 &self.field_cfg,
1176 )
1177 };
1178
1179 if num_affine_virtuals > 0 {
1180 let alpha_prime = alpha_prime.ok_or(ProtocolError::BooleanityProofMissing)?;
1181 let alpha_powers = powers(&self.field_cfg, alpha_prime, D);
1182 let mut source_bridge_evals: Vec<C::Element> = self
1183 .base
1184 .public_trace
1185 .binary_poly
1186 .iter()
1187 .map(|col| {
1188 project_binary_col_at_field::<C, D>(col, &alpha_powers, &self.field_cfg)
1189 .evaluate(&self.field_cfg, &self.cpr_eval_point)
1190 .map_err(ProtocolError::AffineVirtualSourceProjection)
1191 })
1192 .collect::<Result<_, _>>()?;
1193 debug_assert_eq!(source_bridge_evals.len(), num_pub_bin);
1194 source_bridge_evals.extend(witness_bridge_evals.iter().cloned());
1195
1196 let affine_bridge_evals = collapse_bit_slice_evals::<C, D>(
1197 self.affine_bool_bit_slice_evals
1198 .as_ref()
1199 .ok_or(ProtocolError::BooleanityProofMissing)?,
1200 num_affine_virtuals,
1201 alpha_prime,
1202 &self.field_cfg,
1203 );
1204 let expected_affine_evals = expected_affine_virtual_bridge_evals::<C, _, D>(
1205 &sig,
1206 &source_bridge_evals,
1207 alpha_prime,
1208 &self.field_cfg,
1209 );
1210 for (spec_index, (got, expected)) in affine_bridge_evals
1211 .into_iter()
1212 .zip(expected_affine_evals)
1213 .enumerate()
1214 {
1215 if got != expected {
1216 return Err(ProtocolError::AffineVirtualBridgeMismatch {
1217 spec_index,
1218 got,
1219 expected,
1220 });
1221 }
1222 }
1223 }
1224
1225 let q_up_evals: Vec<C::Element> = self
1226 .cpr_up_evals
1227 .iter()
1228 .cloned()
1229 .chain(witness_bridge_evals)
1230 .collect();
1231
1232 let fq_up_evals_ext: Vec<Vec<C::Element>> = (0..n_fq)
1238 .map(|prime_idx| {
1239 let family_idx = add!(prime_idx, 1);
1240 let zero_i = self.all_field_cfgs[family_idx].zero();
1241 let mut up_i = self.cpr_up_evals_fq[prime_idx].clone();
1242 up_i.extend((0..num_wit_bin).map(|_| zero_i.clone()));
1243 up_i
1244 })
1245 .collect();
1246
1247 let shifts = self.base.uair_signature.shifts();
1248 let num_vars = self.base.num_vars;
1249 let q_star_cfg = self.all_field_cfgs[self.q_star_idx].clone();
1250
1251 let mut all_proofs: Vec<multipoint_eval::Proof<C::Element>> =
1253 Vec::with_capacity(add!(n_fq, 1));
1254 all_proofs.push(self.proof_multipoint_eval);
1255 all_proofs.extend(self.proof_multipoint_evals_fq);
1256
1257 let mut all_families: Vec<MultipointEvalFamilyInputs<'_, C>> =
1258 Vec::with_capacity(add!(n_fq, 1));
1259 all_families.push(MultipointEvalFamilyInputs {
1262 field_cfg: &self.field_cfg,
1263 trace_mles: &[],
1264 bit_op_mles: &[],
1265 eval_point: &self.cpr_eval_point,
1266 up_evals: &q_up_evals,
1267 bit_op_evals: &self.cpr_bit_op_evals,
1268 down_evals: &self.cpr_down_evals,
1269 });
1270
1271 for (prime_idx, up_evals) in fq_up_evals_ext.iter().enumerate() {
1272 let family_idx = add!(prime_idx, 1);
1273 all_families.push(MultipointEvalFamilyInputs {
1274 field_cfg: &self.all_field_cfgs[family_idx],
1275 trace_mles: &[],
1276 bit_op_mles: &[],
1277 eval_point: &self.cpr_eval_points_fq[prime_idx],
1278 up_evals,
1279 bit_op_evals: &self.cpr_bit_op_evals_fq[prime_idx],
1280 down_evals: &self.cpr_down_evals_fq[prime_idx],
1281 });
1282 }
1283
1284 let mut subclaims_iter = MultipointEval::verify_as_subprotocol(
1285 &mut self.base.pcs_transcript.fs_transcript,
1286 all_proofs,
1287 all_families,
1288 shifts,
1289 num_vars,
1290 &q_star_cfg,
1291 )?
1292 .into_iter();
1293
1294 let mp_subclaim = subclaims_iter.next().expect("Q-family subclaim present");
1295 let mp_subclaims_fq: Vec<multipoint_eval::Subclaim<C::Element>> = subclaims_iter.collect();
1296 debug_assert_eq!(mp_subclaims_fq.len(), n_fq);
1297
1298 Ok(VerifierMultipointEvaled {
1299 base: self.base,
1300 field_cfg: self.field_cfg,
1301 all_field_cfgs: self.all_field_cfgs,
1302 projecting_elements: self.projecting_elements,
1303 alpha_prime_f: self.alpha_prime_f,
1304 mp_subclaim,
1305 mp_subclaims_fq,
1306 proof_commitments: self.proof_commitments,
1307 proof_witness_lifted_evals: self.proof_witness_lifted_evals,
1308 proof_witness_lifted_evals_pp: self.proof_witness_lifted_evals_pp,
1309 proof_lookup_proof: self.proof_lookup_proof,
1310 _phantom: PhantomData,
1311 })
1312 }
1313}
1314
1315impl<'a, Zt, C, IdealOverF, const D: usize, const FD: usize>
1316 VerifierMultipointEvaled<'a, Zt, C, IdealOverF, D, FD>
1317where
1318 Zt: ZincTypes<D, FD>,
1319 Zt::Int: ProjectableToField<C>,
1320 <Zt::ArbitraryZt as ZipTypes>::Eval: ProjectableToField<C>,
1321 C: BaseFieldConfig<Integer = Zt::Fmod>
1322 + ProjectPrimitiveIntegersWithConfig
1323 + ProjectElementWithConfig<Zt::Int>
1324 + ProjectElementWithConfig<Zt::Chal>
1325 + Clone
1326 + Send
1327 + Sync
1328 + 'static,
1329 IdealOverF: Ideal,
1330{
1331 #[allow(clippy::arithmetic_side_effects, clippy::too_many_lines)]
1365 pub fn step6_lifted_evals<U: Uair>(
1366 mut self,
1367 ) -> Result<VerifierLiftedEvalsChecked<'a, Zt, C, IdealOverF, D, FD>, ProtocolError<C::Element>>
1368 {
1369 let n_fq = self.mp_subclaims_fq.len();
1370 let n_families = add!(n_fq, 1);
1374
1375 if self.proof_witness_lifted_evals.len() != n_families {
1377 return Err(ProtocolError::FqIdealCheck {
1378 prime_idx: self.proof_witness_lifted_evals.len(),
1379 q: "<witness-lifted-evals family count mismatch>".to_owned(),
1380 source: IdealCheckError::IdealCollectorError(
1381 ideal_check::BatchedIdealCheckError::LengthMismatch {
1382 num_ideals: n_families,
1383 provided_values: self.proof_witness_lifted_evals.len(),
1384 },
1385 ),
1386 });
1387 }
1388
1389 let pub_cols = self.base.uair_signature.public_cols();
1390 let num_pub_bin = pub_cols.num_binary_poly_cols();
1391 let num_pub_arb = pub_cols.num_arbitrary_poly_cols();
1392 let num_pub_int = pub_cols.num_int_cols();
1393
1394 let wit_cols = self.base.uair_signature.witness_cols();
1395 let num_wit_bin = wit_cols.num_binary_poly_cols();
1396 let num_wit_arb = wit_cols.num_arbitrary_poly_cols();
1397 let num_wit_int = wit_cols.num_int_cols();
1398
1399 let num_wit_total = add!(add!(num_wit_bin, num_wit_arb), num_wit_int);
1403
1404 for (family_idx, witness_lifted_i) in self.proof_witness_lifted_evals.iter().enumerate() {
1405 if witness_lifted_i.len() != num_wit_total {
1406 return Err(ProtocolError::WitnessLiftedEvalsLengthMismatch {
1407 family_idx,
1408 got: witness_lifted_i.len(),
1409 expected: num_wit_total,
1410 });
1411 }
1412 }
1413
1414 let expected_pp_len = if n_fq == 0 { 0 } else { num_wit_total };
1415 let actual_pp_len = self
1416 .proof_witness_lifted_evals_pp
1417 .as_ref()
1418 .map(|v| v.len())
1419 .unwrap_or(0);
1420 if actual_pp_len != expected_pp_len {
1421 return Err(ProtocolError::WitnessLiftedEvalsPpLengthMismatch {
1422 got: actual_pp_len,
1423 expected: expected_pp_len,
1424 });
1425 }
1426
1427 let q_pp_cfg = if n_fq == 0 {
1431 self.field_cfg.clone()
1432 } else {
1433 self.base
1434 .pcs_transcript
1435 .fs_transcript
1436 .get_random_field_cfg::<C, Zt::Fmod, Zt::PrimeTest>()
1437 };
1438
1439 let proof_witness_lifted_evals_pp = self
1442 .proof_witness_lifted_evals_pp
1443 .as_ref()
1444 .map(|polys| {
1445 polys
1446 .iter()
1447 .map(|poly| poly.try_map(|int| project_canonical(&q_pp_cfg, int)))
1448 .collect::<Result<Vec<_>, _>>()
1449 })
1450 .transpose()?;
1451 let r_star: Vec<C::Element> = if n_fq == 0 {
1455 self.mp_subclaim.r0.clone()
1456 } else {
1457 self.mp_subclaim
1458 .r0
1459 .iter()
1460 .map(|x| q_pp_cfg.project(&self.field_cfg.lift(x)))
1461 .collect()
1462 };
1463
1464 let assemble_all = |public_lifted: &[DynamicPolynomial<C::Element>],
1467 witness_lifted: &[DynamicPolynomial<C::Element>]|
1468 -> Vec<DynamicPolynomial<C::Element>> {
1469 public_lifted[..num_pub_bin]
1470 .iter()
1471 .chain(&witness_lifted[..num_wit_bin])
1472 .chain(&public_lifted[num_pub_bin..add!(num_pub_bin, num_pub_arb)])
1473 .chain(&witness_lifted[num_wit_bin..add!(num_wit_bin, num_wit_arb)])
1474 .chain(&public_lifted[add!(num_pub_bin, num_pub_arb)..])
1475 .chain(&witness_lifted[add!(num_wit_bin, num_wit_arb)..])
1476 .cloned()
1477 .collect()
1478 };
1479
1480 let recompute_public_lifted =
1483 |family_r0: &[C::Element], family_cfg: &C| -> Vec<DynamicPolynomial<C::Element>> {
1484 if add!(add!(num_pub_bin, num_pub_arb), num_pub_int) == 0 {
1485 return Vec::new();
1486 }
1487 let projected_public = project_trace_coeffs_row_major::<C, Zt::Int, Zt::Int, D, D>(
1488 self.base.public_trace,
1489 family_cfg,
1490 );
1491 compute_lifted_evals::<C, D>(
1492 family_r0,
1493 &self.base.public_trace.binary_poly,
1494 &ProjectedTrace::RowMajor(projected_public),
1495 family_cfg,
1496 )
1497 };
1498
1499 let q_r_0 = self.mp_subclaim.r0.clone();
1501 let q_public_lifted = recompute_public_lifted(&q_r_0, &self.field_cfg);
1502 let q_witness_lifted = self.proof_witness_lifted_evals[0].clone();
1503 let q_all_lifted = assemble_all(&q_public_lifted, &q_witness_lifted);
1504
1505 let q_poly_cfg = self.field_cfg.dyn_poly_cfg();
1506 let mut q_open_evals: Vec<C::Element> = q_all_lifted
1507 .iter()
1508 .map(|bar_u| {
1509 q_poly_cfg
1510 .evaluate_at_point(bar_u, &self.projecting_elements[0])
1511 .map_err(ProtocolError::LiftedEvalProjection)
1512 })
1513 .collect::<Result<Vec<_>, _>>()?;
1514
1515 if let Some(alpha_prime) = &self.alpha_prime_f {
1517 for bar_u in &q_witness_lifted[..num_wit_bin] {
1518 q_open_evals.push(
1519 q_poly_cfg
1520 .evaluate_at_point(bar_u, alpha_prime)
1521 .map_err(ProtocolError::LiftedEvalProjection)?,
1522 );
1523 }
1524 }
1525
1526 MultipointEval::verify_subclaim(
1527 &self.mp_subclaim,
1528 &q_open_evals,
1529 &derive_bit_op_open_evals::<C, D>(
1530 self.base.uair_signature.bit_op_specs(),
1531 &q_all_lifted,
1532 &self.projecting_elements[0],
1533 &self.field_cfg,
1534 )?,
1535 self.base.uair_signature.shifts(),
1536 &self.field_cfg,
1537 )?;
1538
1539 for prime_idx in 0..n_fq {
1541 let family_idx = add!(prime_idx, 1);
1542 let cfg_i = &self.all_field_cfgs[family_idx];
1543 let r_0_i = self.mp_subclaims_fq[prime_idx].r0.clone();
1544 let witness_lifted_i = &self.proof_witness_lifted_evals[family_idx];
1545 let public_lifted_i = recompute_public_lifted(&r_0_i, cfg_i);
1546 let all_lifted_i = assemble_all(&public_lifted_i, witness_lifted_i);
1547
1548 let mut open_evals_i: Vec<C::Element> = all_lifted_i
1549 .iter()
1550 .map(|bar_u| {
1551 cfg_i
1552 .dyn_poly_cfg()
1553 .evaluate_at_point(bar_u, &self.projecting_elements[family_idx])
1554 .map_err(ProtocolError::LiftedEvalProjection)
1555 })
1556 .collect::<Result<Vec<_>, _>>()?;
1557
1558 if self.alpha_prime_f.is_some() {
1561 let zero_i = cfg_i.zero();
1562 open_evals_i.extend((0..num_wit_bin).map(|_| zero_i.clone()));
1563 }
1564
1565 MultipointEval::verify_subclaim(
1566 &self.mp_subclaims_fq[prime_idx],
1567 &open_evals_i,
1568 &derive_bit_op_open_evals::<C, D>(
1569 self.base.uair_signature.bit_op_specs(),
1570 &all_lifted_i,
1571 &self.projecting_elements[family_idx],
1572 cfg_i,
1573 )?,
1574 self.base.uair_signature.shifts(),
1575 cfg_i,
1576 )?;
1577 }
1578
1579 let mut transcription_buf: Vec<u8> = vec![0; C::Integer::NUM_BYTES];
1582 debug_assert_eq!(
1583 self.all_field_cfgs.len(),
1584 self.proof_witness_lifted_evals.len()
1585 );
1586 for (cfg_i, witness_lifted_i) in self
1587 .all_field_cfgs
1588 .iter()
1589 .zip(&self.proof_witness_lifted_evals)
1590 {
1591 for bar_u in witness_lifted_i {
1592 self.base
1593 .pcs_transcript
1594 .fs_transcript
1595 .absorb_field_element_slice(cfg_i, &bar_u.coeffs, &mut transcription_buf);
1596 }
1597 }
1598 if let Some(ref lifted_evals_pp) = proof_witness_lifted_evals_pp {
1599 for bar_u in lifted_evals_pp.iter() {
1600 self.base
1601 .pcs_transcript
1602 .fs_transcript
1603 .absorb_field_element_slice(&q_pp_cfg, &bar_u.coeffs, &mut transcription_buf);
1604 }
1605 }
1606
1607 let lifted_evals_pp = if n_fq == 0 {
1609 self.proof_witness_lifted_evals[0].clone()
1610 } else {
1611 let Some(lifted_evals_pp) = proof_witness_lifted_evals_pp else {
1612 return Err(ProtocolError::WitnessLiftedEvalsPpLengthMismatch {
1613 got: 0,
1614 expected: expected_pp_len,
1615 });
1616 };
1617 lifted_evals_pp
1618 };
1619
1620 Ok(VerifierLiftedEvalsChecked {
1621 base: self.base,
1622 lifted_evals_pp,
1623 q_pp_cfg,
1624 r_star,
1625 proof_commitments: self.proof_commitments,
1626 proof_lookup_proof: self.proof_lookup_proof,
1627 _phantom: PhantomData,
1628 })
1629 }
1630}
1631
1632impl<'a, Zt, C, IdealOverF, const D: usize, const FD: usize>
1633 VerifierLiftedEvalsChecked<'a, Zt, C, IdealOverF, D, FD>
1634where
1635 Zt: ZincTypes<D, FD>,
1636 C: BaseFieldConfig<Integer = Zt::Fmod>
1637 + ProjectPrimitiveIntegersWithConfig
1638 + ProjectElementWithConfig<Zt::CombR>
1639 + ProjectElementWithConfig<Zt::Chal>
1640 + Clone
1641 + Send
1642 + Sync
1643 + 'static,
1644 IdealOverF: Ideal,
1645{
1646 pub fn step7_pcs_verify<U: Uair, const CHECK_FOR_OVERFLOW: bool>(
1659 mut self,
1660 ) -> Result<VerifierPcsVerified<IdealOverF>, ProtocolError<C::Element>> {
1661 let commitments = &self.proof_commitments;
1662
1663 let wit_cols = self.base.uair_signature.witness_cols();
1664 let num_wit_bin = wit_cols.num_binary_poly_cols();
1665 let num_wit_arb = wit_cols.num_arbitrary_poly_cols();
1666
1667 let pcs_transcript = &mut self.base.pcs_transcript;
1668 let lifted_evals_pp = &self.lifted_evals_pp;
1669 let q_pp_cfg = &self.q_pp_cfg;
1670 let r_star = &self.r_star;
1671
1672 let zero = q_pp_cfg.zero();
1673
1674 macro_rules! verify_pcs_batch {
1675 ($Zt:ty, $Lc:ty, $vp:expr, $idx:tt, $pt:expr, [$evals_range:expr]) => {{
1677 verify_pcs_batch!(
1678 $Zt,
1679 $Lc,
1680 $vp,
1681 $idx,
1682 $pt,
1683 [$evals_range],
1684 |bar_u: &DynamicPolynomial<C::Element>, alphas: &[_]| {
1685 let mut eval_j = zero.clone();
1686 for (coeff, alpha) in bar_u.coeffs.iter().zip(alphas.iter()) {
1687 let term = q_pp_cfg.mul(&q_pp_cfg.project(alpha), coeff);
1689 q_pp_cfg.add_assign(&mut eval_j, &term);
1690 }
1691 eval_j
1692 }
1693 )
1694 }};
1695
1696 ($Zt:ty, $Lc:ty, $vp:expr, $idx:tt, $pt:expr, [$evals_range:expr], $compute_eval_j:expr) => {{
1698 let comm = &commitments.$idx;
1699 if comm.batch_size > 0 {
1700 let per_poly_alphas = ZipPlus::<$Zt, $Lc>::sample_alphas(
1701 &mut pcs_transcript.fs_transcript,
1702 comm.batch_size,
1703 );
1704 let mut eval_f = zero.clone();
1705 for (bar_u, alphas) in lifted_evals_pp[$evals_range]
1706 .iter()
1707 .zip(per_poly_alphas.iter())
1708 {
1709 let eval_j = $compute_eval_j(bar_u, alphas);
1710 q_pp_cfg.add_assign(&mut eval_f, &eval_j);
1711 }
1712 ZipPlus::<$Zt, $Lc>::verify_with_alphas::<C, CHECK_FOR_OVERFLOW>(
1713 pcs_transcript,
1714 $vp,
1715 comm,
1716 q_pp_cfg,
1717 $pt,
1718 &eval_f,
1719 &per_poly_alphas,
1720 )
1721 .map_err(|e| ProtocolError::PcsVerification($idx, e))?;
1722 }
1723 }};
1724 }
1725
1726 let num_folding_challenges = Zt::BinaryFold::FOLDING_FACTOR.ilog2();
1730 let folding_challenges = (0..num_folding_challenges)
1731 .map(|_| {
1732 let g_chal: Zt::Chal = pcs_transcript.fs_transcript.get_challenge();
1733 q_pp_cfg.project(&g_chal)
1734 })
1735 .collect_vec();
1736 let mut r_star_ext = r_star.clone();
1737 r_star_ext.extend_from_slice(&folding_challenges);
1738
1739 verify_pcs_batch!(
1743 Zt::BinaryZt,
1744 Zt::BinaryLc,
1745 self.base.vp_bin,
1746 0,
1747 &r_star_ext,
1748 [0..num_wit_bin],
1749 |bar_u: &DynamicPolynomial<C::Element>, alphas: &[_]| {
1750 Zt::BinaryFold::fold_eval_claim(
1752 &bar_u.coeffs,
1753 alphas,
1754 &folding_challenges,
1755 q_pp_cfg,
1756 )
1757 }
1758 );
1759 verify_pcs_batch!(
1760 Zt::ArbitraryZt,
1761 Zt::ArbitraryLc,
1762 self.base.vp_arb,
1763 1,
1764 r_star,
1765 [num_wit_bin..add!(num_wit_bin, num_wit_arb)]
1766 );
1767 verify_pcs_batch!(
1768 Zt::IntZt,
1769 Zt::IntLc,
1770 self.base.vp_int,
1771 2,
1772 r_star,
1773 [add!(num_wit_bin, num_wit_arb)..]
1774 );
1775
1776 Ok(VerifierPcsVerified {
1777 pcs_transcript: self.base.pcs_transcript,
1778 _phantom: PhantomData,
1779 })
1780 }
1781}
1782
1783impl<IdealOverF: Ideal> VerifierPcsVerified<IdealOverF> {
1784 pub fn finish<E: SetElement>(self) -> Result<(), ProtocolError<E>> {
1788 self.pcs_transcript.check_eof()?;
1789 Ok(())
1790 }
1791}
1792
1793fn derive_bit_op_open_evals<C: BaseFieldConfig, const D: usize>(
1794 specs: &[zinc_uair::BitOpSpec],
1795 all_lifted: &[DynamicPolynomial<C::Element>],
1796 projecting_element: &C::Element,
1797 field_cfg: &C,
1798) -> Result<Vec<C::Element>, ProtocolError<C::Element>> {
1799 specs
1800 .iter()
1801 .map(|spec| {
1802 let source = &all_lifted[spec.source_col()];
1803 let transformed = spec.op().transform::<C, D>(source, field_cfg);
1804 field_cfg
1805 .dyn_poly_cfg()
1806 .evaluate_at_point(&transformed, projecting_element)
1807 .map_err(ProtocolError::LiftedEvalProjection)
1808 })
1809 .collect()
1810}
1811
1812impl<Zt, U, C, const D: usize, const FD: usize> ZincPlusPiop<Zt, U, C, D, FD>
1817where
1818 Zt: ZincTypes<D, FD>,
1819 Zt::Int: ProjectableToField<C>,
1820 <Zt::ArbitraryZt as ZipTypes>::Eval: ProjectableToField<C>,
1821 C: BaseFieldConfig<Integer = Zt::Fmod>
1822 + ProjectPrimitiveIntegersWithConfig
1823 + ProjectElementWithConfig<Zt::Int>
1824 + ProjectElementWithConfig<Zt::CombR>
1825 + ProjectElementWithConfig<Zt::Chal>
1826 + Clone
1827 + Send
1828 + Sync
1829 + 'static,
1830 U: Uair<Prime = Zt::Fmod> + 'static,
1831{
1832 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
1839 pub fn verify<IdealOverF, const CHECK_FOR_OVERFLOW: bool>(
1840 vp: &(
1841 ZipPlusParams<Zt::BinaryZt, Zt::BinaryLc>,
1842 ZipPlusParams<Zt::ArbitraryZt, Zt::ArbitraryLc>,
1843 ZipPlusParams<Zt::IntZt, Zt::IntLc>,
1844 ),
1845 proof: Proof<Zt::Fmod>,
1846 public_trace: &UairTrace<Zt::Int, Zt::Int, D, D>,
1847 num_vars: usize,
1848 project_scalar: impl Fn(&U::Scalar, &C) -> DynamicPolynomial<C::Element>,
1849 project_ideal: impl Fn(&IdealOrZero<U::Ideal>, &C) -> IdealOverF,
1850 project_fq_ideal: impl Fn(&IdealOrZero<U::FqIdeal>, &C) -> IdealOverF,
1851 ) -> Result<(), ProtocolError<C::Element>>
1852 where
1853 IdealOverF: Ideal + for<'cfg> IdealCheck<DynamicPolynomialConfig<'cfg, C>>,
1854 {
1855 ZincPlusPiop::<Zt, U, C, D, FD>::step0_reconstruct_transcript::<IdealOverF>(
1856 vp,
1857 proof,
1858 public_trace,
1859 num_vars,
1860 )?
1861 .step1_prime_projection()?
1862 .step2_ideal_check(project_ideal, project_fq_ideal)?
1863 .step3_eval_projection(project_scalar)?
1864 .step4_sumcheck_verify()?
1865 .step5_multipoint_eval::<U>()?
1866 .step6_lifted_evals::<U>()?
1867 .step7_pcs_verify::<U, CHECK_FOR_OVERFLOW>()?
1868 .finish::<C::Element>()
1869 }
1870}
1871
1872#[cfg(test)]
1874pub mod test_helpers {
1875 use super::*;
1876
1877 #[cfg(test)]
1878 impl<'a, Zt: ZincTypes<D, FD>, const D: usize, const FD: usize> VerifierBase<'a, Zt, D, FD> {
1879 #[cfg(test)]
1880 pub fn fs_transcript_mut(&mut self) -> &mut Blake3Transcript {
1881 &mut self.pcs_transcript.fs_transcript
1882 }
1883 }
1884
1885 #[cfg(test)]
1886 impl<'a, Zt, U, C, IdealOverF, const D: usize, const FD: usize>
1887 VerifierEvalProjected<'a, Zt, U, C, IdealOverF, D, FD>
1888 where
1889 Zt: ZincTypes<D, FD>,
1890 C: BaseFieldConfig,
1891 U: Uair,
1892 {
1893 pub fn projecting_element_f(&self) -> &C::Element {
1894 &self.projecting_elements[0]
1895 }
1896
1897 pub fn field_cfg(&self) -> &C {
1898 &self.field_cfg
1899 }
1900
1901 pub fn fs_transcript_mut(&mut self) -> &mut Blake3Transcript {
1902 self.base.fs_transcript_mut()
1903 }
1904
1905 pub fn proof_combined_sumcheck(&self) -> &MultiDegreeSumcheckProof<C::Element> {
1906 &self.proof_combined_sumcheck
1907 }
1908
1909 pub fn ic_subclaim(&self) -> &ideal_check::VerifierSubclaim<C::Element> {
1910 &self.ic_subclaims[0]
1911 }
1912
1913 pub fn proof_cpr(&self) -> &CombinedPolyResolverProof<C::Element> {
1914 &self.proof_cpr
1915 }
1916
1917 pub fn num_vars(&self) -> usize {
1918 self.base.num_vars
1919 }
1920
1921 pub fn uair_signature(&self) -> &UairSignature<Zt::Fmod> {
1922 &self.base.uair_signature
1923 }
1924 }
1925}