Skip to content

Commit a37abb5

Browse files
authored
Merge pull request #547 from semaphore-protocol/feat/verify-without-nullifier
New function to verify proofs without nullifiers
2 parents 0223f10 + 7cb1c8d commit a37abb5

File tree

3 files changed

+125
-66
lines changed

3 files changed

+125
-66
lines changed

packages/contracts/contracts/Semaphore.sol

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,35 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
100100
groups[groupId].merkleRootCreationDates[merkleTreeRoot] = block.timestamp;
101101
}
102102

103-
/// @dev See {ISemaphore-verifyProof}.
104-
function verifyProof(
103+
function validateProof(
105104
uint256 groupId,
106105
uint256 merkleTreeRoot,
107106
uint256 nullifier,
108107
uint256 message,
109108
uint256 scope,
110109
uint256[8] calldata proof
111110
) external override onlyExistingGroup(groupId) {
111+
if (groups[groupId].nullifiers[nullifier]) {
112+
revert Semaphore__YouAreUsingTheSameNullifierTwice();
113+
}
114+
115+
if (!verifyProof(groupId, merkleTreeRoot, nullifier, message, scope, proof)) {
116+
revert Semaphore__InvalidProof();
117+
}
118+
119+
groups[groupId].nullifiers[nullifier] = true;
120+
121+
emit ProofValidated(groupId, merkleTreeRoot, nullifier, message, scope, proof);
122+
}
123+
124+
function verifyProof(
125+
uint256 groupId,
126+
uint256 merkleTreeRoot,
127+
uint256 nullifier,
128+
uint256 message,
129+
uint256 scope,
130+
uint256[8] calldata proof
131+
) public view override onlyExistingGroup(groupId) returns (bool) {
112132
uint256 merkleTreeSize = getMerkleTreeSize(groupId);
113133

114134
if (merkleTreeSize == 0) {
@@ -132,24 +152,13 @@ contract Semaphore is ISemaphore, SemaphoreGroups {
132152
}
133153
}
134154

135-
if (groups[groupId].nullifiers[nullifier]) {
136-
revert Semaphore__YouAreUsingTheSameNullifierTwice();
137-
}
138-
139-
if (
140-
!verifier.verifyProof(
155+
return
156+
verifier.verifyProof(
141157
[proof[0], proof[1]],
142158
[[proof[2], proof[3]], [proof[4], proof[5]]],
143159
[proof[6], proof[7]],
144160
[merkleTreeRoot, nullifier, _hash(message), _hash(scope)]
145-
)
146-
) {
147-
revert Semaphore__InvalidProof();
148-
}
149-
150-
groups[groupId].nullifiers[nullifier] = true;
151-
152-
emit ProofVerified(groupId, merkleTreeRoot, nullifier, message, scope, proof);
161+
);
153162
}
154163

155164
/// @dev Creates a keccak256 hash of a message compatible with the SNARK scalar modulus.

packages/contracts/contracts/interfaces/ISemaphore.sol

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ interface ISemaphore {
2727
uint256 newMerkleTreeDuration
2828
);
2929

30-
/// @dev Emitted when a Semaphore proof is verified.
30+
/// @dev Emitted when a Semaphore proof is validated.
3131
/// @param groupId: Id of the group.
3232
/// @param merkleTreeRoot: Root of the Merkle tree.
3333
/// @param nullifier: Nullifier.
3434
/// @param message: Semaphore message.
3535
/// @param scope: Scope.
3636
/// @param proof: Zero-knowledge proof.
37-
event ProofVerified(
37+
event ProofValidated(
3838
uint256 indexed groupId,
3939
uint256 indexed merkleTreeRoot,
4040
uint256 nullifier,
@@ -85,12 +85,28 @@ interface ISemaphore {
8585
/// @param message: Semaphore message.
8686
/// @param scope: Scope.
8787
/// @param proof: Zero-knowledge proof.
88-
function verifyProof(
88+
function validateProof(
8989
uint256 groupId,
9090
uint256 merkleTreeRoot,
9191
uint256 nullifier,
9292
uint256 message,
9393
uint256 scope,
9494
uint256[8] calldata proof
9595
) external;
96+
97+
/// @dev Verifies a zero-knowledge proof by returning true or false.
98+
/// @param groupId: Id of the group.
99+
/// @param merkleTreeRoot: Root of the Merkle tree.
100+
/// @param nullifier: Nullifier.
101+
/// @param message: Semaphore message.
102+
/// @param scope: Scope.
103+
/// @param proof: Zero-knowledge proof.
104+
function verifyProof(
105+
uint256 groupId,
106+
uint256 merkleTreeRoot,
107+
uint256 nullifier,
108+
uint256 message,
109+
uint256 scope,
110+
uint256[8] calldata proof
111+
) external view returns (bool);
96112
}

packages/contracts/test/Semaphore.ts

Lines changed: 81 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,78 @@ describe("Semaphore", () => {
205205
})
206206

207207
describe("# verifyProof", () => {
208+
const groupId = 10
209+
const message = 2
210+
const identity = new Identity("0")
211+
212+
const group = new Group()
213+
214+
group.addMembers(members)
215+
216+
let fullProof: SemaphoreProof
217+
218+
before(async () => {
219+
await semaphoreContract["createGroup(uint256,address)"](groupId, accounts[0])
220+
221+
await semaphoreContract.addMembers(groupId, members)
222+
223+
fullProof = await generateProof(identity, group, message, group.root as string, 10)
224+
})
225+
226+
it("Should not verify a proof if the group does not exist", async () => {
227+
const transaction = semaphoreContract.verifyProof(11, 1, 0, message, 0, [0, 0, 0, 0, 0, 0, 0, 0])
228+
229+
await expect(transaction).to.be.revertedWithCustomError(semaphoreContract, "Semaphore__GroupDoesNotExist")
230+
})
231+
232+
it("Should not verify a proof if the Merkle tree root is not part of the group", async () => {
233+
const transaction = semaphoreContract.verifyProof(groupId, 1, 0, message, 0, [0, 0, 0, 0, 0, 0, 0, 0])
234+
235+
await expect(transaction).to.be.revertedWithCustomError(
236+
semaphoreContract,
237+
"Semaphore__MerkleTreeRootIsNotPartOfTheGroup"
238+
)
239+
})
240+
241+
it("Should verify a proof for an onchain group", async () => {
242+
const validProof = await semaphoreContract.verifyProof(
243+
groupId,
244+
fullProof.merkleRoot,
245+
fullProof.nullifier,
246+
fullProof.message,
247+
fullProof.merkleRoot,
248+
fullProof.proof
249+
)
250+
251+
expect(validProof).to.equal(true)
252+
})
253+
254+
it("Should not verify a proof if the Merkle tree root is expired", async () => {
255+
const groupId = 2
256+
257+
const group = new Group()
258+
259+
group.addMembers([members[0], members[1]])
260+
261+
const fullProof = await generateProof(identity, group, message, group.root as string, 10)
262+
263+
const transaction = semaphoreContract.verifyProof(
264+
groupId,
265+
fullProof.merkleRoot,
266+
fullProof.nullifier,
267+
fullProof.message,
268+
fullProof.merkleRoot,
269+
fullProof.proof
270+
)
271+
272+
await expect(transaction).to.be.revertedWithCustomError(
273+
semaphoreContract,
274+
"Semaphore__MerkleTreeRootIsExpired"
275+
)
276+
})
277+
})
278+
279+
describe("# validateProof", () => {
208280
const message = 2
209281
const identity = new Identity("0")
210282
const groupOneMemberId = 6
@@ -234,23 +306,8 @@ describe("Semaphore", () => {
234306
)
235307
})
236308

237-
it("Should not verify a proof if the group does not exist", async () => {
238-
const transaction = semaphoreContract.verifyProof(10, 1, 0, message, 0, [0, 0, 0, 0, 0, 0, 0, 0])
239-
240-
await expect(transaction).to.be.revertedWithCustomError(semaphoreContract, "Semaphore__GroupDoesNotExist")
241-
})
242-
243-
it("Should not verify a proof if the Merkle tree root is not part of the group", async () => {
244-
const transaction = semaphoreContract.verifyProof(2, 1, 0, message, 0, [0, 0, 0, 0, 0, 0, 0, 0])
245-
246-
await expect(transaction).to.be.revertedWithCustomError(
247-
semaphoreContract,
248-
"Semaphore__MerkleTreeRootIsNotPartOfTheGroup"
249-
)
250-
})
251-
252309
it("Should throw an exception if the proof is not valid", async () => {
253-
const transaction = semaphoreContract.verifyProof(
310+
const transaction = semaphoreContract.validateProof(
254311
groupId,
255312
fullProof.merkleRoot,
256313
fullProof.nullifier,
@@ -262,8 +319,8 @@ describe("Semaphore", () => {
262319
await expect(transaction).to.be.revertedWithCustomError(semaphoreContract, "Semaphore__InvalidProof")
263320
})
264321

265-
it("Should verify a proof for an onchain group with one member correctly", async () => {
266-
const transaction = semaphoreContract.verifyProof(
322+
it("Should validate a proof for an onchain group with one member correctly", async () => {
323+
const transaction = semaphoreContract.validateProof(
267324
groupOneMemberId,
268325
fullProofOneMember.merkleRoot,
269326
fullProofOneMember.nullifier,
@@ -273,7 +330,7 @@ describe("Semaphore", () => {
273330
)
274331

275332
await expect(transaction)
276-
.to.emit(semaphoreContract, "ProofVerified")
333+
.to.emit(semaphoreContract, "ProofValidated")
277334
.withArgs(
278335
groupOneMemberId,
279336
fullProofOneMember.merkleRoot,
@@ -284,8 +341,8 @@ describe("Semaphore", () => {
284341
)
285342
})
286343

287-
it("Should verify a proof for an onchain group with more than one member correctly", async () => {
288-
const transaction = semaphoreContract.verifyProof(
344+
it("Should validate a proof for an onchain group with more than one member correctly", async () => {
345+
const transaction = semaphoreContract.validateProof(
289346
groupId,
290347
fullProof.merkleRoot,
291348
fullProof.nullifier,
@@ -295,7 +352,7 @@ describe("Semaphore", () => {
295352
)
296353

297354
await expect(transaction)
298-
.to.emit(semaphoreContract, "ProofVerified")
355+
.to.emit(semaphoreContract, "ProofValidated")
299356
.withArgs(
300357
groupId,
301358
fullProof.merkleRoot,
@@ -306,8 +363,8 @@ describe("Semaphore", () => {
306363
)
307364
})
308365

309-
it("Should not verify the same proof for an onchain group twice", async () => {
310-
const transaction = semaphoreContract.verifyProof(
366+
it("Should not validate the same proof for an onchain group twice", async () => {
367+
const transaction = semaphoreContract.validateProof(
311368
groupId,
312369
fullProof.merkleRoot,
313370
fullProof.nullifier,
@@ -321,28 +378,5 @@ describe("Semaphore", () => {
321378
"Semaphore__YouAreUsingTheSameNullifierTwice"
322379
)
323380
})
324-
325-
it("Should not verify a proof if the Merkle tree root is expired", async () => {
326-
const groupId = 2
327-
const group = new Group()
328-
329-
group.addMembers([members[0], members[1]])
330-
331-
const fullProof = await generateProof(identity, group, message, group.root as string, 10)
332-
333-
const transaction = semaphoreContract.verifyProof(
334-
groupId,
335-
fullProof.merkleRoot,
336-
fullProof.nullifier,
337-
fullProof.message,
338-
fullProof.merkleRoot,
339-
fullProof.proof
340-
)
341-
342-
await expect(transaction).to.be.revertedWithCustomError(
343-
semaphoreContract,
344-
"Semaphore__MerkleTreeRootIsExpired"
345-
)
346-
})
347381
})
348382
})

0 commit comments

Comments
 (0)