micro-zk-proofs
Version:
Create & verify zero-knowledge SNARK proofs in parallel, using noble cryptography
103 lines • 3.71 kB
JavaScript
/**
* MiMC: Efficient Encryption and Cryptographic
* Hashing with Minimal Multiplicative Complexity.
* {@link https://eprint.iacr.org/2016/492.pdf}
* {@link https://crypto.ethereum.org/bounties/mimc-hash-challenge}
* @module
*/
import { bn254 } from '@noble/curves/bn254.js';
import { keccak_256 } from '@noble/hashes/sha3.js';
import { utf8ToBytes } from '@noble/hashes/utils.js';
const Fr = bn254.fields.Fr;
const SEED = 'mimcsponge';
const NROUNDS = 220;
/**
* Derives the MiMC sponge IV from the seed.
* @param seed - Seed string used for derivation.
* @returns Field element used as the initial vector.
* @example
* Derive the default MiMC IV, or pass your own seed for compatibility tests.
* ```ts
* const iv = getIV('mimcsponge');
* ```
*/
export function getIV(seed = SEED) {
return Fr.create(Fr.fromBytes(keccak_256(utf8ToBytes(`${seed}_iv`)), true));
}
/**
* Derives the MiMC round constants from the seed.
* @param seed - Seed string used for derivation.
* @param nRounds - Number of MiMC rounds to generate; must include the two zero endpoints.
* @returns Round constant list.
* @throws If `nRounds` is not an integer at least 2. {@link Error}
* @example
* Rebuild a short constant table for test vectors or compatibility checks.
* ```ts
* const constants = getConstants('mimcsponge', 4);
* ```
*/
export function getConstants(seed = SEED, nRounds = NROUNDS) {
// Circomlib-compatible tables reserve first and last constants as zero endpoints.
if (!Number.isSafeInteger(nRounds) || nRounds < 2)
throw new Error(`expected nRounds >= 2, got ${nRounds}`);
const cts = [BigInt(0)];
let c = keccak_256(utf8ToBytes(seed));
for (let i = 0; i < nRounds - 2; i++)
cts.push(Fr.create(Fr.fromBytes((c = keccak_256(c)), true)));
cts.push(BigInt(0));
return cts;
}
const CONSTANTS = /* @__PURE__ */ getConstants(SEED, NROUNDS);
/**
* Runs one MiMC sponge hash round sequence.
* @param L - Left input lane.
* @param R - Right input lane.
* @param k - Key value.
* @returns Updated left and right lanes.
* @example
* Run one MiMC round sequence on two field elements with an optional key.
* ```ts
* const round = hash(1n, 2n, 0n);
* round.xL;
* ```
*/
export function hash(L, R, k) {
for (let i = 0; i < NROUNDS; i++) {
const t = i == 0 ? Fr.addN(L, k) : Fr.addN(Fr.addN(L, k), CONSTANTS[i]);
const tmp = Fr.addN(R, Fr.pow(t, BigInt(5)));
if (i < NROUNDS - 1) {
R = L;
L = tmp;
}
else
R = tmp;
}
return { xL: Fr.create(L), xR: Fr.create(R) };
}
/**
* Hashes one or more field elements with the MiMC sponge.
* @param lst - Input field elements.
* @param key - Optional key value.
* @param numOutputs - Number of outputs to squeeze; must be at least one.
* @returns One field element or an array of field elements.
* @throws If `numOutputs` is not an integer at least 1. {@link Error}
* @example
* Hash one or more field elements and optionally squeeze multiple outputs.
* ```ts
* const out = multiHash([1n, 2n], 0n, 2);
* ```
*/
export function multiHash(lst, key = Fr.ZERO, numOutputs = 1) {
if (!Number.isSafeInteger(numOutputs) || numOutputs < 1)
throw new Error(`expected numOutputs >= 1, got ${numOutputs}`);
let R = Fr.ZERO;
let C = Fr.ZERO;
for (let i = 0; i < lst.length; i++)
({ xL: R, xR: C } = hash(Fr.addN(R, BigInt(lst[i])), C, key));
const out = [R];
for (let i = 1; i < numOutputs; i++, out.push(R))
({ xL: R, xR: C } = hash(R, C, key));
const res = out.map((x) => Fr.create(x));
return numOutputs === 1 ? res[0] : res;
}
//# sourceMappingURL=mimcsponge.js.map