@semaphore-protocol/contracts 2.0.0 → 2.1.0
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 +75 -15
- package/base/SemaphoreConstants.sol +1 -1
- package/base/SemaphoreCore.sol +4 -2
- package/base/SemaphoreGroups.sol +55 -21
- package/extensions/SemaphoreVoting.sol +37 -28
- package/extensions/SemaphoreWhistleblowing.sol +25 -24
- package/interfaces/ISemaphore.sol +47 -4
- package/interfaces/ISemaphoreCore.sol +3 -1
- package/interfaces/ISemaphoreGroups.sol +27 -11
- package/interfaces/ISemaphoreNullifiers.sol +1 -1
- package/interfaces/ISemaphoreVoting.sol +13 -3
- package/interfaces/ISemaphoreWhistleblowing.sol +11 -3
- package/interfaces/IVerifier.sol +1 -1
- package/package.json +3 -3
package/Semaphore.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.4;
|
|
3
3
|
|
|
4
4
|
import "./interfaces/ISemaphore.sol";
|
|
5
5
|
import "./interfaces/IVerifier.sol";
|
|
@@ -9,43 +9,71 @@ import "./base/SemaphoreGroups.sol";
|
|
|
9
9
|
/// @title Semaphore
|
|
10
10
|
contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
|
11
11
|
/// @dev Gets a tree depth and returns its verifier address.
|
|
12
|
-
mapping(
|
|
12
|
+
mapping(uint256 => IVerifier) public verifiers;
|
|
13
13
|
|
|
14
14
|
/// @dev Gets a group id and returns the group admin address.
|
|
15
15
|
mapping(uint256 => address) public groupAdmins;
|
|
16
16
|
|
|
17
|
+
/// @dev Gets a group id and returns data to check if a Merkle root is expired.
|
|
18
|
+
mapping(uint256 => MerkleTreeExpiry) public merkleTreeExpiries;
|
|
19
|
+
|
|
17
20
|
/// @dev Checks if the group admin is the transaction sender.
|
|
18
21
|
/// @param groupId: Id of the group.
|
|
19
22
|
modifier onlyGroupAdmin(uint256 groupId) {
|
|
20
|
-
|
|
23
|
+
if (groupAdmins[groupId] != _msgSender()) {
|
|
24
|
+
revert Semaphore__CallerIsNotTheGroupAdmin();
|
|
25
|
+
}
|
|
21
26
|
_;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
/// @dev Checks if there is a verifier for the given tree depth.
|
|
25
|
-
/// @param
|
|
26
|
-
modifier
|
|
27
|
-
|
|
30
|
+
/// @param merkleTreeDepth: Depth of the tree.
|
|
31
|
+
modifier onlySupportedMerkleTreeDepth(uint256 merkleTreeDepth) {
|
|
32
|
+
if (address(verifiers[merkleTreeDepth]) == address(0)) {
|
|
33
|
+
revert Semaphore__MerkleTreeDepthIsNotSupported();
|
|
34
|
+
}
|
|
28
35
|
_;
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
/// @dev Initializes the Semaphore verifiers used to verify the user's ZK proofs.
|
|
32
39
|
/// @param _verifiers: List of Semaphore verifiers (address and related Merkle tree depth).
|
|
33
40
|
constructor(Verifier[] memory _verifiers) {
|
|
34
|
-
for (uint8 i = 0; i < _verifiers.length;
|
|
41
|
+
for (uint8 i = 0; i < _verifiers.length; ) {
|
|
35
42
|
verifiers[_verifiers[i].merkleTreeDepth] = IVerifier(_verifiers[i].contractAddress);
|
|
43
|
+
|
|
44
|
+
unchecked {
|
|
45
|
+
++i;
|
|
46
|
+
}
|
|
36
47
|
}
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
/// @dev See {ISemaphore-createGroup}.
|
|
40
51
|
function createGroup(
|
|
41
52
|
uint256 groupId,
|
|
42
|
-
|
|
53
|
+
uint256 merkleTreeDepth,
|
|
43
54
|
uint256 zeroValue,
|
|
44
55
|
address admin
|
|
45
|
-
) external override
|
|
46
|
-
_createGroup(groupId,
|
|
56
|
+
) external override onlySupportedMerkleTreeDepth(merkleTreeDepth) {
|
|
57
|
+
_createGroup(groupId, merkleTreeDepth, zeroValue);
|
|
47
58
|
|
|
48
59
|
groupAdmins[groupId] = admin;
|
|
60
|
+
merkleTreeExpiries[groupId].rootDuration = 1 hours;
|
|
61
|
+
|
|
62
|
+
emit GroupAdminUpdated(groupId, address(0), admin);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// @dev See {ISemaphore-createGroup}.
|
|
66
|
+
function createGroup(
|
|
67
|
+
uint256 groupId,
|
|
68
|
+
uint256 merkleTreeDepth,
|
|
69
|
+
uint256 zeroValue,
|
|
70
|
+
address admin,
|
|
71
|
+
uint256 merkleTreeRootDuration
|
|
72
|
+
) external override onlySupportedMerkleTreeDepth(merkleTreeDepth) {
|
|
73
|
+
_createGroup(groupId, merkleTreeDepth, zeroValue);
|
|
74
|
+
|
|
75
|
+
groupAdmins[groupId] = admin;
|
|
76
|
+
merkleTreeExpiries[groupId].rootDuration = merkleTreeRootDuration;
|
|
49
77
|
|
|
50
78
|
emit GroupAdminUpdated(groupId, address(0), admin);
|
|
51
79
|
}
|
|
@@ -60,6 +88,21 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
|
|
60
88
|
/// @dev See {ISemaphore-addMember}.
|
|
61
89
|
function addMember(uint256 groupId, uint256 identityCommitment) external override onlyGroupAdmin(groupId) {
|
|
62
90
|
_addMember(groupId, identityCommitment);
|
|
91
|
+
|
|
92
|
+
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
|
93
|
+
|
|
94
|
+
merkleTreeExpiries[groupId].rootCreationDates[merkleTreeRoot] = block.timestamp;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// @dev See {ISemaphore-updateMember}.
|
|
98
|
+
function updateMember(
|
|
99
|
+
uint256 groupId,
|
|
100
|
+
uint256 identityCommitment,
|
|
101
|
+
uint256 newIdentityCommitment,
|
|
102
|
+
uint256[] calldata proofSiblings,
|
|
103
|
+
uint8[] calldata proofPathIndices
|
|
104
|
+
) external override onlyGroupAdmin(groupId) {
|
|
105
|
+
_updateMember(groupId, identityCommitment, newIdentityCommitment, proofSiblings, proofPathIndices);
|
|
63
106
|
}
|
|
64
107
|
|
|
65
108
|
/// @dev See {ISemaphore-removeMember}.
|
|
@@ -75,19 +118,36 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
|
|
|
75
118
|
/// @dev See {ISemaphore-verifyProof}.
|
|
76
119
|
function verifyProof(
|
|
77
120
|
uint256 groupId,
|
|
121
|
+
uint256 merkleTreeRoot,
|
|
78
122
|
bytes32 signal,
|
|
79
123
|
uint256 nullifierHash,
|
|
80
124
|
uint256 externalNullifier,
|
|
81
125
|
uint256[8] calldata proof
|
|
82
126
|
) external override {
|
|
83
|
-
uint256
|
|
84
|
-
|
|
127
|
+
uint256 currentMerkleTreeRoot = getMerkleTreeRoot(groupId);
|
|
128
|
+
|
|
129
|
+
if (currentMerkleTreeRoot == 0) {
|
|
130
|
+
revert Semaphore__GroupDoesNotExist();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (merkleTreeRoot != currentMerkleTreeRoot) {
|
|
134
|
+
uint256 rootCreationDate = merkleTreeExpiries[groupId].rootCreationDates[merkleTreeRoot];
|
|
135
|
+
uint256 rootDuration = merkleTreeExpiries[groupId].rootDuration;
|
|
136
|
+
|
|
137
|
+
if (rootCreationDate == 0) {
|
|
138
|
+
revert Semaphore__MerkleTreeRootIsNotPartOfTheGroup();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (block.timestamp > rootCreationDate + rootDuration) {
|
|
142
|
+
revert Semaphore__MerkleTreeRootIsExpired();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
85
145
|
|
|
86
|
-
|
|
146
|
+
uint256 merkleTreeDepth = getMerkleTreeDepth(groupId);
|
|
87
147
|
|
|
88
|
-
IVerifier verifier = verifiers[
|
|
148
|
+
IVerifier verifier = verifiers[merkleTreeDepth];
|
|
89
149
|
|
|
90
|
-
_verifyProof(signal,
|
|
150
|
+
_verifyProof(signal, merkleTreeRoot, nullifierHash, externalNullifier, proof, verifier);
|
|
91
151
|
|
|
92
152
|
_saveNullifierHash(nullifierHash);
|
|
93
153
|
|
package/base/SemaphoreCore.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.4;
|
|
3
3
|
|
|
4
4
|
import "../interfaces/ISemaphoreCore.sol";
|
|
5
5
|
import "../interfaces/IVerifier.sol";
|
|
@@ -30,7 +30,9 @@ contract SemaphoreCore is ISemaphoreCore {
|
|
|
30
30
|
uint256[8] calldata proof,
|
|
31
31
|
IVerifier verifier
|
|
32
32
|
) internal view {
|
|
33
|
-
|
|
33
|
+
if (nullifierHashes[nullifierHash]) {
|
|
34
|
+
revert Semaphore__YouAreUsingTheSameNillifierTwice();
|
|
35
|
+
}
|
|
34
36
|
|
|
35
37
|
uint256 signalHash = _hashSignal(signal);
|
|
36
38
|
|
package/base/SemaphoreGroups.sol
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
//SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.4;
|
|
3
3
|
|
|
4
4
|
import {SNARK_SCALAR_FIELD} from "./SemaphoreConstants.sol";
|
|
5
5
|
import "../interfaces/ISemaphoreGroups.sol";
|
|
6
|
-
import "@zk-kit/incremental-merkle-tree.sol/
|
|
6
|
+
import "@zk-kit/incremental-merkle-tree.sol/IncrementalBinaryTree.sol";
|
|
7
7
|
import "@openzeppelin/contracts/utils/Context.sol";
|
|
8
8
|
|
|
9
9
|
/// @title Semaphore groups contract.
|
|
@@ -17,38 +17,70 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
|
|
17
17
|
|
|
18
18
|
/// @dev Creates a new group by initializing the associated tree.
|
|
19
19
|
/// @param groupId: Id of the group.
|
|
20
|
-
/// @param
|
|
20
|
+
/// @param merkleTreeDepth: Depth of the tree.
|
|
21
21
|
/// @param zeroValue: Zero value of the tree.
|
|
22
22
|
function _createGroup(
|
|
23
23
|
uint256 groupId,
|
|
24
|
-
|
|
24
|
+
uint256 merkleTreeDepth,
|
|
25
25
|
uint256 zeroValue
|
|
26
26
|
) internal virtual {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
if (groupId >= SNARK_SCALAR_FIELD) {
|
|
28
|
+
revert Semaphore__GroupIdIsNotLessThanSnarkScalarField();
|
|
29
|
+
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
if (getMerkleTreeDepth(groupId) != 0) {
|
|
32
|
+
revert Semaphore__GroupAlreadyExists();
|
|
33
|
+
}
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
groups[groupId].init(merkleTreeDepth, zeroValue);
|
|
36
|
+
|
|
37
|
+
emit GroupCreated(groupId, merkleTreeDepth, zeroValue);
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
/// @dev Adds an identity commitment to an existing group.
|
|
36
41
|
/// @param groupId: Id of the group.
|
|
37
42
|
/// @param identityCommitment: New identity commitment.
|
|
38
43
|
function _addMember(uint256 groupId, uint256 identityCommitment) internal virtual {
|
|
39
|
-
|
|
44
|
+
if (getMerkleTreeDepth(groupId) == 0) {
|
|
45
|
+
revert Semaphore__GroupDoesNotExist();
|
|
46
|
+
}
|
|
40
47
|
|
|
41
48
|
groups[groupId].insert(identityCommitment);
|
|
42
49
|
|
|
43
|
-
uint256
|
|
50
|
+
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
|
51
|
+
|
|
52
|
+
emit MemberAdded(groupId, identityCommitment, merkleTreeRoot);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// @dev Updates an identity commitment of an existing group. A proof of membership is
|
|
56
|
+
/// needed to check if the node to be updated is part of the tree.
|
|
57
|
+
/// @param groupId: Id of the group.
|
|
58
|
+
/// @param identityCommitment: Existing identity commitment to be updated.
|
|
59
|
+
/// @param newIdentityCommitment: New identity commitment.
|
|
60
|
+
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
|
|
61
|
+
/// @param proofPathIndices: Path of the proof of membership.
|
|
62
|
+
function _updateMember(
|
|
63
|
+
uint256 groupId,
|
|
64
|
+
uint256 identityCommitment,
|
|
65
|
+
uint256 newIdentityCommitment,
|
|
66
|
+
uint256[] calldata proofSiblings,
|
|
67
|
+
uint8[] calldata proofPathIndices
|
|
68
|
+
) internal virtual {
|
|
69
|
+
if (getMerkleTreeRoot(groupId) == 0) {
|
|
70
|
+
revert Semaphore__GroupDoesNotExist();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
groups[groupId].update(identityCommitment, newIdentityCommitment, proofSiblings, proofPathIndices);
|
|
74
|
+
|
|
75
|
+
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
|
44
76
|
|
|
45
|
-
emit
|
|
77
|
+
emit MemberUpdated(groupId, identityCommitment, newIdentityCommitment, merkleTreeRoot);
|
|
46
78
|
}
|
|
47
79
|
|
|
48
80
|
/// @dev Removes an identity commitment from an existing group. A proof of membership is
|
|
49
81
|
/// needed to check if the node to be deleted is part of the tree.
|
|
50
82
|
/// @param groupId: Id of the group.
|
|
51
|
-
/// @param identityCommitment: Existing identity commitment to be
|
|
83
|
+
/// @param identityCommitment: Existing identity commitment to be removed.
|
|
52
84
|
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
|
|
53
85
|
/// @param proofPathIndices: Path of the proof of membership.
|
|
54
86
|
function _removeMember(
|
|
@@ -57,27 +89,29 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
|
|
|
57
89
|
uint256[] calldata proofSiblings,
|
|
58
90
|
uint8[] calldata proofPathIndices
|
|
59
91
|
) internal virtual {
|
|
60
|
-
|
|
92
|
+
if (getMerkleTreeRoot(groupId) == 0) {
|
|
93
|
+
revert Semaphore__GroupDoesNotExist();
|
|
94
|
+
}
|
|
61
95
|
|
|
62
96
|
groups[groupId].remove(identityCommitment, proofSiblings, proofPathIndices);
|
|
63
97
|
|
|
64
|
-
uint256
|
|
98
|
+
uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);
|
|
65
99
|
|
|
66
|
-
emit MemberRemoved(groupId, identityCommitment,
|
|
100
|
+
emit MemberRemoved(groupId, identityCommitment, merkleTreeRoot);
|
|
67
101
|
}
|
|
68
102
|
|
|
69
|
-
/// @dev See {ISemaphoreGroups-
|
|
70
|
-
function
|
|
103
|
+
/// @dev See {ISemaphoreGroups-getMerkleTreeRoot}.
|
|
104
|
+
function getMerkleTreeRoot(uint256 groupId) public view virtual override returns (uint256) {
|
|
71
105
|
return groups[groupId].root;
|
|
72
106
|
}
|
|
73
107
|
|
|
74
|
-
/// @dev See {ISemaphoreGroups-
|
|
75
|
-
function
|
|
108
|
+
/// @dev See {ISemaphoreGroups-getMerkleTreeDepth}.
|
|
109
|
+
function getMerkleTreeDepth(uint256 groupId) public view virtual override returns (uint256) {
|
|
76
110
|
return groups[groupId].depth;
|
|
77
111
|
}
|
|
78
112
|
|
|
79
|
-
/// @dev See {ISemaphoreGroups-
|
|
80
|
-
function
|
|
113
|
+
/// @dev See {ISemaphoreGroups-getNumberOfMerkleTreeLeaves}.
|
|
114
|
+
function getNumberOfMerkleTreeLeaves(uint256 groupId) public view virtual override returns (uint256) {
|
|
81
115
|
return groups[groupId].numberOfLeaves;
|
|
82
116
|
}
|
|
83
117
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.4;
|
|
3
3
|
|
|
4
4
|
import "../interfaces/ISemaphoreVoting.sol";
|
|
5
5
|
import "../base/SemaphoreCore.sol";
|
|
@@ -9,32 +9,30 @@ import "../base/SemaphoreGroups.sol";
|
|
|
9
9
|
/// @dev The following code allows you to create polls, add voters and allow them to vote anonymously.
|
|
10
10
|
contract SemaphoreVoting is ISemaphoreVoting, SemaphoreCore, SemaphoreGroups {
|
|
11
11
|
/// @dev Gets a tree depth and returns its verifier address.
|
|
12
|
-
mapping(
|
|
12
|
+
mapping(uint256 => IVerifier) internal verifiers;
|
|
13
13
|
|
|
14
14
|
/// @dev Gets a poll id and returns the poll data.
|
|
15
15
|
mapping(uint256 => Poll) internal polls;
|
|
16
16
|
|
|
17
|
-
/// @dev
|
|
18
|
-
///
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"SemaphoreVoting: parameters lists does not have the same length"
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
for (uint8 i = 0; i < depths.length; i++) {
|
|
30
|
-
verifiers[depths[i]] = IVerifier(verifierAddresses[i]);
|
|
17
|
+
/// @dev Initializes the Semaphore verifiers used to verify the user's ZK proofs.
|
|
18
|
+
/// @param _verifiers: List of Semaphore verifiers (address and related Merkle tree depth).
|
|
19
|
+
constructor(Verifier[] memory _verifiers) {
|
|
20
|
+
for (uint8 i = 0; i < _verifiers.length; ) {
|
|
21
|
+
verifiers[_verifiers[i].merkleTreeDepth] = IVerifier(_verifiers[i].contractAddress);
|
|
22
|
+
|
|
23
|
+
unchecked {
|
|
24
|
+
++i;
|
|
25
|
+
}
|
|
31
26
|
}
|
|
32
27
|
}
|
|
33
28
|
|
|
34
29
|
/// @dev Checks if the poll coordinator is the transaction sender.
|
|
35
30
|
/// @param pollId: Id of the poll.
|
|
36
31
|
modifier onlyCoordinator(uint256 pollId) {
|
|
37
|
-
|
|
32
|
+
if (polls[pollId].coordinator != _msgSender()) {
|
|
33
|
+
revert Semaphore__CallerIsNotThePollCoordinator();
|
|
34
|
+
}
|
|
35
|
+
|
|
38
36
|
_;
|
|
39
37
|
}
|
|
40
38
|
|
|
@@ -42,11 +40,13 @@ contract SemaphoreVoting is ISemaphoreVoting, SemaphoreCore, SemaphoreGroups {
|
|
|
42
40
|
function createPoll(
|
|
43
41
|
uint256 pollId,
|
|
44
42
|
address coordinator,
|
|
45
|
-
|
|
43
|
+
uint256 merkleTreeDepth
|
|
46
44
|
) public override {
|
|
47
|
-
|
|
45
|
+
if (address(verifiers[merkleTreeDepth]) == address(0)) {
|
|
46
|
+
revert Semaphore__MerkleTreeDepthIsNotSupported();
|
|
47
|
+
}
|
|
48
48
|
|
|
49
|
-
_createGroup(pollId,
|
|
49
|
+
_createGroup(pollId, merkleTreeDepth, 0);
|
|
50
50
|
|
|
51
51
|
Poll memory poll;
|
|
52
52
|
|
|
@@ -59,14 +59,18 @@ contract SemaphoreVoting is ISemaphoreVoting, SemaphoreCore, SemaphoreGroups {
|
|
|
59
59
|
|
|
60
60
|
/// @dev See {ISemaphoreVoting-addVoter}.
|
|
61
61
|
function addVoter(uint256 pollId, uint256 identityCommitment) public override onlyCoordinator(pollId) {
|
|
62
|
-
|
|
62
|
+
if (polls[pollId].state != PollState.Created) {
|
|
63
|
+
revert Semaphore__PollHasAlreadyBeenStarted();
|
|
64
|
+
}
|
|
63
65
|
|
|
64
66
|
_addMember(pollId, identityCommitment);
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
/// @dev See {ISemaphoreVoting-addVoter}.
|
|
68
70
|
function startPoll(uint256 pollId, uint256 encryptionKey) public override onlyCoordinator(pollId) {
|
|
69
|
-
|
|
71
|
+
if (polls[pollId].state != PollState.Created) {
|
|
72
|
+
revert Semaphore__PollHasAlreadyBeenStarted();
|
|
73
|
+
}
|
|
70
74
|
|
|
71
75
|
polls[pollId].state = PollState.Ongoing;
|
|
72
76
|
|
|
@@ -82,13 +86,16 @@ contract SemaphoreVoting is ISemaphoreVoting, SemaphoreCore, SemaphoreGroups {
|
|
|
82
86
|
) public override onlyCoordinator(pollId) {
|
|
83
87
|
Poll memory poll = polls[pollId];
|
|
84
88
|
|
|
85
|
-
|
|
89
|
+
if (poll.state != PollState.Ongoing) {
|
|
90
|
+
revert Semaphore__PollIsNotOngoing();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
uint256 merkleTreeDepth = getMerkleTreeDepth(pollId);
|
|
94
|
+
uint256 merkleTreeRoot = getMerkleTreeRoot(pollId);
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
uint256 root = getRoot(pollId);
|
|
89
|
-
IVerifier verifier = verifiers[depth];
|
|
96
|
+
IVerifier verifier = verifiers[merkleTreeDepth];
|
|
90
97
|
|
|
91
|
-
_verifyProof(vote,
|
|
98
|
+
_verifyProof(vote, merkleTreeRoot, nullifierHash, pollId, proof, verifier);
|
|
92
99
|
|
|
93
100
|
// Prevent double-voting (nullifierHash = hash(pollId + identityNullifier)).
|
|
94
101
|
_saveNullifierHash(nullifierHash);
|
|
@@ -98,7 +105,9 @@ contract SemaphoreVoting is ISemaphoreVoting, SemaphoreCore, SemaphoreGroups {
|
|
|
98
105
|
|
|
99
106
|
/// @dev See {ISemaphoreVoting-publishDecryptionKey}.
|
|
100
107
|
function endPoll(uint256 pollId, uint256 decryptionKey) public override onlyCoordinator(pollId) {
|
|
101
|
-
|
|
108
|
+
if (polls[pollId].state != PollState.Ongoing) {
|
|
109
|
+
revert Semaphore__PollIsNotOngoing();
|
|
110
|
+
}
|
|
102
111
|
|
|
103
112
|
polls[pollId].state = PollState.Ended;
|
|
104
113
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.4;
|
|
3
3
|
|
|
4
4
|
import "../interfaces/ISemaphoreWhistleblowing.sol";
|
|
5
5
|
import "../base/SemaphoreCore.sol";
|
|
@@ -11,32 +11,30 @@ import "../base/SemaphoreGroups.sol";
|
|
|
11
11
|
/// Leaks can be IPFS hashes, permanent links or other kinds of reference.
|
|
12
12
|
contract SemaphoreWhistleblowing is ISemaphoreWhistleblowing, SemaphoreCore, SemaphoreGroups {
|
|
13
13
|
/// @dev Gets a tree depth and returns its verifier address.
|
|
14
|
-
mapping(
|
|
14
|
+
mapping(uint256 => IVerifier) internal verifiers;
|
|
15
15
|
|
|
16
16
|
/// @dev Gets an editor address and return their entity.
|
|
17
17
|
mapping(address => uint256) private entities;
|
|
18
18
|
|
|
19
|
-
/// @dev
|
|
20
|
-
///
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"SemaphoreWhistleblowing: parameters lists does not have the same length"
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
for (uint8 i = 0; i < depths.length; i++) {
|
|
32
|
-
verifiers[depths[i]] = IVerifier(verifierAddresses[i]);
|
|
19
|
+
/// @dev Initializes the Semaphore verifiers used to verify the user's ZK proofs.
|
|
20
|
+
/// @param _verifiers: List of Semaphore verifiers (address and related Merkle tree depth).
|
|
21
|
+
constructor(Verifier[] memory _verifiers) {
|
|
22
|
+
for (uint8 i = 0; i < _verifiers.length; ) {
|
|
23
|
+
verifiers[_verifiers[i].merkleTreeDepth] = IVerifier(_verifiers[i].contractAddress);
|
|
24
|
+
|
|
25
|
+
unchecked {
|
|
26
|
+
++i;
|
|
27
|
+
}
|
|
33
28
|
}
|
|
34
29
|
}
|
|
35
30
|
|
|
36
31
|
/// @dev Checks if the editor is the transaction sender.
|
|
37
32
|
/// @param entityId: Id of the entity.
|
|
38
33
|
modifier onlyEditor(uint256 entityId) {
|
|
39
|
-
|
|
34
|
+
if (entityId != entities[_msgSender()]) {
|
|
35
|
+
revert Semaphore__CallerIsNotTheEditor();
|
|
36
|
+
}
|
|
37
|
+
|
|
40
38
|
_;
|
|
41
39
|
}
|
|
42
40
|
|
|
@@ -44,11 +42,13 @@ contract SemaphoreWhistleblowing is ISemaphoreWhistleblowing, SemaphoreCore, Sem
|
|
|
44
42
|
function createEntity(
|
|
45
43
|
uint256 entityId,
|
|
46
44
|
address editor,
|
|
47
|
-
|
|
45
|
+
uint256 merkleTreeDepth
|
|
48
46
|
) public override {
|
|
49
|
-
|
|
47
|
+
if (address(verifiers[merkleTreeDepth]) == address(0)) {
|
|
48
|
+
revert Semaphore__MerkleTreeDepthIsNotSupported();
|
|
49
|
+
}
|
|
50
50
|
|
|
51
|
-
_createGroup(entityId,
|
|
51
|
+
_createGroup(entityId, merkleTreeDepth, 0);
|
|
52
52
|
|
|
53
53
|
entities[editor] = entityId;
|
|
54
54
|
|
|
@@ -77,11 +77,12 @@ contract SemaphoreWhistleblowing is ISemaphoreWhistleblowing, SemaphoreCore, Sem
|
|
|
77
77
|
uint256 entityId,
|
|
78
78
|
uint256[8] calldata proof
|
|
79
79
|
) public override onlyEditor(entityId) {
|
|
80
|
-
|
|
81
|
-
uint256
|
|
82
|
-
|
|
80
|
+
uint256 merkleTreeDepth = getMerkleTreeDepth(entityId);
|
|
81
|
+
uint256 merkleTreeRoot = getMerkleTreeRoot(entityId);
|
|
82
|
+
|
|
83
|
+
IVerifier verifier = verifiers[merkleTreeDepth];
|
|
83
84
|
|
|
84
|
-
_verifyProof(leak,
|
|
85
|
+
_verifyProof(leak, merkleTreeRoot, nullifierHash, entityId, proof, verifier);
|
|
85
86
|
|
|
86
87
|
emit LeakPublished(entityId, leak);
|
|
87
88
|
}
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
//SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.4;
|
|
3
3
|
|
|
4
4
|
/// @title Semaphore interface.
|
|
5
5
|
/// @dev Interface of a Semaphore contract.
|
|
6
6
|
interface ISemaphore {
|
|
7
|
+
error Semaphore__CallerIsNotTheGroupAdmin();
|
|
8
|
+
error Semaphore__MerkleTreeDepthIsNotSupported();
|
|
9
|
+
error Semaphore__MerkleTreeRootIsExpired();
|
|
10
|
+
error Semaphore__MerkleTreeRootIsNotPartOfTheGroup();
|
|
11
|
+
|
|
7
12
|
struct Verifier {
|
|
8
13
|
address contractAddress;
|
|
9
|
-
|
|
14
|
+
uint256 merkleTreeDepth;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// It defines all the parameters needed to check whether a
|
|
18
|
+
/// zero-knowledge proof generated with a certain Merkle tree is still valid.
|
|
19
|
+
struct MerkleTreeExpiry {
|
|
20
|
+
uint256 rootDuration;
|
|
21
|
+
mapping(uint256 => uint256) rootCreationDates;
|
|
10
22
|
}
|
|
11
23
|
|
|
12
24
|
/// @dev Emitted when an admin is assigned to a group.
|
|
@@ -23,12 +35,14 @@ interface ISemaphore {
|
|
|
23
35
|
/// @dev Saves the nullifier hash to avoid double signaling and emits an event
|
|
24
36
|
/// if the zero-knowledge proof is valid.
|
|
25
37
|
/// @param groupId: Id of the group.
|
|
38
|
+
/// @param merkleTreeRoot: Root of the Merkle tree.
|
|
26
39
|
/// @param signal: Semaphore signal.
|
|
27
40
|
/// @param nullifierHash: Nullifier hash.
|
|
28
41
|
/// @param externalNullifier: External nullifier.
|
|
29
42
|
/// @param proof: Zero-knowledge proof.
|
|
30
43
|
function verifyProof(
|
|
31
44
|
uint256 groupId,
|
|
45
|
+
uint256 merkleTreeRoot,
|
|
32
46
|
bytes32 signal,
|
|
33
47
|
uint256 nullifierHash,
|
|
34
48
|
uint256 externalNullifier,
|
|
@@ -42,11 +56,25 @@ interface ISemaphore {
|
|
|
42
56
|
/// @param admin: Admin of the group.
|
|
43
57
|
function createGroup(
|
|
44
58
|
uint256 groupId,
|
|
45
|
-
|
|
59
|
+
uint256 depth,
|
|
46
60
|
uint256 zeroValue,
|
|
47
61
|
address admin
|
|
48
62
|
) external;
|
|
49
63
|
|
|
64
|
+
/// @dev Creates a new group. Only the admin will be able to add or remove members.
|
|
65
|
+
/// @param groupId: Id of the group.
|
|
66
|
+
/// @param depth: Depth of the tree.
|
|
67
|
+
/// @param zeroValue: Zero value of the tree.
|
|
68
|
+
/// @param admin: Admin of the group.
|
|
69
|
+
/// @param merkleTreeRootDuration: Time before the validity of a root expires.
|
|
70
|
+
function createGroup(
|
|
71
|
+
uint256 groupId,
|
|
72
|
+
uint256 depth,
|
|
73
|
+
uint256 zeroValue,
|
|
74
|
+
address admin,
|
|
75
|
+
uint256 merkleTreeRootDuration
|
|
76
|
+
) external;
|
|
77
|
+
|
|
50
78
|
/// @dev Updates the group admin.
|
|
51
79
|
/// @param groupId: Id of the group.
|
|
52
80
|
/// @param newAdmin: New admin of the group.
|
|
@@ -57,10 +85,25 @@ interface ISemaphore {
|
|
|
57
85
|
/// @param identityCommitment: New identity commitment.
|
|
58
86
|
function addMember(uint256 groupId, uint256 identityCommitment) external;
|
|
59
87
|
|
|
88
|
+
/// @dev Updates an identity commitment of an existing group. A proof of membership is
|
|
89
|
+
/// needed to check if the node to be updated is part of the tree.
|
|
90
|
+
/// @param groupId: Id of the group.
|
|
91
|
+
/// @param identityCommitment: Existing identity commitment to be updated.
|
|
92
|
+
/// @param newIdentityCommitment: New identity commitment.
|
|
93
|
+
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
|
|
94
|
+
/// @param proofPathIndices: Path of the proof of membership.
|
|
95
|
+
function updateMember(
|
|
96
|
+
uint256 groupId,
|
|
97
|
+
uint256 identityCommitment,
|
|
98
|
+
uint256 newIdentityCommitment,
|
|
99
|
+
uint256[] calldata proofSiblings,
|
|
100
|
+
uint8[] calldata proofPathIndices
|
|
101
|
+
) external;
|
|
102
|
+
|
|
60
103
|
/// @dev Removes a member from an existing group. A proof of membership is
|
|
61
104
|
/// needed to check if the node to be removed is part of the tree.
|
|
62
105
|
/// @param groupId: Id of the group.
|
|
63
|
-
/// @param identityCommitment: Identity commitment to be
|
|
106
|
+
/// @param identityCommitment: Identity commitment to be removed.
|
|
64
107
|
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
|
|
65
108
|
/// @param proofPathIndices: Path of the proof of membership.
|
|
66
109
|
function removeMember(
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
//SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.4;
|
|
3
3
|
|
|
4
4
|
/// @title SemaphoreCore interface.
|
|
5
5
|
/// @dev Interface of SemaphoreCore contract.
|
|
6
6
|
interface ISemaphoreCore {
|
|
7
|
+
error Semaphore__YouAreUsingTheSameNillifierTwice();
|
|
8
|
+
|
|
7
9
|
/// @notice Emitted when a proof is verified correctly and a new nullifier hash is added.
|
|
8
10
|
/// @param nullifierHash: Hash of external and identity nullifiers.
|
|
9
11
|
event NullifierHashAdded(uint256 nullifierHash);
|
|
@@ -1,39 +1,55 @@
|
|
|
1
1
|
//SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.4;
|
|
3
3
|
|
|
4
4
|
/// @title SemaphoreGroups interface.
|
|
5
5
|
/// @dev Interface of a SemaphoreGroups contract.
|
|
6
6
|
interface ISemaphoreGroups {
|
|
7
|
+
error Semaphore__GroupDoesNotExist();
|
|
8
|
+
error Semaphore__GroupAlreadyExists();
|
|
9
|
+
error Semaphore__GroupIdIsNotLessThanSnarkScalarField();
|
|
10
|
+
|
|
7
11
|
/// @dev Emitted when a new group is created.
|
|
8
12
|
/// @param groupId: Id of the group.
|
|
9
|
-
/// @param
|
|
13
|
+
/// @param merkleTreeDepth: Depth of the tree.
|
|
10
14
|
/// @param zeroValue: Zero value of the tree.
|
|
11
|
-
event GroupCreated(uint256 indexed groupId,
|
|
15
|
+
event GroupCreated(uint256 indexed groupId, uint256 merkleTreeDepth, uint256 zeroValue);
|
|
12
16
|
|
|
13
17
|
/// @dev Emitted when a new identity commitment is added.
|
|
14
18
|
/// @param groupId: Group id of the group.
|
|
15
19
|
/// @param identityCommitment: New identity commitment.
|
|
16
|
-
/// @param
|
|
17
|
-
event MemberAdded(uint256 indexed groupId, uint256 identityCommitment, uint256
|
|
20
|
+
/// @param merkleTreeRoot: New root hash of the tree.
|
|
21
|
+
event MemberAdded(uint256 indexed groupId, uint256 identityCommitment, uint256 merkleTreeRoot);
|
|
18
22
|
|
|
19
|
-
/// @dev Emitted when
|
|
23
|
+
/// @dev Emitted when an identity commitment is updated.
|
|
20
24
|
/// @param groupId: Group id of the group.
|
|
21
25
|
/// @param identityCommitment: New identity commitment.
|
|
22
|
-
/// @param
|
|
23
|
-
|
|
26
|
+
/// @param newIdentityCommitment: New identity commitment.
|
|
27
|
+
/// @param merkleTreeRoot: New root hash of the tree.
|
|
28
|
+
event MemberUpdated(
|
|
29
|
+
uint256 indexed groupId,
|
|
30
|
+
uint256 identityCommitment,
|
|
31
|
+
uint256 newIdentityCommitment,
|
|
32
|
+
uint256 merkleTreeRoot
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
/// @dev Emitted when a new identity commitment is removed.
|
|
36
|
+
/// @param groupId: Group id of the group.
|
|
37
|
+
/// @param identityCommitment: Existing identity commitment to be removed.
|
|
38
|
+
/// @param merkleTreeRoot: New root hash of the tree.
|
|
39
|
+
event MemberRemoved(uint256 indexed groupId, uint256 identityCommitment, uint256 merkleTreeRoot);
|
|
24
40
|
|
|
25
41
|
/// @dev Returns the last root hash of a group.
|
|
26
42
|
/// @param groupId: Id of the group.
|
|
27
43
|
/// @return Root hash of the group.
|
|
28
|
-
function
|
|
44
|
+
function getMerkleTreeRoot(uint256 groupId) external view returns (uint256);
|
|
29
45
|
|
|
30
46
|
/// @dev Returns the depth of the tree of a group.
|
|
31
47
|
/// @param groupId: Id of the group.
|
|
32
48
|
/// @return Depth of the group tree.
|
|
33
|
-
function
|
|
49
|
+
function getMerkleTreeDepth(uint256 groupId) external view returns (uint256);
|
|
34
50
|
|
|
35
51
|
/// @dev Returns the number of tree leaves of a group.
|
|
36
52
|
/// @param groupId: Id of the group.
|
|
37
53
|
/// @return Number of tree leaves.
|
|
38
|
-
function
|
|
54
|
+
function getNumberOfMerkleTreeLeaves(uint256 groupId) external view returns (uint256);
|
|
39
55
|
}
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
//SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.4;
|
|
3
3
|
|
|
4
4
|
/// @title SemaphoreVoting interface.
|
|
5
5
|
/// @dev Interface of SemaphoreVoting contract.
|
|
6
6
|
interface ISemaphoreVoting {
|
|
7
|
+
error Semaphore__CallerIsNotThePollCoordinator();
|
|
8
|
+
error Semaphore__MerkleTreeDepthIsNotSupported();
|
|
9
|
+
error Semaphore__PollHasAlreadyBeenStarted();
|
|
10
|
+
error Semaphore__PollIsNotOngoing();
|
|
11
|
+
|
|
7
12
|
enum PollState {
|
|
8
13
|
Created,
|
|
9
14
|
Ongoing,
|
|
10
15
|
Ended
|
|
11
16
|
}
|
|
12
17
|
|
|
18
|
+
struct Verifier {
|
|
19
|
+
address contractAddress;
|
|
20
|
+
uint256 merkleTreeDepth;
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
struct Poll {
|
|
14
24
|
address coordinator;
|
|
15
25
|
PollState state;
|
|
@@ -40,11 +50,11 @@ interface ISemaphoreVoting {
|
|
|
40
50
|
/// @dev Creates a poll and the associated Merkle tree/group.
|
|
41
51
|
/// @param pollId: Id of the poll.
|
|
42
52
|
/// @param coordinator: Coordinator of the poll.
|
|
43
|
-
/// @param
|
|
53
|
+
/// @param merkleTreeDepth: Depth of the tree.
|
|
44
54
|
function createPoll(
|
|
45
55
|
uint256 pollId,
|
|
46
56
|
address coordinator,
|
|
47
|
-
|
|
57
|
+
uint256 merkleTreeDepth
|
|
48
58
|
) external;
|
|
49
59
|
|
|
50
60
|
/// @dev Adds a voter to a poll.
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
//SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity
|
|
2
|
+
pragma solidity 0.8.4;
|
|
3
3
|
|
|
4
4
|
/// @title SemaphoreWhistleblowing interface.
|
|
5
5
|
/// @dev Interface of SemaphoreWhistleblowing contract.
|
|
6
6
|
interface ISemaphoreWhistleblowing {
|
|
7
|
+
error Semaphore__CallerIsNotTheEditor();
|
|
8
|
+
error Semaphore__MerkleTreeDepthIsNotSupported();
|
|
9
|
+
|
|
10
|
+
struct Verifier {
|
|
11
|
+
address contractAddress;
|
|
12
|
+
uint256 merkleTreeDepth;
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
/// @dev Emitted when a new entity is created.
|
|
8
16
|
/// @param entityId: Id of the entity.
|
|
9
17
|
/// @param editor: Editor of the entity.
|
|
@@ -17,11 +25,11 @@ interface ISemaphoreWhistleblowing {
|
|
|
17
25
|
/// @dev Creates an entity and the associated Merkle tree/group.
|
|
18
26
|
/// @param entityId: Id of the entity.
|
|
19
27
|
/// @param editor: Editor of the entity.
|
|
20
|
-
/// @param
|
|
28
|
+
/// @param merkleTreeDepth: Depth of the tree.
|
|
21
29
|
function createEntity(
|
|
22
30
|
uint256 entityId,
|
|
23
31
|
address editor,
|
|
24
|
-
|
|
32
|
+
uint256 merkleTreeDepth
|
|
25
33
|
) external;
|
|
26
34
|
|
|
27
35
|
/// @dev Adds a whistleblower to an entity.
|
package/interfaces/IVerifier.sol
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@semaphore-protocol/contracts",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Semaphore contracts to manage groups and broadcast anonymous signals.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@openzeppelin/contracts": "
|
|
36
|
-
"@zk-kit/incremental-merkle-tree.sol": "
|
|
35
|
+
"@openzeppelin/contracts": "4.4.2",
|
|
36
|
+
"@zk-kit/incremental-merkle-tree.sol": "1.3.0"
|
|
37
37
|
}
|
|
38
38
|
}
|