@crisp-e3/sdk 0.16.0 → 0.18.0-insecure.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.
@@ -0,0 +1,49 @@
1
+ import { CompiledCircuit } from '@noir-lang/noir_js';
2
+
3
+ /** BFV parameter sets the circuits can be compiled against. */
4
+ type CircuitPreset = 'insecure-512' | 'secure-8192';
5
+ /**
6
+ * The circuits whose ABI is shaped by the BFV degree, and which therefore exist once per preset.
7
+ *
8
+ * The aggregation circuits — `crisp_fold`, `crisp_onchain_fold` and `user_data_encryption` — are
9
+ * deliberately absent. Their parameters are proof and verification-key shaped (410/115 fields), not
10
+ * polynomial shaped, so one compiled artifact serves both presets; the fold circuits assert
11
+ * `chain_key_hash` against the insecure *or* the secure constant for exactly that reason. They ship
12
+ * in the main entry point, which is why `verifyProof` works without a preset loaded.
13
+ */
14
+ type CircuitBundle = {
15
+ readonly preset: CircuitPreset;
16
+ readonly crisp: CompiledCircuit;
17
+ readonly crispOnchain: CompiledCircuit;
18
+ readonly userDataEncryptionCt0: CompiledCircuit;
19
+ readonly userDataEncryptionCt1: CompiledCircuit;
20
+ };
21
+ /**
22
+ * Install the preset-bound circuits used by `generateProof`.
23
+ *
24
+ * The bundle is not bundled into the main entry point, because the secure-8192 artifacts are more
25
+ * than an order of magnitude larger than the insecure-512 ones and no consumer needs both. Load the
26
+ * one you want from its subpath and register it once at start-up:
27
+ *
28
+ * ```ts
29
+ * import { setCircuits } from '@crisp-e3/sdk'
30
+ * import { loadCircuits } from '@crisp-e3/sdk/insecure-512'
31
+ *
32
+ * setCircuits(await loadCircuits())
33
+ * ```
34
+ */
35
+ declare const setCircuits: (bundle: CircuitBundle) => void;
36
+ /** The registered bundle, or `null` when none has been installed yet. */
37
+ declare const getRegisteredCircuits: () => CircuitBundle | null;
38
+ /** The preset currently installed, or `null` when none has been installed yet. */
39
+ declare const registeredPreset: () => CircuitPreset | null;
40
+ /**
41
+ * The registered bundle, throwing a directed error when nothing has been installed.
42
+ *
43
+ * Proving cannot fall back to a default preset: a ballot proved against the wrong parameters fails
44
+ * on chain rather than locally, so guessing here would move the failure somewhere much harder to
45
+ * read.
46
+ */
47
+ declare const requireCircuits: () => CircuitBundle;
48
+
49
+ export { type CircuitBundle as C, type CircuitPreset as a, requireCircuits as b, getRegisteredCircuits as g, registeredPreset as r, setCircuits as s };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,14 @@
1
+ export { C as CircuitBundle, a as CircuitPreset, g as getRegisteredCircuits, r as registeredPreset, b as requireCircuits, s as setCircuits } from './circuits-BWegRaZy.js';
1
2
  import { LeanIMTMerkleProof, LeanIMT } from '@zk-kit/lean-imt';
2
3
  import { Hex } from 'viem';
4
+ import '@noir-lang/noir_js';
3
5
 
4
6
  /**
5
7
  * Get the merkle tree data from the CRISP server
6
8
  * @param serverUrl - The base URL of the CRISP server
7
9
  * @param e3Id - The e3Id of the round
8
10
  */
9
- declare const getTreeData: (serverUrl: string, e3Id: number) => Promise<bigint[]>;
11
+ declare const getTreeData: (serverUrl: string, e3Id: bigint) => Promise<bigint[]>;
10
12
  /**
11
13
  * Get the token balance at a specific block for a given address
12
14
  * @param voterAddress - The address of the voter
@@ -94,54 +96,125 @@ type ProofData = {
94
96
  publicInputs: string[];
95
97
  proof: Uint8Array;
96
98
  encryptedVote: Uint8Array;
99
+ /**
100
+ * The tree index of the entry this input extends, plus one; zero when it extends nothing.
101
+ *
102
+ * `CRISPProgram` reads the parent's commitment from this and hands it to the circuit as
103
+ * `prev_ct_commitment`, and the Secure Process walks each slot's chain by it. Offset by one so
104
+ * that zero means "no parent", which is what index 0 would otherwise be ambiguous with.
105
+ */
106
+ parentIndexPlusOne: number;
97
107
  };
98
- type MaskVoteProofInputs = {
99
- publicKey: Uint8Array;
100
- balance: bigint;
101
- slotAddress: string;
102
- merkleLeaves: string[] | bigint[];
103
- previousCiphertext?: Uint8Array;
104
- numOptions: number;
108
+ /**
109
+ * Which circuit a ballot is built for.
110
+ *
111
+ * `merkle` proves membership of a census tree, and matches `CensusMode.TOKEN` and
112
+ * `CensusMode.BY_REQUESTER`. `onchain` takes voting power as a public input that the contract
113
+ * reads from the token, and matches `CensusMode.ONCHAIN`.
114
+ */
115
+ type CensusVariant = 'merkle' | 'onchain';
116
+ /**
117
+ * The two halves of a slot head, which only mean anything together.
118
+ *
119
+ * Modelled as a pair rather than two optional fields: a ciphertext without its index would be
120
+ * proven against one entry and published against another, and the mismatch only surfaces as a
121
+ * rejected proof.
122
+ */
123
+ type SlotHeadInputs = {
124
+ /**
125
+ * The ciphertext currently in the slot: the end of its chain of usable entries, not simply
126
+ * the newest one published. An entry whose bytes do not reproduce its commitment is never
127
+ * selected by the Secure Process and is never a valid parent, so building on it would have
128
+ * this input dropped from the tally.
129
+ */
130
+ previousCiphertext: Uint8Array;
131
+ /** The tree index of `previousCiphertext`, which this input names as its parent. */
132
+ previousIndex: number;
133
+ } | {
134
+ previousCiphertext?: undefined;
135
+ previousIndex?: undefined;
105
136
  };
106
- type MaskVoteProofRequest = {
107
- e3Id: number;
137
+ type PrepareBallotInputsBase = {
108
138
  publicKey: Uint8Array;
109
- balance: bigint;
110
139
  slotAddress: string;
111
- merkleLeaves: string[] | bigint[];
140
+ isMaskVote: boolean;
112
141
  numOptions: number;
113
- };
114
- type VoteProofInputs = {
115
- merkleLeaves: string[] | bigint[];
116
- publicKey: Uint8Array;
117
- balance: bigint;
118
142
  vote: Vote;
119
- signature: `0x${string}`;
120
- messageHash: `0x${string}`;
121
- slotAddress: string;
122
- previousCiphertext?: Uint8Array;
123
- };
124
- type VoteProofRequest = {
125
- e3Id: number;
126
- merkleLeaves: string[] | bigint[];
127
- publicKey: Uint8Array;
143
+ } & SlotHeadInputs;
144
+ /**
145
+ * Everything needed to encrypt a ballot, before the voter has signed anything.
146
+ */
147
+ type PrepareBallotInputs = (PrepareBallotInputsBase & {
148
+ censusMode: 'merkle';
128
149
  balance: bigint;
129
- vote: Vote;
130
- signature: `0x${string}`;
131
- messageHash: `0x${string}`;
132
- slotAddress: string;
150
+ merkleLeaves: string[] | bigint[];
151
+ }) | (PrepareBallotInputsBase & {
152
+ censusMode: 'onchain';
153
+ votingPower: bigint;
154
+ });
155
+ /**
156
+ * A ballot that is encrypted but not yet signed.
157
+ *
158
+ * `ctCommitment` is the value to pass to `CRISPProgram.ballotDigest`. Reading the digest from the
159
+ * contract rather than rebuilding the EIP-712 struct here keeps one implementation of the domain.
160
+ */
161
+ type PreparedBallot = {
162
+ circuitInputs: any;
163
+ /**
164
+ * The ciphertext to publish, which is the ballot itself for a vote or a re-vote, and the slot's
165
+ * ciphertext plus the zero ballot for a mask over an occupied slot.
166
+ */
167
+ encryptedVote: Uint8Array;
168
+ /**
169
+ * The commitment to `encryptedVote`: what the circuit returns, what the E3 program stores, and
170
+ * what `CRISPProgram.ballotDigest` takes as its `ciphertextCommitment` argument.
171
+ *
172
+ * Since the digest is itself a circuit input, a caller has to know this value before proving,
173
+ * which is why the wasm exports it rather than leaving it to be read off the finished proof.
174
+ */
175
+ ctCommitment: `0x${string}`;
176
+ /** The value {@link ProofData.parentIndexPlusOne} carries through to `encodeSolidityProof`. */
177
+ parentIndexPlusOne: number;
178
+ censusMode: CensusVariant;
179
+ };
180
+ /**
181
+ * `Omit` that maps over each member of a union instead of collapsing it.
182
+ *
183
+ * A plain `Omit` on a discriminated union merges the members and loses the discriminant, which
184
+ * would let a caller pass `censusMode: 'onchain'` with no `votingPower`.
185
+ */
186
+ type DistributiveOmit<T, K extends keyof never> = T extends unknown ? Omit<T, K> : never;
187
+ /**
188
+ * A {@link PrepareBallotInputs} plus the round it belongs to.
189
+ *
190
+ * The SDK resolves the slot head from the server, so callers pass neither part of it.
191
+ */
192
+ type PrepareBallotRequest = {
193
+ e3Id: bigint;
194
+ } & DistributiveOmit<PrepareBallotInputs, 'previousCiphertext' | 'previousIndex'>;
195
+ /**
196
+ * The end of a slot's chain of usable entries: what a new input extends.
197
+ *
198
+ * Not simply the newest entry published to the slot. An entry whose bytes do not reproduce its
199
+ * commitment is never selected by the Secure Process and is never a valid parent, so the server
200
+ * resolves the chain and answers with the entry that actually holds the slot.
201
+ */
202
+ type SlotHead = {
203
+ ciphertext: Uint8Array;
204
+ /** The tree index of that entry. */
205
+ index: number;
133
206
  };
134
207
  /**
135
208
  * Type representing the current round returned by the CRISP server (`rounds/current`)
136
209
  */
137
210
  type CurrentRoundResponse = {
138
- id: number;
211
+ id: string;
139
212
  };
140
213
  /**
141
214
  * Type representing the lite state of a round returned by the CRISP server (`state/lite`)
142
215
  */
143
216
  type E3StateLiteResponse = {
144
- id: number;
217
+ id: string;
145
218
  chain_id: number;
146
219
  interfold_address: string;
147
220
  status: string;
@@ -177,7 +250,7 @@ type NewRoundRequest = {
177
250
  * Type representing a request to broadcast an encrypted vote (`voting/broadcast`)
178
251
  */
179
252
  type BroadcastVoteRequest = {
180
- e3Id: number;
253
+ e3Id: bigint;
181
254
  encodedProof: string;
182
255
  address: string;
183
256
  };
@@ -198,7 +271,7 @@ type BroadcastVoteResponse = {
198
271
  * Type representing the vote status of an address in a round (`voting/status`)
199
272
  */
200
273
  type VoteStatusResponse = {
201
- round_id: number;
274
+ round_id: string;
202
275
  address: string;
203
276
  has_voted: boolean;
204
277
  round_status: string | null;
@@ -207,7 +280,7 @@ type VoteStatusResponse = {
207
280
  * Type representing the result of a round (`state/result` and `state/all`)
208
281
  */
209
282
  type WebResultResponse = {
210
- round_id: number;
283
+ round_id: string;
211
284
  tally: string[];
212
285
  option_1_emoji: string;
213
286
  option_2_emoji: string;
@@ -238,14 +311,14 @@ declare enum CreditMode {
238
311
  * @param e3Id - The e3Id of the round
239
312
  * @returns The round details
240
313
  */
241
- declare const getRoundDetails: (serverUrl: string, e3Id: number) => Promise<RoundDetails>;
314
+ declare const getRoundDetails: (serverUrl: string, e3Id: bigint) => Promise<RoundDetails>;
242
315
  /**
243
316
  * Get the token address, balance threshold and snapshot block for a specific round
244
317
  * @param serverUrl - The base URL of the CRISP server
245
318
  * @param e3Id - The e3Id of the round
246
319
  * @returns The token address, balance threshold and snapshot block
247
320
  */
248
- declare const getRoundTokenDetails: (serverUrl: string, e3Id: number) => Promise<TokenDetails>;
321
+ declare const getRoundTokenDetails: (serverUrl: string, e3Id: bigint) => Promise<TokenDetails>;
249
322
  /**
250
323
  * Get the round data stored in the CRISPProgram contract, such as the merkle root
251
324
  * of the census and the merkle root of the encrypted votes published so far.
@@ -258,7 +331,23 @@ declare const getRoundTokenDetails: (serverUrl: string, e3Id: number) => Promise
258
331
  * @param chainId - The chain ID of the network the program is deployed on
259
332
  * @returns The on chain round data
260
333
  */
261
- declare const getOnChainRoundData: (programAddress: string, e3Id: number, chainId: number) => Promise<OnChainRoundData>;
334
+ declare const getOnChainRoundData: (programAddress: string, e3Id: bigint, chainId: number) => Promise<OnChainRoundData>;
335
+ /**
336
+ * Get the voting power a slot may spend in a `CensusMode.ONCHAIN` round, in ballot units.
337
+ *
338
+ * Read from the CRISP program rather than derived here. The contract scales raw token power by a
339
+ * per-round divisor before handing it to the circuit as public input 4, and it is the same
340
+ * contract that verifies the proof — so recomputing the value client-side would mean re-deriving
341
+ * the round's snapshot, its divisor and the rounding, and any drift surfaces only as an opaque
342
+ * verifier failure.
343
+ *
344
+ * @param programAddress - The CRISP program address
345
+ * @param e3Id - The e3Id of the round
346
+ * @param slot - The slot address the ballot is written to
347
+ * @param chainId - The chain the program is deployed on
348
+ * @returns The spendable voting power in ballot units, or 0 for a round that is not ONCHAIN
349
+ */
350
+ declare const getOnchainVotingPower: (programAddress: string, e3Id: bigint, slot: string, chainId: number) => Promise<bigint>;
262
351
  /**
263
352
  * Get the previous ciphertext for a slot from the CRISP server.
264
353
  * Returns undefined when the slot is empty (404).
@@ -266,9 +355,10 @@ declare const getOnChainRoundData: (programAddress: string, e3Id: number, chainI
266
355
  * @param serverUrl - The base URL of the CRISP server
267
356
  * @param e3Id - The e3Id of the round
268
357
  * @param address - The address of the slot
269
- * @returns The previous ciphertext for the slot, or undefined if the slot is empty
358
+ * @returns The end of the slot's chain of usable entries and its tree index, or undefined when the
359
+ * slot holds nothing usable. The index is what a new input names as its parent.
270
360
  */
271
- declare const getPreviousCiphertext: (serverUrl: string, e3Id: number, address: string) => Promise<Uint8Array | undefined>;
361
+ declare const getPreviousCiphertext: (serverUrl: string, e3Id: bigint, address: string) => Promise<SlotHead | undefined>;
272
362
 
273
363
  /**
274
364
  * Get the current (most recent) round, optionally filtered by requester addresses.
@@ -284,14 +374,14 @@ declare const getCurrentRound: (serverUrl: string, requesters?: string[]) => Pro
284
374
  * @param e3Id - The e3Id of the round
285
375
  * @returns The committee public key bytes
286
376
  */
287
- declare const getRoundPublicKey: (serverUrl: string, e3Id: number) => Promise<Uint8Array>;
377
+ declare const getRoundPublicKey: (serverUrl: string, e3Id: bigint) => Promise<Uint8Array>;
288
378
  /**
289
379
  * Get the ciphertext output for a given round.
290
380
  * @param serverUrl - The base URL of the CRISP server
291
381
  * @param e3Id - The e3Id of the round
292
382
  * @returns The ciphertext output bytes
293
383
  */
294
- declare const getRoundCiphertext: (serverUrl: string, e3Id: number) => Promise<Uint8Array>;
384
+ declare const getRoundCiphertext: (serverUrl: string, e3Id: bigint) => Promise<Uint8Array>;
295
385
  /**
296
386
  * Request a new E3 round. Requires the server's cron API key.
297
387
  * @param serverUrl - The base URL of the CRISP server
@@ -313,14 +403,14 @@ declare const broadcastVote: (serverUrl: string, request: BroadcastVoteRequest)
313
403
  * @param address - The voter address
314
404
  * @returns The vote status for the address
315
405
  */
316
- declare const getVoteStatus: (serverUrl: string, e3Id: number, address: string) => Promise<VoteStatusResponse>;
406
+ declare const getVoteStatus: (serverUrl: string, e3Id: bigint, address: string) => Promise<VoteStatusResponse>;
317
407
  /**
318
408
  * Get the result for a given round.
319
409
  * @param serverUrl - The base URL of the CRISP server
320
410
  * @param e3Id - The e3Id of the round
321
411
  * @returns The round result (tally, emojis, total votes, end time and requester)
322
412
  */
323
- declare const getRoundResult: (serverUrl: string, e3Id: number) => Promise<WebResultResponse>;
413
+ declare const getRoundResult: (serverUrl: string, e3Id: bigint) => Promise<WebResultResponse>;
324
414
  /**
325
415
  * Get the results for all rounds, optionally filtered by requester addresses.
326
416
  * @param serverUrl - The base URL of the CRISP server
@@ -335,7 +425,7 @@ declare const getAllRoundResults: (serverUrl: string, requesters?: string[]) =>
335
425
  * @param e3Id - The e3Id of the round
336
426
  * @returns The lite round state
337
427
  */
338
- declare const getRoundStateLite: (serverUrl: string, e3Id: number) => Promise<E3StateLiteResponse>;
428
+ declare const getRoundStateLite: (serverUrl: string, e3Id: bigint) => Promise<E3StateLiteResponse>;
339
429
  /**
340
430
  * Get the token holder hashes (hash(address, balance)) for a given round.
341
431
  * These are the Merkle tree leaves used for eligibility proofs.
@@ -343,14 +433,14 @@ declare const getRoundStateLite: (serverUrl: string, e3Id: number) => Promise<E3
343
433
  * @param e3Id - The e3Id of the round
344
434
  * @returns The list of token holder hashes
345
435
  */
346
- declare const getTokenHolderHashes: (serverUrl: string, e3Id: number) => Promise<string[]>;
436
+ declare const getTokenHolderHashes: (serverUrl: string, e3Id: bigint) => Promise<string[]>;
347
437
  /**
348
438
  * Get the eligible addresses and their balances for a given round.
349
439
  * @param serverUrl - The base URL of the CRISP server
350
440
  * @param e3Id - The e3Id of the round
351
441
  * @returns The list of eligible token holders
352
442
  */
353
- declare const getEligibleAddresses: (serverUrl: string, e3Id: number) => Promise<TokenHolder[]>;
443
+ declare const getEligibleAddresses: (serverUrl: string, e3Id: bigint) => Promise<TokenHolder[]>;
354
444
 
355
445
  declare const MERKLE_TREE_MAX_DEPTH = 20;
356
446
  declare const MAX_MSG_NON_ZERO_COEFFS = 100;
@@ -467,7 +557,27 @@ declare const generateBFVKeys: () => {
467
557
  publicKey: Uint8Array;
468
558
  };
469
559
 
560
+ /**
561
+ * Split a 32-byte digest into the two 16-byte halves the circuit takes as public inputs.
562
+ *
563
+ * A Keccak digest is 256 bits and a field element holds fewer than 254, so it cannot cross the
564
+ * circuit boundary in one piece. `crisp_lib::ecdsa::digest_from_halves` rebuilds the 32 bytes and
565
+ * range-checks each half, and `CRISPProgram.publishInput` splits the same way.
566
+ *
567
+ * @param digest The 32-byte ballot digest.
568
+ * @returns The high and low halves as hex field elements.
569
+ */
570
+ declare const splitDigest: (digest: `0x${string}`) => {
571
+ digestHi: `0x${string}`;
572
+ digestLo: `0x${string}`;
573
+ };
574
+
470
575
  declare const destroyBBApi: () => void;
576
+ /**
577
+ * Encrypt a ballot and build every circuit input that does not depend on the signature.
578
+ * Runs in a worker when available to avoid blocking the main thread.
579
+ */
580
+ declare const prepareCircuitInputs: (inputs: PrepareBallotInputs) => Promise<PreparedBallot>;
471
581
  /**
472
582
  * Validate a vote.
473
583
  * @param vote - The vote to validate.
@@ -475,30 +585,55 @@ declare const destroyBBApi: () => void;
475
585
  */
476
586
  declare const validateVote: (vote: Vote, balance: bigint) => void;
477
587
  /**
478
- * Generate a vote proof for the CRISP circuit given the vote proof inputs.
479
- * @param voteProofInputs - The vote proof inputs.
480
- * @returns The vote proof.
588
+ * Phase one: encrypt a ballot, before the voter signs anything.
589
+ *
590
+ * A ballot must be encrypted before it can be signed, because the digest binds the ciphertext.
591
+ * Take `ctCommitment` from the result, read the digest from `CRISPProgram.ballotDigest`, have the
592
+ * voter sign it, then call {@link finishBallotProof}.
593
+ *
594
+ * @param inputs - The ballot to encrypt.
595
+ * @returns The prepared ballot.
481
596
  */
482
- declare const generateVoteProof: (voteProofInputs: VoteProofInputs) => Promise<ProofData>;
597
+ declare const prepareBallot: (inputs: PrepareBallotInputs) => Promise<PreparedBallot>;
483
598
  /**
484
- * Generate a proof for a vote masking operation.
485
- * @param maskVoteProofInputs The mask vote proof inputs.
486
- * @returns
599
+ * Phase two: prove a prepared ballot, given the signature over its digest.
600
+ *
601
+ * Get the digest from `CRISPProgram.ballotDigest(e3Id, slot, prepared.ctCommitment)` and have the
602
+ * voter sign it. Reading it from the contract rather than rebuilding the EIP-712 struct here means
603
+ * there is only one implementation of the domain to keep correct.
604
+ *
605
+ * @param prepared The output of `prepareBallot`.
606
+ * @param digest The ballot digest.
607
+ * @param signature The voter signature over that digest.
608
+ * @returns The proof.
609
+ */
610
+ declare const finishBallotProof: (prepared: PreparedBallot, digest: `0x${string}`, signature: `0x${string}`) => Promise<ProofData>;
611
+ /**
612
+ * Phase two for a mask.
613
+ *
614
+ * A mask carries the same digest as a real vote, because `CRISPProgram.publishInput` computes it
615
+ * for every input regardless of branch. Only the signature is a placeholder, and the circuit does
616
+ * not check it on the mask branch. Passing a real digest here is what keeps a mask and a vote
617
+ * indistinguishable in the published public inputs.
618
+ *
619
+ * @param prepared The output of `prepareBallot` with `isMaskVote: true`.
620
+ * @param digest The ballot digest, from the same contract call a real vote would use.
621
+ * @returns The proof.
487
622
  */
488
- declare const generateMaskVoteProof: (maskVoteProofInputs: MaskVoteProofInputs) => Promise<ProofData>;
623
+ declare const finishMaskProof: (prepared: PreparedBallot, digest: `0x${string}`) => Promise<ProofData>;
489
624
  /**
490
625
  * Locally verify a Noir proof.
491
626
  * @param proof - The proof to verify.
492
627
  * @returns True if the proof is valid, false otherwise.
493
628
  */
494
- declare const verifyProof: (proof: ProofData) => Promise<boolean>;
629
+ declare const verifyProof: (proof: ProofData, censusMode?: CensusVariant) => Promise<boolean>;
495
630
  /**
496
631
  * Encode the proof data into a format that can be used by the CRISP program in Solidity
497
632
  * to validate the proof.
498
633
  * @param proof The proof data.
499
634
  * @returns The encoded proof data as a hex string.
500
635
  */
501
- declare const encodeSolidityProof: ({ publicInputs, proof, encryptedVote }: ProofData) => Hex;
636
+ declare const encodeSolidityProof: ({ publicInputs, proof, encryptedVote, parentIndexPlusOne }: ProofData) => Hex;
502
637
 
503
638
  /**
504
639
  * A class representing the CRISP SDK.
@@ -515,22 +650,33 @@ declare class CrispSDK {
515
650
  */
516
651
  constructor(serverUrl: string);
517
652
  /**
518
- * Generate a proof for a vote masking.
519
- * @param maskProofInputs - The inputs required to generate the mask vote proof.
520
- * @returns A promise that resolves to the generated proof data.
653
+ * Phase one: encrypt a ballot, before the voter signs anything.
654
+ *
655
+ * A ballot has to be encrypted before it can be signed, because the digest binds the ciphertext.
656
+ * Take `ctCommitment` from the result, read the digest from
657
+ * `CRISPProgram.ballotDigest(e3Id, slot, ctCommitment)`, have the voter sign it, then call
658
+ * {@link finishBallot}.
659
+ *
660
+ * Masks and real votes take the same path. This method calls the same server API
661
+ * (previous-ciphertext) for both, so the server cannot infer the ballot type from the request
662
+ * pattern, and the encryption is identical either way.
663
+ *
664
+ * @param request - The ballot to encrypt.
665
+ * @returns A promise that resolves to the prepared ballot.
521
666
  */
522
- generateMaskVoteProof(maskProofInputs: MaskVoteProofRequest): Promise<ProofData>;
667
+ prepareBallot(request: PrepareBallotRequest): Promise<PreparedBallot>;
523
668
  /**
524
- * Generate a proof for a vote.
669
+ * Phase two: prove a prepared ballot.
525
670
  *
526
- * Note: The previous ciphertext is not used in the proof computation. This method still calls
527
- * the same server API (previous-ciphertext) as {@link generateMaskVoteProof} to prevent the
528
- * server from inferring the vote type (mask vs normal) from the client's API usage pattern.
671
+ * A mask passes no signature and gets the placeholder. It still carries the same digest as a
672
+ * real vote, because the contract computes the digest for every input.
529
673
  *
530
- * @param voteProofInputs - The inputs required to generate the vote proof.
674
+ * @param prepared - The output of {@link prepareBallot}.
675
+ * @param digest - The digest read from `CRISPProgram.ballotDigest`.
676
+ * @param signature - The voter signature, omitted for a mask.
531
677
  * @returns A promise that resolves to the generated proof data.
532
678
  */
533
- generateVoteProof(voteProofInputs: VoteProofRequest): Promise<ProofData>;
679
+ finishBallot(prepared: PreparedBallot, digest: `0x${string}`, signature?: `0x${string}`): Promise<ProofData>;
534
680
  /**
535
681
  * Get the current (most recent) round, optionally filtered by requester addresses.
536
682
  * @param requesters - Optional list of requester addresses to filter by
@@ -542,13 +688,13 @@ declare class CrispSDK {
542
688
  * @param e3Id - The e3Id of the round
543
689
  * @returns The committee public key bytes
544
690
  */
545
- getRoundPublicKey(e3Id: number): Promise<Uint8Array>;
691
+ getRoundPublicKey(e3Id: bigint): Promise<Uint8Array>;
546
692
  /**
547
693
  * Get the ciphertext output for a given round.
548
694
  * @param e3Id - The e3Id of the round
549
695
  * @returns The ciphertext output bytes
550
696
  */
551
- getRoundCiphertext(e3Id: number): Promise<Uint8Array>;
697
+ getRoundCiphertext(e3Id: bigint): Promise<Uint8Array>;
552
698
  /**
553
699
  * Request a new E3 round. Requires the server's cron API key.
554
700
  * @param request - The new round request (cron API key, token address and balance threshold)
@@ -567,13 +713,13 @@ declare class CrispSDK {
567
713
  * @param address - The voter address
568
714
  * @returns The vote status for the address
569
715
  */
570
- getVoteStatus(e3Id: number, address: string): Promise<VoteStatusResponse>;
716
+ getVoteStatus(e3Id: bigint, address: string): Promise<VoteStatusResponse>;
571
717
  /**
572
718
  * Get the result for a given round.
573
719
  * @param e3Id - The e3Id of the round
574
720
  * @returns The round result (tally, emojis, total votes, end time and requester)
575
721
  */
576
- getRoundResult(e3Id: number): Promise<WebResultResponse>;
722
+ getRoundResult(e3Id: bigint): Promise<WebResultResponse>;
577
723
  /**
578
724
  * Get the results for all rounds, optionally filtered by requester addresses.
579
725
  * @param requesters - Optional list of requester addresses to filter by
@@ -585,13 +731,13 @@ declare class CrispSDK {
585
731
  * @param e3Id - The e3Id of the round
586
732
  * @returns The lite round state
587
733
  */
588
- getRoundStateLite(e3Id: number): Promise<E3StateLiteResponse>;
734
+ getRoundStateLite(e3Id: bigint): Promise<E3StateLiteResponse>;
589
735
  /**
590
736
  * Get the details of a specific round in a camelCase convenience format.
591
737
  * @param e3Id - The e3Id of the round
592
738
  * @returns The round details
593
739
  */
594
- getRoundDetails(e3Id: number): Promise<RoundDetails>;
740
+ getRoundDetails(e3Id: bigint): Promise<RoundDetails>;
595
741
  /**
596
742
  * Get the round data stored in the CRISPProgram contract, read directly from the chain.
597
743
  *
@@ -602,33 +748,33 @@ declare class CrispSDK {
602
748
  * @param chainId - The chain ID of the network the program is deployed on
603
749
  * @returns The on chain round data
604
750
  */
605
- getOnChainRoundData(programAddress: string, e3Id: number, chainId?: number): Promise<OnChainRoundData>;
751
+ getOnChainRoundData(programAddress: string, e3Id: bigint, chainId?: number): Promise<OnChainRoundData>;
606
752
  /**
607
753
  * Get the token address, balance threshold and snapshot block for a specific round.
608
754
  * @param e3Id - The e3Id of the round
609
755
  * @returns The token details
610
756
  */
611
- getRoundTokenDetails(e3Id: number): Promise<TokenDetails>;
757
+ getRoundTokenDetails(e3Id: bigint): Promise<TokenDetails>;
612
758
  /**
613
759
  * Get the token holder hashes (hash(address, balance)) for a given round.
614
760
  * These are the Merkle tree leaves used for eligibility proofs.
615
761
  * @param e3Id - The e3Id of the round
616
762
  * @returns The list of token holder hashes
617
763
  */
618
- getTokenHolderHashes(e3Id: number): Promise<string[]>;
764
+ getTokenHolderHashes(e3Id: bigint): Promise<string[]>;
619
765
  /**
620
766
  * Get the eligible addresses and their balances for a given round.
621
767
  * @param e3Id - The e3Id of the round
622
768
  * @returns The list of eligible token holders
623
769
  */
624
- getEligibleAddresses(e3Id: number): Promise<TokenHolder[]>;
770
+ getEligibleAddresses(e3Id: bigint): Promise<TokenHolder[]>;
625
771
  /**
626
772
  * Get the previous ciphertext input for a slot address in a given round.
627
773
  * @param e3Id - The e3Id of the round
628
774
  * @param address - The address of the slot
629
- * @returns The previous ciphertext, or undefined if the slot is empty
775
+ * @returns The slot head and its tree index, or undefined if the slot holds nothing usable
630
776
  */
631
- getPreviousCiphertext(e3Id: number, address: string): Promise<Uint8Array | undefined>;
777
+ getPreviousCiphertext(e3Id: bigint, address: string): Promise<SlotHead | undefined>;
632
778
  }
633
779
 
634
- export { type BroadcastVoteRequest, type BroadcastVoteResponse, CreditMode, CrispSDK, type CurrentRoundResponse, type E3StateLiteResponse, type JsonResponse, MAX_MSG_NON_ZERO_COEFFS, MAX_VOTE_OPTIONS, MERKLE_TREE_MAX_DEPTH, type MaskVoteProofInputs, type NewRoundRequest, type OnChainRoundData, type ProofData, type RoundDetails, SIGNATURE_MESSAGE, SIGNATURE_MESSAGE_HASH, type TallyResult, type TokenDetails, type TokenHolder, type Vote, type VoteProofInputs, type VoteResponseStatus, type VoteStatusResponse, type WebResultResponse, broadcastVote, decodeTally, decryptVote, destroyBBApi, encodeSolidityProof, encodeVote, encryptVote, generateBFVKeys, generateMaskVoteProof, generateMerkleProof, generateMerkleTree, generateVoteProof, getAddressFromSignature, getAllRoundResults, getBalanceAt, getCurrentRound, getEligibleAddresses, getMaxVoteValue, getOnChainRoundData, getPreviousCiphertext, getRoundCiphertext, getRoundDetails, getRoundPublicKey, getRoundResult, getRoundStateLite, getRoundTokenDetails, getScaledBalance, getTokenHolderHashes, getTotalSupplyAt, getTreeData, getVoteStatus, getZeroVote, hashLeaf, requestNewRound, validateVote, verifyProof };
780
+ export { type BroadcastVoteRequest, type BroadcastVoteResponse, type CensusVariant, CreditMode, CrispSDK, type CurrentRoundResponse, type E3StateLiteResponse, type JsonResponse, MAX_MSG_NON_ZERO_COEFFS, MAX_VOTE_OPTIONS, MERKLE_TREE_MAX_DEPTH, type NewRoundRequest, type OnChainRoundData, type PrepareBallotInputs, type PrepareBallotRequest, type PreparedBallot, type ProofData, type RoundDetails, SIGNATURE_MESSAGE, SIGNATURE_MESSAGE_HASH, type SlotHead, type TallyResult, type TokenDetails, type TokenHolder, type Vote, type VoteResponseStatus, type VoteStatusResponse, type WebResultResponse, broadcastVote, decodeTally, decryptVote, destroyBBApi, encodeSolidityProof, encodeVote, encryptVote, finishBallotProof, finishMaskProof, generateBFVKeys, generateMerkleProof, generateMerkleTree, getAddressFromSignature, getAllRoundResults, getBalanceAt, getCurrentRound, getEligibleAddresses, getMaxVoteValue, getOnChainRoundData, getOnchainVotingPower, getPreviousCiphertext, getRoundCiphertext, getRoundDetails, getRoundPublicKey, getRoundResult, getRoundStateLite, getRoundTokenDetails, getScaledBalance, getTokenHolderHashes, getTotalSupplyAt, getTreeData, getVoteStatus, getZeroVote, hashLeaf, prepareBallot, prepareCircuitInputs, requestNewRound, splitDigest, validateVote, verifyProof };