mirror of
https://github.com/semaphore-protocol/semaphore.git
synced 2026-04-28 03:00:41 -04:00
172 lines
6.0 KiB
TypeScript
172 lines
6.0 KiB
TypeScript
import { Strategy, ZkIdentity } from "@zk-kit/identity"
|
|
import { generateMerkleProof, Semaphore, SemaphoreFullProof, SemaphoreSolidityProof } from "@zk-kit/protocols"
|
|
import { expect } from "chai"
|
|
import { config as dotenvConfig } from "dotenv"
|
|
import { constants, Signer, utils } from "ethers"
|
|
import { run } from "hardhat"
|
|
import { resolve } from "path"
|
|
import { Semaphore as SemaphoreContract } from "../build/typechain/Semaphore"
|
|
import { createIdentityCommitments, createTree } from "./utils"
|
|
|
|
dotenvConfig({ path: resolve(__dirname, "../.env") })
|
|
|
|
describe("Semaphore", () => {
|
|
let contract: SemaphoreContract
|
|
let signers: Signer[]
|
|
let accounts: string[]
|
|
|
|
const depth = 20
|
|
const groupId = 1
|
|
const members = createIdentityCommitments(3)
|
|
|
|
const wasmFilePath = "./build/snark/semaphore.wasm"
|
|
const finalZkeyPath = "./build/snark/semaphore_final.zkey"
|
|
|
|
before(async () => {
|
|
const { address: verifierAddress } = await run("deploy:verifier", { logs: false })
|
|
contract = await run("deploy:semaphore", {
|
|
logs: false,
|
|
verifiers: [{ merkleTreeDepth: depth, contractAddress: verifierAddress }]
|
|
})
|
|
|
|
signers = await run("accounts", { logs: false })
|
|
accounts = await Promise.all(signers.map((signer: Signer) => signer.getAddress()))
|
|
})
|
|
|
|
describe("# createGroup", () => {
|
|
it("Should not create a group if the tree depth is not supported", async () => {
|
|
const transaction = contract.createGroup(groupId, 10, 0, accounts[0])
|
|
|
|
await expect(transaction).to.be.revertedWith("Semaphore: tree depth is not supported")
|
|
})
|
|
|
|
it("Should create a group", async () => {
|
|
const transaction = contract.connect(signers[1]).createGroup(groupId, depth, 0, accounts[1])
|
|
|
|
await expect(transaction).to.emit(contract, "GroupCreated").withArgs(groupId, depth, 0)
|
|
await expect(transaction)
|
|
.to.emit(contract, "GroupAdminUpdated")
|
|
.withArgs(groupId, constants.AddressZero, accounts[1])
|
|
})
|
|
})
|
|
|
|
describe("# updateGroupAdmin", () => {
|
|
it("Should not update a group admin if the caller is not the group admin", async () => {
|
|
const transaction = contract.updateGroupAdmin(groupId, accounts[0])
|
|
|
|
await expect(transaction).to.be.revertedWith("Semaphore: caller is not the group admin")
|
|
})
|
|
|
|
it("Should update the group admin", async () => {
|
|
const transaction = contract.connect(signers[1]).updateGroupAdmin(groupId, accounts[0])
|
|
|
|
await expect(transaction).to.emit(contract, "GroupAdminUpdated").withArgs(groupId, accounts[1], accounts[0])
|
|
})
|
|
})
|
|
|
|
describe("# addMember", () => {
|
|
it("Should not add a member if the caller is not the group admin", async () => {
|
|
const member = BigInt(2)
|
|
|
|
const transaction = contract.connect(signers[1]).addMember(groupId, member)
|
|
|
|
await expect(transaction).to.be.revertedWith("Semaphore: caller is not the group admin")
|
|
})
|
|
|
|
it("Should add a new member in an existing group", async () => {
|
|
const transaction = contract.addMember(groupId, members[0])
|
|
|
|
await expect(transaction)
|
|
.to.emit(contract, "MemberAdded")
|
|
.withArgs(groupId, members[0], "18951329906296061785889394467312334959162736293275411745101070722914184798221")
|
|
})
|
|
})
|
|
|
|
describe("# removeMember", () => {
|
|
it("Should not remove a member if the caller is not the group admin", async () => {
|
|
const transaction = contract.connect(signers[1]).removeMember(groupId, members[0], [0, 1], [0, 1])
|
|
|
|
await expect(transaction).to.be.revertedWith("Semaphore: caller is not the group admin")
|
|
})
|
|
|
|
it("Should remove a member from an existing group", async () => {
|
|
const groupId = 100
|
|
const tree = createTree(depth, 3)
|
|
|
|
tree.delete(0)
|
|
|
|
await contract.createGroup(groupId, depth, 0, accounts[0])
|
|
await contract.addMember(groupId, BigInt(1))
|
|
await contract.addMember(groupId, BigInt(2))
|
|
await contract.addMember(groupId, BigInt(3))
|
|
|
|
const { siblings, pathIndices, root } = tree.createProof(0)
|
|
|
|
const transaction = contract.removeMember(
|
|
groupId,
|
|
BigInt(1),
|
|
siblings.map((s) => s[0]),
|
|
pathIndices
|
|
)
|
|
|
|
await expect(transaction).to.emit(contract, "MemberRemoved").withArgs(groupId, BigInt(1), root)
|
|
})
|
|
})
|
|
|
|
describe("# verifyProof", () => {
|
|
const signal = "Hello world"
|
|
const bytes32Signal = utils.formatBytes32String(signal)
|
|
const identity = new ZkIdentity(Strategy.MESSAGE, "0")
|
|
const identityCommitment = identity.genIdentityCommitment()
|
|
const merkleProof = generateMerkleProof(depth, BigInt(0), members, identityCommitment)
|
|
const witness = Semaphore.genWitness(
|
|
identity.getTrapdoor(),
|
|
identity.getNullifier(),
|
|
merkleProof,
|
|
merkleProof.root,
|
|
signal
|
|
)
|
|
|
|
let fullProof: SemaphoreFullProof
|
|
let solidityProof: SemaphoreSolidityProof
|
|
|
|
before(async () => {
|
|
await contract.addMember(groupId, members[1])
|
|
await contract.addMember(groupId, members[2])
|
|
|
|
fullProof = await Semaphore.genProof(witness, wasmFilePath, finalZkeyPath)
|
|
solidityProof = Semaphore.packToSolidityProof(fullProof.proof)
|
|
})
|
|
|
|
it("Should not verify a proof if the group does not exist", async () => {
|
|
const transaction = contract.verifyProof(10, bytes32Signal, 0, 0, [0, 0, 0, 0, 0, 0, 0, 0])
|
|
|
|
await expect(transaction).to.be.revertedWith("Semaphore: group does not exist")
|
|
})
|
|
|
|
it("Should throw an exception if the proof is not valid", async () => {
|
|
const transaction = contract.verifyProof(
|
|
groupId,
|
|
bytes32Signal,
|
|
fullProof.publicSignals.nullifierHash,
|
|
0,
|
|
solidityProof
|
|
)
|
|
|
|
await expect(transaction).to.be.revertedWith("InvalidProof()")
|
|
})
|
|
|
|
it("Should verify a proof for an onchain group correctly", async () => {
|
|
const transaction = contract.verifyProof(
|
|
groupId,
|
|
bytes32Signal,
|
|
fullProof.publicSignals.nullifierHash,
|
|
fullProof.publicSignals.merkleRoot,
|
|
solidityProof
|
|
)
|
|
|
|
await expect(transaction).to.emit(contract, "ProofVerified").withArgs(groupId, bytes32Signal)
|
|
})
|
|
})
|
|
})
|