mirror of
https://github.com/semaphore-protocol/semaphore.git
synced 2026-01-14 17:18:03 -05:00
Compare commits
50 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c7afa888f | ||
|
|
96cb583861 | ||
|
|
b603dbaf81 | ||
|
|
4fc4675678 | ||
|
|
6f0fdddae5 | ||
|
|
31b05dba43 | ||
|
|
94bdb44a45 | ||
|
|
2bc10b0d5e | ||
|
|
7ab9898beb | ||
|
|
9e5f8351cc | ||
|
|
b6e6a8db03 | ||
|
|
9eebaf4967 | ||
|
|
9e5c464237 | ||
|
|
2ea5d5c1cf | ||
|
|
e9e24b04d1 | ||
|
|
2c5b0641c7 | ||
|
|
9e6d386748 | ||
|
|
546d9f9dd6 | ||
|
|
38b14a515c | ||
|
|
193be0e1d5 | ||
|
|
dfb5473414 | ||
|
|
25847c4422 | ||
|
|
7b8a35470a | ||
|
|
ee26734d94 | ||
|
|
b50cddad2e | ||
|
|
4578fc63de | ||
|
|
eb1f3ad5e2 | ||
|
|
6049a27308 | ||
|
|
7943fcd665 | ||
|
|
66be942cb2 | ||
|
|
b93ad8e14d | ||
|
|
7c69037745 | ||
|
|
fcb3634e07 | ||
|
|
b5825c52e5 | ||
|
|
8795302788 | ||
|
|
3c2db6f724 | ||
|
|
fb79707e3e | ||
|
|
c5378b6914 | ||
|
|
8709b373e8 | ||
|
|
3c993ab150 | ||
|
|
541007d4a5 | ||
|
|
f3971efb55 | ||
|
|
c8db909122 | ||
|
|
ab0366aa07 | ||
|
|
d4bda547ff | ||
|
|
d1f5fb331a | ||
|
|
14fe011ad7 | ||
|
|
2897754358 | ||
|
|
4a0ea1201c | ||
|
|
634c2b11e6 |
20
.commitlintrc.js
Normal file
20
.commitlintrc.js
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": ["@commitlint/config-conventional"]
|
||||
}
|
||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -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}}
|
||||
|
||||
6
.husky/prepare-commit-msg
Normal file
6
.husky/prepare-commit-msg
Normal 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
|
||||
@@ -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**:
|
||||
|
||||
|
||||
16
README.md
16
README.md
@@ -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.
|
||||
|
||||
@@ -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(() => [])
|
||||
|
||||
@@ -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
42
package.json
42
package.json
@@ -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": {
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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" })
|
||||
]
|
||||
|
||||
@@ -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",
|
||||
|
||||
6
packages/contracts/.prettierrc.json
Normal file
6
packages/contracts/.prettierrc.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"semi": false,
|
||||
"arrowParens": "always",
|
||||
"trailingComma": "none",
|
||||
"plugins": ["prettier-plugin-solidity"]
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
8
packages/contracts/contracts/base/Constants.sol
Normal file
8
packages/contracts/contracts/base/Constants.sol
Normal 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;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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" })]
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ jest.mock("ethers/contract", () => ({
|
||||
getMerkleTreeDepth: () => BigInt(3),
|
||||
getMerkleTreeSize: () => BigInt(8),
|
||||
getGroupAdmin: () => "0xA9C2B639a28cDa8b59C4377e980F75A93dD8605F"
|
||||
} as any)
|
||||
}) as any
|
||||
)
|
||||
}))
|
||||
|
||||
|
||||
@@ -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")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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"
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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" })
|
||||
]
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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" })
|
||||
]
|
||||
|
||||
@@ -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 () => ({
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
]
|
||||
}
|
||||
@@ -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" })
|
||||
]
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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")}`))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
]
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" })
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
5
packages/utils/src/constants.ts
Normal file
5
packages/utils/src/constants.ts
Normal 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
|
||||
17
packages/utils/src/decode-message.ts
Normal file
17
packages/utils/src/decode-message.ts
Normal 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))
|
||||
}
|
||||
@@ -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(", ")}`)
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
6
packages/utils/src/supported-networks.ts
Normal file
6
packages/utils/src/supported-networks.ts
Normal 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
|
||||
@@ -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)
|
||||
}
|
||||
1
packages/utils/src/types/index.ts
Normal file
1
packages/utils/src/types/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type SupportedNetwork = "sepolia" | "mumbai" | "optimism-sepolia" | "arbitrum-sepolia" | "arbitrum"
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1 +1 @@
|
||||
2b45175ad550878da123feccb41268083e5bc7e5
|
||||
bd481ecd18c519e117c14a9078d3844222780cba
|
||||
Reference in New Issue
Block a user