@bsv/overlay 0.4.4 → 0.4.6

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/overlay",
3
- "version": "0.4.4",
3
+ "version": "0.4.6",
4
4
  "type": "module",
5
5
  "description": "BSV Blockchain Overlay Services Engine",
6
6
  "main": "dist/cjs/mod.js",
@@ -74,7 +74,7 @@
74
74
  },
75
75
  "dependencies": {
76
76
  "@bsv/gasp": "^1.2.0",
77
- "@bsv/sdk": "^1.6.16",
77
+ "@bsv/sdk": "^1.6.20",
78
78
  "knex": "^3.1.0"
79
79
  }
80
80
  }
package/src/Engine.ts CHANGED
@@ -47,8 +47,9 @@ export class Engine {
47
47
  * @param {boolean} throwOnBroadcastFailure - Enables / disables throwing an error when a transaction broadcast failure is detected.
48
48
  * @param {OverlayBroadcastFacilitator} overlayBroadcastFacilitator - Facilitator for propagation to other Overlay Services.
49
49
  * @param {typeof console} logger - The place where log entries are written.
50
+ * @param {boolean} suppressDefaultSyncAdvertisements - Whether to suppress the default (SHIP/SLAP) sync advertisements.
50
51
  */
51
- constructor (
52
+ constructor(
52
53
  public managers: { [key: string]: TopicManager },
53
54
  public lookupServices: { [key: string]: LookupService },
54
55
  public storage: Storage,
@@ -63,7 +64,8 @@ export class Engine {
63
64
  public logPrefix = '[OVERLAY_ENGINE] ',
64
65
  public throwOnBroadcastFailure = false,
65
66
  public overlayBroadcastFacilitator: OverlayBroadcastFacilitator = new HTTPSOverlayBroadcastFacilitator(),
66
- public logger: typeof console = console
67
+ public logger: typeof console = console,
68
+ public suppressDefaultSyncAdvertisements = true
67
69
  ) {
68
70
  // To encourage synchronization of overlay services, the SHIP sync strategy is used by default for all overlay topics, except for 'tm_ship' and 'tm_slap'.
69
71
  // For these two topics, any existing trackers are combined with the provided shipTrackers and slapTrackers omitting any duplicates.
@@ -98,13 +100,13 @@ export class Engine {
98
100
  }
99
101
 
100
102
  // Helper functions for logging timings
101
- private startTime (label: string): void {
103
+ private startTime(label: string): void {
102
104
  if (this.logTime) {
103
105
  this.logger.time(`${this.logPrefix} ${label}`)
104
106
  }
105
107
  }
106
108
 
107
- private endTime (label: string): void {
109
+ private endTime(label: string): void {
108
110
  if (this.logTime) {
109
111
  this.logger.timeEnd(`${this.logPrefix} ${label}`)
110
112
  }
@@ -121,7 +123,7 @@ export class Engine {
121
123
  *
122
124
  * @returns {Promise<STEAK>} The submitted transaction execution acknowledgement
123
125
  */
124
- async submit (taggedBEEF: TaggedBEEF, onSteakReady?: (steak: STEAK) => void, mode: 'historical-tx' | 'current-tx' = 'current-tx', offChainValues?: number[]): Promise<STEAK> {
126
+ async submit(taggedBEEF: TaggedBEEF, onSteakReady?: (steak: STEAK) => void, mode: 'historical-tx' | 'current-tx' = 'current-tx', offChainValues?: number[]): Promise<STEAK> {
125
127
  for (const t of taggedBEEF.topics) {
126
128
  if (this.managers[t] === undefined || this.managers[t] === null) {
127
129
  throw new Error(`This server does not support this topic: ${t}`)
@@ -455,7 +457,7 @@ export class Engine {
455
457
  * @param LookupQuestion — The question to ask the Overlay Services Engine
456
458
  * @returns The answer to the question
457
459
  */
458
- async lookup (lookupQuestion: LookupQuestion): Promise<LookupAnswer> {
460
+ async lookup(lookupQuestion: LookupQuestion): Promise<LookupAnswer> {
459
461
  // Validate a lookup service for the provider is found
460
462
  const lookupService = this.lookupServices[lookupQuestion.service]
461
463
  if (lookupService === undefined || lookupService === null) throw new Error(`Lookup service not found for provider: ${lookupQuestion.service} `)
@@ -508,7 +510,7 @@ export class Engine {
508
510
  * @throws Will throw an error if there are issues during the advertisement synchronization process.
509
511
  * @returns {Promise<void>} A promise that resolves when the synchronization process is complete.
510
512
  */
511
- async syncAdvertisements (): Promise<void> {
513
+ async syncAdvertisements(): Promise<void> {
512
514
  if (
513
515
  this.advertiser === undefined ||
514
516
  typeof this.hostingURL !== 'string' ||
@@ -520,8 +522,14 @@ export class Engine {
520
522
  const advertiser = this.advertiser
521
523
 
522
524
  // Step 1: Retrieve Current Configuration
523
- const configuredTopics = Object.keys(this.managers)
524
- const configuredServices = Object.keys(this.lookupServices)
525
+ let configuredTopics = Object.keys(this.managers)
526
+ let configuredServices = Object.keys(this.lookupServices)
527
+
528
+ // Filter out default SHIP/SLAP topics/services if suppressDefaultSyncAdvertisements is true
529
+ if (this.suppressDefaultSyncAdvertisements === true) {
530
+ configuredTopics = configuredTopics.filter(topic => topic !== 'tm_ship' && topic !== 'tm_slap')
531
+ configuredServices = configuredServices.filter(service => service !== 'ls_ship' && service !== 'ls_slap')
532
+ }
525
533
 
526
534
  // Step 2: Fetch Existing Advertisements
527
535
  const currentSHIPAdvertisements = await advertiser.findAllAdvertisements('SHIP')
@@ -574,7 +582,7 @@ export class Engine {
574
582
  *
575
583
  * @throws Error if the overlay service engine is not configured for topical synchronization.
576
584
  */
577
- async startGASPSync (): Promise<void> {
585
+ async startGASPSync(): Promise<void> {
578
586
  if (this.syncConfiguration === undefined) {
579
587
  throw new Error('Overlay Service Engine not configured for topical synchronization!')
580
588
  }
@@ -636,7 +644,7 @@ export class Engine {
636
644
  try {
637
645
  // Read the last interaction score from storage
638
646
  const lastInteraction = await this.storage.getLastInteraction(endpoint, topic)
639
-
647
+
640
648
  const gasp = new GASP(
641
649
  new OverlayGASPStorage(topic, this),
642
650
  new OverlayGASPRemote(endpoint, topic),
@@ -646,7 +654,7 @@ export class Engine {
646
654
  true
647
655
  )
648
656
  await gasp.sync(endpoint, DEFAULT_GASP_SYNC_LIMIT)
649
-
657
+
650
658
  // Save the updated last interaction score
651
659
  if (gasp.lastInteraction > lastInteraction) {
652
660
  await this.storage.updateLastInteraction(endpoint, topic, gasp.lastInteraction)
@@ -676,7 +684,7 @@ export class Engine {
676
684
  * @param topic - The topic for which UTXOs are being requested.
677
685
  * @returns A promise that resolves to a GASPInitialResponse containing the list of UTXOs and the provided min block height.
678
686
  */
679
- async provideForeignSyncResponse (initialRequest: GASPInitialRequest, topic: string): Promise<GASPInitialResponse> {
687
+ async provideForeignSyncResponse(initialRequest: GASPInitialRequest, topic: string): Promise<GASPInitialResponse> {
680
688
  const outputs = await this.storage.findUTXOsForTopic(topic, initialRequest.since, initialRequest.limit)
681
689
 
682
690
  return {
@@ -698,7 +706,7 @@ export class Engine {
698
706
  * @returns A promise that resolves to a GASPNode containing the raw transaction and other optional data.
699
707
  * @throws An error if no output is found for the given transaction ID and output index.
700
708
  */
701
- async provideForeignGASPNode (graphID: string, txid: string, outputIndex: number): Promise<GASPNode> {
709
+ async provideForeignGASPNode(graphID: string, txid: string, outputIndex: number): Promise<GASPNode> {
702
710
  const hydrator = async (output: Output | null): Promise<GASPNode> => {
703
711
  if (output?.beef === undefined) {
704
712
  throw new Error('No matching output found!')
@@ -742,7 +750,7 @@ export class Engine {
742
750
  let foundNode: GASPNode | undefined
743
751
  for (const currentOutput of output.outputsConsumed) {
744
752
  try {
745
- const outputFound = await this.storage.findOutput(currentOutput.txid, currentOutput.outputIndex)
753
+ const outputFound = await this.storage.findOutput(currentOutput.txid, currentOutput.outputIndex, undefined, undefined, true)
746
754
  foundNode = await hydrator(outputFound)
747
755
  break
748
756
  } catch (error) {
@@ -757,7 +765,7 @@ export class Engine {
757
765
  }
758
766
 
759
767
  const [rootTxid, rootOutputIndex] = graphID.split('.')
760
- const output = await this.storage.findOutput(rootTxid, Number(rootOutputIndex))
768
+ const output = await this.storage.findOutput(rootTxid, Number(rootOutputIndex), undefined, undefined, true)
761
769
  return await hydrator(output)
762
770
  }
763
771
 
@@ -776,7 +784,7 @@ export class Engine {
776
784
  *
777
785
  * @returns {Promise<Output | undefined>} - A promise that resolves to the output history if found, or undefined if not.
778
786
  */
779
- async getUTXOHistory (
787
+ async getUTXOHistory(
780
788
  output: Output,
781
789
  historySelector?: ((beef: number[], outputIndex: number, currentDepth: number) => Promise<boolean>) | number,
782
790
  currentDepth = 0
@@ -857,7 +865,7 @@ export class Engine {
857
865
  * @param output - The UTXO to be deleted.
858
866
  * @returns {Promise<void>} - A promise that resolves when the deletion process is complete.
859
867
  */
860
- private async deleteUTXODeep (output: Output): Promise<void> {
868
+ private async deleteUTXODeep(output: Output): Promise<void> {
861
869
  try {
862
870
  // Delete the current output IFF there are no references to it
863
871
  if (output.consumedBy.length === 0) {
@@ -913,7 +921,7 @@ export class Engine {
913
921
  * @param txid BE hex string double hash of transaction proven by proof.
914
922
  * @param proof for txid
915
923
  */
916
- private updateInputProofs (tx: Transaction, txid: string, proof: MerklePath): void {
924
+ private updateInputProofs(tx: Transaction, txid: string, proof: MerklePath): void {
917
925
  if (tx.merklePath !== undefined) {
918
926
  // Update the merkle path to handle potential reorgs
919
927
  tx.merklePath = proof
@@ -938,7 +946,7 @@ export class Engine {
938
946
  * @param txid - The txid for which proof is a valid merkle path.
939
947
  * @param proof - The merklePath proving txid is a mined transaction hash
940
948
  */
941
- private async updateMerkleProof (output: Output, txid: string, proof: MerklePath): Promise<void> {
949
+ private async updateMerkleProof(output: Output, txid: string, proof: MerklePath): Promise<void> {
942
950
  if (output.beef === undefined) {
943
951
  throw new Error('Output must have associated transaction BEEF!')
944
952
  }
@@ -972,7 +980,7 @@ export class Engine {
972
980
  * @param proof - Merkle proof containing the Merkle path and other relevant data to verify the transaction.
973
981
  * @param blockHeight - The block height associated with the incoming merkle proof.
974
982
  */
975
- async handleNewMerkleProof (txid: string, proof: MerklePath, blockHeight?: number): Promise<void> {
983
+ async handleNewMerkleProof(txid: string, proof: MerklePath, blockHeight?: number): Promise<void> {
976
984
  const outputs = await this.storage.findOutputsForTransaction(txid, true)
977
985
 
978
986
  if (outputs === undefined || outputs.length === 0) {
@@ -995,7 +1003,7 @@ export class Engine {
995
1003
  * @public
996
1004
  * @returns {Promise<Record<string, { name: string; shortDescription: string; iconURL?: string; version?: string; informationURL?: string; }>>} - Supported topic managers and their metadata
997
1005
  */
998
- async listTopicManagers (): Promise<Record<string, {
1006
+ async listTopicManagers(): Promise<Record<string, {
999
1007
  name: string
1000
1008
  shortDescription: string
1001
1009
  iconURL?: string
@@ -1028,7 +1036,7 @@ export class Engine {
1028
1036
  * @public
1029
1037
  * @returns {Promise<Record<string, { name: string; shortDescription: string; iconURL?: string; version?: string; informationURL?: string; }>>} - Supported lookup services and their metadata
1030
1038
  */
1031
- async listLookupServiceProviders (): Promise<Record<string, {
1039
+ async listLookupServiceProviders(): Promise<Record<string, {
1032
1040
  name: string
1033
1041
  shortDescription: string
1034
1042
  iconURL?: string
@@ -1061,7 +1069,7 @@ export class Engine {
1061
1069
  * @public
1062
1070
  * @returns {Promise<string>} - the documentation for the topic manager
1063
1071
  */
1064
- async getDocumentationForTopicManager (manager: any): Promise<string> {
1072
+ async getDocumentationForTopicManager(manager: any): Promise<string> {
1065
1073
  const documentation = await this.managers[manager]?.getDocumentation?.()
1066
1074
  return documentation !== undefined ? documentation : 'No documentation found!'
1067
1075
  }
@@ -1071,7 +1079,7 @@ export class Engine {
1071
1079
  * @public
1072
1080
  * @returns {Promise<string>} - the documentation for the lookup service
1073
1081
  */
1074
- async getDocumentationForLookupServiceProvider (provider: any): Promise<string> {
1082
+ async getDocumentationForLookupServiceProvider(provider: any): Promise<string> {
1075
1083
  const documentation = await this.lookupServices[provider]?.getDocumentation?.()
1076
1084
  return documentation !== undefined ? documentation : 'No documentation found!'
1077
1085
  }
@@ -1086,7 +1094,7 @@ export class Engine {
1086
1094
  * @param url - The URL string to validate
1087
1095
  * @returns {boolean} - Returns `false` if the URL violates any of the conditions `true` otherwise
1088
1096
  */
1089
- private isValidUrl (url: string): boolean {
1097
+ private isValidUrl(url: string): boolean {
1090
1098
  try {
1091
1099
  const parsedUrl = new URL(url)
1092
1100
 
@@ -24,14 +24,16 @@ export interface Storage {
24
24
  * Finds an output from storage
25
25
  * @param txid — TXID of hte output to find
26
26
  * @param outputIndex — Output index for the output to find
27
- * @param topic — The topic in which the output is stored
28
- * @param spent — Whether the output must be spent to be returned
27
+ * @param topic — The topic in which the output is stored (optional)
28
+ * @param spent — Whether the output must be spent to be returned (optional)
29
+ * @param includeBEEF — Whether to include the BEEF data for the output (optional)
29
30
  */
30
31
  findOutput: (txid: string, outputIndex: number, topic?: string, spent?: boolean, includeBEEF?: boolean) => Promise<Output | null>
31
32
 
32
33
  /**
33
34
  * Finds outputs with a matching transaction ID from storage
34
35
  * @param txid — TXID of the outputs to find
36
+ * @param includeBEEF — Whether to include the BEEF data for the outputs (optional)
35
37
  */
36
38
  findOutputsForTransaction: (txid: string, includeBEEF?: boolean) => Promise<Output[]>
37
39
 
@@ -40,6 +42,7 @@ export interface Storage {
40
42
  * @param topic - The topic for which we want to find Unspent Transaction Outputs (UTXOs).
41
43
  * @param since - Optional parameter indicating the minimum score value to retrieve matching UTXOs from. This is used for score-based filtering.
42
44
  * @param limit - Optional parameter to limit the number of results returned
45
+ * @param includeBEEF — Whether to include the BEEF data for the outputs (optional)
43
46
  * @returns A promise that resolves to an array of matching UTXOs.
44
47
  */
45
48
  findUTXOsForTopic: (topic: string, since?: number, limit?: number, includeBEEF?: boolean) => Promise<Output[]>
@@ -154,7 +154,7 @@ export class KnexStorage implements Storage {
154
154
  }))
155
155
  }
156
156
 
157
- async deleteOutput (txid: string, outputIndex: number, topic: string): Promise<void> {
157
+ async deleteOutput (txid: string, outputIndex: number, _: string): Promise<void> {
158
158
  await this.knex.transaction(async trx => {
159
159
  // Delete the specific output
160
160
  await trx('outputs').where({ txid, outputIndex }).del()