Skip to main content

ekzg_multi_open/
verification_key.rs

1use bls12_381::{
2    lincomb::{g1_lincomb, g2_lincomb},
3    G1Point, G1Projective, G2Point, G2Projective, Scalar,
4};
5
6/// Verification Key is used to verify opening proofs made about a committed polynomial.
7#[derive(Clone, Debug)]
8pub struct VerificationKey {
9    /// The powers of tau G1 used in the setup
10    ///
11    /// ie group elements of the form `{ \tau^i G }`
12    pub g1s: Vec<G1Point>,
13    /// The powers of tau G2 used in the setup
14    ///
15    /// ie group elements of the form `{ \tau^i G }`
16    pub g2s: Vec<G2Point>,
17    /// The degree-0 term in the powers of tau G2 elements.
18    pub g2_gen: G2Point,
19    /// This is the number of points that will be a
20    /// opened at any one time. Another way to think
21    /// of this, is that its the number of points a
22    /// proof will attest to.
23    ///
24    /// In most cases, this is the number of G1 elements,
25    /// however, we have this explicit parameter to
26    /// avoid foot guns.
27    pub coset_size: usize,
28    /// The number of coefficients in the polynomial that we want to
29    /// verify claims about.
30    ///
31    /// Note: We could also use the max degree bound here. (This is a matter of preference)
32    pub num_coefficients_in_polynomial: usize,
33}
34
35impl VerificationKey {
36    pub fn new(
37        g1s: Vec<G1Point>,
38        g2s: Vec<G2Point>,
39        coset_size: usize,
40        num_coefficients_in_polynomial: usize,
41    ) -> Self {
42        // This assumes that the trusted setup contains more than 1 element.
43        //
44        // For all of our purposes and for any useful applications, this will be the case.
45        let g2_gen = g2s[0];
46
47        assert!(coset_size < g2s.len(), "The coset size must be less than the amount of g2 elements as the verifier needs to do a g2 msm of size `coset_size`");
48
49        Self {
50            g1s,
51            g2s,
52            g2_gen,
53            coset_size,
54            num_coefficients_in_polynomial,
55        }
56    }
57
58    /// Commit to a polynomial in monomial form using the G2 group elements
59    pub fn commit_g2(&self, polynomial: &[Scalar]) -> G2Projective {
60        assert!(self.g2s.len() >= polynomial.len());
61        g2_lincomb(&self.g2s[..polynomial.len()], polynomial)
62            .expect("number of g2 points is equal to the number of coefficients in the polynomial")
63    }
64
65    /// Commit to a polynomial in monomial form using the G1 group elements
66    pub fn commit_g1(&self, polynomial: &[Scalar]) -> G1Projective {
67        assert!(self.g1s.len() >= polynomial.len());
68        g1_lincomb(&self.g1s[..polynomial.len()], polynomial)
69            .expect("number of g1 points is equal to the number of coefficients in the polynomial")
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use bls12_381::{traits::*, G1Projective, Scalar};
76
77    use super::*;
78
79    #[test]
80    fn test_commit_g1_matches_lincomb() {
81        // Polynomial coefficients
82        let poly = vec![Scalar::from(1u64), Scalar::from(2u64), Scalar::from(3u64)];
83
84        // G1 points: just use the generator three times
85        let g1s: Vec<G1Point> = (0..3).map(|_| G1Projective::generator().into()).collect();
86
87        // Dummy G2s, unused in this test
88        let g2s: Vec<_> = (0..4).map(|_| G2Projective::generator().into()).collect();
89
90        let vk = VerificationKey::new(g1s, g2s, 2, 3);
91
92        // Expected = 1 * G + 2 * G + 3 * G = (1 + 2 + 3) * G = 6 * G
93        let g = G1Projective::generator();
94        let expected = g * Scalar::from(6u64);
95
96        let actual = vk.commit_g1(&poly);
97
98        assert_eq!(actual, expected);
99    }
100
101    #[test]
102    fn test_commit_g2_matches_lincomb() {
103        // Polynomial coefficients
104        let poly = vec![Scalar::from(5u64), Scalar::from(7u64), Scalar::from(11u64)];
105
106        // G2 points: use the generator three times
107        let g2s: Vec<G2Point> = (0..3).map(|_| G2Projective::generator().into()).collect();
108
109        // Dummy G1s, not used here
110        let g1s: Vec<_> = (0..4).map(|_| G1Projective::generator().into()).collect();
111
112        let vk = VerificationKey::new(g1s, g2s, 2, 3);
113
114        // Expected: 5 * G + 7 * G + 11 * G = (5 + 7 + 11) * G = 23 * G
115        let g = G2Projective::generator();
116        let expected = g * Scalar::from(23u64);
117
118        let actual = vk.commit_g2(&poly);
119
120        assert_eq!(actual, expected);
121    }
122
123    #[test]
124    #[should_panic]
125    fn test_coset_size_check_panics() {
126        let g1s = vec![G1Projective::generator().into(); 2];
127        let g2s = vec![G2Projective::generator().into(); 2];
128        let _vk = VerificationKey::new(g1s, g2s, 2, 2); // coset_size == g2s.len() → panic
129    }
130
131    #[test]
132    fn test_g2_gen_is_first_element() {
133        let g2s: Vec<_> = (0..4).map(|_| G2Projective::generator().into()).collect();
134        let g1s: Vec<_> = (0..4).map(|_| G1Projective::generator().into()).collect();
135
136        let vk = VerificationKey::new(g1s, g2s.clone(), 1, 3);
137        assert_eq!(vk.g2_gen, g2s[0]);
138    }
139
140    #[test]
141    #[should_panic]
142    fn test_commit_g1_panics_when_poly_longer_than_g1s() {
143        // 2 G1 points
144        let g1s: Vec<G1Point> = (0..2).map(|_| G1Projective::generator().into()).collect();
145        let g2s: Vec<G2Point> = (0..3).map(|_| G2Projective::generator().into()).collect();
146
147        // Polynomial of length 3 — longer than g1s
148        let poly = vec![Scalar::from(1), Scalar::from(2), Scalar::from(3)];
149
150        let vk = VerificationKey::new(g1s, g2s, 1, 3);
151
152        // This will panic: not enough g1s to match poly length
153        let _ = vk.commit_g1(&poly);
154    }
155
156    #[test]
157    #[should_panic]
158    fn test_commit_g2_panics_when_poly_longer_than_g2s() {
159        // 2 G2 points
160        let g2s: Vec<G2Point> = (0..2).map(|_| G2Projective::generator().into()).collect();
161        let g1s: Vec<G1Point> = (0..3).map(|_| G1Projective::generator().into()).collect();
162
163        // Polynomial of length 3 — longer than g2s
164        let poly = vec![Scalar::from(5), Scalar::from(7), Scalar::from(11)];
165
166        let vk = VerificationKey::new(g1s, g2s, 1, 3);
167
168        // This will panic: not enough g2s to match poly length
169        let _ = vk.commit_g2(&poly);
170    }
171
172    #[test]
173    fn test_commit_g1_with_more_g1s_than_poly_len() {
174        // 5 G1 points in trusted setup
175        let g1s: Vec<G1Point> = (0..5).map(|_| G1Projective::generator().into()).collect();
176        let g2s: Vec<G2Point> = (0..5).map(|_| G2Projective::generator().into()).collect();
177
178        // Polynomial of length 3
179        let poly = vec![Scalar::from(1), Scalar::from(2), Scalar::from(3)];
180
181        let vk = VerificationKey::new(g1s, g2s, 1, 5);
182
183        // Compute manually: 1 * G + 2 * G + 3 * G = 6 * G
184        let g = G1Projective::generator();
185        let expected = g * Scalar::from(6u64);
186
187        let actual = vk.commit_g1(&poly);
188        assert_eq!(actual, expected);
189    }
190
191    #[test]
192    fn test_commit_g2_with_more_g2s_than_poly_len() {
193        // 5 G2 points in trusted setup
194        let g2s: Vec<G2Point> = (0..5).map(|_| G2Projective::generator().into()).collect();
195        let g1s: Vec<G1Point> = (0..5).map(|_| G1Projective::generator().into()).collect();
196
197        // Polynomial of length 3
198        let poly = vec![Scalar::from(2), Scalar::from(4), Scalar::from(6)];
199
200        let vk = VerificationKey::new(g1s, g2s, 1, 5);
201
202        // Compute manually: 2 * G + 4 * G + 6 * G = 12 * G
203        let g = G2Projective::generator();
204        let expected = g * Scalar::from(12u64);
205
206        let actual = vk.commit_g2(&poly);
207        assert_eq!(actual, expected);
208    }
209}