pragma circom 2.0.0;
|
|
|
|
include "./poseidon_constants.circom";
|
|
|
|
template Sigma() {
|
|
signal input in;
|
|
signal output out;
|
|
|
|
signal in2;
|
|
signal in4;
|
|
|
|
in2 <== in*in;
|
|
in4 <== in2*in2;
|
|
|
|
out <== in4*in;
|
|
}
|
|
|
|
template Ark(t, C, r) {
|
|
signal input in[t];
|
|
signal output out[t];
|
|
|
|
for (var i=0; i<t; i++) {
|
|
out[i] <== in[i] + C[i + r];
|
|
}
|
|
}
|
|
|
|
template Mix(t, M) {
|
|
signal input in[t];
|
|
signal output out[t];
|
|
|
|
var lc;
|
|
for (var i=0; i<t; i++) {
|
|
lc = 0;
|
|
for (var j=0; j<t; j++) {
|
|
lc += M[j][i]*in[j];
|
|
}
|
|
out[i] <== lc;
|
|
}
|
|
}
|
|
|
|
template MixLast(t, M, s) {
|
|
signal input in[t];
|
|
signal output out;
|
|
|
|
var lc = 0;
|
|
for (var j=0; j<t; j++) {
|
|
lc += M[j][s]*in[j];
|
|
}
|
|
out <== lc;
|
|
}
|
|
|
|
template MixS(t, S, r) {
|
|
signal input in[t];
|
|
signal output out[t];
|
|
|
|
|
|
var lc = 0;
|
|
for (var i=0; i<t; i++) {
|
|
lc += S[(t*2-1)*r+i]*in[i];
|
|
}
|
|
out[0] <== lc;
|
|
for (var i=1; i<t; i++) {
|
|
out[i] <== in[i] + in[0] * S[(t*2-1)*r + t + i -1];
|
|
}
|
|
}
|
|
|
|
template PoseidonEx(nInputs, nOuts) {
|
|
signal input inputs[nInputs];
|
|
signal input initialState;
|
|
signal output out[nOuts];
|
|
|
|
// Using recommended parameters from whitepaper https://eprint.iacr.org/2019/458.pdf (table 2, table 8)
|
|
// Generated by https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/calc_round_numbers.py
|
|
// And rounded up to nearest integer that divides by t
|
|
var N_ROUNDS_P[16] = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68];
|
|
var t = nInputs + 1;
|
|
var nRoundsF = 8;
|
|
var nRoundsP = N_ROUNDS_P[t - 2];
|
|
var C[t*nRoundsF + nRoundsP] = POSEIDON_C(t);
|
|
var S[ N_ROUNDS_P[t-2] * (t*2-1) ] = POSEIDON_S(t);
|
|
var M[t][t] = POSEIDON_M(t);
|
|
var P[t][t] = POSEIDON_P(t);
|
|
|
|
component ark[nRoundsF];
|
|
component sigmaF[nRoundsF][t];
|
|
component sigmaP[nRoundsP];
|
|
component mix[nRoundsF-1];
|
|
component mixS[nRoundsP];
|
|
component mixLast[nOuts];
|
|
|
|
|
|
ark[0] = Ark(t, C, 0);
|
|
for (var j=0; j<t; j++) {
|
|
if (j>0) {
|
|
ark[0].in[j] <== inputs[j-1];
|
|
} else {
|
|
ark[0].in[j] <== initialState;
|
|
}
|
|
}
|
|
|
|
for (var r = 0; r < nRoundsF\2-1; r++) {
|
|
for (var j=0; j<t; j++) {
|
|
sigmaF[r][j] = Sigma();
|
|
if(r==0) {
|
|
sigmaF[r][j].in <== ark[0].out[j];
|
|
} else {
|
|
sigmaF[r][j].in <== mix[r-1].out[j];
|
|
}
|
|
}
|
|
|
|
ark[r+1] = Ark(t, C, (r+1)*t);
|
|
for (var j=0; j<t; j++) {
|
|
ark[r+1].in[j] <== sigmaF[r][j].out;
|
|
}
|
|
|
|
mix[r] = Mix(t,M);
|
|
for (var j=0; j<t; j++) {
|
|
mix[r].in[j] <== ark[r+1].out[j];
|
|
}
|
|
|
|
}
|
|
|
|
for (var j=0; j<t; j++) {
|
|
sigmaF[nRoundsF\2-1][j] = Sigma();
|
|
sigmaF[nRoundsF\2-1][j].in <== mix[nRoundsF\2-2].out[j];
|
|
}
|
|
|
|
ark[nRoundsF\2] = Ark(t, C, (nRoundsF\2)*t );
|
|
for (var j=0; j<t; j++) {
|
|
ark[nRoundsF\2].in[j] <== sigmaF[nRoundsF\2-1][j].out;
|
|
}
|
|
|
|
mix[nRoundsF\2-1] = Mix(t,P);
|
|
for (var j=0; j<t; j++) {
|
|
mix[nRoundsF\2-1].in[j] <== ark[nRoundsF\2].out[j];
|
|
}
|
|
|
|
|
|
for (var r = 0; r < nRoundsP; r++) {
|
|
sigmaP[r] = Sigma();
|
|
if (r==0) {
|
|
sigmaP[r].in <== mix[nRoundsF\2-1].out[0];
|
|
} else {
|
|
sigmaP[r].in <== mixS[r-1].out[0];
|
|
}
|
|
|
|
mixS[r] = MixS(t, S, r);
|
|
for (var j=0; j<t; j++) {
|
|
if (j==0) {
|
|
mixS[r].in[j] <== sigmaP[r].out + C[(nRoundsF\2+1)*t + r];
|
|
} else {
|
|
if (r==0) {
|
|
mixS[r].in[j] <== mix[nRoundsF\2-1].out[j];
|
|
} else {
|
|
mixS[r].in[j] <== mixS[r-1].out[j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var r = 0; r < nRoundsF\2-1; r++) {
|
|
for (var j=0; j<t; j++) {
|
|
sigmaF[nRoundsF\2 + r][j] = Sigma();
|
|
if (r==0) {
|
|
sigmaF[nRoundsF\2 + r][j].in <== mixS[nRoundsP-1].out[j];
|
|
} else {
|
|
sigmaF[nRoundsF\2 + r][j].in <== mix[nRoundsF\2+r-1].out[j];
|
|
}
|
|
}
|
|
|
|
ark[ nRoundsF\2 + r + 1] = Ark(t, C, (nRoundsF\2+1)*t + nRoundsP + r*t );
|
|
for (var j=0; j<t; j++) {
|
|
ark[nRoundsF\2 + r + 1].in[j] <== sigmaF[nRoundsF\2 + r][j].out;
|
|
}
|
|
|
|
mix[nRoundsF\2 + r] = Mix(t,M);
|
|
for (var j=0; j<t; j++) {
|
|
mix[nRoundsF\2 + r].in[j] <== ark[nRoundsF\2 + r + 1].out[j];
|
|
}
|
|
|
|
}
|
|
|
|
for (var j=0; j<t; j++) {
|
|
sigmaF[nRoundsF-1][j] = Sigma();
|
|
sigmaF[nRoundsF-1][j].in <== mix[nRoundsF-2].out[j];
|
|
}
|
|
|
|
for (var i=0; i<nOuts; i++) {
|
|
mixLast[i] = MixLast(t,M,i);
|
|
for (var j=0; j<t; j++) {
|
|
mixLast[i].in[j] <== sigmaF[nRoundsF-1][j].out;
|
|
}
|
|
out[i] <== mixLast[i].out;
|
|
}
|
|
|
|
}
|
|
|
|
template Poseidon(nInputs) {
|
|
signal input inputs[nInputs];
|
|
signal output out;
|
|
|
|
component pEx = PoseidonEx(nInputs, 1);
|
|
pEx.initialState <== 0;
|
|
for (var i=0; i<nInputs; i++) {
|
|
pEx.inputs[i] <== inputs[i];
|
|
}
|
|
out <== pEx.out[0];
|
|
}
|