mirror of
https://github.com/semaphore-protocol/semaphore.git
synced 2026-01-11 15:48:12 -05:00
Compare commits
209 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10fb031f0b | ||
|
|
4d7a976f06 | ||
|
|
fe07103ff2 | ||
|
|
aa230345fb | ||
|
|
d5c24c0f9c | ||
|
|
c1752dcb2e | ||
|
|
b00ff99e5e | ||
|
|
00e3c4a7f5 | ||
|
|
0176a67e3a | ||
|
|
4fa9f41483 | ||
|
|
64b9ef28e8 | ||
|
|
d7ce540f88 | ||
|
|
3411f55403 | ||
|
|
1bc492c39d | ||
|
|
6197a03734 | ||
|
|
6369fdfa71 | ||
|
|
48f779cd90 | ||
|
|
3ff6709a7b | ||
|
|
90bb4ccfa0 | ||
|
|
5f29690641 | ||
|
|
2665a440fa | ||
|
|
cf31913d27 | ||
|
|
103a676455 | ||
|
|
9aa5bc5840 | ||
|
|
894bc87952 | ||
|
|
cd4bd6ad91 | ||
|
|
dca7b67677 | ||
|
|
60d698f6ee | ||
|
|
f05e654f95 | ||
|
|
9a4fe6ba64 | ||
|
|
c8d472a286 | ||
|
|
c9711b9007 | ||
|
|
8afcac50c2 | ||
|
|
75c1c1373c | ||
|
|
2318628ca2 | ||
|
|
2603600716 | ||
|
|
3f0f1e006c | ||
|
|
2a4f2455d7 | ||
|
|
0ea221d9ea | ||
|
|
437674f3bc | ||
|
|
4202186c0b | ||
|
|
383e818833 | ||
|
|
74014a42b5 | ||
|
|
e875fa4aa3 | ||
|
|
186dae79d2 | ||
|
|
195d545a95 | ||
|
|
84245b9b1f | ||
|
|
461e91f53b | ||
|
|
2893feca3c | ||
|
|
4be2973761 | ||
|
|
01a30fd8af | ||
|
|
a70c1b001c | ||
|
|
30ed65d569 | ||
|
|
04a6649f2f | ||
|
|
a337021d8d | ||
|
|
4cd3863d03 | ||
|
|
c88639659d | ||
|
|
0ca17d078b | ||
|
|
f171506891 | ||
|
|
e085ecd0df | ||
|
|
b744ee1e7b | ||
|
|
8d51a6689d | ||
|
|
7bec0a4e42 | ||
|
|
71ed4d605d | ||
|
|
810a5358c0 | ||
|
|
636a9156b4 | ||
|
|
a679d047fa | ||
|
|
6d3e685284 | ||
|
|
3bfcf67b9d | ||
|
|
e16dbe663e | ||
|
|
0aca50ea34 | ||
|
|
54a6abdd2b | ||
|
|
de32db6d2d | ||
|
|
d43f9278e0 | ||
|
|
f3d8c4ee75 | ||
|
|
60fedc4e90 | ||
|
|
34d5743fa4 | ||
|
|
6810fcb5bb | ||
|
|
0bdf064614 | ||
|
|
7c4d2c2007 | ||
|
|
1bb57ca607 | ||
|
|
44860d70b9 | ||
|
|
d286a8f1e8 | ||
|
|
729ba41afd | ||
|
|
972fde32a7 | ||
|
|
c3d9e23e2a | ||
|
|
5fd7435c6d | ||
|
|
07888687a0 | ||
|
|
54c3a54597 | ||
|
|
81dba510c3 | ||
|
|
343cdabe9c | ||
|
|
fe6fbc1a0d | ||
|
|
f854492709 | ||
|
|
30af236547 | ||
|
|
d65b4aae47 | ||
|
|
245fb2cba8 | ||
|
|
f469ccc36a | ||
|
|
8ee1bd9c1b | ||
|
|
348a4327d7 | ||
|
|
faffde79f0 | ||
|
|
89a83c7b6b | ||
|
|
f36e8c1536 | ||
|
|
46ed508de8 | ||
|
|
821cdb7fd3 | ||
|
|
dd2df7ab06 | ||
|
|
71fdabe65c | ||
|
|
cce6c31461 | ||
|
|
f1aced7ee3 | ||
|
|
bd8b06bb7b | ||
|
|
9f692e57e8 | ||
|
|
32aabf81d2 | ||
|
|
a72a57ef63 | ||
|
|
4e5b4495a3 | ||
|
|
578d426d1a | ||
|
|
362b025fac | ||
|
|
84aa8d0eac | ||
|
|
6592a62f25 | ||
|
|
a59a2af50b | ||
|
|
1e68ed1fec | ||
|
|
2abd068cf8 | ||
|
|
8b746ff2bc | ||
|
|
23d9786e16 | ||
|
|
3b20402476 | ||
|
|
91bcc66c17 | ||
|
|
bcf9dad496 | ||
|
|
29195519fb | ||
|
|
17efdb38af | ||
|
|
8713e2b339 | ||
|
|
ef69356dca | ||
|
|
252b866daa | ||
|
|
5627f6eae7 | ||
|
|
45ef560324 | ||
|
|
ee80aa172e | ||
|
|
c0f106edb6 | ||
|
|
3aea38cc9b | ||
|
|
c6fe15cf13 | ||
|
|
6e948d63dc | ||
|
|
c2bd4df1d9 | ||
|
|
06e3063c3e | ||
|
|
cb52b3db20 | ||
|
|
88781ea466 | ||
|
|
2c47ad9f28 | ||
|
|
228ebd941a | ||
|
|
71f31f0910 | ||
|
|
7cf8eb11a1 | ||
|
|
6bbe45fb33 | ||
|
|
7b4e03c7fd | ||
|
|
8e0dd30ccc | ||
|
|
0661320c2a | ||
|
|
b419344297 | ||
|
|
fc636b732d | ||
|
|
5ac1cad96c | ||
|
|
33a6a3601e | ||
|
|
2dc8be161c | ||
|
|
a9dae718d3 | ||
|
|
f3ca0ce3d8 | ||
|
|
310b1a8dcd | ||
|
|
3c3d374c54 | ||
|
|
c6247ace3b | ||
|
|
1e9d56d3da | ||
|
|
a52a3bee1b | ||
|
|
e91bc74ab0 | ||
|
|
2594c4e1c2 | ||
|
|
a199b01a50 | ||
|
|
c97a0cbbb5 | ||
|
|
04fd0a3d91 | ||
|
|
3fb849f076 | ||
|
|
b3c83bac8b | ||
|
|
00c302aefc | ||
|
|
feee90e69d | ||
|
|
ca87f3601d | ||
|
|
a3abb810a9 | ||
|
|
5d19919474 | ||
|
|
5c66f16a5b | ||
|
|
4b2f9e4492 | ||
|
|
b593d1a0e9 | ||
|
|
d3b206ec3a | ||
|
|
adadacb218 | ||
|
|
b7411bfcb7 | ||
|
|
3feeb7f9fd | ||
|
|
05125c11d9 | ||
|
|
3188d3dbff | ||
|
|
04f57db7f0 | ||
|
|
066f38c471 | ||
|
|
37e8784471 | ||
|
|
889cf1890a | ||
|
|
1a4a7f36e0 | ||
|
|
3d6c24a51b | ||
|
|
781922436e | ||
|
|
277a790e36 | ||
|
|
d880925604 | ||
|
|
5466178f40 | ||
|
|
6cc4dc07bb | ||
|
|
98a35c0a37 | ||
|
|
bfeb24791d | ||
|
|
37e2614ac7 | ||
|
|
84bb9c89a4 | ||
|
|
a4aaf7f7ec | ||
|
|
b92a6e1c7a | ||
|
|
3afae28e06 | ||
|
|
63cddf3da2 | ||
|
|
499ec1cbeb | ||
|
|
fb1ffee89d | ||
|
|
f7bc7900e0 | ||
|
|
bc14210bc7 | ||
|
|
aabad94a81 | ||
|
|
eeac211c01 | ||
|
|
6e0236e9bc | ||
|
|
a9f8379545 |
@@ -1,5 +1,6 @@
|
||||
DEFAULT_NETWORK=hardhat
|
||||
TREE_DEPTH=20
|
||||
ALL_SNARK_ARTIFACTS=true
|
||||
REPORT_GAS=false
|
||||
BACKEND_PRIVATE_KEY=
|
||||
INFURA_API_KEY=
|
||||
|
||||
@@ -9,7 +9,9 @@ coverage
|
||||
coverage.json
|
||||
|
||||
# hardhat
|
||||
artifacts
|
||||
cache
|
||||
typechain-types
|
||||
|
||||
# types
|
||||
types
|
||||
@@ -30,3 +32,6 @@ docs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# packages
|
||||
cli-template-*
|
||||
|
||||
4
.github/workflows/production.yml
vendored
4
.github/workflows/production.yml
vendored
@@ -5,6 +5,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
TREE_DEPTH: 20
|
||||
ALL_SNARK_ARTIFACTS: false
|
||||
|
||||
jobs:
|
||||
style:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/pull-requests.yml
vendored
4
.github/workflows/pull-requests.yml
vendored
@@ -3,6 +3,10 @@ name: pull-requests
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TREE_DEPTH: 20
|
||||
ALL_SNARK_ARTIFACTS: false
|
||||
|
||||
jobs:
|
||||
style:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
43
.github/workflows/release.yml
vendored
Normal file
43
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: release
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- run: yarn version:release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -70,6 +70,10 @@ docs/*
|
||||
# Hardhat
|
||||
artifacts
|
||||
cache
|
||||
typechain-types
|
||||
packages/contracts/deployed-contracts/undefined.json
|
||||
packages/contracts/deployed-contracts/hardhat.json
|
||||
packages/contracts/deployed-contracts/localhost.json
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
@@ -9,10 +9,12 @@ coverage
|
||||
coverage.json
|
||||
|
||||
# hardhat
|
||||
artifacts
|
||||
cache
|
||||
|
||||
# types
|
||||
types
|
||||
typechain-types
|
||||
packages/contracts/deployed-contracts/undefined.json
|
||||
packages/contracts/deployed-contracts/hardhat.json
|
||||
packages/contracts/deployed-contracts/localhost.json
|
||||
|
||||
# circuits
|
||||
circuits
|
||||
|
||||
23
.yarn/patches/changelogithub-npm-0.12.7-72f348805d.patch
Normal file
23
.yarn/patches/changelogithub-npm-0.12.7-72f348805d.patch
Normal file
@@ -0,0 +1,23 @@
|
||||
diff --git a/dist/shared/changelogithub.821fab93.mjs b/dist/shared/changelogithub.821fab93.mjs
|
||||
index 5fc2100867613c20f7827eac8715a5fc28bdc39e..97bd8dff878b81c63d2220e496904f6f3933589a 100644
|
||||
--- a/dist/shared/changelogithub.821fab93.mjs
|
||||
+++ b/dist/shared/changelogithub.821fab93.mjs
|
||||
@@ -181,7 +181,7 @@ function formatLine(commit, options) {
|
||||
function formatTitle(name, options) {
|
||||
if (!options.emoji)
|
||||
name = name.replace(emojisRE, "");
|
||||
- return `### ${name.trim()}`;
|
||||
+ return `## ${name.trim()}`;
|
||||
}
|
||||
function formatSection(commits, sectionName, options) {
|
||||
if (!commits.length)
|
||||
@@ -198,7 +198,8 @@ function formatSection(commits, sectionName, options) {
|
||||
Object.keys(scopes).sort().forEach((scope) => {
|
||||
let padding = "";
|
||||
let prefix = "";
|
||||
- const scopeText = `**${options.scopeMap[scope] || scope}**`;
|
||||
+ const url = `https://github.com/${options.github}/tree/main/packages/${scope}`
|
||||
+ const scopeText = `[**@${options.github.split("/")[0]}/${options.scopeMap[scope] || scope}**](${url})`
|
||||
if (scope && useScopeGroup) {
|
||||
lines.push(`- ${scopeText}:`);
|
||||
padding = " ";
|
||||
1
.yarn/plugins/@yarnpkg/plugin-version.cjs.REMOVED.git-id
vendored
Normal file
1
.yarn/plugins/@yarnpkg/plugin-version.cjs.REMOVED.git-id
vendored
Normal file
@@ -0,0 +1 @@
|
||||
87de4f440a77841135f97a187e09140c6d4e6ae2
|
||||
@@ -1,8 +1,11 @@
|
||||
nodeLinker: node-modules
|
||||
checksumBehavior: update
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||
spec: "@yarnpkg/plugin-workspace-tools"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
|
||||
spec: "@yarnpkg/plugin-version"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.1.cjs
|
||||
|
||||
45
README.md
45
README.md
@@ -17,7 +17,7 @@
|
||||
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/actions?query=workflow%3Aproduction">
|
||||
<img alt="GitHub Workflow test" src="https://img.shields.io/github/workflow/status/semaphore-protocol/semaphore/production?label=test&style=flat-square&logo=github">
|
||||
<img alt="GitHub Workflow test" src="https://img.shields.io/github/actions/workflow/status/semaphore-protocol/semaphore/production.yml?branch=main&label=test&style=flat-square&logo=github">
|
||||
</a>
|
||||
<a href="https://coveralls.io/github/semaphore-protocol/semaphore">
|
||||
<img alt="Coveralls" src="https://img.shields.io/coveralls/github/semaphore-protocol/semaphore?style=flat-square&logo=coveralls">
|
||||
@@ -32,6 +32,10 @@
|
||||
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier">
|
||||
</a>
|
||||
<img alt="Repository top language" src="https://img.shields.io/github/languages/top/semaphore-protocol/semaphore?style=flat-square">
|
||||
<a href="https://www.gitpoap.io/gh/semaphore-protocol/semaphore" target="_blank">
|
||||
<img src="https://public-api.gitpoap.io/v1/repo/semaphore-protocol/semaphore/badge">
|
||||
</a>
|
||||
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
@@ -48,7 +52,7 @@
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://discord.gg/6mSdGHnstH">
|
||||
<a href="https://semaphore.appliedzkp.org/discord">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
@@ -57,9 +61,7 @@
|
||||
| Semaphore is a protocol, designed to be a simple and generic privacy layer for Ethereum DApps. Using zero knowledge, Ethereum users can prove their membership of a group and send signals such as votes or endorsements without revealing their original identity. |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
The core of the Semaphore protocol is in the [circuit logic](/packages/circuits/scheme.png). However Semaphore also provides [Solidity contracts](/packages/contracts) (NPM: `@semaphore-protocol/contracts`) and JavaScript libraries to make the steps for offchain proof creation and onchain verification easier. To learn more about Semaphore visit [semaphore.appliedzkp.org](https://semaphore.appliedzkp.org).
|
||||
|
||||
You can find Semaphore V1 on [`version/1.0.0`](https://github.com/semaphore-protocol/semaphore/tree/version/1.0.0).
|
||||
The core of the Semaphore protocol is in the [circuit logic](/packages/circuits/scheme.png). However Semaphore also provides [Solidity contracts](/packages/contracts) and JavaScript libraries to make the steps for offchain proof creation and onchain verification easier. To learn more about Semaphore visit [semaphore.appliedzkp.org](https://semaphore.appliedzkp.org).
|
||||
|
||||
---
|
||||
|
||||
@@ -157,23 +159,23 @@ You can find Semaphore V1 on [`version/1.0.0`](https://github.com/semaphore-prot
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/subgraph">
|
||||
@semaphore-protocol/subgraph
|
||||
<a href="/packages/data">
|
||||
@semaphore-protocol/data
|
||||
</a>
|
||||
<a href="https://semaphore-protocol.github.io/semaphore/subgraph">
|
||||
<a href="https://semaphore-protocol.github.io/semaphore/data">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/subgraph">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/subgraph.svg?style=flat-square" alt="NPM version" />
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/data">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/data.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/subgraph">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/subgraph.svg?style=flat-square" alt="Downloads" />
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/data">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/data.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -196,6 +198,25 @@ You can find Semaphore V1 on [`version/1.0.0`](https://github.com/semaphore-prot
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/cli">
|
||||
@semaphore-protocol/cli
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/cli">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/cli.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/cli">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/cli.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
|
||||
</table>
|
||||
|
||||
17
package.json
17
package.json
@@ -7,9 +7,9 @@
|
||||
"bugs": "https://github.com/semaphore-protocol/semaphore/issues",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build:libraries": "yarn workspaces foreach run build",
|
||||
"build:libraries": "yarn workspaces foreach -t run build",
|
||||
"compile:contracts": "yarn workspace contracts compile",
|
||||
"download:snark-artifacts": "ts-node scripts/download-snark-artifacts.ts",
|
||||
"download:snark-artifacts": "rimraf snark-artifacts && ts-node scripts/download-snark-artifacts.ts",
|
||||
"test": "yarn test:libraries && yarn test:contracts",
|
||||
"test:libraries": "jest --coverage",
|
||||
"test:contracts": "yarn workspace contracts test:coverage",
|
||||
@@ -17,6 +17,9 @@
|
||||
"prettier": "prettier -c .",
|
||||
"prettier:write": "prettier -w .",
|
||||
"docs": "yarn workspaces foreach run docs",
|
||||
"version:bump": "yarn workspaces foreach --no-private version -d ${0} && yarn version apply --all && git commit -am \"chore: v${0}\" && git tag v${0}",
|
||||
"version:publish": "yarn build:libraries && yarn workspaces foreach --no-private npm publish --tolerate-republish",
|
||||
"version:release": "changelogithub",
|
||||
"commit": "cz",
|
||||
"precommit": "lint-staged",
|
||||
"postinstall": "yarn download:snark-artifacts"
|
||||
@@ -35,7 +38,8 @@
|
||||
"monorepo"
|
||||
],
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
"packages/*",
|
||||
"packages/contracts/contracts"
|
||||
],
|
||||
"packageManager": "yarn@3.2.1",
|
||||
"devDependencies": {
|
||||
@@ -53,7 +57,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
||||
"@typescript-eslint/parser": "^5.9.1",
|
||||
"babel-jest": "^27.4.6",
|
||||
"circomlibjs": "^0.0.8",
|
||||
"changelogithub": "0.12.7",
|
||||
"commitizen": "^4.2.4",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"dotenv": "^16.0.2",
|
||||
@@ -71,11 +75,14 @@
|
||||
"rollup": "^2.64.0",
|
||||
"ts-node": "^10.4.0",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.5.4"
|
||||
"typescript": "^4.7.0"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
"changelogithub@0.12.7": "patch:changelogithub@npm:0.12.7#.yarn/patches/changelogithub-npm-0.12.7-72f348805d.patch"
|
||||
}
|
||||
}
|
||||
|
||||
37
packages/circuits/README.md
Normal file
37
packages/circuits/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
Semaphore circuits
|
||||
</h1>
|
||||
<p align="center">Semaphore circuits to create and verify zero-knowledge proofs.</p>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/semaphore-protocol">
|
||||
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/LICENSE">
|
||||
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
<h4>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CONTRIBUTING.md">
|
||||
👥 Contributing
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
|
||||
🤝 Code of conduct
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://semaphore.appliedzkp.org/discord">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
To learn more about circuits visit [semaphore.appliedzkp.org](https://semaphore.appliedzkp.org/docs/technical-reference/circuits).
|
||||
@@ -43,7 +43,7 @@ template CalculateNullifierHash() {
|
||||
out <== poseidon.out;
|
||||
}
|
||||
|
||||
// nLevels must be < 32.
|
||||
// The current Semaphore smart contracts require nLevels <= 32 and nLevels >= 16.
|
||||
template Semaphore(nLevels) {
|
||||
signal input identityNullifier;
|
||||
signal input identityTrapdoor;
|
||||
|
||||
4
packages/cli-template-hardhat/.env.example
Normal file
4
packages/cli-template-hardhat/.env.example
Normal file
@@ -0,0 +1,4 @@
|
||||
ETHEREUM_URL=
|
||||
ETHEREUM_PRIVATE_KEY=
|
||||
REPORT_GAS=false
|
||||
COINMARKETCAP_API_KEY=
|
||||
10
packages/cli-template-hardhat/.gitignore
vendored
Normal file
10
packages/cli-template-hardhat/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
node_modules
|
||||
.env
|
||||
coverage
|
||||
coverage.json
|
||||
typechain
|
||||
typechain-types
|
||||
|
||||
# Hardhat files
|
||||
cache
|
||||
artifacts
|
||||
54
packages/cli-template-hardhat/README.md
Normal file
54
packages/cli-template-hardhat/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Semaphore Hardhat template
|
||||
|
||||
This project demonstrates a basic Semaphore use case. It comes with a sample contract, a test for that contract and a sample task that deploys that contract.
|
||||
|
||||
## Usage
|
||||
|
||||
### Compile
|
||||
|
||||
```bash
|
||||
yarn compile
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
You can also generate a test coverage report:
|
||||
|
||||
```bash
|
||||
yarn test:coverage
|
||||
```
|
||||
|
||||
Or a test gas report:
|
||||
|
||||
```bash
|
||||
yarn test:report-gas
|
||||
```
|
||||
|
||||
### Deploy
|
||||
|
||||
1. Copy the `.env.example` file as `.env`.
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Add your environment variables.
|
||||
|
||||
> **Note**
|
||||
> You should at least set a valid Ethereum URL (e.g. Infura) and a private key with some ethers.
|
||||
|
||||
3. And deploy your contract.
|
||||
|
||||
```bash
|
||||
yarn deploy --semaphore <semaphore-address> --group <group-id> --network goerli
|
||||
```
|
||||
|
||||
> **Note**
|
||||
> Check the Semaphore contract addresses [here](https://semaphore.appliedzkp.org/docs/deployed-contracts#semaphore).
|
||||
|
||||
> **Warning**
|
||||
> The group id is a number!
|
||||
42
packages/cli-template-hardhat/contracts/Greeter.sol
Normal file
42
packages/cli-template-hardhat/contracts/Greeter.sol
Normal file
@@ -0,0 +1,42 @@
|
||||
//SPDX-License-Identifier: Unlicense
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
|
||||
|
||||
/// @title Greeter contract.
|
||||
/// @dev The following code is just a example to show how Semaphore can be used.
|
||||
contract Greeter {
|
||||
event NewGreeting(bytes32 greeting);
|
||||
event NewUser(uint256 identityCommitment, bytes32 username);
|
||||
|
||||
ISemaphore public semaphore;
|
||||
|
||||
uint256 groupId;
|
||||
mapping(uint256 => bytes32) users;
|
||||
|
||||
constructor(address semaphoreAddress, uint256 _groupId) {
|
||||
semaphore = ISemaphore(semaphoreAddress);
|
||||
groupId = _groupId;
|
||||
|
||||
semaphore.createGroup(groupId, 20, address(this));
|
||||
}
|
||||
|
||||
function joinGroup(uint256 identityCommitment, bytes32 username) external {
|
||||
semaphore.addMember(groupId, identityCommitment);
|
||||
|
||||
users[identityCommitment] = username;
|
||||
|
||||
emit NewUser(identityCommitment, username);
|
||||
}
|
||||
|
||||
function greet(
|
||||
bytes32 greeting,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 nullifierHash,
|
||||
uint256[8] calldata proof
|
||||
) external {
|
||||
semaphore.verifyProof(groupId, merkleTreeRoot, uint256(greeting), nullifierHash, groupId, proof);
|
||||
|
||||
emit NewGreeting(greeting);
|
||||
}
|
||||
}
|
||||
61
packages/cli-template-hardhat/hardhat.config.ts
Normal file
61
packages/cli-template-hardhat/hardhat.config.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import "@nomicfoundation/hardhat-toolbox"
|
||||
import "@semaphore-protocol/hardhat"
|
||||
import { config as dotenvConfig } from "dotenv"
|
||||
import { HardhatUserConfig } from "hardhat/config"
|
||||
import { NetworksUserConfig } from "hardhat/types"
|
||||
import "./tasks/deploy"
|
||||
|
||||
dotenvConfig()
|
||||
|
||||
function getNetworks(): NetworksUserConfig {
|
||||
if (process.env.ETHEREUM_URL && process.env.ETHEREUM_PRIVATE_KEY) {
|
||||
const accounts = [`0x${process.env.ETHEREUM_PRIVATE_KEY}`]
|
||||
|
||||
return {
|
||||
goerli: {
|
||||
url: process.env.ETHEREUM_URL,
|
||||
chainId: 5,
|
||||
accounts
|
||||
},
|
||||
sepolia: {
|
||||
url: process.env.ETHEREUM_URL,
|
||||
chainId: 11155111,
|
||||
accounts
|
||||
},
|
||||
mumbai: {
|
||||
url: process.env.ETHEREUM_URL,
|
||||
chainId: 80001,
|
||||
accounts
|
||||
},
|
||||
"optimism-goerli": {
|
||||
url: process.env.ETHEREUM_URL,
|
||||
chainId: 420,
|
||||
accounts
|
||||
},
|
||||
arbitrum: {
|
||||
url: process.env.ETHEREUM_URL,
|
||||
chainId: 42161,
|
||||
accounts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
const config: HardhatUserConfig = {
|
||||
solidity: "0.8.4",
|
||||
networks: {
|
||||
hardhat: {
|
||||
chainId: 1337
|
||||
},
|
||||
...getNetworks()
|
||||
},
|
||||
gasReporter: {
|
||||
currency: "USD",
|
||||
enabled: process.env.REPORT_GAS === "true",
|
||||
coinmarketcap: process.env.COINMARKETCAP_API_KEY
|
||||
}
|
||||
}
|
||||
|
||||
export default config
|
||||
60
packages/cli-template-hardhat/package.json
Normal file
60
packages/cli-template-hardhat/package.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/cli-template-hardhat",
|
||||
"version": "3.2.0",
|
||||
"description": "Semaphore Hardhat template.",
|
||||
"license": "Unlicense",
|
||||
"files": [
|
||||
".gitignore",
|
||||
".env.example",
|
||||
"contracts/",
|
||||
"tasks/",
|
||||
"test/",
|
||||
"hardhat.config.ts",
|
||||
"tsconfig.json",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "hardhat node",
|
||||
"compile": "hardhat compile",
|
||||
"deploy": "hardhat deploy",
|
||||
"test": "hardhat test",
|
||||
"test:report-gas": "REPORT_GAS=true hardhat test",
|
||||
"test:coverage": "hardhat coverage"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ethersproject/abi": "^5.4.7",
|
||||
"@ethersproject/providers": "^5.4.7",
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^1.0.0",
|
||||
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
|
||||
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.0",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.0.0",
|
||||
"@semaphore-protocol/group": "3.2.0",
|
||||
"@semaphore-protocol/hardhat": "3.2.0",
|
||||
"@semaphore-protocol/identity": "3.2.0",
|
||||
"@semaphore-protocol/proof": "3.2.0",
|
||||
"@typechain/ethers-v5": "^10.1.0",
|
||||
"@typechain/hardhat": "^6.1.2",
|
||||
"@types/chai": "^4.2.0",
|
||||
"@types/download": "^8.0.1",
|
||||
"@types/mocha": "^9.1.0",
|
||||
"@types/node": ">=12.0.0",
|
||||
"chai": "^4.2.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"download": "^8.0.0",
|
||||
"ethers": "^5.4.7",
|
||||
"hardhat": "^2.11.0",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
"solidity-coverage": "^0.8.1",
|
||||
"ts-node": ">=8.0.0",
|
||||
"typechain": "^8.1.0",
|
||||
"typescript": ">=4.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@semaphore-protocol/contracts": "3.2.0"
|
||||
}
|
||||
}
|
||||
27
packages/cli-template-hardhat/tasks/deploy.ts
Normal file
27
packages/cli-template-hardhat/tasks/deploy.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { task, types } from "hardhat/config"
|
||||
|
||||
task("deploy", "Deploy a Greeter contract")
|
||||
.addOptionalParam("semaphore", "Semaphore contract address", undefined, types.string)
|
||||
.addOptionalParam("group", "Group id", "42", types.string)
|
||||
.addOptionalParam("logs", "Print the logs", true, types.boolean)
|
||||
.setAction(async ({ logs, semaphore: semaphoreAddress, group: groupId }, { ethers, run }) => {
|
||||
if (!semaphoreAddress) {
|
||||
const { semaphore } = await run("deploy:semaphore", {
|
||||
logs
|
||||
})
|
||||
|
||||
semaphoreAddress = semaphore.address
|
||||
}
|
||||
|
||||
const Greeter = await ethers.getContractFactory("Greeter")
|
||||
|
||||
const greeter = await Greeter.deploy(semaphoreAddress, groupId)
|
||||
|
||||
await greeter.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Greeter contract has been deployed to: ${greeter.address}`)
|
||||
}
|
||||
|
||||
return greeter
|
||||
})
|
||||
72
packages/cli-template-hardhat/test/Greeter.ts
Normal file
72
packages/cli-template-hardhat/test/Greeter.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { generateProof } from "@semaphore-protocol/proof"
|
||||
import { expect } from "chai"
|
||||
import download from "download"
|
||||
import { existsSync } from "fs"
|
||||
import { ethers, run } from "hardhat"
|
||||
// @ts-ignore: typechain-types folder will be generated after contracts compilation
|
||||
import { Greeter } from "../typechain-types"
|
||||
|
||||
describe("Greeter", () => {
|
||||
let greeter: Greeter
|
||||
|
||||
const snarkArtifactsURL = "https://www.trusted-setup-pse.org/semaphore/20"
|
||||
const snarkArtifactsPath = "./artifacts/snark"
|
||||
|
||||
const users: any[] = []
|
||||
const groupId = "42"
|
||||
const group = new Group(groupId)
|
||||
|
||||
before(async () => {
|
||||
if (!existsSync(`${snarkArtifactsPath}/semaphore.wasm`)) {
|
||||
await download(`${snarkArtifactsURL}/semaphore.wasm`, `${snarkArtifactsPath}`)
|
||||
await download(`${snarkArtifactsURL}/semaphore.zkey`, `${snarkArtifactsPath}`)
|
||||
}
|
||||
|
||||
greeter = await run("deploy", { logs: false, group: groupId })
|
||||
|
||||
users.push({
|
||||
identity: new Identity(),
|
||||
username: ethers.utils.formatBytes32String("anon1")
|
||||
})
|
||||
|
||||
users.push({
|
||||
identity: new Identity(),
|
||||
username: ethers.utils.formatBytes32String("anon2")
|
||||
})
|
||||
|
||||
group.addMember(users[0].identity.commitment)
|
||||
group.addMember(users[1].identity.commitment)
|
||||
})
|
||||
|
||||
describe("# joinGroup", () => {
|
||||
it("Should allow users to join the group", async () => {
|
||||
for (let i = 0; i < group.members.length; i += 1) {
|
||||
const transaction = greeter.joinGroup(group.members[i], users[i].username)
|
||||
|
||||
await expect(transaction).to.emit(greeter, "NewUser").withArgs(group.members[i], users[i].username)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("# greet", () => {
|
||||
it("Should allow users to greet", async () => {
|
||||
const greeting = ethers.utils.formatBytes32String("Hello World")
|
||||
|
||||
const fullProof = await generateProof(users[1].identity, group, groupId, greeting, {
|
||||
wasmFilePath: `${snarkArtifactsPath}/semaphore.wasm`,
|
||||
zkeyFilePath: `${snarkArtifactsPath}/semaphore.zkey`
|
||||
})
|
||||
|
||||
const transaction = greeter.greet(
|
||||
greeting,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.nullifierHash,
|
||||
fullProof.proof
|
||||
)
|
||||
|
||||
await expect(transaction).to.emit(greeter, "NewGreeting").withArgs(greeting)
|
||||
})
|
||||
})
|
||||
})
|
||||
12
packages/cli-template-hardhat/tsconfig.json
Normal file
12
packages/cli-template-hardhat/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["./tasks", "./test", "./typechain-types"],
|
||||
"files": ["./hardhat.config.ts"]
|
||||
}
|
||||
82
packages/cli/README.md
Normal file
82
packages/cli/README.md
Normal file
@@ -0,0 +1,82 @@
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
Semaphore CLI
|
||||
</h1>
|
||||
<p align="center">A command line tool to set up your Semaphore project and get group data.</p>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/semaphore-protocol">
|
||||
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/LICENSE">
|
||||
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/@semaphore-protocol/cli">
|
||||
<img alt="NPM version" src="https://img.shields.io/npm/v/@semaphore-protocol/cli?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/cli">
|
||||
<img alt="Downloads" src="https://img.shields.io/npm/dm/@semaphore-protocol/cli.svg?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://eslint.org/">
|
||||
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint" />
|
||||
</a>
|
||||
<a href="https://prettier.io/">
|
||||
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
<h4>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CONTRIBUTING.md">
|
||||
👥 Contributing
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
|
||||
🤝 Code of conduct
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://semaphore.appliedzkp.org/discord">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
| Setting up a project, although not particularly complex, can be a lengthy process for some people. The Semaphore CLI reduces the set-up time from a few minutes to a few seconds. In addition, it can also be used to obtain on-chain group data. |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
## 🛠 Install
|
||||
|
||||
Install the `@semaphore-protocol/cli` package globally:
|
||||
|
||||
```bash
|
||||
npm i -g @semaphore-protocol/cli
|
||||
```
|
||||
|
||||
or run specific commands with `npx`:
|
||||
|
||||
```bash
|
||||
npx @semaphore-protocol/cli create my-app
|
||||
```
|
||||
|
||||
## 📜 Usage
|
||||
|
||||
```
|
||||
Usage: semaphore [options] [command]
|
||||
|
||||
A command line tool to set up your Semaphore project and get group data.
|
||||
|
||||
Options:
|
||||
-v, --version Show Semaphore CLI version.
|
||||
-h, --help Display this help.
|
||||
|
||||
Commands:
|
||||
create <project-directory> Create a Semaphore project with a supported template.
|
||||
get-groups [options] Get the list of groups from a supported network (Goerli or Arbitrum).
|
||||
get-group [options] <group-id> Get the data of a group from a supported network (Goerli or Arbitrum).
|
||||
help [command] Display help for a specific command.
|
||||
```
|
||||
9
packages/cli/build.tsconfig.json
Normal file
9
packages/cli/build.tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"target": "es2020",
|
||||
"module": "ESNext",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
56
packages/cli/package.json
Normal file
56
packages/cli/package.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/cli",
|
||||
"type": "module",
|
||||
"version": "3.2.0",
|
||||
"description": "A command line tool to set up your Semaphore project and get group data.",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"semaphore": "dist/index.js"
|
||||
},
|
||||
"types": "dist/types/index.d.ts",
|
||||
"files": [
|
||||
"dist/",
|
||||
"src/",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"repository": "https://github.com/semaphore-protocol/semaphore",
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore/tree/main/packages/cli",
|
||||
"bugs": {
|
||||
"url": "https://github.com/semaphore-protocol/semaphore.git/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "ts-node --esm src/index.ts",
|
||||
"build:watch": "rollup -c rollup.config.ts -w --configPlugin typescript",
|
||||
"build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
|
||||
"prepublishOnly": "yarn build"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/clear": "^0.1.2",
|
||||
"@types/figlet": "^1.5.5",
|
||||
"@types/semver": "^7.3.13",
|
||||
"rollup-plugin-cleanup": "^3.2.1",
|
||||
"rollup-plugin-typescript2": "^0.31.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"typedoc": "^0.22.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@semaphore-protocol/data": "3.2.0",
|
||||
"axios": "^1.3.2",
|
||||
"boxen": "^7.0.1",
|
||||
"chalk": "^5.1.2",
|
||||
"commander": "^9.4.1",
|
||||
"download": "^8.0.0",
|
||||
"figlet": "^1.5.2",
|
||||
"inquirer": "^9.1.4",
|
||||
"log-symbols": "^5.1.0",
|
||||
"ora": "^6.1.2",
|
||||
"semver": "^7.3.8"
|
||||
}
|
||||
}
|
||||
29
packages/cli/rollup.config.ts
Normal file
29
packages/cli/rollup.config.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import typescript from "rollup-plugin-typescript2"
|
||||
import fs from "fs"
|
||||
import cleanup from "rollup-plugin-cleanup"
|
||||
|
||||
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
|
||||
const banner = `#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* @module ${pkg.name}
|
||||
* @version ${pkg.version}
|
||||
* @file ${pkg.description}
|
||||
* @copyright Ethereum Foundation 2022
|
||||
* @license ${pkg.license}
|
||||
* @see [Github]{@link ${pkg.homepage}}
|
||||
*/
|
||||
`
|
||||
|
||||
export default {
|
||||
input: "src/index.ts",
|
||||
output: [{ file: pkg.bin.semaphore, format: "es", banner }],
|
||||
external: ["url", "fs", "path", ...Object.keys(pkg.dependencies)],
|
||||
plugins: [
|
||||
(typescript as any)({
|
||||
tsconfig: "./build.tsconfig.json",
|
||||
useTsconfigDeclarationDir: true
|
||||
}),
|
||||
cleanup({ comments: "jsdoc" })
|
||||
]
|
||||
}
|
||||
48
packages/cli/src/checkLatestVersion.ts
Normal file
48
packages/cli/src/checkLatestVersion.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import axios from "axios"
|
||||
import boxen from "boxen"
|
||||
import chalk from "chalk"
|
||||
import { execSync } from "child_process"
|
||||
import { lt as semverLt } from "semver"
|
||||
|
||||
const cliRegistryURL = "https://registry.npmjs.org/-/package/@semaphore-protocol/cli/dist-tags"
|
||||
|
||||
/**
|
||||
* Checks the registry directly via the API, if that fails, tries the slower `npm view [package] version` command.
|
||||
* This is important for users in environments where direct access to npm is blocked by a firewall, and packages are
|
||||
* provided exclusively via a private registry.
|
||||
* @param currentVersion The current version of the CLI.
|
||||
*/
|
||||
export default async function checkLatestVersion(currentVersion: string) {
|
||||
let latestVersion: string
|
||||
|
||||
try {
|
||||
const { data } = await axios.get(cliRegistryURL)
|
||||
|
||||
latestVersion = data.latest
|
||||
} catch {
|
||||
try {
|
||||
latestVersion = execSync("npm view @semaphore-protocol/cli version").toString().trim()
|
||||
} catch {
|
||||
latestVersion = null
|
||||
}
|
||||
}
|
||||
|
||||
if (latestVersion && semverLt(currentVersion, latestVersion)) {
|
||||
console.info("\n")
|
||||
console.info(
|
||||
boxen(
|
||||
chalk.white(
|
||||
`Update available ${chalk.gray(currentVersion)} -> ${chalk.green(
|
||||
latestVersion
|
||||
)} \n\n You are currently using @semaphore-protocol/cli ${chalk.gray(
|
||||
currentVersion
|
||||
)} which is behind the latest release ${chalk.green(latestVersion)} \n\n Run ${chalk.cyan(
|
||||
"npm install -g @semaphore-protocol/cli@latest"
|
||||
)} to get the latest version`
|
||||
),
|
||||
{ padding: 1, borderColor: "yellow", textAlignment: "center" }
|
||||
)
|
||||
)
|
||||
console.info("")
|
||||
}
|
||||
}
|
||||
255
packages/cli/src/index.ts
Normal file
255
packages/cli/src/index.ts
Normal file
@@ -0,0 +1,255 @@
|
||||
import { SemaphoreSubgraph } from "@semaphore-protocol/data"
|
||||
import chalk from "chalk"
|
||||
import { program } from "commander"
|
||||
import download from "download"
|
||||
import figlet from "figlet"
|
||||
import { existsSync, readFileSync, renameSync } from "fs"
|
||||
import inquirer from "inquirer"
|
||||
import logSymbols from "log-symbols"
|
||||
import { dirname } from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
import checkLatestVersion from "./checkLatestVersion.js"
|
||||
import Spinner from "./spinner.js"
|
||||
|
||||
const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`
|
||||
const { description, version } = JSON.parse(readFileSync(`${packagePath}/package.json`, "utf8"))
|
||||
|
||||
const supportedNetworks = ["goerli", "mumbai", "optimism-goerli", "arbitrum"]
|
||||
|
||||
program
|
||||
.name("semaphore")
|
||||
.description(description)
|
||||
.version(version, "-v, --version", "Show Semaphore CLI version.")
|
||||
.addHelpText("before", `${figlet.textSync("Semaphore")}\n`)
|
||||
.addHelpText("after", "\r")
|
||||
.helpOption(undefined, "Display this help.")
|
||||
.addHelpCommand("help [command]", "Display help for a specific command.")
|
||||
.configureOutput({
|
||||
outputError: (message) => {
|
||||
console.info(`\n ${logSymbols.error}`, message)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command("create")
|
||||
.description("Create a Semaphore project with a supported template.")
|
||||
.argument("[project-directory]", "Directory of the project.")
|
||||
// .option("-t, --template <template-name>", "Supported Semaphore template.", "hardhat")
|
||||
.allowExcessArguments(false)
|
||||
.action(async (projectDirectory) => {
|
||||
if (!projectDirectory) {
|
||||
const { projectName } = await inquirer.prompt({
|
||||
name: "projectName",
|
||||
type: "input",
|
||||
message: "What is your project name?",
|
||||
default: "my-app"
|
||||
})
|
||||
projectDirectory = projectName
|
||||
}
|
||||
|
||||
const currentDirectory = process.cwd()
|
||||
const spinner = new Spinner(`Creating your project in ${chalk.green(`./${projectDirectory}`)}`)
|
||||
const templateURL = `https://registry.npmjs.org/@semaphore-protocol/cli-template-hardhat/-/cli-template-hardhat-${version}.tgz`
|
||||
|
||||
if (existsSync(projectDirectory)) {
|
||||
console.info(`\n ${logSymbols.error}`, `error: the '${projectDirectory}' folder already exists\n`)
|
||||
return
|
||||
}
|
||||
|
||||
spinner.start()
|
||||
|
||||
await checkLatestVersion(version)
|
||||
|
||||
await download(templateURL, currentDirectory, { extract: true })
|
||||
|
||||
renameSync(`${currentDirectory}/package`, `${currentDirectory}/${projectDirectory}`)
|
||||
|
||||
spinner.stop()
|
||||
|
||||
console.info(`\n ${logSymbols.success}`, `Your project is ready!\n`)
|
||||
console.info(` Please, install your dependencies by running:\n`)
|
||||
console.info(` ${chalk.cyan("cd")} ${projectDirectory}`)
|
||||
console.info(` ${chalk.cyan("npm i")}\n`)
|
||||
|
||||
const { scripts } = JSON.parse(readFileSync(`${currentDirectory}/${projectDirectory}/package.json`, "utf8"))
|
||||
|
||||
if (scripts) {
|
||||
console.info(` Available scripts:\n`)
|
||||
|
||||
console.info(
|
||||
`${Object.keys(scripts)
|
||||
.map((s) => ` ${chalk.cyan(`npm run ${s}`)}`)
|
||||
.join("\n")}\n`
|
||||
)
|
||||
|
||||
console.info(` See the README.md file to understand how to use them!\n`)
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command("get-groups")
|
||||
.description("Get the list of groups from a supported network (goerli or arbitrum).")
|
||||
.option("-n, --network <network-name>", "Supported Ethereum network.")
|
||||
.allowExcessArguments(false)
|
||||
.action(async ({ network }) => {
|
||||
if (!network) {
|
||||
const { selectedNetwork } = await inquirer.prompt({
|
||||
name: "selectedNetwork",
|
||||
type: "list",
|
||||
message: "Select one of our supported networks:",
|
||||
default: supportedNetworks[0],
|
||||
choices: supportedNetworks
|
||||
})
|
||||
network = selectedNetwork
|
||||
}
|
||||
|
||||
if (!supportedNetworks.includes(network)) {
|
||||
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`)
|
||||
return
|
||||
}
|
||||
|
||||
const subgraph = new SemaphoreSubgraph(network)
|
||||
const spinner = new Spinner("Fetching groups")
|
||||
|
||||
spinner.start()
|
||||
|
||||
try {
|
||||
const groupIds = await subgraph.getGroupIds()
|
||||
|
||||
spinner.stop()
|
||||
|
||||
if (groupIds.length === 0) {
|
||||
console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n")
|
||||
return
|
||||
}
|
||||
|
||||
const content = `\n${groupIds.map((id: any) => ` - ${id}`).join("\n")}`
|
||||
|
||||
console.info(`${content}\n`)
|
||||
} catch (error) {
|
||||
spinner.stop()
|
||||
|
||||
console.info(`\n ${logSymbols.error}`, "error: unexpected error with the Semaphore subgraph")
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command("get-group")
|
||||
.description("Get the data of a group from a supported network (Goerli or Arbitrum).")
|
||||
.argument("[group-id]", "Identifier of the group.")
|
||||
.option("-n, --network <network-name>", "Supported Ethereum network.")
|
||||
.option("-m, --members", "Show group members.")
|
||||
.option("-s, --signals", "Show group signals.")
|
||||
.allowExcessArguments(false)
|
||||
.action(async (groupId, { network, members, signals }) => {
|
||||
if (!network) {
|
||||
const { selectedNetwork } = await inquirer.prompt({
|
||||
name: "selectedNetwork",
|
||||
type: "list",
|
||||
message: "Select one of our supported networks:",
|
||||
default: supportedNetworks[0],
|
||||
choices: supportedNetworks
|
||||
})
|
||||
network = selectedNetwork
|
||||
}
|
||||
|
||||
if (!groupId) {
|
||||
const subgraphGroups = new SemaphoreSubgraph(network)
|
||||
const spinnerGroups = new Spinner("Fetching groups")
|
||||
spinnerGroups.start()
|
||||
try {
|
||||
const groups = await subgraphGroups.getGroups()
|
||||
|
||||
spinnerGroups.stop()
|
||||
|
||||
if (groups.length === 0) {
|
||||
console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n")
|
||||
return
|
||||
}
|
||||
|
||||
const groupIds = groups.map(({ id }: any) => id)
|
||||
|
||||
const { selectedGroupId } = await inquirer.prompt({
|
||||
name: "selectedGroupId",
|
||||
type: "list",
|
||||
message: "Select one of the following existing group ids:",
|
||||
choices: groupIds
|
||||
})
|
||||
groupId = selectedGroupId
|
||||
} catch (error) {
|
||||
spinnerGroups.stop()
|
||||
console.info(`\n ${logSymbols.error}`, "error: unexpected error with the Semaphore subgraph")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!members && !signals) {
|
||||
const { showMembers } = await inquirer.prompt({
|
||||
name: "showMembers",
|
||||
type: "confirm",
|
||||
message: "Do you want to show members?",
|
||||
default: false
|
||||
})
|
||||
|
||||
members = showMembers
|
||||
|
||||
const { showSignals } = await inquirer.prompt({
|
||||
name: "showSignals",
|
||||
type: "confirm",
|
||||
message: "Do you want to show signals?",
|
||||
default: false
|
||||
})
|
||||
|
||||
signals = showSignals
|
||||
}
|
||||
|
||||
if (!supportedNetworks.includes(network)) {
|
||||
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`)
|
||||
return
|
||||
}
|
||||
|
||||
const subgraph = new SemaphoreSubgraph(network)
|
||||
const spinner = new Spinner(`Fetching group ${groupId}`)
|
||||
|
||||
spinner.start()
|
||||
|
||||
try {
|
||||
const group = await subgraph.getGroup(groupId, { members, verifiedProofs: signals })
|
||||
|
||||
spinner.stop()
|
||||
|
||||
if (!group) {
|
||||
console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let content = ` ${chalk.bold("Id")}: ${group.id}\n`
|
||||
content += ` ${chalk.bold("Admin")}: ${group.admin}\n`
|
||||
content += ` ${chalk.bold("Merkle tree")}:\n`
|
||||
content += ` Root: ${group.merkleTree.root}\n`
|
||||
content += ` Depth: ${group.merkleTree.depth}\n`
|
||||
content += ` Zero value: ${group.merkleTree.zeroValue}\n`
|
||||
content += ` Number of leaves: ${group.merkleTree.numberOfLeaves}`
|
||||
|
||||
if (members) {
|
||||
content += `\n\n ${chalk.bold("Members")}: \n${group.members
|
||||
.map((member: string, i: number) => ` ${i}. ${member}`)
|
||||
.join("\n")}`
|
||||
}
|
||||
|
||||
if (signals) {
|
||||
content += `\n\n ${chalk.bold("Signals")}: \n${group.verifiedProofs
|
||||
.map(({ signal }: any) => ` - ${signal}`)
|
||||
.join("\n")}`
|
||||
}
|
||||
|
||||
console.info(`\n${content}\n`)
|
||||
} catch (error) {
|
||||
spinner.stop()
|
||||
|
||||
console.info(`\n ${logSymbols.error}`, "error: unexpected error with the Semaphore subgraph")
|
||||
}
|
||||
})
|
||||
|
||||
program.parse(process.argv)
|
||||
25
packages/cli/src/spinner.ts
Normal file
25
packages/cli/src/spinner.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import ora, { Ora } from "ora"
|
||||
|
||||
export default class Spinner {
|
||||
private text: string
|
||||
private ora: Ora
|
||||
|
||||
constructor(text: string) {
|
||||
this.text = text
|
||||
}
|
||||
|
||||
start() {
|
||||
console.info("")
|
||||
|
||||
this.ora = ora({
|
||||
text: this.text,
|
||||
indent: 1
|
||||
}).start()
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.ora.stop()
|
||||
|
||||
process.stdout.moveCursor(0, -1)
|
||||
}
|
||||
}
|
||||
10
packages/cli/tsconfig.json
Normal file
10
packages/cli/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"target": "es2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node16",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["src", "rollup.config.ts"]
|
||||
}
|
||||
@@ -1,22 +1,4 @@
|
||||
module.exports = {
|
||||
istanbulFolder: "../../coverage/contracts",
|
||||
skipFiles: [
|
||||
"verifiers/Verifier16.sol",
|
||||
"verifiers/Verifier17.sol",
|
||||
"verifiers/Verifier18.sol",
|
||||
"verifiers/Verifier19.sol",
|
||||
"verifiers/Verifier20.sol",
|
||||
"verifiers/Verifier21.sol",
|
||||
"verifiers/Verifier22.sol",
|
||||
"verifiers/Verifier23.sol",
|
||||
"verifiers/Verifier24.sol",
|
||||
"verifiers/Verifier25.sol",
|
||||
"verifiers/Verifier26.sol",
|
||||
"verifiers/Verifier27.sol",
|
||||
"verifiers/Verifier28.sol",
|
||||
"verifiers/Verifier29.sol",
|
||||
"verifiers/Verifier30.sol",
|
||||
"verifiers/Verifier31.sol",
|
||||
"verifiers/Verifier32.sol"
|
||||
]
|
||||
skipFiles: ["base/Pairing.sol"]
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"rules": {
|
||||
"code-complexity": ["error", 7],
|
||||
"compiler-version": ["error", ">=0.8.0"],
|
||||
"var-name-mixedcase": "off",
|
||||
"const-name-snakecase": "off",
|
||||
"no-empty-blocks": "off",
|
||||
"constructor-syntax": "error",
|
||||
|
||||
@@ -1 +1 @@
|
||||
contracts/verifiers
|
||||
contracts/base/Pairing.sol
|
||||
|
||||
21
packages/contracts/contracts/LICENSE
Normal file
21
packages/contracts/contracts/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ethereum Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -34,7 +34,7 @@
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://discord.gg/6mSdGHnstH">
|
||||
<a href="https://semaphore.appliedzkp.org/discord">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
@@ -92,29 +92,29 @@ yarn test:report-gas
|
||||
|
||||
### Deploy contracts
|
||||
|
||||
Deploy a verifier contract with depth = 20:
|
||||
Deploy the `Semaphore.sol` contract without any parameter:
|
||||
|
||||
```bash
|
||||
yarn deploy:verifier --depth 20
|
||||
yarn deploy:semaphore
|
||||
```
|
||||
|
||||
Deploy the `Semaphore.sol` contract with one verifier:
|
||||
or deploy it by providing the addresses of the contracts/libraries on which it depends:
|
||||
|
||||
```bash
|
||||
yarn deploy:semaphore --verifiers '[{"merkleTreeDepth": 20, "contractAddress": "0x06bcD633988c1CE7Bd134DbE2C12119b6f3E4bD1"}]'
|
||||
yarn deploy:semaphore --semaphoreVerifier <address>
|
||||
```
|
||||
|
||||
Deploy all verifiers and Semaphore contract:
|
||||
> **Note**
|
||||
> Run `yarn deploy:semaphore --help` to see the complete list.
|
||||
|
||||
If you want to deploy your contract in a specific network you can set up the `DEFAULT_NETWORK` variable in your `.env` file with the name of one of our supported networks (hardhat, localhost, goerli, arbitrum). Or you can specify it as an option:
|
||||
|
||||
```bash
|
||||
yarn deploy:all
|
||||
```
|
||||
|
||||
If you want to deploy contracts in a specific network you can set up the `DEFAULT_NETWORK` variable in your `.env` file with the name of one of our supported networks (hardhat, localhost, goerli, arbitrum). Or you can specify it as option:
|
||||
|
||||
```bash
|
||||
yarn deploy:all --network goerli
|
||||
yarn deploy:all --network localhost
|
||||
yarn deploy:semaphore --network goerli
|
||||
yarn deploy:semaphore --network sepolia
|
||||
yarn deploy:semaphore --network mumbai
|
||||
yarn deploy:semaphore --network optimism-goerli
|
||||
yarn deploy:semaphore --network arbitrum
|
||||
```
|
||||
|
||||
If you want to deploy contracts on Goerli or Arbitrum, remember to provide a valid private key and an Infura API in your `.env` file.
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
import "./interfaces/ISemaphore.sol";
|
||||
import "./interfaces/IVerifier.sol";
|
||||
import "./base/SemaphoreCore.sol";
|
||||
import "./interfaces/ISemaphoreVerifier.sol";
|
||||
import "./base/SemaphoreGroups.sol";
|
||||
|
||||
/// @title Semaphore
|
||||
contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
/// @dev Gets a tree depth and returns its verifier address.
|
||||
mapping(uint256 => IVerifier) public verifiers;
|
||||
/// @dev This contract uses the Semaphore base contracts to provide a complete service
|
||||
/// to allow admins to create and manage groups and their members to generate Semaphore proofs
|
||||
/// and verify them. Group admins can add, update or remove group members, and can be
|
||||
/// an Ethereum account or a smart contract. This contract also assigns each new Merkle tree
|
||||
/// generated with a new root a duration (or an expiry) within which the proofs generated with that root
|
||||
/// can be validated.
|
||||
contract Semaphore is ISemaphore, SemaphoreGroups {
|
||||
ISemaphoreVerifier public verifier;
|
||||
|
||||
/// @dev Gets a group id and returns the group parameters.
|
||||
mapping(uint256 => Group) public groups;
|
||||
@@ -26,35 +30,28 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
/// @dev Checks if there is a verifier for the given tree depth.
|
||||
/// @param merkleTreeDepth: Depth of the tree.
|
||||
modifier onlySupportedMerkleTreeDepth(uint256 merkleTreeDepth) {
|
||||
if (address(verifiers[merkleTreeDepth]) == address(0)) {
|
||||
if (merkleTreeDepth < 16 || merkleTreeDepth > 32) {
|
||||
revert Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Initializes the Semaphore verifiers used to verify the user's ZK proofs.
|
||||
/// @param _verifiers: List of Semaphore verifiers (address and related Merkle tree depth).
|
||||
constructor(Verifier[] memory _verifiers) {
|
||||
for (uint8 i = 0; i < _verifiers.length; ) {
|
||||
verifiers[_verifiers[i].merkleTreeDepth] = IVerifier(_verifiers[i].contractAddress);
|
||||
|
||||
unchecked {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
/// @dev Initializes the Semaphore verifier used to verify the user's ZK proofs.
|
||||
/// @param _verifier: Semaphore verifier address.
|
||||
constructor(ISemaphoreVerifier _verifier) {
|
||||
verifier = _verifier;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-createGroup}.
|
||||
function createGroup(
|
||||
uint256 groupId,
|
||||
uint256 merkleTreeDepth,
|
||||
uint256 zeroValue,
|
||||
address admin
|
||||
) external override onlySupportedMerkleTreeDepth(merkleTreeDepth) {
|
||||
_createGroup(groupId, merkleTreeDepth, zeroValue);
|
||||
_createGroup(groupId, merkleTreeDepth);
|
||||
|
||||
groups[groupId].admin = admin;
|
||||
groups[groupId].merkleRootDuration = 1 hours;
|
||||
groups[groupId].merkleTreeDuration = 1 hours;
|
||||
|
||||
emit GroupAdminUpdated(groupId, address(0), admin);
|
||||
}
|
||||
@@ -63,14 +60,13 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
function createGroup(
|
||||
uint256 groupId,
|
||||
uint256 merkleTreeDepth,
|
||||
uint256 zeroValue,
|
||||
address admin,
|
||||
uint256 merkleTreeRootDuration
|
||||
uint256 merkleTreeDuration
|
||||
) external override onlySupportedMerkleTreeDepth(merkleTreeDepth) {
|
||||
_createGroup(groupId, merkleTreeDepth, zeroValue);
|
||||
_createGroup(groupId, merkleTreeDepth);
|
||||
|
||||
groups[groupId].admin = admin;
|
||||
groups[groupId].merkleRootDuration = merkleTreeRootDuration;
|
||||
groups[groupId].merkleTreeDuration = merkleTreeDuration;
|
||||
|
||||
emit GroupAdminUpdated(groupId, address(0), admin);
|
||||
}
|
||||
@@ -82,6 +78,19 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
emit GroupAdminUpdated(groupId, _msgSender(), newAdmin);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-updateGroupMerkleTreeDuration}.
|
||||
function updateGroupMerkleTreeDuration(uint256 groupId, uint256 newMerkleTreeDuration)
|
||||
external
|
||||
override
|
||||
onlyGroupAdmin(groupId)
|
||||
{
|
||||
uint256 oldMerkleTreeDuration = groups[groupId].merkleTreeDuration;
|
||||
|
||||
groups[groupId].merkleTreeDuration = newMerkleTreeDuration;
|
||||
|
||||
emit GroupMerkleTreeDurationUpdated(groupId, oldMerkleTreeDuration, newMerkleTreeDuration);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-addMember}.
|
||||
function addMember(uint256 groupId, uint256 identityCommitment) external override onlyGroupAdmin(groupId) {
|
||||
_addMember(groupId, identityCommitment);
|
||||
@@ -97,7 +106,7 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
override
|
||||
onlyGroupAdmin(groupId)
|
||||
{
|
||||
for (uint8 i = 0; i < identityCommitments.length; ) {
|
||||
for (uint256 i = 0; i < identityCommitments.length; ) {
|
||||
_addMember(groupId, identityCommitments[i]);
|
||||
|
||||
unchecked {
|
||||
@@ -119,6 +128,10 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
uint8[] calldata proofPathIndices
|
||||
) external override onlyGroupAdmin(groupId) {
|
||||
_updateMember(groupId, identityCommitment, newIdentityCommitment, proofSiblings, proofPathIndices);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
|
||||
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-removeMember}.
|
||||
@@ -129,32 +142,40 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
uint8[] calldata proofPathIndices
|
||||
) external override onlyGroupAdmin(groupId) {
|
||||
_removeMember(groupId, identityCommitment, proofSiblings, proofPathIndices);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
|
||||
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-verifyProof}.
|
||||
function verifyProof(
|
||||
uint256 groupId,
|
||||
uint256 merkleTreeRoot,
|
||||
bytes32 signal,
|
||||
uint256 signal,
|
||||
uint256 nullifierHash,
|
||||
uint256 externalNullifier,
|
||||
uint256[8] calldata proof
|
||||
) external override {
|
||||
uint256 currentMerkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
uint256 merkleTreeDepth = getMerkleTreeDepth(groupId);
|
||||
|
||||
if (currentMerkleTreeRoot == 0) {
|
||||
if (merkleTreeDepth == 0) {
|
||||
revert Semaphore__GroupDoesNotExist();
|
||||
}
|
||||
|
||||
uint256 currentMerkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
|
||||
// A proof could have used an old Merkle tree root.
|
||||
// https://github.com/semaphore-protocol/semaphore/issues/98
|
||||
if (merkleTreeRoot != currentMerkleTreeRoot) {
|
||||
uint256 merkleRootCreationDate = groups[groupId].merkleRootCreationDates[merkleTreeRoot];
|
||||
uint256 merkleRootDuration = groups[groupId].merkleRootDuration;
|
||||
uint256 merkleTreeDuration = groups[groupId].merkleTreeDuration;
|
||||
|
||||
if (merkleRootCreationDate == 0) {
|
||||
revert Semaphore__MerkleTreeRootIsNotPartOfTheGroup();
|
||||
}
|
||||
|
||||
if (block.timestamp > merkleRootCreationDate + merkleRootDuration) {
|
||||
if (block.timestamp > merkleRootCreationDate + merkleTreeDuration) {
|
||||
revert Semaphore__MerkleTreeRootIsExpired();
|
||||
}
|
||||
}
|
||||
@@ -163,11 +184,7 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
revert Semaphore__YouAreUsingTheSameNillifierTwice();
|
||||
}
|
||||
|
||||
uint256 merkleTreeDepth = getMerkleTreeDepth(groupId);
|
||||
|
||||
IVerifier verifier = verifiers[merkleTreeDepth];
|
||||
|
||||
_verifyProof(signal, merkleTreeRoot, nullifierHash, externalNullifier, proof, verifier);
|
||||
verifier.verifyProof(merkleTreeRoot, nullifierHash, signal, externalNullifier, proof, merkleTreeDepth);
|
||||
|
||||
groups[groupId].nullifierHashes[nullifierHash] = true;
|
||||
|
||||
|
||||
151
packages/contracts/contracts/base/Pairing.sol
Normal file
151
packages/contracts/contracts/base/Pairing.sol
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// The following Pairing library is a modified version adapted to Semaphore.
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() public pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() public pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) public pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) {
|
||||
return G1Point(0, 0);
|
||||
}
|
||||
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) {
|
||||
revert InvalidProof();
|
||||
}
|
||||
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) public view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
|
||||
bool success;
|
||||
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) public view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) {
|
||||
revert InvalidProof();
|
||||
}
|
||||
|
||||
uint256[3] memory input;
|
||||
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
|
||||
bool success;
|
||||
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) public view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) {
|
||||
revert InvalidProof();
|
||||
}
|
||||
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
|
||||
if (!success || out[0] != 1) {
|
||||
revert InvalidProof();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
@@ -1,45 +0,0 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
import "../interfaces/ISemaphoreCore.sol";
|
||||
import "../interfaces/IVerifier.sol";
|
||||
|
||||
/// @title Semaphore core contract.
|
||||
/// @notice Minimal code to allow users to signal their endorsement of an arbitrary string.
|
||||
/// @dev The following code verifies that the proof is correct and saves the hash of the
|
||||
/// nullifier to prevent double-signaling. External nullifier and Merkle trees (i.e. groups) must be
|
||||
/// managed externally.
|
||||
contract SemaphoreCore is ISemaphoreCore {
|
||||
/// @dev Asserts that no nullifier already exists and if the zero-knowledge proof is valid.
|
||||
/// Otherwise it reverts.
|
||||
/// @param signal: Semaphore signal.
|
||||
/// @param root: Root of the Merkle tree.
|
||||
/// @param nullifierHash: Nullifier hash.
|
||||
/// @param externalNullifier: External nullifier.
|
||||
/// @param proof: Zero-knowledge proof.
|
||||
/// @param verifier: Verifier address.
|
||||
function _verifyProof(
|
||||
bytes32 signal,
|
||||
uint256 root,
|
||||
uint256 nullifierHash,
|
||||
uint256 externalNullifier,
|
||||
uint256[8] calldata proof,
|
||||
IVerifier verifier
|
||||
) internal view {
|
||||
uint256 signalHash = _hashSignal(signal);
|
||||
|
||||
verifier.verifyProof(
|
||||
[proof[0], proof[1]],
|
||||
[[proof[2], proof[3]], [proof[4], proof[5]]],
|
||||
[proof[6], proof[7]],
|
||||
[root, nullifierHash, signalHash, externalNullifier]
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Creates a keccak256 hash of the signal.
|
||||
/// @param signal: Semaphore signal.
|
||||
/// @return Hash of the signal.
|
||||
function _hashSignal(bytes32 signal) private pure returns (uint256) {
|
||||
return uint256(keccak256(abi.encodePacked(signal))) >> 8;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,33 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
import {SNARK_SCALAR_FIELD} from "./SemaphoreConstants.sol";
|
||||
import "../interfaces/ISemaphoreGroups.sol";
|
||||
import "@zk-kit/incremental-merkle-tree.sol/IncrementalBinaryTree.sol";
|
||||
import "@openzeppelin/contracts/utils/Context.sol";
|
||||
|
||||
/// @title Semaphore groups contract.
|
||||
/// @dev The following code allows you to create groups, add and remove members.
|
||||
/// @dev This contract allows you to create groups, add, remove and update members.
|
||||
/// You can use getters to obtain informations about groups (root, depth, number of leaves).
|
||||
abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
using IncrementalBinaryTree for IncrementalTreeData;
|
||||
|
||||
/// @dev Gets a group id and returns the tree data.
|
||||
mapping(uint256 => IncrementalTreeData) internal merkleTree;
|
||||
mapping(uint256 => IncrementalTreeData) internal merkleTrees;
|
||||
|
||||
/// @dev Creates a new group by initializing the associated tree.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param merkleTreeDepth: Depth of the tree.
|
||||
/// @param zeroValue: Zero value of the tree.
|
||||
function _createGroup(
|
||||
uint256 groupId,
|
||||
uint256 merkleTreeDepth,
|
||||
uint256 zeroValue
|
||||
) internal virtual {
|
||||
if (groupId >= SNARK_SCALAR_FIELD) {
|
||||
revert Semaphore__GroupIdIsNotLessThanSnarkScalarField();
|
||||
}
|
||||
|
||||
function _createGroup(uint256 groupId, uint256 merkleTreeDepth) internal virtual {
|
||||
if (getMerkleTreeDepth(groupId) != 0) {
|
||||
revert Semaphore__GroupAlreadyExists();
|
||||
}
|
||||
|
||||
merkleTree[groupId].init(merkleTreeDepth, zeroValue);
|
||||
// The zeroValue is an implicit member of the group, or an implicit leaf of the Merkle tree.
|
||||
// Although there is a remote possibility that the preimage of
|
||||
// the hash may be calculated, using this value we aim to minimize the risk.
|
||||
uint256 zeroValue = uint256(keccak256(abi.encodePacked(groupId))) >> 8;
|
||||
|
||||
merkleTrees[groupId].init(merkleTreeDepth, zeroValue);
|
||||
|
||||
emit GroupCreated(groupId, merkleTreeDepth, zeroValue);
|
||||
}
|
||||
@@ -45,7 +40,7 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
revert Semaphore__GroupDoesNotExist();
|
||||
}
|
||||
|
||||
merkleTree[groupId].insert(identityCommitment);
|
||||
merkleTrees[groupId].insert(identityCommitment);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
uint256 index = getNumberOfMerkleTreeLeaves(groupId) - 1;
|
||||
@@ -67,11 +62,11 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
) internal virtual {
|
||||
if (getMerkleTreeRoot(groupId) == 0) {
|
||||
if (getMerkleTreeDepth(groupId) == 0) {
|
||||
revert Semaphore__GroupDoesNotExist();
|
||||
}
|
||||
|
||||
merkleTree[groupId].update(identityCommitment, newIdentityCommitment, proofSiblings, proofPathIndices);
|
||||
merkleTrees[groupId].update(identityCommitment, newIdentityCommitment, proofSiblings, proofPathIndices);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
uint256 index = proofPathIndicesToMemberIndex(proofPathIndices);
|
||||
@@ -91,11 +86,11 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
) internal virtual {
|
||||
if (getMerkleTreeRoot(groupId) == 0) {
|
||||
if (getMerkleTreeDepth(groupId) == 0) {
|
||||
revert Semaphore__GroupDoesNotExist();
|
||||
}
|
||||
|
||||
merkleTree[groupId].remove(identityCommitment, proofSiblings, proofPathIndices);
|
||||
merkleTrees[groupId].remove(identityCommitment, proofSiblings, proofPathIndices);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
uint256 index = proofPathIndicesToMemberIndex(proofPathIndices);
|
||||
@@ -105,17 +100,17 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
|
||||
/// @dev See {ISemaphoreGroups-getMerkleTreeRoot}.
|
||||
function getMerkleTreeRoot(uint256 groupId) public view virtual override returns (uint256) {
|
||||
return merkleTree[groupId].root;
|
||||
return merkleTrees[groupId].root;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreGroups-getMerkleTreeDepth}.
|
||||
function getMerkleTreeDepth(uint256 groupId) public view virtual override returns (uint256) {
|
||||
return merkleTree[groupId].depth;
|
||||
return merkleTrees[groupId].depth;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreGroups-getNumberOfMerkleTreeLeaves}.
|
||||
function getNumberOfMerkleTreeLeaves(uint256 groupId) public view virtual override returns (uint256) {
|
||||
return merkleTree[groupId].numberOfLeaves;
|
||||
return merkleTrees[groupId].numberOfLeaves;
|
||||
}
|
||||
|
||||
/// @dev Converts the path indices of a Merkle proof to the identity commitment index in the tree.
|
||||
|
||||
113
packages/contracts/contracts/base/SemaphoreVerifier.sol
Normal file
113
packages/contracts/contracts/base/SemaphoreVerifier.sol
Normal file
File diff suppressed because one or more lines are too long
@@ -2,34 +2,18 @@
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
import "../interfaces/ISemaphoreVoting.sol";
|
||||
import "../base/SemaphoreCore.sol";
|
||||
import "../interfaces/ISemaphoreVerifier.sol";
|
||||
import "../base/SemaphoreGroups.sol";
|
||||
|
||||
/// @title Semaphore voting contract.
|
||||
/// @notice It allows users to vote anonymously in a poll.
|
||||
/// @dev The following code allows you to create polls, add voters and allow them to vote anonymously.
|
||||
contract SemaphoreVoting is ISemaphoreVoting, SemaphoreCore, SemaphoreGroups {
|
||||
/// @dev Gets a tree depth and returns its verifier address.
|
||||
mapping(uint256 => IVerifier) internal verifiers;
|
||||
contract SemaphoreVoting is ISemaphoreVoting, SemaphoreGroups {
|
||||
ISemaphoreVerifier public verifier;
|
||||
|
||||
/// @dev Gets a poll id and returns the poll data.
|
||||
mapping(uint256 => Poll) internal polls;
|
||||
|
||||
/// @dev Gets a nullifier hash and returns true or false.
|
||||
/// It is used to prevent double-voting.
|
||||
mapping(uint256 => bool) internal nullifierHashes;
|
||||
|
||||
/// @dev Initializes the Semaphore verifiers used to verify the user's ZK proofs.
|
||||
/// @param _verifiers: List of Semaphore verifiers (address and related Merkle tree depth).
|
||||
constructor(Verifier[] memory _verifiers) {
|
||||
for (uint8 i = 0; i < _verifiers.length; ) {
|
||||
verifiers[_verifiers[i].merkleTreeDepth] = IVerifier(_verifiers[i].contractAddress);
|
||||
|
||||
unchecked {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Checks if the poll coordinator is the transaction sender.
|
||||
/// @param pollId: Id of the poll.
|
||||
modifier onlyCoordinator(uint256 pollId) {
|
||||
@@ -40,23 +24,25 @@ contract SemaphoreVoting is ISemaphoreVoting, SemaphoreCore, SemaphoreGroups {
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Initializes the Semaphore verifier used to verify the user's ZK proofs.
|
||||
/// @param _verifier: Semaphore verifier address.
|
||||
constructor(ISemaphoreVerifier _verifier) {
|
||||
verifier = _verifier;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreVoting-createPoll}.
|
||||
function createPoll(
|
||||
uint256 pollId,
|
||||
address coordinator,
|
||||
uint256 merkleTreeDepth
|
||||
) public override {
|
||||
if (address(verifiers[merkleTreeDepth]) == address(0)) {
|
||||
if (merkleTreeDepth < 16 || merkleTreeDepth > 32) {
|
||||
revert Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
}
|
||||
|
||||
_createGroup(pollId, merkleTreeDepth, 0);
|
||||
_createGroup(pollId, merkleTreeDepth);
|
||||
|
||||
Poll memory poll;
|
||||
|
||||
poll.coordinator = coordinator;
|
||||
|
||||
polls[pollId] = poll;
|
||||
polls[pollId].coordinator = coordinator;
|
||||
|
||||
emit PollCreated(pollId, coordinator);
|
||||
}
|
||||
@@ -83,29 +69,25 @@ contract SemaphoreVoting is ISemaphoreVoting, SemaphoreCore, SemaphoreGroups {
|
||||
|
||||
/// @dev See {ISemaphoreVoting-castVote}.
|
||||
function castVote(
|
||||
bytes32 vote,
|
||||
uint256 vote,
|
||||
uint256 nullifierHash,
|
||||
uint256 pollId,
|
||||
uint256[8] calldata proof
|
||||
) public override onlyCoordinator(pollId) {
|
||||
Poll memory poll = polls[pollId];
|
||||
|
||||
if (poll.state != PollState.Ongoing) {
|
||||
) public override {
|
||||
if (polls[pollId].state != PollState.Ongoing) {
|
||||
revert Semaphore__PollIsNotOngoing();
|
||||
}
|
||||
|
||||
if (nullifierHashes[nullifierHash]) {
|
||||
if (polls[pollId].nullifierHashes[nullifierHash]) {
|
||||
revert Semaphore__YouAreUsingTheSameNillifierTwice();
|
||||
}
|
||||
|
||||
uint256 merkleTreeDepth = getMerkleTreeDepth(pollId);
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(pollId);
|
||||
|
||||
IVerifier verifier = verifiers[merkleTreeDepth];
|
||||
verifier.verifyProof(merkleTreeRoot, nullifierHash, vote, pollId, proof, merkleTreeDepth);
|
||||
|
||||
_verifyProof(vote, merkleTreeRoot, nullifierHash, pollId, proof, verifier);
|
||||
|
||||
nullifierHashes[nullifierHash] = true;
|
||||
polls[pollId].nullifierHashes[nullifierHash] = true;
|
||||
|
||||
emit VoteAdded(pollId, vote);
|
||||
}
|
||||
|
||||
@@ -2,55 +2,49 @@
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
import "../interfaces/ISemaphoreWhistleblowing.sol";
|
||||
import "../base/SemaphoreCore.sol";
|
||||
import "../interfaces/ISemaphoreVerifier.sol";
|
||||
import "../base/SemaphoreGroups.sol";
|
||||
|
||||
/// @title Semaphore whistleblowing contract.
|
||||
/// @notice It allows users to leak information anonymously .
|
||||
/// @dev The following code allows you to create entities for whistleblowers (e.g. non-profit
|
||||
/// organization, newspaper) and to allow them to publish news leaks anonymously.
|
||||
/// Leaks can be IPFS hashes, permanent links or other kinds of reference.
|
||||
contract SemaphoreWhistleblowing is ISemaphoreWhistleblowing, SemaphoreCore, SemaphoreGroups {
|
||||
/// @dev Gets a tree depth and returns its verifier address.
|
||||
mapping(uint256 => IVerifier) internal verifiers;
|
||||
/// organization, newspaper) and allow them to leak anonymously.
|
||||
/// Leaks can be IPFS hashes, permanent links or other kinds of references.
|
||||
contract SemaphoreWhistleblowing is ISemaphoreWhistleblowing, SemaphoreGroups {
|
||||
ISemaphoreVerifier public verifier;
|
||||
|
||||
/// @dev Gets an editor address and return their entity.
|
||||
mapping(address => uint256) private entities;
|
||||
|
||||
/// @dev Initializes the Semaphore verifiers used to verify the user's ZK proofs.
|
||||
/// @param _verifiers: List of Semaphore verifiers (address and related Merkle tree depth).
|
||||
constructor(Verifier[] memory _verifiers) {
|
||||
for (uint8 i = 0; i < _verifiers.length; ) {
|
||||
verifiers[_verifiers[i].merkleTreeDepth] = IVerifier(_verifiers[i].contractAddress);
|
||||
|
||||
unchecked {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @dev Gets an entity id and return its editor address.
|
||||
mapping(uint256 => address) private entities;
|
||||
|
||||
/// @dev Checks if the editor is the transaction sender.
|
||||
/// @param entityId: Id of the entity.
|
||||
modifier onlyEditor(uint256 entityId) {
|
||||
if (entityId != entities[_msgSender()]) {
|
||||
if (entities[entityId] != _msgSender()) {
|
||||
revert Semaphore__CallerIsNotTheEditor();
|
||||
}
|
||||
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Initializes the Semaphore verifier used to verify the user's ZK proofs.
|
||||
/// @param _verifier: Semaphore verifier address.
|
||||
constructor(ISemaphoreVerifier _verifier) {
|
||||
verifier = _verifier;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreWhistleblowing-createEntity}.
|
||||
function createEntity(
|
||||
uint256 entityId,
|
||||
address editor,
|
||||
uint256 merkleTreeDepth
|
||||
) public override {
|
||||
if (address(verifiers[merkleTreeDepth]) == address(0)) {
|
||||
if (merkleTreeDepth < 16 || merkleTreeDepth > 32) {
|
||||
revert Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
}
|
||||
|
||||
_createGroup(entityId, merkleTreeDepth, 0);
|
||||
_createGroup(entityId, merkleTreeDepth);
|
||||
|
||||
entities[editor] = entityId;
|
||||
entities[entityId] = editor;
|
||||
|
||||
emit EntityCreated(entityId, editor);
|
||||
}
|
||||
@@ -72,17 +66,15 @@ contract SemaphoreWhistleblowing is ISemaphoreWhistleblowing, SemaphoreCore, Sem
|
||||
|
||||
/// @dev See {ISemaphoreWhistleblowing-publishLeak}.
|
||||
function publishLeak(
|
||||
bytes32 leak,
|
||||
uint256 leak,
|
||||
uint256 nullifierHash,
|
||||
uint256 entityId,
|
||||
uint256[8] calldata proof
|
||||
) public override onlyEditor(entityId) {
|
||||
) public override {
|
||||
uint256 merkleTreeDepth = getMerkleTreeDepth(entityId);
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(entityId);
|
||||
|
||||
IVerifier verifier = verifiers[merkleTreeDepth];
|
||||
|
||||
_verifyProof(leak, merkleTreeRoot, nullifierHash, entityId, proof, verifier);
|
||||
verifier.verifyProof(merkleTreeRoot, nullifierHash, leak, entityId, proof, merkleTreeDepth);
|
||||
|
||||
emit LeakPublished(entityId, leak);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
/// @title Semaphore interface.
|
||||
/// @dev Interface of a Semaphore contract.
|
||||
/// @title Semaphore contract interface.
|
||||
interface ISemaphore {
|
||||
error Semaphore__CallerIsNotTheGroupAdmin();
|
||||
error Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
@@ -10,15 +9,10 @@ interface ISemaphore {
|
||||
error Semaphore__MerkleTreeRootIsNotPartOfTheGroup();
|
||||
error Semaphore__YouAreUsingTheSameNillifierTwice();
|
||||
|
||||
struct Verifier {
|
||||
address contractAddress;
|
||||
uint256 merkleTreeDepth;
|
||||
}
|
||||
|
||||
/// It defines all the group parameters, in addition to those in the Merkle tree.
|
||||
struct Group {
|
||||
address admin;
|
||||
uint256 merkleRootDuration;
|
||||
uint256 merkleTreeDuration;
|
||||
mapping(uint256 => uint256) merkleRootCreationDates;
|
||||
mapping(uint256 => bool) nullifierHashes;
|
||||
}
|
||||
@@ -29,6 +23,16 @@ interface ISemaphore {
|
||||
/// @param newAdmin: New admin of the group.
|
||||
event GroupAdminUpdated(uint256 indexed groupId, address indexed oldAdmin, address indexed newAdmin);
|
||||
|
||||
/// @dev Emitted when the Merkle tree duration of a group is updated.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param oldMerkleTreeDuration: Old Merkle tree duration of the group.
|
||||
/// @param newMerkleTreeDuration: New Merkle tree duration of the group.
|
||||
event GroupMerkleTreeDurationUpdated(
|
||||
uint256 indexed groupId,
|
||||
uint256 oldMerkleTreeDuration,
|
||||
uint256 newMerkleTreeDuration
|
||||
);
|
||||
|
||||
/// @dev Emitted when a Semaphore proof is verified.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param merkleTreeRoot: Root of the Merkle tree.
|
||||
@@ -37,10 +41,10 @@ interface ISemaphore {
|
||||
/// @param signal: Semaphore signal.
|
||||
event ProofVerified(
|
||||
uint256 indexed groupId,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 externalNullifier,
|
||||
uint256 indexed merkleTreeRoot,
|
||||
uint256 indexed externalNullifier,
|
||||
uint256 nullifierHash,
|
||||
bytes32 signal
|
||||
uint256 signal
|
||||
);
|
||||
|
||||
/// @dev Saves the nullifier hash to avoid double signaling and emits an event
|
||||
@@ -54,7 +58,7 @@ interface ISemaphore {
|
||||
function verifyProof(
|
||||
uint256 groupId,
|
||||
uint256 merkleTreeRoot,
|
||||
bytes32 signal,
|
||||
uint256 signal,
|
||||
uint256 nullifierHash,
|
||||
uint256 externalNullifier,
|
||||
uint256[8] calldata proof
|
||||
@@ -63,25 +67,21 @@ interface ISemaphore {
|
||||
/// @dev Creates a new group. Only the admin will be able to add or remove members.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param depth: Depth of the tree.
|
||||
/// @param zeroValue: Zero value of the tree.
|
||||
/// @param admin: Admin of the group.
|
||||
function createGroup(
|
||||
uint256 groupId,
|
||||
uint256 depth,
|
||||
uint256 zeroValue,
|
||||
address admin
|
||||
) external;
|
||||
|
||||
/// @dev Creates a new group. Only the admin will be able to add or remove members.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param depth: Depth of the tree.
|
||||
/// @param zeroValue: Zero value of the tree.
|
||||
/// @param admin: Admin of the group.
|
||||
/// @param merkleTreeRootDuration: Time before the validity of a root expires.
|
||||
function createGroup(
|
||||
uint256 groupId,
|
||||
uint256 depth,
|
||||
uint256 zeroValue,
|
||||
address admin,
|
||||
uint256 merkleTreeRootDuration
|
||||
) external;
|
||||
@@ -91,6 +91,11 @@ interface ISemaphore {
|
||||
/// @param newAdmin: New admin of the group.
|
||||
function updateGroupAdmin(uint256 groupId, address newAdmin) external;
|
||||
|
||||
/// @dev Updates the group Merkle tree duration.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param newMerkleTreeDuration: New Merkle tree duration.
|
||||
function updateGroupMerkleTreeDuration(uint256 groupId, uint256 newMerkleTreeDuration) external;
|
||||
|
||||
/// @dev Adds a new member to an existing group.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param identityCommitment: New identity commitment.
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
/// @title SemaphoreCore interface.
|
||||
/// @dev Interface of SemaphoreCore contract.
|
||||
interface ISemaphoreCore {
|
||||
/// @notice Emitted when a proof is verified correctly and a new nullifier hash is added.
|
||||
/// @param nullifierHash: Hash of external and identity nullifiers.
|
||||
event NullifierHashAdded(uint256 nullifierHash);
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
/// @title SemaphoreGroups interface.
|
||||
/// @dev Interface of a SemaphoreGroups contract.
|
||||
/// @title SemaphoreGroups contract interface.
|
||||
interface ISemaphoreGroups {
|
||||
error Semaphore__GroupDoesNotExist();
|
||||
error Semaphore__GroupAlreadyExists();
|
||||
error Semaphore__GroupIdIsNotLessThanSnarkScalarField();
|
||||
|
||||
/// @dev Emitted when a new group is created.
|
||||
/// @param groupId: Id of the group.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
/// @title SemaphoreNullifiers interface.
|
||||
/// @dev Interface of SemaphoreNullifiers contract.
|
||||
interface ISemaphoreNullifiers {
|
||||
/// @dev Emitted when a external nullifier is added.
|
||||
/// @param externalNullifier: External Semaphore nullifier.
|
||||
event ExternalNullifierAdded(uint256 externalNullifier);
|
||||
|
||||
/// @dev Emitted when a external nullifier is removed.
|
||||
/// @param externalNullifier: External Semaphore nullifier.
|
||||
event ExternalNullifierRemoved(uint256 externalNullifier);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
import "../base/Pairing.sol";
|
||||
|
||||
/// @title SemaphoreVerifier contract interface.
|
||||
interface ISemaphoreVerifier {
|
||||
struct VerificationKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
/// @dev Verifies whether a Semaphore proof is valid.
|
||||
/// @param merkleTreeRoot: Root of the Merkle tree.
|
||||
/// @param nullifierHash: Nullifier hash.
|
||||
/// @param signal: Semaphore signal.
|
||||
/// @param externalNullifier: External nullifier.
|
||||
/// @param proof: Zero-knowledge proof.
|
||||
/// @param merkleTreeDepth: Depth of the tree.
|
||||
function verifyProof(
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 nullifierHash,
|
||||
uint256 signal,
|
||||
uint256 externalNullifier,
|
||||
uint256[8] calldata proof,
|
||||
uint256 merkleTreeDepth
|
||||
) external view;
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
/// @title SemaphoreVoting interface.
|
||||
/// @dev Interface of SemaphoreVoting contract.
|
||||
/// @title SemaphoreVoting contract interface.
|
||||
interface ISemaphoreVoting {
|
||||
error Semaphore__CallerIsNotThePollCoordinator();
|
||||
error Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
@@ -24,6 +23,7 @@ interface ISemaphoreVoting {
|
||||
struct Poll {
|
||||
address coordinator;
|
||||
PollState state;
|
||||
mapping(uint256 => bool) nullifierHashes;
|
||||
}
|
||||
|
||||
/// @dev Emitted when a new poll is created.
|
||||
@@ -40,7 +40,7 @@ interface ISemaphoreVoting {
|
||||
/// @dev Emitted when a user votes on a poll.
|
||||
/// @param pollId: Id of the poll.
|
||||
/// @param vote: User encrypted vote.
|
||||
event VoteAdded(uint256 indexed pollId, bytes32 vote);
|
||||
event VoteAdded(uint256 indexed pollId, uint256 vote);
|
||||
|
||||
/// @dev Emitted when a poll is ended.
|
||||
/// @param pollId: Id of the poll.
|
||||
@@ -74,7 +74,7 @@ interface ISemaphoreVoting {
|
||||
/// @param pollId: Id of the poll.
|
||||
/// @param proof: Private zk-proof parameters.
|
||||
function castVote(
|
||||
bytes32 vote,
|
||||
uint256 vote,
|
||||
uint256 nullifierHash,
|
||||
uint256 pollId,
|
||||
uint256[8] calldata proof
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
/// @title SemaphoreWhistleblowing interface.
|
||||
/// @dev Interface of SemaphoreWhistleblowing contract.
|
||||
/// @title SemaphoreWhistleblowing contract interface.
|
||||
interface ISemaphoreWhistleblowing {
|
||||
error Semaphore__CallerIsNotTheEditor();
|
||||
error Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
@@ -20,7 +19,7 @@ interface ISemaphoreWhistleblowing {
|
||||
/// @dev Emitted when a whistleblower publish a new leak.
|
||||
/// @param entityId: Id of the entity.
|
||||
/// @param leak: News leak.
|
||||
event LeakPublished(uint256 indexed entityId, bytes32 leak);
|
||||
event LeakPublished(uint256 indexed entityId, uint256 leak);
|
||||
|
||||
/// @dev Creates an entity and the associated Merkle tree/group.
|
||||
/// @param entityId: Id of the entity.
|
||||
@@ -55,7 +54,7 @@ interface ISemaphoreWhistleblowing {
|
||||
/// @param entityId: Id of the entity.
|
||||
/// @param proof: Private zk-proof parameters.
|
||||
function publishLeak(
|
||||
bytes32 leak,
|
||||
uint256 leak,
|
||||
uint256 nullifierHash,
|
||||
uint256 entityId,
|
||||
uint256[8] calldata proof
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
/// @title Verifier interface.
|
||||
/// @dev Interface of Verifier contract.
|
||||
interface IVerifier {
|
||||
function verifyProof(
|
||||
uint256[2] memory a,
|
||||
uint256[2][2] memory b,
|
||||
uint256[2] memory c,
|
||||
uint256[4] memory input
|
||||
) external view;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/contracts",
|
||||
"version": "2.6.1",
|
||||
"version": "3.2.0",
|
||||
"description": "Semaphore contracts to manage groups and broadcast anonymous signals.",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
@@ -30,6 +30,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "4.7.3",
|
||||
"@zk-kit/incremental-merkle-tree.sol": "1.3.1"
|
||||
"@zk-kit/incremental-merkle-tree.sol": "1.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier16 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[13406811599156507528361773763681356312643537981039994686313383243831956396116, 16243966861079634958125511652590761846958471358623040426599000904006426210032],
|
||||
[11781596534582143578120404722739278517564025497573071755253972265891888117374, 15688083679237922164673518758181461582601853873216319711156397437601833996222]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
1964404930528116823793003656764176108669615750422202377358993070935069307720,
|
||||
2137714996673694828207437580381836490878070731768805974506391024595988817424
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
19568893707760843340848992184233194433177372925415116053368211122719346671126,
|
||||
11639469568629189918046964192305250472192697612201524135560178632824282818614
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
5317268879687484957437879782519918549127939892210247573193613900261494313825,
|
||||
528174394975085006443543773707702838726735933116136102590448357278717993744
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
14865918005176722116473730206622066845866539143554731094374354951675249722731,
|
||||
3197770568483953664363740385883457803041685902965668289308665954510373380344
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
6863358721495494421022713667808247652425178970453300712435830652679038918987,
|
||||
15025816433373311798308762709072064417001390853103872064614174594927359131281
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier17 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[15629200772768268814959330350023920183087521275477047626405113853190187031523, 13589689305661231568162336263197960570915890299814486885851912452076929115480],
|
||||
[11464919285924930973853174493551975632739604254498590354200272115844983493029, 16004221700357242255845535848024178544616388017965468694776181247983831995562]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
17789438292552571310739605737896030466581277887660997531707911256058650850910,
|
||||
4112657509505371631825493224748310061184972897405589115208158208294581472016
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
3322052920119834475842380240689494113984887785733316517680891208549118967155,
|
||||
381029395779795399840019487059126246243641886087320875571067736504031557148
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
8777645223617381095463415690983421308854368583891690388850387317049320450400,
|
||||
11923582117369144413749726090967341613266070909169947059497952692052020331958
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
15493263571528401950994933073246603557158047091963487223668240334879173885581,
|
||||
6315532173951617115856055775098532808695228294437279844344466163873167020700
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
3481637421055377106140197938175958155334313900824697193932986771017625492245,
|
||||
20088416136090515091300914661950097694450984520235647990572441134215240947932
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier18 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[9218320951536642499143228327011901814587826948504871816273184688188019956292, 19717684456458906358368865507225121991585492363133107109865920739019288468011],
|
||||
[16717590750910963405756115910371408378114896008824240863060392362901176601412, 18221695645112467945186983098720611586049108689347006136423489099202471884089]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
4691595252082380256698158158199364410440273386659834000993210659508747323919,
|
||||
9205801980459323513061837717352821162780471027241700646145937351740096374660
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
16150531426263112884093068164597994126623437929929609532055221646496813246000,
|
||||
20245743178241899668170758952526381872637304119026868520579207157118516761827
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
6063536446992770713985314309889717594240410784717230886576072989709763902848,
|
||||
18258781411255795973918859665416013869184055573057512603788635470145328981347
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
10109932964756104512054045207253535333686585863745296080906925765480296575285,
|
||||
4174640428253153601540284363759502713687021920150940723252842152556151210349
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
18049428534741480832385046397049175120355008065781483226058177421025493210952,
|
||||
591730261265040164434889324846001338201068482543108348317417391345612814922
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier19 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[3995128789564535587814512245259203300137618476815456454931286633947953135662, 15953239752392927777442331623182226063776310198012173504208557434319753428770],
|
||||
[20957319343912866335583737646657534123362052690050674068142580221965936605075, 2523786679709693946058523307330825034772478122295850507521258983130425334580]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
9877211178693075145402462781884120278654771727348087433632224794894486095150,
|
||||
19972682062587174829535281061580296764150591339640180868104711395548066529340
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
6324578424031095537345184040149690238371517387586958921377481904541316423724,
|
||||
15513931720576048544404512239839508014664224085062729779520992909505663748296
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
11371337652479737143800707796204655130812036287859296372695832558127430723628,
|
||||
11757275188600040111649009832378343123994225623498773406233261322165903848967
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
13282496583564708104981015168203451877588903263486398132954741568835583461335,
|
||||
1746144324840370907926720490289700342734912534857331743685374514401176014195
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
7993952462467372951144011615584426050192046712674662254138390197508963352374,
|
||||
5156942148925224345709309361345680948125600198010285179548841917923439945819
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier20 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[18976133691706015337908381757202123182841901611067930614519324084182946094218, 1382518990777992893805140303684642328066746531257780279226677247567004248173],
|
||||
[6627710380771660558660627878547223719795356903257079198333641681330388499309, 21806956747910197517744499423107239699428979652113081469385876768212706694581]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
19918517214839406678907482305035208173510172567546071380302965459737278553528,
|
||||
7151186077716310064777520690144511885696297127165278362082219441732663131220
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
690581125971423619528508316402701520070153774868732534279095503611995849608,
|
||||
21271996888576045810415843612869789314680408477068973024786458305950370465558
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
16461282535702132833442937829027913110152135149151199860671943445720775371319,
|
||||
2814052162479976678403678512565563275428791320557060777323643795017729081887
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
4319780315499060392574138782191013129592543766464046592208884866569377437627,
|
||||
13920930439395002698339449999482247728129484070642079851312682993555105218086
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
3554830803181375418665292545416227334138838284686406179598687755626325482686,
|
||||
5951609174746846070367113593675211691311013364421437923470787371738135276998
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier21 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[3811592683283527904145155808200366192489850711742363953668998371801696238057, 9032545080831535702239063467087720597970266046938395860207839433937324718536],
|
||||
[16308433125974933290258540904373317426123214107276055539769464205982500660715, 12429982191499850873612518410809641163252887523090441166572590809691267943605]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
9494885690931955877467315318223108618392113101843890678090902614660136056680,
|
||||
11783514256715757384821021009301806722951917744219075907912683963173706887379
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
7562082660623781416745328104576133910743071878837764423695105915778139873834,
|
||||
17954307004260053757579194018551114133664721761483240877658498973152950708099
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
19338184851116432029108109461622579541195083625346674255186169347975445785058,
|
||||
38361206266360048012365562393026952048730052530888439195454086987795985927
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
21178537742782571863590222710872928190886000600239072595684369348717288330049,
|
||||
9786438258541172244884631831247223050494423968411444302812755467521949734320
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
11330504221972341797183339350494223413034293674225690456356444509688810101433,
|
||||
1490009915387901405464437253469086864085891770312035292355706249426866485365
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier22 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[9485639152672984144988597737758037391807993615552051606205480347442429414340, 17626503110323089701269363177710295379967225765713250625279671011873619640598],
|
||||
[12391874700409435648975069978280047983726144854114915177376036190441913967689, 18953587685067712486092665232725058638563458484886448540567142557894080640927]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
21791720972262589799021600767292883644106575897307484548888696814333235336885,
|
||||
11092962469758788187888592619035811117815082357439060720677582048880121542623
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
9418924955930663972575130074928583215922927562059194231976193350658171304436,
|
||||
16113558481826020406162261319744796072664750077095575593106901121115073101408
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
20054934960262983176880675919444457578562219675808407582143519621873973120773,
|
||||
14877415271301547911435683263206245199959943680225555496786470669330176961657
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
4215199263810110748751715719957184804379752373072771007598572158043965517488,
|
||||
5225943468606602818132879686778547605180105897615251160509064537462109826521
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
6250242626034734280813142093008675407723196706248829741247204621913994561803,
|
||||
1472231555266678689888727724824566171966416459791722465278225775922487343641
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier23 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[9830856103389248449121962275587399130605902703453384856543071762984116567573, 11408965575174993375815840422438995549652812400401163392501956884932167624437],
|
||||
[11814906841949499037550820576929552248172160643991870665022770052632331265834, 19969543376625663966419118899515353499678204573709836615846115182224340858492]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
3047486363455933831148688762823238723024952519326207356549121929667745957778,
|
||||
20241836359289449005887237560564358543646542598344362915541027571505243817211
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
5965631918800530319167124148627450454569264331058008407732200168631989208657,
|
||||
20463557477532480934514091877628554948892025887087712764683631108388998871350
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
16605042322692983282732511249912403956057999815658038166796858627082222971215,
|
||||
12219061498275616585164456833410962809536084885494309093787669879221959361956
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
1548998572074037722622224303222294716243074837074272552644853986075252666508,
|
||||
10393312002885367652301897874262367916506364670364584602554176742602334134772
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
16180907689593358346406392015123900260925622357393826746385511046141256905390,
|
||||
12267326749885120640972074479210537480053065569337817484467225562817467244765
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier24 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[15035335306919942325459417688135340085377315274625768597233474641923619728582, 10090041889587324002759549286390619541526396451963494627957072069124011137562],
|
||||
[21342049717074059749518233491526445388158772701642182532370641230478027030319, 10507786999799841055999967456762679569286329319056926475375760604262707147294]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
19590996174696909242575628014943555633938195923520472786993379268302478708283,
|
||||
2673753072556442230312995111304911178679525806396134504594492458566941824354
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
13411253172375451489380472831999887223592471057462692619008484995624281735092,
|
||||
17181767455563581254432161119660408482332423481128600038352147258951772423229
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
19138864631164378176055647711995352935065134904103255748190268290992108588628,
|
||||
14282526277736365863821375748687709839392307698935143595732632710176778519757
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
20183773658676161990469276414858234178608794783112866811307579993999118293429,
|
||||
5223464433544489066271184294750886227362580875255044558831927430970236355539
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
12333466991139269670298178539679773509487545471126920233507132846828588847444,
|
||||
3787586478923104354547687861486563468235879611952775292288436085429794222238
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier25 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[15718373132479769904443326381037437528372212185108294117696143473979328398658, 43456740675249348549891878341522275183186932745162972528932808393415299552],
|
||||
[11236864934894600819960883124570686936554376109344998527334431594565774237827, 4289247401578837038775845192875793775418122783738936298355403103074020081838]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
18580370382199518848261939652153768394883698461842792002922164533882262019935,
|
||||
20516185953882700254387267244708111605796661864845495645678049276372075842359
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
20041291712709610738573661974551517833120775539593003477018637287434210072702,
|
||||
6326630253906616820412999166182553773360987412889775567442543181359104720511
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
13268971611130152315428629919012388924225656285593904211561391821918930327614,
|
||||
9247437189452353488017802041158840512956111558640958728149597697508914590433
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
6267384495557139339708615182113725421733376438932580472141549274050146739549,
|
||||
1832264154031452148715318442722960696977572389206897240030908464579133134237
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
16650684165487873559901140599157559153018449083939294496255590830891994564285,
|
||||
14140282729498011406186082176268025578697081678243955538935501306868500498994
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier26 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[1723458149089715907994189658689343304709709060535625667210252753337752162173, 4023016874169005249382064394379671330447496454371261692205411970999350949293],
|
||||
[7651670126664625790835334090273463062538865895183205964669372719235003083565, 17710652158212212080502343565075513548898593397103675832636832371532093744857]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
4247947150009812467217672970806328247513830308400387953244764907353849211641,
|
||||
14500381439127180474801393438175928191199696177607750163263715436006533630877
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
21213779524495874664157797605662894019112036728653622806607467354233012380232,
|
||||
1429370857470083395421401524518861545167550347090873730934256398864585069083
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
12465277751642747637430517396067173985821959773399832969105187923427872239200,
|
||||
4377704428607835904642653580543541241155601291484645500691968624389522190030
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
11283027832501128633761619552392013253304972822086786857121687098087331014745,
|
||||
21463394238922953607096052056881931791797740737164052798044623278557203313720
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
19687293493101130967741578773742597470558958652351513582962108464055656171331,
|
||||
4445165696525061401582979300506082669540223774145877762689724631935313716632
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier27 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[745924679191739894055143748466112994378439645681039136007774787076115375124, 13132169670125192016391258838554965176628317453468870968867717287446623320643],
|
||||
[2126777833939378028304266129616145667925849332481755567268747182629795296580, 20909608709868730010029182074820840312550443752829480953667886902663547957991]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
3388767735894417381503201756905214431625081913405504580464345986403824999889,
|
||||
21014112837214011009096825602791072748195337199912773858499588477762724153070
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
10521317016331497094903116740581271122844131442882845700567581775404872949272,
|
||||
13201921794561774338466680421903602920184688290946713194187958007088351657367
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
16170260722059932609965743383032703380650557609693540121262881902248073364496,
|
||||
6004983491336500911294872035126141746032033211872472427212274143945425740617
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
10275615677574391293596971122111363003313434841806630200532546038183081960924,
|
||||
5955568702561336410725734958627459212680756023420452791680213386065159525989
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
19059081014385850734732058652137664919364805650872154944590269874395511868415,
|
||||
19202365837673729366500417038229950532560250566916189579621883380623278182155
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier28 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[4553625243522856553165922942982108474187282402890756796515747778282922584601, 16835654219229187428071649241190746119082269636345872682107941472241044260584],
|
||||
[3272293478534046729728233267765357195255129499603632413158978822084188871854, 873742823867191038535544062852920538566418819521732785500614249239215175476]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
7856986171681248404396064225772749784181602218562773063185003409958949630985,
|
||||
11707218736744382138692483591389641607570557654489363179025201039696228471230
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
2902255937308264958973169948617099471543255757887963647238093192858290079050,
|
||||
4092153880227661899721872164083575597602963673456107552146583620177664115673
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
18380478859138320895837407377103009470968863533040661874531861881638854174636,
|
||||
14502773952184441371657781525836310753176308880224816843041318743809785835984
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
2781117248053224106149213822307598926495461873135153638774638501111353469325,
|
||||
3500056595279027698683405880585654897391289317486204483344715855049598477604
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
8880120765926282932795149634761705738498809569874317407549203808931092257005,
|
||||
19080036326648068547894941015038877788526324720587349784852594495705578761000
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier29 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[7252337675475138150830402909353772156046809729627064992143762325769537840623, 7601443214415704135008588588192028557655441716696726549510699770097979655628],
|
||||
[436607343827794507835462908831699962173244647704538949914686722631806931932, 18500126298578278987997086114400065402270866280547473913420536595663876273004]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
18427701611614193839908361166447988195308352665132182219164437649866377475111,
|
||||
5299493942596042045861137432338955179078182570752746487573709678936617478454
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
4188155714164125069834512529839479682516489319499446390214266838952761728656,
|
||||
2720966082507704094346897998659841489771837229143573083003847010258396944787
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
13256461570028177373135283778770729308216900804505379897951455548375840027026,
|
||||
10722074030307391322177899534114921764931623271723882054692012663305322382747
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
9824147497244652955949696442395586567974424828238608972020527958186701134273,
|
||||
15755269950882650791869946186461432242513999576056199368058858215068920022191
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
21172488506061181949536573476893375313339715931330476837156243346077173297265,
|
||||
13892434487977776248366965108031841947713544939953824768291380177301871559945
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier30 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[10202326166286888893675634318107715186834588694714750762952081034135561546271, 15028154694713144242204861571552635520290993855826554325002991692907421516918],
|
||||
[18486039841380105976272577521609866666900576498507352937328726490052296469859, 12766289885372833812620582632847872978085960777075662988932200910695848591357]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
1452272927738590248356371174422184656932731110936062990115610832462181634644,
|
||||
3608050114233210789542189629343107890943266759827387991788718454179833288695
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
14798240452388909327945424685903532333765637883272751382037716636327236955001,
|
||||
10773894897711848209682368488916121016695006898681985691467605219098835500201
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
17204267933132009093604099819536245144503489322639121825381131096467570698650,
|
||||
7704298975420304156332734115679983371345754866278811368869074990486717531131
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
8060465662017324080560848316478407038163145149983639907596180500095598669247,
|
||||
20475082166427284188002500222093571716651248980245637602667562336751029856573
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
7457566682692308112726332096733260585025339741083447785327706250123165087868,
|
||||
11904519443874922292602150685069370036383697877657723976244907400392778002614
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier31 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[14930624777162656776068112402283260602512252179767747308433194885322661150422, 13682963731073238132274278610660469286329368216526659590944079211949686450402],
|
||||
[18705481657148807016785305378773304476425591636333098330324049960258682574070, 21315724107376627085778492378001676935454590984229146391746301404292016287653]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
12628427235010608529869146871556870477182704310235373946877240509680742038961,
|
||||
15093298104438768585559335868663959710321348106117735180051519837845319121254
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
6593907467779318957599440584793099005109789224774644007604434924706249001015,
|
||||
18549596630007199540674697114946251030815675677713256327810772799104711621483
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
6271101737045248834759003849256661059806617144229427987717476992610974162336,
|
||||
355748132218964841305454070022507122319085542484477110563322753565651576458
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
2116139772133141967317791473319540620104888687412078412336248003979594158546,
|
||||
4004400204967325849492155713520296687406035356901102254880522534085890616486
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
4206647028595764233995379982714022410660284578620723510907006350595207905228,
|
||||
19380634286337609988098517090003334645113675227742745065381519159322795845003
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// 2019 OKIMS
|
||||
// ported to solidity 0.6
|
||||
// fixed linter warnings
|
||||
// added requiere error messages
|
||||
//
|
||||
// 2021 Remco Bloemen
|
||||
// cleaned up code
|
||||
// added InvalidProve() error
|
||||
// always revert with InvalidProof() on invalid proof
|
||||
// make Pairing strict
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime moludus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() internal pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() internal pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) revert InvalidProof();
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitelly.
|
||||
if (s >= SCALAR_MODULUS) revert InvalidProof();
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
if (!success) revert InvalidProof();
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) internal view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) revert InvalidProof();
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
if (!success || out[0] != 1) revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier32 {
|
||||
using Pairing for *;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(
|
||||
20491192805390485299153009773594534940189261866228447918068658471970481763042,
|
||||
9383485363053290200918347156157836566562967994039712273449902621266178545958
|
||||
);
|
||||
|
||||
vk.beta2 = Pairing.G2Point(
|
||||
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
|
||||
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
|
||||
);
|
||||
|
||||
vk.gamma2 = Pairing.G2Point(
|
||||
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
|
||||
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
|
||||
);
|
||||
|
||||
vk.delta2 = Pairing.G2Point(
|
||||
[12315240965742683516581565369496371929586281338862761742109651525191835544242, 18994803742708336446369128568423705404354655742604689352630273180469431952708],
|
||||
[18019403342409608922812569436317484250134945386869657285229378095251425778096, 12707009780301102830224094192984906206920666691015255692741008594808694787917]
|
||||
);
|
||||
|
||||
vk.IC = new Pairing.G1Point[](5);
|
||||
|
||||
|
||||
vk.IC[0] = Pairing.G1Point(
|
||||
2592407181901686208061988776764501828311271519595797153264758207470081204331,
|
||||
11847594161160074962679125411562687287595382335410213641115001866587988494499
|
||||
);
|
||||
|
||||
vk.IC[1] = Pairing.G1Point(
|
||||
3346927026869562921166545684451290646273836362895645367665514203662899621366,
|
||||
15758185693543979820528128025093553492246135914029575732836221618882836493143
|
||||
);
|
||||
|
||||
vk.IC[2] = Pairing.G1Point(
|
||||
20528686657810499188368147206002308531447185877994439397529705707372170337045,
|
||||
18025396678079701612906003769476076600196287001844168390936182972248852818155
|
||||
);
|
||||
|
||||
vk.IC[3] = Pairing.G1Point(
|
||||
9799815250059685769827017947834627563597884023490186073806184882963949644596,
|
||||
4998495094322372762314630336611134866447406022687118703953312157819349892603
|
||||
);
|
||||
|
||||
vk.IC[4] = Pairing.G1Point(
|
||||
16176535527670849161173306151058200762642157343823553073439957507563856439772,
|
||||
21877331533292960470552563236986670222564955589137303622102707801351340670855
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// @dev Verifies a Semaphore proof. Reverts with InvalidProof if the proof is invalid.
|
||||
function verifyProof(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) public view {
|
||||
// If the values are not in the correct range, the Pairing contract will revert.
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x of inputs times IC
|
||||
if (input.length + 1 != vk.IC.length) revert Pairing.InvalidProof();
|
||||
Pairing.G1Point memory vk_x = vk.IC[0];
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[1], input[0]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[2], input[1]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[3], input[2]));
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[4], input[3]));
|
||||
|
||||
// Check pairing
|
||||
Pairing.G1Point[] memory p1 = new Pairing.G1Point[](4);
|
||||
Pairing.G2Point[] memory p2 = new Pairing.G2Point[](4);
|
||||
p1[0] = Pairing.negate(proof.A);
|
||||
p2[0] = proof.B;
|
||||
p1[1] = vk.alfa1;
|
||||
p2[1] = vk.beta2;
|
||||
p1[2] = vk_x;
|
||||
p2[2] = vk.gamma2;
|
||||
p1[3] = proof.C;
|
||||
p2[3] = vk.delta2;
|
||||
Pairing.pairingCheck(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,7 @@
|
||||
{
|
||||
"verifiers": {
|
||||
"Verifier16": "0x6143ECd9Fd1A00EDe1046d456f8aab53a7D71609",
|
||||
"Verifier17": "0xAc12fFFE354D6446eb50dd33E683B78FED73Fb02",
|
||||
"Verifier18": "0x610aeF0F2da3CD1C8bDefe4BDB434Ee146E0C701",
|
||||
"Verifier19": "0x5477725177035bbC9d70443eb921D29749D6FCb4",
|
||||
"Verifier20": "0x3fB2C0988a37b76e760c44e6516aF720935f3136",
|
||||
"Verifier21": "0xDc8f6B8A42836d4566256f4c6C53131DFD127DF8",
|
||||
"Verifier22": "0x6962b5e706be5278eeCb01c286b50A48484632f2",
|
||||
"Verifier23": "0x41e4796Bd89B4BF04013b559c93fC32E9a2BdF6B",
|
||||
"Verifier24": "0xD528B1D1408ab3583af4694F92b0aFEbE33d5b60",
|
||||
"Verifier25": "0x1683a27EF9c10c5286dB56412E1272cD0Ca733e7",
|
||||
"Verifier26": "0x78194bB665d1E33b97eE45B1A755c15717E94C00",
|
||||
"Verifier27": "0x997Dac00E6701Ef7F3518280E5a9922801126E42",
|
||||
"Verifier28": "0xDd3C7f4cBA2467aE41c0F614A3c3E24bC80268c6",
|
||||
"Verifier29": "0xe53eF12093933D5df5691EAbA3821bD1c1EB60Cd",
|
||||
"Verifier30": "0x7FeA07c536ABBB0E7FB3c833376EE4EaDc21340e",
|
||||
"Verifier31": "0xe4539a592df18936202480FBe77E47DE012F2178",
|
||||
"Verifier32": "0x98c90845A7870e215cBd7265DDC653E6c07032F4"
|
||||
},
|
||||
"Semaphore": "0x86337c87A56117f8264bbaBA70e5a522C6E8A604",
|
||||
"PoseidonT3": "0xe0c8d1e53D9Bfc9071F6564755FCFf6cC0dB61d0",
|
||||
"IncrementalBinaryTree": "0x91cD2B8573629d00BeC72EA1188d446897BD3948"
|
||||
"Pairing": "0xE3a4C2FE9f025405cA6F60f6E960B4558604A74C",
|
||||
"SemaphoreVerifier": "0xCAbeED6cB96a287000aBd834b0B79c05e6Ea4d07",
|
||||
"Poseidon": "0xe0c8d1e53D9Bfc9071F6564755FCFf6cC0dB61d0",
|
||||
"IncrementalBinaryTree": "0xcDF8efE6334c68aF283C83f2F14648da51fcfFb0",
|
||||
"Semaphore": "0x72dca3c971136bf47BACF16A141f0fcfAC925aeC"
|
||||
}
|
||||
|
||||
@@ -1,24 +1,8 @@
|
||||
{
|
||||
"verifiers": {
|
||||
"Verifier16": "0xA5253ba39381Aa99c4C2C5A4D5C2deC036d06629",
|
||||
"Verifier17": "0xe0418A5f8fBF051D6cbc41Ff29855Dd2a02201Ab",
|
||||
"Verifier18": "0x7CdB3336d7d7c55Bce0FB1508594C54521656797",
|
||||
"Verifier19": "0xbd870921d8A5398a3314C950d1fc63b8C3AB190B",
|
||||
"Verifier20": "0x2a96c5696F85e3d2aa918496806B5c5a4D93E099",
|
||||
"Verifier21": "0x5Ec7d851a52A2a25CEc528F42a7ACA8EcF4667Cd",
|
||||
"Verifier22": "0x919d3d9c05FA7411e334deA5a763354fC7B6aA5b",
|
||||
"Verifier23": "0x63917b00a6dA7865bEfdd107AfC83CC2e6BDE552",
|
||||
"Verifier24": "0xd05CAd7d940114c1419098EE3cEA0776ab510E7D",
|
||||
"Verifier25": "0x6D9862e6140D94E932d94c8BcE74a0BDD0ea5ACb",
|
||||
"Verifier26": "0x8c29e0b77e32f704F03eeCE01c041192A5EB6c77",
|
||||
"Verifier27": "0x066cC22f8CA2A8D90D7Ff77D8a10A27e629c9c4C",
|
||||
"Verifier28": "0x698F9507f504E2BD238be7da56E8D9fee60C6D15",
|
||||
"Verifier29": "0xbBfC2E201C3c3c6F50063c3Edb4746c6Fcb36346",
|
||||
"Verifier30": "0x06bcD633988c1CE7Bd134DbE2C12119b6f3E4bD1",
|
||||
"Verifier31": "0x133b69Ce47BF20C49368354914DF47519Ca6cCFE",
|
||||
"Verifier32": "0xe2978F79cb4AF62e5C990EE5c7E12fb22ee22e2D"
|
||||
},
|
||||
"Semaphore": "0x5259d32659F1806ccAfcE593ED5a89eBAb85262f",
|
||||
"Semaphore": "0x89490c95eD199D980Cdb4FF8Bac9977EDb41A7E7",
|
||||
"PoseidonT3": "0xe0A452533853310C371b50Bd91BB9DCC8961350F",
|
||||
"IncrementalBinaryTree": "0x61AE89E372492e53D941DECaaC9821649fa9B236"
|
||||
"IncrementalBinaryTree": "0x9Ed9f58CA9212Ddf0377C8C4Cd089748F9337820",
|
||||
"Pairing": "0xE9c41c912CF750D79Cf304a196d4Bc8Dfd626C86",
|
||||
"SemaphoreVerifier": "0x66e772B0B8Ee1c24E4b6aC99A3A82C77f431792E",
|
||||
"Poseidon": "0xe0A452533853310C371b50Bd91BB9DCC8961350F"
|
||||
}
|
||||
|
||||
7
packages/contracts/deployed-contracts/mumbai.json
Normal file
7
packages/contracts/deployed-contracts/mumbai.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"Pairing": "0x3d3df6CFc6BFf68d9693e097F32bF4a9903E77a5",
|
||||
"SemaphoreVerifier": "0x5f4edC58142f4395D1D536e793137A0252dA5a49",
|
||||
"Poseidon": "0x181B7f34538cE3BceC68597d4A212aB3f7881648",
|
||||
"IncrementalBinaryTree": "0x220fBdB6F996827b1Cf12f0C181E8d5e6de3a36F",
|
||||
"Semaphore": "0xF864ABa335073e01234c9a88888BfFfa965650bD"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"Pairing": "0xEFD83f827FA5B0496359D817c6CD8a5AA5D2aCe5",
|
||||
"SemaphoreVerifier": "0x3d3df6CFc6BFf68d9693e097F32bF4a9903E77a5",
|
||||
"Poseidon": "0x5f4edC58142f4395D1D536e793137A0252dA5a49",
|
||||
"IncrementalBinaryTree": "0x181B7f34538cE3BceC68597d4A212aB3f7881648",
|
||||
"Semaphore": "0x220fBdB6F996827b1Cf12f0C181E8d5e6de3a36F"
|
||||
}
|
||||
7
packages/contracts/deployed-contracts/sepolia.json
Normal file
7
packages/contracts/deployed-contracts/sepolia.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"Pairing": "0xEFD83f827FA5B0496359D817c6CD8a5AA5D2aCe5",
|
||||
"SemaphoreVerifier": "0x3d3df6CFc6BFf68d9693e097F32bF4a9903E77a5",
|
||||
"Poseidon": "0x5f4edC58142f4395D1D536e793137A0252dA5a49",
|
||||
"IncrementalBinaryTree": "0x181B7f34538cE3BceC68597d4A212aB3f7881648",
|
||||
"Semaphore": "0x220fBdB6F996827b1Cf12f0C181E8d5e6de3a36F"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import "@nomicfoundation/hardhat-chai-matchers"
|
||||
import "@nomiclabs/hardhat-ethers"
|
||||
import "@nomiclabs/hardhat-etherscan"
|
||||
import "@nomiclabs/hardhat-waffle"
|
||||
import "@typechain/hardhat"
|
||||
import { config as dotenvConfig } from "dotenv"
|
||||
import "hardhat-gas-reporter"
|
||||
@@ -13,7 +13,6 @@ import "./tasks/accounts"
|
||||
import "./tasks/deploy-semaphore"
|
||||
import "./tasks/deploy-semaphore-voting"
|
||||
import "./tasks/deploy-semaphore-whistleblowing"
|
||||
import "./tasks/deploy-verifier"
|
||||
|
||||
dotenvConfig({ path: resolve(__dirname, "../../.env") })
|
||||
|
||||
@@ -31,8 +30,23 @@ function getNetworks(): NetworksUserConfig {
|
||||
chainId: 5,
|
||||
accounts
|
||||
},
|
||||
sepolia: {
|
||||
url: `https://sepolia.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 11155111,
|
||||
accounts
|
||||
},
|
||||
mumbai: {
|
||||
url: `https://polygon-mumbai.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 80001,
|
||||
accounts
|
||||
},
|
||||
"optimism-goerli": {
|
||||
url: `https://optimism-goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 420,
|
||||
accounts
|
||||
},
|
||||
arbitrum: {
|
||||
url: "https://arb1.arbitrum.io/rpc",
|
||||
url: `https://arbitrum-mainnet.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 42161,
|
||||
accounts
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
"scripts": {
|
||||
"start": "hardhat node",
|
||||
"compile": "hardhat compile",
|
||||
"deploy:verifier": "hardhat deploy:verifier",
|
||||
"deploy:verifiers": "hardhat run scripts/deploy-verifiers.ts",
|
||||
"deploy:semaphore": "hardhat deploy:semaphore",
|
||||
"deploy:semaphore-voting": "hardhat deploy:semaphore-voting",
|
||||
"deploy:semaphore-whistleblowing": "hardhat deploy:semaphore-whistleblowing",
|
||||
@@ -17,9 +15,9 @@
|
||||
"lint": "solhint 'contracts/**/*.sol'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^1.0.5",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.6",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.1.0",
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.3",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.1.7",
|
||||
"@semaphore-protocol/group": "workspace:packages/group",
|
||||
"@semaphore-protocol/identity": "workspace:packages/identity",
|
||||
"@semaphore-protocol/proof": "workspace:packages/proof",
|
||||
@@ -34,7 +32,6 @@
|
||||
"circomlib": "^2.0.2",
|
||||
"circomlibjs": "^0.0.8",
|
||||
"download": "^8.0.0",
|
||||
"ethereum-waffle": "^3.4.4",
|
||||
"ethers": "^5.6.8",
|
||||
"hardhat": "^2.9.7",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
@@ -66,6 +63,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "4.7.3",
|
||||
"@zk-kit/incremental-merkle-tree.sol": "1.3.1"
|
||||
"@zk-kit/incremental-merkle-tree.sol": "1.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { hardhatArguments, run } from "hardhat"
|
||||
import { saveDeployedContracts } from "./utils"
|
||||
|
||||
async function main() {
|
||||
const verifiers: Record<string, string> = {}
|
||||
|
||||
// Deploy verifiers.
|
||||
for (let treeDepth = 16; treeDepth <= 32; treeDepth += 1) {
|
||||
const { address } = await run("deploy:verifier", { depth: treeDepth })
|
||||
|
||||
verifiers[`Verifier${treeDepth}`] = address
|
||||
}
|
||||
|
||||
saveDeployedContracts(hardhatArguments.network, { verifiers })
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -1,34 +1,21 @@
|
||||
import { readFileSync, writeFileSync } from "fs"
|
||||
|
||||
export function saveDeployedContracts(network: string | undefined, deployedContracts: any) {
|
||||
if (network !== "goerli" && network !== "arbitrum") {
|
||||
return
|
||||
}
|
||||
|
||||
writeFileSync(`./deployed-contracts/${network}.json`, JSON.stringify(deployedContracts, null, 4))
|
||||
type DeployedContracts = {
|
||||
Pairing: string
|
||||
SemaphoreVerifier: string
|
||||
Poseidon: string
|
||||
IncrementalBinaryTree: string
|
||||
Semaphore: string
|
||||
}
|
||||
|
||||
export function getDeployedContracts(network: string | undefined): any {
|
||||
if (network !== "goerli" && network !== "arbitrum") {
|
||||
export function getDeployedContracts(network: string | undefined): DeployedContracts | null {
|
||||
try {
|
||||
return JSON.parse(readFileSync(`./deployed-contracts/${network}.json`, "utf8"))
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
|
||||
return JSON.parse(readFileSync(`./deployed-contracts/${network}.json`, "utf8"))
|
||||
}
|
||||
|
||||
export function verifiersToSolidityArgument(deployedContracts: any): any {
|
||||
const verifiers = []
|
||||
|
||||
if (deployedContracts && deployedContracts.verifiers) {
|
||||
for (const verifier in deployedContracts.verifiers) {
|
||||
if (Object.prototype.hasOwnProperty.call(deployedContracts.verifiers, verifier)) {
|
||||
verifiers.push({
|
||||
merkleTreeDepth: verifier.substring(8),
|
||||
contractAddress: deployedContracts.verifiers[verifier]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return verifiers
|
||||
export function saveDeployedContracts(network: string | undefined, deployedContracts: DeployedContracts) {
|
||||
writeFileSync(`./deployed-contracts/${network}.json`, JSON.stringify(deployedContracts, null, 4))
|
||||
}
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
import { hardhatArguments, run } from "hardhat"
|
||||
import { getDeployedContracts, verifiersToSolidityArgument } from "./utils"
|
||||
import { getDeployedContracts } from "./utils"
|
||||
|
||||
async function verify(address: string, constructorArguments?: any[]): Promise<void> {
|
||||
try {
|
||||
await run("verify:verify", {
|
||||
address,
|
||||
constructorArguments
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const deployedContracts = getDeployedContracts(hardhatArguments.network)
|
||||
|
||||
if (deployedContracts) {
|
||||
await run("verify:verify", {
|
||||
address: deployedContracts.Semaphore,
|
||||
constructorArguments: [verifiersToSolidityArgument(deployedContracts)]
|
||||
})
|
||||
|
||||
await run("verify:verify", {
|
||||
address: deployedContracts.IncrementalBinaryTree
|
||||
})
|
||||
await verify(deployedContracts.IncrementalBinaryTree)
|
||||
await verify(deployedContracts.Pairing)
|
||||
await verify(deployedContracts.SemaphoreVerifier)
|
||||
await verify(deployedContracts.Semaphore, [deployedContracts.SemaphoreVerifier])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,51 +1,78 @@
|
||||
import { poseidon_gencontract as poseidonContract } from "circomlibjs"
|
||||
import { Contract } from "ethers"
|
||||
import { task, types } from "hardhat/config"
|
||||
|
||||
task("deploy:semaphore-voting", "Deploy a SemaphoreVoting contract")
|
||||
.addOptionalParam<boolean>("logs", "Print the logs", true, types.boolean)
|
||||
.addParam("verifiers", "Tree depths and verifier addresses", undefined, types.json)
|
||||
.setAction(async ({ logs, verifiers }, { ethers }): Promise<Contract> => {
|
||||
.setAction(async ({ logs }, { ethers }): Promise<any> => {
|
||||
const PairingFactory = await ethers.getContractFactory("Pairing")
|
||||
const pairing = await PairingFactory.deploy()
|
||||
|
||||
await pairing.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Pairing library has been deployed to: ${pairing.address}`)
|
||||
}
|
||||
|
||||
const SemaphoreVerifierFactory = await ethers.getContractFactory("SemaphoreVerifier", {
|
||||
libraries: {
|
||||
Pairing: pairing.address
|
||||
}
|
||||
})
|
||||
|
||||
const semaphoreVerifier = await SemaphoreVerifierFactory.deploy()
|
||||
|
||||
await semaphoreVerifier.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`SemaphoreVerifier contract has been deployed to: ${semaphoreVerifier.address}`)
|
||||
}
|
||||
|
||||
const poseidonABI = poseidonContract.generateABI(2)
|
||||
const poseidonBytecode = poseidonContract.createCode(2)
|
||||
|
||||
const [signer] = await ethers.getSigners()
|
||||
|
||||
const PoseidonLibFactory = new ethers.ContractFactory(poseidonABI, poseidonBytecode, signer)
|
||||
const poseidonLib = await PoseidonLibFactory.deploy()
|
||||
const PoseidonFactory = new ethers.ContractFactory(poseidonABI, poseidonBytecode, signer)
|
||||
const poseidon = await PoseidonFactory.deploy()
|
||||
|
||||
await poseidonLib.deployed()
|
||||
await poseidon.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Poseidon library has been deployed to: ${poseidonLib.address}`)
|
||||
console.info(`Poseidon library has been deployed to: ${poseidon.address}`)
|
||||
}
|
||||
|
||||
const IncrementalBinaryTreeLibFactory = await ethers.getContractFactory("IncrementalBinaryTree", {
|
||||
const IncrementalBinaryTreeFactory = await ethers.getContractFactory("IncrementalBinaryTree", {
|
||||
libraries: {
|
||||
PoseidonT3: poseidonLib.address
|
||||
PoseidonT3: poseidon.address
|
||||
}
|
||||
})
|
||||
const incrementalBinaryTreeLib = await IncrementalBinaryTreeLibFactory.deploy()
|
||||
const incrementalBinaryTree = await IncrementalBinaryTreeFactory.deploy()
|
||||
|
||||
await incrementalBinaryTreeLib.deployed()
|
||||
await incrementalBinaryTree.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`IncrementalBinaryTree library has been deployed to: ${incrementalBinaryTreeLib.address}`)
|
||||
console.info(`IncrementalBinaryTree library has been deployed to: ${incrementalBinaryTree.address}`)
|
||||
}
|
||||
|
||||
const ContractFactory = await ethers.getContractFactory("SemaphoreVoting", {
|
||||
const SemaphoreVotingFactory = await ethers.getContractFactory("SemaphoreVoting", {
|
||||
libraries: {
|
||||
IncrementalBinaryTree: incrementalBinaryTreeLib.address
|
||||
IncrementalBinaryTree: incrementalBinaryTree.address
|
||||
}
|
||||
})
|
||||
|
||||
const contract = await ContractFactory.deploy(verifiers)
|
||||
const semaphoreVoting = await SemaphoreVotingFactory.deploy(semaphoreVerifier.address)
|
||||
|
||||
await contract.deployed()
|
||||
await semaphoreVoting.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`SemaphoreVoting contract has been deployed to: ${contract.address}`)
|
||||
console.info(`SemaphoreVoting contract has been deployed to: ${semaphoreVoting.address}`)
|
||||
}
|
||||
|
||||
return contract
|
||||
return {
|
||||
semaphoreVoting,
|
||||
pairingAddress: pairing.address,
|
||||
semaphoreVerifierAddress: semaphoreVerifier.address,
|
||||
poseidonAddress: poseidon.address,
|
||||
incrementalBinaryTreeAddress: incrementalBinaryTree.address
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,51 +1,78 @@
|
||||
import { poseidon_gencontract as poseidonContract } from "circomlibjs"
|
||||
import { Contract } from "ethers"
|
||||
import { task, types } from "hardhat/config"
|
||||
|
||||
task("deploy:semaphore-whistleblowing", "Deploy a SemaphoreWhistleblowing contract")
|
||||
.addOptionalParam<boolean>("logs", "Print the logs", true, types.boolean)
|
||||
.addParam("verifiers", "Verifier contract address", undefined, types.json)
|
||||
.setAction(async ({ logs, verifiers }, { ethers }): Promise<Contract> => {
|
||||
.setAction(async ({ logs }, { ethers }): Promise<any> => {
|
||||
const PairingFactory = await ethers.getContractFactory("Pairing")
|
||||
const pairing = await PairingFactory.deploy()
|
||||
|
||||
await pairing.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Pairing library has been deployed to: ${pairing.address}`)
|
||||
}
|
||||
|
||||
const SemaphoreVerifierFactory = await ethers.getContractFactory("SemaphoreVerifier", {
|
||||
libraries: {
|
||||
Pairing: pairing.address
|
||||
}
|
||||
})
|
||||
|
||||
const semaphoreVerifier = await SemaphoreVerifierFactory.deploy()
|
||||
|
||||
await semaphoreVerifier.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`SemaphoreVerifier contract has been deployed to: ${semaphoreVerifier.address}`)
|
||||
}
|
||||
|
||||
const poseidonABI = poseidonContract.generateABI(2)
|
||||
const poseidonBytecode = poseidonContract.createCode(2)
|
||||
|
||||
const [signer] = await ethers.getSigners()
|
||||
|
||||
const PoseidonLibFactory = new ethers.ContractFactory(poseidonABI, poseidonBytecode, signer)
|
||||
const poseidonLib = await PoseidonLibFactory.deploy()
|
||||
const PoseidonFactory = new ethers.ContractFactory(poseidonABI, poseidonBytecode, signer)
|
||||
const poseidon = await PoseidonFactory.deploy()
|
||||
|
||||
await poseidonLib.deployed()
|
||||
await poseidon.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Poseidon library has been deployed to: ${poseidonLib.address}`)
|
||||
console.info(`Poseidon library has been deployed to: ${poseidon.address}`)
|
||||
}
|
||||
|
||||
const IncrementalBinaryTreeLibFactory = await ethers.getContractFactory("IncrementalBinaryTree", {
|
||||
const IncrementalBinaryTreeFactory = await ethers.getContractFactory("IncrementalBinaryTree", {
|
||||
libraries: {
|
||||
PoseidonT3: poseidonLib.address
|
||||
PoseidonT3: poseidon.address
|
||||
}
|
||||
})
|
||||
const incrementalBinaryTreeLib = await IncrementalBinaryTreeLibFactory.deploy()
|
||||
const incrementalBinaryTree = await IncrementalBinaryTreeFactory.deploy()
|
||||
|
||||
await incrementalBinaryTreeLib.deployed()
|
||||
await incrementalBinaryTree.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`IncrementalBinaryTree library has been deployed to: ${incrementalBinaryTreeLib.address}`)
|
||||
console.info(`IncrementalBinaryTree library has been deployed to: ${incrementalBinaryTree.address}`)
|
||||
}
|
||||
|
||||
const ContractFactory = await ethers.getContractFactory("SemaphoreWhistleblowing", {
|
||||
const SemaphoreWhistleblowingFactory = await ethers.getContractFactory("SemaphoreWhistleblowing", {
|
||||
libraries: {
|
||||
IncrementalBinaryTree: incrementalBinaryTreeLib.address
|
||||
IncrementalBinaryTree: incrementalBinaryTree.address
|
||||
}
|
||||
})
|
||||
|
||||
const contract = await ContractFactory.deploy(verifiers)
|
||||
const semaphoreWhistleblowing = await SemaphoreWhistleblowingFactory.deploy(semaphoreVerifier.address)
|
||||
|
||||
await contract.deployed()
|
||||
await semaphoreWhistleblowing.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`SemaphoreWhistleblowing contract has been deployed to: ${contract.address}`)
|
||||
console.info(`SemaphoreWhistleblowing contract has been deployed to: ${semaphoreWhistleblowing.address}`)
|
||||
}
|
||||
|
||||
return contract
|
||||
return {
|
||||
semaphoreWhistleblowing,
|
||||
pairingAddress: pairing.address,
|
||||
semaphoreVerifierAddress: semaphoreVerifier.address,
|
||||
poseidonAddress: poseidon.address,
|
||||
incrementalBinaryTreeAddress: incrementalBinaryTree.address
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,67 +1,123 @@
|
||||
import { poseidon_gencontract as poseidonContract } from "circomlibjs"
|
||||
import { Contract } from "ethers"
|
||||
import { task, types } from "hardhat/config"
|
||||
import { getDeployedContracts, saveDeployedContracts, verifiersToSolidityArgument } from "../scripts/utils"
|
||||
import { saveDeployedContracts } from "../scripts/utils"
|
||||
|
||||
task("deploy:semaphore", "Deploy a Semaphore contract")
|
||||
.addOptionalParam<boolean>("pairing", "Pairing library address", undefined, types.string)
|
||||
.addOptionalParam<boolean>("semaphoreVerifier", "SemaphoreVerifier contract address", undefined, types.string)
|
||||
.addOptionalParam<boolean>("poseidon", "Poseidon library address", undefined, types.string)
|
||||
.addOptionalParam<boolean>(
|
||||
"incrementalBinaryTree",
|
||||
"IncrementalBinaryTree library address",
|
||||
undefined,
|
||||
types.string
|
||||
)
|
||||
.addOptionalParam<boolean>("logs", "Print the logs", true, types.boolean)
|
||||
.addOptionalParam("verifiers", "Tree depths and verifier addresses", [], types.json)
|
||||
.setAction(async ({ logs, verifiers }, { ethers, hardhatArguments }): Promise<Contract> => {
|
||||
let deployedContracts: any
|
||||
.setAction(
|
||||
async (
|
||||
{
|
||||
logs,
|
||||
pairing: pairingAddress,
|
||||
semaphoreVerifier: semaphoreVerifierAddress,
|
||||
poseidon: poseidonAddress,
|
||||
incrementalBinaryTree: incrementalBinaryTreeAddress
|
||||
},
|
||||
{ ethers, hardhatArguments }
|
||||
): Promise<any> => {
|
||||
if (!semaphoreVerifierAddress) {
|
||||
if (!pairingAddress) {
|
||||
const PairingFactory = await ethers.getContractFactory("Pairing")
|
||||
const pairing = await PairingFactory.deploy()
|
||||
|
||||
if (verifiers.length === 0) {
|
||||
deployedContracts = getDeployedContracts(hardhatArguments.network)
|
||||
verifiers = verifiersToSolidityArgument(deployedContracts)
|
||||
}
|
||||
await pairing.deployed()
|
||||
|
||||
const poseidonABI = poseidonContract.generateABI(2)
|
||||
const poseidonBytecode = poseidonContract.createCode(2)
|
||||
if (logs) {
|
||||
console.info(`Pairing library has been deployed to: ${pairing.address}`)
|
||||
}
|
||||
|
||||
const [signer] = await ethers.getSigners()
|
||||
pairingAddress = pairing.address
|
||||
}
|
||||
|
||||
const PoseidonLibFactory = new ethers.ContractFactory(poseidonABI, poseidonBytecode, signer)
|
||||
const poseidonLib = await PoseidonLibFactory.deploy()
|
||||
const SemaphoreVerifierFactory = await ethers.getContractFactory("SemaphoreVerifier", {
|
||||
libraries: {
|
||||
Pairing: pairingAddress
|
||||
}
|
||||
})
|
||||
|
||||
await poseidonLib.deployed()
|
||||
const semaphoreVerifier = await SemaphoreVerifierFactory.deploy()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Poseidon library has been deployed to: ${poseidonLib.address}`)
|
||||
}
|
||||
await semaphoreVerifier.deployed()
|
||||
|
||||
const IncrementalBinaryTreeLibFactory = await ethers.getContractFactory("IncrementalBinaryTree", {
|
||||
libraries: {
|
||||
PoseidonT3: poseidonLib.address
|
||||
if (logs) {
|
||||
console.info(`SemaphoreVerifier contract has been deployed to: ${semaphoreVerifier.address}`)
|
||||
}
|
||||
|
||||
semaphoreVerifierAddress = semaphoreVerifier.address
|
||||
}
|
||||
})
|
||||
const incrementalBinaryTreeLib = await IncrementalBinaryTreeLibFactory.deploy()
|
||||
|
||||
await incrementalBinaryTreeLib.deployed()
|
||||
if (!incrementalBinaryTreeAddress) {
|
||||
if (!poseidonAddress) {
|
||||
const poseidonABI = poseidonContract.generateABI(2)
|
||||
const poseidonBytecode = poseidonContract.createCode(2)
|
||||
|
||||
if (logs) {
|
||||
console.info(`IncrementalBinaryTree library has been deployed to: ${incrementalBinaryTreeLib.address}`)
|
||||
}
|
||||
const [signer] = await ethers.getSigners()
|
||||
|
||||
const ContractFactory = await ethers.getContractFactory("Semaphore", {
|
||||
libraries: {
|
||||
IncrementalBinaryTree: incrementalBinaryTreeLib.address
|
||||
const PoseidonFactory = new ethers.ContractFactory(poseidonABI, poseidonBytecode, signer)
|
||||
const poseidon = await PoseidonFactory.deploy()
|
||||
|
||||
await poseidon.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Poseidon library has been deployed to: ${poseidon.address}`)
|
||||
}
|
||||
|
||||
poseidonAddress = poseidon.address
|
||||
}
|
||||
|
||||
const IncrementalBinaryTreeFactory = await ethers.getContractFactory("IncrementalBinaryTree", {
|
||||
libraries: {
|
||||
PoseidonT3: poseidonAddress
|
||||
}
|
||||
})
|
||||
const incrementalBinaryTree = await IncrementalBinaryTreeFactory.deploy()
|
||||
|
||||
await incrementalBinaryTree.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`IncrementalBinaryTree library has been deployed to: ${incrementalBinaryTree.address}`)
|
||||
}
|
||||
|
||||
incrementalBinaryTreeAddress = incrementalBinaryTree.address
|
||||
}
|
||||
})
|
||||
|
||||
const contract = await ContractFactory.deploy(verifiers)
|
||||
const SemaphoreFactory = await ethers.getContractFactory("Semaphore", {
|
||||
libraries: {
|
||||
IncrementalBinaryTree: incrementalBinaryTreeAddress
|
||||
}
|
||||
})
|
||||
|
||||
await contract.deployed()
|
||||
const semaphore = await SemaphoreFactory.deploy(semaphoreVerifierAddress)
|
||||
|
||||
if (logs) {
|
||||
console.info(`Semaphore contract has been deployed to: ${contract.address}`)
|
||||
await semaphore.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Semaphore contract has been deployed to: ${semaphore.address}`)
|
||||
}
|
||||
|
||||
saveDeployedContracts(hardhatArguments.network, {
|
||||
Pairing: pairingAddress,
|
||||
SemaphoreVerifier: semaphoreVerifierAddress,
|
||||
Poseidon: poseidonAddress,
|
||||
IncrementalBinaryTree: incrementalBinaryTreeAddress,
|
||||
Semaphore: semaphore.address
|
||||
})
|
||||
|
||||
return {
|
||||
semaphore,
|
||||
pairingAddress,
|
||||
semaphoreVerifierAddress,
|
||||
poseidonAddress,
|
||||
incrementalBinaryTreeAddress
|
||||
}
|
||||
}
|
||||
|
||||
if (deployedContracts) {
|
||||
deployedContracts.PoseidonT3 = poseidonLib.address
|
||||
deployedContracts.IncrementalBinaryTree = incrementalBinaryTreeLib.address
|
||||
deployedContracts.Semaphore = contract.address
|
||||
|
||||
saveDeployedContracts(hardhatArguments.network, deployedContracts)
|
||||
}
|
||||
|
||||
return contract
|
||||
})
|
||||
)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Contract } from "ethers"
|
||||
import { task, types } from "hardhat/config"
|
||||
|
||||
task("deploy:verifier", "Deploy a Verifier contract")
|
||||
.addOptionalParam<number>("depth", "Tree depth", Number(process.env.TREE_DEPTH) || 20, types.int)
|
||||
.addOptionalParam<boolean>("logs", "Print the logs", true, types.boolean)
|
||||
.setAction(async ({ depth, logs }, { ethers }): Promise<Contract> => {
|
||||
const ContractFactory = await ethers.getContractFactory(`Verifier${depth}`)
|
||||
|
||||
const contract = await ContractFactory.deploy()
|
||||
|
||||
await contract.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Verifier${depth} contract has been deployed to: ${contract.address}`)
|
||||
}
|
||||
|
||||
return contract
|
||||
})
|
||||
@@ -2,87 +2,121 @@
|
||||
/* eslint-disable jest/valid-expect */
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { FullProof, generateProof, packToSolidityProof, SolidityProof } from "@semaphore-protocol/proof"
|
||||
import { FullProof, generateProof } from "@semaphore-protocol/proof"
|
||||
import { expect } from "chai"
|
||||
import { constants, Signer, utils } from "ethers"
|
||||
import { run } from "hardhat"
|
||||
import { Semaphore } from "../build/typechain"
|
||||
import { constants, Signer } from "ethers"
|
||||
import { ethers, run } from "hardhat"
|
||||
import { Pairing, Semaphore } from "../build/typechain"
|
||||
import { createIdentityCommitments } from "./utils"
|
||||
|
||||
describe("Semaphore", () => {
|
||||
let contract: Semaphore
|
||||
let semaphoreContract: Semaphore
|
||||
let pairingContract: Pairing
|
||||
let signers: Signer[]
|
||||
let accounts: string[]
|
||||
|
||||
const treeDepth = Number(process.env.TREE_DEPTH) || 20
|
||||
const groupId = 1
|
||||
const group = new Group(groupId, treeDepth)
|
||||
const members = createIdentityCommitments(3)
|
||||
|
||||
const wasmFilePath = `../../snark-artifacts/semaphore.wasm`
|
||||
const zkeyFilePath = `../../snark-artifacts/semaphore.zkey`
|
||||
const wasmFilePath = `../../snark-artifacts/${treeDepth}/semaphore.wasm`
|
||||
const zkeyFilePath = `../../snark-artifacts/${treeDepth}/semaphore.zkey`
|
||||
|
||||
before(async () => {
|
||||
const { address: verifierAddress } = await run("deploy:verifier", { logs: false, depth: treeDepth })
|
||||
contract = await run("deploy:semaphore", {
|
||||
logs: false,
|
||||
verifiers: [{ merkleTreeDepth: treeDepth, contractAddress: verifierAddress }]
|
||||
const { semaphore, pairingAddress } = await run("deploy:semaphore", {
|
||||
logs: false
|
||||
})
|
||||
|
||||
semaphoreContract = semaphore
|
||||
pairingContract = await ethers.getContractAt("Pairing", pairingAddress)
|
||||
|
||||
signers = await run("accounts", { logs: false })
|
||||
accounts = await Promise.all(signers.map((signer: Signer) => signer.getAddress()))
|
||||
})
|
||||
|
||||
describe("# createGroup", () => {
|
||||
it("Should not create a group if the tree depth is not supported", async () => {
|
||||
const transaction = contract["createGroup(uint256,uint256,uint256,address)"](groupId, 10, 0, accounts[0])
|
||||
const transaction = semaphoreContract["createGroup(uint256,uint256,address)"](groupId, 10, accounts[0])
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__MerkleTreeDepthIsNotSupported()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreContract,
|
||||
"Semaphore__MerkleTreeDepthIsNotSupported"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should create a group", async () => {
|
||||
const transaction = contract
|
||||
const transaction = semaphoreContract
|
||||
.connect(signers[1])
|
||||
["createGroup(uint256,uint256,uint256,address)"](groupId, treeDepth, 0, accounts[1])
|
||||
["createGroup(uint256,uint256,address)"](groupId, treeDepth, accounts[1])
|
||||
|
||||
await expect(transaction).to.emit(contract, "GroupCreated").withArgs(groupId, treeDepth, 0)
|
||||
await expect(transaction)
|
||||
.to.emit(contract, "GroupAdminUpdated")
|
||||
.to.emit(semaphoreContract, "GroupCreated")
|
||||
.withArgs(groupId, treeDepth, group.zeroValue)
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreContract, "GroupAdminUpdated")
|
||||
.withArgs(groupId, constants.AddressZero, accounts[1])
|
||||
})
|
||||
|
||||
it("Should create a group with a custom Merkle tree root expiration", async () => {
|
||||
const groupId = 2
|
||||
const transaction = await contract
|
||||
const group = new Group(2)
|
||||
const transaction = await semaphoreContract
|
||||
.connect(signers[1])
|
||||
["createGroup(uint256,uint256,uint256,address,uint256)"](
|
||||
["createGroup(uint256,uint256,address,uint256)"](
|
||||
groupId,
|
||||
treeDepth,
|
||||
0,
|
||||
accounts[0],
|
||||
5 // 5 seconds.
|
||||
)
|
||||
await contract.addMember(groupId, members[0])
|
||||
await contract.addMember(groupId, members[1])
|
||||
await contract.addMember(groupId, members[2])
|
||||
await semaphoreContract.addMember(groupId, members[0])
|
||||
await semaphoreContract.addMember(groupId, members[1])
|
||||
await semaphoreContract.addMember(groupId, members[2])
|
||||
|
||||
await expect(transaction).to.emit(contract, "GroupCreated").withArgs(groupId, treeDepth, 0)
|
||||
await expect(transaction)
|
||||
.to.emit(contract, "GroupAdminUpdated")
|
||||
.to.emit(semaphoreContract, "GroupCreated")
|
||||
.withArgs(groupId, treeDepth, group.zeroValue)
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreContract, "GroupAdminUpdated")
|
||||
.withArgs(groupId, constants.AddressZero, accounts[0])
|
||||
})
|
||||
})
|
||||
|
||||
describe("# updateGroupMerkleTreeDuration", () => {
|
||||
it("Should not update a group Merkle tree duration if the caller is not the group admin", async () => {
|
||||
const transaction = semaphoreContract.updateGroupMerkleTreeDuration(groupId, 300)
|
||||
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreContract,
|
||||
"Semaphore__CallerIsNotTheGroupAdmin"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should update the group Merkle tree duration", async () => {
|
||||
const transaction = semaphoreContract.connect(signers[1]).updateGroupMerkleTreeDuration(groupId, 300)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreContract, "GroupMerkleTreeDurationUpdated")
|
||||
.withArgs(groupId, 3600, 300)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# updateGroupAdmin", () => {
|
||||
it("Should not update a group admin if the caller is not the group admin", async () => {
|
||||
const transaction = contract.updateGroupAdmin(groupId, accounts[0])
|
||||
const transaction = semaphoreContract.updateGroupAdmin(groupId, accounts[0])
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotTheGroupAdmin()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreContract,
|
||||
"Semaphore__CallerIsNotTheGroupAdmin"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should update the group admin", async () => {
|
||||
const transaction = contract.connect(signers[1]).updateGroupAdmin(groupId, accounts[0])
|
||||
const transaction = semaphoreContract.connect(signers[1]).updateGroupAdmin(groupId, accounts[0])
|
||||
|
||||
await expect(transaction).to.emit(contract, "GroupAdminUpdated").withArgs(groupId, accounts[1], accounts[0])
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreContract, "GroupAdminUpdated")
|
||||
.withArgs(groupId, accounts[1], accounts[0])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -90,22 +124,24 @@ describe("Semaphore", () => {
|
||||
it("Should not add a member if the caller is not the group admin", async () => {
|
||||
const member = BigInt(2)
|
||||
|
||||
const transaction = contract.connect(signers[1]).addMember(groupId, member)
|
||||
const transaction = semaphoreContract.connect(signers[1]).addMember(groupId, member)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotTheGroupAdmin()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreContract,
|
||||
"Semaphore__CallerIsNotTheGroupAdmin"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should add a new member in an existing group", async () => {
|
||||
const transaction = contract.addMember(groupId, members[0])
|
||||
const group = new Group(groupId, treeDepth)
|
||||
|
||||
group.addMember(members[0])
|
||||
|
||||
const transaction = semaphoreContract.addMember(groupId, members[0])
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(contract, "MemberAdded")
|
||||
.withArgs(
|
||||
groupId,
|
||||
0,
|
||||
members[0],
|
||||
"18951329906296061785889394467312334959162736293275411745101070722914184798221"
|
||||
)
|
||||
.to.emit(semaphoreContract, "MemberAdded")
|
||||
.withArgs(groupId, 0, members[0], group.root)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -113,15 +149,17 @@ describe("Semaphore", () => {
|
||||
it("Should add new members to an existing group", async () => {
|
||||
const groupId = 3
|
||||
const members = [BigInt(1), BigInt(2), BigInt(3)]
|
||||
const group = new Group(treeDepth)
|
||||
const group = new Group(groupId, treeDepth)
|
||||
|
||||
group.addMembers(members)
|
||||
|
||||
await contract["createGroup(uint256,uint256,uint256,address)"](groupId, treeDepth, 0, accounts[0])
|
||||
await semaphoreContract["createGroup(uint256,uint256,address)"](groupId, treeDepth, accounts[0])
|
||||
|
||||
const transaction = contract.addMembers(groupId, members)
|
||||
const transaction = semaphoreContract.addMembers(groupId, members)
|
||||
|
||||
await expect(transaction).to.emit(contract, "MemberAdded").withArgs(groupId, 2, BigInt(3), group.root)
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreContract, "MemberAdded")
|
||||
.withArgs(groupId, 2, BigInt(3), group.root)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -129,29 +167,32 @@ describe("Semaphore", () => {
|
||||
it("Should not update a member if the caller is not the group admin", async () => {
|
||||
const member = BigInt(2)
|
||||
|
||||
const transaction = contract.connect(signers[1]).updateMember(groupId, member, 1, [0, 1], [0, 1])
|
||||
const transaction = semaphoreContract.connect(signers[1]).updateMember(groupId, member, 1, [0, 1], [0, 1])
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotTheGroupAdmin()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreContract,
|
||||
"Semaphore__CallerIsNotTheGroupAdmin"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should update a member from an existing group", async () => {
|
||||
const groupId = 4
|
||||
const members = [BigInt(1), BigInt(2), BigInt(3)]
|
||||
const group = new Group(treeDepth)
|
||||
const group = new Group(groupId, treeDepth)
|
||||
|
||||
group.addMembers(members)
|
||||
|
||||
group.updateMember(0, BigInt(4))
|
||||
|
||||
await contract["createGroup(uint256,uint256,uint256,address)"](groupId, treeDepth, 0, accounts[0])
|
||||
await contract.addMembers(groupId, members)
|
||||
await semaphoreContract["createGroup(uint256,uint256,address)"](groupId, treeDepth, accounts[0])
|
||||
await semaphoreContract.addMembers(groupId, members)
|
||||
|
||||
const { siblings, pathIndices, root } = group.generateProofOfMembership(0)
|
||||
const { siblings, pathIndices, root } = group.generateMerkleProof(0)
|
||||
|
||||
const transaction = contract.updateMember(groupId, BigInt(1), BigInt(4), siblings, pathIndices)
|
||||
const transaction = semaphoreContract.updateMember(groupId, BigInt(1), BigInt(4), siblings, pathIndices)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(contract, "MemberUpdated")
|
||||
.to.emit(semaphoreContract, "MemberUpdated")
|
||||
.withArgs(groupId, 0, BigInt(1), BigInt(4), root)
|
||||
})
|
||||
})
|
||||
@@ -160,114 +201,121 @@ describe("Semaphore", () => {
|
||||
it("Should not remove a member if the caller is not the group admin", async () => {
|
||||
const member = BigInt(2)
|
||||
|
||||
const transaction = contract.connect(signers[1]).removeMember(groupId, member, [0, 1], [0, 1])
|
||||
const transaction = semaphoreContract.connect(signers[1]).removeMember(groupId, member, [0, 1], [0, 1])
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotTheGroupAdmin()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreContract,
|
||||
"Semaphore__CallerIsNotTheGroupAdmin"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should remove a member from an existing group", async () => {
|
||||
const groupId = 5
|
||||
const members = [BigInt(1), BigInt(2), BigInt(3)]
|
||||
const group = new Group(treeDepth)
|
||||
const group = new Group(groupId, treeDepth)
|
||||
|
||||
group.addMembers(members)
|
||||
|
||||
group.removeMember(2)
|
||||
|
||||
await contract["createGroup(uint256,uint256,uint256,address)"](groupId, treeDepth, 0, accounts[0])
|
||||
await contract.addMembers(groupId, members)
|
||||
await semaphoreContract["createGroup(uint256,uint256,address)"](groupId, treeDepth, accounts[0])
|
||||
await semaphoreContract.addMembers(groupId, members)
|
||||
|
||||
const { siblings, pathIndices, root } = group.generateProofOfMembership(2)
|
||||
const { siblings, pathIndices, root } = group.generateMerkleProof(2)
|
||||
|
||||
const transaction = contract.removeMember(groupId, BigInt(3), siblings, pathIndices)
|
||||
const transaction = semaphoreContract.removeMember(groupId, BigInt(3), siblings, pathIndices)
|
||||
|
||||
await expect(transaction).to.emit(contract, "MemberRemoved").withArgs(groupId, 2, BigInt(3), root)
|
||||
await expect(transaction).to.emit(semaphoreContract, "MemberRemoved").withArgs(groupId, 2, BigInt(3), root)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# verifyProof", () => {
|
||||
const signal = utils.formatBytes32String("Hello world")
|
||||
const signal = 2
|
||||
const identity = new Identity("0")
|
||||
|
||||
const group = new Group(treeDepth)
|
||||
const group = new Group(groupId, treeDepth)
|
||||
|
||||
group.addMembers(members)
|
||||
|
||||
let fullProof: FullProof
|
||||
let solidityProof: SolidityProof
|
||||
|
||||
before(async () => {
|
||||
await contract.addMembers(groupId, [members[1], members[2]])
|
||||
await semaphoreContract.addMembers(groupId, [members[1], members[2]])
|
||||
|
||||
fullProof = await generateProof(identity, group, group.root, signal, {
|
||||
wasmFilePath,
|
||||
zkeyFilePath
|
||||
})
|
||||
solidityProof = packToSolidityProof(fullProof.proof)
|
||||
})
|
||||
|
||||
it("Should not verify a proof if the group does not exist", async () => {
|
||||
const transaction = contract.verifyProof(10, 1, signal, 0, 0, [0, 0, 0, 0, 0, 0, 0, 0])
|
||||
const transaction = semaphoreContract.verifyProof(10, 1, signal, 0, 0, [0, 0, 0, 0, 0, 0, 0, 0])
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__GroupDoesNotExist()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(semaphoreContract, "Semaphore__GroupDoesNotExist")
|
||||
})
|
||||
|
||||
it("Should not verify a proof if the Merkle tree root is not part of the group", async () => {
|
||||
const transaction = contract.verifyProof(2, 1, signal, 0, 0, [0, 0, 0, 0, 0, 0, 0, 0])
|
||||
const transaction = semaphoreContract.verifyProof(2, 1, signal, 0, 0, [0, 0, 0, 0, 0, 0, 0, 0])
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__MerkleTreeRootIsNotPartOfTheGroup()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreContract,
|
||||
"Semaphore__MerkleTreeRootIsNotPartOfTheGroup"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should throw an exception if the proof is not valid", async () => {
|
||||
const transaction = contract.verifyProof(
|
||||
const transaction = semaphoreContract.verifyProof(
|
||||
groupId,
|
||||
group.root,
|
||||
signal,
|
||||
fullProof.publicSignals.nullifierHash,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.signal,
|
||||
fullProof.nullifierHash,
|
||||
0,
|
||||
solidityProof
|
||||
fullProof.proof
|
||||
)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("InvalidProof()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(pairingContract, "InvalidProof")
|
||||
})
|
||||
|
||||
it("Should verify a proof for an onchain group correctly", async () => {
|
||||
const transaction = contract.verifyProof(
|
||||
const transaction = semaphoreContract.verifyProof(
|
||||
groupId,
|
||||
group.root,
|
||||
signal,
|
||||
fullProof.publicSignals.nullifierHash,
|
||||
fullProof.publicSignals.merkleRoot,
|
||||
solidityProof
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.signal,
|
||||
fullProof.nullifierHash,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.proof
|
||||
)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(contract, "ProofVerified")
|
||||
.to.emit(semaphoreContract, "ProofVerified")
|
||||
.withArgs(
|
||||
groupId,
|
||||
group.root,
|
||||
fullProof.publicSignals.nullifierHash,
|
||||
fullProof.publicSignals.externalNullifier,
|
||||
signal
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.nullifierHash,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.signal
|
||||
)
|
||||
})
|
||||
|
||||
it("Should not verify the same proof for an onchain group twice", async () => {
|
||||
const transaction = contract.verifyProof(
|
||||
const transaction = semaphoreContract.verifyProof(
|
||||
groupId,
|
||||
group.root,
|
||||
signal,
|
||||
fullProof.publicSignals.nullifierHash,
|
||||
fullProof.publicSignals.merkleRoot,
|
||||
solidityProof
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.signal,
|
||||
fullProof.nullifierHash,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.proof
|
||||
)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__YouAreUsingTheSameNillifierTwice()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreContract,
|
||||
"Semaphore__YouAreUsingTheSameNillifierTwice"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should not verify a proof if the Merkle tree root is expired", async () => {
|
||||
const groupId = 2
|
||||
const group = new Group(treeDepth)
|
||||
const group = new Group(groupId, treeDepth)
|
||||
|
||||
group.addMembers([members[0], members[1]])
|
||||
|
||||
@@ -275,18 +323,20 @@ describe("Semaphore", () => {
|
||||
wasmFilePath,
|
||||
zkeyFilePath
|
||||
})
|
||||
const solidityProof = packToSolidityProof(fullProof.proof)
|
||||
|
||||
const transaction = contract.verifyProof(
|
||||
const transaction = semaphoreContract.verifyProof(
|
||||
groupId,
|
||||
group.root,
|
||||
signal,
|
||||
fullProof.publicSignals.nullifierHash,
|
||||
0,
|
||||
solidityProof
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.signal,
|
||||
fullProof.nullifierHash,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.proof
|
||||
)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__MerkleTreeRootIsExpired()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreContract,
|
||||
"Semaphore__MerkleTreeRootIsExpired"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,130 +1,134 @@
|
||||
/* eslint-disable jest/valid-expect */
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import {
|
||||
generateNullifierHash,
|
||||
generateProof,
|
||||
packToSolidityProof,
|
||||
PublicSignals,
|
||||
SolidityProof
|
||||
} from "@semaphore-protocol/proof"
|
||||
import { FullProof, generateProof } from "@semaphore-protocol/proof"
|
||||
import { expect } from "chai"
|
||||
import { Signer, utils } from "ethers"
|
||||
import { Signer } from "ethers"
|
||||
import { ethers, run } from "hardhat"
|
||||
import { SemaphoreVoting } from "../build/typechain"
|
||||
import { Pairing, SemaphoreVoting } from "../build/typechain"
|
||||
|
||||
describe("SemaphoreVoting", () => {
|
||||
let contract: SemaphoreVoting
|
||||
let semaphoreVotingContract: SemaphoreVoting
|
||||
let pairingContract: Pairing
|
||||
let accounts: Signer[]
|
||||
let coordinator: string
|
||||
|
||||
const treeDepth = Number(process.env.TREE_DEPTH) || 20
|
||||
const pollIds = [BigInt(1), BigInt(2), BigInt(3)]
|
||||
const pollIds = [1, 2, 3]
|
||||
const encryptionKey = BigInt(0)
|
||||
const decryptionKey = BigInt(0)
|
||||
|
||||
const wasmFilePath = `../../snark-artifacts/semaphore.wasm`
|
||||
const zkeyFilePath = `../../snark-artifacts/semaphore.zkey`
|
||||
const wasmFilePath = `../../snark-artifacts/${treeDepth}/semaphore.wasm`
|
||||
const zkeyFilePath = `../../snark-artifacts/${treeDepth}/semaphore.zkey`
|
||||
|
||||
before(async () => {
|
||||
const { address: verifierAddress } = await run("deploy:verifier", { logs: false, depth: treeDepth })
|
||||
contract = await run("deploy:semaphore-voting", {
|
||||
logs: false,
|
||||
verifiers: [{ merkleTreeDepth: treeDepth, contractAddress: verifierAddress }]
|
||||
const { semaphoreVoting, pairingAddress } = await run("deploy:semaphore-voting", {
|
||||
logs: false
|
||||
})
|
||||
|
||||
semaphoreVotingContract = semaphoreVoting
|
||||
pairingContract = await ethers.getContractAt("Pairing", pairingAddress)
|
||||
|
||||
accounts = await ethers.getSigners()
|
||||
coordinator = await accounts[1].getAddress()
|
||||
})
|
||||
|
||||
describe("# createPoll", () => {
|
||||
it("Should not create a poll with a wrong depth", async () => {
|
||||
const transaction = contract.createPoll(pollIds[0], coordinator, 10)
|
||||
const transaction = semaphoreVotingContract.createPoll(pollIds[0], coordinator, 10)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__MerkleTreeDepthIsNotSupported()")
|
||||
})
|
||||
|
||||
it("Should not create a poll greater than the snark scalar field", async () => {
|
||||
const transaction = contract.createPoll(
|
||||
BigInt("21888242871839275222246405745257275088548364400416034343698204186575808495618"),
|
||||
coordinator,
|
||||
treeDepth
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreVotingContract,
|
||||
"Semaphore__MerkleTreeDepthIsNotSupported"
|
||||
)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__GroupIdIsNotLessThanSnarkScalarField()")
|
||||
})
|
||||
|
||||
it("Should create a poll", async () => {
|
||||
const transaction = contract.createPoll(pollIds[0], coordinator, treeDepth)
|
||||
const transaction = semaphoreVotingContract.createPoll(pollIds[0], coordinator, treeDepth)
|
||||
|
||||
await expect(transaction).to.emit(contract, "PollCreated").withArgs(pollIds[0], coordinator)
|
||||
await expect(transaction).to.emit(semaphoreVotingContract, "PollCreated").withArgs(pollIds[0], coordinator)
|
||||
})
|
||||
|
||||
it("Should not create a poll if it already exists", async () => {
|
||||
const transaction = contract.createPoll(pollIds[0], coordinator, treeDepth)
|
||||
const transaction = semaphoreVotingContract.createPoll(pollIds[0], coordinator, treeDepth)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__GroupAlreadyExists()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreVotingContract,
|
||||
"Semaphore__GroupAlreadyExists"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# startPoll", () => {
|
||||
it("Should not start the poll if the caller is not the coordinator", async () => {
|
||||
const transaction = contract.startPoll(pollIds[0], encryptionKey)
|
||||
const transaction = semaphoreVotingContract.startPoll(pollIds[0], encryptionKey)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotThePollCoordinator()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreVotingContract,
|
||||
"Semaphore__CallerIsNotThePollCoordinator"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should start the poll", async () => {
|
||||
const transaction = contract.connect(accounts[1]).startPoll(pollIds[0], encryptionKey)
|
||||
const transaction = semaphoreVotingContract.connect(accounts[1]).startPoll(pollIds[0], encryptionKey)
|
||||
|
||||
await expect(transaction).to.emit(contract, "PollStarted").withArgs(pollIds[0], coordinator, encryptionKey)
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreVotingContract, "PollStarted")
|
||||
.withArgs(pollIds[0], coordinator, encryptionKey)
|
||||
})
|
||||
|
||||
it("Should not start a poll if it has already been started", async () => {
|
||||
const transaction = contract.connect(accounts[1]).startPoll(pollIds[0], encryptionKey)
|
||||
const transaction = semaphoreVotingContract.connect(accounts[1]).startPoll(pollIds[0], encryptionKey)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__PollHasAlreadyBeenStarted()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreVotingContract,
|
||||
"Semaphore__PollHasAlreadyBeenStarted"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# addVoter", () => {
|
||||
before(async () => {
|
||||
await contract.createPoll(pollIds[1], coordinator, treeDepth)
|
||||
await semaphoreVotingContract.createPoll(pollIds[1], coordinator, treeDepth)
|
||||
})
|
||||
|
||||
it("Should not add a voter if the caller is not the coordinator", async () => {
|
||||
const { commitment } = new Identity()
|
||||
|
||||
const transaction = contract.addVoter(pollIds[0], commitment)
|
||||
const transaction = semaphoreVotingContract.addVoter(pollIds[0], commitment)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotThePollCoordinator()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreVotingContract,
|
||||
"Semaphore__CallerIsNotThePollCoordinator"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should not add a voter if the poll has already been started", async () => {
|
||||
const { commitment } = new Identity()
|
||||
|
||||
const transaction = contract.connect(accounts[1]).addVoter(pollIds[0], commitment)
|
||||
const transaction = semaphoreVotingContract.connect(accounts[1]).addVoter(pollIds[0], commitment)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__PollHasAlreadyBeenStarted()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreVotingContract,
|
||||
"Semaphore__PollHasAlreadyBeenStarted"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should add a voter to an existing poll", async () => {
|
||||
const { commitment } = new Identity("test")
|
||||
const group = new Group(pollIds[1], treeDepth)
|
||||
|
||||
const transaction = contract.connect(accounts[1]).addVoter(pollIds[1], commitment)
|
||||
group.addMember(commitment)
|
||||
|
||||
const transaction = semaphoreVotingContract.connect(accounts[1]).addVoter(pollIds[1], commitment)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(contract, "MemberAdded")
|
||||
.withArgs(
|
||||
pollIds[1],
|
||||
0,
|
||||
commitment,
|
||||
"14787813191318312920980352979830075893203307366494541177071234930769373297362"
|
||||
)
|
||||
.to.emit(semaphoreVotingContract, "MemberAdded")
|
||||
.withArgs(pollIds[1], 0, commitment, group.root)
|
||||
})
|
||||
|
||||
it("Should return the correct number of poll voters", async () => {
|
||||
const size = await contract.getNumberOfMerkleTreeLeaves(pollIds[1])
|
||||
const size = await semaphoreVotingContract.getNumberOfMerkleTreeLeaves(pollIds[1])
|
||||
|
||||
expect(size).to.be.eq(1)
|
||||
})
|
||||
@@ -132,88 +136,89 @@ describe("SemaphoreVoting", () => {
|
||||
|
||||
describe("# castVote", () => {
|
||||
const identity = new Identity("test")
|
||||
const vote = "1"
|
||||
const bytes32Vote = utils.formatBytes32String(vote)
|
||||
const vote = 1
|
||||
|
||||
const group = new Group(treeDepth)
|
||||
const group = new Group(pollIds[1], treeDepth)
|
||||
|
||||
group.addMembers([identity.commitment, BigInt(1)])
|
||||
|
||||
let solidityProof: SolidityProof
|
||||
let publicSignals: PublicSignals
|
||||
let fullProof: FullProof
|
||||
|
||||
before(async () => {
|
||||
await contract.connect(accounts[1]).addVoter(pollIds[1], BigInt(1))
|
||||
await contract.connect(accounts[1]).startPoll(pollIds[1], encryptionKey)
|
||||
await contract.createPoll(pollIds[2], coordinator, treeDepth)
|
||||
await semaphoreVotingContract.connect(accounts[1]).addVoter(pollIds[1], BigInt(1))
|
||||
await semaphoreVotingContract.connect(accounts[1]).startPoll(pollIds[1], encryptionKey)
|
||||
await semaphoreVotingContract.createPoll(pollIds[2], coordinator, treeDepth)
|
||||
|
||||
const fullProof = await generateProof(identity, group, pollIds[1], vote, {
|
||||
fullProof = await generateProof(identity, group, pollIds[1], vote, {
|
||||
wasmFilePath,
|
||||
zkeyFilePath
|
||||
})
|
||||
|
||||
publicSignals = fullProof.publicSignals
|
||||
solidityProof = packToSolidityProof(fullProof.proof)
|
||||
})
|
||||
|
||||
it("Should not cast a vote if the caller is not the coordinator", async () => {
|
||||
const transaction = contract.castVote(bytes32Vote, publicSignals.nullifierHash, pollIds[0], solidityProof)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotThePollCoordinator()")
|
||||
})
|
||||
|
||||
it("Should not cast a vote if the poll is not ongoing", async () => {
|
||||
const transaction = contract
|
||||
const transaction = semaphoreVotingContract
|
||||
.connect(accounts[1])
|
||||
.castVote(bytes32Vote, publicSignals.nullifierHash, pollIds[2], solidityProof)
|
||||
.castVote(vote, fullProof.nullifierHash, pollIds[2], fullProof.proof)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__PollIsNotOngoing()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreVotingContract,
|
||||
"Semaphore__PollIsNotOngoing"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should not cast a vote if the proof is not valid", async () => {
|
||||
const nullifierHash = generateNullifierHash(pollIds[0], identity.getNullifier())
|
||||
|
||||
const transaction = contract
|
||||
const transaction = semaphoreVotingContract
|
||||
.connect(accounts[1])
|
||||
.castVote(bytes32Vote, nullifierHash, pollIds[1], solidityProof)
|
||||
.castVote(vote, 0, pollIds[1], fullProof.proof)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("InvalidProof()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(pairingContract, "InvalidProof")
|
||||
})
|
||||
|
||||
it("Should cast a vote", async () => {
|
||||
const transaction = contract
|
||||
const transaction = semaphoreVotingContract
|
||||
.connect(accounts[1])
|
||||
.castVote(bytes32Vote, publicSignals.nullifierHash, pollIds[1], solidityProof)
|
||||
.castVote(vote, fullProof.nullifierHash, pollIds[1], fullProof.proof)
|
||||
|
||||
await expect(transaction).to.emit(contract, "VoteAdded").withArgs(pollIds[1], bytes32Vote)
|
||||
await expect(transaction).to.emit(semaphoreVotingContract, "VoteAdded").withArgs(pollIds[1], vote)
|
||||
})
|
||||
|
||||
it("Should not cast a vote twice", async () => {
|
||||
const transaction = contract
|
||||
const transaction = semaphoreVotingContract
|
||||
.connect(accounts[1])
|
||||
.castVote(bytes32Vote, publicSignals.nullifierHash, pollIds[1], solidityProof)
|
||||
.castVote(vote, fullProof.nullifierHash, pollIds[1], fullProof.proof)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__YouAreUsingTheSameNillifierTwice()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreVotingContract,
|
||||
"Semaphore__YouAreUsingTheSameNillifierTwice"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# endPoll", () => {
|
||||
it("Should not end the poll if the caller is not the coordinator", async () => {
|
||||
const transaction = contract.endPoll(pollIds[1], decryptionKey)
|
||||
const transaction = semaphoreVotingContract.endPoll(pollIds[1], decryptionKey)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotThePollCoordinator()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreVotingContract,
|
||||
"Semaphore__CallerIsNotThePollCoordinator"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should end the poll", async () => {
|
||||
const transaction = contract.connect(accounts[1]).endPoll(pollIds[1], encryptionKey)
|
||||
const transaction = semaphoreVotingContract.connect(accounts[1]).endPoll(pollIds[1], encryptionKey)
|
||||
|
||||
await expect(transaction).to.emit(contract, "PollEnded").withArgs(pollIds[1], coordinator, decryptionKey)
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreVotingContract, "PollEnded")
|
||||
.withArgs(pollIds[1], coordinator, decryptionKey)
|
||||
})
|
||||
|
||||
it("Should not end a poll if it has already been ended", async () => {
|
||||
const transaction = contract.connect(accounts[1]).endPoll(pollIds[1], encryptionKey)
|
||||
const transaction = semaphoreVotingContract.connect(accounts[1]).endPoll(pollIds[1], encryptionKey)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__PollIsNotOngoing()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreVotingContract,
|
||||
"Semaphore__PollIsNotOngoing"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,67 +1,61 @@
|
||||
/* eslint-disable jest/valid-expect */
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import {
|
||||
generateNullifierHash,
|
||||
generateProof,
|
||||
packToSolidityProof,
|
||||
PublicSignals,
|
||||
SolidityProof
|
||||
} from "@semaphore-protocol/proof"
|
||||
import { FullProof, generateProof } from "@semaphore-protocol/proof"
|
||||
import { expect } from "chai"
|
||||
import { Signer, utils } from "ethers"
|
||||
import { ethers, run } from "hardhat"
|
||||
import { SemaphoreWhistleblowing } from "../build/typechain"
|
||||
import { Pairing, SemaphoreWhistleblowing } from "../build/typechain"
|
||||
|
||||
describe("SemaphoreWhistleblowing", () => {
|
||||
let contract: SemaphoreWhistleblowing
|
||||
let semaphoreWhistleblowingContract: SemaphoreWhistleblowing
|
||||
let pairingContract: Pairing
|
||||
let accounts: Signer[]
|
||||
let editor: string
|
||||
|
||||
const treeDepth = Number(process.env.TREE_DEPTH) || 20
|
||||
const entityIds = [BigInt(1), BigInt(2)]
|
||||
const entityIds = [1, 2]
|
||||
|
||||
const wasmFilePath = `../../snark-artifacts/semaphore.wasm`
|
||||
const zkeyFilePath = `../../snark-artifacts/semaphore.zkey`
|
||||
const wasmFilePath = `../../snark-artifacts/${treeDepth}/semaphore.wasm`
|
||||
const zkeyFilePath = `../../snark-artifacts/${treeDepth}/semaphore.zkey`
|
||||
|
||||
before(async () => {
|
||||
const { address: verifierAddress } = await run("deploy:verifier", { logs: false, depth: treeDepth })
|
||||
contract = await run("deploy:semaphore-whistleblowing", {
|
||||
logs: false,
|
||||
verifiers: [{ merkleTreeDepth: treeDepth, contractAddress: verifierAddress }]
|
||||
const { semaphoreWhistleblowing, pairingAddress } = await run("deploy:semaphore-whistleblowing", {
|
||||
logs: false
|
||||
})
|
||||
|
||||
semaphoreWhistleblowingContract = semaphoreWhistleblowing
|
||||
pairingContract = await ethers.getContractAt("Pairing", pairingAddress)
|
||||
|
||||
accounts = await ethers.getSigners()
|
||||
editor = await accounts[1].getAddress()
|
||||
})
|
||||
|
||||
describe("# createEntity", () => {
|
||||
it("Should not create an entity with a wrong depth", async () => {
|
||||
const transaction = contract.createEntity(entityIds[0], editor, 10)
|
||||
const transaction = semaphoreWhistleblowingContract.createEntity(entityIds[0], editor, 10)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__MerkleTreeDepthIsNotSupported()")
|
||||
})
|
||||
|
||||
it("Should not create an entity greater than the snark scalar field", async () => {
|
||||
const transaction = contract.createEntity(
|
||||
BigInt("21888242871839275222246405745257275088548364400416034343698204186575808495618"),
|
||||
editor,
|
||||
treeDepth
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreWhistleblowingContract,
|
||||
"Semaphore__MerkleTreeDepthIsNotSupported"
|
||||
)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__GroupIdIsNotLessThanSnarkScalarField()")
|
||||
})
|
||||
|
||||
it("Should create an entity", async () => {
|
||||
const transaction = contract.createEntity(entityIds[0], editor, treeDepth)
|
||||
const transaction = semaphoreWhistleblowingContract.createEntity(entityIds[0], editor, treeDepth)
|
||||
|
||||
await expect(transaction).to.emit(contract, "EntityCreated").withArgs(entityIds[0], editor)
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreWhistleblowingContract, "EntityCreated")
|
||||
.withArgs(entityIds[0], editor)
|
||||
})
|
||||
|
||||
it("Should not create a entity if it already exists", async () => {
|
||||
const transaction = contract.createEntity(entityIds[0], editor, treeDepth)
|
||||
const transaction = semaphoreWhistleblowingContract.createEntity(entityIds[0], editor, treeDepth)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__GroupAlreadyExists()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreWhistleblowingContract,
|
||||
"Semaphore__GroupAlreadyExists"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -69,28 +63,31 @@ describe("SemaphoreWhistleblowing", () => {
|
||||
it("Should not add a whistleblower if the caller is not the editor", async () => {
|
||||
const { commitment } = new Identity()
|
||||
|
||||
const transaction = contract.addWhistleblower(entityIds[0], commitment)
|
||||
const transaction = semaphoreWhistleblowingContract.addWhistleblower(entityIds[0], commitment)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotTheEditor()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreWhistleblowingContract,
|
||||
"Semaphore__CallerIsNotTheEditor"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should add a whistleblower to an existing entity", async () => {
|
||||
const { commitment } = new Identity("test")
|
||||
const group = new Group(entityIds[0], treeDepth)
|
||||
|
||||
const transaction = contract.connect(accounts[1]).addWhistleblower(entityIds[0], commitment)
|
||||
group.addMember(commitment)
|
||||
|
||||
const transaction = semaphoreWhistleblowingContract
|
||||
.connect(accounts[1])
|
||||
.addWhistleblower(entityIds[0], commitment)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(contract, "MemberAdded")
|
||||
.withArgs(
|
||||
entityIds[0],
|
||||
0,
|
||||
commitment,
|
||||
"14787813191318312920980352979830075893203307366494541177071234930769373297362"
|
||||
)
|
||||
.to.emit(semaphoreWhistleblowingContract, "MemberAdded")
|
||||
.withArgs(entityIds[0], 0, commitment, group.root)
|
||||
})
|
||||
|
||||
it("Should return the correct number of whistleblowers of an entity", async () => {
|
||||
const size = await contract.getNumberOfMerkleTreeLeaves(entityIds[0])
|
||||
const size = await semaphoreWhistleblowingContract.getNumberOfMerkleTreeLeaves(entityIds[0])
|
||||
|
||||
expect(size).to.be.eq(1)
|
||||
})
|
||||
@@ -99,93 +96,84 @@ describe("SemaphoreWhistleblowing", () => {
|
||||
describe("# removeWhistleblower", () => {
|
||||
it("Should not remove a whistleblower if the caller is not the editor", async () => {
|
||||
const { commitment } = new Identity()
|
||||
const group = new Group(treeDepth)
|
||||
const group = new Group(entityIds[0], treeDepth)
|
||||
|
||||
group.addMember(commitment)
|
||||
|
||||
const { siblings, pathIndices } = group.generateProofOfMembership(0)
|
||||
const { siblings, pathIndices } = group.generateMerkleProof(0)
|
||||
|
||||
const transaction = contract.removeWhistleblower(entityIds[0], commitment, siblings, pathIndices)
|
||||
const transaction = semaphoreWhistleblowingContract.removeWhistleblower(
|
||||
entityIds[0],
|
||||
commitment,
|
||||
siblings,
|
||||
pathIndices
|
||||
)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotTheEditor()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(
|
||||
semaphoreWhistleblowingContract,
|
||||
"Semaphore__CallerIsNotTheEditor"
|
||||
)
|
||||
})
|
||||
|
||||
it("Should remove a whistleblower from an existing entity", async () => {
|
||||
const { commitment } = new Identity("test")
|
||||
const group = new Group(treeDepth)
|
||||
const group = new Group(entityIds[0], treeDepth)
|
||||
|
||||
group.addMember(commitment)
|
||||
|
||||
const { siblings, pathIndices } = group.generateProofOfMembership(0)
|
||||
const { siblings, pathIndices } = group.generateMerkleProof(0)
|
||||
|
||||
const transaction = contract
|
||||
group.removeMember(0)
|
||||
|
||||
const transaction = semaphoreWhistleblowingContract
|
||||
.connect(accounts[1])
|
||||
.removeWhistleblower(entityIds[0], commitment, siblings, pathIndices)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(contract, "MemberRemoved")
|
||||
.withArgs(
|
||||
entityIds[0],
|
||||
0,
|
||||
commitment,
|
||||
"15019797232609675441998260052101280400536945603062888308240081994073687793470"
|
||||
)
|
||||
.to.emit(semaphoreWhistleblowingContract, "MemberRemoved")
|
||||
.withArgs(entityIds[0], 0, commitment, group.root)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# publishLeak", () => {
|
||||
const identity = new Identity("test")
|
||||
const leak = "leak"
|
||||
const bytes32Leak = utils.formatBytes32String(leak)
|
||||
const leak = utils.formatBytes32String("This is a leak")
|
||||
|
||||
const group = new Group(treeDepth)
|
||||
const group = new Group(entityIds[1], treeDepth)
|
||||
|
||||
group.addMembers([identity.commitment, BigInt(1)])
|
||||
|
||||
let solidityProof: SolidityProof
|
||||
let publicSignals: PublicSignals
|
||||
let fullProof: FullProof
|
||||
|
||||
before(async () => {
|
||||
await contract.createEntity(entityIds[1], editor, treeDepth)
|
||||
await contract.connect(accounts[1]).addWhistleblower(entityIds[1], identity.commitment)
|
||||
await contract.connect(accounts[1]).addWhistleblower(entityIds[1], BigInt(1))
|
||||
await semaphoreWhistleblowingContract.createEntity(entityIds[1], editor, treeDepth)
|
||||
await semaphoreWhistleblowingContract
|
||||
.connect(accounts[1])
|
||||
.addWhistleblower(entityIds[1], identity.commitment)
|
||||
await semaphoreWhistleblowingContract.connect(accounts[1]).addWhistleblower(entityIds[1], BigInt(1))
|
||||
|
||||
const fullProof = await generateProof(identity, group, entityIds[1], leak, {
|
||||
fullProof = await generateProof(identity, group, entityIds[1], leak, {
|
||||
wasmFilePath,
|
||||
zkeyFilePath
|
||||
})
|
||||
|
||||
publicSignals = fullProof.publicSignals
|
||||
solidityProof = packToSolidityProof(fullProof.proof)
|
||||
})
|
||||
|
||||
it("Should not publish a leak if the caller is not the editor", async () => {
|
||||
const transaction = contract.publishLeak(
|
||||
bytes32Leak,
|
||||
publicSignals.nullifierHash,
|
||||
entityIds[0],
|
||||
solidityProof
|
||||
)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotTheEditor()")
|
||||
})
|
||||
|
||||
it("Should not publish a leak if the proof is not valid", async () => {
|
||||
const nullifierHash = generateNullifierHash(entityIds[0], identity.getNullifier())
|
||||
|
||||
const transaction = contract
|
||||
const transaction = semaphoreWhistleblowingContract
|
||||
.connect(accounts[1])
|
||||
.publishLeak(bytes32Leak, nullifierHash, entityIds[1], solidityProof)
|
||||
.publishLeak(leak, 0, entityIds[1], fullProof.proof)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("InvalidProof()")
|
||||
await expect(transaction).to.be.revertedWithCustomError(pairingContract, "InvalidProof")
|
||||
})
|
||||
|
||||
it("Should publish a leak", async () => {
|
||||
const transaction = contract
|
||||
const transaction = semaphoreWhistleblowingContract
|
||||
.connect(accounts[1])
|
||||
.publishLeak(bytes32Leak, publicSignals.nullifierHash, entityIds[1], solidityProof)
|
||||
.publishLeak(leak, fullProof.nullifierHash, entityIds[1], fullProof.proof)
|
||||
|
||||
await expect(transaction).to.emit(contract, "LeakPublished").withArgs(entityIds[1], bytes32Leak)
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreWhistleblowingContract, "LeakPublished")
|
||||
.withArgs(entityIds[1], leak)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
21
packages/data/LICENSE
Normal file
21
packages/data/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Ethereum Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
159
packages/data/README.md
Normal file
159
packages/data/README.md
Normal file
@@ -0,0 +1,159 @@
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
Semaphore data
|
||||
</h1>
|
||||
<p align="center">A library to query Semaphore contracts.</p>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/semaphore-protocol">
|
||||
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/LICENSE">
|
||||
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/@semaphore-protocol/data">
|
||||
<img alt="NPM version" src="https://img.shields.io/npm/v/@semaphore-protocol/data?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/data">
|
||||
<img alt="Downloads" src="https://img.shields.io/npm/dm/@semaphore-protocol/data.svg?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://eslint.org/">
|
||||
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint" />
|
||||
</a>
|
||||
<a href="https://prettier.io/">
|
||||
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
<h4>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CONTRIBUTING.md">
|
||||
👥 Contributing
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
|
||||
🤝 Code of conduct
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://semaphore.appliedzkp.org/discord">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
| This library allows you to query the [`Semaphore.sol`](https://github.com/semaphore-protocol/semaphore/blob/main/contracts/Semaphore.sol) contract data (i.e. groups) using the [Semaphore subgraph](https://github.com/semaphore-protocol/subgraph) or Ethers. It can be used on Node.js and browsers. |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
## 🛠 Install
|
||||
|
||||
### npm or yarn
|
||||
|
||||
Install the `@semaphore-protocol/data` package with npm:
|
||||
|
||||
```bash
|
||||
npm i @semaphore-protocol/data
|
||||
```
|
||||
|
||||
or yarn:
|
||||
|
||||
```bash
|
||||
yarn add @semaphore-protocol/data
|
||||
```
|
||||
|
||||
## 📜 Usage
|
||||
|
||||
\# **new SemaphoreSubgraph**(networkOrSubgraphURL: _Network_ | _string_ = "goerli"): _SemaphoreSubgraph_
|
||||
|
||||
```typescript
|
||||
import { SemaphoreSubgraph } from "@semaphore-protocol/data"
|
||||
|
||||
const semaphoreSubgraph = new SemaphoreSubgraph()
|
||||
|
||||
// or:
|
||||
const semaphoreSubgraph = new SemaphoreSubgraph("arbitrum")
|
||||
|
||||
// or:
|
||||
const semaphoreSubgraph = new SemaphoreSubgraph(
|
||||
"https://api.studio.thegraph.com/query/14377/<your-subgraph>/<your-version>"
|
||||
)
|
||||
```
|
||||
|
||||
\# **getGroupIds**(): _Promise\<string[]>_
|
||||
|
||||
```typescript
|
||||
const groupIds = await semaphoreSubgraph.getGroupIds()
|
||||
```
|
||||
|
||||
\# **getGroups**(options?: _GroupOptions_): _Promise\<GroupResponse[]>_
|
||||
|
||||
```typescript
|
||||
const groups = await semaphoreSubgraph.getGroups()
|
||||
|
||||
// or
|
||||
|
||||
const groups = await semaphoreSubgraph.getGroups({ members: true, verifiedProofs: true })
|
||||
```
|
||||
|
||||
\# **getGroup**(groupId: _string_, options?: _GroupOptions_): _Promise\<GroupResponse>_
|
||||
|
||||
```typescript
|
||||
const group = await semaphoreSubgraph.getGroup("42")
|
||||
|
||||
// or
|
||||
|
||||
const { members, verifiedProofs } = semaphoreSubgraph.getGroup("42", { members: true, verifiedProofs: true })
|
||||
```
|
||||
|
||||
\# **new Ethers**(networkOrEthereumURL: Network | string = "goerli", options: EthersOptions = {}): _SemaphoreEthers_
|
||||
|
||||
```typescript
|
||||
import { SemaphoreEthers } from "@semaphore-protocol/data"
|
||||
|
||||
const semaphoreEthers = new SemaphoreEthers()
|
||||
|
||||
// or:
|
||||
const semaphoreEthers = new SemaphoreEthers("homestead", {
|
||||
address: "semaphore-address",
|
||||
startBlock: 0
|
||||
})
|
||||
|
||||
// or:
|
||||
const semaphoreEthers = new SemaphoreEthers("http://localhost:8545", {
|
||||
address: "semaphore-address"
|
||||
})
|
||||
```
|
||||
|
||||
\# **getGroupIds**(): _Promise\<string[]>_
|
||||
|
||||
```typescript
|
||||
const groupIds = await semaphoreEthers.getGroupIds()
|
||||
```
|
||||
|
||||
\# **getGroup**(groupId: _string_): _Promise\<GroupResponse>_
|
||||
|
||||
```typescript
|
||||
const group = await semaphoreEthers.getGroup("42")
|
||||
```
|
||||
|
||||
\# **getGroupAdmin**(groupId: _string_): _Promise\<string>_
|
||||
|
||||
```typescript
|
||||
const admin = await semaphoreEthers.getGroupAdmin("42")
|
||||
```
|
||||
|
||||
\# **getGroupMembers**(groupId: _string_): _Promise\<string[]>_
|
||||
|
||||
```typescript
|
||||
const members = await semaphoreEthers.getGroupMembers("42")
|
||||
```
|
||||
|
||||
\# **getGroupVerifiedProofs**(groupId: _string_): _Promise\<any[]>_
|
||||
|
||||
```typescript
|
||||
const verifiedProofs = await semaphoreEthers.getGroupVerifiedProofs()
|
||||
```
|
||||
@@ -1,17 +1,14 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/subgraph",
|
||||
"version": "2.6.1",
|
||||
"name": "@semaphore-protocol/data",
|
||||
"version": "3.2.0",
|
||||
"description": "A library to query Semaphore contracts.",
|
||||
"license": "MIT",
|
||||
"iife": "dist/index.js",
|
||||
"unpkg": "dist/index.min.js",
|
||||
"jsdelivr": "dist/index.min.js",
|
||||
"main": "dist/index.node.js",
|
||||
"exports": {
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.node.js"
|
||||
},
|
||||
"types": "dist/types/index.d.ts",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"files": [
|
||||
"dist/",
|
||||
"src/",
|
||||
@@ -19,7 +16,7 @@
|
||||
"README.md"
|
||||
],
|
||||
"repository": "https://github.com/semaphore-protocol/semaphore",
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore/tree/main/packages/subgraph",
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore/tree/main/packages/data",
|
||||
"bugs": {
|
||||
"url": "https://github.com/semaphore-protocol/semaphore.git/issues"
|
||||
},
|
||||
@@ -27,18 +24,20 @@
|
||||
"build:watch": "rollup -c rollup.config.ts -w --configPlugin typescript",
|
||||
"build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
|
||||
"prepublishOnly": "yarn build",
|
||||
"docs": "typedoc src/index.ts --out ../../docs/subgraph"
|
||||
"docs": "typedoc src/index.ts --out ../../docs/data"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-json": "^6.0.0",
|
||||
"rollup-plugin-cleanup": "^3.2.1",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-typescript2": "^0.31.2",
|
||||
"typedoc": "^0.22.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ethersproject/contracts": "^5.7.0",
|
||||
"@ethersproject/providers": "^5.7.0",
|
||||
"axios": "^0.27.2"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import json from "@rollup/plugin-json"
|
||||
import * as fs from "fs"
|
||||
import cleanup from "rollup-plugin-cleanup"
|
||||
import { terser } from "rollup-plugin-terser"
|
||||
import typescript from "rollup-plugin-typescript2"
|
||||
|
||||
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
|
||||
const name = pkg.name.substr(1).replace(/[-/]./g, (x: string) => x.toUpperCase()[1])
|
||||
const banner = `/**
|
||||
* @module ${pkg.name}
|
||||
* @version ${pkg.version}
|
||||
@@ -17,25 +16,12 @@ const banner = `/**
|
||||
export default {
|
||||
input: "src/index.ts",
|
||||
output: [
|
||||
{
|
||||
file: pkg.iife,
|
||||
name,
|
||||
format: "iife",
|
||||
globals: { axios: "axios" },
|
||||
banner
|
||||
},
|
||||
{
|
||||
file: pkg.unpkg,
|
||||
name,
|
||||
format: "iife",
|
||||
globals: { axios: "axios" },
|
||||
plugins: [terser({ output: { preamble: banner } })]
|
||||
},
|
||||
{ file: pkg.exports.require, format: "cjs", banner, exports: "auto" },
|
||||
{ file: pkg.exports.import, format: "es", banner }
|
||||
],
|
||||
external: Object.keys(pkg.dependencies),
|
||||
plugins: [
|
||||
json(),
|
||||
typescript({ tsconfig: "./build.tsconfig.json", useTsconfigDeclarationDir: true }),
|
||||
cleanup({ comments: "jsdoc" })
|
||||
]
|
||||
276
packages/data/src/ethers.test.ts
Normal file
276
packages/data/src/ethers.test.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
import SemaphoreEthers from "./ethers"
|
||||
import getEvents from "./getEvents"
|
||||
|
||||
jest.mock("./getEvents", () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn()
|
||||
}))
|
||||
|
||||
jest.mock("@ethersproject/contracts", () => ({
|
||||
__esModule: true,
|
||||
Contract: jest.fn(
|
||||
() =>
|
||||
({
|
||||
getMerkleTreeRoot: () => "222",
|
||||
getNumberOfMerkleTreeLeaves: () => ({
|
||||
toNumber: () => 2
|
||||
})
|
||||
} as any)
|
||||
)
|
||||
}))
|
||||
|
||||
const getEventsMocked = getEvents as jest.MockedFunction<typeof getEvents>
|
||||
|
||||
describe("SemaphoreEthers", () => {
|
||||
let semaphore: SemaphoreEthers
|
||||
|
||||
describe("# SemaphoreEthers", () => {
|
||||
it("Should instantiate a SemaphoreEthers object with different networks", () => {
|
||||
semaphore = new SemaphoreEthers()
|
||||
const semaphore1 = new SemaphoreEthers("arbitrum")
|
||||
const semaphore2 = new SemaphoreEthers("homestead", {
|
||||
address: "0x0000000000000000000000000000000000000000",
|
||||
startBlock: 0
|
||||
})
|
||||
|
||||
expect(semaphore.network).toBe("goerli")
|
||||
expect(semaphore.contract).toBeInstanceOf(Object)
|
||||
expect(semaphore1.network).toBe("arbitrum")
|
||||
expect(semaphore2.network).toBe("homestead")
|
||||
expect(semaphore2.options.startBlock).toBe(0)
|
||||
expect(semaphore2.options.address).toContain("0x000000")
|
||||
})
|
||||
|
||||
it("Should instantiate a SemaphoreEthers object with different providers", () => {
|
||||
const semaphore1 = new SemaphoreEthers("homestead", {
|
||||
provider: "infura",
|
||||
address: "0x0000000000000000000000000000000000000000",
|
||||
apiKey: "1234567890"
|
||||
})
|
||||
const semaphore2 = new SemaphoreEthers("homestead", {
|
||||
provider: "etherscan",
|
||||
address: "0x0000000000000000000000000000000000000000"
|
||||
})
|
||||
const semaphore3 = new SemaphoreEthers("homestead", {
|
||||
provider: "alchemy",
|
||||
address: "0x0000000000000000000000000000000000000000"
|
||||
})
|
||||
const semaphore4 = new SemaphoreEthers("homestead", {
|
||||
provider: "cloudflare",
|
||||
address: "0x0000000000000000000000000000000000000000"
|
||||
})
|
||||
const semaphore5 = new SemaphoreEthers("homestead", {
|
||||
provider: "pocket",
|
||||
address: "0x0000000000000000000000000000000000000000"
|
||||
})
|
||||
const semaphore6 = new SemaphoreEthers("homestead", {
|
||||
provider: "ankr",
|
||||
address: "0x0000000000000000000000000000000000000000"
|
||||
})
|
||||
|
||||
expect(semaphore1.options.provider).toBe("infura")
|
||||
expect(semaphore1.options.apiKey).toBe("1234567890")
|
||||
expect(semaphore2.options.provider).toBe("etherscan")
|
||||
expect(semaphore3.options.provider).toBe("alchemy")
|
||||
expect(semaphore4.options.provider).toBe("cloudflare")
|
||||
expect(semaphore5.options.provider).toBe("pocket")
|
||||
expect(semaphore6.options.provider).toBe("ankr")
|
||||
})
|
||||
|
||||
it("Should instantiate a SemaphoreEthers object with a custom URL", () => {
|
||||
const semaphore1 = new SemaphoreEthers("http://localhost:8545", {
|
||||
address: "0x0000000000000000000000000000000000000000"
|
||||
})
|
||||
|
||||
expect(semaphore1.network).toBe("http://localhost:8545")
|
||||
})
|
||||
|
||||
it("Should throw an error if the network is not supported by Semaphore yet and there's no address", () => {
|
||||
const fun = () => new SemaphoreEthers("homestead")
|
||||
|
||||
expect(fun).toThrow("You should provide a Semaphore contract address for this network")
|
||||
})
|
||||
|
||||
it("Should throw an error if the provider is not supported", () => {
|
||||
const fun = () =>
|
||||
new SemaphoreEthers("goerli", {
|
||||
provider: "hello" as any
|
||||
})
|
||||
|
||||
expect(fun).toThrow("Provider 'hello' is not supported")
|
||||
})
|
||||
})
|
||||
|
||||
describe("# getGroupIds", () => {
|
||||
it("Should return all the existing groups", async () => {
|
||||
getEventsMocked.mockReturnValueOnce(Promise.resolve([["32"], ["42"]]))
|
||||
|
||||
const groupIds = await semaphore.getGroupIds()
|
||||
|
||||
expect(groupIds).toContain("42")
|
||||
})
|
||||
})
|
||||
|
||||
describe("# getGroup", () => {
|
||||
it("Should return a specific group", async () => {
|
||||
getEventsMocked.mockReturnValueOnce(
|
||||
Promise.resolve([
|
||||
{
|
||||
merkleTreeDepth: "20",
|
||||
zeroValue: "111"
|
||||
}
|
||||
])
|
||||
)
|
||||
|
||||
const group = await semaphore.getGroup("42")
|
||||
|
||||
expect(group.merkleTree.depth).toBe("20")
|
||||
expect(group.merkleTree.root).toBe("222")
|
||||
expect(group.merkleTree.zeroValue).toContain("111")
|
||||
})
|
||||
|
||||
it("Should throw an error if the group does not exist", async () => {
|
||||
getEventsMocked.mockReturnValueOnce(Promise.resolve([]))
|
||||
|
||||
const fun = () => semaphore.getGroup("666")
|
||||
|
||||
await expect(fun).rejects.toThrow("Group '666' not found")
|
||||
})
|
||||
})
|
||||
|
||||
describe("# getGroupAdmin", () => {
|
||||
it("Should return a group admin", async () => {
|
||||
getEventsMocked.mockReturnValueOnce(
|
||||
Promise.resolve([
|
||||
{
|
||||
newAdmin: "0xA9C2B639a28cDa8b59C4377e980F75A93dD8605F"
|
||||
}
|
||||
])
|
||||
)
|
||||
|
||||
const admin = await semaphore.getGroupAdmin("42")
|
||||
|
||||
expect(admin).toBe("0xA9C2B639a28cDa8b59C4377e980F75A93dD8605F")
|
||||
})
|
||||
|
||||
it("Should throw an error if the group does not exist", async () => {
|
||||
getEventsMocked.mockReturnValueOnce(Promise.resolve([]))
|
||||
|
||||
const fun = () => semaphore.getGroupAdmin("666")
|
||||
|
||||
await expect(fun).rejects.toThrow("Group '666' not found")
|
||||
})
|
||||
})
|
||||
|
||||
describe("# getGroupMembers", () => {
|
||||
it("Should return a list of group members", async () => {
|
||||
getEventsMocked.mockReturnValueOnce(
|
||||
Promise.resolve([
|
||||
{
|
||||
merkleTreeDepth: "20",
|
||||
zeroValue: "0"
|
||||
}
|
||||
])
|
||||
)
|
||||
getEventsMocked.mockReturnValueOnce(
|
||||
Promise.resolve([
|
||||
{
|
||||
index: "0",
|
||||
merkleTreeRoot: "223",
|
||||
blockNumber: 3
|
||||
},
|
||||
{
|
||||
index: "2",
|
||||
merkleTreeRoot: "224",
|
||||
blockNumber: 4
|
||||
}
|
||||
])
|
||||
)
|
||||
getEventsMocked.mockReturnValueOnce(
|
||||
Promise.resolve([
|
||||
{
|
||||
index: "1",
|
||||
newIdentityCommitment: "113",
|
||||
merkleTreeRoot: "225",
|
||||
blockNumber: 3
|
||||
},
|
||||
{
|
||||
index: "2",
|
||||
newIdentityCommitment: "114",
|
||||
merkleTreeRoot: "226",
|
||||
blockNumber: 3
|
||||
}
|
||||
])
|
||||
)
|
||||
getEventsMocked.mockReturnValueOnce(
|
||||
Promise.resolve([
|
||||
{
|
||||
index: "0",
|
||||
identityCommitment: "110",
|
||||
merkleTreeRoot: "220",
|
||||
blockNumber: 0
|
||||
},
|
||||
{
|
||||
index: "1",
|
||||
identityCommitment: "111",
|
||||
merkleTreeRoot: "221",
|
||||
blockNumber: 1
|
||||
},
|
||||
{
|
||||
index: "2",
|
||||
identityCommitment: "112",
|
||||
merkleTreeRoot: "222",
|
||||
blockNumber: 2
|
||||
},
|
||||
{
|
||||
index: "3",
|
||||
identityCommitment: "113",
|
||||
merkleTreeRoot: "223",
|
||||
blockNumber: 3
|
||||
}
|
||||
])
|
||||
)
|
||||
|
||||
const members = await semaphore.getGroupMembers("42")
|
||||
|
||||
expect(members[0]).toBe("0")
|
||||
expect(members[1]).toBe("113")
|
||||
expect(members[2]).toBe("0")
|
||||
})
|
||||
|
||||
it("Should throw an error if the group does not exist", async () => {
|
||||
getEventsMocked.mockReturnValueOnce(Promise.resolve([]))
|
||||
|
||||
const fun = () => semaphore.getGroupMembers("666")
|
||||
|
||||
await expect(fun).rejects.toThrow("Group '666' not found")
|
||||
})
|
||||
})
|
||||
|
||||
describe("# getGroupVerifiedProofs", () => {
|
||||
it("Should return a list of group verified proofs", async () => {
|
||||
getEventsMocked.mockReturnValueOnce(
|
||||
Promise.resolve([
|
||||
{
|
||||
signal: "111",
|
||||
merkleTreeRoot: "112",
|
||||
externalNullifier: "113",
|
||||
nullifierHash: "114"
|
||||
}
|
||||
])
|
||||
)
|
||||
|
||||
const [verifiedProof] = await semaphore.getGroupVerifiedProofs("42")
|
||||
|
||||
expect(verifiedProof.signal).toContain("111")
|
||||
})
|
||||
|
||||
it("Should throw an error if the group does not exist", async () => {
|
||||
getEventsMocked.mockReturnValueOnce(Promise.resolve([]))
|
||||
|
||||
const fun = () => semaphore.getGroupVerifiedProofs("666")
|
||||
|
||||
await expect(fun).rejects.toThrow("Group '666' not found")
|
||||
})
|
||||
})
|
||||
})
|
||||
267
packages/data/src/ethers.ts
Normal file
267
packages/data/src/ethers.ts
Normal file
@@ -0,0 +1,267 @@
|
||||
import { Contract } from "@ethersproject/contracts"
|
||||
import {
|
||||
AlchemyProvider,
|
||||
AnkrProvider,
|
||||
CloudflareProvider,
|
||||
EtherscanProvider,
|
||||
InfuraProvider,
|
||||
JsonRpcProvider,
|
||||
PocketProvider,
|
||||
Provider
|
||||
} from "@ethersproject/providers"
|
||||
import checkParameter from "./checkParameter"
|
||||
import getEvents from "./getEvents"
|
||||
import SemaphoreABI from "./semaphoreABI.json"
|
||||
import { EthersOptions, GroupResponse, Network } from "./types"
|
||||
|
||||
export default class SemaphoreEthers {
|
||||
private _network: Network | string
|
||||
private _options: EthersOptions
|
||||
private _contract: Contract
|
||||
|
||||
/**
|
||||
* Initializes the Ethers object with an Ethereum network or custom URL.
|
||||
* @param networkOrEthereumURL Ethereum network or custom URL.
|
||||
* @param options Ethers options.
|
||||
*/
|
||||
constructor(networkOrEthereumURL: Network | string = "goerli", options: EthersOptions = {}) {
|
||||
checkParameter(networkOrEthereumURL, "networkOrSubgraphURL", "string")
|
||||
|
||||
if (options.provider) {
|
||||
checkParameter(options.provider, "provider", "string")
|
||||
} else if (!networkOrEthereumURL.startsWith("http")) {
|
||||
options.provider = "infura"
|
||||
}
|
||||
|
||||
if (options.apiKey) {
|
||||
checkParameter(options.apiKey, "apiKey", "string")
|
||||
}
|
||||
|
||||
if (networkOrEthereumURL === "matic") {
|
||||
networkOrEthereumURL = "maticmum"
|
||||
}
|
||||
|
||||
switch (networkOrEthereumURL) {
|
||||
case "arbitrum":
|
||||
options.address = "0x72dca3c971136bf47BACF16A141f0fcfAC925aeC"
|
||||
options.startBlock = 54934350
|
||||
break
|
||||
case "maticmum":
|
||||
options.address = "0xF864ABa335073e01234c9a88888BfFfa965650bD"
|
||||
options.startBlock = 32902215
|
||||
break
|
||||
case "goerli":
|
||||
options.address = "0x89490c95eD199D980Cdb4FF8Bac9977EDb41A7E7"
|
||||
options.startBlock = 8255063
|
||||
break
|
||||
default:
|
||||
if (options.address === undefined) {
|
||||
throw new Error(`You should provide a Semaphore contract address for this network`)
|
||||
}
|
||||
|
||||
if (options.startBlock === undefined) {
|
||||
options.startBlock = 0
|
||||
}
|
||||
}
|
||||
|
||||
let provider: Provider
|
||||
|
||||
switch (options.provider) {
|
||||
case "infura":
|
||||
provider = new InfuraProvider(networkOrEthereumURL, options.apiKey)
|
||||
break
|
||||
case "alchemy":
|
||||
provider = new AlchemyProvider(networkOrEthereumURL, options.apiKey)
|
||||
break
|
||||
case "cloudflare":
|
||||
provider = new CloudflareProvider(networkOrEthereumURL, options.apiKey)
|
||||
break
|
||||
case "etherscan":
|
||||
provider = new EtherscanProvider(networkOrEthereumURL, options.apiKey)
|
||||
break
|
||||
case "pocket":
|
||||
provider = new PocketProvider(networkOrEthereumURL, options.apiKey)
|
||||
break
|
||||
case "ankr":
|
||||
provider = new AnkrProvider(networkOrEthereumURL, options.apiKey)
|
||||
break
|
||||
default:
|
||||
if (!networkOrEthereumURL.startsWith("http")) {
|
||||
throw new Error(`Provider '${options.provider}' is not supported`)
|
||||
}
|
||||
|
||||
provider = new JsonRpcProvider(networkOrEthereumURL)
|
||||
}
|
||||
|
||||
this._network = networkOrEthereumURL
|
||||
this._options = options
|
||||
this._contract = new Contract(options.address, SemaphoreABI, provider)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Ethereum network or custom URL.
|
||||
* @returns Ethereum network or custom URL.
|
||||
*/
|
||||
get network(): Network | string {
|
||||
return this._network
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Ethers options.
|
||||
* @returns Ethers options.
|
||||
*/
|
||||
get options(): EthersOptions {
|
||||
return this._options
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contract object.
|
||||
* @returns Contract object.
|
||||
*/
|
||||
get contract(): Contract {
|
||||
return this._contract
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of group ids.
|
||||
* @returns List of group ids.
|
||||
*/
|
||||
async getGroupIds(): Promise<string[]> {
|
||||
const groups = await getEvents(this._contract, "GroupCreated", [], this._options.startBlock)
|
||||
|
||||
return groups.map((event: any) => event[0].toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific group.
|
||||
* @param groupId Group id.
|
||||
* @returns Specific group.
|
||||
*/
|
||||
async getGroup(groupId: string): Promise<GroupResponse> {
|
||||
checkParameter(groupId, "groupId", "string")
|
||||
|
||||
const [groupCreatedEvent] = await getEvents(this._contract, "GroupCreated", [groupId], this._options.startBlock)
|
||||
|
||||
if (!groupCreatedEvent) {
|
||||
throw new Error(`Group '${groupId}' not found`)
|
||||
}
|
||||
|
||||
const merkleTreeRoot = await this._contract.getMerkleTreeRoot(groupId)
|
||||
const numberOfLeaves = await this._contract.getNumberOfMerkleTreeLeaves(groupId)
|
||||
|
||||
const group: GroupResponse = {
|
||||
id: groupId,
|
||||
merkleTree: {
|
||||
depth: groupCreatedEvent.merkleTreeDepth.toString(),
|
||||
zeroValue: groupCreatedEvent.zeroValue.toString(),
|
||||
numberOfLeaves: numberOfLeaves.toNumber(),
|
||||
root: merkleTreeRoot.toString()
|
||||
}
|
||||
}
|
||||
|
||||
return group
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group admin.
|
||||
* @param groupId Group id.
|
||||
* @returns Group admin.
|
||||
*/
|
||||
async getGroupAdmin(groupId: string): Promise<string> {
|
||||
checkParameter(groupId, "groupId", "string")
|
||||
|
||||
const groupAdminUpdatedEvents = await getEvents(
|
||||
this._contract,
|
||||
"GroupAdminUpdated",
|
||||
[groupId],
|
||||
this._options.startBlock
|
||||
)
|
||||
|
||||
if (groupAdminUpdatedEvents.length === 0) {
|
||||
throw new Error(`Group '${groupId}' not found`)
|
||||
}
|
||||
|
||||
return groupAdminUpdatedEvents[groupAdminUpdatedEvents.length - 1].newAdmin.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of group members.
|
||||
* @param groupId Group id.
|
||||
* @returns Group members.
|
||||
*/
|
||||
async getGroupMembers(groupId: string): Promise<string[]> {
|
||||
checkParameter(groupId, "groupId", "string")
|
||||
|
||||
const [groupCreatedEvent] = await getEvents(this._contract, "GroupCreated", [groupId], this._options.startBlock)
|
||||
|
||||
if (!groupCreatedEvent) {
|
||||
throw new Error(`Group '${groupId}' not found`)
|
||||
}
|
||||
|
||||
const zeroValue = groupCreatedEvent.zeroValue.toString()
|
||||
const memberRemovedEvents = await getEvents(
|
||||
this._contract,
|
||||
"MemberRemoved",
|
||||
[groupId],
|
||||
this._options.startBlock
|
||||
)
|
||||
const memberUpdatedEvents = await getEvents(
|
||||
this._contract,
|
||||
"MemberUpdated",
|
||||
[groupId],
|
||||
this._options.startBlock
|
||||
)
|
||||
const groupUpdates = new Map<string, [number, string]>()
|
||||
|
||||
for (const { blockNumber, index, newIdentityCommitment } of memberUpdatedEvents) {
|
||||
groupUpdates.set(index.toString(), [blockNumber, newIdentityCommitment.toString()])
|
||||
}
|
||||
|
||||
for (const { blockNumber, index } of memberRemovedEvents) {
|
||||
const groupUpdate = groupUpdates.get(index.toString())
|
||||
|
||||
if (!groupUpdate || (groupUpdate && groupUpdate[0] < blockNumber)) {
|
||||
groupUpdates.set(index.toString(), [blockNumber, zeroValue])
|
||||
}
|
||||
}
|
||||
|
||||
const memberAddedEvents = await getEvents(this._contract, "MemberAdded", [groupId], this._options.startBlock)
|
||||
const members: string[] = []
|
||||
|
||||
for (const { index, identityCommitment } of memberAddedEvents) {
|
||||
const groupUpdate = groupUpdates.get(index.toString())
|
||||
const member = groupUpdate ? groupUpdate[1].toString() : identityCommitment.toString()
|
||||
|
||||
members.push(member)
|
||||
}
|
||||
|
||||
return members
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of group verified proofs.
|
||||
* @param groupId Group id.
|
||||
* @returns Group verified proofs.
|
||||
*/
|
||||
async getGroupVerifiedProofs(groupId: string): Promise<any> {
|
||||
checkParameter(groupId, "groupId", "string")
|
||||
|
||||
const proofVerifiedEvents = await getEvents(
|
||||
this._contract,
|
||||
"ProofVerified",
|
||||
[groupId],
|
||||
this._options.startBlock
|
||||
)
|
||||
|
||||
if (proofVerifiedEvents.length === 0) {
|
||||
throw new Error(`Group '${groupId}' not found`)
|
||||
}
|
||||
|
||||
return proofVerifiedEvents.map((event) => ({
|
||||
signal: event.signal.toString(),
|
||||
merkleTreeRoot: event.merkleTreeRoot.toString(),
|
||||
externalNullifier: event.externalNullifier.toString(),
|
||||
nullifierHash: event.externalNullifier.toString()
|
||||
}))
|
||||
}
|
||||
}
|
||||
22
packages/data/src/getEvents.ts
Normal file
22
packages/data/src/getEvents.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/* istanbul ignore file */
|
||||
import { Contract } from "@ethersproject/contracts"
|
||||
|
||||
/**
|
||||
* Returns the list of events of a contract with possible filters.
|
||||
* @param contract Contract instance.
|
||||
* @param eventName Name of the event.
|
||||
* @param filterArgs Filter arguments.
|
||||
* @param startBlock Block from which to start fetching.
|
||||
* @returns List of contract events.
|
||||
*/
|
||||
export default async function getEvents(
|
||||
contract: Contract,
|
||||
eventName: string,
|
||||
filterArgs: any[] = [],
|
||||
startBlock: number = 0
|
||||
): Promise<any[]> {
|
||||
const filter = contract.filters[eventName](...filterArgs)
|
||||
const events = await contract.queryFilter(filter, startBlock)
|
||||
|
||||
return events.map(({ args, blockNumber }) => ({ ...args, blockNumber }))
|
||||
}
|
||||
@@ -8,9 +8,10 @@ import { Network } from "./types"
|
||||
export default function getURL(network: Network): string {
|
||||
switch (network) {
|
||||
case "goerli":
|
||||
return `https://api.thegraph.com/subgraphs/name/semaphore-protocol/goerli-5259d3`
|
||||
case "mumbai":
|
||||
case "optimism-goerli":
|
||||
case "arbitrum":
|
||||
return `https://api.thegraph.com/subgraphs/name/semaphore-protocol/arbitrum-86337c`
|
||||
return `https://api.studio.thegraph.com/query/14377/semaphore-${network}/v3.2.0`
|
||||
default:
|
||||
throw new TypeError(`Network '${network}' is not supported`)
|
||||
}
|
||||
5
packages/data/src/index.ts
Normal file
5
packages/data/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import SemaphoreSubgraph from "./subgraph"
|
||||
import SemaphoreEthers from "./ethers"
|
||||
|
||||
export { SemaphoreSubgraph, SemaphoreEthers }
|
||||
export * from "./types"
|
||||
@@ -8,7 +8,13 @@ import axios, { AxiosRequestConfig, AxiosResponse } from "axios"
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
export default async function request(url: string, config?: AxiosRequestConfig): Promise<any> {
|
||||
const { data }: AxiosResponse<any> = await axios(url, config)
|
||||
const { data }: AxiosResponse<any> = await axios(url, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...config?.headers
|
||||
},
|
||||
...config
|
||||
})
|
||||
|
||||
return data?.data
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user