@bcts/frost-hubert 1.0.0-beta.2 → 1.0.0-beta.3
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/bin/frost.mjs +16 -16
- package/dist/cmd/index.mjs +3 -3
- package/dist/{cmd-ChW3eHA8.mjs → cmd-BOzLt8T8.mjs} +20 -20
- package/dist/{cmd-ChW3eHA8.mjs.map → cmd-BOzLt8T8.mjs.map} +1 -1
- package/dist/{common--IfAHVkl.mjs → common-DCyUSYkT.mjs} +2 -2
- package/dist/{common--IfAHVkl.mjs.map → common-DCyUSYkT.mjs.map} +1 -1
- package/dist/{common-4spC1kNw.mjs → common-k2QC6AST.mjs} +2 -2
- package/dist/{common-4spC1kNw.mjs.map → common-k2QC6AST.mjs.map} +1 -1
- package/dist/{finalize-BAwtGCQW.mjs → finalize-BVOveyol.mjs} +4 -4
- package/dist/{finalize-BAwtGCQW.mjs.map → finalize-BVOveyol.mjs.map} +1 -1
- package/dist/{finalize-Bm5RZIox.mjs → finalize-Bg0-BYxV.mjs} +3 -3
- package/dist/{finalize-Bm5RZIox.mjs.map → finalize-Bg0-BYxV.mjs.map} +1 -1
- package/dist/{finalize-CELJsR4C.mjs → finalize-f9hvnJ2R.mjs} +4 -4
- package/dist/{finalize-CELJsR4C.mjs.map → finalize-f9hvnJ2R.mjs.map} +1 -1
- package/dist/frost/index.mjs +1 -1
- package/dist/index.mjs +3 -3
- package/dist/{invite-vRv9LMEE.mjs → invite-B4pfTK_5.mjs} +3 -3
- package/dist/{invite-vRv9LMEE.mjs.map → invite-B4pfTK_5.mjs.map} +1 -1
- package/dist/{invite-DNlTkwzM.mjs → invite-CPLnWY7c.mjs} +4 -4
- package/dist/{invite-DNlTkwzM.mjs.map → invite-CPLnWY7c.mjs.map} +1 -1
- package/dist/{receive-C2Dwb_FQ.mjs → receive-Bl_utFpz.mjs} +4 -4
- package/dist/{receive-C2Dwb_FQ.mjs.map → receive-Bl_utFpz.mjs.map} +1 -1
- package/dist/{receive-Bzn87ahI.mjs → receive-CYmQ_x3S.mjs} +4 -4
- package/dist/{receive-Bzn87ahI.mjs.map → receive-CYmQ_x3S.mjs.map} +1 -1
- package/dist/{registry-ycylEdoc.mjs → registry-DkhElxx1.mjs} +2 -2
- package/dist/{registry-ycylEdoc.mjs.map → registry-DkhElxx1.mjs.map} +1 -1
- package/dist/{rolldown-runtime-z9aeyW2b.mjs → rolldown-runtime-BhDjJH2R.mjs} +1 -1
- package/dist/{round1-BRgDPU4Y.mjs → round1-5RsoUTsC.mjs} +4 -4
- package/dist/{round1-BRgDPU4Y.mjs.map → round1-5RsoUTsC.mjs.map} +1 -1
- package/dist/{round1-_fHip4H1.mjs → round1-B0j_4sJq.mjs} +5 -5
- package/dist/{round1-_fHip4H1.mjs.map → round1-B0j_4sJq.mjs.map} +1 -1
- package/dist/{round1-DSaOFCar.mjs → round1-DC5LcN2C.mjs} +3 -3
- package/dist/{round1-DSaOFCar.mjs.map → round1-DC5LcN2C.mjs.map} +1 -1
- package/dist/{round1-DL4N2QJ5.mjs → round1-I9T20net.mjs} +5 -5
- package/dist/{round1-DL4N2QJ5.mjs.map → round1-I9T20net.mjs.map} +1 -1
- package/dist/{round2-DmQvJDVj.mjs → round2-BmpKBQwi.mjs} +4 -4
- package/dist/{round2-DmQvJDVj.mjs.map → round2-BmpKBQwi.mjs.map} +1 -1
- package/dist/{round2-Cq2SD6F8.mjs → round2-C0SsEt1J.mjs} +3 -3
- package/dist/{round2-Cq2SD6F8.mjs.map → round2-C0SsEt1J.mjs.map} +1 -1
- package/dist/{round2-CU-yTXHT.mjs → round2-CXkgjjyf.mjs} +4 -4
- package/dist/{round2-CU-yTXHT.mjs.map → round2-CXkgjjyf.mjs.map} +1 -1
- package/dist/{round2-BLOgZeAo.mjs → round2-DrZDN8Tt.mjs} +4 -4
- package/dist/{round2-BLOgZeAo.mjs.map → round2-DrZDN8Tt.mjs.map} +1 -1
- package/package.json +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"finalize-Bm5RZIox.mjs","names":[],"sources":["../src/cmd/dkg/coordinator/finalize.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * DKG coordinator finalize command.\n *\n * Port of cmd/dkg/coordinator/finalize.rs from frost-hubert-rust.\n *\n * @module\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { type ARID, type PrivateKeys, type SigningPublicKey, type XID } from \"@bcts/components\";\nimport { type Envelope } from \"@bcts/envelope\";\nimport { SealedResponse } from \"@bcts/gstp\";\n\nimport {\n type GroupRecord,\n type PendingRequests,\n Registry,\n resolveRegistryPath,\n} from \"../../../registry/index.js\";\nimport { groupStateDir, isVerbose } from \"../../common.js\";\nimport {\n type CollectionResult,\n parallelFetch,\n type ParallelFetchConfig,\n parallelFetchConfigWithTimeout,\n} from \"../../parallel.js\";\nimport { type StorageClient } from \"../../storage.js\";\nimport { getWithIndicator } from \"../../busy.js\";\nimport { parseAridUr, signingKeyFromVerifying } from \"../common.js\";\n\n/**\n * Options for the DKG finalize command.\n */\nexport interface DkgFinalizeOptions {\n registryPath?: string;\n groupId: string;\n parallel?: boolean;\n timeoutSeconds?: number;\n verbose?: boolean;\n}\n\n/**\n * Result of the DKG finalize command.\n */\nexport interface DkgFinalizeResult {\n verifyingKey: string;\n collected: number;\n rejected: number;\n errors: number;\n timeouts: number;\n}\n\n/**\n * Data extracted from a successful finalize response.\n *\n * Port of `struct FinalizeResponseData` from finalize.rs.\n */\ninterface FinalizeResponseData {\n keyPackage: unknown;\n publicKeyPackage: unknown;\n}\n\n/**\n * Entry for a collected finalize response.\n *\n * Port of `struct FinalizeEntry` from finalize.rs.\n */\ninterface FinalizeEntry {\n participant: XID;\n keyPackage: unknown;\n publicKeyPackage: unknown;\n}\n\n/**\n * Validate that the owner is the coordinator of the group.\n *\n * Port of coordinator check from finalize.rs lines 76-82.\n */\nfunction validateCoordinator(groupRecord: GroupRecord, ownerXid: XID): void {\n if (groupRecord.coordinator().xid().urString() !== ownerXid.urString()) {\n throw new Error(\n `Only the coordinator can collect finalize responses. Coordinator: ${groupRecord.coordinator().xid().urString()}, Owner: ${ownerXid.urString()}`,\n );\n }\n}\n\n/**\n * Validate envelope and extract finalize data (for parallel fetch).\n *\n * Port of `validate_and_extract_finalize_response()` from finalize.rs lines 407-466.\n */\nfunction validateAndExtractFinalizeResponse(\n envelope: Envelope,\n coordinatorKeys: PrivateKeys,\n expectedGroupId: ARID,\n expectedParticipant: XID,\n): FinalizeResponseData | { rejected: string } {\n const now = new Date();\n\n let sealed: SealedResponse;\n try {\n sealed = SealedResponse.tryFromEncryptedEnvelope(envelope, undefined, now, coordinatorKeys);\n } catch (err) {\n return {\n rejected: `Failed to decrypt/parse response: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n // Check for error response\n if (!sealed.isOk()) {\n try {\n const error = sealed.error();\n const reasonEnv = error.optionalObjectForPredicate(\"reason\");\n const reason = reasonEnv?.extractString() ?? \"unknown reason\";\n return { rejected: `Participant reported error: ${reason}` };\n } catch {\n return { rejected: \"Participant reported error: unknown reason\" };\n }\n }\n\n // Get and validate result\n let result: Envelope;\n try {\n result = sealed.result();\n } catch {\n return { rejected: \"Finalize response has no result\" };\n }\n\n // Validate response type\n try {\n result.checkSubjectUnit();\n result.checkType(\"dkgFinalizeResponse\");\n } catch (err) {\n return {\n rejected: `Invalid response type: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n // Validate group ID\n try {\n const groupEnv = result.objectForPredicate(\"group\");\n const groupIdStr = groupEnv.extractString();\n const groupId = parseAridUr(groupIdStr);\n if (groupId.urString() !== expectedGroupId.urString()) {\n return {\n rejected: `Group ${groupId.urString()} does not match expected ${expectedGroupId.urString()}`,\n };\n }\n } catch (err) {\n return {\n rejected: `Failed to extract group: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n // Validate participant\n try {\n const participantEnv = result.objectForPredicate(\"participant\");\n const participantStr = participantEnv.extractString();\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports, no-undef\n const { XID: XIDClass } = require(\"@bcts/components\");\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access\n const participantXid = XIDClass.fromURString(participantStr) as XID;\n if (participantXid.urString() !== expectedParticipant.urString()) {\n return {\n rejected: `Participant ${participantXid.urString()} does not match expected ${expectedParticipant.urString()}`,\n };\n }\n } catch (err) {\n return {\n rejected: `Failed to extract participant: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n // Extract key packages\n let keyPackage: unknown;\n let publicKeyPackage: unknown;\n\n try {\n const keyJsonEnv = result.objectForPredicate(\"key_package\");\n const keyJsonStr = keyJsonEnv.extractString();\n keyPackage = JSON.parse(keyJsonStr);\n } catch (err) {\n return {\n rejected: `Failed to parse key_package: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n try {\n const pubJsonEnv = result.objectForPredicate(\"public_key_package\");\n const pubJsonStr = pubJsonEnv.extractString();\n publicKeyPackage = JSON.parse(pubJsonStr);\n } catch (err) {\n return {\n rejected: `Failed to parse public_key_package: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n return { keyPackage, publicKeyPackage };\n}\n\n/**\n * Fetch a finalize response sequentially.\n *\n * Port of `fetch_finalize_response()` from finalize.rs lines 282-358.\n */\nasync function fetchFinalizeResponse(\n client: StorageClient,\n responseArid: ARID,\n timeout: number | undefined,\n coordinatorKeys: PrivateKeys,\n expectedGroup: ARID,\n expectedParticipant: XID,\n participantName: string,\n): Promise<FinalizeEntry> {\n const envelope = await getWithIndicator(\n client,\n responseArid,\n participantName,\n timeout,\n isVerbose(),\n );\n\n if (envelope === null || envelope === undefined) {\n throw new Error(\"Finalize response not found in Hubert storage\");\n }\n\n const result = validateAndExtractFinalizeResponse(\n envelope,\n coordinatorKeys,\n expectedGroup,\n expectedParticipant,\n );\n\n if (\"rejected\" in result) {\n throw new Error(result.rejected);\n }\n\n return {\n participant: expectedParticipant,\n keyPackage: result.keyPackage,\n publicKeyPackage: result.publicKeyPackage,\n };\n}\n\n/**\n * Collect finalize responses in parallel with progress display.\n *\n * Port of `collect_finalize_parallel()` from finalize.rs lines 371-404.\n */\nasync function collectFinalizeParallel(\n client: StorageClient,\n registry: Registry,\n pendingRequests: PendingRequests,\n coordinatorKeys: PrivateKeys,\n expectedGroupId: ARID,\n timeout: number | undefined,\n): Promise<CollectionResult<FinalizeResponseData>> {\n const requests: [XID, ARID, string][] = [];\n\n for (const [xid, arid] of pendingRequests.iterCollect()) {\n const record = registry.participant(xid);\n const name = record?.petName() ?? xid.urString();\n requests.push([xid, arid, name]);\n }\n\n const config: ParallelFetchConfig = parallelFetchConfigWithTimeout(timeout);\n\n return parallelFetch(\n client,\n requests,\n (envelope: Envelope, xid: XID) =>\n validateAndExtractFinalizeResponse(envelope, coordinatorKeys, expectedGroupId, xid),\n config,\n );\n}\n\n/**\n * Finalize collection results: persist, update registry, print summary.\n *\n * Port of `finalize_collection_results()` from finalize.rs lines 469-590.\n */\nfunction finalizeFinalizeCollectionResults(\n collection: CollectionResult<FinalizeResponseData>,\n registryPath: string,\n registry: Registry,\n groupId: ARID,\n): SigningPublicKey | undefined {\n // Report any failures\n if (collection.rejections.length > 0) {\n console.error();\n console.error(\"Rejections:\");\n for (const [xid, reason] of collection.rejections) {\n console.error(` ${xid.urString()}: ${reason}`);\n }\n }\n if (collection.errors.length > 0) {\n console.error();\n console.error(\"Errors:\");\n for (const [xid, error] of collection.errors) {\n console.error(` ${xid.urString()}: ${error}`);\n }\n }\n if (collection.timeouts.length > 0) {\n console.error();\n console.error(\"Timeouts:\");\n for (const xid of collection.timeouts) {\n console.error(` ${xid.urString()}`);\n }\n }\n\n if (!collection.allSucceeded()) {\n throw new Error(\n `Finalize collection incomplete: ${collection.successes.length} succeeded, ` +\n `${collection.rejections.length} rejected, ${collection.errors.length} errors, ` +\n `${collection.timeouts.length} timeouts`,\n );\n }\n\n // Validate group verifying key consistency\n let groupVerifyingKey: SigningPublicKey | undefined;\n\n for (const [xid, data] of collection.successes) {\n // Extract verifying_key from public_key_package\n const pubKeyPkg = data.publicKeyPackage as { verifying_key?: string };\n if (!pubKeyPkg.verifying_key) {\n throw new Error(\n `Failed to extract verifying key for ${xid.urString()}: missing verifying_key field`,\n );\n }\n\n // The verifying key is typically hex-encoded\n let signingKey: SigningPublicKey;\n try {\n const verifyingKeyBytes = hexToBytes(pubKeyPkg.verifying_key);\n signingKey = signingKeyFromVerifying(verifyingKeyBytes) as SigningPublicKey;\n } catch (err) {\n throw new Error(\n `Failed to extract verifying key for ${xid.urString()}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n\n if (groupVerifyingKey !== undefined) {\n if (groupVerifyingKey.urString() !== signingKey.urString()) {\n throw new Error(`Group verifying key mismatch for participant ${xid.urString()}`);\n }\n } else {\n groupVerifyingKey = signingKey;\n }\n }\n\n // Persist collected finalize data\n const stateDir = groupStateDir(registryPath, groupId.hex());\n fs.mkdirSync(stateDir, { recursive: true });\n\n const collectedPath = path.join(stateDir, \"collected_finalize.json\");\n const root: Record<string, { key_package: unknown; public_key_package: unknown }> = {};\n\n for (const [xid, data] of collection.successes) {\n root[xid.urString()] = {\n key_package: data.keyPackage,\n public_key_package: data.publicKeyPackage,\n };\n }\n\n fs.writeFileSync(collectedPath, JSON.stringify(root, null, 2));\n\n // Update registry\n const groupRecord = registry.group(groupId);\n if (groupRecord === undefined) {\n throw new Error(\"Group not found in registry\");\n }\n\n if (groupVerifyingKey !== undefined) {\n groupRecord.setVerifyingKey(groupVerifyingKey);\n }\n groupRecord.clearPendingRequests();\n registry.save(registryPath);\n\n if (isVerbose()) {\n console.error();\n console.error(\n `Collected ${collection.successes.length} finalize responses. Saved to ${collectedPath}`,\n );\n if (groupVerifyingKey !== undefined) {\n console.error(groupVerifyingKey.urString());\n }\n } else if (groupVerifyingKey !== undefined) {\n console.log(groupVerifyingKey.urString());\n }\n\n return groupVerifyingKey;\n}\n\n/**\n * Helper to convert hex string to bytes.\n */\nfunction hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Execute the DKG coordinator finalize command.\n *\n * Collects finalize responses (key/public key packages) from all participants.\n *\n * Port of `finalize()` from cmd/dkg/coordinator/finalize.rs.\n */\nexport async function finalize(\n client: StorageClient,\n options: DkgFinalizeOptions,\n cwd: string,\n): Promise<DkgFinalizeResult> {\n const registryPath = resolveRegistryPath(options.registryPath, cwd);\n const registry = Registry.load(registryPath);\n\n const owner = registry.owner();\n if (owner === undefined) {\n throw new Error(\"Registry owner is required\");\n }\n\n const groupId = parseAridUr(options.groupId);\n const groupRecord = registry.group(groupId);\n\n if (groupRecord === undefined) {\n throw new Error(`Group ${options.groupId} not found in registry`);\n }\n\n // Validate that owner is the coordinator\n validateCoordinator(groupRecord, owner.xid());\n\n const pendingRequests = groupRecord.pendingRequests();\n if (pendingRequests.isEmpty()) {\n throw new Error(\n \"No pending requests for this group. Did you run 'frost dkg coordinator finalize send'?\",\n );\n }\n\n const coordinatorKeys = owner.xidDocument().inceptionPrivateKeys();\n if (coordinatorKeys === undefined) {\n throw new Error(\"Coordinator XID document has no private keys\");\n }\n\n let verifyingKey: SigningPublicKey | undefined;\n let collected: number;\n let rejected = 0;\n let errors: number;\n let timeouts = 0;\n\n if (options.parallel === true) {\n // Parallel path with progress display\n const collection = await collectFinalizeParallel(\n client,\n registry,\n pendingRequests,\n coordinatorKeys,\n groupId,\n options.timeoutSeconds,\n );\n\n verifyingKey = finalizeFinalizeCollectionResults(collection, registryPath, registry, groupId);\n\n collected = collection.successes.length;\n rejected = collection.rejections.length;\n errors = collection.errors.length;\n timeouts = collection.timeouts.length;\n } else {\n // Sequential path (original behavior)\n const collectedEntries: FinalizeEntry[] = [];\n const errorEntries: [XID, string][] = [];\n let groupVerifyingKey: SigningPublicKey | undefined;\n\n if (isVerbose()) {\n console.error(`Collecting finalize responses from ${pendingRequests.len()} participants...`);\n }\n\n for (const [participantXid, collectFromArid] of pendingRequests.iterCollect()) {\n const record = registry.participant(participantXid);\n const name = record?.petName() ?? participantXid.urString();\n\n try {\n const entry = await fetchFinalizeResponse(\n client,\n collectFromArid,\n options.timeoutSeconds,\n coordinatorKeys,\n groupId,\n participantXid,\n name,\n );\n\n // Extract verifying key from public_key_package\n const pubKeyPkg = entry.publicKeyPackage as { verifying_key?: string };\n if (!pubKeyPkg.verifying_key) {\n throw new Error(\"missing verifying_key field\");\n }\n\n const verifyingKeyBytes = hexToBytes(pubKeyPkg.verifying_key);\n const signingKey = signingKeyFromVerifying(verifyingKeyBytes) as SigningPublicKey;\n\n if (groupVerifyingKey !== undefined) {\n if (groupVerifyingKey.urString() !== signingKey.urString()) {\n if (isVerbose()) {\n console.error(\"error: group verifying key mismatch\");\n }\n errorEntries.push([participantXid, \"Group verifying key mismatch across responses\"]);\n continue;\n }\n } else {\n groupVerifyingKey = signingKey;\n }\n\n collectedEntries.push(entry);\n } catch (err) {\n if (isVerbose()) {\n console.error(`error: ${err instanceof Error ? err.message : String(err)}`);\n }\n errorEntries.push([participantXid, err instanceof Error ? err.message : String(err)]);\n }\n }\n\n if (errorEntries.length > 0) {\n if (isVerbose()) {\n console.error();\n console.error(`Failed to collect from ${errorEntries.length} participants:`);\n for (const [xid, error] of errorEntries) {\n console.error(` ${xid.urString()}: ${error}`);\n }\n }\n throw new Error(\n `Finalize collection incomplete: ${errorEntries.length} of ${pendingRequests.len()} responses failed`,\n );\n }\n\n // Persist collected finalize data\n const stateDir = groupStateDir(registryPath, groupId.hex());\n fs.mkdirSync(stateDir, { recursive: true });\n\n const collectedPath = path.join(stateDir, \"collected_finalize.json\");\n const root: Record<string, { key_package: unknown; public_key_package: unknown }> = {};\n\n for (const entry of collectedEntries) {\n root[entry.participant.urString()] = {\n key_package: entry.keyPackage,\n public_key_package: entry.publicKeyPackage,\n };\n }\n\n fs.writeFileSync(collectedPath, JSON.stringify(root, null, 2));\n\n // Update registry pending requests cleared\n const groupRecordMut = registry.group(groupId);\n if (groupRecordMut === undefined) {\n throw new Error(\"Group not found in registry\");\n }\n\n if (groupVerifyingKey !== undefined) {\n groupRecordMut.setVerifyingKey(groupVerifyingKey);\n }\n groupRecordMut.clearPendingRequests();\n registry.save(registryPath);\n\n if (isVerbose()) {\n console.error();\n console.error(\n `Collected ${collectedEntries.length} finalize responses. Saved to ${collectedPath}`,\n );\n if (groupVerifyingKey !== undefined) {\n console.error(groupVerifyingKey.urString());\n }\n } else if (groupVerifyingKey !== undefined) {\n console.log(groupVerifyingKey.urString());\n }\n\n verifyingKey = groupVerifyingKey;\n collected = collectedEntries.length;\n errors = errorEntries.length;\n }\n\n return {\n verifyingKey: verifyingKey?.urString() ?? \"\",\n collected,\n rejected,\n errors,\n timeouts,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"finalize-Bg0-BYxV.mjs","names":[],"sources":["../src/cmd/dkg/coordinator/finalize.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * DKG coordinator finalize command.\n *\n * Port of cmd/dkg/coordinator/finalize.rs from frost-hubert-rust.\n *\n * @module\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { type ARID, type PrivateKeys, type SigningPublicKey, type XID } from \"@bcts/components\";\nimport { type Envelope } from \"@bcts/envelope\";\nimport { SealedResponse } from \"@bcts/gstp\";\n\nimport {\n type GroupRecord,\n type PendingRequests,\n Registry,\n resolveRegistryPath,\n} from \"../../../registry/index.js\";\nimport { groupStateDir, isVerbose } from \"../../common.js\";\nimport {\n type CollectionResult,\n parallelFetch,\n type ParallelFetchConfig,\n parallelFetchConfigWithTimeout,\n} from \"../../parallel.js\";\nimport { type StorageClient } from \"../../storage.js\";\nimport { getWithIndicator } from \"../../busy.js\";\nimport { parseAridUr, signingKeyFromVerifying } from \"../common.js\";\n\n/**\n * Options for the DKG finalize command.\n */\nexport interface DkgFinalizeOptions {\n registryPath?: string;\n groupId: string;\n parallel?: boolean;\n timeoutSeconds?: number;\n verbose?: boolean;\n}\n\n/**\n * Result of the DKG finalize command.\n */\nexport interface DkgFinalizeResult {\n verifyingKey: string;\n collected: number;\n rejected: number;\n errors: number;\n timeouts: number;\n}\n\n/**\n * Data extracted from a successful finalize response.\n *\n * Port of `struct FinalizeResponseData` from finalize.rs.\n */\ninterface FinalizeResponseData {\n keyPackage: unknown;\n publicKeyPackage: unknown;\n}\n\n/**\n * Entry for a collected finalize response.\n *\n * Port of `struct FinalizeEntry` from finalize.rs.\n */\ninterface FinalizeEntry {\n participant: XID;\n keyPackage: unknown;\n publicKeyPackage: unknown;\n}\n\n/**\n * Validate that the owner is the coordinator of the group.\n *\n * Port of coordinator check from finalize.rs lines 76-82.\n */\nfunction validateCoordinator(groupRecord: GroupRecord, ownerXid: XID): void {\n if (groupRecord.coordinator().xid().urString() !== ownerXid.urString()) {\n throw new Error(\n `Only the coordinator can collect finalize responses. Coordinator: ${groupRecord.coordinator().xid().urString()}, Owner: ${ownerXid.urString()}`,\n );\n }\n}\n\n/**\n * Validate envelope and extract finalize data (for parallel fetch).\n *\n * Port of `validate_and_extract_finalize_response()` from finalize.rs lines 407-466.\n */\nfunction validateAndExtractFinalizeResponse(\n envelope: Envelope,\n coordinatorKeys: PrivateKeys,\n expectedGroupId: ARID,\n expectedParticipant: XID,\n): FinalizeResponseData | { rejected: string } {\n const now = new Date();\n\n let sealed: SealedResponse;\n try {\n sealed = SealedResponse.tryFromEncryptedEnvelope(envelope, undefined, now, coordinatorKeys);\n } catch (err) {\n return {\n rejected: `Failed to decrypt/parse response: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n // Check for error response\n if (!sealed.isOk()) {\n try {\n const error = sealed.error();\n const reasonEnv = error.optionalObjectForPredicate(\"reason\");\n const reason = reasonEnv?.extractString() ?? \"unknown reason\";\n return { rejected: `Participant reported error: ${reason}` };\n } catch {\n return { rejected: \"Participant reported error: unknown reason\" };\n }\n }\n\n // Get and validate result\n let result: Envelope;\n try {\n result = sealed.result();\n } catch {\n return { rejected: \"Finalize response has no result\" };\n }\n\n // Validate response type\n try {\n result.checkSubjectUnit();\n result.checkType(\"dkgFinalizeResponse\");\n } catch (err) {\n return {\n rejected: `Invalid response type: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n // Validate group ID\n try {\n const groupEnv = result.objectForPredicate(\"group\");\n const groupIdStr = groupEnv.extractString();\n const groupId = parseAridUr(groupIdStr);\n if (groupId.urString() !== expectedGroupId.urString()) {\n return {\n rejected: `Group ${groupId.urString()} does not match expected ${expectedGroupId.urString()}`,\n };\n }\n } catch (err) {\n return {\n rejected: `Failed to extract group: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n // Validate participant\n try {\n const participantEnv = result.objectForPredicate(\"participant\");\n const participantStr = participantEnv.extractString();\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports, no-undef\n const { XID: XIDClass } = require(\"@bcts/components\");\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access\n const participantXid = XIDClass.fromURString(participantStr) as XID;\n if (participantXid.urString() !== expectedParticipant.urString()) {\n return {\n rejected: `Participant ${participantXid.urString()} does not match expected ${expectedParticipant.urString()}`,\n };\n }\n } catch (err) {\n return {\n rejected: `Failed to extract participant: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n // Extract key packages\n let keyPackage: unknown;\n let publicKeyPackage: unknown;\n\n try {\n const keyJsonEnv = result.objectForPredicate(\"key_package\");\n const keyJsonStr = keyJsonEnv.extractString();\n keyPackage = JSON.parse(keyJsonStr);\n } catch (err) {\n return {\n rejected: `Failed to parse key_package: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n try {\n const pubJsonEnv = result.objectForPredicate(\"public_key_package\");\n const pubJsonStr = pubJsonEnv.extractString();\n publicKeyPackage = JSON.parse(pubJsonStr);\n } catch (err) {\n return {\n rejected: `Failed to parse public_key_package: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n\n return { keyPackage, publicKeyPackage };\n}\n\n/**\n * Fetch a finalize response sequentially.\n *\n * Port of `fetch_finalize_response()` from finalize.rs lines 282-358.\n */\nasync function fetchFinalizeResponse(\n client: StorageClient,\n responseArid: ARID,\n timeout: number | undefined,\n coordinatorKeys: PrivateKeys,\n expectedGroup: ARID,\n expectedParticipant: XID,\n participantName: string,\n): Promise<FinalizeEntry> {\n const envelope = await getWithIndicator(\n client,\n responseArid,\n participantName,\n timeout,\n isVerbose(),\n );\n\n if (envelope === null || envelope === undefined) {\n throw new Error(\"Finalize response not found in Hubert storage\");\n }\n\n const result = validateAndExtractFinalizeResponse(\n envelope,\n coordinatorKeys,\n expectedGroup,\n expectedParticipant,\n );\n\n if (\"rejected\" in result) {\n throw new Error(result.rejected);\n }\n\n return {\n participant: expectedParticipant,\n keyPackage: result.keyPackage,\n publicKeyPackage: result.publicKeyPackage,\n };\n}\n\n/**\n * Collect finalize responses in parallel with progress display.\n *\n * Port of `collect_finalize_parallel()` from finalize.rs lines 371-404.\n */\nasync function collectFinalizeParallel(\n client: StorageClient,\n registry: Registry,\n pendingRequests: PendingRequests,\n coordinatorKeys: PrivateKeys,\n expectedGroupId: ARID,\n timeout: number | undefined,\n): Promise<CollectionResult<FinalizeResponseData>> {\n const requests: [XID, ARID, string][] = [];\n\n for (const [xid, arid] of pendingRequests.iterCollect()) {\n const record = registry.participant(xid);\n const name = record?.petName() ?? xid.urString();\n requests.push([xid, arid, name]);\n }\n\n const config: ParallelFetchConfig = parallelFetchConfigWithTimeout(timeout);\n\n return parallelFetch(\n client,\n requests,\n (envelope: Envelope, xid: XID) =>\n validateAndExtractFinalizeResponse(envelope, coordinatorKeys, expectedGroupId, xid),\n config,\n );\n}\n\n/**\n * Finalize collection results: persist, update registry, print summary.\n *\n * Port of `finalize_collection_results()` from finalize.rs lines 469-590.\n */\nfunction finalizeFinalizeCollectionResults(\n collection: CollectionResult<FinalizeResponseData>,\n registryPath: string,\n registry: Registry,\n groupId: ARID,\n): SigningPublicKey | undefined {\n // Report any failures\n if (collection.rejections.length > 0) {\n console.error();\n console.error(\"Rejections:\");\n for (const [xid, reason] of collection.rejections) {\n console.error(` ${xid.urString()}: ${reason}`);\n }\n }\n if (collection.errors.length > 0) {\n console.error();\n console.error(\"Errors:\");\n for (const [xid, error] of collection.errors) {\n console.error(` ${xid.urString()}: ${error}`);\n }\n }\n if (collection.timeouts.length > 0) {\n console.error();\n console.error(\"Timeouts:\");\n for (const xid of collection.timeouts) {\n console.error(` ${xid.urString()}`);\n }\n }\n\n if (!collection.allSucceeded()) {\n throw new Error(\n `Finalize collection incomplete: ${collection.successes.length} succeeded, ` +\n `${collection.rejections.length} rejected, ${collection.errors.length} errors, ` +\n `${collection.timeouts.length} timeouts`,\n );\n }\n\n // Validate group verifying key consistency\n let groupVerifyingKey: SigningPublicKey | undefined;\n\n for (const [xid, data] of collection.successes) {\n // Extract verifying_key from public_key_package\n const pubKeyPkg = data.publicKeyPackage as { verifying_key?: string };\n if (!pubKeyPkg.verifying_key) {\n throw new Error(\n `Failed to extract verifying key for ${xid.urString()}: missing verifying_key field`,\n );\n }\n\n // The verifying key is typically hex-encoded\n let signingKey: SigningPublicKey;\n try {\n const verifyingKeyBytes = hexToBytes(pubKeyPkg.verifying_key);\n signingKey = signingKeyFromVerifying(verifyingKeyBytes) as SigningPublicKey;\n } catch (err) {\n throw new Error(\n `Failed to extract verifying key for ${xid.urString()}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n\n if (groupVerifyingKey !== undefined) {\n if (groupVerifyingKey.urString() !== signingKey.urString()) {\n throw new Error(`Group verifying key mismatch for participant ${xid.urString()}`);\n }\n } else {\n groupVerifyingKey = signingKey;\n }\n }\n\n // Persist collected finalize data\n const stateDir = groupStateDir(registryPath, groupId.hex());\n fs.mkdirSync(stateDir, { recursive: true });\n\n const collectedPath = path.join(stateDir, \"collected_finalize.json\");\n const root: Record<string, { key_package: unknown; public_key_package: unknown }> = {};\n\n for (const [xid, data] of collection.successes) {\n root[xid.urString()] = {\n key_package: data.keyPackage,\n public_key_package: data.publicKeyPackage,\n };\n }\n\n fs.writeFileSync(collectedPath, JSON.stringify(root, null, 2));\n\n // Update registry\n const groupRecord = registry.group(groupId);\n if (groupRecord === undefined) {\n throw new Error(\"Group not found in registry\");\n }\n\n if (groupVerifyingKey !== undefined) {\n groupRecord.setVerifyingKey(groupVerifyingKey);\n }\n groupRecord.clearPendingRequests();\n registry.save(registryPath);\n\n if (isVerbose()) {\n console.error();\n console.error(\n `Collected ${collection.successes.length} finalize responses. Saved to ${collectedPath}`,\n );\n if (groupVerifyingKey !== undefined) {\n console.error(groupVerifyingKey.urString());\n }\n } else if (groupVerifyingKey !== undefined) {\n console.log(groupVerifyingKey.urString());\n }\n\n return groupVerifyingKey;\n}\n\n/**\n * Helper to convert hex string to bytes.\n */\nfunction hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Execute the DKG coordinator finalize command.\n *\n * Collects finalize responses (key/public key packages) from all participants.\n *\n * Port of `finalize()` from cmd/dkg/coordinator/finalize.rs.\n */\nexport async function finalize(\n client: StorageClient,\n options: DkgFinalizeOptions,\n cwd: string,\n): Promise<DkgFinalizeResult> {\n const registryPath = resolveRegistryPath(options.registryPath, cwd);\n const registry = Registry.load(registryPath);\n\n const owner = registry.owner();\n if (owner === undefined) {\n throw new Error(\"Registry owner is required\");\n }\n\n const groupId = parseAridUr(options.groupId);\n const groupRecord = registry.group(groupId);\n\n if (groupRecord === undefined) {\n throw new Error(`Group ${options.groupId} not found in registry`);\n }\n\n // Validate that owner is the coordinator\n validateCoordinator(groupRecord, owner.xid());\n\n const pendingRequests = groupRecord.pendingRequests();\n if (pendingRequests.isEmpty()) {\n throw new Error(\n \"No pending requests for this group. Did you run 'frost dkg coordinator finalize send'?\",\n );\n }\n\n const coordinatorKeys = owner.xidDocument().inceptionPrivateKeys();\n if (coordinatorKeys === undefined) {\n throw new Error(\"Coordinator XID document has no private keys\");\n }\n\n let verifyingKey: SigningPublicKey | undefined;\n let collected: number;\n let rejected = 0;\n let errors: number;\n let timeouts = 0;\n\n if (options.parallel === true) {\n // Parallel path with progress display\n const collection = await collectFinalizeParallel(\n client,\n registry,\n pendingRequests,\n coordinatorKeys,\n groupId,\n options.timeoutSeconds,\n );\n\n verifyingKey = finalizeFinalizeCollectionResults(collection, registryPath, registry, groupId);\n\n collected = collection.successes.length;\n rejected = collection.rejections.length;\n errors = collection.errors.length;\n timeouts = collection.timeouts.length;\n } else {\n // Sequential path (original behavior)\n const collectedEntries: FinalizeEntry[] = [];\n const errorEntries: [XID, string][] = [];\n let groupVerifyingKey: SigningPublicKey | undefined;\n\n if (isVerbose()) {\n console.error(`Collecting finalize responses from ${pendingRequests.len()} participants...`);\n }\n\n for (const [participantXid, collectFromArid] of pendingRequests.iterCollect()) {\n const record = registry.participant(participantXid);\n const name = record?.petName() ?? participantXid.urString();\n\n try {\n const entry = await fetchFinalizeResponse(\n client,\n collectFromArid,\n options.timeoutSeconds,\n coordinatorKeys,\n groupId,\n participantXid,\n name,\n );\n\n // Extract verifying key from public_key_package\n const pubKeyPkg = entry.publicKeyPackage as { verifying_key?: string };\n if (!pubKeyPkg.verifying_key) {\n throw new Error(\"missing verifying_key field\");\n }\n\n const verifyingKeyBytes = hexToBytes(pubKeyPkg.verifying_key);\n const signingKey = signingKeyFromVerifying(verifyingKeyBytes) as SigningPublicKey;\n\n if (groupVerifyingKey !== undefined) {\n if (groupVerifyingKey.urString() !== signingKey.urString()) {\n if (isVerbose()) {\n console.error(\"error: group verifying key mismatch\");\n }\n errorEntries.push([participantXid, \"Group verifying key mismatch across responses\"]);\n continue;\n }\n } else {\n groupVerifyingKey = signingKey;\n }\n\n collectedEntries.push(entry);\n } catch (err) {\n if (isVerbose()) {\n console.error(`error: ${err instanceof Error ? err.message : String(err)}`);\n }\n errorEntries.push([participantXid, err instanceof Error ? err.message : String(err)]);\n }\n }\n\n if (errorEntries.length > 0) {\n if (isVerbose()) {\n console.error();\n console.error(`Failed to collect from ${errorEntries.length} participants:`);\n for (const [xid, error] of errorEntries) {\n console.error(` ${xid.urString()}: ${error}`);\n }\n }\n throw new Error(\n `Finalize collection incomplete: ${errorEntries.length} of ${pendingRequests.len()} responses failed`,\n );\n }\n\n // Persist collected finalize data\n const stateDir = groupStateDir(registryPath, groupId.hex());\n fs.mkdirSync(stateDir, { recursive: true });\n\n const collectedPath = path.join(stateDir, \"collected_finalize.json\");\n const root: Record<string, { key_package: unknown; public_key_package: unknown }> = {};\n\n for (const entry of collectedEntries) {\n root[entry.participant.urString()] = {\n key_package: entry.keyPackage,\n public_key_package: entry.publicKeyPackage,\n };\n }\n\n fs.writeFileSync(collectedPath, JSON.stringify(root, null, 2));\n\n // Update registry pending requests cleared\n const groupRecordMut = registry.group(groupId);\n if (groupRecordMut === undefined) {\n throw new Error(\"Group not found in registry\");\n }\n\n if (groupVerifyingKey !== undefined) {\n groupRecordMut.setVerifyingKey(groupVerifyingKey);\n }\n groupRecordMut.clearPendingRequests();\n registry.save(registryPath);\n\n if (isVerbose()) {\n console.error();\n console.error(\n `Collected ${collectedEntries.length} finalize responses. Saved to ${collectedPath}`,\n );\n if (groupVerifyingKey !== undefined) {\n console.error(groupVerifyingKey.urString());\n }\n } else if (groupVerifyingKey !== undefined) {\n console.log(groupVerifyingKey.urString());\n }\n\n verifyingKey = groupVerifyingKey;\n collected = collectedEntries.length;\n errors = errorEntries.length;\n }\n\n return {\n verifyingKey: verifyingKey?.urString() ?? \"\",\n collected,\n rejected,\n errors,\n timeouts,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { t as __exportAll } from "./rolldown-runtime-
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
2
2
|
import { n as compareXidBytes } from "./proposed-participant-Detb823_.mjs";
|
|
3
3
|
import { Registry, resolveRegistryPath } from "./registry/index.mjs";
|
|
4
|
-
import { c as parseAridUr, h as signingKeyFromVerifying, n as isVerbose, t as groupStateDir } from "./common-
|
|
4
|
+
import { c as parseAridUr, h as signingKeyFromVerifying, n as isVerbose, t as groupStateDir } from "./common-k2QC6AST.mjs";
|
|
5
5
|
import { n as putWithIndicator, t as getWithIndicator } from "./busy-BlU8_pS2.mjs";
|
|
6
|
-
import { i as createStorageClient } from "./registry-
|
|
6
|
+
import { i as createStorageClient } from "./registry-DkhElxx1.mjs";
|
|
7
7
|
import { bytesToHex, dkgPart3, hexToBytes, identifierFromU16, identifierToHex, serializeKeyPackage, serializePublicKeyPackage } from "./frost/index.mjs";
|
|
8
8
|
import { ARID, JSON as JSON$1, XID } from "@bcts/components";
|
|
9
9
|
import { CborDate } from "@bcts/dcbor";
|
|
@@ -249,4 +249,4 @@ async function finalize(_client, options, cwd) {
|
|
|
249
249
|
//#endregion
|
|
250
250
|
export { finalize_exports as n, finalize as t };
|
|
251
251
|
|
|
252
|
-
//# sourceMappingURL=finalize-
|
|
252
|
+
//# sourceMappingURL=finalize-f9hvnJ2R.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"finalize-CELJsR4C.mjs","names":["EnvelopeFunction","JSONWrapper"],"sources":["../src/cmd/dkg/participant/finalize.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * DKG participant finalize command.\n *\n * Port of cmd/dkg/participant/finalize.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, XID } from \"@bcts/components\";\nimport { compareXidBytes } from \"../../../dkg/proposed-participant.js\";\nimport { CborDate } from \"@bcts/dcbor\";\nimport { Envelope, Function as EnvelopeFunction } from \"@bcts/envelope\";\nimport { SealedRequest, SealedResponse } from \"@bcts/gstp\";\nimport type { XIDDocument } from \"@bcts/xid\";\n\nimport { type GroupRecord, Registry, resolveRegistryPath } from \"../../../registry/index.js\";\nimport { getWithIndicator, putWithIndicator } from \"../../busy.js\";\nimport { groupStateDir, isVerbose } from \"../../common.js\";\nimport { createStorageClient, type StorageClient, type StorageSelection } from \"../../storage.js\";\nimport { parseAridUr, signingKeyFromVerifying } from \"../common.js\";\nimport {\n dkgPart3,\n identifierFromU16,\n identifierToHex,\n hexToBytes,\n bytesToHex,\n serializeKeyPackage,\n serializePublicKeyPackage,\n type DkgRound1Package,\n type DkgRound2Package,\n type DkgRound2SecretPackage,\n type FrostIdentifier,\n type FrostKeyPackage,\n type FrostPublicKeyPackage,\n} from \"../../../frost/index.js\";\nimport { Ed25519Sha512, serde } from \"@frosts/ed25519\";\nimport { round2, CoefficientCommitment, VerifiableSecretSharingCommitment } from \"@frosts/core\";\n\n/**\n * Options for the DKG finalize command.\n */\nexport interface DkgFinalizeOptions {\n registryPath?: string;\n groupId: string;\n timeoutSeconds?: number;\n preview?: boolean;\n storageSelection?: StorageSelection;\n verbose?: boolean;\n}\n\n/**\n * Result of the DKG finalize command.\n */\nexport interface DkgFinalizeResult {\n verifyingKey: string;\n keyPackagePath: string;\n publicKeyPackagePath: string;\n}\n\n/**\n * Persisted round 2 state loaded from disk.\n */\ninterface Round2State {\n secretPackage: DkgRound2SecretPackage;\n round1Packages: Map<string, DkgRound1Package>;\n}\n\n/**\n * Load persisted round 2 state from disk.\n *\n * Port of round2_secret loading from cmd/dkg/participant/finalize.rs lines 82-106.\n */\nfunction loadRound2State(registryPath: string, groupId: ARID): Round2State {\n const stateDir = groupStateDir(registryPath, groupId.hex());\n\n // Load Round 2 secret\n const round2SecretPath = path.join(stateDir, \"round2_secret.json\");\n if (!fs.existsSync(round2SecretPath)) {\n throw new Error(`Round 2 secret not found at ${round2SecretPath}. Did you run round2?`);\n }\n\n // Mirrors Rust `frost::keys::dkg::round2::SecretPackage` JSON\n // (`frost-rust/frost-core/src/keys/dkg.rs:269-287`):\n //\n // {\n // \"identifier\": \"<lowercase hex scalar>\",\n // \"commitment\": [\"<hex>\", \"<hex>\", ...],\n // \"secret_share\": \"<hex>\",\n // \"min_signers\": <u16>,\n // \"max_signers\": <u16>\n // }\n //\n // The struct is `#[serde(deny_unknown_fields)]` and the\n // `commitment` is a `VerifiableSecretSharingCommitment` (a\n // single-field tuple struct over `Vec<CoefficientCommitment>`),\n // which serde flattens to a bare JSON array. The earlier port\n // emitted camelCase keys plus a nested `commitment.coefficients`\n // shape and a numeric `identifier`, which Rust would reject and\n // which had no chance of being read by Rust's standard derive.\n const secretJson = JSON.parse(fs.readFileSync(round2SecretPath, \"utf-8\")) as {\n identifier: string;\n commitment: string[];\n secret_share: string;\n min_signers: number;\n max_signers: number;\n };\n\n // Identifier hex → little-endian u16 (the FROST 1-indexed\n // participant position). The scalar bytes are 32-LE for Ed25519, so\n // the first two bytes hold the u16 value when the identifier is in\n // the small-integer range (1..=N) used by the DKG.\n const idBytes = hexToBytes(secretJson.identifier);\n let identifierU16 = 1;\n if (idBytes.length >= 2) {\n identifierU16 = idBytes[0] | (idBytes[1] << 8);\n }\n if (identifierU16 === 0) {\n identifierU16 = 1;\n }\n const identifier = identifierFromU16(identifierU16);\n\n const coefficientCommitments = secretJson.commitment.map((hex) =>\n CoefficientCommitment.deserialize(Ed25519Sha512, hexToBytes(hex)),\n );\n\n const commitment = new VerifiableSecretSharingCommitment(Ed25519Sha512, coefficientCommitments);\n\n const secretShareScalar = Ed25519Sha512.deserializeScalar(hexToBytes(secretJson.secret_share));\n\n const secretPackage: DkgRound2SecretPackage = new round2.SecretPackage(\n Ed25519Sha512,\n identifier,\n commitment,\n secretShareScalar,\n secretJson.min_signers,\n secretJson.max_signers,\n );\n\n // Load collected Round 1 packages (from round2 phase)\n const round1Path = path.join(stateDir, \"collected_round1.json\");\n if (!fs.existsSync(round1Path)) {\n throw new Error(`Round 1 packages not found at ${round1Path}. Did you receive earlier phases?`);\n }\n\n const round1Json = JSON.parse(fs.readFileSync(round1Path, \"utf-8\")) as Record<string, unknown>;\n\n // Convert to Map<string, DkgRound1Package> - keyed by XID UR string\n const round1Packages = new Map<string, DkgRound1Package>();\n for (const [xidStr, value] of Object.entries(round1Json)) {\n const packageJson = value as {\n header: { version: number; ciphersuite: string };\n commitment: string[];\n proof_of_knowledge: string;\n };\n const pkg = serde.round1PackageFromJson(packageJson);\n round1Packages.set(xidStr, pkg);\n }\n\n return { secretPackage, round1Packages };\n}\n\n/**\n * Validate the finalize request from the coordinator.\n *\n * Port of request validation from cmd/dkg/participant/finalize.rs lines 139-161.\n */\nfunction validateFinalizeRequest(\n sealedRequest: SealedRequest,\n groupId: ARID,\n expectedCoordinator: XID,\n): ARID {\n // Validate the request function\n if (!sealedRequest.function().equals(EnvelopeFunction.fromString(\"dkgFinalize\"))) {\n throw new Error(`Unexpected request function: ${sealedRequest.function().toString()}`);\n }\n\n // Validate the sender is the expected coordinator\n if (sealedRequest.sender().xid().urString() !== expectedCoordinator.urString()) {\n throw new Error(\n `Unexpected request sender: ${sealedRequest.sender().xid().urString()} ` +\n `(expected coordinator ${expectedCoordinator.urString()})`,\n );\n }\n\n // Validate the group ID matches\n const requestGroupIdEnvelope = sealedRequest.objectForParameter(\"group\");\n if (requestGroupIdEnvelope === undefined) {\n throw new Error(\"Request missing group parameter\");\n }\n const requestGroupId = requestGroupIdEnvelope.extractSubject((cbor) => ARID.fromTaggedCbor(cbor));\n if (requestGroupId.urString() !== groupId.urString()) {\n throw new Error(\n `Request group ID ${requestGroupId.urString()} does not match expected ${groupId.urString()}`,\n );\n }\n\n // Extract where we should post our response\n const responseAridEnvelope = sealedRequest.objectForParameter(\"responseArid\");\n if (responseAridEnvelope === undefined) {\n throw new Error(\"Request missing responseArid parameter\");\n }\n const responseArid = responseAridEnvelope.extractSubject((cbor) => ARID.fromTaggedCbor(cbor));\n\n return responseArid;\n}\n\n/**\n * Extract round 2 packages from the finalize request.\n *\n * Port of round2 package extraction from cmd/dkg/participant/finalize.rs lines 209-229.\n */\nfunction extractFinalizePackages(\n request: SealedRequest,\n groupRecord: GroupRecord,\n ownerXid: XID,\n): Map<string, DkgRound2Package> {\n // Build XID -> Identifier mapping based on sorted participant order\n const sortedXids: XID[] = groupRecord.participants().map((p) => p.xid());\n\n // Add owner if not already in list\n const ownerUrString = ownerXid.urString();\n if (!sortedXids.some((xid) => xid.urString() === ownerUrString)) {\n sortedXids.push(ownerXid);\n }\n\n // Sort by XID byte order — mirrors Rust `XID::cmp` (raw 32-byte\n // lex compare). The earlier port used `urString().localeCompare(...)`,\n // which differs from byte order for any byte ≥ 0x80 and is locale-\n // aware, producing different FROST identifier assignments than Rust.\n sortedXids.sort((a, b) => compareXidBytes(a.toData(), b.toData()));\n\n // Deduplicate\n const deduped: XID[] = [];\n for (const xid of sortedXids) {\n if (deduped.length === 0 || deduped[deduped.length - 1].urString() !== xid.urString()) {\n deduped.push(xid);\n }\n }\n\n // Build XID -> Identifier mapping (1-indexed)\n const xidToIdentifier = new Map<string, FrostIdentifier>();\n for (let i = 0; i < deduped.length; i++) {\n const identifier = identifierFromU16(i + 1);\n xidToIdentifier.set(deduped[i].urString(), identifier);\n }\n\n const myXidStr = ownerXid.urString();\n\n // Extract all round2Package parameters\n const packages = new Map<string, DkgRound2Package>();\n\n const packageEnvelopes = request.objectsForParameter(\"round2Package\");\n for (const packageEnvelope of packageEnvelopes) {\n // Extract sender XID from the envelope\n const senderEnvelope = packageEnvelope.objectForPredicate(\"sender\");\n if (senderEnvelope === undefined) {\n throw new Error(\"round2Package missing sender predicate\");\n }\n const senderXid = senderEnvelope.extractSubject((cbor) => XID.fromTaggedCbor(cbor));\n\n // Skip our own package\n if (senderXid.urString() === myXidStr) {\n continue;\n }\n\n // Get the identifier for this sender\n const identifier = xidToIdentifier.get(senderXid.urString());\n if (identifier === undefined) {\n throw new Error(`Unknown sender XID in round2Package: ${senderXid.urString()}`);\n }\n\n // Extract the package bytes (stored as JSON tag)\n const packageJson = packageEnvelope.extractSubject((cbor) => JSONWrapper.fromTaggedCbor(cbor));\n const packageData = JSON.parse(new TextDecoder().decode(packageJson.toData())) as {\n header: { version: number; ciphersuite: string };\n signing_share: string;\n };\n\n const pkg = serde.round2PackageFromJson(packageData);\n packages.set(identifierToHex(identifier), pkg);\n }\n\n return packages;\n}\n\n/**\n * Build the response body for the finalize response.\n *\n * Port of `build_response_body()` from cmd/dkg/participant/finalize.rs lines 344-359.\n */\nfunction buildResponseBody(\n groupId: ARID,\n participantXid: XID,\n keyPackage: FrostKeyPackage,\n publicKeyPackage: FrostPublicKeyPackage,\n): Envelope {\n // Serialize key packages to JSON\n const keyPackageJson = serializeKeyPackage(keyPackage);\n const publicKeyPackageJson = serializePublicKeyPackage(publicKeyPackage);\n\n const keyJsonBytes = new TextEncoder().encode(JSON.stringify(keyPackageJson));\n const keyJsonWrapper = JSONWrapper.fromData(keyJsonBytes);\n\n const pubJsonBytes = new TextEncoder().encode(JSON.stringify(publicKeyPackageJson));\n const pubJsonWrapper = JSONWrapper.fromData(pubJsonBytes);\n\n return Envelope.unit()\n .addType(\"dkgFinalizeResponse\")\n .addAssertion(\"group\", groupId)\n .addAssertion(\"participant\", participantXid)\n .addAssertion(\"key_package\", keyJsonWrapper)\n .addAssertion(\"public_key_package\", pubJsonWrapper);\n}\n\n/**\n * Persist finalize state (key packages) to disk.\n *\n * Port of key package persistence from cmd/dkg/participant/finalize.rs lines 251-257.\n */\nfunction persistFinalizeState(\n registryPath: string,\n groupId: ARID,\n keyPackage: FrostKeyPackage,\n publicKeyPackage: FrostPublicKeyPackage,\n): { keyPackagePath: string; publicKeyPackagePath: string } {\n const stateDir = groupStateDir(registryPath, groupId.hex());\n fs.mkdirSync(stateDir, { recursive: true });\n\n // Serialize and save key package\n const serializedKeyPackage = serializeKeyPackage(keyPackage);\n const keyPackagePath = path.join(stateDir, \"key_package.json\");\n fs.writeFileSync(keyPackagePath, JSON.stringify(serializedKeyPackage, null, 2));\n\n // Serialize and save public key package\n const serializedPublicKeyPackage = serializePublicKeyPackage(publicKeyPackage);\n const publicKeyPackagePath = path.join(stateDir, \"public_key_package.json\");\n fs.writeFileSync(publicKeyPackagePath, JSON.stringify(serializedPublicKeyPackage, null, 2));\n\n return { keyPackagePath, publicKeyPackagePath };\n}\n\n/**\n * Execute the DKG participant finalize command.\n *\n * Responds to the finalize request from the coordinator, runs FROST DKG part3\n * to generate the final key package, and posts the response back.\n *\n * Port of `CommandArgs::exec()` from cmd/dkg/participant/finalize.rs lines 52-341.\n */\nexport async function finalize(\n _client: StorageClient | undefined,\n options: DkgFinalizeOptions,\n cwd: string,\n): Promise<DkgFinalizeResult> {\n if (options.storageSelection === undefined) {\n throw new Error(\"Hubert storage is required for finalize respond\");\n }\n\n const registryPath = resolveRegistryPath(options.registryPath, cwd);\n const registry = Registry.load(registryPath);\n\n const owner = registry.owner();\n if (owner === undefined) {\n throw new Error(\"Registry owner is required\");\n }\n\n const groupId = parseAridUr(options.groupId);\n const groupRecord = registry.group(groupId);\n if (groupRecord === undefined) {\n throw new Error(\"Group not found in registry\");\n }\n\n // Get the ARID where we're listening for the finalize request\n const listeningAtArid = groupRecord.listeningAtArid();\n if (listeningAtArid === undefined) {\n throw new Error(\"No listening ARID for this group. Did you receive finalize send?\");\n }\n\n // Load Round 2 state (secret and collected round1 packages)\n const round2State = loadRound2State(registryPath, groupId);\n\n if (isVerbose() || options.verbose === true) {\n console.error(\"Fetching finalize request from Hubert...\");\n }\n\n const client = await createStorageClient(options.storageSelection);\n\n // Fetch the finalize request from where we're listening\n const requestEnvelope = await getWithIndicator(\n client,\n listeningAtArid,\n \"Finalize request\",\n options.timeoutSeconds,\n options.verbose ?? false,\n );\n\n if (requestEnvelope === null || requestEnvelope === undefined) {\n throw new Error(\"Finalize request not found in Hubert storage\");\n }\n\n // Decrypt and validate the request\n const ownerPrivateKeys = owner.xidDocument().inceptionPrivateKeys();\n if (ownerPrivateKeys === undefined) {\n throw new Error(\"Owner XID document has no private keys\");\n }\n\n const now = CborDate.now().datetime();\n const sealedRequest = SealedRequest.tryFromEnvelope(\n requestEnvelope,\n undefined,\n now,\n ownerPrivateKeys,\n );\n\n // Validate the request and extract response ARID\n const expectedCoordinator = groupRecord.coordinator().xid();\n const responseArid = validateFinalizeRequest(sealedRequest, groupId, expectedCoordinator);\n\n // Build identifier mapping for round1 packages (XID UR -> Identifier hex)\n const sortedXids: XID[] = groupRecord.participants().map((p) => p.xid());\n\n // Add owner if not already in list\n const ownerUrString = owner.xid().urString();\n if (!sortedXids.some((xid) => xid.urString() === ownerUrString)) {\n sortedXids.push(owner.xid());\n }\n\n // Sort by XID byte order — mirrors Rust `XID::cmp` (raw 32-byte\n // lex compare). The earlier port used `urString().localeCompare(...)`,\n // which differs from byte order for any byte ≥ 0x80 and is locale-\n // aware, producing different FROST identifier assignments than Rust.\n sortedXids.sort((a, b) => compareXidBytes(a.toData(), b.toData()));\n\n // Deduplicate\n const deduped: XID[] = [];\n for (const xid of sortedXids) {\n if (deduped.length === 0 || deduped[deduped.length - 1].urString() !== xid.urString()) {\n deduped.push(xid);\n }\n }\n\n // Build XID -> Identifier mapping (1-indexed)\n const xidToIdentifier = new Map<string, FrostIdentifier>();\n for (let i = 0; i < deduped.length; i++) {\n const identifier = identifierFromU16(i + 1);\n xidToIdentifier.set(deduped[i].urString(), identifier);\n }\n\n // Convert round1 packages from XID-keyed to identifier-keyed (exclude self)\n const round1PackagesById = new Map<string, DkgRound1Package>();\n for (const [xidStr, pkg] of round2State.round1Packages) {\n if (xidStr === ownerUrString) {\n continue;\n }\n const identifier = xidToIdentifier.get(xidStr);\n if (identifier === undefined) {\n throw new Error(`Unknown participant XID ${xidStr}`);\n }\n round1PackagesById.set(identifierToHex(identifier), pkg);\n }\n\n // Extract Round 2 packages from the request (exclude self)\n const round2PackagesById = extractFinalizePackages(sealedRequest, groupRecord, owner.xid());\n\n if (isVerbose() || options.verbose === true) {\n console.error(`Received ${round2PackagesById.size} Round 2 packages. Running DKG part3...`);\n }\n\n // Run FROST DKG part3 (finalize)\n const [keyPackage, publicKeyPackage] = await dkgPart3(\n round2State.secretPackage,\n round1PackagesById,\n round2PackagesById,\n );\n\n // Get the group verifying key\n const verifyingKeyBytes = publicKeyPackage.verifyingKey;\n const groupVerifyingKey = signingKeyFromVerifying(verifyingKeyBytes);\n\n if (isVerbose() || options.verbose === true) {\n console.error(\"Generated key package and public key package.\");\n }\n\n // Persist key packages\n const { keyPackagePath, publicKeyPackagePath } = persistFinalizeState(\n registryPath,\n groupId,\n keyPackage,\n publicKeyPackage,\n );\n\n // Build response body\n const responseBody = buildResponseBody(groupId, owner.xid(), keyPackage, publicKeyPackage);\n\n const signerPrivateKeys = owner.xidDocument().inceptionPrivateKeys();\n if (signerPrivateKeys === undefined) {\n throw new Error(\"Owner XID document has no signing keys\");\n }\n\n // Get coordinator's XID document for encryption\n const coordinatorXid = groupRecord.coordinator().xid();\n const coordinatorRecord = registry.participant(coordinatorXid);\n let coordinatorDoc: XIDDocument;\n if (coordinatorRecord !== undefined) {\n coordinatorDoc = coordinatorRecord.xidDocument();\n } else {\n // Check if coordinator is the owner\n if (owner.xid().urString() === coordinatorXid.urString()) {\n coordinatorDoc = owner.xidDocument();\n } else {\n throw new Error(`Coordinator ${coordinatorXid.urString()} not found in registry`);\n }\n }\n\n // Get peer continuation from the request\n const peerContinuation = sealedRequest.peerContinuation();\n\n let sealed = SealedResponse.newSuccess(sealedRequest.id(), owner.xidDocument()).withResult(\n responseBody,\n );\n\n if (peerContinuation !== undefined) {\n sealed = sealed.withPeerContinuation(peerContinuation);\n }\n\n if (options.preview === true) {\n // Show the response envelope structure without encryption\n if (isVerbose() || options.verbose === true) {\n // Cast to access urString method\n const verifyingKeyWithUrString = groupVerifyingKey as { urString?: () => string };\n if (typeof verifyingKeyWithUrString.urString === \"function\") {\n console.error(verifyingKeyWithUrString.urString());\n }\n }\n const unsealedEnvelope = sealed.toEnvelope(\n undefined, // No expiration for responses\n signerPrivateKeys,\n undefined,\n );\n console.log(unsealedEnvelope.urString());\n\n return {\n verifyingKey: bytesToHex(verifyingKeyBytes),\n keyPackagePath,\n publicKeyPackagePath,\n };\n }\n\n const responseEnvelope = sealed.toEnvelope(\n undefined, // No expiration for responses\n signerPrivateKeys,\n coordinatorDoc,\n );\n\n // Post the response\n await putWithIndicator(\n client,\n responseArid,\n responseEnvelope,\n \"Finalize Response\",\n options.verbose ?? false,\n );\n\n // Update registry: contributions and verifying key\n const updatedGroupRecord = registry.group(groupId);\n if (updatedGroupRecord !== undefined) {\n const contributions = updatedGroupRecord.contributions();\n contributions.keyPackage = keyPackagePath;\n updatedGroupRecord.setContributions(contributions);\n updatedGroupRecord.clearListeningAtArid();\n\n // Set verifying key if the method exists\n const recordWithVerifyingKey = updatedGroupRecord as {\n setVerifyingKey?: (key: unknown) => void;\n };\n if (typeof recordWithVerifyingKey.setVerifyingKey === \"function\") {\n recordWithVerifyingKey.setVerifyingKey(groupVerifyingKey);\n }\n\n registry.save(registryPath);\n }\n\n // Get verifying key for output\n const verifyingKeyHex = bytesToHex(verifyingKeyBytes);\n\n if (isVerbose() || options.verbose === true) {\n console.error(`Posted finalize response to ${responseArid.urString()}`);\n // Cast to access urString method\n const verifyingKeyWithUrString = groupVerifyingKey as { urString?: () => string };\n if (typeof verifyingKeyWithUrString.urString === \"function\") {\n console.error(verifyingKeyWithUrString.urString());\n }\n } else {\n // Cast to access urString method\n const verifyingKeyWithUrString = groupVerifyingKey as { urString?: () => string };\n if (typeof verifyingKeyWithUrString.urString === \"function\") {\n console.log(verifyingKeyWithUrString.urString());\n }\n }\n\n return {\n verifyingKey: verifyingKeyHex,\n keyPackagePath,\n publicKeyPackagePath,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"finalize-f9hvnJ2R.mjs","names":["EnvelopeFunction","JSONWrapper"],"sources":["../src/cmd/dkg/participant/finalize.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * DKG participant finalize command.\n *\n * Port of cmd/dkg/participant/finalize.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, XID } from \"@bcts/components\";\nimport { compareXidBytes } from \"../../../dkg/proposed-participant.js\";\nimport { CborDate } from \"@bcts/dcbor\";\nimport { Envelope, Function as EnvelopeFunction } from \"@bcts/envelope\";\nimport { SealedRequest, SealedResponse } from \"@bcts/gstp\";\nimport type { XIDDocument } from \"@bcts/xid\";\n\nimport { type GroupRecord, Registry, resolveRegistryPath } from \"../../../registry/index.js\";\nimport { getWithIndicator, putWithIndicator } from \"../../busy.js\";\nimport { groupStateDir, isVerbose } from \"../../common.js\";\nimport { createStorageClient, type StorageClient, type StorageSelection } from \"../../storage.js\";\nimport { parseAridUr, signingKeyFromVerifying } from \"../common.js\";\nimport {\n dkgPart3,\n identifierFromU16,\n identifierToHex,\n hexToBytes,\n bytesToHex,\n serializeKeyPackage,\n serializePublicKeyPackage,\n type DkgRound1Package,\n type DkgRound2Package,\n type DkgRound2SecretPackage,\n type FrostIdentifier,\n type FrostKeyPackage,\n type FrostPublicKeyPackage,\n} from \"../../../frost/index.js\";\nimport { Ed25519Sha512, serde } from \"@frosts/ed25519\";\nimport { round2, CoefficientCommitment, VerifiableSecretSharingCommitment } from \"@frosts/core\";\n\n/**\n * Options for the DKG finalize command.\n */\nexport interface DkgFinalizeOptions {\n registryPath?: string;\n groupId: string;\n timeoutSeconds?: number;\n preview?: boolean;\n storageSelection?: StorageSelection;\n verbose?: boolean;\n}\n\n/**\n * Result of the DKG finalize command.\n */\nexport interface DkgFinalizeResult {\n verifyingKey: string;\n keyPackagePath: string;\n publicKeyPackagePath: string;\n}\n\n/**\n * Persisted round 2 state loaded from disk.\n */\ninterface Round2State {\n secretPackage: DkgRound2SecretPackage;\n round1Packages: Map<string, DkgRound1Package>;\n}\n\n/**\n * Load persisted round 2 state from disk.\n *\n * Port of round2_secret loading from cmd/dkg/participant/finalize.rs lines 82-106.\n */\nfunction loadRound2State(registryPath: string, groupId: ARID): Round2State {\n const stateDir = groupStateDir(registryPath, groupId.hex());\n\n // Load Round 2 secret\n const round2SecretPath = path.join(stateDir, \"round2_secret.json\");\n if (!fs.existsSync(round2SecretPath)) {\n throw new Error(`Round 2 secret not found at ${round2SecretPath}. Did you run round2?`);\n }\n\n // Mirrors Rust `frost::keys::dkg::round2::SecretPackage` JSON\n // (`frost-rust/frost-core/src/keys/dkg.rs:269-287`):\n //\n // {\n // \"identifier\": \"<lowercase hex scalar>\",\n // \"commitment\": [\"<hex>\", \"<hex>\", ...],\n // \"secret_share\": \"<hex>\",\n // \"min_signers\": <u16>,\n // \"max_signers\": <u16>\n // }\n //\n // The struct is `#[serde(deny_unknown_fields)]` and the\n // `commitment` is a `VerifiableSecretSharingCommitment` (a\n // single-field tuple struct over `Vec<CoefficientCommitment>`),\n // which serde flattens to a bare JSON array. The earlier port\n // emitted camelCase keys plus a nested `commitment.coefficients`\n // shape and a numeric `identifier`, which Rust would reject and\n // which had no chance of being read by Rust's standard derive.\n const secretJson = JSON.parse(fs.readFileSync(round2SecretPath, \"utf-8\")) as {\n identifier: string;\n commitment: string[];\n secret_share: string;\n min_signers: number;\n max_signers: number;\n };\n\n // Identifier hex → little-endian u16 (the FROST 1-indexed\n // participant position). The scalar bytes are 32-LE for Ed25519, so\n // the first two bytes hold the u16 value when the identifier is in\n // the small-integer range (1..=N) used by the DKG.\n const idBytes = hexToBytes(secretJson.identifier);\n let identifierU16 = 1;\n if (idBytes.length >= 2) {\n identifierU16 = idBytes[0] | (idBytes[1] << 8);\n }\n if (identifierU16 === 0) {\n identifierU16 = 1;\n }\n const identifier = identifierFromU16(identifierU16);\n\n const coefficientCommitments = secretJson.commitment.map((hex) =>\n CoefficientCommitment.deserialize(Ed25519Sha512, hexToBytes(hex)),\n );\n\n const commitment = new VerifiableSecretSharingCommitment(Ed25519Sha512, coefficientCommitments);\n\n const secretShareScalar = Ed25519Sha512.deserializeScalar(hexToBytes(secretJson.secret_share));\n\n const secretPackage: DkgRound2SecretPackage = new round2.SecretPackage(\n Ed25519Sha512,\n identifier,\n commitment,\n secretShareScalar,\n secretJson.min_signers,\n secretJson.max_signers,\n );\n\n // Load collected Round 1 packages (from round2 phase)\n const round1Path = path.join(stateDir, \"collected_round1.json\");\n if (!fs.existsSync(round1Path)) {\n throw new Error(`Round 1 packages not found at ${round1Path}. Did you receive earlier phases?`);\n }\n\n const round1Json = JSON.parse(fs.readFileSync(round1Path, \"utf-8\")) as Record<string, unknown>;\n\n // Convert to Map<string, DkgRound1Package> - keyed by XID UR string\n const round1Packages = new Map<string, DkgRound1Package>();\n for (const [xidStr, value] of Object.entries(round1Json)) {\n const packageJson = value as {\n header: { version: number; ciphersuite: string };\n commitment: string[];\n proof_of_knowledge: string;\n };\n const pkg = serde.round1PackageFromJson(packageJson);\n round1Packages.set(xidStr, pkg);\n }\n\n return { secretPackage, round1Packages };\n}\n\n/**\n * Validate the finalize request from the coordinator.\n *\n * Port of request validation from cmd/dkg/participant/finalize.rs lines 139-161.\n */\nfunction validateFinalizeRequest(\n sealedRequest: SealedRequest,\n groupId: ARID,\n expectedCoordinator: XID,\n): ARID {\n // Validate the request function\n if (!sealedRequest.function().equals(EnvelopeFunction.fromString(\"dkgFinalize\"))) {\n throw new Error(`Unexpected request function: ${sealedRequest.function().toString()}`);\n }\n\n // Validate the sender is the expected coordinator\n if (sealedRequest.sender().xid().urString() !== expectedCoordinator.urString()) {\n throw new Error(\n `Unexpected request sender: ${sealedRequest.sender().xid().urString()} ` +\n `(expected coordinator ${expectedCoordinator.urString()})`,\n );\n }\n\n // Validate the group ID matches\n const requestGroupIdEnvelope = sealedRequest.objectForParameter(\"group\");\n if (requestGroupIdEnvelope === undefined) {\n throw new Error(\"Request missing group parameter\");\n }\n const requestGroupId = requestGroupIdEnvelope.extractSubject((cbor) => ARID.fromTaggedCbor(cbor));\n if (requestGroupId.urString() !== groupId.urString()) {\n throw new Error(\n `Request group ID ${requestGroupId.urString()} does not match expected ${groupId.urString()}`,\n );\n }\n\n // Extract where we should post our response\n const responseAridEnvelope = sealedRequest.objectForParameter(\"responseArid\");\n if (responseAridEnvelope === undefined) {\n throw new Error(\"Request missing responseArid parameter\");\n }\n const responseArid = responseAridEnvelope.extractSubject((cbor) => ARID.fromTaggedCbor(cbor));\n\n return responseArid;\n}\n\n/**\n * Extract round 2 packages from the finalize request.\n *\n * Port of round2 package extraction from cmd/dkg/participant/finalize.rs lines 209-229.\n */\nfunction extractFinalizePackages(\n request: SealedRequest,\n groupRecord: GroupRecord,\n ownerXid: XID,\n): Map<string, DkgRound2Package> {\n // Build XID -> Identifier mapping based on sorted participant order\n const sortedXids: XID[] = groupRecord.participants().map((p) => p.xid());\n\n // Add owner if not already in list\n const ownerUrString = ownerXid.urString();\n if (!sortedXids.some((xid) => xid.urString() === ownerUrString)) {\n sortedXids.push(ownerXid);\n }\n\n // Sort by XID byte order — mirrors Rust `XID::cmp` (raw 32-byte\n // lex compare). The earlier port used `urString().localeCompare(...)`,\n // which differs from byte order for any byte ≥ 0x80 and is locale-\n // aware, producing different FROST identifier assignments than Rust.\n sortedXids.sort((a, b) => compareXidBytes(a.toData(), b.toData()));\n\n // Deduplicate\n const deduped: XID[] = [];\n for (const xid of sortedXids) {\n if (deduped.length === 0 || deduped[deduped.length - 1].urString() !== xid.urString()) {\n deduped.push(xid);\n }\n }\n\n // Build XID -> Identifier mapping (1-indexed)\n const xidToIdentifier = new Map<string, FrostIdentifier>();\n for (let i = 0; i < deduped.length; i++) {\n const identifier = identifierFromU16(i + 1);\n xidToIdentifier.set(deduped[i].urString(), identifier);\n }\n\n const myXidStr = ownerXid.urString();\n\n // Extract all round2Package parameters\n const packages = new Map<string, DkgRound2Package>();\n\n const packageEnvelopes = request.objectsForParameter(\"round2Package\");\n for (const packageEnvelope of packageEnvelopes) {\n // Extract sender XID from the envelope\n const senderEnvelope = packageEnvelope.objectForPredicate(\"sender\");\n if (senderEnvelope === undefined) {\n throw new Error(\"round2Package missing sender predicate\");\n }\n const senderXid = senderEnvelope.extractSubject((cbor) => XID.fromTaggedCbor(cbor));\n\n // Skip our own package\n if (senderXid.urString() === myXidStr) {\n continue;\n }\n\n // Get the identifier for this sender\n const identifier = xidToIdentifier.get(senderXid.urString());\n if (identifier === undefined) {\n throw new Error(`Unknown sender XID in round2Package: ${senderXid.urString()}`);\n }\n\n // Extract the package bytes (stored as JSON tag)\n const packageJson = packageEnvelope.extractSubject((cbor) => JSONWrapper.fromTaggedCbor(cbor));\n const packageData = JSON.parse(new TextDecoder().decode(packageJson.toData())) as {\n header: { version: number; ciphersuite: string };\n signing_share: string;\n };\n\n const pkg = serde.round2PackageFromJson(packageData);\n packages.set(identifierToHex(identifier), pkg);\n }\n\n return packages;\n}\n\n/**\n * Build the response body for the finalize response.\n *\n * Port of `build_response_body()` from cmd/dkg/participant/finalize.rs lines 344-359.\n */\nfunction buildResponseBody(\n groupId: ARID,\n participantXid: XID,\n keyPackage: FrostKeyPackage,\n publicKeyPackage: FrostPublicKeyPackage,\n): Envelope {\n // Serialize key packages to JSON\n const keyPackageJson = serializeKeyPackage(keyPackage);\n const publicKeyPackageJson = serializePublicKeyPackage(publicKeyPackage);\n\n const keyJsonBytes = new TextEncoder().encode(JSON.stringify(keyPackageJson));\n const keyJsonWrapper = JSONWrapper.fromData(keyJsonBytes);\n\n const pubJsonBytes = new TextEncoder().encode(JSON.stringify(publicKeyPackageJson));\n const pubJsonWrapper = JSONWrapper.fromData(pubJsonBytes);\n\n return Envelope.unit()\n .addType(\"dkgFinalizeResponse\")\n .addAssertion(\"group\", groupId)\n .addAssertion(\"participant\", participantXid)\n .addAssertion(\"key_package\", keyJsonWrapper)\n .addAssertion(\"public_key_package\", pubJsonWrapper);\n}\n\n/**\n * Persist finalize state (key packages) to disk.\n *\n * Port of key package persistence from cmd/dkg/participant/finalize.rs lines 251-257.\n */\nfunction persistFinalizeState(\n registryPath: string,\n groupId: ARID,\n keyPackage: FrostKeyPackage,\n publicKeyPackage: FrostPublicKeyPackage,\n): { keyPackagePath: string; publicKeyPackagePath: string } {\n const stateDir = groupStateDir(registryPath, groupId.hex());\n fs.mkdirSync(stateDir, { recursive: true });\n\n // Serialize and save key package\n const serializedKeyPackage = serializeKeyPackage(keyPackage);\n const keyPackagePath = path.join(stateDir, \"key_package.json\");\n fs.writeFileSync(keyPackagePath, JSON.stringify(serializedKeyPackage, null, 2));\n\n // Serialize and save public key package\n const serializedPublicKeyPackage = serializePublicKeyPackage(publicKeyPackage);\n const publicKeyPackagePath = path.join(stateDir, \"public_key_package.json\");\n fs.writeFileSync(publicKeyPackagePath, JSON.stringify(serializedPublicKeyPackage, null, 2));\n\n return { keyPackagePath, publicKeyPackagePath };\n}\n\n/**\n * Execute the DKG participant finalize command.\n *\n * Responds to the finalize request from the coordinator, runs FROST DKG part3\n * to generate the final key package, and posts the response back.\n *\n * Port of `CommandArgs::exec()` from cmd/dkg/participant/finalize.rs lines 52-341.\n */\nexport async function finalize(\n _client: StorageClient | undefined,\n options: DkgFinalizeOptions,\n cwd: string,\n): Promise<DkgFinalizeResult> {\n if (options.storageSelection === undefined) {\n throw new Error(\"Hubert storage is required for finalize respond\");\n }\n\n const registryPath = resolveRegistryPath(options.registryPath, cwd);\n const registry = Registry.load(registryPath);\n\n const owner = registry.owner();\n if (owner === undefined) {\n throw new Error(\"Registry owner is required\");\n }\n\n const groupId = parseAridUr(options.groupId);\n const groupRecord = registry.group(groupId);\n if (groupRecord === undefined) {\n throw new Error(\"Group not found in registry\");\n }\n\n // Get the ARID where we're listening for the finalize request\n const listeningAtArid = groupRecord.listeningAtArid();\n if (listeningAtArid === undefined) {\n throw new Error(\"No listening ARID for this group. Did you receive finalize send?\");\n }\n\n // Load Round 2 state (secret and collected round1 packages)\n const round2State = loadRound2State(registryPath, groupId);\n\n if (isVerbose() || options.verbose === true) {\n console.error(\"Fetching finalize request from Hubert...\");\n }\n\n const client = await createStorageClient(options.storageSelection);\n\n // Fetch the finalize request from where we're listening\n const requestEnvelope = await getWithIndicator(\n client,\n listeningAtArid,\n \"Finalize request\",\n options.timeoutSeconds,\n options.verbose ?? false,\n );\n\n if (requestEnvelope === null || requestEnvelope === undefined) {\n throw new Error(\"Finalize request not found in Hubert storage\");\n }\n\n // Decrypt and validate the request\n const ownerPrivateKeys = owner.xidDocument().inceptionPrivateKeys();\n if (ownerPrivateKeys === undefined) {\n throw new Error(\"Owner XID document has no private keys\");\n }\n\n const now = CborDate.now().datetime();\n const sealedRequest = SealedRequest.tryFromEnvelope(\n requestEnvelope,\n undefined,\n now,\n ownerPrivateKeys,\n );\n\n // Validate the request and extract response ARID\n const expectedCoordinator = groupRecord.coordinator().xid();\n const responseArid = validateFinalizeRequest(sealedRequest, groupId, expectedCoordinator);\n\n // Build identifier mapping for round1 packages (XID UR -> Identifier hex)\n const sortedXids: XID[] = groupRecord.participants().map((p) => p.xid());\n\n // Add owner if not already in list\n const ownerUrString = owner.xid().urString();\n if (!sortedXids.some((xid) => xid.urString() === ownerUrString)) {\n sortedXids.push(owner.xid());\n }\n\n // Sort by XID byte order — mirrors Rust `XID::cmp` (raw 32-byte\n // lex compare). The earlier port used `urString().localeCompare(...)`,\n // which differs from byte order for any byte ≥ 0x80 and is locale-\n // aware, producing different FROST identifier assignments than Rust.\n sortedXids.sort((a, b) => compareXidBytes(a.toData(), b.toData()));\n\n // Deduplicate\n const deduped: XID[] = [];\n for (const xid of sortedXids) {\n if (deduped.length === 0 || deduped[deduped.length - 1].urString() !== xid.urString()) {\n deduped.push(xid);\n }\n }\n\n // Build XID -> Identifier mapping (1-indexed)\n const xidToIdentifier = new Map<string, FrostIdentifier>();\n for (let i = 0; i < deduped.length; i++) {\n const identifier = identifierFromU16(i + 1);\n xidToIdentifier.set(deduped[i].urString(), identifier);\n }\n\n // Convert round1 packages from XID-keyed to identifier-keyed (exclude self)\n const round1PackagesById = new Map<string, DkgRound1Package>();\n for (const [xidStr, pkg] of round2State.round1Packages) {\n if (xidStr === ownerUrString) {\n continue;\n }\n const identifier = xidToIdentifier.get(xidStr);\n if (identifier === undefined) {\n throw new Error(`Unknown participant XID ${xidStr}`);\n }\n round1PackagesById.set(identifierToHex(identifier), pkg);\n }\n\n // Extract Round 2 packages from the request (exclude self)\n const round2PackagesById = extractFinalizePackages(sealedRequest, groupRecord, owner.xid());\n\n if (isVerbose() || options.verbose === true) {\n console.error(`Received ${round2PackagesById.size} Round 2 packages. Running DKG part3...`);\n }\n\n // Run FROST DKG part3 (finalize)\n const [keyPackage, publicKeyPackage] = await dkgPart3(\n round2State.secretPackage,\n round1PackagesById,\n round2PackagesById,\n );\n\n // Get the group verifying key\n const verifyingKeyBytes = publicKeyPackage.verifyingKey;\n const groupVerifyingKey = signingKeyFromVerifying(verifyingKeyBytes);\n\n if (isVerbose() || options.verbose === true) {\n console.error(\"Generated key package and public key package.\");\n }\n\n // Persist key packages\n const { keyPackagePath, publicKeyPackagePath } = persistFinalizeState(\n registryPath,\n groupId,\n keyPackage,\n publicKeyPackage,\n );\n\n // Build response body\n const responseBody = buildResponseBody(groupId, owner.xid(), keyPackage, publicKeyPackage);\n\n const signerPrivateKeys = owner.xidDocument().inceptionPrivateKeys();\n if (signerPrivateKeys === undefined) {\n throw new Error(\"Owner XID document has no signing keys\");\n }\n\n // Get coordinator's XID document for encryption\n const coordinatorXid = groupRecord.coordinator().xid();\n const coordinatorRecord = registry.participant(coordinatorXid);\n let coordinatorDoc: XIDDocument;\n if (coordinatorRecord !== undefined) {\n coordinatorDoc = coordinatorRecord.xidDocument();\n } else {\n // Check if coordinator is the owner\n if (owner.xid().urString() === coordinatorXid.urString()) {\n coordinatorDoc = owner.xidDocument();\n } else {\n throw new Error(`Coordinator ${coordinatorXid.urString()} not found in registry`);\n }\n }\n\n // Get peer continuation from the request\n const peerContinuation = sealedRequest.peerContinuation();\n\n let sealed = SealedResponse.newSuccess(sealedRequest.id(), owner.xidDocument()).withResult(\n responseBody,\n );\n\n if (peerContinuation !== undefined) {\n sealed = sealed.withPeerContinuation(peerContinuation);\n }\n\n if (options.preview === true) {\n // Show the response envelope structure without encryption\n if (isVerbose() || options.verbose === true) {\n // Cast to access urString method\n const verifyingKeyWithUrString = groupVerifyingKey as { urString?: () => string };\n if (typeof verifyingKeyWithUrString.urString === \"function\") {\n console.error(verifyingKeyWithUrString.urString());\n }\n }\n const unsealedEnvelope = sealed.toEnvelope(\n undefined, // No expiration for responses\n signerPrivateKeys,\n undefined,\n );\n console.log(unsealedEnvelope.urString());\n\n return {\n verifyingKey: bytesToHex(verifyingKeyBytes),\n keyPackagePath,\n publicKeyPackagePath,\n };\n }\n\n const responseEnvelope = sealed.toEnvelope(\n undefined, // No expiration for responses\n signerPrivateKeys,\n coordinatorDoc,\n );\n\n // Post the response\n await putWithIndicator(\n client,\n responseArid,\n responseEnvelope,\n \"Finalize Response\",\n options.verbose ?? false,\n );\n\n // Update registry: contributions and verifying key\n const updatedGroupRecord = registry.group(groupId);\n if (updatedGroupRecord !== undefined) {\n const contributions = updatedGroupRecord.contributions();\n contributions.keyPackage = keyPackagePath;\n updatedGroupRecord.setContributions(contributions);\n updatedGroupRecord.clearListeningAtArid();\n\n // Set verifying key if the method exists\n const recordWithVerifyingKey = updatedGroupRecord as {\n setVerifyingKey?: (key: unknown) => void;\n };\n if (typeof recordWithVerifyingKey.setVerifyingKey === \"function\") {\n recordWithVerifyingKey.setVerifyingKey(groupVerifyingKey);\n }\n\n registry.save(registryPath);\n }\n\n // Get verifying key for output\n const verifyingKeyHex = bytesToHex(verifyingKeyBytes);\n\n if (isVerbose() || options.verbose === true) {\n console.error(`Posted finalize response to ${responseArid.urString()}`);\n // Cast to access urString method\n const verifyingKeyWithUrString = groupVerifyingKey as { urString?: () => string };\n if (typeof verifyingKeyWithUrString.urString === \"function\") {\n console.error(verifyingKeyWithUrString.urString());\n }\n } else {\n // Cast to access urString method\n const verifyingKeyWithUrString = groupVerifyingKey as { urString?: () => string };\n if (typeof verifyingKeyWithUrString.urString === \"function\") {\n console.log(verifyingKeyWithUrString.urString());\n }\n }\n\n return {\n verifyingKey: verifyingKeyHex,\n keyPackagePath,\n publicKeyPackagePath,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+EA,SAAS,gBAAgB,cAAsB,SAA4B;CACzE,MAAM,WAAW,cAAc,cAAc,QAAQ,IAAI,CAAC;CAG1D,MAAM,mBAAmB,KAAK,KAAK,UAAU,oBAAoB;CACjE,IAAI,CAAC,GAAG,WAAW,gBAAgB,GACjC,MAAM,IAAI,MAAM,+BAA+B,iBAAiB,sBAAsB;CAqBxF,MAAM,aAAa,KAAK,MAAM,GAAG,aAAa,kBAAkB,OAAO,CAAC;CAYxE,MAAM,UAAU,WAAW,WAAW,UAAU;CAChD,IAAI,gBAAgB;CACpB,IAAI,QAAQ,UAAU,GACpB,gBAAgB,QAAQ,KAAM,QAAQ,MAAM;CAE9C,IAAI,kBAAkB,GACpB,gBAAgB;CAElB,MAAM,aAAa,kBAAkB,aAAa;CAMlD,MAAM,aAAa,IAAI,kCAAkC,eAJ1B,WAAW,WAAW,KAAK,QACxD,sBAAsB,YAAY,eAAe,WAAW,GAAG,CAAC,CAG2B,CAAC;CAE9F,MAAM,oBAAoB,cAAc,kBAAkB,WAAW,WAAW,YAAY,CAAC;CAE7F,MAAM,gBAAwC,IAAI,OAAO,cACvD,eACA,YACA,YACA,mBACA,WAAW,aACX,WAAW,WACb;CAGA,MAAM,aAAa,KAAK,KAAK,UAAU,uBAAuB;CAC9D,IAAI,CAAC,GAAG,WAAW,UAAU,GAC3B,MAAM,IAAI,MAAM,iCAAiC,WAAW,kCAAkC;CAGhG,MAAM,aAAa,KAAK,MAAM,GAAG,aAAa,YAAY,OAAO,CAAC;CAGlE,MAAM,iCAAiB,IAAI,IAA8B;CACzD,KAAK,MAAM,CAAC,QAAQ,UAAU,OAAO,QAAQ,UAAU,GAAG;EACxD,MAAM,cAAc;EAKpB,MAAM,MAAM,MAAM,sBAAsB,WAAW;EACnD,eAAe,IAAI,QAAQ,GAAG;CAChC;CAEA,OAAO;EAAE;EAAe;CAAe;AACzC;;;;;;AAOA,SAAS,wBACP,eACA,SACA,qBACM;CAEN,IAAI,CAAC,cAAc,SAAS,CAAC,CAAC,OAAOA,SAAiB,WAAW,aAAa,CAAC,GAC7E,MAAM,IAAI,MAAM,gCAAgC,cAAc,SAAS,CAAC,CAAC,SAAS,GAAG;CAIvF,IAAI,cAAc,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,MAAM,oBAAoB,SAAS,GAC3E,MAAM,IAAI,MACR,8BAA8B,cAAc,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,yBAC3C,oBAAoB,SAAS,EAAE,EAC5D;CAIF,MAAM,yBAAyB,cAAc,mBAAmB,OAAO;CACvE,IAAI,2BAA2B,KAAA,GAC7B,MAAM,IAAI,MAAM,iCAAiC;CAEnD,MAAM,iBAAiB,uBAAuB,gBAAgB,SAAS,KAAK,eAAe,IAAI,CAAC;CAChG,IAAI,eAAe,SAAS,MAAM,QAAQ,SAAS,GACjD,MAAM,IAAI,MACR,oBAAoB,eAAe,SAAS,EAAE,2BAA2B,QAAQ,SAAS,GAC5F;CAIF,MAAM,uBAAuB,cAAc,mBAAmB,cAAc;CAC5E,IAAI,yBAAyB,KAAA,GAC3B,MAAM,IAAI,MAAM,wCAAwC;CAI1D,OAFqB,qBAAqB,gBAAgB,SAAS,KAAK,eAAe,IAAI,CAEzE;AACpB;;;;;;AAOA,SAAS,wBACP,SACA,aACA,UAC+B;CAE/B,MAAM,aAAoB,YAAY,aAAa,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI,CAAC;CAGvE,MAAM,gBAAgB,SAAS,SAAS;CACxC,IAAI,CAAC,WAAW,MAAM,QAAQ,IAAI,SAAS,MAAM,aAAa,GAC5D,WAAW,KAAK,QAAQ;CAO1B,WAAW,MAAM,GAAG,MAAM,gBAAgB,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;CAGjE,MAAM,UAAiB,CAAC;CACxB,KAAK,MAAM,OAAO,YAChB,IAAI,QAAQ,WAAW,KAAK,QAAQ,QAAQ,SAAS,EAAE,CAAC,SAAS,MAAM,IAAI,SAAS,GAClF,QAAQ,KAAK,GAAG;CAKpB,MAAM,kCAAkB,IAAI,IAA6B;CACzD,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,aAAa,kBAAkB,IAAI,CAAC;EAC1C,gBAAgB,IAAI,QAAQ,EAAE,CAAC,SAAS,GAAG,UAAU;CACvD;CAEA,MAAM,WAAW,SAAS,SAAS;CAGnC,MAAM,2BAAW,IAAI,IAA8B;CAEnD,MAAM,mBAAmB,QAAQ,oBAAoB,eAAe;CACpE,KAAK,MAAM,mBAAmB,kBAAkB;EAE9C,MAAM,iBAAiB,gBAAgB,mBAAmB,QAAQ;EAClE,IAAI,mBAAmB,KAAA,GACrB,MAAM,IAAI,MAAM,wCAAwC;EAE1D,MAAM,YAAY,eAAe,gBAAgB,SAAS,IAAI,eAAe,IAAI,CAAC;EAGlF,IAAI,UAAU,SAAS,MAAM,UAC3B;EAIF,MAAM,aAAa,gBAAgB,IAAI,UAAU,SAAS,CAAC;EAC3D,IAAI,eAAe,KAAA,GACjB,MAAM,IAAI,MAAM,wCAAwC,UAAU,SAAS,GAAG;EAIhF,MAAM,cAAc,gBAAgB,gBAAgB,SAASC,OAAY,eAAe,IAAI,CAAC;EAC7F,MAAM,cAAc,KAAK,MAAM,IAAI,YAAY,CAAC,CAAC,OAAO,YAAY,OAAO,CAAC,CAAC;EAK7E,MAAM,MAAM,MAAM,sBAAsB,WAAW;EACnD,SAAS,IAAI,gBAAgB,UAAU,GAAG,GAAG;CAC/C;CAEA,OAAO;AACT;;;;;;AAOA,SAAS,kBACP,SACA,gBACA,YACA,kBACU;CAEV,MAAM,iBAAiB,oBAAoB,UAAU;CACrD,MAAM,uBAAuB,0BAA0B,gBAAgB;CAEvE,MAAM,eAAe,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,UAAU,cAAc,CAAC;CAC5E,MAAM,iBAAiBA,OAAY,SAAS,YAAY;CAExD,MAAM,eAAe,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,UAAU,oBAAoB,CAAC;CAClF,MAAM,iBAAiBA,OAAY,SAAS,YAAY;CAExD,OAAO,SAAS,KAAK,CAAC,CACnB,QAAQ,qBAAqB,CAAC,CAC9B,aAAa,SAAS,OAAO,CAAC,CAC9B,aAAa,eAAe,cAAc,CAAC,CAC3C,aAAa,eAAe,cAAc,CAAC,CAC3C,aAAa,sBAAsB,cAAc;AACtD;;;;;;AAOA,SAAS,qBACP,cACA,SACA,YACA,kBAC0D;CAC1D,MAAM,WAAW,cAAc,cAAc,QAAQ,IAAI,CAAC;CAC1D,GAAG,UAAU,UAAU,EAAE,WAAW,KAAK,CAAC;CAG1C,MAAM,uBAAuB,oBAAoB,UAAU;CAC3D,MAAM,iBAAiB,KAAK,KAAK,UAAU,kBAAkB;CAC7D,GAAG,cAAc,gBAAgB,KAAK,UAAU,sBAAsB,MAAM,CAAC,CAAC;CAG9E,MAAM,6BAA6B,0BAA0B,gBAAgB;CAC7E,MAAM,uBAAuB,KAAK,KAAK,UAAU,yBAAyB;CAC1E,GAAG,cAAc,sBAAsB,KAAK,UAAU,4BAA4B,MAAM,CAAC,CAAC;CAE1F,OAAO;EAAE;EAAgB;CAAqB;AAChD;;;;;;;;;AAUA,eAAsB,SACpB,SACA,SACA,KAC4B;CAC5B,IAAI,QAAQ,qBAAqB,KAAA,GAC/B,MAAM,IAAI,MAAM,iDAAiD;CAGnE,MAAM,eAAe,oBAAoB,QAAQ,cAAc,GAAG;CAClE,MAAM,WAAW,SAAS,KAAK,YAAY;CAE3C,MAAM,QAAQ,SAAS,MAAM;CAC7B,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MAAM,4BAA4B;CAG9C,MAAM,UAAU,YAAY,QAAQ,OAAO;CAC3C,MAAM,cAAc,SAAS,MAAM,OAAO;CAC1C,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,MAAM,6BAA6B;CAI/C,MAAM,kBAAkB,YAAY,gBAAgB;CACpD,IAAI,oBAAoB,KAAA,GACtB,MAAM,IAAI,MAAM,kEAAkE;CAIpF,MAAM,cAAc,gBAAgB,cAAc,OAAO;CAEzD,IAAI,UAAU,KAAK,QAAQ,YAAY,MACrC,QAAQ,MAAM,0CAA0C;CAG1D,MAAM,SAAS,MAAM,oBAAoB,QAAQ,gBAAgB;CAGjE,MAAM,kBAAkB,MAAM,iBAC5B,QACA,iBACA,oBACA,QAAQ,gBACR,QAAQ,WAAW,KACrB;CAEA,IAAI,oBAAoB,QAAQ,oBAAoB,KAAA,GAClD,MAAM,IAAI,MAAM,8CAA8C;CAIhE,MAAM,mBAAmB,MAAM,YAAY,CAAC,CAAC,qBAAqB;CAClE,IAAI,qBAAqB,KAAA,GACvB,MAAM,IAAI,MAAM,wCAAwC;CAG1D,MAAM,MAAM,SAAS,IAAI,CAAC,CAAC,SAAS;CACpC,MAAM,gBAAgB,cAAc,gBAClC,iBACA,KAAA,GACA,KACA,gBACF;CAIA,MAAM,eAAe,wBAAwB,eAAe,SADhC,YAAY,YAAY,CAAC,CAAC,IACiC,CAAC;CAGxF,MAAM,aAAoB,YAAY,aAAa,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI,CAAC;CAGvE,MAAM,gBAAgB,MAAM,IAAI,CAAC,CAAC,SAAS;CAC3C,IAAI,CAAC,WAAW,MAAM,QAAQ,IAAI,SAAS,MAAM,aAAa,GAC5D,WAAW,KAAK,MAAM,IAAI,CAAC;CAO7B,WAAW,MAAM,GAAG,MAAM,gBAAgB,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;CAGjE,MAAM,UAAiB,CAAC;CACxB,KAAK,MAAM,OAAO,YAChB,IAAI,QAAQ,WAAW,KAAK,QAAQ,QAAQ,SAAS,EAAE,CAAC,SAAS,MAAM,IAAI,SAAS,GAClF,QAAQ,KAAK,GAAG;CAKpB,MAAM,kCAAkB,IAAI,IAA6B;CACzD,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,aAAa,kBAAkB,IAAI,CAAC;EAC1C,gBAAgB,IAAI,QAAQ,EAAE,CAAC,SAAS,GAAG,UAAU;CACvD;CAGA,MAAM,qCAAqB,IAAI,IAA8B;CAC7D,KAAK,MAAM,CAAC,QAAQ,QAAQ,YAAY,gBAAgB;EACtD,IAAI,WAAW,eACb;EAEF,MAAM,aAAa,gBAAgB,IAAI,MAAM;EAC7C,IAAI,eAAe,KAAA,GACjB,MAAM,IAAI,MAAM,2BAA2B,QAAQ;EAErD,mBAAmB,IAAI,gBAAgB,UAAU,GAAG,GAAG;CACzD;CAGA,MAAM,qBAAqB,wBAAwB,eAAe,aAAa,MAAM,IAAI,CAAC;CAE1F,IAAI,UAAU,KAAK,QAAQ,YAAY,MACrC,QAAQ,MAAM,YAAY,mBAAmB,KAAK,wCAAwC;CAI5F,MAAM,CAAC,YAAY,oBAAoB,MAAM,SAC3C,YAAY,eACZ,oBACA,kBACF;CAGA,MAAM,oBAAoB,iBAAiB;CAC3C,MAAM,oBAAoB,wBAAwB,iBAAiB;CAEnE,IAAI,UAAU,KAAK,QAAQ,YAAY,MACrC,QAAQ,MAAM,+CAA+C;CAI/D,MAAM,EAAE,gBAAgB,yBAAyB,qBAC/C,cACA,SACA,YACA,gBACF;CAGA,MAAM,eAAe,kBAAkB,SAAS,MAAM,IAAI,GAAG,YAAY,gBAAgB;CAEzF,MAAM,oBAAoB,MAAM,YAAY,CAAC,CAAC,qBAAqB;CACnE,IAAI,sBAAsB,KAAA,GACxB,MAAM,IAAI,MAAM,wCAAwC;CAI1D,MAAM,iBAAiB,YAAY,YAAY,CAAC,CAAC,IAAI;CACrD,MAAM,oBAAoB,SAAS,YAAY,cAAc;CAC7D,IAAI;CACJ,IAAI,sBAAsB,KAAA,GACxB,iBAAiB,kBAAkB,YAAY;MAG/C,IAAI,MAAM,IAAI,CAAC,CAAC,SAAS,MAAM,eAAe,SAAS,GACrD,iBAAiB,MAAM,YAAY;MAEnC,MAAM,IAAI,MAAM,eAAe,eAAe,SAAS,EAAE,uBAAuB;CAKpF,MAAM,mBAAmB,cAAc,iBAAiB;CAExD,IAAI,SAAS,eAAe,WAAW,cAAc,GAAG,GAAG,MAAM,YAAY,CAAC,CAAC,CAAC,WAC9E,YACF;CAEA,IAAI,qBAAqB,KAAA,GACvB,SAAS,OAAO,qBAAqB,gBAAgB;CAGvD,IAAI,QAAQ,YAAY,MAAM;EAE5B,IAAI,UAAU,KAAK,QAAQ,YAAY,MAAM;GAE3C,MAAM,2BAA2B;GACjC,IAAI,OAAO,yBAAyB,aAAa,YAC/C,QAAQ,MAAM,yBAAyB,SAAS,CAAC;EAErD;EACA,MAAM,mBAAmB,OAAO,WAC9B,KAAA,GACA,mBACA,KAAA,CACF;EACA,QAAQ,IAAI,iBAAiB,SAAS,CAAC;EAEvC,OAAO;GACL,cAAc,WAAW,iBAAiB;GAC1C;GACA;EACF;CACF;CASA,MAAM,iBACJ,QACA,cATuB,OAAO,WAC9B,KAAA,GACA,mBACA,cAOe,GACf,qBACA,QAAQ,WAAW,KACrB;CAGA,MAAM,qBAAqB,SAAS,MAAM,OAAO;CACjD,IAAI,uBAAuB,KAAA,GAAW;EACpC,MAAM,gBAAgB,mBAAmB,cAAc;EACvD,cAAc,aAAa;EAC3B,mBAAmB,iBAAiB,aAAa;EACjD,mBAAmB,qBAAqB;EAGxC,MAAM,yBAAyB;EAG/B,IAAI,OAAO,uBAAuB,oBAAoB,YACpD,uBAAuB,gBAAgB,iBAAiB;EAG1D,SAAS,KAAK,YAAY;CAC5B;CAGA,MAAM,kBAAkB,WAAW,iBAAiB;CAEpD,IAAI,UAAU,KAAK,QAAQ,YAAY,MAAM;EAC3C,QAAQ,MAAM,+BAA+B,aAAa,SAAS,GAAG;EAEtE,MAAM,2BAA2B;EACjC,IAAI,OAAO,yBAAyB,aAAa,YAC/C,QAAQ,MAAM,yBAAyB,SAAS,CAAC;CAErD,OAAO;EAEL,MAAM,2BAA2B;EACjC,IAAI,OAAO,yBAAyB,aAAa,YAC/C,QAAQ,IAAI,yBAAyB,SAAS,CAAC;CAEnD;CAEA,OAAO;EACL,cAAc;EACd;EACA;CACF;AACF"}
|
package/dist/frost/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as __exportAll } from "../rolldown-runtime-
|
|
1
|
+
import { t as __exportAll } from "../rolldown-runtime-BhDjJH2R.mjs";
|
|
2
2
|
import { Ed25519Sha512, Identifier, aggregate, commitRound1, keys, serde, signRound2 } from "@frosts/ed25519";
|
|
3
3
|
import { SigningPackageImpl } from "@frosts/core";
|
|
4
4
|
//#region src/frost/index.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { n as compareXidBytes, t as DkgProposedParticipant } from "./proposed-participant-Detb823_.mjs";
|
|
2
2
|
import { DkgInvitation, DkgInvite, accepted, declined } from "./dkg/index.mjs";
|
|
3
3
|
import { AddOutcome, ContributionPaths, GroupOutcome, GroupParticipant, GroupRecord, OwnerOutcome, OwnerRecord, ParticipantRecord, PendingRequests, Registry, resolveRegistryPath } from "./registry/index.mjs";
|
|
4
|
-
import { c as parseAridUr, h as signingKeyFromVerifying, n as isVerbose, r as setVerbose, t as groupStateDir } from "./common-
|
|
4
|
+
import { c as parseAridUr, h as signingKeyFromVerifying, n as isVerbose, r as setVerbose, t as groupStateDir } from "./common-k2QC6AST.mjs";
|
|
5
5
|
import { n as putWithIndicator, t as getWithIndicator } from "./busy-BlU8_pS2.mjs";
|
|
6
6
|
import { a as directionEmoji, c as fetchStatusPending, d as fetchStatusTimeout, f as parallelFetch, i as buildFetchRequests, l as fetchStatusRejected, m as parallelSend, n as DEFAULT_TIMEOUT_SECONDS, o as emptyCollectionResult, p as parallelFetchConfigWithTimeout, r as Direction, s as fetchStatusError, t as CollectionResult, u as fetchStatusSuccess } from "./parallel-PZiwHZT8.mjs";
|
|
7
|
-
import { i as createStorageClient, t as registry_exports } from "./registry-
|
|
8
|
-
import { n as dkg_exports, r as checkAridExists, t as sign_exports } from "./cmd-
|
|
7
|
+
import { i as createStorageClient, t as registry_exports } from "./registry-DkhElxx1.mjs";
|
|
8
|
+
import { n as dkg_exports, r as checkAridExists, t as sign_exports } from "./cmd-BOzLt8T8.mjs";
|
|
9
9
|
import { t as frost_exports } from "./frost/index.mjs";
|
|
10
10
|
//#region src/index.ts
|
|
11
11
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as __exportAll } from "./rolldown-runtime-
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
2
2
|
import { DkgInvite } from "./dkg/index.mjs";
|
|
3
3
|
import { GroupParticipant, GroupRecord, PendingRequests, Registry, resolveRegistryPath } from "./registry/index.mjs";
|
|
4
|
-
import { a as dkgStateDir, d as resolveOwnerXidDocument, f as resolveParticipants } from "./common-
|
|
4
|
+
import { a as dkgStateDir, d as resolveOwnerXidDocument, f as resolveParticipants } from "./common-k2QC6AST.mjs";
|
|
5
5
|
import { n as putWithIndicator } from "./busy-BlU8_pS2.mjs";
|
|
6
6
|
import { ARID } from "@bcts/components";
|
|
7
7
|
import * as fs from "node:fs";
|
|
@@ -93,4 +93,4 @@ async function invite(client, options, cwd) {
|
|
|
93
93
|
//#endregion
|
|
94
94
|
export { invite_exports as n, invite as t };
|
|
95
95
|
|
|
96
|
-
//# sourceMappingURL=invite-
|
|
96
|
+
//# sourceMappingURL=invite-B4pfTK_5.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invite-
|
|
1
|
+
{"version":3,"file":"invite-B4pfTK_5.mjs","names":[],"sources":["../src/cmd/dkg/coordinator/invite.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * DKG coordinator invite command.\n *\n * Port of cmd/dkg/coordinator/invite.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, type XID } from \"@bcts/components\";\n\nimport { DkgInvite } from \"../../../dkg/index.js\";\nimport {\n GroupParticipant,\n GroupRecord,\n PendingRequests,\n Registry,\n resolveRegistryPath,\n} from \"../../../registry/index.js\";\nimport { putWithIndicator } from \"../../busy.js\";\nimport { type StorageClient } from \"../../storage.js\";\nimport { dkgStateDir, resolveOwnerXidDocument, resolveParticipants } from \"../common.js\";\n\n/**\n * Options for the DKG invite command.\n */\nexport interface DkgInviteOptions {\n registryPath?: string;\n minSigners?: number;\n charter: string;\n validDays: number;\n participantNames: string[];\n verbose?: boolean;\n}\n\n/**\n * Result of the DKG invite command.\n */\nexport interface DkgInviteResult {\n groupId: ARID;\n requestId: ARID;\n envelopeUr: string;\n}\n\n/**\n * Internal data structure for building an invite.\n *\n * Port of `InviteData` struct from cmd/dkg/coordinator/invite.rs lines 122-126.\n */\ninterface InviteData {\n invite: DkgInvite;\n participantXids: XID[];\n pendingRequests: PendingRequests;\n}\n\n/**\n * Build the DKG invite with validation.\n *\n * Port of `build_invite()` from cmd/dkg/coordinator/invite.rs lines 128-181.\n */\nfunction buildInvite(\n registry: Registry,\n minSignersArg: number | undefined,\n charter: string,\n participantNames: string[],\n validDays: number,\n): InviteData {\n // Resolve participants using the common utility\n const resolved = resolveParticipants(registry, participantNames);\n const participantDocs: string[] = resolved.map(([, record]) => record.xidDocumentUr());\n const participantXids: XID[] = resolved.map(([xid]) => xid);\n\n // These are the ARIDs where participants will post their invite responses\n const collectFromArids: ARID[] = participantDocs.map(() => ARID.new());\n\n // Build pending_requests: coordinator will collect invite responses from these ARIDs\n const pendingRequests = new PendingRequests();\n for (let i = 0; i < participantXids.length; i++) {\n pendingRequests.addCollectOnly(participantXids[i], collectFromArids[i]);\n }\n\n // Validate participant count\n const participantCount = participantDocs.length;\n if (participantCount < 2) {\n throw new Error(\"At least two participants are required for a DKG invite\");\n }\n\n // Validate and default minSigners\n const minSigners = minSignersArg ?? participantCount;\n if (minSigners < 2) {\n throw new Error(\"--min-signers must be at least 2\");\n }\n if (minSigners > participantCount) {\n throw new Error(\"--min-signers cannot exceed participant count\");\n }\n\n // Get sender (registry owner)\n // The coordinator's invite always identifies the registry owner as\n // the sender. (Rust reads `registry.owner()` directly here too —\n // see `cmd/dkg/coordinator/invite.rs:74-77`.)\n const sender = resolveOwnerXidDocument(registry);\n\n // Calculate dates\n const now = new Date();\n const validUntil = new Date(Date.now() + validDays * 24 * 60 * 60 * 1000);\n\n // Create the invite\n const invite = DkgInvite.create(\n ARID.new(), // requestId\n sender,\n ARID.new(), // groupId\n now,\n validUntil,\n minSigners,\n charter,\n participantDocs,\n collectFromArids,\n );\n\n return { invite, participantXids, pendingRequests };\n}\n\n/**\n * Execute the DKG invite command.\n *\n * Port of `invite()` from cmd/dkg/coordinator/invite.rs.\n */\nexport async function invite(\n client: StorageClient,\n options: DkgInviteOptions,\n cwd: string,\n): Promise<DkgInviteResult> {\n const registryPath = resolveRegistryPath(options.registryPath, cwd);\n const registry = Registry.load(registryPath);\n\n // Build the invite with validation\n const inviteData = buildInvite(\n registry,\n options.minSigners,\n options.charter,\n options.participantNames,\n options.validDays,\n );\n\n const { invite: dkgInvite, participantXids, pendingRequests } = inviteData;\n const groupId = dkgInvite.groupId();\n const requestId = dkgInvite.requestId();\n\n // Create sealed envelope\n const envelope = dkgInvite.toEnvelope();\n\n // Send to storage\n const startArid = ARID.new();\n await putWithIndicator(\n client,\n startArid,\n envelope,\n \"Sending DKG invite\",\n options.verbose ?? false,\n );\n\n // Save group record to registry\n const owner = registry.owner();\n if (!owner) {\n throw new Error(\"Registry owner is required to issue invites\");\n }\n\n const coordinator = new GroupParticipant(owner.xid());\n const groupParticipants = participantXids.map((xid) => new GroupParticipant(xid));\n\n const groupRecord = new GroupRecord(\n options.charter,\n dkgInvite.minSigners(),\n coordinator,\n groupParticipants,\n );\n\n // Track pending requests\n groupRecord.setPendingRequests(pendingRequests);\n\n // Use recordGroup() for proper merge behavior\n registry.recordGroup(groupId, groupRecord);\n registry.save(registryPath);\n\n // Save invite state\n const stateDir = dkgStateDir(registryPath, groupId.hex());\n fs.mkdirSync(stateDir, { recursive: true });\n\n const inviteState: {\n group: string;\n request_id: string;\n start_arid: string;\n valid_until: string;\n participants: { xid: string; response_arid: string }[];\n } = {\n group: groupId.urString(),\n request_id: requestId.urString(),\n start_arid: startArid.urString(),\n valid_until: dkgInvite.validUntil().toISOString(),\n participants: dkgInvite.participants().map((p) => ({\n xid: p.xid().urString(),\n response_arid: p.responseArid().urString(),\n })),\n };\n\n fs.writeFileSync(path.join(stateDir, \"invite.json\"), JSON.stringify(inviteState, null, 2));\n\n if (options.verbose === true) {\n console.log(`Group ID: ${groupId.urString()}`);\n console.log(`Start ARID: ${startArid.urString()}`);\n }\n\n return {\n groupId,\n requestId,\n envelopeUr: envelope.urString(),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAkEA,SAAS,YACP,UACA,eACA,SACA,kBACA,WACY;CAEZ,MAAM,WAAW,oBAAoB,UAAU,gBAAgB;CAC/D,MAAM,kBAA4B,SAAS,KAAK,GAAG,YAAY,OAAO,cAAc,CAAC;CACrF,MAAM,kBAAyB,SAAS,KAAK,CAAC,SAAS,GAAG;CAG1D,MAAM,mBAA2B,gBAAgB,UAAU,KAAK,IAAI,CAAC;CAGrE,MAAM,kBAAkB,IAAI,gBAAgB;CAC5C,KAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAC1C,gBAAgB,eAAe,gBAAgB,IAAI,iBAAiB,EAAE;CAIxE,MAAM,mBAAmB,gBAAgB;CACzC,IAAI,mBAAmB,GACrB,MAAM,IAAI,MAAM,yDAAyD;CAI3E,MAAM,aAAa,iBAAiB;CACpC,IAAI,aAAa,GACf,MAAM,IAAI,MAAM,kCAAkC;CAEpD,IAAI,aAAa,kBACf,MAAM,IAAI,MAAM,+CAA+C;CAOjE,MAAM,SAAS,wBAAwB,QAAQ;CAG/C,MAAM,sBAAM,IAAI,KAAK;CACrB,MAAM,aAAa,IAAI,KAAK,KAAK,IAAI,IAAI,YAAY,KAAK,KAAK,KAAK,GAAI;CAexE,OAAO;EAAE,QAZM,UAAU,OACvB,KAAK,IAAI,GACT,QACA,KAAK,IAAI,GACT,KACA,YACA,YACA,SACA,iBACA,gBAGY;EAAG;EAAiB;CAAgB;AACpD;;;;;;AAOA,eAAsB,OACpB,QACA,SACA,KAC0B;CAC1B,MAAM,eAAe,oBAAoB,QAAQ,cAAc,GAAG;CAClE,MAAM,WAAW,SAAS,KAAK,YAAY;CAW3C,MAAM,EAAE,QAAQ,WAAW,iBAAiB,oBARzB,YACjB,UACA,QAAQ,YACR,QAAQ,SACR,QAAQ,kBACR,QAAQ,SAG+D;CACzE,MAAM,UAAU,UAAU,QAAQ;CAClC,MAAM,YAAY,UAAU,UAAU;CAGtC,MAAM,WAAW,UAAU,WAAW;CAGtC,MAAM,YAAY,KAAK,IAAI;CAC3B,MAAM,iBACJ,QACA,WACA,UACA,sBACA,QAAQ,WAAW,KACrB;CAGA,MAAM,QAAQ,SAAS,MAAM;CAC7B,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,6CAA6C;CAG/D,MAAM,cAAc,IAAI,iBAAiB,MAAM,IAAI,CAAC;CACpD,MAAM,oBAAoB,gBAAgB,KAAK,QAAQ,IAAI,iBAAiB,GAAG,CAAC;CAEhF,MAAM,cAAc,IAAI,YACtB,QAAQ,SACR,UAAU,WAAW,GACrB,aACA,iBACF;CAGA,YAAY,mBAAmB,eAAe;CAG9C,SAAS,YAAY,SAAS,WAAW;CACzC,SAAS,KAAK,YAAY;CAG1B,MAAM,WAAW,YAAY,cAAc,QAAQ,IAAI,CAAC;CACxD,GAAG,UAAU,UAAU,EAAE,WAAW,KAAK,CAAC;CAE1C,MAAM,cAMF;EACF,OAAO,QAAQ,SAAS;EACxB,YAAY,UAAU,SAAS;EAC/B,YAAY,UAAU,SAAS;EAC/B,aAAa,UAAU,WAAW,CAAC,CAAC,YAAY;EAChD,cAAc,UAAU,aAAa,CAAC,CAAC,KAAK,OAAO;GACjD,KAAK,EAAE,IAAI,CAAC,CAAC,SAAS;GACtB,eAAe,EAAE,aAAa,CAAC,CAAC,SAAS;EAC3C,EAAE;CACJ;CAEA,GAAG,cAAc,KAAK,KAAK,UAAU,aAAa,GAAG,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC;CAEzF,IAAI,QAAQ,YAAY,MAAM;EAC5B,QAAQ,IAAI,aAAa,QAAQ,SAAS,GAAG;EAC7C,QAAQ,IAAI,eAAe,UAAU,SAAS,GAAG;CACnD;CAEA,OAAO;EACL;EACA;EACA,YAAY,SAAS,SAAS;CAChC;AACF"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as __exportAll } from "./rolldown-runtime-
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
2
2
|
import { Registry, resolveRegistryPath } from "./registry/index.mjs";
|
|
3
|
-
import { c as parseAridUr } from "./common-
|
|
3
|
+
import { c as parseAridUr } from "./common-k2QC6AST.mjs";
|
|
4
4
|
import { n as putWithIndicator } from "./busy-BlU8_pS2.mjs";
|
|
5
|
-
import { n as signingStateDir } from "./common
|
|
5
|
+
import { n as signingStateDir } from "./common-DCyUSYkT.mjs";
|
|
6
6
|
import { ARID } from "@bcts/components";
|
|
7
7
|
import { CborDate } from "@bcts/dcbor";
|
|
8
8
|
import { Envelope } from "@bcts/envelope";
|
|
@@ -216,4 +216,4 @@ async function invite(client, options, cwd) {
|
|
|
216
216
|
//#endregion
|
|
217
217
|
export { invite as a, persistSessionState as c, gatherRecipientDocuments as i, validateCoordinator as l, buildSignInviteRequest as n, invite_exports as o, createSessionArids as r, loadEnvelopeFromPath as s, buildSessionStateJson as t };
|
|
218
218
|
|
|
219
|
-
//# sourceMappingURL=invite-
|
|
219
|
+
//# sourceMappingURL=invite-CPLnWY7c.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invite-DNlTkwzM.mjs","names":[],"sources":["../src/cmd/sign/coordinator/invite.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * Sign coordinator invite command.\n *\n * Port of cmd/sign/coordinator/invite.rs from frost-hubert-rust.\n *\n * @module\n */\n\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-unsafe-call */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-unsafe-argument */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { ARID } from \"@bcts/components\";\nimport { CborDate } from \"@bcts/dcbor\";\nimport { Envelope } from \"@bcts/envelope\";\nimport { SealedRequest } from \"@bcts/gstp\";\nimport { type XIDDocument } from \"@bcts/xid\";\n\nimport {\n Registry,\n resolveRegistryPath,\n type GroupRecord,\n type GroupParticipant,\n type OwnerRecord,\n} from \"../../../registry/index.js\";\nimport { putWithIndicator } from \"../../busy.js\";\nimport { type StorageClient } from \"../../storage.js\";\nimport { parseAridUr } from \"../../dkg/common.js\";\nimport { signingStateDir } from \"../common.js\";\n\n// -----------------------------------------------------------------------------\n// Session ARID management\n// -----------------------------------------------------------------------------\n\n/**\n * Session ARIDs for tracking the signing session.\n *\n * Port of `struct SessionArids` from cmd/sign/coordinator/invite.rs lines 151-156.\n */\nexport interface SessionArids {\n sessionId: ARID;\n startArid: ARID;\n commitArids: Map<string, ARID>; // Map<XID.urString(), ARID>\n shareArids: Map<string, ARID>; // Map<XID.urString(), ARID>\n}\n\n/**\n * Create new session ARIDs for all participants.\n *\n * Port of `SessionArids::new()` from cmd/sign/coordinator/invite.rs lines 158-173.\n */\nexport function createSessionArids(participants: GroupParticipant[]): SessionArids {\n const commitArids = new Map<string, ARID>();\n const shareArids = new Map<string, ARID>();\n\n for (const participant of participants) {\n const xidKey = participant.xid().urString();\n commitArids.set(xidKey, ARID.new());\n shareArids.set(xidKey, ARID.new());\n }\n\n return {\n sessionId: ARID.new(),\n startArid: ARID.new(),\n commitArids,\n shareArids,\n };\n}\n\n// -----------------------------------------------------------------------------\n// Validation\n// -----------------------------------------------------------------------------\n\n/**\n * Validate that the owner is the coordinator of the group.\n *\n * Port of `validate_coordinator()` from cmd/sign/coordinator/invite.rs lines 179-192.\n */\nexport function validateCoordinator(groupRecord: GroupRecord, owner: OwnerRecord): void {\n if (groupRecord.coordinator().xid().urString() !== owner.xid().urString()) {\n throw new Error(\n `Only the coordinator can start signing. Coordinator: ${groupRecord.coordinator().xid().urString()}, Owner: ${owner.xid().urString()}`,\n );\n }\n}\n\n// -----------------------------------------------------------------------------\n// Participant document gathering\n// -----------------------------------------------------------------------------\n\n/**\n * Gather XIDDocuments for all participants from the registry.\n *\n * Port of `gather_recipient_documents()` from cmd/sign/coordinator/invite.rs lines 198-222.\n */\nexport function gatherRecipientDocuments(\n participants: GroupParticipant[],\n owner: OwnerRecord,\n registry: Registry,\n): XIDDocument[] {\n const recipientDocs: XIDDocument[] = [];\n\n for (const participant of participants) {\n const xid = participant.xid();\n if (xid.urString() === owner.xid().urString()) {\n recipientDocs.push(owner.xidDocument());\n } else {\n const record = registry.participant(xid);\n if (record === undefined) {\n throw new Error(`Participant ${xid.urString()} not found in registry`);\n }\n recipientDocs.push(record.xidDocument());\n }\n }\n\n return recipientDocs;\n}\n\n// -----------------------------------------------------------------------------\n// Request building\n// -----------------------------------------------------------------------------\n\n/**\n * Context for building the sign invite request.\n *\n * Port of `struct SignInviteContext` from cmd/sign/coordinator/invite.rs lines 228-237.\n */\nexport interface SignInviteContext {\n arids: SessionArids;\n groupId: ARID;\n targetEnvelope: Envelope;\n groupRecord: GroupRecord;\n owner: OwnerRecord;\n registry: Registry;\n participants: GroupParticipant[];\n validUntil: Date;\n}\n\n/**\n * Build the sign invite request.\n *\n * Port of `build_sign_invite_request()` from cmd/sign/coordinator/invite.rs lines 239-284.\n */\nexport function buildSignInviteRequest(ctx: SignInviteContext): SealedRequest {\n let request = SealedRequest.new(\"signInvite\", ctx.arids.sessionId, ctx.owner.xidDocument())\n .withParameter(\"group\", ctx.groupId)\n .withParameter(\"session\", ctx.arids.sessionId)\n .withParameter(\"target\", ctx.targetEnvelope)\n .withParameter(\"minSigners\", ctx.groupRecord.minSigners())\n .withDate(new Date())\n .withParameter(\"validUntil\", CborDate.fromDatetime(ctx.validUntil));\n\n for (const participant of ctx.participants) {\n const xid = participant.xid();\n const xidKey = xid.urString();\n\n // Get participant document\n let participantDoc: XIDDocument;\n if (xidKey === ctx.owner.xid().urString()) {\n participantDoc = ctx.owner.xidDocument();\n } else {\n const record = ctx.registry.participant(xid);\n if (record === undefined) {\n throw new Error(\"Participant not found in registry\");\n }\n participantDoc = record.xidDocument();\n }\n\n // Get encryption key\n const encryptionKey = participantDoc.encryptionKey();\n if (encryptionKey === undefined) {\n throw new Error(\"Participant XID document has no encryption key\");\n }\n\n // Get commit ARID for this participant\n const responseArid = ctx.arids.commitArids.get(xidKey);\n if (responseArid === undefined) {\n throw new Error(\"commit ARID not found for participant\");\n }\n\n // Encrypt response ARID to participant\n // @ts-expect-error TS2339 - API mismatch: toEnvelope/encryptToRecipient methods\n const encryptedResponseArid = responseArid.toEnvelope().encryptToRecipient(encryptionKey);\n\n // Build participant entry envelope\n const participantEntry = Envelope.new(xid).addAssertion(\"response_arid\", encryptedResponseArid);\n\n request = request.withParameter(\"participant\", participantEntry);\n }\n\n return request;\n}\n\n// -----------------------------------------------------------------------------\n// State persistence\n// -----------------------------------------------------------------------------\n\n/**\n * Build the session state JSON for persistence.\n *\n * Port of `build_session_state_json()` from cmd/sign/coordinator/invite.rs lines 290-346.\n */\nexport function buildSessionStateJson(\n arids: SessionArids,\n groupId: ARID,\n groupRecord: GroupRecord,\n participants: GroupParticipant[],\n targetEnvelope: Envelope,\n): Record<string, unknown> {\n const participantsMap: Record<string, unknown> = {};\n\n for (const participant of participants) {\n const xid = participant.xid();\n const xidKey = xid.urString();\n\n const commitArid = arids.commitArids.get(xidKey);\n const shareArid = arids.shareArids.get(xidKey);\n\n if (commitArid === undefined || shareArid === undefined) {\n throw new Error(\"ARID not found for participant\");\n }\n\n participantsMap[xidKey] = {\n commit_arid: commitArid.urString(),\n share_arid: shareArid.urString(),\n };\n }\n\n return {\n session_id: arids.sessionId.urString(),\n start_arid: arids.startArid.urString(),\n group: groupId.urString(),\n min_signers: groupRecord.minSigners(),\n participants: participantsMap,\n target: targetEnvelope.urString(),\n };\n}\n\n/**\n * Persist the session state to disk.\n *\n * Port of `persist_session_state()` from cmd/sign/coordinator/invite.rs lines 348-356.\n */\nexport function persistSessionState(signingDir: string, stateJson: Record<string, unknown>): void {\n fs.mkdirSync(signingDir, { recursive: true });\n const startStatePath = path.join(signingDir, \"start.json\");\n fs.writeFileSync(startStatePath, JSON.stringify(stateJson, null, 2));\n}\n\n// -----------------------------------------------------------------------------\n// File loading\n// -----------------------------------------------------------------------------\n\n/**\n * Load an envelope from a file path.\n *\n * Port of `load_envelope_from_path()` from cmd/sign/coordinator/invite.rs lines 385-392.\n */\nexport function loadEnvelopeFromPath(filePath: string): Envelope {\n if (!fs.existsSync(filePath)) {\n throw new Error(`Failed to read target envelope from ${filePath}`);\n }\n\n const data = fs.readFileSync(filePath, \"utf-8\");\n const trimmed = data.trim();\n\n try {\n return Envelope.fromURString(trimmed);\n } catch (e) {\n throw new Error(`Failed to load target envelope from ${filePath}: ${String(e)}`, { cause: e });\n }\n}\n\n// -----------------------------------------------------------------------------\n// Options and Result types\n// -----------------------------------------------------------------------------\n\n/**\n * Options for the sign invite command.\n */\nexport interface SignInviteOptions {\n registryPath?: string;\n groupId: string;\n targetFile: string;\n validDays?: number;\n verbose?: boolean;\n preview?: boolean;\n}\n\n/**\n * Result of the sign invite command.\n */\nexport interface SignInviteResult {\n sessionId: string;\n startArid: string;\n}\n\n// -----------------------------------------------------------------------------\n// Main invite function\n// -----------------------------------------------------------------------------\n\n/**\n * Execute the sign coordinator invite command.\n *\n * Invites participants to sign a target envelope.\n *\n * Port of `CommandArgs::exec()` from cmd/sign/coordinator/invite.rs lines 44-144.\n */\nexport async function invite(\n client: StorageClient | undefined,\n options: SignInviteOptions,\n cwd: string,\n): Promise<SignInviteResult> {\n // Validate preview mode\n if (client !== 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 === undefined) {\n throw new Error(\"Registry owner is required\");\n }\n\n const groupId = parseAridUr(options.groupId);\n const groupRecord = registry.group(groupId);\n\n if (groupRecord === undefined) {\n throw new Error(`Group ${options.groupId} not found in registry`);\n }\n\n // Validate sender is coordinator\n validateCoordinator(groupRecord, owner);\n\n // Load target envelope\n const targetPath = path.resolve(cwd, options.targetFile);\n const targetEnvelope = loadEnvelopeFromPath(targetPath);\n\n // Get participants\n const participants = groupRecord.participants();\n\n // Gather recipient documents\n const recipientDocs = gatherRecipientDocuments(participants, owner, registry);\n\n // Get signer keys\n const signerKeys = owner.xidDocument().inceptionPrivateKeys();\n if (signerKeys === undefined) {\n throw new Error(\"Coordinator XID document has no signing keys\");\n }\n\n // Generate session ARIDs\n const sessionArids = createSessionArids(participants);\n\n // Calculate valid until date (default 1 hour)\n const validDays = options.validDays ?? 1 / 24; // 1 hour default\n const validUntil = new Date(Date.now() + validDays * 24 * 60 * 60 * 1000);\n\n // Build request context\n const ctx: SignInviteContext = {\n arids: sessionArids,\n groupId,\n targetEnvelope,\n groupRecord,\n owner,\n registry,\n participants,\n validUntil,\n };\n\n // Build request\n const request = buildSignInviteRequest(ctx);\n\n // Build state for persistence\n const stateJson = buildSessionStateJson(\n sessionArids,\n groupId,\n groupRecord,\n participants,\n targetEnvelope,\n );\n\n // Build envelope for recipients\n const recipientRefs = recipientDocs;\n const sealedEnvelope = request.toEnvelopeForRecipients(validUntil, signerKeys, recipientRefs);\n\n // Handle preview mode\n if (options.preview === true) {\n const unsealed = request.toEnvelope(undefined, signerKeys, undefined);\n console.log(unsealed.urString());\n return {\n sessionId: sessionArids.sessionId.urString(),\n startArid: sessionArids.startArid.urString(),\n };\n }\n\n // Persist state\n const signingDir = signingStateDir(registryPath, groupId.hex(), sessionArids.sessionId.hex());\n persistSessionState(signingDir, stateJson);\n\n // Post to Hubert storage\n if (client === undefined) {\n throw new Error(\"Hubert storage is required for sign start\");\n }\n\n await putWithIndicator(\n client,\n sessionArids.startArid,\n sealedEnvelope,\n \"Signing invite\",\n options.verbose ?? false,\n );\n\n if (options.verbose === true) {\n console.log(`Session ID: ${sessionArids.sessionId.urString()}`);\n console.log(`Start ARID: ${sessionArids.startArid.urString()}`);\n }\n\n // Output the start ARID\n console.log(sessionArids.startArid.urString());\n\n return {\n sessionId: sessionArids.sessionId.urString(),\n startArid: sessionArids.startArid.urString(),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"invite-CPLnWY7c.mjs","names":[],"sources":["../src/cmd/sign/coordinator/invite.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * Sign coordinator invite command.\n *\n * Port of cmd/sign/coordinator/invite.rs from frost-hubert-rust.\n *\n * @module\n */\n\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-unsafe-call */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-unsafe-argument */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { ARID } from \"@bcts/components\";\nimport { CborDate } from \"@bcts/dcbor\";\nimport { Envelope } from \"@bcts/envelope\";\nimport { SealedRequest } from \"@bcts/gstp\";\nimport { type XIDDocument } from \"@bcts/xid\";\n\nimport {\n Registry,\n resolveRegistryPath,\n type GroupRecord,\n type GroupParticipant,\n type OwnerRecord,\n} from \"../../../registry/index.js\";\nimport { putWithIndicator } from \"../../busy.js\";\nimport { type StorageClient } from \"../../storage.js\";\nimport { parseAridUr } from \"../../dkg/common.js\";\nimport { signingStateDir } from \"../common.js\";\n\n// -----------------------------------------------------------------------------\n// Session ARID management\n// -----------------------------------------------------------------------------\n\n/**\n * Session ARIDs for tracking the signing session.\n *\n * Port of `struct SessionArids` from cmd/sign/coordinator/invite.rs lines 151-156.\n */\nexport interface SessionArids {\n sessionId: ARID;\n startArid: ARID;\n commitArids: Map<string, ARID>; // Map<XID.urString(), ARID>\n shareArids: Map<string, ARID>; // Map<XID.urString(), ARID>\n}\n\n/**\n * Create new session ARIDs for all participants.\n *\n * Port of `SessionArids::new()` from cmd/sign/coordinator/invite.rs lines 158-173.\n */\nexport function createSessionArids(participants: GroupParticipant[]): SessionArids {\n const commitArids = new Map<string, ARID>();\n const shareArids = new Map<string, ARID>();\n\n for (const participant of participants) {\n const xidKey = participant.xid().urString();\n commitArids.set(xidKey, ARID.new());\n shareArids.set(xidKey, ARID.new());\n }\n\n return {\n sessionId: ARID.new(),\n startArid: ARID.new(),\n commitArids,\n shareArids,\n };\n}\n\n// -----------------------------------------------------------------------------\n// Validation\n// -----------------------------------------------------------------------------\n\n/**\n * Validate that the owner is the coordinator of the group.\n *\n * Port of `validate_coordinator()` from cmd/sign/coordinator/invite.rs lines 179-192.\n */\nexport function validateCoordinator(groupRecord: GroupRecord, owner: OwnerRecord): void {\n if (groupRecord.coordinator().xid().urString() !== owner.xid().urString()) {\n throw new Error(\n `Only the coordinator can start signing. Coordinator: ${groupRecord.coordinator().xid().urString()}, Owner: ${owner.xid().urString()}`,\n );\n }\n}\n\n// -----------------------------------------------------------------------------\n// Participant document gathering\n// -----------------------------------------------------------------------------\n\n/**\n * Gather XIDDocuments for all participants from the registry.\n *\n * Port of `gather_recipient_documents()` from cmd/sign/coordinator/invite.rs lines 198-222.\n */\nexport function gatherRecipientDocuments(\n participants: GroupParticipant[],\n owner: OwnerRecord,\n registry: Registry,\n): XIDDocument[] {\n const recipientDocs: XIDDocument[] = [];\n\n for (const participant of participants) {\n const xid = participant.xid();\n if (xid.urString() === owner.xid().urString()) {\n recipientDocs.push(owner.xidDocument());\n } else {\n const record = registry.participant(xid);\n if (record === undefined) {\n throw new Error(`Participant ${xid.urString()} not found in registry`);\n }\n recipientDocs.push(record.xidDocument());\n }\n }\n\n return recipientDocs;\n}\n\n// -----------------------------------------------------------------------------\n// Request building\n// -----------------------------------------------------------------------------\n\n/**\n * Context for building the sign invite request.\n *\n * Port of `struct SignInviteContext` from cmd/sign/coordinator/invite.rs lines 228-237.\n */\nexport interface SignInviteContext {\n arids: SessionArids;\n groupId: ARID;\n targetEnvelope: Envelope;\n groupRecord: GroupRecord;\n owner: OwnerRecord;\n registry: Registry;\n participants: GroupParticipant[];\n validUntil: Date;\n}\n\n/**\n * Build the sign invite request.\n *\n * Port of `build_sign_invite_request()` from cmd/sign/coordinator/invite.rs lines 239-284.\n */\nexport function buildSignInviteRequest(ctx: SignInviteContext): SealedRequest {\n let request = SealedRequest.new(\"signInvite\", ctx.arids.sessionId, ctx.owner.xidDocument())\n .withParameter(\"group\", ctx.groupId)\n .withParameter(\"session\", ctx.arids.sessionId)\n .withParameter(\"target\", ctx.targetEnvelope)\n .withParameter(\"minSigners\", ctx.groupRecord.minSigners())\n .withDate(new Date())\n .withParameter(\"validUntil\", CborDate.fromDatetime(ctx.validUntil));\n\n for (const participant of ctx.participants) {\n const xid = participant.xid();\n const xidKey = xid.urString();\n\n // Get participant document\n let participantDoc: XIDDocument;\n if (xidKey === ctx.owner.xid().urString()) {\n participantDoc = ctx.owner.xidDocument();\n } else {\n const record = ctx.registry.participant(xid);\n if (record === undefined) {\n throw new Error(\"Participant not found in registry\");\n }\n participantDoc = record.xidDocument();\n }\n\n // Get encryption key\n const encryptionKey = participantDoc.encryptionKey();\n if (encryptionKey === undefined) {\n throw new Error(\"Participant XID document has no encryption key\");\n }\n\n // Get commit ARID for this participant\n const responseArid = ctx.arids.commitArids.get(xidKey);\n if (responseArid === undefined) {\n throw new Error(\"commit ARID not found for participant\");\n }\n\n // Encrypt response ARID to participant\n // @ts-expect-error TS2339 - API mismatch: toEnvelope/encryptToRecipient methods\n const encryptedResponseArid = responseArid.toEnvelope().encryptToRecipient(encryptionKey);\n\n // Build participant entry envelope\n const participantEntry = Envelope.new(xid).addAssertion(\"response_arid\", encryptedResponseArid);\n\n request = request.withParameter(\"participant\", participantEntry);\n }\n\n return request;\n}\n\n// -----------------------------------------------------------------------------\n// State persistence\n// -----------------------------------------------------------------------------\n\n/**\n * Build the session state JSON for persistence.\n *\n * Port of `build_session_state_json()` from cmd/sign/coordinator/invite.rs lines 290-346.\n */\nexport function buildSessionStateJson(\n arids: SessionArids,\n groupId: ARID,\n groupRecord: GroupRecord,\n participants: GroupParticipant[],\n targetEnvelope: Envelope,\n): Record<string, unknown> {\n const participantsMap: Record<string, unknown> = {};\n\n for (const participant of participants) {\n const xid = participant.xid();\n const xidKey = xid.urString();\n\n const commitArid = arids.commitArids.get(xidKey);\n const shareArid = arids.shareArids.get(xidKey);\n\n if (commitArid === undefined || shareArid === undefined) {\n throw new Error(\"ARID not found for participant\");\n }\n\n participantsMap[xidKey] = {\n commit_arid: commitArid.urString(),\n share_arid: shareArid.urString(),\n };\n }\n\n return {\n session_id: arids.sessionId.urString(),\n start_arid: arids.startArid.urString(),\n group: groupId.urString(),\n min_signers: groupRecord.minSigners(),\n participants: participantsMap,\n target: targetEnvelope.urString(),\n };\n}\n\n/**\n * Persist the session state to disk.\n *\n * Port of `persist_session_state()` from cmd/sign/coordinator/invite.rs lines 348-356.\n */\nexport function persistSessionState(signingDir: string, stateJson: Record<string, unknown>): void {\n fs.mkdirSync(signingDir, { recursive: true });\n const startStatePath = path.join(signingDir, \"start.json\");\n fs.writeFileSync(startStatePath, JSON.stringify(stateJson, null, 2));\n}\n\n// -----------------------------------------------------------------------------\n// File loading\n// -----------------------------------------------------------------------------\n\n/**\n * Load an envelope from a file path.\n *\n * Port of `load_envelope_from_path()` from cmd/sign/coordinator/invite.rs lines 385-392.\n */\nexport function loadEnvelopeFromPath(filePath: string): Envelope {\n if (!fs.existsSync(filePath)) {\n throw new Error(`Failed to read target envelope from ${filePath}`);\n }\n\n const data = fs.readFileSync(filePath, \"utf-8\");\n const trimmed = data.trim();\n\n try {\n return Envelope.fromURString(trimmed);\n } catch (e) {\n throw new Error(`Failed to load target envelope from ${filePath}: ${String(e)}`, { cause: e });\n }\n}\n\n// -----------------------------------------------------------------------------\n// Options and Result types\n// -----------------------------------------------------------------------------\n\n/**\n * Options for the sign invite command.\n */\nexport interface SignInviteOptions {\n registryPath?: string;\n groupId: string;\n targetFile: string;\n validDays?: number;\n verbose?: boolean;\n preview?: boolean;\n}\n\n/**\n * Result of the sign invite command.\n */\nexport interface SignInviteResult {\n sessionId: string;\n startArid: string;\n}\n\n// -----------------------------------------------------------------------------\n// Main invite function\n// -----------------------------------------------------------------------------\n\n/**\n * Execute the sign coordinator invite command.\n *\n * Invites participants to sign a target envelope.\n *\n * Port of `CommandArgs::exec()` from cmd/sign/coordinator/invite.rs lines 44-144.\n */\nexport async function invite(\n client: StorageClient | undefined,\n options: SignInviteOptions,\n cwd: string,\n): Promise<SignInviteResult> {\n // Validate preview mode\n if (client !== 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 === undefined) {\n throw new Error(\"Registry owner is required\");\n }\n\n const groupId = parseAridUr(options.groupId);\n const groupRecord = registry.group(groupId);\n\n if (groupRecord === undefined) {\n throw new Error(`Group ${options.groupId} not found in registry`);\n }\n\n // Validate sender is coordinator\n validateCoordinator(groupRecord, owner);\n\n // Load target envelope\n const targetPath = path.resolve(cwd, options.targetFile);\n const targetEnvelope = loadEnvelopeFromPath(targetPath);\n\n // Get participants\n const participants = groupRecord.participants();\n\n // Gather recipient documents\n const recipientDocs = gatherRecipientDocuments(participants, owner, registry);\n\n // Get signer keys\n const signerKeys = owner.xidDocument().inceptionPrivateKeys();\n if (signerKeys === undefined) {\n throw new Error(\"Coordinator XID document has no signing keys\");\n }\n\n // Generate session ARIDs\n const sessionArids = createSessionArids(participants);\n\n // Calculate valid until date (default 1 hour)\n const validDays = options.validDays ?? 1 / 24; // 1 hour default\n const validUntil = new Date(Date.now() + validDays * 24 * 60 * 60 * 1000);\n\n // Build request context\n const ctx: SignInviteContext = {\n arids: sessionArids,\n groupId,\n targetEnvelope,\n groupRecord,\n owner,\n registry,\n participants,\n validUntil,\n };\n\n // Build request\n const request = buildSignInviteRequest(ctx);\n\n // Build state for persistence\n const stateJson = buildSessionStateJson(\n sessionArids,\n groupId,\n groupRecord,\n participants,\n targetEnvelope,\n );\n\n // Build envelope for recipients\n const recipientRefs = recipientDocs;\n const sealedEnvelope = request.toEnvelopeForRecipients(validUntil, signerKeys, recipientRefs);\n\n // Handle preview mode\n if (options.preview === true) {\n const unsealed = request.toEnvelope(undefined, signerKeys, undefined);\n console.log(unsealed.urString());\n return {\n sessionId: sessionArids.sessionId.urString(),\n startArid: sessionArids.startArid.urString(),\n };\n }\n\n // Persist state\n const signingDir = signingStateDir(registryPath, groupId.hex(), sessionArids.sessionId.hex());\n persistSessionState(signingDir, stateJson);\n\n // Post to Hubert storage\n if (client === undefined) {\n throw new Error(\"Hubert storage is required for sign start\");\n }\n\n await putWithIndicator(\n client,\n sessionArids.startArid,\n sealedEnvelope,\n \"Signing invite\",\n options.verbose ?? false,\n );\n\n if (options.verbose === true) {\n console.log(`Session ID: ${sessionArids.sessionId.urString()}`);\n console.log(`Start ARID: ${sessionArids.startArid.urString()}`);\n }\n\n // Output the start ARID\n console.log(sessionArids.startArid.urString());\n\n return {\n sessionId: sessionArids.sessionId.urString(),\n startArid: sessionArids.startArid.urString(),\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { t as __exportAll } from "./rolldown-runtime-
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
2
2
|
import { DkgInvitation } from "./dkg/index.mjs";
|
|
3
3
|
import { Registry, resolveRegistryPath } from "./registry/index.mjs";
|
|
4
|
-
import { a as dkgStateDir, c as parseAridUr, l as parseEnvelopeUr, m as resolveSenderName, u as participantNamesFromRegistry } from "./common-
|
|
4
|
+
import { a as dkgStateDir, c as parseAridUr, l as parseEnvelopeUr, m as resolveSenderName, u as participantNamesFromRegistry } from "./common-k2QC6AST.mjs";
|
|
5
5
|
import { t as getWithIndicator } from "./busy-BlU8_pS2.mjs";
|
|
6
|
-
import { i as createStorageClient } from "./registry-
|
|
6
|
+
import { i as createStorageClient } from "./registry-DkhElxx1.mjs";
|
|
7
7
|
import { ARID, XID } from "@bcts/components";
|
|
8
8
|
import { CborDate } from "@bcts/dcbor";
|
|
9
9
|
import { Function } from "@bcts/envelope";
|
|
@@ -185,4 +185,4 @@ async function receive(_client, options, cwd) {
|
|
|
185
185
|
//#endregion
|
|
186
186
|
export { resolveInviteEnvelope as i, receive as n, receive_exports as r, decodeInviteDetails as t };
|
|
187
187
|
|
|
188
|
-
//# sourceMappingURL=receive-
|
|
188
|
+
//# sourceMappingURL=receive-Bl_utFpz.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"receive-C2Dwb_FQ.mjs","names":["EnvelopeFunction"],"sources":["../src/cmd/dkg/participant/receive.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * DKG participant receive command.\n *\n * Port of cmd/dkg/participant/receive.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, XID } from \"@bcts/components\";\nimport { CborDate } from \"@bcts/dcbor\";\nimport { type Envelope, Function as EnvelopeFunction } from \"@bcts/envelope\";\nimport { SealedRequest } from \"@bcts/gstp\";\nimport { XIDDocument, XIDVerifySignature } from \"@bcts/xid\";\n\nimport { DkgInvitation } from \"../../../dkg/index.js\";\nimport { Registry, resolveRegistryPath } from \"../../../registry/index.js\";\nimport { getWithIndicator } from \"../../busy.js\";\nimport { createStorageClient, type StorageClient, type StorageSelection } from \"../../storage.js\";\nimport {\n dkgStateDir,\n parseAridUr,\n parseEnvelopeUr,\n participantNamesFromRegistry,\n resolveSenderName,\n} from \"../common.js\";\n\n/**\n * Options for the DKG receive command.\n */\nexport interface DkgReceiveOptions {\n registryPath?: string;\n timeoutSeconds?: number;\n noEnvelope?: boolean;\n info?: boolean;\n sender?: string;\n invite: string;\n storageSelection?: StorageSelection;\n verbose?: boolean;\n}\n\n/**\n * Result of the DKG receive command.\n */\nexport interface DkgReceiveResult {\n groupId: string;\n requestId: string;\n minSigners: number;\n charter: string;\n validUntil: string;\n responseArid: string;\n envelopeUr?: string | undefined;\n coordinatorName?: string | undefined;\n participantNames?: string[] | undefined;\n}\n\n/**\n * Details extracted from a DKG invite.\n *\n * Port of `struct InviteDetails` from cmd/dkg/participant/receive.rs lines 117-120.\n */\nexport interface InviteDetails {\n invitation: DkgInvitation;\n participants: XIDDocument[];\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/receive.rs lines 122-152.\n */\nexport async 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 * Decode and validate invite details from an envelope.\n *\n * Port of `decode_invite_details()` from cmd/dkg/participant/receive.rs lines 154-256.\n */\nexport function decodeInviteDetails(\n invite: Envelope,\n now: Date,\n registry: Registry,\n recipient: XIDDocument,\n expectedSender?: XIDDocument,\n): InviteDetails {\n const recipientPrivateKeys = recipient.inceptionPrivateKeys();\n\n if (recipientPrivateKeys === undefined) {\n throw new Error(\"Recipient XID document has no inception private keys\");\n }\n\n const sealedRequest = SealedRequest.tryFromEnvelope(invite, undefined, now, recipientPrivateKeys);\n\n const senderDocument = sealedRequest.sender();\n if (expectedSender !== undefined) {\n if (senderDocument.xid().urString() !== expectedSender.xid().urString()) {\n throw new Error(\"Invite sender does not match expected sender\");\n }\n } else {\n const senderXid = senderDocument.xid();\n const owner = registry.owner();\n const knownOwner = owner?.xidDocument().xid().urString() === senderXid.urString();\n const knownParticipant = registry.participant(senderXid) !== undefined;\n\n if (!knownOwner && !knownParticipant) {\n throw new Error(`Invite sender not found in registry: ${senderXid.urString()}`);\n }\n }\n\n if (!sealedRequest.request().function().equals(EnvelopeFunction.fromString(\"dkgInvite\"))) {\n throw new Error(\"Unexpected invite function\");\n }\n\n const validUntil = sealedRequest.extractObjectForParameter<Date>(\"validUntil\");\n if (validUntil <= now) {\n throw new Error(\"Invitation expired\");\n }\n\n const minSigners = sealedRequest.extractObjectForParameter<number>(\"minSigners\");\n sealedRequest.extractObjectForParameter<string>(\"charter\");\n sealedRequest.extractObjectForParameter<ARID>(\"group\");\n const participantObjects = sealedRequest.objectsForParameter(\"participant\");\n\n if (minSigners < 2) {\n throw new Error(\"min_signers must be at least 2\");\n }\n\n if (minSigners > participantObjects.length) {\n throw new Error(\"min_signers exceeds participant count\");\n }\n\n const participantDocs: XIDDocument[] = [];\n let responseArid: ARID | undefined;\n const recipientXid = recipient.xid();\n\n for (const participant of participantObjects) {\n const xidDocumentEnvelope = participant.tryUnwrap();\n const xidDocument = XIDDocument.fromEnvelope(\n xidDocumentEnvelope,\n undefined,\n XIDVerifySignature.Inception,\n );\n\n if (xidDocument.xid().urString() === recipientXid.urString()) {\n const encryptedResponseArid = participant.objectForPredicate(\"response_arid\");\n const responseAridEnvelope = encryptedResponseArid.decryptToRecipient(recipientPrivateKeys);\n responseArid = responseAridEnvelope.extractSubject((cbor) => ARID.fromTaggedCbor(cbor));\n }\n\n participantDocs.push(xidDocument);\n }\n\n const invitation = DkgInvitation.fromInvite(invite, now, expectedSender, recipient);\n\n if (responseArid === undefined) {\n throw new Error(\"Invite does not include a response ARID for this recipient\");\n }\n\n return { invitation, participants: participantDocs };\n}\n\n/**\n * Resolve a sender XID document from the registry by UR or pet name.\n *\n * Port of `resolve_sender()` usage in receive.rs for expected sender.\n */\nfunction resolveSenderXidDocument(registry: Registry, raw: string): XIDDocument {\n // Try parsing as XID UR first\n try {\n const xid = XID.fromURString(raw.trim());\n const record = registry.participant(xid);\n if (record) {\n return record.xidDocument();\n }\n const owner = registry.owner();\n if (owner?.xid().urString() === xid.urString()) {\n return owner.xidDocument();\n }\n throw new Error(`Sender with XID ${xid.urString()} not found in registry`);\n } catch {\n // Try looking up by pet name\n const result = registry.participantByPetName(raw.trim());\n if (result) {\n const [, record] = result;\n return record.xidDocument();\n }\n const owner = registry.owner();\n if (owner?.petName() === raw.trim()) {\n return owner.xidDocument();\n }\n throw new Error(`Sender '${raw}' not found in registry`);\n }\n}\n\n/**\n * Execute the DKG participant receive command.\n *\n * Fetches and validates a DKG invite from the coordinator.\n *\n * Port of `receive()` from cmd/dkg/participant/receive.rs.\n */\nexport async function receive(\n _client: StorageClient | undefined,\n options: DkgReceiveOptions,\n cwd: string,\n): Promise<DkgReceiveResult> {\n if (options.storageSelection === undefined && options.timeoutSeconds !== undefined) {\n throw new Error(\"--timeout requires Hubert storage parameters\");\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 const expectedSender = options.sender\n ? resolveSenderXidDocument(registry, options.sender)\n : undefined;\n\n const inviteEnvelope = await resolveInviteEnvelope(\n options.storageSelection,\n options.invite,\n options.timeoutSeconds,\n );\n\n const now = CborDate.now().datetime();\n const details = decodeInviteDetails(\n inviteEnvelope,\n now,\n registry,\n owner.xidDocument(),\n expectedSender,\n );\n\n const participantNames = participantNamesFromRegistry(\n registry,\n details.participants,\n owner.xid(),\n owner.petName(),\n );\n\n const coordinatorName = resolveSenderName(registry, details.invitation.sender());\n\n // Save receive state\n const stateDir = dkgStateDir(registryPath, details.invitation.groupId().hex());\n fs.mkdirSync(stateDir, { recursive: true });\n\n const validUntilStr: string = (\n details.invitation.validUntil() as { toString(): string }\n ).toString();\n const receiveState = {\n group: details.invitation.groupId().urString(),\n request_id: details.invitation.requestId().urString(),\n response_arid: details.invitation.responseArid().urString(),\n valid_until: validUntilStr,\n min_signers: details.invitation.minSigners(),\n charter: details.invitation.charter(),\n sender: details.invitation.sender().xid().urString(),\n };\n\n fs.writeFileSync(path.join(stateDir, \"receive.json\"), JSON.stringify(receiveState, null, 2));\n\n // Output based on options\n let envelopeUr: string | undefined;\n if (options.noEnvelope !== true) {\n envelopeUr = inviteEnvelope.urString();\n console.log(envelopeUr);\n }\n\n if (options.info === true) {\n console.error(`Charter: ${details.invitation.charter()}`);\n console.error(`Min signers: ${details.invitation.minSigners()}`);\n if (coordinatorName !== undefined) {\n console.error(`Coordinator: ${coordinatorName}`);\n }\n console.error(`Participants: ${participantNames.join(\", \")}`);\n }\n\n if (options.verbose === true) {\n console.log(`Group ID: ${details.invitation.groupId().urString()}`);\n console.log(`Min signers: ${details.invitation.minSigners()}`);\n console.log(`Charter: ${details.invitation.charter()}`);\n console.log(`Valid until: ${String(details.invitation.validUntil())}`);\n console.log(`Response ARID: ${details.invitation.responseArid().urString()}`);\n }\n\n const resultValidUntilStr: string = (\n details.invitation.validUntil() as { toString(): string }\n ).toString();\n\n return {\n groupId: details.invitation.groupId().urString(),\n requestId: details.invitation.requestId().urString(),\n minSigners: details.invitation.minSigners(),\n charter: details.invitation.charter(),\n validUntil: resultValidUntilStr,\n responseArid: details.invitation.responseArid().urString(),\n envelopeUr,\n coordinatorName,\n participantNames,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"receive-Bl_utFpz.mjs","names":["EnvelopeFunction"],"sources":["../src/cmd/dkg/participant/receive.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * DKG participant receive command.\n *\n * Port of cmd/dkg/participant/receive.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, XID } from \"@bcts/components\";\nimport { CborDate } from \"@bcts/dcbor\";\nimport { type Envelope, Function as EnvelopeFunction } from \"@bcts/envelope\";\nimport { SealedRequest } from \"@bcts/gstp\";\nimport { XIDDocument, XIDVerifySignature } from \"@bcts/xid\";\n\nimport { DkgInvitation } from \"../../../dkg/index.js\";\nimport { Registry, resolveRegistryPath } from \"../../../registry/index.js\";\nimport { getWithIndicator } from \"../../busy.js\";\nimport { createStorageClient, type StorageClient, type StorageSelection } from \"../../storage.js\";\nimport {\n dkgStateDir,\n parseAridUr,\n parseEnvelopeUr,\n participantNamesFromRegistry,\n resolveSenderName,\n} from \"../common.js\";\n\n/**\n * Options for the DKG receive command.\n */\nexport interface DkgReceiveOptions {\n registryPath?: string;\n timeoutSeconds?: number;\n noEnvelope?: boolean;\n info?: boolean;\n sender?: string;\n invite: string;\n storageSelection?: StorageSelection;\n verbose?: boolean;\n}\n\n/**\n * Result of the DKG receive command.\n */\nexport interface DkgReceiveResult {\n groupId: string;\n requestId: string;\n minSigners: number;\n charter: string;\n validUntil: string;\n responseArid: string;\n envelopeUr?: string | undefined;\n coordinatorName?: string | undefined;\n participantNames?: string[] | undefined;\n}\n\n/**\n * Details extracted from a DKG invite.\n *\n * Port of `struct InviteDetails` from cmd/dkg/participant/receive.rs lines 117-120.\n */\nexport interface InviteDetails {\n invitation: DkgInvitation;\n participants: XIDDocument[];\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/receive.rs lines 122-152.\n */\nexport async 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 * Decode and validate invite details from an envelope.\n *\n * Port of `decode_invite_details()` from cmd/dkg/participant/receive.rs lines 154-256.\n */\nexport function decodeInviteDetails(\n invite: Envelope,\n now: Date,\n registry: Registry,\n recipient: XIDDocument,\n expectedSender?: XIDDocument,\n): InviteDetails {\n const recipientPrivateKeys = recipient.inceptionPrivateKeys();\n\n if (recipientPrivateKeys === undefined) {\n throw new Error(\"Recipient XID document has no inception private keys\");\n }\n\n const sealedRequest = SealedRequest.tryFromEnvelope(invite, undefined, now, recipientPrivateKeys);\n\n const senderDocument = sealedRequest.sender();\n if (expectedSender !== undefined) {\n if (senderDocument.xid().urString() !== expectedSender.xid().urString()) {\n throw new Error(\"Invite sender does not match expected sender\");\n }\n } else {\n const senderXid = senderDocument.xid();\n const owner = registry.owner();\n const knownOwner = owner?.xidDocument().xid().urString() === senderXid.urString();\n const knownParticipant = registry.participant(senderXid) !== undefined;\n\n if (!knownOwner && !knownParticipant) {\n throw new Error(`Invite sender not found in registry: ${senderXid.urString()}`);\n }\n }\n\n if (!sealedRequest.request().function().equals(EnvelopeFunction.fromString(\"dkgInvite\"))) {\n throw new Error(\"Unexpected invite function\");\n }\n\n const validUntil = sealedRequest.extractObjectForParameter<Date>(\"validUntil\");\n if (validUntil <= now) {\n throw new Error(\"Invitation expired\");\n }\n\n const minSigners = sealedRequest.extractObjectForParameter<number>(\"minSigners\");\n sealedRequest.extractObjectForParameter<string>(\"charter\");\n sealedRequest.extractObjectForParameter<ARID>(\"group\");\n const participantObjects = sealedRequest.objectsForParameter(\"participant\");\n\n if (minSigners < 2) {\n throw new Error(\"min_signers must be at least 2\");\n }\n\n if (minSigners > participantObjects.length) {\n throw new Error(\"min_signers exceeds participant count\");\n }\n\n const participantDocs: XIDDocument[] = [];\n let responseArid: ARID | undefined;\n const recipientXid = recipient.xid();\n\n for (const participant of participantObjects) {\n const xidDocumentEnvelope = participant.tryUnwrap();\n const xidDocument = XIDDocument.fromEnvelope(\n xidDocumentEnvelope,\n undefined,\n XIDVerifySignature.Inception,\n );\n\n if (xidDocument.xid().urString() === recipientXid.urString()) {\n const encryptedResponseArid = participant.objectForPredicate(\"response_arid\");\n const responseAridEnvelope = encryptedResponseArid.decryptToRecipient(recipientPrivateKeys);\n responseArid = responseAridEnvelope.extractSubject((cbor) => ARID.fromTaggedCbor(cbor));\n }\n\n participantDocs.push(xidDocument);\n }\n\n const invitation = DkgInvitation.fromInvite(invite, now, expectedSender, recipient);\n\n if (responseArid === undefined) {\n throw new Error(\"Invite does not include a response ARID for this recipient\");\n }\n\n return { invitation, participants: participantDocs };\n}\n\n/**\n * Resolve a sender XID document from the registry by UR or pet name.\n *\n * Port of `resolve_sender()` usage in receive.rs for expected sender.\n */\nfunction resolveSenderXidDocument(registry: Registry, raw: string): XIDDocument {\n // Try parsing as XID UR first\n try {\n const xid = XID.fromURString(raw.trim());\n const record = registry.participant(xid);\n if (record) {\n return record.xidDocument();\n }\n const owner = registry.owner();\n if (owner?.xid().urString() === xid.urString()) {\n return owner.xidDocument();\n }\n throw new Error(`Sender with XID ${xid.urString()} not found in registry`);\n } catch {\n // Try looking up by pet name\n const result = registry.participantByPetName(raw.trim());\n if (result) {\n const [, record] = result;\n return record.xidDocument();\n }\n const owner = registry.owner();\n if (owner?.petName() === raw.trim()) {\n return owner.xidDocument();\n }\n throw new Error(`Sender '${raw}' not found in registry`);\n }\n}\n\n/**\n * Execute the DKG participant receive command.\n *\n * Fetches and validates a DKG invite from the coordinator.\n *\n * Port of `receive()` from cmd/dkg/participant/receive.rs.\n */\nexport async function receive(\n _client: StorageClient | undefined,\n options: DkgReceiveOptions,\n cwd: string,\n): Promise<DkgReceiveResult> {\n if (options.storageSelection === undefined && options.timeoutSeconds !== undefined) {\n throw new Error(\"--timeout requires Hubert storage parameters\");\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 const expectedSender = options.sender\n ? resolveSenderXidDocument(registry, options.sender)\n : undefined;\n\n const inviteEnvelope = await resolveInviteEnvelope(\n options.storageSelection,\n options.invite,\n options.timeoutSeconds,\n );\n\n const now = CborDate.now().datetime();\n const details = decodeInviteDetails(\n inviteEnvelope,\n now,\n registry,\n owner.xidDocument(),\n expectedSender,\n );\n\n const participantNames = participantNamesFromRegistry(\n registry,\n details.participants,\n owner.xid(),\n owner.petName(),\n );\n\n const coordinatorName = resolveSenderName(registry, details.invitation.sender());\n\n // Save receive state\n const stateDir = dkgStateDir(registryPath, details.invitation.groupId().hex());\n fs.mkdirSync(stateDir, { recursive: true });\n\n const validUntilStr: string = (\n details.invitation.validUntil() as { toString(): string }\n ).toString();\n const receiveState = {\n group: details.invitation.groupId().urString(),\n request_id: details.invitation.requestId().urString(),\n response_arid: details.invitation.responseArid().urString(),\n valid_until: validUntilStr,\n min_signers: details.invitation.minSigners(),\n charter: details.invitation.charter(),\n sender: details.invitation.sender().xid().urString(),\n };\n\n fs.writeFileSync(path.join(stateDir, \"receive.json\"), JSON.stringify(receiveState, null, 2));\n\n // Output based on options\n let envelopeUr: string | undefined;\n if (options.noEnvelope !== true) {\n envelopeUr = inviteEnvelope.urString();\n console.log(envelopeUr);\n }\n\n if (options.info === true) {\n console.error(`Charter: ${details.invitation.charter()}`);\n console.error(`Min signers: ${details.invitation.minSigners()}`);\n if (coordinatorName !== undefined) {\n console.error(`Coordinator: ${coordinatorName}`);\n }\n console.error(`Participants: ${participantNames.join(\", \")}`);\n }\n\n if (options.verbose === true) {\n console.log(`Group ID: ${details.invitation.groupId().urString()}`);\n console.log(`Min signers: ${details.invitation.minSigners()}`);\n console.log(`Charter: ${details.invitation.charter()}`);\n console.log(`Valid until: ${String(details.invitation.validUntil())}`);\n console.log(`Response ARID: ${details.invitation.responseArid().urString()}`);\n }\n\n const resultValidUntilStr: string = (\n details.invitation.validUntil() as { toString(): string }\n ).toString();\n\n return {\n groupId: details.invitation.groupId().urString(),\n requestId: details.invitation.requestId().urString(),\n minSigners: details.invitation.minSigners(),\n charter: details.invitation.charter(),\n validUntil: resultValidUntilStr,\n responseArid: details.invitation.responseArid().urString(),\n envelopeUr,\n coordinatorName,\n participantNames,\n };\n}\n"],"mappings":"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"}
|