diff --git a/CHANGELOG.md b/CHANGELOG.md index 1da49d30..a836a65e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- New anonymous function `hasRegisteredSIK` for checking if a user has registered a SIK. + ## [0.6.0] - 2023-11-28 ### Added diff --git a/src/services/anonymous.ts b/src/services/anonymous.ts index a91dfd70..37ae8c08 100644 --- a/src/services/anonymous.ts +++ b/src/services/anonymous.ts @@ -79,9 +79,15 @@ export class AnonymousService extends Service implements AnonymousServicePropert ); } - async fetchAccountSIK(address: string) { + async fetchAccountSIK(address: string): Promise { invariant(this.url, 'No URL set'); - return ZkAPI.sik(this.url, address); + return ZkAPI.sik(this.url, address).then((response) => response.sik); + } + + async hasRegisteredSIK(address: string, signature: string, password?: string): Promise { + return Promise.all([AnonymousService.calcSik(address, signature, password), this.fetchAccountSIK(address)]) + .then(([calcSik, fetchSik]) => calcSik === fetchSik) + .catch(() => false); } async fetchZKProof(address: string) { diff --git a/test/integration/zk.test.ts b/test/integration/zk.test.ts index 1bac65e2..c6457b69 100644 --- a/test/integration/zk.test.ts +++ b/test/integration/zk.test.ts @@ -164,6 +164,59 @@ describe('zkSNARK test', () => { expect(votesLeftCount).toEqual(8); // The user voted twice }); }, 285000); + it('should create an anonymous election, vote and check if the user has the SIK registered', async () => { + const census = new PlainCensus(); + const voter = VocdoniSDKClient.generateWalletFromData('just dummy data' + Math.random()); + census.add(voter.address); + + const election = createElection( + census, + { + anonymous: true, + }, + { + maxVoteOverwrites: 9, + } + ); + + let nullifier: string; + let signature: string; + + await client.createAccount(); + + await client + .createElection(election) + .then((electionId) => { + expect(electionId).toMatch(/^[0-9a-fA-F]{64}$/); + client.setElectionId(electionId); + return client.fetchElection(); + }) + .then((publishedElection) => { + expect(publishedElection.electionType.anonymous).toBeTruthy(); + return waitForElectionReady(client, publishedElection.id); + }) + .then(async () => { + client.wallet = voter; + signature = await client.anonymousService.signSIKPayload(voter); + + const vote = new AnonymousVote([0], signature, 'realpassword'); + nullifier = await AnonymousService.calcVoteId(signature, 'realpassword', client.electionId); + + const hasAlreadyVoted = await client.hasAlreadyVoted({ voteId: nullifier }); + expect(hasAlreadyVoted).toBeFalsy(); + + return client.submitVote(vote); + }) + .then(async (voteId) => { + expect(voteId).toEqual(nullifier); + const hasAlreadyVoted = await client.hasAlreadyVoted({ voteId }); + expect(hasAlreadyVoted).toBeTruthy(); + const votesLeftCount = await client.votesLeftCount({ voteId }); + expect(votesLeftCount).toEqual(9); + expect(await client.anonymousService.hasRegisteredSIK(voter.address, signature, 'realpassword')).toBeTruthy(); + expect(await client.anonymousService.hasRegisteredSIK(voter.address, signature, 'wrongpassword')).toBeFalsy(); + }); + }, 285000); it('should create a weighted anonymous election and vote successfully', async () => { const census = new WeightedCensus(); const voter1 = Wallet.createRandom();