Pedersen2 and BitPoints MulFix and MulAny
jump to
@@ -0,0 +1,14 @@
+ +include "compconstant.circom"; + + +template AliasCheck() { + + signal input in[254]; + + component compConstant = CompConstant(-1); + + for (var i=0; i<254; i++) in[i] ==> compConstant.in[i]; + + compConstant.out === 0; +}
@@ -29,3 +29,36 @@
yout <-- (delta - a * epsilon) / (1-d*tau); (1-d*tau)*yout === (delta - a * epsilon); } + +template BabyDbl() { + signal input x; + signal input y; + signal output xout; + signal output yout; + + component adder = BabyAdd(); + adder.x1 <== x; + adder.y1 <== y; + adder.x2 <== x; + adder.y2 <== y; + + adder.xout ==> xout; + adder.yout ==> yout; +} + + +template BabyCheck() { + signal input x; + signal input y; + + signal x2; + signal y2; + + var a = 168700; + var d = 168696; + + x2 <== x*x; + y2 <== y*y; + + a*x2 + y2 === 1 + d*x2*y2; +}
@@ -0,0 +1,56 @@
+ +include "../node_modules/circom/circuits/bitify.circom"; + +// Returns 1 if in (in binary) > ct + +template CompConstant(ct) { + signal input in[254]; + signal output out; + + signal parts[127]; + signal sout; + + var clsb; + var cmsb; + var slsb; + var smsb; + + var sum=0; + + var b = (1 << 128) -1; + var a = 1; + var e = 1; + var i; + + for (i=0;i<127; i++) { + clsb = (ct >> (i*2)) & 1; + cmsb = (ct >> (i*2+1)) & 1; + slsb = in[i*2]; + smsb = in[i*2+1]; + + + if ((cmsb==0)&(clsb==0)) { + parts[i] <== -b*smsb*slsb + b*smsb + b*slsb; + } else if ((cmsb==0)&(clsb==1)) { + parts[i] <== a*smsb*slsb - a*slsb + b*smsb - a*smsb + a; + } else if ((cmsb==1)&(clsb==0)) { + parts[i] <== b*smsb*slsb - a*smsb + a; + } else { + parts[i] <== -a*smsb*slsb + a; + } + + sum = sum + parts[i]; + + b = b -e; + a = a +e; + e = e*2; + } + + sout <== sum; + + component num2bits = Num2Bits(135); + + num2bits.in <== sout; + + out <== num2bits.out[127]; +}
@@ -1,22 +1,116 @@
+include "../node_modules/circom/circuits/bitify.circom"; +include "../node_modules/circom/circuits/comparators.circom"; +include "escalarmulany.circom"; +include "babyjub.circom"; +templete EdDSAVerfier(n) { + signal input msg[n]; + + signal input A[256]; + signal input R8[256]; + signal input S[256]; + + signal Ax; + signal Ay; + + signal R8x; + signal R8y; + + var i; + +// Ensure S<Subgroup Order + + component compConstant = CompConstant(2736030358979909402780800718157159386076813972158567259200215660948447373040); + + for (var i=0; i<254; i++) { + S[i] ==> compConstant.in[i]; + } + compConstant.out === 0; + S[255] === 0; + S[256] === 0; -templete Verfier() { - signal input hMsg[256]; +// Convert A to Field elements (And verify A) - signal input Ax; - signal input Ay; + component bits2pointA = Bits2Point_Strict(); - signal input Rx; - signal input Ry; + for (i=0; i<256; i++) { + bits2pointA.in[i] <== A[i]; + } + Ax <== bits2pointA.out[0]; + Ay <== bits2pointA.out[1]; - signal input s[256]; +// Convert R8 to Field elements (And verify R8) + component bits2pointR8 = Bits2Point_Strict(); - componet exps = Exp(); - component exph = Exp(); + for (i=0; i<256; i++) { + bits2pointR8.in[i] <== R8[i]; + } + R8x <== bits2pointR8.out[0]; + R8y <== bits2pointR8.out[1]; + +// Calculate the h = H(R,A, msg) - component adder = BabyAdd(); + component hash = Pedersen(512+n); + for (i=0; i<256; i++) { + hash.in[i] <== R[i]; + hash.in[256+i] <== A[i]; + } + for (i=0; i<n; i++) { + hash.in[512+i] <== msg[i]; + } + +// Calculate second part of the right side: right2 = h*8*A + + // Multiply by 8 by adding it 3 times. This also ensure that the result is in + // the subgroup. + component dbl1 = BabyDbl(); + dbl1.x <== Ax; + dbl1.y <== Ay; + component dbl2 = BabyDbl(); + dbl2.x <== dbl1.outx; + dbl2.y <== dbl1.outy; + component dbl3 = BabyDbl(); + dbl3.x <== dbl2.outx; + dbl3.y <== dbl2.outy; + + // We check that A is not zero. + component isZero = IsZero(); + isZero.in <== dbl3.x; + isZero.out === 0; + + component mulAny = EscalarMulAny(256); + for (i=0; i<256; i++) { + mulAny.e[i] <== hash.out[i]; + } + mulAny.p[0] <== dbl3.outx; + mulAny.p[1] <== dbl3.outy; + + +// Compute the right side: right = R8 + right2 + + component addRight = BabyAdd(); + addRight.x1 <== R8x; + addRight.y1 <== R8y; + addRight.x2 <== mulAny.out[0]; + addRight.y2 <== mulAny.out[1]; + +// Calculate left side of equation left = S*B8 + + var BASE8 = [ + 17777552123799933955779906779655732241715742912184938656739573121738514868268, + 2626589144620713026669568689430873010625803728049924121243784502389097019475 + ]; + component mulFix = EscalarMulFix(256, BASE8); + for (i=0; i<256; i++) { + mulFix.e[i] <== S[i]; + } + +// Do the comparation left == right + + mulFix.out[0] === addRight.xout; + mulFix.out[1] === addRight.yout; }
@@ -0,0 +1,174 @@
+ +include "montgomery.circom"; +include "babyjub.circom"; + +template Multiplexor2() { + signal input sel; + signal input in[2][2]; + signal output out[2]; + + out[0] <== (in[1][0] - in[0][0])*sel + in[0][0]; + out[1] <== (in[1][1] - in[0][1])*sel + in[0][1]; +} + +template BitElement() { + signal input sel; + signal input dblIn[2]; + signal input addIn[2]; + signal output dblOut[2]; + signal output addOut[2]; + + component doubler = MontgomeryDouble(); + component adder = MontgomeryAdd(); + component selector = Multiplexor2(); + + + sel ==> selector.sel; + + dblIn[0] ==> doubler.in[0]; + dblIn[1] ==> doubler.in[1]; + doubler.out[0] ==> adder.in1[0]; + doubler.out[1] ==> adder.in1[1]; + addIn[0] ==> adder.in2[0]; + addIn[1] ==> adder.in2[1]; + addIn[0] ==> selector.in[0][0]; + addIn[1] ==> selector.in[0][1]; + adder.out[0] ==> selector.in[1][0]; + adder.out[1] ==> selector.in[1][1]; + + doubler.out[0] ==> dblOut[0]; + doubler.out[1] ==> dblOut[1]; + selector.out[0] ==> addOut[0]; + selector.out[1] ==> addOut[1]; +} + +// p is montgomery point +// n must be <= 248 +// returns out in twisted edwards +// Double is in montgomery to be linked; + +template Segment(n) { + signal input e[n]; + signal input p[2]; + signal output out[2]; + signal output dbl[2]; + + component bits[n-1]; + + component e2m = Edwards2Montgomery(); + + p[0] ==> e2m.in[0]; + p[1] ==> e2m.in[1]; + + var i; + + bits[0] = BitElement(); + e2m.out[0] ==> bits[0].dblIn[0] + e2m.out[1] ==> bits[0].dblIn[1] + e2m.out[0] ==> bits[0].addIn[0] + e2m.out[1] ==> bits[0].addIn[1] + e[1] ==> bits[0].sel; + + for (i=1; i<n-1; i++) { + bits[i] = BitElement(); + + bits[i-1].dblOut[0] ==> bits[i].dblIn[0] + bits[i-1].dblOut[1] ==> bits[i].dblIn[1] + bits[i-1].addOut[0] ==> bits[i].addIn[0] + bits[i-1].addOut[1] ==> bits[i].addIn[1] + e[i+1] ==> bits[i].sel; + } + + bits[n-2].dblOut[0] ==> dbl[0]; + bits[n-2].dblOut[1] ==> dbl[1]; + + component m2e = Montgomery2Edwards(); + + bits[n-2].addOut[0] ==> m2e.in[0]; + bits[n-2].addOut[1] ==> m2e.in[1]; + + component eadder = BabyAdd(); + + m2e.out[0] ==> eadder.x1; + m2e.out[1] ==> eadder.y1; + -p[0] ==> eadder.x2; + p[1] ==> eadder.y2; + + component lastSel = Multiplexor2(); + + e[0] ==> lastSel.sel; + eadder.xout ==> lastSel.in[0][0]; + eadder.yout ==> lastSel.in[0][1]; + m2e.out[0] ==> lastSel.in[1][0]; + m2e.out[1] ==> lastSel.in[1][1]; + + lastSel.out[0] ==> out[0]; + lastSel.out[1] ==> out[1]; +} + +// This function assumes that p is in the subgroup and it is different to 0 + +template EscalarMulAny(n) { + signal input e[n]; // Input in binary format + signal input p[2]; // Point (Twisted format) + signal output out[2]; // Point (Twisted format) + + var nsegments = (n-1)\148 +1; + var nlastsegment = n - (nsegments-1)*148; + + component segments[nsegments]; + component doublers[nsegments-1]; + component m2e[nsegments-1]; + component adders[nsegments-1]; + + var s; + var i; + var nseg; + + for (s=0; s<nsegments; s++) { + + nseg = (s < nsegments-1) ? 148 : nlastsegment; + + segments[s] = Segment(nseg); + + for (i=0; i<nseg; i++) { + e[s*148+i] ==> segments[s].e[i]; + } + + if (s==0) { + p[0] ==> segments[s].p[0]; + p[1] ==> segments[s].p[1]; + } else { + doublers[s-1] = MontgomeryDouble(); + m2e[s-1] = Montgomery2Edwards(); + adders[s-1] = BabyAdd(); + + segments[s-1].dbl[0] ==> doublers[s-1].in[0]; + segments[s-1].dbl[1] ==> doublers[s-1].in[1]; + + doublers[s-1].out[0] ==> m2e[s-1].in[0]; + doublers[s-1].out[1] ==> m2e[s-1].in[1]; + + m2e[s-1].out[0] ==> segments[s].p[0]; + m2e[s-1].out[1] ==> segments[s].p[1]; + + if (s==1) { + segments[s-1].out[0] ==> adders[s-1].x1; + segments[s-1].out[1] ==> adders[s-1].y1; + } else { + adders[s-2].xout ==> adders[s-1].x1; + adders[s-2].yout ==> adders[s-1].y1; + } + segments[s].out[0] ==> adders[s-1].x2; + segments[s].out[1] ==> adders[s-1].y2; + } + } + + if (nsegments == 1) { + segments[0].out[0] ==> out[0]; + segments[0].out[1] ==> out[1]; + } else { + adders[nsegments-2].xout ==> out[0]; + adders[nsegments-2].yout ==> out[1]; + } +}
@@ -0,0 +1,258 @@
+include "mux3.circom"; +include "montgomery.circom"; +include "babyjub.circom"; + +/* + Window of 3 elements, it calculates + out = base + base*in[0] + 2*base*in[1] + 4*base*in[2] + out4 = 4*base + + The result should be compensated. + */ +template WindowMulFix() { + signal input in[3]; + signal input base[2]; + signal output out[2]; + signal output out8[2]; // Returns 8*Base (To be linked) + + component mux = MultiMux3(2); + + mux.s[0] <== in[0]; + mux.s[1] <== in[1]; + mux.s[2] <== in[2]; + + component dbl2 = MontgomeryDouble(); + component adr3 = MontgomeryAdd(); + component adr4 = MontgomeryAdd(); + component adr5 = MontgomeryAdd(); + component adr6 = MontgomeryAdd(); + component adr7 = MontgomeryAdd(); + component adr8 = MontgomeryAdd(); + +// in[0] -> 1*BASE + + mux.c[0][0] <== base[0]; + mux.c[1][0] <== base[1]; + +// in[1] -> 2*BASE + dbl2.in[0] <== base[0]; + dbl2.in[1] <== base[1]; + mux.c[0][1] <== dbl2.out[0]; + mux.c[1][1] <== dbl2.out[1]; + +// in[2] -> 3*BASE + adr3.in1[0] <== base[0]; + adr3.in1[1] <== base[1]; + adr3.in2[0] <== dbl2.out[0]; + adr3.in2[1] <== dbl2.out[1]; + mux.c[0][2] <== adr3.out[0]; + mux.c[1][2] <== adr3.out[1]; + +// in[3] -> 4*BASE + adr4.in1[0] <== base[0]; + adr4.in1[1] <== base[1]; + adr4.in2[0] <== adr3.out[0]; + adr4.in2[1] <== adr3.out[1]; + mux.c[0][3] <== adr4.out[0]; + mux.c[1][3] <== adr4.out[1]; + +// in[4] -> 5*BASE + adr5.in1[0] <== base[0]; + adr5.in1[1] <== base[1]; + adr5.in2[0] <== adr4.out[0]; + adr5.in2[1] <== adr4.out[1]; + mux.c[0][4] <== adr5.out[0]; + mux.c[1][4] <== adr5.out[1]; + +// in[5] -> 6*BASE + adr6.in1[0] <== base[0]; + adr6.in1[1] <== base[1]; + adr6.in2[0] <== adr5.out[0]; + adr6.in2[1] <== adr5.out[1]; + mux.c[0][5] <== adr6.out[0]; + mux.c[1][5] <== adr6.out[1]; + +// in[6] -> 7*BASE + adr7.in1[0] <== base[0]; + adr7.in1[1] <== base[1]; + adr7.in2[0] <== adr6.out[0]; + adr7.in2[1] <== adr6.out[1]; + mux.c[0][6] <== adr7.out[0]; + mux.c[1][6] <== adr7.out[1]; + +// in[7] -> 8*BASE + adr8.in1[0] <== base[0]; + adr8.in1[1] <== base[1]; + adr8.in2[0] <== adr7.out[0]; + adr8.in2[1] <== adr7.out[1]; + mux.c[0][7] <== adr8.out[0]; + mux.c[1][7] <== adr8.out[1]; + + out8[0] <== adr8.out[0]; + out8[1] <== adr8.out[1]; + + out[0] <== mux.out[0]; + out[1] <== mux.out[1]; +} + + +/* + This component does a multiplication of a escalar times a fix base + Signals: + e: The scalar in bits + base: the base point in edwards format + out: The result + dbl: Point in Edwards to be linked to the next segment. + */ + +template SegmentMulFix(nWindows) { + signal input e[nWindows*3]; + signal input base[2]; + signal output out[2]; + signal output dbl[2]; + + var i; + var j; + + // Convert the base to montgomery + + component e2m = Edwards2Montgomery(); + e2m.in[0] <== base[0]; + e2m.in[1] <== base[1]; + + component windows[nWindows]; + component adders[nWindows-1]; + component cadders[nWindows-1]; + for (i=0; i<nWindows; i++) { + windows[i] = WindowMulFix(); + if (i==0) { + windows[i].base[0] <== e2m.out[0]; + windows[i].base[1] <== e2m.out[1]; + } else { + windows[i].base[0] <== windows[i-1].out8[0]; + windows[i].base[1] <== windows[i-1].out8[1]; + + adders[i-1] = MontgomeryAdd(); + cadders[i-1] = MontgomeryAdd(); + if (i==1) { + adders[i-1].in1[0] <== windows[0].out[0]; + adders[i-1].in1[1] <== windows[0].out[1]; + cadders[i-1].in1[0] <== e2m.out[0]; + cadders[i-1].in1[1] <== e2m.out[1]; + } else { + adders[i-1].in1[0] <== adders[i-2].out[0]; + adders[i-1].in1[1] <== adders[i-2].out[1]; + cadders[i-1].in1[0] <== cadders[i-2].out[0]; + cadders[i-1].in1[1] <== cadders[i-2].out[1]; + } + adders[i-1].in2[0] <== windows[i].out[0]; + adders[i-1].in2[1] <== windows[i].out[1]; + cadders[i-1].in2[0] <== windows[i-1].out8[0]; + cadders[i-1].in2[1] <== windows[i-1].out8[1]; + } + for (j=0; j<3; j++) { + windows[i].in[j] <== e[3*i+j]; + } + } + + component m2e = Montgomery2Edwards(); + component cm2e = Montgomery2Edwards(); + + if (nWindows > 1) { + m2e.in[0] <== adders[nWindows-2].out[0]; + m2e.in[1] <== adders[nWindows-2].out[1]; + cm2e.in[0] <== cadders[nWindows-2].out[0]; + cm2e.in[1] <== cadders[nWindows-2].out[1]; + } else { + m2e.in[0] <== windows[0].out[0]; + m2e.in[1] <== windows[0].out[1]; + cm2e.in[0] <== e2m.out[0]; + cm2e.in[1] <== e2m.out[1]; + } + + component cAdd = BabyAdd(); + cAdd.x1 <== m2e.out[0]; + cAdd.y1 <== m2e.out[1]; + cAdd.x2 <== -cm2e.out[0]; + cAdd.y2 <== cm2e.out[1]; + + + cAdd.xout ==> out[0]; + cAdd.yout ==> out[1]; + + windows[nWindows-1].out8[0] ==> dbl[0]; + windows[nWindows-1].out8[1] ==> dbl[1]; +} + + +/* +This component multiplies a escalar times a fixed point BASE (twisted edwards format) + Signals + e: The escalar in binary format + out: The output point in twisted edwards + */ +template EscalarMulFix(n, BASE) { + signal input e[n]; // Input in binary format + signal output out[2]; // Point (Twisted format) + + var nsegments = (n-1)\249 +1; + var nlastsegment = n - (nsegments-1)*249; + + component segments[nsegments]; + + component m2e[nsegments-1]; + component adders[nsegments-1]; + + var s; + var i; + var nseg; + var nWindows + + for (s=0; s<nsegments; s++) { + + nseg = (s < nsegments-1) ? 249 : nlastsegment; + nWindows = ((nseg - 1)\3)+1; + + segments[s] = SegmentMulFix(nWindows); + + for (i=0; i<nseg; i++) { + segments[s].e[i] <== e[s*249+i]; + } + + for (i = nseg; i<nWindows*3; i++) { + segments[s].e[i] <== 0; + } + + if (s==0) { + segments[s].base[0] <== BASE[0]; + segments[s].base[1] <== BASE[1]; + } else { + m2e[s-1] = Montgomery2Edwards(); + adders[s-1] = BabyAdd(); + + segments[s-1].dbl[0] ==> m2e[s-1].in[0]; + segments[s-1].dbl[1] ==> m2e[s-1].in[1]; + + m2e[s-1].out[0] ==> segments[s].base[0]; + m2e[s-1].out[1] ==> segments[s].base[1]; + + if (s==1) { + segments[s-1].out[0] ==> adders[s-1].x1; + segments[s-1].out[1] ==> adders[s-1].y1; + } else { + adders[s-2].xout ==> adders[s-1].x1; + adders[s-2].yout ==> adders[s-1].y1; + } + segments[s].out[0] ==> adders[s-1].x2; + segments[s].out[1] ==> adders[s-1].y2; + } + } + + if (nsegments == 1) { + segments[0].out[0] ==> out[0]; + segments[0].out[1] ==> out[1]; + } else { + adders[nsegments-2].xout ==> out[0]; + adders[nsegments-2].yout ==> out[1]; + } +}
@@ -0,0 +1,123 @@
+ +/* + Source: https://en.wikipedia.org/wiki/Montgomery_curve + + 1 + y 1 + y + [u, v] = [ ------- , ---------- ] + 1 - y (1 - y)x + + */ + +template Edwards2Montgomery() { + signal input in[2]; + signal output out[2]; + + out[0] <-- (1 + in[1]) / (1 - in[1]); + out[1] <-- out[0] / in[0]; + + + out[0] * (1-in[1]) === (1 + in[1]); + out[1] * in[0] === out[0]; +} + +/* + + u u - 1 + [x, y] = [ ---, ------- ] + v u + 1 + + */ +template Montgomery2Edwards() { + signal input in[2]; + signal output out[2]; + + out[0] <-- in[0] / in[1]; + out[1] <-- (in[0] - 1) / (in[0] + 1); + + out[0] * in[1] === in[0]; + out[1] * (in[0] + 1) === in[0] - 1; +} + + +/* + x2 - x1 + lamda = --------- + y2 - y1 + + x3 + A + x1 + x2 + x3 = B * lamda^2 - A - x1 -x2 => lamda^2 = ------------------ + B + + y3 = (2*x1 + x2 + A)*lamda - B*lamda^3 - y1 => + + + => y3 = lamda * ( 2*x1 + x2 + A - x3 - A - x1 - x2) - y1 => + + => y3 = lamda * ( x1 - x3 ) - y1 + +---------- + + y2 - y1 + lamda = --------- + x2 - x1 + + x3 = B * lamda^2 - A - x1 -x2 + + y3 = lamda * ( x1 - x3 ) - y1 + + */ + +template MontgomeryAdd() { + signal input in1[2]; + signal input in2[2]; + signal output out[2]; + + var a = 168700; + var d = 168696; + + var A = (2 * (a + d)) / (a - d); + var B = 4 / (a - d); + + signal lamda; + + lamda <-- (in2[1] - in1[1]) / (in2[0] - in1[0]); + lamda * (in2[0] - in1[0]) === (in2[1] - in1[1]); + + out[0] <== B*lamda*lamda - A - in1[0] -in2[0]; + out[1] <== lamda * (in1[0] - out[0]) - in1[1]; +} + +/* + + x1_2 = x1*x1 + + 3*x1_2 + 2*A*x1 + 1 + lamda = --------------------- + 2*B*y1 + + x3 = B * lamda^2 - A - x1 -x1 + + y3 = lamda * ( x1 - x3 ) - y1 + + */ +template MontgomeryDouble() { + signal input in[2]; + signal output out[2]; + + var a = 168700; + var d = 168696; + + var A = (2 * (a + d)) / (a - d); + var B = 4 / (a - d); + + signal lamda; + signal x1_2; + + x1_2 <== in[0] * in[0]; + + lamda <-- (3*x1_2 + 2*A*in[0] + 1 ) / (2*B*in[1]); + lamda * (2*B*in[1]) === (3*x1_2 + 2*A*in[0] + 1 ); + + out[0] <== B*lamda*lamda - A - 2*in[0]; + out[1] <== lamda * (in[0] - out[0]) - in[1]; +}
@@ -0,0 +1,55 @@
+template MultiMux3(n) { + signal input c[n][8]; // Constants + signal input s[3]; // Selector + signal output out[n]; + + signal a210[n]; + signal a21[n]; + signal a20[n]; + signal a2[n]; + + signal a10[n]; + signal a1[n]; + signal a0[n]; + signal a[n]; + + // 4 constrains for the intermediary variables + signal s10; + s10 <== s[1] * s[0]; + + for (var i=0; i<n; i++) { + + a210[i] <== ( c[i][ 7]-c[i][ 6]-c[i][ 5]+c[i][ 4] - c[i][ 3]+c[i][ 2]+c[i][ 1]-c[i][ 0] ) * s10; + a21[i] <== ( c[i][ 6]-c[i][ 4]-c[i][ 2]+c[i][ 0] ) * s[1]; + a20[i] <== ( c[i][ 5]-c[i][ 4]-c[i][ 1]+c[i][ 0] ) * s[0]; + a2[i] <== ( c[i][ 4]-c[i][ 0] ); + + a10[i] <== ( c[i][ 3]-c[i][ 2]-c[i][ 1]+c[i][ 0] ) * s10; + a1[i] <== ( c[i][ 2]-c[i][ 0] ) * s[1]; + a0[i] <== ( c[i][ 1]-c[i][ 0] ) * s[0]; + a[i] <== ( c[i][ 0] ) + + out[i] <== ( a210[i] + a21[i] + a20[i] + a2[i] ) * s[2] + + ( a10[i] + a1[i] + a0[i] + a[i] ); + + } +} + +template Mux3() { + var i; + signal input c[8]; // Constants + signal input s[3]; // Selector + signal output out; + + component mux = MultiMux3(1); + + for (i=0; i<8; i++) { + mux.c[0][i] <== c[i]; + } + + for (i=0; i<3; i++) { + s[i] ==> mux.s[i]; + } + + mux.out[0] ==> out; +}
@@ -5,8 +5,8 @@ template Pedersen(n) {
signal input in[n]; signal output out[2]; - var nexps = ((n-1) \ 253) + 1; - var nlastbits = n - (nexps-1)*253; + var nexps = ((n-1) \ 250) + 1; + var nlastbits = n - (nexps-1)*250; component escalarMuls[nexps];@@ -27,11 +27,11 @@ var i;
var j; var nexpbits; for (i=0; i<nexps; i++) { - nexpbits = (i == nexps-1) ? nlastbits : 253; + nexpbits = (i == nexps-1) ? nlastbits : 250; escalarMuls[i] = EscalarMul(nexpbits, PBASE[i]); for (j=0; j<nexpbits; j++) { - escalarMuls[i].in[j] <== in[253*i + j]; + escalarMuls[i].in[j] <== in[250*i + j]; } if (i==0) {
@@ -0,0 +1,236 @@
+include "montgomery.circom"; +include "mux3.circom"; +include "babyjub.circom"; + +template Window4() { + signal input in[4]; + signal input base[2]; + signal output out[2]; + signal output out8[2]; // Returns 8*Base (To be linked) + + component mux = MultiMux3(2); + + mux.s[0] <== in[0]; + mux.s[1] <== in[1]; + mux.s[2] <== in[2]; + + component dbl2 = MontgomeryDouble(); + component adr3 = MontgomeryAdd(); + component adr4 = MontgomeryAdd(); + component adr5 = MontgomeryAdd(); + component adr6 = MontgomeryAdd(); + component adr7 = MontgomeryAdd(); + component adr8 = MontgomeryAdd(); + +// in[0] -> 1*BASE + + mux.c[0][0] <== base[0]; + mux.c[1][0] <== base[1]; + +// in[1] -> 2*BASE + dbl2.in[0] <== base[0]; + dbl2.in[1] <== base[1]; + mux.c[0][1] <== dbl2.out[0]; + mux.c[1][1] <== dbl2.out[1]; + +// in[2] -> 3*BASE + adr3.in1[0] <== base[0]; + adr3.in1[1] <== base[1]; + adr3.in2[0] <== dbl2.out[0]; + adr3.in2[1] <== dbl2.out[1]; + mux.c[0][2] <== adr3.out[0]; + mux.c[1][2] <== adr3.out[1]; + +// in[3] -> 4*BASE + adr4.in1[0] <== base[0]; + adr4.in1[1] <== base[1]; + adr4.in2[0] <== adr3.out[0]; + adr4.in2[1] <== adr3.out[1]; + mux.c[0][3] <== adr4.out[0]; + mux.c[1][3] <== adr4.out[1]; + +// in[4] -> 5*BASE + adr5.in1[0] <== base[0]; + adr5.in1[1] <== base[1]; + adr5.in2[0] <== adr4.out[0]; + adr5.in2[1] <== adr4.out[1]; + mux.c[0][4] <== adr5.out[0]; + mux.c[1][4] <== adr5.out[1]; + +// in[5] -> 6*BASE + adr6.in1[0] <== base[0]; + adr6.in1[1] <== base[1]; + adr6.in2[0] <== adr5.out[0]; + adr6.in2[1] <== adr5.out[1]; + mux.c[0][5] <== adr6.out[0]; + mux.c[1][5] <== adr6.out[1]; + +// in[6] -> 7*BASE + adr7.in1[0] <== base[0]; + adr7.in1[1] <== base[1]; + adr7.in2[0] <== adr6.out[0]; + adr7.in2[1] <== adr6.out[1]; + mux.c[0][6] <== adr7.out[0]; + mux.c[1][6] <== adr7.out[1]; + +// in[7] -> 8*BASE + adr8.in1[0] <== base[0]; + adr8.in1[1] <== base[1]; + adr8.in2[0] <== adr7.out[0]; + adr8.in2[1] <== adr7.out[1]; + mux.c[0][7] <== adr8.out[0]; + mux.c[1][7] <== adr8.out[1]; + + out8[0] <== adr8.out[0]; + out8[1] <== adr8.out[1]; + + out[0] <== mux.out[0]; + out[1] <== - mux.out[1]*2*in[3] + mux.out[1]; // Negate y if in[3] is one +} + + +template Segment(nWindows) { + signal input in[nWindows*4]; + signal input base[2]; + signal output out[2]; + + var i; + var j; + + // Convert the base to montgomery + + component e2m = Edwards2Montgomery(); + e2m.in[0] <== base[0]; + e2m.in[1] <== base[1]; + + component windows[nWindows]; + component doublers1[nWindows-1]; + component doublers2[nWindows-1]; + component adders[nWindows-1]; + for (i=0; i<nWindows; i++) { + windows[i] = Window4(); + if (i==0) { + windows[i].base[0] <== e2m.out[0]; + windows[i].base[1] <== e2m.out[1]; + } else { + doublers1[i-1] = MontgomeryDouble(); + doublers2[i-1] = MontgomeryDouble(); + doublers1[i-1].in[0] <== windows[i-1].out8[0]; + doublers1[i-1].in[1] <== windows[i-1].out8[1]; + doublers2[i-1].in[0] <== doublers1[i-1].out[0]; + doublers2[i-1].in[1] <== doublers1[i-1].out[1]; + + windows[i].base[0] <== doublers2[i-1].out[0]; + windows[i].base[1] <== doublers2[i-1].out[1]; + + adders[i-1] = MontgomeryAdd(); + if (i==1) { + adders[i-1].in1[0] <== windows[0].out[0]; + adders[i-1].in1[1] <== windows[0].out[1]; + } else { + adders[i-1].in1[0] <== adders[i-2].out[0]; + adders[i-1].in1[1] <== adders[i-2].out[1]; + } + adders[i-1].in2[0] <== windows[i].out[0]; + adders[i-1].in2[1] <== windows[i].out[1]; + } + for (j=0; j<4; j++) { + windows[i].in[j] <== in[4*i+j]; + } + } + + component m2e = Montgomery2Edwards(); + + if (nWindows > 1) { + m2e.in[0] <== adders[nWindows-2].out[0]; + m2e.in[1] <== adders[nWindows-2].out[1]; + } else { + m2e.in[0] <== windows[0].out[0]; + m2e.in[1] <== windows[0].out[1]; + } + + out[0] <== m2e.out[0]; + out[1] <== m2e.out[1]; +} + +template Pedersen(n) { + signal input in[n]; + signal output out[2]; + + var BASE = [ + [7889815880984390413826091016397158135734961432619494935997950708325418623781,8846020814737052626835496416415322522216827521798085437978304928900248828704], + [12932435660254426850246080929365951045207624124386035886549006330955720993567,15876660444082442781217588393435527739441124986236154572507597829115005542086], + [2482397177297734131621151094340467680859038448217226675361423673093734165962,10039279516804305991696249700635360957313934801940294703211894781106216299926], + [17157815998940296936592098789990444736073034804807810484873853349962905015352,6488208869655503622669430389521947006738035600928015942696596112432120303604], + [264004460746169389447419243214191481604172623204375600962322511417379874376,2415858116338771134001541482988382151008857516531390792628421155957250972277], + [4135925743285698117252356077971179769271452015650275231796007492648697405139,10188226868678337759614729372197905253307539893323271103976079007344248400845], + [1774758779250924961062140611815304699163957993414252473010092444201412186500,4347026286058522695608532575722049241297833321096891696953943795644684841805], + [7879866447646097585900946926276218605564915618236971624614091698429769712458,2093592432852088858177276030443845730480437238346603396739626046140688969347], + [8298560996095230984182228319122592575131718101813938808256495049817179791777,1767915891871602626938298102360238720016341966012238026281701463959008338852], + [10415885340847357003805466620366840573458521568359796855704531856219635265921,3432650026491357206165099540731361444311747596326968441647905394914712226413] + ] + + var nSegments = ((n-1)\200)+1; + + component segments[nSegments]; + + var i; + var j; + var nBits; + var nWindows; + for (i=0; i<nSegments; i++) { + nBits = (i == (nSegments-1)) ? n - (nSegments-1)*200 : 200; + nWindows = ((nBits - 1)\4)+1; + segments[i] = Segment(nWindows); + segments[i].base[0] <== BASE[i][0]; + segments[i].base[1] <== BASE[i][1]; + for (j = 0; j<nBits; j++) { + segments[i].in[j] <== in[i*200+j]; + } + // Fill padding bits + for (j = nBits; j < nWindows*4; j++) { + segments[i].in[j] <== 0; + } + } + + component adders[nSegments-1]; + + for (i=0; i<nSegments-1; i++) { + adders[i] = BabyAdd(); + if (i==0) { + adders[i].x1 <== segments[0].out[0]; + adders[i].y1 <== segments[0].out[1]; + adders[i].x2 <== segments[1].out[0]; + adders[i].y2 <== segments[1].out[1]; + } else { + adders[i].x1 <== adders[i-1].xout; + adders[i].y1 <== adders[i-1].xout; + adders[i].x2 <== segments[i+1].out[0]; + adders[i].y2 <== segments[i+1].out[1]; + } + } + +/* + coponent packPoint = PackPoint(); + + if (nSegments>1) { + packPoint.in[0] <== adders[nSegments-2].xout; + packPoint.in[1] <== adders[nSegments-2].yout; + } else { + packPoint.in[0] <== segments[0].out[0]; + packPoint.in[1] <== segments[0].out[1]; + } + + out[0] <== packPoint.out[0]; + out[1] <== packPoint.out[1]; +*/ + + if (nSegments>1) { + out[0] <== adders[nSegments-2].xout; + out[1] <== adders[nSegments-2].yout; + } else { + out[0] <== segments[0].out[0]; + out[1] <== segments[0].out[1]; + } +} +
@@ -0,0 +1,144 @@
+include "../node_modules/circom/circuits/bitify.circom"; +include "aliascheck.circom"; +include "compconstant.circom"; +include "babyjub.circom"; + + +function sqrt(n) { + + if (n == 0) { + return 0; + } + + // Test that have solution + var res = n ** ((-1) >> 1); +// if (res!=1) assert(false, "SQRT does not exists"); + if (res!=1) return 0; + + var m = 28; + var c = 19103219067921713944291392827692070036145651957329286315305642004821462161904; + var t = n ** 81540058820840996586704275553141814055101440848469862132140264610111; + var r = n ** ((81540058820840996586704275553141814055101440848469862132140264610111+1)>>1); + var sq; + var i; + var b; + var j; + + while ((r != 0)&&(t != 1)) { + sq = t*t; + i = 1; + while (sq!=1) { + i++; + sq = sq*sq; + } + + // b = c ^ m-i-1 + b = c; + for (j=0; j< m-i-1; j ++) b = b*b; + + m = i; + c = b*b; + t = t*c; + r = r*b; + } + + if (r > ((-1) >> 1)) { + r = -r; + } + + return r; +} + + +template Bits2Point() { + signal input in[256]; + signal output out[2]; +} + +template Bits2Point_Strict() { + signal input in[256]; + signal output out[2]; + + var i; + + // Check aliasing + component aliasCheckY = AliasCheck(); + for (i=0; i<254; i++) { + aliasCheckY.in[i] <== in[i]; + } + in[254] === 0; + + component b2nY = Bits2Num(254); + for (i=0; i<254; i++) { + b2nY.in[i] <== in[i]; + } + + out[1] <== b2nY.out; + + var a = 168700; + var d = 168696; + + var y2 = out[1] * out[1]; + + var x = sqrt( (1-y2)/(a - d*y2) ); + + if (in[255] == 1) x = -x; + + out[0] <-- x; + + component babyCheck = BabyCheck(); + babyCheck.x <== out[0]; + babyCheck.y <== out[1]; + + component n2bX = Num2Bits(254); + n2bX.in <== out[0]; + component aliasCheckX = AliasCheck(); + for (i=0; i<254; i++) { + aliasCheckX.in[i] <== n2bX.out[i]; + } + + component signCalc = CompConstant(10944121435919637611123202872628637544274182200208017171849102093287904247808); + for (i=0; i<254; i++) { + signCalc.in[i] <== n2bX.out[i]; + } + + signCalc.out === in[255]; +} + + +template Point2Bits() { + signal input in[2]; + signal output out[256]; + + +} + +template Point2Bits_Strict() { + signal input in[2]; + signal output out[256]; + + var i; + + component n2bX = Num2Bits(254); + n2bX.in <== in[0]; + component n2bY = Num2Bits(254); + n2bY.in <== in[1]; + + component aliasCheckX = AliasCheck(); + component aliasCheckY = AliasCheck(); + for (i=0; i<254; i++) { + aliasCheckX.in[i] <== n2bX.out[i]; + aliasCheckY.in[i] <== n2bY.out[i]; + } + + component signCalc = CompConstant(10944121435919637611123202872628637544274182200208017171849102093287904247808); + for (i=0; i<254; i++) { + signCalc.in[i] <== n2bX.out[i]; + } + + for (i=0; i<254; i++) { + out[i] <== n2bY.out[i]; + } + out[254] <== 0; + out[255] <== signCalc.out; +}
@@ -0,0 +1,16 @@
+include "compconstant.circom"; + +template Sign() { + signal input in[254]; + signal output sign; + + component comp = CompConstant(10944121435919637611123202872628637544274182200208017171849102093287904247808); + + var i; + + for (i=0; i<254; i++) { + comp.in[i] <== in[i]; + } + + sign <== comp.out; +}
@@ -4,6 +4,17 @@
exports.addPoint = addPoint; exports.mulPointEscalar = mulPointEscalar; exports.inCurve = inCurve; +exports.inSubgroup = inSubgroup; +exports.packPoint = packPoint; +exports.unpackPoint = unpackPoint; +exports.Base8 = [ + bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"), + bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475") +]; +exports.order = bigInt("21888242871839275222246405745257275088614511777268538073601725287587578984328"); +exports.subOrder = exports.order.shr(3); +exports.p = bn128.r; + function addPoint(a,b) { const q = bn128.r;@@ -32,26 +43,63 @@
return res; } -function isLowGrade(p) { - const r = bigInt("21888242871839275222246405745257275088614511777268538073601725287587578984328").shr(3); - const res= mulPointEscalar(p, r); +function inSubgroup(P) { + if (!inCurve(P)) return false; + const res= mulPointEscalar(P, exports.subOrder); return (res[0].equals(bigInt(0))) && (res[1].equals(bigInt(1))); } -function inCurve(p) { +function inCurve(P) { const F = bn128.Fr; const a = bigInt("168700"); const d = bigInt("168696"); - const x2 = F.square(p[0]); - const y2 = F.square(p[1]); + const x2 = F.square(P[0]); + const y2 = F.square(P[1]); if (!F.equals( F.add(F.mul(a, x2), y2), F.add(F.one, F.mul(F.mul(x2, y2), d)))) return false; - if (!isLowGrade(p)) return false; - return true; } + +function packPoint(P) { + const buff = bigInt.leInt2Buff(P[1], 32); + if (P[0].greater(exports.p.shr(1))) { + buff[31] = buff[31] | 0x80; + } + return buff; +} + +function unpackPoint(_buff) { + const F = bn128.Fr; + + const buff = Buffer.from(_buff); + let sign = false; + const P = new Array(2); + if (buff[31] & 0x80) { + sign = true; + buff[31] = buff[31] & 0x7F; + } + P[1] = bigInt.leBuff2int(buff); + if (P[1].greaterOrEquals(exports.p)) return null; + + const a = bigInt("168700"); + const d = bigInt("168696"); + + const y2 = F.square(P[1]); + + let x = F.sqrt(F.div( + F.sub(F.one, y2), + F.sub(a, F.mul(d, y2)))); + + if (x == null) return null; + + if (sign) x = F.neg(x); + + P[0] = F.affine(x); + + return P; +}
@@ -0,0 +1,92 @@
+const createBlakeHash = require("blake-hash"); +const bigInt = require("snarkjs").bigInt; +const babyJub = require("./babyjub"); +const pedersenHash = require("./pedersenHash").hash; +const crypto = require("crypto"); + +exports.cratePrvKey = cratePrvKey; +exports.prv2pub= prv2pub; +exports.sign = sign; +exports.verify = verify; +exports.packSignature = packSignature; +exports.unpackSignature = unpackSignature; + + +function cratePrvKey() { + return crypto.randomBytes(32); +} + +function pruneBuffer(_buff) { + const buff = Buffer.from(_buff); + buff[0] = buff[0] & 0xF8; + buff[31] = buff[31] & 0x7F; + buff[31] = buff[31] | 0x40; +} + +function prv2pub(prv) { + const sBuff = pruneBuffer(createBlakeHash("blake512").update(prv).digest().slice(0,32)); + let s = bigInt.leBuff2int(sBuff); + const A = babyJub.mulPointEscalar(babyJub.Base8, s.shr(3)); + return A; +} + +function sign(prv, msg) { + const h1 = createBlakeHash("blake512").update(prv).digest(); + const sBuff = pruneBuffer(h1.slice(0,32)); + const s = bigInt.leBuff2int(sBuff); + const A = babyJub.mulPointEscalar(babyJub.Base8, s.shr(3)); + + const rBuff = createBlakeHash("blake512").update(Buffer.concat(h1.slice(32,64), msg)).digest(); + let r = bigInt.leBuff2int(rBuff); + r = r.mod(babyJub.subOrder); + const R8 = babyJub.mulPointEscalar(babyJub.Base8, r); + const R8p = babyJub.packPoint(R8); + const Ap = babyJub.packPoint(A); + const hmBuff = pedersenHash(Buffer.concat(R8p, Ap, msg)); + const hm = bigInt.leBuff2int(hmBuff); + const S = r.add(hm.mul(s)).mod(babyJub.subOrder); + return { + R8: R8, + S: S + }; +} + +function verify(msg, sig, A) { + // Check parameters + if (typeof sig != "object") return false; + if (!Array.isArray(sig.R8)) return false; + if (sig.R8.length!= 2) return false; + if (!babyJub.inCurve(sig.R8)) return false; + if (!Array.isArray(A)) return false; + if (A.length!= 2) return false; + if (!babyJub.inCurve(sig.A)) return false; + if (sig.S>= babyJub.subOrder) return false; + + const R8p = babyJub.packPoint(sig.R8); + const Ap = babyJub.packPoint(A); + const hmBuff = pedersenHash(Buffer.concat(R8p, Ap, msg)); + const hm = bigInt.leBuff2int(hmBuff); + + const Pleft = babyJub.mulPointEscalar(babyJub.Base8, sig.S); + let Pright = babyJub.mulPointEscalar(A, hm.mul(8)); + Pright = babyJub.addaddPoint(sig.R8, Pright); + + if (!Pleft[0].equals(Pright[0])) return false; + if (!Pleft[1].equals(Pright[1])) return false; + return true; +} + +function packSignature(sig) { + const R8p = babyJub.packPoint(sig.R8); + const Sp = bigInt.leInt2Buff(sig.S, 32); + return Buffer.concat(R8p, Sp); +} + +function unpackSignature(sigBuff) { + return { + R8: babyJub.unpackPoint(sigBuff.slice(0,32)), + S: bigInt.leBuff2int(sigBuff.slice(32,64)) + }; +} + +
@@ -0,0 +1,110 @@
+const bn128 = require("snarkjs").bn128; +const bigInt = require("snarkjs").bigInt; +const babyJub = require("./babyjub"); +const assert = require("assert"); +const createBlakeHash = require("blake-hash"); + +const GENPOINT_PREFIX = "Iden3_PedersenGenerator"; +const windowSize = 4; +const nWindowsPerSegment = 50; + +exports.hash = pedersenHash; +exports.getBasePoint = getBasePoint; + +function pedersenHash(msg) { + const bitsPerSegment = windowSize*nWindowsPerSegment; + const bits = buffer2bits(msg); + + const nSegments = Math.floor((bits.length - 1)/(windowSize*nWindowsPerSegment)) +1; + + let accP = [bigInt.zero,bigInt.one]; + + for (let s=0; s<nSegments; s++) { + let nWindows; + if (s == nSegments-1) { + nWindows = Math.floor(((bits.length - (nSegments - 1)*bitsPerSegment) - 1) / windowSize) +1; + } else { + nWindows = nWindowsPerSegment; + } + let escalar = bigInt.zero; + let exp = bigInt.one; + for (let w=0; w<nWindows; w++) { + let o = s*bitsPerSegment + w*windowSize; + let acc = bigInt.one; + for (let b=0; ((b<windowSize-1)&&(o<bits.length)) ; b++) { + if (bits[o]) { + acc = acc.add( bigInt.one.shl(b) ); + } + o++; + } + if (o<bits.length) { + if (bits[o]) { + acc = acc.neg(); + } + o++; + } + escalar = escalar.add(acc.mul(exp)); + exp = exp.shl(windowSize+1); + } + + if (escalar.lesser(bigInt.zero)) { + escalar = babyJub.subOrder.add(escalar); + } + + accP = babyJub.addPoint(accP, babyJub.mulPointEscalar(getBasePoint(s), escalar)); + } + + return babyJub.packPoint(accP); +} + +let bases = []; + +function getBasePoint(pointIdx) { + if (pointIdx<bases.length) return bases[pointIdx]; + let p= null; + let tryIdx = 0; + while (p==null) { + const S = GENPOINT_PREFIX + "_" + padLeftZeros(pointIdx, 32) + "_" + padLeftZeros(tryIdx, 32); + const h = createBlakeHash("blake256").update(S).digest(); + h[31] = h[31] & 0xBF; // Set 255th bit to 0 (256th is the signal and 254th is the last possible bit to 1) + p = babyJub.unpackPoint(h); + tryIdx++; + } + + const p8 = babyJub.mulPointEscalar(p, 8); + + assert(babyJub.inSubgroup(p8), "Point not in curve"); + + bases[pointIdx] = p8; + return p8; +} + +function padLeftZeros(idx, n) { + let sidx = "" + idx; + while (sidx.length<n) sidx = "0"+sidx; + return sidx; +} + +/* +Input a buffer +Returns an array of booleans. 0 is LSB of first byte and so on. + */ +function buffer2bits(buff) { + const res = new Array(buff.length*8); + for (let i=0; i<buff.length; i++) { + const b = buff[i]; + res[i*8] = b & 0x01; + res[i*8+1] = b & 0x02; + res[i*8+2] = b & 0x04; + res[i*8+3] = b & 0x08; + res[i*8+4] = b & 0x10; + res[i*8+5] = b & 0x20; + res[i*8+6] = b & 0x40; + res[i*8+7] = b & 0x80; + } + return res; +} + + + +
@@ -0,0 +1,13 @@
+const pedersenHash = require("./pedersenHash.js"); + +let nBases; +if (typeof process.argv[2] != "undefined") { + nBases = parseInt(process.argv[2]); +} else { + nBases = 5; +} + +for (let i=0; i < nBases; i++) { + const p = pedersenHash.getBasePoint(i); + console.log(`[${p[0]},${p[1]}]`); +}
@@ -0,0 +1,10 @@
+const snarkjs = require("snarkjs"); + +let n = 16352677002768649294638363183714474939219394808856154771098596513875516795430n + +let r = snarkjs.bn128.Fr.sqrt(n) + +r = snarkjs.bn128.Fr.q -r; + +console.log(r.toString()); +
@@ -0,0 +1,74 @@
+const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + +function print(circuit, w, s) { + console.log(s + ": " + w[circuit.getSignalIdx(s)]); +} + +function getBits(v, n) { + const res = []; + for (let i=0; i<n; i++) { + if (v.shr(i).isOdd()) { + res.push(bigInt.one); + } else { + res.push(bigInt.zero); + } + } + return res; +} + +const q = bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617"); + +describe("Aliascheck test", () => { + let circuit; + before( async() => { + const cirDef = await compiler(path.join(__dirname, "circuits", "aliascheck_test.circom")); + + circuit = new snarkjs.Circuit(cirDef); + + console.log("NConstrains: " + circuit.nConstraints); + }); + + it("Satisfy the aliastest 0", async () => { + const inp = getBits(bigInt.zero, 254); + circuit.calculateWitness({in: inp}); + }); + + it("Satisfy the aliastest 3", async () => { + const inp = getBits(bigInt(3), 254); + circuit.calculateWitness({in: inp}); + }); + + it("Satisfy the aliastest q-1", async () => { + const inp = getBits(q.sub(bigInt.one), 254); + circuit.calculateWitness({in: inp}); + }); + + it("Nhot not satisfy an input of q", async () => { + const inp = getBits(q, 254); + try { + circuit.calculateWitness({in: inp}); + assert(false); + } catch(err) { + assert.equal(err.message, "Constraint doesn't match: 1 != 0"); + } + }); + + it("Nhot not satisfy all ones", async () => { + + const inp = getBits(bigInt(1).shl(254).sub(bigInt(1)), 254); + try { + circuit.calculateWitness({in: inp}); + assert(false); + } catch(err) { + assert.equal(err.message, "Constraint doesn't match: 1 != 0"); + } + }); + +});
@@ -8,30 +8,35 @@
const bigInt = require("big-integer"); -describe("Baby Jub test", () => { - it("Should add point (0,1) and (0,1)", async () => { +describe("Baby Jub test", function () { + let circuitAdd; + let circuitTest; - const cirDef = await compiler(path.join(__dirname, "circuits", "babyadd_tester.circom")); + this.timeout(100000); -// console.log(JSON.stringify(cirDef, null, 1)); + before( async() => { + const cirDefAdd = await compiler(path.join(__dirname, "circuits", "babyadd_tester.circom")); + circuitAdd = new snarkjs.Circuit(cirDefAdd); + console.log("NConstrains BabyAdd: " + circuitAdd.nConstraints); -// assert.equal(cirDef.nVars, 2); - - const circuit = new snarkjs.Circuit(cirDef); + const cirDefTest = await compiler(path.join(__dirname, "circuits", "babycheck_test.circom")); + circuitTest = new snarkjs.Circuit(cirDefTest); + console.log("NConstrains BabyTest: " + circuitTest.nConstraints); + }); - console.log("NConstrains: " + circuit.nConstraints); + it("Should add point (0,1) and (0,1)", async () => { const input={ x1: snarkjs.bigInt(0), y1: snarkjs.bigInt(1), x2: snarkjs.bigInt(0), y2: snarkjs.bigInt(1) - } + }; - const w = circuit.calculateWitness(input); + const w = circuitAdd.calculateWitness(input); - const xout = w[circuit.getSignalIdx("main.xout")]; - const yout = w[circuit.getSignalIdx("main.yout")]; + const xout = w[circuitAdd.getSignalIdx("main.xout")]; + const yout = w[circuitAdd.getSignalIdx("main.yout")]; assert(xout.equals(0)); assert(yout.equals(1));@@ -39,27 +44,17 @@ });
it("Should add 2 same numbers", async () => { - const cirDef = await compiler(path.join(__dirname, "circuits", "babyadd_tester.circom")); - -// console.log(JSON.stringify(cirDef, null, 1)); - -// assert.equal(cirDef.nVars, 2); - - const circuit = new snarkjs.Circuit(cirDef); - - console.log("NConstrains: " + circuit.nConstraints); - const input={ x1: snarkjs.bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"), y1: snarkjs.bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475"), x2: snarkjs.bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"), y2: snarkjs.bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475") - } + }; - const w = circuit.calculateWitness(input); + const w = circuitAdd.calculateWitness(input); - const xout = w[circuit.getSignalIdx("main.xout")]; - const yout = w[circuit.getSignalIdx("main.yout")]; + const xout = w[circuitAdd.getSignalIdx("main.xout")]; + const yout = w[circuitAdd.getSignalIdx("main.yout")]; assert(xout.equals(snarkjs.bigInt("6890855772600357754907169075114257697580319025794532037257385534741338397365"))); assert(yout.equals(snarkjs.bigInt("4338620300185947561074059802482547481416142213883829469920100239455078257889")));@@ -67,32 +62,39 @@ });
it("Should add 2 different numbers", async () => { - const cirDef = await compiler(path.join(__dirname, "circuits", "babyadd_tester.circom")); - -// console.log(JSON.stringify(cirDef, null, 1)); - -// assert.equal(cirDef.nVars, 2); - - const circuit = new snarkjs.Circuit(cirDef); - - console.log("NConstrains: " + circuit.nConstraints); - const input={ x1: snarkjs.bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"), y1: snarkjs.bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475"), x2: snarkjs.bigInt("16540640123574156134436876038791482806971768689494387082833631921987005038935"), y2: snarkjs.bigInt("20819045374670962167435360035096875258406992893633759881276124905556507972311") - } + }; - const w = circuit.calculateWitness(input); + const w = circuitAdd.calculateWitness(input); - const xout = w[circuit.getSignalIdx("main.xout")]; - const yout = w[circuit.getSignalIdx("main.yout")]; + const xout = w[circuitAdd.getSignalIdx("main.xout")]; + const yout = w[circuitAdd.getSignalIdx("main.yout")]; + /* console.log(xout.toString()); console.log(yout.toString()); + */ assert(xout.equals(snarkjs.bigInt("7916061937171219682591368294088513039687205273691143098332585753343424131937"))); assert(yout.equals(snarkjs.bigInt("14035240266687799601661095864649209771790948434046947201833777492504781204499"))); }); + + it("Should check 0 is a valid poiny", async() => { + const w = circuitTest.calculateWitness({x: 0, y:1}); + assert(circuitTest.checkWitness(w)); + }); + + it("Should check 0 is an invalid poiny", async() => { + try { + circuitTest.calculateWitness({x: 1, y: 0}); + assert(false, "Should be a valid point"); + } catch(err) { + assert.equal(err.message, "Constraint doesn't match: 168700 != 1"); + } + }); + });
@@ -0,0 +1,3 @@
+include "../../circuit/aliascheck.circom"; + +component main = AliasCheck()
@@ -0,0 +1,3 @@
+include "../../circuit/babyjub.circom"; + +component main = BabyCheck();
@@ -0,0 +1,3 @@
+include "../../circuit/montgomery.circom"; + +component main = Edwards2Montgomery();
@@ -0,0 +1,28 @@
+include "../../circuit/escalarmulany.circom"; +include "../../node_modules/circom/circuits/bitify.circom"; + +template Main() { + signal input e; + signal input p[2]; + signal output out[2]; + + component n2b = Num2Bits(253); + component escalarMulAny = EscalarMulAny(253); + + escalarMulAny.p[0] <== p[0]; + escalarMulAny.p[1] <== p[1]; + + var i; + + e ==> n2b.in; + + for (i=0; i<253; i++) { + n2b.out[i] ==> escalarMulAny.e[i]; + } + + escalarMulAny.out[0] ==> out[0]; + escalarMulAny.out[1] ==> out[1]; +} + +component main = Main(); +
@@ -0,0 +1,29 @@
+include "../../circuit/escalarmulfix.circom"; +include "../../node_modules/circom/circuits/bitify.circom"; + + +template Main() { + signal input e; + signal output out[2]; + + var base = [17777552123799933955779906779655732241715742912184938656739573121738514868268, + 2626589144620713026669568689430873010625803728049924121243784502389097019475] + + + component n2b = Num2Bits(253); + component escalarMul = EscalarMulFix(253, base); + + var i; + + e ==> n2b.in; + + for (i=0; i<253; i++) { + n2b.out[i] ==> escalarMul.e[i]; + } + + escalarMul.out[0] ==> out[0]; + escalarMul.out[1] ==> out[1]; +} + +component main = Main(); +
@@ -0,0 +1,3 @@
+include "../../circuit/montgomery.circom"; + +component main = Montgomery2Edwards();
@@ -0,0 +1,3 @@
+include "../../circuit/montgomery.circom"; + +component main = MontgomeryAdd();
@@ -0,0 +1,3 @@
+include "../../circuit/montgomery.circom"; + +component main = MontgomeryDouble();
@@ -0,0 +1,39 @@
+include "../../circuit/mux3.circom"; +include "../../node_modules/circom/circuits/bitify.circom"; + + +template Constants() { + var i; + signal output out[8]; + + out[0] <== 37; + out[1] <== 47; + out[2] <== 53; + out[3] <== 71; + out[4] <== 89; + out[5] <== 107; + out[6] <== 163; + out[7] <== 191; +} + +template Main() { + var i; + signal private input selector; + signal output out; + + component mux = Mux3(); + component n2b = Num2Bits(3); + component cst = Constants(); + + selector ==> n2b.in; + for (i=0; i<3; i++) { + n2b.out[i] ==> mux.s[i]; + } + for (i=0; i<8; i++) { + cst.out[i] ==> mux.c[i]; + } + + mux.out ==> out; +} + +component main = Main();
@@ -0,0 +1,32 @@
+include "../../circuit/pedersen2.circom"; +include "../../node_modules/circom/circuits/bitify.circom"; + + +template Main() { + signal input in; + signal output out[2]; + + component pedersen = Pedersen(256); + + component n2b; + n2b = Num2Bits(253); + + var i; + + in ==> n2b.in; + + for (i=0; i<253; i++) { + pedersen.in[i] <== n2b.out[i]; + } + + for (i=253; i<256; i++) { + pedersen.in[i] <== 0; + } + + pedersen.out[0] ==> out[0]; + pedersen.out[1] ==> out[1]; +} + +component main = Main(); + +
@@ -6,20 +6,20 @@ template Main() {
signal input in[2]; signal output out[2]; - component pedersen = Pedersen(253*2); + component pedersen = Pedersen(250*2); component n2b[2]; - n2b[0] = Num2Bits(253); - n2b[1] = Num2Bits(253); + n2b[0] = Num2Bits(250); + n2b[1] = Num2Bits(250); var i; in[0] ==> n2b[0].in; in[1] ==> n2b[1].in; - for (i=0; i<253; i++) { + for (i=0; i<250; i++) { n2b[0].out[i] ==> pedersen.in[i]; - n2b[1].out[i] ==> pedersen.in[253+i]; + n2b[1].out[i] ==> pedersen.in[250+i]; } pedersen.out[0] ==> out[0];
@@ -0,0 +1,23 @@
+include "../../circuit/pointbits.circom"; + + +template Main() { + signal input in[2]; + + var i + + component p2b = Point2Bits_Strict(); + component b2p = Bits2Point_Strict(); + + p2b.in[0] <== in[0]; + p2b.in[1] <== in[1]; + + for (i=0; i<256; i++) { + b2p.in[i] <== p2b.out[i]; + } + + b2p.out[0] === in[0]; + b2p.out[1] === in[1]; +} + +component main = Main();
@@ -0,0 +1,3 @@
+include "../../circuit/sign.circom"; + +component main = Sign();
@@ -0,0 +1,59 @@
+const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + + +function print(circuit, w, s) { + console.log(s + ": " + w[circuit.getSignalIdx(s)]); +} + +describe("Escalarmul test", function () { + let circuitEMulAny; + + this.timeout(100000); + + let g = [ + snarkjs.bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"), + snarkjs.bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475") + ]; + + before( async() => { + const cirDefEMulAny = await compiler(path.join(__dirname, "circuits", "escalarmulany_test.circom")); + circuitEMulAny = new snarkjs.Circuit(cirDefEMulAny); + console.log("NConstrains Escalarmul any: " + circuitEMulAny.nConstraints); + }); + + it("Should generate Same escalar mul", async () => { + + const w = circuitEMulAny.calculateWitness({"e": 1, "p": g}); + + assert(circuitEMulAny.checkWitness(w)); + + const xout = w[circuitEMulAny.getSignalIdx("main.out[0]")]; + const yout = w[circuitEMulAny.getSignalIdx("main.out[1]")]; + + assert(xout.equals(g[0])); + assert(yout.equals(g[1])); + }); + + it("If multiply by order should return 0", async () => { + + const r = bigInt("2736030358979909402780800718157159386076813972158567259200215660948447373041"); + const w = circuitEMulAny.calculateWitness({"e": r, "p": g}); + + assert(circuitEMulAny.checkWitness(w)); + + const xout = w[circuitEMulAny.getSignalIdx("main.out[0]")]; + const yout = w[circuitEMulAny.getSignalIdx("main.out[1]")]; + + assert(xout.equals(bigInt.zero)); + assert(yout.equals(bigInt.one)); + }); + +}); +
@@ -0,0 +1,67 @@
+const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); +const babyjub = require("../src/babyjub"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + + +function print(circuit, w, s) { + console.log(s + ": " + w[circuit.getSignalIdx(s)]); +} + +describe("Escalarmul test", function () { + let circuit; + + this.timeout(100000); + + before( async() => { + const cirDef = await compiler(path.join(__dirname, "circuits", "escalarmulfix_test.circom")); + circuit = new snarkjs.Circuit(cirDef); + console.log("NConstrains Escalarmul fix: " + circuit.nConstraints); + }); + + it("Should generate Same escalar mul", async () => { + + const w = circuit.calculateWitness({"e": 0}); + + assert(circuit.checkWitness(w)); + + const xout = w[circuit.getSignalIdx("main.out[0]")]; + const yout = w[circuit.getSignalIdx("main.out[1]")]; + + assert(xout.equals(0)); + assert(yout.equals(1)); + }); + + it("Should generate Same escalar mul", async () => { + + const w = circuit.calculateWitness({"e": 1}); + + assert(circuit.checkWitness(w)); + + const xout = w[circuit.getSignalIdx("main.out[0]")]; + const yout = w[circuit.getSignalIdx("main.out[1]")]; + + assert(xout.equals(babyjub.Base8[0])); + assert(yout.equals(babyjub.Base8[1])); + }); + + it("If multiply by order should return 0", async () => { + + const w = circuit.calculateWitness({"e": babyjub.subOrder }); + + assert(circuit.checkWitness(w)); + + const xout = w[circuit.getSignalIdx("main.out[0]")]; + const yout = w[circuit.getSignalIdx("main.out[1]")]; + + assert(xout.equals(bigInt.zero)); + assert(yout.equals(bigInt.one)); + }); + +}); +
@@ -0,0 +1,99 @@
+const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); +const babyJub = require("../src/babyjub.js"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + +describe("Montgomery test", function () { + let circuitE2M; + let circuitM2E; + let circuitMAdd; + let circuitMDouble; + + let g = [ + snarkjs.bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"), + snarkjs.bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475")]; + + let mg, mg2, g2, g3, mg3; + + this.timeout(100000); + before( async() => { + const cirDefE2M = await compiler(path.join(__dirname, "circuits", "edwards2montgomery.circom")); + circuitE2M = new snarkjs.Circuit(cirDefE2M); + console.log("NConstrains Edwards -> Montgomery: " + circuitE2M.nConstraints); + + const cirDefM2E = await compiler(path.join(__dirname, "circuits", "montgomery2edwards.circom")); + circuitM2E = new snarkjs.Circuit(cirDefM2E); + console.log("NConstrains Montgomery -> Edwards: " + circuitM2E.nConstraints); + + const cirDefMAdd = await compiler(path.join(__dirname, "circuits", "montgomeryadd.circom")); + circuitMAdd = new snarkjs.Circuit(cirDefMAdd); + console.log("NConstrains Montgomery Add: " + circuitMAdd.nConstraints); + + const cirDefMDouble = await compiler(path.join(__dirname, "circuits", "montgomerydouble.circom")); + circuitMDouble = new snarkjs.Circuit(cirDefMDouble); + console.log("NConstrains Montgomery Double: " + circuitMDouble.nConstraints); + }); + it("Convert Edwards to Montgomery and back again", async () => { + let w, xout, yout; + + w = circuitE2M.calculateWitness({ in: g}); + + xout = w[circuitE2M.getSignalIdx("main.out[0]")]; + yout = w[circuitE2M.getSignalIdx("main.out[1]")]; + + mg = [xout, yout]; + + w = circuitM2E.calculateWitness({ in: [xout, yout]}); + + xout = w[circuitM2E.getSignalIdx("main.out[0]")]; + yout = w[circuitM2E.getSignalIdx("main.out[1]")]; + + assert(xout.equals(g[0])); + assert(yout.equals(g[1])); + }); + it("Should double a point", async () => { + let w, xout, yout; + + g2 = babyJub.addPoint(g,g); + + w = circuitMDouble.calculateWitness({ in: mg}); + + xout = w[circuitE2M.getSignalIdx("main.out[0]")]; + yout = w[circuitE2M.getSignalIdx("main.out[1]")]; + + mg2 = [xout, yout]; + + w = circuitM2E.calculateWitness({ in: mg2}); + + xout = w[circuitM2E.getSignalIdx("main.out[0]")]; + yout = w[circuitM2E.getSignalIdx("main.out[1]")]; + + assert(xout.equals(g2[0])); + assert(yout.equals(g2[1])); + }); + it("Should add a point", async () => { + let w, xout, yout; + + g3 = babyJub.addPoint(g,g2); + + w = circuitMAdd.calculateWitness({ in1: mg, in2: mg2}); + + xout = w[circuitMAdd.getSignalIdx("main.out[0]")]; + yout = w[circuitMAdd.getSignalIdx("main.out[1]")]; + + mg3 = [xout, yout]; + + w = circuitM2E.calculateWitness({ in: mg3}); + + xout = w[circuitM2E.getSignalIdx("main.out[0]")]; + yout = w[circuitM2E.getSignalIdx("main.out[1]")]; + + assert(xout.equals(g3[0])); + assert(yout.equals(g3[1])); + }); +});
@@ -5,7 +5,7 @@ const compiler = require("circom");
const assert = chai.assert; -const bigInt = require("big-integer"); +const bigInt = snarkjs.bigInt; describe("Mux4 test", () => {@@ -19,15 +19,63 @@ // assert.equal(cirDef.nVars, 2);
const circuit = new snarkjs.Circuit(cirDef); - console.log("NConstrains: " + circuit.nConstraints); + console.log("NConstrains Mux4: " + circuit.nConstraints); + + const ct16 = [ + bigInt("123"), + bigInt("456"), + bigInt("789"), + bigInt("012"), + bigInt("111"), + bigInt("222"), + bigInt("333"), + bigInt("4546"), + bigInt("134523"), + bigInt("44356"), + bigInt("15623"), + bigInt("4566"), + bigInt("1223"), + bigInt("4546"), + bigInt("4256"), + bigInt("4456") + ]; - for (i=0; i<16; i++) { - const w = circuit.calculateWitness({ "selector": snarkjs.bigInt(i).toString() }); + for (let i=0; i<16; i++) { + const w = circuit.calculateWitness({ "selector": i }); - assert(w[0].equals(snarkjs.bigInt(1))); + assert(w[0].equals(bigInt(1))); - console.log(i + " -> " + w[circuit.getSignalIdx("main.out")].toString()); -// assert(w[circuit.getSignalIdx("main.out")].equals(snarkjs.bigInt("100").add(snarkjs.bigInt(i)))); + // console.log(i + " -> " + w[circuit.getSignalIdx("main.out")].toString()); + assert(w[circuit.getSignalIdx("main.out")].equals(ct16[i])); + } + }); + + it("Should create a constant multiplexer", async () => { + + const cirDef = await compiler(path.join(__dirname, "circuits", "mux3_1.circom")); + + const circuit = new snarkjs.Circuit(cirDef); + + console.log("NConstrains Mux3: " + circuit.nConstraints); + + const ct8 = [ + bigInt("37"), + bigInt("47"), + bigInt("53"), + bigInt("71"), + bigInt("89"), + bigInt("107"), + bigInt("163"), + bigInt("191") + ]; + + for (let i=0; i<8; i++) { + const w = circuit.calculateWitness({ "selector": i }); + + assert(w[0].equals(bigInt(1))); + + // console.log(i + " -> " + w[circuit.getSignalIdx("main.out")].toString()); + assert(w[circuit.getSignalIdx("main.out")].equals(ct8[i])); } }); });
@@ -88,7 +88,7 @@ });
it("Should pedersen all ones", async () => { let w, xout, yout; - const allOnes = bigInt("1").shl(251).sub(bigInt("1")); + const allOnes = bigInt("1").shl(250).sub(bigInt("1")); w = circuit.calculateWitness({ in: [allOnes, allOnes]}); xout = w[circuit.getSignalIdx("main.out[0]")];
@@ -0,0 +1,74 @@
+const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + +const babyJub = require("../src/babyjub.js"); +const pedersen = require("../src/pedersenHash.js"); + + +describe("Pedersen test", function() { + let circuit; + this.timeout(100000); + before( async() => { + const cirDef = await compiler(path.join(__dirname, "circuits", "pedersen2_test.circom")); + + circuit = new snarkjs.Circuit(cirDef); + + console.log("NConstrains Pedersen2: " + circuit.nConstraints); + }); + it("Should pedersen at zero", async () => { + + let w, xout, yout; + + w = circuit.calculateWitness({ in: 0}); + + xout = w[circuit.getSignalIdx("main.out[0]")]; + yout = w[circuit.getSignalIdx("main.out[1]")]; + + const b = Buffer.alloc(32); + + const h = pedersen.hash(b); + const hP = babyJub.unpackPoint(h); + + /* + console.log(`[${xout.toString()}, ${yout.toString()}]`); + console.log(`[${hP[0].toString()}, ${hP[1].toString()}]`); + */ + + assert(xout.equals(hP[0])); + assert(yout.equals(hP[1])); + }); + it("Should pedersen with 253 ones", async () => { + + let w, xout, yout; + + const n = bigInt.one.shl(253).sub(bigInt.one); + console.log(n.toString(16)); + + w = circuit.calculateWitness({ in: n}); + + xout = w[circuit.getSignalIdx("main.out[0]")]; + yout = w[circuit.getSignalIdx("main.out[1]")]; + + const b = Buffer.alloc(32); + for (let i=0; i<31; i++) b[i] = 0xFF; + b[31] = 0x1F; + + + const h = pedersen.hash(b); + const hP = babyJub.unpackPoint(h); + + /* + console.log(`[${xout.toString()}, ${yout.toString()}]`); + console.log(`[${hP[0].toString()}, ${hP[1].toString()}]`); + */ + + assert(xout.equals(hP[0])); + assert(yout.equals(hP[1])); + }); +});
@@ -0,0 +1,33 @@
+const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + +const babyJub = require("../src/babyjub.js"); + + +describe("Point 2 bits test", function() { + let circuit; + this.timeout(100000); + before( async() => { + const cirDef = await compiler(path.join(__dirname, "circuits", "pointbits_loopback.circom")); + + circuit = new snarkjs.Circuit(cirDef); + + console.log("NConstrains Point2Bits loopback: " + circuit.nConstraints); + }); + it("Should do the both convertions for 8Base", async () => { + const w = circuit.calculateWitness({ in: babyJub.Base8}); + + assert(circuit.checkWitness(w)); + }); + it("Should do the both convertions for Zero point", async () => { + const w = circuit.calculateWitness({ in: [0, 1]}); + + assert(circuit.checkWitness(w)); + }); +});
@@ -0,0 +1,88 @@
+const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + +function print(circuit, w, s) { + console.log(s + ": " + w[circuit.getSignalIdx(s)]); +} + +function getBits(v, n) { + const res = []; + for (let i=0; i<n; i++) { + if (v.shr(i).isOdd()) { + res.push(bigInt.one); + } else { + res.push(bigInt.zero); + } + } + return res; +} + +const q = bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617"); + +describe("Aliascheck test", () => { + let circuit; + before( async() => { + const cirDef = await compiler(path.join(__dirname, "circuits", "sign_test.circom")); + + circuit = new snarkjs.Circuit(cirDef); + + console.log("NConstrains: " + circuit.nConstraints); + }); + + it("Sign of 0", async () => { + const inp = getBits(bigInt.zero, 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(0)) ); + }); + + it("Sign of 3", async () => { + const inp = getBits(bigInt(3), 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(0)) ); + }); + + it("Sign of q/2", async () => { + const inp = getBits(q.shr(bigInt.one), 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(0)) ); + }); + + it("Sign of q/2+1", async () => { + const inp = getBits(q.shr(bigInt.one).add(bigInt.one), 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(1)) ); + }); + + it("Sign of q-1", async () => { + const inp = getBits(q.sub(bigInt.one), 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(1)) ); + }); + + it("Sign of q", async () => { + const inp = getBits(q, 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(1)) ); + }); + + it("Sign of all ones", async () => { + const inp = getBits(bigInt(1).shl(254).sub(bigInt(1)), 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(1)) ); + }); + + +});