1
+ pragma solidity ^ 0.8.0 ;
2
+
3
+ /**
4
+ * @title Elliptic curve operations on twist points for alt_bn128
5
+ * @author Mustafa Al-Bassam ([email protected] )
6
+ * @dev Homepage: https://github.com/musalbas/solidity-BN256G2
7
+ */
8
+ library BN256G2 {
9
+ uint256 internal constant FIELD_MODULUS =
10
+ 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 ;
11
+ uint256 internal constant TWISTBX =
12
+ 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5 ;
13
+ uint256 internal constant TWISTBY =
14
+ 0x9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2 ;
15
+ uint256 internal constant PTXX = 0 ;
16
+ uint256 internal constant PTXY = 1 ;
17
+ uint256 internal constant PTYX = 2 ;
18
+ uint256 internal constant PTYY = 3 ;
19
+ uint256 internal constant PTZX = 4 ;
20
+ uint256 internal constant PTZY = 5 ;
21
+
22
+ /**
23
+ * @notice Add two twist points
24
+ * @param pt1xx Coefficient 1 of x on point 1
25
+ * @param pt1xy Coefficient 2 of x on point 1
26
+ * @param pt1yx Coefficient 1 of y on point 1
27
+ * @param pt1yy Coefficient 2 of y on point 1
28
+ * @param pt2xx Coefficient 1 of x on point 2
29
+ * @param pt2xy Coefficient 2 of x on point 2
30
+ * @param pt2yx Coefficient 1 of y on point 2
31
+ * @param pt2yy Coefficient 2 of y on point 2
32
+ * @return (pt3xx, pt3xy, pt3yx, pt3yy)
33
+ */
34
+ function ECTwistAdd (
35
+ uint256 pt1xx ,
36
+ uint256 pt1xy ,
37
+ uint256 pt1yx ,
38
+ uint256 pt1yy ,
39
+ uint256 pt2xx ,
40
+ uint256 pt2xy ,
41
+ uint256 pt2yx ,
42
+ uint256 pt2yy
43
+ ) public view returns (uint256 , uint256 , uint256 , uint256 ) {
44
+ if (pt1xx == 0 && pt1xy == 0 && pt1yx == 0 && pt1yy == 0 ) {
45
+ if (! (pt2xx == 0 && pt2xy == 0 && pt2yx == 0 && pt2yy == 0 )) {
46
+ assert (_isOnCurve (pt2xx, pt2xy, pt2yx, pt2yy));
47
+ }
48
+ return (pt2xx, pt2xy, pt2yx, pt2yy);
49
+ } else if (pt2xx == 0 && pt2xy == 0 && pt2yx == 0 && pt2yy == 0 ) {
50
+ assert (_isOnCurve (pt1xx, pt1xy, pt1yx, pt1yy));
51
+ return (pt1xx, pt1xy, pt1yx, pt1yy);
52
+ }
53
+
54
+ assert (_isOnCurve (pt1xx, pt1xy, pt1yx, pt1yy));
55
+ assert (_isOnCurve (pt2xx, pt2xy, pt2yx, pt2yy));
56
+
57
+ uint256 [6 ] memory pt3 =
58
+ _ECTwistAddJacobian (pt1xx, pt1xy, pt1yx, pt1yy, 1 , 0 , pt2xx, pt2xy, pt2yx, pt2yy, 1 , 0 );
59
+
60
+ return _fromJacobian (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]);
61
+ }
62
+
63
+ /**
64
+ * @notice Multiply a twist point by a scalar
65
+ * @param s Scalar to multiply by
66
+ * @param pt1xx Coefficient 1 of x
67
+ * @param pt1xy Coefficient 2 of x
68
+ * @param pt1yx Coefficient 1 of y
69
+ * @param pt1yy Coefficient 2 of y
70
+ * @return (pt2xx, pt2xy, pt2yx, pt2yy)
71
+ */
72
+ function ECTwistMul (
73
+ uint256 s ,
74
+ uint256 pt1xx ,
75
+ uint256 pt1xy ,
76
+ uint256 pt1yx ,
77
+ uint256 pt1yy
78
+ ) public view returns (uint256 , uint256 , uint256 , uint256 ) {
79
+ uint256 pt1zx = 1 ;
80
+ if (pt1xx == 0 && pt1xy == 0 && pt1yx == 0 && pt1yy == 0 ) {
81
+ pt1xx = 1 ;
82
+ pt1yx = 1 ;
83
+ pt1zx = 0 ;
84
+ } else {
85
+ assert (_isOnCurve (pt1xx, pt1xy, pt1yx, pt1yy));
86
+ }
87
+
88
+ uint256 [6 ] memory pt2 = _ECTwistMulJacobian (s, pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, 0 );
89
+
90
+ return _fromJacobian (pt2[PTXX], pt2[PTXY], pt2[PTYX], pt2[PTYY], pt2[PTZX], pt2[PTZY]);
91
+ }
92
+
93
+ /**
94
+ * @notice Get the field modulus
95
+ * @return The field modulus
96
+ */
97
+ function GetFieldModulus () public pure returns (uint256 ) {
98
+ return FIELD_MODULUS;
99
+ }
100
+
101
+ function submod (uint256 a , uint256 b , uint256 n ) internal pure returns (uint256 ) {
102
+ return addmod (a, n - b, n);
103
+ }
104
+
105
+ function _FQ2Mul (
106
+ uint256 xx ,
107
+ uint256 xy ,
108
+ uint256 yx ,
109
+ uint256 yy
110
+ ) internal pure returns (uint256 , uint256 ) {
111
+ return (
112
+ submod (mulmod (xx, yx, FIELD_MODULUS), mulmod (xy, yy, FIELD_MODULUS), FIELD_MODULUS),
113
+ addmod (mulmod (xx, yy, FIELD_MODULUS), mulmod (xy, yx, FIELD_MODULUS), FIELD_MODULUS)
114
+ );
115
+ }
116
+
117
+ function _FQ2Muc (uint256 xx , uint256 xy , uint256 c ) internal pure returns (uint256 , uint256 ) {
118
+ return (mulmod (xx, c, FIELD_MODULUS), mulmod (xy, c, FIELD_MODULUS));
119
+ }
120
+
121
+ function _FQ2Add (
122
+ uint256 xx ,
123
+ uint256 xy ,
124
+ uint256 yx ,
125
+ uint256 yy
126
+ ) internal pure returns (uint256 , uint256 ) {
127
+ return (addmod (xx, yx, FIELD_MODULUS), addmod (xy, yy, FIELD_MODULUS));
128
+ }
129
+
130
+ function _FQ2Sub (
131
+ uint256 xx ,
132
+ uint256 xy ,
133
+ uint256 yx ,
134
+ uint256 yy
135
+ ) internal pure returns (uint256 rx , uint256 ry ) {
136
+ return (submod (xx, yx, FIELD_MODULUS), submod (xy, yy, FIELD_MODULUS));
137
+ }
138
+
139
+ function _FQ2Div (
140
+ uint256 xx ,
141
+ uint256 xy ,
142
+ uint256 yx ,
143
+ uint256 yy
144
+ ) internal view returns (uint256 , uint256 ) {
145
+ (yx, yy) = _FQ2Inv (yx, yy);
146
+ return _FQ2Mul (xx, xy, yx, yy);
147
+ }
148
+
149
+ function _FQ2Inv (uint256 x , uint256 y ) internal view returns (uint256 , uint256 ) {
150
+ uint256 inv = _modInv (
151
+ addmod (mulmod (y, y, FIELD_MODULUS), mulmod (x, x, FIELD_MODULUS), FIELD_MODULUS),
152
+ FIELD_MODULUS
153
+ );
154
+ return (mulmod (x, inv, FIELD_MODULUS), FIELD_MODULUS - mulmod (y, inv, FIELD_MODULUS));
155
+ }
156
+
157
+ function _isOnCurve (
158
+ uint256 xx ,
159
+ uint256 xy ,
160
+ uint256 yx ,
161
+ uint256 yy
162
+ ) internal pure returns (bool ) {
163
+ uint256 yyx;
164
+ uint256 yyy;
165
+ uint256 xxxx;
166
+ uint256 xxxy;
167
+ (yyx, yyy) = _FQ2Mul (yx, yy, yx, yy);
168
+ (xxxx, xxxy) = _FQ2Mul (xx, xy, xx, xy);
169
+ (xxxx, xxxy) = _FQ2Mul (xxxx, xxxy, xx, xy);
170
+ (yyx, yyy) = _FQ2Sub (yyx, yyy, xxxx, xxxy);
171
+ (yyx, yyy) = _FQ2Sub (yyx, yyy, TWISTBX, TWISTBY);
172
+ return yyx == 0 && yyy == 0 ;
173
+ }
174
+
175
+ function _modInv (uint256 a , uint256 n ) internal view returns (uint256 result ) {
176
+ bool success;
177
+ assembly ("memory-safe" ) {
178
+ let freemem := mload (0x40 )
179
+ mstore (freemem, 0x20 )
180
+ mstore (add (freemem, 0x20 ), 0x20 )
181
+ mstore (add (freemem, 0x40 ), 0x20 )
182
+ mstore (add (freemem, 0x60 ), a)
183
+ mstore (add (freemem, 0x80 ), sub (n, 2 ))
184
+ mstore (add (freemem, 0xA0 ), n)
185
+ success := staticcall (sub (gas (), 2000 ), 5 , freemem, 0xC0 , freemem, 0x20 )
186
+ result := mload (freemem)
187
+ }
188
+ require (success);
189
+ }
190
+
191
+ function _fromJacobian (
192
+ uint256 pt1xx ,
193
+ uint256 pt1xy ,
194
+ uint256 pt1yx ,
195
+ uint256 pt1yy ,
196
+ uint256 pt1zx ,
197
+ uint256 pt1zy
198
+ ) internal view returns (uint256 pt2xx , uint256 pt2xy , uint256 pt2yx , uint256 pt2yy ) {
199
+ uint256 invzx;
200
+ uint256 invzy;
201
+ (invzx, invzy) = _FQ2Inv (pt1zx, pt1zy);
202
+ (pt2xx, pt2xy) = _FQ2Mul (pt1xx, pt1xy, invzx, invzy);
203
+ (pt2yx, pt2yy) = _FQ2Mul (pt1yx, pt1yy, invzx, invzy);
204
+ }
205
+
206
+ function _ECTwistAddJacobian (
207
+ uint256 pt1xx ,
208
+ uint256 pt1xy ,
209
+ uint256 pt1yx ,
210
+ uint256 pt1yy ,
211
+ uint256 pt1zx ,
212
+ uint256 pt1zy ,
213
+ uint256 pt2xx ,
214
+ uint256 pt2xy ,
215
+ uint256 pt2yx ,
216
+ uint256 pt2yy ,
217
+ uint256 pt2zx ,
218
+ uint256 pt2zy
219
+ ) internal pure returns (uint256 [6 ] memory pt3 ) {
220
+ if (pt1zx == 0 && pt1zy == 0 ) {
221
+ (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) =
222
+ (pt2xx, pt2xy, pt2yx, pt2yy, pt2zx, pt2zy);
223
+ return pt3;
224
+ } else if (pt2zx == 0 && pt2zy == 0 ) {
225
+ (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) =
226
+ (pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy);
227
+ return pt3;
228
+ }
229
+
230
+ (pt2yx, pt2yy) = _FQ2Mul (pt2yx, pt2yy, pt1zx, pt1zy); // U1 = y2 * z1
231
+ (pt3[PTYX], pt3[PTYY]) = _FQ2Mul (pt1yx, pt1yy, pt2zx, pt2zy); // U2 = y1 * z2
232
+ (pt2xx, pt2xy) = _FQ2Mul (pt2xx, pt2xy, pt1zx, pt1zy); // V1 = x2 * z1
233
+ (pt3[PTZX], pt3[PTZY]) = _FQ2Mul (pt1xx, pt1xy, pt2zx, pt2zy); // V2 = x1 * z2
234
+
235
+ if (pt2xx == pt3[PTZX] && pt2xy == pt3[PTZY]) {
236
+ if (pt2yx == pt3[PTYX] && pt2yy == pt3[PTYY]) {
237
+ (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) =
238
+ _ECTwistDoubleJacobian (pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy);
239
+ return pt3;
240
+ }
241
+ (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = (1 , 0 , 1 , 0 , 0 , 0 );
242
+ return pt3;
243
+ }
244
+
245
+ (pt2zx, pt2zy) = _FQ2Mul (pt1zx, pt1zy, pt2zx, pt2zy); // W = z1 * z2
246
+ (pt1xx, pt1xy) = _FQ2Sub (pt2yx, pt2yy, pt3[PTYX], pt3[PTYY]); // U = U1 - U2
247
+ (pt1yx, pt1yy) = _FQ2Sub (pt2xx, pt2xy, pt3[PTZX], pt3[PTZY]); // V = V1 - V2
248
+ (pt1zx, pt1zy) = _FQ2Mul (pt1yx, pt1yy, pt1yx, pt1yy); // V_squared = V * V
249
+ (pt2yx, pt2yy) = _FQ2Mul (pt1zx, pt1zy, pt3[PTZX], pt3[PTZY]); // V_squared_times_V2 = V_squared * V2
250
+ (pt1zx, pt1zy) = _FQ2Mul (pt1zx, pt1zy, pt1yx, pt1yy); // V_cubed = V * V_squared
251
+ (pt3[PTZX], pt3[PTZY]) = _FQ2Mul (pt1zx, pt1zy, pt2zx, pt2zy); // newz = V_cubed * W
252
+ (pt2xx, pt2xy) = _FQ2Mul (pt1xx, pt1xy, pt1xx, pt1xy); // U * U
253
+ (pt2xx, pt2xy) = _FQ2Mul (pt2xx, pt2xy, pt2zx, pt2zy); // U * U * W
254
+ (pt2xx, pt2xy) = _FQ2Sub (pt2xx, pt2xy, pt1zx, pt1zy); // U * U * W - V_cubed
255
+ (pt2zx, pt2zy) = _FQ2Muc (pt2yx, pt2yy, 2 ); // 2 * V_squared_times_V2
256
+ (pt2xx, pt2xy) = _FQ2Sub (pt2xx, pt2xy, pt2zx, pt2zy); // A = U * U * W - V_cubed - 2 * V_squared_times_V2
257
+ (pt3[PTXX], pt3[PTXY]) = _FQ2Mul (pt1yx, pt1yy, pt2xx, pt2xy); // newx = V * A
258
+ (pt1yx, pt1yy) = _FQ2Sub (pt2yx, pt2yy, pt2xx, pt2xy); // V_squared_times_V2 - A
259
+ (pt1yx, pt1yy) = _FQ2Mul (pt1xx, pt1xy, pt1yx, pt1yy); // U * (V_squared_times_V2 - A)
260
+ (pt1xx, pt1xy) = _FQ2Mul (pt1zx, pt1zy, pt3[PTYX], pt3[PTYY]); // V_cubed * U2
261
+ (pt3[PTYX], pt3[PTYY]) = _FQ2Sub (pt1yx, pt1yy, pt1xx, pt1xy); // newy = U * (V_squared_times_V2 - A) - V_cubed * U2
262
+ }
263
+
264
+ function _ECTwistDoubleJacobian (
265
+ uint256 pt1xx ,
266
+ uint256 pt1xy ,
267
+ uint256 pt1yx ,
268
+ uint256 pt1yy ,
269
+ uint256 pt1zx ,
270
+ uint256 pt1zy
271
+ )
272
+ internal
273
+ pure
274
+ returns (
275
+ uint256 pt2xx ,
276
+ uint256 pt2xy ,
277
+ uint256 pt2yx ,
278
+ uint256 pt2yy ,
279
+ uint256 pt2zx ,
280
+ uint256 pt2zy
281
+ )
282
+ {
283
+ (pt2xx, pt2xy) = _FQ2Muc (pt1xx, pt1xy, 3 ); // 3 * x
284
+ (pt2xx, pt2xy) = _FQ2Mul (pt2xx, pt2xy, pt1xx, pt1xy); // W = 3 * x * x
285
+ (pt1zx, pt1zy) = _FQ2Mul (pt1yx, pt1yy, pt1zx, pt1zy); // S = y * z
286
+ (pt2yx, pt2yy) = _FQ2Mul (pt1xx, pt1xy, pt1yx, pt1yy); // x * y
287
+ (pt2yx, pt2yy) = _FQ2Mul (pt2yx, pt2yy, pt1zx, pt1zy); // B = x * y * S
288
+ (pt1xx, pt1xy) = _FQ2Mul (pt2xx, pt2xy, pt2xx, pt2xy); // W * W
289
+ (pt2zx, pt2zy) = _FQ2Muc (pt2yx, pt2yy, 8 ); // 8 * B
290
+ (pt1xx, pt1xy) = _FQ2Sub (pt1xx, pt1xy, pt2zx, pt2zy); // H = W * W - 8 * B
291
+ (pt2zx, pt2zy) = _FQ2Mul (pt1zx, pt1zy, pt1zx, pt1zy); // S_squared = S * S
292
+ (pt2yx, pt2yy) = _FQ2Muc (pt2yx, pt2yy, 4 ); // 4 * B
293
+ (pt2yx, pt2yy) = _FQ2Sub (pt2yx, pt2yy, pt1xx, pt1xy); // 4 * B - H
294
+ (pt2yx, pt2yy) = _FQ2Mul (pt2yx, pt2yy, pt2xx, pt2xy); // W * (4 * B - H)
295
+ (pt2xx, pt2xy) = _FQ2Muc (pt1yx, pt1yy, 8 ); // 8 * y
296
+ (pt2xx, pt2xy) = _FQ2Mul (pt2xx, pt2xy, pt1yx, pt1yy); // 8 * y * y
297
+ (pt2xx, pt2xy) = _FQ2Mul (pt2xx, pt2xy, pt2zx, pt2zy); // 8 * y * y * S_squared
298
+ (pt2yx, pt2yy) = _FQ2Sub (pt2yx, pt2yy, pt2xx, pt2xy); // newy = W * (4 * B - H) - 8 * y * y * S_squared
299
+ (pt2xx, pt2xy) = _FQ2Muc (pt1xx, pt1xy, 2 ); // 2 * H
300
+ (pt2xx, pt2xy) = _FQ2Mul (pt2xx, pt2xy, pt1zx, pt1zy); // newx = 2 * H * S
301
+ (pt2zx, pt2zy) = _FQ2Mul (pt1zx, pt1zy, pt2zx, pt2zy); // S * S_squared
302
+ (pt2zx, pt2zy) = _FQ2Muc (pt2zx, pt2zy, 8 ); // newz = 8 * S * S_squared
303
+ }
304
+
305
+ function _ECTwistMulJacobian (
306
+ uint256 d ,
307
+ uint256 pt1xx ,
308
+ uint256 pt1xy ,
309
+ uint256 pt1yx ,
310
+ uint256 pt1yy ,
311
+ uint256 pt1zx ,
312
+ uint256 pt1zy
313
+ ) internal pure returns (uint256 [6 ] memory pt2 ) {
314
+ while (d != 0 ) {
315
+ if ((d & 1 ) != 0 ) {
316
+ pt2 = _ECTwistAddJacobian (
317
+ pt2[PTXX],
318
+ pt2[PTXY],
319
+ pt2[PTYX],
320
+ pt2[PTYY],
321
+ pt2[PTZX],
322
+ pt2[PTZY],
323
+ pt1xx,
324
+ pt1xy,
325
+ pt1yx,
326
+ pt1yy,
327
+ pt1zx,
328
+ pt1zy
329
+ );
330
+ }
331
+ (pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy) =
332
+ _ECTwistDoubleJacobian (pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy);
333
+
334
+ d = d / 2 ;
335
+ }
336
+ }
337
+ }
0 commit comments