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}