Skip to main content
Version: V3

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.

A Semaphore group is an incremental Merkle tree, and group members (i.e., identity commitments) are tree leaves. Semaphore groups set the following three parameters:

  • Group id: a unique identifier for the group;
  • Tree depth: the maximum number of members a group can contain (max size = 2 ^ tree depth);
  • Members: the list of members to initialize the group.

Learn how to work with groups.

Off-chain groups​

Create a group​

Use the @semaphore-protocol/group library Group class to create an off-chain group with the following parameters:

  • Group id: a unique identifier for the group;
  • Tree depth: (default 20) the maximum number of members a group can contain (max size = 2 ^ tree depth).
  • Members: (default []) the list of members to initialize the group.

Install library:​

npm install @semaphore-protocol/group@^3

To create a group with default treeDepth, call the Group constructor without the second parameter. For example:

import { Group } from "@semaphore-protocol/group"

const group = new Group(1)

The following example code passes treeDepth to create a group for 2 ^ 30 = 1073741824 members:

import { Group } from "@semaphore-protocol/group"

const group = new Group(1, 30)

You can also initialize a group with multiple members by passing the list of identity commitments (members) as the third parameter when creating the group:

import { Group } from "@semaphore-protocol/group"

const members = [
"11237622825477336339577122413451117718539783476837539122310492284566644730311",
"9332663527862709610616009715800254142772436825222910251631161087138559093425",
"13255821893820536903335282929376140649646180444238593676033702344407594536519"
]
const group = new Group(1, 20, members)

Add members​

Use the Group addMember function to add a member (identity commitment) to a group. For example:

group.addMember(identityCommitment)

To add a batch of members to a group, pass an array to the Group addMembers function. For example:

group.addMembers([identityCommitment1, identityCommitment2])
caution

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 Group removeMember function. For example:

group.removeMember(0)

To update members in a group, pass the member index and the new value to the Group updateMember function. For example:

group.updateMember(0, 2)
caution

Removing a member from a group sets the node value to a special value (i.e. zeroValue). Given that the node isn't removed, and the length of the group.members array doesn't change.

On-chain groups​

The SemaphoreGroups contract uses the IncrementalBinaryTree library and provides methods to create and manage groups.

info

You can import SemaphoreGroups.sol and other Semaphore contracts from the @semaphore-protocol/contracts NPM module.

Alternatively, you can use an already deployed Semaphore.sol contract and use its group external functions.

caution

Semaphore.sol does not check if a member with a specific identity commitment already exists in a group. This check must be done off-chain.

caution

Semaphore.sol includes a mechanism to verify Semaphore proofs created with old Merkle tree roots, the duration of which can be defined by the admin in the createGroup function. Members of a group could then continue to generate valid proofs even after being removed. For more info see the issue #98.