1pub mod fold;
21pub mod prover;
22pub mod verifier;
23
24#[cfg(feature = "parallel")]
25use rayon::prelude::*;
26
27use crate::fold::FoldTrace;
28use crypto_primitives::{ConstIntRing, ConstIntSemiring, FromWithConfig, PrimeField, Semiring};
29use std::{fmt::Debug, marker::PhantomData};
30use thiserror::Error;
31use zinc_piop::{
32 combined_poly_resolver::{CombinedPolyResolverError, Proof as CombinedPolyResolverProof},
33 ideal_check::{IdealCheckError, Proof as IdealCheckProof},
34 lookup::{
35 BatchedLookupProof, LookupError,
36 booleanity::{BooleanityError, BooleanityProof},
37 },
38 multipoint_eval::{MultipointEvalError, Proof as MultipointEvalProof},
39 projections::ProjectedTrace,
40 sumcheck::multi_degree::MultiDegreeSumcheckProof,
41};
42use zinc_poly::{
43 ConstCoeffBitWidth, EvaluationError as PolyEvaluationError,
44 mle::DenseMultilinearExtension,
45 univariate::{
46 binary::BinaryPoly,
47 dense::DensePolynomial,
48 dynamic::over_field::{DynamicPolyVecF, DynamicPolynomialF},
49 },
50};
51use zinc_primality::PrimalityTest;
52use zinc_transcript::traits::{ConstTranscribable, GenTranscribable, Transcribable, Transcript};
53use zinc_uair::{Uair, ideal::Ideal};
54use zinc_utils::{cfg_extend, cfg_into_iter, cfg_iter, named::Named};
55use zip_plus::{
56 ZipError,
57 code::LinearCode,
58 pcs::structs::{ZipPlusCommitment, ZipTypes},
59};
60
61#[derive(Clone, Debug, PartialEq, Eq)]
67pub struct Proof<F: PrimeField> {
68 pub commitments: (ZipPlusCommitment, ZipPlusCommitment, ZipPlusCommitment),
70 pub zip: Vec<u8>,
72 pub ideal_check: IdealCheckProof<F>,
74 pub resolver: CombinedPolyResolverProof<F>,
76 pub combined_sumcheck: MultiDegreeSumcheckProof<F>,
78 pub multipoint_eval: MultipointEvalProof<F>,
81 pub witness_lifted_evals: Vec<DynamicPolynomialF<F>>,
88 pub lookup_proof: Option<BatchedLookupProof<F>>,
90 pub booleanity_proof: Option<BooleanityProof<F>>,
94}
95
96impl<F> GenTranscribable for Proof<F>
97where
98 F: PrimeField,
99 F::Inner: ConstTranscribable,
100 F::Modulus: ConstTranscribable,
101{
102 fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
103 let (commit0, bytes) = ZipPlusCommitment::read_transcription_bytes_subset(bytes);
104 let (commit1, bytes) = ZipPlusCommitment::read_transcription_bytes_subset(bytes);
105 let (commit2, bytes) = ZipPlusCommitment::read_transcription_bytes_subset(bytes);
106
107 let (zip_len, bytes) = u32::read_transcription_bytes_subset(bytes);
108 let zip_len = usize::try_from(zip_len).expect("zip length must fit into usize");
109 let (zip_bytes, bytes) = bytes.split_at(zip_len);
110 let zip = zip_bytes.to_vec();
111
112 let (ideal_check, bytes) = IdealCheckProof::<F>::read_transcription_bytes_subset(bytes);
113 let (resolver, bytes) =
114 CombinedPolyResolverProof::<F>::read_transcription_bytes_subset(bytes);
115 let (combined_sumcheck, bytes) =
116 MultiDegreeSumcheckProof::<F>::read_transcription_bytes_subset(bytes);
117 let (multipoint_eval, bytes) =
118 MultipointEvalProof::<F>::read_transcription_bytes_subset(bytes);
119
120 let (witness_vec, bytes) = DynamicPolyVecF::<F>::read_transcription_bytes_subset(bytes);
121 let witness_lifted_evals = witness_vec.0;
122
123 let (presence, bytes) = u32::read_transcription_bytes_subset(bytes);
126 let (booleanity_proof, bytes) = if presence != 0 {
127 let (p, rest) = BooleanityProof::<F>::read_transcription_bytes_subset(bytes);
128 (Some(p), rest)
129 } else {
130 (None, bytes)
131 };
132
133 assert!(bytes.is_empty(), "All bytes should be consumed");
136
137 Self {
138 commitments: (commit0, commit1, commit2),
139 zip,
140 ideal_check,
141 resolver,
142 combined_sumcheck,
143 multipoint_eval,
144 witness_lifted_evals,
145 lookup_proof: None,
146 booleanity_proof,
147 }
148 }
149
150 fn write_transcription_bytes_exact(&self, mut buf: &mut [u8]) {
151 buf = self.commitments.0.write_transcription_bytes_subset(buf);
153 buf = self.commitments.1.write_transcription_bytes_subset(buf);
154 buf = self.commitments.2.write_transcription_bytes_subset(buf);
155
156 let zip_len = u32::try_from(self.zip.len()).expect("zip length must fit into u32");
158 zip_len.write_transcription_bytes_exact(&mut buf[..u32::NUM_BYTES]);
159 buf = &mut buf[u32::NUM_BYTES..];
160 buf[..self.zip.len()].copy_from_slice(&self.zip);
161 buf = &mut buf[self.zip.len()..];
162
163 buf = self.ideal_check.write_transcription_bytes_subset(buf);
165
166 buf = self.resolver.write_transcription_bytes_subset(buf);
168
169 buf = self.combined_sumcheck.write_transcription_bytes_subset(buf);
171
172 buf = self.multipoint_eval.write_transcription_bytes_subset(buf);
174
175 buf = DynamicPolyVecF::reinterpret(&self.witness_lifted_evals)
177 .write_transcription_bytes_subset(buf);
178
179 let presence: u32 = if self.booleanity_proof.is_some() {
182 1
183 } else {
184 0
185 };
186 presence.write_transcription_bytes_exact(&mut buf[..u32::NUM_BYTES]);
187 buf = &mut buf[u32::NUM_BYTES..];
188 if let Some(ref bp) = self.booleanity_proof {
189 buf = bp.write_transcription_bytes_subset(buf);
190 }
191
192 let _ = buf;
195 }
196}
197
198impl<F> Transcribable for Proof<F>
199where
200 F: PrimeField,
201 F::Inner: ConstTranscribable,
202 F::Modulus: ConstTranscribable,
203{
204 #[allow(clippy::arithmetic_side_effects)]
205 fn get_num_bytes(&self) -> usize {
206 let witness_vec = DynamicPolyVecF::reinterpret(&self.witness_lifted_evals);
207 let booleanity_bytes = match &self.booleanity_proof {
208 Some(bp) => BooleanityProof::<F>::LENGTH_NUM_BYTES + bp.get_num_bytes(),
209 None => 0,
210 };
211 3 * ZipPlusCommitment::NUM_BYTES
212 + u32::NUM_BYTES
213 + self.zip.len()
214 + IdealCheckProof::<F>::LENGTH_NUM_BYTES
215 + self.ideal_check.get_num_bytes()
216 + CombinedPolyResolverProof::<F>::LENGTH_NUM_BYTES
217 + self.resolver.get_num_bytes()
218 + MultiDegreeSumcheckProof::<F>::LENGTH_NUM_BYTES
219 + self.combined_sumcheck.get_num_bytes()
220 + MultipointEvalProof::<F>::LENGTH_NUM_BYTES
221 + self.multipoint_eval.get_num_bytes()
222 + DynamicPolyVecF::<F>::LENGTH_NUM_BYTES
225 + witness_vec.get_num_bytes()
226 + u32::NUM_BYTES
228 + booleanity_bytes
229 }
230}
231
232pub trait ZincTypes<const DEGREE_PLUS_ONE: usize, const FOLDED_DEG_PLUS_ONE: usize>:
235 Clone + Debug
236{
237 type Int: Semiring
240 + ConstTranscribable
241 + ConstCoeffBitWidth
242 + Named
243 + Default
244 + Clone
245 + Send
246 + Sync
247 + 'static;
248
249 type Chal: ConstIntRing + ConstTranscribable + Named;
252
253 type Pt: ConstIntRing;
256
257 type CombR;
258
259 type Fmod: ConstIntSemiring + ConstTranscribable + Named;
262
263 type PrimeTest: PrimalityTest<Self::Fmod>;
265
266 type BinaryZt: ZipTypes<
268 Eval = BinaryPoly<FOLDED_DEG_PLUS_ONE>,
269 Chal = Self::Chal,
270 Pt = Self::Pt,
271 CombR = Self::CombR,
272 Fmod = Self::Fmod,
273 PrimeTest = Self::PrimeTest,
274 >;
275
276 type ArbitraryZt: ZipTypes<
278 Eval = DensePolynomial<Self::Int, DEGREE_PLUS_ONE>,
279 Chal = Self::Chal,
280 Pt = Self::Pt,
281 CombR = Self::CombR,
282 Fmod = Self::Fmod,
283 PrimeTest = Self::PrimeTest,
284 >;
285
286 type IntZt: ZipTypes<
288 Eval = Self::Int,
289 Chal = Self::Chal,
290 Pt = Self::Pt,
291 CombR = Self::CombR,
292 Fmod = Self::Fmod,
293 PrimeTest = Self::PrimeTest,
294 >;
295
296 type BinaryFold: FoldTrace<BinaryPoly<DEGREE_PLUS_ONE>, BinaryPoly<FOLDED_DEG_PLUS_ONE>>;
297
298 type BinaryLc: LinearCode<Self::BinaryZt>;
300
301 type ArbitraryLc: LinearCode<Self::ArbitraryZt>;
303
304 type IntLc: LinearCode<Self::IntZt>;
306}
307
308#[derive(Copy, Clone, Default, Debug)]
314pub struct ZincPlusPiop<Zt, U, F, const DEGREE_PLUS_ONE: usize, const FOLDED_DEGREE_PLUS_ONE: usize>(
315 PhantomData<(Zt, U, F)>,
316)
317where
318 Zt: ZincTypes<DEGREE_PLUS_ONE, FOLDED_DEGREE_PLUS_ONE>,
319 U: Uair,
320 F: PrimeField;
321
322#[derive(Debug, Error)]
325pub enum ProtocolError<F: PrimeField, I: Ideal> {
326 #[error("ideal check failed: {0}")]
327 IdealCheck(#[from] IdealCheckError<F, I>),
328 #[error("combined poly resolver failed: {0}")]
329 Resolver(#[from] CombinedPolyResolverError<F>),
330 #[error("scalar projection failed: {0}")]
331 ScalarProjection(PolyEvaluationError),
332 #[error("multi-point evaluation failed: {0}")]
333 MultipointEval(#[from] MultipointEvalError<F>),
334 #[error("lifted eval psi_a projection failed: {0}")]
335 LiftedEvalProjection(PolyEvaluationError),
336 #[error("lookup argument failed: {0}")]
337 Lookup(#[from] LookupError),
338 #[error("booleanity argument failed: {0}")]
339 Booleanity(#[from] BooleanityError<F>),
340 #[error("booleanity proof missing from proof object")]
341 BooleanityProofMissing,
342 #[error("PCS error: {0}")]
343 Pcs(#[from] ZipError),
344 #[error("PCS verification failed at column {0}: {1}")]
345 PcsVerification(usize, ZipError),
346}
347
348fn absorb_public_columns<T: ConstTranscribable>(
358 transcript: &mut impl Transcript,
359 cols: &[DenseMultilinearExtension<T>],
360) {
361 let mut buf = vec![0u8; T::NUM_BYTES];
362 for col in cols {
363 for entry in col.iter() {
364 entry.write_transcription_bytes_exact(&mut buf);
365 transcript.absorb_slice(&buf);
366 }
367 }
368}
369
370#[allow(clippy::arithmetic_side_effects)]
379fn compute_lifted_evals<F: PrimeField, const D: usize>(
380 point: &[F],
381 trace_bin_poly: &[DenseMultilinearExtension<BinaryPoly<D>>],
382 projected_trace: &ProjectedTrace<F>,
383 field_cfg: &F::Config,
384) -> Vec<DynamicPolynomialF<F>> {
385 let eq_table = zinc_poly::utils::build_eq_x_r_vec(point, field_cfg)
386 .expect("compute_lifted_evals: eq table build failed");
387
388 let n_bin = trace_bin_poly.len();
389 let zero = F::zero_with_cfg(field_cfg);
390
391 let mut result: Vec<DynamicPolynomialF<F>> = cfg_iter!(trace_bin_poly)
393 .map(|col| {
394 let mut coeffs = vec![zero.clone(); D];
395 for (b, entry) in col.iter().enumerate() {
396 for (l, coeff) in entry.iter().enumerate() {
397 if coeff.into_inner() {
398 coeffs[l] += &eq_table[b];
399 }
400 }
401 }
402 DynamicPolynomialF::new_trimmed(coeffs)
403 })
404 .collect();
405
406 fn weighted_eq_sum<'a, F2: PrimeField + 'a>(
408 col: impl Iterator<Item = &'a DynamicPolynomialF<F2>> + Clone,
409 eq_table: &[F2],
410 zero: &F2,
411 ) -> DynamicPolynomialF<F2> {
412 let num_coeffs = col.clone().map(|e| e.coeffs.len()).max().unwrap_or(0);
413 let mut coeffs = vec![zero.clone(); num_coeffs];
414 for (b, entry) in col.enumerate() {
415 for (l, coeff) in entry.coeffs.iter().enumerate() {
416 let mut term = eq_table[b].clone();
417 term *= coeff;
418 coeffs[l] += &term;
419 }
420 }
421 DynamicPolynomialF::new_trimmed(coeffs)
422 }
423
424 match projected_trace {
425 ProjectedTrace::RowMajor(t) => {
426 let num_cols = t.first().map(|r| r.len()).unwrap_or(0);
427 cfg_extend!(
428 result,
429 cfg_into_iter!(n_bin..num_cols).map(|col_idx| weighted_eq_sum(
430 t.iter().map(|row| &row[col_idx]),
431 &eq_table,
432 &zero,
433 ))
434 );
435 }
436 ProjectedTrace::ColumnMajor(t) => {
437 cfg_extend!(
438 result,
439 cfg_iter!(t[n_bin..]).map(|col_mle| weighted_eq_sum(
440 col_mle.iter(),
441 &eq_table,
442 &zero,
443 ))
444 );
445 }
446 }
447
448 result
449}
450
451pub fn project_scalar_fn<R, F, const D: usize>(
454 scalar: &DensePolynomial<R, D>,
455 field_cfg: &F::Config,
456) -> DynamicPolynomialF<F>
457where
458 F: PrimeField + for<'a> FromWithConfig<&'a R>,
459{
460 scalar
461 .iter()
462 .map(|coeff| F::from_with_cfg(coeff, field_cfg))
463 .collect()
464}
465
466#[cfg(test)]
471#[cfg(not(miri))] #[allow(
473 clippy::arithmetic_side_effects,
474 clippy::result_large_err,
475 clippy::type_complexity
476)]
477mod tests {
478 use super::*;
479 use crate::fold::FoldBinaryTrace4x;
480 use crypto_bigint::U64;
481 use crypto_primitives::{
482 Field, crypto_bigint_int::Int, crypto_bigint_monty::MontyField, crypto_bigint_uint::Uint,
483 };
484 use rand::rng;
485 use zinc_piop::{
486 combined_poly_resolver::CombinedPolyResolverError, multipoint_eval::MultipointEvalError,
487 };
488 use zinc_poly::univariate::{binary::BinaryPolyInnerProduct, dense::DensePolyInnerProduct};
489 use zinc_primality::MillerRabin;
490 use zinc_test_uair::{
491 BigLinearUair, BigLinearUairWithPublicInput, BinaryDecompositionUair, GenerateRandomTrace,
492 ShaProxy, TestUairMixedShifts, TestUairNoMultiplication, TestUairSimpleMultiplication,
493 };
494 use zinc_uair::{ideal::DegreeOneIdeal, ideal_collector::IdealOrZero};
495 use zinc_utils::{
496 CHECKED,
497 from_ref::FromRef,
498 inner_product::{MBSInnerProduct, ScalarProduct},
499 projectable_to_field::ProjectableToField,
500 };
501 use zip_plus::{
502 code::{
503 iprs::{IprsCode, PnttConfigF65537},
504 raa::{RaaCode, RaaConfig},
505 },
506 pcs::structs::{ZipPlus, ZipPlusParams},
507 pcs_transcript::PcsProverTranscript,
508 };
509
510 const INT_LIMBS: usize = U64::LIMBS;
511 const FIELD_LIMBS: usize = U64::LIMBS * 3;
512
513 const D: usize = 32;
514 const HALF_D: usize = D / 2;
515 const QUARTER_D: usize = D / 4;
516
517 const K: usize = INT_LIMBS * 4;
520 const M: usize = INT_LIMBS * 8;
521
522 const REP_FACTOR: usize = 8;
523
524 type F = MontyField<FIELD_LIMBS>;
525
526 #[derive(Debug, Clone)]
527 pub struct BinPolyZipTypes {}
528 impl ZipTypes for BinPolyZipTypes {
529 const NUM_COLUMN_OPENINGS: usize = 100;
530 type Eval = BinaryPoly<QUARTER_D>;
531 type Cw = DensePolynomial<i64, QUARTER_D>;
532 type Fmod = Uint<FIELD_LIMBS>;
533 type PrimeTest = MillerRabin;
534 type Chal = i128;
535 type Pt = i128;
536 type CombR = Int<M>;
537 type Comb = DensePolynomial<Self::CombR, QUARTER_D>;
538 type EvalDotChal = BinaryPolyInnerProduct<Self::Chal, QUARTER_D>;
539 type CombDotChal =
540 DensePolyInnerProduct<Self::CombR, Self::Chal, Self::CombR, MBSInnerProduct, QUARTER_D>;
541 type ArrCombRDotChal = MBSInnerProduct;
542 }
543
544 #[derive(Debug, Clone)]
545 pub struct ArbitraryPolyZipTypesIprs {}
546 impl ZipTypes for ArbitraryPolyZipTypesIprs {
547 const NUM_COLUMN_OPENINGS: usize = 100;
548 type Eval = DensePolynomial<i64, D>;
549 type Cw = DensePolynomial<i64, D>;
550 type Fmod = Uint<FIELD_LIMBS>;
551 type PrimeTest = MillerRabin;
552 type Chal = i128;
553 type Pt = i128;
554 type CombR = Int<M>;
555 type Comb = DensePolynomial<Self::CombR, D>;
556 type EvalDotChal = DensePolyInnerProduct<i64, Self::Chal, Self::CombR, MBSInnerProduct, D>;
557 type CombDotChal =
558 DensePolyInnerProduct<Self::CombR, Self::Chal, Self::CombR, MBSInnerProduct, D>;
559 type ArrCombRDotChal = MBSInnerProduct;
560 }
561
562 #[derive(Debug, Clone)]
565 pub struct ArbitraryPolyZipTypesRaa {}
566 impl ZipTypes for ArbitraryPolyZipTypesRaa {
567 const NUM_COLUMN_OPENINGS: usize = 100;
568 type Eval = DensePolynomial<i64, D>;
569 type Cw = DensePolynomial<Int<K>, D>;
570 type Fmod = Uint<FIELD_LIMBS>;
571 type PrimeTest = MillerRabin;
572 type Chal = i128;
573 type Pt = i128;
574 type CombR = Int<M>;
575 type Comb = DensePolynomial<Self::CombR, D>;
576 type EvalDotChal = DensePolyInnerProduct<i64, Self::Chal, Self::CombR, MBSInnerProduct, D>;
577 type CombDotChal =
578 DensePolyInnerProduct<Self::CombR, Self::Chal, Self::CombR, MBSInnerProduct, D>;
579 type ArrCombRDotChal = MBSInnerProduct;
580 }
581
582 type ZtInt = i64;
583
584 #[derive(Debug, Clone)]
585 pub struct IntZipTypes {}
586 impl ZipTypes for IntZipTypes {
587 const NUM_COLUMN_OPENINGS: usize = 100;
588 type Eval = ZtInt;
589 type Cw = i128;
590 type Fmod = Uint<FIELD_LIMBS>;
591 type PrimeTest = MillerRabin;
592 type Chal = i128;
593 type Pt = i128;
594 type CombR = Int<M>;
595 type Comb = Self::CombR;
596 type EvalDotChal = ScalarProduct;
597 type CombDotChal = ScalarProduct;
598 type ArrCombRDotChal = MBSInnerProduct;
599 }
600
601 #[derive(Clone, Debug)]
602 struct TestZincTypesIprs;
603
604 impl ZincTypes<D, QUARTER_D> for TestZincTypesIprs {
605 type Int = ZtInt;
606 type Chal = i128;
607 type Pt = i128;
608 type CombR = Int<M>;
609 type Fmod = Uint<FIELD_LIMBS>;
610 type PrimeTest = MillerRabin;
611
612 type BinaryZt = BinPolyZipTypes;
613 type ArbitraryZt = ArbitraryPolyZipTypesIprs;
614 type IntZt = IntZipTypes;
615
616 type BinaryFold = FoldBinaryTrace4x<D, HALF_D, QUARTER_D>;
617
618 type BinaryLc = IprsCode<Self::BinaryZt, PnttConfigF65537, REP_FACTOR, CHECKED>;
619 type ArbitraryLc = IprsCode<Self::ArbitraryZt, PnttConfigF65537, REP_FACTOR, CHECKED>;
620 type IntLc = IprsCode<Self::IntZt, PnttConfigF65537, REP_FACTOR, CHECKED>;
621 }
622
623 #[derive(Copy, Clone)]
624 struct TestRaaConfig;
625 impl RaaConfig for TestRaaConfig {
626 const PERMUTE_IN_PLACE: bool = false;
627 const CHECK_FOR_OVERFLOWS: bool = true;
628 }
629
630 #[derive(Clone, Debug)]
631 struct TestZincTypesRaa;
632
633 impl ZincTypes<D, QUARTER_D> for TestZincTypesRaa {
634 type Int = i64;
635 type Chal = i128;
636 type Pt = i128;
637 type CombR = Int<M>;
638 type Fmod = Uint<FIELD_LIMBS>;
639 type PrimeTest = MillerRabin;
640
641 type BinaryZt = BinPolyZipTypes;
642 type ArbitraryZt = ArbitraryPolyZipTypesRaa;
643 type IntZt = IntZipTypes;
644
645 type BinaryFold = FoldBinaryTrace4x<D, HALF_D, QUARTER_D>;
646
647 type BinaryLc = RaaCode<Self::BinaryZt, TestRaaConfig, REP_FACTOR>;
648 type ArbitraryLc = RaaCode<Self::ArbitraryZt, TestRaaConfig, REP_FACTOR>;
649 type IntLc = RaaCode<Self::IntZt, TestRaaConfig, REP_FACTOR>;
650 }
651
652 fn make_iprs<Zt: ZipTypes>(
654 num_vars: usize,
655 ) -> IprsCode<Zt, PnttConfigF65537, REP_FACTOR, CHECKED> {
656 let poly_size = 1 << num_vars;
657 IprsCode::new_with_optimal_depth(poly_size).unwrap()
658 }
659
660 fn setup_pp<Zt>(
662 num_vars: usize,
663 linear_codes: (Zt::BinaryLc, Zt::ArbitraryLc, Zt::IntLc),
664 ) -> (
665 ZipPlusParams<Zt::BinaryZt, Zt::BinaryLc>,
666 ZipPlusParams<Zt::ArbitraryZt, Zt::ArbitraryLc>,
667 ZipPlusParams<Zt::IntZt, Zt::IntLc>,
668 )
669 where
670 Zt: ZincTypes<D, QUARTER_D>,
671 {
672 let folded_num_vars = num_vars + Zt::BinaryFold::FOLDING_FACTOR.ilog2() as usize;
673
674 let poly_size = 1 << num_vars;
675 let folded_poly_size = 1 << folded_num_vars;
676 (
677 ZipPlus::<Zt::BinaryZt, Zt::BinaryLc>::setup(folded_poly_size, linear_codes.0),
678 ZipPlus::<Zt::ArbitraryZt, Zt::ArbitraryLc>::setup(poly_size, linear_codes.1),
679 ZipPlus::<Zt::IntZt, Zt::IntLc>::setup(poly_size, linear_codes.2),
680 )
681 }
682
683 macro_rules! default_project_ideal {
684 () => {
685 |ideal, field_cfg| ideal.map(|i| DegreeOneIdeal::from_with_cfg(i, field_cfg))
686 };
687 }
688
689 fn do_test<Zt, U>(
690 num_vars: usize,
691 linear_codes: (Zt::BinaryLc, Zt::ArbitraryLc, Zt::IntLc),
692 project_ideal: impl Fn(
693 &IdealOrZero<U::Ideal>,
694 &<F as PrimeField>::Config,
695 ) -> IdealOrZero<DegreeOneIdeal<F>>
696 + Copy,
697 tamper: impl Fn(&mut Proof<F>),
698 check_verification: impl Fn(Result<(), ProtocolError<F, IdealOrZero<DegreeOneIdeal<F>>>>),
699 ) where
700 Zt: ZincTypes<D, QUARTER_D>,
701 <Zt::BinaryZt as ZipTypes>::Cw: ProjectableToField<F>,
702 <Zt::ArbitraryZt as ZipTypes>::Eval: ProjectableToField<F>,
703 <Zt::ArbitraryZt as ZipTypes>::Cw: ProjectableToField<F>,
704 <Zt::IntZt as ZipTypes>::Cw: ProjectableToField<F>,
705 U: Uair<Scalar = DensePolynomial<Zt::Int, D>>
706 + GenerateRandomTrace<D, PolyCoeff = Zt::Int, Int = Zt::Int>
707 + 'static,
708 F: for<'a> FromWithConfig<&'a Zt::Int>
709 + for<'a> FromWithConfig<&'a Zt::CombR>
710 + for<'a> FromWithConfig<&'a Zt::Chal>
711 + for<'a> FromWithConfig<&'a Zt::Pt>,
712 <F as Field>::Inner: FromRef<Zt::Fmod>,
713 <F as Field>::Modulus: FromRef<Zt::Fmod>,
714 {
715 let mut rng = rng();
716 let pp = setup_pp::<Zt>(num_vars, linear_codes);
717
718 let trace = U::generate_random_trace(num_vars, &mut rng);
719
720 let sig = U::signature();
721 let public_trace = trace.public(&sig);
722
723 macro_rules! run_protocol {
724 ($mle_first:ident) => {
725 let mut proof = ZincPlusPiop::<Zt, U, F, D, QUARTER_D>::prove::<
726 { $mle_first },
727 CHECKED,
728 >(&pp, &trace, num_vars, project_scalar_fn)
729 .expect("Prover failed");
730
731 let mut transcript = PcsProverTranscript::new_from_commitments(std::iter::empty());
733 transcript.write(&proof).expect("Failed to serialize proof");
734 let mut transcript = transcript.into_verification_transcript();
735 let proof_2 = transcript
736 .read()
737 .expect("Failed to deserialize proof after serialization");
738 assert_eq!(proof, proof_2);
739
740 tamper(&mut proof);
741
742 let verification_result =
743 ZincPlusPiop::<Zt, U, F, D, QUARTER_D>::verify::<_, CHECKED>(
744 &pp,
745 proof,
746 &public_trace,
747 num_vars,
748 project_scalar_fn,
749 project_ideal,
750 );
751 check_verification(verification_result);
752 };
753 }
754
755 run_protocol!(false);
756
757 run_protocol!(true);
758 }
759
760 #[test]
765 fn test_e2e_no_multiplication() {
766 let num_vars = 8;
767 do_test::<TestZincTypesIprs, TestUairNoMultiplication<ZtInt>>(
768 num_vars,
769 (
770 make_iprs(num_vars),
771 make_iprs(num_vars),
772 make_iprs(num_vars),
773 ),
774 default_project_ideal!(),
775 |_| {},
776 |res| res.unwrap(),
777 );
778 }
779
780 #[test]
794 fn test_e2e_simple_multiplication() {
795 let num_vars = 2;
796 do_test::<TestZincTypesRaa, TestUairSimpleMultiplication<ZtInt>>(
797 num_vars,
798 (
799 RaaCode::new(num_vars),
800 RaaCode::new(num_vars),
801 RaaCode::new(num_vars),
802 ),
803 |_ideal, _field_cfg| IdealOrZero::<DegreeOneIdeal<F>>::zero(),
804 |_| {},
805 |res| res.unwrap(),
806 );
807 }
808
809 #[test]
814 fn test_e2e_mixed_shifts() {
815 let num_vars = 8;
816 do_test::<TestZincTypesIprs, TestUairMixedShifts<ZtInt>>(
817 num_vars,
818 (
819 make_iprs(num_vars),
820 make_iprs(num_vars),
821 make_iprs(num_vars),
822 ),
823 |_ideal, _field_cfg| IdealOrZero::<DegreeOneIdeal<F>>::zero(),
824 |_| {},
825 |res| res.unwrap(),
826 );
827 }
828
829 #[test]
834 fn test_e2e_binary_decomposition() {
835 let num_vars = 8;
836 do_test::<TestZincTypesIprs, BinaryDecompositionUair<ZtInt>>(
837 num_vars,
838 (
839 make_iprs(num_vars),
840 make_iprs(num_vars),
841 make_iprs(num_vars),
842 ),
843 default_project_ideal!(),
844 |_| {},
845 |res| res.unwrap(),
846 );
847 }
848
849 #[test]
859 fn test_e2e_big_linear() {
860 let num_vars = 8;
861 do_test::<TestZincTypesIprs, BigLinearUair<ZtInt>>(
862 num_vars,
863 (
864 make_iprs(num_vars),
865 make_iprs(num_vars),
866 make_iprs(num_vars),
867 ),
868 default_project_ideal!(),
869 |_| {},
870 |res| res.unwrap(),
871 );
872 }
873
874 #[test]
879 fn test_e2e_big_linear_with_public_input() {
880 let num_vars = 8;
881 do_test::<TestZincTypesIprs, BigLinearUairWithPublicInput<ZtInt>>(
882 num_vars,
883 (
884 make_iprs(num_vars),
885 make_iprs(num_vars),
886 make_iprs(num_vars),
887 ),
888 default_project_ideal!(),
889 |_| {},
890 |res| res.unwrap(),
891 );
892 }
893
894 #[test]
907 fn test_e2e_sha_proxy() {
908 let num_vars = 8;
909 do_test::<TestZincTypesIprs, ShaProxy<ZtInt>>(
910 num_vars,
911 (
912 make_iprs(num_vars),
913 make_iprs(num_vars),
914 make_iprs(num_vars),
915 ),
916 default_project_ideal!(),
917 |_| {},
918 |res| res.unwrap(),
919 );
920 }
921
922 #[test]
928 fn test_big_linear_tamper_lifted_evals() {
929 let num_vars = 8;
930 do_test::<TestZincTypesIprs, BigLinearUairWithPublicInput<ZtInt>>(
931 num_vars,
932 (
933 make_iprs(num_vars),
934 make_iprs(num_vars),
935 make_iprs(num_vars),
936 ),
937 default_project_ideal!(),
938 |proof| proof.witness_lifted_evals.swap(0, 1),
939 |res| {
940 assert!(matches!(
941 res.unwrap_err(),
942 ProtocolError::MultipointEval(MultipointEvalError::ClaimMismatch { .. })
943 ));
944 },
945 );
946 }
947
948 #[test]
949 fn test_big_linear_tamper_up_evals() {
950 let num_vars = 8;
951 do_test::<TestZincTypesIprs, BigLinearUairWithPublicInput<ZtInt>>(
952 num_vars,
953 (
954 make_iprs(num_vars),
955 make_iprs(num_vars),
956 make_iprs(num_vars),
957 ),
958 default_project_ideal!(),
959 |proof| proof.resolver.up_evals.swap(0, 1),
960 |res| {
961 assert!(matches!(
962 res.unwrap_err(),
963 ProtocolError::Resolver(
964 CombinedPolyResolverError::ClaimValueDoesNotMatch { .. }
965 )
966 ));
967 },
968 );
969 }
970
971 #[test]
972 fn test_big_linear_tamper_down_evals() {
973 let num_vars = 8;
974 do_test::<TestZincTypesIprs, BigLinearUairWithPublicInput<ZtInt>>(
975 num_vars,
976 (
977 make_iprs(num_vars),
978 make_iprs(num_vars),
979 make_iprs(num_vars),
980 ),
981 default_project_ideal!(),
982 |proof| proof.resolver.down_evals.swap(0, 1),
983 |res| {
984 assert!(matches!(
985 res.unwrap_err(),
986 ProtocolError::Resolver(
987 CombinedPolyResolverError::ClaimValueDoesNotMatch { .. }
988 )
989 ));
990 },
991 );
992 }
993
994 #[test]
998 fn test_big_linear_tamper_commitment() {
999 let num_vars = 8;
1000 do_test::<TestZincTypesIprs, BigLinearUairWithPublicInput<ZtInt>>(
1001 num_vars,
1002 (
1003 make_iprs(num_vars),
1004 make_iprs(num_vars),
1005 make_iprs(num_vars),
1006 ),
1007 default_project_ideal!(),
1008 |proof| proof.commitments.0.root = Default::default(),
1009 |res| {
1010 assert!(matches!(res.unwrap_err(), ProtocolError::IdealCheck(..)));
1011 },
1012 );
1013 }
1014
1015 #[test]
1030 fn test_big_linear_tamper_booleanity_evals() {
1031 use zinc_piop::lookup::booleanity::BooleanityError;
1032 let num_vars = 8;
1033 do_test::<TestZincTypesIprs, BigLinearUair<ZtInt>>(
1034 num_vars,
1035 (
1036 make_iprs(num_vars),
1037 make_iprs(num_vars),
1038 make_iprs(num_vars),
1039 ),
1040 default_project_ideal!(),
1041 |proof| {
1042 let bp = proof
1043 .booleanity_proof
1044 .as_mut()
1045 .expect("BigLinearUair has binary-poly witnesses");
1046 let dup = bp.bit_slice_evals[0].clone();
1050 bp.bit_slice_evals[0] += dup;
1051 },
1052 |res| {
1053 assert!(matches!(
1054 res.unwrap_err(),
1055 ProtocolError::Booleanity(BooleanityError::ClaimValueDoesNotMatch { .. })
1056 ));
1057 },
1058 );
1059 }
1060
1061 #[test]
1065 fn test_big_linear_tamper_booleanity_evals_length() {
1066 use zinc_piop::lookup::booleanity::BooleanityError;
1067 let num_vars = 8;
1068 do_test::<TestZincTypesIprs, BigLinearUair<ZtInt>>(
1069 num_vars,
1070 (
1071 make_iprs(num_vars),
1072 make_iprs(num_vars),
1073 make_iprs(num_vars),
1074 ),
1075 default_project_ideal!(),
1076 |proof| {
1077 let bp = proof
1078 .booleanity_proof
1079 .as_mut()
1080 .expect("BigLinearUair has binary-poly witnesses");
1081 bp.bit_slice_evals.pop();
1082 },
1083 |res| {
1084 assert!(matches!(
1085 res.unwrap_err(),
1086 ProtocolError::Booleanity(BooleanityError::WrongBitSliceEvalsNumber { .. })
1087 ));
1088 },
1089 );
1090 }
1091
1092 #[test]
1095 fn test_big_linear_drop_booleanity_proof() {
1096 let num_vars = 8;
1097 do_test::<TestZincTypesIprs, BigLinearUair<ZtInt>>(
1098 num_vars,
1099 (
1100 make_iprs(num_vars),
1101 make_iprs(num_vars),
1102 make_iprs(num_vars),
1103 ),
1104 default_project_ideal!(),
1105 |proof| {
1106 proof.booleanity_proof = None;
1107 },
1108 |res| {
1109 assert!(matches!(
1110 res.unwrap_err(),
1111 ProtocolError::BooleanityProofMissing
1112 ));
1113 },
1114 );
1115 }
1116
1117 #[test]
1118 fn test_big_linear_tamper_ideal_check() {
1119 let num_vars = 8;
1120 do_test::<TestZincTypesIprs, BigLinearUairWithPublicInput<ZtInt>>(
1121 num_vars,
1122 (
1123 make_iprs(num_vars),
1124 make_iprs(num_vars),
1125 make_iprs(num_vars),
1126 ),
1127 default_project_ideal!(),
1128 |proof| proof.ideal_check.combined_mle_values.swap(0, 1),
1129 |res| {
1130 assert!(matches!(res.unwrap_err(), ProtocolError::IdealCheck(..)));
1131 },
1132 );
1133 }
1134}