mirror of
https://github.com/semaphore-protocol/semaphore.git
synced 2026-01-11 07:38:14 -05:00
Compare commits
88 Commits
v4.4.1
...
v4.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35e994d8ab | ||
|
|
09f4671112 | ||
|
|
9fec3138f7 | ||
|
|
d83e06e833 | ||
|
|
0529774445 | ||
|
|
aa9329007c | ||
|
|
aacff93bd8 | ||
|
|
58aac07f7a | ||
|
|
ac51bba9ea | ||
|
|
0acf57b9b1 | ||
|
|
c91b09d7c5 | ||
|
|
66b6ddd165 | ||
|
|
eecef0e77a | ||
|
|
b3101f0a0a | ||
|
|
4c365862ae | ||
|
|
be46c9c05d | ||
|
|
a290ecbfdc | ||
|
|
d5e8c94af9 | ||
|
|
f3806dd7cb | ||
|
|
22ad55e1eb | ||
|
|
667f890a93 | ||
|
|
546ddba38e | ||
|
|
b714501de0 | ||
|
|
22b54a9e80 | ||
|
|
4e9c930c27 | ||
|
|
93d8aadf2f | ||
|
|
25d3bee901 | ||
|
|
b817f7e186 | ||
|
|
fd4d51ca40 | ||
|
|
94566774f9 | ||
|
|
52f139d2f4 | ||
|
|
5d532c1bff | ||
|
|
62cb8d02f6 | ||
|
|
acda38f666 | ||
|
|
f31d95e834 | ||
|
|
95b38ba7dd | ||
|
|
ba898915d7 | ||
|
|
6d1af90fab | ||
|
|
d42e5c875e | ||
|
|
24828b41a4 | ||
|
|
7033b00c2b | ||
|
|
5dfd8c1cba | ||
|
|
2b5ec257cf | ||
|
|
409cc624fa | ||
|
|
0ff15f5c92 | ||
|
|
6ec5eb8319 | ||
|
|
8cafe09de6 | ||
|
|
bd4d30fd19 | ||
|
|
cf66732974 | ||
|
|
1c300672d0 | ||
|
|
19a6768a1d | ||
|
|
175b5485dc | ||
|
|
2666c69728 | ||
|
|
b9b12184b5 | ||
|
|
38bd1747a0 | ||
|
|
59b8fd30b6 | ||
|
|
5151249a10 | ||
|
|
e30dcca7d6 | ||
|
|
ba99809deb | ||
|
|
3fb4b5effc | ||
|
|
d8f9078471 | ||
|
|
14227d2088 | ||
|
|
af7187a687 | ||
|
|
7572e921a4 | ||
|
|
908b541fab | ||
|
|
4c513c26f5 | ||
|
|
fcecad1146 | ||
|
|
ee819255d6 | ||
|
|
d1e9cdcb16 | ||
|
|
c45137e373 | ||
|
|
7c61118a40 | ||
|
|
17cad2b93c | ||
|
|
7a10acb153 | ||
|
|
ef1f22a043 | ||
|
|
37d081e1d0 | ||
|
|
6a427480cd | ||
|
|
7d781dc09b | ||
|
|
a93b590d42 | ||
|
|
32fd451eb9 | ||
|
|
a5bb7a6c73 | ||
|
|
ddf0045699 | ||
|
|
c0cb8fb0a7 | ||
|
|
f3e896eaaf | ||
|
|
361510d206 | ||
|
|
00953a9233 | ||
|
|
ae7be9a47a | ||
|
|
b3a0e5ac8d | ||
|
|
3aa5241334 |
@@ -1,8 +1,10 @@
|
||||
DEFAULT_NETWORK=hardhat
|
||||
TREE_DEPTH=20
|
||||
ALL_SNARK_ARTIFACTS=true
|
||||
TREE_DEPTH=10
|
||||
REPORT_GAS=false
|
||||
BACKEND_PRIVATE_KEY=
|
||||
INFURA_API_KEY=
|
||||
COINMARKETCAP_API_KEY=
|
||||
ETHERSCAN_API_KEY=
|
||||
DEFENDER_KEY=
|
||||
DEFENDER_SECRET=
|
||||
CREATE2_SALT=1234
|
||||
|
||||
20
.github/workflows/docs.yml
vendored
20
.github/workflows/docs.yml
vendored
@@ -10,25 +10,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v4
|
||||
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-
|
||||
node-version: 20
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
70
.github/workflows/production.yml
vendored
70
.github/workflows/production.yml
vendored
@@ -6,33 +6,20 @@ on:
|
||||
- main
|
||||
|
||||
env:
|
||||
TREE_DEPTH: 20
|
||||
ALL_SNARK_ARTIFACTS: false
|
||||
TREE_DEPTH: 10
|
||||
|
||||
jobs:
|
||||
style:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v4
|
||||
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-
|
||||
node-version: 20
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
@@ -56,25 +43,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v4
|
||||
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-
|
||||
node-version: 20
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
@@ -90,29 +65,25 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
type:
|
||||
- circuits
|
||||
- libraries
|
||||
- contracts
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 20
|
||||
cache: yarn
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
# https://github.com/iden3/circuits/blob/8fffb6609ecad0b7bcda19bb908bdb544bdb3cf7/.github/workflows/main.yml#L18-L22
|
||||
- name: Setup Circom deps
|
||||
run: sudo apt-get update && sudo apt-get install -y wget nlohmann-json3-dev libgmp-dev nasm g++ build-essential
|
||||
|
||||
- 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: Setup Circom
|
||||
run: wget https://github.com/iden3/circom/releases/latest/download/circom-linux-amd64 && sudo mv ./circom-linux-amd64 /usr/bin/circom && sudo chmod +x /usr/bin/circom
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
@@ -120,10 +91,11 @@ jobs:
|
||||
- name: Build libraries
|
||||
run: yarn build:libraries
|
||||
|
||||
- name: Test contracts and libraries
|
||||
- name: Test libraries, contracts and circuits
|
||||
run: yarn test:${{ matrix.type }}
|
||||
|
||||
- name: Coveralls
|
||||
if: matrix.type != 'circuits'
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
48
.github/workflows/pull-requests.yml
vendored
48
.github/workflows/pull-requests.yml
vendored
@@ -4,33 +4,20 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TREE_DEPTH: 20
|
||||
ALL_SNARK_ARTIFACTS: false
|
||||
TREE_DEPTH: 10
|
||||
|
||||
jobs:
|
||||
style:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v4
|
||||
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-
|
||||
node-version: 20
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
@@ -54,25 +41,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 20
|
||||
cache: yarn
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
# https://github.com/iden3/circuits/blob/8fffb6609ecad0b7bcda19bb908bdb544bdb3cf7/.github/workflows/main.yml#L18-L22
|
||||
- name: Setup Circom deps
|
||||
run: sudo apt-get update && sudo apt-get install -y wget nlohmann-json3-dev libgmp-dev nasm g++ build-essential
|
||||
|
||||
- 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: Setup Circom
|
||||
run: wget https://github.com/iden3/circom/releases/latest/download/circom-linux-amd64 && sudo mv ./circom-linux-amd64 /usr/bin/circom && sudo chmod +x /usr/bin/circom
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
@@ -83,5 +65,5 @@ jobs:
|
||||
- name: Build subgraph
|
||||
run: yarn build:subgraph
|
||||
|
||||
- name: Test contracts, libraries and subgraph
|
||||
- name: Test contracts, libraries, circuits and subgraph
|
||||
run: yarn test
|
||||
|
||||
20
.github/workflows/release.yml
vendored
20
.github/workflows/release.yml
vendored
@@ -13,27 +13,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
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-
|
||||
node-version: 20
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
@@ -16,12 +16,6 @@ packages/contracts/deployed-contracts/undefined.json
|
||||
packages/contracts/deployed-contracts/hardhat.json
|
||||
packages/contracts/deployed-contracts/localhost.json
|
||||
|
||||
# circuits
|
||||
circuits
|
||||
|
||||
# contracts
|
||||
Verifier*.sol
|
||||
|
||||
# production
|
||||
dist
|
||||
build
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ethereum Foundation
|
||||
Copyright (c) 2024 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
|
||||
|
||||
@@ -392,6 +392,6 @@
|
||||
"message": "X (Twitter)"
|
||||
},
|
||||
"footer.copyright": {
|
||||
"message": "Copyright © 2023 Ethereum Foundation"
|
||||
"message": "Copyright © 2024 Ethereum Foundation"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,6 +392,6 @@
|
||||
"message": "X (Twitter)"
|
||||
},
|
||||
"footer.copyright": {
|
||||
"message": "Copyright © 2023 Ethereum Foundation"
|
||||
"message": "Copyright © 2024 Ethereum Foundation"
|
||||
}
|
||||
}
|
||||
|
||||
2
apps/subgraph/.gitignore
vendored
2
apps/subgraph/.gitignore
vendored
@@ -6,3 +6,5 @@ subgraph.yaml
|
||||
|
||||
# Tests
|
||||
/tests/.bin
|
||||
|
||||
/data
|
||||
|
||||
@@ -42,13 +42,13 @@
|
||||
|
||||
## Networks
|
||||
|
||||
| Semaphore version | Sepolia | Goerli | Mumbai | Optimism Goerli | Arbitrum Goerli | Arbitrum One |
|
||||
| ----------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| v2.0 | N/A | N/A | N/A | N/A | N/A | [semaphore-protocol/arbitrum](https://thegraph.com/hosted-service/subgraph/semaphore-protocol/arbitrum) |
|
||||
| v2.5 | N/A | [semaphore-protocol/goerli](https://thegraph.com/hosted-service/subgraph/semaphore-protocol/goerli) | N/A | N/A | N/A | N/A |
|
||||
| v2.6 | N/A | [semaphore-protocol/goerli-5259d3](https://thegraph.com/hosted-service/subgraph/semaphore-protocol/goerli-5259d3) | N/A | N/A | N/A | [semaphore-protocol/arbitrum-86337c](https://thegraph.com/hosted-service/subgraph/semaphore-protocol/arbitrum-86337c) |
|
||||
| v3.0 - v3.1 | N/A | [semaphore-protocol/goerli-89490c](https://thegraph.com/hosted-service/subgraph/semaphore-protocol/goerli-89490c) | N/A | N/A | N/A | [semaphore-protocol/arbitrum-72dca3](https://thegraph.com/hosted-service/subgraph/semaphore-protocol/arbitrum-72dca3) |
|
||||
| >= v3.2 | [semaphore-sepolia](https://api.studio.thegraph.com/query/14377/semaphore-sepolia/v3.6.1) | [semaphore-goerli](https://api.studio.thegraph.com/query/14377/semaphore-goerli/v3.6.1) | [semaphore-mumbai](https://api.studio.thegraph.com/query/14377/semaphore-mumbai/v3.6.1) | [semaphore-optimism-goerli](https://api.studio.thegraph.com/query/14377/semaphore-optimism-goerli/v3.6.1) | [semaphore-arbitrum-goerli](https://api.studio.thegraph.com/query/14377/semaphore-arbitrum-goerli/v3.6.1) | [semaphore-arbitrum](https://api.studio.thegraph.com/query/14377/semaphore-arbitrum/v3.6.1) |
|
||||
| Semaphore version | Sepolia | Mumbai | Optimism Sepolia | Arbitrum Sepolia | Arbitrum One |
|
||||
| ----------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ---------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| v2.0 | N/A | N/A | N/A | N/A | [semaphore-protocol/arbitrum](https://thegraph.com/hosted-service/subgraph/semaphore-protocol/arbitrum) |
|
||||
| v2.5 | N/A | N/A | N/A | N/A | N/A |
|
||||
| v2.6 | N/A | N/A | N/A | N/A | [semaphore-protocol/arbitrum-86337c](https://thegraph.com/hosted-service/subgraph/semaphore-protocol/arbitrum-86337c) |
|
||||
| v3.0 - v3.1 | N/A | N/A | N/A | N/A | [semaphore-protocol/arbitrum-72dca3](https://thegraph.com/hosted-service/subgraph/semaphore-protocol/arbitrum-72dca3) |
|
||||
| >= v3.2 | [semaphore-sepolia](https://api.studio.thegraph.com/query/14377/semaphore-sepolia/v3.6.1) | [semaphore-mumbai](https://api.studio.thegraph.com/query/14377/semaphore-mumbai/v3.6.1) | N/A | N/A | [semaphore-arbitrum](https://api.studio.thegraph.com/query/14377/semaphore-arbitrum/v3.6.1) |
|
||||
|
||||
## 🛠 Install
|
||||
|
||||
@@ -111,15 +111,15 @@ yarn deploy <subgraph-name>
|
||||
Start services required for TheGraph node by running:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-graph.yml up
|
||||
docker compose up
|
||||
```
|
||||
|
||||
Start a local Hardhat node and deploy the [Semaphore contract](https://github.com/semaphore-protocol/semaphore/tree/main/packages/contracts):
|
||||
|
||||
```bash
|
||||
# CWD = /semaphore/packages/contracts
|
||||
yarn start
|
||||
yarn deploy:semaphore --network localhost
|
||||
yarn start --hostname 0.0.0.0
|
||||
yarn deploy --network localhost
|
||||
```
|
||||
|
||||
Create the `subgraph.yaml` file for your local network and create/deploy your subgraph:
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,6 @@
|
||||
version: "3.9"
|
||||
# https://github.com/graphprotocol/graph-node/blob/master/docker/docker-compose.yml
|
||||
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
graph-node:
|
||||
@@ -12,49 +14,35 @@ services:
|
||||
depends_on:
|
||||
- ipfs
|
||||
- postgres
|
||||
extra_hosts:
|
||||
- host.docker.internal:host-gateway
|
||||
environment:
|
||||
postgres_host: postgres
|
||||
postgres_user: graph-node
|
||||
postgres_pass: let-me-in
|
||||
postgres_db: graph-node
|
||||
postgres_port: 5432
|
||||
ipfs: "ipfs:5001"
|
||||
ethereum: "localhost:http://host.docker.internal:8545" # Should use the `localhost` as network name in subgraph.yml
|
||||
# ethereum: 'goerli:https://goerli.infura.io/v3/YOUR-API-KEY'
|
||||
ethereum: "localhost:http://host.docker.internal:8545"
|
||||
GRAPH_LOG: info
|
||||
GRAPH_ALLOW_NON_DETERMINISTIC_IPFS: 1
|
||||
networks:
|
||||
- the-graph
|
||||
|
||||
ipfs:
|
||||
image: ipfs/go-ipfs:v0.4.23
|
||||
image: ipfs/kubo:v0.14.0
|
||||
ports:
|
||||
- "5001:5001"
|
||||
volumes:
|
||||
- graph-ipfs:/data/ipfs
|
||||
networks:
|
||||
- the-graph
|
||||
|
||||
- ./data/ipfs:/data/ipfs
|
||||
postgres:
|
||||
image: postgres
|
||||
image: postgres:14
|
||||
ports:
|
||||
- "5433:5432"
|
||||
command: ["postgres", "-cshared_preload_libraries=pg_stat_statements"]
|
||||
- "5432:5432"
|
||||
command: ["postgres", "-cshared_preload_libraries=pg_stat_statements", "-cmax_connections=200"]
|
||||
environment:
|
||||
POSTGRES_USER: graph-node
|
||||
POSTGRES_PASSWORD: let-me-in
|
||||
POSTGRES_DB: graph-node
|
||||
# FIXME: remove this env. var. which we shouldn't need. Introduced by
|
||||
# <https://github.com/graphprotocol/graph-node/pull/3511>, maybe as a
|
||||
# workaround for https://github.com/docker/for-mac/issues/6270?
|
||||
PGDATA: "/var/lib/postgresql/data"
|
||||
POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C"
|
||||
networks:
|
||||
- the-graph
|
||||
volumes:
|
||||
- graph-postgres:/var/lib/postgresql/data
|
||||
|
||||
networks:
|
||||
the-graph:
|
||||
internal: false
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
graph-postgres:
|
||||
graph-ipfs:
|
||||
- ./data/postgres:/var/lib/postgresql/data
|
||||
@@ -1,43 +1,37 @@
|
||||
{
|
||||
"sepolia": {
|
||||
"Semaphore": {
|
||||
"address": "0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131",
|
||||
"startBlock": 3231111
|
||||
}
|
||||
},
|
||||
"goerli": {
|
||||
"Semaphore": {
|
||||
"address": "0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131",
|
||||
"startBlock": 8777695
|
||||
"address": "0x021dC8BF0eADd9C128490A976756C1b052edF99d",
|
||||
"startBlock": 5108003
|
||||
}
|
||||
},
|
||||
"mumbai": {
|
||||
"Semaphore": {
|
||||
"address": "0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131",
|
||||
"startBlock": 33995010
|
||||
"address": "",
|
||||
"startBlock": 0
|
||||
}
|
||||
},
|
||||
"optimism-goerli": {
|
||||
"optimism-sepolia": {
|
||||
"Semaphore": {
|
||||
"address": "0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131",
|
||||
"startBlock": 7632846
|
||||
"address": "",
|
||||
"startBlock": 0
|
||||
}
|
||||
},
|
||||
"arbitrum-goerli": {
|
||||
"arbitrum-sepolia": {
|
||||
"Semaphore": {
|
||||
"address": "0x3889927F0B5Eb1a02C6E2C20b39a1Bd4EAd76131",
|
||||
"startBlock": 15174410
|
||||
"address": "",
|
||||
"startBlock": 0
|
||||
}
|
||||
},
|
||||
"arbitrum-one": {
|
||||
"Semaphore": {
|
||||
"address": "0xc60E0Ee1a2770d5F619858C641f14FC4a6401520",
|
||||
"startBlock": 77278430
|
||||
"address": "",
|
||||
"startBlock": 0
|
||||
}
|
||||
},
|
||||
"localhost": {
|
||||
"Semaphore": {
|
||||
"address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9",
|
||||
"address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0",
|
||||
"startBlock": 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,19 @@
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"codegen": "node scripts/generateSubgraph.js ${0} && graph codegen",
|
||||
"codegen": "node scripts/generate-subgraph.js ${0} && graph codegen",
|
||||
"build": "graph build",
|
||||
"auth": "graph auth --studio",
|
||||
"deploy": "graph deploy --node https://api.studio.thegraph.com/deploy/ ${0}",
|
||||
"start-ipfs": "node scripts/start-ipfs.js",
|
||||
"create-local": "graph create --node http://localhost:8020/ semaphore",
|
||||
"remove-local": "graph remove --node http://localhost:8020/ semaphore",
|
||||
"deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 semaphore",
|
||||
"test": "graph test Semaphore -v 0.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@graphprotocol/graph-cli": "0.56.0",
|
||||
"@graphprotocol/graph-ts": "^0.31.0"
|
||||
"@graphprotocol/graph-cli": "0.67.0",
|
||||
"@graphprotocol/graph-ts": "0.32.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mustache": "^4.2.2",
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
type MerkleTree @entity {
|
||||
id: ID!
|
||||
depth: BigInt!
|
||||
depth: Int!
|
||||
root: BigInt
|
||||
zeroValue: BigInt!
|
||||
numberOfLeaves: Int!
|
||||
size: Int!
|
||||
group: Group!
|
||||
}
|
||||
|
||||
type Group @entity {
|
||||
id: ID!
|
||||
merkleTree: MerkleTree!
|
||||
timestamp: BigInt!
|
||||
merkleTree: MerkleTree!
|
||||
admin: Bytes
|
||||
members: [Member!] @derivedFrom(field: "group")
|
||||
verifiedProofs: [VerifiedProof!] @derivedFrom(field: "group")
|
||||
validatedProofs: [ValidatedProof!] @derivedFrom(field: "group")
|
||||
}
|
||||
|
||||
type Member @entity {
|
||||
id: ID!
|
||||
identityCommitment: BigInt!
|
||||
timestamp: BigInt!
|
||||
identityCommitment: BigInt!
|
||||
index: Int!
|
||||
group: Group!
|
||||
}
|
||||
|
||||
type VerifiedProof @entity {
|
||||
type ValidatedProof @entity {
|
||||
id: ID!
|
||||
signal: BigInt!
|
||||
merkleTreeRoot: BigInt!
|
||||
nullifierHash: BigInt!
|
||||
externalNullifier: BigInt!
|
||||
timestamp: BigInt!
|
||||
message: BigInt!
|
||||
scope: BigInt!
|
||||
merkleTreeRoot: BigInt!
|
||||
merkleTreeDepth: Int!
|
||||
nullifier: BigInt!
|
||||
proof: [BigInt!]!
|
||||
group: Group!
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { ByteArray, log } from "@graphprotocol/graph-ts"
|
||||
import { BigInt, ByteArray, log } from "@graphprotocol/graph-ts"
|
||||
import {
|
||||
GroupAdminUpdated,
|
||||
GroupCreated,
|
||||
MemberAdded,
|
||||
MemberRemoved,
|
||||
MemberUpdated,
|
||||
ProofVerified
|
||||
MembersAdded,
|
||||
ProofValidated
|
||||
} from "../generated/Semaphore/Semaphore"
|
||||
import { Member, Group, VerifiedProof, MerkleTree } from "../generated/schema"
|
||||
import { Group, Member, MerkleTree, ValidatedProof } from "../generated/schema"
|
||||
import { concat, hash } from "./utils"
|
||||
|
||||
/**
|
||||
@@ -22,9 +23,8 @@ export function createGroup(event: GroupCreated): void {
|
||||
|
||||
log.info("Creating group '{}'", [group.id])
|
||||
|
||||
merkleTree.depth = event.params.merkleTreeDepth
|
||||
merkleTree.zeroValue = event.params.zeroValue
|
||||
merkleTree.numberOfLeaves = 0
|
||||
merkleTree.depth = 0
|
||||
merkleTree.size = 0
|
||||
merkleTree.group = group.id
|
||||
|
||||
group.timestamp = event.block.timestamp
|
||||
@@ -76,12 +76,12 @@ export function addMember(event: MemberAdded): void {
|
||||
member.group = merkleTree.group
|
||||
member.identityCommitment = event.params.identityCommitment
|
||||
member.timestamp = event.block.timestamp
|
||||
member.index = merkleTree.numberOfLeaves
|
||||
member.index = merkleTree.size
|
||||
|
||||
member.save()
|
||||
|
||||
merkleTree.root = event.params.merkleTreeRoot
|
||||
merkleTree.numberOfLeaves += 1
|
||||
merkleTree.size += 1
|
||||
|
||||
merkleTree.save()
|
||||
|
||||
@@ -138,7 +138,7 @@ export function removeMember(event: MemberRemoved): void {
|
||||
if (member) {
|
||||
log.info("Removing member '{}' from the onchain group '{}'", [member.id, merkleTree.group])
|
||||
|
||||
member.identityCommitment = merkleTree.zeroValue
|
||||
member.identityCommitment = BigInt.fromI32(0)
|
||||
|
||||
member.save()
|
||||
|
||||
@@ -152,39 +152,83 @@ export function removeMember(event: MemberRemoved): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a verified proof in a group.
|
||||
* @param event Ethereum event emitted when a proof has been verified.
|
||||
* Adds N members to a group.
|
||||
* @param event Ethereum event emitted when many members are added to a group.
|
||||
*/
|
||||
export function addVerifiedProof(event: ProofVerified): void {
|
||||
log.debug(`ProofVerified event block {}`, [event.block.number.toString()])
|
||||
export function addMembers(event: MembersAdded): void {
|
||||
log.debug(`MembersAdded event block {}`, [event.block.number.toString()])
|
||||
|
||||
const merkleTree = MerkleTree.load(event.params.groupId.toString())
|
||||
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
const identityCommitments = event.params.identityCommitments
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
const startIndex = event.params.startIndex
|
||||
|
||||
if (merkleTree) {
|
||||
for (let i = 0; i < identityCommitments.length; i += 1) {
|
||||
const identityCommitment = identityCommitments[i]
|
||||
|
||||
const memberId = hash(
|
||||
concat(ByteArray.fromI32(startIndex.toI32() + i), ByteArray.fromBigInt(event.params.groupId))
|
||||
)
|
||||
const member = new Member(memberId)
|
||||
|
||||
log.info("Adding member '{}' in the onchain group '{}'", [member.id, merkleTree.group])
|
||||
|
||||
member.group = merkleTree.group
|
||||
member.identityCommitment = identityCommitment
|
||||
member.timestamp = event.block.timestamp
|
||||
member.index = startIndex.toI32() + i
|
||||
|
||||
member.save()
|
||||
|
||||
log.info("Member '{}' of the onchain group '{}' has been added", [member.id, merkleTree.id])
|
||||
}
|
||||
|
||||
merkleTree.root = event.params.merkleTreeRoot
|
||||
merkleTree.size += identityCommitments.length
|
||||
|
||||
merkleTree.save()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a validated proof in a group.
|
||||
* @param event Ethereum event emitted when a proof has been validated.
|
||||
*/
|
||||
export function addValidatedProof(event: ProofValidated): void {
|
||||
log.debug(`ProofValidated event block {}`, [event.block.number.toString()])
|
||||
|
||||
const group = Group.load(event.params.groupId.toString())
|
||||
|
||||
if (group) {
|
||||
const verifiedProofId = hash(
|
||||
concat(ByteArray.fromBigInt(event.params.nullifierHash), ByteArray.fromBigInt(event.params.groupId))
|
||||
const validatedProofId = hash(
|
||||
concat(ByteArray.fromBigInt(event.params.nullifier), ByteArray.fromBigInt(event.params.groupId))
|
||||
)
|
||||
|
||||
const verifiedProof = new VerifiedProof(verifiedProofId)
|
||||
const validatedProof = new ValidatedProof(validatedProofId)
|
||||
|
||||
log.info("Adding verified proof with signal '{}' in the onchain group '{}'", [
|
||||
event.params.signal.toHexString(),
|
||||
log.info("Adding validated proof with message '{}' in the onchain group '{}'", [
|
||||
event.params.message.toHexString(),
|
||||
group.id
|
||||
])
|
||||
|
||||
verifiedProof.group = group.id
|
||||
verifiedProof.signal = event.params.signal
|
||||
verifiedProof.merkleTreeRoot = event.params.merkleTreeRoot
|
||||
verifiedProof.externalNullifier = event.params.externalNullifier
|
||||
verifiedProof.nullifierHash = event.params.nullifierHash
|
||||
verifiedProof.timestamp = event.block.timestamp
|
||||
validatedProof.group = group.id
|
||||
validatedProof.message = event.params.message
|
||||
validatedProof.merkleTreeRoot = event.params.merkleTreeRoot
|
||||
validatedProof.merkleTreeDepth = event.params.merkleTreeDepth.toI32()
|
||||
validatedProof.scope = event.params.scope
|
||||
validatedProof.nullifier = event.params.nullifier
|
||||
validatedProof.proof = event.params.proof
|
||||
validatedProof.timestamp = event.block.timestamp
|
||||
|
||||
verifiedProof.save()
|
||||
validatedProof.save()
|
||||
|
||||
group.save()
|
||||
|
||||
log.info("Verified proof with signal '{}' in the onchain group '{}' has been added", [
|
||||
event.params.signal.toHexString(),
|
||||
log.info("Validated proof with message '{}' in the onchain group '{}' has been added", [
|
||||
event.params.message.toHexString(),
|
||||
group.id
|
||||
])
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ dataSources:
|
||||
- name: Semaphore
|
||||
file: ./abis/Semaphore.json
|
||||
eventHandlers:
|
||||
- event: GroupCreated(indexed uint256,uint256,uint256)
|
||||
- event: GroupCreated(indexed uint256)
|
||||
handler: createGroup
|
||||
- event: GroupAdminUpdated(indexed uint256,indexed address,indexed address)
|
||||
handler: updateGroupAdmin
|
||||
@@ -30,6 +30,8 @@ dataSources:
|
||||
handler: updateMember
|
||||
- event: MemberRemoved(indexed uint256,uint256,uint256,uint256)
|
||||
handler: removeMember
|
||||
- event: ProofVerified(indexed uint256,indexed uint256,uint256,indexed uint256,uint256)
|
||||
handler: addVerifiedProof
|
||||
- event: MembersAdded(indexed uint256,uint256,uint256[],uint256)
|
||||
handler: addMembers
|
||||
- event: ProofValidated(indexed uint256,uint256,indexed uint256,uint256,uint256,indexed uint256,uint256[8])
|
||||
handler: addValidatedProof
|
||||
file: ./src/semaphore.ts
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts"
|
||||
import { newMockEvent } from "matchstick-as"
|
||||
import { ethereum, BigInt, Address } from "@graphprotocol/graph-ts"
|
||||
import {
|
||||
GroupAdminUpdated,
|
||||
GroupCreated,
|
||||
@@ -7,21 +7,16 @@ import {
|
||||
MemberAdded,
|
||||
MemberRemoved,
|
||||
MemberUpdated,
|
||||
ProofVerified
|
||||
MembersAdded,
|
||||
ProofValidated
|
||||
} from "../generated/Semaphore/Semaphore"
|
||||
|
||||
export function createGroupCreatedEvent(groupId: BigInt, merkleTreeDepth: BigInt, zeroValue: BigInt): GroupCreated {
|
||||
export function createGroupCreatedEvent(groupId: BigInt): GroupCreated {
|
||||
const groupCreatedEvent = changetype<GroupCreated>(newMockEvent())
|
||||
|
||||
groupCreatedEvent.parameters = []
|
||||
|
||||
groupCreatedEvent.parameters.push(new ethereum.EventParam("groupId", ethereum.Value.fromUnsignedBigInt(groupId)))
|
||||
groupCreatedEvent.parameters.push(
|
||||
new ethereum.EventParam("merkleTreeDepth", ethereum.Value.fromUnsignedBigInt(merkleTreeDepth))
|
||||
)
|
||||
groupCreatedEvent.parameters.push(
|
||||
new ethereum.EventParam("zeroValue", ethereum.Value.fromUnsignedBigInt(zeroValue))
|
||||
)
|
||||
|
||||
return groupCreatedEvent
|
||||
}
|
||||
@@ -132,29 +127,56 @@ export function createMemberUpdatedEvent(
|
||||
return memberUpdatedEvent
|
||||
}
|
||||
|
||||
export function createProofVerifiedEvent(
|
||||
export function createMembersAddedEvent(
|
||||
groupId: BigInt,
|
||||
merkleTreeRoot: BigInt,
|
||||
externalNullifier: BigInt,
|
||||
nullifierHash: BigInt,
|
||||
signal: BigInt
|
||||
): ProofVerified {
|
||||
const proofVerifiedEvent = changetype<ProofVerified>(newMockEvent())
|
||||
startIndex: BigInt,
|
||||
identityCommitments: BigInt[],
|
||||
merkleTreeRoot: BigInt
|
||||
): MembersAdded {
|
||||
const membersAddedEvent = changetype<MembersAdded>(newMockEvent())
|
||||
|
||||
proofVerifiedEvent.parameters = []
|
||||
membersAddedEvent.parameters = []
|
||||
|
||||
proofVerifiedEvent.parameters.push(new ethereum.EventParam("groupId", ethereum.Value.fromUnsignedBigInt(groupId)))
|
||||
proofVerifiedEvent.parameters.push(
|
||||
membersAddedEvent.parameters.push(new ethereum.EventParam("groupId", ethereum.Value.fromUnsignedBigInt(groupId)))
|
||||
membersAddedEvent.parameters.push(
|
||||
new ethereum.EventParam("startIndex", ethereum.Value.fromUnsignedBigInt(startIndex))
|
||||
)
|
||||
membersAddedEvent.parameters.push(
|
||||
new ethereum.EventParam("identityCommitments", ethereum.Value.fromUnsignedBigIntArray(identityCommitments))
|
||||
)
|
||||
membersAddedEvent.parameters.push(
|
||||
new ethereum.EventParam("merkleTreeRoot", ethereum.Value.fromUnsignedBigInt(merkleTreeRoot))
|
||||
)
|
||||
proofVerifiedEvent.parameters.push(
|
||||
new ethereum.EventParam("nullifierHash", ethereum.Value.fromUnsignedBigInt(nullifierHash))
|
||||
)
|
||||
proofVerifiedEvent.parameters.push(
|
||||
new ethereum.EventParam("externalNullifier", ethereum.Value.fromUnsignedBigInt(externalNullifier))
|
||||
)
|
||||
|
||||
proofVerifiedEvent.parameters.push(new ethereum.EventParam("signal", ethereum.Value.fromUnsignedBigInt(signal)))
|
||||
|
||||
return proofVerifiedEvent
|
||||
return membersAddedEvent
|
||||
}
|
||||
|
||||
export function createProofVerifiedEvent(
|
||||
groupId: BigInt,
|
||||
merkleTreeDepth: BigInt,
|
||||
merkleTreeRoot: BigInt,
|
||||
nullifier: BigInt,
|
||||
message: BigInt,
|
||||
scope: BigInt,
|
||||
proof: BigInt[]
|
||||
): ProofValidated {
|
||||
const proofValidatedEvent = changetype<ProofValidated>(newMockEvent())
|
||||
|
||||
proofValidatedEvent.parameters = []
|
||||
|
||||
proofValidatedEvent.parameters.push(new ethereum.EventParam("groupId", ethereum.Value.fromUnsignedBigInt(groupId)))
|
||||
proofValidatedEvent.parameters.push(
|
||||
new ethereum.EventParam("merkleTreeDepth", ethereum.Value.fromUnsignedBigInt(merkleTreeDepth))
|
||||
)
|
||||
proofValidatedEvent.parameters.push(
|
||||
new ethereum.EventParam("merkleTreeRoot", ethereum.Value.fromUnsignedBigInt(merkleTreeRoot))
|
||||
)
|
||||
proofValidatedEvent.parameters.push(
|
||||
new ethereum.EventParam("nullifier", ethereum.Value.fromUnsignedBigInt(nullifier))
|
||||
)
|
||||
proofValidatedEvent.parameters.push(new ethereum.EventParam("message", ethereum.Value.fromUnsignedBigInt(message)))
|
||||
proofValidatedEvent.parameters.push(new ethereum.EventParam("scope", ethereum.Value.fromUnsignedBigInt(scope)))
|
||||
proofValidatedEvent.parameters.push(new ethereum.EventParam("proof", ethereum.Value.fromUnsignedBigIntArray(proof)))
|
||||
|
||||
return proofValidatedEvent
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/* eslint-disable jest/expect-expect */
|
||||
import { Address, BigInt, ByteArray } from "@graphprotocol/graph-ts"
|
||||
import { afterAll, assert, clearStore, describe, test } from "matchstick-as/assembly/index"
|
||||
import {
|
||||
addMember,
|
||||
addVerifiedProof,
|
||||
addMembers,
|
||||
addValidatedProof,
|
||||
createGroup,
|
||||
removeMember,
|
||||
updateGroupAdmin,
|
||||
@@ -15,6 +17,7 @@ import {
|
||||
createMemberAddedEvent,
|
||||
createMemberRemovedEvent,
|
||||
createMemberUpdatedEvent,
|
||||
createMembersAddedEvent,
|
||||
createProofVerifiedEvent
|
||||
} from "./semaphore-utils"
|
||||
|
||||
@@ -27,12 +30,10 @@ describe("Semaphore subgraph", () => {
|
||||
describe("# createGroup", () => {
|
||||
test("Should have created a group", () => {
|
||||
const groupId = BigInt.fromI32(234)
|
||||
const merkleTreeDepth = BigInt.fromI32(20)
|
||||
const zeroValue = BigInt.fromI32(0)
|
||||
const oldAdmin = Address.fromString("0x0000000000000000000000000000000000000000")
|
||||
const newAdmin = Address.fromString("0x0000000000000000000000000000000000000001")
|
||||
|
||||
const event1 = createGroupCreatedEvent(groupId, merkleTreeDepth, zeroValue)
|
||||
const event1 = createGroupCreatedEvent(groupId)
|
||||
const event2 = createGroupAdminUpdatedEvent(groupId, oldAdmin, newAdmin)
|
||||
|
||||
createGroup(event1)
|
||||
@@ -44,9 +45,8 @@ describe("Semaphore subgraph", () => {
|
||||
assert.fieldEquals("Group", groupId.toString(), "admin", "0x0000000000000000000000000000000000000001")
|
||||
assert.fieldEquals("Group", groupId.toString(), "merkleTree", groupId.toString())
|
||||
|
||||
assert.fieldEquals("MerkleTree", groupId.toString(), "depth", "20")
|
||||
assert.fieldEquals("MerkleTree", groupId.toString(), "zeroValue", "0")
|
||||
assert.fieldEquals("MerkleTree", groupId.toString(), "numberOfLeaves", "0")
|
||||
assert.fieldEquals("MerkleTree", groupId.toString(), "depth", "0")
|
||||
assert.fieldEquals("MerkleTree", groupId.toString(), "size", "0")
|
||||
assert.fieldEquals("MerkleTree", groupId.toString(), "group", groupId.toString())
|
||||
})
|
||||
})
|
||||
@@ -84,7 +84,7 @@ describe("Semaphore subgraph", () => {
|
||||
assert.fieldEquals("Member", id, "group", groupId.toString())
|
||||
|
||||
assert.fieldEquals("MerkleTree", groupId.toString(), "root", "999")
|
||||
assert.fieldEquals("MerkleTree", groupId.toString(), "numberOfLeaves", "1")
|
||||
assert.fieldEquals("MerkleTree", groupId.toString(), "size", "1")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -131,26 +131,61 @@ describe("Semaphore subgraph", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("# addMembers", () => {
|
||||
test("Should have added many group members at once", () => {
|
||||
const groupId = BigInt.fromI32(234)
|
||||
const startIndex = BigInt.fromI32(1)
|
||||
const identityCommitments = [BigInt.fromI32(123), BigInt.fromI32(124)]
|
||||
const merkleTreeRoot = BigInt.fromI32(999)
|
||||
const id = hash(concat(ByteArray.fromBigInt(startIndex), ByteArray.fromBigInt(groupId)))
|
||||
|
||||
const event = createMembersAddedEvent(groupId, startIndex, identityCommitments, merkleTreeRoot)
|
||||
|
||||
addMembers(event)
|
||||
|
||||
assert.entityCount("Member", 3)
|
||||
|
||||
assert.fieldEquals("Member", id, "index", "1")
|
||||
assert.fieldEquals("Member", id, "identityCommitment", "123")
|
||||
assert.fieldEquals("Member", id, "group", groupId.toString())
|
||||
|
||||
assert.fieldEquals("MerkleTree", groupId.toString(), "root", "999")
|
||||
assert.fieldEquals("MerkleTree", groupId.toString(), "size", "3")
|
||||
})
|
||||
})
|
||||
|
||||
describe("# addVerifiedProof", () => {
|
||||
test("Should have added a proof", () => {
|
||||
const groupId = BigInt.fromI32(234)
|
||||
const merkleTreeDepth = BigInt.fromI32(32)
|
||||
const merkleTreeRoot = BigInt.fromI32(1001)
|
||||
const externalNullifier = BigInt.fromI32(1)
|
||||
const nullifierHash = BigInt.fromI32(666)
|
||||
const signal = BigInt.fromI32(2)
|
||||
const id = hash(concat(ByteArray.fromBigInt(nullifierHash), ByteArray.fromBigInt(groupId)))
|
||||
const nullifier = BigInt.fromI32(666)
|
||||
const message = BigInt.fromI32(2)
|
||||
const scope = BigInt.fromI32(1)
|
||||
const proof = [BigInt.fromI32(1), BigInt.fromI32(2)]
|
||||
const id = hash(concat(ByteArray.fromBigInt(nullifier), ByteArray.fromBigInt(groupId)))
|
||||
|
||||
const event = createProofVerifiedEvent(groupId, merkleTreeRoot, externalNullifier, nullifierHash, signal)
|
||||
const event = createProofVerifiedEvent(
|
||||
groupId,
|
||||
merkleTreeDepth,
|
||||
merkleTreeRoot,
|
||||
nullifier,
|
||||
message,
|
||||
scope,
|
||||
proof
|
||||
)
|
||||
|
||||
addVerifiedProof(event)
|
||||
addValidatedProof(event)
|
||||
|
||||
assert.entityCount("VerifiedProof", 1)
|
||||
assert.entityCount("ValidatedProof", 1)
|
||||
|
||||
assert.fieldEquals("VerifiedProof", id, "merkleTreeRoot", "1001")
|
||||
assert.fieldEquals("VerifiedProof", id, "externalNullifier", "1")
|
||||
assert.fieldEquals("VerifiedProof", id, "nullifierHash", "666")
|
||||
assert.fieldEquals("VerifiedProof", id, "signal", "2")
|
||||
assert.fieldEquals("VerifiedProof", id, "group", groupId.toString())
|
||||
assert.fieldEquals("ValidatedProof", id, "group", groupId.toString())
|
||||
assert.fieldEquals("ValidatedProof", id, "merkleTreeRoot", "1001")
|
||||
assert.fieldEquals("ValidatedProof", id, "merkleTreeDepth", "32")
|
||||
assert.fieldEquals("ValidatedProof", id, "scope", "1")
|
||||
assert.fieldEquals("ValidatedProof", id, "nullifier", "666")
|
||||
assert.fieldEquals("ValidatedProof", id, "message", "2")
|
||||
assert.fieldEquals("ValidatedProof", id, "proof", `[${proof.join(", ")}]`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function Footer() {
|
||||
</Link>
|
||||
|
||||
<Text fontSize={{ base: "12px", md: "14px" }} color="text.500" pt="2">
|
||||
Copyright © 2023 Ethereum Foundation
|
||||
Copyright © 2024 Ethereum Foundation
|
||||
</Text>
|
||||
</VStack>
|
||||
</VStack>
|
||||
|
||||
@@ -103,7 +103,7 @@ export default function Navbar() {
|
||||
</Link>
|
||||
|
||||
<Text fontSize={{ base: "12px", md: "14px" }} color="text.500" pt="2">
|
||||
Copyright © 2023 Ethereum Foundation
|
||||
Copyright © 2024 Ethereum Foundation
|
||||
</Text>
|
||||
</VStack>
|
||||
</VStack>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import fs from "fs"
|
||||
import type { Config } from "@jest/types"
|
||||
|
||||
const exclude = ["circuits", "contracts"]
|
||||
|
||||
const projects: any = fs
|
||||
.readdirSync("./packages", { withFileTypes: true })
|
||||
.filter((directory) => directory.isDirectory())
|
||||
.filter((directory) => !exclude.includes(directory.name))
|
||||
.map(({ name }) => ({
|
||||
rootDir: `packages/${name}`,
|
||||
displayName: name,
|
||||
|
||||
17
package.json
17
package.json
@@ -8,24 +8,25 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build:libraries": "yarn workspaces foreach -t --no-private run build",
|
||||
"build:subgraph": "yarn workspace semaphore-subgraph codegen goerli && yarn workspace semaphore-subgraph build",
|
||||
"build:subgraph": "yarn workspace semaphore-subgraph codegen sepolia && yarn workspace semaphore-subgraph build",
|
||||
"compile:contracts": "yarn workspace semaphore-contracts compile",
|
||||
"download:snark-artifacts": "rimraf snark-artifacts && ts-node scripts/download-snark-artifacts.ts",
|
||||
"remove:template-files": "ts-node scripts/remove-template-files.ts",
|
||||
"test": "yarn test:libraries && yarn test:contracts && yarn test:subgraph",
|
||||
"test": "yarn test:libraries && yarn test:contracts && yarn test:circuits && yarn test:subgraph",
|
||||
"test:libraries": "jest --coverage",
|
||||
"test:subgraph": "yarn workspace semaphore-subgraph test",
|
||||
"test:contracts": "yarn workspace semaphore-contracts test:coverage",
|
||||
"test:circuits": "yarn workspace @semaphore-protocol/circuits test",
|
||||
"lint": "eslint . --ext .js,.ts,.tsx && yarn workspace semaphore-contracts lint",
|
||||
"prettier": "prettier -c .",
|
||||
"prettier:write": "prettier -w .",
|
||||
"docs": "typedoc --cname js.semaphore.pse.dev --githubPages true",
|
||||
"version:bump": "yarn workspaces foreach --no-private version -d ${0} && yarn version apply --all && git commit -am \"chore: v${0}\" && git tag v${0}",
|
||||
"version:publish": "yarn build:libraries && yarn remove:template-files && yarn workspaces foreach --no-private npm publish --tolerate-republish --access public",
|
||||
"version:publish": "yarn build:libraries && yarn clean:cli-templates && yarn workspaces foreach --no-private npm publish --tolerate-republish --access public",
|
||||
"version:release": "changelogithub",
|
||||
"clean": "ts-node scripts/clean-apps.ts && ts-node scripts/clean-packages.ts && yarn clean:cli-templates && rimraf node_modules",
|
||||
"clean:cli-templates": "ts-node scripts/clean-cli-templates.ts",
|
||||
"commit": "cz",
|
||||
"precommit": "lint-staged",
|
||||
"postinstall": "yarn download:snark-artifacts && husky install"
|
||||
"postinstall": "husky install"
|
||||
},
|
||||
"keywords": [
|
||||
"ethereum",
|
||||
@@ -57,9 +58,8 @@
|
||||
"@types/download": "^8.0.1",
|
||||
"@types/glob": "^7.2.0",
|
||||
"@types/jest": "^27.4.0",
|
||||
"@types/node": "^17.0.9",
|
||||
"@types/node": "^20",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@types/snarkjs": "^0.7.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
||||
"@typescript-eslint/parser": "^5.9.1",
|
||||
"babel-jest": "^27.4.6",
|
||||
@@ -83,6 +83,7 @@
|
||||
"prettier": "^2.5.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.64.0",
|
||||
"snarkjs": "^0.7.2",
|
||||
"ts-node": "^10.4.0",
|
||||
"tslib": "^2.3.1",
|
||||
"typedoc": "^0.25.1",
|
||||
|
||||
3
packages/circuits/.gitignore
vendored
Normal file
3
packages/circuits/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
ptau
|
||||
main
|
||||
test
|
||||
7
packages/circuits/.mocharc.json
Normal file
7
packages/circuits/.mocharc.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extension": ["ts"],
|
||||
"require": "ts-node/register",
|
||||
"spec": "./tests/*.test.ts",
|
||||
"timeout": 100000,
|
||||
"exit": true
|
||||
}
|
||||
21
packages/circuits/LICENSE
Normal file
21
packages/circuits/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 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.
|
||||
@@ -9,8 +9,14 @@
|
||||
<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 href="https://github.com/semaphore-protocol/semaphore/tree/main/packages/circuits/LICENSE">
|
||||
<img alt="NPM license" src="https://img.shields.io/npm/l/%40semaphore-protocol%2Fcircuits?style=flat-square">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/@semaphore-protocol/circuits">
|
||||
<img alt="NPM version" src="https://img.shields.io/npm/v/@semaphore-protocol/circuits?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/circuits">
|
||||
<img alt="Downloads" src="https://img.shields.io/npm/dm/@semaphore-protocol/circuits.svg?style=flat-square" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
17
packages/circuits/circomkit.json
Normal file
17
packages/circuits/circomkit.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"protocol": "groth16",
|
||||
"prime": "bn128",
|
||||
"version": "2.1.5",
|
||||
"circuits": "./circuits.json",
|
||||
"dirPtau": "./ptau",
|
||||
"dirCircuits": "./",
|
||||
"dirInputs": "./inputs",
|
||||
"dirBuild": "./build",
|
||||
"optimization": 2,
|
||||
"inspect": true,
|
||||
"include": ["../../node_modules/circomlib/circuits", "../../node_modules/@zk-kit/circuits/circom"],
|
||||
"groth16numContributions": 1,
|
||||
"groth16askForEntropy": false,
|
||||
"logLevel": "INFO",
|
||||
"verbose": true
|
||||
}
|
||||
8
packages/circuits/circuits.json
Normal file
8
packages/circuits/circuits.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"semaphore": {
|
||||
"file": "semaphore",
|
||||
"template": "Semaphore",
|
||||
"pubs": ["message", "scope"],
|
||||
"params": [10]
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,39 @@
|
||||
{
|
||||
"name": "circuits",
|
||||
"private": true,
|
||||
"name": "@semaphore-protocol/circuits",
|
||||
"version": "4.0.0-alpha",
|
||||
"description": "Semaphore Circom circuits to generate zero-knowledge proofs.",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"**/*.circom",
|
||||
"!main",
|
||||
"!test",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"repository": "https://github.com/semaphore-protocol/semaphore",
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore/tree/main/packages/data",
|
||||
"bugs": {
|
||||
"url": "https://github.com/semaphore-protocol/semaphore.git/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "circomkit compile semaphore",
|
||||
"setup": "circomkit setup semaphore",
|
||||
"test": "mocha"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"circomlib": "^2.0.2"
|
||||
}
|
||||
"@zk-kit/circuits": "0.2.4",
|
||||
"circomlib": "2.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^10.0.6",
|
||||
"@zk-kit/eddsa-poseidon": "0.3.1",
|
||||
"@zk-kit/imt": "^2.0.0-beta",
|
||||
"circomkit": "^0.0.19",
|
||||
"mocha": "^10.2.0",
|
||||
"poseidon-lite": "^0.2.0"
|
||||
},
|
||||
"stableVersion": "3.15.1"
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,90 +1,25 @@
|
||||
pragma circom 2.0.0;
|
||||
pragma circom 2.1.5;
|
||||
|
||||
include "../node_modules/circomlib/circuits/poseidon.circom";
|
||||
include "./tree.circom";
|
||||
include "babyjub.circom";
|
||||
include "poseidon.circom";
|
||||
include "binary-merkle-root.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() {
|
||||
template Semaphore(MAX_DEPTH) {
|
||||
signal input secret;
|
||||
signal input merkleProofLength, merkleProofIndices[MAX_DEPTH], merkleProofSiblings[MAX_DEPTH];
|
||||
signal input message;
|
||||
signal input scope;
|
||||
|
||||
signal output out;
|
||||
signal output merkleRoot, nullifier;
|
||||
|
||||
component poseidon = Poseidon(1);
|
||||
var Ax, Ay;
|
||||
(Ax, Ay) = BabyPbk()(secret);
|
||||
|
||||
poseidon.inputs[0] <== secret;
|
||||
var identityCommitment = Poseidon(2)([Ax, Ay]);
|
||||
|
||||
out <== poseidon.out;
|
||||
merkleRoot <== BinaryMerkleRoot(MAX_DEPTH)(identityCommitment, merkleProofLength, merkleProofIndices, merkleProofSiblings);
|
||||
nullifier <== Poseidon(2)([scope, secret]);
|
||||
|
||||
// Dummy constraint to prevent compiler from optimizing it.
|
||||
signal dummySquare <== message * message;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// The current Semaphore smart contracts require nLevels <= 32 and nLevels >= 16.
|
||||
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);
|
||||
|
||||
12
packages/circuits/tests/common.ts
Normal file
12
packages/circuits/tests/common.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Circomkit } from "circomkit"
|
||||
import { readFileSync } from "fs"
|
||||
import path from "path"
|
||||
|
||||
const configFilePath = path.join(__dirname, "../circomkit.json")
|
||||
const config = JSON.parse(readFileSync(configFilePath, "utf-8"))
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const circomkit = new Circomkit({
|
||||
...config,
|
||||
verbose: false
|
||||
})
|
||||
72
packages/circuits/tests/semaphore.test.ts
Normal file
72
packages/circuits/tests/semaphore.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { derivePublicKey, deriveSecretScalar } from "@zk-kit/eddsa-poseidon"
|
||||
import { LeanIMT } from "@zk-kit/imt"
|
||||
import { WitnessTester } from "circomkit"
|
||||
import { poseidon2 } from "poseidon-lite"
|
||||
import { circomkit } from "./common"
|
||||
|
||||
describe("semaphore", () => {
|
||||
let circuit: WitnessTester<
|
||||
["secret", "merkleProofLength", "merkleProofIndices", "merkleProofSiblings", "scope", "message"],
|
||||
["nullifier", "merkleRoot"]
|
||||
>
|
||||
|
||||
const MAX_DEPTH = 20
|
||||
|
||||
const scope = 32
|
||||
const message = 43
|
||||
|
||||
const secret = 1
|
||||
const publicKey = derivePublicKey(secret)
|
||||
|
||||
const leaf = poseidon2(publicKey)
|
||||
|
||||
const tree = new LeanIMT((a, b) => poseidon2([a, b]))
|
||||
|
||||
tree.insert(leaf)
|
||||
|
||||
for (let i = 1; i < 4; i += 1) {
|
||||
tree.insert(BigInt(i))
|
||||
}
|
||||
|
||||
const { siblings: merkleProofSiblings, index } = tree.generateProof(0)
|
||||
|
||||
// The index must be converted to a list of indices, 1 for each tree level.
|
||||
// The circuit tree depth is 20, so the number of siblings must be 20, even if
|
||||
// the tree depth is actually 3. The missing siblings can be set to 0, as they
|
||||
// won't be used to calculate the root in the circuit.
|
||||
const merkleProofIndices: number[] = []
|
||||
|
||||
for (let i = 0; i < MAX_DEPTH; i += 1) {
|
||||
merkleProofIndices.push((index >> i) & 1)
|
||||
|
||||
if (merkleProofSiblings[i] === undefined) {
|
||||
merkleProofSiblings[i] = BigInt(0)
|
||||
}
|
||||
}
|
||||
|
||||
const INPUT = {
|
||||
secret: deriveSecretScalar(secret),
|
||||
merkleProofLength: tree.depth,
|
||||
merkleProofIndices,
|
||||
merkleProofSiblings,
|
||||
scope,
|
||||
message
|
||||
}
|
||||
|
||||
const OUTPUT = {
|
||||
nullifier: poseidon2([scope, deriveSecretScalar(secret)]),
|
||||
merkleRoot: tree.root
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester("semaphore", {
|
||||
file: "semaphore",
|
||||
template: "Semaphore",
|
||||
params: [MAX_DEPTH]
|
||||
})
|
||||
})
|
||||
|
||||
it("Should calculate the root and the nullifier correctly", async () => {
|
||||
await circuit.expectPass(INPUT, OUTPUT)
|
||||
})
|
||||
})
|
||||
@@ -1,40 +0,0 @@
|
||||
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];
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
INFURA_API_KEY=
|
||||
ETHEREUM_PRIVATE_KEY=
|
||||
REPORT_GAS=false
|
||||
ETHERSCAN_API_KEY=
|
||||
COINMARKETCAP_API_KEY=
|
||||
|
||||
@@ -44,7 +44,7 @@ cp .env.example .env
|
||||
3. And deploy your contract.
|
||||
|
||||
```bash
|
||||
yarn deploy --semaphore <semaphore-address> --group <group-id> --network goerli
|
||||
yarn deploy --semaphore <semaphore-address> --group <group-id> --network sepolia
|
||||
```
|
||||
|
||||
> **Note**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
pragma solidity ^0.8.23;
|
||||
|
||||
import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
|
||||
|
||||
@@ -8,11 +8,11 @@ contract Feedback {
|
||||
|
||||
uint256 public groupId;
|
||||
|
||||
constructor(address semaphoreAddress, uint256 _groupId) {
|
||||
semaphore = ISemaphore(semaphoreAddress);
|
||||
constructor(ISemaphore _semaphore, uint256 _groupId) {
|
||||
semaphore = _semaphore;
|
||||
groupId = _groupId;
|
||||
|
||||
semaphore.createGroup(groupId, 20, address(this));
|
||||
semaphore.createGroup(groupId, address(this));
|
||||
}
|
||||
|
||||
function joinGroup(uint256 identityCommitment) external {
|
||||
@@ -20,11 +20,12 @@ contract Feedback {
|
||||
}
|
||||
|
||||
function sendFeedback(
|
||||
uint256 feedback,
|
||||
uint256 merkleTreeDepth,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 nullifierHash,
|
||||
uint256 nullifier,
|
||||
uint256 feedback,
|
||||
uint256[8] calldata proof
|
||||
) external {
|
||||
semaphore.verifyProof(groupId, merkleTreeRoot, feedback, nullifierHash, groupId, proof);
|
||||
semaphore.validateProof(groupId, merkleTreeDepth, merkleTreeRoot, nullifier, feedback, groupId, proof);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "@nomiclabs/hardhat-ethers"
|
||||
import "@nomicfoundation/hardhat-chai-matchers"
|
||||
import "@nomicfoundation/hardhat-ethers"
|
||||
import "@nomicfoundation/hardhat-verify"
|
||||
import "@semaphore-protocol/hardhat"
|
||||
import "@typechain/hardhat"
|
||||
import { config as dotenvConfig } from "dotenv"
|
||||
@@ -7,7 +8,6 @@ import "hardhat-gas-reporter"
|
||||
import { HardhatUserConfig } from "hardhat/config"
|
||||
import { NetworksUserConfig } from "hardhat/types"
|
||||
import "solidity-coverage"
|
||||
import { config } from "./package.json"
|
||||
import "./tasks/deploy"
|
||||
|
||||
dotenvConfig()
|
||||
@@ -21,11 +21,6 @@ function getNetworks(): NetworksUserConfig {
|
||||
const infuraApiKey = process.env.INFURA_API_KEY
|
||||
|
||||
return {
|
||||
goerli: {
|
||||
url: `https://goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 5,
|
||||
accounts
|
||||
},
|
||||
sepolia: {
|
||||
url: `https://sepolia.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 11155111,
|
||||
@@ -36,13 +31,18 @@ function getNetworks(): NetworksUserConfig {
|
||||
chainId: 80001,
|
||||
accounts
|
||||
},
|
||||
"optimism-goerli": {
|
||||
url: `https://optimism-goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 420,
|
||||
"optimism-sepolia": {
|
||||
url: `https://optimism-sepolia.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 11155420,
|
||||
accounts
|
||||
},
|
||||
"arbitrum-sepolia": {
|
||||
url: "https://sepolia-rollup.arbitrum.io/rpc",
|
||||
chainId: 421614,
|
||||
accounts
|
||||
},
|
||||
arbitrum: {
|
||||
url: `https://arbitrum-mainnet.infura.io/v3/${infuraApiKey}`,
|
||||
url: "https://arb1.arbitrum.io/rpc",
|
||||
chainId: 42161,
|
||||
accounts
|
||||
}
|
||||
@@ -50,13 +50,7 @@ function getNetworks(): NetworksUserConfig {
|
||||
}
|
||||
|
||||
const hardhatConfig: HardhatUserConfig = {
|
||||
solidity: config.solidity,
|
||||
paths: {
|
||||
sources: config.paths.contracts,
|
||||
tests: config.paths.tests,
|
||||
cache: config.paths.cache,
|
||||
artifacts: config.paths.build.contracts
|
||||
},
|
||||
solidity: "0.8.23",
|
||||
networks: {
|
||||
hardhat: {
|
||||
chainId: 1337
|
||||
@@ -69,8 +63,13 @@ const hardhatConfig: HardhatUserConfig = {
|
||||
coinmarketcap: process.env.COINMARKETCAP_API_KEY
|
||||
},
|
||||
typechain: {
|
||||
outDir: config.paths.build.typechain,
|
||||
target: "ethers-v5"
|
||||
target: "ethers-v6"
|
||||
},
|
||||
etherscan: {
|
||||
apiKey: process.env.ETHERSCAN_API_KEY
|
||||
},
|
||||
sourcify: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/cli-template-contracts-hardhat",
|
||||
"version": "3.15.2",
|
||||
"version": "4.0.0-alpha",
|
||||
"description": "Semaphore Hardhat template.",
|
||||
"license": "Unlicense",
|
||||
"files": [
|
||||
@@ -20,9 +20,8 @@
|
||||
"scripts": {
|
||||
"dev": "hardhat node & yarn compile && yarn deploy --network localhost",
|
||||
"compile": "hardhat compile",
|
||||
"download:snark-artifacts": "hardhat run scripts/download-snark-artifacts.ts",
|
||||
"deploy": "yarn compile && hardhat deploy",
|
||||
"test": "hardhat run scripts/download-snark-artifacts.ts && hardhat test",
|
||||
"test": "hardhat test",
|
||||
"test:report-gas": "REPORT_GAS=true hardhat test",
|
||||
"test:coverage": "hardhat coverage",
|
||||
"typechain": "hardhat typechain",
|
||||
@@ -31,48 +30,37 @@
|
||||
"devDependencies": {
|
||||
"@ethersproject/abi": "^5.4.7",
|
||||
"@ethersproject/providers": "^5.4.7",
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^1.0.0",
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^2.0.3",
|
||||
"@nomicfoundation/hardhat-ethers": "^3.0.0",
|
||||
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
|
||||
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.0",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.0.0",
|
||||
"@semaphore-protocol/group": "3.15.2",
|
||||
"@semaphore-protocol/hardhat": "3.15.2",
|
||||
"@semaphore-protocol/identity": "3.15.2",
|
||||
"@semaphore-protocol/proof": "3.15.2",
|
||||
"@typechain/ethers-v5": "^10.1.0",
|
||||
"@typechain/hardhat": "^6.1.2",
|
||||
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
|
||||
"@nomicfoundation/hardhat-verify": "^2.0.0",
|
||||
"@semaphore-protocol/group": "4.0.0-alpha",
|
||||
"@semaphore-protocol/hardhat": "4.0.0-alpha",
|
||||
"@semaphore-protocol/identity": "4.0.0-alpha",
|
||||
"@semaphore-protocol/proof": "4.0.0-alpha",
|
||||
"@typechain/ethers-v6": "^0.5.0",
|
||||
"@typechain/hardhat": "^9.0.0",
|
||||
"@types/chai": "^4.2.0",
|
||||
"@types/download": "^8.0.1",
|
||||
"@types/mocha": "^9.1.0",
|
||||
"@types/node": ">=12.0.0",
|
||||
"@types/node": "^20",
|
||||
"chai": "^4.2.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"download": "^8.0.0",
|
||||
"ethers": "^5.4.7",
|
||||
"hardhat": "^2.11.0",
|
||||
"ethers": "^6.4.0",
|
||||
"hardhat": "^2.19.4",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
"solidity-coverage": "^0.8.1",
|
||||
"ts-node": ">=8.0.0",
|
||||
"typechain": "^8.1.0",
|
||||
"typescript": ">=4.5.0"
|
||||
"ts-node": "^10.9.2",
|
||||
"typechain": "^8.3.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@semaphore-protocol/contracts": "3.15.2"
|
||||
"@semaphore-protocol/contracts": "4.0.0-alpha"
|
||||
},
|
||||
"config": {
|
||||
"solidity": {
|
||||
"version": "0.8.4"
|
||||
},
|
||||
"paths": {
|
||||
"contracts": "./contracts",
|
||||
"tests": "./test",
|
||||
"cache": "./cache",
|
||||
"build": {
|
||||
"snark-artifacts": "./build/snark-artifacts",
|
||||
"contracts": "./build/contracts",
|
||||
"typechain": "./build/typechain"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"stableVersion": "3.15.2"
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
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/${20}`
|
||||
|
||||
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)
|
||||
})
|
||||
@@ -10,7 +10,7 @@ task("deploy", "Deploy a Feedback contract")
|
||||
logs
|
||||
})
|
||||
|
||||
semaphoreAddress = semaphore.address
|
||||
semaphoreAddress = await semaphore.getAddress()
|
||||
}
|
||||
|
||||
if (!groupId) {
|
||||
@@ -21,10 +21,8 @@ task("deploy", "Deploy a Feedback contract")
|
||||
|
||||
const feedbackContract = await FeedbackFactory.deploy(semaphoreAddress, groupId)
|
||||
|
||||
await feedbackContract.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Feedback contract has been deployed to: ${feedbackContract.address}`)
|
||||
console.info(`Feedback contract has been deployed to: ${await feedbackContract.getAddress()}`)
|
||||
}
|
||||
|
||||
return feedbackContract
|
||||
|
||||
@@ -2,18 +2,17 @@ import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { generateProof } from "@semaphore-protocol/proof"
|
||||
import { expect } from "chai"
|
||||
import { formatBytes32String } from "ethers/lib/utils"
|
||||
import { encodeBytes32String } from "ethers"
|
||||
import { run } from "hardhat"
|
||||
// @ts-ignore: typechain folder will be generated after contracts compilation
|
||||
import { Feedback } from "../build/typechain"
|
||||
import { config } from "../package.json"
|
||||
|
||||
describe("Feedback", () => {
|
||||
let feedbackContract: Feedback
|
||||
let semaphoreContract: string
|
||||
|
||||
const groupId = "42"
|
||||
const group = new Group(groupId)
|
||||
const group = new Group()
|
||||
const users: Identity[] = []
|
||||
|
||||
before(async () => {
|
||||
@@ -21,7 +20,7 @@ describe("Feedback", () => {
|
||||
logs: false
|
||||
})
|
||||
|
||||
feedbackContract = await run("deploy", { logs: false, group: groupId, semaphore: semaphore.address })
|
||||
feedbackContract = await run("deploy", { logs: false, group: groupId, semaphore: await semaphore.getAddress() })
|
||||
semaphoreContract = semaphore
|
||||
|
||||
users.push(new Identity())
|
||||
@@ -43,27 +42,30 @@ describe("Feedback", () => {
|
||||
})
|
||||
|
||||
describe("# sendFeedback", () => {
|
||||
const wasmFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.wasm`
|
||||
const zkeyFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.zkey`
|
||||
|
||||
it("Should allow users to send feedback anonymously", async () => {
|
||||
const feedback = formatBytes32String("Hello World")
|
||||
const feedback = encodeBytes32String("Hello World")
|
||||
|
||||
const fullProof = await generateProof(users[1], group, groupId, feedback, {
|
||||
wasmFilePath,
|
||||
zkeyFilePath
|
||||
})
|
||||
const fullProof = await generateProof(users[1], group, feedback, groupId)
|
||||
|
||||
const transaction = feedbackContract.sendFeedback(
|
||||
feedback,
|
||||
fullProof.merkleTreeDepth,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.nullifierHash,
|
||||
fullProof.nullifier,
|
||||
feedback,
|
||||
fullProof.proof
|
||||
)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreContract, "ProofVerified")
|
||||
.withArgs(groupId, fullProof.merkleTreeRoot, fullProof.nullifierHash, groupId, fullProof.signal)
|
||||
.to.emit(semaphoreContract, "ProofValidated")
|
||||
.withArgs(
|
||||
groupId,
|
||||
fullProof.merkleTreeDepth,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.nullifier,
|
||||
fullProof.message,
|
||||
groupId,
|
||||
fullProof.proof
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
DEFAULT_NETWORK=localhost
|
||||
INFURA_API_KEY=
|
||||
ETHEREUM_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
|
||||
FEEDBACK_CONTRACT_ADDRESS=0x5fc8d32690cc91d4c39d9d3abcbd16989f875707
|
||||
SEMAPHORE_CONTRACT_ADDRESS=0xdc64a140aa3e981100a9beca4e685f962f0cf6c9
|
||||
FEEDBACK_CONTRACT_ADDRESS=0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9
|
||||
SEMAPHORE_CONTRACT_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
|
||||
OPENZEPPELIN_AUTOTASK_WEBHOOK=
|
||||
GROUP_ID=42
|
||||
REPORT_GAS=false
|
||||
|
||||
@@ -52,7 +52,11 @@ node_modules/
|
||||
# Generate output
|
||||
dist
|
||||
build
|
||||
|
||||
# Hardhat files
|
||||
artifacts
|
||||
cache
|
||||
typechain-types
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
|
||||
@@ -25,7 +25,7 @@ yarn dev
|
||||
1. Go to the `apps/contracts` directory and deploy your contract:
|
||||
|
||||
```bash
|
||||
yarn deploy --semaphore <semaphore-address> --group <group-id> --network arbitrum-goerli
|
||||
yarn deploy --semaphore <semaphore-address> --group <group-id> --network arbitrum-sepolia
|
||||
```
|
||||
|
||||
2. Update your `.env` file with your new contract address, the group id and the semaphore contract address.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
pragma solidity ^0.8.23;
|
||||
|
||||
import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
|
||||
|
||||
@@ -8,11 +8,11 @@ contract Feedback {
|
||||
|
||||
uint256 public groupId;
|
||||
|
||||
constructor(address semaphoreAddress, uint256 _groupId) {
|
||||
semaphore = ISemaphore(semaphoreAddress);
|
||||
constructor(ISemaphore _semaphore, uint256 _groupId) {
|
||||
semaphore = _semaphore;
|
||||
groupId = _groupId;
|
||||
|
||||
semaphore.createGroup(groupId, 20, address(this));
|
||||
semaphore.createGroup(groupId, address(this));
|
||||
}
|
||||
|
||||
function joinGroup(uint256 identityCommitment) external {
|
||||
@@ -20,11 +20,12 @@ contract Feedback {
|
||||
}
|
||||
|
||||
function sendFeedback(
|
||||
uint256 feedback,
|
||||
uint256 merkleTreeDepth,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 nullifierHash,
|
||||
uint256 nullifier,
|
||||
uint256 feedback,
|
||||
uint256[8] calldata proof
|
||||
) external {
|
||||
semaphore.verifyProof(groupId, merkleTreeRoot, feedback, nullifierHash, groupId, proof);
|
||||
semaphore.validateProof(groupId, merkleTreeDepth, merkleTreeRoot, nullifier, feedback, groupId, proof);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import "@nomicfoundation/hardhat-chai-matchers"
|
||||
import "@nomiclabs/hardhat-ethers"
|
||||
import "@nomiclabs/hardhat-etherscan"
|
||||
import "@nomicfoundation/hardhat-ethers"
|
||||
import "@nomicfoundation/hardhat-verify"
|
||||
import "@semaphore-protocol/hardhat"
|
||||
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/deploy"
|
||||
|
||||
dotenvConfig({ path: resolve(__dirname, "../../.env") })
|
||||
dotenvConfig()
|
||||
|
||||
function getNetworks(): NetworksUserConfig {
|
||||
if (!process.env.INFURA_API_KEY || !process.env.ETHEREUM_PRIVATE_KEY) {
|
||||
@@ -23,11 +21,6 @@ function getNetworks(): NetworksUserConfig {
|
||||
const infuraApiKey = process.env.INFURA_API_KEY
|
||||
|
||||
return {
|
||||
goerli: {
|
||||
url: `https://goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 5,
|
||||
accounts
|
||||
},
|
||||
sepolia: {
|
||||
url: `https://sepolia.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 11155111,
|
||||
@@ -38,14 +31,14 @@ function getNetworks(): NetworksUserConfig {
|
||||
chainId: 80001,
|
||||
accounts
|
||||
},
|
||||
"optimism-goerli": {
|
||||
url: `https://optimism-goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 420,
|
||||
"optimism-sepolia": {
|
||||
url: `https://optimism-sepolia.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 11155420,
|
||||
accounts
|
||||
},
|
||||
"arbitrum-goerli": {
|
||||
url: "https://goerli-rollup.arbitrum.io/rpc",
|
||||
chainId: 421613,
|
||||
"arbitrum-sepolia": {
|
||||
url: "https://sepolia-rollup.arbitrum.io/rpc",
|
||||
chainId: 421614,
|
||||
accounts
|
||||
},
|
||||
arbitrum: {
|
||||
@@ -57,13 +50,7 @@ function getNetworks(): NetworksUserConfig {
|
||||
}
|
||||
|
||||
const hardhatConfig: HardhatUserConfig = {
|
||||
solidity: config.solidity,
|
||||
paths: {
|
||||
sources: config.paths.contracts,
|
||||
tests: config.paths.tests,
|
||||
cache: config.paths.cache,
|
||||
artifacts: config.paths.build.contracts
|
||||
},
|
||||
solidity: "0.8.23",
|
||||
networks: {
|
||||
hardhat: {
|
||||
chainId: 1337
|
||||
@@ -76,11 +63,13 @@ const hardhatConfig: HardhatUserConfig = {
|
||||
coinmarketcap: process.env.COINMARKETCAP_API_KEY
|
||||
},
|
||||
typechain: {
|
||||
outDir: config.paths.build.typechain,
|
||||
target: "ethers-v5"
|
||||
target: "ethers-v6"
|
||||
},
|
||||
etherscan: {
|
||||
apiKey: process.env.ETHERSCAN_API_KEY
|
||||
},
|
||||
sourcify: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,55 +6,39 @@
|
||||
"scripts": {
|
||||
"dev": "hardhat node & yarn compile && yarn deploy --network localhost",
|
||||
"compile": "hardhat compile",
|
||||
"download:snark-artifacts": "hardhat run scripts/download-snark-artifacts.ts",
|
||||
"deploy": "yarn compile && hardhat deploy",
|
||||
"test": "hardhat run scripts/download-snark-artifacts.ts && hardhat test",
|
||||
"test": "hardhat test",
|
||||
"test:report-gas": "REPORT_GAS=true hardhat test",
|
||||
"test:coverage": "hardhat coverage",
|
||||
"typechain": "hardhat typechain",
|
||||
"lint": "solhint 'contracts/**/*.sol'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^1.0.5",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.0",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.1.7",
|
||||
"@semaphore-protocol/group": "3.15.2",
|
||||
"@semaphore-protocol/hardhat": "3.15.2",
|
||||
"@semaphore-protocol/identity": "3.15.2",
|
||||
"@semaphore-protocol/proof": "3.15.2",
|
||||
"@typechain/ethers-v5": "^10.0.0",
|
||||
"@typechain/hardhat": "^6.0.0",
|
||||
"@types/chai": "^4.3.1",
|
||||
"@types/download": "^8.0.1",
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^2.0.3",
|
||||
"@nomicfoundation/hardhat-ethers": "^3.0.0",
|
||||
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
|
||||
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
|
||||
"@nomicfoundation/hardhat-verify": "^2.0.0",
|
||||
"@semaphore-protocol/group": "4.0.0-alpha",
|
||||
"@semaphore-protocol/hardhat": "4.0.0-alpha",
|
||||
"@semaphore-protocol/identity": "4.0.0-alpha",
|
||||
"@semaphore-protocol/proof": "4.0.0-alpha",
|
||||
"@typechain/ethers-v6": "^0.5.0",
|
||||
"@typechain/hardhat": "^9.0.0",
|
||||
"@types/chai": "^4.2.0",
|
||||
"@types/mocha": "^9.1.1",
|
||||
"chai": "^4.2.0",
|
||||
"dotenv": "^14.3.2",
|
||||
"download": "^8.0.0",
|
||||
"ethers": "^5.0.0",
|
||||
"hardhat": "^2.8.4",
|
||||
"ethers": "^6.4.0",
|
||||
"hardhat": "^2.19.4",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
"prettier-plugin-solidity": "^1.0.0-beta.19",
|
||||
"prettier-plugin-solidity": "^1.3.1",
|
||||
"solhint": "^3.3.6",
|
||||
"solhint-plugin-prettier": "^0.0.5",
|
||||
"solidity-coverage": "^0.7.21",
|
||||
"typechain": "^8.0.0"
|
||||
"solidity-coverage": "^0.8.0",
|
||||
"typechain": "^8.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@semaphore-protocol/contracts": "3.15.2"
|
||||
},
|
||||
"config": {
|
||||
"solidity": {
|
||||
"version": "0.8.4"
|
||||
},
|
||||
"paths": {
|
||||
"contracts": "./contracts",
|
||||
"tests": "./test",
|
||||
"cache": "./cache",
|
||||
"build": {
|
||||
"snark-artifacts": "./build/snark-artifacts",
|
||||
"contracts": "./build/contracts",
|
||||
"typechain": "./build/typechain"
|
||||
}
|
||||
}
|
||||
"@semaphore-protocol/contracts": "4.0.0-alpha"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
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/${20}`
|
||||
|
||||
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)
|
||||
})
|
||||
@@ -10,7 +10,7 @@ task("deploy", "Deploy a Feedback contract")
|
||||
logs
|
||||
})
|
||||
|
||||
semaphoreAddress = semaphore.address
|
||||
semaphoreAddress = await semaphore.getAddress()
|
||||
}
|
||||
|
||||
if (!groupId) {
|
||||
@@ -21,10 +21,8 @@ task("deploy", "Deploy a Feedback contract")
|
||||
|
||||
const feedbackContract = await FeedbackFactory.deploy(semaphoreAddress, groupId)
|
||||
|
||||
await feedbackContract.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Feedback contract has been deployed to: ${feedbackContract.address}`)
|
||||
console.info(`Feedback contract has been deployed to: ${await feedbackContract.getAddress()}`)
|
||||
}
|
||||
|
||||
return feedbackContract
|
||||
|
||||
@@ -2,18 +2,17 @@ import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { generateProof } from "@semaphore-protocol/proof"
|
||||
import { expect } from "chai"
|
||||
import { formatBytes32String } from "ethers/lib/utils"
|
||||
import { encodeBytes32String } from "ethers"
|
||||
import { run } from "hardhat"
|
||||
// @ts-ignore: typechain folder will be generated after contracts compilation
|
||||
import { Feedback } from "../build/typechain"
|
||||
import { config } from "../package.json"
|
||||
|
||||
describe("Feedback", () => {
|
||||
let feedbackContract: Feedback
|
||||
let semaphoreContract: string
|
||||
|
||||
const groupId = "42"
|
||||
const group = new Group(groupId)
|
||||
const group = new Group()
|
||||
const users: Identity[] = []
|
||||
|
||||
before(async () => {
|
||||
@@ -21,7 +20,7 @@ describe("Feedback", () => {
|
||||
logs: false
|
||||
})
|
||||
|
||||
feedbackContract = await run("deploy", { logs: false, group: groupId, semaphore: semaphore.address })
|
||||
feedbackContract = await run("deploy", { logs: false, group: groupId, semaphore: await semaphore.getAddress() })
|
||||
semaphoreContract = semaphore
|
||||
|
||||
users.push(new Identity())
|
||||
@@ -43,27 +42,30 @@ describe("Feedback", () => {
|
||||
})
|
||||
|
||||
describe("# sendFeedback", () => {
|
||||
const wasmFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.wasm`
|
||||
const zkeyFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.zkey`
|
||||
|
||||
it("Should allow users to send feedback anonymously", async () => {
|
||||
const feedback = formatBytes32String("Hello World")
|
||||
const feedback = encodeBytes32String("Hello World")
|
||||
|
||||
const fullProof = await generateProof(users[1], group, groupId, feedback, {
|
||||
wasmFilePath,
|
||||
zkeyFilePath
|
||||
})
|
||||
const fullProof = await generateProof(users[1], group, feedback, groupId)
|
||||
|
||||
const transaction = feedbackContract.sendFeedback(
|
||||
feedback,
|
||||
fullProof.merkleTreeDepth,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.nullifierHash,
|
||||
fullProof.nullifier,
|
||||
feedback,
|
||||
fullProof.proof
|
||||
)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreContract, "ProofVerified")
|
||||
.withArgs(groupId, fullProof.merkleTreeRoot, fullProof.nullifierHash, groupId, fullProof.signal)
|
||||
.to.emit(semaphoreContract, "ProofValidated")
|
||||
.withArgs(
|
||||
groupId,
|
||||
fullProof.merkleTreeDepth,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.nullifier,
|
||||
fullProof.message,
|
||||
groupId,
|
||||
fullProof.proof
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "semaphoreAddress",
|
||||
"internalType": "contract ISemaphore",
|
||||
"name": "_semaphore",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
@@ -62,7 +62,7 @@
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "feedback",
|
||||
"name": "merkleTreeDepth",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
@@ -72,7 +72,12 @@
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "nullifierHash",
|
||||
"name": "nullifier",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "feedback",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
@@ -87,8 +92,8 @@
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"bytecode": "0x608060405234801561001057600080fd5b506040516106e13803806106e18339818101604052810190610032919061013c565b816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060018190555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16639c1121416001546014306040518463ffffffff1660e01b81526004016100d9939291906101a5565b600060405180830381600087803b1580156100f357600080fd5b505af1158015610107573d6000803e3d6000fd5b505050505050610258565b6000815190506101218161022a565b92915050565b60008151905061013681610241565b92915050565b6000806040838503121561014f57600080fd5b600061015d85828601610112565b925050602061016e85828601610127565b9150509250929050565b610181816101dc565b82525050565b61019081610218565b82525050565b61019f8161020e565b82525050565b60006060820190506101ba6000830186610196565b6101c76020830185610187565b6101d46040830184610178565b949350505050565b60006101e7826101ee565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006102238261020e565b9050919050565b610233816101dc565b811461023e57600080fd5b50565b61024a8161020e565b811461025557600080fd5b50565b61047a806102676000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d253414610051578063a0f44c921461006f578063d18ed1e91461008d578063eed02e4b146100a9575b600080fd5b6100596100c5565b604051610066919061030f565b60405180910390f35b6100776100e9565b604051610084919061032a565b60405180910390f35b6100a760048036038101906100a2919061027c565b6100ef565b005b6100c360048036038101906100be9190610253565b61018e565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633bc778e3600154858786600154876040518763ffffffff1660e01b81526004016101569695949392919061036e565b600060405180830381600087803b15801561017057600080fd5b505af1158015610184573d6000803e3d6000fd5b5050505050505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b81526004016101eb929190610345565b600060405180830381600087803b15801561020557600080fd5b505af1158015610219573d6000803e3d6000fd5b5050505050565b60008190508260206008028201111561023857600080fd5b92915050565b60008135905061024d8161042d565b92915050565b60006020828403121561026557600080fd5b60006102738482850161023e565b91505092915050565b600080600080610160858703121561029357600080fd5b60006102a18782880161023e565b94505060206102b28782880161023e565b93505060406102c38782880161023e565b92505060606102d487828801610220565b91505092959194509250565b6102ed610100838361041e565b5050565b6102fa816103fa565b82525050565b610309816103f0565b82525050565b600060208201905061032460008301846102f1565b92915050565b600060208201905061033f6000830184610300565b92915050565b600060408201905061035a6000830185610300565b6103676020830184610300565b9392505050565b60006101a0820190506103846000830189610300565b6103916020830188610300565b61039e6040830187610300565b6103ab6060830186610300565b6103b86080830185610300565b6103c560a08301846102e0565b979650505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006104058261040c565b9050919050565b6000610417826103d0565b9050919050565b82818337600083830152505050565b610436816103f0565b811461044157600080fd5b5056fea26469706673582212204d8dc3161abc759242364c3a754a86e5eb8653092bcdf1e20bd6fcd368e1997664736f6c63430008040033",
|
||||
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d253414610051578063a0f44c921461006f578063d18ed1e91461008d578063eed02e4b146100a9575b600080fd5b6100596100c5565b604051610066919061030f565b60405180910390f35b6100776100e9565b604051610084919061032a565b60405180910390f35b6100a760048036038101906100a2919061027c565b6100ef565b005b6100c360048036038101906100be9190610253565b61018e565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633bc778e3600154858786600154876040518763ffffffff1660e01b81526004016101569695949392919061036e565b600060405180830381600087803b15801561017057600080fd5b505af1158015610184573d6000803e3d6000fd5b5050505050505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b81526004016101eb929190610345565b600060405180830381600087803b15801561020557600080fd5b505af1158015610219573d6000803e3d6000fd5b5050505050565b60008190508260206008028201111561023857600080fd5b92915050565b60008135905061024d8161042d565b92915050565b60006020828403121561026557600080fd5b60006102738482850161023e565b91505092915050565b600080600080610160858703121561029357600080fd5b60006102a18782880161023e565b94505060206102b28782880161023e565b93505060406102c38782880161023e565b92505060606102d487828801610220565b91505092959194509250565b6102ed610100838361041e565b5050565b6102fa816103fa565b82525050565b610309816103f0565b82525050565b600060208201905061032460008301846102f1565b92915050565b600060208201905061033f6000830184610300565b92915050565b600060408201905061035a6000830185610300565b6103676020830184610300565b9392505050565b60006101a0820190506103846000830189610300565b6103916020830188610300565b61039e6040830187610300565b6103ab6060830186610300565b6103b86080830185610300565b6103c560a08301846102e0565b979650505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006104058261040c565b9050919050565b6000610417826103d0565b9050919050565b82818337600083830152505050565b610436816103f0565b811461044157600080fd5b5056fea26469706673582212204d8dc3161abc759242364c3a754a86e5eb8653092bcdf1e20bd6fcd368e1997664736f6c63430008040033",
|
||||
"bytecode": "0x608060405234801561001057600080fd5b5060405161072b38038061072b833981810160405281019061003291906101ba565b816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060018190555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c96e71fb600154306040518363ffffffff1660e01b81526004016100d6929190610218565b600060405180830381600087803b1580156100f057600080fd5b505af1158015610104573d6000803e3d6000fd5b505050505050610241565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061013f82610114565b9050919050565b600061015182610134565b9050919050565b61016181610146565b811461016c57600080fd5b50565b60008151905061017e81610158565b92915050565b6000819050919050565b61019781610184565b81146101a257600080fd5b50565b6000815190506101b48161018e565b92915050565b600080604083850312156101d1576101d061010f565b5b60006101df8582860161016f565b92505060206101f0858286016101a5565b9150509250929050565b61020381610184565b82525050565b61021281610134565b82525050565b600060408201905061022d60008301856101fa565b61023a6020830184610209565b9392505050565b6104db806102506000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d2534146100515780637b85d27a1461006f578063a0f44c921461008b578063eed02e4b146100a9575b600080fd5b6100596100c5565b60405161006691906102a2565b60405180910390f35b6100896004803603810190610084919061031f565b6100e9565b005b61009361018b565b6040516100a091906103aa565b60405180910390f35b6100c360048036038101906100be91906103c5565b610191565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632694b47660015487878787600154886040518863ffffffff1660e01b8152600401610152979695949392919061040c565b600060405180830381600087803b15801561016c57600080fd5b505af1158015610180573d6000803e3d6000fd5b505050505050505050565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b81526004016101ee92919061047c565b600060405180830381600087803b15801561020857600080fd5b505af115801561021c573d6000803e3d6000fd5b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061026861026361025e84610223565b610243565b610223565b9050919050565b600061027a8261024d565b9050919050565b600061028c8261026f565b9050919050565b61029c81610281565b82525050565b60006020820190506102b76000830184610293565b92915050565b600080fd5b6000819050919050565b6102d5816102c2565b81146102e057600080fd5b50565b6000813590506102f2816102cc565b92915050565b600080fd5b600081905082602060080282011115610319576103186102f8565b5b92915050565b6000806000806000610180868803121561033c5761033b6102bd565b5b600061034a888289016102e3565b955050602061035b888289016102e3565b945050604061036c888289016102e3565b935050606061037d888289016102e3565b925050608061038e888289016102fd565b9150509295509295909350565b6103a4816102c2565b82525050565b60006020820190506103bf600083018461039b565b92915050565b6000602082840312156103db576103da6102bd565b5b60006103e9848285016102e3565b91505092915050565b82818337505050565b61040861010083836103f2565b5050565b60006101c082019050610422600083018a61039b565b61042f602083018961039b565b61043c604083018861039b565b610449606083018761039b565b610456608083018661039b565b61046360a083018561039b565b61047060c08301846103fb565b98975050505050505050565b6000604082019050610491600083018561039b565b61049e602083018461039b565b939250505056fea26469706673582212204e55122b6afd94ae34af34764263b41bcb463e4fc817ea7ab795705071f8e56f64736f6c63430008170033",
|
||||
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d2534146100515780637b85d27a1461006f578063a0f44c921461008b578063eed02e4b146100a9575b600080fd5b6100596100c5565b60405161006691906102a2565b60405180910390f35b6100896004803603810190610084919061031f565b6100e9565b005b61009361018b565b6040516100a091906103aa565b60405180910390f35b6100c360048036038101906100be91906103c5565b610191565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632694b47660015487878787600154886040518863ffffffff1660e01b8152600401610152979695949392919061040c565b600060405180830381600087803b15801561016c57600080fd5b505af1158015610180573d6000803e3d6000fd5b505050505050505050565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b81526004016101ee92919061047c565b600060405180830381600087803b15801561020857600080fd5b505af115801561021c573d6000803e3d6000fd5b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061026861026361025e84610223565b610243565b610223565b9050919050565b600061027a8261024d565b9050919050565b600061028c8261026f565b9050919050565b61029c81610281565b82525050565b60006020820190506102b76000830184610293565b92915050565b600080fd5b6000819050919050565b6102d5816102c2565b81146102e057600080fd5b50565b6000813590506102f2816102cc565b92915050565b600080fd5b600081905082602060080282011115610319576103186102f8565b5b92915050565b6000806000806000610180868803121561033c5761033b6102bd565b5b600061034a888289016102e3565b955050602061035b888289016102e3565b945050604061036c888289016102e3565b935050606061037d888289016102e3565b925050608061038e888289016102fd565b9150509295509295909350565b6103a4816102c2565b82525050565b60006020820190506103bf600083018461039b565b92915050565b6000602082840312156103db576103da6102bd565b5b60006103e9848285016102e3565b91505092915050565b82818337505050565b61040861010083836103f2565b5050565b60006101c082019050610422600083018a61039b565b61042f602083018961039b565b61043c604083018861039b565b610449606083018761039b565b610456608083018661039b565b61046360a083018561039b565b61047060c08301846103fb565b98975050505050505050565b6000604082019050610491600083018561039b565b61049e602083018461039b565b939250505056fea26469706673582212204e55122b6afd94ae34af34764263b41bcb463e4fc817ea7ab795705071f8e56f64736f6c63430008170033",
|
||||
"linkReferences": {},
|
||||
"deployedLinkReferences": {}
|
||||
}
|
||||
|
||||
@@ -30,15 +30,6 @@ const nextConfig = withPWA({
|
||||
SEMAPHORE_CONTRACT_ADDRESS: process.env.SEMAPHORE_CONTRACT_ADDRESS,
|
||||
OPENZEPPELIN_AUTOTASK_WEBHOOK: process.env.OPENZEPPELIN_AUTOTASK_WEBHOOK,
|
||||
GROUP_ID: process.env.GROUP_ID
|
||||
},
|
||||
webpack: (config, { isServer }) => {
|
||||
if (!isServer) {
|
||||
config.resolve.fallback = {
|
||||
fs: false
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/font": "13.0.3",
|
||||
"@semaphore-protocol/data": "3.15.2",
|
||||
"@semaphore-protocol/group": "3.15.2",
|
||||
"@semaphore-protocol/identity": "3.15.2",
|
||||
"@semaphore-protocol/proof": "3.15.2",
|
||||
"@semaphore-protocol/data": "4.0.0-alpha",
|
||||
"@semaphore-protocol/group": "4.0.0-alpha",
|
||||
"@semaphore-protocol/identity": "4.0.0-alpha",
|
||||
"@semaphore-protocol/proof": "4.0.0-alpha",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "18.0.8",
|
||||
"dotenv": "^16.0.3",
|
||||
"ethers": "^5.7.2",
|
||||
"ethers": "^6.4.0",
|
||||
"next": "13.0.3",
|
||||
"next-pwa": "^5.6.0",
|
||||
"react": "18.2.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SemaphoreEthers } from "@semaphore-protocol/data"
|
||||
import { BigNumber, utils } from "ethers"
|
||||
import { decodeBytes32String, toBeHex } from "ethers"
|
||||
import getNextConfig from "next/config"
|
||||
import { useCallback, useState } from "react"
|
||||
import { SemaphoreContextType } from "../context/SemaphoreContext"
|
||||
@@ -19,7 +19,7 @@ export default function useSemaphore(): SemaphoreContextType {
|
||||
|
||||
const members = await semaphore.getGroupMembers(env.GROUP_ID)
|
||||
|
||||
setUsers(members)
|
||||
setUsers(members.map((member) => member.toString()))
|
||||
}, [])
|
||||
|
||||
const addUser = useCallback(
|
||||
@@ -34,9 +34,9 @@ export default function useSemaphore(): SemaphoreContextType {
|
||||
address: env.SEMAPHORE_CONTRACT_ADDRESS
|
||||
})
|
||||
|
||||
const proofs = await semaphore.getGroupVerifiedProofs(env.GROUP_ID)
|
||||
const proofs = await semaphore.getGroupValidatedProofs(env.GROUP_ID)
|
||||
|
||||
setFeedback(proofs.map(({ signal }: any) => utils.parseBytes32String(BigNumber.from(signal).toHexString())))
|
||||
setFeedback(proofs.map(({ message }: any) => decodeBytes32String(toBeHex(message, 32))))
|
||||
}, [])
|
||||
|
||||
const addFeedback = useCallback(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Contract, providers, Wallet } from "ethers"
|
||||
import { Contract, InfuraProvider, JsonRpcProvider, Wallet } from "ethers"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
import Feedback from "../../../contract-artifacts/Feedback.json"
|
||||
|
||||
@@ -25,17 +25,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const contractAddress = process.env.FEEDBACK_CONTRACT_ADDRESS
|
||||
|
||||
const provider =
|
||||
ethereumNetwork === "localhost"
|
||||
? new providers.JsonRpcProvider()
|
||||
: new providers.InfuraProvider(ethereumNetwork, infuraApiKey)
|
||||
ethereumNetwork === "localhost" ? new JsonRpcProvider() : new InfuraProvider(ethereumNetwork, infuraApiKey)
|
||||
|
||||
const signer = new Wallet(ethereumPrivateKey, provider)
|
||||
const contract = new Contract(contractAddress, Feedback.abi, signer)
|
||||
|
||||
const { feedback, merkleTreeRoot, nullifierHash, proof } = req.body
|
||||
const { feedback, merkleTreeDepth, merkleTreeRoot, nullifier, proof } = req.body
|
||||
|
||||
try {
|
||||
const transaction = await contract.sendFeedback(feedback, merkleTreeRoot, nullifierHash, proof)
|
||||
const transaction = await contract.sendFeedback(merkleTreeDepth, merkleTreeRoot, nullifier, feedback, proof)
|
||||
|
||||
await transaction.wait()
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Contract, providers, Wallet } from "ethers"
|
||||
import { Contract, InfuraProvider, JsonRpcProvider, Wallet } from "ethers"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
import Feedback from "../../../contract-artifacts/Feedback.json"
|
||||
|
||||
@@ -25,9 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const contractAddress = process.env.FEEDBACK_CONTRACT_ADDRESS
|
||||
|
||||
const provider =
|
||||
ethereumNetwork === "localhost"
|
||||
? new providers.JsonRpcProvider()
|
||||
: new providers.InfuraProvider(ethereumNetwork, infuraApiKey)
|
||||
ethereumNetwork === "localhost" ? new JsonRpcProvider() : new InfuraProvider(ethereumNetwork, infuraApiKey)
|
||||
|
||||
const signer = new Wallet(ethereumPrivateKey, provider)
|
||||
const contract = new Contract(contractAddress, Feedback.abi, signer)
|
||||
|
||||
@@ -17,14 +17,14 @@ export default function GroupsPage() {
|
||||
const [_identity, setIdentity] = useState<Identity>()
|
||||
|
||||
useEffect(() => {
|
||||
const identityString = localStorage.getItem("identity")
|
||||
const privateKey = localStorage.getItem("identity")
|
||||
|
||||
if (!identityString) {
|
||||
if (!privateKey) {
|
||||
router.push("/")
|
||||
return
|
||||
}
|
||||
|
||||
setIdentity(new Identity(identityString))
|
||||
setIdentity(new Identity(privateKey))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -75,7 +75,7 @@ export default function GroupsPage() {
|
||||
setLoading(false)
|
||||
}, [_identity])
|
||||
|
||||
const userHasJoined = useCallback((identity: Identity) => _users.includes(identity.commitment.toString()), [_users])
|
||||
const userHasJoined = useCallback((identity: Identity) => _users.includes(identity.commitment), [_users])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -10,10 +10,10 @@ export default function IdentitiesPage() {
|
||||
const [_identity, setIdentity] = useState<Identity>()
|
||||
|
||||
useEffect(() => {
|
||||
const identityString = localStorage.getItem("identity")
|
||||
const privateKey = localStorage.getItem("identity")
|
||||
|
||||
if (identityString) {
|
||||
const identity = new Identity(identityString)
|
||||
if (privateKey) {
|
||||
const identity = new Identity(privateKey)
|
||||
|
||||
setIdentity(identity)
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function IdentitiesPage() {
|
||||
|
||||
setIdentity(identity)
|
||||
|
||||
localStorage.setItem("identity", identity.toString())
|
||||
localStorage.setItem("identity", identity.privateKey.toString())
|
||||
|
||||
setLogs("Your new Semaphore identity was just created 🎉")
|
||||
}, [])
|
||||
@@ -68,8 +68,7 @@ export default function IdentitiesPage() {
|
||||
{_identity ? (
|
||||
<div>
|
||||
<div className="box">
|
||||
<p className="box-text">Trapdoor: {_identity.trapdoor.toString()}</p>
|
||||
<p className="box-text">Nullifier: {_identity.nullifier.toString()}</p>
|
||||
<p className="box-text">Private Key: {_identity.privateKey.toString()}</p>
|
||||
<p className="box-text">Commitment: {_identity.commitment.toString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { generateProof } from "@semaphore-protocol/proof"
|
||||
import { BigNumber, utils } from "ethers"
|
||||
import { encodeBytes32String } from "ethers"
|
||||
import getNextConfig from "next/config"
|
||||
import { useRouter } from "next/router"
|
||||
import { useCallback, useContext, useEffect, useState } from "react"
|
||||
@@ -20,14 +20,14 @@ export default function ProofsPage() {
|
||||
const [_identity, setIdentity] = useState<Identity>()
|
||||
|
||||
useEffect(() => {
|
||||
const identityString = localStorage.getItem("identity")
|
||||
const privateKey = localStorage.getItem("identity")
|
||||
|
||||
if (!identityString) {
|
||||
if (!privateKey) {
|
||||
router.push("/")
|
||||
return
|
||||
}
|
||||
|
||||
setIdentity(new Identity(identityString))
|
||||
setIdentity(new Identity(privateKey))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -51,15 +51,15 @@ export default function ProofsPage() {
|
||||
try {
|
||||
const group = new Group(env.GROUP_ID)
|
||||
|
||||
const signal = BigNumber.from(utils.formatBytes32String(feedback)).toString()
|
||||
const message = encodeBytes32String(feedback)
|
||||
|
||||
group.addMembers(_users)
|
||||
|
||||
const { proof, merkleTreeRoot, nullifierHash } = await generateProof(
|
||||
const { proof, merkleTreeDepth, merkleTreeRoot, nullifier } = await generateProof(
|
||||
_identity,
|
||||
group,
|
||||
env.GROUP_ID,
|
||||
signal
|
||||
message,
|
||||
env.GROUP_ID
|
||||
)
|
||||
|
||||
let response: any
|
||||
@@ -72,7 +72,7 @@ export default function ProofsPage() {
|
||||
abi: Feedback.abi,
|
||||
address: env.FEEDBACK_CONTRACT_ADDRESS,
|
||||
functionName: "sendFeedback",
|
||||
functionParameters: [signal, merkleTreeRoot, nullifierHash, proof]
|
||||
functionParameters: [merkleTreeDepth, merkleTreeRoot, nullifier, message, proof]
|
||||
})
|
||||
})
|
||||
} else {
|
||||
@@ -80,9 +80,10 @@ export default function ProofsPage() {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
feedback: signal,
|
||||
feedback: message,
|
||||
merkleTreeDepth,
|
||||
merkleTreeRoot,
|
||||
nullifierHash,
|
||||
nullifier,
|
||||
proof
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/cli-template-monorepo-ethers",
|
||||
"version": "3.15.2",
|
||||
"version": "4.0.0-alpha",
|
||||
"description": "Semaphore Hardhat + Next.js + SemaphoreEthers template.",
|
||||
"license": "Unlicense",
|
||||
"files": [
|
||||
@@ -22,14 +22,13 @@
|
||||
"lint": "eslint . --ext .js,.ts",
|
||||
"prettier": "prettier -c .",
|
||||
"prettier:write": "prettier -w .",
|
||||
"copy:contract-artifacts": "ts-node scripts/copy-contract-artifacts.ts",
|
||||
"prepublish": "tar -czf files.tgz .gitignore .yarn .yarnrc.yml apps"
|
||||
},
|
||||
"workspaces": [
|
||||
"apps/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.9",
|
||||
"@types/node": "^20",
|
||||
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
||||
"@typescript-eslint/parser": "^5.9.1",
|
||||
"eslint": "^8.2.0",
|
||||
@@ -40,5 +39,6 @@
|
||||
"prettier": "^2.5.1",
|
||||
"ts-node": "^10.8.1",
|
||||
"typescript": "^4.7.3"
|
||||
}
|
||||
},
|
||||
"stableVersion": "3.15.2"
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import * as fs from "fs"
|
||||
|
||||
async function main() {
|
||||
const contractArtifactsPath = "apps/contracts/build/contracts/contracts/Feedback.sol"
|
||||
const webAppArtifactsPath = "apps/web-app/contract-artifacts"
|
||||
|
||||
await fs.promises.copyFile(`${contractArtifactsPath}/Feedback.json`, `${webAppArtifactsPath}/Feedback.json`)
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -1,8 +1,8 @@
|
||||
DEFAULT_NETWORK=localhost
|
||||
INFURA_API_KEY=
|
||||
ETHEREUM_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
|
||||
FEEDBACK_CONTRACT_ADDRESS=0x5fc8d32690cc91d4c39d9d3abcbd16989f875707
|
||||
SEMAPHORE_CONTRACT_ADDRESS=0xdc64a140aa3e981100a9beca4e685f962f0cf6c9
|
||||
FEEDBACK_CONTRACT_ADDRESS=0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9
|
||||
SEMAPHORE_CONTRACT_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
|
||||
OPENZEPPELIN_AUTOTASK_WEBHOOK=
|
||||
GROUP_ID=42
|
||||
REPORT_GAS=false
|
||||
|
||||
@@ -52,7 +52,11 @@ node_modules/
|
||||
# Generate output
|
||||
dist
|
||||
build
|
||||
|
||||
# Hardhat files
|
||||
artifacts
|
||||
cache
|
||||
typechain-types
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
|
||||
@@ -25,7 +25,7 @@ yarn dev
|
||||
1. Go to the `apps/contracts` directory and deploy your contract:
|
||||
|
||||
```bash
|
||||
yarn deploy --semaphore <semaphore-address> --group <group-id> --network arbitrum-goerli
|
||||
yarn deploy --semaphore <semaphore-address> --group <group-id> --network arbitrum-sepolia
|
||||
```
|
||||
|
||||
2. Update your `.env` file with your new contract address, the group id and the semaphore contract address.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
pragma solidity ^0.8.23;
|
||||
|
||||
import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
|
||||
|
||||
@@ -8,11 +8,11 @@ contract Feedback {
|
||||
|
||||
uint256 public groupId;
|
||||
|
||||
constructor(address semaphoreAddress, uint256 _groupId) {
|
||||
semaphore = ISemaphore(semaphoreAddress);
|
||||
constructor(ISemaphore _semaphore, uint256 _groupId) {
|
||||
semaphore = _semaphore;
|
||||
groupId = _groupId;
|
||||
|
||||
semaphore.createGroup(groupId, 20, address(this));
|
||||
semaphore.createGroup(groupId, address(this));
|
||||
}
|
||||
|
||||
function joinGroup(uint256 identityCommitment) external {
|
||||
@@ -20,11 +20,12 @@ contract Feedback {
|
||||
}
|
||||
|
||||
function sendFeedback(
|
||||
uint256 feedback,
|
||||
uint256 merkleTreeDepth,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 nullifierHash,
|
||||
uint256 nullifier,
|
||||
uint256 feedback,
|
||||
uint256[8] calldata proof
|
||||
) external {
|
||||
semaphore.verifyProof(groupId, merkleTreeRoot, feedback, nullifierHash, groupId, proof);
|
||||
semaphore.validateProof(groupId, merkleTreeDepth, merkleTreeRoot, nullifier, feedback, groupId, proof);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import "@nomicfoundation/hardhat-chai-matchers"
|
||||
import "@nomiclabs/hardhat-ethers"
|
||||
import "@nomiclabs/hardhat-etherscan"
|
||||
import "@nomicfoundation/hardhat-ethers"
|
||||
import "@nomicfoundation/hardhat-verify"
|
||||
import "@semaphore-protocol/hardhat"
|
||||
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/deploy"
|
||||
|
||||
dotenvConfig({ path: resolve(__dirname, "../../.env") })
|
||||
dotenvConfig()
|
||||
|
||||
function getNetworks(): NetworksUserConfig {
|
||||
if (!process.env.INFURA_API_KEY || !process.env.ETHEREUM_PRIVATE_KEY) {
|
||||
@@ -23,11 +21,6 @@ function getNetworks(): NetworksUserConfig {
|
||||
const infuraApiKey = process.env.INFURA_API_KEY
|
||||
|
||||
return {
|
||||
goerli: {
|
||||
url: `https://goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 5,
|
||||
accounts
|
||||
},
|
||||
sepolia: {
|
||||
url: `https://sepolia.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 11155111,
|
||||
@@ -38,14 +31,14 @@ function getNetworks(): NetworksUserConfig {
|
||||
chainId: 80001,
|
||||
accounts
|
||||
},
|
||||
"optimism-goerli": {
|
||||
url: `https://optimism-goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 420,
|
||||
"optimism-sepolia": {
|
||||
url: `https://optimism-sepolia.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 11155420,
|
||||
accounts
|
||||
},
|
||||
"arbitrum-goerli": {
|
||||
url: "https://goerli-rollup.arbitrum.io/rpc",
|
||||
chainId: 421613,
|
||||
"arbitrum-sepolia": {
|
||||
url: "https://sepolia-rollup.arbitrum.io/rpc",
|
||||
chainId: 421614,
|
||||
accounts
|
||||
},
|
||||
arbitrum: {
|
||||
@@ -57,13 +50,7 @@ function getNetworks(): NetworksUserConfig {
|
||||
}
|
||||
|
||||
const hardhatConfig: HardhatUserConfig = {
|
||||
solidity: config.solidity,
|
||||
paths: {
|
||||
sources: config.paths.contracts,
|
||||
tests: config.paths.tests,
|
||||
cache: config.paths.cache,
|
||||
artifacts: config.paths.build.contracts
|
||||
},
|
||||
solidity: "0.8.23",
|
||||
networks: {
|
||||
hardhat: {
|
||||
chainId: 1337
|
||||
@@ -76,11 +63,13 @@ const hardhatConfig: HardhatUserConfig = {
|
||||
coinmarketcap: process.env.COINMARKETCAP_API_KEY
|
||||
},
|
||||
typechain: {
|
||||
outDir: config.paths.build.typechain,
|
||||
target: "ethers-v5"
|
||||
target: "ethers-v6"
|
||||
},
|
||||
etherscan: {
|
||||
apiKey: process.env.ETHERSCAN_API_KEY
|
||||
},
|
||||
sourcify: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,55 +6,39 @@
|
||||
"scripts": {
|
||||
"dev": "hardhat node & yarn compile && yarn deploy --network localhost",
|
||||
"compile": "hardhat compile",
|
||||
"download:snark-artifacts": "hardhat run scripts/download-snark-artifacts.ts",
|
||||
"deploy": "yarn compile && hardhat deploy",
|
||||
"test": "hardhat run scripts/download-snark-artifacts.ts && hardhat test",
|
||||
"test": "hardhat test",
|
||||
"test:report-gas": "REPORT_GAS=true hardhat test",
|
||||
"test:coverage": "hardhat coverage",
|
||||
"typechain": "hardhat typechain",
|
||||
"lint": "solhint 'contracts/**/*.sol'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^1.0.5",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.0",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.1.7",
|
||||
"@semaphore-protocol/group": "3.15.2",
|
||||
"@semaphore-protocol/hardhat": "3.15.2",
|
||||
"@semaphore-protocol/identity": "3.15.2",
|
||||
"@semaphore-protocol/proof": "3.15.2",
|
||||
"@typechain/ethers-v5": "^10.0.0",
|
||||
"@typechain/hardhat": "^6.0.0",
|
||||
"@types/chai": "^4.3.1",
|
||||
"@types/download": "^8.0.1",
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^2.0.3",
|
||||
"@nomicfoundation/hardhat-ethers": "^3.0.0",
|
||||
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
|
||||
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
|
||||
"@nomicfoundation/hardhat-verify": "^2.0.0",
|
||||
"@semaphore-protocol/group": "4.0.0-alpha",
|
||||
"@semaphore-protocol/hardhat": "4.0.0-alpha",
|
||||
"@semaphore-protocol/identity": "4.0.0-alpha",
|
||||
"@semaphore-protocol/proof": "4.0.0-alpha",
|
||||
"@typechain/ethers-v6": "^0.5.0",
|
||||
"@typechain/hardhat": "^9.0.0",
|
||||
"@types/chai": "^4.2.0",
|
||||
"@types/mocha": "^9.1.1",
|
||||
"chai": "^4.2.0",
|
||||
"dotenv": "^14.3.2",
|
||||
"download": "^8.0.0",
|
||||
"ethers": "^5.0.0",
|
||||
"hardhat": "^2.8.4",
|
||||
"ethers": "^6.4.0",
|
||||
"hardhat": "^2.19.4",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
"prettier-plugin-solidity": "^1.0.0-beta.19",
|
||||
"prettier-plugin-solidity": "^1.3.1",
|
||||
"solhint": "^3.3.6",
|
||||
"solhint-plugin-prettier": "^0.0.5",
|
||||
"solidity-coverage": "^0.7.21",
|
||||
"typechain": "^8.0.0"
|
||||
"solidity-coverage": "^0.8.0",
|
||||
"typechain": "^8.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@semaphore-protocol/contracts": "3.15.2"
|
||||
},
|
||||
"config": {
|
||||
"solidity": {
|
||||
"version": "0.8.4"
|
||||
},
|
||||
"paths": {
|
||||
"contracts": "./contracts",
|
||||
"tests": "./test",
|
||||
"cache": "./cache",
|
||||
"build": {
|
||||
"snark-artifacts": "./build/snark-artifacts",
|
||||
"contracts": "./build/contracts",
|
||||
"typechain": "./build/typechain"
|
||||
}
|
||||
}
|
||||
"@semaphore-protocol/contracts": "4.0.0-alpha"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
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/${20}`
|
||||
|
||||
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)
|
||||
})
|
||||
@@ -10,7 +10,7 @@ task("deploy", "Deploy a Feedback contract")
|
||||
logs
|
||||
})
|
||||
|
||||
semaphoreAddress = semaphore.address
|
||||
semaphoreAddress = await semaphore.getAddress()
|
||||
}
|
||||
|
||||
if (!groupId) {
|
||||
@@ -21,10 +21,8 @@ task("deploy", "Deploy a Feedback contract")
|
||||
|
||||
const feedbackContract = await FeedbackFactory.deploy(semaphoreAddress, groupId)
|
||||
|
||||
await feedbackContract.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Feedback contract has been deployed to: ${feedbackContract.address}`)
|
||||
console.info(`Feedback contract has been deployed to: ${await feedbackContract.getAddress()}`)
|
||||
}
|
||||
|
||||
return feedbackContract
|
||||
|
||||
@@ -2,18 +2,17 @@ import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { generateProof } from "@semaphore-protocol/proof"
|
||||
import { expect } from "chai"
|
||||
import { formatBytes32String } from "ethers/lib/utils"
|
||||
import { encodeBytes32String } from "ethers"
|
||||
import { run } from "hardhat"
|
||||
// @ts-ignore: typechain folder will be generated after contracts compilation
|
||||
import { Feedback } from "../build/typechain"
|
||||
import { config } from "../package.json"
|
||||
|
||||
describe("Feedback", () => {
|
||||
let feedbackContract: Feedback
|
||||
let semaphoreContract: string
|
||||
|
||||
const groupId = "42"
|
||||
const group = new Group(groupId)
|
||||
const group = new Group()
|
||||
const users: Identity[] = []
|
||||
|
||||
before(async () => {
|
||||
@@ -21,11 +20,7 @@ describe("Feedback", () => {
|
||||
logs: false
|
||||
})
|
||||
|
||||
feedbackContract = await run("deploy", {
|
||||
logs: false,
|
||||
group: groupId,
|
||||
semaphore: semaphore.address
|
||||
})
|
||||
feedbackContract = await run("deploy", { logs: false, group: groupId, semaphore: await semaphore.getAddress() })
|
||||
semaphoreContract = semaphore
|
||||
|
||||
users.push(new Identity())
|
||||
@@ -47,27 +42,30 @@ describe("Feedback", () => {
|
||||
})
|
||||
|
||||
describe("# sendFeedback", () => {
|
||||
const wasmFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.wasm`
|
||||
const zkeyFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.zkey`
|
||||
|
||||
it("Should allow users to send feedback anonymously", async () => {
|
||||
const feedback = formatBytes32String("Hello World")
|
||||
const feedback = encodeBytes32String("Hello World")
|
||||
|
||||
const fullProof = await generateProof(users[1], group, groupId, feedback, {
|
||||
wasmFilePath,
|
||||
zkeyFilePath
|
||||
})
|
||||
const fullProof = await generateProof(users[1], group, feedback, groupId)
|
||||
|
||||
const transaction = feedbackContract.sendFeedback(
|
||||
feedback,
|
||||
fullProof.merkleTreeDepth,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.nullifierHash,
|
||||
fullProof.nullifier,
|
||||
feedback,
|
||||
fullProof.proof
|
||||
)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreContract, "ProofVerified")
|
||||
.withArgs(groupId, fullProof.merkleTreeRoot, fullProof.nullifierHash, groupId, fullProof.signal)
|
||||
.to.emit(semaphoreContract, "ProofValidated")
|
||||
.withArgs(
|
||||
groupId,
|
||||
fullProof.merkleTreeDepth,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.nullifier,
|
||||
fullProof.message,
|
||||
groupId,
|
||||
fullProof.proof
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "semaphoreAddress",
|
||||
"internalType": "contract ISemaphore",
|
||||
"name": "_semaphore",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
@@ -62,7 +62,7 @@
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "feedback",
|
||||
"name": "merkleTreeDepth",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
@@ -72,7 +72,12 @@
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "nullifierHash",
|
||||
"name": "nullifier",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "feedback",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
@@ -87,8 +92,8 @@
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"bytecode": "0x608060405234801561001057600080fd5b506040516106e13803806106e18339818101604052810190610032919061013c565b816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060018190555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16639c1121416001546014306040518463ffffffff1660e01b81526004016100d9939291906101a5565b600060405180830381600087803b1580156100f357600080fd5b505af1158015610107573d6000803e3d6000fd5b505050505050610258565b6000815190506101218161022a565b92915050565b60008151905061013681610241565b92915050565b6000806040838503121561014f57600080fd5b600061015d85828601610112565b925050602061016e85828601610127565b9150509250929050565b610181816101dc565b82525050565b61019081610218565b82525050565b61019f8161020e565b82525050565b60006060820190506101ba6000830186610196565b6101c76020830185610187565b6101d46040830184610178565b949350505050565b60006101e7826101ee565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006102238261020e565b9050919050565b610233816101dc565b811461023e57600080fd5b50565b61024a8161020e565b811461025557600080fd5b50565b61047a806102676000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d253414610051578063a0f44c921461006f578063d18ed1e91461008d578063eed02e4b146100a9575b600080fd5b6100596100c5565b604051610066919061030f565b60405180910390f35b6100776100e9565b604051610084919061032a565b60405180910390f35b6100a760048036038101906100a2919061027c565b6100ef565b005b6100c360048036038101906100be9190610253565b61018e565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633bc778e3600154858786600154876040518763ffffffff1660e01b81526004016101569695949392919061036e565b600060405180830381600087803b15801561017057600080fd5b505af1158015610184573d6000803e3d6000fd5b5050505050505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b81526004016101eb929190610345565b600060405180830381600087803b15801561020557600080fd5b505af1158015610219573d6000803e3d6000fd5b5050505050565b60008190508260206008028201111561023857600080fd5b92915050565b60008135905061024d8161042d565b92915050565b60006020828403121561026557600080fd5b60006102738482850161023e565b91505092915050565b600080600080610160858703121561029357600080fd5b60006102a18782880161023e565b94505060206102b28782880161023e565b93505060406102c38782880161023e565b92505060606102d487828801610220565b91505092959194509250565b6102ed610100838361041e565b5050565b6102fa816103fa565b82525050565b610309816103f0565b82525050565b600060208201905061032460008301846102f1565b92915050565b600060208201905061033f6000830184610300565b92915050565b600060408201905061035a6000830185610300565b6103676020830184610300565b9392505050565b60006101a0820190506103846000830189610300565b6103916020830188610300565b61039e6040830187610300565b6103ab6060830186610300565b6103b86080830185610300565b6103c560a08301846102e0565b979650505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006104058261040c565b9050919050565b6000610417826103d0565b9050919050565b82818337600083830152505050565b610436816103f0565b811461044157600080fd5b5056fea26469706673582212204d8dc3161abc759242364c3a754a86e5eb8653092bcdf1e20bd6fcd368e1997664736f6c63430008040033",
|
||||
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d253414610051578063a0f44c921461006f578063d18ed1e91461008d578063eed02e4b146100a9575b600080fd5b6100596100c5565b604051610066919061030f565b60405180910390f35b6100776100e9565b604051610084919061032a565b60405180910390f35b6100a760048036038101906100a2919061027c565b6100ef565b005b6100c360048036038101906100be9190610253565b61018e565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633bc778e3600154858786600154876040518763ffffffff1660e01b81526004016101569695949392919061036e565b600060405180830381600087803b15801561017057600080fd5b505af1158015610184573d6000803e3d6000fd5b5050505050505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b81526004016101eb929190610345565b600060405180830381600087803b15801561020557600080fd5b505af1158015610219573d6000803e3d6000fd5b5050505050565b60008190508260206008028201111561023857600080fd5b92915050565b60008135905061024d8161042d565b92915050565b60006020828403121561026557600080fd5b60006102738482850161023e565b91505092915050565b600080600080610160858703121561029357600080fd5b60006102a18782880161023e565b94505060206102b28782880161023e565b93505060406102c38782880161023e565b92505060606102d487828801610220565b91505092959194509250565b6102ed610100838361041e565b5050565b6102fa816103fa565b82525050565b610309816103f0565b82525050565b600060208201905061032460008301846102f1565b92915050565b600060208201905061033f6000830184610300565b92915050565b600060408201905061035a6000830185610300565b6103676020830184610300565b9392505050565b60006101a0820190506103846000830189610300565b6103916020830188610300565b61039e6040830187610300565b6103ab6060830186610300565b6103b86080830185610300565b6103c560a08301846102e0565b979650505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006104058261040c565b9050919050565b6000610417826103d0565b9050919050565b82818337600083830152505050565b610436816103f0565b811461044157600080fd5b5056fea26469706673582212204d8dc3161abc759242364c3a754a86e5eb8653092bcdf1e20bd6fcd368e1997664736f6c63430008040033",
|
||||
"bytecode": "0x608060405234801561001057600080fd5b5060405161072b38038061072b833981810160405281019061003291906101ba565b816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060018190555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c96e71fb600154306040518363ffffffff1660e01b81526004016100d6929190610218565b600060405180830381600087803b1580156100f057600080fd5b505af1158015610104573d6000803e3d6000fd5b505050505050610241565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061013f82610114565b9050919050565b600061015182610134565b9050919050565b61016181610146565b811461016c57600080fd5b50565b60008151905061017e81610158565b92915050565b6000819050919050565b61019781610184565b81146101a257600080fd5b50565b6000815190506101b48161018e565b92915050565b600080604083850312156101d1576101d061010f565b5b60006101df8582860161016f565b92505060206101f0858286016101a5565b9150509250929050565b61020381610184565b82525050565b61021281610134565b82525050565b600060408201905061022d60008301856101fa565b61023a6020830184610209565b9392505050565b6104db806102506000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d2534146100515780637b85d27a1461006f578063a0f44c921461008b578063eed02e4b146100a9575b600080fd5b6100596100c5565b60405161006691906102a2565b60405180910390f35b6100896004803603810190610084919061031f565b6100e9565b005b61009361018b565b6040516100a091906103aa565b60405180910390f35b6100c360048036038101906100be91906103c5565b610191565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632694b47660015487878787600154886040518863ffffffff1660e01b8152600401610152979695949392919061040c565b600060405180830381600087803b15801561016c57600080fd5b505af1158015610180573d6000803e3d6000fd5b505050505050505050565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b81526004016101ee92919061047c565b600060405180830381600087803b15801561020857600080fd5b505af115801561021c573d6000803e3d6000fd5b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061026861026361025e84610223565b610243565b610223565b9050919050565b600061027a8261024d565b9050919050565b600061028c8261026f565b9050919050565b61029c81610281565b82525050565b60006020820190506102b76000830184610293565b92915050565b600080fd5b6000819050919050565b6102d5816102c2565b81146102e057600080fd5b50565b6000813590506102f2816102cc565b92915050565b600080fd5b600081905082602060080282011115610319576103186102f8565b5b92915050565b6000806000806000610180868803121561033c5761033b6102bd565b5b600061034a888289016102e3565b955050602061035b888289016102e3565b945050604061036c888289016102e3565b935050606061037d888289016102e3565b925050608061038e888289016102fd565b9150509295509295909350565b6103a4816102c2565b82525050565b60006020820190506103bf600083018461039b565b92915050565b6000602082840312156103db576103da6102bd565b5b60006103e9848285016102e3565b91505092915050565b82818337505050565b61040861010083836103f2565b5050565b60006101c082019050610422600083018a61039b565b61042f602083018961039b565b61043c604083018861039b565b610449606083018761039b565b610456608083018661039b565b61046360a083018561039b565b61047060c08301846103fb565b98975050505050505050565b6000604082019050610491600083018561039b565b61049e602083018461039b565b939250505056fea26469706673582212204e55122b6afd94ae34af34764263b41bcb463e4fc817ea7ab795705071f8e56f64736f6c63430008170033",
|
||||
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d2534146100515780637b85d27a1461006f578063a0f44c921461008b578063eed02e4b146100a9575b600080fd5b6100596100c5565b60405161006691906102a2565b60405180910390f35b6100896004803603810190610084919061031f565b6100e9565b005b61009361018b565b6040516100a091906103aa565b60405180910390f35b6100c360048036038101906100be91906103c5565b610191565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632694b47660015487878787600154886040518863ffffffff1660e01b8152600401610152979695949392919061040c565b600060405180830381600087803b15801561016c57600080fd5b505af1158015610180573d6000803e3d6000fd5b505050505050505050565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b81526004016101ee92919061047c565b600060405180830381600087803b15801561020857600080fd5b505af115801561021c573d6000803e3d6000fd5b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061026861026361025e84610223565b610243565b610223565b9050919050565b600061027a8261024d565b9050919050565b600061028c8261026f565b9050919050565b61029c81610281565b82525050565b60006020820190506102b76000830184610293565b92915050565b600080fd5b6000819050919050565b6102d5816102c2565b81146102e057600080fd5b50565b6000813590506102f2816102cc565b92915050565b600080fd5b600081905082602060080282011115610319576103186102f8565b5b92915050565b6000806000806000610180868803121561033c5761033b6102bd565b5b600061034a888289016102e3565b955050602061035b888289016102e3565b945050604061036c888289016102e3565b935050606061037d888289016102e3565b925050608061038e888289016102fd565b9150509295509295909350565b6103a4816102c2565b82525050565b60006020820190506103bf600083018461039b565b92915050565b6000602082840312156103db576103da6102bd565b5b60006103e9848285016102e3565b91505092915050565b82818337505050565b61040861010083836103f2565b5050565b60006101c082019050610422600083018a61039b565b61042f602083018961039b565b61043c604083018861039b565b610449606083018761039b565b610456608083018661039b565b61046360a083018561039b565b61047060c08301846103fb565b98975050505050505050565b6000604082019050610491600083018561039b565b61049e602083018461039b565b939250505056fea26469706673582212204e55122b6afd94ae34af34764263b41bcb463e4fc817ea7ab795705071f8e56f64736f6c63430008170033",
|
||||
"linkReferences": {},
|
||||
"deployedLinkReferences": {}
|
||||
}
|
||||
|
||||
@@ -30,15 +30,6 @@ const nextConfig = withPWA({
|
||||
SEMAPHORE_CONTRACT_ADDRESS: process.env.SEMAPHORE_CONTRACT_ADDRESS,
|
||||
OPENZEPPELIN_AUTOTASK_WEBHOOK: process.env.OPENZEPPELIN_AUTOTASK_WEBHOOK,
|
||||
GROUP_ID: process.env.GROUP_ID
|
||||
},
|
||||
webpack: (config, { isServer }) => {
|
||||
if (!isServer) {
|
||||
config.resolve.fallback = {
|
||||
fs: false
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/font": "13.0.3",
|
||||
"@semaphore-protocol/data": "3.15.2",
|
||||
"@semaphore-protocol/group": "3.15.2",
|
||||
"@semaphore-protocol/identity": "3.15.2",
|
||||
"@semaphore-protocol/proof": "3.15.2",
|
||||
"@semaphore-protocol/data": "4.0.0-alpha",
|
||||
"@semaphore-protocol/group": "4.0.0-alpha",
|
||||
"@semaphore-protocol/identity": "4.0.0-alpha",
|
||||
"@semaphore-protocol/proof": "4.0.0-alpha",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "18.0.8",
|
||||
"dotenv": "^16.0.3",
|
||||
"ethers": "^5.7.2",
|
||||
"ethers": "^6.4.0",
|
||||
"next": "13.0.3",
|
||||
"next-pwa": "^5.6.0",
|
||||
"react": "18.2.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SemaphoreSubgraph } from "@semaphore-protocol/data"
|
||||
import { BigNumber, utils } from "ethers"
|
||||
import { decodeBytes32String, toBeHex } from "ethers"
|
||||
import getNextConfig from "next/config"
|
||||
import { useCallback, useState } from "react"
|
||||
import { SemaphoreContextType } from "../context/SemaphoreContext"
|
||||
@@ -31,14 +31,10 @@ export default function useSemaphore(): SemaphoreContextType {
|
||||
const semaphore = new SemaphoreSubgraph(ethereumNetwork)
|
||||
|
||||
const group = await semaphore.getGroup(env.GROUP_ID, {
|
||||
verifiedProofs: true
|
||||
validatedProofs: true
|
||||
})
|
||||
|
||||
setFeedback(
|
||||
group.verifiedProofs!.map(({ signal }: any) =>
|
||||
utils.parseBytes32String(BigNumber.from(signal).toHexString())
|
||||
)
|
||||
)
|
||||
setFeedback(group.validatedProofs!.map(({ message }: any) => decodeBytes32String(toBeHex(message, 32))))
|
||||
}, [])
|
||||
|
||||
const addFeedback = useCallback(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Contract, providers, Wallet } from "ethers"
|
||||
import { Contract, InfuraProvider, JsonRpcProvider, Wallet } from "ethers"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
import Feedback from "../../../contract-artifacts/Feedback.json"
|
||||
|
||||
@@ -25,17 +25,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const contractAddress = process.env.FEEDBACK_CONTRACT_ADDRESS
|
||||
|
||||
const provider =
|
||||
ethereumNetwork === "localhost"
|
||||
? new providers.JsonRpcProvider()
|
||||
: new providers.InfuraProvider(ethereumNetwork, infuraApiKey)
|
||||
ethereumNetwork === "localhost" ? new JsonRpcProvider() : new InfuraProvider(ethereumNetwork, infuraApiKey)
|
||||
|
||||
const signer = new Wallet(ethereumPrivateKey, provider)
|
||||
const contract = new Contract(contractAddress, Feedback.abi, signer)
|
||||
|
||||
const { feedback, merkleTreeRoot, nullifierHash, proof } = req.body
|
||||
const { feedback, merkleTreeDepth, merkleTreeRoot, nullifier, proof } = req.body
|
||||
|
||||
try {
|
||||
const transaction = await contract.sendFeedback(feedback, merkleTreeRoot, nullifierHash, proof)
|
||||
const transaction = await contract.sendFeedback(merkleTreeDepth, merkleTreeRoot, nullifier, feedback, proof)
|
||||
|
||||
await transaction.wait()
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Contract, providers, Wallet } from "ethers"
|
||||
import { Contract, InfuraProvider, JsonRpcProvider, Wallet } from "ethers"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
import Feedback from "../../../contract-artifacts/Feedback.json"
|
||||
|
||||
@@ -25,9 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const contractAddress = process.env.FEEDBACK_CONTRACT_ADDRESS
|
||||
|
||||
const provider =
|
||||
ethereumNetwork === "localhost"
|
||||
? new providers.JsonRpcProvider()
|
||||
: new providers.InfuraProvider(ethereumNetwork, infuraApiKey)
|
||||
ethereumNetwork === "localhost" ? new JsonRpcProvider() : new InfuraProvider(ethereumNetwork, infuraApiKey)
|
||||
|
||||
const signer = new Wallet(ethereumPrivateKey, provider)
|
||||
const contract = new Contract(contractAddress, Feedback.abi, signer)
|
||||
|
||||
@@ -17,14 +17,14 @@ export default function GroupsPage() {
|
||||
const [_identity, setIdentity] = useState<Identity>()
|
||||
|
||||
useEffect(() => {
|
||||
const identityString = localStorage.getItem("identity")
|
||||
const privateKey = localStorage.getItem("identity")
|
||||
|
||||
if (!identityString) {
|
||||
if (!privateKey) {
|
||||
router.push("/")
|
||||
return
|
||||
}
|
||||
|
||||
setIdentity(new Identity(identityString))
|
||||
setIdentity(new Identity(privateKey))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -10,10 +10,10 @@ export default function IdentitiesPage() {
|
||||
const [_identity, setIdentity] = useState<Identity>()
|
||||
|
||||
useEffect(() => {
|
||||
const identityString = localStorage.getItem("identity")
|
||||
const privateKey = localStorage.getItem("identity")
|
||||
|
||||
if (identityString) {
|
||||
const identity = new Identity(identityString)
|
||||
if (privateKey) {
|
||||
const identity = new Identity(privateKey)
|
||||
|
||||
setIdentity(identity)
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function IdentitiesPage() {
|
||||
|
||||
setIdentity(identity)
|
||||
|
||||
localStorage.setItem("identity", identity.toString())
|
||||
localStorage.setItem("identity", identity.privateKey.toString())
|
||||
|
||||
setLogs("Your new Semaphore identity was just created 🎉")
|
||||
}, [])
|
||||
@@ -68,8 +68,7 @@ export default function IdentitiesPage() {
|
||||
{_identity ? (
|
||||
<div>
|
||||
<div className="box">
|
||||
<p className="box-text">Trapdoor: {_identity.trapdoor.toString()}</p>
|
||||
<p className="box-text">Nullifier: {_identity.nullifier.toString()}</p>
|
||||
<p className="box-text">Private Key: {_identity.privateKey.toString()}</p>
|
||||
<p className="box-text">Commitment: {_identity.commitment.toString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { generateProof } from "@semaphore-protocol/proof"
|
||||
import { BigNumber, utils } from "ethers"
|
||||
import getNextConfig from "next/config"
|
||||
import { useRouter } from "next/router"
|
||||
import { useCallback, useContext, useEffect, useState } from "react"
|
||||
@@ -9,6 +8,7 @@ import Feedback from "../../contract-artifacts/Feedback.json"
|
||||
import Stepper from "../components/Stepper"
|
||||
import LogsContext from "../context/LogsContext"
|
||||
import SemaphoreContext from "../context/SemaphoreContext"
|
||||
import { encodeBytes32String } from "ethers"
|
||||
|
||||
const { publicRuntimeConfig: env } = getNextConfig()
|
||||
|
||||
@@ -20,14 +20,14 @@ export default function ProofsPage() {
|
||||
const [_identity, setIdentity] = useState<Identity>()
|
||||
|
||||
useEffect(() => {
|
||||
const identityString = localStorage.getItem("identity")
|
||||
const privateKey = localStorage.getItem("identity")
|
||||
|
||||
if (!identityString) {
|
||||
if (!privateKey) {
|
||||
router.push("/")
|
||||
return
|
||||
}
|
||||
|
||||
setIdentity(new Identity(identityString))
|
||||
setIdentity(new Identity(privateKey))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -51,15 +51,15 @@ export default function ProofsPage() {
|
||||
try {
|
||||
const group = new Group(env.GROUP_ID)
|
||||
|
||||
const signal = BigNumber.from(utils.formatBytes32String(feedback)).toString()
|
||||
const message = encodeBytes32String(feedback)
|
||||
|
||||
group.addMembers(_users)
|
||||
|
||||
const { proof, merkleTreeRoot, nullifierHash } = await generateProof(
|
||||
const { proof, merkleTreeDepth, merkleTreeRoot, nullifier } = await generateProof(
|
||||
_identity,
|
||||
group,
|
||||
env.GROUP_ID,
|
||||
signal
|
||||
message,
|
||||
env.GROUP_ID
|
||||
)
|
||||
|
||||
let response: any
|
||||
@@ -72,7 +72,7 @@ export default function ProofsPage() {
|
||||
abi: Feedback.abi,
|
||||
address: env.FEEDBACK_CONTRACT_ADDRESS,
|
||||
functionName: "sendFeedback",
|
||||
functionParameters: [signal, merkleTreeRoot, nullifierHash, proof]
|
||||
functionParameters: [merkleTreeDepth, merkleTreeRoot, nullifier, message, proof]
|
||||
})
|
||||
})
|
||||
} else {
|
||||
@@ -80,9 +80,10 @@ export default function ProofsPage() {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
feedback: signal,
|
||||
feedback: message,
|
||||
merkleTreeDepth,
|
||||
merkleTreeRoot,
|
||||
nullifierHash,
|
||||
nullifier,
|
||||
proof
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/cli-template-monorepo-subgraph",
|
||||
"version": "3.15.2",
|
||||
"version": "4.0.0-alpha",
|
||||
"description": "Semaphore Hardhat + Next.js + SemaphoreSubgraph template.",
|
||||
"license": "Unlicense",
|
||||
"files": [
|
||||
@@ -22,14 +22,13 @@
|
||||
"lint": "eslint . --ext .js,.ts",
|
||||
"prettier": "prettier -c .",
|
||||
"prettier:write": "prettier -w .",
|
||||
"copy:contract-artifacts": "ts-node scripts/copy-contract-artifacts.ts",
|
||||
"prepublish": "tar -czf files.tgz .gitignore .yarn .yarnrc.yml apps"
|
||||
},
|
||||
"workspaces": [
|
||||
"apps/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.9",
|
||||
"@types/node": "^20",
|
||||
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
||||
"@typescript-eslint/parser": "^5.9.1",
|
||||
"eslint": "^8.2.0",
|
||||
@@ -40,5 +39,6 @@
|
||||
"prettier": "^2.5.1",
|
||||
"ts-node": "^10.8.1",
|
||||
"typescript": "^4.7.3"
|
||||
}
|
||||
},
|
||||
"stableVersion": "3.15.2"
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import * as fs from "fs"
|
||||
|
||||
async function main() {
|
||||
const contractArtifactsPath = "apps/contracts/build/contracts/contracts/Feedback.sol"
|
||||
const webAppArtifactsPath = "apps/web-app/contract-artifacts"
|
||||
|
||||
await fs.promises.copyFile(`${contractArtifactsPath}/Feedback.json`, `${webAppArtifactsPath}/Feedback.json`)
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ethereum Foundation
|
||||
Copyright (c) 2024 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
|
||||
|
||||
@@ -76,9 +76,9 @@ Options:
|
||||
|
||||
Commands:
|
||||
create [options] [project-directory] Create a Semaphore project with a supported template.
|
||||
get-groups [options] Get the list of groups from a supported network (e.g. goerli or arbitrum).
|
||||
get-group [options] [group-id] Get the data of a group from a supported network (e.g. goerli or arbitrum).
|
||||
get-members [options] [group-id] Get the members of a group from a supported network (e.g. goerli or arbitrum).
|
||||
get-proofs [options] [group-id] Get the proofs of a group from a supported network (e.g. goerli or arbitrum).
|
||||
get-groups [options] Get the list of groups from a supported network (e.g. sepolia or arbitrum).
|
||||
get-group [options] [group-id] Get the data of a group from a supported network (e.g. sepolia or arbitrum).
|
||||
get-members [options] [group-id] Get the members of a group from a supported network (e.g. sepolia or arbitrum).
|
||||
get-proofs [options] [group-id] Get the proofs of a group from a supported network (e.g. sepolia or arbitrum).
|
||||
help [command] Display help for a specific command.
|
||||
```
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/cli",
|
||||
"type": "module",
|
||||
"version": "3.15.2",
|
||||
"version": "4.0.0-alpha",
|
||||
"description": "A command line tool to set up your Semaphore project and get group data.",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -23,7 +23,7 @@
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "ts-node --esm src/index.ts",
|
||||
"start": "node --loader ts-node/esm --no-warnings src/index.ts",
|
||||
"build:watch": "rollup -c rollup.config.ts -w --configPlugin typescript",
|
||||
"build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
|
||||
"prepublishOnly": "yarn build"
|
||||
@@ -42,7 +42,7 @@
|
||||
"ts-node": "^10.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@semaphore-protocol/data": "3.15.2",
|
||||
"@semaphore-protocol/data": "4.0.0-alpha",
|
||||
"axios": "^1.3.2",
|
||||
"boxen": "^7.0.1",
|
||||
"chalk": "^5.1.2",
|
||||
@@ -54,5 +54,6 @@
|
||||
"ora": "^6.1.2",
|
||||
"pacote": "^15.1.1",
|
||||
"semver": "^7.3.8"
|
||||
}
|
||||
},
|
||||
"stableVersion": "3.15.2"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ const banner = `#!/usr/bin/env node
|
||||
* @module ${pkg.name}
|
||||
* @version ${pkg.version}
|
||||
* @file ${pkg.description}
|
||||
* @copyright Ethereum Foundation 2022
|
||||
* @copyright Ethereum Foundation 2024
|
||||
* @license ${pkg.license}
|
||||
* @see [Github]{@link ${pkg.homepage}}
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { GroupResponse, SemaphoreEthers, SemaphoreSubgraph, getSupportedNetworks } from "@semaphore-protocol/data"
|
||||
import chalk from "chalk"
|
||||
import { program } from "commander"
|
||||
import decompress from "decompress"
|
||||
import figlet from "figlet"
|
||||
import { existsSync, readFileSync, unlinkSync, copyFileSync } from "fs"
|
||||
import { copyFileSync, existsSync, readFileSync, unlinkSync } from "fs"
|
||||
import logSymbols from "log-symbols"
|
||||
import pacote from "pacote"
|
||||
import decompress from "decompress"
|
||||
import { dirname } from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
import checkLatestVersion from "./checkLatestVersion.js"
|
||||
@@ -117,7 +117,7 @@ program
|
||||
|
||||
program
|
||||
.command("get-groups")
|
||||
.description("Get the list of groups from a supported network (e.g. goerli or arbitrum).")
|
||||
.description("Get the list of groups from a supported network (e.g. sepolia or arbitrum).")
|
||||
.option("-n, --network <network-name>", "Supported Ethereum network.")
|
||||
.allowExcessArguments(false)
|
||||
.action(async ({ network }) => {
|
||||
@@ -146,7 +146,7 @@ program
|
||||
|
||||
program
|
||||
.command("get-group")
|
||||
.description("Get the data of a group from a supported network (e.g. goerli or arbitrum).")
|
||||
.description("Get the data of a group from a supported network (e.g. sepolia or arbitrum).")
|
||||
.argument("[group-id]", "Identifier of the group.")
|
||||
.option("-n, --network <network-name>", "Supported Ethereum network.")
|
||||
.allowExcessArguments(false)
|
||||
@@ -209,15 +209,14 @@ program
|
||||
content += ` ${chalk.bold("Merkle tree")}:\n`
|
||||
content += ` Root: ${group.merkleTree.root}\n`
|
||||
content += ` Depth: ${group.merkleTree.depth}\n`
|
||||
content += ` Zero value: ${group.merkleTree.zeroValue}\n`
|
||||
content += ` Number of leaves: ${group.merkleTree.numberOfLeaves}`
|
||||
content += ` Size: ${group.merkleTree.size}`
|
||||
|
||||
console.info(`\n${content}\n`)
|
||||
})
|
||||
|
||||
program
|
||||
.command("get-members")
|
||||
.description("Get the members of a group from a supported network (e.g. goerli or arbitrum).")
|
||||
.description("Get the members of a group from a supported network (e.g. sepolia or arbitrum).")
|
||||
.argument("[group-id]", "Identifier of the group.")
|
||||
.option("-n, --network <network-name>", "Supported Ethereum network.")
|
||||
.allowExcessArguments(false)
|
||||
@@ -283,7 +282,7 @@ program
|
||||
|
||||
program
|
||||
.command("get-proofs")
|
||||
.description("Get the proofs of a group from a supported network (e.g. goerli or arbitrum).")
|
||||
.description("Get the proofs of a group from a supported network (e.g. sepolia or arbitrum).")
|
||||
.argument("[group-id]", "Identifier of the group.")
|
||||
.option("-n, --network <network-name>", "Supported Ethereum network.")
|
||||
.allowExcessArguments(false)
|
||||
@@ -308,7 +307,7 @@ program
|
||||
groupId = await getGroupId(groupIds)
|
||||
}
|
||||
|
||||
let verifiedProofs: any[]
|
||||
let validatedProofs: any[]
|
||||
|
||||
const spinner = new Spinner(`Fetching proofs of group ${groupId}`)
|
||||
|
||||
@@ -317,15 +316,15 @@ program
|
||||
try {
|
||||
const semaphoreSubgraph = new SemaphoreSubgraph(network)
|
||||
|
||||
const group = await semaphoreSubgraph.getGroup(groupId, { verifiedProofs: true })
|
||||
verifiedProofs = group.verifiedProofs
|
||||
const group = await semaphoreSubgraph.getGroup(groupId, { validatedProofs: true })
|
||||
validatedProofs = group.validatedProofs
|
||||
|
||||
spinner.stop()
|
||||
} catch {
|
||||
try {
|
||||
const semaphoreEthers = new SemaphoreEthers(network)
|
||||
|
||||
verifiedProofs = await semaphoreEthers.getGroupVerifiedProofs(groupId)
|
||||
validatedProofs = await semaphoreEthers.getGroupValidatedProofs(groupId)
|
||||
|
||||
spinner.stop()
|
||||
} catch {
|
||||
@@ -335,15 +334,17 @@ program
|
||||
}
|
||||
}
|
||||
|
||||
if (verifiedProofs.length === 0) {
|
||||
if (validatedProofs.length === 0) {
|
||||
console.info(`\n ${logSymbols.info}`, "info: there are no proofs in this group\n")
|
||||
return
|
||||
}
|
||||
|
||||
const content = `${chalk.bold("Proofs")} (${verifiedProofs.length}): \n${verifiedProofs
|
||||
const content = `${chalk.bold("Proofs")} (${validatedProofs.length}): \n${validatedProofs
|
||||
.map(
|
||||
({ signal, merkleTreeRoot, externalNullifier, nullifierHash }: any, i: number) =>
|
||||
` ${i}. signal: ${signal} \n merkleTreeRoot: ${merkleTreeRoot} \n externalNullifier: ${externalNullifier} \n nullifierHash: ${nullifierHash}`
|
||||
({ message, merkleTreeRoot, merkleTreeDepth, scope, nullifier, proof }: any, i: number) =>
|
||||
` ${i}. message: ${message} \n merkleTreeRoot: ${merkleTreeRoot} \n merkleTreeDepth: ${merkleTreeDepth} \n scope: ${scope} \n nullifier: ${nullifier} \n proof: [${proof.join(
|
||||
", "
|
||||
)}]`
|
||||
)
|
||||
.join("\n")}`
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
contracts/base/Pairing.sol
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ethereum Foundation
|
||||
Copyright (c) 2024 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
|
||||
|
||||
@@ -107,14 +107,14 @@ yarn deploy:semaphore --semaphoreVerifier <address>
|
||||
> **Note**
|
||||
> Run `yarn deploy:semaphore --help` to see the complete list.
|
||||
|
||||
If you want to deploy your contract in a specific network you can set up the `DEFAULT_NETWORK` variable in your `.env` file with the name of one of our supported networks (hardhat, localhost, goerli, arbitrum). Or you can specify it as an option:
|
||||
If you want to deploy your contract in a specific network you can set up the `DEFAULT_NETWORK` variable in your `.env` file with the name of one of our supported networks (hardhat, localhost, sepolia, arbitrum). Or you can specify it as an option:
|
||||
|
||||
```bash
|
||||
yarn deploy:semaphore --network goerli
|
||||
yarn deploy:semaphore --network sepolia
|
||||
yarn deploy:semaphore --network mumbai
|
||||
yarn deploy:semaphore --network optimism-goerli
|
||||
yarn deploy:semaphore --network optimism-sepolia
|
||||
yarn deploy:semaphore --network arbitrum-sepolia
|
||||
yarn deploy:semaphore --network arbitrum
|
||||
```
|
||||
|
||||
If you want to deploy contracts on Goerli or Arbitrum, remember to provide a valid private key and an Infura API in your `.env` file.
|
||||
If you want to deploy contracts on Sepolia or Arbitrum, remember to provide a valid private key and an Infura API in your `.env` file.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
pragma solidity 0.8.23;
|
||||
|
||||
import "./interfaces/ISemaphore.sol";
|
||||
import "./interfaces/ISemaphoreVerifier.sol";
|
||||
import "./base/SemaphoreGroups.sol";
|
||||
import {ISemaphore} from "./interfaces/ISemaphore.sol";
|
||||
import {ISemaphoreVerifier} from "./interfaces/ISemaphoreVerifier.sol";
|
||||
import {SemaphoreGroups} from "./base/SemaphoreGroups.sol";
|
||||
|
||||
/// @title Semaphore
|
||||
/// @dev This contract uses the Semaphore base contracts to provide a complete service
|
||||
@@ -18,71 +18,36 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
|
||||
/// @dev Gets a group id and returns the group parameters.
|
||||
mapping(uint256 => Group) public groups;
|
||||
|
||||
/// @dev Checks if the group admin is the transaction sender.
|
||||
/// @param groupId: Id of the group.
|
||||
modifier onlyGroupAdmin(uint256 groupId) {
|
||||
if (groups[groupId].admin != _msgSender()) {
|
||||
revert Semaphore__CallerIsNotTheGroupAdmin();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Checks if there is a verifier for the given tree depth.
|
||||
/// @param merkleTreeDepth: Depth of the tree.
|
||||
modifier onlySupportedMerkleTreeDepth(uint256 merkleTreeDepth) {
|
||||
if (merkleTreeDepth < 16 || merkleTreeDepth > 32) {
|
||||
revert Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Initializes the Semaphore verifier used to verify the user's ZK proofs.
|
||||
/// @param _verifier: Semaphore verifier address.
|
||||
/// @param _verifier: Semaphore verifier addresse.
|
||||
constructor(ISemaphoreVerifier _verifier) {
|
||||
verifier = _verifier;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-createGroup}.
|
||||
function createGroup(
|
||||
uint256 groupId,
|
||||
uint256 merkleTreeDepth,
|
||||
address admin
|
||||
) external override onlySupportedMerkleTreeDepth(merkleTreeDepth) {
|
||||
_createGroup(groupId, merkleTreeDepth);
|
||||
/// @dev See {SemaphoreGroups-_createGroup}.
|
||||
function createGroup(uint256 groupId, address admin) external override {
|
||||
_createGroup(groupId, admin);
|
||||
|
||||
groups[groupId].admin = admin;
|
||||
groups[groupId].merkleTreeDuration = 1 hours;
|
||||
|
||||
emit GroupAdminUpdated(groupId, address(0), admin);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-createGroup}.
|
||||
function createGroup(
|
||||
uint256 groupId,
|
||||
uint256 merkleTreeDepth,
|
||||
address admin,
|
||||
uint256 merkleTreeDuration
|
||||
) external override onlySupportedMerkleTreeDepth(merkleTreeDepth) {
|
||||
_createGroup(groupId, merkleTreeDepth);
|
||||
function createGroup(uint256 groupId, address admin, uint256 merkleTreeDuration) external override {
|
||||
_createGroup(groupId, admin);
|
||||
|
||||
groups[groupId].admin = admin;
|
||||
groups[groupId].merkleTreeDuration = merkleTreeDuration;
|
||||
|
||||
emit GroupAdminUpdated(groupId, address(0), admin);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-updateGroupAdmin}.
|
||||
function updateGroupAdmin(uint256 groupId, address newAdmin) external override onlyGroupAdmin(groupId) {
|
||||
groups[groupId].admin = newAdmin;
|
||||
|
||||
emit GroupAdminUpdated(groupId, _msgSender(), newAdmin);
|
||||
/// @dev See {SemaphoreGroups-_updateGroupAdmin}.
|
||||
function updateGroupAdmin(uint256 groupId, address newAdmin) external override {
|
||||
_updateGroupAdmin(groupId, newAdmin);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-updateGroupMerkleTreeDuration}.
|
||||
function updateGroupMerkleTreeDuration(
|
||||
uint256 groupId,
|
||||
uint256 newMerkleTreeDuration
|
||||
) external override onlyGroupAdmin(groupId) {
|
||||
) external override onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
|
||||
uint256 oldMerkleTreeDuration = groups[groupId].merkleTreeDuration;
|
||||
|
||||
groups[groupId].merkleTreeDuration = newMerkleTreeDuration;
|
||||
@@ -90,8 +55,8 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
|
||||
emit GroupMerkleTreeDurationUpdated(groupId, oldMerkleTreeDuration, newMerkleTreeDuration);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-addMember}.
|
||||
function addMember(uint256 groupId, uint256 identityCommitment) external override onlyGroupAdmin(groupId) {
|
||||
/// @dev See {SemaphoreGroups-_addMember}.
|
||||
function addMember(uint256 groupId, uint256 identityCommitment) external override {
|
||||
_addMember(groupId, identityCommitment);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
@@ -99,66 +64,81 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
|
||||
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-addMembers}.
|
||||
function addMembers(
|
||||
uint256 groupId,
|
||||
uint256[] calldata identityCommitments
|
||||
) external override onlyGroupAdmin(groupId) {
|
||||
for (uint256 i = 0; i < identityCommitments.length; ) {
|
||||
_addMember(groupId, identityCommitments[i]);
|
||||
|
||||
unchecked {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
/// @dev See {SemaphoreGroups-_addMembers}.
|
||||
function addMembers(uint256 groupId, uint256[] calldata identityCommitments) external override {
|
||||
_addMembers(groupId, identityCommitments);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
|
||||
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-updateMember}.
|
||||
/// @dev See {SemaphoreGroups-_updateMember}.
|
||||
function updateMember(
|
||||
uint256 groupId,
|
||||
uint256 identityCommitment,
|
||||
uint256 newIdentityCommitment,
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
) external override onlyGroupAdmin(groupId) {
|
||||
_updateMember(groupId, identityCommitment, newIdentityCommitment, proofSiblings, proofPathIndices);
|
||||
uint256[] calldata merkleProofSiblings
|
||||
) external override {
|
||||
_updateMember(groupId, identityCommitment, newIdentityCommitment, merkleProofSiblings);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
|
||||
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-removeMember}.
|
||||
/// @dev See {SemaphoreGroups-_removeMember}.
|
||||
function removeMember(
|
||||
uint256 groupId,
|
||||
uint256 identityCommitment,
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
) external override onlyGroupAdmin(groupId) {
|
||||
_removeMember(groupId, identityCommitment, proofSiblings, proofPathIndices);
|
||||
uint256[] calldata merkleProofSiblings
|
||||
) external override {
|
||||
_removeMember(groupId, identityCommitment, merkleProofSiblings);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
|
||||
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-verifyProof}.
|
||||
function validateProof(
|
||||
uint256 groupId,
|
||||
uint256 merkleTreeDepth,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 nullifier,
|
||||
uint256 message,
|
||||
uint256 scope,
|
||||
uint256[8] calldata proof
|
||||
) external override onlyExistingGroup(groupId) {
|
||||
if (groups[groupId].nullifiers[nullifier]) {
|
||||
revert Semaphore__YouAreUsingTheSameNullifierTwice();
|
||||
}
|
||||
|
||||
if (!verifyProof(groupId, merkleTreeDepth, merkleTreeRoot, nullifier, message, scope, proof)) {
|
||||
revert Semaphore__InvalidProof();
|
||||
}
|
||||
|
||||
groups[groupId].nullifiers[nullifier] = true;
|
||||
|
||||
emit ProofValidated(groupId, merkleTreeDepth, merkleTreeRoot, nullifier, message, scope, proof);
|
||||
}
|
||||
|
||||
function verifyProof(
|
||||
uint256 groupId,
|
||||
uint256 merkleTreeDepth,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 signal,
|
||||
uint256 nullifierHash,
|
||||
uint256 externalNullifier,
|
||||
uint256 nullifier,
|
||||
uint256 message,
|
||||
uint256 scope,
|
||||
uint256[8] calldata proof
|
||||
) external override {
|
||||
uint256 merkleTreeDepth = getMerkleTreeDepth(groupId);
|
||||
) public view override onlyExistingGroup(groupId) returns (bool) {
|
||||
if (merkleTreeDepth < 1 || merkleTreeDepth > 12) {
|
||||
revert Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
}
|
||||
|
||||
if (merkleTreeDepth == 0) {
|
||||
revert Semaphore__GroupDoesNotExist();
|
||||
uint256 merkleTreeSize = getMerkleTreeSize(groupId);
|
||||
|
||||
if (merkleTreeSize == 0) {
|
||||
revert Semaphore__GroupHasNoMembers();
|
||||
}
|
||||
|
||||
uint256 currentMerkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
@@ -178,14 +158,20 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
|
||||
}
|
||||
}
|
||||
|
||||
if (groups[groupId].nullifierHashes[nullifierHash]) {
|
||||
revert Semaphore__YouAreUsingTheSameNillifierTwice();
|
||||
}
|
||||
return
|
||||
verifier.verifyProof(
|
||||
[proof[0], proof[1]],
|
||||
[[proof[2], proof[3]], [proof[4], proof[5]]],
|
||||
[proof[6], proof[7]],
|
||||
[merkleTreeRoot, nullifier, _hash(message), _hash(scope)],
|
||||
merkleTreeDepth
|
||||
);
|
||||
}
|
||||
|
||||
verifier.verifyProof(merkleTreeRoot, nullifierHash, signal, externalNullifier, proof, merkleTreeDepth);
|
||||
|
||||
groups[groupId].nullifierHashes[nullifierHash] = true;
|
||||
|
||||
emit ProofVerified(groupId, merkleTreeRoot, nullifierHash, externalNullifier, signal);
|
||||
/// @dev Creates a keccak256 hash of a message compatible with the SNARK scalar modulus.
|
||||
/// @param message: Message to be hashed.
|
||||
/// @return Message digest.
|
||||
function _hash(uint256 message) private pure returns (uint256) {
|
||||
return uint256(keccak256(abi.encodePacked(message))) >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// The following Pairing library is a modified version adapted to Semaphore.
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
library Pairing {
|
||||
error InvalidProof();
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// The prime modulus of the scalar field of G1.
|
||||
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/// @return the generator of G1
|
||||
function P1() public pure returns (G1Point memory) {
|
||||
return G1Point(1, 2);
|
||||
}
|
||||
|
||||
/// @return the generator of G2
|
||||
function P2() public pure returns (G2Point memory) {
|
||||
return
|
||||
G2Point(
|
||||
[
|
||||
11559732032986387107991004021392285783925812861821192530917403151452391805634,
|
||||
10857046999023057135944570762232829481370756359578518086990519993285655852781
|
||||
],
|
||||
[
|
||||
4082367875863433681332203403145435568316851327593401208105741076214120093531,
|
||||
8495653923123431417604973247489272438418190587263600148770280649306958101930
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
|
||||
function negate(G1Point memory p) public pure returns (G1Point memory r) {
|
||||
if (p.X == 0 && p.Y == 0) {
|
||||
return G1Point(0, 0);
|
||||
}
|
||||
|
||||
// Validate input or revert
|
||||
if (p.X >= BASE_MODULUS || p.Y >= BASE_MODULUS) {
|
||||
revert InvalidProof();
|
||||
}
|
||||
|
||||
// We know p.Y > 0 and p.Y < BASE_MODULUS.
|
||||
return G1Point(p.X, BASE_MODULUS - p.Y);
|
||||
}
|
||||
|
||||
/// @return r the sum of two points of G1
|
||||
function addition(G1Point memory p1, G1Point memory p2) public view returns (G1Point memory r) {
|
||||
// By EIP-196 all input is validated to be less than the BASE_MODULUS and form points
|
||||
// on the curve.
|
||||
uint256[4] memory input;
|
||||
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
|
||||
bool success;
|
||||
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
/// @return r the product of a point on G1 and a scalar, i.e.
|
||||
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
|
||||
function scalar_mul(G1Point memory p, uint256 s) public view returns (G1Point memory r) {
|
||||
// By EIP-196 the values p.X and p.Y are verified to be less than the BASE_MODULUS and
|
||||
// form a valid point on the curve. But the scalar is not verified, so we do that explicitly.
|
||||
if (s >= SCALAR_MODULUS) {
|
||||
revert InvalidProof();
|
||||
}
|
||||
|
||||
uint256[3] memory input;
|
||||
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
|
||||
bool success;
|
||||
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
revert InvalidProof();
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts the pairing check
|
||||
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should succeed
|
||||
function pairingCheck(G1Point[] memory p1, G2Point[] memory p2) public view {
|
||||
// By EIP-197 all input is verified to be less than the BASE_MODULUS and form elements in their
|
||||
// respective groups of the right order.
|
||||
if (p1.length != p2.length) {
|
||||
revert InvalidProof();
|
||||
}
|
||||
|
||||
uint256 elements = p1.length;
|
||||
uint256 inputSize = elements * 6;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
|
||||
for (uint256 i = 0; i < elements; i++) {
|
||||
input[i * 6 + 0] = p1[i].X;
|
||||
input[i * 6 + 1] = p1[i].Y;
|
||||
input[i * 6 + 2] = p2[i].X[0];
|
||||
input[i * 6 + 3] = p2[i].X[1];
|
||||
input[i * 6 + 4] = p2[i].Y[0];
|
||||
input[i * 6 + 5] = p2[i].Y[1];
|
||||
}
|
||||
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
}
|
||||
|
||||
if (!success || out[0] != 1) {
|
||||
revert InvalidProof();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +1,141 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
pragma solidity 0.8.23;
|
||||
|
||||
import "../interfaces/ISemaphoreGroups.sol";
|
||||
import "@zk-kit/incremental-merkle-tree.sol/IncrementalBinaryTree.sol";
|
||||
import "@openzeppelin/contracts/utils/Context.sol";
|
||||
import {ISemaphoreGroups} from "../interfaces/ISemaphoreGroups.sol";
|
||||
import {InternalLeanIMT, LeanIMTData} from "@zk-kit/imt.sol/internal/InternalLeanIMT.sol";
|
||||
|
||||
/// @title Semaphore groups contract.
|
||||
/// @dev This contract allows you to create groups, add, remove and update members.
|
||||
/// You can use getters to obtain informations about groups (root, depth, number of leaves).
|
||||
abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
using IncrementalBinaryTree for IncrementalTreeData;
|
||||
abstract contract SemaphoreGroups is ISemaphoreGroups {
|
||||
using InternalLeanIMT for LeanIMTData;
|
||||
|
||||
/// @dev Gets a group id and returns the tree data.
|
||||
mapping(uint256 => IncrementalTreeData) internal merkleTrees;
|
||||
/// @dev Gets a group id and returns its tree data.
|
||||
mapping(uint256 => LeanIMTData) internal merkleTrees;
|
||||
|
||||
/// @dev Creates a new group by initializing the associated tree.
|
||||
/// @dev Gets a group id and returns its admin.
|
||||
mapping(uint256 => address) internal admins;
|
||||
|
||||
/// @dev Checks if the group admin is the transaction sender.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param merkleTreeDepth: Depth of the tree.
|
||||
function _createGroup(uint256 groupId, uint256 merkleTreeDepth) internal virtual {
|
||||
if (getMerkleTreeDepth(groupId) != 0) {
|
||||
modifier onlyGroupAdmin(uint256 groupId) {
|
||||
if (admins[groupId] != msg.sender) {
|
||||
revert Semaphore__CallerIsNotTheGroupAdmin();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Checks if the group exists.
|
||||
/// @param groupId: Id of the group.
|
||||
modifier onlyExistingGroup(uint256 groupId) {
|
||||
if (admins[groupId] == address(0)) {
|
||||
revert Semaphore__GroupDoesNotExist();
|
||||
}
|
||||
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Creates a new group. Only the admin will be able to add or remove members.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param admin: Admin of the group.
|
||||
function _createGroup(uint256 groupId, address admin) internal virtual {
|
||||
if (admins[groupId] != address(0)) {
|
||||
revert Semaphore__GroupAlreadyExists();
|
||||
}
|
||||
|
||||
// The zeroValue is an implicit member of the group, or an implicit leaf of the Merkle tree.
|
||||
// Although there is a remote possibility that the preimage of
|
||||
// the hash may be calculated, using this value we aim to minimize the risk.
|
||||
uint256 zeroValue = uint256(keccak256(abi.encodePacked(groupId))) >> 8;
|
||||
admins[groupId] = admin;
|
||||
|
||||
merkleTrees[groupId].init(merkleTreeDepth, zeroValue);
|
||||
emit GroupCreated(groupId);
|
||||
emit GroupAdminUpdated(groupId, address(0), admin);
|
||||
}
|
||||
|
||||
emit GroupCreated(groupId, merkleTreeDepth, zeroValue);
|
||||
/// @dev Updates the group admin.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param newAdmin: New admin of the group.
|
||||
function _updateGroupAdmin(
|
||||
uint256 groupId,
|
||||
address newAdmin
|
||||
) internal virtual onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
|
||||
admins[groupId] = newAdmin;
|
||||
|
||||
emit GroupAdminUpdated(groupId, msg.sender, newAdmin);
|
||||
}
|
||||
|
||||
/// @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 {
|
||||
if (getMerkleTreeDepth(groupId) == 0) {
|
||||
revert Semaphore__GroupDoesNotExist();
|
||||
}
|
||||
|
||||
merkleTrees[groupId].insert(identityCommitment);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
uint256 index = getNumberOfMerkleTreeLeaves(groupId) - 1;
|
||||
function _addMember(
|
||||
uint256 groupId,
|
||||
uint256 identityCommitment
|
||||
) internal virtual onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
|
||||
uint256 index = getMerkleTreeSize(groupId);
|
||||
uint256 merkleTreeRoot = merkleTrees[groupId]._insert(identityCommitment);
|
||||
|
||||
emit MemberAdded(groupId, index, identityCommitment, merkleTreeRoot);
|
||||
}
|
||||
|
||||
/// @dev Adds new members to an existing group.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param identityCommitments: New identity commitments.
|
||||
function _addMembers(uint256 groupId, uint256[] calldata identityCommitments) internal virtual {
|
||||
uint256 startIndex = getMerkleTreeSize(groupId);
|
||||
uint256 merkleTreeRoot = merkleTrees[groupId]._insertMany(identityCommitments);
|
||||
|
||||
emit MembersAdded(groupId, startIndex, identityCommitments, merkleTreeRoot);
|
||||
}
|
||||
|
||||
/// @dev Updates an identity commitment of an existing group. A proof of membership is
|
||||
/// needed to check if the node to be updated is part of the tree.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param identityCommitment: Existing identity commitment to be updated.
|
||||
/// @param oldIdentityCommitment: Existing identity commitment to be updated.
|
||||
/// @param newIdentityCommitment: New identity commitment.
|
||||
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
|
||||
/// @param proofPathIndices: Path of the proof of membership.
|
||||
/// @param merkleProofSiblings: Array of the sibling nodes of the proof of membership.
|
||||
function _updateMember(
|
||||
uint256 groupId,
|
||||
uint256 identityCommitment,
|
||||
uint256 oldIdentityCommitment,
|
||||
uint256 newIdentityCommitment,
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
) internal virtual {
|
||||
if (getMerkleTreeDepth(groupId) == 0) {
|
||||
revert Semaphore__GroupDoesNotExist();
|
||||
}
|
||||
uint256[] calldata merkleProofSiblings
|
||||
) internal virtual onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
|
||||
uint256 index = merkleTrees[groupId]._indexOf(oldIdentityCommitment);
|
||||
uint256 merkleTreeRoot = merkleTrees[groupId]._update(
|
||||
oldIdentityCommitment,
|
||||
newIdentityCommitment,
|
||||
merkleProofSiblings
|
||||
);
|
||||
|
||||
merkleTrees[groupId].update(identityCommitment, newIdentityCommitment, proofSiblings, proofPathIndices);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
uint256 index = proofPathIndicesToMemberIndex(proofPathIndices);
|
||||
|
||||
emit MemberUpdated(groupId, index, identityCommitment, newIdentityCommitment, merkleTreeRoot);
|
||||
emit MemberUpdated(groupId, index, oldIdentityCommitment, newIdentityCommitment, merkleTreeRoot);
|
||||
}
|
||||
|
||||
/// @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 removed.
|
||||
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
|
||||
/// @param proofPathIndices: Path of the proof of membership.
|
||||
/// @param merkleProofSiblings: Array of the sibling nodes of the proof of membership.
|
||||
function _removeMember(
|
||||
uint256 groupId,
|
||||
uint256 identityCommitment,
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
) internal virtual {
|
||||
if (getMerkleTreeDepth(groupId) == 0) {
|
||||
revert Semaphore__GroupDoesNotExist();
|
||||
}
|
||||
uint256[] calldata merkleProofSiblings
|
||||
) internal virtual onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
|
||||
uint256 index = merkleTrees[groupId]._indexOf(identityCommitment);
|
||||
|
||||
merkleTrees[groupId].remove(identityCommitment, proofSiblings, proofPathIndices);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
uint256 index = proofPathIndicesToMemberIndex(proofPathIndices);
|
||||
uint256 merkleTreeRoot = merkleTrees[groupId]._remove(identityCommitment, merkleProofSiblings);
|
||||
|
||||
emit MemberRemoved(groupId, index, identityCommitment, merkleTreeRoot);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreGroups-hasMember}.
|
||||
function hasMember(uint256 groupId, uint256 identityCommitment) public view virtual override returns (bool) {
|
||||
return merkleTrees[groupId]._has(identityCommitment);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreGroups-indexOf}.
|
||||
function indexOf(uint256 groupId, uint256 identityCommitment) public view virtual override returns (uint256) {
|
||||
return merkleTrees[groupId]._indexOf(identityCommitment);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreGroups-getMerkleTreeRoot}.
|
||||
function getMerkleTreeRoot(uint256 groupId) public view virtual override returns (uint256) {
|
||||
return merkleTrees[groupId].root;
|
||||
return merkleTrees[groupId]._root();
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreGroups-getMerkleTreeDepth}.
|
||||
@@ -108,31 +143,8 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
return merkleTrees[groupId].depth;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreGroups-getNumberOfMerkleTreeLeaves}.
|
||||
function getNumberOfMerkleTreeLeaves(uint256 groupId) public view virtual override returns (uint256) {
|
||||
return merkleTrees[groupId].numberOfLeaves;
|
||||
}
|
||||
|
||||
/// @dev Converts the path indices of a Merkle proof to the identity commitment index in the tree.
|
||||
/// @param proofPathIndices: Path of the proof of membership.
|
||||
/// @return Index of a group member.
|
||||
function proofPathIndicesToMemberIndex(uint8[] calldata proofPathIndices) private pure returns (uint256) {
|
||||
uint256 memberIndex = 0;
|
||||
|
||||
for (uint8 i = uint8(proofPathIndices.length); i > 0; ) {
|
||||
if (memberIndex > 0 || proofPathIndices[i - 1] != 0) {
|
||||
memberIndex *= 2;
|
||||
|
||||
if (proofPathIndices[i - 1] == 1) {
|
||||
memberIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
unchecked {
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
return memberIndex;
|
||||
/// @dev See {ISemaphoreGroups-getMerkleTreeSize}.
|
||||
function getMerkleTreeSize(uint256 groupId) public view virtual override returns (uint256) {
|
||||
return merkleTrees[groupId].size;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,96 +0,0 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
import "../interfaces/ISemaphoreVoting.sol";
|
||||
import "../interfaces/ISemaphoreVerifier.sol";
|
||||
import "../base/SemaphoreGroups.sol";
|
||||
|
||||
/// @title Semaphore voting contract.
|
||||
/// @notice It allows users to vote anonymously in a poll.
|
||||
/// @dev The following code allows you to create polls, add voters and allow them to vote anonymously.
|
||||
contract SemaphoreVoting is ISemaphoreVoting, SemaphoreGroups {
|
||||
ISemaphoreVerifier public verifier;
|
||||
|
||||
/// @dev Gets a poll id and returns the poll data.
|
||||
mapping(uint256 => Poll) internal polls;
|
||||
|
||||
/// @dev Checks if the poll coordinator is the transaction sender.
|
||||
/// @param pollId: Id of the poll.
|
||||
modifier onlyCoordinator(uint256 pollId) {
|
||||
if (polls[pollId].coordinator != _msgSender()) {
|
||||
revert Semaphore__CallerIsNotThePollCoordinator();
|
||||
}
|
||||
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Initializes the Semaphore verifier used to verify the user's ZK proofs.
|
||||
/// @param _verifier: Semaphore verifier address.
|
||||
constructor(ISemaphoreVerifier _verifier) {
|
||||
verifier = _verifier;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreVoting-createPoll}.
|
||||
function createPoll(uint256 pollId, address coordinator, uint256 merkleTreeDepth) public override {
|
||||
if (merkleTreeDepth < 16 || merkleTreeDepth > 32) {
|
||||
revert Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
}
|
||||
|
||||
_createGroup(pollId, merkleTreeDepth);
|
||||
|
||||
polls[pollId].coordinator = coordinator;
|
||||
|
||||
emit PollCreated(pollId, coordinator);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreVoting-addVoter}.
|
||||
function addVoter(uint256 pollId, uint256 identityCommitment) public override onlyCoordinator(pollId) {
|
||||
if (polls[pollId].state != PollState.Created) {
|
||||
revert Semaphore__PollHasAlreadyBeenStarted();
|
||||
}
|
||||
|
||||
_addMember(pollId, identityCommitment);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreVoting-addVoter}.
|
||||
function startPoll(uint256 pollId, uint256 encryptionKey) public override onlyCoordinator(pollId) {
|
||||
if (polls[pollId].state != PollState.Created) {
|
||||
revert Semaphore__PollHasAlreadyBeenStarted();
|
||||
}
|
||||
|
||||
polls[pollId].state = PollState.Ongoing;
|
||||
|
||||
emit PollStarted(pollId, _msgSender(), encryptionKey);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreVoting-castVote}.
|
||||
function castVote(uint256 vote, uint256 nullifierHash, uint256 pollId, uint256[8] calldata proof) public override {
|
||||
if (polls[pollId].state != PollState.Ongoing) {
|
||||
revert Semaphore__PollIsNotOngoing();
|
||||
}
|
||||
|
||||
if (polls[pollId].nullifierHashes[nullifierHash]) {
|
||||
revert Semaphore__YouAreUsingTheSameNillifierTwice();
|
||||
}
|
||||
|
||||
uint256 merkleTreeDepth = getMerkleTreeDepth(pollId);
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(pollId);
|
||||
|
||||
verifier.verifyProof(merkleTreeRoot, nullifierHash, vote, pollId, proof, merkleTreeDepth);
|
||||
|
||||
polls[pollId].nullifierHashes[nullifierHash] = true;
|
||||
|
||||
emit VoteAdded(pollId, vote);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreVoting-publishDecryptionKey}.
|
||||
function endPoll(uint256 pollId, uint256 decryptionKey) public override onlyCoordinator(pollId) {
|
||||
if (polls[pollId].state != PollState.Ongoing) {
|
||||
revert Semaphore__PollIsNotOngoing();
|
||||
}
|
||||
|
||||
polls[pollId].state = PollState.Ended;
|
||||
|
||||
emit PollEnded(pollId, _msgSender(), decryptionKey);
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
|
||||
import "../interfaces/ISemaphoreWhistleblowing.sol";
|
||||
import "../interfaces/ISemaphoreVerifier.sol";
|
||||
import "../base/SemaphoreGroups.sol";
|
||||
|
||||
/// @title Semaphore whistleblowing contract.
|
||||
/// @notice It allows users to leak information anonymously .
|
||||
/// @dev The following code allows you to create entities for whistleblowers (e.g. non-profit
|
||||
/// organization, newspaper) and allow them to leak anonymously.
|
||||
/// Leaks can be IPFS hashes, permanent links or other kinds of references.
|
||||
contract SemaphoreWhistleblowing is ISemaphoreWhistleblowing, SemaphoreGroups {
|
||||
ISemaphoreVerifier public verifier;
|
||||
|
||||
/// @dev Gets an entity id and return its editor address.
|
||||
mapping(uint256 => address) private entities;
|
||||
|
||||
/// @dev Checks if the editor is the transaction sender.
|
||||
/// @param entityId: Id of the entity.
|
||||
modifier onlyEditor(uint256 entityId) {
|
||||
if (entities[entityId] != _msgSender()) {
|
||||
revert Semaphore__CallerIsNotTheEditor();
|
||||
}
|
||||
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Initializes the Semaphore verifier used to verify the user's ZK proofs.
|
||||
/// @param _verifier: Semaphore verifier address.
|
||||
constructor(ISemaphoreVerifier _verifier) {
|
||||
verifier = _verifier;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreWhistleblowing-createEntity}.
|
||||
function createEntity(uint256 entityId, address editor, uint256 merkleTreeDepth) public override {
|
||||
if (merkleTreeDepth < 16 || merkleTreeDepth > 32) {
|
||||
revert Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
}
|
||||
|
||||
_createGroup(entityId, merkleTreeDepth);
|
||||
|
||||
entities[entityId] = editor;
|
||||
|
||||
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(
|
||||
uint256 leak,
|
||||
uint256 nullifierHash,
|
||||
uint256 entityId,
|
||||
uint256[8] calldata proof
|
||||
) public override {
|
||||
uint256 merkleTreeDepth = getMerkleTreeDepth(entityId);
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(entityId);
|
||||
|
||||
verifier.verifyProof(merkleTreeRoot, nullifierHash, leak, entityId, proof, merkleTreeDepth);
|
||||
|
||||
emit LeakPublished(entityId, leak);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,22 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.4;
|
||||
pragma solidity 0.8.23;
|
||||
|
||||
/// @title Semaphore contract interface.
|
||||
interface ISemaphore {
|
||||
error Semaphore__CallerIsNotTheGroupAdmin();
|
||||
error Semaphore__GroupHasNoMembers();
|
||||
error Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
error Semaphore__MerkleTreeRootIsExpired();
|
||||
error Semaphore__MerkleTreeRootIsNotPartOfTheGroup();
|
||||
error Semaphore__YouAreUsingTheSameNillifierTwice();
|
||||
error Semaphore__YouAreUsingTheSameNullifierTwice();
|
||||
error Semaphore__InvalidProof();
|
||||
|
||||
/// It defines all the group parameters, in addition to those in the Merkle tree.
|
||||
/// It defines all the group parameters used by Semaphore.sol.
|
||||
struct Group {
|
||||
address admin;
|
||||
uint256 merkleTreeDuration;
|
||||
mapping(uint256 => uint256) merkleRootCreationDates;
|
||||
mapping(uint256 => bool) nullifierHashes;
|
||||
mapping(uint256 => bool) nullifiers;
|
||||
}
|
||||
|
||||
/// @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 the Merkle tree duration of a group is updated.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param oldMerkleTreeDuration: Old Merkle tree duration of the group.
|
||||
@@ -33,53 +27,34 @@ interface ISemaphore {
|
||||
uint256 newMerkleTreeDuration
|
||||
);
|
||||
|
||||
/// @dev Emitted when a Semaphore proof is verified.
|
||||
/// @dev Emitted when a Semaphore proof is validated.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param merkleTreeDepth: Depth of the Merkle tree.
|
||||
/// @param merkleTreeRoot: Root of the Merkle tree.
|
||||
/// @param nullifierHash: Nullifier hash.
|
||||
/// @param externalNullifier: External nullifier.
|
||||
/// @param signal: Semaphore signal.
|
||||
event ProofVerified(
|
||||
/// @param nullifier: Nullifier.
|
||||
/// @param message: Semaphore message.
|
||||
/// @param scope: Scope.
|
||||
/// @param proof: Zero-knowledge proof.
|
||||
event ProofValidated(
|
||||
uint256 indexed groupId,
|
||||
uint256 merkleTreeDepth,
|
||||
uint256 indexed merkleTreeRoot,
|
||||
uint256 nullifierHash,
|
||||
uint256 indexed externalNullifier,
|
||||
uint256 signal
|
||||
uint256 nullifier,
|
||||
uint256 message,
|
||||
uint256 indexed scope,
|
||||
uint256[8] proof
|
||||
);
|
||||
|
||||
/// @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 merkleTreeRoot: Root of the Merkle tree.
|
||||
/// @param signal: Semaphore signal.
|
||||
/// @param nullifierHash: Nullifier hash.
|
||||
/// @param externalNullifier: External nullifier.
|
||||
/// @param proof: Zero-knowledge proof.
|
||||
function verifyProof(
|
||||
uint256 groupId,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 signal,
|
||||
uint256 nullifierHash,
|
||||
uint256 externalNullifier,
|
||||
uint256[8] calldata proof
|
||||
) external;
|
||||
/// @dev See {SemaphoreGroups-_createGroup}.
|
||||
function createGroup(uint256 groupId, address admin) external;
|
||||
|
||||
/// @dev Creates a new group. Only the admin will be able to add or remove members.
|
||||
/// @dev It creates a group with a custom Merkle tree duration.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param depth: Depth of the tree.
|
||||
/// @param admin: Admin of the group.
|
||||
function createGroup(uint256 groupId, uint256 depth, address admin) external;
|
||||
/// @param merkleTreeDuration: Merkle tree duration.
|
||||
function createGroup(uint256 groupId, address admin, uint256 merkleTreeDuration) 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 admin: Admin of the group.
|
||||
/// @param merkleTreeRootDuration: Time before the validity of a root expires.
|
||||
function createGroup(uint256 groupId, uint256 depth, address admin, uint256 merkleTreeRootDuration) external;
|
||||
|
||||
/// @dev Updates the group admin.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param newAdmin: New admin of the group.
|
||||
/// @dev See {SemaphoreGroups-_updateGroupAdmin}.
|
||||
function updateGroupAdmin(uint256 groupId, address newAdmin) external;
|
||||
|
||||
/// @dev Updates the group Merkle tree duration.
|
||||
@@ -87,41 +62,57 @@ interface ISemaphore {
|
||||
/// @param newMerkleTreeDuration: New Merkle tree duration.
|
||||
function updateGroupMerkleTreeDuration(uint256 groupId, uint256 newMerkleTreeDuration) external;
|
||||
|
||||
/// @dev Adds a new member to an existing group.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param identityCommitment: New identity commitment.
|
||||
/// @dev See {SemaphoreGroups-_addMember}.
|
||||
function addMember(uint256 groupId, uint256 identityCommitment) external;
|
||||
|
||||
/// @dev Adds new members to an existing group.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param identityCommitments: New identity commitments.
|
||||
/// @dev See {SemaphoreGroups-_addMembers}.
|
||||
function addMembers(uint256 groupId, uint256[] calldata identityCommitments) external;
|
||||
|
||||
/// @dev Updates an identity commitment of an existing group. A proof of membership is
|
||||
/// needed to check if the node to be updated is part of the tree.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param identityCommitment: Existing identity commitment to be updated.
|
||||
/// @param newIdentityCommitment: New identity commitment.
|
||||
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
|
||||
/// @param proofPathIndices: Path of the proof of membership.
|
||||
/// @dev See {SemaphoreGroups-_updateMember}.
|
||||
function updateMember(
|
||||
uint256 groupId,
|
||||
uint256 identityCommitment,
|
||||
uint256 oldIdentityCommitment,
|
||||
uint256 newIdentityCommitment,
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
uint256[] calldata merkleProofSiblings
|
||||
) 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.
|
||||
/// @dev See {SemaphoreGroups-_removeMember}.
|
||||
function removeMember(uint256 groupId, uint256 identityCommitment, uint256[] calldata merkleProofSiblings) external;
|
||||
|
||||
/// @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 identityCommitment: Identity commitment to be removed.
|
||||
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
|
||||
/// @param proofPathIndices: Path of the proof of membership.
|
||||
function removeMember(
|
||||
/// @param merkleTreeDepth: Depth of the Merkle tree.
|
||||
/// @param merkleTreeRoot: Root of the Merkle tree.
|
||||
/// @param nullifier: Nullifier.
|
||||
/// @param message: Semaphore message.
|
||||
/// @param scope: Scope.
|
||||
/// @param proof: Zero-knowledge proof.
|
||||
function validateProof(
|
||||
uint256 groupId,
|
||||
uint256 identityCommitment,
|
||||
uint256[] calldata proofSiblings,
|
||||
uint8[] calldata proofPathIndices
|
||||
uint256 merkleTreeDepth,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 nullifier,
|
||||
uint256 message,
|
||||
uint256 scope,
|
||||
uint256[8] calldata proof
|
||||
) external;
|
||||
|
||||
/// @dev Verifies a zero-knowledge proof by returning true or false.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param merkleTreeDepth: Depth of the Merkle tree.
|
||||
/// @param merkleTreeRoot: Root of the Merkle tree.
|
||||
/// @param nullifier: Nullifier.
|
||||
/// @param message: Semaphore message.
|
||||
/// @param scope: Scope.
|
||||
/// @param proof: Zero-knowledge proof.
|
||||
function verifyProof(
|
||||
uint256 groupId,
|
||||
uint256 merkleTreeDepth,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 nullifier,
|
||||
uint256 message,
|
||||
uint256 scope,
|
||||
uint256[8] calldata proof
|
||||
) external view returns (bool);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user