Semaphore groups
A Semaphore group contains identity commitments of group members. Example uses of groups include the following:
- poll question that attendees join to rate an event,
- ballot that members join to vote on a proposal,
- whistleblowers who are verified employees of an organization.
Semaphore V4 uses the ZK-Kit LeanIMT (i.e., Lean Incremental Merkle Tree) Solidity and JavaScript implementations for managing groups. Groups are Merkle trees, and the group members (i.e., identity commitments) are their leaves.
Off-chain groupsβ
Use the @semaphore-protocol/group
package to manage off-chain groups.
Install packageβ
- npm
- Yarn
- pnpm
npm install @semaphore-protocol/group
yarn add @semaphore-protocol/group
pnpm add @semaphore-protocol/group
Semaphore also provides @semaphore-protocol/core
, which includes the functions of the following core packages: @semaphore-protocol/identity
, @semaphore-protocol/group
, @semaphore-protocol/proof
.
Create a groupβ
To create a group instantiate Group
without any parameters. For example:
import { Group } from "@semaphore-protocol/group"
const group1 = new Group()
You can also initialize a group with multiple members by passing the list of identity commitments as the first parameter when creating the group:
const members = [
11237622825477336339577122413451117718539783476837539122310492284566644730311n,
9332663527862709610616009715800254142772436825222910251631161087138559093425n,
13255821893820536903335282929376140649646180444238593676033702344407594536519n
]
const group2 = new Group(members)
Add membersβ
Use the addMember
method to add a member to a group. For example:
import { Identity } from "@semaphore-protocol/identity"
const { commitment } = new Identity()
group1.addMember(commitment)
To add a batch of members to a group, pass an array to the addMembers
method. For example:
group1.addMembers(members)
When you use the same Semaphore identity across multiple groups, if an attacker takes control of that identity all the groups it is part of will be compromised. Consider using different identities for each group.
Remove or update membersβ
To remove members from a group, pass the member index to the removeMember
method. For example:
group.removeMember(0)
To update members in a group, pass the member index and the new value to the updateMember
method. For example:
group.updateMember(0, 2n)
Removing a member from a group sets its value to 0.
Given that the member isn't removed, the number of members (i.e., group.size
on group.members.length
) doesn't change.
Generate a Merkle proofβ
Semaphore groups are Merkle trees, and it is therefore possible to calculate the Merkle proof of a group member (i.e., tree leaf) by passing the index of the member to the generateMerkleProof
. For example:
group.generateMerkleProof(0)
On-chain groupsβ
Semaphore provides Semaphore.sol
, a contract designed for managing on-chain groups (deployed on major testnets).
Use the @semaphore-protocol/contracts
package to import the ISemaphore.sol
interface in your contract and start using its functions.
Install packageβ
- npm
- Yarn
- pnpm
npm install @semaphore-protocol/contracts
yarn add @semaphore-protocol/contracts
pnpm add @semaphore-protocol/contracts
Create a groupβ
To create a group initialize your contract with the Semaphore.sol
address.
The createGroup
function can be used to create a Semaphore group. For example:
pragma solidity ^0.8.23;
import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
contract YourContract {
ISemaphore public semaphore;
uint256 public groupId;
constructor(ISemaphore _semaphore) {
semaphore = _semaphore;
groupId = semaphore.createGroup();
}
}
Semaphore.sol
also includes a mechanism to verify Semaphore proofs created with old Merkle tree roots, the duration of which can optionally be defined by the admin in the createGroup
function. The default value duration is 1 hour and it should be fine for most use-cases. For more context see the issue #98.
Add membersβ
Use the addMember
function to add a member to a group. For example:
function addMember(uint256 identityCommitment) external {
semaphore.addMember(groupId, identityCommitment);
}
To add a batch of members to a group, pass an array to the addMembers
function. For example:
function addMembers(uint256[] calldata identityCommitments) external {
semaphore.addMembers(groupId, identityCommitments);
}
Remove or update membersβ
To update members in a group, pass the identity commitment of the member you want to update, its new identity commitment and the siblings of the Merkle proof for that member. For example:
function updateMember(uint256 identityCommitment, uint256 newIdentityCommitment, uint256[] calldata merkleProofSiblings) external {
semaphore.updateMember(groupId, identityCommitment, newIdentityCommitment, merkleProofSiblings);
}
To calculate the Merkle proof of a group member you can use the generateMerkleProof
method of the JavaScript Group
class described above.
To remove members from a group, pass the identity commitment of the member you want to remove and the siblings of the Merkle proof for that member. For example:
function removeMember(uint256 identityCommitment, uint256[] calldata merkleProofSiblings) external {
semaphore.removeMember(groupId, identityCommitment, merkleProofSiblings);
}
If you want to see an example of a working contract, have a look at the contracts-hardhat
CLI template. You can also create a project with that template by running semaphore create my-app --template contracts-hardhat
.