mirror of
https://github.com/semaphore-protocol/semaphore.git
synced 2026-01-13 08:37:55 -05:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a995d7087 | ||
|
|
20fc3c58d7 | ||
|
|
7329ca6a48 | ||
|
|
0c9c0c9791 | ||
|
|
39a7e32143 | ||
|
|
87c27b9d03 | ||
|
|
4d07a1ede5 | ||
|
|
55fbd0f2ed | ||
|
|
6dca198995 | ||
|
|
5f80aab430 | ||
|
|
c30fbb8d1c | ||
|
|
be1014452e | ||
|
|
0036da93b1 | ||
|
|
c984adef0e | ||
|
|
b2d8667963 | ||
|
|
466be38e42 | ||
|
|
adcb8e085d | ||
|
|
e9a3770a39 | ||
|
|
d9a1387f2a | ||
|
|
a6710ad435 | ||
|
|
3d3a63a10d | ||
|
|
0daf5b7dae | ||
|
|
ef4b3dd4b2 | ||
|
|
0f2a13463a | ||
|
|
0a305c019d | ||
|
|
de8c7f20ca | ||
|
|
23d7fdff3a | ||
|
|
21b9965f57 | ||
|
|
023c8bae64 | ||
|
|
0d771eb9fa | ||
|
|
62f775737d | ||
|
|
12aacf24b3 | ||
|
|
b450dcec79 | ||
|
|
e909e1db99 | ||
|
|
92c9c9bcc8 | ||
|
|
5588256072 | ||
|
|
8cf04ddb98 | ||
|
|
ecca5a4ee9 | ||
|
|
0a5ebe60df |
@@ -20,6 +20,7 @@ circuits
|
||||
# production
|
||||
dist
|
||||
build
|
||||
docs
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"es6": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
|
||||
"extends": ["airbnb-base", "airbnb-typescript/base", "plugin:jest/recommended", "plugin:jest/style", "prettier"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "tsconfig.json"
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"project": ["./tsconfig.json", "./packages/**/tsconfig.json"]
|
||||
},
|
||||
"plugins": ["@typescript-eslint"]
|
||||
"plugins": ["@typescript-eslint", "jest"],
|
||||
"rules": {
|
||||
"no-underscore-dangle": "off",
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"no-bitwise": "off",
|
||||
"no-await-in-loop": "off",
|
||||
"no-restricted-syntax": "off",
|
||||
"no-console": ["warn", { "allow": ["info", "warn", "error"] }],
|
||||
"@typescript-eslint/lines-between-class-members": "off",
|
||||
"no-param-reassign": "off"
|
||||
}
|
||||
}
|
||||
|
||||
14
.github/ISSUE_TEMPLATE/---package.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/---package.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: "\U0001F4E6 Package"
|
||||
about: Propose a new Semaphore package
|
||||
title: ''
|
||||
labels: 'feature :rocket:'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the package you'd like**
|
||||
A clear and concise description of the type of package you have in mind.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the package here.
|
||||
50
.github/workflows/coverall.yml
vendored
50
.github/workflows/coverall.yml
vendored
@@ -1,50 +0,0 @@
|
||||
name: coverall
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
TREE_DEPTH: 20
|
||||
|
||||
jobs:
|
||||
coverall:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: YARN_CHECKSUM_BEHAVIOR=ignore yarn
|
||||
|
||||
- name: Download Snark artifacts
|
||||
run: yarn download:snark-artifacts
|
||||
|
||||
- name: Compile contracts
|
||||
run: yarn compile
|
||||
|
||||
- name: Test contracts with coverage
|
||||
run: yarn test:coverage
|
||||
|
||||
- uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,13 +1,12 @@
|
||||
name: style
|
||||
name: docs
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
style:
|
||||
gh-pages:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -32,13 +31,15 @@ jobs:
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: YARN_CHECKSUM_BEHAVIOR=ignore yarn
|
||||
run: yarn
|
||||
|
||||
- name: Run Prettier
|
||||
run: yarn prettier
|
||||
- name: Generate doc website
|
||||
run: yarn docs
|
||||
|
||||
- name: Run Eslint
|
||||
run: yarn lint
|
||||
|
||||
- name: Compile contracts
|
||||
run: yarn compile
|
||||
- name: Publish on Github Pages
|
||||
uses: crazy-max/ghaction-github-pages@v2.5.0
|
||||
with:
|
||||
build_dir: docs
|
||||
jekyll: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
103
.github/workflows/production.yml
vendored
Normal file
103
.github/workflows/production.yml
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
name: production
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
style:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Compile contracts
|
||||
run: yarn compile:contracts
|
||||
|
||||
- name: Build libraries
|
||||
run: yarn build:libraries
|
||||
|
||||
- name: Run Prettier
|
||||
run: yarn prettier
|
||||
|
||||
- name: Run Eslint
|
||||
run: yarn lint
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
type:
|
||||
- libraries
|
||||
- contracts
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Build libraries
|
||||
run: yarn build:libraries
|
||||
|
||||
- name: Test contracts and libraries
|
||||
run: yarn test:${{ matrix.type }}
|
||||
|
||||
- name: Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
flag-name: run-${{ matrix.type }}
|
||||
path-to-lcov: ./coverage/${{ matrix.type }}/lcov.info
|
||||
parallel: true
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
needs: test
|
||||
|
||||
steps:
|
||||
- name: Coveralls Finished
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
parallel-finished: true
|
||||
77
.github/workflows/pull-requests.yml
vendored
Normal file
77
.github/workflows/pull-requests.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
name: pull-requests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
style:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Compile contracts
|
||||
run: yarn compile:contracts
|
||||
|
||||
- name: Build libraries
|
||||
run: yarn build:libraries
|
||||
|
||||
- name: Run Prettier
|
||||
run: yarn prettier
|
||||
|
||||
- name: Run Eslint
|
||||
run: yarn lint
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Build libraries
|
||||
run: yarn build:libraries
|
||||
|
||||
- name: Test contracts and libraries
|
||||
run: yarn test
|
||||
47
.github/workflows/test.yml
vendored
47
.github/workflows/test.yml
vendored
@@ -1,47 +0,0 @@
|
||||
name: test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
TREE_DEPTH: 20
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: YARN_CHECKSUM_BEHAVIOR=ignore yarn
|
||||
|
||||
- name: Download Snark artifacts
|
||||
run: yarn download:snark-artifacts
|
||||
|
||||
- name: Compile contracts
|
||||
run: yarn compile
|
||||
|
||||
- name: Test contracts with coverage
|
||||
run: yarn test:coverage
|
||||
17
.gitignore
vendored
17
.gitignore
vendored
@@ -16,9 +16,14 @@ pids
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# IDE
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
coverage.json
|
||||
*.lcov
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
@@ -28,6 +33,10 @@ node_modules/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
.DS_Store
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
@@ -54,8 +63,9 @@ node_modules/
|
||||
# Production
|
||||
build
|
||||
dist
|
||||
deployed-contracts/undefined.json
|
||||
deployed-contracts/localhost.json
|
||||
docs/*
|
||||
!docs/CNAME
|
||||
!docs/index.html
|
||||
|
||||
# Hardhat
|
||||
artifacts
|
||||
@@ -72,3 +82,6 @@ cache
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
# Other
|
||||
snark-artifacts
|
||||
|
||||
@@ -18,11 +18,12 @@ types
|
||||
circuits
|
||||
|
||||
# contracts
|
||||
contracts/verifiers
|
||||
Verifier*.sol
|
||||
|
||||
# production
|
||||
dist
|
||||
build
|
||||
docs
|
||||
|
||||
# github
|
||||
.github/ISSUE_TEMPLATE
|
||||
@@ -35,3 +36,6 @@ build
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# other
|
||||
snark-artifacts
|
||||
|
||||
28
.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
vendored
Normal file
28
.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,4 +1,8 @@
|
||||
nodeLinker: node-modules
|
||||
checksumBehavior: update
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||
spec: "@yarnpkg/plugin-workspace-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.1.cjs
|
||||
|
||||
195
README.md
195
README.md
@@ -13,18 +13,18 @@
|
||||
<a href="https://github.com/semaphore-protocol" target="_blank">
|
||||
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/LICENSE">
|
||||
<a href="/LICENSE">
|
||||
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/actions?query=workflow%3Atest">
|
||||
<img alt="GitHub Workflow test" src="https://img.shields.io/github/workflow/status/semaphore-protocol/semaphore/test?label=test&style=flat-square&logo=github">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/actions?query=workflow%3Astyle">
|
||||
<img alt="GitHub Workflow style" src="https://img.shields.io/github/workflow/status/semaphore-protocol/semaphore/style?label=style&style=flat-square&logo=github">
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/actions?query=workflow%3Aproduction">
|
||||
<img alt="GitHub Workflow test" src="https://img.shields.io/github/workflow/status/semaphore-protocol/semaphore/production?label=test&style=flat-square&logo=github">
|
||||
</a>
|
||||
<a href="https://coveralls.io/github/semaphore-protocol/semaphore">
|
||||
<img alt="Coveralls" src="https://img.shields.io/coveralls/github/semaphore-protocol/semaphore?style=flat-square&logo=coveralls">
|
||||
</a>
|
||||
<a href="https://deepscan.io/dashboard#view=project&tid=16502&pid=22324&bid=657461">
|
||||
<img src="https://deepscan.io/api/teams/16502/projects/22324/branches/657461/badge/grade.svg" alt="DeepScan grade">
|
||||
</a>
|
||||
<a href="https://eslint.org/">
|
||||
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint">
|
||||
</a>
|
||||
@@ -57,12 +57,130 @@
|
||||
| Semaphore is a protocol, designed to be a simple and generic privacy layer for Ethereum DApps. Using zero knowledge, Ethereum users can prove their membership of a group and send signals such as votes or endorsements without revealing their original identity. |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
The core of the Semaphore protocol is in the [circuit logic](/circuits/scheme.png). However Semaphore also provides [Solidity contracts](/contracts) (NPM: `@semaphore-protocol/contracts`) and [JavaScript libraries](https://github.com/semaphore-protocol/semaphore.js) to make the steps for offchain proof creation and onchain verification easier. To learn more about Semaphore visit [semaphore.appliedzkp.org](https://semaphore.appliedzkp.org).
|
||||
The core of the Semaphore protocol is in the [circuit logic](/packages/circuits/scheme.png). However Semaphore also provides [Solidity contracts](/packages/contracts) (NPM: `@semaphore-protocol/contracts`) and JavaScript libraries to make the steps for offchain proof creation and onchain verification easier. To learn more about Semaphore visit [semaphore.appliedzkp.org](https://semaphore.appliedzkp.org).
|
||||
|
||||
You can find Semaphore V1 on [`version/1.0.0`](https://github.com/semaphore-protocol/semaphore/tree/version/1.0.0).
|
||||
|
||||
---
|
||||
|
||||
## 📦 Packages
|
||||
|
||||
<table>
|
||||
<th>Package</th>
|
||||
<th>Version</th>
|
||||
<th>Downloads</th>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/contracts">
|
||||
@semaphore-protocol/contracts
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/contracts">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/contracts.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/contracts">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/contracts.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/identity">
|
||||
@semaphore-protocol/identity
|
||||
</a>
|
||||
<a href="https://semaphore-protocol.github.io/semaphore/identity">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/identity">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/identity.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/identity">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/identity.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/group">
|
||||
@semaphore-protocol/group
|
||||
</a>
|
||||
<a href="https://semaphore-protocol.github.io/semaphore/group">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/group">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/group.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/group">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/group.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/proof">
|
||||
@semaphore-protocol/proof
|
||||
</a>
|
||||
<a href="https://semaphore-protocol.github.io/semaphore/proof">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/proof">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/proof.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/proof">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/proof.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/subgraph">
|
||||
@semaphore-protocol/subgraph
|
||||
</a>
|
||||
<a href="https://semaphore-protocol.github.io/semaphore/subgraph">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/subgraph">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/subgraph.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/subgraph">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/subgraph.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
|
||||
</table>
|
||||
|
||||
## 🛠 Install
|
||||
|
||||
Clone this repository:
|
||||
@@ -71,7 +189,7 @@ Clone this repository:
|
||||
git clone https://github.com/semaphore-protocol/semaphore.git
|
||||
```
|
||||
|
||||
and install the dependencies:
|
||||
And install the dependencies:
|
||||
|
||||
```bash
|
||||
cd semaphore && yarn
|
||||
@@ -85,7 +203,7 @@ Copy the `.env.example` file as `.env`:
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
and add your environment variables.
|
||||
And add your environment variables.
|
||||
|
||||
### Code quality and formatting
|
||||
|
||||
@@ -101,7 +219,7 @@ Run [Prettier](https://prettier.io/) to check formatting rules:
|
||||
yarn prettier
|
||||
```
|
||||
|
||||
or to automatically format the code:
|
||||
Or to automatically format the code:
|
||||
|
||||
```bash
|
||||
yarn prettier:write
|
||||
@@ -125,59 +243,46 @@ Download the Semaphore snark artifacts needed to generate and verify proofs:
|
||||
yarn download:snark-artifacts
|
||||
```
|
||||
|
||||
### Compile contracts
|
||||
|
||||
Compile the smart contracts with [Hardhat](https://hardhat.org/):
|
||||
|
||||
```bash
|
||||
yarn compile
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
Run [Jest](https://jestjs.io/) to test the JS libraries:
|
||||
|
||||
```bash
|
||||
yarn test:libraries
|
||||
```
|
||||
|
||||
Run [Mocha](https://mochajs.org/) to test the contracts:
|
||||
|
||||
```bash
|
||||
yarn test:contracts
|
||||
```
|
||||
|
||||
Or test everything with:
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
You can also generate a test coverage report:
|
||||
### Build libraries & compile contracts
|
||||
|
||||
Run [Rollup](https://www.rollupjs.org) to build all the packages:
|
||||
|
||||
```bash
|
||||
yarn test:coverage
|
||||
yarn build:libraries
|
||||
```
|
||||
|
||||
or a test gas report:
|
||||
Compile the smart contracts with [Hardhat](https://hardhat.org/):
|
||||
|
||||
```bash
|
||||
yarn test:report-gas
|
||||
yarn compile:contracts
|
||||
```
|
||||
|
||||
### Deploy contracts
|
||||
### Documentation (JS libraries)
|
||||
|
||||
Deploy a verifier contract with depth = 20:
|
||||
Run [TypeDoc](https://typedoc.org/) to generate a documentation website for each package:
|
||||
|
||||
```bash
|
||||
yarn deploy:verifier --depth 20
|
||||
yarn docs
|
||||
```
|
||||
|
||||
Deploy the `Semaphore.sol` contract with one verifier:
|
||||
|
||||
```bash
|
||||
yarn deploy:semaphore --verifiers '[{"merkleTreeDepth": 20, "contractAddress": "0x06bcD633988c1CE7Bd134DbE2C12119b6f3E4bD1"}]'
|
||||
```
|
||||
|
||||
Deploy all verifiers and Semaphore contract:
|
||||
|
||||
```bash
|
||||
yarn deploy:all
|
||||
```
|
||||
|
||||
If you want to deploy contracts in a specific network you can set up the `DEFAULT_NETWORK` variable in your `.env` file with the name of one of our supported networks (hardhat, localhost, goerli, arbitrum). Or you can specify it as option:
|
||||
|
||||
```bash
|
||||
yarn deploy:all --network goerli
|
||||
yarn deploy:all --network localhost
|
||||
```
|
||||
|
||||
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.
|
||||
The output will be placed on the `docs` folder.
|
||||
|
||||
3
babel.config.json
Normal file
3
babel.config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": [["@babel/preset-env", { "targets": { "node": "current" } }], "@babel/preset-typescript"]
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "Verifier16",
|
||||
"address": "0x6143ECd9Fd1A00EDe1046d456f8aab53a7D71609"
|
||||
},
|
||||
{
|
||||
"name": "Verifier17",
|
||||
"address": "0xAc12fFFE354D6446eb50dd33E683B78FED73Fb02"
|
||||
},
|
||||
{
|
||||
"name": "Verifier18",
|
||||
"address": "0x610aeF0F2da3CD1C8bDefe4BDB434Ee146E0C701"
|
||||
},
|
||||
{
|
||||
"name": "Verifier19",
|
||||
"address": "0x5477725177035bbC9d70443eb921D29749D6FCb4"
|
||||
},
|
||||
{
|
||||
"name": "Verifier20",
|
||||
"address": "0x3fB2C0988a37b76e760c44e6516aF720935f3136"
|
||||
},
|
||||
{
|
||||
"name": "Verifier21",
|
||||
"address": "0xDc8f6B8A42836d4566256f4c6C53131DFD127DF8"
|
||||
},
|
||||
{
|
||||
"name": "Verifier22",
|
||||
"address": "0x6962b5e706be5278eeCb01c286b50A48484632f2"
|
||||
},
|
||||
{
|
||||
"name": "Verifier23",
|
||||
"address": "0x41e4796Bd89B4BF04013b559c93fC32E9a2BdF6B"
|
||||
},
|
||||
{
|
||||
"name": "Verifier24",
|
||||
"address": "0xD528B1D1408ab3583af4694F92b0aFEbE33d5b60"
|
||||
},
|
||||
{
|
||||
"name": "Verifier25",
|
||||
"address": "0x1683a27EF9c10c5286dB56412E1272cD0Ca733e7"
|
||||
},
|
||||
{
|
||||
"name": "Verifier26",
|
||||
"address": "0x78194bB665d1E33b97eE45B1A755c15717E94C00"
|
||||
},
|
||||
{
|
||||
"name": "Verifier27",
|
||||
"address": "0x997Dac00E6701Ef7F3518280E5a9922801126E42"
|
||||
},
|
||||
{
|
||||
"name": "Verifier28",
|
||||
"address": "0xDd3C7f4cBA2467aE41c0F614A3c3E24bC80268c6"
|
||||
},
|
||||
{
|
||||
"name": "Verifier29",
|
||||
"address": "0xe53eF12093933D5df5691EAbA3821bD1c1EB60Cd"
|
||||
},
|
||||
{
|
||||
"name": "Verifier30",
|
||||
"address": "0x7FeA07c536ABBB0E7FB3c833376EE4EaDc21340e"
|
||||
},
|
||||
{
|
||||
"name": "Verifier31",
|
||||
"address": "0xe4539a592df18936202480FBe77E47DE012F2178"
|
||||
},
|
||||
{
|
||||
"name": "Verifier32",
|
||||
"address": "0x98c90845A7870e215cBd7265DDC653E6c07032F4"
|
||||
},
|
||||
{
|
||||
"name": "Semaphore",
|
||||
"address": "0x49281E30F17A30808a6ce538f979d539747e6707"
|
||||
}
|
||||
]
|
||||
@@ -1,74 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "Verifier16",
|
||||
"address": "0xA5253ba39381Aa99c4C2C5A4D5C2deC036d06629"
|
||||
},
|
||||
{
|
||||
"name": "Verifier17",
|
||||
"address": "0xe0418A5f8fBF051D6cbc41Ff29855Dd2a02201Ab"
|
||||
},
|
||||
{
|
||||
"name": "Verifier18",
|
||||
"address": "0x7CdB3336d7d7c55Bce0FB1508594C54521656797"
|
||||
},
|
||||
{
|
||||
"name": "Verifier19",
|
||||
"address": "0xbd870921d8A5398a3314C950d1fc63b8C3AB190B"
|
||||
},
|
||||
{
|
||||
"name": "Verifier20",
|
||||
"address": "0x2a96c5696F85e3d2aa918496806B5c5a4D93E099"
|
||||
},
|
||||
{
|
||||
"name": "Verifier21",
|
||||
"address": "0x5Ec7d851a52A2a25CEc528F42a7ACA8EcF4667Cd"
|
||||
},
|
||||
{
|
||||
"name": "Verifier22",
|
||||
"address": "0x919d3d9c05FA7411e334deA5a763354fC7B6aA5b"
|
||||
},
|
||||
{
|
||||
"name": "Verifier23",
|
||||
"address": "0x63917b00a6dA7865bEfdd107AfC83CC2e6BDE552"
|
||||
},
|
||||
{
|
||||
"name": "Verifier24",
|
||||
"address": "0xd05CAd7d940114c1419098EE3cEA0776ab510E7D"
|
||||
},
|
||||
{
|
||||
"name": "Verifier25",
|
||||
"address": "0x6D9862e6140D94E932d94c8BcE74a0BDD0ea5ACb"
|
||||
},
|
||||
{
|
||||
"name": "Verifier26",
|
||||
"address": "0x8c29e0b77e32f704F03eeCE01c041192A5EB6c77"
|
||||
},
|
||||
{
|
||||
"name": "Verifier27",
|
||||
"address": "0x066cC22f8CA2A8D90D7Ff77D8a10A27e629c9c4C"
|
||||
},
|
||||
{
|
||||
"name": "Verifier28",
|
||||
"address": "0x698F9507f504E2BD238be7da56E8D9fee60C6D15"
|
||||
},
|
||||
{
|
||||
"name": "Verifier29",
|
||||
"address": "0xbBfC2E201C3c3c6F50063c3Edb4746c6Fcb36346"
|
||||
},
|
||||
{
|
||||
"name": "Verifier30",
|
||||
"address": "0x06bcD633988c1CE7Bd134DbE2C12119b6f3E4bD1"
|
||||
},
|
||||
{
|
||||
"name": "Verifier31",
|
||||
"address": "0x133b69Ce47BF20C49368354914DF47519Ca6cCFE"
|
||||
},
|
||||
{
|
||||
"name": "Verifier32",
|
||||
"address": "0xe2978F79cb4AF62e5C990EE5c7E12fb22ee22e2D"
|
||||
},
|
||||
{
|
||||
"name": "Semaphore",
|
||||
"address": "0xd688189016277e1a6aE5228ef6894C14585A42D3"
|
||||
}
|
||||
]
|
||||
123
docs/index.html
Normal file
123
docs/index.html
Normal file
@@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html style="height: 100%">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="IE=edge" />
|
||||
<title>Semaphore packages</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="A monorepo of Semaphore packages."
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||
/>
|
||||
</head>
|
||||
<body
|
||||
style="
|
||||
margin: 0;
|
||||
background-color: #EAF0F4;
|
||||
color: #000;
|
||||
height: 100%;
|
||||
font-family: 'Courier New', monospace;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
padding: 0 20px;
|
||||
text-align: center;
|
||||
"
|
||||
>
|
||||
<div style="display: flex">
|
||||
<span style="margin-right: 5px">
|
||||
<img width="40" src="https://raw.githubusercontent.com/semaphore-protocol/website/main/static/img/semaphore-icon.svg">
|
||||
</span>
|
||||
<h1 style="margin: 0; font-size: 40px">Semaphore packages</h1>
|
||||
</div>
|
||||
<p style="max-width: 500px">
|
||||
A monorepo of Semaphore packages.
|
||||
</p>
|
||||
<ul style="list-style-type: none; padding: 0; margin: 0; margin-top: 10px"></ul>
|
||||
</div>
|
||||
<footer
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 15px 20px;
|
||||
background-color: #EAF0F4;
|
||||
"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 900px;
|
||||
"
|
||||
>
|
||||
<p style="margin: 0; font-size: 16px">
|
||||
Copyright © 2022 Ethereum Foundation
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
style="margin-right: 15px; text-decoration: none"
|
||||
target="_blank"
|
||||
href="https://github.com/semaphore-protocol/semaphore"
|
||||
>
|
||||
<i
|
||||
class="fa fa-github"
|
||||
style="font-size: 24px; color: #000"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
<script>
|
||||
const url =
|
||||
"https://api.github.com/repos/semaphore-protocol/semaphore/contents?ref=gh-pages"
|
||||
|
||||
function insertLinks(packages) {
|
||||
const [element] = window.document.getElementsByTagName("ul")
|
||||
let html = ""
|
||||
|
||||
for (const package of packages) {
|
||||
html += `<li style="display: flex; align-items: center; margin-bottom: 8px">
|
||||
<a style="margin-right: 15px" target="_blank" href="https://github.com/semaphore-protocol/semaphore/tree/main/packages/${package}">
|
||||
<i class="fa fa-github" style="font-size: 24px; color: #000"></i>
|
||||
</a>
|
||||
<a style="color: #000; text-decoration: none; font-size: 16px"
|
||||
onmouseover="this.style.color='#404A4E';"
|
||||
onmouseout="this.style.color='#000';"
|
||||
target="_blank" href="https://semaphore-protocol.github.io/semaphore/${package}">
|
||||
@semaphore-protocol/${package} >
|
||||
</a></li>`
|
||||
}
|
||||
|
||||
element.innerHTML = html
|
||||
}
|
||||
|
||||
fetch(url)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
const ignore = [".nojekyll", "index.html", "CNAME"]
|
||||
const packages = data
|
||||
.map((c) => c.name)
|
||||
.filter((name) => !ignore.includes(name))
|
||||
|
||||
localStorage.setItem("packages", JSON.stringify(packages))
|
||||
|
||||
insertLinks(packages)
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
29
jest.config.ts
Normal file
29
jest.config.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import fs from "fs"
|
||||
import type { Config } from "@jest/types"
|
||||
|
||||
const projects: any = fs
|
||||
.readdirSync("./packages", { withFileTypes: true })
|
||||
.filter((directory) => directory.isDirectory())
|
||||
.map(({ name }) => ({
|
||||
rootDir: `packages/${name}`,
|
||||
displayName: name,
|
||||
setupFiles: ["dotenv/config"],
|
||||
moduleNameMapper: {
|
||||
"@semaphore-protocol/(.*)": "<rootDir>/../$1/src/index.ts" // Interdependency packages.
|
||||
}
|
||||
}))
|
||||
|
||||
export default async (): Promise<Config.InitialOptions> => ({
|
||||
projects,
|
||||
verbose: true,
|
||||
coverageDirectory: "./coverage/libraries",
|
||||
collectCoverageFrom: ["<rootDir>/src/**/*.ts", "!<rootDir>/src/**/index.ts", "!<rootDir>/src/**/*.d.ts"],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 90,
|
||||
functions: 95,
|
||||
lines: 95,
|
||||
statements: 95
|
||||
}
|
||||
}
|
||||
})
|
||||
129
package.json
129
package.json
@@ -1,104 +1,81 @@
|
||||
{
|
||||
"name": "semaphore",
|
||||
"name": "semaphore-protocol",
|
||||
"description": "A zero-knowledge protocol for anonymous signalling on Ethereum.",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore.git#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/semaphore-protocol/semaphore.git.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/semaphore-protocol/semaphore.git/issues"
|
||||
},
|
||||
"repository": "git@github.com:semaphore-protocol/semaphore.git",
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore",
|
||||
"bugs": "https://github.com/semaphore-protocol/semaphore/issues",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "hardhat node",
|
||||
"compile": "hardhat compile",
|
||||
"download:snark-artifacts": "hardhat run scripts/download-snark-artifacts.ts",
|
||||
"deploy:all": "hardhat run scripts/deploy-all.ts",
|
||||
"deploy:verifier": "hardhat deploy:verifier",
|
||||
"deploy:semaphore": "hardhat deploy:semaphore",
|
||||
"deploy:semaphore-voting": "hardhat deploy:semaphore-voting",
|
||||
"deploy:semaphore-whistleblowing": "hardhat deploy:semaphore-whistleblowing",
|
||||
"test": "hardhat test",
|
||||
"test:report-gas": "REPORT_GAS=true hardhat test",
|
||||
"test:coverage": "hardhat coverage",
|
||||
"typechain": "hardhat typechain",
|
||||
"lint": "yarn lint:sol && yarn lint:ts",
|
||||
"lint:ts": "eslint . --ext .js,.ts",
|
||||
"lint:sol": "solhint 'contracts/**/*.sol'",
|
||||
"build:libraries": "yarn workspaces foreach run build",
|
||||
"compile:contracts": "yarn workspace contracts compile",
|
||||
"download:snark-artifacts": "ts-node scripts/download-snark-artifacts.ts",
|
||||
"test": "yarn test:libraries && yarn test:contracts",
|
||||
"test:libraries": "jest --coverage",
|
||||
"test:contracts": "yarn workspace contracts test:coverage",
|
||||
"lint": "eslint . --ext .js,.ts && yarn workspace contracts lint",
|
||||
"prettier": "prettier -c .",
|
||||
"prettier:write": "prettier -w .",
|
||||
"docs": "yarn workspaces foreach run docs",
|
||||
"commit": "cz",
|
||||
"precommit": "lint-staged"
|
||||
"precommit": "lint-staged",
|
||||
"postinstall": "yarn download:snark-artifacts"
|
||||
},
|
||||
"keywords": [
|
||||
"ethereum",
|
||||
"semaphore",
|
||||
"solidity",
|
||||
"circom",
|
||||
"javascript",
|
||||
"typescript",
|
||||
"zero-knowledge",
|
||||
"zk-snarks",
|
||||
"zero-knowledge-proofs",
|
||||
"proof-of-membership",
|
||||
"monorepo"
|
||||
],
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"packageManager": "yarn@3.2.1",
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^16.1.0",
|
||||
"@babel/core": "^7.16.7",
|
||||
"@babel/preset-env": "^7.16.8",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@commitlint/cli": "^16.0.2",
|
||||
"@commitlint/config-conventional": "^16.0.0",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.6",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.1.0",
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.3",
|
||||
"@semaphore-protocol/group": "2.2.0",
|
||||
"@semaphore-protocol/identity": "2.0.0",
|
||||
"@semaphore-protocol/proof": "2.3.1",
|
||||
"@typechain/ethers-v5": "^10.0.0",
|
||||
"@typechain/hardhat": "^6.0.0",
|
||||
"@types/chai": "^4.3.0",
|
||||
"@rollup/plugin-typescript": "^8.3.0",
|
||||
"@types/download": "^8.0.1",
|
||||
"@types/mocha": "^9.1.0",
|
||||
"@types/node": "^17.0.12",
|
||||
"@types/glob": "^7.2.0",
|
||||
"@types/jest": "^27.4.0",
|
||||
"@types/node": "^17.0.9",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.1",
|
||||
"@typescript-eslint/parser": "^5.10.1",
|
||||
"chai": "^4.3.5",
|
||||
"circomlib": "^2.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
||||
"@typescript-eslint/parser": "^5.9.1",
|
||||
"babel-jest": "^27.4.6",
|
||||
"circomlibjs": "^0.0.8",
|
||||
"commitizen": "^4.2.4",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"dotenv": "^14.3.2",
|
||||
"download": "^8.0.0",
|
||||
"eslint": "^8.7.0",
|
||||
"dotenv": "^16.0.2",
|
||||
"eslint": "^8.2.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"ethereum-waffle": "^3.4.4",
|
||||
"ethers": "^5.6.8",
|
||||
"hardhat": "^2.9.7",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
"js-logger": "^1.6.1",
|
||||
"lint-staged": "^12.3.2",
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"eslint-plugin-jest": "^25.7.0",
|
||||
"jest": "^27.4.1",
|
||||
"jest-config": "^27.4.7",
|
||||
"lint-staged": "^12.1.7",
|
||||
"prettier": "^2.5.1",
|
||||
"prettier-plugin-solidity": "^1.0.0-beta.19",
|
||||
"rimraf": "^3.0.2",
|
||||
"snarkjs": "^0.4.13",
|
||||
"solhint": "^3.3.6",
|
||||
"solhint-plugin-prettier": "^0.0.5",
|
||||
"solidity-coverage": "^0.7.21",
|
||||
"rollup": "^2.64.0",
|
||||
"ts-node": "^10.4.0",
|
||||
"typechain": "^8.0.0",
|
||||
"typescript": "^4.5.5"
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"config": {
|
||||
"solidity": {
|
||||
"version": "0.8.4"
|
||||
},
|
||||
"paths": {
|
||||
"contracts": "./contracts",
|
||||
"circuit": "./circuit",
|
||||
"tests": "./test",
|
||||
"cache": "./cache",
|
||||
"snarkjs-templates": "./snarkjs-templates",
|
||||
"build": {
|
||||
"snark-artifacts": "./build/snark-artifacts",
|
||||
"contracts": "./build/contracts",
|
||||
"typechain": "./build/typechain"
|
||||
}
|
||||
},
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "4.4.2",
|
||||
"@zk-kit/incremental-merkle-tree.sol": "1.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
7
packages/circuits/package.json
Normal file
7
packages/circuits/package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "circuits",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"circomlib": "^2.0.2"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,4 +1,5 @@
|
||||
module.exports = {
|
||||
istanbulFolder: "../../coverage/contracts",
|
||||
skipFiles: [
|
||||
"verifiers/Verifier16.sol",
|
||||
"verifiers/Verifier17.sol",
|
||||
1
packages/contracts/README.md
Symbolic link
1
packages/contracts/README.md
Symbolic link
@@ -0,0 +1 @@
|
||||
contracts/README.md
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/semaphore-protocol">
|
||||
<img src="https://img.shields.io/badge/project-semaphore-blue.svg?style=flat-square">
|
||||
<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">
|
||||
@@ -59,3 +59,62 @@ or yarn:
|
||||
```bash
|
||||
yarn add @semaphore-protocol/contracts
|
||||
```
|
||||
|
||||
## 📜 Usage
|
||||
|
||||
### Compile contracts
|
||||
|
||||
Compile the smart contracts with [Hardhat](https://hardhat.org/):
|
||||
|
||||
```bash
|
||||
yarn compile
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
Run [Mocha](https://mochajs.org/) to test the contracts:
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
You can also generate a test coverage report:
|
||||
|
||||
```bash
|
||||
yarn test:coverage
|
||||
```
|
||||
|
||||
Or a test gas report:
|
||||
|
||||
```bash
|
||||
yarn test:report-gas
|
||||
```
|
||||
|
||||
### Deploy contracts
|
||||
|
||||
Deploy a verifier contract with depth = 20:
|
||||
|
||||
```bash
|
||||
yarn deploy:verifier --depth 20
|
||||
```
|
||||
|
||||
Deploy the `Semaphore.sol` contract with one verifier:
|
||||
|
||||
```bash
|
||||
yarn deploy:semaphore --verifiers '[{"merkleTreeDepth": 20, "contractAddress": "0x06bcD633988c1CE7Bd134DbE2C12119b6f3E4bD1"}]'
|
||||
```
|
||||
|
||||
Deploy all verifiers and Semaphore contract:
|
||||
|
||||
```bash
|
||||
yarn deploy:all
|
||||
```
|
||||
|
||||
If you want to deploy contracts in a specific network you can set up the `DEFAULT_NETWORK` variable in your `.env` file with the name of one of our supported networks (hardhat, localhost, goerli, arbitrum). Or you can specify it as option:
|
||||
|
||||
```bash
|
||||
yarn deploy:all --network goerli
|
||||
yarn deploy:all --network localhost
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -11,16 +11,13 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
/// @dev Gets a tree depth and returns its verifier address.
|
||||
mapping(uint256 => IVerifier) public verifiers;
|
||||
|
||||
/// @dev Gets a group id and returns the group admin address.
|
||||
mapping(uint256 => address) public groupAdmins;
|
||||
|
||||
/// @dev Gets a group id and returns data to check if a Merkle root is expired.
|
||||
mapping(uint256 => MerkleTreeExpiry) public merkleTreeExpiries;
|
||||
/// @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 (groupAdmins[groupId] != _msgSender()) {
|
||||
if (groups[groupId].admin != _msgSender()) {
|
||||
revert Semaphore__CallerIsNotTheGroupAdmin();
|
||||
}
|
||||
_;
|
||||
@@ -56,8 +53,8 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
) external override onlySupportedMerkleTreeDepth(merkleTreeDepth) {
|
||||
_createGroup(groupId, merkleTreeDepth, zeroValue);
|
||||
|
||||
groupAdmins[groupId] = admin;
|
||||
merkleTreeExpiries[groupId].rootDuration = 1 hours;
|
||||
groups[groupId].admin = admin;
|
||||
groups[groupId].merkleRootDuration = 1 hours;
|
||||
|
||||
emit GroupAdminUpdated(groupId, address(0), admin);
|
||||
}
|
||||
@@ -72,15 +69,15 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
) external override onlySupportedMerkleTreeDepth(merkleTreeDepth) {
|
||||
_createGroup(groupId, merkleTreeDepth, zeroValue);
|
||||
|
||||
groupAdmins[groupId] = admin;
|
||||
merkleTreeExpiries[groupId].rootDuration = merkleTreeRootDuration;
|
||||
groups[groupId].admin = admin;
|
||||
groups[groupId].merkleRootDuration = merkleTreeRootDuration;
|
||||
|
||||
emit GroupAdminUpdated(groupId, address(0), admin);
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-updateGroupAdmin}.
|
||||
function updateGroupAdmin(uint256 groupId, address newAdmin) external override onlyGroupAdmin(groupId) {
|
||||
groupAdmins[groupId] = newAdmin;
|
||||
groups[groupId].admin = newAdmin;
|
||||
|
||||
emit GroupAdminUpdated(groupId, _msgSender(), newAdmin);
|
||||
}
|
||||
@@ -91,7 +88,7 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
|
||||
merkleTreeExpiries[groupId].rootCreationDates[merkleTreeRoot] = block.timestamp;
|
||||
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-addMembers}.
|
||||
@@ -110,7 +107,7 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
|
||||
merkleTreeExpiries[groupId].rootCreationDates[merkleTreeRoot] = block.timestamp;
|
||||
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphore-updateMember}.
|
||||
@@ -150,26 +147,30 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
||||
}
|
||||
|
||||
if (merkleTreeRoot != currentMerkleTreeRoot) {
|
||||
uint256 rootCreationDate = merkleTreeExpiries[groupId].rootCreationDates[merkleTreeRoot];
|
||||
uint256 rootDuration = merkleTreeExpiries[groupId].rootDuration;
|
||||
uint256 merkleRootCreationDate = groups[groupId].merkleRootCreationDates[merkleTreeRoot];
|
||||
uint256 merkleRootDuration = groups[groupId].merkleRootDuration;
|
||||
|
||||
if (rootCreationDate == 0) {
|
||||
if (merkleRootCreationDate == 0) {
|
||||
revert Semaphore__MerkleTreeRootIsNotPartOfTheGroup();
|
||||
}
|
||||
|
||||
if (block.timestamp > rootCreationDate + rootDuration) {
|
||||
if (block.timestamp > merkleRootCreationDate + merkleRootDuration) {
|
||||
revert Semaphore__MerkleTreeRootIsExpired();
|
||||
}
|
||||
}
|
||||
|
||||
if (groups[groupId].nullifierHashes[nullifierHash]) {
|
||||
revert Semaphore__YouAreUsingTheSameNillifierTwice();
|
||||
}
|
||||
|
||||
uint256 merkleTreeDepth = getMerkleTreeDepth(groupId);
|
||||
|
||||
IVerifier verifier = verifiers[merkleTreeDepth];
|
||||
|
||||
_verifyProof(signal, merkleTreeRoot, nullifierHash, externalNullifier, proof, verifier);
|
||||
|
||||
_saveNullifierHash(nullifierHash);
|
||||
groups[groupId].nullifierHashes[nullifierHash] = true;
|
||||
|
||||
emit ProofVerified(groupId, signal);
|
||||
emit ProofVerified(groupId, merkleTreeRoot, nullifierHash, externalNullifier, signal);
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,6 @@ import "../interfaces/IVerifier.sol";
|
||||
/// nullifier to prevent double-signaling. External nullifier and Merkle trees (i.e. groups) must be
|
||||
/// managed externally.
|
||||
contract SemaphoreCore is ISemaphoreCore {
|
||||
/// @dev Gets a nullifier hash and returns true or false.
|
||||
/// It is used to prevent double-signaling.
|
||||
mapping(uint256 => bool) internal nullifierHashes;
|
||||
|
||||
/// @dev Asserts that no nullifier already exists and if the zero-knowledge proof is valid.
|
||||
/// Otherwise it reverts.
|
||||
/// @param signal: Semaphore signal.
|
||||
@@ -30,10 +26,6 @@ contract SemaphoreCore is ISemaphoreCore {
|
||||
uint256[8] calldata proof,
|
||||
IVerifier verifier
|
||||
) internal view {
|
||||
if (nullifierHashes[nullifierHash]) {
|
||||
revert Semaphore__YouAreUsingTheSameNillifierTwice();
|
||||
}
|
||||
|
||||
uint256 signalHash = _hashSignal(signal);
|
||||
|
||||
verifier.verifyProof(
|
||||
@@ -44,14 +36,6 @@ contract SemaphoreCore is ISemaphoreCore {
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Stores the nullifier hash to prevent double-signaling.
|
||||
/// Attention! Remember to call it when you verify a proof if you
|
||||
/// need to prevent double-signaling.
|
||||
/// @param nullifierHash: Semaphore nullifier hash.
|
||||
function _saveNullifierHash(uint256 nullifierHash) internal {
|
||||
nullifierHashes[nullifierHash] = true;
|
||||
}
|
||||
|
||||
/// @dev Creates a keccak256 hash of the signal.
|
||||
/// @param signal: Semaphore signal.
|
||||
/// @return Hash of the signal.
|
||||
@@ -12,8 +12,8 @@ import "@openzeppelin/contracts/utils/Context.sol";
|
||||
abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
using IncrementalBinaryTree for IncrementalTreeData;
|
||||
|
||||
/// @dev Gets a group id and returns the group/tree data.
|
||||
mapping(uint256 => IncrementalTreeData) internal groups;
|
||||
/// @dev Gets a group id and returns the tree data.
|
||||
mapping(uint256 => IncrementalTreeData) internal merkleTree;
|
||||
|
||||
/// @dev Creates a new group by initializing the associated tree.
|
||||
/// @param groupId: Id of the group.
|
||||
@@ -32,7 +32,7 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
revert Semaphore__GroupAlreadyExists();
|
||||
}
|
||||
|
||||
groups[groupId].init(merkleTreeDepth, zeroValue);
|
||||
merkleTree[groupId].init(merkleTreeDepth, zeroValue);
|
||||
|
||||
emit GroupCreated(groupId, merkleTreeDepth, zeroValue);
|
||||
}
|
||||
@@ -45,7 +45,7 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
revert Semaphore__GroupDoesNotExist();
|
||||
}
|
||||
|
||||
groups[groupId].insert(identityCommitment);
|
||||
merkleTree[groupId].insert(identityCommitment);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
uint256 index = getNumberOfMerkleTreeLeaves(groupId) - 1;
|
||||
@@ -71,7 +71,7 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
revert Semaphore__GroupDoesNotExist();
|
||||
}
|
||||
|
||||
groups[groupId].update(identityCommitment, newIdentityCommitment, proofSiblings, proofPathIndices);
|
||||
merkleTree[groupId].update(identityCommitment, newIdentityCommitment, proofSiblings, proofPathIndices);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
uint256 index = proofPathIndicesToMemberIndex(proofPathIndices);
|
||||
@@ -95,7 +95,7 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
revert Semaphore__GroupDoesNotExist();
|
||||
}
|
||||
|
||||
groups[groupId].remove(identityCommitment, proofSiblings, proofPathIndices);
|
||||
merkleTree[groupId].remove(identityCommitment, proofSiblings, proofPathIndices);
|
||||
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
||||
uint256 index = proofPathIndicesToMemberIndex(proofPathIndices);
|
||||
@@ -105,17 +105,17 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
||||
|
||||
/// @dev See {ISemaphoreGroups-getMerkleTreeRoot}.
|
||||
function getMerkleTreeRoot(uint256 groupId) public view virtual override returns (uint256) {
|
||||
return groups[groupId].root;
|
||||
return merkleTree[groupId].root;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreGroups-getMerkleTreeDepth}.
|
||||
function getMerkleTreeDepth(uint256 groupId) public view virtual override returns (uint256) {
|
||||
return groups[groupId].depth;
|
||||
return merkleTree[groupId].depth;
|
||||
}
|
||||
|
||||
/// @dev See {ISemaphoreGroups-getNumberOfMerkleTreeLeaves}.
|
||||
function getNumberOfMerkleTreeLeaves(uint256 groupId) public view virtual override returns (uint256) {
|
||||
return groups[groupId].numberOfLeaves;
|
||||
return merkleTree[groupId].numberOfLeaves;
|
||||
}
|
||||
|
||||
/// @dev Converts the path indices of a Merkle proof to the identity commitment index in the tree.
|
||||
@@ -14,6 +14,10 @@ contract SemaphoreVoting is ISemaphoreVoting, SemaphoreCore, SemaphoreGroups {
|
||||
/// @dev Gets a poll id and returns the poll data.
|
||||
mapping(uint256 => Poll) internal polls;
|
||||
|
||||
/// @dev Gets a nullifier hash and returns true or false.
|
||||
/// It is used to prevent double-voting.
|
||||
mapping(uint256 => bool) internal nullifierHashes;
|
||||
|
||||
/// @dev Initializes the Semaphore verifiers used to verify the user's ZK proofs.
|
||||
/// @param _verifiers: List of Semaphore verifiers (address and related Merkle tree depth).
|
||||
constructor(Verifier[] memory _verifiers) {
|
||||
@@ -90,6 +94,10 @@ contract SemaphoreVoting is ISemaphoreVoting, SemaphoreCore, SemaphoreGroups {
|
||||
revert Semaphore__PollIsNotOngoing();
|
||||
}
|
||||
|
||||
if (nullifierHashes[nullifierHash]) {
|
||||
revert Semaphore__YouAreUsingTheSameNillifierTwice();
|
||||
}
|
||||
|
||||
uint256 merkleTreeDepth = getMerkleTreeDepth(pollId);
|
||||
uint256 merkleTreeRoot = getMerkleTreeRoot(pollId);
|
||||
|
||||
@@ -97,8 +105,7 @@ contract SemaphoreVoting is ISemaphoreVoting, SemaphoreCore, SemaphoreGroups {
|
||||
|
||||
_verifyProof(vote, merkleTreeRoot, nullifierHash, pollId, proof, verifier);
|
||||
|
||||
// Prevent double-voting (nullifierHash = hash(pollId + identityNullifier)).
|
||||
_saveNullifierHash(nullifierHash);
|
||||
nullifierHashes[nullifierHash] = true;
|
||||
|
||||
emit VoteAdded(pollId, vote);
|
||||
}
|
||||
@@ -8,17 +8,19 @@ interface ISemaphore {
|
||||
error Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
error Semaphore__MerkleTreeRootIsExpired();
|
||||
error Semaphore__MerkleTreeRootIsNotPartOfTheGroup();
|
||||
error Semaphore__YouAreUsingTheSameNillifierTwice();
|
||||
|
||||
struct Verifier {
|
||||
address contractAddress;
|
||||
uint256 merkleTreeDepth;
|
||||
}
|
||||
|
||||
/// It defines all the parameters needed to check whether a
|
||||
/// zero-knowledge proof generated with a certain Merkle tree is still valid.
|
||||
struct MerkleTreeExpiry {
|
||||
uint256 rootDuration;
|
||||
mapping(uint256 => uint256) rootCreationDates;
|
||||
/// It defines all the group parameters, in addition to those in the Merkle tree.
|
||||
struct Group {
|
||||
address admin;
|
||||
uint256 merkleRootDuration;
|
||||
mapping(uint256 => uint256) merkleRootCreationDates;
|
||||
mapping(uint256 => bool) nullifierHashes;
|
||||
}
|
||||
|
||||
/// @dev Emitted when an admin is assigned to a group.
|
||||
@@ -29,8 +31,17 @@ interface ISemaphore {
|
||||
|
||||
/// @dev Emitted when a Semaphore proof is verified.
|
||||
/// @param groupId: Id of the group.
|
||||
/// @param merkleTreeRoot: Root of the Merkle tree.
|
||||
/// @param externalNullifier: External nullifier.
|
||||
/// @param nullifierHash: Nullifier hash.
|
||||
/// @param signal: Semaphore signal.
|
||||
event ProofVerified(uint256 indexed groupId, bytes32 signal);
|
||||
event ProofVerified(
|
||||
uint256 indexed groupId,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 externalNullifier,
|
||||
uint256 nullifierHash,
|
||||
bytes32 signal
|
||||
);
|
||||
|
||||
/// @dev Saves the nullifier hash to avoid double signaling and emits an event
|
||||
/// if the zero-knowledge proof is valid.
|
||||
@@ -4,8 +4,6 @@ pragma solidity 0.8.4;
|
||||
/// @title SemaphoreCore interface.
|
||||
/// @dev Interface of SemaphoreCore contract.
|
||||
interface ISemaphoreCore {
|
||||
error Semaphore__YouAreUsingTheSameNillifierTwice();
|
||||
|
||||
/// @notice Emitted when a proof is verified correctly and a new nullifier hash is added.
|
||||
/// @param nullifierHash: Hash of external and identity nullifiers.
|
||||
event NullifierHashAdded(uint256 nullifierHash);
|
||||
@@ -8,6 +8,7 @@ interface ISemaphoreVoting {
|
||||
error Semaphore__MerkleTreeDepthIsNotSupported();
|
||||
error Semaphore__PollHasAlreadyBeenStarted();
|
||||
error Semaphore__PollIsNotOngoing();
|
||||
error Semaphore__YouAreUsingTheSameNillifierTwice();
|
||||
|
||||
enum PollState {
|
||||
Created,
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/contracts",
|
||||
"version": "2.2.0",
|
||||
"version": "2.5.0",
|
||||
"description": "Semaphore contracts to manage groups and broadcast anonymous signals.",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
@@ -20,11 +20,8 @@
|
||||
"circom",
|
||||
"proof-of-membership"
|
||||
],
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore.git#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/semaphore-protocol/semaphore.git.git"
|
||||
},
|
||||
"repository": "https://github.com/semaphore-protocol/semaphore",
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore/tree/main/packages/contracts",
|
||||
"bugs": {
|
||||
"url": "https://github.com/semaphore-protocol/semaphore.git/issues"
|
||||
},
|
||||
24
packages/contracts/deployed-contracts/arbitrum.json
Normal file
24
packages/contracts/deployed-contracts/arbitrum.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"verifiers": {
|
||||
"Verifier16": "0x6143ECd9Fd1A00EDe1046d456f8aab53a7D71609",
|
||||
"Verifier17": "0xAc12fFFE354D6446eb50dd33E683B78FED73Fb02",
|
||||
"Verifier18": "0x610aeF0F2da3CD1C8bDefe4BDB434Ee146E0C701",
|
||||
"Verifier19": "0x5477725177035bbC9d70443eb921D29749D6FCb4",
|
||||
"Verifier20": "0x3fB2C0988a37b76e760c44e6516aF720935f3136",
|
||||
"Verifier21": "0xDc8f6B8A42836d4566256f4c6C53131DFD127DF8",
|
||||
"Verifier22": "0x6962b5e706be5278eeCb01c286b50A48484632f2",
|
||||
"Verifier23": "0x41e4796Bd89B4BF04013b559c93fC32E9a2BdF6B",
|
||||
"Verifier24": "0xD528B1D1408ab3583af4694F92b0aFEbE33d5b60",
|
||||
"Verifier25": "0x1683a27EF9c10c5286dB56412E1272cD0Ca733e7",
|
||||
"Verifier26": "0x78194bB665d1E33b97eE45B1A755c15717E94C00",
|
||||
"Verifier27": "0x997Dac00E6701Ef7F3518280E5a9922801126E42",
|
||||
"Verifier28": "0xDd3C7f4cBA2467aE41c0F614A3c3E24bC80268c6",
|
||||
"Verifier29": "0xe53eF12093933D5df5691EAbA3821bD1c1EB60Cd",
|
||||
"Verifier30": "0x7FeA07c536ABBB0E7FB3c833376EE4EaDc21340e",
|
||||
"Verifier31": "0xe4539a592df18936202480FBe77E47DE012F2178",
|
||||
"Verifier32": "0x98c90845A7870e215cBd7265DDC653E6c07032F4"
|
||||
},
|
||||
"Semaphore": "0x49281E30F17A30808a6ce538f979d539747e6707",
|
||||
"PoseidonT3": "0xe0c8d1e53D9Bfc9071F6564755FCFf6cC0dB61d0",
|
||||
"IncrementalBinaryTree": "0x9fCea5E9aF68D5B0c55A1003b49284d70BffC1A9"
|
||||
}
|
||||
24
packages/contracts/deployed-contracts/goerli.json
Normal file
24
packages/contracts/deployed-contracts/goerli.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"verifiers": {
|
||||
"Verifier16": "0xA5253ba39381Aa99c4C2C5A4D5C2deC036d06629",
|
||||
"Verifier17": "0xe0418A5f8fBF051D6cbc41Ff29855Dd2a02201Ab",
|
||||
"Verifier18": "0x7CdB3336d7d7c55Bce0FB1508594C54521656797",
|
||||
"Verifier19": "0xbd870921d8A5398a3314C950d1fc63b8C3AB190B",
|
||||
"Verifier20": "0x2a96c5696F85e3d2aa918496806B5c5a4D93E099",
|
||||
"Verifier21": "0x5Ec7d851a52A2a25CEc528F42a7ACA8EcF4667Cd",
|
||||
"Verifier22": "0x919d3d9c05FA7411e334deA5a763354fC7B6aA5b",
|
||||
"Verifier23": "0x63917b00a6dA7865bEfdd107AfC83CC2e6BDE552",
|
||||
"Verifier24": "0xd05CAd7d940114c1419098EE3cEA0776ab510E7D",
|
||||
"Verifier25": "0x6D9862e6140D94E932d94c8BcE74a0BDD0ea5ACb",
|
||||
"Verifier26": "0x8c29e0b77e32f704F03eeCE01c041192A5EB6c77",
|
||||
"Verifier27": "0x066cC22f8CA2A8D90D7Ff77D8a10A27e629c9c4C",
|
||||
"Verifier28": "0x698F9507f504E2BD238be7da56E8D9fee60C6D15",
|
||||
"Verifier29": "0xbBfC2E201C3c3c6F50063c3Edb4746c6Fcb36346",
|
||||
"Verifier30": "0x06bcD633988c1CE7Bd134DbE2C12119b6f3E4bD1",
|
||||
"Verifier31": "0x133b69Ce47BF20C49368354914DF47519Ca6cCFE",
|
||||
"Verifier32": "0xe2978F79cb4AF62e5C990EE5c7E12fb22ee22e2D"
|
||||
},
|
||||
"Semaphore": "0x53A34BEDc7b5fC5a52D10E3785D7126BFa60aE16",
|
||||
"PoseidonT3": "0xAe950dF20Fd9d876AAa329Fc8e29FBA31093a9b3",
|
||||
"IncrementalBinaryTree": "0xFec7a351D1Ba4d128594443273F0E32Ef8Ce48Ad"
|
||||
}
|
||||
@@ -15,24 +15,26 @@ import "./tasks/deploy-semaphore-voting"
|
||||
import "./tasks/deploy-semaphore-whistleblowing"
|
||||
import "./tasks/deploy-verifier"
|
||||
|
||||
dotenvConfig({ path: resolve(__dirname, "./.env") })
|
||||
dotenvConfig({ path: resolve(__dirname, "../../.env") })
|
||||
|
||||
function getNetworks(): NetworksUserConfig | undefined {
|
||||
if (process.env.INFURA_API_KEY && process.env.BACKEND_PRIVATE_KEY) {
|
||||
const infuraApiKey = process.env.INFURA_API_KEY
|
||||
const accounts = [`0x${process.env.BACKEND_PRIVATE_KEY}`]
|
||||
function getNetworks(): NetworksUserConfig {
|
||||
if (!process.env.INFURA_API_KEY || !process.env.BACKEND_PRIVATE_KEY) {
|
||||
return {}
|
||||
}
|
||||
|
||||
return {
|
||||
goerli: {
|
||||
url: `https://goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 5,
|
||||
accounts
|
||||
},
|
||||
arbitrum: {
|
||||
url: "https://arb1.arbitrum.io/rpc",
|
||||
chainId: 42161,
|
||||
accounts
|
||||
}
|
||||
const infuraApiKey = process.env.INFURA_API_KEY
|
||||
const accounts = [`0x${process.env.BACKEND_PRIVATE_KEY}`]
|
||||
|
||||
return {
|
||||
goerli: {
|
||||
url: `https://goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 5,
|
||||
accounts
|
||||
},
|
||||
arbitrum: {
|
||||
url: "https://arb1.arbitrum.io/rpc",
|
||||
chainId: 42161,
|
||||
accounts
|
||||
}
|
||||
}
|
||||
}
|
||||
71
packages/contracts/package.json
Normal file
71
packages/contracts/package.json
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"name": "contracts",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "hardhat node",
|
||||
"compile": "hardhat compile",
|
||||
"deploy:verifier": "hardhat deploy:verifier",
|
||||
"deploy:verifiers": "hardhat run scripts/deploy-verifiers.ts",
|
||||
"deploy:semaphore": "hardhat deploy:semaphore",
|
||||
"deploy:semaphore-voting": "hardhat deploy:semaphore-voting",
|
||||
"deploy:semaphore-whistleblowing": "hardhat deploy:semaphore-whistleblowing",
|
||||
"verify:contracts": "hardhat run scripts/verify-contracts.ts",
|
||||
"test": "hardhat test",
|
||||
"test:report-gas": "REPORT_GAS=true hardhat test",
|
||||
"test:coverage": "hardhat coverage",
|
||||
"typechain": "hardhat typechain",
|
||||
"lint": "solhint 'contracts/**/*.sol'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.6",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.1.0",
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.3",
|
||||
"@semaphore-protocol/group": "workspace:packages/group",
|
||||
"@semaphore-protocol/identity": "workspace:packages/identity",
|
||||
"@semaphore-protocol/proof": "workspace:packages/proof",
|
||||
"@typechain/ethers-v5": "^10.0.0",
|
||||
"@typechain/hardhat": "^6.0.0",
|
||||
"@types/chai": "^4.3.0",
|
||||
"@types/download": "^8.0.1",
|
||||
"@types/mocha": "^9.1.0",
|
||||
"@types/node": "^17.0.12",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"chai": "^4.3.5",
|
||||
"circomlib": "^2.0.2",
|
||||
"circomlibjs": "^0.0.8",
|
||||
"download": "^8.0.0",
|
||||
"ethereum-waffle": "^3.4.4",
|
||||
"ethers": "^5.6.8",
|
||||
"hardhat": "^2.9.7",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
"js-logger": "^1.6.1",
|
||||
"prettier-plugin-solidity": "^1.0.0-beta.19",
|
||||
"rimraf": "^3.0.2",
|
||||
"snarkjs": "^0.4.13",
|
||||
"solhint": "^3.3.6",
|
||||
"solhint-plugin-prettier": "^0.0.5",
|
||||
"solidity-coverage": "^0.7.21",
|
||||
"ts-node": "^10.4.0",
|
||||
"typechain": "^8.0.0"
|
||||
},
|
||||
"config": {
|
||||
"solidity": {
|
||||
"version": "0.8.4"
|
||||
},
|
||||
"paths": {
|
||||
"contracts": "./contracts",
|
||||
"circuit": "./circuit",
|
||||
"tests": "./test",
|
||||
"cache": "./cache",
|
||||
"snarkjs-templates": "./snarkjs-templates",
|
||||
"build": {
|
||||
"contracts": "./build/contracts",
|
||||
"typechain": "./build/typechain"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "4.4.2",
|
||||
"@zk-kit/incremental-merkle-tree.sol": "1.3.0"
|
||||
}
|
||||
}
|
||||
22
packages/contracts/scripts/deploy-verifiers.ts
Normal file
22
packages/contracts/scripts/deploy-verifiers.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { hardhatArguments, run } from "hardhat"
|
||||
import { saveDeployedContracts } from "./utils"
|
||||
|
||||
async function main() {
|
||||
const verifiers: Record<string, string> = {}
|
||||
|
||||
// Deploy verifiers.
|
||||
for (let treeDepth = 16; treeDepth <= 32; treeDepth += 1) {
|
||||
const { address } = await run("deploy:verifier", { depth: treeDepth })
|
||||
|
||||
verifiers[`Verifier${treeDepth}`] = address
|
||||
}
|
||||
|
||||
saveDeployedContracts(hardhatArguments.network, { verifiers })
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
34
packages/contracts/scripts/utils.ts
Normal file
34
packages/contracts/scripts/utils.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { readFileSync, writeFileSync } from "fs"
|
||||
|
||||
export function saveDeployedContracts(network: string | undefined, deployedContracts: any) {
|
||||
if (network !== "goerli" && network !== "arbitrum") {
|
||||
return
|
||||
}
|
||||
|
||||
writeFileSync(`./deployed-contracts/${network}.json`, JSON.stringify(deployedContracts, null, 4))
|
||||
}
|
||||
|
||||
export function getDeployedContracts(network: string | undefined): any {
|
||||
if (network !== "goerli" && network !== "arbitrum") {
|
||||
return null
|
||||
}
|
||||
|
||||
return JSON.parse(readFileSync(`./deployed-contracts/${network}.json`, "utf8"))
|
||||
}
|
||||
|
||||
export function verifiersToSolidityArgument(deployedContracts: any): any {
|
||||
const verifiers = []
|
||||
|
||||
if (deployedContracts && deployedContracts.verifiers) {
|
||||
for (const verifier in deployedContracts.verifiers) {
|
||||
if (Object.prototype.hasOwnProperty.call(deployedContracts.verifiers, verifier)) {
|
||||
verifiers.push({
|
||||
merkleTreeDepth: verifier.substring(8),
|
||||
contractAddress: deployedContracts.verifiers[verifier]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return verifiers
|
||||
}
|
||||
24
packages/contracts/scripts/verify-contracts.ts
Normal file
24
packages/contracts/scripts/verify-contracts.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { hardhatArguments, run } from "hardhat"
|
||||
import { getDeployedContracts, verifiersToSolidityArgument } from "./utils"
|
||||
|
||||
async function main() {
|
||||
const deployedContracts = getDeployedContracts(hardhatArguments.network)
|
||||
|
||||
if (deployedContracts) {
|
||||
await run("verify:verify", {
|
||||
address: deployedContracts.Semaphore,
|
||||
constructorArguments: [verifiersToSolidityArgument(deployedContracts)]
|
||||
})
|
||||
|
||||
await run("verify:verify", {
|
||||
address: deployedContracts.IncrementalBinaryTree
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -8,7 +8,7 @@ task("accounts", "Prints the list of accounts")
|
||||
|
||||
if (logs) {
|
||||
for (const account of accounts) {
|
||||
console.log(await account.getAddress())
|
||||
console.info(await account.getAddress())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@ task("deploy:semaphore-voting", "Deploy a SemaphoreVoting contract")
|
||||
|
||||
await poseidonLib.deployed()
|
||||
|
||||
logs && console.log(`Poseidon library has been deployed to: ${poseidonLib.address}`)
|
||||
if (logs) {
|
||||
console.info(`Poseidon library has been deployed to: ${poseidonLib.address}`)
|
||||
}
|
||||
|
||||
const IncrementalBinaryTreeLibFactory = await ethers.getContractFactory("IncrementalBinaryTree", {
|
||||
libraries: {
|
||||
@@ -27,7 +29,9 @@ task("deploy:semaphore-voting", "Deploy a SemaphoreVoting contract")
|
||||
|
||||
await incrementalBinaryTreeLib.deployed()
|
||||
|
||||
logs && console.log(`IncrementalBinaryTree library has been deployed to: ${incrementalBinaryTreeLib.address}`)
|
||||
if (logs) {
|
||||
console.info(`IncrementalBinaryTree library has been deployed to: ${incrementalBinaryTreeLib.address}`)
|
||||
}
|
||||
|
||||
const ContractFactory = await ethers.getContractFactory("SemaphoreVoting", {
|
||||
libraries: {
|
||||
@@ -39,7 +43,9 @@ task("deploy:semaphore-voting", "Deploy a SemaphoreVoting contract")
|
||||
|
||||
await contract.deployed()
|
||||
|
||||
logs && console.log(`SemaphoreVoting contract has been deployed to: ${contract.address}`)
|
||||
if (logs) {
|
||||
console.info(`SemaphoreVoting contract has been deployed to: ${contract.address}`)
|
||||
}
|
||||
|
||||
return contract
|
||||
})
|
||||
@@ -16,7 +16,9 @@ task("deploy:semaphore-whistleblowing", "Deploy a SemaphoreWhistleblowing contra
|
||||
|
||||
await poseidonLib.deployed()
|
||||
|
||||
logs && console.log(`Poseidon library has been deployed to: ${poseidonLib.address}`)
|
||||
if (logs) {
|
||||
console.info(`Poseidon library has been deployed to: ${poseidonLib.address}`)
|
||||
}
|
||||
|
||||
const IncrementalBinaryTreeLibFactory = await ethers.getContractFactory("IncrementalBinaryTree", {
|
||||
libraries: {
|
||||
@@ -27,7 +29,9 @@ task("deploy:semaphore-whistleblowing", "Deploy a SemaphoreWhistleblowing contra
|
||||
|
||||
await incrementalBinaryTreeLib.deployed()
|
||||
|
||||
logs && console.log(`IncrementalBinaryTree library has been deployed to: ${incrementalBinaryTreeLib.address}`)
|
||||
if (logs) {
|
||||
console.info(`IncrementalBinaryTree library has been deployed to: ${incrementalBinaryTreeLib.address}`)
|
||||
}
|
||||
|
||||
const ContractFactory = await ethers.getContractFactory("SemaphoreWhistleblowing", {
|
||||
libraries: {
|
||||
@@ -39,7 +43,9 @@ task("deploy:semaphore-whistleblowing", "Deploy a SemaphoreWhistleblowing contra
|
||||
|
||||
await contract.deployed()
|
||||
|
||||
logs && console.log(`SemaphoreWhistleblowing contract has been deployed to: ${contract.address}`)
|
||||
if (logs) {
|
||||
console.info(`SemaphoreWhistleblowing contract has been deployed to: ${contract.address}`)
|
||||
}
|
||||
|
||||
return contract
|
||||
})
|
||||
@@ -1,11 +1,19 @@
|
||||
import { poseidon_gencontract as poseidonContract } from "circomlibjs"
|
||||
import { Contract } from "ethers"
|
||||
import { task, types } from "hardhat/config"
|
||||
import { getDeployedContracts, saveDeployedContracts, verifiersToSolidityArgument } from "../scripts/utils"
|
||||
|
||||
task("deploy:semaphore", "Deploy a Semaphore contract")
|
||||
.addOptionalParam<boolean>("logs", "Print the logs", true, types.boolean)
|
||||
.addParam("verifiers", "Tree depths and verifier addresses", undefined, types.json)
|
||||
.setAction(async ({ logs, verifiers }, { ethers }): Promise<Contract> => {
|
||||
.addOptionalParam("verifiers", "Tree depths and verifier addresses", [], types.json)
|
||||
.setAction(async ({ logs, verifiers }, { ethers, hardhatArguments }): Promise<Contract> => {
|
||||
let deployedContracts: any
|
||||
|
||||
if (verifiers.length === 0) {
|
||||
deployedContracts = getDeployedContracts(hardhatArguments.network)
|
||||
verifiers = verifiersToSolidityArgument(deployedContracts)
|
||||
}
|
||||
|
||||
const poseidonABI = poseidonContract.generateABI(2)
|
||||
const poseidonBytecode = poseidonContract.createCode(2)
|
||||
|
||||
@@ -16,7 +24,9 @@ task("deploy:semaphore", "Deploy a Semaphore contract")
|
||||
|
||||
await poseidonLib.deployed()
|
||||
|
||||
logs && console.log(`Poseidon library has been deployed to: ${poseidonLib.address}`)
|
||||
if (logs) {
|
||||
console.info(`Poseidon library has been deployed to: ${poseidonLib.address}`)
|
||||
}
|
||||
|
||||
const IncrementalBinaryTreeLibFactory = await ethers.getContractFactory("IncrementalBinaryTree", {
|
||||
libraries: {
|
||||
@@ -27,7 +37,9 @@ task("deploy:semaphore", "Deploy a Semaphore contract")
|
||||
|
||||
await incrementalBinaryTreeLib.deployed()
|
||||
|
||||
logs && console.log(`IncrementalBinaryTree library has been deployed to: ${incrementalBinaryTreeLib.address}`)
|
||||
if (logs) {
|
||||
console.info(`IncrementalBinaryTree library has been deployed to: ${incrementalBinaryTreeLib.address}`)
|
||||
}
|
||||
|
||||
const ContractFactory = await ethers.getContractFactory("Semaphore", {
|
||||
libraries: {
|
||||
@@ -39,7 +51,17 @@ task("deploy:semaphore", "Deploy a Semaphore contract")
|
||||
|
||||
await contract.deployed()
|
||||
|
||||
logs && console.log(`Semaphore contract has been deployed to: ${contract.address}`)
|
||||
if (logs) {
|
||||
console.info(`Semaphore contract has been deployed to: ${contract.address}`)
|
||||
}
|
||||
|
||||
if (deployedContracts) {
|
||||
deployedContracts.PoseidonT3 = poseidonLib.address
|
||||
deployedContracts.IncrementalBinaryTree = incrementalBinaryTreeLib.address
|
||||
deployedContracts.Semaphore = contract.address
|
||||
|
||||
saveDeployedContracts(hardhatArguments.network, deployedContracts)
|
||||
}
|
||||
|
||||
return contract
|
||||
})
|
||||
@@ -11,7 +11,9 @@ task("deploy:verifier", "Deploy a Verifier contract")
|
||||
|
||||
await contract.deployed()
|
||||
|
||||
logs && console.log(`Verifier${depth} contract has been deployed to: ${contract.address}`)
|
||||
if (logs) {
|
||||
console.info(`Verifier${depth} contract has been deployed to: ${contract.address}`)
|
||||
}
|
||||
|
||||
return contract
|
||||
})
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-shadow */
|
||||
/* eslint-disable jest/valid-expect */
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { FullProof, generateProof, packToSolidityProof, SolidityProof } from "@semaphore-protocol/proof"
|
||||
@@ -5,7 +7,6 @@ import { expect } from "chai"
|
||||
import { constants, Signer, utils } from "ethers"
|
||||
import { run } from "hardhat"
|
||||
import { Semaphore } from "../build/typechain"
|
||||
import { config } from "../package.json"
|
||||
import { createIdentityCommitments } from "./utils"
|
||||
|
||||
describe("Semaphore", () => {
|
||||
@@ -13,12 +14,12 @@ describe("Semaphore", () => {
|
||||
let signers: Signer[]
|
||||
let accounts: string[]
|
||||
|
||||
const treeDepth = Number(process.env.TREE_DEPTH)
|
||||
const treeDepth = Number(process.env.TREE_DEPTH) || 20
|
||||
const groupId = 1
|
||||
const members = createIdentityCommitments(3)
|
||||
|
||||
const wasmFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.wasm`
|
||||
const zkeyFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.zkey`
|
||||
const wasmFilePath = `../../snark-artifacts/semaphore.wasm`
|
||||
const zkeyFilePath = `../../snark-artifacts/semaphore.zkey`
|
||||
|
||||
before(async () => {
|
||||
const { address: verifierAddress } = await run("deploy:verifier", { logs: false, depth: treeDepth })
|
||||
@@ -109,12 +110,6 @@ describe("Semaphore", () => {
|
||||
})
|
||||
|
||||
describe("# addMembers", () => {
|
||||
it("Should not add members if the caller is not the group admin", async () => {
|
||||
const transaction = contract.connect(signers[1]).addMembers(groupId, [1, 2, 3])
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotTheGroupAdmin()")
|
||||
})
|
||||
|
||||
it("Should add new members to an existing group", async () => {
|
||||
const groupId = 3
|
||||
const members = [BigInt(1), BigInt(2), BigInt(3)]
|
||||
@@ -132,7 +127,9 @@ describe("Semaphore", () => {
|
||||
|
||||
describe("# updateMember", () => {
|
||||
it("Should not update a member if the caller is not the group admin", async () => {
|
||||
const transaction = contract.connect(signers[1]).updateMember(groupId, members[0], 1, [0, 1], [0, 1])
|
||||
const member = BigInt(2)
|
||||
|
||||
const transaction = contract.connect(signers[1]).updateMember(groupId, member, 1, [0, 1], [0, 1])
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotTheGroupAdmin()")
|
||||
})
|
||||
@@ -161,7 +158,9 @@ describe("Semaphore", () => {
|
||||
|
||||
describe("# removeMember", () => {
|
||||
it("Should not remove a member if the caller is not the group admin", async () => {
|
||||
const transaction = contract.connect(signers[1]).removeMember(groupId, members[0], [0, 1], [0, 1])
|
||||
const member = BigInt(2)
|
||||
|
||||
const transaction = contract.connect(signers[1]).removeMember(groupId, member, [0, 1], [0, 1])
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotTheGroupAdmin()")
|
||||
})
|
||||
@@ -242,7 +241,28 @@ describe("Semaphore", () => {
|
||||
solidityProof
|
||||
)
|
||||
|
||||
await expect(transaction).to.emit(contract, "ProofVerified").withArgs(groupId, signal)
|
||||
await expect(transaction)
|
||||
.to.emit(contract, "ProofVerified")
|
||||
.withArgs(
|
||||
groupId,
|
||||
group.root,
|
||||
fullProof.publicSignals.nullifierHash,
|
||||
fullProof.publicSignals.externalNullifier,
|
||||
signal
|
||||
)
|
||||
})
|
||||
|
||||
it("Should not verify the same proof for an onchain group twice", async () => {
|
||||
const transaction = contract.verifyProof(
|
||||
groupId,
|
||||
group.root,
|
||||
signal,
|
||||
fullProof.publicSignals.nullifierHash,
|
||||
fullProof.publicSignals.merkleRoot,
|
||||
solidityProof
|
||||
)
|
||||
|
||||
await expect(transaction).to.be.revertedWith("Semaphore__YouAreUsingTheSameNillifierTwice()")
|
||||
})
|
||||
|
||||
it("Should not verify a proof if the Merkle tree root is expired", async () => {
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
/* eslint-disable jest/valid-expect */
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import {
|
||||
generateNullifierHash,
|
||||
generateProof,
|
||||
@@ -11,20 +12,19 @@ import { expect } from "chai"
|
||||
import { Signer, utils } from "ethers"
|
||||
import { ethers, run } from "hardhat"
|
||||
import { SemaphoreVoting } from "../build/typechain"
|
||||
import { config } from "../package.json"
|
||||
|
||||
describe("SemaphoreVoting", () => {
|
||||
let contract: SemaphoreVoting
|
||||
let accounts: Signer[]
|
||||
let coordinator: string
|
||||
|
||||
const treeDepth = Number(process.env.TREE_DEPTH)
|
||||
const treeDepth = Number(process.env.TREE_DEPTH) || 20
|
||||
const pollIds = [BigInt(1), BigInt(2), BigInt(3)]
|
||||
const encryptionKey = BigInt(0)
|
||||
const decryptionKey = BigInt(0)
|
||||
|
||||
const wasmFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.wasm`
|
||||
const zkeyFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.zkey`
|
||||
const wasmFilePath = `../../snark-artifacts/semaphore.wasm`
|
||||
const zkeyFilePath = `../../snark-artifacts/semaphore.zkey`
|
||||
|
||||
before(async () => {
|
||||
const { address: verifierAddress } = await run("deploy:verifier", { logs: false, depth: treeDepth })
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable jest/valid-expect */
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import {
|
||||
@@ -11,18 +12,17 @@ import { expect } from "chai"
|
||||
import { Signer, utils } from "ethers"
|
||||
import { ethers, run } from "hardhat"
|
||||
import { SemaphoreWhistleblowing } from "../build/typechain"
|
||||
import { config } from "../package.json"
|
||||
|
||||
describe("SemaphoreWhistleblowing", () => {
|
||||
let contract: SemaphoreWhistleblowing
|
||||
let accounts: Signer[]
|
||||
let editor: string
|
||||
|
||||
const treeDepth = Number(process.env.TREE_DEPTH)
|
||||
const treeDepth = Number(process.env.TREE_DEPTH) || 20
|
||||
const entityIds = [BigInt(1), BigInt(2)]
|
||||
|
||||
const wasmFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.wasm`
|
||||
const zkeyFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.zkey`
|
||||
const wasmFilePath = `../../snark-artifacts/semaphore.wasm`
|
||||
const zkeyFilePath = `../../snark-artifacts/semaphore.zkey`
|
||||
|
||||
before(async () => {
|
||||
const { address: verifierAddress } = await run("deploy:verifier", { logs: false, depth: treeDepth })
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function createIdentityCommitments(n: number): bigint[] {
|
||||
const identityCommitments: bigint[] = []
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
for (let i = 0; i < n; i += 1) {
|
||||
const identity = new Identity(i.toString())
|
||||
const identityCommitment = identity.generateCommitment()
|
||||
|
||||
10
packages/contracts/tsconfig.json
Normal file
10
packages/contracts/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2018",
|
||||
"module": "CommonJS",
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["scripts/**/*", "tasks/**/*", "test/**/*", "build/typechain/**/*", "types/**/*"],
|
||||
"files": ["hardhat.config.ts"]
|
||||
}
|
||||
21
packages/group/LICENSE
Normal file
21
packages/group/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ethereum Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
127
packages/group/README.md
Normal file
127
packages/group/README.md
Normal file
@@ -0,0 +1,127 @@
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
Semaphore group
|
||||
</h1>
|
||||
<p align="center">A library to create and manage Semaphore groups.</p>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/semaphore-protocol">
|
||||
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/LICENSE">
|
||||
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/@semaphore-protocol/group">
|
||||
<img alt="NPM version" src="https://img.shields.io/npm/v/@semaphore-protocol/group?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/group">
|
||||
<img alt="Downloads" src="https://img.shields.io/npm/dm/@semaphore-protocol/group.svg?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://eslint.org/">
|
||||
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint" />
|
||||
</a>
|
||||
<a href="https://prettier.io/">
|
||||
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
<h4>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CONTRIBUTING.md">
|
||||
👥 Contributing
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
|
||||
🤝 Code of conduct
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://discord.gg/6mSdGHnstH">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
| This library is an abstraction of [`@zk-kit/incremental-merkle-tree`](https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/incremental-merkle-tree). The main goal is to make it easier to create offchain groups, which are also used to generate Semaphore proofs. Semaphore groups are actually incremental Merkle trees, and the group members are tree leaves. Since the Merkle tree implementation we are using is a binary tree, the maximum number of members of a group is equal to `2^treeDepth`. |
|
||||
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
## 🛠 Install
|
||||
|
||||
### npm or yarn
|
||||
|
||||
Install the `@semaphore-protocol/group` package with npm:
|
||||
|
||||
```bash
|
||||
npm i @semaphore-protocol/group
|
||||
```
|
||||
|
||||
or yarn:
|
||||
|
||||
```bash
|
||||
yarn add @semaphore-protocol/group
|
||||
```
|
||||
|
||||
## 📜 Usage
|
||||
|
||||
\# **new Group**(treeDepth = 20, zeroValue = BigInt(0)): _Group_
|
||||
|
||||
```typescript
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
|
||||
// Group with max 1048576 members (20^²).
|
||||
const group1 = new Group()
|
||||
|
||||
// Group with max 65536 members (16^²).
|
||||
const group2 = new Group(16)
|
||||
|
||||
// Group with max 16777216 members (24^²).
|
||||
const group3 = new Group(24)
|
||||
```
|
||||
|
||||
\# **addMember**(identityCommitment: _Member_)
|
||||
|
||||
```typescript
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
|
||||
const identity = new Identity()
|
||||
const commitment = identity.generateCommitment()
|
||||
|
||||
group.addMember(commitment)
|
||||
```
|
||||
|
||||
\# **addMembers**(identityCommitments: _Member\[]_)
|
||||
|
||||
```typescript
|
||||
let identityCommitments: bigint[]
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const identity = new Identity()
|
||||
const commitment = identity.generateCommitment()
|
||||
|
||||
identityCommitments.push(commitment)
|
||||
}
|
||||
|
||||
group.addMember(identityCommitments)
|
||||
```
|
||||
|
||||
\# **removeMember**(index: _number_)
|
||||
|
||||
```typescript
|
||||
group.removeMember(0)
|
||||
```
|
||||
|
||||
\# **indexOf**(member: _Member_): _number_
|
||||
|
||||
```typescript
|
||||
group.indexOf(commitment) // 0
|
||||
```
|
||||
|
||||
\# **generateProofOfMembership**(index: _number_): _MerkleProof_
|
||||
|
||||
```typescript
|
||||
const proof = group.generateProofOfMembership(0)
|
||||
```
|
||||
8
packages/group/build.tsconfig.json
Normal file
8
packages/group/build.tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"declarationDir": "dist/types"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
41
packages/group/package.json
Normal file
41
packages/group/package.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/group",
|
||||
"version": "2.5.0",
|
||||
"description": "A library to create and manage Semaphore groups.",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.node.js",
|
||||
"exports": {
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.node.js"
|
||||
},
|
||||
"types": "dist/types/index.d.ts",
|
||||
"files": [
|
||||
"dist/",
|
||||
"src/",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"repository": "https://github.com/semaphore-protocol/semaphore",
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore/tree/main/packages/group",
|
||||
"bugs": {
|
||||
"url": "https://github.com/semaphore-protocol/semaphore.git/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"build:watch": "rollup -c rollup.config.ts -w --configPlugin typescript",
|
||||
"build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
|
||||
"prepublishOnly": "yarn build",
|
||||
"docs": "typedoc src/index.ts --out ../../docs/group"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rollup-plugin-cleanup": "^3.2.1",
|
||||
"rollup-plugin-typescript2": "^0.31.2",
|
||||
"typedoc": "^0.22.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@zk-kit/incremental-merkle-tree": "1.0.0",
|
||||
"circomlibjs": "0.0.8"
|
||||
}
|
||||
}
|
||||
29
packages/group/rollup.config.ts
Normal file
29
packages/group/rollup.config.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import typescript from "rollup-plugin-typescript2"
|
||||
import * as fs from "fs"
|
||||
import cleanup from "rollup-plugin-cleanup"
|
||||
|
||||
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
|
||||
const banner = `/**
|
||||
* @module ${pkg.name}
|
||||
* @version ${pkg.version}
|
||||
* @file ${pkg.description}
|
||||
* @copyright Ethereum Foundation 2022
|
||||
* @license ${pkg.license}
|
||||
* @see [Github]{@link ${pkg.homepage}}
|
||||
*/`
|
||||
|
||||
export default {
|
||||
input: "src/index.ts",
|
||||
output: [
|
||||
{ file: pkg.exports.require, format: "cjs", banner, exports: "auto" },
|
||||
{ file: pkg.exports.import, format: "es", banner }
|
||||
],
|
||||
external: Object.keys(pkg.dependencies),
|
||||
plugins: [
|
||||
typescript({
|
||||
tsconfig: "./build.tsconfig.json",
|
||||
useTsconfigDeclarationDir: true
|
||||
}),
|
||||
cleanup({ comments: "jsdoc" })
|
||||
]
|
||||
}
|
||||
95
packages/group/src/group.test.ts
Normal file
95
packages/group/src/group.test.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import Group from "./group"
|
||||
|
||||
describe("Group", () => {
|
||||
describe("# Group", () => {
|
||||
it("Should create a group", () => {
|
||||
const group = new Group()
|
||||
|
||||
expect(group.root.toString()).toContain("150197")
|
||||
expect(group.depth).toBe(20)
|
||||
expect(group.zeroValue).toBe(BigInt(0))
|
||||
expect(group.members).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("Should not create a group with a wrong tree depth", () => {
|
||||
const fun = () => new Group(33)
|
||||
|
||||
expect(fun).toThrow("The tree depth must be between 16 and 32")
|
||||
})
|
||||
|
||||
it("Should create a group with different parameters", () => {
|
||||
const group = new Group(32, BigInt(1))
|
||||
|
||||
expect(group.root.toString()).toContain("640470")
|
||||
expect(group.depth).toBe(32)
|
||||
expect(group.zeroValue).toBe(BigInt(1))
|
||||
expect(group.members).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# addMember", () => {
|
||||
it("Should add a member to a group", () => {
|
||||
const group = new Group()
|
||||
|
||||
group.addMember(BigInt(3))
|
||||
|
||||
expect(group.members).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# addMembers", () => {
|
||||
it("Should add many members to a group", () => {
|
||||
const group = new Group()
|
||||
|
||||
group.addMembers([BigInt(1), BigInt(3)])
|
||||
|
||||
expect(group.members).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# indexOf", () => {
|
||||
it("Should return the index of a member in a group", () => {
|
||||
const group = new Group()
|
||||
group.addMembers([BigInt(1), BigInt(3)])
|
||||
|
||||
const index = group.indexOf(BigInt(3))
|
||||
|
||||
expect(index).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# updateMember", () => {
|
||||
it("Should update a member in a group", () => {
|
||||
const group = new Group()
|
||||
group.addMembers([BigInt(1), BigInt(3)])
|
||||
|
||||
group.updateMember(0, BigInt(1))
|
||||
|
||||
expect(group.members).toHaveLength(2)
|
||||
expect(group.members[0]).toBe(BigInt(1))
|
||||
})
|
||||
})
|
||||
|
||||
describe("# removeMember", () => {
|
||||
it("Should remove a member from a group", () => {
|
||||
const group = new Group()
|
||||
group.addMembers([BigInt(1), BigInt(3)])
|
||||
|
||||
group.removeMember(0)
|
||||
|
||||
expect(group.members).toHaveLength(2)
|
||||
expect(group.members[0]).toBe(group.zeroValue)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# generateProofOfMembership", () => {
|
||||
it("Should generate a proof of membership", () => {
|
||||
const group = new Group()
|
||||
group.addMembers([BigInt(1), BigInt(3)])
|
||||
|
||||
const proof = group.generateProofOfMembership(0)
|
||||
|
||||
expect(proof.leaf).toBe(BigInt(1))
|
||||
})
|
||||
})
|
||||
})
|
||||
109
packages/group/src/group.ts
Normal file
109
packages/group/src/group.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { IncrementalMerkleTree, MerkleProof } from "@zk-kit/incremental-merkle-tree"
|
||||
import { poseidon } from "circomlibjs"
|
||||
import { Member } from "./types"
|
||||
|
||||
export default class Group {
|
||||
private _merkleTree: IncrementalMerkleTree
|
||||
|
||||
/**
|
||||
* Initializes the group with the tree depth and the zero value.
|
||||
* @param treeDepth Tree depth.
|
||||
* @param zeroValue Zero values for zeroes.
|
||||
*/
|
||||
constructor(treeDepth = 20, zeroValue: Member = BigInt(0)) {
|
||||
if (treeDepth < 16 || treeDepth > 32) {
|
||||
throw new Error("The tree depth must be between 16 and 32")
|
||||
}
|
||||
|
||||
this._merkleTree = new IncrementalMerkleTree(poseidon, treeDepth, zeroValue, 2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root hash of the tree.
|
||||
* @returns Root hash.
|
||||
*/
|
||||
get root(): Member {
|
||||
return this._merkleTree.root
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the depth of the tree.
|
||||
* @returns Tree depth.
|
||||
*/
|
||||
get depth(): number {
|
||||
return this._merkleTree.depth
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the zero value of the tree.
|
||||
* @returns Tree zero value.
|
||||
*/
|
||||
get zeroValue(): Member {
|
||||
return this._merkleTree.zeroes[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the members (i.e. identity commitments) of the group.
|
||||
* @returns List of members.
|
||||
*/
|
||||
get members(): Member[] {
|
||||
return this._merkleTree.leaves
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of a member. If the member does not exist it returns -1.
|
||||
* @param member Group member.
|
||||
* @returns Index of the member.
|
||||
*/
|
||||
indexOf(member: Member): number {
|
||||
return this._merkleTree.indexOf(member)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new member to the group.
|
||||
* @param identityCommitment New member.
|
||||
*/
|
||||
addMember(identityCommitment: Member) {
|
||||
this._merkleTree.insert(BigInt(identityCommitment))
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new members to the group.
|
||||
* @param identityCommitments New members.
|
||||
*/
|
||||
addMembers(identityCommitments: Member[]) {
|
||||
for (const identityCommitment of identityCommitments) {
|
||||
this.addMember(identityCommitment)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a member in the group.
|
||||
* @param index Index of the member to be updated.
|
||||
* @param identityCommitment New member value.
|
||||
*/
|
||||
updateMember(index: number, identityCommitment: Member) {
|
||||
this._merkleTree.update(index, identityCommitment)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a member from the group.
|
||||
* @param index Index of the member to be removed.
|
||||
*/
|
||||
removeMember(index: number) {
|
||||
this._merkleTree.delete(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a proof of membership.
|
||||
* @param index Index of the proof's member.
|
||||
* @returns Proof object.
|
||||
*/
|
||||
generateProofOfMembership(index: number): MerkleProof {
|
||||
const merkleProof = this._merkleTree.createProof(index)
|
||||
|
||||
merkleProof.siblings = merkleProof.siblings.map((s) => s[0])
|
||||
|
||||
return merkleProof
|
||||
}
|
||||
}
|
||||
4
packages/group/src/index.ts
Normal file
4
packages/group/src/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import Group from "./group"
|
||||
|
||||
export { Group }
|
||||
export * from "./types"
|
||||
1
packages/group/src/types/index.ts
Normal file
1
packages/group/src/types/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type Member = string | bigint
|
||||
4
packages/group/tsconfig.json
Normal file
4
packages/group/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src", "rollup.config.ts"]
|
||||
}
|
||||
21
packages/identity/LICENSE
Normal file
21
packages/identity/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ethereum Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
101
packages/identity/README.md
Normal file
101
packages/identity/README.md
Normal file
@@ -0,0 +1,101 @@
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
Semaphore identity
|
||||
</h1>
|
||||
<p align="center">A library to create Semaphore identities.</p>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/semaphore-protocol">
|
||||
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/LICENSE">
|
||||
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/@semaphore-protocol/identity">
|
||||
<img alt="NPM version" src="https://img.shields.io/npm/v/@semaphore-protocol/identity?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/identity">
|
||||
<img alt="Downloads" src="https://img.shields.io/npm/dm/@semaphore-protocol/identity.svg?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://eslint.org/">
|
||||
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint" />
|
||||
</a>
|
||||
<a href="https://prettier.io/">
|
||||
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
<h4>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CONTRIBUTING.md">
|
||||
👥 Contributing
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
|
||||
🤝 Code of conduct
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://discord.gg/6mSdGHnstH">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
| This library provides a class that can be used to create identities compatible with the Semaphore [circuits](https://github.com/semaphore-protocol/semaphore/tree/main/circuits). Each identity contains two private values (_trapdoor_ and _nullifier_), and the Poseidon hash of these values (_commitment_) is used as the public identifier of the Semaphore identity. |
|
||||
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
## 🛠 Install
|
||||
|
||||
### npm or yarn
|
||||
|
||||
Install the `@semaphore-protocol/identity` package with npm:
|
||||
|
||||
```bash
|
||||
npm i @semaphore-protocol/identity
|
||||
```
|
||||
|
||||
or yarn:
|
||||
|
||||
```bash
|
||||
yarn add @semaphore-protocol/identity
|
||||
```
|
||||
|
||||
## 📜 Usage
|
||||
|
||||
\# **new Identity**(identityOrMessage?: _string_): _Identity_
|
||||
|
||||
```typescript
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
|
||||
// Trapdoor and nullifier are generated randomly.
|
||||
const identity1 = new Identity()
|
||||
|
||||
// Trapdoor and nullifier are generated deterministically from a secret message.
|
||||
const identity2 = new Identity("secret-message")
|
||||
|
||||
// Trapdoor and nullifier are generated from an existing identity.
|
||||
const identity3 = new Identity(identity1.toString())
|
||||
```
|
||||
|
||||
\# **getTrapdoor**(): _bigint_
|
||||
|
||||
```typescript
|
||||
const trapdoor = identity.getTrapdoor()
|
||||
```
|
||||
|
||||
\# **getNullifier**(): _bigint_
|
||||
|
||||
```typescript
|
||||
const nullifier = identity.getNullifier()
|
||||
```
|
||||
|
||||
\# **generateCommitment**(): _bigint_
|
||||
|
||||
```typescript
|
||||
const commitment = identity.generateCommitment()
|
||||
```
|
||||
8
packages/identity/build.tsconfig.json
Normal file
8
packages/identity/build.tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"declarationDir": "dist/types"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
44
packages/identity/package.json
Normal file
44
packages/identity/package.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/identity",
|
||||
"version": "2.5.0",
|
||||
"description": "A library to create Semaphore identities.",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.node.js",
|
||||
"exports": {
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.node.js"
|
||||
},
|
||||
"types": "dist/types/index.d.ts",
|
||||
"files": [
|
||||
"dist/",
|
||||
"src/",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"repository": "https://github.com/semaphore-protocol/semaphore",
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore/tree/main/packages/identity",
|
||||
"bugs": {
|
||||
"url": "https://github.com/semaphore-protocol/semaphore.git/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"build:watch": "rollup -c rollup.config.ts -w --configPlugin typescript",
|
||||
"build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
|
||||
"prepublishOnly": "yarn build",
|
||||
"docs": "typedoc src/index.ts --out ../../docs/identity"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rollup-plugin-cleanup": "^3.2.1",
|
||||
"rollup-plugin-typescript2": "^0.31.2",
|
||||
"typedoc": "^0.22.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ethersproject/bignumber": "^5.5.0",
|
||||
"@ethersproject/random": "^5.5.1",
|
||||
"@ethersproject/sha2": "^5.6.1",
|
||||
"@ethersproject/strings": "^5.6.1",
|
||||
"circomlibjs": "0.0.8"
|
||||
}
|
||||
}
|
||||
29
packages/identity/rollup.config.ts
Normal file
29
packages/identity/rollup.config.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import typescript from "rollup-plugin-typescript2"
|
||||
import * as fs from "fs"
|
||||
import cleanup from "rollup-plugin-cleanup"
|
||||
|
||||
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
|
||||
const banner = `/**
|
||||
* @module ${pkg.name}
|
||||
* @version ${pkg.version}
|
||||
* @file ${pkg.description}
|
||||
* @copyright Ethereum Foundation 2022
|
||||
* @license ${pkg.license}
|
||||
* @see [Github]{@link ${pkg.homepage}}
|
||||
*/`
|
||||
|
||||
export default {
|
||||
input: "src/index.ts",
|
||||
output: [
|
||||
{ file: pkg.exports.require, format: "cjs", banner, exports: "auto" },
|
||||
{ file: pkg.exports.import, format: "es", banner }
|
||||
],
|
||||
external: Object.keys(pkg.dependencies),
|
||||
plugins: [
|
||||
typescript({
|
||||
tsconfig: "./build.tsconfig.json",
|
||||
useTsconfigDeclarationDir: true
|
||||
}),
|
||||
cleanup({ comments: "jsdoc" })
|
||||
]
|
||||
}
|
||||
5
packages/identity/src/checkParameter.ts
Normal file
5
packages/identity/src/checkParameter.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default function checkParameter(value: any, name: string, type: string) {
|
||||
if (typeof value !== type) {
|
||||
throw new TypeError(`Parameter '${name}' is not a ${type}`)
|
||||
}
|
||||
}
|
||||
114
packages/identity/src/identity.test.ts
Normal file
114
packages/identity/src/identity.test.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { BigNumber } from "@ethersproject/bignumber"
|
||||
import Identity from "./identity"
|
||||
|
||||
describe("Identity", () => {
|
||||
describe("# Identity", () => {
|
||||
it("Should not create a identity if the parameter is not valid", () => {
|
||||
const fun1 = () => new Identity(13 as any)
|
||||
const fun2 = () => new Identity(true as any)
|
||||
const fun3 = () => new Identity((() => true) as any)
|
||||
|
||||
expect(fun1).toThrow("Parameter 'identityOrMessage' is not a string")
|
||||
expect(fun2).toThrow("Parameter 'identityOrMessage' is not a string")
|
||||
expect(fun3).toThrow("Parameter 'identityOrMessage' is not a string")
|
||||
})
|
||||
|
||||
it("Should create random identities", () => {
|
||||
const identity1 = new Identity()
|
||||
const identity2 = new Identity()
|
||||
|
||||
expect(identity1.getTrapdoor()).not.toBe(identity2.getTrapdoor())
|
||||
expect(identity1.getNullifier()).not.toBe(identity2.getNullifier())
|
||||
})
|
||||
|
||||
it("Should create deterministic identities from a message", () => {
|
||||
const identity1 = new Identity("message")
|
||||
const identity2 = new Identity("message")
|
||||
|
||||
expect(identity1.getTrapdoor()).toBe(identity2.getTrapdoor())
|
||||
expect(identity1.getNullifier()).toBe(identity2.getNullifier())
|
||||
})
|
||||
|
||||
it("Should create deterministic identities from number/boolean messages", () => {
|
||||
const identity1 = new Identity("true")
|
||||
const identity2 = new Identity("true")
|
||||
const identity3 = new Identity("7")
|
||||
const identity4 = new Identity("7")
|
||||
|
||||
expect(identity1.getTrapdoor()).toBe(identity2.getTrapdoor())
|
||||
expect(identity1.getNullifier()).toBe(identity2.getNullifier())
|
||||
expect(identity3.getTrapdoor()).toBe(identity4.getTrapdoor())
|
||||
expect(identity3.getNullifier()).toBe(identity4.getNullifier())
|
||||
})
|
||||
|
||||
it("Should not recreate an existing invalid identity", () => {
|
||||
const fun = () => new Identity('[true, "01323"]')
|
||||
|
||||
expect(fun).toThrow("invalid BigNumber string")
|
||||
})
|
||||
|
||||
it("Should recreate an existing identity", () => {
|
||||
const identity1 = new Identity("message")
|
||||
|
||||
const identity2 = new Identity(identity1.toString())
|
||||
|
||||
expect(identity1.getTrapdoor()).toBe(identity2.getTrapdoor())
|
||||
expect(identity1.getNullifier()).toBe(identity2.getNullifier())
|
||||
})
|
||||
})
|
||||
|
||||
describe("# getTrapdoor", () => {
|
||||
it("Should return the identity trapdoor", () => {
|
||||
const identity = new Identity("message")
|
||||
|
||||
const trapdoor = identity.getTrapdoor()
|
||||
|
||||
expect(trapdoor).toBe(
|
||||
BigInt("58952291509798197436757858062402199043831251943841934828591473955215726495831")
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# getNullifier", () => {
|
||||
it("Should return the identity nullifier", () => {
|
||||
const identity = new Identity("message")
|
||||
|
||||
const nullifier = identity.getNullifier()
|
||||
|
||||
expect(nullifier).toBe(
|
||||
BigInt("44673097405870585416457571638073245190425597599743560105244308998175651589997")
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# generateCommitment", () => {
|
||||
it("Should generate an identity commitment", () => {
|
||||
const identity = new Identity("message")
|
||||
|
||||
const commitment = identity.generateCommitment()
|
||||
|
||||
expect(commitment).toBe(
|
||||
BigInt("1720349790382552497189398984241859233944354304766757200361065203741879866188")
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# toString", () => {
|
||||
it("Should return a string", () => {
|
||||
const identity = new Identity("message")
|
||||
|
||||
const identityString = identity.toString()
|
||||
|
||||
expect(typeof identityString).toBe("string")
|
||||
})
|
||||
|
||||
it("Should return a valid identity string", () => {
|
||||
const identity = new Identity("message")
|
||||
|
||||
const [trapdoor, nullifier] = JSON.parse(identity.toString())
|
||||
|
||||
expect(BigNumber.from(`0x${trapdoor}`).toBigInt()).toBe(identity.getTrapdoor())
|
||||
expect(BigNumber.from(`0x${nullifier}`).toBigInt()).toBe(identity.getNullifier())
|
||||
})
|
||||
})
|
||||
})
|
||||
71
packages/identity/src/identity.ts
Normal file
71
packages/identity/src/identity.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { BigNumber } from "@ethersproject/bignumber"
|
||||
import { poseidon } from "circomlibjs"
|
||||
import checkParameter from "./checkParameter"
|
||||
import { genRandomNumber, isJsonArray, sha256 } from "./utils"
|
||||
|
||||
export default class Identity {
|
||||
private _trapdoor: bigint
|
||||
private _nullifier: bigint
|
||||
|
||||
/**
|
||||
* Initializes the class attributes based on the strategy passed as parameter.
|
||||
* @param identityOrMessage Additional data needed to create identity for given strategy.
|
||||
*/
|
||||
constructor(identityOrMessage?: string) {
|
||||
if (identityOrMessage === undefined) {
|
||||
this._trapdoor = genRandomNumber()
|
||||
this._nullifier = genRandomNumber()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
checkParameter(identityOrMessage, "identityOrMessage", "string")
|
||||
|
||||
if (!isJsonArray(identityOrMessage)) {
|
||||
const messageHash = sha256(identityOrMessage).slice(2)
|
||||
|
||||
this._trapdoor = BigNumber.from(sha256(`${messageHash}identity_trapdoor`)).toBigInt()
|
||||
this._nullifier = BigNumber.from(sha256(`${messageHash}identity_nullifier`)).toBigInt()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const [trapdoor, nullifier] = JSON.parse(identityOrMessage)
|
||||
|
||||
this._trapdoor = BigNumber.from(`0x${trapdoor}`).toBigInt()
|
||||
this._nullifier = BigNumber.from(`0x${nullifier}`).toBigInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identity trapdoor.
|
||||
* @returns The identity trapdoor.
|
||||
*/
|
||||
public getTrapdoor(): bigint {
|
||||
return this._trapdoor
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identity nullifier.
|
||||
* @returns The identity nullifier.
|
||||
*/
|
||||
public getNullifier(): bigint {
|
||||
return this._nullifier
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the identity commitment from trapdoor and nullifier.
|
||||
* @returns identity commitment
|
||||
*/
|
||||
public generateCommitment(): bigint {
|
||||
return poseidon([poseidon([this._nullifier, this._trapdoor])])
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON string with trapdoor and nullifier. It can be used
|
||||
* to export the identity and reuse it later.
|
||||
* @returns The string representation of the identity.
|
||||
*/
|
||||
public toString(): string {
|
||||
return JSON.stringify([this._trapdoor.toString(16), this._nullifier.toString(16)])
|
||||
}
|
||||
}
|
||||
4
packages/identity/src/index.ts
Normal file
4
packages/identity/src/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import Identity from "./identity"
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export { Identity }
|
||||
37
packages/identity/src/utils.ts
Normal file
37
packages/identity/src/utils.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { BigNumber } from "@ethersproject/bignumber"
|
||||
import { randomBytes } from "@ethersproject/random"
|
||||
import { sha256 as _sha256 } from "@ethersproject/sha2"
|
||||
import { toUtf8Bytes } from "@ethersproject/strings"
|
||||
|
||||
/**
|
||||
* Returns an hexadecimal sha256 hash of the message passed as parameter.
|
||||
* @param message The string to hash.
|
||||
* @returns The hexadecimal hash of the message.
|
||||
*/
|
||||
export function sha256(message: string): string {
|
||||
const hash = _sha256(toUtf8Bytes(message))
|
||||
|
||||
return hash
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random big number.
|
||||
* @param numberOfBytes The number of bytes of the number.
|
||||
* @returns The generated random number.
|
||||
*/
|
||||
export function genRandomNumber(numberOfBytes = 31): bigint {
|
||||
return BigNumber.from(randomBytes(numberOfBytes)).toBigInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is a JSON.
|
||||
* @param jsonString The JSON string.
|
||||
* @returns True or false.
|
||||
*/
|
||||
export function isJsonArray(jsonString: string) {
|
||||
try {
|
||||
return Array.isArray(JSON.parse(jsonString))
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
4
packages/identity/tsconfig.json
Normal file
4
packages/identity/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src", "rollup.config.ts"]
|
||||
}
|
||||
21
packages/proof/LICENSE
Normal file
21
packages/proof/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ethereum Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
125
packages/proof/README.md
Normal file
125
packages/proof/README.md
Normal file
@@ -0,0 +1,125 @@
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
Semaphore proof
|
||||
</h1>
|
||||
<p align="center">A library to generate and verify Semaphore proofs.</p>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/semaphore-protocol">
|
||||
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/LICENSE">
|
||||
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/@semaphore-protocol/proof">
|
||||
<img alt="NPM version" src="https://img.shields.io/npm/v/@semaphore-protocol/proof?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/proof">
|
||||
<img alt="Downloads" src="https://img.shields.io/npm/dm/@semaphore-protocol/proof.svg?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://eslint.org/">
|
||||
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint" />
|
||||
</a>
|
||||
<a href="https://prettier.io/">
|
||||
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
<h4>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CONTRIBUTING.md">
|
||||
👥 Contributing
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
|
||||
🤝 Code of conduct
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://discord.gg/6mSdGHnstH">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
| This library provides utility functions to generate and verify Semaphore proofs compatible with the Semaphore [circuits](https://github.com/semaphore-protocol/semaphore/tree/main/circuits). Generating valid zero-knowledge proofs requires files that can only be obtained in an attested [trusted-setup ceremony](https://storage.googleapis.com/trustedsetup-a86f4.appspot.com/semaphore/semaphore_top_index.html). For a complete list of ready-to-use files visit [trusted-setup-pse.org](http://www.trusted-setup-pse.org/). |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
|
||||
## 🛠 Install
|
||||
|
||||
### npm or yarn
|
||||
|
||||
Install the `@semaphore-protocol/proof` package and its peer dependencies with npm:
|
||||
|
||||
```bash
|
||||
npm i @semaphore-protocol/identity @semaphore-protocol/group @semaphore-protocol/proof
|
||||
```
|
||||
|
||||
or yarn:
|
||||
|
||||
```bash
|
||||
yarn add @semaphore-protocol/identity @semaphore-protocol/group @semaphore-protocol/proof
|
||||
```
|
||||
|
||||
## 📜 Usage
|
||||
|
||||
\# **generateProof**(identity: _Identity_, group: _Group_, externalNullifier: _BigNumberish_, signal: _string_, snarkArtifacts?: _SnarkArtifacts_): Promise\<_SemaphoreFullProof_>
|
||||
|
||||
```typescript
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { generateProof } from "@semaphore-protocol/proof"
|
||||
|
||||
const identity = new Identity()
|
||||
const group = new Group()
|
||||
const externalNullifier = BigInt(1)
|
||||
const signal = "Hello world"
|
||||
|
||||
group.addMembers([...identityCommitments, identity.generateCommitment()])
|
||||
|
||||
const fullProof = await generateProof(identity, merkleProof, externalNullifier, signal, {
|
||||
zkeyFilePath: "./semaphore.zkey",
|
||||
wasmFilePath: "./semaphore.wasm"
|
||||
})
|
||||
|
||||
// You can also use the default zkey/wasm files (only for browsers!).
|
||||
// const fullProof = await generateProof(identity, merkleProof, externalNullifier, signal)
|
||||
```
|
||||
|
||||
\# **verifyProof**(verificationKey: _any_, fullProof: _FullProof_): Promise\<_boolean_>
|
||||
|
||||
```typescript
|
||||
import { verifyProof } from "@semaphore-protocol/proof"
|
||||
|
||||
const verificationKey = JSON.parse(fs.readFileSync("/semaphore.json", "utf-8"))
|
||||
|
||||
await verifyProof(verificationKey, fullProof)
|
||||
```
|
||||
|
||||
\# **packToSolidityProof**(proof: _Proof_): _SolidityProof_
|
||||
|
||||
```typescript
|
||||
import { packToSolidityProof } from "@semaphore-protocol/proof"
|
||||
|
||||
const solidityProof = packToSolidityProof(fullProof.proof)
|
||||
```
|
||||
|
||||
\# **generateNullifierHash**(externalNullifier: _BigNumberish_, identityNullifier: _BigNumberish_): _bigint_
|
||||
|
||||
```typescript
|
||||
import { generateNullifierHash } from "@semaphore-protocol/proof"
|
||||
|
||||
const nullifierHash = generateNullifierHash(externalNullifier, identity.getNullifier())
|
||||
```
|
||||
|
||||
\# **generateSignalHash**(signal: _string_): _bigint_
|
||||
|
||||
```typescript
|
||||
import { generateSignalHash } from "@semaphore-protocol/proof"
|
||||
|
||||
const signalHash = generateSignalHash(signal)
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user