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