bug: imports in verifier

This commit is contained in:
0xvikasrushi
2024-07-19 17:47:53 +05:30
parent 282e8ed86c
commit 380a7e2e12
10 changed files with 1820 additions and 10 deletions

View File

@@ -0,0 +1,987 @@
pragma circom 2.0.3;
include "../../../node_modules/circomlib/circuits/comparators.circom";
include "../../../node_modules/circomlib/circuits/bitify.circom";
include "../../../node_modules/circomlib/circuits/gates.circom";
include "bigint_func.circom";
// addition mod 2**n with carry bit
template ModSum(n) {
assert(n <= 252);
signal input a;
signal input b;
signal output sum;
signal output carry;
component n2b = Num2Bits(n + 1);
n2b.in <== a + b;
carry <== n2b.out[n];
sum <== a + b - carry * (1 << n);
}
// check if k-register variables a, b are equal everywhere
template BigIsEqual(k) {
signal input a[k];
signal input b[k];
signal output out;
component isEquals[k];
var total = k;
for (var i = 0; i < k; i ++) {
isEquals[i] = IsEqual();
isEquals[i].in[0] <== a[i];
isEquals[i].in[1] <== b[i];
total -= isEquals[i].out;
}
component checkZero = IsZero();
checkZero.in <== total;
out <== checkZero.out;
}
// check if k-register variable a is equal to zero
template BigIsZero(k) {
signal input in[k];
signal output out;
component isZeros[k];
var total = k;
for (var i = 0; i < k; i ++) {
isZeros[i] = IsZero();
isZeros[i].in <== in[i];
total -= isZeros[i].out;
}
component checkZero = IsZero();
checkZero.in <== total;
out <== checkZero.out;
}
// a - b
template ModSub(n) {
assert(n <= 252);
signal input a;
signal input b;
signal output out;
signal output borrow;
component lt = LessThan(n);
lt.in[0] <== a;
lt.in[1] <== b;
borrow <== lt.out;
out <== borrow * (1 << n) + a - b;
}
// a - b - c
// assume a - b - c + 2**n >= 0
template ModSubThree(n) {
assert(n + 2 <= 253);
signal input a;
signal input b;
signal input c;
assert(a - b - c + (1 << n) >= 0);
signal output out;
signal output borrow;
signal b_plus_c;
b_plus_c <== b + c;
component lt = LessThan(n + 1);
lt.in[0] <== a;
lt.in[1] <== b_plus_c;
borrow <== lt.out;
out <== borrow * (1 << n) + a - b_plus_c;
}
template ModSumThree(n) {
assert(n + 2 <= 253);
signal input a;
signal input b;
signal input c;
signal output sum;
signal output carry;
component n2b = Num2Bits(n + 2);
n2b.in <== a + b + c;
carry <== n2b.out[n] + 2 * n2b.out[n + 1];
sum <== a + b + c - carry * (1 << n);
}
template ModSumFour(n) {
assert(n + 2 <= 253);
signal input a;
signal input b;
signal input c;
signal input d;
signal output sum;
signal output carry;
component n2b = Num2Bits(n + 2);
n2b.in <== a + b + c + d;
carry <== n2b.out[n] + 2 * n2b.out[n + 1];
sum <== a + b + c + d - carry * (1 << n);
}
// product mod 2**n with carry
template ModProd(n) {
assert(n <= 126);
signal input a;
signal input b;
signal output prod;
signal output carry;
component n2b = Num2Bits(2 * n);
n2b.in <== a * b;
component b2n1 = Bits2Num(n);
component b2n2 = Bits2Num(n);
var i;
for (i = 0; i < n; i++) {
b2n1.in[i] <== n2b.out[i];
b2n2.in[i] <== n2b.out[i + n];
}
prod <== b2n1.out;
carry <== b2n2.out;
}
// split a n + m bit input into two outputs
template Split(n, m) {
assert(n <= 126);
signal input in;
signal output small;
signal output big;
small <-- in % (1 << n);
big <-- in \ (1 << n);
component n2b_small = Num2Bits(n);
n2b_small.in <== small;
component n2b_big = Num2Bits(m);
n2b_big.in <== big;
in === small + big * (1 << n);
}
// split a n + m + k bit input into three outputs
template SplitThree(n, m, k) {
assert(n <= 126);
signal input in;
signal output small;
signal output medium;
signal output big;
small <-- in % (1 << n);
medium <-- (in \ (1 << n)) % (1 << m);
big <-- in \ (1 << n + m);
component n2b_small = Num2Bits(n);
n2b_small.in <== small;
component n2b_medium = Num2Bits(m);
n2b_medium.in <== medium;
component n2b_big = Num2Bits(k);
n2b_big.in <== big;
in === small + medium * (1 << n) + big * (1 << n + m);
}
// a[i], b[i] in 0... 2**n-1
// represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k)
template BigAdd(n, k) {
assert(n <= 252);
signal input a[k];
signal input b[k];
signal output out[k + 1];
component unit0 = ModSum(n);
unit0.a <== a[0];
unit0.b <== b[0];
out[0] <== unit0.sum;
component unit[k - 1];
for (var i = 1; i < k; i++) {
unit[i - 1] = ModSumThree(n);
unit[i - 1].a <== a[i];
unit[i - 1].b <== b[i];
if (i == 1) {
unit[i - 1].c <== unit0.carry;
} else {
unit[i - 1].c <== unit[i - 2].carry;
}
out[i] <== unit[i - 1].sum;
}
out[k] <== unit[k - 2].carry;
}
/*
Polynomial Multiplication
Inputs:
- a = a[0] + a[1] * X + ... + a[k-1] * X^{k-1}
- b = b[0] + b[1] * X + ... + b[k-1] * X^{k-1}
Output:
- out = out[0] + out[1] * X + ... + out[2 * k - 2] * X^{2*k - 2}
- out = a * b as polynomials in X
Notes:
- Optimization due to xJsnark:
-- witness is calculated by normal polynomial multiplication
-- out is contrained by evaluating out(X) === a(X) * b(X) at X = 0, ..., 2*k - 2
- If a[i], b[j] have absolute value < B, then out[i] has absolute value < k * B^2
m_out is the expected max number of bits in the output registers
*/
template BigMultShortLong(n, k, m_out) {
assert(n <= 126);
signal input a[k];
signal input b[k];
signal output out[2 * k - 1];
var prod_val[2 * k - 1];
for (var i = 0; i < 2 * k - 1; i++) {
prod_val[i] = 0;
if (i < k) {
for (var a_idx = 0; a_idx <= i; a_idx++) {
prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx];
}
} else {
for (var a_idx = i - k + 1; a_idx < k; a_idx++) {
prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx];
}
}
out[i] <-- prod_val[i];
}
var k2 = 2 * k - 1;
var pow[k2][k2]; // we cache the exponent values because it makes a big difference in witness generation time
for(var i = 0; i<k2; i++)for(var j=0; j<k2; j++)
pow[i][j] = i ** j;
var a_poly[2 * k - 1];
var b_poly[2 * k - 1];
var out_poly[2 * k - 1];
for (var i = 0; i < 2 * k - 1; i++) {
out_poly[i] = 0;
a_poly[i] = 0;
b_poly[i] = 0;
for (var j = 0; j < 2 * k - 1; j++) {
out_poly[i] = out_poly[i] + out[j] * pow[i][j];
}
for (var j = 0; j < k; j++) {
a_poly[i] = a_poly[i] + a[j] * pow[i][j];
b_poly[i] = b_poly[i] + b[j] * pow[i][j];
}
}
for (var i = 0; i < 2 * k - 1; i++) {
out_poly[i] === a_poly[i] * b_poly[i];
}
}
/*
same as BigMultShortLong except a has degree ka - 1, b has degree kb - 1
- If a[i], b[j] have absolute value < B, then out[i] has absolute value < min(ka, kb) * B^2
*/
template BigMultShortLongUnequal(n, ka, kb, m_out) {
assert(n <= 126);
signal input a[ka];
signal input b[kb];
signal output out[ka + kb - 1];
var prod_val[ka + kb - 1];
for (var i = 0; i < ka + kb - 1; i++) {
prod_val[i] = 0;
}
for (var i = 0; i < ka; i++) {
for (var j = 0; j < kb; j++) {
prod_val[i + j] = prod_val[i + j] + a[i] * b[j];
}
}
for (var i = 0; i < ka + kb - 1; i++) {
out[i] <-- prod_val[i];
}
var k2 = ka + kb - 1;
var pow[k2][k2];
for(var i = 0; i<k2; i++)for(var j=0; j<k2; j++)
pow[i][j] = i ** j;
var a_poly[ka + kb - 1];
var b_poly[ka + kb - 1];
var out_poly[ka + kb - 1];
for (var i = 0; i < ka + kb - 1; i++) {
out_poly[i] = 0;
a_poly[i] = 0;
b_poly[i] = 0;
for (var j = 0; j < ka + kb - 1; j++) {
out_poly[i] = out_poly[i] + out[j] * pow[i][j];
}
for (var j = 0; j < ka; j++) {
a_poly[i] = a_poly[i] + a[j] * pow[i][j];
}
for (var j = 0; j < kb; j++) {
b_poly[i] = b_poly[i] + b[j] * pow[i][j];
}
}
for (var i = 0; i < ka + kb - 1; i++) {
out_poly[i] === a_poly[i] * b_poly[i];
}
}
// in[i] contains longs
// out[i] contains shorts
template LongToShortNoEndCarry(n, k) {
assert(n <= 126);
signal input in[k];
signal output out[k+1];
var split[k][3];
for (var i = 0; i < k; i++) {
split[i] = SplitThreeFn(in[i], n, n, n);
}
var carry[k];
carry[0] = 0;
out[0] <-- split[0][0];
if (k > 1) {
var sumAndCarry[2] = SplitFn(split[0][1] + split[1][0], n, n);
out[1] <-- sumAndCarry[0];
carry[1] = sumAndCarry[1];
}
if (k > 2) {
for (var i = 2; i < k; i++) {
var sumAndCarry[2] = SplitFn(split[i][0] + split[i-1][1] + split[i-2][2] + carry[i-1], n, n);
out[i] <-- sumAndCarry[0];
carry[i] = sumAndCarry[1];
}
out[k] <-- split[k-1][1] + split[k-2][2] + carry[k-1];
}
component outRangeChecks[k+1];
for (var i = 0; i < k+1; i++) {
outRangeChecks[i] = Num2Bits(n);
outRangeChecks[i].in <== out[i];
}
signal runningCarry[k];
component runningCarryRangeChecks[k];
runningCarry[0] <-- (in[0] - out[0]) / (1 << n);
runningCarryRangeChecks[0] = Num2Bits(n + log_ceil(k));
runningCarryRangeChecks[0].in <== runningCarry[0];
runningCarry[0] * (1 << n) === in[0] - out[0];
for (var i = 1; i < k; i++) {
runningCarry[i] <-- (in[i] - out[i] + runningCarry[i-1]) / (1 << n);
runningCarryRangeChecks[i] = Num2Bits(n + log_ceil(k));
runningCarryRangeChecks[i].in <== runningCarry[i];
runningCarry[i] * (1 << n) === in[i] - out[i] + runningCarry[i-1];
}
runningCarry[k-1] === out[k];
}
template BigMult(n, k) {
signal input a[k];
signal input b[k];
signal output out[2 * k];
var LOGK = log_ceil(k);
component mult = BigMultShortLong(n, k, 2*n + LOGK);
for (var i = 0; i < k; i++) {
mult.a[i] <== a[i];
mult.b[i] <== b[i];
}
// no carry is possible in the highest order register
component longshort = LongToShortNoEndCarry(n, 2 * k - 1);
for (var i = 0; i < 2 * k - 1; i++) {
longshort.in[i] <== mult.out[i];
}
for (var i = 0; i < 2 * k; i++) {
out[i] <== longshort.out[i];
}
}
/*
Inputs:
- BigInts a, b
Output:
- out = (a < b) ? 1 : 0
*/
template BigLessThan(n, k){
signal input a[k];
signal input b[k];
signal output out;
component lt[k];
component eq[k];
for (var i = 0; i < k; i++) {
lt[i] = LessThan(n);
lt[i].in[0] <== a[i];
lt[i].in[1] <== b[i];
eq[i] = IsEqual();
eq[i].in[0] <== a[i];
eq[i].in[1] <== b[i];
}
// ors[i] holds (lt[k - 1] || (eq[k - 1] && lt[k - 2]) .. || (eq[k - 1] && .. && lt[i]))
// ands[i] holds (eq[k - 1] && .. && lt[i])
// eq_ands[i] holds (eq[k - 1] && .. && eq[i])
component ors[k - 1];
component ands[k - 1];
component eq_ands[k - 1];
for (var i = k - 2; i >= 0; i--) {
ands[i] = AND();
eq_ands[i] = AND();
ors[i] = OR();
if (i == k - 2) {
ands[i].a <== eq[k - 1].out;
ands[i].b <== lt[k - 2].out;
eq_ands[i].a <== eq[k - 1].out;
eq_ands[i].b <== eq[k - 2].out;
ors[i].a <== lt[k - 1].out;
ors[i].b <== ands[i].out;
} else {
ands[i].a <== eq_ands[i + 1].out;
ands[i].b <== lt[i].out;
eq_ands[i].a <== eq_ands[i + 1].out;
eq_ands[i].b <== eq[i].out;
ors[i].a <== ors[i + 1].out;
ors[i].b <== ands[i].out;
}
}
out <== ors[0].out;
}
// leading register of b should be non-zero
template BigMod(n, k) {
assert(n <= 126);
signal input a[2 * k];
signal input b[k];
signal output div[k + 1];
signal output mod[k];
var longdiv[2][50] = long_div(n, k, a, b);
for (var i = 0; i < k; i++) {
div[i] <-- longdiv[0][i];
mod[i] <-- longdiv[1][i];
}
div[k] <-- longdiv[0][k];
component div_range_checks[k + 1];
for (var i = 0; i <= k; i++) {
div_range_checks[i] = Num2Bits(n);
div_range_checks[i].in <== div[i];
}
component mod_range_checks[k];
for (var i = 0; i < k; i++) {
mod_range_checks[i] = Num2Bits(n);
mod_range_checks[i].in <== mod[i];
}
component mul = BigMult(n, k + 1);
for (var i = 0; i < k; i++) {
mul.a[i] <== div[i];
mul.b[i] <== b[i];
}
mul.a[k] <== div[k];
mul.b[k] <== 0;
for (var i = 0; i < 2 * k + 2; i++) {
//log(mul.out[i]);
}
component add = BigAdd(n, 2 * k + 2);
for (var i = 0; i < 2 * k; i++) {
add.a[i] <== mul.out[i];
if (i < k) {
add.b[i] <== mod[i];
} else {
add.b[i] <== 0;
}
}
add.a[2 * k] <== mul.out[2 * k];
add.a[2 * k + 1] <== mul.out[2 * k + 1];
add.b[2 * k] <== 0;
add.b[2 * k + 1] <== 0;
for (var i = 0; i < 2 * k + 2; i++) {
//log(add.out[i]);
}
for (var i = 0; i < 2 * k; i++) {
add.out[i] === a[i];
}
add.out[2 * k] === 0;
add.out[2 * k + 1] === 0;
component lt = BigLessThan(n, k);
for (var i = 0; i < k; i++) {
lt.a[i] <== mod[i];
lt.b[i] <== b[i];
}
lt.out === 1;
}
// copied from BigMod to allow a to have m registers and use long_div2
template BigMod2(n, k, m) {
assert(n <= 126);
signal input a[m];
signal input b[k];
signal output div[m - k + 1];
signal output mod[k];
var longdiv[2][50] = long_div2(n, k, m-k, a, b);
for (var i = 0; i < k; i++) {
mod[i] <-- longdiv[1][i];
}
for (var i = 0; i <= m-k; i++) {
div[i] <-- longdiv[0][i];
}
component div_range_checks[m - k + 1];
for (var i = 0; i <= m-k; i++) {
div_range_checks[i] = Num2Bits(n);
div_range_checks[i].in <== div[i];
}
component mod_range_checks[k];
for (var i = 0; i < k; i++) {
mod_range_checks[i] = Num2Bits(n);
mod_range_checks[i].in <== mod[i];
}
component mul = BigMult(n, m-k + 1);
// this might need to be optimized since b has less registers than div
for (var i = 0; i < k; i++) {
mul.a[i] <== div[i];
mul.b[i] <== b[i];
}
for (var i = k; i <= m-k; i++) {
mul.a[i] <== div[i];
mul.b[i] <== 0;
}
// mul shouldn't have more registers than a
for (var i = m; i < 2*(m-k)+2; i++) {
mul.out[i] === 0;
}
component add = BigAdd(n, m);
for (var i = 0; i < m; i++) {
add.a[i] <== mul.out[i];
if (i < k) {
add.b[i] <== mod[i];
} else {
add.b[i] <== 0;
}
}
for (var i = 0; i < m; i++) {
add.out[i] === a[i];
}
add.out[m] === 0;
component lt = BigLessThan(n, k);
for (var i = 0; i < k; i++) {
lt.a[i] <== mod[i];
lt.b[i] <== b[i];
}
lt.out === 1;
}
// a[i], b[i] in 0... 2**n-1
// represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k)
// calculates (a+b)%p, where 0<= a,b < p
template BigAddModP(n, k){
assert(n <= 252);
signal input a[k];
signal input b[k];
signal input p[k];
signal output out[k];
component add = BigAdd(n,k);
for (var i = 0; i < k; i++) {
add.a[i] <== a[i];
add.b[i] <== b[i];
}
component lt = BigLessThan(n, k+1);
for (var i = 0; i < k; i++) {
lt.a[i] <== add.out[i];
lt.b[i] <== p[i];
}
lt.a[k] <== add.out[k];
lt.b[k] <== 0;
component sub = BigSub(n,k+1);
for (var i = 0; i < k; i++) {
sub.a[i] <== add.out[i];
sub.b[i] <== (1-lt.out) * p[i];
}
sub.a[k] <== add.out[k];
sub.b[k] <== 0;
sub.out[k] === 0;
for (var i = 0; i < k; i++) {
out[i] <== sub.out[i];
}
}
/*
Inputs:
- BigInts a, b
- Assume a >= b
Output:
- BigInt out = a - b
- underflow = how much is borrowed at the highest digit of subtraction, only nonzero if a < b
*/
template BigSub(n, k) {
assert(n <= 252);
signal input a[k];
signal input b[k];
signal output out[k];
signal output underflow;
component unit0 = ModSub(n);
unit0.a <== a[0];
unit0.b <== b[0];
out[0] <== unit0.out;
component unit[k - 1];
for (var i = 1; i < k; i++) {
unit[i - 1] = ModSubThree(n);
unit[i - 1].a <== a[i];
unit[i - 1].b <== b[i];
if (i == 1) {
unit[i - 1].c <== unit0.borrow;
} else {
unit[i - 1].c <== unit[i - 2].borrow;
}
out[i] <== unit[i - 1].out;
}
underflow <== unit[k - 2].borrow;
}
// calculates (a - b) % p, where a, b < p
// note: does not assume a >= b
template BigSubModP(n, k){
assert(n <= 252);
signal input a[k];
signal input b[k];
signal input p[k];
signal output out[k];
component sub = BigSub(n, k);
for (var i = 0; i < k; i++){
sub.a[i] <== a[i];
sub.b[i] <== b[i];
}
signal flag;
flag <== sub.underflow;
component add = BigAdd(n, k);
for (var i = 0; i < k; i++){
add.a[i] <== sub.out[i];
add.b[i] <== p[i];
}
signal tmp[k];
for (var i = 0; i < k; i++){
tmp[i] <== (1 - flag) * sub.out[i];
out[i] <== tmp[i] + flag * add.out[i];
}
}
// Note: deprecated
template BigMultModP(n, k) {
assert(n <= 252);
signal input a[k];
signal input b[k];
signal input p[k];
signal output out[k];
component big_mult = BigMult(n, k);
for (var i = 0; i < k; i++) {
big_mult.a[i] <== a[i];
big_mult.b[i] <== b[i];
}
component big_mod = BigMod(n, k);
for (var i = 0; i < 2 * k; i++) {
big_mod.a[i] <== big_mult.out[i];
}
for (var i = 0; i < k; i++) {
big_mod.b[i] <== p[i];
}
for (var i = 0; i < k; i++) {
out[i] <== big_mod.mod[i];
}
}
template BigModInv(n, k) {
assert(n <= 252);
signal input in[k];
signal input p[k];
signal output out[k];
// length k
var inv[50] = mod_inv(n, k, in, p);
for (var i = 0; i < k; i++) {
out[i] <-- inv[i];
}
component range_checks[k];
for (var i = 0; i < k; i++) {
range_checks[i] = Num2Bits(n);
range_checks[i].in <== out[i];
}
component mult = BigMult(n, k);
for (var i = 0; i < k; i++) {
mult.a[i] <== in[i];
mult.b[i] <== out[i];
}
component mod = BigMod(n, k);
for (var i = 0; i < 2 * k; i++) {
mod.a[i] <== mult.out[i];
}
for (var i = 0; i < k; i++) {
mod.b[i] <== p[i];
}
mod.mod[0] === 1;
for (var i = 1; i < k; i++) {
mod.mod[i] === 0;
}
}
/* Taken from circom-ecdsa
Input:
- in = in[0] + in[1] * X + ... + in[k-1] * X^{k-1} as signed overflow representation
- Assume each in[i] is in range (-2^{m-1}, 2^{m-1})
Implements:
- constrain that in[] evaluated at X = 2^n as a big integer equals zero
*/
template CheckCarryToZero(n, m, k) {
assert(k >= 2);
var EPSILON = 1; // see below for why 1 is ok
signal input in[k];
signal carry[k];
component carryRangeChecks[k];
for (var i = 0; i < k-1; i++){
carryRangeChecks[i] = Num2Bits(m + EPSILON - n);
if( i == 0 ){
carry[i] <-- in[i] / (1<<n);
in[i] === carry[i] * (1<<n);
}
else{
carry[i] <-- (in[i]+carry[i-1]) / (1<<n);
in[i] + carry[i-1] === carry[i] * (1<<n);
}
// checking carry is in the range of -2^(m-n-1+eps), 2^(m-n-1+eps)
carryRangeChecks[i].in <== carry[i] + ( 1<< (m + EPSILON - n - 1));
// carry[i] is bounded by 2^{m-1} * (2^{-n} + 2^{-2n} + ... ) = 2^{m-n-1} * ( 1/ (1-2^{-n})) < 2^{m-n} by geometric series
}
in[k-1] + carry[k-2] === 0;
}
/*
Let X = 2^n
Input:
- in is length k + m array in signed overflow representation
- in = in[0] + in[1] * X + ... + in[k+m-1] * X^{k+m-1}
- Assume each in[i] is a signed integer such that abs(in[i] * 2^n) < 2^252
- p is prime in BigInt format passed as parameter
Output:
- out = out[0] + out[1] * X + ... + out[k-1] * X^{k-1} is BigInt congruent to in (mod p)
Implementation:
- For i >= k, we precompute X^i = r[i] mod p, where r[i] represented as k registers with r[i][j] in [0, 2^n)
- in[i] * X^i is replaced by sum_j in[i] * r[i][j] * X^j
Notes:
- If each in[i] has absolute value <B, then out[i] has absolute value < (m+1) * 2^n * B
m_out is the expected max number of bits in the output registers
*/
template PrimeReduce(n, k, m, p, m_out){
signal input in[m+k];
signal output out[k];
var two[k];
var e[k];
for(var i=1; i<k; i++){
two[i]=0;
e[i]=0;
}
two[0] = 2;
e[0] = n;
var pow2n[50] = mod_exp(n, k, two, p, e);
e[0] = k;
assert(k < (1<<n) );
var pow2nk[50] = mod_exp(n, k, pow2n, p, e);
var r[m][50];
for(var i=0; i<m; i++){
// r[i] = 2^{n(k+i)} mod p
if(i==0){
r[i] = pow2nk;
}else{
r[i] = prod_mod(n, k, r[i-1], pow2n, p);
}
}
var out_sum[k];
for(var i=0; i<k; i++)
out_sum[i] = in[i];
for(var i=0; i<m; i++)
for(var j=0; j<k; j++)
out_sum[j] += in[i+k] * r[i][j]; // linear constraint
for(var i=0; i<k; i++)
out[i] <== out_sum[i];
/*component range_checks[k];
for (var i = 0; i < k; i++) {
range_checks[i] = Num2Bits(m_out+1);
range_checks[i].in <== out[i] + (1 << m_out);
}*/
}
/*
Polynomial multiplication in 2 variables
Input:
- a = sum_{i=0}^{l-1} sum_{j=0}^{k-1} a[i][j] * w^i * X^j
- b = sum_{i=0}^{l-1} sum_{j=0}^{k-1} b[i][j] * w^i * X^j
Output:
- out = sum_{i=0}^{2*l-2} sum_{j=0}^{2*k-1} out[i][j] * w^i * X^j
- out = a * b as product of polynomials in two variables w, X
Notes:
- Uses same xJsnark optimization as BigMultShortLong
- If a[i][j], b[i][j] have absolute value < B, then out[i][j] has absolute value < l * k * B^2
Use case: one variable will end up being 2^n; the other will be the field extension generator
*/
template BigMultShortLong2D(n, k, l) {
signal input a[l][k];
signal input b[l][k];
signal output out[2*l-1][2*k-1];
var prod_val[2*l-1][2*k-1];
for (var i = 0; i < 2*l-1; i++) {
for (var j = 0; j < 2*k-1; j++) {
prod_val[i][j] = 0;
}
}
for (var i1 = 0; i1 < l; i1 ++) {
for (var i2 = 0; i2 < l; i2 ++) {
for (var j1 = 0; j1 < k; j1 ++) {
for (var j2 = 0; j2 < k; j2 ++) {
var i = i1 + i2;
var j = j1 + j2;
prod_val[i][j] += a[i1][j1] * b[i2][j2];
}
}
}
}
for (var i = 0; i < 2*l-1; i++) {
for (var j = 0; j < 2*k-1; j++) {
out[i][j] <-- prod_val[i][j];
}
}
var k2 = (2*k-1 > 2*l-1) ? 2*k-1 : 2*l-1;
var pow[k2][k2];
for(var i = 0; i<k2; i++)for(var j=0; j<k2; j++)
pow[i][j] = i ** j;
var a_poly[2*l-1][2*k-1];
var b_poly[2*l-1][2*k-1];
var out_poly[2*l-1][2*k-1];
for (var i = 0; i < 2*l-1; i++) {
for (var j = 0; j < 2*k-1; j++) {
a_poly[i][j] = 0;
b_poly[i][j] = 0;
out_poly[i][j] = 0;
for (var deg1 = 0; deg1 < l; deg1 ++) {
for (var deg2 = 0; deg2 < k; deg2 ++) {
a_poly[i][j] = a_poly[i][j] + a[deg1][deg2] * pow[i][deg1] * pow[j][deg2]; // (i ** deg1) * (j ** deg2);
b_poly[i][j] = b_poly[i][j] + b[deg1][deg2] * pow[i][deg1] * pow[j][deg2]; // (i ** deg1) * (j ** deg2);
}
}
for (var deg1 = 0; deg1 < 2*l-1; deg1 ++) {
for (var deg2 = 0; deg2 < 2*k-1; deg2 ++) {
out_poly[i][j] = out_poly[i][j] + out[deg1][deg2] * pow[i][deg1] * pow[j][deg2];// (i ** deg1) * (j ** deg2);
}
}
}
}
for (var i = 0; i < 2*l-1; i++) {
for (var j = 0; j < 2*k-1; j++) {
out_poly[i][j] === a_poly[i][j] * b_poly[i][j];
}
}
}
/*
Same as BigMultShortLong2D except a has degrees la - 1, ka - 1 and b has degrees lb - 1, kb - 1
Notes:
- If a[i][j], b[i][j] have absolute value < B, then out[i][j] has absolute value < min(la, lb) * min(ka, kb) * B^2
*/
template BigMultShortLong2DUnequal(n, ka, kb, la, lb) {
signal input a[la][ka];
signal input b[lb][kb];
signal output out[la + lb -1][ka + kb -1];
var prod_val[la + lb -1][ka + kb -1];
for (var i = 0; i < la + lb -1; i++) {
for (var j = 0; j < ka + kb -1; j++) {
prod_val[i][j] = 0;
}
}
for (var i1 = 0; i1 < la; i1 ++) {
for (var i2 = 0; i2 < lb; i2 ++) {
for (var j1 = 0; j1 < ka; j1 ++) {
for (var j2 = 0; j2 < kb; j2 ++) {
var i = i1 + i2;
var j = j1 + j2;
prod_val[i][j] += a[i1][j1] * b[i2][j2];
}
}
}
}
for (var i = 0; i < la + lb -1; i++) {
for (var j = 0; j < ka + kb -1; j++) {
out[i][j] <-- prod_val[i][j];
}
}
var k2 = (ka + kb -1 > la + lb -1) ? ka + kb - 1 : la + lb -1;
var pow[k2][k2];
for(var i = 0; i<k2; i++)for(var j=0; j<k2; j++)
pow[i][j] = i ** j;
var a_poly[la + lb - 1][ka + kb -1];
var b_poly[la + lb - 1][ka + kb -1];
var out_poly[la + lb - 1][ka + kb -1];
for (var i = 0; i < la + lb - 1; i++) {
for (var j = 0; j < ka + kb - 1; j++) {
a_poly[i][j] = 0;
b_poly[i][j] = 0;
out_poly[i][j] = 0;
for (var deg1 = 0; deg1 < la + lb - 1; deg1 ++) {
if (deg1 < la) {
for (var deg2 = 0; deg2 < ka; deg2 ++) {
a_poly[i][j] = a_poly[i][j] + a[deg1][deg2] * pow[i][deg1] * pow[j][deg2]; //(i ** deg1) * (j ** deg2);
}
}
if (deg1 < lb) {
for (var deg2 = 0; deg2 < kb; deg2 ++) {
b_poly[i][j] = b_poly[i][j] + b[deg1][deg2] * pow[i][deg1] * pow[j][deg2]; // (i ** deg1) * (j ** deg2);
}
}
for (var deg2 = 0; deg2 < ka + kb -1; deg2 ++) {
out_poly[i][j] = out_poly[i][j] + out[deg1][deg2] * pow[i][deg1] * pow[j][deg2]; // (i ** deg1) * (j ** deg2);
}
}
}
}
for (var i = 0; i < la + lb - 1; i++) {
for (var j = 0; j < ka + kb - 1; j++) {
out_poly[i][j] === a_poly[i][j] * b_poly[i][j];
}
}
}

View File

@@ -0,0 +1,529 @@
pragma circom 2.0.3;
function min(a, b) {
if(a < b)
return a;
return b;
}
function max(a, b) {
if(a > b)
return a;
return b;
}
function log_ceil(n) {
var n_temp = n;
for (var i = 0; i < 254; i++) {
if (n_temp == 0) {
return i;
}
n_temp = n_temp \ 2;
}
return 254;
}
function div_ceil(m, n) {
var ret = 0;
if (m % n == 0) {
ret = m \ n;
} else {
ret = m \ n + 1;
}
return ret;
}
function SplitFn(in, n, m) {
return [in % (1 << n), (in \ (1 << n)) % (1 << m)];
}
function SplitThreeFn(in, n, m, k) {
return [in % (1 << n), (in \ (1 << n)) % (1 << m), (in \ (1 << n + m)) % (1 << k)];
}
// 1 if true, 0 if false
function long_gt(n, k, a, b) {
for (var i = k - 1; i >= 0; i--) {
if (a[i] > b[i]) {
return 1;
}
if (a[i] < b[i]) {
return 0;
}
}
return 0;
}
function long_is_zero(k, a){
for(var idx=0; idx<k; idx++){
if(a[idx] != 0)
return 0;
}
return 1;
}
// n bits per register
// a has k registers
// b has k registers
// output has k+1 registers
function long_add(n, k, a, b){
var carry = 0;
var sum[50];
for(var i=0; i<k; i++){
var sumAndCarry[2] = SplitFn(a[i] + b[i] + carry, n, n);
sum[i] = sumAndCarry[0];
carry = sumAndCarry[1];
}
sum[k] = carry;
return sum;
}
// n bits per register
// a has k registers
// b has k registers
// c has k registers
// d has k registers
// output has k+1 registers
function long_add4(n, k, a, b, c, d){
var carry = 0;
var sum[50];
for(var i=0; i < k; i++){
var sumAndCarry[2] = SplitFn(a[i] + b[i] + c[i] + d[i] + carry, n, n);
sum[i] = sumAndCarry[0];
carry = sumAndCarry[1];
}
sum[k] = carry;
return sum;
}
// n bits per register
// a has k1 registers
// b has k2 registers
// assume k1 > k2
// output has k1+1 registers
function long_add_unequal(n, k1, k2, a, b){
var carry = 0;
var sum[50];
for(var i=0; i<k1; i++){
if (i < k2) {
var sumAndCarry[2] = SplitFn(a[i] + b[i] + carry, n, n);
sum[i] = sumAndCarry[0];
carry = sumAndCarry[1];
} else {
var sumAndCarry[2] = SplitFn(a[i] + carry, n, n);
sum[i] = sumAndCarry[0];
carry = sumAndCarry[1];
}
}
sum[k1] = carry;
return sum;
}
// n bits per register
// a has k registers
// b has k registers
// a >= b
function long_sub(n, k, a, b) {
var diff[50];
var borrow[50];
for (var i = 0; i < k; i++) {
if (i == 0) {
if (a[i] >= b[i]) {
diff[i] = a[i] - b[i];
borrow[i] = 0;
} else {
diff[i] = a[i] - b[i] + (1 << n);
borrow[i] = 1;
}
} else {
if (a[i] >= b[i] + borrow[i - 1]) {
diff[i] = a[i] - b[i] - borrow[i - 1];
borrow[i] = 0;
} else {
diff[i] = (1 << n) + a[i] - b[i] - borrow[i - 1];
borrow[i] = 1;
}
}
}
return diff;
}
// a is a n-bit scalar
// b has k registers
function long_scalar_mult(n, k, a, b) {
var out[50];
for (var i = 0; i < 50; i++) {
out[i] = 0;
}
for (var i = 0; i < k; i++) {
var temp = out[i] + (a * b[i]);
out[i] = temp % (1 << n);
out[i + 1] = out[i + 1] + temp \ (1 << n);
}
return out;
}
// n bits per register
// a has k + m registers
// b has k registers
// out[0] has length m + 1 -- quotient
// out[1] has length k -- remainder
// implements algorithm of https://people.eecs.berkeley.edu/~fateman/282/F%20Wright%20notes/week4.pdf
// b[k-1] must be nonzero!
function long_div2(n, k, m, a, b){
var out[2][50];
// assume k+m < 50
var remainder[50];
for (var i = 0; i < m + k; i++) {
remainder[i] = a[i];
}
var dividend[50];
for (var i = m; i >= 0; i--) {
if (i == m) {
dividend[k] = 0;
for (var j = k - 1; j >= 0; j--) {
dividend[j] = remainder[j + m];
}
} else {
for (var j = k; j >= 0; j--) {
dividend[j] = remainder[j + i];
}
}
out[0][i] = short_div(n, k, dividend, b);
var mult_shift[50] = long_scalar_mult(n, k, out[0][i], b);
var subtrahend[50];
for (var j = 0; j < m + k; j++) {
subtrahend[j] = 0;
}
for (var j = 0; j <= k; j++) {
if (i + j < m + k) {
subtrahend[i + j] = mult_shift[j];
}
}
remainder = long_sub(n, m + k, remainder, subtrahend);
}
for (var i = 0; i < k; i++) {
out[1][i] = remainder[i];
}
out[1][k] = 0;
return out;
}
function long_div(n, k, a, b) {
return long_div2(n, k, k, a, b);
}
// n bits per register
// a has k + 1 registers
// b has k registers
// assumes leading digit of b is at least 2^(n - 1)
// 0 <= a < (2**n) * b
function short_div_norm(n, k, a, b) {
var qhat = (a[k] * (1 << n) + a[k - 1]) \ b[k - 1];
if (qhat > (1 << n) - 1) {
qhat = (1 << n) - 1;
}
var mult[50] = long_scalar_mult(n, k, qhat, b);
if (long_gt(n, k + 1, mult, a) == 1) {
mult = long_sub(n, k + 1, mult, b);
if (long_gt(n, k + 1, mult, a) == 1) {
return qhat - 2;
} else {
return qhat - 1;
}
} else {
return qhat;
}
}
// n bits per register
// a has k + 1 registers
// b has k registers
// assumes leading digit of b is non-zero
// 0 <= a < b * 2^n
function short_div(n, k, a, b) {
var scale = (1 << n) \ (1 + b[k - 1]);
// k + 2 registers now
var norm_a[50] = long_scalar_mult(n, k + 1, scale, a);
// k + 1 registers now
var norm_b[50] = long_scalar_mult(n, k, scale, b);
var ret;
if (norm_b[k] != 0) {
ret = short_div_norm(n, k + 1, norm_a, norm_b);
} else {
ret = short_div_norm(n, k, norm_a, norm_b);
}
return ret;
}
// a = a0 + a1 * X + ... + a[k-1] * X^{k-1} with X = 2^n
// a_i can be "negative" assume a_i in (-2^251, 2^251)
// output is the value of a with a_i all of the same sign
// out[50] = 0 if positive, 1 if negative
function signed_long_to_short(n, k, a){
var out[51];
var MAXL = 50;
var temp[51];
// is a positive?
for(var i=0; i<k; i++) temp[i] = a[i];
for(var i=k; i<=MAXL; i++) temp[i] = 0;
var X = (1<<n);
for(var i=0; i<MAXL; i++){
if(temp[i] >= 0){ // circom automatically takes care of signs in comparator
out[i] = temp[i] % X;
temp[i+1] += temp[i] \ X;
}else{
var borrow = (-temp[i] + X - 1 ) \ X;
out[i] = temp[i] + borrow * X;
temp[i+1] -= borrow;
}
}
if(temp[MAXL] >= 0){
assert(temp[MAXL]==0); // otherwise not enough registers!
out[MAXL] = 0;
return out;
}
// must be negative then, reset
for(var i=0; i<k; i++) temp[i] = a[i];
for(var i=k; i<=MAXL; i++) temp[i] = 0;
for(var i=0; i<MAXL; i++){
if(temp[i] < 0){
var carry = (-temp[i]) \ X;
out[i] = temp[i] + carry * X;
temp[i+1] -= carry;
}else{
var borrow = (temp[i] + X - 1 ) \ X;
out[i] = temp[i] - borrow * X;
temp[i+1] += borrow;
}
}
assert( temp[MAXL] == 0 );
out[MAXL] = 1;
return out;
}
// n bits per register
// a and b both have k registers
// out[0] has length 2 * k
// adapted from BigMulShortLong and LongToShortNoEndCarry witness computation
function prod(n, k, a, b) {
// first compute the intermediate values. taken from BigMulShortLong
var prod_val[50]; // length is 2 * k - 1
for (var i = 0; i < 2 * k - 1; i++) {
prod_val[i] = 0;
if (i < k) {
for (var a_idx = 0; a_idx <= i; a_idx++) {
prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx];
}
} else {
for (var a_idx = i - k + 1; a_idx < k; a_idx++) {
prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx];
}
}
}
// now do a bunch of carrying to make sure registers not overflowed. taken from LongToShortNoEndCarry
var out[50]; // length is 2 * k
var split[50][3]; // first dimension has length 2 * k - 1
for (var i = 0; i < 2 * k - 1; i++) {
split[i] = SplitThreeFn(prod_val[i], n, n, n);
}
var carry[50]; // length is 2 * k - 1
carry[0] = 0;
out[0] = split[0][0];
if (2 * k - 1 > 1) {
var sumAndCarry[2] = SplitFn(split[0][1] + split[1][0], n, n);
out[1] = sumAndCarry[0];
carry[1] = sumAndCarry[1];
}
if (2 * k - 1 > 2) {
for (var i = 2; i < 2 * k - 1; i++) {
var sumAndCarry[2] = SplitFn(split[i][0] + split[i-1][1] + split[i-2][2] + carry[i-1], n, n);
out[i] = sumAndCarry[0];
carry[i] = sumAndCarry[1];
}
out[2 * k - 1] = split[2*k-2][1] + split[2*k-3][2] + carry[2*k-2];
}
return out;
}
// n bits per register
// a and b both have l x k registers
// out has length 2l - 1 x 2k
// adapted from BigMultShortLong2D and LongToShortNoEndCarry2 witness computation
function prod2D(n, k, l, a, b) {
// first compute the intermediate values. taken from BigMulShortLong
var prod_val[20][50]; // length is 2l - 1 by 2k - 1
for (var i = 0; i < 2 * k - 1; i++) {
for (var j = 0; j < 2 * l - 1; j ++) {
prod_val[j][i] = 0;
}
}
for (var i1 = 0; i1 < k; i1 ++) {
for (var i2 = 0; i2 < k; i2 ++) {
for (var j1 = 0; j1 < l; j1 ++) {
for (var j2 = 0; j2 < l; j2 ++) {
prod_val[j1+j2][i1+i2] = prod_val[j1+j2][i1+i2] + a[j1][i1] * b[j2][i2];
}
}
}
}
// now do a bunch of carrying to make sure registers not overflowed. taken from LongToShortNoEndCarry2
var out[20][50]; // length is 2 * l by 2 * k
var split[20][50][3]; // second dimension has length 2 * k - 1
for (var j = 0; j < 2 * l - 1; j ++) {
for (var i = 0; i < 2 * k - 1; i++) {
split[j][i] = SplitThreeFn(prod_val[j][i], n, n, n);
}
}
var carry[20][50]; // length is 2l-1 x 2k
var sumAndCarry[20][2];
for ( var j = 0; j < 2 * l - 1; j ++) {
carry[j][0] = 0;
out[j][0] = split[j][0][0];
if (2 * k - 1 > 1) {
sumAndCarry[j] = SplitFn(split[j][0][1] + split[j][1][0], n, n);
out[j][1] = sumAndCarry[j][0];
carry[j][1] = sumAndCarry[j][1];
}
if (2 * k - 1 > 2) {
for (var i = 2; i < 2 * k - 1; i++) {
sumAndCarry[j] = SplitFn(split[j][i][0] + split[j][i-1][1] + split[j][i-2][2] + carry[j][i-1], n, n);
out[j][i] = sumAndCarry[j][0];
carry[j][i] = sumAndCarry[j][1];
}
out[j][2 * k - 1] = split[j][2*k-2][1] + split[j][2*k-3][2] + carry[j][2*k-2];
}
}
return out;
}
// Put all modular arithmetic, aka F_p field stuff, at the end
function long_add_mod(n, k, a, b, p) {
var sum[50] = long_add(n,k,a,b);
var temp[2][50] = long_div2(n,k,1,sum,p);
return temp[1];
}
function long_sub_mod(n, k, a, b, p) {
if(long_gt(n, k, b, a) == 1){
return long_add(n, k, a, long_sub(n,k,p,b));
}else{
return long_sub(n, k, a, b);
}
}
function prod_mod(n, k, a, b, p) {
var prod[50] = prod(n,k,a,b);
var temp[2][50] = long_div(n,k,prod,p);
return temp[1];
}
// n bits per register
// a has k registers
// p has k registers
// e has k registers
// k * n <= 500
// p is a prime
// computes a^e mod p
function mod_exp(n, k, a, p, e) {
var eBits[500]; // length is k * n
var bitlength;
for (var i = 0; i < k; i++) {
for (var j = 0; j < n; j++) {
eBits[j + n * i] = (e[i] >> j) & 1;
if(eBits[j + n * i] == 1)
bitlength = j + n * i + 1;
}
}
var out[50]; // length is k
for (var i = 0; i < 50; i++) {
out[i] = 0;
}
out[0] = 1;
// repeated squaring
for (var i = bitlength-1; i >= 0; i--) {
// multiply by a if bit is 0
if (eBits[i] == 1) {
var temp[50]; // length 2 * k
temp = prod(n, k, out, a);
var temp2[2][50];
temp2 = long_div(n, k, temp, p);
out = temp2[1];
}
// square, unless we're at the end
if (i > 0) {
var temp[50]; // length 2 * k
temp = prod(n, k, out, out);
var temp2[2][50];
temp2 = long_div(n, k, temp, p);
out = temp2[1];
}
}
return out;
}
// n bits per register
// a has k registers
// p has k registers
// k * n <= 500
// p is a prime
// if a == 0 mod p, returns 0
// else computes inv = a^(p-2) mod p
function mod_inv(n, k, a, p) {
var isZero = 1;
for (var i = 0; i < k; i++) {
if (a[i] != 0) {
isZero = 0;
}
}
if (isZero == 1) {
var ret[50];
for (var i = 0; i < k; i++) {
ret[i] = 0;
}
return ret;
}
var pCopy[50];
for (var i = 0; i < 50; i++) {
if (i < k) {
pCopy[i] = p[i];
} else {
pCopy[i] = 0;
}
}
var two[50];
for (var i = 0; i < 50; i++) {
two[i] = 0;
}
two[0] = 2;
var pMinusTwo[50];
pMinusTwo = long_sub(n, k, pCopy, two); // length k
var out[50];
out = mod_exp(n, k, a, pCopy, pMinusTwo);
return out;
}

View File

@@ -2,8 +2,8 @@ pragma circom 2.0.3;
include "../../../node_modules/circomlib/circuits/bitify.circom";
include "fp2.circom";
include "../../../node_modules/@zk-email/circuits/lib/bigint.circom";
include "../../../node_modules/@zk-email/circuits/lib/bigint-func.circom";
include "bigint.circom";
include "bigint_func.circom";
// in[i] = (x_i, y_i)
// Implements constraint: (y_1 + y_3) * (x_2 - x_1) - (y_2 - y_1)*(x_1 - x_3) = 0 mod p

View File

@@ -3,7 +3,7 @@
pragma circom 2.1.5;
include "../../../node_modules/circomlib/circuits/multiplexer.circom";
include "../../../node_modules/@zk-email/circuits/lib/bigint-func.circom";
include "bigint_func.circom";
include "p256.circom";
include "ecdsa_func.circom";
include "p256_func.circom";

View File

@@ -1,7 +1,6 @@
pragma circom 2.0.3;
// include "../../../node_modules/@zk-email/circuits/lib/bigint-func.circom";
include "../../../node_modules/@zk-email/circuits/lib/bigint-func.circom";
include "bigint_func.circom";
function get_fp_sgn0(a){
return a[0] % 2;

View File

@@ -0,0 +1,295 @@
pragma circom 2.0.3;
include "bigint.circom";
include "bigint_func.circom";
// a[i], b[i] in 0... 2**n-1
// represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k)
// calculates (a+b)%p, where 0<= a,b < p
template FpAdd(n, k, p){
assert(n <= 252);
signal input a[k];
signal input b[k];
signal output out[k];
component add = BigAdd(n,k);
for (var i = 0; i < k; i++) {
add.a[i] <== a[i];
add.b[i] <== b[i];
}
component lt = BigLessThan(n, k+1);
for (var i = 0; i < k; i++) {
lt.a[i] <== add.out[i];
lt.b[i] <== p[i];
}
lt.a[k] <== add.out[k];
lt.b[k] <== 0;
component sub = BigSub(n,k+1);
for (var i = 0; i < k; i++) {
sub.a[i] <== add.out[i];
sub.b[i] <== p[i] - lt.out * p[i];
}
sub.a[k] <== add.out[k];
sub.b[k] <== 0;
sub.out[k] === 0;
for (var i = 0; i < k; i++) {
out[i] <== sub.out[i];
}
}
// calculates (a - b) % p, where a, b < p
// note: does not assume a >= b
template FpSubtract(n, k, p){
assert(n <= 252);
signal input a[k];
signal input b[k];
signal output out[k];
component sub = BigSub(n, k);
for (var i = 0; i < k; i++){
sub.a[i] <== a[i];
sub.b[i] <== b[i];
}
signal flag;
flag <== sub.underflow;
component add = BigAdd(n, k);
for (var i = 0; i < k; i++){
add.a[i] <== sub.out[i];
add.b[i] <== p[i];
}
signal tmp[k];
for (var i = 0; i < k; i++){
tmp[i] <== (1 - flag) * sub.out[i];
out[i] <== tmp[i] + flag * add.out[i];
}
}
// Input: in <= p
// Output: -in (mod p) = p - in if in != 0, else 0
// Constrains in <= p
template FpNegate(n, k, p){
signal input in[k];
signal output out[k];
component neg = BigSub(n, k);
component is_zero = BigIsZero(k);
for(var idx=0; idx<k; idx++){
neg.a[idx] <== p[idx];
neg.b[idx] <== in[idx];
is_zero.in[idx] <== in[idx];
}
neg.underflow === 0; // constrain in <= p
for(var idx=0; idx<k; idx++)
out[idx] <== (1-is_zero.out)*neg.out[idx];
}
template FpMultiply(n, k, p) {
assert(n <= 252);
signal input a[k];
signal input b[k];
signal output out[k];
var LOGK = log_ceil(k);
component nocarry = BigMultShortLong(n, k, 2*n + LOGK);
for (var i = 0; i < k; i++) {
nocarry.a[i] <== a[i];
nocarry.b[i] <== b[i];
}
component red = PrimeReduce(n, k, k-1, p, 3*n + 2*LOGK);
for(var i=0; i<2*k-1; i++)
red.in[i] <== nocarry.out[i];
component big_mod = SignedFpCarryModP(n, k, 3*n + 2*LOGK, p);
for (var i = 0; i < k; i++)
big_mod.in[i] <== red.out[i];
for (var i = 0; i < k; i++)
out[i] <== big_mod.out[i];
}
// constrain in = p * X + Y
// in[i] in (-2^overflow, 2^overflow)
// assume registers of X have abs value < 2^{overflow - n - log(min(k,m)) - 1}
// assume overflow - 1 >= n
template CheckCarryModP(n, k, m, overflow, p){
signal input in[k];
signal input X[m];
signal input Y[k];
assert( overflow < 251 );
assert( n <= overflow - 1);
component pX;
component carry_check;
pX = BigMultShortLongUnequal(n, k, m, overflow); // p has k registers, X has m registers, so output really has k+m-1 registers
// overflow register in (-2^{overflow-1} , 2^{overflow-1})
for(var i=0; i<k; i++)
pX.a[i] <== p[i];
for(var i=0; i<m; i++)
pX.b[i] <== X[i];
// in - p*X - Y has registers in (-2^{overflow+1}, 2^{overflow+1})
carry_check = CheckCarryToZero(n, overflow+1, k+m-1 );
for(var i=0; i<k; i++){
carry_check.in[i] <== in[i] - pX.out[i] - Y[i];
}
for(var i=k; i<k+m-1; i++)
carry_check.in[i] <== -pX.out[i];
}
// solve for in = p * X + out
// assume in has registers in (-2^overflow, 2^overflow)
// X has registers lying in [-2^n, 2^n)
// X has at most Ceil( overflow / n ) registers
// out has registers in [0, 2^n) but don't constrain out < p
template SignedFpCarryModP(n, k, overflow, p){
signal input in[k];
var m = (overflow + n - 1) \ n;
signal output X[m];
signal output out[k];
assert( overflow < 251 );
var Xvar[2][50] = get_signed_Fp_carry_witness(n, k, m, in, p);
component X_range_checks[m];
component range_checks[k];
//component lt = BigLessThan(n, k);
for(var i=0; i<k; i++){
out[i] <-- Xvar[1][i];
range_checks[i] = Num2Bits(n);
range_checks[i].in <== out[i];
//lt.a[i] <== out[i];
//lt.b[i] <== p[i];
}
//lt.out === 1;
for(var i=0; i<m; i++){
X[i] <-- Xvar[0][i];
X_range_checks[i] = Num2Bits(n+1);
X_range_checks[i].in <== X[i] + (1<<n); // X[i] should be between [-2^n, 2^n)
}
component mod_check = CheckCarryModP(n, k, m, overflow, p);
for(var i=0; i<k; i++){
mod_check.in[i] <== in[i];
mod_check.Y[i] <== out[i];
}
for(var i=0; i<m; i++){
mod_check.X[i] <== X[i];
}
}
// Constrain in = 0 mod p by solving for in = p * X
// assume in has registers in (-2^overflow, 2^overflow)
// X has registers lying in [-2^n, 2^n)
// X has at most Ceil( overflow / n ) registers
// save range check on Y compared to SignedFpCarryModP
template SignedCheckCarryModToZero(n, k, overflow, p){
signal input in[k];
var m = (overflow + n - 1) \ n;
signal output X[m];
assert( overflow < 251 );
var Xvar[2][50] = get_signed_Fp_carry_witness(n, k, m, in, p);
component X_range_checks[m];
for(var i=0; i<m; i++){
X[i] <-- Xvar[0][i];
X_range_checks[i] = Num2Bits(n+1);
X_range_checks[i].in <== X[i] + (1<<n); // X[i] should be between [-2^n, 2^n)
}
component mod_check = CheckCarryModP(n, k, m, overflow, p);
for(var i=0; i<k; i++){
mod_check.in[i] <== in[i];
mod_check.Y[i] <== 0;
}
for(var i=0; i<m; i++){
mod_check.X[i] <== X[i];
}
}
// in has k registers, elt of Fp
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4.1
// return in % 2
// This requires `in` to be the unique element < p.
// NOTE: different from Wahby-Boneh paper https://eprint.iacr.org/2019/403.pdf and python reference code: https://github.com/algorand/bls_sigs_ref/blob/master/python-impl/opt_swu_g2.py
template FpSgn0(n, k, p){
signal input in[k];
signal output out;
// constrain in < p
component lt = BigLessThan(n, k);
for(var i=0; i<k; i++){
lt.a[i] <== in[i];
lt.b[i] <== p[i];
}
lt.out === 1;
// note we only need in[0] !
var r = in[0] % 2;
var q = (in[0] - r) / 2;
out <-- r;
signal div;
div <-- q;
out * (1 - out) === 0;
in[0] === 2 * div + out;
}
template FpIsZero(n, k, p){
signal input in[k];
signal output out;
// check that in < p
component lt = BigLessThan(n, k);
component isZero = BigIsZero(k);
for(var i = 0; i < k; i++) {
lt.a[i] <== in[i];
lt.b[i] <== p[i];
isZero.in[i] <== in[i];
}
lt.out === 1;
out <== isZero.out;
}
template FpIsEqual(n, k, p){
signal input in[2][k];
signal output out;
// check in[i] < p
component lt[2];
for(var i = 0; i < 2; i++){
lt[i] = BigLessThan(n, k);
for(var idx=0; idx<k; idx++){
lt[i].a[idx] <== in[i][idx];
lt[i].b[idx] <== p[idx];
}
lt[i].out === 1;
}
component isEqual[k+1];
var sum = 0;
for(var i = 0; i < k; i++){
isEqual[i] = IsEqual();
isEqual[i].in[0] <== in[0][i];
isEqual[i].in[1] <== in[1][i];
sum = sum + isEqual[i].out;
}
isEqual[k] = IsEqual();
isEqual[k].in[0] <== sum;
isEqual[k].in[1] <== k;
out <== isEqual[k].out;
}

View File

@@ -1,9 +1,9 @@
pragma circom 2.0.3;
include "@zk-email/circuits/lib/bigint.circom";
include "@zk-email/circuits/lib/bigint-func.circom";
include "bigint.circom";
include "bigint_func.circom";
include "field_elements_func.circom";
include "@zk-email/circuits/lib/fp.circom";
include "fp.circom";
// add two elements in Fp2
template Fp2Add(n, k, p) {

View File

@@ -2,7 +2,7 @@
pragma circom 2.1.5;
include "@zk-email/circuits/lib/bigint-func.circom";
include "bigint_func.circom";
include "p256_func.circom";
// P = 2^256 - 2^224 + 2^192 + 2^96 - 1

View File

@@ -41,7 +41,6 @@ describe('Register - SHA1 WITH ECDSA', function () {
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
'./node_modules/circomlib/circuits',
'./node_modules/dmpierre/sha1-circom/circuits',
'./node_modules/@zk-email/circuits',
],
}
);

View File

@@ -27,6 +27,7 @@ describe('Register - SHA1 RSA', function () {
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
'./node_modules/circomlib/circuits',
'./node_modules/dmpierre/sha1-circom/circuits',
'./node_modules/@zk-email/circuits ',
],
}
);