Merge pull request #234 from zkemail/test/poseidon-modular

Feat: Added Poseidon Modular Tests
This commit is contained in:
Yush G
2024-11-22 02:20:33 -05:00
committed by GitHub
3 changed files with 86 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
import path from "path";
import { wasm as wasm_tester } from "circom_tester";
import { poseidonModular } from "@zk-email/helpers/src/hash";
describe("PoseidonModular", () => {
jest.setTimeout(30 * 60 * 1000); // 30 minutes
let circuit: any;
beforeAll(async () => {
circuit = await wasm_tester(
path.join(
__dirname,
"./test-circuits/poseidon-modular-test.circom"
),
{
recompile: true,
include: path.join(__dirname, "../../../node_modules"),
output: path.join(__dirname, "./compiled-test-circuits"),
}
);
});
it("should hash correctly", async function () {
const inputs = Array.from({ length: 37 }, () =>
BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
);
const hash = await poseidonModular(inputs);
const witness = await circuit.calculateWitness({
in: inputs,
});
await circuit.checkConstraints(witness);
expect(witness[1]).toEqual(hash);
});
});

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.6;
include "../../utils/hash.circom";
component main = PoseidonModular(37);

View File

@@ -8,3 +8,47 @@ export async function poseidonLarge(input: bigint, numChunks: number, bitsPerChu
return poseidon.F.toObject(hash) as Promise<bigint>;
}
/**
* Calculates Poseidon hash of an arbitrary number of inputs
* Mimics the behavior of PoseidonModular circuit
* @param inputs Array of bigints to be hashed
* @returns Promise<bigint> The final hash
*/
export async function poseidonModular(inputs: bigint[]): Promise<bigint> {
const poseidon = await buildPoseidon();
const CHUNK_SIZE = 16;
// Calculate number of chunks
const numElements = inputs.length;
let chunks = Math.floor(numElements / CHUNK_SIZE);
const lastChunkSize = numElements % CHUNK_SIZE;
if (lastChunkSize !== 0) {
chunks += 1;
}
let out: bigint | null = null;
// Process each chunk
for (let i = 0; i < chunks; i++) {
const start = i * CHUNK_SIZE;
let end = start + CHUNK_SIZE;
if (end > numElements) {
end = numElements;
}
const chunk = inputs.slice(start, end);
const chunkHash = poseidon.F.toObject(poseidon(chunk));
if (i === 0) {
out = chunkHash;
} else {
out = poseidon.F.toObject(poseidon([out as bigint, chunkHash]));
}
}
if (out === null) {
throw new Error("No inputs provided");
}
return out;
}