From 41d4cbe714d794c211467633e3440cf361beebfc Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Wed, 7 Aug 2024 15:57:36 +0100 Subject: [PATCH] Implement minimal proof generator --- .../circuit-compiler/src/app/actions/index.ts | 205 ++++++++++-------- apps/circuit-compiler/src/app/app.tsx | 6 + .../src/app/components/generateProof.tsx | 2 +- .../src/app/components/setupExports.tsx | 17 +- .../src/app/reducers/state.ts | 16 +- apps/circuit-compiler/src/app/types/index.ts | 8 +- 6 files changed, 139 insertions(+), 115 deletions(-) diff --git a/apps/circuit-compiler/src/app/actions/index.ts b/apps/circuit-compiler/src/app/actions/index.ts index f3dbfdaafd..8b23b852b9 100644 --- a/apps/circuit-compiler/src/app/actions/index.ts +++ b/apps/circuit-compiler/src/app/actions/index.ts @@ -1,8 +1,9 @@ import * as snarkjs from 'snarkjs' import type { CircomPluginClient } from "../services/circomPluginClient" -import { AppState } from "../types" +import { AppState, ICircuitAppContext } from "../types" import { GROTH16_VERIFIER, PLONK_VERIFIER } from './constant' import { extractNameFromKey, extractParentFromKey } from '@remix-ui/helper' +import { ethers } from 'ethers' export const compileCircuit = async (plugin: CircomPluginClient, appState: AppState) => { try { @@ -34,109 +35,121 @@ export const computeWitness = async (plugin: CircomPluginClient, status: string, } } -export const runSetupAndExport = async (plugin: CircomPluginClient, appState: AppState) => { - const ptau_final = `https://ipfs-cluster.ethdevops.io/ipfs/${appState.ptauList.find(ptau => ptau.name === appState.ptauValue)?.ipfsHash}` - await plugin.generateR1cs(appState.filePath, { version: appState.version, prime: appState.primeValue }) - - const fileName = extractNameFromKey(appState.filePath) - const readPath = extractParentFromKey(appState.filePath) + `/.bin/${fileName.replace('.circom', '.r1cs')}` - // @ts-ignore - const r1csBuffer = await plugin.call('fileManager', 'readFile', readPath, { encoding: null }) - // @ts-ignore - const r1cs = new Uint8Array(r1csBuffer) - const zkey_final = { type: "mem" } - - if (appState.provingScheme === 'groth16') { - await snarkjs.zKey.newZKey(r1cs, ptau_final, zkey_final) - if (appState.exportVerificationKey) { +export const runSetupAndExport = async (plugin: CircomPluginClient, appState: AppState, dispatch: ICircuitAppContext['dispatch']) => { + try { + dispatch({ type: 'SET_COMPILER_STATUS', payload: 'exporting' }) + const ptau_final = `https://ipfs-cluster.ethdevops.io/ipfs/${appState.ptauList.find(ptau => ptau.name === appState.ptauValue)?.ipfsHash}` + await plugin.generateR1cs(appState.filePath, { version: appState.version, prime: appState.primeValue }) + + const fileName = extractNameFromKey(appState.filePath) + const readPath = extractParentFromKey(appState.filePath) + `/.bin/${fileName.replace('.circom', '.r1cs')}` + // @ts-ignore + const r1csBuffer = await plugin.call('fileManager', 'readFile', readPath, { encoding: null }) + // @ts-ignore + const r1cs = new Uint8Array(r1csBuffer) + const zkey_final = { type: "mem" } + + if (appState.provingScheme === 'groth16') { + await snarkjs.zKey.newZKey(r1cs, ptau_final, zkey_final) + await snarkjs.zKey.verifyFromR1cs(r1cs, ptau_final, zkey_final) const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) - await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/keys/verification_key.json`, JSON.stringify(vKey, null, 2)) - // @ts-ignore - await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/keys/zkey_final.txt`, (zkey_final as any).data, { encoding: null }) - } - if (appState.exportVerificationContract) { - const templates = { groth16: GROTH16_VERIFIER } - const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) - await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/build/zk_verifier.sol`, solidityContract) - } - } else if (appState.provingScheme === 'plonk') { - await snarkjs.plonk.setup(r1cs, ptau_final, zkey_final) - if (appState.exportVerificationKey) { + if (appState.exportVerificationKey) { + await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/keys/verification_key.json`, JSON.stringify(vKey, null, 2)) + } + if (appState.exportVerificationContract) { + const templates = { groth16: GROTH16_VERIFIER } + const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) + + await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/build/zk_verifier.sol`, solidityContract) + } + dispatch({ type: 'SET_ZKEY', payload: zkey_final }) + dispatch({ type: 'SET_VERIFICATION_KEY', payload: vKey }) + } else if (appState.provingScheme === 'plonk') { + await snarkjs.plonk.setup(r1cs, ptau_final, zkey_final) const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) - await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/keys/verification_key.json`, JSON.stringify(vKey, null, 2)) - // @ts-ignore - await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/keys/zkey_final.txt`, (zkey_final as any).data, { encoding: null }) - } - if (appState.exportVerificationContract) { - const templates = { plonk: PLONK_VERIFIER } - const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) - await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/build/zk_verifier.sol`, solidityContract) + if (appState.exportVerificationKey) { + await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/keys/verification_key.json`, JSON.stringify(vKey, null, 2)) + } + if (appState.exportVerificationContract) { + const templates = { plonk: PLONK_VERIFIER } + const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) + + await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/build/zk_verifier.sol`, solidityContract) + } + dispatch({ type: 'SET_ZKEY', payload: zkey_final }) + dispatch({ type: 'SET_VERIFICATION_KEY', payload: vKey }) } + dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }) + dispatch({ type: 'SET_SETUP_EXPORT_STATUS', payload: 'done' }) + } catch (e) { + dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' }) + dispatch({ type: 'SET_SETUP_EXPORT_FEEDBACK', payload: e.message }) + console.error(e) } } export const generateProof = async (plugin: CircomPluginClient, appState: AppState) => { -// try { -// // @ts-ignore -// const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/calculate_hash.r1cs', { encoding: null }); -// // @ts-ignore -// const r1cs = new Uint8Array(r1csBuffer); -// // @ts-ignore -// await remix.call('circuit-compiler', 'compile', 'circuits/calculate_hash.circom'); -// // @ts-ignore -// const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/calculate_hash.wasm', { encoding: null }); -// // @ts-ignore -// const wasm = new Uint8Array(wasmBuffer); - -// const zkey_final = { -// type: "mem", -// data: new Uint8Array(JSON.parse(await remix.call('fileManager', 'readFile', 'scripts/groth16/zk/keys/zkey_final.txt'))) -// } -// const wtns = { type: "mem" }; - -// const vKey = JSON.parse(await remix.call('fileManager', 'readFile', 'scripts/groth16/zk/keys/verification_key.json')) - -// const value1 = '1234' -// const value2 = '2' -// const value3 = '3' -// const value4 = '4' - -// const wrongValue = '5' // put this in the poseidon hash calculation to simulate a non matching hash. - -// const signals = { -// value1, -// value2, -// value3, -// value4, -// hash: poseidon([value1, value2, value3, value4]) -// } - -// console.log('calculate') -// await snarkjs.wtns.calculate(signals, wasm, wtns); - -// console.log('check') -// await snarkjs.wtns.check(r1cs, wtns, logger); - -// console.log('prove') -// const { proof, publicSignals } = await snarkjs.groth16.prove(zkey_final, wtns); - -// const verified = await snarkjs.groth16.verify(vKey, publicSignals, proof, logger); -// console.log('zk proof validity', verified); - -// const templates = { -// groth16: await remix.call('fileManager', 'readFile', 'templates/groth16_verifier.sol.ejs') -// } -// const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) + const fileName = extractNameFromKey(appState.filePath) + const r1csPath = extractParentFromKey(appState.filePath) + `/.bin/${fileName.replace('.circom', '.r1cs')}` + // @ts-ignore + const r1csBuffer = await plugin.call('fileManager', 'readFile', r1csPath, { encoding: null }) + // @ts-ignore + const r1cs = new Uint8Array(r1csBuffer) + const wtnsPath = r1csPath.replace('.r1cs', '.wtn') + // @ts-ignore + const wtnsBuffer = await plugin.call('fileManager', 'readFile', wtnsPath, { encoding: null }) + // @ts-ignore + const wtns = new Uint8Array(wtnsBuffer) + const zkey_final = appState.zKey + const vKey = appState.verificationKey -// await remix.call('fileManager', 'writeFile', 'scripts/groth16/zk/build/zk_verifier.sol', solidityContract) -// await remix.call('fileManager', 'writeFile', 'scripts/groth16/zk/build/input.json', JSON.stringify({ -// _pA: [proof.pi_a[0], proof.pi_a[1]], -// _pB: [[proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]]], -// _pC: [proof.pi_c[0], proof.pi_c[1]], -// _pubSignals: publicSignals, -// }, null, 2)) -// } catch (e) { -// } + await snarkjs.wtns.check(r1cs, wtns) + if (appState.provingScheme === 'groth16') { + const { proof, publicSignals } = await snarkjs.groth16.prove(zkey_final, wtns) + const verified = await snarkjs.groth16.verify(vKey, publicSignals, proof) + + console.log('zk proof validity', verified) + await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/build/input.json`, JSON.stringify({ + _pA: [proof.pi_a[0], proof.pi_a[1]], + _pB: [[proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]]], + _pC: [proof.pi_c[0], proof.pi_c[1]], + _pubSignals: publicSignals, + }, null, 2)) + } else if (appState.provingScheme === 'plonk') { + const { proof, publicSignals } = await snarkjs.plonk.prove(zkey_final, wtns) + const verified = await snarkjs.plonk.verify(vKey, publicSignals, proof) + + console.log('zk proof validity', verified) + await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/build/input.json`, JSON.stringify({ + _proof: [ + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.A[0]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.A[1]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.B[0]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.B[1]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.C[0]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.C[1]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Z[0]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Z[1]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T1[0]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T1[1]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T2[0]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T2[1]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T3[0]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T3[1]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxi[0]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxi[1]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxiw[0]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxiw[1]).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_a).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_b).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_c).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_s1).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_s2).toHexString(), 32), + ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_zw).toHexString(), 32), + ], + _pubSignals: publicSignals + }, null, 2)) + } } diff --git a/apps/circuit-compiler/src/app/app.tsx b/apps/circuit-compiler/src/app/app.tsx index 747d0b89d7..1e3e6a4a9c 100644 --- a/apps/circuit-compiler/src/app/app.tsx +++ b/apps/circuit-compiler/src/app/app.tsx @@ -99,6 +99,12 @@ function App() { dispatch({ type: 'SET_SIGNAL_INPUTS', payload: [] }) dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }) dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: null }) + dispatch({ type: 'SET_COMPUTE_FEEDBACK', payload: null }) + dispatch({ type: 'SET_SETUP_EXPORT_FEEDBACK', payload: null }) + dispatch({ type: 'SET_PROOF_FEEDBACK', payload: null }) + dispatch({ type: 'SET_SETUP_EXPORT_STATUS', payload: null }) + dispatch({ type: 'SET_VERIFICATION_KEY', payload: null }) + dispatch({ type: 'SET_ZKEY', payload: null }) } }, [appState.filePath]) diff --git a/apps/circuit-compiler/src/app/components/generateProof.tsx b/apps/circuit-compiler/src/app/components/generateProof.tsx index ef663b01db..99dabc9bb6 100644 --- a/apps/circuit-compiler/src/app/components/generateProof.tsx +++ b/apps/circuit-compiler/src/app/components/generateProof.tsx @@ -27,7 +27,7 @@ export function GenerateProof () { disabled={(status === "compiling") || (status === "computing") || (status === "proving") || (status === "exporting")} data-id="compute_witness_btn" > - + diff --git a/apps/circuit-compiler/src/app/components/setupExports.tsx b/apps/circuit-compiler/src/app/components/setupExports.tsx index 2116c6a8ec..2d42bb520f 100644 --- a/apps/circuit-compiler/src/app/components/setupExports.tsx +++ b/apps/circuit-compiler/src/app/components/setupExports.tsx @@ -8,19 +8,6 @@ import { runSetupAndExport } from "../actions" export function SetupExports () { const circuitApp = useContext(CircuitAppContext) - const handleRunSetup = async () => { - try { - circuitApp.dispatch({ type: 'SET_COMPILER_STATUS', payload: 'exporting' }) - await runSetupAndExport(circuitApp.plugin, circuitApp.appState) - circuitApp.dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }) - circuitApp.dispatch({ type: 'SET_SETUP_EXPORT_STATUS', payload: 'done' }) - } catch (e) { - circuitApp.dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' }) - circuitApp.dispatch({ type: 'SET_SETUP_EXPORT_FEEDBACK', payload: e.message }) - console.error(e) - } - } - return (
@@ -115,9 +102,9 @@ export function SetupExports () {
- + runSetupAndExport(circuitApp.plugin, circuitApp.appState, circuitApp.dispatch)} status={circuitApp.appState.status} />
) -} \ No newline at end of file +} diff --git a/apps/circuit-compiler/src/app/reducers/state.ts b/apps/circuit-compiler/src/app/reducers/state.ts index db967007ad..6163e5e4d7 100644 --- a/apps/circuit-compiler/src/app/reducers/state.ts +++ b/apps/circuit-compiler/src/app/reducers/state.ts @@ -21,7 +21,9 @@ export const appInitialState: AppState = { ptauList: PTAU_LIST, ptauValue: "final_14.ptau", exportVerificationContract: true, - exportVerificationKey: true + exportVerificationKey: true, + verificationKey: null, + zKey: null } export const appReducer = (state = appInitialState, action: Actions): AppState => { @@ -129,6 +131,18 @@ export const appReducer = (state = appInitialState, action: Actions): AppState = setupExportStatus: action.payload } + case 'SET_VERIFICATION_KEY': + return { + ...state, + verificationKey: action.payload + } + + case 'SET_ZKEY': + return { + ...state, + zKey: action.payload + } + default: throw new Error() } diff --git a/apps/circuit-compiler/src/app/types/index.ts b/apps/circuit-compiler/src/app/types/index.ts index 22e3160ad1..10d329a408 100644 --- a/apps/circuit-compiler/src/app/types/index.ts +++ b/apps/circuit-compiler/src/app/types/index.ts @@ -38,7 +38,9 @@ export interface ActionPayloadTypes { SET_PTAU_VALUE: string, SET_EXPORT_VERIFICATION_CONTRACT: boolean, SET_EXPORT_VERIFICATION_KEY: boolean, - SET_SETUP_EXPORT_STATUS: SetupExportStatus + SET_SETUP_EXPORT_STATUS: SetupExportStatus, + SET_VERIFICATION_KEY: Record, + SET_ZKEY: any } export interface Action { type: T @@ -66,7 +68,9 @@ export interface AppState { ptauList: Array, ptauValue: string, exportVerificationContract: boolean, - exportVerificationKey: boolean + exportVerificationKey: boolean, + verificationKey: Record, + zKey: Uint8Array } export type CompilationConfig = {