1use std::borrow::Borrow;
20use std::cmp::Ordering;
21use std::fmt::{self, Debug, Formatter};
22use std::hash::{Hash, Hasher};
23use std::iter::repeat_with;
24use std::{cmp, iter, ops};
25
26use ff::Field;
27use group::{CurveAffine, CurveProjective};
28use rand::Rng;
29use serde::{Deserialize, Serialize};
30use zeroize::Zeroize;
31
32use crate::cmp_pairing::cmp_projective;
33use crate::error::{Error, Result};
34use crate::into_fr::IntoFr;
35use crate::secret::clear_fr;
36use crate::{Fr, G1Affine, G1};
37
38#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
40pub struct Poly {
41 #[serde(with = "super::serde_impl::field_vec")]
43 pub(super) coeff: Vec<Fr>,
44}
45
46impl Zeroize for Poly {
47 fn zeroize(&mut self) {
48 for fr in self.coeff.iter_mut() {
49 clear_fr(fr)
50 }
51 }
52}
53
54impl Drop for Poly {
55 fn drop(&mut self) {
56 self.zeroize();
57 }
58}
59
60impl Debug for Poly {
62 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
63 f.debug_struct("Poly").field("coeff", &"...").finish()
64 }
65}
66
67#[allow(clippy::suspicious_op_assign_impl)]
68impl<B: Borrow<Poly>> ops::AddAssign<B> for Poly {
69 fn add_assign(&mut self, rhs: B) {
70 let len = self.coeff.len();
71 let rhs_len = rhs.borrow().coeff.len();
72 if rhs_len > len {
73 self.coeff.resize(rhs_len, Fr::zero());
74 }
75 for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) {
76 Field::add_assign(self_c, rhs_c);
77 }
78 self.remove_zeros();
79 }
80}
81
82impl<'a, B: Borrow<Poly>> ops::Add<B> for &'a Poly {
83 type Output = Poly;
84
85 fn add(self, rhs: B) -> Poly {
86 (*self).clone() + rhs
87 }
88}
89
90impl<B: Borrow<Poly>> ops::Add<B> for Poly {
91 type Output = Poly;
92
93 fn add(mut self, rhs: B) -> Poly {
94 self += rhs;
95 self
96 }
97}
98
99impl<'a> ops::Add<Fr> for Poly {
100 type Output = Poly;
101
102 fn add(mut self, rhs: Fr) -> Self::Output {
103 if self.is_zero() && !rhs.is_zero() {
104 self.coeff.push(rhs);
105 } else {
106 self.coeff[0].add_assign(&rhs);
107 self.remove_zeros();
108 }
109 self
110 }
111}
112
113impl<'a> ops::Add<u64> for Poly {
114 type Output = Poly;
115
116 fn add(self, rhs: u64) -> Self::Output {
117 self + rhs.into_fr()
118 }
119}
120
121impl<B: Borrow<Poly>> ops::SubAssign<B> for Poly {
122 fn sub_assign(&mut self, rhs: B) {
123 let len = self.coeff.len();
124 let rhs_len = rhs.borrow().coeff.len();
125 if rhs_len > len {
126 self.coeff.resize(rhs_len, Fr::zero());
127 }
128 for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) {
129 Field::sub_assign(self_c, rhs_c);
130 }
131 self.remove_zeros();
132 }
133}
134
135impl<'a, B: Borrow<Poly>> ops::Sub<B> for &'a Poly {
136 type Output = Poly;
137
138 fn sub(self, rhs: B) -> Poly {
139 (*self).clone() - rhs
140 }
141}
142
143impl<B: Borrow<Poly>> ops::Sub<B> for Poly {
144 type Output = Poly;
145
146 fn sub(mut self, rhs: B) -> Poly {
147 self -= rhs;
148 self
149 }
150}
151
152#[allow(clippy::suspicious_arithmetic_impl)]
154impl<'a> ops::Sub<Fr> for Poly {
155 type Output = Poly;
156
157 fn sub(self, mut rhs: Fr) -> Self::Output {
158 rhs.negate();
159 self + rhs
160 }
161}
162
163impl<'a> ops::Sub<u64> for Poly {
164 type Output = Poly;
165
166 fn sub(self, rhs: u64) -> Self::Output {
167 self - rhs.into_fr()
168 }
169}
170
171#[allow(clippy::suspicious_arithmetic_impl)]
173impl<'a, B: Borrow<Poly>> ops::Mul<B> for &'a Poly {
174 type Output = Poly;
175
176 fn mul(self, rhs: B) -> Self::Output {
177 let rhs = rhs.borrow();
178 if rhs.is_zero() || self.is_zero() {
179 return Poly::zero();
180 }
181 let n_coeffs = self.coeff.len() + rhs.coeff.len() - 1;
182 let mut coeffs = vec![Fr::zero(); n_coeffs];
183 let mut tmp = Fr::zero();
184 for (i, ca) in self.coeff.iter().enumerate() {
185 for (j, cb) in rhs.coeff.iter().enumerate() {
186 tmp = *ca;
187 tmp.mul_assign(cb);
188 coeffs[i + j].add_assign(&tmp);
189 }
190 }
191 clear_fr(&mut tmp);
192 Poly::from(coeffs)
193 }
194}
195
196impl<B: Borrow<Poly>> ops::Mul<B> for Poly {
197 type Output = Poly;
198
199 fn mul(self, rhs: B) -> Self::Output {
200 &self * rhs
201 }
202}
203
204impl<B: Borrow<Self>> ops::MulAssign<B> for Poly {
205 fn mul_assign(&mut self, rhs: B) {
206 *self = &*self * rhs;
207 }
208}
209
210impl ops::MulAssign<Fr> for Poly {
211 fn mul_assign(&mut self, rhs: Fr) {
212 if rhs.is_zero() {
213 self.zeroize();
214 self.coeff.clear();
215 } else {
216 for c in &mut self.coeff {
217 Field::mul_assign(c, &rhs);
218 }
219 }
220 }
221}
222
223impl<'a> ops::Mul<&'a Fr> for Poly {
224 type Output = Poly;
225
226 fn mul(mut self, rhs: &Fr) -> Self::Output {
227 if rhs.is_zero() {
228 self.zeroize();
229 self.coeff.clear();
230 } else {
231 self.coeff.iter_mut().for_each(|c| c.mul_assign(rhs));
232 }
233 self
234 }
235}
236
237impl ops::Mul<Fr> for Poly {
238 type Output = Poly;
239
240 fn mul(self, rhs: Fr) -> Self::Output {
241 let rhs = &rhs;
242 self * rhs
243 }
244}
245
246impl<'a> ops::Mul<&'a Fr> for &'a Poly {
247 type Output = Poly;
248
249 fn mul(self, rhs: &Fr) -> Self::Output {
250 (*self).clone() * rhs
251 }
252}
253
254impl<'a> ops::Mul<Fr> for &'a Poly {
255 type Output = Poly;
256
257 fn mul(self, rhs: Fr) -> Self::Output {
258 (*self).clone() * rhs
259 }
260}
261
262impl ops::Mul<u64> for Poly {
263 type Output = Poly;
264
265 fn mul(self, rhs: u64) -> Self::Output {
266 self * rhs.into_fr()
267 }
268}
269
270impl From<Vec<Fr>> for Poly {
273 fn from(coeff: Vec<Fr>) -> Self {
274 Poly { coeff }
275 }
276}
277
278impl Poly {
279 pub fn random<R: Rng>(degree: usize, rng: &mut R) -> Self {
285 Poly::try_random(degree, rng)
286 .unwrap_or_else(|e| panic!("Failed to create random `Poly`: {}", e))
287 }
288
289 pub fn try_random<R: Rng>(degree: usize, rng: &mut R) -> Result<Self> {
293 if degree == usize::max_value() {
294 return Err(Error::DegreeTooHigh);
295 }
296 let coeff: Vec<Fr> = repeat_with(|| Fr::random(rng)).take(degree + 1).collect();
297 Ok(Poly::from(coeff))
298 }
299
300 pub fn zero() -> Self {
302 Poly { coeff: vec![] }
303 }
304
305 pub fn is_zero(&self) -> bool {
307 self.coeff.iter().all(|coeff| coeff.is_zero())
308 }
309
310 pub fn one() -> Self {
312 Poly::constant(Fr::one())
313 }
314
315 pub fn constant(mut c: Fr) -> Self {
317 let poly = Poly::from(vec![c]);
321 clear_fr(&mut c);
322 poly
323 }
324
325 pub fn identity() -> Self {
327 Poly::monomial(1)
328 }
329
330 pub fn monomial(degree: usize) -> Self {
332 let coeff: Vec<Fr> = iter::repeat(Fr::zero())
333 .take(degree)
334 .chain(iter::once(Fr::one()))
335 .collect();
336 Poly::from(coeff)
337 }
338
339 pub fn interpolate<T, U, I>(samples_repr: I) -> Self
342 where
343 I: IntoIterator<Item = (T, U)>,
344 T: IntoFr,
345 U: IntoFr,
346 {
347 let convert = |(x, y): (T, U)| (x.into_fr(), y.into_fr());
348 let samples: Vec<(Fr, Fr)> = samples_repr.into_iter().map(convert).collect();
349 Poly::compute_interpolation(&samples)
350 }
351
352 pub fn degree(&self) -> usize {
354 self.coeff.len().saturating_sub(1)
355 }
356
357 pub fn evaluate<T: IntoFr>(&self, i: T) -> Fr {
359 let mut result = match self.coeff.last() {
360 None => return Fr::zero(),
361 Some(c) => *c,
362 };
363 let x = i.into_fr();
364 for c in self.coeff.iter().rev().skip(1) {
365 result.mul_assign(&x);
366 result.add_assign(c);
367 }
368 result
369 }
370
371 pub fn commitment(&self) -> Commitment {
373 let to_g1 = |c: &Fr| G1Affine::one().mul(*c);
374 Commitment {
375 coeff: self.coeff.iter().map(to_g1).collect(),
376 }
377 }
378
379 fn remove_zeros(&mut self) {
381 let zeros = self.coeff.iter().rev().take_while(|c| c.is_zero()).count();
382 let len = self.coeff.len() - zeros;
383 self.coeff.truncate(len);
384 }
385
386 fn compute_interpolation(samples: &[(Fr, Fr)]) -> Self {
389 if samples.is_empty() {
390 return Poly::zero();
391 }
392 let mut poly = Poly::constant(samples[0].1);
394 let mut minus_s0 = samples[0].0;
395 minus_s0.negate();
396 let mut base = Poly::from(vec![minus_s0, Fr::one()]);
398
399 for (ref x, ref y) in &samples[1..] {
402 let mut diff = *y;
405 diff.sub_assign(&poly.evaluate(x));
406 let base_val = base.evaluate(x);
407 diff.mul_assign(&base_val.inverse().expect("sample points must be distinct"));
408 base *= diff;
409 poly += &base;
410
411 let mut minus_x = *x;
413 minus_x.negate();
414 base *= Poly::from(vec![minus_x, Fr::one()]);
415 }
416 poly
417 }
418
419 pub fn reveal(&self) -> String {
423 format!("Poly {{ coeff: {:?} }}", self.coeff)
424 }
425}
426
427#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
429pub struct Commitment {
430 #[serde(with = "super::serde_impl::projective_vec")]
432 pub(super) coeff: Vec<G1>,
433}
434
435impl PartialOrd for Commitment {
436 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
437 Some(self.cmp(&other))
438 }
439}
440
441impl Ord for Commitment {
442 fn cmp(&self, other: &Self) -> Ordering {
443 self.coeff.len().cmp(&other.coeff.len()).then_with(|| {
444 self.coeff
445 .iter()
446 .zip(&other.coeff)
447 .find(|(x, y)| x != y)
448 .map_or(Ordering::Equal, |(x, y)| cmp_projective(x, y))
449 })
450 }
451}
452
453impl Hash for Commitment {
454 fn hash<H: Hasher>(&self, state: &mut H) {
455 self.coeff.len().hash(state);
456 for c in &self.coeff {
457 c.into_affine().into_compressed().as_ref().hash(state);
458 }
459 }
460}
461
462impl<B: Borrow<Commitment>> ops::AddAssign<B> for Commitment {
463 fn add_assign(&mut self, rhs: B) {
464 let len = cmp::max(self.coeff.len(), rhs.borrow().coeff.len());
465 self.coeff.resize(len, G1::zero());
466 for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) {
467 self_c.add_assign(rhs_c);
468 }
469 self.remove_zeros();
470 }
471}
472
473impl<'a, B: Borrow<Commitment>> ops::Add<B> for &'a Commitment {
474 type Output = Commitment;
475
476 fn add(self, rhs: B) -> Commitment {
477 (*self).clone() + rhs
478 }
479}
480
481impl<B: Borrow<Commitment>> ops::Add<B> for Commitment {
482 type Output = Commitment;
483
484 fn add(mut self, rhs: B) -> Commitment {
485 self += rhs;
486 self
487 }
488}
489
490impl Commitment {
491 pub fn degree(&self) -> usize {
493 self.coeff.len() - 1
494 }
495
496 pub fn evaluate<T: IntoFr>(&self, i: T) -> G1 {
498 let mut result = match self.coeff.last() {
499 None => return G1::zero(),
500 Some(c) => *c,
501 };
502 let x = i.into_fr();
503 for c in self.coeff.iter().rev().skip(1) {
504 result.mul_assign(x);
505 result.add_assign(c);
506 }
507 result
508 }
509
510 fn remove_zeros(&mut self) {
512 let zeros = self.coeff.iter().rev().take_while(|c| c.is_zero()).count();
513 let len = self.coeff.len() - zeros;
514 self.coeff.truncate(len)
515 }
516}
517
518#[derive(Clone)]
523pub struct BivarPoly {
524 degree: usize,
526 coeff: Vec<Fr>,
529}
530
531impl Zeroize for BivarPoly {
532 fn zeroize(&mut self) {
533 for fr in self.coeff.iter_mut() {
534 clear_fr(fr)
535 }
536 self.degree.zeroize();
537 }
538}
539
540impl Drop for BivarPoly {
541 fn drop(&mut self) {
542 self.zeroize();
543 }
544}
545
546impl Debug for BivarPoly {
548 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
549 f.debug_struct("BivarPoly")
550 .field("degree", &self.degree)
551 .field("coeff", &"...")
552 .finish()
553 }
554}
555
556impl BivarPoly {
557 pub fn random<R: Rng>(degree: usize, rng: &mut R) -> Self {
563 BivarPoly::try_random(degree, rng).unwrap_or_else(|e| {
564 panic!(
565 "Failed to create random `BivarPoly` of degree {}: {}",
566 degree, e
567 )
568 })
569 }
570
571 pub fn try_random<R: Rng>(degree: usize, rng: &mut R) -> Result<Self> {
573 let len = coeff_pos(degree, degree)
574 .and_then(|l| l.checked_add(1))
575 .ok_or(Error::DegreeTooHigh)?;
576 let poly = BivarPoly {
577 degree,
578 coeff: repeat_with(|| Fr::random(rng)).take(len).collect(),
579 };
580 Ok(poly)
581 }
582
583 pub fn degree(&self) -> usize {
585 self.degree
586 }
587
588 pub fn evaluate<T: IntoFr>(&self, x: T, y: T) -> Fr {
590 let x_pow = self.powers(x);
591 let y_pow = self.powers(y);
592 let mut result = Fr::zero();
594 for (i, x_pow_i) in x_pow.into_iter().enumerate() {
595 for (j, y_pow_j) in y_pow.iter().enumerate() {
596 let index = coeff_pos(i, j).expect("polynomial degree too high");
597 let mut summand = self.coeff[index];
598 summand.mul_assign(&x_pow_i);
599 summand.mul_assign(y_pow_j);
600 result.add_assign(&summand);
601 }
602 }
603 result
604 }
605
606 pub fn row<T: IntoFr>(&self, x: T) -> Poly {
608 let x_pow = self.powers(x);
609 let coeff: Vec<Fr> = (0..=self.degree)
610 .map(|i| {
611 let mut result = Fr::zero();
613 for (j, x_pow_j) in x_pow.iter().enumerate() {
614 let index = coeff_pos(i, j).expect("polynomial degree too high");
615 let mut summand = self.coeff[index];
616 summand.mul_assign(x_pow_j);
617 result.add_assign(&summand);
618 }
619 result
620 })
621 .collect();
622 Poly::from(coeff)
623 }
624
625 pub fn commitment(&self) -> BivarCommitment {
627 let to_pub = |c: &Fr| G1Affine::one().mul(*c);
628 BivarCommitment {
629 degree: self.degree,
630 coeff: self.coeff.iter().map(to_pub).collect(),
631 }
632 }
633
634 fn powers<T: IntoFr>(&self, x: T) -> Vec<Fr> {
636 powers(x, self.degree)
637 }
638
639 pub fn reveal(&self) -> String {
643 format!(
644 "BivarPoly {{ degree: {}, coeff: {:?} }}",
645 self.degree, self.coeff
646 )
647 }
648}
649
650#[derive(Debug, Clone, Eq, PartialEq)]
652pub struct BivarCommitment {
653 pub(crate) degree: usize,
655 pub(crate) coeff: Vec<G1>,
657}
658
659impl Hash for BivarCommitment {
660 fn hash<H: Hasher>(&self, state: &mut H) {
661 self.degree.hash(state);
662 for c in &self.coeff {
663 c.into_affine().into_compressed().as_ref().hash(state);
664 }
665 }
666}
667
668impl PartialOrd for BivarCommitment {
669 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
670 Some(self.cmp(&other))
671 }
672}
673
674impl Ord for BivarCommitment {
675 fn cmp(&self, other: &Self) -> Ordering {
676 self.degree.cmp(&other.degree).then_with(|| {
677 self.coeff
678 .iter()
679 .zip(&other.coeff)
680 .find(|(x, y)| x != y)
681 .map_or(Ordering::Equal, |(x, y)| cmp_projective(x, y))
682 })
683 }
684}
685
686impl BivarCommitment {
687 pub fn degree(&self) -> usize {
689 self.degree
690 }
691
692 pub fn evaluate<T: IntoFr>(&self, x: T, y: T) -> G1 {
694 let x_pow = self.powers(x);
695 let y_pow = self.powers(y);
696 let mut result = G1::zero();
698 for (i, x_pow_i) in x_pow.into_iter().enumerate() {
699 for (j, y_pow_j) in y_pow.iter().enumerate() {
700 let index = coeff_pos(i, j).expect("polynomial degree too high");
701 let mut summand = self.coeff[index];
702 summand.mul_assign(x_pow_i);
703 summand.mul_assign(*y_pow_j);
704 result.add_assign(&summand);
705 }
706 }
707 result
708 }
709
710 pub fn row<T: IntoFr>(&self, x: T) -> Commitment {
712 let x_pow = self.powers(x);
713 let coeff: Vec<G1> = (0..=self.degree)
714 .map(|i| {
715 let mut result = G1::zero();
716 for (j, x_pow_j) in x_pow.iter().enumerate() {
717 let index = coeff_pos(i, j).expect("polynomial degree too high");
718 let mut summand = self.coeff[index];
719 summand.mul_assign(*x_pow_j);
720 result.add_assign(&summand);
721 }
722 result
723 })
724 .collect();
725 Commitment { coeff }
726 }
727
728 fn powers<T: IntoFr>(&self, x: T) -> Vec<Fr> {
730 powers(x, self.degree)
731 }
732}
733
734fn powers<T: IntoFr>(into_x: T, degree: usize) -> Vec<Fr> {
736 let x = into_x.into_fr();
737 let mut x_pow_i = Fr::one();
738 iter::once(x_pow_i)
739 .chain((0..degree).map(|_| {
740 x_pow_i.mul_assign(&x);
741 x_pow_i
742 }))
743 .collect()
744}
745
746pub(crate) fn coeff_pos(i: usize, j: usize) -> Option<usize> {
750 let (j, i) = if j >= i { (j, i) } else { (i, j) };
752 i.checked_add(j.checked_mul(j.checked_add(1)?)? / 2)
753}
754
755#[cfg(test)]
756mod tests {
757 use std::collections::BTreeMap;
758
759 use super::{coeff_pos, BivarPoly, IntoFr, Poly};
760 use super::{Fr, G1Affine, G1};
761 use ff::Field;
762 use group::{CurveAffine, CurveProjective};
763 use zeroize::Zeroize;
764
765 #[test]
766 fn test_coeff_pos() {
767 let mut i = 0;
768 let mut j = 0;
769 for n in 0..100 {
770 assert_eq!(Some(n), coeff_pos(i, j));
771 if i >= j {
772 j += 1;
773 i = 0;
774 } else {
775 i += 1;
776 }
777 }
778 let too_large = 1 << (0usize.count_zeros() / 2);
779 assert_eq!(None, coeff_pos(0, too_large));
780 }
781
782 #[test]
783 fn poly() {
784 let x_pow_3 = Poly::monomial(3);
786 let x_pow_1 = Poly::monomial(1);
787 let poly = x_pow_3 * 5 + x_pow_1 - 2;
788
789 let coeff: Vec<_> = [-2, 1, 0, 5].iter().map(IntoFr::into_fr).collect();
790 assert_eq!(Poly { coeff }, poly);
791 let samples = vec![(-1, -8), (2, 40), (3, 136), (5, 628)];
792 for &(x, y) in &samples {
793 assert_eq!(y.into_fr(), poly.evaluate(x));
794 }
795 let interp = Poly::interpolate(samples);
796 assert_eq!(interp, poly);
797 }
798
799 #[test]
800 fn test_zeroize() {
801 let mut poly = Poly::monomial(3) + Poly::monomial(2) - 1;
802 poly.zeroize();
803 assert!(poly.is_zero());
804
805 let mut bi_poly = BivarPoly::random(3, &mut rand::thread_rng());
806 let random_commitment = bi_poly.commitment();
807
808 bi_poly.zeroize();
809
810 let zero_commitment = bi_poly.commitment();
811 assert_ne!(random_commitment, zero_commitment);
812
813 let mut rng = rand::thread_rng();
814 let (x, y): (Fr, Fr) = (Fr::random(&mut rng), Fr::random(&mut rng));
815 assert_eq!(zero_commitment.evaluate(x, y), G1::zero());
816 }
817
818 #[test]
819 fn distributed_key_generation() {
820 let mut rng = rand::thread_rng();
821 let dealer_num = 3;
822 let node_num = 5;
823 let faulty_num = 2;
824
825 let bi_polys: Vec<BivarPoly> = (0..dealer_num)
829 .map(|_| BivarPoly::random(faulty_num, &mut rng))
830 .collect();
831 let pub_bi_commits: Vec<_> = bi_polys.iter().map(BivarPoly::commitment).collect();
832
833 let mut sec_keys = vec![Fr::zero(); node_num];
834
835 for (bi_poly, bi_commit) in bi_polys.iter().zip(&pub_bi_commits) {
839 for m in 1..=node_num {
840 let row_poly = bi_poly.row(m);
842 let row_commit = bi_commit.row(m);
843 assert_eq!(row_poly.commitment(), row_commit);
844 for s in 1..=node_num {
846 let val = row_poly.evaluate(s);
847 let val_g1 = G1Affine::one().mul(val);
848 assert_eq!(bi_commit.evaluate(m, s), val_g1);
849 assert_eq!(bi_poly.evaluate(m, s), val);
851 }
852
853 let x_pow_2 = Poly::monomial(2);
855 let five = Poly::constant(5.into_fr());
856 let wrong_poly = row_poly.clone() + x_pow_2 * five;
857 assert_ne!(wrong_poly.commitment(), row_commit);
858
859 let received: BTreeMap<_, _> = [1, 2, 4]
867 .iter()
868 .map(|&i| (i, bi_poly.evaluate(m, i)))
869 .collect();
870 let my_row = Poly::interpolate(received);
871 assert_eq!(bi_poly.evaluate(m, 0), my_row.evaluate(0));
872 assert_eq!(row_poly, my_row);
873
874 sec_keys[m - 1].add_assign(&my_row.evaluate(Fr::zero()));
877 }
878 }
879
880 let mut sec_key_set = Poly::zero();
886 for bi_poly in &bi_polys {
887 sec_key_set += bi_poly.row(0);
888 }
889 for m in 1..=node_num {
890 assert_eq!(sec_key_set.evaluate(m), sec_keys[m - 1]);
891 }
892
893 let mut sum_commit = Poly::zero().commitment();
896 for bi_commit in &pub_bi_commits {
897 sum_commit += bi_commit.row(0);
898 }
899 assert_eq!(sum_commit, sec_key_set.commitment());
900 }
901}