@bsv/overlay 0.1.28 → 0.2.1

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 (57) hide show
  1. package/dist/cjs/package.json +2 -2
  2. package/dist/cjs/src/Engine.js +91 -12
  3. package/dist/cjs/src/Engine.js.map +1 -1
  4. package/dist/cjs/src/GASP/OverlayGASPRemote.js.map +1 -1
  5. package/dist/cjs/src/GASP/OverlayGASPStorage.js +1 -1
  6. package/dist/cjs/src/GASP/OverlayGASPStorage.js.map +1 -1
  7. package/dist/cjs/src/storage/knex/KnexStorage.js.map +1 -1
  8. package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.js.map +1 -1
  9. package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.js.map +1 -1
  10. package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.js.map +1 -1
  11. package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.js.map +1 -1
  12. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  13. package/dist/esm/src/Engine.js +89 -11
  14. package/dist/esm/src/Engine.js.map +1 -1
  15. package/dist/esm/src/GASP/OverlayGASPRemote.js.map +1 -1
  16. package/dist/esm/src/GASP/OverlayGASPStorage.js +1 -1
  17. package/dist/esm/src/GASP/OverlayGASPStorage.js.map +1 -1
  18. package/dist/esm/src/storage/knex/KnexStorage.js.map +1 -1
  19. package/dist/esm/src/storage/knex/migrations/2024-05-18-001-initial.js.map +1 -1
  20. package/dist/esm/src/storage/knex/migrations/2024-07-10-001-block-height.js.map +1 -1
  21. package/dist/esm/src/storage/knex/migrations/2024-07-17-001-transactions.js.map +1 -1
  22. package/dist/esm/src/storage/knex/migrations/2024-07-18-001-indexes.js.map +1 -1
  23. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  24. package/dist/types/mod.d.ts +1 -1
  25. package/dist/types/mod.d.ts.map +1 -1
  26. package/dist/types/src/Engine.d.ts.map +1 -1
  27. package/dist/types/src/GASP/OverlayGASPRemote.d.ts +1 -1
  28. package/dist/types/src/GASP/OverlayGASPRemote.d.ts.map +1 -1
  29. package/dist/types/src/GASP/OverlayGASPStorage.d.ts.map +1 -1
  30. package/dist/types/src/LookupService.d.ts +67 -48
  31. package/dist/types/src/LookupService.d.ts.map +1 -1
  32. package/dist/types/src/Output.d.ts +2 -2
  33. package/dist/types/src/Output.d.ts.map +1 -1
  34. package/dist/types/src/storage/Storage.d.ts.map +1 -1
  35. package/dist/types/src/storage/knex/KnexStorage.d.ts.map +1 -1
  36. package/dist/types/src/storage/knex/all-migrations.d.ts +2 -2
  37. package/dist/types/src/storage/knex/all-migrations.d.ts.map +1 -1
  38. package/dist/types/src/storage/knex/migrations/2024-05-18-001-initial.d.ts.map +1 -1
  39. package/dist/types/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts.map +1 -1
  40. package/dist/types/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts.map +1 -1
  41. package/dist/types/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts.map +1 -1
  42. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  43. package/mod.ts +1 -1
  44. package/package.json +2 -2
  45. package/src/Engine.ts +133 -60
  46. package/src/GASP/OverlayGASPRemote.ts +6 -6
  47. package/src/GASP/OverlayGASPStorage.ts +17 -17
  48. package/src/LookupService.ts +112 -49
  49. package/src/Output.ts +1 -1
  50. package/src/__tests/Engine.test.ts +38 -29
  51. package/src/storage/Storage.ts +0 -1
  52. package/src/storage/knex/KnexStorage.ts +12 -12
  53. package/src/storage/knex/all-migrations.ts +1 -1
  54. package/src/storage/knex/migrations/2024-05-18-001-initial.ts +2 -2
  55. package/src/storage/knex/migrations/2024-07-10-001-block-height.ts +2 -2
  56. package/src/storage/knex/migrations/2024-07-17-001-transactions.ts +2 -2
  57. package/src/storage/knex/migrations/2024-07-18-001-indexes.ts +2 -2
package/src/Engine.ts CHANGED
@@ -47,7 +47,7 @@ export class Engine {
47
47
  * @param {OverlayBroadcastFacilitator} overlayBroadcastFacilitator - Facilitator for propagation to other Overlay Services.
48
48
  * @param {typeof console} logger - The place where log entries are written.
49
49
  */
50
- constructor(
50
+ constructor (
51
51
  public managers: { [key: string]: TopicManager },
52
52
  public lookupServices: { [key: string]: LookupService },
53
53
  public storage: Storage,
@@ -76,14 +76,14 @@ export class Engine {
76
76
  if (managerName === 'tm_ship' && this.shipTrackers !== undefined && this.syncConfiguration[managerName] !== false) {
77
77
  // Combine tm_ship trackers with preexisting entries if any
78
78
  const combinedSet = new Set([
79
- ...(Array.isArray(this.syncConfiguration[managerName]) ? this.syncConfiguration[managerName] as string[] : []),
79
+ ...(Array.isArray(this.syncConfiguration[managerName]) ? this.syncConfiguration[managerName] : []),
80
80
  ...this.shipTrackers
81
81
  ])
82
82
  this.syncConfiguration[managerName] = Array.from(combinedSet)
83
83
  } else if (managerName === 'tm_slap' && this.slapTrackers !== undefined && this.syncConfiguration[managerName] !== false) {
84
84
  // Combine tm_slap trackers with preexisting entries if any
85
85
  const combinedSet = new Set([
86
- ...(Array.isArray(this.syncConfiguration[managerName]) ? this.syncConfiguration[managerName] as string[] : []),
86
+ ...(Array.isArray(this.syncConfiguration[managerName]) ? this.syncConfiguration[managerName] : []),
87
87
  ...this.slapTrackers
88
88
  ])
89
89
  this.syncConfiguration[managerName] = Array.from(combinedSet)
@@ -97,13 +97,13 @@ export class Engine {
97
97
  }
98
98
 
99
99
  // Helper functions for logging timings
100
- private startTime(label: string): void {
100
+ private startTime (label: string): void {
101
101
  if (this.logTime) {
102
102
  this.logger.time(`${this.logPrefix} ${label}`)
103
103
  }
104
104
  }
105
105
 
106
- private endTime(label: string): void {
106
+ private endTime (label: string): void {
107
107
  if (this.logTime) {
108
108
  this.logger.timeEnd(`${this.logPrefix} ${label}`)
109
109
  }
@@ -114,12 +114,12 @@ export class Engine {
114
114
  * @param {TaggedBEEF} taggedBEEF - The transaction to process
115
115
  * @param {function(STEAK): void} [onSTEAKReady] - Optional callback function invoked when the STEAK is ready.
116
116
  * @param {string} mode — Indicates the submission behavior, whether historical or current. Historical transactions are not broadcast or propagated.
117
- *
117
+ *
118
118
  * The optional callback function should be used to get STEAK when ready, and avoid waiting for broadcast and transaction propagation to complete.
119
- *
119
+ *
120
120
  * @returns {Promise<STEAK>} The submitted transaction execution acknowledgement
121
121
  */
122
- async submit(taggedBEEF: TaggedBEEF, onSteakReady?: (steak: STEAK) => void, mode: 'historical-tx' | 'current-tx' = 'current-tx'): Promise<STEAK> {
122
+ async submit (taggedBEEF: TaggedBEEF, onSteakReady?: (steak: STEAK) => void, mode: 'historical-tx' | 'current-tx' = 'current-tx'): Promise<STEAK> {
123
123
  for (const t of taggedBEEF.topics) {
124
124
  if (this.managers[t] === undefined || this.managers[t] === null) {
125
125
  throw new Error(`This server does not support this topic: ${t}`)
@@ -181,7 +181,53 @@ export class Engine {
181
181
  await this.storage.markUTXOAsSpent(output.txid, output.outputIndex, topic)
182
182
  await Promise.all(Object.values(this.lookupServices).map(async l => {
183
183
  try {
184
- await l.outputSpent?.(output.txid, output.outputIndex, topic)
184
+ if (typeof l.outputSpent === 'function') {
185
+ if (l.spendNotificationMode === 'txid') {
186
+ await l.outputSpent({
187
+ mode: 'txid',
188
+ spendingTxid: txid,
189
+ txid: output.txid,
190
+ outputIndex: output.outputIndex,
191
+ topic
192
+ })
193
+ } else if (l.spendNotificationMode === 'script') {
194
+ const inputIndex = tx.inputs.findIndex(i => {
195
+ let realSource = i.sourceTXID
196
+ if (!realSource) {
197
+ realSource = i.sourceTransaction?.id('hex')
198
+ }
199
+ return realSource === output.txid && i.sourceOutputIndex === output.outputIndex
200
+ })
201
+ if (!inputIndex) {
202
+ throw new Error('Could not find input index')
203
+ }
204
+ await l.outputSpent({
205
+ mode: 'script',
206
+ spendingTxid: txid,
207
+ inputIndex,
208
+ sequenceNumber: tx.inputs[inputIndex].sequence ?? 0xffffffff,
209
+ unlockingScript: tx.inputs[inputIndex].unlockingScript!,
210
+ txid: output.txid,
211
+ outputIndex: output.outputIndex,
212
+ topic
213
+ })
214
+ } else if (l.spendNotificationMode === 'whole-tx') {
215
+ await l.outputSpent({
216
+ mode: 'whole-tx',
217
+ spendingAtomicBEEF: tx.toAtomicBEEF(),
218
+ txid: output.txid,
219
+ outputIndex: output.outputIndex,
220
+ topic
221
+ })
222
+ } else { // none
223
+ await l.outputSpent({
224
+ mode: 'none',
225
+ txid: output.txid,
226
+ outputIndex: output.outputIndex,
227
+ topic
228
+ })
229
+ }
230
+ }
185
231
  } catch (error) {
186
232
  this.logger.error('Error in lookup service for outputSpent:', error)
187
233
  }
@@ -244,7 +290,7 @@ export class Engine {
244
290
 
245
291
  const outputsToMarkStale: Array<{
246
292
  txid: string
247
- previousOutputIndex: number,
293
+ previousOutputIndex: number
248
294
  inputIndex: number
249
295
  }> = []
250
296
 
@@ -262,12 +308,13 @@ export class Engine {
262
308
 
263
309
  // For each of the previous UTXOs for this topic, if the UTXO was not included in the list of UTXOs identified for retention, then it will be marked as stale.
264
310
  for (const inputIndex of previousCoins) {
265
- const previousTXID = tx.inputs[inputIndex].sourceTXID || tx.inputs[inputIndex].sourceTransaction?.id('hex') as string
311
+ const previousTXID = tx.inputs[inputIndex].sourceTXID ?? tx.inputs[inputIndex].sourceTransaction?.id('hex')
312
+ if (typeof previousTXID !== 'string') continue
266
313
  const previousOutputIndex = tx.inputs[inputIndex].sourceOutputIndex
267
314
  if (!admissibleOutputs.coinsToRetain.includes(inputIndex)) {
268
315
  outputsToMarkStale.push({
269
316
  txid: previousTXID,
270
- previousOutputIndex: previousOutputIndex,
317
+ previousOutputIndex,
271
318
  inputIndex
272
319
  })
273
320
  } else {
@@ -294,12 +341,13 @@ export class Engine {
294
341
  // Handle admittance and notification of incoming UTXOs
295
342
  const newUTXOs: Array<{ txid: string, outputIndex: number }> = []
296
343
  await Promise.all(outputsToAdmit.map(async outputIndex => {
344
+ if (typeof tx.outputs[outputIndex].satoshis !== 'number') return
297
345
  this.startTime(`insertNewOutput_${txid.substring(0, 10)}`)
298
346
  await this.storage.insertOutput({
299
347
  txid,
300
348
  outputIndex,
301
349
  outputScript: tx.outputs[outputIndex].lockingScript.toBinary(),
302
- satoshis: tx.outputs[outputIndex].satoshis as number,
350
+ satoshis: tx.outputs[outputIndex].satoshis,
303
351
  topic,
304
352
  spent: false,
305
353
  beef: taggedBEEF.beef,
@@ -310,7 +358,31 @@ export class Engine {
310
358
  newUTXOs.push({ txid, outputIndex })
311
359
 
312
360
  this.startTime(`notifyLookupService${txid.substring(0, 10)}`)
313
- await Promise.all(Object.values(this.lookupServices).map(async l => await l.outputAdded?.(txid, outputIndex, tx.outputs[outputIndex].lockingScript, topic)))
361
+ await Promise.all(Object.values(this.lookupServices).map(async l => {
362
+ if (l.admissionMode === 'locking-script') {
363
+ if (
364
+ typeof tx.outputs[outputIndex].lockingScript !== 'object' ||
365
+ typeof tx.outputs[outputIndex].satoshis !== 'number'
366
+ ) {
367
+ return
368
+ }
369
+ await l.outputAdmittedByTopic({
370
+ mode: 'locking-script',
371
+ txid,
372
+ outputIndex,
373
+ lockingScript: tx.outputs[outputIndex].lockingScript,
374
+ satoshis: tx.outputs[outputIndex].satoshis,
375
+ topic
376
+ })
377
+ } else {
378
+ await l.outputAdmittedByTopic({
379
+ mode: 'whole-tx',
380
+ atomicBEEF: tx.toAtomicBEEF(),
381
+ outputIndex,
382
+ topic
383
+ })
384
+ }
385
+ }))
314
386
  this.endTime(`notifyLookupService${txid.substring(0, 10)}`)
315
387
  }))
316
388
 
@@ -376,7 +448,7 @@ export class Engine {
376
448
  * @param LookupQuestion — The question to ask the Overlay Services Engine
377
449
  * @returns The answer to the question
378
450
  */
379
- async lookup(lookupQuestion: LookupQuestion): Promise<LookupAnswer> {
451
+ async lookup (lookupQuestion: LookupQuestion): Promise<LookupAnswer> {
380
452
  // Validate a lookup service for the provider is found
381
453
  const lookupService = this.lookupServices[lookupQuestion.service]
382
454
  if (lookupService === undefined || lookupService === null) throw new Error(`Lookup service not found for provider: ${lookupQuestion.service} `)
@@ -417,7 +489,7 @@ export class Engine {
417
489
  }
418
490
 
419
491
  /**
420
- * Ensures alignment between the current SHIP/SLAP advertisements and the
492
+ * Ensures alignment between the current SHIP/SLAP advertisements and the
421
493
  * configured Topic Managers and Lookup Services in the engine.
422
494
  *
423
495
  * This method performs the following actions:
@@ -435,7 +507,7 @@ export class Engine {
435
507
  * @throws Will throw an error if there are issues during the advertisement synchronization process.
436
508
  * @returns {Promise<void>} A promise that resolves when the synchronization process is complete.
437
509
  */
438
- async syncAdvertisements(): Promise<void> {
510
+ async syncAdvertisements (): Promise<void> {
439
511
  if (
440
512
  this.advertiser === undefined ||
441
513
  typeof this.hostingURL !== 'string' ||
@@ -501,7 +573,7 @@ export class Engine {
501
573
  *
502
574
  * @throws Error if the overlay service engine is not configured for topical synchronization.
503
575
  */
504
- async startGASPSync(): Promise<void> {
576
+ async startGASPSync (): Promise<void> {
505
577
  if (this.syncConfiguration === undefined) {
506
578
  throw new Error('Overlay Service Engine not configured for topical synchronization!')
507
579
  }
@@ -595,7 +667,7 @@ export class Engine {
595
667
  * @param topic - The topic for which UTXOs are being requested.
596
668
  * @returns A promise that resolves to a GASPInitialResponse containing the list of UTXOs and the provided min block height.
597
669
  */
598
- async provideForeignSyncResponse(initialRequest: GASPInitialRequest, topic: string): Promise<GASPInitialResponse> {
670
+ async provideForeignSyncResponse (initialRequest: GASPInitialRequest, topic: string): Promise<GASPInitialResponse> {
599
671
  const UTXOs = await this.storage.findUTXOsForTopic(topic, initialRequest.since)
600
672
 
601
673
  return {
@@ -618,7 +690,7 @@ export class Engine {
618
690
  * @returns A promise that resolves to a GASPNode containing the raw transaction and other optional data.
619
691
  * @throws An error if no output is found for the given transaction ID and output index.
620
692
  */
621
- async provideForeignGASPNode(graphID: string, txid: string, outputIndex: number): Promise<GASPNode> {
693
+ async provideForeignGASPNode (graphID: string, txid: string, outputIndex: number): Promise<GASPNode> {
622
694
  const hydrator = async (output: Output | null): Promise<GASPNode> => {
623
695
  if (output?.beef === undefined) {
624
696
  throw new Error('No matching output found!')
@@ -696,7 +768,7 @@ export class Engine {
696
768
  *
697
769
  * @returns {Promise<Output | undefined>} - A promise that resolves to the output history if found, or undefined if not.
698
770
  */
699
- async getUTXOHistory(
771
+ async getUTXOHistory (
700
772
  output: Output,
701
773
  historySelector?: ((beef: number[], outputIndex: number, currentDepth: number) => Promise<boolean>) | number,
702
774
  currentDepth = 0
@@ -777,7 +849,7 @@ export class Engine {
777
849
  * @param output - The UTXO to be deleted.
778
850
  * @returns {Promise<void>} - A promise that resolves when the deletion process is complete.
779
851
  */
780
- private async deleteUTXODeep(output: Output): Promise<void> {
852
+ private async deleteUTXODeep (output: Output): Promise<void> {
781
853
  try {
782
854
  // Delete the current output IFF there are no references to it
783
855
  if (output.consumedBy.length === 0) {
@@ -786,7 +858,7 @@ export class Engine {
786
858
  // Notify the lookup services of the UTXO being deleted
787
859
  for (const l of Object.values(this.lookupServices)) {
788
860
  try {
789
- await l.outputDeleted?.(
861
+ await l.outputNoLongerRetainedInHistory?.(
790
862
  output.txid,
791
863
  output.outputIndex,
792
864
  output.topic
@@ -833,7 +905,7 @@ export class Engine {
833
905
  * @param txid BE hex string double hash of transaction proven by proof.
834
906
  * @param proof for txid
835
907
  */
836
- private updateInputProofs(tx: Transaction, txid: string, proof: MerklePath): void {
908
+ private updateInputProofs (tx: Transaction, txid: string, proof: MerklePath): void {
837
909
  if (tx.merklePath !== undefined) {
838
910
  // Update the merkle path to handle potential reorgs
839
911
  tx.merklePath = proof
@@ -844,7 +916,8 @@ export class Engine {
844
916
  } else {
845
917
  for (const input of tx.inputs) {
846
918
  // All inputs must have sourceTransactions
847
- const stx = input.sourceTransaction!
919
+ const stx = input.sourceTransaction
920
+ if (typeof stx !== 'object') continue
848
921
  this.updateInputProofs(stx, txid, proof)
849
922
  }
850
923
  }
@@ -857,7 +930,7 @@ export class Engine {
857
930
  * @param txid - The txid for which proof is a valid merkle path.
858
931
  * @param proof - The merklePath proving txid is a mined transaction hash
859
932
  */
860
- private async updateMerkleProof(output: Output, txid: string, proof: MerklePath): Promise<void> {
933
+ private async updateMerkleProof (output: Output, txid: string, proof: MerklePath): Promise<void> {
861
934
  if (output.beef === undefined) {
862
935
  throw new Error('Output must have associated transaction BEEF!')
863
936
  }
@@ -891,7 +964,7 @@ export class Engine {
891
964
  * @param proof - Merkle proof containing the Merkle path and other relevant data to verify the transaction.
892
965
  * @param blockHeight - The block height associated with the incoming merkle proof.
893
966
  */
894
- async handleNewMerkleProof(txid: string, proof: MerklePath, blockHeight?: number): Promise<void> {
967
+ async handleNewMerkleProof (txid: string, proof: MerklePath, blockHeight?: number): Promise<void> {
895
968
  const outputs = await this.storage.findOutputsForTransaction(txid, true)
896
969
 
897
970
  if (outputs === undefined || outputs.length === 0) {
@@ -914,21 +987,21 @@ export class Engine {
914
987
  * @public
915
988
  * @returns {Promise<Record<string, { name: string; shortDescription: string; iconURL?: string; version?: string; informationURL?: string; }>>} - Supported topic managers and their metadata
916
989
  */
917
- async listTopicManagers(): Promise<Record<string, {
918
- name: string;
919
- shortDescription: string;
920
- iconURL?: string;
921
- version?: string;
922
- informationURL?: string;
990
+ async listTopicManagers (): Promise<Record<string, {
991
+ name: string
992
+ shortDescription: string
993
+ iconURL?: string
994
+ version?: string
995
+ informationURL?: string
923
996
  }>> {
924
- let result: Record<string, {
925
- name: string;
926
- shortDescription: string;
927
- iconURL?: string;
928
- version?: string;
929
- informationURL?: string;
997
+ const result: Record<string, {
998
+ name: string
999
+ shortDescription: string
1000
+ iconURL?: string
1001
+ version?: string
1002
+ informationURL?: string
930
1003
  }> = {}
931
- for (let t in this.managers) {
1004
+ for (const t in this.managers) {
932
1005
  try {
933
1006
  result[t] = await this.managers[t].getMetaData()
934
1007
  } catch (e) {
@@ -947,21 +1020,21 @@ export class Engine {
947
1020
  * @public
948
1021
  * @returns {Promise<Record<string, { name: string; shortDescription: string; iconURL?: string; version?: string; informationURL?: string; }>>} - Supported lookup services and their metadata
949
1022
  */
950
- async listLookupServiceProviders(): Promise<Record<string, {
951
- name: string;
952
- shortDescription: string;
953
- iconURL?: string;
954
- version?: string;
955
- informationURL?: string;
1023
+ async listLookupServiceProviders (): Promise<Record<string, {
1024
+ name: string
1025
+ shortDescription: string
1026
+ iconURL?: string
1027
+ version?: string
1028
+ informationURL?: string
956
1029
  }>> {
957
- let result: Record<string, {
958
- name: string;
959
- shortDescription: string;
960
- iconURL?: string;
961
- version?: string;
962
- informationURL?: string;
1030
+ const result: Record<string, {
1031
+ name: string
1032
+ shortDescription: string
1033
+ iconURL?: string
1034
+ version?: string
1035
+ informationURL?: string
963
1036
  }> = {}
964
- for (let ls in this.lookupServices) {
1037
+ for (const ls in this.lookupServices) {
965
1038
  try {
966
1039
  result[ls] = await this.lookupServices[ls].getMetaData()
967
1040
  } catch (e) {
@@ -980,7 +1053,7 @@ export class Engine {
980
1053
  * @public
981
1054
  * @returns {Promise<string>} - the documentation for the topic manager
982
1055
  */
983
- async getDocumentationForTopicManager(manager: any): Promise<string> {
1056
+ async getDocumentationForTopicManager (manager: any): Promise<string> {
984
1057
  const documentation = await this.managers[manager]?.getDocumentation?.()
985
1058
  return documentation !== undefined ? documentation : 'No documentation found!'
986
1059
  }
@@ -990,7 +1063,7 @@ export class Engine {
990
1063
  * @public
991
1064
  * @returns {Promise<string>} - the documentation for the lookup service
992
1065
  */
993
- async getDocumentationForLookupServiceProvider(provider: any): Promise<string> {
1066
+ async getDocumentationForLookupServiceProvider (provider: any): Promise<string> {
994
1067
  const documentation = await this.lookupServices[provider]?.getDocumentation?.()
995
1068
  return documentation !== undefined ? documentation : 'No documentation found!'
996
1069
  }
@@ -1005,12 +1078,12 @@ export class Engine {
1005
1078
  * @param url - The URL string to validate
1006
1079
  * @returns {boolean} - Returns `false` if the URL violates any of the conditions `true` otherwise
1007
1080
  */
1008
- private isValidUrl(url: string): boolean {
1081
+ private isValidUrl (url: string): boolean {
1009
1082
  try {
1010
1083
  const parsedUrl = new URL(url)
1011
1084
 
1012
1085
  // Disallow http:
1013
- if (parsedUrl.protocol === "http:") {
1086
+ if (parsedUrl.protocol === 'http:') {
1014
1087
  return false
1015
1088
  }
1016
1089
 
@@ -1026,9 +1099,9 @@ export class Engine {
1026
1099
  const nonRoutableIpv4Patterns = [
1027
1100
  /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/, // Loopback IPs
1028
1101
  /^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/, // 10.x.x.x private IPs
1029
- /^192\.168\.\d{1,3}\.\d{1,3}$/, // 192.168.x.x private IPs
1102
+ /^192\.168\.\d{1,3}\.\d{1,3}$/, // 192.168.x.x private IPs
1030
1103
  /^172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}$/, // 172.16.x.x to 172.31.x.x private IPs
1031
- /^0\.0\.0\.0$/ // Non-routable address
1104
+ /^0\.0\.0\.0$/ // Non-routable address
1032
1105
  ]
1033
1106
 
1034
1107
  // Check for IPv4 matches
@@ -1037,7 +1110,7 @@ export class Engine {
1037
1110
  }
1038
1111
 
1039
1112
  // Check for non-routable IPv6 addresses explicitly
1040
- if (ipAddress === "[::1]") {
1113
+ if (ipAddress === '[::1]') {
1041
1114
  return false
1042
1115
  }
1043
1116
 
@@ -1,14 +1,14 @@
1
- import { GASPInitialReply, GASPInitialRequest, GASPInitialResponse, GASPNode, GASPNodeResponse, GASPRemote } from "@bsv/gasp"
1
+ import { GASPInitialReply, GASPInitialRequest, GASPInitialResponse, GASPNode, GASPNodeResponse, GASPRemote } from '@bsv/gasp'
2
2
 
3
3
  export class OverlayGASPRemote implements GASPRemote {
4
- constructor(public endpointURL: string, public topic: string) { }
4
+ constructor (public endpointURL: string, public topic: string) { }
5
5
 
6
6
  /**
7
7
  * Given an outgoing initial request, sends the request to the foreign instance and obtains their initial response.
8
8
  * @param request
9
9
  * @returns
10
10
  */
11
- async getInitialResponse(request: GASPInitialRequest): Promise<GASPInitialResponse> {
11
+ async getInitialResponse (request: GASPInitialRequest): Promise<GASPInitialResponse> {
12
12
  // Send out an HTTP request to the URL (current host for topic)
13
13
  // Include the topic in the request
14
14
  // Parse out response and return correct format
@@ -50,7 +50,7 @@ export class OverlayGASPRemote implements GASPRemote {
50
50
  * @param metadata
51
51
  * @returns
52
52
  */
53
- async requestNode(graphID: string, txid: string, outputIndex: number, metadata: boolean): Promise<GASPNode> {
53
+ async requestNode (graphID: string, txid: string, outputIndex: number, metadata: boolean): Promise<GASPNode> {
54
54
  // Send an HTTP request with the provided info and get back a gaspNode
55
55
  const url = `${this.endpointURL}/requestForeignGASPNode`
56
56
  const body = {
@@ -95,13 +95,13 @@ export class OverlayGASPRemote implements GASPRemote {
95
95
  // ---- Now optional methods ----
96
96
 
97
97
  // When are only syncing to them
98
- async getInitialReply(response: GASPInitialResponse): Promise<GASPInitialReply> {
98
+ async getInitialReply (response: GASPInitialResponse): Promise<GASPInitialReply> {
99
99
  throw new Error('Function not supported!')
100
100
  }
101
101
 
102
102
  // Only used when supporting bidirectional sync.
103
103
  // Overlay services does not support this.
104
- async submitNode(node: GASPNode): Promise<void | GASPNodeResponse> {
104
+ async submitNode (node: GASPNode): Promise<void | GASPNodeResponse> {
105
105
  throw new Error('Node submission not supported!')
106
106
  }
107
107
  }
@@ -22,14 +22,14 @@ export interface GraphNode {
22
22
  export class OverlayGASPStorage implements GASPStorage {
23
23
  readonly temporaryGraphNodeRefs: Record<string, GraphNode> = {}
24
24
 
25
- constructor(public topic: string, public engine: Engine, public maxNodesInGraph?: number) { }
25
+ constructor (public topic: string, public engine: Engine, public maxNodesInGraph?: number) { }
26
26
 
27
27
  /**
28
28
  *
29
29
  * @param since
30
30
  * @returns
31
31
  */
32
- async findKnownUTXOs(since: number): Promise<Array<{ txid: string, outputIndex: number }>> {
32
+ async findKnownUTXOs (since: number): Promise<Array<{ txid: string, outputIndex: number }>> {
33
33
  const UTXOs = await this.engine.storage.findUTXOsForTopic(this.topic, since)
34
34
  return UTXOs.map(output => ({
35
35
  txid: output.txid,
@@ -39,13 +39,13 @@ export class OverlayGASPStorage implements GASPStorage {
39
39
 
40
40
  /**
41
41
  * For a given txid and output index, returns the associated transaction, a merkle proof if the transaction is in a block, and metadata if if requested. If no metadata is requested, metadata hashes on inputs are not returned.
42
- * @param graphID
43
- * @param txid
44
- * @param outputIndex
45
- * @param metadata
46
- * @returns
42
+ * @param graphID
43
+ * @param txid
44
+ * @param outputIndex
45
+ * @param metadata
46
+ * @returns
47
47
  */
48
- async hydrateGASPNode(graphID: string, txid: string, outputIndex: number, metadata: boolean): Promise<GASPNode> {
48
+ async hydrateGASPNode (graphID: string, txid: string, outputIndex: number, metadata: boolean): Promise<GASPNode> {
49
49
  const output = await this.engine.storage.findOutput(txid, outputIndex, undefined, undefined, true)
50
50
 
51
51
  if (output?.beef === undefined) {
@@ -72,7 +72,7 @@ export class OverlayGASPStorage implements GASPStorage {
72
72
  * @param tx The node for which needed inputs should be found.
73
73
  * @returns A promise for a mapping of requested input transactions and whether metadata should be provided for each.
74
74
  */
75
- async findNeededInputs(tx: GASPNode): Promise<GASPNodeResponse | undefined> {
75
+ async findNeededInputs (tx: GASPNode): Promise<GASPNodeResponse | undefined> {
76
76
  // If there is no Merkle proof, we always need the inputs
77
77
  const response: GASPNodeResponse = {
78
78
  requestedInputs: {}
@@ -120,7 +120,7 @@ export class OverlayGASPStorage implements GASPStorage {
120
120
  * Ensures that no inputs are requested from foreign nodes before sending any GASP response
121
121
  * Also terminates graphs if the response would be empty.
122
122
  */
123
- private async stripAlreadyKnownInputs(response: GASPNodeResponse | undefined): Promise<GASPNodeResponse | undefined> {
123
+ private async stripAlreadyKnownInputs (response: GASPNodeResponse | undefined): Promise<GASPNodeResponse | undefined> {
124
124
  if (typeof response === 'undefined') {
125
125
  return response
126
126
  }
@@ -144,7 +144,7 @@ export class OverlayGASPStorage implements GASPStorage {
144
144
  * @param spentBy Unless this is the same node identified by the graph ID, denotes the TXID and input index for the node which spent this one, in 36-byte format.
145
145
  * @throws If the node cannot be appended to the graph, either because the graph ID is for a graph the recipient does not want or because the graph has grown to be too large before being finalized.
146
146
  */
147
- async appendToGraph(tx: GASPNode, spentBy?: string | undefined): Promise<void> {
147
+ async appendToGraph (tx: GASPNode, spentBy?: string | undefined): Promise<void> {
148
148
  if (this.maxNodesInGraph !== undefined && Object.keys(this.temporaryGraphNodeRefs).length >= this.maxNodesInGraph) {
149
149
  throw new Error('The max number of nodes in transaction graph has been reached!')
150
150
  }
@@ -194,7 +194,7 @@ export class OverlayGASPStorage implements GASPStorage {
194
194
  * @param graphID The TXID and output index (in 36-byte format) for the UTXO at the tip of this graph.
195
195
  * @throws If the graph is not well-anchored, according to the rules of Bitcoin or the rules of the Overlay Topic Manager.
196
196
  */
197
- async validateGraphAnchor(graphID: string): Promise<void> {
197
+ async validateGraphAnchor (graphID: string): Promise<void> {
198
198
  const rootNode = this.temporaryGraphNodeRefs[graphID]
199
199
  if (rootNode === undefined) {
200
200
  throw new Error(`Graph node with ID ${graphID} not found`)
@@ -246,7 +246,7 @@ export class OverlayGASPStorage implements GASPStorage {
246
246
  * Deletes all data associated with a temporary graph that has failed to sync, if the graph exists.
247
247
  * @param graphID The TXID and output index (in 36-byte format) for the UTXO at the tip of this graph.
248
248
  */
249
- async discardGraph(graphID: string): Promise<void> {
249
+ async discardGraph (graphID: string): Promise<void> {
250
250
  for (const [nodeId, graphRef] of Object.entries(this.temporaryGraphNodeRefs)) {
251
251
  if (graphRef.graphID === graphID) {
252
252
  // Delete child node
@@ -260,7 +260,7 @@ export class OverlayGASPStorage implements GASPStorage {
260
260
  * Finalizes a graph, solidifying the new UTXO and its ancestors so that it will appear in the list of known UTXOs.
261
261
  * @param graphID The TXID and output index (in 36-byte format) for the UTXO at the root of this graph.
262
262
  */
263
- async finalizeGraph(graphID: string): Promise<void> {
263
+ async finalizeGraph (graphID: string): Promise<void> {
264
264
  const beefs = this.computeOrderedBEEFsForGraph(graphID)
265
265
 
266
266
  // Submit all historical BEEFs in order, finalizing the graph for the current UTXO
@@ -277,11 +277,11 @@ export class OverlayGASPStorage implements GASPStorage {
277
277
  * @param {string} graphID — The ID of the graph for which BEEFs are required
278
278
  * @returns Ordered BEEFs for the graph
279
279
  */
280
- private computeOrderedBEEFsForGraph(graphID: string): number[][] {
280
+ private computeOrderedBEEFsForGraph (graphID: string): number[][] {
281
281
  const beefs: number[][] = []
282
282
  const hydrator = (node: GraphNode): void => {
283
283
  const currentBEEF = this.getBEEFForNode(node)
284
- if (beefs.indexOf(currentBEEF) === -1) {
284
+ if (!beefs.includes(currentBEEF)) {
285
285
  beefs.unshift(currentBEEF)
286
286
  }
287
287
 
@@ -305,7 +305,7 @@ export class OverlayGASPStorage implements GASPStorage {
305
305
  * @param node Graph node for which BEEF is needed.
306
306
  * @returns BEEF array, including all proofs on inputs.
307
307
  */
308
- private getBEEFForNode(node: GraphNode): number[] {
308
+ private getBEEFForNode (node: GraphNode): number[] {
309
309
  // Given a node, hydrate its merkle proof or all inputs, returning a reference to the hydrated node's Transaction object
310
310
  const hydrator = (node: GraphNode): Transaction => {
311
311
  const tx = Transaction.fromHex(node.rawTx)