├── types.d.ts ├── .eslintrc.json ├── wasm_tester ├── index.js └── utils.js ├── contracts ├── lib │ └── forge-std │ │ ├── .gitignore │ │ ├── lib │ │ └── ds-test │ │ │ ├── .gitignore │ │ │ ├── default.nix │ │ │ ├── Makefile │ │ │ ├── package.json │ │ │ └── .github │ │ │ └── workflows │ │ │ └── build.yml │ │ ├── .gitmodules │ │ ├── test │ │ ├── compilation │ │ │ ├── CompilationTest.sol │ │ │ ├── CompilationScript.sol │ │ │ ├── CompilationTestBase.sol │ │ │ └── CompilationScriptBase.sol │ │ ├── StdError.t.sol │ │ ├── StdStyle.t.sol │ │ └── StdChains.t.sol │ │ ├── package.json │ │ ├── src │ │ ├── interfaces │ │ │ ├── IERC165.sol │ │ │ ├── IERC20.sol │ │ │ ├── IMulticall3.sol │ │ │ └── IERC1155.sol │ │ ├── Script.sol │ │ ├── StdError.sol │ │ ├── Test.sol │ │ ├── StdMath.sol │ │ ├── Base.sol │ │ ├── StdInvariant.sol │ │ └── StdJson.sol │ │ ├── foundry.toml │ │ ├── LICENSE-MIT │ │ └── .github │ │ └── workflows │ │ └── ci.yml ├── foundry.toml ├── readme.md ├── script │ └── SimpleMultiplier.s.sol └── src │ ├── SimpleMultiplier.sol │ └── WhiteList.sol ├── public ├── Zkblind.png ├── favicon.ico ├── welcome.png ├── static │ ├── logo.jpg │ ├── images │ │ ├── FP_BG_Transparent.png │ │ ├── FP_BG_768_Transparent.png │ │ └── FP_BG_991_Transparent_2.png │ ├── FP_NavLine_2pt.svg │ ├── FP_Twitter.svg │ ├── FP_Discord.svg │ └── Fareprotocol-primary.svg ├── vercel.svg └── next.svg ├── src ├── styles │ └── globals.css ├── components │ ├── Layout.js │ └── Navbar.js ├── pages │ ├── _document.tsx │ ├── api │ │ └── generate_proof.ts │ ├── _app.tsx │ ├── 404.tsx │ ├── whitelist.tsx │ ├── example.tsx │ └── zkblind.tsx ├── shared │ └── addresses.tsx ├── utils │ └── contracts.tsx └── lib │ ├── abi │ ├── IVerifier.json │ └── AddWhitelist.json │ ├── executeTransaction.ts │ ├── generateProof.ts │ └── addWhitelistTransaction.ts ├── circuits ├── eff_ecdsa │ ├── ecdsa_verify.circom │ ├── test │ │ ├── ecdsa_verify.ts │ │ ├── eff_ecdsa_to_addr.ts │ │ └── gmail.eml │ ├── secp256k1 │ │ ├── double.circom │ │ ├── add.circom │ │ └── mul.circom │ ├── eff_ecdsa.circom │ ├── eff_ecdsa_to_addr.circom │ └── to_address │ │ ├── vocdoni-keccak │ │ ├── utils.circom │ │ └── keccak.circom │ │ └── zk-identity │ │ └── eth.circom ├── email-suffix │ ├── test │ │ ├── email-suffix.circom │ │ └── email-suffix.ts │ └── email-suffix.circom ├── zkblind │ ├── test │ │ ├── zkbindTest.circom │ │ └── zkblindTest.ts │ ├── zkblind_temp.circom │ └── zkblind.circom ├── ecsda-circom │ ├── test │ │ ├── sig_verify.circom │ │ ├── eth_addr.circom │ │ ├── eth_addr.ts │ │ └── sig_verify.ts │ ├── eth_addr.circom │ ├── eth_addr_2.circom │ ├── vocdoni-keccak │ │ ├── utils.circom │ │ └── keccak.circom │ ├── bigint_4x64_mult.circom │ ├── zk-identity │ │ └── eth.circom │ ├── README.md │ └── secp256k1_func.circom ├── simple_multiplier │ ├── simple_multiplier_js │ │ └── simple_multiplier.wasm │ └── simple_multiplier.circom ├── zkblind.circom ├── multiplier2 │ ├── Multiplier2.circom │ └── test │ │ └── multiplier2.ts ├── readme.md ├── sha256 │ ├── sha256bits2032.circom │ └── test │ │ └── sha256bits2032.ts └── test_utils.ts ├── scripts ├── archived_input_eth_addr.json ├── 2_gen_wtns.sh ├── 5_gen_proof.sh ├── 1_compile.sh ├── 4_gen_vkey.sh ├── 3_gen_chunk_zkey.sh ├── archived_build_eth_addr.sh ├── generateInputs.ts └── zkBindRegistrationEmail.eml ├── esbuild-register.js ├── .mocharc.js ├── next.config.js ├── artifacts ├── public.json └── proof.json ├── .gitignore ├── tsconfig.json ├── package.json └── README.md /types.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'ffjavascript'; -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } -------------------------------------------------------------------------------- /wasm_tester/index.js: -------------------------------------------------------------------------------- 1 | export default require("./tester"); 2 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/.gitignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | out/ 3 | .vscode 4 | .idea 5 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/lib/ds-test/.gitignore: -------------------------------------------------------------------------------- 1 | /.dapple 2 | /build 3 | /out 4 | /cache/ 5 | -------------------------------------------------------------------------------- /public/Zkblind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holyaustin/ZeroSecret/HEAD/public/Zkblind.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holyaustin/ZeroSecret/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holyaustin/ZeroSecret/HEAD/public/welcome.png -------------------------------------------------------------------------------- /src/styles/globals.css: -------------------------------------------------------------------------------- 1 | a { 2 | text-decoration: none; 3 | font-weight: normal; 4 | } 5 | -------------------------------------------------------------------------------- /circuits/eff_ecdsa/ecdsa_verify.circom: -------------------------------------------------------------------------------- 1 | // output of this circom equals to the input of the address -------------------------------------------------------------------------------- /public/static/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holyaustin/ZeroSecret/HEAD/public/static/logo.jpg -------------------------------------------------------------------------------- /contracts/lib/forge-std/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/ds-test"] 2 | path = lib/ds-test 3 | url = https://github.com/dapphub/ds-test 4 | -------------------------------------------------------------------------------- /public/static/images/FP_BG_Transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holyaustin/ZeroSecret/HEAD/public/static/images/FP_BG_Transparent.png -------------------------------------------------------------------------------- /scripts/archived_input_eth_addr.json: -------------------------------------------------------------------------------- 1 | {"privkey": ["6862539325408419825", "7739665414899438580", "3575179427557022600", "11277760030985572954"]} -------------------------------------------------------------------------------- /contracts/lib/forge-std/lib/ds-test/default.nix: -------------------------------------------------------------------------------- 1 | { solidityPackage, dappsys }: solidityPackage { 2 | name = "ds-test"; 3 | src = ./src; 4 | } 5 | -------------------------------------------------------------------------------- /esbuild-register.js: -------------------------------------------------------------------------------- 1 | require('esbuild-register')({ 2 | target: 'node14', // Or the version of Node.js you are targeting 3 | format: 'esm', 4 | }); 5 | -------------------------------------------------------------------------------- /public/static/images/FP_BG_768_Transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holyaustin/ZeroSecret/HEAD/public/static/images/FP_BG_768_Transparent.png -------------------------------------------------------------------------------- /public/static/images/FP_BG_991_Transparent_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holyaustin/ZeroSecret/HEAD/public/static/images/FP_BG_991_Transparent_2.png -------------------------------------------------------------------------------- /circuits/email-suffix/test/email-suffix.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.2; 2 | 3 | include "../email-suffix.circom"; 4 | 5 | component main {public [userEmailSuffix]} = emailSuffix(56); 6 | -------------------------------------------------------------------------------- /circuits/zkblind/test/zkbindTest.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.2; 2 | 3 | include "../zkblind.circom"; 4 | 5 | component main {public [userEthAddr, userEmailSuffix, userId]} = zkBlind(56); 6 | -------------------------------------------------------------------------------- /circuits/ecsda-circom/test/sig_verify.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.2; 2 | 3 | include "../ecdsa.circom"; 4 | 5 | component main {public [r, s, msghash, pubkey]} = ECDSAVerifyNoPubkeyCheck(64, 4); 6 | -------------------------------------------------------------------------------- /circuits/simple_multiplier/simple_multiplier_js/simple_multiplier.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holyaustin/ZeroSecret/HEAD/circuits/simple_multiplier/simple_multiplier_js/simple_multiplier.wasm -------------------------------------------------------------------------------- /contracts/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | libs = ['lib'] 5 | 6 | # Add this 7 | [rpc_endpoints] 8 | goerli = "${GOERLI_RPC_URL}" 9 | mantle = "https://rpc.testnet.mantle.xyz/" -------------------------------------------------------------------------------- /src/components/Layout.js: -------------------------------------------------------------------------------- 1 | import Navbar from "./Navbar"; 2 | 3 | export function Layout({ children }) { 4 | return ( 5 | <> 6 | 7 |
{children}
8 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /circuits/zkblind.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.2; 2 | 3 | include "./zkblind/zkblind.circom"; 4 | 5 | // for wy.dong96@gmail.com, @ is at index 9, 9 * 8 = 72 6 | component main {public [userEthAddr, userEmailSuffix, userId]} = zkBlind(72); 7 | -------------------------------------------------------------------------------- /circuits/multiplier2/Multiplier2.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | template Multiplier2() { 4 | signal input a; 5 | signal input b; 6 | signal output c; 7 | c <== a*b; 8 | } 9 | 10 | component main = Multiplier2(); 11 | -------------------------------------------------------------------------------- /.mocharc.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | require: [ 5 | path.join(__dirname, 'node_modules', 'esbuild-register'), 6 | ], 7 | extension: ['ts'], 8 | spec: 'circuits/*/test/*.ts', 9 | timeout: 10000000 10 | }; -------------------------------------------------------------------------------- /src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /circuits/ecsda-circom/test/eth_addr.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.2; 2 | 3 | include "../../../node_modules/circomlib/circuits/mimcsponge.circom"; 4 | include "../../../node_modules/circomlib/circuits/bitify.circom"; 5 | include "../eth_addr_2.circom"; 6 | 7 | component main {public [privkey, publickey]} = PrivKeyToAddr(64, 4); // 4 -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | rewrites: async () => { 5 | return [ 6 | { 7 | source: "/", 8 | destination: "/index.html", 9 | } 10 | ] 11 | } 12 | } 13 | 14 | 15 | 16 | module.exports = nextConfig 17 | -------------------------------------------------------------------------------- /src/shared/addresses.tsx: -------------------------------------------------------------------------------- 1 | export const Addresses = { 2 | SIMPLE_MULTIPLIER_ADDR: 3 | "0xb22942cc1e812fd91550f19331cf8cf45d6bed09" as `0x${string}`, 4 | VERIFIER_ADDR: 5 | "0x852219c8D765a8FaEE22B6B8304e59348C55da57" as `0x${string}`, 6 | WHITELIST_ADDR: 7 | "0xa6D6f4556B022c0C7051d62E071c0ACecE5a1228" as `0x${string}`, 8 | }; 9 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/lib/ds-test/Makefile: -------------------------------------------------------------------------------- 1 | all:; dapp build 2 | 3 | test: 4 | -dapp --use solc:0.4.23 build 5 | -dapp --use solc:0.4.26 build 6 | -dapp --use solc:0.5.17 build 7 | -dapp --use solc:0.6.12 build 8 | -dapp --use solc:0.7.5 build 9 | 10 | demo: 11 | DAPP_SRC=demo dapp --use solc:0.7.5 build 12 | -hevm dapp-test --verbose 3 13 | 14 | .PHONY: test demo 15 | -------------------------------------------------------------------------------- /public/static/FP_NavLine_2pt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/lib/ds-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ds-test", 3 | "version": "1.0.0", 4 | "description": "Assertions, equality checks and other test helpers ", 5 | "bugs": "https://github.com/dapphub/ds-test/issues", 6 | "license": "GPL-3.0", 7 | "author": "Contributors to ds-test", 8 | "files": [ 9 | "src/*" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/dapphub/ds-test.git" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/test/compilation/CompilationTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Test.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationTest is Test {} 11 | -------------------------------------------------------------------------------- /circuits/readme.md: -------------------------------------------------------------------------------- 1 | # Circuit 2 | 3 | 1. Write circom circuit 4 | 2. Compile the circuit: 5 | `circom simple_multiplier.circom --r1cs --wasm --sym` 6 | 3. Download a powers of tau trusted setup file 7 | 4. Run Plonk setup to get the proving key: 8 | `snarkjs plonk setup circuit.r1cs ptau_file.ptau proving_key.zkey` 9 | 10 | To generate zkey: 11 | `snarkjs plonk setup simple_multiplier/simple_multiplier.r1cs ptau/powersOfTau28_hez_final_08.ptau simple_multiplier/proving_key.zkey` 12 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/test/compilation/CompilationScript.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Script.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationScript is Script {} 11 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/test/compilation/CompilationTestBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Test.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationTestBase is TestBase {} 11 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/test/compilation/CompilationScriptBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Script.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationScriptBase is ScriptBase {} 11 | -------------------------------------------------------------------------------- /artifacts/public.json: -------------------------------------------------------------------------------- 1 | [ 2 | "1", 3 | "55112061583421464676585661546109903034356617899180121332228666720", 4 | "864022899039144919885314", 5 | "0", 6 | "0", 7 | "0", 8 | "0", 9 | "0", 10 | "0", 11 | "0", 12 | "0", 13 | "0", 14 | "0", 15 | "0", 16 | "0", 17 | "0", 18 | "0", 19 | "0", 20 | "485130534418904117", 21 | "2976850090325153789", 22 | "1686340819745431459", 23 | "13123166005230995943", 24 | "7138011456553589905", 25 | "12930535425200765870", 26 | "16477303042261265790", 27 | "17525085552721154914" 28 | ] -------------------------------------------------------------------------------- /src/utils/contracts.tsx: -------------------------------------------------------------------------------- 1 | import WhitelistAbi from "@/lib/abi/AddWhitelist.json"; 2 | 3 | export function getContractInfo(chain?: number) { 4 | if (chain === 5001) 5 | return { 6 | contractAddress: "0x210899C848A107bd5ec3BEfF3eDfEeAaE7aD8723", 7 | abi: WhitelistAbi, 8 | }; 9 | if (chain === 5) 10 | return { 11 | contractAddress: "0x012bfFB750A9E900aB36853E6E7D9C53c8b6B9fb", 12 | abi: WhitelistAbi, 13 | }; 14 | 15 | return { 16 | contractAddress: "0x210899C848A107bd5ec3BEfF3eDfEeAaE7aD8723", 17 | abi: WhitelistAbi, 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /contracts/readme.md: -------------------------------------------------------------------------------- 1 | ## Initial SetUp 2 | 3 | .env file: 4 | 5 | ```bash 6 | GOERLI_RPC_URL=https://eth-goerli.g.alchemy.com/v2/{} 7 | PRIVATE_KEY= 8 | ``` 9 | 10 | to deploy contract on goerli: 11 | `forge script script/SimpleMultiplier.s.sol SimpleMultiplierScript --broadcast --verify --rpc-url goerli` 12 | 13 | to deploy contract on mantle testnet: 14 | `forge script script/SimpleMultiplier.s.sol SimpleMultiplierScript --broadcast --verify --rpc-url mantle --legacy` 15 | 16 | ## Whitelist 17 | 18 | `forge create --rpc-url mantle src/WhiteList.sol:Whitelist --private-key --legacy` 19 | -------------------------------------------------------------------------------- /circuits/eff_ecdsa/test/ecdsa_verify.ts: -------------------------------------------------------------------------------- 1 | describe("ecdsa_verify", () => { 2 | 3 | it("should verify the address and sig in email are valid", async () => { 4 | // const outputDir = setupDirectories(pathToCircom); 5 | 6 | // const circuit = await wasm_tester( 7 | // path.join(__dirname, outputDir), 8 | // { 9 | // prime: "secq256k1" 10 | // } 11 | // ); 12 | 13 | // const circuitInput = generate input from eml 14 | 15 | // const w = await circuit.calculateWitness(circuitInput, true); 16 | 17 | // await circuit.checkConstraints(w); 18 | }) 19 | }); 20 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forge-std", 3 | "version": "1.5.0", 4 | "description": "Forge Standard Library is a collection of helpful contracts and libraries for use with Forge and Foundry.", 5 | "homepage": "https://book.getfoundry.sh/forge/forge-std", 6 | "bugs": "https://github.com/foundry-rs/forge-std/issues", 7 | "license": "(Apache-2.0 OR MIT)", 8 | "author": "Contributors to Forge Standard Library", 9 | "files": [ 10 | "src/*" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/foundry-rs/forge-std.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/src/interfaces/IERC165.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | interface IERC165 { 5 | /// @notice Query if a contract implements an interface 6 | /// @param interfaceID The interface identifier, as specified in ERC-165 7 | /// @dev Interface identification is specified in ERC-165. This function 8 | /// uses less than 30,000 gas. 9 | /// @return `true` if the contract implements `interfaceID` and 10 | /// `interfaceID` is not 0xffffffff, `false` otherwise 11 | function supportsInterface(bytes4 interfaceID) external view returns (bool); 12 | } 13 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /contracts/script/SimpleMultiplier.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import "forge-std/Script.sol"; 5 | import "../src/PlonkVerifier.sol"; 6 | import "../src/SimpleMultiplier.sol"; 7 | 8 | contract SimpleMultiplierScript is Script { 9 | function setUp() public {} 10 | 11 | function run() public { 12 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 13 | vm.startBroadcast(deployerPrivateKey); 14 | 15 | PlonkVerifier pv = new PlonkVerifier(); 16 | SimpleMultiplier sm = new SimpleMultiplier(address(pv)); 17 | 18 | vm.stopBroadcast(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /circuits/eff_ecdsa/secp256k1/double.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.2; 2 | 3 | /** 4 | * Secp256k1Double 5 | * =============== 6 | * 7 | * Double a specific point (xP, yP) on the secp256k1 curve. Should work for any 8 | * short Weierstrass curve (Pasta, P-256). 9 | */ 10 | template Secp256k1Double() { 11 | signal input xP; 12 | signal input yP; 13 | 14 | signal output outX; 15 | signal output outY; 16 | 17 | signal lambda; 18 | signal xPSquared; 19 | 20 | xPSquared <== xP * xP; 21 | 22 | lambda <-- (3 * xPSquared) / (2 * yP); 23 | lambda * 2 * yP === 3 * xPSquared; 24 | 25 | outX <== lambda * lambda - (2 * xP); 26 | outY <== lambda * (xP - outX) - yP; 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | #forge 16 | /contracts/out/ 17 | /contracts/cache/ 18 | /contracts/broadcast/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # local env files 33 | .env*.local 34 | *.env 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | 43 | # circuits 44 | *.r1cs 45 | *.zkey 46 | *.sym 47 | *.ptau 48 | 49 | # test 50 | tmp 51 | 52 | /yarn.lock 53 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve", 20 | "incremental": true, 21 | "paths": { 22 | "@/*": [ 23 | "./src/*" 24 | ] 25 | } 26 | }, 27 | "include": [ 28 | "next-env.d.ts", 29 | "**/*.ts", 30 | "**/*.tsx" 31 | ], 32 | "exclude": [ 33 | "node_modules" 34 | ] 35 | } -------------------------------------------------------------------------------- /circuits/multiplier2/test/multiplier2.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import wasm_tester from "../../../wasm_tester"; 3 | import { setupDirectories } from "../../test_utils" 4 | 5 | const pathToCircom = "../Multiplier2.circom" 6 | 7 | describe("Test multiplier2", function () { 8 | const multiplier2Dir = setupDirectories(pathToCircom); 9 | 10 | it("Checking the compilation of a simple circuit generating wasm in a given folder without recompiling", async function () { 11 | const circuit = await wasm_tester( 12 | path.join(__dirname, pathToCircom), 13 | { 14 | output: multiplier2Dir, 15 | // recompile: false, 16 | } 17 | ); 18 | const w = await circuit.calculateWitness({ a: 6, b: 3 }); 19 | await circuit.checkConstraints(w); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /scripts/2_gen_wtns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Get the absolute path of the current script 3 | SCRIPT_PATH="$(realpath "${BASH_SOURCE[0]}")" 4 | 5 | # Find the directory containing the script 6 | SCRIPT_DIR="$(dirname "${SCRIPT_PATH}")" 7 | 8 | # Get the root path of the repository by going up a level (assuming the script is in a 'scripts' directory) 9 | REPO_ROOT="$(dirname "${SCRIPT_DIR}")" 10 | 11 | # Source the circuit.env file relative to the repository root 12 | source "${REPO_ROOT}/scripts/circuit.env" 13 | 14 | echo "****GENERATING WITNESS FOR SAMPLE INPUT****" 15 | start=$(date +%s) 16 | set -x 17 | node "$BUILD_DIR"/"$CIRCUIT_NAME"_js/generate_witness.js "$BUILD_DIR"/"$CIRCUIT_NAME"_js/"$CIRCUIT_NAME".wasm "$BUILD_DIR"/input.json "$BUILD_DIR"/witness.wtns 18 | { set +x; } 2>/dev/null 19 | end=$(date +%s) 20 | echo "DONE ($((end - start))s)" 21 | echo 22 | -------------------------------------------------------------------------------- /circuits/ecsda-circom/eth_addr.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.2; 2 | 3 | include "./zk-identity/eth.circom"; 4 | include "./ecdsa.circom"; 5 | 6 | template PrivKeyToAddr(n, k) { 7 | signal input privkey[k]; 8 | signal output addr; 9 | 10 | component privToPub = ECDSAPrivToPub(n, k); 11 | for (var i = 0; i < k; i++) { 12 | privToPub.privkey[i] <== privkey[i]; 13 | } 14 | 15 | component flattenPub = FlattenPubkey(n, k); 16 | for (var i = 0; i < k; i++) { 17 | flattenPub.chunkedPubkey[0][i] <== privToPub.pubkey[0][i]; 18 | flattenPub.chunkedPubkey[1][i] <== privToPub.pubkey[1][i]; 19 | } 20 | 21 | component pubToAddr = PubkeyToAddress(); 22 | for (var i = 0; i < 512; i++) { 23 | pubToAddr.pubkeyBits[i] <== flattenPub.pubkeyBits[i]; 24 | } 25 | 26 | addr <== pubToAddr.address; 27 | log("addr", addr); 28 | } -------------------------------------------------------------------------------- /contracts/lib/forge-std/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | fs_permissions = [{ access = "read-write", path = "./"}] 3 | 4 | [rpc_endpoints] 5 | # The RPC URLs are modified versions of the default for testing initialization. 6 | mainnet = "https://mainnet.infura.io/v3/16a8be88795540b9b3903d8de0f7baa5" # Different API key. 7 | optimism_goerli = "https://goerli.optimism.io/" # Adds a trailing slash. 8 | arbitrum_one_goerli = "https://goerli-rollup.arbitrum.io/rpc/" # Adds a trailing slash. 9 | needs_undefined_env_var = "${UNDEFINED_RPC_URL_PLACEHOLDER}" 10 | 11 | [fmt] 12 | # These are all the `forge fmt` defaults. 13 | line_length = 120 14 | tab_width = 4 15 | bracket_spacing = false 16 | int_types = 'long' 17 | multiline_func_header = 'attributes_first' 18 | quote_style = 'double' 19 | number_underscore = 'preserve' 20 | single_line_statement_blocks = 'preserve' 21 | ignore = ["src/console.sol", "src/console2.sol"] -------------------------------------------------------------------------------- /src/lib/abi/IVerifier.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "uint256[2]", 6 | "name": "a", 7 | "type": "uint256[2]" 8 | }, 9 | { 10 | "internalType": "uint256[2][2]", 11 | "name": "b", 12 | "type": "uint256[2][2]" 13 | }, 14 | { 15 | "internalType": "uint256[2]", 16 | "name": "c", 17 | "type": "uint256[2]" 18 | }, 19 | { 20 | "internalType": "uint256[17]", 21 | "name": "input", 22 | "type": "uint256[17]" 23 | } 24 | ], 25 | "name": "verifyProof", 26 | "outputs": [ 27 | { 28 | "internalType": "bool", 29 | "name": "", 30 | "type": "bool" 31 | } 32 | ], 33 | "stateMutability": "nonpayable", 34 | "type": "function" 35 | } 36 | ] -------------------------------------------------------------------------------- /src/lib/executeTransaction.ts: -------------------------------------------------------------------------------- 1 | import { Addresses } from "@/shared/addresses"; 2 | import { TransactionReceipt } from "@ethersproject/abstract-provider"; 3 | import { prepareWriteContract, writeContract } from "@wagmi/core"; 4 | 5 | export const executeTransaction = async ( 6 | proof: any, 7 | publicSignals: Array 8 | ): Promise => { 9 | const abiPath = require("./abi/SimpleMultiplier.json"); 10 | 11 | // Prepare the transaction data 12 | const config = await prepareWriteContract({ 13 | address: Addresses.SIMPLE_MULTIPLIER_ADDR, 14 | abi: abiPath.abi, 15 | functionName: "submitProof", 16 | args: [proof, publicSignals], 17 | }); 18 | 19 | // Execute the transaction 20 | const writeResult = await writeContract(config); 21 | 22 | // Wait for the transaction block to be mined 23 | const txResult = await writeResult.wait(); 24 | return txResult; 25 | }; 26 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/src/Script.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | // 💬 ABOUT 5 | // Standard Library's default Script. 6 | 7 | // 🧩 MODULES 8 | import {ScriptBase} from "./Base.sol"; 9 | import {console} from "./console.sol"; 10 | import {console2} from "./console2.sol"; 11 | import {StdChains} from "./StdChains.sol"; 12 | import {StdCheatsSafe} from "./StdCheats.sol"; 13 | import {stdJson} from "./StdJson.sol"; 14 | import {stdMath} from "./StdMath.sol"; 15 | import {StdStorage, stdStorageSafe} from "./StdStorage.sol"; 16 | import {StdUtils} from "./StdUtils.sol"; 17 | import {VmSafe} from "./Vm.sol"; 18 | 19 | // 📦 BOILERPLATE 20 | import {ScriptBase} from "./Base.sol"; 21 | 22 | // ⭐️ SCRIPT 23 | abstract contract Script is StdChains, StdCheatsSafe, StdUtils, ScriptBase { 24 | // Note: IS_SCRIPT() must return true. 25 | bool public IS_SCRIPT = true; 26 | } 27 | -------------------------------------------------------------------------------- /contracts/src/SimpleMultiplier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | // Interface to PlonkVerifier.sol 5 | interface IPlonkVerifier { 6 | function verifyProof(bytes memory proof, uint[] memory pubSignals) external view returns (bool); 7 | } 8 | 9 | contract SimpleMultiplier { 10 | address public s_plonkVerifierAddress; 11 | 12 | event ProofResult(bool result); 13 | 14 | constructor(address plonkVerifierAddress) { 15 | s_plonkVerifierAddress = plonkVerifierAddress; 16 | } 17 | 18 | // ZK proof is generated in the browser and submitted as a transaction w/ the proof as bytes. 19 | function submitProof(bytes memory proof, uint256[] memory pubSignals) public returns (bool) { 20 | bool result = IPlonkVerifier(s_plonkVerifierAddress).verifyProof(proof, pubSignals); 21 | emit ProofResult(result); 22 | return result; 23 | } 24 | } -------------------------------------------------------------------------------- /circuits/ecsda-circom/eth_addr_2.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.2; 2 | 3 | include "./zk-identity/eth.circom"; 4 | include "./ecdsa.circom"; 5 | 6 | template PrivKeyToAddr(n, k) { 7 | signal input privkey[k]; 8 | signal input publickey; 9 | signal output addr; 10 | 11 | component privToPub = ECDSAPrivToPub(n, k); 12 | for (var i = 0; i < k; i++) { 13 | privToPub.privkey[i] <== privkey[i]; 14 | } 15 | 16 | component flattenPub = FlattenPubkey(n, k); 17 | for (var i = 0; i < k; i++) { 18 | flattenPub.chunkedPubkey[0][i] <== privToPub.pubkey[0][i]; 19 | flattenPub.chunkedPubkey[1][i] <== privToPub.pubkey[1][i]; 20 | } 21 | 22 | component pubToAddr = PubkeyToAddress(); 23 | for (var i = 0; i < 512; i++) { 24 | pubToAddr.pubkeyBits[i] <== flattenPub.pubkeyBits[i]; 25 | } 26 | 27 | addr <== pubToAddr.address; 28 | publickey === addr; 29 | // log("publickey", publickey); 30 | } -------------------------------------------------------------------------------- /circuits/sha256/sha256bits2032.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../../node_modules/circomlib/circuits/sha256/sha256.circom"; 4 | include "../../node_modules/circomlib/circuits/comparators.circom"; 5 | include "../../node_modules/circomlib/circuits/bitify.circom"; 6 | 7 | template userId() { 8 | signal input userEmailAddress[2032]; 9 | signal input userId; 10 | 11 | signal output isValid; 12 | 13 | component sha256Hash = Sha256(2032); 14 | for (var i = 0; i < 2032; i++) { 15 | sha256Hash.in[i] <== userEmailAddress[i]; 16 | } 17 | 18 | component bits2num = Bits2Num(216); 19 | 20 | for (var i=0; i<216; i++) { 21 | bits2num.in[i] <== sha256Hash.out[255-i]; 22 | } 23 | 24 | component isEqual = IsEqual(); 25 | isEqual.in[0] <== userId; 26 | isEqual.in[1] <== bits2num.out; 27 | 28 | isValid <== isEqual.out; 29 | } 30 | 31 | component main {public [userId]} = userId(); 32 | -------------------------------------------------------------------------------- /artifacts/proof.json: -------------------------------------------------------------------------------- 1 | { 2 | "pi_a": [ 3 | "200136400148359332256024721968309292261726082533715246916577276681973054426", 4 | "3286075703795504339963396387915891065984075027043978374227353572554150223129", 5 | "1" 6 | ], 7 | "pi_b": [ 8 | [ 9 | "4967264675316902117648643418491936734469607119589962164029737280533716113979", 10 | "1258452654558983396421365540645579375366706448163095856117997765914424740443" 11 | ], 12 | [ 13 | "16581747474837752257528469494074037883070224175362860256744947197849871887441", 14 | "20770687911416212187034548984101317455766412175821555693160913446692600876025" 15 | ], 16 | [ 17 | "1", 18 | "0" 19 | ] 20 | ], 21 | "pi_c": [ 22 | "12834542173986550583489622854820214593846747018937256741609912801977441011956", 23 | "9926244651965978924810368731147734184775029475285837309740686803901289771677", 24 | "1" 25 | ], 26 | "protocol": "groth16", 27 | "curve": "bn128" 28 | } -------------------------------------------------------------------------------- /public/static/FP_Twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /contracts/src/WhiteList.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity 0.8.13; 4 | 5 | contract Whitelist { 6 | 7 | address owner; 8 | 9 | mapping(address => bool) whitelistedAddresses; 10 | 11 | constructor() { 12 | owner = msg.sender; 13 | whitelistedAddresses[msg.sender] = true; 14 | } 15 | 16 | modifier onlyOwner() { 17 | require(msg.sender == owner, "Ownable: caller is not the owner"); 18 | _; 19 | } 20 | 21 | modifier isWhitelisted(address _address) { 22 | require(whitelistedAddresses[_address], "Whitelist: You need to be whitelisted"); 23 | _; 24 | } 25 | 26 | function addUser(address _addressToWhitelist) public onlyOwner { 27 | whitelistedAddresses[_addressToWhitelist] = true; 28 | } 29 | 30 | function verifyUser(address _whitelistedAddress) public view returns(bool) { 31 | bool userIsWhitelisted = whitelistedAddresses[_whitelistedAddress]; 32 | return userIsWhitelisted; 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/pages/api/generate_proof.ts: -------------------------------------------------------------------------------- 1 | import { generateProof } from "@/lib/generateProof"; 2 | import type { NextApiRequest, NextApiResponse } from "next"; 3 | 4 | export default async function handler( 5 | req: NextApiRequest, 6 | res: NextApiResponse 7 | ) { 8 | const body = req?.body; 9 | if (body === undefined) { 10 | return res.status(403).json({ error: "Request has no body" }); 11 | } 12 | console.log(body); 13 | 14 | const input0 = parseInt(body.input0); 15 | const input1 = parseInt(body.input1); 16 | 17 | if ( 18 | input0 === undefined || 19 | Number.isNaN(input0) || 20 | input1 === undefined || 21 | Number.isNaN(input1) 22 | ) { 23 | return res.status(403).json({ error: "Invalid inputs" }); 24 | } 25 | const proof = await generateProof(input0, input1); 26 | 27 | if (proof.proof === "") { 28 | return res.status(403).json({ error: "Proving failed" }); 29 | } 30 | 31 | res.setHeader("Content-Type", "text/json"); 32 | res.status(200).json(proof); 33 | } 34 | -------------------------------------------------------------------------------- /circuits/ecsda-circom/test/eth_addr.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import wasm_tester from "../../../wasm_tester"; 3 | import { setupDirectories } from "../../test_utils" 4 | 5 | const pathToCircom = "./eth_addr.circom" 6 | 7 | describe("Test eth_addr", function () { 8 | this.timeout(100000); 9 | 10 | const ethaddr2Dir = setupDirectories(pathToCircom); 11 | 12 | // yarn run test -g "eth_addr 2 input" 13 | it("eth_addr 2 input", async function () { 14 | const circuit = await wasm_tester( 15 | path.join(__dirname, pathToCircom), 16 | { 17 | output: ethaddr2Dir, 18 | // recompile: false, 19 | } 20 | ); 21 | const w = await circuit.calculateWitness({ 22 | "privkey": ["6862539325408419825", "7739665414899438580", "3575179427557022600", "11277760030985572954"], 23 | "publickey": "978617770967819762654777740949918972567359649306" 24 | }); 25 | await circuit.checkConstraints(w); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /circuits/ecsda-circom/test/sig_verify.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import wasm_tester from "../../../wasm_tester"; 3 | import { 4 | setupDirectories, 5 | getCircuitInputWithAddrAndSig, 6 | getAddrAndSig 7 | } from "../../test_utils" 8 | import { expect } from 'chai'; 9 | 10 | const pathToCircom = "./sig_verify.circom" 11 | 12 | describe("Test sig verify", function () { 13 | const output = setupDirectories(pathToCircom); 14 | 15 | it("Checks sig verify", async function () { 16 | const privateKey = "f5b552f608f5b552f608f5b552f6082ff5b552f608f5b552f608f5b552f6082f" 17 | const addrAndSig = getAddrAndSig(privateKey) 18 | const input = getCircuitInputWithAddrAndSig(addrAndSig) 19 | 20 | const circuit = await wasm_tester( 21 | path.join(__dirname, pathToCircom), 22 | { 23 | output 24 | } 25 | ); 26 | 27 | const w = await circuit.calculateWitness(input); 28 | 29 | // 0n means failed, 1n measn passed 30 | expect(w[1]).to.equal(1n); 31 | 32 | await circuit.checkConstraints(w); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/src/StdError.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Panics work for versions >=0.8.0, but we lowered the pragma to make this compatible with Test 3 | pragma solidity >=0.6.2 <0.9.0; 4 | 5 | library stdError { 6 | bytes public constant assertionError = abi.encodeWithSignature("Panic(uint256)", 0x01); 7 | bytes public constant arithmeticError = abi.encodeWithSignature("Panic(uint256)", 0x11); 8 | bytes public constant divisionError = abi.encodeWithSignature("Panic(uint256)", 0x12); 9 | bytes public constant enumConversionError = abi.encodeWithSignature("Panic(uint256)", 0x21); 10 | bytes public constant encodeStorageError = abi.encodeWithSignature("Panic(uint256)", 0x22); 11 | bytes public constant popError = abi.encodeWithSignature("Panic(uint256)", 0x31); 12 | bytes public constant indexOOBError = abi.encodeWithSignature("Panic(uint256)", 0x32); 13 | bytes public constant memOverflowError = abi.encodeWithSignature("Panic(uint256)", 0x41); 14 | bytes public constant zeroVarError = abi.encodeWithSignature("Panic(uint256)", 0x51); 15 | } 16 | -------------------------------------------------------------------------------- /circuits/eff_ecdsa/eff_ecdsa.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.2; 2 | 3 | include "./secp256k1/mul.circom"; 4 | include "../../node_modules/circomlib/circuits/bitify.circom"; 5 | 6 | /** 7 | * EfficientECDSA 8 | * ==================== 9 | * 10 | * Converts inputted efficient ECDSA signature to an public key. There is no 11 | * public key validation included. 12 | */ 13 | template EfficientECDSA() { 14 | var bits = 256; 15 | signal input s; 16 | signal input Tx; // T = r^-1 * R 17 | signal input Ty; 18 | signal input Ux; // U = -(m * r^-1 * G) 19 | signal input Uy; 20 | 21 | signal output pubKeyX; 22 | signal output pubKeyY; 23 | 24 | // sMultT = s * T 25 | component sMultT = Secp256k1Mul(); 26 | sMultT.scalar <== s; 27 | sMultT.xP <== Tx; 28 | sMultT.yP <== Ty; 29 | 30 | // pubKey = sMultT + U 31 | component pubKey = Secp256k1AddComplete(); 32 | pubKey.xP <== sMultT.outX; 33 | pubKey.yP <== sMultT.outY; 34 | pubKey.xQ <== Ux; 35 | pubKey.yQ <== Uy; 36 | 37 | pubKeyX <== pubKey.outX; 38 | pubKeyY <== pubKey.outY; 39 | } -------------------------------------------------------------------------------- /contracts/lib/forge-std/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright Contributors to Forge Standard Library 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE O THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE.R 26 | -------------------------------------------------------------------------------- /public/static/FP_Discord.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/5_gen_proof.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the absolute path of the current script 4 | SCRIPT_PATH="$(realpath "${BASH_SOURCE[0]}")" 5 | 6 | # Find the directory containing the script 7 | SCRIPT_DIR="$(dirname "${SCRIPT_PATH}")" 8 | 9 | # Get the root path of the repository by going up a level (assuming the script is in a 'scripts' directory) 10 | REPO_ROOT="$(dirname "${SCRIPT_DIR}")" 11 | 12 | # Source the circuit.env file relative to the repository root 13 | source "${REPO_ROOT}/scripts/circuit.env" 14 | 15 | 16 | echo "****GENERATING PROOF FOR SAMPLE INPUT****" 17 | start=$(date +%s) 18 | set -x 19 | NODE_OPTIONS='--max-old-space-size=644000' npx snarkjs groth16 prove "$BUILD_DIR"/"$CIRCUIT_NAME".zkey "$BUILD_DIR"/witness.wtns "$BUILD_DIR"/proof.json "$BUILD_DIR"/public.json 20 | { set +x; } 2>/dev/null 21 | end=$(date +%s) 22 | echo "DONE ($((end - start))s)" 23 | echo 24 | 25 | echo "****VERIFYING PROOF FOR SAMPLE INPUT****" 26 | start=$(date +%s) 27 | set -x 28 | NODE_OPTIONS='--max-old-space-size=644000' npx snarkjs groth16 verify "$BUILD_DIR"/vkey.json "$BUILD_DIR"/public.json "$BUILD_DIR"/proof.json 29 | end=$(date +%s) 30 | { set +x; } 2>/dev/null 31 | echo "DONE ($((end - start))s)" 32 | echo 33 | -------------------------------------------------------------------------------- /scripts/1_compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Get the absolute path of the current script 3 | SCRIPT_PATH="$(realpath "${BASH_SOURCE[0]}")" 4 | 5 | # Find the directory containing the script 6 | SCRIPT_DIR="$(dirname "${SCRIPT_PATH}")" 7 | 8 | # Get the root path of the repository by going up a level (assuming the script is in a 'scripts' directory) 9 | REPO_ROOT="$(dirname "${SCRIPT_DIR}")" 10 | 11 | # Source the circuit.env file relative to the repository root 12 | source "${REPO_ROOT}/scripts/circuit.env" 13 | 14 | CIRCOM_PATH="$REPO_ROOT/circuits/$CIRCUIT_NAME.circom" 15 | 16 | if [ ! -d "$BUILD_DIR" ]; then 17 | echo "No build directory found. Creating build directory..." 18 | mkdir -p "$BUILD_DIR" 19 | fi 20 | 21 | 22 | echo '****COMPILING CIRCUIT****' 23 | start=$(date +%s) 24 | set -x 25 | circom "$CIRCOM_PATH" --r1cs --wasm --sym --c --wat --output "$BUILD_DIR" 26 | { set +x; } 2>/dev/null 27 | end=$(date +%s) 28 | echo "DONE ($((end - start))s)" 29 | echo 30 | 31 | echo '****INSPECTING CIRCUIT FOR UNDERCONSTRAINTS (OPTIONAL, CAN FORCE EXIT)****' 32 | start=$(date +%s) 33 | set -x 34 | circom "$CIRCOM_PATH" --inspect 35 | { set +x; } 2>/dev/null 36 | end=$(date +%s) 37 | echo "DONE ($((end - start))s)" 38 | echo 39 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/lib/ds-test/.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | on: 3 | pull_request: 4 | push: 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: cachix/install-nix-action@v18 11 | with: 12 | nix_path: nixpkgs=channel:nixos-unstable 13 | extra_nix_config: | 14 | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} 15 | 16 | - name: setup dapp binary cache 17 | uses: cachix/cachix-action@v12 18 | with: 19 | name: dapp 20 | 21 | - name: install dapptools 22 | run: nix profile install github:dapphub/dapptools#dapp --accept-flake-config 23 | 24 | - name: install foundry 25 | uses: foundry-rs/foundry-toolchain@v1 26 | 27 | - name: test with solc-0.5.17 28 | run: dapp --use solc-0.5.17 test -v 29 | 30 | - name: test with solc-0.6.11 31 | run: dapp --use solc-0.6.11 test -v 32 | 33 | - name: test with solc-0.7.6 34 | run: dapp --use solc-0.7.6 test -v 35 | 36 | - name: test with solc-0.8.18 37 | run: dapp --use solc-0.8.18 test -v 38 | 39 | - name: Run tests with foundry 40 | run: forge test -vvv 41 | 42 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/src/Test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | // 💬 ABOUT 7 | // Standard Library's default Test 8 | 9 | // 🧩 MODULES 10 | import {console} from "./console.sol"; 11 | import {console2} from "./console2.sol"; 12 | import {StdAssertions} from "./StdAssertions.sol"; 13 | import {StdChains} from "./StdChains.sol"; 14 | import {StdCheats} from "./StdCheats.sol"; 15 | import {stdError} from "./StdError.sol"; 16 | import {StdInvariant} from "./StdInvariant.sol"; 17 | import {stdJson} from "./StdJson.sol"; 18 | import {stdMath} from "./StdMath.sol"; 19 | import {StdStorage, stdStorage} from "./StdStorage.sol"; 20 | import {StdUtils} from "./StdUtils.sol"; 21 | import {Vm} from "./Vm.sol"; 22 | import {StdStyle} from "./StdStyle.sol"; 23 | 24 | // 📦 BOILERPLATE 25 | import {TestBase} from "./Base.sol"; 26 | import {DSTest} from "ds-test/test.sol"; 27 | 28 | // ⭐️ TEST 29 | abstract contract Test is DSTest, StdAssertions, StdChains, StdCheats, StdInvariant, StdUtils, TestBase { 30 | // Note: IS_TEST() must return true. 31 | // Note: Must have failure system, https://github.com/dapphub/ds-test/blob/cd98eff28324bfac652e63a239a60632a761790b/src/test.sol#L39-L76. 32 | } 33 | -------------------------------------------------------------------------------- /scripts/4_gen_vkey.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the absolute path of the current script 4 | SCRIPT_PATH="$(realpath "${BASH_SOURCE[0]}")" 5 | 6 | # Find the directory containing the script 7 | SCRIPT_DIR="$(dirname "${SCRIPT_PATH}")" 8 | 9 | # Get the root path of the repository by going up a level (assuming the script is in a 'scripts' directory) 10 | REPO_ROOT="$(dirname "${SCRIPT_DIR}")" 11 | 12 | # Source the circuit.env file relative to the repository root 13 | source "${REPO_ROOT}/scripts/circuit.env" 14 | 15 | 16 | R1CS_FILE="$BUILD_DIR/$CIRCUIT_NAME.r1cs" 17 | PHASE1="${REPO_ROOT}/circuits/powersOfTau28_hez_final_22.ptau" 18 | 19 | echo "****EXPORTING VKEY****" 20 | start=$(date +%s) 21 | set -x 22 | NODE_OPTIONS='--max-old-space-size=644000' npx snarkjs zkey export verificationkey "$BUILD_DIR"/"$CIRCUIT_NAME".zkey "$BUILD_DIR"/vkey.json 23 | end=$(date +%s) 24 | { set +x; } 2>/dev/null 25 | echo "DONE ($((end - start))s)" 26 | echo 27 | 28 | echo "****GENERATE SOLIDITY VERIFIER****" 29 | start=$(date +%s) 30 | set -x 31 | NODE_OPTIONS='--max-old-space-size=644000' npx snarkjs zkey export solidityverifier "$BUILD_DIR"/"$CIRCUIT_NAME".zkey --sol "$BUILD_DIR"/verifier.sol 32 | end=$(date +%s) 33 | { set +x; } 2>/dev/null 34 | echo "DONE ($((end - start))s)" 35 | echo 36 | -------------------------------------------------------------------------------- /circuits/eff_ecdsa/eff_ecdsa_to_addr.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.2; 2 | 3 | include "./eff_ecdsa.circom"; 4 | include "./to_address/zk-identity/eth.circom"; 5 | 6 | /** 7 | * EfficientECDSAToAddr 8 | * ==================== 9 | * 10 | * Converts inputted efficient ECDSA signature to an address. 11 | */ 12 | template EfficientECDSAToAddr() { 13 | var bits = 256; 14 | signal input s; 15 | signal input Tx; // T = r^-1 * R 16 | signal input Ty; 17 | signal input Ux; // U = -(m * r^-1 * G) 18 | signal input Uy; 19 | signal output addr; 20 | 21 | component effEcdsa = EfficientECDSA(); 22 | effEcdsa.s <== s; 23 | effEcdsa.Tx <== Tx; 24 | effEcdsa.Ty <== Ty; 25 | effEcdsa.Ux <== Ux; 26 | effEcdsa.Uy <== Uy; 27 | 28 | component pubKeyXBits = Num2Bits(256); 29 | pubKeyXBits.in <== effEcdsa.pubKeyX; 30 | 31 | component pubKeyYBits = Num2Bits(256); 32 | pubKeyYBits.in <== effEcdsa.pubKeyY; 33 | 34 | component pubToAddr = PubkeyToAddress(); 35 | 36 | for (var i = 0; i < 256; i++) { 37 | pubToAddr.pubkeyBits[i] <== pubKeyYBits.out[i]; 38 | pubToAddr.pubkeyBits[i + 256] <== pubKeyXBits.out[i]; 39 | } 40 | 41 | addr <== pubToAddr.address; 42 | } 43 | 44 | component main = EfficientECDSAToAddr(); 45 | 46 | -------------------------------------------------------------------------------- /circuits/sha256/test/sha256bits2032.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import wasm_tester from "../../../wasm_tester"; 3 | import { 4 | setupDirectories, 5 | padEmailTo2032Bits, 6 | stringToBitArray, 7 | extractLeastSignificantBits 8 | } from "../../test_utils" 9 | import { sha256 } from 'js-sha256'; 10 | import { expect } from 'chai'; 11 | 12 | const pathToCircom = "../sha256bits2032.circom" 13 | 14 | describe("Test sha256", function () { 15 | const output = setupDirectories(pathToCircom); 16 | 17 | it("test sha256", async function () { 18 | 19 | const email = "example@example.com"; 20 | 21 | const paddedEmail = padEmailTo2032Bits(email); 22 | 23 | if (!paddedEmail) { 24 | throw ("The email address is not valid."); 25 | } 26 | 27 | const hash = sha256(paddedEmail); 28 | 29 | const userId = extractLeastSignificantBits(hash, 216); 30 | 31 | const inputBits = stringToBitArray(paddedEmail); 32 | 33 | const circuit = await wasm_tester( 34 | path.join(__dirname, pathToCircom), 35 | { 36 | output 37 | } 38 | ); 39 | 40 | const w = await circuit.calculateWitness({ 41 | "userEmailAddress": inputBits, 42 | "userId": userId 43 | }, true); 44 | 45 | expect(w[1]).to.equal(1n); 46 | 47 | await circuit.checkConstraints(w); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wasm_tester/utils.js: -------------------------------------------------------------------------------- 1 | const fnv = require("fnv-plus"); 2 | 3 | module.exports.fnvHash = fnvHash; 4 | module.exports.toArray32 = toArray32; 5 | module.exports.fromArray32 = fromArray32; 6 | module.exports.flatArray = flatArray; 7 | module.exports.normalize = normalize; 8 | 9 | function toArray32(rem,size) { 10 | const res = []; //new Uint32Array(size); //has no unshift 11 | const radix = BigInt(0x100000000); 12 | while (rem) { 13 | res.unshift( Number(rem % radix)); 14 | rem = rem / radix; 15 | } 16 | if (size) { 17 | var i = size - res.length; 18 | while (i>0) { 19 | res.unshift(0); 20 | i--; 21 | } 22 | } 23 | return res; 24 | } 25 | 26 | function fromArray32(arr) { //returns a BigInt 27 | var res = BigInt(0); 28 | const radix = BigInt(0x100000000); 29 | for (let i = 0; i=0.6.2 <0.9.0; 3 | 4 | library stdMath { 5 | int256 private constant INT256_MIN = -57896044618658097711785492504343953926634992332820282019728792003956564819968; 6 | 7 | function abs(int256 a) internal pure returns (uint256) { 8 | // Required or it will fail when `a = type(int256).min` 9 | if (a == INT256_MIN) { 10 | return 57896044618658097711785492504343953926634992332820282019728792003956564819968; 11 | } 12 | 13 | return uint256(a > 0 ? a : -a); 14 | } 15 | 16 | function delta(uint256 a, uint256 b) internal pure returns (uint256) { 17 | return a > b ? a - b : b - a; 18 | } 19 | 20 | function delta(int256 a, int256 b) internal pure returns (uint256) { 21 | // a and b are of the same sign 22 | // this works thanks to two's complement, the left-most bit is the sign bit 23 | if ((a ^ b) > -1) { 24 | return delta(abs(a), abs(b)); 25 | } 26 | 27 | // a and b are of opposite signs 28 | return abs(a) + abs(b); 29 | } 30 | 31 | function percentDelta(uint256 a, uint256 b) internal pure returns (uint256) { 32 | uint256 absDelta = delta(a, b); 33 | 34 | return absDelta * 1e18 / b; 35 | } 36 | 37 | function percentDelta(int256 a, int256 b) internal pure returns (uint256) { 38 | uint256 absDelta = delta(a, b); 39 | uint256 absB = abs(b); 40 | 41 | return absDelta * 1e18 / absB; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "@rainbow-me/rainbowkit/styles.css"; 2 | import "@/styles/globals.css"; 3 | import type { AppProps } from "next/app"; 4 | 5 | import { getDefaultWallets, RainbowKitProvider } from "@rainbow-me/rainbowkit"; 6 | import { configureChains, createClient, WagmiConfig } from "wagmi"; 7 | import { Chain, goerli } from "wagmi/chains"; 8 | import { publicProvider } from "wagmi/providers/public"; 9 | import { Layout } from "@/components/Layout"; 10 | 11 | const MantleChain: Chain = { 12 | id: 5001, 13 | name: "Mantle", 14 | network: "mantle", 15 | nativeCurrency: { 16 | symbol: "BIT", 17 | decimals: 18, 18 | name: "BIT", 19 | }, 20 | rpcUrls: { 21 | default: { 22 | http: ["https://rpc.testnet.mantle.xyz/"], 23 | }, 24 | public: { 25 | http: ["https://rpc.testnet.mantle.xyz/"], 26 | }, 27 | }, 28 | testnet: true, 29 | }; 30 | 31 | const { chains, provider } = configureChains( 32 | [goerli, MantleChain], 33 | [publicProvider()] 34 | ); 35 | 36 | const { connectors } = getDefaultWallets({ 37 | appName: "My RainbowKit App", 38 | projectId: "YOUR_PROJECT_ID", 39 | chains, 40 | }); 41 | 42 | const wagmiClient = createClient({ 43 | autoConnect: true, 44 | connectors, 45 | provider, 46 | }); 47 | 48 | export default function App({ Component, pageProps }: AppProps) { 49 | return ( 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /src/lib/generateProof.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | // @ts-ignore 3 | import * as snarkjs from "snarkjs"; 4 | 5 | export const generateProof = async ( 6 | input0: number, 7 | input1: number 8 | ): Promise => { 9 | console.log(`Generating vote proof with inputs: ${input0}, ${input1}`); 10 | 11 | // We need to have the naming scheme and shape of the inputs match the .circom file 12 | const inputs = { 13 | in: [input0, input1], 14 | }; 15 | 16 | // Paths to the .wasm file and proving key 17 | const wasmPath = path.join( 18 | process.cwd(), 19 | "circuits/simple_multiplier/simple_multiplier_js/simple_multiplier.wasm" 20 | ); 21 | const provingKeyPath = path.join( 22 | process.cwd(), 23 | "circuits/simple_multiplier/proving_key.zkey" 24 | ); 25 | 26 | try { 27 | // Generate a proof of the circuit and create a structure for the output signals 28 | const { proof, publicSignals } = await snarkjs.plonk.fullProve( 29 | inputs, 30 | wasmPath, 31 | provingKeyPath 32 | ); 33 | 34 | // Convert the data into Solidity calldata that can be sent as a transaction 35 | const calldataBlob = await snarkjs.plonk.exportSolidityCallData( 36 | proof, 37 | publicSignals 38 | ); 39 | const calldata = calldataBlob.split(","); 40 | 41 | console.log(calldata); 42 | 43 | return { 44 | proof: calldata[0], 45 | publicSignals: JSON.parse(calldata[1]), 46 | }; 47 | } catch (err) { 48 | console.log(`Error:`, err); 49 | return { 50 | proof: "", 51 | publicSignals: [], 52 | }; 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /src/lib/addWhitelistTransaction.ts: -------------------------------------------------------------------------------- 1 | import { getContractInfo } from "@/utils/contracts"; 2 | import { 3 | prepareWriteContract, 4 | writeContract, 5 | getAccount, 6 | readContract, 7 | getNetwork, 8 | } from "@wagmi/core"; 9 | import { ethers } from "ethers"; 10 | 11 | const { chain } = getNetwork(); 12 | const { contractAddress, abi } = getContractInfo(chain?.id); 13 | 14 | export const checkWhitelisted = async () => { 15 | const account = getAccount(); 16 | console.log("Current account:", account); 17 | const data = await readContract({ 18 | address: contractAddress as `0x${string}`, 19 | abi: abi, 20 | functionName: "verifyUser", 21 | args: [account.address], 22 | }); 23 | 24 | console.log("Verify data:", data); 25 | 26 | if (data) { 27 | return true; 28 | } 29 | return false; 30 | }; 31 | 32 | export const addWhitelistTransaction = async ( 33 | a:any, 34 | b:any, 35 | c:any, 36 | input:any, 37 | userId: string, 38 | emailSuffix: string 39 | ) => { 40 | try { 41 | emailSuffix = ethers.utils.formatBytes32String(emailSuffix); 42 | const config = await prepareWriteContract({ 43 | address: contractAddress as `0x${string}`, 44 | abi: abi, 45 | functionName: "addToWhitelist", 46 | args: [userId, emailSuffix, a, b, c, input], 47 | }); 48 | 49 | // Execute the transaction 50 | const writeResult = await writeContract(config); 51 | 52 | // Wait for the transaction block to be mined 53 | const txResult = await writeResult.wait(); 54 | return txResult.transactionHash; 55 | } catch (err) { 56 | console.log("addWhitelistTransaction err2:", err); 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /circuits/zkblind/zkblind_temp.circom: -------------------------------------------------------------------------------- 1 | 2 | pragma circom 2.0.2; 3 | 4 | include "../../node_modules/circomlib/circuits/sha256/sha256.circom"; 5 | include "../ecsda-circom/eth_addr_2.circom"; 6 | // include "constants.circom"; 7 | // include "../ecsda-circom/ecdsa.circom"; 8 | 9 | template zkBlind(k) { 10 | signal input userEmailAddress[2032]; 11 | signal input privkey[k]; 12 | signal input publickey; 13 | 14 | // signal input hash[256]; 15 | 16 | signal output hashout[256]; 17 | 18 | component sha256 = Sha256(2032); 19 | sha256.in <== userEmailAddress; 20 | hashout <== sha256.out; 21 | 22 | component privToAddr = PrivKeyToAddr(64, k); // 4 23 | 24 | for (var i = 0; i < 4; i++) { 25 | privToAddr.privkey[i] <== privkey[i]; 26 | } 27 | privToAddr.publickey <== publickey; 28 | 29 | // publickey === privToAddr.addr; 30 | 31 | } 32 | 33 | component main {public [userEmailAddress, privkey, publickey]} = zkBlind(4); 34 | 35 | 36 | // template zkBlind() { 37 | // signal input userEmailAddress[8]; 38 | // // signal input userEmailSuffix[2032]; 39 | 40 | // // signal private input userSigR[4]; 41 | // // signal private input userSigS[4]; 42 | // // signal private input userEthAddressSha256Hash[4]; 43 | // // signal private input userPubKey[2][4]; 44 | 45 | // signal output userID[256]; 46 | 47 | // component sha256Hash = Sha256(2032); 48 | 49 | // for (var i = 0; i < 2032; i++) { 50 | // sha256Hash.in[i] <== userEmailAddress[i]; 51 | // } 52 | 53 | // for (var i = 0; i < 256; i++) { 54 | // userID[i] <== sha256Hash.out[i]; 55 | // } 56 | // } 57 | 58 | // component main {public [userEmailAddress]} = zkBlind(); // 4 -------------------------------------------------------------------------------- /contracts/lib/forge-std/src/Base.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | import {StdStorage} from "./StdStorage.sol"; 5 | import {Vm, VmSafe} from "./Vm.sol"; 6 | 7 | abstract contract CommonBase { 8 | // Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D. 9 | address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); 10 | // console.sol and console2.sol work by executing a staticcall to this address. 11 | address internal constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67; 12 | // Default address for tx.origin and msg.sender, 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38. 13 | address internal constant DEFAULT_SENDER = address(uint160(uint256(keccak256("foundry default caller")))); 14 | // Address of the test contract, deployed by the DEFAULT_SENDER. 15 | address internal constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; 16 | // Deterministic deployment address of the Multicall3 contract. 17 | address internal constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11; 18 | 19 | uint256 internal constant UINT256_MAX = 20 | 115792089237316195423570985008687907853269984665640564039457584007913129639935; 21 | 22 | Vm internal constant vm = Vm(VM_ADDRESS); 23 | StdStorage internal stdstore; 24 | } 25 | 26 | abstract contract TestBase is CommonBase {} 27 | 28 | abstract contract ScriptBase is CommonBase { 29 | // Used when deploying with create2, https://github.com/Arachnid/deterministic-deployment-proxy. 30 | address internal constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C; 31 | 32 | VmSafe internal constant vmSafe = VmSafe(VM_ADDRESS); 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zkblind", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "test": "mocha", 11 | "gen-input": "npx tsx scripts/generateInputs.ts" 12 | }, 13 | "dependencies": { 14 | "@emotion/react": "^11.10.6", 15 | "@ethereumjs/util": "^8.0.6", 16 | "@headlessui/react": "^1.7.14", 17 | "@heroicons/react": "^2.0.17", 18 | "@mantine/core": "^6.0.8", 19 | "@mantine/hooks": "^6.0.8", 20 | "@mantine/notifications": "^6.0.8", 21 | "@noble/secp256k1": "^2.0.0", 22 | "@personaelabs/spartan-ecdsa": "^2.1.1", 23 | "@rainbow-me/rainbowkit": "^0.12.9", 24 | "@types/react": "18.0.38", 25 | "@types/react-dom": "18.0.11", 26 | "axios": "^1.3.6", 27 | "chai": "^4.3.6", 28 | "child_process": "^1.0.2", 29 | "circomlib": "^2.0.5", 30 | "ethers": "^5", 31 | "ffjavascript": "^0.2.56", 32 | "fnv-plus": "^1.3.1", 33 | "js-sha256": "^0.9.0", 34 | "next": "13.3.1", 35 | "r1csfile": "^0.0.41", 36 | "react": "18.2.0", 37 | "react-dom": "18.2.0", 38 | "react-icons": "^4.8.0", 39 | "snarkjs": "^0.6.10", 40 | "tabler-icons-react": "^1.56.0", 41 | "tmp-promise": "^3.0.3", 42 | "util": "^0.12.4", 43 | "wagmi": "^0.12.12" 44 | }, 45 | "devDependencies": { 46 | "@types/chai": "^4.3.1", 47 | "@types/mocha": "^9.1.1", 48 | "@types/node": "^18.16.0", 49 | "esbuild": "^0.17.18", 50 | "esbuild-register": "^3.4.2", 51 | "eslint": "^8.39.0", 52 | "eslint-config-next": "^13.3.1", 53 | "eslint-plugin-import": "^2.27.5", 54 | "eslint-plugin-react": "^7.32.2", 55 | "esm": "^3.2.25", 56 | "mocha": "^10.2.0", 57 | "ts-node": "^10.9.1", 58 | "typescript": "^5.0.4" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | createStyles, 3 | Title, 4 | Text, 5 | Button, 6 | Container, 7 | Group, 8 | } from "@mantine/core"; 9 | 10 | const useStyles = createStyles((theme) => ({ 11 | root: { 12 | paddingTop: 80, 13 | paddingBottom: 80, 14 | }, 15 | 16 | label: { 17 | textAlign: "center", 18 | fontWeight: 900, 19 | fontSize: 220, 20 | lineHeight: 1, 21 | marginBottom: theme.spacing.xl, 22 | color: 23 | theme.colorScheme === "dark" 24 | ? theme.colors.dark[4] 25 | : theme.colors.gray[2], 26 | 27 | [theme.fn.smallerThan("sm")]: { 28 | fontSize: 120, 29 | }, 30 | }, 31 | 32 | title: { 33 | fontFamily: `Greycliff CF, ${theme.fontFamily}`, 34 | textAlign: "center", 35 | fontWeight: 900, 36 | fontSize: 38, 37 | 38 | [theme.fn.smallerThan("sm")]: { 39 | fontSize: 32, 40 | }, 41 | }, 42 | 43 | description: { 44 | maxWidth: 500, 45 | margin: "auto", 46 | marginTop: theme.spacing.xl, 47 | marginBottom: theme.spacing.xl, 48 | }, 49 | })); 50 | 51 | export default function NotFoundPage() { 52 | const { classes } = useStyles(); 53 | 54 | return ( 55 | 56 |
404
57 | You have found a secret place. 58 | 64 | Unfortunately, this is only a 404 page. You may have mistyped the 65 | address, or the page has been moved to another URL. 66 | 67 | 68 | 71 | 72 |
73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /scripts/3_gen_chunk_zkey.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the absolute path of the current script 4 | SCRIPT_PATH="$(realpath "${BASH_SOURCE[0]}")" 5 | 6 | # Find the directory containing the script 7 | SCRIPT_DIR="$(dirname "${SCRIPT_PATH}")" 8 | 9 | # Get the root path of the repository by going up a level (assuming the script is in a 'scripts' directory) 10 | REPO_ROOT="$(dirname "${SCRIPT_DIR}")" 11 | 12 | # Source the circuit.env file relative to the repository root 13 | source "${REPO_ROOT}/scripts/circuit.env" 14 | 15 | R1CS_FILE="$BUILD_DIR/$CIRCUIT_NAME.r1cs" 16 | PARTIAL_ZKEYS="$BUILD_DIR"/partial_zkeys 17 | PHASE1="${REPO_ROOT}/circuits/powersOfTau28_hez_final_22.ptau" 18 | 19 | source "${REPO_ROOT}/scripts/entropy.env" 20 | 21 | if [ ! -d "$BUILD_DIR"/partial_zkeys ]; then 22 | echo "No partial_zkeys directory found. Creating partial_zkeys directory..." 23 | mkdir -p "$BUILD_DIR"/partial_zkeys 24 | fi 25 | 26 | echo "****GENERATING ZKEY NONCHUNKED 0****" 27 | start=$(date +%s) 28 | set -x 29 | NODE_OPTIONS='--max-old-space-size=56000' npx snarkjs groth16 setup "$R1CS_FILE" "$PHASE1" "$PARTIAL_ZKEYS"/"$CIRCUIT_NAME"_0.zkey -e=$ENTROPY1 30 | { set +x; } 2>/dev/null 31 | end=$(date +%s) 32 | echo "DONE ($((end - start))s)" 33 | echo 34 | 35 | echo "****GENERATING ZKEY NONCHUNKED 1****" 36 | start=$(date +%s) 37 | set -x 38 | NODE_OPTIONS='--max-old-space-size=56000' npx snarkjs zkey contribute "$PARTIAL_ZKEYS"/"$CIRCUIT_NAME"_0.zkey "$PARTIAL_ZKEYS"/"$CIRCUIT_NAME"_1.zkey --name="1st Contributor Name" -v -e=$ENTROPY2 39 | { set +x; } 2>/dev/null 40 | end=$(date +%s) 41 | echo "DONE ($((end - start))s)" 42 | echo 43 | 44 | echo "****GENERATING ZKEY NONCHUNKED FINAL****" 45 | start=$(date +%s) 46 | set -x 47 | NODE_OPTIONS='--max-old-space-size=56000' npx snarkjs zkey beacon "$PARTIAL_ZKEYS"/"$CIRCUIT_NAME"_1.zkey "$BUILD_DIR"/"$CIRCUIT_NAME".zkey $BEACON 10 -n="Final Beacon phase2" 48 | { set +x; } 2>/dev/null 49 | end=$(date +%s) 50 | echo "DONE ($((end - start))s)" 51 | echo 52 | -------------------------------------------------------------------------------- /circuits/email-suffix/test/email-suffix.ts: -------------------------------------------------------------------------------- 1 | 2 | import path from "path"; 3 | import wasm_tester from "../../../wasm_tester"; 4 | import { 5 | setupDirectories, 6 | padEmailTo2032Bits, 7 | stringToBitArray, 8 | getEmailSuffixStartingIndexInBitArray, 9 | bigint_to_array, 10 | bitArrayToBigInt 11 | } from "../../test_utils" 12 | import { expect } from 'chai'; 13 | 14 | const pathToCircom = "./email-suffix.circom" 15 | 16 | describe("Test email suffix", function () { 17 | const output = setupDirectories(pathToCircom); 18 | 19 | it("Checks email suffix", async function () { 20 | const email = "example@example.com"; 21 | 22 | const paddedEmail = padEmailTo2032Bits(email); 23 | 24 | if (!paddedEmail) { 25 | throw ("The email address is not valid."); 26 | } 27 | 28 | const emailAddressInputBits = stringToBitArray(paddedEmail); 29 | 30 | const emailSuffixStartingIndex = getEmailSuffixStartingIndexInBitArray(paddedEmail); 31 | 32 | // used to create the circuit 33 | console.log(emailSuffixStartingIndex) 34 | 35 | const emailSuffix = `@${email.split('@')[1]}`; 36 | 37 | const paddedEmailSuffix = padEmailTo2032Bits(emailSuffix); 38 | 39 | if (!paddedEmailSuffix) { 40 | throw ("The email suffix is not valid."); 41 | } 42 | 43 | const emailAddressSuffixInputBits = stringToBitArray(paddedEmailSuffix); 44 | 45 | const emailAddressSuffixBigInt = bitArrayToBigInt(emailAddressSuffixInputBits); 46 | 47 | const emailAddressSuffixInput = bigint_to_array(128, 16, emailAddressSuffixBigInt); 48 | 49 | const circuit = await wasm_tester( 50 | path.join(__dirname, pathToCircom), 51 | { 52 | output 53 | } 54 | ); 55 | 56 | const w = await circuit.calculateWitness({ 57 | userEmailAddress: emailAddressInputBits, 58 | userEmailSuffix: emailAddressSuffixInput, 59 | }); 60 | 61 | expect(w[1]).to.equal(1n); 62 | 63 | await circuit.checkConstraints(w); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## ZeroSecret 4 | ZeroSecret is a Zero Knowledge Proover. ZKProof is shoen with this p;roject. 5 | 6 | ## Getting Started 7 | 8 | First, run the development server: 9 | 10 | ```bash 11 | npm run dev 12 | # or 13 | yarn dev 14 | # or 15 | pnpm dev 16 | ``` 17 | 18 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 19 | 20 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. 21 | 22 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. 23 | 24 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 25 | 26 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 27 | 28 | ## Learn More 29 | 30 | To learn more about Next.js, take a look at the following resources: 31 | 32 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 33 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 34 | 35 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 36 | 37 | ## Deploy on Vercel 38 | 39 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 40 | 41 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 42 | 43 | Video Demo : https://www.youtube.com/watch?v=Hn3aHhYUg4M 44 | -------------------------------------------------------------------------------- /circuits/email-suffix/email-suffix.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../../node_modules/circomlib/circuits/sha256/sha256.circom"; 4 | include "../../node_modules/circomlib/circuits/comparators.circom"; 5 | include "../../node_modules/circomlib/circuits/bitify.circom"; 6 | include "../../node_modules/circomlib/circuits/multiplexer.circom"; 7 | 8 | 9 | template emailSuffix(emailSuffixStartingIndex) { 10 | signal input userEmailAddress[2032]; 11 | signal input userEmailSuffix[16]; // 127 bits 12 | 13 | signal output isValid; 14 | 15 | signal userEmailSuffixBitsArray[2032]; 16 | 17 | component num2Bits[16]; 18 | 19 | // Convert each userEmailSuffix element to bits 20 | for (var i = 0; i < 16; i++) { 21 | num2Bits[i] = Num2Bits(127); 22 | num2Bits[i].in <== userEmailSuffix[i]; 23 | } 24 | 25 | // Initialize userEmailSuffixBitsArray with 0s 26 | for (var i = 0; i < 2032; i++) { 27 | if (i < emailSuffixStartingIndex) { 28 | userEmailSuffixBitsArray[i] <== userEmailAddress[i]; 29 | // log("i < emailSuffixStartingIndex", userEmailSuffixBitsArray[i]); 30 | 31 | } else { 32 | var x = (i - emailSuffixStartingIndex) \ 127; 33 | var y = (i - emailSuffixStartingIndex) % 127; 34 | userEmailSuffixBitsArray[i] <== num2Bits[x].out[y]; 35 | // log("i >= emailSuffixStartingIndex", x, y, userEmailSuffixBitsArray[i]); 36 | } 37 | } 38 | 39 | component isEqual[2032]; 40 | // compare 41 | for (var i = 0; i < 2032; i++) { 42 | isEqual[i] = IsEqual(); 43 | isEqual[i].in[0] <== userEmailSuffixBitsArray[i]; 44 | isEqual[i].in[1] <== userEmailAddress[i]; 45 | // log("compare", userEmailSuffixBitsArray[i], userEmailAddress[i]); 46 | } 47 | 48 | signal isValidArray[2033]; 49 | isValidArray[0] <== 1; 50 | for (var i = 1; i < 2033; i++) { 51 | isValidArray[i] <== isEqual[i-1].out * isValidArray[i-1]; 52 | } 53 | 54 | // Output the result 55 | isValid <== isValidArray[2032]; 56 | } 57 | -------------------------------------------------------------------------------- /circuits/simple_multiplier/simple_multiplier.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.3; 2 | 3 | include "../../node_modules/circomlib/circuits/comparators.circom"; 4 | 5 | template SimpleMultiplier() { 6 | // Private input signals 7 | signal input in[2]; 8 | 9 | // Output signal (public) 10 | signal output out; 11 | 12 | // Create a constraint here saying that our two input signals cannot 13 | // equal each other. 14 | component isz = IsZero(); 15 | isz.in <== in[0] - in[1]; 16 | 17 | // The IsZero component returns 1 if the input is 0, or 0 otherwise. 18 | isz.out === 0; 19 | 20 | // Define the greater than and less than components that we'll define 21 | // inside the for loop below. 22 | component gte[2]; 23 | component lte[2]; 24 | 25 | // We loop through the two signals to compare them. 26 | for (var i = 0; i < 2; i++) { 27 | // Both the LessEqThan and GreaterEqThan components take number of 28 | // bits as an input. In this case, we want to ensure our inputs are 29 | // [0,5], which requires 3 bits (101). 30 | lte[i] = LessEqThan(3); 31 | 32 | // We put our circuit's input signal as the input signal to the 33 | // LessEqThan component and compare it against 5. 34 | lte[i].in[0] <== in[i]; 35 | lte[i].in[1] <== 5; 36 | 37 | // The LessEqThan component outputs a 1 if the evaluation is true, 38 | // 0 otherwise, so we create this equality constraint. 39 | lte[i].out === 1; 40 | 41 | // We do the same with GreaterEqThan, and also require 3 bits since 42 | // the range of inputs is still [0,5]. 43 | gte[i] = GreaterEqThan(3); 44 | 45 | // Compare our input with 0 46 | gte[i].in[0] <== in[i]; 47 | gte[i].in[1] <== 0; 48 | 49 | // The GreaterEqThan component outputs a 1 if the evaluation is true, 50 | // 0 otherwise, so we create this equality constraint. 51 | gte[i].out === 1; 52 | } 53 | 54 | // Write a * b into c and then constrain c to be equal to a * b. 55 | out <== in[0] * in[1]; 56 | } 57 | 58 | component main = SimpleMultiplier(); -------------------------------------------------------------------------------- /contracts/lib/forge-std/src/interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | /// @dev Interface of the ERC20 standard as defined in the EIP. 5 | /// @dev This includes the optional name, symbol, and decimals metadata. 6 | interface IERC20 { 7 | /// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`). 8 | event Transfer(address indexed from, address indexed to, uint256 value); 9 | 10 | /// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value` 11 | /// is the new allowance. 12 | event Approval(address indexed owner, address indexed spender, uint256 value); 13 | 14 | /// @notice Returns the amount of tokens in existence. 15 | function totalSupply() external view returns (uint256); 16 | 17 | /// @notice Returns the amount of tokens owned by `account`. 18 | function balanceOf(address account) external view returns (uint256); 19 | 20 | /// @notice Moves `amount` tokens from the caller's account to `to`. 21 | function transfer(address to, uint256 amount) external returns (bool); 22 | 23 | /// @notice Returns the remaining number of tokens that `spender` is allowed 24 | /// to spend on behalf of `owner` 25 | function allowance(address owner, address spender) external view returns (uint256); 26 | 27 | /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens. 28 | /// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 29 | function approve(address spender, uint256 amount) external returns (bool); 30 | 31 | /// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism. 32 | /// `amount` is then deducted from the caller's allowance. 33 | function transferFrom(address from, address to, uint256 amount) external returns (bool); 34 | 35 | /// @notice Returns the name of the token. 36 | function name() external view returns (string memory); 37 | 38 | /// @notice Returns the symbol of the token. 39 | function symbol() external view returns (string memory); 40 | 41 | /// @notice Returns the decimals places of the token. 42 | function decimals() external view returns (uint8); 43 | } 44 | -------------------------------------------------------------------------------- /circuits/eff_ecdsa/test/eff_ecdsa_to_addr.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import wasm_tester from "../../../wasm_tester"; 3 | import { setupDirectories } from "../../test_utils" 4 | import { privateToAddress, hashPersonalMessage, ecsign } from "@ethereumjs/util"; 5 | import { computeEffEcdsaPubInput } from "@personaelabs/spartan-ecdsa"; 6 | var EC = require("elliptic").ec; 7 | 8 | const ec = new EC("secp256k1"); 9 | const pathToCircom = "../eff_ecdsa_to_addr.circom" 10 | 11 | const F1Field = require("ffjavascript").F1Field; 12 | const Scalar = require("ffjavascript").Scalar; 13 | 14 | export const getEffEcdsaCircuitInput = (privKey: Buffer, msg: Buffer) => { 15 | const msgHash = hashPersonalMessage(msg); 16 | const { v, r: _r, s } = ecsign(msgHash, privKey); 17 | 18 | console.log(_r, s) 19 | 20 | const r = BigInt("0x" + _r.toString("hex")); 21 | 22 | const circuitPubInput = computeEffEcdsaPubInput(r, v, msgHash); 23 | const input = { 24 | s: BigInt("0x" + s.toString("hex")), 25 | Tx: circuitPubInput.Tx, 26 | Ty: circuitPubInput.Ty, 27 | Ux: circuitPubInput.Ux, 28 | Uy: circuitPubInput.Uy 29 | }; 30 | 31 | return input; 32 | }; 33 | 34 | export const bytesToBigInt = (bytes: Uint8Array): bigint => 35 | BigInt("0x" + Buffer.from(bytes).toString("hex")); 36 | 37 | 38 | describe("eff_ecdsa_to_addr", () => { 39 | 40 | it("should output correct address", async () => { 41 | 42 | const outputDir = setupDirectories(pathToCircom); 43 | 44 | const circuit = await wasm_tester( 45 | path.join(__dirname, pathToCircom), 46 | { 47 | output: outputDir, 48 | prime: "secq256k1" 49 | } 50 | ); 51 | 52 | const msg = Buffer.from("hello world"); 53 | 54 | const privKey = Buffer.from( 55 | "f5b552f608f5b552f608f5b552f6082ff5b552f608f5b552f608f5b552f6082f", 56 | "hex" 57 | ); 58 | const addr = BigInt( 59 | "0x" + privateToAddress(privKey).toString("hex") 60 | ).toString(10); 61 | 62 | const circuitInput = getEffEcdsaCircuitInput(privKey, msg); 63 | 64 | const w = await circuit.calculateWitness(circuitInput, true); 65 | 66 | await circuit.assertOut(w, { 67 | addr 68 | }); 69 | 70 | await circuit.checkConstraints(w); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /circuits/zkblind/test/zkblindTest.ts: -------------------------------------------------------------------------------- 1 | 2 | import path from "path"; 3 | import wasm_tester from "../../../wasm_tester"; 4 | import { 5 | setupDirectories, 6 | padEmailTo2032Bits, 7 | stringToBitArray, 8 | getCircuitInputWithAddrAndSig, 9 | getAddrAndSig, 10 | bitArrayToBigInt, 11 | extractLeastSignificantBits, 12 | bigint_to_array 13 | } from "../../test_utils" 14 | import { sha256 } from 'js-sha256'; 15 | import { expect } from 'chai'; 16 | 17 | const pathToCircom = "./zkbindTest.circom" 18 | 19 | describe("Test zkblind", function () { 20 | const output = setupDirectories(pathToCircom); 21 | 22 | it("Checks zkblind", async function () { 23 | const email = "example@example.com"; 24 | 25 | const paddedEmail = padEmailTo2032Bits(email); 26 | 27 | if (!paddedEmail) { 28 | throw ("The email address is not valid."); 29 | } 30 | 31 | const emailAddressInputBits = stringToBitArray(paddedEmail); 32 | 33 | const emailSuffix = `@${email.split('@')[1]}`; 34 | const paddedEmailSuffix = padEmailTo2032Bits(emailSuffix); 35 | if (!paddedEmailSuffix) { 36 | throw ("The email suffix is not valid."); 37 | } 38 | const emailAddressSuffixInputBits = stringToBitArray(paddedEmailSuffix); 39 | const emailAddressSuffixBigInt = bitArrayToBigInt(emailAddressSuffixInputBits); 40 | const emailAddressSuffixInput = bigint_to_array(128, 16, emailAddressSuffixBigInt); 41 | 42 | const hash = sha256(paddedEmail); 43 | 44 | const userId = extractLeastSignificantBits(hash, 216); 45 | 46 | const privateKey = "f5b552f608f5b552f608f5b552f6082ff5b552f608f5b552f608f5b552f6082f" 47 | const addrAndSig = getAddrAndSig(privateKey) 48 | const sigInput = getCircuitInputWithAddrAndSig(addrAndSig) 49 | 50 | const circuit = await wasm_tester( 51 | path.join(__dirname, pathToCircom), 52 | { 53 | output 54 | } 55 | ); 56 | 57 | const w = await circuit.calculateWitness({ 58 | userId: userId, 59 | userEmailAddress: emailAddressInputBits, 60 | userEmailSuffix: emailAddressSuffixInput, 61 | userEthAddr: 0n, 62 | userSigR: sigInput.r, 63 | userSigS: sigInput.s, 64 | userEthAddressSha256Hash: sigInput.msghash, 65 | userPubKey: sigInput.pubkey, 66 | }); 67 | 68 | console.log(w[1]) 69 | expect(w[1]).to.equal(1n); 70 | 71 | await circuit.checkConstraints(w); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/src/interfaces/IMulticall3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | interface IMulticall3 { 7 | struct Call { 8 | address target; 9 | bytes callData; 10 | } 11 | 12 | struct Call3 { 13 | address target; 14 | bool allowFailure; 15 | bytes callData; 16 | } 17 | 18 | struct Call3Value { 19 | address target; 20 | bool allowFailure; 21 | uint256 value; 22 | bytes callData; 23 | } 24 | 25 | struct Result { 26 | bool success; 27 | bytes returnData; 28 | } 29 | 30 | function aggregate(Call[] calldata calls) 31 | external 32 | payable 33 | returns (uint256 blockNumber, bytes[] memory returnData); 34 | 35 | function aggregate3(Call3[] calldata calls) external payable returns (Result[] memory returnData); 36 | 37 | function aggregate3Value(Call3Value[] calldata calls) external payable returns (Result[] memory returnData); 38 | 39 | function blockAndAggregate(Call[] calldata calls) 40 | external 41 | payable 42 | returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData); 43 | 44 | function getBasefee() external view returns (uint256 basefee); 45 | 46 | function getBlockHash(uint256 blockNumber) external view returns (bytes32 blockHash); 47 | 48 | function getBlockNumber() external view returns (uint256 blockNumber); 49 | 50 | function getChainId() external view returns (uint256 chainid); 51 | 52 | function getCurrentBlockCoinbase() external view returns (address coinbase); 53 | 54 | function getCurrentBlockDifficulty() external view returns (uint256 difficulty); 55 | 56 | function getCurrentBlockGasLimit() external view returns (uint256 gaslimit); 57 | 58 | function getCurrentBlockTimestamp() external view returns (uint256 timestamp); 59 | 60 | function getEthBalance(address addr) external view returns (uint256 balance); 61 | 62 | function getLastBlockHash() external view returns (bytes32 blockHash); 63 | 64 | function tryAggregate(bool requireSuccess, Call[] calldata calls) 65 | external 66 | payable 67 | returns (Result[] memory returnData); 68 | 69 | function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) 70 | external 71 | payable 72 | returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData); 73 | } 74 | -------------------------------------------------------------------------------- /scripts/archived_build_eth_addr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PHASE1=../../pot20_final.ptau 4 | BUILD_DIR=./build/eth_addr 5 | CIRCUIT_NAME=eth_addr 6 | 7 | if [ -f "$PHASE1" ]; then 8 | echo "Found Phase 1 ptau file" 9 | else 10 | echo "No Phase 1 ptau file found. Exiting..." 11 | exit 1 12 | fi 13 | 14 | if [ ! -d "$BUILD_DIR" ]; then 15 | echo "No build directory found. Creating build directory..." 16 | mkdir -p "$BUILD_DIR" 17 | fi 18 | 19 | echo "****COMPILING CIRCUIT****" 20 | start=`date +%s` 21 | circom "$CIRCUIT_NAME".circom --r1cs --wasm --sym --c --wat --output "$BUILD_DIR" 22 | end=`date +%s` 23 | echo "DONE ($((end-start))s)" 24 | 25 | echo "****GENERATING WITNESS FOR SAMPLE INPUT****" 26 | start=`date +%s` 27 | node "$BUILD_DIR"/"$CIRCUIT_NAME"_js/generate_witness.js "$BUILD_DIR"/"$CIRCUIT_NAME"_js/"$CIRCUIT_NAME".wasm input_eth_addr.json "$BUILD_DIR"/witness.wtns 28 | end=`date +%s` 29 | echo "DONE ($((end-start))s)" 30 | 31 | echo "****GENERATING ZKEY 0****" 32 | start=`date +%s` 33 | npx snarkjs groth16 setup "$BUILD_DIR"/"$CIRCUIT_NAME".r1cs "$PHASE1" "$BUILD_DIR"/"$CIRCUIT_NAME"_0.zkey 34 | end=`date +%s` 35 | echo "DONE ($((end-start))s)" 36 | 37 | echo "****CONTRIBUTE TO THE PHASE 2 CEREMONY****" 38 | start=`date +%s` 39 | echo "test" | npx snarkjs zkey contribute "$BUILD_DIR"/"$CIRCUIT_NAME"_0.zkey "$BUILD_DIR"/"$CIRCUIT_NAME"_1.zkey --name="1st Contributor Name" 40 | end=`date +%s` 41 | echo "DONE ($((end-start))s)" 42 | 43 | echo "****GENERATING FINAL ZKEY****" 44 | start=`date +%s` 45 | npx snarkjs zkey beacon "$BUILD_DIR"/"$CIRCUIT_NAME"_1.zkey "$BUILD_DIR"/"$CIRCUIT_NAME".zkey 0102030405060708090a0b0c0d0e0f101112231415161718221a1b1c1d1e1f 10 -n="Final Beacon phase2" 46 | end=`date +%s` 47 | echo "DONE ($((end-start))s)" 48 | 49 | echo "****VERIFYING FINAL ZKEY****" 50 | start=`date +%s` 51 | npx snarkjs zkey verify "$BUILD_DIR"/"$CIRCUIT_NAME".r1cs "$PHASE1" "$BUILD_DIR"/"$CIRCUIT_NAME".zkey 52 | end=`date +%s` 53 | echo "DONE ($((end-start))s)" 54 | 55 | echo "****EXPORTING VKEY****" 56 | start=`date +%s` 57 | npx snarkjs zkey export verificationkey "$BUILD_DIR"/"$CIRCUIT_NAME".zkey "$BUILD_DIR"/vkey.json 58 | end=`date +%s` 59 | echo "DONE ($((end-start))s)" 60 | 61 | echo "****GENERATING PROOF FOR SAMPLE INPUT****" 62 | start=`date +%s` 63 | npx snarkjs groth16 prove "$BUILD_DIR"/"$CIRCUIT_NAME".zkey "$BUILD_DIR"/witness.wtns "$BUILD_DIR"/proof.json "$BUILD_DIR"/public.json 64 | end=`date +%s` 65 | echo "DONE ($((end-start))s)" 66 | 67 | echo "****VERIFYING PROOF FOR SAMPLE INPUT****" 68 | start=`date +%s` 69 | npx snarkjs groth16 verify "$BUILD_DIR"/vkey.json "$BUILD_DIR"/public.json "$BUILD_DIR"/proof.json 70 | end=`date +%s` 71 | echo "DONE ($((end-start))s)" 72 | -------------------------------------------------------------------------------- /circuits/zkblind/zkblind.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.2; 2 | 3 | include "../ecsda-circom/ecdsa.circom"; 4 | include "../ecsda-circom/eth_addr_2.circom"; 5 | include "../../node_modules/circomlib/circuits/sha256/sha256.circom"; 6 | include "../../node_modules/circomlib/circuits/comparators.circom"; 7 | include "../../node_modules/circomlib/circuits/bitify.circom"; 8 | include "../email-suffix/email-suffix.circom"; 9 | 10 | template zkBlind(emailSuffixStartingIndex) { 11 | signal input userId; 12 | signal input userEmailAddress[2032]; 13 | signal input userEmailSuffix[16]; // 127 bits 14 | 15 | signal input userEthAddr; 16 | 17 | signal input userSigR[4]; 18 | signal input userSigS[4]; 19 | signal input userEthAddressSha256Hash[4]; 20 | signal input userPubKey[2][4]; 21 | 22 | signal output isValid; 23 | 24 | // Constraint 1: User ID is SHA-256 of the user email address 25 | 26 | component sha256Hash = Sha256(2032); 27 | for (var i = 0; i < 2032; i++) { 28 | sha256Hash.in[i] <== userEmailAddress[i]; 29 | } 30 | 31 | component bits2num = Bits2Num(216); 32 | 33 | for (var i=0; i<216; i++) { 34 | bits2num.in[i] <== sha256Hash.out[255-i]; 35 | } 36 | 37 | component isUserIdEqual = IsEqual(); 38 | isUserIdEqual.in[0] <== userId; 39 | isUserIdEqual.in[1] <== bits2num.out; 40 | 41 | 42 | // Constraint 2: User email address is the sender in the email 43 | 44 | 45 | // Constraint 3: User email address suffix is the suffix of the user email address 46 | component emailSuffixCheck = emailSuffix(emailSuffixStartingIndex); 47 | for (var i = 0; i < 2032; i++) { 48 | emailSuffixCheck.userEmailAddress[i] <== userEmailAddress[i]; 49 | } 50 | for (var i = 0; i < 16; i++) { 51 | emailSuffixCheck.userEmailSuffix[i] <== userEmailSuffix[i]; 52 | } 53 | 54 | // Constraint 4: User signature of the ETH address in the email is valid 55 | component ecdsaVerifyNoPubkeyCheck = ECDSAVerifyNoPubkeyCheck(64, 4); 56 | 57 | for (var i = 0; i < 4; i++) { 58 | ecdsaVerifyNoPubkeyCheck.r[i] <== userSigR[i]; 59 | ecdsaVerifyNoPubkeyCheck.s[i] <== userSigS[i]; 60 | ecdsaVerifyNoPubkeyCheck.msghash[i] <== userEthAddressSha256Hash[i]; 61 | } 62 | 63 | for (var i = 0; i < 2; i++) { 64 | for (var j = 0; j < 4; j++) { 65 | ecdsaVerifyNoPubkeyCheck.pubkey[i][j] <== userPubKey[i][j]; 66 | } 67 | } 68 | 69 | // Enforce that the signature is valid 70 | ecdsaVerifyNoPubkeyCheck.result === 1; 71 | 72 | // Constraint 5: User ETH address and signature is the only email body 73 | 74 | // Constraint 6: User email's dkim signature is valid 75 | 76 | // output if all the constraints are met 77 | signal isValid1 <== isUserIdEqual.out * ecdsaVerifyNoPubkeyCheck.result; 78 | isValid <== isValid1 * emailSuffixCheck.isValid; 79 | } 80 | -------------------------------------------------------------------------------- /contracts/lib/forge-std/.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Install Foundry 17 | uses: onbjerg/foundry-toolchain@v1 18 | with: 19 | version: nightly 20 | 21 | - name: Print forge version 22 | run: forge --version 23 | 24 | # Backwards compatibility checks. 25 | - name: Check compatibility with 0.8.0 26 | if: always() 27 | run: forge build --skip test --use solc:0.8.0 28 | 29 | - name: Check compatibility with 0.7.6 30 | if: always() 31 | run: forge build --skip test --use solc:0.7.6 32 | 33 | - name: Check compatibility with 0.7.0 34 | if: always() 35 | run: forge build --skip test --use solc:0.7.0 36 | 37 | - name: Check compatibility with 0.6.12 38 | if: always() 39 | run: forge build --skip test --use solc:0.6.12 40 | 41 | - name: Check compatibility with 0.6.2 42 | if: always() 43 | run: forge build --skip test --use solc:0.6.2 44 | 45 | # via-ir compilation time checks. 46 | - name: Measure compilation time of Test with 0.8.17 --via-ir 47 | if: always() 48 | run: forge build --skip test --contracts test/compilation/CompilationTest.sol --use solc:0.8.17 --via-ir 49 | 50 | - name: Measure compilation time of TestBase with 0.8.17 --via-ir 51 | if: always() 52 | run: forge build --skip test --contracts test/compilation/CompilationTestBase.sol --use solc:0.8.17 --via-ir 53 | 54 | - name: Measure compilation time of Script with 0.8.17 --via-ir 55 | if: always() 56 | run: forge build --skip test --contracts test/compilation/CompilationScript.sol --use solc:0.8.17 --via-ir 57 | 58 | - name: Measure compilation time of ScriptBase with 0.8.17 --via-ir 59 | if: always() 60 | run: forge build --skip test --contracts test/compilation/CompilationScriptBase.sol --use solc:0.8.17 --via-ir 61 | 62 | test: 63 | runs-on: ubuntu-latest 64 | steps: 65 | - uses: actions/checkout@v3 66 | 67 | - name: Install Foundry 68 | uses: onbjerg/foundry-toolchain@v1 69 | with: 70 | version: nightly 71 | 72 | - name: Print forge version 73 | run: forge --version 74 | 75 | - name: Run tests 76 | run: forge test -vvv 77 | 78 | fmt: 79 | runs-on: ubuntu-latest 80 | steps: 81 | - uses: actions/checkout@v3 82 | 83 | - name: Install Foundry 84 | uses: onbjerg/foundry-toolchain@v1 85 | with: 86 | version: nightly 87 | 88 | - name: Print forge version 89 | run: forge --version 90 | 91 | - name: Check formatting 92 | run: forge fmt --check 93 | -------------------------------------------------------------------------------- /circuits/ecsda-circom/vocdoni-keccak/utils.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.2; 2 | 3 | include "../../../node_modules/circomlib/circuits/gates.circom"; 4 | include "../../../node_modules/circomlib/circuits/sha256/xor3.circom"; 5 | include "../../../node_modules/circomlib/circuits/sha256/shift.circom"; // contains ShiftRight 6 | 7 | template Xor5(n) { 8 | signal input a[n]; 9 | signal input b[n]; 10 | signal input c[n]; 11 | signal input d[n]; 12 | signal input e[n]; 13 | signal output out[n]; 14 | var i; 15 | 16 | component xor3 = Xor3(n); 17 | for (i=0; i 2 | 3 | -------------------------------------------------------------------------------- /circuits/ecsda-circom/bigint_4x64_mult.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.2; 2 | 3 | include "bigint.circom"; 4 | 5 | template A2NoCarry() { 6 | signal input a[4]; 7 | 8 | // these representations have overflowed, nonnegative registers 9 | signal output a2[7]; 10 | component a2Comp = BigMultNoCarry(64, 64, 64, 4, 4); 11 | for (var i = 0; i < 4; i++) { 12 | a2Comp.a[i] <== a[i]; 13 | a2Comp.b[i] <== a[i]; 14 | } 15 | for (var i = 0; i < 7; i++) { 16 | a2[i] <== a2Comp.out[i]; // 130 bits 17 | } 18 | } 19 | 20 | template A3NoCarry() { 21 | signal input a[4]; 22 | 23 | // these representations have overflowed, nonnegative registers 24 | signal a2[7]; 25 | component a2Comp = BigMultNoCarry(64, 64, 64, 4, 4); 26 | for (var i = 0; i < 4; i++) { 27 | a2Comp.a[i] <== a[i]; 28 | a2Comp.b[i] <== a[i]; 29 | } 30 | for (var i = 0; i < 7; i++) { 31 | a2[i] <== a2Comp.out[i]; // 130 bits 32 | } 33 | signal output a3[10]; 34 | component a3Comp = BigMultNoCarry(64, 130, 64, 7, 4); 35 | for (var i = 0; i < 7; i++) { 36 | a3Comp.a[i] <== a2[i]; 37 | } 38 | for (var i = 0; i < 4; i++) { 39 | a3Comp.b[i] <== a[i]; 40 | } 41 | for (var i = 0; i < 10; i++) { 42 | a3[i] <== a3Comp.out[i]; // 197 bits 43 | } 44 | } 45 | 46 | template A2B1NoCarry() { 47 | signal input a[4]; 48 | signal input b[4]; 49 | 50 | // these representations have overflowed, nonnegative registers 51 | signal a2[7]; 52 | component a2Comp = BigMultNoCarry(64, 64, 64, 4, 4); 53 | for (var i = 0; i < 4; i++) { 54 | a2Comp.a[i] <== a[i]; 55 | a2Comp.b[i] <== a[i]; 56 | } 57 | for (var i = 0; i < 7; i++) { 58 | a2[i] <== a2Comp.out[i]; // 130 bits 59 | } 60 | 61 | signal output a2b1[10]; 62 | component a2b1Comp = BigMultNoCarry(64, 130, 64, 7, 4); 63 | for (var i = 0; i < 7; i++) { 64 | a2b1Comp.a[i] <== a2[i]; 65 | } 66 | for (var i = 0; i < 4; i++) { 67 | a2b1Comp.b[i] <== b[i]; 68 | } 69 | for (var i = 0; i < 10; i++) { 70 | a2b1[i] <== a2b1Comp.out[i]; // 197 bits 71 | } 72 | } 73 | 74 | template A1B1C1NoCarry() { 75 | signal input a[4]; 76 | signal input b[4]; 77 | signal input c[4]; 78 | 79 | // these representations have overflowed, nonnegative registers 80 | signal a1b1[7]; 81 | component a1b1Comp = BigMultNoCarry(64, 64, 64, 4, 4); 82 | for (var i = 0; i < 4; i++) { 83 | a1b1Comp.a[i] <== a[i]; 84 | a1b1Comp.b[i] <== b[i]; 85 | } 86 | for (var i = 0; i < 7; i++) { 87 | a1b1[i] <== a1b1Comp.out[i]; // 130 bits 88 | } 89 | 90 | signal output a1b1c1[10]; 91 | component a1b1c1Comp = BigMultNoCarry(64, 130, 64, 7, 4); 92 | for (var i = 0; i < 7; i++) { 93 | a1b1c1Comp.a[i] <== a1b1[i]; 94 | } 95 | for (var i = 0; i < 4; i++) { 96 | a1b1c1Comp.b[i] <== c[i]; 97 | } 98 | for (var i = 0; i < 10; i++) { 99 | a1b1c1[i] <== a1b1c1Comp.out[i]; // 197 bits 100 | } 101 | } -------------------------------------------------------------------------------- /contracts/lib/forge-std/test/StdError.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import "../src/StdError.sol"; 5 | import "../src/Test.sol"; 6 | 7 | contract StdErrorsTest is Test { 8 | ErrorsTest test; 9 | 10 | function setUp() public { 11 | test = new ErrorsTest(); 12 | } 13 | 14 | function testExpectAssertion() public { 15 | vm.expectRevert(stdError.assertionError); 16 | test.assertionError(); 17 | } 18 | 19 | function testExpectArithmetic() public { 20 | vm.expectRevert(stdError.arithmeticError); 21 | test.arithmeticError(10); 22 | } 23 | 24 | function testExpectDiv() public { 25 | vm.expectRevert(stdError.divisionError); 26 | test.divError(0); 27 | } 28 | 29 | function testExpectMod() public { 30 | vm.expectRevert(stdError.divisionError); 31 | test.modError(0); 32 | } 33 | 34 | function testExpectEnum() public { 35 | vm.expectRevert(stdError.enumConversionError); 36 | test.enumConversion(1); 37 | } 38 | 39 | function testExpectEncodeStg() public { 40 | vm.expectRevert(stdError.encodeStorageError); 41 | test.encodeStgError(); 42 | } 43 | 44 | function testExpectPop() public { 45 | vm.expectRevert(stdError.popError); 46 | test.pop(); 47 | } 48 | 49 | function testExpectOOB() public { 50 | vm.expectRevert(stdError.indexOOBError); 51 | test.indexOOBError(1); 52 | } 53 | 54 | function testExpectMem() public { 55 | vm.expectRevert(stdError.memOverflowError); 56 | test.mem(); 57 | } 58 | 59 | function testExpectIntern() public { 60 | vm.expectRevert(stdError.zeroVarError); 61 | test.intern(); 62 | } 63 | } 64 | 65 | contract ErrorsTest { 66 | enum T {T1} 67 | 68 | uint256[] public someArr; 69 | bytes someBytes; 70 | 71 | function assertionError() public pure { 72 | assert(false); 73 | } 74 | 75 | function arithmeticError(uint256 a) public pure { 76 | a -= 100; 77 | } 78 | 79 | function divError(uint256 a) public pure { 80 | 100 / a; 81 | } 82 | 83 | function modError(uint256 a) public pure { 84 | 100 % a; 85 | } 86 | 87 | function enumConversion(uint256 a) public pure { 88 | T(a); 89 | } 90 | 91 | function encodeStgError() public { 92 | /// @solidity memory-safe-assembly 93 | assembly { 94 | sstore(someBytes.slot, 1) 95 | } 96 | keccak256(someBytes); 97 | } 98 | 99 | function pop() public { 100 | someArr.pop(); 101 | } 102 | 103 | function indexOOBError(uint256 a) public pure { 104 | uint256[] memory t = new uint256[](0); 105 | t[a]; 106 | } 107 | 108 | function mem() public pure { 109 | uint256 l = 2 ** 256 / 32; 110 | new uint256[](l); 111 | } 112 | 113 | function intern() public returns (uint256) { 114 | function(uint256) internal returns (uint256) x; 115 | x(2); 116 | return 7; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /circuits/ecsda-circom/zk-identity/eth.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.2; 2 | 3 | include "../vocdoni-keccak/keccak.circom"; 4 | 5 | include "../../../node_modules/circomlib/circuits/bitify.circom"; 6 | 7 | /* 8 | * Possibly generalizable, but for now just flatten a single pubkey from k n-bit chunks to a * single bit array 9 | * representing the entire pubkey 10 | * 11 | */ 12 | template FlattenPubkey(numBits, k) { 13 | signal input chunkedPubkey[2][k]; 14 | 15 | signal output pubkeyBits[512]; 16 | 17 | // must be able to hold entire pubkey in input 18 | assert(numBits*k >= 256); 19 | 20 | // convert pubkey to a single bit array 21 | // - concat x and y coords 22 | // - convert each register's number to corresponding bit array 23 | // - concatenate all bit arrays in order 24 | 25 | component chunks2BitsY[k]; 26 | for(var chunk = 0; chunk < k; chunk++){ 27 | chunks2BitsY[chunk] = Num2Bits(numBits); 28 | chunks2BitsY[chunk].in <== chunkedPubkey[1][chunk]; 29 | 30 | for(var bit = 0; bit < numBits; bit++){ 31 | var bitIndex = bit + numBits * chunk; 32 | if(bitIndex < 256) { 33 | pubkeyBits[bitIndex] <== chunks2BitsY[chunk].out[bit]; 34 | } 35 | } 36 | } 37 | 38 | component chunks2BitsX[k]; 39 | for(var chunk = 0; chunk < k; chunk++){ 40 | chunks2BitsX[chunk] = Num2Bits(numBits); 41 | chunks2BitsX[chunk].in <== chunkedPubkey[0][chunk]; 42 | 43 | for(var bit = 0; bit < numBits; bit++){ 44 | var bitIndex = bit + 256 + (numBits * chunk); 45 | if(bitIndex < 512) { 46 | pubkeyBits[bitIndex] <== chunks2BitsX[chunk].out[bit]; 47 | } 48 | } 49 | } 50 | } 51 | 52 | /* 53 | * Helper for verifying an eth address refers to the correct public key point 54 | * 55 | * NOTE: uses https://github.com/vocdoni/keccak256-circom, a highly experimental keccak256 implementation 56 | */ 57 | template PubkeyToAddress() { 58 | // public key is (x, y) curve point. this is a 512-bit little-endian bitstring representation of y + 2**256 * x 59 | signal input pubkeyBits[512]; 60 | 61 | signal output address; 62 | 63 | // our representation is little-endian 512-bit bitstring 64 | // keccak template operates on bytestrings one byte at a time, starting with the biggest byte 65 | // but bytes are represented as little-endian 8-bit bitstrings 66 | signal reverse[512]; 67 | 68 | for (var i = 0; i < 512; i++) { 69 | reverse[i] <== pubkeyBits[511-i]; 70 | } 71 | 72 | component keccak = Keccak(512, 256); 73 | for (var i = 0; i < 512 / 8; i += 1) { 74 | for (var j = 0; j < 8; j++) { 75 | keccak.in[8*i + j] <== reverse[8*i + (7-j)]; 76 | } 77 | } 78 | 79 | // convert the last 160 bits (20 bytes) into the number corresponding to address 80 | // the output of keccak is 32 bytes. bytes are arranged from largest to smallest 81 | // but bytes themselves are little-endian bitstrings of 8 bits 82 | // we just want a little-endian bitstring of 160 bits 83 | component bits2Num = Bits2Num(160); 84 | for (var i = 0; i < 20; i++) { 85 | for (var j = 0; j < 8; j++) { 86 | bits2Num.in[8*i + j] <== keccak.out[256 - 8*(i+1) + j]; 87 | } 88 | } 89 | 90 | address <== bits2Num.out; 91 | } -------------------------------------------------------------------------------- /circuits/eff_ecdsa/to_address/zk-identity/eth.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.2; 2 | 3 | include "../vocdoni-keccak/keccak.circom"; 4 | 5 | include "../../../../node_modules/circomlib/circuits/bitify.circom"; 6 | 7 | /* 8 | * Possibly generalizable, but for now just flatten a single pubkey from k n-bit chunks to a * single bit array 9 | * representing the entire pubkey 10 | * 11 | */ 12 | template FlattenPubkey(numBits, k) { 13 | signal input chunkedPubkey[2][k]; 14 | 15 | signal output pubkeyBits[512]; 16 | 17 | // must be able to hold entire pubkey in input 18 | assert(numBits*k >= 256); 19 | 20 | // convert pubkey to a single bit array 21 | // - concat x and y coords 22 | // - convert each register's number to corresponding bit array 23 | // - concatenate all bit arrays in order 24 | 25 | component chunks2BitsY[k]; 26 | for(var chunk = 0; chunk < k; chunk++){ 27 | chunks2BitsY[chunk] = Num2Bits(numBits); 28 | chunks2BitsY[chunk].in <== chunkedPubkey[1][chunk]; 29 | 30 | for(var bit = 0; bit < numBits; bit++){ 31 | var bitIndex = bit + numBits * chunk; 32 | if(bitIndex < 256) { 33 | pubkeyBits[bitIndex] <== chunks2BitsY[chunk].out[bit]; 34 | } 35 | } 36 | } 37 | 38 | component chunks2BitsX[k]; 39 | for(var chunk = 0; chunk < k; chunk++){ 40 | chunks2BitsX[chunk] = Num2Bits(numBits); 41 | chunks2BitsX[chunk].in <== chunkedPubkey[0][chunk]; 42 | 43 | for(var bit = 0; bit < numBits; bit++){ 44 | var bitIndex = bit + 256 + (numBits * chunk); 45 | if(bitIndex < 512) { 46 | pubkeyBits[bitIndex] <== chunks2BitsX[chunk].out[bit]; 47 | } 48 | } 49 | } 50 | } 51 | 52 | /* 53 | * Helper for verifying an eth address refers to the correct public key point 54 | * 55 | * NOTE: uses https://github.com/vocdoni/keccak256-circom, a highly experimental keccak256 implementation 56 | */ 57 | template PubkeyToAddress() { 58 | // public key is (x, y) curve point. this is a 512-bit little-endian bitstring representation of y + 2**256 * x 59 | signal input pubkeyBits[512]; 60 | 61 | signal output address; 62 | 63 | // our representation is little-endian 512-bit bitstring 64 | // keccak template operates on bytestrings one byte at a time, starting with the biggest byte 65 | // but bytes are represented as little-endian 8-bit bitstrings 66 | signal reverse[512]; 67 | 68 | for (var i = 0; i < 512; i++) { 69 | reverse[i] <== pubkeyBits[511-i]; 70 | } 71 | 72 | component keccak = Keccak(512, 256); 73 | for (var i = 0; i < 512 / 8; i += 1) { 74 | for (var j = 0; j < 8; j++) { 75 | keccak.in[8*i + j] <== reverse[8*i + (7-j)]; 76 | } 77 | } 78 | 79 | // convert the last 160 bits (20 bytes) into the number corresponding to address 80 | // the output of keccak is 32 bytes. bytes are arranged from largest to smallest 81 | // but bytes themselves are little-endian bitstrings of 8 bits 82 | // we just want a little-endian bitstring of 160 bits 83 | component bits2Num = Bits2Num(160); 84 | for (var i = 0; i < 20; i++) { 85 | for (var j = 0; j < 8; j++) { 86 | bits2Num.in[8*i + j] <== keccak.out[256 - 8*(i+1) + j]; 87 | } 88 | } 89 | 90 | address <== bits2Num.out; 91 | } -------------------------------------------------------------------------------- /contracts/lib/forge-std/src/StdInvariant.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | contract StdInvariant { 7 | struct FuzzSelector { 8 | address addr; 9 | bytes4[] selectors; 10 | } 11 | 12 | address[] private _excludedContracts; 13 | address[] private _excludedSenders; 14 | address[] private _targetedContracts; 15 | address[] private _targetedSenders; 16 | 17 | string[] private _excludedArtifacts; 18 | string[] private _targetedArtifacts; 19 | 20 | FuzzSelector[] private _targetedArtifactSelectors; 21 | FuzzSelector[] private _targetedSelectors; 22 | 23 | // Functions for users: 24 | // These are intended to be called in tests. 25 | 26 | function excludeContract(address newExcludedContract_) internal { 27 | _excludedContracts.push(newExcludedContract_); 28 | } 29 | 30 | function excludeSender(address newExcludedSender_) internal { 31 | _excludedSenders.push(newExcludedSender_); 32 | } 33 | 34 | function excludeArtifact(string memory newExcludedArtifact_) internal { 35 | _excludedArtifacts.push(newExcludedArtifact_); 36 | } 37 | 38 | function targetArtifact(string memory newTargetedArtifact_) internal { 39 | _targetedArtifacts.push(newTargetedArtifact_); 40 | } 41 | 42 | function targetArtifactSelector(FuzzSelector memory newTargetedArtifactSelector_) internal { 43 | _targetedArtifactSelectors.push(newTargetedArtifactSelector_); 44 | } 45 | 46 | function targetContract(address newTargetedContract_) internal { 47 | _targetedContracts.push(newTargetedContract_); 48 | } 49 | 50 | function targetSelector(FuzzSelector memory newTargetedSelector_) internal { 51 | _targetedSelectors.push(newTargetedSelector_); 52 | } 53 | 54 | function targetSender(address newTargetedSender_) internal { 55 | _targetedSenders.push(newTargetedSender_); 56 | } 57 | 58 | // Functions for forge: 59 | // These are called by forge to run invariant tests and don't need to be called in tests. 60 | 61 | function excludeArtifacts() public view returns (string[] memory excludedArtifacts_) { 62 | excludedArtifacts_ = _excludedArtifacts; 63 | } 64 | 65 | function excludeContracts() public view returns (address[] memory excludedContracts_) { 66 | excludedContracts_ = _excludedContracts; 67 | } 68 | 69 | function excludeSenders() public view returns (address[] memory excludedSenders_) { 70 | excludedSenders_ = _excludedSenders; 71 | } 72 | 73 | function targetArtifacts() public view returns (string[] memory targetedArtifacts_) { 74 | targetedArtifacts_ = _targetedArtifacts; 75 | } 76 | 77 | function targetArtifactSelectors() public view returns (FuzzSelector[] memory targetedArtifactSelectors_) { 78 | targetedArtifactSelectors_ = _targetedArtifactSelectors; 79 | } 80 | 81 | function targetContracts() public view returns (address[] memory targetedContracts_) { 82 | targetedContracts_ = _targetedContracts; 83 | } 84 | 85 | function targetSelectors() public view returns (FuzzSelector[] memory targetedSelectors_) { 86 | targetedSelectors_ = _targetedSelectors; 87 | } 88 | 89 | function targetSenders() public view returns (address[] memory targetedSenders_) { 90 | targetedSenders_ = _targetedSenders; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { useRouter } from "next/router"; 3 | import { ConnectButton } from "@rainbow-me/rainbowkit"; 4 | 5 | import { createStyles, Text, Header, Container, Group } from "@mantine/core"; 6 | 7 | const navigation = [ 8 | { label: "Home", link: "/" }, 9 | { label: "ZkBlind", link: "/zkblind" }, 10 | { label: "Whitelist", link: "/whitelist" }, 11 | ]; 12 | 13 | const HEADER_HEIGHT = 60; 14 | 15 | const useStyles = createStyles((theme) => ({ 16 | root: { 17 | position: "relative", 18 | zIndex: 1, 19 | }, 20 | 21 | dropdown: { 22 | position: "absolute", 23 | top: HEADER_HEIGHT, 24 | left: 0, 25 | right: 0, 26 | zIndex: 0, 27 | borderTopRightRadius: 0, 28 | borderTopLeftRadius: 0, 29 | borderTopWidth: 0, 30 | overflow: "hidden", 31 | 32 | [theme.fn.largerThan("md")]: { 33 | display: "none", 34 | }, 35 | }, 36 | 37 | header: { 38 | display: "flex", 39 | justifyContent: "space-between", 40 | alignItems: "center", 41 | height: "100%", 42 | }, 43 | 44 | links: { 45 | [theme.fn.smallerThan("md")]: { 46 | display: "none", 47 | }, 48 | }, 49 | 50 | burger: { 51 | [theme.fn.largerThan("md")]: { 52 | display: "none", 53 | }, 54 | }, 55 | 56 | link: { 57 | display: "block", 58 | lineHeight: 1, 59 | padding: "8px 12px", 60 | borderRadius: theme.radius.sm, 61 | textDecoration: "none", 62 | color: 63 | theme.colorScheme === "dark" 64 | ? theme.colors.dark[0] 65 | : theme.colors.gray[7], 66 | fontSize: theme.fontSizes.lg, 67 | fontWeight: 500, 68 | 69 | "&:hover": { 70 | backgroundColor: 71 | theme.colorScheme === "dark" 72 | ? theme.colors.dark[6] 73 | : theme.colors.gray[0], 74 | }, 75 | 76 | [theme.fn.smallerThan("sm")]: { 77 | borderRadius: 0, 78 | padding: theme.spacing.md, 79 | }, 80 | }, 81 | 82 | linkActive: { 83 | "&, &:hover": { 84 | backgroundColor: 85 | theme.colorScheme === "dark" 86 | ? theme.fn.rgba(theme.colors[theme.primaryColor][9], 0.25) 87 | : theme.colors[theme.primaryColor][0], 88 | color: 89 | theme.colors[theme.primaryColor][theme.colorScheme === "dark" ? 3 : 7], 90 | }, 91 | }, 92 | })); 93 | 94 | const Navbar = () => { 95 | const router = useRouter(); 96 | const { classes, cx } = useStyles(); 97 | const items = navigation.map((link) => ( 98 | 99 | 104 | {link.label} 105 | 106 | 107 | )); 108 | 109 | return ( 110 |
111 | 112 | 113 | {items} 114 | 115 | 116 | 122 | 123 | 124 |
125 | ); 126 | }; 127 | 128 | export default Navbar; 129 | -------------------------------------------------------------------------------- /src/pages/whitelist.tsx: -------------------------------------------------------------------------------- 1 | import { Text, Button, Container, Grid, Col, Textarea } from "@mantine/core"; 2 | import React, { useState, useEffect } from "react"; 3 | import { notifications } from "@mantine/notifications"; 4 | import { 5 | addWhitelistTransaction, 6 | checkWhitelisted, 7 | } from "@/lib/addWhitelistTransaction"; 8 | 9 | export default function Whitelist() { 10 | const [proofInput, setProofInput] = useState(""); 11 | const [pubInput, setPubInput] = useState(""); 12 | const [isWhitelisted, setIsWhitelisted] = useState(false); 13 | const [isPosted, setIsPosted] = useState(false); 14 | 15 | useEffect(() => { 16 | const fetchUserStatus = async () => { 17 | const whiteRes = await checkWhitelisted(); 18 | setIsWhitelisted(whiteRes); 19 | }; 20 | 21 | fetchUserStatus(); 22 | }, [isPosted]); 23 | 24 | const handleSubmit = async (event: React.FormEvent) => { 25 | event.preventDefault(); 26 | try { 27 | notifications.show({ 28 | message: "Add whitelist. Submitting transaction...", 29 | color: "green", 30 | }); 31 | 32 | const parsedPubInput = JSON.parse(pubInput); 33 | const parsedProofInput = JSON.parse(proofInput); 34 | const a = parsedProofInput.a; 35 | const b = parsedProofInput.b; 36 | const c = parsedProofInput.c; 37 | const input = parsedPubInput.input; 38 | const userId = parsedPubInput.userId; 39 | const emailSuffix = parsedPubInput.emailSuffix; 40 | 41 | // console.log("a...", a); 42 | // console.log("b...", b); 43 | // console.log("c...", c); 44 | // console.log("input...", input); 45 | // console.log("userId...", userId); 46 | // console.log("emailSuffix...", emailSuffix); 47 | // Write the transaction 48 | const txResult = await addWhitelistTransaction( 49 | a, 50 | b, 51 | c, 52 | input, 53 | userId, 54 | emailSuffix 55 | ); 56 | const txHash = txResult; 57 | setIsPosted(true); 58 | console.log("txHash...", txHash); 59 | 60 | notifications.show({ 61 | message: `Transaction succeeded! Tx Hash: ${txHash}`, 62 | color: "green", 63 | autoClose: false, 64 | }); 65 | } catch (err: any) { 66 | const statusCode = err?.response?.status; 67 | const errorMsg = err?.response?.data?.error; 68 | notifications.show({ 69 | message: `Error ${statusCode}: ${errorMsg}`, 70 | color: "red", 71 | }); 72 | } 73 | }; 74 | 75 | return ( 76 | 77 | {isWhitelisted ? ( 78 | Current user had whitelisted. 79 | ) : ( 80 |
81 | 82 | 83 |