arnaucube
arnaucube/circomlibPublic Self-hosted repository
circomlib/src/poseidon.js
src/poseidon.jsRaw
 1const assert = require("assert");
 2const Scalar = require("ffjavascript").Scalar;
 3const ZqField = require("ffjavascript").ZqField;
 4const { unstringifyBigInts } = require("ffjavascript").utils;
 5
 6// Prime 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
 7const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
 8
 9// Parameters are generated by a reference script https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/generate_parameters_grain.sage
10// Used like so: sage generate_parameters_grain.sage 1 0 254 2 8 56 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
11const { C, M } = unstringifyBigInts(require("./poseidon_constants.json"));
12
13// Using recommended parameters from whitepaper https://eprint.iacr.org/2019/458.pdf (table 2, table 8)
14// Generated by https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/calc_round_numbers.py
15// And rounded up to nearest integer that divides by t
16const N_ROUNDS_F = 8;
17const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63];
18
19const pow5 = a => F.mul(a, F.square(F.square(a, a)));
20
21function poseidon(inputs) {
22    assert(inputs.length > 0);
23    assert(inputs.length < N_ROUNDS_P.length - 1);
24
25    const t = inputs.length + 1;
26    const nRoundsF = N_ROUNDS_F;
27    const nRoundsP = N_ROUNDS_P[t - 2];
28
29    let state = [...inputs.map(a => F.e(a)), F.zero];
30    for (let r = 0; r < nRoundsF + nRoundsP; r++) {
31        state = state.map((a, i) => F.add(a, C[t - 2][r * t + i]));
32
33        if (r < nRoundsF / 2 || r >= nRoundsF / 2 + nRoundsP) {
34            state = state.map(a => pow5(a));
35        } else {
36            state[0] = pow5(state[0]);
37        }
38
39        // no matrix multiplication in the last round
40        if (r < nRoundsF + nRoundsP - 1) {
41            state = state.map((_, i) =>
42                state.reduce((acc, a, j) => F.add(acc, F.mul(M[t - 2][j][i], a)), F.zero)
43            );
44        }
45    }
46    return F.normalize(state[0]);
47}
48
49module.exports = poseidon;