Compare commits

..

50 Commits

Author SHA1 Message Date
cedoor
9c7afa888f chore: v4.0.0-beta.3
Former-commit-id: 9c6f9006e0
2024-03-19 15:16:21 +00:00
Cedoor
96cb583861 Merge pull request #716 from semaphore-protocol/fix/web-worker
Add temporary fixed resolution for `web-worker` dependency

Former-commit-id: 55eaa31dfb
2024-03-19 15:00:17 +00:00
cedoor
b603dbaf81 fix(proof): add temporary fixed resolution for web-worker dep
Adding the 'resolutions' field with the static version of web-worker solves the problem described in
issue #713. To be regarded as a temporary solution.

re #713


Former-commit-id: ca36ed5488
2024-03-19 12:54:43 +00:00
Cedoor
4fc4675678 Merge pull request #714 from semaphore-protocol/fix/deployed-contracts
Update link to fetch deployed contracts on docs

Former-commit-id: 65d3a41fa9
2024-03-18 20:08:37 +00:00
cedoor
6f0fdddae5 chore(contracts): update zk-kit imt.sol dependency
Former-commit-id: 4b074a443f
2024-03-18 20:04:10 +00:00
cedoor
31b05dba43 fix: update link to fetch deployed contracts on docs
Former-commit-id: a875e07ec2
2024-03-18 19:49:52 +00:00
Cedoor
94bdb44a45 Merge pull request #707 from semaphore-protocol/feat/create-group-contracts
Add a new `createGroup` function in the contracts

Former-commit-id: b55693948c
2024-03-18 10:30:49 +00:00
Cedoor
2bc10b0d5e Merge pull request #701 from semaphore-protocol/refactor/update-zk-kit
Update `utils` package and identity private key types.

Former-commit-id: 3b167fb46c
2024-03-15 23:13:54 +00:00
Cedoor
7ab9898beb Merge pull request #709 from semaphore-protocol/ref/remove-groupid-check
Remove existence check for `groupId `

Former-commit-id: d045bf9beb
2024-03-15 23:13:30 +00:00
cedoor
9e5f8351cc fix(utils): add missing exports to index.ts
re #642


Former-commit-id: 0987dee8db
2024-03-15 23:08:47 +00:00
cedoor
b6e6a8db03 fix(identity): update zk-kit packages with fixes
re #700


Former-commit-id: a2ec3013c8
2024-03-15 23:07:56 +00:00
cedoor
9eebaf4967 chore(identity): fix main path in package.json
re #700


Former-commit-id: e01ffb80b6
2024-03-15 23:07:52 +00:00
cedoor
9e5c464237 feat(utils): new semaphore utilities + zk-kit replacements
The utils package will no longer provide errors and types utilities as those functions have been
moved to zk-kit. The utils package now provides a list of supported networks and a function to
decode messages instead.

re #642, #641


Former-commit-id: d2ce1070f0
2024-03-15 23:07:44 +00:00
cedoor
2ea5d5c1cf refactor(identity)!: new private key types
The type of the identities' private key will always be a string type, either text or hexadecimal
strings. This will allow devs to store private keys more easily and avoid confusion with types.

re #700


Former-commit-id: eead04e45d
2024-03-15 23:02:51 +00:00
Cedoor
e9e24b04d1 Merge pull request #711 from semaphore-protocol/ref/add-tree-depth-constants
Add tree depth constants to JavaScript libraries and Solidity contracts

Former-commit-id: d256905c0c
2024-03-15 17:20:29 +00:00
Vivian Plasencia
2c5b0641c7 docs: update code comments related to supported tree depths
re #683


Former-commit-id: c3b9b98370
2024-03-15 17:39:42 +01:00
Vivian Plasencia
9e6d386748 refactor(contracts): add a constants sol file to store the constants
re #683


Former-commit-id: 969f1ed809
2024-03-15 17:27:37 +01:00
Vivian Plasencia
546d9f9dd6 refactor(contracts): add constants for the supported tree depths in contracts
the semaphore contract has new constants to save the min and max supported tree depth

re #683


Former-commit-id: 8793958d75
2024-03-15 13:44:38 +01:00
Vivian Plasencia
38b14a515c refactor(proof): add constants for supported tree depth
there is a new package in utils called constants which has the supported semaphore tree depths

re #683


Former-commit-id: 621003dd6c
2024-03-15 13:34:13 +01:00
Cedoor
193be0e1d5 Merge pull request #710 from semaphore-protocol/test/more-contracts-tests
Add more tests for the semaphore groups contract

Former-commit-id: 750c948157
2024-03-15 11:25:36 +00:00
Vivian Plasencia
dfb5473414 test(contracts): add more tests for the semaphore groups contract
re #484


Former-commit-id: e2c156d883
2024-03-15 11:00:00 +01:00
Vivian Plasencia
25847c4422 refactor(contracts): remove existence check for group id
since the group ids are created incrementally internally in the contracts, there's no need to check
whether the group id has already been taken.

re #708


Former-commit-id: e205bf6ad7
2024-03-14 23:40:41 +01:00
Vivian Plasencia
7b8a35470a feat(contracts): add a new create group function in the contracts
now there is a new function to create groups without passing any parameters

re #704


Former-commit-id: 67a0cecf3e
2024-03-14 23:19:55 +01:00
Cedoor
ee26734d94 Merge pull request #702 from zkfriendly/fix/accessControl
Fixes access control related issues

Former-commit-id: 0fe8d637f3
2024-03-14 20:38:21 +00:00
Cedoor
b50cddad2e Merge pull request #705 from semaphore-protocol/test/add-contracts-tests
Add contract tests

Former-commit-id: dcaba2583f
2024-03-14 20:37:46 +00:00
Cedoor
4578fc63de Merge pull request #703 from semaphore-protocol/fix/create-group
Use an incremental group id when creating a group in contracts

Former-commit-id: 87b48cac69
2024-03-14 20:37:31 +00:00
Vivian Plasencia
eb1f3ad5e2 test(contracts): add missing tests
re #484


Former-commit-id: e1481b8de8
2024-03-14 17:51:12 +01:00
Vivian Plasencia
6049a27308 chore: add hardhat artifacts to prettier ignore file
Former-commit-id: b12dd0fd98
2024-03-14 14:34:49 +01:00
Vivian Plasencia
7943fcd665 docs(contracts): update group counter code comment
Former-commit-id: 644f144e9c
2024-03-14 14:28:55 +01:00
Vivian Plasencia
66be942cb2 refactor(contracts): remove unnecessary initialization for group counter variable
Former-commit-id: f4ee63f909
2024-03-14 14:26:02 +01:00
Vivian Plasencia
b93ad8e14d chore(contracts): update prettier config
Former-commit-id: c670ed4b5d
2024-03-14 14:23:37 +01:00
Vivian Plasencia
7c69037745 style(contracts): add a line at the end of the prettierignore file
Former-commit-id: 2fc164b745
2024-03-14 10:52:42 +01:00
Vivian Plasencia
fcb3634e07 fix(contracts)!: use an incremental group id when creating a group
BREAKING CHANGE: the group id is no longer required when creating a group in contracts

re #682


Former-commit-id: 5f76dd5612
2024-03-14 10:34:42 +01:00
zkFriendly
b5825c52e5 style(contracts): format code with prettier
Former-commit-id: baa18c885e
2024-03-13 23:28:53 +01:00
zkFriendly
8795302788 perf(contracts): remove unnecessary onlyExistingGroup checks
Former-commit-id: 0b9d2a3d86
2024-03-13 23:28:16 +01:00
zkFriendly
3c2db6f724 test(contracts): assert only admin can add members
Former-commit-id: 4e4b4e629a
2024-03-13 23:19:51 +01:00
zkFriendly
fb79707e3e fix(contracts): add missing onlyGroupAdmin modifier to _addMembers
Former-commit-id: bfe050d16b
2024-03-13 23:19:31 +01:00
Cedoor
c5378b6914 Merge pull request #699 from semaphore-protocol/chore/automatic-releases
Update release workflow to auto-publish NPM packages

Former-commit-id: e75273ba27
2024-03-13 19:14:37 +00:00
Cedoor
8709b373e8 Merge pull request #695 from semaphore-protocol/chore/dev-deps
Update remaining dev dependencies

Former-commit-id: b22fa606dc
2024-03-13 19:13:58 +00:00
cedoor
3c993ab150 chore: update release workflow to auto-publish npm packages
re #698


Former-commit-id: 38d434083f
2024-03-13 16:42:11 +00:00
cedoor
541007d4a5 chore(contracts): update solhint-plugin-prettier dev dependency
re #662


Former-commit-id: fe30192879
2024-03-13 16:41:26 +00:00
cedoor
f3971efb55 chore: update dev dependencies + new cz adapter
re #662


Former-commit-id: 15d988efbb
2024-03-13 16:41:11 +00:00
cedoor
c8db909122 style: format code with prettier
re #662


Former-commit-id: 8a9d8a09b9
2024-03-13 16:40:56 +00:00
Cedoor
ab0366aa07 Merge pull request #697 from semaphore-protocol/ref/update-zk-kit-utils
Update `@zk-kit/utils` package

Former-commit-id: 84cf596e99
2024-03-13 16:02:56 +00:00
Vivian Plasencia
d4bda547ff refactor: update zk-kit utils package
Former-commit-id: 287f3a8796
2024-03-13 16:46:07 +01:00
Cedoor
d1f5fb331a Merge pull request #692 from zkfriendly/main
Gas optimization in getting new merkle tree roots after modifying group members

Former-commit-id: 81e8a6885a
2024-03-13 13:35:07 +00:00
Cedoor
14fe011ad7 Merge pull request #694 from semaphore-protocol/ref/proof-types
Replace `PackedPoints` with `PackedGroth16Proof`

Former-commit-id: c43a0a5ba2
2024-03-13 10:07:31 +00:00
Vivian Plasencia
2897754358 refactor(proof): update semaphore proof type
re #693


Former-commit-id: 09c842f9dc
2024-03-12 21:59:48 +01:00
zkFriendly
4a0ea1201c refactor(contracts): use returned new merkle tree root instead of calling getMerkleTreeRoot
Former-commit-id: f06ddd32e4
2024-03-12 16:41:15 +01:00
zkFriendly
634c2b11e6 refactor(contracts): return new merkle tree root after modifying group members
Former-commit-id: 7e2c208efd
2024-03-12 16:39:17 +01:00
78 changed files with 639 additions and 931 deletions

20
.commitlintrc.js Normal file
View File

@@ -0,0 +1,20 @@
const fs = require("node:fs")
const path = require("node:path")
const packages = fs.readdirSync(path.resolve(__dirname, "packages"))
module.exports = {
extends: ["@commitlint/config-conventional"],
prompt: {
scopes: [...packages],
markBreakingChangeMode: true,
allowCustomIssuePrefix: false,
allowEmptyIssuePrefix: false,
issuePrefixes: [
{
value: "re",
name: "re: ISSUES related"
}
]
}
}

View File

@@ -1,3 +0,0 @@
{
"extends": ["@commitlint/config-conventional"]
}

View File

@@ -22,10 +22,22 @@ jobs:
with:
node-version: 20
cache: yarn
registry-url: "https://registry.npmjs.org"
- name: Authentication
run: |
echo npmAuthToken: "$NODE_AUTH_TOKEN" >> ./.yarnrc.yml
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Install dependencies
run: yarn
- name: Publish packages
run: yarn version:publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: yarn version:release
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
if [ "$NO_HOOK" != "1" ]; then
exec < /dev/tty && yarn cz --hook || true
fi

View File

@@ -46,7 +46,7 @@ We always use ESLint and Prettier. To check that your code follows the rules, si
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.
Don't worry if it looks complicated, in our repositories, `git commit` opens an interactive app to create your conventional commit.
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**:

View File

@@ -34,7 +34,9 @@
<a href="https://www.gitpoap.io/gh/semaphore-protocol/semaphore" target="_blank">
<img src="https://public-api.gitpoap.io/v1/repo/semaphore-protocol/semaphore/badge">
</a>
<a href="http://commitizen.github.io/cz-cli/">
<img alt="Commitizen friendly" src="https://img.shields.io/badge/commitizen-friendly-586D76?style=flat-square">
</a>
</p>
<div align="center">
@@ -377,3 +379,15 @@ yarn docs
```
The output will be placed on the `docs` folder.
### Releases
Bump a new version with:
```bash
yarn version:bump <version>
# e.g. yarn version:bump 2.0.0
```
It will create a commit and a git tag that will need to pushed on the main branch. A workflow will be triggered and will
publish the Semaphore packages on [npm](https://www.npmjs.com/) and release a new version on Github with its changelogs automatically.

View File

@@ -27,7 +27,7 @@ export default function DeployedContracts() {
useEffect(() => {
fetch(
"https://raw.githubusercontent.com/semaphore-protocol/semaphore/feat/semaphore-v4/packages/contracts/deployed-contracts.json"
"https://raw.githubusercontent.com/semaphore-protocol/semaphore/main/packages/contracts/deployed-contracts.json"
)
.then((response) => response.json())
.catch(() => [])

View File

@@ -83,7 +83,11 @@ html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
font-feature-settings: "ss01" on, "ss02" on, "cv01" on, "cv03" on;
font-feature-settings:
"ss01" on,
"ss02" on,
"cv01" on,
"cv03" on;
font-weight: 400;
}

File diff suppressed because one or more lines are too long

View File

@@ -21,13 +21,12 @@
"prettier": "prettier -c .",
"prettier:write": "prettier -w .",
"docs": "typedoc --cname js.semaphore.pse.dev --githubPages true",
"version:bump": "yarn workspaces foreach -A --no-private version -d ${0} && yarn version apply --all && yarn remove:stable-version-field && git commit -am \"chore: v${0}\" && git tag v${0}",
"version:bump": "yarn workspaces foreach -A --no-private version -d ${0} && yarn version apply --all && yarn remove:stable-version-field && NO_HOOK=1 git commit -am \"chore: v${0}\" && git tag v${0}",
"version:publish": "yarn build:libraries && yarn clean:cli-templates && yarn workspaces foreach -A --no-private npm publish --tolerate-republish --access public",
"version:release": "changelogithub",
"clean": "ts-node scripts/clean-apps.ts && ts-node scripts/clean-packages.ts && yarn clean:cli-templates && rimraf node_modules",
"clean:cli-templates": "ts-node scripts/clean-cli-templates.ts",
"remove:stable-version-field": "ts-node scripts/remove-stable-version-field.ts && yarn prettier:write",
"commit": "cz",
"precommit": "lint-staged",
"postinstall": "husky install"
},
@@ -51,47 +50,42 @@
],
"packageManager": "yarn@4.1.0",
"devDependencies": {
"@commitlint/cli": "^16.0.2",
"@commitlint/config-conventional": "^16.0.0",
"@rollup/plugin-typescript": "^11.1.6",
"@types/circomlibjs": "^0.1.4",
"@types/download": "^8.0.1",
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.2",
"@types/glob": "^7.2.0",
"@types/jest": "^29.5.12",
"@types/node": "^20",
"@types/rimraf": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^5.9.1",
"@typescript-eslint/parser": "^5.9.1",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"changelogithub": "0.12.7",
"commitizen": "^4.2.4",
"cz-conventional-changelog": "^3.3.0",
"commitizen": "^4.3.0",
"cz-git": "^1.9.0",
"dotenv": "^16.0.2",
"eslint": "^8.2.0",
"eslint": "^8.56.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^16.1.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jest": "^27.8.0",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react": "^7.34.0",
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^8.0.3",
"husky": "^9.0.11",
"jest": "^29.7.0",
"jest-config": "^29.7.0",
"lint-staged": "^12.1.7",
"prettier": "^2.5.1",
"lint-staged": "^15.2.2",
"prettier": "^3.2.5",
"rimraf": "^5.0.5",
"rollup": "^4.9.6",
"snarkjs": "^0.7.2",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"tslib": "^2.6.2",
"typedoc": "^0.25.7",
"typedoc": "^0.25.8",
"typescript": "^5.3.3"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
"path": "./node_modules/cz-git"
}
},
"resolutions": {

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/circuits",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "Semaphore Circom circuits to generate zero-knowledge proofs.",
"license": "MIT",
"files": [

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/cli-template-contracts-hardhat",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "Semaphore Hardhat template.",
"license": "Unlicense",
"files": [
@@ -41,8 +41,8 @@
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.0",
"@semaphore-protocol/core": "4.0.0-beta.2",
"@semaphore-protocol/hardhat": "4.0.0-beta.2",
"@semaphore-protocol/core": "4.0.0-beta.3",
"@semaphore-protocol/hardhat": "4.0.0-beta.3",
"@typechain/ethers-v6": "^0.5.0",
"@typechain/hardhat": "^9.0.0",
"@types/chai": "^4.2.0",
@@ -70,7 +70,7 @@
"typescript": "^5.3.3"
},
"dependencies": {
"@semaphore-protocol/contracts": "4.0.0-beta.2"
"@semaphore-protocol/contracts": "4.0.0-beta.3"
},
"packageManager": "yarn@4.1.0"
}

View File

@@ -19,8 +19,8 @@
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.0",
"@semaphore-protocol/core": "4.0.0-beta.2",
"@semaphore-protocol/hardhat": "4.0.0-beta.2",
"@semaphore-protocol/core": "4.0.0-beta.3",
"@semaphore-protocol/hardhat": "4.0.0-beta.3",
"@typechain/ethers-v6": "^0.5.0",
"@typechain/hardhat": "^9.0.0",
"@types/chai": "^4.2.0",
@@ -48,7 +48,7 @@
"typescript": "^5.3.3"
},
"dependencies": {
"@semaphore-protocol/contracts": "4.0.0-beta.2"
"@semaphore-protocol/contracts": "4.0.0-beta.3"
},
"packageManager": "yarn@4.1.0"
}

View File

@@ -9,8 +9,8 @@
"lint": "next lint"
},
"dependencies": {
"@semaphore-protocol/core": "4.0.0-beta.2",
"@semaphore-protocol/data": "4.0.0-beta.2",
"@semaphore-protocol/core": "4.0.0-beta.3",
"@semaphore-protocol/data": "4.0.0-beta.3",
"ethers": "^6.11.1",
"next": "14.1.0",
"next-pwa": "^5.6.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/cli-template-monorepo-ethers",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "Semaphore Hardhat + Next.js + SemaphoreEthers template.",
"license": "Unlicense",
"files": [

View File

@@ -19,8 +19,8 @@
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.0",
"@semaphore-protocol/core": "4.0.0-beta.2",
"@semaphore-protocol/hardhat": "4.0.0-beta.2",
"@semaphore-protocol/core": "4.0.0-beta.3",
"@semaphore-protocol/hardhat": "4.0.0-beta.3",
"@typechain/ethers-v6": "^0.5.0",
"@typechain/hardhat": "^9.0.0",
"@types/chai": "^4.2.0",
@@ -48,7 +48,7 @@
"typescript": "^5.3.3"
},
"dependencies": {
"@semaphore-protocol/contracts": "4.0.0-beta.2"
"@semaphore-protocol/contracts": "4.0.0-beta.3"
},
"packageManager": "yarn@4.1.0"
}

View File

@@ -9,8 +9,8 @@
"lint": "next lint"
},
"dependencies": {
"@semaphore-protocol/core": "4.0.0-beta.2",
"@semaphore-protocol/data": "4.0.0-beta.2",
"@semaphore-protocol/core": "4.0.0-beta.3",
"@semaphore-protocol/data": "4.0.0-beta.3",
"ethers": "^6.11.1",
"next": "14.1.0",
"next-pwa": "^5.6.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/cli-template-monorepo-subgraph",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "Semaphore Hardhat + Next.js + SemaphoreSubgraph template.",
"license": "Unlicense",
"files": [

View File

@@ -1,7 +1,7 @@
{
"name": "@semaphore-protocol/cli",
"type": "module",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "A command line tool to set up your Semaphore project and get group data.",
"license": "MIT",
"bin": {
@@ -31,15 +31,18 @@
"access": "public"
},
"devDependencies": {
"@rollup/plugin-typescript": "^11.1.6",
"@types/figlet": "^1.5.8",
"@types/inquirer": "^9.0.7",
"@types/pacote": "^11.1.8",
"@types/semver": "^7.5.8",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-typescript2": "^0.36.0"
"rimraf": "^5.0.5",
"rollup": "^4.12.0",
"rollup-plugin-cleanup": "^3.2.1"
},
"dependencies": {
"@semaphore-protocol/data": "4.0.0-beta.2",
"@semaphore-protocol/data": "4.0.0-beta.3",
"@semaphore-protocol/utils": "4.0.0-beta.3",
"axios": "^1.6.7",
"boxen": "^7.1.1",
"chalk": "^5.3.0",

View File

@@ -1,4 +1,4 @@
import typescript from "rollup-plugin-typescript2"
import typescript from "@rollup/plugin-typescript"
import fs from "fs"
import cleanup from "rollup-plugin-cleanup"
@@ -9,7 +9,7 @@ const banner = `#!/usr/bin/env node
* @module ${pkg.name}
* @version ${pkg.version}
* @file ${pkg.description}
* @copyright Ethereum Foundation 2024
* @copyright Ethereum Foundation ${new Date().getFullYear()}
* @license ${pkg.license}
* @see [Github]{@link ${pkg.homepage}}
*/
@@ -18,11 +18,17 @@ const banner = `#!/usr/bin/env node
export default {
input: "src/index.ts",
output: [{ file: pkg.bin.semaphore, format: "es", banner }],
external: [...Object.keys(pkg.dependencies), "url", "fs", "path", "child_process"],
external: [
...Object.keys(pkg.dependencies),
"url",
"fs",
"path",
"child_process",
"@semaphore-protocol/utils/supported-networks"
],
plugins: [
(typescript as any)({
tsconfig: "./build.tsconfig.json",
useTsconfigDeclarationDir: true
typescript({
tsconfig: "./build.tsconfig.json"
}),
cleanup({ comments: "jsdoc" })
]

View File

@@ -1,4 +1,5 @@
import { GroupResponse, SemaphoreEthers, SemaphoreSubgraph, getSupportedNetworks } from "@semaphore-protocol/data"
import { GroupResponse, SemaphoreEthers, SemaphoreSubgraph } from "@semaphore-protocol/data"
import supportedNetworks from "@semaphore-protocol/utils/supported-networks"
import chalk from "chalk"
import { program } from "commander"
import decompress from "decompress"
@@ -16,8 +17,6 @@ import Spinner from "./spinner.js"
const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`
const { description, version } = JSON.parse(readFileSync(`${packagePath}/package.json`, "utf8"))
const supportedNetworks = getSupportedNetworks()
const supportedTemplates = [
{
value: "monorepo-ethers",

View File

@@ -0,0 +1,6 @@
{
"semi": false,
"arrowParens": "always",
"trailingComma": "none",
"plugins": ["prettier-plugin-solidity"]
}

View File

@@ -4,6 +4,7 @@ pragma solidity 0.8.23;
import {ISemaphore} from "./interfaces/ISemaphore.sol";
import {ISemaphoreVerifier} from "./interfaces/ISemaphoreVerifier.sol";
import {SemaphoreGroups} from "./base/SemaphoreGroups.sol";
import {MIN_DEPTH, MAX_DEPTH} from "./base/Constants.sol";
/// @title Semaphore
/// @dev This contract uses the Semaphore base contracts to provide a complete service
@@ -17,6 +18,9 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
/// @dev Gets a group id and returns the group parameters.
mapping(uint256 => Group) public groups;
/// @dev Counter to assign an incremental id to the groups.
uint256 public groupCounter;
/// @dev Initializes the Semaphore verifier used to verify the user's ZK proofs.
/// @param _verifier: Semaphore verifier addresse.
constructor(ISemaphoreVerifier _verifier) {
@@ -24,14 +28,24 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
}
/// @dev See {SemaphoreGroups-_createGroup}.
function createGroup(uint256 groupId, address admin) external override {
function createGroup() external override returns (uint256 groupId) {
groupId = groupCounter++;
_createGroup(groupId, msg.sender);
groups[groupId].merkleTreeDuration = 1 hours;
}
/// @dev See {SemaphoreGroups-_createGroup}.
function createGroup(address admin) external override returns (uint256 groupId) {
groupId = groupCounter++;
_createGroup(groupId, admin);
groups[groupId].merkleTreeDuration = 1 hours;
}
/// @dev See {ISemaphore-createGroup}.
function createGroup(uint256 groupId, address admin, uint256 merkleTreeDuration) external override {
function createGroup(address admin, uint256 merkleTreeDuration) external override returns (uint256 groupId) {
groupId = groupCounter++;
_createGroup(groupId, admin);
groups[groupId].merkleTreeDuration = merkleTreeDuration;
@@ -46,7 +60,7 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
function updateGroupMerkleTreeDuration(
uint256 groupId,
uint256 newMerkleTreeDuration
) external override onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
) external override onlyGroupAdmin(groupId) {
uint256 oldMerkleTreeDuration = groups[groupId].merkleTreeDuration;
groups[groupId].merkleTreeDuration = newMerkleTreeDuration;
@@ -56,18 +70,14 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
/// @dev See {SemaphoreGroups-_addMember}.
function addMember(uint256 groupId, uint256 identityCommitment) external override {
_addMember(groupId, identityCommitment);
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
uint256 merkleTreeRoot = _addMember(groupId, identityCommitment);
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
}
/// @dev See {SemaphoreGroups-_addMembers}.
function addMembers(uint256 groupId, uint256[] calldata identityCommitments) external override {
_addMembers(groupId, identityCommitments);
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
uint256 merkleTreeRoot = _addMembers(groupId, identityCommitments);
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
}
@@ -79,9 +89,7 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
uint256 newIdentityCommitment,
uint256[] calldata merkleProofSiblings
) external override {
_updateMember(groupId, identityCommitment, newIdentityCommitment, merkleProofSiblings);
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
uint256 merkleTreeRoot = _updateMember(groupId, identityCommitment, newIdentityCommitment, merkleProofSiblings);
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
}
@@ -92,18 +100,13 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
uint256 identityCommitment,
uint256[] calldata merkleProofSiblings
) external override {
_removeMember(groupId, identityCommitment, merkleProofSiblings);
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
uint256 merkleTreeRoot = _removeMember(groupId, identityCommitment, merkleProofSiblings);
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
}
/// @dev See {ISemaphore-validateProof}.
function validateProof(
uint256 groupId,
SemaphoreProof calldata proof
) external override onlyExistingGroup(groupId) {
function validateProof(uint256 groupId, SemaphoreProof calldata proof) external override {
// The function will revert if the nullifier that is part of the proof,
// was already used inside the group with id groupId.
if (groups[groupId].nullifiers[proof.nullifier]) {
@@ -136,7 +139,7 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
SemaphoreProof calldata proof
) public view override onlyExistingGroup(groupId) returns (bool) {
// The function will revert if the Merkle tree depth is not supported.
if (proof.merkleTreeDepth < 1 || proof.merkleTreeDepth > 12) {
if (proof.merkleTreeDepth < MIN_DEPTH || proof.merkleTreeDepth > MAX_DEPTH) {
revert Semaphore__MerkleTreeDepthIsNotSupported();
}

View File

@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/// @dev Minimum supported tree depth.
uint8 constant MIN_DEPTH = 1;
/// @dev Maximum supported tree depth.
uint8 constant MAX_DEPTH = 12;

View File

@@ -42,10 +42,6 @@ abstract contract SemaphoreGroups is ISemaphoreGroups {
/// @param groupId: Id of the group.
/// @param admin: Admin of the group.
function _createGroup(uint256 groupId, address admin) internal virtual {
if (admins[groupId] != address(0)) {
revert Semaphore__GroupAlreadyExists();
}
admins[groupId] = admin;
emit GroupCreated(groupId);
@@ -55,10 +51,7 @@ abstract contract SemaphoreGroups is ISemaphoreGroups {
/// @dev Updates the group admin.
/// @param groupId: Id of the group.
/// @param newAdmin: New admin of the group.
function _updateGroupAdmin(
uint256 groupId,
address newAdmin
) internal virtual onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
function _updateGroupAdmin(uint256 groupId, address newAdmin) internal virtual onlyGroupAdmin(groupId) {
admins[groupId] = newAdmin;
emit GroupAdminUpdated(groupId, msg.sender, newAdmin);
@@ -67,12 +60,13 @@ abstract contract SemaphoreGroups is ISemaphoreGroups {
/// @dev Adds an identity commitment to an existing group.
/// @param groupId: Id of the group.
/// @param identityCommitment: New identity commitment.
/// @return merkleTreeRoot New root hash of the tree.
function _addMember(
uint256 groupId,
uint256 identityCommitment
) internal virtual onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
) internal virtual onlyGroupAdmin(groupId) returns (uint256 merkleTreeRoot) {
uint256 index = getMerkleTreeSize(groupId);
uint256 merkleTreeRoot = merkleTrees[groupId]._insert(identityCommitment);
merkleTreeRoot = merkleTrees[groupId]._insert(identityCommitment);
emit MemberAdded(groupId, index, identityCommitment, merkleTreeRoot);
}
@@ -80,9 +74,13 @@ abstract contract SemaphoreGroups is ISemaphoreGroups {
/// @dev Adds new members to an existing group.
/// @param groupId: Id of the group.
/// @param identityCommitments: New identity commitments.
function _addMembers(uint256 groupId, uint256[] calldata identityCommitments) internal virtual {
/// @return merkleTreeRoot New root hash of the tree.
function _addMembers(
uint256 groupId,
uint256[] calldata identityCommitments
) internal virtual onlyGroupAdmin(groupId) returns (uint256 merkleTreeRoot) {
uint256 startIndex = getMerkleTreeSize(groupId);
uint256 merkleTreeRoot = merkleTrees[groupId]._insertMany(identityCommitments);
merkleTreeRoot = merkleTrees[groupId]._insertMany(identityCommitments);
emit MembersAdded(groupId, startIndex, identityCommitments, merkleTreeRoot);
}
@@ -93,14 +91,15 @@ abstract contract SemaphoreGroups is ISemaphoreGroups {
/// @param oldIdentityCommitment: Existing identity commitment to be updated.
/// @param newIdentityCommitment: New identity commitment.
/// @param merkleProofSiblings: Array of the sibling nodes of the proof of membership.
/// @return merkleTreeRoot New root hash of the tree.
function _updateMember(
uint256 groupId,
uint256 oldIdentityCommitment,
uint256 newIdentityCommitment,
uint256[] calldata merkleProofSiblings
) internal virtual onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
) internal virtual onlyGroupAdmin(groupId) returns (uint256 merkleTreeRoot) {
uint256 index = merkleTrees[groupId]._indexOf(oldIdentityCommitment);
uint256 merkleTreeRoot = merkleTrees[groupId]._update(
merkleTreeRoot = merkleTrees[groupId]._update(
oldIdentityCommitment,
newIdentityCommitment,
merkleProofSiblings
@@ -114,14 +113,15 @@ abstract contract SemaphoreGroups is ISemaphoreGroups {
/// @param groupId: Id of the group.
/// @param identityCommitment: Existing identity commitment to be removed.
/// @param merkleProofSiblings: Array of the sibling nodes of the proof of membership.
/// @return merkleTreeRoot New root hash of the tree.
function _removeMember(
uint256 groupId,
uint256 identityCommitment,
uint256[] calldata merkleProofSiblings
) internal virtual onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
) internal virtual onlyGroupAdmin(groupId) returns (uint256 merkleTreeRoot) {
uint256 index = merkleTrees[groupId]._indexOf(identityCommitment);
uint256 merkleTreeRoot = merkleTrees[groupId]._remove(identityCommitment, merkleProofSiblings);
merkleTreeRoot = merkleTrees[groupId]._remove(identityCommitment, merkleProofSiblings);
emit MemberRemoved(groupId, index, identityCommitment, merkleTreeRoot);
}

View File

@@ -3,6 +3,8 @@
pragma solidity 0.8.23;
import {MAX_DEPTH} from "./Constants.sol";
contract SemaphoreVerifier {
// Scalar field size
uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
@@ -22,12 +24,12 @@ contract SemaphoreVerifier {
uint256 constant gammay2 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
// TODO: Add more variables when Semaphore supports tree depth > 12.
// Right now Semaphore supports tree depth 1-12.
// Right now Semaphore supports tree depth 1-12. It will support up to 32.
// Verification Key points.
// These values are taken from the verification key json file generated with snarkjs.
// It allows to use the same verifier to verify proofs for all the tree depths supported by Semaphore.
uint256[14][12] VK_POINTS = [
uint256[14][MAX_DEPTH] VK_POINTS = [
[
563562783592406106461234396505774794044312891062077216951605541624542949349,
16293410697967515504861065986355060225819302510590370360517024529684437085892,

View File

@@ -56,13 +56,16 @@ interface ISemaphore {
);
/// @dev See {SemaphoreGroups-_createGroup}.
function createGroup(uint256 groupId, address admin) external;
function createGroup() external returns (uint256);
/// @dev See {SemaphoreGroups-_createGroup}.
function createGroup(address admin) external returns (uint256);
/// @dev It creates a group with a custom Merkle tree duration.
/// @param groupId: Id of the group.
/// @param admin: Admin of the group. It can be an Ethereum account or a smart contract.
/// @param merkleTreeDuration: Merkle tree duration.
function createGroup(uint256 groupId, address admin, uint256 merkleTreeDuration) external;
/// @return Id of the group.
function createGroup(address admin, uint256 merkleTreeDuration) external returns (uint256);
/// @dev See {SemaphoreGroups-_updateGroupAdmin}.
function updateGroupAdmin(uint256 groupId, address newAdmin) external;

View File

@@ -4,7 +4,6 @@ pragma solidity 0.8.23;
/// @title SemaphoreGroups contract interface.
interface ISemaphoreGroups {
error Semaphore__GroupDoesNotExist();
error Semaphore__GroupAlreadyExists();
error Semaphore__CallerIsNotTheGroupAdmin();
/// @dev Event emitted when a new group is created.

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/contracts",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "Semaphore contracts to manage groups and broadcast anonymous signals.",
"license": "MIT",
"files": [
@@ -30,6 +30,6 @@
"access": "public"
},
"dependencies": {
"@zk-kit/imt.sol": "2.0.0-beta.5"
"@zk-kit/imt.sol": "2.0.0-beta.8"
}
}

View File

@@ -32,13 +32,13 @@
"hardhat-gas-reporter": "^1.0.8",
"prettier-plugin-solidity": "^1.3.1",
"solhint": "^3.3.6",
"solhint-plugin-prettier": "^0.0.5",
"solhint-plugin-prettier": "^0.1.0",
"solidity-coverage": "^0.8.0",
"ts-node": "^10.9.2",
"typechain": "^8.3.0",
"typescript": "^5.3.3"
},
"dependencies": {
"@zk-kit/imt.sol": "^2.0.0-beta.5"
"@zk-kit/imt.sol": "2.0.0-beta.8"
}
}

View File

@@ -14,8 +14,8 @@ describe("Semaphore", () => {
const merkleTreeDepth = 12
const groupId = 1
const members = Array.from({ length: 3 }, (_, i) => new Identity(i)).map(({ commitment }) => commitment)
const groupId = 0
const members = Array.from({ length: 3 }, (_, i) => new Identity(i.toString())).map(({ commitment }) => commitment)
before(async () => {
const { semaphore } = await run("deploy", {
@@ -30,9 +30,8 @@ describe("Semaphore", () => {
describe("# createGroup", () => {
it("Should create a group", async () => {
const transaction = semaphoreContract
.connect(accounts[1])
["createGroup(uint256,address)"](groupId, accountAddresses[1])
const groupId = 0
const transaction = semaphoreContract.connect(accounts[1])["createGroup(address)"](accountAddresses[1])
await expect(transaction).to.emit(semaphoreContract, "GroupCreated").withArgs(groupId)
await expect(transaction)
@@ -41,9 +40,8 @@ describe("Semaphore", () => {
})
it("Should create a group with a custom Merkle tree root expiration", async () => {
const groupId = 2
const transaction = await semaphoreContract.connect(accounts[1])["createGroup(uint256,address,uint256)"](
groupId,
const groupId = 1
const transaction = await semaphoreContract.connect(accounts[1])["createGroup(address,uint256)"](
accountAddresses[0],
5 // 5 seconds.
)
@@ -56,6 +54,17 @@ describe("Semaphore", () => {
.to.emit(semaphoreContract, "GroupAdminUpdated")
.withArgs(groupId, ZeroAddress, accountAddresses[0])
})
it("Should create a group without any parameters", async () => {
const groupId = 2
const transaction = await semaphoreContract["createGroup()"]()
await expect(transaction).to.emit(semaphoreContract, "GroupCreated").withArgs(groupId)
await expect(transaction)
.to.emit(semaphoreContract, "GroupAdminUpdated")
.withArgs(groupId, ZeroAddress, accountAddresses[0])
})
})
describe("# updateGroupMerkleTreeDuration", () => {
@@ -122,6 +131,17 @@ describe("Semaphore", () => {
})
describe("# addMembers", () => {
it("Should not add members if the caller is not the group admin", async () => {
const members = [BigInt(1), BigInt(2), BigInt(3)]
const transaction = semaphoreContract.connect(accounts[1]).addMembers(groupId, members)
await expect(transaction).to.be.revertedWithCustomError(
semaphoreContract,
"Semaphore__CallerIsNotTheGroupAdmin"
)
})
it("Should add new members to an existing group", async () => {
const groupId = 3
const members = [BigInt(1), BigInt(2), BigInt(3)]
@@ -129,7 +149,7 @@ describe("Semaphore", () => {
group.addMembers(members)
await semaphoreContract["createGroup(uint256,address)"](groupId, accountAddresses[0])
await semaphoreContract["createGroup(address)"](accountAddresses[0])
const transaction = semaphoreContract.addMembers(groupId, members)
@@ -160,7 +180,7 @@ describe("Semaphore", () => {
group.updateMember(0, BigInt(4))
await semaphoreContract["createGroup(uint256,address)"](groupId, accountAddresses[0])
await semaphoreContract["createGroup(address)"](accountAddresses[0])
await semaphoreContract.addMembers(groupId, members)
const { siblings, root } = group.generateMerkleProof(0)
@@ -194,7 +214,7 @@ describe("Semaphore", () => {
group.removeMember(2)
await semaphoreContract["createGroup(uint256,address)"](groupId, accountAddresses[0])
await semaphoreContract["createGroup(address)"](accountAddresses[0])
await semaphoreContract.addMembers(groupId, members)
const { siblings, root } = group.generateMerkleProof(2)
@@ -220,7 +240,7 @@ describe("Semaphore", () => {
})
describe("# verifyProof", () => {
const groupId = 10
const groupId = 6
const message = 2
const identity = new Identity("0")
@@ -231,7 +251,7 @@ describe("Semaphore", () => {
let proof: SemaphoreProof
before(async () => {
await semaphoreContract["createGroup(uint256,address)"](groupId, accountAddresses[0])
await semaphoreContract["createGroup(address)"](accountAddresses[0])
await semaphoreContract.addMembers(groupId, members)
@@ -260,7 +280,7 @@ describe("Semaphore", () => {
})
it("Should not verify a proof if the Merkle tree root is expired", async () => {
const groupId = 2
const groupId = 1
const group = new Group()
@@ -275,12 +295,36 @@ describe("Semaphore", () => {
"Semaphore__MerkleTreeRootIsExpired"
)
})
it("Should not verify a proof if the Merkle depth is not supported", async () => {
const scope = "random-scope"
const proof = await generateProof(identity, group, message, scope, merkleTreeDepth)
proof.merkleTreeDepth = 33
const transaction = semaphoreContract.verifyProof(groupId, proof)
await expect(transaction).to.be.revertedWithCustomError(
semaphoreContract,
"Semaphore__MerkleTreeDepthIsNotSupported"
)
})
it("Should not verify a proof if the group has no members", async () => {
const groupId = 7
await semaphoreContract["createGroup(address)"](accountAddresses[0])
const transaction = semaphoreContract.verifyProof(groupId, proof)
await expect(transaction).to.be.revertedWithCustomError(semaphoreContract, "Semaphore__GroupHasNoMembers")
})
})
describe("# validateProof", () => {
const message = 2
const identity = new Identity("0")
const groupOneMemberId = 6
const groupOneMemberId = 7
const group = new Group()
const groupOneMember = new Group()
@@ -292,7 +336,7 @@ describe("Semaphore", () => {
let proofOneMember: SemaphoreProof
before(async () => {
await semaphoreContract["createGroup(uint256,address)"](groupOneMemberId, accountAddresses[0])
await semaphoreContract["createGroup(address)"](accountAddresses[0])
await semaphoreContract.addMembers(groupId, [members[1], members[2]])
await semaphoreContract.addMember(groupOneMemberId, members[0])
@@ -354,4 +398,38 @@ describe("Semaphore", () => {
)
})
})
describe("SemaphoreGroups", () => {
describe("# hasMember", () => {
it("Should return true because the memeber is part of the group", async () => {
const groupId = 1
const isMember = await semaphoreContract.hasMember(groupId, members[0])
await expect(isMember).to.be.true
})
it("Should return false because the memeber is not part of the group", async () => {
const groupId = 1
const identity = new Identity()
const isMember = await semaphoreContract.hasMember(groupId, identity.commitment)
await expect(isMember).to.be.false
})
})
describe("# indexOf", () => {
it("Should return the index of a member", async () => {
const groupId = 1
const index = await semaphoreContract.indexOf(groupId, members[0])
await expect(index).to.equal(0)
})
})
describe("# getMerkleTreeDepth", () => {
it("Should return the merkle tree depth", async () => {
const groupId = 1
const depth = await semaphoreContract.getMerkleTreeDepth(groupId)
await expect(depth).to.equal(2)
})
})
})
})

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/core",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "Core library for the essential Semaphore features.",
"type": "module",
"license": "MIT",
@@ -42,8 +42,8 @@
"access": "public"
},
"dependencies": {
"@semaphore-protocol/group": "4.0.0-beta.2",
"@semaphore-protocol/identity": "4.0.0-beta.2",
"@semaphore-protocol/proof": "4.0.0-beta.2"
"@semaphore-protocol/group": "4.0.0-beta.3",
"@semaphore-protocol/identity": "4.0.0-beta.3",
"@semaphore-protocol/proof": "4.0.0-beta.3"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/data",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "A library to query Semaphore contracts.",
"type": "module",
"license": "MIT",
@@ -31,8 +31,10 @@
},
"devDependencies": {
"@rollup/plugin-json": "^6.1.0",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-typescript2": "^0.36.0"
"@rollup/plugin-typescript": "^11.1.6",
"rimraf": "^5.0.5",
"rollup": "^4.12.0",
"rollup-plugin-cleanup": "^3.2.1"
},
"dependencies": {
"axios": "1.6.6",

View File

@@ -1,14 +1,14 @@
import json from "@rollup/plugin-json"
import typescript from "@rollup/plugin-typescript"
import * as fs from "fs"
import cleanup from "rollup-plugin-cleanup"
import typescript from "rollup-plugin-typescript2"
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
const banner = `/**
* @module ${pkg.name}
* @version ${pkg.version}
* @file ${pkg.description}
* @copyright Ethereum Foundation 2024
* @copyright Ethereum Foundation ${new Date().getFullYear()}
* @license ${pkg.license}
* @see [Github]{@link ${pkg.homepage}}
*/`
@@ -20,9 +20,5 @@ export default {
{ file: pkg.exports.default, format: "es", banner }
],
external: [...Object.keys(pkg.dependencies), "ethers/contract", "ethers/constants", "ethers/providers"],
plugins: [
json(),
typescript({ tsconfig: "./build.tsconfig.json", useTsconfigDeclarationDir: true }),
cleanup({ comments: "jsdoc" })
]
plugins: [json(), typescript({ tsconfig: "./build.tsconfig.json" }), cleanup({ comments: "jsdoc" })]
}

View File

@@ -1,9 +0,0 @@
import { SupportedNetwork } from "./types"
/**
* Returns the list of Semaphore supported networks.
* @returns Semaphore supported networks.
*/
export default function getSupportedNetworks(): string[] {
return Object.values(SupportedNetwork)
}

View File

@@ -1,6 +1,5 @@
import SemaphoreEthers from "./ethers"
import getSupportedNetworks from "./getSupportedNetworks"
import SemaphoreSubgraph from "./subgraph"
export * from "./types"
export { SemaphoreSubgraph, SemaphoreEthers, getSupportedNetworks }
export { SemaphoreSubgraph, SemaphoreEthers }

View File

@@ -35,11 +35,6 @@
"name": "Semaphore__CallerIsNotTheGroupAdmin",
"type": "error"
},
{
"inputs": [],
"name": "Semaphore__GroupAlreadyExists",
"type": "error"
},
{
"inputs": [],
"name": "Semaphore__GroupDoesNotExist",
@@ -360,11 +355,6 @@
},
{
"inputs": [
{
"internalType": "uint256",
"name": "groupId",
"type": "uint256"
},
{
"internalType": "address",
"name": "admin",
@@ -377,17 +367,18 @@
}
],
"name": "createGroup",
"outputs": [],
"outputs": [
{
"internalType": "uint256",
"name": "groupId",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "groupId",
"type": "uint256"
},
{
"internalType": "address",
"name": "admin",
@@ -395,7 +386,13 @@
}
],
"name": "createGroup",
"outputs": [],
"outputs": [
{
"internalType": "uint256",
"name": "groupId",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
@@ -475,6 +472,19 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "groupCounter",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{

View File

@@ -19,7 +19,7 @@ jest.mock("ethers/contract", () => ({
getMerkleTreeDepth: () => BigInt(3),
getMerkleTreeSize: () => BigInt(8),
getGroupAdmin: () => "0xA9C2B639a28cDa8b59C4377e980F75A93dD8605F"
} as any)
}) as any
)
}))

View File

@@ -1,12 +0,0 @@
import getSupportedNetworks from "../src/getSupportedNetworks"
describe("Data", () => {
describe("# getSupportedNetworks", () => {
it("Should return a list of supported networks", () => {
const supportedNetworks = getSupportedNetworks()
expect(supportedNetworks).toHaveLength(5)
expect(supportedNetworks).toContain("sepolia")
})
})
})

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/group",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "A library to create and manage Semaphore groups.",
"type": "module",
"license": "MIT",
@@ -32,9 +32,11 @@
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.6",
"poseidon-lite": "^0.2.0",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-typescript2": "^0.36.0"
"rimraf": "^5.0.5",
"rollup": "^4.12.0",
"rollup-plugin-cleanup": "^3.2.1"
},
"dependencies": {
"@zk-kit/imt": "2.0.0-beta.2"

View File

@@ -1,15 +1,15 @@
import commonjs from "@rollup/plugin-commonjs"
import { nodeResolve } from "@rollup/plugin-node-resolve"
import typescript from "@rollup/plugin-typescript"
import * as fs from "fs"
import cleanup from "rollup-plugin-cleanup"
import typescript from "rollup-plugin-typescript2"
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
const banner = `/**
* @module ${pkg.name}
* @version ${pkg.version}
* @file ${pkg.description}
* @copyright Ethereum Foundation 2024
* @copyright Ethereum Foundation ${new Date().getFullYear()}
* @license ${pkg.license}
* @see [Github]{@link ${pkg.homepage}}
*/`
@@ -23,8 +23,7 @@ export default {
external: Object.keys(pkg.dependencies),
plugins: [
typescript({
tsconfig: "./build.tsconfig.json",
useTsconfigDeclarationDir: true
tsconfig: "./build.tsconfig.json"
}),
commonjs(),
nodeResolve(),

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/hardhat",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "A Semaphore Hardhat plugin to deploy verifiers and Semaphore contract.",
"type": "module",
"license": "MIT",
@@ -30,16 +30,18 @@
"access": "public"
},
"devDependencies": {
"@rollup/plugin-typescript": "^11.1.6",
"hardhat": "^2.19.4",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-typescript2": "^0.36.0"
"rimraf": "^5.0.5",
"rollup": "^4.12.0",
"rollup-plugin-cleanup": "^3.2.1"
},
"peerDependencies": {
"hardhat": "^2.19.4"
},
"dependencies": {
"@nomicfoundation/hardhat-ethers": "^3.0.0",
"@semaphore-protocol/contracts": "4.0.0-beta.2",
"@semaphore-protocol/contracts": "4.0.0-beta.3",
"ethers": "^6.4.0",
"hardhat-dependency-compiler": "^1.1.3"
}

View File

@@ -1,4 +1,4 @@
import typescript from "rollup-plugin-typescript2"
import typescript from "@rollup/plugin-typescript"
import * as fs from "fs"
import cleanup from "rollup-plugin-cleanup"
@@ -7,7 +7,7 @@ const banner = `/**
* @module ${pkg.name}
* @version ${pkg.version}
* @file ${pkg.description}
* @copyright Ethereum Foundation 2024
* @copyright Ethereum Foundation ${new Date().getFullYear()}
* @license ${pkg.license}
* @see [Github]{@link ${pkg.homepage}}
*/`
@@ -21,8 +21,7 @@ export default {
external: [...Object.keys(pkg.dependencies), "hardhat/config"],
plugins: [
typescript({
tsconfig: "./build.tsconfig.json",
useTsconfigDeclarationDir: true
tsconfig: "./build.tsconfig.json"
}),
cleanup({ comments: "jsdoc" })
]

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/heyauthn",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "A library to allow developers to create and manage Semaphore identities using WebAuthn",
"type": "module",
"license": "MIT",
@@ -30,11 +30,13 @@
"access": "public"
},
"devDependencies": {
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-typescript2": "^0.36.0"
"@rollup/plugin-typescript": "^11.1.6",
"rimraf": "^5.0.5",
"rollup": "^4.12.0",
"rollup-plugin-cleanup": "^3.2.1"
},
"dependencies": {
"@semaphore-protocol/identity": "4.0.0-beta.2",
"@semaphore-protocol/identity": "4.0.0-beta.3",
"@simplewebauthn/browser": "7.2.0",
"@simplewebauthn/server": "7.2.0"
}

View File

@@ -1,4 +1,4 @@
import typescript from "rollup-plugin-typescript2"
import typescript from "@rollup/plugin-typescript"
import * as fs from "fs"
import cleanup from "rollup-plugin-cleanup"
@@ -7,7 +7,7 @@ const banner = `/**
* @module ${pkg.name}
* @version ${pkg.version}
* @file ${pkg.description}
* @copyright Vivek Bhupatiraju 2024
* @copyright Vivek Bhupatiraju ${new Date().getFullYear()}
* @license ${pkg.license}
* @see [Github]{@link ${pkg.homepage}}
*/`
@@ -21,8 +21,7 @@ export default {
external: Object.keys(pkg.dependencies),
plugins: [
typescript({
tsconfig: "./build.tsconfig.json",
useTsconfigDeclarationDir: true
tsconfig: "./build.tsconfig.json"
}),
cleanup({ comments: "jsdoc" })
]

View File

@@ -4,7 +4,7 @@ import {
GenerateRegistrationOptionsOpts as RegistrationOptions
} from "@simplewebauthn/server"
import { HeyAuthn } from "../src"
import HeyAuthn from "../src/hey-authn"
jest.mock("@simplewebauthn/browser", () => ({
startRegistration: async () => ({

View File

@@ -1,21 +1,15 @@
{
"name": "@semaphore-protocol/identity",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "A library to create Semaphore identities.",
"type": "module",
"license": "MIT",
"main": "dist/index.browser.js",
"main": "dist/index.js",
"types": "dist/types/index.d.ts",
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"node": {
"require": "./dist/index.node.cjs",
"default": "./dist/index.node.js"
},
"browser": "./dist/index.browser.js",
"default": "./dist/index.browser.js"
}
"types": "./dist/types/index.d.ts",
"require": "./dist/index.cjs",
"default": "./dist/index.js"
},
"files": [
"dist/",
@@ -29,9 +23,7 @@
"url": "https://github.com/semaphore-protocol/semaphore.git/issues"
},
"scripts": {
"build": "rimraf dist && yarn build:browser && yarn build:node",
"build:browser": "rollup -c rollup.browser.config.ts --configPlugin typescript",
"build:node": "rollup -c rollup.node.config.ts --configPlugin typescript",
"build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
"prepublishOnly": "yarn build"
},
"publishConfig": {
@@ -41,13 +33,15 @@
"@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-typescript2": "^0.36.0"
"@rollup/plugin-typescript": "^11.1.6",
"rimraf": "^5.0.5",
"rollup": "^4.12.0",
"rollup-plugin-cleanup": "^3.2.1"
},
"dependencies": {
"@zk-kit/baby-jubjub": "0.2.0",
"@zk-kit/eddsa-poseidon": "0.6.0",
"@zk-kit/utils": "0.3.0",
"@zk-kit/baby-jubjub": "0.3.0",
"@zk-kit/eddsa-poseidon": "0.10.0",
"@zk-kit/utils": "0.8.1",
"poseidon-lite": "0.2.0"
}
}

View File

@@ -1,42 +0,0 @@
import alias from "@rollup/plugin-alias"
import commonjs from "@rollup/plugin-commonjs"
import json from "@rollup/plugin-json"
import { nodeResolve } from "@rollup/plugin-node-resolve"
import * as fs from "fs"
import cleanup from "rollup-plugin-cleanup"
import typescript from "rollup-plugin-typescript2"
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
const banner = `/**
* @module ${pkg.name}
* @version ${pkg.version}
* @file ${pkg.description}
* @copyright Ethereum Foundation 2024
* @license ${pkg.license}
* @see [Github]{@link ${pkg.homepage}}
*/`
export default {
input: "src/index.ts",
output: [
{
file: pkg.exports["."].browser,
format: "es",
banner
}
],
external: pkg.dependencies,
plugins: [
alias({
entries: [{ find: "./random-number.node", replacement: "./random-number.browser" }]
}),
typescript({
tsconfig: "./build.tsconfig.json",
useTsconfigDeclarationDir: true
}),
commonjs(),
nodeResolve(),
cleanup({ comments: "jsdoc" }),
json()
]
}

View File

@@ -1,15 +1,15 @@
import commonjs from "@rollup/plugin-commonjs"
import { nodeResolve } from "@rollup/plugin-node-resolve"
import typescript from "@rollup/plugin-typescript"
import * as fs from "fs"
import cleanup from "rollup-plugin-cleanup"
import typescript from "rollup-plugin-typescript2"
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
const banner = `/**
* @module ${pkg.name}
* @version ${pkg.version}
* @file ${pkg.description}
* @copyright Ethereum Foundation 2024
* @copyright Ethereum Foundation ${new Date().getFullYear()}
* @license ${pkg.license}
* @see [Github]{@link ${pkg.homepage}}
*/`
@@ -17,17 +17,16 @@ const banner = `/**
export default {
input: "src/index.ts",
output: [
{ file: pkg.exports["."].node.require, format: "cjs", banner, exports: "auto" },
{ file: pkg.exports["."].node.default, format: "es", banner }
{ file: pkg.exports.require, format: "cjs", banner, exports: "auto" },
{ file: pkg.exports.default, format: "es", banner }
],
external: [...Object.keys(pkg.dependencies), "node:crypto"],
external: Object.keys(pkg.dependencies),
plugins: [
typescript({
tsconfig: "./build.tsconfig.json",
useTsconfigDeclarationDir: true
tsconfig: "./build.tsconfig.json"
}),
commonjs(),
nodeResolve(),
nodeResolve({ preferBuiltins: false }),
cleanup({ comments: "jsdoc" })
]
}

View File

@@ -1,9 +0,0 @@
export function bytesToBigint(bytes: Uint8Array): bigint {
let hex = "0x"
for (let i = 0; i < bytes.length; i += 1) {
hex += bytes[i].toString(16).padStart(2, "0")
}
return BigInt(hex)
}

View File

@@ -1,8 +1,10 @@
import type { Point } from "@zk-kit/baby-jubjub"
import { Signature, derivePublicKey, deriveSecretScalar, signMessage, verifySignature } from "@zk-kit/eddsa-poseidon"
import { EdDSAPoseidon, Signature, signMessage, verifySignature } from "@zk-kit/eddsa-poseidon"
import type { BigNumberish } from "@zk-kit/utils"
import { bufferToHexadecimal, hexadecimalToBuffer } from "@zk-kit/utils/conversions"
import { requireString } from "@zk-kit/utils/error-handlers"
import { isHexadecimal } from "@zk-kit/utils/type-checks"
import { poseidon2 } from "poseidon-lite/poseidon2"
import { randomNumber } from "./random-number.node"
/**
* The Semaphore identity is essentially an {@link https://www.rfc-editor.org/rfc/rfc8032 | EdDSA}
@@ -11,23 +13,26 @@ import { randomNumber } from "./random-number.node"
* and {@link https://www.poseidon-hash.info | Poseidon} for signatures.
* In addition, the commitment, i.e. the hash of the public key, is used to represent
* Semaphore identities in groups, adding an additional layer of privacy and security.
* The private key of the identity is stored as a hexadecimal string or text.
* The other attributes are stored as stringified bigint.
*/
export default class Identity {
// The EdDSA private key, passed as a parameter or generated randomly.
private _privateKey: BigNumberish
private _privateKey: string
// The secret scalar derived from the private key.
// It is used in circuits to derive the public key.
private _secretScalar: string
private _secretScalar: bigint
// The EdDSA public key, derived from the private key.
private _publicKey: Point<string>
private _publicKey: Point<bigint>
// The identity commitment used as a public value in Semaphore groups.
private _commitment: string
private _commitment: bigint
/**
* Initializes the class attributes based on a given private key.
* If the private key is not passed as a parameter, a random key is generated.
* The constructor calculates the secret scalar and public key from the private key,
* and computes a commitment of the public key using a hash function (Poseidon).
* Initializes the class attributes based on a given private key, which must be a hexadecimal string or a text.
* Hexadecimal strings must not start with '0x' or '0X'.
* If the private key is not passed as a parameter, a random hexadecimal key will be generated.
* The EdDSAPoseidon class is used to generate the secret scalar and the public key.
* Additionally, the constructor computes a commitment of the public key using a hash function (Poseidon).
*
* @example
* // Generates an identity.
@@ -36,20 +41,37 @@ export default class Identity {
* // Generates an identity with a random private key.
* const { privateKey, publicKey, commitment } = new Identity()
*
* @param privateKey The private key used to derive the public key.
* @param privateKey The private key used to derive the public key (hexadecimal or string).
*/
constructor(privateKey: BigNumberish = randomNumber().toString()) {
this._privateKey = privateKey
this._secretScalar = deriveSecretScalar(privateKey)
this._publicKey = derivePublicKey(privateKey)
this._commitment = poseidon2(this._publicKey).toString()
constructor(privateKey?: string) {
let eddsa: EdDSAPoseidon
if (privateKey) {
requireString(privateKey, "privateKey")
this._privateKey = privateKey
if (isHexadecimal(privateKey, false)) {
eddsa = new EdDSAPoseidon(hexadecimalToBuffer(privateKey))
} else {
eddsa = new EdDSAPoseidon(privateKey)
}
} else {
eddsa = new EdDSAPoseidon()
this._privateKey = bufferToHexadecimal(eddsa.privateKey as any)
}
this._secretScalar = eddsa.secretScalar
this._publicKey = eddsa.publicKey
this._commitment = poseidon2(this._publicKey)
}
/**
* Returns the private key.
* @returns The private key as a {@link https://zkkit.pse.dev/types/_zk_kit_utils.BigNumberish.html | BigNumberish}.
* @returns The private key as a string (hexadecimal or text).
*/
public get privateKey(): BigNumberish {
public get privateKey(): string {
return this._privateKey
}
@@ -57,7 +79,7 @@ export default class Identity {
* Returns the secret scalar.
* @returns The secret scalar as a string.
*/
public get secretScalar(): string {
public get secretScalar(): bigint {
return this._secretScalar
}
@@ -65,7 +87,7 @@ export default class Identity {
* Returns the public key as a Baby Jubjub {@link https://zkkit.pse.dev/types/_zk_kit_baby_jubjub.Point.html | Point}.
* @returns The public key as a point.
*/
public get publicKey(): Point<string> {
public get publicKey(): Point<bigint> {
return this._publicKey
}
@@ -73,7 +95,7 @@ export default class Identity {
* Returns the commitment hash of the public key.
* @returns The commitment as a string.
*/
public get commitment(): string {
public get commitment(): bigint {
return this._commitment
}
@@ -89,7 +111,7 @@ export default class Identity {
* @param message The message to be signed.
* @returns A {@link https://zkkit.pse.dev/types/_zk_kit_eddsa_poseidon.Signature.html | Signature} object containing the signature components.
*/
public signMessage(message: BigNumberish): Signature<string> {
public signMessage(message: BigNumberish): Signature<bigint> {
return signMessage(this.privateKey, message)
}

View File

@@ -1,8 +0,0 @@
/* istanbul ignore file */
import { bytesToBigint } from "./bytes-to-bigint"
export function randomNumber(): bigint {
const bytes = crypto.getRandomValues(new Uint8Array(32))
return bytesToBigint(bytes)
}

View File

@@ -1,9 +0,0 @@
import { randomBytes } from "node:crypto"
export function randomNumber(): bigint {
const bytes = randomBytes(32)
const hex = `0x${bytes.toString("hex")}`
return BigInt(hex)
}

View File

@@ -1,60 +1,65 @@
import { randomBytes } from "crypto"
import { Identity } from "../src"
import { bytesToBigint } from "../src/bytes-to-bigint"
describe("Identity", () => {
const privateKey = "secret"
const privateKeyText = "secret"
const privateKeyHexadecimal = "dd998334940df8931b76d899fdb189415f7ff4280599f03a7574725a166aad7d"
describe("# Identity", () => {
it("Should create a random identity", () => {
it("Should create an identity with a random secret (private key)", () => {
const identity = new Identity()
expect(typeof identity.privateKey).toBe("string")
expect(typeof identity.secretScalar).toBe("string")
expect(identity.privateKey).toHaveLength(64)
expect(typeof identity.secretScalar).toBe("bigint")
expect(identity.publicKey).toHaveLength(2)
expect(typeof identity.commitment).toBe("string")
expect(typeof identity.commitment).toBe("bigint")
})
it("Should create deterministic identities from a secret (private key)", () => {
const identity = new Identity(privateKey)
it("Should create deterministic identities from a secret text (private key)", () => {
const identity = new Identity(privateKeyText)
expect(typeof identity.privateKey).toBe("string")
expect(typeof identity.secretScalar).toBe("string")
expect(typeof identity.secretScalar).toBe("bigint")
expect(identity.publicKey).toHaveLength(2)
expect(typeof identity.commitment).toBe("string")
expect(typeof identity.commitment).toBe("bigint")
})
it("Should create deterministic identities from a secret hexadecimal (private key)", () => {
const identity = new Identity(privateKeyHexadecimal)
expect(typeof identity.privateKey).toBe("string")
expect(identity.privateKey).toHaveLength(64)
expect(typeof identity.secretScalar).toBe("bigint")
expect(identity.publicKey).toHaveLength(2)
expect(typeof identity.commitment).toBe("bigint")
})
it("Should throw an error if the private key is not a string", () => {
const fun = () => new Identity(32 as any)
expect(fun).toThrow("Parameter 'privateKey' is not a string, received type: number")
})
})
describe("# signMessage", () => {
it("Should sign a message", () => {
const identity = new Identity(privateKey)
const identity = new Identity(privateKeyText)
const signature = identity.signMessage("message")
expect(signature.R8).toHaveLength(2)
expect(typeof signature.R8[0]).toBe("string")
expect(typeof signature.S).toBe("string")
expect(typeof signature.R8[0]).toBe("bigint")
expect(typeof signature.S).toBe("bigint")
})
})
describe("# verifySignature", () => {
it("Should verify a signature", () => {
const identity = new Identity(privateKey)
const identity = new Identity(privateKeyText)
const signature = identity.signMessage("message")
expect(Identity.verifySignature("message", signature, identity.publicKey)).toBeTruthy()
})
})
describe("# bytesToBigint", () => {
it("Should convert 32 bytes to bigint", () => {
const bytes = randomBytes(32)
const integer = bytesToBigint(bytes)
expect(typeof integer).toBe("bigint")
expect(integer).toBe(BigInt(`0x${bytes.toString("hex")}`))
})
})
})

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/proof",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "A library to generate and verify Semaphore proofs.",
"type": "module",
"license": "MIT",
@@ -40,23 +40,24 @@
"devDependencies": {
"@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-json": "^6.1.0",
"@types/download": "^8.0.5",
"@types/snarkjs": "0.7.8",
"@types/tmp": "^0.2.6",
"@rollup/plugin-typescript": "^11.1.6",
"@types/snarkjs": "^0",
"poseidon-lite": "^0.2.0",
"rimraf": "^5.0.5",
"rollup": "^4.9.6",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-typescript2": "^0.36.0"
"rollup": "^4.12.0",
"rollup-plugin-cleanup": "^3.2.1"
},
"peerDependencies": {
"@semaphore-protocol/group": "4.0.0-beta.2",
"@semaphore-protocol/identity": "4.0.0-beta.2"
"@semaphore-protocol/group": "4.0.0-beta.3",
"@semaphore-protocol/identity": "4.0.0-beta.3"
},
"dependencies": {
"@semaphore-protocol/utils": "4.0.0-beta.2",
"@zk-kit/utils": "0.3.0",
"@semaphore-protocol/utils": "4.0.0-beta.3",
"@zk-kit/utils": "0.8.1",
"ethers": "6.10.0",
"snarkjs": "0.7.3"
},
"resolutions": {
"web-worker": "1.2.0"
}
}

View File

@@ -1,15 +1,15 @@
import alias from "@rollup/plugin-alias"
import json from "@rollup/plugin-json"
import typescript from "@rollup/plugin-typescript"
import * as fs from "fs"
import cleanup from "rollup-plugin-cleanup"
import typescript from "rollup-plugin-typescript2"
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
const banner = `/**
* @module ${pkg.name}
* @version ${pkg.version}
* @file ${pkg.description}
* @copyright Ethereum Foundation 2024
* @copyright Ethereum Foundation ${new Date().getFullYear()}
* @license ${pkg.license}
* @see [Github]{@link ${pkg.homepage}}
*/`
@@ -34,15 +34,15 @@ export default {
"ethers/crypto",
"ethers/utils",
"ethers/abi",
"@semaphore-protocol/utils/errors"
"@zk-kit/utils/error-handlers",
"@zk-kit/utils/proof-packing"
],
plugins: [
alias({
entries: [{ find: "./get-snark-artifacts.node", replacement: "./get-snark-artifacts.browser" }]
}),
typescript({
tsconfig: "./build.tsconfig.json",
useTsconfigDeclarationDir: true
tsconfig: "./build.tsconfig.json"
}),
cleanup({ comments: "jsdoc" }),
json()

View File

@@ -1,14 +1,14 @@
import json from "@rollup/plugin-json"
import typescript from "@rollup/plugin-typescript"
import * as fs from "fs"
import cleanup from "rollup-plugin-cleanup"
import typescript from "rollup-plugin-typescript2"
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
const banner = `/**
* @module ${pkg.name}
* @version ${pkg.version}
* @file ${pkg.description}
* @copyright Ethereum Foundation 2024
* @copyright Ethereum Foundation ${new Date().getFullYear()}
* @license ${pkg.license}
* @see [Github]{@link ${pkg.homepage}}
*/`
@@ -39,12 +39,12 @@ export default {
"ethers/crypto",
"ethers/utils",
"ethers/abi",
"@semaphore-protocol/utils/errors"
"@zk-kit/utils/error-handlers",
"@zk-kit/utils/proof-packing"
],
plugins: [
typescript({
tsconfig: "./build.tsconfig.json",
useTsconfigDeclarationDir: true
tsconfig: "./build.tsconfig.json"
}),
cleanup({ comments: "jsdoc" }),
json()

View File

@@ -1,12 +1,14 @@
import type { Group, MerkleProof } from "@semaphore-protocol/group"
import type { Identity } from "@semaphore-protocol/identity"
import { requireDefined, requireNumber, requireObject, requireTypes } from "@semaphore-protocol/utils/errors"
import { MAX_DEPTH, MIN_DEPTH } from "@semaphore-protocol/utils/constants"
import { requireDefined, requireNumber, requireObject, requireTypes } from "@zk-kit/utils/error-handlers"
import { packGroth16Proof } from "@zk-kit/utils/proof-packing"
import type { BigNumberish } from "ethers"
import { NumericString, groth16 } from "snarkjs"
import { packGroth16Proof } from "@zk-kit/utils"
import getSnarkArtifacts from "./get-snark-artifacts.node"
import hash from "./hash"
import toBigInt from "./to-bigint"
import { BigNumberish, SemaphoreProof, SnarkArtifacts } from "./types"
import { SemaphoreProof, SnarkArtifacts } from "./types"
/**
* It generates a Semaphore proof, i.e. a zero-knowledge proof that an identity that
@@ -43,8 +45,8 @@ export default async function generateProof(
requireObject(identity, "identity")
requireObject(groupOrMerkleProof, "groupOrMerkleProof")
requireTypes(message, "message", ["string", "bigint", "number", "uint8array"])
requireTypes(scope, "scope", ["string", "bigint", "number", "uint8array"])
requireTypes(message, "message", ["string", "bigint", "number", "Uint8Array"])
requireTypes(scope, "scope", ["string", "bigint", "number", "Uint8Array"])
if (merkleTreeDepth) {
requireNumber(merkleTreeDepth, "merkleTreeDepth")
@@ -73,8 +75,8 @@ export default async function generateProof(
const merkleProofLength = merkleProof.siblings.length
if (merkleTreeDepth !== undefined) {
if (merkleTreeDepth < 1 || merkleTreeDepth > 12) {
throw new TypeError("The tree depth must be a number between 1 and 12")
if (merkleTreeDepth < MIN_DEPTH || merkleTreeDepth > MAX_DEPTH) {
throw new TypeError(`The tree depth must be a number between ${MIN_DEPTH} and ${MAX_DEPTH}`)
}
} else {
merkleTreeDepth = merkleProofLength

View File

@@ -1,5 +1,5 @@
/* istanbul ignore file */
import { requireNumber } from "@semaphore-protocol/utils/errors"
import { requireNumber } from "@zk-kit/utils/error-handlers"
import { SnarkArtifacts } from "./types"
/**

View File

@@ -1,5 +1,5 @@
/* istanbul ignore file */
import { requireNumber } from "@semaphore-protocol/utils/errors"
import { requireNumber } from "@zk-kit/utils/error-handlers"
import { createWriteStream, existsSync, readdirSync } from "node:fs"
import { mkdir } from "node:fs/promises"
import os from "node:os"

View File

@@ -1,7 +1,7 @@
import type { BigNumberish } from "ethers"
import { keccak256 } from "ethers/crypto"
import { toBeHex } from "ethers/utils"
import { NumericString } from "snarkjs"
import { BigNumberish } from "./types"
/**
* Creates a keccak256 hash of a message compatible with the SNARK scalar modulus.

View File

@@ -1,4 +1,4 @@
import { packGroth16Proof, unpackGroth16Proof } from "@zk-kit/utils"
import { packGroth16Proof, unpackGroth16Proof } from "@zk-kit/utils/proof-packing"
import generateProof from "./generate-proof"
import verifyProof from "./verify-proof"

View File

@@ -1,9 +1,9 @@
import type { BigNumberish } from "ethers"
import { encodeBytes32String } from "ethers/abi"
import { toBigInt as _toBigInt } from "ethers/utils"
import { BigNumberish } from "./types"
/**
* Converts a bignumberish to a bigint.
* Converts a bignumberish or a text to a bigint.
* @param value The value to be converted to bigint.
* @return The value converted to bigint.
*/

View File

@@ -1,6 +1,5 @@
import type { NumericString } from "snarkjs"
export type BigNumberish = string | number | bigint
import type { PackedGroth16Proof } from "@zk-kit/utils"
export type SnarkArtifacts = {
wasmFilePath: string
@@ -13,16 +12,5 @@ export type SemaphoreProof = {
message: NumericString
nullifier: NumericString
scope: NumericString
points: PackedPoints
points: PackedGroth16Proof
}
export type PackedPoints = [
NumericString,
NumericString,
NumericString,
NumericString,
NumericString,
NumericString,
NumericString,
NumericString
]

View File

@@ -1,12 +1,7 @@
import {
requireArray,
requireDefined,
requireNumber,
requireObject,
requireString
} from "@semaphore-protocol/utils/errors"
import { MAX_DEPTH, MIN_DEPTH } from "@semaphore-protocol/utils/constants"
import { requireArray, requireDefined, requireNumber, requireObject, requireString } from "@zk-kit/utils/error-handlers"
import { unpackGroth16Proof } from "@zk-kit/utils/proof-packing"
import { groth16 } from "snarkjs"
import { unpackGroth16Proof } from "@zk-kit/utils"
import hash from "./hash"
import { SemaphoreProof } from "./types"
import verificationKeys from "./verification-keys.json"
@@ -30,9 +25,8 @@ export default async function verifyProof(proof: SemaphoreProof): Promise<boolea
requireString(scope, "proof.scope")
requireArray(points, "proof.points")
// TODO: support all tree depths after trusted-setup.
if (merkleTreeDepth < 1 || merkleTreeDepth > 12) {
throw new TypeError("The tree depth must be a number between 1 and 12")
if (merkleTreeDepth < MIN_DEPTH || merkleTreeDepth > MAX_DEPTH) {
throw new TypeError(`The tree depth must be a number between ${MIN_DEPTH} and ${MAX_DEPTH}`)
}
const verificationKey = {

View File

@@ -1,7 +1,9 @@
import { Group } from "@semaphore-protocol/group"
import { Identity } from "@semaphore-protocol/identity"
import { getCurveFromName } from "ffjavascript"
import { SemaphoreProof, generateProof, verifyProof } from "../src"
import generateProof from "../src/generate-proof"
import { SemaphoreProof } from "../src/types"
import verifyProof from "../src/verify-proof"
describe("Proof", () => {
const treeDepth = 10
@@ -9,7 +11,7 @@ describe("Proof", () => {
const message = "Hello world"
const scope = "Scope"
const identity = new Identity(42)
const identity = new Identity("secret")
let proof: SemaphoreProof
let curve: any

View File

@@ -70,10 +70,10 @@ yarn add @semaphore-protocol/utils
For more information on the functions and modules provided by `@semaphore-protocol/utils`, please refer to the [TypeDoc documentation](https://js.semaphore.pse.dev/modules/_semaphore_protocol_utils).
```typescript
// You can import modules from the main bundle.
import { errors, types } from "@semaphore-protocol/utils"
// You can import functions/parameters from the main bundle.
import { supportedNetworks, decodeMessage } from "@semaphore-protocol/utils"
// Or by using conditional exports.
import { requireNumber } from "@semaphore-protocol/utils/errors"
import { isNumber } from "@semaphore-protocol/utils/types"
import supportedNetworks from "@semaphore-protocol/utils/supported-networks"
import decodeMessage from "@semaphore-protocol/utils/decode-message"
```

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/utils",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"description": "A library to provide utility functions to the other Semaphore packages.",
"type": "module",
"license": "MIT",
@@ -12,15 +12,20 @@
"require": "./dist/index.cjs",
"default": "./dist/index.js"
},
"./errors": {
"types": "./dist/types/errors.d.ts",
"require": "./dist/lib.commonjs/errors.cjs",
"default": "./dist/lib.esm/errors.js"
"./supported-networks": {
"types": "./dist/types/supported-networks.d.ts",
"require": "./dist/lib.commonjs/supported-networks.cjs",
"default": "./dist/lib.esm/supported-networks.js"
},
"./types": {
"types": "./dist/types/types.d.ts",
"require": "./dist/lib.commonjs/types.cjs",
"default": "./dist/lib.esm/types.js"
"./constants": {
"types": "./dist/types/constants.d.ts",
"require": "./dist/lib.commonjs/constants.cjs",
"default": "./dist/lib.esm/constants.js"
},
"./decode-message": {
"types": "./dist/types/decode-message.d.ts",
"require": "./dist/lib.commonjs/decode-message.cjs",
"default": "./dist/lib.esm/decode-message.js"
}
},
"files": [
@@ -42,9 +47,12 @@
"access": "public"
},
"devDependencies": {
"@rollup/plugin-typescript": "^11.1.6",
"rimraf": "^5.0.5",
"rollup": "^4.9.6",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-typescript2": "^0.36.0"
"rollup": "^4.12.0",
"rollup-plugin-cleanup": "^3.2.1"
},
"dependencies": {
"ethers": "^6.11.1"
}
}

View File

@@ -1,36 +1,50 @@
import typescript from "@rollup/plugin-typescript"
import * as fs from "fs"
import cleanup from "rollup-plugin-cleanup"
import typescript from "rollup-plugin-typescript2"
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
const banner = `/**
* @module ${pkg.name}
* @version ${pkg.version}
* @file ${pkg.description}
* @copyright Ethereum Foundation 2024
* @copyright Ethereum Foundation ${new Date().getFullYear()}
* @license ${pkg.license}
* @see [Github]{@link ${pkg.homepage}}
*/`
export default {
input: "src/index.ts",
output: [
{ file: pkg.exports["."].require, format: "cjs", banner, exports: "auto" },
{ file: pkg.exports["."].default, format: "es", banner },
{
dir: "./dist/lib.commonjs",
format: "cjs",
banner,
preserveModules: true,
entryFileNames: "[name].cjs"
},
{ dir: "./dist/lib.esm", format: "es", banner, preserveModules: true }
],
plugins: [
typescript({
tsconfig: "./build.tsconfig.json",
useTsconfigDeclarationDir: true
}),
cleanup({ comments: "jsdoc" })
]
}
export default [
{
input: "src/index.ts",
output: [
{ file: pkg.exports["."].require, format: "cjs", banner, exports: "auto" },
{ file: pkg.exports["."].default, format: "es", banner }
],
plugins: [
typescript({
tsconfig: "./build.tsconfig.json"
}),
cleanup({ comments: "jsdoc" })
]
},
{
input: "src/index.ts",
output: [
{
dir: "./dist/lib.commonjs",
format: "cjs",
banner,
preserveModules: true,
entryFileNames: "[name].cjs"
},
{ dir: "./dist/lib.esm", format: "es", banner, preserveModules: true }
],
plugins: [
typescript({
tsconfig: "./build.tsconfig.json",
declaration: false,
declarationDir: undefined
}),
cleanup({ comments: "jsdoc" })
]
}
]

View File

@@ -0,0 +1,5 @@
// TODO: Support all tree depths after trusted-setup. MAX_DEPTH would be 32.
export const MIN_DEPTH = 1
export const MAX_DEPTH = 12

View File

@@ -0,0 +1,17 @@
import type { BigNumberish } from "ethers"
import { decodeBytes32String } from "ethers/abi"
import { toBeHex } from "ethers/utils"
/**
* Typically used for decoding on-chain Semaphore messages.
* When Semaphore messages are text they are converted to bigints before
* the proof is generated (and eventually sent on-chain).
* This function help devs converting bigint messages to text again.
* If the original message was not text the output of this
* function won't probably be human-readable text.
* @param message The Semaphore message as a bigint.
* @returns The Semaphore message as a text.
*/
export default function decodeMessage(message: BigNumberish) {
return decodeBytes32String(toBeHex(message, 32))
}

View File

@@ -1,132 +0,0 @@
/**
* @module Errors
* This module is designed to provide utility functions for validating
* function parameters. It includes functions that throw type errors if
* the parameters do not meet specified criteria, such as being defined,
* a number, a string, a function, or an array. This module helps ensure
* that functions receive the correct types of inputs, enhancing code
* reliability and reducing runtime errors.
*/
import {
SupportedType,
isArray,
isBigInt,
isDefined,
isFunction,
isNumber,
isObject,
isString,
isSupportedType,
isType,
isUint8Array
} from "./types"
/**
* It throws a type error if the parameter value has not been defined.
* @param parameterValue The parameter value.
* @param parameterName The parameter name.
*/
export function requireDefined(parameterValue: any, parameterName: string) {
if (!isDefined(parameterValue)) {
throw new TypeError(`Parameter '${parameterName}' is not defined`)
}
}
/**
* It throws a type error if the parameter value is not a number.
* @param parameterValue The parameter value.
* @param parameterName The parameter name.
*/
export function requireNumber(parameterValue: number, parameterName: string) {
if (!isNumber(parameterValue)) {
throw new TypeError(`Parameter '${parameterName}' is not a number`)
}
}
/**
* It throws a type error if the parameter value is not a string.
* @param parameterValue The parameter value.
* @param parameterName The parameter name.
*/
export function requireString(parameterValue: string, parameterName: string) {
if (!isString(parameterValue)) {
throw new TypeError(`Parameter '${parameterName}' is not a string`)
}
}
/**
* It throws a type error if the parameter value is not a function.
* @param parameterValue The parameter value.
* @param parameterName The parameter name.
*/
export function requireFunction(parameterValue: Function, parameterName: string) {
if (!isFunction(parameterValue)) {
throw new TypeError(`Parameter '${parameterName}' is not a function`)
}
}
/**
* It throws a type error if the parameter value is not an array.
* @param parameterValue The parameter value.
* @param parameterName The parameter name.
*/
export function requireArray(parameterValue: any[], parameterName: string) {
if (!isArray(parameterValue)) {
throw new TypeError(`Parameter '${parameterName}' is not an array`)
}
}
/**
* It throws a type error if the parameter value is not a uint8array.
* @param parameterValue The parameter value.
* @param parameterName The parameter name.
*/
export function requireUint8Array(parameterValue: Uint8Array, parameterName: string) {
if (!isUint8Array(parameterValue)) {
throw new TypeError(`Parameter '${parameterName}' is not a Uint8Array`)
}
}
/**
* It throws a type error if the parameter value is not an object.
* @param parameterValue The parameter value.
* @param parameterName The parameter name.
*/
export function requireObject(parameterValue: object, parameterName: string) {
if (!isObject(parameterValue)) {
throw new TypeError(`Parameter '${parameterName}' is not an object`)
}
}
/**
* It throws a type error if the parameter value is not a bigint.
* @param parameterValue The parameter value.
* @param parameterName The parameter name.
*/
export function requireBigInt(parameterValue: bigint, parameterName: string) {
if (!isBigInt(parameterValue)) {
throw new TypeError(`Parameter '${parameterName}' is not a bigint`)
}
}
/**
* It throws a type error if the parameter value type is not part of the list of types.
* @param parameterValue The parameter value.
* @param parameterName The parameter name.
*/
export function requireTypes(parameterValue: any, parameterName: string, types: SupportedType[]) {
for (const type of types) {
if (!isSupportedType(type)) {
throw new Error(`Type '${type}' is not supported`)
}
}
for (const type of types) {
if (isType(parameterValue, type)) {
return
}
}
throw new TypeError(`Parameter '${parameterName}' is none of the following types: ${types.join(", ")}`)
}

View File

@@ -1,4 +1,6 @@
import * as errors from "./errors"
import * as types from "./types"
import * as constants from "./constants"
import decodeMessage from "./decode-message"
import supportedNetworks from "./supported-networks"
export { errors, types }
export * from "./types"
export { constants, decodeMessage, supportedNetworks }

View File

@@ -0,0 +1,6 @@
import { SupportedNetwork } from "./types"
// List of Semaphore supported networks.
const supportedNetwork: SupportedNetwork[] = ["sepolia", "mumbai", "optimism-sepolia", "arbitrum-sepolia", "arbitrum"]
export default supportedNetwork

View File

@@ -1,123 +0,0 @@
/**
* @module Types
* This module provides utility functions to check data types.
* It defines a set of supported types and includes functions to check if
* a value is defined and if it matches a supported type. These functions
* are useful for type checking and validation in the other libraries,
* enhancing code robustness and reliability.
*/
// The list of types supported by this utility functions.
const supportedTypes = ["number", "string", "function", "array", "uint8array", "object", "bigint"] as const
// Type extracted from the list above.
export type SupportedType = (typeof supportedTypes)[number]
/**
* It returns true if the value is defined, false otherwise.
* @param value The value to be checked.
* @returns True or false.
*/
export function isDefined(value: any): boolean {
return typeof value !== "undefined"
}
/**
* It returns true if the value is a number, false otherwise.
* @param value The value to be checked.
* @returns True or false.
*/
export function isNumber(value: any): boolean {
return typeof value === "number"
}
/**
* It returns true if the value is a string, false otherwise.
* @param value The value to be checked.
* @returns True or false.
*/
export function isString(value: any): boolean {
return typeof value === "string"
}
/**
* It returns true if the value is a function, false otherwise.
* @param value The value to be checked.
* @returns True or false.
*/
export function isFunction(value: any): boolean {
return typeof value === "function"
}
/**
* It returns true if the value is an array, false otherwise.
* @param value The value to be checked.
* @returns True or false.
*/
export function isArray(value: any): boolean {
return typeof value === "object" && Array.isArray(value)
}
/**
* It returns true if the value is a uint8array, false otherwise.
* @param value The value to be checked.
* @returns True or false.
*/
export function isUint8Array(value: any): boolean {
return value instanceof Uint8Array
}
/**
* It returns true if the value is an object, false otherwise.
* @param value The value to be checked.
* @returns True or false.
*/
export function isObject(value: any): boolean {
return typeof value === "object"
}
/**
* It returns true if the value is a bigint, false otherwise.
* @param value The value to be checked.
* @returns True or false.
*/
export function isBigInt(value: any): boolean {
return typeof value === "bigint"
}
/**
* It returns true if the value type is the same as the type passed
* as the second parameter, false otherwise.
* @param type The expected type.
* @returns True or false.
*/
export function isType(value: any, type: SupportedType): boolean {
switch (type) {
case "number":
return isNumber(value)
case "string":
return isString(value)
case "function":
return isFunction(value)
case "array":
return isArray(value)
case "uint8array":
return isUint8Array(value)
case "object":
return isObject(value)
case "bigint":
return isBigInt(value)
default:
return false
}
}
/**
* Return true if the type is being supported by this utility
* functions, false otherwise.
* @param type The type to be checked.
* @returns True or false
*/
export function isSupportedType(type: string): type is SupportedType {
return (supportedTypes as readonly string[]).includes(type)
}

View File

@@ -0,0 +1 @@
export type SupportedNetwork = "sepolia" | "mumbai" | "optimism-sepolia" | "arbitrum-sepolia" | "arbitrum"

View File

@@ -1,206 +1,24 @@
import { errors, types } from "../src"
import { encodeBytes32String } from "ethers/abi"
import { toBigInt } from "ethers/utils"
import { supportedNetworks } from "../src"
import decodeMessage from "../src/decode-message"
describe("Utils", () => {
describe("# types", () => {
it("Should return true if the value is a number", () => {
expect(types.isNumber(1)).toBeTruthy()
})
it("Should return false if the value is not a number", () => {
expect(types.isNumber("string")).toBeFalsy()
})
it("Should return true if the value is a string", () => {
expect(types.isString("string")).toBeTruthy()
})
it("Should return false if the value is not a string", () => {
expect(types.isString(1)).toBeFalsy()
})
it("Should return true if the value is a function", () => {
expect(types.isFunction(() => true)).toBeTruthy()
})
it("Should return false if the value is not a function", () => {
expect(types.isFunction(1)).toBeFalsy()
})
it("Should return true if the value is an array", () => {
expect(types.isArray([])).toBeTruthy()
})
it("Should return false if the value is not an array", () => {
expect(types.isArray(1)).toBeFalsy()
})
it("Should return true if the value is a uint8array", () => {
expect(types.isUint8Array(new Uint8Array([]))).toBeTruthy()
})
it("Should return false if the value is not a uint8array", () => {
expect(types.isUint8Array(1)).toBeFalsy()
})
it("Should return true if the value is an object", () => {
expect(types.isObject({})).toBeTruthy()
})
it("Should return false if the value is not an object", () => {
expect(types.isObject(1)).toBeFalsy()
})
it("Should return true if the value is a bigint", () => {
expect(types.isBigInt(BigInt(1))).toBeTruthy()
})
it("Should return false if the value is not a bigint", () => {
expect(types.isBigInt(1)).toBeFalsy()
})
it("Should return true if the value type is the one expected", () => {
expect(types.isType(1, "number")).toBeTruthy()
expect(types.isType("string", "string")).toBeTruthy()
expect(types.isType(() => true, "function")).toBeTruthy()
expect(types.isType([], "array")).toBeTruthy()
expect(types.isType(new Uint8Array([]), "uint8array")).toBeTruthy()
expect(types.isType({}, "object")).toBeTruthy()
expect(types.isType(BigInt(1), "bigint")).toBeTruthy()
})
it("Should return false if the value type is not the one expected or is not supported", () => {
expect(types.isType("string", "number")).toBeFalsy()
expect(types.isType(1, "string")).toBeFalsy()
expect(types.isType(1, "function")).toBeFalsy()
expect(types.isType(1, "array")).toBeFalsy()
expect(types.isType(1, "uint8array")).toBeFalsy()
expect(types.isType(1, "object")).toBeFalsy()
expect(types.isType(1, "bigint")).toBeFalsy()
expect(types.isType(1, "type" as any)).toBeFalsy()
})
it("Should return true if the type is supported", () => {
expect(types.isSupportedType("number")).toBeTruthy()
})
it("Should return false if the type is not supported", () => {
expect(types.isSupportedType("type")).toBeFalsy()
describe("# supportedNetworks", () => {
it("Should be a list of networks supported by Semaphore", () => {
expect(supportedNetworks).toBeInstanceOf(Array)
expect(typeof supportedNetworks[0]).toBe("string")
})
})
describe("# errors", () => {
it("Should throw an error if the parameter is not defined", () => {
const fun = () => errors.requireDefined(undefined as any, "parameter")
describe("# decodeMessage", () => {
it("Should decode a text message previously encoded to 32-byte bigint", () => {
const message = "Hello World"
const encodedMessage = toBigInt(encodeBytes32String(message))
expect(fun).toThrow("Parameter 'parameter' is not defined")
})
const decodedMessage = decodeMessage(encodedMessage)
it("Should not throw an error if the parameter is defined", () => {
const fun = () => errors.requireDefined(1, "parameter")
expect(fun).not.toThrow()
})
it("Should throw an error if the parameter is not a number", () => {
const fun = () => errors.requireNumber("euo" as any, "parameter")
expect(fun).toThrow("Parameter 'parameter' is not a number")
})
it("Should not throw an error if the parameter is a number", () => {
const fun = () => errors.requireNumber(1, "parameter")
expect(fun).not.toThrow()
})
it("Should throw an error if the parameter is not a string", () => {
const fun = () => errors.requireString(1 as any, "parameter")
expect(fun).toThrow("Parameter 'parameter' is not a string")
})
it("Should not throw an error if the parameter is a string", () => {
const fun = () => errors.requireString("string", "parameter")
expect(fun).not.toThrow()
})
it("Should throw an error if the parameter is not an array", () => {
const fun = () => errors.requireArray(1 as any, "parameter")
expect(fun).toThrow("Parameter 'parameter' is not an array")
})
it("Should not throw an error if the parameter is an array", () => {
const fun = () => errors.requireArray([], "parameter")
expect(fun).not.toThrow()
})
it("Should throw an error if the parameter is not a uint8array", () => {
const fun = () => errors.requireUint8Array([] as any, "parameter")
expect(fun).toThrow("Parameter 'parameter' is not a Uint8Array")
})
it("Should not throw an error if the parameter is a uint8array", () => {
const fun = () => errors.requireUint8Array(new Uint8Array([]), "parameter")
expect(fun).not.toThrow()
})
it("Should throw an error if the parameter is not a function", () => {
const fun = () => errors.requireFunction(1 as any, "parameter")
expect(fun).toThrow("Parameter 'parameter' is not a function")
})
it("Should not throw an error if the parameter is a function", () => {
const fun = () => errors.requireFunction(() => true, "parameter")
expect(fun).not.toThrow()
})
it("Should throw an error if the parameter is not an object", () => {
const fun = () => errors.requireObject(1 as any, "parameter")
expect(fun).toThrow("Parameter 'parameter' is not an object")
})
it("Should not throw an error if the parameter is an object", () => {
const fun = () => errors.requireObject({}, "parameter")
expect(fun).not.toThrow()
})
it("Should throw an error if the parameter is not a bigint", () => {
const fun = () => errors.requireBigInt(1 as any, "parameter")
expect(fun).toThrow("Parameter 'parameter' is not a bigint")
})
it("Should not throw an error if the parameter is a bigint", () => {
const fun = () => errors.requireBigInt(BigInt(1), "parameter")
expect(fun).not.toThrow()
})
it("Should throw an error if the parameter is neither a function nor a number", () => {
const fun = () => errors.requireTypes("string", "parameter", ["function", "number"])
expect(fun).toThrow("Parameter 'parameter' is none of the following types: function, number")
})
it("Should not throw an error if the parameter is either a string or an array", () => {
const fun = () => errors.requireTypes("string", "parameter", ["string", "array"])
expect(fun).not.toThrow()
})
it("Should throw an error if the parameter types are not supported", () => {
const fun = () => errors.requireTypes("string", "parameter", ["string", "type" as any])
expect(fun).toThrow("Type 'type' is not supported")
expect(decodedMessage).toBe(message)
})
})
})

View File

@@ -1 +1 @@
2b45175ad550878da123feccb41268083e5bc7e5
bd481ecd18c519e117c14a9078d3844222780cba