@did-btcr2/method 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/browser.js +146 -56
  3. package/dist/browser.mjs +146 -56
  4. package/dist/cjs/index.js +146 -55
  5. package/dist/esm/core/aggregation/cohort.js +3 -1
  6. package/dist/esm/core/aggregation/cohort.js.map +1 -1
  7. package/dist/esm/core/aggregation/conditions.js +75 -0
  8. package/dist/esm/core/aggregation/conditions.js.map +1 -0
  9. package/dist/esm/core/aggregation/messages/base.js.map +1 -1
  10. package/dist/esm/core/aggregation/messages/bodies.js +16 -2
  11. package/dist/esm/core/aggregation/messages/bodies.js.map +1 -1
  12. package/dist/esm/core/aggregation/messages/factories.js.map +1 -1
  13. package/dist/esm/core/aggregation/participant.js +9 -7
  14. package/dist/esm/core/aggregation/participant.js.map +1 -1
  15. package/dist/esm/core/aggregation/runner/service-runner.js +8 -0
  16. package/dist/esm/core/aggregation/runner/service-runner.js.map +1 -1
  17. package/dist/esm/core/aggregation/service.js +23 -3
  18. package/dist/esm/core/aggregation/service.js.map +1 -1
  19. package/dist/esm/index.js +1 -0
  20. package/dist/esm/index.js.map +1 -1
  21. package/dist/types/core/aggregation/cohort.d.ts.map +1 -1
  22. package/dist/types/core/aggregation/conditions.d.ts +58 -0
  23. package/dist/types/core/aggregation/conditions.d.ts.map +1 -0
  24. package/dist/types/core/aggregation/messages/base.d.ts +2 -3
  25. package/dist/types/core/aggregation/messages/base.d.ts.map +1 -1
  26. package/dist/types/core/aggregation/messages/bodies.d.ts +2 -3
  27. package/dist/types/core/aggregation/messages/bodies.d.ts.map +1 -1
  28. package/dist/types/core/aggregation/messages/factories.d.ts +2 -3
  29. package/dist/types/core/aggregation/messages/factories.d.ts.map +1 -1
  30. package/dist/types/core/aggregation/participant.d.ts +7 -4
  31. package/dist/types/core/aggregation/participant.d.ts.map +1 -1
  32. package/dist/types/core/aggregation/runner/service-runner.d.ts.map +1 -1
  33. package/dist/types/core/aggregation/service.d.ts +8 -4
  34. package/dist/types/core/aggregation/service.d.ts.map +1 -1
  35. package/dist/types/index.d.ts +1 -0
  36. package/dist/types/index.d.ts.map +1 -1
  37. package/package.json +4 -4
  38. package/src/core/aggregation/cohort.ts +3 -1
  39. package/src/core/aggregation/conditions.ts +116 -0
  40. package/src/core/aggregation/messages/base.ts +6 -3
  41. package/src/core/aggregation/messages/bodies.ts +18 -6
  42. package/src/core/aggregation/messages/factories.ts +2 -3
  43. package/src/core/aggregation/participant.ts +16 -11
  44. package/src/core/aggregation/runner/service-runner.ts +9 -0
  45. package/src/core/aggregation/service.ts +39 -7
  46. 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 || 2;
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
- cohortSize: state.config.minParticipants,
104020
- beaconType: state.config.beaconType,
104021
- network: state.config.network,
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
- const cohortId = message2.body?.cohortId;
104489
- if (!cohortId) return;
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
- cohortSize: message2.body?.cohortSize ?? 0,
104495
- network: message2.body?.network ?? "",
104496
- beaconType: message2.body?.beaconType ?? "CASBeacon",
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.cohortSize,
104651
+ minParticipants: state.advert.minParticipants,
104523
104652
  network: state.advert.network,
104524
104653
  beaconType: state.advert.beaconType
104525
104654
  });
@@ -104842,50 +104971,6 @@ a=end-of-candidates
104842
104971
  // src/core/aggregation/messages/index.ts
104843
104972
  init_shim();
104844
104973
 
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
104974
  // src/core/aggregation/messages/guards.ts
104890
104975
  init_shim();
104891
104976
  var KEYGEN_VALUES = /* @__PURE__ */ new Set([
@@ -111731,6 +111816,11 @@ ${value2}`;
111731
111816
  }
111732
111817
  const decision = await this.#onOptInReceived(optIn);
111733
111818
  if (!decision.accepted) return;
111819
+ const maxParticipants = this.#config.maxParticipants;
111820
+ const cohortNow = this.session.getCohort(this.#cohortId);
111821
+ if (maxParticipants !== void 0 && cohortNow && cohortNow.participants.length >= maxParticipants) {
111822
+ return;
111823
+ }
111734
111824
  await this.#sendAll(this.session.acceptParticipant(this.#cohortId, msg.from));
111735
111825
  this.emit("participant-accepted", { participantDid: msg.from });
111736
111826
  const cohort = this.session.getCohort(this.#cohortId);
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 || 2;
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
- cohortSize: state.config.minParticipants,
103884
- beaconType: state.config.beaconType,
103885
- network: state.config.network,
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
- const cohortId = message2.body?.cohortId;
104353
- if (!cohortId) return;
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
- cohortSize: message2.body?.cohortSize ?? 0,
104359
- network: message2.body?.network ?? "",
104360
- beaconType: message2.body?.beaconType ?? "CASBeacon",
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.cohortSize,
104513
+ minParticipants: state.advert.minParticipants,
104387
104514
  network: state.advert.network,
104388
104515
  beaconType: state.advert.beaconType
104389
104516
  });
@@ -104706,50 +104833,6 @@ var SILENT_LOGGER = {
104706
104833
  // src/core/aggregation/messages/index.ts
104707
104834
  init_shim();
104708
104835
 
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
104836
  // src/core/aggregation/messages/guards.ts
104754
104837
  init_shim();
104755
104838
  var KEYGEN_VALUES = /* @__PURE__ */ new Set([
@@ -111595,6 +111678,11 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
111595
111678
  }
111596
111679
  const decision = await this.#onOptInReceived(optIn);
111597
111680
  if (!decision.accepted) return;
111681
+ const maxParticipants = this.#config.maxParticipants;
111682
+ const cohortNow = this.session.getCohort(this.#cohortId);
111683
+ if (maxParticipants !== void 0 && cohortNow && cohortNow.participants.length >= maxParticipants) {
111684
+ return;
111685
+ }
111598
111686
  await this.#sendAll(this.session.acceptParticipant(this.#cohortId, msg.from));
111599
111687
  this.emit("participant-accepted", { participantDid: msg.from });
111600
111688
  const cohort = this.session.getCohort(this.#cohortId);
@@ -129101,6 +129189,7 @@ export {
129101
129189
  InMemoryRateLimitStore,
129102
129190
  InMemoryTransport,
129103
129191
  InboxBuffer,
129192
+ KNOWN_BEACON_TYPES,
129104
129193
  KeyPairAggregationSigner,
129105
129194
  NONCE_CONTRIBUTION,
129106
129195
  NonceCache,
@@ -129173,6 +129262,7 @@ export {
129173
129262
  registerBeaconStrategy,
129174
129263
  reviveFromWire,
129175
129264
  signEnvelope,
129265
+ validateCohortConditions,
129176
129266
  verifyEnvelope,
129177
129267
  verifyRequestAuth
129178
129268
  };