1extern crate alloc;
11use alloc::vec::Vec;
12use core::result::Result;
13
14use lib_q_stark::{
15 Domain,
16 Proof as StarkProof,
17 StarkConfig,
18 StarkGenericConfig,
19 SymbolicAirBuilder,
20 Val,
21 VerificationError,
22 get_log_num_quotient_chunks,
23 prove,
24 verify,
25};
26use lib_q_stark_air::Air;
27use lib_q_stark_challenger::{
28 CanObserve,
29 CanSampleBits,
30 ComplexFieldChallenger,
31 FieldChallenger,
32 GrindingChallenger,
33 Shake256Challenger32,
34};
35use lib_q_stark_commit::{
36 ExtensionMmcs,
37 Pcs,
38 PolynomialSpace,
39};
40use lib_q_stark_field::extension::{
41 BinomialExtensionField,
42 Complex,
43};
44use lib_q_stark_field::{
45 BasedVectorSpace,
46 PrimeCharacteristicRing,
47 TwoAdicField,
48};
49use lib_q_stark_fri::{
50 FriDataExtractor,
51 TwoAdicFriPcs,
52};
53use lib_q_stark_matrix::dense::RowMajorMatrix;
54use lib_q_stark_merkle::MerkleTreeMmcs;
55use lib_q_stark_mersenne31::{
56 Mersenne31,
57 Mersenne31ComplexRadix2Dit,
58};
59use lib_q_stark_shake256::Shake256Hash;
60use lib_q_stark_symmetric::{
61 CompressionFunctionFromHasher,
62 SerializingHasher,
63};
64
65pub type ConfigVal = Complex<Mersenne31>;
68pub type ConfigChallenge = BinomialExtensionField<ConfigVal, 3>;
74pub type ConfigDft = Mersenne31ComplexRadix2Dit;
75pub type DefaultValMmcs = MerkleTreeMmcs<
76 <ConfigVal as lib_q_stark_field::Field>::Packing,
77 u8,
78 SerializingHasher<Shake256Hash>,
79 CompressionFunctionFromHasher<Shake256Hash, 2, 32>,
80 32,
81>;
82pub type DefaultChallengeMmcs = ExtensionMmcs<ConfigVal, ConfigVal, DefaultValMmcs>;
83pub type DefaultPcs = TwoAdicFriPcs<ConfigVal, ConfigDft, DefaultValMmcs, DefaultChallengeMmcs>;
84pub type DefaultConfig =
85 StarkConfig<DefaultPcs, ConfigVal, ComplexFieldChallenger<Shake256Challenger32<Mersenne31>>>;
86
87pub type MembershipChallengeMmcs = ExtensionMmcs<ConfigVal, ConfigChallenge, DefaultValMmcs>;
96pub type MembershipPcs =
97 TwoAdicFriPcs<ConfigVal, ConfigDft, DefaultValMmcs, MembershipChallengeMmcs>;
98pub type MembershipConfig = StarkConfig<
99 MembershipPcs,
100 ConfigChallenge,
101 ComplexFieldChallenger<Shake256Challenger32<Mersenne31>>,
102>;
103
104#[cfg(feature = "recursive-proofs-experimental")]
105use lib_q_stark_merkle::PoseidonMmcs as PoseidonMmcsType;
106#[cfg(feature = "recursive-proofs-experimental")]
107pub type PoseidonChallengeMmcs = ExtensionMmcs<ConfigVal, ConfigVal, PoseidonMmcsType>;
108#[cfg(feature = "recursive-proofs-experimental")]
109pub type PoseidonPcs = TwoAdicFriPcs<ConfigVal, ConfigDft, PoseidonMmcsType, PoseidonChallengeMmcs>;
110#[cfg(feature = "recursive-proofs-experimental")]
111pub type PoseidonConfig =
112 StarkConfig<PoseidonPcs, ConfigVal, ComplexFieldChallenger<Shake256Challenger32<Mersenne31>>>;
113
114use lib_q_stark_fri::HidingFriPcs;
115use lib_q_stark_merkle::MerkleTreeHidingMmcs;
116pub type ZkValMmcs = MerkleTreeHidingMmcs<
117 <ConfigVal as lib_q_stark_field::Field>::Packing,
118 u8,
119 SerializingHasher<Shake256Hash>,
120 CompressionFunctionFromHasher<Shake256Hash, 2, 32>,
121 lib_q_random::Kt128Rng,
122 32,
123 4,
124>;
125pub type ZkChallengeMmcs = ExtensionMmcs<ConfigVal, ConfigVal, ZkValMmcs>;
126pub type ZkPcs =
127 HidingFriPcs<ConfigVal, ConfigDft, ZkValMmcs, ZkChallengeMmcs, lib_q_random::Kt128Rng>;
128pub type ZkConfig =
129 StarkConfig<ZkPcs, ConfigVal, ComplexFieldChallenger<Shake256Challenger32<Mersenne31>>>;
130
131pub type MembershipZkChallengeMmcs = ExtensionMmcs<ConfigVal, ConfigChallenge, ZkValMmcs>;
134pub type MembershipZkPcs = HidingFriPcs<
135 ConfigVal,
136 ConfigDft,
137 ZkValMmcs,
138 MembershipZkChallengeMmcs,
139 lib_q_random::Kt128Rng,
140>;
141pub type MembershipZkConfig = StarkConfig<
142 MembershipZkPcs,
143 ConfigChallenge,
144 ComplexFieldChallenger<Shake256Challenger32<Mersenne31>>,
145>;
146
147#[derive(Clone, Debug)]
149pub struct FriQueryParams {
150 pub num_queries: usize,
151 pub log_blowup: usize,
152 pub log_final_poly_len: usize,
153 pub proof_of_work_bits: usize,
154}
155
156type PcsCommitment<C: StarkGenericConfig> =
158 <C::Pcs as Pcs<C::Challenge, C::Challenger>>::Commitment;
159
160type CommitmentRounds<C: StarkGenericConfig> = Vec<(
161 PcsCommitment<C>,
162 Vec<(Domain<C>, Vec<(C::Challenge, Vec<C::Challenge>)>)>,
163)>;
164
165type QuotientRounds<C: StarkGenericConfig> =
166 Vec<(Domain<C>, Vec<(C::Challenge, Vec<C::Challenge>)>)>;
167
168const MAX_DEGREE_BITS: usize = 30;
177
178fn degree_fits_two_adicity<F: TwoAdicField>(
191 degree_bits: usize,
192 log_num_quotient_chunks: usize,
193 is_zk: usize,
194) -> bool {
195 degree_bits
196 .checked_add(log_num_quotient_chunks)
197 .and_then(|n| n.checked_add(is_zk))
198 .is_some_and(|n| n <= F::TWO_ADICITY)
199}
200
201pub struct StarkProver<C: StarkGenericConfig> {
223 config: C,
224}
225
226impl<C: StarkGenericConfig> StarkProver<C> {
227 pub fn new(config: C) -> Self {
229 Self { config }
230 }
231
232 #[cfg(not(debug_assertions))]
244 pub fn prove<A>(
245 &self,
246 air: &A,
247 trace: RowMajorMatrix<Val<C>>,
248 public_values: &[Val<C>],
249 ) -> Result<StarkProof<C>, lib_q_stark::ProverError>
250 where
251 A: Air<SymbolicAirBuilder<Val<C>>>
252 + for<'a> Air<lib_q_stark::ProverConstraintFolder<'a, C>>,
253 {
254 prove(&self.config, air, trace, public_values)
255 }
256
257 #[cfg(debug_assertions)]
258 pub fn prove<A>(
259 &self,
260 air: &A,
261 trace: RowMajorMatrix<Val<C>>,
262 public_values: &[Val<C>],
263 ) -> Result<StarkProof<C>, lib_q_stark::ProverError>
264 where
265 A: Air<SymbolicAirBuilder<Val<C>>>
266 + for<'a> Air<lib_q_stark::ProverConstraintFolder<'a, C>>
267 + for<'a> Air<lib_q_stark::DebugConstraintBuilder<'a, Val<C>>>,
268 {
269 prove(&self.config, air, trace, public_values)
270 }
271
272 pub fn config(&self) -> &C {
274 &self.config
275 }
276}
277
278pub struct StarkVerifier<C: StarkGenericConfig> {
300 config: C,
301}
302
303impl<C: StarkGenericConfig> StarkVerifier<C> {
304 pub fn new(config: C) -> Self {
306 Self { config }
307 }
308
309 pub fn verify<A>(
321 &self,
322 air: &A,
323 proof: &StarkProof<C>,
324 public_values: &[Val<C>],
325 ) -> Result<(), VerificationError<lib_q_stark::PcsError<C>>>
326 where
327 Val<C>: TwoAdicField,
328 A: Air<SymbolicAirBuilder<Val<C>>>
329 + for<'a> Air<lib_q_stark::VerifierConstraintFolder<'a, C>>,
330 {
331 verify(&self.config, air, proof, public_values)
332 }
333
334 #[allow(clippy::type_complexity)]
340 pub fn derive_challenges<A>(
341 &self,
342 air: &A,
343 proof: &StarkProof<C>,
344 public_values: &[Val<C>],
345 ) -> Result<
346 (
347 C::Challenge,
348 C::Challenge,
349 C::Challenge,
350 Vec<C::Challenge>,
351 ),
352 VerificationError<lib_q_stark::PcsError<C>>,
353 >
354 where
355 A: Air<SymbolicAirBuilder<Val<C>>>
356 + for<'a> Air<lib_q_stark::VerifierConstraintFolder<'a, C>>,
357 Val<C>: TwoAdicField,
358 <<C as StarkGenericConfig>::Pcs as Pcs<C::Challenge, C::Challenger>>::Proof:
359 FriDataExtractor<Challenge = C::Challenge>,
360 C::Challenger: CanObserve<Val<C>>
361 + CanObserve<<C::Pcs as Pcs<C::Challenge, C::Challenger>>::Commitment>
362 + CanObserve<
363 <<<C as StarkGenericConfig>::Pcs as Pcs<C::Challenge, C::Challenger>>::Proof as FriDataExtractor>::Commitment,
364 >,
365{
366 let config = &self.config;
367 let pcs = config.pcs();
368 let commitments = &proof.commitments;
369 let opened_values = &proof.opened_values;
370 let opening_proof = &proof.opening_proof;
371 let degree_bits = proof.degree_bits;
372
373 let preprocessed_width = air
374 .preprocessed_trace()
375 .as_ref()
376 .map(|m| m.width)
377 .unwrap_or(0);
378 if preprocessed_width > 0 {
379 return Err(VerificationError::InvalidProofShape);
380 }
381
382 if degree_bits > MAX_DEGREE_BITS {
386 return Err(VerificationError::InvalidProofShape);
387 }
388 if degree_bits < config.is_zk() {
390 return Err(VerificationError::InvalidProofShape);
391 }
392
393 let degree = 1 << degree_bits;
394 if degree == 0 {
395 return Err(VerificationError::InvalidProofShape);
396 }
397
398 let log_num_quotient_chunks = get_log_num_quotient_chunks::<Val<C>, A>(
399 air,
400 preprocessed_width,
401 public_values.len(),
402 config.is_zk(),
403 );
404 let num_quotient_chunks = 1 << (log_num_quotient_chunks + config.is_zk());
405
406 if !degree_fits_two_adicity::<Val<C>>(degree_bits, log_num_quotient_chunks, config.is_zk())
411 {
412 return Err(VerificationError::InvalidProofShape);
413 }
414
415 let trace_domain: Domain<C> = pcs.natural_domain_for_degree(degree);
416 let init_trace_domain = pcs.natural_domain_for_degree(degree >> config.is_zk());
417
418 if (opened_values.random.is_some() != C::Pcs::ZK) ||
419 (commitments.random.is_some() != C::Pcs::ZK)
420 {
421 return Err(VerificationError::RandomizationError);
422 }
423
424 let air_width = A::width(air);
425 let valid_shape = opened_values.trace_local.len() == air_width &&
426 opened_values.trace_next.len() == air_width &&
427 opened_values.quotient_chunks.len() == num_quotient_chunks &&
428 opened_values
429 .quotient_chunks
430 .iter()
431 .all(|qc| qc.len() == C::Challenge::DIMENSION) &&
432 opened_values
433 .random
434 .as_ref()
435 .is_none_or(|r| r.len() == C::Challenge::DIMENSION);
436 if !valid_shape {
437 return Err(VerificationError::InvalidProofShape);
438 }
439
440 let quotient_domain =
441 trace_domain.create_disjoint_domain(1 << (degree_bits + log_num_quotient_chunks));
442 let quotient_chunks_domains = quotient_domain.split_domains(num_quotient_chunks);
443 let randomized_quotient_chunks_domains: Vec<Domain<C>> = quotient_chunks_domains
444 .iter()
445 .map(|d: &Domain<C>| pcs.natural_domain_for_degree(d.size() << config.is_zk()))
446 .collect();
447
448 let mut challenger = config.initialise_challenger();
449
450 challenger.observe(Val::<C>::from_usize(degree_bits));
451 challenger.observe(Val::<C>::from_usize(degree_bits - config.is_zk()));
452 challenger.observe(Val::<C>::from_usize(preprocessed_width));
453 challenger.observe(Val::<C>::from_usize(A::width(air)));
454 challenger.observe(commitments.trace.clone());
455 challenger.observe_slice(public_values);
456
457 let alpha = challenger.sample_algebra_element();
458 challenger.observe(commitments.quotient_chunks.clone());
459 if let Some(ref r_commit) = commitments.random {
460 challenger.observe(r_commit.clone());
461 }
462
463 let zeta = challenger.sample_algebra_element();
464 let zeta_next = init_trace_domain
465 .next_point(zeta)
466 .ok_or(VerificationError::NextPointUnavailable)?;
467
468 let mut coms_to_verify: CommitmentRounds<C> =
469 if let Some(ref random_commit) = commitments.random {
470 let random_values = opened_values
471 .random
472 .as_ref()
473 .ok_or(VerificationError::RandomizationError)?;
474 alloc::vec![(
475 random_commit.clone(),
476 alloc::vec![(trace_domain, alloc::vec![(zeta, random_values.clone())],)],
477 )]
478 } else {
479 alloc::vec![]
480 };
481
482 coms_to_verify.push((
483 commitments.trace.clone(),
484 alloc::vec![(
485 trace_domain,
486 alloc::vec![
487 (zeta, opened_values.trace_local.clone()),
488 (zeta_next, opened_values.trace_next.clone()),
489 ],
490 )],
491 ));
492
493 let quotient_rounds: QuotientRounds<C> = randomized_quotient_chunks_domains
494 .iter()
495 .zip(opened_values.quotient_chunks.iter())
496 .map(|(domain, values)| (*domain, alloc::vec![(zeta, values.clone())]))
497 .collect();
498 coms_to_verify.push((commitments.quotient_chunks.clone(), quotient_rounds));
499
500 for (_, round) in &coms_to_verify {
501 for (_, mat) in round {
502 for (_, point) in mat {
503 for opening in point {
504 challenger.observe_algebra_element(*opening);
505 }
506 }
507 }
508 }
509
510 let _alpha_fri = challenger.sample_algebra_element::<C::Challenge>();
511
512 let betas: Vec<C::Challenge> = opening_proof
513 .commit_phase_commits()
514 .iter()
515 .map(|comm| {
516 challenger.observe(comm.clone());
517 challenger.sample_algebra_element()
518 })
519 .collect();
520
521 Ok((zeta, zeta_next, alpha, betas))
522 }
523
524 pub fn derive_query_positions<A>(
530 &self,
531 air: &A,
532 proof: &StarkProof<C>,
533 public_values: &[Val<C>],
534 fri_params: &FriQueryParams,
535 ) -> Result<Vec<usize>, VerificationError<lib_q_stark::PcsError<C>>>
536 where
537 A: Air<SymbolicAirBuilder<Val<C>>>
538 + for<'a> Air<lib_q_stark::VerifierConstraintFolder<'a, C>>,
539 Val<C>: TwoAdicField,
540 <<C as StarkGenericConfig>::Pcs as Pcs<C::Challenge, C::Challenger>>::Proof:
541 FriDataExtractor<Challenge = C::Challenge>,
542 C::Challenger: CanObserve<Val<C>>
543 + CanObserve<<C::Pcs as Pcs<C::Challenge, C::Challenger>>::Commitment>
544 + CanObserve<
545 <<<C as StarkGenericConfig>::Pcs as Pcs<C::Challenge, C::Challenger>>::Proof as FriDataExtractor>::Commitment,
546 >
547 + GrindingChallenger<
548 Witness = <<<C as StarkGenericConfig>::Pcs as Pcs<C::Challenge, C::Challenger>>::Proof as FriDataExtractor>::Witness,
549 >,
550 <<<C as StarkGenericConfig>::Pcs as Pcs<C::Challenge, C::Challenger>>::Proof as FriDataExtractor>::Witness: Clone,
551{
552 let config = &self.config;
553 let pcs = config.pcs();
554 let commitments = &proof.commitments;
555 let opened_values = &proof.opened_values;
556 let opening_proof = &proof.opening_proof;
557 let degree_bits = proof.degree_bits;
558
559 let preprocessed_width = air
560 .preprocessed_trace()
561 .as_ref()
562 .map(|m| m.width)
563 .unwrap_or(0);
564 if preprocessed_width > 0 {
565 return Err(VerificationError::InvalidProofShape);
566 }
567
568 if degree_bits > MAX_DEGREE_BITS {
572 return Err(VerificationError::InvalidProofShape);
573 }
574 if degree_bits < config.is_zk() {
576 return Err(VerificationError::InvalidProofShape);
577 }
578
579 let degree = 1 << degree_bits;
580 if degree == 0 {
581 return Err(VerificationError::InvalidProofShape);
582 }
583
584 let log_num_quotient_chunks = get_log_num_quotient_chunks::<Val<C>, A>(
585 air,
586 preprocessed_width,
587 public_values.len(),
588 config.is_zk(),
589 );
590 let num_quotient_chunks = 1 << (log_num_quotient_chunks + config.is_zk());
591
592 if !degree_fits_two_adicity::<Val<C>>(degree_bits, log_num_quotient_chunks, config.is_zk())
594 {
595 return Err(VerificationError::InvalidProofShape);
596 }
597
598 if (opened_values.random.is_some() != C::Pcs::ZK) ||
599 (commitments.random.is_some() != C::Pcs::ZK)
600 {
601 return Err(VerificationError::RandomizationError);
602 }
603
604 let air_width = A::width(air);
605 let valid_shape = opened_values.trace_local.len() == air_width &&
606 opened_values.trace_next.len() == air_width &&
607 opened_values.quotient_chunks.len() == num_quotient_chunks &&
608 opened_values
609 .quotient_chunks
610 .iter()
611 .all(|qc| qc.len() == C::Challenge::DIMENSION) &&
612 opened_values
613 .random
614 .as_ref()
615 .is_none_or(|r| r.len() == C::Challenge::DIMENSION);
616 if !valid_shape {
617 return Err(VerificationError::InvalidProofShape);
618 }
619
620 let trace_domain: Domain<C> = pcs.natural_domain_for_degree(degree);
621 let init_trace_domain = pcs.natural_domain_for_degree(degree >> config.is_zk());
622 let quotient_domain =
623 trace_domain.create_disjoint_domain(1 << (degree_bits + log_num_quotient_chunks));
624 let quotient_chunks_domains = quotient_domain.split_domains(num_quotient_chunks);
625 let randomized_quotient_chunks_domains: Vec<Domain<C>> = quotient_chunks_domains
626 .iter()
627 .map(|d: &Domain<C>| pcs.natural_domain_for_degree(d.size() << config.is_zk()))
628 .collect();
629
630 let mut challenger = config.initialise_challenger();
631
632 challenger.observe(Val::<C>::from_usize(degree_bits));
633 challenger.observe(Val::<C>::from_usize(degree_bits - config.is_zk()));
634 challenger.observe(Val::<C>::from_usize(preprocessed_width));
635 challenger.observe(Val::<C>::from_usize(A::width(air)));
636 challenger.observe(commitments.trace.clone());
637 challenger.observe_slice(public_values);
638
639 let _alpha: Val<C> = challenger.sample_algebra_element();
640 challenger.observe(commitments.quotient_chunks.clone());
641 if let Some(ref r_commit) = commitments.random {
642 challenger.observe(r_commit.clone());
643 }
644
645 let zeta = challenger.sample_algebra_element();
646 let _zeta_next = init_trace_domain
647 .next_point(zeta)
648 .ok_or(VerificationError::NextPointUnavailable)?;
649
650 let mut coms_to_verify: CommitmentRounds<C> =
651 if let Some(ref random_commit) = commitments.random {
652 let random_values = opened_values
653 .random
654 .as_ref()
655 .ok_or(VerificationError::RandomizationError)?;
656 alloc::vec![(
657 random_commit.clone(),
658 alloc::vec![(trace_domain, alloc::vec![(zeta, random_values.clone())],)],
659 )]
660 } else {
661 alloc::vec![]
662 };
663
664 coms_to_verify.push((
665 commitments.trace.clone(),
666 alloc::vec![(
667 trace_domain,
668 alloc::vec![
669 (zeta, opened_values.trace_local.clone()),
670 (
671 init_trace_domain
672 .next_point(zeta)
673 .ok_or(VerificationError::NextPointUnavailable)?,
674 opened_values.trace_next.clone(),
675 ),
676 ],
677 )],
678 ));
679
680 let quotient_rounds: QuotientRounds<C> = randomized_quotient_chunks_domains
681 .iter()
682 .zip(opened_values.quotient_chunks.iter())
683 .map(|(domain, values)| (*domain, alloc::vec![(zeta, values.clone())]))
684 .collect();
685 coms_to_verify.push((commitments.quotient_chunks.clone(), quotient_rounds));
686
687 for (_, round) in &coms_to_verify {
688 for (_, mat) in round {
689 for (_, point) in mat {
690 for opening in point {
691 challenger.observe_algebra_element(*opening);
692 }
693 }
694 }
695 }
696
697 let _alpha_fri = challenger.sample_algebra_element::<C::Challenge>();
698
699 for comm in opening_proof.commit_phase_commits() {
700 challenger.observe(comm.clone());
701 let _beta: C::Challenge = challenger.sample_algebra_element();
702 }
703
704 for coeff in opening_proof.final_poly() {
705 challenger.observe_algebra_element(*coeff);
706 }
707
708 if !challenger.check_witness(
709 fri_params.proof_of_work_bits,
710 opening_proof.pow_witness().clone(),
711 ) {
712 return Err(VerificationError::InvalidProofShape);
713 }
714
715 let log_global_max_height = opening_proof.commit_phase_commits().len() +
716 fri_params.log_blowup +
717 fri_params.log_final_poly_len;
718 const EXTRA_QUERY_INDEX_BITS: usize = 0;
719
720 let mut positions = Vec::with_capacity(fri_params.num_queries);
721 for _ in 0..fri_params.num_queries {
722 let index = challenger.sample_bits(log_global_max_height + EXTRA_QUERY_INDEX_BITS);
723 positions.push(index);
724 }
725
726 Ok(positions)
727 }
728
729 pub fn config(&self) -> &C {
731 &self.config
732 }
733}
734
735pub fn default_config() -> DefaultConfig {
752 use lib_q_stark_fri::FriParameters;
753
754 type ValMmcs = DefaultValMmcs;
755 type ChallengeMmcs = DefaultChallengeMmcs;
756 type Dft = ConfigDft;
757 type Pcs = DefaultPcs;
758 type MyHash = SerializingHasher<Shake256Hash>;
759 type MyCompress = CompressionFunctionFromHasher<Shake256Hash, 2, 32>;
760 type BaseChallenger = Shake256Challenger32<Mersenne31>;
761 type Challenger = ComplexFieldChallenger<BaseChallenger>;
762
763 let shake256 = Shake256Hash {};
764 let hash = MyHash::new(shake256);
765 let compress = MyCompress::new(shake256);
766 let val_mmcs = ValMmcs::new(hash, compress);
767 let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());
768 let dft = Dft::default();
769 let fri_params = FriParameters {
770 log_blowup: 2,
771 log_final_poly_len: 0,
772 num_queries: 100,
773 proof_of_work_bits: 16,
774 mmcs: challenge_mmcs,
775 };
776 let pcs = Pcs::new(dft, val_mmcs, fri_params);
777 let base_challenger = BaseChallenger::from_hasher(Vec::new(), Shake256Hash);
778 let challenger = Challenger::new(base_challenger);
779
780 StarkConfig::new(pcs, challenger)
781}
782
783pub fn membership_config() -> MembershipConfig {
792 use lib_q_stark_fri::FriParameters;
793
794 let shake256 = Shake256Hash {};
795 let hash = SerializingHasher::<Shake256Hash>::new(shake256);
796 let compress = CompressionFunctionFromHasher::<Shake256Hash, 2, 32>::new(shake256);
797 let val_mmcs = DefaultValMmcs::new(hash, compress);
798 let challenge_mmcs = MembershipChallengeMmcs::new(val_mmcs.clone());
799 let dft = ConfigDft::default();
800 let fri_params = FriParameters {
801 log_blowup: 3,
802 log_final_poly_len: 0,
803 num_queries: 96,
804 proof_of_work_bits: 20,
805 mmcs: challenge_mmcs,
806 };
807 let pcs = MembershipPcs::new(dft, val_mmcs, fri_params);
808 let base_challenger = Shake256Challenger32::<Mersenne31>::from_hasher(Vec::new(), Shake256Hash);
809 let challenger = ComplexFieldChallenger::new(base_challenger);
810 StarkConfig::new(pcs, challenger)
811}
812
813pub fn fast_proof_config() -> DefaultConfig {
824 use lib_q_stark_fri::create_test_fri_params;
825
826 type ValMmcs = DefaultValMmcs;
827 type ChallengeMmcs = DefaultChallengeMmcs;
828 type Dft = ConfigDft;
829 type Pcs = DefaultPcs;
830 type MyHash = SerializingHasher<Shake256Hash>;
831 type MyCompress = CompressionFunctionFromHasher<Shake256Hash, 2, 32>;
832 type BaseChallenger = Shake256Challenger32<Mersenne31>;
833 type Challenger = ComplexFieldChallenger<BaseChallenger>;
834
835 let shake256 = Shake256Hash {};
836 let hash = MyHash::new(shake256);
837 let compress = MyCompress::new(shake256);
838 let val_mmcs = ValMmcs::new(hash, compress);
839 let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());
840 let dft = Dft::default();
841 let fri_params = create_test_fri_params(challenge_mmcs, 0);
842 let pcs = Pcs::new(dft, val_mmcs, fri_params);
843 let base_challenger = BaseChallenger::from_hasher(Vec::new(), Shake256Hash);
844 let challenger = Challenger::new(base_challenger);
845
846 StarkConfig::new(pcs, challenger)
847}
848
849pub fn membership_fast_config() -> MembershipConfig {
852 use lib_q_stark_fri::create_test_fri_params;
853
854 let shake256 = Shake256Hash {};
855 let hash = SerializingHasher::<Shake256Hash>::new(shake256);
856 let compress = CompressionFunctionFromHasher::<Shake256Hash, 2, 32>::new(shake256);
857 let val_mmcs = DefaultValMmcs::new(hash, compress);
858 let challenge_mmcs = MembershipChallengeMmcs::new(val_mmcs.clone());
859 let dft = ConfigDft::default();
860 let fri_params = create_test_fri_params(challenge_mmcs, 0);
861 let pcs = MembershipPcs::new(dft, val_mmcs, fri_params);
862 let base_challenger = Shake256Challenger32::<Mersenne31>::from_hasher(Vec::new(), Shake256Hash);
863 let challenger = ComplexFieldChallenger::new(base_challenger);
864 StarkConfig::new(pcs, challenger)
865}
866
867#[cfg(feature = "recursive-proofs-experimental")]
871pub fn poseidon_config() -> PoseidonConfig {
872 use lib_q_stark_fri::FriParameters;
873 use lib_q_stark_merkle::{
874 PoseidonMmcs,
875 poseidon_mmcs_instance,
876 };
877
878 type ValMmcs = PoseidonMmcs;
879 type ChallengeMmcs = PoseidonChallengeMmcs;
880 type Dft = ConfigDft;
881 type Pcs = PoseidonPcs;
882 type BaseChallenger = Shake256Challenger32<Mersenne31>;
883 type Challenger = ComplexFieldChallenger<BaseChallenger>;
884
885 let (hash, compress) = poseidon_mmcs_instance();
886 let val_mmcs = ValMmcs::new(hash, compress);
887 let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());
888 let dft = Dft::default();
889 let fri_params = FriParameters {
890 log_blowup: 2,
891 log_final_poly_len: 0,
892 num_queries: 100,
893 proof_of_work_bits: 16,
894 mmcs: challenge_mmcs,
895 };
896 let pcs = Pcs::new(dft, val_mmcs, fri_params);
897 let base_challenger = BaseChallenger::from_hasher(Vec::new(), Shake256Hash);
898 let challenger = Challenger::new(base_challenger);
899
900 StarkConfig::new(pcs, challenger)
901}
902
903#[cfg(feature = "recursive-proofs-experimental")]
908pub fn poseidon_test_config() -> PoseidonConfig {
909 use lib_q_stark_fri::create_test_fri_params;
910 use lib_q_stark_merkle::{
911 PoseidonMmcs,
912 poseidon_mmcs_instance,
913 };
914
915 type ValMmcs = PoseidonMmcs;
916 type ChallengeMmcs = PoseidonChallengeMmcs;
917 type Dft = ConfigDft;
918 type Pcs = PoseidonPcs;
919 type BaseChallenger = Shake256Challenger32<Mersenne31>;
920 type Challenger = ComplexFieldChallenger<BaseChallenger>;
921
922 let (hash, compress) = poseidon_mmcs_instance();
923 let val_mmcs = ValMmcs::new(hash, compress);
924 let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());
925 let dft = Dft::default();
926 let fri_params = create_test_fri_params(challenge_mmcs, 0);
927 let pcs = Pcs::new(dft, val_mmcs, fri_params);
928 let base_challenger = BaseChallenger::from_hasher(Vec::new(), Shake256Hash);
929 let challenger = Challenger::new(base_challenger);
930
931 StarkConfig::new(pcs, challenger)
932}
933
934#[doc(hidden)]
937pub const fn default_fri_params_for_tests() -> (usize, usize, usize) {
938 (2, 100, 16)
939}
940
941pub fn zk_config() -> ZkConfig {
944 zk_config_with_seeds(0, 1)
945}
946
947#[doc(hidden)]
949pub fn zk_config_with_seeds(val_mmcs_seed: u64, pcs_seed: u64) -> ZkConfig {
950 use lib_q_stark_fri::create_test_fri_params_zk;
951
952 type ValMmcs = ZkValMmcs;
953 type ChallengeMmcs = ZkChallengeMmcs;
954 type Dft = ConfigDft;
955 type Pcs = ZkPcs;
956 type MyHash = SerializingHasher<Shake256Hash>;
957 type MyCompress = CompressionFunctionFromHasher<Shake256Hash, 2, 32>;
958 type BaseChallenger = Shake256Challenger32<Mersenne31>;
959 type Challenger = ComplexFieldChallenger<BaseChallenger>;
960
961 let shake256 = Shake256Hash {};
962 let hash = MyHash::new(shake256);
963 let compress = MyCompress::new(shake256);
964 let val_mmcs = ValMmcs::new(
965 hash,
966 compress,
967 lib_q_random::Kt128Rng::from_u64(val_mmcs_seed),
968 );
969 let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());
970 let dft = Dft::default();
971 let fri_params = create_test_fri_params_zk(challenge_mmcs);
972 let pcs = Pcs::new(
973 dft,
974 val_mmcs,
975 fri_params,
976 4,
977 lib_q_random::Kt128Rng::from_u64(pcs_seed),
978 );
979 let base_challenger = BaseChallenger::from_hasher(Vec::new(), Shake256Hash);
980 let challenger = Challenger::new(base_challenger);
981
982 StarkConfig::new(pcs, challenger)
983}
984
985#[doc(hidden)]
993pub fn zk_config_with_params(
994 log_blowup: usize,
995 num_queries: usize,
996 proof_of_work_bits: usize,
997 val_mmcs_seed: u64,
998 pcs_seed: u64,
999) -> ZkConfig {
1000 use lib_q_stark_fri::FriParameters;
1001
1002 type ValMmcs = ZkValMmcs;
1003 type ChallengeMmcs = ZkChallengeMmcs;
1004 type Dft = ConfigDft;
1005 type Pcs = ZkPcs;
1006 type MyHash = SerializingHasher<Shake256Hash>;
1007 type MyCompress = CompressionFunctionFromHasher<Shake256Hash, 2, 32>;
1008 type BaseChallenger = Shake256Challenger32<Mersenne31>;
1009 type Challenger = ComplexFieldChallenger<BaseChallenger>;
1010
1011 let shake256 = Shake256Hash {};
1012 let hash = MyHash::new(shake256);
1013 let compress = MyCompress::new(shake256);
1014 let val_mmcs = ValMmcs::new(
1015 hash,
1016 compress,
1017 lib_q_random::Kt128Rng::from_u64(val_mmcs_seed),
1018 );
1019 let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());
1020 let dft = Dft::default();
1021 let fri_params = FriParameters {
1022 log_blowup,
1023 log_final_poly_len: 0,
1024 num_queries,
1025 proof_of_work_bits,
1026 mmcs: challenge_mmcs,
1027 };
1028 let pcs = Pcs::new(
1029 dft,
1030 val_mmcs,
1031 fri_params,
1032 4,
1033 lib_q_random::Kt128Rng::from_u64(pcs_seed),
1034 );
1035 let base_challenger = BaseChallenger::from_hasher(Vec::new(), Shake256Hash);
1036 let challenger = Challenger::new(base_challenger);
1037
1038 StarkConfig::new(pcs, challenger)
1039}
1040
1041pub fn zk_config_with_seed_bytes(
1048 log_blowup: usize,
1049 num_queries: usize,
1050 proof_of_work_bits: usize,
1051 val_seed: [u8; 32],
1052 pcs_seed: [u8; 32],
1053) -> ZkConfig {
1054 use lib_q_stark_fri::FriParameters;
1055
1056 type ValMmcs = ZkValMmcs;
1057 type ChallengeMmcs = ZkChallengeMmcs;
1058 type Dft = ConfigDft;
1059 type Pcs = ZkPcs;
1060 type MyHash = SerializingHasher<Shake256Hash>;
1061 type MyCompress = CompressionFunctionFromHasher<Shake256Hash, 2, 32>;
1062 type BaseChallenger = Shake256Challenger32<Mersenne31>;
1063 type Challenger = ComplexFieldChallenger<BaseChallenger>;
1064
1065 let shake256 = Shake256Hash {};
1066 let hash = MyHash::new(shake256);
1067 let compress = MyCompress::new(shake256);
1068 let val_mmcs = ValMmcs::new(
1069 hash,
1070 compress,
1071 lib_q_random::Kt128Rng::from_seed_bytes(val_seed),
1072 );
1073 let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());
1074 let dft = Dft::default();
1075 let fri_params = FriParameters {
1076 log_blowup,
1077 log_final_poly_len: 0,
1078 num_queries,
1079 proof_of_work_bits,
1080 mmcs: challenge_mmcs,
1081 };
1082 let pcs = Pcs::new(
1083 dft,
1084 val_mmcs,
1085 fri_params,
1086 4,
1087 lib_q_random::Kt128Rng::from_seed_bytes(pcs_seed),
1088 );
1089 let base_challenger = BaseChallenger::from_hasher(Vec::new(), Shake256Hash);
1090 let challenger = Challenger::new(base_challenger);
1091
1092 StarkConfig::new(pcs, challenger)
1093}
1094
1095pub fn membership_zk_config_with_seed_bytes(
1098 log_blowup: usize,
1099 num_queries: usize,
1100 proof_of_work_bits: usize,
1101 val_seed: [u8; 32],
1102 pcs_seed: [u8; 32],
1103) -> MembershipZkConfig {
1104 use lib_q_stark_fri::FriParameters;
1105
1106 let shake256 = Shake256Hash {};
1107 let hash = SerializingHasher::<Shake256Hash>::new(shake256);
1108 let compress = CompressionFunctionFromHasher::<Shake256Hash, 2, 32>::new(shake256);
1109 let val_mmcs = ZkValMmcs::new(
1110 hash,
1111 compress,
1112 lib_q_random::Kt128Rng::from_seed_bytes(val_seed),
1113 );
1114 let challenge_mmcs = MembershipZkChallengeMmcs::new(val_mmcs.clone());
1115 let dft = ConfigDft::default();
1116 let fri_params = FriParameters {
1117 log_blowup,
1118 log_final_poly_len: 0,
1119 num_queries,
1120 proof_of_work_bits,
1121 mmcs: challenge_mmcs,
1122 };
1123 let pcs = MembershipZkPcs::new(
1124 dft,
1125 val_mmcs,
1126 fri_params,
1127 4,
1128 lib_q_random::Kt128Rng::from_seed_bytes(pcs_seed),
1129 );
1130 let base_challenger = Shake256Challenger32::<Mersenne31>::from_hasher(Vec::new(), Shake256Hash);
1131 let challenger = ComplexFieldChallenger::new(base_challenger);
1132 StarkConfig::new(pcs, challenger)
1133}
1134
1135#[doc(hidden)]
1139pub fn membership_zk_config_with_params(
1140 log_blowup: usize,
1141 num_queries: usize,
1142 proof_of_work_bits: usize,
1143 val_mmcs_seed: u64,
1144 pcs_seed: u64,
1145) -> MembershipZkConfig {
1146 use lib_q_stark_fri::FriParameters;
1147
1148 let shake256 = Shake256Hash {};
1149 let hash = SerializingHasher::<Shake256Hash>::new(shake256);
1150 let compress = CompressionFunctionFromHasher::<Shake256Hash, 2, 32>::new(shake256);
1151 let val_mmcs = ZkValMmcs::new(
1152 hash,
1153 compress,
1154 lib_q_random::Kt128Rng::from_u64(val_mmcs_seed),
1155 );
1156 let challenge_mmcs = MembershipZkChallengeMmcs::new(val_mmcs.clone());
1157 let dft = ConfigDft::default();
1158 let fri_params = FriParameters {
1159 log_blowup,
1160 log_final_poly_len: 0,
1161 num_queries,
1162 proof_of_work_bits,
1163 mmcs: challenge_mmcs,
1164 };
1165 let pcs = MembershipZkPcs::new(
1166 dft,
1167 val_mmcs,
1168 fri_params,
1169 4,
1170 lib_q_random::Kt128Rng::from_u64(pcs_seed),
1171 );
1172 let base_challenger = Shake256Challenger32::<Mersenne31>::from_hasher(Vec::new(), Shake256Hash);
1173 let challenger = ComplexFieldChallenger::new(base_challenger);
1174 StarkConfig::new(pcs, challenger)
1175}
1176
1177#[cfg(test)]
1178mod tests {
1179 extern crate alloc;
1180 use alloc::vec;
1181
1182 use super::*;
1183 use crate::air::{
1184 ArithmeticAir,
1185 TraceGenerator,
1186 };
1187
1188 fn sample_arithmetic_proof() -> (ArithmeticAir, StarkProof<DefaultConfig>, Vec<ConfigVal>) {
1189 let air = ArithmeticAir::new(1).expect("ArithmeticAir");
1190 let input = vec![(ConfigVal::ONE, ConfigVal::ONE)];
1191 let trace = air.generate_trace(&input).expect("trace");
1192 let public_values = air.public_values(&input);
1193 let proof = StarkProver::new(default_config())
1194 .prove(&air, trace, &public_values)
1195 .expect("proof generation");
1196 (air, proof, public_values)
1197 }
1198
1199 #[test]
1200 fn test_stark_prover_creation() {
1201 let config = default_config();
1202 let _prover = StarkProver::new(config);
1203 }
1205
1206 #[test]
1207 fn test_stark_verifier_creation() {
1208 let config = default_config();
1209 let _verifier = StarkVerifier::new(config);
1210 }
1212
1213 #[test]
1214 fn test_default_config() {
1215 let _config = default_config();
1216 }
1218
1219 #[test]
1220 fn test_default_fri_params_for_tests_values() {
1221 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
1222 assert_eq!(log_blowup, 2);
1223 assert_eq!(num_queries, 100);
1224 assert_eq!(proof_of_work_bits, 16);
1225 }
1226
1227 #[test]
1228 fn test_zk_config_builders_create_zk_configs() {
1229 let zk_a = zk_config();
1230 let zk_b = zk_config_with_seeds(11, 29);
1231 assert_eq!(zk_a.is_zk(), 1);
1232 assert_eq!(zk_b.is_zk(), 1);
1233 }
1234
1235 #[test]
1236 fn test_prover_and_verifier_config_accessors() {
1237 let prover = StarkProver::new(default_config());
1238 let verifier = StarkVerifier::new(default_config());
1239 assert_eq!(prover.config().is_zk(), 0);
1240 assert_eq!(verifier.config().is_zk(), 0);
1241 }
1242
1243 #[test]
1244 fn test_stark_prove_and_verify_roundtrip() {
1245 let (air, proof, public_values) = sample_arithmetic_proof();
1246 let verifier = StarkVerifier::new(default_config());
1247 verifier
1248 .verify(&air, &proof, &public_values)
1249 .expect("proof should verify");
1250 }
1251
1252 #[test]
1253 fn test_derive_challenges_and_query_positions() {
1254 let (air, proof, public_values) = sample_arithmetic_proof();
1255 let verifier = StarkVerifier::new(default_config());
1256
1257 let (_zeta, _zeta_next, _alpha, betas) = verifier
1258 .derive_challenges(&air, &proof, &public_values)
1259 .expect("derive_challenges");
1260
1261 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
1262 assert!(betas.len() <= num_queries);
1263 let fri_params = FriQueryParams {
1264 num_queries,
1265 log_blowup,
1266 log_final_poly_len: 0,
1267 proof_of_work_bits,
1268 };
1269 let positions = verifier
1270 .derive_query_positions(&air, &proof, &public_values, &fri_params)
1271 .expect("derive_query_positions");
1272 assert_eq!(positions.len(), num_queries);
1273 }
1274
1275 #[test]
1276 fn test_derive_query_positions_rejects_wrong_public_values_shape() {
1277 let (air, proof, _public_values) = sample_arithmetic_proof();
1278 let verifier = StarkVerifier::new(default_config());
1279 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
1280 let fri_params = FriQueryParams {
1281 num_queries,
1282 log_blowup,
1283 log_final_poly_len: 0,
1284 proof_of_work_bits,
1285 };
1286 let wrong_public_values = vec![ConfigVal::ZERO; 2];
1287 let result =
1288 verifier.derive_query_positions(&air, &proof, &wrong_public_values, &fri_params);
1289 assert!(result.is_err());
1290 }
1291
1292 #[test]
1293 fn test_derive_challenges_rejects_random_commitment_mismatch() {
1294 let (air, mut proof, public_values) = sample_arithmetic_proof();
1295 let verifier = StarkVerifier::new(default_config());
1296
1297 proof.commitments.random = Some(proof.commitments.trace.clone());
1298 let result = verifier.derive_challenges(&air, &proof, &public_values);
1299 assert!(matches!(result, Err(VerificationError::RandomizationError)));
1300 }
1301
1302 #[test]
1303 fn test_derive_challenges_rejects_random_values_mismatch() {
1304 let (air, mut proof, public_values) = sample_arithmetic_proof();
1305 let verifier = StarkVerifier::new(default_config());
1306
1307 proof.opened_values.random = Some(vec![ConfigVal::ZERO]);
1308 let result = verifier.derive_challenges(&air, &proof, &public_values);
1309 assert!(matches!(result, Err(VerificationError::RandomizationError)));
1310 }
1311
1312 #[test]
1313 fn test_derive_challenges_rejects_invalid_trace_shape() {
1314 let (air, mut proof, public_values) = sample_arithmetic_proof();
1315 let verifier = StarkVerifier::new(default_config());
1316
1317 let _ = proof.opened_values.trace_local.pop();
1318 let result = verifier.derive_challenges(&air, &proof, &public_values);
1319 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
1320 }
1321
1322 #[test]
1323 fn test_derive_challenges_rejects_invalid_quotient_chunk_shape() {
1324 let (air, mut proof, public_values) = sample_arithmetic_proof();
1325 let verifier = StarkVerifier::new(default_config());
1326
1327 proof.opened_values.quotient_chunks.clear();
1328 let result = verifier.derive_challenges(&air, &proof, &public_values);
1329 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
1330 }
1331
1332 #[test]
1350 fn test_derive_challenges_rejects_degree_bits_exceeding_two_adicity() {
1351 let (air, mut proof, public_values) = sample_arithmetic_proof();
1352 let verifier = StarkVerifier::new(default_config());
1353
1354 proof.degree_bits = 40;
1355 let result = verifier.derive_challenges(&air, &proof, &public_values);
1356 assert!(
1357 matches!(result, Err(VerificationError::InvalidProofShape)),
1358 "degree_bits = 40 (> Complex<Mersenne31>::TWO_ADICITY = 32) must reject with \
1359 InvalidProofShape, got: {result:?}"
1360 );
1361 }
1362
1363 #[test]
1368 fn test_derive_query_positions_rejects_degree_bits_exceeding_two_adicity() {
1369 let (air, mut proof, public_values) = sample_arithmetic_proof();
1370 let verifier = StarkVerifier::new(default_config());
1371 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
1372 let fri_params = FriQueryParams {
1373 num_queries,
1374 log_blowup,
1375 log_final_poly_len: 0,
1376 proof_of_work_bits,
1377 };
1378
1379 proof.degree_bits = 40;
1380 let result = verifier.derive_query_positions(&air, &proof, &public_values, &fri_params);
1381 assert!(
1382 matches!(result, Err(VerificationError::InvalidProofShape)),
1383 "degree_bits = 40 (> Complex<Mersenne31>::TWO_ADICITY = 32) must reject with \
1384 InvalidProofShape, got: {result:?}"
1385 );
1386 }
1387
1388 #[test]
1389 fn test_derive_query_positions_rejects_random_commitment_mismatch() {
1390 let (air, mut proof, public_values) = sample_arithmetic_proof();
1391 let verifier = StarkVerifier::new(default_config());
1392 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
1393 let fri_params = FriQueryParams {
1394 num_queries,
1395 log_blowup,
1396 log_final_poly_len: 0,
1397 proof_of_work_bits,
1398 };
1399
1400 proof.commitments.random = Some(proof.commitments.trace.clone());
1401 let result = verifier.derive_query_positions(&air, &proof, &public_values, &fri_params);
1402 assert!(matches!(result, Err(VerificationError::RandomizationError)));
1403 }
1404
1405 #[test]
1406 fn test_derive_query_positions_rejects_random_values_without_commitment() {
1407 let (air, mut proof, public_values) = sample_arithmetic_proof();
1408 let verifier = StarkVerifier::new(default_config());
1409 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
1410 let fri_params = FriQueryParams {
1411 num_queries,
1412 log_blowup,
1413 log_final_poly_len: 0,
1414 proof_of_work_bits,
1415 };
1416
1417 proof.opened_values.random = Some(vec![ConfigVal::ZERO]);
1418 let result = verifier.derive_query_positions(&air, &proof, &public_values, &fri_params);
1419 assert!(matches!(result, Err(VerificationError::RandomizationError)));
1420 }
1421
1422 #[test]
1423 fn test_derive_query_positions_rejects_invalid_trace_shape() {
1424 let (air, mut proof, public_values) = sample_arithmetic_proof();
1425 let verifier = StarkVerifier::new(default_config());
1426 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
1427 let fri_params = FriQueryParams {
1428 num_queries,
1429 log_blowup,
1430 log_final_poly_len: 0,
1431 proof_of_work_bits,
1432 };
1433
1434 let _ = proof.opened_values.trace_next.pop();
1435 let result = verifier.derive_query_positions(&air, &proof, &public_values, &fri_params);
1436 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
1437 }
1438
1439 #[test]
1440 fn test_derive_query_positions_rejects_invalid_pow_witness() {
1441 let (air, proof, public_values) = sample_arithmetic_proof();
1442 let verifier = StarkVerifier::new(default_config());
1443 let (log_blowup, num_queries, _proof_of_work_bits) = default_fri_params_for_tests();
1444 let fri_params = FriQueryParams {
1445 num_queries,
1446 log_blowup,
1447 log_final_poly_len: 0,
1448 proof_of_work_bits: 30,
1450 };
1451
1452 let result = verifier.derive_query_positions(&air, &proof, &public_values, &fri_params);
1453 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
1454 }
1455
1456 #[test]
1457 fn test_verify_rejects_invalid_trace_local_shape() {
1458 let (air, mut proof, public_values) = sample_arithmetic_proof();
1459 let verifier = StarkVerifier::new(default_config());
1460 let _ = proof.opened_values.trace_local.pop();
1461 let result = verifier.verify(&air, &proof, &public_values);
1462 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
1463 }
1464
1465 #[test]
1466 fn test_verify_rejects_invalid_trace_next_shape() {
1467 let (air, mut proof, public_values) = sample_arithmetic_proof();
1468 let verifier = StarkVerifier::new(default_config());
1469 let _ = proof.opened_values.trace_next.pop();
1470 let result = verifier.verify(&air, &proof, &public_values);
1471 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
1472 }
1473
1474 #[test]
1475 fn test_verify_rejects_invalid_quotient_chunk_shape() {
1476 let (air, mut proof, public_values) = sample_arithmetic_proof();
1477 let verifier = StarkVerifier::new(default_config());
1478 proof.opened_values.quotient_chunks.clear();
1479 let result = verifier.verify(&air, &proof, &public_values);
1480 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
1481 }
1482
1483 #[test]
1484 fn test_verify_rejects_random_commitment_mismatch() {
1485 let (air, mut proof, public_values) = sample_arithmetic_proof();
1486 let verifier = StarkVerifier::new(default_config());
1487 proof.commitments.random = Some(proof.commitments.trace.clone());
1488 let result = verifier.verify(&air, &proof, &public_values);
1489 assert!(matches!(result, Err(VerificationError::RandomizationError)));
1490 }
1491
1492 #[test]
1493 fn test_verify_rejects_random_values_mismatch() {
1494 let (air, mut proof, public_values) = sample_arithmetic_proof();
1495 let verifier = StarkVerifier::new(default_config());
1496 proof.opened_values.random = Some(vec![ConfigVal::ZERO]);
1497 let result = verifier.verify(&air, &proof, &public_values);
1498 assert!(matches!(result, Err(VerificationError::RandomizationError)));
1499 }
1500
1501 #[test]
1517 fn test_degree_fits_two_adicity_accepts_exact_boundary() {
1518 assert!(degree_fits_two_adicity::<ConfigVal>(30, 1, 1));
1520 }
1521
1522 #[test]
1523 fn test_degree_fits_two_adicity_rejects_one_past_boundary() {
1524 assert!(!degree_fits_two_adicity::<ConfigVal>(30, 1, 2));
1526 }
1527
1528 #[test]
1529 fn test_degree_fits_two_adicity_rejects_degree_bits_within_max_degree_bits_but_over_field() {
1530 const {
1535 assert!(
1536 30 <= MAX_DEGREE_BITS,
1537 "test assumption: 30 must not trip MAX_DEGREE_BITS alone"
1538 )
1539 };
1540 assert!(!degree_fits_two_adicity::<ConfigVal>(30, 3, 0));
1541 }
1542
1543 #[test]
1544 fn test_degree_fits_two_adicity_accepts_well_under_boundary() {
1545 assert!(degree_fits_two_adicity::<ConfigVal>(10, 2, 1));
1546 }
1547
1548 #[test]
1549 fn test_degree_fits_two_adicity_rejects_zero_degree_bits_with_large_quotient_chunks() {
1550 assert!(!degree_fits_two_adicity::<ConfigVal>(0, 33, 0));
1552 }
1553
1554 #[test]
1555 fn test_degree_fits_two_adicity_does_not_panic_on_usize_max() {
1556 assert!(!degree_fits_two_adicity::<ConfigVal>(
1559 usize::MAX,
1560 usize::MAX,
1561 usize::MAX
1562 ));
1563 assert!(!degree_fits_two_adicity::<ConfigVal>(usize::MAX, 0, 0));
1564 }
1565}