├── .github
└── CODEOWNERS
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc.json
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── TODO.md
├── circuits
├── Hasher.circom
├── Multiplier.circom
├── Pubkey.circom
├── Pythagoras.circom
└── templates
│ ├── BabyJubJub.circom
│ └── Square.circom
├── contracts
└── verifiers
│ ├── .gitkeep
│ ├── HasherVerifier.sol
│ ├── MultiplierVerifier.sol
│ ├── PubkeyVerifier.sol
│ └── PythagorasVerifier.sol
├── hardhat.config.ts
├── images
└── arrival_louis_has_weapon.jpeg
├── index.d.ts
├── package-lock.json
├── package.json
├── scripts
├── bash
│ ├── 0_install_circom.sh
│ ├── 1_plonk_setup.sh
│ └── 2_build_circuits.sh
└── deploy.ts
├── setup
├── hermez
│ ├── .gitkeep
│ └── powersOfTau_hez_final.ptau
└── local
│ ├── .gitkeep
│ └── powersOfTau_local_final.ptau
├── test
├── hasher.ts
└── pubkey.ts
├── tsconfig.json
└── utils
├── Circomlib.ts
└── Circuit.ts
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @infinitywarg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env
3 | coverage
4 | coverage.json
5 | typechain
6 | typechain-types
7 |
8 | # Hardhat Files
9 | cache
10 | artifacts
11 |
12 | # Circom Files
13 | build
14 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | artifacts
3 | cache
4 | coverage*
5 | gasReporterOutput.json
6 | package.json
7 | img
8 | .env
9 | .*
10 | README.md
11 | coverage.json
12 | deployments
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "overrides": [
3 | {
4 | "files": "*.sol",
5 | "options": {
6 | "printWidth": 120,
7 | "tabWidth": 4,
8 | "useTabs": false,
9 | "singleQuote": false,
10 | "bracketSpacing": false,
11 | "explicitTypes": "always"
12 | }
13 | },
14 | {
15 | "files": ["*.ts"],
16 | "options": {
17 | "trailingComma": "es5",
18 | "printWidth": 120,
19 | "tabWidth": 4,
20 | "useTabs": true,
21 | "singleQuote": false,
22 | "semi": true,
23 | "bracketSpacing": true,
24 | "arrowParens": "always"
25 | }
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finitology/hardhat-circom-starter/6ab36475770162951c576b6df52c59d2783277b4/CONTRIBUTING.md
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 infinitywarg
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZK-SNARKS with Circom Starter Kit
7 |
8 |
9 |
10 | []()
11 | [](https://github.com/infinitywarg/zksnark-starter/issues)
12 | [](https://github.com/infinitywarg/zksnark-starter/pulls)
13 | [](/LICENSE)
14 |
15 |
16 |
17 | ---
18 |
19 | 💡 Hardhat starter kit to build zero knowledge proof applications using circom and snark.js 💡
20 |
21 | ## 📝 Table of Contents
22 | - [About](#about)
23 | - [Getting Started](#getting_started)
24 | - [What are zkSNARKS?](#zksnarks)
25 | - [What are trusted setups in zkSNARKS?](#trusted_setups)
26 | - [Usage](#usage)
27 | - [npm run install](#install)
28 | - [npm run setup](#setup)
29 | - [npm run build](#build)
30 | - [npm run test:onchain](#test_onchain)
31 | - [npm run test:offchain](#test_offchain)
32 | - [Built Using](#built_using)
33 | - [TODO](../TODO.md)
34 | - [Contributing](../CONTRIBUTING.md)
35 | - [Authors](#authors)
36 | - [Disclaimer](#disclaimer)
37 |
38 | ## About
39 | Write about 1-2 paragraphs describing the purpose of your project.
40 |
41 | ## Getting Started
42 |
43 |
44 | ### What are zkSNARKS?
45 |
46 | ### What are trusted setups in zkSNARKS?
47 |
48 |
49 | ## Usage
50 | Add notes about how to use the starter kit.
51 |
52 | ### 🧐 npm run install
53 |
54 | ### 🏁 npm run setup
55 |
56 | ### 🔧 npm run build
57 |
58 | ### 🚀 npm run test:onchain
59 |
60 | ### 🎈 npm run test:offchain
61 |
62 |
63 | ## ⛏️ Built Using
64 | - [hardhat](https://hardhat.org/tutorial) - Smart Contracts Development Framework
65 | - [circom](https://docs.circom.io/) - Domain Specific Language for writing snark circuits and Circuit Compiler
66 | - [snarkjs](https://github.com/iden3/snarkjs) - JS library for building circuits, generating & verifying proofs
67 | - [npm](https://www.npmjs.com/package/snarkjs) - Node.js package manager
68 |
69 | ## ✍️ Authors
70 | - [@infinitywarg](https://github.com/infinitywarg) - Idea & Initial work
71 |
72 | See also the list of [contributors](https://github.com/infinitywarg/zksnark-starter/contributors) who participated in this project.
73 |
74 | ## Disclaimer
75 | - zk-snarks starter is under active development. If you have any improvements and suggestions please file an [issue](https://github.com/infinitywarg/zksnark-starter/issues/new).
76 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finitology/hardhat-circom-starter/6ab36475770162951c576b6df52c59d2783277b4/TODO.md
--------------------------------------------------------------------------------
/circuits/Hasher.circom:
--------------------------------------------------------------------------------
1 | pragma circom 2.2.0;
2 |
3 | include "../node_modules/circomlib/circuits/poseidon.circom";
4 |
5 | // I know x0 & x1 such that their hash is public y
6 | // without revealing x0 & x1
7 |
8 | template Hasher() {
9 | signal input x[2];
10 | signal output y;
11 | y <== Poseidon(2)(x);
12 | }
13 |
14 | component main = Hasher();
--------------------------------------------------------------------------------
/circuits/Multiplier.circom:
--------------------------------------------------------------------------------
1 | pragma circom 2.2.0;
2 |
3 | // I know a, b and c such that a*b=c
4 | // withut revealing a, b or c
5 |
6 | template Multiplier() {
7 | signal input a;
8 | signal input b;
9 | signal output c;
10 | c <== a * b;
11 | }
12 |
13 | component main = Multiplier();
14 |
--------------------------------------------------------------------------------
/circuits/Pubkey.circom:
--------------------------------------------------------------------------------
1 | pragma circom 2.2.0;
2 |
3 | include "./templates/BabyJubJub.circom";
4 |
5 | // I know private key sk such that corresponding public key is pk
6 | // without revealing privKey
7 |
8 | template Pubkey() {
9 | signal input sk;
10 | signal output pk[2];
11 | pk <== PointMulBase()(sk);
12 | }
13 |
14 | component main = Pubkey();
--------------------------------------------------------------------------------
/circuits/Pythagoras.circom:
--------------------------------------------------------------------------------
1 | pragma circom 2.2.0;
2 |
3 | include "./templates/Square.circom";
4 |
5 | // I know a, b and c such that a square * b square = c square
6 | // withut revealing a and b
7 | // demonstrates composable circuit templates
8 |
9 | template Pythagoras() {
10 | signal input a;
11 | signal input b;
12 | signal input c2;
13 |
14 | signal a2 <== Square()(a);
15 | signal b2 <== Square()(b);
16 | c2 === a2 + b2;
17 | }
18 |
19 | component main {public [c2]} = Pythagoras();
20 |
--------------------------------------------------------------------------------
/circuits/templates/BabyJubJub.circom:
--------------------------------------------------------------------------------
1 | pragma circom 2.2.0;
2 |
3 | include "../../node_modules/circomlib/circuits/bitify.circom";
4 | include "../../node_modules/circomlib/circuits/escalarmulany.circom";
5 | include "../../node_modules/circomlib/circuits/escalarmulfix.circom";
6 |
7 | // templates for curve operations on BabyJubJub twisted edwards curve
8 | // BabyJubJub is constructed on the same scalar field as that of pairing friendly zk curve BN254
9 |
10 | template PointCheck() {
11 | signal input point[2];
12 |
13 | signal x2;
14 | signal y2;
15 |
16 | var a = 168700;
17 | var d = 168696;
18 |
19 | x2 <== point[0]*point[0];
20 | y2 <== point[1]*point[1];
21 |
22 | a*x2 + y2 === 1 + d*x2*y2;
23 | }
24 |
25 | template PointAdd() {
26 | signal input pointP[2];
27 | signal input pointQ[2];
28 | signal output outpoint[2];
29 |
30 | signal beta;
31 | signal gamma;
32 | signal delta;
33 | signal tau;
34 |
35 | var a = 168700;
36 | var d = 168696;
37 |
38 | beta <== pointP[0]*pointQ[1];
39 | gamma <== pointP[1]*pointQ[0];
40 | delta <== (-a*pointP[0]+pointP[1])*(pointQ[0] + pointQ[1]);
41 | tau <== beta * gamma;
42 |
43 | outpoint[0] <-- (beta + gamma) / (1+ d*tau);
44 | (1+ d*tau) * outpoint[0] === (beta + gamma);
45 |
46 | outpoint[1] <-- (delta + a*beta - gamma) / (1-d*tau);
47 | (1-d*tau)*outpoint[1] === (delta + a*beta - gamma);
48 | }
49 |
50 |
51 | template PointMulBase() {
52 | signal input scalar;
53 | signal output outpoint[2];
54 |
55 | // babyjub base point, ref: https://eips.ethereum.org/EIPS/eip-2494
56 | var BASE8[2] = [
57 | 5299619240641551281634865583518297030282874472190772894086521144482721001553,
58 | 16950150798460657717958625567821834550301663161624707787222815936182638968203
59 | ];
60 | var i;
61 |
62 | component scalar_bits = Num2Bits(253);
63 | scalar_bits.in <== scalar;
64 |
65 | component mulFix = EscalarMulFix(253, BASE8);
66 | for (i=0; i<253; i++) {
67 | mulFix.e[i] <== scalar_bits.out[i];
68 | }
69 | outpoint[0] <== mulFix.out[0];
70 | outpoint[1] <== mulFix.out[1];
71 | }
72 |
73 | template PointMulAny() {
74 | signal input scalar;
75 | signal input inpoint[2];
76 | signal output outpoint[2];
77 |
78 | var i;
79 |
80 | component scalar_bits = Num2Bits(253);
81 | scalar_bits.in <== scalar;
82 |
83 | component mulAny = EscalarMulAny(253);
84 | for (i = 0; i < 253; i ++) {
85 | mulAny.e[i] <== scalar_bits.out[i];
86 | }
87 | mulAny.p[0] <== inpoint[0];
88 | mulAny.p[1] <== inpoint[1];
89 |
90 | outpoint[0] <== mulAny.out[0];
91 | outpoint[1] <== mulAny.out[1];
92 | }
--------------------------------------------------------------------------------
/circuits/templates/Square.circom:
--------------------------------------------------------------------------------
1 | pragma circom 2.2.0;
2 |
3 | template Square() {
4 | signal input in;
5 | signal output out;
6 | out <== in * in;
7 | }
--------------------------------------------------------------------------------
/contracts/verifiers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finitology/hardhat-circom-starter/6ab36475770162951c576b6df52c59d2783277b4/contracts/verifiers/.gitkeep
--------------------------------------------------------------------------------
/contracts/verifiers/HasherVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | /*
3 | Copyright 2021 0KIMS association.
4 |
5 | This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
6 |
7 | snarkJS is a free software: you can redistribute it and/or modify it
8 | under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | snarkJS is distributed in the hope that it will be useful, but WITHOUT
13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 | License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with snarkJS. If not, see .
19 | */
20 |
21 |
22 | pragma solidity >=0.7.0 <0.9.0;
23 |
24 | contract HasherVerifier {
25 | // Omega
26 | uint256 constant w1 = 3161067157621608152362653341354432744960400845131437947728257924963983317266;
27 | // Scalar field size
28 | uint256 constant q = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
29 | // Base field size
30 | uint256 constant qf = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
31 |
32 | // [1]_1
33 | uint256 constant G1x = 1;
34 | uint256 constant G1y = 2;
35 | // [1]_2
36 | uint256 constant G2x1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
37 | uint256 constant G2x2 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
38 | uint256 constant G2y1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
39 | uint256 constant G2y2 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
40 |
41 | // Verification Key data
42 | uint32 constant n = 1024;
43 | uint16 constant nPublic = 1;
44 | uint16 constant nLagrange = 1;
45 |
46 | uint256 constant Qmx = 11686796019388672167368420428277750457722397909975389756051220981035901561192;
47 | uint256 constant Qmy = 11838790121246330883384148737451348245270401140839345604198739837267849034278;
48 | uint256 constant Qlx = 310137838149165845666396324544758440470685889401217850115576739174248280405;
49 | uint256 constant Qly = 475041219513741459304817744558821824411568465291313491737303693586825986371;
50 | uint256 constant Qrx = 14555408616543257755547711050376138569890634125159230692697063423113130650267;
51 | uint256 constant Qry = 3980455840367248486047310141246391460778997495520849944572400061836749041835;
52 | uint256 constant Qox = 12589629160569551945599510764665406449496375203250020144602852365499754463437;
53 | uint256 constant Qoy = 15658278828835223326455254187174174525235376976386900959729833634394206700733;
54 | uint256 constant Qcx = 15373329647991826868419076995789334569541505777916148287216979162126905140102;
55 | uint256 constant Qcy = 19665530159316943321956849128711879737033171916347731907098830371142257325435;
56 | uint256 constant S1x = 2985267348659398049745307874015813428809417913979653687415281521674597605357;
57 | uint256 constant S1y = 12532686939328436824755498388100694055278604155642809152468088674919514748054;
58 | uint256 constant S2x = 7369956234360318058494477601690412195381448860983564047748562636525252110950;
59 | uint256 constant S2y = 19500496223568755775918599190771690446620095244677073576570476493084225302833;
60 | uint256 constant S3x = 11403847334509885939944023601892910177484887872352063280554864752305016585470;
61 | uint256 constant S3y = 18719967396960240308784317188647458333240725870608903035455602993527815723269;
62 | uint256 constant k1 = 2;
63 | uint256 constant k2 = 3;
64 | uint256 constant X2x1 = 21831381940315734285607113342023901060522397560371972897001948545212302161822;
65 | uint256 constant X2x2 = 17231025384763736816414546592865244497437017442647097510447326538965263639101;
66 | uint256 constant X2y1 = 2388026358213174446665280700919698872609886601280537296205114254867301080648;
67 | uint256 constant X2y2 = 11507326595632554467052522095592665270651932854513688777769618397986436103170;
68 |
69 | // Proof calldata
70 | // Byte offset of every parameter of the calldata
71 | // Polynomial commitments
72 | uint16 constant pA = 4 + 0;
73 | uint16 constant pB = 4 + 64;
74 | uint16 constant pC = 4 + 128;
75 | uint16 constant pZ = 4 + 192;
76 | uint16 constant pT1 = 4 + 256;
77 | uint16 constant pT2 = 4 + 320;
78 | uint16 constant pT3 = 4 + 384;
79 | uint16 constant pWxi = 4 + 448;
80 | uint16 constant pWxiw = 4 + 512;
81 | // Opening evaluations
82 | uint16 constant pEval_a = 4 + 576;
83 | uint16 constant pEval_b = 4 + 608;
84 | uint16 constant pEval_c = 4 + 640;
85 | uint16 constant pEval_s1 = 4 + 672;
86 | uint16 constant pEval_s2 = 4 + 704;
87 | uint16 constant pEval_zw = 4 + 736;
88 |
89 | // Memory data
90 | // Challenges
91 | uint16 constant pAlpha = 0;
92 | uint16 constant pBeta = 32;
93 | uint16 constant pGamma = 64;
94 | uint16 constant pXi = 96;
95 | uint16 constant pXin = 128;
96 | uint16 constant pBetaXi = 160;
97 | uint16 constant pV1 = 192;
98 | uint16 constant pV2 = 224;
99 | uint16 constant pV3 = 256;
100 | uint16 constant pV4 = 288;
101 | uint16 constant pV5 = 320;
102 | uint16 constant pU = 352;
103 |
104 | uint16 constant pPI = 384;
105 | uint16 constant pEval_r0 = 416;
106 | uint16 constant pD = 448;
107 | uint16 constant pF = 512;
108 | uint16 constant pE = 576;
109 | uint16 constant pTmp = 640;
110 | uint16 constant pAlpha2 = 704;
111 | uint16 constant pZh = 736;
112 | uint16 constant pZhInv = 768;
113 |
114 |
115 | uint16 constant pEval_l1 = 800;
116 |
117 |
118 |
119 | uint16 constant lastMem = 832;
120 |
121 | function verifyProof(uint256[24] calldata _proof, uint256[1] calldata _pubSignals) public view returns (bool) {
122 | assembly {
123 | /////////
124 | // Computes the inverse using the extended euclidean algorithm
125 | /////////
126 | function inverse(a, q) -> inv {
127 | let t := 0
128 | let newt := 1
129 | let r := q
130 | let newr := a
131 | let quotient
132 | let aux
133 |
134 | for { } newr { } {
135 | quotient := sdiv(r, newr)
136 | aux := sub(t, mul(quotient, newt))
137 | t:= newt
138 | newt:= aux
139 |
140 | aux := sub(r,mul(quotient, newr))
141 | r := newr
142 | newr := aux
143 | }
144 |
145 | if gt(r, 1) { revert(0,0) }
146 | if slt(t, 0) { t:= add(t, q) }
147 |
148 | inv := t
149 | }
150 |
151 | ///////
152 | // Computes the inverse of an array of values
153 | // See https://vitalik.ca/general/2018/07/21/starks_part_3.html in section where explain fields operations
154 | //////
155 | function inverseArray(pVals, n) {
156 |
157 | let pAux := mload(0x40) // Point to the next free position
158 | let pIn := pVals
159 | let lastPIn := add(pVals, mul(n, 32)) // Read n elements
160 | let acc := mload(pIn) // Read the first element
161 | pIn := add(pIn, 32) // Point to the second element
162 | let inv
163 |
164 |
165 | for { } lt(pIn, lastPIn) {
166 | pAux := add(pAux, 32)
167 | pIn := add(pIn, 32)
168 | }
169 | {
170 | mstore(pAux, acc)
171 | acc := mulmod(acc, mload(pIn), q)
172 | }
173 | acc := inverse(acc, q)
174 |
175 | // At this point pAux pint to the next free position we subtract 1 to point to the last used
176 | pAux := sub(pAux, 32)
177 | // pIn points to the n+1 element, we subtract to point to n
178 | pIn := sub(pIn, 32)
179 | lastPIn := pVals // We don't process the first element
180 | for { } gt(pIn, lastPIn) {
181 | pAux := sub(pAux, 32)
182 | pIn := sub(pIn, 32)
183 | }
184 | {
185 | inv := mulmod(acc, mload(pAux), q)
186 | acc := mulmod(acc, mload(pIn), q)
187 | mstore(pIn, inv)
188 | }
189 | // pIn points to first element, we just set it.
190 | mstore(pIn, acc)
191 | }
192 |
193 | function checkField(v) {
194 | if iszero(lt(v, q)) {
195 | mstore(0, 0)
196 | return(0,0x20)
197 | }
198 | }
199 |
200 | function checkInput() {
201 | checkField(calldataload(pEval_a))
202 | checkField(calldataload(pEval_b))
203 | checkField(calldataload(pEval_c))
204 | checkField(calldataload(pEval_s1))
205 | checkField(calldataload(pEval_s2))
206 | checkField(calldataload(pEval_zw))
207 | }
208 |
209 | function calculateChallenges(pMem, pPublic) {
210 | let beta
211 | let aux
212 |
213 | let mIn := mload(0x40) // Pointer to the next free memory position
214 |
215 | // Compute challenge.beta & challenge.gamma
216 | mstore(mIn, Qmx)
217 | mstore(add(mIn, 32), Qmy)
218 | mstore(add(mIn, 64), Qlx)
219 | mstore(add(mIn, 96), Qly)
220 | mstore(add(mIn, 128), Qrx)
221 | mstore(add(mIn, 160), Qry)
222 | mstore(add(mIn, 192), Qox)
223 | mstore(add(mIn, 224), Qoy)
224 | mstore(add(mIn, 256), Qcx)
225 | mstore(add(mIn, 288), Qcy)
226 | mstore(add(mIn, 320), S1x)
227 | mstore(add(mIn, 352), S1y)
228 | mstore(add(mIn, 384), S2x)
229 | mstore(add(mIn, 416), S2y)
230 | mstore(add(mIn, 448), S3x)
231 | mstore(add(mIn, 480), S3y)
232 |
233 |
234 | mstore(add(mIn, 512), calldataload(add(pPublic, 0)))
235 |
236 | mstore(add(mIn, 544 ), calldataload(pA))
237 | mstore(add(mIn, 576 ), calldataload(add(pA, 32)))
238 | mstore(add(mIn, 608 ), calldataload(pB))
239 | mstore(add(mIn, 640 ), calldataload(add(pB, 32)))
240 | mstore(add(mIn, 672 ), calldataload(pC))
241 | mstore(add(mIn, 704 ), calldataload(add(pC, 32)))
242 |
243 | beta := mod(keccak256(mIn, 736), q)
244 | mstore(add(pMem, pBeta), beta)
245 |
246 | // challenges.gamma
247 | mstore(add(pMem, pGamma), mod(keccak256(add(pMem, pBeta), 32), q))
248 |
249 | // challenges.alpha
250 | mstore(mIn, mload(add(pMem, pBeta)))
251 | mstore(add(mIn, 32), mload(add(pMem, pGamma)))
252 | mstore(add(mIn, 64), calldataload(pZ))
253 | mstore(add(mIn, 96), calldataload(add(pZ, 32)))
254 |
255 | aux := mod(keccak256(mIn, 128), q)
256 | mstore(add(pMem, pAlpha), aux)
257 | mstore(add(pMem, pAlpha2), mulmod(aux, aux, q))
258 |
259 | // challenges.xi
260 | mstore(mIn, aux)
261 | mstore(add(mIn, 32), calldataload(pT1))
262 | mstore(add(mIn, 64), calldataload(add(pT1, 32)))
263 | mstore(add(mIn, 96), calldataload(pT2))
264 | mstore(add(mIn, 128), calldataload(add(pT2, 32)))
265 | mstore(add(mIn, 160), calldataload(pT3))
266 | mstore(add(mIn, 192), calldataload(add(pT3, 32)))
267 |
268 | aux := mod(keccak256(mIn, 224), q)
269 | mstore( add(pMem, pXi), aux)
270 |
271 | // challenges.v
272 | mstore(mIn, aux)
273 | mstore(add(mIn, 32), calldataload(pEval_a))
274 | mstore(add(mIn, 64), calldataload(pEval_b))
275 | mstore(add(mIn, 96), calldataload(pEval_c))
276 | mstore(add(mIn, 128), calldataload(pEval_s1))
277 | mstore(add(mIn, 160), calldataload(pEval_s2))
278 | mstore(add(mIn, 192), calldataload(pEval_zw))
279 |
280 | let v1 := mod(keccak256(mIn, 224), q)
281 | mstore(add(pMem, pV1), v1)
282 |
283 | // challenges.beta * challenges.xi
284 | mstore(add(pMem, pBetaXi), mulmod(beta, aux, q))
285 |
286 | // challenges.xi^n
287 |
288 | aux:= mulmod(aux, aux, q)
289 |
290 | aux:= mulmod(aux, aux, q)
291 |
292 | aux:= mulmod(aux, aux, q)
293 |
294 | aux:= mulmod(aux, aux, q)
295 |
296 | aux:= mulmod(aux, aux, q)
297 |
298 | aux:= mulmod(aux, aux, q)
299 |
300 | aux:= mulmod(aux, aux, q)
301 |
302 | aux:= mulmod(aux, aux, q)
303 |
304 | aux:= mulmod(aux, aux, q)
305 |
306 | aux:= mulmod(aux, aux, q)
307 |
308 | mstore(add(pMem, pXin), aux)
309 |
310 | // Zh
311 | aux:= mod(add(sub(aux, 1), q), q)
312 | mstore(add(pMem, pZh), aux)
313 | mstore(add(pMem, pZhInv), aux) // We will invert later together with lagrange pols
314 |
315 | // challenges.v^2, challenges.v^3, challenges.v^4, challenges.v^5
316 | aux := mulmod(v1, v1, q)
317 | mstore(add(pMem, pV2), aux)
318 | aux := mulmod(aux, v1, q)
319 | mstore(add(pMem, pV3), aux)
320 | aux := mulmod(aux, v1, q)
321 | mstore(add(pMem, pV4), aux)
322 | aux := mulmod(aux, v1, q)
323 | mstore(add(pMem, pV5), aux)
324 |
325 | // challenges.u
326 | mstore(mIn, calldataload(pWxi))
327 | mstore(add(mIn, 32), calldataload(add(pWxi, 32)))
328 | mstore(add(mIn, 64), calldataload(pWxiw))
329 | mstore(add(mIn, 96), calldataload(add(pWxiw, 32)))
330 |
331 | mstore(add(pMem, pU), mod(keccak256(mIn, 128), q))
332 | }
333 |
334 | function calculateLagrange(pMem) {
335 | let w := 1
336 |
337 | mstore(
338 | add(pMem, pEval_l1),
339 | mulmod(
340 | n,
341 | mod(
342 | add(
343 | sub(
344 | mload(add(pMem, pXi)),
345 | w
346 | ),
347 | q
348 | ),
349 | q
350 | ),
351 | q
352 | )
353 | )
354 |
355 |
356 |
357 | inverseArray(add(pMem, pZhInv), 2 )
358 |
359 | let zh := mload(add(pMem, pZh))
360 | w := 1
361 |
362 |
363 | mstore(
364 | add(pMem, pEval_l1 ),
365 | mulmod(
366 | mload(add(pMem, pEval_l1 )),
367 | zh,
368 | q
369 | )
370 | )
371 |
372 |
373 |
374 |
375 |
376 | }
377 |
378 | function calculatePI(pMem, pPub) {
379 | let pl := 0
380 |
381 |
382 | pl := mod(
383 | add(
384 | sub(
385 | pl,
386 | mulmod(
387 | mload(add(pMem, pEval_l1)),
388 | calldataload(add(pPub, 0)),
389 | q
390 | )
391 | ),
392 | q
393 | ),
394 | q
395 | )
396 |
397 |
398 | mstore(add(pMem, pPI), pl)
399 | }
400 |
401 | function calculateR0(pMem) {
402 | let e1 := mload(add(pMem, pPI))
403 |
404 | let e2 := mulmod(mload(add(pMem, pEval_l1)), mload(add(pMem, pAlpha2)), q)
405 |
406 | let e3a := addmod(
407 | calldataload(pEval_a),
408 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q),
409 | q)
410 | e3a := addmod(e3a, mload(add(pMem, pGamma)), q)
411 |
412 | let e3b := addmod(
413 | calldataload(pEval_b),
414 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q),
415 | q)
416 | e3b := addmod(e3b, mload(add(pMem, pGamma)), q)
417 |
418 | let e3c := addmod(
419 | calldataload(pEval_c),
420 | mload(add(pMem, pGamma)),
421 | q)
422 |
423 | let e3 := mulmod(mulmod(e3a, e3b, q), e3c, q)
424 | e3 := mulmod(e3, calldataload(pEval_zw), q)
425 | e3 := mulmod(e3, mload(add(pMem, pAlpha)), q)
426 |
427 | let r0 := addmod(e1, mod(sub(q, e2), q), q)
428 | r0 := addmod(r0, mod(sub(q, e3), q), q)
429 |
430 | mstore(add(pMem, pEval_r0) , r0)
431 | }
432 |
433 | function g1_set(pR, pP) {
434 | mstore(pR, mload(pP))
435 | mstore(add(pR, 32), mload(add(pP,32)))
436 | }
437 |
438 | function g1_setC(pR, x, y) {
439 | mstore(pR, x)
440 | mstore(add(pR, 32), y)
441 | }
442 |
443 | function g1_calldataSet(pR, pP) {
444 | mstore(pR, calldataload(pP))
445 | mstore(add(pR, 32), calldataload(add(pP, 32)))
446 | }
447 |
448 | function g1_acc(pR, pP) {
449 | let mIn := mload(0x40)
450 | mstore(mIn, mload(pR))
451 | mstore(add(mIn,32), mload(add(pR, 32)))
452 | mstore(add(mIn,64), mload(pP))
453 | mstore(add(mIn,96), mload(add(pP, 32)))
454 |
455 | let success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
456 |
457 | if iszero(success) {
458 | mstore(0, 0)
459 | return(0,0x20)
460 | }
461 | }
462 |
463 | function g1_mulAcc(pR, pP, s) {
464 | let success
465 | let mIn := mload(0x40)
466 | mstore(mIn, mload(pP))
467 | mstore(add(mIn,32), mload(add(pP, 32)))
468 | mstore(add(mIn,64), s)
469 |
470 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
471 |
472 | if iszero(success) {
473 | mstore(0, 0)
474 | return(0,0x20)
475 | }
476 |
477 | mstore(add(mIn,64), mload(pR))
478 | mstore(add(mIn,96), mload(add(pR, 32)))
479 |
480 | success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
481 |
482 | if iszero(success) {
483 | mstore(0, 0)
484 | return(0,0x20)
485 | }
486 |
487 | }
488 |
489 | function g1_mulAccC(pR, x, y, s) {
490 | let success
491 | let mIn := mload(0x40)
492 | mstore(mIn, x)
493 | mstore(add(mIn,32), y)
494 | mstore(add(mIn,64), s)
495 |
496 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
497 |
498 | if iszero(success) {
499 | mstore(0, 0)
500 | return(0,0x20)
501 | }
502 |
503 | mstore(add(mIn,64), mload(pR))
504 | mstore(add(mIn,96), mload(add(pR, 32)))
505 |
506 | success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
507 |
508 | if iszero(success) {
509 | mstore(0, 0)
510 | return(0,0x20)
511 | }
512 | }
513 |
514 | function g1_mulSetC(pR, x, y, s) {
515 | let success
516 | let mIn := mload(0x40)
517 | mstore(mIn, x)
518 | mstore(add(mIn,32), y)
519 | mstore(add(mIn,64), s)
520 |
521 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, pR, 64)
522 |
523 | if iszero(success) {
524 | mstore(0, 0)
525 | return(0,0x20)
526 | }
527 | }
528 |
529 | function g1_mulSet(pR, pP, s) {
530 | g1_mulSetC(pR, mload(pP), mload(add(pP, 32)), s)
531 | }
532 |
533 | function calculateD(pMem) {
534 | let _pD:= add(pMem, pD)
535 | let gamma := mload(add(pMem, pGamma))
536 | let mIn := mload(0x40)
537 | mstore(0x40, add(mIn, 256)) // d1, d2, d3 & d4 (4*64 bytes)
538 |
539 | g1_setC(_pD, Qcx, Qcy)
540 | g1_mulAccC(_pD, Qmx, Qmy, mulmod(calldataload(pEval_a), calldataload(pEval_b), q))
541 | g1_mulAccC(_pD, Qlx, Qly, calldataload(pEval_a))
542 | g1_mulAccC(_pD, Qrx, Qry, calldataload(pEval_b))
543 | g1_mulAccC(_pD, Qox, Qoy, calldataload(pEval_c))
544 |
545 | let betaxi := mload(add(pMem, pBetaXi))
546 | let val1 := addmod(
547 | addmod(calldataload(pEval_a), betaxi, q),
548 | gamma, q)
549 |
550 | let val2 := addmod(
551 | addmod(
552 | calldataload(pEval_b),
553 | mulmod(betaxi, k1, q),
554 | q), gamma, q)
555 |
556 | let val3 := addmod(
557 | addmod(
558 | calldataload(pEval_c),
559 | mulmod(betaxi, k2, q),
560 | q), gamma, q)
561 |
562 | let d2a := mulmod(
563 | mulmod(mulmod(val1, val2, q), val3, q),
564 | mload(add(pMem, pAlpha)),
565 | q
566 | )
567 |
568 | let d2b := mulmod(
569 | mload(add(pMem, pEval_l1)),
570 | mload(add(pMem, pAlpha2)),
571 | q
572 | )
573 |
574 | // We'll use mIn to save d2
575 | g1_calldataSet(add(mIn, 192), pZ)
576 | g1_mulSet(
577 | mIn,
578 | add(mIn, 192),
579 | addmod(addmod(d2a, d2b, q), mload(add(pMem, pU)), q))
580 |
581 |
582 | val1 := addmod(
583 | addmod(
584 | calldataload(pEval_a),
585 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q),
586 | q), gamma, q)
587 |
588 | val2 := addmod(
589 | addmod(
590 | calldataload(pEval_b),
591 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q),
592 | q), gamma, q)
593 |
594 | val3 := mulmod(
595 | mulmod(mload(add(pMem, pAlpha)), mload(add(pMem, pBeta)), q),
596 | calldataload(pEval_zw), q)
597 |
598 |
599 | // We'll use mIn + 64 to save d3
600 | g1_mulSetC(
601 | add(mIn, 64),
602 | S3x,
603 | S3y,
604 | mulmod(mulmod(val1, val2, q), val3, q))
605 |
606 | // We'll use mIn + 128 to save d4
607 | g1_calldataSet(add(mIn, 128), pT1)
608 |
609 | g1_mulAccC(add(mIn, 128), calldataload(pT2), calldataload(add(pT2, 32)), mload(add(pMem, pXin)))
610 | let xin2 := mulmod(mload(add(pMem, pXin)), mload(add(pMem, pXin)), q)
611 | g1_mulAccC(add(mIn, 128), calldataload(pT3), calldataload(add(pT3, 32)) , xin2)
612 |
613 | g1_mulSetC(add(mIn, 128), mload(add(mIn, 128)), mload(add(mIn, 160)), mload(add(pMem, pZh)))
614 |
615 | mstore(add(add(mIn, 64), 32), mod(sub(qf, mload(add(add(mIn, 64), 32))), qf))
616 | mstore(add(mIn, 160), mod(sub(qf, mload(add(mIn, 160))), qf))
617 | g1_acc(_pD, mIn)
618 | g1_acc(_pD, add(mIn, 64))
619 | g1_acc(_pD, add(mIn, 128))
620 | }
621 |
622 | function calculateF(pMem) {
623 | let p := add(pMem, pF)
624 |
625 | g1_set(p, add(pMem, pD))
626 | g1_mulAccC(p, calldataload(pA), calldataload(add(pA, 32)), mload(add(pMem, pV1)))
627 | g1_mulAccC(p, calldataload(pB), calldataload(add(pB, 32)), mload(add(pMem, pV2)))
628 | g1_mulAccC(p, calldataload(pC), calldataload(add(pC, 32)), mload(add(pMem, pV3)))
629 | g1_mulAccC(p, S1x, S1y, mload(add(pMem, pV4)))
630 | g1_mulAccC(p, S2x, S2y, mload(add(pMem, pV5)))
631 | }
632 |
633 | function calculateE(pMem) {
634 | let s := mod(sub(q, mload(add(pMem, pEval_r0))), q)
635 |
636 | s := addmod(s, mulmod(calldataload(pEval_a), mload(add(pMem, pV1)), q), q)
637 | s := addmod(s, mulmod(calldataload(pEval_b), mload(add(pMem, pV2)), q), q)
638 | s := addmod(s, mulmod(calldataload(pEval_c), mload(add(pMem, pV3)), q), q)
639 | s := addmod(s, mulmod(calldataload(pEval_s1), mload(add(pMem, pV4)), q), q)
640 | s := addmod(s, mulmod(calldataload(pEval_s2), mload(add(pMem, pV5)), q), q)
641 | s := addmod(s, mulmod(calldataload(pEval_zw), mload(add(pMem, pU)), q), q)
642 |
643 | g1_mulSetC(add(pMem, pE), G1x, G1y, s)
644 | }
645 |
646 | function checkPairing(pMem) -> isOk {
647 | let mIn := mload(0x40)
648 | mstore(0x40, add(mIn, 576)) // [0..383] = pairing data, [384..447] = pWxi, [448..512] = pWxiw
649 |
650 | let _pWxi := add(mIn, 384)
651 | let _pWxiw := add(mIn, 448)
652 | let _aux := add(mIn, 512)
653 |
654 | g1_calldataSet(_pWxi, pWxi)
655 | g1_calldataSet(_pWxiw, pWxiw)
656 |
657 | // A1
658 | g1_mulSet(mIn, _pWxiw, mload(add(pMem, pU)))
659 | g1_acc(mIn, _pWxi)
660 | mstore(add(mIn, 32), mod(sub(qf, mload(add(mIn, 32))), qf))
661 |
662 | // [X]_2
663 | mstore(add(mIn,64), X2x2)
664 | mstore(add(mIn,96), X2x1)
665 | mstore(add(mIn,128), X2y2)
666 | mstore(add(mIn,160), X2y1)
667 |
668 | // B1
669 | g1_mulSet(add(mIn, 192), _pWxi, mload(add(pMem, pXi)))
670 |
671 | let s := mulmod(mload(add(pMem, pU)), mload(add(pMem, pXi)), q)
672 | s := mulmod(s, w1, q)
673 | g1_mulSet(_aux, _pWxiw, s)
674 | g1_acc(add(mIn, 192), _aux)
675 | g1_acc(add(mIn, 192), add(pMem, pF))
676 | mstore(add(pMem, add(pE, 32)), mod(sub(qf, mload(add(pMem, add(pE, 32)))), qf))
677 | g1_acc(add(mIn, 192), add(pMem, pE))
678 |
679 | // [1]_2
680 | mstore(add(mIn,256), G2x2)
681 | mstore(add(mIn,288), G2x1)
682 | mstore(add(mIn,320), G2y2)
683 | mstore(add(mIn,352), G2y1)
684 |
685 | let success := staticcall(sub(gas(), 2000), 8, mIn, 384, mIn, 0x20)
686 |
687 | isOk := and(success, mload(mIn))
688 | }
689 |
690 | let pMem := mload(0x40)
691 | mstore(0x40, add(pMem, lastMem))
692 |
693 | checkInput()
694 | calculateChallenges(pMem, _pubSignals)
695 | calculateLagrange(pMem)
696 | calculatePI(pMem, _pubSignals)
697 | calculateR0(pMem)
698 | calculateD(pMem)
699 | calculateF(pMem)
700 | calculateE(pMem)
701 | let isValid := checkPairing(pMem)
702 |
703 | mstore(0x40, sub(pMem, lastMem))
704 | mstore(0, isValid)
705 | return(0,0x20)
706 | }
707 |
708 | }
709 | }
710 |
--------------------------------------------------------------------------------
/contracts/verifiers/MultiplierVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | /*
3 | Copyright 2021 0KIMS association.
4 |
5 | This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
6 |
7 | snarkJS is a free software: you can redistribute it and/or modify it
8 | under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | snarkJS is distributed in the hope that it will be useful, but WITHOUT
13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 | License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with snarkJS. If not, see .
19 | */
20 |
21 |
22 | pragma solidity >=0.7.0 <0.9.0;
23 |
24 | contract MultiplierVerifier {
25 | // Omega
26 | uint256 constant w1 = 19540430494807482326159819597004422086093766032135589407132600596362845576832;
27 | // Scalar field size
28 | uint256 constant q = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
29 | // Base field size
30 | uint256 constant qf = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
31 |
32 | // [1]_1
33 | uint256 constant G1x = 1;
34 | uint256 constant G1y = 2;
35 | // [1]_2
36 | uint256 constant G2x1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
37 | uint256 constant G2x2 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
38 | uint256 constant G2y1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
39 | uint256 constant G2y2 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
40 |
41 | // Verification Key data
42 | uint32 constant n = 8;
43 | uint16 constant nPublic = 1;
44 | uint16 constant nLagrange = 1;
45 |
46 | uint256 constant Qmx = 10294367845524522889674980414658158979115219665406612861401259333422895729896;
47 | uint256 constant Qmy = 17339696279167455564514853058684962930296864414660175742312401951183098671156;
48 | uint256 constant Qlx = 14297155691368363150439281660551929853142513799648244067851273621337387750022;
49 | uint256 constant Qly = 9875708754215138125887194540142977977698752545665252035430927128878501866163;
50 | uint256 constant Qrx = 0;
51 | uint256 constant Qry = 0;
52 | uint256 constant Qox = 10294367845524522889674980414658158979115219665406612861401259333422895729896;
53 | uint256 constant Qoy = 4548546592671819657731552686572312158399446742637647920376635943462127537427;
54 | uint256 constant Qcx = 0;
55 | uint256 constant Qcy = 0;
56 | uint256 constant S1x = 2694761611667402433549058650401049833973608710551146850129171008254242491412;
57 | uint256 constant S1y = 4407815622841625592989621790140274705225113068749062773093818734897989348824;
58 | uint256 constant S2x = 174950894878901504258554221888959060942005622520060188523927601854050691737;
59 | uint256 constant S2y = 4218570225917094256073281485929194442981718242484973947497628954679969816940;
60 | uint256 constant S3x = 5287191920074181963852791792640835974097875195213946104826736553520071559657;
61 | uint256 constant S3y = 20309358409622118500558363825899879958276827259299017541094321370900713472551;
62 | uint256 constant k1 = 2;
63 | uint256 constant k2 = 3;
64 | uint256 constant X2x1 = 21831381940315734285607113342023901060522397560371972897001948545212302161822;
65 | uint256 constant X2x2 = 17231025384763736816414546592865244497437017442647097510447326538965263639101;
66 | uint256 constant X2y1 = 2388026358213174446665280700919698872609886601280537296205114254867301080648;
67 | uint256 constant X2y2 = 11507326595632554467052522095592665270651932854513688777769618397986436103170;
68 |
69 | // Proof calldata
70 | // Byte offset of every parameter of the calldata
71 | // Polynomial commitments
72 | uint16 constant pA = 4 + 0;
73 | uint16 constant pB = 4 + 64;
74 | uint16 constant pC = 4 + 128;
75 | uint16 constant pZ = 4 + 192;
76 | uint16 constant pT1 = 4 + 256;
77 | uint16 constant pT2 = 4 + 320;
78 | uint16 constant pT3 = 4 + 384;
79 | uint16 constant pWxi = 4 + 448;
80 | uint16 constant pWxiw = 4 + 512;
81 | // Opening evaluations
82 | uint16 constant pEval_a = 4 + 576;
83 | uint16 constant pEval_b = 4 + 608;
84 | uint16 constant pEval_c = 4 + 640;
85 | uint16 constant pEval_s1 = 4 + 672;
86 | uint16 constant pEval_s2 = 4 + 704;
87 | uint16 constant pEval_zw = 4 + 736;
88 |
89 | // Memory data
90 | // Challenges
91 | uint16 constant pAlpha = 0;
92 | uint16 constant pBeta = 32;
93 | uint16 constant pGamma = 64;
94 | uint16 constant pXi = 96;
95 | uint16 constant pXin = 128;
96 | uint16 constant pBetaXi = 160;
97 | uint16 constant pV1 = 192;
98 | uint16 constant pV2 = 224;
99 | uint16 constant pV3 = 256;
100 | uint16 constant pV4 = 288;
101 | uint16 constant pV5 = 320;
102 | uint16 constant pU = 352;
103 |
104 | uint16 constant pPI = 384;
105 | uint16 constant pEval_r0 = 416;
106 | uint16 constant pD = 448;
107 | uint16 constant pF = 512;
108 | uint16 constant pE = 576;
109 | uint16 constant pTmp = 640;
110 | uint16 constant pAlpha2 = 704;
111 | uint16 constant pZh = 736;
112 | uint16 constant pZhInv = 768;
113 |
114 |
115 | uint16 constant pEval_l1 = 800;
116 |
117 |
118 |
119 | uint16 constant lastMem = 832;
120 |
121 | function verifyProof(uint256[24] calldata _proof, uint256[1] calldata _pubSignals) public view returns (bool) {
122 | assembly {
123 | /////////
124 | // Computes the inverse using the extended euclidean algorithm
125 | /////////
126 | function inverse(a, q) -> inv {
127 | let t := 0
128 | let newt := 1
129 | let r := q
130 | let newr := a
131 | let quotient
132 | let aux
133 |
134 | for { } newr { } {
135 | quotient := sdiv(r, newr)
136 | aux := sub(t, mul(quotient, newt))
137 | t:= newt
138 | newt:= aux
139 |
140 | aux := sub(r,mul(quotient, newr))
141 | r := newr
142 | newr := aux
143 | }
144 |
145 | if gt(r, 1) { revert(0,0) }
146 | if slt(t, 0) { t:= add(t, q) }
147 |
148 | inv := t
149 | }
150 |
151 | ///////
152 | // Computes the inverse of an array of values
153 | // See https://vitalik.ca/general/2018/07/21/starks_part_3.html in section where explain fields operations
154 | //////
155 | function inverseArray(pVals, n) {
156 |
157 | let pAux := mload(0x40) // Point to the next free position
158 | let pIn := pVals
159 | let lastPIn := add(pVals, mul(n, 32)) // Read n elements
160 | let acc := mload(pIn) // Read the first element
161 | pIn := add(pIn, 32) // Point to the second element
162 | let inv
163 |
164 |
165 | for { } lt(pIn, lastPIn) {
166 | pAux := add(pAux, 32)
167 | pIn := add(pIn, 32)
168 | }
169 | {
170 | mstore(pAux, acc)
171 | acc := mulmod(acc, mload(pIn), q)
172 | }
173 | acc := inverse(acc, q)
174 |
175 | // At this point pAux pint to the next free position we subtract 1 to point to the last used
176 | pAux := sub(pAux, 32)
177 | // pIn points to the n+1 element, we subtract to point to n
178 | pIn := sub(pIn, 32)
179 | lastPIn := pVals // We don't process the first element
180 | for { } gt(pIn, lastPIn) {
181 | pAux := sub(pAux, 32)
182 | pIn := sub(pIn, 32)
183 | }
184 | {
185 | inv := mulmod(acc, mload(pAux), q)
186 | acc := mulmod(acc, mload(pIn), q)
187 | mstore(pIn, inv)
188 | }
189 | // pIn points to first element, we just set it.
190 | mstore(pIn, acc)
191 | }
192 |
193 | function checkField(v) {
194 | if iszero(lt(v, q)) {
195 | mstore(0, 0)
196 | return(0,0x20)
197 | }
198 | }
199 |
200 | function checkInput() {
201 | checkField(calldataload(pEval_a))
202 | checkField(calldataload(pEval_b))
203 | checkField(calldataload(pEval_c))
204 | checkField(calldataload(pEval_s1))
205 | checkField(calldataload(pEval_s2))
206 | checkField(calldataload(pEval_zw))
207 | }
208 |
209 | function calculateChallenges(pMem, pPublic) {
210 | let beta
211 | let aux
212 |
213 | let mIn := mload(0x40) // Pointer to the next free memory position
214 |
215 | // Compute challenge.beta & challenge.gamma
216 | mstore(mIn, Qmx)
217 | mstore(add(mIn, 32), Qmy)
218 | mstore(add(mIn, 64), Qlx)
219 | mstore(add(mIn, 96), Qly)
220 | mstore(add(mIn, 128), Qrx)
221 | mstore(add(mIn, 160), Qry)
222 | mstore(add(mIn, 192), Qox)
223 | mstore(add(mIn, 224), Qoy)
224 | mstore(add(mIn, 256), Qcx)
225 | mstore(add(mIn, 288), Qcy)
226 | mstore(add(mIn, 320), S1x)
227 | mstore(add(mIn, 352), S1y)
228 | mstore(add(mIn, 384), S2x)
229 | mstore(add(mIn, 416), S2y)
230 | mstore(add(mIn, 448), S3x)
231 | mstore(add(mIn, 480), S3y)
232 |
233 |
234 | mstore(add(mIn, 512), calldataload(add(pPublic, 0)))
235 |
236 | mstore(add(mIn, 544 ), calldataload(pA))
237 | mstore(add(mIn, 576 ), calldataload(add(pA, 32)))
238 | mstore(add(mIn, 608 ), calldataload(pB))
239 | mstore(add(mIn, 640 ), calldataload(add(pB, 32)))
240 | mstore(add(mIn, 672 ), calldataload(pC))
241 | mstore(add(mIn, 704 ), calldataload(add(pC, 32)))
242 |
243 | beta := mod(keccak256(mIn, 736), q)
244 | mstore(add(pMem, pBeta), beta)
245 |
246 | // challenges.gamma
247 | mstore(add(pMem, pGamma), mod(keccak256(add(pMem, pBeta), 32), q))
248 |
249 | // challenges.alpha
250 | mstore(mIn, mload(add(pMem, pBeta)))
251 | mstore(add(mIn, 32), mload(add(pMem, pGamma)))
252 | mstore(add(mIn, 64), calldataload(pZ))
253 | mstore(add(mIn, 96), calldataload(add(pZ, 32)))
254 |
255 | aux := mod(keccak256(mIn, 128), q)
256 | mstore(add(pMem, pAlpha), aux)
257 | mstore(add(pMem, pAlpha2), mulmod(aux, aux, q))
258 |
259 | // challenges.xi
260 | mstore(mIn, aux)
261 | mstore(add(mIn, 32), calldataload(pT1))
262 | mstore(add(mIn, 64), calldataload(add(pT1, 32)))
263 | mstore(add(mIn, 96), calldataload(pT2))
264 | mstore(add(mIn, 128), calldataload(add(pT2, 32)))
265 | mstore(add(mIn, 160), calldataload(pT3))
266 | mstore(add(mIn, 192), calldataload(add(pT3, 32)))
267 |
268 | aux := mod(keccak256(mIn, 224), q)
269 | mstore( add(pMem, pXi), aux)
270 |
271 | // challenges.v
272 | mstore(mIn, aux)
273 | mstore(add(mIn, 32), calldataload(pEval_a))
274 | mstore(add(mIn, 64), calldataload(pEval_b))
275 | mstore(add(mIn, 96), calldataload(pEval_c))
276 | mstore(add(mIn, 128), calldataload(pEval_s1))
277 | mstore(add(mIn, 160), calldataload(pEval_s2))
278 | mstore(add(mIn, 192), calldataload(pEval_zw))
279 |
280 | let v1 := mod(keccak256(mIn, 224), q)
281 | mstore(add(pMem, pV1), v1)
282 |
283 | // challenges.beta * challenges.xi
284 | mstore(add(pMem, pBetaXi), mulmod(beta, aux, q))
285 |
286 | // challenges.xi^n
287 |
288 | aux:= mulmod(aux, aux, q)
289 |
290 | aux:= mulmod(aux, aux, q)
291 |
292 | aux:= mulmod(aux, aux, q)
293 |
294 | mstore(add(pMem, pXin), aux)
295 |
296 | // Zh
297 | aux:= mod(add(sub(aux, 1), q), q)
298 | mstore(add(pMem, pZh), aux)
299 | mstore(add(pMem, pZhInv), aux) // We will invert later together with lagrange pols
300 |
301 | // challenges.v^2, challenges.v^3, challenges.v^4, challenges.v^5
302 | aux := mulmod(v1, v1, q)
303 | mstore(add(pMem, pV2), aux)
304 | aux := mulmod(aux, v1, q)
305 | mstore(add(pMem, pV3), aux)
306 | aux := mulmod(aux, v1, q)
307 | mstore(add(pMem, pV4), aux)
308 | aux := mulmod(aux, v1, q)
309 | mstore(add(pMem, pV5), aux)
310 |
311 | // challenges.u
312 | mstore(mIn, calldataload(pWxi))
313 | mstore(add(mIn, 32), calldataload(add(pWxi, 32)))
314 | mstore(add(mIn, 64), calldataload(pWxiw))
315 | mstore(add(mIn, 96), calldataload(add(pWxiw, 32)))
316 |
317 | mstore(add(pMem, pU), mod(keccak256(mIn, 128), q))
318 | }
319 |
320 | function calculateLagrange(pMem) {
321 | let w := 1
322 |
323 | mstore(
324 | add(pMem, pEval_l1),
325 | mulmod(
326 | n,
327 | mod(
328 | add(
329 | sub(
330 | mload(add(pMem, pXi)),
331 | w
332 | ),
333 | q
334 | ),
335 | q
336 | ),
337 | q
338 | )
339 | )
340 |
341 |
342 |
343 | inverseArray(add(pMem, pZhInv), 2 )
344 |
345 | let zh := mload(add(pMem, pZh))
346 | w := 1
347 |
348 |
349 | mstore(
350 | add(pMem, pEval_l1 ),
351 | mulmod(
352 | mload(add(pMem, pEval_l1 )),
353 | zh,
354 | q
355 | )
356 | )
357 |
358 |
359 |
360 |
361 |
362 | }
363 |
364 | function calculatePI(pMem, pPub) {
365 | let pl := 0
366 |
367 |
368 | pl := mod(
369 | add(
370 | sub(
371 | pl,
372 | mulmod(
373 | mload(add(pMem, pEval_l1)),
374 | calldataload(add(pPub, 0)),
375 | q
376 | )
377 | ),
378 | q
379 | ),
380 | q
381 | )
382 |
383 |
384 | mstore(add(pMem, pPI), pl)
385 | }
386 |
387 | function calculateR0(pMem) {
388 | let e1 := mload(add(pMem, pPI))
389 |
390 | let e2 := mulmod(mload(add(pMem, pEval_l1)), mload(add(pMem, pAlpha2)), q)
391 |
392 | let e3a := addmod(
393 | calldataload(pEval_a),
394 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q),
395 | q)
396 | e3a := addmod(e3a, mload(add(pMem, pGamma)), q)
397 |
398 | let e3b := addmod(
399 | calldataload(pEval_b),
400 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q),
401 | q)
402 | e3b := addmod(e3b, mload(add(pMem, pGamma)), q)
403 |
404 | let e3c := addmod(
405 | calldataload(pEval_c),
406 | mload(add(pMem, pGamma)),
407 | q)
408 |
409 | let e3 := mulmod(mulmod(e3a, e3b, q), e3c, q)
410 | e3 := mulmod(e3, calldataload(pEval_zw), q)
411 | e3 := mulmod(e3, mload(add(pMem, pAlpha)), q)
412 |
413 | let r0 := addmod(e1, mod(sub(q, e2), q), q)
414 | r0 := addmod(r0, mod(sub(q, e3), q), q)
415 |
416 | mstore(add(pMem, pEval_r0) , r0)
417 | }
418 |
419 | function g1_set(pR, pP) {
420 | mstore(pR, mload(pP))
421 | mstore(add(pR, 32), mload(add(pP,32)))
422 | }
423 |
424 | function g1_setC(pR, x, y) {
425 | mstore(pR, x)
426 | mstore(add(pR, 32), y)
427 | }
428 |
429 | function g1_calldataSet(pR, pP) {
430 | mstore(pR, calldataload(pP))
431 | mstore(add(pR, 32), calldataload(add(pP, 32)))
432 | }
433 |
434 | function g1_acc(pR, pP) {
435 | let mIn := mload(0x40)
436 | mstore(mIn, mload(pR))
437 | mstore(add(mIn,32), mload(add(pR, 32)))
438 | mstore(add(mIn,64), mload(pP))
439 | mstore(add(mIn,96), mload(add(pP, 32)))
440 |
441 | let success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
442 |
443 | if iszero(success) {
444 | mstore(0, 0)
445 | return(0,0x20)
446 | }
447 | }
448 |
449 | function g1_mulAcc(pR, pP, s) {
450 | let success
451 | let mIn := mload(0x40)
452 | mstore(mIn, mload(pP))
453 | mstore(add(mIn,32), mload(add(pP, 32)))
454 | mstore(add(mIn,64), s)
455 |
456 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
457 |
458 | if iszero(success) {
459 | mstore(0, 0)
460 | return(0,0x20)
461 | }
462 |
463 | mstore(add(mIn,64), mload(pR))
464 | mstore(add(mIn,96), mload(add(pR, 32)))
465 |
466 | success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
467 |
468 | if iszero(success) {
469 | mstore(0, 0)
470 | return(0,0x20)
471 | }
472 |
473 | }
474 |
475 | function g1_mulAccC(pR, x, y, s) {
476 | let success
477 | let mIn := mload(0x40)
478 | mstore(mIn, x)
479 | mstore(add(mIn,32), y)
480 | mstore(add(mIn,64), s)
481 |
482 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
483 |
484 | if iszero(success) {
485 | mstore(0, 0)
486 | return(0,0x20)
487 | }
488 |
489 | mstore(add(mIn,64), mload(pR))
490 | mstore(add(mIn,96), mload(add(pR, 32)))
491 |
492 | success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
493 |
494 | if iszero(success) {
495 | mstore(0, 0)
496 | return(0,0x20)
497 | }
498 | }
499 |
500 | function g1_mulSetC(pR, x, y, s) {
501 | let success
502 | let mIn := mload(0x40)
503 | mstore(mIn, x)
504 | mstore(add(mIn,32), y)
505 | mstore(add(mIn,64), s)
506 |
507 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, pR, 64)
508 |
509 | if iszero(success) {
510 | mstore(0, 0)
511 | return(0,0x20)
512 | }
513 | }
514 |
515 | function g1_mulSet(pR, pP, s) {
516 | g1_mulSetC(pR, mload(pP), mload(add(pP, 32)), s)
517 | }
518 |
519 | function calculateD(pMem) {
520 | let _pD:= add(pMem, pD)
521 | let gamma := mload(add(pMem, pGamma))
522 | let mIn := mload(0x40)
523 | mstore(0x40, add(mIn, 256)) // d1, d2, d3 & d4 (4*64 bytes)
524 |
525 | g1_setC(_pD, Qcx, Qcy)
526 | g1_mulAccC(_pD, Qmx, Qmy, mulmod(calldataload(pEval_a), calldataload(pEval_b), q))
527 | g1_mulAccC(_pD, Qlx, Qly, calldataload(pEval_a))
528 | g1_mulAccC(_pD, Qrx, Qry, calldataload(pEval_b))
529 | g1_mulAccC(_pD, Qox, Qoy, calldataload(pEval_c))
530 |
531 | let betaxi := mload(add(pMem, pBetaXi))
532 | let val1 := addmod(
533 | addmod(calldataload(pEval_a), betaxi, q),
534 | gamma, q)
535 |
536 | let val2 := addmod(
537 | addmod(
538 | calldataload(pEval_b),
539 | mulmod(betaxi, k1, q),
540 | q), gamma, q)
541 |
542 | let val3 := addmod(
543 | addmod(
544 | calldataload(pEval_c),
545 | mulmod(betaxi, k2, q),
546 | q), gamma, q)
547 |
548 | let d2a := mulmod(
549 | mulmod(mulmod(val1, val2, q), val3, q),
550 | mload(add(pMem, pAlpha)),
551 | q
552 | )
553 |
554 | let d2b := mulmod(
555 | mload(add(pMem, pEval_l1)),
556 | mload(add(pMem, pAlpha2)),
557 | q
558 | )
559 |
560 | // We'll use mIn to save d2
561 | g1_calldataSet(add(mIn, 192), pZ)
562 | g1_mulSet(
563 | mIn,
564 | add(mIn, 192),
565 | addmod(addmod(d2a, d2b, q), mload(add(pMem, pU)), q))
566 |
567 |
568 | val1 := addmod(
569 | addmod(
570 | calldataload(pEval_a),
571 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q),
572 | q), gamma, q)
573 |
574 | val2 := addmod(
575 | addmod(
576 | calldataload(pEval_b),
577 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q),
578 | q), gamma, q)
579 |
580 | val3 := mulmod(
581 | mulmod(mload(add(pMem, pAlpha)), mload(add(pMem, pBeta)), q),
582 | calldataload(pEval_zw), q)
583 |
584 |
585 | // We'll use mIn + 64 to save d3
586 | g1_mulSetC(
587 | add(mIn, 64),
588 | S3x,
589 | S3y,
590 | mulmod(mulmod(val1, val2, q), val3, q))
591 |
592 | // We'll use mIn + 128 to save d4
593 | g1_calldataSet(add(mIn, 128), pT1)
594 |
595 | g1_mulAccC(add(mIn, 128), calldataload(pT2), calldataload(add(pT2, 32)), mload(add(pMem, pXin)))
596 | let xin2 := mulmod(mload(add(pMem, pXin)), mload(add(pMem, pXin)), q)
597 | g1_mulAccC(add(mIn, 128), calldataload(pT3), calldataload(add(pT3, 32)) , xin2)
598 |
599 | g1_mulSetC(add(mIn, 128), mload(add(mIn, 128)), mload(add(mIn, 160)), mload(add(pMem, pZh)))
600 |
601 | mstore(add(add(mIn, 64), 32), mod(sub(qf, mload(add(add(mIn, 64), 32))), qf))
602 | mstore(add(mIn, 160), mod(sub(qf, mload(add(mIn, 160))), qf))
603 | g1_acc(_pD, mIn)
604 | g1_acc(_pD, add(mIn, 64))
605 | g1_acc(_pD, add(mIn, 128))
606 | }
607 |
608 | function calculateF(pMem) {
609 | let p := add(pMem, pF)
610 |
611 | g1_set(p, add(pMem, pD))
612 | g1_mulAccC(p, calldataload(pA), calldataload(add(pA, 32)), mload(add(pMem, pV1)))
613 | g1_mulAccC(p, calldataload(pB), calldataload(add(pB, 32)), mload(add(pMem, pV2)))
614 | g1_mulAccC(p, calldataload(pC), calldataload(add(pC, 32)), mload(add(pMem, pV3)))
615 | g1_mulAccC(p, S1x, S1y, mload(add(pMem, pV4)))
616 | g1_mulAccC(p, S2x, S2y, mload(add(pMem, pV5)))
617 | }
618 |
619 | function calculateE(pMem) {
620 | let s := mod(sub(q, mload(add(pMem, pEval_r0))), q)
621 |
622 | s := addmod(s, mulmod(calldataload(pEval_a), mload(add(pMem, pV1)), q), q)
623 | s := addmod(s, mulmod(calldataload(pEval_b), mload(add(pMem, pV2)), q), q)
624 | s := addmod(s, mulmod(calldataload(pEval_c), mload(add(pMem, pV3)), q), q)
625 | s := addmod(s, mulmod(calldataload(pEval_s1), mload(add(pMem, pV4)), q), q)
626 | s := addmod(s, mulmod(calldataload(pEval_s2), mload(add(pMem, pV5)), q), q)
627 | s := addmod(s, mulmod(calldataload(pEval_zw), mload(add(pMem, pU)), q), q)
628 |
629 | g1_mulSetC(add(pMem, pE), G1x, G1y, s)
630 | }
631 |
632 | function checkPairing(pMem) -> isOk {
633 | let mIn := mload(0x40)
634 | mstore(0x40, add(mIn, 576)) // [0..383] = pairing data, [384..447] = pWxi, [448..512] = pWxiw
635 |
636 | let _pWxi := add(mIn, 384)
637 | let _pWxiw := add(mIn, 448)
638 | let _aux := add(mIn, 512)
639 |
640 | g1_calldataSet(_pWxi, pWxi)
641 | g1_calldataSet(_pWxiw, pWxiw)
642 |
643 | // A1
644 | g1_mulSet(mIn, _pWxiw, mload(add(pMem, pU)))
645 | g1_acc(mIn, _pWxi)
646 | mstore(add(mIn, 32), mod(sub(qf, mload(add(mIn, 32))), qf))
647 |
648 | // [X]_2
649 | mstore(add(mIn,64), X2x2)
650 | mstore(add(mIn,96), X2x1)
651 | mstore(add(mIn,128), X2y2)
652 | mstore(add(mIn,160), X2y1)
653 |
654 | // B1
655 | g1_mulSet(add(mIn, 192), _pWxi, mload(add(pMem, pXi)))
656 |
657 | let s := mulmod(mload(add(pMem, pU)), mload(add(pMem, pXi)), q)
658 | s := mulmod(s, w1, q)
659 | g1_mulSet(_aux, _pWxiw, s)
660 | g1_acc(add(mIn, 192), _aux)
661 | g1_acc(add(mIn, 192), add(pMem, pF))
662 | mstore(add(pMem, add(pE, 32)), mod(sub(qf, mload(add(pMem, add(pE, 32)))), qf))
663 | g1_acc(add(mIn, 192), add(pMem, pE))
664 |
665 | // [1]_2
666 | mstore(add(mIn,256), G2x2)
667 | mstore(add(mIn,288), G2x1)
668 | mstore(add(mIn,320), G2y2)
669 | mstore(add(mIn,352), G2y1)
670 |
671 | let success := staticcall(sub(gas(), 2000), 8, mIn, 384, mIn, 0x20)
672 |
673 | isOk := and(success, mload(mIn))
674 | }
675 |
676 | let pMem := mload(0x40)
677 | mstore(0x40, add(pMem, lastMem))
678 |
679 | checkInput()
680 | calculateChallenges(pMem, _pubSignals)
681 | calculateLagrange(pMem)
682 | calculatePI(pMem, _pubSignals)
683 | calculateR0(pMem)
684 | calculateD(pMem)
685 | calculateF(pMem)
686 | calculateE(pMem)
687 | let isValid := checkPairing(pMem)
688 |
689 | mstore(0x40, sub(pMem, lastMem))
690 | mstore(0, isValid)
691 | return(0,0x20)
692 | }
693 |
694 | }
695 | }
696 |
--------------------------------------------------------------------------------
/contracts/verifiers/PubkeyVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | /*
3 | Copyright 2021 0KIMS association.
4 |
5 | This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
6 |
7 | snarkJS is a free software: you can redistribute it and/or modify it
8 | under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | snarkJS is distributed in the hope that it will be useful, but WITHOUT
13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 | License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with snarkJS. If not, see .
19 | */
20 |
21 |
22 | pragma solidity >=0.7.0 <0.9.0;
23 |
24 | contract PubkeyVerifier {
25 | // Omega
26 | uint256 constant w1 = 20619701001583904760601357484951574588621083236087856586626117568842480512645;
27 | // Scalar field size
28 | uint256 constant q = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
29 | // Base field size
30 | uint256 constant qf = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
31 |
32 | // [1]_1
33 | uint256 constant G1x = 1;
34 | uint256 constant G1y = 2;
35 | // [1]_2
36 | uint256 constant G2x1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
37 | uint256 constant G2x2 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
38 | uint256 constant G2y1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
39 | uint256 constant G2y2 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
40 |
41 | // Verification Key data
42 | uint32 constant n = 16384;
43 | uint16 constant nPublic = 2;
44 | uint16 constant nLagrange = 2;
45 |
46 | uint256 constant Qmx = 6867829495162913273480976895526541598685870069495998602901303227188301753506;
47 | uint256 constant Qmy = 5941537034499810921674076675447841767341374588507403901762746189386863247541;
48 | uint256 constant Qlx = 5248545586957936474188076821967326148169008088264912930877502326296598786595;
49 | uint256 constant Qly = 45572317913468097456416085513935613600582112837555717643911715883351303349;
50 | uint256 constant Qrx = 19844448801426135299671017224430069603002765947192806184961066400283789236932;
51 | uint256 constant Qry = 16405463065817359035151864380017507406676163394821430263642100859963236257743;
52 | uint256 constant Qox = 6154774522789462721122514053321999158513020192591192536111604161188074752866;
53 | uint256 constant Qoy = 2457657539741409538278456775142827246933257501018485221926151679330255508788;
54 | uint256 constant Qcx = 13382799044224319508948459627654629012873869535769719391361696515793239236815;
55 | uint256 constant Qcy = 7179102158446198760225732555579464417631844202122662974977840702516392142845;
56 | uint256 constant S1x = 14528227241625450402028146430155259012861609073721530220858988611267849427030;
57 | uint256 constant S1y = 19536291007726862802239737200689353557031818326602763633582458119400946465154;
58 | uint256 constant S2x = 4879720925355118365436081578615265836625032679317452702530233171596525176792;
59 | uint256 constant S2y = 16374574122633868663751709177619652588953168014150586257646938005746970582239;
60 | uint256 constant S3x = 1265824111895057295598663260540993261098479386669151513713785079900023464528;
61 | uint256 constant S3y = 16744216054402972557860453464013058985275908105456878668971672483126336320630;
62 | uint256 constant k1 = 2;
63 | uint256 constant k2 = 3;
64 | uint256 constant X2x1 = 21831381940315734285607113342023901060522397560371972897001948545212302161822;
65 | uint256 constant X2x2 = 17231025384763736816414546592865244497437017442647097510447326538965263639101;
66 | uint256 constant X2y1 = 2388026358213174446665280700919698872609886601280537296205114254867301080648;
67 | uint256 constant X2y2 = 11507326595632554467052522095592665270651932854513688777769618397986436103170;
68 |
69 | // Proof calldata
70 | // Byte offset of every parameter of the calldata
71 | // Polynomial commitments
72 | uint16 constant pA = 4 + 0;
73 | uint16 constant pB = 4 + 64;
74 | uint16 constant pC = 4 + 128;
75 | uint16 constant pZ = 4 + 192;
76 | uint16 constant pT1 = 4 + 256;
77 | uint16 constant pT2 = 4 + 320;
78 | uint16 constant pT3 = 4 + 384;
79 | uint16 constant pWxi = 4 + 448;
80 | uint16 constant pWxiw = 4 + 512;
81 | // Opening evaluations
82 | uint16 constant pEval_a = 4 + 576;
83 | uint16 constant pEval_b = 4 + 608;
84 | uint16 constant pEval_c = 4 + 640;
85 | uint16 constant pEval_s1 = 4 + 672;
86 | uint16 constant pEval_s2 = 4 + 704;
87 | uint16 constant pEval_zw = 4 + 736;
88 |
89 | // Memory data
90 | // Challenges
91 | uint16 constant pAlpha = 0;
92 | uint16 constant pBeta = 32;
93 | uint16 constant pGamma = 64;
94 | uint16 constant pXi = 96;
95 | uint16 constant pXin = 128;
96 | uint16 constant pBetaXi = 160;
97 | uint16 constant pV1 = 192;
98 | uint16 constant pV2 = 224;
99 | uint16 constant pV3 = 256;
100 | uint16 constant pV4 = 288;
101 | uint16 constant pV5 = 320;
102 | uint16 constant pU = 352;
103 |
104 | uint16 constant pPI = 384;
105 | uint16 constant pEval_r0 = 416;
106 | uint16 constant pD = 448;
107 | uint16 constant pF = 512;
108 | uint16 constant pE = 576;
109 | uint16 constant pTmp = 640;
110 | uint16 constant pAlpha2 = 704;
111 | uint16 constant pZh = 736;
112 | uint16 constant pZhInv = 768;
113 |
114 |
115 | uint16 constant pEval_l1 = 800;
116 |
117 | uint16 constant pEval_l2 = 832;
118 |
119 |
120 |
121 | uint16 constant lastMem = 864;
122 |
123 | function verifyProof(uint256[24] calldata _proof, uint256[2] calldata _pubSignals) public view returns (bool) {
124 | assembly {
125 | /////////
126 | // Computes the inverse using the extended euclidean algorithm
127 | /////////
128 | function inverse(a, q) -> inv {
129 | let t := 0
130 | let newt := 1
131 | let r := q
132 | let newr := a
133 | let quotient
134 | let aux
135 |
136 | for { } newr { } {
137 | quotient := sdiv(r, newr)
138 | aux := sub(t, mul(quotient, newt))
139 | t:= newt
140 | newt:= aux
141 |
142 | aux := sub(r,mul(quotient, newr))
143 | r := newr
144 | newr := aux
145 | }
146 |
147 | if gt(r, 1) { revert(0,0) }
148 | if slt(t, 0) { t:= add(t, q) }
149 |
150 | inv := t
151 | }
152 |
153 | ///////
154 | // Computes the inverse of an array of values
155 | // See https://vitalik.ca/general/2018/07/21/starks_part_3.html in section where explain fields operations
156 | //////
157 | function inverseArray(pVals, n) {
158 |
159 | let pAux := mload(0x40) // Point to the next free position
160 | let pIn := pVals
161 | let lastPIn := add(pVals, mul(n, 32)) // Read n elements
162 | let acc := mload(pIn) // Read the first element
163 | pIn := add(pIn, 32) // Point to the second element
164 | let inv
165 |
166 |
167 | for { } lt(pIn, lastPIn) {
168 | pAux := add(pAux, 32)
169 | pIn := add(pIn, 32)
170 | }
171 | {
172 | mstore(pAux, acc)
173 | acc := mulmod(acc, mload(pIn), q)
174 | }
175 | acc := inverse(acc, q)
176 |
177 | // At this point pAux pint to the next free position we subtract 1 to point to the last used
178 | pAux := sub(pAux, 32)
179 | // pIn points to the n+1 element, we subtract to point to n
180 | pIn := sub(pIn, 32)
181 | lastPIn := pVals // We don't process the first element
182 | for { } gt(pIn, lastPIn) {
183 | pAux := sub(pAux, 32)
184 | pIn := sub(pIn, 32)
185 | }
186 | {
187 | inv := mulmod(acc, mload(pAux), q)
188 | acc := mulmod(acc, mload(pIn), q)
189 | mstore(pIn, inv)
190 | }
191 | // pIn points to first element, we just set it.
192 | mstore(pIn, acc)
193 | }
194 |
195 | function checkField(v) {
196 | if iszero(lt(v, q)) {
197 | mstore(0, 0)
198 | return(0,0x20)
199 | }
200 | }
201 |
202 | function checkInput() {
203 | checkField(calldataload(pEval_a))
204 | checkField(calldataload(pEval_b))
205 | checkField(calldataload(pEval_c))
206 | checkField(calldataload(pEval_s1))
207 | checkField(calldataload(pEval_s2))
208 | checkField(calldataload(pEval_zw))
209 | }
210 |
211 | function calculateChallenges(pMem, pPublic) {
212 | let beta
213 | let aux
214 |
215 | let mIn := mload(0x40) // Pointer to the next free memory position
216 |
217 | // Compute challenge.beta & challenge.gamma
218 | mstore(mIn, Qmx)
219 | mstore(add(mIn, 32), Qmy)
220 | mstore(add(mIn, 64), Qlx)
221 | mstore(add(mIn, 96), Qly)
222 | mstore(add(mIn, 128), Qrx)
223 | mstore(add(mIn, 160), Qry)
224 | mstore(add(mIn, 192), Qox)
225 | mstore(add(mIn, 224), Qoy)
226 | mstore(add(mIn, 256), Qcx)
227 | mstore(add(mIn, 288), Qcy)
228 | mstore(add(mIn, 320), S1x)
229 | mstore(add(mIn, 352), S1y)
230 | mstore(add(mIn, 384), S2x)
231 | mstore(add(mIn, 416), S2y)
232 | mstore(add(mIn, 448), S3x)
233 | mstore(add(mIn, 480), S3y)
234 |
235 |
236 | mstore(add(mIn, 512), calldataload(add(pPublic, 0)))
237 |
238 | mstore(add(mIn, 544), calldataload(add(pPublic, 32)))
239 |
240 | mstore(add(mIn, 576 ), calldataload(pA))
241 | mstore(add(mIn, 608 ), calldataload(add(pA, 32)))
242 | mstore(add(mIn, 640 ), calldataload(pB))
243 | mstore(add(mIn, 672 ), calldataload(add(pB, 32)))
244 | mstore(add(mIn, 704 ), calldataload(pC))
245 | mstore(add(mIn, 736 ), calldataload(add(pC, 32)))
246 |
247 | beta := mod(keccak256(mIn, 768), q)
248 | mstore(add(pMem, pBeta), beta)
249 |
250 | // challenges.gamma
251 | mstore(add(pMem, pGamma), mod(keccak256(add(pMem, pBeta), 32), q))
252 |
253 | // challenges.alpha
254 | mstore(mIn, mload(add(pMem, pBeta)))
255 | mstore(add(mIn, 32), mload(add(pMem, pGamma)))
256 | mstore(add(mIn, 64), calldataload(pZ))
257 | mstore(add(mIn, 96), calldataload(add(pZ, 32)))
258 |
259 | aux := mod(keccak256(mIn, 128), q)
260 | mstore(add(pMem, pAlpha), aux)
261 | mstore(add(pMem, pAlpha2), mulmod(aux, aux, q))
262 |
263 | // challenges.xi
264 | mstore(mIn, aux)
265 | mstore(add(mIn, 32), calldataload(pT1))
266 | mstore(add(mIn, 64), calldataload(add(pT1, 32)))
267 | mstore(add(mIn, 96), calldataload(pT2))
268 | mstore(add(mIn, 128), calldataload(add(pT2, 32)))
269 | mstore(add(mIn, 160), calldataload(pT3))
270 | mstore(add(mIn, 192), calldataload(add(pT3, 32)))
271 |
272 | aux := mod(keccak256(mIn, 224), q)
273 | mstore( add(pMem, pXi), aux)
274 |
275 | // challenges.v
276 | mstore(mIn, aux)
277 | mstore(add(mIn, 32), calldataload(pEval_a))
278 | mstore(add(mIn, 64), calldataload(pEval_b))
279 | mstore(add(mIn, 96), calldataload(pEval_c))
280 | mstore(add(mIn, 128), calldataload(pEval_s1))
281 | mstore(add(mIn, 160), calldataload(pEval_s2))
282 | mstore(add(mIn, 192), calldataload(pEval_zw))
283 |
284 | let v1 := mod(keccak256(mIn, 224), q)
285 | mstore(add(pMem, pV1), v1)
286 |
287 | // challenges.beta * challenges.xi
288 | mstore(add(pMem, pBetaXi), mulmod(beta, aux, q))
289 |
290 | // challenges.xi^n
291 |
292 | aux:= mulmod(aux, aux, q)
293 |
294 | aux:= mulmod(aux, aux, q)
295 |
296 | aux:= mulmod(aux, aux, q)
297 |
298 | aux:= mulmod(aux, aux, q)
299 |
300 | aux:= mulmod(aux, aux, q)
301 |
302 | aux:= mulmod(aux, aux, q)
303 |
304 | aux:= mulmod(aux, aux, q)
305 |
306 | aux:= mulmod(aux, aux, q)
307 |
308 | aux:= mulmod(aux, aux, q)
309 |
310 | aux:= mulmod(aux, aux, q)
311 |
312 | aux:= mulmod(aux, aux, q)
313 |
314 | aux:= mulmod(aux, aux, q)
315 |
316 | aux:= mulmod(aux, aux, q)
317 |
318 | aux:= mulmod(aux, aux, q)
319 |
320 | mstore(add(pMem, pXin), aux)
321 |
322 | // Zh
323 | aux:= mod(add(sub(aux, 1), q), q)
324 | mstore(add(pMem, pZh), aux)
325 | mstore(add(pMem, pZhInv), aux) // We will invert later together with lagrange pols
326 |
327 | // challenges.v^2, challenges.v^3, challenges.v^4, challenges.v^5
328 | aux := mulmod(v1, v1, q)
329 | mstore(add(pMem, pV2), aux)
330 | aux := mulmod(aux, v1, q)
331 | mstore(add(pMem, pV3), aux)
332 | aux := mulmod(aux, v1, q)
333 | mstore(add(pMem, pV4), aux)
334 | aux := mulmod(aux, v1, q)
335 | mstore(add(pMem, pV5), aux)
336 |
337 | // challenges.u
338 | mstore(mIn, calldataload(pWxi))
339 | mstore(add(mIn, 32), calldataload(add(pWxi, 32)))
340 | mstore(add(mIn, 64), calldataload(pWxiw))
341 | mstore(add(mIn, 96), calldataload(add(pWxiw, 32)))
342 |
343 | mstore(add(pMem, pU), mod(keccak256(mIn, 128), q))
344 | }
345 |
346 | function calculateLagrange(pMem) {
347 | let w := 1
348 |
349 | mstore(
350 | add(pMem, pEval_l1),
351 | mulmod(
352 | n,
353 | mod(
354 | add(
355 | sub(
356 | mload(add(pMem, pXi)),
357 | w
358 | ),
359 | q
360 | ),
361 | q
362 | ),
363 | q
364 | )
365 | )
366 |
367 | w := mulmod(w, w1, q)
368 |
369 |
370 | mstore(
371 | add(pMem, pEval_l2),
372 | mulmod(
373 | n,
374 | mod(
375 | add(
376 | sub(
377 | mload(add(pMem, pXi)),
378 | w
379 | ),
380 | q
381 | ),
382 | q
383 | ),
384 | q
385 | )
386 | )
387 |
388 |
389 |
390 | inverseArray(add(pMem, pZhInv), 3 )
391 |
392 | let zh := mload(add(pMem, pZh))
393 | w := 1
394 |
395 |
396 | mstore(
397 | add(pMem, pEval_l1 ),
398 | mulmod(
399 | mload(add(pMem, pEval_l1 )),
400 | zh,
401 | q
402 | )
403 | )
404 |
405 |
406 | w := mulmod(w, w1, q)
407 |
408 |
409 |
410 | mstore(
411 | add(pMem, pEval_l2),
412 | mulmod(
413 | w,
414 | mulmod(
415 | mload(add(pMem, pEval_l2)),
416 | zh,
417 | q
418 | ),
419 | q
420 | )
421 | )
422 |
423 |
424 |
425 |
426 |
427 | }
428 |
429 | function calculatePI(pMem, pPub) {
430 | let pl := 0
431 |
432 |
433 | pl := mod(
434 | add(
435 | sub(
436 | pl,
437 | mulmod(
438 | mload(add(pMem, pEval_l1)),
439 | calldataload(add(pPub, 0)),
440 | q
441 | )
442 | ),
443 | q
444 | ),
445 | q
446 | )
447 |
448 | pl := mod(
449 | add(
450 | sub(
451 | pl,
452 | mulmod(
453 | mload(add(pMem, pEval_l2)),
454 | calldataload(add(pPub, 32)),
455 | q
456 | )
457 | ),
458 | q
459 | ),
460 | q
461 | )
462 |
463 |
464 | mstore(add(pMem, pPI), pl)
465 | }
466 |
467 | function calculateR0(pMem) {
468 | let e1 := mload(add(pMem, pPI))
469 |
470 | let e2 := mulmod(mload(add(pMem, pEval_l1)), mload(add(pMem, pAlpha2)), q)
471 |
472 | let e3a := addmod(
473 | calldataload(pEval_a),
474 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q),
475 | q)
476 | e3a := addmod(e3a, mload(add(pMem, pGamma)), q)
477 |
478 | let e3b := addmod(
479 | calldataload(pEval_b),
480 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q),
481 | q)
482 | e3b := addmod(e3b, mload(add(pMem, pGamma)), q)
483 |
484 | let e3c := addmod(
485 | calldataload(pEval_c),
486 | mload(add(pMem, pGamma)),
487 | q)
488 |
489 | let e3 := mulmod(mulmod(e3a, e3b, q), e3c, q)
490 | e3 := mulmod(e3, calldataload(pEval_zw), q)
491 | e3 := mulmod(e3, mload(add(pMem, pAlpha)), q)
492 |
493 | let r0 := addmod(e1, mod(sub(q, e2), q), q)
494 | r0 := addmod(r0, mod(sub(q, e3), q), q)
495 |
496 | mstore(add(pMem, pEval_r0) , r0)
497 | }
498 |
499 | function g1_set(pR, pP) {
500 | mstore(pR, mload(pP))
501 | mstore(add(pR, 32), mload(add(pP,32)))
502 | }
503 |
504 | function g1_setC(pR, x, y) {
505 | mstore(pR, x)
506 | mstore(add(pR, 32), y)
507 | }
508 |
509 | function g1_calldataSet(pR, pP) {
510 | mstore(pR, calldataload(pP))
511 | mstore(add(pR, 32), calldataload(add(pP, 32)))
512 | }
513 |
514 | function g1_acc(pR, pP) {
515 | let mIn := mload(0x40)
516 | mstore(mIn, mload(pR))
517 | mstore(add(mIn,32), mload(add(pR, 32)))
518 | mstore(add(mIn,64), mload(pP))
519 | mstore(add(mIn,96), mload(add(pP, 32)))
520 |
521 | let success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
522 |
523 | if iszero(success) {
524 | mstore(0, 0)
525 | return(0,0x20)
526 | }
527 | }
528 |
529 | function g1_mulAcc(pR, pP, s) {
530 | let success
531 | let mIn := mload(0x40)
532 | mstore(mIn, mload(pP))
533 | mstore(add(mIn,32), mload(add(pP, 32)))
534 | mstore(add(mIn,64), s)
535 |
536 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
537 |
538 | if iszero(success) {
539 | mstore(0, 0)
540 | return(0,0x20)
541 | }
542 |
543 | mstore(add(mIn,64), mload(pR))
544 | mstore(add(mIn,96), mload(add(pR, 32)))
545 |
546 | success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
547 |
548 | if iszero(success) {
549 | mstore(0, 0)
550 | return(0,0x20)
551 | }
552 |
553 | }
554 |
555 | function g1_mulAccC(pR, x, y, s) {
556 | let success
557 | let mIn := mload(0x40)
558 | mstore(mIn, x)
559 | mstore(add(mIn,32), y)
560 | mstore(add(mIn,64), s)
561 |
562 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
563 |
564 | if iszero(success) {
565 | mstore(0, 0)
566 | return(0,0x20)
567 | }
568 |
569 | mstore(add(mIn,64), mload(pR))
570 | mstore(add(mIn,96), mload(add(pR, 32)))
571 |
572 | success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
573 |
574 | if iszero(success) {
575 | mstore(0, 0)
576 | return(0,0x20)
577 | }
578 | }
579 |
580 | function g1_mulSetC(pR, x, y, s) {
581 | let success
582 | let mIn := mload(0x40)
583 | mstore(mIn, x)
584 | mstore(add(mIn,32), y)
585 | mstore(add(mIn,64), s)
586 |
587 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, pR, 64)
588 |
589 | if iszero(success) {
590 | mstore(0, 0)
591 | return(0,0x20)
592 | }
593 | }
594 |
595 | function g1_mulSet(pR, pP, s) {
596 | g1_mulSetC(pR, mload(pP), mload(add(pP, 32)), s)
597 | }
598 |
599 | function calculateD(pMem) {
600 | let _pD:= add(pMem, pD)
601 | let gamma := mload(add(pMem, pGamma))
602 | let mIn := mload(0x40)
603 | mstore(0x40, add(mIn, 256)) // d1, d2, d3 & d4 (4*64 bytes)
604 |
605 | g1_setC(_pD, Qcx, Qcy)
606 | g1_mulAccC(_pD, Qmx, Qmy, mulmod(calldataload(pEval_a), calldataload(pEval_b), q))
607 | g1_mulAccC(_pD, Qlx, Qly, calldataload(pEval_a))
608 | g1_mulAccC(_pD, Qrx, Qry, calldataload(pEval_b))
609 | g1_mulAccC(_pD, Qox, Qoy, calldataload(pEval_c))
610 |
611 | let betaxi := mload(add(pMem, pBetaXi))
612 | let val1 := addmod(
613 | addmod(calldataload(pEval_a), betaxi, q),
614 | gamma, q)
615 |
616 | let val2 := addmod(
617 | addmod(
618 | calldataload(pEval_b),
619 | mulmod(betaxi, k1, q),
620 | q), gamma, q)
621 |
622 | let val3 := addmod(
623 | addmod(
624 | calldataload(pEval_c),
625 | mulmod(betaxi, k2, q),
626 | q), gamma, q)
627 |
628 | let d2a := mulmod(
629 | mulmod(mulmod(val1, val2, q), val3, q),
630 | mload(add(pMem, pAlpha)),
631 | q
632 | )
633 |
634 | let d2b := mulmod(
635 | mload(add(pMem, pEval_l1)),
636 | mload(add(pMem, pAlpha2)),
637 | q
638 | )
639 |
640 | // We'll use mIn to save d2
641 | g1_calldataSet(add(mIn, 192), pZ)
642 | g1_mulSet(
643 | mIn,
644 | add(mIn, 192),
645 | addmod(addmod(d2a, d2b, q), mload(add(pMem, pU)), q))
646 |
647 |
648 | val1 := addmod(
649 | addmod(
650 | calldataload(pEval_a),
651 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q),
652 | q), gamma, q)
653 |
654 | val2 := addmod(
655 | addmod(
656 | calldataload(pEval_b),
657 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q),
658 | q), gamma, q)
659 |
660 | val3 := mulmod(
661 | mulmod(mload(add(pMem, pAlpha)), mload(add(pMem, pBeta)), q),
662 | calldataload(pEval_zw), q)
663 |
664 |
665 | // We'll use mIn + 64 to save d3
666 | g1_mulSetC(
667 | add(mIn, 64),
668 | S3x,
669 | S3y,
670 | mulmod(mulmod(val1, val2, q), val3, q))
671 |
672 | // We'll use mIn + 128 to save d4
673 | g1_calldataSet(add(mIn, 128), pT1)
674 |
675 | g1_mulAccC(add(mIn, 128), calldataload(pT2), calldataload(add(pT2, 32)), mload(add(pMem, pXin)))
676 | let xin2 := mulmod(mload(add(pMem, pXin)), mload(add(pMem, pXin)), q)
677 | g1_mulAccC(add(mIn, 128), calldataload(pT3), calldataload(add(pT3, 32)) , xin2)
678 |
679 | g1_mulSetC(add(mIn, 128), mload(add(mIn, 128)), mload(add(mIn, 160)), mload(add(pMem, pZh)))
680 |
681 | mstore(add(add(mIn, 64), 32), mod(sub(qf, mload(add(add(mIn, 64), 32))), qf))
682 | mstore(add(mIn, 160), mod(sub(qf, mload(add(mIn, 160))), qf))
683 | g1_acc(_pD, mIn)
684 | g1_acc(_pD, add(mIn, 64))
685 | g1_acc(_pD, add(mIn, 128))
686 | }
687 |
688 | function calculateF(pMem) {
689 | let p := add(pMem, pF)
690 |
691 | g1_set(p, add(pMem, pD))
692 | g1_mulAccC(p, calldataload(pA), calldataload(add(pA, 32)), mload(add(pMem, pV1)))
693 | g1_mulAccC(p, calldataload(pB), calldataload(add(pB, 32)), mload(add(pMem, pV2)))
694 | g1_mulAccC(p, calldataload(pC), calldataload(add(pC, 32)), mload(add(pMem, pV3)))
695 | g1_mulAccC(p, S1x, S1y, mload(add(pMem, pV4)))
696 | g1_mulAccC(p, S2x, S2y, mload(add(pMem, pV5)))
697 | }
698 |
699 | function calculateE(pMem) {
700 | let s := mod(sub(q, mload(add(pMem, pEval_r0))), q)
701 |
702 | s := addmod(s, mulmod(calldataload(pEval_a), mload(add(pMem, pV1)), q), q)
703 | s := addmod(s, mulmod(calldataload(pEval_b), mload(add(pMem, pV2)), q), q)
704 | s := addmod(s, mulmod(calldataload(pEval_c), mload(add(pMem, pV3)), q), q)
705 | s := addmod(s, mulmod(calldataload(pEval_s1), mload(add(pMem, pV4)), q), q)
706 | s := addmod(s, mulmod(calldataload(pEval_s2), mload(add(pMem, pV5)), q), q)
707 | s := addmod(s, mulmod(calldataload(pEval_zw), mload(add(pMem, pU)), q), q)
708 |
709 | g1_mulSetC(add(pMem, pE), G1x, G1y, s)
710 | }
711 |
712 | function checkPairing(pMem) -> isOk {
713 | let mIn := mload(0x40)
714 | mstore(0x40, add(mIn, 576)) // [0..383] = pairing data, [384..447] = pWxi, [448..512] = pWxiw
715 |
716 | let _pWxi := add(mIn, 384)
717 | let _pWxiw := add(mIn, 448)
718 | let _aux := add(mIn, 512)
719 |
720 | g1_calldataSet(_pWxi, pWxi)
721 | g1_calldataSet(_pWxiw, pWxiw)
722 |
723 | // A1
724 | g1_mulSet(mIn, _pWxiw, mload(add(pMem, pU)))
725 | g1_acc(mIn, _pWxi)
726 | mstore(add(mIn, 32), mod(sub(qf, mload(add(mIn, 32))), qf))
727 |
728 | // [X]_2
729 | mstore(add(mIn,64), X2x2)
730 | mstore(add(mIn,96), X2x1)
731 | mstore(add(mIn,128), X2y2)
732 | mstore(add(mIn,160), X2y1)
733 |
734 | // B1
735 | g1_mulSet(add(mIn, 192), _pWxi, mload(add(pMem, pXi)))
736 |
737 | let s := mulmod(mload(add(pMem, pU)), mload(add(pMem, pXi)), q)
738 | s := mulmod(s, w1, q)
739 | g1_mulSet(_aux, _pWxiw, s)
740 | g1_acc(add(mIn, 192), _aux)
741 | g1_acc(add(mIn, 192), add(pMem, pF))
742 | mstore(add(pMem, add(pE, 32)), mod(sub(qf, mload(add(pMem, add(pE, 32)))), qf))
743 | g1_acc(add(mIn, 192), add(pMem, pE))
744 |
745 | // [1]_2
746 | mstore(add(mIn,256), G2x2)
747 | mstore(add(mIn,288), G2x1)
748 | mstore(add(mIn,320), G2y2)
749 | mstore(add(mIn,352), G2y1)
750 |
751 | let success := staticcall(sub(gas(), 2000), 8, mIn, 384, mIn, 0x20)
752 |
753 | isOk := and(success, mload(mIn))
754 | }
755 |
756 | let pMem := mload(0x40)
757 | mstore(0x40, add(pMem, lastMem))
758 |
759 | checkInput()
760 | calculateChallenges(pMem, _pubSignals)
761 | calculateLagrange(pMem)
762 | calculatePI(pMem, _pubSignals)
763 | calculateR0(pMem)
764 | calculateD(pMem)
765 | calculateF(pMem)
766 | calculateE(pMem)
767 | let isValid := checkPairing(pMem)
768 |
769 | mstore(0x40, sub(pMem, lastMem))
770 | mstore(0, isValid)
771 | return(0,0x20)
772 | }
773 |
774 | }
775 | }
776 |
--------------------------------------------------------------------------------
/contracts/verifiers/PythagorasVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | /*
3 | Copyright 2021 0KIMS association.
4 |
5 | This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
6 |
7 | snarkJS is a free software: you can redistribute it and/or modify it
8 | under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | snarkJS is distributed in the hope that it will be useful, but WITHOUT
13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 | License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with snarkJS. If not, see .
19 | */
20 |
21 |
22 | pragma solidity >=0.7.0 <0.9.0;
23 |
24 | contract PythagorasVerifier {
25 | // Omega
26 | uint256 constant w1 = 19540430494807482326159819597004422086093766032135589407132600596362845576832;
27 | // Scalar field size
28 | uint256 constant q = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
29 | // Base field size
30 | uint256 constant qf = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
31 |
32 | // [1]_1
33 | uint256 constant G1x = 1;
34 | uint256 constant G1y = 2;
35 | // [1]_2
36 | uint256 constant G2x1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
37 | uint256 constant G2x2 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
38 | uint256 constant G2y1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
39 | uint256 constant G2y2 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
40 |
41 | // Verification Key data
42 | uint32 constant n = 8;
43 | uint16 constant nPublic = 1;
44 | uint16 constant nLagrange = 1;
45 |
46 | uint256 constant Qmx = 15709197065441308741336200385458053194001524411421163210476606625901610602784;
47 | uint256 constant Qmy = 20107188510478061735753067637169416844479414539001516380099756067788484389544;
48 | uint256 constant Qlx = 11061768654402029366013515682522360582550291908840061488505685296538451527254;
49 | uint256 constant Qly = 15471600944923485559452784938718837008623891847925721777620478053180790737461;
50 | uint256 constant Qrx = 11448008371105398943869362113810658997753184086726857543614610338733680103806;
51 | uint256 constant Qry = 8526946509698979755701815775739707350493042104526490190408488647604655638122;
52 | uint256 constant Qox = 11417277899075430604770434269880819725304375503216236587767173094679019570847;
53 | uint256 constant Qoy = 6543395296813345384304199905094388711319556926105207124271547473226589676469;
54 | uint256 constant Qcx = 0;
55 | uint256 constant Qcy = 0;
56 | uint256 constant S1x = 7918996172219386295704521183819205952088645185718821264523139557196406611774;
57 | uint256 constant S1y = 18241057293545811364170150548902277646716818548360251998847516104141099611496;
58 | uint256 constant S2x = 711545892626180513471403042031241695523966026880927433000273155143947215114;
59 | uint256 constant S2y = 12900839569779827099988758485946384810772554052279167178798448800403150738937;
60 | uint256 constant S3x = 10602638595357484529514252613879624122103895786001250478976706546743832291793;
61 | uint256 constant S3y = 20529981353225414621581824290818764730356644360811324751475405681369672112062;
62 | uint256 constant k1 = 2;
63 | uint256 constant k2 = 3;
64 | uint256 constant X2x1 = 21831381940315734285607113342023901060522397560371972897001948545212302161822;
65 | uint256 constant X2x2 = 17231025384763736816414546592865244497437017442647097510447326538965263639101;
66 | uint256 constant X2y1 = 2388026358213174446665280700919698872609886601280537296205114254867301080648;
67 | uint256 constant X2y2 = 11507326595632554467052522095592665270651932854513688777769618397986436103170;
68 |
69 | // Proof calldata
70 | // Byte offset of every parameter of the calldata
71 | // Polynomial commitments
72 | uint16 constant pA = 4 + 0;
73 | uint16 constant pB = 4 + 64;
74 | uint16 constant pC = 4 + 128;
75 | uint16 constant pZ = 4 + 192;
76 | uint16 constant pT1 = 4 + 256;
77 | uint16 constant pT2 = 4 + 320;
78 | uint16 constant pT3 = 4 + 384;
79 | uint16 constant pWxi = 4 + 448;
80 | uint16 constant pWxiw = 4 + 512;
81 | // Opening evaluations
82 | uint16 constant pEval_a = 4 + 576;
83 | uint16 constant pEval_b = 4 + 608;
84 | uint16 constant pEval_c = 4 + 640;
85 | uint16 constant pEval_s1 = 4 + 672;
86 | uint16 constant pEval_s2 = 4 + 704;
87 | uint16 constant pEval_zw = 4 + 736;
88 |
89 | // Memory data
90 | // Challenges
91 | uint16 constant pAlpha = 0;
92 | uint16 constant pBeta = 32;
93 | uint16 constant pGamma = 64;
94 | uint16 constant pXi = 96;
95 | uint16 constant pXin = 128;
96 | uint16 constant pBetaXi = 160;
97 | uint16 constant pV1 = 192;
98 | uint16 constant pV2 = 224;
99 | uint16 constant pV3 = 256;
100 | uint16 constant pV4 = 288;
101 | uint16 constant pV5 = 320;
102 | uint16 constant pU = 352;
103 |
104 | uint16 constant pPI = 384;
105 | uint16 constant pEval_r0 = 416;
106 | uint16 constant pD = 448;
107 | uint16 constant pF = 512;
108 | uint16 constant pE = 576;
109 | uint16 constant pTmp = 640;
110 | uint16 constant pAlpha2 = 704;
111 | uint16 constant pZh = 736;
112 | uint16 constant pZhInv = 768;
113 |
114 |
115 | uint16 constant pEval_l1 = 800;
116 |
117 |
118 |
119 | uint16 constant lastMem = 832;
120 |
121 | function verifyProof(uint256[24] calldata _proof, uint256[1] calldata _pubSignals) public view returns (bool) {
122 | assembly {
123 | /////////
124 | // Computes the inverse using the extended euclidean algorithm
125 | /////////
126 | function inverse(a, q) -> inv {
127 | let t := 0
128 | let newt := 1
129 | let r := q
130 | let newr := a
131 | let quotient
132 | let aux
133 |
134 | for { } newr { } {
135 | quotient := sdiv(r, newr)
136 | aux := sub(t, mul(quotient, newt))
137 | t:= newt
138 | newt:= aux
139 |
140 | aux := sub(r,mul(quotient, newr))
141 | r := newr
142 | newr := aux
143 | }
144 |
145 | if gt(r, 1) { revert(0,0) }
146 | if slt(t, 0) { t:= add(t, q) }
147 |
148 | inv := t
149 | }
150 |
151 | ///////
152 | // Computes the inverse of an array of values
153 | // See https://vitalik.ca/general/2018/07/21/starks_part_3.html in section where explain fields operations
154 | //////
155 | function inverseArray(pVals, n) {
156 |
157 | let pAux := mload(0x40) // Point to the next free position
158 | let pIn := pVals
159 | let lastPIn := add(pVals, mul(n, 32)) // Read n elements
160 | let acc := mload(pIn) // Read the first element
161 | pIn := add(pIn, 32) // Point to the second element
162 | let inv
163 |
164 |
165 | for { } lt(pIn, lastPIn) {
166 | pAux := add(pAux, 32)
167 | pIn := add(pIn, 32)
168 | }
169 | {
170 | mstore(pAux, acc)
171 | acc := mulmod(acc, mload(pIn), q)
172 | }
173 | acc := inverse(acc, q)
174 |
175 | // At this point pAux pint to the next free position we subtract 1 to point to the last used
176 | pAux := sub(pAux, 32)
177 | // pIn points to the n+1 element, we subtract to point to n
178 | pIn := sub(pIn, 32)
179 | lastPIn := pVals // We don't process the first element
180 | for { } gt(pIn, lastPIn) {
181 | pAux := sub(pAux, 32)
182 | pIn := sub(pIn, 32)
183 | }
184 | {
185 | inv := mulmod(acc, mload(pAux), q)
186 | acc := mulmod(acc, mload(pIn), q)
187 | mstore(pIn, inv)
188 | }
189 | // pIn points to first element, we just set it.
190 | mstore(pIn, acc)
191 | }
192 |
193 | function checkField(v) {
194 | if iszero(lt(v, q)) {
195 | mstore(0, 0)
196 | return(0,0x20)
197 | }
198 | }
199 |
200 | function checkInput() {
201 | checkField(calldataload(pEval_a))
202 | checkField(calldataload(pEval_b))
203 | checkField(calldataload(pEval_c))
204 | checkField(calldataload(pEval_s1))
205 | checkField(calldataload(pEval_s2))
206 | checkField(calldataload(pEval_zw))
207 | }
208 |
209 | function calculateChallenges(pMem, pPublic) {
210 | let beta
211 | let aux
212 |
213 | let mIn := mload(0x40) // Pointer to the next free memory position
214 |
215 | // Compute challenge.beta & challenge.gamma
216 | mstore(mIn, Qmx)
217 | mstore(add(mIn, 32), Qmy)
218 | mstore(add(mIn, 64), Qlx)
219 | mstore(add(mIn, 96), Qly)
220 | mstore(add(mIn, 128), Qrx)
221 | mstore(add(mIn, 160), Qry)
222 | mstore(add(mIn, 192), Qox)
223 | mstore(add(mIn, 224), Qoy)
224 | mstore(add(mIn, 256), Qcx)
225 | mstore(add(mIn, 288), Qcy)
226 | mstore(add(mIn, 320), S1x)
227 | mstore(add(mIn, 352), S1y)
228 | mstore(add(mIn, 384), S2x)
229 | mstore(add(mIn, 416), S2y)
230 | mstore(add(mIn, 448), S3x)
231 | mstore(add(mIn, 480), S3y)
232 |
233 |
234 | mstore(add(mIn, 512), calldataload(add(pPublic, 0)))
235 |
236 | mstore(add(mIn, 544 ), calldataload(pA))
237 | mstore(add(mIn, 576 ), calldataload(add(pA, 32)))
238 | mstore(add(mIn, 608 ), calldataload(pB))
239 | mstore(add(mIn, 640 ), calldataload(add(pB, 32)))
240 | mstore(add(mIn, 672 ), calldataload(pC))
241 | mstore(add(mIn, 704 ), calldataload(add(pC, 32)))
242 |
243 | beta := mod(keccak256(mIn, 736), q)
244 | mstore(add(pMem, pBeta), beta)
245 |
246 | // challenges.gamma
247 | mstore(add(pMem, pGamma), mod(keccak256(add(pMem, pBeta), 32), q))
248 |
249 | // challenges.alpha
250 | mstore(mIn, mload(add(pMem, pBeta)))
251 | mstore(add(mIn, 32), mload(add(pMem, pGamma)))
252 | mstore(add(mIn, 64), calldataload(pZ))
253 | mstore(add(mIn, 96), calldataload(add(pZ, 32)))
254 |
255 | aux := mod(keccak256(mIn, 128), q)
256 | mstore(add(pMem, pAlpha), aux)
257 | mstore(add(pMem, pAlpha2), mulmod(aux, aux, q))
258 |
259 | // challenges.xi
260 | mstore(mIn, aux)
261 | mstore(add(mIn, 32), calldataload(pT1))
262 | mstore(add(mIn, 64), calldataload(add(pT1, 32)))
263 | mstore(add(mIn, 96), calldataload(pT2))
264 | mstore(add(mIn, 128), calldataload(add(pT2, 32)))
265 | mstore(add(mIn, 160), calldataload(pT3))
266 | mstore(add(mIn, 192), calldataload(add(pT3, 32)))
267 |
268 | aux := mod(keccak256(mIn, 224), q)
269 | mstore( add(pMem, pXi), aux)
270 |
271 | // challenges.v
272 | mstore(mIn, aux)
273 | mstore(add(mIn, 32), calldataload(pEval_a))
274 | mstore(add(mIn, 64), calldataload(pEval_b))
275 | mstore(add(mIn, 96), calldataload(pEval_c))
276 | mstore(add(mIn, 128), calldataload(pEval_s1))
277 | mstore(add(mIn, 160), calldataload(pEval_s2))
278 | mstore(add(mIn, 192), calldataload(pEval_zw))
279 |
280 | let v1 := mod(keccak256(mIn, 224), q)
281 | mstore(add(pMem, pV1), v1)
282 |
283 | // challenges.beta * challenges.xi
284 | mstore(add(pMem, pBetaXi), mulmod(beta, aux, q))
285 |
286 | // challenges.xi^n
287 |
288 | aux:= mulmod(aux, aux, q)
289 |
290 | aux:= mulmod(aux, aux, q)
291 |
292 | aux:= mulmod(aux, aux, q)
293 |
294 | mstore(add(pMem, pXin), aux)
295 |
296 | // Zh
297 | aux:= mod(add(sub(aux, 1), q), q)
298 | mstore(add(pMem, pZh), aux)
299 | mstore(add(pMem, pZhInv), aux) // We will invert later together with lagrange pols
300 |
301 | // challenges.v^2, challenges.v^3, challenges.v^4, challenges.v^5
302 | aux := mulmod(v1, v1, q)
303 | mstore(add(pMem, pV2), aux)
304 | aux := mulmod(aux, v1, q)
305 | mstore(add(pMem, pV3), aux)
306 | aux := mulmod(aux, v1, q)
307 | mstore(add(pMem, pV4), aux)
308 | aux := mulmod(aux, v1, q)
309 | mstore(add(pMem, pV5), aux)
310 |
311 | // challenges.u
312 | mstore(mIn, calldataload(pWxi))
313 | mstore(add(mIn, 32), calldataload(add(pWxi, 32)))
314 | mstore(add(mIn, 64), calldataload(pWxiw))
315 | mstore(add(mIn, 96), calldataload(add(pWxiw, 32)))
316 |
317 | mstore(add(pMem, pU), mod(keccak256(mIn, 128), q))
318 | }
319 |
320 | function calculateLagrange(pMem) {
321 | let w := 1
322 |
323 | mstore(
324 | add(pMem, pEval_l1),
325 | mulmod(
326 | n,
327 | mod(
328 | add(
329 | sub(
330 | mload(add(pMem, pXi)),
331 | w
332 | ),
333 | q
334 | ),
335 | q
336 | ),
337 | q
338 | )
339 | )
340 |
341 |
342 |
343 | inverseArray(add(pMem, pZhInv), 2 )
344 |
345 | let zh := mload(add(pMem, pZh))
346 | w := 1
347 |
348 |
349 | mstore(
350 | add(pMem, pEval_l1 ),
351 | mulmod(
352 | mload(add(pMem, pEval_l1 )),
353 | zh,
354 | q
355 | )
356 | )
357 |
358 |
359 |
360 |
361 |
362 | }
363 |
364 | function calculatePI(pMem, pPub) {
365 | let pl := 0
366 |
367 |
368 | pl := mod(
369 | add(
370 | sub(
371 | pl,
372 | mulmod(
373 | mload(add(pMem, pEval_l1)),
374 | calldataload(add(pPub, 0)),
375 | q
376 | )
377 | ),
378 | q
379 | ),
380 | q
381 | )
382 |
383 |
384 | mstore(add(pMem, pPI), pl)
385 | }
386 |
387 | function calculateR0(pMem) {
388 | let e1 := mload(add(pMem, pPI))
389 |
390 | let e2 := mulmod(mload(add(pMem, pEval_l1)), mload(add(pMem, pAlpha2)), q)
391 |
392 | let e3a := addmod(
393 | calldataload(pEval_a),
394 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q),
395 | q)
396 | e3a := addmod(e3a, mload(add(pMem, pGamma)), q)
397 |
398 | let e3b := addmod(
399 | calldataload(pEval_b),
400 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q),
401 | q)
402 | e3b := addmod(e3b, mload(add(pMem, pGamma)), q)
403 |
404 | let e3c := addmod(
405 | calldataload(pEval_c),
406 | mload(add(pMem, pGamma)),
407 | q)
408 |
409 | let e3 := mulmod(mulmod(e3a, e3b, q), e3c, q)
410 | e3 := mulmod(e3, calldataload(pEval_zw), q)
411 | e3 := mulmod(e3, mload(add(pMem, pAlpha)), q)
412 |
413 | let r0 := addmod(e1, mod(sub(q, e2), q), q)
414 | r0 := addmod(r0, mod(sub(q, e3), q), q)
415 |
416 | mstore(add(pMem, pEval_r0) , r0)
417 | }
418 |
419 | function g1_set(pR, pP) {
420 | mstore(pR, mload(pP))
421 | mstore(add(pR, 32), mload(add(pP,32)))
422 | }
423 |
424 | function g1_setC(pR, x, y) {
425 | mstore(pR, x)
426 | mstore(add(pR, 32), y)
427 | }
428 |
429 | function g1_calldataSet(pR, pP) {
430 | mstore(pR, calldataload(pP))
431 | mstore(add(pR, 32), calldataload(add(pP, 32)))
432 | }
433 |
434 | function g1_acc(pR, pP) {
435 | let mIn := mload(0x40)
436 | mstore(mIn, mload(pR))
437 | mstore(add(mIn,32), mload(add(pR, 32)))
438 | mstore(add(mIn,64), mload(pP))
439 | mstore(add(mIn,96), mload(add(pP, 32)))
440 |
441 | let success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
442 |
443 | if iszero(success) {
444 | mstore(0, 0)
445 | return(0,0x20)
446 | }
447 | }
448 |
449 | function g1_mulAcc(pR, pP, s) {
450 | let success
451 | let mIn := mload(0x40)
452 | mstore(mIn, mload(pP))
453 | mstore(add(mIn,32), mload(add(pP, 32)))
454 | mstore(add(mIn,64), s)
455 |
456 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
457 |
458 | if iszero(success) {
459 | mstore(0, 0)
460 | return(0,0x20)
461 | }
462 |
463 | mstore(add(mIn,64), mload(pR))
464 | mstore(add(mIn,96), mload(add(pR, 32)))
465 |
466 | success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
467 |
468 | if iszero(success) {
469 | mstore(0, 0)
470 | return(0,0x20)
471 | }
472 |
473 | }
474 |
475 | function g1_mulAccC(pR, x, y, s) {
476 | let success
477 | let mIn := mload(0x40)
478 | mstore(mIn, x)
479 | mstore(add(mIn,32), y)
480 | mstore(add(mIn,64), s)
481 |
482 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
483 |
484 | if iszero(success) {
485 | mstore(0, 0)
486 | return(0,0x20)
487 | }
488 |
489 | mstore(add(mIn,64), mload(pR))
490 | mstore(add(mIn,96), mload(add(pR, 32)))
491 |
492 | success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
493 |
494 | if iszero(success) {
495 | mstore(0, 0)
496 | return(0,0x20)
497 | }
498 | }
499 |
500 | function g1_mulSetC(pR, x, y, s) {
501 | let success
502 | let mIn := mload(0x40)
503 | mstore(mIn, x)
504 | mstore(add(mIn,32), y)
505 | mstore(add(mIn,64), s)
506 |
507 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, pR, 64)
508 |
509 | if iszero(success) {
510 | mstore(0, 0)
511 | return(0,0x20)
512 | }
513 | }
514 |
515 | function g1_mulSet(pR, pP, s) {
516 | g1_mulSetC(pR, mload(pP), mload(add(pP, 32)), s)
517 | }
518 |
519 | function calculateD(pMem) {
520 | let _pD:= add(pMem, pD)
521 | let gamma := mload(add(pMem, pGamma))
522 | let mIn := mload(0x40)
523 | mstore(0x40, add(mIn, 256)) // d1, d2, d3 & d4 (4*64 bytes)
524 |
525 | g1_setC(_pD, Qcx, Qcy)
526 | g1_mulAccC(_pD, Qmx, Qmy, mulmod(calldataload(pEval_a), calldataload(pEval_b), q))
527 | g1_mulAccC(_pD, Qlx, Qly, calldataload(pEval_a))
528 | g1_mulAccC(_pD, Qrx, Qry, calldataload(pEval_b))
529 | g1_mulAccC(_pD, Qox, Qoy, calldataload(pEval_c))
530 |
531 | let betaxi := mload(add(pMem, pBetaXi))
532 | let val1 := addmod(
533 | addmod(calldataload(pEval_a), betaxi, q),
534 | gamma, q)
535 |
536 | let val2 := addmod(
537 | addmod(
538 | calldataload(pEval_b),
539 | mulmod(betaxi, k1, q),
540 | q), gamma, q)
541 |
542 | let val3 := addmod(
543 | addmod(
544 | calldataload(pEval_c),
545 | mulmod(betaxi, k2, q),
546 | q), gamma, q)
547 |
548 | let d2a := mulmod(
549 | mulmod(mulmod(val1, val2, q), val3, q),
550 | mload(add(pMem, pAlpha)),
551 | q
552 | )
553 |
554 | let d2b := mulmod(
555 | mload(add(pMem, pEval_l1)),
556 | mload(add(pMem, pAlpha2)),
557 | q
558 | )
559 |
560 | // We'll use mIn to save d2
561 | g1_calldataSet(add(mIn, 192), pZ)
562 | g1_mulSet(
563 | mIn,
564 | add(mIn, 192),
565 | addmod(addmod(d2a, d2b, q), mload(add(pMem, pU)), q))
566 |
567 |
568 | val1 := addmod(
569 | addmod(
570 | calldataload(pEval_a),
571 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q),
572 | q), gamma, q)
573 |
574 | val2 := addmod(
575 | addmod(
576 | calldataload(pEval_b),
577 | mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q),
578 | q), gamma, q)
579 |
580 | val3 := mulmod(
581 | mulmod(mload(add(pMem, pAlpha)), mload(add(pMem, pBeta)), q),
582 | calldataload(pEval_zw), q)
583 |
584 |
585 | // We'll use mIn + 64 to save d3
586 | g1_mulSetC(
587 | add(mIn, 64),
588 | S3x,
589 | S3y,
590 | mulmod(mulmod(val1, val2, q), val3, q))
591 |
592 | // We'll use mIn + 128 to save d4
593 | g1_calldataSet(add(mIn, 128), pT1)
594 |
595 | g1_mulAccC(add(mIn, 128), calldataload(pT2), calldataload(add(pT2, 32)), mload(add(pMem, pXin)))
596 | let xin2 := mulmod(mload(add(pMem, pXin)), mload(add(pMem, pXin)), q)
597 | g1_mulAccC(add(mIn, 128), calldataload(pT3), calldataload(add(pT3, 32)) , xin2)
598 |
599 | g1_mulSetC(add(mIn, 128), mload(add(mIn, 128)), mload(add(mIn, 160)), mload(add(pMem, pZh)))
600 |
601 | mstore(add(add(mIn, 64), 32), mod(sub(qf, mload(add(add(mIn, 64), 32))), qf))
602 | mstore(add(mIn, 160), mod(sub(qf, mload(add(mIn, 160))), qf))
603 | g1_acc(_pD, mIn)
604 | g1_acc(_pD, add(mIn, 64))
605 | g1_acc(_pD, add(mIn, 128))
606 | }
607 |
608 | function calculateF(pMem) {
609 | let p := add(pMem, pF)
610 |
611 | g1_set(p, add(pMem, pD))
612 | g1_mulAccC(p, calldataload(pA), calldataload(add(pA, 32)), mload(add(pMem, pV1)))
613 | g1_mulAccC(p, calldataload(pB), calldataload(add(pB, 32)), mload(add(pMem, pV2)))
614 | g1_mulAccC(p, calldataload(pC), calldataload(add(pC, 32)), mload(add(pMem, pV3)))
615 | g1_mulAccC(p, S1x, S1y, mload(add(pMem, pV4)))
616 | g1_mulAccC(p, S2x, S2y, mload(add(pMem, pV5)))
617 | }
618 |
619 | function calculateE(pMem) {
620 | let s := mod(sub(q, mload(add(pMem, pEval_r0))), q)
621 |
622 | s := addmod(s, mulmod(calldataload(pEval_a), mload(add(pMem, pV1)), q), q)
623 | s := addmod(s, mulmod(calldataload(pEval_b), mload(add(pMem, pV2)), q), q)
624 | s := addmod(s, mulmod(calldataload(pEval_c), mload(add(pMem, pV3)), q), q)
625 | s := addmod(s, mulmod(calldataload(pEval_s1), mload(add(pMem, pV4)), q), q)
626 | s := addmod(s, mulmod(calldataload(pEval_s2), mload(add(pMem, pV5)), q), q)
627 | s := addmod(s, mulmod(calldataload(pEval_zw), mload(add(pMem, pU)), q), q)
628 |
629 | g1_mulSetC(add(pMem, pE), G1x, G1y, s)
630 | }
631 |
632 | function checkPairing(pMem) -> isOk {
633 | let mIn := mload(0x40)
634 | mstore(0x40, add(mIn, 576)) // [0..383] = pairing data, [384..447] = pWxi, [448..512] = pWxiw
635 |
636 | let _pWxi := add(mIn, 384)
637 | let _pWxiw := add(mIn, 448)
638 | let _aux := add(mIn, 512)
639 |
640 | g1_calldataSet(_pWxi, pWxi)
641 | g1_calldataSet(_pWxiw, pWxiw)
642 |
643 | // A1
644 | g1_mulSet(mIn, _pWxiw, mload(add(pMem, pU)))
645 | g1_acc(mIn, _pWxi)
646 | mstore(add(mIn, 32), mod(sub(qf, mload(add(mIn, 32))), qf))
647 |
648 | // [X]_2
649 | mstore(add(mIn,64), X2x2)
650 | mstore(add(mIn,96), X2x1)
651 | mstore(add(mIn,128), X2y2)
652 | mstore(add(mIn,160), X2y1)
653 |
654 | // B1
655 | g1_mulSet(add(mIn, 192), _pWxi, mload(add(pMem, pXi)))
656 |
657 | let s := mulmod(mload(add(pMem, pU)), mload(add(pMem, pXi)), q)
658 | s := mulmod(s, w1, q)
659 | g1_mulSet(_aux, _pWxiw, s)
660 | g1_acc(add(mIn, 192), _aux)
661 | g1_acc(add(mIn, 192), add(pMem, pF))
662 | mstore(add(pMem, add(pE, 32)), mod(sub(qf, mload(add(pMem, add(pE, 32)))), qf))
663 | g1_acc(add(mIn, 192), add(pMem, pE))
664 |
665 | // [1]_2
666 | mstore(add(mIn,256), G2x2)
667 | mstore(add(mIn,288), G2x1)
668 | mstore(add(mIn,320), G2y2)
669 | mstore(add(mIn,352), G2y1)
670 |
671 | let success := staticcall(sub(gas(), 2000), 8, mIn, 384, mIn, 0x20)
672 |
673 | isOk := and(success, mload(mIn))
674 | }
675 |
676 | let pMem := mload(0x40)
677 | mstore(0x40, add(pMem, lastMem))
678 |
679 | checkInput()
680 | calculateChallenges(pMem, _pubSignals)
681 | calculateLagrange(pMem)
682 | calculatePI(pMem, _pubSignals)
683 | calculateR0(pMem)
684 | calculateD(pMem)
685 | calculateF(pMem)
686 | calculateE(pMem)
687 | let isValid := checkPairing(pMem)
688 |
689 | mstore(0x40, sub(pMem, lastMem))
690 | mstore(0, isValid)
691 | return(0,0x20)
692 | }
693 |
694 | }
695 | }
696 |
--------------------------------------------------------------------------------
/hardhat.config.ts:
--------------------------------------------------------------------------------
1 | import { HardhatUserConfig } from "hardhat/config";
2 | import "@nomicfoundation/hardhat-toolbox";
3 |
4 | const config: HardhatUserConfig = {
5 | defaultNetwork: "hardhat",
6 | networks: {
7 | hardhat: {
8 | chainId: 31337,
9 | },
10 | localhost: {
11 | chainId: 31337,
12 | },
13 | },
14 | solidity: {
15 | compilers: [
16 | {
17 | version: "0.8.9",
18 | },
19 | ],
20 | settings: {
21 | optimizer: {
22 | enabled: true,
23 | runs: 200,
24 | },
25 | },
26 | },
27 | paths: {
28 | sources: "./contracts",
29 | tests: "./test",
30 | cache: "./build/cache",
31 | artifacts: "./build/artifacts",
32 | },
33 | mocha: {
34 | timeout: 180000, // 3 mins max for running tests
35 | },
36 | };
37 |
38 | export default config;
39 |
--------------------------------------------------------------------------------
/images/arrival_louis_has_weapon.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finitology/hardhat-circom-starter/6ab36475770162951c576b6df52c59d2783277b4/images/arrival_louis_has_weapon.jpeg
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module "snarkjs";
2 | declare module "ffjavascript";
3 | declare module "circomlibjs";
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hardhat-circom-starter",
3 | "version": "1.0.0",
4 | "description": "ZK-Snarks with Circom, hardhat and typescript",
5 | "author": {
6 | "name": "Ganesh Anantwar",
7 | "email": "ganesh.anantwar@outlook.com"
8 | },
9 | "keywords": [
10 | "zk-snarks",
11 | "zksnarks",
12 | "zk-proof",
13 | "circom",
14 | "zksnark",
15 | "zk",
16 | "snarks",
17 | "snark",
18 | "zkp",
19 | "zk-proofs",
20 | "zk-snark",
21 | "zk-snarks",
22 | "plonk",
23 | "powers-of-tau",
24 | "circuits",
25 | "verifier",
26 | "prover",
27 | "BN254",
28 | "BN128",
29 | "alt-bn128"
30 | ],
31 | "scripts": {
32 | "install": "bash ./scripts/bash/0_install_circom.sh",
33 | "setup": "bash ./scripts/bash/1_plonk_setup.sh",
34 | "build": "bash ./scripts/bash/2_build_circuits.sh && npx hardhat compile",
35 | "test": "npx hardhat test",
36 | "lint": "echo \"work in progress\""
37 | },
38 | "devDependencies": {
39 | "@nomicfoundation/hardhat-toolbox": "^2.0.1",
40 | "@types/node": "^18.15.3",
41 | "hardhat": "^2.12.6"
42 | },
43 | "dependencies": {
44 | "circomlib": "^2.0.5",
45 | "circomlibjs": "^0.1.7",
46 | "ffjavascript": "^0.2.63",
47 | "snarkjs": "^0.7.5"
48 | },
49 | "engines": {
50 | "node": ">=16.0.0",
51 | "npm": ">=7.10.0"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/scripts/bash/0_install_circom.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | VERSION="2.1.6"
4 |
5 | if [[ "$OSTYPE" == "linux-gnu"* ]]; then
6 | echo "installing circom v2.1.6 on Linux"
7 | mkdir ~/bin
8 | cd ~/bin
9 | if [ -d "circom" ]; then
10 | rm -rf circom
11 | fi
12 | wget https://github.com/iden3/circom/releases/download/v${VERSION}/circom-linux-amd64
13 | mv circom-linux-amd64 circom
14 | chmod +x circom
15 | circom --help
16 | echo "installed circom, please make sure /home/${USER}/bin is in your PATH"
17 | elif [[ "$OSTYPE" == "darwin"* ]]; then
18 | echo "installing circom v2.1.6 on MacOS"
19 | sudo mv ./releases/2.1.3/circom-macos-amd64 /usr/local/bin/circom
20 | circom --help
21 | fi
22 |
--------------------------------------------------------------------------------
/scripts/bash/1_plonk_setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # CONTRIBUTIONS=3
4 | RANDOM_BEACON=0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
5 | CURVE=bn128
6 | # CONSTRAINTS=16
7 | LOCAL_PTAU_PATH="./setup/local/"
8 | LOCAL_PTAU_PREFIX="powersOfTau_local_"
9 | HERMEZ_PTAU_PATH="./setup/hermez/"
10 |
11 | echo "Choose the powersOfTau ceremony to generate trusted-setup public parameters:"
12 | echo "0. I want to run my own local trusted-setup"
13 | echo "1. Polygon Hermez/zkEVM"
14 | read CEREMONY
15 |
16 | echo "How many constraints your circuits will have?"
17 | echo "Please type a number between 10 and 28"
18 | echo "The maximum constraints your circuits can have are 2^number"
19 | read CONSTRAINTS
20 |
21 |
22 | if [ "$CEREMONY" == 0 ]; then
23 | if [ -d "$LOCAL_PTAU_PATH" ]; then
24 | rm -rf ${LOCAL_PTAU_PATH}/PowersOfTau*
25 | fi
26 | echo "How many random contributions you need for local setup"
27 | echo "Please note higher the contributions, more time it will take for ceremony to complete"
28 | echo "Recommended contributions are between 3 to 10 for local testing"
29 | read CONTRIBUTIONS
30 |
31 | echo "initiating local powersOfTau ceremony on curve ${CURVE} with max 2^${CONSTRAINTS}"
32 | echo "WARNING: this trusted-setup ceremony is only for development and does not provide production grade security"
33 | npx snarkjs powersoftau new ${CURVE} ${CONSTRAINTS} "${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}0.ptau" -v
34 |
35 | for ((i=1; i<=$CONTRIBUTIONS; i++)); do
36 | echo "Applying random contribution $i"
37 | echo "Old ptau file: ${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}$(($i-1)).ptau ---> New ptau file: ${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}${i}.ptau"
38 | npx snarkjs powersoftau contribute "${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}$(($i-1)).ptau" "${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}${i}.ptau" -n="contribution_0${i}" -v -e=$(openssl rand -base64 32)
39 | rm -rf "${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}$(($i-1)).ptau"
40 | done
41 |
42 | echo "applying random beacon to local powersOfTau ceremony"
43 | npx snarkjs powersoftau beacon "${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}${CONTRIBUTIONS}.ptau" "${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}beacon.ptau" ${RANDOM_BEACON} 10 -n="final_beacon"
44 | rm -rf "${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}${CONTRIBUTIONS}.ptau"
45 |
46 | echo "finalizing local powersOfTau ceremony"
47 | npx snarkjs powersoftau prepare phase2 "${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}beacon.ptau" "${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}final.ptau" -v
48 | rm -rf "${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}beacon.ptau"
49 |
50 | echo "verifying local powersOfTau ceremony"
51 | npx snarkjs powersoftau verify "${LOCAL_PTAU_PATH}${LOCAL_PTAU_PREFIX}final.ptau"
52 |
53 | echo "ptau generation complete!"
54 | elif [ "$CEREMONY" == 1 ]; then
55 | cd ${HERMEZ_PTAU_PATH}
56 | echo "downloading setup file with 2^20 constraints from Polygon Hermez/zkEVM setup ceremony"
57 | wget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_${CONSTRAINTS}.ptau
58 | mv powersOfTau28_hez_final_${CONSTRAINTS}.ptau powersOfTau_hez_final.ptau
59 | echo "ptau download complete!"
60 | fi
61 |
62 |
63 |
--------------------------------------------------------------------------------
/scripts/bash/2_build_circuits.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | CIRCUIT_PATH="./circuits/"
4 | BUILD_PATH="./build/circuits/"
5 | VERIFIER_PATH="./contracts/verifiers/"
6 |
7 | if [ -d "$BUILD_PATH" ]; then
8 | rm -r ${BUILD_PATH}
9 | fi
10 | mkdir -p ${BUILD_PATH}
11 |
12 | echo "Choose the trusted-setup used to compile your circuits:"
13 | echo "0. Local Setup"
14 | echo "1. Polygon Hermez/zkEVM"
15 | read CEREMONY
16 |
17 | if [ "$CEREMONY" == 0 ]; then
18 | PTAU_FINAL_PATH="./setup/local/powersOfTau_local_final.ptau"
19 | elif [ "$CEREMONY" == 1 ]; then
20 | PTAU_FINAL_PATH="./setup/hermez/powersOfTau_hez_final.ptau"
21 | fi
22 |
23 | for f in $(find ${CIRCUIT_PATH} -maxdepth 1 -name '*.circom'); do
24 | FILE_FULLNAME=$(basename $f)
25 | FILE_BASENAME=$(basename $f .circom)
26 | BUILD_DIR=${BUILD_PATH}${FILE_BASENAME}
27 |
28 | echo ------------------------------------------------------------
29 | echo Building circuit: ${FILE_FULLNAME}
30 | echo ------------------------------------------------------------
31 | mkdir -p ${BUILD_DIR}/
32 | circom $f --wasm --r1cs --prime bn128 --output ${BUILD_DIR}/
33 | mv ${BUILD_DIR}/${FILE_BASENAME}_js/${FILE_BASENAME}.wasm ${BUILD_DIR}/${FILE_BASENAME}.wasm
34 | rm -r ${BUILD_DIR}/${FILE_BASENAME}_js/
35 | npx snarkjs plonk setup ${BUILD_DIR}/${FILE_BASENAME}.r1cs ${PTAU_FINAL_PATH} ${BUILD_DIR}/${FILE_BASENAME}.zkey
36 | npx snarkjs zkey export verificationkey ${BUILD_DIR}/${FILE_BASENAME}.zkey ${BUILD_DIR}/${FILE_BASENAME}.vkey.json
37 | npx snarkjs zkey export solidityverifier ${BUILD_DIR}/${FILE_BASENAME}.zkey ${BUILD_DIR}/${FILE_BASENAME}Verifier.sol
38 | sed -i -e "s/PlonkVerifier/${FILE_BASENAME}Verifier/g" ${BUILD_DIR}/${FILE_BASENAME}Verifier.sol
39 | rm -r ${VERIFIER_PATH}/${FILE_BASENAME}Verifier.sol
40 | cp ${BUILD_DIR}/${FILE_BASENAME}Verifier.sol ${VERIFIER_PATH}/${FILE_BASENAME}Verifier.sol
41 | done
--------------------------------------------------------------------------------
/scripts/deploy.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from "hardhat";
2 | import { readdirSync } from "fs";
3 | import { resolve } from "path";
4 |
5 | async function main() {
6 | const [deployer] = await ethers.getSigners();
7 | let Verifier;
8 | let verifier;
9 | let verifierList: string[] = readdirSync(resolve(__dirname, `../contracts/verifiers`));
10 | verifierList.shift();
11 | let contractListPromises = verifierList.map(async (v) => {
12 | let name = v.split(".")[0];
13 | Verifier = await ethers.getContractFactory(name, deployer);
14 | verifier = await Verifier.deploy();
15 | await verifier.deployed();
16 | console.log(`${name} deployed to ${verifier.address}`);
17 | });
18 | await Promise.all(contractListPromises);
19 | }
20 |
21 | main().catch((error) => {
22 | console.error(error);
23 | process.exitCode = 1;
24 | });
25 |
--------------------------------------------------------------------------------
/setup/hermez/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finitology/hardhat-circom-starter/6ab36475770162951c576b6df52c59d2783277b4/setup/hermez/.gitkeep
--------------------------------------------------------------------------------
/setup/hermez/powersOfTau_hez_final.ptau:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finitology/hardhat-circom-starter/6ab36475770162951c576b6df52c59d2783277b4/setup/hermez/powersOfTau_hez_final.ptau
--------------------------------------------------------------------------------
/setup/local/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finitology/hardhat-circom-starter/6ab36475770162951c576b6df52c59d2783277b4/setup/local/.gitkeep
--------------------------------------------------------------------------------
/setup/local/powersOfTau_local_final.ptau:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/finitology/hardhat-circom-starter/6ab36475770162951c576b6df52c59d2783277b4/setup/local/powersOfTau_local_final.ptau
--------------------------------------------------------------------------------
/test/hasher.ts:
--------------------------------------------------------------------------------
1 | import { time, loadFixture } from "@nomicfoundation/hardhat-network-helpers";
2 | import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs";
3 | import { expect } from "chai";
4 | import { ethers } from "hardhat";
5 | import { Circuit } from "../utils/Circuit";
6 | import { randomField, poseidon } from "../utils/Circomlib";
7 | import { HasherVerifier, HasherVerifier__factory } from "../typechain-types";
8 |
9 | describe("Hasher circuit tests", function () {
10 | const deployVerifier = async () => {
11 | const [deployer, relayer] = await ethers.getSigners();
12 | const Verifier: HasherVerifier__factory = await ethers.getContractFactory("HasherVerifier", deployer);
13 | const verifier: HasherVerifier = await Verifier.deploy();
14 | await verifier.deployed();
15 | console.log(`Verifier contract deployed to ${verifier.address} from deployer ${deployer.address}`);
16 | return { verifier, deployer, relayer };
17 | };
18 |
19 | describe("Verify Offchain in Typescript", function () {
20 | it("Should verify the zksnark for correct witness & circom-generated public signals", async function () {
21 | const Hasher = new Circuit("Hasher");
22 | const x: string[] = [randomField(), randomField()];
23 | const { offchain } = await Hasher.generateProof({ x: x });
24 | const { proof, publicSignals } = offchain;
25 | let verify;
26 | verify = await Hasher.verifyProof(proof, publicSignals);
27 | expect(verify).to.be.true;
28 | });
29 |
30 | it("Should verify the zksnark for correct witness & typescript-generated public signals", async function () {
31 | const Hasher = new Circuit("Hasher");
32 | const x: string[] = [randomField(), randomField()];
33 | const { offchain } = await Hasher.generateProof({ x: x });
34 | const { proof, publicSignals } = offchain;
35 | let verify;
36 | const y: string = await poseidon(x);
37 | verify = await Hasher.verifyProof(proof, [y]);
38 | expect(verify).to.be.true;
39 | });
40 |
41 | it("Should not verify zk-snark for incorrect witness", async function () {
42 | const Hasher = new Circuit("Hasher");
43 | const x: string[] = [randomField(), randomField()];
44 | const { offchain } = await Hasher.generateProof({ x: x });
45 | const { proof, publicSignals } = offchain;
46 | const verify = await Hasher.verifyProof(proof, [randomField()]);
47 | expect(verify).to.be.false;
48 | });
49 | });
50 |
51 | describe("Verify Onchain in Solidity", function () {
52 | it("Should verify the zksnark for correct witness & circom-generated public signals", async function () {
53 | const { verifier, deployer, relayer } = await loadFixture(deployVerifier);
54 | const Hasher = new Circuit("Hasher");
55 | const x: string[] = [randomField(), randomField()];
56 | const { onchain } = await Hasher.generateProof({ x: x });
57 | const { proof, publicSignals } = onchain;
58 | let verify;
59 | verify = await verifier.connect(relayer).verifyProof(proof, publicSignals);
60 | expect(verify).to.be.true;
61 | });
62 |
63 | it("Should verify the zksnark for correct witness & typescript-generated public signals", async function () {
64 | const { verifier, deployer, relayer } = await loadFixture(deployVerifier);
65 | const Hasher = new Circuit("Hasher");
66 | const x: string[] = [randomField(), randomField()];
67 | const { onchain } = await Hasher.generateProof({ x: x });
68 | const { proof, publicSignals } = onchain;
69 | let verify;
70 | const y: string = await poseidon(x);
71 | verify = await verifier.connect(relayer).verifyProof(proof, [y]);
72 | expect(verify).to.be.true;
73 | });
74 |
75 | it("Should not verify zk-snark for incorrect witness", async function () {
76 | const { verifier, deployer, relayer } = await loadFixture(deployVerifier);
77 | const Hasher = new Circuit("Hasher");
78 | const x: string[] = [randomField(), randomField()];
79 | const { onchain } = await Hasher.generateProof({ x: x });
80 | const { proof, publicSignals } = onchain;
81 | const verify = await verifier.connect(relayer).verifyProof(proof, [randomField()]);
82 | expect(verify).to.be.false;
83 | });
84 | });
85 | });
86 |
--------------------------------------------------------------------------------
/test/pubkey.ts:
--------------------------------------------------------------------------------
1 | import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
2 | import { expect } from "chai";
3 | import { ethers } from "hardhat";
4 | import { Circuit } from "../utils/Circuit";
5 | import { randomField, pointMulBase, BabyJubJubPoint } from "../utils/Circomlib";
6 | import { PubkeyVerifier, PubkeyVerifier__factory } from "../typechain-types";
7 |
8 | describe("Pubkey circuit tests", function () {
9 | const deployVerifier = async () => {
10 | const [deployer, relayer] = await ethers.getSigners();
11 | const Verifier: PubkeyVerifier__factory = await ethers.getContractFactory("PubkeyVerifier", deployer);
12 | const verifier: PubkeyVerifier = await Verifier.deploy();
13 | await verifier.deployed();
14 | // console.log(`Verifier contract deployed to ${verifier.address}`);
15 | return { verifier, deployer, relayer };
16 | };
17 |
18 | describe("Verify Offchain", function () {
19 | it("Should verify the zksnark for correct witness & circom-generated public signals", async function () {
20 | const Pubkey = new Circuit("Pubkey");
21 | const sk: string = randomField();
22 | const { proof, publicSignals } = (await Pubkey.generateProof({ sk: sk })).offchain;
23 | let verify;
24 | verify = await Pubkey.verifyProof(proof, publicSignals);
25 | expect(verify).to.be.true;
26 | });
27 |
28 | it("Should verify the zksnark for correct witness & typescript-generated public signals", async function () {
29 | const Pubkey = new Circuit("Pubkey");
30 | const sk: string = randomField();
31 | const { proof, publicSignals } = (await Pubkey.generateProof({ sk: sk })).offchain;
32 | let verify;
33 | const pk: BabyJubJubPoint = await pointMulBase(sk);
34 | verify = await Pubkey.verifyProof(proof, [pk.x, pk.y]);
35 | expect(verify).to.be.true;
36 | });
37 |
38 | it("Should not verify zk-snark for incorrect witness", async function () {
39 | const Pubkey = new Circuit("Pubkey");
40 | const sk: string = randomField();
41 | const { proof, publicSignals } = (await Pubkey.generateProof({ sk: sk })).offchain;
42 | const verify = await Pubkey.verifyProof(proof, [randomField(), randomField()]);
43 | expect(verify).to.be.false;
44 | });
45 | });
46 |
47 | describe("Verify Onchain", function () {
48 | it("Should verify the zksnark for correct witness & circom-generated public signals", async function () {
49 | const { verifier, deployer, relayer } = await loadFixture(deployVerifier);
50 | const Pubkey = new Circuit("Pubkey");
51 | const sk: string = randomField();
52 | const { proof, publicSignals } = (await Pubkey.generateProof({ sk: sk })).onchain;
53 | let verify;
54 | verify = await verifier.connect(relayer).verifyProof(proof, publicSignals);
55 | expect(verify).to.be.true;
56 | });
57 |
58 | it("Should verify the zksnark for correct witness & typescript-generated public signals", async function () {
59 | const { verifier, deployer, relayer } = await loadFixture(deployVerifier);
60 | const Pubkey = new Circuit("Pubkey");
61 | const sk: string = randomField();
62 | const { proof, publicSignals } = (await Pubkey.generateProof({ sk: sk })).onchain;
63 | let verify;
64 | const pk: BabyJubJubPoint = await pointMulBase(sk);
65 | verify = await verifier.connect(relayer).verifyProof(proof, [pk.x, pk.y]);
66 | expect(verify).to.be.true;
67 | });
68 |
69 | it("Should not verify zk-snark for incorrect witness", async function () {
70 | const { verifier, deployer, relayer } = await loadFixture(deployVerifier);
71 | const Pubkey = new Circuit("Pubkey");
72 | const sk: string = randomField();
73 | const { proof, publicSignals } = (await Pubkey.generateProof({ sk: sk })).onchain;
74 | const verify = await verifier.connect(relayer).verifyProof(proof, [randomField(), randomField()]);
75 | expect(verify).to.be.false;
76 | });
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2020",
4 | "module": "commonjs",
5 | "esModuleInterop": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "strict": true,
8 | "skipLibCheck": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/utils/Circomlib.ts:
--------------------------------------------------------------------------------
1 | import { randomBytes } from "crypto";
2 | import { buildBabyjub, buildPoseidon } from "circomlibjs";
3 | import { Scalar } from "ffjavascript";
4 |
5 | // constrain x and y points to BabyJubJub scalar field
6 | export type BabyJubJubPoint = {
7 | x: string;
8 | y: string;
9 | };
10 |
11 | export const randomField = (): string => {
12 | // return Scalar.e(BigInt(`0x${randomBytes(31).toString("hex")}`).toString()).toString();
13 | const random = BigInt(`0x${randomBytes(32).toString("hex")}`) >> BigInt(4);
14 | return `0x${random.toString(16).padStart(64, "0")}`;
15 | };
16 |
17 | export const poseidon = async (inputs: string[]) => {
18 | const poseidon = await buildPoseidon();
19 | const field = poseidon.F;
20 | const result = poseidon(inputs);
21 | return field.toObject(result).toString();
22 | };
23 |
24 | // export const pointAdd = (point1: BabyJubJubPoint, point2: BabyJubJubPoint): Promise => {};
25 |
26 | export const pointMulBase = async (scalar: string): Promise => {
27 | const babyjubjub = await buildBabyjub();
28 | const field = babyjubjub.F;
29 | const base8 = [
30 | field.e("5299619240641551281634865583518297030282874472190772894086521144482721001553"),
31 | field.e("16950150798460657717958625567821834550301663161624707787222815936182638968203"),
32 | ];
33 | const result = babyjubjub.mulPointEscalar(base8, scalar);
34 | return { x: field.toObject(result[0]).toString(), y: field.toObject(result[1]).toString() };
35 | };
36 |
37 | // export const pointMulAny = (point: BabyJubJubPoint, scalar: string): Promise => {};
38 |
--------------------------------------------------------------------------------
/utils/Circuit.ts:
--------------------------------------------------------------------------------
1 | import { readFileSync } from "fs";
2 | import { resolve } from "path";
3 | import { plonk } from "snarkjs";
4 |
5 | export class Circuit {
6 | circuit: string;
7 | wasmPath: string;
8 | zkeyPath: string;
9 | vkey: any;
10 |
11 | constructor(circuit: string) {
12 | this.circuit = circuit;
13 | this.vkey = JSON.parse(
14 | readFileSync(resolve(__dirname, `../build/circuits/${circuit}/${circuit}.vkey.json`), `utf-8`)
15 | );
16 | this.zkeyPath = resolve(__dirname, `../build/circuits/${circuit}/${circuit}.zkey`);
17 | this.wasmPath = resolve(__dirname, `../build/circuits/${circuit}/${circuit}.wasm`);
18 | }
19 |
20 | async generateProof(inputs: any): Promise {
21 | const { proof, publicSignals } = await plonk.fullProve(inputs, this.wasmPath, this.zkeyPath);
22 | const calldata: string = (await plonk.exportSolidityCallData(proof, publicSignals)).split("][");
23 | return {
24 | offchain: { proof: proof, publicSignals: publicSignals },
25 | onchain: { proof: JSON.parse(calldata[0] + "]"), publicSignals: JSON.parse("[" + calldata[1]) },
26 | };
27 | }
28 |
29 | async verifyProof(proofJson: any, publicSignals: any): Promise {
30 | const verify = await plonk.verify(this.vkey, publicSignals, proofJson);
31 | return verify;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------