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