arnaucube
arnaucube/circomlibPublic Self-hosted repository
circomlib/circuits/poseidon.circom
circuits/poseidon.circomRaw
 1include "./poseidon_constants.circom";
 2
 3template Sigma() {
 4    signal input in;
 5    signal output out;
 6
 7    signal in2;
 8    signal in4;
 9
10    in2 <== in*in;
11    in4 <== in2*in2;
12
13    out <== in4*in;
14}
15
16template Ark(t, C, r) {
17    signal input in[t];
18    signal output out[t];
19
20    for (var i=0; i<t; i++) {
21        out[i] <== in[i] + C[i + r];
22    }
23}
24
25template Mix(t, M) {
26    signal input in[t];
27    signal output out[t];
28
29    var lc;
30    for (var i=0; i<t; i++) {
31        lc = 0;
32        for (var j=0; j<t; j++) {
33            lc += M[j][i]*in[j];
34        }
35        out[i] <== lc;
36    }
37}
38
39template Poseidon(nInputs) {
40    signal input inputs[nInputs];
41    signal output out;
42
43    // Using recommended parameters from whitepaper https://eprint.iacr.org/2019/458.pdf (table 2, table 8)
44    // Generated by https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/calc_round_numbers.py
45    // And rounded up to nearest integer that divides by t
46    var N_ROUNDS_P[8] = [56, 57, 56, 60, 60, 63, 64, 63];
47    var t = nInputs + 1;
48    var nRoundsF = 8;
49    var nRoundsP = N_ROUNDS_P[t - 2];
50    var C[t*(nRoundsF + nRoundsP)] = POSEIDON_C(t);
51    var M[t][t] = POSEIDON_M(t);
52
53    component ark[nRoundsF + nRoundsP - 1];
54    component sigmaF[nRoundsF - 1][t];
55    component sigmaP[nRoundsP];
56    component mix[nRoundsF + nRoundsP - 1];
57
58    var k;
59
60    for (var i=0; i<nRoundsF + nRoundsP - 1; i++) {
61        ark[i] = Ark(t, C, t*i);
62        for (var j=0; j<t; j++) {
63            if (i==0) {
64                if (j<nInputs) {
65                    ark[i].in[j] <== inputs[j];
66                } else {
67                    ark[i].in[j] <== 0;
68                }
69            } else {
70                ark[i].in[j] <== mix[i-1].out[j];
71            }
72        }
73
74        if (i < nRoundsF/2 || i >= nRoundsP + nRoundsF/2) {
75            k = i < nRoundsF/2 ? i : i - nRoundsP;
76            mix[i] = Mix(t, M);
77            for (var j=0; j<t; j++) {
78                sigmaF[k][j] = Sigma();
79                sigmaF[k][j].in <== ark[i].out[j];
80                mix[i].in[j] <== sigmaF[k][j].out;
81            }
82        } else {
83            k = i - nRoundsF/2;
84            mix[i] = Mix(t, M);
85            sigmaP[k] = Sigma();
86            sigmaP[k].in <== ark[i].out[0];
87            mix[i].in[0] <== sigmaP[k].out;
88            for (var j=1; j<t; j++) {
89                mix[i].in[j] <== ark[i].out[j];
90            }
91        }
92    }
93
94    // last round is done only for the first word, so we do it manually to save constraints
95    component lastSigmaF = Sigma();
96    lastSigmaF.in <== mix[nRoundsF + nRoundsP - 2].out[0] + C[t*(nRoundsF + nRoundsP - 1)];
97    out <== lastSigmaF.out;
98}