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("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