From a0fe67398e1b72fd2875d51bb4dcd85963636681 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Thu, 4 Apr 2024 13:48:38 +0100 Subject: [PATCH 01/12] Update hash checker template with plonk scripts --- .../src/templates/hashchecker/README.md | 10 +- .../src/templates/hashchecker/index.ts | 10 +- .../groth16_trusted_setup.ts} | 13 +- .../groth16_zkproof.ts} | 11 +- .../scripts/plonk/plonk_trusted_setup.ts | 32 + .../scripts/plonk/plonk_zkproof.ts | 93 +++ .../templates/plonk_verifier.sol.ejs | 710 ++++++++++++++++++ 7 files changed, 861 insertions(+), 18 deletions(-) rename libs/remix-ws-templates/src/templates/hashchecker/scripts/{run_setup.ts => groth16/groth16_trusted_setup.ts} (72%) rename libs/remix-ws-templates/src/templates/hashchecker/scripts/{run_verification.ts => groth16/groth16_zkproof.ts} (77%) create mode 100644 libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_trusted_setup.ts create mode 100644 libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_zkproof.ts create mode 100644 libs/remix-ws-templates/src/templates/hashchecker/templates/plonk_verifier.sol.ejs diff --git a/libs/remix-ws-templates/src/templates/hashchecker/README.md b/libs/remix-ws-templates/src/templates/hashchecker/README.md index 77ebb3641d..5262f35d80 100644 --- a/libs/remix-ws-templates/src/templates/hashchecker/README.md +++ b/libs/remix-ws-templates/src/templates/hashchecker/README.md @@ -7,21 +7,21 @@ The workspace comprises two main directories: ### circuits: Contains sample Hash Checker contracts. These can be compiled to generate a witness using 'Circom ZKP Compiler' plugin. -### scripts: Provides a sample script designed for a trusted setup using snarkjs. This script also aids in generating Solidity code, which is essential for on-chain deployment. +### scripts: Provides a sample script designed for a trusted setup using snarkjs. This script also aids in generating Solidity code, which is essential for on-chain deployment. There have 2 scripts options to choose from, Groth16 and Plonk. ### first steps: #### 1) compile the hash checker circuit using the remix circom compiler. This will generate artifacts. -#### 2) execute the file `run_setup.ts`: +#### 2) execute the file `groth16_trusted_setup.ts` found in `scripts/groth16` directory: This step generate a verification key that can be used for generating proof, it will also generate a Solidity contract for on-chain verification. Note that this section should only be used for development purposes as this way of running the setup is heavily centralized (although some pieces of this script can be used to achieve that). -This generates a verification key (`./zk/build/verification_key.json`) and artifacts from the setup (`./zk/build/zk_setup.txt`). +This generates a verification key (`./zk/build/groth16/verification_key.json`) and a key for proof generation (`./zk/build/groth16/zkey_final.txt`). -#### 3) execute the file `run_verification.ts`: +#### 3) execute the file `groth16_zkproof.ts` found in `scripts/groth16`: This script: @@ -33,3 +33,5 @@ This script: The witness will be generated only if the provided hash is the poseidon hash of these 4 values. - verify that the proof is valid `(snarkjs.groth16.verify)` + +#### The steps above for groth16 scripts apply also to plonk scripts. diff --git a/libs/remix-ws-templates/src/templates/hashchecker/index.ts b/libs/remix-ws-templates/src/templates/hashchecker/index.ts index b119c6d251..c298a5f0fe 100644 --- a/libs/remix-ws-templates/src/templates/hashchecker/index.ts +++ b/libs/remix-ws-templates/src/templates/hashchecker/index.ts @@ -3,12 +3,18 @@ export default async () => { // @ts-ignore 'circuits/calculate_hash.circom': (await import('raw-loader!./circuits/calculate_hash.circom')).default, // @ts-ignore - 'scripts/run_setup.ts': (await import('!!raw-loader!./scripts/run_setup.ts')).default, + 'scripts/groth16/groth16_trusted_setup.ts': (await import('!!raw-loader!./scripts/groth16/groth16_trusted_setup.ts')).default, // @ts-ignore - 'scripts/run_verification.ts': (await import('!!raw-loader!./scripts/run_verification.ts')).default, + 'scripts/groth16/groth16_zkproof.ts': (await import('!!raw-loader!./scripts/groth16/groth16_zkproof.ts')).default, + // @ts-ignore + 'scripts/plonk/plonk_trusted_setup.ts': (await import('!!raw-loader!./scripts/plonk/plonk_trusted_setup.ts')).default, + // @ts-ignore + 'scripts/plonk/plonk_zkproof.ts': (await import('!!raw-loader!./scripts/plonk/plonk_zkproof.ts')).default, // @ts-ignore 'templates/groth16_verifier.sol.ejs': (await import('!!raw-loader!./templates/groth16_verifier.sol.ejs')).default, // @ts-ignore + 'templates/plonk_verifier.sol.ejs': (await import('!!raw-loader!./templates/plonk_verifier.sol.ejs')).default, + // @ts-ignore 'README.md': (await import('raw-loader!./README.md')).default } } \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/hashchecker/scripts/run_setup.ts b/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_trusted_setup.ts similarity index 72% rename from libs/remix-ws-templates/src/templates/hashchecker/scripts/run_setup.ts rename to libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_trusted_setup.ts index 1fc7312546..f8e91b12af 100644 --- a/libs/remix-ws-templates/src/templates/hashchecker/scripts/run_setup.ts +++ b/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_trusted_setup.ts @@ -39,17 +39,10 @@ const logger = { console.log('exportVerificationKey') const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) - await remix.call('fileManager', 'writeFile', './zk/build/verification_key.json', JSON.stringify(vKey)) + await remix.call('fileManager', 'writeFile', './zk/keys/groth16/verification_key.json', JSON.stringify(vKey, null, 2)) - const templates = { - groth16: await remix.call('fileManager', 'readFile', 'templates/groth16_verifier.sol.ejs') - } - const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) - - await remix.call('fileManager', 'writeFile', './zk/build/zk_verifier.sol', solidityContract) - - console.log('buffer', (zkey_final as any).data.length) - await remix.call('fileManager', 'writeFile', './zk/build/zk_setup.txt', JSON.stringify(Array.from(((zkey_final as any).data)))) + console.log('save zkey_final') + await remix.call('fileManager', 'writeFile', './zk/keys/groth16/zkey_final.txt', JSON.stringify(Array.from(((zkey_final as any).data)))) console.log('setup done.') diff --git a/libs/remix-ws-templates/src/templates/hashchecker/scripts/run_verification.ts b/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_zkproof.ts similarity index 77% rename from libs/remix-ws-templates/src/templates/hashchecker/scripts/run_verification.ts rename to libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_zkproof.ts index 7f595f9fbc..cefde1f38e 100644 --- a/libs/remix-ws-templates/src/templates/hashchecker/scripts/run_verification.ts +++ b/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_zkproof.ts @@ -16,17 +16,19 @@ const logger = { // @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', true); // @ts-ignore const wasm = new Uint8Array(wasmBuffer); const zkey_final = { type: "mem", - data: new Uint8Array(JSON.parse(await remix.call('fileManager', 'readFile', './zk/build/zk_setup.txt'))) + data: new Uint8Array(JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/zkey_final.txt'))) } const wtns = { type: "mem" }; - const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/build/verification_key.json')) + const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/verification_key.json')) const value1 = '1234' const value2 = '2' @@ -56,7 +58,12 @@ const logger = { 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) + await remix.call('fileManager', 'writeFile', './zk/build/groth16/zk_verifier.sol', solidityContract) } catch (e) { console.error(e.message) } diff --git a/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_trusted_setup.ts b/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_trusted_setup.ts new file mode 100644 index 0000000000..0ae1ae0e49 --- /dev/null +++ b/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_trusted_setup.ts @@ -0,0 +1,32 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const snarkjs = require('snarkjs'); + +(async () => { + try { + // @ts-ignore + await remix.call('circuit-compiler', 'generateR1cs', 'circuits/calculate_hash.circom'); + + const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW"; + // @ts-ignore + const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/calculate_hash.r1cs', true); + // @ts-ignore + const r1cs = new Uint8Array(r1csBuffer); + const zkey_final = { type: "mem" }; + + console.log('plonk setup') + await snarkjs.plonk.setup(r1cs, ptau_final, zkey_final) + + console.log('exportVerificationKey') + const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) + + console.log('save zkey_final') + await remix.call('fileManager', 'writeFile', './zk/keys/plonk/zkey_final.txt', JSON.stringify(Array.from(((zkey_final as any).data)))) + + console.log('save verification key') + await remix.call('fileManager', 'writeFile', './zk/keys/plonk/verification_key.json', JSON.stringify(vKey, null, 2)) + + console.log('setup done') + } catch (e) { + console.error(e.message) + } +})() \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_zkproof.ts b/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_zkproof.ts new file mode 100644 index 0000000000..730c98135a --- /dev/null +++ b/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_zkproof.ts @@ -0,0 +1,93 @@ +import { ethers, BigNumber } from 'ethers' +import { poseidon } from "circomlibjs" // v0.0.8 + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const snarkjs = require('snarkjs'); + +const logger = { + info: (...args) => console.log(...args), + debug: (...args) => console.log(...args), + error: (...args) => console.error(...args), +}; + +(async () => { + try { + // @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', './zk/keys/plonk/zkey_final.txt'))) + } + + const wtns = { type: "mem" }; + 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, logger); + + const { proof, publicSignals } = await snarkjs.plonk.prove(zkey_final, wtns); + + const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/plonk/verification_key.json')) + + const verified = await snarkjs.plonk.verify(vKey, publicSignals, proof); + + console.log('zk proof validity', verified); + const templates = { + plonk: await remix.call('fileManager', 'readFile', 'templates/plonk_verifier.sol.ejs') + } + const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) + + await remix.call('fileManager', 'writeFile', 'zk/build/plonk/zk_verifier.sol', solidityContract) + await remix.call('fileManager', 'writeFile', 'zk/build/plonk/input.json', JSON.stringify({ + _pubSignals: publicSignals, + _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), + ] + }, null, 2)) + + console.log('proof done.') + + } catch (e) { + console.error(e.message) + } +})() diff --git a/libs/remix-ws-templates/src/templates/hashchecker/templates/plonk_verifier.sol.ejs b/libs/remix-ws-templates/src/templates/hashchecker/templates/plonk_verifier.sol.ejs new file mode 100644 index 0000000000..da3f145b53 --- /dev/null +++ b/libs/remix-ws-templates/src/templates/hashchecker/templates/plonk_verifier.sol.ejs @@ -0,0 +1,710 @@ +// SPDX-License-Identifier: GPL-3.0 +/* + Copyright 2021 0KIMS association. + + This file is generated with [snarkJS](https://github.com/iden3/snarkjs). + + snarkJS is a free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + snarkJS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with snarkJS. If not, see . +*/ + + +pragma solidity >=0.7.0 <0.9.0; + +import "hardhat/console.sol"; + +contract PlonkVerifier { + // Omega + uint256 constant w1 = <%=w%>; + // Scalar field size + uint256 constant q = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + // Base field size + uint256 constant qf = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + // [1]_1 + uint256 constant G1x = 1; + uint256 constant G1y = 2; + // [1]_2 + uint256 constant G2x1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 constant G2x2 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 constant G2y1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; + uint256 constant G2y2 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; + + // Verification Key data + uint32 constant n = <%=2**power%>; + uint16 constant nPublic = <%=nPublic%>; + uint16 constant nLagrange = <%=Math.max(nPublic, 1)%>; + + uint256 constant Qmx = <%=Qm[0]%>; + uint256 constant Qmy = <%=Qm[0] == "0" ? "0" : Qm[1]%>; + uint256 constant Qlx = <%=Ql[0]%>; + uint256 constant Qly = <%=Ql[0] == "0" ? "0" : Ql[1]%>; + uint256 constant Qrx = <%=Qr[0]%>; + uint256 constant Qry = <%=Qr[0] == "0" ? "0" : Qr[1]%>; + uint256 constant Qox = <%=Qo[0]%>; + uint256 constant Qoy = <%=Qo[0] == "0" ? "0" : Qo[1]%>; + uint256 constant Qcx = <%=Qc[0]%>; + uint256 constant Qcy = <%=Qc[0] == "0" ? "0" : Qc[1]%>; + uint256 constant S1x = <%=S1[0]%>; + uint256 constant S1y = <%=S1[0] == "0" ? "0" : S1[1]%>; + uint256 constant S2x = <%=S2[0]%>; + uint256 constant S2y = <%=S2[0] == "0" ? "0" : S2[1]%>; + uint256 constant S3x = <%=S3[0]%>; + uint256 constant S3y = <%=S3[0] == "0" ? "0" : S3[1]%>; + uint256 constant k1 = <%=k1%>; + uint256 constant k2 = <%=k2%>; + uint256 constant X2x1 = <%=X_2[0][0]%>; + uint256 constant X2x2 = <%=X_2[0][1]%>; + uint256 constant X2y1 = <%=X_2[1][0]%>; + uint256 constant X2y2 = <%=X_2[1][1]%>; + + // Proof calldata + // Byte offset of every parameter of the calldata + // Polynomial commitments + uint16 constant pA = 4 + 0; + uint16 constant pB = 4 + 64; + uint16 constant pC = 4 + 128; + uint16 constant pZ = 4 + 192; + uint16 constant pT1 = 4 + 256; + uint16 constant pT2 = 4 + 320; + uint16 constant pT3 = 4 + 384; + uint16 constant pWxi = 4 + 448; + uint16 constant pWxiw = 4 + 512; + // Opening evaluations + uint16 constant pEval_a = 4 + 576; + uint16 constant pEval_b = 4 + 608; + uint16 constant pEval_c = 4 + 640; + uint16 constant pEval_s1 = 4 + 672; + uint16 constant pEval_s2 = 4 + 704; + uint16 constant pEval_zw = 4 + 736; + + // Memory data + // Challenges + uint16 constant pAlpha = 0; + uint16 constant pBeta = 32; + uint16 constant pGamma = 64; + uint16 constant pXi = 96; + uint16 constant pXin = 128; + uint16 constant pBetaXi = 160; + uint16 constant pV1 = 192; + uint16 constant pV2 = 224; + uint16 constant pV3 = 256; + uint16 constant pV4 = 288; + uint16 constant pV5 = 320; + uint16 constant pU = 352; + + uint16 constant pPI = 384; + uint16 constant pEval_r0 = 416; + uint16 constant pD = 448; + uint16 constant pF = 512; + uint16 constant pE = 576; + uint16 constant pTmp = 640; + uint16 constant pAlpha2 = 704; + uint16 constant pZh = 736; + uint16 constant pZhInv = 768; + + <% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> + uint16 constant pEval_l<%=i%> = <%=768+i*32%>; + <% } %> + <% let pLastMem = 800+32*Math.max(nPublic,1) %> + + uint16 constant lastMem = <%=pLastMem%>; + + function verifyProof(uint256[24] calldata _proof, uint256[<%=nPublic%>] calldata _pubSignals) public view returns (bool) { + assembly { + ///////// + // Computes the inverse using the extended euclidean algorithm + ///////// + function inverse(a, q) -> inv { + let t := 0 + let newt := 1 + let r := q + let newr := a + let quotient + let aux + + for { } newr { } { + quotient := sdiv(r, newr) + aux := sub(t, mul(quotient, newt)) + t:= newt + newt:= aux + + aux := sub(r,mul(quotient, newr)) + r := newr + newr := aux + } + + if gt(r, 1) { revert(0,0) } + if slt(t, 0) { t:= add(t, q) } + + inv := t + } + + /////// + // Computes the inverse of an array of values + // See https://vitalik.ca/general/2018/07/21/starks_part_3.html in section where explain fields operations + ////// + function inverseArray(pVals, n) { + + let pAux := mload(0x40) // Point to the next free position + let pIn := pVals + let lastPIn := add(pVals, mul(n, 32)) // Read n elemnts + let acc := mload(pIn) // Read the first element + pIn := add(pIn, 32) // Point to the second element + let inv + + + for { } lt(pIn, lastPIn) { + pAux := add(pAux, 32) + pIn := add(pIn, 32) + } + { + mstore(pAux, acc) + acc := mulmod(acc, mload(pIn), q) + } + acc := inverse(acc, q) + + // At this point pAux pint to the next free position we substract 1 to point to the last used + pAux := sub(pAux, 32) + // pIn points to the n+1 element, we substract to point to n + pIn := sub(pIn, 32) + lastPIn := pVals // We don't process the first element + for { } gt(pIn, lastPIn) { + pAux := sub(pAux, 32) + pIn := sub(pIn, 32) + } + { + inv := mulmod(acc, mload(pAux), q) + acc := mulmod(acc, mload(pIn), q) + mstore(pIn, inv) + } + // pIn points to first element, we just set it. + mstore(pIn, acc) + } + + function checkField(v) { + if iszero(lt(v, q)) { + mstore(0, 0) + return(0,0x20) + } + } + + function checkInput() { + checkField(calldataload(pEval_a)) + checkField(calldataload(pEval_b)) + checkField(calldataload(pEval_c)) + checkField(calldataload(pEval_s1)) + checkField(calldataload(pEval_s2)) + checkField(calldataload(pEval_zw)) + } + + function calculateChallenges(pMem, pPublic) { + let beta + let aux + + let mIn := mload(0x40) // Pointer to the next free memory position + + // Compute challenge.beta & challenge.gamma + mstore(mIn, Qmx) + mstore(add(mIn, 32), Qmy) + mstore(add(mIn, 64), Qlx) + mstore(add(mIn, 96), Qly) + mstore(add(mIn, 128), Qrx) + mstore(add(mIn, 160), Qry) + mstore(add(mIn, 192), Qox) + mstore(add(mIn, 224), Qoy) + mstore(add(mIn, 256), Qcx) + mstore(add(mIn, 288), Qcy) + mstore(add(mIn, 320), S1x) + mstore(add(mIn, 352), S1y) + mstore(add(mIn, 384), S2x) + mstore(add(mIn, 416), S2y) + mstore(add(mIn, 448), S3x) + mstore(add(mIn, 480), S3y) + + <%for (let i=0; i + mstore(add(mIn, <%= 512 + i*32 %>), calldataload(add(pPublic, <%=i*32%>))) + <%}%> + mstore(add(mIn, <%= 512 + nPublic*32 + 0 %> ), calldataload(pA)) + mstore(add(mIn, <%= 512 + nPublic*32 + 32 %> ), calldataload(add(pA, 32))) + mstore(add(mIn, <%= 512 + nPublic*32 + 64 %> ), calldataload(pB)) + mstore(add(mIn, <%= 512 + nPublic*32 + 96 %> ), calldataload(add(pB, 32))) + mstore(add(mIn, <%= 512 + nPublic*32 + 128 %> ), calldataload(pC)) + mstore(add(mIn, <%= 512 + nPublic*32 + 160 %> ), calldataload(add(pC, 32))) + + beta := mod(keccak256(mIn, <%= 704 + 32 * nPublic %>), q) + mstore(add(pMem, pBeta), beta) + + // challenges.gamma + mstore(add(pMem, pGamma), mod(keccak256(add(pMem, pBeta), 32), q)) + + // challenges.alpha + mstore(mIn, mload(add(pMem, pBeta))) + mstore(add(mIn, 32), mload(add(pMem, pGamma))) + mstore(add(mIn, 64), calldataload(pZ)) + mstore(add(mIn, 96), calldataload(add(pZ, 32))) + + aux := mod(keccak256(mIn, 128), q) + mstore(add(pMem, pAlpha), aux) + mstore(add(pMem, pAlpha2), mulmod(aux, aux, q)) + + // challenges.xi + mstore(mIn, aux) + mstore(add(mIn, 32), calldataload(pT1)) + mstore(add(mIn, 64), calldataload(add(pT1, 32))) + mstore(add(mIn, 96), calldataload(pT2)) + mstore(add(mIn, 128), calldataload(add(pT2, 32))) + mstore(add(mIn, 160), calldataload(pT3)) + mstore(add(mIn, 192), calldataload(add(pT3, 32))) + + aux := mod(keccak256(mIn, 224), q) + mstore( add(pMem, pXi), aux) + + // challenges.v + mstore(mIn, aux) + mstore(add(mIn, 32), calldataload(pEval_a)) + mstore(add(mIn, 64), calldataload(pEval_b)) + mstore(add(mIn, 96), calldataload(pEval_c)) + mstore(add(mIn, 128), calldataload(pEval_s1)) + mstore(add(mIn, 160), calldataload(pEval_s2)) + mstore(add(mIn, 192), calldataload(pEval_zw)) + + let v1 := mod(keccak256(mIn, 224), q) + mstore(add(pMem, pV1), v1) + + // challenges.beta * challenges.xi + mstore(add(pMem, pBetaXi), mulmod(beta, aux, q)) + + // challenges.xi^n + <%for (let i=0; i + aux:= mulmod(aux, aux, q) + <%}%> + mstore(add(pMem, pXin), aux) + + // Zh + aux:= mod(add(sub(aux, 1), q), q) + mstore(add(pMem, pZh), aux) + mstore(add(pMem, pZhInv), aux) // We will invert later together with lagrange pols + + // challenges.v^2, challenges.v^3, challenges.v^4, challenges.v^5 + aux := mulmod(v1, v1, q) + mstore(add(pMem, pV2), aux) + aux := mulmod(aux, v1, q) + mstore(add(pMem, pV3), aux) + aux := mulmod(aux, v1, q) + mstore(add(pMem, pV4), aux) + aux := mulmod(aux, v1, q) + mstore(add(pMem, pV5), aux) + + // challenges.u + mstore(mIn, calldataload(pWxi)) + mstore(add(mIn, 32), calldataload(add(pWxi, 32))) + mstore(add(mIn, 64), calldataload(pWxiw)) + mstore(add(mIn, 96), calldataload(add(pWxiw, 32))) + + mstore(add(pMem, pU), mod(keccak256(mIn, 128), q)) + } + + function calculateLagrange(pMem) { + let w := 1 + <% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> + mstore( + add(pMem, pEval_l<%=i%>), + mulmod( + n, + mod( + add( + sub( + mload(add(pMem, pXi)), + w + ), + q + ), + q + ), + q + ) + ) + <% if (i + w := mulmod(w, w1, q) + <% } %> + <% } %> + + inverseArray(add(pMem, pZhInv), <%=Math.max(nPublic, 1)+1%> ) + + let zh := mload(add(pMem, pZh)) + w := 1 + <% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> + <% if (i==1) { %> + mstore( + add(pMem, pEval_l1 ), + mulmod( + mload(add(pMem, pEval_l1 )), + zh, + q + ) + ) + <% } else { %> + mstore( + add(pMem, pEval_l<%=i%>), + mulmod( + w, + mulmod( + mload(add(pMem, pEval_l<%=i%>)), + zh, + q + ), + q + ) + ) + <% } %> + <% if (i + w := mulmod(w, w1, q) + <% } %> + <% } %> + + + } + + function calculatePI(pMem, pPub) { + let pl := 0 + + <% for (let i=0; i + pl := mod( + add( + sub( + pl, + mulmod( + mload(add(pMem, pEval_l<%=i+1%>)), + calldataload(add(pPub, <%=i*32%>)), + q + ) + ), + q + ), + q + ) + <% } %> + + mstore(add(pMem, pPI), pl) + } + + function calculateR0(pMem) { + let e1 := mload(add(pMem, pPI)) + + let e2 := mulmod(mload(add(pMem, pEval_l1)), mload(add(pMem, pAlpha2)), q) + + let e3a := addmod( + calldataload(pEval_a), + mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), + q) + e3a := addmod(e3a, mload(add(pMem, pGamma)), q) + + let e3b := addmod( + calldataload(pEval_b), + mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), + q) + e3b := addmod(e3b, mload(add(pMem, pGamma)), q) + + let e3c := addmod( + calldataload(pEval_c), + mload(add(pMem, pGamma)), + q) + + let e3 := mulmod(mulmod(e3a, e3b, q), e3c, q) + e3 := mulmod(e3, calldataload(pEval_zw), q) + e3 := mulmod(e3, mload(add(pMem, pAlpha)), q) + + let r0 := addmod(e1, mod(sub(q, e2), q), q) + r0 := addmod(r0, mod(sub(q, e3), q), q) + + mstore(add(pMem, pEval_r0) , r0) + } + + function g1_set(pR, pP) { + mstore(pR, mload(pP)) + mstore(add(pR, 32), mload(add(pP,32))) + } + + function g1_setC(pR, x, y) { + mstore(pR, x) + mstore(add(pR, 32), y) + } + + function g1_calldataSet(pR, pP) { + mstore(pR, calldataload(pP)) + mstore(add(pR, 32), calldataload(add(pP, 32))) + } + + function g1_acc(pR, pP) { + let mIn := mload(0x40) + mstore(mIn, mload(pR)) + mstore(add(mIn,32), mload(add(pR, 32))) + mstore(add(mIn,64), mload(pP)) + mstore(add(mIn,96), mload(add(pP, 32))) + + let success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + } + + function g1_mulAcc(pR, pP, s) { + let success + let mIn := mload(0x40) + mstore(mIn, mload(pP)) + mstore(add(mIn,32), mload(add(pP, 32))) + mstore(add(mIn,64), s) + + success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + + mstore(add(mIn,64), mload(pR)) + mstore(add(mIn,96), mload(add(pR, 32))) + + success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + + } + + function g1_mulAccC(pR, x, y, s) { + let success + let mIn := mload(0x40) + mstore(mIn, x) + mstore(add(mIn,32), y) + mstore(add(mIn,64), s) + + success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + + mstore(add(mIn,64), mload(pR)) + mstore(add(mIn,96), mload(add(pR, 32))) + + success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + } + + function g1_mulSetC(pR, x, y, s) { + let success + let mIn := mload(0x40) + mstore(mIn, x) + mstore(add(mIn,32), y) + mstore(add(mIn,64), s) + + success := staticcall(sub(gas(), 2000), 7, mIn, 96, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + } + + function g1_mulSet(pR, pP, s) { + g1_mulSetC(pR, mload(pP), mload(add(pP, 32)), s) + } + + function calculateD(pMem) { + let _pD:= add(pMem, pD) + let gamma := mload(add(pMem, pGamma)) + let mIn := mload(0x40) + mstore(0x40, add(mIn, 256)) // d1, d2, d3 & d4 (4*64 bytes) + + g1_setC(_pD, Qcx, Qcy) + g1_mulAccC(_pD, Qmx, Qmy, mulmod(calldataload(pEval_a), calldataload(pEval_b), q)) + g1_mulAccC(_pD, Qlx, Qly, calldataload(pEval_a)) + g1_mulAccC(_pD, Qrx, Qry, calldataload(pEval_b)) + g1_mulAccC(_pD, Qox, Qoy, calldataload(pEval_c)) + + let betaxi := mload(add(pMem, pBetaXi)) + let val1 := addmod( + addmod(calldataload(pEval_a), betaxi, q), + gamma, q) + + let val2 := addmod( + addmod( + calldataload(pEval_b), + mulmod(betaxi, k1, q), + q), gamma, q) + + let val3 := addmod( + addmod( + calldataload(pEval_c), + mulmod(betaxi, k2, q), + q), gamma, q) + + let d2a := mulmod( + mulmod(mulmod(val1, val2, q), val3, q), + mload(add(pMem, pAlpha)), + q + ) + + let d2b := mulmod( + mload(add(pMem, pEval_l1)), + mload(add(pMem, pAlpha2)), + q + ) + + // We'll use mIn to save d2 + g1_calldataSet(add(mIn, 192), pZ) + g1_mulSet( + mIn, + add(mIn, 192), + addmod(addmod(d2a, d2b, q), mload(add(pMem, pU)), q)) + + + val1 := addmod( + addmod( + calldataload(pEval_a), + mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), + q), gamma, q) + + val2 := addmod( + addmod( + calldataload(pEval_b), + mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), + q), gamma, q) + + val3 := mulmod( + mulmod(mload(add(pMem, pAlpha)), mload(add(pMem, pBeta)), q), + calldataload(pEval_zw), q) + + + // We'll use mIn + 64 to save d3 + g1_mulSetC( + add(mIn, 64), + S3x, + S3y, + mulmod(mulmod(val1, val2, q), val3, q)) + + // We'll use mIn + 128 to save d4 + g1_calldataSet(add(mIn, 128), pT1) + + g1_mulAccC(add(mIn, 128), calldataload(pT2), calldataload(add(pT2, 32)), mload(add(pMem, pXin))) + let xin2 := mulmod(mload(add(pMem, pXin)), mload(add(pMem, pXin)), q) + g1_mulAccC(add(mIn, 128), calldataload(pT3), calldataload(add(pT3, 32)) , xin2) + + g1_mulSetC(add(mIn, 128), mload(add(mIn, 128)), mload(add(mIn, 160)), mload(add(pMem, pZh))) + + mstore(add(add(mIn, 64), 32), mod(sub(qf, mload(add(add(mIn, 64), 32))), qf)) + mstore(add(mIn, 160), mod(sub(qf, mload(add(mIn, 160))), qf)) + g1_acc(_pD, mIn) + g1_acc(_pD, add(mIn, 64)) + g1_acc(_pD, add(mIn, 128)) + } + + function calculateF(pMem) { + let p := add(pMem, pF) + + g1_set(p, add(pMem, pD)) + g1_mulAccC(p, calldataload(pA), calldataload(add(pA, 32)), mload(add(pMem, pV1))) + g1_mulAccC(p, calldataload(pB), calldataload(add(pB, 32)), mload(add(pMem, pV2))) + g1_mulAccC(p, calldataload(pC), calldataload(add(pC, 32)), mload(add(pMem, pV3))) + g1_mulAccC(p, S1x, S1y, mload(add(pMem, pV4))) + g1_mulAccC(p, S2x, S2y, mload(add(pMem, pV5))) + } + + function calculateE(pMem) { + let s := mod(sub(q, mload(add(pMem, pEval_r0))), q) + + s := addmod(s, mulmod(calldataload(pEval_a), mload(add(pMem, pV1)), q), q) + s := addmod(s, mulmod(calldataload(pEval_b), mload(add(pMem, pV2)), q), q) + s := addmod(s, mulmod(calldataload(pEval_c), mload(add(pMem, pV3)), q), q) + s := addmod(s, mulmod(calldataload(pEval_s1), mload(add(pMem, pV4)), q), q) + s := addmod(s, mulmod(calldataload(pEval_s2), mload(add(pMem, pV5)), q), q) + s := addmod(s, mulmod(calldataload(pEval_zw), mload(add(pMem, pU)), q), q) + + g1_mulSetC(add(pMem, pE), G1x, G1y, s) + } + + function checkPairing(pMem) -> isOk { + let mIn := mload(0x40) + mstore(0x40, add(mIn, 576)) // [0..383] = pairing data, [384..447] = pWxi, [448..512] = pWxiw + + let _pWxi := add(mIn, 384) + let _pWxiw := add(mIn, 448) + let _aux := add(mIn, 512) + + g1_calldataSet(_pWxi, pWxi) + g1_calldataSet(_pWxiw, pWxiw) + + // A1 + g1_mulSet(mIn, _pWxiw, mload(add(pMem, pU))) + g1_acc(mIn, _pWxi) + mstore(add(mIn, 32), mod(sub(qf, mload(add(mIn, 32))), qf)) + + // [X]_2 + mstore(add(mIn,64), X2x2) + mstore(add(mIn,96), X2x1) + mstore(add(mIn,128), X2y2) + mstore(add(mIn,160), X2y1) + + // B1 + g1_mulSet(add(mIn, 192), _pWxi, mload(add(pMem, pXi))) + + let s := mulmod(mload(add(pMem, pU)), mload(add(pMem, pXi)), q) + s := mulmod(s, w1, q) + g1_mulSet(_aux, _pWxiw, s) + g1_acc(add(mIn, 192), _aux) + g1_acc(add(mIn, 192), add(pMem, pF)) + mstore(add(pMem, add(pE, 32)), mod(sub(qf, mload(add(pMem, add(pE, 32)))), qf)) + g1_acc(add(mIn, 192), add(pMem, pE)) + + // [1]_2 + mstore(add(mIn,256), G2x2) + mstore(add(mIn,288), G2x1) + mstore(add(mIn,320), G2y2) + mstore(add(mIn,352), G2y1) + + let success := staticcall(sub(gas(), 2000), 8, mIn, 384, mIn, 0x20) + + isOk := and(success, mload(mIn)) + } + + let pMem := mload(0x40) + mstore(0x40, add(pMem, lastMem)) + + checkInput() + calculateChallenges(pMem, _pubSignals) + calculateLagrange(pMem) + calculatePI(pMem, _pubSignals) + calculateR0(pMem) + calculateD(pMem) + calculateF(pMem) + calculateE(pMem) + let isValid := checkPairing(pMem) + + mstore(0x40, sub(pMem, lastMem)) + mstore(0, isValid) + return(0,0x20) + } + + } +} \ No newline at end of file From 6bcb2f951cb8eaee6f83c96c07b2f9cae6f0edc6 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Tue, 9 Apr 2024 14:19:30 +0100 Subject: [PATCH 02/12] Update rln scripts --- .../scripts/groth16/groth16_trusted_setup.ts | 2 +- .../scripts/groth16/groth16_zkproof.ts | 8 +- .../scripts/plonk/plonk_trusted_setup.ts | 2 +- .../src/templates/rln/index.ts | 8 +- .../groth16_trusted_setup.ts} | 15 +- .../groth16_zkproof.ts} | 21 +- .../rln/scripts/plonk/plonk_trusted_setup.ts | 32 + .../rln/scripts/plonk/plonk_zkproof.ts | 93 +++ .../rln/templates/plonk_verifier.sol.ejs | 710 ++++++++++++++++++ 9 files changed, 866 insertions(+), 25 deletions(-) rename libs/remix-ws-templates/src/templates/rln/scripts/{run_setup.ts => groth16/groth16_trusted_setup.ts} (70%) rename libs/remix-ws-templates/src/templates/rln/scripts/{run_verification.ts => groth16/groth16_zkproof.ts} (86%) create mode 100644 libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_trusted_setup.ts create mode 100644 libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_zkproof.ts create mode 100644 libs/remix-ws-templates/src/templates/rln/templates/plonk_verifier.sol.ejs diff --git a/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_trusted_setup.ts b/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_trusted_setup.ts index f8e91b12af..278146b08a 100644 --- a/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_trusted_setup.ts +++ b/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_trusted_setup.ts @@ -13,7 +13,7 @@ const logger = { const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW"; // @ts-ignore - const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/calculate_hash.r1cs', true); + const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/calculate_hash.r1cs', { encoding: null }); // @ts-ignore const r1cs = new Uint8Array(r1csBuffer); const zkey_0 = { type: "mem" }; diff --git a/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_zkproof.ts b/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_zkproof.ts index cefde1f38e..e4be9baa1c 100644 --- a/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_zkproof.ts +++ b/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_zkproof.ts @@ -12,23 +12,23 @@ const logger = { (async () => { try { // @ts-ignore - const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/calculate_hash.r1cs', true); + 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', true); + 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', './zk/keys/zkey_final.txt'))) + data: new Uint8Array(JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/groth16/zkey_final.txt'))) } const wtns = { type: "mem" }; - const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/verification_key.json')) + const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/groth16/verification_key.json')) const value1 = '1234' const value2 = '2' diff --git a/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_trusted_setup.ts b/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_trusted_setup.ts index 0ae1ae0e49..192cd2d12d 100644 --- a/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_trusted_setup.ts +++ b/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_trusted_setup.ts @@ -8,7 +8,7 @@ const snarkjs = require('snarkjs'); const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW"; // @ts-ignore - const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/calculate_hash.r1cs', true); + const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/calculate_hash.r1cs', { encoding: null }); // @ts-ignore const r1cs = new Uint8Array(r1csBuffer); const zkey_final = { type: "mem" }; diff --git a/libs/remix-ws-templates/src/templates/rln/index.ts b/libs/remix-ws-templates/src/templates/rln/index.ts index 92e290ccc3..f3fc2a2eca 100644 --- a/libs/remix-ws-templates/src/templates/rln/index.ts +++ b/libs/remix-ws-templates/src/templates/rln/index.ts @@ -7,9 +7,13 @@ export default async () => { // @ts-ignore 'circuits/withdraw.circom': (await import('!!raw-loader!./circuits/withdraw.circom')).default, // @ts-ignore - 'scripts/run_setup.ts': (await import('!!raw-loader!./scripts/run_setup.ts')).default, + 'scripts/groth16/groth16_trusted_setup.ts': (await import('!!raw-loader!./scripts/groth16/groth16_trusted_setup.ts')).default, // @ts-ignore - 'scripts/run_verification.ts': (await import('!!raw-loader!./scripts/run_verification.ts')).default, + 'scripts/groth16/groth16_zkproof.ts': (await import('!!raw-loader!./scripts/groth16/groth16_zkproof.ts')).default, + // @ts-ignore + 'scripts/plonk/plonk_trusted_setup.ts': (await import('!!raw-loader!./scripts/plonk/plonk_trusted_setup.ts')).default, + // @ts-ignore + 'scripts/plonk/plonk_zkproof.ts': (await import('!!raw-loader!./scripts/plonk/plonk_zkproof.ts')).default, // @ts-ignore 'templates/groth16_verifier.sol.ejs': (await import('!!raw-loader!./templates/groth16_verifier.sol.ejs')).default, // @ts-ignore diff --git a/libs/remix-ws-templates/src/templates/rln/scripts/run_setup.ts b/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_trusted_setup.ts similarity index 70% rename from libs/remix-ws-templates/src/templates/rln/scripts/run_setup.ts rename to libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_trusted_setup.ts index 2f9e9684cf..0452df371f 100644 --- a/libs/remix-ws-templates/src/templates/rln/scripts/run_setup.ts +++ b/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_trusted_setup.ts @@ -13,7 +13,7 @@ const logger = { const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW"; // @ts-ignore - const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.r1cs', true); + const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.r1cs', { encoding: null }); // @ts-ignore const r1cs = new Uint8Array(r1csBuffer); const zkey_0 = { type: "mem" }; @@ -39,17 +39,10 @@ const logger = { console.log('exportVerificationKey') const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) - await remix.call('fileManager', 'writeFile', './zk/build/verification_key.json', JSON.stringify(vKey)) + await remix.call('fileManager', 'writeFile', './zk/keys/groth16/verification_key.json', JSON.stringify(vKey, null, 2)) - const templates = { - groth16: await remix.call('fileManager', 'readFile', 'templates/groth16_verifier.sol.ejs') - } - const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) - - await remix.call('fileManager', 'writeFile', './zk/build/zk_verifier.sol', solidityContract) - - console.log('buffer', (zkey_final as any).data.length) - await remix.call('fileManager', 'writeFile', './zk/build/zk_setup.txt', JSON.stringify(Array.from(((zkey_final as any).data)))) + console.log('save zkey_final') + await remix.call('fileManager', 'writeFile', './zk/keys/groth16/zkey_final.txt', JSON.stringify(Array.from(((zkey_final as any).data)))) console.log('setup done.') diff --git a/libs/remix-ws-templates/src/templates/rln/scripts/run_verification.ts b/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_zkproof.ts similarity index 86% rename from libs/remix-ws-templates/src/templates/rln/scripts/run_verification.ts rename to libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_zkproof.ts index 61771faad8..109718273b 100644 --- a/libs/remix-ws-templates/src/templates/rln/scripts/run_verification.ts +++ b/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_zkproof.ts @@ -64,21 +64,23 @@ async function prove (signals, wasm, wtns, r1cs, zkey_final, vKey) { (async () => { try { // @ts-ignore - const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.r1cs', true); + const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.r1cs', { encoding: null }); // @ts-ignore const r1cs = new Uint8Array(r1csBuffer); // @ts-ignore - const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.wasm', true); + await remix.call('circuit-compiler', 'compile', 'circuits/rln.circom'); + // @ts-ignore + const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.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', './zk/build/zk_setup.txt'))) + data: new Uint8Array(JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/groth16/zkey_final.txt'))) } const wtns = { type: "mem" }; - const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/build/verification_key.json')) + const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/groth16/verification_key.json')) // build list of identity commitments const secrets = [] @@ -119,7 +121,7 @@ async function prove (signals, wasm, wtns, r1cs, zkey_final, vKey) { externalNullifier: 0xa // hash(epoch, appId) } const proof1 = await prove(signals1, wasm, wtns, r1cs, zkey_final, vKey) - + const signals2 = { identitySecret: secrets[0], userMessageLimit, @@ -135,7 +137,14 @@ async function prove (signals, wasm, wtns, r1cs, zkey_final, vKey) { console.log(secret.toString(10)) console.log(Fq.normalize(secrets[0])) + + const templates = { + groth16: await remix.call('fileManager', 'readFile', 'templates/groth16_verifier.sol.ejs') + } + const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) + + await remix.call('fileManager', 'writeFile', './zk/build/groth16/zk_verifier.sol', solidityContract) } catch (e) { console.error(e.message) } -})() \ No newline at end of file +})() diff --git a/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_trusted_setup.ts b/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_trusted_setup.ts new file mode 100644 index 0000000000..866cb1dac6 --- /dev/null +++ b/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_trusted_setup.ts @@ -0,0 +1,32 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const snarkjs = require('snarkjs'); + +(async () => { + try { + // @ts-ignore + await remix.call('circuit-compiler', 'generateR1cs', 'circuits/rln.circom'); + + const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW"; + // @ts-ignore + const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.r1cs', { encoding: null }); + // @ts-ignore + const r1cs = new Uint8Array(r1csBuffer); + const zkey_final = { type: "mem" }; + + console.log('plonk setup') + await snarkjs.plonk.setup(r1cs, ptau_final, zkey_final) + + console.log('exportVerificationKey') + const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) + + console.log('save zkey_final') + await remix.call('fileManager', 'writeFile', './zk/keys/plonk/zkey_final.txt', JSON.stringify(Array.from(((zkey_final as any).data)))) + + console.log('save verification key') + await remix.call('fileManager', 'writeFile', './zk/keys/plonk/verification_key.json', JSON.stringify(vKey, null, 2)) + + console.log('setup done') + } catch (e) { + console.error(e.message) + } +})() \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_zkproof.ts b/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_zkproof.ts new file mode 100644 index 0000000000..730c98135a --- /dev/null +++ b/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_zkproof.ts @@ -0,0 +1,93 @@ +import { ethers, BigNumber } from 'ethers' +import { poseidon } from "circomlibjs" // v0.0.8 + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const snarkjs = require('snarkjs'); + +const logger = { + info: (...args) => console.log(...args), + debug: (...args) => console.log(...args), + error: (...args) => console.error(...args), +}; + +(async () => { + try { + // @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', './zk/keys/plonk/zkey_final.txt'))) + } + + const wtns = { type: "mem" }; + 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, logger); + + const { proof, publicSignals } = await snarkjs.plonk.prove(zkey_final, wtns); + + const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/plonk/verification_key.json')) + + const verified = await snarkjs.plonk.verify(vKey, publicSignals, proof); + + console.log('zk proof validity', verified); + const templates = { + plonk: await remix.call('fileManager', 'readFile', 'templates/plonk_verifier.sol.ejs') + } + const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) + + await remix.call('fileManager', 'writeFile', 'zk/build/plonk/zk_verifier.sol', solidityContract) + await remix.call('fileManager', 'writeFile', 'zk/build/plonk/input.json', JSON.stringify({ + _pubSignals: publicSignals, + _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), + ] + }, null, 2)) + + console.log('proof done.') + + } catch (e) { + console.error(e.message) + } +})() diff --git a/libs/remix-ws-templates/src/templates/rln/templates/plonk_verifier.sol.ejs b/libs/remix-ws-templates/src/templates/rln/templates/plonk_verifier.sol.ejs new file mode 100644 index 0000000000..da3f145b53 --- /dev/null +++ b/libs/remix-ws-templates/src/templates/rln/templates/plonk_verifier.sol.ejs @@ -0,0 +1,710 @@ +// SPDX-License-Identifier: GPL-3.0 +/* + Copyright 2021 0KIMS association. + + This file is generated with [snarkJS](https://github.com/iden3/snarkjs). + + snarkJS is a free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + snarkJS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with snarkJS. If not, see . +*/ + + +pragma solidity >=0.7.0 <0.9.0; + +import "hardhat/console.sol"; + +contract PlonkVerifier { + // Omega + uint256 constant w1 = <%=w%>; + // Scalar field size + uint256 constant q = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + // Base field size + uint256 constant qf = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + // [1]_1 + uint256 constant G1x = 1; + uint256 constant G1y = 2; + // [1]_2 + uint256 constant G2x1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 constant G2x2 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 constant G2y1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; + uint256 constant G2y2 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; + + // Verification Key data + uint32 constant n = <%=2**power%>; + uint16 constant nPublic = <%=nPublic%>; + uint16 constant nLagrange = <%=Math.max(nPublic, 1)%>; + + uint256 constant Qmx = <%=Qm[0]%>; + uint256 constant Qmy = <%=Qm[0] == "0" ? "0" : Qm[1]%>; + uint256 constant Qlx = <%=Ql[0]%>; + uint256 constant Qly = <%=Ql[0] == "0" ? "0" : Ql[1]%>; + uint256 constant Qrx = <%=Qr[0]%>; + uint256 constant Qry = <%=Qr[0] == "0" ? "0" : Qr[1]%>; + uint256 constant Qox = <%=Qo[0]%>; + uint256 constant Qoy = <%=Qo[0] == "0" ? "0" : Qo[1]%>; + uint256 constant Qcx = <%=Qc[0]%>; + uint256 constant Qcy = <%=Qc[0] == "0" ? "0" : Qc[1]%>; + uint256 constant S1x = <%=S1[0]%>; + uint256 constant S1y = <%=S1[0] == "0" ? "0" : S1[1]%>; + uint256 constant S2x = <%=S2[0]%>; + uint256 constant S2y = <%=S2[0] == "0" ? "0" : S2[1]%>; + uint256 constant S3x = <%=S3[0]%>; + uint256 constant S3y = <%=S3[0] == "0" ? "0" : S3[1]%>; + uint256 constant k1 = <%=k1%>; + uint256 constant k2 = <%=k2%>; + uint256 constant X2x1 = <%=X_2[0][0]%>; + uint256 constant X2x2 = <%=X_2[0][1]%>; + uint256 constant X2y1 = <%=X_2[1][0]%>; + uint256 constant X2y2 = <%=X_2[1][1]%>; + + // Proof calldata + // Byte offset of every parameter of the calldata + // Polynomial commitments + uint16 constant pA = 4 + 0; + uint16 constant pB = 4 + 64; + uint16 constant pC = 4 + 128; + uint16 constant pZ = 4 + 192; + uint16 constant pT1 = 4 + 256; + uint16 constant pT2 = 4 + 320; + uint16 constant pT3 = 4 + 384; + uint16 constant pWxi = 4 + 448; + uint16 constant pWxiw = 4 + 512; + // Opening evaluations + uint16 constant pEval_a = 4 + 576; + uint16 constant pEval_b = 4 + 608; + uint16 constant pEval_c = 4 + 640; + uint16 constant pEval_s1 = 4 + 672; + uint16 constant pEval_s2 = 4 + 704; + uint16 constant pEval_zw = 4 + 736; + + // Memory data + // Challenges + uint16 constant pAlpha = 0; + uint16 constant pBeta = 32; + uint16 constant pGamma = 64; + uint16 constant pXi = 96; + uint16 constant pXin = 128; + uint16 constant pBetaXi = 160; + uint16 constant pV1 = 192; + uint16 constant pV2 = 224; + uint16 constant pV3 = 256; + uint16 constant pV4 = 288; + uint16 constant pV5 = 320; + uint16 constant pU = 352; + + uint16 constant pPI = 384; + uint16 constant pEval_r0 = 416; + uint16 constant pD = 448; + uint16 constant pF = 512; + uint16 constant pE = 576; + uint16 constant pTmp = 640; + uint16 constant pAlpha2 = 704; + uint16 constant pZh = 736; + uint16 constant pZhInv = 768; + + <% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> + uint16 constant pEval_l<%=i%> = <%=768+i*32%>; + <% } %> + <% let pLastMem = 800+32*Math.max(nPublic,1) %> + + uint16 constant lastMem = <%=pLastMem%>; + + function verifyProof(uint256[24] calldata _proof, uint256[<%=nPublic%>] calldata _pubSignals) public view returns (bool) { + assembly { + ///////// + // Computes the inverse using the extended euclidean algorithm + ///////// + function inverse(a, q) -> inv { + let t := 0 + let newt := 1 + let r := q + let newr := a + let quotient + let aux + + for { } newr { } { + quotient := sdiv(r, newr) + aux := sub(t, mul(quotient, newt)) + t:= newt + newt:= aux + + aux := sub(r,mul(quotient, newr)) + r := newr + newr := aux + } + + if gt(r, 1) { revert(0,0) } + if slt(t, 0) { t:= add(t, q) } + + inv := t + } + + /////// + // Computes the inverse of an array of values + // See https://vitalik.ca/general/2018/07/21/starks_part_3.html in section where explain fields operations + ////// + function inverseArray(pVals, n) { + + let pAux := mload(0x40) // Point to the next free position + let pIn := pVals + let lastPIn := add(pVals, mul(n, 32)) // Read n elemnts + let acc := mload(pIn) // Read the first element + pIn := add(pIn, 32) // Point to the second element + let inv + + + for { } lt(pIn, lastPIn) { + pAux := add(pAux, 32) + pIn := add(pIn, 32) + } + { + mstore(pAux, acc) + acc := mulmod(acc, mload(pIn), q) + } + acc := inverse(acc, q) + + // At this point pAux pint to the next free position we substract 1 to point to the last used + pAux := sub(pAux, 32) + // pIn points to the n+1 element, we substract to point to n + pIn := sub(pIn, 32) + lastPIn := pVals // We don't process the first element + for { } gt(pIn, lastPIn) { + pAux := sub(pAux, 32) + pIn := sub(pIn, 32) + } + { + inv := mulmod(acc, mload(pAux), q) + acc := mulmod(acc, mload(pIn), q) + mstore(pIn, inv) + } + // pIn points to first element, we just set it. + mstore(pIn, acc) + } + + function checkField(v) { + if iszero(lt(v, q)) { + mstore(0, 0) + return(0,0x20) + } + } + + function checkInput() { + checkField(calldataload(pEval_a)) + checkField(calldataload(pEval_b)) + checkField(calldataload(pEval_c)) + checkField(calldataload(pEval_s1)) + checkField(calldataload(pEval_s2)) + checkField(calldataload(pEval_zw)) + } + + function calculateChallenges(pMem, pPublic) { + let beta + let aux + + let mIn := mload(0x40) // Pointer to the next free memory position + + // Compute challenge.beta & challenge.gamma + mstore(mIn, Qmx) + mstore(add(mIn, 32), Qmy) + mstore(add(mIn, 64), Qlx) + mstore(add(mIn, 96), Qly) + mstore(add(mIn, 128), Qrx) + mstore(add(mIn, 160), Qry) + mstore(add(mIn, 192), Qox) + mstore(add(mIn, 224), Qoy) + mstore(add(mIn, 256), Qcx) + mstore(add(mIn, 288), Qcy) + mstore(add(mIn, 320), S1x) + mstore(add(mIn, 352), S1y) + mstore(add(mIn, 384), S2x) + mstore(add(mIn, 416), S2y) + mstore(add(mIn, 448), S3x) + mstore(add(mIn, 480), S3y) + + <%for (let i=0; i + mstore(add(mIn, <%= 512 + i*32 %>), calldataload(add(pPublic, <%=i*32%>))) + <%}%> + mstore(add(mIn, <%= 512 + nPublic*32 + 0 %> ), calldataload(pA)) + mstore(add(mIn, <%= 512 + nPublic*32 + 32 %> ), calldataload(add(pA, 32))) + mstore(add(mIn, <%= 512 + nPublic*32 + 64 %> ), calldataload(pB)) + mstore(add(mIn, <%= 512 + nPublic*32 + 96 %> ), calldataload(add(pB, 32))) + mstore(add(mIn, <%= 512 + nPublic*32 + 128 %> ), calldataload(pC)) + mstore(add(mIn, <%= 512 + nPublic*32 + 160 %> ), calldataload(add(pC, 32))) + + beta := mod(keccak256(mIn, <%= 704 + 32 * nPublic %>), q) + mstore(add(pMem, pBeta), beta) + + // challenges.gamma + mstore(add(pMem, pGamma), mod(keccak256(add(pMem, pBeta), 32), q)) + + // challenges.alpha + mstore(mIn, mload(add(pMem, pBeta))) + mstore(add(mIn, 32), mload(add(pMem, pGamma))) + mstore(add(mIn, 64), calldataload(pZ)) + mstore(add(mIn, 96), calldataload(add(pZ, 32))) + + aux := mod(keccak256(mIn, 128), q) + mstore(add(pMem, pAlpha), aux) + mstore(add(pMem, pAlpha2), mulmod(aux, aux, q)) + + // challenges.xi + mstore(mIn, aux) + mstore(add(mIn, 32), calldataload(pT1)) + mstore(add(mIn, 64), calldataload(add(pT1, 32))) + mstore(add(mIn, 96), calldataload(pT2)) + mstore(add(mIn, 128), calldataload(add(pT2, 32))) + mstore(add(mIn, 160), calldataload(pT3)) + mstore(add(mIn, 192), calldataload(add(pT3, 32))) + + aux := mod(keccak256(mIn, 224), q) + mstore( add(pMem, pXi), aux) + + // challenges.v + mstore(mIn, aux) + mstore(add(mIn, 32), calldataload(pEval_a)) + mstore(add(mIn, 64), calldataload(pEval_b)) + mstore(add(mIn, 96), calldataload(pEval_c)) + mstore(add(mIn, 128), calldataload(pEval_s1)) + mstore(add(mIn, 160), calldataload(pEval_s2)) + mstore(add(mIn, 192), calldataload(pEval_zw)) + + let v1 := mod(keccak256(mIn, 224), q) + mstore(add(pMem, pV1), v1) + + // challenges.beta * challenges.xi + mstore(add(pMem, pBetaXi), mulmod(beta, aux, q)) + + // challenges.xi^n + <%for (let i=0; i + aux:= mulmod(aux, aux, q) + <%}%> + mstore(add(pMem, pXin), aux) + + // Zh + aux:= mod(add(sub(aux, 1), q), q) + mstore(add(pMem, pZh), aux) + mstore(add(pMem, pZhInv), aux) // We will invert later together with lagrange pols + + // challenges.v^2, challenges.v^3, challenges.v^4, challenges.v^5 + aux := mulmod(v1, v1, q) + mstore(add(pMem, pV2), aux) + aux := mulmod(aux, v1, q) + mstore(add(pMem, pV3), aux) + aux := mulmod(aux, v1, q) + mstore(add(pMem, pV4), aux) + aux := mulmod(aux, v1, q) + mstore(add(pMem, pV5), aux) + + // challenges.u + mstore(mIn, calldataload(pWxi)) + mstore(add(mIn, 32), calldataload(add(pWxi, 32))) + mstore(add(mIn, 64), calldataload(pWxiw)) + mstore(add(mIn, 96), calldataload(add(pWxiw, 32))) + + mstore(add(pMem, pU), mod(keccak256(mIn, 128), q)) + } + + function calculateLagrange(pMem) { + let w := 1 + <% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> + mstore( + add(pMem, pEval_l<%=i%>), + mulmod( + n, + mod( + add( + sub( + mload(add(pMem, pXi)), + w + ), + q + ), + q + ), + q + ) + ) + <% if (i + w := mulmod(w, w1, q) + <% } %> + <% } %> + + inverseArray(add(pMem, pZhInv), <%=Math.max(nPublic, 1)+1%> ) + + let zh := mload(add(pMem, pZh)) + w := 1 + <% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> + <% if (i==1) { %> + mstore( + add(pMem, pEval_l1 ), + mulmod( + mload(add(pMem, pEval_l1 )), + zh, + q + ) + ) + <% } else { %> + mstore( + add(pMem, pEval_l<%=i%>), + mulmod( + w, + mulmod( + mload(add(pMem, pEval_l<%=i%>)), + zh, + q + ), + q + ) + ) + <% } %> + <% if (i + w := mulmod(w, w1, q) + <% } %> + <% } %> + + + } + + function calculatePI(pMem, pPub) { + let pl := 0 + + <% for (let i=0; i + pl := mod( + add( + sub( + pl, + mulmod( + mload(add(pMem, pEval_l<%=i+1%>)), + calldataload(add(pPub, <%=i*32%>)), + q + ) + ), + q + ), + q + ) + <% } %> + + mstore(add(pMem, pPI), pl) + } + + function calculateR0(pMem) { + let e1 := mload(add(pMem, pPI)) + + let e2 := mulmod(mload(add(pMem, pEval_l1)), mload(add(pMem, pAlpha2)), q) + + let e3a := addmod( + calldataload(pEval_a), + mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), + q) + e3a := addmod(e3a, mload(add(pMem, pGamma)), q) + + let e3b := addmod( + calldataload(pEval_b), + mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), + q) + e3b := addmod(e3b, mload(add(pMem, pGamma)), q) + + let e3c := addmod( + calldataload(pEval_c), + mload(add(pMem, pGamma)), + q) + + let e3 := mulmod(mulmod(e3a, e3b, q), e3c, q) + e3 := mulmod(e3, calldataload(pEval_zw), q) + e3 := mulmod(e3, mload(add(pMem, pAlpha)), q) + + let r0 := addmod(e1, mod(sub(q, e2), q), q) + r0 := addmod(r0, mod(sub(q, e3), q), q) + + mstore(add(pMem, pEval_r0) , r0) + } + + function g1_set(pR, pP) { + mstore(pR, mload(pP)) + mstore(add(pR, 32), mload(add(pP,32))) + } + + function g1_setC(pR, x, y) { + mstore(pR, x) + mstore(add(pR, 32), y) + } + + function g1_calldataSet(pR, pP) { + mstore(pR, calldataload(pP)) + mstore(add(pR, 32), calldataload(add(pP, 32))) + } + + function g1_acc(pR, pP) { + let mIn := mload(0x40) + mstore(mIn, mload(pR)) + mstore(add(mIn,32), mload(add(pR, 32))) + mstore(add(mIn,64), mload(pP)) + mstore(add(mIn,96), mload(add(pP, 32))) + + let success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + } + + function g1_mulAcc(pR, pP, s) { + let success + let mIn := mload(0x40) + mstore(mIn, mload(pP)) + mstore(add(mIn,32), mload(add(pP, 32))) + mstore(add(mIn,64), s) + + success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + + mstore(add(mIn,64), mload(pR)) + mstore(add(mIn,96), mload(add(pR, 32))) + + success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + + } + + function g1_mulAccC(pR, x, y, s) { + let success + let mIn := mload(0x40) + mstore(mIn, x) + mstore(add(mIn,32), y) + mstore(add(mIn,64), s) + + success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + + mstore(add(mIn,64), mload(pR)) + mstore(add(mIn,96), mload(add(pR, 32))) + + success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + } + + function g1_mulSetC(pR, x, y, s) { + let success + let mIn := mload(0x40) + mstore(mIn, x) + mstore(add(mIn,32), y) + mstore(add(mIn,64), s) + + success := staticcall(sub(gas(), 2000), 7, mIn, 96, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + } + + function g1_mulSet(pR, pP, s) { + g1_mulSetC(pR, mload(pP), mload(add(pP, 32)), s) + } + + function calculateD(pMem) { + let _pD:= add(pMem, pD) + let gamma := mload(add(pMem, pGamma)) + let mIn := mload(0x40) + mstore(0x40, add(mIn, 256)) // d1, d2, d3 & d4 (4*64 bytes) + + g1_setC(_pD, Qcx, Qcy) + g1_mulAccC(_pD, Qmx, Qmy, mulmod(calldataload(pEval_a), calldataload(pEval_b), q)) + g1_mulAccC(_pD, Qlx, Qly, calldataload(pEval_a)) + g1_mulAccC(_pD, Qrx, Qry, calldataload(pEval_b)) + g1_mulAccC(_pD, Qox, Qoy, calldataload(pEval_c)) + + let betaxi := mload(add(pMem, pBetaXi)) + let val1 := addmod( + addmod(calldataload(pEval_a), betaxi, q), + gamma, q) + + let val2 := addmod( + addmod( + calldataload(pEval_b), + mulmod(betaxi, k1, q), + q), gamma, q) + + let val3 := addmod( + addmod( + calldataload(pEval_c), + mulmod(betaxi, k2, q), + q), gamma, q) + + let d2a := mulmod( + mulmod(mulmod(val1, val2, q), val3, q), + mload(add(pMem, pAlpha)), + q + ) + + let d2b := mulmod( + mload(add(pMem, pEval_l1)), + mload(add(pMem, pAlpha2)), + q + ) + + // We'll use mIn to save d2 + g1_calldataSet(add(mIn, 192), pZ) + g1_mulSet( + mIn, + add(mIn, 192), + addmod(addmod(d2a, d2b, q), mload(add(pMem, pU)), q)) + + + val1 := addmod( + addmod( + calldataload(pEval_a), + mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), + q), gamma, q) + + val2 := addmod( + addmod( + calldataload(pEval_b), + mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), + q), gamma, q) + + val3 := mulmod( + mulmod(mload(add(pMem, pAlpha)), mload(add(pMem, pBeta)), q), + calldataload(pEval_zw), q) + + + // We'll use mIn + 64 to save d3 + g1_mulSetC( + add(mIn, 64), + S3x, + S3y, + mulmod(mulmod(val1, val2, q), val3, q)) + + // We'll use mIn + 128 to save d4 + g1_calldataSet(add(mIn, 128), pT1) + + g1_mulAccC(add(mIn, 128), calldataload(pT2), calldataload(add(pT2, 32)), mload(add(pMem, pXin))) + let xin2 := mulmod(mload(add(pMem, pXin)), mload(add(pMem, pXin)), q) + g1_mulAccC(add(mIn, 128), calldataload(pT3), calldataload(add(pT3, 32)) , xin2) + + g1_mulSetC(add(mIn, 128), mload(add(mIn, 128)), mload(add(mIn, 160)), mload(add(pMem, pZh))) + + mstore(add(add(mIn, 64), 32), mod(sub(qf, mload(add(add(mIn, 64), 32))), qf)) + mstore(add(mIn, 160), mod(sub(qf, mload(add(mIn, 160))), qf)) + g1_acc(_pD, mIn) + g1_acc(_pD, add(mIn, 64)) + g1_acc(_pD, add(mIn, 128)) + } + + function calculateF(pMem) { + let p := add(pMem, pF) + + g1_set(p, add(pMem, pD)) + g1_mulAccC(p, calldataload(pA), calldataload(add(pA, 32)), mload(add(pMem, pV1))) + g1_mulAccC(p, calldataload(pB), calldataload(add(pB, 32)), mload(add(pMem, pV2))) + g1_mulAccC(p, calldataload(pC), calldataload(add(pC, 32)), mload(add(pMem, pV3))) + g1_mulAccC(p, S1x, S1y, mload(add(pMem, pV4))) + g1_mulAccC(p, S2x, S2y, mload(add(pMem, pV5))) + } + + function calculateE(pMem) { + let s := mod(sub(q, mload(add(pMem, pEval_r0))), q) + + s := addmod(s, mulmod(calldataload(pEval_a), mload(add(pMem, pV1)), q), q) + s := addmod(s, mulmod(calldataload(pEval_b), mload(add(pMem, pV2)), q), q) + s := addmod(s, mulmod(calldataload(pEval_c), mload(add(pMem, pV3)), q), q) + s := addmod(s, mulmod(calldataload(pEval_s1), mload(add(pMem, pV4)), q), q) + s := addmod(s, mulmod(calldataload(pEval_s2), mload(add(pMem, pV5)), q), q) + s := addmod(s, mulmod(calldataload(pEval_zw), mload(add(pMem, pU)), q), q) + + g1_mulSetC(add(pMem, pE), G1x, G1y, s) + } + + function checkPairing(pMem) -> isOk { + let mIn := mload(0x40) + mstore(0x40, add(mIn, 576)) // [0..383] = pairing data, [384..447] = pWxi, [448..512] = pWxiw + + let _pWxi := add(mIn, 384) + let _pWxiw := add(mIn, 448) + let _aux := add(mIn, 512) + + g1_calldataSet(_pWxi, pWxi) + g1_calldataSet(_pWxiw, pWxiw) + + // A1 + g1_mulSet(mIn, _pWxiw, mload(add(pMem, pU))) + g1_acc(mIn, _pWxi) + mstore(add(mIn, 32), mod(sub(qf, mload(add(mIn, 32))), qf)) + + // [X]_2 + mstore(add(mIn,64), X2x2) + mstore(add(mIn,96), X2x1) + mstore(add(mIn,128), X2y2) + mstore(add(mIn,160), X2y1) + + // B1 + g1_mulSet(add(mIn, 192), _pWxi, mload(add(pMem, pXi))) + + let s := mulmod(mload(add(pMem, pU)), mload(add(pMem, pXi)), q) + s := mulmod(s, w1, q) + g1_mulSet(_aux, _pWxiw, s) + g1_acc(add(mIn, 192), _aux) + g1_acc(add(mIn, 192), add(pMem, pF)) + mstore(add(pMem, add(pE, 32)), mod(sub(qf, mload(add(pMem, add(pE, 32)))), qf)) + g1_acc(add(mIn, 192), add(pMem, pE)) + + // [1]_2 + mstore(add(mIn,256), G2x2) + mstore(add(mIn,288), G2x1) + mstore(add(mIn,320), G2y2) + mstore(add(mIn,352), G2y1) + + let success := staticcall(sub(gas(), 2000), 8, mIn, 384, mIn, 0x20) + + isOk := and(success, mload(mIn)) + } + + let pMem := mload(0x40) + mstore(0x40, add(pMem, lastMem)) + + checkInput() + calculateChallenges(pMem, _pubSignals) + calculateLagrange(pMem) + calculatePI(pMem, _pubSignals) + calculateR0(pMem) + calculateD(pMem) + calculateF(pMem) + calculateE(pMem) + let isValid := checkPairing(pMem) + + mstore(0x40, sub(pMem, lastMem)) + mstore(0, isValid) + return(0,0x20) + } + + } +} \ No newline at end of file From f91fa145fe56d5268e8830dbe6bbb0d9a8cf4173 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Thu, 11 Apr 2024 13:44:32 +0100 Subject: [PATCH 03/12] Update rln scripts --- .../src/templates/rln/index.ts | 2 + .../scripts/groth16/groth16_trusted_setup.ts | 2 +- .../rln/scripts/groth16/groth16_zkproof.ts | 2 +- .../rln/scripts/plonk/plonk_trusted_setup.ts | 3 +- .../rln/scripts/plonk/plonk_zkproof.ts | 207 +++++++++++++----- 5 files changed, 154 insertions(+), 62 deletions(-) diff --git a/libs/remix-ws-templates/src/templates/rln/index.ts b/libs/remix-ws-templates/src/templates/rln/index.ts index f3fc2a2eca..aeb3b9be59 100644 --- a/libs/remix-ws-templates/src/templates/rln/index.ts +++ b/libs/remix-ws-templates/src/templates/rln/index.ts @@ -17,6 +17,8 @@ export default async () => { // @ts-ignore 'templates/groth16_verifier.sol.ejs': (await import('!!raw-loader!./templates/groth16_verifier.sol.ejs')).default, // @ts-ignore + 'templates/plonk_verifier.sol.ejs': (await import('!!raw-loader!./templates/groth16_verifier.sol.ejs')).default, + // @ts-ignore 'LICENSE-APACHE': (await import('!!raw-loader!./LICENSE-APACHE')).default, // @ts-ignore 'LICENSE-MIT': (await import('!!raw-loader!./LICENSE-MIT')).default, diff --git a/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_trusted_setup.ts b/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_trusted_setup.ts index 0452df371f..19b9736314 100644 --- a/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_trusted_setup.ts +++ b/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_trusted_setup.ts @@ -42,7 +42,7 @@ const logger = { await remix.call('fileManager', 'writeFile', './zk/keys/groth16/verification_key.json', JSON.stringify(vKey, null, 2)) console.log('save zkey_final') - await remix.call('fileManager', 'writeFile', './zk/keys/groth16/zkey_final.txt', JSON.stringify(Array.from(((zkey_final as any).data)))) + await remix.call('fileManager', 'writeFile', './zk/keys/groth16/zkey_final.txt', (zkey_final as any).data, { encoding: null }) console.log('setup done.') diff --git a/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_zkproof.ts b/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_zkproof.ts index 109718273b..3c3fe6b20e 100644 --- a/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_zkproof.ts +++ b/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_zkproof.ts @@ -76,7 +76,7 @@ async function prove (signals, wasm, wtns, r1cs, zkey_final, vKey) { const zkey_final = { type: "mem", - data: new Uint8Array(JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/groth16/zkey_final.txt'))) + data: new Uint8Array(await remix.call('fileManager', 'readFile', './zk/keys/groth16/zkey_final.txt', { encoding: null })) } const wtns = { type: "mem" }; diff --git a/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_trusted_setup.ts b/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_trusted_setup.ts index 866cb1dac6..2cff9dabae 100644 --- a/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_trusted_setup.ts +++ b/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_trusted_setup.ts @@ -20,7 +20,8 @@ const snarkjs = require('snarkjs'); const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) console.log('save zkey_final') - await remix.call('fileManager', 'writeFile', './zk/keys/plonk/zkey_final.txt', JSON.stringify(Array.from(((zkey_final as any).data)))) + // @ts-ignore + await remix.call('fileManager', 'writeFile', './zk/keys/plonk/zkey_final.txt', (zkey_final as any).data, { encoding: null }) console.log('save verification key') await remix.call('fileManager', 'writeFile', './zk/keys/plonk/verification_key.json', JSON.stringify(vKey, null, 2)) diff --git a/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_zkproof.ts b/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_zkproof.ts index 730c98135a..e6010af4dc 100644 --- a/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_zkproof.ts +++ b/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_zkproof.ts @@ -1,6 +1,13 @@ import { ethers, BigNumber } from 'ethers' +import { IncrementalMerkleTree } from "@zk-kit/incremental-merkle-tree" import { poseidon } from "circomlibjs" // v0.0.8 +import { ZqField } from 'ffjavascript' +const SNARK_FIELD_SIZE = BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617') + +// Creates the finite field +const Fq = new ZqField(SNARK_FIELD_SIZE) + // eslint-disable-next-line @typescript-eslint/no-var-requires const snarkjs = require('snarkjs'); @@ -8,86 +15,168 @@ const logger = { info: (...args) => console.log(...args), debug: (...args) => console.log(...args), error: (...args) => console.error(...args), -}; +} + +/** + * Recovers secret from two shares + * @param x1 signal hash of first message + * @param x2 signal hash of second message + * @param y1 yshare of first message + * @param y2 yshare of second message + * @returns identity secret + */ +function shamirRecovery(x1: bigint, x2: bigint, y1: bigint, y2: bigint): bigint { + const slope = Fq.div(Fq.sub(y2, y1), Fq.sub(x2, x1)) + const privateKey = Fq.sub(y1, Fq.mul(slope, x1)) + + return Fq.normalize(privateKey) +} + +function hash(message: any): bigint { + message = BigNumber.from(message).toTwos(256).toHexString() + message = ethers.utils.zeroPad(message, 32) + return BigInt(ethers.utils.keccak256(message)) >> BigInt(8) +} + +function hashNullifier(message: any): bigint { + return BigInt(ethers.utils.keccak256(message)) >> BigInt(8) +} + +async function prove (signals, wasm, wtns, r1cs, zkey_final, vKey) { + 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.plonk.prove(zkey_final, wtns); + + const verified = await snarkjs.plonk.verify(vKey, publicSignals, proof, logger); + console.log('zk proof validity', verified); + + await remix.call('fileManager', 'writeFile', `zk/build/plonk/input-${Date.now()}.json`, JSON.stringify({ + _pubSignals: publicSignals, + _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), + ] + }, null, 2)) + + console.log('proof done.') + return { + proof, + x: publicSignals[3], + y: publicSignals[0] + } +} (async () => { try { // @ts-ignore - await remix.call('circuit-compiler', 'compile', 'circuits/calculate_hash.circom'); + const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.r1cs', { encoding: null }); + // @ts-ignore + const r1cs = new Uint8Array(r1csBuffer); + // @ts-ignore + await remix.call('circuit-compiler', 'compile', 'circuits/rln.circom'); // @ts-ignore - const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/calculate_hash.wasm', { encoding: null }); + const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.wasm', { encoding: null }); // @ts-ignore - const wasm = new Uint8Array(wasmBuffer); + const wasm = new Uint8Array(wasmBuffer); + const zkey_final = { type: "mem", - data: new Uint8Array(JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/plonk/zkey_final.txt'))) + data: new Uint8Array(await remix.call('fileManager', 'readFile', './zk/keys/plonk/zkey_final.txt', { encoding: null })) } + const wtns = { type: "mem" }; - const wtns = { type: "mem" }; - const value1 = '1234' - const value2 = '2' - const value3 = '3' - const value4 = '4' + const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/plonk/verification_key.json')) + + // build list of identity commitments + const secrets = [] + const identityCommitments = [] + const rateCommitments = [] + const userMessageLimit = 0x2 + for (let k = 0; k < 2; k++) { + const identitySecret = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32))) + secrets.push(identitySecret) + + const identityCommitment = poseidon([identitySecret]) + const rateCommitment = poseidon([identityCommitment, userMessageLimit]) + identityCommitments.push(identityCommitment) + rateCommitments.push(rateCommitment) + } - 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]) + let tree + + try { + tree = new IncrementalMerkleTree(poseidon, 20, BigInt(0), 2, rateCommitments) // Binary tree. + } catch (e) { + console.error(e.message) + return } - console.log('calculate') - await snarkjs.wtns.calculate(signals, wasm, wtns, logger); + const merkleproof1 = tree.createProof(0) + + const appId = 0xa + const epoch = 0x1 + + const signals1 = { + identitySecret: secrets[0], + userMessageLimit, + messageId: 0x0, + pathElements: merkleproof1.siblings, + identityPathIndex: merkleproof1.pathIndices, + x: 0xabcd, // hash(message) + externalNullifier: 0xa // hash(epoch, appId) + } + const proof1 = await prove(signals1, wasm, wtns, r1cs, zkey_final, vKey) - const { proof, publicSignals } = await snarkjs.plonk.prove(zkey_final, wtns); + const signals2 = { + identitySecret: secrets[0], + userMessageLimit, + messageId: 0x0, + pathElements: merkleproof1.siblings, + identityPathIndex: merkleproof1.pathIndices, + x: 0xabce, // hash(message) + externalNullifier: 0xa // hash(epoch, appId) + } + const proof2 = await prove(signals2, wasm, wtns, r1cs, zkey_final, vKey) - const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/plonk/verification_key.json')) - - const verified = await snarkjs.plonk.verify(vKey, publicSignals, proof); + const secret = shamirRecovery(BigInt(proof1.x), BigInt(proof2.x), BigInt(proof1.y), BigInt(proof2.y)) - console.log('zk proof validity', verified); + console.log(secret.toString(10)) + console.log(Fq.normalize(secrets[0])) + const templates = { plonk: await remix.call('fileManager', 'readFile', 'templates/plonk_verifier.sol.ejs') } const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) - await remix.call('fileManager', 'writeFile', 'zk/build/plonk/zk_verifier.sol', solidityContract) - await remix.call('fileManager', 'writeFile', 'zk/build/plonk/input.json', JSON.stringify({ - _pubSignals: publicSignals, - _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), - ] - }, null, 2)) - - console.log('proof done.') - + await remix.call('fileManager', 'writeFile', './zk/build/plonk/zk_verifier.sol', solidityContract) } catch (e) { console.error(e.message) } -})() +})() \ No newline at end of file From dab59297dac566931c55499cce6e8fc14ef09eef Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Thu, 11 Apr 2024 13:48:36 +0100 Subject: [PATCH 04/12] Remove simple circom circuit from semaphore template --- .../src/templates/semaphore/circuits/simple.circom | 11 ----------- .../src/templates/semaphore/index.ts | 2 -- 2 files changed, 13 deletions(-) delete mode 100644 libs/remix-ws-templates/src/templates/semaphore/circuits/simple.circom diff --git a/libs/remix-ws-templates/src/templates/semaphore/circuits/simple.circom b/libs/remix-ws-templates/src/templates/semaphore/circuits/simple.circom deleted file mode 100644 index 9a2120df7a..0000000000 --- a/libs/remix-ws-templates/src/templates/semaphore/circuits/simple.circom +++ /dev/null @@ -1,11 +0,0 @@ -pragma circom 2.0.0; - -template Multiplier2() { - signal input a; - signal input b; - signal output c; - c <== a*b; - } - - component main = Multiplier2(); - \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/semaphore/index.ts b/libs/remix-ws-templates/src/templates/semaphore/index.ts index 3c63bc8327..f53eb5459e 100644 --- a/libs/remix-ws-templates/src/templates/semaphore/index.ts +++ b/libs/remix-ws-templates/src/templates/semaphore/index.ts @@ -3,8 +3,6 @@ export default async () => { // @ts-ignore 'circuits/semaphore.circom': (await import('raw-loader!./circuits/semaphore.circom')).default, // @ts-ignore - 'circuits/simple.circom': (await import('!!raw-loader!./circuits/simple.circom')).default, - // @ts-ignore 'circuits/tree.circom': (await import('!!raw-loader!./circuits/tree.circom')).default, // @ts-ignore 'scripts/run_setup.ts': (await import('!!raw-loader!./scripts/run_setup.ts')).default, From fc5a4f1169718392d2005c90b7ce70040d238295 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Fri, 12 Apr 2024 17:13:40 +0100 Subject: [PATCH 05/12] Add plonk to semaphore scripts --- .../src/templates/semaphore/index.ts | 12 +- .../scripts/groth16/groth16_trusted_setup.ts | 53 ++ .../scripts/groth16/groth16_zkproof.ts | 108 +++ .../scripts/plonk/plonk_trusted_setup.ts | 37 + .../semaphore/scripts/plonk/plonk_zkproof.ts | 139 ++++ .../templates/plonk_verifier.sol.ejs | 710 ++++++++++++++++++ 6 files changed, 1056 insertions(+), 3 deletions(-) create mode 100644 libs/remix-ws-templates/src/templates/semaphore/scripts/groth16/groth16_trusted_setup.ts create mode 100644 libs/remix-ws-templates/src/templates/semaphore/scripts/groth16/groth16_zkproof.ts create mode 100644 libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_trusted_setup.ts create mode 100644 libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_zkproof.ts create mode 100644 libs/remix-ws-templates/src/templates/semaphore/templates/plonk_verifier.sol.ejs diff --git a/libs/remix-ws-templates/src/templates/semaphore/index.ts b/libs/remix-ws-templates/src/templates/semaphore/index.ts index f53eb5459e..37f8484829 100644 --- a/libs/remix-ws-templates/src/templates/semaphore/index.ts +++ b/libs/remix-ws-templates/src/templates/semaphore/index.ts @@ -5,12 +5,18 @@ export default async () => { // @ts-ignore 'circuits/tree.circom': (await import('!!raw-loader!./circuits/tree.circom')).default, // @ts-ignore - 'scripts/run_setup.ts': (await import('!!raw-loader!./scripts/run_setup.ts')).default, + 'scripts/groth16/groth16_trusted_setup.ts': (await import('!!raw-loader!./scripts/groth16/groth16_trusted_setup.ts')).default, // @ts-ignore - 'scripts/run_verification.ts': (await import('!!raw-loader!./scripts/run_verification.ts')).default, + 'scripts/groth16/groth16_zkproof.ts': (await import('!!raw-loader!./scripts/groth16/groth16_zkproof.ts')).default, + // @ts-ignore + 'scripts/plonk/plonk_trusted_setup.ts': (await import('!!raw-loader!./scripts/plonk/plonk_trusted_setup.ts')).default, + // @ts-ignore + 'scripts/plonk/plonk_zkproof.ts': (await import('!!raw-loader!./scripts/plonk/plonk_zkproof.ts')).default, // @ts-ignore 'templates/groth16_verifier.sol.ejs': (await import('!!raw-loader!./templates/groth16_verifier.sol.ejs')).default, // @ts-ignore + 'templates/plonk_verifier.sol.ejs': (await import('!!raw-loader!./templates/plonk_verifier.sol.ejs')).default, + // @ts-ignore 'README.md': (await import('raw-loader!./README.md')).default } -} \ No newline at end of file +} diff --git a/libs/remix-ws-templates/src/templates/semaphore/scripts/groth16/groth16_trusted_setup.ts b/libs/remix-ws-templates/src/templates/semaphore/scripts/groth16/groth16_trusted_setup.ts new file mode 100644 index 0000000000..d3b205f858 --- /dev/null +++ b/libs/remix-ws-templates/src/templates/semaphore/scripts/groth16/groth16_trusted_setup.ts @@ -0,0 +1,53 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const snarkjs = require('snarkjs'); + +const logger = { + info: (...args) => console.log(...args), + debug: (...args) => console.log(...args) +}; + +(async () => { + try { + // @ts-ignore + await remix.call('circuit-compiler', 'generateR1cs', 'circuits/semaphore.circom'); + + const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW"; + // @ts-ignore + const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', { encoding: null }); + // @ts-ignore + const r1cs = new Uint8Array(r1csBuffer); + const zkey_0 = { type: "mem" }; + const zkey_1 = { type: "mem" }; + const zkey_final = { type: "mem" }; + + console.log('newZkey') + await snarkjs.zKey.newZKey(r1cs, ptau_final, zkey_0); + + console.log('contribute') + await snarkjs.zKey.contribute(zkey_0, zkey_1, "p2_C1", "pa_Entropy1"); + + console.log('beacon') + await snarkjs.zKey.beacon(zkey_1, zkey_final, "B3", "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", 10); + + console.log('verifyFromR1cs') + const verifyFromR1csResult = await snarkjs.zKey.verifyFromR1cs(r1cs, ptau_final, zkey_final); + console.assert(verifyFromR1csResult); + + console.log('verifyFromInit') + const verifyFromInit = await snarkjs.zKey.verifyFromInit(zkey_0, ptau_final, zkey_final); + console.assert(verifyFromInit); + + console.log('exportVerificationKey') + const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) + await remix.call('fileManager', 'writeFile', './zk/keys/groth16/verification_key.json', JSON.stringify(vKey, null, 2)) + + console.log('save zkey_final') + // @ts-ignore + await remix.call('fileManager', 'writeFile', './zk/keys/groth16/zkey_final.txt', (zkey_final as any).data, { encoding: null }) + + console.log('setup done.') + + } catch (e) { + console.error(e.message) + } +})() \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/semaphore/scripts/groth16/groth16_zkproof.ts b/libs/remix-ws-templates/src/templates/semaphore/scripts/groth16/groth16_zkproof.ts new file mode 100644 index 0000000000..0ac12e2d7b --- /dev/null +++ b/libs/remix-ws-templates/src/templates/semaphore/scripts/groth16/groth16_zkproof.ts @@ -0,0 +1,108 @@ +import { ethers, BigNumber } from 'ethers' +import { IncrementalMerkleTree } from "@zk-kit/incremental-merkle-tree" +import { poseidon } from "circomlibjs" // v0.0.8 + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const snarkjs = require('snarkjs'); + +const logger = { + info: (...args) => console.log(...args), + debug: (...args) => console.log(...args), + error: (...args) => console.error(...args), +} + +/** + * Creates a keccak256 hash of a message compatible with the SNARK scalar modulus. + * @param message The message to be hashed. + * @returns The message digest. + */ +function hash(message: any): bigint { + message = BigNumber.from(message).toTwos(256).toHexString() + message = ethers.utils.zeroPad(message, 32) + return BigInt(ethers.utils.keccak256(message)) >> BigInt(8) +} + +(async () => { + try { + // @ts-ignore + const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', true); + // @ts-ignore + const r1cs = new Uint8Array(r1csBuffer); + // @ts-ignore + await remix.call('circuit-compiler', 'compile', 'circuits/semaphore.circom'); + // @ts-ignore + const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.wasm', true); + // @ts-ignore + const wasm = new Uint8Array(wasmBuffer); + + const zkey_final = { + type: "mem", + // @ts-ignore + data: new Uint8Array(await remix.call('fileManager', 'readFile', './zk/keys/groth16/zkey_final.txt', { encoding: null })) + } + const wtns = { type: "mem" }; + + const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/groth16/verification_key.json')) + + // build list of identity commitments + const secrets = [] + const identityCommitments = [] + for (let k = 0; k < 2; k++) { + const identityTrapdoor = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32))) + const identityNullifier = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32))) + secrets.push({identityTrapdoor, identityNullifier}) + + const secret = poseidon([identityNullifier, identityTrapdoor]) + const identityCommitment = poseidon([secret]) + identityCommitments.push(identityCommitment) + } + //console.log('incremental tree', identityCommitments.map((x) => x.toString())) + + let tree + + try { + tree = new IncrementalMerkleTree(poseidon, 20, BigInt(0), 2, identityCommitments) // Binary tree. + } catch (e) { + console.error(e.message) + return + } + const index = tree.indexOf(identityCommitments[0]) + + console.log(index.toString()) + + const proof1 = tree.createProof(0) + + console.log('prepare signals for id ', identityCommitments[0].toString(), tree.indexOf(identityCommitments[0]), proof1.siblings.map((x)=> x.toString())) + + const signals = { + identityTrapdoor: secrets[0].identityTrapdoor, + identityNullifier: secrets[0].identityNullifier, + treePathIndices: proof1.pathIndices, + treeSiblings: proof1.siblings, + externalNullifier: hash(42), + signalHash: hash(ethers.utils.formatBytes32String("Hello World")) + } + + 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); + proof1.root.toString() === publicSignals[0] ? console.log('merkle proof valid') : console.log('merkle proof invalid') + + const templates = { + groth16: await remix.call('fileManager', 'readFile', 'templates/groth16_verifier.sol.ejs') + } + const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) + + await remix.call('fileManager', 'writeFile', './zk/build/groth16/zk_verifier.sol', solidityContract) + } catch (e) { + console.error(e.message) + } +})() \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_trusted_setup.ts b/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_trusted_setup.ts new file mode 100644 index 0000000000..f23770246b --- /dev/null +++ b/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_trusted_setup.ts @@ -0,0 +1,37 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const snarkjs = require('snarkjs'); + +const logger = { + info: (...args) => console.log(...args), + debug: (...args) => console.log(...args) +}; + +(async () => { + try { + // @ts-ignore + await remix.call('circuit-compiler', 'generateR1cs', 'circuits/semaphore.circom'); + + const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW"; + // @ts-ignore + const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', { encoding: null }); + // @ts-ignore + const r1cs = new Uint8Array(r1csBuffer); + const zkey_final = { type: "mem" }; + + console.log('plonk setup') + await snarkjs.plonk.setup(r1cs, ptau_final, zkey_final) + + console.log('exportVerificationKey') + const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) + await remix.call('fileManager', 'writeFile', './zk/keys/plonk/verification_key.json', JSON.stringify(vKey, null, 2)) + + console.log('save zkey_final') + // @ts-ignore + await remix.call('fileManager', 'writeFile', './zk/keys/plonk/zkey_final.txt', (zkey_final as any).data, { encoding: null }) + + console.log('setup done.') + + } catch (e) { + console.error(e.message) + } +})() \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_zkproof.ts b/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_zkproof.ts new file mode 100644 index 0000000000..224c866018 --- /dev/null +++ b/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_zkproof.ts @@ -0,0 +1,139 @@ +import { ethers, BigNumber } from 'ethers' +import { IncrementalMerkleTree } from "@zk-kit/incremental-merkle-tree" +import { poseidon } from "circomlibjs" // v0.0.8 + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const snarkjs = require('snarkjs'); + +const logger = { + info: (...args) => console.log(...args), + debug: (...args) => console.log(...args), + error: (...args) => console.error(...args), +} + +/** + * Creates a keccak256 hash of a message compatible with the SNARK scalar modulus. + * @param message The message to be hashed. + * @returns The message digest. + */ +function hash(message: any): bigint { + message = BigNumber.from(message).toTwos(256).toHexString() + message = ethers.utils.zeroPad(message, 32) + return BigInt(ethers.utils.keccak256(message)) >> BigInt(8) +} + +(async () => { + try { + // @ts-ignore + const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', true); + // @ts-ignore + const r1cs = new Uint8Array(r1csBuffer); + // @ts-ignore + await remix.call('circuit-compiler', 'compile', 'circuits/semaphore.circom'); + // @ts-ignore + const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.wasm', true); + // @ts-ignore + const wasm = new Uint8Array(wasmBuffer); + + const zkey_final = { + type: "mem", + // @ts-ignore + data: new Uint8Array(await remix.call('fileManager', 'readFile', './zk/keys/plonk/zkey_final.txt', { encoding: null })) + } + const wtns = { type: "mem" }; + + const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/plonk/verification_key.json')) + + // build list of identity commitments + const secrets = [] + const identityCommitments = [] + for (let k = 0; k < 2; k++) { + const identityTrapdoor = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32))) + const identityNullifier = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32))) + secrets.push({identityTrapdoor, identityNullifier}) + + const secret = poseidon([identityNullifier, identityTrapdoor]) + const identityCommitment = poseidon([secret]) + identityCommitments.push(identityCommitment) + } + //console.log('incremental tree', identityCommitments.map((x) => x.toString())) + + let tree + + try { + tree = new IncrementalMerkleTree(poseidon, 20, BigInt(0), 2, identityCommitments) // Binary tree. + } catch (e) { + console.error(e.message) + return + } + const index = tree.indexOf(identityCommitments[0]) + + console.log(index.toString()) + + const proof1 = tree.createProof(0) + + console.log('prepare signals for id ', identityCommitments[0].toString(), tree.indexOf(identityCommitments[0]), proof1.siblings.map((x)=> x.toString())) + + const signals = { + identityTrapdoor: secrets[0].identityTrapdoor, + identityNullifier: secrets[0].identityNullifier, + treePathIndices: proof1.pathIndices, + treeSiblings: proof1.siblings, + externalNullifier: hash(42), + signalHash: hash(ethers.utils.formatBytes32String("Hello World")) + } + + 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.plonk.prove(zkey_final, wtns); + + const verified = await snarkjs.plonk.verify(vKey, publicSignals, proof, logger); + console.log('zk proof validity', verified); + proof1.root.toString() === publicSignals[0] ? console.log('merkle proof valid') : console.log('merkle proof invalid') + + const templates = { + plonk: await remix.call('fileManager', 'readFile', 'templates/plonk_verifier.sol.ejs') + } + const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) + + await remix.call('fileManager', 'writeFile', './zk/build/plonk/zk_verifier.sol', solidityContract) + await remix.call('fileManager', 'writeFile', 'zk/build/plonk/input.json', JSON.stringify({ + _pubSignals: publicSignals, + _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), + ] + }, null, 2)) + + console.log('proof done.') + } catch (e) { + console.error(e.message) + } +})() \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/semaphore/templates/plonk_verifier.sol.ejs b/libs/remix-ws-templates/src/templates/semaphore/templates/plonk_verifier.sol.ejs new file mode 100644 index 0000000000..da3f145b53 --- /dev/null +++ b/libs/remix-ws-templates/src/templates/semaphore/templates/plonk_verifier.sol.ejs @@ -0,0 +1,710 @@ +// SPDX-License-Identifier: GPL-3.0 +/* + Copyright 2021 0KIMS association. + + This file is generated with [snarkJS](https://github.com/iden3/snarkjs). + + snarkJS is a free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + snarkJS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with snarkJS. If not, see . +*/ + + +pragma solidity >=0.7.0 <0.9.0; + +import "hardhat/console.sol"; + +contract PlonkVerifier { + // Omega + uint256 constant w1 = <%=w%>; + // Scalar field size + uint256 constant q = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + // Base field size + uint256 constant qf = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + // [1]_1 + uint256 constant G1x = 1; + uint256 constant G1y = 2; + // [1]_2 + uint256 constant G2x1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 constant G2x2 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 constant G2y1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; + uint256 constant G2y2 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; + + // Verification Key data + uint32 constant n = <%=2**power%>; + uint16 constant nPublic = <%=nPublic%>; + uint16 constant nLagrange = <%=Math.max(nPublic, 1)%>; + + uint256 constant Qmx = <%=Qm[0]%>; + uint256 constant Qmy = <%=Qm[0] == "0" ? "0" : Qm[1]%>; + uint256 constant Qlx = <%=Ql[0]%>; + uint256 constant Qly = <%=Ql[0] == "0" ? "0" : Ql[1]%>; + uint256 constant Qrx = <%=Qr[0]%>; + uint256 constant Qry = <%=Qr[0] == "0" ? "0" : Qr[1]%>; + uint256 constant Qox = <%=Qo[0]%>; + uint256 constant Qoy = <%=Qo[0] == "0" ? "0" : Qo[1]%>; + uint256 constant Qcx = <%=Qc[0]%>; + uint256 constant Qcy = <%=Qc[0] == "0" ? "0" : Qc[1]%>; + uint256 constant S1x = <%=S1[0]%>; + uint256 constant S1y = <%=S1[0] == "0" ? "0" : S1[1]%>; + uint256 constant S2x = <%=S2[0]%>; + uint256 constant S2y = <%=S2[0] == "0" ? "0" : S2[1]%>; + uint256 constant S3x = <%=S3[0]%>; + uint256 constant S3y = <%=S3[0] == "0" ? "0" : S3[1]%>; + uint256 constant k1 = <%=k1%>; + uint256 constant k2 = <%=k2%>; + uint256 constant X2x1 = <%=X_2[0][0]%>; + uint256 constant X2x2 = <%=X_2[0][1]%>; + uint256 constant X2y1 = <%=X_2[1][0]%>; + uint256 constant X2y2 = <%=X_2[1][1]%>; + + // Proof calldata + // Byte offset of every parameter of the calldata + // Polynomial commitments + uint16 constant pA = 4 + 0; + uint16 constant pB = 4 + 64; + uint16 constant pC = 4 + 128; + uint16 constant pZ = 4 + 192; + uint16 constant pT1 = 4 + 256; + uint16 constant pT2 = 4 + 320; + uint16 constant pT3 = 4 + 384; + uint16 constant pWxi = 4 + 448; + uint16 constant pWxiw = 4 + 512; + // Opening evaluations + uint16 constant pEval_a = 4 + 576; + uint16 constant pEval_b = 4 + 608; + uint16 constant pEval_c = 4 + 640; + uint16 constant pEval_s1 = 4 + 672; + uint16 constant pEval_s2 = 4 + 704; + uint16 constant pEval_zw = 4 + 736; + + // Memory data + // Challenges + uint16 constant pAlpha = 0; + uint16 constant pBeta = 32; + uint16 constant pGamma = 64; + uint16 constant pXi = 96; + uint16 constant pXin = 128; + uint16 constant pBetaXi = 160; + uint16 constant pV1 = 192; + uint16 constant pV2 = 224; + uint16 constant pV3 = 256; + uint16 constant pV4 = 288; + uint16 constant pV5 = 320; + uint16 constant pU = 352; + + uint16 constant pPI = 384; + uint16 constant pEval_r0 = 416; + uint16 constant pD = 448; + uint16 constant pF = 512; + uint16 constant pE = 576; + uint16 constant pTmp = 640; + uint16 constant pAlpha2 = 704; + uint16 constant pZh = 736; + uint16 constant pZhInv = 768; + + <% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> + uint16 constant pEval_l<%=i%> = <%=768+i*32%>; + <% } %> + <% let pLastMem = 800+32*Math.max(nPublic,1) %> + + uint16 constant lastMem = <%=pLastMem%>; + + function verifyProof(uint256[24] calldata _proof, uint256[<%=nPublic%>] calldata _pubSignals) public view returns (bool) { + assembly { + ///////// + // Computes the inverse using the extended euclidean algorithm + ///////// + function inverse(a, q) -> inv { + let t := 0 + let newt := 1 + let r := q + let newr := a + let quotient + let aux + + for { } newr { } { + quotient := sdiv(r, newr) + aux := sub(t, mul(quotient, newt)) + t:= newt + newt:= aux + + aux := sub(r,mul(quotient, newr)) + r := newr + newr := aux + } + + if gt(r, 1) { revert(0,0) } + if slt(t, 0) { t:= add(t, q) } + + inv := t + } + + /////// + // Computes the inverse of an array of values + // See https://vitalik.ca/general/2018/07/21/starks_part_3.html in section where explain fields operations + ////// + function inverseArray(pVals, n) { + + let pAux := mload(0x40) // Point to the next free position + let pIn := pVals + let lastPIn := add(pVals, mul(n, 32)) // Read n elemnts + let acc := mload(pIn) // Read the first element + pIn := add(pIn, 32) // Point to the second element + let inv + + + for { } lt(pIn, lastPIn) { + pAux := add(pAux, 32) + pIn := add(pIn, 32) + } + { + mstore(pAux, acc) + acc := mulmod(acc, mload(pIn), q) + } + acc := inverse(acc, q) + + // At this point pAux pint to the next free position we substract 1 to point to the last used + pAux := sub(pAux, 32) + // pIn points to the n+1 element, we substract to point to n + pIn := sub(pIn, 32) + lastPIn := pVals // We don't process the first element + for { } gt(pIn, lastPIn) { + pAux := sub(pAux, 32) + pIn := sub(pIn, 32) + } + { + inv := mulmod(acc, mload(pAux), q) + acc := mulmod(acc, mload(pIn), q) + mstore(pIn, inv) + } + // pIn points to first element, we just set it. + mstore(pIn, acc) + } + + function checkField(v) { + if iszero(lt(v, q)) { + mstore(0, 0) + return(0,0x20) + } + } + + function checkInput() { + checkField(calldataload(pEval_a)) + checkField(calldataload(pEval_b)) + checkField(calldataload(pEval_c)) + checkField(calldataload(pEval_s1)) + checkField(calldataload(pEval_s2)) + checkField(calldataload(pEval_zw)) + } + + function calculateChallenges(pMem, pPublic) { + let beta + let aux + + let mIn := mload(0x40) // Pointer to the next free memory position + + // Compute challenge.beta & challenge.gamma + mstore(mIn, Qmx) + mstore(add(mIn, 32), Qmy) + mstore(add(mIn, 64), Qlx) + mstore(add(mIn, 96), Qly) + mstore(add(mIn, 128), Qrx) + mstore(add(mIn, 160), Qry) + mstore(add(mIn, 192), Qox) + mstore(add(mIn, 224), Qoy) + mstore(add(mIn, 256), Qcx) + mstore(add(mIn, 288), Qcy) + mstore(add(mIn, 320), S1x) + mstore(add(mIn, 352), S1y) + mstore(add(mIn, 384), S2x) + mstore(add(mIn, 416), S2y) + mstore(add(mIn, 448), S3x) + mstore(add(mIn, 480), S3y) + + <%for (let i=0; i + mstore(add(mIn, <%= 512 + i*32 %>), calldataload(add(pPublic, <%=i*32%>))) + <%}%> + mstore(add(mIn, <%= 512 + nPublic*32 + 0 %> ), calldataload(pA)) + mstore(add(mIn, <%= 512 + nPublic*32 + 32 %> ), calldataload(add(pA, 32))) + mstore(add(mIn, <%= 512 + nPublic*32 + 64 %> ), calldataload(pB)) + mstore(add(mIn, <%= 512 + nPublic*32 + 96 %> ), calldataload(add(pB, 32))) + mstore(add(mIn, <%= 512 + nPublic*32 + 128 %> ), calldataload(pC)) + mstore(add(mIn, <%= 512 + nPublic*32 + 160 %> ), calldataload(add(pC, 32))) + + beta := mod(keccak256(mIn, <%= 704 + 32 * nPublic %>), q) + mstore(add(pMem, pBeta), beta) + + // challenges.gamma + mstore(add(pMem, pGamma), mod(keccak256(add(pMem, pBeta), 32), q)) + + // challenges.alpha + mstore(mIn, mload(add(pMem, pBeta))) + mstore(add(mIn, 32), mload(add(pMem, pGamma))) + mstore(add(mIn, 64), calldataload(pZ)) + mstore(add(mIn, 96), calldataload(add(pZ, 32))) + + aux := mod(keccak256(mIn, 128), q) + mstore(add(pMem, pAlpha), aux) + mstore(add(pMem, pAlpha2), mulmod(aux, aux, q)) + + // challenges.xi + mstore(mIn, aux) + mstore(add(mIn, 32), calldataload(pT1)) + mstore(add(mIn, 64), calldataload(add(pT1, 32))) + mstore(add(mIn, 96), calldataload(pT2)) + mstore(add(mIn, 128), calldataload(add(pT2, 32))) + mstore(add(mIn, 160), calldataload(pT3)) + mstore(add(mIn, 192), calldataload(add(pT3, 32))) + + aux := mod(keccak256(mIn, 224), q) + mstore( add(pMem, pXi), aux) + + // challenges.v + mstore(mIn, aux) + mstore(add(mIn, 32), calldataload(pEval_a)) + mstore(add(mIn, 64), calldataload(pEval_b)) + mstore(add(mIn, 96), calldataload(pEval_c)) + mstore(add(mIn, 128), calldataload(pEval_s1)) + mstore(add(mIn, 160), calldataload(pEval_s2)) + mstore(add(mIn, 192), calldataload(pEval_zw)) + + let v1 := mod(keccak256(mIn, 224), q) + mstore(add(pMem, pV1), v1) + + // challenges.beta * challenges.xi + mstore(add(pMem, pBetaXi), mulmod(beta, aux, q)) + + // challenges.xi^n + <%for (let i=0; i + aux:= mulmod(aux, aux, q) + <%}%> + mstore(add(pMem, pXin), aux) + + // Zh + aux:= mod(add(sub(aux, 1), q), q) + mstore(add(pMem, pZh), aux) + mstore(add(pMem, pZhInv), aux) // We will invert later together with lagrange pols + + // challenges.v^2, challenges.v^3, challenges.v^4, challenges.v^5 + aux := mulmod(v1, v1, q) + mstore(add(pMem, pV2), aux) + aux := mulmod(aux, v1, q) + mstore(add(pMem, pV3), aux) + aux := mulmod(aux, v1, q) + mstore(add(pMem, pV4), aux) + aux := mulmod(aux, v1, q) + mstore(add(pMem, pV5), aux) + + // challenges.u + mstore(mIn, calldataload(pWxi)) + mstore(add(mIn, 32), calldataload(add(pWxi, 32))) + mstore(add(mIn, 64), calldataload(pWxiw)) + mstore(add(mIn, 96), calldataload(add(pWxiw, 32))) + + mstore(add(pMem, pU), mod(keccak256(mIn, 128), q)) + } + + function calculateLagrange(pMem) { + let w := 1 + <% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> + mstore( + add(pMem, pEval_l<%=i%>), + mulmod( + n, + mod( + add( + sub( + mload(add(pMem, pXi)), + w + ), + q + ), + q + ), + q + ) + ) + <% if (i + w := mulmod(w, w1, q) + <% } %> + <% } %> + + inverseArray(add(pMem, pZhInv), <%=Math.max(nPublic, 1)+1%> ) + + let zh := mload(add(pMem, pZh)) + w := 1 + <% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> + <% if (i==1) { %> + mstore( + add(pMem, pEval_l1 ), + mulmod( + mload(add(pMem, pEval_l1 )), + zh, + q + ) + ) + <% } else { %> + mstore( + add(pMem, pEval_l<%=i%>), + mulmod( + w, + mulmod( + mload(add(pMem, pEval_l<%=i%>)), + zh, + q + ), + q + ) + ) + <% } %> + <% if (i + w := mulmod(w, w1, q) + <% } %> + <% } %> + + + } + + function calculatePI(pMem, pPub) { + let pl := 0 + + <% for (let i=0; i + pl := mod( + add( + sub( + pl, + mulmod( + mload(add(pMem, pEval_l<%=i+1%>)), + calldataload(add(pPub, <%=i*32%>)), + q + ) + ), + q + ), + q + ) + <% } %> + + mstore(add(pMem, pPI), pl) + } + + function calculateR0(pMem) { + let e1 := mload(add(pMem, pPI)) + + let e2 := mulmod(mload(add(pMem, pEval_l1)), mload(add(pMem, pAlpha2)), q) + + let e3a := addmod( + calldataload(pEval_a), + mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), + q) + e3a := addmod(e3a, mload(add(pMem, pGamma)), q) + + let e3b := addmod( + calldataload(pEval_b), + mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), + q) + e3b := addmod(e3b, mload(add(pMem, pGamma)), q) + + let e3c := addmod( + calldataload(pEval_c), + mload(add(pMem, pGamma)), + q) + + let e3 := mulmod(mulmod(e3a, e3b, q), e3c, q) + e3 := mulmod(e3, calldataload(pEval_zw), q) + e3 := mulmod(e3, mload(add(pMem, pAlpha)), q) + + let r0 := addmod(e1, mod(sub(q, e2), q), q) + r0 := addmod(r0, mod(sub(q, e3), q), q) + + mstore(add(pMem, pEval_r0) , r0) + } + + function g1_set(pR, pP) { + mstore(pR, mload(pP)) + mstore(add(pR, 32), mload(add(pP,32))) + } + + function g1_setC(pR, x, y) { + mstore(pR, x) + mstore(add(pR, 32), y) + } + + function g1_calldataSet(pR, pP) { + mstore(pR, calldataload(pP)) + mstore(add(pR, 32), calldataload(add(pP, 32))) + } + + function g1_acc(pR, pP) { + let mIn := mload(0x40) + mstore(mIn, mload(pR)) + mstore(add(mIn,32), mload(add(pR, 32))) + mstore(add(mIn,64), mload(pP)) + mstore(add(mIn,96), mload(add(pP, 32))) + + let success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + } + + function g1_mulAcc(pR, pP, s) { + let success + let mIn := mload(0x40) + mstore(mIn, mload(pP)) + mstore(add(mIn,32), mload(add(pP, 32))) + mstore(add(mIn,64), s) + + success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + + mstore(add(mIn,64), mload(pR)) + mstore(add(mIn,96), mload(add(pR, 32))) + + success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + + } + + function g1_mulAccC(pR, x, y, s) { + let success + let mIn := mload(0x40) + mstore(mIn, x) + mstore(add(mIn,32), y) + mstore(add(mIn,64), s) + + success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + + mstore(add(mIn,64), mload(pR)) + mstore(add(mIn,96), mload(add(pR, 32))) + + success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + } + + function g1_mulSetC(pR, x, y, s) { + let success + let mIn := mload(0x40) + mstore(mIn, x) + mstore(add(mIn,32), y) + mstore(add(mIn,64), s) + + success := staticcall(sub(gas(), 2000), 7, mIn, 96, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0,0x20) + } + } + + function g1_mulSet(pR, pP, s) { + g1_mulSetC(pR, mload(pP), mload(add(pP, 32)), s) + } + + function calculateD(pMem) { + let _pD:= add(pMem, pD) + let gamma := mload(add(pMem, pGamma)) + let mIn := mload(0x40) + mstore(0x40, add(mIn, 256)) // d1, d2, d3 & d4 (4*64 bytes) + + g1_setC(_pD, Qcx, Qcy) + g1_mulAccC(_pD, Qmx, Qmy, mulmod(calldataload(pEval_a), calldataload(pEval_b), q)) + g1_mulAccC(_pD, Qlx, Qly, calldataload(pEval_a)) + g1_mulAccC(_pD, Qrx, Qry, calldataload(pEval_b)) + g1_mulAccC(_pD, Qox, Qoy, calldataload(pEval_c)) + + let betaxi := mload(add(pMem, pBetaXi)) + let val1 := addmod( + addmod(calldataload(pEval_a), betaxi, q), + gamma, q) + + let val2 := addmod( + addmod( + calldataload(pEval_b), + mulmod(betaxi, k1, q), + q), gamma, q) + + let val3 := addmod( + addmod( + calldataload(pEval_c), + mulmod(betaxi, k2, q), + q), gamma, q) + + let d2a := mulmod( + mulmod(mulmod(val1, val2, q), val3, q), + mload(add(pMem, pAlpha)), + q + ) + + let d2b := mulmod( + mload(add(pMem, pEval_l1)), + mload(add(pMem, pAlpha2)), + q + ) + + // We'll use mIn to save d2 + g1_calldataSet(add(mIn, 192), pZ) + g1_mulSet( + mIn, + add(mIn, 192), + addmod(addmod(d2a, d2b, q), mload(add(pMem, pU)), q)) + + + val1 := addmod( + addmod( + calldataload(pEval_a), + mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), + q), gamma, q) + + val2 := addmod( + addmod( + calldataload(pEval_b), + mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), + q), gamma, q) + + val3 := mulmod( + mulmod(mload(add(pMem, pAlpha)), mload(add(pMem, pBeta)), q), + calldataload(pEval_zw), q) + + + // We'll use mIn + 64 to save d3 + g1_mulSetC( + add(mIn, 64), + S3x, + S3y, + mulmod(mulmod(val1, val2, q), val3, q)) + + // We'll use mIn + 128 to save d4 + g1_calldataSet(add(mIn, 128), pT1) + + g1_mulAccC(add(mIn, 128), calldataload(pT2), calldataload(add(pT2, 32)), mload(add(pMem, pXin))) + let xin2 := mulmod(mload(add(pMem, pXin)), mload(add(pMem, pXin)), q) + g1_mulAccC(add(mIn, 128), calldataload(pT3), calldataload(add(pT3, 32)) , xin2) + + g1_mulSetC(add(mIn, 128), mload(add(mIn, 128)), mload(add(mIn, 160)), mload(add(pMem, pZh))) + + mstore(add(add(mIn, 64), 32), mod(sub(qf, mload(add(add(mIn, 64), 32))), qf)) + mstore(add(mIn, 160), mod(sub(qf, mload(add(mIn, 160))), qf)) + g1_acc(_pD, mIn) + g1_acc(_pD, add(mIn, 64)) + g1_acc(_pD, add(mIn, 128)) + } + + function calculateF(pMem) { + let p := add(pMem, pF) + + g1_set(p, add(pMem, pD)) + g1_mulAccC(p, calldataload(pA), calldataload(add(pA, 32)), mload(add(pMem, pV1))) + g1_mulAccC(p, calldataload(pB), calldataload(add(pB, 32)), mload(add(pMem, pV2))) + g1_mulAccC(p, calldataload(pC), calldataload(add(pC, 32)), mload(add(pMem, pV3))) + g1_mulAccC(p, S1x, S1y, mload(add(pMem, pV4))) + g1_mulAccC(p, S2x, S2y, mload(add(pMem, pV5))) + } + + function calculateE(pMem) { + let s := mod(sub(q, mload(add(pMem, pEval_r0))), q) + + s := addmod(s, mulmod(calldataload(pEval_a), mload(add(pMem, pV1)), q), q) + s := addmod(s, mulmod(calldataload(pEval_b), mload(add(pMem, pV2)), q), q) + s := addmod(s, mulmod(calldataload(pEval_c), mload(add(pMem, pV3)), q), q) + s := addmod(s, mulmod(calldataload(pEval_s1), mload(add(pMem, pV4)), q), q) + s := addmod(s, mulmod(calldataload(pEval_s2), mload(add(pMem, pV5)), q), q) + s := addmod(s, mulmod(calldataload(pEval_zw), mload(add(pMem, pU)), q), q) + + g1_mulSetC(add(pMem, pE), G1x, G1y, s) + } + + function checkPairing(pMem) -> isOk { + let mIn := mload(0x40) + mstore(0x40, add(mIn, 576)) // [0..383] = pairing data, [384..447] = pWxi, [448..512] = pWxiw + + let _pWxi := add(mIn, 384) + let _pWxiw := add(mIn, 448) + let _aux := add(mIn, 512) + + g1_calldataSet(_pWxi, pWxi) + g1_calldataSet(_pWxiw, pWxiw) + + // A1 + g1_mulSet(mIn, _pWxiw, mload(add(pMem, pU))) + g1_acc(mIn, _pWxi) + mstore(add(mIn, 32), mod(sub(qf, mload(add(mIn, 32))), qf)) + + // [X]_2 + mstore(add(mIn,64), X2x2) + mstore(add(mIn,96), X2x1) + mstore(add(mIn,128), X2y2) + mstore(add(mIn,160), X2y1) + + // B1 + g1_mulSet(add(mIn, 192), _pWxi, mload(add(pMem, pXi))) + + let s := mulmod(mload(add(pMem, pU)), mload(add(pMem, pXi)), q) + s := mulmod(s, w1, q) + g1_mulSet(_aux, _pWxiw, s) + g1_acc(add(mIn, 192), _aux) + g1_acc(add(mIn, 192), add(pMem, pF)) + mstore(add(pMem, add(pE, 32)), mod(sub(qf, mload(add(pMem, add(pE, 32)))), qf)) + g1_acc(add(mIn, 192), add(pMem, pE)) + + // [1]_2 + mstore(add(mIn,256), G2x2) + mstore(add(mIn,288), G2x1) + mstore(add(mIn,320), G2y2) + mstore(add(mIn,352), G2y1) + + let success := staticcall(sub(gas(), 2000), 8, mIn, 384, mIn, 0x20) + + isOk := and(success, mload(mIn)) + } + + let pMem := mload(0x40) + mstore(0x40, add(pMem, lastMem)) + + checkInput() + calculateChallenges(pMem, _pubSignals) + calculateLagrange(pMem) + calculatePI(pMem, _pubSignals) + calculateR0(pMem) + calculateD(pMem) + calculateF(pMem) + calculateE(pMem) + let isValid := checkPairing(pMem) + + mstore(0x40, sub(pMem, lastMem)) + mstore(0, isValid) + return(0,0x20) + } + + } +} \ No newline at end of file From 710f8ea3d3f214e5c58f9b91a8d92b4d32755ea0 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Sun, 14 Apr 2024 17:53:23 +0100 Subject: [PATCH 06/12] Save groth16 inputs --- .../hashchecker/scripts/groth16/groth16_zkproof.ts | 6 ++++++ .../templates/hashchecker/scripts/plonk/plonk_zkproof.ts | 4 ++-- .../src/templates/rln/scripts/groth16/groth16_zkproof.ts | 9 +++++++++ .../src/templates/rln/scripts/plonk/plonk_zkproof.ts | 3 ++- .../semaphore/scripts/groth16/groth16_zkproof.ts | 6 ++++++ .../templates/semaphore/scripts/plonk/plonk_zkproof.ts | 6 +++--- 6 files changed, 28 insertions(+), 6 deletions(-) diff --git a/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_zkproof.ts b/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_zkproof.ts index e4be9baa1c..daea2ed230 100644 --- a/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_zkproof.ts +++ b/libs/remix-ws-templates/src/templates/hashchecker/scripts/groth16/groth16_zkproof.ts @@ -64,6 +64,12 @@ const logger = { const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) await remix.call('fileManager', 'writeFile', './zk/build/groth16/zk_verifier.sol', solidityContract) + await remix.call('fileManager', 'writeFile', 'zk/build/groth16/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) { console.error(e.message) } diff --git a/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_zkproof.ts b/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_zkproof.ts index 730c98135a..31b2b8a1f9 100644 --- a/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_zkproof.ts +++ b/libs/remix-ws-templates/src/templates/hashchecker/scripts/plonk/plonk_zkproof.ts @@ -56,7 +56,6 @@ const logger = { await remix.call('fileManager', 'writeFile', 'zk/build/plonk/zk_verifier.sol', solidityContract) await remix.call('fileManager', 'writeFile', 'zk/build/plonk/input.json', JSON.stringify({ - _pubSignals: publicSignals, _proof: [ ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.A[0]).toHexString(), 32), ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.A[1]).toHexString(), 32), @@ -82,7 +81,8 @@ const logger = { 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)) console.log('proof done.') diff --git a/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_zkproof.ts b/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_zkproof.ts index 3c3fe6b20e..f8cef8c28e 100644 --- a/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_zkproof.ts +++ b/libs/remix-ws-templates/src/templates/rln/scripts/groth16/groth16_zkproof.ts @@ -54,6 +54,15 @@ async function prove (signals, wasm, wtns, r1cs, zkey_final, vKey) { const verified = await snarkjs.groth16.verify(vKey, publicSignals, proof, logger); console.log('zk proof validity', verified); + + await remix.call('fileManager', 'writeFile', `zk/build/groth16/input-${Date.now()}.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)) + + console.log('proof done.') return { proof, x: publicSignals[3], diff --git a/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_zkproof.ts b/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_zkproof.ts index e6010af4dc..09b0a885f8 100644 --- a/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_zkproof.ts +++ b/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_zkproof.ts @@ -108,9 +108,10 @@ async function prove (signals, wasm, wtns, r1cs, zkey_final, vKey) { const zkey_final = { type: "mem", + // @ts-ignore data: new Uint8Array(await remix.call('fileManager', 'readFile', './zk/keys/plonk/zkey_final.txt', { encoding: null })) } - const wtns = { type: "mem" }; + const wtns = { type: "mem" }; const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/plonk/verification_key.json')) diff --git a/libs/remix-ws-templates/src/templates/semaphore/scripts/groth16/groth16_zkproof.ts b/libs/remix-ws-templates/src/templates/semaphore/scripts/groth16/groth16_zkproof.ts index 0ac12e2d7b..d477342506 100644 --- a/libs/remix-ws-templates/src/templates/semaphore/scripts/groth16/groth16_zkproof.ts +++ b/libs/remix-ws-templates/src/templates/semaphore/scripts/groth16/groth16_zkproof.ts @@ -102,6 +102,12 @@ function hash(message: any): bigint { const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) await remix.call('fileManager', 'writeFile', './zk/build/groth16/zk_verifier.sol', solidityContract) + await remix.call('fileManager', 'writeFile', 'zk/build/groth16/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) { console.error(e.message) } diff --git a/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_zkproof.ts b/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_zkproof.ts index 224c866018..6472567bb1 100644 --- a/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_zkproof.ts +++ b/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_zkproof.ts @@ -103,7 +103,6 @@ function hash(message: any): bigint { await remix.call('fileManager', 'writeFile', './zk/build/plonk/zk_verifier.sol', solidityContract) await remix.call('fileManager', 'writeFile', 'zk/build/plonk/input.json', JSON.stringify({ - _pubSignals: publicSignals, _proof: [ ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.A[0]).toHexString(), 32), ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.A[1]).toHexString(), 32), @@ -129,11 +128,12 @@ function hash(message: any): bigint { 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)) console.log('proof done.') } catch (e) { console.error(e.message) } -})() \ No newline at end of file +})() From 05957b79e2a52c874859268553d37dd82d34178d Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Sun, 14 Apr 2024 19:07:06 +0100 Subject: [PATCH 07/12] Update circom e2e --- apps/remix-ide-e2e/src/tests/circom.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/remix-ide-e2e/src/tests/circom.test.ts b/apps/remix-ide-e2e/src/tests/circom.test.ts index 0f7d9e87ec..f522d63e51 100644 --- a/apps/remix-ide-e2e/src/tests/circom.test.ts +++ b/apps/remix-ide-e2e/src/tests/circom.test.ts @@ -23,9 +23,15 @@ module.exports = { .waitForElementVisible('*[data-id="treeViewLitreeViewItemcircuits"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcircuits/semaphore.circom"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/run_setup.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/groth16"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/groth16/groth16_trusted_setup.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/groth16/groth16_zkproof.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/plonk"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/plonk/plonk_trusted_setup.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/plonk/plonk_zkproof.ts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtemplates"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtemplates/groth16_verifier.sol.ejs"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtemplates/plonk_verifier.sol.ejs"]') }, 'Should compile a simple circuit using editor play button #group1': function (browser: NightwatchBrowser) { browser From a6715d47a98b7f1b2388caa76e89d49363d937d1 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Sun, 14 Apr 2024 19:18:45 +0100 Subject: [PATCH 08/12] Update workspace e2e --- apps/remix-ide-e2e/src/tests/workspace.test.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index 11a8eaa7a1..37a7dc1d0b 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -402,9 +402,15 @@ module.exports = { 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/run_setup.ts"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/run_verification.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/groth16"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/groth16/groth16_trusted_setup.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/groth16/groth16_zkproof.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/plonk"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/plonk/plonk_trusted_setup.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/plonk/plonk_zkproof.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtemplates"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtemplates/groth16_verifier.sol.ejs"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtemplates/plonk_verifier.sol.ejs"]') .click('*[data-id="treeViewLitreeViewItemtemplates/groth16_verifier.sol.ejs"]') .getEditorValue((content) => { browser.assert.ok(content.indexOf(`contract Groth16Verifier {`) !== -1, From 67b1c2231204b8dc0b547da6d45211cb9be10136 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Sun, 14 Apr 2024 19:43:26 +0100 Subject: [PATCH 09/12] Restore simple.circom --- .../src/templates/semaphore/circuits/simple.circom | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 libs/remix-ws-templates/src/templates/semaphore/circuits/simple.circom diff --git a/libs/remix-ws-templates/src/templates/semaphore/circuits/simple.circom b/libs/remix-ws-templates/src/templates/semaphore/circuits/simple.circom new file mode 100644 index 0000000000..9a2120df7a --- /dev/null +++ b/libs/remix-ws-templates/src/templates/semaphore/circuits/simple.circom @@ -0,0 +1,11 @@ +pragma circom 2.0.0; + +template Multiplier2() { + signal input a; + signal input b; + signal output c; + c <== a*b; + } + + component main = Multiplier2(); + \ No newline at end of file From 3552c708820f40f7acf0c88f89777eba5a3a13af Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Sun, 14 Apr 2024 20:06:15 +0100 Subject: [PATCH 10/12] Export simple circuit to template --- libs/remix-ws-templates/src/templates/semaphore/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/remix-ws-templates/src/templates/semaphore/index.ts b/libs/remix-ws-templates/src/templates/semaphore/index.ts index 37f8484829..54a8012e0a 100644 --- a/libs/remix-ws-templates/src/templates/semaphore/index.ts +++ b/libs/remix-ws-templates/src/templates/semaphore/index.ts @@ -5,6 +5,8 @@ export default async () => { // @ts-ignore 'circuits/tree.circom': (await import('!!raw-loader!./circuits/tree.circom')).default, // @ts-ignore + 'circuits/simple.circom': (await import('!!raw-loader!./circuits/simple.circom')).default, + // @ts-ignore 'scripts/groth16/groth16_trusted_setup.ts': (await import('!!raw-loader!./scripts/groth16/groth16_trusted_setup.ts')).default, // @ts-ignore 'scripts/groth16/groth16_zkproof.ts': (await import('!!raw-loader!./scripts/groth16/groth16_zkproof.ts')).default, From d2244e0447de8da3d16910fc7e44af24a5d0757e Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Mon, 15 Apr 2024 14:43:40 +0100 Subject: [PATCH 11/12] Update IPFS urls --- libs/remix-ws-templates/src/templates/rln/index.ts | 2 +- .../src/templates/rln/scripts/plonk/plonk_trusted_setup.ts | 2 +- .../templates/semaphore/scripts/plonk/plonk_trusted_setup.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/remix-ws-templates/src/templates/rln/index.ts b/libs/remix-ws-templates/src/templates/rln/index.ts index aeb3b9be59..30b76d4933 100644 --- a/libs/remix-ws-templates/src/templates/rln/index.ts +++ b/libs/remix-ws-templates/src/templates/rln/index.ts @@ -17,7 +17,7 @@ export default async () => { // @ts-ignore 'templates/groth16_verifier.sol.ejs': (await import('!!raw-loader!./templates/groth16_verifier.sol.ejs')).default, // @ts-ignore - 'templates/plonk_verifier.sol.ejs': (await import('!!raw-loader!./templates/groth16_verifier.sol.ejs')).default, + 'templates/plonk_verifier.sol.ejs': (await import('!!raw-loader!./templates/plonk_verifier.sol.ejs')).default, // @ts-ignore 'LICENSE-APACHE': (await import('!!raw-loader!./LICENSE-APACHE')).default, // @ts-ignore diff --git a/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_trusted_setup.ts b/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_trusted_setup.ts index 2cff9dabae..8f146a1611 100644 --- a/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_trusted_setup.ts +++ b/libs/remix-ws-templates/src/templates/rln/scripts/plonk/plonk_trusted_setup.ts @@ -6,7 +6,7 @@ const snarkjs = require('snarkjs'); // @ts-ignore await remix.call('circuit-compiler', 'generateR1cs', 'circuits/rln.circom'); - const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW"; + const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmciCq5JcZQyTLvC9GRanrLBi82ZmSriq1Fr5zANkGHebf"; // @ts-ignore const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.r1cs', { encoding: null }); // @ts-ignore diff --git a/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_trusted_setup.ts b/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_trusted_setup.ts index f23770246b..8ebdab1e71 100644 --- a/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_trusted_setup.ts +++ b/libs/remix-ws-templates/src/templates/semaphore/scripts/plonk/plonk_trusted_setup.ts @@ -11,7 +11,7 @@ const logger = { // @ts-ignore await remix.call('circuit-compiler', 'generateR1cs', 'circuits/semaphore.circom'); - const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW"; + const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmciCq5JcZQyTLvC9GRanrLBi82ZmSriq1Fr5zANkGHebf"; // @ts-ignore const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', { encoding: null }); // @ts-ignore From 8e5834875168e5a1d88021f52c08dbf55dbb3111 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Tue, 16 Apr 2024 12:41:28 +0100 Subject: [PATCH 12/12] Add e2e for plonk and groth16 scripts --- apps/remix-ide-e2e/src/tests/circom.test.ts | 99 +++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/apps/remix-ide-e2e/src/tests/circom.test.ts b/apps/remix-ide-e2e/src/tests/circom.test.ts index f522d63e51..2f64844faa 100644 --- a/apps/remix-ide-e2e/src/tests/circom.test.ts +++ b/apps/remix-ide-e2e/src/tests/circom.test.ts @@ -149,6 +149,105 @@ module.exports = { .frameParent() .clickLaunchIcon('filePanel') .waitForElementPresent('[data-id="treeViewLitreeViewItemcircuits/.bin/simple.wasm"]') + }, + 'Should create a new workspace using hash checker template #group5 #group6': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('filePanel') + .click('*[data-id="workspacesMenuDropdown"]') + .click('*[data-id="workspacecreate"]') + .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') + .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') + .click('select[id="wstemplate"]') + .click('select[id="wstemplate"] option[value=hashchecker]') + .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') + .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) + .pause(100) + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcircuits"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcircuits/calculate_hash.circom"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/groth16"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/groth16/groth16_trusted_setup.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/groth16/groth16_zkproof.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/plonk"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/plonk/plonk_trusted_setup.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/plonk/plonk_zkproof.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtemplates"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtemplates/groth16_verifier.sol.ejs"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtemplates/plonk_verifier.sol.ejs"]') + }, + 'Should run groth16 trusted setup script for hash checker #group5': function (browser: NightwatchBrowser) { + browser + .click('[data-id="treeViewLitreeViewItemscripts/groth16/groth16_trusted_setup.ts"]') + .waitForElementPresent('[data-path="Hash Checker - 1/scripts/groth16/groth16_trusted_setup.ts"]') + .waitForElementVisible('[data-path="Hash Checker - 1/scripts/groth16/groth16_trusted_setup.ts"]') + .waitForElementPresent('[data-id="verticalIconsKindcircuit-compiler"]') + .waitForElementVisible('[data-id="verticalIconsKindcircuit-compiler"]') + .click('[data-id="play-editor"]') + .pause(2000) + .journalLastChildIncludes('Generating R1CS for circuits/calculate_hash.circom') + .pause(5000) + .journalLastChildIncludes('Everything went okay') + .journalLastChildIncludes('newZkey') + .pause(25000) + .journalLastChildIncludes('setup done.') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemzk/keys/groth16/verification_key.json"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemzk/keys/groth16/zkey_final.txt"]') + }, + 'Should run groth16 zkproof script for hash checker #group5': function (browser: NightwatchBrowser) { + browser + .click('[data-id="treeViewLitreeViewItemscripts/groth16/groth16_zkproof.ts"]') + .waitForElementPresent('[data-path="Hash Checker - 1/scripts/groth16/groth16_zkproof.ts"]') + .waitForElementVisible('[data-path="Hash Checker - 1/scripts/groth16/groth16_zkproof.ts"]') + .waitForElementPresent('[data-id="verticalIconsKindcircuit-compiler"]') + .waitForElementVisible('[data-id="verticalIconsKindcircuit-compiler"]') + .click('[data-id="play-editor"]') + .pause(2000) + .journalLastChildIncludes('Compiling circuits/calculate_hash.circom') + .pause(5000) + .journalLastChildIncludes('Everything went okay') + .journalLastChildIncludes('WITNESS CHECKING STARTED') + .pause(5000) + .journalLastChildIncludes('WITNESS CHECKING FINISHED SUCCESSFULLY') + .pause(2000) + .journalLastChildIncludes('zk proof validity') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemzk/build/groth16/zk_verifier.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemzk/build/groth16/input.json"]') + }, + 'Should run plonk trusted setup script for hash checker #group6': function (browser: NightwatchBrowser) { + browser + .click('[data-id="treeViewLitreeViewItemscripts/plonk/plonk_trusted_setup.ts"]') + .waitForElementPresent('[data-path="Hash Checker - 1/scripts/plonk/plonk_trusted_setup.ts"]') + .waitForElementVisible('[data-path="Hash Checker - 1/scripts/plonk/plonk_trusted_setup.ts"]') + .waitForElementPresent('[data-id="verticalIconsKindcircuit-compiler"]') + .waitForElementVisible('[data-id="verticalIconsKindcircuit-compiler"]') + .click('[data-id="play-editor"]') + .pause(2000) + .journalLastChildIncludes('Generating R1CS for circuits/calculate_hash.circom') + .pause(5000) + .journalLastChildIncludes('Everything went okay') + .journalLastChildIncludes('plonk setup') + .pause(10000) + .journalLastChildIncludes('setup done') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemzk/keys/plonk/verification_key.json"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemzk/keys/plonk/zkey_final.txt"]') + }, + 'Should run plonk zkproof script for hash checker #group6': function (browser: NightwatchBrowser) { + browser + .click('[data-id="treeViewLitreeViewItemscripts/plonk/plonk_zkproof.ts"]') + .waitForElementPresent('[data-path="Hash Checker - 1/scripts/plonk/plonk_zkproof.ts"]') + .waitForElementVisible('[data-path="Hash Checker - 1/scripts/plonk/plonk_zkproof.ts"]') + .waitForElementPresent('[data-id="verticalIconsKindcircuit-compiler"]') + .waitForElementVisible('[data-id="verticalIconsKindcircuit-compiler"]') + .click('[data-id="play-editor"]') + .pause(2000) + .journalLastChildIncludes('Compiling circuits/calculate_hash.circom') + .pause(5000) + .journalLastChildIncludes('Everything went okay') + .pause(5000) + .journalLastChildIncludes('zk proof validity') + .journalLastChildIncludes('proof done.') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemzk/build/plonk/zk_verifier.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemzk/build/plonk/input.json"]') } }