mirror of
https://github.com/semaphore-protocol/semaphore.git
synced 2026-01-11 15:48:12 -05:00
Compare commits
316 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05adc1e2fe | ||
|
|
971b354e1c | ||
|
|
fa1e86c3b0 | ||
|
|
621f56a0c6 | ||
|
|
5470804e1c | ||
|
|
17e683debe | ||
|
|
29090843aa | ||
|
|
d7c677bb05 | ||
|
|
16ebaa61e5 | ||
|
|
52581a6d56 | ||
|
|
43803ec21d | ||
|
|
79313cbf6c | ||
|
|
36c3252b0c | ||
|
|
168215d23e | ||
|
|
75bef955ab | ||
|
|
68a65f9645 | ||
|
|
13b724a34f | ||
|
|
073ec22b34 | ||
|
|
5d6f7b0629 | ||
|
|
75fc12f867 | ||
|
|
e21ccedc15 | ||
|
|
68187531a5 | ||
|
|
3dba37fd06 | ||
|
|
17234ddfd2 | ||
|
|
536cf452bb | ||
|
|
efe56e80ac | ||
|
|
f09e930857 | ||
|
|
2e5650a5b8 | ||
|
|
7c3763c012 | ||
|
|
e67cf5d75b | ||
|
|
5a99b66f18 | ||
|
|
df9286a455 | ||
|
|
f4dd5ba694 | ||
|
|
8c49c4ed14 | ||
|
|
1f805543e8 | ||
|
|
a674301faf | ||
|
|
12e92840a4 | ||
|
|
ac7f03a695 | ||
|
|
20036c3739 | ||
|
|
8826ddb47b | ||
|
|
c074930aad | ||
|
|
f2a1d50603 | ||
|
|
3dc63c83bb | ||
|
|
0f62fe1356 | ||
|
|
10562ca54f | ||
|
|
f237a44980 | ||
|
|
ec99ab0759 | ||
|
|
7c48005647 | ||
|
|
3b61ceebde | ||
|
|
0d43e0b4c8 | ||
|
|
6cf990bc22 | ||
|
|
9875778ad9 | ||
|
|
6ef014fa7c | ||
|
|
06fbe09823 | ||
|
|
80d3ffb622 | ||
|
|
52dbe8b14e | ||
|
|
c4207c73b0 | ||
|
|
45a74798c7 | ||
|
|
5e7c3a5ae9 | ||
|
|
4975db4f58 | ||
|
|
aa96f0bbc1 | ||
|
|
6e5c6fc84c | ||
|
|
8c1119dba1 | ||
|
|
68f4bead0e | ||
|
|
bd78a71387 | ||
|
|
e8f34ea272 | ||
|
|
2a4e92449b | ||
|
|
57b61614c4 | ||
|
|
45f48799f0 | ||
|
|
42c934ec1b | ||
|
|
35822a480e | ||
|
|
56edbb6cd7 | ||
|
|
d0962fda19 | ||
|
|
d17c4a6e0c | ||
|
|
4080f4af20 | ||
|
|
9e74e1a278 | ||
|
|
a8e0c1474c | ||
|
|
d1fcf8bfe2 | ||
|
|
5a895f42f8 | ||
|
|
8a44757417 | ||
|
|
ad1d77b200 | ||
|
|
c31b9490ed | ||
|
|
4aeeb3e1cc | ||
|
|
23dac4c6ed | ||
|
|
4c4bfb568a | ||
|
|
8595e3cd0f | ||
|
|
01a5b156bb | ||
|
|
dd19a5e213 | ||
|
|
9e2b54ef16 | ||
|
|
51fe424473 | ||
|
|
5dbfaf3518 | ||
|
|
4e18efb75c | ||
|
|
55a89753bb | ||
|
|
8105d9761b | ||
|
|
55cef34142 | ||
|
|
bf42264033 | ||
|
|
0035a62d7a | ||
|
|
d34bded3cc | ||
|
|
e156cf77c2 | ||
|
|
4d662a4a0d | ||
|
|
67e907da2c | ||
|
|
7771f15e5a | ||
|
|
aff0c87366 | ||
|
|
77bc53d9a7 | ||
|
|
2965d8decf | ||
|
|
528ad6ef4e | ||
|
|
bd2145dc09 | ||
|
|
67dd25e604 | ||
|
|
25379748e6 | ||
|
|
9e6c0df6a2 | ||
|
|
07cbe28701 | ||
|
|
d212947d07 | ||
|
|
bdffdfe200 | ||
|
|
a8922efae1 | ||
|
|
4ee9913035 | ||
|
|
04df22ed56 | ||
|
|
abc8ef3cf6 | ||
|
|
c2a8aee15e | ||
|
|
292ce60b81 | ||
|
|
3a64ab44dd | ||
|
|
d597b461a7 | ||
|
|
a7a42f8d2c | ||
|
|
9f7aa7955f | ||
|
|
2ffaf28a12 | ||
|
|
bfaeb19d14 | ||
|
|
aed9fa6785 | ||
|
|
ac1ad33bfd | ||
|
|
e0e366c52a | ||
|
|
e6f7df17a9 | ||
|
|
c610ddb45a | ||
|
|
8c7d09a5c1 | ||
|
|
8d5968a3c0 | ||
|
|
936e0d76a6 | ||
|
|
b5389fed3d | ||
|
|
5f1ed4e08e | ||
|
|
37bb2976b8 | ||
|
|
17e6330798 | ||
|
|
a4ecc5a125 | ||
|
|
9bfe26d81d | ||
|
|
9756917fc7 | ||
|
|
83e29909c3 | ||
|
|
ae22bd7be9 | ||
|
|
3295483a24 | ||
|
|
b21a17ea70 | ||
|
|
d1419045e3 | ||
|
|
c136ba809f | ||
|
|
bf87f4a3af | ||
|
|
7ddd0e0920 | ||
|
|
498d14cde1 | ||
|
|
91867d53aa | ||
|
|
dbc9ae303d | ||
|
|
0c9d717fa8 | ||
|
|
a6256d2cdb | ||
|
|
9944eb3746 | ||
|
|
d5fd1f99a3 | ||
|
|
71dc72daa8 | ||
|
|
7dd398ebd9 | ||
|
|
28990a27f3 | ||
|
|
7993440e53 | ||
|
|
05c26c108c | ||
|
|
940e6fcb75 | ||
|
|
31e885a075 | ||
|
|
d37deffb73 | ||
|
|
9285fe54e6 | ||
|
|
7095e03162 | ||
|
|
ce09c9e4dd | ||
|
|
17d616e0ba | ||
|
|
af6f040429 | ||
|
|
eeb65e4639 | ||
|
|
aa544bf2c9 | ||
|
|
967e93789e | ||
|
|
ec5c6a1c23 | ||
|
|
79a6bfcbf7 | ||
|
|
a08c5460c3 | ||
|
|
1259d8e53e | ||
|
|
bea3aca4a9 | ||
|
|
04e89d83e0 | ||
|
|
83798c959e | ||
|
|
23a9fe78bf | ||
|
|
108160740d | ||
|
|
fdc66d3769 | ||
|
|
bad1176c60 | ||
|
|
6d8511c35c | ||
|
|
ec48e81fe0 | ||
|
|
bf7dda0666 | ||
|
|
ccb1d762c4 | ||
|
|
6ecf6c20af | ||
|
|
e575c6025b | ||
|
|
1f4120f30d | ||
|
|
ea6ff9d6a9 | ||
|
|
ec7387f1c5 | ||
|
|
04e716a917 | ||
|
|
3efea8c2bf | ||
|
|
a38c5b4360 | ||
|
|
fd7342f5b3 | ||
|
|
7ca5de85d6 | ||
|
|
6d313e194e | ||
|
|
e9ed84c868 | ||
|
|
010726618e | ||
|
|
df0c87f5e7 | ||
|
|
55079c25a9 | ||
|
|
1242e73d34 | ||
|
|
1be081c10a | ||
|
|
4a4ef3d3bf | ||
|
|
ba02e7719f | ||
|
|
406424c0a3 | ||
|
|
633a1a7a19 | ||
|
|
3680cc294d | ||
|
|
6d51e94a88 | ||
|
|
9bd085195f | ||
|
|
0826bbcfa3 | ||
|
|
4c643f012b | ||
|
|
1a3d35662c | ||
|
|
064aa99b3d | ||
|
|
eec2ff5c1f | ||
|
|
7527f159ad | ||
|
|
f06a12535f | ||
|
|
7ad7acfb54 | ||
|
|
76092003f7 | ||
|
|
817783e797 | ||
|
|
14a7493cc8 | ||
|
|
f15ba8fcda | ||
|
|
4e04ba2413 | ||
|
|
5b9a4aaceb | ||
|
|
af9b07aa20 | ||
|
|
1ca9e568c4 | ||
|
|
c21d0b6e6b | ||
|
|
56716cb258 | ||
|
|
04b585021f | ||
|
|
2a44bd6c75 | ||
|
|
7d54825ba1 | ||
|
|
0900615ae4 | ||
|
|
6837dd3cbd | ||
|
|
7888aebcd7 | ||
|
|
a6cf53520b | ||
|
|
0b66144c76 | ||
|
|
9f931d1f19 | ||
|
|
db8da587ab | ||
|
|
96b5e10d75 | ||
|
|
e15fd3174e | ||
|
|
b0bbb981a0 | ||
|
|
d7dc8bdd60 | ||
|
|
21b7ccabd3 | ||
|
|
778d5a1574 | ||
|
|
e7c515aabc | ||
|
|
a77fcf848f | ||
|
|
1c91e59f71 | ||
|
|
19e54ac348 | ||
|
|
b7b8190edc | ||
|
|
ead549e9f1 | ||
|
|
55938b0eaa | ||
|
|
e614cc68ac | ||
|
|
3a94fb72f9 | ||
|
|
1772e776a7 | ||
|
|
e6a5d1f4b6 | ||
|
|
5abfed2b77 | ||
|
|
82c4dea962 | ||
|
|
544046a392 | ||
|
|
541cd4298b | ||
|
|
67c6af125e | ||
|
|
a11449493b | ||
|
|
42031746fa | ||
|
|
ca2d5a193c | ||
|
|
3602219bb6 | ||
|
|
710e546eee | ||
|
|
882191996c | ||
|
|
a9630e436b | ||
|
|
b524655db1 | ||
|
|
7dead05108 | ||
|
|
ed355829cd | ||
|
|
c37496cf00 | ||
|
|
1be8ac5df5 | ||
|
|
bc8362f4f0 | ||
|
|
e84264b24d | ||
|
|
970b19257f | ||
|
|
421b7da0ba | ||
|
|
665ab42870 | ||
|
|
69c25d4b05 | ||
|
|
37337ccff6 | ||
|
|
583cb2fb2a | ||
|
|
d5a3781d01 | ||
|
|
4d6045a01c | ||
|
|
acf5d7dc98 | ||
|
|
358dd69753 | ||
|
|
57be569adb | ||
|
|
948685edf2 | ||
|
|
548d7bbe31 | ||
|
|
ceb20422b7 | ||
|
|
8e41b6cffd | ||
|
|
a4378925ac | ||
|
|
8f0c937023 | ||
|
|
ad6873bfb3 | ||
|
|
2a5ebeb6fb | ||
|
|
bc4b0acefd | ||
|
|
3824857b04 | ||
|
|
d945172ce9 | ||
|
|
8f7214a921 | ||
|
|
4510809a1b | ||
|
|
c7ca8dc02d | ||
|
|
589ade76d5 | ||
|
|
36d634c47e | ||
|
|
dabd686b1f | ||
|
|
5f8ad2b71b | ||
|
|
f4734cf9ce | ||
|
|
12d54003c5 | ||
|
|
cfa98a9833 | ||
|
|
01921268fb | ||
|
|
93448311f5 | ||
|
|
6ed742771a | ||
|
|
86e22c8cb9 | ||
|
|
dd435e0bf9 | ||
|
|
6d26f0d39e | ||
|
|
c01132b535 | ||
|
|
f94e88c1af | ||
|
|
0af2ccc8c9 | ||
|
|
99922c3a76 |
@@ -1,66 +0,0 @@
|
||||
# Javascript Node CircleCI 2.0 configuration file
|
||||
#
|
||||
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
|
||||
#
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
# specify the version you desire here
|
||||
- image: circleci/node:11.14.0
|
||||
|
||||
working_directory: ~/semaphore/semaphorejs
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/semaphore
|
||||
|
||||
# restore or install npm packages
|
||||
- restore_cache:
|
||||
name: restore-npm-cache
|
||||
keys:
|
||||
- v1.10-dependencies-{{ checksum "package-lock.json" }}
|
||||
|
||||
- run: npm install
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- node_modules
|
||||
key: v1.10-dependencies-{{ checksum "package-lock.json" }}
|
||||
|
||||
# checksum the snarks definitions
|
||||
- run:
|
||||
name: checksum-snarks
|
||||
command: cd scripts && ./checksum_snarks.sh
|
||||
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
name: restore-snark-cache
|
||||
keys:
|
||||
- v1.10-dependencies-{{ checksum "build/.snark_checksum" }}
|
||||
|
||||
# build snarks
|
||||
- run:
|
||||
name: end-to-end
|
||||
command: cd scripts && ./build_snarks.sh
|
||||
no_output_timeout: 600m
|
||||
|
||||
# cache generated snark circuit and keys
|
||||
- save_cache:
|
||||
key: v1.10-dependencies-{{ checksum "build/.snark_checksum" }}
|
||||
paths:
|
||||
- build/circuit.json
|
||||
- build/proving_key.bin
|
||||
- build/proving_key.json
|
||||
- build/verification_key.json
|
||||
- build/verifier.sol
|
||||
|
||||
# build snarks
|
||||
- run:
|
||||
name: integration_tests
|
||||
command: cd scripts && ./integration_tests.sh
|
||||
no_output_timeout: 600m
|
||||
|
||||
- store_artifacts:
|
||||
path: ~/semaphore/semaphorejs/build
|
||||
|
||||
3
.commitlintrc.json
Normal file
3
.commitlintrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": ["@commitlint/config-conventional"]
|
||||
}
|
||||
13
.editorconfig
Normal file
13
.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
#root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 120
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
7
.env.example
Normal file
7
.env.example
Normal file
@@ -0,0 +1,7 @@
|
||||
DEFAULT_NETWORK=hardhat
|
||||
TREE_DEPTH=20
|
||||
REPORT_GAS=false
|
||||
BACKEND_PRIVATE_KEY=
|
||||
INFURA_API_KEY=
|
||||
COINMARKETCAP_API_KEY=
|
||||
ETHERSCAN_API_KEY=
|
||||
31
.eslintignore
Normal file
31
.eslintignore
Normal file
@@ -0,0 +1,31 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.yarn
|
||||
|
||||
# testing
|
||||
coverage
|
||||
coverage.json
|
||||
|
||||
# hardhat
|
||||
cache
|
||||
|
||||
# types
|
||||
types
|
||||
|
||||
# circuits
|
||||
circuits
|
||||
|
||||
# production
|
||||
dist
|
||||
build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
11
.eslintrc.json
Normal file
11
.eslintrc.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"env": {
|
||||
"es6": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "tsconfig.json"
|
||||
},
|
||||
"plugins": ["@typescript-eslint"]
|
||||
}
|
||||
18
.github/ISSUE_TEMPLATE/----network.md
vendored
Normal file
18
.github/ISSUE_TEMPLATE/----network.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: "\U0001F578️ Network"
|
||||
about: Propose a new network in which to deploy Semaphore contracts
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Provide data related to the network**
|
||||
|
||||
* Name
|
||||
* Chain id
|
||||
* URL
|
||||
|
||||
**Why are you using this network?**
|
||||
|
||||
Describe the reasons why you think it is important for Semaphore to be deployed on this network.
|
||||
22
.github/ISSUE_TEMPLATE/----project.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/----project.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
name: " \U0001F4A0 Project"
|
||||
about: If you are using Semaphore we can help you share your project
|
||||
title: ''
|
||||
labels: "documentation \U0001F4D6"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe your project**
|
||||
A brief description of your project. In what way have you used Semaphore?
|
||||
|
||||
**Other info**
|
||||
|
||||
- Name
|
||||
- Icon
|
||||
|
||||
**Links**
|
||||
|
||||
- Website
|
||||
- Github
|
||||
- Socials
|
||||
34
.github/ISSUE_TEMPLATE/---bug.md
vendored
Normal file
34
.github/ISSUE_TEMPLATE/---bug.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: "\U0001F41E Bug"
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: "bug \U0001F41B"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Technologies (please complete the following information):**
|
||||
|
||||
- Node.js version
|
||||
- NPM version
|
||||
- Solidity version
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
.github/ISSUE_TEMPLATE/---feature.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/---feature.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: "\U0001F680 Feature"
|
||||
about: Suggest an idea for Semaphore
|
||||
title: ''
|
||||
labels: 'feature :rocket:'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
24
.github/pull_request_template.md
vendored
Normal file
24
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<!-- Please refer to our contributing documentation for any questions on submitting a pull request -->
|
||||
<!--- Provide a general summary of your changes in the Title above -->
|
||||
|
||||
## Description
|
||||
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
## Related Issue
|
||||
|
||||
<!--- This project accepts pull requests related to open issues -->
|
||||
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
|
||||
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
|
||||
<!--- Please link to the issue here: -->
|
||||
|
||||
## Does this introduce a breaking change?
|
||||
|
||||
- [ ] Yes
|
||||
- [ ] No
|
||||
|
||||
<!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. -->
|
||||
|
||||
## Other information
|
||||
|
||||
<!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. -->
|
||||
50
.github/workflows/coverall.yml
vendored
Normal file
50
.github/workflows/coverall.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: coverall
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
TREE_DEPTH: 20
|
||||
|
||||
jobs:
|
||||
coverall:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
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_CHECKSUM_BEHAVIOR=ignore yarn
|
||||
|
||||
- name: Download Snark artifacts
|
||||
run: yarn download:snark-artifacts
|
||||
|
||||
- name: Compile contracts
|
||||
run: yarn compile
|
||||
|
||||
- name: Test contracts with coverage
|
||||
run: yarn test:coverage
|
||||
|
||||
- uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
44
.github/workflows/style.yml
vendored
Normal file
44
.github/workflows/style.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: style
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
style:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
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_CHECKSUM_BEHAVIOR=ignore yarn
|
||||
|
||||
- name: Run Prettier
|
||||
run: yarn prettier
|
||||
|
||||
- name: Run Eslint
|
||||
run: yarn lint
|
||||
|
||||
- name: Compile contracts
|
||||
run: yarn compile
|
||||
47
.github/workflows/test.yml
vendored
Normal file
47
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
TREE_DEPTH: 20
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
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_CHECKSUM_BEHAVIOR=ignore yarn
|
||||
|
||||
- name: Download Snark artifacts
|
||||
run: yarn download:snark-artifacts
|
||||
|
||||
- name: Compile contracts
|
||||
run: yarn compile
|
||||
|
||||
- name: Test contracts with coverage
|
||||
run: yarn test:coverage
|
||||
74
.gitignore
vendored
Normal file
74
.gitignore
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
coverage.json
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# Production
|
||||
build
|
||||
dist
|
||||
deployed-contracts/undefined.json
|
||||
deployed-contracts/localhost.json
|
||||
|
||||
# Hardhat
|
||||
artifacts
|
||||
cache
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v3
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
3
.lintstagedrc.json
Normal file
3
.lintstagedrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"**/*.{js,ts}": ["prettier --write", "eslint --fix"]
|
||||
}
|
||||
37
.prettierignore
Normal file
37
.prettierignore
Normal file
@@ -0,0 +1,37 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.yarn
|
||||
|
||||
# testing
|
||||
coverage
|
||||
coverage.json
|
||||
|
||||
# hardhat
|
||||
cache
|
||||
|
||||
# types
|
||||
types
|
||||
|
||||
# circuits
|
||||
circuits
|
||||
|
||||
# contracts
|
||||
contracts/verifiers
|
||||
|
||||
# production
|
||||
dist
|
||||
build
|
||||
|
||||
# github
|
||||
.github/ISSUE_TEMPLATE
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
5
.prettierrc.json
Normal file
5
.prettierrc.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"semi": false,
|
||||
"arrowParens": "always",
|
||||
"trailingComma": "none"
|
||||
}
|
||||
21
.solcover.js
Normal file
21
.solcover.js
Normal file
@@ -0,0 +1,21 @@
|
||||
module.exports = {
|
||||
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"
|
||||
]
|
||||
}
|
||||
21
.solhint.json
Normal file
21
.solhint.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"extends": "solhint:recommended",
|
||||
"plugins": ["prettier"],
|
||||
"rules": {
|
||||
"code-complexity": ["error", 7],
|
||||
"compiler-version": ["error", ">=0.8.0"],
|
||||
"const-name-snakecase": "off",
|
||||
"no-empty-blocks": "off",
|
||||
"constructor-syntax": "error",
|
||||
"func-visibility": ["error", { "ignoreConstructors": true }],
|
||||
"max-line-length": ["error", 120],
|
||||
"not-rely-on-time": "off",
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
],
|
||||
"reason-string": ["warn", { "maxLength": 80 }]
|
||||
}
|
||||
}
|
||||
1
.solhintignore
Normal file
1
.solhintignore
Normal file
@@ -0,0 +1 @@
|
||||
contracts/verifiers
|
||||
1
.yarn/releases/yarn-3.2.1.cjs.REMOVED.git-id
vendored
Normal file
1
.yarn/releases/yarn-3.2.1.cjs.REMOVED.git-id
vendored
Normal file
@@ -0,0 +1 @@
|
||||
b3cadff6efb37a12712d12c2553ec703dbcaa4dd
|
||||
3
.yarnrc.yml
Normal file
3
.yarnrc.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.1.cjs
|
||||
127
CODE_OF_CONDUCT.md
Normal file
127
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
111
CONTRIBUTING.md
Normal file
111
CONTRIBUTING.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Contributing
|
||||
|
||||
:tada: Thank you for being interested in contributing to the Semaphore project! :tada:
|
||||
|
||||
Feel welcome and read the following sections in order to know how to ask questions and how to work on something.
|
||||
|
||||
All members of our community are expected to follow our [Code of Conduct](/CODE_OF_CONDUCT.md). Please make sure you are welcoming and friendly in all of our spaces.
|
||||
|
||||
We're really glad you're reading this, because we need volunteer developers to help this project come to fruition. 👏
|
||||
|
||||
## Issues
|
||||
|
||||
The best way to contribute to our projects is by opening a [new issue](https://github.com/semaphore-protocol/semaphore/issues/new/choose) or tackling one of the issues listed [here](https://github.com/semaphore-protocol/semaphore/contribute).
|
||||
|
||||
## Pull Requests
|
||||
|
||||
Pull requests are great if you want to add a feature or fix a bug. Here's a quick guide:
|
||||
|
||||
1. Fork the repo.
|
||||
|
||||
2. Run the tests. We only take pull requests with passing tests.
|
||||
|
||||
3. Add a test for your change. Only refactoring and documentation changes require no new tests.
|
||||
|
||||
4. Make sure to check out the [Style Guide](/CONTRIBUTING#style-guide) and ensure that your code complies with the rules.
|
||||
|
||||
5. Make the test pass.
|
||||
|
||||
6. Commit your changes.
|
||||
|
||||
7. Push to your fork and submit a pull request on our `dev` branch. Please provide us with some explanation of why you made the changes you made. For new features make sure to explain a standard use case to us.
|
||||
|
||||
## CI (Github Actions) Tests
|
||||
|
||||
We use GitHub Actions to test each PR before it is merged.
|
||||
|
||||
When you submit your PR (or later change that code), a CI build will automatically be kicked off. A note will be added to the PR, and will indicate the current status of the build.
|
||||
|
||||
## Style Guide
|
||||
|
||||
### Code rules
|
||||
|
||||
We always use ESLint and Prettier. To check that your code follows the rules, simply run the npm script `yarn lint`.
|
||||
|
||||
### Commits rules
|
||||
|
||||
For commits it is recommended to use [Conventional Commits](https://www.conventionalcommits.org).
|
||||
|
||||
Don't worry if it looks complicated, in our repositories, after `git add`, you can usually run the npm script `yarn commit` to make many of these steps interactive.
|
||||
|
||||
Each commit message consists of a **header**, a **body** and a **footer**. The **header** has a special format that includes a **type**, a **scope** and a **subject**:
|
||||
|
||||
<type>(<scope>): <subject>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
|
||||
The **header** is mandatory and the **scope** of the header is optional.
|
||||
|
||||
#### Type
|
||||
|
||||
The type must be one of the following:
|
||||
|
||||
- feat: A new feature
|
||||
- fix: A bug fix
|
||||
- docs: Documentation only changes
|
||||
- style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
- refactor: A code change that neither fixes a bug nor adds a feature (improvements of the code structure)
|
||||
- perf: A code change that improves the performance
|
||||
- test: Adding missing or correcting existing tests
|
||||
- build: Changes that affect the build system or external dependencies (example scopes: gulp, npm)
|
||||
- ci: Changes to CI configuration files and scripts (example scopes: travis, circle)
|
||||
- chore: Other changes that don't modify src or test files
|
||||
- revert: Reverts a previous commit
|
||||
|
||||
#### Scope
|
||||
|
||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
|
||||
|
||||
#### Subject
|
||||
|
||||
The subject contains a succinct description of the change:
|
||||
|
||||
- Use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
- Don't capitalize the first letter
|
||||
- No dot (.) at the end
|
||||
|
||||
#### Body
|
||||
|
||||
Just as in the subject, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
|
||||
|
||||
### Branch rules
|
||||
|
||||
- There must be a `main` branch, used only for the releases.
|
||||
- There must be a `dev` branch, used to merge all the branches under it.
|
||||
- Avoid long descriptive names for long-lived branches.
|
||||
- Use kebab-case (no CamelCase).
|
||||
- Use grouping tokens (words) at the beginning of your branch names (in a similar way to the `type` of commit).
|
||||
- Define and use short lead tokens to differentiate branches in a way that is meaningful to your workflow.
|
||||
- Use slashes to separate parts of your branch names.
|
||||
- Remove branch after merge if it is not important.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
git branch -b docs/readme
|
||||
git branch -b test/a-feature
|
||||
git branch -b feat/sidebar
|
||||
git branch -b fix/b-feature
|
||||
```
|
||||
21
LICENSE
Normal file
21
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.
|
||||
250
README.md
250
README.md
@@ -1,169 +1,179 @@
|
||||
# Semaphore
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
<img width="40" src="https://github.com/semaphore-protocol/website/blob/main/static/img/semaphore-icon.svg">
|
||||
Semaphore
|
||||
</h1>
|
||||
</p>
|
||||
|
||||
[](https://circleci.com/gh/kobigurk/semaphore)
|
||||
<p align="center">
|
||||
<a href="https://github.com/semaphore-protocol" target="_blank">
|
||||
<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://github.com/semaphore-protocol/semaphore/actions?query=workflow%3Atest">
|
||||
<img alt="GitHub Workflow test" src="https://img.shields.io/github/workflow/status/semaphore-protocol/semaphore/test?label=test&style=flat-square&logo=github">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/actions?query=workflow%3Astyle">
|
||||
<img alt="GitHub Workflow style" src="https://img.shields.io/github/workflow/status/semaphore-protocol/semaphore/style?label=style&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">
|
||||
</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>
|
||||
<img alt="Repository top language" src="https://img.shields.io/github/languages/top/semaphore-protocol/semaphore?style=flat-square">
|
||||
</p>
|
||||
|
||||
Join the [Telegram group](https://t.me/joinchat/B-PQx1U3GtAh--Z4Fwo56A) to discuss.
|
||||
<div align="center">
|
||||
<h4>
|
||||
<a href="/CONTRIBUTING.md">
|
||||
👥 Contributing
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="/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://t.me/joinchat/B-PQx1U3GtAh--Z4Fwo56A">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
## Introduction
|
||||
| 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. |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
Semaphore has been introduced by [barryWhiteHat](https://github.com/barryWhiteHat) as a method of zero-knowledge signaling - a method for an approved user to broadcast an arbitrary string without exposing their identity. This repository is an implementation of an upgraded version of the concept, including the zero-knowledge circuits and the tools necessary to use it, both server-side and client-side.
|
||||
The core of the Semaphore protocol is in the [circuit logic](/circuits/scheme.png). However Semaphore also provides [Solidity contracts](/contracts) (NPM: `@semaphore-protocol/contracts`) and [JavaScript libraries](https://github.com/semaphore-protocol/semaphore.js) 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).
|
||||
|
||||
The project is implemented in plain Node.JS and uses [circom](https://github.com/iden3/circom) for the zero-knowledge proofs.
|
||||
You can find Semaphore V1 on [`version/1.0.0`](https://github.com/semaphore-protocol/semaphore/tree/version/1.0.0).
|
||||
|
||||
## Design
|
||||
Semaphore is comprised of a zkSNARK statement, a few smart contracts, a server application and a client application.
|
||||
---
|
||||
|
||||
### Smart contracts
|
||||
Implemented in [**semaphorejs/contracts**](semaphorejs/contracts).
|
||||
## 🛠 Install
|
||||
|
||||
#### Semaphore
|
||||
The Semaphore contract is the base layer of Semaphore. Other contracts can build upon this to create applications that rely on anonymous signaling. Semaphore has a tree of allowed identities, a tree of signals, a set of previously broadcast nullifiers hashes, an external nullifier and a gas price refund price:
|
||||
Clone this repository:
|
||||
|
||||
* The tree of allowed identities allows a prover to show that they are an identity which is approved to signal.
|
||||
* The tree of signals allows a user to verify the integrity of a list of signals.
|
||||
* The nullifiers hashes set and external nullifier allows the contract to prevent double signals by the same user, without exposing the specific user.
|
||||
* The gas price refund price is a mechanism that supports transaction abstraction - a server can broadcast on behalf of a user to provide further anonymity, and in return they receive a refund and a small reward, with a maximum gas price for their transaction.
|
||||
```bash
|
||||
git clone https://github.com/semaphore-protocol/semaphore.git
|
||||
```
|
||||
|
||||
The contract allows administrative operations that only the owner is allowed to perform:
|
||||
and install the dependencies:
|
||||
|
||||
* Managing identities using the **insertIdentity** and **updateIdentity** methods.
|
||||
* Setting the **external_nullifier**.
|
||||
* Setting the broadcast permissioning - whether only the owner can broadcast.
|
||||
```bash
|
||||
cd semaphore && yarn
|
||||
```
|
||||
|
||||
The contract allows anyone to read the current state:
|
||||
## 📜 Usage
|
||||
|
||||
* Reading the roots of the two trees.
|
||||
* Reading the current parameters of **external_nullifier**.
|
||||
Copy the `.env.example` file as `.env`:
|
||||
|
||||
The contract allows anyone to attempt broadcasting a signal, given a signal, a proof and the relevant public inputs.
|
||||
The contract allows anyone to fund the contract for gas refund and rewards.
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Lastly, the contract has a few events to allow a server to build a local state to serve users wishing to generate proofs:
|
||||
and add your environment variables.
|
||||
|
||||
* **Funded** - when the contract has received some funding for refunds and rewards.
|
||||
* **SignalBroadcast** - when a signal has been broadcast successfully, after verification of the proof, the public inputs and double-signaling checks.
|
||||
* **LeafAdded**, **LeafUpdated** (from MerkleTreeLib) - when the trees have been updated.
|
||||
### Code quality and formatting
|
||||
|
||||
Run [ESLint](https://eslint.org/) to analyze the code and catch bugs:
|
||||
|
||||
#### MerkleTreeLib
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
Manages a number of append-only Merkle trees with efficient inserts and updates.
|
||||
Run [Prettier](https://prettier.io/) to check formatting rules:
|
||||
|
||||
### zkSNARK statement
|
||||
Implemented in [**semaphorejs/snark**](semaphorejs/snark).
|
||||
```bash
|
||||
yarn prettier
|
||||
```
|
||||
|
||||
The statement assures that given public inputs:
|
||||
or to automatically format the code:
|
||||
|
||||
* **signal_hash**
|
||||
* **external_nullifier**
|
||||
* **broadcaster_address**
|
||||
* **root**
|
||||
* **nullifiers_hash**
|
||||
```bash
|
||||
yarn prettier:write
|
||||
```
|
||||
|
||||
and private inputs:
|
||||
* **identity_pk**
|
||||
* **identity_nullifier**
|
||||
* **identity_trapdoor**
|
||||
* **identity_path_elements**
|
||||
* **identity_path_index**
|
||||
* **auth_sig_r**
|
||||
* **auth_sig_s**
|
||||
### Conventional commits
|
||||
|
||||
the following conditions hold:
|
||||
Semaphore uses [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). A [command line utility](https://github.com/commitizen/cz-cli) to commit using the correct syntax can be used by running:
|
||||
|
||||
* The commitment of the identity structure (**identity_pk**, **identity_nullifier**, **identity_trapdoor**) exists in the identity tree with the root **root**, using the path (**identity_path_elements**, **identity_path_index**). This ensures that the user was added to the system at some point in the past.
|
||||
* **nullifiers_hash** is uniquely derived from **external_nullifier**, **identity_nullifier** and **identity_path_index**. This ensures a user cannot broadcast a signal with the same **external_nullifier** more than once.
|
||||
* The message (**external_nullifier**, **signal_hash**, **broadcaster_address**) is signed by the secret key corresponding to **identity_pk**, having the signature (**auth_sig_r**, **auth_sig_s**). This ensures that the user approves the signal broadcast by a specific **broadcaster_address**, preventing front-running attacks, and a specific state of the contract having a specific **external_nullifier**, ensuring no double-signaling.
|
||||
```bash
|
||||
yarn commit
|
||||
```
|
||||
|
||||
#### Cryptographic primitives
|
||||
It will also automatically check that the modified files comply with ESLint and Prettier rules.
|
||||
|
||||
Semaphore uses a few cryptographic primitives provided by circomlib:
|
||||
### Snark artifacts
|
||||
|
||||
* MiMCHash for the Merkle tree, the identity commitments and the message hash in the signature.
|
||||
* EdDSA for the signature.
|
||||
Download the Semaphore snark artifacts needed to generate and verify proofs:
|
||||
|
||||
Note: MiMCHash, and especially the specific paramteres used in the circuit, have not been heavily audited yet by the cryptography community. Additionally, the circuit and code should also receive further review before relying on it for production applications.
|
||||
```bash
|
||||
yarn download:snark-artifacts
|
||||
```
|
||||
|
||||
### Server
|
||||
### Compile contracts
|
||||
|
||||
Implemented in [**semaphorejs/src/server/server.js**](semaphorejs/src/server/server.js). Acts as a manager of the identities merkle tree and as an identity onboarder. The REST API allows:
|
||||
Compile the smart contracts with [Hardhat](https://hardhat.org/):
|
||||
|
||||
* An owner to submit a transaction that adds an identity to the merkle tree, provided proper authentication.
|
||||
* A client to ask for a path from an identity commitment to the current root of the tree, relieving the client from the need to manage this tree by themselves.
|
||||
* A client to ask a list of signals, together with their paths to the signals tree root.
|
||||
* An owner to set the external nullifier.
|
||||
```bash
|
||||
yarn compile
|
||||
```
|
||||
|
||||
The server relies on an Ethereum node and the events in the smart contract to synchronize to the current state and handle rollbacks if they occur.
|
||||
### Testing
|
||||
|
||||
It uses [**semaphore-merkle-tree**](https://github.com/weijiekoh/semaphore-merkle-tree) - Semaphore requires managing a growing merkle tree containing the identities allowed to signal and the signals broadcast by users. semaphore-merkle-tree manages the trees either in-memory, for browser usage or a database, making the tree scale by the disk size.
|
||||
Run [Mocha](https://mochajs.org/) to test the contracts:
|
||||
|
||||
### Client
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
Implemented in [**src/client/client.js**](semaphorejs/src/client/client.js). Enables signaling a user's support of an arbitrary statemnt, given identity secrets of an identity existing in the tree. The client has 2 CLI functions:
|
||||
You can also generate a test coverage report:
|
||||
|
||||
* **generate_identity** - generate random identity secrets and randomness, save them to disk and print the identity commitment. The client can then send the commitment to the onboarder (using another channel), requesting they add them to the tree.
|
||||
* **signal STRING** - given an arbitrary string, generates a zero-knowledge proof of the client's authorization to signal. The signalling requests the path of the identity commitment from the server, and broadcasts the transaction directly to the contract.
|
||||
```bash
|
||||
yarn test:coverage
|
||||
```
|
||||
|
||||
### Web
|
||||
or a test gas report:
|
||||
|
||||
A web interface to run the server and client APIs, generate identities and proofs directly in the browser and broadcast signals.
|
||||
```bash
|
||||
yarn test:report-gas
|
||||
```
|
||||
|
||||
## Running modes
|
||||
### Deploy contracts
|
||||
|
||||
Schematically, Semaphore has the following actors:
|
||||
Deploy a verifier contract with depth = 20:
|
||||
|
||||

|
||||
```bash
|
||||
yarn deploy:verifier --depth 20
|
||||
```
|
||||
|
||||
There are 3 main running modes:
|
||||
Deploy the `Semaphore.sol` contract with one verifier:
|
||||
|
||||
* One server is the owner of the Semaphore contract, other servers act as intermediate miners and clients uses them to broadcast their signals. This the lightest running mode for clients, as they rely on servers to provide them with tree paths, a list of signals and broadcast. This is the default mode that is exposed in the CLI and web clients.
|
||||
* One server is the owner of the Semaphore contract and clients broadcast directly. This is still a light running mode for clients, and is less prone to server censorship, at the cost of less anonymity - as the user's Ethereum address is exposed.
|
||||
* One server is the owner of the Semaphore contract and clients run a server locally. This is a heavier running mode, allowing the clients to run autonomously, reconstructing the signals and identities states locally.
|
||||
```bash
|
||||
yarn deploy:semaphore --verifiers '[{"merkleTreeDepth": 20, "contractAddress": "0x06bcD633988c1CE7Bd134DbE2C12119b6f3E4bD1"}]'
|
||||
```
|
||||
|
||||
## Configuration
|
||||
Deploy all verifiers and Semaphore contract:
|
||||
|
||||
The server and the client look for **server-config.json** and **client-config.json**, respectively. They can also accept their configuration as environment variables:
|
||||
* **server**:
|
||||
* CONFIG_ENV - load configuration from environment variables rather than a file.
|
||||
* CONFIG_PATH - location of the configuration file.
|
||||
* LOG_LEVEL - error, info, debug or verbose.
|
||||
* DB_PATH - location of the RocksDB database.
|
||||
* CHAIN_ID - chain ID of the Ethereum network.
|
||||
* CONTRACT_ADDRESS - the deployed Semaphore contract address.
|
||||
* CREATION_HASH - the transaction hash in which the contract was created, to allow for faster initial sync.
|
||||
* NODE_URL - the RPC URL of the Ehtereum node.
|
||||
* SEMAPHORE_PORT - the port on which to serve the Semaphore server REST API.
|
||||
* SEMAPHORE_LOGIN - the password with which clients communicating with the Semaphore server REST API must authenticate.
|
||||
* FROM_ADDRESS - the address to send transactions from.
|
||||
* FROM_PRIVATE_KEY - the private key of FROM_ADDRESS.
|
||||
* TRANSACTION_CONFIRMATION_BLOCKS - the amount of blocks to wait until a transaction is considered confirmed. The default is 24.
|
||||
* **client:**
|
||||
* CONFIG_ENV - load configuration from environment variables rather than a file.
|
||||
* LOG_LEVEL - error, info, debug or verbose.
|
||||
* IDENTITY_PATH - location of the identity secrets file.
|
||||
* CHAIN_ID - chain ID of the Ethereum network.
|
||||
* CONTRACT_ADDRESS - the deployed Semaphore contract address.
|
||||
* NODE_URL - the RPC URL of the Ehtereum node.
|
||||
* FROM_ADDRESS - the address to send transactions from.
|
||||
* FROM_PRIVATE_KEY - the private key of FROM_ADDRESS.
|
||||
* TRANSACTION_CONFIRMATION_BLOCKS - the amount of blocks to wait until a transaction is considered confirmed. The default is 24.
|
||||
* EXTERNAL_NULLIFIER - the external nullifier to be used with the signal. Must match the one in the contract.
|
||||
* SEMAPHORE_SERVER_URL - the URL of the Semaphore REST server.
|
||||
* BROADCASTER_ADDRESS - the address of the Semaphore server that will be allowed to broadcast the client's signals.
|
||||
```bash
|
||||
yarn deploy:all
|
||||
```
|
||||
|
||||
## Running
|
||||
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, kovan, arbitrum). Or you can specify it as option:
|
||||
|
||||
The easiest way to try Semaphore out is to use [https://semaphore.kobi.one](https://semaphore.kobi.one) - a web interface to broadcast to a remote server and generate proofs locally. First, load the Rinkeby config using the button at the top. Then, you can generate an identity and send the commitment to @kobigurk on Telegram or open an issue in the repository. Then, you can broadcast signals, including the proof generation, directly in the browser. Lastly, you can see the signals that have been broadcast to date in the table.
|
||||
```bash
|
||||
yarn deploy:all --network kovan
|
||||
yarn deploy:all --network localhost
|
||||
```
|
||||
|
||||
* To try out Semaphore locally you can clone the repository and run the following in `semaphore/semaphorejs/`:
|
||||
* **npm install && npm link**
|
||||
* **cd scripts && ./compile.sh && ./do_setup.sh && ./build_verifier.sh** - compile, do a setup and build the verifier of the Semaphore circuit.
|
||||
|
||||
* **scripts/run_ganache.sh** - runs ganache with appropriate parameters for Semaphore testing.
|
||||
* **scripts/run_all_test.sh** - runs a server and a client, generates a new random identity and broadcasts a signal.
|
||||
|
||||
It assumes bash, node and truffle are globally available.
|
||||
|
||||
Examples of run commands (roughly matching the test contract deployed on Rinkeby):
|
||||
* `LOG_LEVEL=debug CHAIN_ID=4 CONTRACT_ADDRESS=0x3dE2c3f8853594440c3363f8D491449Defa0bE1F NODE_URL=https://rinkeby.infura.io/v3/f4a3ad81db3f4750bd201955c8d20066 SEMAPHORE_PORT=3000 FROM_ADDRESS=0x1929c15f4e818abf2549510622a50c440c474223 FROM_PRIVATE_KEY=0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21 TRANSACTION_CONFIRMATION_BLOCKS=1 CREATION_HASH=0x4d6998f49f3ebb6e2bd3567c5adbf3f5ab711fbb24e618b4b53498d521f9c758 SEMAPHORE_LOGIN=test123 CONFIG_ENV=true npx semaphorejs-server`
|
||||
* `LOG_LEVEL=debug TRANSACTION_CONFIRMATION_BLOCKS=1 CHAIN_ID=4 CONTRACT_ADDRESS=0x3dE2c3f8853594440c3363f8D491449Defa0bE1F NODE_URL=https://rinkeby.infura.io/v3/f4a3ad81db3f4750bd201955c8d20066 EXTERNAL_NULLIFIER=12312 SEMAPHORE_SERVER_URL=https://semaphore-server.kobi.one BROADCASTER_ADDRESS=0x1929c15f4e818abf2549510622a50c440c474223 CONFIG_ENV=true npx semaphorejs-client signal "I vote for fork A"`
|
||||
If you want to deploy contracts on Goerli, Kovan or Arbitrum, remember to provide a valid private key and an Infura API in your `.env` file.
|
||||
|
||||
BIN
circuits/scheme.png
Normal file
BIN
circuits/scheme.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
90
circuits/semaphore.circom
Normal file
90
circuits/semaphore.circom
Normal file
@@ -0,0 +1,90 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "../node_modules/circomlib/circuits/poseidon.circom";
|
||||
include "./tree.circom";
|
||||
|
||||
template CalculateSecret() {
|
||||
signal input identityNullifier;
|
||||
signal input identityTrapdoor;
|
||||
|
||||
signal output out;
|
||||
|
||||
component poseidon = Poseidon(2);
|
||||
|
||||
poseidon.inputs[0] <== identityNullifier;
|
||||
poseidon.inputs[1] <== identityTrapdoor;
|
||||
|
||||
out <== poseidon.out;
|
||||
}
|
||||
|
||||
template CalculateIdentityCommitment() {
|
||||
signal input secret;
|
||||
|
||||
signal output out;
|
||||
|
||||
component poseidon = Poseidon(1);
|
||||
|
||||
poseidon.inputs[0] <== secret;
|
||||
|
||||
out <== poseidon.out;
|
||||
}
|
||||
|
||||
template CalculateNullifierHash() {
|
||||
signal input externalNullifier;
|
||||
signal input identityNullifier;
|
||||
|
||||
signal output out;
|
||||
|
||||
component poseidon = Poseidon(2);
|
||||
|
||||
poseidon.inputs[0] <== externalNullifier;
|
||||
poseidon.inputs[1] <== identityNullifier;
|
||||
|
||||
out <== poseidon.out;
|
||||
}
|
||||
|
||||
// nLevels must be < 32.
|
||||
template Semaphore(nLevels) {
|
||||
signal input identityNullifier;
|
||||
signal input identityTrapdoor;
|
||||
signal input treePathIndices[nLevels];
|
||||
signal input treeSiblings[nLevels];
|
||||
|
||||
signal input signalHash;
|
||||
signal input externalNullifier;
|
||||
|
||||
signal output root;
|
||||
signal output nullifierHash;
|
||||
|
||||
component calculateSecret = CalculateSecret();
|
||||
calculateSecret.identityNullifier <== identityNullifier;
|
||||
calculateSecret.identityTrapdoor <== identityTrapdoor;
|
||||
|
||||
signal secret;
|
||||
secret <== calculateSecret.out;
|
||||
|
||||
component calculateIdentityCommitment = CalculateIdentityCommitment();
|
||||
calculateIdentityCommitment.secret <== secret;
|
||||
|
||||
component calculateNullifierHash = CalculateNullifierHash();
|
||||
calculateNullifierHash.externalNullifier <== externalNullifier;
|
||||
calculateNullifierHash.identityNullifier <== identityNullifier;
|
||||
|
||||
component inclusionProof = MerkleTreeInclusionProof(nLevels);
|
||||
inclusionProof.leaf <== calculateIdentityCommitment.out;
|
||||
|
||||
for (var i = 0; i < nLevels; i++) {
|
||||
inclusionProof.siblings[i] <== treeSiblings[i];
|
||||
inclusionProof.pathIndices[i] <== treePathIndices[i];
|
||||
}
|
||||
|
||||
root <== inclusionProof.root;
|
||||
|
||||
// Dummy square to prevent tampering signalHash.
|
||||
signal signalHashSquared;
|
||||
signalHashSquared <== signalHash * signalHash;
|
||||
|
||||
nullifierHash <== calculateNullifierHash.out;
|
||||
}
|
||||
|
||||
component main {public [signalHash, externalNullifier]} = Semaphore(20);
|
||||
40
circuits/tree.circom
Normal file
40
circuits/tree.circom
Normal file
@@ -0,0 +1,40 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "../node_modules/circomlib/circuits/poseidon.circom";
|
||||
include "../node_modules/circomlib/circuits/mux1.circom";
|
||||
|
||||
template MerkleTreeInclusionProof(nLevels) {
|
||||
signal input leaf;
|
||||
signal input pathIndices[nLevels];
|
||||
signal input siblings[nLevels];
|
||||
|
||||
signal output root;
|
||||
|
||||
component poseidons[nLevels];
|
||||
component mux[nLevels];
|
||||
|
||||
signal hashes[nLevels + 1];
|
||||
hashes[0] <== leaf;
|
||||
|
||||
for (var i = 0; i < nLevels; i++) {
|
||||
pathIndices[i] * (1 - pathIndices[i]) === 0;
|
||||
|
||||
poseidons[i] = Poseidon(2);
|
||||
mux[i] = MultiMux1(2);
|
||||
|
||||
mux[i].c[0][0] <== hashes[i];
|
||||
mux[i].c[0][1] <== siblings[i];
|
||||
|
||||
mux[i].c[1][0] <== siblings[i];
|
||||
mux[i].c[1][1] <== hashes[i];
|
||||
|
||||
mux[i].s <== pathIndices[i];
|
||||
|
||||
poseidons[i].inputs[0] <== mux[i].out[0];
|
||||
poseidons[i].inputs[1] <== mux[i].out[1];
|
||||
|
||||
hashes[i + 1] <== poseidons[i].out;
|
||||
}
|
||||
|
||||
root <== hashes[nLevels];
|
||||
}
|
||||
61
contracts/README.md
Normal file
61
contracts/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
Semaphore contracts
|
||||
</h1>
|
||||
<p align="center">Semaphore contracts to manage groups and broadcast anonymous signals.</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/contracts">
|
||||
<img alt="NPM version" src="https://img.shields.io/npm/v/@semaphore-protocol/contracts?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/contracts">
|
||||
<img alt="Downloads" src="https://img.shields.io/npm/dm/@semaphore-protocol/contracts.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://t.me/joinchat/B-PQx1U3GtAh--Z4Fwo56A">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
To learn more about contracts visit [semaphore.appliedzkp.org](https://semaphore.appliedzkp.org/docs/technical-reference/contracts).
|
||||
|
||||
---
|
||||
|
||||
## 🛠 Install
|
||||
|
||||
### npm or yarn
|
||||
|
||||
Install the `@semaphore-protocol/contracts` package with npm:
|
||||
|
||||
```bash
|
||||
npm i @semaphore-protocol/contracts
|
||||
```
|
||||
|
||||
or yarn:
|
||||
|
||||
```bash
|
||||
yarn add @semaphore-protocol/contracts
|
||||
```
|
||||
96
contracts/Semaphore.sol
Normal file
96
contracts/Semaphore.sol
Normal file
@@ -0,0 +1,96 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import "./interfaces/ISemaphore.sol";
|
||||
import "./interfaces/IVerifier.sol";
|
||||
import "./base/SemaphoreCore.sol";
|
||||
import "./base/SemaphoreGroups.sol";
|
||||
|
||||
/// @title Semaphore
|
||||
contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
/// @dev Gets a tree depth and returns its verifier address.
|
||||
mapping(uint8 => IVerifier) public verifiers;
|
||||
|
||||
/// @dev Gets a group id and returns the group admin address.
|
||||
mapping(uint256 => address) public groupAdmins;
|
||||
|
||||
/// @dev Checks if the group admin is the transaction sender.
|
||||
/// @param groupId: Id of the group.
|
||||
modifier onlyGroupAdmin(uint256 groupId) {
|
||||
require(groupAdmins[groupId] == _msgSender(), "Semaphore: caller is not the group admin");
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Checks if there is a verifier for the given tree depth.
|
||||
/// @param depth: Depth of the tree.
|
||||
modifier onlySupportedDepth(uint8 depth) {
|
||||
require(address(verifiers[depth]) != address(0), "Semaphore: tree depth is not supported");
|
||||
_;
|
||||
}
|
||||
|
||||
/// @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; i++) {
|
||||
verifiers[_verifiers[i].merkleTreeDepth] = IVerifier(_verifiers[i].contractAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-createGroup}.
|
||||
function createGroup(
|
||||
uint256 groupId,
|
||||
uint8 depth,
|
||||
uint256 zeroValue,
|
||||
address admin
|
||||
) external override onlySupportedDepth(depth) {
|
||||
_createGroup(groupId, depth, zeroValue);
|
||||
|
||||
groupAdmins[groupId] = admin;
|
||||
|
||||
emit GroupAdminUpdated(groupId, address(0), admin);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-updateGroupAdmin}.
|
||||
function updateGroupAdmin(uint256 groupId, address newAdmin) external override onlyGroupAdmin(groupId) {
|
||||
groupAdmins[groupId] = newAdmin;
|
||||
|
||||
emit GroupAdminUpdated(groupId, _msgSender(), newAdmin);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-addMember}.
|
||||
function addMember(uint256 groupId, uint256 identityCommitment) external override onlyGroupAdmin(groupId) {
|
||||
_addMember(groupId, identityCommitment);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-removeMember}.
|
||||
function removeMember(
|
||||
uint256 groupId,
|
||||
uint256 identityCommitment,
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
) external override onlyGroupAdmin(groupId) {
|
||||
_removeMember(groupId, identityCommitment, proofSiblings, proofPathIndices);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-verifyProof}.
|
||||
function verifyProof(
|
||||
uint256 groupId,
|
||||
bytes32 signal,
|
||||
uint256 nullifierHash,
|
||||
uint256 externalNullifier,
|
||||
uint256[8] calldata proof
|
||||
) external override {
|
||||
uint256 root = getRoot(groupId);
|
||||
uint8 depth = getDepth(groupId);
|
||||
|
||||
require(depth != 0, "Semaphore: group does not exist");
|
||||
|
||||
IVerifier verifier = verifiers[depth];
|
||||
|
||||
_verifyProof(signal, root, nullifierHash, externalNullifier, proof, verifier);
|
||||
|
||||
_saveNullifierHash(nullifierHash);
|
||||
|
||||
emit ProofVerified(groupId, signal);
|
||||
}
|
||||
}
|
||||
4
contracts/base/SemaphoreConstants.sol
Normal file
4
contracts/base/SemaphoreConstants.sol
Normal file
@@ -0,0 +1,4 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
59
contracts/base/SemaphoreCore.sol
Normal file
59
contracts/base/SemaphoreCore.sol
Normal file
@@ -0,0 +1,59 @@
|
||||
//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 Gets a nullifier hash and returns true or false.
|
||||
/// It is used to prevent double-signaling.
|
||||
mapping(uint256 => bool) internal nullifierHashes;
|
||||
|
||||
/// @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 {
|
||||
require(!nullifierHashes[nullifierHash], "SemaphoreCore: you cannot use the same nullifier twice");
|
||||
|
||||
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 Stores the nullifier hash to prevent double-signaling.
|
||||
/// Attention! Remember to call it when you verify a proof if you
|
||||
/// need to prevent double-signaling.
|
||||
/// @param nullifierHash: Semaphore nullifier hash.
|
||||
function _saveNullifierHash(uint256 nullifierHash) internal {
|
||||
nullifierHashes[nullifierHash] = true;
|
||||
}
|
||||
|
||||
/// @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;
|
||||
}
|
||||
}
|
||||
83
contracts/base/SemaphoreGroups.sol
Normal file
83
contracts/base/SemaphoreGroups.sol
Normal file
@@ -0,0 +1,83 @@
|
||||
//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/contracts/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.
|
||||
/// 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 group/tree data.
|
||||
mapping(uint256 => IncrementalTreeData) internal groups;
|
||||
|
||||
/// @dev Creates a new group by initializing the associated tree.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param depth: Depth of the tree.
|
||||
/// @param zeroValue: Zero value of the tree.
|
||||
function _createGroup(
|
||||
uint256 groupId,
|
||||
uint8 depth,
|
||||
uint256 zeroValue
|
||||
) internal virtual {
|
||||
require(groupId < SNARK_SCALAR_FIELD, "SemaphoreGroups: group id must be < SNARK_SCALAR_FIELD");
|
||||
require(getDepth(groupId) == 0, "SemaphoreGroups: group already exists");
|
||||
|
||||
groups[groupId].init(depth, zeroValue);
|
||||
|
||||
emit GroupCreated(groupId, depth, zeroValue);
|
||||
}
|
||||
|
||||
/// @dev Adds an identity commitment to an existing group.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param identityCommitment: New identity commitment.
|
||||
function _addMember(uint256 groupId, uint256 identityCommitment) internal virtual {
|
||||
require(getDepth(groupId) != 0, "SemaphoreGroups: group does not exist");
|
||||
|
||||
groups[groupId].insert(identityCommitment);
|
||||
|
||||
uint256 root = getRoot(groupId);
|
||||
|
||||
emit MemberAdded(groupId, identityCommitment, root);
|
||||
}
|
||||
|
||||
/// @dev Removes an identity commitment from an existing group. A proof of membership is
|
||||
/// needed to check if the node to be deleted is part of the tree.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param identityCommitment: Existing identity commitment to be deleted.
|
||||
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
|
||||
/// @param proofPathIndices: Path of the proof of membership.
|
||||
function _removeMember(
|
||||
uint256 groupId,
|
||||
uint256 identityCommitment,
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
) internal virtual {
|
||||
require(getDepth(groupId) != 0, "SemaphoreGroups: group does not exist");
|
||||
|
||||
groups[groupId].remove(identityCommitment, proofSiblings, proofPathIndices);
|
||||
|
||||
uint256 root = getRoot(groupId);
|
||||
|
||||
emit MemberRemoved(groupId, identityCommitment, root);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreGroups-getRoot}.
|
||||
function getRoot(uint256 groupId) public view virtual override returns (uint256) {
|
||||
return groups[groupId].root;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreGroups-getDepth}.
|
||||
function getDepth(uint256 groupId) public view virtual override returns (uint8) {
|
||||
return groups[groupId].depth;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreGroups-getNumberOfLeaves}.
|
||||
function getNumberOfLeaves(uint256 groupId) public view virtual override returns (uint256) {
|
||||
return groups[groupId].numberOfLeaves;
|
||||
}
|
||||
}
|
||||
107
contracts/extensions/SemaphoreVoting.sol
Normal file
107
contracts/extensions/SemaphoreVoting.sol
Normal file
@@ -0,0 +1,107 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import "../interfaces/ISemaphoreVoting.sol";
|
||||
import "../base/SemaphoreCore.sol";
|
||||
import "../base/SemaphoreGroups.sol";
|
||||
|
||||
/// @title Semaphore voting contract.
|
||||
/// @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(uint8 => IVerifier) internal verifiers;
|
||||
|
||||
/// @dev Gets a poll id and returns the poll data.
|
||||
mapping(uint256 => Poll) internal polls;
|
||||
|
||||
/// @dev Since there can be multiple verifier contracts (each associated with a certain tree depth),
|
||||
/// it is necessary to pass the addresses of the previously deployed contracts with the associated
|
||||
/// tree depth. Depending on the depth chosen when creating the poll, a certain verifier will be
|
||||
/// used to verify that the proof is correct.
|
||||
/// @param depths: Three depths used in verifiers.
|
||||
/// @param verifierAddresses: Verifier addresses.
|
||||
constructor(uint8[] memory depths, address[] memory verifierAddresses) {
|
||||
require(
|
||||
depths.length == verifierAddresses.length,
|
||||
"SemaphoreVoting: parameters lists does not have the same length"
|
||||
);
|
||||
|
||||
for (uint8 i = 0; i < depths.length; i++) {
|
||||
verifiers[depths[i]] = IVerifier(verifierAddresses[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Checks if the poll coordinator is the transaction sender.
|
||||
/// @param pollId: Id of the poll.
|
||||
modifier onlyCoordinator(uint256 pollId) {
|
||||
require(polls[pollId].coordinator == _msgSender(), "SemaphoreVoting: caller is not the poll coordinator");
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreVoting-createPoll}.
|
||||
function createPoll(
|
||||
uint256 pollId,
|
||||
address coordinator,
|
||||
uint8 depth
|
||||
) public override {
|
||||
require(address(verifiers[depth]) != address(0), "SemaphoreVoting: depth value is not supported");
|
||||
|
||||
_createGroup(pollId, depth, 0);
|
||||
|
||||
Poll memory poll;
|
||||
|
||||
poll.coordinator = coordinator;
|
||||
|
||||
polls[pollId] = poll;
|
||||
|
||||
emit PollCreated(pollId, coordinator);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreVoting-addVoter}.
|
||||
function addVoter(uint256 pollId, uint256 identityCommitment) public override onlyCoordinator(pollId) {
|
||||
require(polls[pollId].state == PollState.Created, "SemaphoreVoting: voters can only be added before voting");
|
||||
|
||||
_addMember(pollId, identityCommitment);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreVoting-addVoter}.
|
||||
function startPoll(uint256 pollId, uint256 encryptionKey) public override onlyCoordinator(pollId) {
|
||||
require(polls[pollId].state == PollState.Created, "SemaphoreVoting: poll has already been started");
|
||||
|
||||
polls[pollId].state = PollState.Ongoing;
|
||||
|
||||
emit PollStarted(pollId, _msgSender(), encryptionKey);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreVoting-castVote}.
|
||||
function castVote(
|
||||
bytes32 vote,
|
||||
uint256 nullifierHash,
|
||||
uint256 pollId,
|
||||
uint256[8] calldata proof
|
||||
) public override onlyCoordinator(pollId) {
|
||||
Poll memory poll = polls[pollId];
|
||||
|
||||
require(poll.state == PollState.Ongoing, "SemaphoreVoting: vote can only be cast in an ongoing poll");
|
||||
|
||||
uint8 depth = getDepth(pollId);
|
||||
uint256 root = getRoot(pollId);
|
||||
IVerifier verifier = verifiers[depth];
|
||||
|
||||
_verifyProof(vote, root, nullifierHash, pollId, proof, verifier);
|
||||
|
||||
// Prevent double-voting (nullifierHash = hash(pollId + identityNullifier)).
|
||||
_saveNullifierHash(nullifierHash);
|
||||
|
||||
emit VoteAdded(pollId, vote);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreVoting-publishDecryptionKey}.
|
||||
function endPoll(uint256 pollId, uint256 decryptionKey) public override onlyCoordinator(pollId) {
|
||||
require(polls[pollId].state == PollState.Ongoing, "SemaphoreVoting: poll is not ongoing");
|
||||
|
||||
polls[pollId].state = PollState.Ended;
|
||||
|
||||
emit PollEnded(pollId, _msgSender(), decryptionKey);
|
||||
}
|
||||
}
|
||||
88
contracts/extensions/SemaphoreWhistleblowing.sol
Normal file
88
contracts/extensions/SemaphoreWhistleblowing.sol
Normal file
@@ -0,0 +1,88 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import "../interfaces/ISemaphoreWhistleblowing.sol";
|
||||
import "../base/SemaphoreCore.sol";
|
||||
import "../base/SemaphoreGroups.sol";
|
||||
|
||||
/// @title Semaphore whistleblowing contract.
|
||||
/// @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(uint8 => IVerifier) internal verifiers;
|
||||
|
||||
/// @dev Gets an editor address and return their entity.
|
||||
mapping(address => uint256) private entities;
|
||||
|
||||
/// @dev Since there can be multiple verifier contracts (each associated with a certain tree depth),
|
||||
/// it is necessary to pass the addresses of the previously deployed contracts with the associated
|
||||
/// tree depth. Depending on the depth chosen when creating the entity, a certain verifier will be
|
||||
/// used to verify that the proof is correct.
|
||||
/// @param depths: Three depths used in verifiers.
|
||||
/// @param verifierAddresses: Verifier addresses.
|
||||
constructor(uint8[] memory depths, address[] memory verifierAddresses) {
|
||||
require(
|
||||
depths.length == verifierAddresses.length,
|
||||
"SemaphoreWhistleblowing: parameters lists does not have the same length"
|
||||
);
|
||||
|
||||
for (uint8 i = 0; i < depths.length; i++) {
|
||||
verifiers[depths[i]] = IVerifier(verifierAddresses[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Checks if the editor is the transaction sender.
|
||||
/// @param entityId: Id of the entity.
|
||||
modifier onlyEditor(uint256 entityId) {
|
||||
require(entityId == entities[_msgSender()], "SemaphoreWhistleblowing: caller is not the editor");
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreWhistleblowing-createEntity}.
|
||||
function createEntity(
|
||||
uint256 entityId,
|
||||
address editor,
|
||||
uint8 depth
|
||||
) public override {
|
||||
require(address(verifiers[depth]) != address(0), "SemaphoreWhistleblowing: depth value is not supported");
|
||||
|
||||
_createGroup(entityId, depth, 0);
|
||||
|
||||
entities[editor] = entityId;
|
||||
|
||||
emit EntityCreated(entityId, editor);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreWhistleblowing-addWhistleblower}.
|
||||
function addWhistleblower(uint256 entityId, uint256 identityCommitment) public override onlyEditor(entityId) {
|
||||
_addMember(entityId, identityCommitment);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreWhistleblowing-removeWhistleblower}.
|
||||
function removeWhistleblower(
|
||||
uint256 entityId,
|
||||
uint256 identityCommitment,
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
) public override onlyEditor(entityId) {
|
||||
_removeMember(entityId, identityCommitment, proofSiblings, proofPathIndices);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreWhistleblowing-publishLeak}.
|
||||
function publishLeak(
|
||||
bytes32 leak,
|
||||
uint256 nullifierHash,
|
||||
uint256 entityId,
|
||||
uint256[8] calldata proof
|
||||
) public override onlyEditor(entityId) {
|
||||
uint8 depth = getDepth(entityId);
|
||||
uint256 root = getRoot(entityId);
|
||||
IVerifier verifier = verifiers[depth];
|
||||
|
||||
_verifyProof(leak, root, nullifierHash, entityId, proof, verifier);
|
||||
|
||||
emit LeakPublished(entityId, leak);
|
||||
}
|
||||
}
|
||||
72
contracts/interfaces/ISemaphore.sol
Normal file
72
contracts/interfaces/ISemaphore.sol
Normal file
@@ -0,0 +1,72 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
/// @title Semaphore interface.
|
||||
/// @dev Interface of a Semaphore contract.
|
||||
interface ISemaphore {
|
||||
struct Verifier {
|
||||
address contractAddress;
|
||||
uint8 merkleTreeDepth;
|
||||
}
|
||||
|
||||
/// @dev Emitted when an admin is assigned to a group.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param oldAdmin: Old admin of the group.
|
||||
/// @param newAdmin: New admin of the group.
|
||||
event GroupAdminUpdated(uint256 indexed groupId, address indexed oldAdmin, address indexed newAdmin);
|
||||
|
||||
/// @dev Emitted when a Semaphore proof is verified.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param signal: Semaphore signal.
|
||||
event ProofVerified(uint256 indexed groupId, bytes32 signal);
|
||||
|
||||
/// @dev Saves the nullifier hash to avoid double signaling and emits an event
|
||||
/// if the zero-knowledge proof is valid.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param signal: Semaphore signal.
|
||||
/// @param nullifierHash: Nullifier hash.
|
||||
/// @param externalNullifier: External nullifier.
|
||||
/// @param proof: Zero-knowledge proof.
|
||||
function verifyProof(
|
||||
uint256 groupId,
|
||||
bytes32 signal,
|
||||
uint256 nullifierHash,
|
||||
uint256 externalNullifier,
|
||||
uint256[8] calldata proof
|
||||
) 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.
|
||||
function createGroup(
|
||||
uint256 groupId,
|
||||
uint8 depth,
|
||||
uint256 zeroValue,
|
||||
address admin
|
||||
) external;
|
||||
|
||||
/// @dev Updates the group admin.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param newAdmin: New admin of the group.
|
||||
function updateGroupAdmin(uint256 groupId, address newAdmin) external;
|
||||
|
||||
/// @dev Adds a new member to an existing group.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param identityCommitment: New identity commitment.
|
||||
function addMember(uint256 groupId, uint256 identityCommitment) external;
|
||||
|
||||
/// @dev Removes a member from an existing group. A proof of membership is
|
||||
/// needed to check if the node to be removed is part of the tree.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param identityCommitment: Identity commitment to be deleted.
|
||||
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
|
||||
/// @param proofPathIndices: Path of the proof of membership.
|
||||
function removeMember(
|
||||
uint256 groupId,
|
||||
uint256 identityCommitment,
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
) external;
|
||||
}
|
||||
10
contracts/interfaces/ISemaphoreCore.sol
Normal file
10
contracts/interfaces/ISemaphoreCore.sol
Normal file
@@ -0,0 +1,10 @@
|
||||
//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);
|
||||
}
|
||||
39
contracts/interfaces/ISemaphoreGroups.sol
Normal file
39
contracts/interfaces/ISemaphoreGroups.sol
Normal file
@@ -0,0 +1,39 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
/// @title SemaphoreGroups interface.
|
||||
/// @dev Interface of a SemaphoreGroups contract.
|
||||
interface ISemaphoreGroups {
|
||||
/// @dev Emitted when a new group is created.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param depth: Depth of the tree.
|
||||
/// @param zeroValue: Zero value of the tree.
|
||||
event GroupCreated(uint256 indexed groupId, uint8 depth, uint256 zeroValue);
|
||||
|
||||
/// @dev Emitted when a new identity commitment is added.
|
||||
/// @param groupId: Group id of the group.
|
||||
/// @param identityCommitment: New identity commitment.
|
||||
/// @param root: New root hash of the tree.
|
||||
event MemberAdded(uint256 indexed groupId, uint256 identityCommitment, uint256 root);
|
||||
|
||||
/// @dev Emitted when a new identity commitment is removed.
|
||||
/// @param groupId: Group id of the group.
|
||||
/// @param identityCommitment: New identity commitment.
|
||||
/// @param root: New root hash of the tree.
|
||||
event MemberRemoved(uint256 indexed groupId, uint256 identityCommitment, uint256 root);
|
||||
|
||||
/// @dev Returns the last root hash of a group.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @return Root hash of the group.
|
||||
function getRoot(uint256 groupId) external view returns (uint256);
|
||||
|
||||
/// @dev Returns the depth of the tree of a group.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @return Depth of the group tree.
|
||||
function getDepth(uint256 groupId) external view returns (uint8);
|
||||
|
||||
/// @dev Returns the number of tree leaves of a group.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @return Number of tree leaves.
|
||||
function getNumberOfLeaves(uint256 groupId) external view returns (uint256);
|
||||
}
|
||||
14
contracts/interfaces/ISemaphoreNullifiers.sol
Normal file
14
contracts/interfaces/ISemaphoreNullifiers.sol
Normal file
@@ -0,0 +1,14 @@
|
||||
//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);
|
||||
}
|
||||
76
contracts/interfaces/ISemaphoreVoting.sol
Normal file
76
contracts/interfaces/ISemaphoreVoting.sol
Normal file
@@ -0,0 +1,76 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
/// @title SemaphoreVoting interface.
|
||||
/// @dev Interface of SemaphoreVoting contract.
|
||||
interface ISemaphoreVoting {
|
||||
enum PollState {
|
||||
Created,
|
||||
Ongoing,
|
||||
Ended
|
||||
}
|
||||
|
||||
struct Poll {
|
||||
address coordinator;
|
||||
PollState state;
|
||||
}
|
||||
|
||||
/// @dev Emitted when a new poll is created.
|
||||
/// @param pollId: Id of the poll.
|
||||
/// @param coordinator: Coordinator of the poll.
|
||||
event PollCreated(uint256 pollId, address indexed coordinator);
|
||||
|
||||
/// @dev Emitted when a poll is started.
|
||||
/// @param pollId: Id of the poll.
|
||||
/// @param coordinator: Coordinator of the poll.
|
||||
/// @param encryptionKey: Key to encrypt the poll votes.
|
||||
event PollStarted(uint256 pollId, address indexed coordinator, uint256 encryptionKey);
|
||||
|
||||
/// @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);
|
||||
|
||||
/// @dev Emitted when a poll is ended.
|
||||
/// @param pollId: Id of the poll.
|
||||
/// @param coordinator: Coordinator of the poll.
|
||||
/// @param decryptionKey: Key to decrypt the poll votes.
|
||||
event PollEnded(uint256 pollId, address indexed coordinator, uint256 decryptionKey);
|
||||
|
||||
/// @dev Creates a poll and the associated Merkle tree/group.
|
||||
/// @param pollId: Id of the poll.
|
||||
/// @param coordinator: Coordinator of the poll.
|
||||
/// @param depth: Depth of the tree.
|
||||
function createPoll(
|
||||
uint256 pollId,
|
||||
address coordinator,
|
||||
uint8 depth
|
||||
) external;
|
||||
|
||||
/// @dev Adds a voter to a poll.
|
||||
/// @param pollId: Id of the poll.
|
||||
/// @param identityCommitment: Identity commitment of the group member.
|
||||
function addVoter(uint256 pollId, uint256 identityCommitment) external;
|
||||
|
||||
/// @dev Starts a pull and publishes the key to encrypt the votes.
|
||||
/// @param pollId: Id of the poll.
|
||||
/// @param encryptionKey: Key to encrypt poll votes.
|
||||
function startPoll(uint256 pollId, uint256 encryptionKey) external;
|
||||
|
||||
/// @dev Casts an anonymous vote in a poll.
|
||||
/// @param vote: Encrypted vote.
|
||||
/// @param nullifierHash: Nullifier hash.
|
||||
/// @param pollId: Id of the poll.
|
||||
/// @param proof: Private zk-proof parameters.
|
||||
function castVote(
|
||||
bytes32 vote,
|
||||
uint256 nullifierHash,
|
||||
uint256 pollId,
|
||||
uint256[8] calldata proof
|
||||
) external;
|
||||
|
||||
/// @dev Ends a pull and publishes the key to decrypt the votes.
|
||||
/// @param pollId: Id of the poll.
|
||||
/// @param decryptionKey: Key to decrypt poll votes.
|
||||
function endPoll(uint256 pollId, uint256 decryptionKey) external;
|
||||
}
|
||||
55
contracts/interfaces/ISemaphoreWhistleblowing.sol
Normal file
55
contracts/interfaces/ISemaphoreWhistleblowing.sol
Normal file
@@ -0,0 +1,55 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
/// @title SemaphoreWhistleblowing interface.
|
||||
/// @dev Interface of SemaphoreWhistleblowing contract.
|
||||
interface ISemaphoreWhistleblowing {
|
||||
/// @dev Emitted when a new entity is created.
|
||||
/// @param entityId: Id of the entity.
|
||||
/// @param editor: Editor of the entity.
|
||||
event EntityCreated(uint256 entityId, address indexed editor);
|
||||
|
||||
/// @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);
|
||||
|
||||
/// @dev Creates an entity and the associated Merkle tree/group.
|
||||
/// @param entityId: Id of the entity.
|
||||
/// @param editor: Editor of the entity.
|
||||
/// @param depth: Depth of the tree.
|
||||
function createEntity(
|
||||
uint256 entityId,
|
||||
address editor,
|
||||
uint8 depth
|
||||
) external;
|
||||
|
||||
/// @dev Adds a whistleblower to an entity.
|
||||
/// @param entityId: Id of the entity.
|
||||
/// @param identityCommitment: Identity commitment of the group member.
|
||||
function addWhistleblower(uint256 entityId, uint256 identityCommitment) external;
|
||||
|
||||
/// @dev Removes a whistleblower from an entity.
|
||||
/// @param entityId: Id of the entity.
|
||||
/// @param identityCommitment: Identity commitment of the group member.
|
||||
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
|
||||
/// @param proofPathIndices: Path of the proof of membership.
|
||||
function removeWhistleblower(
|
||||
uint256 entityId,
|
||||
uint256 identityCommitment,
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
) external;
|
||||
|
||||
/// @dev Allows whistleblowers to publish leaks anonymously.
|
||||
/// @param leak: News leak.
|
||||
/// @param nullifierHash: Nullifier hash.
|
||||
/// @param entityId: Id of the entity.
|
||||
/// @param proof: Private zk-proof parameters.
|
||||
function publishLeak(
|
||||
bytes32 leak,
|
||||
uint256 nullifierHash,
|
||||
uint256 entityId,
|
||||
uint256[8] calldata proof
|
||||
) external;
|
||||
}
|
||||
13
contracts/interfaces/IVerifier.sol
Normal file
13
contracts/interfaces/IVerifier.sol
Normal file
@@ -0,0 +1,13 @@
|
||||
//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;
|
||||
}
|
||||
38
contracts/package.json
Normal file
38
contracts/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/contracts",
|
||||
"version": "2.0.0",
|
||||
"description": "Semaphore contracts to manage groups and broadcast anonymous signals.",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"**/*.sol"
|
||||
],
|
||||
"keywords": [
|
||||
"blockchain",
|
||||
"ethereum",
|
||||
"hardhat",
|
||||
"smart-contracts",
|
||||
"semaphore",
|
||||
"identity",
|
||||
"solidity",
|
||||
"zero-knowledge",
|
||||
"zk-snarks",
|
||||
"zero-knowledge-proofs",
|
||||
"circom",
|
||||
"proof-of-membership"
|
||||
],
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore.git#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/semaphore-protocol/semaphore.git.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/semaphore-protocol/semaphore.git/issues"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "^4.4.2",
|
||||
"@zk-kit/incremental-merkle-tree.sol": "^0.3.1"
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier16.sol
Normal file
237
contracts/verifiers/Verifier16.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier17.sol
Normal file
237
contracts/verifiers/Verifier17.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier18.sol
Normal file
237
contracts/verifiers/Verifier18.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier19.sol
Normal file
237
contracts/verifiers/Verifier19.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier20.sol
Normal file
237
contracts/verifiers/Verifier20.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier21.sol
Normal file
237
contracts/verifiers/Verifier21.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier22.sol
Normal file
237
contracts/verifiers/Verifier22.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier23.sol
Normal file
237
contracts/verifiers/Verifier23.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier24.sol
Normal file
237
contracts/verifiers/Verifier24.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier25.sol
Normal file
237
contracts/verifiers/Verifier25.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier26.sol
Normal file
237
contracts/verifiers/Verifier26.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier27.sol
Normal file
237
contracts/verifiers/Verifier27.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier28.sol
Normal file
237
contracts/verifiers/Verifier28.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier29.sol
Normal file
237
contracts/verifiers/Verifier29.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier30.sol
Normal file
237
contracts/verifiers/Verifier30.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier31.sol
Normal file
237
contracts/verifiers/Verifier31.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
237
contracts/verifiers/Verifier32.sol
Normal file
237
contracts/verifiers/Verifier32.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
74
deployed-contracts/goerli.json
Normal file
74
deployed-contracts/goerli.json
Normal file
@@ -0,0 +1,74 @@
|
||||
[
|
||||
{
|
||||
"name": "Verifier16",
|
||||
"address": "0xA5253ba39381Aa99c4C2C5A4D5C2deC036d06629"
|
||||
},
|
||||
{
|
||||
"name": "Verifier17",
|
||||
"address": "0xe0418A5f8fBF051D6cbc41Ff29855Dd2a02201Ab"
|
||||
},
|
||||
{
|
||||
"name": "Verifier18",
|
||||
"address": "0x7CdB3336d7d7c55Bce0FB1508594C54521656797"
|
||||
},
|
||||
{
|
||||
"name": "Verifier19",
|
||||
"address": "0xbd870921d8A5398a3314C950d1fc63b8C3AB190B"
|
||||
},
|
||||
{
|
||||
"name": "Verifier20",
|
||||
"address": "0x2a96c5696F85e3d2aa918496806B5c5a4D93E099"
|
||||
},
|
||||
{
|
||||
"name": "Verifier21",
|
||||
"address": "0x5Ec7d851a52A2a25CEc528F42a7ACA8EcF4667Cd"
|
||||
},
|
||||
{
|
||||
"name": "Verifier22",
|
||||
"address": "0x919d3d9c05FA7411e334deA5a763354fC7B6aA5b"
|
||||
},
|
||||
{
|
||||
"name": "Verifier23",
|
||||
"address": "0x63917b00a6dA7865bEfdd107AfC83CC2e6BDE552"
|
||||
},
|
||||
{
|
||||
"name": "Verifier24",
|
||||
"address": "0xd05CAd7d940114c1419098EE3cEA0776ab510E7D"
|
||||
},
|
||||
{
|
||||
"name": "Verifier25",
|
||||
"address": "0x6D9862e6140D94E932d94c8BcE74a0BDD0ea5ACb"
|
||||
},
|
||||
{
|
||||
"name": "Verifier26",
|
||||
"address": "0x8c29e0b77e32f704F03eeCE01c041192A5EB6c77"
|
||||
},
|
||||
{
|
||||
"name": "Verifier27",
|
||||
"address": "0x066cC22f8CA2A8D90D7Ff77D8a10A27e629c9c4C"
|
||||
},
|
||||
{
|
||||
"name": "Verifier28",
|
||||
"address": "0x698F9507f504E2BD238be7da56E8D9fee60C6D15"
|
||||
},
|
||||
{
|
||||
"name": "Verifier29",
|
||||
"address": "0xbBfC2E201C3c3c6F50063c3Edb4746c6Fcb36346"
|
||||
},
|
||||
{
|
||||
"name": "Verifier30",
|
||||
"address": "0x06bcD633988c1CE7Bd134DbE2C12119b6f3E4bD1"
|
||||
},
|
||||
{
|
||||
"name": "Verifier31",
|
||||
"address": "0x133b69Ce47BF20C49368354914DF47519Ca6cCFE"
|
||||
},
|
||||
{
|
||||
"name": "Verifier32",
|
||||
"address": "0xe2978F79cb4AF62e5C990EE5c7E12fb22ee22e2D"
|
||||
},
|
||||
{
|
||||
"name": "Semaphore",
|
||||
"address": "0x99aAb52e60f40AAC0BFE53e003De847bBDbC9611"
|
||||
}
|
||||
]
|
||||
74
deployed-contracts/kovan.json
Normal file
74
deployed-contracts/kovan.json
Normal file
@@ -0,0 +1,74 @@
|
||||
[
|
||||
{
|
||||
"name": "Verifier16",
|
||||
"address": "0x0C9DF9a417871b531684302f4062180E6A479431"
|
||||
},
|
||||
{
|
||||
"name": "Verifier17",
|
||||
"address": "0xa3739cFc24dd66deF7674393B0c7EcD3fe1DD90c"
|
||||
},
|
||||
{
|
||||
"name": "Verifier18",
|
||||
"address": "0xF9149c3AA6711c0f579b0cFB505D09A1226BbB02"
|
||||
},
|
||||
{
|
||||
"name": "Verifier19",
|
||||
"address": "0x863bB7875429B9774C4c771567778E2dc4991140"
|
||||
},
|
||||
{
|
||||
"name": "Verifier20",
|
||||
"address": "0xA701c1c97C1d42593D2b567f08c1859BBA7D6441"
|
||||
},
|
||||
{
|
||||
"name": "Verifier21",
|
||||
"address": "0x915d0A168894053362E64dCf04A731E2b4eE0276"
|
||||
},
|
||||
{
|
||||
"name": "Verifier22",
|
||||
"address": "0x22F2369A0d50fEb07F810d2B5411D2c58D371eB0"
|
||||
},
|
||||
{
|
||||
"name": "Verifier23",
|
||||
"address": "0x7D46E0612861Ff15FDF4fC16eF4BE0f37796E656"
|
||||
},
|
||||
{
|
||||
"name": "Verifier24",
|
||||
"address": "0xd3339E5Ff07bC961f3F7B641F5D3Bf690C2D14BF"
|
||||
},
|
||||
{
|
||||
"name": "Verifier25",
|
||||
"address": "0xc340cc18f682E4320217e8198d2e65B1C70BfDff"
|
||||
},
|
||||
{
|
||||
"name": "Verifier26",
|
||||
"address": "0x90FA0d9Ec6EFe94410bF06efeaa4Ec0F1dba0D67"
|
||||
},
|
||||
{
|
||||
"name": "Verifier27",
|
||||
"address": "0xd688189016277e1a6aE5228ef6894C14585A42D3"
|
||||
},
|
||||
{
|
||||
"name": "Verifier28",
|
||||
"address": "0x11EA2F1Bf24d46da9bb88B13e7d2Cb9FaCDb6B01"
|
||||
},
|
||||
{
|
||||
"name": "Verifier29",
|
||||
"address": "0x4784656EbE7AcF393154487A575b11e1c0E0dd47"
|
||||
},
|
||||
{
|
||||
"name": "Verifier30",
|
||||
"address": "0x590CB053A5F429719E6858CeeC56e6bedD4e9cfA"
|
||||
},
|
||||
{
|
||||
"name": "Verifier31",
|
||||
"address": "0x9E4D3a0B22B13d142711d7Eb69786f91c51E9ba6"
|
||||
},
|
||||
{
|
||||
"name": "Verifier32",
|
||||
"address": "0xa66F7B0Ce8662C1BfD7Ea97B7Dd8F4A7436A3cCd"
|
||||
},
|
||||
{
|
||||
"name": "Semaphore",
|
||||
"address": "0x9e4080e133384d2D09b593C003DCaF3c5a0C53A6"
|
||||
}
|
||||
]
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 25 KiB |
70
hardhat.config.ts
Normal file
70
hardhat.config.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import "@nomiclabs/hardhat-ethers"
|
||||
import "@nomiclabs/hardhat-waffle"
|
||||
import "@typechain/hardhat"
|
||||
import { config as dotenvConfig } from "dotenv"
|
||||
import "hardhat-gas-reporter"
|
||||
import { HardhatUserConfig } from "hardhat/config"
|
||||
import { NetworksUserConfig } from "hardhat/types"
|
||||
import { resolve } from "path"
|
||||
import "solidity-coverage"
|
||||
import { config } from "./package.json"
|
||||
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") })
|
||||
|
||||
function getNetworks(): NetworksUserConfig | undefined {
|
||||
if (process.env.INFURA_API_KEY && process.env.BACKEND_PRIVATE_KEY) {
|
||||
const infuraApiKey = process.env.INFURA_API_KEY
|
||||
const accounts = [`0x${process.env.BACKEND_PRIVATE_KEY}`]
|
||||
|
||||
return {
|
||||
goerli: {
|
||||
url: `https://goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 5,
|
||||
accounts
|
||||
},
|
||||
kovan: {
|
||||
url: `https://kovan.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 42,
|
||||
accounts
|
||||
},
|
||||
arbitrum: {
|
||||
url: "https://arb1.arbitrum.io/rpc",
|
||||
chainId: 42161,
|
||||
accounts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hardhatConfig: HardhatUserConfig = {
|
||||
solidity: config.solidity,
|
||||
paths: {
|
||||
sources: config.paths.contracts,
|
||||
tests: config.paths.tests,
|
||||
cache: config.paths.cache,
|
||||
artifacts: config.paths.build.contracts
|
||||
},
|
||||
networks: {
|
||||
hardhat: {
|
||||
chainId: 1337,
|
||||
allowUnlimitedContractSize: true
|
||||
},
|
||||
...getNetworks()
|
||||
},
|
||||
gasReporter: {
|
||||
currency: "USD",
|
||||
enabled: process.env.REPORT_GAS === "true",
|
||||
coinmarketcap: process.env.COINMARKETCAP_API_KEY
|
||||
},
|
||||
typechain: {
|
||||
outDir: config.paths.build.typechain,
|
||||
target: "ethers-v5"
|
||||
}
|
||||
}
|
||||
|
||||
export default hardhatConfig
|
||||
104
package.json
Normal file
104
package.json
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"name": "semaphore",
|
||||
"version": "2.0.0",
|
||||
"description": "A zero-knowledge protocol for anonymous signalling on Ethereum.",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore.git#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/semaphore-protocol/semaphore.git.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/semaphore-protocol/semaphore.git/issues"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "hardhat node",
|
||||
"compile": "hardhat compile",
|
||||
"download:snark-artifacts": "hardhat run scripts/download-snark-artifacts.ts",
|
||||
"deploy:all": "hardhat run scripts/deploy-all.ts",
|
||||
"deploy:verifier": "hardhat deploy:verifier",
|
||||
"deploy:semaphore": "hardhat deploy:semaphore",
|
||||
"deploy:semaphore-voting": "hardhat deploy:semaphore-voting",
|
||||
"deploy:semaphore-whistleblowing": "hardhat deploy:semaphore-whistleblowing",
|
||||
"test": "hardhat test",
|
||||
"test:report-gas": "REPORT_GAS=true hardhat test",
|
||||
"test:coverage": "hardhat coverage",
|
||||
"typechain": "hardhat typechain",
|
||||
"lint": "yarn lint:sol && yarn lint:ts",
|
||||
"lint:ts": "eslint . --ext .js,.ts",
|
||||
"lint:sol": "solhint 'contracts/**/*.sol'",
|
||||
"prettier": "prettier -c .",
|
||||
"prettier:write": "prettier -w .",
|
||||
"commit": "cz",
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
"packageManager": "yarn@3.2.1",
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^16.1.0",
|
||||
"@commitlint/config-conventional": "^16.0.0",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.6",
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.3",
|
||||
"@semaphore-protocol/group": "0.1.2",
|
||||
"@semaphore-protocol/identity": "0.2.2",
|
||||
"@semaphore-protocol/proof": "0.3.3",
|
||||
"@typechain/ethers-v5": "^10.0.0",
|
||||
"@typechain/hardhat": "^6.0.0",
|
||||
"@types/chai": "^4.3.0",
|
||||
"@types/download": "^8.0.1",
|
||||
"@types/mocha": "^9.1.0",
|
||||
"@types/node": "^17.0.12",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.1",
|
||||
"@typescript-eslint/parser": "^5.10.1",
|
||||
"chai": "^4.3.5",
|
||||
"circomlib": "^2.0.2",
|
||||
"circomlibjs": "^0.0.8",
|
||||
"commitizen": "^4.2.4",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"dotenv": "^14.3.2",
|
||||
"download": "^8.0.0",
|
||||
"eslint": "^8.7.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"ethereum-waffle": "^3.4.4",
|
||||
"ethers": "^5.6.8",
|
||||
"hardhat": "^2.9.7",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
"js-logger": "^1.6.1",
|
||||
"lint-staged": "^12.3.2",
|
||||
"prettier": "^2.5.1",
|
||||
"prettier-plugin-solidity": "^1.0.0-beta.19",
|
||||
"rimraf": "^3.0.2",
|
||||
"snarkjs": "^0.4.13",
|
||||
"solhint": "^3.3.6",
|
||||
"solhint-plugin-prettier": "^0.0.5",
|
||||
"solidity-coverage": "^0.7.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typechain": "^8.0.0",
|
||||
"typescript": "^4.5.5"
|
||||
},
|
||||
"config": {
|
||||
"solidity": {
|
||||
"version": "0.8.4"
|
||||
},
|
||||
"paths": {
|
||||
"contracts": "./contracts",
|
||||
"circuit": "./circuit",
|
||||
"tests": "./test",
|
||||
"cache": "./cache",
|
||||
"snarkjs-templates": "./snarkjs-templates",
|
||||
"build": {
|
||||
"snark-artifacts": "./build/snark-artifacts",
|
||||
"contracts": "./build/contracts",
|
||||
"typechain": "./build/typechain"
|
||||
}
|
||||
},
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "^4.4.2",
|
||||
"@zk-kit/incremental-merkle-tree.sol": "^0.3.1"
|
||||
}
|
||||
}
|
||||
38
scripts/deploy-all.ts
Normal file
38
scripts/deploy-all.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import fs from "fs"
|
||||
import { run, hardhatArguments } from "hardhat"
|
||||
|
||||
async function main() {
|
||||
const deployedContracts: { name: string; address: string }[] = []
|
||||
|
||||
// Deploy verifiers.
|
||||
for (let treeDepth = 16; treeDepth <= 32; treeDepth++) {
|
||||
const { address } = await run("deploy:verifier", { depth: treeDepth })
|
||||
|
||||
deployedContracts.push({
|
||||
name: `Verifier${treeDepth}`,
|
||||
address
|
||||
})
|
||||
}
|
||||
|
||||
// Deploy Semaphore.
|
||||
const { address } = await run("deploy:semaphore", {
|
||||
verifiers: deployedContracts.map((c) => ({ merkleTreeDepth: c.name.substring(8), contractAddress: c.address }))
|
||||
})
|
||||
|
||||
deployedContracts.push({
|
||||
name: `Semaphore`,
|
||||
address
|
||||
})
|
||||
|
||||
fs.writeFileSync(
|
||||
`./deployed-contracts/${hardhatArguments.network}.json`,
|
||||
JSON.stringify(deployedContracts, null, 4)
|
||||
)
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
24
scripts/download-snark-artifacts.ts
Normal file
24
scripts/download-snark-artifacts.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import download from "download"
|
||||
import fs from "fs"
|
||||
import { config } from "../package.json"
|
||||
|
||||
async function main() {
|
||||
const snarkArtifactsPath = config.paths.build["snark-artifacts"]
|
||||
const url = `http://www.trusted-setup-pse.org/semaphore/${process.env.TREE_DEPTH}`
|
||||
|
||||
if (!fs.existsSync(snarkArtifactsPath)) {
|
||||
fs.mkdirSync(snarkArtifactsPath, { recursive: true })
|
||||
}
|
||||
|
||||
if (!fs.existsSync(`${snarkArtifactsPath}/semaphore.zkey`)) {
|
||||
await download(`${url}/semaphore.wasm`, snarkArtifactsPath)
|
||||
await download(`${url}/semaphore.zkey`, snarkArtifactsPath)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
78
semaphorejs/.gitignore
vendored
78
semaphorejs/.gitignore
vendored
@@ -1,78 +0,0 @@
|
||||
blake2sdef.json
|
||||
build/.snark_checksum
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
build
|
||||
src/web/dist
|
||||
src/web/semaphore.js
|
||||
src/web/Semaphore.json
|
||||
src/web/circuit.json
|
||||
src/web/groth16_wasm.js
|
||||
src/web/proving_key.json
|
||||
src/web/proving_key.bin
|
||||
src/web/verification_key.json
|
||||
src/web/websnark.js
|
||||
scripts/semaphore_identity.json
|
||||
scripts/semaphore_server.db
|
||||
scripts/witness.json
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
*
|
||||
* This file is part of semaphorejs.
|
||||
*
|
||||
* semaphorejs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* semaphorejs is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
contract HashTester {
|
||||
constructor() public {
|
||||
|
||||
}
|
||||
|
||||
event DebugHash(bytes32 normal, uint256 converted, bytes32 normal_shifted, uint256 converted_shifted);
|
||||
event DebugRollingHash(uint256 prev_rolling_hash, uint256 signal_hash, uint256 rolling_hash, bytes encoded);
|
||||
|
||||
uint256 rolling_hash = 1238129381923;
|
||||
|
||||
function Test(bytes memory signal) public {
|
||||
uint256 signal_hash = uint256(sha256(signal)) >> 8;
|
||||
emit DebugHash(sha256(signal), uint256(sha256(signal)), sha256(signal) >> 8, signal_hash);
|
||||
bytes memory encoded = abi.encodePacked(rolling_hash, signal_hash);
|
||||
uint256 new_rolling_hash = uint256(sha256(encoded));
|
||||
emit DebugRollingHash(rolling_hash, signal_hash, new_rolling_hash, encoded);
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
*
|
||||
* This file is part of semaphorejs.
|
||||
*
|
||||
* semaphorejs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* semaphorejs is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
library MiMC {
|
||||
function MiMCSponge(uint256 in_xL, uint256 in_xR, uint256 in_k) pure public returns (uint256 xL, uint256 xR);
|
||||
}
|
||||
|
||||
contract MerkleTree {
|
||||
uint8 levels;
|
||||
|
||||
uint256 public root = 0;
|
||||
uint256[] public filled_subtrees;
|
||||
uint256[] public zeros;
|
||||
|
||||
uint32 public next_index = 0;
|
||||
|
||||
event LeafAdded(uint256 leaf, uint32 leaf_index);
|
||||
event LeafUpdated(uint256 leaf, uint32 leaf_index);
|
||||
|
||||
constructor(uint8 tree_levels, uint256 zero_value) public {
|
||||
levels = tree_levels;
|
||||
|
||||
zeros.push(zero_value);
|
||||
filled_subtrees.push(zeros[0]);
|
||||
|
||||
for (uint8 i = 1; i < levels; i++) {
|
||||
zeros.push(HashLeftRight(zeros[i-1], zeros[i-1]));
|
||||
filled_subtrees.push(zeros[i]);
|
||||
}
|
||||
|
||||
root = HashLeftRight(zeros[levels - 1], zeros[levels - 1]);
|
||||
}
|
||||
|
||||
function HashLeftRight(uint256 left, uint256 right) public pure returns (uint256 mimc_hash) {
|
||||
uint256 k = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
uint256 R = 0;
|
||||
uint256 C = 0;
|
||||
|
||||
R = addmod(R, left, k);
|
||||
(R, C) = MiMC.MiMCSponge(R, C, 0);
|
||||
|
||||
R = addmod(R, right, k);
|
||||
(R, C) = MiMC.MiMCSponge(R, C, 0);
|
||||
|
||||
mimc_hash = R;
|
||||
}
|
||||
|
||||
function insert(uint256 leaf) internal {
|
||||
uint32 leaf_index = next_index;
|
||||
uint32 current_index = next_index;
|
||||
next_index += 1;
|
||||
|
||||
uint256 current_level_hash = leaf;
|
||||
uint256 left;
|
||||
uint256 right;
|
||||
|
||||
for (uint8 i = 0; i < levels; i++) {
|
||||
if (current_index % 2 == 0) {
|
||||
left = current_level_hash;
|
||||
right = zeros[i];
|
||||
|
||||
filled_subtrees[i] = current_level_hash;
|
||||
} else {
|
||||
left = filled_subtrees[i];
|
||||
right = current_level_hash;
|
||||
}
|
||||
|
||||
current_level_hash = HashLeftRight(left, right);
|
||||
|
||||
current_index /= 2;
|
||||
}
|
||||
|
||||
root = current_level_hash;
|
||||
|
||||
emit LeafAdded(leaf, leaf_index);
|
||||
}
|
||||
|
||||
function update(uint256 leaf, uint32 leaf_index, uint256[] memory path) internal {
|
||||
uint32 current_index = leaf_index;
|
||||
|
||||
uint256 current_level_hash = leaf;
|
||||
uint256 left;
|
||||
uint256 right;
|
||||
|
||||
for (uint8 i = 0; i < levels; i++) {
|
||||
if (current_index % 2 == 0) {
|
||||
left = current_level_hash;
|
||||
right = path[i];
|
||||
} else {
|
||||
left = path[i];
|
||||
right = current_level_hash;
|
||||
}
|
||||
|
||||
current_level_hash = HashLeftRight(left, right);
|
||||
|
||||
current_index /= 2;
|
||||
}
|
||||
|
||||
emit LeafUpdated(leaf, leaf_index);
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
*
|
||||
* This file is part of semaphorejs.
|
||||
*
|
||||
* semaphorejs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* semaphorejs is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
library MiMC {
|
||||
function MiMCSponge(uint256 in_xL, uint256 in_xR, uint256 in_k) pure public returns (uint256 xL, uint256 xR);
|
||||
}
|
||||
|
||||
contract MultipleMerkleTree {
|
||||
uint8[] levels;
|
||||
|
||||
uint256[] internal tree_roots;
|
||||
uint256[][] filled_subtrees;
|
||||
uint256[][] zeros;
|
||||
|
||||
uint32[] next_index;
|
||||
|
||||
uint256[][] internal tree_leaves;
|
||||
|
||||
event LeafAdded(uint8 tree_index, uint256 leaf, uint32 leaf_index);
|
||||
event LeafUpdated(uint8 tree_index, uint256 leaf, uint32 leaf_index);
|
||||
|
||||
function init_tree(uint8 tree_levels, uint256 zero_value) public returns (uint8 tree_index) {
|
||||
levels.push(tree_levels);
|
||||
|
||||
uint256[] memory current_zeros = new uint256[](tree_levels);
|
||||
current_zeros[0] = zero_value;
|
||||
|
||||
uint256[] memory current_filled_subtrees = new uint256[](tree_levels);
|
||||
current_filled_subtrees[0] = current_zeros[0];
|
||||
|
||||
for (uint8 i = 1; i < tree_levels; i++) {
|
||||
current_zeros[i] = HashLeftRight(current_zeros[i-1], current_zeros[i-1]);
|
||||
current_filled_subtrees[i] = current_zeros[i];
|
||||
}
|
||||
|
||||
zeros.push(current_zeros);
|
||||
filled_subtrees.push(current_filled_subtrees);
|
||||
|
||||
tree_roots.push(HashLeftRight(current_zeros[tree_levels - 1], current_zeros[tree_levels - 1]));
|
||||
next_index.push(0);
|
||||
|
||||
tree_leaves.push(new uint256[](0));
|
||||
|
||||
return uint8(tree_roots.length) - 1;
|
||||
}
|
||||
|
||||
function HashLeftRight(uint256 left, uint256 right) public pure returns (uint256 mimc_hash) {
|
||||
uint256 k = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
uint256 R = 0;
|
||||
uint256 C = 0;
|
||||
|
||||
R = addmod(R, left, k);
|
||||
(R, C) = MiMC.MiMCSponge(R, C, 0);
|
||||
|
||||
R = addmod(R, right, k);
|
||||
(R, C) = MiMC.MiMCSponge(R, C, 0);
|
||||
|
||||
mimc_hash = R;
|
||||
}
|
||||
|
||||
function insert(uint8 tree_index, uint256 leaf) internal {
|
||||
uint32 leaf_index = next_index[tree_index];
|
||||
uint32 current_index = next_index[tree_index];
|
||||
next_index[tree_index] += 1;
|
||||
|
||||
uint256 current_level_hash = leaf;
|
||||
uint256 left;
|
||||
uint256 right;
|
||||
|
||||
for (uint8 i = 0; i < levels[tree_index]; i++) {
|
||||
if (current_index % 2 == 0) {
|
||||
left = current_level_hash;
|
||||
right = zeros[tree_index][i];
|
||||
|
||||
filled_subtrees[tree_index][i] = current_level_hash;
|
||||
} else {
|
||||
left = filled_subtrees[tree_index][i];
|
||||
right = current_level_hash;
|
||||
}
|
||||
|
||||
current_level_hash = HashLeftRight(left, right);
|
||||
|
||||
current_index /= 2;
|
||||
}
|
||||
|
||||
tree_roots[tree_index] = current_level_hash;
|
||||
|
||||
tree_leaves[tree_index].push(leaf);
|
||||
emit LeafAdded(tree_index, leaf, leaf_index);
|
||||
}
|
||||
|
||||
function update(uint8 tree_index, uint256 old_leaf, uint256 leaf, uint32 leaf_index, uint256[] memory old_path, uint256[] memory path) internal {
|
||||
uint32 current_index = leaf_index;
|
||||
|
||||
uint256 current_level_hash = old_leaf;
|
||||
uint256 left;
|
||||
uint256 right;
|
||||
|
||||
for (uint8 i = 0; i < levels[tree_index]; i++) {
|
||||
if (current_index % 2 == 0) {
|
||||
left = current_level_hash;
|
||||
right = old_path[i];
|
||||
} else {
|
||||
left = old_path[i];
|
||||
right = current_level_hash;
|
||||
}
|
||||
|
||||
current_level_hash = HashLeftRight(left, right);
|
||||
|
||||
current_index /= 2;
|
||||
}
|
||||
|
||||
require(tree_roots[tree_index] == current_level_hash, "MultipleMerkleTree: tree root / current level hash mismatch");
|
||||
|
||||
current_index = leaf_index;
|
||||
|
||||
current_level_hash = leaf;
|
||||
|
||||
for (uint8 i = 0; i < levels[tree_index]; i++) {
|
||||
if (current_index % 2 == 0) {
|
||||
left = current_level_hash;
|
||||
right = path[i];
|
||||
} else {
|
||||
left = path[i];
|
||||
right = current_level_hash;
|
||||
}
|
||||
|
||||
current_level_hash = HashLeftRight(left, right);
|
||||
|
||||
current_index /= 2;
|
||||
}
|
||||
|
||||
tree_roots[tree_index] = current_level_hash;
|
||||
|
||||
tree_leaves[tree_index][leaf_index] = leaf;
|
||||
emit LeafUpdated(tree_index, leaf, leaf_index);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
*
|
||||
* This file is part of semaphorejs.
|
||||
*
|
||||
* semaphorejs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* semaphorejs is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
import "./MerkleTree.sol";
|
||||
|
||||
contract MerkleTreeTester is MerkleTree {
|
||||
constructor() MerkleTree(2, 4) public {
|
||||
|
||||
}
|
||||
function insert_test(uint256 leaf) public {
|
||||
insert(leaf);
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
*
|
||||
* This file is part of semaphorejs.
|
||||
*
|
||||
* semaphorejs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* semaphorejs is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity >=0.4.21 <0.6.0;
|
||||
|
||||
contract Migrations {
|
||||
address public owner;
|
||||
uint public last_completed_migration;
|
||||
|
||||
constructor() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
modifier restricted() {
|
||||
if (msg.sender == owner) _;
|
||||
}
|
||||
|
||||
function setCompleted(uint completed) public restricted {
|
||||
last_completed_migration = completed;
|
||||
}
|
||||
|
||||
function upgrade(address new_address) public restricted {
|
||||
Migrations upgraded = Migrations(new_address);
|
||||
upgraded.setCompleted(last_completed_migration);
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
/**
|
||||
* @dev Contract module which provides a basic access control mechanism, where
|
||||
* there is an account (an owner) that can be granted exclusive access to
|
||||
* specific functions.
|
||||
*
|
||||
* This module is used through inheritance. It will make available the modifier
|
||||
* `onlyOwner`, which can be aplied to your functions to restrict their use to
|
||||
* the owner.
|
||||
*/
|
||||
contract Ownable {
|
||||
address private _owner;
|
||||
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||||
|
||||
/**
|
||||
* @dev Initializes the contract setting the deployer as the initial owner.
|
||||
*/
|
||||
constructor () internal {
|
||||
_owner = msg.sender;
|
||||
emit OwnershipTransferred(address(0), _owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the address of the current owner.
|
||||
*/
|
||||
function owner() public view returns (address) {
|
||||
return _owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Throws if called by any account other than the owner.
|
||||
*/
|
||||
modifier onlyOwner() {
|
||||
require(isOwner(), "Ownable: caller is not the owner");
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns true if the caller is the current owner.
|
||||
*/
|
||||
function isOwner() public view returns (bool) {
|
||||
return msg.sender == _owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Leaves the contract without owner. It will not be possible to call
|
||||
* `onlyOwner` functions anymore. Can only be called by the current owner.
|
||||
*
|
||||
* > Note: Renouncing ownership will leave the contract without an owner,
|
||||
* thereby removing any functionality that is only available to the owner.
|
||||
*/
|
||||
function renounceOwnership() public onlyOwner {
|
||||
emit OwnershipTransferred(_owner, address(0));
|
||||
_owner = address(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfers ownership of the contract to a new account (`newOwner`).
|
||||
* Can only be called by the current owner.
|
||||
*/
|
||||
function transferOwnership(address newOwner) public onlyOwner {
|
||||
_transferOwnership(newOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfers ownership of the contract to a new account (`newOwner`).
|
||||
*/
|
||||
function _transferOwnership(address newOwner) internal {
|
||||
require(newOwner != address(0), "Ownable: new owner is the zero address");
|
||||
emit OwnershipTransferred(_owner, newOwner);
|
||||
_owner = newOwner;
|
||||
}
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
* semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
*
|
||||
* This file is part of semaphorejs.
|
||||
*
|
||||
* semaphorejs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* semaphorejs is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
import "./verifier.sol";
|
||||
import "./MerkleTreeLib.sol";
|
||||
import "./Ownable.sol";
|
||||
|
||||
contract Semaphore is Verifier, MultipleMerkleTree, Ownable {
|
||||
// The external_nullifier helps to prevent double-signalling by the same
|
||||
// user.
|
||||
uint256 public external_nullifier;
|
||||
|
||||
uint8 public id_tree_index;
|
||||
|
||||
// Whether broadcastSignal() can only be called by the owner of this
|
||||
// contract. This is the case as a safe default.
|
||||
bool public is_broadcast_permissioned = true;
|
||||
|
||||
// Whether the contract has already seen a particular Merkle tree root
|
||||
mapping (uint256 => bool) root_history;
|
||||
|
||||
uint8 current_root_index = 0;
|
||||
|
||||
// Whether the contract has already seen a particular nullifier hash
|
||||
mapping (uint => bool) nullifier_hash_history;
|
||||
|
||||
// All signals broadcasted
|
||||
mapping (uint => bytes) public signals;
|
||||
|
||||
// The higest index of the `signals` mapping
|
||||
uint public current_signal_index = 0;
|
||||
|
||||
event SignalBroadcast(bytes signal, uint256 nullifiers_hash, uint256 external_nullifier);
|
||||
|
||||
/*
|
||||
* If broadcastSignal is permissioned, check if msg.sender is the contract owner
|
||||
*/
|
||||
modifier onlyOwnerIfPermissioned() {
|
||||
require(!is_broadcast_permissioned || isOwner(), "Semaphore: broadcast permission denied");
|
||||
_;
|
||||
}
|
||||
|
||||
constructor(uint8 tree_levels, uint256 zero_value, uint256 external_nullifier_in) Ownable() public {
|
||||
external_nullifier = external_nullifier_in;
|
||||
id_tree_index = init_tree(tree_levels, zero_value);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Register a new user.
|
||||
* @param identity_commitment The user's identity commitment, which is the
|
||||
* hash of their public key and their identity
|
||||
* nullifier (a random 31-byte value)
|
||||
*/
|
||||
function insertIdentity(uint256 identity_commitment) public onlyOwner {
|
||||
insert(id_tree_index, identity_commitment);
|
||||
root_history[tree_roots[id_tree_index]] = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change a user's identity commitment.
|
||||
* @param old_leaf The user's original identity commitment
|
||||
* @param leaf The user's new identity commitment
|
||||
* @param leaf_index The index of the original identity commitment in the tree
|
||||
* @param old_path The Merkle path to the original identity commitment
|
||||
* @param path The Merkle path to the new identity commitment
|
||||
*/
|
||||
function updateIdentity(
|
||||
uint256 old_leaf,
|
||||
uint256 leaf,
|
||||
uint32 leaf_index,
|
||||
uint256[] memory old_path,
|
||||
uint256[] memory path
|
||||
) public onlyOwner {
|
||||
update(id_tree_index, old_leaf, leaf, leaf_index, old_path, path);
|
||||
root_history[tree_roots[id_tree_index]] = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* @param n The nulllifier hash to check
|
||||
* @return True if the nullifier hash has previously been stored in the
|
||||
* contract
|
||||
*/
|
||||
function hasNullifier(uint n) public view returns (bool) {
|
||||
return nullifier_hash_history[n];
|
||||
}
|
||||
|
||||
/*
|
||||
* @param The Merkle root to check
|
||||
* @return True if the root has previously been stored in the
|
||||
* contract
|
||||
*/
|
||||
function isInRootHistory(uint n) public view returns (bool) {
|
||||
return root_history[n];
|
||||
}
|
||||
|
||||
/*
|
||||
* A convenience view function which helps operators to easily verify all
|
||||
* inputs to broadcastSignal() using a single contract call. This helps
|
||||
* them to save gas by detecting invalid inputs before they invoke
|
||||
* broadcastSignal(). Note that this function does the same checks as
|
||||
* `isValidSignalAndProof` but returns a bool instead of using require()
|
||||
* statements.
|
||||
*/
|
||||
function preBroadcastCheck (
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input,
|
||||
uint256 signal_hash
|
||||
) public view returns (bool) {
|
||||
return hasNullifier(input[1]) == false &&
|
||||
signal_hash == input[2] &&
|
||||
external_nullifier == input[3] &&
|
||||
isInRootHistory(input[0]) &&
|
||||
verifyProof(a, b, c, input);
|
||||
}
|
||||
|
||||
/*
|
||||
* A modifier which ensures that the signal and proof are valid.
|
||||
* @param signal The signal to broadcast
|
||||
* @param a The corresponding `a` parameter to verifier.sol's verifyProof()
|
||||
* @param b The corresponding `b` parameter to verifier.sol's verifyProof()
|
||||
* @param c The corresponding `c` parameter to verifier.sol's verifyProof()
|
||||
* @param input The public inputs to the zk-SNARK
|
||||
*/
|
||||
modifier isValidSignalAndProof (
|
||||
bytes memory signal,
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input
|
||||
) {
|
||||
// Hash the signal
|
||||
uint256 signal_hash = uint256(keccak256(signal)) >> 8;
|
||||
|
||||
require(hasNullifier(input[1]) == false, "Semaphore: nullifier already seen");
|
||||
require(signal_hash == input[2], "Semaphore: signal hash mismatch");
|
||||
require(external_nullifier == input[3], "Semaphore: external nullifier mismatch");
|
||||
require(isInRootHistory(input[0]), "Semaphore: root not seen");
|
||||
require(verifyProof(a, b, c, input), "Semaphore: invalid proof");
|
||||
_;
|
||||
}
|
||||
|
||||
/*
|
||||
* Broadcast the signal.
|
||||
* @param signal The signal to broadcast
|
||||
* @param a The corresponding `a` parameter to verifier.sol's verifyProof()
|
||||
* @param b The corresponding `b` parameter to verifier.sol's verifyProof()
|
||||
* @param c The corresponding `c` parameter to verifier.sol's verifyProof()
|
||||
* @param input The public inputs to the zk-SNARK
|
||||
*/
|
||||
function broadcastSignal(
|
||||
bytes memory signal,
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[4] memory input // (root, nullifiers_hash, signal_hash, external_nullifier)
|
||||
) public
|
||||
onlyOwnerIfPermissioned
|
||||
isValidSignalAndProof(signal, a, b, c, input)
|
||||
{
|
||||
uint nullifiers_hash = input[1];
|
||||
signals[current_signal_index++] = signal;
|
||||
nullifier_hash_history[nullifiers_hash] = true;
|
||||
emit SignalBroadcast(signal, nullifiers_hash, external_nullifier);
|
||||
}
|
||||
|
||||
/*
|
||||
* @param tree_index The tree in question
|
||||
* @return The Merkle root
|
||||
*/
|
||||
function root(uint8 tree_index) public view returns (uint256) {
|
||||
return tree_roots[tree_index];
|
||||
}
|
||||
|
||||
/*
|
||||
* @param tree_index The tree in question
|
||||
* @return The leaves of the tree
|
||||
*/
|
||||
function leaves(uint8 tree_index) public view returns (uint256[] memory) {
|
||||
return tree_leaves[tree_index];
|
||||
}
|
||||
|
||||
/*
|
||||
* @param tree_index The tree in question
|
||||
* @param leaf_index The index of the leaf to fetch
|
||||
* @return The leaf at leaf_index of the tree with index tree_index
|
||||
*/
|
||||
function leaf(uint8 tree_index, uint256 leaf_index) public view returns (uint256) {
|
||||
return tree_leaves[tree_index][leaf_index];
|
||||
}
|
||||
|
||||
/*
|
||||
* @return The index of the identity tree in MultipleMerkleTree
|
||||
*/
|
||||
function getIdTreeIndex() public view returns (uint8) {
|
||||
return id_tree_index;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets a new external nullifier for the contract. Only the owner can do this.
|
||||
* @param new_external_nullifier The new external nullifier to set
|
||||
*/
|
||||
function setExternalNullifier(uint256 new_external_nullifier) public onlyOwner {
|
||||
external_nullifier = new_external_nullifier;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the `is_broadcast_permissioned` storage variable, which determines
|
||||
* whether broadcastSignal can or cannot be called by only the contract
|
||||
* owner.
|
||||
*/
|
||||
function setPermissioning(bool _newPermission) public onlyOwner {
|
||||
is_broadcast_permissioned = _newPermission;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../build/verifier.sol
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
*
|
||||
* This file is part of semaphorejs.
|
||||
*
|
||||
* semaphorejs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* semaphorejs is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const Migrations = artifacts.require("Migrations");
|
||||
|
||||
module.exports = (deployer) => {
|
||||
return deployer.then( async () => {
|
||||
await deployer.deploy(Migrations);
|
||||
});
|
||||
};
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
*
|
||||
* This file is part of semaphorejs.
|
||||
*
|
||||
* semaphorejs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* semaphorejs is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const mimcGenContract = require('circomlib/src/mimcsponge_gencontract.js');
|
||||
const Artifactor = require('truffle-artifactor');
|
||||
|
||||
const SEED = 'mimcsponge';
|
||||
|
||||
|
||||
module.exports = function(deployer) {
|
||||
return deployer.then( async () => {
|
||||
const contractsDir = path.join(__dirname, '..', 'build/contracts');
|
||||
let artifactor = new Artifactor(contractsDir);
|
||||
let mimcContractName = 'MiMC';
|
||||
await artifactor.save({
|
||||
contractName: mimcContractName,
|
||||
abi: mimcGenContract.abi,
|
||||
unlinked_binary: mimcGenContract.createCode(SEED, 220),
|
||||
})
|
||||
.then(async () => {
|
||||
const MiMC = artifacts.require(mimcContractName);
|
||||
await deployer.deploy(MiMC);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
*
|
||||
* This file is part of semaphorejs.
|
||||
*
|
||||
* semaphorejs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* semaphorejs is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const MiMC = artifacts.require('MiMC');
|
||||
const MerkleTree = artifacts.require('MerkleTree');
|
||||
|
||||
module.exports = function(deployer) {
|
||||
return deployer.then( async () => {
|
||||
await deployer.link(MiMC, MerkleTree);
|
||||
await deployer.deploy(MerkleTree, 2, 4);
|
||||
});
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
*
|
||||
* This file is part of semaphorejs.
|
||||
*
|
||||
* semaphorejs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* semaphorejs is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const MiMC = artifacts.require('MiMC');
|
||||
const Semaphore = artifacts.require('Semaphore');
|
||||
const snarkjs = require('snarkjs');
|
||||
|
||||
const bigInt = snarkjs.bigInt;
|
||||
|
||||
module.exports = function(deployer) {
|
||||
return deployer.then( async () => {
|
||||
await deployer.link(MiMC, Semaphore);
|
||||
await deployer.deploy(Semaphore, 20, 0, 12312);
|
||||
});
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
*
|
||||
* This file is part of semaphorejs.
|
||||
*
|
||||
* semaphorejs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* semaphorejs is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const MiMC = artifacts.require('MiMC');
|
||||
const MerkleTree = artifacts.require('MerkleTreeTester');
|
||||
|
||||
module.exports = function(deployer) {
|
||||
return deployer.then( async () => {
|
||||
await deployer.link(MiMC, MerkleTree);
|
||||
await deployer.deploy(MerkleTree);
|
||||
});
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
*
|
||||
* This file is part of semaphorejs.
|
||||
*
|
||||
* semaphorejs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* semaphorejs is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const HashTester = artifacts.require('HashTester');
|
||||
|
||||
module.exports = function(deployer) {
|
||||
return deployer.then( async () => {
|
||||
await deployer.deploy(HashTester);
|
||||
});
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
f009e2cd6f4f37fcf6b6e060415ec6d65182b03a
|
||||
@@ -1,53 +0,0 @@
|
||||
{
|
||||
"name": "zkp-semaphorejs",
|
||||
"version": "0.3.3",
|
||||
"description": "Zero-knowledge signaling on Ethereum",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "truffle test --network=local",
|
||||
"migrate": "truffle migrate --network=local",
|
||||
"npm-update-git-deps": "npm-update-git-deps"
|
||||
},
|
||||
"bin": {
|
||||
"semaphorejs-server": "./src/server/server.js",
|
||||
"semaphorejs-client": "./src/client/client.js"
|
||||
},
|
||||
"author": "Kobi Gurkan",
|
||||
"license": "GPL-3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/kobigurk/semaphore.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"blakejs": "^1.1.0",
|
||||
"circom": "0.0.30",
|
||||
"circomlib": "0.0.13",
|
||||
"cors": "^2.8.5",
|
||||
"del": "^4.1.0",
|
||||
"ethers": "^4.0.33",
|
||||
"file-saver": "^2.0.1",
|
||||
"ganache-cli": "^6.4.1",
|
||||
"ganache-core": "^2.5.3",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"level-rocksdb": "^3.0.1",
|
||||
"mocha": "^6.0.2",
|
||||
"node-fetch": "^2.3.0",
|
||||
"require-nocache": "^1.0.0",
|
||||
"semaphore-merkle-tree": "1.0.6",
|
||||
"snarkjs": "0.1.17",
|
||||
"truffle": "^5.0.10",
|
||||
"truffle-artifactor": "^4.0.10",
|
||||
"truffle-contract": "^4.0.11",
|
||||
"truffle-hdwallet-provider": "^1.0.0-web3one.5",
|
||||
"truffle-privatekey-provider": "^1.1.0",
|
||||
"web3": "^1.0.0-beta.51",
|
||||
"webpack": "^4.30.0",
|
||||
"websnark": "0.0.5",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"npm-update-git-deps": "^1.2.4",
|
||||
"null-loader": "^0.1.1",
|
||||
"webpack-cli": "^3.3.0"
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/bin/bash -xe
|
||||
#
|
||||
# semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
# Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
#
|
||||
# This file is part of semaphorejs.
|
||||
#
|
||||
# semaphorejs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# semaphorejs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
echo "Working directory: `pwd`"
|
||||
./compile.sh
|
||||
./do_setup.sh
|
||||
./convert_to_wasm.sh
|
||||
./build_verifier.sh
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
#
|
||||
# semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
# Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
#
|
||||
# This file is part of semaphorejs.
|
||||
#
|
||||
# semaphorejs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# semaphorejs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
mkdir -p ../build
|
||||
cd ../build
|
||||
|
||||
if [ ! -f ../build/verifier.sol ]; then
|
||||
npx snarkjs generateverifier --vk ../build/verification_key.json -v ../build/verifier.sol
|
||||
fi
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
cd ..
|
||||
mkdir -p build
|
||||
find snark -type f -exec md5sum {} \; | sort -k 2 | md5sum > ./build/.snark_checksum
|
||||
echo './build/.snark_checksum:'
|
||||
cat ./build/.snark_checksum
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
#
|
||||
# semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
# Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
#
|
||||
# This file is part of semaphorejs.
|
||||
#
|
||||
# semaphorejs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# semaphorejs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
mkdir -p ../build
|
||||
cd ../build
|
||||
|
||||
if [ ! -f ./circuit.json ]; then
|
||||
export NODE_OPTIONS=--max-old-space-size=4096
|
||||
npx circom ../snark/semaphore.circom
|
||||
fi
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/bin/bash -xe
|
||||
#
|
||||
# semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
# Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
#
|
||||
# This file is part of semaphorejs.
|
||||
#
|
||||
# semaphorejs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# semaphorejs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
if [ ! -f ../build/proving_key.bin ]; then
|
||||
export NODE_OPTIONS=--max-old-space-size=4096
|
||||
node ../node_modules/websnark/tools/buildpkey.js -i ../build/proving_key.json -o ../build/proving_key.bin
|
||||
fi
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
#
|
||||
# semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
# Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
#
|
||||
# This file is part of semaphorejs.
|
||||
#
|
||||
# semaphorejs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# semaphorejs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
mkdir -p ../build
|
||||
cd ../build
|
||||
|
||||
if [ ! -f ./proving_key.json ]; then
|
||||
export NODE_OPTIONS=--max-old-space-size=4096
|
||||
npx snarkjs setup --protocol groth
|
||||
fi
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash -xe
|
||||
#
|
||||
# semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
# Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
#
|
||||
# This file is part of semaphorejs.
|
||||
#
|
||||
# semaphorejs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# semaphorejs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
echo "Working directory: `pwd`"
|
||||
|
||||
npx mocha --recursive ../test/circuits
|
||||
../node_modules/.bin/ganache-cli -p 7545 -l 8800000 -i 5777 --account='0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21,100000000000000000000000000' --account='0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd22,10000000000000000000000000' -q &
|
||||
../node_modules/.bin/truffle migrate --reset --network=local
|
||||
../node_modules/.bin/truffle test ../test/contracts/* --network=local
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
#!/bin/bash -xe
|
||||
#
|
||||
# semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
# Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
#
|
||||
# This file is part of semaphorejs.
|
||||
#
|
||||
# semaphorejs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# semaphorejs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
rm -rf semaphore_server.db
|
||||
|
||||
export CHAIN_ID=5777
|
||||
export NODE_URL=http://localhost:7545
|
||||
export SERVER_NODE_URL=http://localhost:7545
|
||||
export DEPLOY_TO=local
|
||||
export SLEEP_TIME=10
|
||||
|
||||
if [ "$1" == "goerli" ]; then
|
||||
CHAIN_ID=5
|
||||
NODE_URL=https://goerli.infura.io/v3/f4a3ad81db3f4750bd201955c8d20066
|
||||
SERVER_NODE_URL=https://goerli.infura.io/v3/f4a3ad81db3f4750bd201955c8d20066
|
||||
DEPLOY_TO=goerli
|
||||
SLEEP_TIME=20
|
||||
fi
|
||||
if [ "$1" == "ropsten" ]; then
|
||||
CHAIN_ID=3
|
||||
NODE_URL=https://ropsten.infura.io/v3/f4a3ad81db3f4750bd201955c8d20066
|
||||
SERVER_NODE_URL=https://ropsten.infura.io/v3/f4a3ad81db3f4750bd201955c8d20066
|
||||
DEPLOY_TO=ropsten
|
||||
SLEEP_TIME=20
|
||||
fi
|
||||
|
||||
if [ "$1" == "rinkeby" ]; then
|
||||
CHAIN_ID=4
|
||||
NODE_URL=https://rinkeby.infura.io/v3/f4a3ad81db3f4750bd201955c8d20066
|
||||
SERVER_NODE_URL=https://rinkeby.infura.io/v3/f4a3ad81db3f4750bd201955c8d20066
|
||||
DEPLOY_TO=rinkeby
|
||||
SLEEP_TIME=20
|
||||
fi
|
||||
|
||||
LOG_LEVEL=$2 || "verbose"
|
||||
|
||||
if [ "$3" == "true" ]; then
|
||||
truffle migrate --network ${DEPLOY_TO} -f 2 --to 2
|
||||
truffle migrate --network ${DEPLOY_TO} -f 4 --to 4
|
||||
fi
|
||||
|
||||
ADDRESS=`cat ../build/contracts/Semaphore.json | jq ".networks.\"${CHAIN_ID}\".address" | sed 's/"//g'`
|
||||
CREATION_HASH=`cat ../build/contracts/Semaphore.json | jq ".networks.\"${CHAIN_ID}\".transactionHash" | sed 's/"//g'`
|
||||
|
||||
CHAIN_ID=${CHAIN_ID} CONTRACT_ADDRESS=$ADDRESS FROM_ADDRESS=0x1929c15f4e818abf2549510622a50c440c474223 FROM_PRIVATE_KEY=0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21 NODE_URL=${NODE_URL} EXTERNAL_NULLIFIER=12312 SEMAPHORE_SERVER_URL=http://localhost:3000 CONFIG_ENV=true BASE_DIR=../.. npx semaphorejs-client generate_identity
|
||||
|
||||
echo done
|
||||
IDENTITY_COMMITMENT=`cat ./semaphore_identity.json | jq '.identity_commitment' | sed 's/"//g'`
|
||||
echo ${IDENTITY_COMMITMENT}
|
||||
|
||||
tmux \
|
||||
new-session "source ~/.bashrc; export CONFIG_ENV=true LOG_LEVEL=${LOG_LEVEL} CHAIN_ID=${CHAIN_ID} CONTRACT_ADDRESS=$ADDRESS NODE_URL=${SERVER_NODE_URL} SEMAPHORE_PORT=3000 FROM_ADDRESS=0x1929c15f4e818abf2549510622a50c440c474223 FROM_PRIVATE_KEY=0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21 TRANSACTION_CONFIRMATION_BLOCKS=1 CREATION_HASH=${CREATION_HASH}; npx semaphorejs-server disable_permissioning; npx semaphorejs-server; bash -i" \; \
|
||||
split-window -h -t 0 "source ~/.bashrc; sleep ${SLEEP_TIME}; LOG_LEVEL=${LOG_LEVEL} curl http://localhost:3000/add_identity -X POST -d'{\"leaf\": \"${IDENTITY_COMMITMENT}\"}' -H 'Content-Type: application/json'; bash -i" \; \
|
||||
split-window -t 0 "source ~/.bashrc; sleep ${SLEEP_TIME}; ./wait_for_element.sh ${IDENTITY_COMMITMENT}; LOG_LEVEL=${LOG_LEVEL} TRANSACTION_CONFIRMATION_BLOCKS=1 CHAIN_ID=${CHAIN_ID} CONTRACT_ADDRESS=$ADDRESS FROM_ADDRESS=0x1929c15f4e818abf2549510622a50c440c474223 FROM_PRIVATE_KEY=0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21 NODE_URL=${NODE_URL} EXTERNAL_NULLIFIER=12312 SEMAPHORE_SERVER_URL=http://localhost:3000 BROADCASTER_ADDRESS=0x1929c15f4e818abf2549510622a50c440c474223 CONFIG_ENV=true BASE_DIR=`pwd`/.. npx semaphorejs-client signal `shuf -i 1-100000000 -n 1`; bash -i"
|
||||
@@ -1,21 +0,0 @@
|
||||
#
|
||||
# semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
# Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
#
|
||||
# This file is part of semaphorejs.
|
||||
#
|
||||
# semaphorejs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# semaphorejs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
../node_modules/.bin/ganache-cli -p 7545 -l 8800000 -i 5777 --account='0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21,100000000000000000000000000' --account='0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd22,10000000000000000000000000' -b 1 -q
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/bash -xe
|
||||
#
|
||||
# semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
# Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
#
|
||||
# This file is part of semaphorejs.
|
||||
#
|
||||
# semaphorejs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# semaphorejs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
while true; do
|
||||
code=`curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/element_index/$1`
|
||||
if [ $code -eq 200 ]; then
|
||||
echo "found commitment, continuing"
|
||||
break
|
||||
else
|
||||
echo "didn't find commitment, waiting"
|
||||
sleep 1
|
||||
fi
|
||||
done
|
||||
@@ -1,384 +0,0 @@
|
||||
// based on https://github.com/zcash/librustzcash/blob/master/sapling-crypto/src/circuit/blake2s.rs
|
||||
|
||||
include "uint32.circom";
|
||||
include "../../node_modules/circomlib/circuits/bitify.circom";
|
||||
include "../../node_modules/circomlib/circuits/sha256/rotate.circom";
|
||||
|
||||
template MixingG(a, b, c, d) {
|
||||
signal input in_v[16][32];
|
||||
signal input x[32];
|
||||
signal input y[32];
|
||||
|
||||
signal output out_v[16][32];
|
||||
|
||||
component v_a_add = Uint32Add(3);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_a_add.nums_bits[0][i] <== in_v[a][i];
|
||||
v_a_add.nums_bits[1][i] <== in_v[b][i];
|
||||
v_a_add.nums_bits[2][i] <== x[i];
|
||||
}
|
||||
|
||||
component v_d_a_xor = Uint32Xor();
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_d_a_xor.a_bits[i] <== in_v[d][i];
|
||||
v_d_a_xor.b_bits[i] <== v_a_add.out_bits[i];
|
||||
}
|
||||
|
||||
component v_d_a_rot = RotR(32, 16);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_d_a_rot.in[i] <== v_d_a_xor.out_bits[i];
|
||||
}
|
||||
|
||||
component v_c_add = Uint32Add(2);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_c_add.nums_bits[0][i] <== in_v[c][i];
|
||||
v_c_add.nums_bits[1][i] <== v_d_a_rot.out[i];
|
||||
}
|
||||
|
||||
component v_b_c_xor = Uint32Xor();
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_b_c_xor.a_bits[i] <== in_v[b][i];
|
||||
v_b_c_xor.b_bits[i] <== v_c_add.out_bits[i];
|
||||
}
|
||||
|
||||
component v_b_c_rot = RotR(32, 12);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_b_c_rot.in[i] <== v_b_c_xor.out_bits[i];
|
||||
}
|
||||
|
||||
component v_a_add_2 = Uint32Add(3);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_a_add_2.nums_bits[0][i] <== v_a_add.out_bits[i];
|
||||
v_a_add_2.nums_bits[1][i] <== v_b_c_rot.out[i];
|
||||
v_a_add_2.nums_bits[2][i] <== y[i];
|
||||
}
|
||||
|
||||
component v_d_a_xor_2 = Uint32Xor();
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_d_a_xor_2.a_bits[i] <== v_d_a_rot.out[i];
|
||||
v_d_a_xor_2.b_bits[i] <== v_a_add_2.out_bits[i];
|
||||
}
|
||||
|
||||
component v_d_a_rot_2 = RotR(32, 8);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_d_a_rot_2.in[i] <== v_d_a_xor_2.out_bits[i];
|
||||
}
|
||||
|
||||
component v_c_add_2 = Uint32Add(2);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_c_add_2.nums_bits[0][i] <== v_c_add.out_bits[i];
|
||||
v_c_add_2.nums_bits[1][i] <== v_d_a_rot_2.out[i];
|
||||
}
|
||||
|
||||
component v_b_c_xor_2 = Uint32Xor();
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_b_c_xor_2.a_bits[i] <== v_b_c_rot.out[i];
|
||||
v_b_c_xor_2.b_bits[i] <== v_c_add_2.out_bits[i];
|
||||
}
|
||||
|
||||
component v_b_c_rot_2 = RotR(32, 7);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_b_c_rot_2.in[i] <== v_b_c_xor_2.out_bits[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < 16; i++) {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
if (i == a) {
|
||||
out_v[i][j] <== v_a_add_2.out_bits[j];
|
||||
} else if (i == b) {
|
||||
out_v[i][j] <== v_b_c_rot_2.out[j];
|
||||
} else if (i == c) {
|
||||
out_v[i][j] <== v_c_add_2.out_bits[j];
|
||||
} else if (i == d) {
|
||||
out_v[i][j] <== v_d_a_rot_2.out[j];
|
||||
} else {
|
||||
out_v[i][j] <== in_v[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template Blake2sCompression(t, f) {
|
||||
signal input in_h[8][32];
|
||||
signal input in_m[16][32];
|
||||
|
||||
signal output out_h[8][32];
|
||||
|
||||
var v_consts = [
|
||||
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
|
||||
];
|
||||
signal v_h[16][32];
|
||||
|
||||
for (var i = 0; i < 16; i++) {
|
||||
if (i < 8) {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_h[i][j] <== in_h[i][j];
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_h[i][j] <== (v_consts[i - 8] >> j) & 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signal v_pass_1[16][32];
|
||||
component v_12_xor = Uint32Xor();
|
||||
component v_13_xor = Uint32Xor();
|
||||
component v_14_xor = Uint32Xor();
|
||||
|
||||
for (var i = 0; i < 16; i++) {
|
||||
if (i == 12) {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_12_xor.a_bits[j] <== v_h[i][j];
|
||||
v_12_xor.b_bits[j] <== (t >> j) & 1;
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_pass_1[i][j] <== v_12_xor.out_bits[j];
|
||||
}
|
||||
} else if (i == 13) {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_13_xor.a_bits[j] <== v_h[i][j];
|
||||
v_13_xor.b_bits[j] <== (t >> (32 + j)) & 1;
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_pass_1[i][j] <== v_13_xor.out_bits[j];
|
||||
}
|
||||
} else if ((i == 14)) {
|
||||
if (f == 1) {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_14_xor.a_bits[j] <== v_h[i][j];
|
||||
v_14_xor.b_bits[j] <== 1;
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_pass_1[i][j] <== v_14_xor.out_bits[j];
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_14_xor.a_bits[j] <== v_h[i][j];
|
||||
v_14_xor.b_bits[j] <== 0;
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_pass_1[i][j] <== v_h[i][j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_pass_1[i][j] <== v_h[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sigma = [
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
|
||||
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
|
||||
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
|
||||
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
|
||||
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
|
||||
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
|
||||
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
|
||||
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
|
||||
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]
|
||||
];
|
||||
|
||||
component mixing_g[10][8];
|
||||
var s;
|
||||
for (var i = 0; i < 10; i++) {
|
||||
s = sigma[i];
|
||||
mixing_g[i][0] = MixingG(0, 4, 8, 12);
|
||||
for (var j = 0; j < 16; j++) {
|
||||
for (var k = 0; k < 32; k++) {
|
||||
if (i == 0) {
|
||||
mixing_g[i][0].in_v[j][k] <== v_pass_1[j][k];
|
||||
} else {
|
||||
mixing_g[i][0].in_v[j][k] <== mixing_g[i - 1][7].out_v[j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var k = 0; k < 32; k++) {
|
||||
mixing_g[i][0].x[k] <== in_m[s[0]][k];
|
||||
mixing_g[i][0].y[k] <== in_m[s[1]][k];
|
||||
}
|
||||
|
||||
|
||||
mixing_g[i][1] = MixingG(1, 5, 9, 13);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][1].in_v[j][k] <== mixing_g[i][0].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][1].x[k] <== in_m[s[2]][k];
|
||||
mixing_g[i][1].y[k] <== in_m[s[3]][k];
|
||||
}
|
||||
|
||||
mixing_g[i][2] = MixingG(2, 6, 10, 14);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][2].in_v[j][k] <== mixing_g[i][1].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][2].x[k] <== in_m[s[4]][k];
|
||||
mixing_g[i][2].y[k] <== in_m[s[5]][k];
|
||||
}
|
||||
|
||||
mixing_g[i][3] = MixingG(3, 7, 11, 15);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][3].in_v[j][k] <== mixing_g[i][2].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][3].x[k] <== in_m[s[6]][k];
|
||||
mixing_g[i][3].y[k] <== in_m[s[7]][k];
|
||||
}
|
||||
|
||||
mixing_g[i][4] = MixingG(0, 5, 10, 15);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][4].in_v[j][k] <== mixing_g[i][3].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][4].x[k] <== in_m[s[8]][k];
|
||||
mixing_g[i][4].y[k] <== in_m[s[9]][k];
|
||||
}
|
||||
|
||||
mixing_g[i][5] = MixingG(1, 6, 11, 12);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][5].in_v[j][k] <== mixing_g[i][4].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][5].x[k] <== in_m[s[10]][k];
|
||||
mixing_g[i][5].y[k] <== in_m[s[11]][k];
|
||||
}
|
||||
|
||||
mixing_g[i][6] = MixingG(2, 7, 8, 13);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][6].in_v[j][k] <== mixing_g[i][5].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][6].x[k] <== in_m[s[12]][k];
|
||||
mixing_g[i][6].y[k] <== in_m[s[13]][k];
|
||||
}
|
||||
|
||||
mixing_g[i][7] = MixingG(3, 4, 9, 14);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][7].in_v[j][k] <== mixing_g[i][6].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][7].x[k] <== in_m[s[14]][k];
|
||||
mixing_g[i][7].y[k] <== in_m[s[15]][k];
|
||||
}
|
||||
}
|
||||
|
||||
component h_xor_1[8];
|
||||
component h_xor_2[8];
|
||||
for (var i = 0; i < 8; i++) {
|
||||
h_xor_1[i] = Uint32Xor();
|
||||
h_xor_2[i] = Uint32Xor();
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h_xor_1[i].a_bits[j] <== in_h[i][j];
|
||||
h_xor_1[i].b_bits[j] <== mixing_g[9][7].out_v[i][j];
|
||||
}
|
||||
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h_xor_2[i].a_bits[j] <== h_xor_1[i].out_bits[j];
|
||||
h_xor_2[i].b_bits[j] <== mixing_g[9][7].out_v[i + 8][j];
|
||||
}
|
||||
|
||||
for (var j = 0; j < 32; j++) {
|
||||
out_h[i][j] <== h_xor_2[i].out_bits[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template Blake2s(n_bits, personalization) {
|
||||
signal input in_bits[n_bits];
|
||||
signal output out[256];
|
||||
|
||||
signal h[8][32];
|
||||
component h_from_bits[8];
|
||||
component h6_xor;
|
||||
component h7_xor;
|
||||
|
||||
var h_consts = [
|
||||
0x6A09E667 ^ 0x01010000 ^ 32,
|
||||
0xBB67AE85,
|
||||
0x3C6EF372,
|
||||
0xA54FF53A,
|
||||
0x510E527F,
|
||||
0x9B05688C,
|
||||
0x1F83D9AB,
|
||||
0x5BE0CD19
|
||||
];
|
||||
|
||||
for (var i = 0; i < 8; i++) {
|
||||
h_from_bits[i] = Num2Bits(32);
|
||||
h_from_bits[i].in <== h_consts[i];
|
||||
if (i == 6) {
|
||||
h6_xor = Uint32Xor();
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h6_xor.a_bits[j] <== h_from_bits[i].out[j];
|
||||
h6_xor.b_bits[j] <== (personalization >> j) & 1;
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h[i][j] <== h6_xor.out_bits[j];
|
||||
}
|
||||
} else if (i == 7) {
|
||||
h7_xor = Uint32Xor();
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h7_xor.a_bits[j] <== h_from_bits[i].out[j];
|
||||
h7_xor.b_bits[j] <== (personalization >> (32 + j)) & 1;
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h[i][j] <== h7_xor.out_bits[j];
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h[i][j] <== h_from_bits[i].out[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var n_rounded;
|
||||
if ( (n_bits % 512) == 0) {
|
||||
n_rounded = n_bits;
|
||||
} else {
|
||||
n_rounded = n_bits + (512 - (n_bits % 512));
|
||||
}
|
||||
var num_blocks = n_rounded / 512;
|
||||
if (num_blocks == 0) {
|
||||
num_blocks = 1;
|
||||
}
|
||||
component compressions[num_blocks];
|
||||
var current_bit = 0;
|
||||
for (var i = 0; i < num_blocks; i++) {
|
||||
if (i < (num_blocks - 1)) {
|
||||
compressions[i] = Blake2sCompression((i + 1)*64, 0);
|
||||
} else {
|
||||
compressions[i] = Blake2sCompression((n_bits - (n_bits % 512))/8, 1);
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
for (var k = 0; k < 8; k++) {
|
||||
if (i == 0) {
|
||||
compressions[i].in_h[k][j] <== h[k][j];
|
||||
} else {
|
||||
compressions[i].in_h[k][j] <== compressions[i - 1].out_h[k][j];
|
||||
}
|
||||
}
|
||||
for (var l = 0; l < 16; l++) {
|
||||
current_bit = 512*i + 32*l + j;
|
||||
if (current_bit < n_bits) {
|
||||
compressions[i].in_m[l][j] <== in_bits[current_bit];
|
||||
} else {
|
||||
compressions[i].in_m[l][j] <== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == (num_blocks - 1)) {
|
||||
for (var j = 0; j < 8; j++) {
|
||||
for (var k = 0; k < 4; k++) {
|
||||
for (var l = 0; l < 8; l++) {
|
||||
out[32*j + 8*k + l] <== compressions[num_blocks - 1].out_h[8 - 1 - j][(4 - 1 - k)*8 + l];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
// based on https://github.com/zcash/librustzcash/blob/master/sapling-crypto/src/circuit/uint32.rs
|
||||
|
||||
include "../../node_modules/circomlib/circuits/bitify.circom";
|
||||
|
||||
template Uint32Add(n) {
|
||||
signal input nums_bits[n][32];
|
||||
signal output out_bits[32];
|
||||
|
||||
var result_num_bits = 32 + n - 1;
|
||||
signal result_bits[result_num_bits];
|
||||
signal nums_vals[n];
|
||||
signal result_val[n];
|
||||
|
||||
var coeff;
|
||||
var num_lc;
|
||||
|
||||
var lc = 0;
|
||||
for (var i = 0; i < n; i++) {
|
||||
num_lc = 0;
|
||||
coeff = 1;
|
||||
for (var j = 0; j < 32; j++) {
|
||||
lc += nums_bits[i][j]*coeff;
|
||||
num_lc += nums_bits[i][j]*coeff;
|
||||
coeff *= 2;
|
||||
}
|
||||
nums_vals[i] <-- num_lc;
|
||||
|
||||
if (i == 0) {
|
||||
result_val[i] <-- nums_vals[i];
|
||||
} else {
|
||||
result_val[i] <-- result_val[i-1] + nums_vals[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var result_lc = 0;
|
||||
coeff = 1;
|
||||
for (var i = 0; i < result_num_bits; i++) {
|
||||
|
||||
result_bits[i] <-- (result_val[n-1] >> i) & 1;
|
||||
result_bits[i] * (result_bits[i] - 1) === 0;
|
||||
|
||||
result_lc += result_bits[i]*coeff;
|
||||
coeff *= 2;
|
||||
}
|
||||
|
||||
result_lc === lc;
|
||||
for (var i = 0; i < 32; i++) {
|
||||
out_bits[i] <== result_bits[i];
|
||||
}
|
||||
}
|
||||
|
||||
template Uint32Xor() {
|
||||
signal input a_bits[32];
|
||||
signal input b_bits[32];
|
||||
|
||||
signal output out_bits[32];
|
||||
|
||||
for (var i = 0; i < 32; i++) {
|
||||
out_bits[i] <== a_bits[i] + b_bits[i] - 2*a_bits[i]*b_bits[i];
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
include "../node_modules/circomlib/circuits/pedersen.circom";
|
||||
include "../node_modules/circomlib/circuits/mimcsponge.circom";
|
||||
include "../node_modules/circomlib/circuits/bitify.circom";
|
||||
include "../node_modules/circomlib/circuits/eddsamimcsponge.circom";
|
||||
include "./blake2s/blake2s.circom";
|
||||
|
||||
template HashLeftRight(n_rounds) {
|
||||
signal input left;
|
||||
signal input right;
|
||||
|
||||
signal output hash;
|
||||
|
||||
component hasher = MiMCSponge(2, n_rounds, 1);
|
||||
left ==> hasher.ins[0];
|
||||
right ==> hasher.ins[1];
|
||||
hasher.k <== 0;
|
||||
|
||||
hash <== hasher.outs[0];
|
||||
}
|
||||
|
||||
template Selector() {
|
||||
signal input input_elem;
|
||||
signal input path_elem;
|
||||
signal input path_index;
|
||||
|
||||
signal output left;
|
||||
signal output right;
|
||||
|
||||
signal left_selector_1;
|
||||
signal left_selector_2;
|
||||
signal right_selector_1;
|
||||
signal right_selector_2;
|
||||
|
||||
path_index * (1-path_index) === 0
|
||||
|
||||
left_selector_1 <== (1 - path_index)*input_elem;
|
||||
left_selector_2 <== (path_index)*path_elem;
|
||||
right_selector_1 <== (path_index)*input_elem;
|
||||
right_selector_2 <== (1 - path_index)*path_elem;
|
||||
|
||||
left <== left_selector_1 + left_selector_2;
|
||||
right <== right_selector_1 + right_selector_2;
|
||||
}
|
||||
|
||||
template Semaphore(jubjub_field_size, n_levels, n_rounds) {
|
||||
// BEGIN signals
|
||||
|
||||
signal input signal_hash;
|
||||
signal input external_nullifier;
|
||||
|
||||
// mimc vector commitment
|
||||
signal private input identity_pk[2];
|
||||
signal private input identity_nullifier;
|
||||
signal private input identity_trapdoor;
|
||||
signal private input identity_path_elements[n_levels];
|
||||
signal private input identity_path_index[n_levels];
|
||||
|
||||
// signature on (external nullifier, signal_hash) with identity_pk
|
||||
signal private input auth_sig_r[2];
|
||||
signal private input auth_sig_s;
|
||||
|
||||
// get a prime subgroup element derived from identity_pk
|
||||
component dbl1 = BabyDbl();
|
||||
dbl1.x <== identity_pk[0];
|
||||
dbl1.y <== identity_pk[1];
|
||||
component dbl2 = BabyDbl();
|
||||
dbl2.x <== dbl1.xout;
|
||||
dbl2.y <== dbl1.yout;
|
||||
component dbl3 = BabyDbl();
|
||||
dbl3.x <== dbl2.xout;
|
||||
dbl3.y <== dbl2.yout;
|
||||
|
||||
// mimc hash
|
||||
signal output root;
|
||||
signal output nullifiers_hash;
|
||||
|
||||
// END signals
|
||||
|
||||
|
||||
component identity_nullifier_bits = Num2Bits(248);
|
||||
identity_nullifier_bits.in <== identity_nullifier;
|
||||
|
||||
component identity_trapdoor_bits = Num2Bits(248);
|
||||
identity_trapdoor_bits.in <== identity_trapdoor;
|
||||
|
||||
component identity_pk_0_bits = Num2Bits_strict();
|
||||
identity_pk_0_bits.in <== dbl3.xout;
|
||||
|
||||
component identity_commitment = Pedersen(3*256);
|
||||
// BEGIN identity commitment
|
||||
for (var i = 0; i < 256; i++) {
|
||||
if (i < 254) {
|
||||
identity_commitment.in[i] <== identity_pk_0_bits.out[i];
|
||||
} else {
|
||||
identity_commitment.in[i] <== 0;
|
||||
}
|
||||
|
||||
if (i < 248) {
|
||||
identity_commitment.in[i + 256] <== identity_nullifier_bits.out[i];
|
||||
identity_commitment.in[i + 2*256] <== identity_trapdoor_bits.out[i];
|
||||
} else {
|
||||
identity_commitment.in[i + 256] <== 0;
|
||||
identity_commitment.in[i + 2*256] <== 0;
|
||||
}
|
||||
}
|
||||
// END identity commitment
|
||||
|
||||
// BEGIN tree
|
||||
component selectors[n_levels];
|
||||
component hashers[n_levels];
|
||||
|
||||
for (var i = 0; i < n_levels; i++) {
|
||||
selectors[i] = Selector();
|
||||
hashers[i] = HashLeftRight(n_rounds);
|
||||
|
||||
identity_path_index[i] ==> selectors[i].path_index;
|
||||
identity_path_elements[i] ==> selectors[i].path_elem;
|
||||
|
||||
selectors[i].left ==> hashers[i].left;
|
||||
selectors[i].right ==> hashers[i].right;
|
||||
}
|
||||
|
||||
identity_commitment.out[0] ==> selectors[0].input_elem;
|
||||
|
||||
for (var i = 1; i < n_levels; i++) {
|
||||
hashers[i-1].hash ==> selectors[i].input_elem;
|
||||
}
|
||||
|
||||
root <== hashers[n_levels - 1].hash;
|
||||
// END tree
|
||||
|
||||
// BEGIN nullifiers
|
||||
component external_nullifier_bits = Num2Bits(232);
|
||||
external_nullifier_bits.in <== external_nullifier;
|
||||
|
||||
component nullifiers_hasher = Blake2s(512, 0);
|
||||
for (var i = 0; i < 248; i++) {
|
||||
nullifiers_hasher.in_bits[i] <== identity_nullifier_bits.out[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < 232; i++) {
|
||||
nullifiers_hasher.in_bits[248 + i] <== external_nullifier_bits.out[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < n_levels; i++) {
|
||||
nullifiers_hasher.in_bits[248 + 232 + i] <== identity_path_index[i];
|
||||
}
|
||||
|
||||
for (var i = (248 + 232 + n_levels); i < 512; i++) {
|
||||
nullifiers_hasher.in_bits[i] <== 0;
|
||||
}
|
||||
|
||||
component nullifiers_hash_num = Bits2Num(253);
|
||||
for (var i = 0; i < 253; i++) {
|
||||
nullifiers_hash_num.in[i] <== nullifiers_hasher.out[i];
|
||||
}
|
||||
|
||||
nullifiers_hash <== nullifiers_hash_num.out;
|
||||
|
||||
// END nullifiers
|
||||
|
||||
// BEGIN verify sig
|
||||
component msg_hasher = MiMCSponge(2, n_rounds, 1);
|
||||
msg_hasher.ins[0] <== external_nullifier;
|
||||
msg_hasher.ins[1] <== signal_hash;
|
||||
msg_hasher.k <== 0;
|
||||
|
||||
component sig_verifier = EdDSAMiMCSpongeVerifier();
|
||||
1 ==> sig_verifier.enabled;
|
||||
identity_pk[0] ==> sig_verifier.Ax;
|
||||
identity_pk[1] ==> sig_verifier.Ay;
|
||||
auth_sig_r[0] ==> sig_verifier.R8x;
|
||||
auth_sig_r[1] ==> sig_verifier.R8y;
|
||||
auth_sig_s ==> sig_verifier.S;
|
||||
msg_hasher.outs[0] ==> sig_verifier.M;
|
||||
|
||||
// END verify sig
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
include "./semaphore-base.circom";
|
||||
|
||||
component main = Semaphore(251, 20, 220);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user