Semaphore proofs
Once a user joins a Semaphore group with their Semaphore identity, the user can send their anonymous message with a zero-knowledge proof that proves the following:
- the user is a member of the group,
- the same user created the message and the proof.
A unique nullifier is also generated for each proof that can be used to check whether that proof has already been validated.
Install package
In your code, use the @semaphore-protocol/proof
package to generate and verify a proof.
- npm
- Yarn
- pnpm
npm install @semaphore-protocol/proof
yarn add @semaphore-protocol/proof
pnpm add @semaphore-protocol/proof
Semaphore also provides @semaphore-protocol/core
, which includes the functions of the following core packages: @semaphore-protocol/identity
, @semaphore-protocol/group
, @semaphore-protocol/proof
.
Generate a proof
1. Create the identity
In order for a user to generate a proof, it is necessary to create a Semaphore identity. If you do not know how to create an identity, see the previous guide on identities.
2. Create the group
Before generating a proof you also need to create a Semaphore group containing the commitment of the Semaphore identity of the user who will generate the proof. If you do not know how to create a group, see the previous guide on groups.
If your group is on-chain, you can use the @semaphore-protocol/data
library to fetch the group members and re-create the off-chain group. For example:
import { SemaphoreSubgraph } from "@semaphore-protocol/data"
import { Group } from "@semaphore-protocol/group"
const semaphoreSubgraph = new SemaphoreSubgraph("sepolia")
const { members } = await semaphoreSubgraph.getGroup("42", { members: true })
const group = new Group(members)
3. Choose the scope
Each proof requires a scope, on which each user may only generate one valid proof. The scope, together with the user's private key, is used to generate the nullifier, which is the value you can actually use to check whether a proof with that scope has already been generated by that user. In a voting application where double-voting must be prevented, the scope could be the ballot id, or the Merkle root of the group.
4. Generate the anomymous message
Finally, you can generate the proof with the anonymous message using the generateProof
function. For example:
import { generateProof } from "@semaphore-protocol/proof"
const scope = group.root
const message = 1
const proof = await generateProof(identity, group, message, scope)
Verify a proof
To verify a proof, pass the proof you generated to the verifyProof
function. For example:
import { verifyProof } from "@semaphore-protocol/proof"
await verifyProof(proof) // true or false.
If you want to validate a proof on-chain, you can use @semaphore-protocol/contracts
and the Semaphore.sol
contract, as explained in the previous guide, and use the validateProof
function. For example:
function validateProof(ISemaphore.SemaphoreProof calldata proof) external {
semaphore.validateProof(groupId, proof);
}