mirror of
https://github.com/semaphore-protocol/semaphore.git
synced 2026-01-14 17:18:03 -05:00
Compare commits
21 Commits
v3.0.0-bet
...
audit/v1
| 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,32 +0,0 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.yarn
|
||||
|
||||
# testing
|
||||
coverage
|
||||
coverage.json
|
||||
|
||||
# hardhat
|
||||
cache
|
||||
|
||||
# types
|
||||
types
|
||||
|
||||
# circuits
|
||||
circuits
|
||||
|
||||
# production
|
||||
dist
|
||||
build
|
||||
docs
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -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. -->
|
||||
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
|
||||
98
.gitignore
vendored
98
.gitignore
vendored
@@ -1,90 +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
|
||||
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,3 +0,0 @@
|
||||
{
|
||||
"**/*.{js,ts}": ["prettier --write", "eslint --fix"]
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.yarn
|
||||
|
||||
# testing
|
||||
coverage
|
||||
coverage.json
|
||||
|
||||
# hardhat
|
||||
cache
|
||||
packages/contracts/deployed-contracts/undefined.json
|
||||
packages/contracts/deployed-contracts/hardhat.json
|
||||
packages/contracts/deployed-contracts/localhost.json
|
||||
|
||||
# types
|
||||
types
|
||||
|
||||
# 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"
|
||||
}
|
||||
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
|
||||
@@ -1,8 +0,0 @@
|
||||
nodeLinker: node-modules
|
||||
checksumBehavior: update
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||
spec: "@yarnpkg/plugin-workspace-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.1.cjs
|
||||
@@ -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.
|
||||
313
README.md
313
README.md
@@ -1,307 +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/workflow/status/semaphore-protocol/semaphore/production?label=test&style=flat-square&logo=github">
|
||||
</a>
|
||||
<a href="https://coveralls.io/github/semaphore-protocol/semaphore">
|
||||
<img alt="Coveralls" src="https://img.shields.io/coveralls/github/semaphore-protocol/semaphore?style=flat-square&logo=coveralls">
|
||||
</a>
|
||||
<a href="https://deepscan.io/dashboard#view=project&tid=16502&pid=22324&bid=657461">
|
||||
<img src="https://deepscan.io/api/teams/16502/projects/22324/branches/657461/badge/grade.svg" alt="DeepScan grade">
|
||||
</a>
|
||||
<a href="https://eslint.org/">
|
||||
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint">
|
||||
</a>
|
||||
<a href="https://prettier.io/">
|
||||
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier">
|
||||
</a>
|
||||
<img alt="Repository top language" src="https://img.shields.io/github/languages/top/semaphore-protocol/semaphore?style=flat-square">
|
||||
</p>
|
||||
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.
|
||||
|
||||
<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://discord.gg/6mSdGHnstH">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
For more information, refer to the
|
||||
[documentation](https://appliedzkp.github.io/semaphore/).
|
||||
|
||||
| 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) (NPM: `@semaphore-protocol/contracts`) and JavaScript libraries to make the steps for offchain proof creation and onchain verification easier. To learn more about Semaphore visit [semaphore.appliedzkp.org](https://semaphore.appliedzkp.org).
|
||||
|
||||
You can find Semaphore V1 on [`version/1.0.0`](https://github.com/semaphore-protocol/semaphore/tree/version/1.0.0).
|
||||
|
||||
---
|
||||
|
||||
## 📦 Packages
|
||||
|
||||
<table>
|
||||
<th>Package</th>
|
||||
<th>Version</th>
|
||||
<th>Downloads</th>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/contracts">
|
||||
@semaphore-protocol/contracts
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/contracts">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/contracts.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/contracts">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/contracts.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/identity">
|
||||
@semaphore-protocol/identity
|
||||
</a>
|
||||
<a href="https://semaphore-protocol.github.io/semaphore/identity">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/identity">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/identity.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/identity">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/identity.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/group">
|
||||
@semaphore-protocol/group
|
||||
</a>
|
||||
<a href="https://semaphore-protocol.github.io/semaphore/group">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/group">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/group.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/group">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/group.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/proof">
|
||||
@semaphore-protocol/proof
|
||||
</a>
|
||||
<a href="https://semaphore-protocol.github.io/semaphore/proof">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/proof">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/proof.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/proof">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/proof.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/subgraph">
|
||||
@semaphore-protocol/subgraph
|
||||
</a>
|
||||
<a href="https://semaphore-protocol.github.io/semaphore/subgraph">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/subgraph">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/subgraph.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/subgraph">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/subgraph.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<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>
|
||||
<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"]
|
||||
}
|
||||
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
BIN
docs/favicon.png
Normal file
BIN
docs/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
79
docs/highlight.css
Normal file
79
docs/highlight.css
Normal file
@@ -0,0 +1,79 @@
|
||||
/* Base16 Atelier Dune Light - Theme */
|
||||
/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) */
|
||||
/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */
|
||||
|
||||
/* Atelier-Dune Comment */
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #AAA;
|
||||
}
|
||||
|
||||
/* Atelier-Dune Red */
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-attribute,
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-regexp,
|
||||
.hljs-link,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #d73737;
|
||||
}
|
||||
|
||||
/* Atelier-Dune Orange */
|
||||
.hljs-number,
|
||||
.hljs-meta,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params {
|
||||
color: #b65611;
|
||||
}
|
||||
|
||||
/* Atelier-Dune Green */
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet {
|
||||
color: #60ac39;
|
||||
}
|
||||
|
||||
/* Atelier-Dune Blue */
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #6684e1;
|
||||
}
|
||||
|
||||
/* Atelier-Dune Purple */
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
color: #b854d4;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
background: #f1f1f1;
|
||||
color: #6e6b5e;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #22863a;
|
||||
background-color: #f0fff4;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #b31d28;
|
||||
background-color: #ffeef0;
|
||||
}
|
||||
2
docs/highlight.js
Normal file
2
docs/highlight.js
Normal file
File diff suppressed because one or more lines are too long
330
docs/howitworks.html
Normal file
330
docs/howitworks.html
Normal file
@@ -0,0 +1,330 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>How it works - </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" class="active"><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="#how-it-works" id="how-it-works">How it works</a></h1>
|
||||
<h2><a class="header" href="#inserting-identities" id="inserting-identities">Inserting identities</a></h2>
|
||||
<p>An identity is comprised of the following information:</p>
|
||||
<ol>
|
||||
<li>An <a href="https://en.wikipedia.org/wiki/EdDSA">EdDSA</a> private key. Note that it is
|
||||
<em>not</em> an Ethereum private key.</li>
|
||||
<li>An identity nullifier, whih is a random 32-byte value.</li>
|
||||
<li>An identity trapdoor, whih is a random 32-byte value.</li>
|
||||
</ol>
|
||||
<p>An identity commitment is the Pedersen hash of:</p>
|
||||
<ol>
|
||||
<li>The public key associated with the identity's private key.</li>
|
||||
<li>The identity nullifier.</li>
|
||||
<li>The identity trapdoor.</li>
|
||||
</ol>
|
||||
<p>To register an identity, the user must insert their identity commitment into
|
||||
Semaphore's identity tree. They can do this by calling the Semaphore contract's
|
||||
<code>insertIdentity(uint256 _identityCommitment)</code> function. See the <a href="./api.html">API
|
||||
reference</a> for more information.</p>
|
||||
<h2><a class="header" href="#broadcasting-signals" id="broadcasting-signals">Broadcasting signals</a></h2>
|
||||
<p>To broadcast a signal, the user must invoke this Semaphore contract function:</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>To zk-SNARK proof must satisfy the constraints created by Semaphore's zk-SNARK
|
||||
circuit as described below:</p>
|
||||
<h3><a class="header" href="#the-zk-snark-circuit" id="the-zk-snark-circuit">The zk-SNARK circuit</a></h3>
|
||||
<p>The <a href="./circuits/circom/semaphore-base.circom">semaphore-base.circom</a> circuit
|
||||
helps to prove the following:</p>
|
||||
<h3><a class="header" href="#that-the-identity-commitment-exists-in-the-merkle-tree" id="that-the-identity-commitment-exists-in-the-merkle-tree">That the identity commitment exists in the Merkle tree</a></h3>
|
||||
<p><strong>Private inputs:</strong></p>
|
||||
<ul>
|
||||
<li><code>identity_pk</code>: the user's EdDSA public key</li>
|
||||
<li><code>identity_nullifier</code>: a random 32-byte value which the user should save</li>
|
||||
<li><code>identity_trapdoor</code>: a random 32-byte value which the user should save</li>
|
||||
<li><code>identity_path_elements</code>: the values along the Merkle path to the
|
||||
user's identity commitment</li>
|
||||
<li><code>identity_path_index[n_levels]</code>: the direction (left/right) per tree level
|
||||
corresponding to the Merkle path to the user's identity commitment</li>
|
||||
</ul>
|
||||
<p><strong>Public inputs:</strong></p>
|
||||
<ul>
|
||||
<li><code>root</code>: The Merkle root of the identity tree</li>
|
||||
</ul>
|
||||
<p><strong>Procedure:</strong></p>
|
||||
<p>The circuit hashes the public key, identity nullifier, and identity trapdoor to
|
||||
generate an <strong>identity commitment</strong>. It then verifies the Merkle proof against
|
||||
the Merkle root and the identity commitment.</p>
|
||||
<h3><a class="header" href="#that-the-signal-was-only-broadcasted-once" id="that-the-signal-was-only-broadcasted-once">That the signal was only broadcasted once</a></h3>
|
||||
<p><strong>Private inputs:</strong></p>
|
||||
<ul>
|
||||
<li><code>identity_nullifier</code>: as above</li>
|
||||
<li><code>identity_path_index</code>: as above</li>
|
||||
</ul>
|
||||
<p><strong>Public inputs:</strong></p>
|
||||
<ul>
|
||||
<li><code>external_nullifier</code>: the 29-byte external nullifier - see above</li>
|
||||
<li><code>nullifiers_hash</code>: the hash of the identity nullifier, external nullifier,
|
||||
and Merkle path index (<code>identity_path_index</code>)</li>
|
||||
</ul>
|
||||
<p><strong>Procedure:</strong></p>
|
||||
<p>The circuit hashes the given identity nullifier, external nullifier, and Merkle
|
||||
path index, and checks that it matches the given nullifiers hash. Additionally,
|
||||
the smart contract ensures that it has not previously seen this nullifiers
|
||||
hash. This way, double-signalling is impossible.</p>
|
||||
<h3><a class="header" href="#that-the-signal-was-truly-broadcasted-by-the-user-who-generated-the-proof" id="that-the-signal-was-truly-broadcasted-by-the-user-who-generated-the-proof">That the signal was truly broadcasted by the user who generated the proof</a></h3>
|
||||
<p><strong>Private inputs:</strong></p>
|
||||
<ul>
|
||||
<li><code>identity_pk</code>: as above</li>
|
||||
<li><code>auth_sig_r</code>: the <code>r</code> value of the signature of the signal</li>
|
||||
<li><code>auth_sig_s</code>: the <code>s</code> value of the signature of the signal</li>
|
||||
</ul>
|
||||
<p><strong>Public inputs:</strong></p>
|
||||
<ul>
|
||||
<li><code>signal_hash</code>: the hash of the signal</li>
|
||||
<li><code>external_nullifier</code>: the 29-byte external nullifier - see above</li>
|
||||
</ul>
|
||||
<p><strong>Procedure:</strong></p>
|
||||
<p>The circuit hashes the signal hash and the external nullifier, and verifies
|
||||
this output against the given public key and signature. This ensures the
|
||||
authenticity of the signal and prevents front-running attacks.</p>
|
||||
<h2><a class="header" href="#cryptographic-primitives" id="cryptographic-primitives">Cryptographic primitives</a></h2>
|
||||
<p>Semaphore uses MiMC for the Merkle tree, Pedersen commmitments for the identity
|
||||
commitments, Blake2 for the nullifiers hash, and EdDSA for the signature.</p>
|
||||
<p>MiMC is a relatively new hash function. We use the recommended MiMC
|
||||
construction from <a href="https://eprint.iacr.org/2016/492.pdf">Albrecht et al</a>, and
|
||||
there is a prize to break MiMC at <a href="http://mimchash.org">http://mimchash.org</a>
|
||||
which has not been claimed yet.</p>
|
||||
<p>We have also implemented a version of Semaphore which uses the Poseidon hash
|
||||
function for the Merkle tree and EdDSA signature verification. This may have
|
||||
better security than MiMC, allows identity insertions to save about 20% gas,
|
||||
and roughly halves the proving time. Note, however, that the Poseidon-related
|
||||
circuits and EVM bytecode generator have not been audited, so use it with
|
||||
caution. To use it, checkout the <code>feat/poseidon</code> branch of this repository.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
<a rel="prev" href="about.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="quickstart.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="about.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="quickstart.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>
|
||||
402
docs/index.html
402
docs/index.html
@@ -1,123 +1,293 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html style="height: 100%">
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="IE=edge" />
|
||||
<title>Semaphore packages</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="A monorepo of Semaphore packages."
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||
/>
|
||||
<!-- 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
|
||||
style="
|
||||
margin: 0;
|
||||
background-color: #EAF0F4;
|
||||
color: #000;
|
||||
height: 100%;
|
||||
font-family: 'Courier New', monospace;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
padding: 0 20px;
|
||||
text-align: center;
|
||||
"
|
||||
>
|
||||
<div style="display: flex">
|
||||
<span style="margin-right: 5px">
|
||||
<img width="40" src="https://raw.githubusercontent.com/semaphore-protocol/website/main/static/img/semaphore-icon.svg">
|
||||
</span>
|
||||
<h1 style="margin: 0; font-size: 40px">Semaphore packages</h1>
|
||||
<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"><strong aria-hidden="true">9.</strong> Credits and resources</a></li></ol>
|
||||
</div>
|
||||
<p style="max-width: 500px">
|
||||
A monorepo of Semaphore packages.
|
||||
</p>
|
||||
<ul style="list-style-type: none; padding: 0; margin: 0; margin-top: 10px"></ul>
|
||||
</div>
|
||||
<footer
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 15px 20px;
|
||||
background-color: #EAF0F4;
|
||||
"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 900px;
|
||||
"
|
||||
>
|
||||
<p style="margin: 0; font-size: 16px">
|
||||
Copyright © 2022 Ethereum Foundation
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
style="margin-right: 15px; text-decoration: none"
|
||||
target="_blank"
|
||||
href="https://github.com/semaphore-protocol/semaphore"
|
||||
>
|
||||
<i
|
||||
class="fa fa-github"
|
||||
style="font-size: 24px; color: #000"
|
||||
></i>
|
||||
</a>
|
||||
<div 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>
|
||||
</footer>
|
||||
|
||||
<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>
|
||||
<script>
|
||||
const url =
|
||||
"https://api.github.com/repos/semaphore-protocol/semaphore/contents?ref=gh-pages"
|
||||
|
||||
function insertLinks(packages) {
|
||||
const [element] = window.document.getElementsByTagName("ul")
|
||||
let html = ""
|
||||
|
||||
for (const package of packages) {
|
||||
html += `<li style="display: flex; align-items: center; margin-bottom: 8px">
|
||||
<a style="margin-right: 15px" target="_blank" href="https://github.com/semaphore-protocol/semaphore/tree/main/packages/${package}">
|
||||
<i class="fa fa-github" style="font-size: 24px; color: #000"></i>
|
||||
</a>
|
||||
<a style="color: #000; text-decoration: none; font-size: 16px"
|
||||
onmouseover="this.style.color='#404A4E';"
|
||||
onmouseout="this.style.color='#000';"
|
||||
target="_blank" href="https://semaphore-protocol.github.io/semaphore/${package}">
|
||||
@semaphore-protocol/${package} >
|
||||
</a></li>`
|
||||
}
|
||||
|
||||
element.innerHTML = html
|
||||
}
|
||||
|
||||
fetch(url)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
const ignore = [".nojekyll", "index.html", "CNAME"]
|
||||
const packages = data
|
||||
.map((c) => c.name)
|
||||
.filter((name) => !ignore.includes(name))
|
||||
|
||||
localStorage.setItem("packages", JSON.stringify(packages))
|
||||
|
||||
insertLinks(packages)
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
|
||||
382
docs/libsemaphore.html
Normal file
382
docs/libsemaphore.html
Normal file
@@ -0,0 +1,382 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>libsemaphore - </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" class="active"><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="#libsemaphore" id="libsemaphore">libsemaphore</a></h1>
|
||||
<p><a href="https://www.npmjs.com/package/libsemaphore"><code>libsemaphore</code></a> is a helper
|
||||
library for Semaphore written in Typescript. Any dApp written in Javascript or
|
||||
Typescript should use it as it provides useful abstractions over common tasks
|
||||
and objects, such as identities and proof generation.</p>
|
||||
<p>Note that only v1.0.14 and above works with the Semaphore code in this
|
||||
repository. v0.0.x is compatible with the pre-audited Semaphore code.</p>
|
||||
<h2><a class="header" href="#available-types-interfaces-and-functions" id="available-types-interfaces-and-functions">Available types, interfaces, and functions</a></h2>
|
||||
<h3><a class="header" href="#types" id="types">Types</a></h3>
|
||||
<p><strong><code>SnarkBigInt</code></strong></p>
|
||||
<p>A big integer type compatible with the <code>snarkjs</code> library. Note that it is not
|
||||
advisable to mix variables of this type with <code>bigNumber</code>s or <code>BigInt</code>s.
|
||||
Encapsulates <code>snarkjs.bigInt</code>.</p>
|
||||
<p><strong><code>EddsaPrivateKey</code></strong></p>
|
||||
<p>An <a href="https://tools.ietf.org/html/rfc8032">EdDSA</a> private key which should be 32
|
||||
bytes long. Encapsulates a <a href="https://nodejs.org/api/buffer.html"><code>Buffer</code></a>.</p>
|
||||
<p><strong><code>EddsaPublicKey</code></strong></p>
|
||||
<p>An EdDSA public key. Encapsulates an array of <code>SnarkBigInt</code>s.</p>
|
||||
<p><strong><code>SnarkProvingKey</code></strong></p>
|
||||
<p>A proving key, which when used with a secret <em>witness</em>, generates a zk-SNARK
|
||||
proof about said witness. Encapsulates a <code>Buffer</code>.</p>
|
||||
<p><strong><code>SnarkVerifyingKey</code></strong></p>
|
||||
<p>A verifying key which when used with public inputs to a zk-SNARK and a
|
||||
<code>SnarkProof</code>, can prove the proof's validity. Encapsulates a <code>Buffer</code>.</p>
|
||||
<p><strong><code>SnarkWitness</code></strong></p>
|
||||
<p>The secret inputs to a zk-SNARK. Encapsulates an array of <code>SnarkBigInt</code>s.</p>
|
||||
<p><strong><code>SnarkPublicSignals</code></strong></p>
|
||||
<p>The public inputs to a zk-SNARK. Encapsulates an array of <code>SnarkBigInt</code>s.</p>
|
||||
<h3><a class="header" href="#interfaces" id="interfaces">Interfaces</a></h3>
|
||||
<p><strong><code>EddsaKeyPair</code></strong></p>
|
||||
<p>Encapsulates an <code>EddsaPublicKey</code> and an <code>EddsaPrivateKey</code>.</p>
|
||||
<pre><code class="language-ts">interface EddsaKeyPair {
|
||||
pubKey: EddsaPublicKey,
|
||||
privKey: EddsaPrivateKey,
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong><code>Identity</code></strong></p>
|
||||
<p>Encapsulates all information required to generate an identity commitment, and
|
||||
is crucial to creating <code>SnarkProof</code>s to broadcast signals.</p>
|
||||
<pre><code class="language-ts">interface Identity {
|
||||
keypair: EddsaKeyPair,
|
||||
identityNullifier: SnarkBigInt,
|
||||
identityTrapdoor: SnarkBigInt,
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong><code>SnarkProof</code></strong></p>
|
||||
<p>Note that <code>broadcastSignal()</code> accepts a <code>uint256[8]</code> array for its <code>_proof</code>
|
||||
parameter. See <code>genBroadcastSignalParams()</code>.</p>
|
||||
<pre><code class="language-ts">interface SnarkProof {
|
||||
pi_a: SnarkBigInt[]
|
||||
pi_b: SnarkBigInt[][]
|
||||
pi_c: SnarkBigInt[]
|
||||
}
|
||||
</code></pre>
|
||||
<h3><a class="header" href="#functions" id="functions">Functions</a></h3>
|
||||
<p><strong><code>genPubKey(privKey: EddsaPrivateKey): EddsaPublicKey</code></strong></p>
|
||||
<p>Generates a public EdDSA key from a supplied private key. To generate a private
|
||||
key, use <code>crypto.randomBytes(32)</code> where <code>crypto</code> is the built-in Node or
|
||||
browser module.</p>
|
||||
<p><strong><code>genIdentity(): Identity</code></strong></p>
|
||||
<p>This is a convenience function to generate a fresh and random <code>Identity</code>. That
|
||||
is, the 32-byte private key for the <code>EddsaKeyPair</code> is randomly generated, as
|
||||
are the distinct 31-byte identity nullifier and the 31-byte identity trapdoor
|
||||
values.</p>
|
||||
<p><strong><code>serialiseIdentity(identity: Identity): string</code></strong></p>
|
||||
<p>Converts an <code>Identity</code> into a JSON string which looks like this:</p>
|
||||
<pre><code class="language-text">["e82cc2b8654705e427df423c6300307a873a2e637028fab3163cf95b18bb172e","a02e517dfb3a4184adaa951d02bfe0fe092d1ee34438721d798db75b8db083","15c6540bf7bddb0616984fccda7e954a0fb5ea4679ac686509dc4bd7ba9c3b"]
|
||||
</code></pre>
|
||||
<p>You can also spell this function as <code>serializeIdentity</code>.</p>
|
||||
<p>To convert this string back into an <code>Identity</code>, use <code>unSerialiseIdentity()</code>.</p>
|
||||
<p><strong><code>unSerialiseIdentity(string: serialisedId): Identity</code></strong></p>
|
||||
<p>Converts the <code>string</code> output of <code>serialiseIdentity()</code> to an <code>Identity</code>.</p>
|
||||
<p>You can also spell this function as <code>unSerializeIdentity</code>.</p>
|
||||
<p><strong><code>genIdentityCommitment(identity: Identity): SnarkBigInt</code></strong></p>
|
||||
<p>Generates an identity commitment, which is the hash of the public key, the
|
||||
identity nullifier, and the identity trapdoor.</p>
|
||||
<p><strong><code>async genProof(witness: SnarkWitness, provingKey: SnarkProvingKey): SnarkProof</code></strong></p>
|
||||
<p>Generates a <code>SnarkProof</code>, which can be sent to the Semaphore contract's
|
||||
<code>broadcastSignal()</code> function. It can also be verified off-chain using
|
||||
<code>verifyProof()</code> below.</p>
|
||||
<p><strong><code>genPublicSignals(witness: SnarkWitness, circuit: SnarkCircuit): SnarkPublicSignals</code></strong></p>
|
||||
<p>Extracts the public signals to be supplied to the contract or <code>verifyProof()</code>.</p>
|
||||
<p><strong><code>verifyProof(verifyingKey: SnarkVerifyingKey, proof: SnarkProof, publicSignals: SnarkPublicSignals): boolean</code></strong></p>
|
||||
<p>Returns <code>true</code> if the given <code>proof</code> is valid, given the correct verifying key
|
||||
and public signals.</p>
|
||||
<p>Returns <code>false</code> otherwise.</p>
|
||||
<p><strong><code>signMsg(privKey: EddsaPrivateKey, msg: SnarkBigInt): EdDSAMiMcSpongeSignature)</code></strong></p>
|
||||
<p>Encapsualtes <code>circomlib.eddsa.signMiMCSponge</code> to sign a message <code>msg</code> using private key <code>privKey</code>.</p>
|
||||
<p><strong><code>verifySignature(msg: SnarkBigInt, signature: EdDSAMiMcSpongeSignature, pubKey: EddsaPublicKey)</code>: boolean</strong></p>
|
||||
<p>Returns <code>true</code> if the cryptographic <code>signature</code> of the signed <code>msg</code> is from the
|
||||
private key associated with <code>pubKey</code>.</p>
|
||||
<p>Returns <code>false</code> otherwise.</p>
|
||||
<p><strong><code>setupTree(levels: number, prefix: string): MerkleTree</code></strong></p>
|
||||
<p>Returns a Merkle tree created using
|
||||
<a href="https://www.npmjs.com/package/semaphore-merkle-tree"><code>semaphore-merkle-tree</code></a>
|
||||
with the same number of levels which the Semaphore zk-SNARK circuit expects.
|
||||
This tree is also configured to use <code>MimcSpongeHasher</code>, which is also what the
|
||||
circuit expects.</p>
|
||||
<p><code>levels</code> sets the number of levels of the tree. A tree with 20 levels, for
|
||||
instance, supports up to 1048576 deposits.</p>
|
||||
<p><strong><code>genCircuit(circuitDefinition: any)</code></strong></p>
|
||||
<p>Returns a <code>new snarkjs.Circuit(circuitDefinition)</code>. The <code>circuitDefinition</code>
|
||||
object should be the <code>JSON.parse</code>d result of the <code>circom</code> command which
|
||||
converts a <code>.circom</code> file to a <code>.json</code> file.</p>
|
||||
<p><strong><code>async genWitness(...)</code></strong></p>
|
||||
<p>This function has the following signature:</p>
|
||||
<pre><code class="language-ts">const genWitness = async (
|
||||
signal: string,
|
||||
circuit: SnarkCircuit,
|
||||
identity: Identity,
|
||||
idCommitments: SnarkBigInt[] | BigInt[] | ethers.utils.BigNumber[],
|
||||
treeDepth: number,
|
||||
externalNullifier: SnarkBigInt,
|
||||
)
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><code>signal</code> is the string you wish to broadcast.</li>
|
||||
<li><code>circuit</code> is the output of <code>genCircuit()</code>.</li>
|
||||
<li><code>identity</code> is the <code>Identity</code> whose identity commitment you want to prove is
|
||||
in the set of registered identities.</li>
|
||||
<li><code>idCommitments</code> is an array of registered identity commmitments; i.e. the
|
||||
leaves of the tree.</li>
|
||||
<li><code>treeDepth</code> is the number of levels which the Merkle tree used has</li>
|
||||
<li><code>externalNullifier</code> is the current external nullifier</li>
|
||||
</ul>
|
||||
<p>It returns an object as such:</p>
|
||||
<ul>
|
||||
<li><code>witness</code>: The witness to pass to <code>genProof()</code>.</li>
|
||||
<li><code>signal</code>: The computed signal for Semaphore. This is the hash of the
|
||||
recipient's address, relayer's address, and fee.</li>
|
||||
<li><code>signalHash</code>: The hash of the computed signal.</li>
|
||||
<li><code>msg</code>: The hash of the external nullifier and the signal hash</li>
|
||||
<li><code>signature</code>: The signature on the above msg.</li>
|
||||
<li><code>tree</code>: The Merkle tree object after it has been updated with the identity commitment</li>
|
||||
<li><code>identityPath</code>: The Merkle path to the identity commmitment</li>
|
||||
<li><code>identityPathIndex</code>: The leaf index of the identity commitment</li>
|
||||
<li><code>identityPathElements</code>: The elements along the above Merkle path</li>
|
||||
</ul>
|
||||
<p>Only <code>witness</code> is essential to generate the proof; the other data is only
|
||||
useful for debugging and additional off-chain checks, such as verifying the
|
||||
signature and the Merkle tree root.</p>
|
||||
<p><strong><code>formatForVerifierContract = (proof: SnarkProof, publicSignals: SnarkPublicSignals</code></strong></p>
|
||||
<p>Converts the data in <code>proof</code> and <code>publicSignals</code> to strings and rearranges
|
||||
elements of <code>proof.pi_b</code> so that <code>snarkjs</code>'s <code>verifier.sol</code> will accept it.
|
||||
To be specific, it returns an object as such:</p>
|
||||
<pre><code class="language-ts">{
|
||||
a: [ proof.pi_a[0].toString(), proof.pi_a[1].toString() ],
|
||||
b: [
|
||||
[ proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString() ],
|
||||
[ proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString() ],
|
||||
],
|
||||
c: [ proof.pi_c[0].toString(), proof.pi_c[1].toString() ],
|
||||
input: publicSignals.map((x) => x.toString()),
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong><code>stringifyBigInts = (obj: any) => object</code></strong></p>
|
||||
<p>Encapsulates <code>snarkjs.stringifyBigInts()</code>. Makes it easy to convert <code>SnarkProof</code>s to JSON. </p>
|
||||
<p><strong><code>unstringifyBigInts = (obj: any) => object</code></strong></p>
|
||||
<p>Encapsulates <code>snarkjs.unstringifyBigInts()</code>. Makes it easy to convert JSON to <code>SnarkProof</code>s.</p>
|
||||
<p><strong><code>genExternalNullifier = (plaintext: string) => string</code></strong></p>
|
||||
<p>Each external nullifier must be at most 29 bytes large. This function
|
||||
keccak-256-hashes a given <code>plaintext</code>, takes the last 29 bytes, and pads it
|
||||
(from the start) with 0s, and returns the resulting hex string.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
<a rel="prev" href="api.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="trustedsetup.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="api.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="trustedsetup.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>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user