1use alloc::vec::Vec;
2use core::cell::RefCell;
3use core::fmt::Debug;
4
5use itertools::Itertools;
6use p3_challenger::{CanObserve, FieldChallenger, GrindingChallenger};
7use p3_commit::{BatchOpening, Mmcs, OpenedValues, Pcs, PolynomialSpace};
8use p3_dft::TwoAdicSubgroupDft;
9use p3_field::coset::TwoAdicMultiplicativeCoset;
10use p3_field::{ExtensionField, Field, TwoAdicField, batch_multiplicative_inverse};
11use p3_matrix::Matrix;
12use p3_matrix::bitrev::{BitReversalPerm, BitReversibleMatrix};
13use p3_matrix::dense::{DenseMatrix, RowMajorMatrix, RowMajorMatrixView};
14use p3_matrix::horizontally_truncated::HorizontallyTruncated;
15use p3_matrix::row_index_mapped::RowIndexMappedView;
16use p3_util::zip_eq::zip_eq;
17use rand::Rng;
18use rand::distr::{Distribution, StandardUniform};
19use tracing::{info_span, instrument};
20
21use crate::verifier::FriError;
22use crate::{FriParameters, FriProof, TwoAdicFriPcs};
23
24#[derive(Debug)]
27pub struct HidingFriPcs<Val, Dft, InputMmcs, FriMmcs, R> {
28 inner: TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs>,
29 num_random_codewords: usize,
30 rng: RefCell<R>,
31}
32
33impl<Val, Dft, InputMmcs, FriMmcs, R> HidingFriPcs<Val, Dft, InputMmcs, FriMmcs, R> {
34 pub fn new(
35 dft: Dft,
36 mmcs: InputMmcs,
37 params: FriParameters<FriMmcs>,
38 num_random_codewords: usize,
39 rng: R,
40 ) -> Self {
41 let inner = TwoAdicFriPcs::new(dft, mmcs, params);
42 Self {
43 inner,
44 num_random_codewords,
45 rng: rng.into(),
46 }
47 }
48}
49
50impl<Val, Dft, InputMmcs, FriMmcs, Challenge, Challenger, R> Pcs<Challenge, Challenger>
51 for HidingFriPcs<Val, Dft, InputMmcs, FriMmcs, R>
52where
53 Val: TwoAdicField,
54 StandardUniform: Distribution<Val>,
55 Dft: TwoAdicSubgroupDft<Val>,
56 InputMmcs: Mmcs<Val>,
57 FriMmcs: Mmcs<Challenge>,
58 Challenge: TwoAdicField + ExtensionField<Val>,
59 Challenger:
60 FieldChallenger<Val> + CanObserve<FriMmcs::Commitment> + GrindingChallenger<Witness = Val>,
61 R: Rng + Send + Sync,
62{
63 type Domain = TwoAdicMultiplicativeCoset<Val>;
64 type Commitment = InputMmcs::Commitment;
65 type ProverData = InputMmcs::ProverData<RowMajorMatrix<Val>>;
66 type EvaluationsOnDomain<'a> = HorizontallyTruncated<
67 Val,
68 RowIndexMappedView<BitReversalPerm, RowMajorMatrixView<'a, Val>>,
69 >;
70 type Proof = (
73 OpenedValues<Challenge>,
74 FriProof<Challenge, FriMmcs, Val, Vec<BatchOpening<Val, InputMmcs>>>,
75 );
76 type Error = FriError<FriMmcs::Error, InputMmcs::Error>;
77
78 const ZK: bool = true;
79
80 fn natural_domain_for_degree(&self, degree: usize) -> Self::Domain {
81 <TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs> as Pcs<Challenge, Challenger>>::natural_domain_for_degree(
82 &self.inner, degree)
83 }
84
85 fn commit(
86 &self,
87 evaluations: impl IntoIterator<Item = (Self::Domain, RowMajorMatrix<Val>)>,
88 ) -> (Self::Commitment, Self::ProverData) {
89 let randomized_evaluations: Vec<(Self::Domain, RowMajorMatrix<Val>)> =
90 info_span!("randomize polys").in_scope(|| {
91 evaluations
92 .into_iter()
93 .map(|(domain, mat)| {
94 let mat_width = mat.width();
95 let mut random_evaluation = add_random_cols(
100 &mat,
101 mat_width + 2 * self.num_random_codewords,
102 &mut *self.rng.borrow_mut(),
103 );
104 random_evaluation.width = mat_width + self.num_random_codewords;
105
106 (domain, random_evaluation)
107 })
108 .collect()
109 });
110
111 Pcs::<Challenge, Challenger>::commit(&self.inner, randomized_evaluations)
112 }
113
114 fn commit_quotient(
129 &self,
130 quotient_domain: Self::Domain,
131 quotient_evaluations: RowMajorMatrix<Val>,
132 num_chunks: usize,
133 ) -> (Self::Commitment, Self::ProverData) {
134 assert!(num_chunks > 1);
135
136 let evaluations = quotient_domain.split_evals(num_chunks, quotient_evaluations);
139 let domains = quotient_domain.split_domains(num_chunks);
140
141 let cis = get_zp_cis(&domains);
142 let last_chunk = num_chunks - 1;
143 let last_chunk_ci_inv = cis[last_chunk].inverse();
144 let mul_coeffs = (0..last_chunk)
145 .map(|i| cis[i] * last_chunk_ci_inv)
146 .collect_vec();
147
148 let mut rng = self.rng.borrow_mut();
149 let randomized_evaluations: Vec<RowMajorMatrix<Val>> = evaluations
150 .into_iter()
151 .map(|mat| add_random_cols(&mat, self.num_random_codewords, &mut *rng))
152 .collect();
153 let h = randomized_evaluations[0].height();
157 let w = randomized_evaluations[0].width();
158 let mut all_random_values = (0..(randomized_evaluations.len() - 1) * h * w)
159 .map(|_| rng.random())
160 .chain(core::iter::repeat_n(Val::ZERO, h * w))
161 .collect::<Vec<_>>();
162
163 for j in 0..last_chunk {
165 let mul_coeff = mul_coeffs[j];
166 for k in 0..h * w {
167 let t = all_random_values[j * h * w + k] * mul_coeff;
168 all_random_values[last_chunk * h * w + k] -= t;
169 }
170 }
171
172 let ldes: Vec<_> = domains
173 .into_iter()
174 .zip(randomized_evaluations)
175 .enumerate()
176 .map(|(i, (domain, evals))| {
177 assert_eq!(domain.size(), evals.height());
178 let shift = Val::GENERATOR / domain.shift();
179 let random_values = &all_random_values[i * h * w..(i + 1) * h * w];
180
181 let mut lde_evals = self
183 .inner
184 .dft
185 .coset_lde_batch(evals, self.inner.fri.log_blowup + 1, shift)
186 .to_row_major_matrix();
187
188 let mut vanishing_poly_coeffs =
192 Val::zero_vec((h * w) << (self.inner.fri.log_blowup + 1));
193 let p = shift.exp_u64(h as u64);
194 Val::GENERATOR
195 .powers()
196 .take(h)
197 .enumerate()
198 .for_each(|(i, p_i)| {
199 for j in 0..w {
200 let mul_coeff = p_i * random_values[i * w + j];
201 vanishing_poly_coeffs[i * w + j] -= mul_coeff;
202 vanishing_poly_coeffs[(h + i) * w + j] = p * mul_coeff;
203 }
204 });
205 let random_eval = self
206 .inner
207 .dft
208 .dft_batch(DenseMatrix::new(vanishing_poly_coeffs, w))
209 .to_row_major_matrix();
210
211 for i in 0..h * w * (1 << (self.inner.fri.log_blowup + 1)) {
213 lde_evals.values[i] += random_eval.values[i];
214 }
215
216 lde_evals.bit_reverse_rows().to_row_major_matrix()
217 })
218 .collect();
219
220 self.inner.mmcs.commit(ldes)
221 }
222
223 fn get_evaluations_on_domain<'a>(
224 &self,
225 prover_data: &'a Self::ProverData,
226 idx: usize,
227 domain: Self::Domain,
228 ) -> Self::EvaluationsOnDomain<'a> {
229 let inner_evals = <TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs> as Pcs<
230 Challenge,
231 Challenger,
232 >>::get_evaluations_on_domain(
233 &self.inner, prover_data, idx, domain
234 );
235 let inner_width = inner_evals.width();
236 HorizontallyTruncated::new(inner_evals, inner_width - self.num_random_codewords).unwrap()
239 }
240
241 fn open(
242 &self,
243 rounds: Vec<(
245 &Self::ProverData,
246 Vec<
248 Vec<Challenge>,
250 >,
251 )>,
252 challenger: &mut Challenger,
253 ) -> (OpenedValues<Challenge>, Self::Proof) {
254 let (mut inner_opened_values, inner_proof) = self.inner.open(rounds, challenger);
255
256 let opened_values_rand = inner_opened_values
259 .iter_mut()
260 .map(|opened_values_for_round| {
261 opened_values_for_round
262 .iter_mut()
263 .map(|opened_values_for_mat| {
264 opened_values_for_mat
265 .iter_mut()
266 .map(|opened_values_for_point| {
267 let split =
268 opened_values_for_point.len() - self.num_random_codewords;
269 opened_values_for_point.drain(split..).collect()
270 })
271 .collect()
272 })
273 .collect()
274 })
275 .collect();
276
277 (inner_opened_values, (opened_values_rand, inner_proof))
278 }
279
280 fn verify(
281 &self,
282 mut rounds: Vec<(
284 Self::Commitment,
285 Vec<(
287 Self::Domain,
289 Vec<(
291 Challenge,
293 Vec<Challenge>,
295 )>,
296 )>,
297 )>,
298 proof: &Self::Proof,
299 challenger: &mut Challenger,
300 ) -> Result<(), Self::Error> {
301 let (opened_values_for_rand_cws, inner_proof) = proof;
302 for (round, rand_round) in zip_eq(
306 rounds.iter_mut(),
307 opened_values_for_rand_cws,
308 FriError::InvalidProofShape,
309 )? {
310 for (mat, rand_mat) in
311 zip_eq(round.1.iter_mut(), rand_round, FriError::InvalidProofShape)?
312 {
313 for (point, rand_point) in
314 zip_eq(mat.1.iter_mut(), rand_mat, FriError::InvalidProofShape)?
315 {
316 point.1.extend(rand_point);
317 }
318 }
319 }
320 self.inner.verify(rounds, inner_proof, challenger)
321 }
322
323 fn get_opt_randomization_poly_commitment(
324 &self,
325 ext_trace_domain: Self::Domain,
326 ) -> Option<(Self::Commitment, Self::ProverData)> {
327 let random_vals = DenseMatrix::rand(
328 &mut *self.rng.borrow_mut(),
329 ext_trace_domain.size(),
330 self.num_random_codewords + Challenge::DIMENSION,
331 );
332 let extended_domain = <Self as Pcs<Challenge, Challenger>>::natural_domain_for_degree(
333 self,
334 ext_trace_domain.size(),
335 );
336 let r_commit_and_data =
337 Pcs::<Challenge, Challenger>::commit(&self.inner, [(extended_domain, random_vals)]);
338 Some(r_commit_and_data)
339 }
340}
341
342#[instrument(level = "debug", skip_all)]
343fn add_random_cols<Val, R>(
344 mat: &RowMajorMatrix<Val>,
345 num_random_codewords: usize,
346 mut rng: R,
347) -> RowMajorMatrix<Val>
348where
349 Val: Field,
350 R: Rng + Send + Sync,
351 StandardUniform: Distribution<Val>,
352{
353 let old_w = mat.width();
354 let new_w = old_w + num_random_codewords;
355 let h = mat.height();
356
357 let new_values = Val::zero_vec(new_w * h);
358 let mut result = RowMajorMatrix::new(new_values, new_w);
359 result
362 .rows_mut()
363 .zip(mat.row_slices())
364 .for_each(|(new_row, old_row)| {
365 new_row[..old_w].copy_from_slice(old_row);
366 new_row[old_w..].iter_mut().for_each(|v| *v = rng.random());
367 });
368 result
369}
370
371fn get_zp_cis<D: PolynomialSpace>(qc_domains: &[D]) -> Vec<p3_commit::Val<D>> {
374 batch_multiplicative_inverse(
375 &qc_domains
376 .iter()
377 .enumerate()
378 .map(|(i, domain)| {
379 qc_domains
380 .iter()
381 .enumerate()
382 .filter(|(j, _)| *j != i)
383 .map(|(_, other_domain)| {
384 other_domain.vanishing_poly_at_point(domain.first_point())
385 })
386 .product()
387 })
388 .collect::<Vec<_>>(),
389 )
390}