@unconfirmed/kei 0.0.3 → 0.0.5
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 +4 -4
- package/dist/cli.js +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -7
- package/dist/types.d.ts +2 -2
- package/dist/verify.d.ts +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -10,12 +10,12 @@ No native dependencies — works in browsers, Cloudflare Workers, Bun, and Node.
|
|
|
10
10
|
|
|
11
11
|
## How it works
|
|
12
12
|
|
|
13
|
-
Sui validators sign checkpoint summaries using BLS12-381 aggregate signatures. A checkpoint is valid if validators holding ≥66.67% of
|
|
13
|
+
Sui validators sign checkpoint summaries using BLS12-381 aggregate signatures. A checkpoint is valid if validators holding ≥66.67% of weight (6667/10000) have signed it. Kei:
|
|
14
14
|
|
|
15
15
|
1. **Decodes the signers bitmap** (RoaringBitmap) to identify which validators signed
|
|
16
16
|
2. **Aggregates their BLS public keys** (G2 points in min-sig mode)
|
|
17
17
|
3. **Verifies the aggregate signature** against the BCS-serialized checkpoint summary
|
|
18
|
-
4. **Validates quorum** — total
|
|
18
|
+
4. **Validates quorum** — total weight of signers must meet the threshold
|
|
19
19
|
|
|
20
20
|
Once a checkpoint is verified, any data committed to it (transactions, objects, events) can be trusted via hash chains.
|
|
21
21
|
|
|
@@ -85,9 +85,9 @@ import {
|
|
|
85
85
|
// Build committee from validator data (once per epoch)
|
|
86
86
|
const committee = {
|
|
87
87
|
epoch: 1052n,
|
|
88
|
-
members: validators.map(({ publicKey,
|
|
88
|
+
members: validators.map(({ publicKey, weight }) => ({
|
|
89
89
|
publicKey, // 96-byte BLS12-381 G2 compressed
|
|
90
|
-
|
|
90
|
+
weight,
|
|
91
91
|
})),
|
|
92
92
|
};
|
|
93
93
|
|
package/dist/cli.js
CHANGED
|
@@ -236,7 +236,7 @@ class PreparedCommittee {
|
|
|
236
236
|
this.epoch = committee.epoch;
|
|
237
237
|
this.members = committee.members.map((m) => ({
|
|
238
238
|
point: bls12_381.G2.ProjectivePoint.fromHex(m.publicKey),
|
|
239
|
-
|
|
239
|
+
weight: m.weight
|
|
240
240
|
}));
|
|
241
241
|
}
|
|
242
242
|
}
|
|
@@ -253,11 +253,11 @@ function verifyCheckpoint(summaryBcs, authSignature, committee) {
|
|
|
253
253
|
throw new Error(`Signer index ${idx} exceeds committee size ${prepared.members.length}`);
|
|
254
254
|
}
|
|
255
255
|
const member = prepared.members[idx];
|
|
256
|
-
totalPower += member.
|
|
256
|
+
totalPower += member.weight;
|
|
257
257
|
aggregatedPoint = aggregatedPoint ? aggregatedPoint.add(member.point) : member.point;
|
|
258
258
|
}
|
|
259
259
|
if (totalPower < QUORUM_THRESHOLD) {
|
|
260
|
-
throw new Error(`Insufficient
|
|
260
|
+
throw new Error(`Insufficient weight: ${totalPower} < ${QUORUM_THRESHOLD} (${signerIndices.length}/${prepared.members.length} validators)`);
|
|
261
261
|
}
|
|
262
262
|
const epochBytes = new Uint8Array(8);
|
|
263
263
|
const epochView = new DataView(epochBytes.buffer);
|
|
@@ -351,7 +351,7 @@ async function fetchCommittee(url, epoch) {
|
|
|
351
351
|
epoch: BigInt(epoch),
|
|
352
352
|
members: json.result.validators.map(([pk, stake]) => ({
|
|
353
353
|
publicKey: new Uint8Array(Buffer.from(pk, "base64")),
|
|
354
|
-
|
|
354
|
+
weight: BigInt(stake)
|
|
355
355
|
}))
|
|
356
356
|
};
|
|
357
357
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,4 +5,4 @@ export { suiDigest, checkpointDigest, checkpointContentsDigest, transactionDiges
|
|
|
5
5
|
export { decodeRoaringBitmap } from './bitmap.js';
|
|
6
6
|
export { bcsCheckpointSummary, bcsCheckpointContents, bcsAuthorityQuorumSignInfo } from './bcs.js';
|
|
7
7
|
export type { CheckpointSummary, CheckpointContents, ExecutionDigests, Committee, CommitteeMember, AuthorityQuorumSignInfo, CertifiedCheckpointSummary, EndOfEpochData, GasCostSummary, CheckpointCommitment, } from './types.js';
|
|
8
|
-
export {
|
|
8
|
+
export { TOTAL_WEIGHT, QUORUM_THRESHOLD, CHECKPOINT_SUMMARY_INTENT } from './types.js';
|
package/dist/index.js
CHANGED
|
@@ -108,7 +108,7 @@ function transactionEffectsDigest(effectsBcs) {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
// src/types.ts
|
|
111
|
-
var
|
|
111
|
+
var TOTAL_WEIGHT = 10000n;
|
|
112
112
|
var QUORUM_THRESHOLD = 6667n;
|
|
113
113
|
var CHECKPOINT_SUMMARY_INTENT = new Uint8Array([2, 0, 0]);
|
|
114
114
|
|
|
@@ -168,7 +168,7 @@ class PreparedCommittee {
|
|
|
168
168
|
this.epoch = committee.epoch;
|
|
169
169
|
this.members = committee.members.map((m) => ({
|
|
170
170
|
point: bls12_381.G2.ProjectivePoint.fromHex(m.publicKey),
|
|
171
|
-
|
|
171
|
+
weight: m.weight
|
|
172
172
|
}));
|
|
173
173
|
}
|
|
174
174
|
}
|
|
@@ -185,11 +185,11 @@ function verifyCheckpoint(summaryBcs, authSignature, committee) {
|
|
|
185
185
|
throw new Error(`Signer index ${idx} exceeds committee size ${prepared.members.length}`);
|
|
186
186
|
}
|
|
187
187
|
const member = prepared.members[idx];
|
|
188
|
-
totalPower += member.
|
|
188
|
+
totalPower += member.weight;
|
|
189
189
|
aggregatedPoint = aggregatedPoint ? aggregatedPoint.add(member.point) : member.point;
|
|
190
190
|
}
|
|
191
191
|
if (totalPower < QUORUM_THRESHOLD) {
|
|
192
|
-
throw new Error(`Insufficient
|
|
192
|
+
throw new Error(`Insufficient weight: ${totalPower} < ${QUORUM_THRESHOLD} (${signerIndices.length}/${prepared.members.length} validators)`);
|
|
193
193
|
}
|
|
194
194
|
const epochBytes = new Uint8Array(8);
|
|
195
195
|
const epochView = new DataView(epochBytes.buffer);
|
|
@@ -275,9 +275,9 @@ function verifyCommitteeTransition(summaryBcs, summary, authSignature, currentCo
|
|
|
275
275
|
throw new Error("Checkpoint is not an end-of-epoch checkpoint");
|
|
276
276
|
}
|
|
277
277
|
const nextEpoch = summary.epoch + 1n;
|
|
278
|
-
const members = summary.endOfEpochData.nextEpochCommittee.map(([publicKey,
|
|
278
|
+
const members = summary.endOfEpochData.nextEpochCommittee.map(([publicKey, weight]) => ({
|
|
279
279
|
publicKey: Uint8Array.from(publicKey),
|
|
280
|
-
|
|
280
|
+
weight
|
|
281
281
|
}));
|
|
282
282
|
if (members.length === 0) {
|
|
283
283
|
throw new Error("End-of-epoch checkpoint has empty next committee");
|
|
@@ -311,7 +311,7 @@ export {
|
|
|
311
311
|
bcsCheckpointSummary,
|
|
312
312
|
bcsCheckpointContents,
|
|
313
313
|
bcsAuthorityQuorumSignInfo,
|
|
314
|
-
|
|
314
|
+
TOTAL_WEIGHT,
|
|
315
315
|
QUORUM_THRESHOLD,
|
|
316
316
|
PreparedCommittee,
|
|
317
317
|
CHECKPOINT_SUMMARY_INTENT
|
package/dist/types.d.ts
CHANGED
|
@@ -52,12 +52,12 @@ export interface CheckpointContents {
|
|
|
52
52
|
}
|
|
53
53
|
export interface CommitteeMember {
|
|
54
54
|
publicKey: AuthorityPublicKeyBytes;
|
|
55
|
-
|
|
55
|
+
weight: bigint;
|
|
56
56
|
}
|
|
57
57
|
export interface Committee {
|
|
58
58
|
epoch: bigint;
|
|
59
59
|
members: CommitteeMember[];
|
|
60
60
|
}
|
|
61
|
-
export declare const
|
|
61
|
+
export declare const TOTAL_WEIGHT = 10000n;
|
|
62
62
|
export declare const QUORUM_THRESHOLD = 6667n;
|
|
63
63
|
export declare const CHECKPOINT_SUMMARY_INTENT: Uint8Array<ArrayBuffer>;
|
package/dist/verify.d.ts
CHANGED
package/package.json
CHANGED