1use crate::{
2 ZipError,
3 code::LinearCode,
4 pcs::{
5 structs::{ZipPlus, ZipPlusCommitment, ZipPlusParams, ZipTypes},
6 utils::{point_to_tensor, validate_input},
7 },
8 pcs_transcript::PcsVerifierTranscript,
9};
10use crypto_primitives::{FromPrimitiveWithConfig, FromWithConfig};
11use itertools::Itertools;
12use num_traits::{ConstOne, ConstZero, Zero};
13#[cfg(feature = "parallel")]
14use rayon::prelude::*;
15use zinc_poly::Polynomial;
16use zinc_transcript::{
17 Blake3Transcript,
18 traits::{Transcribable, Transcript},
19};
20use zinc_utils::{
21 UNCHECKED, add, cfg_into_iter,
22 from_ref::FromRef,
23 inner_product::{InnerProduct, MBSInnerProduct},
24 mul_by_scalar::MulByScalar,
25};
26
27impl<Zt: ZipTypes, Lc: LinearCode<Zt>> ZipPlus<Zt, Lc> {
28 #[allow(clippy::arithmetic_side_effects, clippy::type_complexity)]
100 pub fn verify<F, const CHECK_FOR_OVERFLOW: bool>(
101 transcript: &mut PcsVerifierTranscript,
102 vp: &ZipPlusParams<Zt, Lc>,
103 comm: &ZipPlusCommitment,
104 field_cfg: &F::Config,
105 point_f: &[F],
106 eval_f: &F,
107 ) -> Result<(), ZipError>
108 where
109 F: FromPrimitiveWithConfig
110 + FromRef<F>
111 + for<'a> FromWithConfig<&'a Zt::CombR>
112 + for<'a> FromWithConfig<&'a Zt::Chal>
113 + for<'a> MulByScalar<&'a F>,
114 F::Inner: Transcribable,
115 F::Modulus: FromRef<Zt::Fmod> + Transcribable,
116 {
117 let per_poly_alphas = Self::sample_alphas(&mut transcript.fs_transcript, comm.batch_size);
118 Self::verify_with_alphas::<F, CHECK_FOR_OVERFLOW>(
119 transcript,
120 vp,
121 comm,
122 field_cfg,
123 point_f,
124 eval_f,
125 &per_poly_alphas,
126 )
127 }
128
129 #[allow(clippy::arithmetic_side_effects, clippy::type_complexity)]
137 pub fn verify_with_alphas<F, const CHECK_FOR_OVERFLOW: bool>(
138 transcript: &mut PcsVerifierTranscript,
139 vp: &ZipPlusParams<Zt, Lc>,
140 comm: &ZipPlusCommitment,
141 field_cfg: &F::Config,
142 point_f: &[F],
143 eval_f: &F,
144 per_poly_alphas: &[Vec<Zt::Chal>],
145 ) -> Result<(), ZipError>
146 where
147 F: FromPrimitiveWithConfig
148 + FromRef<F>
149 + for<'a> FromWithConfig<&'a Zt::CombR>
150 + for<'a> FromWithConfig<&'a Zt::Chal>
151 + for<'a> MulByScalar<&'a F>,
152 F::Inner: Transcribable,
153 F::Modulus: FromRef<Zt::Fmod> + Transcribable,
154 {
155 let batch_size = comm.batch_size;
156 validate_input::<Zt, Lc, _>(
157 "verify",
158 vp.num_vars,
159 vp.linear_code.row_len(),
160 batch_size,
161 &[],
162 &[point_f],
163 )?;
164
165 let num_rows = vp.num_rows;
166 let row_len = vp.linear_code.row_len();
167
168 let (q_0, q_1) = point_to_tensor(vp.num_rows, point_f, field_cfg)?;
171 let zero_f = F::zero_with_cfg(field_cfg);
172
173 let b: Vec<F> = transcript.read_field_elements(num_rows)?;
174
175 if MBSInnerProduct::inner_product::<UNCHECKED>(&q_0, &b, zero_f.clone())? != *eval_f {
177 return Err(ZipError::InvalidPcsOpen(
178 "Evaluation consistency failure".into(),
179 ));
180 }
181
182 let coeffs: Vec<Zt::Chal> = if num_rows == 1 {
183 vec![Zt::Chal::ONE]
184 } else {
185 transcript.fs_transcript.get_challenges(num_rows)
186 };
187
188 let combined_row: Vec<Zt::CombR> = transcript.read_const_many(row_len)?;
189 let encoded_combined_row: Vec<Zt::CombR> = vp.linear_code.encode_wide(&combined_row);
190
191 let lhs = MBSInnerProduct::inner_product_field(&combined_row, &q_1, zero_f.clone())?;
198 let rhs = MBSInnerProduct::inner_product_field(&coeffs, &b, zero_f.clone())?;
199
200 if lhs != rhs {
201 return Err(ZipError::InvalidPcsOpen("Coherence failure".into()));
202 }
203
204 let columns_and_proofs: Vec<_> = (0..Zt::NUM_COLUMN_OPENINGS)
205 .map(|_| -> Result<_, ZipError> {
206 let column_idx = transcript.squeeze_challenge_idx(vp.linear_code.codeword_len());
207 let column_values = transcript.read_const_many(batch_size * vp.num_rows)?;
208 let proof = transcript.read_merkle_proof().map_err(|e| {
209 ZipError::InvalidPcsOpen(format!("Failed to read Merkle a proof: {e}"))
210 })?;
211
212 Ok((column_idx, column_values, proof))
213 })
214 .try_collect()?;
215
216 cfg_into_iter!(columns_and_proofs).try_for_each(
217 |(column_idx, column_values, proof)| -> Result<(), ZipError> {
218 Self::verify_column_testing_batched::<CHECK_FOR_OVERFLOW>(
219 per_poly_alphas,
220 &coeffs,
221 &encoded_combined_row,
222 &column_values,
223 column_idx,
224 vp.num_rows,
225 batch_size,
226 )?;
227
228 proof
229 .verify(&comm.root, &column_values, column_idx)
230 .map_err(|e| {
231 ZipError::InvalidPcsOpen(format!("Column opening verification failed: {e}"))
232 })?;
233
234 Ok(())
235 },
236 )?;
237
238 Ok(())
239 }
240
241 pub fn sample_alphas(
248 transcript: &mut Blake3Transcript,
249 batch_size: usize,
250 ) -> Vec<Vec<Zt::Chal>> {
251 let degree_bound = Zt::Comb::DEGREE_BOUND;
252 (0..batch_size)
253 .map(|_| {
254 if degree_bound.is_zero() {
255 vec![Zt::Chal::ONE]
256 } else {
257 transcript.get_challenges(add!(degree_bound, 1))
258 }
259 })
260 .collect()
261 }
262
263 pub(super) fn verify_column_testing_batched<const CHECK_FOR_OVERFLOW: bool>(
268 per_poly_alphas: &[Vec<Zt::Chal>],
269 coeffs: &[Zt::Chal],
270 encoded_combined_row: &[Zt::CombR],
271 all_column_entries: &[Zt::Cw],
272 column: usize,
273 num_rows: usize,
274 batch_size: usize,
275 ) -> Result<(), ZipError> {
276 #[allow(clippy::arithmetic_side_effects)]
277 let all_column_entries_comb =
278 (0..batch_size).try_fold(Zt::CombR::ZERO, |acc, i| -> Result<_, ZipError> {
279 let column_entries: Vec<_> = all_column_entries[i * num_rows..(i + 1) * num_rows]
280 .iter()
281 .map(Zt::Comb::from_ref)
282 .map(|p| {
283 Zt::CombDotChal::inner_product::<CHECK_FOR_OVERFLOW>(
284 &p,
285 &per_poly_alphas[i],
286 Zt::CombR::ZERO,
287 )
288 })
289 .try_collect()?;
290
291 Ok(acc
292 + Zt::ArrCombRDotChal::inner_product::<CHECK_FOR_OVERFLOW>(
293 &column_entries,
294 coeffs,
295 Zt::CombR::ZERO,
296 )?)
297 })?;
298
299 if all_column_entries_comb != encoded_combined_row[column] {
300 return Err(ZipError::InvalidPcsOpen("Proximity failure".into()));
301 }
302
303 Ok(())
304 }
305}
306
307#[cfg(test)]
308#[allow(
309 clippy::arithmetic_side_effects,
310 clippy::cast_possible_truncation,
311 clippy::cast_possible_wrap
312)]
313mod tests {
314 use crate::{
315 ZipError,
316 code::{LinearCode, iprs::IprsCode},
317 merkle::MerkleTree,
318 pcs::{
319 structs::{ZipPlus, ZipPlusHint, ZipTypes},
320 test_utils::*,
321 },
322 pcs_transcript::{PcsProverTranscript, PcsVerifierTranscript},
323 };
324 use crypto_bigint::U64;
325 use crypto_primitives::{
326 Field, FromWithConfig, IntSemiring, IntoWithConfig, PrimeField, crypto_bigint_int::Int,
327 crypto_bigint_monty::MontyField,
328 };
329 use itertools::Itertools;
330 use num_traits::{ConstOne, ConstZero, Zero};
331 use rand::prelude::*;
332 use std::{mem::size_of, sync::LazyLock};
333 use zinc_poly::{
334 mle::{DenseMultilinearExtension, MultilinearExtensionRand},
335 univariate::binary::BinaryPoly,
336 };
337 use zinc_transcript::traits::{ConstTranscribable, Transcribable, Transcript};
338 use zinc_utils::CHECKED;
339
340 const INT_LIMBS: usize = U64::LIMBS;
341
342 const N: usize = INT_LIMBS;
343 const K: usize = INT_LIMBS * 4;
344 const M: usize = INT_LIMBS * 8;
345 const DEGREE_PLUS_ONE: usize = 3;
346
347 type F = MontyField<K>;
348
349 type Zt = TestZipTypes<N, K, M>;
350 type C = IprsCode<Zt, TestIprsConfig, REP_FACTOR, CHECKED>;
351 static C: LazyLock<C> = LazyLock::new(|| C::new(IPRS_ROW_LEN, IPRS_DEPTH).unwrap());
352
353 type PolyZt = TestBinPolyZipTypes<K, M, DEGREE_PLUS_ONE>;
354 type PolyC = IprsCode<PolyZt, TestIprsConfig, REP_FACTOR, CHECKED>;
355 static POLY_C: LazyLock<PolyC> =
356 LazyLock::new(|| PolyC::new(IPRS_ROW_LEN, IPRS_DEPTH).unwrap());
357
358 type TestZip = ZipPlus<Zt, C>;
359 type TestPolyZip = ZipPlus<PolyZt, PolyC>;
360
361 #[test]
362 fn successful_verification_of_valid_proof() {
363 let num_vars = 10;
364 {
365 let (pp, comm, point_f, eval_f, mut transcript) =
366 setup_full_protocol::<F, N, K, M>(num_vars);
367 let field_cfg = get_field_cfg::<Zt, F>(&mut transcript.fs_transcript);
368
369 let result = TestZip::verify::<_, CHECKED>(
370 &mut transcript,
371 &pp,
372 &comm,
373 &field_cfg,
374 &point_f,
375 &eval_f,
376 );
377 assert!(result.is_ok(), "Verification failed: {result:?}")
378 };
379 {
380 let (pp, comm, point_f, eval_f, mut transcript) =
381 setup_full_protocol_poly::<F, N, K, M, DEGREE_PLUS_ONE>(num_vars);
382 let field_cfg = get_field_cfg::<PolyZt, F>(&mut transcript.fs_transcript);
383
384 let result = TestPolyZip::verify::<_, CHECKED>(
385 &mut transcript,
386 &pp,
387 &comm,
388 &field_cfg,
389 &point_f,
390 &eval_f,
391 );
392
393 assert!(result.is_ok(), "Verification failed: {result:?}");
394 }
395 }
396
397 #[test]
398 fn verification_fails_with_incorrect_evaluation() {
399 let num_vars = 10;
400
401 {
402 let (pp, comm, point_f, eval_f, mut transcript) =
403 setup_full_protocol::<F, N, K, M>(num_vars);
404 let field_cfg = get_field_cfg::<Zt, F>(&mut transcript.fs_transcript);
405 let tampered = eval_f + F::one_with_cfg(&field_cfg);
406
407 let result = TestZip::verify::<_, CHECKED>(
408 &mut transcript,
409 &pp,
410 &comm,
411 &field_cfg,
412 &point_f,
413 &tampered,
414 );
415
416 assert!(result.is_err());
417 }
418
419 {
420 let (pp, comm, point_f, eval_f, mut transcript) =
421 setup_full_protocol_poly::<F, N, K, M, DEGREE_PLUS_ONE>(num_vars);
422 let field_cfg = get_field_cfg::<PolyZt, F>(&mut transcript.fs_transcript);
423 let tampered = eval_f + F::one_with_cfg(&field_cfg);
424
425 let result = TestPolyZip::verify::<_, CHECKED>(
426 &mut transcript,
427 &pp,
428 &comm,
429 &field_cfg,
430 &point_f,
431 &tampered,
432 );
433
434 assert!(result.is_err());
435 }
436 }
437
438 #[test]
439 fn verification_fails_with_tampered_proof() {
440 fn tamper(mut proof: PcsVerifierTranscript) -> PcsVerifierTranscript {
441 let original_f0: F = proof.clone().read_field_elements(1).unwrap().remove(0);
442 type Mod = <F as Field>::Modulus;
445 let offset = Mod::LENGTH_NUM_BYTES + Mod::NUM_BYTES;
446 proof.stream.get_mut()[offset] ^= 0x01;
447
448 let tampered_f0: F = proof.clone().read_field_elements(1).unwrap().remove(0);
450 assert_eq!(original_f0.modulus(), tampered_f0.modulus());
451 assert_ne!(original_f0, tampered_f0);
452
453 proof
454 }
455 let num_vars = 10;
456
457 {
458 let (pp, comm, point_f, eval_f, proof) = setup_full_protocol::<F, N, K, M>(num_vars);
459 let mut tampered = tamper(proof);
460 let field_cfg = get_field_cfg::<Zt, F>(&mut tampered.fs_transcript);
461 let result = TestZip::verify::<_, CHECKED>(
462 &mut tampered,
463 &pp,
464 &comm,
465 &field_cfg,
466 &point_f,
467 &eval_f,
468 );
469 assert!(result.is_err());
470 }
471
472 {
473 let (pp, comm, point_f, eval_f, proof) =
474 setup_full_protocol_poly::<F, N, K, M, DEGREE_PLUS_ONE>(num_vars);
475 let mut tampered = tamper(proof);
476 let field_cfg = get_field_cfg::<PolyZt, F>(&mut tampered.fs_transcript);
477 let result = TestPolyZip::verify::<_, CHECKED>(
478 &mut tampered,
479 &pp,
480 &comm,
481 &field_cfg,
482 &point_f,
483 &eval_f,
484 );
485 assert!(result.is_err());
486 }
487 }
488
489 #[test]
490 fn verification_fails_with_wrong_commitment() {
491 let num_vars = 10;
492 {
493 let (pp, _comm_poly1, point_f, eval_f, mut transcript) =
494 setup_full_protocol::<F, N, K, M>(num_vars);
495 let field_cfg = get_field_cfg::<Zt, F>(&mut transcript.fs_transcript);
496
497 let poly2: DenseMultilinearExtension<_> =
498 (20..(20 + (1 << num_vars))).map(Int::from).collect();
499
500 let (_, comm_poly2) = TestZip::commit_single(&pp, &poly2).unwrap();
501
502 let result = TestZip::verify::<_, CHECKED>(
503 &mut transcript,
504 &pp,
505 &comm_poly2,
506 &field_cfg,
507 &point_f,
508 &eval_f,
509 );
510
511 assert!(result.is_err());
512 }
513
514 {
515 let (pp, _comm_poly1, point_f, eval_f, mut transcript) =
516 setup_full_protocol_poly::<F, N, K, M, DEGREE_PLUS_ONE>(num_vars);
517 let field_cfg = get_field_cfg::<PolyZt, F>(&mut transcript.fs_transcript);
518
519 let different_evals = {
520 let different_eval_coeffs: Vec<_> = (1..=((1 << num_vars) * (DEGREE_PLUS_ONE - 1)))
521 .map(|x| (x % 3 == 0).into())
522 .collect_vec();
523 different_eval_coeffs
524 .chunks_exact(DEGREE_PLUS_ONE - 1)
525 .map(BinaryPoly::new)
526 .collect_vec()
527 };
528
529 let poly2 = DenseMultilinearExtension::from_evaluations_vec(
530 num_vars,
531 different_evals,
532 Zero::zero(),
533 );
534 let (_, comm_poly2) = TestPolyZip::commit_single(&pp, &poly2).unwrap();
535
536 let result = TestPolyZip::verify::<_, CHECKED>(
537 &mut transcript,
538 &pp,
539 &comm_poly2,
540 &field_cfg,
541 &point_f,
542 &eval_f,
543 );
544
545 assert!(result.is_err());
546 }
547 }
548
549 #[test]
550 fn verification_fails_with_invalid_point_size() {
551 let num_vars = 10;
552
553 let make_invalid_point = |cfg: &<F as PrimeField>::Config| {
554 let mut invalid_point = vec![];
555 for i in 0..=num_vars {
556 invalid_point.push(F::from_with_cfg(100 + i as i32, cfg));
557 }
558 invalid_point
559 };
560
561 {
562 let (pp, comm, _point_f, eval_f, mut transcript) =
563 setup_full_protocol_poly::<F, N, K, M, DEGREE_PLUS_ONE>(num_vars);
564 let field_cfg = get_field_cfg::<PolyZt, F>(&mut transcript.fs_transcript);
565 let invalid_point = make_invalid_point(eval_f.cfg());
566
567 let result = TestPolyZip::verify::<_, CHECKED>(
568 &mut transcript,
569 &pp,
570 &comm,
571 &field_cfg,
572 &invalid_point,
573 &eval_f,
574 );
575
576 assert!(matches!(result, Err(..)));
577 }
578
579 {
580 let (pp, comm, _point_f, eval_f, mut transcript) =
581 setup_full_protocol::<F, N, K, M>(num_vars);
582 let field_cfg = get_field_cfg::<Zt, F>(&mut transcript.fs_transcript);
583 let invalid_point = make_invalid_point(eval_f.cfg());
584
585 let result = TestZip::verify::<_, CHECKED>(
586 &mut transcript,
587 &pp,
588 &comm,
589 &field_cfg,
590 &invalid_point,
591 &eval_f,
592 );
593
594 assert!(matches!(result, Err(..)));
595 }
596 }
597
598 #[test]
599 fn verification_fails_due_to_incorrect_polynomial() {
600 let num_vars = 10;
601 let (pp, mle1) = setup_test_params(num_vars);
602 let poly_size = 1 << num_vars;
603
604 let (hint, comm) = TestZip::commit_single(&pp, &mle1).unwrap();
605
606 let mle2: DenseMultilinearExtension<_> = (20..20 + poly_size).map(Int::from).collect();
607
608 let point: Vec<<Zt as ZipTypes>::Pt> =
609 (0..num_vars).map(|i| Int::from(i as i32 + 2)).collect();
610
611 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
612 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
613
614 let _eval_f = TestZip::prove_single::<F, CHECKED>(
615 &mut prover_transcript,
616 &pp,
617 &mle2,
618 &point,
619 &hint,
620 &field_cfg,
621 )
622 .unwrap();
623
624 let eval_mle1 = mle1
625 .evaluate(&point, Zero::zero())
626 .expect("Failed to evaluate polynomial");
627
628 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
629 let eval_mle1_f = eval_mle1.into_with_cfg(&field_cfg);
630
631 let mut verifier_transcript = prover_transcript.into_verification_transcript();
632 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
633 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
634
635 let verification_result = TestZip::verify::<_, CHECKED>(
636 &mut verifier_transcript,
637 &pp,
638 &comm,
639 &field_cfg,
640 &point_f,
641 &eval_mle1_f,
642 );
643 assert!(verification_result.is_err());
644 }
645
646 #[test]
647 fn verification_fails_due_to_a_hint_that_is_not_close() {
648 let num_vars = 10;
649 let (pp, mle) = setup_test_params(num_vars);
650
651 let (original_hint, comm) = TestZip::commit_single(&pp, &mle).unwrap();
652
653 let mut corrupted_data = original_hint.cw_matrices[0].clone();
654 {
655 let mut corrupted_rows = corrupted_data.to_rows_slices_mut();
656 let codeword_len = pp.linear_code.codeword_len();
657 let corruption_count = codeword_len / 2 + 1;
658 for i in corrupted_rows[0].iter_mut().take(corruption_count) {
659 *i += Int::ONE;
660 }
661 }
662
663 let corrupted_merkle_tree = MerkleTree::new(&corrupted_data.to_rows_slices());
664 let corrupted_hint = ZipPlusHint::new(vec![corrupted_data], corrupted_merkle_tree);
665
666 let point: Vec<<Zt as ZipTypes>::Pt> =
667 (0..num_vars).map(|i| Int::from(i as i32 + 2)).collect();
668
669 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
670 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
671
672 let eval_f = TestZip::prove_single::<F, CHECKED>(
673 &mut prover_transcript,
674 &pp,
675 &mle,
676 &point,
677 &corrupted_hint,
678 &field_cfg,
679 )
680 .unwrap();
681
682 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
683
684 let mut verifier_transcript = prover_transcript.into_verification_transcript();
685 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
686 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
687
688 let verification_result = TestZip::verify::<_, CHECKED>(
689 &mut verifier_transcript,
690 &pp,
691 &comm,
692 &field_cfg,
693 &point_f,
694 &eval_f,
695 );
696
697 assert!(verification_result.is_err());
698 }
699
700 #[test]
701 fn verification_fails_due_to_incorrect_evaluation() {
702 let num_vars = 10;
703 let (pp, mle) = setup_test_params(num_vars);
704
705 let (hint, comm) = TestZip::commit_single(&pp, &mle).unwrap();
706
707 let point: Vec<<Zt as ZipTypes>::Pt> =
708 (0..num_vars).map(|i| Int::from(i as i32 + 2)).collect();
709
710 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
711 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
712
713 let eval_f = TestZip::prove_single::<F, CHECKED>(
714 &mut prover_transcript,
715 &pp,
716 &mle,
717 &point,
718 &hint,
719 &field_cfg,
720 )
721 .unwrap();
722
723 let incorrect_eval_f = eval_f + F::one_with_cfg(&field_cfg);
724 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
725
726 let mut verifier_transcript = prover_transcript.into_verification_transcript();
727 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
728 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
729
730 let verification_result = TestZip::verify::<_, CHECKED>(
731 &mut verifier_transcript,
732 &pp,
733 &comm,
734 &field_cfg,
735 &point_f,
736 &incorrect_eval_f, );
738
739 assert!(verification_result.is_err());
740 }
741
742 #[test]
743 fn verification_fails_if_proximity_check_is_invalid() {
744 let num_vars = 10;
745 let poly_size: usize = 1 << num_vars;
746
747 let pp = TestZip::setup(poly_size, C.clone());
748
749 let mle: DenseMultilinearExtension<_> = (0..poly_size as i32)
750 .map(<Zt as ZipTypes>::Eval::from)
751 .collect();
752
753 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
754
755 let point = vec![ConstOne::ONE; num_vars];
756
757 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
758 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
759
760 let eval_f = TestZip::prove_single::<F, CHECKED>(
761 &mut prover_transcript,
762 &pp,
763 &mle,
764 &point,
765 &hint,
766 &field_cfg,
767 )
768 .unwrap();
769
770 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
771
772 let row_len = pp.linear_code.row_len();
776 let num_bytes_f = eval_f.inner().get_num_bytes();
777 let b_section_size = 1 + pp.num_rows * 2 * num_bytes_f;
778 let bytes_per_comb_r = M * size_of::<crypto_bigint::Word>();
779 let combined_row_size = row_len * bytes_per_comb_r;
780 let column_values_start = b_section_size + combined_row_size;
781 let bytes_per_cw = K * size_of::<crypto_bigint::Word>();
782
783 let mut verifier_transcript = prover_transcript.into_verification_transcript();
784 assert!(
785 column_values_start + bytes_per_cw <= verifier_transcript.stream.get_ref().len(),
786 "proof too small to tamper column values"
787 );
788
789 let flip_at = column_values_start + bytes_per_cw / 2;
790 verifier_transcript.stream.get_mut()[flip_at] ^= 0x01;
791
792 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
793 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
794
795 let res = TestZip::verify::<_, CHECKED>(
796 &mut verifier_transcript,
797 &pp,
798 &comm,
799 &field_cfg,
800 &point_f,
801 &eval_f,
802 );
803
804 match res {
805 Err(ZipError::InvalidPcsOpen(msg)) => {
806 assert_eq!(msg, "Proximity failure");
807 }
808 Ok(()) => panic!("verification unexpectedly succeeded"),
809 Err(e) => panic!("unexpected error: {e:?}"),
810 }
811 }
812
813 #[test]
814 fn verification_fails_if_evaluation_consistency_check_is_invalid() {
815 let num_vars = 10;
816 let poly_size: usize = 1 << num_vars;
817 let pp = TestZip::setup(poly_size, C.clone());
818
819 let mle: DenseMultilinearExtension<_> =
820 (0..poly_size as i32).map(Int::<INT_LIMBS>::from).collect();
821
822 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
823
824 let point: Vec<<Zt as ZipTypes>::Pt> = vec![Zero::zero(); num_vars];
825
826 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
827 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
828
829 let eval_f = TestZip::prove_single::<F, CHECKED>(
830 &mut prover_transcript,
831 &pp,
832 &mle,
833 &point,
834 &hint,
835 &field_cfg,
836 )
837 .unwrap();
838
839 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
840
841 let num_bytes_f_mod = eval_f.modulus().get_num_bytes();
845 let num_bytes_f_val = eval_f.inner().get_num_bytes();
846 let flip_at = 1 + num_bytes_f_mod + num_bytes_f_val / 4;
847
848 let mut verifier_transcript = prover_transcript.into_verification_transcript();
849 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
850 get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
851 assert!(
852 flip_at < verifier_transcript.stream.get_ref().len(),
853 "proof too small to tamper b section"
854 );
855 verifier_transcript.stream.get_mut()[flip_at] ^= 0x01;
856
857 let res = TestZip::verify::<_, CHECKED>(
858 &mut verifier_transcript,
859 &pp,
860 &comm,
861 &field_cfg,
862 &point_f,
863 &eval_f,
864 );
865
866 match res {
867 Err(ZipError::InvalidPcsOpen(msg)) => {
868 assert_eq!(msg, "Evaluation consistency failure");
869 }
870 Ok(()) => panic!("verification unexpectedly succeeded"),
871 Err(e) => panic!("unexpected error: {e:?}"),
872 }
873 }
874
875 #[test]
876 fn verification_succeeds_for_zero_polynomial() {
877 let num_vars = 10;
878 let poly_size: usize = 1 << num_vars;
879 let pp = TestZip::setup(poly_size, C.clone());
880
881 let mle: DenseMultilinearExtension<_> = vec![Zero::zero(); poly_size].into_iter().collect();
882
883 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
884
885 let point: Vec<<Zt as ZipTypes>::Pt> = vec![Zero::zero(); num_vars];
886
887 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
888 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
889
890 let eval_f = TestZip::prove_single::<F, CHECKED>(
891 &mut prover_transcript,
892 &pp,
893 &mle,
894 &point,
895 &hint,
896 &field_cfg,
897 )
898 .unwrap();
899
900 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
901
902 let mut verifier_transcript = prover_transcript.into_verification_transcript();
903 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
904 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
905
906 let res = TestZip::verify::<_, CHECKED>(
907 &mut verifier_transcript,
908 &pp,
909 &comm,
910 &field_cfg,
911 &point_f,
912 &eval_f,
913 );
914 assert!(res.is_ok());
915 }
916
917 #[test]
918 fn verification_succeeds_at_zero_point() {
919 let num_vars = 10;
920 let poly_size: usize = 1 << num_vars;
921 let pp = TestZip::setup(poly_size, C.clone());
922
923 let mle: DenseMultilinearExtension<_> =
924 (1..=poly_size as i32).map(Int::<INT_LIMBS>::from).collect();
925
926 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
927
928 let point: Vec<<Zt as ZipTypes>::Pt> = vec![Zero::zero(); num_vars];
929
930 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
931 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
932
933 let eval_f = TestZip::prove_single::<F, CHECKED>(
934 &mut prover_transcript,
935 &pp,
936 &mle,
937 &point,
938 &hint,
939 &field_cfg,
940 )
941 .unwrap();
942
943 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
944
945 let mut verifier_transcript = prover_transcript.into_verification_transcript();
946 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
947 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
948
949 let res = TestZip::verify::<_, CHECKED>(
950 &mut verifier_transcript,
951 &pp,
952 &comm,
953 &field_cfg,
954 &point_f,
955 &eval_f,
956 );
957 assert!(res.is_ok());
958 }
959
960 #[test]
961 fn verification_succeeds_when_polynomial_coefficients_are_max_bit_size() {
962 let num_vars = 10;
963 let (pp, _) = setup_test_params(num_vars);
964
965 let mut evals: Vec<<Zt as ZipTypes>::Eval> =
966 (0..1 << num_vars as i32).map(Int::from).collect();
967 evals[1] = Int::from(i64::MAX);
968 let poly = DenseMultilinearExtension::from_evaluations_vec(num_vars, evals, Zero::zero());
969
970 let (hint, comm) = TestZip::commit_single(&pp, &poly).unwrap();
971
972 let mut point = vec![<Zt as ZipTypes>::Pt::ZERO; num_vars];
973 point[0] = <Zt as ZipTypes>::Pt::ONE;
974
975 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
976 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
977
978 let eval_f = TestZip::prove_single::<F, CHECKED>(
979 &mut prover_transcript,
980 &pp,
981 &poly,
982 &point,
983 &hint,
984 &field_cfg,
985 )
986 .unwrap();
987
988 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
989
990 let mut verifier_transcript = prover_transcript.into_verification_transcript();
991 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
992 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
993
994 let verification_result = TestZip::verify::<_, CHECKED>(
995 &mut verifier_transcript,
996 &pp,
997 &comm,
998 &field_cfg,
999 &point_f,
1000 &eval_f,
1001 );
1002
1003 assert!(
1004 verification_result.is_ok(),
1005 "Verification failed: {verification_result:?}",
1006 );
1007 }
1008
1009 #[test]
1010 fn verification_succeeds_with_minimal_polynomial_size_mu_is_8() {
1011 let num_vars = 10;
1012 let (pp, poly) = setup_test_params(num_vars);
1013
1014 let (hint, comm) = TestZip::commit_single(&pp, &poly).unwrap();
1015
1016 let point: Vec<<Zt as ZipTypes>::Pt> = (1..=num_vars as i32).map(Int::from).collect_vec();
1017
1018 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1019 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
1020
1021 let eval_f = TestZip::prove_single::<F, CHECKED>(
1022 &mut prover_transcript,
1023 &pp,
1024 &poly,
1025 &point,
1026 &hint,
1027 &field_cfg,
1028 )
1029 .unwrap();
1030
1031 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1032
1033 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1034 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
1035 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
1036
1037 let verification_result = TestZip::verify::<_, CHECKED>(
1038 &mut verifier_transcript,
1039 &pp,
1040 &comm,
1041 &field_cfg,
1042 &point_f,
1043 &eval_f,
1044 );
1045
1046 assert!(verification_result.is_ok());
1047 }
1048
1049 #[test]
1050 fn verification_succeeds_for_code_row_length_of_1() {
1051 let num_vars = 8;
1052 macro_rules! make_code {
1053 () => {
1054 IprsCode::new(1, 0).unwrap()
1055 };
1056 }
1057 {
1058 let (pp, comm, point_f, eval_f, mut transcript) =
1059 setup_full_protocol_inner::<Zt, C, F, N>(
1060 num_vars,
1061 |num_vars| {
1062 setup_test_params_inner(num_vars, make_code!(), |poly_size| {
1063 (1..=poly_size as i32).map(Int::from).collect()
1064 })
1065 },
1066 || (0..num_vars).map(|i| Int::from(i as i32 + 2)).collect(),
1067 );
1068 let field_cfg = get_field_cfg::<Zt, F>(&mut transcript.fs_transcript);
1069
1070 let result = TestZip::verify::<_, CHECKED>(
1071 &mut transcript,
1072 &pp,
1073 &comm,
1074 &field_cfg,
1075 &point_f,
1076 &eval_f,
1077 );
1078 assert!(result.is_ok(), "Verification failed: {result:?}")
1079 };
1080 {
1081 let (pp, comm, point_f, eval_f, mut transcript) =
1082 setup_full_protocol_inner::<PolyZt, PolyC, F, N>(
1083 num_vars,
1084 |num_vars| {
1085 setup_test_params_inner(num_vars, make_code!(), |poly_size| {
1086 let degree = DEGREE_PLUS_ONE - 1;
1087 let eval_coeffs: Vec<_> = (1..=(poly_size * degree) as i64)
1088 .map(|v| v.is_odd().into())
1089 .collect_vec();
1090 eval_coeffs
1091 .chunks_exact(degree)
1092 .map(BinaryPoly::new)
1093 .collect_vec()
1094 })
1095 },
1096 || (0..num_vars).map(|i| i as i128 + 2).collect(),
1097 );
1098 let field_cfg = get_field_cfg::<Zt, F>(&mut transcript.fs_transcript);
1099
1100 let result = TestPolyZip::verify::<_, CHECKED>(
1101 &mut transcript,
1102 &pp,
1103 &comm,
1104 &field_cfg,
1105 &point_f,
1106 &eval_f,
1107 );
1108 assert!(result.is_ok(), "Verification failed: {result:?}")
1109 }
1110 }
1111
1112 #[test]
1113 fn verification_fails_at_proximity_link_check_if_combined_row_is_corrupted() {
1114 let num_vars = 10;
1115 let poly_size: usize = 1 << num_vars;
1116 let pp = TestZip::setup(poly_size, C.clone());
1117
1118 let mle: DenseMultilinearExtension<_> = (1..=poly_size as i32).map(Int::from).collect();
1119
1120 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
1121
1122 let point: Vec<<Zt as ZipTypes>::Pt> = vec![Zero::zero(); num_vars];
1123
1124 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1125 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
1126
1127 let eval_f = TestZip::prove_single::<F, CHECKED>(
1128 &mut prover_transcript,
1129 &pp,
1130 &mle,
1131 &point,
1132 &hint,
1133 &field_cfg,
1134 )
1135 .unwrap();
1136
1137 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1138
1139 let num_bytes_f = eval_f.inner().get_num_bytes();
1141 let b_section_size = 1 + pp.num_rows * 2 * num_bytes_f;
1142 let bytes_to_corrupt = M * size_of::<crypto_bigint::Word>();
1143
1144 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1145 assert!(
1146 b_section_size + bytes_to_corrupt <= verifier_transcript.stream.get_ref().len(),
1147 "proof too small to tamper combined_row"
1148 );
1149
1150 for b in &mut verifier_transcript.stream.get_mut()
1151 [b_section_size..b_section_size + bytes_to_corrupt]
1152 {
1153 *b = 0xFF;
1154 }
1155
1156 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
1157 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
1158
1159 let res = TestZip::verify::<_, CHECKED>(
1160 &mut verifier_transcript,
1161 &pp,
1162 &comm,
1163 &field_cfg,
1164 &point_f,
1165 &eval_f,
1166 );
1167 assert!(res.is_err());
1168 }
1169
1170 #[test]
1172 fn bench_p12_verify() {
1173 fn inner<const P: usize>() {
1174 let mut rng = ThreadRng::default();
1175 let poly_size: usize = 1 << P;
1177 let pp = TestZip::setup(poly_size, C.clone());
1178
1179 let mle = DenseMultilinearExtension::rand(P, &mut rng);
1180 let (hint, commitment) = TestZip::commit_single(&pp, &mle).expect("commit");
1181
1182 let point = vec![1i64; P].iter().map(|v| v.into()).collect_vec();
1183
1184 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&commitment);
1185 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
1186
1187 let eval_f = TestZip::prove_single::<F, CHECKED>(
1188 &mut prover_transcript,
1189 &pp,
1190 &mle,
1191 &point,
1192 &hint,
1193 &field_cfg,
1194 )
1195 .unwrap();
1196
1197 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1198
1199 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1200 verifier_transcript
1201 .fs_transcript
1202 .absorb_slice(&commitment.root);
1203 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
1204
1205 let zero_f = F::zero_with_cfg(&field_cfg);
1206 let mle_f = DenseMultilinearExtension::from_evaluations_vec(
1207 P,
1208 mle.iter().map(|c| c.into_with_cfg(&field_cfg)).collect(),
1209 zero_f.clone(),
1210 );
1211 let expected_eval_f = mle_f.evaluate(&point_f, zero_f).unwrap();
1212 assert_eq!(eval_f, expected_eval_f, "prover returned wrong eval");
1213
1214 TestZip::verify::<_, CHECKED>(
1215 &mut verifier_transcript,
1216 &pp,
1217 &commitment,
1218 &field_cfg,
1219 &point_f,
1220 &eval_f,
1221 )
1222 .expect("verify");
1223 }
1224
1225 inner::<12>();
1226 }
1227
1228 #[test]
1230 fn bench_p12_verify_poly() {
1231 fn inner<const P: usize>() {
1232 let mut rng = ThreadRng::default();
1233 let poly_size: usize = 1 << P;
1235 let pp = TestPolyZip::setup(poly_size, POLY_C.clone());
1236
1237 let mle = DenseMultilinearExtension::rand(P, &mut rng);
1238 let (hint, comm) = TestPolyZip::commit_single(&pp, &mle).expect("commit");
1239
1240 let point = vec![1i64; P].iter().map(|v| (*v).into()).collect_vec();
1241
1242 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1243 let field_cfg = get_field_cfg::<PolyZt, F>(&mut prover_transcript.fs_transcript);
1244
1245 let eval_f = TestPolyZip::prove_single::<F, CHECKED>(
1246 &mut prover_transcript,
1247 &pp,
1248 &mle,
1249 &point,
1250 &hint,
1251 &field_cfg,
1252 )
1253 .unwrap();
1254
1255 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1256
1257 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1258 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
1259 let field_cfg = get_field_cfg::<PolyZt, F>(&mut verifier_transcript.fs_transcript);
1260
1261 TestPolyZip::verify::<_, CHECKED>(
1263 &mut verifier_transcript,
1264 &pp,
1265 &comm,
1266 &field_cfg,
1267 &point_f,
1268 &eval_f,
1269 )
1270 .expect("verify");
1271 }
1272
1273 inner::<12>();
1274 }
1275
1276 fn batched_prove_verify_inner<const BATCH: usize>(num_vars: usize) {
1277 let poly_size = 1 << num_vars;
1278 let pp = TestZip::setup(poly_size, C.clone());
1279
1280 let polys: Vec<DenseMultilinearExtension<_>> = (0..BATCH)
1281 .map(|b| {
1282 let base = (b * poly_size) as i32;
1283 (base + 1..=base + poly_size as i32)
1284 .map(Int::<INT_LIMBS>::from)
1285 .collect()
1286 })
1287 .collect();
1288
1289 let (hint, comm) = TestZip::commit(&pp, &polys).unwrap();
1290 let point: Vec<<Zt as ZipTypes>::Pt> =
1291 (0..num_vars).map(|i| Int::from(i as i32 + 2)).collect();
1292
1293 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1294 let field_cfg = get_field_cfg::<PolyZt, F>(&mut prover_transcript.fs_transcript);
1295
1296 let eval_f = TestZip::prove::<F, CHECKED>(
1297 &mut prover_transcript,
1298 &pp,
1299 &polys,
1300 &point,
1301 &hint,
1302 &field_cfg,
1303 )
1304 .unwrap();
1305
1306 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1307
1308 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1309 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
1310 let field_cfg = get_field_cfg::<PolyZt, F>(&mut verifier_transcript.fs_transcript);
1311
1312 let res = TestZip::verify::<_, CHECKED>(
1313 &mut verifier_transcript,
1314 &pp,
1315 &comm,
1316 &field_cfg,
1317 &point_f,
1318 &eval_f,
1319 );
1320 assert!(
1321 res.is_ok(),
1322 "Batched verify (batch={BATCH}) failed: {res:?}"
1323 );
1324 }
1325
1326 #[test]
1327 fn batched_prove_verify_batch_2() {
1328 batched_prove_verify_inner::<2>(10);
1329 }
1330
1331 #[test]
1332 fn batched_prove_verify_batch_5() {
1333 batched_prove_verify_inner::<5>(10);
1334 }
1335
1336 #[test]
1337 fn batched_prove_verify_batch_1_roundtrip() {
1338 batched_prove_verify_inner::<1>(10);
1339 }
1340
1341 #[test]
1342 fn batched_verify_fails_with_tampered_eval() {
1343 let num_vars = 10;
1344 let poly_size = 1 << num_vars;
1345 let pp = TestZip::setup(poly_size, C.clone());
1346
1347 let polys: Vec<DenseMultilinearExtension<_>> = vec![
1348 (1..=poly_size as i32).map(Int::from).collect(),
1349 (17..=16 + poly_size as i32).map(Int::from).collect(),
1350 ];
1351
1352 let (hint, comm) = TestZip::commit(&pp, &polys).unwrap();
1353 let point: Vec<<Zt as ZipTypes>::Pt> = (0..num_vars).map(|i| Int::from(i + 2)).collect();
1354
1355 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1356 let field_cfg = get_field_cfg::<PolyZt, F>(&mut prover_transcript.fs_transcript);
1357
1358 let eval_f = TestZip::prove::<F, CHECKED>(
1359 &mut prover_transcript,
1360 &pp,
1361 &polys,
1362 &point,
1363 &hint,
1364 &field_cfg,
1365 )
1366 .unwrap();
1367 let tampered_eval = eval_f + F::one_with_cfg(&field_cfg);
1368
1369 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1370
1371 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1372 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
1373 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
1374
1375 let res = TestZip::verify::<_, CHECKED>(
1376 &mut verifier_transcript,
1377 &pp,
1378 &comm,
1379 &field_cfg,
1380 &point_f,
1381 &tampered_eval,
1382 );
1383 assert!(res.is_err(), "Should fail when eval is tampered");
1384 }
1385}