@did-btcr2/method 0.37.0 → 0.39.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 (245) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/browser.js +47868 -34771
  3. package/dist/browser.mjs +47867 -34770
  4. package/dist/cjs/index.js +462 -6060
  5. package/dist/esm/core/beacon/beacon.js +101 -10
  6. package/dist/esm/core/beacon/beacon.js.map +1 -1
  7. package/dist/esm/core/beacon/fee-estimator.js +12 -27
  8. package/dist/esm/core/beacon/fee-estimator.js.map +1 -1
  9. package/dist/esm/core/did-sender-resolver.js +26 -0
  10. package/dist/esm/core/did-sender-resolver.js.map +1 -0
  11. package/dist/esm/index.js +3 -17
  12. package/dist/esm/index.js.map +1 -1
  13. package/dist/types/core/beacon/beacon.d.ts +59 -3
  14. package/dist/types/core/beacon/beacon.d.ts.map +1 -1
  15. package/dist/types/core/beacon/fee-estimator.d.ts +10 -37
  16. package/dist/types/core/beacon/fee-estimator.d.ts.map +1 -1
  17. package/dist/types/core/did-sender-resolver.d.ts +14 -0
  18. package/dist/types/core/did-sender-resolver.d.ts.map +1 -0
  19. package/dist/types/index.d.ts +2 -16
  20. package/dist/types/index.d.ts.map +1 -1
  21. package/package.json +6 -5
  22. package/src/core/beacon/beacon.ts +131 -12
  23. package/src/core/beacon/fee-estimator.ts +15 -49
  24. package/src/core/did-sender-resolver.ts +25 -0
  25. package/src/index.ts +3 -17
  26. package/dist/esm/core/aggregation/beacon-strategy.js +0 -76
  27. package/dist/esm/core/aggregation/beacon-strategy.js.map +0 -1
  28. package/dist/esm/core/aggregation/cohort.js +0 -316
  29. package/dist/esm/core/aggregation/cohort.js.map +0 -1
  30. package/dist/esm/core/aggregation/conditions.js +0 -102
  31. package/dist/esm/core/aggregation/conditions.js.map +0 -1
  32. package/dist/esm/core/aggregation/errors.js +0 -22
  33. package/dist/esm/core/aggregation/errors.js.map +0 -1
  34. package/dist/esm/core/aggregation/fallback-spend.js +0 -132
  35. package/dist/esm/core/aggregation/fallback-spend.js.map +0 -1
  36. package/dist/esm/core/aggregation/logger.js +0 -15
  37. package/dist/esm/core/aggregation/logger.js.map +0 -1
  38. package/dist/esm/core/aggregation/messages/base.js +0 -36
  39. package/dist/esm/core/aggregation/messages/base.js.map +0 -1
  40. package/dist/esm/core/aggregation/messages/bodies.js +0 -149
  41. package/dist/esm/core/aggregation/messages/bodies.js.map +0 -1
  42. package/dist/esm/core/aggregation/messages/constants.js +0 -37
  43. package/dist/esm/core/aggregation/messages/constants.js.map +0 -1
  44. package/dist/esm/core/aggregation/messages/factories.js +0 -139
  45. package/dist/esm/core/aggregation/messages/factories.js.map +0 -1
  46. package/dist/esm/core/aggregation/messages/guards.js +0 -40
  47. package/dist/esm/core/aggregation/messages/guards.js.map +0 -1
  48. package/dist/esm/core/aggregation/messages/index.js +0 -6
  49. package/dist/esm/core/aggregation/messages/index.js.map +0 -1
  50. package/dist/esm/core/aggregation/participant.js +0 -588
  51. package/dist/esm/core/aggregation/participant.js.map +0 -1
  52. package/dist/esm/core/aggregation/phases.js +0 -45
  53. package/dist/esm/core/aggregation/phases.js.map +0 -1
  54. package/dist/esm/core/aggregation/recovery-policy.js +0 -156
  55. package/dist/esm/core/aggregation/recovery-policy.js.map +0 -1
  56. package/dist/esm/core/aggregation/recovery-spend.js +0 -106
  57. package/dist/esm/core/aggregation/recovery-spend.js.map +0 -1
  58. package/dist/esm/core/aggregation/runner/aggregation-runner.js +0 -74
  59. package/dist/esm/core/aggregation/runner/aggregation-runner.js.map +0 -1
  60. package/dist/esm/core/aggregation/runner/events.js +0 -2
  61. package/dist/esm/core/aggregation/runner/events.js.map +0 -1
  62. package/dist/esm/core/aggregation/runner/index.js +0 -6
  63. package/dist/esm/core/aggregation/runner/index.js.map +0 -1
  64. package/dist/esm/core/aggregation/runner/participant-runner.js +0 -407
  65. package/dist/esm/core/aggregation/runner/participant-runner.js.map +0 -1
  66. package/dist/esm/core/aggregation/runner/service-runner.js +0 -694
  67. package/dist/esm/core/aggregation/runner/service-runner.js.map +0 -1
  68. package/dist/esm/core/aggregation/runner/typed-emitter.js +0 -80
  69. package/dist/esm/core/aggregation/runner/typed-emitter.js.map +0 -1
  70. package/dist/esm/core/aggregation/service.js +0 -774
  71. package/dist/esm/core/aggregation/service.js.map +0 -1
  72. package/dist/esm/core/aggregation/signer.js +0 -33
  73. package/dist/esm/core/aggregation/signer.js.map +0 -1
  74. package/dist/esm/core/aggregation/signing-session.js +0 -195
  75. package/dist/esm/core/aggregation/signing-session.js.map +0 -1
  76. package/dist/esm/core/aggregation/transport/didcomm.js +0 -41
  77. package/dist/esm/core/aggregation/transport/didcomm.js.map +0 -1
  78. package/dist/esm/core/aggregation/transport/error.js +0 -12
  79. package/dist/esm/core/aggregation/transport/error.js.map +0 -1
  80. package/dist/esm/core/aggregation/transport/factory.js +0 -29
  81. package/dist/esm/core/aggregation/transport/factory.js.map +0 -1
  82. package/dist/esm/core/aggregation/transport/http/client.js +0 -350
  83. package/dist/esm/core/aggregation/transport/http/client.js.map +0 -1
  84. package/dist/esm/core/aggregation/transport/http/envelope.js +0 -126
  85. package/dist/esm/core/aggregation/transport/http/envelope.js.map +0 -1
  86. package/dist/esm/core/aggregation/transport/http/errors.js +0 -11
  87. package/dist/esm/core/aggregation/transport/http/errors.js.map +0 -1
  88. package/dist/esm/core/aggregation/transport/http/inbox-buffer.js +0 -45
  89. package/dist/esm/core/aggregation/transport/http/inbox-buffer.js.map +0 -1
  90. package/dist/esm/core/aggregation/transport/http/index.js +0 -12
  91. package/dist/esm/core/aggregation/transport/http/index.js.map +0 -1
  92. package/dist/esm/core/aggregation/transport/http/nonce-cache.js +0 -38
  93. package/dist/esm/core/aggregation/transport/http/nonce-cache.js.map +0 -1
  94. package/dist/esm/core/aggregation/transport/http/protocol.js +0 -28
  95. package/dist/esm/core/aggregation/transport/http/protocol.js.map +0 -1
  96. package/dist/esm/core/aggregation/transport/http/rate-limiter.js +0 -45
  97. package/dist/esm/core/aggregation/transport/http/rate-limiter.js.map +0 -1
  98. package/dist/esm/core/aggregation/transport/http/request-auth.js +0 -100
  99. package/dist/esm/core/aggregation/transport/http/request-auth.js.map +0 -1
  100. package/dist/esm/core/aggregation/transport/http/server.js +0 -481
  101. package/dist/esm/core/aggregation/transport/http/server.js.map +0 -1
  102. package/dist/esm/core/aggregation/transport/http/sse-stream.js +0 -110
  103. package/dist/esm/core/aggregation/transport/http/sse-stream.js.map +0 -1
  104. package/dist/esm/core/aggregation/transport/http/sse-writer.js +0 -25
  105. package/dist/esm/core/aggregation/transport/http/sse-writer.js.map +0 -1
  106. package/dist/esm/core/aggregation/transport/in-memory.js +0 -149
  107. package/dist/esm/core/aggregation/transport/in-memory.js.map +0 -1
  108. package/dist/esm/core/aggregation/transport/index.js +0 -8
  109. package/dist/esm/core/aggregation/transport/index.js.map +0 -1
  110. package/dist/esm/core/aggregation/transport/nostr.js +0 -491
  111. package/dist/esm/core/aggregation/transport/nostr.js.map +0 -1
  112. package/dist/esm/core/aggregation/transport/transport.js +0 -2
  113. package/dist/esm/core/aggregation/transport/transport.js.map +0 -1
  114. package/dist/types/core/aggregation/beacon-strategy.d.ts +0 -56
  115. package/dist/types/core/aggregation/beacon-strategy.d.ts.map +0 -1
  116. package/dist/types/core/aggregation/cohort.d.ts +0 -182
  117. package/dist/types/core/aggregation/cohort.d.ts.map +0 -1
  118. package/dist/types/core/aggregation/conditions.d.ts +0 -86
  119. package/dist/types/core/aggregation/conditions.d.ts.map +0 -1
  120. package/dist/types/core/aggregation/errors.d.ts +0 -14
  121. package/dist/types/core/aggregation/errors.d.ts.map +0 -1
  122. package/dist/types/core/aggregation/fallback-spend.d.ts +0 -94
  123. package/dist/types/core/aggregation/fallback-spend.d.ts.map +0 -1
  124. package/dist/types/core/aggregation/logger.d.ts +0 -22
  125. package/dist/types/core/aggregation/logger.d.ts.map +0 -1
  126. package/dist/types/core/aggregation/messages/base.d.ts +0 -58
  127. package/dist/types/core/aggregation/messages/base.d.ts.map +0 -1
  128. package/dist/types/core/aggregation/messages/bodies.d.ts +0 -177
  129. package/dist/types/core/aggregation/messages/bodies.d.ts.map +0 -1
  130. package/dist/types/core/aggregation/messages/constants.d.ts +0 -31
  131. package/dist/types/core/aggregation/messages/constants.d.ts.map +0 -1
  132. package/dist/types/core/aggregation/messages/factories.d.ts +0 -217
  133. package/dist/types/core/aggregation/messages/factories.d.ts.map +0 -1
  134. package/dist/types/core/aggregation/messages/guards.d.ts +0 -11
  135. package/dist/types/core/aggregation/messages/guards.d.ts.map +0 -1
  136. package/dist/types/core/aggregation/messages/index.d.ts +0 -6
  137. package/dist/types/core/aggregation/messages/index.d.ts.map +0 -1
  138. package/dist/types/core/aggregation/participant.d.ts +0 -162
  139. package/dist/types/core/aggregation/participant.d.ts.map +0 -1
  140. package/dist/types/core/aggregation/phases.d.ts +0 -55
  141. package/dist/types/core/aggregation/phases.d.ts.map +0 -1
  142. package/dist/types/core/aggregation/recovery-policy.d.ts +0 -132
  143. package/dist/types/core/aggregation/recovery-policy.d.ts.map +0 -1
  144. package/dist/types/core/aggregation/recovery-spend.d.ts +0 -94
  145. package/dist/types/core/aggregation/recovery-spend.d.ts.map +0 -1
  146. package/dist/types/core/aggregation/runner/aggregation-runner.d.ts +0 -66
  147. package/dist/types/core/aggregation/runner/aggregation-runner.d.ts.map +0 -1
  148. package/dist/types/core/aggregation/runner/events.d.ts +0 -147
  149. package/dist/types/core/aggregation/runner/events.d.ts.map +0 -1
  150. package/dist/types/core/aggregation/runner/index.d.ts +0 -6
  151. package/dist/types/core/aggregation/runner/index.d.ts.map +0 -1
  152. package/dist/types/core/aggregation/runner/participant-runner.d.ts +0 -122
  153. package/dist/types/core/aggregation/runner/participant-runner.d.ts.map +0 -1
  154. package/dist/types/core/aggregation/runner/service-runner.d.ts +0 -211
  155. package/dist/types/core/aggregation/runner/service-runner.d.ts.map +0 -1
  156. package/dist/types/core/aggregation/runner/typed-emitter.d.ts +0 -41
  157. package/dist/types/core/aggregation/runner/typed-emitter.d.ts.map +0 -1
  158. package/dist/types/core/aggregation/service.d.ts +0 -177
  159. package/dist/types/core/aggregation/service.d.ts.map +0 -1
  160. package/dist/types/core/aggregation/signer.d.ts +0 -50
  161. package/dist/types/core/aggregation/signer.d.ts.map +0 -1
  162. package/dist/types/core/aggregation/signing-session.d.ts +0 -81
  163. package/dist/types/core/aggregation/signing-session.d.ts.map +0 -1
  164. package/dist/types/core/aggregation/transport/didcomm.d.ts +0 -23
  165. package/dist/types/core/aggregation/transport/didcomm.d.ts.map +0 -1
  166. package/dist/types/core/aggregation/transport/error.d.ts +0 -8
  167. package/dist/types/core/aggregation/transport/error.d.ts.map +0 -1
  168. package/dist/types/core/aggregation/transport/factory.d.ts +0 -28
  169. package/dist/types/core/aggregation/transport/factory.d.ts.map +0 -1
  170. package/dist/types/core/aggregation/transport/http/client.d.ts +0 -48
  171. package/dist/types/core/aggregation/transport/http/client.d.ts.map +0 -1
  172. package/dist/types/core/aggregation/transport/http/envelope.d.ts +0 -64
  173. package/dist/types/core/aggregation/transport/http/envelope.d.ts.map +0 -1
  174. package/dist/types/core/aggregation/transport/http/errors.d.ts +0 -9
  175. package/dist/types/core/aggregation/transport/http/errors.d.ts.map +0 -1
  176. package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts +0 -32
  177. package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts.map +0 -1
  178. package/dist/types/core/aggregation/transport/http/index.d.ts +0 -12
  179. package/dist/types/core/aggregation/transport/http/index.d.ts.map +0 -1
  180. package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts +0 -26
  181. package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts.map +0 -1
  182. package/dist/types/core/aggregation/transport/http/protocol.d.ts +0 -53
  183. package/dist/types/core/aggregation/transport/http/protocol.d.ts.map +0 -1
  184. package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts +0 -41
  185. package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts.map +0 -1
  186. package/dist/types/core/aggregation/transport/http/request-auth.d.ts +0 -50
  187. package/dist/types/core/aggregation/transport/http/request-auth.d.ts.map +0 -1
  188. package/dist/types/core/aggregation/transport/http/server.d.ts +0 -110
  189. package/dist/types/core/aggregation/transport/http/server.d.ts.map +0 -1
  190. package/dist/types/core/aggregation/transport/http/sse-stream.d.ts +0 -34
  191. package/dist/types/core/aggregation/transport/http/sse-stream.d.ts.map +0 -1
  192. package/dist/types/core/aggregation/transport/http/sse-writer.d.ts +0 -12
  193. package/dist/types/core/aggregation/transport/http/sse-writer.d.ts.map +0 -1
  194. package/dist/types/core/aggregation/transport/in-memory.d.ts +0 -64
  195. package/dist/types/core/aggregation/transport/in-memory.d.ts.map +0 -1
  196. package/dist/types/core/aggregation/transport/index.d.ts +0 -8
  197. package/dist/types/core/aggregation/transport/index.d.ts.map +0 -1
  198. package/dist/types/core/aggregation/transport/nostr.d.ts +0 -153
  199. package/dist/types/core/aggregation/transport/nostr.d.ts.map +0 -1
  200. package/dist/types/core/aggregation/transport/transport.d.ts +0 -62
  201. package/dist/types/core/aggregation/transport/transport.d.ts.map +0 -1
  202. package/src/core/aggregation/beacon-strategy.ts +0 -142
  203. package/src/core/aggregation/cohort.ts +0 -415
  204. package/src/core/aggregation/conditions.ts +0 -172
  205. package/src/core/aggregation/errors.ts +0 -25
  206. package/src/core/aggregation/fallback-spend.ts +0 -223
  207. package/src/core/aggregation/logger.ts +0 -33
  208. package/src/core/aggregation/messages/base.ts +0 -80
  209. package/src/core/aggregation/messages/bodies.ts +0 -328
  210. package/src/core/aggregation/messages/constants.ts +0 -40
  211. package/src/core/aggregation/messages/factories.ts +0 -297
  212. package/src/core/aggregation/messages/guards.ts +0 -61
  213. package/src/core/aggregation/messages/index.ts +0 -5
  214. package/src/core/aggregation/participant.ts +0 -779
  215. package/src/core/aggregation/phases.ts +0 -91
  216. package/src/core/aggregation/recovery-policy.ts +0 -237
  217. package/src/core/aggregation/recovery-spend.ts +0 -192
  218. package/src/core/aggregation/runner/aggregation-runner.ts +0 -118
  219. package/src/core/aggregation/runner/events.ts +0 -129
  220. package/src/core/aggregation/runner/index.ts +0 -5
  221. package/src/core/aggregation/runner/participant-runner.ts +0 -488
  222. package/src/core/aggregation/runner/service-runner.ts +0 -866
  223. package/src/core/aggregation/runner/typed-emitter.ts +0 -87
  224. package/src/core/aggregation/service.ts +0 -1000
  225. package/src/core/aggregation/signer.ts +0 -67
  226. package/src/core/aggregation/signing-session.ts +0 -290
  227. package/src/core/aggregation/transport/didcomm.ts +0 -59
  228. package/src/core/aggregation/transport/error.ts +0 -13
  229. package/src/core/aggregation/transport/factory.ts +0 -65
  230. package/src/core/aggregation/transport/http/client.ts +0 -409
  231. package/src/core/aggregation/transport/http/envelope.ts +0 -204
  232. package/src/core/aggregation/transport/http/errors.ts +0 -11
  233. package/src/core/aggregation/transport/http/inbox-buffer.ts +0 -53
  234. package/src/core/aggregation/transport/http/index.ts +0 -11
  235. package/src/core/aggregation/transport/http/nonce-cache.ts +0 -43
  236. package/src/core/aggregation/transport/http/protocol.ts +0 -57
  237. package/src/core/aggregation/transport/http/rate-limiter.ts +0 -75
  238. package/src/core/aggregation/transport/http/request-auth.ts +0 -164
  239. package/src/core/aggregation/transport/http/server.ts +0 -615
  240. package/src/core/aggregation/transport/http/sse-stream.ts +0 -121
  241. package/src/core/aggregation/transport/http/sse-writer.ts +0 -23
  242. package/src/core/aggregation/transport/in-memory.ts +0 -178
  243. package/src/core/aggregation/transport/index.ts +0 -7
  244. package/src/core/aggregation/transport/nostr.ts +0 -576
  245. package/src/core/aggregation/transport/transport.ts +0 -79
@@ -1,694 +0,0 @@
1
- var _a;
2
- import { AggregationServiceError } from '../errors.js';
3
- import { COHORT_OPT_IN, FALLBACK_SIGNATURE, NONCE_CONTRIBUTION, SIGNATURE_AUTHORIZATION, SUBMIT_NONINCLUDED, SUBMIT_UPDATE, VALIDATION_ACK, } from '../messages/constants.js';
4
- import { ServiceCohortPhase } from '../phases.js';
5
- import { AggregationService } from '../service.js';
6
- import { TypedEventEmitter } from './typed-emitter.js';
7
- /** Default cadence for re-publishing COHORT_ADVERT until keygen completes: 60 seconds. */
8
- export const DEFAULT_ADVERT_REPEAT_INTERVAL_MS = 60_000;
9
- /**
10
- * High-level facade for running an Aggregation Service over a Transport.
11
- *
12
- * Wires the {@link AggregationService} state machine to a {@link Transport},
13
- * encapsulating message handler registration, outgoing message dispatch,
14
- * and decision callback orchestration.
15
- *
16
- * A single runner is a long-lived multiplexer: it advertises and drives many
17
- * cohorts concurrently over one transport. Each advertised cohort owns an
18
- * independent completion promise and fails in isolation - a stalled or failed
19
- * cohort never settles its siblings (see ADR 040). Use
20
- * {@link AggregationServiceRunner.advertiseCohort} for the multi-cohort path;
21
- * {@link AggregationServiceRunner.run} is a thin single-cohort convenience over
22
- * it.
23
- *
24
- * @example
25
- * ```typescript
26
- * const transport = new NostrTransport({ relays: [RELAY] });
27
- * transport.registerActor(serviceDid, serviceKeys);
28
- *
29
- * const runner = new AggregationServiceRunner({
30
- * transport,
31
- * did: serviceDid,
32
- * keys: serviceKeys,
33
- * onProvideTxData: async ({ cohortId, beaconAddress, signalBytes }) => {
34
- * return await buildBeaconTransaction(beaconAddress, signalBytes, bitcoin);
35
- * },
36
- * });
37
- *
38
- * runner.on('keygen-complete', ({ cohortId, beaconAddress }) => console.log(beaconAddress));
39
- * runner.on('signing-complete', ({ cohortId, signature }) => console.log('done', cohortId));
40
- *
41
- * // Multi-cohort: advertise several cohorts; each completion resolves independently.
42
- * const a = runner.advertiseCohort({ minParticipants: 2, network: 'mutinynet', beaconType: 'CASBeacon' });
43
- * const b = runner.advertiseCohort({ minParticipants: 3, network: 'mutinynet', beaconType: 'SMTBeacon' });
44
- * const [ra, rb] = await Promise.all([a.completion, b.completion]);
45
- *
46
- * // Single-cohort convenience (requires `config` in the options):
47
- * // const result = await runner.run();
48
- * ```
49
- *
50
- * For full manual control, drop down to the underlying state machine via
51
- * `runner.session`. The state machine has no transport coupling and exposes
52
- * every protocol decision as an explicit method.
53
- *
54
- * @class AggregationServiceRunner
55
- */
56
- export class AggregationServiceRunner extends TypedEventEmitter {
57
- /** Direct access to the underlying state machine for advanced use. */
58
- session;
59
- #transport;
60
- #did;
61
- #defaultConfig;
62
- #onOptInReceived;
63
- #onReadyToFinalize;
64
- #onProvideTxData;
65
- #cohortTtlMs;
66
- #phaseTimeoutMs;
67
- #advertRepeatIntervalMs;
68
- #autoFallbackOnStall;
69
- /** Phases during which a stall can be salvaged by the k-of-n fallback (ADR 042). */
70
- static #SIGNING_PHASES = [
71
- ServiceCohortPhase.SigningStarted,
72
- ServiceCohortPhase.NoncesCollected,
73
- ServiceCohortPhase.AwaitingPartialSigs,
74
- ];
75
- /** Per-cohort run state, keyed by cohortId. */
76
- #contexts = new Map();
77
- #handlersRegistered = false;
78
- #stopped = false;
79
- constructor(options) {
80
- super();
81
- this.#transport = options.transport;
82
- this.#did = options.did;
83
- this.#defaultConfig = options.config;
84
- this.#onOptInReceived = options.onOptInReceived ?? (async () => ({ accepted: true }));
85
- this.#onReadyToFinalize = options.onReadyToFinalize ?? (async ({ acceptedCount, minRequired }) => ({
86
- finalize: acceptedCount >= minRequired,
87
- }));
88
- this.#onProvideTxData = options.onProvideTxData;
89
- this.#cohortTtlMs = options.cohortTtlMs;
90
- this.#phaseTimeoutMs = options.phaseTimeoutMs;
91
- this.#advertRepeatIntervalMs = options.advertRepeatIntervalMs ?? DEFAULT_ADVERT_REPEAT_INTERVAL_MS;
92
- this.#autoFallbackOnStall = options.autoFallbackOnStall ?? false;
93
- this.session = new AggregationService({
94
- // The coordinator never signs, so the state machine receives only the
95
- // public half of the operator's keypair (see ADR 038). The full keypair
96
- // remains the operator's transport/communication identity.
97
- did: options.did,
98
- publicKey: options.keys.publicKey,
99
- maxUpdateSizeBytes: options.maxUpdateSizeBytes,
100
- });
101
- }
102
- /** Resolve the {@link RunContext} an inbound message belongs to, by cohortId. */
103
- #contextFor(msg) {
104
- const cohortId = msg.body?.cohortId;
105
- if (!cohortId)
106
- return undefined;
107
- return this.#contexts.get(cohortId);
108
- }
109
- /**
110
- * Drain any silent rejections the state machine recorded for a cohort during
111
- * the most recent receive() and surface them as `message-rejected` events.
112
- */
113
- #drainRejections(ctx) {
114
- for (const r of this.session.drainRejections(ctx.cohortId)) {
115
- this.emit('message-rejected', { cohortId: ctx.cohortId, ...r });
116
- }
117
- }
118
- /**
119
- * Advertise a new cohort and begin driving it to completion. Callable many
120
- * times on one runner; each cohort runs concurrently and independently.
121
- *
122
- * @param config Per-cohort conditions + network (see {@link CohortConfig}).
123
- * @returns The new cohort's id and a `completion` promise that resolves with
124
- * that cohort's {@link AggregationResult} (or rejects if it fails/stalls).
125
- * @throws If the runner has been stopped, or the config is invalid
126
- * (fail-fast via `createCohort`).
127
- */
128
- advertiseCohort(config) {
129
- if (this.#stopped) {
130
- throw new AggregationServiceError('Cannot advertise on a stopped runner.', 'RUNNER_STOPPED', {});
131
- }
132
- this.#registerHandlers();
133
- // createCohort validates the conditions and throws on a bad config before
134
- // any context exists - fail-fast, nothing to clean up.
135
- const cohortId = this.session.createCohort(config);
136
- let resolve;
137
- let reject;
138
- const completion = new Promise((res, rej) => { resolve = res; reject = rej; });
139
- const ctx = {
140
- cohortId,
141
- config,
142
- resolve,
143
- reject,
144
- completion,
145
- finalizing: false,
146
- settled: false,
147
- };
148
- this.#contexts.set(cohortId, ctx);
149
- try {
150
- this.#startTimers(ctx);
151
- // Emit cohort-advertised BEFORE the send so the event fires before any downstream cascade.
152
- const advertMsgs = this.session.advertise(cohortId);
153
- this.#onPhaseMaybeChanged(ctx);
154
- this.emit('cohort-advertised', { cohortId });
155
- // Publish the advert. If advertRepeatIntervalMs > 0 we republish on that
156
- // cadence until this cohort's keygen-complete / fail / stop - works around
157
- // relays that don't backfill historical events to late subscribers.
158
- // Otherwise fall back to a single send.
159
- if (this.#advertRepeatIntervalMs > 0) {
160
- this.#startAdvertRepeat(ctx, advertMsgs);
161
- }
162
- else {
163
- this.#sendAll(advertMsgs).catch(err => this.#failCohort(ctx, err));
164
- }
165
- }
166
- catch (err) {
167
- this.#failCohort(ctx, err);
168
- }
169
- return { cohortId, completion };
170
- }
171
- /**
172
- * Run a single cohort to completion using the `config` supplied in the
173
- * runner options. Thin convenience over {@link advertiseCohort} for the
174
- * single-cohort case (and the path {@link AggregationRunner.solo} rides).
175
- *
176
- * @returns {Promise<AggregationResult>} The final result with signature and signed tx.
177
- */
178
- run() {
179
- if (!this.#defaultConfig) {
180
- return Promise.reject(new AggregationServiceError('run() requires `config` in the runner options; use advertiseCohort(config) to drive cohorts explicitly.', 'MISSING_COHORT_CONFIG', {}));
181
- }
182
- try {
183
- return this.advertiseCohort(this.#defaultConfig).completion;
184
- }
185
- catch (err) {
186
- return Promise.reject(err);
187
- }
188
- }
189
- /**
190
- * Wait for every currently-outstanding cohort to settle and return the
191
- * successful results. Dynamic drain: cohorts advertised while this is pending
192
- * are included, and it resolves only once no cohorts remain. Failed cohorts
193
- * are surfaced via `error` / `cohort-failed` events and their rejected
194
- * `completion` promises; they are omitted from the returned array (this
195
- * method does not throw). Bound long-running cohorts with `cohortTtlMs` /
196
- * `phaseTimeoutMs` or this may never resolve.
197
- *
198
- * @returns {Promise<AggregationResult[]>} Results of the cohorts that completed.
199
- */
200
- async runAll() {
201
- const collected = new Map();
202
- // Capture every completion, including a cohort that is advertised and
203
- // finishes entirely within one drain round (so it never appears in a
204
- // snapshot below).
205
- const onComplete = (result) => { collected.set(result.cohortId, result); };
206
- this.on('signing-complete', onComplete);
207
- try {
208
- // Block until the live set empties; re-snapshot each round to pick up
209
- // cohorts advertised mid-drain.
210
- while (this.#contexts.size > 0) {
211
- await Promise.allSettled([...this.#contexts.values()].map(c => c.completion));
212
- }
213
- }
214
- finally {
215
- this.off('signing-complete', onComplete);
216
- }
217
- return [...collected.values()];
218
- }
219
- /**
220
- * Begin publishing a cohort's advert immediately and on a repeating interval
221
- * until the cohort's advert loop is stopped. Each advert is broadcast (no
222
- * recipient) via the transport's `publishRepeating` primitive.
223
- */
224
- #startAdvertRepeat(ctx, advertMsgs) {
225
- // COHORT_ADVERT is always a single broadcast message in the current
226
- // protocol, but iterate for generality.
227
- const stops = [];
228
- for (const msg of advertMsgs) {
229
- stops.push(this.#transport.publishRepeating(msg, this.#did, this.#advertRepeatIntervalMs));
230
- }
231
- ctx.stopAdvertRepeat = () => {
232
- for (const stop of stops) {
233
- try {
234
- stop();
235
- }
236
- catch { /* ignore */ }
237
- }
238
- };
239
- }
240
- /** Stop a cohort's advert republish loop. Idempotent. */
241
- #stopAdvertRepeating(ctx) {
242
- if (!ctx.stopAdvertRepeat)
243
- return;
244
- const stop = ctx.stopAdvertRepeat;
245
- ctx.stopAdvertRepeat = undefined;
246
- stop();
247
- }
248
- /** Schedule a cohort's TTL + phase timeout when it is advertised. */
249
- #startTimers(ctx) {
250
- if (this.#cohortTtlMs !== undefined) {
251
- ctx.cohortTtlTimer = setTimeout(() => {
252
- const reason = `Cohort ${ctx.cohortId} exceeded TTL of ${this.#cohortTtlMs}ms`;
253
- this.emit('cohort-failed', { cohortId: ctx.cohortId, reason });
254
- this.#failCohort(ctx, new Error(reason));
255
- }, this.#cohortTtlMs);
256
- }
257
- this.#resetPhaseTimer(ctx);
258
- }
259
- /** Reset a cohort's per-phase stall timer. Called when a phase transition is observed. */
260
- #resetPhaseTimer(ctx) {
261
- if (ctx.phaseTimer)
262
- clearTimeout(ctx.phaseTimer);
263
- ctx.phaseTimer = undefined;
264
- if (this.#phaseTimeoutMs === undefined)
265
- return;
266
- ctx.phaseTimer = setTimeout(() => {
267
- // A stall during the optimistic signing round can be salvaged by the k-of-n
268
- // fallback rather than failing the whole cohort (graceful liveness, ADR
269
- // 042) - but only if enabled and not already committed to a path.
270
- const phase = this.session.getCohortPhase(ctx.cohortId);
271
- const inSigning = phase !== undefined && _a.#SIGNING_PHASES.includes(phase);
272
- if (this.#autoFallbackOnStall && inSigning && !ctx.committedPath && !ctx.settled) {
273
- this.triggerFallback(ctx.cohortId).catch(err => this.#failCohort(ctx, err));
274
- return;
275
- }
276
- const reason = `Cohort ${ctx.cohortId} stalled in phase ${ctx.lastObservedPhase ?? '?'} for ${this.#phaseTimeoutMs}ms`;
277
- this.emit('cohort-failed', { cohortId: ctx.cohortId, reason });
278
- this.#failCohort(ctx, new Error(reason));
279
- }, this.#phaseTimeoutMs);
280
- }
281
- /**
282
- * Abandon the optimistic n-of-n key path for a cohort and collect k-of-n
283
- * fallback (script-path) signatures instead (ADR 042). Idempotent and safe
284
- * against the optimistic completion: it commits the cohort to the fallback
285
- * path synchronously (the `committedPath` latch) before sending anything, so a
286
- * late optimistic signature can no longer complete-and-broadcast a competing
287
- * spend of the same UTXO. No-op if the cohort is unknown, already settled, or
288
- * already committed to a path.
289
- *
290
- * Wired automatically to the phase-stall timer when `autoFallbackOnStall` is
291
- * set; otherwise call it from an operator decision (a UI "fall back now"
292
- * action). Throws only if the underlying state machine rejects the transition
293
- * (e.g. signing has not started).
294
- */
295
- async triggerFallback(cohortId) {
296
- const ctx = this.#contexts.get(cohortId);
297
- if (!ctx || ctx.settled || ctx.committedPath)
298
- return;
299
- // startFallbackSigning is synchronous and throws if the cohort is not in a
300
- // signing phase (e.g. a premature operator call). Run it FIRST so a rejected
301
- // transition cannot poison the latch: only after it commits the state machine
302
- // to the fallback do we set committedPath. This still happens synchronously
303
- // before any await, so a concurrent optimistic completion observes the latch
304
- // and stands down - but a bad-phase call leaves the optimistic path intact.
305
- const messages = this.session.startFallbackSigning(cohortId);
306
- ctx.committedPath = 'fallback';
307
- this.#stopAdvertRepeating(ctx);
308
- this.#onPhaseMaybeChanged(ctx);
309
- const sessionId = this.session.getSigningSessionId(cohortId) ?? '';
310
- this.emit('fallback-started', { cohortId, sessionId });
311
- await this.#sendAll(messages);
312
- }
313
- /** Detect a phase change for a cohort since the last observation and reset its phase timer. */
314
- #onPhaseMaybeChanged(ctx) {
315
- const phase = this.session.getCohortPhase(ctx.cohortId);
316
- if (phase !== ctx.lastObservedPhase) {
317
- ctx.lastObservedPhase = phase;
318
- this.#resetPhaseTimer(ctx);
319
- }
320
- }
321
- /** Clear a cohort's timers. Called on completion, stop, and failure. */
322
- #clearTimers(ctx) {
323
- if (ctx.cohortTtlTimer)
324
- clearTimeout(ctx.cohortTtlTimer);
325
- if (ctx.phaseTimer)
326
- clearTimeout(ctx.phaseTimer);
327
- ctx.cohortTtlTimer = undefined;
328
- ctx.phaseTimer = undefined;
329
- }
330
- /**
331
- * Reclaim one cohort's runner-layer bookkeeping: stop its advert loop, clear
332
- * its timers, and drop its {@link RunContext}. Does NOT touch sibling cohorts
333
- * and does NOT detach the shared transport handlers. Leaves the cohort in the
334
- * state machine; whether that cohort's `session` state is also removed is the
335
- * caller's choice (see {@link #completeCohort} vs {@link #failCohort}).
336
- */
337
- #disposeCohort(ctx) {
338
- this.#stopAdvertRepeating(ctx);
339
- this.#clearTimers(ctx);
340
- this.#contexts.delete(ctx.cohortId);
341
- }
342
- /**
343
- * Settle one cohort successfully. Reclaims the runner context but leaves the
344
- * completed cohort in `session` so callers can read its beaconAddress / cohort
345
- * via `session.getCohort(result.cohortId)`; reclaim it with
346
- * `session.removeCohort(cohortId)` when done. Idempotent via `ctx.settled`.
347
- */
348
- #completeCohort(ctx, result) {
349
- if (ctx.settled)
350
- return;
351
- ctx.settled = true;
352
- this.#disposeCohort(ctx);
353
- this.emit('signing-complete', result);
354
- ctx.resolve(result);
355
- }
356
- /**
357
- * Fail one cohort. Reclaims its runner context, drops its now-dead state from
358
- * the state machine, and rejects only its completion; siblings keep running
359
- * and the shared transport handlers stay registered. Idempotent via
360
- * `ctx.settled`.
361
- */
362
- #failCohort(ctx, err) {
363
- if (ctx.settled)
364
- return;
365
- ctx.settled = true;
366
- this.#disposeCohort(ctx);
367
- this.session.removeCohort(ctx.cohortId);
368
- this.emit('error', err);
369
- ctx.reject(err);
370
- }
371
- /**
372
- * Stop a single cohort early without affecting the rest of the runner. Drops
373
- * the cohort's state machine state; its `completion` promise rejects with a
374
- * stopped error.
375
- */
376
- stopCohort(cohortId) {
377
- const ctx = this.#contexts.get(cohortId);
378
- if (!ctx || ctx.settled)
379
- return;
380
- ctx.settled = true;
381
- this.#disposeCohort(ctx);
382
- this.session.removeCohort(cohortId);
383
- ctx.reject(new AggregationServiceError(`Cohort ${cohortId} stopped.`, 'COHORT_STOPPED', { cohortId }));
384
- }
385
- /**
386
- * Stop the whole runner. Fails every outstanding cohort, then detaches the
387
- * shared transport handlers so a restart or a new runner doesn't inherit
388
- * stale dispatch. Safe to call repeatedly.
389
- */
390
- stop() {
391
- this.#stopped = true;
392
- for (const ctx of [...this.#contexts.values()]) {
393
- if (ctx.settled)
394
- continue;
395
- ctx.settled = true;
396
- this.#disposeCohort(ctx);
397
- this.session.removeCohort(ctx.cohortId);
398
- ctx.reject(new AggregationServiceError('Service runner stopped.', 'RUNNER_STOPPED', { cohortId: ctx.cohortId }));
399
- }
400
- this.#contexts.clear();
401
- this.#unregisterHandlers();
402
- }
403
- /** Message types this runner listens for on the transport. */
404
- static #HANDLED_MESSAGE_TYPES = [
405
- COHORT_OPT_IN,
406
- SUBMIT_UPDATE,
407
- SUBMIT_NONINCLUDED,
408
- VALIDATION_ACK,
409
- NONCE_CONTRIBUTION,
410
- SIGNATURE_AUTHORIZATION,
411
- FALLBACK_SIGNATURE,
412
- ];
413
- /**
414
- * Internal: handler registration with the transport. Idempotent. Handlers
415
- * are DID-scoped and cohort-agnostic - one registration serves every cohort
416
- * this runner drives; demux to the right {@link RunContext} happens in each
417
- * handler via the inbound message's cohortId.
418
- */
419
- #registerHandlers() {
420
- if (this.#handlersRegistered)
421
- return;
422
- this.#handlersRegistered = true;
423
- this.#transport.registerMessageHandler(this.#did, COHORT_OPT_IN, this.#handleOptIn.bind(this));
424
- this.#transport.registerMessageHandler(this.#did, SUBMIT_UPDATE, this.#handleSubmitUpdate.bind(this));
425
- // A non-inclusion (decline) is an update-phase response handled identically:
426
- // session.receive() routes by type, and the response gate + distribute
427
- // trigger are shared with SUBMIT_UPDATE.
428
- this.#transport.registerMessageHandler(this.#did, SUBMIT_NONINCLUDED, this.#handleSubmitUpdate.bind(this));
429
- this.#transport.registerMessageHandler(this.#did, VALIDATION_ACK, this.#handleValidationAck.bind(this));
430
- this.#transport.registerMessageHandler(this.#did, NONCE_CONTRIBUTION, this.#handleNonceContribution.bind(this));
431
- this.#transport.registerMessageHandler(this.#did, SIGNATURE_AUTHORIZATION, this.#handleSignatureAuthorization.bind(this));
432
- this.#transport.registerMessageHandler(this.#did, FALLBACK_SIGNATURE, this.#handleFallbackSignature.bind(this));
433
- }
434
- /** Internal: detach from the transport. Safe to call repeatedly. */
435
- #unregisterHandlers() {
436
- if (!this.#handlersRegistered)
437
- return;
438
- this.#handlersRegistered = false;
439
- for (const type of _a.#HANDLED_MESSAGE_TYPES) {
440
- this.#transport.unregisterMessageHandler(this.#did, type);
441
- }
442
- }
443
- /**
444
- * Internal: message handlers for each protocol step. Each handler:
445
- * 1) resolves the cohort the message belongs to (by cohortId); ignores it if unknown
446
- * 2) feeds the message into the state machine via session.receive()
447
- * 3) emits a high-level event (carrying cohortId) for external observers
448
- * 4) checks if the new state triggers any automatic next steps, and if so:
449
- * a) calls the appropriate decision callback(s)
450
- * b) sends any resulting messages from the state machine
451
- * Errors fail only the owning cohort. A stopped runner ignores messages.
452
- * @param {BaseMessage} msg - The incoming message to handle.
453
- * @returns {Promise<void>} Resolves when handling is complete.
454
- */
455
- async #handleOptIn(msg) {
456
- if (this.#stopped)
457
- return;
458
- const ctx = this.#contextFor(msg);
459
- if (!ctx)
460
- return;
461
- try {
462
- this.session.receive(msg);
463
- this.#drainRejections(ctx);
464
- this.#onPhaseMaybeChanged(ctx);
465
- const optIn = this.session.pendingOptIns(ctx.cohortId).get(msg.from);
466
- if (!optIn)
467
- return;
468
- // PendingOptIn already carries cohortId, so this event is cohort-identified.
469
- this.emit('opt-in-received', optIn);
470
- // Register peer key for encrypted messaging
471
- if (optIn.communicationPk) {
472
- this.#transport.registerPeer(msg.from, optIn.communicationPk);
473
- }
474
- const decision = await this.#onOptInReceived(optIn);
475
- if (!decision.accepted)
476
- return;
477
- // Don't accept past the advertised maxParticipants: acceptParticipant
478
- // would throw COHORT_FULL and fail the cohort. Silently ignore the surplus
479
- // opt-in (the cohort is full).
480
- const maxParticipants = ctx.config.maxParticipants;
481
- const cohortNow = this.session.getCohort(ctx.cohortId);
482
- if (maxParticipants !== undefined && cohortNow && cohortNow.participants.length >= maxParticipants) {
483
- return;
484
- }
485
- await this.#sendAll(this.session.acceptParticipant(ctx.cohortId, msg.from));
486
- this.emit('participant-accepted', { cohortId: ctx.cohortId, participantDid: msg.from });
487
- // Check if it's time to finalize. The per-cohort `finalizing` flag is set
488
- // synchronously before the first await so concurrent opt-in handlers for
489
- // the same cohort observe it and skip - otherwise two handlers could both
490
- // pass the minParticipants check and both call finalizeKeygen, the second
491
- // of which would throw (phase mismatch).
492
- const cohort = this.session.getCohort(ctx.cohortId);
493
- if (cohort.participants.length >= ctx.config.minParticipants && !ctx.finalizing) {
494
- ctx.finalizing = true;
495
- const finalizeDecision = await this.#onReadyToFinalize({
496
- acceptedCount: cohort.participants.length,
497
- minRequired: ctx.config.minParticipants,
498
- });
499
- if (!finalizeDecision.finalize) {
500
- // Operator declined - reset the flag so a later opt-in can retry.
501
- ctx.finalizing = false;
502
- return;
503
- }
504
- // finalizeKeygen() computes the beacon address synchronously
505
- // emit BEFORE awaiting sendAll. Otherwise the downstream cascade
506
- // (which can run all the way to signing-complete) would resolve the
507
- // cohort's completion promise before this event fires.
508
- const readyMsgs = this.session.finalizeKeygen(ctx.cohortId);
509
- // Keygen done - stop re-advertising the cohort. New participants
510
- // arriving after this point would be rejected anyway.
511
- this.#stopAdvertRepeating(ctx);
512
- this.emit('keygen-complete', {
513
- cohortId: ctx.cohortId,
514
- beaconAddress: cohort.beaconAddress,
515
- });
516
- await this.#sendAll(readyMsgs);
517
- }
518
- }
519
- catch (err) {
520
- this.#failCohort(ctx, err);
521
- }
522
- }
523
- /**
524
- * Handler for receiving participant updates. When all updates are received, automatically builds
525
- * and distributes the data for validation.
526
- * @param {BaseMessage} msg - The incoming message to handle.
527
- * @returns {Promise<void>} Resolves when handling is complete.
528
- */
529
- async #handleSubmitUpdate(msg) {
530
- if (this.#stopped)
531
- return;
532
- const ctx = this.#contextFor(msg);
533
- if (!ctx)
534
- return;
535
- try {
536
- this.session.receive(msg);
537
- this.#drainRejections(ctx);
538
- this.#onPhaseMaybeChanged(ctx);
539
- this.emit('update-received', { cohortId: ctx.cohortId, participantDid: msg.from });
540
- // When all updates collected, build and distribute
541
- if (this.session.getCohortPhase(ctx.cohortId) === ServiceCohortPhase.UpdatesCollected) {
542
- const distributeMsgs = this.session.buildAndDistribute(ctx.cohortId);
543
- this.emit('data-distributed', { cohortId: ctx.cohortId });
544
- await this.#sendAll(distributeMsgs);
545
- }
546
- }
547
- catch (err) {
548
- this.#failCohort(ctx, err);
549
- }
550
- }
551
- /**
552
- * Handler for receiving validation acknowledgments. When all validations are received,
553
- * automatically requests tx data and starts signing.
554
- * @param {BaseMessage} msg - The incoming message to handle.
555
- * @returns {Promise<void>} Resolves when handling is complete.
556
- */
557
- async #handleValidationAck(msg) {
558
- if (this.#stopped)
559
- return;
560
- const ctx = this.#contextFor(msg);
561
- if (!ctx)
562
- return;
563
- try {
564
- this.session.receive(msg);
565
- this.#drainRejections(ctx);
566
- this.#onPhaseMaybeChanged(ctx);
567
- const approved = !!msg.body?.approved;
568
- this.emit('validation-received', { cohortId: ctx.cohortId, participantDid: msg.from, approved });
569
- const phase = this.session.getCohortPhase(ctx.cohortId);
570
- // A participant rejection flips the cohort to Failed. Emit a structured
571
- // event so the runner/caller sees the failure instead of the cohort
572
- // silently stalling.
573
- if (phase === ServiceCohortPhase.Failed) {
574
- const reason = `Validation rejected by participant ${msg.from}`;
575
- this.emit('cohort-failed', { cohortId: ctx.cohortId, reason });
576
- this.#failCohort(ctx, new Error(reason));
577
- return;
578
- }
579
- // When all validations received, request tx data and start signing
580
- if (phase === ServiceCohortPhase.Validated) {
581
- const cohort = this.session.getCohort(ctx.cohortId);
582
- const txData = await this.#onProvideTxData({
583
- cohortId: ctx.cohortId,
584
- beaconAddress: cohort.beaconAddress,
585
- signalBytes: cohort.signalBytes,
586
- });
587
- const authMsgs = this.session.startSigning(ctx.cohortId, txData);
588
- const sessionId = this.session.getSigningSessionId(ctx.cohortId) ?? '';
589
- this.emit('signing-started', { cohortId: ctx.cohortId, sessionId });
590
- await this.#sendAll(authMsgs);
591
- }
592
- }
593
- catch (err) {
594
- this.#failCohort(ctx, err);
595
- }
596
- }
597
- /**
598
- * Handler for receiving nonce contributions. When all nonces are received, sends the aggregated
599
- * nonce back to the cohort.
600
- * @param {BaseMessage} msg - The incoming message to handle.
601
- * @returns {Promise<void>} Resolves when handling is complete.
602
- */
603
- async #handleNonceContribution(msg) {
604
- if (this.#stopped)
605
- return;
606
- const ctx = this.#contextFor(msg);
607
- if (!ctx)
608
- return;
609
- try {
610
- this.session.receive(msg);
611
- this.#drainRejections(ctx);
612
- this.#onPhaseMaybeChanged(ctx);
613
- this.emit('nonce-received', { cohortId: ctx.cohortId, participantDid: msg.from });
614
- // When all nonces collected, send aggregated nonce
615
- if (this.session.getCohortPhase(ctx.cohortId) === ServiceCohortPhase.NoncesCollected) {
616
- await this.#sendAll(this.session.sendAggregatedNonce(ctx.cohortId));
617
- }
618
- }
619
- catch (err) {
620
- this.#failCohort(ctx, err);
621
- }
622
- }
623
- /**
624
- * Handler for receiving signature authorizations. When all partial signatures are received, the
625
- * session automatically completes; the final result is emitted and the cohort's completion
626
- * promise resolves.
627
- * @param {BaseMessage} msg - The incoming message to handle.
628
- * @returns {Promise<void>} Resolves when handling is complete.
629
- */
630
- async #handleSignatureAuthorization(msg) {
631
- if (this.#stopped)
632
- return;
633
- const ctx = this.#contextFor(msg);
634
- if (!ctx)
635
- return;
636
- try {
637
- this.session.receive(msg);
638
- this.#drainRejections(ctx);
639
- this.#onPhaseMaybeChanged(ctx);
640
- // If the cohort already committed to the fallback path, ignore a late
641
- // optimistic completion: only one path may finalize the single beacon UTXO.
642
- if (ctx.committedPath === 'fallback')
643
- return;
644
- // The state machine auto-completes when all partial sigs received.
645
- const result = this.session.getResult(ctx.cohortId);
646
- if (result) {
647
- ctx.committedPath = 'optimistic';
648
- this.#completeCohort(ctx, result);
649
- }
650
- }
651
- catch (err) {
652
- this.#failCohort(ctx, err);
653
- }
654
- }
655
- /**
656
- * Handler for receiving fallback (k-of-n script-path) signatures. The state
657
- * machine assembles and finalizes the fallback spend once k valid signatures
658
- * are in; the result is then emitted and the cohort's completion resolves. The
659
- * cohort is already committed to the fallback path (via {@link triggerFallback}).
660
- * @param {BaseMessage} msg - The incoming FALLBACK_SIGNATURE message.
661
- * @returns {Promise<void>} Resolves when handling is complete.
662
- */
663
- async #handleFallbackSignature(msg) {
664
- if (this.#stopped)
665
- return;
666
- const ctx = this.#contextFor(msg);
667
- if (!ctx)
668
- return;
669
- try {
670
- this.session.receive(msg);
671
- this.#drainRejections(ctx);
672
- this.#onPhaseMaybeChanged(ctx);
673
- const result = this.session.getResult(ctx.cohortId);
674
- if (result) {
675
- this.#completeCohort(ctx, result);
676
- }
677
- }
678
- catch (err) {
679
- this.#failCohort(ctx, err);
680
- }
681
- }
682
- /**
683
- * Internal: helper to send all messages sequentially. Catches and propagates errors.
684
- * @param {BaseMessage[]} msgs - The messages to send.
685
- * @returns {Promise<void>} Resolves when all messages have been sent.
686
- */
687
- async #sendAll(msgs) {
688
- for (const m of msgs) {
689
- await this.#transport.sendMessage(m, this.#did, m.to);
690
- }
691
- }
692
- }
693
- _a = AggregationServiceRunner;
694
- //# sourceMappingURL=service-runner.js.map