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::{BaseFieldConfig, ProjectElementWithConfig};
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::{ConstTranscribable, Transcript},
19};
20use zinc_utils::{
21 UNCHECKED, add, cfg_into_iter,
22 from_ref::FromRef,
23 inner_product::{FieldInnerProduct, InnerProduct, NativeInnerProduct},
24};
25
26impl<Zt: ZipTypes, Lc: LinearCode<Zt>> ZipPlus<Zt, Lc> {
27 #[allow(clippy::arithmetic_side_effects, clippy::type_complexity)]
99 pub fn verify<C, const CHECK_FOR_OVERFLOW: bool>(
100 transcript: &mut PcsVerifierTranscript,
101 vp: &ZipPlusParams<Zt, Lc>,
102 comm: &ZipPlusCommitment,
103 field_cfg: &C,
104 point_f: &[C::Element],
105 eval_f: &C::Element,
106 ) -> Result<(), ZipError>
107 where
108 C: BaseFieldConfig
109 + ProjectElementWithConfig<Zt::CombR>
110 + ProjectElementWithConfig<Zt::Chal>,
111 C::Integer: ConstTranscribable,
112 {
113 let per_poly_alphas = Self::sample_alphas(&mut transcript.fs_transcript, comm.batch_size);
114 Self::verify_with_alphas::<C, CHECK_FOR_OVERFLOW>(
115 transcript,
116 vp,
117 comm,
118 field_cfg,
119 point_f,
120 eval_f,
121 &per_poly_alphas,
122 )
123 }
124
125 #[allow(clippy::arithmetic_side_effects, clippy::type_complexity)]
133 pub fn verify_with_alphas<C, const CHECK_FOR_OVERFLOW: bool>(
134 transcript: &mut PcsVerifierTranscript,
135 vp: &ZipPlusParams<Zt, Lc>,
136 comm: &ZipPlusCommitment,
137 field_cfg: &C,
138 point_f: &[C::Element],
139 eval_f: &C::Element,
140 per_poly_alphas: &[Vec<Zt::Chal>],
141 ) -> Result<(), ZipError>
142 where
143 C: BaseFieldConfig
144 + ProjectElementWithConfig<Zt::CombR>
145 + ProjectElementWithConfig<Zt::Chal>,
146 C::Integer: ConstTranscribable,
147 {
148 let batch_size = comm.batch_size;
149 validate_input::<Zt, Lc, _>(
150 "verify",
151 vp.num_vars,
152 vp.linear_code.row_len(),
153 batch_size,
154 &[],
155 &[point_f],
156 )?;
157
158 let num_rows = vp.num_rows;
159 let row_len = vp.linear_code.row_len();
160
161 let (q_0, q_1) = point_to_tensor(field_cfg, point_f, vp.num_rows)?;
164 let zero_f = field_cfg.zero();
165
166 let b: Vec<C::Element> = transcript.read_field_elements(field_cfg, num_rows)?;
167
168 let q_0_dot_b =
170 NativeInnerProduct::inner_product::<UNCHECKED>(field_cfg, &q_0, &b, zero_f.clone())?;
171 if q_0_dot_b != *eval_f {
172 return Err(ZipError::InvalidPcsOpen(
173 "Evaluation consistency failure".into(),
174 ));
175 }
176
177 let coeffs: Vec<Zt::Chal> = if num_rows == 1 {
178 vec![Zt::Chal::ONE]
179 } else {
180 transcript.fs_transcript.get_challenges(num_rows)
181 };
182
183 let combined_row: Vec<Zt::CombR> = transcript.read_const_many(row_len)?;
184 let encoded_combined_row: Vec<Zt::CombR> = vp.linear_code.encode_wide(&combined_row);
185
186 let lhs = FieldInnerProduct::inner_product::<UNCHECKED>(
191 field_cfg,
192 &q_1,
193 &combined_row,
194 zero_f.clone(),
195 )?;
196 let rhs = FieldInnerProduct::inner_product::<UNCHECKED>(field_cfg, &b, &coeffs, zero_f)?;
197
198 if lhs != rhs {
199 return Err(ZipError::InvalidPcsOpen("Coherence failure".into()));
200 }
201
202 let columns_and_proofs: Vec<_> = (0..Zt::NUM_COLUMN_OPENINGS)
203 .map(|_| -> Result<_, ZipError> {
204 let column_idx = transcript.squeeze_challenge_idx(vp.linear_code.codeword_len());
205 let column_values = transcript.read_const_many(batch_size * vp.num_rows)?;
206 let proof = transcript.read_merkle_proof().map_err(|e| {
207 ZipError::InvalidPcsOpen(format!("Failed to read Merkle a proof: {e}"))
208 })?;
209
210 Ok((column_idx, column_values, proof))
211 })
212 .try_collect()?;
213
214 cfg_into_iter!(columns_and_proofs).try_for_each(
215 |(column_idx, column_values, proof)| -> Result<(), ZipError> {
216 Self::verify_column_testing_batched::<CHECK_FOR_OVERFLOW>(
217 per_poly_alphas,
218 &coeffs,
219 &encoded_combined_row,
220 &column_values,
221 column_idx,
222 vp.num_rows,
223 batch_size,
224 )?;
225
226 proof
227 .verify(&comm.root, &column_values, column_idx)
228 .map_err(|e| {
229 ZipError::InvalidPcsOpen(format!("Column opening verification failed: {e}"))
230 })?;
231
232 Ok(())
233 },
234 )?;
235
236 Ok(())
237 }
238
239 pub fn sample_alphas(
246 transcript: &mut Blake3Transcript,
247 batch_size: usize,
248 ) -> Vec<Vec<Zt::Chal>> {
249 let degree_bound = Zt::Comb::DEGREE_BOUND;
250 (0..batch_size)
251 .map(|_| {
252 if degree_bound.is_zero() {
253 vec![Zt::Chal::ONE]
254 } else {
255 transcript.get_challenges(add!(degree_bound, 1))
256 }
257 })
258 .collect()
259 }
260
261 pub(super) fn verify_column_testing_batched<const CHECK_FOR_OVERFLOW: bool>(
266 per_poly_alphas: &[Vec<Zt::Chal>],
267 coeffs: &[Zt::Chal],
268 encoded_combined_row: &[Zt::CombR],
269 all_column_entries: &[Zt::Cw],
270 column: usize,
271 num_rows: usize,
272 batch_size: usize,
273 ) -> Result<(), ZipError> {
274 #[allow(clippy::arithmetic_side_effects)]
275 let all_column_entries_comb =
276 (0..batch_size).try_fold(Zt::CombR::ZERO, |acc, i| -> Result<_, ZipError> {
277 let column_entries: Vec<_> = all_column_entries[i * num_rows..(i + 1) * num_rows]
278 .iter()
279 .map(Zt::Comb::from_ref)
280 .map(|p| {
281 Zt::CombDotChal::inner_product::<CHECK_FOR_OVERFLOW>(
282 &(),
283 &p,
284 &per_poly_alphas[i],
285 Zt::CombR::ZERO,
286 )
287 })
288 .try_collect()?;
289
290 Ok(acc
291 + Zt::ArrCombRDotChal::inner_product::<CHECK_FOR_OVERFLOW>(
292 &(),
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_primitives::{
325 FixedConfig, IntSemiring, ProjectElementWithConfig, SemiringConfig, WithAssociatedInteger,
326 crypto_bigint_int::Int,
327 crypto_bigint_monty::{MontyField, MontyFieldElement},
328 crypto_bigint_uint::{U64, Uint},
329 };
330 use itertools::Itertools;
331 use num_traits::{ConstOne, ConstZero, Zero};
332 use rand::prelude::*;
333 use std::{mem::size_of, sync::LazyLock};
334 use zinc_poly::{
335 mle::{DenseMultilinearExtension, MultilinearExtensionRand},
336 univariate::binary::BinaryPoly,
337 };
338 use zinc_transcript::traits::{ConstTranscribable, Transcript};
339 use zinc_utils::CHECKED;
340
341 const INT_LIMBS: usize = U64::LIMBS;
342
343 const N: usize = INT_LIMBS;
344 const K: usize = INT_LIMBS * 4;
345 const M: usize = INT_LIMBS * 8;
346 const DEGREE_PLUS_ONE: usize = 3;
347
348 type Cfg = MontyField<K>;
349 type F = MontyFieldElement<K>;
350
351 type Zt = TestZipTypes<N, K, M>;
352 type C = IprsCode<Zt, TestIprsConfig, REP_FACTOR, CHECKED>;
353 static C: LazyLock<C> = LazyLock::new(|| C::new(IPRS_ROW_LEN, IPRS_DEPTH).unwrap());
354
355 type PolyZt = TestBinPolyZipTypes<K, M, DEGREE_PLUS_ONE>;
356 type PolyC = IprsCode<PolyZt, TestIprsConfig, REP_FACTOR, CHECKED>;
357 static POLY_C: LazyLock<PolyC> =
358 LazyLock::new(|| PolyC::new(IPRS_ROW_LEN, IPRS_DEPTH).unwrap());
359
360 type TestZip = ZipPlus<Zt, C>;
361 type TestPolyZip = ZipPlus<PolyZt, PolyC>;
362
363 #[test]
364 fn successful_verification_of_valid_proof() {
365 let num_vars = 10;
366 {
367 let (pp, comm, point_f, eval_f, mut transcript) =
368 setup_full_protocol::<Cfg, N, K, M>(num_vars);
369 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut transcript.fs_transcript);
370
371 let result = TestZip::verify::<_, CHECKED>(
372 &mut transcript,
373 &pp,
374 &comm,
375 &field_cfg,
376 &point_f,
377 &eval_f,
378 );
379 assert!(result.is_ok(), "Verification failed: {result:?}")
380 };
381 {
382 let (pp, comm, point_f, eval_f, mut transcript) =
383 setup_full_protocol_poly::<Cfg, N, K, M, DEGREE_PLUS_ONE>(num_vars);
384 let field_cfg = get_field_cfg::<PolyZt, Cfg>(&mut transcript.fs_transcript);
385
386 let result = TestPolyZip::verify::<_, CHECKED>(
387 &mut transcript,
388 &pp,
389 &comm,
390 &field_cfg,
391 &point_f,
392 &eval_f,
393 );
394
395 assert!(result.is_ok(), "Verification failed: {result:?}");
396 }
397 }
398
399 #[test]
400 #[cfg_attr(miri, ignore)] fn verification_fails_with_incorrect_evaluation() {
402 let num_vars = 10;
403
404 {
405 let (pp, comm, point_f, eval_f, mut transcript) =
406 setup_full_protocol::<Cfg, N, K, M>(num_vars);
407 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut transcript.fs_transcript);
408 let tampered = field_cfg.add(&eval_f, &field_cfg.one());
409
410 let result = TestZip::verify::<_, CHECKED>(
411 &mut transcript,
412 &pp,
413 &comm,
414 &field_cfg,
415 &point_f,
416 &tampered,
417 );
418
419 assert!(result.is_err());
420 }
421
422 {
423 let (pp, comm, point_f, eval_f, mut transcript) =
424 setup_full_protocol_poly::<Cfg, N, K, M, DEGREE_PLUS_ONE>(num_vars);
425 let field_cfg = get_field_cfg::<PolyZt, Cfg>(&mut transcript.fs_transcript);
426 let tampered = field_cfg.add(&eval_f, &field_cfg.one());
427
428 let result = TestPolyZip::verify::<_, CHECKED>(
429 &mut transcript,
430 &pp,
431 &comm,
432 &field_cfg,
433 &point_f,
434 &tampered,
435 );
436
437 assert!(result.is_err());
438 }
439 }
440
441 #[test]
442 #[cfg_attr(miri, ignore)] fn verification_fails_with_tampered_proof() {
444 fn tamper(mut proof: PcsVerifierTranscript) -> PcsVerifierTranscript {
445 let original_i0: Uint<K> = proof.clone().read_const_many(1).unwrap().remove(0);
446 proof.stream.get_mut()[0] ^= 0x01;
450
451 let tampered_i0: Uint<K> = proof.clone().read_const_many(1).unwrap().remove(0);
453 assert_ne!(original_i0, tampered_i0);
454
455 proof
456 }
457 let num_vars = 10;
458
459 {
460 let (pp, comm, point_f, eval_f, proof) = setup_full_protocol::<Cfg, N, K, M>(num_vars);
461 let mut tampered = tamper(proof);
462 let field_cfg = get_field_cfg::<Zt, Cfg>(&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::<Cfg, N, K, M, DEGREE_PLUS_ONE>(num_vars);
477 let mut tampered = tamper(proof);
478 let field_cfg = get_field_cfg::<PolyZt, Cfg>(&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::<Cfg, N, K, M>(num_vars);
498 let field_cfg = get_field_cfg::<Zt, Cfg>(&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::<Cfg, N, K, M, DEGREE_PLUS_ONE>(num_vars);
520 let field_cfg = get_field_cfg::<PolyZt, Cfg>(&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_padded)
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: &Cfg| {
558 let mut invalid_point = vec![];
559 for i in 0..=num_vars {
560 invalid_point.push(cfg.project(&Int::<N>::from(100 + i as i32)));
561 }
562 invalid_point
563 };
564
565 {
566 let (pp, comm, _point_f, eval_f, mut transcript) =
567 setup_full_protocol_poly::<Cfg, N, K, M, DEGREE_PLUS_ONE>(num_vars);
568 let field_cfg = get_field_cfg::<PolyZt, Cfg>(&mut transcript.fs_transcript);
569 let invalid_point = make_invalid_point(&field_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::<Cfg, N, K, M>(num_vars);
586 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut transcript.fs_transcript);
587 let invalid_point = make_invalid_point(&field_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, Cfg>(&mut prover_transcript.fs_transcript);
618
619 let _eval_f = TestZip::prove_single::<Cfg, 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(&FixedConfig::default(), &point)
631 .expect("Failed to evaluate polynomial");
632
633 let point_f: Vec<F> = point.iter().map(|v| field_cfg.project(v)).collect();
634 let eval_mle1_f = field_cfg.project(&eval_mle1);
635
636 let mut verifier_transcript = prover_transcript.into_verification_transcript();
637 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
638 let field_cfg = get_field_cfg::<Zt, Cfg>(&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, Cfg>(&mut prover_transcript.fs_transcript);
677
678 let eval_f = TestZip::prove_single::<Cfg, 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| field_cfg.project(v)).collect();
689
690 let mut verifier_transcript = prover_transcript.into_verification_transcript();
691 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
692 let field_cfg = get_field_cfg::<Zt, Cfg>(&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, Cfg>(&mut prover_transcript.fs_transcript);
719
720 let eval_f = TestZip::prove_single::<Cfg, 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 = field_cfg.add(&eval_f, &field_cfg.one());
731 let point_f: Vec<F> = point.iter().map(|v| field_cfg.project(v)).collect();
732
733 let mut verifier_transcript = prover_transcript.into_verification_transcript();
734 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
735 let field_cfg = get_field_cfg::<Zt, Cfg>(&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, Cfg>(&mut prover_transcript.fs_transcript);
767
768 let eval_f = TestZip::prove_single::<Cfg, 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| field_cfg.project(v)).collect();
779
780 let row_len = pp.linear_code.row_len();
784 let b_section_size = pp.num_rows * <Cfg as WithAssociatedInteger>::Integer::NUM_BYTES;
785 let bytes_per_comb_r = M * size_of::<crypto_bigint::Word>();
786 let combined_row_size = row_len * bytes_per_comb_r;
787 let column_values_start = b_section_size + combined_row_size;
788 let bytes_per_cw = K * size_of::<crypto_bigint::Word>();
789
790 let mut verifier_transcript = prover_transcript.into_verification_transcript();
791 assert!(
792 column_values_start + bytes_per_cw <= verifier_transcript.stream.get_ref().len(),
793 "proof too small to tamper column values"
794 );
795
796 let flip_at = column_values_start + bytes_per_cw / 2;
797 verifier_transcript.stream.get_mut()[flip_at] ^= 0x01;
798
799 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
800 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut verifier_transcript.fs_transcript);
801
802 let res = TestZip::verify::<_, CHECKED>(
803 &mut verifier_transcript,
804 &pp,
805 &comm,
806 &field_cfg,
807 &point_f,
808 &eval_f,
809 );
810
811 match res {
812 Err(ZipError::InvalidPcsOpen(msg)) => {
813 assert_eq!(msg, "Proximity failure");
814 }
815 Ok(()) => panic!("verification unexpectedly succeeded"),
816 Err(e) => panic!("unexpected error: {e:?}"),
817 }
818 }
819
820 #[test]
821 #[cfg_attr(miri, ignore)] fn verification_fails_if_evaluation_consistency_check_is_invalid() {
823 let num_vars = 10;
824 let poly_size: usize = 1 << num_vars;
825 let pp = TestZip::setup(poly_size, C.clone());
826
827 let mle: DenseMultilinearExtension<_> =
828 (0..poly_size as i32).map(Int::<INT_LIMBS>::from).collect();
829
830 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
831
832 let point: Vec<<Zt as ZipTypes>::Pt> = vec![Zero::zero(); num_vars];
833
834 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
835 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut prover_transcript.fs_transcript);
836
837 let eval_f = TestZip::prove_single::<Cfg, CHECKED>(
838 &mut prover_transcript,
839 &pp,
840 &mle,
841 &point,
842 &hint,
843 &field_cfg,
844 )
845 .unwrap();
846
847 let point_f: Vec<F> = point.iter().map(|v| field_cfg.project(v)).collect();
848
849 let flip_at = <Cfg as WithAssociatedInteger>::Integer::NUM_BYTES / 4;
852
853 let mut verifier_transcript = prover_transcript.into_verification_transcript();
854 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
855 get_field_cfg::<Zt, Cfg>(&mut verifier_transcript.fs_transcript);
856 assert!(
857 flip_at < verifier_transcript.stream.get_ref().len(),
858 "proof too small to tamper b section"
859 );
860 verifier_transcript.stream.get_mut()[flip_at] ^= 0x01;
861
862 let res = TestZip::verify::<_, CHECKED>(
863 &mut verifier_transcript,
864 &pp,
865 &comm,
866 &field_cfg,
867 &point_f,
868 &eval_f,
869 );
870
871 match res {
872 Err(ZipError::InvalidPcsOpen(msg)) => {
873 assert_eq!(msg, "Evaluation consistency failure");
874 }
875 Ok(()) => panic!("verification unexpectedly succeeded"),
876 Err(e) => panic!("unexpected error: {e:?}"),
877 }
878 }
879
880 #[test]
881 fn verification_succeeds_for_zero_polynomial() {
882 let num_vars = 10;
883 let poly_size: usize = 1 << num_vars;
884 let pp = TestZip::setup(poly_size, C.clone());
885
886 let mle: DenseMultilinearExtension<_> = vec![Zero::zero(); poly_size].into_iter().collect();
887
888 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
889
890 let point: Vec<<Zt as ZipTypes>::Pt> = vec![Zero::zero(); num_vars];
891
892 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
893 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut prover_transcript.fs_transcript);
894
895 let eval_f = TestZip::prove_single::<Cfg, CHECKED>(
896 &mut prover_transcript,
897 &pp,
898 &mle,
899 &point,
900 &hint,
901 &field_cfg,
902 )
903 .unwrap();
904
905 let point_f: Vec<F> = point.iter().map(|v| field_cfg.project(v)).collect();
906
907 let mut verifier_transcript = prover_transcript.into_verification_transcript();
908 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
909 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut verifier_transcript.fs_transcript);
910
911 let res = TestZip::verify::<_, CHECKED>(
912 &mut verifier_transcript,
913 &pp,
914 &comm,
915 &field_cfg,
916 &point_f,
917 &eval_f,
918 );
919 assert!(res.is_ok());
920 }
921
922 #[test]
923 #[cfg_attr(miri, ignore)] fn verification_succeeds_at_zero_point() {
925 let num_vars = 10;
926 let poly_size: usize = 1 << num_vars;
927 let pp = TestZip::setup(poly_size, C.clone());
928
929 let mle: DenseMultilinearExtension<_> =
930 (1..=poly_size as i32).map(Int::<INT_LIMBS>::from).collect();
931
932 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
933
934 let point: Vec<<Zt as ZipTypes>::Pt> = vec![Zero::zero(); num_vars];
935
936 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
937 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut prover_transcript.fs_transcript);
938
939 let eval_f = TestZip::prove_single::<Cfg, CHECKED>(
940 &mut prover_transcript,
941 &pp,
942 &mle,
943 &point,
944 &hint,
945 &field_cfg,
946 )
947 .unwrap();
948
949 let point_f: Vec<F> = point.iter().map(|v| field_cfg.project(v)).collect();
950
951 let mut verifier_transcript = prover_transcript.into_verification_transcript();
952 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
953 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut verifier_transcript.fs_transcript);
954
955 let res = TestZip::verify::<_, CHECKED>(
956 &mut verifier_transcript,
957 &pp,
958 &comm,
959 &field_cfg,
960 &point_f,
961 &eval_f,
962 );
963 assert!(res.is_ok());
964 }
965
966 #[test]
967 #[cfg_attr(miri, ignore)] fn verification_succeeds_when_polynomial_coefficients_are_max_bit_size() {
969 let num_vars = 10;
970 let (pp, _) = setup_test_params(num_vars);
971
972 let mut evals: Vec<<Zt as ZipTypes>::Eval> =
973 (0..1 << num_vars as i32).map(Int::from).collect();
974 evals[1] = Int::from(i64::MAX);
975 let poly = DenseMultilinearExtension::from_evaluations_vec(num_vars, evals, Zero::zero());
976
977 let (hint, comm) = TestZip::commit_single(&pp, &poly).unwrap();
978
979 let mut point = vec![<Zt as ZipTypes>::Pt::ZERO; num_vars];
980 point[0] = <Zt as ZipTypes>::Pt::ONE;
981
982 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
983 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut prover_transcript.fs_transcript);
984
985 let eval_f = TestZip::prove_single::<Cfg, CHECKED>(
986 &mut prover_transcript,
987 &pp,
988 &poly,
989 &point,
990 &hint,
991 &field_cfg,
992 )
993 .unwrap();
994
995 let point_f: Vec<F> = point.iter().map(|v| field_cfg.project(v)).collect();
996
997 let mut verifier_transcript = prover_transcript.into_verification_transcript();
998 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
999 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut verifier_transcript.fs_transcript);
1000
1001 let verification_result = TestZip::verify::<_, CHECKED>(
1002 &mut verifier_transcript,
1003 &pp,
1004 &comm,
1005 &field_cfg,
1006 &point_f,
1007 &eval_f,
1008 );
1009
1010 assert!(
1011 verification_result.is_ok(),
1012 "Verification failed: {verification_result:?}",
1013 );
1014 }
1015
1016 #[test]
1017 #[cfg_attr(miri, ignore)] fn verification_succeeds_with_minimal_polynomial_size_mu_is_8() {
1019 let num_vars = 10;
1020 let (pp, poly) = setup_test_params(num_vars);
1021
1022 let (hint, comm) = TestZip::commit_single(&pp, &poly).unwrap();
1023
1024 let point: Vec<<Zt as ZipTypes>::Pt> = (1..=num_vars as i32).map(Int::from).collect_vec();
1025
1026 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1027 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut prover_transcript.fs_transcript);
1028
1029 let eval_f = TestZip::prove_single::<Cfg, CHECKED>(
1030 &mut prover_transcript,
1031 &pp,
1032 &poly,
1033 &point,
1034 &hint,
1035 &field_cfg,
1036 )
1037 .unwrap();
1038
1039 let point_f: Vec<F> = point.iter().map(|v| field_cfg.project(v)).collect();
1040
1041 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1042 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
1043 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut verifier_transcript.fs_transcript);
1044
1045 let verification_result = TestZip::verify::<_, CHECKED>(
1046 &mut verifier_transcript,
1047 &pp,
1048 &comm,
1049 &field_cfg,
1050 &point_f,
1051 &eval_f,
1052 );
1053
1054 assert!(verification_result.is_ok());
1055 }
1056
1057 #[test]
1058 #[cfg_attr(miri, ignore)] fn verification_succeeds_for_code_row_length_of_1() {
1060 let num_vars = 8;
1061 macro_rules! make_code {
1062 () => {
1063 IprsCode::new(1, 0).unwrap()
1064 };
1065 }
1066 {
1067 let (pp, comm, point_f, eval_f, mut transcript) =
1068 setup_full_protocol_inner::<Zt, C, Cfg, N>(
1069 num_vars,
1070 |num_vars| {
1071 setup_test_params_inner(num_vars, make_code!(), |poly_size| {
1072 (1..=poly_size as i32).map(Int::from).collect()
1073 })
1074 },
1075 || (0..num_vars).map(|i| Int::from(i as i32 + 2)).collect(),
1076 );
1077 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut transcript.fs_transcript);
1078
1079 let result = TestZip::verify::<_, CHECKED>(
1080 &mut transcript,
1081 &pp,
1082 &comm,
1083 &field_cfg,
1084 &point_f,
1085 &eval_f,
1086 );
1087 assert!(result.is_ok(), "Verification failed: {result:?}")
1088 };
1089 {
1090 let (pp, comm, point_f, eval_f, mut transcript) =
1091 setup_full_protocol_inner::<PolyZt, PolyC, Cfg, N>(
1092 num_vars,
1093 |num_vars| {
1094 setup_test_params_inner(num_vars, make_code!(), |poly_size| {
1095 let degree = DEGREE_PLUS_ONE - 1;
1096 let eval_coeffs: Vec<_> = (1..=(poly_size * degree) as i64)
1097 .map(|v| v.is_odd().into())
1098 .collect_vec();
1099 eval_coeffs
1100 .chunks_exact(degree)
1101 .map(BinaryPoly::new_padded)
1102 .collect_vec()
1103 })
1104 },
1105 || (0..num_vars).map(|i| i as i128 + 2).collect(),
1106 );
1107 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut transcript.fs_transcript);
1108
1109 let result = TestPolyZip::verify::<_, CHECKED>(
1110 &mut transcript,
1111 &pp,
1112 &comm,
1113 &field_cfg,
1114 &point_f,
1115 &eval_f,
1116 );
1117 assert!(result.is_ok(), "Verification failed: {result:?}")
1118 }
1119 }
1120
1121 #[test]
1122 #[cfg_attr(miri, ignore)] fn verification_fails_at_proximity_link_check_if_combined_row_is_corrupted() {
1124 let num_vars = 10;
1125 let poly_size: usize = 1 << num_vars;
1126 let pp = TestZip::setup(poly_size, C.clone());
1127
1128 let mle: DenseMultilinearExtension<_> = (1..=poly_size as i32).map(Int::from).collect();
1129
1130 let (hint, comm) = TestZip::commit_single(&pp, &mle).expect("commit should succeed");
1131
1132 let point: Vec<<Zt as ZipTypes>::Pt> = vec![Zero::zero(); num_vars];
1133
1134 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1135 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut prover_transcript.fs_transcript);
1136
1137 let eval_f = TestZip::prove_single::<Cfg, CHECKED>(
1138 &mut prover_transcript,
1139 &pp,
1140 &mle,
1141 &point,
1142 &hint,
1143 &field_cfg,
1144 )
1145 .unwrap();
1146
1147 let point_f: Vec<F> = point.iter().map(|v| field_cfg.project(v)).collect();
1148
1149 let b_section_size = pp.num_rows * <Cfg as WithAssociatedInteger>::Integer::NUM_BYTES;
1151 let bytes_to_corrupt = M * size_of::<crypto_bigint::Word>();
1152
1153 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1154 assert!(
1155 b_section_size + bytes_to_corrupt <= verifier_transcript.stream.get_ref().len(),
1156 "proof too small to tamper combined_row"
1157 );
1158
1159 for b in &mut verifier_transcript.stream.get_mut()
1160 [b_section_size..b_section_size + bytes_to_corrupt]
1161 {
1162 *b = 0xFF;
1163 }
1164
1165 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
1166 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut verifier_transcript.fs_transcript);
1167
1168 let res = TestZip::verify::<_, CHECKED>(
1169 &mut verifier_transcript,
1170 &pp,
1171 &comm,
1172 &field_cfg,
1173 &point_f,
1174 &eval_f,
1175 );
1176 assert!(res.is_err());
1177 }
1178
1179 #[test]
1181 #[cfg_attr(miri, ignore)] fn bench_p12_verify() {
1183 fn inner<const P: usize>() {
1184 let mut rng = ThreadRng::default();
1185 let poly_size: usize = 1 << P;
1187 let pp = TestZip::setup(poly_size, C.clone());
1188
1189 let mle = DenseMultilinearExtension::rand(P, &mut rng);
1190 let (hint, commitment) = TestZip::commit_single(&pp, &mle).expect("commit");
1191
1192 let point = vec![1i64; P].iter().map(|v| v.into()).collect_vec();
1193
1194 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&commitment);
1195 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut prover_transcript.fs_transcript);
1196
1197 let eval_f = TestZip::prove_single::<Cfg, CHECKED>(
1198 &mut prover_transcript,
1199 &pp,
1200 &mle,
1201 &point,
1202 &hint,
1203 &field_cfg,
1204 )
1205 .unwrap();
1206
1207 let point_f: Vec<F> = point.iter().map(|v| field_cfg.project(v)).collect();
1208
1209 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1210 verifier_transcript
1211 .fs_transcript
1212 .absorb_bytes(&commitment.root);
1213 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut verifier_transcript.fs_transcript);
1214
1215 let zero_f = field_cfg.zero();
1216 let mle_f = DenseMultilinearExtension::from_evaluations_vec(
1217 P,
1218 mle.iter().map(|c| field_cfg.project(c)).collect(),
1219 zero_f,
1220 );
1221 let expected_eval_f = mle_f.evaluate(&field_cfg, &point_f).unwrap();
1222 assert_eq!(eval_f, expected_eval_f, "prover returned wrong eval");
1223
1224 TestZip::verify::<_, CHECKED>(
1225 &mut verifier_transcript,
1226 &pp,
1227 &commitment,
1228 &field_cfg,
1229 &point_f,
1230 &eval_f,
1231 )
1232 .expect("verify");
1233 }
1234
1235 inner::<12>();
1236 }
1237
1238 #[test]
1240 #[cfg_attr(miri, ignore)] fn bench_p12_verify_poly() {
1242 fn inner<const P: usize>() {
1243 let mut rng = ThreadRng::default();
1244 let poly_size: usize = 1 << P;
1246 let pp = TestPolyZip::setup(poly_size, POLY_C.clone());
1247
1248 let mle = DenseMultilinearExtension::rand(P, &mut rng);
1249 let (hint, comm) = TestPolyZip::commit_single(&pp, &mle).expect("commit");
1250
1251 let point = vec![1i64; P].iter().map(|v| (*v).into()).collect_vec();
1252
1253 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1254 let field_cfg = get_field_cfg::<PolyZt, Cfg>(&mut prover_transcript.fs_transcript);
1255
1256 let eval_f = TestPolyZip::prove_single::<Cfg, CHECKED>(
1257 &mut prover_transcript,
1258 &pp,
1259 &mle,
1260 &point,
1261 &hint,
1262 &field_cfg,
1263 )
1264 .unwrap();
1265
1266 let point_f: Vec<F> = point.iter().map(|v| field_cfg.project(v)).collect();
1267
1268 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1269 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
1270 let field_cfg = get_field_cfg::<PolyZt, Cfg>(&mut verifier_transcript.fs_transcript);
1271
1272 TestPolyZip::verify::<_, CHECKED>(
1274 &mut verifier_transcript,
1275 &pp,
1276 &comm,
1277 &field_cfg,
1278 &point_f,
1279 &eval_f,
1280 )
1281 .expect("verify");
1282 }
1283
1284 inner::<12>();
1285 }
1286
1287 fn batched_prove_verify_inner<const BATCH: usize>(num_vars: usize) {
1288 let poly_size = 1 << num_vars;
1289 let pp = TestZip::setup(poly_size, C.clone());
1290
1291 let polys: Vec<DenseMultilinearExtension<_>> = (0..BATCH)
1292 .map(|b| {
1293 let base = (b * poly_size) as i32;
1294 (base + 1..=base + poly_size as i32)
1295 .map(Int::<INT_LIMBS>::from)
1296 .collect()
1297 })
1298 .collect();
1299
1300 let (hint, comm) = TestZip::commit(&pp, &polys).unwrap();
1301 let point: Vec<<Zt as ZipTypes>::Pt> =
1302 (0..num_vars).map(|i| Int::from(i as i32 + 2)).collect();
1303
1304 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1305 let field_cfg = get_field_cfg::<PolyZt, Cfg>(&mut prover_transcript.fs_transcript);
1306
1307 let eval_f = TestZip::prove::<Cfg, CHECKED>(
1308 &mut prover_transcript,
1309 &pp,
1310 &polys,
1311 &point,
1312 &hint,
1313 &field_cfg,
1314 )
1315 .unwrap();
1316
1317 let point_f: Vec<F> = point.iter().map(|v| field_cfg.project(v)).collect();
1318
1319 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1320 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
1321 let field_cfg = get_field_cfg::<PolyZt, Cfg>(&mut verifier_transcript.fs_transcript);
1322
1323 let res = TestZip::verify::<_, CHECKED>(
1324 &mut verifier_transcript,
1325 &pp,
1326 &comm,
1327 &field_cfg,
1328 &point_f,
1329 &eval_f,
1330 );
1331 assert!(
1332 res.is_ok(),
1333 "Batched verify (batch={BATCH}) failed: {res:?}"
1334 );
1335 }
1336
1337 #[test]
1338 fn batched_prove_verify_batch_2() {
1339 batched_prove_verify_inner::<2>(10);
1340 }
1341
1342 #[test]
1343 #[cfg_attr(miri, ignore)] fn batched_prove_verify_batch_5() {
1345 batched_prove_verify_inner::<5>(10);
1346 }
1347
1348 #[test]
1349 #[cfg_attr(miri, ignore)] fn batched_prove_verify_batch_1_roundtrip() {
1351 batched_prove_verify_inner::<1>(10);
1352 }
1353
1354 #[test]
1355 #[cfg_attr(miri, ignore)] fn batched_verify_fails_with_tampered_eval() {
1357 let num_vars = 10;
1358 let poly_size = 1 << num_vars;
1359 let pp = TestZip::setup(poly_size, C.clone());
1360
1361 let polys: Vec<DenseMultilinearExtension<_>> = vec![
1362 (1..=poly_size as i32).map(Int::from).collect(),
1363 (17..=16 + poly_size as i32).map(Int::from).collect(),
1364 ];
1365
1366 let (hint, comm) = TestZip::commit(&pp, &polys).unwrap();
1367 let point: Vec<<Zt as ZipTypes>::Pt> = (0..num_vars).map(|i| Int::from(i + 2)).collect();
1368
1369 let mut prover_transcript = PcsProverTranscript::new_from_commitment(&comm);
1370 let field_cfg = get_field_cfg::<PolyZt, Cfg>(&mut prover_transcript.fs_transcript);
1371
1372 let eval_f = TestZip::prove::<Cfg, CHECKED>(
1373 &mut prover_transcript,
1374 &pp,
1375 &polys,
1376 &point,
1377 &hint,
1378 &field_cfg,
1379 )
1380 .unwrap();
1381 let tampered_eval = field_cfg.add(&eval_f, &field_cfg.one());
1382
1383 let point_f: Vec<F> = point.iter().map(|v| field_cfg.project(v)).collect();
1384
1385 let mut verifier_transcript = prover_transcript.into_verification_transcript();
1386 verifier_transcript.fs_transcript.absorb_bytes(&comm.root);
1387 let field_cfg = get_field_cfg::<Zt, Cfg>(&mut verifier_transcript.fs_transcript);
1388
1389 let res = TestZip::verify::<_, CHECKED>(
1390 &mut verifier_transcript,
1391 &pp,
1392 &comm,
1393 &field_cfg,
1394 &point_f,
1395 &tampered_eval,
1396 );
1397 assert!(res.is_err(), "Should fail when eval is tampered");
1398 }
1399}