9
9
// fixed linter warnings
10
10
// added requiere error messages
11
11
//
12
+ // 2021 Remco Bloemen
13
+ // cleaned up code
14
+ // added InvalidProve() error
15
+ // always revert with InvalidProof() on invalid proof
16
+ // make Pairing strict
12
17
//
13
18
// SPDX-License-Identifier: GPL-3.0
14
19
pragma solidity ^ 0.8.4 ;
15
20
16
21
library Pairing {
22
+ error InvalidProof ();
23
+
24
+ // The prime q in the base field F_q for G1
25
+ uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583 ;
26
+
27
+ // The prime moludus of the scalar field of G1.
28
+ uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617 ;
29
+
17
30
struct G1Point {
18
31
uint256 X;
19
32
uint256 Y;
20
33
}
34
+
21
35
// Encoding of field elements is: X[0] * z + X[1]
22
36
struct G2Point {
23
37
uint256 [2 ] X;
@@ -31,7 +45,6 @@ library Pairing {
31
45
32
46
/// @return the generator of G2
33
47
function P2 () internal pure returns (G2Point memory ) {
34
- // Original code point
35
48
return
36
49
G2Point (
37
50
[
@@ -43,28 +56,21 @@ library Pairing {
43
56
8495653923123431417604973247489272438418190587263600148770280649306958101930
44
57
]
45
58
);
46
-
47
- /*
48
- // Changed by Jordi point
49
- return G2Point(
50
- [10857046999023057135944570762232829481370756359578518086990519993285655852781,
51
- 11559732032986387107991004021392285783925812861821192530917403151452391805634],
52
- [8495653923123431417604973247489272438418190587263600148770280649306958101930,
53
- 4082367875863433681332203403145435568316851327593401208105741076214120093531]
54
- );
55
- */
56
59
}
57
60
58
61
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
59
62
function negate (G1Point memory p ) internal pure returns (G1Point memory r ) {
60
- // The prime q in the base field F_q for G1
61
- uint256 q = 21888242871839275222246405745257275088696311157297823662689037894645226208583 ;
62
63
if (p.X == 0 && p.Y == 0 ) return G1Point (0 , 0 );
63
- return G1Point (p.X, q - (p.Y % q));
64
+ // Validate input or revert
65
+ if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof ();
66
+ // We know p.Y > 0 and p.Y < BASE_MODULUS.
67
+ return G1Point (p.X, BASE_MODULUS - p.Y);
64
68
}
65
69
66
70
/// @return r the sum of two points of G1
67
71
function addition (G1Point memory p1 , G1Point memory p2 ) internal view returns (G1Point memory r ) {
72
+ // By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
73
+ // on the curve.
68
74
uint256 [4 ] memory input;
69
75
input[0 ] = p1.X;
70
76
input[1 ] = p1.Y;
@@ -74,18 +80,16 @@ library Pairing {
74
80
// solium-disable-next-line security/no-inline-assembly
75
81
assembly {
76
82
success := staticcall (sub (gas (), 2000 ), 6 , input, 0xc0 , r, 0x60 )
77
- // Use "invalid" to make gas estimation work
78
- switch success
79
- case 0 {
80
- invalid ()
81
- }
82
83
}
83
- require ( success, " pairing-add-failed " );
84
+ if ( ! success) revert InvalidProof ( );
84
85
}
85
86
86
87
/// @return r the product of a point on G1 and a scalar, i.e.
87
88
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
88
89
function scalar_mul (G1Point memory p , uint256 s ) internal view returns (G1Point memory r ) {
90
+ // By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
91
+ // form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
92
+ if (s >= SCALAR_MODULUS) revert InvalidProof ();
89
93
uint256 [3 ] memory input;
90
94
input[0 ] = p.X;
91
95
input[1 ] = p.Y;
@@ -94,21 +98,17 @@ library Pairing {
94
98
// solium-disable-next-line security/no-inline-assembly
95
99
assembly {
96
100
success := staticcall (sub (gas (), 2000 ), 7 , input, 0x80 , r, 0x60 )
97
- // Use "invalid" to make gas estimation work
98
- switch success
99
- case 0 {
100
- invalid ()
101
- }
102
101
}
103
- require ( success, " pairing-mul-failed " );
102
+ if ( ! success) revert InvalidProof ( );
104
103
}
105
104
106
- /// @return the result of computing the pairing check
105
+ /// Asserts the pairing check
107
106
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
108
- /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should
109
- /// return true.
110
- function pairing (G1Point[] memory p1 , G2Point[] memory p2 ) internal view returns (bool ) {
111
- require (p1.length == p2.length , "pairing-lengths-failed " );
107
+ /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
108
+ function pairingCheck (G1Point[] memory p1 , G2Point[] memory p2 ) internal view {
109
+ // By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
110
+ // respective groups of the right order.
111
+ if (p1.length != p2.length ) revert InvalidProof ();
112
112
uint256 elements = p1.length ;
113
113
uint256 inputSize = elements * 6 ;
114
114
uint256 [] memory input = new uint256 [](inputSize);
@@ -125,86 +125,22 @@ library Pairing {
125
125
// solium-disable-next-line security/no-inline-assembly
126
126
assembly {
127
127
success := staticcall (sub (gas (), 2000 ), 8 , add (input, 0x20 ), mul (inputSize, 0x20 ), out, 0x20 )
128
- // Use "invalid" to make gas estimation work
129
- switch success
130
- case 0 {
131
- invalid ()
132
- }
133
128
}
134
- require (success, "pairing-opcode-failed " );
135
- return out[0 ] != 0 ;
136
- }
137
-
138
- /// Convenience method for a pairing check for two pairs.
139
- function pairingProd2 (
140
- G1Point memory a1 ,
141
- G2Point memory a2 ,
142
- G1Point memory b1 ,
143
- G2Point memory b2
144
- ) internal view returns (bool ) {
145
- G1Point[] memory p1 = new G1Point [](2 );
146
- G2Point[] memory p2 = new G2Point [](2 );
147
- p1[0 ] = a1;
148
- p1[1 ] = b1;
149
- p2[0 ] = a2;
150
- p2[1 ] = b2;
151
- return pairing (p1, p2);
152
- }
153
-
154
- /// Convenience method for a pairing check for three pairs.
155
- function pairingProd3 (
156
- G1Point memory a1 ,
157
- G2Point memory a2 ,
158
- G1Point memory b1 ,
159
- G2Point memory b2 ,
160
- G1Point memory c1 ,
161
- G2Point memory c2
162
- ) internal view returns (bool ) {
163
- G1Point[] memory p1 = new G1Point [](3 );
164
- G2Point[] memory p2 = new G2Point [](3 );
165
- p1[0 ] = a1;
166
- p1[1 ] = b1;
167
- p1[2 ] = c1;
168
- p2[0 ] = a2;
169
- p2[1 ] = b2;
170
- p2[2 ] = c2;
171
- return pairing (p1, p2);
172
- }
173
-
174
- /// Convenience method for a pairing check for four pairs.
175
- function pairingProd4 (
176
- G1Point memory a1 ,
177
- G2Point memory a2 ,
178
- G1Point memory b1 ,
179
- G2Point memory b2 ,
180
- G1Point memory c1 ,
181
- G2Point memory c2 ,
182
- G1Point memory d1 ,
183
- G2Point memory d2
184
- ) internal view returns (bool ) {
185
- G1Point[] memory p1 = new G1Point [](4 );
186
- G2Point[] memory p2 = new G2Point [](4 );
187
- p1[0 ] = a1;
188
- p1[1 ] = b1;
189
- p1[2 ] = c1;
190
- p1[3 ] = d1;
191
- p2[0 ] = a2;
192
- p2[1 ] = b2;
193
- p2[2 ] = c2;
194
- p2[3 ] = d2;
195
- return pairing (p1, p2);
129
+ if (! success || out[0 ] != 1 ) revert InvalidProof ();
196
130
}
197
131
}
198
132
199
133
contract Verifier {
200
134
using Pairing for * ;
135
+
201
136
struct VerifyingKey {
202
137
Pairing.G1Point alfa1;
203
138
Pairing.G2Point beta2;
204
139
Pairing.G2Point gamma2;
205
140
Pairing.G2Point delta2;
206
141
Pairing.G1Point[] IC;
207
142
}
143
+
208
144
struct Proof {
209
145
Pairing.G1Point A;
210
146
Pairing.G2Point B;
@@ -275,42 +211,40 @@ contract Verifier {
275
211
);
276
212
}
277
213
278
- function verify (uint256 [] memory input , Proof memory proof ) internal view returns (uint256 ) {
279
- uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617 ;
280
- VerifyingKey memory vk = verifyingKey ();
281
- require (input.length + 1 == vk.IC.length , "verifier-bad-input " );
282
- // Compute the linear combination vk_x
283
- Pairing.G1Point memory vk_x = Pairing.G1Point (0 , 0 );
284
- for (uint256 i = 0 ; i < input.length ; i++ ) {
285
- require (input[i] < snark_scalar_field, "verifier-gte-snark-scalar-field " );
286
- vk_x = Pairing.addition (vk_x, Pairing.scalar_mul (vk.IC[i + 1 ], input[i]));
287
- }
288
- vk_x = Pairing.addition (vk_x, vk.IC[0 ]);
289
- if (
290
- ! Pairing.pairingProd4 (Pairing.negate (proof.A), proof.B, vk.alfa1, vk.beta2, vk_x, vk.gamma2, proof.C, vk.delta2)
291
- ) return 1 ;
292
- return 0 ;
293
- }
294
-
295
- /// @return r bool true if proof is valid
214
+ /// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
296
215
function verifyProof (
297
216
uint256 [2 ] memory a ,
298
217
uint256 [2 ][2 ] memory b ,
299
218
uint256 [2 ] memory c ,
300
219
uint256 [4 ] memory input
301
- ) public view returns (bool r ) {
220
+ ) public view {
221
+ // If the values are not in the correct range, the Pairing contract will revert.
302
222
Proof memory proof;
303
223
proof.A = Pairing.G1Point (a[0 ], a[1 ]);
304
224
proof.B = Pairing.G2Point ([b[0 ][0 ], b[0 ][1 ]], [b[1 ][0 ], b[1 ][1 ]]);
305
225
proof.C = Pairing.G1Point (c[0 ], c[1 ]);
306
- uint256 [] memory inputValues = new uint256 [](input.length );
307
- for (uint256 i = 0 ; i < input.length ; i++ ) {
308
- inputValues[i] = input[i];
309
- }
310
- if (verify (inputValues, proof) == 0 ) {
311
- return true ;
312
- } else {
313
- return false ;
314
- }
226
+
227
+ VerifyingKey memory vk = verifyingKey ();
228
+
229
+ // Compute the linear combination vk_x of inputs times IC
230
+ if (input.length + 1 != vk.IC.length ) revert Pairing.InvalidProof ();
231
+ Pairing.G1Point memory vk_x = vk.IC[0 ];
232
+ vk_x = Pairing.addition (vk_x, Pairing.scalar_mul (vk.IC[1 ], input[0 ]));
233
+ vk_x = Pairing.addition (vk_x, Pairing.scalar_mul (vk.IC[2 ], input[1 ]));
234
+ vk_x = Pairing.addition (vk_x, Pairing.scalar_mul (vk.IC[3 ], input[2 ]));
235
+ vk_x = Pairing.addition (vk_x, Pairing.scalar_mul (vk.IC[4 ], input[3 ]));
236
+
237
+ // Check pairing
238
+ Pairing.G1Point[] memory p1 = new Pairing.G1Point [](4 );
239
+ Pairing.G2Point[] memory p2 = new Pairing.G2Point [](4 );
240
+ p1[0 ] = Pairing.negate (proof.A);
241
+ p2[0 ] = proof.B;
242
+ p1[1 ] = vk.alfa1;
243
+ p2[1 ] = vk.beta2;
244
+ p1[2 ] = vk_x;
245
+ p2[2 ] = vk.gamma2;
246
+ p1[3 ] = proof.C;
247
+ p2[3 ] = vk.delta2;
248
+ Pairing.pairingCheck (p1, p2);
315
249
}
316
250
}
0 commit comments