diff --git a/circuits/ts/index.ts b/circuits/ts/index.ts index 8b60c53583..640eba5978 100644 --- a/circuits/ts/index.ts +++ b/circuits/ts/index.ts @@ -2,14 +2,26 @@ import * as fs from 'fs' import * as path from 'path' import * as shelljs from 'shelljs' import * as tmp from 'tmp' +import { zKey, groth16 } from 'snarkjs' import { stringifyBigInts } from 'maci-crypto' -const snarkjsPath = path.join( - __dirname, - '..', - './node_modules/snarkjs/cli.js', -) +/* + * https://github.com/iden3/snarkjs/issues/152 + * Need to cleanup the threads to avoid stalling + */ +const cleanThreads = async () => { + if (!globalThis) { + return Promise.resolve(true) + } + + const curves = ['curve_bn128', 'curve_bls12381'] + const promises = Promise.all(curves.map(curve => { + return globalThis[curve]?.terminate? globalThis[curve]?.terminate() : null + }).filter(Boolean)) + + return promises +} const genProof = ( inputs: string[], @@ -71,69 +83,16 @@ const genProof = ( return { proof, publicInputs } } -const verifyProof = ( - publicInputs: any, - proof: any, - vk: any, -) => { - // Create tmp directory - const tmpObj = tmp.dirSync() - const tmpDirPath = tmpObj.name - - const publicJsonPath = path.join(tmpDirPath, 'public.json') - const proofJsonPath = path.join(tmpDirPath, 'proof.json') - const vkJsonPath = path.join(tmpDirPath, 'vk.json') - - fs.writeFileSync( - publicJsonPath, - JSON.stringify(stringifyBigInts(publicInputs)), - ) - - fs.writeFileSync( - proofJsonPath, - JSON.stringify(stringifyBigInts(proof)), - ) - - fs.writeFileSync( - vkJsonPath, - JSON.stringify(stringifyBigInts(vk)), - ) - - const verifyCmd = `node ${snarkjsPath} g16v ${vkJsonPath} ${publicJsonPath} ${proofJsonPath}` - const output = shelljs.exec(verifyCmd, { silent: true }) - const isValid = output.stdout && output.stdout.indexOf('OK!') > -1 - - //// Generate calldata - //const calldataCmd = `node ${snarkjsPath} zkesc ${publicJsonPath} ${proofJsonPath}` - //console.log(shelljs.exec(calldataCmd).stdout) - - fs.unlinkSync(proofJsonPath) - fs.unlinkSync(publicJsonPath) - fs.unlinkSync(vkJsonPath) - tmpObj.removeCallback() - +const verifyProof = async (publicInputs: any, proof: any, vk: any) => { + const isValid = await groth16.verify(vk, publicInputs, proof) + await cleanThreads() return isValid } -const extractVk = (zkeyPath: string) => { - // Create tmp directory - const tmpObj = tmp.dirSync() - const tmpDirPath = tmpObj.name - const vkJsonPath = path.join(tmpDirPath, 'vk.json') - - const exportCmd = `node ${snarkjsPath} zkev ${zkeyPath} ${vkJsonPath}` - shelljs.exec(exportCmd) - - const vk = JSON.parse(fs.readFileSync(vkJsonPath).toString()) - - fs.unlinkSync(vkJsonPath) - tmpObj.removeCallback() - +const extractVk = async (zkeyPath: string) => { + const vk = await zKey.exportVerificationKey(zkeyPath) + await cleanThreads() return vk } -export { - genProof, - verifyProof, - extractVk, -} +export { genProof, verifyProof, extractVk } diff --git a/circuits/ts/snarkjs.d.ts b/circuits/ts/snarkjs.d.ts new file mode 100644 index 0000000000..58717255ca --- /dev/null +++ b/circuits/ts/snarkjs.d.ts @@ -0,0 +1,28 @@ +declare module 'snarkjs' { + export type NumericString = `${number}` | string + export type PublicSignals = NumericString[] + + export interface Groth16Proof { + pi_a: NumericString[]; + pi_b: NumericString[][]; + pi_c: NumericString[]; + protocol: string; + curve: string; + } + + export namespace zKey { + function exportVerificationKey( + zkeyName: any, + logger?: any, + ): Promise + } + + export namespace groth16 { + function verify( + _vk_verifier: any, + _publicSignals: PublicSignals, + _proof: Groth16Proof, + logger?: any, + ): Promise + } +} diff --git a/cli/ts/checkVerifyingKey.ts b/cli/ts/checkVerifyingKey.ts index 858f70b68e..4fc522c3c6 100644 --- a/cli/ts/checkVerifyingKey.ts +++ b/cli/ts/checkVerifyingKey.ts @@ -126,8 +126,8 @@ const checkVerifyingKey = async (args: any) => { const pmZkeyFile = path.resolve(args.process_messages_zkey) const tvZkeyFile = path.resolve(args.tally_votes_zkey) - const processVk: VerifyingKey = VerifyingKey.fromObj(extractVk(pmZkeyFile)) - const tallyVk: VerifyingKey = VerifyingKey.fromObj(extractVk(tvZkeyFile)) + const processVk: VerifyingKey = VerifyingKey.fromObj(await extractVk(pmZkeyFile)) + const tallyVk: VerifyingKey = VerifyingKey.fromObj(await extractVk(tvZkeyFile)) const signer = await getDefaultSigner() if (!await contractExists(signer.provider, maciAddress)) { diff --git a/cli/ts/genProofs.ts b/cli/ts/genProofs.ts index bcb52580e6..09108ef278 100644 --- a/cli/ts/genProofs.ts +++ b/cli/ts/genProofs.ts @@ -232,12 +232,12 @@ const genProofs = async (args: any) => { return } - subsidyVk = extractVk(args.subsidy_zkey) + subsidyVk = await extractVk(args.subsidy_zkey) } // Extract the verifying keys - const processVk = extractVk(args.process_zkey) - const tallyVk = extractVk(args.tally_zkey) + const processVk = await extractVk(args.process_zkey) + const tallyVk = await extractVk(args.tally_zkey) // The coordinator's MACI private key let serializedPrivkey @@ -394,7 +394,7 @@ const genProofs = async (args: any) => { } // Verify the proof - const isValid = verifyProof( + const isValid = await verifyProof( r.publicInputs, r.proof, processVk, @@ -440,7 +440,7 @@ const genProofs = async (args: any) => { subsidyCircuitInputs = poll.subsidyPerBatch() const r = genProof(subsidyCircuitInputs, rapidsnarkExe, args.subsidy_witnessgen, args.subsidy_zkey) - const isValid = verifyProof(r.publicInputs, r.proof, subsidyVk) + const isValid = await verifyProof(r.publicInputs, r.proof, subsidyVk) if (!isValid) { console.error('Error: generated an invalid subsidy calc proof') return @@ -501,7 +501,7 @@ const genProofs = async (args: any) => { ) // Verify the proof - const isValid = verifyProof( + const isValid = await verifyProof( r.publicInputs, r.proof, tallyVk, diff --git a/cli/ts/setVerifyingKeys.ts b/cli/ts/setVerifyingKeys.ts index 2cfb5d5771..8edbf1e35f 100644 --- a/cli/ts/setVerifyingKeys.ts +++ b/cli/ts/setVerifyingKeys.ts @@ -131,8 +131,8 @@ const setVerifyingKeys = async (args: any) => { return } - const processVk: VerifyingKey = VerifyingKey.fromObj(extractVk(pmZkeyFile)) - const tallyVk: VerifyingKey = VerifyingKey.fromObj(extractVk(tvZkeyFile)) + const processVk: VerifyingKey = VerifyingKey.fromObj(await extractVk(pmZkeyFile)) + const tallyVk: VerifyingKey = VerifyingKey.fromObj(await extractVk(tvZkeyFile)) let ssZkeyFile: string @@ -144,7 +144,7 @@ const setVerifyingKeys = async (args: any) => { return } - subsidyVk = VerifyingKey.fromObj(extractVk(ssZkeyFile)) + subsidyVk = VerifyingKey.fromObj(await extractVk(ssZkeyFile)) } // Simple validation diff --git a/integrationTests/integrations.yml b/integrationTests/integrations.yml index a30cbd29e2..89de60ff41 100644 --- a/integrationTests/integrations.yml +++ b/integrationTests/integrations.yml @@ -17,7 +17,7 @@ invalidVote: voteCreditBalance: 1 constants: poll: - duration: 250 + duration: 300 intStateTreeDepth: 1 messageTreeDepth: 2 messageBatchDepth: 1