1use itertools::{izip, Itertools};
2use p3_baby_bear::BabyBear;
3use p3_commit::PolynomialSpace;
4use p3_field::{AbstractField, TwoAdicField};
5use p3_fri::FriConfig;
6use p3_matrix::Dimensions;
7use p3_util::log2_strict_usize;
8use sp1_recursion_compiler::ir::{Builder, Felt, SymbolicExt, SymbolicFelt};
9use std::{
10 cmp::Reverse,
11 iter::{once, repeat_with, zip},
12};
13
14use crate::{
15 challenger::{CanSampleBitsVariable, FieldChallengerVariable},
16 BabyBearFriConfigVariable, CanObserveVariable, CircuitConfig, Ext, FriChallenges, FriMmcs,
17 FriProofVariable, FriQueryProofVariable, TwoAdicPcsProofVariable, TwoAdicPcsRoundVariable,
18};
19
20pub fn verify_shape_and_sample_challenges<
21 C: CircuitConfig<F = BabyBear>,
22 SC: BabyBearFriConfigVariable<C>,
23>(
24 builder: &mut Builder<C>,
25 config: &FriConfig<FriMmcs<SC>>,
26 proof: &FriProofVariable<C, SC>,
27 challenger: &mut SC::FriChallengerVariable,
28) -> FriChallenges<C> {
29 let betas = proof
30 .commit_phase_commits
31 .iter()
32 .map(|commitment| {
33 challenger.observe(builder, *commitment);
34 challenger.sample_ext(builder)
35 })
36 .collect();
37
38 let final_poly_felts = C::ext2felt(builder, proof.final_poly);
40 final_poly_felts.iter().for_each(|felt| {
41 challenger.observe(builder, *felt);
42 });
43
44 assert_eq!(proof.query_proofs.len(), config.num_queries);
45 challenger.check_witness(builder, config.proof_of_work_bits, proof.pow_witness);
46
47 let log_max_height = proof.commit_phase_commits.len() + config.log_blowup;
48 let query_indices: Vec<Vec<C::Bit>> =
49 repeat_with(|| challenger.sample_bits(builder, log_max_height))
50 .take(config.num_queries)
51 .collect();
52
53 FriChallenges { query_indices, betas }
54}
55
56pub fn verify_two_adic_pcs<C: CircuitConfig<F = SC::Val>, SC: BabyBearFriConfigVariable<C>>(
57 builder: &mut Builder<C>,
58 config: &FriConfig<FriMmcs<SC>>,
59 proof: &TwoAdicPcsProofVariable<C, SC>,
60 challenger: &mut SC::FriChallengerVariable,
61 rounds: Vec<TwoAdicPcsRoundVariable<C, SC>>,
62) {
63 let alpha = challenger.sample_ext(builder);
64
65 let fri_challenges =
66 verify_shape_and_sample_challenges::<C, SC>(builder, config, &proof.fri_proof, challenger);
67
68 let log_global_max_height = proof.fri_proof.commit_phase_commits.len() + config.log_blowup;
69
70 let mut alpha_pows: Vec<Ext<C::F, C::EF>> =
72 vec![builder.eval(SymbolicExt::from_f(C::EF::one()))];
73
74 let reduced_openings = proof
75 .query_openings
76 .iter()
77 .zip(&fri_challenges.query_indices)
78 .map(|(query_opening, index_bits)| {
79 let mut log_height_pow = [0usize; 32];
81 let mut ro: [Ext<C::F, C::EF>; 32] =
82 [builder.eval(SymbolicExt::from_f(C::EF::zero())); 32];
83
84 for (batch_opening, round) in zip(query_opening, rounds.iter().cloned()) {
85 let batch_commit = round.batch_commit;
86 let mats = round.domains_points_and_opens;
87 let batch_heights =
88 mats.iter().map(|mat| mat.domain.size() << config.log_blowup).collect_vec();
89 let batch_dims = batch_heights
90 .iter()
91 .map(|&height| Dimensions { width: 0, height })
92 .collect_vec();
93
94 let batch_max_height = batch_heights.iter().max().expect("Empty batch?");
95 let log_batch_max_height = log2_strict_usize(*batch_max_height);
96 let bits_reduced = log_global_max_height - log_batch_max_height;
97
98 let reduced_index_bits = index_bits[bits_reduced..].to_vec();
99
100 verify_batch::<C, SC>(
101 builder,
102 batch_commit,
103 batch_dims,
104 reduced_index_bits,
105 batch_opening.opened_values.clone(),
106 batch_opening.opening_proof.clone(),
107 );
108
109 for (mat_opening, mat) in izip!(&batch_opening.opened_values, mats) {
110 let mat_domain = mat.domain;
111 let mat_points = mat.points;
112 let mat_values = mat.values;
113 let log_height = log2_strict_usize(mat_domain.size()) + config.log_blowup;
114
115 let bits_reduced = log_global_max_height - log_height;
116 let reduced_index_bits_trunc =
117 index_bits[bits_reduced..(bits_reduced + log_height)].to_vec();
118
119 let g = builder.generator();
120 let two_adic_generator: Felt<_> =
121 builder.eval(C::F::two_adic_generator(log_height));
122 let two_adic_generator_exp =
123 C::exp_reverse_bits(builder, two_adic_generator, reduced_index_bits_trunc);
124 let x: Felt<_> = builder.eval(g * two_adic_generator_exp);
125
126 for (z, ps_at_z) in izip!(mat_points, mat_values) {
127 let mut acc: Ext<C::F, C::EF> =
129 builder.eval(SymbolicExt::from_f(C::EF::zero()));
130 for (p_at_x, p_at_z) in izip!(mat_opening.clone(), ps_at_z) {
131 let pow = log_height_pow[log_height];
132 (alpha_pows.len()..pow + 1).for_each(|_| {
134 let new_alpha = builder.eval(*alpha_pows.last().unwrap() * alpha);
135 builder.reduce_e(new_alpha);
136 alpha_pows.push(new_alpha);
137 });
138 acc = builder.eval(acc + (alpha_pows[pow] * (p_at_z - p_at_x[0])));
139 log_height_pow[log_height] += 1;
140 }
141 ro[log_height] = builder.eval(ro[log_height] + acc / (z - x));
142 }
144 }
145 }
146 ro
147 })
148 .collect::<Vec<_>>();
149
150 verify_challenges::<C, SC>(
151 builder,
152 config,
153 &proof.fri_proof,
154 &fri_challenges,
155 reduced_openings,
156 );
157}
158
159pub fn verify_challenges<C: CircuitConfig<F = SC::Val>, SC: BabyBearFriConfigVariable<C>>(
160 builder: &mut Builder<C>,
161 config: &FriConfig<FriMmcs<SC>>,
162 proof: &FriProofVariable<C, SC>,
163 challenges: &FriChallenges<C>,
164 reduced_openings: Vec<[Ext<C::F, C::EF>; 32]>,
165) {
166 let log_max_height = proof.commit_phase_commits.len() + config.log_blowup;
167 for ((index_bits, query_proof), ro) in
168 challenges.query_indices.iter().zip(&proof.query_proofs).zip(reduced_openings)
169 {
170 let folded_eval = verify_query::<C, SC>(
171 builder,
172 proof.commit_phase_commits.clone(),
173 index_bits,
174 query_proof.clone(),
175 challenges.betas.clone(),
176 ro,
177 log_max_height,
178 );
179
180 builder.assert_ext_eq(folded_eval, proof.final_poly);
181 }
182}
183
184pub fn verify_query<C: CircuitConfig<F = SC::Val>, SC: BabyBearFriConfigVariable<C>>(
185 builder: &mut Builder<C>,
186 commit_phase_commits: Vec<SC::Digest>,
187 index_bits: &[C::Bit],
188 proof: FriQueryProofVariable<C, SC>,
189 betas: Vec<Ext<C::F, C::EF>>,
190 reduced_openings: [Ext<C::F, C::EF>; 32],
191 log_max_height: usize,
192) -> Ext<C::F, C::EF> {
193 let mut folded_eval: Ext<_, _> = builder.constant(C::EF::zero());
194 let two_adic_generator: Felt<_> = builder.constant(C::F::two_adic_generator(log_max_height));
195
196 let x_felt =
197 C::exp_reverse_bits(builder, two_adic_generator, index_bits[..log_max_height].to_vec());
198 let mut x: Ext<_, _> = builder.eval(SymbolicExt::one() * SymbolicFelt::from(x_felt));
199
200 for (offset, log_folded_height, commit, step, beta) in izip!(
201 0..,
202 (0..log_max_height).rev(),
203 commit_phase_commits,
204 &proof.commit_phase_openings,
205 betas,
206 ) {
207 folded_eval = builder.eval(folded_eval + reduced_openings[log_folded_height + 1]);
208
209 let index_sibling_complement: C::Bit = index_bits[offset].clone();
210 let index_pair = &index_bits[(offset + 1)..];
212
213 builder.reduce_e(folded_eval);
214
215 let evals_ext = C::select_chain_ef(
216 builder,
217 index_sibling_complement.clone(),
218 once(folded_eval),
219 once(step.sibling_value),
220 );
221 let evals_felt = vec![
222 C::ext2felt(builder, evals_ext[0]).to_vec(),
223 C::ext2felt(builder, evals_ext[1]).to_vec(),
224 ];
225
226 let dims = &[Dimensions { width: 2, height: (1 << log_folded_height) }];
227 verify_batch::<C, SC>(
228 builder,
229 commit,
230 dims.to_vec(),
231 index_pair.to_vec(),
232 [evals_felt].to_vec(),
233 step.opening_proof.clone(),
234 );
235
236 let xs_new: Ext<_, _> = builder.eval(x * C::EF::two_adic_generator(1));
237 let xs = C::select_chain_ef(builder, index_sibling_complement, once(x), once(xs_new));
238 folded_eval = builder
239 .eval(evals_ext[0] + (beta - xs[0]) * (evals_ext[1] - evals_ext[0]) / (xs[1] - xs[0]));
240 x = builder.eval(x * x);
241 }
242
243 folded_eval
244}
245
246pub fn verify_batch<C: CircuitConfig<F = SC::Val>, SC: BabyBearFriConfigVariable<C>>(
247 builder: &mut Builder<C>,
248 commit: SC::Digest,
249 dimensions: Vec<Dimensions>,
250 index_bits: Vec<C::Bit>,
251 opened_values: Vec<Vec<Vec<Felt<C::F>>>>,
252 proof: Vec<SC::Digest>,
253) {
254 let mut heights_tallest_first =
255 dimensions.iter().enumerate().sorted_by_key(|(_, dims)| Reverse(dims.height)).peekable();
256
257 let mut curr_height_padded = heights_tallest_first.peek().unwrap().1.height.next_power_of_two();
258
259 let ext_slice: Vec<Vec<Felt<C::F>>> = heights_tallest_first
260 .peeking_take_while(|(_, dims)| dims.height.next_power_of_two() == curr_height_padded)
261 .flat_map(|(i, _)| opened_values[i].as_slice())
262 .cloned()
263 .collect::<Vec<_>>();
264 let felt_slice: Vec<Felt<C::F>> =
265 ext_slice.iter().flat_map(|ext| ext.as_slice()).cloned().collect::<Vec<_>>();
266 let mut root: SC::Digest = SC::hash(builder, &felt_slice[..]);
267
268 zip(index_bits, proof).for_each(|(bit, sibling): (C::Bit, SC::Digest)| {
269 let compress_args = SC::select_chain_digest(builder, bit, [root, sibling]);
270
271 root = SC::compress(builder, compress_args);
272 curr_height_padded >>= 1;
273
274 let next_height = heights_tallest_first
275 .peek()
276 .map(|(_, dims)| dims.height)
277 .filter(|h| h.next_power_of_two() == curr_height_padded);
278
279 if let Some(next_height) = next_height {
280 let ext_slice: Vec<Vec<Felt<C::F>>> = heights_tallest_first
281 .peeking_take_while(|(_, dims)| dims.height == next_height)
282 .flat_map(|(i, _)| opened_values[i].as_slice())
283 .cloned()
284 .collect::<Vec<_>>();
285 let felt_slice: Vec<Felt<C::F>> =
286 ext_slice.iter().flat_map(|ext| ext.as_slice()).cloned().collect::<Vec<_>>();
287 let next_height_openings_digest = SC::hash(builder, &felt_slice);
288 root = SC::compress(builder, [root, next_height_openings_digest]);
289 }
290 });
291
292 SC::assert_digest_eq(builder, root, commit);
293}
294
295#[cfg(test)]
296mod tests {
297 use super::*;
298 use crate::{
299 challenger::DuplexChallengerVariable, utils::tests::run_test_recursion,
300 BatchOpeningVariable, FriCommitPhaseProofStepVariable, FriProofVariable,
301 FriQueryProofVariable, TwoAdicPcsMatsVariable, TwoAdicPcsProofVariable,
302 };
303 use p3_challenger::{CanObserve, CanSample, FieldChallenger};
304 use p3_commit::{Pcs, TwoAdicMultiplicativeCoset};
305 use p3_field::AbstractField;
306 use p3_fri::{verifier, TwoAdicFriPcsProof};
307 use p3_matrix::dense::RowMajorMatrix;
308 use rand::{
309 rngs::{OsRng, StdRng},
310 SeedableRng,
311 };
312 use sp1_recursion_compiler::{
313 asm::AsmBuilder,
314 config::InnerConfig,
315 ir::{Builder, Ext, SymbolicExt},
316 };
317 use sp1_stark::{
318 baby_bear_poseidon2::BabyBearPoseidon2, inner_fri_config, inner_perm, InnerChallenge,
319 InnerChallengeMmcs, InnerChallenger, InnerCompress, InnerDft, InnerFriProof, InnerHash,
320 InnerPcs, InnerVal, InnerValMmcs, StarkGenericConfig,
321 };
322
323 use sp1_recursion_core_v2::DIGEST_SIZE;
324
325 use crate::Digest;
326
327 type C = InnerConfig;
328 type SC = BabyBearPoseidon2;
329 type F = <SC as StarkGenericConfig>::Val;
330 type EF = <SC as StarkGenericConfig>::Challenge;
331
332 pub fn const_fri_proof(
333 builder: &mut AsmBuilder<F, EF>,
334 fri_proof: InnerFriProof,
335 ) -> FriProofVariable<InnerConfig, SC> {
336 let commit_phase_commits = fri_proof
338 .commit_phase_commits
339 .iter()
340 .map(|commit| {
341 let commit: [F; DIGEST_SIZE] = (*commit).into();
342 commit.map(|x| builder.eval(x))
343 })
344 .collect::<Vec<_>>();
345
346 let query_proofs = fri_proof
348 .query_proofs
349 .iter()
350 .map(|query_proof| {
351 let commit_phase_openings = query_proof
352 .commit_phase_openings
353 .iter()
354 .map(|commit_phase_opening| {
355 let sibling_value =
356 builder.eval(SymbolicExt::from_f(commit_phase_opening.sibling_value));
357 let opening_proof = commit_phase_opening
358 .opening_proof
359 .iter()
360 .map(|sibling| sibling.map(|x| builder.eval(x)))
361 .collect::<Vec<_>>();
362 FriCommitPhaseProofStepVariable { sibling_value, opening_proof }
363 })
364 .collect::<Vec<_>>();
365 FriQueryProofVariable { commit_phase_openings }
366 })
367 .collect::<Vec<_>>();
368
369 FriProofVariable {
371 commit_phase_commits,
372 query_proofs,
373 final_poly: builder.eval(SymbolicExt::from_f(fri_proof.final_poly)),
374 pow_witness: builder.eval(fri_proof.pow_witness),
375 }
376 }
377
378 pub fn const_two_adic_pcs_proof(
379 builder: &mut Builder<InnerConfig>,
380 proof: TwoAdicFriPcsProof<InnerVal, InnerChallenge, InnerValMmcs, InnerChallengeMmcs>,
381 ) -> TwoAdicPcsProofVariable<InnerConfig, SC> {
382 let fri_proof = const_fri_proof(builder, proof.fri_proof);
383 let query_openings = proof
384 .query_openings
385 .iter()
386 .map(|query_opening| {
387 query_opening
388 .iter()
389 .map(|opening| BatchOpeningVariable {
390 opened_values: opening
391 .opened_values
392 .iter()
393 .map(|opened_value| {
394 opened_value
395 .iter()
396 .map(|value| vec![builder.eval::<Felt<_>, _>(*value)])
397 .collect::<Vec<_>>()
398 })
399 .collect::<Vec<_>>(),
400 opening_proof: opening
401 .opening_proof
402 .iter()
403 .map(|opening_proof| opening_proof.map(|x| builder.eval(x)))
404 .collect::<Vec<_>>(),
405 })
406 .collect::<Vec<_>>()
407 })
408 .collect::<Vec<_>>();
409 TwoAdicPcsProofVariable { fri_proof, query_openings }
410 }
411
412 #[allow(clippy::type_complexity)]
413 pub fn const_two_adic_pcs_rounds(
414 builder: &mut Builder<InnerConfig>,
415 commit: [F; DIGEST_SIZE],
416 os: Vec<(TwoAdicMultiplicativeCoset<InnerVal>, Vec<(InnerChallenge, Vec<InnerChallenge>)>)>,
417 ) -> (Digest<InnerConfig, SC>, Vec<TwoAdicPcsRoundVariable<InnerConfig, SC>>) {
418 let commit: Digest<InnerConfig, SC> = commit.map(|x| builder.eval(x));
419
420 let mut domains_points_and_opens = Vec::new();
421 for (domain, poly) in os.into_iter() {
422 let points: Vec<Ext<InnerVal, InnerChallenge>> =
423 poly.iter().map(|(p, _)| builder.eval(SymbolicExt::from_f(*p))).collect::<Vec<_>>();
424 let values: Vec<Vec<Ext<InnerVal, InnerChallenge>>> = poly
425 .iter()
426 .map(|(_, v)| {
427 v.clone()
428 .iter()
429 .map(|t| builder.eval(SymbolicExt::from_f(*t)))
430 .collect::<Vec<_>>()
431 })
432 .collect::<Vec<_>>();
433 let domain_points_and_values = TwoAdicPcsMatsVariable { domain, points, values };
434 domains_points_and_opens.push(domain_points_and_values);
435 }
436
437 (commit, vec![TwoAdicPcsRoundVariable { batch_commit: commit, domains_points_and_opens }])
438 }
439
440 #[test]
442 fn size_gaps() {
443 use p3_commit::Mmcs;
444 let perm = inner_perm();
445 let hash = InnerHash::new(perm.clone());
446 let compress = InnerCompress::new(perm);
447 let mmcs = InnerValMmcs::new(hash, compress);
448
449 let mut builder = Builder::<InnerConfig>::default();
450
451 let large_mats = (0..4).map(|_| RowMajorMatrix::<F>::rand(&mut OsRng, 1000, 8));
453 let large_mat_dims = (0..4).map(|_| Dimensions { height: 1000, width: 8 });
454
455 let medium_mats = (0..5).map(|_| RowMajorMatrix::<F>::rand(&mut OsRng, 70, 8));
457 let medium_mat_dims = (0..5).map(|_| Dimensions { height: 70, width: 8 });
458
459 let small_mats = (0..6).map(|_| RowMajorMatrix::<F>::rand(&mut OsRng, 8, 8));
461 let small_mat_dims = (0..6).map(|_| Dimensions { height: 8, width: 8 });
462
463 let (commit, prover_data) =
464 mmcs.commit(large_mats.chain(medium_mats).chain(small_mats).collect_vec());
465
466 let commit: [_; DIGEST_SIZE] = commit.into();
467 let commit = commit.map(|x| builder.eval(x));
468 let (opened_values, proof) = mmcs.open_batch(6, &prover_data);
470 let opened_values = opened_values
471 .into_iter()
472 .map(|x| x.into_iter().map(|y| vec![builder.eval::<Felt<_>, _>(y)]).collect())
473 .collect();
474 let index = builder.eval(F::from_canonical_u32(6));
475 let index_bits = C::num2bits(&mut builder, index, 32);
476 let proof = proof.into_iter().map(|p| p.map(|x| builder.eval(x))).collect();
477 verify_batch::<_, SC>(
478 &mut builder,
479 commit,
480 large_mat_dims.chain(medium_mat_dims).chain(small_mat_dims).collect_vec(),
481 index_bits,
482 opened_values,
483 proof,
484 );
485 }
486
487 #[test]
488 fn test_fri_verify_shape_and_sample_challenges() {
489 let mut rng = &mut OsRng;
490 let log_degrees = &[16, 9, 7, 4, 2];
491 let perm = inner_perm();
492 let fri_config = inner_fri_config();
493 let hash = InnerHash::new(perm.clone());
494 let compress = InnerCompress::new(perm.clone());
495 let val_mmcs = InnerValMmcs::new(hash, compress);
496 let dft = InnerDft {};
497 let pcs: InnerPcs =
498 InnerPcs::new(log_degrees.iter().copied().max().unwrap(), dft, val_mmcs, fri_config);
499
500 let domains_and_polys = log_degrees
502 .iter()
503 .map(|&d| {
504 (
505 <InnerPcs as Pcs<InnerChallenge, InnerChallenger>>::natural_domain_for_degree(
506 &pcs,
507 1 << d,
508 ),
509 RowMajorMatrix::<InnerVal>::rand(&mut rng, 1 << d, 10),
510 )
511 })
512 .collect::<Vec<_>>();
513 let (commit, data) = <InnerPcs as Pcs<InnerChallenge, InnerChallenger>>::commit(
514 &pcs,
515 domains_and_polys.clone(),
516 );
517 let mut challenger = InnerChallenger::new(perm.clone());
518 challenger.observe(commit);
519 let zeta = challenger.sample_ext_element::<InnerChallenge>();
520 let points = repeat_with(|| vec![zeta]).take(domains_and_polys.len()).collect::<Vec<_>>();
521 let (_, proof) = pcs.open(vec![(&data, points)], &mut challenger);
522
523 let mut challenger = InnerChallenger::new(perm.clone());
525 challenger.observe(commit);
526 let _: InnerChallenge = challenger.sample();
527 let fri_challenges_gt = verifier::verify_shape_and_sample_challenges(
528 &inner_fri_config(),
529 &proof.fri_proof,
530 &mut challenger,
531 )
532 .unwrap();
533
534 let mut builder = Builder::<InnerConfig>::default();
536 let config = inner_fri_config();
537 let fri_proof = const_fri_proof(&mut builder, proof.fri_proof);
538
539 let mut challenger = DuplexChallengerVariable::new(&mut builder);
540 let commit: [_; DIGEST_SIZE] = commit.into();
541 let commit: [Felt<InnerVal>; DIGEST_SIZE] = commit.map(|x| builder.eval(x));
542 challenger.observe_slice(&mut builder, commit);
543 let _ = challenger.sample_ext(&mut builder);
544 let fri_challenges = verify_shape_and_sample_challenges::<InnerConfig, BabyBearPoseidon2>(
545 &mut builder,
546 &config,
547 &fri_proof,
548 &mut challenger,
549 );
550
551 for i in 0..fri_challenges_gt.betas.len() {
552 builder.assert_ext_eq(
553 SymbolicExt::from_f(fri_challenges_gt.betas[i]),
554 fri_challenges.betas[i],
555 );
556 }
557
558 for i in 0..fri_challenges_gt.query_indices.len() {
559 let query_indices =
560 C::bits2num(&mut builder, fri_challenges.query_indices[i].iter().cloned());
561 builder.assert_felt_eq(
562 F::from_canonical_usize(fri_challenges_gt.query_indices[i]),
563 query_indices,
564 );
565 }
566
567 run_test_recursion(builder.operations, None);
568 }
569
570 #[test]
571 fn test_verify_two_adic_pcs_inner() {
572 let mut rng = StdRng::seed_from_u64(0xDEADBEEF);
573 let log_degrees = &[19, 19];
574 let perm = inner_perm();
575 let fri_config = inner_fri_config();
576 let hash = InnerHash::new(perm.clone());
577 let compress = InnerCompress::new(perm.clone());
578 let val_mmcs = InnerValMmcs::new(hash, compress);
579 let dft = InnerDft {};
580 let pcs: InnerPcs =
581 InnerPcs::new(log_degrees.iter().copied().max().unwrap(), dft, val_mmcs, fri_config);
582
583 let domains_and_polys = log_degrees
585 .iter()
586 .map(|&d| {
587 (
588 <InnerPcs as Pcs<InnerChallenge, InnerChallenger>>::natural_domain_for_degree(
589 &pcs,
590 1 << d,
591 ),
592 RowMajorMatrix::<InnerVal>::rand(&mut rng, 1 << d, 100),
593 )
594 })
595 .collect::<Vec<_>>();
596 let (commit, data) = <InnerPcs as Pcs<InnerChallenge, InnerChallenger>>::commit(
597 &pcs,
598 domains_and_polys.clone(),
599 );
600 let mut challenger = InnerChallenger::new(perm.clone());
601 challenger.observe(commit);
602 let zeta = challenger.sample_ext_element::<InnerChallenge>();
603 let points = domains_and_polys.iter().map(|_| vec![zeta]).collect::<Vec<_>>();
604 let (opening, proof) = pcs.open(vec![(&data, points)], &mut challenger);
605
606 let mut challenger = InnerChallenger::new(perm.clone());
608 challenger.observe(commit);
609 let x1 = challenger.sample_ext_element::<InnerChallenge>();
610 let os = domains_and_polys
611 .iter()
612 .zip(&opening[0])
613 .map(|((domain, _), mat_openings)| (*domain, vec![(zeta, mat_openings[0].clone())]))
614 .collect::<Vec<_>>();
615 pcs.verify(vec![(commit, os.clone())], &proof, &mut challenger).unwrap();
616
617 let mut builder = Builder::<InnerConfig>::default();
619 let config = inner_fri_config();
620 let proof = const_two_adic_pcs_proof(&mut builder, proof);
621 let (commit, rounds) = const_two_adic_pcs_rounds(&mut builder, commit.into(), os);
622 let mut challenger = DuplexChallengerVariable::new(&mut builder);
623 challenger.observe_slice(&mut builder, commit);
624 let x2 = challenger.sample_ext(&mut builder);
625 let x1: Ext<_, _> = builder.constant(x1);
626 builder.assert_ext_eq(x1, x2);
627 verify_two_adic_pcs::<_, BabyBearPoseidon2>(
628 &mut builder,
629 &config,
630 &proof,
631 &mut challenger,
632 rounds,
633 );
634
635 run_test_recursion(builder.operations, std::iter::empty());
636 }
637}