@bcts/frost-hubert 1.0.0-alpha.23 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/frost.cjs +345 -71
- package/dist/bin/frost.cjs.map +1 -1
- package/dist/bin/frost.mjs +345 -71
- package/dist/bin/frost.mjs.map +1 -1
- package/dist/busy-DkM2jAIZ.mjs +27 -0
- package/dist/busy-DkM2jAIZ.mjs.map +1 -0
- package/dist/busy-EZU7EKr6.cjs +38 -0
- package/dist/busy-EZU7EKr6.cjs.map +1 -0
- package/dist/cmd/index.cjs +28 -22
- package/dist/cmd/index.d.cts +2 -2
- package/dist/cmd/index.d.mts +2 -2
- package/dist/cmd/index.mjs +7 -3
- package/dist/cmd-Bw9_i2_f.cjs +130 -0
- package/dist/cmd-Bw9_i2_f.cjs.map +1 -0
- package/dist/cmd-CS1uJtuD.mjs +113 -0
- package/dist/cmd-CS1uJtuD.mjs.map +1 -0
- package/dist/common-CvH6dFvQ.mjs +282 -0
- package/dist/common-CvH6dFvQ.mjs.map +1 -0
- package/dist/common-DUWvtc08.mjs +96 -0
- package/dist/common-DUWvtc08.mjs.map +1 -0
- package/dist/common-lKP5EzHy.cjs +372 -0
- package/dist/common-lKP5EzHy.cjs.map +1 -0
- package/dist/common-lThIvJmZ.cjs +114 -0
- package/dist/common-lThIvJmZ.cjs.map +1 -0
- package/dist/dkg/index.cjs +6 -102
- package/dist/dkg/index.cjs.map +1 -1
- package/dist/dkg/index.d.cts +2 -2
- package/dist/dkg/index.d.mts +2 -2
- package/dist/dkg/index.mjs +4 -101
- package/dist/dkg/index.mjs.map +1 -1
- package/dist/finalize-BRgJK-Xv.cjs +402 -0
- package/dist/finalize-BRgJK-Xv.cjs.map +1 -0
- package/dist/finalize-BfLgzn8f.cjs +303 -0
- package/dist/finalize-BfLgzn8f.cjs.map +1 -0
- package/dist/finalize-CNTDj6aS.mjs +389 -0
- package/dist/finalize-CNTDj6aS.mjs.map +1 -0
- package/dist/finalize-EC3ikHQq.mjs +252 -0
- package/dist/finalize-EC3ikHQq.mjs.map +1 -0
- package/dist/finalize-IA01t_Qq.mjs +290 -0
- package/dist/finalize-IA01t_Qq.mjs.map +1 -0
- package/dist/finalize-UPyI1yb1.cjs +265 -0
- package/dist/finalize-UPyI1yb1.cjs.map +1 -0
- package/dist/{index-BkqLimZT.d.mts → index-B3c-80VS.d.cts} +26 -3
- package/dist/index-B3c-80VS.d.cts.map +1 -0
- package/dist/{index-BJlwbPYu.d.cts → index-BgbSGpxn.d.mts} +102 -80
- package/dist/index-BgbSGpxn.d.mts.map +1 -0
- package/dist/{index-BMbPgH0W.d.cts → index-C8QeHNwa.d.cts} +46 -2
- package/dist/{index-BMbPgH0W.d.cts.map → index-C8QeHNwa.d.cts.map} +1 -1
- package/dist/{index-DmxfT59Y.d.cts → index-D3QTWkEm.d.mts} +26 -3
- package/dist/index-D3QTWkEm.d.mts.map +1 -0
- package/dist/{index-DoV5HFvV.d.mts → index-DVbWyOs7.d.mts} +46 -2
- package/dist/{index-DoV5HFvV.d.mts.map → index-DVbWyOs7.d.mts.map} +1 -1
- package/dist/{index-Dzm1v4_4.d.mts → index-F1iNEAJR.d.cts} +102 -80
- package/dist/index-F1iNEAJR.d.cts.map +1 -0
- package/dist/index.cjs +31 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +9 -4
- package/dist/index.mjs.map +1 -1
- package/dist/invite-5277FQVT.cjs +274 -0
- package/dist/invite-5277FQVT.cjs.map +1 -0
- package/dist/invite-DUTcfTgX.cjs +109 -0
- package/dist/invite-DUTcfTgX.cjs.map +1 -0
- package/dist/invite-IU4n0dq2.mjs +96 -0
- package/dist/invite-IU4n0dq2.mjs.map +1 -0
- package/dist/invite-RU-OXTNS.mjs +219 -0
- package/dist/invite-RU-OXTNS.mjs.map +1 -0
- package/dist/parallel-D1R6ZGlY.cjs +318 -0
- package/dist/parallel-D1R6ZGlY.cjs.map +1 -0
- package/dist/parallel-D6zc6VW4.mjs +235 -0
- package/dist/parallel-D6zc6VW4.mjs.map +1 -0
- package/dist/proposed-participant-Dm1Eq6mX.cjs +141 -0
- package/dist/proposed-participant-Dm1Eq6mX.cjs.map +1 -0
- package/dist/proposed-participant-cWM7iUrO.mjs +129 -0
- package/dist/proposed-participant-cWM7iUrO.mjs.map +1 -0
- package/dist/receive-CAI-x4II.cjs +213 -0
- package/dist/receive-CAI-x4II.cjs.map +1 -0
- package/dist/receive-D2Nn68L7.mjs +188 -0
- package/dist/receive-D2Nn68L7.mjs.map +1 -0
- package/dist/receive-DA_KQEgk.mjs +177 -0
- package/dist/receive-DA_KQEgk.mjs.map +1 -0
- package/dist/receive-kZMsXhbK.cjs +190 -0
- package/dist/receive-kZMsXhbK.cjs.map +1 -0
- package/dist/registry/index.cjs +85 -10
- package/dist/registry/index.cjs.map +1 -1
- package/dist/registry/index.d.cts +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +85 -10
- package/dist/registry/index.mjs.map +1 -1
- package/dist/{registry-loI1_Mh1.cjs → registry-9puTaRrD.cjs} +1 -1
- package/dist/{registry-loI1_Mh1.cjs.map → registry-9puTaRrD.cjs.map} +1 -1
- package/dist/{registry-CgrCZ4En.mjs → registry-BpCwtrRt.mjs} +1 -1
- package/dist/{registry-CgrCZ4En.mjs.map → registry-BpCwtrRt.mjs.map} +1 -1
- package/dist/round1-4Hyx8w0x.cjs +422 -0
- package/dist/round1-4Hyx8w0x.cjs.map +1 -0
- package/dist/round1-7v9LlE11.mjs +373 -0
- package/dist/round1-7v9LlE11.mjs.map +1 -0
- package/dist/round1-BHBjru1m.cjs +465 -0
- package/dist/round1-BHBjru1m.cjs.map +1 -0
- package/dist/round1-CMLKN2RR.mjs +195 -0
- package/dist/round1-CMLKN2RR.mjs.map +1 -0
- package/dist/round1-CWSXZx5R.cjs +208 -0
- package/dist/round1-CWSXZx5R.cjs.map +1 -0
- package/dist/round1-CcQCGlIT.mjs +208 -0
- package/dist/round1-CcQCGlIT.mjs.map +1 -0
- package/dist/round1-Cgm7j1kI.mjs +452 -0
- package/dist/round1-Cgm7j1kI.mjs.map +1 -0
- package/dist/round1-DQ0fnc1H.cjs +221 -0
- package/dist/round1-DQ0fnc1H.cjs.map +1 -0
- package/dist/round2-BWz9SQIi.cjs +305 -0
- package/dist/round2-BWz9SQIi.cjs.map +1 -0
- package/dist/round2-BkNRCXgS.mjs +292 -0
- package/dist/round2-BkNRCXgS.mjs.map +1 -0
- package/dist/round2-Bl2uK93U.mjs +450 -0
- package/dist/round2-Bl2uK93U.mjs.map +1 -0
- package/dist/round2-CdUT-AhH.cjs +499 -0
- package/dist/round2-CdUT-AhH.cjs.map +1 -0
- package/dist/round2-DOA3rnV-.mjs +280 -0
- package/dist/round2-DOA3rnV-.mjs.map +1 -0
- package/dist/round2-Dg24w-TU.mjs +397 -0
- package/dist/round2-Dg24w-TU.mjs.map +1 -0
- package/dist/round2-LylCa84n.cjs +293 -0
- package/dist/round2-LylCa84n.cjs.map +1 -0
- package/dist/round2-o2Q-GMbX.cjs +410 -0
- package/dist/round2-o2Q-GMbX.cjs.map +1 -0
- package/dist/storage-B-Gu68-O.cjs +79 -0
- package/dist/storage-B-Gu68-O.cjs.map +1 -0
- package/dist/storage-Bkkliz0K.mjs +74 -0
- package/dist/storage-Bkkliz0K.mjs.map +1 -0
- package/package.json +10 -10
- package/src/bin/frost.ts +849 -128
- package/src/cmd/common.ts +19 -1
- package/src/cmd/dkg/common.ts +97 -10
- package/src/cmd/dkg/coordinator/invite.ts +5 -2
- package/src/cmd/dkg/participant/finalize.ts +51 -17
- package/src/cmd/dkg/participant/round1.ts +39 -38
- package/src/cmd/dkg/participant/round2.ts +60 -26
- package/src/cmd/sign/coordinator/round2.ts +5 -1
- package/src/cmd/sign/participant/finalize.ts +6 -2
- package/src/cmd/sign/participant/receive.ts +5 -2
- package/src/dkg/group-invite.ts +12 -2
- package/src/dkg/proposed-participant.ts +32 -3
- package/src/registry/owner-record.ts +12 -0
- package/src/registry/participant-record.ts +35 -2
- package/src/registry/registry-impl.ts +74 -18
- package/dist/cmd-5yLeC_QL.mjs +0 -4708
- package/dist/cmd-5yLeC_QL.mjs.map +0 -1
- package/dist/cmd-BfZjC3Uh.cjs +0 -4847
- package/dist/cmd-BfZjC3Uh.cjs.map +0 -1
- package/dist/index-BJlwbPYu.d.cts.map +0 -1
- package/dist/index-BkqLimZT.d.mts.map +0 -1
- package/dist/index-DmxfT59Y.d.cts.map +0 -1
- package/dist/index-Dzm1v4_4.d.mts.map +0 -1
package/src/dkg/group-invite.ts
CHANGED
|
@@ -355,7 +355,14 @@ export class DkgInvitation {
|
|
|
355
355
|
recipientPrivateKeys,
|
|
356
356
|
);
|
|
357
357
|
|
|
358
|
-
|
|
358
|
+
// Rust `group_invite.rs:275-279` uses `!=` on `XID`, which is the
|
|
359
|
+
// derived `PartialEq` (byte-wise compare on the 32-byte ARID). TS
|
|
360
|
+
// `!==` is reference identity — two distinct XID instances with
|
|
361
|
+
// the same bytes would fail. Compare via hex equality instead.
|
|
362
|
+
if (
|
|
363
|
+
expectedSender !== undefined &&
|
|
364
|
+
!sealedRequest.sender().xid().equals(expectedSender.xid())
|
|
365
|
+
) {
|
|
359
366
|
throw new Error("Invite sender does not match expected sender");
|
|
360
367
|
}
|
|
361
368
|
|
|
@@ -391,7 +398,10 @@ export class DkgInvitation {
|
|
|
391
398
|
XIDVerifySignature.Inception,
|
|
392
399
|
);
|
|
393
400
|
|
|
394
|
-
|
|
401
|
+
// Rust `group_invite.rs` compares via `XID::PartialEq` (byte-wise).
|
|
402
|
+
// TS reference identity would never match for two distinct
|
|
403
|
+
// `XID` instances representing the same identity.
|
|
404
|
+
if (!xidDocument.xid().equals(recipientXid)) {
|
|
395
405
|
continue;
|
|
396
406
|
}
|
|
397
407
|
|
|
@@ -95,12 +95,41 @@ export class DkgProposedParticipant {
|
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* Compare participants by XID for sorting.
|
|
98
|
+
*
|
|
99
|
+
* Mirrors Rust `PartialOrd::partial_cmp` which uses
|
|
100
|
+
* `self.xid().cmp(&other.xid())` — i.e. the underlying 32-byte XID
|
|
101
|
+
* data is compared lexicographically (`Vec<u8>` ordering). The
|
|
102
|
+
* earlier port used `xid.toString().localeCompare(...)`, which (a)
|
|
103
|
+
* compares the UR-encoded base32-ish string, not the bytes, and (b)
|
|
104
|
+
* is locale-aware. Sorting on UR strings differs from the byte
|
|
105
|
+
* order whenever the underlying bytes contain values ≥ 0x80, so
|
|
106
|
+
* Rust and TS would assign different FROST identifiers to the same
|
|
107
|
+
* participant set — producing different secret shares.
|
|
98
108
|
*/
|
|
99
109
|
compareTo(other: DkgProposedParticipant): number {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
return compareXidBytes(this.xid().toData(), other.xid().toData());
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Lexicographic byte compare matching Rust's `Vec<u8>::cmp` /
|
|
116
|
+
* `XID::cmp`. Exported so the cmd-tree call sites (round1 / finalize)
|
|
117
|
+
* can use the same comparator when they sort deduplicated XID lists.
|
|
118
|
+
*
|
|
119
|
+
* `XID` is exactly 32 bytes so this only ever compares two equal-length
|
|
120
|
+
* inputs; the length-tiebreak branch mirrors the generic `Ord` impl on
|
|
121
|
+
* `Vec<u8>` and is included for correctness if ever applied to other
|
|
122
|
+
* byte sequences.
|
|
123
|
+
*
|
|
124
|
+
* @internal
|
|
125
|
+
*/
|
|
126
|
+
export function compareXidBytes(a: Uint8Array, b: Uint8Array): number {
|
|
127
|
+
const minLen = Math.min(a.length, b.length);
|
|
128
|
+
for (let i = 0; i < minLen; i++) {
|
|
129
|
+
if (a[i] !== b[i]) return a[i] < b[i] ? -1 : 1;
|
|
103
130
|
}
|
|
131
|
+
if (a.length !== b.length) return a.length < b.length ? -1 : 1;
|
|
132
|
+
return 0;
|
|
104
133
|
}
|
|
105
134
|
|
|
106
135
|
/**
|
|
@@ -101,8 +101,20 @@ export class OwnerRecord {
|
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
103
|
* Deserialize from JSON object.
|
|
104
|
+
*
|
|
105
|
+
* Mirrors Rust's `#[serde(deny_unknown_fields)]` derive on
|
|
106
|
+
* `OwnerRecord` (`owner_record.rs:13-17`) — any field not in
|
|
107
|
+
* `{xid_document, pet_name}` causes Rust's `serde_json::from_str`
|
|
108
|
+
* to error with `unknown field`, and we mirror that here so a
|
|
109
|
+
* registry file produced by a future Rust version with extra
|
|
110
|
+
* fields is rejected explicitly rather than silently lossy.
|
|
104
111
|
*/
|
|
105
112
|
static fromJSON(json: Record<string, unknown>): OwnerRecord {
|
|
113
|
+
for (const key of Object.keys(json)) {
|
|
114
|
+
if (key !== "xid_document" && key !== "pet_name") {
|
|
115
|
+
throw new Error(`unknown field \`${key}\`, expected \`xid_document\` or \`pet_name\``);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
106
118
|
const xidDocumentUr = json["xid_document"] as string;
|
|
107
119
|
const petName = json["pet_name"] as string | undefined;
|
|
108
120
|
return OwnerRecord.fromSignedXidUr(xidDocumentUr, petName);
|
|
@@ -141,8 +141,21 @@ export class ParticipantRecord {
|
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
143
|
* Deserialize from JSON object.
|
|
144
|
+
*
|
|
145
|
+
* Mirrors Rust's `#[serde(deny_unknown_fields)]` derive on
|
|
146
|
+
* `ParticipantRecord` (`participant_record.rs:12-17`) — Rust's
|
|
147
|
+
* `serde_json::from_str` errors with `unknown field` for any
|
|
148
|
+
* field outside `{xid_document, pet_name}`. We mirror that
|
|
149
|
+
* behaviour so a registry file produced by a future Rust
|
|
150
|
+
* version with extra fields is rejected explicitly rather than
|
|
151
|
+
* silently lossy.
|
|
144
152
|
*/
|
|
145
153
|
static fromJSON(json: Record<string, unknown>): ParticipantRecord {
|
|
154
|
+
for (const key of Object.keys(json)) {
|
|
155
|
+
if (key !== "xid_document" && key !== "pet_name") {
|
|
156
|
+
throw new Error(`unknown field \`${key}\`, expected \`xid_document\` or \`pet_name\``);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
146
159
|
const xidDocumentUr = json["xid_document"] as string;
|
|
147
160
|
const petName = json["pet_name"] as string | undefined;
|
|
148
161
|
return ParticipantRecord.recreateFromSerialized(xidDocumentUr, petName);
|
|
@@ -167,10 +180,30 @@ function parseSignedXidDocument(xidDocumentUr: string): [string, XIDDocument] {
|
|
|
167
180
|
try {
|
|
168
181
|
envelope = Envelope.fromTaggedCbor(envelopeCbor);
|
|
169
182
|
} catch {
|
|
170
|
-
|
|
183
|
+
try {
|
|
184
|
+
envelope = Envelope.fromUntaggedCbor(envelopeCbor);
|
|
185
|
+
} catch (e) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Unable to decode XID document envelope: ${(e as Error).message ?? String(e)}`,
|
|
188
|
+
{
|
|
189
|
+
cause: e,
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
}
|
|
171
193
|
}
|
|
172
194
|
|
|
173
|
-
|
|
195
|
+
// Mirror Rust `participant_record.rs:198-203`'s `.context(...)` wrap:
|
|
196
|
+
// any failure from `XIDDocument::from_envelope(..., XIDVerifySignature::Inception)`
|
|
197
|
+
// is surfaced as "XID document must be signed by its inception key: <cause>".
|
|
198
|
+
let document: XIDDocument;
|
|
199
|
+
try {
|
|
200
|
+
document = XIDDocument.fromEnvelope(envelope, undefined, XIDVerifySignature.Inception);
|
|
201
|
+
} catch (e) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`XID document must be signed by its inception key: ${(e as Error).message ?? String(e)}`,
|
|
204
|
+
{ cause: e },
|
|
205
|
+
);
|
|
206
|
+
}
|
|
174
207
|
|
|
175
208
|
return [sanitized, document];
|
|
176
209
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import * as fs from "node:fs";
|
|
14
14
|
import * as path from "node:path";
|
|
15
15
|
|
|
16
|
-
import { type ARID, type XID } from "@bcts/components";
|
|
16
|
+
import { type ARID, type PublicKeys, type XID } from "@bcts/components";
|
|
17
17
|
|
|
18
18
|
import { GroupRecord } from "./group-record.js";
|
|
19
19
|
import { OwnerRecord } from "./owner-record.js";
|
|
@@ -140,20 +140,57 @@ export class Registry {
|
|
|
140
140
|
/**
|
|
141
141
|
* Add a participant.
|
|
142
142
|
*
|
|
143
|
-
*
|
|
143
|
+
* Mirrors Rust `Registry::add_participant`
|
|
144
|
+
* (`registry_impl.rs:83-124`):
|
|
145
|
+
*
|
|
146
|
+
* 1. If `record.pet_name()` is already used by some other XID,
|
|
147
|
+
* bail with `"Pet name '{name}' already used by another
|
|
148
|
+
* participant"`.
|
|
149
|
+
* 2. If `record.pet_name()` matches an existing record under the
|
|
150
|
+
* *same* XID and the public keys also match, return
|
|
151
|
+
* `AlreadyPresent`.
|
|
152
|
+
* 3. If the pet name matches the same XID but public keys don't,
|
|
153
|
+
* bail with `"Participant already exists with a different pet
|
|
154
|
+
* name"`.
|
|
155
|
+
* 4. Otherwise look up by XID. If present and public-keys + pet-name
|
|
156
|
+
* both match, return `AlreadyPresent`; if XID is present but
|
|
157
|
+
* anything differs, bail. If XID is new, insert and return
|
|
158
|
+
* `Inserted`.
|
|
159
|
+
*
|
|
160
|
+
* The earlier port short-circuited on `participants.has(xidUr)` and
|
|
161
|
+
* always returned `AlreadyPresent` — silently allowing re-adds with
|
|
162
|
+
* a different pet name or different public keys, which Rust
|
|
163
|
+
* correctly forbids.
|
|
144
164
|
*/
|
|
145
165
|
addParticipant(xid: XID, record: ParticipantRecord): AddOutcome {
|
|
146
166
|
const xidUr = xid.urString();
|
|
167
|
+
const petName = record.petName();
|
|
147
168
|
|
|
148
|
-
//
|
|
149
|
-
if (
|
|
150
|
-
|
|
169
|
+
// Steps 1–3: pet-name conflict resolution.
|
|
170
|
+
if (petName !== undefined) {
|
|
171
|
+
for (const [existingXidUr, existingRecord] of this._participants) {
|
|
172
|
+
if (existingRecord.petName() === petName) {
|
|
173
|
+
if (existingXidUr !== xidUr) {
|
|
174
|
+
throw new Error(`Pet name '${petName}' already used by another participant`);
|
|
175
|
+
}
|
|
176
|
+
if (publicKeysEqual(existingRecord.publicKeys(), record.publicKeys())) {
|
|
177
|
+
return AddOutcome.AlreadyPresent;
|
|
178
|
+
}
|
|
179
|
+
throw new Error("Participant already exists with a different pet name");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
151
182
|
}
|
|
152
183
|
|
|
153
|
-
//
|
|
154
|
-
const
|
|
155
|
-
if (
|
|
156
|
-
|
|
184
|
+
// Step 4: XID lookup.
|
|
185
|
+
const existing = this._participants.get(xidUr);
|
|
186
|
+
if (existing !== undefined) {
|
|
187
|
+
if (
|
|
188
|
+
publicKeysEqual(existing.publicKeys(), record.publicKeys()) &&
|
|
189
|
+
existing.petName() === record.petName()
|
|
190
|
+
) {
|
|
191
|
+
return AddOutcome.AlreadyPresent;
|
|
192
|
+
}
|
|
193
|
+
throw new Error("Participant already exists with a different pet name");
|
|
157
194
|
}
|
|
158
195
|
|
|
159
196
|
this._participants.set(xidUr, record);
|
|
@@ -287,26 +324,34 @@ export class Registry {
|
|
|
287
324
|
|
|
288
325
|
/**
|
|
289
326
|
* Serialize to JSON object.
|
|
327
|
+
*
|
|
328
|
+
* Mirrors Rust `Registry`'s field declaration order
|
|
329
|
+
* (`registry_impl.rs:8-14` — `owner, participants, groups`). JSON
|
|
330
|
+
* object member order is not semantically significant, but
|
|
331
|
+
* `serde_json::to_string_pretty` emits keys in declaration order,
|
|
332
|
+
* so for byte-equal `registry.json` (used by the integration tests
|
|
333
|
+
* as a string assertion) the TS port must match Rust's order.
|
|
334
|
+
* Empty `owner` is omitted via `Option::None` skip in Rust; we
|
|
335
|
+
* reproduce that by only setting the key when the owner exists.
|
|
290
336
|
*/
|
|
291
337
|
toJSON(): Record<string, unknown> {
|
|
338
|
+
const obj: Record<string, unknown> = {};
|
|
339
|
+
|
|
340
|
+
if (this._owner !== undefined) {
|
|
341
|
+
obj["owner"] = this._owner.toJSON();
|
|
342
|
+
}
|
|
343
|
+
|
|
292
344
|
const participants: Record<string, unknown> = {};
|
|
293
345
|
for (const [xidUr, record] of this._participants) {
|
|
294
346
|
participants[xidUr] = record.toJSON();
|
|
295
347
|
}
|
|
348
|
+
obj["participants"] = participants;
|
|
296
349
|
|
|
297
350
|
const groups: Record<string, unknown> = {};
|
|
298
351
|
for (const [aridHex, record] of this._groups) {
|
|
299
352
|
groups[aridHex] = record.toJSON();
|
|
300
353
|
}
|
|
301
|
-
|
|
302
|
-
const obj: Record<string, unknown> = {
|
|
303
|
-
groups,
|
|
304
|
-
participants,
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
if (this._owner !== undefined) {
|
|
308
|
-
obj["owner"] = this._owner.toJSON();
|
|
309
|
-
}
|
|
354
|
+
obj["groups"] = groups;
|
|
310
355
|
|
|
311
356
|
return obj;
|
|
312
357
|
}
|
|
@@ -366,3 +411,14 @@ export function resolveRegistryPath(registryArg: string | undefined, cwd: string
|
|
|
366
411
|
// Otherwise, treat as relative path
|
|
367
412
|
return path.resolve(cwd, registryArg);
|
|
368
413
|
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Structural equality on `PublicKeys`, mirroring Rust's
|
|
417
|
+
* `PartialEq::eq` derive (per-component comparison of the signing
|
|
418
|
+
* and encapsulation public keys).
|
|
419
|
+
*
|
|
420
|
+
* @internal
|
|
421
|
+
*/
|
|
422
|
+
function publicKeysEqual(a: PublicKeys, b: PublicKeys): boolean {
|
|
423
|
+
return a.equals(b);
|
|
424
|
+
}
|