1#[cfg(not(feature = "std"))]
2use alloc::{vec, vec::Vec};
3
4use hashbrown::HashSet;
5
6use super::circuit_builder::NUM_COINS_LOOKUP;
7use crate::field::extension::Extendable;
8use crate::field::polynomial::PolynomialCoeffs;
9use crate::fri::proof::{CompressedFriProof, FriChallenges, FriProof, FriProofTarget};
10use crate::fri::verifier::{compute_evaluation, fri_combine_initial, PrecomputedReducedOpenings};
11use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
12use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
13use crate::hash::merkle_tree::MerkleCap;
14use crate::iop::challenger::{Challenger, RecursiveChallenger};
15use crate::iop::target::Target;
16use crate::plonk::circuit_builder::CircuitBuilder;
17use crate::plonk::circuit_data::CommonCircuitData;
18use crate::plonk::config::{AlgebraicHasher, GenericConfig, Hasher};
19use crate::plonk::proof::{
20 CompressedProof, CompressedProofWithPublicInputs, FriInferredElements, OpeningSet,
21 OpeningSetTarget, Proof, ProofChallenges, ProofChallengesTarget, ProofTarget,
22 ProofWithPublicInputs, ProofWithPublicInputsTarget,
23};
24use crate::util::reverse_bits;
25
26fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
27 public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
28 wires_cap: &MerkleCap<F, C::Hasher>,
29 plonk_zs_partial_products_cap: &MerkleCap<F, C::Hasher>,
30 quotient_polys_cap: &MerkleCap<F, C::Hasher>,
31 openings: &OpeningSet<F, D>,
32 commit_phase_merkle_caps: &[MerkleCap<F, C::Hasher>],
33 final_poly: &PolynomialCoeffs<F::Extension>,
34 pow_witness: F,
35 circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
36 common_data: &CommonCircuitData<F, D>,
37) -> anyhow::Result<ProofChallenges<F, D>> {
38 let config = &common_data.config;
39 let num_challenges = config.num_challenges;
40
41 let mut challenger = Challenger::<F, C::Hasher>::new();
42 let has_lookup = common_data.num_lookup_polys != 0;
43
44 common_data.fri_params.observe(&mut challenger);
46
47 challenger.observe_hash::<C::Hasher>(*circuit_digest);
49 challenger.observe_hash::<C::InnerHasher>(public_inputs_hash);
50
51 challenger.observe_cap::<C::Hasher>(wires_cap);
52 let plonk_betas = challenger.get_n_challenges(num_challenges);
53 let plonk_gammas = challenger.get_n_challenges(num_challenges);
54
55 let plonk_deltas = if has_lookup {
58 let num_lookup_challenges = NUM_COINS_LOOKUP * num_challenges;
59 let mut deltas = Vec::with_capacity(num_lookup_challenges);
60 let num_additional_challenges = num_lookup_challenges - 2 * num_challenges;
61 let additional = challenger.get_n_challenges(num_additional_challenges);
62 deltas.extend(&plonk_betas);
63 deltas.extend(&plonk_gammas);
64 deltas.extend(additional);
65 deltas
66 } else {
67 vec![]
68 };
69
70 challenger.observe_cap::<C::Hasher>(plonk_zs_partial_products_cap);
72 let plonk_alphas = challenger.get_n_challenges(num_challenges);
73
74 challenger.observe_cap::<C::Hasher>(quotient_polys_cap);
75 let plonk_zeta = challenger.get_extension_challenge::<D>();
76
77 challenger.observe_openings(&openings.to_fri_openings());
78
79 Ok(ProofChallenges {
80 plonk_betas,
81 plonk_gammas,
82 plonk_alphas,
83 plonk_deltas,
84 plonk_zeta,
85 fri_challenges: challenger.fri_challenges::<C, D>(
86 commit_phase_merkle_caps,
87 final_poly,
88 pow_witness,
89 common_data.degree_bits(),
90 &config.fri_config,
91 None,
92 None,
93 ),
94 })
95}
96
97impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
98 ProofWithPublicInputs<F, C, D>
99{
100 pub(crate) fn fri_query_indices(
101 &self,
102 circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
103 common_data: &CommonCircuitData<F, D>,
104 ) -> anyhow::Result<Vec<usize>> {
105 Ok(self
106 .get_challenges(self.get_public_inputs_hash(), circuit_digest, common_data)?
107 .fri_challenges
108 .fri_query_indices)
109 }
110
111 pub fn get_challenges(
113 &self,
114 public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
115 circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
116 common_data: &CommonCircuitData<F, D>,
117 ) -> anyhow::Result<ProofChallenges<F, D>> {
118 let Proof {
119 wires_cap,
120 plonk_zs_partial_products_cap,
121 quotient_polys_cap,
122 openings,
123 opening_proof:
124 FriProof {
125 commit_phase_merkle_caps,
126 final_poly,
127 pow_witness,
128 ..
129 },
130 } = &self.proof;
131
132 get_challenges::<F, C, D>(
133 public_inputs_hash,
134 wires_cap,
135 plonk_zs_partial_products_cap,
136 quotient_polys_cap,
137 openings,
138 commit_phase_merkle_caps,
139 final_poly,
140 *pow_witness,
141 circuit_digest,
142 common_data,
143 )
144 }
145}
146
147impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
148 CompressedProofWithPublicInputs<F, C, D>
149{
150 pub(crate) fn get_challenges(
152 &self,
153 public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
154 circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
155 common_data: &CommonCircuitData<F, D>,
156 ) -> anyhow::Result<ProofChallenges<F, D>> {
157 let CompressedProof {
158 wires_cap,
159 plonk_zs_partial_products_cap,
160 quotient_polys_cap,
161 openings,
162 opening_proof:
163 CompressedFriProof {
164 commit_phase_merkle_caps,
165 final_poly,
166 pow_witness,
167 ..
168 },
169 } = &self.proof;
170
171 get_challenges::<F, C, D>(
172 public_inputs_hash,
173 wires_cap,
174 plonk_zs_partial_products_cap,
175 quotient_polys_cap,
176 openings,
177 commit_phase_merkle_caps,
178 final_poly,
179 *pow_witness,
180 circuit_digest,
181 common_data,
182 )
183 }
184
185 pub(crate) fn get_inferred_elements(
187 &self,
188 challenges: &ProofChallenges<F, D>,
189 common_data: &CommonCircuitData<F, D>,
190 ) -> FriInferredElements<F, D> {
191 let ProofChallenges {
192 plonk_zeta,
193 fri_challenges:
194 FriChallenges {
195 fri_alpha,
196 fri_betas,
197 fri_query_indices,
198 ..
199 },
200 ..
201 } = challenges;
202 let mut fri_inferred_elements = Vec::new();
203 let mut seen_indices_by_depth =
205 vec![HashSet::new(); common_data.fri_params.reduction_arity_bits.len()];
206 let precomputed_reduced_evals = PrecomputedReducedOpenings::from_os_and_alpha(
207 &self.proof.openings.to_fri_openings(),
208 *fri_alpha,
209 );
210 let log_n = common_data.degree_bits() + common_data.config.fri_config.rate_bits;
211 for &(mut x_index) in fri_query_indices {
214 let mut subgroup_x = F::MULTIPLICATIVE_GROUP_GENERATOR
215 * F::primitive_root_of_unity(log_n).exp_u64(reverse_bits(x_index, log_n) as u64);
216 let mut old_eval = fri_combine_initial::<F, C, D>(
217 &common_data.get_fri_instance(*plonk_zeta),
218 &self
219 .proof
220 .opening_proof
221 .query_round_proofs
222 .initial_trees_proofs[&x_index],
223 *fri_alpha,
224 subgroup_x,
225 &precomputed_reduced_evals,
226 &common_data.fri_params,
227 );
228 for (i, &arity_bits) in common_data
229 .fri_params
230 .reduction_arity_bits
231 .iter()
232 .enumerate()
233 {
234 let coset_index = x_index >> arity_bits;
235 if !seen_indices_by_depth[i].insert(coset_index) {
236 break;
238 }
239 fri_inferred_elements.push(old_eval);
240 let arity = 1 << arity_bits;
241 let mut evals = self.proof.opening_proof.query_round_proofs.steps[i][&coset_index]
242 .evals
243 .clone();
244 let x_index_within_coset = x_index & (arity - 1);
245 evals.insert(x_index_within_coset, old_eval);
246 old_eval = compute_evaluation(
247 subgroup_x,
248 x_index_within_coset,
249 arity_bits,
250 &evals,
251 fri_betas[i],
252 );
253 subgroup_x = subgroup_x.exp_power_of_2(arity_bits);
254 x_index = coset_index;
255 }
256 }
257 FriInferredElements(fri_inferred_elements)
258 }
259}
260
261impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
262 fn get_challenges<C: GenericConfig<D, F = F>>(
263 &mut self,
264 public_inputs_hash: HashOutTarget,
265 wires_cap: &MerkleCapTarget,
266 plonk_zs_partial_products_cap: &MerkleCapTarget,
267 quotient_polys_cap: &MerkleCapTarget,
268 openings: &OpeningSetTarget<D>,
269 commit_phase_merkle_caps: &[MerkleCapTarget],
270 final_poly: &PolynomialCoeffsExtTarget<D>,
271 pow_witness: Target,
272 inner_circuit_digest: HashOutTarget,
273 inner_common_data: &CommonCircuitData<F, D>,
274 ) -> ProofChallengesTarget<D>
275 where
276 C::Hasher: AlgebraicHasher<F>,
277 {
278 let config = &inner_common_data.config;
279 let num_challenges = config.num_challenges;
280
281 let mut challenger = RecursiveChallenger::<F, C::Hasher, D>::new(self);
282 let has_lookup = inner_common_data.num_lookup_polys != 0;
283
284 inner_common_data
286 .fri_params
287 .observe_target(self, &mut challenger);
288
289 challenger.observe_hash(&inner_circuit_digest);
291 challenger.observe_hash(&public_inputs_hash);
292
293 challenger.observe_cap(wires_cap);
294
295 let plonk_betas = challenger.get_n_challenges(self, num_challenges);
296 let plonk_gammas = challenger.get_n_challenges(self, num_challenges);
297
298 let plonk_deltas = if has_lookup {
301 let num_lookup_challenges = NUM_COINS_LOOKUP * num_challenges;
302 let mut deltas = Vec::with_capacity(num_lookup_challenges);
303 let num_additional_challenges = num_lookup_challenges - 2 * num_challenges;
304 let additional = challenger.get_n_challenges(self, num_additional_challenges);
305 deltas.extend(&plonk_betas);
306 deltas.extend(&plonk_gammas);
307 deltas.extend(additional);
308 deltas
309 } else {
310 vec![]
311 };
312
313 challenger.observe_cap(plonk_zs_partial_products_cap);
314 let plonk_alphas = challenger.get_n_challenges(self, num_challenges);
315
316 challenger.observe_cap(quotient_polys_cap);
317 let plonk_zeta = challenger.get_extension_challenge(self);
318
319 challenger.observe_openings(&openings.to_fri_openings());
320
321 ProofChallengesTarget {
322 plonk_betas,
323 plonk_gammas,
324 plonk_alphas,
325 plonk_deltas,
326 plonk_zeta,
327 fri_challenges: challenger.fri_challenges(
328 self,
329 commit_phase_merkle_caps,
330 final_poly,
331 pow_witness,
332 &inner_common_data.config.fri_config,
333 ),
334 }
335 }
336}
337
338impl<const D: usize> ProofWithPublicInputsTarget<D> {
339 pub(crate) fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>>(
340 &self,
341 builder: &mut CircuitBuilder<F, D>,
342 public_inputs_hash: HashOutTarget,
343 inner_circuit_digest: HashOutTarget,
344 inner_common_data: &CommonCircuitData<F, D>,
345 ) -> ProofChallengesTarget<D>
346 where
347 C::Hasher: AlgebraicHasher<F>,
348 {
349 let ProofTarget {
350 wires_cap,
351 plonk_zs_partial_products_cap,
352 quotient_polys_cap,
353 openings,
354 opening_proof:
355 FriProofTarget {
356 commit_phase_merkle_caps,
357 final_poly,
358 pow_witness,
359 ..
360 },
361 } = &self.proof;
362
363 builder.get_challenges::<C>(
364 public_inputs_hash,
365 wires_cap,
366 plonk_zs_partial_products_cap,
367 quotient_polys_cap,
368 openings,
369 commit_phase_merkle_caps,
370 final_poly,
371 *pow_witness,
372 inner_circuit_digest,
373 inner_common_data,
374 )
375 }
376}