├── .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 | Project logo 4 |

5 | 6 |

ZK-SNARKS with Circom Starter Kit

7 | 8 |
9 | 10 | [![Status](https://img.shields.io/badge/status-active-success.svg)]() 11 | [![GitHub Issues](https://img.shields.io/github/issues/kylelobo/The-Documentation-Compendium.svg)](https://github.com/infinitywarg/zksnark-starter/issues) 12 | [![GitHub Pull Requests](https://img.shields.io/github/issues-pr/kylelobo/The-Documentation-Compendium.svg)](https://github.com/infinitywarg/zksnark-starter/pulls) 13 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](/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 | --------------------------------------------------------------------------------