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::Complex;
41use lib_q_stark_field::{
42 BasedVectorSpace,
43 PrimeCharacteristicRing,
44};
45use lib_q_stark_fri::{
46 FriDataExtractor,
47 TwoAdicFriPcs,
48};
49use lib_q_stark_matrix::dense::RowMajorMatrix;
50use lib_q_stark_merkle::MerkleTreeMmcs;
51use lib_q_stark_mersenne31::{
52 Mersenne31,
53 Mersenne31ComplexRadix2Dit,
54};
55use lib_q_stark_shake256::Shake256Hash;
56use lib_q_stark_symmetric::{
57 CompressionFunctionFromHasher,
58 SerializingHasher,
59};
60
61pub type ConfigVal = Complex<Mersenne31>;
64pub type ConfigDft = Mersenne31ComplexRadix2Dit;
65pub type DefaultValMmcs = MerkleTreeMmcs<
66 <ConfigVal as lib_q_stark_field::Field>::Packing,
67 u8,
68 SerializingHasher<Shake256Hash>,
69 CompressionFunctionFromHasher<Shake256Hash, 2, 32>,
70 32,
71>;
72pub type DefaultChallengeMmcs = ExtensionMmcs<ConfigVal, ConfigVal, DefaultValMmcs>;
73pub type DefaultPcs = TwoAdicFriPcs<ConfigVal, ConfigDft, DefaultValMmcs, DefaultChallengeMmcs>;
74pub type DefaultConfig =
75 StarkConfig<DefaultPcs, ConfigVal, ComplexFieldChallenger<Shake256Challenger32<Mersenne31>>>;
76
77#[cfg(feature = "recursive-proofs-experimental")]
78use lib_q_stark_merkle::PoseidonMmcs as PoseidonMmcsType;
79#[cfg(feature = "recursive-proofs-experimental")]
80pub type PoseidonChallengeMmcs = ExtensionMmcs<ConfigVal, ConfigVal, PoseidonMmcsType>;
81#[cfg(feature = "recursive-proofs-experimental")]
82pub type PoseidonPcs = TwoAdicFriPcs<ConfigVal, ConfigDft, PoseidonMmcsType, PoseidonChallengeMmcs>;
83#[cfg(feature = "recursive-proofs-experimental")]
84pub type PoseidonConfig =
85 StarkConfig<PoseidonPcs, ConfigVal, ComplexFieldChallenger<Shake256Challenger32<Mersenne31>>>;
86
87use lib_q_stark_fri::HidingFriPcs;
88use lib_q_stark_merkle::MerkleTreeHidingMmcs;
89pub type ZkValMmcs = MerkleTreeHidingMmcs<
90 <ConfigVal as lib_q_stark_field::Field>::Packing,
91 u8,
92 SerializingHasher<Shake256Hash>,
93 CompressionFunctionFromHasher<Shake256Hash, 2, 32>,
94 lib_q_random::DeterministicRng,
95 32,
96 4,
97>;
98pub type ZkChallengeMmcs = ExtensionMmcs<ConfigVal, ConfigVal, ZkValMmcs>;
99pub type ZkPcs =
100 HidingFriPcs<ConfigVal, ConfigDft, ZkValMmcs, ZkChallengeMmcs, lib_q_random::DeterministicRng>;
101pub type ZkConfig =
102 StarkConfig<ZkPcs, ConfigVal, ComplexFieldChallenger<Shake256Challenger32<Mersenne31>>>;
103
104#[derive(Clone, Debug)]
106pub struct FriQueryParams {
107 pub num_queries: usize,
108 pub log_blowup: usize,
109 pub log_final_poly_len: usize,
110 pub proof_of_work_bits: usize,
111}
112
113type PcsCommitment<C: StarkGenericConfig> =
115 <C::Pcs as Pcs<C::Challenge, C::Challenger>>::Commitment;
116
117type CommitmentRounds<C: StarkGenericConfig> = Vec<(
118 PcsCommitment<C>,
119 Vec<(Domain<C>, Vec<(C::Challenge, Vec<C::Challenge>)>)>,
120)>;
121
122type QuotientRounds<C: StarkGenericConfig> =
123 Vec<(Domain<C>, Vec<(C::Challenge, Vec<C::Challenge>)>)>;
124
125pub struct StarkProver<C: StarkGenericConfig> {
147 config: C,
148}
149
150impl<C: StarkGenericConfig> StarkProver<C> {
151 pub fn new(config: C) -> Self {
153 Self { config }
154 }
155
156 #[cfg(not(debug_assertions))]
168 pub fn prove<A>(
169 &self,
170 air: &A,
171 trace: RowMajorMatrix<Val<C>>,
172 public_values: &[Val<C>],
173 ) -> Result<StarkProof<C>, lib_q_stark::ProverError>
174 where
175 A: Air<SymbolicAirBuilder<Val<C>>>
176 + for<'a> Air<lib_q_stark::ProverConstraintFolder<'a, C>>,
177 {
178 prove(&self.config, air, trace, public_values)
179 }
180
181 #[cfg(debug_assertions)]
182 pub fn prove<A>(
183 &self,
184 air: &A,
185 trace: RowMajorMatrix<Val<C>>,
186 public_values: &[Val<C>],
187 ) -> Result<StarkProof<C>, lib_q_stark::ProverError>
188 where
189 A: Air<SymbolicAirBuilder<Val<C>>>
190 + for<'a> Air<lib_q_stark::ProverConstraintFolder<'a, C>>
191 + for<'a> Air<lib_q_stark::DebugConstraintBuilder<'a, Val<C>>>,
192 {
193 prove(&self.config, air, trace, public_values)
194 }
195
196 pub fn config(&self) -> &C {
198 &self.config
199 }
200}
201
202pub struct StarkVerifier<C: StarkGenericConfig> {
224 config: C,
225}
226
227impl<C: StarkGenericConfig> StarkVerifier<C> {
228 pub fn new(config: C) -> Self {
230 Self { config }
231 }
232
233 pub fn verify<A>(
245 &self,
246 air: &A,
247 proof: &StarkProof<C>,
248 public_values: &[Val<C>],
249 ) -> Result<(), VerificationError<lib_q_stark::PcsError<C>>>
250 where
251 A: Air<SymbolicAirBuilder<Val<C>>>
252 + for<'a> Air<lib_q_stark::VerifierConstraintFolder<'a, C>>,
253 {
254 verify(&self.config, air, proof, public_values)
255 }
256
257 #[allow(clippy::type_complexity)]
263 pub fn derive_challenges<A>(
264 &self,
265 air: &A,
266 proof: &StarkProof<C>,
267 public_values: &[Val<C>],
268 ) -> Result<
269 (
270 C::Challenge,
271 C::Challenge,
272 C::Challenge,
273 Vec<C::Challenge>,
274 ),
275 VerificationError<lib_q_stark::PcsError<C>>,
276 >
277 where
278 A: Air<SymbolicAirBuilder<Val<C>>>
279 + for<'a> Air<lib_q_stark::VerifierConstraintFolder<'a, C>>,
280 <<C as StarkGenericConfig>::Pcs as Pcs<C::Challenge, C::Challenger>>::Proof:
281 FriDataExtractor<Challenge = C::Challenge>,
282 C::Challenger: CanObserve<Val<C>>
283 + CanObserve<<C::Pcs as Pcs<C::Challenge, C::Challenger>>::Commitment>
284 + CanObserve<
285 <<<C as StarkGenericConfig>::Pcs as Pcs<C::Challenge, C::Challenger>>::Proof as FriDataExtractor>::Commitment,
286 >,
287 {
288 let config = &self.config;
289 let pcs = config.pcs();
290 let commitments = &proof.commitments;
291 let opened_values = &proof.opened_values;
292 let opening_proof = &proof.opening_proof;
293 let degree_bits = proof.degree_bits;
294
295 let preprocessed_width = air
296 .preprocessed_trace()
297 .as_ref()
298 .map(|m| m.width)
299 .unwrap_or(0);
300 if preprocessed_width > 0 {
301 return Err(VerificationError::InvalidProofShape);
302 }
303
304 let degree = 1 << degree_bits;
305 if degree == 0 {
306 return Err(VerificationError::InvalidProofShape);
307 }
308
309 let trace_domain: Domain<C> = pcs.natural_domain_for_degree(degree);
310 let init_trace_domain = pcs.natural_domain_for_degree(degree >> config.is_zk());
311
312 let log_num_quotient_chunks = get_log_num_quotient_chunks::<Val<C>, A>(
313 air,
314 preprocessed_width,
315 public_values.len(),
316 config.is_zk(),
317 );
318 let num_quotient_chunks = 1 << (log_num_quotient_chunks + config.is_zk());
319
320 if (opened_values.random.is_some() != C::Pcs::ZK) ||
321 (commitments.random.is_some() != C::Pcs::ZK)
322 {
323 return Err(VerificationError::RandomizationError);
324 }
325
326 let air_width = A::width(air);
327 let valid_shape = opened_values.trace_local.len() == air_width &&
328 opened_values.trace_next.len() == air_width &&
329 opened_values.quotient_chunks.len() == num_quotient_chunks &&
330 opened_values
331 .quotient_chunks
332 .iter()
333 .all(|qc| qc.len() == C::Challenge::DIMENSION) &&
334 opened_values
335 .random
336 .as_ref()
337 .is_none_or(|r| r.len() == C::Challenge::DIMENSION);
338 if !valid_shape {
339 return Err(VerificationError::InvalidProofShape);
340 }
341
342 let quotient_domain =
343 trace_domain.create_disjoint_domain(1 << (degree_bits + log_num_quotient_chunks));
344 let quotient_chunks_domains = quotient_domain.split_domains(num_quotient_chunks);
345 let randomized_quotient_chunks_domains: Vec<Domain<C>> = quotient_chunks_domains
346 .iter()
347 .map(|d: &Domain<C>| pcs.natural_domain_for_degree(d.size() << config.is_zk()))
348 .collect();
349
350 let mut challenger = config.initialise_challenger();
351
352 challenger.observe(Val::<C>::from_usize(degree_bits));
353 challenger.observe(Val::<C>::from_usize(degree_bits - config.is_zk()));
354 challenger.observe(Val::<C>::from_usize(preprocessed_width));
355 challenger.observe(Val::<C>::from_usize(A::width(air)));
356 challenger.observe(commitments.trace.clone());
357 challenger.observe_slice(public_values);
358
359 let alpha = challenger.sample_algebra_element();
360 challenger.observe(commitments.quotient_chunks.clone());
361 if let Some(ref r_commit) = commitments.random {
362 challenger.observe(r_commit.clone());
363 }
364
365 let zeta = challenger.sample_algebra_element();
366 let zeta_next = init_trace_domain
367 .next_point(zeta)
368 .ok_or(VerificationError::NextPointUnavailable)?;
369
370 let mut coms_to_verify: CommitmentRounds<C> =
371 if let Some(ref random_commit) = commitments.random {
372 let random_values = opened_values
373 .random
374 .as_ref()
375 .ok_or(VerificationError::RandomizationError)?;
376 alloc::vec![(
377 random_commit.clone(),
378 alloc::vec![(trace_domain, alloc::vec![(zeta, random_values.clone())],)],
379 )]
380 } else {
381 alloc::vec![]
382 };
383
384 coms_to_verify.push((
385 commitments.trace.clone(),
386 alloc::vec![(
387 trace_domain,
388 alloc::vec![
389 (zeta, opened_values.trace_local.clone()),
390 (zeta_next, opened_values.trace_next.clone()),
391 ],
392 )],
393 ));
394
395 let quotient_rounds: QuotientRounds<C> = randomized_quotient_chunks_domains
396 .iter()
397 .zip(opened_values.quotient_chunks.iter())
398 .map(|(domain, values)| (*domain, alloc::vec![(zeta, values.clone())]))
399 .collect();
400 coms_to_verify.push((commitments.quotient_chunks.clone(), quotient_rounds));
401
402 for (_, round) in &coms_to_verify {
403 for (_, mat) in round {
404 for (_, point) in mat {
405 for opening in point {
406 challenger.observe_algebra_element(*opening);
407 }
408 }
409 }
410 }
411
412 let _alpha_fri = challenger.sample_algebra_element::<C::Challenge>();
413
414 let betas: Vec<C::Challenge> = opening_proof
415 .commit_phase_commits()
416 .iter()
417 .map(|comm| {
418 challenger.observe(comm.clone());
419 challenger.sample_algebra_element()
420 })
421 .collect();
422
423 Ok((zeta, zeta_next, alpha, betas))
424 }
425
426 pub fn derive_query_positions<A>(
432 &self,
433 air: &A,
434 proof: &StarkProof<C>,
435 public_values: &[Val<C>],
436 fri_params: &FriQueryParams,
437 ) -> Result<Vec<usize>, VerificationError<lib_q_stark::PcsError<C>>>
438 where
439 A: Air<SymbolicAirBuilder<Val<C>>>
440 + for<'a> Air<lib_q_stark::VerifierConstraintFolder<'a, C>>,
441 <<C as StarkGenericConfig>::Pcs as Pcs<C::Challenge, C::Challenger>>::Proof:
442 FriDataExtractor<Challenge = C::Challenge>,
443 C::Challenger: CanObserve<Val<C>>
444 + CanObserve<<C::Pcs as Pcs<C::Challenge, C::Challenger>>::Commitment>
445 + CanObserve<
446 <<<C as StarkGenericConfig>::Pcs as Pcs<C::Challenge, C::Challenger>>::Proof as FriDataExtractor>::Commitment,
447 >
448 + GrindingChallenger<
449 Witness = <<<C as StarkGenericConfig>::Pcs as Pcs<C::Challenge, C::Challenger>>::Proof as FriDataExtractor>::Witness,
450 >,
451 <<<C as StarkGenericConfig>::Pcs as Pcs<C::Challenge, C::Challenger>>::Proof as FriDataExtractor>::Witness: Clone,
452 {
453 let config = &self.config;
454 let pcs = config.pcs();
455 let commitments = &proof.commitments;
456 let opened_values = &proof.opened_values;
457 let opening_proof = &proof.opening_proof;
458 let degree_bits = proof.degree_bits;
459
460 let preprocessed_width = air
461 .preprocessed_trace()
462 .as_ref()
463 .map(|m| m.width)
464 .unwrap_or(0);
465 if preprocessed_width > 0 {
466 return Err(VerificationError::InvalidProofShape);
467 }
468
469 let degree = 1 << degree_bits;
470 if degree == 0 {
471 return Err(VerificationError::InvalidProofShape);
472 }
473
474 let log_num_quotient_chunks = get_log_num_quotient_chunks::<Val<C>, A>(
475 air,
476 preprocessed_width,
477 public_values.len(),
478 config.is_zk(),
479 );
480 let num_quotient_chunks = 1 << (log_num_quotient_chunks + config.is_zk());
481
482 if (opened_values.random.is_some() != C::Pcs::ZK) ||
483 (commitments.random.is_some() != C::Pcs::ZK)
484 {
485 return Err(VerificationError::RandomizationError);
486 }
487
488 let air_width = A::width(air);
489 let valid_shape = opened_values.trace_local.len() == air_width &&
490 opened_values.trace_next.len() == air_width &&
491 opened_values.quotient_chunks.len() == num_quotient_chunks &&
492 opened_values
493 .quotient_chunks
494 .iter()
495 .all(|qc| qc.len() == C::Challenge::DIMENSION) &&
496 opened_values
497 .random
498 .as_ref()
499 .is_none_or(|r| r.len() == C::Challenge::DIMENSION);
500 if !valid_shape {
501 return Err(VerificationError::InvalidProofShape);
502 }
503
504 let trace_domain: Domain<C> = pcs.natural_domain_for_degree(degree);
505 let init_trace_domain = pcs.natural_domain_for_degree(degree >> config.is_zk());
506 let quotient_domain =
507 trace_domain.create_disjoint_domain(1 << (degree_bits + log_num_quotient_chunks));
508 let quotient_chunks_domains = quotient_domain.split_domains(num_quotient_chunks);
509 let randomized_quotient_chunks_domains: Vec<Domain<C>> = quotient_chunks_domains
510 .iter()
511 .map(|d: &Domain<C>| pcs.natural_domain_for_degree(d.size() << config.is_zk()))
512 .collect();
513
514 let mut challenger = config.initialise_challenger();
515
516 challenger.observe(Val::<C>::from_usize(degree_bits));
517 challenger.observe(Val::<C>::from_usize(degree_bits - config.is_zk()));
518 challenger.observe(Val::<C>::from_usize(preprocessed_width));
519 challenger.observe(Val::<C>::from_usize(A::width(air)));
520 challenger.observe(commitments.trace.clone());
521 challenger.observe_slice(public_values);
522
523 let _alpha: Val<C> = challenger.sample_algebra_element();
524 challenger.observe(commitments.quotient_chunks.clone());
525 if let Some(ref r_commit) = commitments.random {
526 challenger.observe(r_commit.clone());
527 }
528
529 let zeta = challenger.sample_algebra_element();
530 let _zeta_next = init_trace_domain
531 .next_point(zeta)
532 .ok_or(VerificationError::NextPointUnavailable)?;
533
534 let mut coms_to_verify: CommitmentRounds<C> =
535 if let Some(ref random_commit) = commitments.random {
536 let random_values = opened_values
537 .random
538 .as_ref()
539 .ok_or(VerificationError::RandomizationError)?;
540 alloc::vec![(
541 random_commit.clone(),
542 alloc::vec![(trace_domain, alloc::vec![(zeta, random_values.clone())],)],
543 )]
544 } else {
545 alloc::vec![]
546 };
547
548 coms_to_verify.push((
549 commitments.trace.clone(),
550 alloc::vec![(
551 trace_domain,
552 alloc::vec![
553 (zeta, opened_values.trace_local.clone()),
554 (
555 init_trace_domain
556 .next_point(zeta)
557 .ok_or(VerificationError::NextPointUnavailable)?,
558 opened_values.trace_next.clone(),
559 ),
560 ],
561 )],
562 ));
563
564 let quotient_rounds: QuotientRounds<C> = randomized_quotient_chunks_domains
565 .iter()
566 .zip(opened_values.quotient_chunks.iter())
567 .map(|(domain, values)| (*domain, alloc::vec![(zeta, values.clone())]))
568 .collect();
569 coms_to_verify.push((commitments.quotient_chunks.clone(), quotient_rounds));
570
571 for (_, round) in &coms_to_verify {
572 for (_, mat) in round {
573 for (_, point) in mat {
574 for opening in point {
575 challenger.observe_algebra_element(*opening);
576 }
577 }
578 }
579 }
580
581 let _alpha_fri = challenger.sample_algebra_element::<C::Challenge>();
582
583 for comm in opening_proof.commit_phase_commits() {
584 challenger.observe(comm.clone());
585 let _beta: C::Challenge = challenger.sample_algebra_element();
586 }
587
588 for coeff in opening_proof.final_poly() {
589 challenger.observe_algebra_element(*coeff);
590 }
591
592 if !challenger.check_witness(
593 fri_params.proof_of_work_bits,
594 opening_proof.pow_witness().clone(),
595 ) {
596 return Err(VerificationError::InvalidProofShape);
597 }
598
599 let log_global_max_height = opening_proof.commit_phase_commits().len() +
600 fri_params.log_blowup +
601 fri_params.log_final_poly_len;
602 const EXTRA_QUERY_INDEX_BITS: usize = 0;
603
604 let mut positions = Vec::with_capacity(fri_params.num_queries);
605 for _ in 0..fri_params.num_queries {
606 let index = challenger.sample_bits(log_global_max_height + EXTRA_QUERY_INDEX_BITS);
607 positions.push(index);
608 }
609
610 Ok(positions)
611 }
612
613 pub fn config(&self) -> &C {
615 &self.config
616 }
617}
618
619pub fn default_config() -> DefaultConfig {
636 use lib_q_stark_fri::FriParameters;
637
638 type ValMmcs = DefaultValMmcs;
639 type ChallengeMmcs = DefaultChallengeMmcs;
640 type Dft = ConfigDft;
641 type Pcs = DefaultPcs;
642 type MyHash = SerializingHasher<Shake256Hash>;
643 type MyCompress = CompressionFunctionFromHasher<Shake256Hash, 2, 32>;
644 type BaseChallenger = Shake256Challenger32<Mersenne31>;
645 type Challenger = ComplexFieldChallenger<BaseChallenger>;
646
647 let shake256 = Shake256Hash {};
648 let hash = MyHash::new(shake256);
649 let compress = MyCompress::new(shake256);
650 let val_mmcs = ValMmcs::new(hash, compress);
651 let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());
652 let dft = Dft::default();
653 let fri_params = FriParameters {
654 log_blowup: 2,
655 log_final_poly_len: 0,
656 num_queries: 100,
657 proof_of_work_bits: 16,
658 mmcs: challenge_mmcs,
659 };
660 let pcs = Pcs::new(dft, val_mmcs, fri_params);
661 let base_challenger = BaseChallenger::from_hasher(Vec::new(), Shake256Hash);
662 let challenger = Challenger::new(base_challenger);
663
664 StarkConfig::new(pcs, challenger)
665}
666
667pub fn fast_proof_config() -> DefaultConfig {
678 use lib_q_stark_fri::create_test_fri_params;
679
680 type ValMmcs = DefaultValMmcs;
681 type ChallengeMmcs = DefaultChallengeMmcs;
682 type Dft = ConfigDft;
683 type Pcs = DefaultPcs;
684 type MyHash = SerializingHasher<Shake256Hash>;
685 type MyCompress = CompressionFunctionFromHasher<Shake256Hash, 2, 32>;
686 type BaseChallenger = Shake256Challenger32<Mersenne31>;
687 type Challenger = ComplexFieldChallenger<BaseChallenger>;
688
689 let shake256 = Shake256Hash {};
690 let hash = MyHash::new(shake256);
691 let compress = MyCompress::new(shake256);
692 let val_mmcs = ValMmcs::new(hash, compress);
693 let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());
694 let dft = Dft::default();
695 let fri_params = create_test_fri_params(challenge_mmcs, 0);
696 let pcs = Pcs::new(dft, val_mmcs, fri_params);
697 let base_challenger = BaseChallenger::from_hasher(Vec::new(), Shake256Hash);
698 let challenger = Challenger::new(base_challenger);
699
700 StarkConfig::new(pcs, challenger)
701}
702
703#[cfg(feature = "recursive-proofs-experimental")]
707pub fn poseidon_config() -> PoseidonConfig {
708 use lib_q_stark_fri::FriParameters;
709 use lib_q_stark_merkle::{
710 PoseidonMmcs,
711 poseidon_mmcs_instance,
712 };
713
714 type ValMmcs = PoseidonMmcs;
715 type ChallengeMmcs = PoseidonChallengeMmcs;
716 type Dft = ConfigDft;
717 type Pcs = PoseidonPcs;
718 type BaseChallenger = Shake256Challenger32<Mersenne31>;
719 type Challenger = ComplexFieldChallenger<BaseChallenger>;
720
721 let (hash, compress) = poseidon_mmcs_instance();
722 let val_mmcs = ValMmcs::new(hash, compress);
723 let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());
724 let dft = Dft::default();
725 let fri_params = FriParameters {
726 log_blowup: 2,
727 log_final_poly_len: 0,
728 num_queries: 100,
729 proof_of_work_bits: 16,
730 mmcs: challenge_mmcs,
731 };
732 let pcs = Pcs::new(dft, val_mmcs, fri_params);
733 let base_challenger = BaseChallenger::from_hasher(Vec::new(), Shake256Hash);
734 let challenger = Challenger::new(base_challenger);
735
736 StarkConfig::new(pcs, challenger)
737}
738
739#[cfg(feature = "recursive-proofs-experimental")]
744pub fn poseidon_test_config() -> PoseidonConfig {
745 use lib_q_stark_fri::create_test_fri_params;
746 use lib_q_stark_merkle::{
747 PoseidonMmcs,
748 poseidon_mmcs_instance,
749 };
750
751 type ValMmcs = PoseidonMmcs;
752 type ChallengeMmcs = PoseidonChallengeMmcs;
753 type Dft = ConfigDft;
754 type Pcs = PoseidonPcs;
755 type BaseChallenger = Shake256Challenger32<Mersenne31>;
756 type Challenger = ComplexFieldChallenger<BaseChallenger>;
757
758 let (hash, compress) = poseidon_mmcs_instance();
759 let val_mmcs = ValMmcs::new(hash, compress);
760 let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());
761 let dft = Dft::default();
762 let fri_params = create_test_fri_params(challenge_mmcs, 0);
763 let pcs = Pcs::new(dft, val_mmcs, fri_params);
764 let base_challenger = BaseChallenger::from_hasher(Vec::new(), Shake256Hash);
765 let challenger = Challenger::new(base_challenger);
766
767 StarkConfig::new(pcs, challenger)
768}
769
770#[doc(hidden)]
773pub const fn default_fri_params_for_tests() -> (usize, usize, usize) {
774 (2, 100, 16)
775}
776
777pub fn zk_config() -> ZkConfig {
780 zk_config_with_seeds(0, 1)
781}
782
783#[doc(hidden)]
785pub fn zk_config_with_seeds(val_mmcs_seed: u64, pcs_seed: u64) -> ZkConfig {
786 use lib_q_stark_fri::create_test_fri_params_zk;
787
788 type ValMmcs = ZkValMmcs;
789 type ChallengeMmcs = ZkChallengeMmcs;
790 type Dft = ConfigDft;
791 type Pcs = ZkPcs;
792 type MyHash = SerializingHasher<Shake256Hash>;
793 type MyCompress = CompressionFunctionFromHasher<Shake256Hash, 2, 32>;
794 type BaseChallenger = Shake256Challenger32<Mersenne31>;
795 type Challenger = ComplexFieldChallenger<BaseChallenger>;
796
797 let shake256 = Shake256Hash {};
798 let hash = MyHash::new(shake256);
799 let compress = MyCompress::new(shake256);
800 let val_mmcs = ValMmcs::new(
801 hash,
802 compress,
803 lib_q_random::DeterministicRng::seed_from_u64(val_mmcs_seed),
804 );
805 let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());
806 let dft = Dft::default();
807 let fri_params = create_test_fri_params_zk(challenge_mmcs);
808 let pcs = Pcs::new(
809 dft,
810 val_mmcs,
811 fri_params,
812 4,
813 lib_q_random::DeterministicRng::seed_from_u64(pcs_seed),
814 );
815 let base_challenger = BaseChallenger::from_hasher(Vec::new(), Shake256Hash);
816 let challenger = Challenger::new(base_challenger);
817
818 StarkConfig::new(pcs, challenger)
819}
820
821#[cfg(test)]
822mod tests {
823 extern crate alloc;
824 use alloc::vec;
825
826 use super::*;
827 use crate::air::{
828 ArithmeticAir,
829 TraceGenerator,
830 };
831
832 fn sample_arithmetic_proof() -> (ArithmeticAir, StarkProof<DefaultConfig>, Vec<ConfigVal>) {
833 let air = ArithmeticAir::new(1).expect("ArithmeticAir");
834 let input = vec![(ConfigVal::ONE, ConfigVal::ONE)];
835 let trace = air.generate_trace(&input).expect("trace");
836 let public_values = air.public_values(&input);
837 let proof = StarkProver::new(default_config())
838 .prove(&air, trace, &public_values)
839 .expect("proof generation");
840 (air, proof, public_values)
841 }
842
843 #[test]
844 fn test_stark_prover_creation() {
845 let config = default_config();
846 let _prover = StarkProver::new(config);
847 }
849
850 #[test]
851 fn test_stark_verifier_creation() {
852 let config = default_config();
853 let _verifier = StarkVerifier::new(config);
854 }
856
857 #[test]
858 fn test_default_config() {
859 let _config = default_config();
860 }
862
863 #[test]
864 fn test_default_fri_params_for_tests_values() {
865 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
866 assert_eq!(log_blowup, 2);
867 assert_eq!(num_queries, 100);
868 assert_eq!(proof_of_work_bits, 16);
869 }
870
871 #[test]
872 fn test_zk_config_builders_create_zk_configs() {
873 let zk_a = zk_config();
874 let zk_b = zk_config_with_seeds(11, 29);
875 assert_eq!(zk_a.is_zk(), 1);
876 assert_eq!(zk_b.is_zk(), 1);
877 }
878
879 #[test]
880 fn test_prover_and_verifier_config_accessors() {
881 let prover = StarkProver::new(default_config());
882 let verifier = StarkVerifier::new(default_config());
883 assert_eq!(prover.config().is_zk(), 0);
884 assert_eq!(verifier.config().is_zk(), 0);
885 }
886
887 #[test]
888 fn test_stark_prove_and_verify_roundtrip() {
889 let (air, proof, public_values) = sample_arithmetic_proof();
890 let verifier = StarkVerifier::new(default_config());
891 verifier
892 .verify(&air, &proof, &public_values)
893 .expect("proof should verify");
894 }
895
896 #[test]
897 fn test_derive_challenges_and_query_positions() {
898 let (air, proof, public_values) = sample_arithmetic_proof();
899 let verifier = StarkVerifier::new(default_config());
900
901 let (_zeta, _zeta_next, _alpha, betas) = verifier
902 .derive_challenges(&air, &proof, &public_values)
903 .expect("derive_challenges");
904
905 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
906 assert!(betas.len() <= num_queries);
907 let fri_params = FriQueryParams {
908 num_queries,
909 log_blowup,
910 log_final_poly_len: 0,
911 proof_of_work_bits,
912 };
913 let positions = verifier
914 .derive_query_positions(&air, &proof, &public_values, &fri_params)
915 .expect("derive_query_positions");
916 assert_eq!(positions.len(), num_queries);
917 }
918
919 #[test]
920 fn test_derive_query_positions_rejects_wrong_public_values_shape() {
921 let (air, proof, _public_values) = sample_arithmetic_proof();
922 let verifier = StarkVerifier::new(default_config());
923 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
924 let fri_params = FriQueryParams {
925 num_queries,
926 log_blowup,
927 log_final_poly_len: 0,
928 proof_of_work_bits,
929 };
930 let wrong_public_values = vec![ConfigVal::ZERO; 2];
931 let result =
932 verifier.derive_query_positions(&air, &proof, &wrong_public_values, &fri_params);
933 assert!(result.is_err());
934 }
935
936 #[test]
937 fn test_derive_challenges_rejects_random_commitment_mismatch() {
938 let (air, mut proof, public_values) = sample_arithmetic_proof();
939 let verifier = StarkVerifier::new(default_config());
940
941 proof.commitments.random = Some(proof.commitments.trace.clone());
942 let result = verifier.derive_challenges(&air, &proof, &public_values);
943 assert!(matches!(result, Err(VerificationError::RandomizationError)));
944 }
945
946 #[test]
947 fn test_derive_challenges_rejects_random_values_mismatch() {
948 let (air, mut proof, public_values) = sample_arithmetic_proof();
949 let verifier = StarkVerifier::new(default_config());
950
951 proof.opened_values.random = Some(vec![ConfigVal::ZERO]);
952 let result = verifier.derive_challenges(&air, &proof, &public_values);
953 assert!(matches!(result, Err(VerificationError::RandomizationError)));
954 }
955
956 #[test]
957 fn test_derive_challenges_rejects_invalid_trace_shape() {
958 let (air, mut proof, public_values) = sample_arithmetic_proof();
959 let verifier = StarkVerifier::new(default_config());
960
961 let _ = proof.opened_values.trace_local.pop();
962 let result = verifier.derive_challenges(&air, &proof, &public_values);
963 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
964 }
965
966 #[test]
967 fn test_derive_challenges_rejects_invalid_quotient_chunk_shape() {
968 let (air, mut proof, public_values) = sample_arithmetic_proof();
969 let verifier = StarkVerifier::new(default_config());
970
971 proof.opened_values.quotient_chunks.clear();
972 let result = verifier.derive_challenges(&air, &proof, &public_values);
973 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
974 }
975
976 #[test]
977 fn test_derive_query_positions_rejects_random_commitment_mismatch() {
978 let (air, mut proof, public_values) = sample_arithmetic_proof();
979 let verifier = StarkVerifier::new(default_config());
980 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
981 let fri_params = FriQueryParams {
982 num_queries,
983 log_blowup,
984 log_final_poly_len: 0,
985 proof_of_work_bits,
986 };
987
988 proof.commitments.random = Some(proof.commitments.trace.clone());
989 let result = verifier.derive_query_positions(&air, &proof, &public_values, &fri_params);
990 assert!(matches!(result, Err(VerificationError::RandomizationError)));
991 }
992
993 #[test]
994 fn test_derive_query_positions_rejects_random_values_without_commitment() {
995 let (air, mut proof, public_values) = sample_arithmetic_proof();
996 let verifier = StarkVerifier::new(default_config());
997 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
998 let fri_params = FriQueryParams {
999 num_queries,
1000 log_blowup,
1001 log_final_poly_len: 0,
1002 proof_of_work_bits,
1003 };
1004
1005 proof.opened_values.random = Some(vec![ConfigVal::ZERO]);
1006 let result = verifier.derive_query_positions(&air, &proof, &public_values, &fri_params);
1007 assert!(matches!(result, Err(VerificationError::RandomizationError)));
1008 }
1009
1010 #[test]
1011 fn test_derive_query_positions_rejects_invalid_trace_shape() {
1012 let (air, mut proof, public_values) = sample_arithmetic_proof();
1013 let verifier = StarkVerifier::new(default_config());
1014 let (log_blowup, num_queries, proof_of_work_bits) = default_fri_params_for_tests();
1015 let fri_params = FriQueryParams {
1016 num_queries,
1017 log_blowup,
1018 log_final_poly_len: 0,
1019 proof_of_work_bits,
1020 };
1021
1022 let _ = proof.opened_values.trace_next.pop();
1023 let result = verifier.derive_query_positions(&air, &proof, &public_values, &fri_params);
1024 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
1025 }
1026
1027 #[test]
1028 fn test_derive_query_positions_rejects_invalid_pow_witness() {
1029 let (air, proof, public_values) = sample_arithmetic_proof();
1030 let verifier = StarkVerifier::new(default_config());
1031 let (log_blowup, num_queries, _proof_of_work_bits) = default_fri_params_for_tests();
1032 let fri_params = FriQueryParams {
1033 num_queries,
1034 log_blowup,
1035 log_final_poly_len: 0,
1036 proof_of_work_bits: 30,
1038 };
1039
1040 let result = verifier.derive_query_positions(&air, &proof, &public_values, &fri_params);
1041 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
1042 }
1043
1044 #[test]
1045 fn test_verify_rejects_invalid_trace_local_shape() {
1046 let (air, mut proof, public_values) = sample_arithmetic_proof();
1047 let verifier = StarkVerifier::new(default_config());
1048 let _ = proof.opened_values.trace_local.pop();
1049 let result = verifier.verify(&air, &proof, &public_values);
1050 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
1051 }
1052
1053 #[test]
1054 fn test_verify_rejects_invalid_trace_next_shape() {
1055 let (air, mut proof, public_values) = sample_arithmetic_proof();
1056 let verifier = StarkVerifier::new(default_config());
1057 let _ = proof.opened_values.trace_next.pop();
1058 let result = verifier.verify(&air, &proof, &public_values);
1059 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
1060 }
1061
1062 #[test]
1063 fn test_verify_rejects_invalid_quotient_chunk_shape() {
1064 let (air, mut proof, public_values) = sample_arithmetic_proof();
1065 let verifier = StarkVerifier::new(default_config());
1066 proof.opened_values.quotient_chunks.clear();
1067 let result = verifier.verify(&air, &proof, &public_values);
1068 assert!(matches!(result, Err(VerificationError::InvalidProofShape)));
1069 }
1070
1071 #[test]
1072 fn test_verify_rejects_random_commitment_mismatch() {
1073 let (air, mut proof, public_values) = sample_arithmetic_proof();
1074 let verifier = StarkVerifier::new(default_config());
1075 proof.commitments.random = Some(proof.commitments.trace.clone());
1076 let result = verifier.verify(&air, &proof, &public_values);
1077 assert!(matches!(result, Err(VerificationError::RandomizationError)));
1078 }
1079
1080 #[test]
1081 fn test_verify_rejects_random_values_mismatch() {
1082 let (air, mut proof, public_values) = sample_arithmetic_proof();
1083 let verifier = StarkVerifier::new(default_config());
1084 proof.opened_values.random = Some(vec![ConfigVal::ZERO]);
1085 let result = verifier.verify(&air, &proof, &public_values);
1086 assert!(matches!(result, Err(VerificationError::RandomizationError)));
1087 }
1088}