circomlib/src/poseidon.js
src/poseidon.jsRaw
1const bigInt = require("big-integer");
2const blake2b = require("blake2b");
3const assert = require("assert");
4const ZqField = require("fflib").ZqField;
5const utils = require("./utils");
6
7const F = new ZqField(bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
8
9const SEED = "poseidon";
10const NROUNDSF = 8;
11const NROUNDSP = 57;
12const T = 6;
13
14function getPseudoRandom(seed, n) {
15 const res = [];
16 let input = Buffer.from(seed);
17 let h = blake2b(32).update(input).digest();
18 while (res.length<n) {
19 const n = F.normalize(utils.leBuff2int(h));
20 res.push(n);
21 h = blake2b(32).update(h).digest();
22 }
23
24 return res;
25}
26
27function allDifferent(v) {
28 for (let i=0; i<v.length; i++) {
29 if (v[i].isZero()) return false;
30 for (let j=i+1; j<v.length; j++) {
31 if (v[i].equals(v[j])) return false;
32 }
33 }
34 return true;
35}
36
37exports.getMatrix = (t, seed, nRounds) => {
38 if (typeof seed === "undefined") seed = SEED;
39 if (typeof nRounds === "undefined") nRounds = NROUNDSF + NROUNDSP;
40 if (typeof t === "undefined") t = T;
41 let nonce = "0000";
42 let cmatrix = getPseudoRandom(seed+"_matrix_"+nonce, t*2);
43 while (!allDifferent(cmatrix)) {
44 nonce = (Number(nonce)+1)+"";
45 while(nonce.length<4) nonce = "0"+nonce;
46 cmatrix = getPseudoRandom(seed+"_matrix_"+nonce, t*2);
47 }
48
49 const M = new Array(t);
50 for (let i=0; i<t; i++) {
51 M[i] = new Array(t);
52 for (let j=0; j<t; j++) {
53 M[i][j] = F.normalize(F.inverse(F.sub(cmatrix[i], cmatrix[t+j])));
54 }
55 }
56 return M;
57};
58
59exports.getConstants = (t, seed, nRounds) => {
60 if (typeof seed === "undefined") seed = SEED;
61 if (typeof nRounds === "undefined") nRounds = NROUNDSF + NROUNDSP;
62 if (typeof t === "undefined") t = T;
63 const cts = getPseudoRandom(seed+"_constants", nRounds);
64 return cts;
65};
66
67function ark(state, c) {
68 for (let j=0; j<state.length; j++ ) {
69 state[j] = F.add(state[j], c);
70 }
71}
72
73function sigma(a) {
74 return F.mul(a, F.square(F.square(a,a)));
75}
76
77function mix(state, M) {
78 const newState = new Array(state.length);
79 for (let i=0; i<state.length; i++) {
80 newState[i] = F.zero;
81 for (let j=0; j<state.length; j++) {
82 newState[i] = F.add(newState[i], F.mul(M[i][j], state[j]) );
83 }
84 }
85 for (let i=0; i<state.length; i++) state[i] = newState[i];
86}
87
88exports.createHash = (t, nRoundsF, nRoundsP, seed) => {
89
90 if (typeof seed === "undefined") seed = SEED;
91 if (typeof nRoundsF === "undefined") nRoundsF = NROUNDSF;
92 if (typeof nRoundsP === "undefined") nRoundsP = NROUNDSP;
93 if (typeof t === "undefined") t = T;
94
95 assert(nRoundsF % 2 == 0);
96 const C = exports.getConstants(t, seed, nRoundsF + nRoundsP);
97 const M = exports.getMatrix(t, seed, nRoundsF + nRoundsP);
98 return function(inputs) {
99 let state = [];
100 assert(inputs.length <= t);
101 assert(inputs.length > 0);
102 for (let i=0; i<inputs.length; i++) state[i] = bigInt(inputs[i]);
103 for (let i=inputs.length; i<t; i++) state[i] = F.zero;
104
105 for (let i=0; i< nRoundsF + nRoundsP; i++) {
106 ark(state, C[i]);
107 if ((i<nRoundsF/2) || (i >= nRoundsF/2 + nRoundsP)) {
108 for (let j=0; j<t; j++) state[j] = sigma(state[j]);
109 } else {
110 state[0] = sigma(state[0]);
111 }
112 mix(state, M);
113 }
114 return F.normalize(state[0]);
115 };
116};
117
118