1use std::sync::Arc;
31
32use itertools::Itertools;
33use openvm_stark_backend::{
34 any_air_arc_vec,
35 keygen::types::LinearConstraint,
36 poly_common::Squarable,
37 prover::{
38 poly::Ple, stacked_pcs::stacked_commit, whir::prove_whir_opening, AirProvingContext,
39 ColMajorMatrix, CpuColMajorBackend, DeviceDataTransporter, DeviceMultiStarkProvingKey,
40 MatrixDimensions, ProvingContext,
41 },
42 test_utils::{
43 default_test_params_small,
44 dummy_airs::{
45 fib_air::air::FibonacciAir,
46 fib_selector_air::air::FibonacciSelectorAir,
47 interaction::dummy_interaction_air::{
48 DummyInteractionAir, DummyInteractionChip, DummyInteractionData,
49 },
50 },
51 prove_up_to_batch_constraints, test_system_params_small, test_whir_config_small,
52 CachedFixture11, FibFixture, InteractionsFixture11, MixtureFixture,
53 PreprocessedAndCachedFixture, PreprocessedFibFixture, SelfInteractionFixture, TestFixture,
54 },
55 transcript::duplex_sponge::DuplexSpongeValidator,
56 utils::disable_debug_builder,
57 verifier::{
58 batch_constraints::verify_zerocheck_and_logup,
59 fractional_sumcheck_gkr::{verify_gkr, GkrVerificationError},
60 proof_shape::verify_proof_shape,
61 verify,
62 whir::{binary_k_fold, verify_whir, VerifyWhirError},
63 },
64 AirRef, FiatShamirTranscript, StarkEngine, StarkProtocolConfig, SystemParams,
65 TranscriptHistory, WhirConfig, WhirParams, WhirProximityStrategy, WhirRoundConfig,
66};
67use openvm_stark_sdk::{
68 config::{
69 baby_bear_poseidon2::{
70 default_duplex_sponge, default_duplex_sponge_recorder, poseidon2_perm,
71 BabyBearPoseidon2Config, BabyBearPoseidon2RefEngine, DuplexSponge, EF, F,
72 },
73 log_up_params::log_up_security_params_baby_bear_100_bits,
74 },
75 utils::{setup_tracing, setup_tracing_with_log_level},
76};
77use p3_field::{Field, PrimeCharacteristicRing, PrimeField32, TwoAdicField};
78use p3_matrix::dense::RowMajorMatrix;
79use p3_util::log2_strict_usize;
80use rand::{rngs::StdRng, Rng, SeedableRng};
81use tracing::{debug, Level};
82
83pub type SC = BabyBearPoseidon2Config;
84
85pub fn run_test_on_cpu_ctx<E: StarkEngine<SC = SC>>(
92 engine: &E,
93 airs: Vec<AirRef<SC>>,
94 ctxs: Vec<AirProvingContext<CpuColMajorBackend<SC>>>,
95) -> eyre::Result<()> {
96 let cpu_ctx = ProvingContext::new(ctxs.into_iter().enumerate().collect());
97 let d_ctx = engine.device().transport_proving_ctx_to_device(&cpu_ctx);
98 engine.run_test(airs, d_ctx.per_trace.into_iter().map(|(_, c)| c).collect())?;
99 Ok(())
100}
101
102pub fn proof_shape_verifier<E: StarkEngine<SC = SC>>() -> eyre::Result<()> {
107 setup_tracing();
108 let log_trace_degree = 3;
109
110 let engine = E::new(default_test_params_small());
111
112 let (vk, proof) = FibFixture::new(0, 1, 1 << log_trace_degree).keygen_and_prove(&engine);
114 verify_proof_shape(&vk.inner, &proof)?;
115
116 let (vk, proof) = InteractionsFixture11.keygen_and_prove(&engine);
118 verify_proof_shape(&vk.inner, &proof)?;
119
120 let params = engine.config().clone();
122 let (vk, proof) = CachedFixture11::new(params).keygen_and_prove(&engine);
123 verify_proof_shape(&vk.inner, &proof)?;
124
125 let height = 1 << log_trace_degree;
127 let sels = (0..height).map(|i| i % 2 == 0).collect_vec();
128 let (vk, proof) = PreprocessedFibFixture::new(0, 1, sels).keygen_and_prove(&engine);
129 verify_proof_shape(&vk.inner, &proof)?;
130
131 Ok(())
132}
133
134pub fn proof_shape_verifier_rng_system_params<E: StarkEngine<SC = SC>>() -> eyre::Result<()> {
135 setup_tracing();
136 let mut rng = StdRng::from_seed([228; 32]);
137 let w_stack = 16;
138 for _ in 0..10 {
139 let l_skip = rng.random_range(1usize..=2);
140 let n_stack = rng.random_range(8usize..=9);
141 let k_whir = rng.random_range(1usize..=4);
142 let log_blowup = rng.random_range(1usize..=3);
143 let num_whir_rounds = rng.random_range(1..=2);
144 let mut rounds = Vec::with_capacity(num_whir_rounds);
145 for _ in 0..num_whir_rounds {
146 rounds.push(WhirRoundConfig {
147 num_queries: rng.random_range(1..=10),
148 });
149 }
150 let whir = WhirConfig {
151 k: k_whir,
152 rounds,
153 mu_pow_bits: 1,
154 query_phase_pow_bits: 1,
155 folding_pow_bits: 1,
156 proximity: WhirProximityStrategy::SplitUniqueList {
157 m: 3,
158 list_start_round: 1,
159 },
160 };
161 let params = SystemParams {
162 l_skip,
163 n_stack,
164 w_stack,
165 log_blowup,
166 whir,
167 logup: log_up_security_params_baby_bear_100_bits(0.0),
168 max_constraint_degree: 3,
169 };
170 let engine = E::new(params);
171 let (vk, proof) = InteractionsFixture11.keygen_and_prove(&engine);
172 verify_proof_shape(&vk.inner, &proof)?;
173 }
174 Ok(())
175}
176
177pub fn interactions_single_sender_receiver_happy<E: StarkEngine<SC = SC>>() -> eyre::Result<()> {
182 setup_tracing();
183 let engine = E::new(default_test_params_small());
184 let fx = InteractionsFixture11;
185 let (vk, proof) = fx.keygen_and_prove(&engine);
186 engine.verify(&vk, &proof)?;
187 Ok(())
188}
189
190pub fn single_cached_trace_stark<E: StarkEngine<SC = SC>>() -> eyre::Result<()> {
191 setup_tracing();
192 let engine = E::new(default_test_params_small());
193 let fx = CachedFixture11::new(engine.config().clone());
194 let (vk, proof) = fx.keygen_and_prove(&engine);
195 engine.verify(&vk, &proof)?;
196 Ok(())
197}
198
199pub fn single_preprocessed_trace_stark<E: StarkEngine<SC = SC>>(
200 log_trace_degree: usize,
201) -> eyre::Result<()> {
202 setup_tracing();
203 let engine = E::new(default_test_params_small());
204 let height = 1 << log_trace_degree;
205 let sels = (0..height).map(|i| i % 2 == 0).collect_vec();
206 let fx = PreprocessedFibFixture::new(0, 1, sels);
207 let (vk, proof) = fx.keygen_and_prove(&engine);
208 engine.verify(&vk, &proof)?;
209 Ok(())
210}
211
212pub fn multi_interaction_traces_stark<E: StarkEngine<SC = SC>>(
213 log_trace_degree: usize,
214) -> eyre::Result<()> {
215 setup_tracing();
216 let engine = E::new(default_test_params_small());
217 let fx = SelfInteractionFixture {
218 widths: vec![4, 7, 8, 8, 10, 100],
219 log_height: log_trace_degree,
220 bus_index: 4,
221 };
222 let (vk, proof) = fx.keygen_and_prove(&engine);
223 engine.verify(&vk, &proof)?;
224 Ok(())
225}
226
227pub fn mixture_traces_stark<E: StarkEngine<SC = SC>>(log_trace_degree: usize) -> eyre::Result<()> {
228 setup_tracing();
229 let engine = E::new(default_test_params_small());
230 let fx = MixtureFixture::standard(log_trace_degree, engine.config().clone());
231 let (vk, proof) = fx.keygen_and_prove(&engine);
232 engine.verify(&vk, &proof)?;
233 Ok(())
234}
235
236pub fn matrix_stacking_overflow<E: StarkEngine<SC = SC>>() -> eyre::Result<()> {
237 setup_tracing();
238 let params = test_system_params_small(3, 5, 3);
239 let engine = E::new(params);
240 let fx = SelfInteractionFixture {
241 widths: vec![4, 7, 8, 8, 10],
242 log_height: 1,
243 bus_index: 4,
244 };
245 let (vk, proof) = fx.keygen_and_prove(&engine);
246 engine.verify(&vk, &proof)?;
247 Ok(())
248}
249
250pub fn fib_air_roundtrip<E: StarkEngine<SC = SC>>(
255 l_skip: usize,
256 log_trace_degree: usize,
257) -> eyre::Result<()> {
258 setup_tracing_with_log_level(Level::DEBUG);
259
260 let n_stack = 8;
261 let w_stack = 8;
262 let k_whir = 4;
263 let whir_params = WhirParams {
264 k: k_whir,
265 log_final_poly_len: k_whir,
266 query_phase_pow_bits: 1,
267 folding_pow_bits: 11,
268 mu_pow_bits: 10,
269 proximity: WhirProximityStrategy::UniqueDecoding,
270 };
271 let log_blowup = 1;
272 let whir = WhirConfig::new(log_blowup, l_skip + n_stack, whir_params, 80);
273 let params = SystemParams {
274 l_skip,
275 n_stack,
276 w_stack,
277 log_blowup,
278 whir,
279 logup: log_up_security_params_baby_bear_100_bits(0.0),
280 max_constraint_degree: 3,
281 };
282 let fib = FibFixture::new(0, 1, 1 << log_trace_degree);
283
284 let engine = E::new(params);
285 let (pk, vk) = fib.keygen(&engine);
286 let mut prover_transcript = engine.initial_transcript();
287 let proof = fib.prove_from_transcript(&engine, &pk, &mut prover_transcript);
288
289 let mut verifier_sponge = default_duplex_sponge();
290 verify(engine.config(), &vk, &proof, &mut verifier_sponge)?;
291 Ok(())
292}
293
294pub fn dummy_interactions_roundtrip<E: StarkEngine<SC = SC>>(
295 l_skip: usize,
296 n_stack: usize,
297 k_whir: usize,
298) -> eyre::Result<()> {
299 let params = test_system_params_small(l_skip, n_stack, k_whir);
300 let engine = E::new(params);
301 let fx = InteractionsFixture11;
302 let (pk, vk) = fx.keygen(&engine);
303
304 let mut prover_transcript = engine.initial_transcript();
305 let proof = fx.prove_from_transcript(&engine, &pk, &mut prover_transcript);
306
307 let mut verifier_sponge = default_duplex_sponge();
308 verify(engine.config(), &vk, &proof, &mut verifier_sponge)?;
309 Ok(())
310}
311
312pub fn cached_trace_roundtrip<E: StarkEngine<SC = SC>>(
313 l_skip: usize,
314 n_stack: usize,
315 k_whir: usize,
316) -> eyre::Result<()> {
317 setup_tracing_with_log_level(Level::DEBUG);
318 let params = test_system_params_small(l_skip, n_stack, k_whir);
319 let engine = E::new(params);
320 let fx = CachedFixture11::new(engine.config().clone());
321 let (pk, vk) = fx.keygen(&engine);
322
323 let mut prover_transcript = engine.initial_transcript();
324 let proof = fx.prove_from_transcript(&engine, &pk, &mut prover_transcript);
325
326 let mut verifier_sponge = default_duplex_sponge();
327 verify(engine.config(), &vk, &proof, &mut verifier_sponge)?;
328 Ok(())
329}
330
331pub fn preprocessed_trace_roundtrip<E: StarkEngine<SC = SC>>(
332 l_skip: usize,
333 n_stack: usize,
334 k_whir: usize,
335) -> eyre::Result<()> {
336 let params = test_system_params_small(l_skip, n_stack, k_whir);
337 let engine = E::new(params);
338 let log_trace_degree = 8;
339 let height = 1 << log_trace_degree;
340 let sels = (0..height).map(|i| i % 2 == 0).collect_vec();
341 let fx = PreprocessedFibFixture::new(0, 1, sels);
342 let (pk, vk) = fx.keygen(&engine);
343
344 let mut prover_transcript = engine.initial_transcript();
345 let proof = fx.prove_from_transcript(&engine, &pk, &mut prover_transcript);
346
347 let mut verifier_sponge = default_duplex_sponge();
348 verify(engine.config(), &vk, &proof, &mut verifier_sponge)?;
349 Ok(())
350}
351
352pub fn preprocessed_and_cached_trace_roundtrip<E: StarkEngine<SC = SC>>(
353 l_skip: usize,
354 n_stack: usize,
355 k_whir: usize,
356 num_cached_parts: usize,
357) -> eyre::Result<()> {
358 let params = test_system_params_small(l_skip, n_stack, k_whir);
359 let engine = E::new(params);
360 let log_trace_degree = 8;
361 let height = 1 << log_trace_degree;
362 let sels = (0..height).map(|i| i % 2 == 0).collect_vec();
363 let fx = PreprocessedAndCachedFixture::new(sels, engine.config().clone(), num_cached_parts);
364 let (pk, vk) = fx.keygen(&engine);
365
366 let mut prover_transcript = engine.initial_transcript();
367 let proof = fx.prove_from_transcript(&engine, &pk, &mut prover_transcript);
368
369 let mut verifier_sponge = default_duplex_sponge();
370 verify(engine.config(), &vk, &proof, &mut verifier_sponge)?;
371 Ok(())
372}
373
374pub fn batch_sumcheck_zero_interactions<E: StarkEngine<SC = SC>>(
379 log_trace_degree: usize,
380) -> eyre::Result<()> {
381 setup_tracing_with_log_level(Level::DEBUG);
382
383 let engine = E::new(default_test_params_small());
384 let params = engine.params();
385 let fib = FibFixture::new(0, 1, 1 << log_trace_degree);
386 let (pk, vk) = fib.keygen(&engine);
387 let device = engine.device();
388 let d_pk = device.transport_pk_to_device(&pk);
389 let cpu_ctx = fib.generate_proving_ctx();
390 let ctx = device.transport_proving_ctx_to_device(&cpu_ctx);
391
392 let mut n_per_air: Vec<isize> = Vec::with_capacity(ctx.per_trace.len());
393 for (_, trace) in ctx.common_main_traces() {
394 let trace_height = trace.height();
395 let log_height = log2_strict_usize(trace_height);
396 let n = log_height as isize - params.l_skip as isize;
397 n_per_air.push(n);
398 }
399
400 let mut prover_transcript = engine.initial_transcript();
401 let mut verifier_sponge = default_duplex_sponge();
402
403 let omega_skip = F::two_adic_generator(params.l_skip);
404 let omega_skip_pows = omega_skip.powers().take(1 << params.l_skip).collect_vec();
405
406 let pvs = vec![ctx.per_trace[0].1.public_values.clone()];
407 let (partial_proof, _) =
408 prove_up_to_batch_constraints(&engine, &mut prover_transcript, &d_pk, ctx);
409 let (gkr_proof, batch_proof) = partial_proof.into();
410
411 let r = verify_zerocheck_and_logup::<SC, _>(
412 &mut verifier_sponge,
413 &vk.inner,
414 &pvs,
415 &gkr_proof,
416 &batch_proof,
417 &[0],
418 &n_per_air,
419 &omega_skip_pows,
420 )?;
421 assert_eq!(r.len(), log_trace_degree.saturating_sub(params.l_skip) + 1);
422 Ok(())
423}
424
425pub fn batch_sumcheck_zero_interactions_malleable_q0<E: StarkEngine<SC = SC>>() -> eyre::Result<()>
428{
429 setup_tracing_with_log_level(Level::DEBUG);
430
431 let engine = E::new(default_test_params_small());
432 let params = engine.params();
433 let log_trace_degree = 4;
434 let fib = FibFixture::new(0, 1, 1 << log_trace_degree);
435 let (pk, vk) = fib.keygen(&engine);
436 let device = engine.device();
437 let d_pk = device.transport_pk_to_device(&pk);
438 let cpu_ctx = fib.generate_proving_ctx();
439 let ctx = device.transport_proving_ctx_to_device(&cpu_ctx);
440
441 let mut n_per_air: Vec<isize> = Vec::with_capacity(ctx.per_trace.len());
442 for (_, trace) in ctx.common_main_traces() {
443 let trace_height = trace.height();
444 let log_height = log2_strict_usize(trace_height);
445 let n = log_height as isize - params.l_skip as isize;
446 n_per_air.push(n);
447 }
448
449 let mut prover_transcript = engine.initial_transcript();
450 let omega_skip = F::two_adic_generator(params.l_skip);
451 let omega_skip_pows = omega_skip.powers().take(1 << params.l_skip).collect_vec();
452
453 let pvs = vec![ctx.per_trace[0].1.public_values.clone()];
454 let (partial_proof, _) =
455 prove_up_to_batch_constraints(&engine, &mut prover_transcript, &d_pk, ctx);
456 let (mut gkr_proof, batch_proof) = partial_proof.into();
457
458 assert_eq!(gkr_proof.q0_claim, EF::ONE);
460 let tampered = EF::from_u64(2);
461 gkr_proof.q0_claim = tampered;
462
463 let mut verifier_sponge = default_duplex_sponge();
464 let err = verify_zerocheck_and_logup::<SC, _>(
465 &mut verifier_sponge,
466 &vk.inner,
467 &pvs,
468 &gkr_proof,
469 &batch_proof,
470 &[0],
471 &n_per_air,
472 &omega_skip_pows,
473 )
474 .expect_err("verifier must reject a tampered q0_claim when there are no interactions");
475 assert_eq!(
476 err,
477 GkrVerificationError::InvalidZeroRoundValue { actual: tampered }.into(),
478 );
479 Ok(())
480}
481
482pub fn gkr_verify_zero_interactions<E: StarkEngine<SC = SC>>() -> eyre::Result<()> {
483 setup_tracing_with_log_level(Level::DEBUG);
484
485 let engine = E::new(default_test_params_small());
486 let params = engine.params();
487 let fx = InteractionsFixture11;
488 let (pk, _vk) = fx.keygen(&engine);
489 let device = engine.device();
490 let d_pk = device.transport_pk_to_device(&pk);
491 let cpu_ctx = fx.generate_proving_ctx();
492 let ctx = device
493 .transport_proving_ctx_to_device(&cpu_ctx)
494 .into_sorted();
495
496 let mut prover_transcript = engine.initial_transcript();
497 let (partial_proof, _) =
498 prove_up_to_batch_constraints(&engine, &mut prover_transcript, &d_pk, ctx);
499 let (gkr_proof, _) = partial_proof.into();
500
501 let mut transcript = default_duplex_sponge();
502 assert!(FiatShamirTranscript::<SC>::check_witness(
503 &mut transcript,
504 params.logup.pow_bits,
505 gkr_proof.logup_pow_witness
506 ));
507 let _alpha = FiatShamirTranscript::<SC>::sample_ext(&mut transcript);
508 let _beta = FiatShamirTranscript::<SC>::sample_ext(&mut transcript);
509 let total_rounds = gkr_proof.claims_per_layer.len();
510 let _ = verify_gkr::<SC, _>(&gkr_proof, &mut transcript, total_rounds)?;
511
512 Ok(())
513}
514
515pub fn batch_constraints_with_interactions<E: StarkEngine<SC = SC>>() -> eyre::Result<()> {
516 setup_tracing_with_log_level(Level::DEBUG);
517
518 let engine = E::new(default_test_params_small());
519 let fx = InteractionsFixture11;
520 let (pk, vk) = fx.keygen(&engine);
521 let device = engine.device();
522 let d_pk = device.transport_pk_to_device(&pk);
523 let cpu_ctx = fx.generate_proving_ctx();
524 let ctx = device
525 .transport_proving_ctx_to_device(&cpu_ctx)
526 .into_sorted();
527 let l_skip = engine.params().l_skip;
528
529 let mut pvs = vec![vec![]; vk.inner.per_air.len()];
530 let (trace_id_to_air_ids, ns): (Vec<_>, Vec<_>) = ctx
531 .per_trace
532 .iter()
533 .map(|(air_idx, trace_ctx)| {
534 pvs[*air_idx] = trace_ctx.public_values.clone();
535 (
536 *air_idx,
537 log2_strict_usize(trace_ctx.common_main.height()) as isize - l_skip as isize,
538 )
539 })
540 .multiunzip();
541 debug!(?trace_id_to_air_ids);
542 debug!(n_per_trace = ?ns);
543 let omega_pows = F::two_adic_generator(l_skip)
544 .powers()
545 .take(1 << l_skip)
546 .collect_vec();
547
548 let mut prover_transcript = engine.initial_transcript();
549 let (partial_proof, _) =
550 prove_up_to_batch_constraints(&engine, &mut prover_transcript, &d_pk, ctx);
551 let (gkr_proof, batch_proof) = partial_proof.into();
552
553 let mut verifier_transcript = default_duplex_sponge();
554 verify_zerocheck_and_logup::<SC, _>(
555 &mut verifier_transcript,
556 &vk.inner,
557 &pvs,
558 &gkr_proof,
559 &batch_proof,
560 &trace_id_to_air_ids,
561 &ns,
562 &omega_pows,
563 )?;
564 Ok(())
565}
566
567pub fn single_fib_and_dummy_trace_stark<E: StarkEngine<SC = SC>>(
572 log_trace_degree: usize,
573) -> eyre::Result<()> {
574 setup_tracing();
575
576 let engine = E::new(default_test_params_small());
577
578 let sender_height = 2 * (1 << 3);
580 let sender_trace = RowMajorMatrix::new(
581 [0, 1, 3, 5, 7, 4, 546, 889]
582 .into_iter()
583 .cycle()
584 .take(2 * sender_height)
585 .map(F::from_usize)
586 .collect(),
587 2,
588 );
589 let receiver_trace = RowMajorMatrix::new(
590 [1, 5, 3, 4, 4, 4, 2, 5, 0, 123, 545, 889, 1, 889, 0, 456]
591 .into_iter()
592 .cycle()
593 .take(4 * sender_height)
594 .map(F::from_usize)
595 .collect(),
596 2,
597 );
598
599 let height = 2 * (1 << log_trace_degree);
601 let fib = FibFixture::new(0, 1, height);
602
603 let fx_fixture = InteractionsFixture11;
605 let fx_airs = fx_fixture.airs();
606 let fib_airs = fib.airs();
607 let mut combined_airs = fx_airs;
608 combined_airs.extend(fib_airs);
609 let (combined_pk, _combined_vk) = engine.keygen(&combined_airs);
610 let device = engine.device();
611 let combined_pk = device.transport_pk_to_device(&combined_pk);
612
613 let mut per_trace: Vec<_> = [sender_trace, receiver_trace]
615 .into_iter()
616 .enumerate()
617 .map(|(air_idx, trace)| {
618 (
619 air_idx,
620 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&trace)),
621 )
622 })
623 .collect();
624 let fib_ctx = fib.generate_proving_ctx().per_trace.pop().unwrap().1;
625
626 per_trace.push((per_trace.len(), fib_ctx));
628 let cpu_ctx = ProvingContext::new(per_trace);
629 let combined_ctx = device
630 .transport_proving_ctx_to_device(&cpu_ctx)
631 .into_sorted();
632
633 let proof = engine.prove(&combined_pk, combined_ctx)?;
634 engine.verify(&combined_pk.get_vk(), &proof)?;
635 Ok(())
636}
637
638pub fn optional_air<E: StarkEngine<SC = SC>>() -> eyre::Result<()> {
643 setup_tracing();
644
645 let engine = E::new(default_test_params_small());
646 let config = engine.config().clone();
647 let device = engine.device();
648
649 let fib_air = Arc::new(FibonacciAir) as AirRef<SC>;
650 let send_chip1: DummyInteractionChip<SC> =
651 DummyInteractionChip::new_without_partition(1, true, 0);
652 let send_chip2: DummyInteractionChip<SC> =
653 DummyInteractionChip::new_with_partition(config.clone(), 1, true, 0);
654 let recv_chip1: DummyInteractionChip<SC> =
655 DummyInteractionChip::new_without_partition(1, false, 0);
656
657 let airs = vec![
658 fib_air,
659 send_chip1.air(),
660 send_chip2.air(),
661 recv_chip1.air(),
662 ];
663 let (pk, _vk) = engine.keygen(&airs);
664 let d_pk = device.transport_pk_to_device(&pk);
665
666 {
668 let fib = FibFixture::new(0, 1, 8);
669 let fib_air_ctx = fib
670 .generate_proving_ctx()
671 .per_trace
672 .into_iter()
673 .next()
674 .unwrap()
675 .1;
676
677 let mut s1: DummyInteractionChip<SC> =
678 DummyInteractionChip::new_without_partition(1, true, 0);
679 s1.load_data(DummyInteractionData {
680 count: vec![1, 2, 4],
681 fields: vec![vec![1], vec![2], vec![3]],
682 });
683 let mut s2: DummyInteractionChip<SC> =
684 DummyInteractionChip::new_with_partition(config.clone(), 1, true, 0);
685 s2.load_data(DummyInteractionData {
686 count: vec![1, 2, 8],
687 fields: vec![vec![1], vec![2], vec![3]],
688 });
689 let mut r1: DummyInteractionChip<SC> =
690 DummyInteractionChip::new_without_partition(1, false, 0);
691 r1.load_data(DummyInteractionData {
692 count: vec![2, 4, 12],
693 fields: vec![vec![1], vec![2], vec![3]],
694 });
695
696 let cpu_ctx = ProvingContext::new(vec![
697 (0, fib_air_ctx),
698 (1, s1.generate_proving_ctx()),
699 (2, s2.generate_proving_ctx()),
700 (3, r1.generate_proving_ctx()),
701 ]);
702 let d_ctx = device.transport_proving_ctx_to_device(&cpu_ctx);
703 let proof = engine.prove(&d_pk, d_ctx)?;
704 engine.verify(&pk.get_vk(), &proof)?;
705 }
706
707 {
709 let mut s1: DummyInteractionChip<SC> =
710 DummyInteractionChip::new_without_partition(1, true, 0);
711 s1.load_data(DummyInteractionData {
712 count: vec![1, 2, 4],
713 fields: vec![vec![1], vec![2], vec![3]],
714 });
715 let mut r1: DummyInteractionChip<SC> =
716 DummyInteractionChip::new_without_partition(1, false, 0);
717 r1.load_data(DummyInteractionData {
718 count: vec![1, 2, 4],
719 fields: vec![vec![1], vec![2], vec![3]],
720 });
721
722 let cpu_ctx = ProvingContext::new(vec![
723 (1, s1.generate_proving_ctx()),
724 (3, r1.generate_proving_ctx()),
725 ]);
726 let d_ctx = device.transport_proving_ctx_to_device(&cpu_ctx);
727 let proof = engine.prove(&d_pk, d_ctx)?;
728 engine.verify(&pk.get_vk(), &proof)?;
729 }
730
731 {
733 disable_debug_builder();
734 let mut r1: DummyInteractionChip<SC> =
735 DummyInteractionChip::new_without_partition(1, false, 0);
736 r1.load_data(DummyInteractionData {
737 count: vec![1, 2, 4],
738 fields: vec![vec![1], vec![2], vec![3]],
739 });
740
741 let cpu_ctx = ProvingContext::new(vec![(3, r1.generate_proving_ctx())]);
742 let d_ctx = device.transport_proving_ctx_to_device(&cpu_ctx);
743 let result = engine.prove(&d_pk, d_ctx);
744 assert!(
745 result.is_err(),
746 "Expected prover to fail with unbalanced interactions"
747 );
748 }
749 Ok(())
750}
751
752pub fn vkey_methods<E: StarkEngine<SC = SC>>() {
753 setup_tracing();
754
755 let engine = E::new(default_test_params_small());
756 let fib_air = FibonacciAir;
757 let send_air = DummyInteractionAir::new(1, true, 0);
758 let recv_air = DummyInteractionAir::new(1, false, 0);
759
760 let airs = any_air_arc_vec![fib_air, send_air, recv_air];
761 let (_pk, vk) = engine.keygen(&airs);
762
763 assert_eq!(vk.inner.per_air.len(), 3);
764 assert_eq!(vk.inner.per_air[0].params.width.main_width(), 2);
765 assert_eq!(vk.inner.per_air[1].params.width.main_width(), 2);
766 assert_eq!(vk.inner.per_air[2].params.width.main_width(), 2);
767 assert_eq!(vk.inner.per_air[0].num_interactions(), 0);
768 assert_eq!(vk.inner.per_air[1].num_interactions(), 1);
769 assert_eq!(vk.inner.per_air[2].num_interactions(), 1);
770}
771
772pub fn interaction_trace_height_constraints<E: StarkEngine<SC = SC>>() {
773 let log_trace_degree = 3;
774 let n = 1usize << log_trace_degree;
775 let sels: Vec<bool> = (0..n).map(|i| i % 2 == 0).collect();
776 let fib_air = FibonacciSelectorAir::new(sels, true);
777 let mut sender_air = DummyInteractionAir::new(1, true, 0);
778 sender_air.count_weight = 3;
779 let mut sender_air_2 = DummyInteractionAir::new(1, true, 0);
780 sender_air_2.count_weight = 1;
781 let mut sender_air_3 = DummyInteractionAir::new(1, true, 1);
782 sender_air_3.count_weight = 7;
783
784 let engine = E::new(default_test_params_small());
785 let airs: Vec<AirRef<SC>> = vec![
786 Arc::new(fib_air),
787 Arc::new(sender_air),
788 Arc::new(sender_air_2),
789 Arc::new(sender_air_3),
790 ];
791 let (_pk, vk) = engine.keygen(&airs);
792
793 assert_eq!(vk.inner.trace_height_constraints.len(), 3);
794
795 assert_eq!(
796 vk.inner.trace_height_constraints[0],
797 LinearConstraint {
798 coefficients: vec![0, 3, 1, 0],
799 threshold: F::ORDER_U32,
800 }
801 );
802 assert_eq!(
803 vk.inner.trace_height_constraints[1],
804 LinearConstraint {
805 coefficients: vec![0, 0, 0, 7],
806 threshold: F::ORDER_U32,
807 }
808 );
809 assert_eq!(
810 vk.inner.trace_height_constraints[2],
811 LinearConstraint {
812 coefficients: vec![1, 1, 1, 1],
813 threshold: engine.params().logup.max_interaction_count,
814 }
815 );
816}
817
818pub fn trace_height_constraints_implied_removal<E: StarkEngine<SC = SC>>() {
819 let log_trace_degree = 3;
820 let n = 1usize << log_trace_degree;
821 let sels: Vec<bool> = (0..n).map(|i| i % 2 == 0).collect();
822 let fib_air = FibonacciSelectorAir::new(sels, true);
823 let sender_air = DummyInteractionAir::new(1, true, 0);
824 let sender_air_2 = DummyInteractionAir::new(1, true, 1);
825
826 let engine = E::new(default_test_params_small());
827 let airs: Vec<AirRef<SC>> = vec![
828 Arc::new(fib_air),
829 Arc::new(sender_air),
830 Arc::new(sender_air_2),
831 ];
832 let (_pk, vk) = engine.keygen(&airs);
833
834 assert_eq!(vk.inner.trace_height_constraints.len(), 1);
835 assert_eq!(
836 vk.inner.trace_height_constraints[0],
837 LinearConstraint {
838 coefficients: vec![1, 1, 1],
839 threshold: engine.params().logup.max_interaction_count,
840 }
841 );
842}
843
844pub fn interaction_multi_rows_neg<E: StarkEngine<SC = SC>>() {
845 setup_tracing();
846
847 let sender_trace = RowMajorMatrix::new(
848 [0, 1, 3, 5, 7, 4, 546, 0]
849 .into_iter()
850 .map(F::from_usize)
851 .collect(),
852 2,
853 );
854 let sender_air = DummyInteractionAir::new(1, true, 0);
855
856 let receiver_trace = RowMajorMatrix::new(
858 [1, 5, 3, 4, 4, 4, 2, 5, 0, 123, 545, 0, 0, 0, 0, 456]
859 .into_iter()
860 .map(F::from_usize)
861 .collect(),
862 2,
863 );
864 let receiver_air = DummyInteractionAir::new(1, false, 0);
865
866 disable_debug_builder();
867 let engine = E::new(default_test_params_small());
868 let result = run_test_on_cpu_ctx(
869 &engine,
870 any_air_arc_vec![sender_air, receiver_air],
871 vec![
872 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&sender_trace)),
873 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&receiver_trace)),
874 ],
875 );
876 assert!(
877 result.is_err(),
878 "Expected test to fail with unbalanced interactions"
879 );
880}
881
882pub fn interaction_all_zero_sender<E: StarkEngine<SC = SC>>() -> eyre::Result<()> {
883 setup_tracing();
884
885 let sender_trace = RowMajorMatrix::new(
886 [0, 1, 0, 5, 0, 4, 0, 889]
887 .into_iter()
888 .map(F::from_usize)
889 .collect(),
890 2,
891 );
892 let sender_air = DummyInteractionAir::new(1, true, 0);
893
894 let engine = E::new(default_test_params_small());
895 run_test_on_cpu_ctx(
896 &engine,
897 any_air_arc_vec![sender_air],
898 vec![AirProvingContext::simple_no_pis(
899 ColMajorMatrix::from_row_major(&sender_trace),
900 )],
901 )?;
902 Ok(())
903}
904
905pub fn interaction_multi_senders<E: StarkEngine<SC = SC>>() -> eyre::Result<()> {
906 setup_tracing();
907
908 let sender_trace1 = RowMajorMatrix::new(
909 [0, 1, 3, 5, 6, 4, 333, 889]
910 .into_iter()
911 .map(F::from_usize)
912 .collect(),
913 2,
914 );
915 let sender_trace2 =
916 RowMajorMatrix::new([1, 4, 213, 889].into_iter().map(F::from_usize).collect(), 2);
917 let sender_air = DummyInteractionAir::new(1, true, 0);
918
919 let receiver_trace = RowMajorMatrix::new(
920 [1, 5, 3, 4, 4, 4, 2, 5, 0, 123, 545, 889, 1, 889, 0, 456]
921 .into_iter()
922 .map(F::from_usize)
923 .collect(),
924 2,
925 );
926 let receiver_air = DummyInteractionAir::new(1, false, 0);
927
928 let engine = E::new(default_test_params_small());
929 run_test_on_cpu_ctx(
930 &engine,
931 any_air_arc_vec![sender_air, sender_air, receiver_air],
932 vec![
933 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&sender_trace1)),
934 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&sender_trace2)),
935 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&receiver_trace)),
936 ],
937 )?;
938 Ok(())
939}
940
941pub fn interaction_multi_senders_neg<E: StarkEngine<SC = SC>>() {
942 setup_tracing();
943
944 let sender_trace1 = RowMajorMatrix::new(
946 [0, 1, 3, 5, 5, 4, 333, 889]
947 .into_iter()
948 .map(F::from_usize)
949 .collect(),
950 2,
951 );
952 let sender_trace2 =
953 RowMajorMatrix::new([1, 4, 213, 889].into_iter().map(F::from_usize).collect(), 2);
954 let sender_air = DummyInteractionAir::new(1, true, 0);
955
956 let receiver_trace = RowMajorMatrix::new(
957 [1, 5, 3, 4, 4, 4, 2, 5, 0, 123, 545, 889, 1, 889, 0, 456]
958 .into_iter()
959 .map(F::from_usize)
960 .collect(),
961 2,
962 );
963 let receiver_air = DummyInteractionAir::new(1, false, 0);
964
965 disable_debug_builder();
966 let engine = E::new(default_test_params_small());
967 let result = run_test_on_cpu_ctx(
968 &engine,
969 any_air_arc_vec![sender_air, sender_air, receiver_air],
970 vec![
971 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&sender_trace1)),
972 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&sender_trace2)),
973 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&receiver_trace)),
974 ],
975 );
976 assert!(
977 result.is_err(),
978 "Expected test to fail with unbalanced interactions"
979 );
980}
981
982pub fn interaction_multi_sender_receiver<E: StarkEngine<SC = SC>>() -> eyre::Result<()> {
983 setup_tracing();
984
985 let sender_trace1 = RowMajorMatrix::new(
986 [0, 1, 3, 5, 6, 4, 333, 889]
987 .into_iter()
988 .map(F::from_usize)
989 .collect(),
990 2,
991 );
992 let sender_trace2 =
993 RowMajorMatrix::new([1, 4, 213, 889].into_iter().map(F::from_usize).collect(), 2);
994 let sender_air = DummyInteractionAir::new(1, true, 0);
995
996 let receiver_trace1 = RowMajorMatrix::new(
997 [1, 5, 3, 4, 4, 4, 2, 5, 0, 123, 545, 889, 0, 289, 0, 456]
998 .into_iter()
999 .map(F::from_usize)
1000 .collect(),
1001 2,
1002 );
1003 let receiver_trace2 = RowMajorMatrix::new([1, 889].into_iter().map(F::from_usize).collect(), 2);
1004 let receiver_air = DummyInteractionAir::new(1, false, 0);
1005
1006 let engine = E::new(default_test_params_small());
1007 run_test_on_cpu_ctx(
1008 &engine,
1009 any_air_arc_vec![sender_air, sender_air, receiver_air, receiver_air],
1010 vec![
1011 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&sender_trace1)),
1012 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&sender_trace2)),
1013 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&receiver_trace1)),
1014 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&receiver_trace2)),
1015 ],
1016 )?;
1017 Ok(())
1018}
1019
1020pub fn interaction_cached_trace_neg<E: StarkEngine<SC = SC>>() {
1021 setup_tracing();
1022
1023 let engine = E::new(default_test_params_small());
1024 let config = engine.config().clone();
1025
1026 let mut sender_chip: DummyInteractionChip<SC> =
1027 DummyInteractionChip::new_without_partition(2, true, 0);
1028 sender_chip.load_data(DummyInteractionData {
1029 count: vec![0, 7, 3, 546],
1030 fields: vec![vec![1, 1], vec![4, 2], vec![5, 1], vec![889, 4]],
1031 });
1032
1033 let mut receiver_chip: DummyInteractionChip<SC> =
1035 DummyInteractionChip::new_with_partition(config, 2, false, 0);
1036 receiver_chip.load_data(DummyInteractionData {
1037 count: vec![1, 3, 4, 2, 0, 545, 1, 0],
1038 fields: vec![
1039 vec![5, 1],
1040 vec![4, 2],
1041 vec![4, 2],
1042 vec![5, 1],
1043 vec![123, 3],
1044 vec![889, 4],
1045 vec![889, 10], vec![456, 5],
1047 ],
1048 });
1049
1050 let airs = vec![receiver_chip.air(), sender_chip.air()];
1051 let ctxs = vec![
1052 receiver_chip.generate_proving_ctx(),
1053 sender_chip.generate_proving_ctx(),
1054 ];
1055
1056 disable_debug_builder();
1057 let device = engine.device();
1058 let (pk, vk) = engine.keygen(&airs);
1059 let d_pk = device.transport_pk_to_device(&pk);
1060 let cpu_ctx = ProvingContext::new(ctxs.into_iter().enumerate().collect());
1061 let d_ctx = device.transport_proving_ctx_to_device(&cpu_ctx);
1062 let result = engine.prove(&d_pk, d_ctx);
1063 if let Ok(proof) = result {
1064 assert!(
1065 engine.verify(&vk, &proof).is_err(),
1066 "Expected verification to fail with unbalanced interactions"
1067 );
1068 }
1069}
1070
1071pub fn generate_random_z(params: &SystemParams, rng: &mut StdRng) -> (Vec<EF>, Vec<EF>) {
1077 let z_prism: Vec<_> = (0..params.n_stack + 1)
1078 .map(|_| EF::from_u64(rng.random()))
1079 .collect();
1080
1081 let z_cube = {
1082 let z_cube = z_prism[0]
1083 .exp_powers_of_2()
1084 .take(params.l_skip)
1085 .chain(z_prism[1..].iter().copied())
1086 .collect_vec();
1087 debug_assert_eq!(z_cube.len(), params.n_stack + params.l_skip);
1088 z_cube
1089 };
1090
1091 (z_prism, z_cube)
1092}
1093
1094pub fn stacking_openings_for_matrix(
1096 params: &SystemParams,
1097 z_prism: &[EF],
1098 matrix: &ColMajorMatrix<F>,
1099) -> Vec<EF> {
1100 matrix
1101 .columns()
1102 .map(|col| {
1103 Ple::from_evaluations(params.l_skip, col).eval_at_point(
1104 params.l_skip,
1105 z_prism[0],
1106 &z_prism[1..],
1107 )
1108 })
1109 .collect()
1110}
1111
1112fn run_whir_test(
1114 config: &SC,
1115 pk: DeviceMultiStarkProvingKey<CpuColMajorBackend<SC>>,
1116 ctx: &ProvingContext<CpuColMajorBackend<SC>>,
1117) -> eyre::Result<()> {
1118 let params = config.params();
1119 let (common_main_commit, common_main_pcs_data) = {
1120 let traces = ctx
1121 .common_main_traces()
1122 .map(|(_, trace)| trace)
1123 .collect_vec();
1124 stacked_commit(
1125 config.hasher(),
1126 params.l_skip,
1127 params.n_stack,
1128 params.log_blowup,
1129 params.k_whir(),
1130 &traces,
1131 )?
1132 };
1133
1134 let mut commits = vec![common_main_commit];
1135 let mut committed_mats = vec![(&common_main_pcs_data.matrix, &common_main_pcs_data.tree)];
1136 for (air_id, trace_ctx) in &ctx.per_trace {
1137 let pcs_datas = pk.per_air[*air_id]
1138 .preprocessed_data
1139 .iter()
1140 .chain(&trace_ctx.cached_mains);
1141 for cd in pcs_datas {
1142 let data = &cd.data;
1143 committed_mats.push((&data.matrix, &data.tree));
1144 commits.push(data.commit()?);
1145 }
1146 }
1147
1148 let mut rng = StdRng::seed_from_u64(0);
1149 let (z_prism, z_cube) = generate_random_z(params, &mut rng);
1150
1151 let mut prover_sponge = default_duplex_sponge_recorder();
1152
1153 let proof = prove_whir_opening::<SC, _>(
1154 &mut prover_sponge,
1155 config.hasher(),
1156 params.l_skip,
1157 params.log_blowup,
1158 params.whir(),
1159 &committed_mats,
1160 &z_cube,
1161 )?;
1162
1163 let stacking_openings = committed_mats
1164 .iter()
1165 .map(|(matrix, _)| stacking_openings_for_matrix(params, &z_prism, matrix))
1166 .collect_vec();
1167
1168 let mut verifier_sponge =
1169 DuplexSpongeValidator::new(poseidon2_perm().clone(), prover_sponge.into_log());
1170 verify_whir::<SC, _>(
1171 &mut verifier_sponge,
1172 config,
1173 &proof,
1174 &stacking_openings,
1175 &commits,
1176 &z_cube,
1177 )?;
1178 Ok(())
1179}
1180
1181fn run_whir_fib_test(params: SystemParams) -> eyre::Result<()> {
1182 let engine = BabyBearPoseidon2RefEngine::<DuplexSponge>::new(params.clone());
1183 let fib = FibFixture::new(0, 1, 1 << params.log_stacked_height());
1184 let (pk, _vk) = fib.keygen(&engine);
1185 let pk = engine.device().transport_pk_to_device(&pk);
1186 let ctx = fib.generate_proving_ctx();
1187 run_whir_test(engine.config(), pk, &ctx)
1188}
1189
1190pub fn fold_single() {
1192 let mut rng = StdRng::seed_from_u64(0);
1193
1194 let a0 = EF::from_u32(rng.random());
1195 let a1 = EF::from_u32(rng.random());
1196 let alpha = EF::from_u32(rng.random());
1197 let x = F::from_u32(rng.random());
1198
1199 let result = binary_k_fold(vec![a0, a1], &[alpha], x);
1200 assert_eq!(result, a0 + (alpha - x) * (a0 - a1) * x.double().inverse());
1201}
1202
1203pub fn fold_double() {
1205 let mut rng = StdRng::seed_from_u64(0);
1206
1207 let a0 = EF::from_u32(rng.random());
1208 let a1 = EF::from_u32(rng.random());
1209 let a2 = EF::from_u32(rng.random());
1210 let a3 = EF::from_u32(rng.random());
1211 let alpha0 = EF::from_u32(rng.random());
1212 let alpha1 = EF::from_u32(rng.random());
1213
1214 let x = F::from_u32(rng.random());
1215
1216 let result = binary_k_fold(vec![a0, a1, a2, a3], &[alpha0, alpha1], x);
1217 let tw = F::two_adic_generator(2);
1218
1219 let b0 = a0 + (alpha0 - x) * (a0 - a2) * x.double().inverse();
1220 let b1 = a1 + (alpha0 - (tw * x)) * (a1 - a3) * (tw * x).double().inverse();
1221 let x2 = x.square();
1222 let expected = b0 + (alpha1 - x2) * (b0 - b1) * x2.double().inverse();
1223
1224 assert_eq!(result, expected);
1225}
1226
1227pub fn whir_single_fib(
1230 n_stack: usize,
1231 log_blowup: usize,
1232 k_whir: usize,
1233 log_final_poly_len: usize,
1234) -> eyre::Result<()> {
1235 setup_tracing_with_log_level(Level::DEBUG);
1236 let l_skip = 2;
1237 let w_stack = 8;
1238 let whir = test_whir_config_small(log_blowup, l_skip + n_stack, k_whir, log_final_poly_len);
1239
1240 let params = SystemParams {
1241 l_skip,
1242 n_stack,
1243 w_stack,
1244 log_blowup,
1245 whir,
1246 logup: log_up_security_params_baby_bear_100_bits(0.0),
1247 max_constraint_degree: 3,
1248 };
1249 run_whir_fib_test(params)
1250}
1251
1252pub fn whir_test_config(k_whir: usize) -> WhirConfig {
1254 WhirConfig {
1255 k: k_whir,
1256 rounds: vec![
1257 WhirRoundConfig { num_queries: 6 },
1258 WhirRoundConfig { num_queries: 5 },
1259 ],
1260 mu_pow_bits: 1,
1261 query_phase_pow_bits: 1,
1262 folding_pow_bits: 1,
1263 proximity: WhirProximityStrategy::UniqueDecoding, }
1266}
1267
1268pub fn whir_multiple_commitments() -> eyre::Result<()> {
1270 setup_tracing_with_log_level(Level::DEBUG);
1271
1272 let mut rng = StdRng::seed_from_u64(42);
1273
1274 let params = SystemParams {
1275 l_skip: 3,
1276 n_stack: 3,
1277 w_stack: 64,
1278 log_blowup: 1,
1279 whir: whir_test_config(2),
1280 logup: log_up_security_params_baby_bear_100_bits(0.0),
1281 max_constraint_degree: 3,
1282 };
1283 let config = BabyBearPoseidon2Config::default_from_params(params);
1284 let params = config.params();
1285
1286 let n_rows = 1 << (params.n_stack + params.l_skip);
1287
1288 let mut matrices = vec![];
1289 let mut commits = vec![];
1290 let mut trees = vec![];
1291
1292 let num_commitments = 5;
1293 for _ in 0..num_commitments {
1294 let n_cols = (rng.random::<u64>() % 10 + 3) as usize;
1295 let data = (0..n_rows * n_cols)
1296 .map(|_| F::from_u64(rng.random()))
1297 .collect_vec();
1298 let mat = ColMajorMatrix::new(data, n_cols);
1299
1300 let (commit, pcs_data) = stacked_commit(
1301 config.hasher(),
1302 params.l_skip,
1303 params.n_stack,
1304 params.log_blowup,
1305 params.k_whir(),
1306 &[&mat],
1307 )?;
1308
1309 matrices.push(mat);
1310 commits.push(commit);
1311 trees.push(pcs_data.tree);
1312 }
1313
1314 debug_assert_eq!(matrices[0].height(), 1 << (params.n_stack + params.l_skip));
1315
1316 let (z_prism, z_cube) = generate_random_z(params, &mut rng);
1317
1318 let mut prover_sponge = default_duplex_sponge_recorder();
1319
1320 let committed_mats = matrices.iter().zip(trees.iter()).collect_vec();
1321 let proof = prove_whir_opening::<SC, _>(
1322 &mut prover_sponge,
1323 config.hasher(),
1324 params.l_skip,
1325 params.log_blowup,
1326 params.whir(),
1327 &committed_mats,
1328 &z_cube,
1329 )?;
1330
1331 let stacking_openings: Vec<Vec<EF>> = matrices
1332 .iter()
1333 .map(|mat| stacking_openings_for_matrix(params, &z_prism, mat))
1334 .collect();
1335
1336 let mut verifier_sponge =
1337 DuplexSpongeValidator::new(poseidon2_perm().clone(), prover_sponge.into_log());
1338 verify_whir::<SC, _>(
1339 &mut verifier_sponge,
1340 &config,
1341 &proof,
1342 &stacking_openings,
1343 &commits,
1344 &z_cube,
1345 )?;
1346 Ok(())
1347}
1348
1349pub fn whir_multiple_commitments_negative() {
1351 setup_tracing_with_log_level(Level::DEBUG);
1352
1353 let mut rng = StdRng::seed_from_u64(42);
1354
1355 let params = SystemParams {
1356 l_skip: 3,
1357 n_stack: 3,
1358 w_stack: 64,
1359 log_blowup: 1,
1360 whir: whir_test_config(2),
1361 logup: log_up_security_params_baby_bear_100_bits(0.0),
1362 max_constraint_degree: 3,
1363 };
1364 let config = BabyBearPoseidon2Config::default_from_params(params);
1365 let params = config.params();
1366
1367 let n_rows = 1 << (params.n_stack + params.l_skip);
1368
1369 let mut matrices = vec![];
1370 let mut commits = vec![];
1371 let mut trees = vec![];
1372
1373 let num_commitments = 5;
1374 for _ in 0..num_commitments {
1375 let n_cols = (rng.random::<u64>() % 10 + 3) as usize;
1376 let data = (0..n_rows * n_cols)
1377 .map(|_| F::from_u64(rng.random()))
1378 .collect_vec();
1379 let mat = ColMajorMatrix::new(data, n_cols);
1380
1381 let (commit, pcs_data) = stacked_commit(
1382 config.hasher(),
1383 params.l_skip,
1384 params.n_stack,
1385 params.log_blowup,
1386 params.k_whir(),
1387 &[&mat],
1388 )
1389 .unwrap();
1390
1391 matrices.push(mat);
1392 commits.push(commit);
1393 trees.push(pcs_data.tree);
1394 }
1395
1396 debug_assert_eq!(matrices[0].height(), 1 << (params.n_stack + params.l_skip));
1397
1398 let (z_prism, z_cube) = generate_random_z(params, &mut rng);
1399
1400 let mut prover_sponge = default_duplex_sponge();
1401 let mut verifier_sponge = default_duplex_sponge();
1402
1403 let committed_mats = matrices.iter().zip(trees.iter()).collect_vec();
1404 let proof = prove_whir_opening::<SC, _>(
1405 &mut prover_sponge,
1406 config.hasher(),
1407 params.l_skip,
1408 params.log_blowup,
1409 params.whir(),
1410 &committed_mats,
1411 &z_cube,
1412 )
1413 .unwrap();
1414
1415 let mut stacking_openings: Vec<Vec<EF>> = matrices
1416 .iter()
1417 .map(|mat| stacking_openings_for_matrix(params, &z_prism, mat))
1418 .collect();
1419
1420 stacking_openings[1][2] = EF::ONE;
1422
1423 assert!(matches!(
1424 verify_whir::<SC, _>(
1425 &mut verifier_sponge,
1426 &config,
1427 &proof,
1428 &stacking_openings,
1429 &commits,
1430 &z_cube,
1431 ),
1432 Err(VerifyWhirError::FinalPolyConstraint)
1433 ));
1434}
1435
1436#[doc(hidden)]
1465#[macro_export]
1467macro_rules! __test_cases {
1468 ($engine:ty, $func:ident, unwrap, { $( $test_name:ident( $($arg:expr),* ) ),+ $(,)? }) => {
1470 $(
1471 #[test]
1472 fn $test_name() {
1473 $crate::$func::<$engine>($($arg),*).unwrap();
1474 }
1475 )+
1476 };
1477 ($engine:ty, $func:ident, { $( $test_name:ident( $($arg:expr),* ) ),+ $(,)? }) => {
1479 $(
1480 #[test]
1481 fn $test_name() {
1482 $crate::$func::<$engine>($($arg),*);
1483 }
1484 )+
1485 };
1486}
1487
1488#[macro_export]
1502macro_rules! backend_test_suite {
1503 ($engine:ty) => {
1504 $crate::__test_cases!($engine, proof_shape_verifier, unwrap, {
1507 test_proof_shape_verifier(),
1508 });
1509
1510 $crate::__test_cases!($engine, proof_shape_verifier_rng_system_params, unwrap, {
1511 test_proof_shape_verifier_rng_system_params(),
1512 });
1513
1514 $crate::__test_cases!($engine, interactions_single_sender_receiver_happy, unwrap, {
1517 test_interactions_single_sender_receiver_happy(),
1518 });
1519
1520 $crate::__test_cases!($engine, single_cached_trace_stark, unwrap, {
1521 test_single_cached_trace_stark(),
1522 });
1523
1524 $crate::__test_cases!($engine, single_preprocessed_trace_stark, unwrap, {
1525 test_single_preprocessed_trace_stark(10),
1526 test_single_preprocessed_trace_stark_log_height_gt_l_skip(3),
1527 test_single_preprocessed_trace_stark_log_height_eq_l_skip(2),
1528 test_single_preprocessed_trace_stark_log_height_lt_l_skip(1),
1529 test_single_preprocessed_trace_stark_log_height_zero(0),
1530 });
1531
1532 $crate::__test_cases!($engine, multi_interaction_traces_stark, unwrap, {
1533 test_multi_interaction_traces_stark(10),
1534 test_multi_interaction_traces_stark_log_height_gt_l_skip(3),
1535 test_multi_interaction_traces_stark_log_height_eq_l_skip(2),
1536 test_multi_interaction_traces_stark_log_height_lt_l_skip(1),
1537 test_multi_interaction_traces_stark_log_height_zero(0),
1538 });
1539
1540 $crate::__test_cases!($engine, mixture_traces_stark, unwrap, {
1541 test_mixture_traces_stark(10),
1542 test_mixture_traces_stark_log_height_gt_l_skip(3),
1543 test_mixture_traces_stark_log_height_eq_l_skip(2),
1544 test_mixture_traces_stark_log_height_lt_l_skip(1),
1545 test_mixture_traces_stark_log_height_zero(0),
1546 });
1547
1548 $crate::__test_cases!($engine, matrix_stacking_overflow, unwrap, {
1549 test_matrix_stacking_overflow(),
1550 });
1551
1552 $crate::__test_cases!($engine, fib_air_roundtrip, unwrap, {
1555 test_fib_air_roundtrip(2, 10),
1556 test_fib_air_roundtrip_log_trace_degree_1_lt_l_skip_2(2, 1),
1557 test_fib_air_roundtrip_log_trace_degree_0_lt_l_skip_2(2, 0),
1558 test_fib_air_roundtrip_log_trace_degree_2_lt_l_skip_3(3, 2),
1559 test_fib_air_roundtrip_large_l_skip(6, 10),
1560 });
1561
1562 $crate::__test_cases!($engine, dummy_interactions_roundtrip, unwrap, {
1563 test_dummy_interactions_roundtrip_2_8_3(2, 8, 3),
1564 test_dummy_interactions_roundtrip_5_5_4(5, 5, 4),
1565 });
1566
1567 $crate::__test_cases!($engine, cached_trace_roundtrip, unwrap, {
1568 test_cached_trace_roundtrip_2_8_3(2, 8, 3),
1569 test_cached_trace_roundtrip_5_5_4(5, 5, 4),
1570 test_cached_trace_roundtrip_5_8_3(5, 8, 3),
1571 test_cached_trace_roundtrip_6_7_3(6, 7, 3),
1572 });
1573
1574 $crate::__test_cases!($engine, preprocessed_trace_roundtrip, unwrap, {
1575 test_preprocessed_trace_roundtrip_2_8_3(2, 8, 3),
1576 test_preprocessed_trace_roundtrip_5_5_4(5, 5, 4),
1577 });
1578
1579 $crate::__test_cases!($engine, preprocessed_and_cached_trace_roundtrip, unwrap, {
1580 test_preprocessed_and_cached_trace_roundtrip_2_8_3_1(2, 8, 3, 1),
1581 test_preprocessed_and_cached_trace_roundtrip_2_8_3_2(2, 8, 3, 2),
1582 test_preprocessed_and_cached_trace_roundtrip_2_8_3_3(2, 8, 3, 3),
1583 test_preprocessed_and_cached_trace_roundtrip_5_5_4_1(5, 5, 4, 1),
1584 test_preprocessed_and_cached_trace_roundtrip_5_5_4_2(5, 5, 4, 2),
1585 test_preprocessed_and_cached_trace_roundtrip_5_5_4_3(5, 5, 4, 3),
1586 });
1587
1588 $crate::__test_cases!($engine, batch_sumcheck_zero_interactions, unwrap, {
1591 test_batch_sumcheck_zero_interactions(4),
1592 test_batch_sumcheck_zero_interactions_log_height_eq_l_skip(2),
1593 test_batch_sumcheck_zero_interactions_log_height_lt_l_skip(1),
1594 test_batch_sumcheck_zero_interactions_log_height_zero(0),
1595 });
1596
1597 $crate::__test_cases!($engine, batch_sumcheck_zero_interactions_malleable_q0, unwrap, {
1598 test_batch_sumcheck_zero_interactions_malleable_q0(),
1599 });
1600
1601 $crate::__test_cases!($engine, gkr_verify_zero_interactions, unwrap, {
1602 test_gkr_verify_zero_interactions(),
1603 });
1604
1605 $crate::__test_cases!($engine, batch_constraints_with_interactions, unwrap, {
1606 test_batch_constraints_with_interactions(),
1607 });
1608
1609 $crate::__test_cases!($engine, single_fib_and_dummy_trace_stark, unwrap, {
1612 test_single_fib_and_dummy_trace_stark(3),
1613 test_single_fib_and_dummy_trace_stark_log_height_eq_l_skip(2),
1614 test_single_fib_and_dummy_trace_stark_log_height_lt_l_skip(1),
1615 test_single_fib_and_dummy_trace_stark_log_height_zero(0),
1616 });
1617
1618 $crate::__test_cases!($engine, optional_air, unwrap, {
1621 test_optional_air(),
1622 });
1623
1624 $crate::__test_cases!($engine, vkey_methods, {
1625 test_vkey_methods(),
1626 });
1627
1628 $crate::__test_cases!($engine, interaction_trace_height_constraints, {
1629 test_interaction_trace_height_constraints(),
1630 });
1631
1632 $crate::__test_cases!($engine, trace_height_constraints_implied_removal, {
1633 test_trace_height_constraints_implied_removal(),
1634 });
1635
1636 $crate::__test_cases!($engine, interaction_multi_rows_neg, {
1637 test_interaction_multi_rows_neg(),
1638 });
1639
1640 $crate::__test_cases!($engine, interaction_all_zero_sender, unwrap, {
1641 test_interaction_all_zero_sender(),
1642 });
1643
1644 $crate::__test_cases!($engine, interaction_multi_senders, unwrap, {
1645 test_interaction_multi_senders(),
1646 });
1647
1648 $crate::__test_cases!($engine, interaction_multi_senders_neg, {
1649 test_interaction_multi_senders_neg(),
1650 });
1651
1652 $crate::__test_cases!($engine, interaction_multi_sender_receiver, unwrap, {
1653 test_interaction_multi_sender_receiver(),
1654 });
1655
1656 $crate::__test_cases!($engine, interaction_cached_trace_neg, {
1657 test_interaction_cached_trace_neg(),
1658 });
1659
1660 #[test]
1663 fn test_fold_single() {
1664 $crate::fold_single();
1665 }
1666
1667 #[test]
1668 fn test_fold_double() {
1669 $crate::fold_double();
1670 }
1671
1672 #[test]
1673 fn test_whir_single_fib_0_1_1_0() {
1674 $crate::whir_single_fib(0, 1, 1, 0).unwrap();
1675 }
1676
1677 #[test]
1678 fn test_whir_single_fib_2_1_1_2() {
1679 $crate::whir_single_fib(2, 1, 1, 2).unwrap();
1680 }
1681
1682 #[test]
1683 fn test_whir_single_fib_2_1_2_0() {
1684 $crate::whir_single_fib(2, 1, 2, 0).unwrap();
1685 }
1686
1687 #[test]
1688 fn test_whir_single_fib_2_1_3_1() {
1689 $crate::whir_single_fib(2, 1, 3, 1).unwrap();
1690 }
1691
1692 #[test]
1693 fn test_whir_single_fib_2_1_4_0() {
1694 $crate::whir_single_fib(2, 1, 4, 0).unwrap();
1695 }
1696
1697 #[test]
1698 fn test_whir_single_fib_2_2_4_0() {
1699 $crate::whir_single_fib(2, 2, 4, 0).unwrap();
1700 }
1701
1702 #[test]
1703 fn test_whir_multiple_commitments() {
1704 $crate::whir_multiple_commitments().unwrap();
1705 }
1706
1707 #[test]
1708 fn test_whir_multiple_commitments_negative() {
1709 $crate::whir_multiple_commitments_negative();
1710 }
1711 };
1712}