From 1aed70e9bc41512f6838b00c576399042c3160e9 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Tue, 28 Feb 2023 16:37:21 +0000 Subject: [PATCH] feat(verification): added actions helper to extract artifacts from a zKey Added helper functions to extract the solidity verifier and the verification key from a zkey --- packages/actions/src/helpers/verification.ts | 67 +++++++++++++++++- packages/actions/src/index.ts | 8 ++- .../test/data/circuit-small_00001.zkey | Bin 0 -> 3730 bytes .../actions/test/unit/verification.test.ts | 65 +++++++++++++++++ 4 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 packages/actions/test/data/circuit-small_00001.zkey create mode 100644 packages/actions/test/unit/verification.test.ts diff --git a/packages/actions/src/helpers/verification.ts b/packages/actions/src/helpers/verification.ts index 625f2201..4cd199f4 100644 --- a/packages/actions/src/helpers/verification.ts +++ b/packages/actions/src/helpers/verification.ts @@ -1,6 +1,10 @@ import { DocumentData, Firestore } from "firebase/firestore" -import { FirebaseDocumentInfo } from "../types" +import { zKey } from "snarkjs" +import fs from "fs" +import path from "path" +import { cwd } from "process" import { getCurrentContributorContribution } from "./database" +import { FirebaseDocumentInfo } from "../types" /** * Return an array of true of false based on contribution verification result per each circuit. @@ -121,3 +125,64 @@ export const getValidContributionAttestation = async ( return attestation } + +/** + * Helper method to extract the Solidity verifier + * from a final zKey file and save it to a local file. + * @param solidityVersion <string> The solidity version to include in the verifier pragma definition. + * @param finalZkeyPath <string> The path to the zKey file. + * @param verifierLocalPath <string> The path to the local file where the verifier will be saved. + */ +export const exportVerifierContract = async ( + solidityVersion: string, + finalZkeyPath: string, + verifierLocalPath: string +) => { + // Extract verifier. + + let verifierCode = await zKey.exportSolidityVerifier( + finalZkeyPath, + { + groth16: fs + .readFileSync(path.join(cwd(), "node_modules/snarkjs/templates/verifier_groth16.sol.ejs")) + .toString() + }, + console + ) + + // Update solidity version. + verifierCode = verifierCode.replace( + /pragma solidity \^\d+\.\d+\.\d+/, + `pragma solidity ^${solidityVersion || "0.8.0"}` + ) + + fs.writeFileSync(verifierLocalPath, verifierCode) +} + +/** + * Helpers method to extract the vKey from a final zKey file + * @param finalZkeyPath <string> The path to the zKey file. + * @param vKeyLocalPath <string> The path to the local file where the vKey will be saved. + */ +export const exportVkey = async (finalZkeyPath: string, vKeyLocalPath: string) => { + const verificationKeyJSONData = await zKey.exportVerificationKey(finalZkeyPath) + fs.writeFileSync(vKeyLocalPath, JSON.stringify(verificationKeyJSONData)) +} + +/** + * Helper method to extract the Solidity verifier and the Verification key + * from a final zKey file and save them to local files. + * @param solidityVersion <string> The solidity version to include in the verifier pragma definition. + * @param finalZkeyPath <string> The path to the zKey file. + * @param verifierLocalPath <string> The path to the local file where the verifier will be saved. + * @param vKeyLocalPath <string> The path to the local file where the vKey will be saved. + */ +export const exportVerifierAndVKey = async ( + solidityVersion: string, + finalZkeyPath: string, + verifierLocalPath: string, + vKeyLocalPath: string +) => { + await exportVerifierContract(solidityVersion, finalZkeyPath, verifierLocalPath) + await exportVkey(finalZkeyPath, vKeyLocalPath) +} diff --git a/packages/actions/src/index.ts b/packages/actions/src/index.ts index c3e2c9ef..59f9ffa1 100644 --- a/packages/actions/src/index.ts +++ b/packages/actions/src/index.ts @@ -43,7 +43,13 @@ export { getContributionsCollectionPath, getTimeoutsCollectionPath } from "./helpers/database" -export { getContributorContributionsVerificationResults, getValidContributionAttestation } from "./helpers/verification" +export { + getContributorContributionsVerificationResults, + getValidContributionAttestation, + exportVerifierAndVKey, + exportVerifierContract, + exportVkey +} from "./helpers/verification" export { initializeFirebaseCoreServices } from "./helpers/services" export { signInToFirebaseWithCredentials, getCurrentFirebaseAuthUser, isCoordinator } from "./helpers/authentication" export { diff --git a/packages/actions/test/data/circuit-small_00001.zkey b/packages/actions/test/data/circuit-small_00001.zkey new file mode 100644 index 0000000000000000000000000000000000000000..7e2afc6cd05630b2eb425df63b47100b866dccde GIT binary patch literal 3730 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 R?n`tG_j5XobFkWn`2$Yrc;Wy6 literal 0 HcmV?d00001 diff --git a/packages/actions/test/unit/verification.test.ts b/packages/actions/test/unit/verification.test.ts new file mode 100644 index 00000000..1d1b4285 --- /dev/null +++ b/packages/actions/test/unit/verification.test.ts @@ -0,0 +1,65 @@ +import chai, { expect } from "chai" +import chaiAsPromised from "chai-as-promised" +import dotenv from "dotenv" +import { cwd } from "process" +import fs from "fs" +import { exportVerifierAndVKey, exportVerifierContract, exportVkey } from "../../src" +import { envType } from "../utils" +import { TestingEnvironment } from "../../src/types/enums" + +chai.use(chaiAsPromised) +dotenv.config() + +/** + * Unit test for Verification utilities. + */ + +describe("Verification utilities", () => { + const finalZkeyPath = `${cwd()}/packages/actions/test/data/circuit-small_00001.zkey` + const verifierExportPath = `${cwd()}/packages/actions/test/data/verifier.sol` + const vKeyExportPath = `${cwd()}/packages/actions/test/data/vkey.json` + const solidityVersion = "0.8.10" + describe("exportVerifierContract", () => { + if (envType === TestingEnvironment.PRODUCTION) { + it("should export the verifier contract", async () => { + await exportVerifierContract(solidityVersion, finalZkeyPath, verifierExportPath) + expect(fs.existsSync(verifierExportPath)).to.be.true + }) + } + it("should fail when the zkey is not found", async () => { + await expect(exportVerifierContract("0.8.0", "invalid-path", verifierExportPath)).to.be.rejected + }) + }) + describe("exportVkey", () => { + if (envType === TestingEnvironment.PRODUCTION) { + it("should export the vkey", async () => { + await exportVkey(finalZkeyPath, vKeyExportPath) + expect(fs.existsSync(vKeyExportPath)).to.be.true + }) + } + it("should fail when the zkey is not found", async () => { + await expect(exportVkey("invalid-path", vKeyExportPath)).to.be.rejected + }) + }) + describe("exportVerifierAndVKey", () => { + if (envType === TestingEnvironment.PRODUCTION) { + it("should export the verifier contract and the vkey", async () => { + await exportVerifierAndVKey("0.8.0", finalZkeyPath, verifierExportPath, vKeyExportPath) + expect(fs.existsSync(verifierExportPath)).to.be.true + expect(fs.existsSync(vKeyExportPath)).to.be.true + }) + } + it("should fail when the zkey is not found", async () => { + await expect(exportVerifierAndVKey("0.8.0", "invalid-path", verifierExportPath, vKeyExportPath)).to.be + .rejected + }) + }) + afterAll(() => { + if (fs.existsSync(verifierExportPath)) { + fs.unlinkSync(verifierExportPath) + } + if (fs.existsSync(vKeyExportPath)) { + fs.unlinkSync(vKeyExportPath) + } + }) +})