1use anyhow::anyhow;
2use ark_ec::Group;
3use ark_ff::{One, Zero};
4use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate};
5use rand_core::{CryptoRngCore, OsRng};
6
7use crate::parallel_utils::{flatten_results, transform_parallel, zip_map_parallel};
8use crate::single::dlog;
9use crate::single::group::{
10 BatchedPairingChecker11, BatchedPairingChecker12, GroupHasher, F, G1, G2,
11};
12use crate::single::log::{ContributionHash, Hashable, Phase};
13
14const fn is_degree_large_enough(d: usize) -> bool {
18 d >= 2
20}
21
22const fn short_len(d: usize) -> usize {
25 (d - 1) + 1
26}
27
28const fn short_len_to_degree(l: usize) -> usize {
29 l
30}
31
32const fn long_len(d: usize) -> usize {
33 (2 * d - 2) + 1
34}
35
36#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize, PartialEq)]
38pub struct RawCRSElements {
39 pub alpha_1: G1,
40 pub beta_1: G1,
41 pub beta_2: G2,
42 pub x_1: Vec<G1>,
43 pub x_2: Vec<G2>,
44 pub alpha_x_1: Vec<G1>,
45 pub beta_x_1: Vec<G1>,
46}
47
48impl RawCRSElements {
49 fn get_degree(&self) -> Option<usize> {
54 let l = self.x_2.len();
55 let d = short_len_to_degree(l);
56 if !is_degree_large_enough(d) {
57 return None;
58 }
59 if self.alpha_x_1.len() != short_len(d) {
60 return None;
61 }
62 if self.beta_x_1.len() != short_len(d) {
63 return None;
64 }
65 if self.x_1.len() != long_len(d) {
66 return None;
67 }
68 Some(d)
69 }
70
71 pub fn validate(self) -> anyhow::Result<CRSElements> {
76 let d = self
78 .get_degree()
79 .ok_or_else(|| anyhow!("failed to get degree"))?;
80 if self.alpha_1.is_zero()
82 || self.beta_1.is_zero()
83 || self.beta_2.is_zero()
84 || self.x_1[1].is_zero()
85 || self.x_2[1].is_zero()
86 {
87 anyhow::bail!("one of alpha_1, beta_1, beta_2, x_1[1], x_2[1] was zero");
88 }
89 let mut checker0 = BatchedPairingChecker12::new(G2::generator(), G1::generator());
92 checker0.add(self.beta_1, self.beta_2);
93 for (&x_1_i, &x_2_i) in self.x_1.iter().zip(self.x_2.iter()) {
94 checker0.add(x_1_i, x_2_i);
95 }
96
97 let mut checker1 = BatchedPairingChecker12::new(G2::generator(), self.alpha_1);
99 for (&alpha_x_i, &x_i) in self.alpha_x_1.iter().zip(self.x_2.iter()) {
100 checker1.add(alpha_x_i, x_i);
101 }
102 let mut checker2 = BatchedPairingChecker12::new(G2::generator(), self.beta_1);
105 for (&beta_x_i, &x_i) in self.beta_x_1.iter().zip(self.x_2.iter()) {
106 checker2.add(beta_x_i, x_i);
107 }
108 let mut checker3 = BatchedPairingChecker11::new(self.x_2[1], G2::generator());
110 for (&x_i, &x_i_plus_1) in self.x_1.iter().zip(self.x_1.iter().skip(1)) {
111 checker3.add(x_i, x_i_plus_1);
112 }
113 flatten_results(transform_parallel(
115 [
116 (0, Ok(checker0)),
117 (1, Ok(checker1)),
118 (2, Ok(checker2)),
119 (3, Err(checker3)),
120 ],
121 |(i, x)| {
122 let ok = match x {
123 Ok(x) => x.check(&mut OsRng),
124 Err(x) => x.check(&mut OsRng),
125 };
126 if ok {
127 Ok(())
128 } else {
129 Err(anyhow!("checker {} failed", i))
130 }
131 },
132 ))?;
133
134 Ok(CRSElements {
135 degree: d,
136 raw: self,
137 })
138 }
139
140 pub(crate) fn assume_valid(self) -> CRSElements {
142 let d = self
143 .get_degree()
144 .expect("can get degree from valid elements");
145
146 CRSElements {
147 degree: d,
148 raw: self,
149 }
150 }
151
152 #[cfg(not(feature = "parallel"))]
154 pub(crate) fn checked_deserialize_parallel(
155 compress: Compress,
156 data: &[u8],
157 ) -> anyhow::Result<Self> {
158 Ok(Self::deserialize_with_mode(data, compress, Validate::Yes)?)
159 }
160
161 #[cfg(feature = "parallel")]
163 pub(crate) fn checked_deserialize_parallel(
164 compress: Compress,
165 data: &[u8],
166 ) -> anyhow::Result<Self> {
167 use ark_serialize::Valid;
168 use rayon::prelude::*;
169
170 let out = Self::deserialize_with_mode(data, compress, Validate::No)?;
171 out.alpha_1.check()?;
172 out.beta_1.check()?;
173 out.beta_2.check()?;
174
175 let mut check_x_1 = Ok(());
176 let mut check_x_2 = Ok(());
177 let mut check_alpha_x_1 = Ok(());
178 let mut check_beta_x_1 = Ok(());
179
180 rayon::join(
181 || {
182 rayon::join(
183 || {
184 check_x_1 = out
185 .x_1
186 .par_iter()
187 .map(|x| x.check())
188 .collect::<Result<_, _>>();
189 },
190 || {
191 check_x_2 = out
192 .x_2
193 .par_iter()
194 .map(|x| x.check())
195 .collect::<Result<_, _>>();
196 },
197 )
198 },
199 || {
200 rayon::join(
201 || {
202 check_alpha_x_1 = out
203 .alpha_x_1
204 .par_iter()
205 .map(|x| x.check())
206 .collect::<Result<_, _>>();
207 },
208 || {
209 check_beta_x_1 = out
210 .beta_x_1
211 .par_iter()
212 .map(|x| x.check())
213 .collect::<Result<_, _>>();
214 },
215 )
216 },
217 );
218
219 check_x_1?;
220 check_x_2?;
221 check_alpha_x_1?;
222 check_beta_x_1?;
223 Ok(out)
224 }
225}
226
227impl Hashable for RawCRSElements {
228 fn hash(&self) -> ContributionHash {
229 let mut hasher = GroupHasher::new(b"PC$:crs_elmnts1");
230 hasher.eat_g1(&self.alpha_1);
231 hasher.eat_g1(&self.beta_1);
232 hasher.eat_g2(&self.beta_2);
233
234 hasher.eat_usize(self.x_1.len());
235 for v in &self.x_1 {
236 hasher.eat_g1(v);
237 }
238
239 hasher.eat_usize(self.x_2.len());
240 for v in &self.x_2 {
241 hasher.eat_g2(v);
242 }
243
244 hasher.eat_usize(self.alpha_x_1.len());
245 for v in &self.alpha_x_1 {
246 hasher.eat_g1(v);
247 }
248
249 hasher.eat_usize(self.beta_x_1.len());
250 for v in &self.beta_x_1 {
251 hasher.eat_g1(v);
252 }
253
254 ContributionHash(hasher.finalize_bytes())
255 }
256}
257
258#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
262pub struct CRSElements {
263 pub(crate) degree: usize,
264 pub(crate) raw: RawCRSElements,
265}
266
267impl CRSElements {
268 pub fn root(d: usize) -> Self {
276 assert!(is_degree_large_enough(d));
277
278 let raw = RawCRSElements {
279 alpha_1: G1::generator(),
280 beta_1: G1::generator(),
281 beta_2: G2::generator(),
282 x_1: vec![G1::generator(); (2 * d - 2) + 1],
283 x_2: vec![G2::generator(); (d - 1) + 1],
284 alpha_x_1: vec![G1::generator(); (d - 1) + 1],
285 beta_x_1: vec![G1::generator(); (d - 1) + 1],
286 };
287 Self { degree: d, raw }
288 }
289}
290
291impl Hashable for CRSElements {
292 fn hash(&self) -> ContributionHash {
293 self.raw.hash()
295 }
296}
297
298#[derive(Clone, Copy, Debug, CanonicalSerialize, CanonicalDeserialize)]
304pub(crate) struct LinkingProof {
305 alpha_proof: dlog::Proof,
306 beta_proof: dlog::Proof,
307 x_proof: dlog::Proof,
308}
309
310#[derive(Clone, Debug)]
312pub struct RawContribution {
313 pub parent: ContributionHash,
314 pub new_elements: RawCRSElements,
315 pub(crate) linking_proof: LinkingProof,
316}
317
318impl RawContribution {
319 pub fn validate(&self) -> Option<Contribution> {
321 self.new_elements
322 .to_owned()
323 .validate()
324 .ok()
325 .map(|new_elements| Contribution {
326 parent: self.parent,
327 new_elements,
328 linking_proof: self.linking_proof,
329 })
330 }
331
332 pub(crate) fn assume_valid(self) -> Contribution {
333 Contribution {
334 parent: self.parent,
335 new_elements: self.new_elements.assume_valid(),
336 linking_proof: self.linking_proof,
337 }
338 }
339}
340
341impl Hashable for RawContribution {
342 fn hash(&self) -> ContributionHash {
343 let mut hasher = GroupHasher::new(b"PC$:contrbution1");
344 hasher.eat_bytes(self.parent.as_ref());
345 hasher.eat_bytes(self.new_elements.hash().as_ref());
346 hasher.eat_bytes(self.linking_proof.alpha_proof.hash().as_ref());
350 hasher.eat_bytes(self.linking_proof.beta_proof.hash().as_ref());
351 hasher.eat_bytes(self.linking_proof.x_proof.hash().as_ref());
352 ContributionHash(hasher.finalize_bytes())
353 }
354}
355
356impl From<Contribution> for RawContribution {
357 fn from(value: Contribution) -> Self {
358 Self {
359 parent: value.parent,
360 new_elements: value.new_elements.raw,
361 linking_proof: value.linking_proof,
362 }
363 }
364}
365
366#[derive(Clone, Debug)]
373pub struct Contribution {
374 pub parent: ContributionHash,
375 pub new_elements: CRSElements,
376 pub(crate) linking_proof: LinkingProof,
377}
378
379impl Hashable for Contribution {
380 fn hash(&self) -> ContributionHash {
381 RawContribution::from(self.to_owned()).hash()
382 }
383}
384
385impl Contribution {
386 pub fn make<R: CryptoRngCore>(
392 rng: &mut R,
393 parent: ContributionHash,
394 old: &CRSElements,
395 ) -> Self {
396 let alpha = F::rand(rng);
397 let beta = F::rand(rng);
398 let x = F::rand(rng);
399
400 let alpha_proof = dlog::prove(
401 rng,
402 b"phase1 alpha proof",
403 dlog::Statement {
404 result: old.raw.alpha_1 * alpha,
405 base: old.raw.alpha_1,
406 },
407 dlog::Witness { dlog: alpha },
408 );
409 let beta_proof = dlog::prove(
410 rng,
411 b"phase1 beta proof",
412 dlog::Statement {
413 result: old.raw.beta_1 * beta,
414 base: old.raw.beta_1,
415 },
416 dlog::Witness { dlog: beta },
417 );
418 let x_proof = dlog::prove(
419 rng,
420 b"phase1 x proof",
421 dlog::Statement {
422 result: old.raw.x_1[1] * x,
423 base: old.raw.x_1[1],
424 },
425 dlog::Witness { dlog: x },
426 );
427
428 let d = old.degree;
429
430 let (mut x_i_tweaks, mut alpha_x_i_tweaks, mut beta_x_i_tweaks) = {
431 let mut x_i_tweaks = Vec::new();
432 let mut alpha_x_i_tweaks = Vec::new();
433 let mut beta_x_i_tweaks = Vec::new();
434
435 let mut x_i = F::one();
436 let mut alpha_x_i = alpha;
437 let mut beta_x_i = beta;
438
439 for _ in 0..short_len(d) {
440 x_i_tweaks.push(x_i);
441 alpha_x_i_tweaks.push(alpha_x_i);
442 beta_x_i_tweaks.push(beta_x_i);
443
444 x_i *= x;
445 alpha_x_i *= x;
446 beta_x_i *= x;
447 }
448 for _ in short_len(d)..long_len(d) {
449 x_i_tweaks.push(x_i);
450
451 x_i *= x;
452 }
453
454 (x_i_tweaks, alpha_x_i_tweaks, beta_x_i_tweaks)
455 };
456
457 let mut old = old.clone();
458 let x_1 = zip_map_parallel(&mut old.raw.x_1, &mut x_i_tweaks, |g, x| *g * x);
459 let x_2 = zip_map_parallel(&mut old.raw.x_2, &mut x_i_tweaks, |g, x| *g * x);
460 let alpha_x_1 =
461 zip_map_parallel(&mut old.raw.alpha_x_1, &mut alpha_x_i_tweaks, |g, x| *g * x);
462 let beta_x_1 = zip_map_parallel(&mut old.raw.beta_x_1, &mut beta_x_i_tweaks, |g, x| *g * x);
463
464 let new_elements = CRSElements {
465 degree: d,
466 raw: RawCRSElements {
467 alpha_1: old.raw.alpha_1 * alpha,
468 beta_1: old.raw.beta_1 * beta,
469 beta_2: old.raw.beta_2 * beta,
470 x_1,
471 x_2,
472 alpha_x_1,
473 beta_x_1,
474 },
475 };
476
477 Self {
478 parent,
479 new_elements,
480 linking_proof: LinkingProof {
481 alpha_proof,
482 beta_proof,
483 x_proof,
484 },
485 }
486 }
487
488 #[must_use]
490 pub fn is_linked_to(&self, parent: &CRSElements) -> bool {
491 if self.new_elements.degree != parent.degree {
493 return false;
494 }
495 let ctxs: [&'static [u8]; 3] = [
497 b"phase1 alpha proof",
498 b"phase1 beta proof",
499 b"phase1 x proof",
500 ];
501 let statements = [
502 dlog::Statement {
503 result: self.new_elements.raw.alpha_1,
504 base: parent.raw.alpha_1,
505 },
506 dlog::Statement {
507 result: self.new_elements.raw.beta_1,
508 base: parent.raw.beta_1,
509 },
510 dlog::Statement {
511 result: self.new_elements.raw.x_1[1],
512 base: parent.raw.x_1[1],
513 },
514 ];
515 let proofs = [
516 self.linking_proof.alpha_proof,
517 self.linking_proof.beta_proof,
518 self.linking_proof.x_proof,
519 ];
520 if !ctxs
521 .iter()
522 .zip(statements.iter())
523 .zip(proofs.iter())
524 .all(|((c, &s), p)| dlog::verify(c, s, p))
525 {
526 return false;
527 }
528
529 true
530 }
531}
532
533#[derive(Clone, Debug, Default)]
535struct Phase1;
536
537impl Phase for Phase1 {
538 type CRSElements = CRSElements;
539
540 type RawContribution = RawContribution;
541
542 type Contribution = Contribution;
543
544 fn parent_hash(contribution: &Self::RawContribution) -> ContributionHash {
545 contribution.parent
546 }
547
548 fn elements(contribution: &Self::Contribution) -> &Self::CRSElements {
549 &contribution.new_elements
550 }
551
552 fn validate(
553 _root: &Self::CRSElements,
554 contribution: &Self::RawContribution,
555 ) -> Option<Self::Contribution> {
556 contribution.validate()
557 }
558
559 fn is_linked_to(contribution: &Self::Contribution, elements: &Self::CRSElements) -> bool {
560 contribution.is_linked_to(elements)
561 }
562}
563
564#[cfg(test)]
565mod test {
566 use super::*;
567
568 use rand_core::OsRng;
569
570 use crate::single::group::F;
571 use crate::single::log::CONTRIBUTION_HASH_SIZE;
572
573 const D: usize = 2;
577
578 fn make_crs(alpha: F, beta: F, x: F) -> RawCRSElements {
579 RawCRSElements {
580 alpha_1: G1::generator() * alpha,
581 beta_1: G1::generator() * beta,
582 beta_2: G2::generator() * beta,
583 x_1: vec![
584 G1::generator(),
585 G1::generator() * x,
586 G1::generator() * (x * x),
587 ],
588 x_2: vec![G2::generator(), G2::generator() * x],
589 alpha_x_1: vec![G1::generator() * alpha, G1::generator() * (alpha * x)],
590 beta_x_1: vec![G1::generator() * beta, G1::generator() * (beta * x)],
591 }
592 }
593
594 fn non_trivial_crs() -> RawCRSElements {
595 let alpha = F::rand(&mut OsRng);
596 let beta = F::rand(&mut OsRng);
597 let x = F::rand(&mut OsRng);
598
599 make_crs(alpha, beta, x)
600 }
601
602 #[test]
603 fn test_root_crs_is_valid() {
604 let root = CRSElements::root(D);
605 assert!(root.raw.validate().is_ok());
606 }
607
608 #[test]
609 fn test_nontrivial_crs_is_valid() {
610 let crs = non_trivial_crs();
611 assert!(crs.validate().is_ok());
612 }
613
614 #[test]
615 fn test_changing_alpha_makes_crs_invalid() {
616 let mut crs = non_trivial_crs();
617 crs.alpha_1 = G1::generator();
618 assert!(crs.validate().is_err());
619 }
620
621 #[test]
622 fn test_changing_beta_makes_crs_invalid() {
623 let mut crs = non_trivial_crs();
624 crs.beta_1 = G1::generator();
625 assert!(crs.validate().is_err());
626 }
627
628 #[test]
629 fn test_setting_zero_elements_makes_crs_invalid() {
630 let alpha = F::rand(&mut OsRng);
631 let beta = F::rand(&mut OsRng);
632 let x = F::rand(&mut OsRng);
633
634 let crs0 = make_crs(F::zero(), beta, x);
635 assert!(crs0.validate().is_err());
636 let crs1 = make_crs(alpha, F::zero(), x);
637 assert!(crs1.validate().is_err());
638 let crs2 = make_crs(alpha, beta, F::zero());
639 assert!(crs2.validate().is_err());
640 }
641
642 #[test]
643 fn test_bad_powers_of_x_makes_crs_invalid() {
644 let alpha = F::rand(&mut OsRng);
645 let beta = F::rand(&mut OsRng);
646 let x = F::rand(&mut OsRng);
647 let crs = RawCRSElements {
648 alpha_1: G1::generator() * alpha,
649 beta_1: G1::generator() * beta,
650 beta_2: G2::generator() * beta,
651 x_1: vec![
652 G1::generator(),
653 G1::generator() * x,
654 G1::generator() * (x * x),
655 G1::generator() * (x * x),
657 ],
658 x_2: vec![G2::generator(), G2::generator() * x],
659 alpha_x_1: vec![G1::generator() * alpha, G1::generator() * (alpha * x)],
660 beta_x_1: vec![G1::generator() * beta, G1::generator() * (beta * x)],
661 };
662 assert!(crs.validate().is_err());
663 }
664
665 #[test]
666 fn test_contribution_produces_valid_crs() {
667 let root = CRSElements::root(D);
668 let contribution = Contribution::make(
669 &mut OsRng,
670 ContributionHash([0u8; CONTRIBUTION_HASH_SIZE]),
671 &root,
672 );
673 assert!(contribution.new_elements.raw.validate().is_ok());
674 }
675
676 #[test]
677 fn test_contribution_is_linked_to_parent() {
678 let root = CRSElements::root(D);
679 let contribution = Contribution::make(
680 &mut OsRng,
681 ContributionHash([0u8; CONTRIBUTION_HASH_SIZE]),
682 &root,
683 );
684 assert!(contribution.is_linked_to(&root));
685 }
686
687 #[test]
688 fn test_can_calculate_contribution_hash() {
689 let root = CRSElements::root(D);
690 let contribution = Contribution::make(
691 &mut OsRng,
692 ContributionHash([0u8; CONTRIBUTION_HASH_SIZE]),
693 &root,
694 );
695 assert_ne!(contribution.hash(), contribution.parent)
696 }
697
698 #[test]
699 fn test_contribution_is_not_linked_to_itself() {
700 let root = CRSElements::root(D);
701 let contribution = Contribution::make(
702 &mut OsRng,
703 ContributionHash([0u8; CONTRIBUTION_HASH_SIZE]),
704 &root,
705 );
706 assert!(!contribution.is_linked_to(&contribution.new_elements));
707 }
708
709 #[test]
710 fn test_contribution_is_not_linked_if_degree_changes() {
711 let root0 = CRSElements::root(D);
713 let root1 = CRSElements::root(D + 1);
714 let contribution = Contribution::make(
715 &mut OsRng,
716 ContributionHash([0u8; CONTRIBUTION_HASH_SIZE]),
717 &root0,
718 );
719 assert!(!contribution.is_linked_to(&root1));
720 }
721}