@digitaldefiance/node-ecies-lib 4.7.0 → 4.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ /**
3
+ * Event Logger for Government-Grade Voting
4
+ * Node.js optimized with native Buffer
5
+ * Implements requirement 1.3: Comprehensive event logging with microsecond timestamps
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.PollEventLogger = exports.EventType = void 0;
9
+ var EventType;
10
+ (function (EventType) {
11
+ EventType["PollCreated"] = "poll_created";
12
+ EventType["VoteCast"] = "vote_cast";
13
+ EventType["PollClosed"] = "poll_closed";
14
+ EventType["VoteVerified"] = "vote_verified";
15
+ EventType["TallyComputed"] = "tally_computed";
16
+ EventType["AuditRequested"] = "audit_requested";
17
+ })(EventType || (exports.EventType = EventType = {}));
18
+ /**
19
+ * Comprehensive event logger with sequence tracking
20
+ */
21
+ class PollEventLogger {
22
+ events = [];
23
+ sequence = 0;
24
+ logPollCreated(pollId, creatorId, configuration) {
25
+ return this.appendEvent({
26
+ eventType: EventType.PollCreated,
27
+ pollId,
28
+ creatorId,
29
+ configuration,
30
+ });
31
+ }
32
+ logVoteCast(pollId, voterToken, metadata) {
33
+ return this.appendEvent({
34
+ eventType: EventType.VoteCast,
35
+ pollId,
36
+ voterToken,
37
+ metadata,
38
+ });
39
+ }
40
+ logPollClosed(pollId, tallyHash, metadata) {
41
+ return this.appendEvent({
42
+ eventType: EventType.PollClosed,
43
+ pollId,
44
+ tallyHash,
45
+ metadata,
46
+ });
47
+ }
48
+ logEvent(eventType, pollId, data) {
49
+ return this.appendEvent({
50
+ eventType,
51
+ pollId,
52
+ ...data,
53
+ });
54
+ }
55
+ getEvents() {
56
+ return Object.freeze([...this.events]);
57
+ }
58
+ getEventsForPoll(pollId) {
59
+ const pollIdStr = pollId.toString('hex');
60
+ return Object.freeze(this.events.filter((e) => e.pollId.toString('hex') === pollIdStr));
61
+ }
62
+ getEventsByType(eventType) {
63
+ return Object.freeze(this.events.filter((e) => e.eventType === eventType));
64
+ }
65
+ verifySequence() {
66
+ for (let i = 0; i < this.events.length; i++) {
67
+ if (this.events[i].sequence !== i) {
68
+ return false;
69
+ }
70
+ }
71
+ return true;
72
+ }
73
+ export() {
74
+ const parts = [];
75
+ parts.push(this.encodeNumber(this.events.length));
76
+ for (const event of this.events) {
77
+ parts.push(this.serializeEvent(event));
78
+ }
79
+ return Buffer.concat(parts);
80
+ }
81
+ appendEvent(partial) {
82
+ const entry = {
83
+ sequence: this.sequence++,
84
+ timestamp: this.getMicrosecondTimestamp(),
85
+ ...partial,
86
+ };
87
+ this.events.push(entry);
88
+ return entry;
89
+ }
90
+ serializeEvent(event) {
91
+ const parts = [
92
+ this.encodeNumber(event.sequence),
93
+ this.encodeNumber(event.timestamp),
94
+ Buffer.from(event.eventType, 'utf8'),
95
+ this.encodeNumber(event.pollId.length),
96
+ event.pollId,
97
+ ];
98
+ if (event.creatorId) {
99
+ parts.push(this.encodeNumber(1), this.encodeNumber(event.creatorId.length), event.creatorId);
100
+ }
101
+ else {
102
+ parts.push(this.encodeNumber(0));
103
+ }
104
+ if (event.voterToken) {
105
+ parts.push(this.encodeNumber(1), this.encodeNumber(event.voterToken.length), event.voterToken);
106
+ }
107
+ else {
108
+ parts.push(this.encodeNumber(0));
109
+ }
110
+ if (event.configuration) {
111
+ const configStr = JSON.stringify({
112
+ method: event.configuration.method,
113
+ choices: event.configuration.choices,
114
+ maxWeight: event.configuration.maxWeight?.toString(),
115
+ threshold: event.configuration.threshold,
116
+ });
117
+ const encoded = Buffer.from(configStr, 'utf8');
118
+ parts.push(this.encodeNumber(1), this.encodeNumber(encoded.length), encoded);
119
+ }
120
+ else {
121
+ parts.push(this.encodeNumber(0));
122
+ }
123
+ if (event.tallyHash) {
124
+ parts.push(this.encodeNumber(1), this.encodeNumber(event.tallyHash.length), event.tallyHash);
125
+ }
126
+ else {
127
+ parts.push(this.encodeNumber(0));
128
+ }
129
+ if (event.metadata) {
130
+ const metaStr = JSON.stringify(event.metadata);
131
+ const encoded = Buffer.from(metaStr, 'utf8');
132
+ parts.push(this.encodeNumber(1), this.encodeNumber(encoded.length), encoded);
133
+ }
134
+ else {
135
+ parts.push(this.encodeNumber(0));
136
+ }
137
+ return Buffer.concat(parts);
138
+ }
139
+ getMicrosecondTimestamp() {
140
+ // Get milliseconds since epoch and convert to microseconds
141
+ // performance.now() is relative to process start, not epoch, so we only use Date.now()
142
+ const now = Date.now();
143
+ return now * 1000;
144
+ }
145
+ encodeNumber(n) {
146
+ const buffer = Buffer.alloc(8);
147
+ buffer.writeBigUInt64BE(BigInt(n));
148
+ return buffer;
149
+ }
150
+ }
151
+ exports.PollEventLogger = PollEventLogger;
152
+ //# sourceMappingURL=event-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-logger.js","sourceRoot":"","sources":["../../../../../../packages/digitaldefiance-node-ecies-lib/src/lib/voting/event-logger.ts"],"names":[],"mappings":"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"}
@@ -2,10 +2,13 @@
2
2
  * Secure Voting System - Node.js Optimized
3
3
  */
4
4
  export { Poll } from './poll-core';
5
+ export { VotingPoll, type ECKeyPairBuffer, type VotingPollResults, } from './poll';
5
6
  export { VoteEncoder } from './encoder';
6
7
  export { PollTallier } from './tallier';
7
8
  export { PollFactory } from './factory';
8
9
  export { VotingSecurityValidator, VOTING_SECURITY, SecurityLevel, } from './security';
9
10
  export { ImmutableAuditLog, AuditEventType, type AuditEntry, type AuditLog, } from './audit';
11
+ export { PublicBulletinBoard, type BulletinBoard, type BulletinBoardEntry, type TallyProof, } from './bulletin-board';
12
+ export { PollEventLogger, EventType, type EventLogger, type EventLogEntry, type PollConfiguration, } from './event-logger';
10
13
  export { VotingMethod, type VoteReceipt, type PollResults, type RoundResult, type EncryptedVote, type SupermajorityConfig, type PlaintextVote, } from './types';
11
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../packages/digitaldefiance-node-ecies-lib/src/lib/voting/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,KAAK,UAAU,EACf,KAAK,QAAQ,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../packages/digitaldefiance-node-ecies-lib/src/lib/voting/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EACL,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,KAAK,UAAU,EACf,KAAK,QAAQ,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,UAAU,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,eAAe,EACf,SAAS,EACT,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,SAAS,CAAC"}
@@ -3,9 +3,11 @@
3
3
  * Secure Voting System - Node.js Optimized
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.VotingMethod = exports.AuditEventType = exports.ImmutableAuditLog = exports.SecurityLevel = exports.VOTING_SECURITY = exports.VotingSecurityValidator = exports.PollFactory = exports.PollTallier = exports.VoteEncoder = exports.Poll = void 0;
6
+ exports.VotingMethod = exports.EventType = exports.PollEventLogger = exports.PublicBulletinBoard = exports.AuditEventType = exports.ImmutableAuditLog = exports.SecurityLevel = exports.VOTING_SECURITY = exports.VotingSecurityValidator = exports.PollFactory = exports.PollTallier = exports.VoteEncoder = exports.VotingPoll = exports.Poll = void 0;
7
7
  var poll_core_1 = require("./poll-core");
8
8
  Object.defineProperty(exports, "Poll", { enumerable: true, get: function () { return poll_core_1.Poll; } });
9
+ var poll_1 = require("./poll");
10
+ Object.defineProperty(exports, "VotingPoll", { enumerable: true, get: function () { return poll_1.VotingPoll; } });
9
11
  var encoder_1 = require("./encoder");
10
12
  Object.defineProperty(exports, "VoteEncoder", { enumerable: true, get: function () { return encoder_1.VoteEncoder; } });
11
13
  var tallier_1 = require("./tallier");
@@ -19,6 +21,11 @@ Object.defineProperty(exports, "SecurityLevel", { enumerable: true, get: functio
19
21
  var audit_1 = require("./audit");
20
22
  Object.defineProperty(exports, "ImmutableAuditLog", { enumerable: true, get: function () { return audit_1.ImmutableAuditLog; } });
21
23
  Object.defineProperty(exports, "AuditEventType", { enumerable: true, get: function () { return audit_1.AuditEventType; } });
24
+ var bulletin_board_1 = require("./bulletin-board");
25
+ Object.defineProperty(exports, "PublicBulletinBoard", { enumerable: true, get: function () { return bulletin_board_1.PublicBulletinBoard; } });
26
+ var event_logger_1 = require("./event-logger");
27
+ Object.defineProperty(exports, "PollEventLogger", { enumerable: true, get: function () { return event_logger_1.PollEventLogger; } });
28
+ Object.defineProperty(exports, "EventType", { enumerable: true, get: function () { return event_logger_1.EventType; } });
22
29
  var types_1 = require("./types");
23
30
  Object.defineProperty(exports, "VotingMethod", { enumerable: true, get: function () { return types_1.VotingMethod; } });
24
31
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/digitaldefiance-node-ecies-lib/src/lib/voting/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,yCAAmC;AAA1B,iGAAA,IAAI,OAAA;AACb,qCAAwC;AAA/B,sGAAA,WAAW,OAAA;AACpB,qCAAwC;AAA/B,sGAAA,WAAW,OAAA;AACpB,qCAAwC;AAA/B,sGAAA,WAAW,OAAA;AACpB,uCAIoB;AAHlB,mHAAA,uBAAuB,OAAA;AACvB,2GAAA,eAAe,OAAA;AACf,yGAAA,aAAa,OAAA;AAEf,iCAKiB;AAJf,0GAAA,iBAAiB,OAAA;AACjB,uGAAA,cAAc,OAAA;AAIhB,iCAQiB;AAPf,qGAAA,YAAY,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/digitaldefiance-node-ecies-lib/src/lib/voting/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,yCAAmC;AAA1B,iGAAA,IAAI,OAAA;AACb,+BAIgB;AAHd,kGAAA,UAAU,OAAA;AAIZ,qCAAwC;AAA/B,sGAAA,WAAW,OAAA;AACpB,qCAAwC;AAA/B,sGAAA,WAAW,OAAA;AACpB,qCAAwC;AAA/B,sGAAA,WAAW,OAAA;AACpB,uCAIoB;AAHlB,mHAAA,uBAAuB,OAAA;AACvB,2GAAA,eAAe,OAAA;AACf,yGAAA,aAAa,OAAA;AAEf,iCAKiB;AAJf,0GAAA,iBAAiB,OAAA;AACjB,uGAAA,cAAc,OAAA;AAIhB,mDAK0B;AAJxB,qHAAA,mBAAmB,OAAA;AAKrB,+CAMwB;AALtB,+GAAA,eAAe,OAAA;AACf,yGAAA,SAAS,OAAA;AAKX,iCAQiB;AAPf,qGAAA,YAAY,OAAA"}
@@ -0,0 +1,124 @@
1
+ import { KeyPair as PaillierKeyPair } from 'paillier-bigint';
2
+ import { Member } from '../../member';
3
+ import { ECIESService } from '../../services/ecies/service';
4
+ import { VotingService } from '../../services/voting.service';
5
+ export interface ECKeyPairBuffer {
6
+ privateKey: Buffer;
7
+ publicKey: Buffer;
8
+ }
9
+ /**
10
+ * Result of a voting poll with decrypted tallies
11
+ */
12
+ export interface VotingPollResults {
13
+ /** Total number of votes cast */
14
+ totalVotes: bigint;
15
+ /** Tallies for each choice */
16
+ tallies: bigint[];
17
+ /** Choice names */
18
+ choices: string[];
19
+ /** Percentage for each choice (0-100) */
20
+ percentages: number[];
21
+ /** Index of the winning choice */
22
+ winnerIndex: number;
23
+ /** Name of the winning choice */
24
+ winnerName: string;
25
+ /** Number of unique voters */
26
+ voterCount: number;
27
+ }
28
+ /**
29
+ * VotingPoll provides a high-level interface for conducting secure, verifiable polls
30
+ * using Paillier homomorphic encryption.
31
+ *
32
+ * Features:
33
+ * - Privacy-preserving vote aggregation (votes remain encrypted until tally)
34
+ * - Verifiable receipts for each voter
35
+ * - Multiple tallying and analysis methods
36
+ * - Ranked choice voting support
37
+ * - Weighted voting support
38
+ */
39
+ export declare class VotingPoll {
40
+ readonly choices: string[];
41
+ readonly votes: bigint[];
42
+ private readonly paillierKeyPair;
43
+ private readonly ecKeyPair;
44
+ private readonly eciesService;
45
+ readonly receipts: Map<string, Buffer>;
46
+ private readonly createdAt;
47
+ private closedAt?;
48
+ constructor(eciesService: ECIESService, choices: string[], paillierKeyPair: PaillierKeyPair, ecKeyPair: ECKeyPairBuffer, votes: bigint[]);
49
+ generateEncryptedReceipt(member: Member): Promise<Buffer>;
50
+ memberVoted(member: Member): boolean;
51
+ verifyReceipt(member: Member, encryptedReceipt: Buffer): boolean;
52
+ vote(choiceIndex: number, member: Member): Promise<Buffer>;
53
+ voteWeighted(choiceIndex: number, weight: bigint, member: Member): Promise<Buffer>;
54
+ voteRanked(rankedChoices: number[], member: Member): Promise<Buffer>;
55
+ voteApproval(approvedChoices: number[], member: Member): Promise<Buffer>;
56
+ get tallies(): bigint[];
57
+ getTally(choiceIndex: number): bigint;
58
+ get leadingChoice(): string;
59
+ /**
60
+ * Get the index of the leading choice
61
+ */
62
+ get leadingChoiceIndex(): number;
63
+ /**
64
+ * Get complete poll results with percentages and winner
65
+ */
66
+ getResults(): VotingPollResults;
67
+ /**
68
+ * Get sorted results (descending by vote count)
69
+ */
70
+ getSortedResults(): Array<{
71
+ choice: string;
72
+ index: number;
73
+ tally: bigint;
74
+ percentage: number;
75
+ }>;
76
+ /**
77
+ * Check if there is a tie for first place
78
+ */
79
+ get hasTie(): boolean;
80
+ /**
81
+ * Get all choices tied for first place
82
+ */
83
+ get tiedChoices(): string[];
84
+ /**
85
+ * Get total number of unique voters
86
+ */
87
+ get voterCount(): number;
88
+ /**
89
+ * Get total encrypted votes (sum of all choice tallies)
90
+ * Note: This returns encrypted sum - decrypt with getTotalVotes()
91
+ */
92
+ get encryptedTotalVotes(): bigint;
93
+ /**
94
+ * Get total decrypted vote count
95
+ */
96
+ getTotalVotes(): bigint;
97
+ /**
98
+ * Close the poll (no more votes can be cast)
99
+ */
100
+ close(): void;
101
+ /**
102
+ * Check if poll is closed
103
+ */
104
+ get isClosed(): boolean;
105
+ /**
106
+ * Get poll creation timestamp
107
+ */
108
+ get createdAtTimestamp(): Date;
109
+ /**
110
+ * Get poll closed timestamp (undefined if not closed)
111
+ */
112
+ get closedAtTimestamp(): Date | undefined;
113
+ /**
114
+ * Get poll duration in milliseconds (undefined if not closed)
115
+ */
116
+ get durationMs(): number | undefined;
117
+ static newPoll(eciesService: ECIESService, choices: string[], paillierKeyPair: PaillierKeyPair, ecKeyPair: ECKeyPairBuffer): VotingPoll;
118
+ static newPollWithKeys(eciesService: ECIESService, votingService: VotingService, choices: string[]): Promise<{
119
+ poll: VotingPoll;
120
+ paillierKeyPair: PaillierKeyPair;
121
+ ecKeyPair: ECKeyPairBuffer;
122
+ }>;
123
+ }
124
+ //# sourceMappingURL=poll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../../../../../../packages/digitaldefiance-node-ecies-lib/src/lib/voting/poll.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,323 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VotingPoll = void 0;
4
+ /**
5
+ * VotingPoll - High-level interface for secure, verifiable polls
6
+ * Node.js optimized with native crypto and Buffer
7
+ */
8
+ const crypto_1 = require("crypto");
9
+ const constants_1 = require("../../constants");
10
+ /**
11
+ * VotingPoll provides a high-level interface for conducting secure, verifiable polls
12
+ * using Paillier homomorphic encryption.
13
+ *
14
+ * Features:
15
+ * - Privacy-preserving vote aggregation (votes remain encrypted until tally)
16
+ * - Verifiable receipts for each voter
17
+ * - Multiple tallying and analysis methods
18
+ * - Ranked choice voting support
19
+ * - Weighted voting support
20
+ */
21
+ class VotingPoll {
22
+ choices;
23
+ votes;
24
+ paillierKeyPair;
25
+ ecKeyPair;
26
+ eciesService;
27
+ receipts = new Map();
28
+ createdAt;
29
+ closedAt;
30
+ constructor(eciesService, choices, paillierKeyPair, ecKeyPair, votes) {
31
+ this.eciesService = eciesService;
32
+ if (choices.length === 0) {
33
+ throw new Error('Poll must have at least one choice');
34
+ }
35
+ if (choices.length !== votes.length) {
36
+ throw new Error('Number of choices must match number of vote tallies');
37
+ }
38
+ this.choices = choices;
39
+ this.paillierKeyPair = paillierKeyPair;
40
+ this.ecKeyPair = ecKeyPair;
41
+ this.votes = votes;
42
+ this.createdAt = new Date();
43
+ }
44
+ async generateEncryptedReceipt(member) {
45
+ const randomNonce = (0, crypto_1.randomBytes)(16).toString(constants_1.Constants.VOTING.KEY_FORMAT);
46
+ const memberId = constants_1.Constants.idProvider.serialize(member.id);
47
+ const hashInput = `${Date.now()}-${randomNonce}-${memberId}`;
48
+ const hash = (0, crypto_1.createHash)('sha256').update(hashInput).digest();
49
+ const signature = this.eciesService.signMessage(this.ecKeyPair.privateKey, hash);
50
+ const receipt = Buffer.concat([hash, signature]);
51
+ // Encrypt to the poll's public key so the poll can decrypt and verify later
52
+ const encryptedReceipt = this.eciesService.encryptSimpleOrSingle(false, this.ecKeyPair.publicKey, receipt);
53
+ this.receipts.set(memberId, encryptedReceipt);
54
+ return encryptedReceipt;
55
+ }
56
+ memberVoted(member) {
57
+ const memberId = constants_1.Constants.idProvider.serialize(member.id);
58
+ return this.receipts.has(memberId);
59
+ }
60
+ verifyReceipt(member, encryptedReceipt) {
61
+ const memberId = constants_1.Constants.idProvider.serialize(member.id);
62
+ const foundReceipt = this.receipts.get(memberId);
63
+ if (!foundReceipt) {
64
+ return false;
65
+ }
66
+ if (!foundReceipt.equals(encryptedReceipt)) {
67
+ return false;
68
+ }
69
+ const decryptedReceipt = this.eciesService.decryptSimpleOrSingleWithHeader(false, this.ecKeyPair.privateKey, encryptedReceipt);
70
+ const hash = decryptedReceipt.subarray(0, 32);
71
+ const signature = decryptedReceipt.subarray(32);
72
+ return this.eciesService.verifyMessage(this.ecKeyPair.publicKey, hash, signature);
73
+ }
74
+ async vote(choiceIndex, member) {
75
+ if (this.isClosed) {
76
+ throw new Error('Poll is closed');
77
+ }
78
+ if (this.memberVoted(member)) {
79
+ throw new Error('Member has already voted');
80
+ }
81
+ if (choiceIndex < 0 || choiceIndex >= this.choices.length) {
82
+ throw new Error(`Invalid option index ${choiceIndex}`);
83
+ }
84
+ // vote a 1 for the selected candidate and a 0 for all others
85
+ for (let i = 0; i < this.choices.length; i++) {
86
+ if (i == choiceIndex) {
87
+ this.votes[i] = this.paillierKeyPair.publicKey.addition(this.votes[i], this.paillierKeyPair.publicKey.encrypt(1n));
88
+ }
89
+ else {
90
+ this.votes[i] = this.paillierKeyPair.publicKey.addition(this.votes[i], this.paillierKeyPair.publicKey.encrypt(0n));
91
+ }
92
+ }
93
+ return await this.generateEncryptedReceipt(member);
94
+ }
95
+ async voteWeighted(choiceIndex, weight, member) {
96
+ if (this.isClosed) {
97
+ throw new Error('Poll is closed');
98
+ }
99
+ if (this.memberVoted(member)) {
100
+ throw new Error('Member has already voted');
101
+ }
102
+ if (choiceIndex < 0 || choiceIndex >= this.choices.length) {
103
+ throw new Error(`Invalid option index ${choiceIndex}`);
104
+ }
105
+ if (weight <= 0n) {
106
+ throw new Error('Vote weight must be positive');
107
+ }
108
+ // Add weighted vote to selected choice
109
+ this.votes[choiceIndex] = this.paillierKeyPair.publicKey.addition(this.votes[choiceIndex], this.paillierKeyPair.publicKey.encrypt(weight));
110
+ return await this.generateEncryptedReceipt(member);
111
+ }
112
+ async voteRanked(rankedChoices, member) {
113
+ if (this.isClosed) {
114
+ throw new Error('Poll is closed');
115
+ }
116
+ if (this.memberVoted(member)) {
117
+ throw new Error('Member has already voted');
118
+ }
119
+ if (rankedChoices.length === 0) {
120
+ throw new Error('Must provide at least one ranked choice');
121
+ }
122
+ // Validate all choices are valid and unique
123
+ const seen = new Set();
124
+ for (const choiceIndex of rankedChoices) {
125
+ if (choiceIndex < 0 || choiceIndex >= this.choices.length) {
126
+ throw new Error(`Invalid choice index ${choiceIndex}`);
127
+ }
128
+ if (seen.has(choiceIndex)) {
129
+ throw new Error(`Duplicate choice index ${choiceIndex}`);
130
+ }
131
+ seen.add(choiceIndex);
132
+ }
133
+ // Award points based on ranking (first choice gets highest points)
134
+ const maxPoints = BigInt(rankedChoices.length);
135
+ for (let i = 0; i < rankedChoices.length; i++) {
136
+ const choiceIndex = rankedChoices[i];
137
+ const points = maxPoints - BigInt(i); // First choice gets n points, second gets n-1, etc.
138
+ this.votes[choiceIndex] = this.paillierKeyPair.publicKey.addition(this.votes[choiceIndex], this.paillierKeyPair.publicKey.encrypt(points));
139
+ }
140
+ return await this.generateEncryptedReceipt(member);
141
+ }
142
+ async voteApproval(approvedChoices, member) {
143
+ if (this.isClosed) {
144
+ throw new Error('Poll is closed');
145
+ }
146
+ if (this.memberVoted(member)) {
147
+ throw new Error('Member has already voted');
148
+ }
149
+ if (approvedChoices.length === 0) {
150
+ throw new Error('Must approve at least one choice');
151
+ }
152
+ // Validate all choices and check for duplicates
153
+ const seen = new Set();
154
+ for (const choiceIndex of approvedChoices) {
155
+ if (choiceIndex < 0 || choiceIndex >= this.choices.length) {
156
+ throw new Error(`Invalid choice index ${choiceIndex}`);
157
+ }
158
+ if (seen.has(choiceIndex)) {
159
+ throw new Error(`Duplicate choice index ${choiceIndex}`);
160
+ }
161
+ seen.add(choiceIndex);
162
+ }
163
+ // Add 1 vote to each approved choice
164
+ for (const choiceIndex of approvedChoices) {
165
+ this.votes[choiceIndex] = this.paillierKeyPair.publicKey.addition(this.votes[choiceIndex], this.paillierKeyPair.publicKey.encrypt(1n));
166
+ }
167
+ return await this.generateEncryptedReceipt(member);
168
+ }
169
+ get tallies() {
170
+ return this.votes.map((encryptedVote) => this.paillierKeyPair.privateKey.decrypt(encryptedVote));
171
+ }
172
+ getTally(choiceIndex) {
173
+ return this.paillierKeyPair.privateKey.decrypt(this.votes[choiceIndex]);
174
+ }
175
+ get leadingChoice() {
176
+ const tallies = this.tallies;
177
+ let leadingOptionIndex = 0;
178
+ for (let i = 1; i < tallies.length; i++) {
179
+ if (tallies[i] > tallies[leadingOptionIndex]) {
180
+ leadingOptionIndex = i;
181
+ }
182
+ }
183
+ return this.choices[leadingOptionIndex];
184
+ }
185
+ /**
186
+ * Get the index of the leading choice
187
+ */
188
+ get leadingChoiceIndex() {
189
+ const tallies = this.tallies;
190
+ let leadingOptionIndex = 0;
191
+ for (let i = 1; i < tallies.length; i++) {
192
+ if (tallies[i] > tallies[leadingOptionIndex]) {
193
+ leadingOptionIndex = i;
194
+ }
195
+ }
196
+ return leadingOptionIndex;
197
+ }
198
+ /**
199
+ * Get complete poll results with percentages and winner
200
+ */
201
+ getResults() {
202
+ const tallies = this.tallies;
203
+ const totalVotes = tallies.reduce((sum, tally) => sum + tally, 0n);
204
+ const percentages = tallies.map((tally) => totalVotes > 0n ? Number((tally * 10000n) / totalVotes) / 100 : 0);
205
+ return {
206
+ totalVotes,
207
+ tallies,
208
+ choices: [...this.choices],
209
+ percentages,
210
+ winnerIndex: this.leadingChoiceIndex,
211
+ winnerName: this.leadingChoice,
212
+ voterCount: this.receipts.size,
213
+ };
214
+ }
215
+ /**
216
+ * Get sorted results (descending by vote count)
217
+ */
218
+ getSortedResults() {
219
+ const results = this.getResults();
220
+ return this.choices
221
+ .map((choice, index) => ({
222
+ choice,
223
+ index,
224
+ tally: results.tallies[index],
225
+ percentage: results.percentages[index],
226
+ }))
227
+ .sort((a, b) => (b.tally > a.tally ? 1 : b.tally < a.tally ? -1 : 0));
228
+ }
229
+ /**
230
+ * Check if there is a tie for first place
231
+ */
232
+ get hasTie() {
233
+ const tallies = this.tallies;
234
+ const maxTally = tallies.reduce((max, tally) => (tally > max ? tally : max), 0n);
235
+ return tallies.filter((tally) => tally === maxTally).length > 1;
236
+ }
237
+ /**
238
+ * Get all choices tied for first place
239
+ */
240
+ get tiedChoices() {
241
+ if (!this.hasTie) {
242
+ return [];
243
+ }
244
+ const tallies = this.tallies;
245
+ const maxTally = tallies.reduce((max, tally) => (tally > max ? tally : max), 0n);
246
+ return this.choices.filter((_, index) => tallies[index] === maxTally);
247
+ }
248
+ /**
249
+ * Get total number of unique voters
250
+ */
251
+ get voterCount() {
252
+ return this.receipts.size;
253
+ }
254
+ /**
255
+ * Get total encrypted votes (sum of all choice tallies)
256
+ * Note: This returns encrypted sum - decrypt with getTotalVotes()
257
+ */
258
+ get encryptedTotalVotes() {
259
+ return this.votes.reduce((sum, vote) => this.paillierKeyPair.publicKey.addition(sum, vote), this.paillierKeyPair.publicKey.encrypt(0n));
260
+ }
261
+ /**
262
+ * Get total decrypted vote count
263
+ */
264
+ getTotalVotes() {
265
+ return this.paillierKeyPair.privateKey.decrypt(this.encryptedTotalVotes);
266
+ }
267
+ /**
268
+ * Close the poll (no more votes can be cast)
269
+ */
270
+ close() {
271
+ if (this.isClosed) {
272
+ throw new Error('Poll is already closed');
273
+ }
274
+ this.closedAt = new Date();
275
+ }
276
+ /**
277
+ * Check if poll is closed
278
+ */
279
+ get isClosed() {
280
+ return this.closedAt !== undefined;
281
+ }
282
+ /**
283
+ * Get poll creation timestamp
284
+ */
285
+ get createdAtTimestamp() {
286
+ return new Date(this.createdAt);
287
+ }
288
+ /**
289
+ * Get poll closed timestamp (undefined if not closed)
290
+ */
291
+ get closedAtTimestamp() {
292
+ return this.closedAt ? new Date(this.closedAt) : undefined;
293
+ }
294
+ /**
295
+ * Get poll duration in milliseconds (undefined if not closed)
296
+ */
297
+ get durationMs() {
298
+ if (!this.closedAt) {
299
+ return undefined;
300
+ }
301
+ return this.closedAt.getTime() - this.createdAt.getTime();
302
+ }
303
+ static newPoll(eciesService, choices, paillierKeyPair, ecKeyPair) {
304
+ const votes = new Array(choices.length);
305
+ for (let i = 0; i < choices.length; i++) {
306
+ votes[i] = paillierKeyPair.publicKey.encrypt(0n);
307
+ }
308
+ return new VotingPoll(eciesService, choices, paillierKeyPair, ecKeyPair, votes);
309
+ }
310
+ static async newPollWithKeys(eciesService, votingService, choices) {
311
+ const mnemonic = eciesService.generateNewMnemonic();
312
+ const keyPair = eciesService.mnemonicToSimpleKeyPair(mnemonic);
313
+ const ecKeyPair = {
314
+ privateKey: keyPair.privateKey,
315
+ publicKey: keyPair.publicKey,
316
+ };
317
+ const paillierKeyPair = await votingService.deriveVotingKeysFromECDH(ecKeyPair.privateKey, ecKeyPair.publicKey);
318
+ const poll = VotingPoll.newPoll(eciesService, choices, paillierKeyPair, ecKeyPair);
319
+ return { poll, paillierKeyPair, ecKeyPair };
320
+ }
321
+ }
322
+ exports.VotingPoll = VotingPoll;
323
+ //# sourceMappingURL=poll.js.map