Skip to main content

ekzg_multi_open/
commit_key.rs

1use bls12_381::{lincomb::g1_lincomb, G1Point, G1Projective, Scalar};
2
3/// The key that is used to commit to polynomials in monomial form
4///
5/// This contains group elements of the form `{ \tau^i G }`
6///  Where:
7/// - `i` ranges from 0 to `degree`.
8/// - `G` is some generator of the group
9#[derive(Debug, Clone)]
10pub struct CommitKey {
11    /// A list of G1 group elements of the form $\tau^i \cdot G$,
12    /// used to commit to polynomial coefficients.
13    ///
14    /// The length of this vector determines the maximum degree polynomial
15    /// that can be safely committed using this key.
16    pub g1s: Vec<G1Point>,
17}
18
19impl CommitKey {
20    /// Constructs a new `CommitKey` from a list of G1 group elements.
21    ///
22    /// # Arguments
23    /// - `g1s`: A non-empty vector of G1 elements representing powers of the trapdoor $\tau$,
24    ///   i.e., [ \tau^0 \cdot G, \tau^1 \cdot G, \dots ].
25    ///
26    /// # Panics
27    /// Panics if `g1s` is empty.
28    pub fn new(g1s: Vec<G1Point>) -> Self {
29        assert!(
30            !g1s.is_empty(),
31            "cannot initialize `CommitKey` with no g1 points"
32        );
33
34        Self { g1s }
35    }
36
37    /// Commit to `polynomial` in monomial form using the G1 group elements
38    pub fn commit_g1(&self, poly_coeff: &[Scalar]) -> G1Projective {
39        // Note: We could use g1_lincomb_unsafe here, because we know that none of the points are the
40        // identity element.
41        // We use g1_lincomb because it is safer and the performance difference is negligible
42        g1_lincomb(&self.g1s[0..poly_coeff.len()], poly_coeff)
43            .expect("number of g1 points is equal to the number of coefficients in the polynomial")
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use bls12_381::{traits::*, G1Projective, Scalar};
50    use rand::{rngs::StdRng, SeedableRng};
51
52    use super::*;
53
54    #[test]
55    fn test_commit_g1_matches_manual_lincomb() {
56        // Polynomial
57        let poly = vec![Scalar::from(1), Scalar::from(2), Scalar::from(3)];
58
59        // Setup: 3 G1 generator points
60        let g1s: Vec<G1Point> = (0..3).map(|_| G1Projective::generator().into()).collect();
61        let ck = CommitKey::new(g1s);
62
63        // Expected = 1*G + 2*G + 3*G = 6*G
64        let expected = G1Projective::generator() * Scalar::from(6);
65        let actual = ck.commit_g1(&poly);
66
67        assert_eq!(actual, expected);
68    }
69
70    #[test]
71    fn test_commit_g1_with_more_g1s_than_poly_len() {
72        // Polynomial
73        let poly = vec![Scalar::from(1), Scalar::from(2)];
74
75        // 5 G1 generator points available, only 2 used
76        let g1s: Vec<G1Point> = (0..5).map(|_| G1Projective::generator().into()).collect();
77        let ck = CommitKey::new(g1s);
78
79        // Expected = 1*G + 2*G = 3*G
80        let expected = G1Projective::generator() * Scalar::from(3);
81        let actual = ck.commit_g1(&poly);
82
83        assert_eq!(actual, expected);
84    }
85
86    #[test]
87    #[should_panic]
88    fn test_commit_g1_panics_when_poly_longer_than_g1s() {
89        // Polynomial
90        let poly = vec![Scalar::from(1), Scalar::from(2), Scalar::from(3)];
91
92        // Only 2 G1 points
93        let g1s: Vec<G1Point> = (0..2).map(|_| G1Projective::generator().into()).collect();
94        let ck = CommitKey::new(g1s);
95
96        // Should panic because poly.len() > g1s.len()
97        let _ = ck.commit_g1(&poly);
98    }
99
100    #[test]
101    #[should_panic]
102    fn test_commit_key_empty_panics() {
103        let _ = CommitKey::new(vec![]);
104    }
105
106    #[test]
107    fn test_commit_g1_identity_when_poly_is_zero() {
108        // Polynomial is all zero coefficients
109        let poly = vec![Scalar::ZERO, Scalar::ZERO, Scalar::ZERO];
110
111        let g1s: Vec<G1Point> = (0..3).map(|_| G1Projective::generator().into()).collect();
112        let ck = CommitKey::new(g1s);
113
114        let result = ck.commit_g1(&poly);
115        assert_eq!(result, G1Projective::identity());
116    }
117
118    #[test]
119    fn test_commit_key_commit_g1_randomized_consistency() {
120        let mut rng = StdRng::seed_from_u64(42);
121
122        let g1s: Vec<G1Point> = (0..10)
123            .map(|_| G1Projective::random(&mut rng).into())
124            .collect();
125        let poly: Vec<Scalar> = (0..10).map(|_| Scalar::random(&mut rng)).collect();
126
127        let ck = CommitKey::new(g1s.clone());
128
129        // Naive expected commitment
130        let expected: G1Projective = g1s
131            .iter()
132            .zip(&poly)
133            .map(|(g, s)| G1Projective::from(*g) * s)
134            .sum();
135
136        let actual = ck.commit_g1(&poly);
137        assert_eq!(actual, expected);
138    }
139}