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 #[cfg_attr(miri, ignore)] fn verification_fails_with_incorrect_evaluation() {
400 let num_vars = 10;
401
402 {
403 let (pp, comm, point_f, eval_f, mut transcript) =
404 setup_full_protocol::<F, N, K, M>(num_vars);
405 let field_cfg = get_field_cfg::<Zt, F>(&mut transcript.fs_transcript);
406 let tampered = eval_f + F::one_with_cfg(&field_cfg);
407
408 let result = TestZip::verify::<_, CHECKED>(
409 &mut transcript,
410 &pp,
411 &comm,
412 &field_cfg,
413 &point_f,
414 &tampered,
415 );
416
417 assert!(result.is_err());
418 }
419
420 {
421 let (pp, comm, point_f, eval_f, mut transcript) =
422 setup_full_protocol_poly::<F, N, K, M, DEGREE_PLUS_ONE>(num_vars);
423 let field_cfg = get_field_cfg::<PolyZt, F>(&mut transcript.fs_transcript);
424 let tampered = eval_f + F::one_with_cfg(&field_cfg);
425
426 let result = TestPolyZip::verify::<_, CHECKED>(
427 &mut transcript,
428 &pp,
429 &comm,
430 &field_cfg,
431 &point_f,
432 &tampered,
433 );
434
435 assert!(result.is_err());
436 }
437 }
438
439 #[test]
440 #[cfg_attr(miri, ignore)] fn verification_fails_with_tampered_proof() {
442 fn tamper(mut proof: PcsVerifierTranscript) -> PcsVerifierTranscript {
443 let original_f0: F = proof.clone().read_field_elements(1).unwrap().remove(0);
444 type Mod = <F as Field>::Modulus;
447 let offset = Mod::LENGTH_NUM_BYTES + Mod::NUM_BYTES;
448 proof.stream.get_mut()[offset] ^= 0x01;
449
450 let tampered_f0: F = proof.clone().read_field_elements(1).unwrap().remove(0);
452 assert_eq!(original_f0.modulus(), tampered_f0.modulus());
453 assert_ne!(original_f0, tampered_f0);
454
455 proof
456 }
457 let num_vars = 10;
458
459 {
460 let (pp, comm, point_f, eval_f, proof) = setup_full_protocol::<F, N, K, M>(num_vars);
461 let mut tampered = tamper(proof);
462 let field_cfg = get_field_cfg::<Zt, F>(&mut tampered.fs_transcript);
463 let result = TestZip::verify::<_, CHECKED>(
464 &mut tampered,
465 &pp,
466 &comm,
467 &field_cfg,
468 &point_f,
469 &eval_f,
470 );
471 assert!(result.is_err());
472 }
473
474 {
475 let (pp, comm, point_f, eval_f, proof) =
476 setup_full_protocol_poly::<F, N, K, M, DEGREE_PLUS_ONE>(num_vars);
477 let mut tampered = tamper(proof);
478 let field_cfg = get_field_cfg::<PolyZt, F>(&mut tampered.fs_transcript);
479 let result = TestPolyZip::verify::<_, CHECKED>(
480 &mut tampered,
481 &pp,
482 &comm,
483 &field_cfg,
484 &point_f,
485 &eval_f,
486 );
487 assert!(result.is_err());
488 }
489 }
490
491 #[test]
492 #[cfg_attr(miri, ignore)] fn verification_fails_with_wrong_commitment() {
494 let num_vars = 10;
495 {
496 let (pp, _comm_poly1, point_f, eval_f, mut transcript) =
497 setup_full_protocol::<F, N, K, M>(num_vars);
498 let field_cfg = get_field_cfg::<Zt, F>(&mut transcript.fs_transcript);
499
500 let poly2: DenseMultilinearExtension<_> =
501 (20..(20 + (1 << num_vars))).map(Int::from).collect();
502
503 let (_, comm_poly2) = TestZip::commit_single(&pp, &poly2).unwrap();
504
505 let result = TestZip::verify::<_, CHECKED>(
506 &mut transcript,
507 &pp,
508 &comm_poly2,
509 &field_cfg,
510 &point_f,
511 &eval_f,
512 );
513
514 assert!(result.is_err());
515 }
516
517 {
518 let (pp, _comm_poly1, point_f, eval_f, mut transcript) =
519 setup_full_protocol_poly::<F, N, K, M, DEGREE_PLUS_ONE>(num_vars);
520 let field_cfg = get_field_cfg::<PolyZt, F>(&mut transcript.fs_transcript);
521
522 let different_evals = {
523 let different_eval_coeffs: Vec<_> = (1..=((1 << num_vars) * (DEGREE_PLUS_ONE - 1)))
524 .map(|x| (x % 3 == 0).into())
525 .collect_vec();
526 different_eval_coeffs
527 .chunks_exact(DEGREE_PLUS_ONE - 1)
528 .map(BinaryPoly::new)
529 .collect_vec()
530 };
531
532 let poly2 = DenseMultilinearExtension::from_evaluations_vec(
533 num_vars,
534 different_evals,
535 Zero::zero(),
536 );
537 let (_, comm_poly2) = TestPolyZip::commit_single(&pp, &poly2).unwrap();
538
539 let result = TestPolyZip::verify::<_, CHECKED>(
540 &mut transcript,
541 &pp,
542 &comm_poly2,
543 &field_cfg,
544 &point_f,
545 &eval_f,
546 );
547
548 assert!(result.is_err());
549 }
550 }
551
552 #[test]
553 #[cfg_attr(miri, ignore)] fn verification_fails_with_invalid_point_size() {
555 let num_vars = 10;
556
557 let make_invalid_point = |cfg: &<F as PrimeField>::Config| {
558 let mut invalid_point = vec![];
559 for i in 0..=num_vars {
560 invalid_point.push(F::from_with_cfg(100 + i as i32, cfg));
561 }
562 invalid_point
563 };
564
565 {
566 let (pp, comm, _point_f, eval_f, mut transcript) =
567 setup_full_protocol_poly::<F, N, K, M, DEGREE_PLUS_ONE>(num_vars);
568 let field_cfg = get_field_cfg::<PolyZt, F>(&mut transcript.fs_transcript);
569 let invalid_point = make_invalid_point(eval_f.cfg());
570
571 let result = TestPolyZip::verify::<_, CHECKED>(
572 &mut transcript,
573 &pp,
574 &comm,
575 &field_cfg,
576 &invalid_point,
577 &eval_f,
578 );
579
580 assert!(matches!(result, Err(..)));
581 }
582
583 {
584 let (pp, comm, _point_f, eval_f, mut transcript) =
585 setup_full_protocol::<F, N, K, M>(num_vars);
586 let field_cfg = get_field_cfg::<Zt, F>(&mut transcript.fs_transcript);
587 let invalid_point = make_invalid_point(eval_f.cfg());
588
589 let result = TestZip::verify::<_, CHECKED>(
590 &mut transcript,
591 &pp,
592 &comm,
593 &field_cfg,
594 &invalid_point,
595 &eval_f,
596 );
597
598 assert!(matches!(result, Err(..)));
599 }
600 }
601
602 #[test]
603 #[cfg_attr(miri, ignore)] fn verification_fails_due_to_incorrect_polynomial() {
605 let num_vars = 10;
606 let (pp, mle1) = setup_test_params(num_vars);
607 let poly_size = 1 << num_vars;
608
609 let (hint, comm) = TestZip::commit_single(&pp, &mle1).unwrap();
610
611 let mle2: DenseMultilinearExtension<_> = (20..20 + poly_size).map(Int::from).collect();
612
613 let point: Vec<<Zt as ZipTypes>::Pt> =
614 (0..num_vars).map(|i| Int::from(i as i32 + 2)).collect();
615
616 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
617 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
618
619 let _eval_f = TestZip::prove_single::<F, CHECKED>(
620 &mut prover_transcript,
621 &pp,
622 &mle2,
623 &point,
624 &hint,
625 &field_cfg,
626 )
627 .unwrap();
628
629 let eval_mle1 = mle1
630 .evaluate(&point, Zero::zero())
631 .expect("Failed to evaluate polynomial");
632
633 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
634 let eval_mle1_f = eval_mle1.into_with_cfg(&field_cfg);
635
636 let mut verifier_transcript = prover_transcript.into_verification_transcript();
637 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
638 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
639
640 let verification_result = TestZip::verify::<_, CHECKED>(
641 &mut verifier_transcript,
642 &pp,
643 &comm,
644 &field_cfg,
645 &point_f,
646 &eval_mle1_f,
647 );
648 assert!(verification_result.is_err());
649 }
650
651 #[test]
652 #[cfg_attr(miri, ignore)] fn verification_fails_due_to_a_hint_that_is_not_close() {
654 let num_vars = 10;
655 let (pp, mle) = setup_test_params(num_vars);
656
657 let (original_hint, comm) = TestZip::commit_single(&pp, &mle).unwrap();
658
659 let mut corrupted_data = original_hint.cw_matrices[0].clone();
660 {
661 let mut corrupted_rows = corrupted_data.to_rows_slices_mut();
662 let codeword_len = pp.linear_code.codeword_len();
663 let corruption_count = codeword_len / 2 + 1;
664 for i in corrupted_rows[0].iter_mut().take(corruption_count) {
665 *i += Int::ONE;
666 }
667 }
668
669 let corrupted_merkle_tree = MerkleTree::new(&corrupted_data.to_rows_slices());
670 let corrupted_hint = ZipPlusHint::new(vec![corrupted_data], corrupted_merkle_tree);
671
672 let point: Vec<<Zt as ZipTypes>::Pt> =
673 (0..num_vars).map(|i| Int::from(i as i32 + 2)).collect();
674
675 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
676 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
677
678 let eval_f = TestZip::prove_single::<F, CHECKED>(
679 &mut prover_transcript,
680 &pp,
681 &mle,
682 &point,
683 &corrupted_hint,
684 &field_cfg,
685 )
686 .unwrap();
687
688 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
689
690 let mut verifier_transcript = prover_transcript.into_verification_transcript();
691 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
692 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
693
694 let verification_result = TestZip::verify::<_, CHECKED>(
695 &mut verifier_transcript,
696 &pp,
697 &comm,
698 &field_cfg,
699 &point_f,
700 &eval_f,
701 );
702
703 assert!(verification_result.is_err());
704 }
705
706 #[test]
707 #[cfg_attr(miri, ignore)] fn verification_fails_due_to_incorrect_evaluation() {
709 let num_vars = 10;
710 let (pp, mle) = setup_test_params(num_vars);
711
712 let (hint, comm) = TestZip::commit_single(&pp, &mle).unwrap();
713
714 let point: Vec<<Zt as ZipTypes>::Pt> =
715 (0..num_vars).map(|i| Int::from(i as i32 + 2)).collect();
716
717 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
718 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
719
720 let eval_f = TestZip::prove_single::<F, CHECKED>(
721 &mut prover_transcript,
722 &pp,
723 &mle,
724 &point,
725 &hint,
726 &field_cfg,
727 )
728 .unwrap();
729
730 let incorrect_eval_f = eval_f + F::one_with_cfg(&field_cfg);
731 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
732
733 let mut verifier_transcript = prover_transcript.into_verification_transcript();
734 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
735 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
736
737 let verification_result = TestZip::verify::<_, CHECKED>(
738 &mut verifier_transcript,
739 &pp,
740 &comm,
741 &field_cfg,
742 &point_f,
743 &incorrect_eval_f, );
745
746 assert!(verification_result.is_err());
747 }
748
749 #[test]
750 #[cfg_attr(miri, ignore)] fn verification_fails_if_proximity_check_is_invalid() {
752 let num_vars = 10;
753 let poly_size: usize = 1 << num_vars;
754
755 let pp = TestZip::setup(poly_size, C.clone());
756
757 let mle: DenseMultilinearExtension<_> = (0..poly_size as i32)
758 .map(<Zt as ZipTypes>::Eval::from)
759 .collect();
760
761 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
762
763 let point = vec![ConstOne::ONE; num_vars];
764
765 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
766 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
767
768 let eval_f = TestZip::prove_single::<F, CHECKED>(
769 &mut prover_transcript,
770 &pp,
771 &mle,
772 &point,
773 &hint,
774 &field_cfg,
775 )
776 .unwrap();
777
778 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
779
780 let row_len = pp.linear_code.row_len();
784 let num_bytes_f = eval_f.inner().get_num_bytes();
785 let b_section_size = 1 + pp.num_rows * 2 * num_bytes_f;
786 let bytes_per_comb_r = M * size_of::<crypto_bigint::Word>();
787 let combined_row_size = row_len * bytes_per_comb_r;
788 let column_values_start = b_section_size + combined_row_size;
789 let bytes_per_cw = K * size_of::<crypto_bigint::Word>();
790
791 let mut verifier_transcript = prover_transcript.into_verification_transcript();
792 assert!(
793 column_values_start + bytes_per_cw <= verifier_transcript.stream.get_ref().len(),
794 "proof too small to tamper column values"
795 );
796
797 let flip_at = column_values_start + bytes_per_cw / 2;
798 verifier_transcript.stream.get_mut()[flip_at] ^= 0x01;
799
800 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
801 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
802
803 let res = TestZip::verify::<_, CHECKED>(
804 &mut verifier_transcript,
805 &pp,
806 &comm,
807 &field_cfg,
808 &point_f,
809 &eval_f,
810 );
811
812 match res {
813 Err(ZipError::InvalidPcsOpen(msg)) => {
814 assert_eq!(msg, "Proximity failure");
815 }
816 Ok(()) => panic!("verification unexpectedly succeeded"),
817 Err(e) => panic!("unexpected error: {e:?}"),
818 }
819 }
820
821 #[test]
822 #[cfg_attr(miri, ignore)] fn verification_fails_if_evaluation_consistency_check_is_invalid() {
824 let num_vars = 10;
825 let poly_size: usize = 1 << num_vars;
826 let pp = TestZip::setup(poly_size, C.clone());
827
828 let mle: DenseMultilinearExtension<_> =
829 (0..poly_size as i32).map(Int::<INT_LIMBS>::from).collect();
830
831 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
832
833 let point: Vec<<Zt as ZipTypes>::Pt> = vec![Zero::zero(); num_vars];
834
835 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
836 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
837
838 let eval_f = TestZip::prove_single::<F, CHECKED>(
839 &mut prover_transcript,
840 &pp,
841 &mle,
842 &point,
843 &hint,
844 &field_cfg,
845 )
846 .unwrap();
847
848 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
849
850 let num_bytes_f_mod = eval_f.modulus().get_num_bytes();
854 let num_bytes_f_val = eval_f.inner().get_num_bytes();
855 let flip_at = 1 + num_bytes_f_mod + num_bytes_f_val / 4;
856
857 let mut verifier_transcript = prover_transcript.into_verification_transcript();
858 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
859 get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
860 assert!(
861 flip_at < verifier_transcript.stream.get_ref().len(),
862 "proof too small to tamper b section"
863 );
864 verifier_transcript.stream.get_mut()[flip_at] ^= 0x01;
865
866 let res = TestZip::verify::<_, CHECKED>(
867 &mut verifier_transcript,
868 &pp,
869 &comm,
870 &field_cfg,
871 &point_f,
872 &eval_f,
873 );
874
875 match res {
876 Err(ZipError::InvalidPcsOpen(msg)) => {
877 assert_eq!(msg, "Evaluation consistency failure");
878 }
879 Ok(()) => panic!("verification unexpectedly succeeded"),
880 Err(e) => panic!("unexpected error: {e:?}"),
881 }
882 }
883
884 #[test]
885 fn verification_succeeds_for_zero_polynomial() {
886 let num_vars = 10;
887 let poly_size: usize = 1 << num_vars;
888 let pp = TestZip::setup(poly_size, C.clone());
889
890 let mle: DenseMultilinearExtension<_> = vec![Zero::zero(); poly_size].into_iter().collect();
891
892 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
893
894 let point: Vec<<Zt as ZipTypes>::Pt> = vec![Zero::zero(); num_vars];
895
896 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
897 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
898
899 let eval_f = TestZip::prove_single::<F, CHECKED>(
900 &mut prover_transcript,
901 &pp,
902 &mle,
903 &point,
904 &hint,
905 &field_cfg,
906 )
907 .unwrap();
908
909 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
910
911 let mut verifier_transcript = prover_transcript.into_verification_transcript();
912 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
913 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
914
915 let res = TestZip::verify::<_, CHECKED>(
916 &mut verifier_transcript,
917 &pp,
918 &comm,
919 &field_cfg,
920 &point_f,
921 &eval_f,
922 );
923 assert!(res.is_ok());
924 }
925
926 #[test]
927 #[cfg_attr(miri, ignore)] fn verification_succeeds_at_zero_point() {
929 let num_vars = 10;
930 let poly_size: usize = 1 << num_vars;
931 let pp = TestZip::setup(poly_size, C.clone());
932
933 let mle: DenseMultilinearExtension<_> =
934 (1..=poly_size as i32).map(Int::<INT_LIMBS>::from).collect();
935
936 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
937
938 let point: Vec<<Zt as ZipTypes>::Pt> = vec![Zero::zero(); num_vars];
939
940 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
941 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
942
943 let eval_f = TestZip::prove_single::<F, CHECKED>(
944 &mut prover_transcript,
945 &pp,
946 &mle,
947 &point,
948 &hint,
949 &field_cfg,
950 )
951 .unwrap();
952
953 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
954
955 let mut verifier_transcript = prover_transcript.into_verification_transcript();
956 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
957 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
958
959 let res = TestZip::verify::<_, CHECKED>(
960 &mut verifier_transcript,
961 &pp,
962 &comm,
963 &field_cfg,
964 &point_f,
965 &eval_f,
966 );
967 assert!(res.is_ok());
968 }
969
970 #[test]
971 #[cfg_attr(miri, ignore)] fn verification_succeeds_when_polynomial_coefficients_are_max_bit_size() {
973 let num_vars = 10;
974 let (pp, _) = setup_test_params(num_vars);
975
976 let mut evals: Vec<<Zt as ZipTypes>::Eval> =
977 (0..1 << num_vars as i32).map(Int::from).collect();
978 evals[1] = Int::from(i64::MAX);
979 let poly = DenseMultilinearExtension::from_evaluations_vec(num_vars, evals, Zero::zero());
980
981 let (hint, comm) = TestZip::commit_single(&pp, &poly).unwrap();
982
983 let mut point = vec![<Zt as ZipTypes>::Pt::ZERO; num_vars];
984 point[0] = <Zt as ZipTypes>::Pt::ONE;
985
986 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
987 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
988
989 let eval_f = TestZip::prove_single::<F, CHECKED>(
990 &mut prover_transcript,
991 &pp,
992 &poly,
993 &point,
994 &hint,
995 &field_cfg,
996 )
997 .unwrap();
998
999 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1000
1001 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1002 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
1003 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
1004
1005 let verification_result = TestZip::verify::<_, CHECKED>(
1006 &mut verifier_transcript,
1007 &pp,
1008 &comm,
1009 &field_cfg,
1010 &point_f,
1011 &eval_f,
1012 );
1013
1014 assert!(
1015 verification_result.is_ok(),
1016 "Verification failed: {verification_result:?}",
1017 );
1018 }
1019
1020 #[test]
1021 #[cfg_attr(miri, ignore)] fn verification_succeeds_with_minimal_polynomial_size_mu_is_8() {
1023 let num_vars = 10;
1024 let (pp, poly) = setup_test_params(num_vars);
1025
1026 let (hint, comm) = TestZip::commit_single(&pp, &poly).unwrap();
1027
1028 let point: Vec<<Zt as ZipTypes>::Pt> = (1..=num_vars as i32).map(Int::from).collect_vec();
1029
1030 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1031 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
1032
1033 let eval_f = TestZip::prove_single::<F, CHECKED>(
1034 &mut prover_transcript,
1035 &pp,
1036 &poly,
1037 &point,
1038 &hint,
1039 &field_cfg,
1040 )
1041 .unwrap();
1042
1043 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1044
1045 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1046 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
1047 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
1048
1049 let verification_result = TestZip::verify::<_, CHECKED>(
1050 &mut verifier_transcript,
1051 &pp,
1052 &comm,
1053 &field_cfg,
1054 &point_f,
1055 &eval_f,
1056 );
1057
1058 assert!(verification_result.is_ok());
1059 }
1060
1061 #[test]
1062 #[cfg_attr(miri, ignore)] fn verification_succeeds_for_code_row_length_of_1() {
1064 let num_vars = 8;
1065 macro_rules! make_code {
1066 () => {
1067 IprsCode::new(1, 0).unwrap()
1068 };
1069 }
1070 {
1071 let (pp, comm, point_f, eval_f, mut transcript) =
1072 setup_full_protocol_inner::<Zt, C, F, N>(
1073 num_vars,
1074 |num_vars| {
1075 setup_test_params_inner(num_vars, make_code!(), |poly_size| {
1076 (1..=poly_size as i32).map(Int::from).collect()
1077 })
1078 },
1079 || (0..num_vars).map(|i| Int::from(i as i32 + 2)).collect(),
1080 );
1081 let field_cfg = get_field_cfg::<Zt, F>(&mut transcript.fs_transcript);
1082
1083 let result = TestZip::verify::<_, CHECKED>(
1084 &mut transcript,
1085 &pp,
1086 &comm,
1087 &field_cfg,
1088 &point_f,
1089 &eval_f,
1090 );
1091 assert!(result.is_ok(), "Verification failed: {result:?}")
1092 };
1093 {
1094 let (pp, comm, point_f, eval_f, mut transcript) =
1095 setup_full_protocol_inner::<PolyZt, PolyC, F, N>(
1096 num_vars,
1097 |num_vars| {
1098 setup_test_params_inner(num_vars, make_code!(), |poly_size| {
1099 let degree = DEGREE_PLUS_ONE - 1;
1100 let eval_coeffs: Vec<_> = (1..=(poly_size * degree) as i64)
1101 .map(|v| v.is_odd().into())
1102 .collect_vec();
1103 eval_coeffs
1104 .chunks_exact(degree)
1105 .map(BinaryPoly::new)
1106 .collect_vec()
1107 })
1108 },
1109 || (0..num_vars).map(|i| i as i128 + 2).collect(),
1110 );
1111 let field_cfg = get_field_cfg::<Zt, F>(&mut transcript.fs_transcript);
1112
1113 let result = TestPolyZip::verify::<_, CHECKED>(
1114 &mut transcript,
1115 &pp,
1116 &comm,
1117 &field_cfg,
1118 &point_f,
1119 &eval_f,
1120 );
1121 assert!(result.is_ok(), "Verification failed: {result:?}")
1122 }
1123 }
1124
1125 #[test]
1126 #[cfg_attr(miri, ignore)] fn verification_fails_at_proximity_link_check_if_combined_row_is_corrupted() {
1128 let num_vars = 10;
1129 let poly_size: usize = 1 << num_vars;
1130 let pp = TestZip::setup(poly_size, C.clone());
1131
1132 let mle: DenseMultilinearExtension<_> = (1..=poly_size as i32).map(Int::from).collect();
1133
1134 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
1135
1136 let point: Vec<<Zt as ZipTypes>::Pt> = vec![Zero::zero(); num_vars];
1137
1138 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1139 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
1140
1141 let eval_f = TestZip::prove_single::<F, CHECKED>(
1142 &mut prover_transcript,
1143 &pp,
1144 &mle,
1145 &point,
1146 &hint,
1147 &field_cfg,
1148 )
1149 .unwrap();
1150
1151 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1152
1153 let num_bytes_f = eval_f.inner().get_num_bytes();
1155 let b_section_size = 1 + pp.num_rows * 2 * num_bytes_f;
1156 let bytes_to_corrupt = M * size_of::<crypto_bigint::Word>();
1157
1158 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1159 assert!(
1160 b_section_size + bytes_to_corrupt <= verifier_transcript.stream.get_ref().len(),
1161 "proof too small to tamper combined_row"
1162 );
1163
1164 for b in &mut verifier_transcript.stream.get_mut()
1165 [b_section_size..b_section_size + bytes_to_corrupt]
1166 {
1167 *b = 0xFF;
1168 }
1169
1170 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
1171 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
1172
1173 let res = TestZip::verify::<_, CHECKED>(
1174 &mut verifier_transcript,
1175 &pp,
1176 &comm,
1177 &field_cfg,
1178 &point_f,
1179 &eval_f,
1180 );
1181 assert!(res.is_err());
1182 }
1183
1184 #[test]
1186 #[cfg_attr(miri, ignore)] fn bench_p12_verify() {
1188 fn inner<const P: usize>() {
1189 let mut rng = ThreadRng::default();
1190 let poly_size: usize = 1 << P;
1192 let pp = TestZip::setup(poly_size, C.clone());
1193
1194 let mle = DenseMultilinearExtension::rand(P, &mut rng);
1195 let (hint, commitment) = TestZip::commit_single(&pp, &mle).expect("commit");
1196
1197 let point = vec![1i64; P].iter().map(|v| v.into()).collect_vec();
1198
1199 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&commitment);
1200 let field_cfg = get_field_cfg::<Zt, F>(&mut prover_transcript.fs_transcript);
1201
1202 let eval_f = TestZip::prove_single::<F, CHECKED>(
1203 &mut prover_transcript,
1204 &pp,
1205 &mle,
1206 &point,
1207 &hint,
1208 &field_cfg,
1209 )
1210 .unwrap();
1211
1212 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1213
1214 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1215 verifier_transcript
1216 .fs_transcript
1217 .absorb_slice(&commitment.root);
1218 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
1219
1220 let zero_f = F::zero_with_cfg(&field_cfg);
1221 let mle_f = DenseMultilinearExtension::from_evaluations_vec(
1222 P,
1223 mle.iter().map(|c| c.into_with_cfg(&field_cfg)).collect(),
1224 zero_f.clone(),
1225 );
1226 let expected_eval_f = mle_f.evaluate(&point_f, zero_f).unwrap();
1227 assert_eq!(eval_f, expected_eval_f, "prover returned wrong eval");
1228
1229 TestZip::verify::<_, CHECKED>(
1230 &mut verifier_transcript,
1231 &pp,
1232 &commitment,
1233 &field_cfg,
1234 &point_f,
1235 &eval_f,
1236 )
1237 .expect("verify");
1238 }
1239
1240 inner::<12>();
1241 }
1242
1243 #[test]
1245 #[cfg_attr(miri, ignore)] fn bench_p12_verify_poly() {
1247 fn inner<const P: usize>() {
1248 let mut rng = ThreadRng::default();
1249 let poly_size: usize = 1 << P;
1251 let pp = TestPolyZip::setup(poly_size, POLY_C.clone());
1252
1253 let mle = DenseMultilinearExtension::rand(P, &mut rng);
1254 let (hint, comm) = TestPolyZip::commit_single(&pp, &mle).expect("commit");
1255
1256 let point = vec![1i64; P].iter().map(|v| (*v).into()).collect_vec();
1257
1258 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1259 let field_cfg = get_field_cfg::<PolyZt, F>(&mut prover_transcript.fs_transcript);
1260
1261 let eval_f = TestPolyZip::prove_single::<F, CHECKED>(
1262 &mut prover_transcript,
1263 &pp,
1264 &mle,
1265 &point,
1266 &hint,
1267 &field_cfg,
1268 )
1269 .unwrap();
1270
1271 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1272
1273 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1274 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
1275 let field_cfg = get_field_cfg::<PolyZt, F>(&mut verifier_transcript.fs_transcript);
1276
1277 TestPolyZip::verify::<_, CHECKED>(
1279 &mut verifier_transcript,
1280 &pp,
1281 &comm,
1282 &field_cfg,
1283 &point_f,
1284 &eval_f,
1285 )
1286 .expect("verify");
1287 }
1288
1289 inner::<12>();
1290 }
1291
1292 fn batched_prove_verify_inner<const BATCH: usize>(num_vars: usize) {
1293 let poly_size = 1 << num_vars;
1294 let pp = TestZip::setup(poly_size, C.clone());
1295
1296 let polys: Vec<DenseMultilinearExtension<_>> = (0..BATCH)
1297 .map(|b| {
1298 let base = (b * poly_size) as i32;
1299 (base + 1..=base + poly_size as i32)
1300 .map(Int::<INT_LIMBS>::from)
1301 .collect()
1302 })
1303 .collect();
1304
1305 let (hint, comm) = TestZip::commit(&pp, &polys).unwrap();
1306 let point: Vec<<Zt as ZipTypes>::Pt> =
1307 (0..num_vars).map(|i| Int::from(i as i32 + 2)).collect();
1308
1309 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1310 let field_cfg = get_field_cfg::<PolyZt, F>(&mut prover_transcript.fs_transcript);
1311
1312 let eval_f = TestZip::prove::<F, CHECKED>(
1313 &mut prover_transcript,
1314 &pp,
1315 &polys,
1316 &point,
1317 &hint,
1318 &field_cfg,
1319 )
1320 .unwrap();
1321
1322 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1323
1324 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1325 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
1326 let field_cfg = get_field_cfg::<PolyZt, F>(&mut verifier_transcript.fs_transcript);
1327
1328 let res = TestZip::verify::<_, CHECKED>(
1329 &mut verifier_transcript,
1330 &pp,
1331 &comm,
1332 &field_cfg,
1333 &point_f,
1334 &eval_f,
1335 );
1336 assert!(
1337 res.is_ok(),
1338 "Batched verify (batch={BATCH}) failed: {res:?}"
1339 );
1340 }
1341
1342 #[test]
1343 fn batched_prove_verify_batch_2() {
1344 batched_prove_verify_inner::<2>(10);
1345 }
1346
1347 #[test]
1348 #[cfg_attr(miri, ignore)] fn batched_prove_verify_batch_5() {
1350 batched_prove_verify_inner::<5>(10);
1351 }
1352
1353 #[test]
1354 #[cfg_attr(miri, ignore)] fn batched_prove_verify_batch_1_roundtrip() {
1356 batched_prove_verify_inner::<1>(10);
1357 }
1358
1359 #[test]
1360 #[cfg_attr(miri, ignore)] fn batched_verify_fails_with_tampered_eval() {
1362 let num_vars = 10;
1363 let poly_size = 1 << num_vars;
1364 let pp = TestZip::setup(poly_size, C.clone());
1365
1366 let polys: Vec<DenseMultilinearExtension<_>> = vec![
1367 (1..=poly_size as i32).map(Int::from).collect(),
1368 (17..=16 + poly_size as i32).map(Int::from).collect(),
1369 ];
1370
1371 let (hint, comm) = TestZip::commit(&pp, &polys).unwrap();
1372 let point: Vec<<Zt as ZipTypes>::Pt> = (0..num_vars).map(|i| Int::from(i + 2)).collect();
1373
1374 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1375 let field_cfg = get_field_cfg::<PolyZt, F>(&mut prover_transcript.fs_transcript);
1376
1377 let eval_f = TestZip::prove::<F, CHECKED>(
1378 &mut prover_transcript,
1379 &pp,
1380 &polys,
1381 &point,
1382 &hint,
1383 &field_cfg,
1384 )
1385 .unwrap();
1386 let tampered_eval = eval_f + F::one_with_cfg(&field_cfg);
1387
1388 let point_f: Vec<F> = point.iter().map(|v| v.into_with_cfg(&field_cfg)).collect();
1389
1390 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1391 verifier_transcript.fs_transcript.absorb_slice(&comm.root);
1392 let field_cfg = get_field_cfg::<Zt, F>(&mut verifier_transcript.fs_transcript);
1393
1394 let res = TestZip::verify::<_, CHECKED>(
1395 &mut verifier_transcript,
1396 &pp,
1397 &comm,
1398 &field_cfg,
1399 &point_f,
1400 &tampered_eval,
1401 );
1402 assert!(res.is_err(), "Should fail when eval is tampered");
1403 }
1404}