Skip to content

Commit

Permalink
Merge pull request #128 from semaphore-protocol/feat/update-member
Browse files Browse the repository at this point in the history
New function to update group members
  • Loading branch information
cedoor authored Sep 9, 2022
2 parents 4a94fb1 + 52837e7 commit 2d4f2de
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 25 deletions.
11 changes: 11 additions & 0 deletions contracts/Semaphore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ contract Semaphore is ISemaphore, SemaphoreCore, SemaphoreGroups {
merkleTreeExpiries[groupId].rootCreationDates[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);
}

/// @dev See {ISemaphore-removeMember}.
function removeMember(
uint256 groupId,
Expand Down
27 changes: 26 additions & 1 deletion contracts/base/SemaphoreGroups.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,35 @@ abstract contract SemaphoreGroups is Context, ISemaphoreGroups {
emit MemberAdded(groupId, 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 newIdentityCommitment: New identity commitment.
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
/// @param proofPathIndices: Path of the proof of membership.
function _updateMember(
uint256 groupId,
uint256 identityCommitment,
uint256 newIdentityCommitment,
uint256[] calldata proofSiblings,
uint8[] calldata proofPathIndices
) internal virtual {
if (getMerkleTreeRoot(groupId) == 0) {
revert Semaphore__GroupDoesNotExist();
}

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

uint256 merkleTreeRoot = getMerkleTreeRoot(groupId);

emit MemberUpdated(groupId, identityCommitment, 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 deleted.
/// @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.
function _removeMember(
Expand Down
17 changes: 16 additions & 1 deletion contracts/interfaces/ISemaphore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,25 @@ interface ISemaphore {
/// @param identityCommitment: New identity commitment.
function addMember(uint256 groupId, uint256 identityCommitment) external;

/// @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 newIdentityCommitment: New identity commitment.
/// @param proofSiblings: Array of the sibling nodes of the proof of membership.
/// @param proofPathIndices: Path of the proof of membership.
function updateMember(
uint256 groupId,
uint256 identityCommitment,
uint256 newIdentityCommitment,
uint256[] calldata proofSiblings,
uint8[] calldata proofPathIndices
) external;

/// @dev Removes a member from an existing group. A proof of membership is
/// needed to check if the node to be removed is part of the tree.
/// @param groupId: Id of the group.
/// @param identityCommitment: Identity commitment to be deleted.
/// @param identityCommitment: 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.
function removeMember(
Expand Down
14 changes: 13 additions & 1 deletion contracts/interfaces/ISemaphoreGroups.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,21 @@ interface ISemaphoreGroups {
/// @param merkleTreeRoot: New root hash of the tree.
event MemberAdded(uint256 indexed groupId, uint256 identityCommitment, uint256 merkleTreeRoot);

/// @dev Emitted when a new identity commitment is removed.
/// @dev Emitted when an identity commitment is updated.
/// @param groupId: Group id of the group.
/// @param identityCommitment: New identity commitment.
/// @param newIdentityCommitment: New identity commitment.
/// @param merkleTreeRoot: New root hash of the tree.
event MemberUpdated(
uint256 indexed groupId,
uint256 identityCommitment,
uint256 newIdentityCommitment,
uint256 merkleTreeRoot
);

/// @dev Emitted when a new identity commitment is removed.
/// @param groupId: Group id of the group.
/// @param identityCommitment: Existing identity commitment to be removed.
/// @param merkleTreeRoot: New root hash of the tree.
event MemberRemoved(uint256 indexed groupId, uint256 identityCommitment, uint256 merkleTreeRoot);

Expand Down
2 changes: 1 addition & 1 deletion contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@
},
"dependencies": {
"@openzeppelin/contracts": "4.4.2",
"@zk-kit/incremental-merkle-tree.sol": "1.2.0"
"@zk-kit/incremental-merkle-tree.sol": "1.3.0"
}
}
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
"@nomiclabs/hardhat-ethers": "^2.0.6",
"@nomiclabs/hardhat-etherscan": "^3.1.0",
"@nomiclabs/hardhat-waffle": "^2.0.3",
"@semaphore-protocol/group": "2.0.0",
"@semaphore-protocol/group": "2.2.0",
"@semaphore-protocol/identity": "2.0.0",
"@semaphore-protocol/proof": "2.3.0",
"@semaphore-protocol/proof": "2.3.1",
"@typechain/ethers-v5": "^10.0.0",
"@typechain/hardhat": "^6.0.0",
"@types/chai": "^4.3.0",
Expand Down Expand Up @@ -100,6 +100,6 @@
},
"dependencies": {
"@openzeppelin/contracts": "4.4.2",
"@zk-kit/incremental-merkle-tree.sol": "1.2.0"
"@zk-kit/incremental-merkle-tree.sol": "1.3.0"
}
}
30 changes: 29 additions & 1 deletion test/Semaphore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,34 @@ describe("Semaphore", () => {
})
})

describe("# updateMember", () => {
it("Should not update a member if the caller is not the group admin", async () => {
const transaction = contract.connect(signers[1]).updateMember(groupId, members[0], 1, [0, 1], [0, 1])

await expect(transaction).to.be.revertedWith("Semaphore__CallerIsNotTheGroupAdmin()")
})

it("Should update a member from an existing group", async () => {
const groupId = 3
const group = new Group(treeDepth)

group.addMembers([BigInt(1), BigInt(2), BigInt(3)])

group.updateMember(0, BigInt(4))

await contract["createGroup(uint256,uint256,uint256,address)"](groupId, treeDepth, 0, accounts[0])
await contract.addMember(groupId, BigInt(1))
await contract.addMember(groupId, BigInt(2))
await contract.addMember(groupId, BigInt(3))

const { siblings, pathIndices, root } = group.generateProofOfMembership(0)

const transaction = contract.updateMember(groupId, BigInt(1), BigInt(4), siblings, pathIndices)

await expect(transaction).to.emit(contract, "MemberUpdated").withArgs(groupId, BigInt(1), BigInt(4), root)
})
})

describe("# removeMember", () => {
it("Should not remove a member if the caller is not the group admin", async () => {
const transaction = contract.connect(signers[1]).removeMember(groupId, members[0], [0, 1], [0, 1])
Expand All @@ -115,7 +143,7 @@ describe("Semaphore", () => {
})

it("Should remove a member from an existing group", async () => {
const groupId = 3
const groupId = 4
const group = new Group(treeDepth)

group.addMembers([BigInt(1), BigInt(2), BigInt(3)])
Expand Down
41 changes: 24 additions & 17 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1372,13 +1372,13 @@ __metadata:
languageName: node
linkType: hard

"@semaphore-protocol/group@npm:2.0.0":
version: 2.0.0
resolution: "@semaphore-protocol/group@npm:2.0.0"
"@semaphore-protocol/group@npm:2.2.0":
version: 2.2.0
resolution: "@semaphore-protocol/group@npm:2.2.0"
dependencies:
"@zk-kit/incremental-merkle-tree": 0.4.3
"@zk-kit/incremental-merkle-tree": 1.0.0
circomlibjs: 0.0.8
checksum: 871bccaa2bc5958b93dc47d5d24db4282bf5e2af667f1b79e1040a4125192e0ab094464232985f70ee5bb35299016fffda56e097d08f5511e2f09180ba018c78
checksum: e7e509399277bfda2e850c0adfacaa9fb25a99db2727227066448324ffe5445fecfacac1aae26b68b4bd1f305b5748defb996368a87e33d979d3b855d76b8b78
languageName: node
linkType: hard

Expand All @@ -1395,19 +1395,19 @@ __metadata:
languageName: node
linkType: hard

"@semaphore-protocol/proof@npm:2.3.0":
version: 2.3.0
resolution: "@semaphore-protocol/proof@npm:2.3.0"
"@semaphore-protocol/proof@npm:2.3.1":
version: 2.3.1
resolution: "@semaphore-protocol/proof@npm:2.3.1"
dependencies:
"@ethersproject/bytes": ^5.7.0
"@ethersproject/solidity": ^5.5.0
"@ethersproject/strings": ^5.5.0
"@semaphore-protocol/group": 2.0.0
"@semaphore-protocol/group": 2.2.0
"@semaphore-protocol/identity": 2.0.0
"@zk-kit/incremental-merkle-tree": 0.4.3
circomlibjs: 0.0.8
snarkjs: ^0.4.13
checksum: 880a70936637f5eb0a9def9844907872701888dbf8ad60035ecb07fec988040fd303e1b847d8addaff4075af52daf9e82c2015f539bb709b34ea4a813cf71808
checksum: 5c7204d48c3dedf0ad4b33be6fe88e8dca0270b1cc7e56e073ae3ce7d8d4152cb7c3c361a51ff5a9fa9f4e8e61b806f4651167f4311c9c3397a05be0bc32257e
languageName: node
linkType: hard

Expand Down Expand Up @@ -2133,10 +2133,10 @@ __metadata:
languageName: node
linkType: hard

"@zk-kit/incremental-merkle-tree.sol@npm:1.2.0":
version: 1.2.0
resolution: "@zk-kit/incremental-merkle-tree.sol@npm:1.2.0"
checksum: 7fc03fe399bc18e6a6e3775e1fd37f85cfea01846004e3a7d3829eeef11bccd432685aaab03045e7c24f42df67c9ec89c5fa01b36c1c47576874e1ef3b57cc08
"@zk-kit/incremental-merkle-tree.sol@npm:1.3.0":
version: 1.3.0
resolution: "@zk-kit/incremental-merkle-tree.sol@npm:1.3.0"
checksum: 1cd99d6867c87b01ff839ad93bed76ffcab079ca3d687b847b8d9a2f1f7e0d253c38d80587b4e1faa293f8a0c79ceb90ea45c3a0b699db5e7c476948f6a3d713
languageName: node
linkType: hard

Expand All @@ -2147,6 +2147,13 @@ __metadata:
languageName: node
linkType: hard

"@zk-kit/incremental-merkle-tree@npm:1.0.0":
version: 1.0.0
resolution: "@zk-kit/incremental-merkle-tree@npm:1.0.0"
checksum: 2b5d7b5cc08cf3aea536d6541a1177221fcacca5b93fb22e37cd05926d138fe69035b73cb42e09798505caf1b7a75ad225027d13d35c2f0d7b216147cafd184e
languageName: node
linkType: hard

"JSONStream@npm:^1.0.4":
version: 1.3.5
resolution: "JSONStream@npm:1.3.5"
Expand Down Expand Up @@ -13378,9 +13385,9 @@ __metadata:
"@nomiclabs/hardhat-etherscan": ^3.1.0
"@nomiclabs/hardhat-waffle": ^2.0.3
"@openzeppelin/contracts": 4.4.2
"@semaphore-protocol/group": 2.0.0
"@semaphore-protocol/group": 2.2.0
"@semaphore-protocol/identity": 2.0.0
"@semaphore-protocol/proof": 2.3.0
"@semaphore-protocol/proof": 2.3.1
"@typechain/ethers-v5": ^10.0.0
"@typechain/hardhat": ^6.0.0
"@types/chai": ^4.3.0
Expand All @@ -13390,7 +13397,7 @@ __metadata:
"@types/rimraf": ^3.0.2
"@typescript-eslint/eslint-plugin": ^5.10.1
"@typescript-eslint/parser": ^5.10.1
"@zk-kit/incremental-merkle-tree.sol": 1.2.0
"@zk-kit/incremental-merkle-tree.sol": 1.3.0
chai: ^4.3.5
circomlib: ^2.0.2
circomlibjs: ^0.0.8
Expand Down

0 comments on commit 2d4f2de

Please sign in to comment.