@umbra-privacy/ceremony 0.2.3 → 0.2.6
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 +226 -104
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { render } from "ink";
|
|
5
5
|
|
|
6
6
|
// src/components/App.tsx
|
|
7
|
-
import { useEffect as
|
|
7
|
+
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
8
8
|
import { Box as Box7, Text as Text7, useApp, useInput as useInput2 } from "ink";
|
|
9
9
|
|
|
10
10
|
// src/cleanup.ts
|
|
@@ -90,26 +90,20 @@ var api = {
|
|
|
90
90
|
},
|
|
91
91
|
// Slot polling
|
|
92
92
|
myTurn(ceremonyId2, trackId, token) {
|
|
93
|
-
return request(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
);
|
|
93
|
+
return request(`/api/ceremonies/${ceremonyId2}/tracks/${trackId}/my-turn`, {
|
|
94
|
+
headers: bearer(token)
|
|
95
|
+
});
|
|
97
96
|
},
|
|
98
97
|
// Contribution
|
|
99
98
|
signalUploaded(ceremonyId2, trackId, contributionId, token) {
|
|
100
|
-
return request(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
body: JSON.stringify({ contribution_id: contributionId })
|
|
106
|
-
}
|
|
107
|
-
);
|
|
99
|
+
return request(`/api/ceremonies/${ceremonyId2}/tracks/${trackId}/signal-uploaded`, {
|
|
100
|
+
method: "POST",
|
|
101
|
+
headers: bearer(token),
|
|
102
|
+
body: JSON.stringify({ contribution_id: contributionId })
|
|
103
|
+
});
|
|
108
104
|
},
|
|
109
105
|
getReceipt(ceremonyId2, contributionId) {
|
|
110
|
-
return request(
|
|
111
|
-
`/api/ceremonies/${ceremonyId2}/contributions/${contributionId}/receipt`
|
|
112
|
-
);
|
|
106
|
+
return request(`/api/ceremonies/${ceremonyId2}/contributions/${contributionId}/receipt`);
|
|
113
107
|
},
|
|
114
108
|
// Admin — signed-request flow. Caller supplies the 64-byte Solana keypair
|
|
115
109
|
// bytes (32 secret + 32 public). The helper fetches a one-time challenge,
|
|
@@ -252,6 +246,38 @@ async function recordContribution(trackId, contribution) {
|
|
|
252
246
|
|
|
253
247
|
// src/components/Header.tsx
|
|
254
248
|
import { Box, Text } from "ink";
|
|
249
|
+
|
|
250
|
+
// src/labels.ts
|
|
251
|
+
function ceremonyStatusLabel(status) {
|
|
252
|
+
switch (status) {
|
|
253
|
+
case "open":
|
|
254
|
+
return "open";
|
|
255
|
+
case "initialized":
|
|
256
|
+
return "not yet open";
|
|
257
|
+
case "finalizing":
|
|
258
|
+
return "closing";
|
|
259
|
+
case "completed":
|
|
260
|
+
return "complete";
|
|
261
|
+
default:
|
|
262
|
+
return status;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function trackStatusLabel(status) {
|
|
266
|
+
switch (status) {
|
|
267
|
+
case "open":
|
|
268
|
+
return "open";
|
|
269
|
+
case "initializing":
|
|
270
|
+
return "preparing";
|
|
271
|
+
case "awaiting_beacon":
|
|
272
|
+
return "awaiting beacon";
|
|
273
|
+
case "finalized":
|
|
274
|
+
return "complete";
|
|
275
|
+
default:
|
|
276
|
+
return status;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/components/Header.tsx
|
|
255
281
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
256
282
|
var STATUS_COLOR = {
|
|
257
283
|
open: "green",
|
|
@@ -264,10 +290,10 @@ function Header({ ceremony, subtitle }) {
|
|
|
264
290
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
265
291
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u25C6 Umbra Ceremony TUI" }),
|
|
266
292
|
ceremony && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
267
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "
|
|
293
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
|
|
268
294
|
/* @__PURE__ */ jsx(Text, { bold: true, children: ceremony.name }),
|
|
269
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "
|
|
270
|
-
/* @__PURE__ */ jsx(Text, { color: STATUS_COLOR[ceremony.status] ?? "white", children: ceremony.status }),
|
|
295
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " [" }),
|
|
296
|
+
/* @__PURE__ */ jsx(Text, { color: STATUS_COLOR[ceremony.status] ?? "white", children: ceremonyStatusLabel(ceremony.status) }),
|
|
271
297
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "]" })
|
|
272
298
|
] })
|
|
273
299
|
] }),
|
|
@@ -289,22 +315,61 @@ function Header({ ceremony, subtitle }) {
|
|
|
289
315
|
}
|
|
290
316
|
|
|
291
317
|
// src/components/QueueView.tsx
|
|
292
|
-
import { useEffect, useRef, useState } from "react";
|
|
318
|
+
import { useEffect as useEffect2, useRef, useState as useState2 } from "react";
|
|
293
319
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
294
320
|
import Spinner from "ink-spinner";
|
|
295
|
-
|
|
321
|
+
|
|
322
|
+
// src/hooks/useCyclingMessage.ts
|
|
323
|
+
import { useEffect, useState } from "react";
|
|
324
|
+
function useCyclingMessage(messages, intervalMs = 2500, active = true) {
|
|
325
|
+
const [idx, setIdx] = useState(0);
|
|
326
|
+
useEffect(() => {
|
|
327
|
+
if (!active || messages.length <= 1) return;
|
|
328
|
+
const t = setInterval(() => setIdx((i) => (i + 1) % messages.length), intervalMs);
|
|
329
|
+
return () => clearInterval(t);
|
|
330
|
+
}, [active, intervalMs, messages.length]);
|
|
331
|
+
return messages[idx] ?? messages[0] ?? "";
|
|
332
|
+
}
|
|
333
|
+
var COMPUTING_MESSAGES = [
|
|
334
|
+
"summoning fresh entropy",
|
|
335
|
+
"shuffling response bytes",
|
|
336
|
+
"tickling bn128 pairings",
|
|
337
|
+
"applying tau locally",
|
|
338
|
+
"folding contribution proof",
|
|
339
|
+
"blinding the trapdoor",
|
|
340
|
+
"sealing the bellman response"
|
|
341
|
+
];
|
|
342
|
+
var VERIFYING_MESSAGES = [
|
|
343
|
+
"worker pulling your response from S3",
|
|
344
|
+
"importing bellman contribution into a new zkey",
|
|
345
|
+
"checking the contribution's pairing proof",
|
|
346
|
+
"writing the next zkey",
|
|
347
|
+
"uploading the new zkey",
|
|
348
|
+
"anchoring contribution hash in the audit chain",
|
|
349
|
+
"almost there \u2014 finalising your receipt"
|
|
350
|
+
];
|
|
351
|
+
var EXPORTING_MESSAGES = [
|
|
352
|
+
"preparing your challenge file",
|
|
353
|
+
"extracting bellman params from the current zkey",
|
|
354
|
+
"wrapping the zkey for handoff",
|
|
355
|
+
"stamping a sha256 over the challenge bundle",
|
|
356
|
+
"presigning your download URL"
|
|
357
|
+
];
|
|
358
|
+
|
|
359
|
+
// src/components/QueueView.tsx
|
|
360
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
296
361
|
var POLL_FAST_MS = 5e3;
|
|
297
362
|
var POLL_SLOW_MS = 15e3;
|
|
298
363
|
function QueueView({ ceremonyId: ceremonyId2, trackId, token, onReady, onError }) {
|
|
299
|
-
const [status, setStatus] =
|
|
300
|
-
const [pollErr, setPollErr] =
|
|
301
|
-
const [tick, setTick] =
|
|
364
|
+
const [status, setStatus] = useState2(null);
|
|
365
|
+
const [pollErr, setPollErr] = useState2(null);
|
|
366
|
+
const [tick, setTick] = useState2(0);
|
|
302
367
|
const timeoutRef = useRef(null);
|
|
303
|
-
|
|
368
|
+
useEffect2(() => {
|
|
304
369
|
const id = setInterval(() => setTick((t) => (t + 1) % 4), 500);
|
|
305
370
|
return () => clearInterval(id);
|
|
306
371
|
}, []);
|
|
307
|
-
|
|
372
|
+
useEffect2(() => {
|
|
308
373
|
let cancelled = false;
|
|
309
374
|
async function poll() {
|
|
310
375
|
try {
|
|
@@ -342,7 +407,10 @@ function QueueView({ ceremonyId: ceremonyId2, trackId, token, onReady, onError }
|
|
|
342
407
|
] });
|
|
343
408
|
}
|
|
344
409
|
const waitMins = Math.ceil((status.estimated_wait_secs ?? 0) / 60);
|
|
345
|
-
const expiresAt = status.slot_expires_at ? new Date(status.slot_expires_at).toLocaleTimeString([], {
|
|
410
|
+
const expiresAt = status.slot_expires_at ? new Date(status.slot_expires_at).toLocaleTimeString([], {
|
|
411
|
+
hour: "2-digit",
|
|
412
|
+
minute: "2-digit"
|
|
413
|
+
}) : null;
|
|
346
414
|
const fastPoll = status.queue_position <= 2;
|
|
347
415
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", gap: 1, children: [
|
|
348
416
|
/* @__PURE__ */ jsxs2(Box2, { gap: 2, children: [
|
|
@@ -366,17 +434,13 @@ function QueueView({ ceremonyId: ceremonyId2, trackId, token, onReady, onError }
|
|
|
366
434
|
] })
|
|
367
435
|
] })
|
|
368
436
|
] }),
|
|
369
|
-
status.status === "exporting" || status.status === "your_turn" || status.status === "ready_to_download" ? /* @__PURE__ */ jsx2(
|
|
437
|
+
status.status === "exporting" || status.status === "your_turn" || status.status === "ready_to_download" ? /* @__PURE__ */ jsx2(ExportingMessage, { status: status.status }) : status.active_since ? /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
370
438
|
"Another contributor is active",
|
|
371
439
|
expiresAt ? ` \xB7 slot expires at ${expiresAt}` : ""
|
|
372
440
|
] }) }) : status.queue_position > 1 ? (
|
|
373
441
|
// Slot is idle but people are ahead — they joined and left without releasing.
|
|
374
442
|
// timeout_watchdog will clear each stale slot after contribution_timeout_secs.
|
|
375
|
-
/* @__PURE__ */
|
|
376
|
-
"Slot is idle \u2014 waiting for positions ahead to respond or time out",
|
|
377
|
-
" ",
|
|
378
|
-
"(up to ~5 min each)"
|
|
379
|
-
] })
|
|
443
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Slot is idle \u2014 waiting for positions ahead to respond or time out (up to ~5 min each)" })
|
|
380
444
|
) : (
|
|
381
445
|
// Position 1, slot idle — advance_queue should fire shortly.
|
|
382
446
|
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Slot is idle \u2014 your turn is being prepared..." })
|
|
@@ -394,9 +458,21 @@ function QueueView({ ceremonyId: ceremonyId2, trackId, token, onReady, onError }
|
|
|
394
458
|
] })
|
|
395
459
|
] });
|
|
396
460
|
}
|
|
461
|
+
function ExportingMessage({ status }) {
|
|
462
|
+
const exportingMsg = useCyclingMessage(EXPORTING_MESSAGES, 2500, status === "exporting");
|
|
463
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: status === "exporting" ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
464
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "green", children: [
|
|
465
|
+
/* @__PURE__ */ jsx2(Spinner, { type: "dots" }),
|
|
466
|
+
" ",
|
|
467
|
+
exportingMsg,
|
|
468
|
+
"\u2026"
|
|
469
|
+
] }),
|
|
470
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Your slot is active. The worker is preparing the challenge file just for you." })
|
|
471
|
+
] }) : /* @__PURE__ */ jsx2(Text2, { color: "green", children: "Challenge ready \u2014 loading contribution flow\u2026" }) });
|
|
472
|
+
}
|
|
397
473
|
|
|
398
474
|
// src/components/EntropyCollector.tsx
|
|
399
|
-
import { useRef as useRef2, useState as
|
|
475
|
+
import { useRef as useRef2, useState as useState3 } from "react";
|
|
400
476
|
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
401
477
|
|
|
402
478
|
// src/entropy.ts
|
|
@@ -417,8 +493,8 @@ function buildEntropyFromKeystrokes(chars, timingsNs) {
|
|
|
417
493
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
418
494
|
var TARGET = 20;
|
|
419
495
|
function EntropyCollector({ onComplete, onError }) {
|
|
420
|
-
const [count, setCount] =
|
|
421
|
-
const [done, setDone] =
|
|
496
|
+
const [count, setCount] = useState3(0);
|
|
497
|
+
const [done, setDone] = useState3(false);
|
|
422
498
|
const charsRef = useRef2([]);
|
|
423
499
|
const timingsRef = useRef2([]);
|
|
424
500
|
const lastRef = useRef2(process.hrtime.bigint());
|
|
@@ -460,7 +536,7 @@ function EntropyCollector({ onComplete, onError }) {
|
|
|
460
536
|
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "Type anything to generate entropy:" }),
|
|
461
537
|
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Keystroke timing (nanosecond intervals) is the randomness source." }),
|
|
462
538
|
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, children: [
|
|
463
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "
|
|
539
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " > " }),
|
|
464
540
|
/* @__PURE__ */ jsx3(Text3, { color: "green", children: stars }),
|
|
465
541
|
/* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "\u2588" })
|
|
466
542
|
] }),
|
|
@@ -475,7 +551,7 @@ function EntropyCollector({ onComplete, onError }) {
|
|
|
475
551
|
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
476
552
|
"/",
|
|
477
553
|
TARGET,
|
|
478
|
-
" keystrokes
|
|
554
|
+
" keystrokes (",
|
|
479
555
|
pct,
|
|
480
556
|
"%)"
|
|
481
557
|
] })
|
|
@@ -486,7 +562,7 @@ function EntropyCollector({ onComplete, onError }) {
|
|
|
486
562
|
}
|
|
487
563
|
|
|
488
564
|
// src/components/ContributeFlow.tsx
|
|
489
|
-
import { useEffect as
|
|
565
|
+
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
490
566
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
491
567
|
import Spinner2 from "ink-spinner";
|
|
492
568
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -525,8 +601,8 @@ var STEP_INDEX = {
|
|
|
525
601
|
};
|
|
526
602
|
function ContributeFlow(props) {
|
|
527
603
|
const { ceremonyId: ceremonyId2, trackId, token, slotStatus, entropy, displayName: displayName2 } = props;
|
|
528
|
-
const [step, setStep] =
|
|
529
|
-
|
|
604
|
+
const [step, setStep] = useState4({ name: "downloading", bytesReceived: 0, total: null });
|
|
605
|
+
useEffect4(() => {
|
|
530
606
|
let cancelled = false;
|
|
531
607
|
const challengePath = join4(tmpdir2(), `ceremony-challenge-${Date.now()}.mpcparams`);
|
|
532
608
|
let responsePath = null;
|
|
@@ -572,9 +648,11 @@ function ContributeFlow(props) {
|
|
|
572
648
|
await api.signalUploaded(ceremonyId2, trackId, slotStatus.contribution_id, token);
|
|
573
649
|
if (cancelled) return;
|
|
574
650
|
setStep({ name: "verifying", attempt: 1 });
|
|
651
|
+
const TOTAL_POLLS = 100;
|
|
652
|
+
const FAST_POLLS = 10;
|
|
575
653
|
let receipt = null;
|
|
576
654
|
let lastErr = null;
|
|
577
|
-
for (let i = 0; i <
|
|
655
|
+
for (let i = 0; i < TOTAL_POLLS; i++) {
|
|
578
656
|
if (cancelled) return;
|
|
579
657
|
setStep({ name: "verifying", attempt: i + 1 });
|
|
580
658
|
try {
|
|
@@ -583,7 +661,8 @@ function ContributeFlow(props) {
|
|
|
583
661
|
break;
|
|
584
662
|
} catch (err) {
|
|
585
663
|
lastErr = err instanceof Error ? err : new Error(String(err));
|
|
586
|
-
|
|
664
|
+
const delay = i < FAST_POLLS ? 3e3 : 1e4;
|
|
665
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
587
666
|
}
|
|
588
667
|
}
|
|
589
668
|
if (cancelled) return;
|
|
@@ -604,6 +683,8 @@ function ContributeFlow(props) {
|
|
|
604
683
|
};
|
|
605
684
|
}, []);
|
|
606
685
|
const currentIdx = STEP_INDEX[step.name] ?? 0;
|
|
686
|
+
const computingMsg = useCyclingMessage(COMPUTING_MESSAGES, 2500, step.name === "computing");
|
|
687
|
+
const verifyingMsg = useCyclingMessage(VERIFYING_MESSAGES, 3e3, step.name === "verifying");
|
|
607
688
|
return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", gap: 1, children: STEP_LABELS.map((label, i) => {
|
|
608
689
|
const isDone = i < currentIdx;
|
|
609
690
|
const isActive = i === currentIdx;
|
|
@@ -618,7 +699,8 @@ function ContributeFlow(props) {
|
|
|
618
699
|
const filled = Math.round(pct / 5);
|
|
619
700
|
const bar = "\u2588".repeat(filled) + "\u2591".repeat(20 - filled);
|
|
620
701
|
detail = /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
621
|
-
"
|
|
702
|
+
" ",
|
|
703
|
+
"[",
|
|
622
704
|
bar,
|
|
623
705
|
"] ",
|
|
624
706
|
pct,
|
|
@@ -626,10 +708,23 @@ function ContributeFlow(props) {
|
|
|
626
708
|
] });
|
|
627
709
|
}
|
|
628
710
|
if (isActive && step.name === "verifying") {
|
|
711
|
+
const elapsed = step.attempt <= 10 ? step.attempt * 3 : 10 * 3 + (step.attempt - 10) * 10;
|
|
712
|
+
const mins = Math.floor(elapsed / 60);
|
|
713
|
+
const secs = elapsed % 60;
|
|
714
|
+
const time = mins > 0 ? `${mins}m ${secs.toString().padStart(2, "0")}s` : `${secs}s`;
|
|
629
715
|
detail = /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
630
|
-
"
|
|
631
|
-
|
|
632
|
-
"
|
|
716
|
+
" ",
|
|
717
|
+
verifyingMsg,
|
|
718
|
+
"\u2026 (",
|
|
719
|
+
time,
|
|
720
|
+
" elapsed)"
|
|
721
|
+
] });
|
|
722
|
+
}
|
|
723
|
+
if (isActive && step.name === "computing") {
|
|
724
|
+
detail = /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
725
|
+
" ",
|
|
726
|
+
computingMsg,
|
|
727
|
+
"\u2026"
|
|
633
728
|
] });
|
|
634
729
|
}
|
|
635
730
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
@@ -644,8 +739,7 @@ function ContributeFlow(props) {
|
|
|
644
739
|
children: [
|
|
645
740
|
label.charAt(0).toUpperCase() + label.slice(1),
|
|
646
741
|
" ",
|
|
647
|
-
isActive && label === "computing" && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "(entropy stays local)" })
|
|
648
|
-
isActive && label === "uploading" && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "(~544 bytes)" })
|
|
742
|
+
isActive && label === "computing" && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "(entropy stays local)" })
|
|
649
743
|
]
|
|
650
744
|
}
|
|
651
745
|
)
|
|
@@ -662,25 +756,25 @@ function Attestation({ contribution }) {
|
|
|
662
756
|
const hashShort = contribution.contributionHash ? `${contribution.contributionHash.slice(0, 16)}...${contribution.contributionHash.slice(-8)}` : "(pending verification)";
|
|
663
757
|
const verifiedAt = contribution.verifiedAt ? new Date(contribution.verifiedAt).toLocaleString() : null;
|
|
664
758
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", gap: 1, children: [
|
|
665
|
-
/* @__PURE__ */ jsx5(Text5, { bold: true, color: "green", children: "\u2713
|
|
759
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, color: "green", children: "\u2713 Contribution verified!" }),
|
|
666
760
|
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", gap: 0, children: [
|
|
667
761
|
/* @__PURE__ */ jsxs5(Box5, { gap: 2, children: [
|
|
668
762
|
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Circuit" }),
|
|
669
763
|
/* @__PURE__ */ jsx5(Text5, { bold: true, children: contribution.circuitName })
|
|
670
764
|
] }),
|
|
671
765
|
/* @__PURE__ */ jsxs5(Box5, { gap: 2, children: [
|
|
672
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Round
|
|
766
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Round " }),
|
|
673
767
|
/* @__PURE__ */ jsxs5(Text5, { bold: true, children: [
|
|
674
768
|
"#",
|
|
675
769
|
contribution.sequenceNumber
|
|
676
770
|
] })
|
|
677
771
|
] }),
|
|
678
772
|
/* @__PURE__ */ jsxs5(Box5, { gap: 2, children: [
|
|
679
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Hash
|
|
773
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Hash " }),
|
|
680
774
|
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: hashShort })
|
|
681
775
|
] }),
|
|
682
776
|
verifiedAt && /* @__PURE__ */ jsxs5(Box5, { gap: 2, children: [
|
|
683
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Time
|
|
777
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Time " }),
|
|
684
778
|
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: verifiedAt })
|
|
685
779
|
] })
|
|
686
780
|
] }),
|
|
@@ -721,19 +815,19 @@ var NAME_MAX_LEN = 100;
|
|
|
721
815
|
var NAME_VALID_RE = /^[\p{L}\p{N} _.\-]*$/u;
|
|
722
816
|
function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName }) {
|
|
723
817
|
const { exit } = useApp();
|
|
724
|
-
const [activeCeremonyId, setActiveCeremonyId] =
|
|
725
|
-
const [displayName2, setDisplayName] =
|
|
726
|
-
const [nameSet, setNameSet] =
|
|
727
|
-
const [screen, setScreen] =
|
|
818
|
+
const [activeCeremonyId, setActiveCeremonyId] = useState5(initialCeremonyId);
|
|
819
|
+
const [displayName2, setDisplayName] = useState5(initialDisplayName ?? "anonymous");
|
|
820
|
+
const [nameSet, setNameSet] = useState5(initialDisplayName !== void 0);
|
|
821
|
+
const [screen, setScreen] = useState5(
|
|
728
822
|
initialDisplayName === void 0 ? { name: "name-input", value: "" } : initialCeremonyId ? { name: "loading" } : { name: "ceremony-picker", ceremonies: [], loading: true }
|
|
729
823
|
);
|
|
730
|
-
const [ceremony, setCeremony] =
|
|
731
|
-
const [session, setSession] =
|
|
732
|
-
const [contributed, setContributed] =
|
|
733
|
-
const [selectedIdx, setSelectedIdx] =
|
|
734
|
-
const [tab, setTab] =
|
|
735
|
-
const [showInfo, setShowInfo] =
|
|
736
|
-
|
|
824
|
+
const [ceremony, setCeremony] = useState5(null);
|
|
825
|
+
const [session, setSession] = useState5(null);
|
|
826
|
+
const [contributed, setContributed] = useState5({});
|
|
827
|
+
const [selectedIdx, setSelectedIdx] = useState5(0);
|
|
828
|
+
const [tab, setTab] = useState5(0);
|
|
829
|
+
const [showInfo, setShowInfo] = useState5(false);
|
|
830
|
+
useEffect5(() => {
|
|
737
831
|
if (!nameSet) return;
|
|
738
832
|
if (!initialCeremonyId) {
|
|
739
833
|
loadCeremonies();
|
|
@@ -747,7 +841,11 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
747
841
|
setScreen({ name: "ceremony-picker", ceremonies, loading: false });
|
|
748
842
|
setSelectedIdx(0);
|
|
749
843
|
} catch (e) {
|
|
750
|
-
setScreen({
|
|
844
|
+
setScreen({
|
|
845
|
+
name: "error",
|
|
846
|
+
message: e.message ?? "Failed to load ceremonies.",
|
|
847
|
+
recoverable: false
|
|
848
|
+
});
|
|
751
849
|
}
|
|
752
850
|
}
|
|
753
851
|
async function boot(cId) {
|
|
@@ -769,7 +867,11 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
769
867
|
try {
|
|
770
868
|
const { tracks } = await api.listTracks(cId, s.session_token);
|
|
771
869
|
if (tracks.length === 0) {
|
|
772
|
-
setScreen({
|
|
870
|
+
setScreen({
|
|
871
|
+
name: "error",
|
|
872
|
+
message: "No tracks found in this ceremony.",
|
|
873
|
+
recoverable: false
|
|
874
|
+
});
|
|
773
875
|
return;
|
|
774
876
|
}
|
|
775
877
|
setSelectedIdx(0);
|
|
@@ -782,17 +884,29 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
782
884
|
setSession(fresh);
|
|
783
885
|
const { tracks } = await api.listTracks(cId, fresh.session_token);
|
|
784
886
|
if (tracks.length === 0) {
|
|
785
|
-
setScreen({
|
|
887
|
+
setScreen({
|
|
888
|
+
name: "error",
|
|
889
|
+
message: "No tracks found in this ceremony.",
|
|
890
|
+
recoverable: false
|
|
891
|
+
});
|
|
786
892
|
return;
|
|
787
893
|
}
|
|
788
894
|
setSelectedIdx(0);
|
|
789
895
|
setScreen({ name: "tracks", tracks });
|
|
790
896
|
} catch (e2) {
|
|
791
|
-
setScreen({
|
|
897
|
+
setScreen({
|
|
898
|
+
name: "error",
|
|
899
|
+
message: e2.message ?? "Failed to load tracks.",
|
|
900
|
+
recoverable: false
|
|
901
|
+
});
|
|
792
902
|
}
|
|
793
903
|
return;
|
|
794
904
|
}
|
|
795
|
-
setScreen({
|
|
905
|
+
setScreen({
|
|
906
|
+
name: "error",
|
|
907
|
+
message: e.message ?? "Failed to load tracks.",
|
|
908
|
+
recoverable: false
|
|
909
|
+
});
|
|
796
910
|
}
|
|
797
911
|
}
|
|
798
912
|
function commitName(raw) {
|
|
@@ -805,10 +919,9 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
805
919
|
);
|
|
806
920
|
}
|
|
807
921
|
function randomAnonName() {
|
|
808
|
-
const hex = Array.from(
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
).join("");
|
|
922
|
+
const hex = Array.from({ length: 6 }, () => Math.floor(Math.random() * 16).toString(16)).join(
|
|
923
|
+
""
|
|
924
|
+
);
|
|
812
925
|
return `anon-${hex}`;
|
|
813
926
|
}
|
|
814
927
|
async function joinTrack(track) {
|
|
@@ -818,7 +931,11 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
818
931
|
await api.joinQueue(activeCeremonyId, track.id, session.session_token);
|
|
819
932
|
} catch (e) {
|
|
820
933
|
if (e.code !== "ALREADY_IN_QUEUE") {
|
|
821
|
-
setScreen({
|
|
934
|
+
setScreen({
|
|
935
|
+
name: "error",
|
|
936
|
+
message: e.message ?? "Failed to join queue.",
|
|
937
|
+
recoverable: true
|
|
938
|
+
});
|
|
822
939
|
return;
|
|
823
940
|
}
|
|
824
941
|
}
|
|
@@ -833,7 +950,7 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
833
950
|
setScreen({ name: "ceremony-picker", ceremonies: [], loading: true });
|
|
834
951
|
loadCeremonies();
|
|
835
952
|
}
|
|
836
|
-
|
|
953
|
+
useEffect5(() => {
|
|
837
954
|
if (screen.name === "queue" && session) {
|
|
838
955
|
const { trackId } = screen;
|
|
839
956
|
setQueueCleanup(() => {
|
|
@@ -990,6 +1107,7 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
990
1107
|
const isSelected = i === selectedIdx;
|
|
991
1108
|
const isOpen = c.status === "open";
|
|
992
1109
|
const statusColor = c.status === "open" ? "green" : c.status === "completed" ? "cyan" : "yellow";
|
|
1110
|
+
const statusLabel = ceremonyStatusLabel(c.status);
|
|
993
1111
|
return /* @__PURE__ */ jsxs7(Box7, { gap: 2, children: [
|
|
994
1112
|
/* @__PURE__ */ jsxs7(Text7, { color: isSelected ? "cyan" : isOpen ? void 0 : "gray", children: [
|
|
995
1113
|
isSelected ? "\u25B6 " : " ",
|
|
@@ -997,7 +1115,7 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
997
1115
|
] }),
|
|
998
1116
|
/* @__PURE__ */ jsxs7(Text7, { color: statusColor, children: [
|
|
999
1117
|
"[",
|
|
1000
|
-
|
|
1118
|
+
statusLabel,
|
|
1001
1119
|
"]"
|
|
1002
1120
|
] }),
|
|
1003
1121
|
/* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
@@ -1017,20 +1135,23 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1017
1135
|
if (!c) return null;
|
|
1018
1136
|
if (c.status === "initialized") {
|
|
1019
1137
|
return /* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
|
|
1020
|
-
"
|
|
1021
|
-
"
|
|
1022
|
-
" then open ",
|
|
1023
|
-
"<id>",
|
|
1024
|
-
" to start contributions."
|
|
1138
|
+
" ",
|
|
1139
|
+
"This ceremony is not yet open for contributions. The Umbra team is still preparing the circuits \u2014 please check back shortly."
|
|
1025
1140
|
] });
|
|
1026
1141
|
}
|
|
1027
1142
|
if (c.status === "finalizing") {
|
|
1028
|
-
return /* @__PURE__ */
|
|
1143
|
+
return /* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
|
|
1144
|
+
" ",
|
|
1145
|
+
"Contributions are closed. The ceremony is computing the final verification key \u2014 no further contributions can be added."
|
|
1146
|
+
] });
|
|
1029
1147
|
}
|
|
1030
1148
|
if (c.status === "completed") {
|
|
1031
|
-
return /* @__PURE__ */
|
|
1149
|
+
return /* @__PURE__ */ jsxs7(Text7, { color: "cyan", children: [
|
|
1150
|
+
" ",
|
|
1151
|
+
"Ceremony complete. The verification keys are finalised \u2014 thank you to everyone who contributed."
|
|
1152
|
+
] });
|
|
1032
1153
|
}
|
|
1033
|
-
return /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u2191/\u2193 select
|
|
1154
|
+
return /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u2191/\u2193 select \xB7 Enter join \xB7 Q quit" });
|
|
1034
1155
|
})()
|
|
1035
1156
|
] })
|
|
1036
1157
|
] });
|
|
@@ -1046,7 +1167,7 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1046
1167
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1047
1168
|
/* @__PURE__ */ jsx7(Header, { ceremony }),
|
|
1048
1169
|
/* @__PURE__ */ jsxs7(Text7, { color: "red", bold: true, children: [
|
|
1049
|
-
"\u2717
|
|
1170
|
+
"\u2717 ",
|
|
1050
1171
|
screen.message
|
|
1051
1172
|
] }),
|
|
1052
1173
|
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: backHint })
|
|
@@ -1055,12 +1176,14 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1055
1176
|
if (screen.name === "tracks") {
|
|
1056
1177
|
const { tracks } = screen;
|
|
1057
1178
|
const openTracks = tracks.filter((t) => t.status === "open");
|
|
1058
|
-
const myContributions = Object.values(contributed).filter(
|
|
1179
|
+
const myContributions = Object.values(contributed).filter(
|
|
1180
|
+
(c) => c.ceremonyId === activeCeremonyId
|
|
1181
|
+
);
|
|
1059
1182
|
const TabBar = () => /* @__PURE__ */ jsxs7(Box7, { gap: 1, marginBottom: 1, children: [
|
|
1060
1183
|
/* @__PURE__ */ jsx7(Text7, { bold: tab === 0, color: tab === 0 ? "cyan" : void 0, dimColor: tab !== 0, children: tab === 0 ? "[ Dashboard ]" : " Dashboard " }),
|
|
1061
1184
|
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "|" }),
|
|
1062
1185
|
/* @__PURE__ */ jsx7(Text7, { bold: tab === 1, color: tab === 1 ? "cyan" : void 0, dimColor: tab !== 1, children: tab === 1 ? `[ My Contributions (${myContributions.length}) ]` : ` My Contributions (${myContributions.length}) ` }),
|
|
1063
|
-
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "
|
|
1186
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " Tab to switch" })
|
|
1064
1187
|
] });
|
|
1065
1188
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1066
1189
|
/* @__PURE__ */ jsx7(Header, { ceremony }),
|
|
@@ -1072,10 +1195,10 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1072
1195
|
" CIRCUIT".padEnd(30),
|
|
1073
1196
|
"TOTAL".padEnd(10),
|
|
1074
1197
|
"QUEUE".padEnd(8),
|
|
1075
|
-
"STATUS".padEnd(
|
|
1198
|
+
"STATUS".padEnd(16),
|
|
1076
1199
|
"MY CONTRIBUTIONS"
|
|
1077
1200
|
] }),
|
|
1078
|
-
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(
|
|
1201
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(70) }),
|
|
1079
1202
|
tracks.map((t, i) => {
|
|
1080
1203
|
const isSelected = i === selectedIdx;
|
|
1081
1204
|
const canContribute = t.status === "open";
|
|
@@ -1089,21 +1212,21 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1089
1212
|
String(t.contribution_count).padEnd(10),
|
|
1090
1213
|
String(t.queue_depth).padEnd(8)
|
|
1091
1214
|
] }),
|
|
1092
|
-
/* @__PURE__ */ jsx7(Text7, { color: statusColor, children: t.status.padEnd(
|
|
1215
|
+
/* @__PURE__ */ jsx7(Text7, { color: statusColor, children: trackStatusLabel(t.status).padEnd(16) }),
|
|
1093
1216
|
myContrib ? isSelected ? /* @__PURE__ */ jsxs7(Text7, { color: "green", children: [
|
|
1094
1217
|
"\u2713 contributed (round #",
|
|
1095
1218
|
myContrib.sequenceNumber,
|
|
1096
|
-
")
|
|
1219
|
+
") \u2014 already done"
|
|
1097
1220
|
] }) : /* @__PURE__ */ jsxs7(Text7, { color: "green", children: [
|
|
1098
|
-
"\u2713 contributed
|
|
1221
|
+
"\u2713 contributed (round #",
|
|
1099
1222
|
myContrib.sequenceNumber,
|
|
1100
1223
|
")"
|
|
1101
1224
|
] }) : canContribute ? isSelected ? /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: "\u2190 Enter to contribute" }) : /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "not contributed" }) : /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u2014" })
|
|
1102
1225
|
] }, t.id);
|
|
1103
1226
|
}),
|
|
1104
|
-
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(
|
|
1105
|
-
openTracks.length === 0 ? /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: "No open
|
|
1106
|
-
"\u2191/\u2193 select
|
|
1227
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(70) }),
|
|
1228
|
+
openTracks.length === 0 ? /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: "No circuits are open for contributions right now \u2014 the ceremony may be closing or already complete." }) : /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
1229
|
+
"\u2191/\u2193 select \xB7 Enter contribute \xB7 R refresh \xB7 Q quit",
|
|
1107
1230
|
!initialCeremonyId ? " \xB7 \u232B back to ceremony list" : ""
|
|
1108
1231
|
] })
|
|
1109
1232
|
] })
|
|
@@ -1122,7 +1245,7 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1122
1245
|
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(68) }),
|
|
1123
1246
|
myContributions.map((c, i) => /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1124
1247
|
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1125
|
-
/* @__PURE__ */ jsx7(Text7, { color: "green", children: "
|
|
1248
|
+
/* @__PURE__ */ jsx7(Text7, { color: "green", children: " \u2713 " }),
|
|
1126
1249
|
/* @__PURE__ */ jsx7(Text7, { bold: true, children: c.circuitName.padEnd(24) }),
|
|
1127
1250
|
/* @__PURE__ */ jsx7(Text7, { color: "yellow", children: "#" + c.sequenceNumber + " " }),
|
|
1128
1251
|
/* @__PURE__ */ jsx7(Text7, { color: "cyan", children: c.contributionHash ? c.contributionHash.slice(0, 16) + "..." : "(pending)" })
|
|
@@ -1139,7 +1262,7 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1139
1262
|
" contribution",
|
|
1140
1263
|
myContributions.length !== 1 ? "s" : "",
|
|
1141
1264
|
" \xB7 ",
|
|
1142
|
-
"Tab to switch
|
|
1265
|
+
"Tab to switch \xB7 Q to quit"
|
|
1143
1266
|
] })
|
|
1144
1267
|
] }) })
|
|
1145
1268
|
)
|
|
@@ -1157,7 +1280,7 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1157
1280
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1158
1281
|
/* @__PURE__ */ jsx7(Header, { ceremony, subtitle: `Circuit: ${circuitName}` }),
|
|
1159
1282
|
priorContrib && /* @__PURE__ */ jsx7(Box7, { marginBottom: 1, paddingX: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
|
|
1160
|
-
"\u26A0
|
|
1283
|
+
"\u26A0 You already contributed to this circuit (round #",
|
|
1161
1284
|
priorContrib.sequenceNumber,
|
|
1162
1285
|
").",
|
|
1163
1286
|
" ",
|
|
@@ -1173,7 +1296,7 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1173
1296
|
onError: (e) => setScreen({ name: "error", message: e.message, recoverable: true })
|
|
1174
1297
|
}
|
|
1175
1298
|
),
|
|
1176
|
-
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u232B Backspace to go back
|
|
1299
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u232B Backspace to go back \xB7 Q to quit" })
|
|
1177
1300
|
] });
|
|
1178
1301
|
}
|
|
1179
1302
|
if (screen.name === "entropy") {
|
|
@@ -1225,7 +1348,7 @@ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName })
|
|
|
1225
1348
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1226
1349
|
/* @__PURE__ */ jsx7(Header, { ceremony }),
|
|
1227
1350
|
/* @__PURE__ */ jsx7(Attestation, { contribution }),
|
|
1228
|
-
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u232B Backspace / B = contribute to another circuit
|
|
1351
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u232B Backspace / B = contribute to another circuit \xB7 Q to quit" }) })
|
|
1229
1352
|
] });
|
|
1230
1353
|
}
|
|
1231
1354
|
return null;
|
|
@@ -1256,10 +1379,9 @@ process.on("SIGTERM", () => {
|
|
|
1256
1379
|
process.exit(0);
|
|
1257
1380
|
});
|
|
1258
1381
|
});
|
|
1259
|
-
var { waitUntilExit } = render(
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
);
|
|
1382
|
+
var { waitUntilExit } = render(/* @__PURE__ */ jsx8(App, { ceremonyId, displayName }), {
|
|
1383
|
+
exitOnCtrlC: false
|
|
1384
|
+
});
|
|
1263
1385
|
await waitUntilExit();
|
|
1264
1386
|
restoreScreen();
|
|
1265
1387
|
process.exit(0);
|