@bsv/sdk 1.2.2 → 1.2.4

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 (72) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/auth/Certificate.js +1 -1
  3. package/dist/cjs/src/auth/Certificate.js.map +1 -1
  4. package/dist/cjs/src/overlay-tools/LookupResolver.js +8 -5
  5. package/dist/cjs/src/overlay-tools/LookupResolver.js.map +1 -1
  6. package/dist/cjs/src/overlay-tools/OverlayAdminTokenTemplate.js.map +1 -1
  7. package/dist/cjs/src/overlay-tools/SHIPBroadcaster.js +2 -1
  8. package/dist/cjs/src/overlay-tools/SHIPBroadcaster.js.map +1 -1
  9. package/dist/cjs/src/transaction/Beef.js +19 -10
  10. package/dist/cjs/src/transaction/Beef.js.map +1 -1
  11. package/dist/cjs/src/transaction/Transaction.js +38 -6
  12. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  13. package/dist/cjs/src/wallet/CachedKeyDeriver.js.map +1 -1
  14. package/dist/cjs/src/wallet/KeyDeriver.js.map +1 -1
  15. package/dist/cjs/src/wallet/ProtoWallet.js.map +1 -1
  16. package/dist/cjs/src/wallet/WalletClient.js.map +1 -1
  17. package/dist/cjs/src/wallet/WalletError.js.map +1 -1
  18. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  19. package/dist/esm/src/auth/Certificate.js +1 -1
  20. package/dist/esm/src/auth/Certificate.js.map +1 -1
  21. package/dist/esm/src/overlay-tools/LookupResolver.js +8 -5
  22. package/dist/esm/src/overlay-tools/LookupResolver.js.map +1 -1
  23. package/dist/esm/src/overlay-tools/OverlayAdminTokenTemplate.js.map +1 -1
  24. package/dist/esm/src/overlay-tools/SHIPBroadcaster.js +2 -1
  25. package/dist/esm/src/overlay-tools/SHIPBroadcaster.js.map +1 -1
  26. package/dist/esm/src/transaction/Beef.js +19 -10
  27. package/dist/esm/src/transaction/Beef.js.map +1 -1
  28. package/dist/esm/src/transaction/Transaction.js +38 -6
  29. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  30. package/dist/esm/src/wallet/CachedKeyDeriver.js.map +1 -1
  31. package/dist/esm/src/wallet/KeyDeriver.js.map +1 -1
  32. package/dist/esm/src/wallet/ProtoWallet.js.map +1 -1
  33. package/dist/esm/src/wallet/WalletClient.js.map +1 -1
  34. package/dist/esm/src/wallet/WalletError.js.map +1 -1
  35. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  36. package/dist/types/src/auth/Certificate.d.ts.map +1 -1
  37. package/dist/types/src/overlay-tools/LookupResolver.d.ts +10 -3
  38. package/dist/types/src/overlay-tools/LookupResolver.d.ts.map +1 -1
  39. package/dist/types/src/overlay-tools/OverlayAdminTokenTemplate.d.ts.map +1 -1
  40. package/dist/types/src/overlay-tools/SHIPBroadcaster.d.ts.map +1 -1
  41. package/dist/types/src/transaction/Beef.d.ts.map +1 -1
  42. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  43. package/dist/types/src/wallet/CachedKeyDeriver.d.ts.map +1 -1
  44. package/dist/types/src/wallet/KeyDeriver.d.ts.map +1 -1
  45. package/dist/types/src/wallet/ProtoWallet.d.ts.map +1 -1
  46. package/dist/types/src/wallet/Wallet.interfaces.d.ts +11 -11
  47. package/dist/types/src/wallet/Wallet.interfaces.d.ts.map +1 -1
  48. package/dist/types/src/wallet/WalletClient.d.ts.map +1 -1
  49. package/dist/types/src/wallet/WalletError.d.ts.map +1 -1
  50. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  51. package/dist/umd/bundle.js +1 -1
  52. package/docs/overlay-tools.md +19 -4
  53. package/package.json +1 -1
  54. package/src/auth/Certificate.ts +9 -9
  55. package/src/auth/index.ts +1 -1
  56. package/src/overlay-tools/LookupResolver.ts +25 -9
  57. package/src/overlay-tools/OverlayAdminTokenTemplate.ts +4 -4
  58. package/src/overlay-tools/SHIPBroadcaster.ts +11 -9
  59. package/src/overlay-tools/__tests/LookupResolver.test.ts +83 -42
  60. package/src/overlay-tools/__tests/SHIPBroadcaster.test.ts +5 -5
  61. package/src/overlay-tools/index.ts +1 -1
  62. package/src/transaction/Beef.ts +33 -48
  63. package/src/transaction/BeefParty.ts +4 -4
  64. package/src/transaction/BeefTx.ts +1 -1
  65. package/src/transaction/Transaction.ts +38 -10
  66. package/src/transaction/__tests/Transaction.test.ts +133 -4
  67. package/src/wallet/CachedKeyDeriver.ts +10 -10
  68. package/src/wallet/KeyDeriver.ts +8 -8
  69. package/src/wallet/ProtoWallet.ts +176 -176
  70. package/src/wallet/Wallet.interfaces.ts +19 -19
  71. package/src/wallet/WalletClient.ts +30 -30
  72. package/src/wallet/WalletError.ts +2 -2
@@ -169,12 +169,27 @@ Facilitates lookups to URLs that return answers.
169
169
 
170
170
  ```ts
171
171
  export interface OverlayLookupFacilitator {
172
- lookup: (url: string, question: LookupQuestion) => Promise<LookupAnswer>;
172
+ lookup: (url: string, question: LookupQuestion, timeout?: number) => Promise<LookupAnswer>;
173
173
  }
174
174
  ```
175
175
 
176
176
  See also: [LookupAnswer](#type-lookupanswer), [LookupQuestion](#interface-lookupquestion)
177
177
 
178
+ <details>
179
+
180
+ <summary>Interface OverlayLookupFacilitator Details</summary>
181
+
182
+ #### Property lookup
183
+
184
+ Returns a lookup answer for a lookup question
185
+
186
+ ```ts
187
+ lookup: (url: string, question: LookupQuestion, timeout?: number) => Promise<LookupAnswer>
188
+ ```
189
+ See also: [LookupAnswer](#type-lookupanswer), [LookupQuestion](#interface-lookupquestion)
190
+
191
+ </details>
192
+
178
193
  Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
179
194
 
180
195
  ---
@@ -294,7 +309,7 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](
294
309
  export class HTTPSOverlayLookupFacilitator implements OverlayLookupFacilitator {
295
310
  fetchClient: typeof fetch;
296
311
  constructor(httpClient = fetch)
297
- async lookup(url: string, question: LookupQuestion): Promise<LookupAnswer>
312
+ async lookup(url: string, question: LookupQuestion, timeout: number = 5000): Promise<LookupAnswer>
298
313
  }
299
314
  ```
300
315
 
@@ -310,7 +325,7 @@ Represents an SHIP transaction broadcaster.
310
325
  ```ts
311
326
  export default class LookupResolver {
312
327
  constructor(config?: LookupResolverConfig)
313
- async query(question: LookupQuestion): Promise<LookupAnswer>
328
+ async query(question: LookupQuestion, timeout?: number): Promise<LookupAnswer>
314
329
  }
315
330
  ```
316
331
 
@@ -325,7 +340,7 @@ See also: [LookupAnswer](#type-lookupanswer), [LookupQuestion](#interface-lookup
325
340
  Given a LookupQuestion, returns a LookupAnswer. Aggregates across multiple services and supports resiliency.
326
341
 
327
342
  ```ts
328
- async query(question: LookupQuestion): Promise<LookupAnswer>
343
+ async query(question: LookupQuestion, timeout?: number): Promise<LookupAnswer>
329
344
  ```
330
345
  See also: [LookupAnswer](#type-lookupanswer), [LookupQuestion](#interface-lookupquestion)
331
346
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/sdk",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "type": "module",
5
5
  "description": "BSV Blockchain Software Development Kit",
6
6
  "main": "dist/cjs/mod.js",
@@ -54,14 +54,14 @@ export default class Certificate {
54
54
  * @param {Record<CertificateFieldNameUnder50Bytes, string>} fields - All the fields present in the certificate.
55
55
  * @param {HexString} signature - Certificate signature by the certifier's private key, DER encoded hex string.
56
56
  */
57
- constructor(
57
+ constructor (
58
58
  type: Base64String,
59
59
  serialNumber: Base64String,
60
60
  subject: PubKeyHex,
61
61
  certifier: PubKeyHex,
62
62
  revocationOutpoint: OutpointString,
63
63
  fields: Record<CertificateFieldNameUnder50Bytes, string>,
64
- signature?: HexString,
64
+ signature?: HexString
65
65
  ) {
66
66
  this.type = type
67
67
  this.serialNumber = serialNumber
@@ -78,7 +78,7 @@ export default class Certificate {
78
78
  * @param {boolean} [includeSignature=true] - Whether to include the signature in the serialization.
79
79
  * @returns {number[]} - The serialized certificate in binary format.
80
80
  */
81
- toBin(includeSignature: boolean = true): number[] {
81
+ toBin (includeSignature: boolean = true): number[] {
82
82
  const writer = new Utils.Writer()
83
83
 
84
84
  // Write type (Base64String, 32 bytes)
@@ -134,7 +134,7 @@ export default class Certificate {
134
134
  * @param {number[]} bin - The binary data representing the certificate.
135
135
  * @returns {Certificate} - The deserialized Certificate object.
136
136
  */
137
- static fromBin(bin: number[]): Certificate {
137
+ static fromBin (bin: number[]): Certificate {
138
138
  const reader = new Utils.Reader(bin)
139
139
 
140
140
  // Read type
@@ -166,7 +166,7 @@ export default class Certificate {
166
166
  // Field name
167
167
  const fieldNameLength = reader.readVarIntNum()
168
168
  const fieldNameBytes = reader.read(fieldNameLength)
169
- const fieldName = Utils.toUTF8(fieldNameBytes) as CertificateFieldNameUnder50Bytes
169
+ const fieldName = Utils.toUTF8(fieldNameBytes)
170
170
 
171
171
  // Field value
172
172
  const fieldValueLength = reader.readVarIntNum()
@@ -177,7 +177,7 @@ export default class Certificate {
177
177
  }
178
178
 
179
179
  // Read signature if present
180
- let signature: string | undefined = undefined
180
+ let signature: string | undefined
181
181
  if (!reader.eof()) {
182
182
  const signatureLength = reader.readVarIntNum()
183
183
  const signatureBytes = reader.read(signatureLength)
@@ -200,7 +200,7 @@ export default class Certificate {
200
200
  *
201
201
  * @returns {Promise<boolean>} - A promise that resolves to true if the signature is valid.
202
202
  */
203
- async verify(): Promise<boolean> {
203
+ async verify (): Promise<boolean> {
204
204
  // A verifier can be any wallet capable of verifying signatures
205
205
  const verifier = new ProtoWallet('anyone')
206
206
  const verificationData = this.toBin(false) // Exclude the signature from the verification data
@@ -221,7 +221,7 @@ export default class Certificate {
221
221
  * @param {Wallet} certifier - The wallet representing the certifier.
222
222
  * @returns {Promise<void>}
223
223
  */
224
- async sign(certifier: Wallet): Promise<void> {
224
+ async sign (certifier: Wallet): Promise<void> {
225
225
  const preimage = this.toBin(false) // Exclude the signature when signing
226
226
  const { signature } = await certifier.createSignature({
227
227
  data: preimage,
@@ -230,4 +230,4 @@ export default class Certificate {
230
230
  })
231
231
  this.signature = Utils.toHex(signature)
232
232
  }
233
- }
233
+ }
package/src/auth/index.ts CHANGED
@@ -1 +1 @@
1
- export { default as Certificate } from './Certificate.js'
1
+ export { default as Certificate } from './Certificate.js'
@@ -49,6 +49,8 @@ export const DEFAULT_SLAP_TRACKERS: string[] = [
49
49
  // Trackers known to host invalid or illegal records will be removed at the discretion of the BSV Association.
50
50
  ]
51
51
 
52
+ const MAX_TRACKER_WAIT_TIME = 1000
53
+
52
54
  /** Configuration options for the Lookup resolver. */
53
55
  export interface LookupResolverConfig {
54
56
  /** The facilitator used to make requests to Overlay Services hosts. */
@@ -63,27 +65,41 @@ export interface LookupResolverConfig {
63
65
 
64
66
  /** Facilitates lookups to URLs that return answers. */
65
67
  export interface OverlayLookupFacilitator {
66
- lookup: (url: string, question: LookupQuestion) => Promise<LookupAnswer>
68
+ /**
69
+ * Returns a lookup answer for a lookup question
70
+ * @param url - Overlay Service URL to send the lookup question to.
71
+ * @param question - Lookup question to find an answer to.
72
+ * @param timeout - Specifics how long to wait for a lookup answer in milliseconds.
73
+ * @returns
74
+ */
75
+ lookup: (url: string, question: LookupQuestion, timeout?: number) => Promise<LookupAnswer>
67
76
  }
68
77
 
69
78
  export class HTTPSOverlayLookupFacilitator implements OverlayLookupFacilitator {
70
79
  fetchClient: typeof fetch
71
80
 
72
- constructor(httpClient = fetch) {
81
+ constructor (httpClient = fetch) {
73
82
  this.fetchClient = httpClient
74
83
  }
75
84
 
76
- async lookup(url: string, question: LookupQuestion): Promise<LookupAnswer> {
85
+ async lookup (url: string, question: LookupQuestion, timeout: number = 5000): Promise<LookupAnswer> {
77
86
  if (!url.startsWith('https:')) {
78
87
  throw new Error('HTTPS facilitator can only use URLs that start with "https:"')
79
88
  }
80
- const response = await fetch(`${url}/lookup`, {
89
+ const timeoutPromise = new Promise((_, reject) =>
90
+ setTimeout(() => reject(new Error('Request timed out')), timeout)
91
+ )
92
+
93
+ const fetchPromise = fetch(`${url}/lookup`, {
81
94
  method: 'POST',
82
95
  headers: {
83
96
  'Content-Type': 'application/json'
84
97
  },
85
98
  body: JSON.stringify({ service: question.service, query: question.query })
86
99
  })
100
+
101
+ const response: Response = await Promise.race([fetchPromise, timeoutPromise]) as Response
102
+
87
103
  if (response.ok) {
88
104
  return await response.json()
89
105
  } else {
@@ -101,7 +117,7 @@ export default class LookupResolver {
101
117
  private readonly hostOverrides: Record<string, string[]>
102
118
  private readonly additionalHosts: Record<string, string[]>
103
119
 
104
- constructor(config?: LookupResolverConfig) {
120
+ constructor (config?: LookupResolverConfig) {
105
121
  const { facilitator, slapTrackers, hostOverrides, additionalHosts } = config ?? {} as LookupResolverConfig
106
122
  this.facilitator = facilitator ?? new HTTPSOverlayLookupFacilitator()
107
123
  this.slapTrackers = slapTrackers ?? DEFAULT_SLAP_TRACKERS
@@ -112,7 +128,7 @@ export default class LookupResolver {
112
128
  /**
113
129
  * Given a LookupQuestion, returns a LookupAnswer. Aggregates across multiple services and supports resiliency.
114
130
  */
115
- async query(question: LookupQuestion): Promise<LookupAnswer> {
131
+ async query (question: LookupQuestion, timeout?: number): Promise<LookupAnswer> {
116
132
  let competentHosts: string[] = []
117
133
  if (question.service === 'ls_slap') {
118
134
  competentHosts = this.slapTrackers
@@ -133,7 +149,7 @@ export default class LookupResolver {
133
149
 
134
150
  // Use Promise.allSettled to handle individual host failures
135
151
  const hostResponses = await Promise.allSettled(
136
- competentHosts.map(async host => await this.facilitator.lookup(host, question))
152
+ competentHosts.map(async host => await this.facilitator.lookup(host, question, timeout))
137
153
  )
138
154
 
139
155
  const successfulResponses = hostResponses
@@ -180,7 +196,7 @@ export default class LookupResolver {
180
196
  * @param service Service for which competent hosts are to be returned
181
197
  * @returns Array of hosts competent for resolving queries
182
198
  */
183
- private async findCompetentHosts(service: string): Promise<string[]> {
199
+ private async findCompetentHosts (service: string): Promise<string[]> {
184
200
  const query: LookupQuestion = {
185
201
  service: 'ls_slap',
186
202
  query: {
@@ -190,7 +206,7 @@ export default class LookupResolver {
190
206
 
191
207
  // Use Promise.allSettled to handle individual SLAP tracker failures
192
208
  const trackerResponses = await Promise.allSettled(
193
- this.slapTrackers.map(async tracker => await this.facilitator.lookup(tracker, query))
209
+ this.slapTrackers.map(async tracker => await this.facilitator.lookup(tracker, query, MAX_TRACKER_WAIT_TIME))
194
210
  )
195
211
 
196
212
  const hosts = new Set<string>()
@@ -15,7 +15,7 @@ export default class OverlayAdminTokenTemplate implements ScriptTemplate {
15
15
  * @param script Locking script comprising a SHIP or SLAP token to decode
16
16
  * @returns Decoded SHIP or SLAP advertisement
17
17
  */
18
- static decode(script: LockingScript): { protocol: 'SHIP' | 'SLAP', identityKey: string, domain: string, topicOrService: string } {
18
+ static decode (script: LockingScript): { protocol: 'SHIP' | 'SLAP', identityKey: string, domain: string, topicOrService: string } {
19
19
  const result = PushDrop.decode(script)
20
20
  if (result.fields.length < 4) {
21
21
  throw new Error('Invalid SHIP/SLAP advertisement!')
@@ -39,7 +39,7 @@ export default class OverlayAdminTokenTemplate implements ScriptTemplate {
39
39
  * Constructs a new Overlay Admin template instance
40
40
  * @param wallet Wallet to use for locking and unlocking
41
41
  */
42
- constructor(wallet: Wallet) {
42
+ constructor (wallet: Wallet) {
43
43
  this.pushDrop = new PushDrop(wallet)
44
44
  }
45
45
 
@@ -50,7 +50,7 @@ export default class OverlayAdminTokenTemplate implements ScriptTemplate {
50
50
  * @param topicOrService Topic or service to advertise
51
51
  * @returns Locking script comprising the advertisement token
52
52
  */
53
- async lock(protocol: 'SHIP' | 'SLAP', domain: string, topicOrService: string): Promise<LockingScript> {
53
+ async lock (protocol: 'SHIP' | 'SLAP', domain: string, topicOrService: string): Promise<LockingScript> {
54
54
  const { publicKey: identityKey } = await this.pushDrop.wallet.getPublicKey({ identityKey: true })
55
55
  return await this.pushDrop.lock(
56
56
  [
@@ -70,7 +70,7 @@ export default class OverlayAdminTokenTemplate implements ScriptTemplate {
70
70
  * @param protocol SHIP or SLAP, depending on the token to unlock
71
71
  * @returns Script unlocker capable of unlocking the advertisement token
72
72
  */
73
- unlock(protocol: 'SHIP' | 'SLAP'): {
73
+ unlock (protocol: 'SHIP' | 'SLAP'): {
74
74
  sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
75
75
  estimateLength: (tx: Transaction, inputIndex: number) => Promise<number>
76
76
  } {
@@ -56,14 +56,16 @@ export interface OverlayBroadcastFacilitator {
56
56
  send: (url: string, taggedBEEF: TaggedBEEF) => Promise<STEAK>
57
57
  }
58
58
 
59
+ const MAX_SHIP_QUERY_TIMEOUT = 1000
60
+
59
61
  export class HTTPSOverlayBroadcastFacilitator implements OverlayBroadcastFacilitator {
60
62
  httpClient: typeof fetch
61
63
 
62
- constructor(httpClient = fetch) {
64
+ constructor (httpClient = fetch) {
63
65
  this.httpClient = httpClient
64
66
  }
65
67
 
66
- async send(url: string, taggedBEEF: TaggedBEEF): Promise<STEAK> {
68
+ async send (url: string, taggedBEEF: TaggedBEEF): Promise<STEAK> {
67
69
  if (!url.startsWith('https:')) {
68
70
  throw new Error('HTTPS facilitator can only use URLs that start with "https:"')
69
71
  }
@@ -100,7 +102,7 @@ export default class SHIPCast implements Broadcaster {
100
102
  * @param {string[]} topics - The list of SHIP topic names where transactions are to be sent.
101
103
  * @param {SHIPBroadcasterConfig} config - Configuration options for the SHIP broadcaster.
102
104
  */
103
- constructor(topics: string[], config?: SHIPBroadcasterConfig) {
105
+ constructor (topics: string[], config?: SHIPBroadcasterConfig) {
104
106
  if (topics.length === 0) {
105
107
  throw new Error('At least one topic is required for broadcast.')
106
108
  }
@@ -127,7 +129,7 @@ export default class SHIPCast implements Broadcaster {
127
129
  * @param {Transaction} tx - The transaction to be sent.
128
130
  * @returns {Promise<BroadcastResponse | BroadcastFailure>} A promise that resolves to either a success or failure response.
129
131
  */
130
- async broadcast(tx: Transaction): Promise<BroadcastResponse | BroadcastFailure> {
132
+ async broadcast (tx: Transaction): Promise<BroadcastResponse | BroadcastFailure> {
131
133
  let beef: number[]
132
134
  try {
133
135
  beef = tx.toBEEF()
@@ -269,7 +271,7 @@ export default class SHIPCast implements Broadcaster {
269
271
  }
270
272
  }
271
273
 
272
- private checkAcknowledgmentFromAllHosts(hostAcknowledgments: Record<string, Set<string>>, requiredTopics: string[], require: 'all' | 'any'): boolean {
274
+ private checkAcknowledgmentFromAllHosts (hostAcknowledgments: Record<string, Set<string>>, requiredTopics: string[], require: 'all' | 'any'): boolean {
273
275
  for (const acknowledgedTopics of Object.values(hostAcknowledgments)) {
274
276
  if (require === 'all') {
275
277
  for (const topic of requiredTopics) {
@@ -293,7 +295,7 @@ export default class SHIPCast implements Broadcaster {
293
295
  return true
294
296
  }
295
297
 
296
- private checkAcknowledgmentFromAnyHost(hostAcknowledgments: Record<string, Set<string>>, requiredTopics: string[], require: 'all' | 'any'): boolean {
298
+ private checkAcknowledgmentFromAnyHost (hostAcknowledgments: Record<string, Set<string>>, requiredTopics: string[], require: 'all' | 'any'): boolean {
297
299
  if (require === 'all') {
298
300
  // All required topics must be acknowledged by at least one host
299
301
  for (const acknowledgedTopics of Object.values(hostAcknowledgments)) {
@@ -322,7 +324,7 @@ export default class SHIPCast implements Broadcaster {
322
324
  }
323
325
  }
324
326
 
325
- private checkAcknowledgmentFromSpecificHosts(hostAcknowledgments: Record<string, Set<string>>, requirements: Record<string, 'all' | 'any' | string[]>): boolean {
327
+ private checkAcknowledgmentFromSpecificHosts (hostAcknowledgments: Record<string, Set<string>>, requirements: Record<string, 'all' | 'any' | string[]>): boolean {
326
328
  for (const [host, requiredTopicsOrAllAny] of Object.entries(requirements)) {
327
329
  const acknowledgedTopics = hostAcknowledgments[host]
328
330
  if (!acknowledgedTopics) {
@@ -368,7 +370,7 @@ export default class SHIPCast implements Broadcaster {
368
370
  *
369
371
  * @returns A mapping of URLs for hosts interested in this transaction. Keys are URLs, values are which of our topics the specific host cares about.
370
372
  */
371
- private async findInterestedHosts(): Promise<Record<string, Set<string>>> {
373
+ private async findInterestedHosts (): Promise<Record<string, Set<string>>> {
372
374
  // TODO: cache the list of interested hosts to avoid spamming SHIP trackers.
373
375
  // TODO: Monetize the operation of the SHIP tracker system.
374
376
  // TODO: Cache ship/slap lookup with expiry (every 5min)
@@ -380,7 +382,7 @@ export default class SHIPCast implements Broadcaster {
380
382
  query: {
381
383
  topics: this.topics
382
384
  }
383
- })
385
+ }, MAX_SHIP_QUERY_TIMEOUT)
384
386
  if (answer.type !== 'output-list') {
385
387
  throw new Error('SHIP answer is not an output list.')
386
388
  }