Compare commits

..

49 Commits

Author SHA1 Message Date
cedoor
12fd0f7a80 chore: v3.4.0
Former-commit-id: 254218b1e5
2023-03-21 19:41:10 +00:00
cedoor
78da99055d feat(cli): add support for arbitrum goerli
Former-commit-id: 7c4a2a1022
2023-03-21 19:40:40 +00:00
cedoor
77e4770b53 docs: add heyauthn docs link
Former-commit-id: ff692f1507
2023-03-21 19:35:50 +00:00
cedoor
799afc82f4 docs: add heyauthn package to readme
Former-commit-id: 79103bbbb0
2023-03-21 19:31:49 +00:00
cedoor
46fe2fc8f8 chore: v3.3.0
Former-commit-id: af89f3a0a1
2023-03-21 19:28:09 +00:00
cedoor
7bb9554388 style(heyauthn): format code with prettier
Former-commit-id: 587e3045c8
2023-03-21 19:22:22 +00:00
Cedoor
d88a71ec4d Merge pull request #285 from semaphore-protocol/feat/heyauthn
New HeyAuthn package

Former-commit-id: b497be753e
2023-03-21 19:17:11 +00:00
cedoor
2afc4ce1de style(cli): format code
Former-commit-id: 22ee1e0109
2023-03-21 11:36:42 +00:00
Cedoor
64f7b24c53 Merge pull request #281 from semaphore-protocol/feat/cli-sepolia-network
Add support for Sepolia network in the CLI

Former-commit-id: 889fe89162
2023-03-21 11:19:44 +00:00
Cedoor
88ba0af2d2 Merge pull request #282 from semaphore-protocol/chore/arbitrum-goerli
Semaphore support for Arbitrum Goerli network

Former-commit-id: 0a6061c9c5
2023-03-21 11:05:31 +00:00
vplasencia
c2e8ba6856 fix(cli): add admin in get-group when using SemaphoreEthers
Former-commit-id: 4641735932
2023-03-21 01:15:07 +01:00
cedoor
f9a8d68641 docs(heyauthn): add authors to readme file
re #284


Former-commit-id: 53637009b1
2023-03-20 16:37:55 +00:00
cedoor
df84100c22 feat(heyauthn): create heyauthn package
re #284


Former-commit-id: f67c1f07d7
2023-03-20 16:29:26 +00:00
cedoor
b962339203 feat(data): add support for arbitrum goerli network
re #275


Former-commit-id: 0571bbcdaa
2023-03-20 12:42:52 +00:00
Cedoor
073f5a5772 Merge pull request #283 from semaphore-protocol/feat/cli-template-hardhat
Add new version of cli-template-hardhat

Former-commit-id: e44ca7d447
2023-03-20 12:18:09 +00:00
vplasencia
b26f74a453 feat(cli-template-hardhat): add new version of cli-template-hardhat
Former-commit-id: 4133ae12c3
2023-03-20 12:36:12 +01:00
vplasencia
e9cac671f2 refactor(cli): remove unnecessary code in get-group command
Former-commit-id: 716a20cb7c
2023-03-17 23:11:40 +01:00
vplasencia
ecf8dcafb1 refactor(cli): remove unused code
Former-commit-id: 4ecf293cd4
2023-03-17 22:59:41 +01:00
vplasencia
f90c99193a refactor(cli): organize get-groups command code
Former-commit-id: eefffca9f4
2023-03-17 22:55:40 +01:00
vplasencia
a826708320 feat(cli): update get-group command to use sepolia
Former-commit-id: 4ddf75b378
2023-03-17 22:41:11 +01:00
cedoor
43370202a7 style(contracts): format code with prettier
Former-commit-id: ef393e9c03
2023-03-17 18:07:53 +00:00
cedoor
a4d1180d26 chore(contracts): deploy semaphore on arbitrum goerli network
re #275


Former-commit-id: e34ce9c61a
2023-03-17 18:01:40 +00:00
vplasencia
5c42f9e09c feat(cli): add support for sepolia network in the cli
Former-commit-id: 63666f6a9c
2023-03-17 14:27:58 +01:00
Cedoor
c8362e373b Merge pull request #280 from dbrans/patch-1
Comment typo in Pairing.sol

Former-commit-id: 032702b245
2023-03-17 10:53:27 +00:00
Derek Brans
06765f2f88 Fix typo in Pairing.sol
Former-commit-id: 0810ffbe0f
2023-03-16 19:15:42 -05:00
Cedoor
f26c84445e Merge pull request #279 from semaphore-protocol/chore/default-network
New default network (Sepolia)

Former-commit-id: 6c644522f0
2023-03-16 10:42:46 +00:00
cedoor
61a2d6adc2 test(data): update tests for semaphore ethers class
re #277


Former-commit-id: 8988f478c9
2023-03-15 19:04:23 +00:00
cedoor
eddd6b3dd5 chore(data): change default network
re #277


Former-commit-id: 49221b6ba7
2023-03-15 18:56:49 +00:00
cedoor
fa561f8f00 chore(cli): update command description
Former-commit-id: a4f9e47957
2023-03-15 18:55:34 +00:00
cedoor
c1842eefc3 chore: v3.2.3
Former-commit-id: 59d2d1367c
2023-03-13 22:52:08 +00:00
Cedoor
320187ff89 Merge pull request #276 from semaphore-protocol/fix/bundle-poseidon
JS bundles with Poseidon functions

Former-commit-id: e135b1d8fa
2023-03-13 22:51:20 +00:00
cedoor
abbf1a1d30 chore: update lockfile
Former-commit-id: 982ee86208
2023-03-13 22:41:37 +00:00
cedoor
59269b067d build: bundle poseidon-lite functions with rollup
Former-commit-id: 797e62dd28
2023-03-13 22:32:23 +00:00
cedoor
21bd9fe540 ci: update auto-assign workflow name
Former-commit-id: 6a3079bf52
2023-03-13 19:35:41 +00:00
Cedoor
f60a4c02f1 Merge pull request #274 from semaphore-protocol/chore/husky
New Husky script to check commit messages' format

Former-commit-id: e00dc8eca8
2023-03-13 19:30:46 +00:00
cedoor
d012310ae1 chore: add husky script to check commit message format
re #263


Former-commit-id: 77552b0da3
2023-03-13 19:26:40 +00:00
Cedoor
f01cb91472 chore: update github token
Former-commit-id: d33d306478
2023-03-13 19:25:17 +00:00
cedoor
31e6954aff style: format code with prettier
Former-commit-id: 0137d6e759
2023-03-13 18:36:01 +00:00
Cedoor
7518d938d1 Merge pull request #273 from semaphore-protocol/chore/auto-assign-action
New Github workflow to assign PRs

Former-commit-id: f777603fba
2023-03-13 18:31:43 +00:00
Cedoor
083d8a03a4 chore: update team name
Former-commit-id: 07244fc6bc
2023-03-13 18:30:56 +00:00
Cedoor
16c9e90ae4 chore: set org devs as reviewers
Former-commit-id: b9db0663ec
2023-03-13 18:18:50 +00:00
Cedoor
1fe2745594 chore: add github workflow to auto assign PRs
Former-commit-id: 0c6df193e4
2023-03-13 16:57:52 +00:00
cedoor
18d4b740ca chore: v3.2.2
Former-commit-id: 5ea3b7e6ef
2023-03-13 11:52:02 +00:00
Cedoor
8392173370 Merge pull request #272 from semaphore-protocol/fix/data-ethers
Correct check for group existence

Former-commit-id: 46c2ad2c7a
2023-03-13 11:50:59 +00:00
cedoor
26490304e4 fix(data): set correct check for group existence
Former-commit-id: 7b938935ca
2023-03-13 11:40:23 +00:00
cedoor
1878ce2320 chore: v3.2.1
Former-commit-id: 5cc6877d15
2023-03-11 21:41:08 +00:00
cedoor
98aed04bd9 chore(contracts): include Semaphore.sol contract in npm files
Former-commit-id: d227ee1907
2023-03-11 21:40:38 +00:00
cedoor
5a0585cec2 chore: add refactor type to changelog
Former-commit-id: 4f7af880a9
2023-03-10 13:09:10 +00:00
cedoor
1a51263e5c chore: v3.2.0
Former-commit-id: 33d814fe6d
2023-03-10 11:19:52 +00:00
41 changed files with 842 additions and 271 deletions

14
.github/workflows/auto-assign.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
name: auto-assign
on:
pull_request:
types: [opened]
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: wow-actions/auto-assign@v3
with:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
reviewers: org/core-devs

4
.husky/commit-msg Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no-install commitlint --edit $1

View File

@@ -217,6 +217,28 @@ The core of the Semaphore protocol is in the [circuit logic](/packages/circuits/
</a>
</td>
</tr>
<tr>
<td>
<a href="/packages/heyauthn">
@semaphore-protocol/heyauthn
</a>
<a href="https://semaphore-protocol.github.io/semaphore/heyauthn">
(docs)
</a>
</td>
<td>
<!-- NPM version -->
<a href="https://npmjs.org/package/@semaphore-protocol/heyauthn">
<img src="https://img.shields.io/npm/v/@semaphore-protocol/heyauthn.svg?style=flat-square" alt="NPM version" />
</a>
</td>
<td>
<!-- Downloads -->
<a href="https://npmjs.org/package/@semaphore-protocol/heyauthn">
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/heyauthn.svg?style=flat-square" alt="Downloads" />
</a>
</td>
</tr>
<tbody>
</table>

View File

@@ -0,0 +1,7 @@
{
"types": {
"feat": { "title": "🚀 Features" },
"fix": { "title": "🐞 Bug Fixes" },
"refactor": { "title": "♻️ Refactoring" }
}
}

View File

@@ -22,7 +22,7 @@
"version:release": "changelogithub",
"commit": "cz",
"precommit": "lint-staged",
"postinstall": "yarn download:snark-artifacts"
"postinstall": "yarn download:snark-artifacts && husky install"
},
"keywords": [
"ethereum",
@@ -67,6 +67,7 @@
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jest": "^25.7.0",
"husky": "^8.0.3",
"jest": "^27.4.1",
"jest-config": "^27.4.7",
"lint-staged": "^12.1.7",

View File

@@ -1,4 +1,4 @@
ETHEREUM_URL=
INFURA_API_KEY=
ETHEREUM_PRIVATE_KEY=
REPORT_GAS=false
COINMARKETCAP_API_KEY=

View File

@@ -0,0 +1,30 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
contract Feedback {
ISemaphore public semaphore;
uint256 public groupId;
constructor(address semaphoreAddress, uint256 _groupId) {
semaphore = ISemaphore(semaphoreAddress);
groupId = _groupId;
semaphore.createGroup(groupId, 20, address(this));
}
function joinGroup(uint256 identityCommitment) external {
semaphore.addMember(groupId, identityCommitment);
}
function sendFeedback(
uint256 feedback,
uint256 merkleTreeRoot,
uint256 nullifierHash,
uint256[8] calldata proof
) external {
semaphore.verifyProof(groupId, merkleTreeRoot, feedback, nullifierHash, groupId, proof);
}
}

View File

@@ -1,42 +0,0 @@
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.4;
import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
/// @title Greeter contract.
/// @dev The following code is just a example to show how Semaphore can be used.
contract Greeter {
event NewGreeting(bytes32 greeting);
event NewUser(uint256 identityCommitment, bytes32 username);
ISemaphore public semaphore;
uint256 groupId;
mapping(uint256 => bytes32) users;
constructor(address semaphoreAddress, uint256 _groupId) {
semaphore = ISemaphore(semaphoreAddress);
groupId = _groupId;
semaphore.createGroup(groupId, 20, address(this));
}
function joinGroup(uint256 identityCommitment, bytes32 username) external {
semaphore.addMember(groupId, identityCommitment);
users[identityCommitment] = username;
emit NewUser(identityCommitment, username);
}
function greet(
bytes32 greeting,
uint256 merkleTreeRoot,
uint256 nullifierHash,
uint256[8] calldata proof
) external {
semaphore.verifyProof(groupId, merkleTreeRoot, uint256(greeting), nullifierHash, groupId, proof);
emit NewGreeting(greeting);
}
}

View File

@@ -1,50 +1,63 @@
import "@nomicfoundation/hardhat-toolbox"
import "@nomiclabs/hardhat-ethers"
import "@nomicfoundation/hardhat-chai-matchers"
import "@semaphore-protocol/hardhat"
import "@typechain/hardhat"
import { config as dotenvConfig } from "dotenv"
import "hardhat-gas-reporter"
import { HardhatUserConfig } from "hardhat/config"
import { NetworksUserConfig } from "hardhat/types"
import { resolve } from "path"
import "solidity-coverage"
import { config } from "./package.json"
import "./tasks/deploy"
dotenvConfig()
dotenvConfig({ path: resolve(__dirname, "../../.env") })
function getNetworks(): NetworksUserConfig {
if (process.env.ETHEREUM_URL && process.env.ETHEREUM_PRIVATE_KEY) {
const accounts = [`0x${process.env.ETHEREUM_PRIVATE_KEY}`]
return {
goerli: {
url: process.env.ETHEREUM_URL,
chainId: 5,
accounts
},
sepolia: {
url: process.env.ETHEREUM_URL,
chainId: 11155111,
accounts
},
mumbai: {
url: process.env.ETHEREUM_URL,
chainId: 80001,
accounts
},
"optimism-goerli": {
url: process.env.ETHEREUM_URL,
chainId: 420,
accounts
},
arbitrum: {
url: process.env.ETHEREUM_URL,
chainId: 42161,
accounts
}
}
if (!process.env.INFURA_API_KEY || !process.env.ETHEREUM_PRIVATE_KEY) {
return {}
}
return {}
const accounts = [`0x${process.env.ETHEREUM_PRIVATE_KEY}`]
const infuraApiKey = process.env.INFURA_API_KEY
return {
goerli: {
url: `https://goerli.infura.io/v3/${infuraApiKey}`,
chainId: 5,
accounts
},
sepolia: {
url: `https://sepolia.infura.io/v3/${infuraApiKey}`,
chainId: 11155111,
accounts
},
mumbai: {
url: `https://polygon-mumbai.infura.io/v3/${infuraApiKey}`,
chainId: 80001,
accounts
},
"optimism-goerli": {
url: `https://optimism-goerli.infura.io/v3/${infuraApiKey}`,
chainId: 420,
accounts
},
arbitrum: {
url: `https://arbitrum-mainnet.infura.io/v3/${infuraApiKey}`,
chainId: 42161,
accounts
}
}
}
const config: HardhatUserConfig = {
solidity: "0.8.4",
const hardhatConfig: HardhatUserConfig = {
solidity: config.solidity,
paths: {
sources: config.paths.contracts,
tests: config.paths.tests,
cache: config.paths.cache,
artifacts: config.paths.build.contracts
},
networks: {
hardhat: {
chainId: 1337
@@ -55,7 +68,11 @@ const config: HardhatUserConfig = {
currency: "USD",
enabled: process.env.REPORT_GAS === "true",
coinmarketcap: process.env.COINMARKETCAP_API_KEY
},
typechain: {
outDir: config.paths.build.typechain,
target: "ethers-v5"
}
}
export default config
export default hardhatConfig

View File

@@ -1,12 +1,13 @@
{
"name": "@semaphore-protocol/cli-template-hardhat",
"version": "3.2.0",
"version": "3.4.0",
"description": "Semaphore Hardhat template.",
"license": "Unlicense",
"files": [
".gitignore",
".env.example",
"contracts/",
"scripts/",
"tasks/",
"test/",
"hardhat.config.ts",
@@ -18,12 +19,14 @@
"access": "public"
},
"scripts": {
"start": "hardhat node",
"dev": "hardhat node & yarn compile && yarn deploy --network localhost",
"compile": "hardhat compile",
"deploy": "hardhat deploy",
"test": "hardhat test",
"download:snark-artifacts": "hardhat run scripts/download-snark-artifacts.ts",
"deploy": "yarn compile && hardhat deploy",
"test": "hardhat run scripts/download-snark-artifacts.ts && hardhat test",
"test:report-gas": "REPORT_GAS=true hardhat test",
"test:coverage": "hardhat coverage"
"test:coverage": "hardhat coverage",
"typechain": "hardhat typechain"
},
"devDependencies": {
"@ethersproject/abi": "^5.4.7",
@@ -33,10 +36,10 @@
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
"@nomiclabs/hardhat-ethers": "^2.0.0",
"@nomiclabs/hardhat-etherscan": "^3.0.0",
"@semaphore-protocol/group": "3.2.0",
"@semaphore-protocol/hardhat": "3.2.0",
"@semaphore-protocol/identity": "3.2.0",
"@semaphore-protocol/proof": "3.2.0",
"@semaphore-protocol/group": "3.4.0",
"@semaphore-protocol/hardhat": "3.4.0",
"@semaphore-protocol/identity": "3.4.0",
"@semaphore-protocol/proof": "3.4.0",
"@typechain/ethers-v5": "^10.1.0",
"@typechain/hardhat": "^6.1.2",
"@types/chai": "^4.2.0",
@@ -55,6 +58,21 @@
"typescript": ">=4.5.0"
},
"dependencies": {
"@semaphore-protocol/contracts": "3.2.0"
"@semaphore-protocol/contracts": "3.4.0"
},
"config": {
"solidity": {
"version": "0.8.4"
},
"paths": {
"contracts": "./contracts",
"tests": "./test",
"cache": "./cache",
"build": {
"snark-artifacts": "./build/snark-artifacts",
"contracts": "./build/contracts",
"typechain": "./build/typechain"
}
}
}
}

View File

@@ -0,0 +1,24 @@
import download from "download"
import fs from "fs"
import { config } from "../package.json"
async function main() {
const snarkArtifactsPath = config.paths.build["snark-artifacts"]
const url = `http://www.trusted-setup-pse.org/semaphore/${20}`
if (!fs.existsSync(snarkArtifactsPath)) {
fs.mkdirSync(snarkArtifactsPath, { recursive: true })
}
if (!fs.existsSync(`${snarkArtifactsPath}/semaphore.zkey`)) {
await download(`${url}/semaphore.wasm`, snarkArtifactsPath)
await download(`${url}/semaphore.zkey`, snarkArtifactsPath)
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
})

View File

@@ -1,6 +1,6 @@
import { task, types } from "hardhat/config"
task("deploy", "Deploy a Greeter contract")
task("deploy", "Deploy a Feedback contract")
.addOptionalParam("semaphore", "Semaphore contract address", undefined, types.string)
.addOptionalParam("group", "Group id", "42", types.string)
.addOptionalParam("logs", "Print the logs", true, types.boolean)
@@ -13,15 +13,19 @@ task("deploy", "Deploy a Greeter contract")
semaphoreAddress = semaphore.address
}
const Greeter = await ethers.getContractFactory("Greeter")
const greeter = await Greeter.deploy(semaphoreAddress, groupId)
await greeter.deployed()
if (logs) {
console.info(`Greeter contract has been deployed to: ${greeter.address}`)
if (!groupId) {
groupId = process.env.GROUP_ID
}
return greeter
const FeedbackFactory = await ethers.getContractFactory("Feedback")
const feedbackContract = await FeedbackFactory.deploy(semaphoreAddress, groupId)
await feedbackContract.deployed()
if (logs) {
console.info(`Feedback contract has been deployed to: ${feedbackContract.address}`)
}
return feedbackContract
})

View File

@@ -0,0 +1,69 @@
import { Group } from "@semaphore-protocol/group"
import { Identity } from "@semaphore-protocol/identity"
import { generateProof } from "@semaphore-protocol/proof"
import { expect } from "chai"
import { formatBytes32String } from "ethers/lib/utils"
import { run } from "hardhat"
// @ts-ignore: typechain folder will be generated after contracts compilation
import { Feedback } from "../build/typechain"
import { config } from "../package.json"
describe("Feedback", () => {
let feedbackContract: Feedback
let semaphoreContract: string
const groupId = "42"
const group = new Group(groupId)
const users: Identity[] = []
before(async () => {
const { semaphore } = await run("deploy:semaphore", {
logs: false
})
feedbackContract = await run("deploy", { logs: false, group: groupId, semaphore: semaphore.address })
semaphoreContract = semaphore
users.push(new Identity())
users.push(new Identity())
})
describe("# joinGroup", () => {
it("Should allow users to join the group", async () => {
for await (const [i, user] of users.entries()) {
const transaction = feedbackContract.joinGroup(user.commitment)
group.addMember(user.commitment)
await expect(transaction)
.to.emit(semaphoreContract, "MemberAdded")
.withArgs(groupId, i, user.commitment, group.root)
}
})
})
describe("# sendFeedback", () => {
const wasmFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.wasm`
const zkeyFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.zkey`
it("Should allow users to send feedback anonymously", async () => {
const feedback = formatBytes32String("Hello World")
const fullProof = await generateProof(users[1], group, groupId, feedback, {
wasmFilePath,
zkeyFilePath
})
const transaction = feedbackContract.sendFeedback(
feedback,
fullProof.merkleTreeRoot,
fullProof.nullifierHash,
fullProof.proof
)
await expect(transaction)
.to.emit(semaphoreContract, "ProofVerified")
.withArgs(groupId, fullProof.merkleTreeRoot, fullProof.nullifierHash, groupId, fullProof.signal)
})
})
})

View File

@@ -1,72 +0,0 @@
import { Group } from "@semaphore-protocol/group"
import { Identity } from "@semaphore-protocol/identity"
import { generateProof } from "@semaphore-protocol/proof"
import { expect } from "chai"
import download from "download"
import { existsSync } from "fs"
import { ethers, run } from "hardhat"
// @ts-ignore: typechain-types folder will be generated after contracts compilation
import { Greeter } from "../typechain-types"
describe("Greeter", () => {
let greeter: Greeter
const snarkArtifactsURL = "https://www.trusted-setup-pse.org/semaphore/20"
const snarkArtifactsPath = "./artifacts/snark"
const users: any[] = []
const groupId = "42"
const group = new Group(groupId)
before(async () => {
if (!existsSync(`${snarkArtifactsPath}/semaphore.wasm`)) {
await download(`${snarkArtifactsURL}/semaphore.wasm`, `${snarkArtifactsPath}`)
await download(`${snarkArtifactsURL}/semaphore.zkey`, `${snarkArtifactsPath}`)
}
greeter = await run("deploy", { logs: false, group: groupId })
users.push({
identity: new Identity(),
username: ethers.utils.formatBytes32String("anon1")
})
users.push({
identity: new Identity(),
username: ethers.utils.formatBytes32String("anon2")
})
group.addMember(users[0].identity.commitment)
group.addMember(users[1].identity.commitment)
})
describe("# joinGroup", () => {
it("Should allow users to join the group", async () => {
for (let i = 0; i < group.members.length; i += 1) {
const transaction = greeter.joinGroup(group.members[i], users[i].username)
await expect(transaction).to.emit(greeter, "NewUser").withArgs(group.members[i], users[i].username)
}
})
})
describe("# greet", () => {
it("Should allow users to greet", async () => {
const greeting = ethers.utils.formatBytes32String("Hello World")
const fullProof = await generateProof(users[1].identity, group, groupId, greeting, {
wasmFilePath: `${snarkArtifactsPath}/semaphore.wasm`,
zkeyFilePath: `${snarkArtifactsPath}/semaphore.zkey`
})
const transaction = greeter.greet(
greeting,
fullProof.merkleTreeRoot,
fullProof.nullifierHash,
fullProof.proof
)
await expect(transaction).to.emit(greeter, "NewGreeting").withArgs(greeting)
})
})
})

View File

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

View File

@@ -1,7 +1,7 @@
{
"name": "@semaphore-protocol/cli",
"type": "module",
"version": "3.2.0",
"version": "3.4.0",
"description": "A command line tool to set up your Semaphore project and get group data.",
"license": "MIT",
"bin": {
@@ -41,7 +41,7 @@
"typedoc": "^0.22.11"
},
"dependencies": {
"@semaphore-protocol/data": "3.2.0",
"@semaphore-protocol/data": "3.4.0",
"axios": "^1.3.2",
"boxen": "^7.0.1",
"chalk": "^5.1.2",

View File

@@ -1,4 +1,4 @@
import { SemaphoreSubgraph } from "@semaphore-protocol/data"
import { SemaphoreSubgraph, SemaphoreEthers, GroupResponse } from "@semaphore-protocol/data"
import chalk from "chalk"
import { program } from "commander"
import download from "download"
@@ -14,7 +14,7 @@ import Spinner from "./spinner.js"
const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`
const { description, version } = JSON.parse(readFileSync(`${packagePath}/package.json`, "utf8"))
const supportedNetworks = ["goerli", "mumbai", "optimism-goerli", "arbitrum"]
const supportedNetworks = ["sepolia", "goerli", "mumbai", "optimism-goerli", "arbitrum", "arbitrum-goerli"]
program
.name("semaphore")
@@ -53,6 +53,7 @@ program
if (existsSync(projectDirectory)) {
console.info(`\n ${logSymbols.error}`, `error: the '${projectDirectory}' folder already exists\n`)
return
}
@@ -88,7 +89,7 @@ program
program
.command("get-groups")
.description("Get the list of groups from a supported network (goerli or arbitrum).")
.description("Get the list of groups from a supported network (e.g. goerli or arbitrum).")
.option("-n, --network <network-name>", "Supported Ethereum network.")
.allowExcessArguments(false)
.action(async ({ network }) => {
@@ -105,37 +106,51 @@ program
if (!supportedNetworks.includes(network)) {
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`)
return
}
const subgraph = new SemaphoreSubgraph(network)
let groupIds: string[]
const spinner = new Spinner("Fetching groups")
spinner.start()
try {
const groupIds = await subgraph.getGroupIds()
const semaphoreSubgraph = new SemaphoreSubgraph(network)
groupIds = await semaphoreSubgraph.getGroupIds()
spinner.stop()
} catch {
try {
const semaphoreEthers = new SemaphoreEthers(network)
groupIds = await semaphoreEthers.getGroupIds()
spinner.stop()
} catch {
spinner.stop()
console.info(`\n ${logSymbols.error}`, "error: unexpected error with the SemaphoreEthers package")
if (groupIds.length === 0) {
console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n")
return
}
const content = `\n${groupIds.map((id: any) => ` - ${id}`).join("\n")}`
console.info(`${content}\n`)
} catch (error) {
spinner.stop()
console.info(`\n ${logSymbols.error}`, "error: unexpected error with the Semaphore subgraph")
}
if (groupIds.length === 0) {
console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n")
return
}
const content = `\n${groupIds.map((id: any) => ` - ${id}`).join("\n")}`
console.info(`${content}\n`)
})
program
.command("get-group")
.description("Get the data of a group from a supported network (Goerli or Arbitrum).")
.description("Get the data of a group from a supported network (e.g. goerli or arbitrum).")
.argument("[group-id]", "Identifier of the group.")
.option("-n, --network <network-name>", "Supported Ethereum network.")
.option("-m, --members", "Show group members.")
@@ -150,37 +165,59 @@ program
default: supportedNetworks[0],
choices: supportedNetworks
})
network = selectedNetwork
}
if (!supportedNetworks.includes(network)) {
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`)
return
}
if (!groupId) {
const subgraphGroups = new SemaphoreSubgraph(network)
let groupIds: string[]
const spinnerGroups = new Spinner("Fetching groups")
spinnerGroups.start()
try {
const groups = await subgraphGroups.getGroups()
const semaphoreSubgraphGroups = new SemaphoreSubgraph(network)
groupIds = await semaphoreSubgraphGroups.getGroupIds()
spinnerGroups.stop()
} catch {
try {
const semaphoreEthersGroups = new SemaphoreEthers(network)
groupIds = await semaphoreEthersGroups.getGroupIds()
spinnerGroups.stop()
} catch {
spinnerGroups.stop()
console.info(`\n ${logSymbols.error}`, "error: unexpected error with the SemaphoreEthers package")
if (groups.length === 0) {
console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n")
return
}
}
const groupIds = groups.map(({ id }: any) => id)
if (groupIds.length === 0) {
console.info(`\n ${logSymbols.info}`, "info: there are no groups in this network\n")
const { selectedGroupId } = await inquirer.prompt({
name: "selectedGroupId",
type: "list",
message: "Select one of the following existing group ids:",
choices: groupIds
})
groupId = selectedGroupId
} catch (error) {
spinnerGroups.stop()
console.info(`\n ${logSymbols.error}`, "error: unexpected error with the Semaphore subgraph")
return
}
const { selectedGroupId } = await inquirer.prompt({
name: "selectedGroupId",
type: "list",
message: "Select one of the following existing group ids:",
choices: groupIds
})
groupId = selectedGroupId
}
if (!members && !signals) {
@@ -203,53 +240,65 @@ program
signals = showSignals
}
if (!supportedNetworks.includes(network)) {
console.info(`\n ${logSymbols.error}`, `error: the network '${network}' is not supported\n`)
return
}
let group: GroupResponse
const subgraph = new SemaphoreSubgraph(network)
const spinner = new Spinner(`Fetching group ${groupId}`)
spinner.start()
try {
const group = await subgraph.getGroup(groupId, { members, verifiedProofs: signals })
const semaphoreSubgraph = new SemaphoreSubgraph(network)
group = await semaphoreSubgraph.getGroup(groupId, { members, verifiedProofs: signals })
spinner.stop()
} catch {
try {
const semaphoreEthers = new SemaphoreEthers(network)
group = await semaphoreEthers.getGroup(groupId)
if (members) {
group.members = await semaphoreEthers.getGroupMembers(groupId)
}
if (signals) {
group.verifiedProofs = await semaphoreEthers.getGroupVerifiedProofs(groupId)
}
group.admin = await semaphoreEthers.getGroupAdmin(groupId)
spinner.stop()
} catch {
spinner.stop()
if (!group) {
console.info(`\n ${logSymbols.error}`, "error: the group does not exist\n")
return
}
let content = ` ${chalk.bold("Id")}: ${group.id}\n`
content += ` ${chalk.bold("Admin")}: ${group.admin}\n`
content += ` ${chalk.bold("Merkle tree")}:\n`
content += ` Root: ${group.merkleTree.root}\n`
content += ` Depth: ${group.merkleTree.depth}\n`
content += ` Zero value: ${group.merkleTree.zeroValue}\n`
content += ` Number of leaves: ${group.merkleTree.numberOfLeaves}`
if (members) {
content += `\n\n ${chalk.bold("Members")}: \n${group.members
.map((member: string, i: number) => ` ${i}. ${member}`)
.join("\n")}`
}
if (signals) {
content += `\n\n ${chalk.bold("Signals")}: \n${group.verifiedProofs
.map(({ signal }: any) => ` - ${signal}`)
.join("\n")}`
}
console.info(`\n${content}\n`)
} catch (error) {
spinner.stop()
console.info(`\n ${logSymbols.error}`, "error: unexpected error with the Semaphore subgraph")
}
let content = ` ${chalk.bold("Id")}: ${group.id}\n`
content += ` ${chalk.bold("Admin")}: ${group.admin}\n`
content += ` ${chalk.bold("Merkle tree")}:\n`
content += ` Root: ${group.merkleTree.root}\n`
content += ` Depth: ${group.merkleTree.depth}\n`
content += ` Zero value: ${group.merkleTree.zeroValue}\n`
content += ` Number of leaves: ${group.merkleTree.numberOfLeaves}`
if (members) {
content += `\n\n ${chalk.bold("Members")}: \n${group.members
.map((member: string, i: number) => ` ${i}. ${member}`)
.join("\n")}`
}
if (signals) {
content += `\n\n ${chalk.bold("Signals")}: \n${group.verifiedProofs
.map(({ signal }: any) => ` - ${signal}`)
.join("\n")}`
}
console.info(`\n${content}\n`)
})
program.parse(process.argv)

View File

@@ -14,7 +14,7 @@ library Pairing {
// The prime q in the base field F_q for G1
uint256 constant BASE_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
// The prime moludus of the scalar field of G1.
// The prime modulus of the scalar field of G1.
uint256 constant SCALAR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
struct G1Point {

View File

@@ -1,9 +1,10 @@
{
"name": "@semaphore-protocol/contracts",
"version": "3.2.0",
"version": "3.4.0",
"description": "Semaphore contracts to manage groups and broadcast anonymous signals.",
"license": "MIT",
"files": [
"Semaphore.sol",
"**/*.sol"
],
"keywords": [

View File

@@ -0,0 +1,7 @@
{
"Pairing": "0xC0ae1a8D3505B2bE9DCe0e826abd722Afd13F1c9",
"SemaphoreVerifier": "0x346a936b19071b2f619200848B8ADbb938D72250",
"Poseidon": "0xb69aABB5D8d8e4920834761bD0C9DEEfa5D5502F",
"IncrementalBinaryTree": "0x9f44be9F69aF1e049dCeCDb2d9296f36C49Ceafb",
"Semaphore": "0xbE66454b0Fa9E6b3D53DC1b0f9D21978bb864531"
}

View File

@@ -45,8 +45,13 @@ function getNetworks(): NetworksUserConfig {
chainId: 420,
accounts
},
"arbitrum-goerli": {
url: "https://goerli-rollup.arbitrum.io/rpc",
chainId: 421613,
accounts
},
arbitrum: {
url: `https://arbitrum-mainnet.infura.io/v3/${infuraApiKey}`,
url: "https://arb1.arbitrum.io/rpc",
chainId: 42161,
accounts
}

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/data",
"version": "3.2.0",
"version": "3.4.0",
"description": "A library to query Semaphore contracts.",
"license": "MIT",
"main": "dist/index.node.js",

View File

@@ -28,17 +28,23 @@ describe("SemaphoreEthers", () => {
it("Should instantiate a SemaphoreEthers object with different networks", () => {
semaphore = new SemaphoreEthers()
const semaphore1 = new SemaphoreEthers("arbitrum")
const semaphore2 = new SemaphoreEthers("homestead", {
const semaphore2 = new SemaphoreEthers("matic")
const semaphore3 = new SemaphoreEthers("optimism-goerli")
const semaphore4 = new SemaphoreEthers("arbitrum-goerli")
const semaphore5 = new SemaphoreEthers("homestead", {
address: "0x0000000000000000000000000000000000000000",
startBlock: 0
})
expect(semaphore.network).toBe("goerli")
expect(semaphore.network).toBe("sepolia")
expect(semaphore.contract).toBeInstanceOf(Object)
expect(semaphore1.network).toBe("arbitrum")
expect(semaphore2.network).toBe("homestead")
expect(semaphore2.options.startBlock).toBe(0)
expect(semaphore2.options.address).toContain("0x000000")
expect(semaphore2.network).toBe("maticmum")
expect(semaphore3.network).toBe("optimism-goerli")
expect(semaphore4.network).toBe("arbitrum-goerli")
expect(semaphore5.network).toBe("homestead")
expect(semaphore5.options.startBlock).toBe(0)
expect(semaphore5.options.address).toContain("0x000000")
})
it("Should instantiate a SemaphoreEthers object with different providers", () => {
@@ -249,6 +255,14 @@ describe("SemaphoreEthers", () => {
describe("# getGroupVerifiedProofs", () => {
it("Should return a list of group verified proofs", async () => {
getEventsMocked.mockReturnValueOnce(
Promise.resolve([
{
merkleTreeDepth: "20",
zeroValue: "0"
}
])
)
getEventsMocked.mockReturnValueOnce(
Promise.resolve([
{

View File

@@ -24,7 +24,7 @@ export default class SemaphoreEthers {
* @param networkOrEthereumURL Ethereum network or custom URL.
* @param options Ethers options.
*/
constructor(networkOrEthereumURL: Network | string = "goerli", options: EthersOptions = {}) {
constructor(networkOrEthereumURL: Network | string = "sepolia", options: EthersOptions = {}) {
checkParameter(networkOrEthereumURL, "networkOrSubgraphURL", "string")
if (options.provider) {
@@ -46,6 +46,10 @@ export default class SemaphoreEthers {
options.address = "0x72dca3c971136bf47BACF16A141f0fcfAC925aeC"
options.startBlock = 54934350
break
case "arbitrum-goerli":
options.address = "0xbE66454b0Fa9E6b3D53DC1b0f9D21978bb864531"
options.startBlock = 11902029
break
case "maticmum":
options.address = "0xF864ABa335073e01234c9a88888BfFfa965650bD"
options.startBlock = 32902215
@@ -54,6 +58,14 @@ export default class SemaphoreEthers {
options.address = "0x89490c95eD199D980Cdb4FF8Bac9977EDb41A7E7"
options.startBlock = 8255063
break
case "sepolia":
options.address = "0x220fBdB6F996827b1Cf12f0C181E8d5e6de3a36F"
options.startBlock = 3053948
break
case "optimism-goerli":
options.address = "0x220fBdB6F996827b1Cf12f0C181E8d5e6de3a36F"
options.startBlock = 6477953
break
default:
if (options.address === undefined) {
throw new Error(`You should provide a Semaphore contract address for this network`)
@@ -246,6 +258,12 @@ export default class SemaphoreEthers {
async getGroupVerifiedProofs(groupId: string): Promise<any> {
checkParameter(groupId, "groupId", "string")
const [groupCreatedEvent] = await getEvents(this._contract, "GroupCreated", [groupId], this._options.startBlock)
if (!groupCreatedEvent) {
throw new Error(`Group '${groupId}' not found`)
}
const proofVerifiedEvents = await getEvents(
this._contract,
"ProofVerified",
@@ -253,10 +271,6 @@ export default class SemaphoreEthers {
this._options.startBlock
)
if (proofVerifiedEvents.length === 0) {
throw new Error(`Group '${groupId}' not found`)
}
return proofVerifiedEvents.map((event) => ({
signal: event.signal.toString(),
merkleTreeRoot: event.merkleTreeRoot.toString(),

View File

@@ -10,6 +10,7 @@ export default function getURL(network: Network): string {
case "goerli":
case "mumbai":
case "optimism-goerli":
case "arbitrum-goerli":
case "arbitrum":
return `https://api.studio.thegraph.com/query/14377/semaphore-${network}/v3.2.0`
default:

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/group",
"version": "3.2.0",
"version": "3.4.0",
"description": "A library to create and manage Semaphore groups.",
"license": "MIT",
"main": "dist/index.node.js",
@@ -30,6 +30,9 @@
"access": "public"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-node-resolve": "^15.0.1",
"poseidon-lite": "^0.1.0",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-typescript2": "^0.31.2",
"typedoc": "^0.22.11"
@@ -38,7 +41,6 @@
"@ethersproject/bignumber": "^5.7.0",
"@ethersproject/bytes": "^5.7.0",
"@ethersproject/keccak256": "^5.7.0",
"@zk-kit/incremental-merkle-tree": "1.0.0",
"poseidon-lite": "^0.1.0"
"@zk-kit/incremental-merkle-tree": "1.0.0"
}
}

View File

@@ -1,6 +1,8 @@
import typescript from "rollup-plugin-typescript2"
import commonjs from "@rollup/plugin-commonjs"
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 = `/**
@@ -24,6 +26,8 @@ export default {
tsconfig: "./build.tsconfig.json",
useTsconfigDeclarationDir: true
}),
commonjs(),
nodeResolve(),
cleanup({ comments: "jsdoc" })
]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/hardhat",
"version": "3.2.0",
"version": "3.4.0",
"description": "A Semaphore Hardhat plugin to deploy verifiers and Semaphore contract.",
"license": "MIT",
"main": "dist/index.node.js",
@@ -38,7 +38,7 @@
},
"dependencies": {
"@nomiclabs/hardhat-ethers": "^2.1.1",
"@semaphore-protocol/contracts": "3.2.0",
"@semaphore-protocol/contracts": "3.4.0",
"circomlibjs": "^0.0.8",
"ethers": "^5.7.1",
"hardhat-dependency-compiler": "^1.1.3"

21
packages/heyauthn/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Vivek Bhupatiraju
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

118
packages/heyauthn/README.md Normal file
View File

@@ -0,0 +1,118 @@
<p align="center">
<h1 align="center">
HeyAuthn
</h1>
<p align="center">A library to allow developers to create and manage Semaphore identities using WebAuthn.</p>
</p>
<p align="center">
<a href="https://github.com/semaphore-protocol">
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
</a>
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/LICENSE">
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
</a>
<a href="https://www.npmjs.com/package/@semaphore-protocol/heyauthn">
<img alt="NPM version" src="https://img.shields.io/npm/v/@semaphore-protocol/heyauthn?style=flat-square" />
</a>
<a href="https://npmjs.org/package/@semaphore-protocol/heyauthn">
<img alt="Downloads" src="https://img.shields.io/npm/dm/@semaphore-protocol/heyauthn.svg?style=flat-square" />
</a>
<a href="https://eslint.org/">
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint" />
</a>
<a href="https://prettier.io/">
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier" />
</a>
</p>
<div align="center">
<h4>
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CONTRIBUTING.md">
👥 Contributing
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
🤝 Code of conduct
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
🔎 Issues
</a>
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
<a href="https://semaphore.appliedzkp.org/discord">
🗣️ Chat &amp; Support
</a>
</h4>
</div>
| This library allows developers to create and manage Semaphore identities using [WebAuthn](https://webauthn.io/) as a cross-device biometric authentication in a way that is more convenient, smoother and secure than localStorage, Chrome extensions, or password manager based solutions. |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
## 🛠 Install
### npm or yarn
Install the `@semaphore-protocol/heyauthn` package with npm:
```bash
npm i @semaphore-protocol/heyauthn
```
or yarn:
```bash
yarn add @semaphore-protocol/heyauthn
```
## 📜 Usage
```typescript
import { HeyAuthn } from "@semaphore-protocol/heyauthn"
// STEP 1: Configure WebAuthn options.
const options = {
rpName: "my-app",
rpID: window.location.hostname,
userID: "my-id",
userName: "my-name"
}
// STEP 2: Register a new WebAuthn credential and get its Semaphore identity.
const { identity } = await HeyAuthn.fromRegister(options)
// Now you could also save the identity commitment in your DB (pseudocode).
fetch("/api/register" /* Replace this with your endpoint */, {
identity.commmitment
// ...
})
// STEP 3: Authenticate existing WebAuthn credential and signal.
const { identity } = await HeyAuthn.fromRegister(options)
// Get existing group and signal anonymously (pseudocode).
import { Group } from "@semaphore-protocol/group"
import { generateProof } from "@semaphore-protocol/proof"
import { utils } from "ethers"
const group = new Group("42")
group.addMembers(memberList)
const signal = utils.formatBytes32String("Hey anon!")
generateProof(identity, group, group.id, "42", {
zkeyFilePath: "./semaphore.zkey",
wasmFilePath: "./semaphore.wasm"
})
```
## Authors
- @vb7401
- @rrrliu
- @emmaguo13
- @sehyunc

View File

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

View File

@@ -0,0 +1,42 @@
{
"name": "@semaphore-protocol/heyauthn",
"version": "3.4.0",
"description": "A library to allow developers to create and manage Semaphore identities using WebAuthn",
"license": "MIT",
"main": "dist/index.node.js",
"exports": {
"import": "./dist/index.mjs",
"require": "./dist/index.node.js"
},
"types": "dist/types/index.d.ts",
"files": [
"dist/",
"src/",
"LICENSE",
"README.md"
],
"repository": "https://github.com/semaphore-protocol/semaphore",
"homepage": "https://github.com/semaphore-protocol/semaphore/tree/main/packages/heyauthn",
"bugs": {
"url": "https://github.com/semaphore-protocol/semaphore.git/issues"
},
"scripts": {
"build:watch": "rollup -c rollup.config.ts -w --configPlugin typescript",
"build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
"prepublishOnly": "yarn build",
"docs": "typedoc src/index.ts --out ../../docs/heyauthn"
},
"publishConfig": {
"access": "public"
},
"devDependencies": {
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-typescript2": "^0.31.2",
"typedoc": "^0.22.11"
},
"dependencies": {
"@semaphore-protocol/identity": "3.4.0",
"@simplewebauthn/browser": "7.2.0",
"@simplewebauthn/server": "7.2.0"
}
}

View File

@@ -0,0 +1,29 @@
import typescript from "rollup-plugin-typescript2"
import * as fs from "fs"
import cleanup from "rollup-plugin-cleanup"
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
const banner = `/**
* @module ${pkg.name}
* @version ${pkg.version}
* @file ${pkg.description}
* @copyright Vivek Bhupatiraju 2023
* @license ${pkg.license}
* @see [Github]{@link ${pkg.homepage}}
*/`
export default {
input: "src/index.ts",
output: [
{ file: pkg.exports.require, format: "cjs", banner, exports: "auto" },
{ file: pkg.exports.import, format: "es", banner }
],
external: Object.keys(pkg.dependencies),
plugins: [
typescript({
tsconfig: "./build.tsconfig.json",
useTsconfigDeclarationDir: true
}),
cleanup({ comments: "jsdoc" })
]
}

View File

@@ -0,0 +1,75 @@
import { Identity } from "@semaphore-protocol/identity"
import {
GenerateAuthenticationOptionsOpts as AuthenticationOptions,
GenerateRegistrationOptionsOpts as RegistrationOptions
} from "@simplewebauthn/server"
import HeyAuthn from "./heyAuthn"
jest.mock("@simplewebauthn/browser", () => ({
startRegistration: async () => ({
id: "my-new-credential",
rawId: "my-new-credential",
response: {
clientDataJSON: "",
attestationObject: ""
},
clientExtensionResults: {},
type: "public-key"
}),
startAuthentication: async () => ({
id: "my-existing-credential",
rawId: "my-existing-credential",
response: {
clientDataJSON: "",
attestationObject: ""
},
clientExtensionResults: {},
type: "public-key"
})
}))
describe("HeyAuthn", () => {
describe("# getIdentity", () => {
it("Should get the identity of the HeyAuthn instance", async () => {
const expectedIdentity = new Identity()
const heyAuthn = new HeyAuthn(expectedIdentity)
const identity = heyAuthn.getIdentity()
expect(expectedIdentity.toString()).toEqual(identity.toString())
})
})
describe("# fromRegister", () => {
const options: RegistrationOptions = {
rpName: "my-app",
rpID: "hostname",
userID: "my-id",
userName: "my-name"
}
it("Should create an identity identical to the one created registering credential", async () => {
const { identity } = await HeyAuthn.fromRegister(options)
const expectedIdentity = new Identity("my-new-credential")
expect(expectedIdentity.trapdoor).toEqual(identity.trapdoor)
expect(expectedIdentity.nullifier).toEqual(identity.nullifier)
expect(expectedIdentity.commitment).toEqual(identity.commitment)
})
})
describe("# fromAuthenticate", () => {
const options: AuthenticationOptions = {
rpID: "hostname"
}
it("Should create an identity identical to the one created authenticating credential", async () => {
const { identity } = await HeyAuthn.fromAuthenticate(options)
const expectedIdentity = new Identity("my-existing-credential")
expect(expectedIdentity.trapdoor).toEqual(identity.trapdoor)
expect(expectedIdentity.nullifier).toEqual(identity.nullifier)
expect(expectedIdentity.commitment).toEqual(identity.commitment)
})
})
})

View File

@@ -0,0 +1,62 @@
import {
generateAuthenticationOptions,
generateRegistrationOptions,
GenerateRegistrationOptionsOpts as RegistrationOptions,
GenerateAuthenticationOptionsOpts as AuthenticationOptions
} from "@simplewebauthn/server"
import { startAuthentication, startRegistration } from "@simplewebauthn/browser"
import { Identity } from "@semaphore-protocol/identity"
export default class HeyAuthn {
private _identity: Identity
constructor(identity: Identity) {
this._identity = identity
}
/**
* Registers a new WebAuthn credential and returns its HeyAuthn instance.
*
* @param {GenerateRegistrationOptionsOpts} options - WebAuthn options for registering a new credential.
* @returns A HeyAuthn instance with the newly registered credential.
*/
public static async fromRegister(options: RegistrationOptions) {
const registrationOptions = generateRegistrationOptions(options)
const { id } = await startRegistration(registrationOptions)
const identity = new Identity(id)
return new HeyAuthn(identity)
}
/**
* Authenticates an existing WebAuthn credential and returns its HeyAuthn instance.
*
* @param {GenerateAuthenticationOptionsOpts} options - WebAuthn options for authenticating an existing credential.
* @returns A HeyAuthn instance with the existing credential.
*/
public static async fromAuthenticate(options: AuthenticationOptions) {
const authenticationOptions = generateAuthenticationOptions(options)
const { id } = await startAuthentication(authenticationOptions)
const identity = new Identity(id)
return new HeyAuthn(identity)
}
/**
* Returns the Semaphore identity instance.
* @returns The Semaphore identity.
*/
public get identity(): Identity {
return this._identity
}
/**
* Returns the Semaphore identity instance.
* @returns The Semaphore identity.
*/
public getIdentity(): Identity {
return this._identity
}
}

View File

@@ -0,0 +1,10 @@
import { Identity } from "@semaphore-protocol/identity"
import { GenerateAuthenticationOptionsOpts, GenerateRegistrationOptionsOpts } from "@simplewebauthn/server"
import HeyAuthn from "./heyAuthn"
export {
HeyAuthn,
GenerateRegistrationOptionsOpts as RegistrationOptions,
GenerateAuthenticationOptionsOpts as AuthenticationOptions,
Identity
}

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/identity",
"version": "3.2.0",
"version": "3.4.0",
"description": "A library to create Semaphore identities.",
"license": "MIT",
"main": "dist/index.node.js",
@@ -30,6 +30,9 @@
"access": "public"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-node-resolve": "^15.0.1",
"poseidon-lite": "^0.1.0",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-typescript2": "^0.31.2",
"typedoc": "^0.22.11"
@@ -39,7 +42,6 @@
"@ethersproject/keccak256": "^5.7.0",
"@ethersproject/random": "^5.5.1",
"@ethersproject/strings": "^5.6.1",
"js-sha512": "^0.8.0",
"poseidon-lite": "^0.1.0"
"js-sha512": "^0.8.0"
}
}

View File

@@ -1,6 +1,8 @@
import typescript from "rollup-plugin-typescript2"
import commonjs from "@rollup/plugin-commonjs"
import * as fs from "fs"
import cleanup from "rollup-plugin-cleanup"
import { nodeResolve } from "@rollup/plugin-node-resolve"
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
const banner = `/**
@@ -24,6 +26,8 @@ export default {
tsconfig: "./build.tsconfig.json",
useTsconfigDeclarationDir: true
}),
commonjs(),
nodeResolve(),
cleanup({ comments: "jsdoc" })
]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/proof",
"version": "3.2.0",
"version": "3.4.0",
"description": "A library to generate and verify Semaphore proofs.",
"license": "MIT",
"main": "dist/index.node.js",
@@ -37,8 +37,8 @@
"typedoc": "^0.22.11"
},
"peerDependencies": {
"@semaphore-protocol/group": "3.2.0",
"@semaphore-protocol/identity": "3.2.0"
"@semaphore-protocol/group": "3.4.0",
"@semaphore-protocol/identity": "3.4.0"
},
"dependencies": {
"@ethersproject/bignumber": "^5.5.0",

View File

@@ -1 +1 @@
37fdb99af156c475e680a24b069baea47cf69d07
749e590bb5a7dbd0f505b3b8f4e68dea5c991395