This repository has been archived by the owner on Oct 28, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SemaphoreIdentity.sol
224 lines (186 loc) · 7.14 KB
/
SemaphoreIdentity.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
import "@semaphore-protocol/contracts/interfaces/ISemaphoreVerifier.sol";
import "@semaphore-protocol/contracts/base/SemaphoreGroups.sol";
/// @title SemaphoreIdentity
/// @dev This contract uses the Semaphore base contracts to provide a complete service
/// to allow admins to create and manage groups and their members to generate Semaphore proofs
/// and verify them. Group admins can add, update or remove group members after off-chain verification,
/// and can be an Ethereum account or a smart contract. This contract also assigns each new Merkle tree
/// generated with a new root a duration (or an expiry) within which the proofs generated with that root
/// can be validated.
contract SemaphoreIdentity is ISemaphore, SemaphoreGroups {
ISemaphoreVerifier public verifier;
/// @dev Gets a group id and returns the group parameters.
mapping(uint256 => Group) public groups;
/// @dev Checks if the group admin is the transaction sender.
/// @param groupId: Id of the group.
modifier onlyGroupAdmin(uint256 groupId) {
if (groups[groupId].admin != _msgSender()) {
revert Semaphore__CallerIsNotTheGroupAdmin();
}
_;
}
/// @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 Initializes the Semaphore verifier used to verify the user's ZK proofs.
/// @param _verifier: Semaphore verifier address.
constructor(ISemaphoreVerifier _verifier) {
verifier = _verifier;
}
/// @dev See {ISemaphore-createGroup}.
function createGroup(
uint256 groupId,
uint256 merkleTreeDepth,
address admin
) external override onlySupportedMerkleTreeDepth(merkleTreeDepth) {
_createGroup(groupId, merkleTreeDepth);
groups[groupId].admin = admin;
groups[groupId].merkleTreeDuration = 1 minutes;
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);
groups[groupId].admin = admin;
groups[groupId].merkleTreeDuration = merkleTreeDuration;
emit GroupAdminUpdated(groupId, address(0), admin);
}
/// @dev See {ISemaphore-updateGroupAdmin}.
function updateGroupAdmin(
uint256 groupId,
address newAdmin
) external override onlyGroupAdmin(groupId) {
groups[groupId].admin = newAdmin;
emit GroupAdminUpdated(groupId, _msgSender(), newAdmin);
}
/// @dev See {ISemaphore-updateGroupMerkleTreeDuration}.
function updateGroupMerkleTreeDuration(
uint256 groupId,
uint256 newMerkleTreeDuration
) external override onlyGroupAdmin(groupId) {
uint256 oldMerkleTreeDuration = groups[groupId].merkleTreeDuration;
groups[groupId].merkleTreeDuration = newMerkleTreeDuration;
emit GroupMerkleTreeDurationUpdated(
groupId,
oldMerkleTreeDuration,
newMerkleTreeDuration
);
}
/// @dev See {ISemaphore-addMember}.
function addMember(
uint256 groupId,
uint256 identityCommitment
) external override onlyGroupAdmin(groupId) {
_addMember(groupId, identityCommitment);
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block
.timestamp;
}
/// @dev See {ISemaphore-addMembers}.
function addMembers(
uint256 groupId,
uint256[] calldata identityCommitments
) external override onlyGroupAdmin(groupId) {
for (uint256 i = 0; i < identityCommitments.length; ) {
_addMember(groupId, identityCommitments[i]);
unchecked {
++i;
}
}
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block
.timestamp;
}
/// @dev See {ISemaphore-updateMember}.
function updateMember(
uint256 groupId,
uint256 identityCommitment,
uint256 newIdentityCommitment,
uint256[] calldata proofSiblings,
uint8[] calldata proofPathIndices
) external override onlyGroupAdmin(groupId) {
_updateMember(
groupId,
identityCommitment,
newIdentityCommitment,
proofSiblings,
proofPathIndices
);
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block
.timestamp;
}
/// @dev See {ISemaphore-removeMember}.
function removeMember(
uint256 groupId,
uint256 identityCommitment,
uint256[] calldata proofSiblings,
uint8[] calldata proofPathIndices
) external override onlyGroupAdmin(groupId) {
_removeMember(
groupId,
identityCommitment,
proofSiblings,
proofPathIndices
);
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block
.timestamp;
}
/// @dev See {ISemaphore-verifyProof}.
function verifyProof(
uint256 groupId,
uint256 merkleTreeRoot,
uint256 signal,
uint256 nullifierHash,
uint256 externalNullifier,
uint256[8] calldata proof
) external override {
uint256 merkleTreeDepth = getMerkleTreeDepth(groupId);
if (merkleTreeDepth == 0) {
revert Semaphore__GroupDoesNotExist();
}
uint256 currentMerkleTreeRoot = getMerkleTreeRoot(groupId);
// A proof could have used an old Merkle tree root.
// https://github.com/semaphore-protocol/semaphore/issues/98
if (merkleTreeRoot != currentMerkleTreeRoot) {
uint256 merkleRootCreationDate = groups[groupId]
.merkleRootCreationDates[merkleTreeRoot];
uint256 merkleTreeDuration = groups[groupId].merkleTreeDuration;
if (merkleRootCreationDate == 0) {
revert Semaphore__MerkleTreeRootIsNotPartOfTheGroup();
}
if (block.timestamp > merkleRootCreationDate + merkleTreeDuration) {
revert Semaphore__MerkleTreeRootIsExpired();
}
}
verifier.verifyProof(
merkleTreeRoot,
nullifierHash,
signal,
externalNullifier,
proof,
merkleTreeDepth
);
emit ProofVerified(
groupId,
merkleTreeRoot,
nullifierHash,
externalNullifier,
signal
);
}
}