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