├── 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 |
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 |
107 | )}
108 |
109 | );
110 | }
111 |
--------------------------------------------------------------------------------
/src/pages/example.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { Stack, Text, Grid, Input, Space, Button } from "@mantine/core";
3 | import axios, { AxiosRequestConfig } from "axios";
4 | import { notifications } from "@mantine/notifications";
5 | import { executeTransaction } from "@/lib/executeTransaction";
6 |
7 | export default function Home() {
8 | const [input0, setInput0] = useState("");
9 | const [input1, setInput1] = useState("");
10 |
11 | const handleGenerateProofSendTransaction = async (e: any) => {
12 | e.preventDefault();
13 |
14 | // We will send an HTTP request with our inputs to our next.js backend to
15 | // request a proof to be generated.
16 | const data = {
17 | input0,
18 | input1,
19 | };
20 | const config: AxiosRequestConfig = {
21 | headers: {
22 | "Content-Type": "application/json",
23 | },
24 | };
25 |
26 | // Send the HTTP request
27 | try {
28 | const res = await axios.post("/api/generate_proof", data, config);
29 | notifications.show({
30 | message: "Proof generated successfully! Submitting transaction...",
31 | color: "green",
32 | });
33 |
34 | // Split out the proof and public signals from the response data
35 | const { proof, publicSignals } = res.data;
36 |
37 | // Write the transaction
38 | const txResult = await executeTransaction(proof, publicSignals);
39 | const txHash = txResult.transactionHash;
40 |
41 | notifications.show({
42 | message: `Transaction succeeded! Tx Hash: ${txHash}`,
43 | color: "green",
44 | autoClose: false,
45 | });
46 | } catch (err: any) {
47 | const statusCode = err?.response?.status;
48 | const errorMsg = err?.response?.data?.error;
49 | notifications.show({
50 | message: `Error ${statusCode}: ${errorMsg}`,
51 | color: "red",
52 | });
53 | }
54 | };
55 |
56 | return (
57 | <>
58 |
59 |
60 |
61 |
62 |
63 | {
64 | "Input two numbers between 0 and 5, inclusive. The two numbers must \
65 | not be equal. We'll generate a ZK proof locally in the browser, and \
66 | only the proof will be sent to the blockchain so that no one \
67 | watching the blockchain will know the two numbers."
68 | }
69 |
70 |
71 |
92 |
93 |
94 |
95 |
96 | >
97 | );
98 | }
99 |
--------------------------------------------------------------------------------
/src/lib/abi/AddWhitelist.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "inputs": [
4 | {
5 | "internalType": "contract IVerifier",
6 | "name": "_verifier",
7 | "type": "address"
8 | }
9 | ],
10 | "stateMutability": "nonpayable",
11 | "type": "constructor"
12 | },
13 | {
14 | "anonymous": false,
15 | "inputs": [
16 | {
17 | "indexed": true,
18 | "internalType": "address",
19 | "name": "previousOwner",
20 | "type": "address"
21 | },
22 | {
23 | "indexed": true,
24 | "internalType": "address",
25 | "name": "newOwner",
26 | "type": "address"
27 | }
28 | ],
29 | "name": "OwnershipTransferred",
30 | "type": "event"
31 | },
32 | {
33 | "inputs": [
34 | {
35 | "internalType": "uint256",
36 | "name": "_userId",
37 | "type": "uint256"
38 | },
39 | {
40 | "internalType": "bytes32",
41 | "name": "_emailSuffix",
42 | "type": "bytes32"
43 | },
44 | {
45 | "internalType": "uint256[2]",
46 | "name": "a",
47 | "type": "uint256[2]"
48 | },
49 | {
50 | "internalType": "uint256[2][2]",
51 | "name": "b",
52 | "type": "uint256[2][2]"
53 | },
54 | {
55 | "internalType": "uint256[2]",
56 | "name": "c",
57 | "type": "uint256[2]"
58 | },
59 | {
60 | "internalType": "uint256[17]",
61 | "name": "input",
62 | "type": "uint256[17]"
63 | }
64 | ],
65 | "name": "addToWhitelist",
66 | "outputs": [],
67 | "stateMutability": "nonpayable",
68 | "type": "function"
69 | },
70 | {
71 | "inputs": [],
72 | "name": "owner",
73 | "outputs": [
74 | {
75 | "internalType": "address",
76 | "name": "",
77 | "type": "address"
78 | }
79 | ],
80 | "stateMutability": "view",
81 | "type": "function"
82 | },
83 | {
84 | "inputs": [],
85 | "name": "renounceOwnership",
86 | "outputs": [],
87 | "stateMutability": "nonpayable",
88 | "type": "function"
89 | },
90 | {
91 | "inputs": [
92 | {
93 | "internalType": "address",
94 | "name": "newOwner",
95 | "type": "address"
96 | }
97 | ],
98 | "name": "transferOwnership",
99 | "outputs": [],
100 | "stateMutability": "nonpayable",
101 | "type": "function"
102 | },
103 | {
104 | "inputs": [],
105 | "name": "verifier",
106 | "outputs": [
107 | {
108 | "internalType": "contract IVerifier",
109 | "name": "",
110 | "type": "address"
111 | }
112 | ],
113 | "stateMutability": "view",
114 | "type": "function"
115 | },
116 | {
117 | "inputs": [
118 | {
119 | "internalType": "address",
120 | "name": "_white",
121 | "type": "address"
122 | }
123 | ],
124 | "name": "verifyUser",
125 | "outputs": [
126 | {
127 | "internalType": "bool",
128 | "name": "",
129 | "type": "bool"
130 | }
131 | ],
132 | "stateMutability": "view",
133 | "type": "function"
134 | },
135 | {
136 | "inputs": [
137 | {
138 | "internalType": "address",
139 | "name": "",
140 | "type": "address"
141 | }
142 | ],
143 | "name": "whitelistedList",
144 | "outputs": [
145 | {
146 | "internalType": "uint256",
147 | "name": "userId",
148 | "type": "uint256"
149 | },
150 | {
151 | "internalType": "address",
152 | "name": "userAddr",
153 | "type": "address"
154 | },
155 | {
156 | "internalType": "bytes32",
157 | "name": "emailSuffix",
158 | "type": "bytes32"
159 | }
160 | ],
161 | "stateMutability": "view",
162 | "type": "function"
163 | }
164 | ]
--------------------------------------------------------------------------------
/circuits/ecsda-circom/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ```bash
4 | $ rm ./test/build
5 | $ ./build_eth_addr.sh
6 | Found Phase 1 ptau file
7 | ****COMPILING CIRCUIT****
8 |
9 | template instances: 199
10 |
11 | non-linear constraints: 247380
12 | linear constraints: 0
13 | public inputs: 4
14 | public outputs: 1
15 | private inputs: 0
16 | private outputs: 0
17 | wires: 246844
18 | labels: 2642008
19 |
20 | Written successfully: ./build/eth_addr/eth_addr.r1cs
21 | Written successfully: ./build/eth_addr/eth_addr.sym
22 | Written successfully: ./build/eth_addr/eth_addr_cpp/eth_addr.cpp and ./build/eth_addr/eth_addr_cpp/eth_addr.dat
23 | Written successfully: ./build/eth_addr/eth_addr_cpp/main.cpp, circom.hpp, calcwit.hpp, calcwit.cpp, fr.hpp, fr.cpp, fr.asm and Makefile
24 | Written successfully: ./build/eth_addr/eth_addr_js/eth_addr.wat
25 | Written successfully: ./build/eth_addr/eth_addr_js/eth_addr.wasm
26 | Everything went okay, circom safe
27 | DONE (58s)
28 | ****GENERATING WITNESS FOR SAMPLE INPUT****
29 | addr 978617770967819762654777740949918972567359649306
30 | DONE (13s)
31 | ****GENERATING ZKEY 0****
32 | [INFO] snarkJS: Reading r1cs
33 | [INFO] snarkJS: Reading tauG1
34 | [INFO] snarkJS: Reading tauG2
35 | [INFO] snarkJS: Reading alphatauG1
36 | [INFO] snarkJS: Reading betatauG1
37 | [INFO] snarkJS: Circuit hash:
38 | ac85e369 552d9c9d 453cfb43 998810c9
39 | d00295a2 e4ff91be 90d0fe7d 46b5618d
40 | cd29079c 685d5ee4 341e6827 2b6c58cf
41 | 166175f2 dac3cca1 3e22a34b 58249263
42 | DONE (224s)
43 | ****CONTRIBUTE TO THE PHASE 2 CEREMONY****
44 | Enter a random text. (Entropy): test
45 | [INFO] snarkJS: Circuit Hash:
46 | ac85e369 552d9c9d 453cfb43 998810c9
47 | d00295a2 e4ff91be 90d0fe7d 46b5618d
48 | cd29079c 685d5ee4 341e6827 2b6c58cf
49 | 166175f2 dac3cca1 3e22a34b 58249263
50 | [INFO] snarkJS: Contribution Hash:
51 | 2a17ecf6 95c59bce 5eb39005 542d8197
52 | 548192e6 96f77b66 00c421d8 5c49253d
53 | 8dc98f8a be1c02f5 57090518 705e36c5
54 | 8e9a7763 7337e8fb ebbab4ac 86a19585
55 | DONE (55s)
56 | ****GENERATING FINAL ZKEY****
57 | [INFO] snarkJS: Contribution Hash:
58 | ec51bd8b 359a9dab 3b9c664e 7ee18096
59 | 5c85b8cf b3d6ba54 fe73336d 400ccc54
60 | 04953c1a f5212d94 318b437a d60dc9aa
61 | 7e76219f f3877f92 05a0bb6f 05a113d0
62 | DONE (55s)
63 | ****VERIFYING FINAL ZKEY****
64 | [INFO] snarkJS: Reading r1cs
65 | [INFO] snarkJS: Reading tauG1
66 | [INFO] snarkJS: Reading tauG2
67 | [INFO] snarkJS: Reading alphatauG1
68 | [INFO] snarkJS: Reading betatauG1
69 | [INFO] snarkJS: Circuit hash:
70 | ac85e369 552d9c9d 453cfb43 998810c9
71 | d00295a2 e4ff91be 90d0fe7d 46b5618d
72 | cd29079c 685d5ee4 341e6827 2b6c58cf
73 | 166175f2 dac3cca1 3e22a34b 58249263
74 | [INFO] snarkJS: Circuit Hash:
75 | ac85e369 552d9c9d 453cfb43 998810c9
76 | d00295a2 e4ff91be 90d0fe7d 46b5618d
77 | cd29079c 685d5ee4 341e6827 2b6c58cf
78 | 166175f2 dac3cca1 3e22a34b 58249263
79 | [INFO] snarkJS: -------------------------
80 | [INFO] snarkJS: contribution #2 Final Beacon phase2:
81 | ec51bd8b 359a9dab 3b9c664e 7ee18096
82 | 5c85b8cf b3d6ba54 fe73336d 400ccc54
83 | 04953c1a f5212d94 318b437a d60dc9aa
84 | 7e76219f f3877f92 05a0bb6f 05a113d0
85 | [INFO] snarkJS: Beacon generator: 0102030405060708090a0b0c0d0e0f101112231415161718221a1b1c1d1e1f
86 | [INFO] snarkJS: Beacon iterations Exp: 10
87 | [INFO] snarkJS: -------------------------
88 | [INFO] snarkJS: contribution #1 1st Contributor Name:
89 | 2a17ecf6 95c59bce 5eb39005 542d8197
90 | 548192e6 96f77b66 00c421d8 5c49253d
91 | 8dc98f8a be1c02f5 57090518 705e36c5
92 | 8e9a7763 7337e8fb ebbab4ac 86a19585
93 | [INFO] snarkJS: -------------------------
94 | [INFO] snarkJS: ZKey Ok!
95 | DONE (244s)
96 | ****EXPORTING VKEY****
97 | [INFO] snarkJS: EXPORT VERIFICATION KEY STARTED
98 | [INFO] snarkJS: > Detected protocol: groth16
99 | [INFO] snarkJS: EXPORT VERIFICATION KEY FINISHED
100 | DONE (2s)
101 | ****GENERATING PROOF FOR SAMPLE INPUT****
102 | DONE (13s)
103 | ****VERIFYING PROOF FOR SAMPLE INPUT****
104 | [INFO] snarkJS: OK!
105 | DONE (1s)
106 | ```
107 |
108 |
--------------------------------------------------------------------------------
/scripts/generateInputs.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs';
2 | import * as path from 'path';
3 | import { execSync } from 'child_process';
4 | import {
5 | padEmailTo2032Bits,
6 | stringToBitArray,
7 | getCircuitInputWithAddrAndSig,
8 | bigint_to_array,
9 | bitArrayToBigInt,
10 | extractLeastSignificantBits,
11 | ethAddressToBigInt
12 | } from "../circuits/test_utils"
13 | import { sha256 } from 'js-sha256';
14 |
15 | type ParsedEmail = {
16 | emailAddress: string;
17 | addrAndSig: [string, string];
18 | };
19 |
20 | function parseEmailFile(path: string): ParsedEmail | null {
21 | try {
22 | const fileContent = fs.readFileSync(path, 'utf8');
23 | const lines = fileContent.split('\n');
24 |
25 | let emailAddress = '';
26 | let addrAndSigLine = '';
27 |
28 | for (const line of lines) {
29 | if (line.startsWith('From:')) {
30 | const match = line.match(/<(.+)>/);
31 | if (match && match[1]) {
32 | emailAddress = match[1];
33 | }
34 | } else if (line.startsWith('["0x')) {
35 | addrAndSigLine = line;
36 | break;
37 | }
38 | }
39 |
40 | if (emailAddress && addrAndSigLine) {
41 | const addrAndSig = addrAndSigLine
42 | .replace(/[\[\]"]/g, '')
43 | .split(',')
44 | .map((item) => item.trim()) as [string, string];
45 |
46 | return { emailAddress, addrAndSig };
47 | }
48 | } catch (error) {
49 | console.error(`Error reading or parsing the file: ${error}`);
50 | }
51 |
52 | return null;
53 | }
54 |
55 | // Get the root of the repo using a git command
56 | function getRepoRoot(): string {
57 | try {
58 | const root = execSync('git rev-parse --show-toplevel', { encoding: 'utf-8' });
59 | return root.trim();
60 | } catch (err) {
61 | console.error('Error: Not in a git repository');
62 | process.exit(1);
63 | }
64 | }
65 |
66 | // Save file to the specified location
67 | function saveFile(filePath: string, data: any): void {
68 | console.log(`Writing to default filePath ${filePath}`);
69 |
70 | const jsonString = JSON.stringify(data, bigintReplacer);
71 |
72 | fs.mkdirSync(path.dirname(filePath), { recursive: true });
73 | fs.writeFileSync(filePath, jsonString, { encoding: 'utf-8' });
74 | }
75 |
76 | function bigintReplacer(key: string, value: any): any {
77 | if (typeof value === 'bigint') {
78 | return value.toString(); // Convert BigInt to string
79 | } else {
80 | return value;
81 | }
82 | }
83 |
84 | const parsedEmailContent = parseEmailFile("/Users/alanwang/git/ZkBlindNextJs/scripts/zkBindRegistrationEmail.eml")
85 |
86 | if (!parsedEmailContent) {
87 | throw "can't parse email content"
88 | }
89 |
90 | console.log(parsedEmailContent)
91 |
92 | const email = parsedEmailContent.emailAddress;
93 |
94 | const paddedEmail = padEmailTo2032Bits(email);
95 |
96 | if (!paddedEmail) {
97 | throw ("The email address is not valid.");
98 | }
99 |
100 | const hash = sha256(paddedEmail);
101 |
102 | const userId = extractLeastSignificantBits(hash, 216);
103 |
104 | const emailAddressInputBits = stringToBitArray(paddedEmail);
105 |
106 | const emailSuffix = `@${email.split('@')[1]}`;
107 | const paddedEmailSuffix = padEmailTo2032Bits(emailSuffix);
108 | if (!paddedEmailSuffix) {
109 | throw ("The email suffix is not valid.");
110 | }
111 | const emailAddressSuffixInputBits = stringToBitArray(paddedEmailSuffix);
112 | const emailAddressSuffixBigInt = bitArrayToBigInt(emailAddressSuffixInputBits);
113 | const emailAddressSuffixInput = bigint_to_array(128, 16, emailAddressSuffixBigInt);
114 |
115 | const addrAndSig = JSON.stringify(parsedEmailContent.addrAndSig)
116 | const sigInput = getCircuitInputWithAddrAndSig(addrAndSig)
117 |
118 | const userEthAddr = parsedEmailContent.addrAndSig[0]
119 |
120 | const userEthAddrBigInt = ethAddressToBigInt(userEthAddr)
121 |
122 | const gen_inputs = {
123 | userId: userId,
124 | userEmailAddress: emailAddressInputBits,
125 | userEmailSuffix: emailAddressSuffixInput,
126 | userEthAddr: userEthAddrBigInt,
127 | userSigR: sigInput.r,
128 | userSigS: sigInput.s,
129 | userEthAddressSha256Hash: sigInput.msghash,
130 | userPubKey: sigInput.pubkey
131 | };
132 |
133 | const repoRoot = getRepoRoot();
134 |
135 | const buildPath = path.join(repoRoot, 'build');
136 | const outputPath = path.join(buildPath, 'input.json');
137 |
138 | saveFile(outputPath, gen_inputs);
139 |
140 |
--------------------------------------------------------------------------------
/circuits/eff_ecdsa/secp256k1/add.circom:
--------------------------------------------------------------------------------
1 | pragma circom 2.1.2;
2 |
3 | include "../../../node_modules/circomlib/circuits/comparators.circom";
4 | include "../../../node_modules/circomlib/circuits/gates.circom";
5 |
6 | /**
7 | * Secp256k1AddIncomplete
8 | * ======================
9 | *
10 | * Adds two points (xP, yP) and (xQ, yQ) on the secp256k1 curve. This function
11 | * only works for points where xP != xQ and are not at infinity. We can implement
12 | * the raw formulae for this operation as we are doing right field arithmetic
13 | * (we are doing secp256k1 base field arithmetic in the secq256k1 scalar field,
14 | * which are equal). Should work for any short Weierstrass curve (Pasta, P-256).
15 | */
16 | template Secp256k1AddIncomplete() {
17 | signal input xP;
18 | signal input yP;
19 | signal input xQ;
20 | signal input yQ;
21 | signal output outX;
22 | signal output outY;
23 |
24 | signal lambda;
25 | signal dx;
26 | signal dy;
27 |
28 | dx <== xP - xQ;
29 | dy <== yP - yQ;
30 |
31 | lambda <-- dy / dx;
32 | dx * lambda === dy;
33 |
34 | outX <== lambda * lambda - xP - xQ;
35 | outY <== lambda * (xP - outX) - yP;
36 | }
37 |
38 | /**
39 | * Secp256k1AddComplete
40 | * ====================
41 | *
42 | * Implements https://zcash.github.io/halo2/design/gadgets/ecc/addition.html#complete-addition
43 | * so we can add any pair of points. Assumes (0, 0) is not a valid point (which
44 | * is true for secp256k1) and is used as the point at infinity.
45 | */
46 | template Secp256k1AddComplete() {
47 | signal input xP;
48 | signal input yP;
49 | signal input xQ;
50 | signal input yQ;
51 |
52 | signal output outX;
53 | signal output outY;
54 |
55 | signal xPSquared <== xP * xP;
56 |
57 | component isXEqual = IsEqual();
58 | isXEqual.in[0] <== xP;
59 | isXEqual.in[1] <== xQ;
60 |
61 | component isXpZero = IsZero();
62 | isXpZero.in <== xP;
63 |
64 | component isXqZero = IsZero();
65 | isXqZero.in <== xQ;
66 |
67 | component isXEitherZero = IsZero();
68 | isXEitherZero.in <== (1 - isXpZero.out) * (1 - isXqZero.out);
69 |
70 | // dx = xQ - xP
71 | // dy = xP != xQ ? yQ - yP : 0
72 | // lambdaA = xP != xQ ? (yQ - yP) / (xQ - xP) : 0
73 | signal dx <== xQ - xP;
74 | signal dy <== (yQ - yP) * (1 - isXEqual.out);
75 | signal lambdaA <-- ((yQ - yP) / dx) * (1 - isXEqual.out);
76 | dx * lambdaA === dy;
77 |
78 | // lambdaB = (3 * xP^2) / (2 * yP)
79 | signal lambdaB <-- ((3 * xPSquared) / (2 * yP));
80 | lambdaB * 2 * yP === 3 * xPSquared;
81 |
82 | // lambda = xP != xQ ? lambdaA : lambdaB
83 | signal lambda <== (lambdaB * isXEqual.out) + lambdaA;
84 |
85 | // outAx = lambda^2 - xP - xQ
86 | // outAy = lambda * (xP - outAx) - yP
87 | signal outAx <== lambda * lambda - xP - xQ;
88 | signal outAy <== lambda * (xP - outAx) - yP;
89 |
90 | // (outBx, outBy) = xP != 0 and xQ != 0 ? (outAx, outAy) : (0, 0)
91 | signal outBx <== outAx * (1 - isXEitherZero.out);
92 | signal outBy <== outAy * (1 - isXEitherZero.out);
93 |
94 | //(outCx, outCy) = xP = 0 ? (xQ, yQ) : (0, 0)
95 | signal outCx <== isXpZero.out * xQ;
96 | signal outCy <== isXpZero.out * yQ;
97 |
98 | // (outDx, outDy) = xQ = 0 ? (xP, yP) : (0, 0)
99 | signal outDx <== isXqZero.out * xP;
100 | signal outDy <== isXqZero.out * yP;
101 |
102 | // zeroizeA = (xP = xQ and yP = -yQ) ? 1 : 0
103 | component zeroizeA = IsEqual();
104 | zeroizeA.in[0] <== isXEqual.out;
105 | zeroizeA.in[1] <== 1 - (yP + yQ);
106 |
107 | // zeroizeB = (xP = 0 and xQ = 0) ? 1 : 0
108 | component zeroizeB = AND();
109 | zeroizeB.a <== isXpZero.out;
110 | zeroizeB.b <== isXqZero.out;
111 |
112 | // zeroize = (xP = xQ and yP = -yQ) or (xP = 0 and xQ = 0) ? 1 : 0
113 | // for this case we want to output the point at infinity (0, 0)
114 | component zeroize = OR();
115 | zeroize.a <== zeroizeA.out;
116 | zeroize.b <== zeroizeB.out;
117 |
118 | // The below three conditionals are mutually exclusive when zeroize = 0,
119 | // so we can safely sum the outputs.
120 | // outBx != 0 iff xP != 0 and xQ != 0
121 | // outCx != 0 iff xP = 0
122 | // outDx != 0 iff xQ = 0
123 | outX <== (outBx + outCx + outDx) * (1 - zeroize.out);
124 | outY <== (outBy + outCy + outDy) * (1 - zeroize.out);
125 | }
--------------------------------------------------------------------------------
/circuits/ecsda-circom/vocdoni-keccak/keccak.circom:
--------------------------------------------------------------------------------
1 | pragma circom 2.0.2;
2 |
3 | include "./utils.circom";
4 | include "./permutations.circom";
5 |
6 | template Pad(nBits) {
7 | signal input in[nBits];
8 |
9 | var blockSize=136*8;
10 | signal output out[blockSize];
11 | signal out2[blockSize];
12 |
13 | var i;
14 |
15 | for (i=0; i> i) & 1;
21 | }
22 | for (i=nBits+8; i> i) & 1;
29 | }
30 | for (i=0; i<8; i++) {
31 | out[blockSize-8+i] <== aux.out[i];
32 | }
33 | for (i=0; i> i) & 1;
21 | }
22 | for (i=nBits+8; i> i) & 1;
29 | }
30 | for (i=0; i<8; i++) {
31 | out[blockSize-8+i] <== aux.out[i];
32 | }
33 | for (i=0; i
28 | Received: from mail-sor-f41.google.com (mail-sor-f41.google.com. [209.85.220.41])
29 | by mx.google.com with SMTPS id g5-20020aa7c845000000b00504487292a7sor2806680edt.34.2023.04.21.08.08.21
30 | for
31 | (Google Transport Security);
32 | Fri, 21 Apr 2023 08:08:21 -0700 (PDT)
33 | Received-SPF: pass (google.com: domain of alanywang96@gmail.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41;
34 | Authentication-Results: mx.google.com;
35 | dkim=pass header.i=@gmail.com header.s=20221208 header.b=lNklpo9r;
36 | spf=pass (google.com: domain of alanywang96@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=alanywang96@gmail.com;
37 | dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com
38 | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
39 | d=gmail.com; s=20221208; t=1682089701; x=1684681701;
40 | h=to:subject:message-id:date:from:mime-version:from:to:cc:subject
41 | :date:message-id:reply-to;
42 | bh=5jC4oUamT97/hfOJCaewNyrVSNoA/9Q1eQqzni8T/VU=;
43 | b=lNklpo9rR3kLuPYvPq0DN096JynwgsjdqmjPLJREQOic1GZPYHNXQ8rjhW8iqG+iGC
44 | IKG6RD8e7YHQYhiBfLOg87eIT6wkJMSy/CqXb0bxGLU9higz7++NSsxMZ0jFJLoD06hp
45 | tuC/TleaUlxc7fkeMMhJ7Nrd8hUjU4VFZ1MhfOlxpYPXprclGw8EyVF7jbr5xSX62qS8
46 | WTutZyD1Kc4jw1PmMHGgbMUD+38/a22YImbLYy+IBg46fxAof2vXeNqP4Sh2BdcAON2q
47 | ACLzyCNg0AVrEgVtrY2PnpqClakH2PWKEoDSc29O7SuqMINPDfDPQkobQJIYU1X05yKf
48 | HUeA==
49 | X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
50 | d=1e100.net; s=20221208; t=1682089701; x=1684681701;
51 | h=to:subject:message-id:date:from:mime-version:x-gm-message-state
52 | :from:to:cc:subject:date:message-id:reply-to;
53 | bh=5jC4oUamT97/hfOJCaewNyrVSNoA/9Q1eQqzni8T/VU=;
54 | b=bJcwTMH2yieiWgcAlmRXPoooqGsAcZyIse15JfHmYxfczchdB3/NlqHAMsM0+MFpP6
55 | wTF+vqw31bsIPIEE4C+3cU51SU1B5EQtSKepunqSmScbE6rWUM2HIUV2eOtTUzvRQ2LA
56 | wu8iPVTNQNMYKI/kaTG1W2rwbvfiwvnzNCYzcoAKKRO/y4dX6zR5m4vKLtRSv5vKXY7A
57 | ltiliqwqsSP7zd0ky1n4Yp+OVI/ejdEjQzVBsVgUt0+1ABHx2Wc5GrO0eRQnA1FrXPy/
58 | AqbeQdwbrzTBs9oWQb3qgKzO+25gACc7mwKR3WU/gC2iNaqI/PaTY157uTzkZCcvgdDn
59 | 9TtQ==
60 | X-Gm-Message-State: AAQBX9dXVuHfYwbiAvYWjSmyBDmCqeBFIfZ6GRuACD8784lXKjqg/YGk
61 | lpkcTWHhTMxUqhfaAnZreH/4t4qvcL1KlaSLQDovtMujdzU=
62 | X-Google-Smtp-Source: AKy350YRpecRA1vRcDOignHxVMFWHUYocuDP4GHdDPmq8f7VTDpuBfdNLlbSUUDFbuSMuksiuPKihwEM5fbo3YaqNSA=
63 | X-Received: by 2002:aa7:da81:0:b0:506:905b:816d with SMTP id
64 | q1-20020aa7da81000000b00506905b816dmr4948487eds.6.1682089700631; Fri, 21 Apr
65 | 2023 08:08:20 -0700 (PDT)
66 | MIME-Version: 1.0
67 | From: Alan Y Wang
68 | Date: Fri, 21 Apr 2023 22:08:08 +0700
69 | Message-ID:
70 | Subject: self email
71 | To: wy.dong96@gmail.com
72 | Content-Type: multipart/alternative; boundary="0000000000001626fa05f9da072d"
73 |
74 | --0000000000001626fa05f9da072d
75 | Content-Type: text/plain; charset="UTF-8"
76 |
77 | sk: a379a2760b835b9e64e96de47a4a02ecfb094d11965d532a83b71f5ec7686507,
78 |
79 | public key: 0x4730BF500EbD7D6523Ac751617e2C9e045EE7d8e
80 |
81 | sig: XXX
82 |
83 | --0000000000001626fa05f9da072d
84 | Content-Type: text/html; charset="UTF-8"
85 | Content-Transfer-Encoding: quoted-printable
86 |
87 | self email=C2=A0
88 |
89 | --0000000000001626fa05f9da072d--
90 |
--------------------------------------------------------------------------------
/scripts/zkBindRegistrationEmail.eml:
--------------------------------------------------------------------------------
1 | Delivered-To: alanywang96@gmail.com
2 | Received: by 2002:aa7:dccb:0:b0:506:b244:9cad with SMTP id w11csp352864edu;
3 | Thu, 27 Apr 2023 08:15:12 -0700 (PDT)
4 | X-Received: by 2002:a17:902:dacb:b0:1a3:dcc1:307d with SMTP id q11-20020a170902dacb00b001a3dcc1307dmr7606753plx.23.1682608511906;
5 | Thu, 27 Apr 2023 08:15:11 -0700 (PDT)
6 | ARC-Seal: i=1; a=rsa-sha256; t=1682608511; cv=none;
7 | d=google.com; s=arc-20160816;
8 | b=HXlm97pfkuZFh7elx406R+HTSMABv8Y4iCw6Q4N9wqcp8a/bNle3ldm4iWN2sZ71QE
9 | Cy9k/LvknNsJq3a3TlOZtVhFjPxUYZgZ/sxf0TGW1pmnama5xyFXE1e5tbL0dt1VqZc5
10 | D+qkc1dxdBR9gHNNeNxlAq/P0uFi2rfmg27jJ+iW6Swhpu/olELYxz4/fU4aLF1Qe1W7
11 | aGUvK222uQKCnPGZWfljk4tojMCudOe57PQ92GqrV3wq9lC/o2IML4X+YNW0GVzpzijQ
12 | X+NSE54ByjCh+lr1COp+5cDMujCXXQufenhF5nqD0fgBuCYzVbd4EcBQYHUj2QaTeZs6
13 | jlsw==
14 | ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
15 | h=to:subject:message-id:date:from:mime-version:dkim-signature;
16 | bh=IGZFsmS8GzAFPmlYyY8hdwopdxa2doCqi+xli4tLthg=;
17 | b=c1mSLO1nJllptyEuU22pAsGjqkld0MmfkfdJTdaEfwqxItMSjKxPuTS2d1qfZbL93g
18 | gJPJCrnoMbhOBWXqQobbIvzIcDbX+MR6JmRN98mAynvRJ7dvyLEDTPCDEJLktoDn5JLh
19 | VRxZeBt1bqkSXharm+d3ZpbkgwvxTARx2jmGeMyl/yN3GdYDabaCd4pAy9AgV+miNAOw
20 | 7OfvTChYwYsgG6jIW6eJGF+jO92puOjkfWTud23DzDyAQn/exCUOfPaZF5sKyj2QT8f1
21 | Wt8SYrrMS9GLpmlfIp16OUG5bk/W+qeYqmmWgVG/qoksD3BXCggbjjwu0oWiZ59M/jNH
22 | OWqg==
23 | ARC-Authentication-Results: i=1; mx.google.com;
24 | dkim=pass header.i=@gmail.com header.s=20221208 header.b=nZZRnlvr;
25 | spf=pass (google.com: domain of wy.dong96@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=wy.dong96@gmail.com;
26 | dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com
27 | Return-Path:
28 | Received: from mail-sor-f41.google.com (mail-sor-f41.google.com. [209.85.220.41])
29 | by mx.google.com with SMTPS id o8-20020a170902778800b001a6820df7e9sor9796609pll.171.2023.04.27.08.15.11
30 | for
31 | (Google Transport Security);
32 | Thu, 27 Apr 2023 08:15:11 -0700 (PDT)
33 | Received-SPF: pass (google.com: domain of wy.dong96@gmail.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41;
34 | Authentication-Results: mx.google.com;
35 | dkim=pass header.i=@gmail.com header.s=20221208 header.b=nZZRnlvr;
36 | spf=pass (google.com: domain of wy.dong96@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=wy.dong96@gmail.com;
37 | dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com
38 | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
39 | d=gmail.com; s=20221208; t=1682608511; x=1685200511;
40 | h=to:subject:message-id:date:from:mime-version:from:to:cc:subject
41 | :date:message-id:reply-to;
42 | bh=IGZFsmS8GzAFPmlYyY8hdwopdxa2doCqi+xli4tLthg=;
43 | b=nZZRnlvrUGxTLBKL3sLVsmlXCqkmkNEzXDceMy3acv0Uj0OabVeCDlinNz0WCvcXyh
44 | BwOM7aOqliWZVpwKMSWb4y/axaFWOEvo2YPlMjcFIQ5LpG0ivgjab8BdGih/EYjlvOr2
45 | oqN21dvey1jqtaD/qyUQRI9ZGJ9lPkyj501GVOk45BQkN4kH0oGZDfy4Fd6sc4r7eZhE
46 | dISbkZl8pY+xUVv6OUMRvzPtmZLhbcB922C2gLPVjv03xYrhMlx2dQDvuReUm+hCnu9R
47 | h2TiNlP9MPO5RChqFcFXrrVBVqRZSVW7yVUDVVh0YEyUV5VMYB1VGxG6umjGVojcaBrK
48 | HY9Q==
49 | X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
50 | d=1e100.net; s=20221208; t=1682608511; x=1685200511;
51 | h=to:subject:message-id:date:from:mime-version:x-gm-message-state
52 | :from:to:cc:subject:date:message-id:reply-to;
53 | bh=IGZFsmS8GzAFPmlYyY8hdwopdxa2doCqi+xli4tLthg=;
54 | b=F+k4s6AOq9GsrWAij4OGH8FdCGgecSrYaQYh3j6Ud1oAyNMPgVDoYhl8CJcVDWTZgh
55 | Z4MkoX4KQJKd2cuWo+NHKCvE5+yQespKIKJbh1QojZkJVFI16uPQ4zSi6DIs5fr0N9Wn
56 | ejmWXVtIEzmVJruKzcyf70QhcbJGACj/Kgg6y1wAiwDE9WZdStAGcvqIr/MjgvNxQI2b
57 | /VshBNYov5ujJWV+y6OC7D2xZEIfBGVQqfk2yy7jhWadiJGLb2LSgqSzoCgDlWeD6PRc
58 | cPRhqyzzVarG+gwITvXrJRlmAEoQw2Qad6VgwO5TXQMBNtB2xTQf1qOVmW8XNAytOtui
59 | YXtA==
60 | X-Gm-Message-State: AC+VfDzDl/jXjenmqqC7fvzyYxdryr/X8zv9vmWwZ/YznSAeiGwypyJ4
61 | D/7HW6WmuQRDuLrbB7eydzdIn4+PEmMoFEMm9OgMPv6X8wmWBTZO
62 | X-Google-Smtp-Source: ACHHUZ5P7VNuVOvYm5Qwgtd8m8JHWXHuWHMUZ63EBArp980XxzDt6T/s2LwDQqTwWPLd8ytnWQhavso2nEBB3yPkyM4=
63 | X-Received: by 2002:a17:90b:33ca:b0:247:4e73:cbdd with SMTP id
64 | lk10-20020a17090b33ca00b002474e73cbddmr7443708pjb.9.1682608510894; Thu, 27
65 | Apr 2023 08:15:10 -0700 (PDT)
66 | MIME-Version: 1.0
67 | From: Alan Wang
68 | Date: Thu, 27 Apr 2023 22:14:59 +0700
69 | Message-ID:
70 | Subject: Register for zkBlind
71 | To: "alanywang96@gmail.com"
72 | Content-Type: multipart/alternative; boundary="00000000000096851e05fa52d2d4"
73 |
74 | --00000000000096851e05fa52d2d4
75 | Content-Type: text/plain; charset="UTF-8"
76 |
77 | ["0xefcbe272b0febe3edadc034af7a3f53ed35aaa53","9ea2c44a6c411cddd0351db03d87b127b64fd420104e0e072d39b29f6318e6302e6f6390a4f17f3f77a83c7b12aa692c1e6fae925a399e0800afe9de71ebab7c"]
78 |
79 | --00000000000096851e05fa52d2d4
80 | Content-Type: text/html; charset="UTF-8"
81 |
82 | ["0xefcbe272b0febe3edadc034af7a3f53ed35aaa53","9ea2c44a6c411cddd0351db03d87b127b64fd420104e0e072d39b29f6318e6302e6f6390a4f17f3f77a83c7b12aa692c1e6fae925a399e0800afe9de71ebab7c"]
83 |
84 | --00000000000096851e05fa52d2d4--
85 |
--------------------------------------------------------------------------------
/src/pages/zkblind.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Image,
3 | Button,
4 | Paper,
5 | Grid,
6 | Center,
7 | Modal,
8 | Group,
9 | TextInput,
10 | } from "@mantine/core";
11 | import { useDisclosure } from "@mantine/hooks";
12 | import React, { useState, useEffect } from "react";
13 | import { useAccount, useSigner, useNetwork } from "wagmi";
14 | import { ethers } from "ethers";
15 | import { createStyles } from "@mantine/core";
16 | import Link from "next/link";
17 |
18 | import { getContractInfo } from "@/utils/contracts";
19 |
20 | const useStyles = createStyles((theme) => ({
21 | post: {
22 | display: "block",
23 | lineHeight: 1,
24 | padding: "8px 12px",
25 | borderRadius: theme.radius.sm,
26 | textDecoration: "none",
27 | color:
28 | theme.colorScheme === "dark"
29 | ? theme.colors.dark[0]
30 | : theme.colors.gray[7],
31 | fontSize: theme.fontSizes.lg,
32 | fontWeight: 500,
33 | backgroundColor:
34 | theme.colorScheme === "dark"
35 | ? theme.colors.dark[6]
36 | : theme.colors.blue[0],
37 |
38 | [theme.fn.smallerThan("sm")]: {
39 | borderRadius: 0,
40 | padding: theme.spacing.md,
41 | },
42 | },
43 | error: {
44 | display: "block",
45 | lineHeight: 1,
46 | padding: "8px 12px",
47 | borderRadius: theme.radius.sm,
48 | textDecoration: "none",
49 | color:
50 | theme.colorScheme === "dark"
51 | ? theme.colors.dark[0]
52 | : theme.colors.gray[7],
53 | fontSize: theme.fontSizes.lg,
54 | fontWeight: 600,
55 | backgroundColor:
56 | theme.colorScheme === "dark"
57 | ? theme.colors.dark[6]
58 | : theme.colors.blue[0],
59 |
60 | [theme.fn.smallerThan("sm")]: {
61 | borderRadius: 0,
62 | padding: theme.spacing.md,
63 | },
64 | },
65 | }));
66 |
67 | type ZkBlindMessage = {
68 | id: number;
69 | message: string;
70 | suffix: string;
71 | userId: number;
72 | };
73 |
74 | export default function Zkblind() {
75 | const { classes } = useStyles();
76 | const [opened, { open, close }] = useDisclosure(false);
77 | const { chain } = useNetwork();
78 | const { contractAddress, abi } = getContractInfo(chain?.id);
79 |
80 | const { address } = useAccount();
81 | const { data: signer } = useSigner();
82 |
83 | const [messages, setMessages] = useState([]);
84 |
85 | const [isAllowed, setAllowed] = useState(false);
86 |
87 | const [message, setMessage] = useState("");
88 | const [suffix, setSuffix] = useState("");
89 | const [userId, setUserId] = useState(0);
90 |
91 | const loadFromLocalStorage = () => {
92 | const savedObj = localStorage.getItem("myObject");
93 | if (savedObj) {
94 | setMessages(JSON.parse(savedObj));
95 | }
96 | };
97 |
98 | useEffect(() => {
99 | async function checkUser() {
100 | if (address && signer) {
101 | //console.log("contractAddress :", contractAddress, abi);
102 | const contract = new ethers.Contract(contractAddress, abi, signer);
103 | let isWhiteListed = await contract.verifyUser(address);
104 | let data = await contract.whitelistedList(address);
105 |
106 | loadFromLocalStorage();
107 |
108 | setSuffix(ethers.utils.parseBytes32String(data.emailSuffix));
109 | setUserId(ethers.BigNumber.from(data.userId).toNumber());
110 |
111 | setAllowed(isWhiteListed);
112 | }
113 | }
114 |
115 | checkUser();
116 | }, [signer, contractAddress, abi, address, isAllowed]);
117 |
118 | function postNewMessage() {
119 | let len = messages.length + 1;
120 | setMessages([
121 | ...messages,
122 | { id: len, message: message, userId: userId, suffix: suffix },
123 | ]);
124 | localStorage.setItem(
125 | "myObject",
126 | JSON.stringify([
127 | ...messages,
128 | { id: len, message: message, userId: userId, suffix: suffix },
129 | ])
130 | );
131 | }
132 |
133 | return (
134 | <>
135 | {isAllowed ? (
136 | <>
137 |
138 | setMessage(event.currentTarget.value)}
144 | />
145 |
148 |
149 |
150 |
151 |
157 |
158 |
159 |
162 |
163 |
164 |
165 |
166 | {messages?.map((message: ZkBlindMessage) => (
167 | <>
168 |
176 | {message.message}
177 |
178 | {message.suffix} id: {message.userId}
179 |
180 |
181 | >
182 | ))}
183 |
184 |
185 | >
186 | ) : (
187 |
188 |
189 |
190 | Please, Register
191 |
192 |
193 | )}
194 | >
195 | );
196 | }
197 |
--------------------------------------------------------------------------------
/contracts/lib/forge-std/test/StdStyle.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity >=0.7.0 <0.9.0;
3 |
4 | import "../src/Test.sol";
5 |
6 | contract StdStyleTest is Test {
7 | function testStyleColor() public view {
8 | console2.log(StdStyle.red("StdStyle.red String Test"));
9 | console2.log(StdStyle.red(uint256(10e18)));
10 | console2.log(StdStyle.red(int256(-10e18)));
11 | console2.log(StdStyle.red(true));
12 | console2.log(StdStyle.red(address(0)));
13 | console2.log(StdStyle.redBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
14 | console2.log(StdStyle.redBytes32("StdStyle.redBytes32"));
15 | console2.log(StdStyle.green("StdStyle.green String Test"));
16 | console2.log(StdStyle.green(uint256(10e18)));
17 | console2.log(StdStyle.green(int256(-10e18)));
18 | console2.log(StdStyle.green(true));
19 | console2.log(StdStyle.green(address(0)));
20 | console2.log(StdStyle.greenBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
21 | console2.log(StdStyle.greenBytes32("StdStyle.greenBytes32"));
22 | console2.log(StdStyle.yellow("StdStyle.yellow String Test"));
23 | console2.log(StdStyle.yellow(uint256(10e18)));
24 | console2.log(StdStyle.yellow(int256(-10e18)));
25 | console2.log(StdStyle.yellow(true));
26 | console2.log(StdStyle.yellow(address(0)));
27 | console2.log(StdStyle.yellowBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
28 | console2.log(StdStyle.yellowBytes32("StdStyle.yellowBytes32"));
29 | console2.log(StdStyle.blue("StdStyle.blue String Test"));
30 | console2.log(StdStyle.blue(uint256(10e18)));
31 | console2.log(StdStyle.blue(int256(-10e18)));
32 | console2.log(StdStyle.blue(true));
33 | console2.log(StdStyle.blue(address(0)));
34 | console2.log(StdStyle.blueBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
35 | console2.log(StdStyle.blueBytes32("StdStyle.blueBytes32"));
36 | console2.log(StdStyle.magenta("StdStyle.magenta String Test"));
37 | console2.log(StdStyle.magenta(uint256(10e18)));
38 | console2.log(StdStyle.magenta(int256(-10e18)));
39 | console2.log(StdStyle.magenta(true));
40 | console2.log(StdStyle.magenta(address(0)));
41 | console2.log(StdStyle.magentaBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
42 | console2.log(StdStyle.magentaBytes32("StdStyle.magentaBytes32"));
43 | console2.log(StdStyle.cyan("StdStyle.cyan String Test"));
44 | console2.log(StdStyle.cyan(uint256(10e18)));
45 | console2.log(StdStyle.cyan(int256(-10e18)));
46 | console2.log(StdStyle.cyan(true));
47 | console2.log(StdStyle.cyan(address(0)));
48 | console2.log(StdStyle.cyanBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
49 | console2.log(StdStyle.cyanBytes32("StdStyle.cyanBytes32"));
50 | }
51 |
52 | function testStyleFontWeight() public view {
53 | console2.log(StdStyle.bold("StdStyle.bold String Test"));
54 | console2.log(StdStyle.bold(uint256(10e18)));
55 | console2.log(StdStyle.bold(int256(-10e18)));
56 | console2.log(StdStyle.bold(address(0)));
57 | console2.log(StdStyle.bold(true));
58 | console2.log(StdStyle.boldBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
59 | console2.log(StdStyle.boldBytes32("StdStyle.boldBytes32"));
60 | console2.log(StdStyle.dim("StdStyle.dim String Test"));
61 | console2.log(StdStyle.dim(uint256(10e18)));
62 | console2.log(StdStyle.dim(int256(-10e18)));
63 | console2.log(StdStyle.dim(address(0)));
64 | console2.log(StdStyle.dim(true));
65 | console2.log(StdStyle.dimBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
66 | console2.log(StdStyle.dimBytes32("StdStyle.dimBytes32"));
67 | console2.log(StdStyle.italic("StdStyle.italic String Test"));
68 | console2.log(StdStyle.italic(uint256(10e18)));
69 | console2.log(StdStyle.italic(int256(-10e18)));
70 | console2.log(StdStyle.italic(address(0)));
71 | console2.log(StdStyle.italic(true));
72 | console2.log(StdStyle.italicBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
73 | console2.log(StdStyle.italicBytes32("StdStyle.italicBytes32"));
74 | console2.log(StdStyle.underline("StdStyle.underline String Test"));
75 | console2.log(StdStyle.underline(uint256(10e18)));
76 | console2.log(StdStyle.underline(int256(-10e18)));
77 | console2.log(StdStyle.underline(address(0)));
78 | console2.log(StdStyle.underline(true));
79 | console2.log(StdStyle.underlineBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
80 | console2.log(StdStyle.underlineBytes32("StdStyle.underlineBytes32"));
81 | console2.log(StdStyle.inverse("StdStyle.inverse String Test"));
82 | console2.log(StdStyle.inverse(uint256(10e18)));
83 | console2.log(StdStyle.inverse(int256(-10e18)));
84 | console2.log(StdStyle.inverse(address(0)));
85 | console2.log(StdStyle.inverse(true));
86 | console2.log(StdStyle.inverseBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
87 | console2.log(StdStyle.inverseBytes32("StdStyle.inverseBytes32"));
88 | }
89 |
90 | function testStyleCombined() public view {
91 | console2.log(StdStyle.red(StdStyle.bold("Red Bold String Test")));
92 | console2.log(StdStyle.green(StdStyle.dim(uint256(10e18))));
93 | console2.log(StdStyle.yellow(StdStyle.italic(int256(-10e18))));
94 | console2.log(StdStyle.blue(StdStyle.underline(address(0))));
95 | console2.log(StdStyle.magenta(StdStyle.inverse(true)));
96 | }
97 |
98 | function testStyleCustom() public view {
99 | console2.log(h1("Custom Style 1"));
100 | console2.log(h2("Custom Style 2"));
101 | }
102 |
103 | function h1(string memory a) private pure returns (string memory) {
104 | return StdStyle.cyan(StdStyle.inverse(StdStyle.bold(a)));
105 | }
106 |
107 | function h2(string memory a) private pure returns (string memory) {
108 | return StdStyle.magenta(StdStyle.bold(StdStyle.underline(a)));
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/circuits/ecsda-circom/secp256k1_func.circom:
--------------------------------------------------------------------------------
1 | pragma circom 2.0.2;
2 |
3 | // from https://github.com/ethereum/py_ecc/blob/master/py_ecc/secp256k1/secp256k1.py
4 | function get_gx(n, k) {
5 | assert(n == 86 && k == 3);
6 | var ret[100];
7 | if (n == 86 && k == 3) {
8 | ret[0] = 17117865558768631194064792;
9 | ret[1] = 12501176021340589225372855;
10 | ret[2] = 9198697782662356105779718;
11 | }
12 | return ret;
13 | }
14 |
15 | function get_gy(n, k) {
16 | assert(n == 86 && k == 3);
17 | var ret[100];
18 | if (n == 86 && k == 3) {
19 | ret[0] = 6441780312434748884571320;
20 | ret[1] = 57953919405111227542741658;
21 | ret[2] = 5457536640262350763842127;
22 | }
23 | return ret;
24 | }
25 |
26 | function get_secp256k1_prime(n, k) {
27 | assert((n == 86 && k == 3) || (n == 64 && k == 4));
28 | var ret[100];
29 | if (n == 86 && k == 3) {
30 | ret[0] = 77371252455336262886226991;
31 | ret[1] = 77371252455336267181195263;
32 | ret[2] = 19342813113834066795298815;
33 | }
34 | if (n == 64 && k == 4) {
35 | ret[0] = 18446744069414583343;
36 | ret[1] = 18446744073709551615;
37 | ret[2] = 18446744073709551615;
38 | ret[3] = 18446744073709551615;
39 | }
40 | return ret;
41 | }
42 |
43 | function get_secp256k1_order(n, k) {
44 | assert((n == 86 && k == 3) || (n == 64 && k == 4));
45 | var ret[100];
46 | if (n == 86 && k == 3) {
47 | ret[0] = 10428087374290690730508609;
48 | ret[1] = 77371252455330678278691517;
49 | ret[2] = 19342813113834066795298815;
50 | }
51 | if (n == 64 && k == 4) {
52 | ret[0] = 13822214165235122497;
53 | ret[1] = 13451932020343611451;
54 | ret[2] = 18446744073709551614;
55 | ret[3] = 18446744073709551615;
56 | }
57 | return ret;
58 | }
59 |
60 | // returns G * 2 ** 255
61 | // TODO check that this is correct...
62 | function get_dummy_point(n, k) {
63 | assert(n == 86 && k == 3 || n == 64 && k == 4);
64 | var ret[2][100]; // should be [2][k]
65 | if (k == 3) {
66 | ret[0][0] = 34318960048412842733519232;
67 | ret[0][1] = 3417427387252098100392906;
68 | ret[0][2] = 2056756886390887154635856;
69 | ret[1][0] = 35848273954982567597050105;
70 | ret[1][1] = 74802087901123421621824957;
71 | ret[1][2] = 4851915413831746153124691;
72 | } else {
73 | ret[0][0] = 10184385086782357888;
74 | ret[0][1] = 16068507144229249874;
75 | ret[0][2] = 17097072337414981695;
76 | ret[0][3] = 1961476217642676500;
77 | ret[1][0] = 15220267994978715897;
78 | ret[1][1] = 2812694141792528170;
79 | ret[1][2] = 9886878341545582154;
80 | ret[1][3] = 4627147115546938088;
81 | }
82 | return ret;
83 | }
84 |
85 | // a[0], a[1] = x1, y1
86 | // b[0], b[1] = x2, y2
87 | // lamb = (b[1] - a[1]) / (b[0] - a[0]) % p
88 | // out[0] = lamb ** 2 - a[0] - b[0] % p
89 | // out[1] = lamb * (a[0] - out[0]) - a[1] % p
90 | function secp256k1_addunequal_func(n, k, x1, y1, x2, y2){
91 | var a[2][100];
92 | var b[2][100];
93 |
94 | for(var i = 0; i < k; i++){
95 | a[0][i] = x1[i];
96 | a[1][i] = y1[i];
97 | b[0][i] = x2[i];
98 | b[1][i] = y2[i];
99 | }
100 |
101 | var out[2][100];
102 |
103 | var p[100] = get_secp256k1_prime(n, k);
104 |
105 | // b[1] - a[1]
106 | var sub1_out[100] = long_sub_mod_p(n, k, b[1], a[1], p);
107 |
108 | // b[0] - a[0]
109 | var sub0_out[100]= long_sub_mod_p(n, k, b[0], a[0], p);
110 |
111 | // lambda = (b[1] - a[1]) * inv(b[0] - a[0])
112 | var sub0inv[100] = mod_inv(n, k, sub0_out, p);
113 | var lambda[100] = prod_mod_p(n, k, sub1_out, sub0inv, p);
114 |
115 | // out[0] = lambda ** 2 - a[0] - b[0]
116 | var lambdasq_out[100] = prod_mod_p(n, k, lambda, lambda, p);
117 | var out0_pre_out[100] = long_sub_mod_p(n, k, lambdasq_out, a[0], p);
118 | var out0_out[100] = long_sub_mod_p(n, k, out0_pre_out, b[0], p);
119 | for (var i = 0; i < k; i++) {
120 | out[0][i] = out0_out[i];
121 | }
122 |
123 | // out[1] = lambda * (a[0] - out[0]) - a[1]
124 | var out1_0_out[100] = long_sub_mod_p(n, k, a[0], out[0], p);
125 | var out1_1_out[100] = prod_mod_p(n, k, lambda, out1_0_out, p);
126 | var out1_out[100] = long_sub_mod_p(n, k, out1_1_out, a[1], p);
127 | for (var i = 0; i < k; i++) {
128 | out[1][i] = out1_out[i];
129 | }
130 |
131 | return out;
132 | }
133 |
134 | // a[0], a[1] = x1, y1
135 | // lamb = (3 * a[0] ** 2) / (2 * a[1]) % p
136 | // out[0] = lamb ** 2 - (2 * a[0]) % p
137 | // out[1] = lamb * (a[0] - out[0]) - a[1] % p
138 | function secp256k1_double_func(n, k, x1, y1){
139 | var a[2][100];
140 | var b[2][100];
141 |
142 | for(var i = 0; i < k; i++){
143 | a[0][i] = x1[i];
144 | a[1][i] = y1[i];
145 | }
146 |
147 | var out[2][100];
148 |
149 | var p[100] = get_secp256k1_prime(n, k);
150 |
151 | // lamb_numer = 3 * a[0] ** 2
152 | var x1_sq[100] = prod_mod_p(n, k, a[0], a[0], p);
153 | var three[100];
154 | for (var i = 0; i < 100; i++) three[i] = i == 0 ? 3 : 0;
155 | var lamb_numer[100] = prod_mod_p(n, k, x1_sq, three, p);
156 |
157 | // lamb_denom = 2 * a[1]
158 | var two[100];
159 | for (var i = 0; i < 100; i++) two[i] = i == 0 ? 2 : 0;
160 | var lamb_denom[100] = prod_mod_p(n, k, a[1], two, p);
161 |
162 | // lambda = lamb_numer * inv(lamb_denom)
163 | var lamb_denom_inv[100] = mod_inv(n, k, lamb_denom, p);
164 | var lambda[100] = prod_mod_p(n, k, lamb_numer, lamb_denom_inv, p);
165 |
166 | // out[0] = lambda ** 2 - 2 * a[0]
167 | var lambdasq_out[100] = prod_mod_p(n, k, lambda, lambda, p);
168 | var out0_pre_out[100] = long_sub_mod_p(n, k, lambdasq_out, a[0], p);
169 | var out0_out[100] = long_sub_mod_p(n, k, out0_pre_out, a[0], p);
170 | for (var i = 0; i < k; i++) {
171 | out[0][i] = out0_out[i];
172 | }
173 |
174 | // out[1] = lambda * (a[0] - out[0]) - a[1]
175 | var out1_0_out[100] = long_sub_mod_p(n, k, a[0], out[0], p);
176 | var out1_1_out[100] = prod_mod_p(n, k, lambda, out1_0_out, p);
177 | var out1_out[100] = long_sub_mod_p(n, k, out1_1_out, a[1], p);
178 | for (var i = 0; i < k; i++) {
179 | out[1][i] = out1_out[i];
180 | }
181 |
182 | return out;
183 | }
184 |
--------------------------------------------------------------------------------
/circuits/test_utils.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import fs from "fs";
3 | import { execSync } from "child_process";
4 | import { hmac } from '@noble/hashes/hmac';
5 | import { sha256 } from '@noble/hashes/sha256';
6 | import { privateToAddress } from "@ethereumjs/util";
7 | import * as secp256k1 from '@noble/secp256k1';
8 | import { sha256 as jsSha256 } from 'js-sha256';
9 |
10 | export function generateEmailSuffix(email: string): string | null {
11 | const atIndex = email.indexOf('@');
12 | const dotIndex = email.indexOf('.');
13 |
14 | if (atIndex === -1 || dotIndex === -1) {
15 | console.error("Invalid email address: missing '@' or '.'.");
16 | return null;
17 | }
18 |
19 | const suffixWithSameLength = email.slice(0, atIndex + 1).replace(/./g, String.fromCharCode(0)) + email.slice(atIndex + 1);
20 | return suffixWithSameLength;
21 | }
22 |
23 | export function bigint_to_array(n: number, k: number, x: bigint) {
24 | let mod: bigint = 1n;
25 | for (var idx = 0; idx < n; idx++) {
26 | mod = mod * 2n;
27 | }
28 |
29 | let ret: bigint[] = [];
30 | var x_temp: bigint = x;
31 | for (var idx = 0; idx < k; idx++) {
32 | ret.push(x_temp % mod);
33 | x_temp = x_temp / mod;
34 | }
35 | return ret;
36 | }
37 |
38 | export function getCircuitInputWithAddrAndSig(input: string) {
39 | const [msg, sigHex]: [string, string] = JSON.parse(input)
40 |
41 | const sig = secp256k1.Signature.fromCompact(sigHex).addRecoveryBit(0)
42 |
43 | const msgHash = jsSha256(msg)
44 |
45 | const msgHashBigInt = BigInt("0x" + msgHash)
46 |
47 | const recoveredPublicKey = sig.recoverPublicKey(msgHash); // Public key recovery
48 |
49 | return getCircuitInput(
50 | sig.r,
51 | sig.s,
52 | msgHashBigInt,
53 | recoveredPublicKey.x,
54 | recoveredPublicKey.y
55 | )
56 | }
57 |
58 | export function getCircuitInput(
59 | r_bigint: bigint,
60 | s_bigint: bigint,
61 | msghash_bigint: bigint,
62 | pub0: bigint,
63 | pub1: bigint,
64 | ) {
65 | var r_array: bigint[] = bigint_to_array(64, 4, r_bigint);
66 | var s_array: bigint[] = bigint_to_array(64, 4, s_bigint);
67 | var msghash_array: bigint[] = bigint_to_array(64, 4, msghash_bigint);
68 | var pub0_array: bigint[] = bigint_to_array(64, 4, pub0);
69 | var pub1_array: bigint[] = bigint_to_array(64, 4, pub1);
70 |
71 | return {
72 | "r": r_array,
73 | "s": s_array,
74 | "msghash": msghash_array,
75 | "pubkey": [pub0_array, pub1_array]
76 | }
77 | }
78 |
79 | // example output of this function:
80 | // ["0xefcbe272b0febe3edadc034af7a3f53ed35aaa53","9ea2c44a6c411cddd0351db03d87b127b64fd420104e0e072d39b29f6318e6302e6f6390a4f17f3f77a83c7b12aa692c1e6fae925a399e0800afe9de71ebab7c"]
81 | export function getAddrAndSig(privateKey: string) {
82 | const privKeyBuffer = Buffer.from(privateKey, "hex");
83 | const addr = "0x" + privateToAddress(privKeyBuffer).toString("hex");
84 |
85 | const addrHash = jsSha256(addr)
86 | secp256k1.etc.hmacSha256Sync = (k, ...m) => hmac(sha256, k, secp256k1.etc.concatBytes(...m))
87 | const addrSig = secp256k1.sign(addrHash, privateKey).toCompactHex()
88 |
89 | const result = [
90 | addr,
91 | addrSig
92 | ]
93 |
94 | return JSON.stringify(result)
95 | }
96 |
97 | export function isASCII(str: string): boolean {
98 | return /^[\x00-\x7F]*$/.test(str);
99 | }
100 |
101 | export function padEmailTo2032Bits(email: string): string | null {
102 | if (!isASCII(email)) {
103 | console.error("The email address contains non-ASCII characters.");
104 | return null;
105 | }
106 |
107 | const emailBytes = new TextEncoder().encode(email);
108 | const bitSize = emailBytes.length * 8;
109 |
110 | if (bitSize > 2032) {
111 | console.error("The email address is larger than 2032 bits.");
112 | return null;
113 | }
114 |
115 | const paddedEmailBytes = new Uint8Array(2032 / 8);
116 | paddedEmailBytes.set(emailBytes);
117 |
118 | const paddedEmail = Array.from(paddedEmailBytes)
119 | .map(byte => String.fromCharCode(byte))
120 | .join('');
121 |
122 | return paddedEmail;
123 | }
124 |
125 | export function stringToBitArray(input: string): number[] {
126 | const bitArray = input
127 | .split('')
128 | .flatMap(char => {
129 | const byte = char.charCodeAt(0);
130 | return Array.from({ length: 8 }, (_, j) => (byte >> (7 - j)) & 1);
131 | });
132 | return bitArray;
133 | }
134 |
135 | export function bitArrayToBigInt(bitArray: number[]): bigint {
136 | return bitArray.reduce(
137 | (acc, bit, index) =>
138 | acc + (BigInt(bit) * (BigInt(2) ** BigInt(index))), BigInt(0)
139 | );
140 | }
141 |
142 | export function getEmailSuffixStartingIndexInBitArray(input: string): number {
143 | // Find the index of the '@' symbol in the input string
144 | const atIndex = input.indexOf('@');
145 |
146 | // Check if the '@' symbol is found in the input string
147 | if (atIndex === -1) {
148 | throw new Error("Invalid email address: missing '@' symbol");
149 | }
150 |
151 | // Convert the index of the '@' symbol in the input string to the corresponding index in the bit array
152 | const atIndexInBitArray = atIndex * 8;
153 |
154 | return atIndexInBitArray;
155 | }
156 |
157 | export function bitArray2buffer(a: number[]) {
158 | const len = Math.floor((a.length - 1) / 8) + 1;
159 | const b = Buffer.alloc(len);
160 |
161 | for (let i = 0; i < a.length; i++) {
162 | const p = Math.floor(i / 8);
163 | b[p] = b[p] | (Number(a[i]) << (7 - (i % 8)));
164 | }
165 | return b;
166 | }
167 |
168 | export function setupDirectories(pathToCircom: string) {
169 | // Get the repository root directory using the 'git' command
170 | const repoRoot = path.resolve(execSync('git rev-parse --show-toplevel', { encoding: 'utf-8' }).trim());
171 |
172 | // Create a 'tmp' directory in the root of the repository if it doesn't exist
173 | const tmpDir = path.join(repoRoot, 'tmp');
174 | if (!fs.existsSync(tmpDir)) {
175 | fs.mkdirSync(tmpDir);
176 | }
177 |
178 | // Extract the directory name from pathToCircom variable
179 | const circomDirName = path.parse(pathToCircom).name;
180 |
181 | // Create a directory with the extracted name inside the 'tmp' directory
182 | const multiplier2Dir = path.join(tmpDir, circomDirName);
183 | if (!fs.existsSync(multiplier2Dir)) {
184 | fs.mkdirSync(multiplier2Dir);
185 | }
186 |
187 | return multiplier2Dir;
188 | }
189 |
190 | export function extractLeastSignificantBits(hexString: string, bits: number): bigint {
191 | const fullNumber = BigInt(`0x${hexString}`);
192 | const mask = (BigInt(2) ** BigInt(bits)) - BigInt(1);
193 | return fullNumber & mask;
194 | }
195 |
196 | export function ethAddressToBigInt(ethAddress: string): bigint {
197 | const hexString = ethAddress.startsWith("0x") ? ethAddress.slice(2) : ethAddress;
198 | return BigInt(`0x${hexString}`);
199 | }
--------------------------------------------------------------------------------
/circuits/eff_ecdsa/secp256k1/mul.circom:
--------------------------------------------------------------------------------
1 | pragma circom 2.1.2;
2 |
3 | include "./add.circom";
4 | include "./double.circom";
5 | include "../../../node_modules/circomlib/circuits/bitify.circom";
6 | include "../../../node_modules/circomlib/circuits/comparators.circom";
7 | include "../../../node_modules/circomlib/circuits/gates.circom";
8 |
9 | //
10 |
11 | /**
12 | * Secp256k1Mul
13 | * ============
14 | *
15 | * Implements https://zcash.github.io/halo2/design/gadgets/ecc/var-base-scalar-mul.html
16 | * which allows us to use incomplete addition for the majority of the addition steps
17 | * and only use complete addition for the final 3 steps.
18 | */
19 | template Secp256k1Mul() {
20 | var bits = 256;
21 | signal input scalar;
22 | signal input xP;
23 | signal input yP;
24 | signal output outX;
25 | signal output outY;
26 |
27 | component kBits = K();
28 | kBits.s <== scalar;
29 |
30 | component acc0 = Secp256k1Double();
31 | acc0.xP <== xP;
32 | acc0.yP <== yP;
33 |
34 | component PIncomplete[bits-3];
35 | component accIncomplete[bits];
36 |
37 | for (var i = 0; i < bits-3; i++) {
38 | if (i == 0) {
39 | PIncomplete[i] = Secp256k1AddIncomplete(); // (Acc + P)
40 | PIncomplete[i].xP <== xP; // kBits[i] ? xP : -xP;
41 | PIncomplete[i].yP <== -yP;// kBits[i] ? xP : -xP;
42 | PIncomplete[i].xQ <== acc0.outX;
43 | PIncomplete[i].yQ <== acc0.outY;
44 |
45 |
46 | accIncomplete[i] = Secp256k1AddIncomplete(); // (Acc + P) + Acc
47 | accIncomplete[i].xP <== acc0.outX;
48 | accIncomplete[i].yP <== acc0.outY;
49 | accIncomplete[i].xQ <== PIncomplete[i].outX;
50 | accIncomplete[i].yQ <== PIncomplete[i].outY;
51 | } else {
52 | PIncomplete[i] = Secp256k1AddIncomplete(); // (Acc + P)
53 | PIncomplete[i].xP <== xP; // k_i ? xP : -xP;
54 | PIncomplete[i].yP <== (2 * kBits.out[bits-i] - 1) * yP;// k_i ? xP : -xP;
55 | PIncomplete[i].xQ <== accIncomplete[i-1].outX;
56 | PIncomplete[i].yQ <== accIncomplete[i-1].outY;
57 |
58 | accIncomplete[i] = Secp256k1AddIncomplete(); // (Acc + P) + Acc
59 | accIncomplete[i].xP <== accIncomplete[i-1].outX;
60 | accIncomplete[i].yP <== accIncomplete[i-1].outY;
61 | accIncomplete[i].xQ <== PIncomplete[i].outX;
62 | accIncomplete[i].yQ <== PIncomplete[i].outY;
63 | }
64 | }
65 |
66 | component PComplete[bits-3];
67 | component accComplete[3];
68 |
69 | for (var i = 0; i < 3; i++) {
70 | PComplete[i] = Secp256k1AddComplete(); // (Acc + P)
71 |
72 | PComplete[i].xP <== xP; // k_i ? xP : -xP;
73 | PComplete[i].yP <== (2 * kBits.out[3 - i] - 1) * yP;// k_i ? xP : -xP;
74 | if (i == 0) {
75 | PComplete[i].xQ <== accIncomplete[252].outX;
76 | PComplete[i].yQ <== accIncomplete[252].outY;
77 | } else {
78 | PComplete[i].xQ <== accComplete[i-1].outX;
79 | PComplete[i].yQ <== accComplete[i-1].outY;
80 | }
81 |
82 | accComplete[i] = Secp256k1AddComplete(); // (Acc + P) + Acc
83 | if (i == 0) {
84 | accComplete[i].xP <== accIncomplete[252].outX;
85 | accComplete[i].yP <== accIncomplete[252].outY;
86 | } else {
87 | accComplete[i].xP <== accComplete[i-1].outX;
88 | accComplete[i].yP <== accComplete[i-1].outY;
89 | }
90 |
91 | accComplete[i].xQ <== PComplete[i].outX;
92 | accComplete[i].yQ <== PComplete[i].outY;
93 | }
94 |
95 | component out = Secp256k1AddComplete();
96 | out.xP <== accComplete[2].outX;
97 | out.yP <== accComplete[2].outY;
98 | out.xQ <== (1 - kBits.out[0]) * xP;
99 | out.yQ <== (1 - kBits.out[0]) * -yP;
100 |
101 | outX <== out.outX;
102 | outY <== out.outY;
103 | }
104 |
105 | // Calculate k = (s + tQ) % q as follows:
106 | // Define notation: (s + tQ) / q = (quotient, remainder)
107 | // We can calculate the quotient and remainder as:
108 | // (s + tQ) < q ? = (0, s - tQ) : (1, (s - tQ) - q)
109 | // We use 128-bit registers to calculate the above since (s + tQ) can be larger than p.
110 | template K() {
111 | var bits = 256;
112 | signal input s;
113 | signal output out[bits];
114 |
115 | // Split elemnts into 128 bit registers
116 |
117 | var q = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; // The order of the scalar field
118 | var qlo = q & ((2 ** 128) - 1);
119 | var qhi = q >> 128;
120 | var tQ = 115792089237316195423570985008687907852405143892509244725752742275123193348738; // (q - 2^256) % q;
121 | var tQlo = tQ & (2 ** (128) - 1);
122 | var tQhi = tQ >> 128;
123 | signal slo <-- s & (2 ** (128) - 1);
124 | signal shi <-- s >> 128;
125 |
126 | // Get carry bit of (slo + tQlo)
127 |
128 | component inBits = Num2Bits(128 + 1);
129 | inBits.in <== slo + tQlo;
130 | signal carry <== inBits.out[128];
131 |
132 | // check a >= b
133 | // where
134 | // a = (s + tQ)
135 | // b = q
136 |
137 | // - alpha: ahi > bhi
138 | // - beta: ahi = bhi
139 | // - gamma: alo ≥ blo
140 | // if alpha or (beta and gamma) then a >= b
141 |
142 | signal ahi <== shi + tQhi + carry;
143 | signal bhi <== qhi;
144 | signal alo <== slo + tQlo - (carry * 2 ** 128);
145 | signal blo <== qlo;
146 |
147 | component alpha = GreaterThan(129);
148 | alpha.in[0] <== ahi;
149 | alpha.in[1] <== bhi;
150 |
151 | component beta = IsEqual();
152 | beta.in[0] <== ahi;
153 | beta.in[1] <== bhi;
154 |
155 | component gamma = GreaterEqThan(129);
156 | gamma.in[0] <== alo;
157 | gamma.in[1] <== blo;
158 |
159 | component betaANDgamma = AND();
160 | betaANDgamma.a <== beta.out;
161 | betaANDgamma.b <== gamma.out;
162 |
163 | component isQuotientOne = OR();
164 | isQuotientOne.a <== betaANDgamma.out;
165 | isQuotientOne.b <== alpha.out;
166 |
167 | // theta: (slo + tQlo) < qlo
168 | component theta = GreaterThan(129);
169 | theta.in[0] <== qlo;
170 | theta.in[1] <== slo + tQlo;
171 |
172 | // borrow: (slo + tQlo) < qlo and isQuotientOne ? 1 : 0
173 | component borrow = AND();
174 | borrow.a <== theta.out;
175 | borrow.b <== isQuotientOne.out;
176 |
177 | signal klo <== (slo + tQlo + borrow.out * (2 ** 128)) - isQuotientOne.out * qlo;
178 | signal khi <== (shi + tQhi - borrow.out * 1) - isQuotientOne.out * qhi;
179 |
180 | component kloBits = Num2Bits(256);
181 | kloBits.in <== klo;
182 |
183 | component khiBits = Num2Bits(256);
184 | khiBits.in <== khi;
185 |
186 | for (var i = 0; i < 128; i++) {
187 | out[i] <== kloBits.out[i];
188 | out[i + 128] <== khiBits.out[i];
189 | }
190 | }
--------------------------------------------------------------------------------
/contracts/lib/forge-std/src/StdJson.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity >=0.6.0 <0.9.0;
3 |
4 | pragma experimental ABIEncoderV2;
5 |
6 | import {VmSafe} from "./Vm.sol";
7 |
8 | // Helpers for parsing and writing JSON files
9 | // To parse:
10 | // ```
11 | // using stdJson for string;
12 | // string memory json = vm.readFile("some_peth");
13 | // json.parseUint("");
14 | // ```
15 | // To write:
16 | // ```
17 | // using stdJson for string;
18 | // string memory json = "deploymentArtifact";
19 | // Contract contract = new Contract();
20 | // json.serialize("contractAddress", address(contract));
21 | // json = json.serialize("deploymentTimes", uint(1));
22 | // // store the stringified JSON to the 'json' variable we have been using as a key
23 | // // as we won't need it any longer
24 | // string memory json2 = "finalArtifact";
25 | // string memory final = json2.serialize("depArtifact", json);
26 | // final.write("");
27 | // ```
28 |
29 | library stdJson {
30 | VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
31 |
32 | function parseRaw(string memory json, string memory key) internal pure returns (bytes memory) {
33 | return vm.parseJson(json, key);
34 | }
35 |
36 | function readUint(string memory json, string memory key) internal returns (uint256) {
37 | return vm.parseJsonUint(json, key);
38 | }
39 |
40 | function readUintArray(string memory json, string memory key) internal returns (uint256[] memory) {
41 | return vm.parseJsonUintArray(json, key);
42 | }
43 |
44 | function readInt(string memory json, string memory key) internal returns (int256) {
45 | return vm.parseJsonInt(json, key);
46 | }
47 |
48 | function readIntArray(string memory json, string memory key) internal returns (int256[] memory) {
49 | return vm.parseJsonIntArray(json, key);
50 | }
51 |
52 | function readBytes32(string memory json, string memory key) internal returns (bytes32) {
53 | return vm.parseJsonBytes32(json, key);
54 | }
55 |
56 | function readBytes32Array(string memory json, string memory key) internal returns (bytes32[] memory) {
57 | return vm.parseJsonBytes32Array(json, key);
58 | }
59 |
60 | function readString(string memory json, string memory key) internal returns (string memory) {
61 | return vm.parseJsonString(json, key);
62 | }
63 |
64 | function readStringArray(string memory json, string memory key) internal returns (string[] memory) {
65 | return vm.parseJsonStringArray(json, key);
66 | }
67 |
68 | function readAddress(string memory json, string memory key) internal returns (address) {
69 | return vm.parseJsonAddress(json, key);
70 | }
71 |
72 | function readAddressArray(string memory json, string memory key) internal returns (address[] memory) {
73 | return vm.parseJsonAddressArray(json, key);
74 | }
75 |
76 | function readBool(string memory json, string memory key) internal returns (bool) {
77 | return vm.parseJsonBool(json, key);
78 | }
79 |
80 | function readBoolArray(string memory json, string memory key) internal returns (bool[] memory) {
81 | return vm.parseJsonBoolArray(json, key);
82 | }
83 |
84 | function readBytes(string memory json, string memory key) internal returns (bytes memory) {
85 | return vm.parseJsonBytes(json, key);
86 | }
87 |
88 | function readBytesArray(string memory json, string memory key) internal returns (bytes[] memory) {
89 | return vm.parseJsonBytesArray(json, key);
90 | }
91 |
92 | function serialize(string memory jsonKey, string memory key, bool value) internal returns (string memory) {
93 | return vm.serializeBool(jsonKey, key, value);
94 | }
95 |
96 | function serialize(string memory jsonKey, string memory key, bool[] memory value)
97 | internal
98 | returns (string memory)
99 | {
100 | return vm.serializeBool(jsonKey, key, value);
101 | }
102 |
103 | function serialize(string memory jsonKey, string memory key, uint256 value) internal returns (string memory) {
104 | return vm.serializeUint(jsonKey, key, value);
105 | }
106 |
107 | function serialize(string memory jsonKey, string memory key, uint256[] memory value)
108 | internal
109 | returns (string memory)
110 | {
111 | return vm.serializeUint(jsonKey, key, value);
112 | }
113 |
114 | function serialize(string memory jsonKey, string memory key, int256 value) internal returns (string memory) {
115 | return vm.serializeInt(jsonKey, key, value);
116 | }
117 |
118 | function serialize(string memory jsonKey, string memory key, int256[] memory value)
119 | internal
120 | returns (string memory)
121 | {
122 | return vm.serializeInt(jsonKey, key, value);
123 | }
124 |
125 | function serialize(string memory jsonKey, string memory key, address value) internal returns (string memory) {
126 | return vm.serializeAddress(jsonKey, key, value);
127 | }
128 |
129 | function serialize(string memory jsonKey, string memory key, address[] memory value)
130 | internal
131 | returns (string memory)
132 | {
133 | return vm.serializeAddress(jsonKey, key, value);
134 | }
135 |
136 | function serialize(string memory jsonKey, string memory key, bytes32 value) internal returns (string memory) {
137 | return vm.serializeBytes32(jsonKey, key, value);
138 | }
139 |
140 | function serialize(string memory jsonKey, string memory key, bytes32[] memory value)
141 | internal
142 | returns (string memory)
143 | {
144 | return vm.serializeBytes32(jsonKey, key, value);
145 | }
146 |
147 | function serialize(string memory jsonKey, string memory key, bytes memory value) internal returns (string memory) {
148 | return vm.serializeBytes(jsonKey, key, value);
149 | }
150 |
151 | function serialize(string memory jsonKey, string memory key, bytes[] memory value)
152 | internal
153 | returns (string memory)
154 | {
155 | return vm.serializeBytes(jsonKey, key, value);
156 | }
157 |
158 | function serialize(string memory jsonKey, string memory key, string memory value)
159 | internal
160 | returns (string memory)
161 | {
162 | return vm.serializeString(jsonKey, key, value);
163 | }
164 |
165 | function serialize(string memory jsonKey, string memory key, string[] memory value)
166 | internal
167 | returns (string memory)
168 | {
169 | return vm.serializeString(jsonKey, key, value);
170 | }
171 |
172 | function write(string memory jsonKey, string memory path) internal {
173 | vm.writeJson(jsonKey, path);
174 | }
175 |
176 | function write(string memory jsonKey, string memory path, string memory valueKey) internal {
177 | vm.writeJson(jsonKey, path, valueKey);
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/contracts/lib/forge-std/test/StdChains.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity >=0.7.0 <0.9.0;
3 |
4 | import "../src/Test.sol";
5 |
6 | contract StdChainsTest is Test {
7 | function testChainRpcInitialization() public {
8 | // RPCs specified in `foundry.toml` should be updated.
9 | assertEq(getChain(1).rpcUrl, "https://mainnet.infura.io/v3/16a8be88795540b9b3903d8de0f7baa5");
10 | assertEq(getChain("optimism_goerli").rpcUrl, "https://goerli.optimism.io/");
11 | assertEq(getChain("arbitrum_one_goerli").rpcUrl, "https://goerli-rollup.arbitrum.io/rpc/");
12 |
13 | // Environment variables should be the next fallback
14 | assertEq(getChain("arbitrum_nova").rpcUrl, "https://nova.arbitrum.io/rpc");
15 | vm.setEnv("ARBITRUM_NOVA_RPC_URL", "myoverride");
16 | assertEq(getChain("arbitrum_nova").rpcUrl, "myoverride");
17 | vm.setEnv("ARBITRUM_NOVA_RPC_URL", "https://nova.arbitrum.io/rpc");
18 |
19 | // Cannot override RPCs defined in `foundry.toml`
20 | vm.setEnv("MAINNET_RPC_URL", "myoverride2");
21 | assertEq(getChain("mainnet").rpcUrl, "https://mainnet.infura.io/v3/16a8be88795540b9b3903d8de0f7baa5");
22 |
23 | // Other RPCs should remain unchanged.
24 | assertEq(getChain(31337).rpcUrl, "http://127.0.0.1:8545");
25 | assertEq(getChain("sepolia").rpcUrl, "https://sepolia.infura.io/v3/f4a0bdad42674adab5fc0ac077ffab2b");
26 | }
27 |
28 | function testRpc(string memory rpcAlias) internal {
29 | string memory rpcUrl = getChain(rpcAlias).rpcUrl;
30 | vm.createSelectFork(rpcUrl);
31 | }
32 |
33 | // Ensure we can connect to the default RPC URL for each chain.
34 | function testRpcs() public {
35 | testRpc("mainnet");
36 | testRpc("goerli");
37 | testRpc("sepolia");
38 | testRpc("optimism");
39 | testRpc("optimism_goerli");
40 | testRpc("arbitrum_one");
41 | testRpc("arbitrum_one_goerli");
42 | testRpc("arbitrum_nova");
43 | testRpc("polygon");
44 | testRpc("polygon_mumbai");
45 | testRpc("avalanche");
46 | testRpc("avalanche_fuji");
47 | testRpc("bnb_smart_chain");
48 | testRpc("bnb_smart_chain_testnet");
49 | testRpc("gnosis_chain");
50 | }
51 |
52 | function testChainNoDefault() public {
53 | vm.expectRevert("StdChains getChain(string): Chain with alias \"does_not_exist\" not found.");
54 | getChain("does_not_exist");
55 | }
56 |
57 | function testSetChainFirstFails() public {
58 | vm.expectRevert("StdChains setChain(string,ChainData): Chain ID 31337 already used by \"anvil\".");
59 | setChain("anvil2", ChainData("Anvil", 31337, "URL"));
60 | }
61 |
62 | function testChainBubbleUp() public {
63 | setChain("needs_undefined_env_var", ChainData("", 123456789, ""));
64 | vm.expectRevert(
65 | "Failed to resolve env var `UNDEFINED_RPC_URL_PLACEHOLDER` in `${UNDEFINED_RPC_URL_PLACEHOLDER}`: environment variable not found"
66 | );
67 | getChain("needs_undefined_env_var");
68 | }
69 |
70 | function testCannotSetChain_ChainIdExists() public {
71 | setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/"));
72 |
73 | vm.expectRevert('StdChains setChain(string,ChainData): Chain ID 123456789 already used by "custom_chain".');
74 |
75 | setChain("another_custom_chain", ChainData("", 123456789, ""));
76 | }
77 |
78 | function testSetChain() public {
79 | setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/"));
80 | Chain memory customChain = getChain("custom_chain");
81 | assertEq(customChain.name, "Custom Chain");
82 | assertEq(customChain.chainId, 123456789);
83 | assertEq(customChain.chainAlias, "custom_chain");
84 | assertEq(customChain.rpcUrl, "https://custom.chain/");
85 | Chain memory chainById = getChain(123456789);
86 | assertEq(chainById.name, customChain.name);
87 | assertEq(chainById.chainId, customChain.chainId);
88 | assertEq(chainById.chainAlias, customChain.chainAlias);
89 | assertEq(chainById.rpcUrl, customChain.rpcUrl);
90 | customChain.name = "Another Custom Chain";
91 | customChain.chainId = 987654321;
92 | setChain("another_custom_chain", customChain);
93 | Chain memory anotherCustomChain = getChain("another_custom_chain");
94 | assertEq(anotherCustomChain.name, "Another Custom Chain");
95 | assertEq(anotherCustomChain.chainId, 987654321);
96 | assertEq(anotherCustomChain.chainAlias, "another_custom_chain");
97 | assertEq(anotherCustomChain.rpcUrl, "https://custom.chain/");
98 | // Verify the first chain data was not overwritten
99 | chainById = getChain(123456789);
100 | assertEq(chainById.name, "Custom Chain");
101 | assertEq(chainById.chainId, 123456789);
102 | }
103 |
104 | function testSetNoEmptyAlias() public {
105 | vm.expectRevert("StdChains setChain(string,ChainData): Chain alias cannot be the empty string.");
106 | setChain("", ChainData("", 123456789, ""));
107 | }
108 |
109 | function testSetNoChainId0() public {
110 | vm.expectRevert("StdChains setChain(string,ChainData): Chain ID cannot be 0.");
111 | setChain("alias", ChainData("", 0, ""));
112 | }
113 |
114 | function testGetNoChainId0() public {
115 | vm.expectRevert("StdChains getChain(uint256): Chain ID cannot be 0.");
116 | getChain(0);
117 | }
118 |
119 | function testGetNoEmptyAlias() public {
120 | vm.expectRevert("StdChains getChain(string): Chain alias cannot be the empty string.");
121 | getChain("");
122 | }
123 |
124 | function testChainIdNotFound() public {
125 | vm.expectRevert("StdChains getChain(string): Chain with alias \"no_such_alias\" not found.");
126 | getChain("no_such_alias");
127 | }
128 |
129 | function testChainAliasNotFound() public {
130 | vm.expectRevert("StdChains getChain(uint256): Chain with ID 321 not found.");
131 | getChain(321);
132 | }
133 |
134 | function testSetChain_ExistingOne() public {
135 | setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/"));
136 | assertEq(getChain(123456789).chainId, 123456789);
137 |
138 | setChain("custom_chain", ChainData("Modified Chain", 999999999, "https://modified.chain/"));
139 | vm.expectRevert("StdChains getChain(uint256): Chain with ID 123456789 not found.");
140 | getChain(123456789);
141 |
142 | Chain memory modifiedChain = getChain(999999999);
143 | assertEq(modifiedChain.name, "Modified Chain");
144 | assertEq(modifiedChain.chainId, 999999999);
145 | assertEq(modifiedChain.rpcUrl, "https://modified.chain/");
146 | }
147 |
148 | function testDontUseDefaultRpcUrl() public {
149 | // Should error if default RPCs flag is set to false.
150 | setFallbackToDefaultRpcUrls(false);
151 | vm.expectRevert(
152 | "Failed to get environment variable `ANVIL_RPC_URL` as type `string`: environment variable not found"
153 | );
154 | getChain(31337);
155 | vm.expectRevert(
156 | "Failed to get environment variable `SEPOLIA_RPC_URL` as type `string`: environment variable not found"
157 | );
158 | getChain("sepolia");
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/contracts/lib/forge-std/src/interfaces/IERC1155.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity >=0.6.2;
3 |
4 | import "./IERC165.sol";
5 |
6 | /// @title ERC-1155 Multi Token Standard
7 | /// @dev See https://eips.ethereum.org/EIPS/eip-1155
8 | /// Note: The ERC-165 identifier for this interface is 0xd9b67a26.
9 | interface IERC1155 is IERC165 {
10 | /// @dev
11 | /// - Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
12 | /// - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
13 | /// - The `_from` argument MUST be the address of the holder whose balance is decreased.
14 | /// - The `_to` argument MUST be the address of the recipient whose balance is increased.
15 | /// - The `_id` argument MUST be the token type being transferred.
16 | /// - The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
17 | /// - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
18 | /// - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
19 | event TransferSingle(
20 | address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value
21 | );
22 |
23 | /// @dev
24 | /// - Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
25 | /// - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
26 | /// - The `_from` argument MUST be the address of the holder whose balance is decreased.
27 | /// - The `_to` argument MUST be the address of the recipient whose balance is increased.
28 | /// - The `_ids` argument MUST be the list of tokens being transferred.
29 | /// - The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by.
30 | /// - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
31 | /// - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
32 | event TransferBatch(
33 | address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values
34 | );
35 |
36 | /// @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absence of an event assumes disabled).
37 | event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
38 |
39 | /// @dev MUST emit when the URI is updated for a token ID. URIs are defined in RFC 3986.
40 | /// The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema".
41 | event URI(string _value, uint256 indexed _id);
42 |
43 | /// @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call).
44 | /// @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
45 | /// - MUST revert if `_to` is the zero address.
46 | /// - MUST revert if balance of holder for token `_id` is lower than the `_value` sent.
47 | /// - MUST revert on any other error.
48 | /// - MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard).
49 | /// - After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
50 | /// @param _from Source address
51 | /// @param _to Target address
52 | /// @param _id ID of the token type
53 | /// @param _value Transfer amount
54 | /// @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to`
55 | function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
56 |
57 | /// @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call).
58 | /// @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
59 | /// - MUST revert if `_to` is the zero address.
60 | /// - MUST revert if length of `_ids` is not the same as length of `_values`.
61 | /// - MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient.
62 | /// - MUST revert on any other error.
63 | /// - MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard).
64 | /// - Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc).
65 | /// - After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
66 | /// @param _from Source address
67 | /// @param _to Target address
68 | /// @param _ids IDs of each token type (order and length must match _values array)
69 | /// @param _values Transfer amounts per token type (order and length must match _ids array)
70 | /// @param _data Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `_to`
71 | function safeBatchTransferFrom(
72 | address _from,
73 | address _to,
74 | uint256[] calldata _ids,
75 | uint256[] calldata _values,
76 | bytes calldata _data
77 | ) external;
78 |
79 | /// @notice Get the balance of an account's tokens.
80 | /// @param _owner The address of the token holder
81 | /// @param _id ID of the token
82 | /// @return The _owner's balance of the token type requested
83 | function balanceOf(address _owner, uint256 _id) external view returns (uint256);
84 |
85 | /// @notice Get the balance of multiple account/token pairs
86 | /// @param _owners The addresses of the token holders
87 | /// @param _ids ID of the tokens
88 | /// @return The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair)
89 | function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids)
90 | external
91 | view
92 | returns (uint256[] memory);
93 |
94 | /// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
95 | /// @dev MUST emit the ApprovalForAll event on success.
96 | /// @param _operator Address to add to the set of authorized operators
97 | /// @param _approved True if the operator is approved, false to revoke approval
98 | function setApprovalForAll(address _operator, bool _approved) external;
99 |
100 | /// @notice Queries the approval status of an operator for a given owner.
101 | /// @param _owner The owner of the tokens
102 | /// @param _operator Address of authorized operator
103 | /// @return True if the operator is approved, false if not
104 | function isApprovedForAll(address _owner, address _operator) external view returns (bool);
105 | }
106 |
--------------------------------------------------------------------------------