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