arnaucube
arnaucube/circomlibPublic Self-hosted repository
circomlib/src/poseidon.js
src/poseidon.jsRaw
  1const bigInt = require("big-integer");
  2const blake2b = require("blake2b");
  3const assert = require("assert");
  4const ZqField = require("ffjavascript").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    assert(t<=6);  // Force the same matrix for all.
 42    t=6;
 43    let nonce = "0000";
 44    let cmatrix = getPseudoRandom(seed+"_matrix_"+nonce, t*2);
 45    while (!allDifferent(cmatrix)) {
 46        nonce = (Number(nonce)+1)+"";
 47        while(nonce.length<4) nonce = "0"+nonce;
 48        cmatrix = getPseudoRandom(seed+"_matrix_"+nonce, t*2);
 49    }
 50
 51    const M = new Array(t);
 52    for (let i=0; i<t; i++) {
 53        M[i] = new Array(t);
 54        for (let j=0; j<t; j++) {
 55            M[i][j] = F.normalize(F.inv(F.sub(cmatrix[i], cmatrix[t+j])));
 56        }
 57    }
 58    return M;
 59};
 60
 61exports.getConstants = (t, seed, nRounds) => {
 62    if (typeof seed === "undefined") seed = SEED;
 63    if (typeof nRounds === "undefined") nRounds = NROUNDSF + NROUNDSP;
 64    if (typeof t === "undefined") t = T;
 65    const cts = getPseudoRandom(seed+"_constants", nRounds);
 66    return cts;
 67};
 68
 69function ark(state, c) {
 70    for (let j=0; j<state.length; j++ ) {
 71        state[j] = F.add(state[j], c);
 72    }
 73}
 74
 75function sigma(a) {
 76    return F.mul(a, F.square(F.square(a,a)));
 77}
 78
 79function mix(state, M) {
 80    const newState = new Array(state.length);
 81    for (let i=0; i<state.length; i++) {
 82        newState[i] = F.zero;
 83        for (let j=0; j<state.length; j++) {
 84            newState[i] = F.add(newState[i], F.mul(M[i][j], state[j]) );
 85        }
 86    }
 87    for (let i=0; i<state.length; i++) state[i] = newState[i];
 88}
 89
 90exports.createHash = (t, nRoundsF, nRoundsP, seed) => {
 91
 92    if (typeof seed === "undefined") seed = SEED;
 93    if (typeof nRoundsF === "undefined") nRoundsF = NROUNDSF;
 94    if (typeof nRoundsP === "undefined") nRoundsP = NROUNDSP;
 95    if (typeof t === "undefined") t = T;
 96
 97    assert(nRoundsF % 2 == 0);
 98    const C = exports.getConstants(t, seed, nRoundsF + nRoundsP);
 99    const M = exports.getMatrix(t, seed, nRoundsF + nRoundsP);
100    return function(inputs) {
101        let state = [];
102        assert(inputs.length <= t);
103        assert(inputs.length > 0);
104        for (let i=0; i<inputs.length; i++) state[i] = bigInt(inputs[i]);
105        for (let i=inputs.length; i<t; i++) state[i] = F.zero;
106
107        for (let i=0; i< nRoundsF + nRoundsP; i++) {
108            ark(state, C[i]);
109            if ((i<nRoundsF/2) || (i >= nRoundsF/2 + nRoundsP)) {
110                for (let j=0; j<t; j++) state[j] = sigma(state[j]);
111            } else {
112                state[0] = sigma(state[0]);
113            }
114            mix(state, M);
115        }
116        return F.normalize(state[0]);
117    };
118};
119
120