Skip to content

Commit 3ef2292

Browse files
authored
2.0.0 docs update (#34)
* 2.0.0 docs update * ballotClosePayloads
1 parent 676b340 commit 3ef2292

16 files changed

Lines changed: 188 additions & 41 deletions

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ The cryptographic threshold is derived internally from the accepted registration
9494

9595
There is no supported `n-of-n` mode and no supported public `k-of-n` configuration.
9696

97-
Transcript verification requires key-derivation confirmations from every qualified participant.
97+
Transcript verification requires `key-derivation-confirmation` payloads from every qualified participant. In the current design those unanimous confirmations are part of verifier soundness: the library does not implement a public post-Feldman complaint/reconstruction phase, so the DKG verifier is participant-confirmed rather than fully public-data-only. Lowering confirmation acceptance to threshold-many is out of scope unless that missing public consistency machinery is added.
9898

9999
See [Honest-majority voting flow](https://tenemo.github.io/threshold-elgamal/guides/three-participant-voting-flow/) for the full phase-by-phase transcript.
100100

@@ -130,7 +130,7 @@ const rosterHash = await hashRosterEntries([
130130
const manifest = createElectionManifest({
131131
rosterHash,
132132
optionList: ["Option A", "Option B"],
133-
scoreRange: { min: 1, max: 10 },
133+
scoreRange: { min: 0, max: 5 },
134134
});
135135

136136
const manifestHash = await hashElectionManifest(manifest);
@@ -145,6 +145,10 @@ console.log(majorityThreshold(3)); // 2
145145
console.log(sessionId.length); // 64
146146
```
147147

148+
The example uses `0..5` only as one concrete score range. The supported rule is
149+
one manifest-declared contiguous range with non-negative bounds and
150+
`scoreRange.max <= 100`.
151+
148152
If your application consumes a complete public board, start with [Verifying a public board](https://tenemo.github.io/threshold-elgamal/guides/verifying-a-public-board/) and then move directly to the verifier entry point:
149153

150154
```typescript
@@ -158,7 +162,7 @@ const bundle: VerifyElectionCeremonyInput = {
158162
sessionId,
159163
dkgTranscript,
160164
ballotPayloads,
161-
ballotClosePayload,
165+
ballotClosePayloads: [ballotClosePayload],
162166
decryptionSharePayloads,
163167
tallyPublications,
164168
};
@@ -173,6 +177,8 @@ if (!result.ok) {
173177
}
174178
```
175179

180+
Pass the full published `ballot-close` slot in `ballotClosePayloads`, even when the normal case is one organizer payload. The verifier audits that slot, collapses only exact retransmissions, and requires exactly one accepted close record.
181+
176182
The root package exposes the builders and lower-level helpers required for the documented ceremony, including:
177183

178184
- manifest publication

docs/src/content/docs/guides/browser-and-worker-usage.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const rosterHash = await hashRosterEntries(
5757
const manifest = createElectionManifest({
5858
rosterHash,
5959
optionList: ["Budget", "Hiring"],
60-
scoreRange: { min: 1, max: 10 },
60+
scoreRange: { min: 0, max: 5 },
6161
});
6262

6363
const manifestHash = await hashElectionManifest(manifest);
@@ -128,6 +128,9 @@ console.log(acceptance.payload.messageType);
128128
console.log(new TextDecoder().decode(decrypted));
129129
```
130130

131+
This example uses `0..5` only as sample data. The supported rule is still one
132+
manifest-declared contiguous score range with `scoreRange.max <= 100`.
133+
131134
This covers the browser-native pieces most applications need first:
132135

133136
- auth and transport key generation

docs/src/content/docs/guides/getting-started.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const bundle: VerifyElectionCeremonyInput = {
3636
sessionId,
3737
dkgTranscript,
3838
ballotPayloads,
39-
ballotClosePayload,
39+
ballotClosePayloads: [ballotClosePayload],
4040
decryptionSharePayloads,
4141
tallyPublications,
4242
};
@@ -53,7 +53,7 @@ if (!result.ok) {
5353
}
5454
```
5555

56-
Use `verifyElectionCeremony(...)` when you want the same checks but prefer exceptions over a structured result.
56+
Use `verifyElectionCeremony(...)` when you want the same checks but prefer exceptions over a structured result. Pass the full published `ballot-close` slot in `ballotClosePayloads`, even when that slot only contains one organizer payload.
5757

5858
## What to persist in your application
5959

docs/src/content/docs/guides/three-participant-voting-flow.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const rosterHash = await hashRosterEntries([
5858
const manifest = createElectionManifest({
5959
rosterHash,
6060
optionList: ["Option A", "Option B", "Option C"],
61-
scoreRange: { min: 1, max: 10 },
61+
scoreRange: { min: 0, max: 5 },
6262
});
6363

6464
const manifestHash = await hashElectionManifest(manifest);
@@ -72,6 +72,10 @@ const sessionId = await deriveSessionId(
7272
console.log(majorityThreshold(3)); // 2
7373
```
7474

75+
This example uses `0..5` only as sample data. The supported rule is one
76+
manifest-declared contiguous score range with non-negative bounds and
77+
`scoreRange.max <= 100`.
78+
7579
The manifest does not carry `participantCount`, `reconstructionThreshold`, publication floors, or deadline metadata. It does carry one explicit global `scoreRange`. The verifier derives `n` from the accepted registration roster and derives `k` internally as `ceil(n / 2)`.
7680

7781
## Supported public flow helpers

docs/src/content/docs/guides/verifying-a-public-board.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ The full verifier consumes one `VerifyElectionCeremonyInput` bundle:
1515
- `sessionId`
1616
- `dkgTranscript`
1717
- `ballotPayloads`
18-
- `ballotClosePayload`
18+
- `ballotClosePayloads`
1919
- `decryptionSharePayloads`
2020
- `tallyPublications` if you want published tally records checked against the recomputed tallies
2121

22+
`ballotClosePayloads` must contain the full published `ballot-close` board slot, not a prefiltered close record. The verifier audits that slot, collapses only exact retransmissions, and requires exactly one accepted close payload after audit.
23+
2224
If `tallyPublications` is omitted or empty, the verifier still replays the DKG, ballot, and decryption-share flow and still recomputes per-option tallies locally.
2325

2426
## Use the non-throwing verifier first
@@ -34,7 +36,7 @@ const bundle: VerifyElectionCeremonyInput = {
3436
sessionId,
3537
dkgTranscript,
3638
ballotPayloads,
37-
ballotClosePayload,
39+
ballotClosePayloads,
3840
decryptionSharePayloads,
3941
tallyPublications,
4042
};
@@ -55,6 +57,15 @@ console.log(result.verified.boardAudit.overall.fingerprint);
5557

5658
This is usually the best application entry point because it gives you a stable `stage`, `code`, and `reason` without exception handling.
5759

60+
## What the DKG verifier assumes
61+
62+
`verifyElectionCeremony(...)` delegates DKG validation to `verifyDKGTranscript(...)`, which currently consumes:
63+
64+
- the public signed DKG transcript
65+
- `key-derivation-confirmation` payloads from every qualified participant
66+
67+
This verifier does not implement a public post-Feldman complaint/reconstruction phase. That means the current DKG check is participant-confirmed transcript verification, not the stronger fully public-data-only variant sometimes described in the GJKR literature. Lowering confirmation acceptance to threshold-many is out of scope unless that missing public consistency machinery is added.
68+
5869
## Use the throwing verifier when failure should abort immediately
5970

6071
```typescript

src/dkg/verification.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,10 +1487,17 @@ const verifyCheckpointedDKGTranscript = async (
14871487
/**
14881488
* Verifies a DKG transcript, its signatures, Feldman extraction proofs, the
14891489
* exact claimed threshold degree, accepted complaint outcomes, the DKG
1490-
* transcript hash, and the announced joint public key.
1490+
* transcript hash, the announced joint public key, and unanimous qualified
1491+
* participant key confirmations.
14911492
*
14921493
* This is the DKG-specific verifier that the full ceremony verifier delegates
1493-
* to before it touches ballots or tally material.
1494+
* to before it touches ballots or tally material. It consumes a public signed
1495+
* transcript plus `key-derivation-confirmation` payloads from every qualified
1496+
* participant. It does not implement a public post-Feldman
1497+
* complaint/reconstruction phase, so it is a participant-confirmed transcript
1498+
* verifier rather than a fully public-data-only GJKR verifier. Lowering this
1499+
* requirement to threshold-many confirmations is out of scope unless that
1500+
* missing public consistency machinery is added.
14941501
*/
14951502
export const verifyDKGTranscript = async (
14961503
input: VerifyDKGTranscriptInput,

src/protocol/types.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ export type FeldmanCommitmentPayload = BaseProtocolPayload & {
173173
};
174174

175175
/**
176-
* Final key-derivation confirmation payload for the derived joint key.
176+
* Final participant confirmation payload for the derived joint key.
177177
*/
178178
export type KeyDerivationConfirmation = BaseProtocolPayload & {
179179
readonly messageType: 'key-derivation-confirmation';
@@ -348,14 +348,15 @@ export type VerifyDecryptionSharePayloadsByOptionInput = {
348348
* Input bundle for full ceremony verification across all published options.
349349
*
350350
* This is the top-level verifier input that an auditor or bulletin-board
351-
* reader supplies when replaying a full ceremony.
351+
* reader supplies when replaying a full ceremony from the published board,
352+
* including the full ballot-close slot instead of a preselected close record.
352353
*/
353354
export type VerifyElectionCeremonyInput = {
354355
readonly manifest: ElectionManifest;
355356
readonly sessionId: string;
356357
readonly dkgTranscript: readonly SignedPayload[];
357358
readonly ballotPayloads: readonly SignedPayload<BallotSubmissionPayload>[];
358-
readonly ballotClosePayload: SignedPayload<BallotClosePayload>;
359+
readonly ballotClosePayloads: readonly SignedPayload<BallotClosePayload>[];
359360
readonly decryptionSharePayloads: readonly SignedPayload<DecryptionSharePayload>[];
360361
readonly tallyPublications?: readonly SignedPayload<TallyPublicationPayload>[];
361362
};

src/protocol/voting-ballots.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,10 @@ const verifyAuditedBallotSubmissionPayloadsByOption = async (input: {
7676
* Verifies typed ballot-submission payloads and recomputes one aggregate tally
7777
* ciphertext per manifest option.
7878
*
79-
* This is the public entry point for applications that already have
80-
* signature-checked ballot payloads and want the per-option verified
81-
* ciphertext aggregates that feed threshold decryption.
79+
* This is the public entry point for applications that already collected
80+
* signed ballot payloads and want the per-option verified ciphertext
81+
* aggregates that feed threshold decryption. The helper re-audits the signed
82+
* payloads before it decodes and aggregates them.
8283
*/
8384
export const verifyBallotSubmissionPayloadsByOption = async (
8485
input: VerifyBallotSubmissionPayloadsByOptionInput,

src/protocol/voting-decryption.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ const verifyAuditedDecryptionSharePayloadsByOption = async (input: {
189189
*
190190
* This is the public entry point for applications that have already accepted a
191191
* DKG transcript and verified ballot aggregates and now need to validate the
192-
* published threshold shares.
192+
* published threshold shares. The helper re-audits the signed share payloads
193+
* before it groups and verifies them.
193194
*/
194195
export const verifyDecryptionSharePayloadsByOption = async (
195196
input: VerifyDecryptionSharePayloadsByOptionInput,

src/protocol/voting-verification.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* This module is the shortest path for auditors and bulletin-board readers who
55
* want one call that replays manifest validation, board audit, DKG, ballots,
6-
* decryption shares, and tally checks.
6+
* the full ballot-close slot, decryption shares, and tally checks.
77
*/
88
import { InvalidPayloadError } from '../core/index';
99
import { decodeScalar } from '../core/ristretto';
@@ -364,8 +364,8 @@ const verifyBallotClosePayload = (input: {
364364

365365
/**
366366
* Replays the published ceremony from manifest to tally, including board audit,
367-
* DKG verification, ballot verification, decryption-share verification, and
368-
* per-option tally checks.
367+
* DKG verification, full ballot-close-slot audit, ballot verification,
368+
* decryption-share verification, and per-option tally checks.
369369
*
370370
* This is the main verifier entry point for callers that want failures to
371371
* abort immediately.
@@ -392,9 +392,7 @@ export const verifyElectionCeremony = async (
392392
try {
393393
dkgAudit = await auditSignedPayloads(input.dkgTranscript);
394394
ballotAudit = await auditSignedPayloads(input.ballotPayloads);
395-
ballotCloseAudit = await auditSignedPayloads([
396-
input.ballotClosePayload,
397-
]);
395+
ballotCloseAudit = await auditSignedPayloads(input.ballotClosePayloads);
398396
decryptionAudit = await auditSignedPayloads(
399397
input.decryptionSharePayloads,
400398
);

0 commit comments

Comments
 (0)