Skip to content

Commit

Permalink
feat(contracts): update contracts with new imt
Browse files Browse the repository at this point in the history
  • Loading branch information
cedoor committed Jan 12, 2024
1 parent 6b347ff commit cc32fee
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 321 deletions.
80 changes: 41 additions & 39 deletions packages/contracts/contracts/Semaphore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
_;
}

/// @dev Checks if there is a verifier for the given tree depth.
/// @param merkleTreeDepth: Depth of the tree.
modifier onlySupportedMerkleTreeDepth(uint256 merkleTreeDepth) {
if (merkleTreeDepth < 16 || merkleTreeDepth > 32) {
revert Semaphore__MerkleTreeDepthIsNotSupported();
/// @dev Checks if the group exists.
/// @param groupId: Id of the group.
modifier onlyExistingGroup(uint256 groupId) {
if (groups[groupId].admin == address(0)) {
revert Semaphore__GroupDoesNotExist();
}

_;
}

Expand All @@ -43,36 +44,36 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
}

/// @dev See {ISemaphore-createGroup}.
function createGroup(
uint256 groupId,
uint256 merkleTreeDepth,
address admin
) external override onlySupportedMerkleTreeDepth(merkleTreeDepth) {
_createGroup(groupId, merkleTreeDepth);
function createGroup(uint256 groupId, address admin) external override {
if (groups[groupId].admin != address(0)) {
revert Semaphore__GroupAlreadyExists();
}

groups[groupId].admin = admin;
groups[groupId].merkleTreeDuration = 1 hours;

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

/// @dev See {ISemaphore-createGroup}.
function createGroup(
uint256 groupId,
uint256 merkleTreeDepth,
address admin,
uint256 merkleTreeDuration
) external override onlySupportedMerkleTreeDepth(merkleTreeDepth) {
_createGroup(groupId, merkleTreeDepth);
function createGroup(uint256 groupId, address admin, uint256 merkleTreeDuration) external override {
if (groups[groupId].admin != address(0)) {
revert Semaphore__GroupAlreadyExists();
}

groups[groupId].admin = admin;
groups[groupId].merkleTreeDuration = merkleTreeDuration;

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

/// @dev See {ISemaphore-updateGroupAdmin}.
function updateGroupAdmin(uint256 groupId, address newAdmin) external override onlyGroupAdmin(groupId) {
function updateGroupAdmin(
uint256 groupId,
address newAdmin
) external override onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
groups[groupId].admin = newAdmin;

emit GroupAdminUpdated(groupId, _msgSender(), newAdmin);
Expand All @@ -82,7 +83,7 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
function updateGroupMerkleTreeDuration(
uint256 groupId,
uint256 newMerkleTreeDuration
) external override onlyGroupAdmin(groupId) {
) external override onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
uint256 oldMerkleTreeDuration = groups[groupId].merkleTreeDuration;

groups[groupId].merkleTreeDuration = newMerkleTreeDuration;
Expand All @@ -91,7 +92,10 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
}

/// @dev See {ISemaphore-addMember}.
function addMember(uint256 groupId, uint256 identityCommitment) external override onlyGroupAdmin(groupId) {
function addMember(
uint256 groupId,
uint256 identityCommitment
) external override onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
_addMember(groupId, identityCommitment);

uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
Expand All @@ -103,7 +107,7 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
function addMembers(
uint256 groupId,
uint256[] calldata identityCommitments
) external override onlyGroupAdmin(groupId) {
) external override onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
for (uint256 i = 0; i < identityCommitments.length; ) {
_addMember(groupId, identityCommitments[i]);

Expand All @@ -122,10 +126,9 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
uint256 groupId,
uint256 identityCommitment,
uint256 newIdentityCommitment,
uint256[] calldata proofSiblings,
uint8[] calldata proofPathIndices
) external override onlyGroupAdmin(groupId) {
_updateMember(groupId, identityCommitment, newIdentityCommitment, proofSiblings, proofPathIndices);
uint256[] calldata merkleProofSiblings
) external override onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
_updateMember(groupId, identityCommitment, newIdentityCommitment, merkleProofSiblings);

uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);

Expand All @@ -136,10 +139,9 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
function removeMember(
uint256 groupId,
uint256 identityCommitment,
uint256[] calldata proofSiblings,
uint8[] calldata proofPathIndices
) external override onlyGroupAdmin(groupId) {
_removeMember(groupId, identityCommitment, proofSiblings, proofPathIndices);
uint256[] calldata merkleProofSiblings
) external override onlyExistingGroup(groupId) onlyGroupAdmin(groupId) {
_removeMember(groupId, identityCommitment, merkleProofSiblings);

uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);

Expand All @@ -150,15 +152,15 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
function verifyProof(
uint256 groupId,
uint256 merkleTreeRoot,
uint256 signal,
uint256 nullifierHash,
uint256 externalNullifier,
uint256 message,
uint256 nullifier,
uint256 scope,
uint256[8] calldata proof
) external override {
) external override onlyExistingGroup(groupId) {
uint256 merkleTreeDepth = getMerkleTreeDepth(groupId);

if (merkleTreeDepth == 0) {
revert Semaphore__GroupDoesNotExist();
revert Semaphore__GroupHasNoMembers();
}

uint256 currentMerkleTreeRoot = getMerkleTreeRoot(groupId);
Expand All @@ -178,14 +180,14 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
}
}

if (groups[groupId].nullifierHashes[nullifierHash]) {
if (groups[groupId].nullifiers[nullifier]) {
revert Semaphore__YouAreUsingTheSameNillifierTwice();
}

verifier.verifyProof(merkleTreeRoot, nullifierHash, signal, externalNullifier, proof, merkleTreeDepth);
verifier.verifyProof(merkleTreeRoot, nullifier, message, scope, proof);

groups[groupId].nullifierHashes[nullifierHash] = true;
groups[groupId].nullifiers[nullifier] = true;

emit ProofVerified(groupId, merkleTreeRoot, nullifierHash, externalNullifier, signal);
emit ProofVerified(groupId, merkleTreeRoot, nullifier, scope, message, proof);
}
}
119 changes: 35 additions & 84 deletions packages/contracts/contracts/base/SemaphoreGroups.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,137 +2,88 @@
pragma solidity 0.8.4;

import "../interfaces/ISemaphoreGroups.sol";
import "@zk-kit/incremental-merkle-tree.sol/IncrementalBinaryTree.sol";
import {InternalLeanIMT, LeanIMTData} from "@zk-kit/imt.sol/internal/InternalLeanIMT.sol";
import "@openzeppelin/contracts/utils/Context.sol";

/// @title Semaphore groups contract.
/// @dev This contract allows you to create groups, add, remove and update members.
/// You can use getters to obtain informations about groups (root, depth, number of leaves).
abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
using IncrementalBinaryTree for IncrementalTreeData;
using InternalLeanIMT for LeanIMTData;

/// @dev Gets a group id and returns the tree data.
mapping(uint256 => IncrementalTreeData) internal merkleTrees;

/// @dev Creates a new group by initializing the associated tree.
/// @param groupId: Id of the group.
/// @param merkleTreeDepth: Depth of the tree.
function _createGroup(uint256 groupId, uint256 merkleTreeDepth) internal virtual {
if (getMerkleTreeDepth(groupId) != 0) {
revert Semaphore__GroupAlreadyExists();
}

// The zeroValue is an implicit member of the group, or an implicit leaf of the Merkle tree.
// Although there is a remote possibility that the preimage of
// the hash may be calculated, using this value we aim to minimize the risk.
uint256 zeroValue = uint256(keccak256(abi.encodePacked(groupId))) >> 8;

merkleTrees[groupId].init(merkleTreeDepth, zeroValue);

emit GroupCreated(groupId, merkleTreeDepth, zeroValue);
}
mapping(uint256 => LeanIMTData) internal merkleTrees;

/// @dev Adds an identity commitment to an existing group.
/// @param groupId: Id of the group.
/// @param identityCommitment: New identity commitment.
function _addMember(uint256 groupId, uint256 identityCommitment) internal virtual {
if (getMerkleTreeDepth(groupId) == 0) {
revert Semaphore__GroupDoesNotExist();
}

merkleTrees[groupId].insert(identityCommitment);

uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
uint256 index = getNumberOfMerkleTreeLeaves(groupId) - 1;
uint256 merkleTreeRoot = merkleTrees[groupId]._insert(identityCommitment);
uint256 leafIndex = getMerkleTreeSize(groupId) - 1;

emit MemberAdded(groupId, index, identityCommitment, merkleTreeRoot);
emit MemberAdded(groupId, leafIndex, identityCommitment, merkleTreeRoot);
}

/// @dev Updates an identity commitment of an existing group. A proof of membership is
/// needed to check if the node to be updated is part of the tree.
/// @param groupId: Id of the group.
/// @param identityCommitment: Existing identity commitment to be updated.
/// @param oldIdentityCommitment: Existing identity commitment to be updated.
/// @param newIdentityCommitment: New identity commitment.
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
/// @param proofPathIndices: Path of the proof of membership.
/// @param merkleProofSiblings: Array of the sibling nodes of the proof of membership.
function _updateMember(
uint256 groupId,
uint256 identityCommitment,
uint256 oldIdentityCommitment,
uint256 newIdentityCommitment,
uint256[] calldata proofSiblings,
uint8[] calldata proofPathIndices
uint256[] calldata merkleProofSiblings
) internal virtual {
if (getMerkleTreeDepth(groupId) == 0) {
revert Semaphore__GroupDoesNotExist();
}

merkleTrees[groupId].update(identityCommitment, newIdentityCommitment, proofSiblings, proofPathIndices);

uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
uint256 index = proofPathIndicesToMemberIndex(proofPathIndices);

emit MemberUpdated(groupId, index, identityCommitment, newIdentityCommitment, merkleTreeRoot);
uint256 merkleTreeRoot = merkleTrees[groupId]._update(
oldIdentityCommitment,
newIdentityCommitment,
merkleProofSiblings
);
uint256 leafIndex = merkleTrees[groupId]._indexOf(newIdentityCommitment);

emit MemberUpdated(groupId, leafIndex, oldIdentityCommitment, newIdentityCommitment, merkleTreeRoot);
}

/// @dev Removes an identity commitment from an existing group. A proof of membership is
/// needed to check if the node to be deleted is part of the tree.
/// @param groupId: Id of the group.
/// @param identityCommitment: Existing identity commitment to be removed.
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
/// @param proofPathIndices: Path of the proof of membership.
/// @param merkleProofSiblings: Array of the sibling nodes of the proof of membership.
function _removeMember(
uint256 groupId,
uint256 identityCommitment,
uint256[] calldata proofSiblings,
uint8[] calldata proofPathIndices
uint256[] calldata merkleProofSiblings
) internal virtual {
if (getMerkleTreeDepth(groupId) == 0) {
revert Semaphore__GroupDoesNotExist();
}
uint256 leafIndex = merkleTrees[groupId]._indexOf(identityCommitment);
uint256 merkleTreeRoot = merkleTrees[groupId]._remove(identityCommitment, merkleProofSiblings);

merkleTrees[groupId].remove(identityCommitment, proofSiblings, proofPathIndices);
emit MemberRemoved(groupId, leafIndex, identityCommitment, merkleTreeRoot);
}

uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
uint256 index = proofPathIndicesToMemberIndex(proofPathIndices);
/// @dev See {ISemaphoreGroups-hasMember}.
function hasMember(uint256 groupId, uint256 identityCommitment) public view virtual override returns (bool) {
return merkleTrees[groupId]._has(identityCommitment);
}

emit MemberRemoved(groupId, index, identityCommitment, merkleTreeRoot);
/// @dev See {ISemaphoreGroups-indexOf}.
function indexOf(uint256 groupId, uint256 identityCommitment) public view virtual override returns (uint256) {
return merkleTrees[groupId]._indexOf(identityCommitment);
}

/// @dev See {ISemaphoreGroups-getMerkleTreeRoot}.
function getMerkleTreeRoot(uint256 groupId) public view virtual override returns (uint256) {
return merkleTrees[groupId].root;
return merkleTrees[groupId]._root();
}

/// @dev See {ISemaphoreGroups-getMerkleTreeDepth}.
function getMerkleTreeDepth(uint256 groupId) public view virtual override returns (uint256) {
return merkleTrees[groupId].depth;
}

/// @dev See {ISemaphoreGroups-getNumberOfMerkleTreeLeaves}.
function getNumberOfMerkleTreeLeaves(uint256 groupId) public view virtual override returns (uint256) {
return merkleTrees[groupId].numberOfLeaves;
}

/// @dev Converts the path indices of a Merkle proof to the identity commitment index in the tree.
/// @param proofPathIndices: Path of the proof of membership.
/// @return Index of a group member.
function proofPathIndicesToMemberIndex(uint8[] calldata proofPathIndices) private pure returns (uint256) {
uint256 memberIndex = 0;

for (uint8 i = uint8(proofPathIndices.length); i > 0; ) {
if (memberIndex > 0 || proofPathIndices[i - 1] != 0) {
memberIndex *= 2;

if (proofPathIndices[i - 1] == 1) {
memberIndex += 1;
}
}

unchecked {
--i;
}
}

return memberIndex;
/// @dev See {ISemaphoreGroups-getMerkleTreeSize}.
function getMerkleTreeSize(uint256 groupId) public view virtual override returns (uint256) {
return merkleTrees[groupId].size;
}
}
20 changes: 10 additions & 10 deletions packages/contracts/contracts/base/SemaphoreVerifier.sol

Large diffs are not rendered by default.

Loading

0 comments on commit cc32fee

Please sign in to comment.