1use crate::{
2 linear_codes::{
3 data_structures::*,
4 utils::{calculate_t, get_indices_from_sponge},
5 },
6 to_bytes,
7 utils::{inner_product, Matrix},
8 Error, LabeledCommitment, LabeledPolynomial, PCCommitterKey, PCUniversalParams, PCVerifierKey,
9 PolynomialCommitment,
10};
11use ark_crypto_primitives::{
12 crh::{CRHScheme, TwoToOneCRHScheme},
13 merkle_tree::{Config, MerkleTree},
14 sponge::{Absorb, CryptographicSponge},
15};
16use ark_ff::PrimeField;
17use ark_poly::Polynomial;
18use ark_std::{borrow::Borrow, marker::PhantomData, rand::RngCore};
19#[cfg(not(feature = "std"))]
20use ark_std::{string::ToString, vec::Vec};
21
22#[cfg(feature = "parallel")]
23use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
24
25mod data_structures;
26mod utils;
27
28mod brakedown;
29
30mod ligero;
31
32mod multilinear_brakedown;
33
34mod multilinear_ligero;
35mod univariate_ligero;
36
37pub use data_structures::{BrakedownPCParams, LigeroPCParams, LinCodePCProof};
38pub use multilinear_brakedown::MultilinearBrakedown;
39pub use multilinear_ligero::MultilinearLigero;
40pub use univariate_ligero::UnivariateLigero;
41
42const FIELD_SIZE_ERROR: &str = "This field is not suitable for the proposed parameters";
43
44pub trait LinCodeParametersInfo<C, H>
48where
49 C: Config,
50 H: CRHScheme,
51{
52 fn sec_param(&self) -> usize;
54
55 fn distance(&self) -> (usize, usize);
57
58 fn check_well_formedness(&self) -> bool;
60
61 fn compute_dimensions(&self, n: usize) -> (usize, usize);
63
64 fn leaf_hash_param(&self) -> &<<C as Config>::LeafHash as CRHScheme>::Parameters;
66
67 fn two_to_one_hash_param(
69 &self,
70 ) -> &<<C as Config>::TwoToOneHash as TwoToOneCRHScheme>::Parameters;
71
72 fn col_hash_params(&self) -> &H::Parameters;
75}
76
77pub trait LinearEncode<F, C, P, H>
79where
80 F: PrimeField,
81 C: Config,
82 H: CRHScheme,
83 P: Polynomial<F>,
84{
85 type LinCodePCParams: PCUniversalParams
88 + PCCommitterKey
89 + PCVerifierKey
90 + LinCodeParametersInfo<C, H>
91 + Sync;
92
93 fn setup<R: RngCore>(
95 max_degree: usize,
96 num_vars: Option<usize>,
97 rng: &mut R,
98 leaf_hash_param: <<C as Config>::LeafHash as CRHScheme>::Parameters,
99 two_to_one_hash_param: <<C as Config>::TwoToOneHash as TwoToOneCRHScheme>::Parameters,
100 col_hash_params: H::Parameters,
101 ) -> Self::LinCodePCParams;
102
103 fn encode(msg: &[F], param: &Self::LinCodePCParams) -> Result<Vec<F>, Error>;
106
107 fn poly_to_vec(polynomial: &P) -> Vec<F>;
111
112 fn point_to_vec(point: P::Point) -> Vec<F>;
114
115 fn compute_matrices(polynomial: &P, param: &Self::LinCodePCParams) -> (Matrix<F>, Matrix<F>) {
119 let mut coeffs = Self::poly_to_vec(polynomial);
120
121 let (n_rows, n_cols) = param.compute_dimensions(coeffs.len());
123
124 coeffs.resize(n_rows * n_cols, F::zero());
126
127 let mat = Matrix::new_from_flat(n_rows, n_cols, &coeffs);
128
129 let rows = mat.rows();
131 let ext_mat = Matrix::new_from_rows(
132 cfg_iter!(rows)
133 .map(|r| Self::encode(r, param).unwrap())
134 .collect(),
135 );
136
137 (mat, ext_mat)
138 }
139
140 fn tensor(z: &P::Point, n: usize, m: usize) -> (Vec<F>, Vec<F>);
147}
148
149pub struct LinearCodePCS<L, F, P, C, H>
151where
152 F: PrimeField,
153 C: Config,
154 P: Polynomial<F>,
155 H: CRHScheme,
156 L: LinearEncode<F, C, P, H>,
157{
158 _phantom: PhantomData<(L, F, P, C, H)>,
159}
160
161impl<L, F, P, C, H> PolynomialCommitment<F, P> for LinearCodePCS<L, F, P, C, H>
162where
163 L: LinearEncode<F, C, P, H>,
164 F: PrimeField + Absorb,
165 P: Polynomial<F>,
166 C: Config + 'static,
167 Vec<F>: Borrow<<H as CRHScheme>::Input>,
168 H::Output: Into<C::Leaf> + Send,
169 C::Leaf: Sized + Clone + Default + Send + AsRef<C::Leaf>,
170 H: CRHScheme + 'static,
171{
172 type UniversalParams = L::LinCodePCParams;
173
174 type CommitterKey = L::LinCodePCParams;
175
176 type VerifierKey = L::LinCodePCParams;
177
178 type Commitment = LinCodePCCommitment<C>;
179
180 type CommitmentState = LinCodePCCommitmentState<F, H>;
181
182 type Proof = LPCPArray<F, C>;
183
184 type BatchProof = Vec<Self::Proof>;
185
186 type Error = Error;
187
188 fn setup<R: RngCore>(
192 max_degree: usize,
193 num_vars: Option<usize>,
194 rng: &mut R,
195 ) -> Result<Self::UniversalParams, Self::Error> {
196 let leaf_hash_param = <C::LeafHash as CRHScheme>::setup(rng).unwrap();
197 let two_to_one_hash_param = <C::TwoToOneHash as TwoToOneCRHScheme>::setup(rng)
198 .unwrap()
199 .clone();
200 let col_hash_params = <H as CRHScheme>::setup(rng).unwrap();
201 let pp = L::setup::<R>(
202 max_degree,
203 num_vars,
204 rng,
205 leaf_hash_param,
206 two_to_one_hash_param,
207 col_hash_params,
208 );
209 let real_max_degree = <Self::UniversalParams as PCUniversalParams>::max_degree(&pp);
210 if max_degree > real_max_degree || real_max_degree == 0 {
211 return Err(Error::InvalidParameters(FIELD_SIZE_ERROR.to_string()));
212 }
213 Ok(pp)
214 }
215
216 fn trim(
217 pp: &Self::UniversalParams,
218 _supported_degree: usize,
219 _supported_hiding_bound: usize,
220 _enforced_degree_bounds: Option<&[usize]>,
221 ) -> Result<(Self::CommitterKey, Self::VerifierKey), Self::Error> {
222 if <Self::UniversalParams as PCUniversalParams>::max_degree(pp) == 0 {
223 return Err(Error::InvalidParameters(FIELD_SIZE_ERROR.to_string()));
224 }
225 Ok((pp.clone(), pp.clone()))
226 }
227
228 fn commit<'a>(
229 ck: &Self::CommitterKey,
230 polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<F, P>>,
231 _rng: Option<&mut dyn RngCore>,
232 ) -> Result<
233 (
234 Vec<LabeledCommitment<Self::Commitment>>,
235 Vec<Self::CommitmentState>,
236 ),
237 Self::Error,
238 >
239 where
240 P: 'a,
241 {
242 let mut commitments = Vec::new();
243 let mut states = Vec::new();
244
245 for labeled_polynomial in polynomials {
246 let polynomial = labeled_polynomial.polynomial();
247
248 let (mat, ext_mat) = L::compute_matrices(polynomial, ck);
251 let n_rows = mat.n;
252 let n_cols = mat.m;
253 let n_ext_cols = ext_mat.m;
254
255 let ext_mat_cols = ext_mat.cols();
257 let leaves: Vec<H::Output> = cfg_into_iter!(ext_mat_cols)
258 .map(|col| {
259 H::evaluate(ck.col_hash_params(), col)
260 .map_err(|_| Error::HashingError)
261 .unwrap()
262 })
263 .collect();
264 let state = Self::CommitmentState {
265 mat,
266 ext_mat,
267 leaves,
268 };
269 let mut leaves: Vec<C::Leaf> =
270 state.leaves.clone().into_iter().map(|h| h.into()).collect(); let col_tree = create_merkle_tree::<C>(
272 &mut leaves,
273 ck.leaf_hash_param(),
274 ck.two_to_one_hash_param(),
275 )?;
276
277 let root = col_tree.root();
279
280 let commitment = LinCodePCCommitment {
282 metadata: Metadata {
283 n_rows,
284 n_cols,
285 n_ext_cols,
286 },
287 root,
288 };
289
290 commitments.push(LabeledCommitment::new(
291 labeled_polynomial.label().clone(),
292 commitment,
293 None,
294 ));
295 states.push(state);
296 }
297 Ok((commitments, states))
298 }
299
300 fn open<'a>(
301 ck: &Self::CommitterKey,
302 _labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<F, P>>,
303 commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
304 point: &'a P::Point,
305 sponge: &mut impl CryptographicSponge,
306 states: impl IntoIterator<Item = &'a Self::CommitmentState>,
307 _rng: Option<&mut dyn RngCore>,
308 ) -> Result<Self::Proof, Self::Error>
309 where
310 P: 'a,
311 Self::CommitmentState: 'a,
312 Self::Commitment: 'a,
313 {
314 let mut proof_array = LPCPArray::default();
315
316 for (labeled_commitment, state) in commitments.into_iter().zip(states) {
317 let commitment = labeled_commitment.commitment();
318 let n_rows = commitment.metadata.n_rows;
319 let n_cols = commitment.metadata.n_cols;
320
321 let Self::CommitmentState {
325 mat,
326 ext_mat,
327 leaves: col_hashes,
328 } = state;
329 let mut col_hashes: Vec<C::Leaf> =
330 col_hashes.clone().into_iter().map(|h| h.into()).collect(); let col_tree = create_merkle_tree::<C>(
333 &mut col_hashes,
334 ck.leaf_hash_param(),
335 ck.two_to_one_hash_param(),
336 )?;
337
338 let (_, b) = L::tensor(point, n_cols, n_rows);
340
341 sponge.absorb(&to_bytes!(&commitment.root).map_err(|_| Error::TranscriptError)?);
342
343 let well_formedness = if ck.check_well_formedness() {
345 let r = sponge.squeeze_field_elements::<F>(n_rows);
346 let v = mat.row_mul(&r);
347
348 sponge.absorb(&v);
349 Some(v)
350 } else {
351 None
352 };
353
354 let point_vec = L::point_to_vec(point.clone());
355 sponge.absorb(&point_vec);
356
357 proof_array.push(LinCodePCProof {
358 opening: generate_proof(
360 ck.sec_param(),
361 ck.distance(),
362 &b,
363 &mat,
364 &ext_mat,
365 &col_tree,
366 sponge,
367 )?,
368 well_formedness,
369 });
370 }
371
372 Ok(proof_array)
373 }
374
375 fn check<'a>(
376 vk: &Self::VerifierKey,
377 commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
378 point: &'a P::Point,
379 values: impl IntoIterator<Item = F>,
380 proof_array: &Self::Proof,
381 sponge: &mut impl CryptographicSponge,
382 _rng: Option<&mut dyn RngCore>,
383 ) -> Result<bool, Self::Error>
384 where
385 Self::Commitment: 'a,
386 {
387 let leaf_hash_param: &<<C as Config>::LeafHash as CRHScheme>::Parameters =
388 vk.leaf_hash_param();
389 let two_to_one_hash_param: &<<C as Config>::TwoToOneHash as TwoToOneCRHScheme>::Parameters =
390 vk.two_to_one_hash_param();
391
392 for (i, (labeled_commitment, value)) in commitments.into_iter().zip(values).enumerate() {
393 let proof = &proof_array[i];
394 let commitment = labeled_commitment.commitment();
395 let n_rows = commitment.metadata.n_rows;
396 let n_cols = commitment.metadata.n_cols;
397 let n_ext_cols = commitment.metadata.n_ext_cols;
398 let root = &commitment.root;
399 let t = calculate_t::<F>(vk.sec_param(), vk.distance(), n_ext_cols)?;
400
401 sponge.absorb(&to_bytes!(&commitment.root).map_err(|_| Error::TranscriptError)?);
402
403 let out = if vk.check_well_formedness() {
404 if proof.well_formedness.is_none() {
405 return Err(Error::InvalidCommitment);
406 }
407 let tmp = &proof.well_formedness.as_ref();
408 let v = tmp.unwrap();
409 let r = sponge.squeeze_field_elements::<F>(n_rows);
410 sponge.absorb(&v);
412
413 (Some(v), Some(r))
414 } else {
415 (None, None)
416 };
417
418 let point_vec = L::point_to_vec(point.clone());
421 sponge.absorb(&point_vec);
422 sponge.absorb(&proof.opening.v);
423
424 let indices = get_indices_from_sponge(n_ext_cols, t, sponge)?;
426
427 let col_hashes: Vec<C::Leaf> = proof
429 .opening
430 .columns
431 .iter()
432 .map(|c| {
433 H::evaluate(vk.col_hash_params(), c.clone())
434 .map_err(|_| Error::HashingError)
435 .unwrap()
436 .into()
437 })
438 .collect();
439
440 for (j, (leaf, q_j)) in col_hashes.iter().zip(indices.iter()).enumerate() {
444 let path = &proof.opening.paths[j];
445 if path.leaf_index != *q_j {
446 return Err(Error::InvalidCommitment);
447 }
448
449 path.verify(leaf_hash_param, two_to_one_hash_param, root, leaf.clone())
450 .map_err(|_| Error::InvalidCommitment)?;
451 }
452
453 let check_inner_product = |a, b, c| -> Result<(), Error> {
455 if inner_product(a, b) != c {
456 return Err(Error::InvalidCommitment);
457 }
458
459 Ok(())
460 };
461
462 let w = L::encode(&proof.opening.v, vk)?;
464
465 let (a, b) = L::tensor(point, n_cols, n_rows);
467
468 if let (Some(well_formedness), Some(r)) = out {
472 let w_well_formedness = L::encode(well_formedness, vk)?;
473 for (transcript_index, matrix_index) in indices.iter().enumerate() {
474 check_inner_product(
475 &r,
476 &proof.opening.columns[transcript_index],
477 w_well_formedness[*matrix_index],
478 )?;
479 check_inner_product(
480 &b,
481 &proof.opening.columns[transcript_index],
482 w[*matrix_index],
483 )?;
484 }
485 } else {
486 for (transcript_index, matrix_index) in indices.iter().enumerate() {
487 check_inner_product(
488 &b,
489 &proof.opening.columns[transcript_index],
490 w[*matrix_index],
491 )?;
492 }
493 }
494
495 if inner_product(&proof.opening.v, &a) != value {
496 eprintln!("Function check: claimed value in position {i} does not match the evaluation of the committed polynomial in the same position");
497 return Ok(false);
498 }
499 }
500
501 Ok(true)
502 }
503}
504
505fn create_merkle_tree<C>(
507 leaves: &mut Vec<C::Leaf>,
508 leaf_hash_param: &<<C as Config>::LeafHash as CRHScheme>::Parameters,
509 two_to_one_hash_param: &<<C as Config>::TwoToOneHash as TwoToOneCRHScheme>::Parameters,
510) -> Result<MerkleTree<C>, Error>
511where
512 C: Config,
513 C::Leaf: Default + Clone + Send + AsRef<C::Leaf>,
514{
515 let next_pow_of_two = leaves.len().next_power_of_two();
517 leaves.resize(next_pow_of_two, <C::Leaf>::default());
518
519 MerkleTree::<C>::new(leaf_hash_param, two_to_one_hash_param, leaves)
520 .map_err(|_| Error::HashingError)
521}
522
523fn generate_proof<F, C, S>(
524 sec_param: usize,
525 distance: (usize, usize),
526 b: &[F],
527 mat: &Matrix<F>,
528 ext_mat: &Matrix<F>,
529 col_tree: &MerkleTree<C>,
530 sponge: &mut S,
531) -> Result<LinCodePCProofSingle<F, C>, Error>
532where
533 F: PrimeField + Absorb,
534 C: Config,
535 S: CryptographicSponge,
536{
537 let t = calculate_t::<F>(sec_param, distance, ext_mat.m)?;
538
539 let v = mat.row_mul(b);
541 sponge.absorb(&v);
542
543 let indices = get_indices_from_sponge(ext_mat.m, t, sponge)?;
545
546 let mut queried_columns = Vec::with_capacity(t);
548 let mut paths = Vec::with_capacity(t);
549
550 let ext_mat_cols = ext_mat.cols();
551
552 for i in indices {
553 queried_columns.push(ext_mat_cols[i].clone());
554 paths.push(
555 col_tree
556 .generate_proof(i)
557 .map_err(|_| Error::TranscriptError)?,
558 );
559 }
560
561 Ok(LinCodePCProofSingle {
562 paths,
563 v,
564 columns: queried_columns,
565 })
566}