diff --git a/packages/contracts/contracts/Semaphore.sol b/packages/contracts/contracts/Semaphore.sol index 02e7b512..fe83ec53 100644 --- a/packages/contracts/contracts/Semaphore.sol +++ b/packages/contracts/contracts/Semaphore.sol @@ -102,36 +102,34 @@ contract Semaphore is ISemaphore, SemaphoreGroups { function validateProof( uint256 groupId, - uint256 merkleTreeDepth, - uint256 merkleTreeRoot, - uint256 nullifier, - uint256 message, - uint256 scope, - uint256[8] calldata proof + SemaphoreProof calldata proof ) external override onlyExistingGroup(groupId) { - if (groups[groupId].nullifiers[nullifier]) { + if (groups[groupId].nullifiers[proof.nullifier]) { revert Semaphore__YouAreUsingTheSameNullifierTwice(); } - if (!verifyProof(groupId, merkleTreeDepth, merkleTreeRoot, nullifier, message, scope, proof)) { + if (!verifyProof(groupId, proof)) { revert Semaphore__InvalidProof(); } - groups[groupId].nullifiers[nullifier] = true; + groups[groupId].nullifiers[proof.nullifier] = true; - emit ProofValidated(groupId, merkleTreeDepth, merkleTreeRoot, nullifier, message, scope, proof); + emit ProofValidated( + groupId, + proof.merkleTreeDepth, + proof.merkleTreeRoot, + proof.nullifier, + proof.message, + proof.scope, + proof.proof + ); } function verifyProof( uint256 groupId, - uint256 merkleTreeDepth, - uint256 merkleTreeRoot, - uint256 nullifier, - uint256 message, - uint256 scope, - uint256[8] calldata proof + SemaphoreProof calldata proof ) public view override onlyExistingGroup(groupId) returns (bool) { - if (merkleTreeDepth < 1 || merkleTreeDepth > 12) { + if (proof.merkleTreeDepth < 1 || proof.merkleTreeDepth > 12) { revert Semaphore__MerkleTreeDepthIsNotSupported(); } @@ -145,8 +143,8 @@ contract Semaphore is ISemaphore, SemaphoreGroups { // 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]; + if (proof.merkleTreeRoot != currentMerkleTreeRoot) { + uint256 merkleRootCreationDate = groups[groupId].merkleRootCreationDates[proof.merkleTreeRoot]; uint256 merkleTreeDuration = groups[groupId].merkleTreeDuration; if (merkleRootCreationDate == 0) { @@ -160,11 +158,11 @@ contract Semaphore is ISemaphore, SemaphoreGroups { return verifier.verifyProof( - [proof[0], proof[1]], - [[proof[2], proof[3]], [proof[4], proof[5]]], - [proof[6], proof[7]], - [merkleTreeRoot, nullifier, _hash(message), _hash(scope)], - merkleTreeDepth + [proof.proof[0], proof.proof[1]], + [[proof.proof[2], proof.proof[3]], [proof.proof[4], proof.proof[5]]], + [proof.proof[6], proof.proof[7]], + [proof.merkleTreeRoot, proof.nullifier, _hash(proof.message), _hash(proof.scope)], + proof.merkleTreeDepth ); } diff --git a/packages/contracts/contracts/interfaces/ISemaphore.sol b/packages/contracts/contracts/interfaces/ISemaphore.sol index 597002e3..b81e7190 100644 --- a/packages/contracts/contracts/interfaces/ISemaphore.sol +++ b/packages/contracts/contracts/interfaces/ISemaphore.sol @@ -17,6 +17,16 @@ interface ISemaphore { mapping(uint256 => bool) nullifiers; } + /// It defines all the group parameters used by Semaphore.sol. + struct SemaphoreProof { + uint256 merkleTreeDepth; + uint256 merkleTreeRoot; + uint256 nullifier; + uint256 message; + uint256 scope; + uint256[8] proof; + } + /// @dev Emitted when the Merkle tree duration of a group is updated. /// @param groupId: Id of the group. /// @param oldMerkleTreeDuration: Old Merkle tree duration of the group. @@ -82,37 +92,11 @@ interface ISemaphore { /// @dev Saves the nullifier hash to avoid double signaling and emits an event /// if the zero-knowledge proof is valid. /// @param groupId: Id of the group. - /// @param merkleTreeDepth: Depth of the Merkle tree. - /// @param merkleTreeRoot: Root of the Merkle tree. - /// @param nullifier: Nullifier. - /// @param message: Semaphore message. - /// @param scope: Scope. /// @param proof: Zero-knowledge proof. - function validateProof( - uint256 groupId, - uint256 merkleTreeDepth, - uint256 merkleTreeRoot, - uint256 nullifier, - uint256 message, - uint256 scope, - uint256[8] calldata proof - ) external; + function validateProof(uint256 groupId, SemaphoreProof calldata proof) external; /// @dev Verifies a zero-knowledge proof by returning true or false. /// @param groupId: Id of the group. - /// @param merkleTreeDepth: Depth of the Merkle tree. - /// @param merkleTreeRoot: Root of the Merkle tree. - /// @param nullifier: Nullifier. - /// @param message: Semaphore message. - /// @param scope: Scope. /// @param proof: Zero-knowledge proof. - function verifyProof( - uint256 groupId, - uint256 merkleTreeDepth, - uint256 merkleTreeRoot, - uint256 nullifier, - uint256 message, - uint256 scope, - uint256[8] calldata proof - ) external view returns (bool); + function verifyProof(uint256 groupId, SemaphoreProof calldata proof) external view returns (bool); } diff --git a/packages/contracts/test/Semaphore.ts b/packages/contracts/test/Semaphore.ts index f28a8b40..727537aa 100644 --- a/packages/contracts/test/Semaphore.ts +++ b/packages/contracts/test/Semaphore.ts @@ -226,29 +226,13 @@ describe("Semaphore", () => { }) it("Should not verify a proof if the group does not exist", async () => { - const transaction = semaphoreContract.verifyProof( - 11, - merkleTreeDepth, - 1, - 0, - message, - 0, - [0, 0, 0, 0, 0, 0, 0, 0] - ) + const transaction = semaphoreContract.verifyProof(11, fullProof) await expect(transaction).to.be.revertedWithCustomError(semaphoreContract, "Semaphore__GroupDoesNotExist") }) it("Should not verify a proof if the Merkle tree root is not part of the group", async () => { - const transaction = semaphoreContract.verifyProof( - groupId, - merkleTreeDepth, - 1, - 0, - message, - 0, - [0, 0, 0, 0, 0, 0, 0, 0] - ) + const transaction = semaphoreContract.verifyProof(groupId, { ...fullProof, merkleTreeRoot: 1 }) await expect(transaction).to.be.revertedWithCustomError( semaphoreContract, @@ -257,15 +241,7 @@ describe("Semaphore", () => { }) it("Should verify a proof for an onchain group", async () => { - const validProof = await semaphoreContract.verifyProof( - groupId, - fullProof.merkleTreeDepth, - fullProof.merkleTreeRoot, - fullProof.nullifier, - fullProof.message, - fullProof.merkleTreeRoot, - fullProof.proof - ) + const validProof = await semaphoreContract.verifyProof(groupId, fullProof) expect(validProof).to.equal(true) }) @@ -279,15 +255,7 @@ describe("Semaphore", () => { const fullProof = await generateProof(identity, group, message, group.root as string, merkleTreeDepth) - const transaction = semaphoreContract.verifyProof( - groupId, - fullProof.merkleTreeDepth, - fullProof.merkleTreeRoot, - fullProof.nullifier, - fullProof.message, - fullProof.merkleTreeRoot, - fullProof.proof - ) + const transaction = semaphoreContract.verifyProof(groupId, fullProof) await expect(transaction).to.be.revertedWithCustomError( semaphoreContract, @@ -327,29 +295,13 @@ describe("Semaphore", () => { }) it("Should throw an exception if the proof is not valid", async () => { - const transaction = semaphoreContract.validateProof( - groupId, - fullProof.merkleTreeDepth, - fullProof.merkleTreeRoot, - fullProof.nullifier, - fullProof.message, - 0, - fullProof.proof - ) + const transaction = semaphoreContract.validateProof(groupId, { ...fullProof, scope: 0 }) await expect(transaction).to.be.revertedWithCustomError(semaphoreContract, "Semaphore__InvalidProof") }) it("Should validate a proof for an onchain group with one member correctly", async () => { - const transaction = semaphoreContract.validateProof( - groupOneMemberId, - fullProof.merkleTreeDepth, - fullProofOneMember.merkleTreeRoot, - fullProofOneMember.nullifier, - fullProofOneMember.message, - fullProofOneMember.merkleTreeRoot, - fullProofOneMember.proof - ) + const transaction = semaphoreContract.validateProof(groupOneMemberId, fullProofOneMember) await expect(transaction) .to.emit(semaphoreContract, "ProofValidated") @@ -365,15 +317,7 @@ describe("Semaphore", () => { }) it("Should validate a proof for an onchain group with more than one member correctly", async () => { - const transaction = semaphoreContract.validateProof( - groupId, - fullProof.merkleTreeDepth, - fullProof.merkleTreeRoot, - fullProof.nullifier, - fullProof.message, - fullProof.merkleTreeRoot, - fullProof.proof - ) + const transaction = semaphoreContract.validateProof(groupId, fullProof) await expect(transaction) .to.emit(semaphoreContract, "ProofValidated") @@ -389,15 +333,7 @@ describe("Semaphore", () => { }) it("Should not validate the same proof for an onchain group twice", async () => { - const transaction = semaphoreContract.validateProof( - groupId, - fullProof.merkleTreeDepth, - fullProof.merkleTreeRoot, - fullProof.nullifier, - fullProof.message, - fullProof.merkleTreeRoot, - fullProof.proof - ) + const transaction = semaphoreContract.validateProof(groupId, fullProof) await expect(transaction).to.be.revertedWithCustomError( semaphoreContract,