@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 +1 @@
1
- {"version":3,"file":"beacon.d.ts","sourceRoot":"","sources":["../../../../src/core/beacon/beacon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,EAA4D,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC1G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAKnE;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,iEAAiE;AACjE,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAInF,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,UAAU,GAClB,mBAAmB,CAWrB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAE,UAAU,GAClB,MAAM,CAKR;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kGAAkG;IAClG,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,EAAE,EAAE,WAAW,CAAC;IAChB,6EAA6E;IAC7E,cAAc,EAAE,UAAU,EAAE,CAAC;IAC7B,uDAAuD;IACvD,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,UAAU,EAAE,mBAAmB,CAAC;CACjC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,UAAU,GAAG,UAAU,CAElE;AA4BD;;;;;;;;;;;GAWG;AACH,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,0EAA0E;IAC1E,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,cAAc,EAAE,UAAU,CAAC;IAC3B,8DAA8D;IAC9D,WAAW,EAAE,UAAU,CAAC;IACxB,yDAAyD;IACzD,OAAO,EAAE,iBAAiB,CAAC;IAC3B,iEAAiE;IACjE,OAAO,EAAE,UAAU,CAAC;IACpB,qDAAqD;IACrD,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,GAAG,OAAO,CAAC,YAAY,CAAC,CAwCxB;AAkFD;;;;;;;;;;;;;;;;GAgBG;AACH,8BAAsB,iBAAiB;IACrC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;gBAEpB,OAAO,EAAE,aAAa;IAIlC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,CACrB,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,EAC5B,OAAO,EAAE,WAAW,GACnB,mBAAmB;IAEtB;;;;;;;;;OASG;IACH,QAAQ,CAAC,eAAe,CACtB,YAAY,EAAE,iBAAiB,EAC/B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,iBAAiB,EAC1B,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,iBAAiB,CAAC;IAE7B;;;;;;;;;;;;;;;;;OAiBG;cACa,qBAAqB,CACnC,WAAW,EAAE,UAAU,EACvB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,iBAAiB,EAC1B,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,MAAM,CAAC;IAWlB;;;;;;;;;OASG;cACa,kBAAkB,CAAC,IAAI,EAAE;QACvC,WAAW,EAAE,UAAU,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,WAAW,CAAC;QAClB,WAAW,EAAE,UAAU,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,iBAAiB,CAAC;QAC3B,YAAY,EAAE,YAAY,CAAC;KAC5B,GAAG,OAAO,CAAC,YAAY,CAAC;IA0EzB;;;OAGG;cACa,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOtF;;OAEG;cACa,cAAc,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAG5F"}
1
+ {"version":3,"file":"beacon.d.ts","sourceRoot":"","sources":["../../../../src/core/beacon/beacon.ts"],"names":[],"mappings":"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"}
@@ -7,7 +7,7 @@ import type { BroadcastOptions } from './beacon.js';
7
7
  import { SinglePartyBeacon } from './beacon.js';
8
8
  import type { BeaconService, BeaconSignal, CasPublishFn } from './interfaces.js';
9
9
  /**
10
- * CAS-specific broadcast options extends {@link BroadcastOptions} with an optional
10
+ * CAS-specific broadcast options: extends {@link BroadcastOptions} with an optional
11
11
  * `casPublish` callback used to publish the CAS Announcement off-chain after the
12
12
  * OP_RETURN signal is broadcast.
13
13
  */
@@ -18,7 +18,7 @@ export interface CASBroadcastOptions extends BroadcastOptions {
18
18
  * Implements {@link https://dcdpr.github.io/did-btcr2/terminology.html#cas-beacon | CAS Beacon}.
19
19
  *
20
20
  * A CAS (Content-Addressed Store) Beacon aggregates updates for multiple DIDs
21
- * into a single CAS Announcement a mapping of DIDs to their update hashes.
21
+ * into a single CAS Announcement: a mapping of DIDs to their update hashes.
22
22
  * The hash of the CAS Announcement is broadcast on-chain via OP_RETURN.
23
23
  * During resolution, the CAS Announcement is retrieved from the sidecar (or CAS)
24
24
  * and used to look up the individual signed update for the DID being resolved.
@@ -62,7 +62,7 @@ export interface BeaconSignal {
62
62
  * The method package defines this type; the api layer provides the implementation
63
63
  * (e.g., via CasApi.publish backed by IPFS/Helia).
64
64
  *
65
- * @param announcement The CAS Announcement object (DID update hash mapping).
65
+ * @param announcement The CAS Announcement object (DID to update hash mapping).
66
66
  */
67
67
  export type CasPublishFn = (announcement: Record<string, string>) => Promise<void>;
68
68
  //# sourceMappingURL=interfaces.d.ts.map
@@ -99,7 +99,7 @@ export interface BeaconProcessResult {
99
99
  export declare class Resolver {
100
100
  #private;
101
101
  /**
102
- * @internal Use {@link DidBtcr2.resolve} to create instances.
102
+ * @internal Use {@link DidBtcr2.resolve} to create instances.
103
103
  */
104
104
  constructor(didComponents: DidComponents, sidecarData: SidecarData, currentDocument: DidDocument | null, options?: {
105
105
  versionId?: string;
@@ -153,8 +153,8 @@ export declare class Resolver {
153
153
  private static applyUpdate;
154
154
  /**
155
155
  * Advance the state machine. Returns either:
156
- * - `{ status: 'action-required', needs }` caller must provide data via {@link provide}
157
- * - `{ status: 'resolved', result }` resolution complete
156
+ * - `{ status: 'action-required', needs }` - caller must provide data via {@link provide}
157
+ * - `{ status: 'resolved', result }` - resolution complete
158
158
  *
159
159
  * Analogous to Rust's `Resolver::resolve()`.
160
160
  */
@@ -93,7 +93,7 @@ export interface UpdaterParams {
93
93
  beaconService: BeaconService;
94
94
  }
95
95
  /**
96
- * Sans-I/O state machine for did:btcr2 updates the counterpart to {@link Resolver}.
96
+ * Sans-I/O state machine for did:btcr2 updates: the counterpart to {@link Resolver}.
97
97
  *
98
98
  * Created by {@link DidBtcr2.update} (the factory). The caller drives the update by
99
99
  * repeatedly calling {@link advance} and {@link provide}:
@@ -139,7 +139,7 @@ export interface UpdaterParams {
139
139
  export declare class Updater {
140
140
  #private;
141
141
  /**
142
- * @internal Use {@link DidBtcr2.update} to create instances.
142
+ * @internal Use {@link DidBtcr2.update} to create instances.
143
143
  */
144
144
  constructor(params: UpdaterParams);
145
145
  /**
@@ -175,8 +175,8 @@ export declare class Updater {
175
175
  static announce(beaconService: BeaconService, update: SignedBTCR2Update, signer: Signer, bitcoin: BitcoinConnection): Promise<SignedBTCR2Update>;
176
176
  /**
177
177
  * Advance the state machine. Returns either:
178
- * - `{ status: 'action-required', needs }` caller must provide data via {@link provide}
179
- * - `{ status: 'complete', result }` update is signed and broadcast
178
+ * - `{ status: 'action-required', needs }` caller must provide data via {@link provide}
179
+ * - `{ status: 'complete', result }` update is signed and broadcast
180
180
  */
181
181
  advance(): UpdaterState;
182
182
  /**
@@ -74,8 +74,8 @@ export declare class DidBtcr2 implements DidMethod {
74
74
  *
75
75
  * Factory method that validates the update parameters and returns a sans-I/O
76
76
  * {@link Updater} state machine. The caller drives the updater through its
77
- * phases (Construct Sign Broadcast Complete) by calling `advance()` and
78
- * `provide()`. The method package performs **zero I/O** signing key retrieval
77
+ * phases (Construct -> Sign -> Broadcast -> Complete) by calling `advance()` and
78
+ * `provide()`. The method package performs **zero I/O**: signing key retrieval
79
79
  * (or KMS delegation) and the on-chain broadcast are the caller's responsibility.
80
80
  *
81
81
  * For a fully-wired version with Bitcoin broadcast and key handling, see
@@ -2,6 +2,9 @@ export * from './core/aggregation/service.js';
2
2
  export * from './core/aggregation/participant.js';
3
3
  export * from './core/aggregation/signer.js';
4
4
  export * from './core/aggregation/conditions.js';
5
+ export * from './core/aggregation/recovery-policy.js';
6
+ export * from './core/aggregation/recovery-spend.js';
7
+ export * from './core/aggregation/fallback-spend.js';
5
8
  export * from './core/aggregation/cohort.js';
6
9
  export * from './core/aggregation/signing-session.js';
7
10
  export * from './core/aggregation/phases.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AAGnD,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iCAAiC,CAAC;AAChD,cAAc,yBAAyB,CAAC;AAGxC,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,uCAAuC,CAAC;AACtD,cAAc,sCAAsC,CAAC;AACrD,cAAc,sCAAsC,CAAC;AACrD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AAGnD,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iCAAiC,CAAC;AAChD,cAAc,yBAAyB,CAAC;AAGxC,cAAc,gBAAgB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@did-btcr2/method",
3
- "version": "0.36.0",
3
+ "version": "0.37.0",
4
4
  "type": "module",
5
5
  "description": "Reference implementation for the did:btcr2 DID method written in TypeScript and JavaScript. did:btcr2 is a censorship resistant DID Method using the Bitcoin blockchain as a Verifiable Data Registry to announce changes to the DID document. This is the core method implementation for the did-btcr2-js monorepo.",
6
6
  "main": "./dist/cjs/index.js",
@@ -80,11 +80,11 @@
80
80
  "helia": "^5.5.1",
81
81
  "multiformats": "^13.4.2",
82
82
  "nostr-tools": "^2.23.3",
83
- "@did-btcr2/smt": "^0.3.0",
83
+ "@did-btcr2/bitcoin": "^0.6.0",
84
84
  "@did-btcr2/common": "^9.1.0",
85
- "@did-btcr2/keypair": "^0.13.1",
85
+ "@did-btcr2/smt": "^0.3.0",
86
86
  "@did-btcr2/cryptosuite": "^8.0.0",
87
- "@did-btcr2/bitcoin": "^0.6.0"
87
+ "@did-btcr2/keypair": "^0.13.1"
88
88
  },
89
89
  "devDependencies": {
90
90
  "@eslint/js": "^9.39.4",
@@ -43,13 +43,17 @@ export interface AggregateBeaconStrategy {
43
43
 
44
44
  /**
45
45
  * Participant: verify the aggregated data they received reflects their own
46
- * submitted update. Pure function returns matches + sidecar fields for
47
- * the caller to store.
46
+ * slot. A submitter (`included: true`) checks its update is included; a
47
+ * decliner (`included: false`, cooperative non-inclusion) checks its
48
+ * non-inclusion slot instead (absence from the CAS map, or an SMT
49
+ * non-inclusion proof). `submittedUpdate`/`expectedHash` are present only for a
50
+ * submitter. Pure function: returns matches + sidecar fields for the caller.
48
51
  */
49
52
  validateParticipantView(params: {
50
53
  participantDid: string;
51
- submittedUpdate: SignedBTCR2Update;
52
- expectedHash: string;
54
+ included: boolean;
55
+ submittedUpdate?: SignedBTCR2Update;
56
+ expectedHash?: string;
53
57
  body: BaseBody;
54
58
  }): BeaconValidationResult;
55
59
  }
@@ -65,9 +69,13 @@ const CAS_STRATEGY: AggregateBeaconStrategy = {
65
69
  return { casAnnouncement: cohort.casAnnouncement };
66
70
  },
67
71
 
68
- validateParticipantView({ participantDid, expectedHash, body }) {
72
+ validateParticipantView({ participantDid, included, expectedHash, body }) {
69
73
  const casAnnouncement = body.casAnnouncement;
70
74
  if(!casAnnouncement) return { matches: false };
75
+ if(!included) {
76
+ // Cooperative non-inclusion: the decliner must be ABSENT from the map.
77
+ return { matches: !(participantDid in casAnnouncement), casAnnouncement };
78
+ }
71
79
  return {
72
80
  matches : casAnnouncement[participantDid] === expectedHash,
73
81
  casAnnouncement,
@@ -87,9 +95,20 @@ const SMT_STRATEGY: AggregateBeaconStrategy = {
87
95
  return { smtProof: proof as unknown as Record<string, unknown> | undefined };
88
96
  },
89
97
 
90
- validateParticipantView({ participantDid, submittedUpdate, body }) {
98
+ validateParticipantView({ participantDid, included, submittedUpdate, body }) {
91
99
  const smtProof = body.smtProof as unknown as SerializedSMTProof | undefined;
92
- if(!smtProof?.updateId || !smtProof?.nonce) return { matches: false };
100
+ const index = didToIndex(participantDid);
101
+
102
+ if(!included) {
103
+ // Cooperative non-inclusion: the proof has a nonce but no updateId by
104
+ // construction. The leaf is SHA-256(SHA-256(nonce)); do NOT run the
105
+ // inclusion-only updateId guard (a missing updateId is correct here).
106
+ if(!smtProof?.nonce || smtProof?.updateId) return { matches: false, smtProof };
107
+ const candidateHash = blockHash(blockHash(base64UrlToHash(smtProof.nonce)));
108
+ return { matches: verifySerializedProof(smtProof, index, candidateHash), smtProof };
109
+ }
110
+
111
+ if(!smtProof?.updateId || !smtProof?.nonce || !submittedUpdate) return { matches: false, smtProof };
93
112
  // Verify updateId matches the canonicalized update hash. Proof hash fields
94
113
  // are base64url (no padding) per the SMT Proof spec.
95
114
  const canonicalBytes = new TextEncoder().encode(canonicalize(submittedUpdate as unknown as Record<string, unknown>));
@@ -97,8 +116,7 @@ const SMT_STRATEGY: AggregateBeaconStrategy = {
97
116
  if(smtProof.updateId !== expectedUpdateId) {
98
117
  return { matches: false, smtProof };
99
118
  }
100
- // Verify Merkle inclusion
101
- const index = didToIndex(participantDid);
119
+ // Verify Merkle inclusion. The leaf is SHA-256(SHA-256(nonce) || SHA-256(update)).
102
120
  const candidateHash = blockHash(blockHash(base64UrlToHash(smtProof.nonce)), base64UrlToHash(smtProof.updateId));
103
121
  return {
104
122
  matches : verifySerializedProof(smtProof, index, candidateHash),
@@ -4,11 +4,13 @@ import type { SignedBTCR2Update } from '@did-btcr2/cryptosuite';
4
4
  import type { SerializedSMTProof, TreeEntry } from '@did-btcr2/smt';
5
5
  import { BTCR2MerkleTree } from '@did-btcr2/smt';
6
6
  import { schnorr } from '@noble/curves/secp256k1.js';
7
- import { hexToBytes, randomBytes } from '@noble/hashes/utils';
7
+ import { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils';
8
8
  import { p2tr } from '@scure/btc-signer';
9
9
  import { keyAggExport, keyAggregate, sortKeys } from '@scure/btc-signer/musig2';
10
10
  import type { CASAnnouncement } from '../types.js';
11
11
  import { AggregationCohortError } from './errors.js';
12
+ import type { FundingModel } from './recovery-policy.js';
13
+ import { DEFAULT_FUNDING_MODEL, buildRecoveryLeaves, resolveFallbackThreshold } from './recovery-policy.js';
12
14
 
13
15
  export type AggregationCohortParams = {
14
16
  id?: string;
@@ -16,14 +18,22 @@ export type AggregationCohortParams = {
16
18
  minParticipants?: number;
17
19
  network: string;
18
20
  beaconType?: string;
21
+ /** Operator recovery key, x-only (32 bytes). Required to compute the beacon address. */
22
+ recoveryKey?: Uint8Array;
23
+ /** Relative-timelock (BIP-68 nSequence) before recovery is spendable. Required to compute the beacon address. */
24
+ recoverySequence?: number;
25
+ /** Funding model governing the recovery leaves. Defaults to 'operator-funded'. */
26
+ fundingModel?: FundingModel;
27
+ /** Advertised k of the k-of-n fallback leaf. Absent defaults to n-1 at address computation. */
28
+ fallbackThreshold?: number;
19
29
  };
20
30
 
21
31
  /**
22
- * Represents an Aggregation Cohort a set of Aggregation Participants who
32
+ * Represents an Aggregation Cohort: a set of Aggregation Participants who
23
33
  * submitted cryptographic material to an Aggregation Service to coordinate
24
34
  * signing of a shared n-of-n MuSig2 Bitcoin transaction.
25
35
  *
26
- * This is a pure data class it holds cohort state and provides computation
36
+ * This is a pure data class: it holds cohort state and provides computation
27
37
  * helpers (key aggregation, CAS Announcement building, SMT tree building).
28
38
  * It performs no I/O and emits no messages. Both AggregationService and
29
39
  * AggregationParticipant create their own AggregationCohort instances to
@@ -51,8 +61,8 @@ export class AggregationCohort {
51
61
  participants: Array<string> = [];
52
62
 
53
63
  /**
54
- * Mapping from participant DID their compressed secp256k1 public key.
55
- * Distinct from {@link cohortKeys} (which is sorted per BIP-327) this lets
64
+ * Mapping from participant DID to their compressed secp256k1 public key.
65
+ * Distinct from {@link cohortKeys} (which is sorted per BIP-327): this lets
56
66
  * callers look up a participant's key without knowing their position in the
57
67
  * sorted array. Populated by the service at `acceptParticipant` time.
58
68
  */
@@ -62,19 +72,51 @@ export class AggregationCohort {
62
72
  #cohortKeys: Array<Uint8Array> = [];
63
73
 
64
74
  /**
65
- * BIP-341 TapTweak `taggedHash("TapTweak", internalPubkey)` for a key-path-only
66
- * Taproot output. Despite prior naming, this is NOT a Merkle root: key-path-only
67
- * spends have no script tree.
75
+ * BIP-341 TapTweak scalar: `taggedHash("TapTweak", internalPubkey || tapMerkleRoot)`.
76
+ * The beacon output is an internal key (the MuSig2 aggregate) plus a script
77
+ * tree (the recovery leaves), so the tweak commits to the tree's Merkle root.
78
+ * The MuSig2 signing session applies this as an x-only tweak; a value that does
79
+ * not match the root the funded address was derived from silently yields an
80
+ * invalid key-path signature.
68
81
  */
69
82
  tapTweak: Uint8Array = new Uint8Array();
70
83
 
71
- /** The n-of-n MuSig2 Taproot beacon address. */
84
+ /** The n-of-n MuSig2 aggregate internal key, x-only (32 bytes), set by computeBeaconAddress(). */
85
+ internalKey: Uint8Array = new Uint8Array();
86
+
87
+ /** BIP-341 Taproot Merkle root of the recovery script tree, set by computeBeaconAddress(). */
88
+ tapMerkleRoot: Uint8Array = new Uint8Array();
89
+
90
+ /** Operator recovery key, x-only (32 bytes). Used to build the CSV recovery leaf. */
91
+ recoveryKey?: Uint8Array;
92
+
93
+ /** Relative-timelock (BIP-68 nSequence) before the recovery leaf is spendable. */
94
+ recoverySequence?: number;
95
+
96
+ /** Funding model governing the recovery leaves (default 'operator-funded'). */
97
+ fundingModel: FundingModel;
98
+
99
+ /**
100
+ * Advertised k of the k-of-n fallback leaf, or undefined to default to n-1 at
101
+ * address computation. Read the resolved value via {@link effectiveFallbackThreshold}.
102
+ */
103
+ fallbackThreshold?: number;
104
+
105
+ /** The Taproot beacon address: key path is the MuSig2 aggregate, script path is fallback + recovery. */
72
106
  beaconAddress: string = '';
73
107
 
74
108
  /** Pending DID updates submitted by participants, keyed by DID. */
75
109
  pendingUpdates: Map<string, SignedBTCR2Update> = new Map();
76
110
 
77
- /** CAS Beacon Announcement Map (DID → updateHash), set by buildCASAnnouncement(). */
111
+ /**
112
+ * Participant DIDs that explicitly declined to submit an update this round
113
+ * (cooperative non-inclusion). A decliner is absent from the CAS Announcement
114
+ * Map and carries a non-inclusion leaf in the SMT, yet still signs. Kept
115
+ * disjoint from {@link pendingUpdates} so CAS correctness holds by construction.
116
+ */
117
+ nonIncluded: Set<string> = new Set();
118
+
119
+ /** CAS Beacon Announcement Map (DID to updateHash), set by buildCASAnnouncement(). */
78
120
  casAnnouncement?: CASAnnouncement;
79
121
 
80
122
  /** Per-participant SMT proofs, set by buildSMTTree(). */
@@ -89,7 +131,7 @@ export class AggregationCohort {
89
131
  /** Set of participant DIDs that have rejected the aggregated data. */
90
132
  validationRejections: Set<string> = new Set();
91
133
 
92
- constructor({ id, minParticipants, serviceDid, network, beaconType }: AggregationCohortParams) {
134
+ constructor({ id, minParticipants, serviceDid, network, beaconType, recoveryKey, recoverySequence, fundingModel, fallbackThreshold }: AggregationCohortParams) {
93
135
  this.id = id || crypto.randomUUID();
94
136
  // `?? 2` (not `|| 2`) so a deliberately-passed 0 is preserved rather than
95
137
  // silently coerced; the service rejects an invalid count at createCohort.
@@ -97,6 +139,10 @@ export class AggregationCohort {
97
139
  this.serviceDid = serviceDid || '';
98
140
  this.network = network;
99
141
  this.beaconType = beaconType || 'CASBeacon';
142
+ this.recoveryKey = recoveryKey;
143
+ this.recoverySequence = recoverySequence;
144
+ this.fundingModel = fundingModel ?? DEFAULT_FUNDING_MODEL;
145
+ this.fallbackThreshold = fallbackThreshold;
100
146
  }
101
147
 
102
148
  /** Sorted cohort keys (sorted on assignment per BIP-327). */
@@ -109,8 +155,27 @@ export class AggregationCohort {
109
155
  }
110
156
 
111
157
  /**
112
- * Computes the n-of-n MuSig2 Taproot beacon address from cohort keys.
113
- * Sets `tapTweak` to the BIP-341 key-path-only tweak.
158
+ * The resolved k of the k-of-n fallback leaf: the advertised
159
+ * {@link fallbackThreshold}, or n-1 (floored at 1) when unadvertised, where n is
160
+ * the current cohort size. This is the value the beacon address commits to and
161
+ * the spend builders must reproduce. Returns 0 before any cohort keys are set.
162
+ */
163
+ public get effectiveFallbackThreshold(): number {
164
+ if(this.#cohortKeys.length === 0) return 0;
165
+ return resolveFallbackThreshold(this.fallbackThreshold, this.#cohortKeys.length);
166
+ }
167
+
168
+ /**
169
+ * Computes the Taproot beacon address from the cohort keys and recovery params.
170
+ *
171
+ * The output's key path is the n-of-n MuSig2 aggregate of the cohort keys; its
172
+ * script path is the recovery tree (a CSV recovery leaf so a missing signer
173
+ * cannot permanently lock the UTXO). Sets `internalKey`, `tapMerkleRoot`, and
174
+ * the `tapTweak` the MuSig2 session must apply for the key-path spend.
175
+ *
176
+ * The tweak is derived from the Merkle root the address was built with (read
177
+ * back from the payment), never recomputed by hand, so the MuSig2 key-path
178
+ * signature is guaranteed to validate against the funded address.
114
179
  */
115
180
  public computeBeaconAddress(): string {
116
181
  if(this.#cohortKeys.length === 0) {
@@ -119,16 +184,39 @@ export class AggregationCohort {
119
184
  'NO_COHORT_KEYS', { cohortId: this.id }
120
185
  );
121
186
  }
187
+ if(!this.recoveryKey || this.recoveryKey.length === 0 || this.recoverySequence === undefined) {
188
+ throw new AggregationCohortError(
189
+ 'Cannot compute beacon address: missing recovery key or sequence.',
190
+ 'NO_RECOVERY_PARAMS', { cohortId: this.id }
191
+ );
192
+ }
122
193
  const keyAggContext = keyAggregate(this.#cohortKeys);
123
194
  const aggPubkey = keyAggExport(keyAggContext);
124
- // Derive the address for the cohort's network. Without the network arg
125
- // p2tr defaults to mainnet, so a mutinynet/signet/regtest cohort would
126
- // otherwise advertise a `bc1p...` address that no participant can fund.
127
- const payment = p2tr(aggPubkey, undefined, getNetwork(this.network));
128
195
 
129
- // BIP-341: key-path-only P2TR has no script tree. Compute the tweak:
130
- // taggedHash("TapTweak", internalPubkey).
131
- this.tapTweak = schnorr.utils.taggedHash('TapTweak', aggPubkey);
196
+ // The beacon output commits to the script tree (k-of-n fallback leaf + CSV
197
+ // recovery leaf) alongside the MuSig2 internal key. Derive the address for
198
+ // the cohort's network. Without the network arg p2tr defaults to mainnet, so
199
+ // a mutinynet/signet/regtest cohort would otherwise advertise a `bc1p...`
200
+ // address that no participant can fund.
201
+ const leaves = buildRecoveryLeaves(this.fundingModel, {
202
+ recoveryKey : this.recoveryKey,
203
+ recoverySequence : this.recoverySequence,
204
+ cohortKeys : this.#cohortKeys,
205
+ fallbackThreshold : resolveFallbackThreshold(this.fallbackThreshold, this.#cohortKeys.length),
206
+ });
207
+ // allowUnknownOutputs: the CSV recovery leaf is a custom script, not one of
208
+ // p2tr's recognized templates (tr_ns/tr_ms), so the template check must be
209
+ // waived. The leaf hash (and thus the address) is computed from the raw
210
+ // script bytes regardless.
211
+ const payment = p2tr(aggPubkey, leaves, getNetwork(this.network), true);
212
+
213
+ // BIP-341: the key-path tweak commits to the script tree's Merkle root.
214
+ // taggedHash("TapTweak", internalPubkey || tapMerkleRoot). Use the root the
215
+ // library committed to (payment.tapMerkleRoot) so the tweak matches the
216
+ // funded address byte-for-byte.
217
+ this.internalKey = aggPubkey;
218
+ this.tapMerkleRoot = payment.tapMerkleRoot;
219
+ this.tapTweak = schnorr.utils.taggedHash('TapTweak', concatBytes(aggPubkey, payment.tapMerkleRoot));
132
220
 
133
221
  if(!payment.address) {
134
222
  throw new AggregationCohortError(
@@ -189,20 +277,56 @@ export class AggregationCohort {
189
277
  this.pendingUpdates.set(participantDid, signedUpdate);
190
278
  }
191
279
 
280
+ /**
281
+ * Record that a participant declined to submit an update this round
282
+ * (cooperative non-inclusion). The member stays in the cohort and still signs.
283
+ */
284
+ public addNonInclusion(participantDid: string): void {
285
+ if(!this.participants.includes(participantDid)) {
286
+ throw new AggregationCohortError(
287
+ `Participant ${participantDid} is not in cohort ${this.id}.`,
288
+ 'UNKNOWN_PARTICIPANT', { cohortId: this.id, participantDid }
289
+ );
290
+ }
291
+ // A DID cannot both submit and decline. The service guards re-submission, but
292
+ // keep the invariant local so the response gate and builders stay consistent.
293
+ if(this.pendingUpdates.has(participantDid)) {
294
+ throw new AggregationCohortError(
295
+ `Participant ${participantDid} already submitted an update; cannot also decline.`,
296
+ 'CONFLICTING_RESPONSE', { cohortId: this.id, participantDid }
297
+ );
298
+ }
299
+ this.nonIncluded.add(participantDid);
300
+ }
301
+
192
302
  public hasAllUpdates(): boolean {
193
303
  return this.pendingUpdates.size === this.participants.length;
194
304
  }
195
305
 
306
+ /**
307
+ * True when every participant has responded for this round, either with an
308
+ * update or with an explicit non-inclusion. This is the aggregation gate when
309
+ * non-inclusion is in play; it generalizes {@link hasAllUpdates} the same way
310
+ * {@link hasAllValidationResponses} generalizes a unanimous ack.
311
+ */
312
+ public hasAllResponses(): boolean {
313
+ return this.pendingUpdates.size + this.nonIncluded.size === this.participants.length;
314
+ }
315
+
196
316
  /**
197
317
  * Builds a CAS Announcement Map from collected updates.
198
- * Maps each participant DID base64url canonical hash of their signed update.
318
+ * Maps each participant DID to base64url canonical hash of their signed update.
199
319
  * Computes signal bytes as SHA-256 of canonicalized announcement.
320
+ *
321
+ * Members who declined (cooperative non-inclusion) are naturally absent from
322
+ * the map: the body iterates {@link pendingUpdates}, which never holds a
323
+ * decliner. Absence from the map is exactly the CAS non-inclusion signal.
200
324
  */
201
325
  public buildCASAnnouncement(): CASAnnouncement {
202
- if(!this.hasAllUpdates()) {
326
+ if(!this.hasAllResponses()) {
203
327
  throw new AggregationCohortError(
204
- 'Cannot build CAS Announcement: not all updates collected.',
205
- 'INCOMPLETE_UPDATES', { cohortId: this.id, collected: this.pendingUpdates.size, total: this.participants.length }
328
+ 'Cannot build CAS Announcement: not all participants have responded.',
329
+ 'INCOMPLETE_RESPONSES', { cohortId: this.id, updates: this.pendingUpdates.size, declined: this.nonIncluded.size, total: this.participants.length }
206
330
  );
207
331
  }
208
332
  const announcement: CASAnnouncement = {};
@@ -215,25 +339,37 @@ export class AggregationCohort {
215
339
  }
216
340
 
217
341
  /**
218
- * Builds an SMT tree from collected updates.
219
- * Each entry uses a random 32-byte nonce + canonicalized signed update bytes.
220
- * Stores per-participant proofs and the SMT root as signalBytes.
342
+ * Builds an SMT tree with one leaf per participant.
343
+ *
344
+ * A member who submitted an update gets an inclusion leaf
345
+ * (SHA-256(SHA-256(nonce) || SHA-256(update))); a member who declined gets a
346
+ * non-inclusion leaf (SHA-256(SHA-256(nonce)), the `signedUpdate` entry field
347
+ * omitted). The cohort mints each member's nonce and returns it inside that
348
+ * member's serialized proof, so a decliner can self-validate its own
349
+ * non-inclusion slot and the resolver can recompute the leaf. Stores
350
+ * per-participant proofs and the SMT root as signalBytes.
221
351
  */
222
352
  public buildSMTTree(): Map<string, SerializedSMTProof> {
223
- if(!this.hasAllUpdates()) {
353
+ if(!this.hasAllResponses()) {
224
354
  throw new AggregationCohortError(
225
- 'Cannot build SMT tree: not all updates collected.',
226
- 'INCOMPLETE_UPDATES', { cohortId: this.id }
355
+ 'Cannot build SMT tree: not all participants have responded.',
356
+ 'INCOMPLETE_RESPONSES', { cohortId: this.id, updates: this.pendingUpdates.size, declined: this.nonIncluded.size, total: this.participants.length }
227
357
  );
228
358
  }
229
359
  const tree = new BTCR2MerkleTree();
230
360
  const entries: TreeEntry[] = [];
231
361
  const encoder = new TextEncoder();
232
362
 
233
- for(const [did, signedUpdate] of this.pendingUpdates) {
234
- const canonicalBytes = encoder.encode(canonicalize(signedUpdate));
363
+ // Slot every participant: an inclusion leaf for submitters, a non-inclusion
364
+ // leaf (signedUpdate omitted) for decliners.
365
+ for(const did of this.participants) {
235
366
  const nonce = randomBytes(32);
236
- entries.push({ did, nonce, signedUpdate: canonicalBytes });
367
+ const signedUpdate = this.pendingUpdates.get(did);
368
+ if(signedUpdate) {
369
+ entries.push({ did, nonce, signedUpdate: encoder.encode(canonicalize(signedUpdate)) });
370
+ } else {
371
+ entries.push({ did, nonce });
372
+ }
237
373
  }
238
374
 
239
375
  tree.addEntries(entries);
@@ -241,7 +377,7 @@ export class AggregationCohort {
241
377
 
242
378
  this.signalBytes = tree.rootHash;
243
379
  this.smtProofs = new Map();
244
- for(const [did] of this.pendingUpdates) {
380
+ for(const did of this.participants) {
245
381
  this.smtProofs.set(did, tree.proof(did));
246
382
  }
247
383
  return this.smtProofs;
@@ -269,7 +405,7 @@ export class AggregationCohort {
269
405
  }
270
406
 
271
407
  /**
272
- * True when all participants approved. Note: differs from {@link hasAllValidationResponses}
408
+ * True when all participants approved. Note: differs from {@link hasAllValidationResponses},
273
409
  * this returns false if any participant rejected, even if all responses are in.
274
410
  */
275
411
  public isFullyValidated(): boolean {
@@ -15,9 +15,15 @@
15
15
  * settlement (consistent with ADR 008).
16
16
  */
17
17
 
18
+ import type { FundingModel } from './recovery-policy.js';
19
+ import { MAX_RECOVERY_SEQUENCE } from './recovery-policy.js';
20
+
18
21
  /** Beacon types that support aggregation (singleton is single-party only, per ADR 037). */
19
22
  export const KNOWN_BEACON_TYPES = ['CASBeacon', 'SMTBeacon'] as const;
20
23
 
24
+ /** Funding models an operator may advertise. Only 'operator-funded' is implemented (ADR 042). */
25
+ export const KNOWN_FUNDING_MODELS = ['operator-funded', 'participant-funded'] as const;
26
+
21
27
  /**
22
28
  * An advertised price. `unit` is operator-defined (the spec does not specify a
23
29
  * currency); `basis` distinguishes a per-DID from a per-participant charge for
@@ -51,6 +57,31 @@ export interface CohortConditions {
51
57
  maxSecondsBetweenAnnouncements?: number;
52
58
  /** 7. Pending-update count that triggers an announcement. Advertised; enforcement staged (AGG-5) - generalizes hasAllUpdates(). */
53
59
  pendingUpdateTrigger?: number;
60
+ /**
61
+ * Who funds the beacon UTXO and holds the recovery path. Defaults to
62
+ * 'operator-funded' when absent. See ADR 042.
63
+ */
64
+ fundingModel?: FundingModel;
65
+ /**
66
+ * Operator recovery key, x-only (64-character hex / 32 bytes). The beacon
67
+ * output commits to a CSV recovery leaf keyed to this key, so a missing signer
68
+ * can never permanently lock the funded UTXO. Required (ADR 042).
69
+ */
70
+ recoveryKey: string;
71
+ /**
72
+ * Relative-timelock (BIP-68 nSequence, in blocks) before the recovery leaf is
73
+ * spendable. Required, >= 1 (ADR 042).
74
+ */
75
+ recoverySequence: number;
76
+ /**
77
+ * Number of signers (k) the k-of-n fallback leaf requires, so any k cohort
78
+ * members can still announce when the optimistic n-of-n key path stalls
79
+ * (graceful liveness, ADR 042). Optional: when absent it defaults to n-1 at
80
+ * keygen, where n is the finalized participant count. When advertised it must
81
+ * be an integer >= 1 (and <= maxParticipants when that is set); the upper bound
82
+ * against the actual cohort size n is enforced at keygen.
83
+ */
84
+ fallbackThreshold?: number;
54
85
  }
55
86
 
56
87
  /** Validate an optional [min, max] integer pair. */
@@ -109,8 +140,33 @@ export function validateCohortConditions(c: CohortConditions): string[] {
109
140
  problems.push('pendingUpdateTrigger must be an integer >= 1');
110
141
  }
111
142
 
143
+ // Fallback threshold (k of the k-of-n fallback leaf). Optional; when advertised
144
+ // it must be a positive integer and cannot exceed maxParticipants (the binding
145
+ // upper bound against the actual cohort size n is checked at keygen, where n is
146
+ // known). See ADR 042.
147
+ if(c.fallbackThreshold !== undefined) {
148
+ if(!Number.isInteger(c.fallbackThreshold) || c.fallbackThreshold < 1) {
149
+ problems.push('fallbackThreshold must be an integer >= 1');
150
+ } else if(c.maxParticipants !== undefined && Number.isInteger(c.maxParticipants) && c.fallbackThreshold > c.maxParticipants) {
151
+ problems.push('fallbackThreshold must be <= maxParticipants');
152
+ }
153
+ }
154
+
112
155
  checkCost(problems, 'costOfEnrollment', c.costOfEnrollment);
113
156
  checkCost(problems, 'costPerAnnouncement', c.costPerAnnouncement);
114
157
 
158
+ // Recovery params: the beacon output's CSV recovery leaf. Required so a
159
+ // missing signer can never permanently lock the funded UTXO (ADR 042). The
160
+ // key is an x-only (32-byte) Schnorr public key, carried as 64 hex chars.
161
+ if(typeof c.recoveryKey !== 'string' || !/^[0-9a-fA-F]{64}$/.test(c.recoveryKey)) {
162
+ problems.push('recoveryKey must be a 64-character hex string (x-only public key)');
163
+ }
164
+ if(!Number.isInteger(c.recoverySequence) || c.recoverySequence < 1 || c.recoverySequence > MAX_RECOVERY_SEQUENCE) {
165
+ problems.push(`recoverySequence must be a block-based BIP-68 relative timelock in [1, ${MAX_RECOVERY_SEQUENCE}]`);
166
+ }
167
+ if(c.fundingModel !== undefined && !(KNOWN_FUNDING_MODELS as readonly string[]).includes(c.fundingModel)) {
168
+ problems.push(`fundingModel must be one of ${KNOWN_FUNDING_MODELS.join(', ')}`);
169
+ }
170
+
115
171
  return problems;
116
172
  }