Implement minimal proof generator

pull/5057/head
ioedeveloper 4 months ago committed by Aniket
parent 862cf1645a
commit 41d4cbe714
  1. 205
      apps/circuit-compiler/src/app/actions/index.ts
  2. 6
      apps/circuit-compiler/src/app/app.tsx
  3. 2
      apps/circuit-compiler/src/app/components/generateProof.tsx
  4. 15
      apps/circuit-compiler/src/app/components/setupExports.tsx
  5. 16
      apps/circuit-compiler/src/app/reducers/state.ts
  6. 8
      apps/circuit-compiler/src/app/types/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))
}
}

@ -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])

@ -27,7 +27,7 @@ export function GenerateProof () {
disabled={(status === "compiling") || (status === "computing") || (status === "proving") || (status === "exporting")}
data-id="compute_witness_btn"
>
<RenderIf condition={status === 'computing'}>
<RenderIf condition={status === 'proving'}>
<i className="fas fa-sync fa-spin mr-2" aria-hidden="true"></i>
</RenderIf>
<FormattedMessage id="circuit.generateProof" />

@ -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 (
<div className="flex-column">
<div className="flex-column d-flex">
@ -115,7 +102,7 @@ export function SetupExports () {
<FormattedMessage id="circuit.exportVerificationKey" />
</label>
</div>
<SetupExportsBtn handleRunSetup={handleRunSetup} status={circuitApp.appState.status} />
<SetupExportsBtn handleRunSetup={() => runSetupAndExport(circuitApp.plugin, circuitApp.appState, circuitApp.dispatch)} status={circuitApp.appState.status} />
</div>
</div>
</div>

@ -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()
}

@ -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<string, any>,
SET_ZKEY: any
}
export interface Action<T extends keyof ActionPayloadTypes> {
type: T
@ -66,7 +68,9 @@ export interface AppState {
ptauList: Array<PtauFile>,
ptauValue: string,
exportVerificationContract: boolean,
exportVerificationKey: boolean
exportVerificationKey: boolean,
verificationKey: Record<string, any>,
zKey: Uint8Array
}
export type CompilationConfig = {

Loading…
Cancel
Save