1pub mod prover;
21pub mod verifier;
22
23#[cfg(feature = "parallel")]
24use rayon::prelude::*;
25
26use crypto_primitives::{ConstIntRing, ConstIntSemiring, FromWithConfig, PrimeField, Semiring};
27use std::{fmt::Debug, marker::PhantomData};
28use thiserror::Error;
29use zinc_piop::{
30 combined_poly_resolver::{CombinedPolyResolverError, Proof as CombinedPolyResolverProof},
31 ideal_check::{IdealCheckError, Proof as IdealCheckProof},
32 lookup::{BatchedLookupProof, LookupError},
33 multipoint_eval::{MultipointEvalError, Proof as MultipointEvalProof},
34 projections::ProjectedTrace,
35 sumcheck::multi_degree::MultiDegreeSumcheckProof,
36};
37use zinc_poly::{
38 ConstCoeffBitWidth, EvaluationError as PolyEvaluationError,
39 mle::DenseMultilinearExtension,
40 univariate::{
41 binary::BinaryPoly,
42 dense::DensePolynomial,
43 dynamic::over_field::{DynamicPolyVecF, DynamicPolynomialF},
44 },
45};
46use zinc_primality::PrimalityTest;
47use zinc_transcript::traits::{ConstTranscribable, GenTranscribable, Transcribable, Transcript};
48use zinc_uair::{Uair, ideal::Ideal};
49use zinc_utils::{cfg_extend, cfg_into_iter, cfg_iter, named::Named};
50use zip_plus::{
51 ZipError,
52 code::LinearCode,
53 pcs::structs::{ZipPlusCommitment, ZipTypes},
54};
55
56#[derive(Clone, Debug, PartialEq, Eq)]
62pub struct Proof<F: PrimeField> {
63 pub commitments: (ZipPlusCommitment, ZipPlusCommitment, ZipPlusCommitment),
65 pub zip: Vec<u8>,
67 pub ideal_check: IdealCheckProof<F>,
69 pub resolver: CombinedPolyResolverProof<F>,
71 pub combined_sumcheck: MultiDegreeSumcheckProof<F>,
73 pub multipoint_eval: MultipointEvalProof<F>,
76 pub witness_lifted_evals: Vec<DynamicPolynomialF<F>>,
83 pub lookup_proof: Option<BatchedLookupProof<F>>,
85}
86
87impl<F> GenTranscribable for Proof<F>
88where
89 F: PrimeField,
90 F::Inner: ConstTranscribable,
91 F::Modulus: ConstTranscribable,
92{
93 fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
94 let (commit0, bytes) = ZipPlusCommitment::read_transcription_bytes_subset(bytes);
95 let (commit1, bytes) = ZipPlusCommitment::read_transcription_bytes_subset(bytes);
96 let (commit2, bytes) = ZipPlusCommitment::read_transcription_bytes_subset(bytes);
97
98 let (zip_len, bytes) = u32::read_transcription_bytes_subset(bytes);
99 let zip_len = usize::try_from(zip_len).expect("zip length must fit into usize");
100 let (zip_bytes, bytes) = bytes.split_at(zip_len);
101 let zip = zip_bytes.to_vec();
102
103 let (ideal_check, bytes) = IdealCheckProof::<F>::read_transcription_bytes_subset(bytes);
104 let (resolver, bytes) =
105 CombinedPolyResolverProof::<F>::read_transcription_bytes_subset(bytes);
106 let (combined_sumcheck, bytes) =
107 MultiDegreeSumcheckProof::<F>::read_transcription_bytes_subset(bytes);
108 let (multipoint_eval, bytes) =
109 MultipointEvalProof::<F>::read_transcription_bytes_subset(bytes);
110
111 let (witness_vec, bytes) = DynamicPolyVecF::<F>::read_transcription_bytes_subset(bytes);
112 let witness_lifted_evals = witness_vec.0;
113
114 assert!(bytes.is_empty(), "All bytes should be consumed");
117
118 Self {
119 commitments: (commit0, commit1, commit2),
120 zip,
121 ideal_check,
122 resolver,
123 combined_sumcheck,
124 multipoint_eval,
125 witness_lifted_evals,
126 lookup_proof: None,
127 }
128 }
129
130 fn write_transcription_bytes_exact(&self, mut buf: &mut [u8]) {
131 buf = self.commitments.0.write_transcription_bytes_subset(buf);
133 buf = self.commitments.1.write_transcription_bytes_subset(buf);
134 buf = self.commitments.2.write_transcription_bytes_subset(buf);
135
136 let zip_len = u32::try_from(self.zip.len()).expect("zip length must fit into u32");
138 zip_len.write_transcription_bytes_exact(&mut buf[..u32::NUM_BYTES]);
139 buf = &mut buf[u32::NUM_BYTES..];
140 buf[..self.zip.len()].copy_from_slice(&self.zip);
141 buf = &mut buf[self.zip.len()..];
142
143 buf = self.ideal_check.write_transcription_bytes_subset(buf);
145
146 buf = self.resolver.write_transcription_bytes_subset(buf);
148
149 buf = self.combined_sumcheck.write_transcription_bytes_subset(buf);
151
152 buf = self.multipoint_eval.write_transcription_bytes_subset(buf);
154
155 DynamicPolyVecF::reinterpret(&self.witness_lifted_evals)
159 .write_transcription_bytes_subset(buf);
160 }
161}
162
163impl<F> Transcribable for Proof<F>
164where
165 F: PrimeField,
166 F::Inner: ConstTranscribable,
167 F::Modulus: ConstTranscribable,
168{
169 #[allow(clippy::arithmetic_side_effects)]
170 fn get_num_bytes(&self) -> usize {
171 let witness_vec = DynamicPolyVecF::reinterpret(&self.witness_lifted_evals);
172 3 * ZipPlusCommitment::NUM_BYTES
173 + u32::NUM_BYTES
174 + self.zip.len()
175 + IdealCheckProof::<F>::LENGTH_NUM_BYTES
176 + self.ideal_check.get_num_bytes()
177 + CombinedPolyResolverProof::<F>::LENGTH_NUM_BYTES
178 + self.resolver.get_num_bytes()
179 + MultiDegreeSumcheckProof::<F>::LENGTH_NUM_BYTES
180 + self.combined_sumcheck.get_num_bytes()
181 + MultipointEvalProof::<F>::LENGTH_NUM_BYTES
182 + self.multipoint_eval.get_num_bytes()
183 + DynamicPolyVecF::<F>::LENGTH_NUM_BYTES
186 + witness_vec.get_num_bytes()
187 }
188}
189
190pub trait ZincTypes<const DEGREE_PLUS_ONE: usize>: Clone + Debug {
193 type Int: Semiring
196 + ConstTranscribable
197 + ConstCoeffBitWidth
198 + Named
199 + Default
200 + Clone
201 + Send
202 + Sync
203 + 'static;
204
205 type Chal: ConstIntRing + ConstTranscribable + Named;
208
209 type Pt: ConstIntRing;
212
213 type CombR;
214
215 type Fmod: ConstIntSemiring + ConstTranscribable + Named;
218
219 type PrimeTest: PrimalityTest<Self::Fmod>;
221
222 type BinaryZt: ZipTypes<
224 Eval = BinaryPoly<DEGREE_PLUS_ONE>,
225 Chal = Self::Chal,
226 Pt = Self::Pt,
227 CombR = Self::CombR,
228 Fmod = Self::Fmod,
229 PrimeTest = Self::PrimeTest,
230 >;
231
232 type ArbitraryZt: ZipTypes<
234 Eval = DensePolynomial<Self::Int, DEGREE_PLUS_ONE>,
235 Chal = Self::Chal,
236 Pt = Self::Pt,
237 CombR = Self::CombR,
238 Fmod = Self::Fmod,
239 PrimeTest = Self::PrimeTest,
240 >;
241
242 type IntZt: ZipTypes<
244 Eval = Self::Int,
245 Chal = Self::Chal,
246 Pt = Self::Pt,
247 CombR = Self::CombR,
248 Fmod = Self::Fmod,
249 PrimeTest = Self::PrimeTest,
250 >;
251
252 type BinaryLc: LinearCode<Self::BinaryZt>;
254
255 type ArbitraryLc: LinearCode<Self::ArbitraryZt>;
257
258 type IntLc: LinearCode<Self::IntZt>;
260}
261
262#[derive(Copy, Clone, Default, Debug)]
268pub struct ZincPlusPiop<Zt, U, F, const DEGREE_PLUS_ONE: usize>(PhantomData<(Zt, U, F)>)
269where
270 Zt: ZincTypes<DEGREE_PLUS_ONE>,
271 U: Uair,
272 F: PrimeField;
273
274#[derive(Debug, Error)]
277pub enum ProtocolError<F: PrimeField, I: Ideal> {
278 #[error("ideal check failed: {0}")]
279 IdealCheck(#[from] IdealCheckError<F, I>),
280 #[error("combined poly resolver failed: {0}")]
281 Resolver(#[from] CombinedPolyResolverError<F>),
282 #[error("scalar projection failed: {0}")]
283 ScalarProjection(PolyEvaluationError),
284 #[error("multi-point evaluation failed: {0}")]
285 MultipointEval(#[from] MultipointEvalError<F>),
286 #[error("lifted eval psi_a projection failed: {0}")]
287 LiftedEvalProjection(PolyEvaluationError),
288 #[error("lookup argument failed: {0}")]
289 Lookup(#[from] LookupError),
290 #[error("PCS error: {0}")]
291 Pcs(#[from] ZipError),
292 #[error("PCS verification failed at column {0}: {1}")]
293 PcsVerification(usize, ZipError),
294}
295
296fn absorb_public_columns<T: ConstTranscribable>(
306 transcript: &mut impl Transcript,
307 cols: &[DenseMultilinearExtension<T>],
308) {
309 let mut buf = vec![0u8; T::NUM_BYTES];
310 for col in cols {
311 for entry in col.iter() {
312 entry.write_transcription_bytes_exact(&mut buf);
313 transcript.absorb_slice(&buf);
314 }
315 }
316}
317
318#[allow(clippy::arithmetic_side_effects)]
327fn compute_lifted_evals<F: PrimeField, const D: usize>(
328 point: &[F],
329 trace_bin_poly: &[DenseMultilinearExtension<BinaryPoly<D>>],
330 projected_trace: &ProjectedTrace<F>,
331 field_cfg: &F::Config,
332) -> Vec<DynamicPolynomialF<F>> {
333 let eq_table = zinc_poly::utils::build_eq_x_r_vec(point, field_cfg)
334 .expect("compute_lifted_evals: eq table build failed");
335
336 let n_bin = trace_bin_poly.len();
337 let zero = F::zero_with_cfg(field_cfg);
338
339 let mut result: Vec<DynamicPolynomialF<F>> = cfg_iter!(trace_bin_poly)
341 .map(|col| {
342 let mut coeffs = vec![zero.clone(); D];
343 for (b, entry) in col.iter().enumerate() {
344 for (l, coeff) in entry.iter().enumerate() {
345 if coeff.into_inner() {
346 coeffs[l] += &eq_table[b];
347 }
348 }
349 }
350 DynamicPolynomialF::new_trimmed(coeffs)
351 })
352 .collect();
353
354 fn weighted_eq_sum<'a, F2: PrimeField + 'a>(
356 col: impl Iterator<Item = &'a DynamicPolynomialF<F2>> + Clone,
357 eq_table: &[F2],
358 zero: &F2,
359 ) -> DynamicPolynomialF<F2> {
360 let num_coeffs = col.clone().map(|e| e.coeffs.len()).max().unwrap_or(0);
361 let mut coeffs = vec![zero.clone(); num_coeffs];
362 for (b, entry) in col.enumerate() {
363 for (l, coeff) in entry.coeffs.iter().enumerate() {
364 let mut term = eq_table[b].clone();
365 term *= coeff;
366 coeffs[l] += &term;
367 }
368 }
369 DynamicPolynomialF::new_trimmed(coeffs)
370 }
371
372 match projected_trace {
373 ProjectedTrace::RowMajor(t) => {
374 let num_cols = t.first().map(|r| r.len()).unwrap_or(0);
375 cfg_extend!(
376 result,
377 cfg_into_iter!(n_bin..num_cols).map(|col_idx| weighted_eq_sum(
378 t.iter().map(|row| &row[col_idx]),
379 &eq_table,
380 &zero,
381 ))
382 );
383 }
384 ProjectedTrace::ColumnMajor(t) => {
385 cfg_extend!(
386 result,
387 cfg_iter!(t[n_bin..]).map(|col_mle| weighted_eq_sum(
388 col_mle.iter(),
389 &eq_table,
390 &zero,
391 ))
392 );
393 }
394 }
395
396 result
397}
398
399pub fn project_scalar_fn<R, F, const D: usize>(
402 scalar: &DensePolynomial<R, D>,
403 field_cfg: &F::Config,
404) -> DynamicPolynomialF<F>
405where
406 F: PrimeField + for<'a> FromWithConfig<&'a R>,
407{
408 scalar
409 .iter()
410 .map(|coeff| F::from_with_cfg(coeff, field_cfg))
411 .collect()
412}
413
414#[cfg(test)]
419mod tests {
420 use super::*;
421 use crypto_bigint::U64;
422 use crypto_primitives::{
423 Field, crypto_bigint_int::Int, crypto_bigint_monty::MontyField, crypto_bigint_uint::Uint,
424 };
425 use rand::rng;
426 use zinc_piop::{
427 combined_poly_resolver::CombinedPolyResolverError, multipoint_eval::MultipointEvalError,
428 };
429 use zinc_poly::univariate::{binary::BinaryPolyInnerProduct, dense::DensePolyInnerProduct};
430 use zinc_primality::MillerRabin;
431 use zinc_test_uair::{
432 BigLinearUair, BigLinearUairWithPublicInput, BinaryDecompositionUair, GenerateRandomTrace,
433 TestUairMixedShifts, TestUairNoMultiplication, TestUairSimpleMultiplication,
434 };
435 use zinc_uair::{
436 degree_counter::count_max_degree, ideal::DegreeOneIdeal, ideal_collector::IdealOrZero,
437 };
438 use zinc_utils::{
439 CHECKED,
440 from_ref::FromRef,
441 inner_product::{MBSInnerProduct, ScalarProduct},
442 projectable_to_field::ProjectableToField,
443 };
444 use zip_plus::{
445 code::{
446 iprs::{IprsCode, PnttConfigF65537},
447 raa::{RaaCode, RaaConfig},
448 },
449 pcs::structs::{ZipPlus, ZipPlusParams},
450 pcs_transcript::PcsProverTranscript,
451 };
452
453 const INT_LIMBS: usize = U64::LIMBS;
454 const FIELD_LIMBS: usize = U64::LIMBS * 3;
455 const DEGREE_PLUS_ONE: usize = 32;
456
457 const K: usize = INT_LIMBS * 4;
460 const M: usize = INT_LIMBS * 8;
461
462 const REP: usize = 4;
463
464 type F = MontyField<FIELD_LIMBS>;
465
466 #[derive(Debug, Clone)]
467 pub struct BinPolyZipTypes {}
468 impl ZipTypes for BinPolyZipTypes {
469 const NUM_COLUMN_OPENINGS: usize = 147;
470 type Eval = BinaryPoly<DEGREE_PLUS_ONE>;
471 type Cw = DensePolynomial<i64, DEGREE_PLUS_ONE>;
472 type Fmod = Uint<FIELD_LIMBS>;
473 type PrimeTest = MillerRabin;
474 type Chal = i128;
475 type Pt = i128;
476 type CombR = Int<M>;
477 type Comb = DensePolynomial<Self::CombR, DEGREE_PLUS_ONE>;
478 type EvalDotChal = BinaryPolyInnerProduct<Self::Chal, DEGREE_PLUS_ONE>;
479 type CombDotChal = DensePolyInnerProduct<
480 Self::CombR,
481 Self::Chal,
482 Self::CombR,
483 MBSInnerProduct,
484 DEGREE_PLUS_ONE,
485 >;
486 type ArrCombRDotChal = MBSInnerProduct;
487 }
488
489 #[derive(Debug, Clone)]
490 pub struct ArbitraryPolyZipTypesIprs {}
491 impl ZipTypes for ArbitraryPolyZipTypesIprs {
492 const NUM_COLUMN_OPENINGS: usize = 147;
493 type Eval = DensePolynomial<i64, DEGREE_PLUS_ONE>;
494 type Cw = DensePolynomial<i64, DEGREE_PLUS_ONE>;
495 type Fmod = Uint<FIELD_LIMBS>;
496 type PrimeTest = MillerRabin;
497 type Chal = i128;
498 type Pt = i128;
499 type CombR = Int<M>;
500 type Comb = DensePolynomial<Self::CombR, DEGREE_PLUS_ONE>;
501 type EvalDotChal =
502 DensePolyInnerProduct<i64, Self::Chal, Self::CombR, MBSInnerProduct, DEGREE_PLUS_ONE>;
503 type CombDotChal = DensePolyInnerProduct<
504 Self::CombR,
505 Self::Chal,
506 Self::CombR,
507 MBSInnerProduct,
508 DEGREE_PLUS_ONE,
509 >;
510 type ArrCombRDotChal = MBSInnerProduct;
511 }
512
513 #[derive(Debug, Clone)]
516 pub struct ArbitraryPolyZipTypesRaa {}
517 impl ZipTypes for ArbitraryPolyZipTypesRaa {
518 const NUM_COLUMN_OPENINGS: usize = 147;
519 type Eval = DensePolynomial<i64, DEGREE_PLUS_ONE>;
520 type Cw = DensePolynomial<Int<K>, DEGREE_PLUS_ONE>;
521 type Fmod = Uint<FIELD_LIMBS>;
522 type PrimeTest = MillerRabin;
523 type Chal = i128;
524 type Pt = i128;
525 type CombR = Int<M>;
526 type Comb = DensePolynomial<Self::CombR, DEGREE_PLUS_ONE>;
527 type EvalDotChal =
528 DensePolyInnerProduct<i64, Self::Chal, Self::CombR, MBSInnerProduct, DEGREE_PLUS_ONE>;
529 type CombDotChal = DensePolyInnerProduct<
530 Self::CombR,
531 Self::Chal,
532 Self::CombR,
533 MBSInnerProduct,
534 DEGREE_PLUS_ONE,
535 >;
536 type ArrCombRDotChal = MBSInnerProduct;
537 }
538
539 type ZtInt = i64;
540
541 #[derive(Debug, Clone)]
542 pub struct IntZipTypes {}
543 impl ZipTypes for IntZipTypes {
544 const NUM_COLUMN_OPENINGS: usize = 147;
545 type Eval = ZtInt;
546 type Cw = i128;
547 type Fmod = Uint<FIELD_LIMBS>;
548 type PrimeTest = MillerRabin;
549 type Chal = i128;
550 type Pt = i128;
551 type CombR = Int<M>;
552 type Comb = Self::CombR;
553 type EvalDotChal = ScalarProduct;
554 type CombDotChal = ScalarProduct;
555 type ArrCombRDotChal = MBSInnerProduct;
556 }
557
558 #[derive(Clone, Debug)]
559 struct TestZincTypesIprs;
560
561 impl ZincTypes<DEGREE_PLUS_ONE> for TestZincTypesIprs {
562 type Int = ZtInt;
563 type Chal = i128;
564 type Pt = i128;
565 type CombR = Int<M>;
566 type Fmod = Uint<FIELD_LIMBS>;
567 type PrimeTest = MillerRabin;
568
569 type BinaryZt = BinPolyZipTypes;
570 type ArbitraryZt = ArbitraryPolyZipTypesIprs;
571 type IntZt = IntZipTypes;
572
573 type BinaryLc = IprsCode<Self::BinaryZt, PnttConfigF65537, REP, CHECKED>;
574 type ArbitraryLc = IprsCode<Self::ArbitraryZt, PnttConfigF65537, REP, CHECKED>;
575 type IntLc = IprsCode<Self::IntZt, PnttConfigF65537, REP, CHECKED>;
576 }
577
578 #[derive(Copy, Clone)]
579 struct TestRaaConfig;
580 impl RaaConfig for TestRaaConfig {
581 const PERMUTE_IN_PLACE: bool = false;
582 const CHECK_FOR_OVERFLOWS: bool = true;
583 }
584
585 #[derive(Clone, Debug)]
586 struct TestZincTypesRaa;
587
588 impl ZincTypes<DEGREE_PLUS_ONE> for TestZincTypesRaa {
589 type Int = i64;
590 type Chal = i128;
591 type Pt = i128;
592 type CombR = Int<M>;
593 type Fmod = Uint<FIELD_LIMBS>;
594 type PrimeTest = MillerRabin;
595
596 type BinaryZt = BinPolyZipTypes;
597 type ArbitraryZt = ArbitraryPolyZipTypesRaa;
598 type IntZt = IntZipTypes;
599
600 type BinaryLc = RaaCode<Self::BinaryZt, TestRaaConfig, REP>;
601 type ArbitraryLc = RaaCode<Self::ArbitraryZt, TestRaaConfig, REP>;
602 type IntLc = RaaCode<Self::IntZt, TestRaaConfig, REP>;
603 }
604
605 fn make_iprs<Zt: ZipTypes>(num_vars: usize) -> IprsCode<Zt, PnttConfigF65537, REP, CHECKED> {
607 let poly_size = 1 << num_vars;
608 IprsCode::new_with_optimal_depth(poly_size).unwrap()
609 }
610
611 #[allow(clippy::type_complexity)]
613 fn setup_pp<Zt>(
614 num_vars: usize,
615 linear_codes: (Zt::BinaryLc, Zt::ArbitraryLc, Zt::IntLc),
616 ) -> (
617 ZipPlusParams<Zt::BinaryZt, Zt::BinaryLc>,
618 ZipPlusParams<Zt::ArbitraryZt, Zt::ArbitraryLc>,
619 ZipPlusParams<Zt::IntZt, Zt::IntLc>,
620 )
621 where
622 Zt: ZincTypes<DEGREE_PLUS_ONE>,
623 {
624 let poly_size = 1 << num_vars;
625 (
626 ZipPlus::<Zt::BinaryZt, Zt::BinaryLc>::setup(poly_size, linear_codes.0),
627 ZipPlus::<Zt::ArbitraryZt, Zt::ArbitraryLc>::setup(poly_size, linear_codes.1),
628 ZipPlus::<Zt::IntZt, Zt::IntLc>::setup(poly_size, linear_codes.2),
629 )
630 }
631
632 macro_rules! default_project_ideal {
633 () => {
634 |ideal, field_cfg| ideal.map(|i| DegreeOneIdeal::from_with_cfg(i, field_cfg))
635 };
636 }
637
638 #[allow(clippy::result_large_err)]
639 fn do_test<Zt, U>(
640 num_vars: usize,
641 linear_codes: (Zt::BinaryLc, Zt::ArbitraryLc, Zt::IntLc),
642 project_ideal: impl Fn(
643 &IdealOrZero<U::Ideal>,
644 &<F as PrimeField>::Config,
645 ) -> IdealOrZero<DegreeOneIdeal<F>>
646 + Copy,
647 tamper: impl Fn(&mut Proof<F>),
648 check_verification: impl Fn(Result<(), ProtocolError<F, IdealOrZero<DegreeOneIdeal<F>>>>),
649 ) where
650 Zt: ZincTypes<DEGREE_PLUS_ONE>,
651 <Zt::BinaryZt as ZipTypes>::Cw: ProjectableToField<F>,
652 <Zt::ArbitraryZt as ZipTypes>::Eval: ProjectableToField<F>,
653 <Zt::ArbitraryZt as ZipTypes>::Cw: ProjectableToField<F>,
654 <Zt::IntZt as ZipTypes>::Cw: ProjectableToField<F>,
655 U: Uair<Scalar = DensePolynomial<Zt::Int, DEGREE_PLUS_ONE>>
656 + GenerateRandomTrace<DEGREE_PLUS_ONE, PolyCoeff = Zt::Int, Int = Zt::Int>
657 + 'static,
658 F: for<'a> FromWithConfig<&'a Zt::Int>
659 + for<'a> FromWithConfig<&'a Zt::CombR>
660 + for<'a> FromWithConfig<&'a Zt::Chal>
661 + for<'a> FromWithConfig<&'a Zt::Pt>,
662 <F as Field>::Inner: FromRef<Zt::Fmod>,
663 <F as Field>::Modulus: FromRef<Zt::Fmod>,
664 {
665 let mut rng = rng();
666 let pp = setup_pp::<Zt>(num_vars, linear_codes);
667
668 let trace = U::generate_random_trace(num_vars, &mut rng);
669
670 let sig = U::signature();
671 let public_trace = trace.public(&sig);
672
673 macro_rules! run_protocol {
674 ($mle_first:ident) => {
675 let mut proof = ZincPlusPiop::<Zt, U, F, DEGREE_PLUS_ONE>::prove::<
676 { $mle_first },
677 CHECKED,
678 >(&pp, &trace, num_vars, project_scalar_fn)
679 .expect("Prover failed");
680
681 let mut transcript = PcsProverTranscript::new_from_commitments(std::iter::empty());
683 transcript.write(&proof).expect("Failed to serialize proof");
684 let mut transcript = transcript.into_verification_transcript();
685 let proof_2 = transcript
686 .read()
687 .expect("Failed to deserialize proof after serialization");
688 assert_eq!(proof, proof_2);
689
690 tamper(&mut proof);
691
692 let verification_result =
693 ZincPlusPiop::<Zt, U, F, DEGREE_PLUS_ONE>::verify::<_, CHECKED>(
694 &pp,
695 proof,
696 &public_trace,
697 num_vars,
698 project_scalar_fn,
699 project_ideal,
700 );
701 check_verification(verification_result);
702 };
703 }
704
705 run_protocol!(false);
706
707 if count_max_degree::<U>() <= 1 {
708 run_protocol!(true);
710 }
711 }
712
713 #[test]
718 fn test_e2e_no_multiplication() {
719 let num_vars = 8;
720 do_test::<TestZincTypesIprs, TestUairNoMultiplication<ZtInt>>(
721 num_vars,
722 (
723 make_iprs(num_vars),
724 make_iprs(num_vars),
725 make_iprs(num_vars),
726 ),
727 default_project_ideal!(),
728 |_| {},
729 |res| res.unwrap(),
730 );
731 }
732
733 #[test]
745 fn test_e2e_simple_multiplication() {
746 let num_vars = 2;
747 do_test::<TestZincTypesRaa, TestUairSimpleMultiplication<ZtInt>>(
748 num_vars,
749 (
750 RaaCode::new(num_vars),
751 RaaCode::new(num_vars),
752 RaaCode::new(num_vars),
753 ),
754 |_ideal, _field_cfg| IdealOrZero::<DegreeOneIdeal<F>>::zero(),
755 |_| {},
756 |res| res.unwrap(),
757 );
758 }
759
760 #[test]
765 fn test_e2e_mixed_shifts() {
766 let num_vars = 8;
767 do_test::<TestZincTypesIprs, TestUairMixedShifts<ZtInt>>(
768 num_vars,
769 (
770 make_iprs(num_vars),
771 make_iprs(num_vars),
772 make_iprs(num_vars),
773 ),
774 |_ideal, _field_cfg| IdealOrZero::<DegreeOneIdeal<F>>::zero(),
775 |_| {},
776 |res| res.unwrap(),
777 );
778 }
779
780 #[test]
785 fn test_e2e_binary_decomposition() {
786 let num_vars = 8;
787 do_test::<TestZincTypesIprs, BinaryDecompositionUair<ZtInt>>(
788 num_vars,
789 (
790 make_iprs(num_vars),
791 make_iprs(num_vars),
792 make_iprs(num_vars),
793 ),
794 default_project_ideal!(),
795 |_| {},
796 |res| res.unwrap(),
797 );
798 }
799
800 #[test]
808 fn test_e2e_big_linear() {
809 let num_vars = 8;
810 do_test::<TestZincTypesIprs, BigLinearUair<ZtInt>>(
811 num_vars,
812 (
813 make_iprs(num_vars),
814 make_iprs(num_vars),
815 make_iprs(num_vars),
816 ),
817 default_project_ideal!(),
818 |_| {},
819 |res| res.unwrap(),
820 );
821 }
822
823 #[test]
828 fn test_e2e_big_linear_with_public_input() {
829 let num_vars = 8;
830 do_test::<TestZincTypesIprs, BigLinearUairWithPublicInput<ZtInt>>(
831 num_vars,
832 (
833 make_iprs(num_vars),
834 make_iprs(num_vars),
835 make_iprs(num_vars),
836 ),
837 default_project_ideal!(),
838 |_| {},
839 |res| res.unwrap(),
840 );
841 }
842
843 #[test]
849 fn test_big_linear_tamper_lifted_evals() {
850 let num_vars = 8;
851 do_test::<TestZincTypesIprs, BigLinearUairWithPublicInput<ZtInt>>(
852 num_vars,
853 (
854 make_iprs(num_vars),
855 make_iprs(num_vars),
856 make_iprs(num_vars),
857 ),
858 default_project_ideal!(),
859 |proof| proof.witness_lifted_evals.swap(0, 1),
860 |res| {
861 assert!(matches!(
862 res.unwrap_err(),
863 ProtocolError::MultipointEval(MultipointEvalError::ClaimMismatch { .. })
864 ));
865 },
866 );
867 }
868
869 #[test]
870 fn test_big_linear_tamper_up_evals() {
871 let num_vars = 8;
872 do_test::<TestZincTypesIprs, BigLinearUairWithPublicInput<ZtInt>>(
873 num_vars,
874 (
875 make_iprs(num_vars),
876 make_iprs(num_vars),
877 make_iprs(num_vars),
878 ),
879 default_project_ideal!(),
880 |proof| proof.resolver.up_evals.swap(0, 1),
881 |res| {
882 assert!(matches!(
883 res.unwrap_err(),
884 ProtocolError::Resolver(
885 CombinedPolyResolverError::ClaimValueDoesNotMatch { .. }
886 )
887 ));
888 },
889 );
890 }
891
892 #[test]
893 fn test_big_linear_tamper_down_evals() {
894 let num_vars = 8;
895 do_test::<TestZincTypesIprs, BigLinearUairWithPublicInput<ZtInt>>(
896 num_vars,
897 (
898 make_iprs(num_vars),
899 make_iprs(num_vars),
900 make_iprs(num_vars),
901 ),
902 default_project_ideal!(),
903 |proof| proof.resolver.down_evals.swap(0, 1),
904 |res| {
905 assert!(matches!(
906 res.unwrap_err(),
907 ProtocolError::Resolver(
908 CombinedPolyResolverError::ClaimValueDoesNotMatch { .. }
909 )
910 ));
911 },
912 );
913 }
914
915 #[test]
919 fn test_big_linear_tamper_commitment() {
920 let num_vars = 8;
921 do_test::<TestZincTypesIprs, BigLinearUairWithPublicInput<ZtInt>>(
922 num_vars,
923 (
924 make_iprs(num_vars),
925 make_iprs(num_vars),
926 make_iprs(num_vars),
927 ),
928 default_project_ideal!(),
929 |proof| proof.commitments.0.root = Default::default(),
930 |res| {
931 assert!(matches!(res.unwrap_err(), ProtocolError::IdealCheck(..)));
932 },
933 );
934 }
935
936 #[test]
937 fn test_big_linear_tamper_ideal_check() {
938 let num_vars = 8;
939 do_test::<TestZincTypesIprs, BigLinearUairWithPublicInput<ZtInt>>(
940 num_vars,
941 (
942 make_iprs(num_vars),
943 make_iprs(num_vars),
944 make_iprs(num_vars),
945 ),
946 default_project_ideal!(),
947 |proof| proof.ideal_check.combined_mle_values.swap(0, 1),
948 |res| {
949 assert!(matches!(res.unwrap_err(), ProtocolError::IdealCheck(..)));
950 },
951 );
952 }
953}