mirror of
https://github.com/semaphore-protocol/semaphore.git
synced 2026-01-12 08:07:55 -05:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
236e6b241c | ||
|
|
e259482ad8 | ||
|
|
76101a0c54 | ||
|
|
8489d149c7 | ||
|
|
dc0298599b | ||
|
|
55fa4203d2 | ||
|
|
1cd0e5f9d7 | ||
|
|
08961c0197 | ||
|
|
bbe13aa12a | ||
|
|
d5a9ba4b60 | ||
|
|
ba5f2a038f | ||
|
|
375b79628e | ||
|
|
242924fff4 | ||
|
|
18e0afc711 | ||
|
|
ab9a0e84f3 | ||
|
|
be42bcf8eb | ||
|
|
b3fc357ecf | ||
|
|
5ba953ecd0 | ||
|
|
13121a907c | ||
|
|
648bf9a097 | ||
|
|
24340b0692 |
85
.circleci/config.yml
Normal file
85
.circleci/config.yml
Normal file
@@ -0,0 +1,85 @@
|
||||
# Javascript Node CircleCI 2.0 configuration file
|
||||
#
|
||||
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
|
||||
#
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
# specify the version you desire here
|
||||
- image: circleci/node:11.14.0
|
||||
|
||||
working_directory: ~/semaphore_private/
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/semaphore_private/
|
||||
|
||||
- run:
|
||||
name: Install solc
|
||||
command: wget https://github.com/ethereum/solidity/releases/download/v0.5.12/solc-static-linux && chmod a+x solc-static-linux && sudo mv solc-static-linux /usr/bin/solc
|
||||
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1.9-dependencies-{{ checksum "package-lock.json" }}
|
||||
|
||||
- run: npm install
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- node_modules
|
||||
key: v1.9-dependencies-{{ checksum "package-lock.json" }}
|
||||
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1.9-dependencies-{{ checksum "contracts/package-lock.json" }}-{{ checksum "circuits/package-lock.json" }}-{{ checksum "config/package-lock.json" }}
|
||||
|
||||
- run: npm run bootstrap && npm run build
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- contracts/node_modules
|
||||
- config/node_modules
|
||||
- circuits/node_modules
|
||||
key: v1.8-dependencies-{{ checksum "contracts/package-lock.json" }}-{{ checksum "circuits/package-lock.json" }}-{{ checksum "config/package-lock.json" }}
|
||||
|
||||
# checksum the snarks definitions
|
||||
- run:
|
||||
name: Checksum snark files
|
||||
command: cd circuits/ && ./scripts/checksum_snarks.sh
|
||||
|
||||
- restore_cache:
|
||||
name: restore-snark-cache
|
||||
keys:
|
||||
- v1.9-dependencies-{{ checksum "circuits/build/.snark_checksum" }}
|
||||
|
||||
# build snarks
|
||||
- run:
|
||||
name: Build snark files
|
||||
command: cd circuits && ./scripts/build_snarks.sh
|
||||
no_output_timeout: 600m
|
||||
|
||||
# cache generated snark circuit and keys
|
||||
- save_cache:
|
||||
key: v1.9-dependencies-{{ checksum "circuits/build/.snark_checksum" }}
|
||||
paths:
|
||||
- circuits/build/circuit.json
|
||||
- circuits/build/proving_key.bin
|
||||
- circuits/build/proving_key.json
|
||||
- circuits/build/verification_key.json
|
||||
- circuits/build/verifier.sol
|
||||
|
||||
- run:
|
||||
name: Compile contracts
|
||||
command: cd contracts && npm run compileSol
|
||||
|
||||
- run:
|
||||
name: Run circuit tests
|
||||
command: cd circuits && ./scripts/runTestsInCircleCi.sh
|
||||
|
||||
- run:
|
||||
name: Run contract tests
|
||||
command: cd contracts && ./scripts/runTestsInCircleCi.sh
|
||||
|
||||
- store_artifacts:
|
||||
path: circuits/build
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": ["@commitlint/config-conventional"]
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 120
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -1,8 +0,0 @@
|
||||
DEFAULT_NETWORK=hardhat
|
||||
TREE_DEPTH=20
|
||||
ALL_SNARK_ARTIFACTS=true
|
||||
REPORT_GAS=false
|
||||
BACKEND_PRIVATE_KEY=
|
||||
INFURA_API_KEY=
|
||||
COINMARKETCAP_API_KEY=
|
||||
ETHERSCAN_API_KEY=
|
||||
@@ -1,37 +0,0 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.yarn
|
||||
|
||||
# testing
|
||||
coverage
|
||||
coverage.json
|
||||
|
||||
# hardhat
|
||||
artifacts
|
||||
cache
|
||||
typechain-types
|
||||
|
||||
# types
|
||||
types
|
||||
|
||||
# circuits
|
||||
circuits
|
||||
|
||||
# production
|
||||
dist
|
||||
build
|
||||
docs
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# packages
|
||||
cli-template-*
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"es6": true
|
||||
},
|
||||
"extends": ["airbnb-base", "airbnb-typescript/base", "plugin:jest/recommended", "plugin:jest/style", "prettier"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"project": ["./tsconfig.json", "./packages/**/tsconfig.json"]
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
18
.github/ISSUE_TEMPLATE/----network.md
vendored
18
.github/ISSUE_TEMPLATE/----network.md
vendored
@@ -1,18 +0,0 @@
|
||||
---
|
||||
name: "\U0001F578️ Network"
|
||||
about: Propose a new network in which to deploy Semaphore contracts
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Provide data related to the network**
|
||||
|
||||
* Name
|
||||
* Chain id
|
||||
* URL
|
||||
|
||||
**Why are you using this network?**
|
||||
|
||||
Describe the reasons why you think it is important for Semaphore to be deployed on this network.
|
||||
22
.github/ISSUE_TEMPLATE/----project.md
vendored
22
.github/ISSUE_TEMPLATE/----project.md
vendored
@@ -1,22 +0,0 @@
|
||||
---
|
||||
name: " \U0001F4A0 Project"
|
||||
about: If you are using Semaphore we can help you share your project
|
||||
title: ''
|
||||
labels: "documentation \U0001F4D6"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe your project**
|
||||
A brief description of your project. In what way have you used Semaphore?
|
||||
|
||||
**Other info**
|
||||
|
||||
- Name
|
||||
- Icon
|
||||
|
||||
**Links**
|
||||
|
||||
- Website
|
||||
- Github
|
||||
- Socials
|
||||
34
.github/ISSUE_TEMPLATE/---bug.md
vendored
34
.github/ISSUE_TEMPLATE/---bug.md
vendored
@@ -1,34 +0,0 @@
|
||||
---
|
||||
name: "\U0001F41E Bug"
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: "bug \U0001F41B"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Technologies (please complete the following information):**
|
||||
|
||||
- Node.js version
|
||||
- NPM version
|
||||
- Solidity version
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
.github/ISSUE_TEMPLATE/---feature.md
vendored
20
.github/ISSUE_TEMPLATE/---feature.md
vendored
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: "\U0001F680 Feature"
|
||||
about: Suggest an idea for Semaphore
|
||||
title: ''
|
||||
labels: 'feature :rocket:'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
14
.github/ISSUE_TEMPLATE/---package.md
vendored
14
.github/ISSUE_TEMPLATE/---package.md
vendored
@@ -1,14 +0,0 @@
|
||||
---
|
||||
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.
|
||||
24
.github/pull_request_template.md
vendored
24
.github/pull_request_template.md
vendored
@@ -1,24 +0,0 @@
|
||||
<!-- Please refer to our contributing documentation for any questions on submitting a pull request -->
|
||||
<!--- Provide a general summary of your changes in the Title above -->
|
||||
|
||||
## Description
|
||||
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
## Related Issue
|
||||
|
||||
<!--- This project accepts pull requests related to open issues -->
|
||||
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
|
||||
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
|
||||
<!--- Please link to the issue here: -->
|
||||
|
||||
## Does this introduce a breaking change?
|
||||
|
||||
- [ ] Yes
|
||||
- [ ] No
|
||||
|
||||
<!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. -->
|
||||
|
||||
## Other information
|
||||
|
||||
<!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. -->
|
||||
14
.github/workflows/auto-assign.yml
vendored
14
.github/workflows/auto-assign.yml
vendored
@@ -1,14 +0,0 @@
|
||||
name: auto-assign
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: wow-actions/auto-assign@v3
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
reviewers: org/core-devs
|
||||
45
.github/workflows/docs.yml
vendored
45
.github/workflows/docs.yml
vendored
@@ -1,45 +0,0 @@
|
||||
name: docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
gh-pages:
|
||||
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: Generate doc website
|
||||
run: yarn docs
|
||||
|
||||
- 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 }}
|
||||
107
.github/workflows/production.yml
vendored
107
.github/workflows/production.yml
vendored
@@ -1,107 +0,0 @@
|
||||
name: production
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
TREE_DEPTH: 20
|
||||
ALL_SNARK_ARTIFACTS: false
|
||||
|
||||
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
|
||||
81
.github/workflows/pull-requests.yml
vendored
81
.github/workflows/pull-requests.yml
vendored
@@ -1,81 +0,0 @@
|
||||
name: pull-requests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TREE_DEPTH: 20
|
||||
ALL_SNARK_ARTIFACTS: false
|
||||
|
||||
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
|
||||
43
.github/workflows/release.yml
vendored
43
.github/workflows/release.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: release
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- run: yarn version:release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
99
.gitignore
vendored
99
.gitignore
vendored
@@ -1,91 +1,8 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# IDE
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
coverage.json
|
||||
*.lcov
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
.DS_Store
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# Production
|
||||
build
|
||||
dist
|
||||
docs/*
|
||||
!docs/CNAME
|
||||
!docs/index.html
|
||||
|
||||
# Hardhat
|
||||
artifacts
|
||||
cache
|
||||
typechain-types
|
||||
packages/contracts/deployed-contracts/undefined.json
|
||||
packages/contracts/deployed-contracts/hardhat.json
|
||||
packages/contracts/deployed-contracts/localhost.json
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v3
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
# Other
|
||||
snark-artifacts
|
||||
docs_src/book/
|
||||
.etherlime-store
|
||||
**/build/
|
||||
circuits/build
|
||||
contracts/compiled
|
||||
node_modules
|
||||
blake2sdef.json
|
||||
build/.snark_checksum
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install commitlint --edit $1
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"**/*.{js,ts}": ["prettier --write", "eslint --fix"]
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.yarn
|
||||
|
||||
# testing
|
||||
coverage
|
||||
coverage.json
|
||||
|
||||
# hardhat
|
||||
artifacts
|
||||
cache
|
||||
typechain-types
|
||||
packages/contracts/deployed-contracts/undefined.json
|
||||
packages/contracts/deployed-contracts/hardhat.json
|
||||
packages/contracts/deployed-contracts/localhost.json
|
||||
|
||||
# circuits
|
||||
circuits
|
||||
|
||||
# contracts
|
||||
Verifier*.sol
|
||||
|
||||
# production
|
||||
dist
|
||||
build
|
||||
docs
|
||||
|
||||
# github
|
||||
.github/ISSUE_TEMPLATE
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# other
|
||||
snark-artifacts
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"semi": false,
|
||||
"arrowParens": "always",
|
||||
"trailingComma": "none"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
diff --git a/dist/shared/changelogithub.821fab93.mjs b/dist/shared/changelogithub.821fab93.mjs
|
||||
index 5fc2100867613c20f7827eac8715a5fc28bdc39e..97bd8dff878b81c63d2220e496904f6f3933589a 100644
|
||||
--- a/dist/shared/changelogithub.821fab93.mjs
|
||||
+++ b/dist/shared/changelogithub.821fab93.mjs
|
||||
@@ -181,7 +181,7 @@ function formatLine(commit, options) {
|
||||
function formatTitle(name, options) {
|
||||
if (!options.emoji)
|
||||
name = name.replace(emojisRE, "");
|
||||
- return `### ${name.trim()}`;
|
||||
+ return `## ${name.trim()}`;
|
||||
}
|
||||
function formatSection(commits, sectionName, options) {
|
||||
if (!commits.length)
|
||||
@@ -198,7 +198,8 @@ function formatSection(commits, sectionName, options) {
|
||||
Object.keys(scopes).sort().forEach((scope) => {
|
||||
let padding = "";
|
||||
let prefix = "";
|
||||
- const scopeText = `**${options.scopeMap[scope] || scope}**`;
|
||||
+ const url = `https://github.com/${options.github}/tree/main/packages/${scope}`
|
||||
+ const scopeText = `[**@${options.github.split("/")[0]}/${options.scopeMap[scope] || scope}**](${url})`
|
||||
if (scope && useScopeGroup) {
|
||||
lines.push(`- ${scopeText}:`);
|
||||
padding = " ";
|
||||
@@ -1 +0,0 @@
|
||||
87de4f440a77841135f97a187e09140c6d4e6ae2
|
||||
File diff suppressed because one or more lines are too long
1
.yarn/releases/yarn-3.2.1.cjs.REMOVED.git-id
vendored
1
.yarn/releases/yarn-3.2.1.cjs.REMOVED.git-id
vendored
@@ -1 +0,0 @@
|
||||
b3cadff6efb37a12712d12c2553ec703dbcaa4dd
|
||||
11
.yarnrc.yml
11
.yarnrc.yml
@@ -1,11 +0,0 @@
|
||||
checksumBehavior: update
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||
spec: "@yarnpkg/plugin-workspace-tools"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
|
||||
spec: "@yarnpkg/plugin-version"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.1.cjs
|
||||
@@ -1,127 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
111
CONTRIBUTING.md
111
CONTRIBUTING.md
@@ -1,111 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
:tada: Thank you for being interested in contributing to the Semaphore project! :tada:
|
||||
|
||||
Feel welcome and read the following sections in order to know how to ask questions and how to work on something.
|
||||
|
||||
All members of our community are expected to follow our [Code of Conduct](/CODE_OF_CONDUCT.md). Please make sure you are welcoming and friendly in all of our spaces.
|
||||
|
||||
We're really glad you're reading this, because we need volunteer developers to help this project come to fruition. 👏
|
||||
|
||||
## Issues
|
||||
|
||||
The best way to contribute to our projects is by opening a [new issue](https://github.com/semaphore-protocol/semaphore/issues/new/choose) or tackling one of the issues listed [here](https://github.com/semaphore-protocol/semaphore/contribute).
|
||||
|
||||
## Pull Requests
|
||||
|
||||
Pull requests are great if you want to add a feature or fix a bug. Here's a quick guide:
|
||||
|
||||
1. Fork the repo.
|
||||
|
||||
2. Run the tests. We only take pull requests with passing tests.
|
||||
|
||||
3. Add a test for your change. Only refactoring and documentation changes require no new tests.
|
||||
|
||||
4. Make sure to check out the [Style Guide](/CONTRIBUTING#style-guide) and ensure that your code complies with the rules.
|
||||
|
||||
5. Make the test pass.
|
||||
|
||||
6. Commit your changes.
|
||||
|
||||
7. Push to your fork and submit a pull request on our `dev` branch. Please provide us with some explanation of why you made the changes you made. For new features make sure to explain a standard use case to us.
|
||||
|
||||
## CI (Github Actions) Tests
|
||||
|
||||
We use GitHub Actions to test each PR before it is merged.
|
||||
|
||||
When you submit your PR (or later change that code), a CI build will automatically be kicked off. A note will be added to the PR, and will indicate the current status of the build.
|
||||
|
||||
## Style Guide
|
||||
|
||||
### Code rules
|
||||
|
||||
We always use ESLint and Prettier. To check that your code follows the rules, simply run the npm script `yarn lint`.
|
||||
|
||||
### Commits rules
|
||||
|
||||
For commits it is recommended to use [Conventional Commits](https://www.conventionalcommits.org).
|
||||
|
||||
Don't worry if it looks complicated, in our repositories, after `git add`, you can usually run the npm script `yarn commit` to make many of these steps interactive.
|
||||
|
||||
Each commit message consists of a **header**, a **body** and a **footer**. The **header** has a special format that includes a **type**, a **scope** and a **subject**:
|
||||
|
||||
<type>(<scope>): <subject>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
|
||||
The **header** is mandatory and the **scope** of the header must contain the name of the package you are working on.
|
||||
|
||||
#### Type
|
||||
|
||||
The type must be one of the following:
|
||||
|
||||
- feat: A new feature
|
||||
- fix: A bug fix
|
||||
- docs: Documentation only changes
|
||||
- style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
- refactor: A code change that neither fixes a bug nor adds a feature (improvements of the code structure)
|
||||
- perf: A code change that improves the performance
|
||||
- test: Adding missing or correcting existing tests
|
||||
- build: Changes that affect the build system or external dependencies (example scopes: gulp, npm)
|
||||
- ci: Changes to CI configuration files and scripts (example scopes: travis, circle)
|
||||
- chore: Other changes that don't modify src or test files
|
||||
- revert: Reverts a previous commit
|
||||
|
||||
#### Scope
|
||||
|
||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
|
||||
|
||||
#### Subject
|
||||
|
||||
The subject contains a succinct description of the change:
|
||||
|
||||
- Use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
- Don't capitalize the first letter
|
||||
- No dot (.) at the end
|
||||
|
||||
#### Body
|
||||
|
||||
Just as in the subject, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
|
||||
|
||||
### Branch rules
|
||||
|
||||
- There must be a `main` branch, used only for the releases.
|
||||
- There must be a `dev` branch, used to merge all the branches under it.
|
||||
- Avoid long descriptive names for long-lived branches.
|
||||
- Use kebab-case (no CamelCase).
|
||||
- Use grouping tokens (words) at the beginning of your branch names (in a similar way to the `type` of commit).
|
||||
- Define and use short lead tokens to differentiate branches in a way that is meaningful to your workflow.
|
||||
- Use slashes to separate parts of your branch names.
|
||||
- Remove branch after merge if it is not important.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
git branch -b docs/readme
|
||||
git branch -b test/a-feature
|
||||
git branch -b feat/sidebar
|
||||
git branch -b fix/b-feature
|
||||
```
|
||||
21
LICENSE
21
LICENSE
@@ -1,21 +0,0 @@
|
||||
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.
|
||||
334
README.md
334
README.md
@@ -1,328 +1,12 @@
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/semaphore-protocol/website/blob/main/static/img/semaphore-icon-dark.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://github.com/semaphore-protocol/website/blob/main/static/img/semaphore-icon.svg">
|
||||
<img width="40" alt="Semaphore icon." src="https://github.com/semaphore-protocol/website/blob/main/static/img/semaphore-icon.svg">
|
||||
</picture>
|
||||
Semaphore
|
||||
</h1>
|
||||
</p>
|
||||
# Semaphore
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/semaphore-protocol" target="_blank">
|
||||
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="/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%3Aproduction">
|
||||
<img alt="GitHub Workflow test" src="https://img.shields.io/github/actions/workflow/status/semaphore-protocol/semaphore/production.yml?branch=main&label=test&style=flat-square&logo=github">
|
||||
</a>
|
||||
<a href="https://coveralls.io/github/semaphore-protocol/semaphore">
|
||||
<img alt="Coveralls" src="https://img.shields.io/coveralls/github/semaphore-protocol/semaphore?style=flat-square&logo=coveralls">
|
||||
</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>
|
||||
<a href="https://prettier.io/">
|
||||
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier">
|
||||
</a>
|
||||
<img alt="Repository top language" src="https://img.shields.io/github/languages/top/semaphore-protocol/semaphore?style=flat-square">
|
||||
<a href="https://www.gitpoap.io/gh/semaphore-protocol/semaphore" target="_blank">
|
||||
<img src="https://public-api.gitpoap.io/v1/repo/semaphore-protocol/semaphore/badge">
|
||||
</a>
|
||||
Semaphore is a zero-knowledge gadget which allows users to prove their
|
||||
membership of a set without revealing their original identity. At the same
|
||||
time, it allows users to signal their endorsement of an arbitrary string. It is
|
||||
designed to be a simple and generic privacy layer for Ethereum dApps. Use cases
|
||||
include private voting, whistleblowing, mixers, and anonymous authentication.
|
||||
|
||||
</p>
|
||||
For more information, refer to the
|
||||
[documentation](https://appliedzkp.github.io/semaphore/).
|
||||
|
||||
<div align="center">
|
||||
<h4>
|
||||
<a href="/CONTRIBUTING.md">
|
||||
👥 Contributing
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="/CODE_OF_CONDUCT.md">
|
||||
🤝 Code of conduct
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://semaphore.appliedzkp.org/discord">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
| Semaphore is a protocol, designed to be a simple and generic privacy layer for Ethereum DApps. Using zero knowledge, Ethereum users can prove their membership of a group and send signals such as votes or endorsements without revealing their original identity. |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
The core of the Semaphore protocol is in the [circuit logic](/packages/circuits/scheme.png). However Semaphore also provides [Solidity contracts](/packages/contracts) 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).
|
||||
|
||||
---
|
||||
|
||||
## 📦 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/data">
|
||||
@semaphore-protocol/data
|
||||
</a>
|
||||
<a href="https://semaphore-protocol.github.io/semaphore/data">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/data">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/data.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/data">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/data.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/hardhat">
|
||||
@semaphore-protocol/hardhat
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/hardhat">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/hardhat.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/hardhat">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/hardhat.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/cli">
|
||||
@semaphore-protocol/cli
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/cli">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/cli.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/cli">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/cli.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
|
||||
</table>
|
||||
|
||||
## 🛠 Install
|
||||
|
||||
Clone this repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/semaphore-protocol/semaphore.git
|
||||
```
|
||||
|
||||
And install the dependencies:
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
Run [Prettier](https://prettier.io/) to check formatting rules:
|
||||
|
||||
```bash
|
||||
yarn prettier
|
||||
```
|
||||
|
||||
Or to automatically format the code:
|
||||
|
||||
```bash
|
||||
yarn prettier:write
|
||||
```
|
||||
|
||||
### Conventional commits
|
||||
|
||||
Semaphore uses [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). A [command line utility](https://github.com/commitizen/cz-cli) to commit using the correct syntax can be used by running:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### Build libraries & compile contracts
|
||||
|
||||
Run [Rollup](https://www.rollupjs.org) to build all the packages:
|
||||
|
||||
```bash
|
||||
yarn build:libraries
|
||||
```
|
||||
|
||||
Compile the smart contracts with [Hardhat](https://hardhat.org/):
|
||||
|
||||
```bash
|
||||
yarn compile:contracts
|
||||
```
|
||||
|
||||
### Documentation (JS libraries)
|
||||
|
||||
Run [TypeDoc](https://typedoc.org/) to generate a documentation website for each package:
|
||||
|
||||
```bash
|
||||
yarn docs
|
||||
```
|
||||
|
||||
The output will be placed on the `docs` folder.
|
||||
Join the [Telegram group](https://t.me/joinchat/B-PQx1U3GtAh--Z4Fwo56A) to discuss.
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"presets": [["@babel/preset-env", { "targets": { "node": "current" } }], "@babel/preset-typescript"]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"types": {
|
||||
"feat": { "title": "🚀 Features" },
|
||||
"fix": { "title": "🐞 Bug Fixes" },
|
||||
"refactor": { "title": "♻️ Refactoring" }
|
||||
}
|
||||
}
|
||||
392
circuits/circom/blake2s/blake2s.circom
Normal file
392
circuits/circom/blake2s/blake2s.circom
Normal file
@@ -0,0 +1,392 @@
|
||||
// based on https://github.com/zcash/librustzcash/blob/master/sapling-crypto/src/circuit/blake2s.rs
|
||||
|
||||
include "uint32.circom";
|
||||
include "../../node_modules/circomlib/circuits/bitify.circom";
|
||||
include "../../node_modules/circomlib/circuits/sha256/rotate.circom";
|
||||
|
||||
template MixingG(a, b, c, d) {
|
||||
signal input in_v[16][32];
|
||||
signal input x[32];
|
||||
signal input y[32];
|
||||
|
||||
signal output out_v[16][32];
|
||||
|
||||
component v_a_add = Uint32Add(3);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_a_add.nums_bits[0][i] <== in_v[a][i];
|
||||
v_a_add.nums_bits[1][i] <== in_v[b][i];
|
||||
v_a_add.nums_bits[2][i] <== x[i];
|
||||
}
|
||||
|
||||
component v_d_a_xor = Uint32Xor();
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_d_a_xor.a_bits[i] <== in_v[d][i];
|
||||
v_d_a_xor.b_bits[i] <== v_a_add.out_bits[i];
|
||||
}
|
||||
|
||||
component v_d_a_rot = RotR(32, 16);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_d_a_rot.in[i] <== v_d_a_xor.out_bits[i];
|
||||
}
|
||||
|
||||
component v_c_add = Uint32Add(2);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_c_add.nums_bits[0][i] <== in_v[c][i];
|
||||
v_c_add.nums_bits[1][i] <== v_d_a_rot.out[i];
|
||||
}
|
||||
|
||||
component v_b_c_xor = Uint32Xor();
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_b_c_xor.a_bits[i] <== in_v[b][i];
|
||||
v_b_c_xor.b_bits[i] <== v_c_add.out_bits[i];
|
||||
}
|
||||
|
||||
component v_b_c_rot = RotR(32, 12);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_b_c_rot.in[i] <== v_b_c_xor.out_bits[i];
|
||||
}
|
||||
|
||||
component v_a_add_2 = Uint32Add(3);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_a_add_2.nums_bits[0][i] <== v_a_add.out_bits[i];
|
||||
v_a_add_2.nums_bits[1][i] <== v_b_c_rot.out[i];
|
||||
v_a_add_2.nums_bits[2][i] <== y[i];
|
||||
}
|
||||
|
||||
component v_d_a_xor_2 = Uint32Xor();
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_d_a_xor_2.a_bits[i] <== v_d_a_rot.out[i];
|
||||
v_d_a_xor_2.b_bits[i] <== v_a_add_2.out_bits[i];
|
||||
}
|
||||
|
||||
component v_d_a_rot_2 = RotR(32, 8);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_d_a_rot_2.in[i] <== v_d_a_xor_2.out_bits[i];
|
||||
}
|
||||
|
||||
component v_c_add_2 = Uint32Add(2);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_c_add_2.nums_bits[0][i] <== v_c_add.out_bits[i];
|
||||
v_c_add_2.nums_bits[1][i] <== v_d_a_rot_2.out[i];
|
||||
}
|
||||
|
||||
component v_b_c_xor_2 = Uint32Xor();
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_b_c_xor_2.a_bits[i] <== v_b_c_rot.out[i];
|
||||
v_b_c_xor_2.b_bits[i] <== v_c_add_2.out_bits[i];
|
||||
}
|
||||
|
||||
component v_b_c_rot_2 = RotR(32, 7);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
v_b_c_rot_2.in[i] <== v_b_c_xor_2.out_bits[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < 16; i++) {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
if (i == a) {
|
||||
out_v[i][j] <== v_a_add_2.out_bits[j];
|
||||
} else if (i == b) {
|
||||
out_v[i][j] <== v_b_c_rot_2.out[j];
|
||||
} else if (i == c) {
|
||||
out_v[i][j] <== v_c_add_2.out_bits[j];
|
||||
} else if (i == d) {
|
||||
out_v[i][j] <== v_d_a_rot_2.out[j];
|
||||
} else {
|
||||
out_v[i][j] <== in_v[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template Blake2sCompression(t, f) {
|
||||
signal input in_h[8][32];
|
||||
signal input in_m[16][32];
|
||||
|
||||
signal output out_h[8][32];
|
||||
|
||||
var v_consts = [
|
||||
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
|
||||
];
|
||||
signal v_h[16][32];
|
||||
|
||||
for (var i = 0; i < 16; i++) {
|
||||
if (i < 8) {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_h[i][j] <== in_h[i][j];
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_h[i][j] <== (v_consts[i - 8] >> j) & 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signal v_pass_1[16][32];
|
||||
component v_12_xor = Uint32Xor();
|
||||
component v_13_xor = Uint32Xor();
|
||||
component v_14_xor = Uint32Xor();
|
||||
|
||||
for (var i = 0; i < 16; i++) {
|
||||
if (i == 12) {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_12_xor.a_bits[j] <== v_h[i][j];
|
||||
v_12_xor.b_bits[j] <== (t >> j) & 1;
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_pass_1[i][j] <== v_12_xor.out_bits[j];
|
||||
}
|
||||
} else if (i == 13) {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_13_xor.a_bits[j] <== v_h[i][j];
|
||||
v_13_xor.b_bits[j] <== (t >> (32 + j)) & 1;
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_pass_1[i][j] <== v_13_xor.out_bits[j];
|
||||
}
|
||||
} else if ((i == 14)) {
|
||||
if (f == 1) {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_14_xor.a_bits[j] <== v_h[i][j];
|
||||
v_14_xor.b_bits[j] <== 1;
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_pass_1[i][j] <== v_14_xor.out_bits[j];
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_14_xor.a_bits[j] <== v_h[i][j];
|
||||
v_14_xor.b_bits[j] <== 0;
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_pass_1[i][j] <== v_h[i][j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
v_pass_1[i][j] <== v_h[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sigma = [
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
|
||||
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
|
||||
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
|
||||
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
|
||||
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
|
||||
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
|
||||
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
|
||||
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
|
||||
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]
|
||||
];
|
||||
|
||||
component mixing_g[10][8];
|
||||
var s;
|
||||
for (var i = 0; i < 10; i++) {
|
||||
s = sigma[i];
|
||||
mixing_g[i][0] = MixingG(0, 4, 8, 12);
|
||||
for (var j = 0; j < 16; j++) {
|
||||
for (var k = 0; k < 32; k++) {
|
||||
if (i == 0) {
|
||||
mixing_g[i][0].in_v[j][k] <== v_pass_1[j][k];
|
||||
} else {
|
||||
mixing_g[i][0].in_v[j][k] <== mixing_g[i - 1][7].out_v[j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var k = 0; k < 32; k++) {
|
||||
mixing_g[i][0].x[k] <== in_m[s[0]][k];
|
||||
mixing_g[i][0].y[k] <== in_m[s[1]][k];
|
||||
}
|
||||
|
||||
|
||||
mixing_g[i][1] = MixingG(1, 5, 9, 13);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][1].in_v[j][k] <== mixing_g[i][0].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][1].x[k] <== in_m[s[2]][k];
|
||||
mixing_g[i][1].y[k] <== in_m[s[3]][k];
|
||||
}
|
||||
|
||||
mixing_g[i][2] = MixingG(2, 6, 10, 14);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][2].in_v[j][k] <== mixing_g[i][1].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][2].x[k] <== in_m[s[4]][k];
|
||||
mixing_g[i][2].y[k] <== in_m[s[5]][k];
|
||||
}
|
||||
|
||||
mixing_g[i][3] = MixingG(3, 7, 11, 15);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][3].in_v[j][k] <== mixing_g[i][2].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][3].x[k] <== in_m[s[6]][k];
|
||||
mixing_g[i][3].y[k] <== in_m[s[7]][k];
|
||||
}
|
||||
|
||||
mixing_g[i][4] = MixingG(0, 5, 10, 15);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][4].in_v[j][k] <== mixing_g[i][3].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][4].x[k] <== in_m[s[8]][k];
|
||||
mixing_g[i][4].y[k] <== in_m[s[9]][k];
|
||||
}
|
||||
|
||||
mixing_g[i][5] = MixingG(1, 6, 11, 12);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][5].in_v[j][k] <== mixing_g[i][4].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][5].x[k] <== in_m[s[10]][k];
|
||||
mixing_g[i][5].y[k] <== in_m[s[11]][k];
|
||||
}
|
||||
|
||||
mixing_g[i][6] = MixingG(2, 7, 8, 13);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][6].in_v[j][k] <== mixing_g[i][5].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][6].x[k] <== in_m[s[12]][k];
|
||||
mixing_g[i][6].y[k] <== in_m[s[13]][k];
|
||||
}
|
||||
|
||||
mixing_g[i][7] = MixingG(3, 4, 9, 14);
|
||||
for (var k = 0; k < 32; k++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g[i][7].in_v[j][k] <== mixing_g[i][6].out_v[j][k];
|
||||
}
|
||||
mixing_g[i][7].x[k] <== in_m[s[14]][k];
|
||||
mixing_g[i][7].y[k] <== in_m[s[15]][k];
|
||||
}
|
||||
}
|
||||
|
||||
component h_xor_1[8];
|
||||
component h_xor_2[8];
|
||||
for (var i = 0; i < 8; i++) {
|
||||
h_xor_1[i] = Uint32Xor();
|
||||
h_xor_2[i] = Uint32Xor();
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h_xor_1[i].a_bits[j] <== in_h[i][j];
|
||||
h_xor_1[i].b_bits[j] <== mixing_g[9][7].out_v[i][j];
|
||||
}
|
||||
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h_xor_2[i].a_bits[j] <== h_xor_1[i].out_bits[j];
|
||||
h_xor_2[i].b_bits[j] <== mixing_g[9][7].out_v[i + 8][j];
|
||||
}
|
||||
|
||||
for (var j = 0; j < 32; j++) {
|
||||
out_h[i][j] <== h_xor_2[i].out_bits[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template Blake2s(n_bits, personalization) {
|
||||
signal input in_bits[n_bits];
|
||||
signal output out[256];
|
||||
|
||||
signal h[8][32];
|
||||
component h_from_bits[8];
|
||||
component h6_xor;
|
||||
component h7_xor;
|
||||
|
||||
var h_consts = [
|
||||
0x6A09E667 ^ 0x01010000 ^ 32,
|
||||
0xBB67AE85,
|
||||
0x3C6EF372,
|
||||
0xA54FF53A,
|
||||
0x510E527F,
|
||||
0x9B05688C,
|
||||
0x1F83D9AB,
|
||||
0x5BE0CD19
|
||||
];
|
||||
|
||||
for (var i = 0; i < 8; i++) {
|
||||
h_from_bits[i] = Num2Bits(32);
|
||||
h_from_bits[i].in <== h_consts[i];
|
||||
if (i == 6) {
|
||||
h6_xor = Uint32Xor();
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h6_xor.a_bits[j] <== h_from_bits[i].out[j];
|
||||
h6_xor.b_bits[j] <== (personalization >> j) & 1;
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h[i][j] <== h6_xor.out_bits[j];
|
||||
}
|
||||
} else if (i == 7) {
|
||||
h7_xor = Uint32Xor();
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h7_xor.a_bits[j] <== h_from_bits[i].out[j];
|
||||
h7_xor.b_bits[j] <== (personalization >> (32 + j)) & 1;
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h[i][j] <== h7_xor.out_bits[j];
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < 32; j++) {
|
||||
h[i][j] <== h_from_bits[i].out[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var n_rounded;
|
||||
if ( (n_bits % 512) == 0) {
|
||||
n_rounded = n_bits;
|
||||
} else {
|
||||
n_rounded = n_bits + (512 - (n_bits % 512));
|
||||
}
|
||||
var num_blocks = n_rounded / 512;
|
||||
if (num_blocks == 0) {
|
||||
num_blocks = 1;
|
||||
}
|
||||
|
||||
var n_rounded_bytes;
|
||||
if ( (n_bits % 8) == 0) {
|
||||
n_rounded_bytes = n_bits;
|
||||
} else {
|
||||
n_rounded_bytes = n_bits + (8 - (n_bits % 8));
|
||||
}
|
||||
|
||||
component compressions[num_blocks];
|
||||
var current_bit = 0;
|
||||
for (var i = 0; i < num_blocks; i++) {
|
||||
if (i < (num_blocks - 1)) {
|
||||
compressions[i] = Blake2sCompression((i + 1)*64, 0);
|
||||
} else {
|
||||
compressions[i] = Blake2sCompression(n_rounded_bytes/8, 1);
|
||||
}
|
||||
for (var j = 0; j < 32; j++) {
|
||||
for (var k = 0; k < 8; k++) {
|
||||
if (i == 0) {
|
||||
compressions[i].in_h[k][j] <== h[k][j];
|
||||
} else {
|
||||
compressions[i].in_h[k][j] <== compressions[i - 1].out_h[k][j];
|
||||
}
|
||||
}
|
||||
for (var l = 0; l < 16; l++) {
|
||||
current_bit = 512*i + 32*l + j;
|
||||
if (current_bit < n_bits) {
|
||||
compressions[i].in_m[l][j] <== in_bits[current_bit];
|
||||
} else {
|
||||
compressions[i].in_m[l][j] <== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == (num_blocks - 1)) {
|
||||
for (var j = 0; j < 8; j++) {
|
||||
for (var k = 0; k < 4; k++) {
|
||||
for (var l = 0; l < 8; l++) {
|
||||
out[32*j + 8*k + l] <== compressions[num_blocks - 1].out_h[8 - 1 - j][(4 - 1 - k)*8 + l];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
circuits/circom/blake2s/uint32.circom
Normal file
30
circuits/circom/blake2s/uint32.circom
Normal file
@@ -0,0 +1,30 @@
|
||||
include "../../node_modules/circomlib/circuits/binsum.circom";
|
||||
|
||||
template Uint32Add(n) {
|
||||
signal input nums_bits[n][32];
|
||||
signal output out_bits[32];
|
||||
|
||||
component sum = BinSum(32, n);
|
||||
var i;
|
||||
var j;
|
||||
for (i = 0; i < n; i++) {
|
||||
for (j = 0; j < 32; j++) {
|
||||
sum.in[i][j] <== nums_bits[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < 32; j++) {
|
||||
out_bits[j] <== sum.out[j];
|
||||
}
|
||||
}
|
||||
|
||||
template Uint32Xor() {
|
||||
signal input a_bits[32];
|
||||
signal input b_bits[32];
|
||||
|
||||
signal output out_bits[32];
|
||||
|
||||
for (var i = 0; i < 32; i++) {
|
||||
out_bits[i] <== a_bits[i] + b_bits[i] - 2*a_bits[i]*b_bits[i];
|
||||
}
|
||||
}
|
||||
258
circuits/circom/semaphore-base.circom
Normal file
258
circuits/circom/semaphore-base.circom
Normal file
@@ -0,0 +1,258 @@
|
||||
include "../node_modules/circomlib/circuits/pedersen.circom";
|
||||
include "../node_modules/circomlib/circuits/mimcsponge.circom";
|
||||
include "../node_modules/circomlib/circuits/bitify.circom";
|
||||
include "../node_modules/circomlib/circuits/eddsamimcsponge.circom";
|
||||
include "../node_modules/circomlib/circuits/babyjub.circom";
|
||||
include "../node_modules/circomlib/circuits/mux1.circom";
|
||||
include "../node_modules/circomlib/circuits/assert.circom";
|
||||
include "./blake2s/blake2s.circom";
|
||||
|
||||
template HashLeftRight() {
|
||||
signal input left;
|
||||
signal input right;
|
||||
|
||||
signal output hash;
|
||||
|
||||
component hasher = MiMCSponge(2, 1);
|
||||
left ==> hasher.ins[0];
|
||||
right ==> hasher.ins[1];
|
||||
hasher.k <== 0;
|
||||
|
||||
hash <== hasher.outs[0];
|
||||
}
|
||||
|
||||
template Selector() {
|
||||
signal input input_elem;
|
||||
signal input path_elem;
|
||||
signal input path_index;
|
||||
|
||||
signal output left;
|
||||
signal output right;
|
||||
|
||||
path_index * (1-path_index) === 0
|
||||
|
||||
component mux = MultiMux1(2);
|
||||
mux.c[0][0] <== input_elem;
|
||||
mux.c[0][1] <== path_elem;
|
||||
|
||||
mux.c[1][0] <== path_elem;
|
||||
mux.c[1][1] <== input_elem;
|
||||
|
||||
mux.s <== path_index;
|
||||
|
||||
left <== mux.out[0];
|
||||
right <== mux.out[1];
|
||||
}
|
||||
|
||||
template MerkleTreeInclusionProof(n_levels) {
|
||||
signal input identity_commitment;
|
||||
signal input identity_path_index[n_levels];
|
||||
signal input identity_path_elements[n_levels];
|
||||
signal output root;
|
||||
|
||||
component selectors[n_levels];
|
||||
component hashers[n_levels];
|
||||
|
||||
for (var i = 0; i < n_levels; i++) {
|
||||
selectors[i] = Selector();
|
||||
hashers[i] = HashLeftRight();
|
||||
|
||||
identity_path_index[i] ==> selectors[i].path_index;
|
||||
identity_path_elements[i] ==> selectors[i].path_elem;
|
||||
|
||||
selectors[i].left ==> hashers[i].left;
|
||||
selectors[i].right ==> hashers[i].right;
|
||||
}
|
||||
|
||||
identity_commitment ==> selectors[0].input_elem;
|
||||
|
||||
for (var i = 1; i < n_levels; i++) {
|
||||
hashers[i-1].hash ==> selectors[i].input_elem;
|
||||
}
|
||||
|
||||
root <== hashers[n_levels - 1].hash;
|
||||
}
|
||||
|
||||
template CalculateIdentityCommitment(IDENTITY_PK_SIZE_IN_BITS, NULLIFIER_TRAPDOOR_SIZE_IN_BITS) {
|
||||
signal input identity_pk[IDENTITY_PK_SIZE_IN_BITS];
|
||||
signal input identity_nullifier[NULLIFIER_TRAPDOOR_SIZE_IN_BITS];
|
||||
signal input identity_trapdoor[NULLIFIER_TRAPDOOR_SIZE_IN_BITS];
|
||||
|
||||
signal output out;
|
||||
|
||||
// identity commitment is a pedersen hash of (identity_pk, identity_nullifier, identity_trapdoor), each element padded up to 256 bits
|
||||
component identity_commitment = Pedersen(3*256);
|
||||
for (var i = 0; i < 256; i++) {
|
||||
if (i < IDENTITY_PK_SIZE_IN_BITS) {
|
||||
identity_commitment.in[i] <== identity_pk[i];
|
||||
} else {
|
||||
identity_commitment.in[i] <== 0;
|
||||
}
|
||||
|
||||
if (i < NULLIFIER_TRAPDOOR_SIZE_IN_BITS) {
|
||||
identity_commitment.in[i + 256] <== identity_nullifier[i];
|
||||
identity_commitment.in[i + 2*256] <== identity_trapdoor[i];
|
||||
} else {
|
||||
identity_commitment.in[i + 256] <== 0;
|
||||
identity_commitment.in[i + 2*256] <== 0;
|
||||
}
|
||||
}
|
||||
|
||||
out <== identity_commitment.out[0];
|
||||
}
|
||||
|
||||
template CalculateNullifier(NULLIFIER_TRAPDOOR_SIZE_IN_BITS, EXTERNAL_NULLIFIER_SIZE_IN_BITS, n_levels) {
|
||||
signal input external_nullifier;
|
||||
signal input identity_nullifier[NULLIFIER_TRAPDOOR_SIZE_IN_BITS];
|
||||
signal input identity_path_index[n_levels];
|
||||
|
||||
signal output nullifiers_hash;
|
||||
|
||||
component external_nullifier_bits = Num2Bits(EXTERNAL_NULLIFIER_SIZE_IN_BITS);
|
||||
external_nullifier_bits.in <== external_nullifier;
|
||||
|
||||
var nullifiers_hasher_bits = NULLIFIER_TRAPDOOR_SIZE_IN_BITS + EXTERNAL_NULLIFIER_SIZE_IN_BITS + n_levels;
|
||||
if (nullifiers_hasher_bits < 512) {
|
||||
nullifiers_hasher_bits = 512;
|
||||
}
|
||||
assert (nullifiers_hasher_bits <= 512);
|
||||
|
||||
component nullifiers_hasher = Blake2s(nullifiers_hasher_bits, 0);
|
||||
for (var i = 0; i < NULLIFIER_TRAPDOOR_SIZE_IN_BITS; i++) {
|
||||
nullifiers_hasher.in_bits[i] <== identity_nullifier[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < EXTERNAL_NULLIFIER_SIZE_IN_BITS; i++) {
|
||||
nullifiers_hasher.in_bits[NULLIFIER_TRAPDOOR_SIZE_IN_BITS + i] <== external_nullifier_bits.out[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < n_levels; i++) {
|
||||
nullifiers_hasher.in_bits[NULLIFIER_TRAPDOOR_SIZE_IN_BITS + EXTERNAL_NULLIFIER_SIZE_IN_BITS + i] <== identity_path_index[i];
|
||||
}
|
||||
|
||||
for (var i = (NULLIFIER_TRAPDOOR_SIZE_IN_BITS + EXTERNAL_NULLIFIER_SIZE_IN_BITS + n_levels); i < nullifiers_hasher_bits; i++) {
|
||||
nullifiers_hasher.in_bits[i] <== 0;
|
||||
}
|
||||
|
||||
component nullifiers_hash_num = Bits2Num(250);
|
||||
for (var i = 0; i < 250; i++) {
|
||||
nullifiers_hash_num.in[i] <== nullifiers_hasher.out[i];
|
||||
}
|
||||
|
||||
nullifiers_hash <== nullifiers_hash_num.out;
|
||||
}
|
||||
|
||||
// n_levels must be < 32
|
||||
template Semaphore(n_levels) {
|
||||
// BEGIN signals
|
||||
|
||||
signal input signal_hash;
|
||||
signal input external_nullifier;
|
||||
|
||||
signal private input fake_zero;
|
||||
|
||||
// mimc vector commitment
|
||||
signal private input identity_pk[2];
|
||||
signal private input identity_nullifier;
|
||||
signal private input identity_trapdoor;
|
||||
signal private input identity_path_elements[n_levels];
|
||||
signal private input identity_path_index[n_levels];
|
||||
|
||||
// signature on (external nullifier, signal_hash) with identity_pk
|
||||
signal private input auth_sig_r[2];
|
||||
signal private input auth_sig_s;
|
||||
|
||||
// mimc hash
|
||||
signal output root;
|
||||
signal output nullifiers_hash;
|
||||
|
||||
// END signals
|
||||
|
||||
// BEGIN constants
|
||||
|
||||
var IDENTITY_PK_SIZE_IN_BITS = 254;
|
||||
var NULLIFIER_TRAPDOOR_SIZE_IN_BITS = 248;
|
||||
var EXTERNAL_NULLIFIER_SIZE_IN_BITS = 232;
|
||||
|
||||
// END constants
|
||||
|
||||
fake_zero === 0;
|
||||
|
||||
component verify_identity_pk_on_curve = BabyCheck();
|
||||
verify_identity_pk_on_curve.x <== identity_pk[0];
|
||||
verify_identity_pk_on_curve.y <== identity_pk[1];
|
||||
|
||||
component verify_auth_sig_r_on_curve = BabyCheck();
|
||||
verify_auth_sig_r_on_curve.x <== auth_sig_r[0];
|
||||
verify_auth_sig_r_on_curve.y <== auth_sig_r[1];
|
||||
|
||||
// get a prime subgroup element derived from identity_pk
|
||||
component dbl1 = BabyDbl();
|
||||
dbl1.x <== identity_pk[0];
|
||||
dbl1.y <== identity_pk[1];
|
||||
component dbl2 = BabyDbl();
|
||||
dbl2.x <== dbl1.xout;
|
||||
dbl2.y <== dbl1.yout;
|
||||
component dbl3 = BabyDbl();
|
||||
dbl3.x <== dbl2.xout;
|
||||
dbl3.y <== dbl2.yout;
|
||||
|
||||
component identity_nullifier_bits = Num2Bits(NULLIFIER_TRAPDOOR_SIZE_IN_BITS);
|
||||
identity_nullifier_bits.in <== identity_nullifier;
|
||||
|
||||
component identity_trapdoor_bits = Num2Bits(NULLIFIER_TRAPDOOR_SIZE_IN_BITS);
|
||||
identity_trapdoor_bits.in <== identity_trapdoor;
|
||||
|
||||
component identity_pk_0_bits = Num2Bits_strict();
|
||||
identity_pk_0_bits.in <== dbl3.xout;
|
||||
|
||||
// BEGIN identity commitment
|
||||
component identity_commitment = CalculateIdentityCommitment(IDENTITY_PK_SIZE_IN_BITS, NULLIFIER_TRAPDOOR_SIZE_IN_BITS);
|
||||
for (var i = 0; i < IDENTITY_PK_SIZE_IN_BITS; i++) {
|
||||
identity_commitment.identity_pk[i] <== identity_pk_0_bits.out[i];
|
||||
}
|
||||
for (var i = 0; i < NULLIFIER_TRAPDOOR_SIZE_IN_BITS; i++) {
|
||||
identity_commitment.identity_nullifier[i] <== identity_nullifier_bits.out[i];
|
||||
identity_commitment.identity_trapdoor[i] <== identity_trapdoor_bits.out[i];
|
||||
}
|
||||
// END identity commitment
|
||||
|
||||
// BEGIN tree
|
||||
component tree = MerkleTreeInclusionProof(n_levels);
|
||||
tree.identity_commitment <== identity_commitment.out;
|
||||
for (var i = 0; i < n_levels; i++) {
|
||||
tree.identity_path_index[i] <== identity_path_index[i];
|
||||
tree.identity_path_elements[i] <== identity_path_elements[i];
|
||||
}
|
||||
root <== tree.root;
|
||||
// END tree
|
||||
|
||||
// BEGIN nullifiers
|
||||
component nullifiers_hasher = CalculateNullifier(NULLIFIER_TRAPDOOR_SIZE_IN_BITS, EXTERNAL_NULLIFIER_SIZE_IN_BITS, n_levels);
|
||||
nullifiers_hasher.external_nullifier <== external_nullifier;
|
||||
for (var i = 0; i < NULLIFIER_TRAPDOOR_SIZE_IN_BITS; i++) {
|
||||
nullifiers_hasher.identity_nullifier[i] <== identity_nullifier_bits.out[i];
|
||||
}
|
||||
for (var i = 0; i < n_levels; i++) {
|
||||
nullifiers_hasher.identity_path_index[i] <== identity_path_index[i];
|
||||
}
|
||||
nullifiers_hash <== nullifiers_hasher.nullifiers_hash;
|
||||
// END nullifiers
|
||||
|
||||
// BEGIN verify sig
|
||||
component msg_hasher = MiMCSponge(2, 1);
|
||||
msg_hasher.ins[0] <== external_nullifier;
|
||||
msg_hasher.ins[1] <== signal_hash;
|
||||
msg_hasher.k <== 0;
|
||||
|
||||
component sig_verifier = EdDSAMiMCSpongeVerifier();
|
||||
(1 - fake_zero) ==> sig_verifier.enabled;
|
||||
identity_pk[0] ==> sig_verifier.Ax;
|
||||
identity_pk[1] ==> sig_verifier.Ay;
|
||||
auth_sig_r[0] ==> sig_verifier.R8x;
|
||||
auth_sig_r[1] ==> sig_verifier.R8y;
|
||||
auth_sig_s ==> sig_verifier.S;
|
||||
msg_hasher.outs[0] ==> sig_verifier.M;
|
||||
|
||||
// END verify sig
|
||||
}
|
||||
3
circuits/circom/semaphore.circom
Normal file
3
circuits/circom/semaphore.circom
Normal file
@@ -0,0 +1,3 @@
|
||||
include "./semaphore-base.circom";
|
||||
|
||||
component main = Semaphore(20);
|
||||
29
circuits/jest.config.js
Normal file
29
circuits/jest.config.js
Normal file
@@ -0,0 +1,29 @@
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
transform: {
|
||||
"^.+\\.tsx?$": 'ts-jest'
|
||||
},
|
||||
testPathIgnorePatterns: [
|
||||
"/build/",
|
||||
"/node_modules/",
|
||||
],
|
||||
testRegex: '/__tests__/.*\\.test\\.ts$',
|
||||
moduleFileExtensions: [
|
||||
'ts',
|
||||
'tsx',
|
||||
'js',
|
||||
'jsx',
|
||||
'json',
|
||||
'node'
|
||||
],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
diagnostics: {
|
||||
// Do not fail on TS compilation errors
|
||||
// https://kulshekhar.github.io/ts-jest/user/config/diagnostics#do-not-fail-on-first-error
|
||||
warnOnly: true
|
||||
}
|
||||
}
|
||||
},
|
||||
testEnvironment: 'node'
|
||||
}
|
||||
8451
circuits/package-lock.json
generated
Normal file
8451
circuits/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
circuits/package.json
Normal file
24
circuits/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "semaphore-circuits",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
"watch": "tsc --watch",
|
||||
"build": "tsc",
|
||||
"test-blake2s": "NODE_ENV=local-dev jest --forceExit --testPathPattern Blake2s.test.ts Uint32.test.ts"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.0.20",
|
||||
"@types/node": "^12.7.7",
|
||||
"circom": "0.0.34",
|
||||
"circomlib": "git+https://github.com/kobigurk/circomlib.git#4284dc1ef984a204db08864f5da530c97f9376ef",
|
||||
"jest": "^24.9.0",
|
||||
"module-alias": "^2.2.2",
|
||||
"snarkjs": "https://github.com/weijiekoh/snarkjs.git#ef8bbbbe5a7d37f59cdb45d3fdf2c1dcf7dd9c7a",
|
||||
"ts-jest": "^24.1.0",
|
||||
"typescript": "^3.7.3",
|
||||
"websnark": "0.0.5"
|
||||
}
|
||||
}
|
||||
58
circuits/scripts/build_snarks.sh
Executable file
58
circuits/scripts/build_snarks.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# semaphorejs - Zero-knowledge signaling on Ethereum
|
||||
# Copyright (C) 2019 Kobi Gurkan <kobigurk@gmail.com>
|
||||
#
|
||||
# This file is part of semaphorejs.
|
||||
#
|
||||
# semaphorejs is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# semaphorejs is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with semaphorejs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
mkdir -p ../build
|
||||
cd ../build
|
||||
|
||||
if [ -f ./circuit.json ]; then
|
||||
echo "circuit.json already exists. Skipping."
|
||||
else
|
||||
echo 'Generating circuit.json'
|
||||
export NODE_OPTIONS=--max-old-space-size=4096
|
||||
npx circom ../circom/semaphore.circom
|
||||
fi
|
||||
|
||||
if [ -f ./proving_key.json ]; then
|
||||
echo "proving_key.json already exists. Skipping."
|
||||
else
|
||||
echo 'Generating proving_key.json'
|
||||
export NODE_OPTIONS=--max-old-space-size=4096
|
||||
npx snarkjs setup --protocol groth
|
||||
fi
|
||||
|
||||
if [ -f ./proving_key.bin ]; then
|
||||
echo 'proving_key.bin already exists. Skipping.'
|
||||
else
|
||||
echo 'Generating proving_key.bin'
|
||||
export NODE_OPTIONS=--max-old-space-size=4096
|
||||
node ../node_modules/websnark/tools/buildpkey.js -i ./proving_key.json -o ./proving_key.bin
|
||||
fi
|
||||
|
||||
if [ -f ./verifier.sol ]; then
|
||||
echo 'verifier.sol already exists. Skipping.'
|
||||
else
|
||||
echo 'Generating verifier.sol'
|
||||
npx snarkjs generateverifier --vk ./verification_key.json -v ./verifier.sol
|
||||
fi
|
||||
|
||||
# Copy verifier.sol to the contracts/sol directory
|
||||
echo 'Copying verifier.sol to contracts/sol.'
|
||||
cp ./verifier.sol ../../contracts/sol/
|
||||
12
circuits/scripts/checksum_snarks.sh
Executable file
12
circuits/scripts/checksum_snarks.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Used by the CircleCI process to generate a checksum of the snark files so
|
||||
# that it can cache them.
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
mkdir -p ../build
|
||||
cd ../build
|
||||
|
||||
find ../circom -type f -exec md5sum {} \; | sort -k 2 | md5sum > ./.snark_checksum
|
||||
echo 'snark checksum:'
|
||||
cat .snark_checksum
|
||||
35
circuits/scripts/download_snarks.sh
Executable file
35
circuits/scripts/download_snarks.sh
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
CIRCUIT_JSON="https://www.dropbox.com/s/3gzxjibqgb6ke13/circuit.json?dl=1"
|
||||
PROVING_KEY_BIN="https://www.dropbox.com/s/qjlu6v125g7jkcq/proving_key.bin?dl=1"
|
||||
VERIFICATION_KEY_JSON="https://www.dropbox.com/s/rwjwu31c7pzhsth/verification_key.json?dl=1"
|
||||
VERIFIER_SOL="https://www.dropbox.com/s/q5fjzu4zxhc0393/verifier.sol?dl=1"
|
||||
|
||||
CIRCUIT_JSON_PATH="../build/circuit.json"
|
||||
PROVING_KEY_BIN_PATH="../build/proving_key.bin"
|
||||
VERIFICATION_KEY_PATH="../build/verification_key.json"
|
||||
VERIFIER_SOL_PATH="../build/verifier.sol"
|
||||
|
||||
mkdir -p ../build
|
||||
|
||||
if [ ! -f "$CIRCUIT_JSON_PATH" ]; then
|
||||
echo "Downloading circuit.json"
|
||||
wget --quiet $CIRCUIT_JSON -O $CIRCUIT_JSON_PATH
|
||||
fi
|
||||
|
||||
if [ ! -f "$PROVING_KEY_BIN_PATH" ]; then
|
||||
echo "Downloading proving_key.bin"
|
||||
wget --quiet $PROVING_KEY_BIN -O $PROVING_KEY_BIN_PATH
|
||||
fi
|
||||
|
||||
if [ ! -f "$VERIFICATION_KEY_PATH" ]; then
|
||||
echo "Downloading verification_key.json"
|
||||
wget --quiet $VERIFICATION_KEY_JSON -O $VERIFICATION_KEY_PATH
|
||||
fi
|
||||
|
||||
if [ ! -f "$VERIFIER_SOL_PATH" ]; then
|
||||
echo "Downloading verifier.sol"
|
||||
wget --quiet $VERIFIER_SOL -O $VERIFIER_SOL_PATH
|
||||
fi
|
||||
6
circuits/scripts/runTestsInCircleCi.sh
Executable file
6
circuits/scripts/runTestsInCircleCi.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash -xe
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
cd ..
|
||||
|
||||
npm run test-blake2s
|
||||
51
circuits/ts/__tests__/blake2s/Blake2s.test.ts
Normal file
51
circuits/ts/__tests__/blake2s/Blake2s.test.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
require('module-alias/register')
|
||||
jest.setTimeout(1000000)
|
||||
|
||||
import * as path from 'path'
|
||||
import * as snarkjs from 'snarkjs'
|
||||
const bigInt = snarkjs.bigInt
|
||||
import * as crypto from 'crypto'
|
||||
import * as compiler from 'circom'
|
||||
|
||||
describe('Blake2s should match test vectors', () => {
|
||||
test('Should compile mixing_g', async () => {
|
||||
const cirDef = await compiler(path.join(__dirname, 'mixing_g_test.circom'))
|
||||
const circuit = new snarkjs.Circuit(cirDef)
|
||||
|
||||
console.log('Vars: '+circuit.nVars)
|
||||
console.log('Constraints: '+circuit.nConstraints)
|
||||
|
||||
})
|
||||
|
||||
test('Should compile blake2s_compression', async () => {
|
||||
const cirDef = await compiler(path.join(__dirname, 'blake2s_compression_test.circom'))
|
||||
const circuit = new snarkjs.Circuit(cirDef)
|
||||
|
||||
console.log('Vars: '+circuit.nVars)
|
||||
console.log('Constraints: '+circuit.nConstraints)
|
||||
})
|
||||
|
||||
test('Should run blake2s', async () => {
|
||||
const cirDef = await compiler(path.join(__dirname, 'blake2s_test.circom'))
|
||||
const circuit = new snarkjs.Circuit(cirDef)
|
||||
|
||||
console.log('Vars: '+circuit.nVars)
|
||||
console.log('Constraints: '+circuit.nConstraints)
|
||||
|
||||
const bits = '11111111'
|
||||
const inputs = {}
|
||||
for (let i = 0; i < bits.length; i++) {
|
||||
inputs[`in_bits[${i}]`] = bigInt(bits[i])
|
||||
}
|
||||
const witness = circuit.calculateWitness(inputs)
|
||||
|
||||
let coeff = bigInt(1)
|
||||
let result = bigInt(0)
|
||||
for (let i = 0; i < 256; i++) {
|
||||
result = result.add(bigInt(witness[circuit.getSignalIdx(`main.out[${i}]`)].toString()).mul(coeff))
|
||||
coeff = coeff.shl(1)
|
||||
}
|
||||
console.log(`blake2s hash: 0x${result.toString(16)}`)
|
||||
expect(result.toString(16)).toEqual('8a1ef126b4e286703744a80b2f414be700cc93023e7bfc8688b79b54931abd27')
|
||||
})
|
||||
})
|
||||
93
circuits/ts/__tests__/blake2s/Uint32.test.ts
Normal file
93
circuits/ts/__tests__/blake2s/Uint32.test.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
require('module-alias/register')
|
||||
jest.setTimeout(1000000)
|
||||
|
||||
import * as path from 'path'
|
||||
import * as snarkjs from 'snarkjs'
|
||||
const bigInt = snarkjs.bigInt
|
||||
import * as crypto from 'crypto'
|
||||
import * as compiler from 'circom'
|
||||
|
||||
describe("Uint32 test", () => {
|
||||
test("Should add 5 Uint32s", async () => {
|
||||
const cirDef = await compiler(path.join(__dirname, "uint32_add_test.circom"))
|
||||
const circuit = new snarkjs.Circuit(cirDef)
|
||||
|
||||
console.log("Vars: "+circuit.nVars)
|
||||
console.log("Constraints: "+circuit.nConstraints)
|
||||
|
||||
const inputs = {}
|
||||
|
||||
const nums = [4294967295, 4294967294, 4294967293, 4294967292, 4294967291]
|
||||
for (let i = 0; i < nums.length; i++) {
|
||||
let num = bigInt(nums[i])
|
||||
//inputs[`nums_vals[${i}]`] = num
|
||||
for (let j = 0; j < 32; j++) {
|
||||
// this extracts the least significant bit
|
||||
const bit = num.and(bigInt(1))
|
||||
inputs[`nums_bits[${i}][${j}]`] = bit
|
||||
num = num.shr(1)
|
||||
}
|
||||
}
|
||||
const witness = circuit.calculateWitness(inputs)
|
||||
|
||||
let expected_num = nums.reduce((result, current) => {
|
||||
if (result == bigInt(-1)) {
|
||||
result = bigInt(current)
|
||||
} else {
|
||||
result = result.add(bigInt(current))
|
||||
}
|
||||
return result
|
||||
}, bigInt(-1))
|
||||
let expected_bits: number[] = []
|
||||
for (let j = 0; j < 32; j++) {
|
||||
const bit = expected_num.and(bigInt(1))
|
||||
expected_bits.push(bit)
|
||||
expected_num = expected_num.shr(1)
|
||||
}
|
||||
|
||||
for (let i = 0; i < 32; i++) {
|
||||
expect(witness[circuit.getSignalIdx(`main.out_bits[${i}]`)].toString()).toEqual(snarkjs.bigInt(expected_bits[i]).toString())
|
||||
}
|
||||
})
|
||||
|
||||
test("Should xor 2 Uint32s", async () => {
|
||||
const cirDef = await compiler(path.join(__dirname, "uint32_xor_test.circom"))
|
||||
const circuit = new snarkjs.Circuit(cirDef)
|
||||
|
||||
console.log("Vars: "+circuit.nVars)
|
||||
console.log("Constraints: "+circuit.nConstraints)
|
||||
|
||||
const inputs = {}
|
||||
|
||||
const nums = [24959295, 4594067494]
|
||||
for (let i = 0; i < nums.length; i++) {
|
||||
let num = bigInt(nums[i])
|
||||
//inputs[`nums_vals[${i}]`] = num
|
||||
for (let j = 0; j < 32; j++) {
|
||||
// this extracts the least significant bit
|
||||
const bit = num.and(bigInt(1))
|
||||
let var_name
|
||||
if (i == 0) {
|
||||
var_name = 'a'
|
||||
} else {
|
||||
var_name = 'b'
|
||||
}
|
||||
inputs[`${var_name}_bits[${j}]`] = bit
|
||||
num = num.shr(1)
|
||||
}
|
||||
}
|
||||
const witness = circuit.calculateWitness(inputs)
|
||||
|
||||
let expected_num = bigInt(nums[0] ^ nums[1])
|
||||
let expected_bits: number[] = []
|
||||
for (let j = 0; j < 32; j++) {
|
||||
const bit = expected_num.and(bigInt(1))
|
||||
expected_bits.push(bit)
|
||||
expected_num = expected_num.shr(1)
|
||||
}
|
||||
|
||||
for (let i = 0; i < 32; i++) {
|
||||
expect(witness[circuit.getSignalIdx(`main.out_bits[${i}]`)].toString()).toEqual(snarkjs.bigInt(expected_bits[i]).toString())
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
include "../../../circom/blake2s/blake2s.circom";
|
||||
|
||||
component main = Blake2sCompression(5, 0);
|
||||
3
circuits/ts/__tests__/blake2s/blake2s_test.circom
Normal file
3
circuits/ts/__tests__/blake2s/blake2s_test.circom
Normal file
@@ -0,0 +1,3 @@
|
||||
include "../../../circom/blake2s/blake2s.circom";
|
||||
|
||||
component main = Blake2s(8, 0);
|
||||
21
circuits/ts/__tests__/blake2s/mixing_g_test.circom
Normal file
21
circuits/ts/__tests__/blake2s/mixing_g_test.circom
Normal file
@@ -0,0 +1,21 @@
|
||||
include "../../../circom/blake2s/blake2s.circom";
|
||||
|
||||
template MixingGTester(a, b, c, d) {
|
||||
signal input in_v[16][32];
|
||||
signal input x[32];
|
||||
signal input y[32];
|
||||
|
||||
signal out_v[16][32];
|
||||
|
||||
component mixing_g = MixingG(a, b, c, d);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
mixing_g.x[i] <== x[i];
|
||||
mixing_g.y[i] <== y[i];
|
||||
for (var j = 0; j < 16; j++) {
|
||||
mixing_g.in_v[j][i] <== in_v[j][i];
|
||||
mixing_g.out_v[j][i] <== out_v[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component main = MixingGTester(1, 2, 3, 4);
|
||||
3
circuits/ts/__tests__/blake2s/uint32_add_test.circom
Normal file
3
circuits/ts/__tests__/blake2s/uint32_add_test.circom
Normal file
@@ -0,0 +1,3 @@
|
||||
include "../../../circom/blake2s/uint32.circom";
|
||||
|
||||
component main = Uint32Add(5);
|
||||
3
circuits/ts/__tests__/blake2s/uint32_xor_test.circom
Normal file
3
circuits/ts/__tests__/blake2s/uint32_xor_test.circom
Normal file
@@ -0,0 +1,3 @@
|
||||
include "../../../circom/blake2s/uint32.circom";
|
||||
|
||||
component main = Uint32Xor();
|
||||
11
circuits/ts/bin2hex.ts
Normal file
11
circuits/ts/bin2hex.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
const bin = process.argv[2]
|
||||
|
||||
const buf1 = Buffer.alloc(bin.length/8)
|
||||
for (let i = 0; i < buf1.length; i++) {
|
||||
let t = 1
|
||||
for (let j = 0; j < 8; j++) {
|
||||
buf1[i] |= bin[8*i + j] == '1' ? t : 0
|
||||
t *= 2
|
||||
}
|
||||
}
|
||||
console.log(buf1.toString('hex'))
|
||||
10
circuits/ts/hex2bin.ts
Normal file
10
circuits/ts/hex2bin.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
const buf = Buffer.from(process.argv[2], 'hex')
|
||||
let s = ''
|
||||
for (let i = 0; i < buf.length; i++) {
|
||||
let t = 1
|
||||
for (let j = 0; j < 8; j++) {
|
||||
s += ((buf[i] & t) == t) ? '1' : '0'
|
||||
t *= 2
|
||||
}
|
||||
}
|
||||
console.log(s)
|
||||
0
circuits/ts/index.ts
Normal file
0
circuits/ts/index.ts
Normal file
9
circuits/tsconfig.json
Normal file
9
circuits/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./build"
|
||||
},
|
||||
"include": [
|
||||
"./ts"
|
||||
]
|
||||
}
|
||||
7
config/local-dev.yaml
Normal file
7
config/local-dev.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
env: 'local-dev'
|
||||
|
||||
chain:
|
||||
url: "http://localhost:8545"
|
||||
chainId: 1234
|
||||
mnemonic: "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat"
|
||||
89
config/package-lock.json
generated
Normal file
89
config/package-lock.json
generated
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"name": "semaphore-config",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "12.12.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.3.tgz",
|
||||
"integrity": "sha512-opgSsy+cEF9N8MgaVPnWVtdJ3o4mV2aMHvDq7thkQUFt0EuOHJon4rQpJfhjmNHB+ikl0Cd6WhWIErOyQ+f7tw==",
|
||||
"dev": true
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/config/-/config-3.2.4.tgz",
|
||||
"integrity": "sha512-H1XIGfnU1EAkfjSLn9ZvYDRx9lOezDViuzLDgiJ/lMeqjYe3q6iQfpcLt2NInckJgpAeekbNhQkmnnbdEDs9rw==",
|
||||
"requires": {
|
||||
"json5": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.13.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
||||
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"path": {
|
||||
"version": "0.12.7",
|
||||
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
|
||||
"integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=",
|
||||
"requires": {
|
||||
"process": "^0.11.1",
|
||||
"util": "^0.10.3"
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"util": {
|
||||
"version": "0.10.4",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
|
||||
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
|
||||
"requires": {
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
config/package.json
Normal file
17
config/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "semaphore-config",
|
||||
"version": "0.0.1",
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
"watch": "tsc --watch",
|
||||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"config": "^3.1.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"path": "^0.12.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.0.10"
|
||||
}
|
||||
}
|
||||
15
config/ts/index.ts
Normal file
15
config/ts/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import * as path from 'path'
|
||||
|
||||
// set NODE_CONFIG_DIR
|
||||
|
||||
if (!process.env.hasOwnProperty('NODE_CONFIG_DIR')) {
|
||||
process.env.NODE_CONFIG_DIR = path.join(__dirname, '../')
|
||||
}
|
||||
|
||||
if (!process.env.hasOwnProperty('NODE_ENV')) {
|
||||
process.env.NODE_ENV = 'local-dev'
|
||||
}
|
||||
|
||||
const config = require('config')
|
||||
|
||||
export { config }
|
||||
9
config/tsconfig.json
Normal file
9
config/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./build"
|
||||
},
|
||||
"include": [
|
||||
"./ts"
|
||||
]
|
||||
}
|
||||
32
contracts/jest.config.js
Normal file
32
contracts/jest.config.js
Normal file
@@ -0,0 +1,32 @@
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
transform: {
|
||||
"^.+\\.tsx?$": 'ts-jest'
|
||||
},
|
||||
testPathIgnorePatterns: [
|
||||
"/build/",
|
||||
"/node_modules/",
|
||||
],
|
||||
testRegex: '/__tests__/.*\\.test\\.ts$',
|
||||
moduleFileExtensions: [
|
||||
'ts',
|
||||
'tsx',
|
||||
'js',
|
||||
'jsx',
|
||||
'json',
|
||||
'node'
|
||||
],
|
||||
moduleNameMapper: {
|
||||
"^@semaphore-contracts(.*)$": "<rootDir>./$1",
|
||||
},
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
diagnostics: {
|
||||
// Do not fail on TS compilation errors
|
||||
// https://kulshekhar.github.io/ts-jest/user/config/diagnostics#do-not-fail-on-first-error
|
||||
warnOnly: true
|
||||
}
|
||||
}
|
||||
},
|
||||
testEnvironment: 'node'
|
||||
}
|
||||
1
contracts/package-lock.json.REMOVED.git-id
Normal file
1
contracts/package-lock.json.REMOVED.git-id
Normal file
@@ -0,0 +1 @@
|
||||
a22dd75fdaddd98d93ccf3467f78af832dc94864
|
||||
39
contracts/package.json
Normal file
39
contracts/package.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "semaphore-contracts",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
"watch": "tsc --watch",
|
||||
"ganache": "./scripts/runGanache.sh",
|
||||
"compileSol": "./scripts/compileSol.sh",
|
||||
"test-semaphore": "NODE_ENV=local-dev jest --forceExit --testPathPattern Semaphore.test.ts",
|
||||
"test-semaphore-debug": "NODE_ENV=local-dev node --inspect-brk ./node_modules/jest/bin/jest.js --testPathPattern Semaphore.test.ts",
|
||||
"test-verifier": "NODE_ENV=local-dev jest --forceExit --testPathPattern FixedVerifier.test.ts",
|
||||
"test-verifier-debug": "NODE_ENV=local-dev node --inspect-brk ./node_modules/jest/bin/jest.js --testPathPattern FixedVerifier.test.ts",
|
||||
"test-mt": "NODE_ENV=local-dev jest --forceExit --testPathPattern IncrementalMerkleTree.test.ts",
|
||||
"test-mt-debug": "NODE_ENV=local-dev node --inspect-brk ./node_modules/jest/bin/jest.js --testPathPattern IncrementalMerkleTree.test.ts",
|
||||
"build": "tsc"
|
||||
},
|
||||
"_moduleAliases": {
|
||||
"@semaphore-contracts": "."
|
||||
},
|
||||
"dependencies": {
|
||||
"semaphore-config": "0.0.1",
|
||||
"ethers": "4.0.38",
|
||||
"circomlib": "https://github.com/kobigurk/circomlib.git#347822604996bf25f659f96ee0f02810a1f71bb0",
|
||||
"ganache-cli": "^6.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.0.20",
|
||||
"@types/node": "^12.7.7",
|
||||
"etherlime": "^2.2.4",
|
||||
"etherlime-lib": "^1.1.5",
|
||||
"jest": "^24.9.0",
|
||||
"libsemaphore": "^1.0.14",
|
||||
"semaphore-merkle-tree": "^1.0.12",
|
||||
"module-alias": "^2.2.2",
|
||||
"truffle-artifactor": "^4.0.30",
|
||||
"ts-jest": "^24.1.0"
|
||||
}
|
||||
}
|
||||
19
contracts/scripts/compileSol.sh
Executable file
19
contracts/scripts/compileSol.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
echo 'Building contracts'
|
||||
|
||||
# Delete old files
|
||||
rm -rf ./compiled/*
|
||||
|
||||
mkdir -p ./compiled/abis
|
||||
|
||||
# Copy the Semaphore contracts from the submodule into solidity/
|
||||
|
||||
npx etherlime compile --solcVersion=native --buildDirectory=compiled --workingDirectory=sol --exportAbi
|
||||
|
||||
# Build the MiMC contract from bytecode
|
||||
node build/buildMiMC.js
|
||||
23
contracts/scripts/runGanache.sh
Executable file
23
contracts/scripts/runGanache.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This mnemonic seed is well-known to the public. If you transfer any ETH to
|
||||
# addreses derived from it, expect it to be swept away.
|
||||
|
||||
# Etherlime's ganache command works differently from ganache-cli. It
|
||||
# concatenates `--count minus 10` new accounts generated from `--mnemonic`. The
|
||||
# first 10 are predefined.
|
||||
npx etherlime ganache --mnemonic "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat" --gasLimit=8800000 count=10 --networkId 1234
|
||||
|
||||
#npx ganache-cli -a 10 -m='candy maple cake sugar pudding cream honey rich smooth crumble sweet treat' --gasLimit=8800000 --port 8545 -i 1234
|
||||
|
||||
# ETH accounts from the 'candy maple...' mnemonic
|
||||
#0: 0x627306090abab3a6e1400e9345bc60c78a8bef57
|
||||
#1: 0xf17f52151ebef6c7334fad080c5704d77216b732
|
||||
#2: 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef
|
||||
#3: 0x821aea9a577a9b44299b9c15c88cf3087f3b5544
|
||||
#4: 0x0d1d4e623d10f9fba5db95830f7d3839406c6af2
|
||||
#5: 0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e
|
||||
#6: 0x2191ef87e392377ec08e7c08eb105ef5448eced5
|
||||
#7: 0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5
|
||||
#8: 0x6330a553fc93768f612722bb8c2ec78ac90b3bbc
|
||||
#9: 0x5aeda56215b167893e80b4fe645ba6d5bab767de
|
||||
10
contracts/scripts/runTestsInCircleCi.sh
Executable file
10
contracts/scripts/runTestsInCircleCi.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash -xe
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
cd ..
|
||||
|
||||
npm run ganache &
|
||||
sleep 3 &&
|
||||
npm run test-semaphore &&
|
||||
sleep 1 &&
|
||||
npm run test-mt
|
||||
180
contracts/sol/IncrementalMerkleTree.sol
Normal file
180
contracts/sol/IncrementalMerkleTree.sol
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Semaphore - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2020 Barry WhiteHat <barrywhitehat@protonmail.com>, Kobi
|
||||
* Gurkan <kobigurk@gmail.com> and Koh Wei Jie (contact@kohweijie.com)
|
||||
*
|
||||
* This file is part of Semaphore.
|
||||
*
|
||||
* Semaphore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Semaphore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Semaphore. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
import { SnarkConstants } from "./SnarkConstants.sol";
|
||||
import { MiMC } from "./MiMC.sol";
|
||||
|
||||
contract IncrementalMerkleTree is SnarkConstants {
|
||||
// The maximum tree depth
|
||||
uint8 internal constant MAX_DEPTH = 32;
|
||||
|
||||
// The tree depth
|
||||
uint8 internal treeLevels;
|
||||
|
||||
// The number of inserted leaves
|
||||
uint256 internal nextLeafIndex = 0;
|
||||
|
||||
// The Merkle root
|
||||
uint256 public root;
|
||||
|
||||
// The Merkle path to the leftmost leaf upon initialisation. It *should
|
||||
// not* be modified after it has been set by the `initMerkleTree` function.
|
||||
// Caching these values is essential to efficient appends.
|
||||
uint256[MAX_DEPTH] internal zeros;
|
||||
|
||||
// Allows you to compute the path to the element (but it's not the path to
|
||||
// the elements). Caching these values is essential to efficient appends.
|
||||
uint256[MAX_DEPTH] internal filledSubtrees;
|
||||
|
||||
// Whether the contract has already seen a particular Merkle tree root
|
||||
mapping (uint256 => bool) public rootHistory;
|
||||
|
||||
event LeafInsertion(uint256 indexed leaf, uint256 indexed leafIndex);
|
||||
|
||||
/*
|
||||
* Stores the Merkle root and intermediate values (the Merkle path to the
|
||||
* the first leaf) assuming that all leaves are set to _zeroValue.
|
||||
* @param _treeLevels The number of levels of the tree
|
||||
* @param _zeroValue The value to set for every leaf. Ideally, this should
|
||||
* be a nothing-up-my-sleeve value, so that nobody can
|
||||
* say that the deployer knows the preimage of an empty
|
||||
* leaf.
|
||||
*/
|
||||
constructor(uint8 _treeLevels, uint256 _zeroValue) internal {
|
||||
// Limit the Merkle tree to MAX_DEPTH levels
|
||||
require(
|
||||
_treeLevels > 0 && _treeLevels <= MAX_DEPTH,
|
||||
"IncrementalMerkleTree: _treeLevels must be between 0 and 33"
|
||||
);
|
||||
|
||||
/*
|
||||
To initialise the Merkle tree, we need to calculate the Merkle root
|
||||
assuming that each leaf is the zero value.
|
||||
|
||||
H(H(a,b), H(c,d))
|
||||
/ \
|
||||
H(a,b) H(c,d)
|
||||
/ \ / \
|
||||
a b c d
|
||||
|
||||
`zeros` and `filledSubtrees` will come in handy later when we do
|
||||
inserts or updates. e.g when we insert a value in index 1, we will
|
||||
need to look up values from those arrays to recalculate the Merkle
|
||||
root.
|
||||
*/
|
||||
treeLevels = _treeLevels;
|
||||
|
||||
zeros[0] = _zeroValue;
|
||||
|
||||
uint256 currentZero = _zeroValue;
|
||||
for (uint8 i = 1; i < _treeLevels; i++) {
|
||||
uint256 hashed = hashLeftRight(currentZero, currentZero);
|
||||
zeros[i] = hashed;
|
||||
filledSubtrees[i] = hashed;
|
||||
currentZero = hashed;
|
||||
}
|
||||
|
||||
root = hashLeftRight(currentZero, currentZero);
|
||||
}
|
||||
|
||||
/*
|
||||
* Inserts a leaf into the Merkle tree and updates the root and filled
|
||||
* subtrees.
|
||||
* @param _leaf The value to insert. It must be less than the snark scalar
|
||||
* field or this function will throw.
|
||||
* @return The leaf index.
|
||||
*/
|
||||
function insertLeaf(uint256 _leaf) internal returns (uint256) {
|
||||
require(
|
||||
_leaf < SNARK_SCALAR_FIELD,
|
||||
"IncrementalMerkleTree: insertLeaf argument must be < SNARK_SCALAR_FIELD"
|
||||
);
|
||||
|
||||
uint256 currentIndex = nextLeafIndex;
|
||||
|
||||
uint256 depth = uint256(treeLevels);
|
||||
require(currentIndex < uint256(2) ** depth, "IncrementalMerkleTree: tree is full");
|
||||
|
||||
uint256 currentLevelHash = _leaf;
|
||||
uint256 left;
|
||||
uint256 right;
|
||||
|
||||
for (uint8 i = 0; i < treeLevels; i++) {
|
||||
// if current_index is 5, for instance, over the iterations it will
|
||||
// look like this: 5, 2, 1, 0, 0, 0 ...
|
||||
|
||||
if (currentIndex % 2 == 0) {
|
||||
// For later values of `i`, use the previous hash as `left`, and
|
||||
// the (hashed) zero value for `right`
|
||||
left = currentLevelHash;
|
||||
right = zeros[i];
|
||||
|
||||
filledSubtrees[i] = currentLevelHash;
|
||||
} else {
|
||||
left = filledSubtrees[i];
|
||||
right = currentLevelHash;
|
||||
}
|
||||
|
||||
currentLevelHash = hashLeftRight(left, right);
|
||||
|
||||
// equivalent to currentIndex /= 2;
|
||||
currentIndex >>= 1;
|
||||
}
|
||||
|
||||
root = currentLevelHash;
|
||||
rootHistory[root] = true;
|
||||
|
||||
uint256 n = nextLeafIndex;
|
||||
nextLeafIndex += 1;
|
||||
|
||||
emit LeafInsertion(_leaf, n);
|
||||
|
||||
return currentIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
* Concatenates and hashes two `uint256` values (left and right) using
|
||||
* a combination of MiMCSponge and `addmod`.
|
||||
* @param _left The first value
|
||||
* @param _right The second value
|
||||
* @return The uint256 hash of _left and _right
|
||||
*/
|
||||
function hashLeftRight(uint256 _left, uint256 _right) internal pure returns (uint256) {
|
||||
|
||||
// Solidity documentation states:
|
||||
// `addmod(uint x, uint y, uint k) returns (uint)`:
|
||||
// compute (x + y) % k where the addition is performed with arbitrary
|
||||
// precision and does not wrap around at 2**256. Assert that k != 0
|
||||
// starting from version 0.5.0.
|
||||
|
||||
uint256 R = _left;
|
||||
uint256 C = 0;
|
||||
|
||||
(R, C) = MiMC.MiMCSponge(R, 0);
|
||||
|
||||
R = addmod(R, _right, SNARK_SCALAR_FIELD);
|
||||
(R, C) = MiMC.MiMCSponge(R, C);
|
||||
|
||||
return R;
|
||||
}
|
||||
}
|
||||
35
contracts/sol/IncrementalMerkleTreeClient.sol
Normal file
35
contracts/sol/IncrementalMerkleTreeClient.sol
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Semaphore - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2020 Barry WhiteHat <barrywhitehat@protonmail.com>, Kobi
|
||||
* Gurkan <kobigurk@gmail.com> and Koh Wei Jie (contact@kohweijie.com)
|
||||
*
|
||||
* This file is part of Semaphore.
|
||||
*
|
||||
* Semaphore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Semaphore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Semaphore. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
import { IncrementalMerkleTree } from './IncrementalMerkleTree.sol';
|
||||
|
||||
contract IncrementalMerkleTreeClient is IncrementalMerkleTree{
|
||||
constructor(uint8 _treeLevels, uint256 _zeroValue)
|
||||
IncrementalMerkleTree(_treeLevels, _zeroValue)
|
||||
public {
|
||||
}
|
||||
|
||||
function insertLeafAsClient(uint256 _leaf) public {
|
||||
insertLeaf(_leaf);
|
||||
}
|
||||
}
|
||||
30
contracts/sol/MiMC.sol
Normal file
30
contracts/sol/MiMC.sol
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Semaphore - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2020 Barry WhiteHat <barrywhitehat@protonmail.com>, Kobi
|
||||
* Gurkan <kobigurk@gmail.com> and Koh Wei Jie (contact@kohweijie.com)
|
||||
*
|
||||
* This file is part of Semaphore.
|
||||
*
|
||||
* Semaphore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Semaphore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Semaphore. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
library MiMC {
|
||||
// Note that this function could also be called "MiMCFeistel", but we name
|
||||
// it "MiMCSponge" for consistency.
|
||||
function MiMCSponge(uint256 in_xL, uint256 in_xR) pure public
|
||||
returns (uint256 xL, uint256 xR);
|
||||
}
|
||||
|
||||
75
contracts/sol/Ownable.sol
Normal file
75
contracts/sol/Ownable.sol
Normal file
@@ -0,0 +1,75 @@
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
/**
|
||||
* @dev Contract module which provides a basic access control mechanism, where
|
||||
* there is an account (an owner) that can be granted exclusive access to
|
||||
* specific functions.
|
||||
*
|
||||
* This module is used through inheritance. It will make available the modifier
|
||||
* `onlyOwner`, which can be aplied to your functions to restrict their use to
|
||||
* the owner.
|
||||
*/
|
||||
contract Ownable {
|
||||
address private _owner;
|
||||
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||||
|
||||
/**
|
||||
* @dev Initializes the contract setting the deployer as the initial owner.
|
||||
*/
|
||||
constructor () internal {
|
||||
_owner = msg.sender;
|
||||
emit OwnershipTransferred(address(0), _owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the address of the current owner.
|
||||
*/
|
||||
function owner() public view returns (address) {
|
||||
return _owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Throws if called by any account other than the owner.
|
||||
*/
|
||||
modifier onlyOwner() {
|
||||
require(isOwner(), "Ownable: caller is not the owner");
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns true if the caller is the current owner.
|
||||
*/
|
||||
function isOwner() public view returns (bool) {
|
||||
return msg.sender == _owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Leaves the contract without owner. It will not be possible to call
|
||||
* `onlyOwner` functions anymore. Can only be called by the current owner.
|
||||
*
|
||||
* > Note: Renouncing ownership will leave the contract without an owner,
|
||||
* thereby removing any functionality that is only available to the owner.
|
||||
*/
|
||||
function renounceOwnership() public onlyOwner {
|
||||
emit OwnershipTransferred(_owner, address(0));
|
||||
_owner = address(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfers ownership of the contract to a new account (`newOwner`).
|
||||
* Can only be called by the current owner.
|
||||
*/
|
||||
function transferOwnership(address newOwner) public onlyOwner {
|
||||
_transferOwnership(newOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfers ownership of the contract to a new account (`newOwner`).
|
||||
*/
|
||||
function _transferOwnership(address newOwner) internal {
|
||||
require(newOwner != address(0), "Ownable: new owner is the zero address");
|
||||
emit OwnershipTransferred(_owner, newOwner);
|
||||
_owner = newOwner;
|
||||
}
|
||||
}
|
||||
486
contracts/sol/Semaphore.sol
Normal file
486
contracts/sol/Semaphore.sol
Normal file
@@ -0,0 +1,486 @@
|
||||
/*
|
||||
* Semaphore - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2020 Barry WhiteHat <barrywhitehat@protonmail.com>, Kobi
|
||||
* Gurkan <kobigurk@gmail.com> and Koh Wei Jie (contact@kohweijie.com)
|
||||
*
|
||||
* This file is part of Semaphore.
|
||||
*
|
||||
* Semaphore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Semaphore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Semaphore. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
import "./verifier.sol";
|
||||
import { IncrementalMerkleTree } from "./IncrementalMerkleTree.sol";
|
||||
import "./Ownable.sol";
|
||||
|
||||
contract Semaphore is Verifier, IncrementalMerkleTree, Ownable {
|
||||
// The external nullifier helps to prevent double-signalling by the same
|
||||
// user. An external nullifier can be active or deactivated.
|
||||
|
||||
// Each node in the linked list
|
||||
struct ExternalNullifierNode {
|
||||
uint232 next;
|
||||
bool exists;
|
||||
bool isActive;
|
||||
}
|
||||
|
||||
// We store the external nullifiers using a mapping of the form:
|
||||
// enA => { next external nullifier; if enA exists; if enA is active }
|
||||
// Think of it as a linked list.
|
||||
mapping (uint232 => ExternalNullifierNode) public
|
||||
externalNullifierLinkedList;
|
||||
|
||||
uint256 public numExternalNullifiers = 0;
|
||||
|
||||
// First and last external nullifiers for linked list enumeration
|
||||
uint232 public firstExternalNullifier = 0;
|
||||
uint232 public lastExternalNullifier = 0;
|
||||
|
||||
// Whether broadcastSignal() can only be called by the owner of this
|
||||
// contract. This is the case as a safe default.
|
||||
bool public isBroadcastPermissioned = true;
|
||||
|
||||
// Whether the contract has already seen a particular nullifier hash
|
||||
mapping (uint256 => bool) public nullifierHashHistory;
|
||||
|
||||
event PermissionSet(bool indexed newPermission);
|
||||
event ExternalNullifierAdd(uint232 indexed externalNullifier);
|
||||
event ExternalNullifierChangeStatus(
|
||||
uint232 indexed externalNullifier,
|
||||
bool indexed active
|
||||
);
|
||||
|
||||
// This value should be equal to
|
||||
// 0x7d10c03d1f7884c85edee6353bd2b2ffbae9221236edde3778eac58089912bc0
|
||||
// which you can calculate using the following ethersjs code:
|
||||
// ethers.utils.solidityKeccak256(['bytes'], [ethers.utils.toUtf8Bytes('Semaphore')])
|
||||
// By setting the value of unset (empty) tree leaves to this
|
||||
// nothing-up-my-sleeve value, the authors hope to demonstrate that they do
|
||||
// not have its preimage and therefore cannot spend funds they do not own.
|
||||
|
||||
uint256 public NOTHING_UP_MY_SLEEVE_ZERO =
|
||||
uint256(keccak256(abi.encodePacked('Semaphore'))) % SNARK_SCALAR_FIELD;
|
||||
|
||||
/*
|
||||
* If broadcastSignal is permissioned, check if msg.sender is the contract
|
||||
* owner
|
||||
*/
|
||||
modifier onlyOwnerIfPermissioned() {
|
||||
require(
|
||||
!isBroadcastPermissioned || isOwner(),
|
||||
"Semaphore: broadcast permission denied"
|
||||
);
|
||||
|
||||
_;
|
||||
}
|
||||
|
||||
/*
|
||||
* @param _treeLevels The depth of the identity tree.
|
||||
* @param _firstExternalNullifier The first identity nullifier to add.
|
||||
*/
|
||||
constructor(uint8 _treeLevels, uint232 _firstExternalNullifier)
|
||||
IncrementalMerkleTree(_treeLevels, NOTHING_UP_MY_SLEEVE_ZERO)
|
||||
Ownable()
|
||||
public {
|
||||
addEn(_firstExternalNullifier, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Registers a new user.
|
||||
* @param _identity_commitment The user's identity commitment, which is the
|
||||
* hash of their public key and their identity
|
||||
* nullifier (a random 31-byte value). It should
|
||||
* be the output of a Pedersen hash. It is the
|
||||
* responsibility of the caller to verify this.
|
||||
*/
|
||||
function insertIdentity(uint256 _identityCommitment) public onlyOwner
|
||||
returns (uint256) {
|
||||
// Ensure that the given identity commitment is not the zero value
|
||||
require(
|
||||
_identityCommitment != NOTHING_UP_MY_SLEEVE_ZERO,
|
||||
"Semaphore: identity commitment cannot be the nothing-up-my-sleeve-value"
|
||||
);
|
||||
|
||||
return insertLeaf(_identityCommitment);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if all values within pi_a, pi_b, and pi_c of a zk-SNARK are less
|
||||
* than the scalar field.
|
||||
* @param _a The corresponding `a` parameter to verifier.sol's
|
||||
* verifyProof()
|
||||
* @param _b The corresponding `b` parameter to verifier.sol's
|
||||
* verifyProof()
|
||||
* @param _c The corresponding `c` parameter to verifier.sol's
|
||||
verifyProof()
|
||||
*/
|
||||
function areAllValidFieldElements(
|
||||
uint256[8] memory _proof
|
||||
) internal pure returns (bool) {
|
||||
return
|
||||
_proof[0] < SNARK_SCALAR_FIELD &&
|
||||
_proof[1] < SNARK_SCALAR_FIELD &&
|
||||
_proof[2] < SNARK_SCALAR_FIELD &&
|
||||
_proof[3] < SNARK_SCALAR_FIELD &&
|
||||
_proof[4] < SNARK_SCALAR_FIELD &&
|
||||
_proof[5] < SNARK_SCALAR_FIELD &&
|
||||
_proof[6] < SNARK_SCALAR_FIELD &&
|
||||
_proof[7] < SNARK_SCALAR_FIELD;
|
||||
}
|
||||
|
||||
/*
|
||||
* Produces a keccak256 hash of the given signal, shifted right by 8 bits.
|
||||
* @param _signal The signal to hash
|
||||
*/
|
||||
function hashSignal(bytes memory _signal) internal pure returns (uint256) {
|
||||
return uint256(keccak256(_signal)) >> 8;
|
||||
}
|
||||
|
||||
/*
|
||||
* A convenience function which returns a uint256 array of 8 elements which
|
||||
* comprise a Groth16 zk-SNARK proof's pi_a, pi_b, and pi_c values.
|
||||
* @param _a The corresponding `a` parameter to verifier.sol's
|
||||
* verifyProof()
|
||||
* @param _b The corresponding `b` parameter to verifier.sol's
|
||||
* verifyProof()
|
||||
* @param _c The corresponding `c` parameter to verifier.sol's
|
||||
* verifyProof()
|
||||
*/
|
||||
function packProof (
|
||||
uint256[2] memory _a,
|
||||
uint256[2][2] memory _b,
|
||||
uint256[2] memory _c
|
||||
) public pure returns (uint256[8] memory) {
|
||||
|
||||
return [
|
||||
_a[0],
|
||||
_a[1],
|
||||
_b[0][0],
|
||||
_b[0][1],
|
||||
_b[1][0],
|
||||
_b[1][1],
|
||||
_c[0],
|
||||
_c[1]
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
* A convenience function which converts an array of 8 elements, generated
|
||||
* by packProof(), into a format which verifier.sol's verifyProof()
|
||||
* accepts.
|
||||
* @param _proof The proof elements.
|
||||
*/
|
||||
function unpackProof(
|
||||
uint256[8] memory _proof
|
||||
) public pure returns (
|
||||
uint256[2] memory,
|
||||
uint256[2][2] memory,
|
||||
uint256[2] memory
|
||||
) {
|
||||
|
||||
return (
|
||||
[_proof[0], _proof[1]],
|
||||
[
|
||||
[_proof[2], _proof[3]],
|
||||
[_proof[4], _proof[5]]
|
||||
],
|
||||
[_proof[6], _proof[7]]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* A convenience view function which helps operators to easily verify all
|
||||
* inputs to broadcastSignal() using a single contract call. This helps
|
||||
* them to save gas by detecting invalid inputs before they invoke
|
||||
* broadcastSignal(). Note that this function does the same checks as
|
||||
* `isValidSignalAndProof` but returns a bool instead of using require()
|
||||
* statements.
|
||||
* @param _signal The signal to broadcast
|
||||
* @param _proof The proof elements.
|
||||
* @param _root The Merkle tree root
|
||||
* @param _nullifiersHash The nullifiers hash
|
||||
* @param _signalHash The signal hash. This is included so as to verify in
|
||||
* Solidity that the signal hash computed off-chain
|
||||
* matches.
|
||||
* @param _externalNullifier The external nullifier
|
||||
*/
|
||||
function preBroadcastCheck (
|
||||
bytes memory _signal,
|
||||
uint256[8] memory _proof,
|
||||
uint256 _root,
|
||||
uint256 _nullifiersHash,
|
||||
uint256 _signalHash,
|
||||
uint232 _externalNullifier
|
||||
) public view returns (bool) {
|
||||
|
||||
uint256[4] memory publicSignals =
|
||||
[_root, _nullifiersHash, _signalHash, _externalNullifier];
|
||||
|
||||
(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c) =
|
||||
unpackProof(_proof);
|
||||
|
||||
return nullifierHashHistory[_nullifiersHash] == false &&
|
||||
hashSignal(_signal) == _signalHash &&
|
||||
_signalHash == hashSignal(_signal) &&
|
||||
isExternalNullifierActive(_externalNullifier) &&
|
||||
rootHistory[_root] &&
|
||||
areAllValidFieldElements(_proof) &&
|
||||
_root < SNARK_SCALAR_FIELD &&
|
||||
_nullifiersHash < SNARK_SCALAR_FIELD &&
|
||||
verifyProof(a, b, c, publicSignals);
|
||||
}
|
||||
|
||||
/*
|
||||
* A modifier which ensures that the signal and proof are valid.
|
||||
* @param _signal The signal to broadcast
|
||||
* @param _proof The proof elements.
|
||||
* @param _root The Merkle tree root
|
||||
* @param _nullifiersHash The nullifiers hash
|
||||
* @param _signalHash The signal hash
|
||||
* @param _externalNullifier The external nullifier
|
||||
*/
|
||||
modifier isValidSignalAndProof (
|
||||
bytes memory _signal,
|
||||
uint256[8] memory _proof,
|
||||
uint256 _root,
|
||||
uint256 _nullifiersHash,
|
||||
uint232 _externalNullifier
|
||||
) {
|
||||
// Check whether each element in _proof is a valid field element. Even
|
||||
// if verifier.sol does this check too, it is good to do so here for
|
||||
// the sake of good protocol design.
|
||||
require(
|
||||
areAllValidFieldElements(_proof),
|
||||
"Semaphore: invalid field element(s) in proof"
|
||||
);
|
||||
|
||||
// Check whether the nullifier hash has been seen
|
||||
require(
|
||||
nullifierHashHistory[_nullifiersHash] == false,
|
||||
"Semaphore: nullifier already seen"
|
||||
);
|
||||
|
||||
// Check whether the nullifier hash is active
|
||||
require(
|
||||
isExternalNullifierActive(_externalNullifier),
|
||||
"Semaphore: external nullifier not found"
|
||||
);
|
||||
|
||||
// Check whether the given Merkle root has been seen previously
|
||||
require(rootHistory[_root], "Semaphore: root not seen");
|
||||
|
||||
uint256 signalHash = hashSignal(_signal);
|
||||
|
||||
// Check whether _nullifiersHash is a valid field element.
|
||||
require(
|
||||
_nullifiersHash < SNARK_SCALAR_FIELD,
|
||||
"Semaphore: the nullifiers hash must be lt the snark scalar field"
|
||||
);
|
||||
|
||||
uint256[4] memory publicSignals =
|
||||
[_root, _nullifiersHash, signalHash, _externalNullifier];
|
||||
|
||||
(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c) =
|
||||
unpackProof(_proof);
|
||||
|
||||
require(
|
||||
verifyProof(a, b, c, publicSignals),
|
||||
"Semaphore: invalid proof"
|
||||
);
|
||||
|
||||
// Note that we don't need to check if signalHash is less than
|
||||
// SNARK_SCALAR_FIELD because it always holds true due to the
|
||||
// definition of hashSignal()
|
||||
|
||||
_;
|
||||
}
|
||||
|
||||
/*
|
||||
* Broadcasts the signal.
|
||||
* @param _signal The signal to broadcast
|
||||
* @param _proof The proof elements.
|
||||
* @param _root The root of the Merkle tree (the 1st public signal)
|
||||
* @param _nullifiersHash The nullifiers hash (the 2nd public signal)
|
||||
* @param _externalNullifier The nullifiers hash (the 4th public signal)
|
||||
*/
|
||||
function broadcastSignal(
|
||||
bytes memory _signal,
|
||||
uint256[8] memory _proof,
|
||||
uint256 _root,
|
||||
uint256 _nullifiersHash,
|
||||
uint232 _externalNullifier
|
||||
) public
|
||||
onlyOwnerIfPermissioned
|
||||
isValidSignalAndProof(
|
||||
_signal, _proof, _root, _nullifiersHash, _externalNullifier
|
||||
)
|
||||
{
|
||||
// Client contracts should be responsible for storing the signal and/or
|
||||
// emitting it as an event
|
||||
|
||||
// Store the nullifiers hash to prevent double-signalling
|
||||
nullifierHashHistory[_nullifiersHash] = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* A private helper function which adds an external nullifier.
|
||||
* @param _externalNullifier The external nullifier to add.
|
||||
* @param _isFirst Whether _externalNullifier is the first external
|
||||
* nullifier. Only the constructor should set _isFirst to true when it
|
||||
* calls addEn().
|
||||
*/
|
||||
function addEn(uint232 _externalNullifier, bool isFirst) private {
|
||||
if (isFirst) {
|
||||
firstExternalNullifier = _externalNullifier;
|
||||
} else {
|
||||
// The external nullifier must not have already been set
|
||||
require(
|
||||
externalNullifierLinkedList[_externalNullifier].exists == false,
|
||||
"Semaphore: external nullifier already set"
|
||||
);
|
||||
|
||||
// Connect the previously added external nullifier node to this one
|
||||
externalNullifierLinkedList[lastExternalNullifier].next =
|
||||
_externalNullifier;
|
||||
}
|
||||
|
||||
// Add a new external nullifier
|
||||
externalNullifierLinkedList[_externalNullifier].next = 0;
|
||||
externalNullifierLinkedList[_externalNullifier].isActive = true;
|
||||
externalNullifierLinkedList[_externalNullifier].exists = true;
|
||||
|
||||
// Set the last external nullifier to this one
|
||||
lastExternalNullifier = _externalNullifier;
|
||||
|
||||
numExternalNullifiers ++;
|
||||
|
||||
emit ExternalNullifierAdd(_externalNullifier);
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds an external nullifier to the contract. This external nullifier is
|
||||
* active once it is added. Only the owner can do this.
|
||||
* @param _externalNullifier The new external nullifier to set.
|
||||
*/
|
||||
function addExternalNullifier(uint232 _externalNullifier) public
|
||||
onlyOwner {
|
||||
addEn(_externalNullifier, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deactivate an external nullifier. The external nullifier must already be
|
||||
* active for this function to work. Only the owner can do this.
|
||||
* @param _externalNullifier The new external nullifier to deactivate.
|
||||
*/
|
||||
function deactivateExternalNullifier(uint232 _externalNullifier) public
|
||||
onlyOwner {
|
||||
// The external nullifier must already exist
|
||||
require(
|
||||
externalNullifierLinkedList[_externalNullifier].exists,
|
||||
"Semaphore: external nullifier not found"
|
||||
);
|
||||
|
||||
// The external nullifier must already be active
|
||||
require(
|
||||
externalNullifierLinkedList[_externalNullifier].isActive == true,
|
||||
"Semaphore: external nullifier already deactivated"
|
||||
);
|
||||
|
||||
// Deactivate the external nullifier. Note that we don't change the
|
||||
// value of nextEn.
|
||||
externalNullifierLinkedList[_externalNullifier].isActive = false;
|
||||
|
||||
emit ExternalNullifierChangeStatus(_externalNullifier, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reactivate an external nullifier. The external nullifier must already be
|
||||
* inactive for this function to work. Only the owner can do this.
|
||||
* @param _externalNullifier The new external nullifier to reactivate.
|
||||
*/
|
||||
function reactivateExternalNullifier(uint232 _externalNullifier) public
|
||||
onlyOwner {
|
||||
// The external nullifier must already exist
|
||||
require(
|
||||
externalNullifierLinkedList[_externalNullifier].exists,
|
||||
"Semaphore: external nullifier not found"
|
||||
);
|
||||
|
||||
// The external nullifier must already have been deactivated
|
||||
require(
|
||||
externalNullifierLinkedList[_externalNullifier].isActive == false,
|
||||
"Semaphore: external nullifier is already active"
|
||||
);
|
||||
|
||||
// Reactivate the external nullifier
|
||||
externalNullifierLinkedList[_externalNullifier].isActive = true;
|
||||
|
||||
emit ExternalNullifierChangeStatus(_externalNullifier, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if and only if the specified external nullifier is active
|
||||
* @param _externalNullifier The specified external nullifier.
|
||||
*/
|
||||
function isExternalNullifierActive(uint232 _externalNullifier) public view
|
||||
returns (bool) {
|
||||
return externalNullifierLinkedList[_externalNullifier].isActive;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the next external nullifier after the specified external
|
||||
* nullifier in the linked list.
|
||||
* @param _externalNullifier The specified external nullifier.
|
||||
*/
|
||||
function getNextExternalNullifier(uint232 _externalNullifier) public view
|
||||
returns (uint232) {
|
||||
|
||||
require(
|
||||
externalNullifierLinkedList[_externalNullifier].exists,
|
||||
"Semaphore: no such external nullifier"
|
||||
);
|
||||
|
||||
uint232 n = externalNullifierLinkedList[_externalNullifier].next;
|
||||
|
||||
require(
|
||||
numExternalNullifiers > 1 && externalNullifierLinkedList[n].exists,
|
||||
"Semaphore: no external nullifier exists after the specified one"
|
||||
);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of inserted identity commitments.
|
||||
*/
|
||||
function getNumIdentityCommitments() public view returns (uint256) {
|
||||
return nextLeafIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the `isBroadcastPermissioned` storage variable, which determines
|
||||
* whether broadcastSignal can or cannot be called by only the contract
|
||||
* owner.
|
||||
* @param _newPermission True if the broadcastSignal can only be called by
|
||||
* the contract owner; and False otherwise.
|
||||
*/
|
||||
function setPermissioning(bool _newPermission) public onlyOwner {
|
||||
|
||||
isBroadcastPermissioned = _newPermission;
|
||||
|
||||
emit PermissionSet(_newPermission);
|
||||
}
|
||||
}
|
||||
106
contracts/sol/SemaphoreClient.sol
Normal file
106
contracts/sol/SemaphoreClient.sol
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Semaphore - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2020 Barry WhiteHat <barrywhitehat@protonmail.com>, Kobi
|
||||
* Gurkan <kobigurk@gmail.com> and Koh Wei Jie (contact@kohweijie.com)
|
||||
*
|
||||
* This file is part of Semaphore.
|
||||
*
|
||||
* Semaphore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Semaphore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Semaphore. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
import { Semaphore } from './Semaphore.sol';
|
||||
|
||||
contract SemaphoreClient {
|
||||
uint256[] public identityCommitments;
|
||||
|
||||
// A mapping of all signals broadcasted
|
||||
mapping (uint256 => bytes) public signalIndexToSignal;
|
||||
|
||||
// A mapping between signal indices to external nullifiers
|
||||
mapping (uint256 => uint256) public signalIndexToExternalNullifier;
|
||||
|
||||
// The next index of the `signalIndexToSignal` mapping
|
||||
uint256 public nextSignalIndex = 0;
|
||||
|
||||
Semaphore public semaphore;
|
||||
|
||||
event SignalBroadcastByClient(uint256 indexed signalIndex);
|
||||
|
||||
constructor(Semaphore _semaphore) public {
|
||||
semaphore = _semaphore;
|
||||
}
|
||||
|
||||
function getIdentityCommitments() public view returns (uint256 [] memory) {
|
||||
return identityCommitments;
|
||||
}
|
||||
|
||||
function getIdentityCommitment(uint256 _index) public view returns (uint256) {
|
||||
return identityCommitments[_index];
|
||||
}
|
||||
|
||||
function insertIdentityAsClient(uint256 _leaf) public {
|
||||
semaphore.insertIdentity(_leaf);
|
||||
identityCommitments.push(_leaf);
|
||||
}
|
||||
|
||||
function addExternalNullifier(uint232 _externalNullifier) public {
|
||||
semaphore.addExternalNullifier(_externalNullifier);
|
||||
}
|
||||
|
||||
function deactivateExternalNullifier(uint232 _externalNullifier) public {
|
||||
semaphore.deactivateExternalNullifier(_externalNullifier);
|
||||
}
|
||||
|
||||
function reactivateExternalNullifier(uint232 _externalNullifier) public {
|
||||
semaphore.reactivateExternalNullifier(_externalNullifier);
|
||||
}
|
||||
|
||||
function broadcastSignal(
|
||||
bytes memory _signal,
|
||||
uint256[8] memory _proof,
|
||||
uint256 _root,
|
||||
uint256 _nullifiersHash,
|
||||
uint232 _externalNullifier
|
||||
) public {
|
||||
uint256 signalIndex = nextSignalIndex;
|
||||
|
||||
// store the signal
|
||||
signalIndexToSignal[nextSignalIndex] = _signal;
|
||||
|
||||
// map the the signal index to the given external nullifier
|
||||
signalIndexToExternalNullifier[nextSignalIndex] = _externalNullifier;
|
||||
|
||||
// increment the signal index
|
||||
nextSignalIndex ++;
|
||||
|
||||
// broadcast the signal
|
||||
semaphore.broadcastSignal(_signal, _proof, _root, _nullifiersHash, _externalNullifier);
|
||||
|
||||
emit SignalBroadcastByClient(signalIndex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the external nullifier which a signal at _index broadcasted to
|
||||
* @param _index The index to use to look up the signalIndexToExternalNullifier mapping
|
||||
*/
|
||||
function getExternalNullifierBySignalIndex(uint256 _index) public view returns (uint256) {
|
||||
return signalIndexToExternalNullifier[_index];
|
||||
}
|
||||
|
||||
function setPermissioning(bool _newPermission) public {
|
||||
semaphore.setPermissioning(_newPermission);
|
||||
}
|
||||
}
|
||||
27
contracts/sol/SnarkConstants.sol
Normal file
27
contracts/sol/SnarkConstants.sol
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Semaphore - Zero-knowledge signaling on Ethereum
|
||||
* Copyright (C) 2020 Barry WhiteHat <barrywhitehat@protonmail.com>, Kobi
|
||||
* Gurkan <kobigurk@gmail.com> and Koh Wei Jie (contact@kohweijie.com)
|
||||
*
|
||||
* This file is part of Semaphore.
|
||||
*
|
||||
* Semaphore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Semaphore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Semaphore. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
contract SnarkConstants {
|
||||
// The scalar field
|
||||
uint256 internal constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
}
|
||||
231
contracts/sol/verifier.sol
Normal file
231
contracts/sol/verifier.sol
Normal file
@@ -0,0 +1,231 @@
|
||||
// Copyright 2017 Christian Reitwiessner
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
// 2019 OKIMS
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
library Pairing {
|
||||
|
||||
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
struct G1Point {
|
||||
uint256 X;
|
||||
uint256 Y;
|
||||
}
|
||||
|
||||
// Encoding of field elements is: X[0] * z + X[1]
|
||||
struct G2Point {
|
||||
uint256[2] X;
|
||||
uint256[2] Y;
|
||||
}
|
||||
|
||||
/*
|
||||
* @return The negation of p, i.e. p.plus(p.negate()) should be zero.
|
||||
*/
|
||||
function negate(G1Point memory p) internal pure returns (G1Point memory) {
|
||||
|
||||
// The prime q in the base field F_q for G1
|
||||
if (p.X == 0 && p.Y == 0) {
|
||||
return G1Point(0, 0);
|
||||
} else {
|
||||
return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @return The sum of two points of G1
|
||||
*/
|
||||
function plus(
|
||||
G1Point memory p1,
|
||||
G1Point memory p2
|
||||
) internal view returns (G1Point memory r) {
|
||||
|
||||
uint256[4] memory input;
|
||||
input[0] = p1.X;
|
||||
input[1] = p1.Y;
|
||||
input[2] = p2.X;
|
||||
input[3] = p2.Y;
|
||||
bool success;
|
||||
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas, 2000), 6, input, 0xc0, r, 0x60)
|
||||
// Use "invalid" to make gas estimation work
|
||||
switch success case 0 { invalid() }
|
||||
}
|
||||
|
||||
require(success,"pairing-add-failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* @return The product of a point on G1 and a scalar, i.e.
|
||||
* p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all
|
||||
* points p.
|
||||
*/
|
||||
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
||||
|
||||
uint256[3] memory input;
|
||||
input[0] = p.X;
|
||||
input[1] = p.Y;
|
||||
input[2] = s;
|
||||
bool success;
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas, 2000), 7, input, 0x80, r, 0x60)
|
||||
// Use "invalid" to make gas estimation work
|
||||
switch success case 0 { invalid() }
|
||||
}
|
||||
require (success,"pairing-mul-failed");
|
||||
}
|
||||
|
||||
/* @return The result of computing the pairing check
|
||||
* e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
||||
* For example,
|
||||
* pairing([P1(), P1().negate()], [P2(), P2()]) should return true.
|
||||
*/
|
||||
function pairing(
|
||||
G1Point memory a1,
|
||||
G2Point memory a2,
|
||||
G1Point memory b1,
|
||||
G2Point memory b2,
|
||||
G1Point memory c1,
|
||||
G2Point memory c2,
|
||||
G1Point memory d1,
|
||||
G2Point memory d2
|
||||
) internal view returns (bool) {
|
||||
|
||||
G1Point[4] memory p1 = [a1, b1, c1, d1];
|
||||
G2Point[4] memory p2 = [a2, b2, c2, d2];
|
||||
|
||||
uint256 inputSize = 24;
|
||||
uint256[] memory input = new uint256[](inputSize);
|
||||
|
||||
for (uint256 i = 0; i < 4; i++) {
|
||||
uint256 j = i * 6;
|
||||
input[j + 0] = p1[i].X;
|
||||
input[j + 1] = p1[i].Y;
|
||||
input[j + 2] = p2[i].X[0];
|
||||
input[j + 3] = p2[i].X[1];
|
||||
input[j + 4] = p2[i].Y[0];
|
||||
input[j + 5] = p2[i].Y[1];
|
||||
}
|
||||
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
|
||||
// solium-disable-next-line security/no-inline-assembly
|
||||
assembly {
|
||||
success := staticcall(sub(gas, 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
|
||||
// Use "invalid" to make gas estimation work
|
||||
switch success case 0 { invalid() }
|
||||
}
|
||||
|
||||
require(success,"pairing-opcode-failed");
|
||||
|
||||
return out[0] != 0;
|
||||
}
|
||||
}
|
||||
|
||||
contract Verifier {
|
||||
|
||||
using Pairing for *;
|
||||
|
||||
uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point alfa1;
|
||||
Pairing.G2Point beta2;
|
||||
Pairing.G2Point gamma2;
|
||||
Pairing.G2Point delta2;
|
||||
Pairing.G1Point[5] IC;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
|
||||
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
||||
vk.alfa1 = Pairing.G1Point(uint256(19412864166662840704199897530865176132379551878490098268546775855212807511623), uint256(1659653118858063231936623514142744009632862245288902525823221677276210935161));
|
||||
vk.beta2 = Pairing.G2Point([uint256(21642731927166283717597040076624334182856179384944638206172042877912623746964), uint256(10167148937945084930725321596284210462590725859059519349238552658864205847292)], [uint256(1224040963938177736215304045934094846196618140310043882848453181887293487598), uint256(21818148805990469610055209831928752212083869699635755409679256612234566494214)]);
|
||||
vk.gamma2 = Pairing.G2Point([uint256(7084583264208126476050882701870582484117653569260992588151213710616665494315), uint256(21338791141815158629032141160990160038215366570564838558882493662897305673845)], [uint256(20409386432685531109985133572396335050408469502427908880430269312598850603009), uint256(21694931277671378411802527161940275869759588185961914055258162012593400433477)]);
|
||||
vk.delta2 = Pairing.G2Point([uint256(15974226699330331350608610622797702188540365463638301508490182464179306746479), uint256(10800304862034523735970868610105048512813625407055208260881866244104890739413)], [uint256(3115757193545898321493679843214264410358333980282409841160781532582592563749), uint256(20585865237865669840907249885451244426128970908885747090346049467936130099745)]);
|
||||
vk.IC[0] = Pairing.G1Point(uint256(15132217740731663181077706894312753465210500809816534743630331656993829080728), uint256(110196461348215931979632312103651461241391911014889357659299988542624772231));
|
||||
vk.IC[1] = Pairing.G1Point(uint256(10128725078198782996699361178651605009720713215878609701039758932704577595075), uint256(6404467707897071196443816328081887791672216217394045289711692279719912978002));
|
||||
vk.IC[2] = Pairing.G1Point(uint256(10522674726928807143308489533204590506138344033395366996236503798983177262637), uint256(4729387380831061502587298076566203099165040519549972799644442785786542168149));
|
||||
vk.IC[3] = Pairing.G1Point(uint256(17764548214378097040054627843654571919160111811296549132642271073668911279441), uint256(18047600752595374913303954050610046390396466710244400595444056973195374265781));
|
||||
vk.IC[4] = Pairing.G1Point(uint256(7483741608009854810379599415076882430339556147495509532415486196559753628073), uint256(15133497018284592127224880003457371607602553122563204208188040273053737050972));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* @returns Whether the proof is valid given the hardcoded verifying key
|
||||
* above and the public inputs
|
||||
*/
|
||||
function verifyProof(
|
||||
uint256[2] memory a,
|
||||
uint256[2][2] memory b,
|
||||
uint256[2] memory c,
|
||||
uint256[4] memory input
|
||||
) public view returns (bool r) {
|
||||
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
|
||||
// Compute the linear combination vk_x
|
||||
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
|
||||
|
||||
// Make sure that proof.A, B, and C are each less than the prime q
|
||||
require(proof.A.X < PRIME_Q, "verifier-aX-gte-prime-q");
|
||||
require(proof.A.Y < PRIME_Q, "verifier-aY-gte-prime-q");
|
||||
|
||||
require(proof.B.X[0] < PRIME_Q, "verifier-bX0-gte-prime-q");
|
||||
require(proof.B.Y[0] < PRIME_Q, "verifier-bY0-gte-prime-q");
|
||||
|
||||
require(proof.B.X[1] < PRIME_Q, "verifier-bX1-gte-prime-q");
|
||||
require(proof.B.Y[1] < PRIME_Q, "verifier-bY1-gte-prime-q");
|
||||
|
||||
require(proof.C.X < PRIME_Q, "verifier-cX-gte-prime-q");
|
||||
require(proof.C.Y < PRIME_Q, "verifier-cY-gte-prime-q");
|
||||
|
||||
// Make sure that every input is less than the snark scalar field
|
||||
for (uint256 i = 0; i < input.length; i++) {
|
||||
require(input[i] < SNARK_SCALAR_FIELD,"verifier-gte-snark-scalar-field");
|
||||
vk_x = Pairing.plus(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
|
||||
}
|
||||
|
||||
vk_x = Pairing.plus(vk_x, vk.IC[0]);
|
||||
|
||||
return Pairing.pairing(
|
||||
Pairing.negate(proof.A),
|
||||
proof.B,
|
||||
vk.alfa1,
|
||||
vk.beta2,
|
||||
vk_x,
|
||||
vk.gamma2,
|
||||
proof.C,
|
||||
vk.delta2
|
||||
);
|
||||
}
|
||||
}
|
||||
220
contracts/ts/__tests__/FixedVerifier.test.ts
Normal file
220
contracts/ts/__tests__/FixedVerifier.test.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
require('module-alias/register')
|
||||
jest.setTimeout(90000)
|
||||
|
||||
const MiMC = require('@semaphore-contracts/compiled/MiMC.json')
|
||||
const Semaphore = require('@semaphore-contracts/compiled/Semaphore.json')
|
||||
const SemaphoreClient = require('@semaphore-contracts/compiled/SemaphoreClient.json')
|
||||
const hasEvent = require('etherlime/cli-commands/etherlime-test/events.js').hasEvent
|
||||
|
||||
import {
|
||||
SnarkBigInt,
|
||||
genIdentity,
|
||||
genIdentityCommitment,
|
||||
genExternalNullifier,
|
||||
genWitness,
|
||||
genCircuit,
|
||||
genProof,
|
||||
genPublicSignals,
|
||||
verifyProof,
|
||||
SnarkProvingKey,
|
||||
SnarkVerifyingKey,
|
||||
parseVerifyingKeyJson,
|
||||
formatForVerifierContract,
|
||||
} from 'libsemaphore'
|
||||
import * as etherlime from 'etherlime-lib'
|
||||
import { config } from 'semaphore-config'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
import * as ethers from 'ethers'
|
||||
|
||||
const NUM_LEVELS = 20
|
||||
const FIRST_EXTERNAL_NULLIFIER = 0
|
||||
const SIGNAL = 'signal0'
|
||||
|
||||
const genTestAccounts = (num: number, mnemonic: string) => {
|
||||
let accounts: ethers.Wallet[] = []
|
||||
|
||||
for (let i=0; i<num; i++) {
|
||||
const p = `m/44'/60'/${i}'/0/0`
|
||||
const wallet = ethers.Wallet.fromMnemonic(mnemonic, p)
|
||||
accounts.push(wallet)
|
||||
}
|
||||
|
||||
return accounts
|
||||
}
|
||||
|
||||
const accounts = genTestAccounts(2, config.chain.mnemonic)
|
||||
let semaphoreContract
|
||||
let semaphoreClientContract
|
||||
let mimcContract
|
||||
|
||||
// hex representations of all inserted identity commitments
|
||||
let insertedIdentityCommitments: string[] = []
|
||||
const activeEn = genExternalNullifier(Date.now().toString())
|
||||
const inactiveEn = genExternalNullifier(Date.now().toString())
|
||||
const invalidEn = BigInt(Math.pow(2, 232)).toString()
|
||||
|
||||
let deployer
|
||||
|
||||
describe('Semaphore', () => {
|
||||
beforeAll(async () => {
|
||||
deployer = new etherlime.JSONRPCPrivateKeyDeployer(
|
||||
accounts[0].privateKey,
|
||||
config.get('chain.url'),
|
||||
{
|
||||
gasLimit: 8800000,
|
||||
chainId: config.get('chain.chainId'),
|
||||
},
|
||||
)
|
||||
|
||||
console.log('Deploying MiMC')
|
||||
mimcContract = await deployer.deploy(MiMC, {})
|
||||
|
||||
const libraries = {
|
||||
MiMC: mimcContract.contractAddress,
|
||||
}
|
||||
|
||||
console.log('Deploying Semaphore')
|
||||
semaphoreContract = await deployer.deploy(
|
||||
Semaphore,
|
||||
libraries,
|
||||
NUM_LEVELS,
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
|
||||
console.log('Deploying Semaphore Client')
|
||||
semaphoreClientContract = await deployer.deploy(
|
||||
SemaphoreClient,
|
||||
{},
|
||||
semaphoreContract.contractAddress,
|
||||
)
|
||||
|
||||
console.log('Transferring ownership of the Semaphore contract to the Semaphore Client')
|
||||
const tx = await semaphoreContract.transferOwnership(
|
||||
semaphoreClientContract.contractAddress,
|
||||
)
|
||||
|
||||
await tx.wait()
|
||||
})
|
||||
|
||||
test('insert an identity commitment', async () => {
|
||||
const identity = genIdentity()
|
||||
const identityCommitment: SnarkBigInt = genIdentityCommitment(identity)
|
||||
|
||||
const tx = await semaphoreClientContract.insertIdentityAsClient(
|
||||
identityCommitment.toString()
|
||||
)
|
||||
const receipt = await tx.wait()
|
||||
expect(receipt.status).toEqual(1)
|
||||
|
||||
console.log('Gas used by insertIdentityAsClient():', receipt.gasUsed.toString())
|
||||
|
||||
insertedIdentityCommitments.push('0x' + identityCommitment.toString(16))
|
||||
expect(hasEvent(receipt, semaphoreContract, 'LeafInsertion')).toBeTruthy()
|
||||
})
|
||||
|
||||
describe('signal broadcasts', () => {
|
||||
// Load circuit, proving key, and verifying key
|
||||
const circuitPath = path.join(__dirname, '../../../circuits/build/circuit.json')
|
||||
const provingKeyPath = path.join(__dirname, '../../../circuits/build/proving_key.bin')
|
||||
const verifyingKeyPath = path.join(__dirname, '../../../circuits/build/verification_key.json')
|
||||
|
||||
const cirDef = JSON.parse(fs.readFileSync(circuitPath).toString())
|
||||
const provingKey: SnarkProvingKey = fs.readFileSync(provingKeyPath)
|
||||
const verifyingKey: SnarkVerifyingKey = parseVerifyingKeyJson(fs.readFileSync(verifyingKeyPath).toString())
|
||||
const circuit = genCircuit(cirDef)
|
||||
let identity
|
||||
let identityCommitment
|
||||
let proof
|
||||
let publicSignals
|
||||
let params
|
||||
|
||||
beforeAll(async () => {
|
||||
identity = genIdentity()
|
||||
identityCommitment = genIdentityCommitment(identity)
|
||||
|
||||
await (await semaphoreClientContract.insertIdentityAsClient(identityCommitment.toString())).wait()
|
||||
|
||||
const leaves = await semaphoreClientContract.getIdentityCommitments()
|
||||
|
||||
const result = await genWitness(
|
||||
SIGNAL,
|
||||
circuit,
|
||||
identity,
|
||||
leaves,
|
||||
NUM_LEVELS,
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
|
||||
proof = await genProof(result.witness, provingKey)
|
||||
publicSignals = genPublicSignals(result.witness, circuit)
|
||||
params = formatForVerifierContract(proof, publicSignals)
|
||||
})
|
||||
|
||||
test('the proof should be valid', async () => {
|
||||
expect.assertions(1)
|
||||
const isValid = verifyProof(verifyingKey, proof, publicSignals)
|
||||
expect(isValid).toBeTruthy()
|
||||
})
|
||||
|
||||
test('the pre-broadcast check should pass', async () => {
|
||||
expect.assertions(1)
|
||||
|
||||
const check = await semaphoreContract.preBroadcastCheck(
|
||||
ethers.utils.toUtf8Bytes(SIGNAL),
|
||||
params.a,
|
||||
params.b,
|
||||
params.c,
|
||||
params.input[0],
|
||||
params.input[1],
|
||||
params.input[2],
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
expect(check).toBeTruthy()
|
||||
})
|
||||
|
||||
test('broadcastSignal with an input element above the scalar field should fail', async () => {
|
||||
expect.assertions(1)
|
||||
const size = BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617')
|
||||
const oversizedInput = (BigInt(params.input[1]) + size).toString()
|
||||
try {
|
||||
await semaphoreClientContract.broadcastSignal(
|
||||
ethers.utils.toUtf8Bytes(SIGNAL),
|
||||
params.a,
|
||||
params.b,
|
||||
params.c,
|
||||
params.input[0],
|
||||
oversizedInput,
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('verifier-gte-snark-scalar-field')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('broadcastSignal to active external nullifier with an account with the right permissions should work', async () => {
|
||||
expect.assertions(4)
|
||||
const tx = await semaphoreClientContract.broadcastSignal(
|
||||
ethers.utils.toUtf8Bytes(SIGNAL),
|
||||
params.a,
|
||||
params.b,
|
||||
params.c,
|
||||
params.input[0],
|
||||
params.input[1],
|
||||
params.input[3],
|
||||
{ gasLimit: 1000000 },
|
||||
)
|
||||
const receipt = await tx.wait()
|
||||
expect(receipt.status).toEqual(1)
|
||||
console.log('Gas used by broadcastSignal():', receipt.gasUsed.toString())
|
||||
|
||||
const index = (await semaphoreClientContract.nextSignalIndex()) - 1
|
||||
const signal = await semaphoreClientContract.signalIndexToSignal(index.toString())
|
||||
|
||||
expect(ethers.utils.toUtf8String(signal)).toEqual(SIGNAL)
|
||||
|
||||
expect(hasEvent(receipt, semaphoreContract, 'SignalBroadcast')).toBeTruthy()
|
||||
expect(hasEvent(receipt, semaphoreClientContract, 'SignalBroadcastByClient')).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
130
contracts/ts/__tests__/IncrementalMerkleTree.test.ts
Normal file
130
contracts/ts/__tests__/IncrementalMerkleTree.test.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
require('module-alias/register')
|
||||
jest.setTimeout(90000)
|
||||
|
||||
const MiMC = require('@semaphore-contracts/compiled/MiMC.json')
|
||||
const IncrementalMerkleTreeClient = require('@semaphore-contracts/compiled/IncrementalMerkleTreeClient.json')
|
||||
import * as etherlime from 'etherlime-lib'
|
||||
import { config } from 'semaphore-config'
|
||||
import * as ethers from 'ethers'
|
||||
import { storage, hashers, tree } from 'semaphore-merkle-tree'
|
||||
const mimcSpongeHasher = new hashers.MimcSpongeHasher()
|
||||
|
||||
const account = ethers.Wallet.fromMnemonic(config.chain.mnemonic, `m/44'/60'/0'/0/0`)
|
||||
let deployer
|
||||
let mtContract
|
||||
let mimcContract
|
||||
import {
|
||||
genIdentity,
|
||||
genIdentityCommitment,
|
||||
setupTree,
|
||||
} from 'libsemaphore'
|
||||
|
||||
const LEVELS = 20
|
||||
let tree
|
||||
|
||||
const ZERO_VALUE =
|
||||
ethers.utils.solidityKeccak256(
|
||||
['bytes'],
|
||||
[ethers.utils.toUtf8Bytes('Semaphore')]
|
||||
)
|
||||
|
||||
describe('IncrementalMerkleTree functions should match the semaphore-merkle-tree implementation', () => {
|
||||
let libraries
|
||||
|
||||
beforeAll(async () => {
|
||||
tree = setupTree(LEVELS)
|
||||
|
||||
deployer = new etherlime.JSONRPCPrivateKeyDeployer(
|
||||
account.privateKey,
|
||||
config.get('chain.url'),
|
||||
{
|
||||
gasLimit: 8800000,
|
||||
chainId: config.get('chain.chainId'),
|
||||
},
|
||||
)
|
||||
|
||||
console.log('Deploying MiMC')
|
||||
mimcContract = await deployer.deploy(MiMC, {})
|
||||
|
||||
libraries = {
|
||||
MiMC: mimcContract.contractAddress,
|
||||
}
|
||||
})
|
||||
|
||||
test('deployment', async () => {
|
||||
console.log('Deploying IncrementalMerkleTreeClient')
|
||||
|
||||
mtContract = await deployer.deploy(
|
||||
IncrementalMerkleTreeClient,
|
||||
libraries,
|
||||
LEVELS,
|
||||
ZERO_VALUE,
|
||||
)
|
||||
|
||||
const root = await mtContract.root()
|
||||
const root2 = await tree.root()
|
||||
expect(root.toString()).toEqual(root2)
|
||||
})
|
||||
|
||||
test('deployment should fail if the specified number of levels is 0', async () => {
|
||||
try {
|
||||
await deployer.deploy(
|
||||
IncrementalMerkleTreeClient,
|
||||
libraries,
|
||||
0,
|
||||
ZERO_VALUE,
|
||||
)
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('IncrementalMerkleTree: _treeLevels must be between 0 and 33')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('initMerkleTree should fail if the specified number of levels exceeds 32', async () => {
|
||||
try {
|
||||
await deployer.deploy(
|
||||
IncrementalMerkleTreeClient,
|
||||
libraries,
|
||||
33,
|
||||
ZERO_VALUE,
|
||||
)
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('IncrementalMerkleTree: _treeLevels must be between 0 and 33')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('insertLeaf should fail if the leaf > the snark scalar field', async () => {
|
||||
const leaf = '21888242871839275222246405745257275088548364400416034343698204186575808495618'
|
||||
try {
|
||||
await mtContract.insertLeafAsClient(leaf)
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('IncrementalMerkleTree: insertLeaf argument must be < SNARK_SCALAR_FIELD'))
|
||||
}
|
||||
})
|
||||
|
||||
test('insertLeaf (via insertLeafAsClient)', async () => {
|
||||
const leaf = genIdentityCommitment(genIdentity()).toString()
|
||||
const tx = await mtContract.insertLeafAsClient(leaf)
|
||||
const receipt = await tx.wait()
|
||||
|
||||
console.log('Gas used by insertLeaf:', receipt.gasUsed.toString())
|
||||
await tree.update(0, leaf)
|
||||
|
||||
const root = await mtContract.root()
|
||||
const root2 = await tree.root()
|
||||
expect(root.toString()).toEqual(root2)
|
||||
})
|
||||
|
||||
test('inserting a few leaves should work', async () => {
|
||||
for (let i = 1; i < 9; i++) {
|
||||
const leaf = genIdentityCommitment(genIdentity()).toString()
|
||||
const tx = await mtContract.insertLeafAsClient(leaf)
|
||||
const receipt = await tx.wait()
|
||||
|
||||
await tree.update(i, leaf)
|
||||
|
||||
const root = await mtContract.root()
|
||||
const root2 = await tree.root()
|
||||
expect(root.toString()).toEqual(root2)
|
||||
}
|
||||
})
|
||||
})
|
||||
581
contracts/ts/__tests__/Semaphore.test.ts
Normal file
581
contracts/ts/__tests__/Semaphore.test.ts
Normal file
@@ -0,0 +1,581 @@
|
||||
require('module-alias/register')
|
||||
jest.setTimeout(90000)
|
||||
|
||||
const MiMC = require('@semaphore-contracts/compiled/MiMC.json')
|
||||
const Semaphore = require('@semaphore-contracts/compiled/Semaphore.json')
|
||||
const SemaphoreClient = require('@semaphore-contracts/compiled/SemaphoreClient.json')
|
||||
const hasEvent = require('etherlime/cli-commands/etherlime-test/events.js').hasEvent
|
||||
|
||||
import {
|
||||
SnarkBigInt,
|
||||
genIdentity,
|
||||
genIdentityCommitment,
|
||||
genExternalNullifier,
|
||||
genWitness,
|
||||
genCircuit,
|
||||
genProof,
|
||||
genPublicSignals,
|
||||
verifyProof,
|
||||
SnarkProvingKey,
|
||||
SnarkVerifyingKey,
|
||||
parseVerifyingKeyJson,
|
||||
genBroadcastSignalParams,
|
||||
genSignalHash,
|
||||
} from 'libsemaphore'
|
||||
import * as etherlime from 'etherlime-lib'
|
||||
import { config } from 'semaphore-config'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
import * as ethers from 'ethers'
|
||||
|
||||
const NUM_LEVELS = 20
|
||||
const FIRST_EXTERNAL_NULLIFIER = 0
|
||||
const SIGNAL = 'signal0'
|
||||
|
||||
const genTestAccounts = (num: number, mnemonic: string) => {
|
||||
let accounts: ethers.Wallet[] = []
|
||||
|
||||
for (let i=0; i<num; i++) {
|
||||
const p = `m/44'/60'/${i}'/0/0`
|
||||
const wallet = ethers.Wallet.fromMnemonic(mnemonic, p)
|
||||
accounts.push(wallet)
|
||||
}
|
||||
|
||||
return accounts
|
||||
}
|
||||
|
||||
const accounts = genTestAccounts(2, config.chain.mnemonic)
|
||||
let semaphoreContract
|
||||
let semaphoreClientContract
|
||||
let mimcContract
|
||||
|
||||
// hex representations of all inserted identity commitments
|
||||
let insertedIdentityCommitments: string[] = []
|
||||
const activeEn = genExternalNullifier('1111')
|
||||
const inactiveEn = genExternalNullifier('2222')
|
||||
|
||||
let deployer
|
||||
|
||||
describe('Semaphore', () => {
|
||||
beforeAll(async () => {
|
||||
deployer = new etherlime.JSONRPCPrivateKeyDeployer(
|
||||
accounts[0].privateKey,
|
||||
config.get('chain.url'),
|
||||
{
|
||||
gasLimit: 8800000,
|
||||
chainId: config.get('chain.chainId'),
|
||||
},
|
||||
)
|
||||
|
||||
console.log('Deploying MiMC')
|
||||
mimcContract = await deployer.deploy(MiMC, {})
|
||||
|
||||
const libraries = {
|
||||
MiMC: mimcContract.contractAddress,
|
||||
}
|
||||
|
||||
console.log('Deploying Semaphore')
|
||||
semaphoreContract = await deployer.deploy(
|
||||
Semaphore,
|
||||
libraries,
|
||||
NUM_LEVELS,
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
|
||||
console.log('Deploying Semaphore Client')
|
||||
semaphoreClientContract = await deployer.deploy(
|
||||
SemaphoreClient,
|
||||
{},
|
||||
semaphoreContract.contractAddress,
|
||||
)
|
||||
|
||||
console.log('Transferring ownership of the Semaphore contract to the Semaphore Client')
|
||||
const tx = await semaphoreContract.transferOwnership(
|
||||
semaphoreClientContract.contractAddress,
|
||||
)
|
||||
|
||||
await tx.wait()
|
||||
})
|
||||
|
||||
test('Semaphore belongs to the correct owner', async () => {
|
||||
const owner = await semaphoreContract.owner()
|
||||
expect(owner).toEqual(semaphoreClientContract.contractAddress)
|
||||
})
|
||||
|
||||
test('insert an identity commitment', async () => {
|
||||
const identity = genIdentity()
|
||||
const identityCommitment: SnarkBigInt = genIdentityCommitment(identity)
|
||||
|
||||
const tx = await semaphoreClientContract.insertIdentityAsClient(
|
||||
identityCommitment.toString()
|
||||
)
|
||||
const receipt = await tx.wait()
|
||||
expect(receipt.status).toEqual(1)
|
||||
|
||||
const numInserted = await semaphoreContract.getNumIdentityCommitments()
|
||||
expect(numInserted.toString()).toEqual('1')
|
||||
|
||||
console.log('Gas used by insertIdentityAsClient():', receipt.gasUsed.toString())
|
||||
|
||||
insertedIdentityCommitments.push('0x' + identityCommitment.toString(16))
|
||||
expect(hasEvent(receipt, semaphoreContract, 'LeafInsertion')).toBeTruthy()
|
||||
})
|
||||
|
||||
describe('identity insertions', () => {
|
||||
test('should be stored in the contract and retrievable via leaves()', async () => {
|
||||
expect.assertions(insertedIdentityCommitments.length + 1)
|
||||
|
||||
const leaves = await semaphoreClientContract.getIdentityCommitments()
|
||||
expect(leaves.length).toEqual(insertedIdentityCommitments.length)
|
||||
|
||||
const leavesHex = leaves.map(BigInt)
|
||||
|
||||
for (let i = 0; i < insertedIdentityCommitments.length; i++) {
|
||||
const containsLeaf = leavesHex.indexOf(BigInt(insertedIdentityCommitments[i])) > -1
|
||||
expect(containsLeaf).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('should be stored in the contract and retrievable by enumerating leaf()', async () => {
|
||||
expect.assertions(insertedIdentityCommitments.length)
|
||||
|
||||
// Assumes that insertedIdentityCommitments has the same number of
|
||||
// elements as the number of leaves
|
||||
const idCommsBigint = insertedIdentityCommitments.map(BigInt)
|
||||
for (let i = 0; i < insertedIdentityCommitments.length; i++) {
|
||||
const leaf = await semaphoreClientContract.getIdentityCommitment(i)
|
||||
const leafHex = BigInt(leaf.toHexString())
|
||||
expect(idCommsBigint.indexOf(leafHex) > -1).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('inserting an identity commitment of the nothing-up-my-sleeve value should fail', async () => {
|
||||
expect.assertions(1)
|
||||
const nothingUpMySleeve =
|
||||
BigInt(ethers.utils.solidityKeccak256(['bytes'], [ethers.utils.toUtf8Bytes('Semaphore')]))
|
||||
%
|
||||
BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617')
|
||||
|
||||
try {
|
||||
await semaphoreClientContract.insertIdentityAsClient(nothingUpMySleeve.toString())
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('Semaphore: identity commitment cannot be the nothing-up-my-sleeve-value')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('external nullifiers', () => {
|
||||
|
||||
test('when there is only 1 external nullifier, the first and last external nullifier variables should be the same', async () => {
|
||||
expect((await semaphoreContract.numExternalNullifiers()).toNumber()).toEqual(1)
|
||||
const firstEn = await semaphoreContract.firstExternalNullifier()
|
||||
const lastEn = await semaphoreContract.lastExternalNullifier()
|
||||
expect(firstEn.toString()).toEqual(lastEn.toString())
|
||||
})
|
||||
|
||||
test('getNextExternalNullifier should throw if there is only 1 external nullifier', async () => {
|
||||
expect((await semaphoreContract.numExternalNullifiers()).toNumber()).toEqual(1)
|
||||
const firstEn = await semaphoreContract.firstExternalNullifier()
|
||||
try {
|
||||
await semaphoreContract.getNextExternalNullifier(firstEn)
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('Semaphore: no external nullifier exists after the specified one')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('should be able to add an external nullifier', async () => {
|
||||
expect.assertions(4)
|
||||
const tx = await semaphoreClientContract.addExternalNullifier(
|
||||
activeEn,
|
||||
{ gasLimit: 200000 },
|
||||
)
|
||||
const receipt = await tx.wait()
|
||||
|
||||
expect(receipt.status).toEqual(1)
|
||||
expect(hasEvent(receipt, semaphoreContract, 'ExternalNullifierAdd')).toBeTruthy()
|
||||
|
||||
// Check if isExternalNullifierActive works
|
||||
const isActive = await semaphoreContract.isExternalNullifierActive(activeEn)
|
||||
expect(isActive).toBeTruthy()
|
||||
|
||||
// Check if numExternalNullifiers() returns the correct value
|
||||
expect((await semaphoreContract.numExternalNullifiers()).toNumber()).toEqual(2)
|
||||
})
|
||||
|
||||
test('getNextExternalNullifier should throw if there is no such external nullifier', async () => {
|
||||
try {
|
||||
await semaphoreContract.getNextExternalNullifier('876876876876')
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('Semaphore: no such external nullifier')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('should be able to deactivate an external nullifier', async () => {
|
||||
await (await semaphoreClientContract.addExternalNullifier(
|
||||
inactiveEn,
|
||||
{ gasLimit: 200000 },
|
||||
)).wait()
|
||||
const tx = await semaphoreClientContract.deactivateExternalNullifier(
|
||||
inactiveEn,
|
||||
{ gasLimit: 100000 },
|
||||
)
|
||||
const receipt = await tx.wait()
|
||||
expect(receipt.status).toEqual(1)
|
||||
|
||||
expect(await semaphoreContract.isExternalNullifierActive(inactiveEn)).toBeFalsy()
|
||||
})
|
||||
|
||||
test('reactivating a deactivated external nullifier and then deactivating it should work', async () => {
|
||||
expect.assertions(3)
|
||||
|
||||
// inactiveEn should be inactive
|
||||
expect(await semaphoreContract.isExternalNullifierActive(inactiveEn)).toBeFalsy()
|
||||
|
||||
// reactivate inactiveEn
|
||||
let tx = await semaphoreClientContract.reactivateExternalNullifier(
|
||||
inactiveEn,
|
||||
{ gasLimit: 100000 },
|
||||
)
|
||||
await tx.wait()
|
||||
|
||||
expect(await semaphoreContract.isExternalNullifierActive(inactiveEn)).toBeTruthy()
|
||||
|
||||
tx = await semaphoreClientContract.deactivateExternalNullifier(
|
||||
inactiveEn,
|
||||
{ gasLimit: 100000 },
|
||||
)
|
||||
await tx.wait()
|
||||
|
||||
expect(await semaphoreContract.isExternalNullifierActive(inactiveEn)).toBeFalsy()
|
||||
})
|
||||
|
||||
test('enumerating external nullifiers should work', async () => {
|
||||
const firstEn = await semaphoreContract.firstExternalNullifier()
|
||||
const lastEn = await semaphoreContract.lastExternalNullifier()
|
||||
|
||||
const externalNullifiers: BigInt[] = [ firstEn ]
|
||||
let currentEn = firstEn
|
||||
|
||||
while (currentEn.toString() !== lastEn.toString()) {
|
||||
currentEn = await semaphoreContract.getNextExternalNullifier(currentEn)
|
||||
externalNullifiers.push(currentEn)
|
||||
}
|
||||
|
||||
expect(externalNullifiers).toHaveLength(3)
|
||||
expect(BigInt(externalNullifiers[0].toString())).toEqual(BigInt(firstEn.toString()))
|
||||
expect(BigInt(externalNullifiers[1].toString())).toEqual(BigInt(activeEn.toString()))
|
||||
expect(BigInt(externalNullifiers[2].toString())).toEqual(BigInt(inactiveEn.toString()))
|
||||
})
|
||||
})
|
||||
|
||||
describe('signal broadcasts', () => {
|
||||
// Load circuit, proving key, and verifying key
|
||||
const circuitPath = path.join(__dirname, '../../../circuits/build/circuit.json')
|
||||
const provingKeyPath = path.join(__dirname, '../../../circuits/build/proving_key.bin')
|
||||
const verifyingKeyPath = path.join(__dirname, '../../../circuits/build/verification_key.json')
|
||||
|
||||
const cirDef = JSON.parse(fs.readFileSync(circuitPath).toString())
|
||||
const provingKey: SnarkProvingKey = fs.readFileSync(provingKeyPath)
|
||||
const verifyingKey: SnarkVerifyingKey = parseVerifyingKeyJson(fs.readFileSync(verifyingKeyPath).toString())
|
||||
const circuit = genCircuit(cirDef)
|
||||
let identity
|
||||
let identityCommitment
|
||||
let proof
|
||||
let publicSignals
|
||||
let params
|
||||
|
||||
beforeAll(async () => {
|
||||
identity = genIdentity()
|
||||
identityCommitment = genIdentityCommitment(identity)
|
||||
|
||||
await (await semaphoreClientContract.insertIdentityAsClient(identityCommitment.toString())).wait()
|
||||
|
||||
const leaves = await semaphoreClientContract.getIdentityCommitments()
|
||||
|
||||
const result = await genWitness(
|
||||
SIGNAL,
|
||||
circuit,
|
||||
identity,
|
||||
leaves,
|
||||
NUM_LEVELS,
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
|
||||
proof = await genProof(result.witness, provingKey)
|
||||
publicSignals = genPublicSignals(result.witness, circuit)
|
||||
params = genBroadcastSignalParams(result, proof, publicSignals)
|
||||
})
|
||||
|
||||
test('the proof should be valid', async () => {
|
||||
expect.assertions(1)
|
||||
const isValid = verifyProof(verifyingKey, proof, publicSignals)
|
||||
expect(isValid).toBeTruthy()
|
||||
})
|
||||
|
||||
test('the pre-broadcast check should pass', async () => {
|
||||
expect.assertions(1)
|
||||
|
||||
const signal = ethers.utils.toUtf8Bytes(SIGNAL)
|
||||
const check = await semaphoreContract.preBroadcastCheck(
|
||||
signal,
|
||||
params.proof,
|
||||
params.root,
|
||||
params.nullifiersHash,
|
||||
genSignalHash(signal).toString(),
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
expect(check).toBeTruthy()
|
||||
})
|
||||
|
||||
test('the pre-broadcast check with an invalid signal should fail', async () => {
|
||||
expect.assertions(1)
|
||||
|
||||
const signal = ethers.utils.toUtf8Bytes(SIGNAL)
|
||||
const check = await semaphoreContract.preBroadcastCheck(
|
||||
'0x0',
|
||||
params.proof,
|
||||
params.root,
|
||||
params.nullifiersHash,
|
||||
genSignalHash(signal).toString(),
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
expect(check).toBeFalsy()
|
||||
})
|
||||
|
||||
test('broadcastSignal with an input element above the scalar field should fail', async () => {
|
||||
expect.assertions(1)
|
||||
const size = BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617')
|
||||
const oversizedInput = (BigInt(params.nullifiersHash) + size).toString()
|
||||
try {
|
||||
await semaphoreClientContract.broadcastSignal(
|
||||
ethers.utils.toUtf8Bytes(SIGNAL),
|
||||
params.proof,
|
||||
params.root,
|
||||
oversizedInput,
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('Semaphore: the nullifiers hash must be lt the snark scalar field')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('broadcastSignal with an invalid proof_data should fail', async () => {
|
||||
expect.assertions(1)
|
||||
try {
|
||||
await semaphoreClientContract.broadcastSignal(
|
||||
ethers.utils.toUtf8Bytes(SIGNAL),
|
||||
[
|
||||
"21888242871839275222246405745257275088548364400416034343698204186575808495617",
|
||||
"7",
|
||||
"7",
|
||||
"7",
|
||||
"7",
|
||||
"7",
|
||||
"7",
|
||||
"7",
|
||||
],
|
||||
params.root,
|
||||
params.nullifiersHash,
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('Semaphore: invalid field element(s) in proof')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('broadcastSignal with an unseen root should fail', async () => {
|
||||
expect.assertions(1)
|
||||
try {
|
||||
await semaphoreClientContract.broadcastSignal(
|
||||
ethers.utils.toUtf8Bytes(SIGNAL),
|
||||
params.proof,
|
||||
params.nullifiersHash, // note that this is delibrately swapped
|
||||
params.root,
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('Semaphore: root not seen')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('broadcastSignal by an unpermissioned user should fail', async () => {
|
||||
expect.assertions(1)
|
||||
try {
|
||||
await semaphoreContract.broadcastSignal(
|
||||
ethers.utils.toUtf8Bytes(SIGNAL),
|
||||
params.proof,
|
||||
params.root,
|
||||
params.nullifiersHash,
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('Semaphore: broadcast permission denied')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('broadcastSignal to active external nullifier with an account with the right permissions should work', async () => {
|
||||
expect.assertions(3)
|
||||
const tx = await semaphoreClientContract.broadcastSignal(
|
||||
ethers.utils.toUtf8Bytes(SIGNAL),
|
||||
params.proof,
|
||||
params.root,
|
||||
params.nullifiersHash,
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
//params.externalNullifier,
|
||||
{ gasLimit: 1000000 },
|
||||
)
|
||||
const receipt = await tx.wait()
|
||||
expect(receipt.status).toEqual(1)
|
||||
console.log('Gas used by broadcastSignal():', receipt.gasUsed.toString())
|
||||
|
||||
const index = (await semaphoreClientContract.nextSignalIndex()) - 1
|
||||
const signal = await semaphoreClientContract.signalIndexToSignal(index.toString())
|
||||
|
||||
expect(ethers.utils.toUtf8String(signal)).toEqual(SIGNAL)
|
||||
|
||||
expect(hasEvent(receipt, semaphoreClientContract, 'SignalBroadcastByClient')).toBeTruthy()
|
||||
})
|
||||
|
||||
test('double-signalling to the same external nullifier should fail', async () => {
|
||||
expect.assertions(1)
|
||||
const leaves = await semaphoreClientContract.getIdentityCommitments()
|
||||
const newSignal = 'newSignal0'
|
||||
|
||||
const result = await genWitness(
|
||||
newSignal,
|
||||
circuit,
|
||||
identity,
|
||||
leaves,
|
||||
NUM_LEVELS,
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
|
||||
proof = await genProof(result.witness, provingKey)
|
||||
publicSignals = genPublicSignals(result.witness, circuit)
|
||||
params = genBroadcastSignalParams(result, proof, publicSignals)
|
||||
try {
|
||||
const tx = await semaphoreClientContract.broadcastSignal(
|
||||
ethers.utils.toUtf8Bytes(newSignal),
|
||||
params.proof,
|
||||
params.root,
|
||||
params.nullifiersHash,
|
||||
FIRST_EXTERNAL_NULLIFIER,
|
||||
)
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('Semaphore: nullifier already seen')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('signalling to a different external nullifier should work', async () => {
|
||||
expect.assertions(1)
|
||||
const leaves = await semaphoreClientContract.getIdentityCommitments()
|
||||
const newSignal = 'newSignal1'
|
||||
|
||||
const result = await genWitness(
|
||||
newSignal,
|
||||
circuit,
|
||||
identity,
|
||||
leaves,
|
||||
NUM_LEVELS,
|
||||
activeEn,
|
||||
)
|
||||
|
||||
proof = await genProof(result.witness, provingKey)
|
||||
publicSignals = genPublicSignals(result.witness, circuit)
|
||||
params = genBroadcastSignalParams(result, proof, publicSignals)
|
||||
const tx = await semaphoreClientContract.broadcastSignal(
|
||||
ethers.utils.toUtf8Bytes(newSignal),
|
||||
params.proof,
|
||||
params.root,
|
||||
params.nullifiersHash,
|
||||
activeEn,
|
||||
{ gasLimit: 1000000 },
|
||||
)
|
||||
const receipt = await tx.wait()
|
||||
expect(receipt.status).toEqual(1)
|
||||
})
|
||||
|
||||
test('broadcastSignal to a deactivated external nullifier should fail', async () => {
|
||||
expect.assertions(2)
|
||||
expect(await semaphoreContract.isExternalNullifierActive(inactiveEn)).toBeFalsy()
|
||||
|
||||
identity = genIdentity()
|
||||
identityCommitment = genIdentityCommitment(identity)
|
||||
|
||||
await (await semaphoreClientContract.insertIdentityAsClient(identityCommitment.toString())).wait()
|
||||
|
||||
const leaves = await semaphoreClientContract.getIdentityCommitments()
|
||||
|
||||
const result = await genWitness(
|
||||
SIGNAL,
|
||||
circuit,
|
||||
identity,
|
||||
leaves,
|
||||
NUM_LEVELS,
|
||||
inactiveEn,
|
||||
)
|
||||
|
||||
proof = await genProof(result.witness, provingKey)
|
||||
publicSignals = genPublicSignals(result.witness, circuit)
|
||||
params = genBroadcastSignalParams(result, proof, publicSignals)
|
||||
|
||||
try {
|
||||
const tx = await semaphoreClientContract.broadcastSignal(
|
||||
ethers.utils.toUtf8Bytes(SIGNAL),
|
||||
params.proof,
|
||||
params.root,
|
||||
params.nullifiersHash,
|
||||
inactiveEn,
|
||||
)
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('Semaphore: external nullifier not found')).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
test('setPermissioning(false) should allow anyone to broadcast a signal', async () => {
|
||||
expect.assertions(2)
|
||||
const leaves = await semaphoreClientContract.getIdentityCommitments()
|
||||
const newSignal = 'newSignal2'
|
||||
|
||||
const result = await genWitness(
|
||||
newSignal,
|
||||
circuit,
|
||||
identity,
|
||||
leaves,
|
||||
NUM_LEVELS,
|
||||
activeEn,
|
||||
)
|
||||
|
||||
proof = await genProof(result.witness, provingKey)
|
||||
publicSignals = genPublicSignals(result.witness, circuit)
|
||||
params = genBroadcastSignalParams(result, proof, publicSignals)
|
||||
try {
|
||||
await semaphoreContract.broadcastSignal(
|
||||
ethers.utils.toUtf8Bytes(newSignal),
|
||||
params.proof,
|
||||
params.root,
|
||||
params.nullifiersHash,
|
||||
activeEn,
|
||||
{ gasLimit: 1000000 },
|
||||
)
|
||||
} catch (e) {
|
||||
expect(e.message.endsWith('Semaphore: broadcast permission denied')).toBeTruthy()
|
||||
}
|
||||
|
||||
await (await semaphoreClientContract.setPermissioning(false, { gasLimit: 100000 })).wait()
|
||||
|
||||
const tx = await semaphoreClientContract.broadcastSignal(
|
||||
ethers.utils.toUtf8Bytes(newSignal),
|
||||
params.proof,
|
||||
params.root,
|
||||
params.nullifiersHash,
|
||||
activeEn,
|
||||
{ gasLimit: 1000000 },
|
||||
)
|
||||
const receipt = await tx.wait()
|
||||
expect(receipt.status).toEqual(1)
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
18
contracts/ts/buildMiMC.ts
Normal file
18
contracts/ts/buildMiMC.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import * as Artifactor from 'truffle-artifactor'
|
||||
const mimcGenContract = require('circomlib/src/mimcsponge_gencontract.js');
|
||||
const artifactor = new Artifactor('compiled/')
|
||||
const SEED = 'mimcsponge'
|
||||
|
||||
const buildMiMC = async () => {
|
||||
await artifactor.save({
|
||||
contractName: 'MiMC',
|
||||
abi: mimcGenContract.abi,
|
||||
unlinked_binary: mimcGenContract.createCode(SEED, 220),
|
||||
})
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
buildMiMC()
|
||||
}
|
||||
|
||||
export default buildMiMC
|
||||
0
contracts/ts/index.ts
Normal file
0
contracts/ts/index.ts
Normal file
9
contracts/tsconfig.json
Normal file
9
contracts/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./build"
|
||||
},
|
||||
"include": [
|
||||
"./ts"
|
||||
]
|
||||
}
|
||||
1
docs/.nojekyll
Normal file
1
docs/.nojekyll
Normal file
@@ -0,0 +1 @@
|
||||
This file makes sure that Github Pages doesn't process mdBook's output.
|
||||
4
docs/FontAwesome/css/font-awesome.css
vendored
Normal file
4
docs/FontAwesome/css/font-awesome.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
docs/FontAwesome/fonts/FontAwesome.ttf
Normal file
BIN
docs/FontAwesome/fonts/FontAwesome.ttf
Normal file
Binary file not shown.
BIN
docs/FontAwesome/fonts/fontawesome-webfont.eot
Normal file
BIN
docs/FontAwesome/fonts/fontawesome-webfont.eot
Normal file
Binary file not shown.
2671
docs/FontAwesome/fonts/fontawesome-webfont.svg
Normal file
2671
docs/FontAwesome/fonts/fontawesome-webfont.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 434 KiB |
BIN
docs/FontAwesome/fonts/fontawesome-webfont.ttf
Normal file
BIN
docs/FontAwesome/fonts/fontawesome-webfont.ttf
Normal file
Binary file not shown.
BIN
docs/FontAwesome/fonts/fontawesome-webfont.woff
Normal file
BIN
docs/FontAwesome/fonts/fontawesome-webfont.woff
Normal file
Binary file not shown.
BIN
docs/FontAwesome/fonts/fontawesome-webfont.woff2
Normal file
BIN
docs/FontAwesome/fonts/fontawesome-webfont.woff2
Normal file
Binary file not shown.
293
docs/about.html
Normal file
293
docs/about.html
Normal file
@@ -0,0 +1,293 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>About - </title>
|
||||
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="shortcut icon" href="favicon.png">
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/general.css">
|
||||
<link rel="stylesheet" href="css/chrome.css">
|
||||
<link rel="stylesheet" href="css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="highlight.css">
|
||||
<link rel="stylesheet" href="tomorrow-night.css">
|
||||
<link rel="stylesheet" href="ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="expanded "><a href="about.html" class="active"><strong aria-hidden="true">1.</strong> About</a></li><li class="expanded "><a href="howitworks.html"><strong aria-hidden="true">2.</strong> How it works</a></li><li class="expanded "><a href="quickstart.html"><strong aria-hidden="true">3.</strong> Quick start</a></li><li class="expanded "><a href="usage.html"><strong aria-hidden="true">4.</strong> Usage</a></li><li class="expanded "><a href="api.html"><strong aria-hidden="true">5.</strong> Contract API</a></li><li class="expanded "><a href="libsemaphore.html"><strong aria-hidden="true">6.</strong> libsemaphore</a></li><li class="expanded "><a href="trustedsetup.html"><strong aria-hidden="true">7.</strong> Trusted setup</a></li><li class="expanded "><a href="audit.html"><strong aria-hidden="true">8.</strong> Security audit</a></li><li class="expanded "><a href="creditsandresources.html"><strong aria-hidden="true">9.</strong> Credits and resources</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
|
||||
<div id="menu-bar" class="menu-bar">
|
||||
<div id="menu-bar-sticky-container">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#about" id="about">About</a></h1>
|
||||
<p><a href="https://github.com/appliedzkp/semaphore">Semaphore</a> is a zero-knowledge gadget
|
||||
which allows Ethereum users to prove their membership of a set which they had
|
||||
previously joined without revealing their original identity. At the same time,
|
||||
it allows users to signal their endorsement of an arbitrary string. It is
|
||||
designed to be a simple and generic privacy layer for Ethereum dApps. Use cases
|
||||
include private voting, whistleblowing, mixers, and anonymous authentication.
|
||||
Finally, it provides a simple built-in mechanism to prevent double-signalling
|
||||
or double-spending.</p>
|
||||
<p>This gadget comprises of smart contracts and
|
||||
<a href="https://z.cash/technology/zksnarks/">zero-knowledge</a> components which work in
|
||||
tandem. The Semaphore smart contract handles state, permissions, and proof
|
||||
verification on-chain. The zero-knowledge components work off-chain to allow
|
||||
the user to generate proofs, which allow the smart contract to update its state
|
||||
if these proofs are valid.</p>
|
||||
<p>Semaphore is designed for smart contract and dApp developers, not end users.
|
||||
Developers should abstract its features away in order to provide user-friendly
|
||||
privacy.</p>
|
||||
<p>Try a simple demo <a href="https://weijiekoh.github.io/semaphore-ui/">here</a> or read a
|
||||
high-level description of Semaphore
|
||||
<a href="https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b">here</a>.</p>
|
||||
<h2><a class="header" href="#basic-features" id="basic-features">Basic features</a></h2>
|
||||
<p>In sum, Semaphore provides the ability to:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Register an identity in a smart contract, and then:</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Broadcast a signal:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Anonymously prove that their identity is in the set of registered
|
||||
identities, and at the same time:</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Publicly store an arbitrary string in the contract, if and only if that
|
||||
string is unique to the user and the contract’s current external
|
||||
nullifier, which is a unique value akin to a topic. This means that
|
||||
double-signalling the same message under the same external nullifier is
|
||||
not possible.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<h3><a class="header" href="#about-external-nullifiers" id="about-external-nullifiers">About external nullifiers</a></h3>
|
||||
<p>Think of an external nullifier as a voting booth where each user may only cast
|
||||
one vote. If they try to cast a second vote a the same booth, that vote is
|
||||
invalid.</p>
|
||||
<p>An external nullifier is any 29-byte value. Semaphore always starts with one
|
||||
external nullifier, which is set upon contract deployment. The owner of the
|
||||
Semaphore contract may add more external nullifiers, deactivate, or reactivate
|
||||
existing ones.</p>
|
||||
<p>The first time a particular user broadcasts a signal to an active external
|
||||
nullifier <code>n</code>, and if the user's proof of membership of the set of registered
|
||||
users is valid, the transaction will succeed. The second time she does so to
|
||||
the same <code>n</code>, however, her transaction will fail.</p>
|
||||
<p>Additionally, all signals broadcast transactions to a deactivated external
|
||||
nullifier will fail.</p>
|
||||
<p>Each client application must use the above features of Semaphore in a unique
|
||||
way to achieve its privacy goals. A mixer, for instance, would use one external
|
||||
nullifier as such:</p>
|
||||
<table><thead><tr><th>Signal</th><th>External nullifier</th></tr></thead><tbody>
|
||||
<tr><td>The hash of the recipient's address, relayer's address, and the relayer's fee</td><td>The mixer contract's address</td></tr>
|
||||
</tbody></table>
|
||||
<p>This allows anonymous withdrawals of funds (via a transaction relayer, who is
|
||||
rewarded with a fee), and prevents double-spending as there is only one
|
||||
external nullifier.</p>
|
||||
<p>An anonymous voting app would be configured differently:</p>
|
||||
<table><thead><tr><th>Signal</th><th>External nullifier</th></tr></thead><tbody>
|
||||
<tr><td>The hash of the respondent's answer</td><td>The hash of the question</td></tr>
|
||||
</tbody></table>
|
||||
<p>This allows any user to vote with an arbitary response (e.g. yes, no, or maybe)
|
||||
to any question. The user, however, can only vote once per question.</p>
|
||||
<h2><a class="header" href="#about-the-code" id="about-the-code">About the code</a></h2>
|
||||
<p>This repository contains the code for Semaphore's contracts written in
|
||||
Soliidty, and zk-SNARK circuits written in
|
||||
<a href="https://github.com/iden3/circom">circom</a>. It also contains Typescript code to
|
||||
execute tests.</p>
|
||||
<p>The code has been audited by ABDK Consulting. Their suggested security and
|
||||
efficiency fixes have been applied.</p>
|
||||
<p>A multi-party computation to produce the zk-SNARK proving and verification keys
|
||||
for Semaphore will begin in the near future.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
|
||||
|
||||
<a rel="next" href="howitworks.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
||||
|
||||
|
||||
<a href="howitworks.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playpen_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
|
||||
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
299
docs/api.html
Normal file
299
docs/api.html
Normal file
@@ -0,0 +1,299 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Contract API - </title>
|
||||
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="shortcut icon" href="favicon.png">
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/general.css">
|
||||
<link rel="stylesheet" href="css/chrome.css">
|
||||
<link rel="stylesheet" href="css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="highlight.css">
|
||||
<link rel="stylesheet" href="tomorrow-night.css">
|
||||
<link rel="stylesheet" href="ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="expanded "><a href="about.html"><strong aria-hidden="true">1.</strong> About</a></li><li class="expanded "><a href="howitworks.html"><strong aria-hidden="true">2.</strong> How it works</a></li><li class="expanded "><a href="quickstart.html"><strong aria-hidden="true">3.</strong> Quick start</a></li><li class="expanded "><a href="usage.html"><strong aria-hidden="true">4.</strong> Usage</a></li><li class="expanded "><a href="api.html" class="active"><strong aria-hidden="true">5.</strong> Contract API</a></li><li class="expanded "><a href="libsemaphore.html"><strong aria-hidden="true">6.</strong> libsemaphore</a></li><li class="expanded "><a href="trustedsetup.html"><strong aria-hidden="true">7.</strong> Trusted setup</a></li><li class="expanded "><a href="audit.html"><strong aria-hidden="true">8.</strong> Security audit</a></li><li class="expanded "><a href="creditsandresources.html"><strong aria-hidden="true">9.</strong> Credits and resources</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
|
||||
<div id="menu-bar" class="menu-bar">
|
||||
<div id="menu-bar-sticky-container">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#contract-api" id="contract-api">Contract API</a></h1>
|
||||
<h2><a class="header" href="#constructor" id="constructor">Constructor</a></h2>
|
||||
<p><strong>Contract ABI</strong>:</p>
|
||||
<p><code>constructor(uint8 _treeLevels, uint232 _firstExternalNullifier)</code></p>
|
||||
<ul>
|
||||
<li><code>_treeLevels</code>: The depth of the identity tree.</li>
|
||||
<li><code>_firstExternalNullifier</code>: The first identity nullifier to add.</li>
|
||||
</ul>
|
||||
<p>The depth of the identity tree determines how many identity commitments may be
|
||||
added to this contract: <code>2 ^ _treeLevels</code>. Once the tree is full, further
|
||||
insertions will fail with the revert reason <code>IncrementalMerkleTree: tree is full</code>.</p>
|
||||
<p>The first external nullifier will be added as an external nullifier to the
|
||||
contract, and this external nullifier will be active once the deployment
|
||||
completes.</p>
|
||||
<h2><a class="header" href="#add-deactivate-or-reactivate-external-nullifiiers" id="add-deactivate-or-reactivate-external-nullifiiers">Add, deactivate, or reactivate external nullifiiers</a></h2>
|
||||
<p><strong>Contract ABI</strong>:</p>
|
||||
<p><code>addExternalNullifier(uint232 _externalNullifier)</code></p>
|
||||
<p>Adds an external nullifier to the contract. Only the owner can do this.
|
||||
This external nullifier is active once it is added.</p>
|
||||
<ul>
|
||||
<li><code>_externalNullifier</code>: The new external nullifier to set.</li>
|
||||
</ul>
|
||||
<p><code>deactivateExternalNullifier(uint232 _externalNullifier)</code></p>
|
||||
<ul>
|
||||
<li><code>_externalNullifier</code>: The existing external nullifier to deactivate.</li>
|
||||
</ul>
|
||||
<p>Deactivate an external nullifier. The external nullifier must already be active
|
||||
for this function to work. Only the owner can do this.</p>
|
||||
<p><code>reactivateExternalNullifier(uint232 _externalNullifier)</code></p>
|
||||
<p>Reactivate an external nullifier. The external nullifier must already be
|
||||
inactive for this function to work. Only the owner can do this.</p>
|
||||
<ul>
|
||||
<li><code>_externalNullifier</code>: The deactivated external nullifier to reactivate.</li>
|
||||
</ul>
|
||||
<h2><a class="header" href="#insert-identities" id="insert-identities">Insert identities</a></h2>
|
||||
<p><strong>Contract ABI</strong>:</p>
|
||||
<p><code>function insertIdentity(uint256 _identityCommitment)</code></p>
|
||||
<ul>
|
||||
<li><code>_identity_commitment</code>: The user's identity commitment, which is the hash of
|
||||
their public key and their identity nullifier (a random 31-byte value). It
|
||||
should be the output of a Pedersen hash. It is the responsibility of the
|
||||
caller to verify this.</li>
|
||||
</ul>
|
||||
<p><strong>Off-chain <code>libsemaphore</code> helper functions</strong>:</p>
|
||||
<p>Use <code>genIdentity()</code> to generate an <code>Identity</code> object, and
|
||||
<code>genIdentityCommitment(identity: Identity)</code> to generate the
|
||||
<code>_identityCommitment</code> value to pass to the contract.</p>
|
||||
<p>To convert <code>identity</code> to a string and back, so that you can store it in a
|
||||
database or somewhere safe, use <code>serialiseIdentity()</code> and
|
||||
<code>unSerialiseIdentity()</code>.</p>
|
||||
<p>See the <a href="./usage.html#insert-identities">Usage section on inserting
|
||||
identities</a> for more information.</p>
|
||||
<h2><a class="header" href="#broadcast-signals" id="broadcast-signals">Broadcast signals</a></h2>
|
||||
<p><strong>Contract ABI</strong>:</p>
|
||||
<pre><code>broadcastSignal(
|
||||
bytes memory _signal,
|
||||
uint256[8] memory _proof,
|
||||
uint256 _root,
|
||||
uint256 _nullifiersHash,
|
||||
uint232 _externalNullifier
|
||||
)
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><code>_signal</code>: the signal to broadcast.</li>
|
||||
<li><code>_proof</code>: a zk-SNARK proof (see below).</li>
|
||||
<li><code>_root</code>: The root of the identity tree, where the user's identity commitment
|
||||
is the last-inserted leaf.</li>
|
||||
<li><code>_nullifiersHash</code>: A uniquely derived hash of the external nullifier, user's
|
||||
identity nullifier, and the Merkle path index to their identity commitment.
|
||||
It ensures that a user cannot broadcast a signal with the same external
|
||||
nullifier more than once.</li>
|
||||
<li><code>_externalNullifier</code>: The external nullifier at which the signal is
|
||||
broadcast.</li>
|
||||
</ul>
|
||||
<p><strong>Off-chain <code>libsemaphore</code> helper functions</strong>:</p>
|
||||
<p>Use <code>libsemaphore</code>'s <code>genWitness()</code>, <code>genProof()</code>, <code>genPublicSignals()</code> and
|
||||
finally <code>genBroadcastSignalParams()</code> to generate the parameters to the
|
||||
contract's <code>broadcastSignal()</code> function.</p>
|
||||
<p>See the <a href="./usage.html#broadcast-signals">Usage section on broadcasting
|
||||
signals</a> for more information.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
<a rel="prev" href="usage.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a rel="next" href="libsemaphore.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
||||
<a href="usage.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="libsemaphore.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playpen_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
|
||||
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
228
docs/audit.html
Normal file
228
docs/audit.html
Normal file
@@ -0,0 +1,228 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Security audit - </title>
|
||||
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="shortcut icon" href="favicon.png">
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/general.css">
|
||||
<link rel="stylesheet" href="css/chrome.css">
|
||||
<link rel="stylesheet" href="css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="highlight.css">
|
||||
<link rel="stylesheet" href="tomorrow-night.css">
|
||||
<link rel="stylesheet" href="ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="expanded "><a href="about.html"><strong aria-hidden="true">1.</strong> About</a></li><li class="expanded "><a href="howitworks.html"><strong aria-hidden="true">2.</strong> How it works</a></li><li class="expanded "><a href="quickstart.html"><strong aria-hidden="true">3.</strong> Quick start</a></li><li class="expanded "><a href="usage.html"><strong aria-hidden="true">4.</strong> Usage</a></li><li class="expanded "><a href="api.html"><strong aria-hidden="true">5.</strong> Contract API</a></li><li class="expanded "><a href="libsemaphore.html"><strong aria-hidden="true">6.</strong> libsemaphore</a></li><li class="expanded "><a href="trustedsetup.html"><strong aria-hidden="true">7.</strong> Trusted setup</a></li><li class="expanded "><a href="audit.html" class="active"><strong aria-hidden="true">8.</strong> Security audit</a></li><li class="expanded "><a href="creditsandresources.html"><strong aria-hidden="true">9.</strong> Credits and resources</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
|
||||
<div id="menu-bar" class="menu-bar">
|
||||
<div id="menu-bar-sticky-container">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#security-audit" id="security-audit">Security audit</a></h1>
|
||||
<p>The <a href="https://ethereum.org/">Ethereum Foundation</a> and <a href="https://www.poa.network/">POA
|
||||
Network</a> commissioned <a href="https://www.abdk.consulting">ABDK
|
||||
Consulting</a> to audit the source code of Semaphore
|
||||
as well as relevant circuits in
|
||||
<a href="https://github.com/iden3/circomlib">circomlib</a>, which contains components
|
||||
which the Semaphore zk-SNARK uses.</p>
|
||||
<p>All security and performance issues have been fixed. The full audit report will
|
||||
be available soon.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
<a rel="prev" href="trustedsetup.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a rel="next" href="creditsandresources.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
||||
<a href="trustedsetup.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="creditsandresources.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playpen_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
|
||||
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
79
docs/ayu-highlight.css
Normal file
79
docs/ayu-highlight.css
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
Based off of the Ayu theme
|
||||
Original by Dempfi (https://github.com/dempfi/ayu)
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
background: #191f26;
|
||||
color: #e6e1cf;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote,
|
||||
.hljs-meta {
|
||||
color: #5c6773;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-attribute,
|
||||
.hljs-attr,
|
||||
.hljs-regexp,
|
||||
.hljs-link,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #ff7733;
|
||||
}
|
||||
|
||||
.hljs-number,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params {
|
||||
color: #ffee99;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-bullet {
|
||||
color: #b8cc52;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-built_in,
|
||||
.hljs-section {
|
||||
color: #ffb454;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-symbol {
|
||||
color: #ff7733;
|
||||
}
|
||||
|
||||
.hljs-name {
|
||||
color: #36a3d9;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #00568d;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #91b362;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #d96c75;
|
||||
}
|
||||
605
docs/book.js
Normal file
605
docs/book.js
Normal file
@@ -0,0 +1,605 @@
|
||||
"use strict";
|
||||
|
||||
// Fix back button cache problem
|
||||
window.onunload = function () { };
|
||||
|
||||
// Global variable, shared between modules
|
||||
function playpen_text(playpen) {
|
||||
let code_block = playpen.querySelector("code");
|
||||
|
||||
if (window.ace && code_block.classList.contains("editable")) {
|
||||
let editor = window.ace.edit(code_block);
|
||||
return editor.getValue();
|
||||
} else {
|
||||
return code_block.textContent;
|
||||
}
|
||||
}
|
||||
|
||||
(function codeSnippets() {
|
||||
function fetch_with_timeout(url, options, timeout = 6000) {
|
||||
return Promise.race([
|
||||
fetch(url, options),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
|
||||
]);
|
||||
}
|
||||
|
||||
var playpens = Array.from(document.querySelectorAll(".playpen"));
|
||||
if (playpens.length > 0) {
|
||||
fetch_with_timeout("https://play.rust-lang.org/meta/crates", {
|
||||
headers: {
|
||||
'Content-Type': "application/json",
|
||||
},
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
// get list of crates available in the rust playground
|
||||
let playground_crates = response.crates.map(item => item["id"]);
|
||||
playpens.forEach(block => handle_crate_list_update(block, playground_crates));
|
||||
});
|
||||
}
|
||||
|
||||
function handle_crate_list_update(playpen_block, playground_crates) {
|
||||
// update the play buttons after receiving the response
|
||||
update_play_button(playpen_block, playground_crates);
|
||||
|
||||
// and install on change listener to dynamically update ACE editors
|
||||
if (window.ace) {
|
||||
let code_block = playpen_block.querySelector("code");
|
||||
if (code_block.classList.contains("editable")) {
|
||||
let editor = window.ace.edit(code_block);
|
||||
editor.addEventListener("change", function (e) {
|
||||
update_play_button(playpen_block, playground_crates);
|
||||
});
|
||||
// add Ctrl-Enter command to execute rust code
|
||||
editor.commands.addCommand({
|
||||
name: "run",
|
||||
bindKey: {
|
||||
win: "Ctrl-Enter",
|
||||
mac: "Ctrl-Enter"
|
||||
},
|
||||
exec: _editor => run_rust_code(playpen_block)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// updates the visibility of play button based on `no_run` class and
|
||||
// used crates vs ones available on http://play.rust-lang.org
|
||||
function update_play_button(pre_block, playground_crates) {
|
||||
var play_button = pre_block.querySelector(".play-button");
|
||||
|
||||
// skip if code is `no_run`
|
||||
if (pre_block.querySelector('code').classList.contains("no_run")) {
|
||||
play_button.classList.add("hidden");
|
||||
return;
|
||||
}
|
||||
|
||||
// get list of `extern crate`'s from snippet
|
||||
var txt = playpen_text(pre_block);
|
||||
var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g;
|
||||
var snippet_crates = [];
|
||||
var item;
|
||||
while (item = re.exec(txt)) {
|
||||
snippet_crates.push(item[1]);
|
||||
}
|
||||
|
||||
// check if all used crates are available on play.rust-lang.org
|
||||
var all_available = snippet_crates.every(function (elem) {
|
||||
return playground_crates.indexOf(elem) > -1;
|
||||
});
|
||||
|
||||
if (all_available) {
|
||||
play_button.classList.remove("hidden");
|
||||
} else {
|
||||
play_button.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function run_rust_code(code_block) {
|
||||
var result_block = code_block.querySelector(".result");
|
||||
if (!result_block) {
|
||||
result_block = document.createElement('code');
|
||||
result_block.className = 'result hljs language-bash';
|
||||
|
||||
code_block.append(result_block);
|
||||
}
|
||||
|
||||
let text = playpen_text(code_block);
|
||||
let classes = code_block.querySelector('code').classList;
|
||||
let has_2018 = classes.contains("edition2018");
|
||||
let edition = has_2018 ? "2018" : "2015";
|
||||
|
||||
var params = {
|
||||
version: "stable",
|
||||
optimize: "0",
|
||||
code: text,
|
||||
edition: edition
|
||||
};
|
||||
|
||||
if (text.indexOf("#![feature") !== -1) {
|
||||
params.version = "nightly";
|
||||
}
|
||||
|
||||
result_block.innerText = "Running...";
|
||||
|
||||
fetch_with_timeout("https://play.rust-lang.org/evaluate.json", {
|
||||
headers: {
|
||||
'Content-Type': "application/json",
|
||||
},
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
body: JSON.stringify(params)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => result_block.innerText = response.result)
|
||||
.catch(error => result_block.innerText = "Playground Communication: " + error.message);
|
||||
}
|
||||
|
||||
// Syntax highlighting Configuration
|
||||
hljs.configure({
|
||||
tabReplace: ' ', // 4 spaces
|
||||
languages: [], // Languages used for auto-detection
|
||||
});
|
||||
|
||||
if (window.ace) {
|
||||
// language-rust class needs to be removed for editable
|
||||
// blocks or highlightjs will capture events
|
||||
Array
|
||||
.from(document.querySelectorAll('code.editable'))
|
||||
.forEach(function (block) { block.classList.remove('language-rust'); });
|
||||
|
||||
Array
|
||||
.from(document.querySelectorAll('code:not(.editable)'))
|
||||
.forEach(function (block) { hljs.highlightBlock(block); });
|
||||
} else {
|
||||
Array
|
||||
.from(document.querySelectorAll('code'))
|
||||
.forEach(function (block) { hljs.highlightBlock(block); });
|
||||
}
|
||||
|
||||
// Adding the hljs class gives code blocks the color css
|
||||
// even if highlighting doesn't apply
|
||||
Array
|
||||
.from(document.querySelectorAll('code'))
|
||||
.forEach(function (block) { block.classList.add('hljs'); });
|
||||
|
||||
Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) {
|
||||
|
||||
var lines = Array.from(block.querySelectorAll('.boring'));
|
||||
// If no lines were hidden, return
|
||||
if (!lines.length) { return; }
|
||||
block.classList.add("hide-boring");
|
||||
|
||||
var buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
buttons.innerHTML = "<button class=\"fa fa-expand\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>";
|
||||
|
||||
// add expand button
|
||||
var pre_block = block.parentNode;
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
|
||||
pre_block.querySelector('.buttons').addEventListener('click', function (e) {
|
||||
if (e.target.classList.contains('fa-expand')) {
|
||||
e.target.classList.remove('fa-expand');
|
||||
e.target.classList.add('fa-compress');
|
||||
e.target.title = 'Hide lines';
|
||||
e.target.setAttribute('aria-label', e.target.title);
|
||||
|
||||
block.classList.remove('hide-boring');
|
||||
} else if (e.target.classList.contains('fa-compress')) {
|
||||
e.target.classList.remove('fa-compress');
|
||||
e.target.classList.add('fa-expand');
|
||||
e.target.title = 'Show hidden lines';
|
||||
e.target.setAttribute('aria-label', e.target.title);
|
||||
|
||||
block.classList.add('hide-boring');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (window.playpen_copyable) {
|
||||
Array.from(document.querySelectorAll('pre code')).forEach(function (block) {
|
||||
var pre_block = block.parentNode;
|
||||
if (!pre_block.classList.contains('playpen')) {
|
||||
var buttons = pre_block.querySelector(".buttons");
|
||||
if (!buttons) {
|
||||
buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
}
|
||||
|
||||
var clipButton = document.createElement('button');
|
||||
clipButton.className = 'fa fa-copy clip-button';
|
||||
clipButton.title = 'Copy to clipboard';
|
||||
clipButton.setAttribute('aria-label', clipButton.title);
|
||||
clipButton.innerHTML = '<i class=\"tooltiptext\"></i>';
|
||||
|
||||
buttons.insertBefore(clipButton, buttons.firstChild);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Process playpen code blocks
|
||||
Array.from(document.querySelectorAll(".playpen")).forEach(function (pre_block) {
|
||||
// Add play button
|
||||
var buttons = pre_block.querySelector(".buttons");
|
||||
if (!buttons) {
|
||||
buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||
}
|
||||
|
||||
var runCodeButton = document.createElement('button');
|
||||
runCodeButton.className = 'fa fa-play play-button';
|
||||
runCodeButton.hidden = true;
|
||||
runCodeButton.title = 'Run this code';
|
||||
runCodeButton.setAttribute('aria-label', runCodeButton.title);
|
||||
|
||||
buttons.insertBefore(runCodeButton, buttons.firstChild);
|
||||
runCodeButton.addEventListener('click', function (e) {
|
||||
run_rust_code(pre_block);
|
||||
});
|
||||
|
||||
if (window.playpen_copyable) {
|
||||
var copyCodeClipboardButton = document.createElement('button');
|
||||
copyCodeClipboardButton.className = 'fa fa-copy clip-button';
|
||||
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
|
||||
copyCodeClipboardButton.title = 'Copy to clipboard';
|
||||
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
|
||||
|
||||
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
|
||||
}
|
||||
|
||||
let code_block = pre_block.querySelector("code");
|
||||
if (window.ace && code_block.classList.contains("editable")) {
|
||||
var undoChangesButton = document.createElement('button');
|
||||
undoChangesButton.className = 'fa fa-history reset-button';
|
||||
undoChangesButton.title = 'Undo changes';
|
||||
undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
|
||||
|
||||
buttons.insertBefore(undoChangesButton, buttons.firstChild);
|
||||
|
||||
undoChangesButton.addEventListener('click', function () {
|
||||
let editor = window.ace.edit(code_block);
|
||||
editor.setValue(editor.originalCode);
|
||||
editor.clearSelection();
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function themes() {
|
||||
var html = document.querySelector('html');
|
||||
var themeToggleButton = document.getElementById('theme-toggle');
|
||||
var themePopup = document.getElementById('theme-list');
|
||||
var themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
|
||||
var stylesheets = {
|
||||
ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"),
|
||||
tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"),
|
||||
highlight: document.querySelector("[href$='highlight.css']"),
|
||||
};
|
||||
|
||||
function showThemes() {
|
||||
themePopup.style.display = 'block';
|
||||
themeToggleButton.setAttribute('aria-expanded', true);
|
||||
themePopup.querySelector("button#" + document.body.className).focus();
|
||||
}
|
||||
|
||||
function hideThemes() {
|
||||
themePopup.style.display = 'none';
|
||||
themeToggleButton.setAttribute('aria-expanded', false);
|
||||
themeToggleButton.focus();
|
||||
}
|
||||
|
||||
function set_theme(theme, store = true) {
|
||||
let ace_theme;
|
||||
|
||||
if (theme == 'coal' || theme == 'navy') {
|
||||
stylesheets.ayuHighlight.disabled = true;
|
||||
stylesheets.tomorrowNight.disabled = false;
|
||||
stylesheets.highlight.disabled = true;
|
||||
|
||||
ace_theme = "ace/theme/tomorrow_night";
|
||||
} else if (theme == 'ayu') {
|
||||
stylesheets.ayuHighlight.disabled = false;
|
||||
stylesheets.tomorrowNight.disabled = true;
|
||||
stylesheets.highlight.disabled = true;
|
||||
ace_theme = "ace/theme/tomorrow_night";
|
||||
} else {
|
||||
stylesheets.ayuHighlight.disabled = true;
|
||||
stylesheets.tomorrowNight.disabled = true;
|
||||
stylesheets.highlight.disabled = false;
|
||||
ace_theme = "ace/theme/dawn";
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor;
|
||||
}, 1);
|
||||
|
||||
if (window.ace && window.editors) {
|
||||
window.editors.forEach(function (editor) {
|
||||
editor.setTheme(ace_theme);
|
||||
});
|
||||
}
|
||||
|
||||
var previousTheme;
|
||||
try { previousTheme = localStorage.getItem('mdbook-theme'); } catch (e) { }
|
||||
if (previousTheme === null || previousTheme === undefined) { previousTheme = default_theme; }
|
||||
|
||||
if (store) {
|
||||
try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
|
||||
}
|
||||
|
||||
html.classList.remove(previousTheme);
|
||||
html.classList.add(theme);
|
||||
}
|
||||
|
||||
// Set theme
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
|
||||
set_theme(theme, false);
|
||||
|
||||
themeToggleButton.addEventListener('click', function () {
|
||||
if (themePopup.style.display === 'block') {
|
||||
hideThemes();
|
||||
} else {
|
||||
showThemes();
|
||||
}
|
||||
});
|
||||
|
||||
themePopup.addEventListener('click', function (e) {
|
||||
var theme = e.target.id || e.target.parentElement.id;
|
||||
set_theme(theme);
|
||||
});
|
||||
|
||||
themePopup.addEventListener('focusout', function(e) {
|
||||
// e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
|
||||
if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) {
|
||||
hideThemes();
|
||||
}
|
||||
});
|
||||
|
||||
// Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628
|
||||
document.addEventListener('click', function(e) {
|
||||
if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) {
|
||||
hideThemes();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
|
||||
if (!themePopup.contains(e.target)) { return; }
|
||||
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
hideThemes();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
var li = document.activeElement.parentElement;
|
||||
if (li && li.previousElementSibling) {
|
||||
li.previousElementSibling.querySelector('button').focus();
|
||||
}
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
var li = document.activeElement.parentElement;
|
||||
if (li && li.nextElementSibling) {
|
||||
li.nextElementSibling.querySelector('button').focus();
|
||||
}
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
themePopup.querySelector('li:first-child button').focus();
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
themePopup.querySelector('li:last-child button').focus();
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function sidebar() {
|
||||
var html = document.querySelector("html");
|
||||
var sidebar = document.getElementById("sidebar");
|
||||
var sidebarScrollBox = document.getElementById("sidebar-scrollbox");
|
||||
var sidebarLinks = document.querySelectorAll('#sidebar a');
|
||||
var sidebarToggleButton = document.getElementById("sidebar-toggle");
|
||||
var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
|
||||
var firstContact = null;
|
||||
|
||||
function showSidebar() {
|
||||
html.classList.remove('sidebar-hidden')
|
||||
html.classList.add('sidebar-visible');
|
||||
Array.from(sidebarLinks).forEach(function (link) {
|
||||
link.setAttribute('tabIndex', 0);
|
||||
});
|
||||
sidebarToggleButton.setAttribute('aria-expanded', true);
|
||||
sidebar.setAttribute('aria-hidden', false);
|
||||
try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
|
||||
}
|
||||
|
||||
|
||||
var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle');
|
||||
|
||||
function toggleSection(ev) {
|
||||
ev.currentTarget.parentElement.classList.toggle('expanded');
|
||||
}
|
||||
|
||||
Array.from(sidebarAnchorToggles).forEach(function (el) {
|
||||
el.addEventListener('click', toggleSection);
|
||||
});
|
||||
|
||||
function hideSidebar() {
|
||||
html.classList.remove('sidebar-visible')
|
||||
html.classList.add('sidebar-hidden');
|
||||
Array.from(sidebarLinks).forEach(function (link) {
|
||||
link.setAttribute('tabIndex', -1);
|
||||
});
|
||||
sidebarToggleButton.setAttribute('aria-expanded', false);
|
||||
sidebar.setAttribute('aria-hidden', true);
|
||||
try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
|
||||
}
|
||||
|
||||
// Toggle sidebar
|
||||
sidebarToggleButton.addEventListener('click', function sidebarToggle() {
|
||||
if (html.classList.contains("sidebar-hidden")) {
|
||||
showSidebar();
|
||||
} else if (html.classList.contains("sidebar-visible")) {
|
||||
hideSidebar();
|
||||
} else {
|
||||
if (getComputedStyle(sidebar)['transform'] === 'none') {
|
||||
hideSidebar();
|
||||
} else {
|
||||
showSidebar();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
sidebarResizeHandle.addEventListener('mousedown', initResize, false);
|
||||
|
||||
function initResize(e) {
|
||||
window.addEventListener('mousemove', resize, false);
|
||||
window.addEventListener('mouseup', stopResize, false);
|
||||
html.classList.add('sidebar-resizing');
|
||||
}
|
||||
function resize(e) {
|
||||
document.documentElement.style.setProperty('--sidebar-width', (e.clientX - sidebar.offsetLeft) + 'px');
|
||||
}
|
||||
//on mouseup remove windows functions mousemove & mouseup
|
||||
function stopResize(e) {
|
||||
html.classList.remove('sidebar-resizing');
|
||||
window.removeEventListener('mousemove', resize, false);
|
||||
window.removeEventListener('mouseup', stopResize, false);
|
||||
}
|
||||
|
||||
document.addEventListener('touchstart', function (e) {
|
||||
firstContact = {
|
||||
x: e.touches[0].clientX,
|
||||
time: Date.now()
|
||||
};
|
||||
}, { passive: true });
|
||||
|
||||
document.addEventListener('touchmove', function (e) {
|
||||
if (!firstContact)
|
||||
return;
|
||||
|
||||
var curX = e.touches[0].clientX;
|
||||
var xDiff = curX - firstContact.x,
|
||||
tDiff = Date.now() - firstContact.time;
|
||||
|
||||
if (tDiff < 250 && Math.abs(xDiff) >= 150) {
|
||||
if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
|
||||
showSidebar();
|
||||
else if (xDiff < 0 && curX < 300)
|
||||
hideSidebar();
|
||||
|
||||
firstContact = null;
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
// Scroll sidebar to current active section
|
||||
var activeSection = sidebar.querySelector(".active");
|
||||
if (activeSection) {
|
||||
sidebarScrollBox.scrollTop = activeSection.offsetTop;
|
||||
}
|
||||
})();
|
||||
|
||||
(function chapterNavigation() {
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
|
||||
if (window.search && window.search.hasFocus()) { return; }
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowRight':
|
||||
e.preventDefault();
|
||||
var nextButton = document.querySelector('.nav-chapters.next');
|
||||
if (nextButton) {
|
||||
window.location.href = nextButton.href;
|
||||
}
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
e.preventDefault();
|
||||
var previousButton = document.querySelector('.nav-chapters.previous');
|
||||
if (previousButton) {
|
||||
window.location.href = previousButton.href;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function clipboard() {
|
||||
var clipButtons = document.querySelectorAll('.clip-button');
|
||||
|
||||
function hideTooltip(elem) {
|
||||
elem.firstChild.innerText = "";
|
||||
elem.className = 'fa fa-copy clip-button';
|
||||
}
|
||||
|
||||
function showTooltip(elem, msg) {
|
||||
elem.firstChild.innerText = msg;
|
||||
elem.className = 'fa fa-copy tooltipped';
|
||||
}
|
||||
|
||||
var clipboardSnippets = new ClipboardJS('.clip-button', {
|
||||
text: function (trigger) {
|
||||
hideTooltip(trigger);
|
||||
let playpen = trigger.closest("pre");
|
||||
return playpen_text(playpen);
|
||||
}
|
||||
});
|
||||
|
||||
Array.from(clipButtons).forEach(function (clipButton) {
|
||||
clipButton.addEventListener('mouseout', function (e) {
|
||||
hideTooltip(e.currentTarget);
|
||||
});
|
||||
});
|
||||
|
||||
clipboardSnippets.on('success', function (e) {
|
||||
e.clearSelection();
|
||||
showTooltip(e.trigger, "Copied!");
|
||||
});
|
||||
|
||||
clipboardSnippets.on('error', function (e) {
|
||||
showTooltip(e.trigger, "Clipboard error!");
|
||||
});
|
||||
})();
|
||||
|
||||
(function scrollToTop () {
|
||||
var menuTitle = document.querySelector('.menu-title');
|
||||
|
||||
menuTitle.addEventListener('click', function () {
|
||||
document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
})();
|
||||
|
||||
(function autoHideMenu() {
|
||||
var menu = document.getElementById('menu-bar');
|
||||
|
||||
var previousScrollTop = document.scrollingElement.scrollTop;
|
||||
|
||||
document.addEventListener('scroll', function () {
|
||||
if (menu.classList.contains('folded') && document.scrollingElement.scrollTop < previousScrollTop) {
|
||||
menu.classList.remove('folded');
|
||||
} else if (!menu.classList.contains('folded') && document.scrollingElement.scrollTop > previousScrollTop) {
|
||||
menu.classList.add('folded');
|
||||
}
|
||||
|
||||
if (!menu.classList.contains('bordered') && document.scrollingElement.scrollTop > 0) {
|
||||
menu.classList.add('bordered');
|
||||
}
|
||||
|
||||
if (menu.classList.contains('bordered') && document.scrollingElement.scrollTop === 0) {
|
||||
menu.classList.remove('bordered');
|
||||
}
|
||||
|
||||
previousScrollTop = Math.max(document.scrollingElement.scrollTop, 0);
|
||||
}, { passive: true });
|
||||
})();
|
||||
7
docs/clipboard.min.js
vendored
Normal file
7
docs/clipboard.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
234
docs/creditsandresources.html
Normal file
234
docs/creditsandresources.html
Normal file
@@ -0,0 +1,234 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Credits and resources - </title>
|
||||
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="shortcut icon" href="favicon.png">
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/general.css">
|
||||
<link rel="stylesheet" href="css/chrome.css">
|
||||
<link rel="stylesheet" href="css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="highlight.css">
|
||||
<link rel="stylesheet" href="tomorrow-night.css">
|
||||
<link rel="stylesheet" href="ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="expanded "><a href="about.html"><strong aria-hidden="true">1.</strong> About</a></li><li class="expanded "><a href="howitworks.html"><strong aria-hidden="true">2.</strong> How it works</a></li><li class="expanded "><a href="quickstart.html"><strong aria-hidden="true">3.</strong> Quick start</a></li><li class="expanded "><a href="usage.html"><strong aria-hidden="true">4.</strong> Usage</a></li><li class="expanded "><a href="api.html"><strong aria-hidden="true">5.</strong> Contract API</a></li><li class="expanded "><a href="libsemaphore.html"><strong aria-hidden="true">6.</strong> libsemaphore</a></li><li class="expanded "><a href="trustedsetup.html"><strong aria-hidden="true">7.</strong> Trusted setup</a></li><li class="expanded "><a href="audit.html"><strong aria-hidden="true">8.</strong> Security audit</a></li><li class="expanded "><a href="creditsandresources.html" class="active"><strong aria-hidden="true">9.</strong> Credits and resources</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
|
||||
<div id="menu-bar" class="menu-bar">
|
||||
<div id="menu-bar-sticky-container">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#credits" id="credits">Credits</a></h1>
|
||||
<ul>
|
||||
<li>Barry WhiteHat</li>
|
||||
<li>Chih Cheng Liang</li>
|
||||
<li>Kobi Gurkan</li>
|
||||
<li>Koh Wei Jie</li>
|
||||
<li>Harry Roberts</li>
|
||||
</ul>
|
||||
<p>Many thanks to:</p>
|
||||
<ul>
|
||||
<li>ABDK Consulting</li>
|
||||
<li>Jordi Baylina / iden3</li>
|
||||
<li>POA Network</li>
|
||||
<li>PepperSec</li>
|
||||
<li>Ethereum Foundation</li>
|
||||
</ul>
|
||||
<h1><a class="header" href="#resources" id="resources">Resources</a></h1>
|
||||
<p><a href="https://medium.com/coinmonks/to-mixers-and-beyond-presenting-semaphore-a-privacy-gadget-built-on-ethereum-4c8b00857c9b">To Mixers and Beyond: presenting Semaphore, a privacy gadget built on Ethereum</a> - Koh Wei Jie</p>
|
||||
<p><a href="https://www.youtube.com/watch?v=maDHYyj30kg">Privacy in Ethereum</a> - Barry WhiteHat at the Taipei Ethereum Meetup</p>
|
||||
<p><a href="https://www.youtube.com/watch?v=lv6iK9qezBY">Snarks for mixing, signaling and scaling by</a> - Barry WhiteHat at Devcon 4</p>
|
||||
<p><a href="https://www.youtube.com/watch?v=zBUo7G95wYE">Privacy in Ethereum</a> - Barry WhiteHat at Devcon 5</p>
|
||||
<p><a href="https://www.youtube.com/watch?v=GzVT16lFOHU">A trustless Ethereum mixer using zero-knowledge signalling</a> - Koh Wei Jie and Barry WhiteHat at Devcon 5</p>
|
||||
<p><a href="https://www.youtube.com/watch?v=7wd2aAN2jXI">Hands-on Applications of Zero-Knowledge Signalling</a> - Koh Wei Jie at Devcon 5</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
<a rel="prev" href="audit.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
||||
<a href="audit.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playpen_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
|
||||
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
484
docs/css/chrome.css
Normal file
484
docs/css/chrome.css
Normal file
@@ -0,0 +1,484 @@
|
||||
/* CSS for UI elements (a.k.a. chrome) */
|
||||
|
||||
@import 'variables.css';
|
||||
|
||||
::-webkit-scrollbar {
|
||||
background: var(--bg);
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar);
|
||||
}
|
||||
html {
|
||||
scrollbar-color: var(--scrollbar) var(--bg);
|
||||
}
|
||||
#searchresults a,
|
||||
.content a:link,
|
||||
a:visited,
|
||||
a > .hljs {
|
||||
color: var(--links);
|
||||
}
|
||||
|
||||
/* Menu Bar */
|
||||
|
||||
#menu-bar {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 101;
|
||||
margin: auto calc(0px - var(--page-padding));
|
||||
}
|
||||
#menu-bar > #menu-bar-sticky-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
background-color: var(--bg);
|
||||
border-bottom-color: var(--bg);
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
.js #menu-bar > #menu-bar-sticky-container {
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
#menu-bar.bordered > #menu-bar-sticky-container {
|
||||
border-bottom-color: var(--table-border-color);
|
||||
}
|
||||
#menu-bar i, #menu-bar .icon-button {
|
||||
position: relative;
|
||||
padding: 0 8px;
|
||||
z-index: 10;
|
||||
line-height: var(--menu-bar-height);
|
||||
cursor: pointer;
|
||||
transition: color 0.5s;
|
||||
}
|
||||
@media only screen and (max-width: 420px) {
|
||||
#menu-bar i, #menu-bar .icon-button {
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
.icon-button i {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.right-buttons {
|
||||
margin: 0 15px;
|
||||
}
|
||||
.right-buttons a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-container {
|
||||
transform: translateY(calc(-10px - var(--menu-bar-height)));
|
||||
}
|
||||
|
||||
.left-buttons {
|
||||
display: flex;
|
||||
margin: 0 5px;
|
||||
}
|
||||
.no-js .left-buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
display: inline-block;
|
||||
font-weight: 200;
|
||||
font-size: 2rem;
|
||||
line-height: var(--menu-bar-height);
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.js .menu-title {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-bar,
|
||||
.menu-bar:visited,
|
||||
.nav-chapters,
|
||||
.nav-chapters:visited,
|
||||
.mobile-nav-chapters,
|
||||
.mobile-nav-chapters:visited,
|
||||
.menu-bar .icon-button,
|
||||
.menu-bar a i {
|
||||
color: var(--icons);
|
||||
}
|
||||
|
||||
.menu-bar i:hover,
|
||||
.menu-bar .icon-button:hover,
|
||||
.nav-chapters:hover,
|
||||
.mobile-nav-chapters i:hover {
|
||||
color: var(--icons-hover);
|
||||
}
|
||||
|
||||
/* Nav Icons */
|
||||
|
||||
.nav-chapters {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
max-width: 150px;
|
||||
min-width: 90px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
transition: color 0.5s, background-color 0.5s;
|
||||
}
|
||||
|
||||
.nav-chapters:hover {
|
||||
text-decoration: none;
|
||||
background-color: var(--theme-hover);
|
||||
transition: background-color 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
margin-top: 50px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-nav-chapters {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
width: 90px;
|
||||
border-radius: 5px;
|
||||
background-color: var(--sidebar-bg);
|
||||
}
|
||||
|
||||
.previous {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.next {
|
||||
float: right;
|
||||
right: var(--page-padding);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1080px) {
|
||||
.nav-wide-wrapper { display: none; }
|
||||
.nav-wrapper { display: block; }
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1380px) {
|
||||
.sidebar-visible .nav-wide-wrapper { display: none; }
|
||||
.sidebar-visible .nav-wrapper { display: block; }
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
|
||||
:not(pre) > .hljs {
|
||||
display: inline;
|
||||
padding: 0.1em 0.3em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
:not(pre):not(a) > .hljs {
|
||||
color: var(--inline-code-color);
|
||||
overflow-x: initial;
|
||||
}
|
||||
|
||||
a:hover > .hljs {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
pre {
|
||||
position: relative;
|
||||
}
|
||||
pre > .buttons {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
|
||||
color: var(--sidebar-fg);
|
||||
cursor: pointer;
|
||||
}
|
||||
pre > .buttons :hover {
|
||||
color: var(--sidebar-active);
|
||||
}
|
||||
pre > .buttons i {
|
||||
margin-left: 8px;
|
||||
}
|
||||
pre > .buttons button {
|
||||
color: inherit;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: inherit;
|
||||
}
|
||||
pre > .result {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Search */
|
||||
|
||||
#searchresults a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
mark {
|
||||
border-radius: 2px;
|
||||
padding: 0 3px 1px 3px;
|
||||
margin: 0 -3px -1px -3px;
|
||||
background-color: var(--search-mark-bg);
|
||||
transition: background-color 300ms linear;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
mark.fade-out {
|
||||
background-color: rgba(0,0,0,0) !important;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.searchbar-outer {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: var(--content-max-width);
|
||||
}
|
||||
|
||||
#searchbar {
|
||||
width: 100%;
|
||||
margin: 5px auto 0px auto;
|
||||
padding: 10px 16px;
|
||||
transition: box-shadow 300ms ease-in-out;
|
||||
border: 1px solid var(--searchbar-border-color);
|
||||
border-radius: 3px;
|
||||
background-color: var(--searchbar-bg);
|
||||
color: var(--searchbar-fg);
|
||||
}
|
||||
#searchbar:focus,
|
||||
#searchbar.active {
|
||||
box-shadow: 0 0 3px var(--searchbar-shadow-color);
|
||||
}
|
||||
|
||||
.searchresults-header {
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
padding: 18px 0 0 5px;
|
||||
color: var(--searchresults-header-fg);
|
||||
}
|
||||
|
||||
.searchresults-outer {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: var(--content-max-width);
|
||||
border-bottom: 1px dashed var(--searchresults-border-color);
|
||||
}
|
||||
|
||||
ul#searchresults {
|
||||
list-style: none;
|
||||
padding-left: 20px;
|
||||
}
|
||||
ul#searchresults li {
|
||||
margin: 10px 0px;
|
||||
padding: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
ul#searchresults li.focus {
|
||||
background-color: var(--searchresults-li-bg);
|
||||
}
|
||||
ul#searchresults span.teaser {
|
||||
display: block;
|
||||
clear: both;
|
||||
margin: 5px 0 0 20px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
ul#searchresults span.teaser em {
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: var(--sidebar-width);
|
||||
font-size: 0.875em;
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overscroll-behavior-y: contain;
|
||||
background-color: var(--sidebar-bg);
|
||||
color: var(--sidebar-fg);
|
||||
}
|
||||
.sidebar-resizing {
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.js:not(.sidebar-resizing) .sidebar {
|
||||
transition: transform 0.3s; /* Animation: slide away */
|
||||
}
|
||||
.sidebar code {
|
||||
line-height: 2em;
|
||||
}
|
||||
.sidebar .sidebar-scrollbox {
|
||||
overflow-y: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 10px 10px;
|
||||
}
|
||||
.sidebar .sidebar-resize-handle {
|
||||
position: absolute;
|
||||
cursor: col-resize;
|
||||
width: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.js .sidebar .sidebar-resize-handle {
|
||||
cursor: col-resize;
|
||||
width: 5px;
|
||||
}
|
||||
.sidebar-hidden .sidebar {
|
||||
transform: translateX(calc(0px - var(--sidebar-width)));
|
||||
}
|
||||
.sidebar::-webkit-scrollbar {
|
||||
background: var(--sidebar-bg);
|
||||
}
|
||||
.sidebar::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar);
|
||||
}
|
||||
|
||||
.sidebar-visible .page-wrapper {
|
||||
transform: translateX(var(--sidebar-width));
|
||||
}
|
||||
@media only screen and (min-width: 620px) {
|
||||
.sidebar-visible .page-wrapper {
|
||||
transform: none;
|
||||
margin-left: var(--sidebar-width);
|
||||
}
|
||||
}
|
||||
|
||||
.chapter {
|
||||
list-style: none outside none;
|
||||
padding-left: 0;
|
||||
line-height: 2.2em;
|
||||
}
|
||||
|
||||
.chapter ol {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chapter li {
|
||||
display: flex;
|
||||
color: var(--sidebar-non-existant);
|
||||
}
|
||||
.chapter li a {
|
||||
display: block;
|
||||
padding: 0;
|
||||
text-decoration: none;
|
||||
color: var(--sidebar-fg);
|
||||
}
|
||||
|
||||
.chapter li a:hover {
|
||||
color: var(--sidebar-active);
|
||||
}
|
||||
|
||||
.chapter li a.active {
|
||||
color: var(--sidebar-active);
|
||||
}
|
||||
|
||||
.chapter li > a.toggle {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
padding: 0 10px;
|
||||
user-select: none;
|
||||
opacity: 0.68;
|
||||
}
|
||||
|
||||
.chapter li > a.toggle div {
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
|
||||
/* collapse the section */
|
||||
.chapter li:not(.expanded) + li > ol {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chapter li.expanded > a.toggle div {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.spacer {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
.chapter .spacer {
|
||||
background-color: var(--sidebar-spacer);
|
||||
}
|
||||
|
||||
@media (-moz-touch-enabled: 1), (pointer: coarse) {
|
||||
.chapter li a { padding: 5px 0; }
|
||||
.spacer { margin: 10px 0; }
|
||||
}
|
||||
|
||||
.section {
|
||||
list-style: none outside none;
|
||||
padding-left: 20px;
|
||||
line-height: 1.9em;
|
||||
}
|
||||
|
||||
/* Theme Menu Popup */
|
||||
|
||||
.theme-popup {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: var(--menu-bar-height);
|
||||
z-index: 1000;
|
||||
border-radius: 4px;
|
||||
font-size: 0.7em;
|
||||
color: var(--fg);
|
||||
background: var(--theme-popup-bg);
|
||||
border: 1px solid var(--theme-popup-border);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: none;
|
||||
}
|
||||
.theme-popup .default {
|
||||
color: var(--icons);
|
||||
}
|
||||
.theme-popup .theme {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 2px 10px;
|
||||
line-height: 25px;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
background: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
.theme-popup .theme:hover {
|
||||
background-color: var(--theme-hover);
|
||||
}
|
||||
.theme-popup .theme:hover:first-child,
|
||||
.theme-popup .theme:hover:last-child {
|
||||
border-top-left-radius: inherit;
|
||||
border-top-right-radius: inherit;
|
||||
}
|
||||
159
docs/css/general.css
Normal file
159
docs/css/general.css
Normal file
@@ -0,0 +1,159 @@
|
||||
/* Base styles and content styles */
|
||||
|
||||
@import 'variables.css';
|
||||
|
||||
:root {
|
||||
/* Browser default font-size is 16px, this way 1 rem = 10px */
|
||||
font-size: 62.5%;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
color: var(--fg);
|
||||
background-color: var(--bg);
|
||||
text-size-adjust: none;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-size: 1.6rem;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important;
|
||||
font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */
|
||||
}
|
||||
|
||||
.left { float: left; }
|
||||
.right { float: right; }
|
||||
.boring { opacity: 0.6; }
|
||||
.hide-boring .boring { display: none; }
|
||||
.hidden { display: none; }
|
||||
|
||||
h2, h3 { margin-top: 2.5em; }
|
||||
h4, h5 { margin-top: 2em; }
|
||||
|
||||
.header + .header h3,
|
||||
.header + .header h4,
|
||||
.header + .header h5 {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
h1 a.header:target::before,
|
||||
h2 a.header:target::before,
|
||||
h3 a.header:target::before,
|
||||
h4 a.header:target::before {
|
||||
display: inline-block;
|
||||
content: "»";
|
||||
margin-left: -30px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
h1 a.header:target,
|
||||
h2 a.header:target,
|
||||
h3 a.header:target,
|
||||
h4 a.header:target {
|
||||
scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);
|
||||
}
|
||||
|
||||
.page {
|
||||
outline: 0;
|
||||
padding: 0 var(--page-padding);
|
||||
}
|
||||
.page-wrapper {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.js:not(.sidebar-resizing) .page-wrapper {
|
||||
transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow-y: auto;
|
||||
padding: 0 15px;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
.content main {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: var(--content-max-width);
|
||||
}
|
||||
.content a { text-decoration: none; }
|
||||
.content a:hover { text-decoration: underline; }
|
||||
.content img { max-width: 100%; }
|
||||
.content .header:link,
|
||||
.content .header:visited {
|
||||
color: var(--fg);
|
||||
}
|
||||
.content .header:link,
|
||||
.content .header:visited:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 0 auto;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table td {
|
||||
padding: 3px 20px;
|
||||
border: 1px var(--table-border-color) solid;
|
||||
}
|
||||
table thead {
|
||||
background: var(--table-header-bg);
|
||||
}
|
||||
table thead td {
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
}
|
||||
table thead th {
|
||||
padding: 3px 20px;
|
||||
}
|
||||
table thead tr {
|
||||
border: 1px var(--table-header-bg) solid;
|
||||
}
|
||||
/* Alternate background colors for rows */
|
||||
table tbody tr:nth-child(2n) {
|
||||
background: var(--table-alternate-bg);
|
||||
}
|
||||
|
||||
|
||||
blockquote {
|
||||
margin: 20px 0;
|
||||
padding: 0 20px;
|
||||
color: var(--fg);
|
||||
background-color: var(--quote-bg);
|
||||
border-top: .1em solid var(--quote-border);
|
||||
border-bottom: .1em solid var(--quote-border);
|
||||
}
|
||||
|
||||
|
||||
:not(.footnote-definition) + .footnote-definition,
|
||||
.footnote-definition + :not(.footnote-definition) {
|
||||
margin-top: 2em;
|
||||
}
|
||||
.footnote-definition {
|
||||
font-size: 0.9em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.footnote-definition p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.tooltiptext {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
color: #fff;
|
||||
background-color: #333;
|
||||
transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */
|
||||
left: -8px; /* Half of the width of the icon */
|
||||
top: -35px;
|
||||
font-size: 0.8em;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px 8px;
|
||||
margin: 5px;
|
||||
z-index: 1000;
|
||||
}
|
||||
.tooltipped .tooltiptext {
|
||||
visibility: visible;
|
||||
}
|
||||
54
docs/css/print.css
Normal file
54
docs/css/print.css
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
#sidebar,
|
||||
#menu-bar,
|
||||
.nav-chapters,
|
||||
.mobile-nav-chapters {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#page-wrapper.page-wrapper {
|
||||
transform: none;
|
||||
margin-left: 0px;
|
||||
overflow-y: initial;
|
||||
}
|
||||
|
||||
#content {
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.page {
|
||||
overflow-y: initial;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #666666;
|
||||
border-radius: 5px;
|
||||
|
||||
/* Force background to be printed in Chrome */
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
pre > .buttons {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
a, a:visited, a:active, a:hover {
|
||||
color: #4183c4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
page-break-inside: avoid;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
page-break-inside: avoid;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.fa {
|
||||
display: none !important;
|
||||
}
|
||||
253
docs/css/variables.css
Normal file
253
docs/css/variables.css
Normal file
@@ -0,0 +1,253 @@
|
||||
|
||||
/* Globals */
|
||||
|
||||
:root {
|
||||
--sidebar-width: 300px;
|
||||
--page-padding: 15px;
|
||||
--content-max-width: 750px;
|
||||
--menu-bar-height: 50px;
|
||||
}
|
||||
|
||||
/* Themes */
|
||||
|
||||
.ayu {
|
||||
--bg: hsl(210, 25%, 8%);
|
||||
--fg: #c5c5c5;
|
||||
|
||||
--sidebar-bg: #14191f;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existant: #5c6773;
|
||||
--sidebar-active: #ffb454;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
--links: #0096cf;
|
||||
|
||||
--inline-code-color: #ffb454;
|
||||
|
||||
--theme-popup-bg: #14191f;
|
||||
--theme-popup-border: #5c6773;
|
||||
--theme-hover: #191f26;
|
||||
|
||||
--quote-bg: hsl(226, 15%, 17%);
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--table-border-color: hsl(210, 25%, 13%);
|
||||
--table-header-bg: hsl(210, 25%, 28%);
|
||||
--table-alternate-bg: hsl(210, 25%, 11%);
|
||||
|
||||
--searchbar-border-color: #848484;
|
||||
--searchbar-bg: #424242;
|
||||
--searchbar-fg: #fff;
|
||||
--searchbar-shadow-color: #d4c89f;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #252932;
|
||||
--search-mark-bg: #e3b171;
|
||||
}
|
||||
|
||||
.coal {
|
||||
--bg: hsl(200, 7%, 8%);
|
||||
--fg: #98a3ad;
|
||||
|
||||
--sidebar-bg: #292c2f;
|
||||
--sidebar-fg: #a1adb8;
|
||||
--sidebar-non-existant: #505254;
|
||||
--sidebar-active: #3473ad;
|
||||
--sidebar-spacer: #393939;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #43484d;
|
||||
--icons-hover: #b3c0cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;;
|
||||
|
||||
--theme-popup-bg: #141617;
|
||||
--theme-popup-border: #43484d;
|
||||
--theme-hover: #1f2124;
|
||||
|
||||
--quote-bg: hsl(234, 21%, 18%);
|
||||
--quote-border: hsl(234, 21%, 23%);
|
||||
|
||||
--table-border-color: hsl(200, 7%, 13%);
|
||||
--table-header-bg: hsl(200, 7%, 28%);
|
||||
--table-alternate-bg: hsl(200, 7%, 11%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #b7b7b7;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #98a3ad;
|
||||
--searchresults-li-bg: #2b2b2f;
|
||||
--search-mark-bg: #355c7d;
|
||||
}
|
||||
|
||||
.light {
|
||||
--bg: hsl(0, 0%, 100%);
|
||||
--fg: #333333;
|
||||
|
||||
--sidebar-bg: #fafafa;
|
||||
--sidebar-fg: #364149;
|
||||
--sidebar-non-existant: #aaaaaa;
|
||||
--sidebar-active: #008cff;
|
||||
--sidebar-spacer: #f4f4f4;
|
||||
|
||||
--scrollbar: #cccccc;
|
||||
|
||||
--icons: #cccccc;
|
||||
--icons-hover: #333333;
|
||||
|
||||
--links: #4183c4;
|
||||
|
||||
--inline-code-color: #6e6b5e;
|
||||
|
||||
--theme-popup-bg: #fafafa;
|
||||
--theme-popup-border: #cccccc;
|
||||
--theme-hover: #e6e6e6;
|
||||
|
||||
--quote-bg: hsl(197, 37%, 96%);
|
||||
--quote-border: hsl(197, 37%, 91%);
|
||||
|
||||
--table-border-color: hsl(0, 0%, 95%);
|
||||
--table-header-bg: hsl(0, 0%, 80%);
|
||||
--table-alternate-bg: hsl(0, 0%, 97%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #fafafa;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #e4f2fe;
|
||||
--search-mark-bg: #a2cff5;
|
||||
}
|
||||
|
||||
.navy {
|
||||
--bg: hsl(226, 23%, 11%);
|
||||
--fg: #bcbdd0;
|
||||
|
||||
--sidebar-bg: #282d3f;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existant: #505274;
|
||||
--sidebar-active: #2b79a2;
|
||||
--sidebar-spacer: #2d334f;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #b7b9cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;;
|
||||
|
||||
--theme-popup-bg: #161923;
|
||||
--theme-popup-border: #737480;
|
||||
--theme-hover: #282e40;
|
||||
|
||||
--quote-bg: hsl(226, 15%, 17%);
|
||||
--quote-border: hsl(226, 15%, 22%);
|
||||
|
||||
--table-border-color: hsl(226, 23%, 16%);
|
||||
--table-header-bg: hsl(226, 23%, 31%);
|
||||
--table-alternate-bg: hsl(226, 23%, 14%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #aeaec6;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #5f5f71;
|
||||
--searchresults-border-color: #5c5c68;
|
||||
--searchresults-li-bg: #242430;
|
||||
--search-mark-bg: #a2cff5;
|
||||
}
|
||||
|
||||
.rust {
|
||||
--bg: hsl(60, 9%, 87%);
|
||||
--fg: #262625;
|
||||
|
||||
--sidebar-bg: #3b2e2a;
|
||||
--sidebar-fg: #c8c9db;
|
||||
--sidebar-non-existant: #505254;
|
||||
--sidebar-active: #e69f67;
|
||||
--sidebar-spacer: #45373a;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #737480;
|
||||
--icons-hover: #262625;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #6e6b5e;
|
||||
|
||||
--theme-popup-bg: #e1e1db;
|
||||
--theme-popup-border: #b38f6b;
|
||||
--theme-hover: #99908a;
|
||||
|
||||
--quote-bg: hsl(60, 5%, 75%);
|
||||
--quote-border: hsl(60, 5%, 70%);
|
||||
|
||||
--table-border-color: hsl(60, 9%, 82%);
|
||||
--table-header-bg: #b3a497;
|
||||
--table-alternate-bg: hsl(60, 9%, 84%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #fafafa;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #dec2a2;
|
||||
--search-mark-bg: #e69f67;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.light.no-js {
|
||||
--bg: hsl(200, 7%, 8%);
|
||||
--fg: #98a3ad;
|
||||
|
||||
--sidebar-bg: #292c2f;
|
||||
--sidebar-fg: #a1adb8;
|
||||
--sidebar-non-existant: #505254;
|
||||
--sidebar-active: #3473ad;
|
||||
--sidebar-spacer: #393939;
|
||||
|
||||
--scrollbar: var(--sidebar-fg);
|
||||
|
||||
--icons: #43484d;
|
||||
--icons-hover: #b3c0cc;
|
||||
|
||||
--links: #2b79a2;
|
||||
|
||||
--inline-code-color: #c5c8c6;;
|
||||
|
||||
--theme-popup-bg: #141617;
|
||||
--theme-popup-border: #43484d;
|
||||
--theme-hover: #1f2124;
|
||||
|
||||
--quote-bg: hsl(234, 21%, 18%);
|
||||
--quote-border: hsl(234, 21%, 23%);
|
||||
|
||||
--table-border-color: hsl(200, 7%, 13%);
|
||||
--table-header-bg: hsl(200, 7%, 28%);
|
||||
--table-alternate-bg: hsl(200, 7%, 11%);
|
||||
|
||||
--searchbar-border-color: #aaa;
|
||||
--searchbar-bg: #b7b7b7;
|
||||
--searchbar-fg: #000;
|
||||
--searchbar-shadow-color: #aaa;
|
||||
--searchresults-header-fg: #666;
|
||||
--searchresults-border-color: #98a3ad;
|
||||
--searchresults-li-bg: #2b2b2f;
|
||||
--search-mark-bg: #355c7d;
|
||||
}
|
||||
}
|
||||
10
docs/elasticlunr.min.js
vendored
Normal file
10
docs/elasticlunr.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user