@crisp-e3/sdk 0.13.0 → 0.15.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/dist/index.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ type RoundDetails = {
|
|
|
38
38
|
startTime: bigint;
|
|
39
39
|
endTime: bigint;
|
|
40
40
|
startBlock: bigint;
|
|
41
|
+
snapshotBlock: bigint;
|
|
41
42
|
committeePublicKey: Uint8Array;
|
|
42
43
|
emojis: [string, string];
|
|
43
44
|
tokenAddress: string;
|
|
@@ -80,6 +81,15 @@ type MerkleProof = {
|
|
|
80
81
|
* Type representing a vote
|
|
81
82
|
*/
|
|
82
83
|
type Vote = number[];
|
|
84
|
+
/**
|
|
85
|
+
* Type representing a decoded tally: one total per option.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* Uses `bigint` because an aggregated tally coefficient is a sum over all ballots,
|
|
89
|
+
* so a total can exceed `Number.MAX_SAFE_INTEGER`. This matches the `BigUint` the
|
|
90
|
+
* CRISP server returns and the `uint256` the CRISP contract returns.
|
|
91
|
+
*/
|
|
92
|
+
type TallyResult = bigint[];
|
|
83
93
|
type ProofData = {
|
|
84
94
|
publicInputs: string[];
|
|
85
95
|
proof: Uint8Array;
|
|
@@ -139,6 +149,7 @@ type E3StateLiteResponse = {
|
|
|
139
149
|
start_time: number;
|
|
140
150
|
end_time: number;
|
|
141
151
|
start_block: number;
|
|
152
|
+
snapshot_block: number;
|
|
142
153
|
committee_public_key: number[];
|
|
143
154
|
emojis: [string, string];
|
|
144
155
|
token_address: string;
|
|
@@ -403,6 +414,17 @@ declare const getScaledBalance: (balance: bigint, decimals: bigint) => bigint;
|
|
|
403
414
|
* encoding, encryption, decryption, and tally decoding.
|
|
404
415
|
*/
|
|
405
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Encodes vote choices into a polynomial coefficient array for BFV encryption.
|
|
419
|
+
* Each choice occupies floor(MAX_MSG_NON_ZERO_COEFFS / n) binary coefficients;
|
|
420
|
+
* remaining slots in the first MAX_MSG_NON_ZERO_COEFFS coeffs are zero; then
|
|
421
|
+
* the vector is padded to the BFV degree.
|
|
422
|
+
*
|
|
423
|
+
* @param vote - Array of numeric values per choice (e.g. [10, 5] for 2 options)
|
|
424
|
+
* @returns Array of 0s and 1s representing coefficients
|
|
425
|
+
* @throws If vote has fewer than 2 choices, any value exceeds max for its segment, or degree is too small
|
|
426
|
+
*/
|
|
427
|
+
declare const encodeVote: (vote: Vote) => number[];
|
|
406
428
|
/**
|
|
407
429
|
* Encrypts an encoded vote using BFV homomorphic encryption.
|
|
408
430
|
*
|
|
@@ -412,14 +434,29 @@ declare const getScaledBalance: (balance: bigint, decimals: bigint) => bigint;
|
|
|
412
434
|
*/
|
|
413
435
|
declare const encryptVote: (vote: Vote, publicKey: Uint8Array) => Uint8Array;
|
|
414
436
|
/**
|
|
415
|
-
* Decodes raw tally bytes (or
|
|
437
|
+
* Decodes raw tally bytes (or coefficients) into a total per choice.
|
|
416
438
|
* Expects the same segment layout as used in encodeVote.
|
|
417
439
|
*
|
|
418
|
-
*
|
|
440
|
+
* Mirrors `crisp_utils::decode_tally` (Rust) and `CRISPProgram.decodeTally` (Solidity):
|
|
441
|
+
* only the first MAX_MSG_NON_ZERO_COEFFS coefficients carry the payload, split into
|
|
442
|
+
* `floor(MAX_MSG_NON_ZERO_COEFFS / numChoices)` binary coefficients per choice, MSB first.
|
|
443
|
+
*
|
|
444
|
+
* @param tallyBytes - Hex string, or the polynomial coefficients from tally/decryption
|
|
445
|
+
* @param numChoices - Number of vote options: an integer from 2 to MAX_VOTE_OPTIONS
|
|
446
|
+
* @returns One total per choice
|
|
447
|
+
* @throws If numChoices is outside 2..MAX_VOTE_OPTIONS or not an integer, or there are fewer
|
|
448
|
+
* coefficients than the payload region
|
|
449
|
+
*/
|
|
450
|
+
declare const decodeTally: (tallyBytes: string | number[] | bigint[], numChoices: number) => TallyResult;
|
|
451
|
+
/**
|
|
452
|
+
* Decrypts a BFV-encrypted vote and decodes it to vote values.
|
|
453
|
+
*
|
|
454
|
+
* @param ciphertext - Encrypted vote
|
|
455
|
+
* @param secretKey - BFV secret key
|
|
419
456
|
* @param numChoices - Number of vote options
|
|
420
|
-
* @returns
|
|
457
|
+
* @returns One total per choice
|
|
421
458
|
*/
|
|
422
|
-
declare const
|
|
459
|
+
declare const decryptVote: (ciphertext: Uint8Array, secretKey: Uint8Array, numChoices: number) => TallyResult;
|
|
423
460
|
/**
|
|
424
461
|
* Generates a BFV keypair for vote encryption and decryption.
|
|
425
462
|
*
|
|
@@ -594,4 +631,4 @@ declare class CrispSDK {
|
|
|
594
631
|
getPreviousCiphertext(e3Id: number, address: string): Promise<Uint8Array | undefined>;
|
|
595
632
|
}
|
|
596
633
|
|
|
597
|
-
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 TokenDetails, type TokenHolder, type Vote, type VoteProofInputs, type VoteResponseStatus, type VoteStatusResponse, type WebResultResponse, broadcastVote, decodeTally, destroyBBApi, encodeSolidityProof, 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 };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -172,6 +172,7 @@ var getRoundDetails = async (serverUrl, e3Id) => {
|
|
|
172
172
|
startTime: BigInt(data.start_time),
|
|
173
173
|
endTime: BigInt(data.end_time),
|
|
174
174
|
startBlock: BigInt(data.start_block),
|
|
175
|
+
snapshotBlock: BigInt(data.snapshot_block),
|
|
175
176
|
committeePublicKey: new Uint8Array(data.committee_public_key),
|
|
176
177
|
emojis: data.emojis,
|
|
177
178
|
numOptions: BigInt(data.num_options),
|
|
@@ -185,7 +186,7 @@ var getRoundTokenDetails = async (serverUrl, e3Id) => {
|
|
|
185
186
|
return {
|
|
186
187
|
tokenAddress: roundDetails.tokenAddress,
|
|
187
188
|
threshold: roundDetails.balanceThreshold,
|
|
188
|
-
snapshotBlock: roundDetails.
|
|
189
|
+
snapshotBlock: roundDetails.snapshotBlock
|
|
189
190
|
};
|
|
190
191
|
};
|
|
191
192
|
var getOnChainRoundData = async (programAddress, e3Id, chainId) => {
|
|
@@ -294,7 +295,7 @@ var getMaxVoteValue = (numChoices) => {
|
|
|
294
295
|
var getZeroVote = (numChoices) => {
|
|
295
296
|
return Array(numChoices).fill(0);
|
|
296
297
|
};
|
|
297
|
-
var
|
|
298
|
+
var decodeBytesToBigInts = (data) => {
|
|
298
299
|
if (data.length % 8 !== 0) {
|
|
299
300
|
throw new Error("Data length must be multiple of 8");
|
|
300
301
|
}
|
|
@@ -304,7 +305,7 @@ var decodeBytesToNumbers = (data) => {
|
|
|
304
305
|
for (let i = 0; i < arrayLength; i++) {
|
|
305
306
|
result.push(view.getBigUint64(i * 8, true));
|
|
306
307
|
}
|
|
307
|
-
return result
|
|
308
|
+
return result;
|
|
308
309
|
};
|
|
309
310
|
var numberArrayToBigInt64Array = (numberArray) => {
|
|
310
311
|
return BigInt64Array.from(numberArray.map(BigInt));
|
|
@@ -337,6 +338,9 @@ var encodeVote = (vote) => {
|
|
|
337
338
|
if (numChoices < 2) {
|
|
338
339
|
throw new Error("Vote must have at least two choices");
|
|
339
340
|
}
|
|
341
|
+
if (numChoices > MAX_VOTE_OPTIONS) {
|
|
342
|
+
throw new Error(`Number of choices (${numChoices}) exceeds MAX_VOTE_OPTIONS (${MAX_VOTE_OPTIONS})`);
|
|
343
|
+
}
|
|
340
344
|
const bfvParams = getZkInputsGenerator().getBFVParams();
|
|
341
345
|
const degree = bfvParams.degree;
|
|
342
346
|
if (degree < MAX_MSG_NON_ZERO_COEFFS) {
|
|
@@ -370,27 +374,38 @@ var encryptVote = (vote, publicKey) => {
|
|
|
370
374
|
return getZkInputsGenerator().encryptVote(publicKey, numberArrayToBigInt64Array(encodedVote));
|
|
371
375
|
};
|
|
372
376
|
var decodeTally = (tallyBytes, numChoices) => {
|
|
377
|
+
if (!Number.isInteger(numChoices) || numChoices < 2) {
|
|
378
|
+
throw new Error(`Number of choices (${numChoices}) must be an integer of at least 2`);
|
|
379
|
+
}
|
|
380
|
+
if (numChoices > MAX_VOTE_OPTIONS) {
|
|
381
|
+
throw new Error(`Number of choices (${numChoices}) exceeds MAX_VOTE_OPTIONS (${MAX_VOTE_OPTIONS})`);
|
|
382
|
+
}
|
|
383
|
+
let coefficients;
|
|
373
384
|
if (typeof tallyBytes === "string") {
|
|
374
385
|
const hexString = tallyBytes.startsWith("0x") ? tallyBytes : `0x${tallyBytes}`;
|
|
375
|
-
|
|
386
|
+
coefficients = decodeBytesToBigInts(hexToBytes2(hexString));
|
|
387
|
+
} else {
|
|
388
|
+
coefficients = tallyBytes.map(BigInt);
|
|
376
389
|
}
|
|
377
|
-
if (
|
|
378
|
-
throw new Error(
|
|
390
|
+
if (coefficients.length < MAX_MSG_NON_ZERO_COEFFS) {
|
|
391
|
+
throw new Error(`decoded coefficient count (${coefficients.length}) is less than MAX_MSG_NON_ZERO_COEFFS (${MAX_MSG_NON_ZERO_COEFFS})`);
|
|
379
392
|
}
|
|
380
393
|
const segmentSize = Math.floor(MAX_MSG_NON_ZERO_COEFFS / numChoices);
|
|
381
394
|
const results = [];
|
|
382
395
|
for (let choiceIdx = 0; choiceIdx < numChoices; choiceIdx++) {
|
|
383
396
|
const segmentStart = choiceIdx * segmentSize;
|
|
384
|
-
|
|
385
|
-
let
|
|
386
|
-
|
|
387
|
-
const weight = 2 ** (segment.length - 1 - i);
|
|
388
|
-
value += segment[i] * weight;
|
|
397
|
+
let value = 0n;
|
|
398
|
+
for (let i = 0; i < segmentSize; i++) {
|
|
399
|
+
value += coefficients[segmentStart + i] << BigInt(segmentSize - 1 - i);
|
|
389
400
|
}
|
|
390
401
|
results.push(value);
|
|
391
402
|
}
|
|
392
403
|
return results;
|
|
393
404
|
};
|
|
405
|
+
var decryptVote = (ciphertext, secretKey, numChoices) => {
|
|
406
|
+
const decryptedVote = getZkInputsGenerator().decryptVote(secretKey, ciphertext);
|
|
407
|
+
return decodeTally(Array.from(decryptedVote), numChoices);
|
|
408
|
+
};
|
|
394
409
|
var generateBFVKeys = () => {
|
|
395
410
|
return getZkInputsGenerator().generateKeys();
|
|
396
411
|
};
|
|
@@ -3625,8 +3640,10 @@ export {
|
|
|
3625
3640
|
SIGNATURE_MESSAGE_HASH,
|
|
3626
3641
|
broadcastVote,
|
|
3627
3642
|
decodeTally,
|
|
3643
|
+
decryptVote,
|
|
3628
3644
|
destroyBBApi,
|
|
3629
3645
|
encodeSolidityProof,
|
|
3646
|
+
encodeVote,
|
|
3630
3647
|
encryptVote,
|
|
3631
3648
|
generateBFVKeys,
|
|
3632
3649
|
generateMaskVoteProof,
|