Skip to content

Commit

Permalink
Merge pull request #363 from semaphore-protocol/chore/snarkjs-circoml…
Browse files Browse the repository at this point in the history
…ibjs-types

New Snarkjs/Circomlibjs type definitions
  • Loading branch information
cedoor authored Sep 21, 2023
2 parents 4280bff + ebe3628 commit f212684
Show file tree
Hide file tree
Showing 16 changed files with 111 additions and 1,038 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@
"@commitlint/cli": "^16.0.2",
"@commitlint/config-conventional": "^16.0.0",
"@rollup/plugin-typescript": "^8.3.0",
"@types/circomlibjs": "^0.1.1",
"@types/download": "^8.0.1",
"@types/glob": "^7.2.0",
"@types/jest": "^27.4.0",
"@types/node": "^17.0.9",
"@types/rimraf": "^3.0.2",
"@types/snarkjs": "^0.7.2",
"@typescript-eslint/eslint-plugin": "^5.9.1",
"@typescript-eslint/parser": "^5.9.1",
"babel-jest": "^27.4.6",
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"@types/rimraf": "^3.0.2",
"chai": "^4.3.5",
"circomlib": "^2.0.2",
"circomlibjs": "^0.0.8",
"circomlibjs": "^0.1.7",
"download": "^8.0.0",
"ethers": "^5.6.8",
"hardhat": "^2.9.7",
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/tasks/deploy-semaphore-voting.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { poseidon_gencontract as poseidonContract } from "circomlibjs"
import { poseidonContract } from "circomlibjs"
import { task, types } from "hardhat/config"

task("deploy:semaphore-voting", "Deploy a SemaphoreVoting contract")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { poseidon_gencontract as poseidonContract } from "circomlibjs"
import { poseidonContract } from "circomlibjs"
import { task, types } from "hardhat/config"

task("deploy:semaphore-whistleblowing", "Deploy a SemaphoreWhistleblowing contract")
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/tasks/deploy-semaphore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { poseidon_gencontract as poseidonContract } from "circomlibjs"
import { poseidonContract } from "circomlibjs"
import { task, types } from "hardhat/config"
import { saveDeployedContracts } from "../scripts/utils"

Expand Down
2 changes: 1 addition & 1 deletion packages/hardhat/src/tasks/deploy-semaphore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { poseidon_gencontract as poseidonContract } from "circomlibjs"
import { poseidonContract } from "circomlibjs"
import { task, types } from "hardhat/config"

task("deploy:semaphore", "Deploy a Semaphore contract")
Expand Down
10 changes: 5 additions & 5 deletions packages/proof/src/generateProof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { BytesLike, Hexable } from "@ethersproject/bytes"
import { Group } from "@semaphore-protocol/group"
import type { Identity } from "@semaphore-protocol/identity"
import { MerkleProof } from "@zk-kit/incremental-merkle-tree"
import { groth16 } from "snarkjs"
import { groth16, NumericString } from "snarkjs"
import hash from "./hash"
import packProof from "./packProof"
import { FullProof, SnarkArtifacts } from "./types"
import { SemaphoreProof, SnarkArtifacts } from "./types"

/**
* Generates a Semaphore proof.
Expand All @@ -23,7 +23,7 @@ export default async function generateProof(
externalNullifier: BytesLike | Hexable | number | bigint,
signal: BytesLike | Hexable | number | bigint,
snarkArtifacts?: SnarkArtifacts
): Promise<FullProof> {
): Promise<SemaphoreProof> {
let merkleProof: MerkleProof

if ("depth" in groupOrMerkleProof) {
Expand Down Expand Up @@ -61,8 +61,8 @@ export default async function generateProof(
return {
merkleTreeRoot: publicSignals[0],
nullifierHash: publicSignals[1],
signal: BigNumber.from(signal).toString(),
externalNullifier: BigNumber.from(externalNullifier).toString(),
signal: BigNumber.from(signal).toString() as NumericString,
externalNullifier: BigNumber.from(externalNullifier).toString() as NumericString,
proof: packProof(proof)
}
}
5 changes: 3 additions & 2 deletions packages/proof/src/hash.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { BigNumber } from "@ethersproject/bignumber"
import { BytesLike, Hexable, zeroPad } from "@ethersproject/bytes"
import { keccak256 } from "@ethersproject/keccak256"
import { NumericString } from "snarkjs"

/**
* Creates a keccak256 hash of a message compatible with the SNARK scalar modulus.
* @param message The message to be hashed.
* @returns The message digest.
*/
export default function hash(message: BytesLike | Hexable | number | bigint): bigint {
export default function hash(message: BytesLike | Hexable | number | bigint): NumericString {
message = BigNumber.from(message).toTwos(256).toHexString()
message = zeroPad(message, 32)

return BigInt(keccak256(message)) >> BigInt(8)
return (BigInt(keccak256(message)) >> BigInt(8)).toString() as NumericString
}
46 changes: 13 additions & 33 deletions packages/proof/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import calculateNullifierHash from "./calculateNullifierHash"
import generateProof from "./generateProof"
import hash from "./hash"
import packProof from "./packProof"
import { FullProof } from "./types"
import { SemaphoreProof } from "./types"
import unpackProof from "./unpackProof"
import verifyProof from "./verifyProof"

Expand All @@ -21,7 +21,7 @@ describe("Proof", () => {

const identity = new Identity()

let fullProof: FullProof
let fullProof: SemaphoreProof
let curve: any

beforeAll(async () => {
Expand All @@ -34,9 +34,7 @@ describe("Proof", () => {

describe("# generateProof", () => {
it("Should not generate Semaphore proofs if the identity is not part of the group", async () => {
const group = new Group(treeDepth)

group.addMembers([BigInt(1), BigInt(2)])
const group = new Group(treeDepth, 20, [BigInt(1), BigInt(2)])

const fun = () =>
generateProof(identity, group, externalNullifier, signal, {
Expand All @@ -48,19 +46,15 @@ describe("Proof", () => {
})

it("Should not generate a Semaphore proof with default snark artifacts with Node.js", async () => {
const group = new Group(treeDepth)

group.addMembers([BigInt(1), BigInt(2), identity.commitment])
const group = new Group(treeDepth, 20, [BigInt(1), BigInt(2), identity.commitment])

const fun = () => generateProof(identity, group, externalNullifier, signal)

await expect(fun).rejects.toThrow("ENOENT: no such file or directory")
})

it("Should generate a Semaphore proof passing a group as parameter", async () => {
const group = new Group(treeDepth)

group.addMembers([BigInt(1), BigInt(2), identity.commitment])
const group = new Group(treeDepth, 20, [BigInt(1), BigInt(2), identity.commitment])

fullProof = await generateProof(identity, group, externalNullifier, signal, {
wasmFilePath,
Expand All @@ -72,9 +66,7 @@ describe("Proof", () => {
}, 20000)

it("Should generate a Semaphore proof passing a Merkle proof as parameter", async () => {
const group = new Group(treeDepth)

group.addMembers([BigInt(1), BigInt(2), identity.commitment])
const group = new Group(treeDepth, 20, [BigInt(1), BigInt(2), identity.commitment])

fullProof = await generateProof(identity, group.generateMerkleProof(2), externalNullifier, signal, {
wasmFilePath,
Expand Down Expand Up @@ -104,47 +96,35 @@ describe("Proof", () => {
it("Should hash the signal value correctly", async () => {
const signalHash = hash(signal)

expect(signalHash.toString()).toBe(
"8665846418922331996225934941481656421248110469944536651334918563951783029"
)
expect(signalHash).toBe("8665846418922331996225934941481656421248110469944536651334918563951783029")
})

it("Should hash the external nullifier value correctly", async () => {
const externalNullifierHash = hash(externalNullifier)

expect(externalNullifierHash.toString()).toBe(
expect(externalNullifierHash).toBe(
"244178201824278269437519042830883072613014992408751798420801126401127326826"
)
})

it("Should hash a number", async () => {
expect(hash(2).toString()).toBe(
"113682330006535319932160121224458771213356533826860247409332700812532759386"
)
expect(hash(2)).toBe("113682330006535319932160121224458771213356533826860247409332700812532759386")
})

it("Should hash a big number", async () => {
expect(hash(BigInt(2)).toString()).toBe(
"113682330006535319932160121224458771213356533826860247409332700812532759386"
)
expect(hash(BigInt(2))).toBe("113682330006535319932160121224458771213356533826860247409332700812532759386")
})

it("Should hash an hex number", async () => {
expect(hash("0x2").toString()).toBe(
"113682330006535319932160121224458771213356533826860247409332700812532759386"
)
expect(hash("0x2")).toBe("113682330006535319932160121224458771213356533826860247409332700812532759386")
})

it("Should hash an string number", async () => {
expect(hash("2").toString()).toBe(
"113682330006535319932160121224458771213356533826860247409332700812532759386"
)
expect(hash("2")).toBe("113682330006535319932160121224458771213356533826860247409332700812532759386")
})

it("Should hash an array", async () => {
expect(hash([2]).toString()).toBe(
"113682330006535319932160121224458771213356533826860247409332700812532759386"
)
expect(hash([2])).toBe("113682330006535319932160121224458771213356533826860247409332700812532759386")
})
})

Expand Down
23 changes: 12 additions & 11 deletions packages/proof/src/packProof.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { SnarkJSProof, Proof } from "./types"
import { Groth16Proof } from "snarkjs"
import { PackedProof } from "./types"

/**
* Packs a proof into a format compatible with Semaphore.
* @param originalProof The proof generated with SnarkJS.
* @param proof The Groth16 proof generated with SnarkJS.
* @returns The proof compatible with Semaphore.
*/
export default function packProof(originalProof: SnarkJSProof): Proof {
export default function packProof(proof: Groth16Proof): PackedProof {
return [
originalProof.pi_a[0],
originalProof.pi_a[1],
originalProof.pi_b[0][1],
originalProof.pi_b[0][0],
originalProof.pi_b[1][1],
originalProof.pi_b[1][0],
originalProof.pi_c[0],
originalProof.pi_c[1]
proof.pi_a[0],
proof.pi_a[1],
proof.pi_b[0][1],
proof.pi_b[0][0],
proof.pi_b[1][1],
proof.pi_b[1][0],
proof.pi_c[0],
proof.pi_c[1]
]
}
40 changes: 16 additions & 24 deletions packages/proof/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,25 @@
export type BigNumberish = string | bigint
export type NumericString = `${number}`

export type SnarkArtifacts = {
wasmFilePath: string
zkeyFilePath: string
}

export type SnarkJSProof = {
pi_a: BigNumberish[]
pi_b: BigNumberish[][]
pi_c: BigNumberish[]
protocol: string
curve: string
export type SemaphoreProof = {
merkleTreeRoot: NumericString
signal: NumericString
nullifierHash: NumericString
externalNullifier: NumericString
proof: PackedProof
}

export type FullProof = {
merkleTreeRoot: BigNumberish
signal: BigNumberish
nullifierHash: BigNumberish
externalNullifier: BigNumberish
proof: Proof
}

export type Proof = [
BigNumberish,
BigNumberish,
BigNumberish,
BigNumberish,
BigNumberish,
BigNumberish,
BigNumberish,
BigNumberish
export type PackedProof = [
NumericString,
NumericString,
NumericString,
NumericString,
NumericString,
NumericString,
NumericString,
NumericString
]
5 changes: 3 additions & 2 deletions packages/proof/src/unpackProof.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { SnarkJSProof, Proof } from "./types"
import { Groth16Proof } from "snarkjs"
import { PackedProof } from "./types"

/**
* Unpacks a proof into its original form.
* @param proof The proof compatible with Semaphore.
* @returns The proof compatible with SnarkJS.
*/
export default function unpackProof(proof: Proof): SnarkJSProof {
export default function unpackProof(proof: PackedProof): Groth16Proof {
return {
pi_a: [proof[0], proof[1]],
pi_b: [
Expand Down
4 changes: 2 additions & 2 deletions packages/proof/src/verifyProof.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { groth16 } from "snarkjs"
import hash from "./hash"
import { FullProof } from "./types"
import { SemaphoreProof } from "./types"
import unpackProof from "./unpackProof"
import verificationKeys from "./verificationKeys.json"

Expand All @@ -11,7 +11,7 @@ import verificationKeys from "./verificationKeys.json"
* @returns True if the proof is valid, false otherwise.
*/
export default function verifyProof(
{ merkleTreeRoot, nullifierHash, externalNullifier, signal, proof }: FullProof,
{ merkleTreeRoot, nullifierHash, externalNullifier, signal, proof }: SemaphoreProof,
treeDepth: number
): Promise<boolean> {
if (treeDepth < 16 || treeDepth > 32) {
Expand Down
Loading

0 comments on commit f212684

Please sign in to comment.