@did-btcr2/method 0.36.0 → 0.37.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 (164) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/browser.js +1371 -260
  3. package/dist/browser.mjs +1371 -260
  4. package/dist/cjs/index.js +1464 -343
  5. package/dist/esm/core/aggregation/beacon-strategy.js +19 -6
  6. package/dist/esm/core/aggregation/beacon-strategy.js.map +1 -1
  7. package/dist/esm/core/aggregation/cohort.js +142 -33
  8. package/dist/esm/core/aggregation/cohort.js.map +1 -1
  9. package/dist/esm/core/aggregation/conditions.js +27 -0
  10. package/dist/esm/core/aggregation/conditions.js.map +1 -1
  11. package/dist/esm/core/aggregation/fallback-spend.js +132 -0
  12. package/dist/esm/core/aggregation/fallback-spend.js.map +1 -0
  13. package/dist/esm/core/aggregation/messages/base.js.map +1 -1
  14. package/dist/esm/core/aggregation/messages/bodies.js +46 -1
  15. package/dist/esm/core/aggregation/messages/bodies.js.map +1 -1
  16. package/dist/esm/core/aggregation/messages/constants.js +11 -0
  17. package/dist/esm/core/aggregation/messages/constants.js.map +1 -1
  18. package/dist/esm/core/aggregation/messages/factories.js +27 -1
  19. package/dist/esm/core/aggregation/messages/factories.js.map +1 -1
  20. package/dist/esm/core/aggregation/messages/guards.js +4 -1
  21. package/dist/esm/core/aggregation/messages/guards.js.map +1 -1
  22. package/dist/esm/core/aggregation/participant.js +218 -18
  23. package/dist/esm/core/aggregation/participant.js.map +1 -1
  24. package/dist/esm/core/aggregation/phases.js +6 -0
  25. package/dist/esm/core/aggregation/phases.js.map +1 -1
  26. package/dist/esm/core/aggregation/recovery-policy.js +156 -0
  27. package/dist/esm/core/aggregation/recovery-policy.js.map +1 -0
  28. package/dist/esm/core/aggregation/recovery-spend.js +106 -0
  29. package/dist/esm/core/aggregation/recovery-spend.js.map +1 -0
  30. package/dist/esm/core/aggregation/runner/aggregation-runner.js +11 -3
  31. package/dist/esm/core/aggregation/runner/aggregation-runner.js.map +1 -1
  32. package/dist/esm/core/aggregation/runner/participant-runner.js +106 -19
  33. package/dist/esm/core/aggregation/runner/participant-runner.js.map +1 -1
  34. package/dist/esm/core/aggregation/runner/service-runner.js +409 -195
  35. package/dist/esm/core/aggregation/runner/service-runner.js.map +1 -1
  36. package/dist/esm/core/aggregation/service.js +221 -11
  37. package/dist/esm/core/aggregation/service.js.map +1 -1
  38. package/dist/esm/core/aggregation/signing-session.js +3 -3
  39. package/dist/esm/core/aggregation/signing-session.js.map +1 -1
  40. package/dist/esm/core/aggregation/transport/http/envelope.js +3 -3
  41. package/dist/esm/core/aggregation/transport/http/inbox-buffer.js +1 -1
  42. package/dist/esm/core/aggregation/transport/http/nonce-cache.js +1 -1
  43. package/dist/esm/core/aggregation/transport/http/rate-limiter.js +1 -1
  44. package/dist/esm/core/aggregation/transport/http/request-auth.js +1 -1
  45. package/dist/esm/core/aggregation/transport/http/server.js +1 -1
  46. package/dist/esm/core/aggregation/transport/http/sse-stream.js +1 -1
  47. package/dist/esm/core/aggregation/transport/http/sse-writer.js +1 -1
  48. package/dist/esm/core/aggregation/transport/in-memory.js +1 -1
  49. package/dist/esm/core/aggregation/transport/nostr.js +6 -6
  50. package/dist/esm/core/beacon/beacon.js +15 -10
  51. package/dist/esm/core/beacon/beacon.js.map +1 -1
  52. package/dist/esm/core/beacon/cas-beacon.js +6 -6
  53. package/dist/esm/core/beacon/cas-beacon.js.map +1 -1
  54. package/dist/esm/core/beacon/singleton-beacon.js +2 -2
  55. package/dist/esm/core/beacon/singleton-beacon.js.map +1 -1
  56. package/dist/esm/core/beacon/smt-beacon.js +3 -3
  57. package/dist/esm/core/beacon/smt-beacon.js.map +1 -1
  58. package/dist/esm/core/resolver.js +8 -8
  59. package/dist/esm/core/resolver.js.map +1 -1
  60. package/dist/esm/core/updater.js +6 -6
  61. package/dist/esm/core/updater.js.map +1 -1
  62. package/dist/esm/did-btcr2.js +2 -2
  63. package/dist/esm/index.js +3 -0
  64. package/dist/esm/index.js.map +1 -1
  65. package/dist/types/core/aggregation/beacon-strategy.d.ts +8 -4
  66. package/dist/types/core/aggregation/beacon-strategy.d.ts.map +1 -1
  67. package/dist/types/core/aggregation/cohort.d.ts +88 -17
  68. package/dist/types/core/aggregation/cohort.d.ts.map +1 -1
  69. package/dist/types/core/aggregation/conditions.d.ts +28 -0
  70. package/dist/types/core/aggregation/conditions.d.ts.map +1 -1
  71. package/dist/types/core/aggregation/fallback-spend.d.ts +94 -0
  72. package/dist/types/core/aggregation/fallback-spend.d.ts.map +1 -0
  73. package/dist/types/core/aggregation/logger.d.ts +1 -1
  74. package/dist/types/core/aggregation/messages/base.d.ts +6 -0
  75. package/dist/types/core/aggregation/messages/base.d.ts.map +1 -1
  76. package/dist/types/core/aggregation/messages/bodies.d.ts +50 -2
  77. package/dist/types/core/aggregation/messages/bodies.d.ts.map +1 -1
  78. package/dist/types/core/aggregation/messages/constants.d.ts +8 -0
  79. package/dist/types/core/aggregation/messages/constants.d.ts.map +1 -1
  80. package/dist/types/core/aggregation/messages/factories.d.ts +40 -0
  81. package/dist/types/core/aggregation/messages/factories.d.ts.map +1 -1
  82. package/dist/types/core/aggregation/messages/guards.d.ts.map +1 -1
  83. package/dist/types/core/aggregation/participant.d.ts +47 -4
  84. package/dist/types/core/aggregation/participant.d.ts.map +1 -1
  85. package/dist/types/core/aggregation/phases.d.ts +8 -2
  86. package/dist/types/core/aggregation/phases.d.ts.map +1 -1
  87. package/dist/types/core/aggregation/recovery-policy.d.ts +132 -0
  88. package/dist/types/core/aggregation/recovery-policy.d.ts.map +1 -0
  89. package/dist/types/core/aggregation/recovery-spend.d.ts +94 -0
  90. package/dist/types/core/aggregation/recovery-spend.d.ts.map +1 -0
  91. package/dist/types/core/aggregation/runner/aggregation-runner.d.ts +13 -3
  92. package/dist/types/core/aggregation/runner/aggregation-runner.d.ts.map +1 -1
  93. package/dist/types/core/aggregation/runner/events.d.ts +48 -16
  94. package/dist/types/core/aggregation/runner/events.d.ts.map +1 -1
  95. package/dist/types/core/aggregation/runner/participant-runner.d.ts +23 -14
  96. package/dist/types/core/aggregation/runner/participant-runner.d.ts.map +1 -1
  97. package/dist/types/core/aggregation/runner/service-runner.d.ts +102 -23
  98. package/dist/types/core/aggregation/runner/service-runner.d.ts.map +1 -1
  99. package/dist/types/core/aggregation/service.d.ts +26 -2
  100. package/dist/types/core/aggregation/service.d.ts.map +1 -1
  101. package/dist/types/core/aggregation/signing-session.d.ts +2 -2
  102. package/dist/types/core/aggregation/signing-session.d.ts.map +1 -1
  103. package/dist/types/core/aggregation/transport/http/envelope.d.ts +3 -3
  104. package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts +1 -1
  105. package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts +1 -1
  106. package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts +1 -1
  107. package/dist/types/core/aggregation/transport/http/request-auth.d.ts +1 -1
  108. package/dist/types/core/aggregation/transport/http/server.d.ts +1 -1
  109. package/dist/types/core/aggregation/transport/http/sse-stream.d.ts +2 -2
  110. package/dist/types/core/aggregation/transport/http/sse-writer.d.ts +1 -1
  111. package/dist/types/core/aggregation/transport/in-memory.d.ts +1 -1
  112. package/dist/types/core/aggregation/transport/nostr.d.ts +4 -4
  113. package/dist/types/core/aggregation/transport/transport.d.ts +4 -4
  114. package/dist/types/core/aggregation/transport/transport.d.ts.map +1 -1
  115. package/dist/types/core/beacon/beacon.d.ts +7 -7
  116. package/dist/types/core/beacon/beacon.d.ts.map +1 -1
  117. package/dist/types/core/beacon/cas-beacon.d.ts +2 -2
  118. package/dist/types/core/beacon/interfaces.d.ts +1 -1
  119. package/dist/types/core/resolver.d.ts +3 -3
  120. package/dist/types/core/updater.d.ts +4 -4
  121. package/dist/types/did-btcr2.d.ts +2 -2
  122. package/dist/types/index.d.ts +3 -0
  123. package/dist/types/index.d.ts.map +1 -1
  124. package/package.json +4 -4
  125. package/src/core/aggregation/beacon-strategy.ts +27 -9
  126. package/src/core/aggregation/cohort.ts +171 -35
  127. package/src/core/aggregation/conditions.ts +56 -0
  128. package/src/core/aggregation/fallback-spend.ts +223 -0
  129. package/src/core/aggregation/logger.ts +1 -1
  130. package/src/core/aggregation/messages/base.ts +6 -0
  131. package/src/core/aggregation/messages/bodies.ts +94 -1
  132. package/src/core/aggregation/messages/constants.ts +12 -0
  133. package/src/core/aggregation/messages/factories.ts +57 -0
  134. package/src/core/aggregation/messages/guards.ts +6 -0
  135. package/src/core/aggregation/participant.ts +264 -20
  136. package/src/core/aggregation/phases.ts +9 -0
  137. package/src/core/aggregation/recovery-policy.ts +237 -0
  138. package/src/core/aggregation/recovery-spend.ts +192 -0
  139. package/src/core/aggregation/runner/aggregation-runner.ts +27 -5
  140. package/src/core/aggregation/runner/events.ts +47 -19
  141. package/src/core/aggregation/runner/participant-runner.ts +113 -31
  142. package/src/core/aggregation/runner/service-runner.ts +484 -203
  143. package/src/core/aggregation/service.ts +260 -14
  144. package/src/core/aggregation/signing-session.ts +3 -3
  145. package/src/core/aggregation/transport/http/envelope.ts +3 -3
  146. package/src/core/aggregation/transport/http/inbox-buffer.ts +1 -1
  147. package/src/core/aggregation/transport/http/nonce-cache.ts +1 -1
  148. package/src/core/aggregation/transport/http/rate-limiter.ts +1 -1
  149. package/src/core/aggregation/transport/http/request-auth.ts +1 -1
  150. package/src/core/aggregation/transport/http/server.ts +1 -1
  151. package/src/core/aggregation/transport/http/sse-stream.ts +2 -2
  152. package/src/core/aggregation/transport/http/sse-writer.ts +1 -1
  153. package/src/core/aggregation/transport/in-memory.ts +1 -1
  154. package/src/core/aggregation/transport/nostr.ts +6 -6
  155. package/src/core/aggregation/transport/transport.ts +4 -4
  156. package/src/core/beacon/beacon.ts +16 -11
  157. package/src/core/beacon/cas-beacon.ts +7 -7
  158. package/src/core/beacon/interfaces.ts +1 -1
  159. package/src/core/beacon/singleton-beacon.ts +2 -2
  160. package/src/core/beacon/smt-beacon.ts +3 -3
  161. package/src/core/resolver.ts +8 -8
  162. package/src/core/updater.ts +6 -6
  163. package/src/did-btcr2.ts +2 -2
  164. package/src/index.ts +3 -0
@@ -1,6 +1,5 @@
1
1
  import type { SignedBTCR2Update } from '@did-btcr2/cryptosuite';
2
2
  import type { SchnorrKeyPair } from '@did-btcr2/keypair';
3
- import type { SerializedSMTProof } from '@did-btcr2/smt';
4
3
  import type { BaseMessage } from '../messages/base.js';
5
4
  import {
6
5
  AGGREGATED_NONCE,
@@ -9,6 +8,7 @@ import {
9
8
  COHORT_OPT_IN_ACCEPT,
10
9
  COHORT_READY,
11
10
  DISTRIBUTE_AGGREGATED_DATA,
11
+ FALLBACK_AUTHORIZATION_REQUEST,
12
12
  } from '../messages/constants.js';
13
13
  import type {
14
14
  CohortAdvert,
@@ -20,7 +20,7 @@ import {
20
20
  import { ParticipantCohortPhase } from '../phases.js';
21
21
  import { KeyPairAggregationSigner } from '../signer.js';
22
22
  import type { Transport } from '../transport/transport.js';
23
- import type { AggregationParticipantEvents } from './events.js';
23
+ import type { AggregationParticipantEvents, CohortCompleteInfo } from './events.js';
24
24
  import { TypedEventEmitter } from './typed-emitter.js';
25
25
 
26
26
  /** Decision callback: filter discovered cohorts. Default rejects all. */
@@ -30,7 +30,7 @@ export type ShouldJoin = (advert: CohortAdvert) => Promise<boolean>;
30
30
  export type OnProvideUpdate = (info: {
31
31
  cohortId: string;
32
32
  beaconAddress: string;
33
- }) => Promise<SignedBTCR2Update>;
33
+ }) => Promise<SignedBTCR2Update | null>;
34
34
 
35
35
  /** Decision callback: approve or reject aggregated data. */
36
36
  export type OnValidateData = (info: PendingValidation) => Promise<{ approved: boolean }>;
@@ -54,7 +54,7 @@ export interface AggregationParticipantRunnerOptions {
54
54
 
55
55
  /**
56
56
  * Provide a signed BTCR2 update for the cohort.
57
- * REQUIRED no sensible default.
57
+ * REQUIRED - no sensible default.
58
58
  */
59
59
  onProvideUpdate: OnProvideUpdate;
60
60
 
@@ -162,6 +162,7 @@ export class AggregationParticipantRunner extends TypedEventEmitter<AggregationP
162
162
  DISTRIBUTE_AGGREGATED_DATA,
163
163
  AUTHORIZATION_REQUEST,
164
164
  AGGREGATED_NONCE,
165
+ FALLBACK_AUTHORIZATION_REQUEST,
165
166
  ];
166
167
 
167
168
  /** Internal: detach from the transport. Safe to call repeatedly. */
@@ -175,19 +176,12 @@ export class AggregationParticipantRunner extends TypedEventEmitter<AggregationP
175
176
 
176
177
  /**
177
178
  * Single-shot helper: start, join the first cohort that passes `shouldJoin`,
178
- * drive it to completion, and resolve. Convenient for tests and demos.
179
+ * drive it to completion, and resolve. Convenient for tests and demos. The
180
+ * single-cohort special case of {@link joinMatching} (count = 1).
179
181
  */
180
182
  static async joinFirst(
181
183
  options: AggregationParticipantRunnerOptions
182
- ): Promise<{
183
- cohortId: string;
184
- beaconAddress: string;
185
- beaconType: string;
186
- /** DID → base64url update hash. Populated only for CAS beacons. */
187
- casAnnouncement?: Record<string, string>;
188
- /** Merkle inclusion proof for this participant's slot. Populated only for SMT beacons. */
189
- smtProof?: SerializedSMTProof;
190
- }> {
184
+ ): Promise<CohortCompleteInfo> {
191
185
  return new Promise((resolve, reject) => {
192
186
  const runner = new AggregationParticipantRunner(options);
193
187
  runner.once('cohort-complete', (info) => {
@@ -199,6 +193,41 @@ export class AggregationParticipantRunner extends TypedEventEmitter<AggregationP
199
193
  });
200
194
  }
201
195
 
196
+ /**
197
+ * Multi-cohort helper: start, join EVERY cohort whose advert passes
198
+ * `shouldJoin`, drive each to completion in parallel, and resolve once
199
+ * `count` cohorts have completed (the runner stops at that point). The
200
+ * N-cohort generalization of {@link joinFirst}, for a participant that joins
201
+ * several cohorts advertised by one service.
202
+ *
203
+ * For an open-ended, long-lived subscriber (no fixed count), construct an
204
+ * {@link AggregationParticipantRunner} directly, set `shouldJoin`, call
205
+ * `start()`, and listen for `cohort-complete` - the runner already drives
206
+ * any number of cohorts concurrently.
207
+ *
208
+ * @param options Participant runner options (set `shouldJoin` to select cohorts).
209
+ * @param count Number of completed cohorts to collect before resolving.
210
+ * @returns The {@link CohortCompleteInfo} for each completed cohort, in completion order.
211
+ */
212
+ static async joinMatching(
213
+ options: AggregationParticipantRunnerOptions,
214
+ count: number,
215
+ ): Promise<CohortCompleteInfo[]> {
216
+ return new Promise((resolve, reject) => {
217
+ const runner = new AggregationParticipantRunner(options);
218
+ const completed: CohortCompleteInfo[] = [];
219
+ runner.on('cohort-complete', (info) => {
220
+ completed.push(info);
221
+ if(completed.length >= count) {
222
+ runner.stop();
223
+ resolve(completed);
224
+ }
225
+ });
226
+ runner.on('error', reject);
227
+ runner.start().catch(reject);
228
+ });
229
+ }
230
+
202
231
  /**
203
232
  * Internal: handler registration with the transport. Idempotent and safe to call multiple times,
204
233
  * but only registers handlers once.
@@ -213,6 +242,7 @@ export class AggregationParticipantRunner extends TypedEventEmitter<AggregationP
213
242
  this.#transport.registerMessageHandler(this.#did, DISTRIBUTE_AGGREGATED_DATA, this.#handleDistributeData.bind(this));
214
243
  this.#transport.registerMessageHandler(this.#did, AUTHORIZATION_REQUEST, this.#handleAuthorizationRequest.bind(this));
215
244
  this.#transport.registerMessageHandler(this.#did, AGGREGATED_NONCE, this.#handleAggregatedNonce.bind(this));
245
+ this.#transport.registerMessageHandler(this.#did, FALLBACK_AUTHORIZATION_REQUEST, this.#handleFallbackAuthorizationRequest.bind(this));
216
246
  }
217
247
 
218
248
  /**
@@ -275,13 +305,20 @@ export class AggregationParticipantRunner extends TypedEventEmitter<AggregationP
275
305
  if (!info) return;
276
306
  this.emit('cohort-ready', { cohortId, beaconAddress: info.beaconAddress });
277
307
 
278
- // Construct the signed update via caller callback and submit
308
+ // Construct the signed update via caller callback and submit. A null return
309
+ // means the member has no update this round: it declines (cooperative
310
+ // non-inclusion) but stays in the cohort and still signs.
279
311
  const signedUpdate = await this.#onProvideUpdate({
280
312
  cohortId,
281
313
  beaconAddress : info.beaconAddress,
282
314
  });
283
- await this.#sendAll(this.session.submitUpdate(cohortId, signedUpdate));
284
- this.emit('update-submitted', { cohortId });
315
+ if(signedUpdate === null) {
316
+ await this.#sendAll(this.session.declineUpdate(cohortId));
317
+ this.emit('update-declined', { cohortId });
318
+ } else {
319
+ await this.#sendAll(this.session.submitUpdate(cohortId, signedUpdate));
320
+ this.emit('update-submitted', { cohortId });
321
+ }
285
322
  } catch (err) {
286
323
  this.emit('error', err as Error);
287
324
  }
@@ -371,26 +408,71 @@ export class AggregationParticipantRunner extends TypedEventEmitter<AggregationP
371
408
  await this.#sendAll(this.session.generatePartialSignature(cohortId));
372
409
 
373
410
  // Check if we've reached completion
374
- if (this.session.getCohortPhase(cohortId) === ParticipantCohortPhase.Complete) {
375
- const info = this.session.joinedCohorts.get(cohortId);
376
- if (info) {
377
- // Surface the sidecar data the participant will need for future resolutions:
378
- // the CAS Announcement map (CAS beacons) or their SMT inclusion proof.
379
- const validation = this.session.pendingValidations.get(cohortId);
380
- this.emit('cohort-complete', {
381
- cohortId,
382
- beaconAddress : info.beaconAddress,
383
- beaconType : validation?.beaconType ?? '',
384
- casAnnouncement : validation?.casAnnouncement,
385
- smtProof : validation?.smtProof,
386
- });
387
- }
411
+ this.#emitCohortCompleteIfDone(cohortId);
412
+ } catch (err) {
413
+ this.emit('error', err as Error);
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Internal: handler for fallback authorization requests. The service abandoned
419
+ * the optimistic key path; the member authorizes the k-of-n script-path spend
420
+ * of the same beacon transaction (ADR 042). Reuses `onApproveSigning` to gate
421
+ * the decision, signs the fallback, and completes once it has contributed (the
422
+ * service needs only k members).
423
+ * @param {BaseMessage} msg - The received fallback authorization request message.
424
+ * @returns {Promise<void>} Resolves when processing is complete.
425
+ */
426
+ async #handleFallbackAuthorizationRequest(msg: BaseMessage): Promise<void> {
427
+ if (this.#stopped) return;
428
+ try {
429
+ this.session.receive(msg);
430
+
431
+ const cohortId = msg.body?.cohortId;
432
+ if (!cohortId) return;
433
+
434
+ const req = this.session.pendingFallbackRequests.get(cohortId);
435
+ if (!req) return;
436
+ this.emit('fallback-requested', req);
437
+
438
+ const decision = await this.#onApproveSigning(req);
439
+ if (!decision.approved) {
440
+ this.emit('cohort-failed', { cohortId, reason: 'Fallback signing rejected by participant' });
441
+ return;
388
442
  }
443
+
444
+ await this.#sendAll(this.session.approveFallback(cohortId));
445
+ // The member has contributed its fallback signature and is done from its
446
+ // own perspective, regardless of whether the service has yet collected k.
447
+ this.#emitCohortCompleteIfDone(cohortId);
389
448
  } catch (err) {
390
449
  this.emit('error', err as Error);
391
450
  }
392
451
  }
393
452
 
453
+ /**
454
+ * Internal: emit `cohort-complete` with the participant's retained sidecar once
455
+ * the cohort has reached the Complete phase. Surfaces the CAS Announcement map
456
+ * (CAS beacons) or the SMT proof (SMT beacons) the participant keeps for future
457
+ * DID resolution. Read via getValidation (not pendingValidations, which lists
458
+ * only the AwaitingValidation phase) so the sidecar is still available at
459
+ * Complete. Shared by the optimistic and fallback completion paths.
460
+ */
461
+ #emitCohortCompleteIfDone(cohortId: string): void {
462
+ if (this.session.getCohortPhase(cohortId) !== ParticipantCohortPhase.Complete) return;
463
+ const info = this.session.joinedCohorts.get(cohortId);
464
+ if (!info) return;
465
+ const validation = this.session.getValidation(cohortId);
466
+ this.emit('cohort-complete', {
467
+ cohortId,
468
+ beaconAddress : info.beaconAddress,
469
+ beaconType : validation?.beaconType ?? '',
470
+ included : validation?.included ?? true,
471
+ casAnnouncement : validation?.casAnnouncement,
472
+ smtProof : validation?.smtProof,
473
+ });
474
+ }
475
+
394
476
  /**
395
477
  * Internal: send helper to ensure messages are sent sequentially. This is important for protocol
396
478
  * correctness, as some transports may not guarantee message order if sent in parallel.