@umbra-privacy/ceremony 0.2.6 → 0.2.8
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.js +187 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -337,7 +337,32 @@ var COMPUTING_MESSAGES = [
|
|
|
337
337
|
"applying tau locally",
|
|
338
338
|
"folding contribution proof",
|
|
339
339
|
"blinding the trapdoor",
|
|
340
|
-
"sealing the bellman response"
|
|
340
|
+
"sealing the bellman response",
|
|
341
|
+
"asking the curve where it lives",
|
|
342
|
+
"weaving G1 points into the new delta",
|
|
343
|
+
"negotiating with the pairing engine",
|
|
344
|
+
"rotating the keypair commitment",
|
|
345
|
+
"checking that tau is non-zero (just in case)",
|
|
346
|
+
"evaluating polynomials over your secret",
|
|
347
|
+
"scrubbing the entropy buffer mid-flight",
|
|
348
|
+
"ratifying the contribution sequence",
|
|
349
|
+
"computing s \xB7 \u03C4 in G1",
|
|
350
|
+
"computing t \xB7 \u03C4 in G2",
|
|
351
|
+
"running the signature-of-knowledge",
|
|
352
|
+
"binding the transcript to your contribution",
|
|
353
|
+
"double-checking nothing is at infinity",
|
|
354
|
+
"compressing the response footprint",
|
|
355
|
+
"verifying your math one more time",
|
|
356
|
+
"stamping the public key bundle",
|
|
357
|
+
"writing the response file to disk",
|
|
358
|
+
"preparing to hand the response back to the worker",
|
|
359
|
+
"almost done \u2014 finalising your tau",
|
|
360
|
+
"this is the cryptography working hard for you",
|
|
361
|
+
"your secret never leaves this machine",
|
|
362
|
+
"if your power-21 circuit feels slow, blame BN254",
|
|
363
|
+
"snarkjs is single-threaded, give it a moment",
|
|
364
|
+
"trusted setup ceremonies are a team sport",
|
|
365
|
+
"the worker will verify everything you did"
|
|
341
366
|
];
|
|
342
367
|
var VERIFYING_MESSAGES = [
|
|
343
368
|
"worker pulling your response from S3",
|
|
@@ -346,20 +371,75 @@ var VERIFYING_MESSAGES = [
|
|
|
346
371
|
"writing the next zkey",
|
|
347
372
|
"uploading the new zkey",
|
|
348
373
|
"anchoring contribution hash in the audit chain",
|
|
349
|
-
"almost there \u2014 finalising your receipt"
|
|
374
|
+
"almost there \u2014 finalising your receipt",
|
|
375
|
+
"parsing your response's wire format",
|
|
376
|
+
"validating every G1 point is on the curve",
|
|
377
|
+
"validating every G2 point is in the right subgroup",
|
|
378
|
+
"rebuilding the transcript hash chain",
|
|
379
|
+
"running the knowledge proof pairing",
|
|
380
|
+
"verifying the new delta_g1 matches its evidence",
|
|
381
|
+
"verifying the new delta_g2 matches its evidence",
|
|
382
|
+
"checking the L array against your contribution",
|
|
383
|
+
"checking the H array against your contribution",
|
|
384
|
+
"deriving Fiat-Shamir scalars for the batched check",
|
|
385
|
+
"running the random-linear-combination same-ratio",
|
|
386
|
+
"confirming the vKey fields were not altered",
|
|
387
|
+
"sequencing the new zkey for the next contributor",
|
|
388
|
+
"this is the part where every byte gets re-checked",
|
|
389
|
+
"soundness of the whole ceremony rides on this step",
|
|
390
|
+
"exporting the new bellman params for the next round",
|
|
391
|
+
"writing your contribution into the ceremony transcript",
|
|
392
|
+
"publishing the new audit chain entry",
|
|
393
|
+
"any tampering would have failed by now",
|
|
394
|
+
"if you see this, your contribution is provably honest",
|
|
395
|
+
"computing your contribution receipt",
|
|
396
|
+
"the math agrees \u2014 wrapping things up",
|
|
397
|
+
"finalising the verified state in Postgres",
|
|
398
|
+
"you did the cryptographic work \u2014 we just had to check"
|
|
350
399
|
];
|
|
351
400
|
var EXPORTING_MESSAGES = [
|
|
352
401
|
"preparing your challenge file",
|
|
353
402
|
"extracting bellman params from the current zkey",
|
|
354
403
|
"wrapping the zkey for handoff",
|
|
355
404
|
"stamping a sha256 over the challenge bundle",
|
|
356
|
-
"presigning your download URL"
|
|
405
|
+
"presigning your download URL",
|
|
406
|
+
"fetching the latest verified state for this circuit",
|
|
407
|
+
"downloading the previous contribution from S3",
|
|
408
|
+
"checking the previous contribution's hash",
|
|
409
|
+
"carving out a personal challenge for your tau",
|
|
410
|
+
"the worker is queuing your slot",
|
|
411
|
+
"uploading the challenge so you can download it",
|
|
412
|
+
"rotating the worker's S3 connection",
|
|
413
|
+
"binding your challenge to the audit chain",
|
|
414
|
+
"stamping the challenge metadata in Postgres",
|
|
415
|
+
"AWS SDK is recycling connection pools",
|
|
416
|
+
"your slot timer starts the moment this finishes",
|
|
417
|
+
"writing the challenge to a fresh S3 key",
|
|
418
|
+
"double-checking the integrity hash",
|
|
419
|
+
"if this takes >1 min, S3 is having a bad time",
|
|
420
|
+
"Fargate is parsing the current bellman params",
|
|
421
|
+
"verifying the previous contribution's chain link",
|
|
422
|
+
"this step has no contributor-side analogue",
|
|
423
|
+
"the worker does the boring work so you don't have to",
|
|
424
|
+
"any second now \u2014 challenge is almost ready",
|
|
425
|
+
"encoding the challenge in Bellman wire format",
|
|
426
|
+
"making sure the file size matches the spec",
|
|
427
|
+
"presign URL is signed for 1 hour",
|
|
428
|
+
"your download will be tamper-evident",
|
|
429
|
+
"the challenge SHA-256 will be on the public transcript",
|
|
430
|
+
"patience \u2014 this is the longest server-side step",
|
|
431
|
+
"almost there \u2014 your turn is moments away"
|
|
357
432
|
];
|
|
358
433
|
|
|
359
434
|
// src/components/QueueView.tsx
|
|
360
435
|
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
361
436
|
var POLL_FAST_MS = 5e3;
|
|
362
437
|
var POLL_SLOW_MS = 15e3;
|
|
438
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
439
|
+
"timed_out",
|
|
440
|
+
"failed",
|
|
441
|
+
"verified"
|
|
442
|
+
]);
|
|
363
443
|
function QueueView({ ceremonyId: ceremonyId2, trackId, token, onReady, onError }) {
|
|
364
444
|
const [status, setStatus] = useState2(null);
|
|
365
445
|
const [pollErr, setPollErr] = useState2(null);
|
|
@@ -381,6 +461,9 @@ function QueueView({ ceremonyId: ceremonyId2, trackId, token, onReady, onError }
|
|
|
381
461
|
onReady(s);
|
|
382
462
|
return;
|
|
383
463
|
}
|
|
464
|
+
if (TERMINAL_STATUSES.has(s.status)) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
384
467
|
const interval = s.queue_position <= 2 ? POLL_FAST_MS : POLL_SLOW_MS;
|
|
385
468
|
timeoutRef.current = setTimeout(poll, interval);
|
|
386
469
|
} catch (err) {
|
|
@@ -406,6 +489,9 @@ function QueueView({ ceremonyId: ceremonyId2, trackId, token, onReady, onError }
|
|
|
406
489
|
] })
|
|
407
490
|
] });
|
|
408
491
|
}
|
|
492
|
+
if (TERMINAL_STATUSES.has(status.status)) {
|
|
493
|
+
return /* @__PURE__ */ jsx2(TerminalSlotMessage, { status: status.status });
|
|
494
|
+
}
|
|
409
495
|
const waitMins = Math.ceil((status.estimated_wait_secs ?? 0) / 60);
|
|
410
496
|
const expiresAt = status.slot_expires_at ? new Date(status.slot_expires_at).toLocaleTimeString([], {
|
|
411
497
|
hour: "2-digit",
|
|
@@ -458,6 +544,31 @@ function QueueView({ ceremonyId: ceremonyId2, trackId, token, onReady, onError }
|
|
|
458
544
|
] })
|
|
459
545
|
] });
|
|
460
546
|
}
|
|
547
|
+
function TerminalSlotMessage({ status }) {
|
|
548
|
+
const lines = {
|
|
549
|
+
timed_out: {
|
|
550
|
+
headline: "Your slot expired before the challenge file was ready.",
|
|
551
|
+
detail: "This is usually a transient S3 hiccup on the worker side, not a problem with your machine. Press B or Backspace to return to the track list and rejoin the queue \u2014 it almost always succeeds the second time."
|
|
552
|
+
},
|
|
553
|
+
failed: {
|
|
554
|
+
headline: "The worker reported a failure on this contribution.",
|
|
555
|
+
detail: "Press B or Backspace to return to the track list. If this happens repeatedly on the same track, the admin needs to investigate."
|
|
556
|
+
},
|
|
557
|
+
verified: {
|
|
558
|
+
headline: "This contribution is already recorded as verified.",
|
|
559
|
+
detail: "Press B or Backspace to return to the track list and pick another track."
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
const key = status;
|
|
563
|
+
const msg = lines[key] ?? {
|
|
564
|
+
headline: `Contribution ended with status: ${status}.`,
|
|
565
|
+
detail: "Press B or Backspace to return to the track list."
|
|
566
|
+
};
|
|
567
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", gap: 1, children: [
|
|
568
|
+
/* @__PURE__ */ jsx2(Text2, { color: "yellow", bold: true, children: msg.headline }),
|
|
569
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: msg.detail })
|
|
570
|
+
] });
|
|
571
|
+
}
|
|
461
572
|
function ExportingMessage({ status }) {
|
|
462
573
|
const exportingMsg = useCyclingMessage(EXPORTING_MESSAGES, 2500, status === "exporting");
|
|
463
574
|
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: status === "exporting" ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
@@ -813,6 +924,16 @@ function InfoModal() {
|
|
|
813
924
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
814
925
|
var NAME_MAX_LEN = 100;
|
|
815
926
|
var NAME_VALID_RE = /^[\p{L}\p{N} _.\-]*$/u;
|
|
927
|
+
function elideMiddle(s, maxLen) {
|
|
928
|
+
if (s.length <= maxLen) return s;
|
|
929
|
+
const keepHead = Math.ceil((maxLen - 1) / 2);
|
|
930
|
+
const keepTail = Math.floor((maxLen - 1) / 2);
|
|
931
|
+
return s.slice(0, keepHead) + "\u2026" + s.slice(s.length - keepTail);
|
|
932
|
+
}
|
|
933
|
+
function copyToClipboardOSC52(value) {
|
|
934
|
+
const payload = Buffer.from(value, "utf8").toString("base64");
|
|
935
|
+
process.stdout.write(`\x1B]52;c;${payload}\x07`);
|
|
936
|
+
}
|
|
816
937
|
function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName }) {
|
|
817
938
|
const { exit } = useApp();
|
|
818
939
|
const [activeCeremonyId, setActiveCeremonyId] = useState5(initialCeremonyId);
|
|
@@ -826,6 +947,8 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
826
947
|
const [contributed, setContributed] = useState5({});
|
|
827
948
|
const [selectedIdx, setSelectedIdx] = useState5(0);
|
|
828
949
|
const [tab, setTab] = useState5(0);
|
|
950
|
+
const [contribCursor, setContribCursor] = useState5(0);
|
|
951
|
+
const [copyToast, setCopyToast] = useState5(null);
|
|
829
952
|
const [showInfo, setShowInfo] = useState5(false);
|
|
830
953
|
useEffect5(() => {
|
|
831
954
|
if (!nameSet) return;
|
|
@@ -1046,6 +1169,7 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1046
1169
|
const { tracks } = screen;
|
|
1047
1170
|
if (key.tab) {
|
|
1048
1171
|
setTab((t) => (t + 1) % 2);
|
|
1172
|
+
setContribCursor(0);
|
|
1049
1173
|
return;
|
|
1050
1174
|
}
|
|
1051
1175
|
if (tab === 0) {
|
|
@@ -1062,6 +1186,30 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1062
1186
|
if (t && t.status === "open" && !contributed[t.id]) joinTrack(t);
|
|
1063
1187
|
return;
|
|
1064
1188
|
}
|
|
1189
|
+
} else if (tab === 1) {
|
|
1190
|
+
const myContribs = Object.values(contributed).filter(
|
|
1191
|
+
(c) => c.ceremonyId === activeCeremonyId
|
|
1192
|
+
);
|
|
1193
|
+
if (key.upArrow) {
|
|
1194
|
+
setContribCursor((i) => Math.max(0, i - 1));
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
if (key.downArrow) {
|
|
1198
|
+
setContribCursor((i) => Math.min(myContribs.length - 1, i + 1));
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
if (q === "c") {
|
|
1202
|
+
const target = myContribs[contribCursor];
|
|
1203
|
+
if (target && target.contributionHash) {
|
|
1204
|
+
copyToClipboardOSC52(target.contributionHash);
|
|
1205
|
+
setCopyToast(`\u2713 Hash copied (round #${target.sequenceNumber})`);
|
|
1206
|
+
setTimeout(() => setCopyToast(null), 2e3);
|
|
1207
|
+
} else if (target) {
|
|
1208
|
+
setCopyToast("Nothing to copy \u2014 hash is still pending");
|
|
1209
|
+
setTimeout(() => setCopyToast(null), 2e3);
|
|
1210
|
+
}
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1065
1213
|
}
|
|
1066
1214
|
if (q === "r") {
|
|
1067
1215
|
goHome();
|
|
@@ -1190,29 +1338,34 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1190
1338
|
/* @__PURE__ */ jsx7(TabBar, {}),
|
|
1191
1339
|
tab === 0 ? (
|
|
1192
1340
|
// ── Dashboard tab ────────────────────────────────────────────────
|
|
1341
|
+
// CIRCUIT column is sized to fit the longest mainnet name
|
|
1342
|
+
// (`claim-deposit-into-confidential-amount-n4` = 41 chars). Names
|
|
1343
|
+
// longer than that are middle-elided so the disambiguating suffix
|
|
1344
|
+
// (-n1/-n2/-n4) stays visible — previously a hard slice(0,24)
|
|
1345
|
+
// showed every claim variant as `claim-deposit-into-confi..`.
|
|
1193
1346
|
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1194
1347
|
/* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
1195
|
-
" CIRCUIT".padEnd(
|
|
1196
|
-
"TOTAL".padEnd(
|
|
1348
|
+
" CIRCUIT".padEnd(46),
|
|
1349
|
+
"TOTAL".padEnd(8),
|
|
1197
1350
|
"QUEUE".padEnd(8),
|
|
1198
|
-
"STATUS".padEnd(
|
|
1351
|
+
"STATUS".padEnd(14),
|
|
1199
1352
|
"MY CONTRIBUTIONS"
|
|
1200
1353
|
] }),
|
|
1201
|
-
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(
|
|
1354
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(90) }),
|
|
1202
1355
|
tracks.map((t, i) => {
|
|
1203
1356
|
const isSelected = i === selectedIdx;
|
|
1204
1357
|
const canContribute = t.status === "open";
|
|
1205
|
-
const
|
|
1358
|
+
const nameDisplay = elideMiddle(t.circuit_name, 42);
|
|
1206
1359
|
const statusColor = t.status === "open" ? "green" : t.status === "finalized" ? "cyan" : "yellow";
|
|
1207
1360
|
const myContrib = contributed[t.id];
|
|
1208
1361
|
return /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1209
1362
|
/* @__PURE__ */ jsxs7(Text7, { color: isSelected ? "cyan" : canContribute ? void 0 : "gray", children: [
|
|
1210
1363
|
isSelected ? "\u25B6 " : " ",
|
|
1211
|
-
|
|
1212
|
-
String(t.contribution_count).padEnd(
|
|
1364
|
+
nameDisplay.padEnd(44),
|
|
1365
|
+
String(t.contribution_count).padEnd(8),
|
|
1213
1366
|
String(t.queue_depth).padEnd(8)
|
|
1214
1367
|
] }),
|
|
1215
|
-
/* @__PURE__ */ jsx7(Text7, { color: statusColor, children: trackStatusLabel(t.status).padEnd(
|
|
1368
|
+
/* @__PURE__ */ jsx7(Text7, { color: statusColor, children: trackStatusLabel(t.status).padEnd(14) }),
|
|
1216
1369
|
myContrib ? isSelected ? /* @__PURE__ */ jsxs7(Text7, { color: "green", children: [
|
|
1217
1370
|
"\u2713 contributed (round #",
|
|
1218
1371
|
myContrib.sequenceNumber,
|
|
@@ -1237,32 +1390,35 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1237
1390
|
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Switch to Dashboard tab and press Enter on a circuit to contribute." })
|
|
1238
1391
|
] }) : /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1239
1392
|
/* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
1240
|
-
" CIRCUIT".padEnd(
|
|
1241
|
-
"ROUND".padEnd(
|
|
1242
|
-
"
|
|
1243
|
-
"TIME"
|
|
1393
|
+
" CIRCUIT".padEnd(44),
|
|
1394
|
+
"ROUND".padEnd(7),
|
|
1395
|
+
"VERIFIED AT".padEnd(22)
|
|
1244
1396
|
] }),
|
|
1245
|
-
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(
|
|
1246
|
-
myContributions.map((c, i) =>
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
/* @__PURE__ */
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1397
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(90) }),
|
|
1398
|
+
myContributions.map((c, i) => {
|
|
1399
|
+
const isSel = i === contribCursor;
|
|
1400
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1401
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1402
|
+
/* @__PURE__ */ jsx7(Text7, { color: isSel ? "cyan" : "green", children: isSel ? "\u25B6 " : " " }),
|
|
1403
|
+
/* @__PURE__ */ jsx7(Text7, { bold: isSel, children: elideMiddle(c.circuitName, 40).padEnd(42) }),
|
|
1404
|
+
/* @__PURE__ */ jsx7(Text7, { color: "yellow", children: ("#" + c.sequenceNumber).padEnd(7) }),
|
|
1405
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: c.verifiedAt ? new Date(c.verifiedAt).toLocaleString() : "\u2014" })
|
|
1406
|
+
] }),
|
|
1407
|
+
/* @__PURE__ */ jsx7(Box7, { paddingLeft: 4, children: /* @__PURE__ */ jsxs7(Text7, { color: isSel ? "cyan" : "gray", dimColor: !isSel, children: [
|
|
1408
|
+
"hash:",
|
|
1409
|
+
" ",
|
|
1410
|
+
c.contributionHash ? c.contributionHash : "(pending \u2014 verify still in flight)"
|
|
1411
|
+
] }) })
|
|
1412
|
+
] }, i);
|
|
1413
|
+
}),
|
|
1414
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(90) }),
|
|
1415
|
+
copyToast ? /* @__PURE__ */ jsx7(Text7, { color: "green", children: copyToast }) : /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
1260
1416
|
"Total: ",
|
|
1261
1417
|
myContributions.length,
|
|
1262
1418
|
" contribution",
|
|
1263
1419
|
myContributions.length !== 1 ? "s" : "",
|
|
1264
1420
|
" \xB7 ",
|
|
1265
|
-
"Tab
|
|
1421
|
+
"\u2191/\u2193 select \xB7 C copy hash \xB7 Tab switch \xB7 Q quit"
|
|
1266
1422
|
] })
|
|
1267
1423
|
] }) })
|
|
1268
1424
|
)
|