1use alloc::vec::Vec;
2use core::cell::RefCell;
3
4use itertools::Itertools;
5use p3_challenger::{CanObserve, FieldChallenger, GrindingChallenger};
6use p3_commit::{BatchOpening, Mmcs, OpenedValues, Pcs, PolynomialSpace};
7use p3_dft::TwoAdicSubgroupDft;
8use p3_field::coset::TwoAdicMultiplicativeCoset;
9use p3_field::{ExtensionField, Field, TwoAdicField, batch_multiplicative_inverse};
10use p3_matrix::Matrix;
11use p3_matrix::bitrev::{BitReversalPerm, BitReversibleMatrix};
12use p3_matrix::dense::{DenseMatrix, RowMajorMatrix, RowMajorMatrixView};
13use p3_matrix::horizontally_truncated::HorizontallyTruncated;
14use p3_matrix::row_index_mapped::RowIndexMappedView;
15use p3_util::zip_eq::zip_eq;
16use rand::Rng;
17use rand::distr::{Distribution, StandardUniform};
18use tracing::{info_span, instrument};
19
20use crate::verifier::FriError;
21use crate::{FriParameters, FriProof, TwoAdicFriPcs};
22
23#[derive(Clone, Debug)]
26pub struct HidingFriPcs<Val, Dft, InputMmcs, FriMmcs, R> {
27 inner: TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs>,
28 num_random_codewords: usize,
29 rng: RefCell<R>,
30}
31
32impl<Val, Dft, InputMmcs, FriMmcs, R> HidingFriPcs<Val, Dft, InputMmcs, FriMmcs, R> {
33 pub fn new(
34 dft: Dft,
35 mmcs: InputMmcs,
36 params: FriParameters<FriMmcs>,
37 num_random_codewords: usize,
38 rng: R,
39 ) -> Self {
40 let inner = TwoAdicFriPcs::new(dft, mmcs, params);
41 Self {
42 inner,
43 num_random_codewords,
44 rng: rng.into(),
45 }
46 }
47}
48
49impl<Val, Dft, InputMmcs, FriMmcs, Challenge, Challenger, R> Pcs<Challenge, Challenger>
50 for HidingFriPcs<Val, Dft, InputMmcs, FriMmcs, R>
51where
52 Val: TwoAdicField,
53 StandardUniform: Distribution<Val>,
54 Dft: TwoAdicSubgroupDft<Val>,
55 InputMmcs: Mmcs<Val>,
56 FriMmcs: Mmcs<Challenge>,
57 Challenge: TwoAdicField + ExtensionField<Val>,
58 Challenger:
59 FieldChallenger<Val> + CanObserve<FriMmcs::Commitment> + GrindingChallenger<Witness = Val>,
60 R: Rng + Send + Sync,
61{
62 type Domain = TwoAdicMultiplicativeCoset<Val>;
63 type Commitment = InputMmcs::Commitment;
64 type ProverData = InputMmcs::ProverData<RowMajorMatrix<Val>>;
65 type EvaluationsOnDomain<'a> = HorizontallyTruncated<
66 Val,
67 RowIndexMappedView<BitReversalPerm, RowMajorMatrixView<'a, Val>>,
68 >;
69 type Proof = (
72 OpenedValues<Challenge>,
73 FriProof<Challenge, FriMmcs, Val, Vec<BatchOpening<Val, InputMmcs>>>,
74 );
75 type Error = FriError<FriMmcs::Error, InputMmcs::Error>;
76
77 const ZK: bool = true;
78
79 fn natural_domain_for_degree(&self, degree: usize) -> Self::Domain {
80 <TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs> as Pcs<Challenge, Challenger>>::natural_domain_for_degree(
81 &self.inner, degree)
82 }
83
84 fn commit(
85 &self,
86 evaluations: impl IntoIterator<Item = (Self::Domain, RowMajorMatrix<Val>)>,
87 ) -> (Self::Commitment, Self::ProverData) {
88 let randomized_evaluations: Vec<(Self::Domain, RowMajorMatrix<Val>)> =
89 info_span!("randomize polys").in_scope(|| {
90 evaluations
91 .into_iter()
92 .map(|(domain, mat)| {
93 let mat_width = mat.width();
94 let mut random_evaluation = add_random_cols(
99 &mat,
100 mat_width + 2 * self.num_random_codewords,
101 &mut *self.rng.borrow_mut(),
102 );
103 random_evaluation.width = mat_width + self.num_random_codewords;
104
105 (domain, random_evaluation)
106 })
107 .collect()
108 });
109
110 Pcs::<Challenge, Challenger>::commit(&self.inner, randomized_evaluations)
111 }
112
113 fn commit_preprocessing(
114 &self,
115 evaluations: impl IntoIterator<Item = (Self::Domain, RowMajorMatrix<Val>)>,
116 ) -> (Self::Commitment, Self::ProverData) {
117 let padded_evals = evaluations
119 .into_iter()
120 .map(|(domain, mat)| {
121 let mat_width = mat.width();
122 let mut padded_evaluation = add_zero_cols(&mat, mat_width);
126 padded_evaluation.width = mat_width;
127 (domain, padded_evaluation)
128 })
129 .collect::<Vec<_>>();
130
131 Pcs::<Challenge, Challenger>::commit(&self.inner, padded_evals)
132 }
133
134 fn get_quotient_ldes(
149 &self,
150 evaluations: impl IntoIterator<Item = (Self::Domain, RowMajorMatrix<Val>)>,
151 num_chunks: usize,
152 ) -> Vec<RowMajorMatrix<Val>> {
153 let (domains, evaluations): (Vec<_>, Vec<_>) = evaluations.into_iter().unzip();
154 let cis = get_zp_cis(&domains);
155 let last_chunk = num_chunks - 1;
156 let last_chunk_ci_inv = cis[last_chunk].inverse();
157 let mul_coeffs = (0..last_chunk)
158 .map(|i| cis[i] * last_chunk_ci_inv)
159 .collect_vec();
160
161 let mut rng = self.rng.borrow_mut();
162 let randomized_evaluations: Vec<RowMajorMatrix<Val>> = evaluations
163 .into_iter()
164 .map(|mat| add_random_cols(&mat, self.num_random_codewords, &mut *rng))
165 .collect();
166 let h = randomized_evaluations[0].height();
170 let w = randomized_evaluations[0].width();
171 let mut all_random_values = (0..(randomized_evaluations.len() - 1) * h * w)
172 .map(|_| rng.random())
173 .chain(core::iter::repeat_n(Val::ZERO, h * w))
174 .collect::<Vec<_>>();
175
176 for j in 0..last_chunk {
178 let mul_coeff = mul_coeffs[j];
179 for k in 0..h * w {
180 let t = all_random_values[j * h * w + k] * mul_coeff;
181 all_random_values[last_chunk * h * w + k] -= t;
182 }
183 }
184
185 domains
186 .into_iter()
187 .zip(randomized_evaluations)
188 .enumerate()
189 .map(|(i, (domain, evals))| {
190 assert_eq!(domain.size(), evals.height());
191 let shift = Val::GENERATOR / domain.shift();
192 let random_values = &all_random_values[i * h * w..(i + 1) * h * w];
193
194 let mut lde_evals = self
196 .inner
197 .dft
198 .coset_lde_batch(evals, self.inner.fri.log_blowup + 1, shift)
199 .to_row_major_matrix();
200
201 let mut vanishing_poly_coeffs =
205 Val::zero_vec((h * w) << (self.inner.fri.log_blowup + 1));
206 let p = shift.exp_u64(h as u64);
207 Val::GENERATOR
208 .powers()
209 .take(h)
210 .enumerate()
211 .for_each(|(i, p_i)| {
212 for j in 0..w {
213 let mul_coeff = p_i * random_values[i * w + j];
214 vanishing_poly_coeffs[i * w + j] -= mul_coeff;
215 vanishing_poly_coeffs[(h + i) * w + j] = p * mul_coeff;
216 }
217 });
218 let random_eval = self
219 .inner
220 .dft
221 .dft_batch(DenseMatrix::new(vanishing_poly_coeffs, w))
222 .to_row_major_matrix();
223
224 for i in 0..h * w * (1 << (self.inner.fri.log_blowup + 1)) {
226 lde_evals.values[i] += random_eval.values[i];
227 }
228
229 lde_evals.bit_reverse_rows().to_row_major_matrix()
230 })
231 .collect()
232 }
233
234 fn commit_ldes(&self, ldes: Vec<RowMajorMatrix<Val>>) -> (Self::Commitment, Self::ProverData) {
235 Pcs::<Challenge, Challenger>::commit_ldes(&self.inner, ldes)
236 }
237
238 fn get_evaluations_on_domain<'a>(
239 &self,
240 prover_data: &'a Self::ProverData,
241 idx: usize,
242 domain: Self::Domain,
243 ) -> Self::EvaluationsOnDomain<'a> {
244 let inner_evals = <TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs> as Pcs<
245 Challenge,
246 Challenger,
247 >>::get_evaluations_on_domain(
248 &self.inner, prover_data, idx, domain
249 );
250 let inner_width = inner_evals.width();
251 HorizontallyTruncated::new(inner_evals, inner_width - self.num_random_codewords).unwrap()
254 }
255
256 fn get_evaluations_on_domain_no_random<'a>(
257 &self,
258 prover_data: &'a Self::ProverData,
259 idx: usize,
260 domain: Self::Domain,
261 ) -> Self::EvaluationsOnDomain<'a> {
262 let inner_evals = <TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs> as Pcs<
263 Challenge,
264 Challenger,
265 >>::get_evaluations_on_domain(
266 &self.inner, prover_data, idx, domain
267 );
268 let inner_width = inner_evals.width();
269
270 HorizontallyTruncated::new(inner_evals, inner_width).unwrap()
271 }
272
273 fn open(
274 &self,
275 rounds: Vec<(
277 &Self::ProverData,
278 Vec<
280 Vec<Challenge>,
282 >,
283 )>,
284 challenger: &mut Challenger,
285 ) -> (OpenedValues<Challenge>, Self::Proof) {
286 self.open_with_preprocessing(rounds, challenger, false)
287 }
288
289 fn open_with_preprocessing(
290 &self,
291 rounds: Vec<(
293 &Self::ProverData,
294 Vec<
296 Vec<Challenge>,
298 >,
299 )>,
300 challenger: &mut Challenger,
301 is_preprocessing: bool,
302 ) -> (OpenedValues<Challenge>, Self::Proof) {
303 let (mut inner_opened_values, inner_proof) =
304 self.inner
305 .open_with_preprocessing(rounds, challenger, is_preprocessing);
306 let opened_values_rand = inner_opened_values
309 .iter_mut()
310 .enumerate()
311 .map(|(idx, opened_values_for_round)| {
312 opened_values_for_round
313 .iter_mut()
314 .map(|opened_values_for_mat| {
315 opened_values_for_mat
316 .iter_mut()
317 .map(|opened_values_for_point| {
318 let num_random_codewords =
319 if is_preprocessing && idx == <Self as Pcs<Challenge, Challenger>>::PREPROCESSED_TRACE_IDX {
320 0
321 } else {
322 self.num_random_codewords
323 };
324 let split = opened_values_for_point.len() - num_random_codewords;
325 opened_values_for_point.drain(split..).collect()
326 })
327 .collect()
328 })
329 .collect()
330 })
331 .collect();
332
333 (inner_opened_values, (opened_values_rand, inner_proof))
334 }
335
336 fn verify(
337 &self,
338 mut rounds: Vec<(
340 Self::Commitment,
341 Vec<(
343 Self::Domain,
345 Vec<(
347 Challenge,
349 Vec<Challenge>,
351 )>,
352 )>,
353 )>,
354 proof: &Self::Proof,
355 challenger: &mut Challenger,
356 ) -> Result<(), Self::Error> {
357 let (opened_values_for_rand_cws, inner_proof) = proof;
358 for (round, rand_round) in zip_eq(
362 rounds.iter_mut(),
363 opened_values_for_rand_cws,
364 FriError::InvalidProofShape,
365 )? {
366 for (mat, rand_mat) in
367 zip_eq(round.1.iter_mut(), rand_round, FriError::InvalidProofShape)?
368 {
369 for (point, rand_point) in
370 zip_eq(mat.1.iter_mut(), rand_mat, FriError::InvalidProofShape)?
371 {
372 point.1.extend(rand_point);
373 }
374 }
375 }
376 self.inner.verify(rounds, inner_proof, challenger)
377 }
378
379 fn get_opt_randomization_poly_commitment(
380 &self,
381 ext_trace_domains: impl IntoIterator<Item = Self::Domain>,
382 ) -> Option<(Self::Commitment, Self::ProverData)> {
383 let random_input_vals = ext_trace_domains
384 .into_iter()
385 .map(|domain| {
386 let m = DenseMatrix::rand(
387 &mut *self.rng.borrow_mut(),
388 domain.size(),
389 self.num_random_codewords + Challenge::DIMENSION,
390 );
391
392 (domain, m)
393 })
394 .collect::<Vec<_>>();
395
396 let r_commit_and_data =
397 Pcs::<Challenge, Challenger>::commit(&self.inner, random_input_vals);
398 Some(r_commit_and_data)
399 }
400}
401
402#[instrument(level = "debug", skip_all)]
403fn add_random_cols<Val, R>(
404 mat: &RowMajorMatrix<Val>,
405 num_random_codewords: usize,
406 mut rng: R,
407) -> RowMajorMatrix<Val>
408where
409 Val: Field,
410 R: Rng + Send + Sync,
411 StandardUniform: Distribution<Val>,
412{
413 let old_w = mat.width();
414 let new_w = old_w + num_random_codewords;
415 let h = mat.height();
416
417 let new_values = Val::zero_vec(new_w * h);
418 let mut result = RowMajorMatrix::new(new_values, new_w);
419 result
422 .rows_mut()
423 .zip(mat.row_slices())
424 .for_each(|(new_row, old_row)| {
425 new_row[..old_w].copy_from_slice(old_row);
426 new_row[old_w..].iter_mut().for_each(|v| *v = rng.random());
427 });
428 result
429}
430
431#[instrument(level = "debug", skip_all)]
434fn add_zero_cols<Val>(mat: &RowMajorMatrix<Val>, num_extra_columns: usize) -> RowMajorMatrix<Val>
435where
436 Val: Field,
437{
438 let old_w = mat.width();
439 let new_w = old_w + num_extra_columns;
440 let h = mat.height();
441
442 let new_values = Val::zero_vec(new_w * h);
443 let mut result = RowMajorMatrix::new(new_values, new_w);
444
445 result
446 .rows_mut()
447 .zip(mat.row_slices())
448 .for_each(|(new_row, old_row)| {
449 new_row[..old_w].copy_from_slice(old_row);
450 });
451 result
452}
453
454fn get_zp_cis<D: PolynomialSpace>(qc_domains: &[D]) -> Vec<p3_commit::Val<D>> {
457 batch_multiplicative_inverse(
458 &qc_domains
459 .iter()
460 .enumerate()
461 .map(|(i, domain)| {
462 qc_domains
463 .iter()
464 .enumerate()
465 .filter(|(j, _)| *j != i)
466 .map(|(_, other_domain)| {
467 other_domain.vanishing_poly_at_point(domain.first_point())
468 })
469 .product()
470 })
471 .collect::<Vec<_>>(),
472 )
473}