Skip to main content
Version: V2

Semaphore groups

Use Semaphore in your application or smart contract to create off-chain and on-chain 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 tree parameters:

  • Tree depth: the maximum number of members a group can contain (max size = 2 ^ tree depth).
  • Zero value: the value used to calculate the zero nodes of the incremental Merkle tree.

Learn how to work with groups.

Off-chain groups

Create an off-chain group

Use the @semaphore-protocol/group library Group class to create an off-chain group.

Options

  • Tree depth: (default 20) the maximum number of members a group can contain (max size = 2 ^ tree depth).
  • Zero value: (default BigInt(0)) the value for a tree node that doesn't have a member assigned.

To create a group with default treeDepth and zeroValue, call the Group constructor without parameters--for example:

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

// Default parameters: treeDepth = 20, zeroValue = BigInt(0).
const group = new Group()

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(30)

The following example code creates a group with a zeroValue of BigInt(1):

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

const group = new Group(20, BigInt(1))

Add members to an off-chain group

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])

Remove members from an off-chain group

To remove members from a group, pass the member index to the Group removeMember function--for example:

group.removeMember(0)
caution

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

On-chain groups

The SemaphoreGroups contract uses a the IncrementalBinaryTree library and provides methods to create groups and add/remove members.

To use on-chain groups, import SemaphoreGroups and call its internal methods. The following code sample shows how the Semaphore contract uses SemaphoreGroups:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./interfaces/ISemaphore.sol";
import "./base/SemaphoreCore.sol";
import "./base/SemaphoreGroups.sol";

/// @title Semaphore
contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {

...

function createGroup(
uint256 groupId,
uint8 depth,
uint256 zeroValue,
address admin
) external override onlySupportedDepth(depth) {
_createGroup(groupId, depth, zeroValue);

groupAdmins[groupId] = admin;

emit GroupAdminUpdated(groupId, address(0), admin);
}

function addMember(uint256 groupId, uint256 identityCommitment) external override onlyGroupAdmin(groupId) {
_addMember(groupId, identityCommitment);
}

function removeMember(
uint256 groupId,
uint256 identityCommitment,
uint256[] calldata proofSiblings,
uint8[] calldata proofPathIndices
) external override onlyGroupAdmin(groupId) {
_removeMember(groupId, identityCommitment, proofSiblings, proofPathIndices);
}

...
}