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
: (default20
) 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
- Yarn
- pnpm
npm install @semaphore-protocol/group@^3
yarn add @semaphore-protocol/group@^3
pnpm add @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])
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)
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.
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.
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.
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.