@bcts/frost-hubert 1.0.0-alpha.17
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/LICENSE +48 -0
- package/README.md +35 -0
- package/dist/bin/frost.cjs +109 -0
- package/dist/bin/frost.cjs.map +1 -0
- package/dist/bin/frost.d.cts +1 -0
- package/dist/bin/frost.d.mts +1 -0
- package/dist/bin/frost.mjs +109 -0
- package/dist/bin/frost.mjs.map +1 -0
- package/dist/chunk-CQwRTUmo.cjs +53 -0
- package/dist/chunk-D3JzZLW2.mjs +21 -0
- package/dist/cmd/index.cjs +45 -0
- package/dist/cmd/index.d.cts +4 -0
- package/dist/cmd/index.d.mts +4 -0
- package/dist/cmd/index.mjs +7 -0
- package/dist/cmd-C8pmNd28.mjs +4664 -0
- package/dist/cmd-C8pmNd28.mjs.map +1 -0
- package/dist/cmd-CxUgryx_.cjs +4803 -0
- package/dist/cmd-CxUgryx_.cjs.map +1 -0
- package/dist/dkg/index.cjs +7 -0
- package/dist/dkg/index.d.cts +2 -0
- package/dist/dkg/index.d.mts +2 -0
- package/dist/dkg/index.mjs +3 -0
- package/dist/dkg-D4RcblWl.cjs +364 -0
- package/dist/dkg-D4RcblWl.cjs.map +1 -0
- package/dist/dkg-DqGrAV81.mjs +334 -0
- package/dist/dkg-DqGrAV81.mjs.map +1 -0
- package/dist/frost/index.cjs +37 -0
- package/dist/frost/index.d.cts +207 -0
- package/dist/frost/index.d.cts.map +1 -0
- package/dist/frost/index.d.mts +207 -0
- package/dist/frost/index.d.mts.map +1 -0
- package/dist/frost/index.mjs +3 -0
- package/dist/frost-CMH1K0Cw.cjs +511 -0
- package/dist/frost-CMH1K0Cw.cjs.map +1 -0
- package/dist/frost-Csp0IOrd.mjs +326 -0
- package/dist/frost-Csp0IOrd.mjs.map +1 -0
- package/dist/index-BGVoWW5P.d.cts +172 -0
- package/dist/index-BGVoWW5P.d.cts.map +1 -0
- package/dist/index-BJeUYrdE.d.mts +396 -0
- package/dist/index-BJeUYrdE.d.mts.map +1 -0
- package/dist/index-ByMDUYKw.d.mts +1098 -0
- package/dist/index-ByMDUYKw.d.mts.map +1 -0
- package/dist/index-DejLkr_F.d.mts +172 -0
- package/dist/index-DejLkr_F.d.mts.map +1 -0
- package/dist/index-Dib1OE-e.d.cts +1098 -0
- package/dist/index-Dib1OE-e.d.cts.map +1 -0
- package/dist/index-DnvBKgec.d.cts +396 -0
- package/dist/index-DnvBKgec.d.cts.map +1 -0
- package/dist/index.cjs +85 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +15 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +15 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +24 -0
- package/dist/index.mjs.map +1 -0
- package/dist/registry/index.cjs +13 -0
- package/dist/registry/index.d.cts +2 -0
- package/dist/registry/index.d.mts +2 -0
- package/dist/registry/index.mjs +3 -0
- package/dist/registry-CBjRRqNv.mjs +144 -0
- package/dist/registry-CBjRRqNv.mjs.map +1 -0
- package/dist/registry-CWp2amuo.mjs +789 -0
- package/dist/registry-CWp2amuo.mjs.map +1 -0
- package/dist/registry-D5yh293y.cjs +857 -0
- package/dist/registry-D5yh293y.cjs.map +1 -0
- package/dist/registry-DNUNW6SH.cjs +163 -0
- package/dist/registry-DNUNW6SH.cjs.map +1 -0
- package/package.json +119 -0
- package/src/bin/frost.ts +218 -0
- package/src/cmd/busy.ts +64 -0
- package/src/cmd/check.ts +20 -0
- package/src/cmd/common.ts +40 -0
- package/src/cmd/dkg/common.ts +275 -0
- package/src/cmd/dkg/coordinator/finalize.ts +592 -0
- package/src/cmd/dkg/coordinator/index.ts +12 -0
- package/src/cmd/dkg/coordinator/invite.ts +217 -0
- package/src/cmd/dkg/coordinator/round1.ts +889 -0
- package/src/cmd/dkg/coordinator/round2.ts +959 -0
- package/src/cmd/dkg/index.ts +11 -0
- package/src/cmd/dkg/participant/finalize.ts +575 -0
- package/src/cmd/dkg/participant/index.ts +12 -0
- package/src/cmd/dkg/participant/receive.ts +348 -0
- package/src/cmd/dkg/participant/round1.ts +464 -0
- package/src/cmd/dkg/participant/round2.ts +627 -0
- package/src/cmd/index.ts +18 -0
- package/src/cmd/parallel.ts +334 -0
- package/src/cmd/registry/index.ts +88 -0
- package/src/cmd/registry/owner/index.ts +9 -0
- package/src/cmd/registry/owner/set.ts +70 -0
- package/src/cmd/registry/participant/add.ts +70 -0
- package/src/cmd/registry/participant/index.ts +9 -0
- package/src/cmd/sign/common.ts +108 -0
- package/src/cmd/sign/coordinator/index.ts +11 -0
- package/src/cmd/sign/coordinator/invite.ts +431 -0
- package/src/cmd/sign/coordinator/round1.ts +751 -0
- package/src/cmd/sign/coordinator/round2.ts +836 -0
- package/src/cmd/sign/index.ts +11 -0
- package/src/cmd/sign/participant/finalize.ts +823 -0
- package/src/cmd/sign/participant/index.ts +12 -0
- package/src/cmd/sign/participant/receive.ts +378 -0
- package/src/cmd/sign/participant/round1.ts +479 -0
- package/src/cmd/sign/participant/round2.ts +748 -0
- package/src/cmd/storage.ts +116 -0
- package/src/dkg/group-invite.ts +414 -0
- package/src/dkg/index.ts +10 -0
- package/src/dkg/proposed-participant.ts +132 -0
- package/src/frost/index.ts +456 -0
- package/src/index.ts +45 -0
- package/src/registry/group-record.ts +392 -0
- package/src/registry/index.ts +12 -0
- package/src/registry/owner-record.ts +146 -0
- package/src/registry/participant-record.ts +186 -0
- package/src/registry/registry-impl.ts +364 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign participant receive command.
|
|
3
|
+
*
|
|
4
|
+
* Port of cmd/sign/participant/receive.rs from frost-hubert-rust.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as fs from "node:fs";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
|
|
12
|
+
import { type ARID, type XID } from "@bcts/components";
|
|
13
|
+
import { CborDate } from "@bcts/dcbor";
|
|
14
|
+
import type { Envelope } from "@bcts/envelope";
|
|
15
|
+
|
|
16
|
+
import { Registry, resolveRegistryPath, type OwnerRecord } from "../../../registry/index.js";
|
|
17
|
+
import { getWithIndicator } from "../../busy.js";
|
|
18
|
+
import { type StorageClient, type StorageSelection } from "../../storage.js";
|
|
19
|
+
import { parseAridUr, parseEnvelopeUr, formatNameWithOwnerMarker } from "../../dkg/common.js";
|
|
20
|
+
import { signingStateDir } from "../common.js";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Options for the sign receive command.
|
|
24
|
+
*/
|
|
25
|
+
export interface SignReceiveOptions {
|
|
26
|
+
registryPath?: string;
|
|
27
|
+
/** ARID or envelope UR string */
|
|
28
|
+
request: string;
|
|
29
|
+
timeoutSeconds?: number;
|
|
30
|
+
/** Show request details only (info mode) */
|
|
31
|
+
info?: boolean;
|
|
32
|
+
/** Expected sender (XID UR or pet name) */
|
|
33
|
+
sender?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Result of the sign receive command.
|
|
38
|
+
*/
|
|
39
|
+
export interface SignReceiveResult {
|
|
40
|
+
sessionId: string;
|
|
41
|
+
groupId: string;
|
|
42
|
+
targetUr: string;
|
|
43
|
+
coordinatorName: string;
|
|
44
|
+
minSigners: number;
|
|
45
|
+
participantNames: string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Resolve sender from XID UR or pet name in registry.
|
|
50
|
+
*
|
|
51
|
+
* Port of `resolve_sender()` from cmd/dkg/common.rs lines 76-94.
|
|
52
|
+
*/
|
|
53
|
+
function resolveSenderFromInput(registry: Registry, input: string): { xid: () => XID } {
|
|
54
|
+
const trimmed = input.trim();
|
|
55
|
+
if (trimmed === "") {
|
|
56
|
+
throw new Error("Sender is required");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Try parsing as XID UR first
|
|
60
|
+
try {
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports, no-undef
|
|
62
|
+
const { XID: XIDClass } = require("@bcts/components");
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
64
|
+
const xid = XIDClass.fromURString(trimmed) as XID;
|
|
65
|
+
const record = registry.participant(xid);
|
|
66
|
+
if (!record) {
|
|
67
|
+
throw new Error(`Sender with XID ${xid.urString()} not found`);
|
|
68
|
+
}
|
|
69
|
+
return record.xidDocument();
|
|
70
|
+
} catch {
|
|
71
|
+
// Try looking up by pet name
|
|
72
|
+
const result = registry.participantByPetName(trimmed);
|
|
73
|
+
if (!result) {
|
|
74
|
+
throw new Error(`Sender with pet name '${trimmed}' not found`);
|
|
75
|
+
}
|
|
76
|
+
return result[1].xidDocument();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Resolve sign invite from ARID or envelope UR.
|
|
82
|
+
*
|
|
83
|
+
* Port of `resolve_sign_request()` from cmd/sign/participant/receive.rs lines 250-284.
|
|
84
|
+
*/
|
|
85
|
+
async function resolveSignInviteEnvelope(
|
|
86
|
+
client: StorageClient | undefined,
|
|
87
|
+
selection: StorageSelection | undefined,
|
|
88
|
+
request: string,
|
|
89
|
+
timeout: number | undefined,
|
|
90
|
+
): Promise<Envelope> {
|
|
91
|
+
if (selection !== undefined && client !== undefined) {
|
|
92
|
+
// Try to parse as ARID
|
|
93
|
+
try {
|
|
94
|
+
const arid = parseAridUr(request);
|
|
95
|
+
const envelope = await getWithIndicator(client, arid, "Sign invite", timeout, false);
|
|
96
|
+
if (envelope === undefined || envelope === null) {
|
|
97
|
+
throw new Error("signInvite request not found in Hubert storage");
|
|
98
|
+
}
|
|
99
|
+
return envelope;
|
|
100
|
+
} catch {
|
|
101
|
+
// Not an ARID, try as envelope
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (timeout !== undefined) {
|
|
105
|
+
throw new Error("--timeout is only valid when retrieving requests from Hubert");
|
|
106
|
+
}
|
|
107
|
+
return parseEnvelopeUr(request);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// No storage selection
|
|
111
|
+
try {
|
|
112
|
+
parseAridUr(request);
|
|
113
|
+
throw new Error("Hubert storage parameters are required to retrieve requests by ARID");
|
|
114
|
+
} catch (e) {
|
|
115
|
+
if (e instanceof Error && e.message.includes("Hubert storage parameters")) {
|
|
116
|
+
throw e;
|
|
117
|
+
}
|
|
118
|
+
// Not an ARID, parse as envelope
|
|
119
|
+
}
|
|
120
|
+
return parseEnvelopeUr(request);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get display name for sender from registry.
|
|
125
|
+
*
|
|
126
|
+
* Port of `resolve_sender_name()` from cmd/dkg/common.rs lines 96-116.
|
|
127
|
+
*/
|
|
128
|
+
function resolveSenderName(registry: Registry, senderXid: XID): string | undefined {
|
|
129
|
+
const owner = registry.owner();
|
|
130
|
+
|
|
131
|
+
// Check if sender is the owner
|
|
132
|
+
if (owner?.xid().urString() === senderXid.urString()) {
|
|
133
|
+
const name = owner.petName() ?? senderXid.urString();
|
|
134
|
+
return formatNameWithOwnerMarker(name, true);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Look up in participants
|
|
138
|
+
const record = registry.participant(senderXid);
|
|
139
|
+
if (record) {
|
|
140
|
+
const name = record.petName() ?? record.xid().urString();
|
|
141
|
+
return formatNameWithOwnerMarker(name, false);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Format participant names with owner marker.
|
|
149
|
+
*
|
|
150
|
+
* Port of `format_participant_names()` from cmd/sign/participant/receive.rs lines 286-309.
|
|
151
|
+
*/
|
|
152
|
+
function formatParticipantNames(
|
|
153
|
+
registry: Registry,
|
|
154
|
+
participants: XID[],
|
|
155
|
+
owner: OwnerRecord,
|
|
156
|
+
): string[] {
|
|
157
|
+
return participants.map((xid) => {
|
|
158
|
+
const isOwner = xid.urString() === owner.xid().urString();
|
|
159
|
+
let name: string;
|
|
160
|
+
|
|
161
|
+
if (isOwner) {
|
|
162
|
+
name = owner.petName() ?? xid.urString();
|
|
163
|
+
} else {
|
|
164
|
+
const record = registry.participant(xid);
|
|
165
|
+
name = record?.petName() ?? xid.urString();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return formatNameWithOwnerMarker(name, isOwner);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Execute the sign participant receive command.
|
|
174
|
+
*
|
|
175
|
+
* Fetches and validates a sign invite from the coordinator.
|
|
176
|
+
*
|
|
177
|
+
* Port of `CommandArgs::exec()` from cmd/sign/participant/receive.rs lines 56-247.
|
|
178
|
+
*/
|
|
179
|
+
export async function receive(
|
|
180
|
+
client: StorageClient | undefined,
|
|
181
|
+
selection: StorageSelection | undefined,
|
|
182
|
+
options: SignReceiveOptions,
|
|
183
|
+
cwd: string,
|
|
184
|
+
): Promise<SignReceiveResult> {
|
|
185
|
+
// Validate timeout requires storage
|
|
186
|
+
if (selection === undefined && options.timeoutSeconds !== undefined) {
|
|
187
|
+
throw new Error("--timeout requires Hubert storage parameters");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const registryPath = resolveRegistryPath(options.registryPath, cwd);
|
|
191
|
+
const registry = Registry.load(registryPath);
|
|
192
|
+
const owner = registry.owner();
|
|
193
|
+
|
|
194
|
+
if (!owner) {
|
|
195
|
+
throw new Error("Registry owner with private keys is required");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Resolve expected sender if provided
|
|
199
|
+
let expectedSender: { xid: () => XID } | undefined;
|
|
200
|
+
if (options.sender !== undefined && options.sender !== "") {
|
|
201
|
+
expectedSender = resolveSenderFromInput(registry, options.sender);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Resolve the invite envelope
|
|
205
|
+
const envelope = await resolveSignInviteEnvelope(
|
|
206
|
+
client,
|
|
207
|
+
selection,
|
|
208
|
+
options.request,
|
|
209
|
+
options.timeoutSeconds,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const now: CborDate = CborDate.now();
|
|
213
|
+
const recipientKeys = owner.xidDocument().inceptionPrivateKeys();
|
|
214
|
+
|
|
215
|
+
if (recipientKeys === null || recipientKeys === undefined) {
|
|
216
|
+
throw new Error("Owner XID document has no inception private keys");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, no-undef
|
|
220
|
+
const { SealedRequest: SealedRequestClass } = require("@bcts/gstp") as {
|
|
221
|
+
SealedRequest: {
|
|
222
|
+
tryFromEnvelope: (
|
|
223
|
+
envelope: Envelope,
|
|
224
|
+
expectedSender: XID | undefined,
|
|
225
|
+
now: CborDate,
|
|
226
|
+
recipientPrivateKeys: unknown,
|
|
227
|
+
) => SealedRequestInstance;
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
interface SealedRequestInstance {
|
|
232
|
+
sender: () => { xid: () => XID };
|
|
233
|
+
function: () => { equals?: (other: unknown) => boolean; toString?: () => string };
|
|
234
|
+
extractObjectForParameter: <T>(name: string) => T;
|
|
235
|
+
objectForParameter: (name: string) => Envelope;
|
|
236
|
+
objectsForParameter: (name: string) => ParticipantEntry[];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
interface ParticipantEntry {
|
|
240
|
+
extractSubject: () => XID;
|
|
241
|
+
objectForPredicate: (name: string) => {
|
|
242
|
+
decryptToRecipient: (keys: unknown) => {
|
|
243
|
+
extractSubject: () => ARID;
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const sealedRequest: SealedRequestInstance = SealedRequestClass.tryFromEnvelope(
|
|
249
|
+
envelope,
|
|
250
|
+
undefined,
|
|
251
|
+
now,
|
|
252
|
+
recipientKeys,
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// Validate sender
|
|
256
|
+
const senderXid = sealedRequest.sender().xid();
|
|
257
|
+
|
|
258
|
+
if (expectedSender !== undefined) {
|
|
259
|
+
if (senderXid.urString() !== expectedSender.xid().urString()) {
|
|
260
|
+
throw new Error(
|
|
261
|
+
`Request sender does not match expected sender (got ${senderXid.urString()}, expected ${expectedSender.xid().urString()})`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
const knownOwner = owner.xid().urString() === senderXid.urString();
|
|
266
|
+
const knownParticipant = registry.participant(senderXid) !== undefined;
|
|
267
|
+
if (!knownOwner && !knownParticipant) {
|
|
268
|
+
throw new Error(`Request sender not found in registry: ${senderXid.urString()}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, no-undef
|
|
273
|
+
const { Function: FunctionClass } = require("@bcts/envelope") as {
|
|
274
|
+
Function: { from: (name: string) => unknown };
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// Validate function
|
|
278
|
+
const requestFunction = sealedRequest.function();
|
|
279
|
+
const expectedFunction = FunctionClass.from("signInvite");
|
|
280
|
+
const functionMatches =
|
|
281
|
+
requestFunction.equals !== undefined
|
|
282
|
+
? requestFunction.equals(expectedFunction)
|
|
283
|
+
: String(requestFunction) === String(expectedFunction);
|
|
284
|
+
|
|
285
|
+
if (!functionMatches) {
|
|
286
|
+
throw new Error(`Unexpected request function: ${String(requestFunction)}`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Extract parameters
|
|
290
|
+
const validUntil = sealedRequest.extractObjectForParameter<CborDate>("validUntil");
|
|
291
|
+
if (validUntil <= now) {
|
|
292
|
+
throw new Error("signInvite request has expired");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const groupId = sealedRequest.extractObjectForParameter<ARID>("group");
|
|
296
|
+
const sessionId = sealedRequest.extractObjectForParameter<ARID>("session");
|
|
297
|
+
const minSigners = Number(sealedRequest.extractObjectForParameter<bigint | number>("minSigners"));
|
|
298
|
+
|
|
299
|
+
// Extract participants and find our response ARID
|
|
300
|
+
const participantEntries = sealedRequest.objectsForParameter("participant");
|
|
301
|
+
const participants: XID[] = [];
|
|
302
|
+
let responseArid: ARID | undefined;
|
|
303
|
+
|
|
304
|
+
for (const entry of participantEntries) {
|
|
305
|
+
const xid: XID = entry.extractSubject();
|
|
306
|
+
if (xid.urString() === owner.xid().urString()) {
|
|
307
|
+
const encryptedArid = entry.objectForPredicate("response_arid");
|
|
308
|
+
const aridEnv = encryptedArid.decryptToRecipient(recipientKeys);
|
|
309
|
+
responseArid = aridEnv.extractSubject();
|
|
310
|
+
}
|
|
311
|
+
participants.push(xid);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Validations
|
|
315
|
+
if (participants.length === 0) {
|
|
316
|
+
throw new Error("signInvite request contains no participants");
|
|
317
|
+
}
|
|
318
|
+
if (minSigners < 2) {
|
|
319
|
+
throw new Error("minSigners must be at least 2");
|
|
320
|
+
}
|
|
321
|
+
if (minSigners > participants.length) {
|
|
322
|
+
throw new Error("minSigners exceeds participant count");
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const ownerInParticipants = participants.some((p) => p.urString() === owner.xid().urString());
|
|
326
|
+
if (!ownerInParticipants) {
|
|
327
|
+
throw new Error("signInvite request does not include this participant");
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (responseArid === undefined) {
|
|
331
|
+
throw new Error("signInvite request missing response ARID");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Sort participants by XID
|
|
335
|
+
participants.sort((a, b) => a.urString().localeCompare(b.urString()));
|
|
336
|
+
|
|
337
|
+
const targetEnvelope = sealedRequest.objectForParameter("target");
|
|
338
|
+
|
|
339
|
+
const coordinatorName = resolveSenderName(registry, senderXid) ?? senderXid.urString();
|
|
340
|
+
const participantNames = formatParticipantNames(registry, participants, owner);
|
|
341
|
+
|
|
342
|
+
// Output
|
|
343
|
+
console.log(`Group: ${groupId.urString()}`);
|
|
344
|
+
console.log(`Coordinator: ${coordinatorName}`);
|
|
345
|
+
console.log(`Min signers: ${minSigners}`);
|
|
346
|
+
console.log(`Participants: ${participantNames.join(", ")}`);
|
|
347
|
+
console.log("Target:");
|
|
348
|
+
console.log(targetEnvelope.format());
|
|
349
|
+
|
|
350
|
+
// Primary output for scripting: session ID on its own line (no header)
|
|
351
|
+
console.log(sessionId.urString());
|
|
352
|
+
|
|
353
|
+
// Persist request details for follow-up commands
|
|
354
|
+
const stateDir = signingStateDir(registryPath, groupId.hex(), sessionId.hex());
|
|
355
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
356
|
+
|
|
357
|
+
const root: Record<string, unknown> = {
|
|
358
|
+
request_envelope: envelope.urString(),
|
|
359
|
+
group: groupId.urString(),
|
|
360
|
+
session: sessionId.urString(),
|
|
361
|
+
coordinator: senderXid.urString(),
|
|
362
|
+
min_signers: minSigners,
|
|
363
|
+
response_arid: responseArid.urString(),
|
|
364
|
+
participants: participants.map((xid) => xid.urString()),
|
|
365
|
+
target: targetEnvelope.urString(),
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
fs.writeFileSync(path.join(stateDir, "sign_receive.json"), JSON.stringify(root, null, 2));
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
sessionId: sessionId.urString(),
|
|
372
|
+
groupId: groupId.urString(),
|
|
373
|
+
targetUr: targetEnvelope.urString(),
|
|
374
|
+
coordinatorName,
|
|
375
|
+
minSigners,
|
|
376
|
+
participantNames,
|
|
377
|
+
};
|
|
378
|
+
}
|