snarkjs/src/bn128.js
src/bn128.jsRaw
1const bigInt = require("./bigint.js");
2const assert = require("assert");
3
4const F1Field = require("./zqfield.js");
5const F2Field = require("./f2field.js");
6const F3Field = require("./f3field.js");
7const GCurve = require("./gcurve.js");
8
9class BN128 {
10
11 constructor() {
12
13 this.q = bigInt("21888242871839275222246405745257275088696311157297823662689037894645226208583");
14 this.r = bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
15 this.g1 = [ bigInt(1), bigInt(2) ];
16 this.g2 = [
17 [
18 bigInt("10857046999023057135944570762232829481370756359578518086990519993285655852781"),
19 bigInt("11559732032986387107991004021392285783925812861821192530917403151452391805634")
20 ],
21 [
22 bigInt("8495653923123431417604973247489272438418190587263600148770280649306958101930"),
23 bigInt("4082367875863433681332203403145435568316851327593401208105741076214120093531")
24 ]
25 ];
26
27 this.nonResidueF2 = bigInt("21888242871839275222246405745257275088696311157297823662689037894645226208582");
28 this.nonResidueF6 = [ bigInt("9"), bigInt("1") ];
29
30 this.F1 = new F1Field(this.q);
31 this.F2 = new F2Field(this.F1, this.nonResidueF2);
32 this.G1 = new GCurve(this.F1, this.g1);
33 this.G2 = new GCurve(this.F2, this.g2);
34 this.F6 = new F3Field(this.F2, this.nonResidueF6);
35 this.F12 = new F2Field(this.F6, this.nonResidueF6);
36 const self = this;
37 this.F12._mulByNonResidue = function(a) {
38 return [self.F2.mul(this.nonResidue, a[2]), a[0], a[1]];
39 };
40
41 this._preparePairing();
42
43 }
44
45 _preparePairing() {
46 this.loopCount = bigInt("29793968203157093288");// CONSTANT
47
48 // Set loopCountNeg
49 if (this.loopCount.isNegative()) {
50 this.loopCount = this.neg();
51 this.loopCountNeg = true;
52 } else {
53 this.loopCountNeg = false;
54 }
55
56 // Set loop_count_bits
57 let lc = this.loopCount;
58 this.loop_count_bits = []; // Constant
59 while (!lc.isZero()) {
60 this.loop_count_bits.push( lc.isOdd() );
61 lc = lc.shiftRight(1);
62 }
63
64 this.two_inv = this.F1.inverse(bigInt(2));
65
66 this.coef_b = bigInt(3);
67 this.twist = [bigInt(9) , bigInt(1)];
68 this.twist_coeff_b = this.F2.mulEscalar( this.F2.inverse(this.twist), this.coef_b );
69
70 this.frobenius_coeffs_c1_1 = bigInt("21888242871839275222246405745257275088696311157297823662689037894645226208582");
71 this.twist_mul_by_q_X =
72 [
73 bigInt("21575463638280843010398324269430826099269044274347216827212613867836435027261"),
74 bigInt("10307601595873709700152284273816112264069230130616436755625194854815875713954")
75 ];
76 this.twist_mul_by_q_Y =
77 [
78 bigInt("2821565182194536844548159561693502659359617185244120367078079554186484126554"),
79 bigInt("3505843767911556378687030309984248845540243509899259641013678093033130930403")
80 ];
81
82 this.final_exponent = bigInt("552484233613224096312617126783173147097382103762957654188882734314196910839907541213974502761540629817009608548654680343627701153829446747810907373256841551006201639677726139946029199968412598804882391702273019083653272047566316584365559776493027495458238373902875937659943504873220554161550525926302303331747463515644711876653177129578303191095900909191624817826566688241804408081892785725967931714097716709526092261278071952560171111444072049229123565057483750161460024353346284167282452756217662335528813519139808291170539072125381230815729071544861602750936964829313608137325426383735122175229541155376346436093930287402089517426973178917569713384748081827255472576937471496195752727188261435633271238710131736096299798168852925540549342330775279877006784354801422249722573783561685179618816480037695005515426162362431072245638324744480");
83
84 }
85
86
87 pairing(p1, p2) {
88
89 const pre1 = this.precomputeG1(p1);
90 const pre2 = this.precomputeG2(p2);
91
92 const r1 = this.millerLoop(pre1, pre2);
93
94 const res = this.finalExponentiation(r1);
95
96 return res;
97 }
98
99
100 precomputeG1(p) {
101 const Pcopy = this.G1.affine(p);
102
103 const res = {};
104 res.PX = Pcopy[0];
105 res.PY = Pcopy[1];
106
107 return res;
108 }
109
110 precomputeG2(p) {
111
112 const Qcopy = this.G2.affine(p);
113
114 const res = {
115 QX: Qcopy[0],
116 QY: Qcopy[1],
117 coeffs: []
118 };
119
120 const R = {
121 X: Qcopy[0],
122 Y: Qcopy[1],
123 Z: this.F2.one
124 };
125
126 let c;
127
128 for (let i = this.loop_count_bits.length-2; i >= 0; --i)
129 {
130 const bit = this.loop_count_bits[i];
131
132 c = this._doubleStep(R);
133 res.coeffs.push(c);
134
135 if (bit)
136 {
137 c = this._addStep(Qcopy, R);
138 res.coeffs.push(c);
139 }
140 }
141
142 const Q1 = this.G2.affine(this._g2MulByQ(Qcopy));
143 assert(this.F2.equals(Q1[2], this.F2.one));
144 const Q2 = this.G2.affine(this._g2MulByQ(Q1));
145 assert(this.F2.equals(Q2[2], this.F2.one));
146
147 if (this.loopCountNef)
148 {
149 R.Y = this.F2.neg(R.Y);
150 }
151 Q2[1] = this.F2.neg(Q2[1]);
152
153 c = this._addStep(Q1, R);
154 res.coeffs.push(c);
155
156 c = this._addStep(Q2, R);
157 res.coeffs.push(c);
158
159 return res;
160 }
161
162 millerLoop(pre1, pre2) {
163 let f = this.F12.one;
164
165 let idx = 0;
166
167 let c;
168
169 for (let i = this.loop_count_bits.length-2; i >= 0; --i)
170 {
171 const bit = this.loop_count_bits[i];
172
173 /* code below gets executed for all bits (EXCEPT the MSB itself) of
174 alt_bn128_param_p (skipping leading zeros) in MSB to LSB
175 order */
176
177 c = pre2.coeffs[idx++];
178 f = this.F12.square(f);
179 f = this._mul_by_024(
180 f,
181 c.ell_0,
182 this.F2.mulEscalar(c.ell_VW , pre1.PY),
183 this.F2.mulEscalar(c.ell_VV , pre1.PX, ));
184
185 if (bit)
186 {
187 c = pre2.coeffs[idx++];
188 f = this._mul_by_024(
189 f,
190 c.ell_0,
191 this.F2.mulEscalar(c.ell_VW, pre1.PY, ),
192 this.F2.mulEscalar(c.ell_VV, pre1.PX, ));
193 }
194
195 }
196
197 if (this.loopCountNef)
198 {
199 f = this.F12.inverse(f);
200 }
201
202 c = pre2.coeffs[idx++];
203 f = this._mul_by_024(
204 f,
205 c.ell_0,
206 this.F2.mulEscalar(c.ell_VW, pre1.PY),
207 this.F2.mulEscalar(c.ell_VV, pre1.PX));
208
209 c = pre2.coeffs[idx++];
210 f = this._mul_by_024(
211 f,
212 c.ell_0,
213 this.F2.mulEscalar(c.ell_VW, pre1.PY, ),
214 this.F2.mulEscalar(c.ell_VV, pre1.PX));
215
216 return f;
217 }
218
219 finalExponentiation(elt) {
220 // TODO: There is an optimization in FF
221
222 const res = this.F12.exp(elt,this.final_exponent);
223
224 return res;
225 }
226
227 _doubleStep(current) {
228 const X = current.X;
229 const Y = current.Y;
230 const Z = current.Z;
231
232 const A = this.F2.mulEscalar(this.F2.mul(X,Y), this.two_inv); // A = X1 * Y1 / 2
233 const B = this.F2.square(Y); // B = Y1^2
234 const C = this.F2.square(Z); // C = Z1^2
235 const D = this.F2.add(C, this.F2.add(C,C)); // D = 3 * C
236 const E = this.F2.mul(this.twist_coeff_b, D); // E = twist_b * D
237 const F = this.F2.add(E, this.F2.add(E,E)); // F = 3 * E
238 const G =
239 this.F2.mulEscalar(
240 this.F2.add( B , F ),
241 this.two_inv); // G = (B+F)/2
242 const H =
243 this.F2.sub(
244 this.F2.square( this.F2.add(Y,Z) ),
245 this.F2.add( B , C)); // H = (Y1+Z1)^2-(B+C)
246 const I = this.F2.sub(E, B); // I = E-B
247 const J = this.F2.square(X); // J = X1^2
248 const E_squared = this.F2.square(E); // E_squared = E^2
249
250 current.X = this.F2.mul( A, this.F2.sub(B,F) ); // X3 = A * (B-F)
251 current.Y =
252 this.F2.sub(
253 this.F2.sub( this.F2.square(G) , E_squared ),
254 this.F2.add( E_squared , E_squared )); // Y3 = G^2 - 3*E^2
255 current.Z = this.F2.mul( B, H ); // Z3 = B * H
256 const c = {
257 ell_0 : this.F2.mul( I, this.twist), // ell_0 = xi * I
258 ell_VW: this.F2.neg( H ), // ell_VW = - H (later: * yP)
259 ell_VV: this.F2.add( J , this.F2.add(J,J) ) // ell_VV = 3*J (later: * xP)
260 };
261
262 return c;
263 }
264
265 _addStep(base, current) {
266
267 const X1 = current.X;
268 const Y1 = current.Y;
269 const Z1 = current.Z;
270 const x2 = base[0];
271 const y2 = base[1];
272
273 const D = this.F2.sub( X1, this.F2.mul(x2,Z1) ); // D = X1 - X2*Z1
274 const E = this.F2.sub( Y1, this.F2.mul(y2,Z1) ); // E = Y1 - Y2*Z1
275 const F = this.F2.square(D); // F = D^2
276 const G = this.F2.square(E); // G = E^2
277 const H = this.F2.mul(D,F); // H = D*F
278 const I = this.F2.mul(X1,F); // I = X1 * F
279 const J =
280 this.F2.sub(
281 this.F2.add( H, this.F2.mul(Z1,G) ),
282 this.F2.add( I, I )); // J = H + Z1*G - (I+I)
283
284 current.X = this.F2.mul( D , J ); // X3 = D*J
285 current.Y =
286 this.F2.sub(
287 this.F2.mul( E , this.F2.sub(I,J) ),
288 this.F2.mul( H , Y1)); // Y3 = E*(I-J)-(H*Y1)
289 current.Z = this.F2.mul(Z1,H);
290 const c = {
291 ell_0 :
292 this.F2.mul(
293 this.twist,
294 this.F2.sub(
295 this.F2.mul(E , x2),
296 this.F2.mul(D , y2))), // ell_0 = xi * (E * X2 - D * Y2)
297 ell_VV : this.F2.neg(E), // ell_VV = - E (later: * xP)
298 ell_VW : D // ell_VW = D (later: * yP )
299 };
300
301 return c;
302 }
303
304 _mul_by_024(a, ell_0, ell_VW, ell_VV) {
305
306 // Old implementation
307
308 const b = [
309 [ell_0, this.F2.zero, ell_VV],
310 [this.F2.zero, ell_VW, this.F2.zero]
311 ];
312
313 return this.F12.mul(a,b);
314
315 /*
316 // This is a new implementation,
317 // But it does not look worthy
318 // at least in javascript.
319
320 let z0 = a[0][0];
321 let z1 = a[0][1];
322 let z2 = a[0][2];
323 let z3 = a[1][0];
324 let z4 = a[1][1];
325 let z5 = a[1][2];
326
327 const x0 = ell_0;
328 const x2 = ell_VV;
329 const x4 = ell_VW;
330
331 const D0 = this.F2.mul(z0, x0);
332 const D2 = this.F2.mul(z2, x2);
333 const D4 = this.F2.mul(z4, x4);
334 const t2 = this.F2.add(z0, z4);
335 let t1 = this.F2.add(z0, z2);
336 const s0 = this.F2.add(this.F2.add(z1,z3),z5);
337
338 // For z.a_.a_ = z0.
339 let S1 = this.F2.mul(z1, x2);
340 let T3 = this.F2.add(S1, D4);
341 let T4 = this.F2.add( this.F2.mul(this.nonResidueF6, T3),D0);
342 z0 = T4;
343
344 // For z.a_.b_ = z1
345 T3 = this.F2.mul(z5, x4);
346 S1 = this.F2.add(S1, T3);
347 T3 = this.F2.add(T3, D2);
348 T4 = this.F2.mul(this.nonResidueF6, T3);
349 T3 = this.F2.mul(z1, x0);
350 S1 = this.F2.add(S1, T3);
351 T4 = this.F2.add(T4, T3);
352 z1 = T4;
353
354 // For z.a_.c_ = z2
355 let t0 = this.F2.add(x0, x2);
356 T3 = this.F2.sub(
357 this.F2.mul(t1, t0),
358 this.F2.add(D0, D2));
359 T4 = this.F2.mul(z3, x4);
360 S1 = this.F2.add(S1, T4);
361 T3 = this.F2.add(T3, T4);
362
363 // For z.b_.a_ = z3 (z3 needs z2)
364 t0 = this.F2.add(z2, z4);
365 z2 = T3;
366 t1 = this.F2.add(x2, x4);
367 T3 = this.F2.sub(
368 this.F2.mul(t0,t1),
369 this.F2.add(D2, D4));
370
371 T4 = this.F2.mul(this.nonResidueF6, T3);
372 T3 = this.F2.mul(z3, x0);
373 S1 = this.F2.add(S1, T3);
374 T4 = this.F2.add(T4, T3);
375 z3 = T4;
376
377 // For z.b_.b_ = z4
378 T3 = this.F2.mul(z5, x2);
379 S1 = this.F2.add(S1, T3);
380 T4 = this.F2.mul(this.nonResidueF6, T3);
381 t0 = this.F2.add(x0, x4);
382 T3 = this.F2.sub(
383 this.F2.mul(t2,t0),
384 this.F2.add(D0, D4));
385 T4 = this.F2.add(T4, T3);
386 z4 = T4;
387
388 // For z.b_.c_ = z5.
389 t0 = this.F2.add(this.F2.add(x0, x2), x4);
390 T3 = this.F2.sub(this.F2.mul(s0, t0), S1);
391 z5 = T3;
392
393 return [
394 [z0, z1, z2],
395 [z3, z4, z5]
396 ];
397
398 */
399
400 }
401
402 _g2MulByQ(p) {
403 const fmx = [p[0][0], this.F1.mul(p[0][1], this.frobenius_coeffs_c1_1 )];
404 const fmy = [p[1][0], this.F1.mul(p[1][1], this.frobenius_coeffs_c1_1 )];
405 const fmz = [p[2][0], this.F1.mul(p[2][1], this.frobenius_coeffs_c1_1 )];
406 return [
407 this.F2.mul(this.twist_mul_by_q_X , fmx),
408 this.F2.mul(this.twist_mul_by_q_Y , fmy),
409 fmz
410 ];
411 }
412}
413
414module.exports = BN128;