@did-btcr2/method 0.29.0 → 0.33.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 (115) hide show
  1. package/README.md +36 -16
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/browser.js +6763 -6170
  4. package/dist/browser.mjs +6763 -6170
  5. package/dist/cjs/index.js +1860 -467
  6. package/dist/esm/core/aggregation/beacon-strategy.js +5 -4
  7. package/dist/esm/core/aggregation/beacon-strategy.js.map +1 -1
  8. package/dist/esm/core/aggregation/transport/factory.js +15 -6
  9. package/dist/esm/core/aggregation/transport/factory.js.map +1 -1
  10. package/dist/esm/core/aggregation/transport/http/client.js +350 -0
  11. package/dist/esm/core/aggregation/transport/http/client.js.map +1 -0
  12. package/dist/esm/core/aggregation/transport/http/envelope.js +126 -0
  13. package/dist/esm/core/aggregation/transport/http/envelope.js.map +1 -0
  14. package/dist/esm/core/aggregation/transport/http/errors.js +11 -0
  15. package/dist/esm/core/aggregation/transport/http/errors.js.map +1 -0
  16. package/dist/esm/core/aggregation/transport/http/inbox-buffer.js +45 -0
  17. package/dist/esm/core/aggregation/transport/http/inbox-buffer.js.map +1 -0
  18. package/dist/esm/core/aggregation/transport/http/index.js +12 -0
  19. package/dist/esm/core/aggregation/transport/http/index.js.map +1 -0
  20. package/dist/esm/core/aggregation/transport/http/nonce-cache.js +38 -0
  21. package/dist/esm/core/aggregation/transport/http/nonce-cache.js.map +1 -0
  22. package/dist/esm/core/aggregation/transport/http/protocol.js +28 -0
  23. package/dist/esm/core/aggregation/transport/http/protocol.js.map +1 -0
  24. package/dist/esm/core/aggregation/transport/http/rate-limiter.js +45 -0
  25. package/dist/esm/core/aggregation/transport/http/rate-limiter.js.map +1 -0
  26. package/dist/esm/core/aggregation/transport/http/request-auth.js +100 -0
  27. package/dist/esm/core/aggregation/transport/http/request-auth.js.map +1 -0
  28. package/dist/esm/core/aggregation/transport/http/server.js +481 -0
  29. package/dist/esm/core/aggregation/transport/http/server.js.map +1 -0
  30. package/dist/esm/core/aggregation/transport/http/sse-stream.js +110 -0
  31. package/dist/esm/core/aggregation/transport/http/sse-stream.js.map +1 -0
  32. package/dist/esm/core/aggregation/transport/http/sse-writer.js +25 -0
  33. package/dist/esm/core/aggregation/transport/http/sse-writer.js.map +1 -0
  34. package/dist/esm/core/aggregation/transport/index.js +1 -0
  35. package/dist/esm/core/aggregation/transport/index.js.map +1 -1
  36. package/dist/esm/core/beacon/beacon.js +197 -51
  37. package/dist/esm/core/beacon/beacon.js.map +1 -1
  38. package/dist/esm/core/beacon/cas-beacon.js +3 -3
  39. package/dist/esm/core/beacon/cas-beacon.js.map +1 -1
  40. package/dist/esm/core/beacon/singleton-beacon.js +3 -3
  41. package/dist/esm/core/beacon/singleton-beacon.js.map +1 -1
  42. package/dist/esm/core/beacon/smt-beacon.js +22 -14
  43. package/dist/esm/core/beacon/smt-beacon.js.map +1 -1
  44. package/dist/esm/core/resolver.js +7 -4
  45. package/dist/esm/core/resolver.js.map +1 -1
  46. package/dist/esm/core/updater.js +63 -55
  47. package/dist/esm/core/updater.js.map +1 -1
  48. package/dist/types/core/aggregation/beacon-strategy.d.ts.map +1 -1
  49. package/dist/types/core/aggregation/transport/factory.d.ts +22 -7
  50. package/dist/types/core/aggregation/transport/factory.d.ts.map +1 -1
  51. package/dist/types/core/aggregation/transport/http/client.d.ts +48 -0
  52. package/dist/types/core/aggregation/transport/http/client.d.ts.map +1 -0
  53. package/dist/types/core/aggregation/transport/http/envelope.d.ts +64 -0
  54. package/dist/types/core/aggregation/transport/http/envelope.d.ts.map +1 -0
  55. package/dist/types/core/aggregation/transport/http/errors.d.ts +9 -0
  56. package/dist/types/core/aggregation/transport/http/errors.d.ts.map +1 -0
  57. package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts +32 -0
  58. package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts.map +1 -0
  59. package/dist/types/core/aggregation/transport/http/index.d.ts +12 -0
  60. package/dist/types/core/aggregation/transport/http/index.d.ts.map +1 -0
  61. package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts +26 -0
  62. package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts.map +1 -0
  63. package/dist/types/core/aggregation/transport/http/protocol.d.ts +53 -0
  64. package/dist/types/core/aggregation/transport/http/protocol.d.ts.map +1 -0
  65. package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts +41 -0
  66. package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts.map +1 -0
  67. package/dist/types/core/aggregation/transport/http/request-auth.d.ts +50 -0
  68. package/dist/types/core/aggregation/transport/http/request-auth.d.ts.map +1 -0
  69. package/dist/types/core/aggregation/transport/http/server.d.ts +110 -0
  70. package/dist/types/core/aggregation/transport/http/server.d.ts.map +1 -0
  71. package/dist/types/core/aggregation/transport/http/sse-stream.d.ts +34 -0
  72. package/dist/types/core/aggregation/transport/http/sse-stream.d.ts.map +1 -0
  73. package/dist/types/core/aggregation/transport/http/sse-writer.d.ts +12 -0
  74. package/dist/types/core/aggregation/transport/http/sse-writer.d.ts.map +1 -0
  75. package/dist/types/core/aggregation/transport/index.d.ts +1 -0
  76. package/dist/types/core/aggregation/transport/index.d.ts.map +1 -1
  77. package/dist/types/core/aggregation/transport/transport.d.ts +1 -1
  78. package/dist/types/core/aggregation/transport/transport.d.ts.map +1 -1
  79. package/dist/types/core/beacon/beacon.d.ts +72 -12
  80. package/dist/types/core/beacon/beacon.d.ts.map +1 -1
  81. package/dist/types/core/beacon/cas-beacon.d.ts +3 -3
  82. package/dist/types/core/beacon/cas-beacon.d.ts.map +1 -1
  83. package/dist/types/core/beacon/singleton-beacon.d.ts +3 -3
  84. package/dist/types/core/beacon/singleton-beacon.d.ts.map +1 -1
  85. package/dist/types/core/beacon/smt-beacon.d.ts +3 -3
  86. package/dist/types/core/beacon/smt-beacon.d.ts.map +1 -1
  87. package/dist/types/core/interfaces.d.ts +14 -11
  88. package/dist/types/core/interfaces.d.ts.map +1 -1
  89. package/dist/types/core/resolver.d.ts.map +1 -1
  90. package/dist/types/core/updater.d.ts +27 -12
  91. package/dist/types/core/updater.d.ts.map +1 -1
  92. package/package.json +20 -8
  93. package/src/core/aggregation/beacon-strategy.ts +5 -4
  94. package/src/core/aggregation/transport/factory.ts +48 -12
  95. package/src/core/aggregation/transport/http/client.ts +409 -0
  96. package/src/core/aggregation/transport/http/envelope.ts +204 -0
  97. package/src/core/aggregation/transport/http/errors.ts +11 -0
  98. package/src/core/aggregation/transport/http/inbox-buffer.ts +53 -0
  99. package/src/core/aggregation/transport/http/index.ts +11 -0
  100. package/src/core/aggregation/transport/http/nonce-cache.ts +43 -0
  101. package/src/core/aggregation/transport/http/protocol.ts +57 -0
  102. package/src/core/aggregation/transport/http/rate-limiter.ts +75 -0
  103. package/src/core/aggregation/transport/http/request-auth.ts +164 -0
  104. package/src/core/aggregation/transport/http/server.ts +615 -0
  105. package/src/core/aggregation/transport/http/sse-stream.ts +121 -0
  106. package/src/core/aggregation/transport/http/sse-writer.ts +23 -0
  107. package/src/core/aggregation/transport/index.ts +1 -0
  108. package/src/core/aggregation/transport/transport.ts +1 -1
  109. package/src/core/beacon/beacon.ts +255 -64
  110. package/src/core/beacon/cas-beacon.ts +4 -4
  111. package/src/core/beacon/singleton-beacon.ts +4 -4
  112. package/src/core/beacon/smt-beacon.ts +24 -16
  113. package/src/core/interfaces.ts +14 -11
  114. package/src/core/resolver.ts +8 -5
  115. package/src/core/updater.ts +113 -67
package/dist/cjs/index.js CHANGED
@@ -62,7 +62,9 @@ __export(index_exports, {
62
62
  CONSOLE_LOGGER: () => CONSOLE_LOGGER,
63
63
  DEFAULT_ADVERT_REPEAT_INTERVAL_MS: () => DEFAULT_ADVERT_REPEAT_INTERVAL_MS,
64
64
  DEFAULT_BROADCAST_LOOKBACK_MS: () => DEFAULT_BROADCAST_LOOKBACK_MS,
65
+ DEFAULT_CLOCK_SKEW_SEC: () => DEFAULT_CLOCK_SKEW_SEC,
65
66
  DEFAULT_MAX_UPDATE_SIZE_BYTES: () => DEFAULT_MAX_UPDATE_SIZE_BYTES,
67
+ DEFAULT_NONCE_LEN_BYTES: () => DEFAULT_NONCE_LEN_BYTES,
66
68
  DEFAULT_NOSTR_RELAYS: () => DEFAULT_NOSTR_RELAYS,
67
69
  DID_REGEX: () => DID_REGEX,
68
70
  DISTRIBUTE_AGGREGATED_DATA: () => DISTRIBUTE_AGGREGATED_DATA,
@@ -73,16 +75,31 @@ __export(index_exports, {
73
75
  DidVerificationMethod: () => DidVerificationMethod,
74
76
  Document: () => Document,
75
77
  GenesisDocument: () => GenesisDocument,
78
+ HTTP_ENVELOPE_VERSION: () => HTTP_ENVELOPE_VERSION,
79
+ HTTP_ROUTE: () => HTTP_ROUTE,
80
+ HttpClientTransport: () => HttpClientTransport,
81
+ HttpServerTransport: () => HttpServerTransport,
82
+ HttpTransportError: () => HttpTransportError,
76
83
  ID_PLACEHOLDER_VALUE: () => ID_PLACEHOLDER_VALUE,
77
84
  Identifier: () => Identifier,
85
+ InMemoryRateLimitStore: () => InMemoryRateLimitStore,
86
+ InboxBuffer: () => InboxBuffer,
78
87
  NONCE_CONTRIBUTION: () => NONCE_CONTRIBUTION,
88
+ NonceCache: () => NonceCache,
79
89
  NostrTransport: () => NostrTransport,
90
+ P2PKH_BEACON_TX_VSIZE: () => P2PKH_BEACON_TX_VSIZE,
91
+ P2TR_BEACON_TX_VSIZE: () => P2TR_BEACON_TX_VSIZE,
92
+ P2WPKH_BEACON_TX_VSIZE: () => P2WPKH_BEACON_TX_VSIZE,
80
93
  ParticipantCohortPhase: () => ParticipantCohortPhase,
94
+ REQUEST_AUTH_SCHEME: () => REQUEST_AUTH_SCHEME,
95
+ RateLimiter: () => RateLimiter,
81
96
  Resolver: () => Resolver,
82
97
  SIGNATURE_AUTHORIZATION: () => SIGNATURE_AUTHORIZATION,
83
98
  SILENT_LOGGER: () => SILENT_LOGGER,
99
+ SINGLETON_BEACON_TX_VSIZE: () => SINGLETON_BEACON_TX_VSIZE,
84
100
  SMTBeacon: () => SMTBeacon,
85
101
  SMTBeaconError: () => SMTBeaconError,
102
+ SSE_EVENT: () => SSE_EVENT,
86
103
  SUBMIT_UPDATE: () => SUBMIT_UPDATE,
87
104
  ServiceCohortPhase: () => ServiceCohortPhase,
88
105
  SigningSessionError: () => SigningSessionError,
@@ -97,6 +114,7 @@ __export(index_exports, {
97
114
  Updater: () => Updater,
98
115
  VALIDATION_ACK: () => VALIDATION_ACK,
99
116
  buildAggregationBeaconTx: () => buildAggregationBeaconTx,
117
+ buildRequestAuth: () => buildRequestAuth,
100
118
  createAggregatedNonceMessage: () => createAggregatedNonceMessage,
101
119
  createAuthorizationRequestMessage: () => createAuthorizationRequestMessage,
102
120
  createCohortAdvertMessage: () => createCohortAdvertMessage,
@@ -108,6 +126,11 @@ __export(index_exports, {
108
126
  createSignatureAuthorizationMessage: () => createSignatureAuthorizationMessage,
109
127
  createSubmitUpdateMessage: () => createSubmitUpdateMessage,
110
128
  createValidationAckMessage: () => createValidationAckMessage,
129
+ defaultReconnectBackoff: () => defaultReconnectBackoff,
130
+ deriveSingletonAddress: () => deriveSingletonAddress,
131
+ detectSingletonScriptKind: () => detectSingletonScriptKind,
132
+ formatSseComment: () => formatSseComment,
133
+ formatSseEvent: () => formatSseEvent,
111
134
  getBeaconStrategy: () => getBeaconStrategy,
112
135
  isAggregatedNonceMessage: () => isAggregatedNonceMessage,
113
136
  isAggregationMessageType: () => isAggregationMessageType,
@@ -124,8 +147,15 @@ __export(index_exports, {
124
147
  isSubmitUpdateMessage: () => isSubmitUpdateMessage,
125
148
  isUpdateMessageType: () => isUpdateMessageType,
126
149
  isValidationAckMessage: () => isValidationAckMessage,
150
+ normalizeForWire: () => normalizeForWire,
127
151
  opReturnScript: () => opReturnScript,
128
- registerBeaconStrategy: () => registerBeaconStrategy
152
+ parseRequestAuth: () => parseRequestAuth,
153
+ parseSseStream: () => parseSseStream,
154
+ registerBeaconStrategy: () => registerBeaconStrategy,
155
+ reviveFromWire: () => reviveFromWire,
156
+ signEnvelope: () => signEnvelope,
157
+ verifyEnvelope: () => verifyEnvelope,
158
+ verifyRequestAuth: () => verifyRequestAuth
129
159
  });
130
160
  module.exports = __toCommonJS(index_exports);
131
161
 
@@ -167,12 +197,12 @@ var SMT_STRATEGY = {
167
197
  const smtProof = body.smtProof;
168
198
  if (!smtProof?.updateId || !smtProof?.nonce) return { matches: false };
169
199
  const canonicalBytes = new TextEncoder().encode((0, import_common.canonicalize)(submittedUpdate));
170
- const expectedUpdateId = (0, import_smt.hashToHex)((0, import_smt.blockHash)(canonicalBytes));
200
+ const expectedUpdateId = (0, import_smt.hashToBase64Url)((0, import_smt.blockHash)(canonicalBytes));
171
201
  if (smtProof.updateId !== expectedUpdateId) {
172
202
  return { matches: false, smtProof };
173
203
  }
174
204
  const index = (0, import_smt.didToIndex)(participantDid);
175
- const candidateHash = (0, import_smt.blockHash)((0, import_smt.blockHash)((0, import_smt.hexToHash)(smtProof.nonce)), (0, import_smt.hexToHash)(smtProof.updateId));
205
+ const candidateHash = (0, import_smt.blockHash)((0, import_smt.blockHash)((0, import_smt.base64UrlToHash)(smtProof.nonce)), (0, import_smt.base64UrlToHash)(smtProof.updateId));
176
206
  return {
177
207
  matches: (0, import_smt.verifySerializedProof)(smtProof, index, candidateHash),
178
208
  smtProof
@@ -1751,11 +1781,1392 @@ var TransportAdapterError = class extends import_common6.MethodError {
1751
1781
  };
1752
1782
 
1753
1783
  // src/core/aggregation/transport/factory.ts
1784
+ var import_common10 = require("@did-btcr2/common");
1785
+
1786
+ // src/core/aggregation/transport/http/client.ts
1787
+ var import_keypair2 = require("@did-btcr2/keypair");
1788
+
1789
+ // src/core/identifier.ts
1754
1790
  var import_common7 = require("@did-btcr2/common");
1791
+ var import_keypair = require("@did-btcr2/keypair");
1792
+ var import_base4 = require("@scure/base");
1793
+ var Identifier = class _Identifier {
1794
+ /**
1795
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#didbtcr2-identifier-encoding | 3.2 did:btcr2 Identifier Encoding}.
1796
+ *
1797
+ * A did:btcr2 DID consists of a did:btcr2 prefix, followed by an id-bech32 value, which is a Bech32m encoding of:
1798
+ * - the specification version;
1799
+ * - the Bitcoin network identifier; and
1800
+ * - either:
1801
+ * - a key-value representing a secp256k1 public key; or
1802
+ * - a hash-value representing the hash of an initiating external DID document.
1803
+ *
1804
+ * @param {KeyBytes | DocumentBytes} genesisBytes The genesis bytes (public key or document bytes).
1805
+ * @param {DidCreateOptions} options The DID creation options.
1806
+ * @returns {string} The new did:btcr2 identifier.
1807
+ */
1808
+ static encode(genesisBytes, options) {
1809
+ const { idType, version = 1, network } = options;
1810
+ if (!(idType in import_common7.IdentifierTypes)) {
1811
+ throw new import_common7.IdentifierError('Expected "idType" to be "KEY" or "EXTERNAL"', import_common7.INVALID_DID, { idType });
1812
+ }
1813
+ if (isNaN(version) || version > 1) {
1814
+ throw new import_common7.IdentifierError('Expected "version" to be 1', import_common7.INVALID_DID, { version });
1815
+ }
1816
+ if (typeof network === "string" && !(network in import_common7.BitcoinNetworkNames)) {
1817
+ throw new import_common7.IdentifierError('Invalid "network" name', import_common7.INVALID_DID, { network });
1818
+ }
1819
+ if (typeof network === "number" && (network < 0 || network > 8)) {
1820
+ throw new import_common7.IdentifierError('Invalid "network" number', import_common7.INVALID_DID, { network });
1821
+ }
1822
+ if (idType === "KEY") {
1823
+ try {
1824
+ new import_keypair.CompressedSecp256k1PublicKey(genesisBytes);
1825
+ } catch {
1826
+ throw new import_common7.IdentifierError(
1827
+ 'Expected "genesisBytes" to be a valid compressed secp256k1 public key',
1828
+ import_common7.INVALID_DID,
1829
+ { genesisBytes }
1830
+ );
1831
+ }
1832
+ }
1833
+ const hrp = idType === "KEY" ? "k" : "x";
1834
+ const nibbles = [];
1835
+ const fCount = Math.floor((version - 1) / 15);
1836
+ for (let i = 0; i < fCount; i++) {
1837
+ nibbles.push(15);
1838
+ }
1839
+ nibbles.push((version - 1) % 15);
1840
+ if (typeof network === "string") {
1841
+ nibbles.push(import_common7.BitcoinNetworkNames[network]);
1842
+ } else if (typeof network === "number") {
1843
+ nibbles.push(network + 11);
1844
+ }
1845
+ if (nibbles.length % 2 !== 0) {
1846
+ nibbles.push(0);
1847
+ }
1848
+ if (fCount !== 0) {
1849
+ for (const index in Array.from({ length: nibbles.length / 2 - 1 })) {
1850
+ throw new import_common7.IdentifierError("Not implemented", "NOT_IMPLEMENTED", { index });
1851
+ }
1852
+ }
1853
+ const dataBytes = new Uint8Array([nibbles[2 * 0] << 4 | nibbles[2 * 0 + 1], ...genesisBytes]);
1854
+ return `did:btcr2:${import_base4.bech32m.encodeFromBytes(hrp, dataBytes)}`;
1855
+ }
1856
+ /**
1857
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#didbtcr2-identifier-decoding | 3.3 did:btcr2 Identifier Decoding}.
1858
+ * @param {string} identifier The BTCR2 DID to be parsed
1859
+ * @returns {DidComponents} The parsed identifier components. See {@link DidComponents} for details.
1860
+ * @throws {DidError} if an error occurs while parsing the identifier
1861
+ * @throws {DidErrorCode.InvalidDid} if identifier is invalid
1862
+ * @throws {DidErrorCode.MethodNotSupported} if the method is not supported
1863
+ */
1864
+ static decode(identifier) {
1865
+ const components = identifier.split(":");
1866
+ if (components.length !== 3) {
1867
+ throw new import_common7.IdentifierError(`Invalid did: ${identifier}`, import_common7.INVALID_DID, { identifier });
1868
+ }
1869
+ const [scheme, method, encoded] = components;
1870
+ if (scheme !== "did") {
1871
+ throw new import_common7.IdentifierError(`Invalid did: ${identifier}`, import_common7.INVALID_DID, { identifier });
1872
+ }
1873
+ if (method !== "btcr2") {
1874
+ throw new import_common7.IdentifierError(`Invalid did method: ${method}`, import_common7.METHOD_NOT_SUPPORTED, { identifier });
1875
+ }
1876
+ if (!encoded) {
1877
+ throw new import_common7.IdentifierError(`Invalid method-specific id: ${identifier}`, import_common7.INVALID_DID, { identifier });
1878
+ }
1879
+ const { prefix: hrp, bytes: dataBytes } = import_base4.bech32m.decodeToBytes(encoded);
1880
+ if (!["x", "k"].includes(hrp)) {
1881
+ throw new import_common7.IdentifierError(`Invalid hrp: ${hrp}`, import_common7.INVALID_DID, { identifier });
1882
+ }
1883
+ if (!dataBytes) {
1884
+ throw new import_common7.IdentifierError(`Failed to decode id: ${encoded}`, import_common7.INVALID_DID, { identifier });
1885
+ }
1886
+ const idType = hrp === "k" ? "KEY" : "EXTERNAL";
1887
+ let version = 1;
1888
+ let byteIndex = 0;
1889
+ let nibblesConsumed = 0;
1890
+ let currentByte = dataBytes[byteIndex];
1891
+ let versionNibble = currentByte >>> 4;
1892
+ while (versionNibble === 15) {
1893
+ version += 15;
1894
+ if (nibblesConsumed % 2 === 0) {
1895
+ versionNibble = currentByte & 15;
1896
+ } else {
1897
+ currentByte = dataBytes[++byteIndex];
1898
+ versionNibble = currentByte >>> 4;
1899
+ }
1900
+ nibblesConsumed += 1;
1901
+ if (version > 1) {
1902
+ throw new import_common7.IdentifierError(`Invalid version: ${version}`, import_common7.INVALID_DID, { identifier });
1903
+ }
1904
+ }
1905
+ version += versionNibble;
1906
+ nibblesConsumed += 1;
1907
+ let networkValue = nibblesConsumed % 2 === 0 ? dataBytes[++byteIndex] >>> 4 : currentByte & 15;
1908
+ nibblesConsumed += 1;
1909
+ let network = import_common7.BitcoinNetworkNames[networkValue];
1910
+ if (!network) {
1911
+ if (networkValue >= 8 && networkValue <= 15) {
1912
+ network = networkValue - 11;
1913
+ } else {
1914
+ throw new import_common7.IdentifierError(`Invalid did: ${identifier}`, import_common7.INVALID_DID, { identifier });
1915
+ }
1916
+ }
1917
+ if (nibblesConsumed % 2 === 1) {
1918
+ const fillerNibble = currentByte & 15;
1919
+ if (fillerNibble !== 0) {
1920
+ throw new import_common7.IdentifierError(`Invalid did: ${identifier}`, import_common7.INVALID_DID, { identifier });
1921
+ }
1922
+ }
1923
+ const genesisBytes = dataBytes.slice(byteIndex + 1);
1924
+ if (idType === "KEY") {
1925
+ try {
1926
+ new import_keypair.CompressedSecp256k1PublicKey(genesisBytes);
1927
+ } catch {
1928
+ throw new import_common7.IdentifierError(`Invalid genesisBytes: ${genesisBytes}`, import_common7.INVALID_DID, { identifier });
1929
+ }
1930
+ }
1931
+ return { idType, hrp, version, network, genesisBytes };
1932
+ }
1933
+ /**
1934
+ * Generates a new did:btcr2 identifier based on a newly generated key pair.
1935
+ * @returns {string} The new did:btcr2 identifier.
1936
+ */
1937
+ static generate() {
1938
+ const keyPair = import_keypair.SchnorrKeyPair.generate();
1939
+ const did = this.encode(
1940
+ keyPair.publicKey.compressed,
1941
+ {
1942
+ idType: "KEY",
1943
+ version: 1,
1944
+ network: "regtest"
1945
+ }
1946
+ );
1947
+ return { keyPair: keyPair.exportJSON(), did };
1948
+ }
1949
+ /**
1950
+ * Extracts the compressed secp256k1 public key from a KEY-type did:btcr2 identifier.
1951
+ * @param {string} did The did:btcr2 identifier to extract the public key from.
1952
+ * @returns {CompressedSecp256k1PublicKey} The compressed public key.
1953
+ * @throws {IdentifierError} If the DID is EXTERNAL type (genesis bytes are a hash, not a pubkey).
1954
+ */
1955
+ static getPublicKey(did) {
1956
+ const { idType, genesisBytes } = _Identifier.decode(did);
1957
+ if (idType !== "KEY") {
1958
+ throw new import_common7.IdentifierError(
1959
+ `Cannot extract public key from EXTERNAL DID: ${did}. EXTERNAL DIDs encode a document hash, not a public key.`,
1960
+ import_common7.INVALID_DID,
1961
+ { did, idType }
1962
+ );
1963
+ }
1964
+ return new import_keypair.CompressedSecp256k1PublicKey(genesisBytes);
1965
+ }
1966
+ /**
1967
+ * Validates a did:btcr2 identifier.
1968
+ * @param {string} identifier The did:btcr2 identifier to validate.
1969
+ * @returns {boolean} True if the identifier is valid, false otherwise.
1970
+ */
1971
+ static isValid(identifier) {
1972
+ try {
1973
+ this.decode(identifier);
1974
+ return true;
1975
+ } catch {
1976
+ return false;
1977
+ }
1978
+ }
1979
+ };
1980
+
1981
+ // src/core/aggregation/transport/http/envelope.ts
1982
+ var import_common8 = require("@did-btcr2/common");
1983
+ var import_utils4 = require("@noble/hashes/utils");
1984
+
1985
+ // src/core/aggregation/transport/http/errors.ts
1986
+ var HttpTransportError = class extends TransportAdapterError {
1987
+ constructor(message, type = "HttpTransportError", data) {
1988
+ super(message, type, { adapter: "http", ...data ?? {} });
1989
+ }
1990
+ };
1991
+
1992
+ // src/core/aggregation/transport/http/protocol.ts
1993
+ var HTTP_ENVELOPE_VERSION = 1;
1994
+ var HTTP_ROUTE = {
1995
+ ADVERTS: "/v1/adverts",
1996
+ MESSAGES: "/v1/messages",
1997
+ ACTOR_INBOX: "/v1/actors/{did}/inbox",
1998
+ WELL_KNOWN: "/v1/.well-known/aggregation"
1999
+ };
2000
+ var SSE_EVENT = {
2001
+ ADVERT: "advert",
2002
+ MESSAGE: "message",
2003
+ HEARTBEAT: "heartbeat"
2004
+ };
2005
+ var DEFAULT_CLOCK_SKEW_SEC = 60;
2006
+ var DEFAULT_NONCE_LEN_BYTES = 16;
2007
+
2008
+ // src/core/aggregation/transport/http/envelope.ts
2009
+ function signEnvelope(message, sender, opts = {}) {
2010
+ const timestamp = opts.timestamp ?? Math.floor(Date.now() / 1e3);
2011
+ const nonce = opts.nonce ?? (0, import_utils4.bytesToHex)((0, import_utils4.randomBytes)(DEFAULT_NONCE_LEN_BYTES));
2012
+ const messageJson = normalizeForWire(normalizeMessage(message));
2013
+ const unsigned = {
2014
+ v: HTTP_ENVELOPE_VERSION,
2015
+ from: sender.did,
2016
+ ...opts.to !== void 0 ? { to: opts.to } : {},
2017
+ timestamp,
2018
+ nonce,
2019
+ message: messageJson
2020
+ };
2021
+ const digest = (0, import_common8.canonicalHashBytes)(unsigned);
2022
+ const sig = sender.keys.secretKey.sign(digest, { scheme: "schnorr" });
2023
+ return { ...unsigned, sig: (0, import_utils4.bytesToHex)(sig) };
2024
+ }
2025
+ function verifyEnvelope(envelope, senderPk, opts = {}) {
2026
+ if (envelope.v !== HTTP_ENVELOPE_VERSION) {
2027
+ throw new HttpTransportError(
2028
+ `Unsupported envelope version: ${envelope.v}`,
2029
+ "ENVELOPE_VERSION_MISMATCH",
2030
+ { version: envelope.v, expected: HTTP_ENVELOPE_VERSION }
2031
+ );
2032
+ }
2033
+ if (opts.expectedFrom !== void 0 && envelope.from !== opts.expectedFrom) {
2034
+ throw new HttpTransportError(
2035
+ `Envelope from mismatch: expected ${opts.expectedFrom}, got ${envelope.from}`,
2036
+ "ENVELOPE_FROM_MISMATCH",
2037
+ { expected: opts.expectedFrom, got: envelope.from }
2038
+ );
2039
+ }
2040
+ if ("expectedTo" in opts && envelope.to !== opts.expectedTo) {
2041
+ throw new HttpTransportError(
2042
+ `Envelope to mismatch: expected ${opts.expectedTo ?? "<broadcast>"}, got ${envelope.to ?? "<broadcast>"}`,
2043
+ "ENVELOPE_TO_MISMATCH",
2044
+ { expected: opts.expectedTo, got: envelope.to }
2045
+ );
2046
+ }
2047
+ const skewSec = opts.clockSkewSec ?? DEFAULT_CLOCK_SKEW_SEC;
2048
+ const nowMs = opts.now ? opts.now() : Date.now();
2049
+ const nowSec = Math.floor(nowMs / 1e3);
2050
+ const diff = Math.abs(nowSec - envelope.timestamp);
2051
+ if (diff > skewSec) {
2052
+ throw new HttpTransportError(
2053
+ `Envelope timestamp out of skew: ${diff}s > ${skewSec}s`,
2054
+ "ENVELOPE_TIMESTAMP_SKEW",
2055
+ { diff, skewSec, timestamp: envelope.timestamp, now: nowSec }
2056
+ );
2057
+ }
2058
+ let sigBytes;
2059
+ try {
2060
+ sigBytes = (0, import_utils4.hexToBytes)(envelope.sig);
2061
+ } catch {
2062
+ throw new HttpTransportError(
2063
+ "Envelope signature is not valid hex",
2064
+ "ENVELOPE_SIG_HEX"
2065
+ );
2066
+ }
2067
+ if (sigBytes.length !== 64) {
2068
+ throw new HttpTransportError(
2069
+ `Invalid signature length: ${sigBytes.length} (expected 64)`,
2070
+ "ENVELOPE_SIG_LENGTH",
2071
+ { length: sigBytes.length }
2072
+ );
2073
+ }
2074
+ const { sig: _sig, ...unsigned } = envelope;
2075
+ const digest = (0, import_common8.canonicalHashBytes)(unsigned);
2076
+ const ok = senderPk.verify(sigBytes, digest, { scheme: "schnorr" });
2077
+ if (!ok) {
2078
+ throw new HttpTransportError(
2079
+ "Envelope signature verification failed",
2080
+ "ENVELOPE_SIG_INVALID"
2081
+ );
2082
+ }
2083
+ }
2084
+ function normalizeMessage(message) {
2085
+ const maybeToJSON = message.toJSON;
2086
+ if (typeof maybeToJSON === "function") {
2087
+ return maybeToJSON.call(message);
2088
+ }
2089
+ return message;
2090
+ }
2091
+ function normalizeForWire(value) {
2092
+ if (value instanceof Uint8Array) {
2093
+ return { __bytes: (0, import_utils4.bytesToHex)(value) };
2094
+ }
2095
+ if (Array.isArray(value)) {
2096
+ return value.map((v) => normalizeForWire(v));
2097
+ }
2098
+ if (value && typeof value === "object") {
2099
+ const out = {};
2100
+ for (const [k, v] of Object.entries(value)) {
2101
+ out[k] = normalizeForWire(v);
2102
+ }
2103
+ return out;
2104
+ }
2105
+ return value;
2106
+ }
2107
+ function reviveFromWire(value) {
2108
+ if (value && typeof value === "object" && !Array.isArray(value)) {
2109
+ const rec = value;
2110
+ const keys = Object.keys(rec);
2111
+ if (keys.length === 1 && keys[0] === "__bytes" && typeof rec.__bytes === "string") {
2112
+ return (0, import_utils4.hexToBytes)(rec.__bytes);
2113
+ }
2114
+ const out = {};
2115
+ for (const [k, v] of Object.entries(rec)) out[k] = reviveFromWire(v);
2116
+ return out;
2117
+ }
2118
+ if (Array.isArray(value)) {
2119
+ return value.map((v) => reviveFromWire(v));
2120
+ }
2121
+ return value;
2122
+ }
2123
+
2124
+ // src/core/aggregation/transport/http/request-auth.ts
2125
+ var import_common9 = require("@did-btcr2/common");
2126
+ var import_utils5 = require("@noble/hashes/utils");
2127
+ var REQUEST_AUTH_SCHEME = "BTCR2-Sig";
2128
+ function buildRequestAuth(did, keys, path, opts = {}) {
2129
+ const ts = opts.timestamp ?? Math.floor(Date.now() / 1e3);
2130
+ const nonce = opts.nonce ?? (0, import_utils5.bytesToHex)((0, import_utils5.randomBytes)(DEFAULT_NONCE_LEN_BYTES));
2131
+ const digest = (0, import_common9.canonicalHashBytes)({
2132
+ v: HTTP_ENVELOPE_VERSION,
2133
+ did,
2134
+ ts,
2135
+ nonce,
2136
+ path
2137
+ });
2138
+ const sig = keys.secretKey.sign(digest, { scheme: "schnorr" });
2139
+ return `${REQUEST_AUTH_SCHEME} v=${HTTP_ENVELOPE_VERSION},did=${did},ts=${ts},nonce=${nonce},sig=${(0, import_utils5.bytesToHex)(sig)}`;
2140
+ }
2141
+ function parseRequestAuth(headerValue) {
2142
+ const prefix = `${REQUEST_AUTH_SCHEME} `;
2143
+ if (!headerValue.startsWith(prefix)) {
2144
+ throw new HttpTransportError(
2145
+ `Unexpected auth scheme (want ${REQUEST_AUTH_SCHEME})`,
2146
+ "REQUEST_AUTH_SCHEME"
2147
+ );
2148
+ }
2149
+ const params = {};
2150
+ for (const piece of headerValue.slice(prefix.length).split(",")) {
2151
+ const eq = piece.indexOf("=");
2152
+ if (eq === -1) continue;
2153
+ const key = piece.slice(0, eq).trim();
2154
+ const val = piece.slice(eq + 1).trim();
2155
+ if (key.length > 0) params[key] = val;
2156
+ }
2157
+ const v = Number(params.v);
2158
+ const ts = Number(params.ts);
2159
+ if (!Number.isInteger(v) || !Number.isInteger(ts) || !params.did || !params.nonce || !params.sig) {
2160
+ throw new HttpTransportError(
2161
+ "Malformed auth header (missing or invalid field)",
2162
+ "REQUEST_AUTH_MALFORMED",
2163
+ { received: Object.keys(params) }
2164
+ );
2165
+ }
2166
+ return { v, did: params.did, ts, nonce: params.nonce, sig: params.sig };
2167
+ }
2168
+ function verifyRequestAuth(headerValue, expectedPath, senderPk, opts = {}) {
2169
+ const parsed = parseRequestAuth(headerValue);
2170
+ if (parsed.v !== HTTP_ENVELOPE_VERSION) {
2171
+ throw new HttpTransportError(
2172
+ `Unsupported auth version: ${parsed.v}`,
2173
+ "REQUEST_AUTH_VERSION_MISMATCH",
2174
+ { version: parsed.v, expected: HTTP_ENVELOPE_VERSION }
2175
+ );
2176
+ }
2177
+ const skewSec = opts.clockSkewSec ?? DEFAULT_CLOCK_SKEW_SEC;
2178
+ const nowMs = opts.now ? opts.now() : Date.now();
2179
+ const nowSec = Math.floor(nowMs / 1e3);
2180
+ const diff = Math.abs(nowSec - parsed.ts);
2181
+ if (diff > skewSec) {
2182
+ throw new HttpTransportError(
2183
+ `Auth timestamp out of skew: ${diff}s > ${skewSec}s`,
2184
+ "REQUEST_AUTH_TIMESTAMP_SKEW",
2185
+ { diff, skewSec }
2186
+ );
2187
+ }
2188
+ let sigBytes;
2189
+ try {
2190
+ sigBytes = (0, import_utils5.hexToBytes)(parsed.sig);
2191
+ } catch {
2192
+ throw new HttpTransportError("Auth signature is not valid hex", "REQUEST_AUTH_SIG_HEX");
2193
+ }
2194
+ if (sigBytes.length !== 64) {
2195
+ throw new HttpTransportError(
2196
+ `Invalid auth signature length: ${sigBytes.length}`,
2197
+ "REQUEST_AUTH_SIG_LENGTH",
2198
+ { length: sigBytes.length }
2199
+ );
2200
+ }
2201
+ const digest = (0, import_common9.canonicalHashBytes)({
2202
+ v: parsed.v,
2203
+ did: parsed.did,
2204
+ ts: parsed.ts,
2205
+ nonce: parsed.nonce,
2206
+ path: expectedPath
2207
+ });
2208
+ const ok = senderPk.verify(sigBytes, digest, { scheme: "schnorr" });
2209
+ if (!ok) {
2210
+ throw new HttpTransportError("Auth signature verification failed", "REQUEST_AUTH_SIG_INVALID");
2211
+ }
2212
+ return parsed;
2213
+ }
2214
+
2215
+ // src/core/aggregation/transport/http/sse-stream.ts
2216
+ async function* parseSseStream(readable) {
2217
+ const decoder = new TextDecoder("utf-8");
2218
+ const reader = readable.getReader();
2219
+ let buffer = "";
2220
+ let pending = {};
2221
+ const dispatchPending = () => {
2222
+ if (pending.data === void 0) {
2223
+ pending = {};
2224
+ return null;
2225
+ }
2226
+ const ev = { data: pending.data };
2227
+ if (pending.event !== void 0) ev.event = pending.event;
2228
+ if (pending.id !== void 0) ev.id = pending.id;
2229
+ if (pending.retry !== void 0) ev.retry = pending.retry;
2230
+ pending = {};
2231
+ return ev;
2232
+ };
2233
+ const processLine = (line) => {
2234
+ if (line.startsWith(":")) return;
2235
+ const colon = line.indexOf(":");
2236
+ const field = colon === -1 ? line : line.slice(0, colon);
2237
+ let value = colon === -1 ? "" : line.slice(colon + 1);
2238
+ if (value.startsWith(" ")) value = value.slice(1);
2239
+ switch (field) {
2240
+ case "data":
2241
+ pending.data = pending.data === void 0 ? value : `${pending.data}
2242
+ ${value}`;
2243
+ break;
2244
+ case "event":
2245
+ pending.event = value;
2246
+ break;
2247
+ case "id":
2248
+ if (!value.includes("\0")) pending.id = value;
2249
+ break;
2250
+ case "retry": {
2251
+ const n = Number(value);
2252
+ if (Number.isInteger(n) && n >= 0) pending.retry = n;
2253
+ break;
2254
+ }
2255
+ }
2256
+ };
2257
+ try {
2258
+ for (; ; ) {
2259
+ const { value, done } = await reader.read();
2260
+ if (done) {
2261
+ buffer += decoder.decode();
2262
+ if (buffer.length > 0) {
2263
+ const line = buffer.endsWith("\r") ? buffer.slice(0, -1) : buffer;
2264
+ if (line.length > 0) processLine(line);
2265
+ buffer = "";
2266
+ }
2267
+ const tail = dispatchPending();
2268
+ if (tail) yield tail;
2269
+ return;
2270
+ }
2271
+ buffer += decoder.decode(value, { stream: true });
2272
+ let lineEnd = buffer.indexOf("\n");
2273
+ while (lineEnd !== -1) {
2274
+ let line = buffer.slice(0, lineEnd);
2275
+ if (line.endsWith("\r")) line = line.slice(0, -1);
2276
+ buffer = buffer.slice(lineEnd + 1);
2277
+ if (line.length === 0) {
2278
+ const ev = dispatchPending();
2279
+ if (ev) yield ev;
2280
+ } else {
2281
+ processLine(line);
2282
+ }
2283
+ lineEnd = buffer.indexOf("\n");
2284
+ }
2285
+ }
2286
+ } finally {
2287
+ try {
2288
+ reader.releaseLock();
2289
+ } catch {
2290
+ }
2291
+ }
2292
+ }
2293
+
2294
+ // src/core/aggregation/transport/http/client.ts
2295
+ function defaultReconnectBackoff(attempt) {
2296
+ const base2 = Math.min(1e3 * 2 ** attempt, 3e4);
2297
+ const jitter = base2 * 0.2 * Math.random();
2298
+ return Math.floor(base2 + jitter);
2299
+ }
2300
+ var HttpClientTransport = class {
2301
+ name = "http";
2302
+ #baseUrl;
2303
+ #fetch;
2304
+ #logger;
2305
+ #backoff;
2306
+ #clockSkewSec;
2307
+ #actors = /* @__PURE__ */ new Map();
2308
+ #peers = /* @__PURE__ */ new Map();
2309
+ #started = false;
2310
+ #broadcastAbort;
2311
+ constructor(config) {
2312
+ const base2 = typeof config.baseUrl === "string" ? new URL(config.baseUrl) : new URL(config.baseUrl.href);
2313
+ if (!base2.pathname.endsWith("/")) base2.pathname += "/";
2314
+ this.#baseUrl = base2;
2315
+ this.#logger = config.logger ?? CONSOLE_LOGGER;
2316
+ this.#backoff = config.reconnectBackoff ?? defaultReconnectBackoff;
2317
+ this.#clockSkewSec = config.clockSkewSec ?? DEFAULT_CLOCK_SKEW_SEC;
2318
+ const fetchImpl = config.fetchImpl ?? globalThis.fetch;
2319
+ if (typeof fetchImpl !== "function") {
2320
+ throw new HttpTransportError(
2321
+ "No fetch implementation available. Pass config.fetchImpl explicitly.",
2322
+ "NO_FETCH_IMPL"
2323
+ );
2324
+ }
2325
+ this.#fetch = fetchImpl;
2326
+ }
2327
+ start() {
2328
+ if (this.#started) return;
2329
+ this.#started = true;
2330
+ this.#broadcastAbort = new AbortController();
2331
+ this.#runBroadcastLoop(this.#broadcastAbort.signal);
2332
+ for (const [did, entry] of this.#actors) {
2333
+ this.#openInbox(did, entry);
2334
+ }
2335
+ }
2336
+ /**
2337
+ * Tear down all SSE subscriptions and stop reconnect loops. Not part of the
2338
+ * {@link Transport} interface, but needed in tests and whenever a client
2339
+ * wants to cleanly disconnect without unregistering every actor.
2340
+ *
2341
+ * Idempotent. Actors remain registered (re-call {@link start} to resume).
2342
+ */
2343
+ stop() {
2344
+ this.#broadcastAbort?.abort();
2345
+ this.#broadcastAbort = void 0;
2346
+ for (const entry of this.#actors.values()) {
2347
+ entry.inboxAbort?.abort();
2348
+ entry.inboxAbort = void 0;
2349
+ }
2350
+ this.#started = false;
2351
+ }
2352
+ registerActor(did, keys) {
2353
+ const existing = this.#actors.get(did);
2354
+ if (existing?.inboxAbort) existing.inboxAbort.abort();
2355
+ const entry = { keys, handlers: /* @__PURE__ */ new Map() };
2356
+ this.#actors.set(did, entry);
2357
+ if (this.#started) this.#openInbox(did, entry);
2358
+ }
2359
+ unregisterActor(did) {
2360
+ const entry = this.#actors.get(did);
2361
+ if (!entry) return;
2362
+ entry.inboxAbort?.abort();
2363
+ this.#actors.delete(did);
2364
+ this.#peers.delete(did);
2365
+ }
2366
+ getActorPk(did) {
2367
+ return this.#actors.get(did)?.keys.publicKey.compressed;
2368
+ }
2369
+ registerPeer(did, communicationPk) {
2370
+ try {
2371
+ new import_keypair2.CompressedSecp256k1PublicKey(communicationPk);
2372
+ } catch {
2373
+ throw new HttpTransportError(
2374
+ `Invalid communication public key for peer ${did}: expected a 33-byte compressed secp256k1 key.`,
2375
+ "INVALID_PEER_KEY",
2376
+ { did, keyLength: communicationPk.length }
2377
+ );
2378
+ }
2379
+ this.#peers.set(did, communicationPk);
2380
+ }
2381
+ getPeerPk(did) {
2382
+ return this.#peers.get(did);
2383
+ }
2384
+ registerMessageHandler(actorDid, messageType, handler) {
2385
+ const actor = this.#actors.get(actorDid);
2386
+ if (!actor) {
2387
+ throw new HttpTransportError(
2388
+ `Cannot register handler: actor ${actorDid} not registered. Call registerActor() first.`,
2389
+ "UNKNOWN_ACTOR",
2390
+ { did: actorDid }
2391
+ );
2392
+ }
2393
+ actor.handlers.set(messageType, handler);
2394
+ }
2395
+ unregisterMessageHandler(actorDid, messageType) {
2396
+ this.#actors.get(actorDid)?.handlers.delete(messageType);
2397
+ }
2398
+ async sendMessage(message, sender, recipient) {
2399
+ const actor = this.#actors.get(sender);
2400
+ if (!actor) {
2401
+ throw new HttpTransportError(
2402
+ `Unknown sender: ${sender}. Call registerActor() before sending messages.`,
2403
+ "UNKNOWN_SENDER",
2404
+ { did: sender }
2405
+ );
2406
+ }
2407
+ const envelope = signEnvelope(
2408
+ message,
2409
+ { did: sender, keys: actor.keys },
2410
+ { to: recipient }
2411
+ );
2412
+ const url = this.#route(HTTP_ROUTE.MESSAGES);
2413
+ const res = await this.#fetch(url, {
2414
+ method: "POST",
2415
+ headers: { "content-type": "application/json" },
2416
+ body: JSON.stringify(envelope)
2417
+ });
2418
+ if (!res.ok) {
2419
+ const body = await safeText(res);
2420
+ throw new HttpTransportError(
2421
+ `sendMessage failed: HTTP ${res.status}`,
2422
+ "SEND_MESSAGE_HTTP",
2423
+ { status: res.status, body: body.slice(0, 256), messageType: message.type }
2424
+ );
2425
+ }
2426
+ }
2427
+ publishRepeating(message, sender, intervalMs, recipient) {
2428
+ let stopped = false;
2429
+ const attempt = () => {
2430
+ if (stopped) return;
2431
+ this.sendMessage(message, sender, recipient).catch((err) => {
2432
+ this.#logger.debug("publishRepeating send failed:", err);
2433
+ });
2434
+ };
2435
+ attempt();
2436
+ const timer = setInterval(attempt, intervalMs);
2437
+ return () => {
2438
+ if (stopped) return;
2439
+ stopped = true;
2440
+ clearInterval(timer);
2441
+ };
2442
+ }
2443
+ #route(template) {
2444
+ return new URL(template.replace(/^\//, ""), this.#baseUrl);
2445
+ }
2446
+ #openInbox(did, entry) {
2447
+ const abort = new AbortController();
2448
+ entry.inboxAbort = abort;
2449
+ this.#runInboxLoop(did, entry, abort.signal);
2450
+ }
2451
+ async #runBroadcastLoop(signal) {
2452
+ const url = this.#route(HTTP_ROUTE.ADVERTS);
2453
+ let attempt = 0;
2454
+ while (!signal.aborted) {
2455
+ try {
2456
+ const res = await this.#fetch(url, {
2457
+ method: "GET",
2458
+ headers: { accept: "text/event-stream" },
2459
+ signal
2460
+ });
2461
+ if (!res.ok || !res.body) {
2462
+ this.#logger.warn(`Broadcast subscribe failed: HTTP ${res.status}`);
2463
+ await sleep(this.#backoff(attempt++), signal);
2464
+ continue;
2465
+ }
2466
+ attempt = 0;
2467
+ for await (const ev of parseSseStream(res.body)) {
2468
+ if (signal.aborted) return;
2469
+ if (ev.event !== SSE_EVENT.ADVERT) continue;
2470
+ this.#dispatchBroadcast(ev.data);
2471
+ }
2472
+ } catch (err) {
2473
+ if (signal.aborted) return;
2474
+ this.#logger.debug("Broadcast loop error:", err);
2475
+ try {
2476
+ await sleep(this.#backoff(attempt++), signal);
2477
+ } catch {
2478
+ return;
2479
+ }
2480
+ }
2481
+ }
2482
+ }
2483
+ async #runInboxLoop(did, entry, signal) {
2484
+ const url = this.#route(HTTP_ROUTE.ACTOR_INBOX.replace("{did}", encodeURIComponent(did)));
2485
+ let attempt = 0;
2486
+ while (!signal.aborted) {
2487
+ try {
2488
+ const auth = buildRequestAuth(did, entry.keys, url.pathname);
2489
+ const res = await this.#fetch(url, {
2490
+ method: "GET",
2491
+ headers: { accept: "text/event-stream", authorization: auth },
2492
+ signal
2493
+ });
2494
+ if (!res.ok || !res.body) {
2495
+ this.#logger.warn(`Inbox subscribe failed for ${did}: HTTP ${res.status}`);
2496
+ await sleep(this.#backoff(attempt++), signal);
2497
+ continue;
2498
+ }
2499
+ attempt = 0;
2500
+ for await (const ev of parseSseStream(res.body)) {
2501
+ if (signal.aborted) return;
2502
+ if (ev.event !== SSE_EVENT.MESSAGE) continue;
2503
+ await this.#dispatchInbox(ev.data, did, entry);
2504
+ }
2505
+ } catch (err) {
2506
+ if (signal.aborted) return;
2507
+ this.#logger.debug(`Inbox loop error for ${did}:`, err);
2508
+ try {
2509
+ await sleep(this.#backoff(attempt++), signal);
2510
+ } catch {
2511
+ return;
2512
+ }
2513
+ }
2514
+ }
2515
+ }
2516
+ #dispatchBroadcast(dataJson) {
2517
+ const envelope = parseEnvelope(dataJson, this.#logger);
2518
+ if (!envelope) return;
2519
+ const senderPk = this.#resolveSenderPk(envelope.from);
2520
+ if (!senderPk) {
2521
+ this.#logger.debug(`Broadcast from unresolvable DID: ${envelope.from}`);
2522
+ return;
2523
+ }
2524
+ try {
2525
+ verifyEnvelope(envelope, senderPk, { clockSkewSec: this.#clockSkewSec });
2526
+ } catch (err) {
2527
+ this.#logger.debug("Broadcast envelope verification failed:", err);
2528
+ return;
2529
+ }
2530
+ const revived = reviveFromWire(envelope.message);
2531
+ const flat = flattenMessage(revived);
2532
+ const messageType = typeof flat.type === "string" ? flat.type : void 0;
2533
+ if (!messageType) return;
2534
+ for (const actor of this.#actors.values()) {
2535
+ const handler = actor.handlers.get(messageType);
2536
+ if (handler) void Promise.resolve(handler(flat));
2537
+ }
2538
+ }
2539
+ async #dispatchInbox(dataJson, actorDid, entry) {
2540
+ const envelope = parseEnvelope(dataJson, this.#logger);
2541
+ if (!envelope) return;
2542
+ const senderPk = this.#resolveSenderPk(envelope.from);
2543
+ if (!senderPk) {
2544
+ this.#logger.debug(`Inbox message from unresolvable DID: ${envelope.from}`);
2545
+ return;
2546
+ }
2547
+ try {
2548
+ verifyEnvelope(envelope, senderPk, {
2549
+ clockSkewSec: this.#clockSkewSec,
2550
+ expectedTo: actorDid
2551
+ });
2552
+ } catch (err) {
2553
+ this.#logger.debug(`Inbox envelope verification failed for ${actorDid}:`, err);
2554
+ return;
2555
+ }
2556
+ const revived = reviveFromWire(envelope.message);
2557
+ const flat = flattenMessage(revived);
2558
+ const messageType = typeof flat.type === "string" ? flat.type : void 0;
2559
+ if (!messageType) return;
2560
+ const handler = entry.handlers.get(messageType);
2561
+ if (handler) await handler(flat);
2562
+ }
2563
+ #resolveSenderPk(did) {
2564
+ const peerBytes = this.#peers.get(did);
2565
+ if (peerBytes) {
2566
+ try {
2567
+ return new import_keypair2.CompressedSecp256k1PublicKey(peerBytes);
2568
+ } catch {
2569
+ }
2570
+ }
2571
+ try {
2572
+ const components = Identifier.decode(did);
2573
+ if (components.idType === "KEY") {
2574
+ return new import_keypair2.CompressedSecp256k1PublicKey(components.genesisBytes);
2575
+ }
2576
+ } catch {
2577
+ }
2578
+ return void 0;
2579
+ }
2580
+ };
2581
+ function sleep(ms, signal) {
2582
+ if (ms <= 0) return Promise.resolve();
2583
+ return new Promise((resolve, reject) => {
2584
+ const onAbort = () => {
2585
+ clearTimeout(timer);
2586
+ reject(new Error("aborted"));
2587
+ };
2588
+ const timer = setTimeout(() => {
2589
+ signal.removeEventListener("abort", onAbort);
2590
+ resolve();
2591
+ }, ms);
2592
+ if (signal.aborted) onAbort();
2593
+ else signal.addEventListener("abort", onAbort, { once: true });
2594
+ });
2595
+ }
2596
+ async function safeText(res) {
2597
+ try {
2598
+ return await res.text();
2599
+ } catch {
2600
+ return "";
2601
+ }
2602
+ }
2603
+ function parseEnvelope(dataJson, logger) {
2604
+ try {
2605
+ return JSON.parse(dataJson);
2606
+ } catch (err) {
2607
+ logger.debug("SSE event: failed to parse envelope JSON:", err);
2608
+ return void 0;
2609
+ }
2610
+ }
2611
+ function flattenMessage(msg) {
2612
+ if (msg.body && typeof msg.body === "object") {
2613
+ return { ...msg, ...msg.body };
2614
+ }
2615
+ return msg;
2616
+ }
2617
+
2618
+ // src/core/aggregation/transport/http/server.ts
2619
+ var import_keypair3 = require("@did-btcr2/keypair");
2620
+
2621
+ // src/core/aggregation/transport/http/inbox-buffer.ts
2622
+ var InboxBuffer = class {
2623
+ #capacity;
2624
+ #entries = [];
2625
+ #nextId = 1;
2626
+ constructor(capacity = 100) {
2627
+ if (capacity < 1) throw new Error(`InboxBuffer capacity must be >= 1; got ${capacity}`);
2628
+ this.#capacity = capacity;
2629
+ }
2630
+ /** Append an event. Returns the stored record (including its assigned id). */
2631
+ append(event, data) {
2632
+ const stored = { id: String(this.#nextId++), event, data };
2633
+ this.#entries.push(stored);
2634
+ if (this.#entries.length > this.#capacity) this.#entries.shift();
2635
+ return stored;
2636
+ }
2637
+ /**
2638
+ * Return stored events with id strictly greater than `lastEventId`. If
2639
+ * `lastEventId` is unset or unparseable, returns everything currently
2640
+ * retained.
2641
+ */
2642
+ since(lastEventId) {
2643
+ if (!lastEventId) return this.#entries.slice();
2644
+ const boundary = Number(lastEventId);
2645
+ if (!Number.isFinite(boundary)) return this.#entries.slice();
2646
+ return this.#entries.filter((e) => Number(e.id) > boundary);
2647
+ }
2648
+ /** Currently retained event count. */
2649
+ size() {
2650
+ return this.#entries.length;
2651
+ }
2652
+ };
2653
+
2654
+ // src/core/aggregation/transport/http/nonce-cache.ts
2655
+ var NonceCache = class {
2656
+ #maxEntries;
2657
+ #entries = /* @__PURE__ */ new Map();
2658
+ constructor(config = {}) {
2659
+ this.#maxEntries = config.maxEntries ?? 1e4;
2660
+ }
2661
+ /**
2662
+ * Record a nonce. Returns `true` if it was novel (caller should accept the
2663
+ * request) or `false` if it was a replay (caller should reject).
2664
+ */
2665
+ store(did, nonce, timestampSec) {
2666
+ const key = `${did}:${nonce}`;
2667
+ if (this.#entries.has(key)) return false;
2668
+ this.#entries.set(key, timestampSec);
2669
+ if (this.#entries.size > this.#maxEntries) {
2670
+ const oldest = this.#entries.keys().next();
2671
+ if (!oldest.done) this.#entries.delete(oldest.value);
2672
+ }
2673
+ return true;
2674
+ }
2675
+ /** Current cache size. Exposed for observability and tests. */
2676
+ size() {
2677
+ return this.#entries.size;
2678
+ }
2679
+ };
2680
+
2681
+ // src/core/aggregation/transport/http/rate-limiter.ts
2682
+ var InMemoryRateLimitStore = class {
2683
+ #buckets = /* @__PURE__ */ new Map();
2684
+ get(key) {
2685
+ return this.#buckets.get(key);
2686
+ }
2687
+ set(key, state) {
2688
+ this.#buckets.set(key, state);
2689
+ }
2690
+ };
2691
+ var RateLimiter = class {
2692
+ #rps;
2693
+ #burst;
2694
+ #store;
2695
+ constructor(config = {}) {
2696
+ this.#rps = config.rps ?? 10;
2697
+ this.#burst = config.burst ?? 30;
2698
+ this.#store = config.store ?? new InMemoryRateLimitStore();
2699
+ }
2700
+ /** Consume one token for `key`. Returns `true` if accepted, `false` if throttled. */
2701
+ consume(key, nowMs) {
2702
+ const existing = this.#store.get(key);
2703
+ const state = existing ?? { tokens: this.#burst, lastRefillMs: nowMs };
2704
+ if (existing) {
2705
+ const elapsedSec = Math.max(0, (nowMs - existing.lastRefillMs) / 1e3);
2706
+ state.tokens = Math.min(this.#burst, existing.tokens + elapsedSec * this.#rps);
2707
+ state.lastRefillMs = nowMs;
2708
+ }
2709
+ if (state.tokens < 1) {
2710
+ this.#store.set(key, state);
2711
+ return false;
2712
+ }
2713
+ state.tokens -= 1;
2714
+ this.#store.set(key, state);
2715
+ return true;
2716
+ }
2717
+ };
2718
+
2719
+ // src/core/aggregation/transport/http/server.ts
2720
+ var INBOX_PATH_PREFIX = "/v1/actors/";
2721
+ var INBOX_PATH_SUFFIX = "/inbox";
2722
+ var DEFAULT_ADVERT_TTL_MS = 5 * 60 * 1e3;
2723
+ var DEFAULT_HEARTBEAT_MS = 2e4;
2724
+ var HttpServerTransport = class {
2725
+ name = "http";
2726
+ #logger;
2727
+ #cors;
2728
+ #clockSkewSec;
2729
+ #inboxBufferSize;
2730
+ #advertTtlMs;
2731
+ #heartbeatMs;
2732
+ #rateLimiter;
2733
+ #nonceCache;
2734
+ #now;
2735
+ #actors = /* @__PURE__ */ new Map();
2736
+ #peers = /* @__PURE__ */ new Map();
2737
+ #inboxes = /* @__PURE__ */ new Map();
2738
+ #broadcastSubscribers = /* @__PURE__ */ new Set();
2739
+ #currentAdvert;
2740
+ #advertSeq = 0;
2741
+ constructor(config = {}) {
2742
+ this.#logger = config.logger ?? CONSOLE_LOGGER;
2743
+ this.#cors = config.cors ?? { mode: "permissive" };
2744
+ this.#clockSkewSec = config.clockSkewSec ?? DEFAULT_CLOCK_SKEW_SEC;
2745
+ this.#inboxBufferSize = config.inboxBufferSize ?? 100;
2746
+ this.#advertTtlMs = config.advertTtlMs ?? DEFAULT_ADVERT_TTL_MS;
2747
+ this.#heartbeatMs = config.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_MS;
2748
+ this.#rateLimiter = config.rateLimiter ?? new RateLimiter();
2749
+ this.#nonceCache = config.nonceCache ?? new NonceCache();
2750
+ this.#now = config.now ?? (() => Date.now());
2751
+ }
2752
+ // ----------------------------------------------------------------
2753
+ // Transport interface
2754
+ // ----------------------------------------------------------------
2755
+ start() {
2756
+ }
2757
+ /**
2758
+ * Detach the transport: close every open SSE subscription, clear the advert
2759
+ * cache, and drop all actor / peer / inbox state. Intended for shutdown and
2760
+ * for test teardown.
2761
+ */
2762
+ stop() {
2763
+ for (const sub of this.#broadcastSubscribers) this.#closeBroadcastSubscriber(sub);
2764
+ this.#broadcastSubscribers.clear();
2765
+ for (const inbox of this.#inboxes.values()) {
2766
+ for (const sub of inbox.subscribers) this.#closeInboxSubscriber(sub);
2767
+ inbox.subscribers.clear();
2768
+ }
2769
+ this.#inboxes.clear();
2770
+ this.#currentAdvert = void 0;
2771
+ }
2772
+ registerActor(did, keys) {
2773
+ this.#actors.set(did, { keys, handlers: /* @__PURE__ */ new Map() });
2774
+ }
2775
+ unregisterActor(did) {
2776
+ this.#actors.delete(did);
2777
+ this.#peers.delete(did);
2778
+ }
2779
+ getActorPk(did) {
2780
+ return this.#actors.get(did)?.keys.publicKey.compressed;
2781
+ }
2782
+ registerPeer(did, communicationPk) {
2783
+ try {
2784
+ new import_keypair3.CompressedSecp256k1PublicKey(communicationPk);
2785
+ } catch {
2786
+ throw new HttpTransportError(
2787
+ `Invalid peer public key for ${did}`,
2788
+ "INVALID_PEER_KEY",
2789
+ { did, keyLength: communicationPk.length }
2790
+ );
2791
+ }
2792
+ this.#peers.set(did, communicationPk);
2793
+ }
2794
+ getPeerPk(did) {
2795
+ return this.#peers.get(did);
2796
+ }
2797
+ registerMessageHandler(actorDid, messageType, handler) {
2798
+ const actor = this.#actors.get(actorDid);
2799
+ if (!actor) {
2800
+ throw new HttpTransportError(
2801
+ `Cannot register handler: actor ${actorDid} not registered`,
2802
+ "UNKNOWN_ACTOR",
2803
+ { did: actorDid }
2804
+ );
2805
+ }
2806
+ actor.handlers.set(messageType, handler);
2807
+ }
2808
+ unregisterMessageHandler(actorDid, messageType) {
2809
+ this.#actors.get(actorDid)?.handlers.delete(messageType);
2810
+ }
2811
+ async sendMessage(message, sender, recipient) {
2812
+ if (!recipient) {
2813
+ throw new HttpTransportError(
2814
+ "HttpServerTransport.sendMessage requires a recipient. Use publishRepeating for broadcasts.",
2815
+ "MISSING_RECIPIENT",
2816
+ { messageType: message.type }
2817
+ );
2818
+ }
2819
+ const actor = this.#actors.get(sender);
2820
+ if (!actor) {
2821
+ throw new HttpTransportError(
2822
+ `Unknown sender: ${sender}`,
2823
+ "UNKNOWN_SENDER",
2824
+ { did: sender }
2825
+ );
2826
+ }
2827
+ const envelope = signEnvelope(message, { did: sender, keys: actor.keys }, { to: recipient });
2828
+ const dataJson = JSON.stringify(envelope);
2829
+ const inbox = this.#getOrCreateInbox(recipient);
2830
+ const stored = inbox.buffer.append(SSE_EVENT.MESSAGE, dataJson);
2831
+ for (const sub of inbox.subscribers) {
2832
+ this.#safeWrite(sub.stream, stored.event, stored.data, stored.id);
2833
+ }
2834
+ }
2835
+ publishRepeating(message, sender, _intervalMs, _recipient) {
2836
+ const actor = this.#actors.get(sender);
2837
+ if (!actor) {
2838
+ throw new HttpTransportError(`Unknown sender: ${sender}`, "UNKNOWN_SENDER", { did: sender });
2839
+ }
2840
+ const envelope = signEnvelope(message, { did: sender, keys: actor.keys });
2841
+ const dataJson = JSON.stringify(envelope);
2842
+ const id = String(++this.#advertSeq);
2843
+ const expiresAtMs = this.#now() + this.#advertTtlMs;
2844
+ this.#currentAdvert = { dataJson, id, expiresAtMs };
2845
+ for (const sub of this.#broadcastSubscribers) {
2846
+ this.#safeWrite(sub.stream, SSE_EVENT.ADVERT, dataJson, id);
2847
+ }
2848
+ return () => {
2849
+ if (this.#currentAdvert?.id === id) this.#currentAdvert = void 0;
2850
+ };
2851
+ }
2852
+ // ----------------------------------------------------------------
2853
+ // Sans-I/O HTTP surface
2854
+ // ----------------------------------------------------------------
2855
+ /**
2856
+ * Handle a POST / GET request (non-SSE). The caller dispatches SSE paths to
2857
+ * {@link handleSse} instead. Returns a fully formed response; the caller's
2858
+ * adapter turns it into an HTTP write.
2859
+ */
2860
+ async handleRequest(req) {
2861
+ const method = req.method.toUpperCase();
2862
+ if (method === "OPTIONS") return this.#respond(204, "", req);
2863
+ const path = extractPath(req.url);
2864
+ if (method === "GET" && path === HTTP_ROUTE.WELL_KNOWN) {
2865
+ return this.#respondJson(200, this.#wellKnownMetadata(), req);
2866
+ }
2867
+ if (method === "POST" && path === HTTP_ROUTE.MESSAGES) {
2868
+ return await this.#handleMessagesPost(req);
2869
+ }
2870
+ if (method === "POST" && path === HTTP_ROUTE.ADVERTS) {
2871
+ return await this.#handleAdvertsPost(req);
2872
+ }
2873
+ return this.#respondJson(404, { error: "not_found" }, req);
2874
+ }
2875
+ /**
2876
+ * Open an SSE stream for a GET request. The caller is responsible for
2877
+ * flushing writes and propagating the `onClose` callback when the HTTP
2878
+ * connection ends.
2879
+ */
2880
+ handleSse(req, stream) {
2881
+ if (req.method.toUpperCase() !== "GET") {
2882
+ stream.close();
2883
+ return;
2884
+ }
2885
+ const path = extractPath(req.url);
2886
+ if (path === HTTP_ROUTE.ADVERTS) {
2887
+ this.#openBroadcastSubscription(stream);
2888
+ return;
2889
+ }
2890
+ const inboxMatch = matchInboxPath(path);
2891
+ if (inboxMatch) {
2892
+ this.#openInboxSubscription(req, stream, inboxMatch.did, path);
2893
+ return;
2894
+ }
2895
+ stream.close();
2896
+ }
2897
+ // ----------------------------------------------------------------
2898
+ // Request handlers
2899
+ // ----------------------------------------------------------------
2900
+ async #handleMessagesPost(req) {
2901
+ const envelope = parseJsonBody(req.body);
2902
+ if (!envelope) return this.#respondJson(400, { error: "invalid_json" }, req);
2903
+ const senderPk = this.#resolveSenderPk(envelope.from);
2904
+ if (!senderPk) {
2905
+ return this.#respondJson(401, { error: "unknown_sender" }, req);
2906
+ }
2907
+ try {
2908
+ verifyEnvelope(envelope, senderPk, { clockSkewSec: this.#clockSkewSec });
2909
+ } catch (err) {
2910
+ this.#logger.debug("POST /v1/messages: envelope verification failed:", err);
2911
+ return this.#respondJson(401, { error: "invalid_envelope" }, req);
2912
+ }
2913
+ if (!this.#nonceCache.store(envelope.from, envelope.nonce, envelope.timestamp)) {
2914
+ return this.#respondJson(409, { error: "replay" }, req);
2915
+ }
2916
+ if (!this.#rateLimiter.consume(envelope.from, this.#now())) {
2917
+ return this.#respondJson(429, { error: "rate_limited" }, req);
2918
+ }
2919
+ if (!envelope.to) {
2920
+ return this.#respondJson(400, { error: "missing_recipient" }, req);
2921
+ }
2922
+ const actor = this.#actors.get(envelope.to);
2923
+ if (!actor) {
2924
+ return this.#respondJson(404, { error: "unknown_recipient" }, req);
2925
+ }
2926
+ const revived = reviveFromWire(envelope.message);
2927
+ const flat = flattenMessage2(revived);
2928
+ const messageType = typeof flat.type === "string" ? flat.type : void 0;
2929
+ if (!messageType) return this.#respondJson(400, { error: "missing_message_type" }, req);
2930
+ const handler = actor.handlers.get(messageType);
2931
+ if (handler) {
2932
+ try {
2933
+ await handler(flat);
2934
+ } catch (err) {
2935
+ this.#logger.debug(`Handler threw for ${messageType}:`, err);
2936
+ }
2937
+ }
2938
+ return this.#respondJson(202, { ok: true }, req);
2939
+ }
2940
+ async #handleAdvertsPost(req) {
2941
+ const envelope = parseJsonBody(req.body);
2942
+ if (!envelope) return this.#respondJson(400, { error: "invalid_json" }, req);
2943
+ const senderPk = this.#resolveSenderPk(envelope.from);
2944
+ if (!senderPk) return this.#respondJson(401, { error: "unknown_sender" }, req);
2945
+ try {
2946
+ verifyEnvelope(envelope, senderPk, { clockSkewSec: this.#clockSkewSec });
2947
+ } catch {
2948
+ return this.#respondJson(401, { error: "invalid_envelope" }, req);
2949
+ }
2950
+ if (!this.#nonceCache.store(envelope.from, envelope.nonce, envelope.timestamp)) {
2951
+ return this.#respondJson(409, { error: "replay" }, req);
2952
+ }
2953
+ if (!this.#rateLimiter.consume(envelope.from, this.#now())) {
2954
+ return this.#respondJson(429, { error: "rate_limited" }, req);
2955
+ }
2956
+ if (!this.#actors.has(envelope.from)) {
2957
+ return this.#respondJson(403, { error: "not_an_actor" }, req);
2958
+ }
2959
+ const id = String(++this.#advertSeq);
2960
+ this.#currentAdvert = {
2961
+ dataJson: JSON.stringify(envelope),
2962
+ id,
2963
+ expiresAtMs: this.#now() + this.#advertTtlMs
2964
+ };
2965
+ for (const sub of this.#broadcastSubscribers) {
2966
+ this.#safeWrite(sub.stream, SSE_EVENT.ADVERT, this.#currentAdvert.dataJson, id);
2967
+ }
2968
+ return this.#respondJson(202, { ok: true }, req);
2969
+ }
2970
+ #openBroadcastSubscription(stream) {
2971
+ const sub = { stream };
2972
+ this.#broadcastSubscribers.add(sub);
2973
+ stream.onClose(() => {
2974
+ this.#closeBroadcastSubscriber(sub);
2975
+ this.#broadcastSubscribers.delete(sub);
2976
+ });
2977
+ if (this.#currentAdvert && this.#currentAdvert.expiresAtMs > this.#now()) {
2978
+ this.#safeWrite(stream, SSE_EVENT.ADVERT, this.#currentAdvert.dataJson, this.#currentAdvert.id);
2979
+ }
2980
+ if (this.#heartbeatMs > 0) {
2981
+ sub.heartbeatTimer = setInterval(() => {
2982
+ try {
2983
+ stream.writeComment("hb");
2984
+ } catch {
2985
+ }
2986
+ }, this.#heartbeatMs);
2987
+ }
2988
+ }
2989
+ #openInboxSubscription(req, stream, did, path) {
2990
+ const auth = req.headers.authorization;
2991
+ if (!auth) {
2992
+ this.#logger.debug(`Inbox subscribe: missing authorization header for ${did}`);
2993
+ stream.close();
2994
+ return;
2995
+ }
2996
+ const senderPk = this.#resolveSenderPk(did);
2997
+ if (!senderPk) {
2998
+ stream.close();
2999
+ return;
3000
+ }
3001
+ let parsedTs = 0;
3002
+ let parsedNonce = "";
3003
+ try {
3004
+ const parsed = verifyRequestAuth(auth, path, senderPk, {
3005
+ clockSkewSec: this.#clockSkewSec,
3006
+ now: () => this.#now()
3007
+ });
3008
+ if (parsed.did !== did) {
3009
+ stream.close();
3010
+ return;
3011
+ }
3012
+ parsedTs = parsed.ts;
3013
+ parsedNonce = parsed.nonce;
3014
+ } catch (err) {
3015
+ this.#logger.debug(`Inbox subscribe: auth verification failed for ${did}:`, err);
3016
+ stream.close();
3017
+ return;
3018
+ }
3019
+ if (!this.#nonceCache.store(did, parsedNonce, parsedTs)) {
3020
+ stream.close();
3021
+ return;
3022
+ }
3023
+ if (!this.#rateLimiter.consume(did, this.#now())) {
3024
+ stream.close();
3025
+ return;
3026
+ }
3027
+ const inbox = this.#getOrCreateInbox(did);
3028
+ const sub = { stream };
3029
+ inbox.subscribers.add(sub);
3030
+ stream.onClose(() => {
3031
+ this.#closeInboxSubscriber(sub);
3032
+ inbox.subscribers.delete(sub);
3033
+ });
3034
+ const lastEventId = req.headers["last-event-id"];
3035
+ for (const stored of inbox.buffer.since(lastEventId)) {
3036
+ this.#safeWrite(stream, stored.event, stored.data, stored.id);
3037
+ }
3038
+ if (this.#heartbeatMs > 0) {
3039
+ sub.heartbeatTimer = setInterval(() => {
3040
+ try {
3041
+ stream.writeComment("hb");
3042
+ } catch {
3043
+ }
3044
+ }, this.#heartbeatMs);
3045
+ }
3046
+ }
3047
+ // ----------------------------------------------------------------
3048
+ // Internal helpers
3049
+ // ----------------------------------------------------------------
3050
+ #getOrCreateInbox(did) {
3051
+ let inbox = this.#inboxes.get(did);
3052
+ if (!inbox) {
3053
+ inbox = { buffer: new InboxBuffer(this.#inboxBufferSize), subscribers: /* @__PURE__ */ new Set() };
3054
+ this.#inboxes.set(did, inbox);
3055
+ }
3056
+ return inbox;
3057
+ }
3058
+ #resolveSenderPk(did) {
3059
+ const peerBytes = this.#peers.get(did);
3060
+ if (peerBytes) {
3061
+ try {
3062
+ return new import_keypair3.CompressedSecp256k1PublicKey(peerBytes);
3063
+ } catch {
3064
+ }
3065
+ }
3066
+ try {
3067
+ const components = Identifier.decode(did);
3068
+ if (components.idType === "KEY") {
3069
+ return new import_keypair3.CompressedSecp256k1PublicKey(components.genesisBytes);
3070
+ }
3071
+ } catch {
3072
+ }
3073
+ return void 0;
3074
+ }
3075
+ #safeWrite(stream, event, data, id) {
3076
+ try {
3077
+ stream.writeEvent(event, data, id);
3078
+ } catch (err) {
3079
+ this.#logger.debug("SSE writeEvent failed:", err);
3080
+ }
3081
+ }
3082
+ #closeBroadcastSubscriber(sub) {
3083
+ if (sub.heartbeatTimer) clearInterval(sub.heartbeatTimer);
3084
+ try {
3085
+ sub.stream.close();
3086
+ } catch {
3087
+ }
3088
+ }
3089
+ #closeInboxSubscriber(sub) {
3090
+ if (sub.heartbeatTimer) clearInterval(sub.heartbeatTimer);
3091
+ try {
3092
+ sub.stream.close();
3093
+ } catch {
3094
+ }
3095
+ }
3096
+ #respondJson(status, body, req) {
3097
+ return {
3098
+ status,
3099
+ headers: { "content-type": "application/json", ...this.#corsHeaders(req) },
3100
+ body: JSON.stringify(body)
3101
+ };
3102
+ }
3103
+ #respond(status, body, req) {
3104
+ return { status, headers: this.#corsHeaders(req), body };
3105
+ }
3106
+ #corsHeaders(req) {
3107
+ const origin = req.headers.origin;
3108
+ if (!origin) return {};
3109
+ const common = {
3110
+ "access-control-allow-methods": "GET, POST, OPTIONS",
3111
+ "access-control-allow-headers": "authorization, content-type, last-event-id",
3112
+ "access-control-max-age": "86400"
3113
+ };
3114
+ switch (this.#cors.mode) {
3115
+ case "permissive":
3116
+ return { "access-control-allow-origin": "*", ...common };
3117
+ case "allowlist":
3118
+ if (this.#cors.origins.includes(origin)) {
3119
+ return { "access-control-allow-origin": origin, vary: "origin", ...common };
3120
+ }
3121
+ return {};
3122
+ case "same-origin":
3123
+ return {};
3124
+ }
3125
+ }
3126
+ #wellKnownMetadata() {
3127
+ return {
3128
+ envelopeVersion: HTTP_ENVELOPE_VERSION,
3129
+ heartbeatIntervalMs: this.#heartbeatMs,
3130
+ inboxBufferSize: this.#inboxBufferSize,
3131
+ advertTtlMs: this.#advertTtlMs
3132
+ };
3133
+ }
3134
+ };
3135
+ function extractPath(reqUrl) {
3136
+ if (reqUrl.startsWith("http://") || reqUrl.startsWith("https://")) {
3137
+ return new URL(reqUrl).pathname;
3138
+ }
3139
+ const q = reqUrl.indexOf("?");
3140
+ return q === -1 ? reqUrl : reqUrl.slice(0, q);
3141
+ }
3142
+ function matchInboxPath(path) {
3143
+ if (!path.startsWith(INBOX_PATH_PREFIX) || !path.endsWith(INBOX_PATH_SUFFIX)) return void 0;
3144
+ const encodedDid = path.slice(INBOX_PATH_PREFIX.length, path.length - INBOX_PATH_SUFFIX.length);
3145
+ if (!encodedDid) return void 0;
3146
+ try {
3147
+ return { did: decodeURIComponent(encodedDid) };
3148
+ } catch {
3149
+ return void 0;
3150
+ }
3151
+ }
3152
+ function parseJsonBody(body) {
3153
+ if (body === void 0 || body === "") return void 0;
3154
+ try {
3155
+ return JSON.parse(body);
3156
+ } catch {
3157
+ return void 0;
3158
+ }
3159
+ }
3160
+ function flattenMessage2(msg) {
3161
+ if (msg.body && typeof msg.body === "object") {
3162
+ return { ...msg, ...msg.body };
3163
+ }
3164
+ return msg;
3165
+ }
1755
3166
 
1756
3167
  // src/core/aggregation/transport/nostr.ts
1757
- var import_keypair = require("@did-btcr2/keypair");
1758
- var import_utils4 = require("@noble/hashes/utils");
3168
+ var import_keypair4 = require("@did-btcr2/keypair");
3169
+ var import_utils6 = require("@noble/hashes/utils");
1759
3170
  var import_nostr_tools = require("nostr-tools");
1760
3171
  var import_pool = require("nostr-tools/pool");
1761
3172
  var DEFAULT_NOSTR_RELAYS = [
@@ -1852,7 +3263,7 @@ var NostrTransport = class _NostrTransport {
1852
3263
  */
1853
3264
  registerPeer(did, communicationPk) {
1854
3265
  try {
1855
- new import_keypair.CompressedSecp256k1PublicKey(communicationPk);
3266
+ new import_keypair4.CompressedSecp256k1PublicKey(communicationPk);
1856
3267
  } catch {
1857
3268
  throw new TransportAdapterError(
1858
3269
  `Invalid communication public key for peer ${did}: expected a 33-byte compressed secp256k1 key.`,
@@ -1953,14 +3364,14 @@ var NostrTransport = class _NostrTransport {
1953
3364
  }
1954
3365
  const senderKeys = actor.keys;
1955
3366
  const tags = [
1956
- ["p", (0, import_utils4.bytesToHex)(senderKeys.publicKey.x)],
3367
+ ["p", (0, import_utils6.bytesToHex)(senderKeys.publicKey.x)],
1957
3368
  ["t", type]
1958
3369
  ];
1959
3370
  if (to) {
1960
3371
  const recipientPkBytes = this.#peerRegistry.get(to);
1961
3372
  if (recipientPkBytes) {
1962
- const recipientPk = new import_keypair.CompressedSecp256k1PublicKey(recipientPkBytes);
1963
- tags.push(["p", (0, import_utils4.bytesToHex)(recipientPk.x)]);
3373
+ const recipientPk = new import_keypair4.CompressedSecp256k1PublicKey(recipientPkBytes);
3374
+ tags.push(["p", (0, import_utils6.bytesToHex)(recipientPk.x)]);
1964
3375
  }
1965
3376
  }
1966
3377
  if (isKeygenMessageType(type)) {
@@ -1990,10 +3401,10 @@ var NostrTransport = class _NostrTransport {
1990
3401
  { adapter: this.name, did: to }
1991
3402
  );
1992
3403
  }
1993
- const recipientPk = new import_keypair.CompressedSecp256k1PublicKey(recipientPkBytes);
3404
+ const recipientPk = new import_keypair4.CompressedSecp256k1PublicKey(recipientPkBytes);
1994
3405
  const conversationKey = import_nostr_tools.nip44.v2.utils.getConversationKey(
1995
3406
  senderKeys.secretKey.bytes,
1996
- (0, import_utils4.bytesToHex)(recipientPk.x)
3407
+ (0, import_utils6.bytesToHex)(recipientPk.x)
1997
3408
  );
1998
3409
  const content = import_nostr_tools.nip44.v2.encrypt(JSON.stringify(message, _NostrTransport.#jsonReplacer), conversationKey);
1999
3410
  const event = (0, import_nostr_tools.finalizeEvent)({
@@ -2047,7 +3458,7 @@ var NostrTransport = class _NostrTransport {
2047
3458
  */
2048
3459
  #subscribeDirected(did, entry) {
2049
3460
  if (!this.pool) return;
2050
- const pkHex = (0, import_utils4.bytesToHex)(entry.keys.publicKey.x);
3461
+ const pkHex = (0, import_utils6.bytesToHex)(entry.keys.publicKey.x);
2051
3462
  const sub = this.pool.subscribeMany(this.#relays, { kinds: [1, 1059], "#p": [pkHex] }, {
2052
3463
  onclose: (reasons) => this.#logger.debug(`Nostr directed subscription closed for ${did}`, reasons),
2053
3464
  onevent: this.#makeActorEventHandler(did)
@@ -2065,7 +3476,7 @@ var NostrTransport = class _NostrTransport {
2065
3476
  return async (event) => {
2066
3477
  const actor = this.#actors.get(actorDid);
2067
3478
  if (!actor) return;
2068
- if (event.pubkey === (0, import_utils4.bytesToHex)(actor.keys.publicKey.x)) return;
3479
+ if (event.pubkey === (0, import_utils6.bytesToHex)(actor.keys.publicKey.x)) return;
2069
3480
  let message;
2070
3481
  try {
2071
3482
  if (event.kind === 1) {
@@ -2178,7 +3589,7 @@ var NostrTransport = class _NostrTransport {
2178
3589
  */
2179
3590
  static #jsonReplacer(_key, value) {
2180
3591
  if (value instanceof Uint8Array) {
2181
- return { __bytes: (0, import_utils4.bytesToHex)(value) };
3592
+ return { __bytes: (0, import_utils6.bytesToHex)(value) };
2182
3593
  }
2183
3594
  return value;
2184
3595
  }
@@ -2196,7 +3607,7 @@ var NostrTransport = class _NostrTransport {
2196
3607
  */
2197
3608
  static #jsonReviver(_key, value) {
2198
3609
  if (value && typeof value === "object" && "__bytes" in value) {
2199
- return (0, import_utils4.hexToBytes)(value.__bytes);
3610
+ return (0, import_utils6.hexToBytes)(value.__bytes);
2200
3611
  }
2201
3612
  return value;
2202
3613
  }
@@ -2207,9 +3618,19 @@ var TransportFactory = class {
2207
3618
  static establish(config) {
2208
3619
  switch (config.type) {
2209
3620
  case "nostr":
2210
- return new NostrTransport({ relays: config.relays });
3621
+ return new NostrTransport({
3622
+ relays: config.relays,
3623
+ logger: config.logger,
3624
+ broadcastLookbackMs: config.broadcastLookbackMs
3625
+ });
2211
3626
  case "didcomm":
2212
- throw new import_common7.NotImplementedError("DIDComm transport not implemented yet.");
3627
+ throw new import_common10.NotImplementedError("DIDComm transport not implemented yet.");
3628
+ case "http":
3629
+ if (config.role === "client") return new HttpClientTransport(config);
3630
+ if (config.role === "server") return new HttpServerTransport(config);
3631
+ throw new import_common10.NotImplementedError(
3632
+ `HTTP transport role not implemented: ${config.role}`
3633
+ );
2213
3634
  default:
2214
3635
  throw new TransportError(
2215
3636
  `Invalid transport type: ${config.type}`,
@@ -2221,41 +3642,58 @@ var TransportFactory = class {
2221
3642
  };
2222
3643
 
2223
3644
  // src/core/aggregation/transport/didcomm.ts
2224
- var import_common8 = require("@did-btcr2/common");
3645
+ var import_common11 = require("@did-btcr2/common");
2225
3646
  var DidCommTransport = class {
2226
3647
  name = "didcomm";
2227
3648
  start() {
2228
- throw new import_common8.NotImplementedError("DidCommTransport not implemented. Use NostrTransport instead.");
3649
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented. Use NostrTransport instead.");
2229
3650
  }
2230
3651
  registerActor(_did, _keys) {
2231
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3652
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2232
3653
  }
2233
3654
  getActorPk(_did) {
2234
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3655
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2235
3656
  }
2236
3657
  registerPeer(_did, _communicationPk) {
2237
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3658
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2238
3659
  }
2239
3660
  getPeerPk(_did) {
2240
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3661
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2241
3662
  }
2242
3663
  registerMessageHandler(_actorDid, _messageType, _handler) {
2243
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3664
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2244
3665
  }
2245
3666
  unregisterMessageHandler(_actorDid, _messageType) {
2246
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3667
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2247
3668
  }
2248
3669
  unregisterActor(_did) {
2249
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3670
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2250
3671
  }
2251
3672
  async sendMessage(_message, _sender, _recipient) {
2252
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3673
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2253
3674
  }
2254
3675
  publishRepeating(_message, _sender, _intervalMs, _recipient) {
2255
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3676
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2256
3677
  }
2257
3678
  };
2258
3679
 
3680
+ // src/core/aggregation/transport/http/sse-writer.ts
3681
+ function formatSseEvent(event, data, id) {
3682
+ const lines = [];
3683
+ if (id !== void 0) lines.push(`id: ${id}`);
3684
+ lines.push(`event: ${event}`);
3685
+ for (const part of data.split("\n")) lines.push(`data: ${part}`);
3686
+ lines.push("");
3687
+ lines.push("");
3688
+ return lines.join("\n");
3689
+ }
3690
+ function formatSseComment(comment) {
3691
+ const safe = comment.replace(/\n/g, " ");
3692
+ return `: ${safe}
3693
+
3694
+ `;
3695
+ }
3696
+
2259
3697
  // src/core/aggregation/runner/typed-emitter.ts
2260
3698
  var TypedEventEmitter = class {
2261
3699
  #listeners = /* @__PURE__ */ new Map();
@@ -2931,33 +4369,32 @@ var AggregationParticipantRunner = class _AggregationParticipantRunner extends T
2931
4369
  };
2932
4370
 
2933
4371
  // src/core/beacon/beacon.ts
2934
- var import_secp256k12 = require("@noble/secp256k1");
2935
- var import_utils5 = require("@noble/hashes/utils");
4372
+ var import_utils7 = require("@noble/hashes/utils.js");
2936
4373
  var import_btc_signer4 = require("@scure/btc-signer");
2937
4374
 
2938
4375
  // src/core/beacon/error.ts
2939
- var import_common9 = require("@did-btcr2/common");
2940
- var BeaconError = class extends import_common9.MethodError {
4376
+ var import_common12 = require("@did-btcr2/common");
4377
+ var BeaconError = class extends import_common12.MethodError {
2941
4378
  constructor(message, type = "BeaconError", data) {
2942
4379
  super(message, type, data);
2943
4380
  }
2944
4381
  };
2945
- var SingletonBeaconError = class extends import_common9.MethodError {
4382
+ var SingletonBeaconError = class extends import_common12.MethodError {
2946
4383
  constructor(message, type = "SingletonBeaconError", data) {
2947
4384
  super(message, type, data);
2948
4385
  }
2949
4386
  };
2950
- var AggregateBeaconError = class extends import_common9.MethodError {
4387
+ var AggregateBeaconError = class extends import_common12.MethodError {
2951
4388
  constructor(message, type = "AggregateBeaconError", data) {
2952
4389
  super(message, type, data);
2953
4390
  }
2954
4391
  };
2955
- var CASBeaconError = class extends import_common9.MethodError {
4392
+ var CASBeaconError = class extends import_common12.MethodError {
2956
4393
  constructor(message, type = "CASBeaconError", data) {
2957
4394
  super(message, type, data);
2958
4395
  }
2959
4396
  };
2960
- var SMTBeaconError = class extends import_common9.MethodError {
4397
+ var SMTBeaconError = class extends import_common12.MethodError {
2961
4398
  constructor(message, type = "SMTBeaconError", data) {
2962
4399
  super(message, type, data);
2963
4400
  }
@@ -2985,9 +4422,32 @@ var StaticFeeEstimator = class {
2985
4422
 
2986
4423
  // src/core/beacon/beacon.ts
2987
4424
  var DEFAULT_FEE_ESTIMATOR = new StaticFeeEstimator(5);
2988
- var P2TR_BEACON_TX_VSIZE = 140;
4425
+ var P2TR_BEACON_TX_VSIZE = 160;
4426
+ var P2WPKH_BEACON_TX_VSIZE = 155;
4427
+ var P2PKH_BEACON_TX_VSIZE = 240;
4428
+ var SINGLETON_BEACON_TX_VSIZE = {
4429
+ p2pkh: P2PKH_BEACON_TX_VSIZE,
4430
+ p2wpkh: P2WPKH_BEACON_TX_VSIZE,
4431
+ p2tr: P2TR_BEACON_TX_VSIZE
4432
+ };
4433
+ function detectSingletonScriptKind(bitcoinAddress, network) {
4434
+ const decoded = (0, import_btc_signer4.Address)(network).decode(bitcoinAddress);
4435
+ if (decoded.type === "pkh") return "p2pkh";
4436
+ if (decoded.type === "wpkh") return "p2wpkh";
4437
+ if (decoded.type === "tr") return "p2tr";
4438
+ throw new BeaconError(
4439
+ `Unsupported singleton beacon address type "${decoded.type}". Expected P2PKH, P2WPKH, or P2TR (taproot key-path).`,
4440
+ "UNSUPPORTED_BEACON_ADDRESS_TYPE",
4441
+ { address: bitcoinAddress, kind: decoded.type }
4442
+ );
4443
+ }
4444
+ function deriveSingletonAddress(kind, pubkey, network) {
4445
+ if (kind === "p2pkh") return (0, import_btc_signer4.p2pkh)(pubkey, network).address;
4446
+ if (kind === "p2wpkh") return (0, import_btc_signer4.p2wpkh)(pubkey, network).address;
4447
+ return (0, import_btc_signer4.p2tr)(pubkey.slice(1, 33), void 0, network).address;
4448
+ }
2989
4449
  function opReturnScript(signalBytes) {
2990
- return import_btc_signer4.Script.encode([import_btc_signer4.OP.RETURN, signalBytes]);
4450
+ return import_btc_signer4.Script.encode(["RETURN", signalBytes]);
2991
4451
  }
2992
4452
  async function fetchSpendableUtxo(bitcoinAddress, bitcoin) {
2993
4453
  const utxos = await bitcoin.rest.address.getUtxos(bitcoinAddress);
@@ -2995,7 +4455,7 @@ async function fetchSpendableUtxo(bitcoinAddress, bitcoin) {
2995
4455
  throw new BeaconError(
2996
4456
  "No UTXOs found, please fund address!",
2997
4457
  "UNFUNDED_BEACON_ADDRESS",
2998
- { bitcoinAddress }
4458
+ { address: bitcoinAddress }
2999
4459
  );
3000
4460
  }
3001
4461
  const utxo = utxos.sort((a, b) => b.status.block_height - a.status.block_height).shift();
@@ -3003,11 +4463,11 @@ async function fetchSpendableUtxo(bitcoinAddress, bitcoin) {
3003
4463
  throw new BeaconError(
3004
4464
  "Beacon bitcoin address unfunded or utxos unconfirmed.",
3005
4465
  "UNFUNDED_BEACON_ADDRESS",
3006
- { bitcoinAddress }
4466
+ { address: bitcoinAddress }
3007
4467
  );
3008
4468
  }
3009
4469
  const prevTxHex = await bitcoin.rest.transaction.getHex(utxo.txid);
3010
- return { utxo, prevTxBytes: (0, import_utils5.hexToBytes)(prevTxHex) };
4470
+ return { utxo, prevTxBytes: (0, import_utils7.hexToBytes)(prevTxHex) };
3011
4471
  }
3012
4472
  async function buildAggregationBeaconTx(opts) {
3013
4473
  const feeEstimator = opts.feeEstimator ?? DEFAULT_FEE_ESTIMATOR;
@@ -3019,10 +4479,10 @@ async function buildAggregationBeaconTx(opts) {
3019
4479
  throw new BeaconError(
3020
4480
  `UTXO value (${utxo.value}) insufficient to cover fee (${feeSats}).`,
3021
4481
  "INSUFFICIENT_FUNDS",
3022
- { bitcoinAddress: opts.beaconAddress, utxoValue: utxo.value, fee: feeSats.toString() }
4482
+ { address: opts.beaconAddress, valueSats: utxo.value, feeSats }
3023
4483
  );
3024
4484
  }
3025
- const tx = new import_btc_signer4.Transaction();
4485
+ const tx = new import_btc_signer4.Transaction({ allowUnknownOutputs: true });
3026
4486
  tx.addInput({
3027
4487
  txid: utxo.txid,
3028
4488
  index: utxo.vout,
@@ -3038,9 +4498,45 @@ async function buildAggregationBeaconTx(opts) {
3038
4498
  prevOutValues: [BigInt(utxo.value)],
3039
4499
  beaconAddress: opts.beaconAddress,
3040
4500
  utxo,
3041
- feeSats
4501
+ feeSats,
4502
+ scriptKind: "p2tr"
3042
4503
  };
3043
4504
  }
4505
+ async function signSingletonInput(tx, inputIdx, kind, signer, prevOutScript, amount) {
4506
+ const pubkey = signer.publicKey;
4507
+ if (kind === "p2pkh") {
4508
+ const sighashType = import_btc_signer4.SigHash.ALL;
4509
+ const sighash2 = tx.preimageLegacy(inputIdx, prevOutScript, sighashType);
4510
+ const sig2 = signer.sign(sighash2, "ecdsa");
4511
+ const sigWithType = (0, import_utils7.concatBytes)(sig2, new Uint8Array([sighashType]));
4512
+ tx.updateInput(inputIdx, { partialSig: [[pubkey, sigWithType]] }, true);
4513
+ tx.finalize();
4514
+ return tx.hex;
4515
+ }
4516
+ if (kind === "p2wpkh") {
4517
+ const decoded = import_btc_signer4.OutScript.decode(prevOutScript);
4518
+ if (decoded.type !== "wpkh") {
4519
+ throw new BeaconError(
4520
+ `Expected P2WPKH prev-output script, got "${decoded.type}".`,
4521
+ "PREVOUT_SCRIPT_MISMATCH",
4522
+ { kind, observedScriptType: decoded.type }
4523
+ );
4524
+ }
4525
+ const sighashScript = import_btc_signer4.OutScript.encode({ type: "pkh", hash: decoded.hash });
4526
+ const sighashType = import_btc_signer4.SigHash.ALL;
4527
+ const sighash2 = tx.preimageWitnessV0(inputIdx, sighashScript, sighashType, amount);
4528
+ const sig2 = signer.sign(sighash2, "ecdsa");
4529
+ const sigWithType = (0, import_utils7.concatBytes)(sig2, new Uint8Array([sighashType]));
4530
+ tx.updateInput(inputIdx, { partialSig: [[pubkey, sigWithType]] }, true);
4531
+ tx.finalize();
4532
+ return tx.hex;
4533
+ }
4534
+ const sighash = tx.preimageWitnessV1(inputIdx, [prevOutScript], import_btc_signer4.SigHash.DEFAULT, [amount]);
4535
+ const sig = signer.sign(sighash, "bip341");
4536
+ tx.updateInput(inputIdx, { tapKeySig: sig });
4537
+ tx.finalize();
4538
+ return tx.hex;
4539
+ }
3044
4540
  var Beacon = class {
3045
4541
  /**
3046
4542
  * The Beacon service configuration parsed from the DID Document.
@@ -3050,7 +4546,9 @@ var Beacon = class {
3050
4546
  this.service = service;
3051
4547
  }
3052
4548
  /**
3053
- * Build + sign + broadcast a single-party beacon signal transaction (P2WPKH spend).
4549
+ * Build + sign + broadcast a singleton beacon signal transaction. The beacon
4550
+ * address's script kind (P2PKH / P2WPKH / P2TR) is detected automatically
4551
+ * and the input is constructed and signed accordingly.
3054
4552
  *
3055
4553
  * Composed from the three extracted phases ({@link buildSinglePartyTx},
3056
4554
  * {@link signSinglePartyTx}, {@link broadcastRawTx}) so each piece can be exercised
@@ -3059,13 +4557,13 @@ var Beacon = class {
3059
4557
  * plumbing (UTXO fetch + OP_RETURN output + change output) is shared.
3060
4558
  *
3061
4559
  * @param signalBytes 32-byte payload to embed in OP_RETURN.
3062
- * @param secretKey Secret key used to sign the spending input.
4560
+ * @param signer Signer used to sign the spending input.
3063
4561
  * @param bitcoin Bitcoin network connection.
3064
4562
  * @param options Broadcast options (fee estimator, etc.).
3065
4563
  * @returns The txid of the broadcast transaction.
3066
4564
  * @throws {BeaconError} if the address is unfunded, no UTXO is available, or fee exceeds value.
3067
4565
  */
3068
- async buildSignAndBroadcast(signalBytes, secretKey, bitcoin, options) {
4566
+ async buildSignAndBroadcast(signalBytes, signer, bitcoin, options) {
3069
4567
  const feeEstimator = options?.feeEstimator ?? DEFAULT_FEE_ESTIMATOR;
3070
4568
  const beaconAddress = this.service.serviceEndpoint.replace("bitcoin:", "");
3071
4569
  const { utxo, prevTxBytes } = await fetchSpendableUtxo(beaconAddress, bitcoin);
@@ -3074,69 +4572,97 @@ var Beacon = class {
3074
4572
  beaconAddress,
3075
4573
  utxo,
3076
4574
  prevTxBytes,
3077
- secretKey,
4575
+ signer,
3078
4576
  bitcoin,
3079
4577
  feeEstimator
3080
4578
  });
3081
- const signedHex = this.signSinglePartyTx(plan.tx, secretKey);
4579
+ const signedHex = await this.signSinglePartyTx(plan, signer);
3082
4580
  return this.broadcastRawTx(bitcoin, signedHex);
3083
4581
  }
3084
4582
  /**
3085
- * Build an unsigned P2WPKH single-party beacon tx + probe-sign to determine vsize,
3086
- * then rebuild with the real fee. Returns the tx and prev-output metadata.
4583
+ * Build an unsigned singleton beacon tx ready for {@link signSinglePartyTx}.
3087
4584
  *
3088
- * The secret key is required here (not just in `signSinglePartyTx`) because the
3089
- * two-pass fee estimation requires an actual signature to measure vsize accurately.
4585
+ * Detects the beacon address script kind (P2PKH / P2WPKH / P2TR) and configures
4586
+ * the input accordingly. Validates that the signer's pubkey produces the beacon
4587
+ * address under that script kind — without this check, a misconfigured caller
4588
+ * would burn a real UTXO on a tx that fails at broadcast. Fees are computed from
4589
+ * the per-kind {@link SINGLETON_BEACON_TX_VSIZE} constant, avoiding any probe-sign
4590
+ * round-trip.
3090
4591
  */
3091
4592
  async buildSinglePartyTx(opts) {
3092
- const pubkey = this.#derivePubkey(opts.secretKey);
3093
- const witnessOut = (0, import_btc_signer4.p2wpkh)(pubkey, opts.bitcoin.data);
3094
- const witnessScript = witnessOut.script;
3095
- const build = (feeSats2) => {
3096
- const tx2 = new import_btc_signer4.Transaction();
3097
- tx2.addInput({
3098
- txid: opts.utxo.txid,
3099
- index: opts.utxo.vout,
3100
- nonWitnessUtxo: opts.prevTxBytes,
3101
- witnessUtxo: { amount: BigInt(opts.utxo.value), script: witnessScript }
3102
- });
3103
- tx2.addOutputAddress(
3104
- opts.beaconAddress,
3105
- BigInt(opts.utxo.value) - feeSats2,
3106
- opts.bitcoin.data
4593
+ const network = opts.bitcoin.data;
4594
+ const pubkey = opts.signer.publicKey;
4595
+ const kind = detectSingletonScriptKind(opts.beaconAddress, network);
4596
+ const derivedAddress = deriveSingletonAddress(kind, pubkey, network);
4597
+ if (derivedAddress !== opts.beaconAddress) {
4598
+ throw new BeaconError(
4599
+ `Signer pubkey produces ${kind.toUpperCase()} address "${derivedAddress}", but beacon address is "${opts.beaconAddress}".`,
4600
+ "SIGNER_KEY_MISMATCH",
4601
+ { kind, address: opts.beaconAddress, derivedAddress }
3107
4602
  );
3108
- tx2.addOutput({ script: opReturnScript(opts.signalBytes), amount: 0n });
3109
- return tx2;
3110
- };
3111
- const probe = build(0n);
3112
- probe.signIdx(opts.secretKey, 0);
3113
- probe.finalize();
3114
- const vsize = probe.vsize;
3115
- const feeSats = await opts.feeEstimator.estimateFee(vsize);
3116
- if (BigInt(opts.utxo.value) <= feeSats) {
4603
+ }
4604
+ const feeSats = await opts.feeEstimator.estimateFee(SINGLETON_BEACON_TX_VSIZE[kind]);
4605
+ const amount = BigInt(opts.utxo.value);
4606
+ if (amount <= feeSats) {
3117
4607
  throw new BeaconError(
3118
4608
  `UTXO value (${opts.utxo.value}) insufficient to cover fee (${feeSats}).`,
3119
4609
  "INSUFFICIENT_FUNDS",
3120
- { bitcoinAddress: opts.beaconAddress, utxoValue: opts.utxo.value, fee: feeSats.toString() }
4610
+ { address: opts.beaconAddress, valueSats: opts.utxo.value, feeSats }
3121
4611
  );
3122
4612
  }
3123
- const tx = build(feeSats);
4613
+ const tx = new import_btc_signer4.Transaction({ allowUnknownOutputs: true });
4614
+ let prevOutScript;
4615
+ if (kind === "p2pkh") {
4616
+ prevOutScript = (0, import_btc_signer4.p2pkh)(pubkey, network).script;
4617
+ tx.addInput({
4618
+ txid: opts.utxo.txid,
4619
+ index: opts.utxo.vout,
4620
+ nonWitnessUtxo: opts.prevTxBytes
4621
+ });
4622
+ } else if (kind === "p2wpkh") {
4623
+ prevOutScript = (0, import_btc_signer4.p2wpkh)(pubkey, network).script;
4624
+ tx.addInput({
4625
+ txid: opts.utxo.txid,
4626
+ index: opts.utxo.vout,
4627
+ nonWitnessUtxo: opts.prevTxBytes,
4628
+ witnessUtxo: { amount, script: prevOutScript }
4629
+ });
4630
+ } else {
4631
+ const internalKey = pubkey.slice(1, 33);
4632
+ prevOutScript = (0, import_btc_signer4.p2tr)(internalKey, void 0, network).script;
4633
+ tx.addInput({
4634
+ txid: opts.utxo.txid,
4635
+ index: opts.utxo.vout,
4636
+ nonWitnessUtxo: opts.prevTxBytes,
4637
+ witnessUtxo: { amount, script: prevOutScript },
4638
+ tapInternalKey: internalKey
4639
+ });
4640
+ }
4641
+ tx.addOutputAddress(opts.beaconAddress, amount - feeSats, network);
4642
+ tx.addOutput({ script: opReturnScript(opts.signalBytes), amount: 0n });
3124
4643
  return {
3125
4644
  tx,
3126
- prevOutScripts: [witnessScript],
3127
- prevOutValues: [BigInt(opts.utxo.value)],
4645
+ prevOutScripts: [prevOutScript],
4646
+ prevOutValues: [amount],
3128
4647
  beaconAddress: opts.beaconAddress,
3129
4648
  utxo: opts.utxo,
3130
- feeSats
4649
+ feeSats,
4650
+ scriptKind: kind
3131
4651
  };
3132
4652
  }
3133
4653
  /**
3134
4654
  * Sign + finalize the unsigned single-party tx and return its raw hex.
4655
+ * Dispatches to the correct signing primitive based on `plan.scriptKind`.
3135
4656
  */
3136
- signSinglePartyTx(tx, secretKey) {
3137
- tx.signIdx(secretKey, 0);
3138
- tx.finalize();
3139
- return tx.hex;
4657
+ async signSinglePartyTx(plan, signer) {
4658
+ return signSingletonInput(
4659
+ plan.tx,
4660
+ 0,
4661
+ plan.scriptKind,
4662
+ signer,
4663
+ plan.prevOutScripts[0],
4664
+ plan.prevOutValues[0]
4665
+ );
3140
4666
  }
3141
4667
  /**
3142
4668
  * Broadcast raw transaction hex via the Bitcoin REST endpoint. Returns the txid.
@@ -3144,14 +4670,10 @@ var Beacon = class {
3144
4670
  async broadcastRawTx(bitcoin, rawHex) {
3145
4671
  return bitcoin.rest.transaction.send(rawHex);
3146
4672
  }
3147
- /** Derive the compressed secp256k1 public key from a raw secret key. */
3148
- #derivePubkey(secretKey) {
3149
- return (0, import_secp256k12.getPublicKey)(secretKey, true);
3150
- }
3151
4673
  };
3152
4674
 
3153
4675
  // src/core/beacon/cas-beacon.ts
3154
- var import_common10 = require("@did-btcr2/common");
4676
+ var import_common13 = require("@did-btcr2/common");
3155
4677
  var CASBeacon = class extends Beacon {
3156
4678
  /**
3157
4679
  * Creates an instance of CASBeacon.
@@ -3192,7 +4714,7 @@ var CASBeacon = class extends Beacon {
3192
4714
  if (!updateHashEncoded) {
3193
4715
  continue;
3194
4716
  }
3195
- const updateHash = (0, import_common10.encode)((0, import_common10.decode)(updateHashEncoded, "base64urlnopad"), "hex");
4717
+ const updateHash = (0, import_common13.encode)((0, import_common13.decode)(updateHashEncoded, "base64urlnopad"), "hex");
3196
4718
  const signedUpdate = sidecar.updateMap.get(updateHash);
3197
4719
  if (!signedUpdate) {
3198
4720
  needs.push({
@@ -3215,19 +4737,19 @@ var CASBeacon = class extends Beacon {
3215
4737
  * and broadcast are delegated to {@link Beacon.buildSignAndBroadcast}.
3216
4738
  *
3217
4739
  * @param {SignedBTCR2Update} signedUpdate The signed BTCR2 update to broadcast.
3218
- * @param {KeyBytes} secretKey The secret key for signing the Bitcoin transaction.
4740
+ * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
3219
4741
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
3220
4742
  * @param {CASBroadcastOptions} [options] Optional broadcast configuration, including a
3221
4743
  * `casPublish` callback to publish the announcement off-chain and a `feeEstimator`.
3222
4744
  * @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
3223
4745
  * @throws {BeaconError} if the bitcoin address is invalid, unfunded, or UTXO cannot cover the fee.
3224
4746
  */
3225
- async broadcastSignal(signedUpdate, secretKey, bitcoin, options) {
4747
+ async broadcastSignal(signedUpdate, signer, bitcoin, options) {
3226
4748
  const did = this.service.id.split("#")[0];
3227
- const updateHash = (0, import_common10.canonicalHash)(signedUpdate);
4749
+ const updateHash = (0, import_common13.canonicalHash)(signedUpdate);
3228
4750
  const casAnnouncement = { [did]: updateHash };
3229
- const announcementHash = (0, import_common10.hash)((0, import_common10.canonicalize)(casAnnouncement));
3230
- await this.buildSignAndBroadcast(announcementHash, secretKey, bitcoin, options);
4751
+ const announcementHash = (0, import_common13.hash)((0, import_common13.canonicalize)(casAnnouncement));
4752
+ await this.buildSignAndBroadcast(announcementHash, signer, bitcoin, options);
3231
4753
  if (options?.casPublish) {
3232
4754
  await options.casPublish(casAnnouncement);
3233
4755
  }
@@ -3236,10 +4758,10 @@ var CASBeacon = class extends Beacon {
3236
4758
  };
3237
4759
 
3238
4760
  // src/core/beacon/factory.ts
3239
- var import_common13 = require("@did-btcr2/common");
4761
+ var import_common16 = require("@did-btcr2/common");
3240
4762
 
3241
4763
  // src/core/beacon/singleton-beacon.ts
3242
- var import_common11 = require("@did-btcr2/common");
4764
+ var import_common14 = require("@did-btcr2/common");
3243
4765
  var SingletonBeacon = class extends Beacon {
3244
4766
  /**
3245
4767
  * Creates an instance of SingletonBeacon.
@@ -3280,23 +4802,23 @@ var SingletonBeacon = class extends Beacon {
3280
4802
  * {@link Beacon.buildSignAndBroadcast}.
3281
4803
  *
3282
4804
  * @param {SignedBTCR2Update} signedUpdate The signed BTCR2 update to broadcast.
3283
- * @param {KeyBytes} secretKey The secret key for signing the Bitcoin transaction.
4805
+ * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
3284
4806
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
3285
4807
  * @param {BroadcastOptions} [options] Optional broadcast configuration (e.g. fee estimator).
3286
4808
  * @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
3287
4809
  * @throws {BeaconError} if the bitcoin address is invalid, unfunded, or UTXO cannot cover the fee.
3288
4810
  */
3289
- async broadcastSignal(signedUpdate, secretKey, bitcoin, options) {
3290
- const signalBytes = (0, import_common11.hash)((0, import_common11.canonicalize)(signedUpdate));
3291
- await this.buildSignAndBroadcast(signalBytes, secretKey, bitcoin, options);
4811
+ async broadcastSignal(signedUpdate, signer, bitcoin, options) {
4812
+ const signalBytes = (0, import_common14.hash)((0, import_common14.canonicalize)(signedUpdate));
4813
+ await this.buildSignAndBroadcast(signalBytes, signer, bitcoin, options);
3292
4814
  return signedUpdate;
3293
4815
  }
3294
4816
  };
3295
4817
 
3296
4818
  // src/core/beacon/smt-beacon.ts
3297
- var import_common12 = require("@did-btcr2/common");
4819
+ var import_common15 = require("@did-btcr2/common");
3298
4820
  var import_smt3 = require("@did-btcr2/smt");
3299
- var import_utils6 = require("@noble/hashes/utils");
4821
+ var import_utils8 = require("@noble/hashes/utils");
3300
4822
  var SMTBeacon = class extends Beacon {
3301
4823
  /**
3302
4824
  * Creates an instance of SMTBeacon.
@@ -3332,9 +4854,6 @@ var SMTBeacon = class extends Beacon {
3332
4854
  });
3333
4855
  continue;
3334
4856
  }
3335
- if (!smtProof.updateId) {
3336
- continue;
3337
- }
3338
4857
  if (!smtProof.nonce) {
3339
4858
  throw new SMTBeaconError(
3340
4859
  "SMT proof missing required nonce field.",
@@ -3343,7 +4862,8 @@ var SMTBeacon = class extends Beacon {
3343
4862
  );
3344
4863
  }
3345
4864
  const index = (0, import_smt3.didToIndex)(did);
3346
- const candidateHash = (0, import_smt3.blockHash)((0, import_smt3.blockHash)((0, import_smt3.hexToHash)(smtProof.nonce)), (0, import_smt3.hexToHash)(smtProof.updateId));
4865
+ const nonceHash = (0, import_smt3.base64UrlToHash)(smtProof.nonce);
4866
+ const candidateHash = smtProof.updateId ? (0, import_smt3.blockHash)((0, import_smt3.blockHash)(nonceHash), (0, import_smt3.base64UrlToHash)(smtProof.updateId)) : (0, import_smt3.blockHash)((0, import_smt3.blockHash)(nonceHash));
3347
4867
  const valid = (0, import_smt3.verifySerializedProof)(smtProof, index, candidateHash);
3348
4868
  if (!valid) {
3349
4869
  throw new SMTBeaconError(
@@ -3352,11 +4872,15 @@ var SMTBeacon = class extends Beacon {
3352
4872
  { smtProof, did }
3353
4873
  );
3354
4874
  }
3355
- const signedUpdate = sidecar.updateMap.get(smtProof.updateId);
4875
+ if (!smtProof.updateId) {
4876
+ continue;
4877
+ }
4878
+ const updateHashHex = (0, import_smt3.hashToHex)((0, import_smt3.base64UrlToHash)(smtProof.updateId));
4879
+ const signedUpdate = sidecar.updateMap.get(updateHashHex);
3356
4880
  if (!signedUpdate) {
3357
4881
  needs.push({
3358
4882
  kind: "NeedSignedUpdate",
3359
- updateHash: smtProof.updateId,
4883
+ updateHash: updateHashHex,
3360
4884
  beaconServiceId: this.service.id
3361
4885
  });
3362
4886
  continue;
@@ -3374,20 +4898,20 @@ var SMTBeacon = class extends Beacon {
3374
4898
  * signing, and broadcast are delegated to {@link Beacon.buildSignAndBroadcast}.
3375
4899
  *
3376
4900
  * @param {SignedBTCR2Update} signedUpdate The signed BTCR2 update to broadcast.
3377
- * @param {KeyBytes} secretKey The secret key for signing the Bitcoin transaction.
4901
+ * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
3378
4902
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
3379
4903
  * @param {BroadcastOptions} [options] Optional broadcast configuration (e.g. fee estimator).
3380
4904
  * @return {Promise<SignedBTCR2Update>} The signed update that was broadcast.
3381
4905
  * @throws {BeaconError} if the bitcoin address is invalid, unfunded, or UTXO cannot cover the fee.
3382
4906
  */
3383
- async broadcastSignal(signedUpdate, secretKey, bitcoin, options) {
4907
+ async broadcastSignal(signedUpdate, signer, bitcoin, options) {
3384
4908
  const did = this.service.id.split("#")[0];
3385
- const canonicalBytes = new TextEncoder().encode((0, import_common12.canonicalize)(signedUpdate));
3386
- const nonce = (0, import_utils6.randomBytes)(32);
4909
+ const canonicalBytes = new TextEncoder().encode((0, import_common15.canonicalize)(signedUpdate));
4910
+ const nonce = (0, import_utils8.randomBytes)(32);
3387
4911
  const tree = new import_smt3.BTCR2MerkleTree();
3388
4912
  tree.addEntries([{ did, nonce, signedUpdate: canonicalBytes }]);
3389
4913
  tree.finalize();
3390
- await this.buildSignAndBroadcast(tree.rootHash, secretKey, bitcoin, options);
4914
+ await this.buildSignAndBroadcast(tree.rootHash, signer, bitcoin, options);
3391
4915
  return signedUpdate;
3392
4916
  }
3393
4917
  };
@@ -3408,18 +4932,18 @@ var BeaconFactory = class {
3408
4932
  case "SMTBeacon":
3409
4933
  return new SMTBeacon(service);
3410
4934
  default:
3411
- throw new import_common13.MethodError("Invalid Beacon Type", "INVALID_BEACON_ERROR", service);
4935
+ throw new import_common16.MethodError("Invalid Beacon Type", "INVALID_BEACON_ERROR", service);
3412
4936
  }
3413
4937
  }
3414
4938
  };
3415
4939
 
3416
4940
  // src/core/beacon/signal-discovery.ts
3417
4941
  var import_bitcoin2 = require("@did-btcr2/bitcoin");
3418
- var import_common16 = require("@did-btcr2/common");
4942
+ var import_common18 = require("@did-btcr2/common");
3419
4943
 
3420
4944
  // src/core/beacon/utils.ts
3421
4945
  var import_bitcoin = require("@did-btcr2/bitcoin");
3422
- var import_common15 = require("@did-btcr2/common");
4946
+ var import_common17 = require("@did-btcr2/common");
3423
4947
  var import_btc_signer5 = require("@scure/btc-signer");
3424
4948
 
3425
4949
  // src/utils/appendix.ts
@@ -4460,198 +5984,6 @@ var Appendix = class _Appendix {
4460
5984
  }
4461
5985
  };
4462
5986
 
4463
- // src/core/identifier.ts
4464
- var import_common14 = require("@did-btcr2/common");
4465
- var import_keypair2 = require("@did-btcr2/keypair");
4466
- var import_base7 = require("@scure/base");
4467
- var Identifier = class _Identifier {
4468
- /**
4469
- * Implements {@link https://dcdpr.github.io/did-btcr2/#didbtcr2-identifier-encoding | 3.2 did:btcr2 Identifier Encoding}.
4470
- *
4471
- * A did:btcr2 DID consists of a did:btcr2 prefix, followed by an id-bech32 value, which is a Bech32m encoding of:
4472
- * - the specification version;
4473
- * - the Bitcoin network identifier; and
4474
- * - either:
4475
- * - a key-value representing a secp256k1 public key; or
4476
- * - a hash-value representing the hash of an initiating external DID document.
4477
- *
4478
- * @param {KeyBytes | DocumentBytes} genesisBytes The genesis bytes (public key or document bytes).
4479
- * @param {DidCreateOptions} options The DID creation options.
4480
- * @returns {string} The new did:btcr2 identifier.
4481
- */
4482
- static encode(genesisBytes, options) {
4483
- const { idType, version = 1, network } = options;
4484
- if (!(idType in import_common14.IdentifierTypes)) {
4485
- throw new import_common14.IdentifierError('Expected "idType" to be "KEY" or "EXTERNAL"', import_common14.INVALID_DID, { idType });
4486
- }
4487
- if (isNaN(version) || version > 1) {
4488
- throw new import_common14.IdentifierError('Expected "version" to be 1', import_common14.INVALID_DID, { version });
4489
- }
4490
- if (typeof network === "string" && !(network in import_common14.BitcoinNetworkNames)) {
4491
- throw new import_common14.IdentifierError('Invalid "network" name', import_common14.INVALID_DID, { network });
4492
- }
4493
- if (typeof network === "number" && (network < 0 || network > 8)) {
4494
- throw new import_common14.IdentifierError('Invalid "network" number', import_common14.INVALID_DID, { network });
4495
- }
4496
- if (idType === "KEY") {
4497
- try {
4498
- new import_keypair2.CompressedSecp256k1PublicKey(genesisBytes);
4499
- } catch {
4500
- throw new import_common14.IdentifierError(
4501
- 'Expected "genesisBytes" to be a valid compressed secp256k1 public key',
4502
- import_common14.INVALID_DID,
4503
- { genesisBytes }
4504
- );
4505
- }
4506
- }
4507
- const hrp = idType === "KEY" ? "k" : "x";
4508
- const nibbles = [];
4509
- const fCount = Math.floor((version - 1) / 15);
4510
- for (let i = 0; i < fCount; i++) {
4511
- nibbles.push(15);
4512
- }
4513
- nibbles.push((version - 1) % 15);
4514
- if (typeof network === "string") {
4515
- nibbles.push(import_common14.BitcoinNetworkNames[network]);
4516
- } else if (typeof network === "number") {
4517
- nibbles.push(network + 11);
4518
- }
4519
- if (nibbles.length % 2 !== 0) {
4520
- nibbles.push(0);
4521
- }
4522
- if (fCount !== 0) {
4523
- for (const index in Array.from({ length: nibbles.length / 2 - 1 })) {
4524
- throw new import_common14.IdentifierError("Not implemented", "NOT_IMPLEMENTED", { index });
4525
- }
4526
- }
4527
- const dataBytes = new Uint8Array([nibbles[2 * 0] << 4 | nibbles[2 * 0 + 1], ...genesisBytes]);
4528
- return `did:btcr2:${import_base7.bech32m.encodeFromBytes(hrp, dataBytes)}`;
4529
- }
4530
- /**
4531
- * Implements {@link https://dcdpr.github.io/did-btcr2/#didbtcr2-identifier-decoding | 3.3 did:btcr2 Identifier Decoding}.
4532
- * @param {string} identifier The BTCR2 DID to be parsed
4533
- * @returns {DidComponents} The parsed identifier components. See {@link DidComponents} for details.
4534
- * @throws {DidError} if an error occurs while parsing the identifier
4535
- * @throws {DidErrorCode.InvalidDid} if identifier is invalid
4536
- * @throws {DidErrorCode.MethodNotSupported} if the method is not supported
4537
- */
4538
- static decode(identifier) {
4539
- const components = identifier.split(":");
4540
- if (components.length !== 3) {
4541
- throw new import_common14.IdentifierError(`Invalid did: ${identifier}`, import_common14.INVALID_DID, { identifier });
4542
- }
4543
- const [scheme, method, encoded] = components;
4544
- if (scheme !== "did") {
4545
- throw new import_common14.IdentifierError(`Invalid did: ${identifier}`, import_common14.INVALID_DID, { identifier });
4546
- }
4547
- if (method !== "btcr2") {
4548
- throw new import_common14.IdentifierError(`Invalid did method: ${method}`, import_common14.METHOD_NOT_SUPPORTED, { identifier });
4549
- }
4550
- if (!encoded) {
4551
- throw new import_common14.IdentifierError(`Invalid method-specific id: ${identifier}`, import_common14.INVALID_DID, { identifier });
4552
- }
4553
- const { prefix: hrp, bytes: dataBytes } = import_base7.bech32m.decodeToBytes(encoded);
4554
- if (!["x", "k"].includes(hrp)) {
4555
- throw new import_common14.IdentifierError(`Invalid hrp: ${hrp}`, import_common14.INVALID_DID, { identifier });
4556
- }
4557
- if (!dataBytes) {
4558
- throw new import_common14.IdentifierError(`Failed to decode id: ${encoded}`, import_common14.INVALID_DID, { identifier });
4559
- }
4560
- const idType = hrp === "k" ? "KEY" : "EXTERNAL";
4561
- let version = 1;
4562
- let byteIndex = 0;
4563
- let nibblesConsumed = 0;
4564
- let currentByte = dataBytes[byteIndex];
4565
- let versionNibble = currentByte >>> 4;
4566
- while (versionNibble === 15) {
4567
- version += 15;
4568
- if (nibblesConsumed % 2 === 0) {
4569
- versionNibble = currentByte & 15;
4570
- } else {
4571
- currentByte = dataBytes[++byteIndex];
4572
- versionNibble = currentByte >>> 4;
4573
- }
4574
- nibblesConsumed += 1;
4575
- if (version > 1) {
4576
- throw new import_common14.IdentifierError(`Invalid version: ${version}`, import_common14.INVALID_DID, { identifier });
4577
- }
4578
- }
4579
- version += versionNibble;
4580
- nibblesConsumed += 1;
4581
- let networkValue = nibblesConsumed % 2 === 0 ? dataBytes[++byteIndex] >>> 4 : currentByte & 15;
4582
- nibblesConsumed += 1;
4583
- let network = import_common14.BitcoinNetworkNames[networkValue];
4584
- if (!network) {
4585
- if (networkValue >= 8 && networkValue <= 15) {
4586
- network = networkValue - 11;
4587
- } else {
4588
- throw new import_common14.IdentifierError(`Invalid did: ${identifier}`, import_common14.INVALID_DID, { identifier });
4589
- }
4590
- }
4591
- if (nibblesConsumed % 2 === 1) {
4592
- const fillerNibble = currentByte & 15;
4593
- if (fillerNibble !== 0) {
4594
- throw new import_common14.IdentifierError(`Invalid did: ${identifier}`, import_common14.INVALID_DID, { identifier });
4595
- }
4596
- }
4597
- const genesisBytes = dataBytes.slice(byteIndex + 1);
4598
- if (idType === "KEY") {
4599
- try {
4600
- new import_keypair2.CompressedSecp256k1PublicKey(genesisBytes);
4601
- } catch {
4602
- throw new import_common14.IdentifierError(`Invalid genesisBytes: ${genesisBytes}`, import_common14.INVALID_DID, { identifier });
4603
- }
4604
- }
4605
- return { idType, hrp, version, network, genesisBytes };
4606
- }
4607
- /**
4608
- * Generates a new did:btcr2 identifier based on a newly generated key pair.
4609
- * @returns {string} The new did:btcr2 identifier.
4610
- */
4611
- static generate() {
4612
- const keyPair = import_keypair2.SchnorrKeyPair.generate();
4613
- const did = this.encode(
4614
- keyPair.publicKey.compressed,
4615
- {
4616
- idType: "KEY",
4617
- version: 1,
4618
- network: "regtest"
4619
- }
4620
- );
4621
- return { keyPair: keyPair.exportJSON(), did };
4622
- }
4623
- /**
4624
- * Extracts the compressed secp256k1 public key from a KEY-type did:btcr2 identifier.
4625
- * @param {string} did The did:btcr2 identifier to extract the public key from.
4626
- * @returns {CompressedSecp256k1PublicKey} The compressed public key.
4627
- * @throws {IdentifierError} If the DID is EXTERNAL type (genesis bytes are a hash, not a pubkey).
4628
- */
4629
- static getPublicKey(did) {
4630
- const { idType, genesisBytes } = _Identifier.decode(did);
4631
- if (idType !== "KEY") {
4632
- throw new import_common14.IdentifierError(
4633
- `Cannot extract public key from EXTERNAL DID: ${did}. EXTERNAL DIDs encode a document hash, not a public key.`,
4634
- import_common14.INVALID_DID,
4635
- { did, idType }
4636
- );
4637
- }
4638
- return new import_keypair2.CompressedSecp256k1PublicKey(genesisBytes);
4639
- }
4640
- /**
4641
- * Validates a did:btcr2 identifier.
4642
- * @param {string} identifier The did:btcr2 identifier to validate.
4643
- * @returns {boolean} True if the identifier is valid, false otherwise.
4644
- */
4645
- static isValid(identifier) {
4646
- try {
4647
- this.decode(identifier);
4648
- return true;
4649
- } catch {
4650
- return false;
4651
- }
4652
- }
4653
- };
4654
-
4655
5987
  // src/core/beacon/utils.ts
4656
5988
  var BeaconUtils = class {
4657
5989
  /**
@@ -4662,7 +5994,7 @@ var BeaconUtils = class {
4662
5994
  */
4663
5995
  static parseBitcoinAddress(uri) {
4664
5996
  if (!uri.startsWith("bitcoin:")) {
4665
- throw new import_common15.MethodError("Invalid Bitcoin URI format", "BEACON_SERVICE_ERROR", { uri });
5997
+ throw new import_common17.MethodError("Invalid Bitcoin URI format", "BEACON_SERVICE_ERROR", { uri });
4666
5998
  }
4667
5999
  return uri.replace("bitcoin:", "").split("?")[0];
4668
6000
  }
@@ -4743,7 +6075,7 @@ var BeaconUtils = class {
4743
6075
  const p2wpkhAddr = (0, import_btc_signer5.p2wpkh)(publicKey, network).address;
4744
6076
  const p2trAddr = (0, import_btc_signer5.p2tr)(publicKey.slice(1, 33), void 0, network).address;
4745
6077
  if (!p2pkhAddr || !p2wpkhAddr || !p2trAddr) {
4746
- throw new import_common15.DidMethodError("Failed to generate bitcoin addresses");
6078
+ throw new import_common17.DidMethodError("Failed to generate bitcoin addresses");
4747
6079
  }
4748
6080
  return [
4749
6081
  {
@@ -4859,7 +6191,7 @@ var BeaconSignalDiscovery = class {
4859
6191
  }
4860
6192
  const rpc = bitcoin.rpc;
4861
6193
  if (!rpc) {
4862
- throw new import_common16.ResolveError("RPC connection is not available", "RPC_CONNECTION_ERROR", bitcoin);
6194
+ throw new import_common18.ResolveError("RPC connection is not available", "RPC_CONNECTION_ERROR", bitcoin);
4863
6195
  }
4864
6196
  const targetHeight = await rpc.getBlockCount();
4865
6197
  const beaconServicesMap = BeaconUtils.getBeaconServicesMap(beaconServices);
@@ -4930,23 +6262,23 @@ var BeaconSignalDiscovery = class {
4930
6262
 
4931
6263
  // src/core/resolver.ts
4932
6264
  var import_bitcoin4 = require("@did-btcr2/bitcoin");
4933
- var import_common20 = require("@did-btcr2/common");
6265
+ var import_common22 = require("@did-btcr2/common");
4934
6266
  var import_cryptosuite3 = require("@did-btcr2/cryptosuite");
4935
- var import_keypair4 = require("@did-btcr2/keypair");
6267
+ var import_keypair6 = require("@did-btcr2/keypair");
4936
6268
 
4937
6269
  // src/did-btcr2.ts
4938
- var import_common19 = require("@did-btcr2/common");
6270
+ var import_common21 = require("@did-btcr2/common");
4939
6271
  var import_dids2 = require("@web5/dids");
4940
6272
 
4941
6273
  // src/core/updater.ts
4942
- var import_common18 = require("@did-btcr2/common");
6274
+ var import_common20 = require("@did-btcr2/common");
4943
6275
  var import_cryptosuite2 = require("@did-btcr2/cryptosuite");
4944
6276
 
4945
6277
  // src/utils/did-document.ts
4946
6278
  var import_bitcoin3 = require("@did-btcr2/bitcoin");
4947
- var import_common17 = require("@did-btcr2/common");
4948
- var import_keypair3 = require("@did-btcr2/keypair");
4949
- var import_utils8 = require("@web5/dids/utils");
6279
+ var import_common19 = require("@did-btcr2/common");
6280
+ var import_keypair5 = require("@did-btcr2/keypair");
6281
+ var import_utils10 = require("@web5/dids/utils");
4950
6282
  var import_btc_signer6 = require("@scure/btc-signer");
4951
6283
  var BTCR2_DID_DOCUMENT_CONTEXT = [
4952
6284
  "https://www.w3.org/ns/did/v1.1",
@@ -4988,20 +6320,20 @@ var DidDocument = class _DidDocument {
4988
6320
  deactivated;
4989
6321
  constructor(document) {
4990
6322
  if (!document.id) {
4991
- throw new import_common17.DidDocumentError("DID Document must have an id", import_common17.INVALID_DID_DOCUMENT, document);
6323
+ throw new import_common19.DidDocumentError("DID Document must have an id", import_common19.INVALID_DID_DOCUMENT, document);
4992
6324
  }
4993
- const idType = document.id.includes("k1") ? import_common17.IdentifierTypes.KEY : import_common17.IdentifierTypes.EXTERNAL;
6325
+ const idType = document.id.includes("k1") ? import_common19.IdentifierTypes.KEY : import_common19.IdentifierTypes.EXTERNAL;
4994
6326
  const isGenesis = document.id === ID_PLACEHOLDER_VALUE;
4995
6327
  const { id, verificationMethod: vm, service } = document;
4996
6328
  if (!isGenesis) {
4997
6329
  if (!_DidDocument.isValidId(id)) {
4998
- throw new import_common17.DidDocumentError(`Invalid id: ${id}`, import_common17.INVALID_DID_DOCUMENT, document);
6330
+ throw new import_common19.DidDocumentError(`Invalid id: ${id}`, import_common19.INVALID_DID_DOCUMENT, document);
4999
6331
  }
5000
6332
  if (!_DidDocument.isValidVerificationMethods(vm)) {
5001
- throw new import_common17.DidDocumentError("Invalid verificationMethod: " + vm, import_common17.INVALID_DID_DOCUMENT, document);
6333
+ throw new import_common19.DidDocumentError("Invalid verificationMethod: " + vm, import_common19.INVALID_DID_DOCUMENT, document);
5002
6334
  }
5003
6335
  if (!_DidDocument.isValidServices(service)) {
5004
- throw new import_common17.DidDocumentError("Invalid service: " + service, import_common17.INVALID_DID_DOCUMENT, document);
6336
+ throw new import_common19.DidDocumentError("Invalid service: " + service, import_common19.INVALID_DID_DOCUMENT, document);
5005
6337
  }
5006
6338
  }
5007
6339
  this.id = document.id;
@@ -5011,7 +6343,7 @@ var DidDocument = class _DidDocument {
5011
6343
  "https://www.w3.org/ns/did/v1.1",
5012
6344
  "https://btcr2.dev/context/v1"
5013
6345
  ];
5014
- if (idType === import_common17.IdentifierTypes.KEY) {
6346
+ if (idType === import_common19.IdentifierTypes.KEY) {
5015
6347
  const keyRef = `${this.id}#initialKey`;
5016
6348
  this.authentication = document.authentication || [keyRef];
5017
6349
  this.assertionMethod = document.assertionMethod || [keyRef];
@@ -5097,19 +6429,19 @@ var DidDocument = class _DidDocument {
5097
6429
  */
5098
6430
  static isValid(didDocument) {
5099
6431
  if (!this.isValidContext(didDocument?.["@context"])) {
5100
- throw new import_common17.DidDocumentError('Invalid "@context"', import_common17.INVALID_DID_DOCUMENT, didDocument);
6432
+ throw new import_common19.DidDocumentError('Invalid "@context"', import_common19.INVALID_DID_DOCUMENT, didDocument);
5101
6433
  }
5102
6434
  if (!this.isValidId(didDocument?.id)) {
5103
- throw new import_common17.DidDocumentError('Invalid "id"', import_common17.INVALID_DID_DOCUMENT, didDocument);
6435
+ throw new import_common19.DidDocumentError('Invalid "id"', import_common19.INVALID_DID_DOCUMENT, didDocument);
5104
6436
  }
5105
6437
  if (!this.isValidVerificationMethods(didDocument?.verificationMethod)) {
5106
- throw new import_common17.DidDocumentError('Invalid "verificationMethod"', import_common17.INVALID_DID_DOCUMENT, didDocument);
6438
+ throw new import_common19.DidDocumentError('Invalid "verificationMethod"', import_common19.INVALID_DID_DOCUMENT, didDocument);
5107
6439
  }
5108
6440
  if (!this.isValidServices(didDocument?.service)) {
5109
- throw new import_common17.DidDocumentError('Invalid "service"', import_common17.INVALID_DID_DOCUMENT, didDocument);
6441
+ throw new import_common19.DidDocumentError('Invalid "service"', import_common19.INVALID_DID_DOCUMENT, didDocument);
5110
6442
  }
5111
6443
  if (!this.isValidVerificationRelationships(didDocument)) {
5112
- throw new import_common17.DidDocumentError("Invalid verification relationships", import_common17.INVALID_DID_DOCUMENT, didDocument);
6444
+ throw new import_common19.DidDocumentError("Invalid verification relationships", import_common19.INVALID_DID_DOCUMENT, didDocument);
5113
6445
  }
5114
6446
  return true;
5115
6447
  }
@@ -5156,7 +6488,7 @@ var DidDocument = class _DidDocument {
5156
6488
  * @returns {boolean} True if the services are valid.
5157
6489
  */
5158
6490
  static isValidServices(service) {
5159
- return Array.isArray(service) && service.every(import_utils8.isDidService);
6491
+ return Array.isArray(service) && service.every(import_utils10.isDidService);
5160
6492
  }
5161
6493
  /**
5162
6494
  * Validates verification relationships (authentication, assertionMethod, capabilityInvocation, capabilityDelegation).
@@ -5199,16 +6531,16 @@ var DidDocument = class _DidDocument {
5199
6531
  */
5200
6532
  validateGenesis() {
5201
6533
  if (this.id !== ID_PLACEHOLDER_VALUE) {
5202
- throw new import_common17.DidDocumentError("Invalid GenesisDocument ID", import_common17.INVALID_DID_DOCUMENT, this);
6534
+ throw new import_common19.DidDocumentError("Invalid GenesisDocument ID", import_common19.INVALID_DID_DOCUMENT, this);
5203
6535
  }
5204
6536
  if (!this.verificationMethod.every((vm) => vm.id.includes(ID_PLACEHOLDER_VALUE) && vm.controller === ID_PLACEHOLDER_VALUE)) {
5205
- throw new import_common17.DidDocumentError("Invalid GenesisDocument verificationMethod", import_common17.INVALID_DID_DOCUMENT, this);
6537
+ throw new import_common19.DidDocumentError("Invalid GenesisDocument verificationMethod", import_common19.INVALID_DID_DOCUMENT, this);
5206
6538
  }
5207
6539
  if (!this.service.every((svc) => svc.id.includes(ID_PLACEHOLDER_VALUE))) {
5208
- throw new import_common17.DidDocumentError("Invalid GenesisDocument service", import_common17.INVALID_DID_DOCUMENT, this);
6540
+ throw new import_common19.DidDocumentError("Invalid GenesisDocument service", import_common19.INVALID_DID_DOCUMENT, this);
5209
6541
  }
5210
6542
  if (!_DidDocument.isValidVerificationRelationships(this)) {
5211
- throw new import_common17.DidDocumentError("Invalid GenesisDocument assertionMethod", import_common17.INVALID_DID_DOCUMENT, this);
6543
+ throw new import_common19.DidDocumentError("Invalid GenesisDocument assertionMethod", import_common19.INVALID_DID_DOCUMENT, this);
5212
6544
  }
5213
6545
  return true;
5214
6546
  }
@@ -5218,7 +6550,7 @@ var DidDocument = class _DidDocument {
5218
6550
  */
5219
6551
  toIntermediate() {
5220
6552
  if (this.id.includes("k1")) {
5221
- throw new import_common17.DidDocumentError("Cannot convert a key identifier to an intermediate document", import_common17.INVALID_DID_DOCUMENT, this);
6553
+ throw new import_common19.DidDocumentError("Cannot convert a key identifier to an intermediate document", import_common19.INVALID_DID_DOCUMENT, this);
5222
6554
  }
5223
6555
  return new GenesisDocument(this);
5224
6556
  }
@@ -5248,7 +6580,7 @@ var GenesisDocument = class _GenesisDocument extends DidDocument {
5248
6580
  * @returns {GenesisDocument} The GenesisDocument representation of the DidDocument.
5249
6581
  */
5250
6582
  static fromDidDocument(didDocument) {
5251
- const intermediateDocument = import_common17.JSONUtils.cloneReplace(didDocument, DID_REGEX, ID_PLACEHOLDER_VALUE);
6583
+ const intermediateDocument = import_common19.JSONUtils.cloneReplace(didDocument, DID_REGEX, ID_PLACEHOLDER_VALUE);
5252
6584
  return new _GenesisDocument(intermediateDocument);
5253
6585
  }
5254
6586
  /**
@@ -5267,7 +6599,7 @@ var GenesisDocument = class _GenesisDocument extends DidDocument {
5267
6599
  * @returns {GenesisDocument} A new GenesisDocument with the placeholder ID.
5268
6600
  */
5269
6601
  static fromPublicKey(publicKey, network) {
5270
- const pk = new import_keypair3.CompressedSecp256k1PublicKey(publicKey);
6602
+ const pk = new import_keypair5.CompressedSecp256k1PublicKey(publicKey);
5271
6603
  const id = ID_PLACEHOLDER_VALUE;
5272
6604
  const address = (0, import_btc_signer6.p2pkh)(pk.compressed, (0, import_bitcoin3.getNetwork)(network)).address;
5273
6605
  const services = [{
@@ -5305,20 +6637,18 @@ var GenesisDocument = class _GenesisDocument extends DidDocument {
5305
6637
  * @returns {Bytes} The genesis bytes.
5306
6638
  */
5307
6639
  static toGenesisBytes(genesisDocument) {
5308
- return (0, import_common17.hash)((0, import_common17.canonicalize)(genesisDocument));
6640
+ return (0, import_common19.hash)((0, import_common19.canonicalize)(genesisDocument));
5309
6641
  }
5310
6642
  };
5311
6643
 
5312
6644
  // src/core/updater.ts
5313
6645
  var Updater = class _Updater {
5314
- #phase = "Construct" /* Construct */;
6646
+ #state = { phase: "Construct" };
5315
6647
  #sourceDocument;
5316
6648
  #patches;
5317
6649
  #sourceVersionId;
5318
6650
  #verificationMethod;
5319
6651
  #beaconService;
5320
- #unsignedUpdate = null;
5321
- #signedUpdate = null;
5322
6652
  /**
5323
6653
  * @internal — Use {@link DidBtcr2.update} to create instances.
5324
6654
  */
@@ -5352,19 +6682,26 @@ var Updater = class _Updater {
5352
6682
  patch: patches,
5353
6683
  targetHash: "",
5354
6684
  targetVersionId: sourceVersionId + 1,
5355
- sourceHash: (0, import_common18.canonicalHash)(sourceDocument)
6685
+ sourceHash: (0, import_common20.canonicalHash)(sourceDocument)
5356
6686
  };
5357
- const targetDocument = import_common18.JSONPatch.apply(sourceDocument, patches);
6687
+ const targetDocument = import_common20.JSONPatch.apply(sourceDocument, patches);
6688
+ if (targetDocument.id !== sourceDocument.id) {
6689
+ throw new import_common20.UpdateError(
6690
+ `Patches must not change the DID document id (source "${sourceDocument.id}" \u2192 target "${targetDocument.id}").`,
6691
+ import_common20.INVALID_DID_UPDATE,
6692
+ { sourceId: sourceDocument.id, targetId: targetDocument.id }
6693
+ );
6694
+ }
5358
6695
  try {
5359
6696
  DidDocument.isValid(targetDocument);
5360
6697
  } catch (error) {
5361
- throw new import_common18.UpdateError(
6698
+ throw new import_common20.UpdateError(
5362
6699
  "Error validating targetDocument: " + (error instanceof Error ? error.message : String(error)),
5363
- import_common18.INVALID_DID_UPDATE,
6700
+ import_common20.INVALID_DID_UPDATE,
5364
6701
  targetDocument
5365
6702
  );
5366
6703
  }
5367
- unsignedUpdate.targetHash = (0, import_common18.canonicalHash)(targetDocument);
6704
+ unsignedUpdate.targetHash = (0, import_common20.canonicalHash)(targetDocument);
5368
6705
  return unsignedUpdate;
5369
6706
  }
5370
6707
  /**
@@ -5373,13 +6710,28 @@ var Updater = class _Updater {
5373
6710
  * @param {string} did The did-btcr2 identifier to derive the root capability from.
5374
6711
  * @param {UnsignedBTCR2Update} unsignedUpdate The unsigned update to sign.
5375
6712
  * @param {DidVerificationMethod} verificationMethod The verification method for signing.
5376
- * @param {KeyBytes} secretKey The secret key bytes.
6713
+ * @param {Signer} signer Signer that produces the BIP-340 Schnorr signature.
5377
6714
  * @returns {SignedBTCR2Update} The signed update with a Data Integrity proof.
5378
6715
  */
5379
- static sign(did, unsignedUpdate, verificationMethod, secretKey) {
6716
+ static sign(did, unsignedUpdate, verificationMethod, signer) {
6717
+ if (!did.startsWith("did:btcr2:")) {
6718
+ throw new import_common20.UpdateError(
6719
+ `Expected a did:btcr2 identifier for the root capability; got "${did}".`,
6720
+ import_common20.INVALID_DID_UPDATE,
6721
+ { did }
6722
+ );
6723
+ }
5380
6724
  const controller = verificationMethod.controller;
5381
- const id = verificationMethod.id.slice(verificationMethod.id.indexOf("#"));
5382
- const multikey = import_cryptosuite2.SchnorrMultikey.fromSecretKey(id, controller, secretKey);
6725
+ const hashIdx = verificationMethod.id.indexOf("#");
6726
+ if (hashIdx < 0) {
6727
+ throw new import_common20.UpdateError(
6728
+ `Verification method id must contain a fragment (e.g. "${verificationMethod.id}#initialKey"); got "${verificationMethod.id}".`,
6729
+ import_common20.INVALID_DID_UPDATE,
6730
+ { verificationMethodId: verificationMethod.id }
6731
+ );
6732
+ }
6733
+ const id = verificationMethod.id.slice(hashIdx);
6734
+ const multikey = import_cryptosuite2.SchnorrMultikey.fromSigner(id, controller, signer);
5383
6735
  const config = {
5384
6736
  "@context": [
5385
6737
  "https://w3id.org/security/v2",
@@ -5403,24 +6755,20 @@ var Updater = class _Updater {
5403
6755
  *
5404
6756
  * @param {BeaconService} beaconService The beacon service to broadcast through.
5405
6757
  * @param {SignedBTCR2Update} update The signed update to announce.
5406
- * @param {KeyBytes} secretKey The secret key for signing the Bitcoin transaction.
6758
+ * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
5407
6759
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
5408
6760
  * @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
5409
6761
  */
5410
- static async announce(beaconService, update, secretKey, bitcoin) {
6762
+ static async announce(beaconService, update, signer, bitcoin) {
5411
6763
  const beacon = BeaconFactory.establish(beaconService);
5412
- return beacon.broadcastSignal(update, secretKey, bitcoin);
6764
+ return beacon.broadcastSignal(update, signer, bitcoin);
5413
6765
  }
5414
- // ─── Private instance wrappers ─────────────────────────────────────────────
6766
+ // Private instance wrappers
5415
6767
  // Delegate to the public statics with bound instance fields for cleaner
5416
6768
  // advance/provide code.
5417
6769
  #construct() {
5418
6770
  return _Updater.construct(this.#sourceDocument, this.#patches, this.#sourceVersionId);
5419
6771
  }
5420
- #sign(secretKey) {
5421
- return _Updater.sign(this.#sourceDocument.id, this.#unsignedUpdate, this.#verificationMethod, secretKey);
5422
- }
5423
- // ─── State machine ─────────────────────────────────────────────────────────
5424
6772
  /**
5425
6773
  * Advance the state machine. Returns either:
5426
6774
  * - `{ status: 'action-required', needs }` — caller must provide data via {@link provide}
@@ -5428,30 +6776,30 @@ var Updater = class _Updater {
5428
6776
  */
5429
6777
  advance() {
5430
6778
  while (true) {
5431
- switch (this.#phase) {
6779
+ switch (this.#state.phase) {
5432
6780
  // Phase: Construct
5433
6781
  // Build the unsigned update from source doc + patches. Pure, synchronous.
5434
- case "Construct" /* Construct */: {
5435
- this.#unsignedUpdate = this.#construct();
5436
- this.#phase = "Sign" /* Sign */;
6782
+ case "Construct": {
6783
+ const unsignedUpdate = this.#construct();
6784
+ this.#state = { phase: "Sign", unsignedUpdate };
5437
6785
  continue;
5438
6786
  }
5439
6787
  // Phase: Sign
5440
6788
  // Emit NeedSigningKey — the caller supplies the secret key (or a KMS signature).
5441
- case "Sign" /* Sign */: {
6789
+ case "Sign": {
5442
6790
  return {
5443
6791
  status: "action-required",
5444
6792
  needs: [{
5445
6793
  kind: "NeedSigningKey",
5446
6794
  verificationMethodId: this.#verificationMethod.id,
5447
- unsignedUpdate: this.#unsignedUpdate
6795
+ unsignedUpdate: this.#state.unsignedUpdate
5448
6796
  }]
5449
6797
  };
5450
6798
  }
5451
6799
  // Phase: Fund
5452
6800
  // Emit NeedFunding with the beacon address. The caller checks UTXOs,
5453
6801
  // funds the address if needed, and provides to continue.
5454
- case "Fund" /* Fund */: {
6802
+ case "Fund": {
5455
6803
  const beaconAddress = this.#beaconService.serviceEndpoint.replace("bitcoin:", "");
5456
6804
  return {
5457
6805
  status: "action-required",
@@ -5465,21 +6813,21 @@ var Updater = class _Updater {
5465
6813
  // Phase: Broadcast
5466
6814
  // Emit NeedBroadcast with the signed update + beacon service. The caller performs
5467
6815
  // the actual on-chain announcement (or hands off to the aggregation protocol).
5468
- case "Broadcast" /* Broadcast */: {
6816
+ case "Broadcast": {
5469
6817
  return {
5470
6818
  status: "action-required",
5471
6819
  needs: [{
5472
6820
  kind: "NeedBroadcast",
5473
6821
  beaconService: this.#beaconService,
5474
- signedUpdate: this.#signedUpdate
6822
+ signedUpdate: this.#state.signedUpdate
5475
6823
  }]
5476
6824
  };
5477
6825
  }
5478
6826
  // Phase: Complete
5479
- case "Complete" /* Complete */: {
6827
+ case "Complete": {
5480
6828
  return {
5481
6829
  status: "complete",
5482
- result: { signedUpdate: this.#signedUpdate }
6830
+ result: { signedUpdate: this.#state.signedUpdate }
5483
6831
  };
5484
6832
  }
5485
6833
  }
@@ -5488,49 +6836,63 @@ var Updater = class _Updater {
5488
6836
  provide(need, data) {
5489
6837
  switch (need.kind) {
5490
6838
  case "NeedSigningKey": {
5491
- if (this.#phase !== "Sign" /* Sign */) {
5492
- throw new import_common18.UpdateError(
5493
- `Cannot provide NeedSigningKey: updater phase is ${this.#phase}, expected Sign.`,
5494
- import_common18.INVALID_DID_UPDATE,
5495
- { phase: this.#phase }
6839
+ if (this.#state.phase !== "Sign") {
6840
+ throw new import_common20.UpdateError(
6841
+ `Cannot provide NeedSigningKey: updater phase is ${this.#state.phase}, expected Sign.`,
6842
+ import_common20.INVALID_DID_UPDATE,
6843
+ { phase: this.#state.phase }
5496
6844
  );
5497
6845
  }
5498
6846
  if (!data) {
5499
- throw new import_common18.UpdateError(
5500
- "NeedSigningKey requires secret key bytes.",
5501
- import_common18.INVALID_DID_UPDATE
5502
- );
5503
- }
5504
- if (!this.#unsignedUpdate) {
5505
- throw new import_common18.UpdateError(
5506
- "Internal error: unsigned update missing in Sign phase.",
5507
- import_common18.INVALID_DID_UPDATE
6847
+ throw new import_common20.UpdateError(
6848
+ "NeedSigningKey requires a Signer.",
6849
+ import_common20.INVALID_DID_UPDATE
5508
6850
  );
5509
6851
  }
5510
- this.#signedUpdate = this.#sign(data);
5511
- this.#phase = "Fund" /* Fund */;
6852
+ const unsignedUpdate = this.#state.unsignedUpdate;
6853
+ const signedUpdate = _Updater.sign(
6854
+ this.#sourceDocument.id,
6855
+ unsignedUpdate,
6856
+ this.#verificationMethod,
6857
+ data
6858
+ );
6859
+ this.#state = { phase: "Fund", unsignedUpdate, signedUpdate };
5512
6860
  break;
5513
6861
  }
5514
6862
  case "NeedFunding": {
5515
- if (this.#phase !== "Fund" /* Fund */) {
5516
- throw new import_common18.UpdateError(
5517
- `Cannot provide NeedFunding: updater phase is ${this.#phase}, expected Fund.`,
5518
- import_common18.INVALID_DID_UPDATE,
5519
- { phase: this.#phase }
6863
+ if (this.#state.phase !== "Fund") {
6864
+ throw new import_common20.UpdateError(
6865
+ `Cannot provide NeedFunding: updater phase is ${this.#state.phase}, expected Fund.`,
6866
+ import_common20.INVALID_DID_UPDATE,
6867
+ { phase: this.#state.phase }
5520
6868
  );
5521
6869
  }
5522
- this.#phase = "Broadcast" /* Broadcast */;
6870
+ if (data !== void 0) {
6871
+ const proof = data;
6872
+ if (typeof proof.utxoCount !== "number" || !Number.isFinite(proof.utxoCount) || proof.utxoCount < 1) {
6873
+ throw new import_common20.UpdateError(
6874
+ `NeedFunding proof must have utxoCount >= 1; got ${String(proof.utxoCount)}.`,
6875
+ import_common20.INVALID_DID_UPDATE,
6876
+ { utxoCount: proof.utxoCount }
6877
+ );
6878
+ }
6879
+ }
6880
+ this.#state = {
6881
+ phase: "Broadcast",
6882
+ unsignedUpdate: this.#state.unsignedUpdate,
6883
+ signedUpdate: this.#state.signedUpdate
6884
+ };
5523
6885
  break;
5524
6886
  }
5525
6887
  case "NeedBroadcast": {
5526
- if (this.#phase !== "Broadcast" /* Broadcast */) {
5527
- throw new import_common18.UpdateError(
5528
- `Cannot provide NeedBroadcast: updater phase is ${this.#phase}, expected Broadcast.`,
5529
- import_common18.INVALID_DID_UPDATE,
5530
- { phase: this.#phase }
6888
+ if (this.#state.phase !== "Broadcast") {
6889
+ throw new import_common20.UpdateError(
6890
+ `Cannot provide NeedBroadcast: updater phase is ${this.#state.phase}, expected Broadcast.`,
6891
+ import_common20.INVALID_DID_UPDATE,
6892
+ { phase: this.#state.phase }
5531
6893
  );
5532
6894
  }
5533
- this.#phase = "Complete" /* Complete */;
6895
+ this.#state = { phase: "Complete", signedUpdate: this.#state.signedUpdate };
5534
6896
  break;
5535
6897
  }
5536
6898
  }
@@ -5562,9 +6924,9 @@ var DidBtcr2 = class {
5562
6924
  static create(genesisBytes, options) {
5563
6925
  const { idType, version = 1, network = "bitcoin" } = options || {};
5564
6926
  if (!idType) {
5565
- throw new import_common19.MethodError(
6927
+ throw new import_common21.MethodError(
5566
6928
  "idType is required for creating a did:btcr2 identifier",
5567
- import_common19.INVALID_DID_DOCUMENT,
6929
+ import_common21.INVALID_DID_DOCUMENT,
5568
6930
  options
5569
6931
  );
5570
6932
  }
@@ -5594,7 +6956,7 @@ var DidBtcr2 = class {
5594
6956
  static resolve(did, resolutionOptions = {}) {
5595
6957
  const didComponents = Identifier.decode(did);
5596
6958
  const sidecarData = Resolver.sidecarData(resolutionOptions.sidecar);
5597
- const currentDocument = didComponents.hrp === import_common19.IdentifierHrp.k ? Resolver.deterministic(didComponents) : null;
6959
+ const currentDocument = didComponents.hrp === import_common21.IdentifierHrp.k ? Resolver.deterministic(didComponents) : null;
5598
6960
  return new Resolver(didComponents, sidecarData, currentDocument, {
5599
6961
  versionId: resolutionOptions.versionId,
5600
6962
  versionTime: resolutionOptions.versionTime,
@@ -5632,39 +6994,39 @@ var DidBtcr2 = class {
5632
6994
  beaconId
5633
6995
  }) {
5634
6996
  if (!sourceDocument.capabilityInvocation?.some((vr) => vr === verificationMethodId)) {
5635
- throw new import_common19.UpdateError(
6997
+ throw new import_common21.UpdateError(
5636
6998
  "Invalid verificationMethodId: not authorized for capabilityInvocation",
5637
- import_common19.INVALID_DID_DOCUMENT,
6999
+ import_common21.INVALID_DID_DOCUMENT,
5638
7000
  sourceDocument
5639
7001
  );
5640
7002
  }
5641
7003
  const verificationMethod = this.getSigningMethod(sourceDocument, verificationMethodId);
5642
7004
  if (!verificationMethod) {
5643
- throw new import_common19.UpdateError(
7005
+ throw new import_common21.UpdateError(
5644
7006
  "Invalid verificationMethod: not found in source document",
5645
- import_common19.INVALID_DID_DOCUMENT,
7007
+ import_common21.INVALID_DID_DOCUMENT,
5646
7008
  { sourceDocument, verificationMethodId }
5647
7009
  );
5648
7010
  }
5649
7011
  if (verificationMethod.type !== "Multikey") {
5650
- throw new import_common19.UpdateError(
7012
+ throw new import_common21.UpdateError(
5651
7013
  'Invalid verificationMethod: verificationMethod.type must be "Multikey"',
5652
- import_common19.INVALID_DID_DOCUMENT,
7014
+ import_common21.INVALID_DID_DOCUMENT,
5653
7015
  verificationMethod
5654
7016
  );
5655
7017
  }
5656
7018
  if (verificationMethod.publicKeyMultibase?.slice(0, 4) !== "zQ3s") {
5657
- throw new import_common19.UpdateError(
7019
+ throw new import_common21.UpdateError(
5658
7020
  'Invalid verificationMethodId: publicKeyMultibase prefix must start with "zQ3s"',
5659
- import_common19.INVALID_DID_DOCUMENT,
7021
+ import_common21.INVALID_DID_DOCUMENT,
5660
7022
  verificationMethod
5661
7023
  );
5662
7024
  }
5663
7025
  const beaconService = sourceDocument.service.filter((service) => service.id === beaconId).filter((service) => !!service).shift();
5664
7026
  if (!beaconService) {
5665
- throw new import_common19.UpdateError(
7027
+ throw new import_common21.UpdateError(
5666
7028
  "No beacon service found for provided beaconId",
5667
- import_common19.INVALID_DID_UPDATE,
7029
+ import_common21.INVALID_DID_UPDATE,
5668
7030
  { sourceDocument, beaconId }
5669
7031
  );
5670
7032
  }
@@ -5690,7 +7052,7 @@ var DidBtcr2 = class {
5690
7052
  methodId ??= "#initialKey";
5691
7053
  const parsedDid = import_dids2.Did.parse(didDocument.id);
5692
7054
  if (parsedDid && parsedDid.method !== this.methodName) {
5693
- throw new import_common19.MethodError(`Method not supported: ${parsedDid.method}`, import_common19.METHOD_NOT_SUPPORTED, { identifier: didDocument.id });
7055
+ throw new import_common21.MethodError(`Method not supported: ${parsedDid.method}`, import_common21.METHOD_NOT_SUPPORTED, { identifier: didDocument.id });
5694
7056
  }
5695
7057
  const verificationMethod = didDocument.verificationMethod?.find(
5696
7058
  (vm) => Appendix.extractDidFragment(vm.id) === (Appendix.extractDidFragment(methodId) ?? Appendix.extractDidFragment(didDocument.assertionMethod?.[0]))
@@ -5706,7 +7068,7 @@ var DidBtcr2 = class {
5706
7068
  };
5707
7069
 
5708
7070
  // src/core/resolver.ts
5709
- var import_utils10 = require("@noble/curves/utils.js");
7071
+ var import_utils12 = require("@noble/curves/utils.js");
5710
7072
  var Resolver = class _Resolver {
5711
7073
  // --- Immutable inputs ---
5712
7074
  #didComponents;
@@ -5746,7 +7108,7 @@ var Resolver = class _Resolver {
5746
7108
  static deterministic(didComponents) {
5747
7109
  const genesisBytes = didComponents.genesisBytes;
5748
7110
  const did = Identifier.encode(genesisBytes, didComponents);
5749
- const { multibase } = new import_keypair4.CompressedSecp256k1PublicKey(genesisBytes);
7111
+ const { multibase } = new import_keypair6.CompressedSecp256k1PublicKey(genesisBytes);
5750
7112
  const service = BeaconUtils.generateBeaconServices({
5751
7113
  id: did,
5752
7114
  publicKey: genesisBytes,
@@ -5772,14 +7134,14 @@ var Resolver = class _Resolver {
5772
7134
  * @throws {ResolveError} InvalidDidDocument if not conformant to DID Core v1.1
5773
7135
  */
5774
7136
  static external(didComponents, genesisDocument) {
5775
- const genesisDocumentHash = (0, import_common20.canonicalHashBytes)(genesisDocument);
5776
- if (!(0, import_utils10.equalBytes)(didComponents.genesisBytes, genesisDocumentHash)) {
5777
- throw new import_common20.ResolveError(
7137
+ const genesisDocumentHash = (0, import_common22.canonicalHashBytes)(genesisDocument);
7138
+ if (!(0, import_utils12.equalBytes)(didComponents.genesisBytes, genesisDocumentHash)) {
7139
+ throw new import_common22.ResolveError(
5778
7140
  `Initial document mismatch: genesisBytes !== genesisDocumentHash`,
5779
- import_common20.INVALID_DID_DOCUMENT,
7141
+ import_common22.INVALID_DID_DOCUMENT,
5780
7142
  {
5781
- genesisBytes: (0, import_common20.encode)(didComponents.genesisBytes, "hex"),
5782
- genesisDocumentHash: (0, import_common20.encode)(genesisDocumentHash, "hex")
7143
+ genesisBytes: (0, import_common22.encode)(didComponents.genesisBytes, "hex"),
7144
+ genesisDocumentHash: (0, import_common22.encode)(genesisDocumentHash, "hex")
5783
7145
  }
5784
7146
  );
5785
7147
  }
@@ -5798,17 +7160,17 @@ var Resolver = class _Resolver {
5798
7160
  const updateMap = /* @__PURE__ */ new Map();
5799
7161
  if (sidecar.updates?.length)
5800
7162
  for (const update of sidecar.updates) {
5801
- updateMap.set((0, import_common20.canonicalHash)(update, { encoding: "hex" }), update);
7163
+ updateMap.set((0, import_common22.canonicalHash)(update, { encoding: "hex" }), update);
5802
7164
  }
5803
7165
  const casMap = /* @__PURE__ */ new Map();
5804
7166
  if (sidecar.casUpdates?.length)
5805
7167
  for (const update of sidecar.casUpdates) {
5806
- casMap.set((0, import_common20.canonicalHash)(update, { encoding: "hex" }), update);
7168
+ casMap.set((0, import_common22.canonicalHash)(update, { encoding: "hex" }), update);
5807
7169
  }
5808
7170
  const smtMap = /* @__PURE__ */ new Map();
5809
7171
  if (sidecar.smtProofs?.length)
5810
7172
  for (const proof of sidecar.smtProofs) {
5811
- smtMap.set(proof.id, proof);
7173
+ smtMap.set((0, import_common22.encode)((0, import_common22.decode)(proof.id, "base64urlnopad"), "hex"), proof);
5812
7174
  }
5813
7175
  return { updateMap, casMap, smtMap };
5814
7176
  }
@@ -5836,12 +7198,12 @@ var Resolver = class _Resolver {
5836
7198
  }
5837
7199
  };
5838
7200
  for (const [update, block] of updates) {
5839
- const currentDocumentHash = (0, import_common20.canonicalHashBytes)(response.didDocument);
5840
- const blocktime = import_common20.DateUtils.blocktimeToTimestamp(block.time);
5841
- response.metadata.updated = import_common20.DateUtils.toISOStringNonFractional(blocktime);
7201
+ const currentDocumentHash = (0, import_common22.canonicalHashBytes)(response.didDocument);
7202
+ const blocktime = import_common22.DateUtils.blocktimeToTimestamp(block.time);
7203
+ response.metadata.updated = import_common22.DateUtils.toISOStringNonFractional(blocktime);
5842
7204
  response.metadata.confirmations = block.confirmations;
5843
7205
  if (versionTime) {
5844
- if (blocktime > import_common20.DateUtils.dateStringToTimestamp(versionTime)) {
7206
+ if (blocktime > import_common22.DateUtils.dateStringToTimestamp(versionTime)) {
5845
7207
  return response;
5846
7208
  }
5847
7209
  }
@@ -5849,22 +7211,22 @@ var Resolver = class _Resolver {
5849
7211
  updateHashHistory.push(currentDocumentHash);
5850
7212
  this.confirmDuplicate(update, updateHashHistory);
5851
7213
  } else if (update.targetVersionId === currentVersionId + 1) {
5852
- const sourceHashBytes = (0, import_common20.decode)(update.sourceHash, "base64urlnopad");
5853
- if (!(0, import_utils10.equalBytes)(sourceHashBytes, currentDocumentHash)) {
5854
- throw new import_common20.ResolveError(
7214
+ const sourceHashBytes = (0, import_common22.decode)(update.sourceHash, "base64urlnopad");
7215
+ if (!(0, import_utils12.equalBytes)(sourceHashBytes, currentDocumentHash)) {
7216
+ throw new import_common22.ResolveError(
5855
7217
  `Hash mismatch: update.sourceHash !== currentDocumentHash`,
5856
- import_common20.INVALID_DID_UPDATE,
7218
+ import_common22.INVALID_DID_UPDATE,
5857
7219
  {
5858
7220
  sourceHash: update.sourceHash,
5859
- currentDocumentHash: (0, import_common20.encode)(currentDocumentHash, "hex")
7221
+ currentDocumentHash: (0, import_common22.encode)(currentDocumentHash, "hex")
5860
7222
  }
5861
7223
  );
5862
7224
  }
5863
7225
  response.didDocument = this.applyUpdate(response.didDocument, update);
5864
- const unsignedUpdate = import_common20.JSONUtils.deleteKeys(update, ["proof"]);
5865
- updateHashHistory.push((0, import_common20.canonicalHashBytes)(unsignedUpdate));
7226
+ const unsignedUpdate = import_common22.JSONUtils.deleteKeys(update, ["proof"]);
7227
+ updateHashHistory.push((0, import_common22.canonicalHashBytes)(unsignedUpdate));
5866
7228
  } else if (update.targetVersionId > currentVersionId + 1) {
5867
- throw new import_common20.ResolveError(
7229
+ throw new import_common22.ResolveError(
5868
7230
  `Version Id Mismatch: targetVersionId cannot be > currentVersionId + 1`,
5869
7231
  "LATE_PUBLISHING_ERROR",
5870
7232
  {
@@ -5895,15 +7257,15 @@ var Resolver = class _Resolver {
5895
7257
  */
5896
7258
  static confirmDuplicate(update, updateHashHistory) {
5897
7259
  const { proof: _, ...unsignedUpdate } = update;
5898
- const unsignedUpdateHash = (0, import_common20.canonicalHashBytes)(unsignedUpdate);
7260
+ const unsignedUpdateHash = (0, import_common22.canonicalHashBytes)(unsignedUpdate);
5899
7261
  const historicalUpdateHash = updateHashHistory[update.targetVersionId - 2];
5900
- if (!(0, import_utils10.equalBytes)(historicalUpdateHash, unsignedUpdateHash)) {
5901
- throw new import_common20.ResolveError(
7262
+ if (!(0, import_utils12.equalBytes)(historicalUpdateHash, unsignedUpdateHash)) {
7263
+ throw new import_common22.ResolveError(
5902
7264
  `Invalid duplicate: unsigned update hash does not match historical hash`,
5903
- import_common20.LATE_PUBLISHING_ERROR,
7265
+ import_common22.LATE_PUBLISHING_ERROR,
5904
7266
  {
5905
- unsignedUpdateHash: (0, import_common20.encode)(unsignedUpdateHash, "hex"),
5906
- historicalHash: (0, import_common20.encode)(historicalUpdateHash, "hex")
7267
+ unsignedUpdateHash: (0, import_common22.encode)(unsignedUpdateHash, "hex"),
7268
+ historicalHash: (0, import_common22.encode)(historicalUpdateHash, "hex")
5907
7269
  }
5908
7270
  );
5909
7271
  }
@@ -5918,42 +7280,42 @@ var Resolver = class _Resolver {
5918
7280
  static applyUpdate(currentDocument, update) {
5919
7281
  const capabilityId = update.proof?.capability;
5920
7282
  if (!capabilityId) {
5921
- throw new import_common20.ResolveError("No root capability found in update", import_common20.INVALID_DID_UPDATE, update);
7283
+ throw new import_common22.ResolveError("No root capability found in update", import_common22.INVALID_DID_UPDATE, update);
5922
7284
  }
5923
7285
  const rootCapability = Appendix.dereferenceZcapId(capabilityId);
5924
7286
  const { invocationTarget, controller: rootController } = rootCapability;
5925
7287
  if (![invocationTarget, rootController].every((id) => id === currentDocument.id)) {
5926
- throw new import_common20.ResolveError(
7288
+ throw new import_common22.ResolveError(
5927
7289
  "Invalid root capability",
5928
- import_common20.INVALID_DID_UPDATE,
7290
+ import_common22.INVALID_DID_UPDATE,
5929
7291
  { rootCapability, currentDocument }
5930
7292
  );
5931
7293
  }
5932
7294
  const verificationMethodId = update.proof?.verificationMethod;
5933
7295
  if (!verificationMethodId) {
5934
- throw new import_common20.ResolveError("No verificationMethod found in update", import_common20.INVALID_DID_UPDATE, update);
7296
+ throw new import_common22.ResolveError("No verificationMethod found in update", import_common22.INVALID_DID_UPDATE, update);
5935
7297
  }
5936
7298
  const vm = DidBtcr2.getSigningMethod(currentDocument, verificationMethodId);
5937
7299
  const multikey = import_cryptosuite3.SchnorrMultikey.fromVerificationMethod(vm);
5938
7300
  const cryptosuite = new import_cryptosuite3.BIP340Cryptosuite(multikey);
5939
- const canonicalUpdate = (0, import_common20.canonicalize)(update);
7301
+ const canonicalUpdate = (0, import_common22.canonicalize)(update);
5940
7302
  const diProof = new import_cryptosuite3.BIP340DataIntegrityProof(cryptosuite);
5941
7303
  const verificationResult = diProof.verifyProof(canonicalUpdate, "capabilityInvocation");
5942
7304
  if (!verificationResult.verified) {
5943
- throw new import_common20.ResolveError(
7305
+ throw new import_common22.ResolveError(
5944
7306
  "Invalid update: proof not verified",
5945
- import_common20.INVALID_DID_UPDATE,
7307
+ import_common22.INVALID_DID_UPDATE,
5946
7308
  verificationResult
5947
7309
  );
5948
7310
  }
5949
- const updatedDocument = import_common20.JSONPatch.apply(currentDocument, update.patch);
7311
+ const updatedDocument = import_common22.JSONPatch.apply(currentDocument, update.patch);
5950
7312
  DidDocument.validate(updatedDocument);
5951
- const currentDocumentHash = (0, import_common20.canonicalHashBytes)(updatedDocument);
5952
- const updateTargetHash = (0, import_common20.decode)(update.targetHash);
5953
- if (!(0, import_utils10.equalBytes)(updateTargetHash, currentDocumentHash)) {
5954
- throw new import_common20.ResolveError(
7313
+ const currentDocumentHash = (0, import_common22.canonicalHashBytes)(updatedDocument);
7314
+ const updateTargetHash = (0, import_common22.decode)(update.targetHash);
7315
+ if (!(0, import_utils12.equalBytes)(updateTargetHash, currentDocumentHash)) {
7316
+ throw new import_common22.ResolveError(
5955
7317
  `Invalid update: update.targetHash !== currentDocumentHash`,
5956
- import_common20.INVALID_DID_UPDATE,
7318
+ import_common22.INVALID_DID_UPDATE,
5957
7319
  { updateTargetHash, currentDocumentHash }
5958
7320
  );
5959
7321
  }
@@ -5981,7 +7343,7 @@ var Resolver = class _Resolver {
5981
7343
  this.#phase = "BeaconDiscovery" /* BeaconDiscovery */;
5982
7344
  continue;
5983
7345
  }
5984
- const genesisHash = (0, import_common20.encode)(this.#didComponents.genesisBytes, "hex");
7346
+ const genesisHash = (0, import_common22.encode)(this.#didComponents.genesisBytes, "hex");
5985
7347
  return {
5986
7348
  status: "action-required",
5987
7349
  needs: [{ kind: "NeedGenesisDocument", genesisHash }]
@@ -6085,22 +7447,23 @@ var Resolver = class _Resolver {
6085
7447
  }
6086
7448
  case "NeedCASAnnouncement": {
6087
7449
  const announcement = data;
6088
- this.#sidecarData.casMap.set((0, import_common20.canonicalHash)(announcement, { encoding: "hex" }), announcement);
7450
+ this.#sidecarData.casMap.set((0, import_common22.canonicalHash)(announcement, { encoding: "hex" }), announcement);
6089
7451
  break;
6090
7452
  }
6091
7453
  case "NeedSignedUpdate": {
6092
7454
  const update = data;
6093
- this.#sidecarData.updateMap.set((0, import_common20.canonicalHash)(update, { encoding: "hex" }), update);
7455
+ this.#sidecarData.updateMap.set((0, import_common22.canonicalHash)(update, { encoding: "hex" }), update);
6094
7456
  break;
6095
7457
  }
6096
7458
  case "NeedSMTProof": {
6097
7459
  const smtNeed = need;
6098
7460
  const proof = data;
6099
- if (proof.id !== smtNeed.smtRootHash) {
6100
- throw new import_common20.ResolveError(
6101
- `SMT proof root hash mismatch: expected ${smtNeed.smtRootHash}, got ${proof.id}`,
6102
- import_common20.INVALID_DID_UPDATE,
6103
- { expected: smtNeed.smtRootHash, actual: proof.id }
7461
+ const proofIdHex = (0, import_common22.encode)((0, import_common22.decode)(proof.id, "base64urlnopad"), "hex");
7462
+ if (proofIdHex !== smtNeed.smtRootHash) {
7463
+ throw new import_common22.ResolveError(
7464
+ `SMT proof root hash mismatch: expected ${smtNeed.smtRootHash}, got ${proofIdHex}`,
7465
+ import_common22.INVALID_DID_UPDATE,
7466
+ { expected: smtNeed.smtRootHash, actual: proofIdHex }
6104
7467
  );
6105
7468
  }
6106
7469
  this.#sidecarData.smtMap.set(smtNeed.smtRootHash, proof);
@@ -6111,12 +7474,12 @@ var Resolver = class _Resolver {
6111
7474
  };
6112
7475
 
6113
7476
  // src/utils/did-document-builder.ts
6114
- var import_common21 = require("@did-btcr2/common");
7477
+ var import_common23 = require("@did-btcr2/common");
6115
7478
  var DidDocumentBuilder = class {
6116
7479
  document = {};
6117
7480
  constructor(initialDocument) {
6118
7481
  if (!initialDocument.id) {
6119
- throw new import_common21.DidDocumentError('Missing required "id" property', import_common21.INVALID_DID_DOCUMENT, initialDocument);
7482
+ throw new import_common23.DidDocumentError('Missing required "id" property', import_common23.INVALID_DID_DOCUMENT, initialDocument);
6120
7483
  }
6121
7484
  this.document.id = initialDocument.id;
6122
7485
  this.document.verificationMethod = initialDocument.verificationMethod ?? [];
@@ -6198,7 +7561,9 @@ var DidDocumentBuilder = class {
6198
7561
  CONSOLE_LOGGER,
6199
7562
  DEFAULT_ADVERT_REPEAT_INTERVAL_MS,
6200
7563
  DEFAULT_BROADCAST_LOOKBACK_MS,
7564
+ DEFAULT_CLOCK_SKEW_SEC,
6201
7565
  DEFAULT_MAX_UPDATE_SIZE_BYTES,
7566
+ DEFAULT_NONCE_LEN_BYTES,
6202
7567
  DEFAULT_NOSTR_RELAYS,
6203
7568
  DID_REGEX,
6204
7569
  DISTRIBUTE_AGGREGATED_DATA,
@@ -6209,16 +7574,31 @@ var DidDocumentBuilder = class {
6209
7574
  DidVerificationMethod,
6210
7575
  Document,
6211
7576
  GenesisDocument,
7577
+ HTTP_ENVELOPE_VERSION,
7578
+ HTTP_ROUTE,
7579
+ HttpClientTransport,
7580
+ HttpServerTransport,
7581
+ HttpTransportError,
6212
7582
  ID_PLACEHOLDER_VALUE,
6213
7583
  Identifier,
7584
+ InMemoryRateLimitStore,
7585
+ InboxBuffer,
6214
7586
  NONCE_CONTRIBUTION,
7587
+ NonceCache,
6215
7588
  NostrTransport,
7589
+ P2PKH_BEACON_TX_VSIZE,
7590
+ P2TR_BEACON_TX_VSIZE,
7591
+ P2WPKH_BEACON_TX_VSIZE,
6216
7592
  ParticipantCohortPhase,
7593
+ REQUEST_AUTH_SCHEME,
7594
+ RateLimiter,
6217
7595
  Resolver,
6218
7596
  SIGNATURE_AUTHORIZATION,
6219
7597
  SILENT_LOGGER,
7598
+ SINGLETON_BEACON_TX_VSIZE,
6220
7599
  SMTBeacon,
6221
7600
  SMTBeaconError,
7601
+ SSE_EVENT,
6222
7602
  SUBMIT_UPDATE,
6223
7603
  ServiceCohortPhase,
6224
7604
  SigningSessionError,
@@ -6233,6 +7613,7 @@ var DidDocumentBuilder = class {
6233
7613
  Updater,
6234
7614
  VALIDATION_ACK,
6235
7615
  buildAggregationBeaconTx,
7616
+ buildRequestAuth,
6236
7617
  createAggregatedNonceMessage,
6237
7618
  createAuthorizationRequestMessage,
6238
7619
  createCohortAdvertMessage,
@@ -6244,6 +7625,11 @@ var DidDocumentBuilder = class {
6244
7625
  createSignatureAuthorizationMessage,
6245
7626
  createSubmitUpdateMessage,
6246
7627
  createValidationAckMessage,
7628
+ defaultReconnectBackoff,
7629
+ deriveSingletonAddress,
7630
+ detectSingletonScriptKind,
7631
+ formatSseComment,
7632
+ formatSseEvent,
6247
7633
  getBeaconStrategy,
6248
7634
  isAggregatedNonceMessage,
6249
7635
  isAggregationMessageType,
@@ -6260,6 +7646,13 @@ var DidDocumentBuilder = class {
6260
7646
  isSubmitUpdateMessage,
6261
7647
  isUpdateMessageType,
6262
7648
  isValidationAckMessage,
7649
+ normalizeForWire,
6263
7650
  opReturnScript,
6264
- registerBeaconStrategy
7651
+ parseRequestAuth,
7652
+ parseSseStream,
7653
+ registerBeaconStrategy,
7654
+ reviveFromWire,
7655
+ signEnvelope,
7656
+ verifyEnvelope,
7657
+ verifyRequestAuth
6265
7658
  });