@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.mjs
CHANGED
|
@@ -103245,7 +103245,7 @@ var AggregationCohort = class {
|
|
|
103245
103245
|
validationRejections = /* @__PURE__ */ new Set();
|
|
103246
103246
|
constructor({ id, minParticipants, serviceDid, network, beaconType }) {
|
|
103247
103247
|
this.id = id || crypto.randomUUID();
|
|
103248
|
-
this.minParticipants = minParticipants
|
|
103248
|
+
this.minParticipants = minParticipants ?? 2;
|
|
103249
103249
|
this.serviceDid = serviceDid || "";
|
|
103250
103250
|
this.network = network;
|
|
103251
103251
|
this.beaconType = beaconType || "CASBeacon";
|
|
@@ -103411,6 +103411,57 @@ var AggregationCohort = class {
|
|
|
103411
103411
|
}
|
|
103412
103412
|
};
|
|
103413
103413
|
|
|
103414
|
+
// src/core/aggregation/conditions.ts
|
|
103415
|
+
init_shim();
|
|
103416
|
+
var KNOWN_BEACON_TYPES = ["CASBeacon", "SMTBeacon"];
|
|
103417
|
+
function checkPair(problems, label, min, max) {
|
|
103418
|
+
if (min !== void 0 && (!Number.isInteger(min) || min < 0)) {
|
|
103419
|
+
problems.push(`min${label} must be an integer >= 0`);
|
|
103420
|
+
}
|
|
103421
|
+
if (max !== void 0 && (!Number.isInteger(max) || max < 0)) {
|
|
103422
|
+
problems.push(`max${label} must be an integer >= 0`);
|
|
103423
|
+
}
|
|
103424
|
+
if (min !== void 0 && max !== void 0 && Number.isInteger(min) && Number.isInteger(max) && max < min) {
|
|
103425
|
+
problems.push(`max${label} must be >= min${label}`);
|
|
103426
|
+
}
|
|
103427
|
+
}
|
|
103428
|
+
function checkCost(problems, label, cost) {
|
|
103429
|
+
if (cost === void 0) return;
|
|
103430
|
+
if (typeof cost.amount !== "number" || !Number.isFinite(cost.amount) || cost.amount < 0) {
|
|
103431
|
+
problems.push(`${label}.amount must be a finite number >= 0`);
|
|
103432
|
+
}
|
|
103433
|
+
if (typeof cost.unit !== "string" || cost.unit.length === 0) {
|
|
103434
|
+
problems.push(`${label}.unit must be a non-empty string`);
|
|
103435
|
+
}
|
|
103436
|
+
if (cost.basis !== void 0 && cost.basis !== "per-did" && cost.basis !== "per-participant") {
|
|
103437
|
+
problems.push(`${label}.basis must be 'per-did' or 'per-participant'`);
|
|
103438
|
+
}
|
|
103439
|
+
}
|
|
103440
|
+
function validateCohortConditions(c2) {
|
|
103441
|
+
const problems = [];
|
|
103442
|
+
if (!KNOWN_BEACON_TYPES.includes(c2.beaconType)) {
|
|
103443
|
+
problems.push(`beaconType must be one of ${KNOWN_BEACON_TYPES.join(", ")}`);
|
|
103444
|
+
}
|
|
103445
|
+
if (!Number.isInteger(c2.minParticipants) || c2.minParticipants < 1) {
|
|
103446
|
+
problems.push("minParticipants must be an integer >= 1");
|
|
103447
|
+
}
|
|
103448
|
+
if (c2.maxParticipants !== void 0) {
|
|
103449
|
+
if (!Number.isInteger(c2.maxParticipants) || c2.maxParticipants < 1) {
|
|
103450
|
+
problems.push("maxParticipants must be an integer >= 1");
|
|
103451
|
+
} else if (Number.isInteger(c2.minParticipants) && c2.maxParticipants < c2.minParticipants) {
|
|
103452
|
+
problems.push("maxParticipants must be >= minParticipants");
|
|
103453
|
+
}
|
|
103454
|
+
}
|
|
103455
|
+
checkPair(problems, "DidsPerParticipant", c2.minDidsPerParticipant, c2.maxDidsPerParticipant);
|
|
103456
|
+
checkPair(problems, "SecondsBetweenAnnouncements", c2.minSecondsBetweenAnnouncements, c2.maxSecondsBetweenAnnouncements);
|
|
103457
|
+
if (c2.pendingUpdateTrigger !== void 0 && (!Number.isInteger(c2.pendingUpdateTrigger) || c2.pendingUpdateTrigger < 1)) {
|
|
103458
|
+
problems.push("pendingUpdateTrigger must be an integer >= 1");
|
|
103459
|
+
}
|
|
103460
|
+
checkCost(problems, "costOfEnrollment", c2.costOfEnrollment);
|
|
103461
|
+
checkCost(problems, "costPerAnnouncement", c2.costPerAnnouncement);
|
|
103462
|
+
return problems;
|
|
103463
|
+
}
|
|
103464
|
+
|
|
103414
103465
|
// src/core/aggregation/messages/base.ts
|
|
103415
103466
|
init_shim();
|
|
103416
103467
|
var AGGREGATION_WIRE_VERSION = 1;
|
|
@@ -103845,6 +103896,14 @@ var AggregationService = class {
|
|
|
103845
103896
|
* Cohort starts in `Created` phase — call `advertise()` to broadcast.
|
|
103846
103897
|
*/
|
|
103847
103898
|
createCohort(config) {
|
|
103899
|
+
const problems = validateCohortConditions(config);
|
|
103900
|
+
if (problems.length > 0) {
|
|
103901
|
+
throw new AggregationServiceError(
|
|
103902
|
+
`Invalid cohort conditions: ${problems.join("; ")}`,
|
|
103903
|
+
"INVALID_COHORT_CONDITIONS",
|
|
103904
|
+
{ problems }
|
|
103905
|
+
);
|
|
103906
|
+
}
|
|
103848
103907
|
const cohort = new AggregationCohort({
|
|
103849
103908
|
serviceDid: this.did,
|
|
103850
103909
|
minParticipants: config.minParticipants,
|
|
@@ -103877,13 +103936,13 @@ var AggregationService = class {
|
|
|
103877
103936
|
{ cohortId, phase: state.phase }
|
|
103878
103937
|
);
|
|
103879
103938
|
}
|
|
103939
|
+
const { network, ...conditions } = state.config;
|
|
103880
103940
|
const message2 = createCohortAdvertMessage({
|
|
103881
103941
|
from: this.did,
|
|
103882
103942
|
cohortId,
|
|
103883
|
-
|
|
103884
|
-
|
|
103885
|
-
|
|
103886
|
-
communicationPk: this.publicKey.compressed
|
|
103943
|
+
network,
|
|
103944
|
+
communicationPk: this.publicKey.compressed,
|
|
103945
|
+
...conditions
|
|
103887
103946
|
});
|
|
103888
103947
|
state.phase = "Advertised" /* Advertised */;
|
|
103889
103948
|
return [message2];
|
|
@@ -103942,6 +104001,14 @@ var AggregationService = class {
|
|
|
103942
104001
|
{ cohortId, participantDid }
|
|
103943
104002
|
);
|
|
103944
104003
|
}
|
|
104004
|
+
const maxParticipants = state.config.maxParticipants;
|
|
104005
|
+
if (maxParticipants !== void 0 && state.acceptedParticipants.size >= maxParticipants) {
|
|
104006
|
+
throw new AggregationServiceError(
|
|
104007
|
+
`Cohort ${cohortId} is full: ${maxParticipants} participants already accepted.`,
|
|
104008
|
+
"COHORT_FULL",
|
|
104009
|
+
{ cohortId, maxParticipants }
|
|
104010
|
+
);
|
|
104011
|
+
}
|
|
103945
104012
|
state.acceptedParticipants.add(participantDid);
|
|
103946
104013
|
state.cohort.participants.push(participantDid);
|
|
103947
104014
|
state.cohort.participantKeys.set(participantDid, optIn.participantPk);
|
|
@@ -103975,6 +104042,14 @@ var AggregationService = class {
|
|
|
103975
104042
|
{ cohortId }
|
|
103976
104043
|
);
|
|
103977
104044
|
}
|
|
104045
|
+
const maxParticipants = state.config.maxParticipants;
|
|
104046
|
+
if (maxParticipants !== void 0 && state.acceptedParticipants.size > maxParticipants) {
|
|
104047
|
+
throw new AggregationServiceError(
|
|
104048
|
+
`Cohort ${cohortId} has ${state.acceptedParticipants.size} accepted participants, exceeds max ${maxParticipants}.`,
|
|
104049
|
+
"TOO_MANY_PARTICIPANTS",
|
|
104050
|
+
{ cohortId, maxParticipants }
|
|
104051
|
+
);
|
|
104052
|
+
}
|
|
103978
104053
|
const beaconAddress = state.cohort.computeBeaconAddress();
|
|
103979
104054
|
state.phase = "CohortSet" /* CohortSet */;
|
|
103980
104055
|
const messages2 = [];
|
|
@@ -104292,6 +104367,59 @@ var AggregationService = class {
|
|
|
104292
104367
|
// src/core/aggregation/participant.ts
|
|
104293
104368
|
init_shim();
|
|
104294
104369
|
init_utils();
|
|
104370
|
+
|
|
104371
|
+
// src/core/aggregation/messages/bodies.ts
|
|
104372
|
+
init_shim();
|
|
104373
|
+
var hasStr = (b, k) => !!b && typeof b[k] === "string";
|
|
104374
|
+
var hasIntMin = (b, k, min) => {
|
|
104375
|
+
const v = b ? b[k] : void 0;
|
|
104376
|
+
return typeof v === "number" && Number.isInteger(v) && v >= min;
|
|
104377
|
+
};
|
|
104378
|
+
var optIntMin = (b, k, min) => {
|
|
104379
|
+
const v = b ? b[k] : void 0;
|
|
104380
|
+
return v === void 0 || typeof v === "number" && Number.isInteger(v) && v >= min;
|
|
104381
|
+
};
|
|
104382
|
+
var hasBool = (b, k) => !!b && typeof b[k] === "boolean";
|
|
104383
|
+
var hasBytes = (b, k) => !!b && b[k] instanceof Uint8Array;
|
|
104384
|
+
var hasBytesArray = (b, k) => {
|
|
104385
|
+
const v = b ? b[k] : void 0;
|
|
104386
|
+
return Array.isArray(v) && v.every((x) => x instanceof Uint8Array);
|
|
104387
|
+
};
|
|
104388
|
+
function isCohortAdvertMessage(m2) {
|
|
104389
|
+
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");
|
|
104390
|
+
}
|
|
104391
|
+
function isCohortOptInMessage(m2) {
|
|
104392
|
+
return m2.type === COHORT_OPT_IN && hasStr(m2.body, "cohortId") && hasBytes(m2.body, "participantPk") && hasBytes(m2.body, "communicationPk");
|
|
104393
|
+
}
|
|
104394
|
+
function isCohortOptInAcceptMessage(m2) {
|
|
104395
|
+
return m2.type === COHORT_OPT_IN_ACCEPT && hasStr(m2.body, "cohortId");
|
|
104396
|
+
}
|
|
104397
|
+
function isCohortReadyMessage(m2) {
|
|
104398
|
+
return m2.type === COHORT_READY && hasStr(m2.body, "cohortId") && hasStr(m2.body, "beaconAddress") && hasBytesArray(m2.body, "cohortKeys");
|
|
104399
|
+
}
|
|
104400
|
+
function isSubmitUpdateMessage(m2) {
|
|
104401
|
+
return m2.type === SUBMIT_UPDATE && hasStr(m2.body, "cohortId") && !!m2.body && typeof m2.body.signedUpdate === "object";
|
|
104402
|
+
}
|
|
104403
|
+
function isDistributeAggregatedDataMessage(m2) {
|
|
104404
|
+
return m2.type === DISTRIBUTE_AGGREGATED_DATA && hasStr(m2.body, "cohortId") && hasStr(m2.body, "beaconType") && hasStr(m2.body, "signalBytesHex");
|
|
104405
|
+
}
|
|
104406
|
+
function isValidationAckMessage(m2) {
|
|
104407
|
+
return m2.type === VALIDATION_ACK && hasStr(m2.body, "cohortId") && hasBool(m2.body, "approved");
|
|
104408
|
+
}
|
|
104409
|
+
function isAuthorizationRequestMessage(m2) {
|
|
104410
|
+
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");
|
|
104411
|
+
}
|
|
104412
|
+
function isNonceContributionMessage(m2) {
|
|
104413
|
+
return m2.type === NONCE_CONTRIBUTION && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "nonceContribution");
|
|
104414
|
+
}
|
|
104415
|
+
function isAggregatedNonceMessage(m2) {
|
|
104416
|
+
return m2.type === AGGREGATED_NONCE && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "aggregatedNonce");
|
|
104417
|
+
}
|
|
104418
|
+
function isSignatureAuthorizationMessage(m2) {
|
|
104419
|
+
return m2.type === SIGNATURE_AUTHORIZATION && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "partialSignature");
|
|
104420
|
+
}
|
|
104421
|
+
|
|
104422
|
+
// src/core/aggregation/participant.ts
|
|
104295
104423
|
var AggregationParticipant = class {
|
|
104296
104424
|
did;
|
|
104297
104425
|
/** MuSig2 signing capability. The raw secret never lives as a field here. */
|
|
@@ -104349,16 +104477,15 @@ var AggregationParticipant = class {
|
|
|
104349
104477
|
return map3;
|
|
104350
104478
|
}
|
|
104351
104479
|
#handleCohortAdvert(message2) {
|
|
104352
|
-
|
|
104353
|
-
|
|
104480
|
+
if (!isCohortAdvertMessage(message2)) return;
|
|
104481
|
+
const { cohortId, network, communicationPk, ...conditions } = message2.body;
|
|
104354
104482
|
if (this.#cohortStates.has(cohortId)) return;
|
|
104355
104483
|
const advert = {
|
|
104356
104484
|
cohortId,
|
|
104357
104485
|
serviceDid: message2.from,
|
|
104358
|
-
|
|
104359
|
-
|
|
104360
|
-
|
|
104361
|
-
serviceCommunicationPk: message2.body?.communicationPk ?? new Uint8Array()
|
|
104486
|
+
network,
|
|
104487
|
+
serviceCommunicationPk: communicationPk,
|
|
104488
|
+
...conditions
|
|
104362
104489
|
};
|
|
104363
104490
|
this.#cohortStates.set(cohortId, {
|
|
104364
104491
|
phase: "Discovered" /* Discovered */,
|
|
@@ -104383,7 +104510,7 @@ var AggregationParticipant = class {
|
|
|
104383
104510
|
const cohort = new AggregationCohort({
|
|
104384
104511
|
id: cohortId,
|
|
104385
104512
|
serviceDid: state.serviceDid,
|
|
104386
|
-
minParticipants: state.advert.
|
|
104513
|
+
minParticipants: state.advert.minParticipants,
|
|
104387
104514
|
network: state.advert.network,
|
|
104388
104515
|
beaconType: state.advert.beaconType
|
|
104389
104516
|
});
|
|
@@ -104462,6 +104589,17 @@ var AggregationParticipant = class {
|
|
|
104462
104589
|
}
|
|
104463
104590
|
return map3;
|
|
104464
104591
|
}
|
|
104592
|
+
/**
|
|
104593
|
+
* The validated aggregated data retained for a cohort, regardless of phase.
|
|
104594
|
+
* Unlike {@link pendingValidations} (which lists only cohorts still awaiting
|
|
104595
|
+
* the validate decision), this returns the stored validation — including the
|
|
104596
|
+
* participant's sidecar (the CAS Announcement map or its SMT inclusion proof)
|
|
104597
|
+
* — so it is still readable once the cohort reaches Complete. Returns
|
|
104598
|
+
* undefined before aggregated data has been received.
|
|
104599
|
+
*/
|
|
104600
|
+
getValidation(cohortId) {
|
|
104601
|
+
return this.#cohortStates.get(cohortId)?.validation;
|
|
104602
|
+
}
|
|
104465
104603
|
#handleDistributeAggregatedData(message2) {
|
|
104466
104604
|
const cohortId = message2.body?.cohortId;
|
|
104467
104605
|
if (!cohortId) return;
|
|
@@ -104706,50 +104844,6 @@ var SILENT_LOGGER = {
|
|
|
104706
104844
|
// src/core/aggregation/messages/index.ts
|
|
104707
104845
|
init_shim();
|
|
104708
104846
|
|
|
104709
|
-
// src/core/aggregation/messages/bodies.ts
|
|
104710
|
-
init_shim();
|
|
104711
|
-
var hasStr = (b, k) => !!b && typeof b[k] === "string";
|
|
104712
|
-
var hasNum = (b, k) => !!b && typeof b[k] === "number";
|
|
104713
|
-
var hasBool = (b, k) => !!b && typeof b[k] === "boolean";
|
|
104714
|
-
var hasBytes = (b, k) => !!b && b[k] instanceof Uint8Array;
|
|
104715
|
-
var hasBytesArray = (b, k) => {
|
|
104716
|
-
const v = b ? b[k] : void 0;
|
|
104717
|
-
return Array.isArray(v) && v.every((x) => x instanceof Uint8Array);
|
|
104718
|
-
};
|
|
104719
|
-
function isCohortAdvertMessage(m2) {
|
|
104720
|
-
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");
|
|
104721
|
-
}
|
|
104722
|
-
function isCohortOptInMessage(m2) {
|
|
104723
|
-
return m2.type === COHORT_OPT_IN && hasStr(m2.body, "cohortId") && hasBytes(m2.body, "participantPk") && hasBytes(m2.body, "communicationPk");
|
|
104724
|
-
}
|
|
104725
|
-
function isCohortOptInAcceptMessage(m2) {
|
|
104726
|
-
return m2.type === COHORT_OPT_IN_ACCEPT && hasStr(m2.body, "cohortId");
|
|
104727
|
-
}
|
|
104728
|
-
function isCohortReadyMessage(m2) {
|
|
104729
|
-
return m2.type === COHORT_READY && hasStr(m2.body, "cohortId") && hasStr(m2.body, "beaconAddress") && hasBytesArray(m2.body, "cohortKeys");
|
|
104730
|
-
}
|
|
104731
|
-
function isSubmitUpdateMessage(m2) {
|
|
104732
|
-
return m2.type === SUBMIT_UPDATE && hasStr(m2.body, "cohortId") && !!m2.body && typeof m2.body.signedUpdate === "object";
|
|
104733
|
-
}
|
|
104734
|
-
function isDistributeAggregatedDataMessage(m2) {
|
|
104735
|
-
return m2.type === DISTRIBUTE_AGGREGATED_DATA && hasStr(m2.body, "cohortId") && hasStr(m2.body, "beaconType") && hasStr(m2.body, "signalBytesHex");
|
|
104736
|
-
}
|
|
104737
|
-
function isValidationAckMessage(m2) {
|
|
104738
|
-
return m2.type === VALIDATION_ACK && hasStr(m2.body, "cohortId") && hasBool(m2.body, "approved");
|
|
104739
|
-
}
|
|
104740
|
-
function isAuthorizationRequestMessage(m2) {
|
|
104741
|
-
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");
|
|
104742
|
-
}
|
|
104743
|
-
function isNonceContributionMessage(m2) {
|
|
104744
|
-
return m2.type === NONCE_CONTRIBUTION && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "nonceContribution");
|
|
104745
|
-
}
|
|
104746
|
-
function isAggregatedNonceMessage(m2) {
|
|
104747
|
-
return m2.type === AGGREGATED_NONCE && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "aggregatedNonce");
|
|
104748
|
-
}
|
|
104749
|
-
function isSignatureAuthorizationMessage(m2) {
|
|
104750
|
-
return m2.type === SIGNATURE_AUTHORIZATION && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "partialSignature");
|
|
104751
|
-
}
|
|
104752
|
-
|
|
104753
104847
|
// src/core/aggregation/messages/guards.ts
|
|
104754
104848
|
init_shim();
|
|
104755
104849
|
var KEYGEN_VALUES = /* @__PURE__ */ new Set([
|
|
@@ -111382,35 +111476,22 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111382
111476
|
session;
|
|
111383
111477
|
#transport;
|
|
111384
111478
|
#did;
|
|
111385
|
-
#
|
|
111479
|
+
#defaultConfig;
|
|
111386
111480
|
#onOptInReceived;
|
|
111387
111481
|
#onReadyToFinalize;
|
|
111388
111482
|
#onProvideTxData;
|
|
111389
111483
|
#cohortTtlMs;
|
|
111390
111484
|
#phaseTimeoutMs;
|
|
111391
111485
|
#advertRepeatIntervalMs;
|
|
111392
|
-
|
|
111486
|
+
/** Per-cohort run state, keyed by cohortId. */
|
|
111487
|
+
#contexts = /* @__PURE__ */ new Map();
|
|
111393
111488
|
#handlersRegistered = false;
|
|
111394
111489
|
#stopped = false;
|
|
111395
|
-
/**
|
|
111396
|
-
* Guard against the async race where two concurrent #handleOptIn invocations
|
|
111397
|
-
* both pass the `participants.length >= minParticipants` check before either
|
|
111398
|
-
* mutates the cohort phase. Set synchronously before any `await` so subsequent
|
|
111399
|
-
* handlers observe it on their next resumption.
|
|
111400
|
-
*/
|
|
111401
|
-
#finalizing = false;
|
|
111402
|
-
#resolveRun;
|
|
111403
|
-
#rejectRun;
|
|
111404
|
-
#cohortTtlTimer;
|
|
111405
|
-
#phaseTimer;
|
|
111406
|
-
#lastObservedPhase;
|
|
111407
|
-
/** Stop handle for the repeating COHORT_ADVERT publish loop. */
|
|
111408
|
-
#stopAdvertRepeat;
|
|
111409
111490
|
constructor(options2) {
|
|
111410
111491
|
super();
|
|
111411
111492
|
this.#transport = options2.transport;
|
|
111412
111493
|
this.#did = options2.did;
|
|
111413
|
-
this.#
|
|
111494
|
+
this.#defaultConfig = options2.config;
|
|
111414
111495
|
this.#onOptInReceived = options2.onOptInReceived ?? (async () => ({ accepted: true }));
|
|
111415
111496
|
this.#onReadyToFinalize = options2.onReadyToFinalize ?? (async ({ acceptedCount, minRequired }) => ({
|
|
111416
111497
|
finalize: acceptedCount >= minRequired
|
|
@@ -111428,55 +111509,126 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111428
111509
|
maxUpdateSizeBytes: options2.maxUpdateSizeBytes
|
|
111429
111510
|
});
|
|
111430
111511
|
}
|
|
111512
|
+
/** Resolve the {@link RunContext} an inbound message belongs to, by cohortId. */
|
|
111513
|
+
#contextFor(msg) {
|
|
111514
|
+
const cohortId = msg.body?.cohortId;
|
|
111515
|
+
if (!cohortId) return void 0;
|
|
111516
|
+
return this.#contexts.get(cohortId);
|
|
111517
|
+
}
|
|
111431
111518
|
/**
|
|
111432
|
-
* Drain any silent rejections the state machine recorded
|
|
111433
|
-
* recent receive() and surface them as `message-rejected` events.
|
|
111434
|
-
* call even before a cohortId is assigned.
|
|
111519
|
+
* Drain any silent rejections the state machine recorded for a cohort during
|
|
111520
|
+
* the most recent receive() and surface them as `message-rejected` events.
|
|
111435
111521
|
*/
|
|
111436
|
-
#drainRejections() {
|
|
111437
|
-
|
|
111438
|
-
|
|
111439
|
-
this.emit("message-rejected", { cohortId: this.#cohortId, ...r2 });
|
|
111522
|
+
#drainRejections(ctx) {
|
|
111523
|
+
for (const r2 of this.session.drainRejections(ctx.cohortId)) {
|
|
111524
|
+
this.emit("message-rejected", { cohortId: ctx.cohortId, ...r2 });
|
|
111440
111525
|
}
|
|
111441
111526
|
}
|
|
111442
111527
|
/**
|
|
111443
|
-
*
|
|
111444
|
-
*
|
|
111528
|
+
* Advertise a new cohort and begin driving it to completion. Callable many
|
|
111529
|
+
* times on one runner; each cohort runs concurrently and independently.
|
|
111530
|
+
*
|
|
111531
|
+
* @param config Per-cohort conditions + network (see {@link CohortConfig}).
|
|
111532
|
+
* @returns The new cohort's id and a `completion` promise that resolves with
|
|
111533
|
+
* that cohort's {@link AggregationResult} (or rejects if it fails/stalls).
|
|
111534
|
+
* @throws If the runner has been stopped, or the config is invalid
|
|
111535
|
+
* (fail-fast via `createCohort`).
|
|
111536
|
+
*/
|
|
111537
|
+
advertiseCohort(config) {
|
|
111538
|
+
if (this.#stopped) {
|
|
111539
|
+
throw new AggregationServiceError("Cannot advertise on a stopped runner.", "RUNNER_STOPPED", {});
|
|
111540
|
+
}
|
|
111541
|
+
this.#registerHandlers();
|
|
111542
|
+
const cohortId = this.session.createCohort(config);
|
|
111543
|
+
let resolve;
|
|
111544
|
+
let reject;
|
|
111545
|
+
const completion = new Promise((res, rej) => {
|
|
111546
|
+
resolve = res;
|
|
111547
|
+
reject = rej;
|
|
111548
|
+
});
|
|
111549
|
+
const ctx = {
|
|
111550
|
+
cohortId,
|
|
111551
|
+
config,
|
|
111552
|
+
resolve,
|
|
111553
|
+
reject,
|
|
111554
|
+
completion,
|
|
111555
|
+
finalizing: false,
|
|
111556
|
+
settled: false
|
|
111557
|
+
};
|
|
111558
|
+
this.#contexts.set(cohortId, ctx);
|
|
111559
|
+
try {
|
|
111560
|
+
this.#startTimers(ctx);
|
|
111561
|
+
const advertMsgs = this.session.advertise(cohortId);
|
|
111562
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111563
|
+
this.emit("cohort-advertised", { cohortId });
|
|
111564
|
+
if (this.#advertRepeatIntervalMs > 0) {
|
|
111565
|
+
this.#startAdvertRepeat(ctx, advertMsgs);
|
|
111566
|
+
} else {
|
|
111567
|
+
this.#sendAll(advertMsgs).catch((err) => this.#failCohort(ctx, err));
|
|
111568
|
+
}
|
|
111569
|
+
} catch (err) {
|
|
111570
|
+
this.#failCohort(ctx, err);
|
|
111571
|
+
}
|
|
111572
|
+
return { cohortId, completion };
|
|
111573
|
+
}
|
|
111574
|
+
/**
|
|
111575
|
+
* Run a single cohort to completion using the `config` supplied in the
|
|
111576
|
+
* runner options. Thin convenience over {@link advertiseCohort} for the
|
|
111577
|
+
* single-cohort case (and the path {@link AggregationRunner.solo} rides).
|
|
111445
111578
|
*
|
|
111446
111579
|
* @returns {Promise<AggregationResult>} The final result with signature and signed tx.
|
|
111447
111580
|
*/
|
|
111448
111581
|
run() {
|
|
111449
|
-
|
|
111450
|
-
|
|
111451
|
-
|
|
111452
|
-
|
|
111453
|
-
|
|
111454
|
-
|
|
111455
|
-
|
|
111456
|
-
|
|
111457
|
-
|
|
111458
|
-
|
|
111459
|
-
|
|
111460
|
-
|
|
111461
|
-
|
|
111462
|
-
|
|
111463
|
-
|
|
111464
|
-
|
|
111465
|
-
|
|
111582
|
+
if (!this.#defaultConfig) {
|
|
111583
|
+
return Promise.reject(new AggregationServiceError(
|
|
111584
|
+
"run() requires `config` in the runner options; use advertiseCohort(config) to drive cohorts explicitly.",
|
|
111585
|
+
"MISSING_COHORT_CONFIG",
|
|
111586
|
+
{}
|
|
111587
|
+
));
|
|
111588
|
+
}
|
|
111589
|
+
try {
|
|
111590
|
+
return this.advertiseCohort(this.#defaultConfig).completion;
|
|
111591
|
+
} catch (err) {
|
|
111592
|
+
return Promise.reject(err);
|
|
111593
|
+
}
|
|
111594
|
+
}
|
|
111595
|
+
/**
|
|
111596
|
+
* Wait for every currently-outstanding cohort to settle and return the
|
|
111597
|
+
* successful results. Dynamic drain: cohorts advertised while this is pending
|
|
111598
|
+
* are included, and it resolves only once no cohorts remain. Failed cohorts
|
|
111599
|
+
* are surfaced via `error` / `cohort-failed` events and their rejected
|
|
111600
|
+
* `completion` promises; they are omitted from the returned array (this
|
|
111601
|
+
* method does not throw). Bound long-running cohorts with `cohortTtlMs` /
|
|
111602
|
+
* `phaseTimeoutMs` or this may never resolve.
|
|
111603
|
+
*
|
|
111604
|
+
* @returns {Promise<AggregationResult[]>} Results of the cohorts that completed.
|
|
111605
|
+
*/
|
|
111606
|
+
async runAll() {
|
|
111607
|
+
const collected = /* @__PURE__ */ new Map();
|
|
111608
|
+
const onComplete = (result) => {
|
|
111609
|
+
collected.set(result.cohortId, result);
|
|
111610
|
+
};
|
|
111611
|
+
this.on("signing-complete", onComplete);
|
|
111612
|
+
try {
|
|
111613
|
+
while (this.#contexts.size > 0) {
|
|
111614
|
+
await Promise.allSettled([...this.#contexts.values()].map((c2) => c2.completion));
|
|
111466
111615
|
}
|
|
111467
|
-
}
|
|
111616
|
+
} finally {
|
|
111617
|
+
this.off("signing-complete", onComplete);
|
|
111618
|
+
}
|
|
111619
|
+
return [...collected.values()];
|
|
111468
111620
|
}
|
|
111469
111621
|
/**
|
|
111470
|
-
* Begin publishing
|
|
111471
|
-
* until
|
|
111472
|
-
*
|
|
111622
|
+
* Begin publishing a cohort's advert immediately and on a repeating interval
|
|
111623
|
+
* until the cohort's advert loop is stopped. Each advert is broadcast (no
|
|
111624
|
+
* recipient) via the transport's `publishRepeating` primitive.
|
|
111473
111625
|
*/
|
|
111474
|
-
#startAdvertRepeat(advertMsgs) {
|
|
111626
|
+
#startAdvertRepeat(ctx, advertMsgs) {
|
|
111475
111627
|
const stops = [];
|
|
111476
111628
|
for (const msg of advertMsgs) {
|
|
111477
111629
|
stops.push(this.#transport.publishRepeating(msg, this.#did, this.#advertRepeatIntervalMs));
|
|
111478
111630
|
}
|
|
111479
|
-
|
|
111631
|
+
ctx.stopAdvertRepeat = () => {
|
|
111480
111632
|
for (const stop2 of stops) {
|
|
111481
111633
|
try {
|
|
111482
111634
|
stop2();
|
|
@@ -111485,61 +111637,118 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111485
111637
|
}
|
|
111486
111638
|
};
|
|
111487
111639
|
}
|
|
111488
|
-
/** Stop
|
|
111489
|
-
#stopAdvertRepeating() {
|
|
111490
|
-
if (!
|
|
111491
|
-
const stop2 =
|
|
111492
|
-
|
|
111640
|
+
/** Stop a cohort's advert republish loop. Idempotent. */
|
|
111641
|
+
#stopAdvertRepeating(ctx) {
|
|
111642
|
+
if (!ctx.stopAdvertRepeat) return;
|
|
111643
|
+
const stop2 = ctx.stopAdvertRepeat;
|
|
111644
|
+
ctx.stopAdvertRepeat = void 0;
|
|
111493
111645
|
stop2();
|
|
111494
111646
|
}
|
|
111495
|
-
/** Schedule cohort TTL + phase timeout
|
|
111496
|
-
#startTimers() {
|
|
111647
|
+
/** Schedule a cohort's TTL + phase timeout when it is advertised. */
|
|
111648
|
+
#startTimers(ctx) {
|
|
111497
111649
|
if (this.#cohortTtlMs !== void 0) {
|
|
111498
|
-
|
|
111499
|
-
const reason = `Cohort ${
|
|
111500
|
-
this.emit("cohort-failed", { cohortId:
|
|
111501
|
-
this.#
|
|
111650
|
+
ctx.cohortTtlTimer = setTimeout(() => {
|
|
111651
|
+
const reason = `Cohort ${ctx.cohortId} exceeded TTL of ${this.#cohortTtlMs}ms`;
|
|
111652
|
+
this.emit("cohort-failed", { cohortId: ctx.cohortId, reason });
|
|
111653
|
+
this.#failCohort(ctx, new Error(reason));
|
|
111502
111654
|
}, this.#cohortTtlMs);
|
|
111503
111655
|
}
|
|
111504
|
-
this.#resetPhaseTimer();
|
|
111656
|
+
this.#resetPhaseTimer(ctx);
|
|
111505
111657
|
}
|
|
111506
|
-
/** Reset
|
|
111507
|
-
#resetPhaseTimer() {
|
|
111508
|
-
if (
|
|
111509
|
-
|
|
111658
|
+
/** Reset a cohort's per-phase stall timer. Called when a phase transition is observed. */
|
|
111659
|
+
#resetPhaseTimer(ctx) {
|
|
111660
|
+
if (ctx.phaseTimer) clearTimeout(ctx.phaseTimer);
|
|
111661
|
+
ctx.phaseTimer = void 0;
|
|
111510
111662
|
if (this.#phaseTimeoutMs === void 0) return;
|
|
111511
|
-
|
|
111512
|
-
const reason = `Cohort ${
|
|
111513
|
-
this.emit("cohort-failed", { cohortId:
|
|
111514
|
-
this.#
|
|
111663
|
+
ctx.phaseTimer = setTimeout(() => {
|
|
111664
|
+
const reason = `Cohort ${ctx.cohortId} stalled in phase ${ctx.lastObservedPhase ?? "?"} for ${this.#phaseTimeoutMs}ms`;
|
|
111665
|
+
this.emit("cohort-failed", { cohortId: ctx.cohortId, reason });
|
|
111666
|
+
this.#failCohort(ctx, new Error(reason));
|
|
111515
111667
|
}, this.#phaseTimeoutMs);
|
|
111516
111668
|
}
|
|
111517
|
-
/** Detect a phase change since the last observation and reset
|
|
111518
|
-
#onPhaseMaybeChanged() {
|
|
111519
|
-
|
|
111520
|
-
|
|
111521
|
-
|
|
111522
|
-
this.#
|
|
111523
|
-
this.#resetPhaseTimer();
|
|
111669
|
+
/** Detect a phase change for a cohort since the last observation and reset its phase timer. */
|
|
111670
|
+
#onPhaseMaybeChanged(ctx) {
|
|
111671
|
+
const phase = this.session.getCohortPhase(ctx.cohortId);
|
|
111672
|
+
if (phase !== ctx.lastObservedPhase) {
|
|
111673
|
+
ctx.lastObservedPhase = phase;
|
|
111674
|
+
this.#resetPhaseTimer(ctx);
|
|
111524
111675
|
}
|
|
111525
111676
|
}
|
|
111526
|
-
/** Clear
|
|
111527
|
-
#clearTimers() {
|
|
111528
|
-
if (
|
|
111529
|
-
if (
|
|
111530
|
-
|
|
111531
|
-
|
|
111677
|
+
/** Clear a cohort's timers. Called on completion, stop, and failure. */
|
|
111678
|
+
#clearTimers(ctx) {
|
|
111679
|
+
if (ctx.cohortTtlTimer) clearTimeout(ctx.cohortTtlTimer);
|
|
111680
|
+
if (ctx.phaseTimer) clearTimeout(ctx.phaseTimer);
|
|
111681
|
+
ctx.cohortTtlTimer = void 0;
|
|
111682
|
+
ctx.phaseTimer = void 0;
|
|
111683
|
+
}
|
|
111684
|
+
/**
|
|
111685
|
+
* Reclaim one cohort's runner-layer bookkeeping: stop its advert loop, clear
|
|
111686
|
+
* its timers, and drop its {@link RunContext}. Does NOT touch sibling cohorts
|
|
111687
|
+
* and does NOT detach the shared transport handlers. Leaves the cohort in the
|
|
111688
|
+
* state machine; whether that cohort's `session` state is also removed is the
|
|
111689
|
+
* caller's choice (see {@link #completeCohort} vs {@link #failCohort}).
|
|
111690
|
+
*/
|
|
111691
|
+
#disposeCohort(ctx) {
|
|
111692
|
+
this.#stopAdvertRepeating(ctx);
|
|
111693
|
+
this.#clearTimers(ctx);
|
|
111694
|
+
this.#contexts.delete(ctx.cohortId);
|
|
111695
|
+
}
|
|
111696
|
+
/**
|
|
111697
|
+
* Settle one cohort successfully. Reclaims the runner context but leaves the
|
|
111698
|
+
* completed cohort in `session` so callers can read its beaconAddress / cohort
|
|
111699
|
+
* via `session.getCohort(result.cohortId)`; reclaim it with
|
|
111700
|
+
* `session.removeCohort(cohortId)` when done. Idempotent via `ctx.settled`.
|
|
111701
|
+
*/
|
|
111702
|
+
#completeCohort(ctx, result) {
|
|
111703
|
+
if (ctx.settled) return;
|
|
111704
|
+
ctx.settled = true;
|
|
111705
|
+
this.#disposeCohort(ctx);
|
|
111706
|
+
this.emit("signing-complete", result);
|
|
111707
|
+
ctx.resolve(result);
|
|
111708
|
+
}
|
|
111709
|
+
/**
|
|
111710
|
+
* Fail one cohort. Reclaims its runner context, drops its now-dead state from
|
|
111711
|
+
* the state machine, and rejects only its completion; siblings keep running
|
|
111712
|
+
* and the shared transport handlers stay registered. Idempotent via
|
|
111713
|
+
* `ctx.settled`.
|
|
111714
|
+
*/
|
|
111715
|
+
#failCohort(ctx, err) {
|
|
111716
|
+
if (ctx.settled) return;
|
|
111717
|
+
ctx.settled = true;
|
|
111718
|
+
this.#disposeCohort(ctx);
|
|
111719
|
+
this.session.removeCohort(ctx.cohortId);
|
|
111720
|
+
this.emit("error", err);
|
|
111721
|
+
ctx.reject(err);
|
|
111722
|
+
}
|
|
111723
|
+
/**
|
|
111724
|
+
* Stop a single cohort early without affecting the rest of the runner. Drops
|
|
111725
|
+
* the cohort's state machine state; its `completion` promise rejects with a
|
|
111726
|
+
* stopped error.
|
|
111727
|
+
*/
|
|
111728
|
+
stopCohort(cohortId) {
|
|
111729
|
+
const ctx = this.#contexts.get(cohortId);
|
|
111730
|
+
if (!ctx || ctx.settled) return;
|
|
111731
|
+
ctx.settled = true;
|
|
111732
|
+
this.#disposeCohort(ctx);
|
|
111733
|
+
this.session.removeCohort(cohortId);
|
|
111734
|
+
ctx.reject(new AggregationServiceError(`Cohort ${cohortId} stopped.`, "COHORT_STOPPED", { cohortId }));
|
|
111532
111735
|
}
|
|
111533
111736
|
/**
|
|
111534
|
-
* Stop the runner
|
|
111535
|
-
* handlers so a restart or a new runner doesn't inherit
|
|
111737
|
+
* Stop the whole runner. Fails every outstanding cohort, then detaches the
|
|
111738
|
+
* shared transport handlers so a restart or a new runner doesn't inherit
|
|
111739
|
+
* stale dispatch. Safe to call repeatedly.
|
|
111536
111740
|
*/
|
|
111537
111741
|
stop() {
|
|
111538
111742
|
this.#stopped = true;
|
|
111539
|
-
this.#
|
|
111540
|
-
|
|
111743
|
+
for (const ctx of [...this.#contexts.values()]) {
|
|
111744
|
+
if (ctx.settled) continue;
|
|
111745
|
+
ctx.settled = true;
|
|
111746
|
+
this.#disposeCohort(ctx);
|
|
111747
|
+
this.session.removeCohort(ctx.cohortId);
|
|
111748
|
+
ctx.reject(new AggregationServiceError("Service runner stopped.", "RUNNER_STOPPED", { cohortId: ctx.cohortId }));
|
|
111749
|
+
}
|
|
111750
|
+
this.#contexts.clear();
|
|
111541
111751
|
this.#unregisterHandlers();
|
|
111542
|
-
if (this.#cohortId) this.session.removeCohort(this.#cohortId);
|
|
111543
111752
|
}
|
|
111544
111753
|
/** Message types this runner listens for on the transport. */
|
|
111545
111754
|
static #HANDLED_MESSAGE_TYPES = [
|
|
@@ -111550,7 +111759,10 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111550
111759
|
SIGNATURE_AUTHORIZATION
|
|
111551
111760
|
];
|
|
111552
111761
|
/**
|
|
111553
|
-
* Internal: handler registration with the transport. Idempotent.
|
|
111762
|
+
* Internal: handler registration with the transport. Idempotent. Handlers
|
|
111763
|
+
* are DID-scoped and cohort-agnostic — one registration serves every cohort
|
|
111764
|
+
* this runner drives; demux to the right {@link RunContext} happens in each
|
|
111765
|
+
* handler via the inbound message's cohortId.
|
|
111554
111766
|
*/
|
|
111555
111767
|
#registerHandlers() {
|
|
111556
111768
|
if (this.#handlersRegistered) return;
|
|
@@ -111571,23 +111783,25 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111571
111783
|
}
|
|
111572
111784
|
/**
|
|
111573
111785
|
* Internal: message handlers for each protocol step. Each handler:
|
|
111574
|
-
* 1)
|
|
111575
|
-
* 2)
|
|
111576
|
-
* 3)
|
|
111786
|
+
* 1) resolves the cohort the message belongs to (by cohortId); ignores it if unknown
|
|
111787
|
+
* 2) feeds the message into the state machine via session.receive()
|
|
111788
|
+
* 3) emits a high-level event (carrying cohortId) for external observers
|
|
111789
|
+
* 4) checks if the new state triggers any automatic next steps, and if so:
|
|
111577
111790
|
* a) calls the appropriate decision callback(s)
|
|
111578
111791
|
* b) sends any resulting messages from the state machine
|
|
111792
|
+
* Errors fail only the owning cohort. A stopped runner ignores messages.
|
|
111579
111793
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111580
111794
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111581
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111582
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111583
111795
|
*/
|
|
111584
111796
|
async #handleOptIn(msg) {
|
|
111585
111797
|
if (this.#stopped) return;
|
|
111798
|
+
const ctx = this.#contextFor(msg);
|
|
111799
|
+
if (!ctx) return;
|
|
111586
111800
|
try {
|
|
111587
111801
|
this.session.receive(msg);
|
|
111588
|
-
this.#drainRejections();
|
|
111589
|
-
this.#onPhaseMaybeChanged();
|
|
111590
|
-
const optIn = this.session.pendingOptIns(
|
|
111802
|
+
this.#drainRejections(ctx);
|
|
111803
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111804
|
+
const optIn = this.session.pendingOptIns(ctx.cohortId).get(msg.from);
|
|
111591
111805
|
if (!optIn) return;
|
|
111592
111806
|
this.emit("opt-in-received", optIn);
|
|
111593
111807
|
if (optIn.communicationPk) {
|
|
@@ -111595,29 +111809,34 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111595
111809
|
}
|
|
111596
111810
|
const decision = await this.#onOptInReceived(optIn);
|
|
111597
111811
|
if (!decision.accepted) return;
|
|
111598
|
-
|
|
111599
|
-
|
|
111600
|
-
|
|
111601
|
-
|
|
111602
|
-
|
|
111812
|
+
const maxParticipants = ctx.config.maxParticipants;
|
|
111813
|
+
const cohortNow = this.session.getCohort(ctx.cohortId);
|
|
111814
|
+
if (maxParticipants !== void 0 && cohortNow && cohortNow.participants.length >= maxParticipants) {
|
|
111815
|
+
return;
|
|
111816
|
+
}
|
|
111817
|
+
await this.#sendAll(this.session.acceptParticipant(ctx.cohortId, msg.from));
|
|
111818
|
+
this.emit("participant-accepted", { cohortId: ctx.cohortId, participantDid: msg.from });
|
|
111819
|
+
const cohort = this.session.getCohort(ctx.cohortId);
|
|
111820
|
+
if (cohort.participants.length >= ctx.config.minParticipants && !ctx.finalizing) {
|
|
111821
|
+
ctx.finalizing = true;
|
|
111603
111822
|
const finalizeDecision = await this.#onReadyToFinalize({
|
|
111604
111823
|
acceptedCount: cohort.participants.length,
|
|
111605
|
-
minRequired:
|
|
111824
|
+
minRequired: ctx.config.minParticipants
|
|
111606
111825
|
});
|
|
111607
111826
|
if (!finalizeDecision.finalize) {
|
|
111608
|
-
|
|
111827
|
+
ctx.finalizing = false;
|
|
111609
111828
|
return;
|
|
111610
111829
|
}
|
|
111611
|
-
const readyMsgs = this.session.finalizeKeygen(
|
|
111612
|
-
this.#stopAdvertRepeating();
|
|
111830
|
+
const readyMsgs = this.session.finalizeKeygen(ctx.cohortId);
|
|
111831
|
+
this.#stopAdvertRepeating(ctx);
|
|
111613
111832
|
this.emit("keygen-complete", {
|
|
111614
|
-
cohortId:
|
|
111833
|
+
cohortId: ctx.cohortId,
|
|
111615
111834
|
beaconAddress: cohort.beaconAddress
|
|
111616
111835
|
});
|
|
111617
111836
|
await this.#sendAll(readyMsgs);
|
|
111618
111837
|
}
|
|
111619
111838
|
} catch (err) {
|
|
111620
|
-
this.#
|
|
111839
|
+
this.#failCohort(ctx, err);
|
|
111621
111840
|
}
|
|
111622
111841
|
}
|
|
111623
111842
|
/**
|
|
@@ -111625,23 +111844,23 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111625
111844
|
* and distributes the data for validation.
|
|
111626
111845
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111627
111846
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111628
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111629
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111630
111847
|
*/
|
|
111631
111848
|
async #handleSubmitUpdate(msg) {
|
|
111632
111849
|
if (this.#stopped) return;
|
|
111850
|
+
const ctx = this.#contextFor(msg);
|
|
111851
|
+
if (!ctx) return;
|
|
111633
111852
|
try {
|
|
111634
111853
|
this.session.receive(msg);
|
|
111635
|
-
this.#drainRejections();
|
|
111636
|
-
this.#onPhaseMaybeChanged();
|
|
111637
|
-
this.emit("update-received", { participantDid: msg.from });
|
|
111638
|
-
if (this.session.getCohortPhase(
|
|
111639
|
-
const distributeMsgs = this.session.buildAndDistribute(
|
|
111640
|
-
this.emit("data-distributed", { cohortId:
|
|
111854
|
+
this.#drainRejections(ctx);
|
|
111855
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111856
|
+
this.emit("update-received", { cohortId: ctx.cohortId, participantDid: msg.from });
|
|
111857
|
+
if (this.session.getCohortPhase(ctx.cohortId) === "UpdatesCollected" /* UpdatesCollected */) {
|
|
111858
|
+
const distributeMsgs = this.session.buildAndDistribute(ctx.cohortId);
|
|
111859
|
+
this.emit("data-distributed", { cohortId: ctx.cohortId });
|
|
111641
111860
|
await this.#sendAll(distributeMsgs);
|
|
111642
111861
|
}
|
|
111643
111862
|
} catch (err) {
|
|
111644
|
-
this.#
|
|
111863
|
+
this.#failCohort(ctx, err);
|
|
111645
111864
|
}
|
|
111646
111865
|
}
|
|
111647
111866
|
/**
|
|
@@ -111649,111 +111868,95 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
|
|
|
111649
111868
|
* automatically requests tx data and starts signing.
|
|
111650
111869
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111651
111870
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111652
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111653
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111654
111871
|
*/
|
|
111655
111872
|
async #handleValidationAck(msg) {
|
|
111656
111873
|
if (this.#stopped) return;
|
|
111874
|
+
const ctx = this.#contextFor(msg);
|
|
111875
|
+
if (!ctx) return;
|
|
111657
111876
|
try {
|
|
111658
111877
|
this.session.receive(msg);
|
|
111659
|
-
this.#drainRejections();
|
|
111660
|
-
this.#onPhaseMaybeChanged();
|
|
111878
|
+
this.#drainRejections(ctx);
|
|
111879
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111661
111880
|
const approved = !!msg.body?.approved;
|
|
111662
|
-
this.emit("validation-received", { participantDid: msg.from, approved });
|
|
111663
|
-
const phase = this.session.getCohortPhase(
|
|
111881
|
+
this.emit("validation-received", { cohortId: ctx.cohortId, participantDid: msg.from, approved });
|
|
111882
|
+
const phase = this.session.getCohortPhase(ctx.cohortId);
|
|
111664
111883
|
if (phase === "Failed" /* Failed */) {
|
|
111665
111884
|
const reason = `Validation rejected by participant ${msg.from}`;
|
|
111666
|
-
this.emit("cohort-failed", { cohortId:
|
|
111667
|
-
this.#
|
|
111885
|
+
this.emit("cohort-failed", { cohortId: ctx.cohortId, reason });
|
|
111886
|
+
this.#failCohort(ctx, new Error(reason));
|
|
111668
111887
|
return;
|
|
111669
111888
|
}
|
|
111670
111889
|
if (phase === "Validated" /* Validated */) {
|
|
111671
|
-
const cohort = this.session.getCohort(
|
|
111890
|
+
const cohort = this.session.getCohort(ctx.cohortId);
|
|
111672
111891
|
const txData = await this.#onProvideTxData({
|
|
111673
|
-
cohortId:
|
|
111892
|
+
cohortId: ctx.cohortId,
|
|
111674
111893
|
beaconAddress: cohort.beaconAddress,
|
|
111675
111894
|
signalBytes: cohort.signalBytes
|
|
111676
111895
|
});
|
|
111677
|
-
const authMsgs = this.session.startSigning(
|
|
111678
|
-
const sessionId = this.session.getSigningSessionId(
|
|
111679
|
-
this.emit("signing-started", { sessionId });
|
|
111896
|
+
const authMsgs = this.session.startSigning(ctx.cohortId, txData);
|
|
111897
|
+
const sessionId = this.session.getSigningSessionId(ctx.cohortId) ?? "";
|
|
111898
|
+
this.emit("signing-started", { cohortId: ctx.cohortId, sessionId });
|
|
111680
111899
|
await this.#sendAll(authMsgs);
|
|
111681
111900
|
}
|
|
111682
111901
|
} catch (err) {
|
|
111683
|
-
this.#
|
|
111902
|
+
this.#failCohort(ctx, err);
|
|
111684
111903
|
}
|
|
111685
111904
|
}
|
|
111686
111905
|
/**
|
|
111687
|
-
* Handler for receiving nonce contributions
|
|
111688
|
-
*
|
|
111906
|
+
* Handler for receiving nonce contributions. When all nonces are received, sends the aggregated
|
|
111907
|
+
* nonce back to the cohort.
|
|
111689
111908
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111690
111909
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111691
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111692
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111693
111910
|
*/
|
|
111694
111911
|
async #handleNonceContribution(msg) {
|
|
111695
111912
|
if (this.#stopped) return;
|
|
111913
|
+
const ctx = this.#contextFor(msg);
|
|
111914
|
+
if (!ctx) return;
|
|
111696
111915
|
try {
|
|
111697
111916
|
this.session.receive(msg);
|
|
111698
|
-
this.#drainRejections();
|
|
111699
|
-
this.#onPhaseMaybeChanged();
|
|
111700
|
-
this.emit("nonce-received", { participantDid: msg.from });
|
|
111701
|
-
if (this.session.getCohortPhase(
|
|
111702
|
-
await this.#sendAll(this.session.sendAggregatedNonce(
|
|
111917
|
+
this.#drainRejections(ctx);
|
|
111918
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111919
|
+
this.emit("nonce-received", { cohortId: ctx.cohortId, participantDid: msg.from });
|
|
111920
|
+
if (this.session.getCohortPhase(ctx.cohortId) === "NoncesCollected" /* NoncesCollected */) {
|
|
111921
|
+
await this.#sendAll(this.session.sendAggregatedNonce(ctx.cohortId));
|
|
111703
111922
|
}
|
|
111704
111923
|
} catch (err) {
|
|
111705
|
-
this.#
|
|
111924
|
+
this.#failCohort(ctx, err);
|
|
111706
111925
|
}
|
|
111707
111926
|
}
|
|
111708
111927
|
/**
|
|
111709
111928
|
* Handler for receiving signature authorizations. When all partial signatures are received, the
|
|
111710
|
-
* session automatically completes
|
|
111929
|
+
* session automatically completes; the final result is emitted and the cohort's completion
|
|
111930
|
+
* promise resolves.
|
|
111711
111931
|
* @param {BaseMessage} msg - The incoming message to handle.
|
|
111712
111932
|
* @returns {Promise<void>} Resolves when handling is complete.
|
|
111713
|
-
* @throws {Error} If any step of handling fails, the error is emitted and the run promise is rejected.
|
|
111714
|
-
* Note: if the runner has been stopped, handlers will ignore incoming messages.
|
|
111715
111933
|
*/
|
|
111716
111934
|
async #handleSignatureAuthorization(msg) {
|
|
111717
111935
|
if (this.#stopped) return;
|
|
111936
|
+
const ctx = this.#contextFor(msg);
|
|
111937
|
+
if (!ctx) return;
|
|
111718
111938
|
try {
|
|
111719
111939
|
this.session.receive(msg);
|
|
111720
|
-
this.#drainRejections();
|
|
111721
|
-
this.#onPhaseMaybeChanged();
|
|
111722
|
-
const result = this.session.getResult(
|
|
111940
|
+
this.#drainRejections(ctx);
|
|
111941
|
+
this.#onPhaseMaybeChanged(ctx);
|
|
111942
|
+
const result = this.session.getResult(ctx.cohortId);
|
|
111723
111943
|
if (result) {
|
|
111724
|
-
this.#
|
|
111725
|
-
this.#unregisterHandlers();
|
|
111726
|
-
this.emit("signing-complete", result);
|
|
111727
|
-
this.#resolveRun?.(result);
|
|
111944
|
+
this.#completeCohort(ctx, result);
|
|
111728
111945
|
}
|
|
111729
111946
|
} catch (err) {
|
|
111730
|
-
this.#
|
|
111947
|
+
this.#failCohort(ctx, err);
|
|
111731
111948
|
}
|
|
111732
111949
|
}
|
|
111733
111950
|
/**
|
|
111734
111951
|
* Internal: helper to send all messages sequentially. Catches and propagates errors.
|
|
111735
111952
|
* @param {BaseMessage[]} msgs - The messages to send.
|
|
111736
111953
|
* @returns {Promise<void>} Resolves when all messages have been sent.
|
|
111737
|
-
* @throws {Error} If sending any message fails, the error is emitted and the run promise is
|
|
111738
|
-
* rejected.
|
|
111739
111954
|
*/
|
|
111740
111955
|
async #sendAll(msgs) {
|
|
111741
111956
|
for (const m2 of msgs) {
|
|
111742
111957
|
await this.#transport.sendMessage(m2, this.#did, m2.to);
|
|
111743
111958
|
}
|
|
111744
111959
|
}
|
|
111745
|
-
/**
|
|
111746
|
-
* Internal: helper to handle errors. Emits an 'error' event and rejects the run promise.
|
|
111747
|
-
* @param {Error} err - The error to handle.
|
|
111748
|
-
*/
|
|
111749
|
-
#fail(err) {
|
|
111750
|
-
this.#stopAdvertRepeating();
|
|
111751
|
-
this.#clearTimers();
|
|
111752
|
-
this.#unregisterHandlers();
|
|
111753
|
-
if (this.#cohortId) this.session.removeCohort(this.#cohortId);
|
|
111754
|
-
this.emit("error", err);
|
|
111755
|
-
this.#rejectRun?.(err);
|
|
111756
|
-
}
|
|
111757
111960
|
};
|
|
111758
111961
|
|
|
111759
111962
|
// src/core/aggregation/runner/participant-runner.ts
|
|
@@ -111814,7 +112017,8 @@ var AggregationParticipantRunner = class _AggregationParticipantRunner extends T
|
|
|
111814
112017
|
}
|
|
111815
112018
|
/**
|
|
111816
112019
|
* Single-shot helper: start, join the first cohort that passes `shouldJoin`,
|
|
111817
|
-
* drive it to completion, and resolve. Convenient for tests and demos.
|
|
112020
|
+
* drive it to completion, and resolve. Convenient for tests and demos. The
|
|
112021
|
+
* single-cohort special case of {@link joinMatching} (count = 1).
|
|
111818
112022
|
*/
|
|
111819
112023
|
static async joinFirst(options2) {
|
|
111820
112024
|
return new Promise((resolve, reject) => {
|
|
@@ -111827,6 +112031,37 @@ var AggregationParticipantRunner = class _AggregationParticipantRunner extends T
|
|
|
111827
112031
|
runner.start().catch(reject);
|
|
111828
112032
|
});
|
|
111829
112033
|
}
|
|
112034
|
+
/**
|
|
112035
|
+
* Multi-cohort helper: start, join EVERY cohort whose advert passes
|
|
112036
|
+
* `shouldJoin`, drive each to completion in parallel, and resolve once
|
|
112037
|
+
* `count` cohorts have completed (the runner stops at that point). The
|
|
112038
|
+
* N-cohort generalization of {@link joinFirst}, for a participant that joins
|
|
112039
|
+
* several cohorts advertised by one service.
|
|
112040
|
+
*
|
|
112041
|
+
* For an open-ended, long-lived subscriber (no fixed count), construct an
|
|
112042
|
+
* {@link AggregationParticipantRunner} directly, set `shouldJoin`, call
|
|
112043
|
+
* `start()`, and listen for `cohort-complete` — the runner already drives
|
|
112044
|
+
* any number of cohorts concurrently.
|
|
112045
|
+
*
|
|
112046
|
+
* @param options Participant runner options (set `shouldJoin` to select cohorts).
|
|
112047
|
+
* @param count Number of completed cohorts to collect before resolving.
|
|
112048
|
+
* @returns The {@link CohortCompleteInfo} for each completed cohort, in completion order.
|
|
112049
|
+
*/
|
|
112050
|
+
static async joinMatching(options2, count) {
|
|
112051
|
+
return new Promise((resolve, reject) => {
|
|
112052
|
+
const runner = new _AggregationParticipantRunner(options2);
|
|
112053
|
+
const completed = [];
|
|
112054
|
+
runner.on("cohort-complete", (info) => {
|
|
112055
|
+
completed.push(info);
|
|
112056
|
+
if (completed.length >= count) {
|
|
112057
|
+
runner.stop();
|
|
112058
|
+
resolve(completed);
|
|
112059
|
+
}
|
|
112060
|
+
});
|
|
112061
|
+
runner.on("error", reject);
|
|
112062
|
+
runner.start().catch(reject);
|
|
112063
|
+
});
|
|
112064
|
+
}
|
|
111830
112065
|
/**
|
|
111831
112066
|
* Internal: handler registration with the transport. Idempotent and safe to call multiple times,
|
|
111832
112067
|
* but only registers handlers once.
|
|
@@ -111975,7 +112210,7 @@ var AggregationParticipantRunner = class _AggregationParticipantRunner extends T
|
|
|
111975
112210
|
if (this.session.getCohortPhase(cohortId) === "Complete" /* Complete */) {
|
|
111976
112211
|
const info = this.session.joinedCohorts.get(cohortId);
|
|
111977
112212
|
if (info) {
|
|
111978
|
-
const validation = this.session.
|
|
112213
|
+
const validation = this.session.getValidation(cohortId);
|
|
111979
112214
|
this.emit("cohort-complete", {
|
|
111980
112215
|
cohortId,
|
|
111981
112216
|
beaconAddress: info.beaconAddress,
|
|
@@ -129101,6 +129336,7 @@ export {
|
|
|
129101
129336
|
InMemoryRateLimitStore,
|
|
129102
129337
|
InMemoryTransport,
|
|
129103
129338
|
InboxBuffer,
|
|
129339
|
+
KNOWN_BEACON_TYPES,
|
|
129104
129340
|
KeyPairAggregationSigner,
|
|
129105
129341
|
NONCE_CONTRIBUTION,
|
|
129106
129342
|
NonceCache,
|
|
@@ -129173,6 +129409,7 @@ export {
|
|
|
129173
129409
|
registerBeaconStrategy,
|
|
129174
129410
|
reviveFromWire,
|
|
129175
129411
|
signEnvelope,
|
|
129412
|
+
validateCohortConditions,
|
|
129176
129413
|
verifyEnvelope,
|
|
129177
129414
|
verifyRequestAuth
|
|
129178
129415
|
};
|