Skip to main content

sl_mpc_mate/
math.rs

1// Copyright (c) Silence Laboratories Pte. Ltd. All Rights Reserved.
2// This software is licensed under the Silence Laboratories License Agreement.
3
4use alloc::{vec, vec::Vec};
5use core::{
6    fmt::Debug,
7    hash::{Hash, Hasher},
8    ops::{Deref, DerefMut},
9};
10
11use elliptic_curve::{
12    group::GroupEncoding, CurveArithmetic, Field, Group, NonZeroScalar,
13    PrimeField,
14};
15use rand_core::CryptoRngCore;
16
17use crate::matrix::matrix_inverse;
18
19/// A polynomial with coefficients of type `Scalar`.
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21#[cfg_attr(
22    feature = "serde",
23    serde(bound(
24        serialize = "G::Scalar: serde::Serialize",
25        deserialize = "G::Scalar: serde::de::DeserializeOwned"
26    ))
27)]
28#[derive(PartialEq, Eq)]
29pub struct Polynomial<G>
30where
31    G: Group,
32    G::Scalar: ser::Serializable,
33{
34    coeffs: Vec<G::Scalar>,
35}
36
37impl<G> Hash for Polynomial<G>
38where
39    G: Group,
40    G::Scalar: Hash + ser::Serializable,
41{
42    fn hash<H: Hasher>(&self, state: &mut H) {
43        self.coeffs.hash(state);
44    }
45}
46
47#[cfg(test)]
48impl<G> Debug for Polynomial<G>
49where
50    G: Group,
51    G::Scalar: ser::Serializable,
52{
53    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54        f.debug_struct("Polynomial")
55            .field("len", &self.coeffs.len())
56            .finish()
57    }
58}
59
60impl<G> Polynomial<G>
61where
62    G: Group,
63    G::Scalar: ser::Serializable,
64{
65    /// Create a new polynomial with the given coefficients.
66    pub fn new(coeffs: Vec<G::Scalar>) -> Self {
67        Self { coeffs }
68    }
69
70    /// Create a new polynomial with random coefficients.
71    pub fn random(rng: &mut impl CryptoRngCore, degree: usize) -> Self {
72        Self {
73            coeffs: (0..=degree)
74                .map(|_| G::Scalar::random(&mut *rng))
75                .collect(),
76        }
77    }
78
79    /// Set constant to Scalar::ZERO
80    pub fn reset_contant(&mut self) {
81        self.coeffs[0] = G::Scalar::ZERO;
82    }
83
84    /// Set constant to Scalar::ZERO
85    pub fn reset_constant(&mut self) {
86        self.coeffs[0] = G::Scalar::ZERO;
87    }
88
89    /// Set constant
90    pub fn set_constant(&mut self, scalar: G::Scalar) {
91        self.coeffs[0] = scalar;
92    }
93
94    /// Evaluate the polynomial at 0 (the constant term).
95    pub fn get_constant(&self) -> &G::Scalar {
96        &self.coeffs[0]
97    }
98
99    /// Commit to this polynomial by multiplying each coefficient by the generator.
100    pub fn commit(&self) -> GroupPolynomial<G>
101    where
102        G: GroupEncoding,
103    {
104        GroupPolynomial::new(
105            self.coeffs
106                .iter()
107                .map(|coeff| G::generator() * coeff)
108                .collect(),
109        )
110    }
111
112    /// Computes the n_i derivative of a polynomial with coefficients u_i_k at the point x
113    ///
114    /// `n`: order of the derivative
115    ///
116    /// `x`: point at which to compute the derivative.
117    /// Arithmetic is done modulo the curve order
118    pub fn derivative_at(&self, n: usize, x: &G::Scalar) -> G::Scalar {
119        self.coeffs
120            .iter()
121            .enumerate()
122            .skip(n)
123            .map(|(i, coeff)| {
124                let scalar_num: G::Scalar = factorial_range(i - n, i);
125                let result = x.pow_vartime([(i - n) as u64]);
126
127                scalar_num * coeff * result
128            })
129            .sum()
130    }
131
132    /// Evaluate the polynomial at the given point.
133    /// Arithmetic is done modulo the curve order
134    /// # Arguments
135    /// `x`: point at which to evaluate the polynomial.
136    pub fn evaluate_at(&self, x: &G::Scalar) -> G::Scalar {
137        self.coeffs
138            .iter()
139            .enumerate()
140            .map(|(i, coeff)| {
141                let result = x.pow_vartime([i as u64]);
142                result * coeff
143            })
144            .sum()
145    }
146}
147
148/// A polynomial with coefficients of type `ProjectivePoint`.
149#[derive(Debug, Clone, PartialEq, Eq, Default)]
150pub struct GroupPolynomial<G>
151where
152    G: Group + GroupEncoding,
153{
154    pub coeffs: Vec<G>,
155}
156
157impl<G: Group + GroupEncoding> From<GroupPolynomial<G>> for Vec<G> {
158    fn from(p: GroupPolynomial<G>) -> Vec<G> {
159        p.coeffs
160    }
161}
162
163impl<G> Deref for GroupPolynomial<G>
164where
165    G: Group + GroupEncoding,
166{
167    type Target = [G];
168
169    fn deref(&self) -> &Self::Target {
170        &self.coeffs
171    }
172}
173
174impl<G> DerefMut for GroupPolynomial<G>
175where
176    G: Group + GroupEncoding,
177{
178    fn deref_mut(&mut self) -> &mut Self::Target {
179        &mut self.coeffs
180    }
181}
182
183impl<G> AsRef<[G]> for GroupPolynomial<G>
184where
185    G: Group + GroupEncoding,
186{
187    fn as_ref(&self) -> &[G] {
188        &self.coeffs
189    }
190}
191
192impl<G> GroupPolynomial<G>
193where
194    G: Group + GroupEncoding,
195{
196    /// Create a new polynomial with the given coefficients.
197    pub fn new(coeffs: Vec<G>) -> Self {
198        Self { coeffs }
199    }
200
201    pub fn identity(size: usize) -> Self {
202        Self {
203            coeffs: vec![G::identity(); size],
204        }
205    }
206
207    /// Evaluate the polynomial at 0 (the constant term).
208    pub fn get_constant(&self) -> G {
209        self.coeffs[0]
210    }
211
212    /// Add another polynomial's coefficients element wise to this one inplace.
213    /// If the other polynomial has more coefficients than this one, the extra
214    /// coefficients are ignored.
215    pub fn add_mut<T>(&mut self, other: T)
216    where
217        T: AsRef<[G]>,
218    {
219        self.coeffs
220            .iter_mut()
221            .zip(other.as_ref())
222            .for_each(|(a, b)| {
223                *a += b;
224            });
225    }
226
227    /// Get the coeffs of the polynomial derivative
228    pub fn derivative_coeffs(&self, n: usize) -> impl Iterator<Item = G> + '_
229    where
230        G: Group,
231    {
232        self.coeffs[n..]
233            .iter()
234            .enumerate()
235            .map(move |(position, &u_i)| {
236                u_i * factorial_range::<G::Scalar>(position, position + n)
237            })
238    }
239
240    pub fn points(&self) -> impl Iterator<Item = &'_ G> {
241        self.coeffs.iter()
242    }
243
244    pub fn get(&self, idx: usize) -> Option<&G> {
245        self.coeffs.get(idx)
246    }
247
248    pub fn evaluate_at(&self, x: &G::Scalar) -> G
249    where
250        G: Group,
251    {
252        let init = (G::identity(), G::Scalar::ONE);
253
254        let (p, _) = self.coeffs.iter().fold(init, |(s, x_pow_i), &coeff| {
255            (s + coeff * x_pow_i, x_pow_i * x)
256        });
257
258        p
259    }
260}
261
262impl<G> Deref for Polynomial<G>
263where
264    G: Group,
265    G::Scalar: ser::Serializable,
266{
267    type Target = [G::Scalar];
268
269    fn deref(&self) -> &Self::Target {
270        &self.coeffs
271    }
272}
273
274/// Computes the factorial of a number.
275pub fn factorial<S: PrimeField>(n: usize) -> S {
276    factorial_range(0, n)
277}
278
279const fn small_factorial<const N: usize>() -> [u64; N] {
280    let mut a = [1u64; N];
281
282    let mut j = 1;
283
284    while j < N {
285        a[j] = j as u64 * a[j - 1];
286        j += 1;
287    }
288
289    a
290}
291
292// FACT[20] == 20! and fits into u64
293static FACT: [u64; 21] = small_factorial();
294
295/// Computes the factorial of a range of numbers (start, end]
296pub fn factorial_range<S: PrimeField>(start: usize, end: usize) -> S {
297    debug_assert!(start <= end);
298
299    if end < FACT.len() {
300        return S::from(FACT[end] / FACT[start]);
301    }
302
303    (start + 1..=end).fold(S::ONE, |acc, x| acc * S::from(x as u64))
304}
305
306/// Feldman verification
307pub fn feldman_verify<C: CurveArithmetic>(
308    u_i_k: impl Iterator<Item = C::ProjectivePoint>,
309    x_i: &NonZeroScalar<C>,
310    f_i_value: &C::Scalar,
311    g: &C::ProjectivePoint,
312) -> bool {
313    let x_i = x_i as &C::Scalar;
314    let one = C::Scalar::ONE;
315    let s = C::ProjectivePoint::identity();
316
317    // sum( coeff_i * (x_i^i mod p) )
318    let (point, _) = u_i_k
319        .fold((s, one), |(sum, val), coeff| (sum + coeff * val, val * x_i));
320
321    if point.is_identity().into() {
322        return false;
323    }
324
325    let expected_point = *g * f_i_value;
326
327    point == expected_point
328}
329
330pub fn polynomial_coeff_multipliers_iter<C>(
331    x_i: &NonZeroScalar<C>,
332    n_i: usize,
333    n: usize,
334) -> impl Iterator<Item = C::Scalar> + '_
335where
336    C: CurveArithmetic,
337{
338    (0..n).map(move |idx| {
339        if idx < n_i {
340            C::Scalar::ZERO
341        } else {
342            let num: C::Scalar = factorial_range(idx - n_i, idx);
343            let exponent = [(idx - n_i) as u64];
344            let result = x_i.pow_vartime(exponent);
345
346            num * result
347        }
348    })
349}
350
351/// Get the multipliers for the coefficients of the polynomial,
352/// given the `x_i` (point of evaluation),
353/// `n_i` (order of derivative)
354/// `n` (degree of polynomial - 1)
355pub fn polynomial_coeff_multipliers<C>(
356    x_i: &NonZeroScalar<C>,
357    n_i: usize,
358    n: usize,
359) -> Vec<C::Scalar>
360where
361    C: CurveArithmetic,
362{
363    polynomial_coeff_multipliers_iter(x_i, n_i, n).collect()
364}
365
366/// Get the birkhoff coefficients
367pub fn birkhoff_coeffs<C>(
368    params: &[(NonZeroScalar<C>, usize)],
369) -> Vec<C::Scalar>
370where
371    C: CurveArithmetic,
372{
373    let n = params.len();
374
375    let matrix: Vec<Vec<C::Scalar>> = params
376        .iter()
377        .map(|(x_i, n_i)| polynomial_coeff_multipliers(x_i, *n_i, n))
378        .collect();
379
380    matrix_inverse::<C>(matrix, n).swap_remove(0)
381}
382
383#[cfg(not(feature = "serde"))]
384mod ser {
385    pub trait Serializable {}
386    impl<T> Serializable for T {}
387}
388
389#[cfg(feature = "serde")]
390mod ser {
391    use super::*;
392
393    pub trait Serializable:
394        serde::Serialize + serde::de::DeserializeOwned
395    {
396    }
397
398    impl<T: serde::Serialize + serde::de::DeserializeOwned> Serializable for T {}
399
400    impl<G> serde::Serialize for GroupPolynomial<G>
401    where
402        G: Group + GroupEncoding,
403    {
404        fn serialize<S>(
405            &self,
406            serializer: S,
407        ) -> core::result::Result<S::Ok, S::Error>
408        where
409            S: serde::ser::Serializer,
410        {
411            use serde::ser::SerializeSeq;
412
413            let mut seq =
414                serializer.serialize_seq(Some(self.coeffs.len()))?;
415            for coeff in &self.coeffs {
416                seq.serialize_element(&coeff.to_bytes().as_ref())?;
417            }
418            seq.end()
419        }
420    }
421
422    impl<'de, G> serde::Deserialize<'de> for GroupPolynomial<G>
423    where
424        G: Group + GroupEncoding,
425    {
426        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
427        where
428            D: serde::de::Deserializer<'de>,
429        {
430            let data: Vec<Vec<u8>> =
431                serde::Deserialize::deserialize(deserializer)?;
432            let mut coeffs = Vec::with_capacity(data.len());
433
434            for coeff_data in &data {
435                let repr = G::Repr::default();
436                if coeff_data.len() != repr.as_ref().len() {
437                    return Err(serde::de::Error::custom(
438                        "Invalid group element",
439                    ));
440                }
441                let mut repr = repr;
442                repr.as_mut().copy_from_slice(coeff_data);
443                let opt = G::from_bytes(&repr);
444
445                let point = if opt.is_some().into() {
446                    opt.unwrap()
447                } else {
448                    return Err(serde::de::Error::custom(
449                        "Invalid group element",
450                    ));
451                };
452                coeffs.push(point);
453            }
454
455            Ok(Self { coeffs })
456        }
457    }
458}
459
460#[cfg(test)]
461mod tests {
462    use elliptic_curve::{scalar::FromUintUnchecked, Curve};
463    use k256::{ProjectivePoint, Scalar, Secp256k1};
464
465    use super::*;
466
467    #[test]
468    #[cfg(feature = "serde")]
469    fn test_serde() {
470        use super::*;
471        use k256::ProjectivePoint;
472
473        let mut rng = rand::thread_rng();
474        let poly1 = Polynomial::<ProjectivePoint>::random(&mut rng, 5);
475        let mut bytes = Vec::new();
476        ciborium::into_writer(&poly1, &mut bytes).unwrap();
477        let poly2: Polynomial<ProjectivePoint> =
478            ciborium::from_reader(bytes.as_slice()).unwrap();
479
480        let g_poly1 = poly1.commit();
481
482        let mut bytes = Vec::new();
483        ciborium::into_writer(&g_poly1, &mut bytes).unwrap();
484
485        let g_poly2: GroupPolynomial<ProjectivePoint> =
486            ciborium::from_reader(bytes.as_slice()).unwrap();
487
488        assert_eq!(poly1, poly2);
489        assert_eq!(g_poly1, g_poly2);
490    }
491
492    #[test]
493    fn fact() {
494        // static FACT: [u64; 21] = small_factorial();
495
496        assert_eq!(FACT[19], 121645100408832000);
497        assert_eq!(FACT[20], 2432902008176640000); // biggest number fitting into u64
498    }
499
500    #[test]
501    fn test_derivative_large() {
502        // order of the curve
503        let order = Secp256k1::ORDER;
504        // f(x) = 1 + 2x + (p-1)x^2
505        // p is the curve order
506        let u_i_k = vec![
507            Scalar::from(1_u64),
508            Scalar::from(2_u64),
509            Scalar::from_uint_unchecked(order.wrapping_sub(&1u64.into())),
510        ];
511
512        // f'(x) = 2 + 2(p-1)x
513        // f'(2) = (4p-2) mod p => p - 2
514        let poly = Polynomial::<ProjectivePoint>::new(u_i_k);
515        let n = 1;
516
517        let result = poly.derivative_at(n, &Scalar::from(2_u64));
518
519        assert_eq!(
520            result,
521            Scalar::from_uint_unchecked(order.wrapping_sub(&2u64.into()))
522        );
523    }
524
525    #[test]
526    fn test_derivative_normal() {
527        // f(x) = 1 + 2x + 3x^2 + 4x^3
528        let u_i_k = vec![
529            Scalar::from(1_u64),
530            Scalar::from(2_u64),
531            Scalar::from(3_u64),
532            Scalar::from(4_u64),
533        ];
534
535        let poly = Polynomial::<ProjectivePoint>::new(u_i_k);
536
537        // f''(x) = 6 + 24x
538        let n = 2;
539        // f''(2) = 6 + 24(2) = 54
540        let result = poly.derivative_at(n, &Scalar::from(2_u64));
541
542        assert_eq!(result, Scalar::from(54_u64));
543    }
544
545    #[test]
546    fn test_derivative_coeffs() {
547        // f(x) = 1 + 2x + 3x^2 + 4x^3
548        let g = ProjectivePoint::GENERATOR;
549        let u_i_k = vec![
550            (g * Scalar::from(1_u64)),
551            (g * Scalar::from(2_u64)),
552            (g * Scalar::from(3_u64)),
553            (g * Scalar::from(4_u64)),
554        ];
555
556        let poly = GroupPolynomial::<ProjectivePoint>::new(u_i_k);
557
558        // f''(x) = 6 + 24x
559        let n = 2;
560        let coeffs = poly.derivative_coeffs(n).collect::<Vec<_>>();
561
562        assert_eq!(coeffs.len(), 2);
563        assert_eq!(coeffs[0], g * Scalar::from(6_u64));
564        assert_eq!(coeffs[1], g * Scalar::from(24_u64));
565
566        // f'(x) = 2 + 6x + 12x^2
567        let coeffs = poly.derivative_coeffs(1).collect::<Vec<_>>();
568
569        assert_eq!(coeffs.len(), 3);
570        assert_eq!(coeffs[0], g * Scalar::from(2_u64));
571        assert_eq!(coeffs[1], g * Scalar::from(6_u64));
572        assert_eq!(coeffs[2], g * Scalar::from(12_u64));
573    }
574}