@bcts/frost-hubert 1.0.0-alpha.22 → 1.0.0-beta.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.
Files changed (174) hide show
  1. package/dist/bin/frost.cjs +347 -75
  2. package/dist/bin/frost.cjs.map +1 -1
  3. package/dist/bin/frost.mjs +347 -75
  4. package/dist/bin/frost.mjs.map +1 -1
  5. package/dist/busy-DkM2jAIZ.mjs +27 -0
  6. package/dist/busy-DkM2jAIZ.mjs.map +1 -0
  7. package/dist/busy-EZU7EKr6.cjs +38 -0
  8. package/dist/busy-EZU7EKr6.cjs.map +1 -0
  9. package/dist/{chunk-uaV2rQ02.cjs → chunk-CZWwpsFl.cjs} +22 -32
  10. package/dist/{chunk-ClPoSABd.mjs → chunk-CjcI7cDX.mjs} +6 -12
  11. package/dist/cmd/index.cjs +46 -43
  12. package/dist/cmd/index.d.cts +2 -4
  13. package/dist/cmd/index.d.mts +2 -4
  14. package/dist/cmd/index.mjs +7 -6
  15. package/dist/cmd-Bw9_i2_f.cjs +130 -0
  16. package/dist/cmd-Bw9_i2_f.cjs.map +1 -0
  17. package/dist/cmd-CS1uJtuD.mjs +113 -0
  18. package/dist/cmd-CS1uJtuD.mjs.map +1 -0
  19. package/dist/common-CvH6dFvQ.mjs +282 -0
  20. package/dist/common-CvH6dFvQ.mjs.map +1 -0
  21. package/dist/common-DUWvtc08.mjs +96 -0
  22. package/dist/common-DUWvtc08.mjs.map +1 -0
  23. package/dist/common-lKP5EzHy.cjs +372 -0
  24. package/dist/common-lKP5EzHy.cjs.map +1 -0
  25. package/dist/common-lThIvJmZ.cjs +114 -0
  26. package/dist/common-lThIvJmZ.cjs.map +1 -0
  27. package/dist/dkg/index.cjs +245 -7
  28. package/dist/dkg/index.cjs.map +1 -0
  29. package/dist/dkg/index.d.cts +2 -2
  30. package/dist/dkg/index.d.mts +2 -2
  31. package/dist/dkg/index.mjs +238 -2
  32. package/dist/dkg/index.mjs.map +1 -0
  33. package/dist/finalize-BRgJK-Xv.cjs +402 -0
  34. package/dist/finalize-BRgJK-Xv.cjs.map +1 -0
  35. package/dist/finalize-BfLgzn8f.cjs +303 -0
  36. package/dist/finalize-BfLgzn8f.cjs.map +1 -0
  37. package/dist/finalize-CNTDj6aS.mjs +389 -0
  38. package/dist/finalize-CNTDj6aS.mjs.map +1 -0
  39. package/dist/finalize-EC3ikHQq.mjs +252 -0
  40. package/dist/finalize-EC3ikHQq.mjs.map +1 -0
  41. package/dist/finalize-IA01t_Qq.mjs +290 -0
  42. package/dist/finalize-IA01t_Qq.mjs.map +1 -0
  43. package/dist/finalize-UPyI1yb1.cjs +265 -0
  44. package/dist/finalize-UPyI1yb1.cjs.map +1 -0
  45. package/dist/frost/index.cjs +8 -9
  46. package/dist/frost/index.cjs.map +1 -1
  47. package/dist/frost/index.mjs +2 -3
  48. package/dist/frost/index.mjs.map +1 -1
  49. package/dist/{group-invite-Dz1Jmiky.d.cts → index-B3c-80VS.d.cts} +25 -2
  50. package/dist/index-B3c-80VS.d.cts.map +1 -0
  51. package/dist/{index-CcvTi5EA.d.cts → index-BgbSGpxn.d.mts} +102 -80
  52. package/dist/index-BgbSGpxn.d.mts.map +1 -0
  53. package/dist/{registry-impl-CE76sTXQ.d.cts → index-C8QeHNwa.d.cts} +46 -2
  54. package/dist/index-C8QeHNwa.d.cts.map +1 -0
  55. package/dist/{group-invite-Wk9CIbHL.d.mts → index-D3QTWkEm.d.mts} +25 -2
  56. package/dist/index-D3QTWkEm.d.mts.map +1 -0
  57. package/dist/{registry-impl-BETn_lEO.d.mts → index-DVbWyOs7.d.mts} +46 -2
  58. package/dist/index-DVbWyOs7.d.mts.map +1 -0
  59. package/dist/{index-DNCPeLNM.d.mts → index-F1iNEAJR.d.cts} +102 -80
  60. package/dist/index-F1iNEAJR.d.cts.map +1 -0
  61. package/dist/index.cjs +72 -68
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/index.d.cts +4 -7
  64. package/dist/index.d.cts.map +1 -1
  65. package/dist/index.d.mts +4 -7
  66. package/dist/index.d.mts.map +1 -1
  67. package/dist/index.mjs +11 -10
  68. package/dist/index.mjs.map +1 -1
  69. package/dist/invite-5277FQVT.cjs +274 -0
  70. package/dist/invite-5277FQVT.cjs.map +1 -0
  71. package/dist/invite-DUTcfTgX.cjs +109 -0
  72. package/dist/invite-DUTcfTgX.cjs.map +1 -0
  73. package/dist/invite-IU4n0dq2.mjs +96 -0
  74. package/dist/invite-IU4n0dq2.mjs.map +1 -0
  75. package/dist/invite-RU-OXTNS.mjs +219 -0
  76. package/dist/invite-RU-OXTNS.mjs.map +1 -0
  77. package/dist/parallel-D1R6ZGlY.cjs +318 -0
  78. package/dist/parallel-D1R6ZGlY.cjs.map +1 -0
  79. package/dist/parallel-D6zc6VW4.mjs +235 -0
  80. package/dist/parallel-D6zc6VW4.mjs.map +1 -0
  81. package/dist/proposed-participant-Dm1Eq6mX.cjs +141 -0
  82. package/dist/proposed-participant-Dm1Eq6mX.cjs.map +1 -0
  83. package/dist/proposed-participant-cWM7iUrO.mjs +129 -0
  84. package/dist/proposed-participant-cWM7iUrO.mjs.map +1 -0
  85. package/dist/receive-CAI-x4II.cjs +213 -0
  86. package/dist/receive-CAI-x4II.cjs.map +1 -0
  87. package/dist/receive-D2Nn68L7.mjs +188 -0
  88. package/dist/receive-D2Nn68L7.mjs.map +1 -0
  89. package/dist/receive-DA_KQEgk.mjs +177 -0
  90. package/dist/receive-DA_KQEgk.mjs.map +1 -0
  91. package/dist/receive-kZMsXhbK.cjs +190 -0
  92. package/dist/receive-kZMsXhbK.cjs.map +1 -0
  93. package/dist/registry/index.cjs +881 -13
  94. package/dist/registry/index.cjs.map +1 -0
  95. package/dist/registry/index.d.cts +1 -1
  96. package/dist/registry/index.d.mts +1 -1
  97. package/dist/registry/index.mjs +867 -2
  98. package/dist/registry/index.mjs.map +1 -0
  99. package/dist/{registry-FMU-ec5K.cjs → registry-9puTaRrD.cjs} +28 -31
  100. package/dist/registry-9puTaRrD.cjs.map +1 -0
  101. package/dist/{registry-BDnNV1Rk.mjs → registry-BpCwtrRt.mjs} +7 -10
  102. package/dist/{registry-BDnNV1Rk.mjs.map → registry-BpCwtrRt.mjs.map} +1 -1
  103. package/dist/round1-4Hyx8w0x.cjs +422 -0
  104. package/dist/round1-4Hyx8w0x.cjs.map +1 -0
  105. package/dist/round1-7v9LlE11.mjs +373 -0
  106. package/dist/round1-7v9LlE11.mjs.map +1 -0
  107. package/dist/round1-BHBjru1m.cjs +465 -0
  108. package/dist/round1-BHBjru1m.cjs.map +1 -0
  109. package/dist/round1-CMLKN2RR.mjs +195 -0
  110. package/dist/round1-CMLKN2RR.mjs.map +1 -0
  111. package/dist/round1-CWSXZx5R.cjs +208 -0
  112. package/dist/round1-CWSXZx5R.cjs.map +1 -0
  113. package/dist/round1-CcQCGlIT.mjs +208 -0
  114. package/dist/round1-CcQCGlIT.mjs.map +1 -0
  115. package/dist/round1-Cgm7j1kI.mjs +452 -0
  116. package/dist/round1-Cgm7j1kI.mjs.map +1 -0
  117. package/dist/round1-DQ0fnc1H.cjs +221 -0
  118. package/dist/round1-DQ0fnc1H.cjs.map +1 -0
  119. package/dist/round2-BWz9SQIi.cjs +305 -0
  120. package/dist/round2-BWz9SQIi.cjs.map +1 -0
  121. package/dist/round2-BkNRCXgS.mjs +292 -0
  122. package/dist/round2-BkNRCXgS.mjs.map +1 -0
  123. package/dist/round2-Bl2uK93U.mjs +450 -0
  124. package/dist/round2-Bl2uK93U.mjs.map +1 -0
  125. package/dist/round2-CdUT-AhH.cjs +499 -0
  126. package/dist/round2-CdUT-AhH.cjs.map +1 -0
  127. package/dist/round2-DOA3rnV-.mjs +280 -0
  128. package/dist/round2-DOA3rnV-.mjs.map +1 -0
  129. package/dist/round2-Dg24w-TU.mjs +397 -0
  130. package/dist/round2-Dg24w-TU.mjs.map +1 -0
  131. package/dist/round2-LylCa84n.cjs +293 -0
  132. package/dist/round2-LylCa84n.cjs.map +1 -0
  133. package/dist/round2-o2Q-GMbX.cjs +410 -0
  134. package/dist/round2-o2Q-GMbX.cjs.map +1 -0
  135. package/dist/storage-B-Gu68-O.cjs +79 -0
  136. package/dist/storage-B-Gu68-O.cjs.map +1 -0
  137. package/dist/storage-Bkkliz0K.mjs +74 -0
  138. package/dist/storage-Bkkliz0K.mjs.map +1 -0
  139. package/package.json +17 -17
  140. package/src/bin/frost.ts +849 -128
  141. package/src/cmd/common.ts +19 -1
  142. package/src/cmd/dkg/common.ts +97 -10
  143. package/src/cmd/dkg/coordinator/invite.ts +5 -2
  144. package/src/cmd/dkg/participant/finalize.ts +52 -18
  145. package/src/cmd/dkg/participant/round1.ts +39 -38
  146. package/src/cmd/dkg/participant/round2.ts +60 -26
  147. package/src/cmd/sign/coordinator/round2.ts +5 -1
  148. package/src/cmd/sign/participant/finalize.ts +6 -2
  149. package/src/cmd/sign/participant/receive.ts +5 -2
  150. package/src/dkg/group-invite.ts +12 -2
  151. package/src/dkg/proposed-participant.ts +33 -5
  152. package/src/frost/index.ts +1 -1
  153. package/src/registry/owner-record.ts +13 -2
  154. package/src/registry/participant-record.ts +36 -4
  155. package/src/registry/registry-impl.ts +74 -18
  156. package/dist/group-invite-CrbOabFL.cjs +0 -368
  157. package/dist/group-invite-CrbOabFL.cjs.map +0 -1
  158. package/dist/group-invite-Dz1Jmiky.d.cts.map +0 -1
  159. package/dist/group-invite-RPElq-fm.mjs +0 -338
  160. package/dist/group-invite-RPElq-fm.mjs.map +0 -1
  161. package/dist/group-invite-Wk9CIbHL.d.mts.map +0 -1
  162. package/dist/index-CcvTi5EA.d.cts.map +0 -1
  163. package/dist/index-DNCPeLNM.d.mts.map +0 -1
  164. package/dist/registry-FMU-ec5K.cjs.map +0 -1
  165. package/dist/registry-impl-BETn_lEO.d.mts.map +0 -1
  166. package/dist/registry-impl-C7w4awTv.cjs +0 -865
  167. package/dist/registry-impl-C7w4awTv.cjs.map +0 -1
  168. package/dist/registry-impl-CE76sTXQ.d.cts.map +0 -1
  169. package/dist/registry-impl-eYXVSPwM.mjs +0 -797
  170. package/dist/registry-impl-eYXVSPwM.mjs.map +0 -1
  171. package/dist/sign-2bOp18Fs.cjs +0 -4875
  172. package/dist/sign-2bOp18Fs.cjs.map +0 -1
  173. package/dist/sign-D8C3HJ4B.mjs +0 -4736
  174. package/dist/sign-D8C3HJ4B.mjs.map +0 -1
@@ -0,0 +1,195 @@
1
+ import { t as __exportAll } from "./chunk-CjcI7cDX.mjs";
2
+ import { n as compareXidBytes } from "./proposed-participant-cWM7iUrO.mjs";
3
+ import { ContributionPaths, GroupRecord, Registry, resolveRegistryPath } from "./registry/index.mjs";
4
+ import { c as parseAridUr, i as buildGroupParticipants, l as parseEnvelopeUr, p as resolveSender, s as groupParticipantFromRegistry, t as groupStateDir } from "./common-CvH6dFvQ.mjs";
5
+ import { n as putWithIndicator, t as getWithIndicator } from "./busy-DkM2jAIZ.mjs";
6
+ import { t as createStorageClient } from "./storage-Bkkliz0K.mjs";
7
+ import { t as decodeInviteDetails } from "./receive-D2Nn68L7.mjs";
8
+ import { bytesToHex, createRng, dkgPart1, identifierFromU16 } from "./frost/index.mjs";
9
+ import { ARID, JSON } from "@bcts/components";
10
+ import { CborDate } from "@bcts/dcbor";
11
+ import { Envelope } from "@bcts/envelope";
12
+ import { SealedResponse } from "@bcts/gstp";
13
+ import * as fs from "node:fs";
14
+ import * as path from "node:path";
15
+ import { Ed25519Sha512, serde } from "@frosts/ed25519";
16
+ //#region src/cmd/dkg/participant/round1.ts
17
+ /**
18
+ * Copyright © 2023-2026 Blockchain Commons, LLC
19
+ * Copyright © 2025-2026 Parity Technologies
20
+ *
21
+ *
22
+ * DKG participant round 1 command.
23
+ *
24
+ * Port of cmd/dkg/participant/round1.rs from frost-hubert-rust.
25
+ *
26
+ * @module
27
+ */
28
+ var round1_exports = /* @__PURE__ */ __exportAll({ round1: () => round1 });
29
+ /**
30
+ * Resolve an invite envelope from either storage (ARID) or direct UR.
31
+ *
32
+ * Port of `resolve_invite_envelope()` from cmd/dkg/participant/round1.rs lines 256-288.
33
+ */
34
+ async function resolveInviteEnvelope(selection, invite, timeout) {
35
+ if (selection !== void 0) {
36
+ try {
37
+ const arid = parseAridUr(invite);
38
+ const envelope = await getWithIndicator(await createStorageClient(selection), arid, "Invite", timeout, false);
39
+ if (envelope === null || envelope === void 0) throw new Error("Invite not found in Hubert storage");
40
+ return envelope;
41
+ } catch (e) {
42
+ if (e instanceof Error && e.message.includes("Invite not found in Hubert storage")) throw e;
43
+ }
44
+ if (timeout !== void 0) throw new Error("--timeout is only valid when retrieving invites from Hubert");
45
+ return parseEnvelopeUr(invite);
46
+ }
47
+ try {
48
+ parseAridUr(invite);
49
+ throw new Error("Hubert storage parameters are required to retrieve invites by ARID");
50
+ } catch (e) {
51
+ if (e instanceof Error && e.message.includes("Hubert storage parameters are required")) throw e;
52
+ }
53
+ return parseEnvelopeUr(invite);
54
+ }
55
+ /**
56
+ * Build the response body envelope.
57
+ *
58
+ * Port of `build_response_body()` from cmd/dkg/participant/round1.rs lines 290-308.
59
+ */
60
+ function buildResponseBody(groupId, participant, responseArid, round1Package) {
61
+ let envelope = Envelope.unit().addType("dkgRound1Response").addAssertion("group", groupId).addAssertion("participant", participant).addAssertion("response_arid", responseArid);
62
+ if (round1Package !== void 0) {
63
+ const packageJson = serde.round1PackageToJson(round1Package);
64
+ const jsonStr = globalThis.JSON.stringify(packageJson);
65
+ const jsonBytes = new TextEncoder().encode(jsonStr);
66
+ const jsonWrapper = JSON.fromData(jsonBytes);
67
+ envelope = envelope.addAssertion("round1_package", jsonWrapper);
68
+ }
69
+ return envelope;
70
+ }
71
+ /**
72
+ * Serialize round 1 secret package to JSON-compatible format.
73
+ *
74
+ * The @frosts/ed25519 serde module doesn't provide a serializer for SecretPackage,
75
+ * so we manually serialize it here.
76
+ */
77
+ function serializeRound1SecretPackage(secret) {
78
+ const serializedCoefficients = secret.coefficients().map((c) => bytesToHex(Ed25519Sha512.serializeScalar(c)));
79
+ const commitmentCoefficients = secret.commitment.serialize().map((c) => bytesToHex(c));
80
+ return {
81
+ identifier: bytesToHex(secret.identifier.serialize()),
82
+ coefficients: serializedCoefficients,
83
+ commitment: commitmentCoefficients,
84
+ min_signers: secret.minSigners,
85
+ max_signers: secret.maxSigners
86
+ };
87
+ }
88
+ /**
89
+ * Persist round 1 state to disk.
90
+ *
91
+ * Port of `persist_round1_state()` from cmd/dkg/participant/round1.rs lines 310-337.
92
+ */
93
+ function persistRound1State(registryPath, groupId, round1Secret, round1Package) {
94
+ const dir = groupStateDir(registryPath, groupId.hex());
95
+ fs.mkdirSync(dir, { recursive: true });
96
+ const secretPath = path.join(dir, "round1_secret.json");
97
+ const packagePath = path.join(dir, "round1_package.json");
98
+ const secretJson = serializeRound1SecretPackage(round1Secret);
99
+ const packageJson = serde.round1PackageToJson(round1Package);
100
+ fs.writeFileSync(secretPath, globalThis.JSON.stringify(secretJson, null, 2));
101
+ fs.writeFileSync(packagePath, globalThis.JSON.stringify(packageJson, null, 2));
102
+ return new ContributionPaths({
103
+ round1Secret: secretPath,
104
+ round1Package: packagePath,
105
+ round2Secret: void 0,
106
+ keyPackage: void 0
107
+ });
108
+ }
109
+ /**
110
+ * Execute the DKG participant round 1 command.
111
+ *
112
+ * Responds to the DKG invite with commitment packages.
113
+ *
114
+ * Port of `CommandArgs::exec()` from cmd/dkg/participant/round1.rs lines 66-254.
115
+ */
116
+ async function round1(_client, options, cwd) {
117
+ if (options.storageSelection === void 0 && options.timeoutSeconds !== void 0) throw new Error("--timeout requires Hubert storage parameters");
118
+ if (options.storageSelection !== void 0 && options.preview === true) throw new Error("--preview cannot be used with Hubert storage options");
119
+ const registryPath = resolveRegistryPath(options.registryPath, cwd);
120
+ const registry = Registry.load(registryPath);
121
+ const owner = registry.owner();
122
+ if (!owner) throw new Error("Registry owner with private keys is required");
123
+ let expectedSender;
124
+ if (options.sender !== void 0) expectedSender = resolveSender(registry, options.sender);
125
+ const nextResponseArid = options.responseArid !== void 0 ? parseAridUr(options.responseArid) : ARID.new();
126
+ const details = decodeInviteDetails(await resolveInviteEnvelope(options.storageSelection, options.invite, options.timeoutSeconds), CborDate.now().datetime(), registry, owner.xidDocument(), expectedSender);
127
+ const sortedParticipants = [...details.participants].sort((a, b) => compareXidBytes(a.xid().toData(), b.xid().toData()));
128
+ const ownerIndex = sortedParticipants.findIndex((doc) => doc.xid().urString() === owner.xid().urString());
129
+ if (ownerIndex === -1) throw new Error("Invite does not include the registry owner");
130
+ const identifierIndex = ownerIndex + 1;
131
+ if (identifierIndex > 65535) throw new Error("Too many participants for identifiers");
132
+ const identifier = identifierFromU16(identifierIndex);
133
+ const total = sortedParticipants.length;
134
+ if (total > 65535) throw new Error("Too many participants for FROST identifiers");
135
+ const minSigners = details.invitation.minSigners();
136
+ if (minSigners > 65535) throw new Error("min_signers does not fit into identifier space");
137
+ const groupParticipants = buildGroupParticipants(registry, owner, sortedParticipants);
138
+ const coordinator = groupParticipantFromRegistry(registry, owner, details.invitation.sender());
139
+ const isPosting = options.storageSelection !== void 0;
140
+ let responseBody;
141
+ let contributions;
142
+ if (options.rejectReason === void 0 && isPosting) {
143
+ const [round1Secret, round1Package] = dkgPart1(identifier, total, minSigners, createRng());
144
+ contributions = persistRound1State(registryPath, details.invitation.groupId(), round1Secret, round1Package);
145
+ responseBody = buildResponseBody(details.invitation.groupId(), owner.xid(), nextResponseArid, round1Package);
146
+ const groupRecord = new GroupRecord(details.invitation.charter(), details.invitation.minSigners(), coordinator, groupParticipants);
147
+ groupRecord.setContributions(contributions);
148
+ groupRecord.setListeningAtArid(nextResponseArid);
149
+ registry.recordGroup(details.invitation.groupId(), groupRecord);
150
+ registry.save(registryPath);
151
+ } else if (options.rejectReason === void 0) {
152
+ const [, round1Package] = dkgPart1(identifier, total, minSigners, createRng());
153
+ responseBody = buildResponseBody(details.invitation.groupId(), owner.xid(), nextResponseArid, round1Package);
154
+ } else responseBody = buildResponseBody(details.invitation.groupId(), owner.xid(), nextResponseArid, void 0);
155
+ const signerPrivateKeys = owner.xidDocument().inceptionPrivateKeys();
156
+ if (signerPrivateKeys === void 0) throw new Error("Owner XID document has no signing keys");
157
+ let sealed;
158
+ if (options.rejectReason !== void 0) {
159
+ const errorBody = Envelope.new("dkgInviteReject").addAssertion("group", details.invitation.groupId()).addAssertion("response_arid", nextResponseArid).addAssertion("reason", options.rejectReason);
160
+ sealed = SealedResponse.newFailure(details.invitation.requestId(), owner.xidDocument()).withError(errorBody).withState(nextResponseArid);
161
+ } else sealed = SealedResponse.newSuccess(details.invitation.requestId(), owner.xidDocument()).withResult(responseBody).withState(nextResponseArid);
162
+ const peerContinuation = details.invitation.peerContinuation();
163
+ if (peerContinuation !== void 0) sealed = sealed.withPeerContinuation(peerContinuation);
164
+ if (options.storageSelection !== void 0) {
165
+ const responseEnvelope = sealed.toEnvelope(details.invitation.validUntil(), signerPrivateKeys, details.invitation.sender());
166
+ const responseTarget = details.invitation.responseArid();
167
+ await putWithIndicator(await createStorageClient(options.storageSelection), responseTarget, responseEnvelope, "Round 1 Response", options.verbose ?? false);
168
+ if (options.verbose === true) {
169
+ console.log(`Sent round 1 response`);
170
+ console.log(`Listening at: ${nextResponseArid.urString()}`);
171
+ }
172
+ return {
173
+ accepted: options.rejectReason === void 0,
174
+ listeningArid: nextResponseArid.urString()
175
+ };
176
+ } else if (options.preview === true) {
177
+ const envelopeUr = sealed.toEnvelope(void 0, signerPrivateKeys, void 0).urString();
178
+ console.log(envelopeUr);
179
+ return {
180
+ accepted: options.rejectReason === void 0,
181
+ envelopeUr
182
+ };
183
+ } else {
184
+ const envelopeUr = sealed.toEnvelope(details.invitation.validUntil(), signerPrivateKeys, details.invitation.sender()).urString();
185
+ console.log(envelopeUr);
186
+ return {
187
+ accepted: options.rejectReason === void 0,
188
+ envelopeUr
189
+ };
190
+ }
191
+ }
192
+ //#endregion
193
+ export { round1_exports as n, round1 as t };
194
+
195
+ //# sourceMappingURL=round1-CMLKN2RR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"round1-CMLKN2RR.mjs","names":["JSONWrapper"],"sources":["../src/cmd/dkg/participant/round1.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * DKG participant round 1 command.\n *\n * Port of cmd/dkg/participant/round1.rs from frost-hubert-rust.\n *\n * @module\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { ARID, JSON as JSONWrapper, type XID } from \"@bcts/components\";\nimport { compareXidBytes } from \"../../../dkg/proposed-participant.js\";\nimport { Envelope } from \"@bcts/envelope\";\nimport { SealedResponse } from \"@bcts/gstp\";\nimport type { XIDDocument } from \"@bcts/xid\";\n\nimport {\n ContributionPaths,\n GroupRecord,\n Registry,\n resolveRegistryPath,\n} from \"../../../registry/index.js\";\nimport { getWithIndicator, putWithIndicator } from \"../../busy.js\";\nimport { createStorageClient, type StorageClient, type StorageSelection } from \"../../storage.js\";\nimport { groupStateDir } from \"../../common.js\";\nimport {\n buildGroupParticipants,\n groupParticipantFromRegistry,\n parseAridUr,\n parseEnvelopeUr,\n resolveSender,\n} from \"../common.js\";\nimport {\n dkgPart1,\n identifierFromU16,\n createRng,\n bytesToHex,\n type DkgRound1Package,\n type DkgRound1SecretPackage,\n} from \"../../../frost/index.js\";\nimport { Ed25519Sha512, serde } from \"@frosts/ed25519\";\nimport { decodeInviteDetails } from \"./receive.js\";\nimport { CborDate } from \"@bcts/dcbor\";\n\n/**\n * Options for the DKG round1 command.\n */\nexport interface DkgRound1Options {\n registryPath?: string;\n timeoutSeconds?: number;\n responseArid?: string;\n preview?: boolean;\n rejectReason?: string;\n sender?: string;\n invite: string;\n storageSelection?: StorageSelection;\n verbose?: boolean;\n}\n\n/**\n * Result of the DKG round1 command.\n */\nexport interface DkgRound1Result {\n accepted: boolean;\n listeningArid?: string;\n envelopeUr?: string;\n}\n\n/**\n * Resolve an invite envelope from either storage (ARID) or direct UR.\n *\n * Port of `resolve_invite_envelope()` from cmd/dkg/participant/round1.rs lines 256-288.\n */\nasync function resolveInviteEnvelope(\n selection: StorageSelection | undefined,\n invite: string,\n timeout?: number,\n): Promise<Envelope> {\n if (selection !== undefined) {\n // Try to parse as ARID\n try {\n const arid = parseAridUr(invite);\n const client = await createStorageClient(selection);\n const envelope = await getWithIndicator(client, arid, \"Invite\", timeout, false);\n if (envelope === null || envelope === undefined) {\n throw new Error(\"Invite not found in Hubert storage\");\n }\n return envelope;\n } catch (e) {\n // Not an ARID, fall through to envelope parsing\n if (e instanceof Error && e.message.includes(\"Invite not found in Hubert storage\")) {\n throw e;\n }\n }\n\n if (timeout !== undefined) {\n throw new Error(\"--timeout is only valid when retrieving invites from Hubert\");\n }\n\n return parseEnvelopeUr(invite);\n }\n\n // No storage selection\n try {\n parseAridUr(invite);\n throw new Error(\"Hubert storage parameters are required to retrieve invites by ARID\");\n } catch (e) {\n // Not an ARID, parse as envelope\n if (e instanceof Error && e.message.includes(\"Hubert storage parameters are required\")) {\n throw e;\n }\n }\n\n return parseEnvelopeUr(invite);\n}\n\n/**\n * Build the response body envelope.\n *\n * Port of `build_response_body()` from cmd/dkg/participant/round1.rs lines 290-308.\n */\nfunction buildResponseBody(\n groupId: ARID,\n participant: XID,\n responseArid: ARID,\n round1Package: DkgRound1Package | undefined,\n): Envelope {\n let envelope = Envelope.unit()\n .addType(\"dkgRound1Response\")\n .addAssertion(\"group\", groupId)\n .addAssertion(\"participant\", participant)\n .addAssertion(\"response_arid\", responseArid);\n\n if (round1Package !== undefined) {\n // Serialize the package to JSON and wrap as CBOR JSON\n const packageJson = serde.round1PackageToJson(round1Package);\n const jsonStr = globalThis.JSON.stringify(packageJson);\n const jsonBytes = new TextEncoder().encode(jsonStr);\n const jsonWrapper = JSONWrapper.fromData(jsonBytes);\n // Pass the JSONWrapper directly - it implements CborTaggedEncodable\n envelope = envelope.addAssertion(\"round1_package\", jsonWrapper);\n }\n\n return envelope;\n}\n\n/**\n * Serialize round 1 secret package to JSON-compatible format.\n *\n * The @frosts/ed25519 serde module doesn't provide a serializer for SecretPackage,\n * so we manually serialize it here.\n */\nfunction serializeRound1SecretPackage(secret: DkgRound1SecretPackage): Record<string, unknown> {\n // Mirrors the on-disk shape produced by Rust\n // `serde_json::to_vec_pretty(&frost::keys::dkg::round1::SecretPackage)`\n // (see `frost-rust/frost-core/src/keys/dkg.rs:120-139`):\n //\n // {\n // \"identifier\": \"<lowercase hex scalar>\",\n // \"coefficients\": [\"<hex>\", \"<hex>\", ...],\n // \"commitment\": [\"<hex>\", \"<hex>\", ...],\n // \"min_signers\": <u16>,\n // \"max_signers\": <u16>\n // }\n //\n // `frost::keys::dkg::round1::SecretPackage` is `#[serde(deny_unknown_fields)]`\n // and has no `header` field (the secret package is private to the\n // participant). The earlier port emitted a top-level `header` which\n // would fail `deny_unknown_fields` validation if Rust ever loaded\n // the file.\n //\n // `Identifier`/`SerializableScalar` serialize via\n // `serdect::array::serialize_hex_lower_or_bin` → lowercase hex for\n // JSON, which `bytesToHex` produces.\n const coefficients = secret.coefficients();\n const serializedCoefficients = coefficients.map((c: unknown) =>\n bytesToHex(\n Ed25519Sha512.serializeScalar(c as Parameters<typeof Ed25519Sha512.serializeScalar>[0]),\n ),\n );\n\n const commitment = secret.commitment;\n const commitmentCoefficients = commitment.serialize().map((c: Uint8Array) => bytesToHex(c));\n\n return {\n identifier: bytesToHex(secret.identifier.serialize()),\n coefficients: serializedCoefficients,\n commitment: commitmentCoefficients,\n min_signers: secret.minSigners,\n max_signers: secret.maxSigners,\n };\n}\n\n/**\n * Persist round 1 state to disk.\n *\n * Port of `persist_round1_state()` from cmd/dkg/participant/round1.rs lines 310-337.\n */\nfunction persistRound1State(\n registryPath: string,\n groupId: ARID,\n round1Secret: DkgRound1SecretPackage,\n round1Package: DkgRound1Package,\n): ContributionPaths {\n const dir = groupStateDir(registryPath, groupId.hex());\n fs.mkdirSync(dir, { recursive: true });\n\n const secretPath = path.join(dir, \"round1_secret.json\");\n const packagePath = path.join(dir, \"round1_package.json\");\n\n // Serialize the secret package manually since serde doesn't provide it\n const secretJson = serializeRound1SecretPackage(round1Secret);\n // Serialize the public package using the standard serde function\n const packageJson = serde.round1PackageToJson(round1Package);\n\n fs.writeFileSync(secretPath, globalThis.JSON.stringify(secretJson, null, 2));\n fs.writeFileSync(packagePath, globalThis.JSON.stringify(packageJson, null, 2));\n\n return new ContributionPaths({\n round1Secret: secretPath,\n round1Package: packagePath,\n round2Secret: undefined,\n keyPackage: undefined,\n });\n}\n\n/**\n * Execute the DKG participant round 1 command.\n *\n * Responds to the DKG invite with commitment packages.\n *\n * Port of `CommandArgs::exec()` from cmd/dkg/participant/round1.rs lines 66-254.\n */\nexport async function round1(\n _client: StorageClient | undefined,\n options: DkgRound1Options,\n cwd: string,\n): Promise<DkgRound1Result> {\n // Validate options\n if (options.storageSelection === undefined && options.timeoutSeconds !== undefined) {\n throw new Error(\"--timeout requires Hubert storage parameters\");\n }\n if (options.storageSelection !== undefined && options.preview === true) {\n throw new Error(\"--preview cannot be used with Hubert storage options\");\n }\n\n const registryPath = resolveRegistryPath(options.registryPath, cwd);\n const registry = Registry.load(registryPath);\n\n const owner = registry.owner();\n if (!owner) {\n throw new Error(\"Registry owner with private keys is required\");\n }\n\n // Resolve expected sender if provided. Uses the shared helper from\n // `cmd/dkg/common.ts` (mirrors Rust `resolve_sender`); the\n // duplicated inline implementation that this previously called has\n // been removed.\n let expectedSender: XIDDocument | undefined;\n if (options.sender !== undefined) {\n expectedSender = resolveSender(registry, options.sender);\n }\n\n const nextResponseArid =\n options.responseArid !== undefined ? parseAridUr(options.responseArid) : ARID.new();\n\n // Resolve the invite envelope\n const inviteEnvelope = await resolveInviteEnvelope(\n options.storageSelection,\n options.invite,\n options.timeoutSeconds,\n );\n\n // Decode the invite details\n const now = CborDate.now().datetime();\n const details = decodeInviteDetails(\n inviteEnvelope,\n now,\n registry,\n owner.xidDocument(),\n expectedSender,\n );\n\n // Sort participants by XID byte order (mirrors Rust `XID::cmp` —\n // raw 32-byte lex compare). The earlier port used\n // `xid.urString().localeCompare(...)`, which differs from byte\n // order when bytes ≥ 0x80 are present and is locale-aware,\n // producing different FROST identifier assignments and therefore\n // different secret shares than Rust.\n const sortedParticipants = [...details.participants].sort((a, b) =>\n compareXidBytes(a.xid().toData(), b.xid().toData()),\n );\n\n const ownerIndex = sortedParticipants.findIndex(\n (doc) => doc.xid().urString() === owner.xid().urString(),\n );\n if (ownerIndex === -1) {\n throw new Error(\"Invite does not include the registry owner\");\n }\n\n const identifierIndex = ownerIndex + 1; // FROST uses 1-indexed identifiers\n if (identifierIndex > 65535) {\n throw new Error(\"Too many participants for identifiers\");\n }\n const identifier = identifierFromU16(identifierIndex);\n\n const total = sortedParticipants.length;\n if (total > 65535) {\n throw new Error(\"Too many participants for FROST identifiers\");\n }\n\n const minSigners = details.invitation.minSigners();\n if (minSigners > 65535) {\n throw new Error(\"min_signers does not fit into identifier space\");\n }\n\n // Build group participants for the registry\n const groupParticipants = buildGroupParticipants(registry, owner, sortedParticipants);\n const coordinator = groupParticipantFromRegistry(registry, owner, details.invitation.sender());\n\n // Check if we're posting to storage\n const isPosting = options.storageSelection !== undefined;\n\n // Build the response body\n let responseBody: Envelope;\n let contributions: ContributionPaths | undefined;\n\n if (options.rejectReason === undefined && isPosting) {\n // Actually posting - generate and persist round1 state\n const [round1Secret, round1Package] = dkgPart1(identifier, total, minSigners, createRng());\n\n contributions = persistRound1State(\n registryPath,\n details.invitation.groupId(),\n round1Secret,\n round1Package,\n );\n\n responseBody = buildResponseBody(\n details.invitation.groupId(),\n owner.xid(),\n nextResponseArid,\n round1Package,\n );\n\n // Create and save group record\n const groupRecord = new GroupRecord(\n details.invitation.charter(),\n details.invitation.minSigners(),\n coordinator,\n groupParticipants,\n );\n groupRecord.setContributions(contributions);\n groupRecord.setListeningAtArid(nextResponseArid);\n\n registry.recordGroup(details.invitation.groupId(), groupRecord);\n registry.save(registryPath);\n } else if (options.rejectReason === undefined) {\n // Preview mode - generate dummy round1 for envelope structure only\n const [, round1Package] = dkgPart1(identifier, total, minSigners, createRng());\n\n responseBody = buildResponseBody(\n details.invitation.groupId(),\n owner.xid(),\n nextResponseArid,\n round1Package,\n );\n } else {\n // Rejecting - no round1 needed\n responseBody = buildResponseBody(\n details.invitation.groupId(),\n owner.xid(),\n nextResponseArid,\n undefined,\n );\n }\n\n // Build the sealed response\n const signerPrivateKeys = owner.xidDocument().inceptionPrivateKeys();\n if (signerPrivateKeys === undefined) {\n throw new Error(\"Owner XID document has no signing keys\");\n }\n\n let sealed: SealedResponse;\n if (options.rejectReason !== undefined) {\n // Build rejection error body\n const errorBody = Envelope.new(\"dkgInviteReject\")\n .addAssertion(\"group\", details.invitation.groupId())\n .addAssertion(\"response_arid\", nextResponseArid)\n .addAssertion(\"reason\", options.rejectReason);\n\n sealed = SealedResponse.newFailure(details.invitation.requestId(), owner.xidDocument())\n .withError(errorBody)\n .withState(nextResponseArid);\n } else {\n sealed = SealedResponse.newSuccess(details.invitation.requestId(), owner.xidDocument())\n .withResult(responseBody)\n .withState(nextResponseArid);\n }\n\n // Add peer continuation if present\n const peerContinuation = details.invitation.peerContinuation();\n if (peerContinuation !== undefined) {\n sealed = sealed.withPeerContinuation(peerContinuation);\n }\n\n // Handle output based on storage selection\n if (options.storageSelection !== undefined) {\n const responseEnvelope = sealed.toEnvelope(\n details.invitation.validUntil(),\n signerPrivateKeys,\n details.invitation.sender(),\n );\n\n const responseTarget = details.invitation.responseArid();\n const client = await createStorageClient(options.storageSelection);\n\n await putWithIndicator(\n client,\n responseTarget,\n responseEnvelope,\n \"Round 1 Response\",\n options.verbose ?? false,\n );\n\n if (options.verbose === true) {\n console.log(`Sent round 1 response`);\n console.log(`Listening at: ${nextResponseArid.urString()}`);\n }\n\n return {\n accepted: options.rejectReason === undefined,\n listeningArid: nextResponseArid.urString(),\n };\n } else if (options.preview === true) {\n // Show the GSTP response structure without encryption\n const unsealedEnvelope = sealed.toEnvelope(undefined, signerPrivateKeys, undefined);\n const envelopeUr = unsealedEnvelope.urString();\n console.log(envelopeUr);\n\n return {\n accepted: options.rejectReason === undefined,\n envelopeUr,\n };\n } else {\n // Print the sealed envelope\n const responseEnvelope = sealed.toEnvelope(\n details.invitation.validUntil(),\n signerPrivateKeys,\n details.invitation.sender(),\n );\n const envelopeUr = responseEnvelope.urString();\n console.log(envelopeUr);\n\n return {\n accepted: options.rejectReason === undefined,\n envelopeUr,\n };\n }\n}\n\n// `resolveSenderXidDocument` removed — it was an inline duplicate of\n// Rust `resolve_sender(registry, input)`. The shared helper now lives\n// in `cmd/dkg/common.ts` and is imported above.\n"],"mappings":"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"}
@@ -0,0 +1,208 @@
1
+ const require_chunk = require("./chunk-CZWwpsFl.cjs");
2
+ const require_proposed_participant = require("./proposed-participant-Dm1Eq6mX.cjs");
3
+ const require_registry_index = require("./registry/index.cjs");
4
+ const require_common = require("./common-lKP5EzHy.cjs");
5
+ const require_busy = require("./busy-EZU7EKr6.cjs");
6
+ const require_storage = require("./storage-B-Gu68-O.cjs");
7
+ const require_receive = require("./receive-CAI-x4II.cjs");
8
+ const require_frost_index = require("./frost/index.cjs");
9
+ let _bcts_components = require("@bcts/components");
10
+ let _bcts_dcbor = require("@bcts/dcbor");
11
+ let _bcts_envelope = require("@bcts/envelope");
12
+ let _bcts_gstp = require("@bcts/gstp");
13
+ let node_fs = require("node:fs");
14
+ node_fs = require_chunk.__toESM(node_fs, 1);
15
+ let node_path = require("node:path");
16
+ node_path = require_chunk.__toESM(node_path, 1);
17
+ let _frosts_ed25519 = require("@frosts/ed25519");
18
+ //#region src/cmd/dkg/participant/round1.ts
19
+ /**
20
+ * Copyright © 2023-2026 Blockchain Commons, LLC
21
+ * Copyright © 2025-2026 Parity Technologies
22
+ *
23
+ *
24
+ * DKG participant round 1 command.
25
+ *
26
+ * Port of cmd/dkg/participant/round1.rs from frost-hubert-rust.
27
+ *
28
+ * @module
29
+ */
30
+ var round1_exports = /* @__PURE__ */ require_chunk.__exportAll({ round1: () => round1 });
31
+ /**
32
+ * Resolve an invite envelope from either storage (ARID) or direct UR.
33
+ *
34
+ * Port of `resolve_invite_envelope()` from cmd/dkg/participant/round1.rs lines 256-288.
35
+ */
36
+ async function resolveInviteEnvelope(selection, invite, timeout) {
37
+ if (selection !== void 0) {
38
+ try {
39
+ const arid = require_common.parseAridUr(invite);
40
+ const envelope = await require_busy.getWithIndicator(await require_storage.createStorageClient(selection), arid, "Invite", timeout, false);
41
+ if (envelope === null || envelope === void 0) throw new Error("Invite not found in Hubert storage");
42
+ return envelope;
43
+ } catch (e) {
44
+ if (e instanceof Error && e.message.includes("Invite not found in Hubert storage")) throw e;
45
+ }
46
+ if (timeout !== void 0) throw new Error("--timeout is only valid when retrieving invites from Hubert");
47
+ return require_common.parseEnvelopeUr(invite);
48
+ }
49
+ try {
50
+ require_common.parseAridUr(invite);
51
+ throw new Error("Hubert storage parameters are required to retrieve invites by ARID");
52
+ } catch (e) {
53
+ if (e instanceof Error && e.message.includes("Hubert storage parameters are required")) throw e;
54
+ }
55
+ return require_common.parseEnvelopeUr(invite);
56
+ }
57
+ /**
58
+ * Build the response body envelope.
59
+ *
60
+ * Port of `build_response_body()` from cmd/dkg/participant/round1.rs lines 290-308.
61
+ */
62
+ function buildResponseBody(groupId, participant, responseArid, round1Package) {
63
+ let envelope = _bcts_envelope.Envelope.unit().addType("dkgRound1Response").addAssertion("group", groupId).addAssertion("participant", participant).addAssertion("response_arid", responseArid);
64
+ if (round1Package !== void 0) {
65
+ const packageJson = _frosts_ed25519.serde.round1PackageToJson(round1Package);
66
+ const jsonStr = globalThis.JSON.stringify(packageJson);
67
+ const jsonBytes = new TextEncoder().encode(jsonStr);
68
+ const jsonWrapper = _bcts_components.JSON.fromData(jsonBytes);
69
+ envelope = envelope.addAssertion("round1_package", jsonWrapper);
70
+ }
71
+ return envelope;
72
+ }
73
+ /**
74
+ * Serialize round 1 secret package to JSON-compatible format.
75
+ *
76
+ * The @frosts/ed25519 serde module doesn't provide a serializer for SecretPackage,
77
+ * so we manually serialize it here.
78
+ */
79
+ function serializeRound1SecretPackage(secret) {
80
+ const serializedCoefficients = secret.coefficients().map((c) => require_frost_index.bytesToHex(_frosts_ed25519.Ed25519Sha512.serializeScalar(c)));
81
+ const commitmentCoefficients = secret.commitment.serialize().map((c) => require_frost_index.bytesToHex(c));
82
+ return {
83
+ identifier: require_frost_index.bytesToHex(secret.identifier.serialize()),
84
+ coefficients: serializedCoefficients,
85
+ commitment: commitmentCoefficients,
86
+ min_signers: secret.minSigners,
87
+ max_signers: secret.maxSigners
88
+ };
89
+ }
90
+ /**
91
+ * Persist round 1 state to disk.
92
+ *
93
+ * Port of `persist_round1_state()` from cmd/dkg/participant/round1.rs lines 310-337.
94
+ */
95
+ function persistRound1State(registryPath, groupId, round1Secret, round1Package) {
96
+ const dir = require_common.groupStateDir(registryPath, groupId.hex());
97
+ node_fs.mkdirSync(dir, { recursive: true });
98
+ const secretPath = node_path.join(dir, "round1_secret.json");
99
+ const packagePath = node_path.join(dir, "round1_package.json");
100
+ const secretJson = serializeRound1SecretPackage(round1Secret);
101
+ const packageJson = _frosts_ed25519.serde.round1PackageToJson(round1Package);
102
+ node_fs.writeFileSync(secretPath, globalThis.JSON.stringify(secretJson, null, 2));
103
+ node_fs.writeFileSync(packagePath, globalThis.JSON.stringify(packageJson, null, 2));
104
+ return new require_registry_index.ContributionPaths({
105
+ round1Secret: secretPath,
106
+ round1Package: packagePath,
107
+ round2Secret: void 0,
108
+ keyPackage: void 0
109
+ });
110
+ }
111
+ /**
112
+ * Execute the DKG participant round 1 command.
113
+ *
114
+ * Responds to the DKG invite with commitment packages.
115
+ *
116
+ * Port of `CommandArgs::exec()` from cmd/dkg/participant/round1.rs lines 66-254.
117
+ */
118
+ async function round1(_client, options, cwd) {
119
+ if (options.storageSelection === void 0 && options.timeoutSeconds !== void 0) throw new Error("--timeout requires Hubert storage parameters");
120
+ if (options.storageSelection !== void 0 && options.preview === true) throw new Error("--preview cannot be used with Hubert storage options");
121
+ const registryPath = require_registry_index.resolveRegistryPath(options.registryPath, cwd);
122
+ const registry = require_registry_index.Registry.load(registryPath);
123
+ const owner = registry.owner();
124
+ if (!owner) throw new Error("Registry owner with private keys is required");
125
+ let expectedSender;
126
+ if (options.sender !== void 0) expectedSender = require_common.resolveSender(registry, options.sender);
127
+ const nextResponseArid = options.responseArid !== void 0 ? require_common.parseAridUr(options.responseArid) : _bcts_components.ARID.new();
128
+ const details = require_receive.decodeInviteDetails(await resolveInviteEnvelope(options.storageSelection, options.invite, options.timeoutSeconds), _bcts_dcbor.CborDate.now().datetime(), registry, owner.xidDocument(), expectedSender);
129
+ const sortedParticipants = [...details.participants].sort((a, b) => require_proposed_participant.compareXidBytes(a.xid().toData(), b.xid().toData()));
130
+ const ownerIndex = sortedParticipants.findIndex((doc) => doc.xid().urString() === owner.xid().urString());
131
+ if (ownerIndex === -1) throw new Error("Invite does not include the registry owner");
132
+ const identifierIndex = ownerIndex + 1;
133
+ if (identifierIndex > 65535) throw new Error("Too many participants for identifiers");
134
+ const identifier = require_frost_index.identifierFromU16(identifierIndex);
135
+ const total = sortedParticipants.length;
136
+ if (total > 65535) throw new Error("Too many participants for FROST identifiers");
137
+ const minSigners = details.invitation.minSigners();
138
+ if (minSigners > 65535) throw new Error("min_signers does not fit into identifier space");
139
+ const groupParticipants = require_common.buildGroupParticipants(registry, owner, sortedParticipants);
140
+ const coordinator = require_common.groupParticipantFromRegistry(registry, owner, details.invitation.sender());
141
+ const isPosting = options.storageSelection !== void 0;
142
+ let responseBody;
143
+ let contributions;
144
+ if (options.rejectReason === void 0 && isPosting) {
145
+ const [round1Secret, round1Package] = require_frost_index.dkgPart1(identifier, total, minSigners, require_frost_index.createRng());
146
+ contributions = persistRound1State(registryPath, details.invitation.groupId(), round1Secret, round1Package);
147
+ responseBody = buildResponseBody(details.invitation.groupId(), owner.xid(), nextResponseArid, round1Package);
148
+ const groupRecord = new require_registry_index.GroupRecord(details.invitation.charter(), details.invitation.minSigners(), coordinator, groupParticipants);
149
+ groupRecord.setContributions(contributions);
150
+ groupRecord.setListeningAtArid(nextResponseArid);
151
+ registry.recordGroup(details.invitation.groupId(), groupRecord);
152
+ registry.save(registryPath);
153
+ } else if (options.rejectReason === void 0) {
154
+ const [, round1Package] = require_frost_index.dkgPart1(identifier, total, minSigners, require_frost_index.createRng());
155
+ responseBody = buildResponseBody(details.invitation.groupId(), owner.xid(), nextResponseArid, round1Package);
156
+ } else responseBody = buildResponseBody(details.invitation.groupId(), owner.xid(), nextResponseArid, void 0);
157
+ const signerPrivateKeys = owner.xidDocument().inceptionPrivateKeys();
158
+ if (signerPrivateKeys === void 0) throw new Error("Owner XID document has no signing keys");
159
+ let sealed;
160
+ if (options.rejectReason !== void 0) {
161
+ const errorBody = _bcts_envelope.Envelope.new("dkgInviteReject").addAssertion("group", details.invitation.groupId()).addAssertion("response_arid", nextResponseArid).addAssertion("reason", options.rejectReason);
162
+ sealed = _bcts_gstp.SealedResponse.newFailure(details.invitation.requestId(), owner.xidDocument()).withError(errorBody).withState(nextResponseArid);
163
+ } else sealed = _bcts_gstp.SealedResponse.newSuccess(details.invitation.requestId(), owner.xidDocument()).withResult(responseBody).withState(nextResponseArid);
164
+ const peerContinuation = details.invitation.peerContinuation();
165
+ if (peerContinuation !== void 0) sealed = sealed.withPeerContinuation(peerContinuation);
166
+ if (options.storageSelection !== void 0) {
167
+ const responseEnvelope = sealed.toEnvelope(details.invitation.validUntil(), signerPrivateKeys, details.invitation.sender());
168
+ const responseTarget = details.invitation.responseArid();
169
+ await require_busy.putWithIndicator(await require_storage.createStorageClient(options.storageSelection), responseTarget, responseEnvelope, "Round 1 Response", options.verbose ?? false);
170
+ if (options.verbose === true) {
171
+ console.log(`Sent round 1 response`);
172
+ console.log(`Listening at: ${nextResponseArid.urString()}`);
173
+ }
174
+ return {
175
+ accepted: options.rejectReason === void 0,
176
+ listeningArid: nextResponseArid.urString()
177
+ };
178
+ } else if (options.preview === true) {
179
+ const envelopeUr = sealed.toEnvelope(void 0, signerPrivateKeys, void 0).urString();
180
+ console.log(envelopeUr);
181
+ return {
182
+ accepted: options.rejectReason === void 0,
183
+ envelopeUr
184
+ };
185
+ } else {
186
+ const envelopeUr = sealed.toEnvelope(details.invitation.validUntil(), signerPrivateKeys, details.invitation.sender()).urString();
187
+ console.log(envelopeUr);
188
+ return {
189
+ accepted: options.rejectReason === void 0,
190
+ envelopeUr
191
+ };
192
+ }
193
+ }
194
+ //#endregion
195
+ Object.defineProperty(exports, "round1", {
196
+ enumerable: true,
197
+ get: function() {
198
+ return round1;
199
+ }
200
+ });
201
+ Object.defineProperty(exports, "round1_exports", {
202
+ enumerable: true,
203
+ get: function() {
204
+ return round1_exports;
205
+ }
206
+ });
207
+
208
+ //# sourceMappingURL=round1-CWSXZx5R.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"round1-CWSXZx5R.cjs","names":["parseAridUr","getWithIndicator","createStorageClient","parseEnvelopeUr","Envelope","serde","JSONWrapper","bytesToHex","Ed25519Sha512","groupStateDir","path","ContributionPaths","resolveRegistryPath","Registry","resolveSender","ARID","decodeInviteDetails","CborDate","compareXidBytes","identifierFromU16","buildGroupParticipants","groupParticipantFromRegistry","dkgPart1","createRng","GroupRecord","SealedResponse","putWithIndicator"],"sources":["../src/cmd/dkg/participant/round1.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * DKG participant round 1 command.\n *\n * Port of cmd/dkg/participant/round1.rs from frost-hubert-rust.\n *\n * @module\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { ARID, JSON as JSONWrapper, type XID } from \"@bcts/components\";\nimport { compareXidBytes } from \"../../../dkg/proposed-participant.js\";\nimport { Envelope } from \"@bcts/envelope\";\nimport { SealedResponse } from \"@bcts/gstp\";\nimport type { XIDDocument } from \"@bcts/xid\";\n\nimport {\n ContributionPaths,\n GroupRecord,\n Registry,\n resolveRegistryPath,\n} from \"../../../registry/index.js\";\nimport { getWithIndicator, putWithIndicator } from \"../../busy.js\";\nimport { createStorageClient, type StorageClient, type StorageSelection } from \"../../storage.js\";\nimport { groupStateDir } from \"../../common.js\";\nimport {\n buildGroupParticipants,\n groupParticipantFromRegistry,\n parseAridUr,\n parseEnvelopeUr,\n resolveSender,\n} from \"../common.js\";\nimport {\n dkgPart1,\n identifierFromU16,\n createRng,\n bytesToHex,\n type DkgRound1Package,\n type DkgRound1SecretPackage,\n} from \"../../../frost/index.js\";\nimport { Ed25519Sha512, serde } from \"@frosts/ed25519\";\nimport { decodeInviteDetails } from \"./receive.js\";\nimport { CborDate } from \"@bcts/dcbor\";\n\n/**\n * Options for the DKG round1 command.\n */\nexport interface DkgRound1Options {\n registryPath?: string;\n timeoutSeconds?: number;\n responseArid?: string;\n preview?: boolean;\n rejectReason?: string;\n sender?: string;\n invite: string;\n storageSelection?: StorageSelection;\n verbose?: boolean;\n}\n\n/**\n * Result of the DKG round1 command.\n */\nexport interface DkgRound1Result {\n accepted: boolean;\n listeningArid?: string;\n envelopeUr?: string;\n}\n\n/**\n * Resolve an invite envelope from either storage (ARID) or direct UR.\n *\n * Port of `resolve_invite_envelope()` from cmd/dkg/participant/round1.rs lines 256-288.\n */\nasync function resolveInviteEnvelope(\n selection: StorageSelection | undefined,\n invite: string,\n timeout?: number,\n): Promise<Envelope> {\n if (selection !== undefined) {\n // Try to parse as ARID\n try {\n const arid = parseAridUr(invite);\n const client = await createStorageClient(selection);\n const envelope = await getWithIndicator(client, arid, \"Invite\", timeout, false);\n if (envelope === null || envelope === undefined) {\n throw new Error(\"Invite not found in Hubert storage\");\n }\n return envelope;\n } catch (e) {\n // Not an ARID, fall through to envelope parsing\n if (e instanceof Error && e.message.includes(\"Invite not found in Hubert storage\")) {\n throw e;\n }\n }\n\n if (timeout !== undefined) {\n throw new Error(\"--timeout is only valid when retrieving invites from Hubert\");\n }\n\n return parseEnvelopeUr(invite);\n }\n\n // No storage selection\n try {\n parseAridUr(invite);\n throw new Error(\"Hubert storage parameters are required to retrieve invites by ARID\");\n } catch (e) {\n // Not an ARID, parse as envelope\n if (e instanceof Error && e.message.includes(\"Hubert storage parameters are required\")) {\n throw e;\n }\n }\n\n return parseEnvelopeUr(invite);\n}\n\n/**\n * Build the response body envelope.\n *\n * Port of `build_response_body()` from cmd/dkg/participant/round1.rs lines 290-308.\n */\nfunction buildResponseBody(\n groupId: ARID,\n participant: XID,\n responseArid: ARID,\n round1Package: DkgRound1Package | undefined,\n): Envelope {\n let envelope = Envelope.unit()\n .addType(\"dkgRound1Response\")\n .addAssertion(\"group\", groupId)\n .addAssertion(\"participant\", participant)\n .addAssertion(\"response_arid\", responseArid);\n\n if (round1Package !== undefined) {\n // Serialize the package to JSON and wrap as CBOR JSON\n const packageJson = serde.round1PackageToJson(round1Package);\n const jsonStr = globalThis.JSON.stringify(packageJson);\n const jsonBytes = new TextEncoder().encode(jsonStr);\n const jsonWrapper = JSONWrapper.fromData(jsonBytes);\n // Pass the JSONWrapper directly - it implements CborTaggedEncodable\n envelope = envelope.addAssertion(\"round1_package\", jsonWrapper);\n }\n\n return envelope;\n}\n\n/**\n * Serialize round 1 secret package to JSON-compatible format.\n *\n * The @frosts/ed25519 serde module doesn't provide a serializer for SecretPackage,\n * so we manually serialize it here.\n */\nfunction serializeRound1SecretPackage(secret: DkgRound1SecretPackage): Record<string, unknown> {\n // Mirrors the on-disk shape produced by Rust\n // `serde_json::to_vec_pretty(&frost::keys::dkg::round1::SecretPackage)`\n // (see `frost-rust/frost-core/src/keys/dkg.rs:120-139`):\n //\n // {\n // \"identifier\": \"<lowercase hex scalar>\",\n // \"coefficients\": [\"<hex>\", \"<hex>\", ...],\n // \"commitment\": [\"<hex>\", \"<hex>\", ...],\n // \"min_signers\": <u16>,\n // \"max_signers\": <u16>\n // }\n //\n // `frost::keys::dkg::round1::SecretPackage` is `#[serde(deny_unknown_fields)]`\n // and has no `header` field (the secret package is private to the\n // participant). The earlier port emitted a top-level `header` which\n // would fail `deny_unknown_fields` validation if Rust ever loaded\n // the file.\n //\n // `Identifier`/`SerializableScalar` serialize via\n // `serdect::array::serialize_hex_lower_or_bin` → lowercase hex for\n // JSON, which `bytesToHex` produces.\n const coefficients = secret.coefficients();\n const serializedCoefficients = coefficients.map((c: unknown) =>\n bytesToHex(\n Ed25519Sha512.serializeScalar(c as Parameters<typeof Ed25519Sha512.serializeScalar>[0]),\n ),\n );\n\n const commitment = secret.commitment;\n const commitmentCoefficients = commitment.serialize().map((c: Uint8Array) => bytesToHex(c));\n\n return {\n identifier: bytesToHex(secret.identifier.serialize()),\n coefficients: serializedCoefficients,\n commitment: commitmentCoefficients,\n min_signers: secret.minSigners,\n max_signers: secret.maxSigners,\n };\n}\n\n/**\n * Persist round 1 state to disk.\n *\n * Port of `persist_round1_state()` from cmd/dkg/participant/round1.rs lines 310-337.\n */\nfunction persistRound1State(\n registryPath: string,\n groupId: ARID,\n round1Secret: DkgRound1SecretPackage,\n round1Package: DkgRound1Package,\n): ContributionPaths {\n const dir = groupStateDir(registryPath, groupId.hex());\n fs.mkdirSync(dir, { recursive: true });\n\n const secretPath = path.join(dir, \"round1_secret.json\");\n const packagePath = path.join(dir, \"round1_package.json\");\n\n // Serialize the secret package manually since serde doesn't provide it\n const secretJson = serializeRound1SecretPackage(round1Secret);\n // Serialize the public package using the standard serde function\n const packageJson = serde.round1PackageToJson(round1Package);\n\n fs.writeFileSync(secretPath, globalThis.JSON.stringify(secretJson, null, 2));\n fs.writeFileSync(packagePath, globalThis.JSON.stringify(packageJson, null, 2));\n\n return new ContributionPaths({\n round1Secret: secretPath,\n round1Package: packagePath,\n round2Secret: undefined,\n keyPackage: undefined,\n });\n}\n\n/**\n * Execute the DKG participant round 1 command.\n *\n * Responds to the DKG invite with commitment packages.\n *\n * Port of `CommandArgs::exec()` from cmd/dkg/participant/round1.rs lines 66-254.\n */\nexport async function round1(\n _client: StorageClient | undefined,\n options: DkgRound1Options,\n cwd: string,\n): Promise<DkgRound1Result> {\n // Validate options\n if (options.storageSelection === undefined && options.timeoutSeconds !== undefined) {\n throw new Error(\"--timeout requires Hubert storage parameters\");\n }\n if (options.storageSelection !== undefined && options.preview === true) {\n throw new Error(\"--preview cannot be used with Hubert storage options\");\n }\n\n const registryPath = resolveRegistryPath(options.registryPath, cwd);\n const registry = Registry.load(registryPath);\n\n const owner = registry.owner();\n if (!owner) {\n throw new Error(\"Registry owner with private keys is required\");\n }\n\n // Resolve expected sender if provided. Uses the shared helper from\n // `cmd/dkg/common.ts` (mirrors Rust `resolve_sender`); the\n // duplicated inline implementation that this previously called has\n // been removed.\n let expectedSender: XIDDocument | undefined;\n if (options.sender !== undefined) {\n expectedSender = resolveSender(registry, options.sender);\n }\n\n const nextResponseArid =\n options.responseArid !== undefined ? parseAridUr(options.responseArid) : ARID.new();\n\n // Resolve the invite envelope\n const inviteEnvelope = await resolveInviteEnvelope(\n options.storageSelection,\n options.invite,\n options.timeoutSeconds,\n );\n\n // Decode the invite details\n const now = CborDate.now().datetime();\n const details = decodeInviteDetails(\n inviteEnvelope,\n now,\n registry,\n owner.xidDocument(),\n expectedSender,\n );\n\n // Sort participants by XID byte order (mirrors Rust `XID::cmp` —\n // raw 32-byte lex compare). The earlier port used\n // `xid.urString().localeCompare(...)`, which differs from byte\n // order when bytes ≥ 0x80 are present and is locale-aware,\n // producing different FROST identifier assignments and therefore\n // different secret shares than Rust.\n const sortedParticipants = [...details.participants].sort((a, b) =>\n compareXidBytes(a.xid().toData(), b.xid().toData()),\n );\n\n const ownerIndex = sortedParticipants.findIndex(\n (doc) => doc.xid().urString() === owner.xid().urString(),\n );\n if (ownerIndex === -1) {\n throw new Error(\"Invite does not include the registry owner\");\n }\n\n const identifierIndex = ownerIndex + 1; // FROST uses 1-indexed identifiers\n if (identifierIndex > 65535) {\n throw new Error(\"Too many participants for identifiers\");\n }\n const identifier = identifierFromU16(identifierIndex);\n\n const total = sortedParticipants.length;\n if (total > 65535) {\n throw new Error(\"Too many participants for FROST identifiers\");\n }\n\n const minSigners = details.invitation.minSigners();\n if (minSigners > 65535) {\n throw new Error(\"min_signers does not fit into identifier space\");\n }\n\n // Build group participants for the registry\n const groupParticipants = buildGroupParticipants(registry, owner, sortedParticipants);\n const coordinator = groupParticipantFromRegistry(registry, owner, details.invitation.sender());\n\n // Check if we're posting to storage\n const isPosting = options.storageSelection !== undefined;\n\n // Build the response body\n let responseBody: Envelope;\n let contributions: ContributionPaths | undefined;\n\n if (options.rejectReason === undefined && isPosting) {\n // Actually posting - generate and persist round1 state\n const [round1Secret, round1Package] = dkgPart1(identifier, total, minSigners, createRng());\n\n contributions = persistRound1State(\n registryPath,\n details.invitation.groupId(),\n round1Secret,\n round1Package,\n );\n\n responseBody = buildResponseBody(\n details.invitation.groupId(),\n owner.xid(),\n nextResponseArid,\n round1Package,\n );\n\n // Create and save group record\n const groupRecord = new GroupRecord(\n details.invitation.charter(),\n details.invitation.minSigners(),\n coordinator,\n groupParticipants,\n );\n groupRecord.setContributions(contributions);\n groupRecord.setListeningAtArid(nextResponseArid);\n\n registry.recordGroup(details.invitation.groupId(), groupRecord);\n registry.save(registryPath);\n } else if (options.rejectReason === undefined) {\n // Preview mode - generate dummy round1 for envelope structure only\n const [, round1Package] = dkgPart1(identifier, total, minSigners, createRng());\n\n responseBody = buildResponseBody(\n details.invitation.groupId(),\n owner.xid(),\n nextResponseArid,\n round1Package,\n );\n } else {\n // Rejecting - no round1 needed\n responseBody = buildResponseBody(\n details.invitation.groupId(),\n owner.xid(),\n nextResponseArid,\n undefined,\n );\n }\n\n // Build the sealed response\n const signerPrivateKeys = owner.xidDocument().inceptionPrivateKeys();\n if (signerPrivateKeys === undefined) {\n throw new Error(\"Owner XID document has no signing keys\");\n }\n\n let sealed: SealedResponse;\n if (options.rejectReason !== undefined) {\n // Build rejection error body\n const errorBody = Envelope.new(\"dkgInviteReject\")\n .addAssertion(\"group\", details.invitation.groupId())\n .addAssertion(\"response_arid\", nextResponseArid)\n .addAssertion(\"reason\", options.rejectReason);\n\n sealed = SealedResponse.newFailure(details.invitation.requestId(), owner.xidDocument())\n .withError(errorBody)\n .withState(nextResponseArid);\n } else {\n sealed = SealedResponse.newSuccess(details.invitation.requestId(), owner.xidDocument())\n .withResult(responseBody)\n .withState(nextResponseArid);\n }\n\n // Add peer continuation if present\n const peerContinuation = details.invitation.peerContinuation();\n if (peerContinuation !== undefined) {\n sealed = sealed.withPeerContinuation(peerContinuation);\n }\n\n // Handle output based on storage selection\n if (options.storageSelection !== undefined) {\n const responseEnvelope = sealed.toEnvelope(\n details.invitation.validUntil(),\n signerPrivateKeys,\n details.invitation.sender(),\n );\n\n const responseTarget = details.invitation.responseArid();\n const client = await createStorageClient(options.storageSelection);\n\n await putWithIndicator(\n client,\n responseTarget,\n responseEnvelope,\n \"Round 1 Response\",\n options.verbose ?? false,\n );\n\n if (options.verbose === true) {\n console.log(`Sent round 1 response`);\n console.log(`Listening at: ${nextResponseArid.urString()}`);\n }\n\n return {\n accepted: options.rejectReason === undefined,\n listeningArid: nextResponseArid.urString(),\n };\n } else if (options.preview === true) {\n // Show the GSTP response structure without encryption\n const unsealedEnvelope = sealed.toEnvelope(undefined, signerPrivateKeys, undefined);\n const envelopeUr = unsealedEnvelope.urString();\n console.log(envelopeUr);\n\n return {\n accepted: options.rejectReason === undefined,\n envelopeUr,\n };\n } else {\n // Print the sealed envelope\n const responseEnvelope = sealed.toEnvelope(\n details.invitation.validUntil(),\n signerPrivateKeys,\n details.invitation.sender(),\n );\n const envelopeUr = responseEnvelope.urString();\n console.log(envelopeUr);\n\n return {\n accepted: options.rejectReason === undefined,\n envelopeUr,\n };\n }\n}\n\n// `resolveSenderXidDocument` removed — it was an inline duplicate of\n// Rust `resolve_sender(registry, input)`. The shared helper now lives\n// in `cmd/dkg/common.ts` and is imported above.\n"],"mappings":"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"}