1use alloc::vec::Vec;
26
27use p3_challenger::fs::TranscriptField;
28use p3_challenger::{CanObserve, CanSampleUniformBits, FieldChallenger, GrindingChallenger};
29use p3_commit::{Encoder, Mmcs};
30use p3_field::{ExtensionField, Field};
31use p3_matrix::dense::{DenseMatrix, RowMajorMatrix};
32use p3_multilinear_util::point::Point;
33use p3_sumcheck::SumcheckData;
34use p3_sumcheck::layout::{Layout, SuffixResidualProver, Table, Witness};
35
36use crate::PcsLayout;
37use crate::fold::{ChallengeField, FoldAlphabet, fold_codeword_batch};
38use crate::params::BinaryPcsConfig;
39use crate::proof::RoundProof;
40use crate::transcript::BinaryPcsProverTranscript;
41use crate::verifier::flat_coset_indices;
42
43const FOLDING: usize = 0;
49
50const MIN_ENCODED_ARITY: usize = 4;
55
56#[derive(Clone)]
62pub struct BinaryPcsProverData<F: Field, EF: ExtensionField<F>, MT: Mmcs<F>> {
63 pub(crate) layout: PcsLayout<F, EF>,
66 pub(crate) merkle_data: MT::ProverData<DenseMatrix<F>>,
69}
70
71impl<F, EF, MT> BinaryPcsProverData<F, EF, MT>
72where
73 F: TranscriptField,
74 EF: ExtensionField<F>,
75 MT: Mmcs<F>,
76{
77 pub fn table(&self, id: usize) -> &Table<F> {
79 self.layout.table(id)
80 }
81}
82
83pub(crate) struct RoundCommitment<EF: Field, MX: Mmcs<EF>> {
85 pub commitment: MX::Commitment,
87 pub merkle_data: MX::ProverData<DenseMatrix<EF>>,
89}
90
91#[tracing::instrument(name = "binary pcs commit", skip_all)]
100pub(crate) fn commit<F, EF, E, MT>(
101 config: &BinaryPcsConfig,
102 encoder: &E,
103 mmcs: &MT,
104 witness: Witness<F>,
105) -> (MT::Commitment, BinaryPcsProverData<F, EF, MT>)
106where
107 F: TranscriptField,
108 EF: ExtensionField<F>,
109 E: Encoder<F>,
110 MT: Mmcs<F>,
111{
112 assert_eq!(
113 witness.num_variables(),
114 config.num_variables(),
115 "witness arity must match the config it is committed against"
116 );
117
118 let (layout, commitment, merkle_data) =
119 PcsLayout::<F, EF>::commit(encoder, mmcs, witness, FOLDING, config.log_inv_rate());
120
121 (
122 commitment,
123 BinaryPcsProverData {
124 layout,
125 merkle_data,
126 },
127 )
128}
129
130#[allow(clippy::type_complexity)]
161#[tracing::instrument(name = "binary pcs fold rounds", skip_all)]
162pub(crate) fn fold_rounds_with<const BIND_EACH_ROUND: bool, F, EF, MT, MX, Ch>(
163 prover_data: BinaryPcsProverData<F, EF, MT>,
164 config: &BinaryPcsConfig,
165 mmcs: &MT,
166 round_mmcs: &MX,
167 transcript: &mut BinaryPcsProverTranscript<'_, F, EF, Ch>,
168) -> (
169 MT::ProverData<DenseMatrix<F>>,
170 SumcheckData<F, EF>,
171 Vec<RoundCommitment<EF, MX>>,
172 Point<EF>,
173 Vec<EF>,
174)
175where
176 F: TranscriptField + FoldAlphabet<EF>,
177 EF: ChallengeField<F> + ExtensionField<F> + FoldAlphabet<EF>,
178 MT: Mmcs<F>,
179 MX: Mmcs<EF>,
180 Ch: FieldChallenger<F>
181 + GrindingChallenger<Witness = F>
182 + CanSampleUniformBits<F>
183 + CanObserve<MX::Commitment>,
184{
185 let BinaryPcsProverData {
186 layout,
187 merkle_data,
188 } = prover_data;
189
190 let mut sumcheck_data = SumcheckData::default();
191
192 let (mut sumcheck, mut randomness) = transcript.fold_batch(|challenger| {
195 layout.into_sumcheck_in::<<EF as ChallengeField<F>>::SumcheckRepr, _>(
196 &mut sumcheck_data,
197 0,
198 challenger,
199 )
200 });
201 assert_eq!(
202 randomness.num_variables(),
203 0,
204 "the commit phase runs at zero preprocessing depth, so the sumcheck consumes no head rounds"
205 );
206
207 assert_eq!(
208 mmcs.get_matrices(&merkle_data)[0].width,
209 1,
210 "zero preprocessing depth commits a width-1 codeword"
211 );
212
213 let num_batches = config.num_fold_batches();
216 let mut rounds: Vec<RoundCommitment<EF, MX>> = Vec::with_capacity(num_batches - 1);
217 let mut final_codeword = Vec::new();
218 for (batch, (start, arity)) in config.fold_batches().enumerate() {
219 let _batch_span = tracing::info_span!("fold batch", batch, start, arity).entered();
220 let mut challenges = Vec::with_capacity(arity);
221 for round in start..start + arity {
222 let challenge = tracing::debug_span!("sumcheck round", round).in_scope(|| {
223 transcript.fold_batch(|challenger| {
225 sumcheck.compute_sumcheck_polynomials(&mut sumcheck_data, challenger, 1, 0)
226 })
227 });
228 challenges.push(challenge.as_slice()[0]);
229 randomness.extend(&challenge);
230
231 if BIND_EACH_ROUND {
235 sumcheck.settle();
236 }
237 }
238
239 let folded = tracing::info_span!("fold codeword").in_scope(|| {
255 let encoded = (arity >= MIN_ENCODED_ARITY)
256 .then(|| encode_bound_message(&mut sumcheck, config.log_inv_rate()))
257 .flatten();
258 encoded.unwrap_or_else(|| {
259 if batch == 0 {
260 fold_codeword_batch(&mmcs.get_matrices(&merkle_data)[0].values, &challenges)
261 } else {
262 let source = &rounds[batch - 1].merkle_data;
263 fold_codeword_batch(&round_mmcs.get_matrices(source)[0].values, &challenges)
264 }
265 })
266 });
267 if batch + 1 < num_batches {
268 let (commitment, round_data) = tracing::info_span!("commit folded codeword")
269 .in_scope(|| round_mmcs.commit_matrix(RowMajorMatrix::new(folded, 1)));
270 transcript.oracle_commitment(commitment.clone());
271 rounds.push(RoundCommitment {
272 commitment,
273 merkle_data: round_data,
274 });
275 } else {
276 final_codeword = folded;
277 }
278 }
279
280 #[cfg(debug_assertions)]
291 sumcheck.settle();
292
293 (
294 merkle_data,
295 sumcheck_data,
296 rounds,
297 randomness,
298 final_codeword,
299 )
300}
301
302fn encode_bound_message<F, EF>(
309 sumcheck: &mut SuffixResidualProver<F, EF, <EF as ChallengeField<F>>::SumcheckRepr>,
310 log_inv_rate: usize,
311) -> Option<Vec<EF>>
312where
313 F: Field,
314 EF: ChallengeField<F> + ExtensionField<F>,
315{
316 let column = sumcheck.bound_column()?;
317
318 let mut message = EF::zero_vec(column.len() << log_inv_rate);
320 EF::from_sumcheck_repr(column, &mut message[..column.len()]);
321 let codeword = tracing::info_span!("encode bound message").in_scope(|| {
322 EF::Encoder::default().encode_batch_padded(RowMajorMatrix::new(message, 1), log_inv_rate)
323 });
324 Some(codeword.values)
325}
326
327pub(crate) struct QueryProofs<F: Field, EF: Field, MT: Mmcs<F>, MX: Mmcs<EF>> {
330 pub base_opened_values: Vec<Vec<F>>,
333 pub base_multi_proof: MT::MultiProof,
335 pub rounds: Vec<RoundProof<EF, MX>>,
337 pub pow_witness: F,
339}
340
341#[tracing::instrument(name = "binary pcs open queries", skip_all)]
351pub(crate) fn open_queries<F, EF, MT, MX, Ch>(
352 config: &BinaryPcsConfig,
353 mmcs: &MT,
354 round_mmcs: &MX,
355 transcript: &mut BinaryPcsProverTranscript<'_, F, EF, Ch>,
356 base_merkle_data: &MT::ProverData<DenseMatrix<F>>,
357 rounds: &[RoundCommitment<EF, MX>],
358 final_codeword: &[EF],
359) -> QueryProofs<F, EF, MT, MX>
360where
361 F: TranscriptField,
362 EF: ExtensionField<F>,
363 MT: Mmcs<F>,
364 MX: Mmcs<EF>,
365 Ch: FieldChallenger<F> + GrindingChallenger<Witness = F> + CanSampleUniformBits<F>,
366{
367 assert_eq!(
368 rounds.len(),
369 config.num_fold_batches() - 1,
370 "rounds is the caller's own fold-phase output, never proof-supplied data"
371 );
372
373 transcript.final_codeword(final_codeword);
376 let pow_witness = transcript.query_pow();
377
378 let shift = config.log_folding_factor() - 1;
380 let indices: Vec<usize> = transcript
381 .query_pairs()
382 .into_iter()
383 .map(|pair| pair << shift)
384 .collect();
385 let base_indices = flat_coset_indices(&indices, 0, config.log_folding_factor());
386 let (base_values, base_multi_proof) = mmcs.open_multi_batch(&base_indices, base_merkle_data);
387 let base_opened_values = single_matrix_rows(base_values);
388
389 let opened_rounds = rounds
390 .iter()
391 .zip(config.fold_batches().skip(1))
392 .map(|(round, (start, arity))| {
393 let round_indices = flat_coset_indices(&indices, start, arity);
394 let (values, multi_proof) =
395 round_mmcs.open_multi_batch(&round_indices, &round.merkle_data);
396 RoundProof {
397 commitment: round.commitment.clone(),
398 opened_values: single_matrix_rows(values),
399 multi_proof,
400 }
401 })
402 .collect();
403
404 QueryProofs {
405 base_opened_values,
406 base_multi_proof,
407 rounds: opened_rounds,
408 pow_witness,
409 }
410}
411
412fn single_matrix_rows<T>(values: Vec<Vec<Vec<T>>>) -> Vec<Vec<T>> {
415 values
416 .into_iter()
417 .map(|mut per_matrix| {
418 assert_eq!(per_matrix.len(), 1, "each round commits exactly one matrix");
419 per_matrix.swap_remove(0)
420 })
421 .collect()
422}
423
424#[cfg(test)]
425mod tests {
426 use alloc::vec::Vec;
427 use alloc::{format, vec};
428
429 use p3_binary_dft::{AdditiveRsEncoder, EncodableLevel, NaiveAdditiveNtt};
430 use p3_binary_field::{BinaryField8, BinaryField64, BinaryField128};
431 use p3_challenger::fs::TranscriptField;
432 use p3_challenger::{CanObserve, CanSampleUniformBits, FieldChallenger, GrindingChallenger};
433 use p3_commit::Mmcs;
434 use p3_field::ExtensionField;
435 use p3_matrix::dense::RowMajorMatrix;
436 use p3_multilinear_util::point::Point;
437 use p3_multilinear_util::poly::Poly;
438 use p3_sumcheck::layout::{Layout, PrefixProver, SuffixProver, Table};
439 use p3_sumcheck::{OpeningBatch, SumcheckData};
440 use rand::SeedableRng;
441 use rand::distr::{Distribution, StandardUniform};
442 use rand::rngs::SmallRng;
443
444 use super::{MIN_ENCODED_ARITY, commit, fold_rounds_with};
445 use crate::fold::{ChallengeField, FoldAlphabet, fold_codeword, fold_codeword_batch};
446 use crate::params::{BinaryPcsConfig, BinaryPcsParams};
447 use crate::test_util::{
448 LevelChallenger, LevelMmcs, challenger, level_challenger, level_mmcs, mmcs,
449 narrow_challenger, narrow_mmcs,
450 };
451 use crate::transcript::{BinaryPcsProverTranscript, BinaryPcsShape};
452
453 type F = BinaryField128;
454
455 const NUM_VARIABLES: usize = 8;
456 const LOG_INV_RATE: usize = 2;
457
458 #[test]
461 fn the_codeword_and_the_sumcheck_stay_in_lockstep() {
462 let mut rng = SmallRng::seed_from_u64(21);
463 let table = Table::rand(&mut rng, 1, NUM_VARIABLES);
464 let witness = SuffixProver::<F, F>::new_witness(vec![table], 0);
465
466 let mut ch = challenger();
467 let (mut layout, _root, data) = SuffixProver::<F, F>::commit(
468 &AdditiveRsEncoder::<F, NaiveAdditiveNtt<F>>::default(),
469 &mmcs(),
470 witness,
471 0,
472 LOG_INV_RATE,
473 );
474 let m = mmcs().get_matrices(&data)[0].clone();
475 assert_eq!(m.width, 1);
476 let mut codeword = m.values;
477
478 let _virtual_eval = layout.add_virtual_eval(&mut ch);
479 let mut sc = SumcheckData::<F, F>::default();
480 let (mut prover, head) = layout.into_sumcheck(&mut sc, 0, &mut ch);
481 assert_eq!(head.num_variables(), 0);
482
483 for _ in 0..prover.num_variables() {
484 let beta = prover.compute_sumcheck_polynomials(&mut sc, &mut ch, 1, 0, None);
485 codeword = fold_codeword(&codeword, beta.as_slice()[0]);
486 }
487
488 let final_value = prover.evals().as_constant().expect("fully folded");
489 assert_eq!(codeword.len(), 1 << LOG_INV_RATE);
490 assert!(codeword.iter().all(|&v| v == final_value));
491 }
492
493 #[test]
503 fn prefix_layout_does_not_stay_in_lockstep() {
504 let mut rng = SmallRng::seed_from_u64(21);
505 let table = Table::rand(&mut rng, 1, NUM_VARIABLES);
506 let witness = PrefixProver::<F, F>::new_witness(vec![table], 0);
507
508 let mut ch = challenger();
509 let (mut layout, _root, data) = PrefixProver::<F, F>::commit(
510 &AdditiveRsEncoder::<F, NaiveAdditiveNtt<F>>::default(),
511 &mmcs(),
512 witness,
513 0,
514 LOG_INV_RATE,
515 );
516 let m = mmcs().get_matrices(&data)[0].clone();
517 assert_eq!(m.width, 1);
518 let mut codeword = m.values;
519
520 let _virtual_eval = layout.add_virtual_eval(&mut ch);
521 let mut sc = SumcheckData::<F, F>::default();
522 let (mut prover, head) = layout.into_sumcheck(&mut sc, 0, &mut ch);
523 assert_eq!(head.num_variables(), 0);
524
525 for _ in 0..prover.num_variables() {
526 let beta = prover.compute_sumcheck_polynomials(&mut sc, &mut ch, 1, 0, None);
527 codeword = fold_codeword(&codeword, beta.as_slice()[0]);
528 }
529
530 let final_value = prover.evals().as_constant().expect("fully folded");
531 assert!(!codeword.iter().all(|&v| v == final_value));
532 }
533
534 #[test]
541 fn commit_and_fold_rounds_match_an_independent_message_fold() {
542 let mut rng = SmallRng::seed_from_u64(0xC0FFEE);
543 let poly = Poly::<F>::rand(&mut rng, NUM_VARIABLES);
544 let mut message = poly.clone();
545 let table = Table::new(RowMajorMatrix::new(poly.into_evals(), 1 << NUM_VARIABLES));
546 let witness = SuffixProver::<F, F>::new_witness(vec![table], 0);
547
548 let params = BinaryPcsParams {
549 log_inv_rate: LOG_INV_RATE,
550 pow_bits: 4,
551 security_level: 40,
552 };
553 let config = BinaryPcsConfig::try_new::<F, F>(NUM_VARIABLES, params).unwrap();
554 let encoder = AdditiveRsEncoder::<F, NaiveAdditiveNtt<F>>::default();
555 let mmcs_instance = mmcs();
556
557 let mut ch = challenger();
558 let (_commitment, prover_data) =
559 commit::<F, F, _, _>(&config, &encoder, &mmcs_instance, witness);
560 let mut transcript = BinaryPcsProverTranscript::new(&mut ch, BinaryPcsShape::new(&config));
561 let (_merkle_data, _sumcheck_data, rounds, randomness, final_codeword) =
562 fold_rounds_with::<false, F, F, _, _, _>(
563 prover_data,
564 &config,
565 &mmcs_instance,
566 &mmcs_instance,
567 &mut transcript,
568 );
569 transcript.abort();
571
572 assert_eq!(rounds.len(), config.num_fold_rounds() - 1);
573 assert_eq!(randomness.num_variables(), NUM_VARIABLES);
574 assert_eq!(final_codeword.len(), 1 << LOG_INV_RATE);
575
576 for &beta in randomness.as_slice() {
577 message.fix_suffix_var_mut(beta);
578 }
579 let expected = message.as_constant().expect("fully folded");
580 assert!(final_codeword.iter().all(|&v| v == expected));
581 }
582
583 #[test]
588 fn a_narrow_alphabet_folds_to_the_same_constant_as_its_own_message() {
589 let mut rng = SmallRng::seed_from_u64(0xC0FFEE);
590 let table = Table::rand(&mut rng, 1, NUM_VARIABLES);
591
592 let embedded = Poly::new(
594 table
595 .poly(0)
596 .as_slice()
597 .iter()
598 .map(|&value| F::from(value))
599 .collect::<alloc::vec::Vec<_>>(),
600 );
601 let witness = SuffixProver::<BinaryField64, F>::new_witness(vec![table], 0);
602
603 let params = BinaryPcsParams {
604 log_inv_rate: LOG_INV_RATE,
605 pow_bits: 4,
606 security_level: 40,
607 };
608 let config = BinaryPcsConfig::try_new::<BinaryField64, F>(NUM_VARIABLES, params).unwrap();
609 let encoder = <BinaryField64 as EncodableLevel>::Encoder::default();
610
611 let mut ch = narrow_challenger();
613 let (_commitment, prover_data) =
614 commit::<BinaryField64, F, _, _>(&config, &encoder, &narrow_mmcs(), witness);
615 let mut transcript = BinaryPcsProverTranscript::new(&mut ch, BinaryPcsShape::new(&config));
616 let (_merkle_data, _sumcheck_data, _rounds, randomness, final_codeword) =
617 fold_rounds_with::<false, BinaryField64, F, _, _, _>(
618 prover_data,
619 &config,
620 &narrow_mmcs(),
621 &mmcs(),
622 &mut transcript,
623 );
624 transcript.abort();
625
626 let mut message = embedded;
628 for &beta in randomness.as_slice() {
629 message.fix_suffix_var_mut(beta);
630 }
631 let expected = message.as_constant().expect("fully folded");
632 assert_eq!(final_codeword.len(), 1 << LOG_INV_RATE);
633 assert!(final_codeword.iter().all(|&v| v == expected));
634 }
635
636 #[test]
649 fn fold_rounds_agree_with_binding_each_round() {
650 for (num_variables, log_folding_factor) in [(8usize, 1usize), (8, 3), (15, 1), (15, 3)] {
668 let params = BinaryPcsParams {
669 log_inv_rate: LOG_INV_RATE,
670 pow_bits: 4,
671 security_level: 40,
672 };
673 let config = BinaryPcsConfig::try_new_with_folding::<F, F>(
674 num_variables,
675 params,
676 log_folding_factor,
677 )
678 .unwrap();
679
680 let encoder = AdditiveRsEncoder::<F>::default();
684 let mmcs_instance = mmcs();
685
686 let mut rng = SmallRng::seed_from_u64(0xF0FA + num_variables as u64);
687 let table = Table::rand(&mut rng, 1, num_variables);
688 let shape = format!("{num_variables} variables, arity {log_folding_factor}");
689
690 let mut got_ch = challenger();
692 let (_commitment, got_data) = commit::<F, F, _, _>(
693 &config,
694 &encoder,
695 &mmcs_instance,
696 SuffixProver::<F, F>::new_witness(vec![table.clone()], 0),
697 );
698 let mut got_t =
699 BinaryPcsProverTranscript::new(&mut got_ch, BinaryPcsShape::new(&config));
700 let (_, got_sumcheck, got_rounds, got_randomness, got_final) =
701 fold_rounds_with::<false, F, F, _, _, _>(
702 got_data,
703 &config,
704 &mmcs_instance,
705 &mmcs_instance,
706 &mut got_t,
707 );
708 got_t.abort();
710 drop(got_t);
711
712 let mut want_ch = challenger();
714 let (_commitment, want_data) = commit::<F, F, _, _>(
715 &config,
716 &encoder,
717 &mmcs_instance,
718 SuffixProver::<F, F>::new_witness(vec![table], 0),
719 );
720 let mut want_t =
721 BinaryPcsProverTranscript::new(&mut want_ch, BinaryPcsShape::new(&config));
722 let (_, want_sumcheck, want_rounds, want_randomness, want_final) =
723 fold_rounds_with::<true, F, F, _, _, _>(
724 want_data,
725 &config,
726 &mmcs_instance,
727 &mmcs_instance,
728 &mut want_t,
729 );
730 want_t.abort();
731 drop(want_t);
732
733 assert_eq!(
735 got_sumcheck.num_rounds(),
736 want_sumcheck.num_rounds(),
737 "{shape}: round count"
738 );
739 for (round, (got_msg, want_msg)) in got_sumcheck
740 .polynomial_evaluations()
741 .iter()
742 .zip(want_sumcheck.polynomial_evaluations())
743 .enumerate()
744 {
745 assert_eq!(got_msg, want_msg, "{shape}: round {round} message");
746 }
747
748 assert_eq!(
750 got_randomness.as_slice(),
751 want_randomness.as_slice(),
752 "{shape}: folding randomness"
753 );
754 assert_eq!(got_final, want_final, "{shape}: final codeword");
755 for (round, (got_round, want_round)) in got_rounds.iter().zip(&want_rounds).enumerate()
756 {
757 assert_eq!(
758 got_round.commitment, want_round.commitment,
759 "{shape}: round {round} commitment"
760 );
761 }
762
763 assert_eq!(
769 got_ch.sample_algebra_element::<F>(),
770 want_ch.sample_algebra_element::<F>(),
771 "{shape}: transcript state"
772 );
773 }
774 }
775
776 fn check_fold_phase_codewords<const BIND_EACH_ROUND: bool, A, EF>(
783 num_variables: usize,
784 log_folding_factor: usize,
785 log_inv_rate: usize,
786 seed: u64,
787 ) where
788 A: EncodableLevel + TranscriptField + FoldAlphabet<EF>,
789 EF: ChallengeField<A> + ExtensionField<A> + FoldAlphabet<EF>,
790 LevelMmcs<A>: Mmcs<A>,
791 LevelMmcs<EF>: Mmcs<EF>,
792 LevelChallenger<A>: FieldChallenger<A>
793 + GrindingChallenger<Witness = A>
794 + CanSampleUniformBits<A>
795 + CanObserve<<LevelMmcs<EF> as Mmcs<EF>>::Commitment>
796 + Clone,
797 StandardUniform: Distribution<A> + Distribution<EF>,
798 {
799 let params = BinaryPcsParams {
800 log_inv_rate,
801 pow_bits: 0,
802 security_level: 40,
803 };
804 let config = BinaryPcsConfig::try_new_with_folding::<A, EF>(
805 num_variables,
806 params,
807 log_folding_factor,
808 )
809 .unwrap();
810 let (base_mmcs, round_mmcs) = (level_mmcs::<A>(), level_mmcs::<EF>());
811 let shape = format!(
812 "bits {}/{}, {num_variables} variables, arity {log_folding_factor}, rate {log_inv_rate}",
813 A::bits(),
814 EF::bits()
815 );
816
817 let mut rng = SmallRng::seed_from_u64(seed);
818 let table = Table::<A>::rand(&mut rng, 1, num_variables);
819 let (_commitment, mut prover_data) = commit::<A, EF, _, _>(
820 &config,
821 &A::Encoder::default(),
822 &base_mmcs,
823 SuffixProver::<A, EF>::new_witness(vec![table], 0),
824 );
825 let point = Point::<EF>::rand(&mut rng, num_variables);
826 prover_data
827 .layout
828 .record_opening(0, &OpeningBatch::new(vec![0], Vec::new()), &point);
829
830 let mut probe_ch = level_challenger::<A>();
834 let mut probe_sc = SumcheckData::default();
835 let (mut probe, _) = prover_data
836 .layout
837 .clone()
838 .into_sumcheck_in::<EF::SumcheckRepr, _>(&mut probe_sc, 0, &mut probe_ch);
839 let mut holds_bound_column = false;
840 for (batch, (_, arity)) in config.fold_batches().enumerate() {
841 for _ in 0..arity {
842 let _ = probe.compute_sumcheck_polynomials(&mut probe_sc, &mut probe_ch, 1, 0);
843 holds_bound_column |= probe.bound_column().is_some();
844 }
845 if arity >= MIN_ENCODED_ARITY {
846 assert!(
847 probe.bound_column().is_some(),
848 "{shape}: batch {batch} encodes its codeword"
849 );
850 }
851 }
852 assert!(holds_bound_column, "{shape}: banked route");
853
854 let mut ch = level_challenger::<A>();
855 let mut transcript = BinaryPcsProverTranscript::new(&mut ch, BinaryPcsShape::new(&config));
856 let (merkle_data, _sumcheck_data, rounds, randomness, final_codeword) =
857 fold_rounds_with::<BIND_EACH_ROUND, A, EF, _, _, _>(
858 prover_data,
859 &config,
860 &base_mmcs,
861 &round_mmcs,
862 &mut transcript,
863 );
864 transcript.abort();
865
866 let base = base_mmcs.get_matrices(&merkle_data)[0].values.clone();
868 let mut expected: Vec<EF> = Vec::new();
869 for (batch, (start, arity)) in config.fold_batches().enumerate() {
870 let challenges = &randomness.as_slice()[start..start + arity];
871 expected = if batch == 0 {
872 fold_codeword_batch(&base, challenges)
873 } else {
874 fold_codeword_batch(&expected, challenges)
875 };
876 let got = rounds.get(batch).map_or_else(
877 || final_codeword.clone(),
878 |round| {
879 round_mmcs.get_matrices(&round.merkle_data)[0]
880 .values
881 .clone()
882 },
883 );
884 assert_eq!(got, expected, "{shape}: batch {batch}");
885 }
886 }
887
888 #[test]
900 fn every_fold_phase_codeword_is_the_fold_of_the_base_codeword() {
901 for (seed, log_folding_factor) in (0..).zip(1..=4) {
902 for (num_variables, log_inv_rate) in [(13, 1), (13, 2)] {
903 check_fold_phase_codewords::<false, F, F>(
904 num_variables,
905 log_folding_factor,
906 log_inv_rate,
907 seed,
908 );
909 check_fold_phase_codewords::<false, BinaryField64, F>(
910 num_variables,
911 log_folding_factor,
912 log_inv_rate,
913 seed,
914 );
915 check_fold_phase_codewords::<false, BinaryField64, BinaryField64>(
916 num_variables,
917 log_folding_factor,
918 log_inv_rate,
919 seed,
920 );
921 }
922 check_fold_phase_codewords::<false, BinaryField8, BinaryField64>(
923 6,
924 log_folding_factor,
925 2,
926 seed,
927 );
928 check_fold_phase_codewords::<false, BinaryField8, F>(6, log_folding_factor, 2, seed);
929 check_fold_phase_codewords::<true, F, F>(13, log_folding_factor, 1, seed);
930 check_fold_phase_codewords::<true, BinaryField64, BinaryField64>(
931 13,
932 log_folding_factor,
933 1,
934 seed,
935 );
936 }
937 for (seed, num_variables) in [(4, 12), (5, 16)] {
938 check_fold_phase_codewords::<false, F, F>(num_variables, 4, 1, seed);
939 }
940 check_fold_phase_codewords::<false, F, F>(16, 8, 1, 6);
941 }
942
943 #[test]
948 #[should_panic(expected = "witness arity must match the config it is committed against")]
949 fn commit_rejects_a_witness_arity_mismatch() {
950 let params = BinaryPcsParams {
951 log_inv_rate: LOG_INV_RATE,
952 pow_bits: 4,
953 security_level: 40,
954 };
955 let config = BinaryPcsConfig::try_new::<F, F>(NUM_VARIABLES, params).unwrap();
956 let encoder = AdditiveRsEncoder::<F, NaiveAdditiveNtt<F>>::default();
957 let mmcs_instance = mmcs();
958 let mut rng = SmallRng::seed_from_u64(0);
959 let table = Table::rand(&mut rng, 1, NUM_VARIABLES - 1);
960 let witness = SuffixProver::<F, F>::new_witness(vec![table], 0);
961
962 let _ = commit::<F, F, _, _>(&config, &encoder, &mmcs_instance, witness);
963 }
964}