chore: create semaphore monorepo

This commit is contained in:
cedoor
2022-09-16 16:50:21 +02:00
parent 5fa34109c6
commit a38dd20276
127 changed files with 8564 additions and 1445 deletions

View File

@@ -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
View 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.

View File

@@ -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 }}

View File

@@ -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 }}

View File

@@ -10,7 +10,7 @@ env:
TREE_DEPTH: 20
jobs:
test:
install:
runs-on: ubuntu-latest
steps:
@@ -35,13 +35,52 @@ jobs:
${{ runner.os }}-yarn-
- name: Install dependencies
run: YARN_CHECKSUM_BEHAVIOR=ignore yarn
run: yarn
- name: Download Snark artifacts
run: yarn download:snark-artifacts
style:
needs: install
runs-on: ubuntu-latest
steps:
- name: Run Prettier
run: yarn prettier
- name: Run Eslint
run: yarn lint
- name: Compile contracts
run: yarn compile
run: yarn compile:contracts
- name: Test contracts with coverage
run: yarn test:coverage
- name: Build libraries
run: yarn build:libs
test:
needs: style
runs-on: ubuntu-latest
strategy:
matrix:
type:
- libraries
- contracts
steps:
- 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
finish:
needs: test
runs-on: ubuntu-latest
steps:
- name: Coveralls Finished
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true

15
.gitignore vendored
View File

@@ -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
@@ -56,6 +65,9 @@ build
dist
deployed-contracts/undefined.json
deployed-contracts/localhost.json
docs/*
!docs/CNAME
!docs/index.html
# Hardhat
artifacts
@@ -72,3 +84,6 @@ cache
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# Other
snark-artifacts

View File

@@ -18,7 +18,7 @@ types
circuits
# contracts
contracts/verifiers
Verifier*.sol
# production
dist
@@ -35,3 +35,6 @@ build
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# other
snark-artifacts

File diff suppressed because one or more lines are too long

View File

@@ -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

202
README.md
View File

@@ -25,6 +25,9 @@
<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 +60,132 @@
| 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](/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="https://github.com/semaphore-protocol/semaphore/tree/main/packages/contracts/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="https://github.com/semaphore-protocol/semaphore/tree/main/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="https://github.com/semaphore-protocol/semaphore/tree/main/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="https://github.com/semaphore-protocol/semaphore/tree/main/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="https://github.com/semaphore-protocol/semaphore/tree/main/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 +194,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
@@ -79,14 +202,6 @@ cd semaphore && yarn
## 📜 Usage
Copy the `.env.example` file as `.env`:
```bash
cp .env.example .env
```
and add your environment variables.
### Code quality and formatting
Run [ESLint](https://eslint.org/) to analyze the code and catch bugs:
@@ -101,7 +216,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
@@ -117,67 +232,46 @@ yarn commit
It will also automatically check that the modified files comply with ESLint and Prettier rules.
### Snark artifacts
Download the Semaphore snark artifacts needed to generate and verify proofs:
```bash
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 packages & 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
View File

@@ -0,0 +1,3 @@
{
"presets": [["@babel/preset-env", { "targets": { "node": "current" } }], "@babel/preset-typescript"]
}

123
docs/index.html Normal file
View 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>

28
jest.config.ts Normal file
View File

@@ -0,0 +1,28 @@
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,
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
}
}
})

View File

@@ -1,104 +1,78 @@
{
"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",
"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"
},
"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",
"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"
}
}

View File

@@ -0,0 +1,7 @@
{
"name": "circuits",
"private": true,
"dependencies": {
"circomlib": "^2.0.2"
}
}

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,4 +1,5 @@
module.exports = {
istanbulFolder: "../../coverage/contracts",
skipFiles: [
"verifiers/Verifier16.sol",
"verifiers/Verifier17.sol",

View File

@@ -0,0 +1,114 @@
<p align="center">
<h1 align="center">
Semaphore contracts
</h1>
<p align="center">Semaphore contracts to manage groups and broadcast anonymous signals.</p>
</p>
<p align="center">
<a href="https://github.com/semaphore-protocol">
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
</a>
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/LICENSE">
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
</a>
</p>
<div align="center">
<h4>
<a href="/CONTRIBUTING.md">
👥 Contributing
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="/CODE_OF_CONDUCT.md">
🤝 Code of conduct
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
🔎 Issues
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://discord.gg/6mSdGHnstH">
🗣️ Chat &amp; Support
</a>
</h4>
</div>
To learn more about contracts visit [semaphore.appliedzkp.org](https://semaphore.appliedzkp.org/docs/technical-reference/contracts).
---
## 📜 Usage
Copy the `.env.example` file as `.env`:
```bash
cp .env.example .env
```
And add your environment variables.
### Snark artifacts
Download the Semaphore snark artifacts needed to generate and verify proofs:
```bash
yarn download:snark-artifacts
```
### 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.

View File

@@ -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">

View File

@@ -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"
},

View File

@@ -0,0 +1,74 @@
[
{
"name": "Verifier16",
"address": "0x5FbDB2315678afecb367f032d93F642f64180aa3"
},
{
"name": "Verifier17",
"address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"
},
{
"name": "Verifier18",
"address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"
},
{
"name": "Verifier19",
"address": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"
},
{
"name": "Verifier20",
"address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"
},
{
"name": "Verifier21",
"address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707"
},
{
"name": "Verifier22",
"address": "0x0165878A594ca255338adfa4d48449f69242Eb8F"
},
{
"name": "Verifier23",
"address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853"
},
{
"name": "Verifier24",
"address": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6"
},
{
"name": "Verifier25",
"address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318"
},
{
"name": "Verifier26",
"address": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788"
},
{
"name": "Verifier27",
"address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e"
},
{
"name": "Verifier28",
"address": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0"
},
{
"name": "Verifier29",
"address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82"
},
{
"name": "Verifier30",
"address": "0x9A676e781A523b5d0C0e43731313A708CB607508"
},
{
"name": "Verifier31",
"address": "0x0B306BF915C4d645ff596e518fAf3F9669b97016"
},
{
"name": "Verifier32",
"address": "0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1"
},
{
"name": "Semaphore",
"address": "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c"
}
]

View File

@@ -0,0 +1,74 @@
[
{
"name": "Verifier16",
"address": "0x5FbDB2315678afecb367f032d93F642f64180aa3"
},
{
"name": "Verifier17",
"address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"
},
{
"name": "Verifier18",
"address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"
},
{
"name": "Verifier19",
"address": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"
},
{
"name": "Verifier20",
"address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"
},
{
"name": "Verifier21",
"address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707"
},
{
"name": "Verifier22",
"address": "0x0165878A594ca255338adfa4d48449f69242Eb8F"
},
{
"name": "Verifier23",
"address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853"
},
{
"name": "Verifier24",
"address": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6"
},
{
"name": "Verifier25",
"address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318"
},
{
"name": "Verifier26",
"address": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788"
},
{
"name": "Verifier27",
"address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e"
},
{
"name": "Verifier28",
"address": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0"
},
{
"name": "Verifier29",
"address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82"
},
{
"name": "Verifier30",
"address": "0x9A676e781A523b5d0C0e43731313A708CB607508"
},
{
"name": "Verifier31",
"address": "0x0B306BF915C4d645ff596e518fAf3F9669b97016"
},
{
"name": "Verifier32",
"address": "0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1"
},
{
"name": "Semaphore",
"address": "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c"
}
]

View File

@@ -17,22 +17,24 @@ import "./tasks/deploy-verifier"
dotenvConfig({ path: resolve(__dirname, "./.env") })
function getNetworks(): NetworksUserConfig | undefined {
if (process.env.INFURA_API_KEY && process.env.BACKEND_PRIVATE_KEY) {
const infuraApiKey = process.env.INFURA_API_KEY
const accounts = [`0x${process.env.BACKEND_PRIVATE_KEY}`]
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
}
}
}

View File

@@ -0,0 +1,73 @@
{
"name": "contracts",
"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": "yarn download:snark-artifacts && hardhat test",
"test:report-gas": "yarn download:snark-artifacts && REPORT_GAS=true hardhat test",
"test:coverage": "yarn download:snark-artifacts && 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": "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",
"@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",
"dotenv": "^14.3.2",
"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": {
"snark-artifacts": "./build/snark-artifacts",
"contracts": "./build/contracts",
"typechain": "./build/typechain"
}
}
},
"dependencies": {
"@openzeppelin/contracts": "4.4.2",
"@zk-kit/incremental-merkle-tree.sol": "1.3.0"
}
}

View File

@@ -5,7 +5,7 @@ async function main() {
const deployedContracts: { name: string; address: string }[] = []
// Deploy verifiers.
for (let treeDepth = 16; treeDepth <= 32; treeDepth++) {
for (let treeDepth = 16; treeDepth <= 32; treeDepth = +1) {
const { address } = await run("deploy:verifier", { depth: treeDepth })
deployedContracts.push({

View File

@@ -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())
}
}

View File

@@ -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
})

View File

@@ -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
})

View File

@@ -16,7 +16,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 +29,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 +43,9 @@ 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}`)
}
return contract
})

View File

@@ -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
})

View File

@@ -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"
@@ -109,12 +111,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 +128,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 +159,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()")
})

View File

@@ -1,3 +1,4 @@
/* eslint-disable jest/valid-expect */
import { Identity } from "@semaphore-protocol/identity"
import { Group } from "@semaphore-protocol/group"
import {

View File

@@ -1,3 +1,4 @@
/* eslint-disable jest/valid-expect */
import { Group } from "@semaphore-protocol/group"
import { Identity } from "@semaphore-protocol/identity"
import {

View File

@@ -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()

View File

@@ -0,0 +1,15 @@
{
"compilerOptions": {
"moduleResolution": "Node",
"noImplicitAny": true,
"resolveJsonModule": true,
"target": "ES2018",
"module": "CommonJS",
"strict": true,
"esModuleInterop": true,
"outDir": "dist",
"typeRoots": ["node_modules/@types", "../../types"]
},
"include": ["scripts/**/*", "tasks/**/*", "test/**/*", "build/typechain/**/*", "types/**/*"],
"files": ["hardhat.config.ts"]
}

21
packages/group/LICENSE Normal file
View 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
View 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>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
🤝 Code of conduct
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
🔎 Issues
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://discord.gg/6mSdGHnstH">
🗣️ Chat &amp; 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)
```

View File

@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"declarationDir": "dist/types"
},
"include": ["src"]
}

View File

@@ -0,0 +1,41 @@
{
"name": "@semaphore-protocol/group",
"version": "2.2.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"
}
}

View 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" })
]
}

View 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
View 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
}
}

View File

@@ -0,0 +1,4 @@
import Group from "./group"
export { Group }
export * from "./types"

View File

@@ -0,0 +1 @@
export type Member = string | bigint

View File

@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["src", "rollup.config.ts"]
}

21
packages/identity/LICENSE Normal file
View 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
View 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>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
🤝 Code of conduct
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
🔎 Issues
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://discord.gg/6mSdGHnstH">
🗣️ Chat &amp; 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()
```

View File

@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"declarationDir": "dist/types"
},
"include": ["src"]
}

View File

@@ -0,0 +1,44 @@
{
"name": "@semaphore-protocol/identity",
"version": "2.0.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"
}
}

View 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" })
]
}

View 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}`)
}
}

View 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())
})
})
})

View 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)])
}
}

View File

@@ -0,0 +1,4 @@
import Identity from "./identity"
// eslint-disable-next-line import/prefer-default-export
export { Identity }

View 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
}
}

View File

@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["src", "rollup.config.ts"]
}

21
packages/proof/LICENSE Normal file
View 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
View 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>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
🤝 Code of conduct
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
🔎 Issues
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://discord.gg/6mSdGHnstH">
🗣️ Chat &amp; 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 with npm:
```bash
npm i @semaphore-protocol/proof
```
or yarn:
```bash
yarn add @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)
```

View File

@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"declarationDir": "dist/types"
},
"include": ["src"]
}

View File

@@ -0,0 +1,49 @@
{
"name": "@semaphore-protocol/proof",
"version": "2.3.1",
"description": "A library to generate and verify Semaphore proofs.",
"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/proof",
"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/proof"
},
"publishConfig": {
"access": "public"
},
"devDependencies": {
"download": "^8.0.0",
"ffjavascript": "^0.2.54",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-typescript2": "^0.31.2",
"typedoc": "^0.22.11"
},
"dependencies": {
"@ethersproject/bytes": "^5.7.0",
"@ethersproject/solidity": "^5.5.0",
"@ethersproject/strings": "^5.5.0",
"@semaphore-protocol/group": "2.2.0",
"@semaphore-protocol/identity": "2.0.0",
"@zk-kit/incremental-merkle-tree": "0.4.3",
"circomlibjs": "0.0.8",
"snarkjs": "^0.4.13"
}
}

View 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" })
]
}

Some files were not shown because too many files have changed in this diff Show More