@did-btcr2/method 0.35.0 → 0.36.1
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/.tsbuildinfo +1 -1
- package/dist/browser.js +460 -223
- package/dist/browser.mjs +460 -223
- package/dist/cjs/index.js +461 -223
- package/dist/esm/core/aggregation/cohort.js +3 -1
- package/dist/esm/core/aggregation/cohort.js.map +1 -1
- package/dist/esm/core/aggregation/conditions.js +75 -0
- package/dist/esm/core/aggregation/conditions.js.map +1 -0
- package/dist/esm/core/aggregation/messages/base.js.map +1 -1
- package/dist/esm/core/aggregation/messages/bodies.js +16 -2
- package/dist/esm/core/aggregation/messages/bodies.js.map +1 -1
- package/dist/esm/core/aggregation/messages/factories.js.map +1 -1
- package/dist/esm/core/aggregation/participant.js +20 -7
- package/dist/esm/core/aggregation/participant.js.map +1 -1
- package/dist/esm/core/aggregation/runner/participant-runner.js +37 -2
- package/dist/esm/core/aggregation/runner/participant-runner.js.map +1 -1
- package/dist/esm/core/aggregation/runner/service-runner.js +323 -189
- package/dist/esm/core/aggregation/runner/service-runner.js.map +1 -1
- package/dist/esm/core/aggregation/service.js +23 -3
- package/dist/esm/core/aggregation/service.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/types/core/aggregation/cohort.d.ts.map +1 -1
- package/dist/types/core/aggregation/conditions.d.ts +58 -0
- package/dist/types/core/aggregation/conditions.d.ts.map +1 -0
- package/dist/types/core/aggregation/messages/base.d.ts +2 -3
- package/dist/types/core/aggregation/messages/base.d.ts.map +1 -1
- package/dist/types/core/aggregation/messages/bodies.d.ts +2 -3
- package/dist/types/core/aggregation/messages/bodies.d.ts.map +1 -1
- package/dist/types/core/aggregation/messages/factories.d.ts +2 -3
- package/dist/types/core/aggregation/messages/factories.d.ts.map +1 -1
- package/dist/types/core/aggregation/participant.d.ts +16 -4
- package/dist/types/core/aggregation/participant.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/events.d.ts +22 -11
- package/dist/types/core/aggregation/runner/events.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/participant-runner.d.ts +21 -12
- package/dist/types/core/aggregation/runner/participant-runner.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/service-runner.d.ts +76 -22
- package/dist/types/core/aggregation/runner/service-runner.d.ts.map +1 -1
- package/dist/types/core/aggregation/service.d.ts +8 -4
- package/dist/types/core/aggregation/service.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/core/aggregation/cohort.ts +3 -1
- package/src/core/aggregation/conditions.ts +116 -0
- package/src/core/aggregation/messages/base.ts +6 -3
- package/src/core/aggregation/messages/bodies.ts +18 -6
- package/src/core/aggregation/messages/factories.ts +2 -3
- package/src/core/aggregation/participant.ts +28 -11
- package/src/core/aggregation/runner/events.ts +23 -14
- package/src/core/aggregation/runner/participant-runner.ts +43 -13
- package/src/core/aggregation/runner/service-runner.ts +375 -195
- package/src/core/aggregation/service.ts +39 -7
- package/src/index.ts +1 -0
package/dist/browser.js
CHANGED
|
@@ -93203,6 +93203,7 @@ a=end-of-candidates
|
|
|
93203
93203
|
InMemoryRateLimitStore: () => InMemoryRateLimitStore,
|
|
93204
93204
|
InMemoryTransport: () => InMemoryTransport,
|
|
93205
93205
|
InboxBuffer: () => InboxBuffer,
|
|
93206
|
+
KNOWN_BEACON_TYPES: () => KNOWN_BEACON_TYPES,
|
|
93206
93207
|
KeyPairAggregationSigner: () => KeyPairAggregationSigner,
|
|
93207
93208
|
NONCE_CONTRIBUTION: () => NONCE_CONTRIBUTION,
|
|
93208
93209
|
NonceCache: () => NonceCache,
|
|
@@ -93275,6 +93276,7 @@ a=end-of-candidates
|
|
|
93275
93276
|
registerBeaconStrategy: () => registerBeaconStrategy,
|
|
93276
93277
|
reviveFromWire: () => reviveFromWire,
|
|
93277
93278
|
signEnvelope: () => signEnvelope,
|
|
93279
|
+
validateCohortConditions: () => validateCohortConditions,
|
|
93278
93280
|
verifyEnvelope: () => verifyEnvelope,
|
|
93279
93281
|
verifyRequestAuth: () => verifyRequestAuth
|
|
93280
93282
|
});
|
|
@@ -103381,7 +103383,7 @@ a=end-of-candidates
|
|
|
103381
103383
|
validationRejections = /* @__PURE__ */ new Set();
|
|
103382
103384
|
constructor({ id, minParticipants, serviceDid, network, beaconType }) {
|
|
103383
103385
|
this.id = id || crypto.randomUUID();
|
|
103384
|
-
this.minParticipants = minParticipants
|
|
103386
|
+
this.minParticipants = minParticipants ?? 2;
|
|
103385
103387
|
this.serviceDid = serviceDid || "";
|
|
103386
103388
|
this.network = network;
|
|
103387
103389
|
this.beaconType = beaconType || "CASBeacon";
|
|
@@ -103547,6 +103549,57 @@ a=end-of-candidates
|
|
|
103547
103549
|
}
|
|
103548
103550
|
};
|
|
103549
103551
|
|
|
103552
|
+
// src/core/aggregation/conditions.ts
|
|
103553
|
+
init_shim();
|
|
103554
|
+
var KNOWN_BEACON_TYPES = ["CASBeacon", "SMTBeacon"];
|
|
103555
|
+
function checkPair(problems, label, min, max) {
|
|
103556
|
+
if (min !== void 0 && (!Number.isInteger(min) || min < 0)) {
|
|
103557
|
+
problems.push(`min${label} must be an integer >= 0`);
|
|
103558
|
+
}
|
|
103559
|
+
if (max !== void 0 && (!Number.isInteger(max) || max < 0)) {
|
|
103560
|
+
problems.push(`max${label} must be an integer >= 0`);
|
|
103561
|
+
}
|
|
103562
|
+
if (min !== void 0 && max !== void 0 && Number.isInteger(min) && Number.isInteger(max) && max < min) {
|
|
103563
|
+
problems.push(`max${label} must be >= min${label}`);
|
|
103564
|
+
}
|
|
103565
|
+
}
|
|
103566
|
+
function checkCost(problems, label, cost) {
|
|
103567
|
+
if (cost === void 0) return;
|
|
103568
|
+
if (typeof cost.amount !== "number" || !Number.isFinite(cost.amount) || cost.amount < 0) {
|
|
103569
|
+
problems.push(`${label}.amount must be a finite number >= 0`);
|
|
103570
|
+
}
|
|
103571
|
+
if (typeof cost.unit !== "string" || cost.unit.length === 0) {
|
|
103572
|
+
problems.push(`${label}.unit must be a non-empty string`);
|
|
103573
|
+
}
|
|
103574
|
+
if (cost.basis !== void 0 && cost.basis !== "per-did" && cost.basis !== "per-participant") {
|
|
103575
|
+
problems.push(`${label}.basis must be 'per-did' or 'per-participant'`);
|
|
103576
|
+
}
|
|
103577
|
+
}
|
|
103578
|
+
function validateCohortConditions(c2) {
|
|
103579
|
+
const problems = [];
|
|
103580
|
+
if (!KNOWN_BEACON_TYPES.includes(c2.beaconType)) {
|
|
103581
|
+
problems.push(`beaconType must be one of ${KNOWN_BEACON_TYPES.join(", ")}`);
|
|
103582
|
+
}
|
|
103583
|
+
if (!Number.isInteger(c2.minParticipants) || c2.minParticipants < 1) {
|
|
103584
|
+
problems.push("minParticipants must be an integer >= 1");
|
|
103585
|
+
}
|
|
103586
|
+
if (c2.maxParticipants !== void 0) {
|
|
103587
|
+
if (!Number.isInteger(c2.maxParticipants) || c2.maxParticipants < 1) {
|
|
103588
|
+
problems.push("maxParticipants must be an integer >= 1");
|
|
103589
|
+
} else if (Number.isInteger(c2.minParticipants) && c2.maxParticipants < c2.minParticipants) {
|
|
103590
|
+
problems.push("maxParticipants must be >= minParticipants");
|
|
103591
|
+
}
|
|
103592
|
+
}
|
|
103593
|
+
checkPair(problems, "DidsPerParticipant", c2.minDidsPerParticipant, c2.maxDidsPerParticipant);
|
|
103594
|
+
checkPair(problems, "SecondsBetweenAnnouncements", c2.minSecondsBetweenAnnouncements, c2.maxSecondsBetweenAnnouncements);
|
|
103595
|
+
if (c2.pendingUpdateTrigger !== void 0 && (!Number.isInteger(c2.pendingUpdateTrigger) || c2.pendingUpdateTrigger < 1)) {
|
|
103596
|
+
problems.push("pendingUpdateTrigger must be an integer >= 1");
|
|
103597
|
+
}
|
|
103598
|
+
checkCost(problems, "costOfEnrollment", c2.costOfEnrollment);
|
|
103599
|
+
checkCost(problems, "costPerAnnouncement", c2.costPerAnnouncement);
|
|
103600
|
+
return problems;
|
|
103601
|
+
}
|
|
103602
|
+
|
|
103550
103603
|
// src/core/aggregation/messages/base.ts
|
|
103551
103604
|
init_shim();
|
|
103552
103605
|
var AGGREGATION_WIRE_VERSION = 1;
|
|
@@ -103981,6 +104034,14 @@ a=end-of-candidates
|
|
|
103981
104034
|
* Cohort starts in `Created` phase — call `advertise()` to broadcast.
|
|
103982
104035
|
*/
|
|
103983
104036
|
createCohort(config) {
|
|
104037
|
+
const problems = validateCohortConditions(config);
|
|
104038
|
+
if (problems.length > 0) {
|
|
104039
|
+
throw new AggregationServiceError(
|
|
104040
|
+
`Invalid cohort conditions: ${problems.join("; ")}`,
|
|
104041
|
+
"INVALID_COHORT_CONDITIONS",
|
|
104042
|
+
{ problems }
|
|
104043
|
+
);
|
|
104044
|
+
}
|
|
103984
104045
|
const cohort = new AggregationCohort({
|
|
103985
104046
|
serviceDid: this.did,
|
|
103986
104047
|
minParticipants: config.minParticipants,
|
|
@@ -104013,13 +104074,13 @@ a=end-of-candidates
|
|
|
104013
104074
|
{ cohortId, phase: state.phase }
|
|
104014
104075
|
);
|
|
104015
104076
|
}
|
|
104077
|
+
const { network, ...conditions } = state.config;
|
|
104016
104078
|
const message2 = createCohortAdvertMessage({
|
|
104017
104079
|
from: this.did,
|
|
104018
104080
|
cohortId,
|
|
104019
|
-
|
|
104020
|
-
|
|
104021
|
-
|
|
104022
|
-
communicationPk: this.publicKey.compressed
|
|
104081
|
+
network,
|
|
104082
|
+
communicationPk: this.publicKey.compressed,
|
|
104083
|
+
...conditions
|
|
104023
104084
|
});
|
|
104024
104085
|
state.phase = "Advertised" /* Advertised */;
|
|
104025
104086
|
return [message2];
|
|
@@ -104078,6 +104139,14 @@ a=end-of-candidates
|
|
|
104078
104139
|
{ cohortId, participantDid }
|
|
104079
104140
|
);
|
|
104080
104141
|
}
|
|
104142
|
+
const maxParticipants = state.config.maxParticipants;
|
|
104143
|
+
if (maxParticipants !== void 0 && state.acceptedParticipants.size >= maxParticipants) {
|
|
104144
|
+
throw new AggregationServiceError(
|
|
104145
|
+
`Cohort ${cohortId} is full: ${maxParticipants} participants already accepted.`,
|
|
104146
|
+
"COHORT_FULL",
|
|
104147
|
+
{ cohortId, maxParticipants }
|
|
104148
|
+
);
|
|
104149
|
+
}
|
|
104081
104150
|
state.acceptedParticipants.add(participantDid);
|
|
104082
104151
|
state.cohort.participants.push(participantDid);
|
|
104083
104152
|
state.cohort.participantKeys.set(participantDid, optIn.participantPk);
|
|
@@ -104111,6 +104180,14 @@ a=end-of-candidates
|
|
|
104111
104180
|
{ cohortId }
|
|
104112
104181
|
);
|
|
104113
104182
|
}
|
|
104183
|
+
const maxParticipants = state.config.maxParticipants;
|
|
104184
|
+
if (maxParticipants !== void 0 && state.acceptedParticipants.size > maxParticipants) {
|
|
104185
|
+
throw new AggregationServiceError(
|
|
104186
|
+
`Cohort ${cohortId} has ${state.acceptedParticipants.size} accepted participants, exceeds max ${maxParticipants}.`,
|
|
104187
|
+
"TOO_MANY_PARTICIPANTS",
|
|
104188
|
+
{ cohortId, maxParticipants }
|
|
104189
|
+
);
|
|
104190
|
+
}
|
|
104114
104191
|
const beaconAddress = state.cohort.computeBeaconAddress();
|
|
104115
104192
|
state.phase = "CohortSet" /* CohortSet */;
|
|
104116
104193
|
const messages2 = [];
|
|
@@ -104428,6 +104505,59 @@ a=end-of-candidates
|
|
|
104428
104505
|
// src/core/aggregation/participant.ts
|
|
104429
104506
|
init_shim();
|
|
104430
104507
|
init_utils();
|
|
104508
|
+
|
|
104509
|
+
// src/core/aggregation/messages/bodies.ts
|
|
104510
|
+
init_shim();
|
|
104511
|
+
var hasStr = (b, k) => !!b && typeof b[k] === "string";
|
|
104512
|
+
var hasIntMin = (b, k, min) => {
|
|
104513
|
+
const v = b ? b[k] : void 0;
|
|
104514
|
+
return typeof v === "number" && Number.isInteger(v) && v >= min;
|
|
104515
|
+
};
|
|
104516
|
+
var optIntMin = (b, k, min) => {
|
|
104517
|
+
const v = b ? b[k] : void 0;
|
|
104518
|
+
return v === void 0 || typeof v === "number" && Number.isInteger(v) && v >= min;
|
|
104519
|
+
};
|
|
104520
|
+
var hasBool = (b, k) => !!b && typeof b[k] === "boolean";
|
|
104521
|
+
var hasBytes = (b, k) => !!b && b[k] instanceof Uint8Array;
|
|
104522
|
+
var hasBytesArray = (b, k) => {
|
|
104523
|
+
const v = b ? b[k] : void 0;
|
|
104524
|
+
return Array.isArray(v) && v.every((x) => x instanceof Uint8Array);
|
|
104525
|
+
};
|
|
104526
|
+
function isCohortAdvertMessage(m2) {
|
|
104527
|
+
return m2.type === COHORT_ADVERT && hasStr(m2.body, "cohortId") && hasIntMin(m2.body, "minParticipants", 1) && optIntMin(m2.body, "maxParticipants", 1) && hasStr(m2.body, "beaconType") && hasStr(m2.body, "network") && hasBytes(m2.body, "communicationPk");
|
|
104528
|
+
}
|
|
104529
|
+
function isCohortOptInMessage(m2) {
|
|
104530
|
+
return m2.type === COHORT_OPT_IN && hasStr(m2.body, "cohortId") && hasBytes(m2.body, "participantPk") && hasBytes(m2.body, "communicationPk");
|
|
104531
|
+
}
|
|
104532
|
+
function isCohortOptInAcceptMessage(m2) {
|
|
104533
|
+
return m2.type === COHORT_OPT_IN_ACCEPT && hasStr(m2.body, "cohortId");
|
|
104534
|
+
}
|
|
104535
|
+
function isCohortReadyMessage(m2) {
|
|
104536
|
+
return m2.type === COHORT_READY && hasStr(m2.body, "cohortId") && hasStr(m2.body, "beaconAddress") && hasBytesArray(m2.body, "cohortKeys");
|
|
104537
|
+
}
|
|
104538
|
+
function isSubmitUpdateMessage(m2) {
|
|
104539
|
+
return m2.type === SUBMIT_UPDATE && hasStr(m2.body, "cohortId") && !!m2.body && typeof m2.body.signedUpdate === "object";
|
|
104540
|
+
}
|
|
104541
|
+
function isDistributeAggregatedDataMessage(m2) {
|
|
104542
|
+
return m2.type === DISTRIBUTE_AGGREGATED_DATA && hasStr(m2.body, "cohortId") && hasStr(m2.body, "beaconType") && hasStr(m2.body, "signalBytesHex");
|
|
104543
|
+
}
|
|
104544
|
+
function isValidationAckMessage(m2) {
|
|
104545
|
+
return m2.type === VALIDATION_ACK && hasStr(m2.body, "cohortId") && hasBool(m2.body, "approved");
|
|
104546
|
+
}
|
|
104547
|
+
function isAuthorizationRequestMessage(m2) {
|
|
104548
|
+
return m2.type === AUTHORIZATION_REQUEST && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasStr(m2.body, "pendingTx") && hasStr(m2.body, "prevOutScriptHex") && hasStr(m2.body, "prevOutValue");
|
|
104549
|
+
}
|
|
104550
|
+
function isNonceContributionMessage(m2) {
|
|
104551
|
+
return m2.type === NONCE_CONTRIBUTION && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "nonceContribution");
|
|
104552
|
+
}
|
|
104553
|
+
function isAggregatedNonceMessage(m2) {
|
|
104554
|
+
return m2.type === AGGREGATED_NONCE && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "aggregatedNonce");
|
|
104555
|
+
}
|
|
104556
|
+
function isSignatureAuthorizationMessage(m2) {
|
|
104557
|
+
return m2.type === SIGNATURE_AUTHORIZATION && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "partialSignature");
|
|
104558
|
+
}
|
|
104559
|
+
|
|
104560
|
+
// src/core/aggregation/participant.ts
|
|
104431
104561
|
var AggregationParticipant = class {
|
|
104432
104562
|
did;
|
|
104433
104563
|
/** MuSig2 signing capability. The raw secret never lives as a field here. */
|
|
@@ -104485,16 +104615,15 @@ a=end-of-candidates
|
|
|
104485
104615
|
return map3;
|
|
104486
104616
|
}
|
|
104487
104617
|
#handleCohortAdvert(message2) {
|
|
104488
|
-
|
|
104489
|
-
|
|
104618
|
+
if (!isCohortAdvertMessage(message2)) return;
|
|
104619
|
+
const { cohortId, network, communicationPk, ...conditions } = message2.body;
|
|
104490
104620
|
if (this.#cohortStates.has(cohortId)) return;
|
|
104491
104621
|
const advert = {
|
|
104492
104622
|
cohortId,
|
|
104493
104623
|
serviceDid: message2.from,
|
|
104494
|
-
|
|
104495
|
-
|
|
104496
|
-
|
|
104497
|
-
serviceCommunicationPk: message2.body?.communicationPk ?? new Uint8Array()
|
|
104624
|
+
network,
|
|
104625
|
+
serviceCommunicationPk: communicationPk,
|
|
104626
|
+
...conditions
|
|
104498
104627
|
};
|
|
104499
104628
|
this.#cohortStates.set(cohortId, {
|
|
104500
104629
|
phase: "Discovered" /* Discovered */,
|
|
@@ -104519,7 +104648,7 @@ a=end-of-candidates
|
|
|
104519
104648
|
const cohort = new AggregationCohort({
|
|
104520
104649
|
id: cohortId,
|
|
104521
104650
|
serviceDid: state.serviceDid,
|
|
104522
|
-
minParticipants: state.advert.
|
|
104651
|
+
minParticipants: state.advert.minParticipants,
|
|
104523
104652
|
network: state.advert.network,
|
|
104524
104653
|
beaconType: state.advert.beaconType
|
|
104525
104654
|
});
|
|
@@ -104598,6 +104727,17 @@ a=end-of-candidates
|
|
|
104598
104727
|
}
|
|
104599
104728
|
return map3;
|
|
104600
104729
|
}
|
|
104730
|
+
/**
|
|
104731
|
+
* The validated aggregated data retained for a cohort, regardless of phase.
|
|
104732
|
+
* Unlike {@link pendingValidations} (which lists only cohorts still awaiting
|
|
104733
|
+
* the validate decision), this returns the stored validation — including the
|
|
104734
|
+
* participant's sidecar (the CAS Announcement map or its SMT inclusion proof)
|
|
104735
|
+
* — so it is still readable once the cohort reaches Complete. Returns
|
|
104736
|
+
* undefined before aggregated data has been received.
|
|
104737
|
+
*/
|
|
104738
|
+
getValidation(cohortId) {
|
|
104739
|
+
return this.#cohortStates.get(cohortId)?.validation;
|
|
104740
|
+
}
|
|
104601
104741
|
#handleDistributeAggregatedData(message2) {
|
|
104602
104742
|
const cohortId = message2.body?.cohortId;
|
|
104603
104743
|
if (!cohortId) return;
|
|
@@ -104842,50 +104982,6 @@ a=end-of-candidates
|
|
|
104842
104982
|
// src/core/aggregation/messages/index.ts
|
|
104843
104983
|
init_shim();
|
|
104844
104984
|
|
|
104845
|
-
// src/core/aggregation/messages/bodies.ts
|
|
104846
|
-
init_shim();
|
|
104847
|
-
var hasStr = (b, k) => !!b && typeof b[k] === "string";
|
|
104848
|
-
var hasNum = (b, k) => !!b && typeof b[k] === "number";
|
|
104849
|
-
var hasBool = (b, k) => !!b && typeof b[k] === "boolean";
|
|
104850
|
-
var hasBytes = (b, k) => !!b && b[k] instanceof Uint8Array;
|
|
104851
|
-
var hasBytesArray = (b, k) => {
|
|
104852
|
-
const v = b ? b[k] : void 0;
|
|
104853
|
-
return Array.isArray(v) && v.every((x) => x instanceof Uint8Array);
|
|
104854
|
-
};
|
|
104855
|
-
function isCohortAdvertMessage(m2) {
|
|
104856
|
-
return m2.type === COHORT_ADVERT && hasStr(m2.body, "cohortId") && hasNum(m2.body, "cohortSize") && hasStr(m2.body, "beaconType") && hasStr(m2.body, "network") && hasBytes(m2.body, "communicationPk");
|
|
104857
|
-
}
|
|
104858
|
-
function isCohortOptInMessage(m2) {
|
|
104859
|
-
return m2.type === COHORT_OPT_IN && hasStr(m2.body, "cohortId") && hasBytes(m2.body, "participantPk") && hasBytes(m2.body, "communicationPk");
|
|
104860
|
-
}
|
|
104861
|
-
function isCohortOptInAcceptMessage(m2) {
|
|
104862
|
-
return m2.type === COHORT_OPT_IN_ACCEPT && hasStr(m2.body, "cohortId");
|
|
104863
|
-
}
|
|
104864
|
-
function isCohortReadyMessage(m2) {
|
|
104865
|
-
return m2.type === COHORT_READY && hasStr(m2.body, "cohortId") && hasStr(m2.body, "beaconAddress") && hasBytesArray(m2.body, "cohortKeys");
|
|
104866
|
-
}
|
|
104867
|
-
function isSubmitUpdateMessage(m2) {
|
|
104868
|
-
return m2.type === SUBMIT_UPDATE && hasStr(m2.body, "cohortId") && !!m2.body && typeof m2.body.signedUpdate === "object";
|
|
104869
|
-
}
|
|
104870
|
-
function isDistributeAggregatedDataMessage(m2) {
|
|
104871
|
-
return m2.type === DISTRIBUTE_AGGREGATED_DATA && hasStr(m2.body, "cohortId") && hasStr(m2.body, "beaconType") && hasStr(m2.body, "signalBytesHex");
|
|
104872
|
-
}
|
|
104873
|
-
function isValidationAckMessage(m2) {
|
|
104874
|
-
return m2.type === VALIDATION_ACK && hasStr(m2.body, "cohortId") && hasBool(m2.body, "approved");
|
|
104875
|
-
}
|
|
104876
|
-
function isAuthorizationRequestMessage(m2) {
|
|
104877
|
-
return m2.type === AUTHORIZATION_REQUEST && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasStr(m2.body, "pendingTx") && hasStr(m2.body, "prevOutScriptHex") && hasStr(m2.body, "prevOutValue");
|
|
104878
|
-
}
|
|
104879
|
-
function isNonceContributionMessage(m2) {
|
|
104880
|
-
return m2.type === NONCE_CONTRIBUTION && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "nonceContribution");
|
|
104881
|
-
}
|
|
104882
|
-
function isAggregatedNonceMessage(m2) {
|
|
104883
|
-
return m2.type === AGGREGATED_NONCE && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "aggregatedNonce");
|
|
104884
|
-
}
|
|
104885
|
-
function isSignatureAuthorizationMessage(m2) {
|
|
104886
|
-
return m2.type === SIGNATURE_AUTHORIZATION && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "partialSignature");
|
|
104887
|
-
}
|
|
104888
|
-
|
|
104889
104985
|
// src/core/aggregation/messages/guards.ts
|
|
104890
104986
|
init_shim();
|
|
104891
104987
|
var KEYGEN_VALUES = /* @__PURE__ */ new Set([
|
|
@@ -111518,35 +111614,22 @@ ${value2}`;
|
|
|
111518
111614
|
session;
|
|
111519
111615
|
#transport;
|
|
111520
111616
|
#did;
|
|
111521
|
-
#
|
|
111617
|
+
#defaultConfig;
|
|
111522
111618
|
#onOptInReceived;
|
|
111523
111619
|
#onReadyToFinalize;
|
|
111524
111620
|
#onProvideTxData;
|
|
111525
111621
|
#cohortTtlMs;
|
|
111526
111622
|
#phaseTimeoutMs;
|
|
111527
111623
|
#advertRepeatIntervalMs;
|
|
111528
|
-
|
|
111624
|
+
/** Per-cohort run state, keyed by cohortId. */
|
|
111625
|
+
#contexts = /* @__PURE__ */ new Map();
|
|
111529
111626
|
#handlersRegistered = false;
|
|
111530
111627
|
#stopped = false;
|
|
111531
|
-
/**
|
|
111532
|
-
* Guard against the async race where two concurrent #handleOptIn invocations
|
|
111533
|
-
* both pass the `participants.length >= minParticipants` check before either
|
|
111534
|
-
* mutates the cohort phase. Set synchronously before any `await` so subsequent
|
|
111535
|
-
* handlers observe it on their next resumption.
|
|
111536
|
-
*/
|
|
111537
|
-
#finalizing = false;
|
|
111538
|
-
#resolveRun;
|
|
111539
|
-
#rejectRun;
|
|
111540
|
-
#cohortTtlTimer;
|
|
111541
|
-
#phaseTimer;
|
|
111542
|
-
#lastObservedPhase;
|
|
111543
|
-
/** Stop handle for the repeating COHORT_ADVERT publish loop. */
|
|
111544
|
-
#stopAdvertRepeat;
|
|
111545
111628
|
constructor(options2) {
|
|
111546
111629
|
super();
|
|
111547
111630
|
this.#transport = options2.transport;
|
|
111548
111631
|
this.#did = options2.did;
|
|
111549
|
-
this.#
|
|
111632
|
+
this.#defaultConfig = options2.config;
|
|
111550
111633
|
this.#onOptInReceived = options2.onOptInReceived ?? (async () => ({ accepted: true }));
|
|
111551
111634
|
this.#onReadyToFinalize = options2.onReadyToFinalize ?? (async ({ acceptedCount, minRequired }) => ({
|
|
111552
111635
|
finalize: acceptedCount >= minRequired
|
|
@@ -111564,55 +111647,126 @@ ${value2}`;
|
|
|
111564
111647
|
maxUpdateSizeBytes: options2.maxUpdateSizeBytes
|
|
111565
111648
|
});
|
|
111566
111649
|
}
|
|
111650
|
+
/** Resolve the {@link RunContext} an inbound message belongs to, by cohortId. */
|
|
111651
|
+
#contextFor(msg) {
|
|
111652
|
+
const cohortId = msg.body?.cohortId;
|
|
111653
|
+
if (!cohortId) return void 0;
|
|
111654
|
+
return this.#contexts.get(cohortId);
|
|
111655
|
+
}
|
|
111567
111656
|
/**
|
|
111568
|
-
* Drain any silent rejections the state machine recorded
|
|
111569
|
-
* recent receive() and surface them as `message-rejected` events.
|
|
111570
|
-
* call even before a cohortId is assigned.
|
|
111657
|
+
* Drain any silent rejections the state machine recorded for a cohort during
|
|
111658
|
+
* the most recent receive() and surface them as `message-rejected` events.
|
|
111571
111659
|
*/
|
|
111572
|
-
#drainRejections() {
|
|
111573
|
-
|
|
111574
|
-
|
|
111575
|
-
this.emit("message-rejected", { cohortId: this.#cohortId, ...r2 });
|
|
111660
|
+
#drainRejections(ctx) {
|
|
111661
|
+
for (const r2 of this.session.drainRejections(ctx.cohortId)) {
|
|
111662
|
+
this.emit("message-rejected", { cohortId: ctx.cohortId, ...r2 });
|
|
111576
111663
|
}
|
|
111577
111664
|
}
|
|
111578
111665
|
/**
|
|
111579
|
-
*
|
|
111580
|
-
*
|
|
111666
|
+
* Advertise a new cohort and begin driving it to completion. Callable many
|
|
111667
|
+
* times on one runner; each cohort runs concurrently and independently.
|
|
111668
|
+
*
|
|
111669
|
+
* @param config Per-cohort conditions + network (see {@link CohortConfig}).
|
|
111670
|
+
* @returns The new cohort's id and a `completion` promise that resolves with
|
|
111671
|
+
* that cohort's {@link AggregationResult} (or rejects if it fails/stalls).
|
|
111672
|
+
* @throws If the runner has been stopped, or the config is invalid
|
|
111673
|
+
* (fail-fast via `createCohort`).
|
|
111674
|
+
*/
|
|
111675
|
+
advertiseCohort(config) {
|
|
111676
|
+
if (this.#stopped) {
|
|
111677
|
+
throw new AggregationServiceError("Cannot advertise on a stopped runner.", "RUNNER_STOPPED", {});
|
|
111678
|
+
}
|
|
111679
|
+
this.#registerHandlers();
|
|
111680
|
+
const cohortId = this.session.createCohort(config);
|
|
111681
|
+
let resolve;
|
|
111682
|
+
let reject;
|
|
111683
|
+
const completion = new Promise((res, rej) => {
|
|
111684
|
+
resolve = res;
|
|
111685
|
+
reject = rej;
|
|
111686
|
+
});
|
|
111687
|
+
const ctx = {
|
|
111688
|
+
cohortId,
|
|
111689
|
+
config,
|
|
111690
|
+
resolve,
|
|
111691
|
+
reject,
|
|
111692
|
+
completion,
|
|
111693
|
+
finalizing: false,
|
|
111694
|
+
settled: false
|
|
111695
|
+
};
|
|
111696
|
+
this.#contexts.set(cohortId, ctx);
|
|
111697
|
+
try {
|
|
111698
|
+
this.#startTimers(ctx);
|
|
111699
|
+
const advertMsgs = this.session.advertise(cohortId);
|
|
111700
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111701
|
+
this.emit("cohort-advertised", { cohortId });
|
|
111702
|
+
if (this.#advertRepeatIntervalMs > 0) {
|
|
111703
|
+
this.#startAdvertRepeat(ctx, advertMsgs);
|
|
111704
|
+
} else {
|
|
111705
|
+
this.#sendAll(advertMsgs).catch((err) => this.#failCohort(ctx, err));
|
|
111706
|
+
}
|
|
111707
|
+
} catch (err) {
|
|
111708
|
+
this.#failCohort(ctx, err);
|
|
111709
|
+
}
|
|
111710
|
+
return { cohortId, completion };
|
|
111711
|
+
}
|
|
111712
|
+
/**
|
|
111713
|
+
* Run a single cohort to completion using the `config` supplied in the
|
|
111714
|
+
* runner options. Thin convenience over {@link advertiseCohort} for the
|
|
111715
|
+
* single-cohort case (and the path {@link AggregationRunner.solo} rides).
|
|
111581
111716
|
*
|
|
111582
111717
|
* @returns {Promise<AggregationResult>} The final result with signature and signed tx.
|
|
111583
111718
|
*/
|
|
111584
111719
|
run() {
|
|
111585
|
-
|
|
111586
|
-
|
|
111587
|
-
|
|
111588
|
-
|
|
111589
|
-
|
|
111590
|
-
|
|
111591
|
-
|
|
111592
|
-
|
|
111593
|
-
|
|
111594
|
-
|
|
111595
|
-
|
|
111596
|
-
|
|
111597
|
-
|
|
111598
|
-
|
|
111599
|
-
|
|
111600
|
-
|
|
111601
|
-
|
|
111720
|
+
if (!this.#defaultConfig) {
|
|
111721
|
+
return Promise.reject(new AggregationServiceError(
|
|
111722
|
+
"run() requires `config` in the runner options; use advertiseCohort(config) to drive cohorts explicitly.",
|
|
111723
|
+
"MISSING_COHORT_CONFIG",
|
|
111724
|
+
{}
|
|
111725
|
+
));
|
|
111726
|
+
}
|
|
111727
|
+
try {
|
|
111728
|
+
return this.advertiseCohort(this.#defaultConfig).completion;
|
|
111729
|
+
} catch (err) {
|
|
111730
|
+
return Promise.reject(err);
|
|
111731
|
+
}
|
|
111732
|
+
}
|
|
111733
|
+
/**
|
|
111734
|
+
* Wait for every currently-outstanding cohort to settle and return the
|
|
111735
|
+
* successful results. Dynamic drain: cohorts advertised while this is pending
|
|
111736
|
+
* are included, and it resolves only once no cohorts remain. Failed cohorts
|
|
111737
|
+
* are surfaced via `error` / `cohort-failed` events and their rejected
|
|
111738
|
+
* `completion` promises; they are omitted from the returned array (this
|
|
111739
|
+
* method does not throw). Bound long-running cohorts with `cohortTtlMs` /
|
|
111740
|
+
* `phaseTimeoutMs` or this may never resolve.
|
|
111741
|
+
*
|
|
111742
|
+
* @returns {Promise<AggregationResult[]>} Results of the cohorts that completed.
|
|
111743
|
+
*/
|
|
111744
|
+
async runAll() {
|
|
111745
|
+
const collected = /* @__PURE__ */ new Map();
|
|
111746
|
+
const onComplete = (result) => {
|
|
111747
|
+
collected.set(result.cohortId, result);
|
|
111748
|
+
};
|
|
111749
|
+
this.on("signing-complete", onComplete);
|
|
111750
|
+
try {
|
|
111751
|
+
while (this.#contexts.size > 0) {
|
|
111752
|
+
await Promise.allSettled([...this.#contexts.values()].map((c2) => c2.completion));
|
|
111602
111753
|
}
|
|
111603
|
-
}
|
|
111754
|
+
} finally {
|
|
111755
|
+
this.off("signing-complete", onComplete);
|
|
111756
|
+
}
|
|
111757
|
+
return [...collected.values()];
|
|
111604
111758
|
}
|
|
111605
111759
|
/**
|
|
111606
|
-
* Begin publishing
|
|
111607
|
-
* until
|
|
111608
|
-
*
|
|
111760
|
+
* Begin publishing a cohort's advert immediately and on a repeating interval
|
|
111761
|
+
* until the cohort's advert loop is stopped. Each advert is broadcast (no
|
|
111762
|
+
* recipient) via the transport's `publishRepeating` primitive.
|
|
111609
111763
|
*/
|
|
111610
|
-
#startAdvertRepeat(advertMsgs) {
|
|
111764
|
+
#startAdvertRepeat(ctx, advertMsgs) {
|
|
111611
111765
|
const stops = [];
|
|
111612
111766
|
for (const msg of advertMsgs) {
|
|
111613
111767
|
stops.push(this.#transport.publishRepeating(msg, this.#did, this.#advertRepeatIntervalMs));
|
|
111614
111768
|
}
|
|
111615
|
-
|
|
111769
|
+
ctx.stopAdvertRepeat = () => {
|
|
111616
111770
|
for (const stop2 of stops) {
|
|
111617
111771
|
try {
|
|
111618
111772
|
stop2();
|
|
@@ -111621,61 +111775,118 @@ ${value2}`;
|
|
|
111621
111775
|
}
|
|
111622
111776
|
};
|
|
111623
111777
|
}
|
|
111624
|
-
/** Stop
|
|
111625
|
-
#stopAdvertRepeating() {
|
|
111626
|
-
if (!
|
|
111627
|
-
const stop2 =
|
|
111628
|
-
|
|
111778
|
+
/** Stop a cohort's advert republish loop. Idempotent. */
|
|
111779
|
+
#stopAdvertRepeating(ctx) {
|
|
111780
|
+
if (!ctx.stopAdvertRepeat) return;
|
|
111781
|
+
const stop2 = ctx.stopAdvertRepeat;
|
|
111782
|
+
ctx.stopAdvertRepeat = void 0;
|
|
111629
111783
|
stop2();
|
|
111630
111784
|
}
|
|
111631
|
-
/** Schedule cohort TTL + phase timeout
|
|
111632
|
-
#startTimers() {
|
|
111785
|
+
/** Schedule a cohort's TTL + phase timeout when it is advertised. */
|
|
111786
|
+
#startTimers(ctx) {
|
|
111633
111787
|
if (this.#cohortTtlMs !== void 0) {
|
|
111634
|
-
|
|
111635
|
-
const reason = `Cohort ${
|
|
111636
|
-
this.emit("cohort-failed", { cohortId:
|
|
111637
|
-
this.#
|
|
111788
|
+
ctx.cohortTtlTimer = setTimeout(() => {
|
|
111789
|
+
const reason = `Cohort ${ctx.cohortId} exceeded TTL of ${this.#cohortTtlMs}ms`;
|
|
111790
|
+
this.emit("cohort-failed", { cohortId: ctx.cohortId, reason });
|
|
111791
|
+
this.#failCohort(ctx, new Error(reason));
|
|
111638
111792
|
}, this.#cohortTtlMs);
|
|
111639
111793
|
}
|
|
111640
|
-
this.#resetPhaseTimer();
|
|
111794
|
+
this.#resetPhaseTimer(ctx);
|
|
111641
111795
|
}
|
|
111642
|
-
/** Reset
|
|
111643
|
-
#resetPhaseTimer() {
|
|
111644
|
-
if (
|
|
111645
|
-
|
|
111796
|
+
/** Reset a cohort's per-phase stall timer. Called when a phase transition is observed. */
|
|
111797
|
+
#resetPhaseTimer(ctx) {
|
|
111798
|
+
if (ctx.phaseTimer) clearTimeout(ctx.phaseTimer);
|
|
111799
|
+
ctx.phaseTimer = void 0;
|
|
111646
111800
|
if (this.#phaseTimeoutMs === void 0) return;
|
|
111647
|
-
|
|
111648
|
-
const reason = `Cohort ${
|
|
111649
|
-
this.emit("cohort-failed", { cohortId:
|
|
111650
|
-
this.#
|
|
111801
|
+
ctx.phaseTimer = setTimeout(() => {
|
|
111802
|
+
const reason = `Cohort ${ctx.cohortId} stalled in phase ${ctx.lastObservedPhase ?? "?"} for ${this.#phaseTimeoutMs}ms`;
|
|
111803
|
+
this.emit("cohort-failed", { cohortId: ctx.cohortId, reason });
|
|
111804
|
+
this.#failCohort(ctx, new Error(reason));
|
|
111651
111805
|
}, this.#phaseTimeoutMs);
|
|
111652
111806
|
}
|
|
111653
|
-
/** Detect a phase change since the last observation and reset
|
|
111654
|
-
#onPhaseMaybeChanged() {
|
|
111655
|
-
|
|
111656
|
-
|
|
111657
|
-
|
|
111658
|
-
this.#
|
|
111659
|
-
this.#resetPhaseTimer();
|
|
111807
|
+
/** Detect a phase change for a cohort since the last observation and reset its phase timer. */
|
|
111808
|
+
#onPhaseMaybeChanged(ctx) {
|
|
111809
|
+
const phase = this.session.getCohortPhase(ctx.cohortId);
|
|
111810
|
+
if (phase !== ctx.lastObservedPhase) {
|
|
111811
|
+
ctx.lastObservedPhase = phase;
|
|
111812
|
+
this.#resetPhaseTimer(ctx);
|
|
111660
111813
|
}
|
|
111661
111814
|
}
|
|
111662
|
-
/** Clear
|
|
111663
|
-
#clearTimers() {
|
|
111664
|
-
if (
|
|
111665
|
-
if (
|
|
111666
|
-
|
|
111667
|
-
|
|
111815
|
+
/** Clear a cohort's timers. Called on completion, stop, and failure. */
|
|
111816
|
+
#clearTimers(ctx) {
|
|
111817
|
+
if (ctx.cohortTtlTimer) clearTimeout(ctx.cohortTtlTimer);
|
|
111818
|
+
if (ctx.phaseTimer) clearTimeout(ctx.phaseTimer);
|
|
111819
|
+
ctx.cohortTtlTimer = void 0;
|
|
111820
|
+
ctx.phaseTimer = void 0;
|
|
111821
|
+
}
|
|
111822
|
+
/**
|
|
111823
|
+
* Reclaim one cohort's runner-layer bookkeeping: stop its advert loop, clear
|
|
111824
|
+
* its timers, and drop its {@link RunContext}. Does NOT touch sibling cohorts
|
|
111825
|
+
* and does NOT detach the shared transport handlers. Leaves the cohort in the
|
|
111826
|
+
* state machine; whether that cohort's `session` state is also removed is the
|
|
111827
|
+
* caller's choice (see {@link #completeCohort} vs {@link #failCohort}).
|
|
111828
|
+
*/
|
|
111829
|
+
#disposeCohort(ctx) {
|
|
111830
|
+
this.#stopAdvertRepeating(ctx);
|
|
111831
|
+
this.#clearTimers(ctx);
|
|
111832
|
+
this.#contexts.delete(ctx.cohortId);
|
|
111833
|
+
}
|
|
111834
|
+
/**
|
|
111835
|
+
* Settle one cohort successfully. Reclaims the runner context but leaves the
|
|
111836
|
+
* completed cohort in `session` so callers can read its beaconAddress / cohort
|
|
111837
|
+
* via `session.getCohort(result.cohortId)`; reclaim it with
|
|
111838
|
+
* `session.removeCohort(cohortId)` when done. Idempotent via `ctx.settled`.
|
|
111839
|
+
*/
|
|
111840
|
+
#completeCohort(ctx, result) {
|
|
111841
|
+
if (ctx.settled) return;
|
|
111842
|
+
ctx.settled = true;
|
|
111843
|
+
this.#disposeCohort(ctx);
|
|
111844
|
+
this.emit("signing-complete", result);
|
|
111845
|
+
ctx.resolve(result);
|
|
111846
|
+
}
|
|
111847
|
+
/**
|
|
111848
|
+
* Fail one cohort. Reclaims its runner context, drops its now-dead state from
|
|
111849
|
+
* the state machine, and rejects only its completion; siblings keep running
|
|
111850
|
+
* and the shared transport handlers stay registered. Idempotent via
|
|
111851
|
+
* `ctx.settled`.
|
|
111852
|
+
*/
|
|
111853
|
+
#failCohort(ctx, err) {
|
|
111854
|
+
if (ctx.settled) return;
|
|
111855
|
+
ctx.settled = true;
|
|
111856
|
+
this.#disposeCohort(ctx);
|
|
111857
|
+
this.session.removeCohort(ctx.cohortId);
|
|
111858
|
+
this.emit("error", err);
|
|
111859
|
+
ctx.reject(err);
|
|
111860
|
+
}
|
|
111861
|
+
/**
|
|
111862
|
+
* Stop a single cohort early without affecting the rest of the runner. Drops
|
|
111863
|
+
* the cohort's state machine state; its `completion` promise rejects with a
|
|
111864
|
+
* stopped error.
|
|
111865
|
+
*/
|
|
111866
|
+
stopCohort(cohortId) {
|
|
111867
|
+
const ctx = this.#contexts.get(cohortId);
|
|
111868
|
+
if (!ctx || ctx.settled) return;
|
|
111869
|
+
ctx.settled = true;
|
|
111870
|
+
this.#disposeCohort(ctx);
|
|
111871
|
+
this.session.removeCohort(cohortId);
|
|
111872
|
+
ctx.reject(new AggregationServiceError(`Cohort ${cohortId} stopped.`, "COHORT_STOPPED", { cohortId }));
|
|
111668
111873
|
}
|
|
111669
111874
|
/**
|
|
111670
|
-
* Stop the runner
|
|
111671
|
-
* handlers so a restart or a new runner doesn't inherit
|
|
111875
|
+
* Stop the whole runner. Fails every outstanding cohort, then detaches the
|
|
111876
|
+
* shared transport handlers so a restart or a new runner doesn't inherit
|
|
111877
|
+
* stale dispatch. Safe to call repeatedly.
|
|
111672
111878
|
*/
|
|
111673
111879
|
stop() {
|
|
111674
111880
|
this.#stopped = true;
|
|
111675
|
-
this.#
|
|
111676
|
-
|
|
111881
|
+
for (const ctx of [...this.#contexts.values()]) {
|
|
111882
|
+
if (ctx.settled) continue;
|
|
111883
|
+
ctx.settled = true;
|
|
111884
|
+
this.#disposeCohort(ctx);
|
|
111885
|
+
this.session.removeCohort(ctx.cohortId);
|
|
111886
|
+
ctx.reject(new AggregationServiceError("Service runner stopped.", "RUNNER_STOPPED", { cohortId: ctx.cohortId }));
|
|
111887
|
+
}
|
|
111888
|
+
this.#contexts.clear();
|
|
111677
111889
|
this.#unregisterHandlers();
|
|
111678
|
-
if (this.#cohortId) this.session.removeCohort(this.#cohortId);
|
|
111679
111890
|
}
|
|
111680
111891
|
/** Message types this runner listens for on the transport. */
|
|
111681
111892
|
static #HANDLED_MESSAGE_TYPES = [
|
|
@@ -111686,7 +111897,10 @@ ${value2}`;
|
|
|
111686
111897
|
SIGNATURE_AUTHORIZATION
|
|
111687
111898
|
];
|
|
111688
111899
|
/**
|
|
111689
|
-
* Internal: handler registration with the transport. Idempotent.
|
|
111900
|
+
* Internal: handler registration with the transport. Idempotent. Handlers
|
|
111901
|
+
* are DID-scoped and cohort-agnostic — one registration serves every cohort
|
|
111902
|
+
* this runner drives; demux to the right {@link RunContext} happens in each
|
|
111903
|
+
* handler via the inbound message's cohortId.
|
|
111690
111904
|
*/
|
|
111691
111905
|
#registerHandlers() {
|
|
111692
111906
|
if (this.#handlersRegistered) return;
|
|
@@ -111707,23 +111921,25 @@ ${value2}`;
|
|
|
111707
111921
|
}
|
|
111708
111922
|
/**
|
|
111709
111923
|
* Internal: message handlers for each protocol step. Each handler:
|
|
111710
|
-
* 1)
|
|
111711
|
-
* 2)
|
|
111712
|
-
* 3)
|
|
111924
|
+
* 1) resolves the cohort the message belongs to (by cohortId); ignores it if unknown
|
|
111925
|
+
* 2) feeds the message into the state machine via session.receive()
|
|
111926
|
+
* 3) emits a high-level event (carrying cohortId) for external observers
|
|
111927
|
+
* 4) checks if the new state triggers any automatic next steps, and if so:
|
|
111713
111928
|
* a) calls the appropriate decision callback(s)
|
|
111714
111929
|
* b) sends any resulting messages from the state machine
|
|
111930
|
+
* Errors fail only the owning cohort. A stopped runner ignores messages.
|
|
111715
111931
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111716
111932
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111717
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111718
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111719
111933
|
*/
|
|
111720
111934
|
async #handleOptIn(msg) {
|
|
111721
111935
|
if (this.#stopped) return;
|
|
111936
|
+
const ctx = this.#contextFor(msg);
|
|
111937
|
+
if (!ctx) return;
|
|
111722
111938
|
try {
|
|
111723
111939
|
this.session.receive(msg);
|
|
111724
|
-
this.#drainRejections();
|
|
111725
|
-
this.#onPhaseMaybeChanged();
|
|
111726
|
-
const optIn = this.session.pendingOptIns(
|
|
111940
|
+
this.#drainRejections(ctx);
|
|
111941
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111942
|
+
const optIn = this.session.pendingOptIns(ctx.cohortId).get(msg.from);
|
|
111727
111943
|
if (!optIn) return;
|
|
111728
111944
|
this.emit("opt-in-received", optIn);
|
|
111729
111945
|
if (optIn.communicationPk) {
|
|
@@ -111731,29 +111947,34 @@ ${value2}`;
|
|
|
111731
111947
|
}
|
|
111732
111948
|
const decision = await this.#onOptInReceived(optIn);
|
|
111733
111949
|
if (!decision.accepted) return;
|
|
111734
|
-
|
|
111735
|
-
|
|
111736
|
-
|
|
111737
|
-
|
|
111738
|
-
|
|
111950
|
+
const maxParticipants = ctx.config.maxParticipants;
|
|
111951
|
+
const cohortNow = this.session.getCohort(ctx.cohortId);
|
|
111952
|
+
if (maxParticipants !== void 0 && cohortNow && cohortNow.participants.length >= maxParticipants) {
|
|
111953
|
+
return;
|
|
111954
|
+
}
|
|
111955
|
+
await this.#sendAll(this.session.acceptParticipant(ctx.cohortId, msg.from));
|
|
111956
|
+
this.emit("participant-accepted", { cohortId: ctx.cohortId, participantDid: msg.from });
|
|
111957
|
+
const cohort = this.session.getCohort(ctx.cohortId);
|
|
111958
|
+
if (cohort.participants.length >= ctx.config.minParticipants && !ctx.finalizing) {
|
|
111959
|
+
ctx.finalizing = true;
|
|
111739
111960
|
const finalizeDecision = await this.#onReadyToFinalize({
|
|
111740
111961
|
acceptedCount: cohort.participants.length,
|
|
111741
|
-
minRequired:
|
|
111962
|
+
minRequired: ctx.config.minParticipants
|
|
111742
111963
|
});
|
|
111743
111964
|
if (!finalizeDecision.finalize) {
|
|
111744
|
-
|
|
111965
|
+
ctx.finalizing = false;
|
|
111745
111966
|
return;
|
|
111746
111967
|
}
|
|
111747
|
-
const readyMsgs = this.session.finalizeKeygen(
|
|
111748
|
-
this.#stopAdvertRepeating();
|
|
111968
|
+
const readyMsgs = this.session.finalizeKeygen(ctx.cohortId);
|
|
111969
|
+
this.#stopAdvertRepeating(ctx);
|
|
111749
111970
|
this.emit("keygen-complete", {
|
|
111750
|
-
cohortId:
|
|
111971
|
+
cohortId: ctx.cohortId,
|
|
111751
111972
|
beaconAddress: cohort.beaconAddress
|
|
111752
111973
|
});
|
|
111753
111974
|
await this.#sendAll(readyMsgs);
|
|
111754
111975
|
}
|
|
111755
111976
|
} catch (err) {
|
|
111756
|
-
this.#
|
|
111977
|
+
this.#failCohort(ctx, err);
|
|
111757
111978
|
}
|
|
111758
111979
|
}
|
|
111759
111980
|
/**
|
|
@@ -111761,23 +111982,23 @@ ${value2}`;
|
|
|
111761
111982
|
* and distributes the data for validation.
|
|
111762
111983
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111763
111984
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111764
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111765
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111766
111985
|
*/
|
|
111767
111986
|
async #handleSubmitUpdate(msg) {
|
|
111768
111987
|
if (this.#stopped) return;
|
|
111988
|
+
const ctx = this.#contextFor(msg);
|
|
111989
|
+
if (!ctx) return;
|
|
111769
111990
|
try {
|
|
111770
111991
|
this.session.receive(msg);
|
|
111771
|
-
this.#drainRejections();
|
|
111772
|
-
this.#onPhaseMaybeChanged();
|
|
111773
|
-
this.emit("update-received", { participantDid: msg.from });
|
|
111774
|
-
if (this.session.getCohortPhase(
|
|
111775
|
-
const distributeMsgs = this.session.buildAndDistribute(
|
|
111776
|
-
this.emit("data-distributed", { cohortId:
|
|
111992
|
+
this.#drainRejections(ctx);
|
|
111993
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111994
|
+
this.emit("update-received", { cohortId: ctx.cohortId, participantDid: msg.from });
|
|
111995
|
+
if (this.session.getCohortPhase(ctx.cohortId) === "UpdatesCollected" /* UpdatesCollected */) {
|
|
111996
|
+
const distributeMsgs = this.session.buildAndDistribute(ctx.cohortId);
|
|
111997
|
+
this.emit("data-distributed", { cohortId: ctx.cohortId });
|
|
111777
111998
|
await this.#sendAll(distributeMsgs);
|
|
111778
111999
|
}
|
|
111779
112000
|
} catch (err) {
|
|
111780
|
-
this.#
|
|
112001
|
+
this.#failCohort(ctx, err);
|
|
111781
112002
|
}
|
|
111782
112003
|
}
|
|
111783
112004
|
/**
|
|
@@ -111785,111 +112006,95 @@ ${value2}`;
|
|
|
111785
112006
|
* automatically requests tx data and starts signing.
|
|
111786
112007
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111787
112008
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111788
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111789
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111790
112009
|
*/
|
|
111791
112010
|
async #handleValidationAck(msg) {
|
|
111792
112011
|
if (this.#stopped) return;
|
|
112012
|
+
const ctx = this.#contextFor(msg);
|
|
112013
|
+
if (!ctx) return;
|
|
111793
112014
|
try {
|
|
111794
112015
|
this.session.receive(msg);
|
|
111795
|
-
this.#drainRejections();
|
|
111796
|
-
this.#onPhaseMaybeChanged();
|
|
112016
|
+
this.#drainRejections(ctx);
|
|
112017
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111797
112018
|
const approved = !!msg.body?.approved;
|
|
111798
|
-
this.emit("validation-received", { participantDid: msg.from, approved });
|
|
111799
|
-
const phase = this.session.getCohortPhase(
|
|
112019
|
+
this.emit("validation-received", { cohortId: ctx.cohortId, participantDid: msg.from, approved });
|
|
112020
|
+
const phase = this.session.getCohortPhase(ctx.cohortId);
|
|
111800
112021
|
if (phase === "Failed" /* Failed */) {
|
|
111801
112022
|
const reason = `Validation rejected by participant ${msg.from}`;
|
|
111802
|
-
this.emit("cohort-failed", { cohortId:
|
|
111803
|
-
this.#
|
|
112023
|
+
this.emit("cohort-failed", { cohortId: ctx.cohortId, reason });
|
|
112024
|
+
this.#failCohort(ctx, new Error(reason));
|
|
111804
112025
|
return;
|
|
111805
112026
|
}
|
|
111806
112027
|
if (phase === "Validated" /* Validated */) {
|
|
111807
|
-
const cohort = this.session.getCohort(
|
|
112028
|
+
const cohort = this.session.getCohort(ctx.cohortId);
|
|
111808
112029
|
const txData = await this.#onProvideTxData({
|
|
111809
|
-
cohortId:
|
|
112030
|
+
cohortId: ctx.cohortId,
|
|
111810
112031
|
beaconAddress: cohort.beaconAddress,
|
|
111811
112032
|
signalBytes: cohort.signalBytes
|
|
111812
112033
|
});
|
|
111813
|
-
const authMsgs = this.session.startSigning(
|
|
111814
|
-
const sessionId = this.session.getSigningSessionId(
|
|
111815
|
-
this.emit("signing-started", { sessionId });
|
|
112034
|
+
const authMsgs = this.session.startSigning(ctx.cohortId, txData);
|
|
112035
|
+
const sessionId = this.session.getSigningSessionId(ctx.cohortId) ?? "";
|
|
112036
|
+
this.emit("signing-started", { cohortId: ctx.cohortId, sessionId });
|
|
111816
112037
|
await this.#sendAll(authMsgs);
|
|
111817
112038
|
}
|
|
111818
112039
|
} catch (err) {
|
|
111819
|
-
this.#
|
|
112040
|
+
this.#failCohort(ctx, err);
|
|
111820
112041
|
}
|
|
111821
112042
|
}
|
|
111822
112043
|
/**
|
|
111823
|
-
* Handler for receiving nonce contributions
|
|
111824
|
-
*
|
|
112044
|
+
* Handler for receiving nonce contributions. When all nonces are received, sends the aggregated
|
|
112045
|
+
* nonce back to the cohort.
|
|
111825
112046
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111826
112047
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111827
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111828
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111829
112048
|
*/
|
|
111830
112049
|
async #handleNonceContribution(msg) {
|
|
111831
112050
|
if (this.#stopped) return;
|
|
112051
|
+
const ctx = this.#contextFor(msg);
|
|
112052
|
+
if (!ctx) return;
|
|
111832
112053
|
try {
|
|
111833
112054
|
this.session.receive(msg);
|
|
111834
|
-
this.#drainRejections();
|
|
111835
|
-
this.#onPhaseMaybeChanged();
|
|
111836
|
-
this.emit("nonce-received", { participantDid: msg.from });
|
|
111837
|
-
if (this.session.getCohortPhase(
|
|
111838
|
-
await this.#sendAll(this.session.sendAggregatedNonce(
|
|
112055
|
+
this.#drainRejections(ctx);
|
|
112056
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
112057
|
+
this.emit("nonce-received", { cohortId: ctx.cohortId, participantDid: msg.from });
|
|
112058
|
+
if (this.session.getCohortPhase(ctx.cohortId) === "NoncesCollected" /* NoncesCollected */) {
|
|
112059
|
+
await this.#sendAll(this.session.sendAggregatedNonce(ctx.cohortId));
|
|
111839
112060
|
}
|
|
111840
112061
|
} catch (err) {
|
|
111841
|
-
this.#
|
|
112062
|
+
this.#failCohort(ctx, err);
|
|
111842
112063
|
}
|
|
111843
112064
|
}
|
|
111844
112065
|
/**
|
|
111845
112066
|
* Handler for receiving signature authorizations. When all partial signatures are received, the
|
|
111846
|
-
* session automatically completes
|
|
112067
|
+
* session automatically completes; the final result is emitted and the cohort's completion
|
|
112068
|
+
* promise resolves.
|
|
111847
112069
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111848
112070
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111849
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111850
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111851
112071
|
*/
|
|
111852
112072
|
async #handleSignatureAuthorization(msg) {
|
|
111853
112073
|
if (this.#stopped) return;
|
|
112074
|
+
const ctx = this.#contextFor(msg);
|
|
112075
|
+
if (!ctx) return;
|
|
111854
112076
|
try {
|
|
111855
112077
|
this.session.receive(msg);
|
|
111856
|
-
this.#drainRejections();
|
|
111857
|
-
this.#onPhaseMaybeChanged();
|
|
111858
|
-
const result = this.session.getResult(
|
|
112078
|
+
this.#drainRejections(ctx);
|
|
112079
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
112080
|
+
const result = this.session.getResult(ctx.cohortId);
|
|
111859
112081
|
if (result) {
|
|
111860
|
-
this.#
|
|
111861
|
-
this.#unregisterHandlers();
|
|
111862
|
-
this.emit("signing-complete", result);
|
|
111863
|
-
this.#resolveRun?.(result);
|
|
112082
|
+
this.#completeCohort(ctx, result);
|
|
111864
112083
|
}
|
|
111865
112084
|
} catch (err) {
|
|
111866
|
-
this.#
|
|
112085
|
+
this.#failCohort(ctx, err);
|
|
111867
112086
|
}
|
|
111868
112087
|
}
|
|
111869
112088
|
/**
|
|
111870
112089
|
* Internal: helper to send all messages sequentially. Catches and propagates errors.
|
|
111871
112090
|
* @param {BaseMessage[]} msgs - The messages to send.
|
|
111872
112091
|
* @returns {Promise<void>} Resolves when all messages have been sent.
|
|
111873
|
-
* @throws {Error} If sending any message fails, the error is emitted and the run promise is
|
|
111874
|
-
* rejected.
|
|
111875
112092
|
*/
|
|
111876
112093
|
async #sendAll(msgs) {
|
|
111877
112094
|
for (const m2 of msgs) {
|
|
111878
112095
|
await this.#transport.sendMessage(m2, this.#did, m2.to);
|
|
111879
112096
|
}
|
|
111880
112097
|
}
|
|
111881
|
-
/**
|
|
111882
|
-
* Internal: helper to handle errors. Emits an 'error' event and rejects the run promise.
|
|
111883
|
-
* @param {Error} err - The error to handle.
|
|
111884
|
-
*/
|
|
111885
|
-
#fail(err) {
|
|
111886
|
-
this.#stopAdvertRepeating();
|
|
111887
|
-
this.#clearTimers();
|
|
111888
|
-
this.#unregisterHandlers();
|
|
111889
|
-
if (this.#cohortId) this.session.removeCohort(this.#cohortId);
|
|
111890
|
-
this.emit("error", err);
|
|
111891
|
-
this.#rejectRun?.(err);
|
|
111892
|
-
}
|
|
111893
112098
|
};
|
|
111894
112099
|
|
|
111895
112100
|
// src/core/aggregation/runner/participant-runner.ts
|
|
@@ -111950,7 +112155,8 @@ ${value2}`;
|
|
|
111950
112155
|
}
|
|
111951
112156
|
/**
|
|
111952
112157
|
* Single-shot helper: start, join the first cohort that passes `shouldJoin`,
|
|
111953
|
-
* drive it to completion, and resolve. Convenient for tests and demos.
|
|
112158
|
+
* drive it to completion, and resolve. Convenient for tests and demos. The
|
|
112159
|
+
* single-cohort special case of {@link joinMatching} (count = 1).
|
|
111954
112160
|
*/
|
|
111955
112161
|
static async joinFirst(options2) {
|
|
111956
112162
|
return new Promise((resolve, reject) => {
|
|
@@ -111963,6 +112169,37 @@ ${value2}`;
|
|
|
111963
112169
|
runner.start().catch(reject);
|
|
111964
112170
|
});
|
|
111965
112171
|
}
|
|
112172
|
+
/**
|
|
112173
|
+
* Multi-cohort helper: start, join EVERY cohort whose advert passes
|
|
112174
|
+
* `shouldJoin`, drive each to completion in parallel, and resolve once
|
|
112175
|
+
* `count` cohorts have completed (the runner stops at that point). The
|
|
112176
|
+
* N-cohort generalization of {@link joinFirst}, for a participant that joins
|
|
112177
|
+
* several cohorts advertised by one service.
|
|
112178
|
+
*
|
|
112179
|
+
* For an open-ended, long-lived subscriber (no fixed count), construct an
|
|
112180
|
+
* {@link AggregationParticipantRunner} directly, set `shouldJoin`, call
|
|
112181
|
+
* `start()`, and listen for `cohort-complete` — the runner already drives
|
|
112182
|
+
* any number of cohorts concurrently.
|
|
112183
|
+
*
|
|
112184
|
+
* @param options Participant runner options (set `shouldJoin` to select cohorts).
|
|
112185
|
+
* @param count Number of completed cohorts to collect before resolving.
|
|
112186
|
+
* @returns The {@link CohortCompleteInfo} for each completed cohort, in completion order.
|
|
112187
|
+
*/
|
|
112188
|
+
static async joinMatching(options2, count) {
|
|
112189
|
+
return new Promise((resolve, reject) => {
|
|
112190
|
+
const runner = new _AggregationParticipantRunner(options2);
|
|
112191
|
+
const completed = [];
|
|
112192
|
+
runner.on("cohort-complete", (info) => {
|
|
112193
|
+
completed.push(info);
|
|
112194
|
+
if (completed.length >= count) {
|
|
112195
|
+
runner.stop();
|
|
112196
|
+
resolve(completed);
|
|
112197
|
+
}
|
|
112198
|
+
});
|
|
112199
|
+
runner.on("error", reject);
|
|
112200
|
+
runner.start().catch(reject);
|
|
112201
|
+
});
|
|
112202
|
+
}
|
|
111966
112203
|
/**
|
|
111967
112204
|
* Internal: handler registration with the transport. Idempotent and safe to call multiple times,
|
|
111968
112205
|
* but only registers handlers once.
|
|
@@ -112111,7 +112348,7 @@ ${value2}`;
|
|
|
112111
112348
|
if (this.session.getCohortPhase(cohortId) === "Complete" /* Complete */) {
|
|
112112
112349
|
const info = this.session.joinedCohorts.get(cohortId);
|
|
112113
112350
|
if (info) {
|
|
112114
|
-
const validation = this.session.
|
|
112351
|
+
const validation = this.session.getValidation(cohortId);
|
|
112115
112352
|
this.emit("cohort-complete", {
|
|
112116
112353
|
cohortId,
|
|
112117
112354
|
beaconAddress: info.beaconAddress,
|