From a23f2fff8666b79d4fa3f8e484423ad6c151064d Mon Sep 17 00:00:00 2001 From: Mads Christiansen Date: Tue, 6 Dec 2022 10:27:48 +0100 Subject: [PATCH] [TECH-471] VotingRounds (#210) --- lib/av_client.ts | 26 +++++++++++++++---- .../voter_authorization_coordinator.ts | 5 ++-- lib/av_client/types.ts | 12 +++++++-- package.json | 2 +- test/benaloh_flow.test.ts | 4 +-- test/construct_contest_envelopes.test.ts | 1 + test/fixtures/latestConfig.ts | 3 +++ test/validation_checks.test.ts | 3 ++- test/walkthrough.test.ts | 8 +----- 9 files changed, 44 insertions(+), 20 deletions(-) diff --git a/lib/av_client.ts b/lib/av_client.ts index f3c38293..b16ae3ac 100644 --- a/lib/av_client.ts +++ b/lib/av_client.ts @@ -4,7 +4,7 @@ import { OTPProvider, IdentityConfirmationToken } from "./av_client/connectors/o import { extractContestSelections } from './util/nist_cvr_extractor'; import { AVVerifier } from './av_verifier'; import { constructContestEnvelopes } from './av_client/construct_contest_envelopes'; -import { KeyPair, Affidavit, VerifierItem, CommitmentOpening, SpoilRequestItem, LatestConfig, BallotSelection, ContestEnvelope, BallotConfig, BallotStatus } from './av_client/types'; +import { KeyPair, Affidavit, VerifierItem, CommitmentOpening, SpoilRequestItem, LatestConfig, BallotSelection, ContestEnvelope, BallotConfig, BallotStatus, ContestConfig } from './av_client/types'; import { randomKeyPair } from './av_client/generate_key_pair'; import { generateReceipt } from './av_client/generate_receipt'; import * as jwt from 'jose'; @@ -86,6 +86,7 @@ export const sjcl = sjclLib; export class AVClient implements IAVClient { private authorizationSessionId: string; private email: string; + private votingRoundReference: string; private identityConfirmationToken: IdentityConfirmationToken; private dbbPublicKey: string | undefined; @@ -200,12 +201,13 @@ export class AVClient implements IAVClient { * Registers a voter based on the authorization mode of the Voter Authorizer * Authorization is done by 'proof-of-identity' or 'proof-of-election-codes' */ - public async createVoterRegistration(): Promise { + public async createVoterRegistration(votingRoundReference = "voting-round-1"): Promise { const coordinatorURL = this.getLatestConfig().items.voterAuthorizerConfig.content.voterAuthorizer.url; const voterAuthorizerContextUuid = this.getLatestConfig().items.voterAuthorizerConfig.content.voterAuthorizer.contextUuid; const coordinator = new VoterAuthorizationCoordinator(coordinatorURL, voterAuthorizerContextUuid); const latestConfigAddress = this.getLatestConfig().items.latestConfigItem.address; const authorizationMode = this.getLatestConfig().items.voterAuthorizerConfig.content.voterAuthorizer.authorizationMode; + this.votingRoundReference = votingRoundReference let authorizationResponse: AxiosResponse @@ -238,7 +240,8 @@ export class AVClient implements IAVClient { identifier: decoded['identifier'], publicKey: decoded['public_key'], weight: decoded['weight'] || 1, - voterGroup: decoded['voter_group_key'] + voterGroup: decoded['voter_group_key'], + votingRoundReference: decoded['voting_round_reference'] } } @@ -299,7 +302,7 @@ export class AVClient implements IAVClient { * Should be followed by either {@link AVClient.spoilBallot | spoilBallot} * or {@link AVClient.castBallot | castBallot}. * - * @param cvr Object containing the selections for each contest. + * @param ballotSelection BallotSelection containing the selections for each contest. * @returns Returns the ballot tracking code. Example: * ```javascript * '5e4d8fe41fa3819cc064e2ace0eda8a847fe322594a6fd5a9a51c699e63804b7' @@ -510,6 +513,18 @@ export class AVClient implements IAVClient { return ballotConfigs[voterSession.content.voterGroup] } + public getVoterContestConfigs(): ContestConfig[] { + const voterSession = this.getVoterSession() + const { items: { ballotConfigs, votingRoundConfigs, contestConfigs }} = this.getLatestConfig() + + const myBallotConfig = ballotConfigs[voterSession.content.voterGroup] + const myVotingRoundConfig = votingRoundConfigs[voterSession.content.votingRoundReference] + const contestsICanVoteOn = myBallotConfig.content.contestReferences.filter(value => myVotingRoundConfig.contestReferences.includes(value)); + return contestsICanVoteOn.map(contestReference => { + return contestConfigs[contestReference] + }) + } + public getDbbPublicKey(): string { const dbbPublicKeyFromConfig = this.getLatestConfig().items.genesisConfig.content.publicKey; @@ -535,7 +550,8 @@ export class AVClient implements IAVClient { return await coordinator.requestPublicKeyAuthorization( this.authorizationSessionId, this.identityConfirmationToken, - this.keyPair.publicKey + this.keyPair.publicKey, + this.votingRoundReference ); } diff --git a/lib/av_client/connectors/voter_authorization_coordinator.ts b/lib/av_client/connectors/voter_authorization_coordinator.ts index 9caf0d4f..8f240830 100644 --- a/lib/av_client/connectors/voter_authorization_coordinator.ts +++ b/lib/av_client/connectors/voter_authorization_coordinator.ts @@ -47,12 +47,13 @@ export default class VoterAuthorizationCoordinator { }); } - requestPublicKeyAuthorization(sessionId: string, identityConfirmationToken: IdentityConfirmationToken, publicKey: string): Promise { + requestPublicKeyAuthorization(sessionId: string, identityConfirmationToken: IdentityConfirmationToken, publicKey: string, votingRoundReference: string): Promise { return this.backend.post('request_authorization', { electionContextUuid: this.electionContextUuid, sessionId: sessionId, emailConfirmationToken: identityConfirmationToken, - publicKey: publicKey + publicKey: publicKey, + votingRoundReference: votingRoundReference }) } diff --git a/lib/av_client/types.ts b/lib/av_client/types.ts index 014413cb..57a4bc6b 100644 --- a/lib/av_client/types.ts +++ b/lib/av_client/types.ts @@ -132,6 +132,7 @@ export interface VoterSessionItem extends BaseBoardItem { identifier: string voterGroup: string publicKey: string + votingRoundReference: string } type: "VoterSessionItem" @@ -279,7 +280,7 @@ export interface LatestConfigItems { voterAuthorizerConfig: VoterAuthorizer; ballotConfigs: BallotConfigMap; contestConfigs: ContestConfigMap; - // votingRoundConfigs: VotingRoundConfig[]; + votingRoundConfigs: VotingRoundConfigMap; electionConfig: ElectionConfig; genesisConfig: GenesisConfig; latestConfigItem: BaseBoardItem; @@ -364,7 +365,14 @@ export interface OptionContent { } // Voting Round Config Item -// export type VotingRoundConfig = {} +export type VotingRoundConfigMap = { + [votingRoundReference: string]: VotingRoundConfig +} + +export type VotingRoundConfig = { + reference: string + contestReferences: string[] +} // Election Config Item export type ElectionConfig = { diff --git a/package.json b/package.json index f5c4332d..b17ab4f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@aion-dk/js-client", - "version": "2.0.1", + "version": "2.0.2", "license": "MIT", "description": "Assembly Voting JS client", "main": "dist/lib/av_client.js", diff --git a/test/benaloh_flow.test.ts b/test/benaloh_flow.test.ts index e8d61bd5..1859aa97 100644 --- a/test/benaloh_flow.test.ts +++ b/test/benaloh_flow.test.ts @@ -93,8 +93,8 @@ describe.skip('entire benaloh flow', () => { }).timeout(10000); async function placeVote(client: AVClient) { - const voterId = 'B00000000001'; - const voterEmail = 'markitmarchtest@osetinstitute.org' + const voterId = Math.random().toString(); + const voterEmail = 'dev@assemblyvoting.com' await client.requestAccessCode(voterId, voterEmail).catch((e) => { console.error(e); }); diff --git a/test/construct_contest_envelopes.test.ts b/test/construct_contest_envelopes.test.ts index f50e672c..59dd7bba 100644 --- a/test/construct_contest_envelopes.test.ts +++ b/test/construct_contest_envelopes.test.ts @@ -56,6 +56,7 @@ const clientState: ClientState = { electionConfig: latestConfig.items.electionConfig, genesisConfig: latestConfig.items.genesisConfig, latestConfigItem: latestConfig.items.latestConfigItem, + votingRoundConfigs: latestConfig.items.votingRoundConfigs } }, voterSession: { diff --git a/test/fixtures/latestConfig.ts b/test/fixtures/latestConfig.ts index 1b1a3882..9fe7853b 100644 --- a/test/fixtures/latestConfig.ts +++ b/test/fixtures/latestConfig.ts @@ -153,6 +153,9 @@ const latestConfig: LatestConfig = { }, uuid: "bc1b1ed0-943e-4647-bfc1-0633ba08c05e", } + }, + votingRoundConfigs: { + }, genesisConfig: { content: { diff --git a/test/validation_checks.test.ts b/test/validation_checks.test.ts index f7e0ba3b..5e9c3c57 100644 --- a/test/validation_checks.test.ts +++ b/test/validation_checks.test.ts @@ -63,7 +63,8 @@ describe('Validation checks', () => { authToken: "", identifier: "", voterGroup: "", - publicKey: publicKey + publicKey: publicKey, + votingRoundReference: "" }, registeredAt: "", signature: "71b8d5d9565d7a174cc2938b1a591281538bbbd913032ff87f7d92071e01f335,70cef6d1f4d1ef06f5a1a207c70dcd936a2c6fb508361e61cf4c77aa1175319d", diff --git a/test/walkthrough.test.ts b/test/walkthrough.test.ts index d9f16366..6c3d5f5a 100644 --- a/test/walkthrough.test.ts +++ b/test/walkthrough.test.ts @@ -20,7 +20,6 @@ describe.skip('entire voter flow using OTP authorization', () => { }); it('returns a receipt', async () => { - const _performTest = async () => { const client = new AVClient(bulletinBoardHost + 'us'); await client.initialize(undefined, { privateKey: 'bcafc67ca4af6b462f60d494adb675d8b1cf57b16dfd8d110bbc2453709999b0', @@ -28,7 +27,7 @@ describe.skip('entire voter flow using OTP authorization', () => { }); const voterId = Math.random().toString() - const voterEmail = 'markitmarchtest@osetinstitute.org' + const voterEmail = 'dev@assemblyvoting.com' await client.requestAccessCode(voterId, voterEmail).catch((e) => { console.error(e); expect.fail('AVClient#requestAccessCode failed.'); @@ -58,8 +57,6 @@ describe.skip('entire voter flow using OTP authorization', () => { const receipt = await client.castBallot(affidavit); expect(receipt.trackingCode.length).to.eql(7); - await _performTest(); - } }).timeout(10000); async function extractOTPFromEmail() { @@ -99,7 +96,6 @@ describe.skip('entire voter flow using PoEC authorization', () => { }); it('returns a receipt', async () => { - const _performTest = async () => { const client = new AVClient(bulletinBoardHost + '2904b00f_5abcbf894df3_58'); await client.initialize(undefined, { privateKey: 'bcafc67ca4af6b462f60d494adb675d8b1cf57b16dfd8d110bbc2453709999b0', @@ -125,8 +121,6 @@ describe.skip('entire voter flow using PoEC authorization', () => { const receipt = await client.castBallot(affidavit); expect(receipt.trackingCode.length).to.eql(7) - await _performTest(); - } }).timeout(10000); })