@did-btcr2/method 0.34.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 (64) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/browser.js +335 -167
  3. package/dist/browser.mjs +335 -167
  4. package/dist/cjs/index.js +275 -114
  5. package/dist/esm/core/aggregation/cohort.js +8 -2
  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 +34 -15
  14. package/dist/esm/core/aggregation/participant.js.map +1 -1
  15. package/dist/esm/core/aggregation/runner/participant-runner.js +9 -1
  16. package/dist/esm/core/aggregation/runner/participant-runner.js.map +1 -1
  17. package/dist/esm/core/aggregation/runner/service-runner.js +12 -1
  18. package/dist/esm/core/aggregation/runner/service-runner.js.map +1 -1
  19. package/dist/esm/core/aggregation/service.js +27 -7
  20. package/dist/esm/core/aggregation/service.js.map +1 -1
  21. package/dist/esm/core/aggregation/signer.js +33 -0
  22. package/dist/esm/core/aggregation/signer.js.map +1 -0
  23. package/dist/esm/core/aggregation/signing-session.js +34 -11
  24. package/dist/esm/core/aggregation/signing-session.js.map +1 -1
  25. package/dist/esm/core/aggregation/transport/in-memory.js +5 -2
  26. package/dist/esm/core/aggregation/transport/in-memory.js.map +1 -1
  27. package/dist/esm/index.js +2 -0
  28. package/dist/esm/index.js.map +1 -1
  29. package/dist/types/core/aggregation/cohort.d.ts.map +1 -1
  30. package/dist/types/core/aggregation/conditions.d.ts +58 -0
  31. package/dist/types/core/aggregation/conditions.d.ts.map +1 -0
  32. package/dist/types/core/aggregation/messages/base.d.ts +2 -3
  33. package/dist/types/core/aggregation/messages/base.d.ts.map +1 -1
  34. package/dist/types/core/aggregation/messages/bodies.d.ts +2 -3
  35. package/dist/types/core/aggregation/messages/bodies.d.ts.map +1 -1
  36. package/dist/types/core/aggregation/messages/factories.d.ts +2 -3
  37. package/dist/types/core/aggregation/messages/factories.d.ts.map +1 -1
  38. package/dist/types/core/aggregation/participant.d.ts +24 -8
  39. package/dist/types/core/aggregation/participant.d.ts.map +1 -1
  40. package/dist/types/core/aggregation/runner/participant-runner.d.ts.map +1 -1
  41. package/dist/types/core/aggregation/runner/service-runner.d.ts.map +1 -1
  42. package/dist/types/core/aggregation/service.d.ts +18 -8
  43. package/dist/types/core/aggregation/service.d.ts.map +1 -1
  44. package/dist/types/core/aggregation/signer.d.ts +50 -0
  45. package/dist/types/core/aggregation/signer.d.ts.map +1 -0
  46. package/dist/types/core/aggregation/signing-session.d.ts +13 -5
  47. package/dist/types/core/aggregation/signing-session.d.ts.map +1 -1
  48. package/dist/types/core/aggregation/transport/in-memory.d.ts.map +1 -1
  49. package/dist/types/index.d.ts +2 -0
  50. package/dist/types/index.d.ts.map +1 -1
  51. package/package.json +4 -4
  52. package/src/core/aggregation/cohort.ts +8 -2
  53. package/src/core/aggregation/conditions.ts +116 -0
  54. package/src/core/aggregation/messages/base.ts +6 -3
  55. package/src/core/aggregation/messages/bodies.ts +18 -6
  56. package/src/core/aggregation/messages/factories.ts +2 -3
  57. package/src/core/aggregation/participant.ts +54 -23
  58. package/src/core/aggregation/runner/participant-runner.ts +9 -1
  59. package/src/core/aggregation/runner/service-runner.ts +13 -1
  60. package/src/core/aggregation/service.ts +51 -13
  61. package/src/core/aggregation/signer.ts +67 -0
  62. package/src/core/aggregation/signing-session.ts +34 -11
  63. package/src/core/aggregation/transport/in-memory.ts +7 -3
  64. package/src/index.ts +2 -0
package/dist/browser.mjs CHANGED
@@ -99982,6 +99982,13 @@ function compareBytes(a2, b) {
99982
99982
  // ../keypair/dist/esm/types.js
99983
99983
  init_shim();
99984
99984
 
99985
+ // ../keypair/dist/esm/utils.js
99986
+ init_shim();
99987
+ function wipe(bytes3) {
99988
+ if (bytes3)
99989
+ bytes3.fill(0);
99990
+ }
99991
+
99985
99992
  // ../cryptosuite/dist/esm/multikey/index.js
99986
99993
  init_secp256k1();
99987
99994
  init_utils();
@@ -100568,8 +100575,48 @@ function getBeaconStrategy(type) {
100568
100575
 
100569
100576
  // src/core/aggregation/cohort.ts
100570
100577
  init_shim();
100571
- init_secp256k1();
100572
- init_utils();
100578
+
100579
+ // ../bitcoin/dist/esm/index.js
100580
+ init_shim();
100581
+
100582
+ // ../bitcoin/dist/esm/connection.js
100583
+ init_shim();
100584
+
100585
+ // ../bitcoin/dist/esm/client/rest/index.js
100586
+ init_shim();
100587
+
100588
+ // ../bitcoin/dist/esm/client/rest/address.js
100589
+ init_shim();
100590
+
100591
+ // ../bitcoin/dist/esm/client/rest/block.js
100592
+ init_shim();
100593
+
100594
+ // ../bitcoin/dist/esm/errors.js
100595
+ init_shim();
100596
+
100597
+ // ../bitcoin/dist/esm/client/rest/transaction.js
100598
+ init_shim();
100599
+
100600
+ // ../bitcoin/dist/esm/client/rest/protocol.js
100601
+ init_shim();
100602
+
100603
+ // ../bitcoin/dist/esm/client/http.js
100604
+ init_shim();
100605
+
100606
+ // ../bitcoin/dist/esm/client/rpc/index.js
100607
+ init_shim();
100608
+
100609
+ // ../bitcoin/dist/esm/client/rpc/json-rpc.js
100610
+ init_shim();
100611
+
100612
+ // ../bitcoin/dist/esm/client/utils.js
100613
+ init_shim();
100614
+
100615
+ // ../bitcoin/dist/esm/client/rpc/protocol.js
100616
+ init_shim();
100617
+
100618
+ // ../bitcoin/dist/esm/network.js
100619
+ init_shim();
100573
100620
 
100574
100621
  // ../../node_modules/.pnpm/@scure+btc-signer@1.8.1/node_modules/@scure/btc-signer/esm/index.js
100575
100622
  init_shim();
@@ -102768,6 +102815,48 @@ var Transaction = class _Transaction {
102768
102815
  }
102769
102816
  };
102770
102817
 
102818
+ // ../bitcoin/dist/esm/network.js
102819
+ var REGTEST_NETWORK = {
102820
+ bech32: "bcrt",
102821
+ pubKeyHash: 111,
102822
+ scriptHash: 196,
102823
+ wif: 239
102824
+ };
102825
+ function getNetwork(network) {
102826
+ switch (network) {
102827
+ case "bitcoin":
102828
+ return NETWORK;
102829
+ case "testnet3":
102830
+ case "testnet4":
102831
+ case "signet":
102832
+ case "mutinynet":
102833
+ return TEST_NETWORK;
102834
+ case "regtest":
102835
+ return REGTEST_NETWORK;
102836
+ default:
102837
+ throw new Error(`Unknown network "${network}"`);
102838
+ }
102839
+ }
102840
+
102841
+ // ../bitcoin/dist/esm/constants.js
102842
+ init_shim();
102843
+ var TXIN_WITNESS_COINBASE = "0000000000000000000000000000000000000000000000000000000000000000";
102844
+ var GENESIS_TX_ID = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b";
102845
+
102846
+ // ../bitcoin/dist/esm/types.js
102847
+ init_shim();
102848
+ var VerbosityLevel;
102849
+ (function(VerbosityLevel2) {
102850
+ VerbosityLevel2[VerbosityLevel2["hex"] = 0] = "hex";
102851
+ VerbosityLevel2[VerbosityLevel2["json"] = 1] = "json";
102852
+ VerbosityLevel2[VerbosityLevel2["jsonext"] = 2] = "jsonext";
102853
+ VerbosityLevel2[VerbosityLevel2["jsonextprev"] = 3] = "jsonextprev";
102854
+ })(VerbosityLevel || (VerbosityLevel = {}));
102855
+
102856
+ // src/core/aggregation/cohort.ts
102857
+ init_secp256k1();
102858
+ init_utils();
102859
+
102771
102860
  // ../../node_modules/.pnpm/@scure+btc-signer@1.8.1/node_modules/@scure/btc-signer/esm/musig2.js
102772
102861
  init_shim();
102773
102862
  init_modular();
@@ -103156,7 +103245,7 @@ var AggregationCohort = class {
103156
103245
  validationRejections = /* @__PURE__ */ new Set();
103157
103246
  constructor({ id, minParticipants, serviceDid, network, beaconType }) {
103158
103247
  this.id = id || crypto.randomUUID();
103159
- this.minParticipants = minParticipants || 2;
103248
+ this.minParticipants = minParticipants ?? 2;
103160
103249
  this.serviceDid = serviceDid || "";
103161
103250
  this.network = network;
103162
103251
  this.beaconType = beaconType || "CASBeacon";
@@ -103182,7 +103271,7 @@ var AggregationCohort = class {
103182
103271
  }
103183
103272
  const keyAggContext = keyAggregate(this.#cohortKeys);
103184
103273
  const aggPubkey = keyAggExport(keyAggContext);
103185
- const payment = p2tr(aggPubkey);
103274
+ const payment = p2tr(aggPubkey, void 0, getNetwork(this.network));
103186
103275
  this.tapTweak = schnorr2.utils.taggedHash("TapTweak", aggPubkey);
103187
103276
  if (!payment.address) {
103188
103277
  throw new AggregationCohortError(
@@ -103322,6 +103411,57 @@ var AggregationCohort = class {
103322
103411
  }
103323
103412
  };
103324
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
+
103325
103465
  // src/core/aggregation/messages/base.ts
103326
103466
  init_shim();
103327
103467
  var AGGREGATION_WIRE_VERSION = 1;
@@ -103479,8 +103619,14 @@ var BeaconSigningSession = class {
103479
103619
  signature;
103480
103620
  /** Current signing session phase. */
103481
103621
  phase;
103482
- /** Participant's secret nonce (held only by the participant during signing). */
103483
- secretNonce;
103622
+ /**
103623
+ * Participant's MuSig2 secret nonce, held only on the participant side
103624
+ * between {@link generateNonceContribution} and {@link generatePartialSignature}.
103625
+ * Private and cleared on every terminal path (success, failure, or teardown
103626
+ * via {@link clearSecrets}) so a spent or abandoned nonce cannot be reused or
103627
+ * serialized.
103628
+ */
103629
+ #secretNonce;
103484
103630
  constructor({ id, cohort, pendingTx, prevOutScripts, prevOutValues }) {
103485
103631
  this.id = id || crypto.randomUUID();
103486
103632
  this.cohort = cohort;
@@ -103632,22 +103778,24 @@ var BeaconSigningSession = class {
103632
103778
  generateNonceContribution(participantPublicKey, participantSecretKey) {
103633
103779
  const aggPublicKey = keyAggExport(keyAggregate(this.cohort.cohortKeys));
103634
103780
  const nonces = nonceGen(participantPublicKey, participantSecretKey, aggPublicKey);
103635
- this.secretNonce = nonces.secret;
103781
+ this.#secretNonce = nonces.secret;
103636
103782
  return nonces.public;
103637
103783
  }
103638
103784
  /**
103639
103785
  * Generates a partial signature using the participant's secret key + secret nonce.
103640
103786
  * Requires the aggregated nonce to have been set first (via the service).
103641
103787
  *
103642
- * Zeros the stored `secretNonce` after use. JS cannot truly erase memory (GC
103643
- * and immutable strings), but overwriting the bytes shortens the exposure
103644
- * window and prevents accidental reuse or serialization of a spent nonce.
103788
+ * Clears the stored secret nonce after use on every path (success or throw)
103789
+ * via {@link clearSecrets}. JS cannot truly erase memory (GC may relocate
103790
+ * buffers), but overwriting the bytes shortens the exposure window and
103791
+ * prevents accidental reuse or serialization of a spent nonce - reuse of a
103792
+ * MuSig2 nonce leaks the secret key.
103645
103793
  */
103646
103794
  generatePartialSignature(participantSecretKey) {
103647
103795
  if (!this.aggregatedNonce) {
103648
103796
  throw new SigningSessionError("Aggregated nonce not available.", "MISSING_AGGREGATED_NONCE");
103649
103797
  }
103650
- if (!this.secretNonce) {
103798
+ if (!this.#secretNonce) {
103651
103799
  throw new SigningSessionError("Secret nonce not available \u2014 generateNonceContribution() must be called first.", "MISSING_SECRET_NONCE");
103652
103800
  }
103653
103801
  const session = new Session(
@@ -103657,10 +103805,23 @@ var BeaconSigningSession = class {
103657
103805
  [this.cohort.tapTweak],
103658
103806
  [true]
103659
103807
  );
103660
- const partialSig = session.sign(this.secretNonce, participantSecretKey);
103661
- this.secretNonce.fill(0);
103662
- this.secretNonce = void 0;
103663
- return partialSig;
103808
+ try {
103809
+ return session.sign(this.#secretNonce, participantSecretKey);
103810
+ } finally {
103811
+ this.clearSecrets();
103812
+ }
103813
+ }
103814
+ /**
103815
+ * Zeroize any retained secret nonce. Safe to call repeatedly and on any path
103816
+ * (completion, failure, or teardown of an abandoned session). Callers that
103817
+ * drop a session before it reaches a partial signature should invoke this so
103818
+ * the secret nonce does not linger on the live object.
103819
+ */
103820
+ clearSecrets() {
103821
+ if (this.#secretNonce) {
103822
+ wipe(this.#secretNonce);
103823
+ this.#secretNonce = void 0;
103824
+ }
103664
103825
  }
103665
103826
  isComplete() {
103666
103827
  return this.phase === "Complete" /* Complete */;
@@ -103674,13 +103835,13 @@ var BeaconSigningSession = class {
103674
103835
  var DEFAULT_MAX_UPDATE_SIZE_BYTES = 256 * 1024;
103675
103836
  var AggregationService = class {
103676
103837
  did;
103677
- keys;
103838
+ publicKey;
103678
103839
  maxUpdateSizeBytes;
103679
103840
  /** Per-cohort state, keyed by cohortId. */
103680
103841
  #cohortStates = /* @__PURE__ */ new Map();
103681
- constructor({ did, keys, maxUpdateSizeBytes }) {
103842
+ constructor({ did, publicKey: publicKey2, maxUpdateSizeBytes }) {
103682
103843
  this.did = did;
103683
- this.keys = keys;
103844
+ this.publicKey = publicKey2;
103684
103845
  this.maxUpdateSizeBytes = maxUpdateSizeBytes ?? DEFAULT_MAX_UPDATE_SIZE_BYTES;
103685
103846
  }
103686
103847
  receive(message2) {
@@ -103735,6 +103896,14 @@ var AggregationService = class {
103735
103896
  * Cohort starts in `Created` phase — call `advertise()` to broadcast.
103736
103897
  */
103737
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
+ }
103738
103907
  const cohort = new AggregationCohort({
103739
103908
  serviceDid: this.did,
103740
103909
  minParticipants: config.minParticipants,
@@ -103767,13 +103936,13 @@ var AggregationService = class {
103767
103936
  { cohortId, phase: state.phase }
103768
103937
  );
103769
103938
  }
103939
+ const { network, ...conditions } = state.config;
103770
103940
  const message2 = createCohortAdvertMessage({
103771
103941
  from: this.did,
103772
103942
  cohortId,
103773
- cohortSize: state.config.minParticipants,
103774
- beaconType: state.config.beaconType,
103775
- network: state.config.network,
103776
- communicationPk: this.keys.publicKey.compressed
103943
+ network,
103944
+ communicationPk: this.publicKey.compressed,
103945
+ ...conditions
103777
103946
  });
103778
103947
  state.phase = "Advertised" /* Advertised */;
103779
103948
  return [message2];
@@ -103832,6 +104001,14 @@ var AggregationService = class {
103832
104001
  { cohortId, participantDid }
103833
104002
  );
103834
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
+ }
103835
104012
  state.acceptedParticipants.add(participantDid);
103836
104013
  state.cohort.participants.push(participantDid);
103837
104014
  state.cohort.participantKeys.set(participantDid, optIn.participantPk);
@@ -103865,6 +104042,14 @@ var AggregationService = class {
103865
104042
  { cohortId }
103866
104043
  );
103867
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
+ }
103868
104053
  const beaconAddress = state.cohort.computeBeaconAddress();
103869
104054
  state.phase = "CohortSet" /* CohortSet */;
103870
104055
  const messages2 = [];
@@ -104182,14 +104367,72 @@ var AggregationService = class {
104182
104367
  // src/core/aggregation/participant.ts
104183
104368
  init_shim();
104184
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
104185
104423
  var AggregationParticipant = class {
104186
104424
  did;
104187
- keys;
104425
+ /** MuSig2 signing capability. The raw secret never lives as a field here. */
104426
+ #signer;
104188
104427
  /** Per-cohort state, keyed by cohortId. */
104189
104428
  #cohortStates = /* @__PURE__ */ new Map();
104190
- constructor({ did, keys }) {
104429
+ constructor({ did, signer }) {
104191
104430
  this.did = did;
104192
- this.keys = keys;
104431
+ this.#signer = signer;
104432
+ }
104433
+ /** The participant's compressed (33-byte) MuSig2 public key. Not secret. */
104434
+ get publicKey() {
104435
+ return this.#signer.publicKey;
104193
104436
  }
104194
104437
  /**
104195
104438
  * Process an incoming message. Updates internal state but never produces
@@ -104234,16 +104477,15 @@ var AggregationParticipant = class {
104234
104477
  return map3;
104235
104478
  }
104236
104479
  #handleCohortAdvert(message2) {
104237
- const cohortId = message2.body?.cohortId;
104238
- if (!cohortId) return;
104480
+ if (!isCohortAdvertMessage(message2)) return;
104481
+ const { cohortId, network, communicationPk, ...conditions } = message2.body;
104239
104482
  if (this.#cohortStates.has(cohortId)) return;
104240
104483
  const advert = {
104241
104484
  cohortId,
104242
104485
  serviceDid: message2.from,
104243
- cohortSize: message2.body?.cohortSize ?? 0,
104244
- network: message2.body?.network ?? "",
104245
- beaconType: message2.body?.beaconType ?? "CASBeacon",
104246
- serviceCommunicationPk: message2.body?.communicationPk ?? new Uint8Array()
104486
+ network,
104487
+ serviceCommunicationPk: communicationPk,
104488
+ ...conditions
104247
104489
  };
104248
104490
  this.#cohortStates.set(cohortId, {
104249
104491
  phase: "Discovered" /* Discovered */,
@@ -104268,7 +104510,7 @@ var AggregationParticipant = class {
104268
104510
  const cohort = new AggregationCohort({
104269
104511
  id: cohortId,
104270
104512
  serviceDid: state.serviceDid,
104271
- minParticipants: state.advert.cohortSize,
104513
+ minParticipants: state.advert.minParticipants,
104272
104514
  network: state.advert.network,
104273
104515
  beaconType: state.advert.beaconType
104274
104516
  });
@@ -104278,8 +104520,8 @@ var AggregationParticipant = class {
104278
104520
  from: this.did,
104279
104521
  to: state.serviceDid,
104280
104522
  cohortId,
104281
- participantPk: this.keys.publicKey.compressed,
104282
- communicationPk: this.keys.publicKey.compressed
104523
+ participantPk: this.publicKey,
104524
+ communicationPk: this.publicKey
104283
104525
  });
104284
104526
  return [optInMessage];
104285
104527
  }
@@ -104309,7 +104551,7 @@ var AggregationParticipant = class {
104309
104551
  const beaconAddress = message2.body?.beaconAddress;
104310
104552
  const cohortKeys = message2.body?.cohortKeys;
104311
104553
  if (!beaconAddress || !cohortKeys) return;
104312
- const participantPkHex = bytesToHex(this.keys.publicKey.compressed);
104554
+ const participantPkHex = bytesToHex(this.publicKey);
104313
104555
  const cohortKeysHex = cohortKeys.map((k) => bytesToHex(new Uint8Array(k)));
104314
104556
  state.cohort.validateMembership(participantPkHex, cohortKeysHex, beaconAddress);
104315
104557
  state.phase = "CohortReady" /* CohortReady */;
@@ -104476,9 +104718,8 @@ var AggregationParticipant = class {
104476
104718
  prevOutValues
104477
104719
  });
104478
104720
  state.signingSession = session;
104479
- const nonceContribution = session.generateNonceContribution(
104480
- this.keys.publicKey.compressed,
104481
- this.keys.secretKey.bytes
104721
+ const nonceContribution = this.#signer.withSecret(
104722
+ (secretKey) => session.generateNonceContribution(this.publicKey, secretKey)
104482
104723
  );
104483
104724
  state.phase = "NonceSent" /* NonceSent */;
104484
104725
  return [createNonceContributionMessage({
@@ -104521,7 +104762,10 @@ var AggregationParticipant = class {
104521
104762
  { cohortId }
104522
104763
  );
104523
104764
  }
104524
- const partialSig = state.signingSession.generatePartialSignature(this.keys.secretKey.bytes);
104765
+ const signingSession = state.signingSession;
104766
+ const partialSig = this.#signer.withSecret(
104767
+ (secretKey) => signingSession.generatePartialSignature(secretKey)
104768
+ );
104525
104769
  state.phase = "Complete" /* Complete */;
104526
104770
  return [createSignatureAuthorizationMessage({
104527
104771
  from: this.did,
@@ -104534,6 +104778,37 @@ var AggregationParticipant = class {
104534
104778
  getCohortPhase(cohortId) {
104535
104779
  return this.#cohortStates.get(cohortId)?.phase;
104536
104780
  }
104781
+ /**
104782
+ * Zeroize any retained MuSig2 secret nonces across all cohorts. The raw
104783
+ * signing key is never held here (it lives behind the {@link AggregationSigner}
104784
+ * and is wiped per-operation), but an abandoned signing session can still hold
104785
+ * a secret nonce; call this on teardown to clear it deterministically.
104786
+ */
104787
+ clearSecrets() {
104788
+ for (const state of this.#cohortStates.values()) {
104789
+ state.signingSession?.clearSecrets();
104790
+ }
104791
+ }
104792
+ };
104793
+
104794
+ // src/core/aggregation/signer.ts
104795
+ init_shim();
104796
+ var KeyPairAggregationSigner = class {
104797
+ publicKey;
104798
+ #keys;
104799
+ /** @param {SchnorrKeyPair} keys The keypair whose secret backs this signer. */
104800
+ constructor(keys) {
104801
+ this.#keys = keys;
104802
+ this.publicKey = keys.publicKey.compressed;
104803
+ }
104804
+ withSecret(fn) {
104805
+ const secretKey = this.#keys.secretKey.bytes;
104806
+ try {
104807
+ return fn(secretKey);
104808
+ } finally {
104809
+ wipe(secretKey);
104810
+ }
104811
+ }
104537
104812
  };
104538
104813
 
104539
104814
  // src/core/aggregation/logger.ts
@@ -104558,50 +104833,6 @@ var SILENT_LOGGER = {
104558
104833
  // src/core/aggregation/messages/index.ts
104559
104834
  init_shim();
104560
104835
 
104561
- // src/core/aggregation/messages/bodies.ts
104562
- init_shim();
104563
- var hasStr = (b, k) => !!b && typeof b[k] === "string";
104564
- var hasNum = (b, k) => !!b && typeof b[k] === "number";
104565
- var hasBool = (b, k) => !!b && typeof b[k] === "boolean";
104566
- var hasBytes = (b, k) => !!b && b[k] instanceof Uint8Array;
104567
- var hasBytesArray = (b, k) => {
104568
- const v = b ? b[k] : void 0;
104569
- return Array.isArray(v) && v.every((x) => x instanceof Uint8Array);
104570
- };
104571
- function isCohortAdvertMessage(m2) {
104572
- 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");
104573
- }
104574
- function isCohortOptInMessage(m2) {
104575
- return m2.type === COHORT_OPT_IN && hasStr(m2.body, "cohortId") && hasBytes(m2.body, "participantPk") && hasBytes(m2.body, "communicationPk");
104576
- }
104577
- function isCohortOptInAcceptMessage(m2) {
104578
- return m2.type === COHORT_OPT_IN_ACCEPT && hasStr(m2.body, "cohortId");
104579
- }
104580
- function isCohortReadyMessage(m2) {
104581
- return m2.type === COHORT_READY && hasStr(m2.body, "cohortId") && hasStr(m2.body, "beaconAddress") && hasBytesArray(m2.body, "cohortKeys");
104582
- }
104583
- function isSubmitUpdateMessage(m2) {
104584
- return m2.type === SUBMIT_UPDATE && hasStr(m2.body, "cohortId") && !!m2.body && typeof m2.body.signedUpdate === "object";
104585
- }
104586
- function isDistributeAggregatedDataMessage(m2) {
104587
- return m2.type === DISTRIBUTE_AGGREGATED_DATA && hasStr(m2.body, "cohortId") && hasStr(m2.body, "beaconType") && hasStr(m2.body, "signalBytesHex");
104588
- }
104589
- function isValidationAckMessage(m2) {
104590
- return m2.type === VALIDATION_ACK && hasStr(m2.body, "cohortId") && hasBool(m2.body, "approved");
104591
- }
104592
- function isAuthorizationRequestMessage(m2) {
104593
- 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");
104594
- }
104595
- function isNonceContributionMessage(m2) {
104596
- return m2.type === NONCE_CONTRIBUTION && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "nonceContribution");
104597
- }
104598
- function isAggregatedNonceMessage(m2) {
104599
- return m2.type === AGGREGATED_NONCE && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "aggregatedNonce");
104600
- }
104601
- function isSignatureAuthorizationMessage(m2) {
104602
- return m2.type === SIGNATURE_AUTHORIZATION && hasStr(m2.body, "cohortId") && hasStr(m2.body, "sessionId") && hasBytes(m2.body, "partialSignature");
104603
- }
104604
-
104605
104836
  // src/core/aggregation/messages/guards.ts
104606
104837
  init_shim();
104607
104838
  var KEYGEN_VALUES = /* @__PURE__ */ new Set([
@@ -105290,7 +105521,7 @@ var HttpClientTransport = class {
105290
105521
  body: JSON.stringify(envelope)
105291
105522
  });
105292
105523
  if (!res.ok) {
105293
- const body = await safeText(res);
105524
+ const body = await safeText2(res);
105294
105525
  throw new HttpTransportError(
105295
105526
  `sendMessage failed: HTTP ${res.status}`,
105296
105527
  "SEND_MESSAGE_HTTP",
@@ -105467,7 +105698,7 @@ function sleep(ms2, signal) {
105467
105698
  else signal.addEventListener("abort", onAbort, { once: true });
105468
105699
  });
105469
105700
  }
105470
- async function safeText(res) {
105701
+ async function safeText2(res) {
105471
105702
  try {
105472
105703
  return await res.text();
105473
105704
  } catch {
@@ -111046,10 +111277,10 @@ var InMemoryTransport = class {
111046
111277
  start() {
111047
111278
  }
111048
111279
  registerActor(did, keys) {
111049
- this.#actors.set(did, { keys, handlers: /* @__PURE__ */ new Map() });
111280
+ this.#actors.set(did, { publicKey: keys.publicKey.compressed, handlers: /* @__PURE__ */ new Map() });
111050
111281
  }
111051
111282
  getActorPk(did) {
111052
- return this.#actors.get(did)?.keys.publicKey.compressed;
111283
+ return this.#actors.get(did)?.publicKey;
111053
111284
  }
111054
111285
  /** True if `did` is registered on this transport. Used by the bus for routing. */
111055
111286
  hasActor(did) {
@@ -111272,8 +111503,11 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
111272
111503
  this.#phaseTimeoutMs = options2.phaseTimeoutMs;
111273
111504
  this.#advertRepeatIntervalMs = options2.advertRepeatIntervalMs ?? DEFAULT_ADVERT_REPEAT_INTERVAL_MS;
111274
111505
  this.session = new AggregationService({
111506
+ // The coordinator never signs, so the state machine receives only the
111507
+ // public half of the operator's keypair (see ADR 038). The full keypair
111508
+ // remains the operator's transport/communication identity.
111275
111509
  did: options2.did,
111276
- keys: options2.keys,
111510
+ publicKey: options2.keys.publicKey,
111277
111511
  maxUpdateSizeBytes: options2.maxUpdateSizeBytes
111278
111512
  });
111279
111513
  }
@@ -111444,6 +111678,11 @@ var AggregationServiceRunner = class _AggregationServiceRunner extends TypedEven
111444
111678
  }
111445
111679
  const decision = await this.#onOptInReceived(optIn);
111446
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
+ }
111447
111686
  await this.#sendAll(this.session.acceptParticipant(this.#cohortId, msg.from));
111448
111687
  this.emit("participant-accepted", { participantDid: msg.from });
111449
111688
  const cohort = this.session.getCohort(this.#cohortId);
@@ -111626,7 +111865,10 @@ var AggregationParticipantRunner = class _AggregationParticipantRunner extends T
111626
111865
  this.#onProvideUpdate = options2.onProvideUpdate;
111627
111866
  this.#onValidateData = options2.onValidateData ?? (async (info) => ({ approved: info.matches }));
111628
111867
  this.#onApproveSigning = options2.onApproveSigning ?? (async () => ({ approved: true }));
111629
- this.session = new AggregationParticipant({ did: options2.did, keys: options2.keys });
111868
+ this.session = new AggregationParticipant({
111869
+ did: options2.did,
111870
+ signer: new KeyPairAggregationSigner(options2.keys)
111871
+ });
111630
111872
  }
111631
111873
  /**
111632
111874
  * Start listening for cohorts. The runner stays active until {@link stop}
@@ -111639,6 +111881,7 @@ var AggregationParticipantRunner = class _AggregationParticipantRunner extends T
111639
111881
  stop() {
111640
111882
  this.#stopped = true;
111641
111883
  this.#unregisterHandlers();
111884
+ this.session.clearSecrets();
111642
111885
  }
111643
111886
  /** Message types this runner listens for on the transport. */
111644
111887
  static #HANDLED_MESSAGE_TYPES = [
@@ -112480,84 +112723,6 @@ init_shim();
112480
112723
  // src/core/beacon/signal-discovery.ts
112481
112724
  init_shim();
112482
112725
 
112483
- // ../bitcoin/dist/esm/index.js
112484
- init_shim();
112485
-
112486
- // ../bitcoin/dist/esm/connection.js
112487
- init_shim();
112488
-
112489
- // ../bitcoin/dist/esm/client/rest/index.js
112490
- init_shim();
112491
-
112492
- // ../bitcoin/dist/esm/client/rest/address.js
112493
- init_shim();
112494
-
112495
- // ../bitcoin/dist/esm/client/rest/block.js
112496
- init_shim();
112497
-
112498
- // ../bitcoin/dist/esm/errors.js
112499
- init_shim();
112500
-
112501
- // ../bitcoin/dist/esm/client/rest/transaction.js
112502
- init_shim();
112503
-
112504
- // ../bitcoin/dist/esm/client/rest/protocol.js
112505
- init_shim();
112506
-
112507
- // ../bitcoin/dist/esm/client/http.js
112508
- init_shim();
112509
-
112510
- // ../bitcoin/dist/esm/client/rpc/index.js
112511
- init_shim();
112512
-
112513
- // ../bitcoin/dist/esm/client/rpc/json-rpc.js
112514
- init_shim();
112515
-
112516
- // ../bitcoin/dist/esm/client/utils.js
112517
- init_shim();
112518
-
112519
- // ../bitcoin/dist/esm/client/rpc/protocol.js
112520
- init_shim();
112521
-
112522
- // ../bitcoin/dist/esm/network.js
112523
- init_shim();
112524
- var REGTEST_NETWORK = {
112525
- bech32: "bcrt",
112526
- pubKeyHash: 111,
112527
- scriptHash: 196,
112528
- wif: 239
112529
- };
112530
- function getNetwork(network) {
112531
- switch (network) {
112532
- case "bitcoin":
112533
- return NETWORK;
112534
- case "testnet3":
112535
- case "testnet4":
112536
- case "signet":
112537
- case "mutinynet":
112538
- return TEST_NETWORK;
112539
- case "regtest":
112540
- return REGTEST_NETWORK;
112541
- default:
112542
- throw new Error(`Unknown network "${network}"`);
112543
- }
112544
- }
112545
-
112546
- // ../bitcoin/dist/esm/constants.js
112547
- init_shim();
112548
- var TXIN_WITNESS_COINBASE = "0000000000000000000000000000000000000000000000000000000000000000";
112549
- var GENESIS_TX_ID = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b";
112550
-
112551
- // ../bitcoin/dist/esm/types.js
112552
- init_shim();
112553
- var VerbosityLevel;
112554
- (function(VerbosityLevel2) {
112555
- VerbosityLevel2[VerbosityLevel2["hex"] = 0] = "hex";
112556
- VerbosityLevel2[VerbosityLevel2["json"] = 1] = "json";
112557
- VerbosityLevel2[VerbosityLevel2["jsonext"] = 2] = "jsonext";
112558
- VerbosityLevel2[VerbosityLevel2["jsonextprev"] = 3] = "jsonextprev";
112559
- })(VerbosityLevel || (VerbosityLevel = {}));
112560
-
112561
112726
  // src/core/beacon/utils.ts
112562
112727
  init_shim();
112563
112728
 
@@ -129024,6 +129189,8 @@ export {
129024
129189
  InMemoryRateLimitStore,
129025
129190
  InMemoryTransport,
129026
129191
  InboxBuffer,
129192
+ KNOWN_BEACON_TYPES,
129193
+ KeyPairAggregationSigner,
129027
129194
  NONCE_CONTRIBUTION,
129028
129195
  NonceCache,
129029
129196
  NostrTransport,
@@ -129095,6 +129262,7 @@ export {
129095
129262
  registerBeaconStrategy,
129096
129263
  reviveFromWire,
129097
129264
  signEnvelope,
129265
+ validateCohortConditions,
129098
129266
  verifyEnvelope,
129099
129267
  verifyRequestAuth
129100
129268
  };