1use crate::{
10 poly::GeneralDensePolynomial, prelude::Commitment, toeplitz::ToeplitzMatrix, PCSError,
11 PolynomialCommitmentScheme, StructuredReferenceString, UnivariatePCS,
12};
13use ark_ec::{
14 pairing::Pairing, scalar_mul::variable_base::VariableBaseMSM, AffineRepr, CurveGroup,
15};
16use ark_ff::{FftField, PrimeField};
17#[cfg(not(feature = "seq-fk-23"))]
18use ark_poly::EvaluationDomain;
19use ark_poly::{
20 univariate::DensePolynomial, DenseUVPolynomial, Polynomial, Radix2EvaluationDomain,
21};
22use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
23use ark_std::{
24 borrow::Borrow,
25 end_timer, format,
26 marker::PhantomData,
27 ops::Mul,
28 rand::{CryptoRng, RngCore},
29 start_timer,
30 string::ToString,
31 vec,
32 vec::Vec,
33 One, UniformRand, Zero,
34};
35use derive_where::derive_where;
36use jf_utils::par_utils::parallelizable_slice_iter;
37#[cfg(feature = "parallel")]
38use rayon::prelude::*;
39use srs::{UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam};
40
41pub(crate) mod srs;
42
43pub struct UnivariateKzgPCS<E> {
45 #[doc(hidden)]
46 phantom: PhantomData<E>,
47}
48
49#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq, Eq)]
50#[derive_where(Hash)]
51pub struct UnivariateKzgProof<E: Pairing> {
53 pub proof: E::G1Affine,
55}
56pub type UnivariateKzgBatchProof<E> = Vec<UnivariateKzgProof<E>>;
58
59impl<E: Pairing> PolynomialCommitmentScheme for UnivariateKzgPCS<E> {
60 type SRS = UnivariateUniversalParams<E>;
62 type Polynomial = DensePolynomial<E::ScalarField>;
64 type Point = E::ScalarField;
65 type Evaluation = E::ScalarField;
66 type Commitment = Commitment<E>;
68 type BatchCommitment = Vec<Self::Commitment>;
69 type Proof = UnivariateKzgProof<E>;
70 type BatchProof = UnivariateKzgBatchProof<E>;
71
72 fn trim(
76 srs: impl Borrow<Self::SRS>,
77 supported_degree: usize,
78 supported_num_vars: Option<usize>,
79 ) -> Result<(UnivariateProverParam<E>, UnivariateVerifierParam<E>), PCSError> {
80 if supported_num_vars.is_some() {
81 return Err(PCSError::InvalidParameters(
82 "univariate should not receive a num_var param".to_string(),
83 ));
84 }
85 srs.borrow().trim(supported_degree)
86 }
87
88 fn commit(
91 prover_param: impl Borrow<UnivariateProverParam<E>>,
92 poly: &Self::Polynomial,
93 ) -> Result<Self::Commitment, PCSError> {
94 let prover_param = prover_param.borrow();
95
96 #[cfg(feature = "kzg-print-trace")]
97 let commit_time =
98 start_timer!(|| format!("Committing to polynomial of degree {} ", poly.degree()));
99
100 if poly.degree() > prover_param.powers_of_g.len() {
101 return Err(PCSError::InvalidParameters(format!(
102 "poly degree {} is larger than allowed {}",
103 poly.degree(),
104 prover_param.powers_of_g.len()
105 )));
106 }
107
108 let (num_leading_zeros, plain_coeffs) = skip_leading_zeros_and_convert_to_bigints(poly);
109
110 #[cfg(feature = "kzg-print-trace")]
111 let msm_time = start_timer!(|| "MSM to compute commitment to plaintext
112 poly");
113
114 let commitment = E::G1::msm_bigint(
115 &prover_param.powers_of_g[num_leading_zeros..],
116 plain_coeffs.as_ref(),
117 )
118 .into_affine();
119
120 #[cfg(feature = "kzg-print-trace")]
121 end_timer!(msm_time);
122 #[cfg(feature = "kzg-print-trace")]
123 end_timer!(commit_time);
124 Ok(Commitment(commitment))
125 }
126
127 fn batch_commit(
129 prover_param: impl Borrow<UnivariateProverParam<E>>,
130 polys: &[Self::Polynomial],
131 ) -> Result<Self::BatchCommitment, PCSError> {
132 let prover_param = prover_param.borrow();
133 let commit_time = start_timer!(|| format!("batch commit {} polynomials", polys.len()));
134 let res = parallelizable_slice_iter(polys)
135 .map(|poly| Self::commit(prover_param, poly))
136 .collect::<Result<Vec<Self::Commitment>, PCSError>>()?;
137
138 end_timer!(commit_time);
139 Ok(res)
140 }
141
142 fn open(
145 prover_param: impl Borrow<UnivariateProverParam<E>>,
146 polynomial: &Self::Polynomial,
147 point: &Self::Point,
148 ) -> Result<(Self::Proof, Self::Evaluation), PCSError> {
149 #[cfg(feature = "kzg-print-trace")]
150 let open_time =
151 start_timer!(|| format!("Opening polynomial of degree {}", polynomial.degree()));
152
153 let divisor = Self::Polynomial::from_coefficients_vec(vec![-*point, E::ScalarField::one()]);
154
155 #[cfg(feature = "kzg-print-trace")]
156 let witness_time = start_timer!(|| "Computing witness polynomial");
157
158 let witness_polynomial = polynomial / &divisor;
159
160 #[cfg(feature = "kzg-print-trace")]
161 end_timer!(witness_time);
162
163 let (num_leading_zeros, witness_coeffs) =
164 skip_leading_zeros_and_convert_to_bigints(&witness_polynomial);
165
166 let proof: E::G1Affine = E::G1::msm_bigint(
167 &prover_param.borrow().powers_of_g[num_leading_zeros..],
168 &witness_coeffs,
169 )
170 .into_affine();
171
172 let eval = polynomial.evaluate(point);
175
176 #[cfg(feature = "kzg-print-trace")]
177 end_timer!(open_time);
178
179 Ok((Self::Proof { proof }, eval))
180 }
181
182 fn batch_open(
188 prover_param: impl Borrow<UnivariateProverParam<E>>,
189 _multi_commitment: &Self::BatchCommitment,
190 polynomials: &[Self::Polynomial],
191 points: &[Self::Point],
192 ) -> Result<(Self::BatchProof, Vec<Self::Evaluation>), PCSError> {
193 let open_time = start_timer!(|| format!("batch opening {} polynomials", polynomials.len()));
194 if polynomials.len() != points.len() {
195 return Err(PCSError::InvalidParameters(format!(
196 "poly length {} is different from points length {}",
197 polynomials.len(),
198 points.len()
199 )));
200 }
201 let mut batch_proof = vec![];
202 let mut evals = vec![];
203 for (poly, point) in polynomials.iter().zip(points.iter()) {
204 let (proof, eval) = Self::open(prover_param.borrow(), poly, point)?;
205 batch_proof.push(proof);
206 evals.push(eval);
207 }
208
209 end_timer!(open_time);
210 Ok((batch_proof, evals))
211 }
212 fn verify(
215 verifier_param: &UnivariateVerifierParam<E>,
216 commitment: &Self::Commitment,
217 point: &Self::Point,
218 value: &E::ScalarField,
219 proof: &Self::Proof,
220 ) -> Result<bool, PCSError> {
221 let check_time = start_timer!(|| "Checking evaluation");
222 let pairing_inputs_l: Vec<E::G1Prepared> = vec![
223 (verifier_param.g * value - proof.proof * point - commitment.0.into_group())
224 .into_affine()
225 .into(),
226 proof.proof.into(),
227 ];
228 let pairing_inputs_r: Vec<E::G2Prepared> =
229 vec![verifier_param.h.into(), verifier_param.beta_h.into()];
230
231 let res = E::multi_pairing(pairing_inputs_l, pairing_inputs_r)
232 .0
233 .is_one();
234
235 end_timer!(check_time, || format!("Result: {res}"));
236 Ok(res)
237 }
238
239 fn batch_verify<R: RngCore + CryptoRng>(
245 verifier_param: &UnivariateVerifierParam<E>,
246 multi_commitment: &Self::BatchCommitment,
247 points: &[Self::Point],
248 values: &[E::ScalarField],
249 batch_proof: &Self::BatchProof,
250 rng: &mut R,
251 ) -> Result<bool, PCSError> {
252 let check_time =
253 start_timer!(|| format!("Checking {} evaluation proofs", multi_commitment.len()));
254
255 let mut total_c = <E::G1>::zero();
256 let mut total_w = <E::G1>::zero();
257
258 let combination_time = start_timer!(|| "Combining commitments and proofs");
259 let mut randomizer = E::ScalarField::one();
260 let mut g_multiplier = E::ScalarField::zero();
263 for (((c, z), v), proof) in multi_commitment
264 .iter()
265 .zip(points)
266 .zip(values)
267 .zip(batch_proof)
268 {
269 let w = proof.proof;
270 let mut temp = w.mul(*z);
271 temp += &c.0;
272 let c = temp;
273 g_multiplier += &(randomizer * v);
274 total_c += c * randomizer;
275 total_w += w * randomizer;
276 randomizer = u128::rand(rng).into();
279 }
280 total_c -= &verifier_param.g.mul(g_multiplier);
281 end_timer!(combination_time);
282
283 let to_affine_time = start_timer!(|| "Converting results to affine for pairing");
284 let affine_points = E::G1::normalize_batch(&[-total_w, total_c]);
285 let (total_w, total_c) = (affine_points[0], affine_points[1]);
286 end_timer!(to_affine_time);
287
288 let pairing_time = start_timer!(|| "Performing product of pairings");
289 let result = E::multi_pairing(
290 [total_w, total_c],
291 [verifier_param.beta_h, verifier_param.h],
292 )
293 .0
294 .is_one();
295 end_timer!(pairing_time);
296 end_timer!(check_time, || format!("Result: {result}"));
297 Ok(result)
298 }
299
300 fn multi_open(
307 prover_param: impl Borrow<UnivariateProverParam<E>>,
308 polynomial: &Self::Polynomial,
309 points: &[Self::Point],
310 ) -> Result<(Vec<Self::Proof>, Vec<Self::Evaluation>), PCSError> {
311 let h_poly = Self::compute_h_poly_in_fk23(prover_param, &polynomial.coeffs)?;
312 let proofs: Vec<_> = h_poly
313 .batch_evaluate(points)
314 .into_iter()
315 .map(|g| UnivariateKzgProof {
316 proof: g.into_affine(),
317 })
318 .collect();
319
320 let evals =
322 GeneralDensePolynomial::from_coeff_slice(&polynomial.coeffs).batch_evaluate(points);
323 Ok((proofs, evals))
324 }
325}
326
327impl<E: Pairing> UnivariatePCS for UnivariateKzgPCS<E> {
328 fn multi_open_rou_proofs(
329 prover_param: impl Borrow<<Self::SRS as StructuredReferenceString>::ProverParam>,
330 polynomial: &Self::Polynomial,
331 num_points: usize,
332 domain: &Radix2EvaluationDomain<Self::Evaluation>,
333 ) -> Result<Vec<Self::Proof>, PCSError> {
334 #[cfg(not(feature = "seq-fk-23"))]
335 {
336 let h_poly_timer = start_timer!(|| "compute h_poly");
337 let h_poly = Self::compute_h_poly_parallel(prover_param, &polynomial.coeffs)?;
338 end_timer!(h_poly_timer);
339 let small_domain: Radix2EvaluationDomain<Self::Evaluation> =
340 Radix2EvaluationDomain::new(h_poly.degree() + 1).ok_or_else(|| {
341 PCSError::InvalidParameters(format!(
342 "failed to create a domain of size {}",
343 h_poly.degree() + 1,
344 ))
345 })?;
346 let parallel_factor = domain.size() / small_domain.size();
347
348 let mut offsets = Vec::with_capacity(parallel_factor);
349 offsets.push(Self::Evaluation::one());
350 for _ in 1..parallel_factor {
351 offsets.push(domain.group_gen() * offsets.last().unwrap());
352 }
353 let proofs_timer = start_timer!(|| format!(
354 "gen eval proofs with parallel_factor {} and num_points {}",
355 parallel_factor, num_points,
356 ));
357 let proofs: Vec<Vec<_>> = parallelizable_slice_iter(&offsets)
358 .map(|&offset| {
359 small_domain
360 .get_coset(offset)
361 .unwrap()
362 .fft(&h_poly.coeffs[..])
363 })
364 .collect();
365 end_timer!(proofs_timer);
366 let mut res = vec![];
367 for j in 0..small_domain.size() {
368 for proof in proofs.iter() {
369 res.push(UnivariateKzgProof {
370 proof: proof[j].into_affine(),
371 });
372 }
373 }
374 res = res.into_iter().take(num_points).collect();
375 Ok(res)
376 }
377
378 #[cfg(feature = "seq-fk-23")]
379 {
380 let h_poly_timer = start_timer!(|| "compute h_poly");
381 let mut h_poly = Self::compute_h_poly_in_fk23(prover_param, &polynomial.coeffs)?;
382 end_timer!(h_poly_timer);
383 let proofs_timer = start_timer!(|| "gen eval proofs");
384 let proofs: Vec<_> = h_poly
385 .batch_evaluate_rou(domain)?
386 .into_iter()
387 .take(num_points)
388 .map(|g| UnivariateKzgProof {
389 proof: g.into_affine(),
390 })
391 .collect();
392 end_timer!(proofs_timer);
393 Ok(proofs)
394 }
395 }
396
397 fn multi_open_rou_evals(
399 polynomial: &Self::Polynomial,
400 num_points: usize,
401 domain: &Radix2EvaluationDomain<Self::Evaluation>,
402 ) -> Result<Vec<Self::Evaluation>, PCSError> {
403 let evals = GeneralDensePolynomial::from_coeff_slice(&polynomial.coeffs)
404 .batch_evaluate_rou(domain)?
405 .into_iter()
406 .take(num_points)
407 .collect();
408 Ok(evals)
409 }
410
411 fn multi_point_open(
417 prover_param: impl Borrow<<Self::SRS as StructuredReferenceString>::ProverParam>,
418 polynomial: &Self::Polynomial,
419 points: &[Self::Point],
420 ) -> Result<(Self::Proof, Vec<Self::Evaluation>), PCSError> {
421 if points.is_empty() {
422 return Err(PCSError::InvalidParameters(
423 "no point to evaluate and open".to_string(),
424 ));
425 }
426 let open_time = start_timer!(|| format!(
427 "Opening polynomial of degree {} at {} points",
428 polynomial.degree(),
429 points.len()
430 ));
431
432 let evals_time = start_timer!(|| "Computing polynomial evaluations");
433 let evals: Vec<Self::Evaluation> = points
434 .iter()
435 .map(|point| polynomial.evaluate(point))
436 .collect();
437 end_timer!(evals_time);
438
439 let vanish_poly =
444 Self::Polynomial::from_coefficients_vec(vec![-points[0], E::ScalarField::one()]);
445 let divisor: Self::Polynomial = points.iter().skip(1).fold(vanish_poly, |acc, point| {
446 &acc * &Self::Polynomial::from_coefficients_vec(vec![-*point, E::ScalarField::one()])
447 });
448
449 let witness_time = start_timer!(|| "Computing witness polynomial");
454 let witness_polynomial = polynomial / &divisor;
455 end_timer!(witness_time);
456
457 let (num_leading_zeros, witness_coeffs) =
458 skip_leading_zeros_and_convert_to_bigints(&witness_polynomial);
459
460 let proof: E::G1Affine = E::G1::msm_bigint(
461 &prover_param.borrow().powers_of_g[num_leading_zeros..],
462 &witness_coeffs,
463 )
464 .into_affine();
465
466 end_timer!(open_time);
467 Ok((Self::Proof { proof }, evals))
468 }
469
470 fn multi_point_verify(
475 verifier_param: impl Borrow<<Self::SRS as StructuredReferenceString>::VerifierParam>,
476 commitment: &Self::Commitment,
477 points: &[Self::Point],
478 values: &[Self::Evaluation],
479 proof: &Self::Proof,
480 ) -> Result<bool, PCSError> {
481 if points.is_empty() {
482 return Err(PCSError::InvalidParameters(
483 "no evaluation to check".to_string(),
484 ));
485 }
486 if points.len() != values.len() {
487 return Err(PCSError::InvalidParameters(format!(
488 "the number of points {} is different from the number of evaluation values {}",
489 points.len(),
490 values.len(),
491 )));
492 }
493 if verifier_param.borrow().powers_of_h.len() < points.len() + 1 {
494 return Err(PCSError::InvalidParameters(format!(
495 "the number of powers of beta times h {} in SRS <= the number of evaluation points {}",
496 verifier_param.borrow().powers_of_h.len(),
497 points.len(),
498 )));
499 }
500
501 let check_time = start_timer!(|| "Checking evaluations");
502
503 let evals_poly = values
508 .iter()
509 .enumerate()
510 .fold(Self::Polynomial::zero(), |acc, (i, &value)| {
511 acc + lagrange_poly(points, i, value)
512 });
513
514 let (num_leading_zeros, evals_poly_coeffs) =
515 skip_leading_zeros_and_convert_to_bigints(&evals_poly);
516
517 let evals_cm: E::G1Affine = E::G1::msm_bigint(
518 &verifier_param.borrow().powers_of_g[num_leading_zeros..],
519 evals_poly_coeffs.as_ref(),
520 )
521 .into_affine();
522
523 let vanish_poly =
528 Self::Polynomial::from_coefficients_vec(vec![-points[0], E::ScalarField::one()]);
529 let vanish_poly: Self::Polynomial =
530 points.iter().skip(1).fold(vanish_poly, |acc, point| {
531 &acc * &Self::Polynomial::from_coefficients_vec(vec![
532 -*point,
533 E::ScalarField::one(),
534 ])
535 });
536
537 let (num_leading_zeros, vanish_poly_coeffs) =
538 skip_leading_zeros_and_convert_to_bigints(&vanish_poly);
539
540 let vanish_cm: E::G2Affine = E::G2::msm_bigint(
541 &verifier_param.borrow().powers_of_h[num_leading_zeros..],
542 vanish_poly_coeffs.as_ref(),
543 )
544 .into_affine();
545
546 let pairing_inputs_l: Vec<E::G1Prepared> = vec![
548 (evals_cm.into_group() - commitment.0.into_group())
549 .into_affine()
550 .into(),
551 proof.proof.into(),
552 ];
553 let pairing_inputs_r: Vec<E::G2Prepared> =
554 vec![verifier_param.borrow().h.into(), vanish_cm.into()];
555
556 let res = E::multi_pairing(pairing_inputs_l, pairing_inputs_r)
557 .0
558 .is_one();
559
560 end_timer!(check_time, || format!("Result: {res}"));
561 Ok(res)
562 }
563}
564
565impl<E, F> UnivariateKzgPCS<E>
566where
567 E: Pairing<ScalarField = F>,
568 F: FftField,
569{
570 #[cfg(not(feature = "seq-fk-23"))]
572 fn compute_h_poly_parallel(
573 prover_param: impl Borrow<UnivariateProverParam<E>>,
574 poly_coeffs: &[E::ScalarField],
575 ) -> Result<GeneralDensePolynomial<E::G1, F>, PCSError> {
576 if poly_coeffs.is_empty() {
577 return Ok(GeneralDensePolynomial::from_coeff_vec(vec![]));
578 }
579 let h_poly_deg = poly_coeffs.len() - 1;
580 let srs_vec: Vec<E::G1Affine> = prover_param
581 .borrow()
582 .powers_of_g
583 .iter()
584 .take(h_poly_deg)
585 .rev()
586 .cloned()
587 .collect();
588
589 let matrix: Vec<Vec<E::ScalarField>> = (0..h_poly_deg)
590 .map(|i| {
591 poly_coeffs
592 .iter()
593 .rev()
594 .take(h_poly_deg - i)
595 .copied()
596 .collect()
597 })
598 .collect();
599 let h_vec: Vec<E::G1> = parallelizable_slice_iter(&matrix)
600 .map(|coeffs| E::G1::msm(&srs_vec[h_poly_deg - coeffs.len()..], &coeffs[..]).unwrap())
601 .collect();
602 Ok(GeneralDensePolynomial::from_coeff_vec(h_vec))
603 }
604
605 fn compute_h_poly_in_fk23(
607 prover_param: impl Borrow<UnivariateProverParam<E>>,
608 poly_coeffs: &[E::ScalarField],
609 ) -> Result<GeneralDensePolynomial<E::G1, F>, PCSError> {
610 let mut padded_coeffs: Vec<F> = poly_coeffs.to_vec();
612 let padded_degree = padded_coeffs
613 .len()
614 .saturating_sub(1)
615 .checked_next_power_of_two()
616 .ok_or_else(|| {
617 PCSError::InvalidParameters(ark_std::format!(
618 "Next power of two overflows! Got: {}",
619 padded_coeffs.len().saturating_sub(1)
620 ))
621 })?;
622 let padded_len = padded_degree + 1;
623 padded_coeffs.resize(padded_len, F::zero());
624
625 let mut toep_col = vec![*padded_coeffs
628 .last()
629 .ok_or_else(|| PCSError::InvalidParameters("poly degree should >= 1".to_string()))?];
630 toep_col.resize(padded_degree, <<E as Pairing>::ScalarField as Zero>::zero());
631 let toep_row = padded_coeffs.iter().skip(1).rev().cloned().collect();
632 let poly_coeff_matrix = ToeplitzMatrix::new(toep_col, toep_row)?;
633
634 let srs_vec: Vec<E::G1> = prover_param
636 .borrow()
637 .powers_of_g
638 .iter()
639 .take(padded_degree)
640 .rev()
641 .cloned()
642 .map(|g| g.into_group())
643 .collect();
644
645 let h_vec = poly_coeff_matrix.fast_vec_mul(&srs_vec)?;
647
648 Ok(GeneralDensePolynomial::from_coeff_vec(h_vec))
649 }
650}
651
652#[cfg(feature = "icicle")]
653pub(crate) mod icicle {
654 use super::*;
655 use crate::icicle_deps::{curves::*, *};
656 use itertools::Itertools;
657
658 pub trait GPUCommittable<E: Pairing> {
660 type IC: IcicleCurve + MSM<Self::IC>;
662
663 fn gpu_commit(
665 prover_param: impl Borrow<UnivariateProverParam<E>>,
666 poly: &DensePolynomial<E::ScalarField>,
667 ) -> Result<Commitment<E>, PCSError> {
668 let stream = warmup_new_stream().unwrap();
669
670 #[cfg(feature = "kzg-print-trace")]
671 let commit_time =
672 start_timer!(|| format!("Committing to polynomial of degree {} ", poly.degree()));
673
674 let mut srs_on_gpu = Self::load_prover_param_to_gpu(prover_param, poly.degree())?;
675 let comm = Self::gpu_commit_with_loaded_prover_param(&mut srs_on_gpu, poly, &stream)?;
676 #[cfg(feature = "kzg-print-trace")]
677 end_timer!(commit_time);
678
679 Ok(comm)
680 }
681
682 fn gpu_commit_with_loaded_prover_param(
689 prover_param_on_gpu: &mut HostOrDeviceSlice<'_, IcicleAffine<Self::IC>>,
690 poly: &DensePolynomial<E::ScalarField>,
691 stream: &CudaStream,
692 ) -> Result<Commitment<E>, PCSError> {
693 let poly_on_gpu = Self::load_poly_to_gpu(poly)?;
694 let msm_result_on_gpu =
695 Self::commit_on_gpu(prover_param_on_gpu, &poly_on_gpu, 1, stream)?;
696 let comm = Self::load_commitments_to_host(msm_result_on_gpu, stream)?[0];
697
698 Ok(comm)
699 }
700
701 fn gpu_batch_commit(
703 prover_param: impl Borrow<UnivariateProverParam<E>>,
704 polys: &[DensePolynomial<E::ScalarField>],
705 ) -> Result<Vec<Commitment<E>>, PCSError> {
706 if polys.len() == 0 {
707 return Ok(vec![]);
708 }
709
710 let stream = warmup_new_stream().unwrap();
711
712 let degree = polys.iter().map(|poly| poly.degree()).max().unwrap_or(0);
713
714 #[cfg(feature = "kzg-print-trace")]
715 let commit_time = start_timer!(|| format!(
716 "Committing to {} polys of degree {} ",
717 polys.len(),
718 degree,
719 ));
720 let mut srs_on_gpu = Self::load_prover_param_to_gpu(prover_param, degree)?;
721 let comms =
722 Self::gpu_batch_commit_with_loaded_prover_param(&mut srs_on_gpu, polys, &stream)?;
723 #[cfg(feature = "kzg-print-trace")]
724 end_timer!(commit_time);
725
726 Ok(comms)
727 }
728
729 fn gpu_batch_commit_with_loaded_prover_param(
732 prover_param_on_gpu: &mut HostOrDeviceSlice<'_, IcicleAffine<Self::IC>>,
733 polys: &[DensePolynomial<E::ScalarField>],
734 stream: &CudaStream,
735 ) -> Result<Vec<Commitment<E>>, PCSError> {
736 if polys.len() == 0 {
737 return Ok(vec![]);
738 }
739
740 let poly_on_gpu = Self::load_batch_poly_to_gpu(polys)?;
741 let msm_result_on_gpu =
742 Self::commit_on_gpu(prover_param_on_gpu, &poly_on_gpu, polys.len(), stream)?;
743 let comms = Self::load_commitments_to_host(msm_result_on_gpu, stream)?;
744
745 Ok(comms)
746 }
747
748 fn ark_field_to_icicle(f: E::ScalarField) -> <Self::IC as IcicleCurve>::ScalarField;
754
755 fn ark_affine_to_icicle(p: E::G1Affine) -> IcicleAffine<Self::IC>;
761
762 fn icicle_projective_to_ark(p: IcicleProjective<Self::IC>) -> E::G1Affine;
764
765 fn load_prover_param_to_gpu<'srs>(
768 prover_param: impl Borrow<UnivariateProverParam<E>>,
769 supported_degree: usize,
770 ) -> Result<HostOrDeviceSlice<'srs, IcicleAffine<Self::IC>>, PCSError> {
771 let prover_param = prover_param.borrow();
772 if supported_degree > prover_param.powers_of_g.len() - 1 {
773 return Err(PCSError::InvalidParameters(format!(
774 "supported degree {} is larger than allowed {}",
775 supported_degree,
776 prover_param.powers_of_g.len() - 1
777 )));
778 }
779
780 let mut bases_on_device =
781 HostOrDeviceSlice::<'_, IcicleAffine<Self::IC>>::cuda_malloc(supported_degree + 1)?;
782
783 #[cfg(feature = "kzg-print-trace")]
784 let conv_time = start_timer!(|| "Type Conversion: ark->ICICLE: Group");
785 let bases: Vec<IcicleAffine<Self::IC>> = prover_param.powers_of_g
786 [..supported_degree + 1]
787 .par_iter()
788 .map(|&p| Self::ark_affine_to_icicle(p))
789 .collect();
790 #[cfg(feature = "kzg-print-trace")]
791 end_timer!(conv_time);
792
793 #[cfg(feature = "kzg-print-trace")]
794 let load_time = start_timer!(|| "Load group elements: CPU->GPU");
795 bases_on_device.copy_from_host(&bases)?;
796 #[cfg(feature = "kzg-print-trace")]
797 end_timer!(load_time);
798
799 Ok(bases_on_device)
800 }
801
802 fn load_poly_to_gpu<'poly>(
805 poly: &DensePolynomial<E::ScalarField>,
806 ) -> Result<HostOrDeviceSlice<'poly, <Self::IC as IcicleCurve>::ScalarField>, PCSError>
807 {
808 let size = poly.degree() + 1;
809 let mut scalars_on_device =
810 HostOrDeviceSlice::<'_, <Self::IC as IcicleCurve>::ScalarField>::cuda_malloc(size)?;
811
812 #[cfg(feature = "kzg-print-trace")]
813 let conv_time = start_timer!(|| "Type Conversion: ark->ICICLE: Scalar");
814 let scalars = unsafe {
816 poly.coeffs()[..size]
817 .align_to::<<Self::IC as IcicleCurve>::ScalarField>()
818 .1
819 };
820 #[cfg(feature = "kzg-print-trace")]
821 end_timer!(conv_time);
822
823 #[cfg(feature = "kzg-print-trace")]
824 let load_time = start_timer!(|| "Load scalars: CPU->GPU");
825 scalars_on_device.copy_from_host(scalars)?;
826 #[cfg(feature = "kzg-print-trace")]
827 end_timer!(load_time);
828
829 Ok(scalars_on_device)
830 }
831
832 fn load_batch_poly_to_gpu<'poly>(
835 polys: &[DensePolynomial<E::ScalarField>],
836 ) -> Result<HostOrDeviceSlice<'poly, <Self::IC as IcicleCurve>::ScalarField>, PCSError>
837 {
838 if polys.is_empty() {
839 return Err(PCSError::InvalidParameters(
840 "number of polys must be positive".to_string(),
841 ));
842 }
843
844 let num_coeffs = polys
845 .iter()
846 .map(|poly| poly.degree() + 1)
847 .max()
848 .unwrap_or(1);
849
850 let mut scalars_on_device = HostOrDeviceSlice::<
851 '_,
852 <Self::IC as IcicleCurve>::ScalarField,
853 >::cuda_malloc(num_coeffs * polys.len())?;
854
855 #[cfg(feature = "kzg-print-trace")]
856 let conv_time = start_timer!(|| "Type Conversion: ark->ICICLE: Scalar");
857 let zero_for_padding = E::ScalarField::zero();
858 let scalars: Vec<<Self::IC as IcicleCurve>::ScalarField> = polys
859 .iter()
860 .flat_map(|poly| {
861 poly.coeffs()
862 .iter()
863 .pad_using(num_coeffs, |_| &zero_for_padding)
864 })
865 .collect::<Vec<_>>()
866 .into_par_iter()
867 .map(|&s| Self::ark_field_to_icicle(s))
868 .collect();
869 #[cfg(feature = "kzg-print-trace")]
870 end_timer!(conv_time);
871
872 #[cfg(feature = "kzg-print-trace")]
873 let load_time = start_timer!(|| "Load scalars: CPU->GPU");
874 scalars_on_device.copy_from_host(&scalars)?;
875 #[cfg(feature = "kzg-print-trace")]
876 end_timer!(load_time);
877
878 Ok(scalars_on_device)
879 }
880
881 fn commit_on_gpu<'comm>(
901 prover_param: &mut HostOrDeviceSlice<'_, IcicleAffine<Self::IC>>,
902 poly: &HostOrDeviceSlice<'_, <Self::IC as IcicleCurve>::ScalarField>,
903 batch_size: usize,
904 stream: &CudaStream,
905 ) -> Result<HostOrDeviceSlice<'comm, IcicleProjective<Self::IC>>, PCSError> {
906 let trimmed_prover_param = match prover_param {
907 HostOrDeviceSlice::Device(ck, device_id) => {
908 HostOrDeviceSlice::Device(&mut ck[..poly.len()], *device_id)
909 },
910 HostOrDeviceSlice::Host(ck) => HostOrDeviceSlice::Host(ck[..poly.len()].to_vec()),
911 };
912
913 let mut msm_result =
914 HostOrDeviceSlice::<'_, IcicleProjective<Self::IC>>::cuda_malloc(batch_size)?;
915
916 let mut cfg = MSMConfig::default();
917 cfg.ctx.stream = &stream;
918 cfg.is_async = true; #[cfg(feature = "kzg-print-trace")]
921 let msm_time = start_timer!(|| "GPU-accelerated MSM dispatched");
922
923 icicle_core::msm::msm(poly, &trimmed_prover_param, &cfg, &mut msm_result)?;
924
925 #[cfg(feature = "kzg-print-trace")]
926 end_timer!(msm_time);
927
928 ark_std::mem::forget(trimmed_prover_param);
929 Ok(msm_result)
930 }
931
932 fn load_commitments_to_host(
935 commitments_on_gpu: HostOrDeviceSlice<'_, IcicleProjective<Self::IC>>,
936 stream: &CudaStream,
937 ) -> Result<Vec<Commitment<E>>, PCSError> {
938 #[cfg(feature = "kzg-print-trace")]
939 let sync_time = start_timer!(|| "Sync MSM result");
940 stream.synchronize()?;
943 #[cfg(feature = "kzg-print-trace")]
944 end_timer!(sync_time);
945
946 #[cfg(feature = "kzg-print-trace")]
947 let load_time = start_timer!(|| "Load MSM result GPU->CPU");
948 let mut msm_host_result =
949 vec![IcicleProjective::<Self::IC>::zero(); commitments_on_gpu.len()];
950 commitments_on_gpu.copy_to_host(&mut msm_host_result[..])?;
951 #[cfg(feature = "kzg-print-trace")]
952 end_timer!(load_time);
953
954 #[cfg(feature = "kzg-print-trace")]
955 let conv_time = start_timer!(|| "Type Conversion: ICICLE->ark: Group");
956 let comms = msm_host_result
957 .par_iter()
958 .map(|&p| Commitment(Self::icicle_projective_to_ark(p)))
959 .collect();
960 #[cfg(feature = "kzg-print-trace")]
961 end_timer!(conv_time);
962
963 Ok(comms)
964 }
965 }
966
967 impl GPUCommittable<Bn254> for UnivariateKzgPCS<Bn254> {
968 type IC = IcicleBn254;
969 fn ark_field_to_icicle(f: ark_bn254::Fr) -> icicle_bn254::curve::ScalarField {
971 icicle_bn254::curve::ScalarField::from(f.0 .0)
972 }
973
974 fn ark_affine_to_icicle(p: ark_bn254::G1Affine) -> icicle_bn254::curve::G1Affine {
976 icicle_bn254::curve::G1Affine {
977 x: icicle_bn254::curve::BaseField::from(p.x.0 .0),
978 y: icicle_bn254::curve::BaseField::from(p.y.0 .0),
979 }
980 }
981
982 fn icicle_projective_to_ark(p: icicle_bn254::curve::G1Projective) -> ark_bn254::G1Affine {
983 use ark_ff::biginteger::BigInteger256;
984
985 let ic_affine: icicle_bn254::curve::G1Affine = p.into();
986 let x_limbs: [u64; 4] = ic_affine.x.into();
987 let y_limbs: [u64; 4] = ic_affine.y.into();
988
989 if ic_affine == icicle_bn254::curve::G1Affine::zero() {
990 ark_bn254::G1Affine::zero()
991 } else {
992 ark_bn254::G1Affine {
993 x: BigInteger256::new(x_limbs).into(),
994 y: BigInteger256::new(y_limbs).into(),
995 infinity: false,
996 }
997 }
998 }
999
1000 fn commit_on_gpu<'comm>(
1002 prover_param: &mut HostOrDeviceSlice<'_, icicle_bn254::curve::G1Affine>,
1003 poly: &HostOrDeviceSlice<'_, icicle_bn254::curve::ScalarField>,
1004 batch_size: usize,
1005 stream: &CudaStream,
1006 ) -> Result<HostOrDeviceSlice<'comm, icicle_bn254::curve::G1Projective>, PCSError> {
1007 let trimmed_srs_size = poly.len() / batch_size;
1008 let trimmed_prover_param = match prover_param {
1009 HostOrDeviceSlice::Device(ck, device_id) => {
1010 HostOrDeviceSlice::Device(&mut ck[..trimmed_srs_size], *device_id)
1011 },
1012 HostOrDeviceSlice::Host(ck) => {
1013 HostOrDeviceSlice::Host(ck[..trimmed_srs_size].to_vec())
1014 },
1015 };
1016 let mut msm_result =
1017 HostOrDeviceSlice::<'_, icicle_bn254::curve::G1Projective>::cuda_malloc(
1018 batch_size,
1019 )?;
1020
1021 let mut cfg = MSMConfig::default();
1022 cfg.ctx.stream = stream;
1023 cfg.is_async = true;
1024 cfg.are_scalars_montgomery_form = true;
1025 cfg.are_points_montgomery_form = true;
1026
1027 #[cfg(feature = "kzg-print-trace")]
1028 let msm_time = start_timer!(|| "GPU-accelerated MSM");
1029 icicle_core::msm::msm(poly, &trimmed_prover_param, &cfg, &mut msm_result)?;
1030 #[cfg(feature = "kzg-print-trace")]
1031 end_timer!(msm_time);
1032
1033 ark_std::mem::forget(trimmed_prover_param);
1039 Ok(msm_result)
1040 }
1041 }
1042}
1043
1044fn skip_leading_zeros_and_convert_to_bigints<F: PrimeField, P: DenseUVPolynomial<F>>(
1045 p: &P,
1046) -> (usize, Vec<F::BigInt>) {
1047 let mut num_leading_zeros = 0;
1048 while num_leading_zeros < p.coeffs().len() && p.coeffs()[num_leading_zeros].is_zero() {
1049 num_leading_zeros += 1;
1050 }
1051 let coeffs = convert_to_bigints(&p.coeffs()[num_leading_zeros..]);
1052 (num_leading_zeros, coeffs)
1053}
1054
1055fn convert_to_bigints<F: PrimeField>(p: &[F]) -> Vec<F::BigInt> {
1056 #[cfg(feature = "kzg-print-trace")]
1057 let to_bigint_time = start_timer!(|| "Converting polynomial coeffs to
1058 bigints");
1059
1060 let coeffs = p.iter().map(|s| s.into_bigint()).collect::<Vec<_>>();
1061
1062 #[cfg(feature = "kzg-print-trace")]
1063 end_timer!(to_bigint_time);
1064
1065 coeffs
1066}
1067
1068fn lagrange_poly<F: PrimeField>(points: &[F], i: usize, value: F) -> DensePolynomial<F> {
1071 let mut res = DensePolynomial::from_coefficients_vec(vec![value]);
1072 let point_i = points[i];
1073 for (j, &point) in points.iter().enumerate() {
1074 if j != i {
1075 let z_inv = (point_i - point).inverse().unwrap();
1076 res = &res * &DensePolynomial::from_coefficients_vec(vec![-point * z_inv, z_inv]);
1077 }
1078 }
1079 res
1080}
1081
1082#[cfg(test)]
1083mod tests {
1084 pub use super::*;
1085 use ark_bls12_381::Bls12_381;
1086 use ark_std::rand::Rng;
1087 pub use jf_utils::test_rng;
1088
1089 fn end_to_end_test_template<E>() -> Result<(), PCSError>
1090 where
1091 E: Pairing,
1092 {
1093 let rng = &mut test_rng();
1094 for _ in 0..100 {
1095 let mut degree = 0;
1096 while degree <= 1 {
1097 degree = usize::rand(rng) % 20;
1098 }
1099 let pp = UnivariateKzgPCS::<E>::gen_srs_for_testing(rng, degree)?;
1100 let (ck, vk) = pp.trim(degree)?;
1101 let p = <DensePolynomial<E::ScalarField> as DenseUVPolynomial<E::ScalarField>>::rand(
1102 degree, rng,
1103 );
1104 let comm = UnivariateKzgPCS::<E>::commit(&ck, &p)?;
1105 let point = E::ScalarField::rand(rng);
1106 let (proof, value) = UnivariateKzgPCS::<E>::open(&ck, &p, &point)?;
1107 assert!(
1108 UnivariateKzgPCS::<E>::verify(&vk, &comm, &point, &value, &proof)?,
1109 "proof was incorrect for max_degree = {}, polynomial_degree = {}",
1110 degree,
1111 p.degree(),
1112 );
1113 }
1114 Ok(())
1115 }
1116
1117 fn linear_polynomial_test_template<E>() -> Result<(), PCSError>
1118 where
1119 E: Pairing,
1120 {
1121 let rng = &mut test_rng();
1122 for _ in 0..100 {
1123 let degree = 50;
1124
1125 let pp = UnivariateKzgPCS::<E>::gen_srs_for_testing(rng, degree)?;
1126 let (ck, vk) = pp.trim(degree)?;
1127 let p = <DensePolynomial<E::ScalarField> as DenseUVPolynomial<E::ScalarField>>::rand(
1128 degree, rng,
1129 );
1130 let comm = UnivariateKzgPCS::<E>::commit(&ck, &p)?;
1131 let point = E::ScalarField::rand(rng);
1132 let (proof, value) = UnivariateKzgPCS::<E>::open(&ck, &p, &point)?;
1133 assert!(
1134 UnivariateKzgPCS::<E>::verify(&vk, &comm, &point, &value, &proof)?,
1135 "proof was incorrect for max_degree = {}, polynomial_degree = {}",
1136 degree,
1137 p.degree(),
1138 );
1139 }
1140 Ok(())
1141 }
1142
1143 fn batch_check_test_template<E>() -> Result<(), PCSError>
1144 where
1145 E: Pairing,
1146 {
1147 let rng = &mut test_rng();
1148 for _ in 0..10 {
1149 let mut degree = 0;
1150 while degree <= 1 {
1151 degree = usize::rand(rng) % 20;
1152 }
1153 let pp = UnivariateKzgPCS::<E>::gen_srs_for_testing(rng, degree)?;
1154 let (ck, vk) = UnivariateKzgPCS::<E>::trim(&pp, degree, None)?;
1155 let mut comms = Vec::new();
1156 let mut values = Vec::new();
1157 let mut points = Vec::new();
1158 let mut proofs = Vec::new();
1159 for _ in 0..10 {
1160 let p =
1161 <DensePolynomial<E::ScalarField> as DenseUVPolynomial<E::ScalarField>>::rand(
1162 degree, rng,
1163 );
1164 let comm = UnivariateKzgPCS::<E>::commit(&ck, &p)?;
1165 let point = E::ScalarField::rand(rng);
1166 let (proof, value) = UnivariateKzgPCS::<E>::open(&ck, &p, &point)?;
1167
1168 assert!(UnivariateKzgPCS::<E>::verify(
1169 &vk, &comm, &point, &value, &proof
1170 )?);
1171 comms.push(comm);
1172 values.push(value);
1173 points.push(point);
1174 proofs.push(proof);
1175 }
1176 assert!(UnivariateKzgPCS::<E>::batch_verify(
1177 &vk, &comms, &points, &values, &proofs, rng
1178 )?);
1179 }
1180 Ok(())
1181 }
1182
1183 fn multi_point_open_test_template<E>() -> Result<(), PCSError>
1184 where
1185 E: Pairing,
1186 {
1187 let rng = &mut test_rng();
1188 let degree = 20;
1189 let verifier_degree = 10;
1190 let pp = UnivariateKzgPCS::<E>::gen_srs_for_testing_with_verifier_degree(
1191 rng,
1192 degree,
1193 verifier_degree,
1194 )?;
1195 let (ck, vk) = pp
1196 .borrow()
1197 .trim_with_verifier_degree(degree, verifier_degree)?;
1198 for _ in 0..10 {
1199 let p = <DensePolynomial<E::ScalarField> as DenseUVPolynomial<E::ScalarField>>::rand(
1200 degree, rng,
1201 );
1202 let comm = UnivariateKzgPCS::<E>::commit(&ck, &p)?;
1203 let points: Vec<E::ScalarField> = (0..5).map(|_| E::ScalarField::rand(rng)).collect();
1204 let (proof, values) = UnivariateKzgPCS::<E>::multi_point_open(&ck, &p, &points[..])?;
1205
1206 assert!(UnivariateKzgPCS::<E>::multi_point_verify(
1207 &vk,
1208 &comm,
1209 &points[..],
1210 &values[..],
1211 &proof
1212 )?);
1213 }
1214 Ok(())
1215 }
1216
1217 #[test]
1218 fn end_to_end_test() {
1219 end_to_end_test_template::<Bls12_381>().expect("test failed for bls12-381");
1220 }
1221
1222 #[test]
1223 fn linear_polynomial_test() {
1224 linear_polynomial_test_template::<Bls12_381>().expect("test failed for bls12-381");
1225 }
1226 #[test]
1227 fn batch_check_test() {
1228 batch_check_test_template::<Bls12_381>().expect("test failed for bls12-381");
1229 }
1230
1231 #[test]
1232 fn multi_point_open_test() {
1233 multi_point_open_test_template::<Bls12_381>().expect("test failed for bls12-381");
1234 }
1235
1236 #[test]
1237 fn test_multi_open() -> Result<(), PCSError> {
1238 type E = Bls12_381;
1239 type Fr = ark_bls12_381::Fr;
1240
1241 let mut rng = test_rng();
1242 let max_degree = 33;
1243 let pp = UnivariateKzgPCS::<E>::gen_srs_for_testing(&mut rng, max_degree)?;
1244 let degrees = [14, 15, 16, 17, 18];
1245
1246 for degree in degrees {
1247 let num_points = rng.gen_range(5..30); ark_std::println!(
1249 "Multi-opening: poly deg: {}, num of points: {}",
1250 degree,
1251 num_points
1252 );
1253
1254 let (ck, _) = UnivariateKzgPCS::<E>::trim_fft_size(&pp, degree)?;
1258 let poly = <DensePolynomial<Fr> as DenseUVPolynomial<Fr>>::rand(degree, &mut rng);
1259 let points: Vec<Fr> = (0..num_points).map(|_| Fr::rand(&mut rng)).collect();
1260
1261 let (proofs, evals) = UnivariateKzgPCS::<E>::multi_open(&ck, &poly, &points)?;
1263 assert!(
1264 proofs.len() == evals.len() && proofs.len() == num_points,
1265 "fn multi_open() should return the correct number of proofs and evals"
1266 );
1267 points
1268 .iter()
1269 .zip(proofs.into_iter())
1270 .zip(evals.into_iter())
1271 .for_each(|((point, proof), eval)| {
1272 assert_eq!(
1273 UnivariateKzgPCS::<E>::open(&ck, &poly, point).unwrap(),
1274 (proof, eval)
1275 );
1276 });
1277 let domain: Radix2EvaluationDomain<Fr> =
1279 UnivariateKzgPCS::<E>::multi_open_rou_eval_domain(degree, num_points)?;
1280 let (proofs, evals) =
1281 UnivariateKzgPCS::<E>::multi_open_rou(&ck, &poly, num_points, &domain)?;
1282 assert!(
1283 proofs.len() == evals.len() && proofs.len() == num_points,
1284 "fn multi_open_rou() should return the correct number of proofs and evals"
1285 );
1286
1287 domain
1288 .elements()
1289 .take(num_points)
1290 .zip(proofs.into_iter())
1291 .zip(evals.into_iter())
1292 .for_each(|((point, proof), eval)| {
1293 assert_eq!(
1294 UnivariateKzgPCS::<E>::open(&ck, &poly, &point).unwrap(),
1295 (proof, eval)
1296 );
1297 });
1298 }
1299
1300 Ok(())
1301 }
1302
1303 #[cfg(feature = "icicle")]
1304 mod icicle {
1305 use super::*;
1306 #[cfg(feature = "kzg-print-trace")]
1307 use crate::icicle_deps::warmup_new_stream;
1308 use crate::{
1309 icicle_deps::{curves::*, IcicleCurve},
1310 pcs::univariate_kzg::icicle::GPUCommittable,
1311 };
1312 use core::mem::size_of;
1313 use icicle_core::traits::{ArkConvertible, MontgomeryConvertible};
1314 use icicle_cuda_runtime::{error::CudaResultWrap, memory::HostOrDeviceSlice};
1315
1316 #[cfg(feature = "kzg-print-trace")]
1317 fn gpu_profiling<E: Pairing>() -> Result<(), PCSError>
1318 where
1319 UnivariateKzgPCS<E>: GPUCommittable<E>,
1320 {
1321 let rng = &mut test_rng();
1322 let stream = warmup_new_stream().unwrap();
1323 let degree = 2usize.pow(22);
1324
1325 let pp = UnivariateKzgPCS::<E>::gen_srs_for_testing(rng, degree)?;
1326 let (ck, _vk) = pp.trim(degree)?;
1327 let mut srs_on_gpu =
1328 <UnivariateKzgPCS<E> as GPUCommittable<E>>::load_prover_param_to_gpu(&ck, degree)?;
1329
1330 let p = <DensePolynomial<E::ScalarField> as DenseUVPolynomial<E::ScalarField>>::rand(
1331 degree, rng,
1332 );
1333
1334 let _comm =
1335 <UnivariateKzgPCS<E> as GPUCommittable<E>>::gpu_commit_with_loaded_prover_param(
1336 &mut srs_on_gpu,
1337 &p,
1338 &stream,
1339 )?;
1340
1341 let polys: Vec<_> = (0..8)
1342 .map(|_| {
1343 <DensePolynomial<E::ScalarField> as DenseUVPolynomial<E::ScalarField>>::rand(
1344 degree / 8,
1345 rng,
1346 )
1347 })
1348 .collect();
1349 let _comms =
1350 <UnivariateKzgPCS<E> as GPUCommittable<E>>::gpu_batch_commit_with_loaded_prover_param(
1351 &mut srs_on_gpu,
1352 &polys,
1353 &stream,
1354 )?;
1355
1356 Ok(())
1357 }
1358
1359 fn test_gpu_e2e_template<E: Pairing>() -> Result<(), PCSError>
1360 where
1361 UnivariateKzgPCS<E>: GPUCommittable<E>,
1362 {
1363 let rng = &mut test_rng();
1364 let supported_degree = 2usize.pow(12);
1365 let pp = UnivariateKzgPCS::<E>::gen_srs_for_testing(rng, supported_degree)?;
1366
1367 for _ in 0..10 {
1369 let degree = usize::rand(rng) % 1025;
1370 let (ck, vk) = pp.trim(degree)?;
1371 let p =
1372 <DensePolynomial<E::ScalarField> as DenseUVPolynomial<E::ScalarField>>::rand(
1373 degree, rng,
1374 );
1375 let comm_gpu = <UnivariateKzgPCS<E> as GPUCommittable<E>>::gpu_commit(&ck, &p)?;
1376 let comm_cpu = UnivariateKzgPCS::<E>::commit(&ck, &p)?;
1377 assert_eq!(comm_gpu, comm_cpu);
1378
1379 let point = E::ScalarField::rand(rng);
1380 let (proof, value) = UnivariateKzgPCS::<E>::open(&ck, &p, &point)?;
1381 assert!(
1382 UnivariateKzgPCS::<E>::verify(&vk, &comm_gpu, &point, &value, &proof)?,
1383 "proof was incorrect for max_degree = {}, polynomial_degree = {}",
1384 degree,
1385 p.degree(),
1386 );
1387
1388 for i in 0..5 {
1390 let batch_size = 10 + i;
1391 let polys: Vec<_> = (0..batch_size)
1392 .map(|_| {
1393 <DensePolynomial<E::ScalarField> as DenseUVPolynomial<
1394 E::ScalarField,
1395 >>::rand(degree, rng)
1396 })
1397 .collect();
1398 let comms_gpu =
1399 <UnivariateKzgPCS<E> as GPUCommittable<E>>::gpu_batch_commit(&ck, &polys)?;
1400 let comms_cpu = UnivariateKzgPCS::<E>::batch_commit(&ck, &polys)?;
1401 assert_eq!(comms_gpu, comms_cpu);
1402 assert!(
1403 <UnivariateKzgPCS<E> as GPUCommittable<E>>::gpu_batch_commit(&ck, &[])
1404 .is_ok()
1405 );
1406 }
1407 }
1408 Ok(())
1409 }
1410
1411 #[test]
1412 fn test_gpu_e2e() {
1413 test_gpu_e2e_template::<Bn254>().unwrap();
1414 }
1415
1416 fn test_gpu_ark_conversion_template<E: Pairing>()
1417 where
1418 UnivariateKzgPCS<E>: GPUCommittable<E>,
1419 <<UnivariateKzgPCS<E> as GPUCommittable<E>>::IC as IcicleCurve>::ScalarField:
1420 ArkConvertible<ArkEquivalent = E::ScalarField> + MontgomeryConvertible,
1421 {
1422 type ICScalarField<E> =
1423 <<UnivariateKzgPCS<E> as GPUCommittable<E>>::IC as IcicleCurve>::ScalarField;
1424 assert_eq!(size_of::<E::ScalarField>(), size_of::<ICScalarField<E>>());
1425 let size = 100usize;
1426 let mut rng = test_rng();
1427 let scalars: Vec<_> = (0..size)
1428 .map(|_| {
1429 let mut bytes = [0u8; 32];
1430 rng.fill_bytes(&mut bytes);
1431 E::ScalarField::from_le_bytes_mod_order(&bytes)
1432 })
1433 .collect();
1434 let mut ic_scalars: Vec<_> = scalars
1435 .iter()
1436 .copied()
1437 .map(ICScalarField::<E>::from_ark)
1438 .collect();
1439 let mut d_scalars = HostOrDeviceSlice::cuda_malloc(size).unwrap();
1440 d_scalars.copy_from_host(&ic_scalars).unwrap();
1441 ICScalarField::<E>::to_mont(&mut d_scalars).wrap().unwrap();
1442 d_scalars.copy_to_host(&mut ic_scalars).unwrap();
1443 let transformed_scalars = unsafe { scalars.align_to::<ICScalarField<E>>().1 };
1444 assert_eq!(ic_scalars, transformed_scalars);
1445 }
1446
1447 #[test]
1448 fn test_gpu_ark_conversion() {
1452 test_gpu_ark_conversion_template::<Bn254>();
1453 }
1454
1455 #[cfg(feature = "kzg-print-trace")]
1456 #[test]
1457 fn profile_gpu_commit() {
1458 gpu_profiling::<Bn254>().unwrap();
1460 }
1461 }
1462}