Skip to content

Commit

Permalink
feat: added poseidon modular tests
Browse files Browse the repository at this point in the history
  • Loading branch information
shreyas-londhe committed Oct 30, 2024
1 parent 3b3c808 commit bb697f8
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 0 deletions.
37 changes: 37 additions & 0 deletions packages/circuits/tests/poseidon-modular.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pragma circom 2.1.6;

include "../../utils/hash.circom";

component main = PoseidonModular(37);
50 changes: 50 additions & 0 deletions packages/helpers/src/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,53 @@ 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;
let chunkHash: bigint;

if (end > numElements) {
// last chunk
end = numElements;
const lastChunk = inputs.slice(start, end);
chunkHash = poseidon.F.toObject(poseidon(lastChunk));
} else {
const chunk = inputs.slice(start, end);
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;
}

0 comments on commit bb697f8

Please sign in to comment.