@semaphore-protocol/contracts 4.14.2 → 4.14.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Semaphore.sol CHANGED
@@ -10,8 +10,8 @@ import {MIN_DEPTH, MAX_DEPTH} from "./base/Constants.sol";
10
10
  /// @dev This contract uses the Semaphore base contracts to provide a complete service
11
11
  /// to allow admins to create and manage groups and their members to verify Semaphore proofs
12
12
  /// Group admins can add, update or remove group members, and can be an Ethereum account or a smart contract.
13
- /// This contract also assigns each new Merkle tree generated with a new root a duration (or an expiry)
14
- /// within which the proofs generated with that root can be validated.
13
+ /// This contract also gives each root a duration (or an expiry) within which the proofs generated with
14
+ /// that root can still be validated after the root has been replaced by a newer one.
15
15
  contract Semaphore is ISemaphore, SemaphoreGroups {
16
16
  ISemaphoreVerifier public verifier;
17
17
 
@@ -76,16 +76,26 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
76
76
 
77
77
  /// @dev See {SemaphoreGroups-_addMember}.
78
78
  function addMember(uint256 groupId, uint256 identityCommitment) external override {
79
- uint256 merkleTreeRoot = _addMember(groupId, identityCommitment);
79
+ uint256 oldMerkleTreeRoot = getMerkleTreeRoot(groupId);
80
80
 
81
- groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
81
+ _addMember(groupId, identityCommitment);
82
+
83
+ // 0 marks a root as unknown and is also the root of a group with no members,
84
+ // so it is never stored.
85
+ if (oldMerkleTreeRoot != 0) {
86
+ groups[groupId].merkleRootSupersededDates[oldMerkleTreeRoot] = block.timestamp;
87
+ }
82
88
  }
83
89
 
84
90
  /// @dev See {SemaphoreGroups-_addMembers}.
85
91
  function addMembers(uint256 groupId, uint256[] calldata identityCommitments) external override {
86
- uint256 merkleTreeRoot = _addMembers(groupId, identityCommitments);
92
+ uint256 oldMerkleTreeRoot = getMerkleTreeRoot(groupId);
93
+
94
+ _addMembers(groupId, identityCommitments);
87
95
 
88
- groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
96
+ if (oldMerkleTreeRoot != 0) {
97
+ groups[groupId].merkleRootSupersededDates[oldMerkleTreeRoot] = block.timestamp;
98
+ }
89
99
  }
90
100
 
91
101
  /// @dev See {SemaphoreGroups-_updateMember}.
@@ -95,9 +105,12 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
95
105
  uint256 newIdentityCommitment,
96
106
  uint256[] calldata merkleProofSiblings
97
107
  ) external override {
98
- uint256 merkleTreeRoot = _updateMember(groupId, identityCommitment, newIdentityCommitment, merkleProofSiblings);
108
+ uint256 oldMerkleTreeRoot = getMerkleTreeRoot(groupId);
109
+
110
+ _updateMember(groupId, identityCommitment, newIdentityCommitment, merkleProofSiblings);
99
111
 
100
- groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
112
+ // A group whose root is 0 has no members to update, so the old root is never 0.
113
+ groups[groupId].merkleRootSupersededDates[oldMerkleTreeRoot] = block.timestamp;
101
114
  }
102
115
 
103
116
  /// @dev See {SemaphoreGroups-_removeMember}.
@@ -106,9 +119,12 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
106
119
  uint256 identityCommitment,
107
120
  uint256[] calldata merkleProofSiblings
108
121
  ) external override {
109
- uint256 merkleTreeRoot = _removeMember(groupId, identityCommitment, merkleProofSiblings);
122
+ uint256 oldMerkleTreeRoot = getMerkleTreeRoot(groupId);
123
+
124
+ _removeMember(groupId, identityCommitment, merkleProofSiblings);
110
125
 
111
- groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
126
+ // A group whose root is 0 has no members to remove, so the old root is never 0.
127
+ groups[groupId].merkleRootSupersededDates[oldMerkleTreeRoot] = block.timestamp;
112
128
  }
113
129
 
114
130
  /// @dev See {ISemaphore-validateProof}.
@@ -164,14 +180,19 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
164
180
  // A proof could have used an old Merkle tree root.
165
181
  // https://github.com/semaphore-protocol/semaphore/issues/98
166
182
  if (proof.merkleTreeRoot != currentMerkleTreeRoot) {
167
- uint256 merkleRootCreationDate = groups[groupId].merkleRootCreationDates[proof.merkleTreeRoot];
183
+ // An old root is accepted until merkleTreeDuration has passed since it was replaced,
184
+ // giving proofs made while it was still the current root time to arrive. The end of
185
+ // the window is exclusive, so a duration of 0 rejects every old root.
186
+ uint256 merkleRootSupersededDate = groups[groupId].merkleRootSupersededDates[proof.merkleTreeRoot];
168
187
  uint256 merkleTreeDuration = groups[groupId].merkleTreeDuration;
169
188
 
170
- if (merkleRootCreationDate == 0) {
189
+ if (merkleRootSupersededDate == 0) {
171
190
  revert Semaphore__MerkleTreeRootIsNotPartOfTheGroup();
172
191
  }
173
192
 
174
- if (block.timestamp > merkleRootCreationDate + merkleTreeDuration) {
193
+ // merkleRootSupersededDate is never in the future, so this cannot underflow
194
+ // and the check stays within uint256 for any duration.
195
+ if (block.timestamp - merkleRootSupersededDate >= merkleTreeDuration) {
175
196
  revert Semaphore__MerkleTreeRootIsExpired();
176
197
  }
177
198
  }
@@ -13,7 +13,10 @@ interface ISemaphore {
13
13
  /// It defines all the group parameters used by Semaphore.sol.
14
14
  struct Group {
15
15
  uint256 merkleTreeDuration;
16
- mapping(uint256 => uint256) merkleRootCreationDates;
16
+ /// @dev Gets a Merkle root and returns the time at which it was replaced, which is when
17
+ /// its expiration starts. The current root is not in this mapping, as its expiration
18
+ /// has not started yet.
19
+ mapping(uint256 => uint256) merkleRootSupersededDates;
17
20
  mapping(uint256 => bool) nullifiers;
18
21
  }
19
22
 
@@ -78,6 +81,10 @@ interface ISemaphore {
78
81
  function acceptGroupAdmin(uint256 groupId) external;
79
82
 
80
83
  /// @dev Updates the group Merkle tree duration.
84
+ /// The new duration applies to every past root of the group, not only to roots superseded
85
+ /// after this call, so raising it makes older roots valid again and lowering it invalidates
86
+ /// proofs that are already in flight. It is also how long a removed member can keep proving
87
+ /// membership with an older root, so it should be kept as short as the group can tolerate.
81
88
  /// @param groupId: Id of the group.
82
89
  /// @param newMerkleTreeDuration: New Merkle tree duration.
83
90
  function updateGroupMerkleTreeDuration(uint256 groupId, uint256 newMerkleTreeDuration) external;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@semaphore-protocol/contracts",
3
- "version": "4.14.2",
3
+ "version": "4.14.4",
4
4
  "description": "Semaphore contracts to manage groups and broadcast anonymous signals.",
5
5
  "license": "MIT",
6
6
  "files": [