Skip to main content

threshold_crypto/
poly.rs

1//! Utilities for distributed key generation: uni- and bivariate polynomials and commitments.
2//!
3//! If `G` is a group of prime order `r` (written additively), and `g` is a generator, then
4//! multiplication by integers factors through `r`, so the map `x -> x * g` (the sum of `x`
5//! copies of `g`) is a homomorphism from the field `Fr` of integers modulo `r` to `G`. If the
6//! _discrete logarithm_ is hard, i.e. it is infeasible to reverse this map, then `x * g` can be
7//! considered a _commitment_ to `x`: By publishing it, you can guarantee to others that you won't
8//! change your mind about the value `x`, without revealing it.
9//!
10//! This concept extends to polynomials: If you have a polynomial `f` over `Fr`, defined as
11//! `a * X * X + b * X + c`, you can publish `a * g`, `b * g` and `c * g`. Then others will be able
12//! to verify any single value `f(x)` of the polynomial without learning the original polynomial,
13//! because `f(x) * g == x * x * (a * g) + x * (b * g) + (c * g)`. Only after learning three (in
14//! general `degree + 1`) values, they can interpolate `f` itself.
15//!
16//! This module defines univariate polynomials (in one variable) and _symmetric_ bivariate
17//! polynomials (in two variables) over a field `Fr`, as well as their _commitments_ in `G`.
18
19use 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/// A univariate polynomial in the prime field.
39#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
40pub struct Poly {
41    /// The coefficients of a polynomial.
42    #[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
60/// A debug statement where the `coeff` vector of prime field elements has been redacted.
61impl 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// Clippy thinks using `+` in a `Sub` implementation is suspicious.
153#[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// Clippy thinks using any `+` and `-` in a `Mul` implementation is suspicious.
172#[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
270/// Creates a new `Poly` instance from a vector of prime field elements representing the
271/// coefficients of the polynomial.
272impl From<Vec<Fr>> for Poly {
273    fn from(coeff: Vec<Fr>) -> Self {
274        Poly { coeff }
275    }
276}
277
278impl Poly {
279    /// Creates a random polynomial.
280    ///
281    /// # Panics
282    ///
283    /// Panics if the `degree` is too large for the coefficients to fit into a `Vec`.
284    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    /// Creates a random polynomial. This constructor is identical to the `Poly::random()`
290    /// constructor in every way except that this constructor will return an `Err` where
291    /// `try_random` would return an error.
292    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    /// Returns the polynomial with constant value `0`.
301    pub fn zero() -> Self {
302        Poly { coeff: vec![] }
303    }
304
305    /// Returns `true` if the polynomial is the constant value `0`.
306    pub fn is_zero(&self) -> bool {
307        self.coeff.iter().all(|coeff| coeff.is_zero())
308    }
309
310    /// Returns the polynomial with constant value `1`.
311    pub fn one() -> Self {
312        Poly::constant(Fr::one())
313    }
314
315    /// Returns the polynomial with constant value `c`.
316    pub fn constant(mut c: Fr) -> Self {
317        // We create a raw pointer to the field element within this method's stack frame so we can
318        // overwrite that portion of memory with zeros once we have copied the element onto the
319        // heap as part of the vector of polynomial coefficients.
320        let poly = Poly::from(vec![c]);
321        clear_fr(&mut c);
322        poly
323    }
324
325    /// Returns the identity function, i.e. the polynomial "`x`".
326    pub fn identity() -> Self {
327        Poly::monomial(1)
328    }
329
330    /// Returns the (monic) monomial: `x.pow(degree)`.
331    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    /// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values
340    /// `(x, f(x))`.
341    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    /// Returns the degree.
353    pub fn degree(&self) -> usize {
354        self.coeff.len().saturating_sub(1)
355    }
356
357    /// Returns the value at the point `i`.
358    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    /// Returns the corresponding commitment.
372    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    /// Removes all trailing zero coefficients.
380    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    /// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values
387    /// `(x, f(x))`.
388    fn compute_interpolation(samples: &[(Fr, Fr)]) -> Self {
389        if samples.is_empty() {
390            return Poly::zero();
391        }
392        // Interpolates on the first `i` samples.
393        let mut poly = Poly::constant(samples[0].1);
394        let mut minus_s0 = samples[0].0;
395        minus_s0.negate();
396        // Is zero on the first `i` samples.
397        let mut base = Poly::from(vec![minus_s0, Fr::one()]);
398
399        // We update `base` so that it is always zero on all previous samples, and `poly` so that
400        // it has the correct values on the previous samples.
401        for (ref x, ref y) in &samples[1..] {
402            // Scale `base` so that its value at `x` is the difference between `y` and `poly`'s
403            // current value at `x`: Adding it to `poly` will then make it correct for `x`.
404            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            // Finally, multiply `base` by X - x, so that it is zero at `x`, too, now.
412            let mut minus_x = *x;
413            minus_x.negate();
414            base *= Poly::from(vec![minus_x, Fr::one()]);
415        }
416        poly
417    }
418
419    /// Generates a non-redacted debug string. This method differs from
420    /// the `Debug` implementation in that it *does* leak the secret prime
421    /// field elements.
422    pub fn reveal(&self) -> String {
423        format!("Poly {{ coeff: {:?} }}", self.coeff)
424    }
425}
426
427/// A commitment to a univariate polynomial.
428#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
429pub struct Commitment {
430    /// The coefficients of the polynomial.
431    #[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    /// Returns the polynomial's degree.
492    pub fn degree(&self) -> usize {
493        self.coeff.len() - 1
494    }
495
496    /// Returns the `i`-th public key share.
497    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    /// Removes all trailing zero coefficients.
511    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/// A symmetric bivariate polynomial in the prime field.
519///
520/// This can be used for Verifiable Secret Sharing and Distributed Key Generation. See the module
521/// documentation for details.
522#[derive(Clone)]
523pub struct BivarPoly {
524    /// The polynomial's degree in each of the two variables.
525    degree: usize,
526    /// The coefficients of the polynomial. Coefficient `(i, j)` for `i <= j` is in position
527    /// `j * (j + 1) / 2 + i`.
528    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
546/// A debug statement where the `coeff` vector has been redacted.
547impl 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    /// Creates a random polynomial.
558    ///
559    /// # Panics
560    ///
561    /// Panics if the degree is too high for the coefficients to fit into a `Vec`.
562    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    /// Creates a random polynomial.
572    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    /// Returns the polynomial's degree; which is the same in both variables.
584    pub fn degree(&self) -> usize {
585        self.degree
586    }
587
588    /// Returns the polynomial's value at the point `(x, y)`.
589    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        // TODO: Can we save a few multiplication steps here due to the symmetry?
593        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    /// Returns the `x`-th row, as a univariate polynomial.
607    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                // TODO: clear these secrets from the stack.
612                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    /// Returns the corresponding commitment. That information can be shared publicly.
626    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    /// Returns the `0`-th to `degree`-th power of `x`.
635    fn powers<T: IntoFr>(&self, x: T) -> Vec<Fr> {
636        powers(x, self.degree)
637    }
638
639    /// Generates a non-redacted debug string. This method differs from the
640    /// `Debug` implementation in that it *does* leak the the struct's
641    /// internal state.
642    pub fn reveal(&self) -> String {
643        format!(
644            "BivarPoly {{ degree: {}, coeff: {:?} }}",
645            self.degree, self.coeff
646        )
647    }
648}
649
650/// A commitment to a symmetric bivariate polynomial.
651#[derive(Debug, Clone, Eq, PartialEq)]
652pub struct BivarCommitment {
653    /// The polynomial's degree in each of the two variables.
654    pub(crate) degree: usize,
655    /// The commitments to the coefficients.
656    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    /// Returns the polynomial's degree: It is the same in both variables.
688    pub fn degree(&self) -> usize {
689        self.degree
690    }
691
692    /// Returns the commitment's value at the point `(x, y)`.
693    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        // TODO: Can we save a few multiplication steps here due to the symmetry?
697        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    /// Returns the `x`-th row, as a commitment to a univariate polynomial.
711    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    /// Returns the `0`-th to `degree`-th power of `x`.
729    fn powers<T: IntoFr>(&self, x: T) -> Vec<Fr> {
730        powers(x, self.degree)
731    }
732}
733
734/// Returns the `0`-th to `degree`-th power of `x`.
735fn 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
746/// Returns the position of coefficient `(i, j)` in the vector describing a symmetric bivariate
747/// polynomial. If `i` or `j` are too large to represent the position as a `usize`, `None` is
748/// returned.
749pub(crate) fn coeff_pos(i: usize, j: usize) -> Option<usize> {
750    // Since the polynomial is symmetric, we can order such that `j >= i`.
751    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        // The polynomial 5 X³ + X - 2.
785        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        // For distributed key generation, a number of dealers, only one of who needs to be honest,
826        // generates random bivariate polynomials and publicly commits to them. In practice, the
827        // dealers can e.g. be any `faulty_num + 1` nodes.
828        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        // Each dealer sends row `m` to node `m`, where the index starts at `1`. Don't send row `0`
836        // to anyone! The nodes verify their rows, and send _value_ `s` on to node `s`. They again
837        // verify the values they received, and collect them.
838        for (bi_poly, bi_commit) in bi_polys.iter().zip(&pub_bi_commits) {
839            for m in 1..=node_num {
840                // Node `m` receives its row and verifies it.
841                let row_poly = bi_poly.row(m);
842                let row_commit = bi_commit.row(m);
843                assert_eq!(row_poly.commitment(), row_commit);
844                // Node `s` receives the `s`-th value and verifies it.
845                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                    // The node can't verify this directly, but it should have the correct value:
850                    assert_eq!(bi_poly.evaluate(m, s), val);
851                }
852
853                // A cheating dealer who modified the polynomial would be detected.
854                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                // If `2 * faulty_num + 1` nodes confirm that they received a valid row, then at
860                // least `faulty_num + 1` honest ones did, and sent the correct values on to node
861                // `s`. So every node received at least `faulty_num + 1` correct entries of their
862                // column/row (remember that the bivariate polynomial is symmetric). They can
863                // reconstruct the full row and in particular value `0` (which no other node knows,
864                // only the dealer). E.g. let's say nodes `1`, `2` and `4` are honest. Then node
865                // `m` received three correct entries from that row:
866                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                // The node sums up all values number `0` it received from the different dealer. No
875                // dealer and no other node knows the sum in the end.
876                sec_keys[m - 1].add_assign(&my_row.evaluate(Fr::zero()));
877            }
878        }
879
880        // Each node now adds up all the first values of the rows it received from the different
881        // dealers (excluding the dealers where fewer than `2 * faulty_num + 1` nodes confirmed).
882        // The whole first column never gets added up in practice, because nobody has all the
883        // information. We do it anyway here; entry `0` is the secret key that is not known to
884        // anyone, neither a dealer, nor a node:
885        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        // The sum of the first rows of the public commitments is the commitment to the secret key
894        // set.
895        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}