Skip to content

Commit

Permalink
refactor(utils)!: make snark-artifacts utility functions dynamic (#275)
Browse files Browse the repository at this point in the history
* refactor(utils)!: make snark-artifacts utility functions dynamic

re #272

* refactor(utils): use promise.all to fetch artifacts concurrently

re #272

* refactor(utils): add more type checks to versions

re #272

* refactor(utils): add list of supported snark-artifacts projects

re #272

* refactor(utils): update project type

re #272
  • Loading branch information
cedoor authored May 14, 2024
1 parent b5407e2 commit 2505199
Show file tree
Hide file tree
Showing 14 changed files with 158 additions and 591 deletions.
2 changes: 1 addition & 1 deletion jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const projects: any = fs
rootDir: `packages/${name}`,
displayName: name,
moduleNameMapper: {
"@zk-kit/(.*)/(.*)": "<rootDir>/../$1/src/$2.ts",
"@zk-kit/(.*)/(.*)": ["<rootDir>/../$1/src/$2.ts", "<rootDir>/../$1/src/$2/$2.node.ts"],
"@zk-kit/(.*)": "<rootDir>/../$1/src/index.ts"
}
}))
Expand Down
6 changes: 4 additions & 2 deletions packages/eddsa-proof/src/generate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { deriveSecretScalar } from "@zk-kit/eddsa-poseidon"
import { SnarkArtifacts, maybeGetEdDSASnarkArtifacts, packGroth16Proof } from "@zk-kit/utils"
import type { SnarkArtifacts } from "@zk-kit/utils"
import { packGroth16Proof } from "@zk-kit/utils/proof-packing"
import maybeGetSnarkArtifacts, { Project } from "@zk-kit/utils/snark-artifacts"
import type { BigNumberish } from "ethers"
import { NumericString, groth16 } from "snarkjs"
import hash from "./hash"
Expand Down Expand Up @@ -30,7 +32,7 @@ export default async function generate(

// allow user to override our artifacts
// otherwise they'll be downloaded if not already in local tmp folder
snarkArtifacts ??= await maybeGetEdDSASnarkArtifacts()
snarkArtifacts ??= await maybeGetSnarkArtifacts(Project.EDDSA)
const { wasm, zkey } = snarkArtifacts
const secretScalar = deriveSecretScalar(privateKey)

Expand Down
2 changes: 1 addition & 1 deletion packages/eddsa-proof/src/verify.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { unpackGroth16Proof } from "@zk-kit/utils/proof-packing"
import { groth16 } from "snarkjs"
import { unpackGroth16Proof } from "@zk-kit/utils"
import hash from "./hash"
import { EddsaProof } from "./types"
import verificationKey from "./verification-key.json"
Expand Down
6 changes: 4 additions & 2 deletions packages/poseidon-proof/src/generate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { BigNumber } from "@ethersproject/bignumber"
import { SnarkArtifacts, maybeGetPoseidonSnarkArtifacts, packGroth16Proof } from "@zk-kit/utils"
import type { SnarkArtifacts } from "@zk-kit/utils"
import { packGroth16Proof } from "@zk-kit/utils/proof-packing"
import maybeGetSnarkArtifacts, { Project } from "@zk-kit/utils/snark-artifacts"
import { BigNumberish } from "ethers"
import { NumericString, groth16 } from "snarkjs"
import hash from "./hash"
Expand Down Expand Up @@ -30,7 +32,7 @@ export default async function generate(

// allow user to override our artifacts
// otherwise they'll be downloaded if not already in local tmp folder
snarkArtifacts ??= await maybeGetPoseidonSnarkArtifacts(preimages.length)
snarkArtifacts ??= await maybeGetSnarkArtifacts(Project.POSEIDON, { parameters: [preimages.length] })
const { wasm, zkey } = snarkArtifacts

const { proof, publicSignals } = await groth16.fullProve(
Expand Down
2 changes: 1 addition & 1 deletion packages/poseidon-proof/src/verify.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { groth16 } from "snarkjs"
import { unpackGroth16Proof } from "@zk-kit/utils"
import { unpackGroth16Proof } from "@zk-kit/utils/proof-packing"
import hash from "./hash"
import { PoseidonProof } from "./types"
import verificationKeys from "./verification-keys.json"
Expand Down
69 changes: 12 additions & 57 deletions packages/poseidon-proof/tests/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,85 +1,40 @@
import { buildBn128 } from "@zk-kit/groth16"
import { decodeBytes32String, toBeHex } from "ethers"
import {
poseidon1,
poseidon10,
poseidon11,
poseidon12,
poseidon13,
poseidon14,
poseidon15,
poseidon16,
poseidon2,
poseidon3,
poseidon4,
poseidon5,
poseidon6,
poseidon7,
poseidon8,
poseidon9
} from "poseidon-lite"
import { poseidon2 } from "poseidon-lite"
import generate from "../src/generate"
import hash from "../src/hash"
import { PoseidonProof } from "../src/types"
import verify from "../src/verify"

const poseidonFunctions = [
poseidon1,
poseidon2,
poseidon3,
poseidon4,
poseidon5,
poseidon6,
poseidon7,
poseidon8,
poseidon9,
poseidon10,
poseidon11,
poseidon12,
poseidon13,
poseidon14,
poseidon15,
poseidon16
]

const computePoseidon = (preimages: string[]) => poseidonFunctions[preimages.length - 1](preimages)

const preimages = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
const scope = "scope"
let curve: any
const proofs: Array<{ fullProof: PoseidonProof; digest: bigint }> = []
let digest: bigint
let fullProof: PoseidonProof

beforeAll(async () => {
curve = await buildBn128()
for (const preimage of preimages) {
const currentPreimages = preimages.slice(0, preimage)
const fullProof = await generate(currentPreimages, scope)
const digest = computePoseidon(currentPreimages.map((preimage) => hash(preimage)))
proofs.push({ fullProof, digest })
}
}, 180_000)

fullProof = await generate([1, 2], scope)

digest = poseidon2([hash(1), hash(2)])
}, 20_000)

afterAll(async () => {
await curve.terminate()
})

describe("PoseidonProof", () => {
it("should generate a Poseidon proof from 1 to 16 preimages", async () => {
for (const { fullProof, digest } of proofs) {
expect(fullProof.proof).toHaveLength(8)
expect(decodeBytes32String(toBeHex(fullProof.scope, 32))).toBe(scope.toString())
expect(fullProof.digest).toBe(digest.toString())
}
expect(fullProof.proof).toHaveLength(8)
expect(decodeBytes32String(toBeHex(fullProof.scope, 32))).toBe(scope.toString())
expect(fullProof.digest).toBe(digest.toString())
})

it("Should verify a Poseidon proof from 1 to 16 preimage(s)", async () => {
proofs.forEach(async ({ fullProof }) => {
await expect(verify(fullProof)).resolves.toBe(true)
})
await expect(verify(fullProof)).resolves.toBe(true)
})

it("Should verify an invalid Poseidon proof", async () => {
const { fullProof } = proofs[0]
fullProof.digest = "3"

const response = await verify(fullProof)
Expand Down
9 changes: 9 additions & 0 deletions packages/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@
"types": "./dist/types/f1-field.d.ts",
"require": "./dist/lib.commonjs/f1-field.cjs",
"default": "./dist/lib.esm/f1-field.js"
},
"./snark-artifacts": {
"types": "./dist/types/snark-artifacts/snark-artifacts.browser.d.ts",
"node": {
"require": "./dist/lib.commonjs/snark-artifacts/snark-artifacts.node.cjs",
"default": "./dist/lib.esm/snark-artifacts/snark-artifacts.node.js"
},
"browser": "./dist/lib.esm/snark-artifacts/snark-artifacts.browser.js",
"default": "./dist/lib.esm/snark-artifacts/snark-artifacts.browser.js"
}
},
"files": [
Expand Down
16 changes: 8 additions & 8 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import F1Field from "./f1-field"
import * as scalar from "./scalar"
import * as conversions from "./conversions"
import * as crypto from "./crypto/crypto.node"
import * as errorHandlers from "./error-handlers"
import F1Field from "./f1-field"
import * as packing from "./proof-packing"
import * as scalar from "./scalar"
import maybeGetSnarkArtifacts from "./snark-artifacts/snark-artifacts.node"
import * as typeChecks from "./type-checks"
import * as errorHandlers from "./error-handlers"
import * as crypto from "./crypto/crypto.node"

export { F1Field, scalar, conversions, packing, typeChecks, errorHandlers, crypto }
export * from "./types"
export * from "./conversions"
export * from "./error-handlers"
export * from "./proof-packing"
export * from "./type-checks"
export * from "./error-handlers"
export * from "./snark-artifacts/snark-artifacts.node"
export * from "./types"
export { F1Field, conversions, crypto, errorHandlers, maybeGetSnarkArtifacts, packing, scalar, typeChecks }
87 changes: 0 additions & 87 deletions packages/utils/src/snark-artifacts/config.ts

This file was deleted.

9 changes: 9 additions & 0 deletions packages/utils/src/snark-artifacts/projects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
enum Project {
EDDSA = "eddsa",
POSEIDON = "poseidon",
SEMAPHORE = "semaphore"
}

export const projects = Object.values(Project)

export default Project
45 changes: 21 additions & 24 deletions packages/utils/src/snark-artifacts/snark-artifacts.browser.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
import { GetSnarkArtifactUrls } from "./config"
import { Proof, SnarkArtifacts, Version } from "../types"
import { BigNumber, SnarkArtifacts, Version } from "../types"
import Project, { projects } from "./projects"

function MaybeGetSnarkArtifacts(proof: Proof.EDDSA, version?: Version): () => Promise<SnarkArtifacts>
function MaybeGetSnarkArtifacts(
proof: Proof.POSEIDON,
version?: Version
): (numberOfInputs: number) => Promise<SnarkArtifacts>
function MaybeGetSnarkArtifacts(
proof: Proof.SEMAPHORE,
version?: Version
): (treeDepth: number) => Promise<SnarkArtifacts>
function MaybeGetSnarkArtifacts(proof: Proof, version?: Version) {
switch (proof) {
case Proof.POSEIDON:
return async (numberOfInputs: number) => GetSnarkArtifactUrls({ proof, numberOfInputs, version })
export default async function maybeGetSnarkArtifacts(
project: Project,
options: {
parameters?: (BigNumber | number)[]
version?: Version
cdnUrl?: string
} = {}
): Promise<SnarkArtifacts> {
if (!projects.includes(project)) {
throw new Error(`Project '${project}' is not supported`)
}

case Proof.SEMAPHORE:
return async (treeDepth: number) => GetSnarkArtifactUrls({ proof, treeDepth, version })
options.version ??= "latest"
options.cdnUrl ??= "https://unpkg.com"

case Proof.EDDSA:
return async () => GetSnarkArtifactUrls({ proof, version })
const BASE_URL = `${options.cdnUrl}/@zk-kit/${project}-artifacts@${options.version}`
const parameters = options.parameters ? `-${options.parameters.join("-")}` : ""

default:
throw new Error("Unknown proof type")
return {
wasm: `${BASE_URL}/${project}${parameters}.wasm`,
zkey: `${BASE_URL}/${project}${parameters}.zkey`
}
}

export const maybeGetPoseidonSnarkArtifacts = MaybeGetSnarkArtifacts(Proof.POSEIDON)
export const maybeGetEdDSASnarkArtifacts = MaybeGetSnarkArtifacts(Proof.EDDSA)
export const maybeGetSemaphoreSnarkArtifacts = MaybeGetSnarkArtifacts(Proof.SEMAPHORE)
export { Project, projects }
Loading

0 comments on commit 2505199

Please sign in to comment.