1use super::{vid, VidDisperse, VidError, VidResult, VidScheme};
12use ark_ec::{pairing::Pairing, AffineRepr};
13use ark_ff::{Field, PrimeField};
14use ark_poly::{
15 univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, Radix2EvaluationDomain,
16};
17use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
18use ark_std::{
19 borrow::Borrow,
20 end_timer,
21 fmt::Debug,
22 format,
23 marker::PhantomData,
24 ops::{Add, Mul},
25 start_timer,
26 string::ToString,
27 vec,
28 vec::Vec,
29 Zero,
30};
31use bytes_to_field::{bytes_to_field, field_to_bytes};
32use core::mem;
33use derivative::Derivative;
34use digest::crypto_common::Output;
35use itertools::Itertools;
36use jf_merkle_tree::{
37 hasher::{HasherDigest, HasherMerkleTree, HasherNode},
38 prelude::{MerkleNode, MerkleProof},
39 MerkleCommitment, MerkleTreeScheme,
40};
41#[cfg(feature = "gpu-vid")]
42use jf_pcs::icicle_deps::*;
43use jf_pcs::{
44 prelude::{UnivariateKzgPCS, UnivariateKzgProof},
45 PolynomialCommitmentScheme, StructuredReferenceString, UnivariatePCS,
46};
47use jf_utils::{
48 canonical,
49 par_utils::{parallelizable_chunks, parallelizable_slice_iter},
50 reed_solomon_code::reed_solomon_erasure_decode_rou,
51};
52#[cfg(feature = "parallel")]
53use rayon::prelude::ParallelIterator;
54use serde::{Deserialize, Serialize};
55
56mod bytes_to_field;
57pub mod payload_prover;
58pub mod precomputable;
59
60pub type Advz<E, H> = AdvzInternal<E, H, ()>;
62#[cfg(feature = "gpu-vid")]
64pub type AdvzGPU<'srs, E, H> = AdvzInternal<
65 E,
66 H,
67 (
68 HostOrDeviceSlice<'srs, IcicleAffine<<UnivariateKzgPCS<E> as GPUCommittable<E>>::IC>>,
69 CudaStream,
70 ),
71>;
72
73#[derive(Clone, Debug, Eq, PartialEq)]
80pub struct AdvzInternal<E, H, T>
81where
82 E: Pairing,
83 T: Sync,
84{
85 recovery_threshold: u32,
86 num_storage_nodes: u32,
87 max_multiplicity: u32,
88 ck: KzgProverParam<E>,
89 vk: KzgVerifierParam<E>,
90
91 srs_on_gpu_and_cuda_stream: Option<T>,
95 _pd: (PhantomData<H>, PhantomData<T>),
96}
97
98type KzgPolynomial<E> = <UnivariateKzgPCS<E> as PolynomialCommitmentScheme>::Polynomial;
103type KzgCommit<E> = <UnivariateKzgPCS<E> as PolynomialCommitmentScheme>::Commitment;
104type KzgPoint<E> = <UnivariateKzgPCS<E> as PolynomialCommitmentScheme>::Point;
105type KzgEval<E> = <UnivariateKzgPCS<E> as PolynomialCommitmentScheme>::Evaluation;
106type KzgProof<E> = <UnivariateKzgPCS<E> as PolynomialCommitmentScheme>::Proof;
107type KzgSrs<E> = <UnivariateKzgPCS<E> as PolynomialCommitmentScheme>::SRS;
108type KzgProverParam<E> = <<UnivariateKzgPCS<E> as PolynomialCommitmentScheme>::SRS as StructuredReferenceString>::ProverParam;
109type KzgVerifierParam<E> = <<UnivariateKzgPCS<E> as PolynomialCommitmentScheme>::SRS as StructuredReferenceString>::VerifierParam;
110
111type KzgEvalsMerkleTree<E, H> = HasherMerkleTree<H, Vec<KzgEval<E>>>;
112type KzgEvalsMerkleTreeNode<E, H> = <KzgEvalsMerkleTree<E, H> as MerkleTreeScheme>::NodeValue;
113type KzgEvalsMerkleTreeIndex<E, H> = <KzgEvalsMerkleTree<E, H> as MerkleTreeScheme>::Index;
114type KzgEvalsMerkleTreeProof<E, H> =
115 <KzgEvalsMerkleTree<E, H> as MerkleTreeScheme>::MembershipProof;
116
117impl<E, H, T> AdvzInternal<E, H, T>
118where
119 E: Pairing,
120 T: Sync,
121{
122 pub(crate) fn new_internal(
123 num_storage_nodes: u32, recovery_threshold: u32, srs: impl Borrow<KzgSrs<E>>,
126 ) -> VidResult<Self> {
127 let max_multiplicity = 1;
130
131 Self::with_multiplicity_internal(
132 num_storage_nodes,
133 recovery_threshold,
134 max_multiplicity,
135 srs,
136 )
137 }
138
139 pub(crate) fn with_multiplicity_internal(
140 num_storage_nodes: u32, recovery_threshold: u32, max_multiplicity: u32, srs: impl Borrow<KzgSrs<E>>,
144 ) -> VidResult<Self> {
145 if num_storage_nodes < recovery_threshold {
146 return Err(VidError::Argument(format!(
147 "recovery_threshold {} exceeds num_storage_nodes {}",
148 recovery_threshold, num_storage_nodes
149 )));
150 }
151
152 if !recovery_threshold.is_power_of_two() {
155 return Err(VidError::Argument(format!(
156 "recovery_threshold {recovery_threshold} should be a power of two"
157 )));
158 }
159 if !max_multiplicity.is_power_of_two() {
160 return Err(VidError::Argument(format!(
161 "max_multiplicity {max_multiplicity} should be a power of two"
162 )));
163 }
164
165 let supported_degree =
166 usize::try_from(max_multiplicity * recovery_threshold - 1).map_err(vid)?;
167 let (ck, vk) = UnivariateKzgPCS::trim_fft_size(srs, supported_degree).map_err(vid)?;
168
169 Ok(Self {
170 recovery_threshold,
171 num_storage_nodes,
172 max_multiplicity,
173 ck,
174 vk,
175 srs_on_gpu_and_cuda_stream: None,
176 _pd: Default::default(),
177 })
178 }
179}
180
181impl<E, H> Advz<E, H>
182where
183 E: Pairing,
184{
185 pub fn new(
200 num_storage_nodes: u32,
201 recovery_threshold: u32,
202 srs: impl Borrow<KzgSrs<E>>,
203 ) -> VidResult<Self> {
204 Self::new_internal(num_storage_nodes, recovery_threshold, srs)
205 }
206
207 pub fn with_multiplicity(
218 num_storage_nodes: u32,
219 recovery_threshold: u32,
220 max_multiplicity: u32,
221 srs: impl Borrow<KzgSrs<E>>,
222 ) -> VidResult<Self> {
223 Self::with_multiplicity_internal(
224 num_storage_nodes,
225 recovery_threshold,
226 max_multiplicity,
227 srs,
228 )
229 }
230}
231
232#[cfg(feature = "gpu-vid")]
233impl<'srs, E, H> AdvzGPU<'srs, E, H>
234where
235 E: Pairing,
236 UnivariateKzgPCS<E>: GPUCommittable<E>,
237{
238 pub fn new(
240 num_storage_nodes: u32,
241 recovery_threshold: u32,
242 srs: impl Borrow<KzgSrs<E>>,
243 ) -> VidResult<Self> {
244 let mut advz = Self::new_internal(num_storage_nodes, recovery_threshold, srs)?;
245 advz.init_gpu_srs()?;
246 Ok(advz)
247 }
248 pub fn with_multiplicity(
250 num_storage_nodes: u32,
251 recovery_threshold: u32,
252 max_multiplicity: u32,
253 srs: impl Borrow<KzgSrs<E>>,
254 ) -> VidResult<Self> {
255 let mut advz = Self::with_multiplicity_internal(
256 num_storage_nodes,
257 recovery_threshold,
258 max_multiplicity,
259 srs,
260 )?;
261 advz.init_gpu_srs()?;
262 Ok(advz)
263 }
264
265 fn init_gpu_srs(&mut self) -> VidResult<()> {
266 let srs_on_gpu = <UnivariateKzgPCS<E> as GPUCommittable<E>>::load_prover_param_to_gpu(
267 &self.ck,
268 self.ck.powers_of_g.len() - 1,
269 )
270 .map_err(vid)?;
271 self.srs_on_gpu_and_cuda_stream = Some((srs_on_gpu, warmup_new_stream().map_err(vid)?));
272 Ok(())
273 }
274}
275
276#[derive(Derivative, Deserialize, Serialize)]
278#[serde(bound = "Output<H>: Serialize + for<'a> Deserialize<'a>")]
279#[derivative(
280 Clone(bound = ""),
281 Debug(bound = ""),
282 Eq(bound = ""),
283 Hash(bound = ""),
284 PartialEq(bound = "")
285)]
286pub struct Share<E, H>
287where
288 E: Pairing,
289 H: HasherDigest,
290{
291 index: u32,
292
293 #[serde(with = "canonical")]
294 aggregate_proofs: Vec<KzgProof<E>>,
297
298 evals_proof: KzgEvalsMerkleTreeProof<E, H>,
299}
300
301impl<E, H> Share<E, H>
302where
303 E: Pairing,
304 H: HasherDigest,
305{
306 fn evals(&self) -> VidResult<&Vec<KzgEval<E>>> {
314 self.evals_proof
315 .elem()
316 .ok_or_else(|| VidError::Internal(anyhow::anyhow!("empty merkle proof")))
317 }
318}
319
320#[derive(CanonicalSerialize, CanonicalDeserialize, Derivative, Deserialize, Serialize)]
322#[derivative(
323 Clone(bound = ""),
324 Debug(bound = ""),
325 Eq(bound = ""),
326 Hash(bound = ""),
327 PartialEq(bound = "")
328)]
329pub struct Common<E, H>
330where
331 E: Pairing,
332 H: HasherDigest,
333{
334 #[serde(with = "canonical")]
335 poly_commits: Vec<KzgCommit<E>>,
336
337 #[serde(with = "canonical")]
338 all_evals_digest: KzgEvalsMerkleTreeNode<E, H>,
339
340 payload_byte_len: u32,
341 num_storage_nodes: u32,
342 multiplicity: u32,
343}
344
345pub trait MaybeGPU<E: Pairing> {
348 fn kzg_batch_commit(
355 &mut self,
356 polys: &[DensePolynomial<E::ScalarField>],
357 ) -> VidResult<Vec<KzgCommit<E>>>;
358}
359
360impl<E, H> MaybeGPU<E> for Advz<E, H>
361where
362 E: Pairing,
363{
364 fn kzg_batch_commit(
365 &mut self,
366 polys: &[DensePolynomial<E::ScalarField>],
367 ) -> VidResult<Vec<KzgCommit<E>>> {
368 UnivariateKzgPCS::batch_commit(&self.ck, polys).map_err(vid)
369 }
370}
371
372#[cfg(feature = "gpu-vid")]
373impl<'srs, E, H> MaybeGPU<E> for AdvzGPU<'srs, E, H>
374where
375 E: Pairing,
376 UnivariateKzgPCS<E>: GPUCommittable<E>,
377{
378 fn kzg_batch_commit(
379 &mut self,
380 polys: &[DensePolynomial<E::ScalarField>],
381 ) -> VidResult<Vec<KzgCommit<E>>> {
382 if polys.is_empty() {
383 return Ok(vec![]);
384 }
385 let (srs_on_gpu, stream) = self.srs_on_gpu_and_cuda_stream.as_mut().map_err(vid)?; <UnivariateKzgPCS<E> as GPUCommittable<E>>::gpu_batch_commit_with_loaded_prover_param(
387 srs_on_gpu, polys, stream,
388 )
389 .map_err(vid)
390 }
391}
392
393impl<E, H, T> VidScheme for AdvzInternal<E, H, T>
394where
395 E: Pairing,
396 H: HasherDigest,
397 T: Sync,
398 AdvzInternal<E, H, T>: MaybeGPU<E>,
399{
400 type Commit = HasherNode<H>;
402
403 type Share = Share<E, H>;
404 type Common = Common<E, H>;
405
406 fn commit_only<B>(&mut self, payload: B) -> VidResult<Self::Commit>
407 where
408 B: AsRef<[u8]>,
409 {
410 let payload = payload.as_ref();
411 let payload_byte_len = payload.len().try_into().map_err(vid)?;
412 let multiplicity = self.min_multiplicity(payload_byte_len)?;
413 let chunk_size = multiplicity * self.recovery_threshold;
414 let polys = self.bytes_to_polys(payload)?;
415
416 let poly_commits_time = start_timer!(|| "batch poly commit");
417 let poly_commits = <Self as MaybeGPU<E>>::kzg_batch_commit(self, &polys)?;
418 end_timer!(poly_commits_time);
419
420 Self::derive_commit(&poly_commits, payload.len(), self.num_storage_nodes)
421 }
422
423 fn disperse<B>(&mut self, payload: B) -> VidResult<VidDisperse<Self>>
424 where
425 B: AsRef<[u8]>,
426 {
427 let payload = payload.as_ref();
428 let polys = self.bytes_to_polys(payload)?;
429 let poly_commits = <Self as MaybeGPU<E>>::kzg_batch_commit(self, &polys)?;
430
431 self.disperse_with_polys_and_commits(payload, polys, poly_commits)
432 }
433
434 fn verify_share(
435 &self,
436 share: &Self::Share,
437 common: &Self::Common,
438 commit: &Self::Commit,
439 ) -> VidResult<Result<(), ()>> {
440 if common.num_storage_nodes != self.num_storage_nodes {
442 return Err(VidError::Argument(format!(
443 "common num_storage_nodes {} differs from self {}",
444 common.num_storage_nodes, self.num_storage_nodes
445 )));
446 }
447 let multiplicity =
448 self.min_multiplicity(common.payload_byte_len.try_into().map_err(vid)?)?;
449 let multiplicity_usize = usize::try_from(multiplicity).map_err(vid)?;
450 if common.multiplicity != multiplicity {
451 return Err(VidError::Argument(format!(
452 "common multiplicity {} differs from derived min {}",
453 common.multiplicity, multiplicity
454 )));
455 }
456 let evals = share.evals()?;
457 if evals.len() / multiplicity as usize != common.poly_commits.len() {
458 return Err(VidError::Argument(format!(
459 "number of share evals / multiplicity {}/{} differs from number of common polynomial commitments {}",
460 evals.len(), multiplicity,
461 common.poly_commits.len()
462 )));
463 }
464 Self::is_consistent(commit, common)?;
465 if share.index >= self.num_storage_nodes {
466 return Ok(Err(())); }
468
469 if KzgEvalsMerkleTree::<E, H>::verify(
471 common.all_evals_digest,
472 &KzgEvalsMerkleTreeIndex::<E, H>::from(share.index),
473 &share.evals_proof,
474 )
475 .map_err(vid)?
476 .is_err()
477 {
478 return Ok(Err(()));
479 }
480
481 let pseudorandom_scalar = Self::pseudorandom_scalar(common, commit)?;
483
484 let aggregate_poly_commit = KzgCommit::<E>::from(
493 polynomial_eval(
494 common
495 .poly_commits
496 .iter()
497 .map(|x| CurveMultiplier(x.as_ref())),
498 pseudorandom_scalar,
499 )
500 .into(),
501 );
502
503 let multiplicities = Vec::from_iter((0..multiplicity_usize));
505 let polys_len = common.poly_commits.len();
506 let multi_open_domain = self.multi_open_domain(multiplicity)?;
507
508 let verification_iter = parallelizable_slice_iter(&multiplicities).map(|m| {
510 let evals_iter = evals.iter().skip(*m).step_by(multiplicity_usize);
511 let aggregate_eval =
512 polynomial_eval(evals_iter.map(FieldMultiplier), pseudorandom_scalar);
513 let domain_index = usize::try_from(share.index).map_err(vid)?
514 + m * usize::try_from(self.num_storage_nodes).map_err(vid)?;
515 Ok(UnivariateKzgPCS::verify(
516 &self.vk,
517 &aggregate_poly_commit,
518 &multi_open_domain.element(domain_index),
519 &aggregate_eval,
520 &share.aggregate_proofs[*m],
521 )
522 .map_err(vid)?
523 .then_some(())
524 .ok_or(()))
525 });
526
527 let abort = |result: &VidResult<Result<(), ()>>| match result {
529 Ok(success) => success.is_err(),
530 Err(_) => true,
531 };
532
533 #[cfg(feature = "parallel")]
535 let result = verification_iter.find_any(abort);
536
537 #[cfg(not(feature = "parallel"))]
538 let result = verification_iter.clone().find(abort); result.unwrap_or(Ok(Ok(())))
541 }
542
543 fn recover_payload(&self, shares: &[Self::Share], common: &Self::Common) -> VidResult<Vec<u8>> {
544 if shares.len() < self.recovery_threshold as usize {
546 return Err(VidError::Argument(format!(
547 "not enough shares {}, expected at least {}",
548 shares.len(),
549 self.recovery_threshold
550 )));
551 }
552 if common.num_storage_nodes != self.num_storage_nodes {
553 return Err(VidError::Argument(format!(
554 "common num_storage_nodes differs from self ({},{})",
555 common.num_storage_nodes, self.num_storage_nodes
556 )));
557 }
558
559 let all_shares_evals = shares
560 .iter()
561 .map(|share| share.evals())
562 .collect::<VidResult<Vec<_>>>()?;
563
564 let num_evals = all_shares_evals
566 .first()
567 .ok_or_else(|| VidError::Argument("shares is empty".into()))?
568 .len();
569 if let Some((index, share_evals)) = all_shares_evals
570 .iter()
571 .enumerate()
572 .find(|(_, evals)| evals.len() != num_evals)
573 {
574 return Err(VidError::Argument(format!(
575 "shares do not have equal evals lengths: share {} len {}, share {} len {}",
576 0,
577 num_evals,
578 index,
579 share_evals.len()
580 )));
581 }
582 if num_evals != common.multiplicity as usize * common.poly_commits.len() {
583 return Err(VidError::Argument(format!(
584 "num_evals should be (multiplicity * poly_commits): {} but is instead: {}",
585 common.multiplicity as usize * common.poly_commits.len(),
586 num_evals,
587 )));
588 }
589
590 let multiplicity = usize::try_from(common.multiplicity).map_err(vid)?;
592 let num_storage_nodes = usize::try_from(self.num_storage_nodes).map_err(vid)?;
593 let chunk_size = multiplicity * usize::try_from(self.recovery_threshold).map_err(vid)?;
594 let num_polys = common.poly_commits.len();
595 let elems_capacity = num_polys * chunk_size;
596 let fft_domain = Self::eval_domain(chunk_size)?;
597
598 let mut elems = Vec::with_capacity(elems_capacity);
599 let mut evals = Vec::with_capacity(num_evals);
600 for p in 0..num_polys {
601 for (share, share_evals) in shares.iter().zip(all_shares_evals.iter()) {
602 for m in 0..common.multiplicity as usize {
604 evals.push((
605 usize::try_from(share.index).map_err(vid)? + m * num_storage_nodes,
606 share_evals[p * multiplicity + m],
607 ))
608 }
609 }
610 let mut coeffs = reed_solomon_erasure_decode_rou(
611 mem::take(&mut evals),
612 chunk_size as usize,
613 &self.multi_open_domain(common.multiplicity)?,
614 )
615 .map_err(vid)?;
616
617 fft_domain.fft_in_place(&mut coeffs);
621
622 elems.append(&mut coeffs);
623 }
624 assert_eq!(elems.len(), elems_capacity);
625
626 let mut payload: Vec<_> = field_to_bytes(elems).collect();
627 payload.truncate(common.payload_byte_len.try_into().map_err(vid)?);
628 Ok(payload)
629 }
630
631 fn is_consistent(commit: &Self::Commit, common: &Self::Common) -> VidResult<()> {
632 if *commit
633 != Advz::<E, H>::derive_commit(
634 &common.poly_commits,
635 common.payload_byte_len,
636 common.num_storage_nodes,
637 )?
638 {
639 return Err(VidError::Argument(
640 "common inconsistent with commit".to_string(),
641 ));
642 }
643 Ok(())
644 }
645
646 fn get_payload_byte_len(common: &Self::Common) -> u32 {
647 common.payload_byte_len
648 }
649
650 fn get_num_storage_nodes(common: &Self::Common) -> u32 {
651 common.num_storage_nodes
652 }
653
654 fn get_multiplicity(common: &Self::Common) -> u32 {
655 common.multiplicity
656 }
657}
658
659impl<E, H, SrsRef> AdvzInternal<E, H, SrsRef>
660where
661 E: Pairing,
662 H: HasherDigest,
663 SrsRef: Sync,
664 AdvzInternal<E, H, SrsRef>: MaybeGPU<E>,
665{
666 fn disperse_with_polys_and_commits(
667 &self,
668 payload: &[u8],
669 polys: Vec<DensePolynomial<<E as Pairing>::ScalarField>>,
670 poly_commits: Vec<KzgCommit<E>>,
671 ) -> VidResult<VidDisperse<Self>> {
672 let payload_byte_len = payload.len().try_into().map_err(vid)?;
673 let disperse_time = start_timer!(|| format!(
674 "VID disperse {} payload bytes to {} nodes",
675 payload_byte_len, self.num_storage_nodes
676 ));
677 let multiplicity = self.min_multiplicity(payload.len())?;
678 let multiplicity_usize = usize::try_from(multiplicity).map_err(vid)?;
679 let num_storage_nodes_usize = usize::try_from(self.num_storage_nodes).map_err(vid)?;
680 let code_word_size = num_storage_nodes_usize * multiplicity_usize;
681 let multi_open_domain = self.multi_open_domain(multiplicity)?;
682
683 let all_storage_node_evals_timer = start_timer!(|| format!(
685 "compute all storage node evals for {} polynomials with {} coefficients",
686 polys.len(),
687 multiplicity * self.recovery_threshold
688 ));
689 let all_storage_node_evals = {
690 let all_poly_evals = parallelizable_slice_iter(&polys)
691 .map(|poly| {
692 UnivariateKzgPCS::<E>::multi_open_rou_evals(
693 poly,
694 code_word_size,
695 &multi_open_domain,
696 )
697 .map(|poly_evals| {
698 assert_eq!(poly_evals.len(), code_word_size); poly_evals
700 })
701 .map_err(vid)
702 })
703 .collect::<Result<Vec<_>, VidError>>()?;
704 assert_eq!(all_poly_evals.len(), polys.len()); let mut all_storage_node_evals =
721 vec![Vec::with_capacity(polys.len() * multiplicity_usize); num_storage_nodes_usize];
722 for poly_evals in all_poly_evals {
723 for (i, poly_eval) in poly_evals.into_iter().enumerate() {
724 all_storage_node_evals[i % num_storage_nodes_usize].push(poly_eval);
725 }
726 }
727
728 assert_eq!(all_storage_node_evals.len(), num_storage_nodes_usize);
730 for storage_node_evals in all_storage_node_evals.iter() {
731 assert_eq!(storage_node_evals.len(), polys.len() * multiplicity_usize);
732 }
733
734 all_storage_node_evals
735 };
736 end_timer!(all_storage_node_evals_timer);
737
738 let all_evals_commit_timer =
740 start_timer!(|| "compute merkle root of all storage node evals");
741 let all_evals_commit =
742 KzgEvalsMerkleTree::<E, H>::from_elems(None, all_storage_node_evals).map_err(vid)?;
743 end_timer!(all_evals_commit_timer);
744
745 let common = Common {
746 poly_commits,
747 all_evals_digest: all_evals_commit.commitment().digest(),
748 payload_byte_len,
749 num_storage_nodes: self.num_storage_nodes,
750 multiplicity,
751 };
752
753 let commit = Self::derive_commit(
754 &common.poly_commits,
755 payload_byte_len,
756 self.num_storage_nodes,
757 )?;
758 let pseudorandom_scalar = Self::pseudorandom_scalar(&common, &commit)?;
759
760 let aggregate_poly =
765 polynomial_eval(polys.iter().map(PolynomialMultiplier), pseudorandom_scalar);
766
767 let agg_proofs_timer = start_timer!(|| format!(
768 "compute aggregate proofs for {} storage nodes",
769 self.num_storage_nodes
770 ));
771 let aggregate_proofs = UnivariateKzgPCS::multi_open_rou_proofs(
772 &self.ck,
773 &aggregate_poly,
774 code_word_size as usize,
775 &multi_open_domain,
776 )
777 .map_err(vid)?;
778 end_timer!(agg_proofs_timer);
779
780 let assemblage_timer = start_timer!(|| "assemble shares for dispersal");
781
782 let mut all_storage_node_aggregate_proofs =
793 vec![Vec::with_capacity(multiplicity_usize); num_storage_nodes_usize];
794 for (i, aggregate_proof) in aggregate_proofs.into_iter().enumerate() {
795 all_storage_node_aggregate_proofs[i % num_storage_nodes_usize].push(aggregate_proof);
796 }
797
798 assert_eq!(
800 all_storage_node_aggregate_proofs.len(),
801 num_storage_nodes_usize
802 );
803 for storage_node_aggregate_proof in all_storage_node_aggregate_proofs.iter() {
804 assert_eq!(storage_node_aggregate_proof.len(), multiplicity_usize);
805 }
806
807 let shares = all_storage_node_aggregate_proofs
808 .into_iter()
809 .enumerate()
810 .map(|(i, aggregate_proofs)| {
811 Ok(Share {
812 index: u32::try_from(i).map_err(vid)?,
813 aggregate_proofs,
814 evals_proof: all_evals_commit
815 .lookup(KzgEvalsMerkleTreeIndex::<E, H>::from(i as u64))
816 .expect_ok()
817 .map_err(vid)?
818 .1,
819 })
820 })
821 .collect::<VidResult<Vec<_>>>()?;
822
823 end_timer!(assemblage_timer);
824 end_timer!(disperse_time);
825
826 Ok(VidDisperse {
827 shares,
828 common,
829 commit,
830 })
831 }
832
833 fn pseudorandom_scalar(
834 common: &<Self as VidScheme>::Common,
835 commit: &<Self as VidScheme>::Commit,
836 ) -> VidResult<KzgEval<E>> {
837 let mut hasher = H::new();
838 commit.serialize_uncompressed(&mut hasher).map_err(vid)?;
839 common
840 .all_evals_digest
841 .serialize_uncompressed(&mut hasher)
842 .map_err(vid)?;
843
844 Ok(PrimeField::from_le_bytes_mod_order(&hasher.finalize()))
858 }
859
860 fn bytes_to_polys(
862 &self,
863 payload: &[u8],
864 ) -> VidResult<Vec<DensePolynomial<<E as Pairing>::ScalarField>>>
865 where
866 E: Pairing,
867 {
868 let elem_bytes_len = bytes_to_field::elem_byte_capacity::<<E as Pairing>::ScalarField>();
869 let domain_size =
870 usize::try_from(self.min_multiplicity(payload.len())? * self.recovery_threshold)
871 .map_err(vid)?;
872
873 let bytes_to_polys_time = start_timer!(|| "encode payload bytes into polynomials");
874 let result = parallelizable_chunks(payload, domain_size * elem_bytes_len)
875 .map(|chunk| {
876 Self::interpolate_polynomial(bytes_to_field::<_, KzgEval<E>>(chunk), domain_size)
877 })
878 .collect::<VidResult<Vec<_>>>();
879 end_timer!(bytes_to_polys_time);
880 result
881 }
882
883 fn interpolate_polynomial<I>(evals: I, domain_size: usize) -> VidResult<KzgPolynomial<E>>
900 where
901 I: Iterator,
902 I::Item: Borrow<KzgEval<E>>,
903 {
904 let mut evals_vec: Vec<_> = evals.map(|c| *c.borrow()).collect();
908 let pre_fft_len = evals_vec.len();
909 if pre_fft_len > domain_size {
910 return Err(VidError::Internal(anyhow::anyhow!(
911 "number of evals {} exceeds domain_size {}",
912 pre_fft_len,
913 domain_size
914 )));
915 }
916 let domain = Self::eval_domain(domain_size)?;
917
918 domain.ifft_in_place(&mut evals_vec);
919
920 if pre_fft_len == domain_size && pre_fft_len != evals_vec.len() {
924 return Err(VidError::Internal(anyhow::anyhow!(
925 "unexpected output resize from {pre_fft_len} to {}",
926 evals_vec.len()
927 )));
928 }
929
930 Ok(DenseUVPolynomial::from_coefficients_vec(evals_vec))
931 }
932
933 fn min_multiplicity(&self, payload_byte_len: usize) -> VidResult<u32> {
934 let elem_bytes_len = bytes_to_field::elem_byte_capacity::<<E as Pairing>::ScalarField>();
935 let elems: u32 = payload_byte_len
936 .div_ceil(elem_bytes_len)
937 .try_into()
938 .map_err(vid)?;
939 if self.recovery_threshold * self.max_multiplicity < elems {
940 return Ok(self.max_multiplicity);
942 }
943
944 let m = elems.div_ceil(self.recovery_threshold.max(1)).max(1);
948
949 if m <= 1 {
957 Ok(1)
958 } else {
959 Ok(1 << ((m - 1).ilog2() + 1))
960 }
961 }
962
963 fn derive_commit<T, U>(
969 poly_commits: &[KzgCommit<E>],
970 payload_byte_len: T,
971 num_storage_nodes: U,
972 ) -> VidResult<<Self as VidScheme>::Commit>
973 where
974 T: TryInto<u32>,
975 <T as TryInto<u32>>::Error: ark_std::fmt::Display + Debug + Send + Sync + 'static,
976 U: TryInto<u32>,
977 <U as TryInto<u32>>::Error: ark_std::fmt::Display + Debug + Send + Sync + 'static,
978 {
979 let payload_byte_len: u32 = payload_byte_len.try_into().map_err(vid)?;
980 let num_storage_nodes: u32 = num_storage_nodes.try_into().map_err(vid)?;
981 let mut hasher = H::new();
982 payload_byte_len
983 .serialize_uncompressed(&mut hasher)
984 .map_err(vid)?;
985 num_storage_nodes
986 .serialize_uncompressed(&mut hasher)
987 .map_err(vid)?;
988 for poly_commit in poly_commits {
989 poly_commit
990 .serialize_uncompressed(&mut hasher)
991 .map_err(vid)?;
992 }
993 Ok(hasher.finalize().into())
994 }
995
996 fn multi_open_domain(
997 &self,
998 multiplicity: u32,
999 ) -> VidResult<Radix2EvaluationDomain<<E as Pairing>::ScalarField>> {
1000 let chunk_size = usize::try_from(multiplicity * self.recovery_threshold).map_err(vid)?;
1001 let code_word_size = usize::try_from(multiplicity * self.num_storage_nodes).map_err(vid)?;
1002 UnivariateKzgPCS::<E>::multi_open_rou_eval_domain(chunk_size - 1, code_word_size)
1003 .map_err(vid)
1004 }
1005
1006 fn eval_domain(
1007 domain_size: usize,
1008 ) -> VidResult<Radix2EvaluationDomain<<E as Pairing>::ScalarField>> {
1009 Radix2EvaluationDomain::<KzgPoint<E>>::new(domain_size).ok_or_else(|| {
1010 VidError::Internal(anyhow::anyhow!(
1011 "fail to construct domain of size {domain_size}"
1012 ))
1013 })
1014 }
1015}
1016
1017fn polynomial_eval<U, F, I>(coeffs: I, point: impl Borrow<F>) -> U
1022where
1023 I: IntoIterator,
1024 I::Item: for<'a> Mul<&'a F, Output = U>,
1025 U: Add<Output = U> + Zero,
1026{
1027 coeffs
1028 .into_iter()
1029 .fold(U::zero(), |res, coeff| coeff * point.borrow() + res)
1030}
1031
1032struct FieldMultiplier<'a, F>(&'a F);
1033
1034impl<F> Mul<&F> for FieldMultiplier<'_, F>
1036where
1037 F: Field,
1038{
1039 type Output = F;
1040
1041 fn mul(self, rhs: &F) -> Self::Output {
1042 *self.0 * rhs
1043 }
1044}
1045
1046struct CurveMultiplier<'a, C>(&'a C);
1048
1049impl<C, F> Mul<&F> for CurveMultiplier<'_, C>
1050where
1051 C: AffineRepr<ScalarField = F>,
1052{
1053 type Output = C::Group;
1054
1055 fn mul(self, rhs: &F) -> Self::Output {
1056 *self.0 * rhs
1057 }
1058}
1059
1060struct PolynomialMultiplier<'a, P>(&'a P);
1062
1063impl<P, F> Mul<&F> for PolynomialMultiplier<'_, P>
1064where
1065 P: DenseUVPolynomial<F>,
1066 F: Field,
1067{
1068 type Output = P;
1069
1070 fn mul(self, rhs: &F) -> Self::Output {
1071 P::from_coefficients_vec(self.0.coeffs().iter().map(|coeff| *coeff * rhs).collect())
1074 }
1075}
1076
1077#[cfg(test)]
1078mod test;