@digitaldefiance/ecies-lib 4.17.10 → 4.19.0
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.
- package/README.md +3 -2
- package/package.json +2 -2
- package/src/lib/voting/threshold/ceremony-coordinator.d.ts +152 -0
- package/src/lib/voting/threshold/ceremony-coordinator.d.ts.map +1 -0
- package/src/lib/voting/threshold/ceremony-coordinator.js +316 -0
- package/src/lib/voting/threshold/ceremony-coordinator.js.map +1 -0
- package/src/lib/voting/threshold/decryption-combiner.d.ts.map +1 -1
- package/src/lib/voting/threshold/decryption-combiner.js +3 -3
- package/src/lib/voting/threshold/decryption-combiner.js.map +1 -1
- package/src/lib/voting/threshold/guardian-registry.d.ts +1 -1
- package/src/lib/voting/threshold/guardian-registry.d.ts.map +1 -1
- package/src/lib/voting/threshold/index.d.ts +11 -0
- package/src/lib/voting/threshold/index.d.ts.map +1 -1
- package/src/lib/voting/threshold/index.js +32 -1
- package/src/lib/voting/threshold/index.js.map +1 -1
- package/src/lib/voting/threshold/interfaces/partial-decryption.d.ts +2 -2
- package/src/lib/voting/threshold/interfaces/partial-decryption.d.ts.map +1 -1
- package/src/lib/voting/threshold/interval-scheduler.d.ts +123 -0
- package/src/lib/voting/threshold/interval-scheduler.d.ts.map +1 -0
- package/src/lib/voting/threshold/interval-scheduler.js +281 -0
- package/src/lib/voting/threshold/interval-scheduler.js.map +1 -0
- package/src/lib/voting/threshold/partial-decryption-service.d.ts.map +1 -1
- package/src/lib/voting/threshold/partial-decryption-service.js +16 -15
- package/src/lib/voting/threshold/partial-decryption-service.js.map +1 -1
- package/src/lib/voting/threshold/public-tally-feed.d.ts +100 -0
- package/src/lib/voting/threshold/public-tally-feed.d.ts.map +1 -0
- package/src/lib/voting/threshold/public-tally-feed.js +202 -0
- package/src/lib/voting/threshold/public-tally-feed.js.map +1 -0
- package/src/lib/voting/threshold/tally-verifier.d.ts +85 -0
- package/src/lib/voting/threshold/tally-verifier.d.ts.map +1 -0
- package/src/lib/voting/threshold/tally-verifier.js +169 -0
- package/src/lib/voting/threshold/tally-verifier.js.map +1 -0
- package/src/lib/voting/threshold/threshold-audit-log.d.ts +71 -0
- package/src/lib/voting/threshold/threshold-audit-log.d.ts.map +1 -0
- package/src/lib/voting/threshold/threshold-audit-log.js +243 -0
- package/src/lib/voting/threshold/threshold-audit-log.js.map +1 -0
- package/src/lib/voting/threshold/threshold-county-aggregator.d.ts +81 -0
- package/src/lib/voting/threshold/threshold-county-aggregator.d.ts.map +1 -0
- package/src/lib/voting/threshold/threshold-county-aggregator.js +154 -0
- package/src/lib/voting/threshold/threshold-county-aggregator.js.map +1 -0
- package/src/lib/voting/threshold/threshold-key-generator.d.ts.map +1 -1
- package/src/lib/voting/threshold/threshold-key-generator.js.map +1 -1
- package/src/lib/voting/threshold/threshold-national-aggregator.d.ts +95 -0
- package/src/lib/voting/threshold/threshold-national-aggregator.d.ts.map +1 -0
- package/src/lib/voting/threshold/threshold-national-aggregator.js +210 -0
- package/src/lib/voting/threshold/threshold-national-aggregator.js.map +1 -0
- package/src/lib/voting/threshold/threshold-poll-factory.d.ts +88 -0
- package/src/lib/voting/threshold/threshold-poll-factory.d.ts.map +1 -0
- package/src/lib/voting/threshold/threshold-poll-factory.js +156 -0
- package/src/lib/voting/threshold/threshold-poll-factory.js.map +1 -0
- package/src/lib/voting/threshold/threshold-poll.d.ts +76 -0
- package/src/lib/voting/threshold/threshold-poll.d.ts.map +1 -0
- package/src/lib/voting/threshold/threshold-poll.js +144 -0
- package/src/lib/voting/threshold/threshold-poll.js.map +1 -0
- package/src/lib/voting/threshold/threshold-precinct-aggregator.d.ts +86 -0
- package/src/lib/voting/threshold/threshold-precinct-aggregator.d.ts.map +1 -0
- package/src/lib/voting/threshold/threshold-precinct-aggregator.js +156 -0
- package/src/lib/voting/threshold/threshold-precinct-aggregator.js.map +1 -0
- package/src/lib/voting/threshold/threshold-state-aggregator.d.ts +85 -0
- package/src/lib/voting/threshold/threshold-state-aggregator.d.ts.map +1 -0
- package/src/lib/voting/threshold/threshold-state-aggregator.js +164 -0
- package/src/lib/voting/threshold/threshold-state-aggregator.js.map +1 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Threshold Poll
|
|
4
|
+
*
|
|
5
|
+
* Extends the standard Poll with threshold decryption support,
|
|
6
|
+
* integrating IntervalScheduler, CeremonyCoordinator, and PublicTallyFeed.
|
|
7
|
+
* Uses the same encryption (Paillier) and vote encoding as standard polls.
|
|
8
|
+
*
|
|
9
|
+
* @module voting/threshold
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ThresholdPoll = void 0;
|
|
13
|
+
const poll_core_1 = require("../poll-core");
|
|
14
|
+
const ceremony_coordinator_1 = require("./ceremony-coordinator");
|
|
15
|
+
const interval_scheduler_1 = require("./interval-scheduler");
|
|
16
|
+
const public_tally_feed_1 = require("./public-tally-feed");
|
|
17
|
+
/**
|
|
18
|
+
* A poll with threshold decryption support.
|
|
19
|
+
*
|
|
20
|
+
* Wraps a standard Poll and adds:
|
|
21
|
+
* - Interval scheduling for periodic decryption ceremonies
|
|
22
|
+
* - Ceremony coordination for collecting Guardian partial decryptions
|
|
23
|
+
* - Public tally feed for broadcasting verified interval tallies
|
|
24
|
+
*
|
|
25
|
+
* Uses the same encryption and vote encoding as standard polls,
|
|
26
|
+
* ensuring full compatibility with existing vote encoders and talliers.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const poll = new ThresholdPoll(
|
|
31
|
+
* id, choices, method, authority, publicKey, thresholdConfig,
|
|
32
|
+
* );
|
|
33
|
+
*
|
|
34
|
+
* // Cast votes (same as standard poll)
|
|
35
|
+
* poll.vote(voter, encryptedVote);
|
|
36
|
+
*
|
|
37
|
+
* // Start interval scheduling
|
|
38
|
+
* poll.intervalScheduler.start(poll.id);
|
|
39
|
+
*
|
|
40
|
+
* // Close triggers final ceremony
|
|
41
|
+
* poll.close();
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
class ThresholdPoll {
|
|
45
|
+
_innerPoll;
|
|
46
|
+
_thresholdConfig;
|
|
47
|
+
_intervalConfig;
|
|
48
|
+
_intervalScheduler;
|
|
49
|
+
_ceremonyCoordinator;
|
|
50
|
+
_tallyFeed;
|
|
51
|
+
constructor(id, choices, method, authority, publicKey, config) {
|
|
52
|
+
// Create the inner standard poll (same encryption and vote encoding)
|
|
53
|
+
this._innerPoll = new poll_core_1.Poll(id, choices, method, authority, publicKey);
|
|
54
|
+
this._thresholdConfig = config.thresholdConfig;
|
|
55
|
+
this._intervalConfig = config.intervalConfig;
|
|
56
|
+
// Create interval scheduler and configure for this poll
|
|
57
|
+
this._intervalScheduler = new interval_scheduler_1.IntervalScheduler();
|
|
58
|
+
this._intervalScheduler.configure(id, config.intervalConfig);
|
|
59
|
+
// Create ceremony coordinator if key pair is provided
|
|
60
|
+
if (config.keyPair) {
|
|
61
|
+
this._ceremonyCoordinator = new ceremony_coordinator_1.CeremonyCoordinator(config.keyPair.publicKey, config.keyPair.verificationKeys, config.keyPair.theta, config.thresholdConfig, config.intervalConfig.ceremonyTimeoutMs);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// Create a coordinator with the authority's public key
|
|
65
|
+
// (verification keys and theta will need to be set later when key pair is generated)
|
|
66
|
+
this._ceremonyCoordinator = new ceremony_coordinator_1.CeremonyCoordinator(publicKey, [], 0n, config.thresholdConfig, config.intervalConfig.ceremonyTimeoutMs);
|
|
67
|
+
}
|
|
68
|
+
// Create public tally feed
|
|
69
|
+
this._tallyFeed = new public_tally_feed_1.PublicTallyFeed();
|
|
70
|
+
}
|
|
71
|
+
// --- IThresholdPoll properties ---
|
|
72
|
+
get thresholdConfig() {
|
|
73
|
+
return this._thresholdConfig;
|
|
74
|
+
}
|
|
75
|
+
get intervalConfig() {
|
|
76
|
+
return this._intervalConfig;
|
|
77
|
+
}
|
|
78
|
+
get intervalScheduler() {
|
|
79
|
+
return this._intervalScheduler;
|
|
80
|
+
}
|
|
81
|
+
get ceremonyCoordinator() {
|
|
82
|
+
return this._ceremonyCoordinator;
|
|
83
|
+
}
|
|
84
|
+
get tallyFeed() {
|
|
85
|
+
return this._tallyFeed;
|
|
86
|
+
}
|
|
87
|
+
get isThresholdEnabled() {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
// --- IPoll delegation ---
|
|
91
|
+
get id() {
|
|
92
|
+
return this._innerPoll.id;
|
|
93
|
+
}
|
|
94
|
+
get choices() {
|
|
95
|
+
return this._innerPoll.choices;
|
|
96
|
+
}
|
|
97
|
+
get method() {
|
|
98
|
+
return this._innerPoll.method;
|
|
99
|
+
}
|
|
100
|
+
get isClosed() {
|
|
101
|
+
return this._innerPoll.isClosed;
|
|
102
|
+
}
|
|
103
|
+
get voterCount() {
|
|
104
|
+
return this._innerPoll.voterCount;
|
|
105
|
+
}
|
|
106
|
+
get createdAt() {
|
|
107
|
+
return this._innerPoll.createdAt;
|
|
108
|
+
}
|
|
109
|
+
get closedAt() {
|
|
110
|
+
return this._innerPoll.closedAt;
|
|
111
|
+
}
|
|
112
|
+
get auditLog() {
|
|
113
|
+
return this._innerPoll.auditLog;
|
|
114
|
+
}
|
|
115
|
+
getEncryptedVotes() {
|
|
116
|
+
return this._innerPoll.getEncryptedVotes();
|
|
117
|
+
}
|
|
118
|
+
vote(voter, encryptedVote) {
|
|
119
|
+
const receipt = this._innerPoll.vote(voter, encryptedVote);
|
|
120
|
+
// Notify the interval scheduler of the new vote
|
|
121
|
+
try {
|
|
122
|
+
this._intervalScheduler.notifyVote(this.id);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Scheduler may not be started yet; ignore
|
|
126
|
+
}
|
|
127
|
+
return receipt;
|
|
128
|
+
}
|
|
129
|
+
verifyReceipt(voter, receipt) {
|
|
130
|
+
return this._innerPoll.verifyReceipt(voter, receipt);
|
|
131
|
+
}
|
|
132
|
+
close() {
|
|
133
|
+
this._innerPoll.close();
|
|
134
|
+
// Trigger final decryption ceremony
|
|
135
|
+
try {
|
|
136
|
+
this._intervalScheduler.triggerFinal(this.id);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Scheduler may not be configured; ignore
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.ThresholdPoll = ThresholdPoll;
|
|
144
|
+
//# sourceMappingURL=threshold-poll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threshold-poll.js","sourceRoot":"","sources":["../../../../../../../packages/digitaldefiance-ecies-lib/src/lib/voting/threshold/threshold-poll.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threshold Precinct Aggregator
|
|
3
|
+
*
|
|
4
|
+
* Extends the standard PrecinctAggregator with threshold decryption support,
|
|
5
|
+
* enabling interval decryption at the precinct level. Uses the same
|
|
6
|
+
* homomorphic aggregation as the base class but adds the ability to
|
|
7
|
+
* perform threshold decryption ceremonies via a CeremonyCoordinator.
|
|
8
|
+
*
|
|
9
|
+
* @module voting/threshold
|
|
10
|
+
*/
|
|
11
|
+
import type { PublicKey } from 'paillier-bigint';
|
|
12
|
+
import type { PlatformID } from '../../../interfaces/platform-id';
|
|
13
|
+
import { PrecinctAggregator } from '../hierarchical-aggregator';
|
|
14
|
+
import type { ICheckpointManager } from '../interfaces/checkpoint-manager';
|
|
15
|
+
import type { JurisdictionConfig } from '../interfaces/jurisdiction-config';
|
|
16
|
+
import type { IVoteLogger } from '../interfaces/vote-logger';
|
|
17
|
+
import type { Poll } from '../poll-core';
|
|
18
|
+
import type { ICeremonyCoordinator } from './interfaces/ceremony-coordinator';
|
|
19
|
+
import type { IntervalTally } from './interfaces/interval-tally';
|
|
20
|
+
import type { IPublicTallyFeed } from './interfaces/public-tally-feed';
|
|
21
|
+
import type { IThresholdAggregator } from './interfaces/threshold-aggregator';
|
|
22
|
+
import type { ThresholdKeyConfig } from './interfaces/threshold-key-config';
|
|
23
|
+
/**
|
|
24
|
+
* Precinct-level aggregator with threshold decryption support.
|
|
25
|
+
*
|
|
26
|
+
* Inherits all standard precinct aggregation behavior (vote collection,
|
|
27
|
+
* homomorphic tallying, optional persistence) and adds the ability to
|
|
28
|
+
* perform interval decryptions via a CeremonyCoordinator.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const aggregator = new ThresholdPrecinctAggregator(
|
|
33
|
+
* poll, config, publicKey, thresholdConfig, tallyFeed,
|
|
34
|
+
* );
|
|
35
|
+
*
|
|
36
|
+
* // Cast votes as usual
|
|
37
|
+
* await aggregator.vote(voter, encryptedVote);
|
|
38
|
+
*
|
|
39
|
+
* // Perform interval decryption
|
|
40
|
+
* const tally = await aggregator.performIntervalDecryption(coordinator, 1);
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare class ThresholdPrecinctAggregator<TID extends PlatformID = Uint8Array> extends PrecinctAggregator<TID> implements IThresholdAggregator<TID> {
|
|
44
|
+
private readonly _publicKey;
|
|
45
|
+
private readonly _thresholdConfig;
|
|
46
|
+
private readonly _tallyFeed?;
|
|
47
|
+
private readonly _intervalTallies;
|
|
48
|
+
private readonly _choices;
|
|
49
|
+
private readonly _pollId;
|
|
50
|
+
private _parentAggregator?;
|
|
51
|
+
constructor(poll: Poll<TID>, config: JurisdictionConfig<TID>, publicKey: PublicKey, thresholdConfig: ThresholdKeyConfig, tallyFeed?: IPublicTallyFeed<TID>, logger?: IVoteLogger<TID>, checkpointMgr?: ICheckpointManager<TID>);
|
|
52
|
+
/**
|
|
53
|
+
* Set the parent aggregator for result propagation.
|
|
54
|
+
*/
|
|
55
|
+
setParent(parent: IThresholdAggregator<TID>): void;
|
|
56
|
+
/**
|
|
57
|
+
* Get the encrypted aggregate tally for this precinct.
|
|
58
|
+
*
|
|
59
|
+
* Uses the same homomorphic aggregation as the base PrecinctAggregator.
|
|
60
|
+
*/
|
|
61
|
+
getEncryptedTally(): bigint[];
|
|
62
|
+
/**
|
|
63
|
+
* Perform an interval decryption at the precinct level.
|
|
64
|
+
*
|
|
65
|
+
* Starts a ceremony via the coordinator, waits for it to complete,
|
|
66
|
+
* and publishes the result to the tally feed.
|
|
67
|
+
*
|
|
68
|
+
* @param ceremonyCoordinator - The coordinator managing the ceremony
|
|
69
|
+
* @param intervalNumber - The interval number for this decryption
|
|
70
|
+
* @returns The decrypted interval tally
|
|
71
|
+
*/
|
|
72
|
+
performIntervalDecryption(ceremonyCoordinator: ICeremonyCoordinator<TID>, intervalNumber: number): Promise<IntervalTally<TID>>;
|
|
73
|
+
/**
|
|
74
|
+
* Propagate an interval tally result to the parent aggregator.
|
|
75
|
+
*/
|
|
76
|
+
propagateToParent(tally: IntervalTally<TID>): void;
|
|
77
|
+
/**
|
|
78
|
+
* Get all interval tallies produced by this aggregator.
|
|
79
|
+
*/
|
|
80
|
+
getIntervalTallies(): readonly IntervalTally<TID>[];
|
|
81
|
+
/**
|
|
82
|
+
* Build an IntervalTally from decryption results.
|
|
83
|
+
*/
|
|
84
|
+
private buildIntervalTally;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=threshold-precinct-aggregator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threshold-precinct-aggregator.d.ts","sourceRoot":"","sources":["../../../../../../../packages/digitaldefiance-ecies-lib/src/lib/voting/threshold/threshold-precinct-aggregator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,2BAA2B,CAAC,GAAG,SAAS,UAAU,GAAG,UAAU,CAC1E,SAAQ,kBAAkB,CAAC,GAAG,CAC9B,YAAW,oBAAoB,CAAC,GAAG,CAAC;IAEpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAwB;IACpD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA4B;IAC7D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAM;IAC9B,OAAO,CAAC,iBAAiB,CAAC,CAA4B;gBAGpD,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,EACf,MAAM,EAAE,kBAAkB,CAAC,GAAG,CAAC,EAC/B,SAAS,EAAE,SAAS,EACpB,eAAe,EAAE,kBAAkB,EACnC,SAAS,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,EACjC,MAAM,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,EACzB,aAAa,CAAC,EAAE,kBAAkB,CAAC,GAAG,CAAC;IAUzC;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,oBAAoB,CAAC,GAAG,CAAC,GAAG,IAAI;IAIlD;;;;OAIG;IACH,iBAAiB,IAAI,MAAM,EAAE;IAK7B;;;;;;;;;OASG;IACG,yBAAyB,CAC7B,mBAAmB,EAAE,oBAAoB,CAAC,GAAG,CAAC,EAC9C,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IA+D9B;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;IAMlD;;OAEG;IACH,kBAAkB,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE;IAInD;;OAEG;IACH,OAAO,CAAC,kBAAkB;CA8B3B"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Threshold Precinct Aggregator
|
|
4
|
+
*
|
|
5
|
+
* Extends the standard PrecinctAggregator with threshold decryption support,
|
|
6
|
+
* enabling interval decryption at the precinct level. Uses the same
|
|
7
|
+
* homomorphic aggregation as the base class but adds the ability to
|
|
8
|
+
* perform threshold decryption ceremonies via a CeremonyCoordinator.
|
|
9
|
+
*
|
|
10
|
+
* @module voting/threshold
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.ThresholdPrecinctAggregator = void 0;
|
|
14
|
+
const hierarchical_aggregator_1 = require("../hierarchical-aggregator");
|
|
15
|
+
const ceremony_status_1 = require("./enumerations/ceremony-status");
|
|
16
|
+
/**
|
|
17
|
+
* Precinct-level aggregator with threshold decryption support.
|
|
18
|
+
*
|
|
19
|
+
* Inherits all standard precinct aggregation behavior (vote collection,
|
|
20
|
+
* homomorphic tallying, optional persistence) and adds the ability to
|
|
21
|
+
* perform interval decryptions via a CeremonyCoordinator.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const aggregator = new ThresholdPrecinctAggregator(
|
|
26
|
+
* poll, config, publicKey, thresholdConfig, tallyFeed,
|
|
27
|
+
* );
|
|
28
|
+
*
|
|
29
|
+
* // Cast votes as usual
|
|
30
|
+
* await aggregator.vote(voter, encryptedVote);
|
|
31
|
+
*
|
|
32
|
+
* // Perform interval decryption
|
|
33
|
+
* const tally = await aggregator.performIntervalDecryption(coordinator, 1);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
class ThresholdPrecinctAggregator extends hierarchical_aggregator_1.PrecinctAggregator {
|
|
37
|
+
_publicKey;
|
|
38
|
+
_thresholdConfig;
|
|
39
|
+
_tallyFeed;
|
|
40
|
+
_intervalTallies = [];
|
|
41
|
+
_choices;
|
|
42
|
+
_pollId;
|
|
43
|
+
_parentAggregator;
|
|
44
|
+
constructor(poll, config, publicKey, thresholdConfig, tallyFeed, logger, checkpointMgr) {
|
|
45
|
+
super(poll, config, logger, checkpointMgr);
|
|
46
|
+
this._publicKey = publicKey;
|
|
47
|
+
this._thresholdConfig = thresholdConfig;
|
|
48
|
+
this._tallyFeed = tallyFeed;
|
|
49
|
+
this._choices = poll.choices;
|
|
50
|
+
this._pollId = poll.id;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Set the parent aggregator for result propagation.
|
|
54
|
+
*/
|
|
55
|
+
setParent(parent) {
|
|
56
|
+
this._parentAggregator = parent;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the encrypted aggregate tally for this precinct.
|
|
60
|
+
*
|
|
61
|
+
* Uses the same homomorphic aggregation as the base PrecinctAggregator.
|
|
62
|
+
*/
|
|
63
|
+
getEncryptedTally() {
|
|
64
|
+
const tally = this.getTally();
|
|
65
|
+
return [...tally.encryptedTallies];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Perform an interval decryption at the precinct level.
|
|
69
|
+
*
|
|
70
|
+
* Starts a ceremony via the coordinator, waits for it to complete,
|
|
71
|
+
* and publishes the result to the tally feed.
|
|
72
|
+
*
|
|
73
|
+
* @param ceremonyCoordinator - The coordinator managing the ceremony
|
|
74
|
+
* @param intervalNumber - The interval number for this decryption
|
|
75
|
+
* @returns The decrypted interval tally
|
|
76
|
+
*/
|
|
77
|
+
async performIntervalDecryption(ceremonyCoordinator, intervalNumber) {
|
|
78
|
+
const encryptedTally = this.getEncryptedTally();
|
|
79
|
+
// Start a ceremony for this interval
|
|
80
|
+
const ceremony = ceremonyCoordinator.startCeremony(this._pollId, intervalNumber, encryptedTally);
|
|
81
|
+
// Wait for the ceremony to complete (partials submitted externally)
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
// Check if already completed (synchronous submission case)
|
|
84
|
+
const current = ceremonyCoordinator.getCeremony(ceremony.id);
|
|
85
|
+
if (current &&
|
|
86
|
+
current.status === ceremony_status_1.CeremonyStatus.Completed &&
|
|
87
|
+
current.result) {
|
|
88
|
+
const intervalTally = this.buildIntervalTally(current.result.tallies, current.result.combinedProof, current.result.participatingGuardians, intervalNumber);
|
|
89
|
+
this._intervalTallies.push(intervalTally);
|
|
90
|
+
if (this._tallyFeed) {
|
|
91
|
+
this._tallyFeed.publish(intervalTally);
|
|
92
|
+
}
|
|
93
|
+
resolve(intervalTally);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Subscribe to completion
|
|
97
|
+
ceremonyCoordinator.onCeremonyComplete((completedCeremony) => {
|
|
98
|
+
if (completedCeremony.id !== ceremony.id)
|
|
99
|
+
return;
|
|
100
|
+
if (completedCeremony.status === ceremony_status_1.CeremonyStatus.Completed &&
|
|
101
|
+
completedCeremony.result) {
|
|
102
|
+
const intervalTally = this.buildIntervalTally(completedCeremony.result.tallies, completedCeremony.result.combinedProof, completedCeremony.result.participatingGuardians, intervalNumber);
|
|
103
|
+
this._intervalTallies.push(intervalTally);
|
|
104
|
+
if (this._tallyFeed) {
|
|
105
|
+
this._tallyFeed.publish(intervalTally);
|
|
106
|
+
}
|
|
107
|
+
resolve(intervalTally);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
reject(new Error(`Ceremony ${ceremony.id} failed with status: ${completedCeremony.status}`));
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Propagate an interval tally result to the parent aggregator.
|
|
117
|
+
*/
|
|
118
|
+
propagateToParent(tally) {
|
|
119
|
+
if (this._parentAggregator) {
|
|
120
|
+
this._parentAggregator.propagateToParent(tally);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get all interval tallies produced by this aggregator.
|
|
125
|
+
*/
|
|
126
|
+
getIntervalTallies() {
|
|
127
|
+
return this._intervalTallies;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Build an IntervalTally from decryption results.
|
|
131
|
+
*/
|
|
132
|
+
buildIntervalTally(tallies, proof, participatingGuardians, intervalNumber) {
|
|
133
|
+
const baseTally = this.getTally();
|
|
134
|
+
const cumulativeVoteCount = baseTally.voterCount;
|
|
135
|
+
// Compute vote count for this interval
|
|
136
|
+
const previousCumulative = this._intervalTallies.length > 0
|
|
137
|
+
? this._intervalTallies[this._intervalTallies.length - 1]
|
|
138
|
+
.cumulativeVoteCount
|
|
139
|
+
: 0;
|
|
140
|
+
const voteCount = cumulativeVoteCount - previousCumulative;
|
|
141
|
+
return {
|
|
142
|
+
pollId: this._pollId,
|
|
143
|
+
intervalNumber,
|
|
144
|
+
tallies: tallies,
|
|
145
|
+
choices: this._choices,
|
|
146
|
+
voteCount,
|
|
147
|
+
cumulativeVoteCount,
|
|
148
|
+
proof,
|
|
149
|
+
participatingGuardians,
|
|
150
|
+
timestamp: Date.now(),
|
|
151
|
+
isFinal: false,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
exports.ThresholdPrecinctAggregator = ThresholdPrecinctAggregator;
|
|
156
|
+
//# sourceMappingURL=threshold-precinct-aggregator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threshold-precinct-aggregator.js","sourceRoot":"","sources":["../../../../../../../packages/digitaldefiance-ecies-lib/src/lib/voting/threshold/threshold-precinct-aggregator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threshold State Aggregator
|
|
3
|
+
*
|
|
4
|
+
* Extends the standard StateAggregator with threshold decryption support.
|
|
5
|
+
* Enforces that threshold decryption is required at this level when any
|
|
6
|
+
* child jurisdiction uses threshold decryption (Requirement 10.6).
|
|
7
|
+
*
|
|
8
|
+
* @module voting/threshold
|
|
9
|
+
*/
|
|
10
|
+
import type { PublicKey } from 'paillier-bigint';
|
|
11
|
+
import type { PlatformID } from '../../../interfaces/platform-id';
|
|
12
|
+
import { StateAggregator } from '../hierarchical-aggregator';
|
|
13
|
+
import type { JurisdictionConfig } from '../interfaces/jurisdiction-config';
|
|
14
|
+
import type { ICeremonyCoordinator } from './interfaces/ceremony-coordinator';
|
|
15
|
+
import type { IntervalTally } from './interfaces/interval-tally';
|
|
16
|
+
import type { IPublicTallyFeed } from './interfaces/public-tally-feed';
|
|
17
|
+
import type { IThresholdAggregator } from './interfaces/threshold-aggregator';
|
|
18
|
+
import type { ThresholdKeyConfig } from './interfaces/threshold-key-config';
|
|
19
|
+
/**
|
|
20
|
+
* State-level aggregator with threshold decryption support.
|
|
21
|
+
*
|
|
22
|
+
* Combines county tallies using homomorphic addition (inherited) and
|
|
23
|
+
* enforces that threshold decryption is required at this level when
|
|
24
|
+
* child jurisdictions use threshold decryption.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const state = new ThresholdStateAggregator(
|
|
29
|
+
* config, publicKey, thresholdConfig, choices, pollId, tallyFeed,
|
|
30
|
+
* );
|
|
31
|
+
*
|
|
32
|
+
* // Add county tallies
|
|
33
|
+
* state.addCountyTally(countyTally);
|
|
34
|
+
*
|
|
35
|
+
* // Perform interval decryption (enforced at state level)
|
|
36
|
+
* const tally = await state.performIntervalDecryption(coordinator, 1);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare class ThresholdStateAggregator<TID extends PlatformID = Uint8Array> extends StateAggregator<TID> implements IThresholdAggregator<TID> {
|
|
40
|
+
private readonly _thresholdConfig;
|
|
41
|
+
private readonly _tallyFeed?;
|
|
42
|
+
private readonly _intervalTallies;
|
|
43
|
+
private readonly _choices;
|
|
44
|
+
private readonly _pollId;
|
|
45
|
+
private _parentAggregator?;
|
|
46
|
+
private readonly _childIntervalTallies;
|
|
47
|
+
private _thresholdRequired;
|
|
48
|
+
constructor(config: JurisdictionConfig<TID>, publicKey: PublicKey, thresholdConfig: ThresholdKeyConfig, choices: readonly string[], pollId: TID, tallyFeed?: IPublicTallyFeed<TID>);
|
|
49
|
+
/**
|
|
50
|
+
* Set the parent aggregator for result propagation.
|
|
51
|
+
*/
|
|
52
|
+
setParent(parent: IThresholdAggregator<TID>): void;
|
|
53
|
+
/**
|
|
54
|
+
* Whether threshold decryption is enforced at this level.
|
|
55
|
+
* Per Requirement 10.6, if a child jurisdiction uses threshold
|
|
56
|
+
* decryption, all higher levels must also use it.
|
|
57
|
+
*/
|
|
58
|
+
get thresholdRequired(): boolean;
|
|
59
|
+
set thresholdRequired(value: boolean);
|
|
60
|
+
/**
|
|
61
|
+
* Get the encrypted aggregate tally for this state.
|
|
62
|
+
*/
|
|
63
|
+
getEncryptedTally(): bigint[];
|
|
64
|
+
/**
|
|
65
|
+
* Perform an interval decryption at the state level.
|
|
66
|
+
*
|
|
67
|
+
* Enforces that threshold decryption is required at this level
|
|
68
|
+
* before proceeding with the ceremony.
|
|
69
|
+
*/
|
|
70
|
+
performIntervalDecryption(ceremonyCoordinator: ICeremonyCoordinator<TID>, intervalNumber: number): Promise<IntervalTally<TID>>;
|
|
71
|
+
/**
|
|
72
|
+
* Propagate an interval tally result from a child (county) aggregator.
|
|
73
|
+
*/
|
|
74
|
+
propagateToParent(tally: IntervalTally<TID>): void;
|
|
75
|
+
/**
|
|
76
|
+
* Get all interval tallies produced by this aggregator.
|
|
77
|
+
*/
|
|
78
|
+
getIntervalTallies(): readonly IntervalTally<TID>[];
|
|
79
|
+
/**
|
|
80
|
+
* Get child interval tallies received for a specific interval.
|
|
81
|
+
*/
|
|
82
|
+
getChildIntervalTallies(intervalNumber: number): readonly IntervalTally<TID>[];
|
|
83
|
+
private buildIntervalTally;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=threshold-state-aggregator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threshold-state-aggregator.d.ts","sourceRoot":"","sources":["../../../../../../../packages/digitaldefiance-ecies-lib/src/lib/voting/threshold/threshold-state-aggregator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAE5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,wBAAwB,CAAC,GAAG,SAAS,UAAU,GAAG,UAAU,CACvE,SAAQ,eAAe,CAAC,GAAG,CAC3B,YAAW,oBAAoB,CAAC,GAAG,CAAC;IAEpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAwB;IACpD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA4B;IAC7D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAM;IAC9B,OAAO,CAAC,iBAAiB,CAAC,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAC1B;IACZ,OAAO,CAAC,kBAAkB,CAAQ;gBAGhC,MAAM,EAAE,kBAAkB,CAAC,GAAG,CAAC,EAC/B,SAAS,EAAE,SAAS,EACpB,eAAe,EAAE,kBAAkB,EACnC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,MAAM,EAAE,GAAG,EACX,SAAS,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC;IASnC;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,oBAAoB,CAAC,GAAG,CAAC,GAAG,IAAI;IAIlD;;;;OAIG;IACH,IAAI,iBAAiB,IAAI,OAAO,CAE/B;IAED,IAAI,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAEnC;IAED;;OAEG;IACH,iBAAiB,IAAI,MAAM,EAAE;IAK7B;;;;;OAKG;IACG,yBAAyB,CAC7B,mBAAmB,EAAE,oBAAoB,CAAC,GAAG,CAAC,EAC9C,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IA+D9B;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;IAUlD;;OAEG;IACH,kBAAkB,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE;IAInD;;OAEG;IACH,uBAAuB,CACrB,cAAc,EAAE,MAAM,GACrB,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE;IAIhC,OAAO,CAAC,kBAAkB;CA6B3B"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Threshold State Aggregator
|
|
4
|
+
*
|
|
5
|
+
* Extends the standard StateAggregator with threshold decryption support.
|
|
6
|
+
* Enforces that threshold decryption is required at this level when any
|
|
7
|
+
* child jurisdiction uses threshold decryption (Requirement 10.6).
|
|
8
|
+
*
|
|
9
|
+
* @module voting/threshold
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ThresholdStateAggregator = void 0;
|
|
13
|
+
const hierarchical_aggregator_1 = require("../hierarchical-aggregator");
|
|
14
|
+
const ceremony_status_1 = require("./enumerations/ceremony-status");
|
|
15
|
+
/**
|
|
16
|
+
* State-level aggregator with threshold decryption support.
|
|
17
|
+
*
|
|
18
|
+
* Combines county tallies using homomorphic addition (inherited) and
|
|
19
|
+
* enforces that threshold decryption is required at this level when
|
|
20
|
+
* child jurisdictions use threshold decryption.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const state = new ThresholdStateAggregator(
|
|
25
|
+
* config, publicKey, thresholdConfig, choices, pollId, tallyFeed,
|
|
26
|
+
* );
|
|
27
|
+
*
|
|
28
|
+
* // Add county tallies
|
|
29
|
+
* state.addCountyTally(countyTally);
|
|
30
|
+
*
|
|
31
|
+
* // Perform interval decryption (enforced at state level)
|
|
32
|
+
* const tally = await state.performIntervalDecryption(coordinator, 1);
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
class ThresholdStateAggregator extends hierarchical_aggregator_1.StateAggregator {
|
|
36
|
+
_thresholdConfig;
|
|
37
|
+
_tallyFeed;
|
|
38
|
+
_intervalTallies = [];
|
|
39
|
+
_choices;
|
|
40
|
+
_pollId;
|
|
41
|
+
_parentAggregator;
|
|
42
|
+
_childIntervalTallies = new Map();
|
|
43
|
+
_thresholdRequired = true;
|
|
44
|
+
constructor(config, publicKey, thresholdConfig, choices, pollId, tallyFeed) {
|
|
45
|
+
super(config, publicKey);
|
|
46
|
+
this._thresholdConfig = thresholdConfig;
|
|
47
|
+
this._tallyFeed = tallyFeed;
|
|
48
|
+
this._choices = choices;
|
|
49
|
+
this._pollId = pollId;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Set the parent aggregator for result propagation.
|
|
53
|
+
*/
|
|
54
|
+
setParent(parent) {
|
|
55
|
+
this._parentAggregator = parent;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Whether threshold decryption is enforced at this level.
|
|
59
|
+
* Per Requirement 10.6, if a child jurisdiction uses threshold
|
|
60
|
+
* decryption, all higher levels must also use it.
|
|
61
|
+
*/
|
|
62
|
+
get thresholdRequired() {
|
|
63
|
+
return this._thresholdRequired;
|
|
64
|
+
}
|
|
65
|
+
set thresholdRequired(value) {
|
|
66
|
+
this._thresholdRequired = value;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get the encrypted aggregate tally for this state.
|
|
70
|
+
*/
|
|
71
|
+
getEncryptedTally() {
|
|
72
|
+
const tally = this.getTally();
|
|
73
|
+
return [...tally.encryptedTallies];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Perform an interval decryption at the state level.
|
|
77
|
+
*
|
|
78
|
+
* Enforces that threshold decryption is required at this level
|
|
79
|
+
* before proceeding with the ceremony.
|
|
80
|
+
*/
|
|
81
|
+
async performIntervalDecryption(ceremonyCoordinator, intervalNumber) {
|
|
82
|
+
if (!this._thresholdRequired) {
|
|
83
|
+
throw new Error('Threshold decryption is not enabled at the state level');
|
|
84
|
+
}
|
|
85
|
+
const encryptedTally = this.getEncryptedTally();
|
|
86
|
+
const ceremony = ceremonyCoordinator.startCeremony(this._pollId, intervalNumber, encryptedTally);
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
const current = ceremonyCoordinator.getCeremony(ceremony.id);
|
|
89
|
+
if (current &&
|
|
90
|
+
current.status === ceremony_status_1.CeremonyStatus.Completed &&
|
|
91
|
+
current.result) {
|
|
92
|
+
const intervalTally = this.buildIntervalTally(current.result.tallies, current.result.combinedProof, current.result.participatingGuardians, intervalNumber);
|
|
93
|
+
this._intervalTallies.push(intervalTally);
|
|
94
|
+
if (this._tallyFeed) {
|
|
95
|
+
this._tallyFeed.publish(intervalTally);
|
|
96
|
+
}
|
|
97
|
+
resolve(intervalTally);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
ceremonyCoordinator.onCeremonyComplete((completedCeremony) => {
|
|
101
|
+
if (completedCeremony.id !== ceremony.id)
|
|
102
|
+
return;
|
|
103
|
+
if (completedCeremony.status === ceremony_status_1.CeremonyStatus.Completed &&
|
|
104
|
+
completedCeremony.result) {
|
|
105
|
+
const intervalTally = this.buildIntervalTally(completedCeremony.result.tallies, completedCeremony.result.combinedProof, completedCeremony.result.participatingGuardians, intervalNumber);
|
|
106
|
+
this._intervalTallies.push(intervalTally);
|
|
107
|
+
if (this._tallyFeed) {
|
|
108
|
+
this._tallyFeed.publish(intervalTally);
|
|
109
|
+
}
|
|
110
|
+
resolve(intervalTally);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
reject(new Error(`Ceremony ${ceremony.id} failed with status: ${completedCeremony.status}`));
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Propagate an interval tally result from a child (county) aggregator.
|
|
120
|
+
*/
|
|
121
|
+
propagateToParent(tally) {
|
|
122
|
+
const existing = this._childIntervalTallies.get(tally.intervalNumber) ?? [];
|
|
123
|
+
existing.push(tally);
|
|
124
|
+
this._childIntervalTallies.set(tally.intervalNumber, existing);
|
|
125
|
+
if (this._parentAggregator) {
|
|
126
|
+
this._parentAggregator.propagateToParent(tally);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get all interval tallies produced by this aggregator.
|
|
131
|
+
*/
|
|
132
|
+
getIntervalTallies() {
|
|
133
|
+
return this._intervalTallies;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get child interval tallies received for a specific interval.
|
|
137
|
+
*/
|
|
138
|
+
getChildIntervalTallies(intervalNumber) {
|
|
139
|
+
return this._childIntervalTallies.get(intervalNumber) ?? [];
|
|
140
|
+
}
|
|
141
|
+
buildIntervalTally(tallies, proof, participatingGuardians, intervalNumber) {
|
|
142
|
+
const baseTally = this.getTally();
|
|
143
|
+
const cumulativeVoteCount = baseTally.voterCount;
|
|
144
|
+
const previousCumulative = this._intervalTallies.length > 0
|
|
145
|
+
? this._intervalTallies[this._intervalTallies.length - 1]
|
|
146
|
+
.cumulativeVoteCount
|
|
147
|
+
: 0;
|
|
148
|
+
const voteCount = cumulativeVoteCount - previousCumulative;
|
|
149
|
+
return {
|
|
150
|
+
pollId: this._pollId,
|
|
151
|
+
intervalNumber,
|
|
152
|
+
tallies: tallies,
|
|
153
|
+
choices: this._choices,
|
|
154
|
+
voteCount,
|
|
155
|
+
cumulativeVoteCount,
|
|
156
|
+
proof,
|
|
157
|
+
participatingGuardians,
|
|
158
|
+
timestamp: Date.now(),
|
|
159
|
+
isFinal: false,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
exports.ThresholdStateAggregator = ThresholdStateAggregator;
|
|
164
|
+
//# sourceMappingURL=threshold-state-aggregator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threshold-state-aggregator.js","sourceRoot":"","sources":["../../../../../../../packages/digitaldefiance-ecies-lib/src/lib/voting/threshold/threshold-state-aggregator.ts"],"names":[],"mappings":"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"}
|