@bsv/overlay 0.4.8 → 0.5.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 (61) hide show
  1. package/dist/cjs/package.json +2 -2
  2. package/dist/cjs/src/AdmittanceInstructions.js +3 -0
  3. package/dist/cjs/src/AdmittanceInstructions.js.map +1 -0
  4. package/dist/cjs/src/Engine.js +87 -71
  5. package/dist/cjs/src/Engine.js.map +1 -1
  6. package/dist/cjs/src/LookupAnswer.js +3 -0
  7. package/dist/cjs/src/LookupAnswer.js.map +1 -0
  8. package/dist/cjs/src/LookupQuestion.js +3 -0
  9. package/dist/cjs/src/LookupQuestion.js.map +1 -0
  10. package/dist/cjs/src/SHIPAdvertisement.js +3 -0
  11. package/dist/cjs/src/SHIPAdvertisement.js.map +1 -0
  12. package/dist/cjs/src/SLAPAdvertisement.js +3 -0
  13. package/dist/cjs/src/SLAPAdvertisement.js.map +1 -0
  14. package/dist/cjs/src/STEAK.js +3 -0
  15. package/dist/cjs/src/STEAK.js.map +1 -0
  16. package/dist/cjs/src/TaggedBEEF.js +3 -0
  17. package/dist/cjs/src/TaggedBEEF.js.map +1 -0
  18. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  19. package/dist/esm/src/AdmittanceInstructions.js +2 -0
  20. package/dist/esm/src/AdmittanceInstructions.js.map +1 -0
  21. package/dist/esm/src/Engine.js +86 -70
  22. package/dist/esm/src/Engine.js.map +1 -1
  23. package/dist/esm/src/LookupAnswer.js +2 -0
  24. package/dist/esm/src/LookupAnswer.js.map +1 -0
  25. package/dist/esm/src/LookupQuestion.js +2 -0
  26. package/dist/esm/src/LookupQuestion.js.map +1 -0
  27. package/dist/esm/src/SHIPAdvertisement.js +2 -0
  28. package/dist/esm/src/SHIPAdvertisement.js.map +1 -0
  29. package/dist/esm/src/SLAPAdvertisement.js +2 -0
  30. package/dist/esm/src/SLAPAdvertisement.js.map +1 -0
  31. package/dist/esm/src/STEAK.js +2 -0
  32. package/dist/esm/src/STEAK.js.map +1 -0
  33. package/dist/esm/src/TaggedBEEF.js +2 -0
  34. package/dist/esm/src/TaggedBEEF.js.map +1 -0
  35. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  36. package/dist/types/src/AdmittanceInstructions.d.ts +14 -0
  37. package/dist/types/src/AdmittanceInstructions.d.ts.map +1 -0
  38. package/dist/types/src/Engine.d.ts +9 -2
  39. package/dist/types/src/Engine.d.ts.map +1 -1
  40. package/dist/types/src/LookupAnswer.d.ts +15 -0
  41. package/dist/types/src/LookupAnswer.d.ts.map +1 -0
  42. package/dist/types/src/LookupQuestion.d.ts +15 -0
  43. package/dist/types/src/LookupQuestion.d.ts.map +1 -0
  44. package/dist/types/src/SHIPAdvertisement.d.ts +9 -0
  45. package/dist/types/src/SHIPAdvertisement.d.ts.map +1 -0
  46. package/dist/types/src/SLAPAdvertisement.d.ts +9 -0
  47. package/dist/types/src/SLAPAdvertisement.d.ts.map +1 -0
  48. package/dist/types/src/STEAK.d.ts +10 -0
  49. package/dist/types/src/STEAK.d.ts.map +1 -0
  50. package/dist/types/src/TaggedBEEF.d.ts +11 -0
  51. package/dist/types/src/TaggedBEEF.d.ts.map +1 -0
  52. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  53. package/package.json +2 -2
  54. package/src/Engine.ts +125 -79
  55. package/src/__tests/Engine.test.ts +2 -1
  56. package/dist/cjs/src/GASP/GASP.js +0 -283
  57. package/dist/cjs/src/GASP/GASP.js.map +0 -1
  58. package/dist/esm/src/GASP/GASP.js +0 -288
  59. package/dist/esm/src/GASP/GASP.js.map +0 -1
  60. package/dist/types/src/GASP/GASP.d.ts +0 -218
  61. package/dist/types/src/GASP/GASP.d.ts.map +0 -1
package/src/Engine.ts CHANGED
@@ -26,6 +26,15 @@ import { OverlayGASPStorage } from './GASP/OverlayGASPStorage.js'
26
26
 
27
27
  const DEFAULT_GASP_SYNC_LIMIT = 10000
28
28
 
29
+ type UTXOHistoryHydrationContext = {
30
+ outputCache: Map<string, Promise<Output | null>>
31
+ }
32
+
33
+ type HydratedUTXOHistoryNode = {
34
+ output: Output
35
+ transaction: Transaction
36
+ }
37
+
29
38
  /**
30
39
  * An engine for running BSV Overlay Services (topic managers and lookup services).
31
40
  */
@@ -463,34 +472,119 @@ export class Engine {
463
472
  if (lookupService === undefined || lookupService === null) throw new Error(`Lookup service not found for provider: ${lookupQuestion.service} `)
464
473
 
465
474
  const lookupResult = await lookupService.lookup(lookupQuestion)
466
- const hydratedOutputs: Array<{ beef: number[], outputIndex: number, context?: number[] }> = []
467
- for (const { txid, outputIndex, history, context } of lookupResult) {
468
- // Make sure this is an unspent output (UTXO)
469
- const UTXO = await this.storage.findOutput(
470
- txid,
471
- outputIndex,
472
- undefined,
473
- undefined,
474
- true
475
- )
476
- if (UTXO === undefined || UTXO === null) continue
477
-
478
- // Get the history for this utxo and construct a BRC-8 Envelope
479
- const output = await this.getUTXOHistory(UTXO, history, 0)
480
- if (output?.beef !== undefined) {
481
- hydratedOutputs.push({
475
+ const hydrationContext = this.createUTXOHistoryHydrationContext()
476
+ const hydratedOutputs = (await Promise.all(
477
+ lookupResult.map(async ({ txid, outputIndex, history, context }) => {
478
+ const UTXO = await this.loadOutputWithBEEF(txid, outputIndex, hydrationContext)
479
+ if (UTXO === null) {
480
+ return null
481
+ }
482
+
483
+ // Get the history for this utxo and construct a BEEF
484
+ const output = await this.getUTXOHistory(UTXO, history, 0, hydrationContext)
485
+ if (output?.beef === undefined) {
486
+ return null
487
+ }
488
+
489
+ return {
482
490
  beef: output.beef,
483
491
  outputIndex: output.outputIndex,
484
492
  context
485
- })
486
- }
487
- }
493
+ }
494
+ })
495
+ ))
496
+ .filter((output): output is { beef: number[], outputIndex: number, context: number[] | undefined } => output !== null)
497
+ .map(({ beef, outputIndex, context }) => (
498
+ context === undefined
499
+ ? { beef, outputIndex }
500
+ : { beef, outputIndex, context }
501
+ ))
488
502
  return {
489
503
  type: 'output-list',
490
504
  outputs: hydratedOutputs
491
505
  }
492
506
  }
493
507
 
508
+ private createUTXOHistoryHydrationContext(): UTXOHistoryHydrationContext {
509
+ return {
510
+ outputCache: new Map<string, Promise<Output | null>>()
511
+ }
512
+ }
513
+
514
+ private async loadOutputWithBEEF(
515
+ txid: string,
516
+ outputIndex: number,
517
+ context: UTXOHistoryHydrationContext
518
+ ): Promise<Output | null> {
519
+ const cacheKey = `${txid}:${outputIndex}`
520
+ let cached = context.outputCache.get(cacheKey)
521
+ if (cached === undefined) {
522
+ cached = this.storage.findOutput(txid, outputIndex, undefined, undefined, true)
523
+ context.outputCache.set(cacheKey, cached)
524
+ }
525
+ const output = await cached
526
+ return output ?? null
527
+ }
528
+
529
+ private async hydrateUTXOHistoryNode(
530
+ output: Output,
531
+ historySelector: ((beef: number[], outputIndex: number, currentDepth: number) => Promise<boolean>) | number,
532
+ currentDepth: number,
533
+ context: UTXOHistoryHydrationContext
534
+ ): Promise<HydratedUTXOHistoryNode | undefined> {
535
+ if (output.beef === undefined) {
536
+ throw new Error('Output must have associated transaction BEEF!')
537
+ }
538
+
539
+ let shouldTraverseHistory: boolean
540
+ if (typeof historySelector === 'number') {
541
+ shouldTraverseHistory = currentDepth <= historySelector
542
+ } else {
543
+ shouldTraverseHistory = await historySelector(output.beef, output.outputIndex, currentDepth)
544
+ }
545
+
546
+ if (shouldTraverseHistory === false) {
547
+ return undefined
548
+ }
549
+
550
+ const childNodes = (await Promise.all(
551
+ output.outputsConsumed.map(async (outputIdentifier) => {
552
+ const childOutput = await this.loadOutputWithBEEF(outputIdentifier.txid, outputIdentifier.outputIndex, context)
553
+ if (childOutput === null) {
554
+ return undefined
555
+ }
556
+
557
+ return await this.hydrateUTXOHistoryNode(childOutput, historySelector, currentDepth + 1, context)
558
+ })
559
+ )).filter((node): node is HydratedUTXOHistoryNode => node !== undefined)
560
+
561
+ const tx = Transaction.fromBEEF(output.beef)
562
+
563
+ for (const child of childNodes) {
564
+ const inputIndex = tx.inputs.findIndex((candidateInput) => {
565
+ const sourceTXID = candidateInput.sourceTXID !== undefined && candidateInput.sourceTXID !== ''
566
+ ? candidateInput.sourceTXID
567
+ : candidateInput.sourceTransaction?.id('hex')
568
+
569
+ return sourceTXID === child.output.txid && candidateInput.sourceOutputIndex === child.output.outputIndex
570
+ })
571
+
572
+ if (inputIndex === -1 || inputIndex == null) {
573
+ continue
574
+ }
575
+
576
+ const targetInput = tx.inputs[inputIndex]
577
+ if (!targetInput) {
578
+ this.logger.error(`Input at index ${inputIndex} is undefined, but findIndex found it. Possible sparse array from BEEF parsing.`)
579
+ continue
580
+ }
581
+
582
+ targetInput.sourceTransaction = child.transaction
583
+ }
584
+
585
+ return { output, transaction: tx }
586
+ }
587
+
494
588
  /**
495
589
  * Ensures alignment between the current SHIP/SLAP advertisements and the
496
590
  * configured Topic Managers and Lookup Services in the engine.
@@ -783,79 +877,31 @@ export class Engine {
783
877
  * @param {number} [currentDepth=0] - The current depth of the traversal relative to the top-level UTXO.
784
878
  *
785
879
  * @returns {Promise<Output | undefined>} - A promise that resolves to the output history if found, or undefined if not.
786
- */
880
+ */
787
881
  async getUTXOHistory(
788
882
  output: Output,
789
883
  historySelector?: ((beef: number[], outputIndex: number, currentDepth: number) => Promise<boolean>) | number,
790
- currentDepth = 0
884
+ currentDepth = 0,
885
+ context: UTXOHistoryHydrationContext = this.createUTXOHistoryHydrationContext()
791
886
  ): Promise<Output | undefined> {
792
887
  // If we have an output but no history selector, just return the output.
793
888
  if (typeof historySelector === 'undefined') {
794
889
  return output
795
890
  }
796
891
 
797
- if (output.beef === undefined) {
798
- throw new Error('Output must have associated transaction BEEF!')
799
- }
800
-
801
- // Determine if history traversal should continue for the current node
802
- let shouldTraverseHistory
803
- if (typeof historySelector !== 'number') {
804
- shouldTraverseHistory = await historySelector(output.beef, output.outputIndex, currentDepth)
805
- } else {
806
- shouldTraverseHistory = currentDepth <= historySelector
807
- }
808
-
809
- if (shouldTraverseHistory === false) {
810
- return undefined
811
- } else if (output !== null && output !== undefined && output.outputsConsumed.length === 0) {
812
- return output
813
- }
814
-
815
892
  try {
816
- // Query the storage engine for UTXOs consumed by this UTXO
817
- // Only retrieve unique values in case outputs are doubly referenced
818
- const outputsConsumed: Array<{ txid: string, outputIndex: number }> = output.outputsConsumed
819
-
820
- // Find the child outputs for each utxo consumed by the current output
821
- const childHistories = (await Promise.all(
822
- outputsConsumed.map(async (outputIdentifier) => {
823
- const output = await this.storage.findOutput(outputIdentifier.txid, outputIdentifier.outputIndex, undefined, undefined, true)
824
-
825
- // Make sure an output was found
826
- if (output === undefined || output === null) {
827
- return undefined
828
- }
829
-
830
- // Find previousUTXO history
831
- return await this.getUTXOHistory(output, historySelector, currentDepth + 1)
832
- })
833
- )).filter(x => x !== undefined)
834
-
835
- const tx = Transaction.fromBEEF(output.beef)
836
- for (const input of childHistories) {
837
- if (input === undefined || input === null) continue
838
- const inputIndex = tx.inputs.findIndex((candidateInput) => {
839
- const sourceTXID = candidateInput.sourceTXID !== undefined && candidateInput.sourceTXID !== ''
840
- ? candidateInput.sourceTXID
841
- : candidateInput.sourceTransaction?.id('hex')
893
+ if (output.beef === undefined) {
894
+ throw new Error('Output must have associated transaction BEEF!')
895
+ }
842
896
 
843
- return sourceTXID === input.txid && candidateInput.sourceOutputIndex === input.outputIndex
844
- })
845
- if (inputIndex === -1 || inputIndex == null) continue
846
- if (!tx.inputs[inputIndex]) {
847
- this.logger.error(`Input at index ${inputIndex} is undefined, but findIndex found it. Possible sparse array from BEEF parsing.`)
848
- continue
849
- }
850
- if (input.beef === undefined) {
851
- throw new Error('Input must have associated transaction BEEF!')
852
- }
853
- tx.inputs[inputIndex].sourceTransaction = Transaction.fromBEEF(input.beef)
897
+ const hydratedNode = await this.hydrateUTXOHistoryNode(output, historySelector, currentDepth, context)
898
+ if (hydratedNode === undefined) {
899
+ return undefined
854
900
  }
855
- const beef = tx.toBEEF()
901
+
856
902
  return {
857
- ...output,
858
- beef
903
+ ...hydratedNode.output,
904
+ beef: hydratedNode.transaction.toBEEF()
859
905
  }
860
906
  } catch (e) {
861
907
  // Handle any errors that occurred
@@ -899,7 +899,8 @@ describe('BSV Overlay Services Engine', () => {
899
899
  expect(engine.getUTXOHistory).toHaveBeenCalledWith(
900
900
  mockOutput,
901
901
  undefined,
902
- 0
902
+ 0,
903
+ expect.anything()
903
904
  )
904
905
  })
905
906
  })
@@ -1,283 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GASP = exports.GASPVersionMismatchError = void 0;
4
- const sdk_1 = require("@bsv/sdk");
5
- class GASPVersionMismatchError extends Error {
6
- constructor(message, currentVersion, foreignVersion) {
7
- super(message);
8
- this.code = 'ERR_GASP_VERSION_MISMATCH';
9
- this.currentVersion = currentVersion;
10
- this.foreignVersion = foreignVersion;
11
- }
12
- }
13
- exports.GASPVersionMismatchError = GASPVersionMismatchError;
14
- /**
15
- * Main class implementing the Graph Aware Sync Protocol.
16
- */
17
- class GASP {
18
- /**
19
- *
20
- * @param storage The GASP Storage interface to use
21
- * @param remote The GASP Remote interface to use
22
- * @param lastInteraction The timestamp when we last interacted with this remote party
23
- * @param logPrefix Optional prefix for log messages
24
- * @param log Whether to log messages
25
- * @param unidirectional Whether to disable the "reply" side and do pull-only
26
- */
27
- constructor(storage, remote, lastInteraction = 0, logPrefix = '[GASP] ', log = false, unidirectional = false) {
28
- this.storage = storage;
29
- this.remote = remote;
30
- this.lastInteraction = lastInteraction;
31
- this.version = 1;
32
- this.logPrefix = logPrefix;
33
- this.log = log;
34
- this.unidirectional = unidirectional;
35
- this.validateTimestamp(this.lastInteraction);
36
- this.logData(`GASP initialized with version: ${this.version}, lastInteraction: ${this.lastInteraction}, unidirectional: ${this.unidirectional}`);
37
- }
38
- logData(...data) {
39
- if (this.log) {
40
- console.log(this.logPrefix, ...data);
41
- }
42
- }
43
- validateTimestamp(timestamp) {
44
- if (typeof timestamp !== 'number' || isNaN(timestamp) || timestamp < 0 || !Number.isInteger(timestamp)) {
45
- throw new Error('Invalid timestamp format');
46
- }
47
- }
48
- /**
49
- * Computes a 36-byte structure from a transaction ID and output index.
50
- * @param txid The transaction ID.
51
- * @param index The output index.
52
- * @returns A string representing the 36-byte structure.
53
- */
54
- compute36ByteStructure(txid, index) {
55
- const result = `${txid}.${index.toString()}`;
56
- this.logData(`Computed 36-byte structure: ${result} from txid: ${txid}, index: ${index}`);
57
- return result;
58
- }
59
- /**
60
- * Deconstructs a 36-byte structure into a transaction ID and output index.
61
- * @param outpoint The 36-byte structure.
62
- * @returns An object containing the transaction ID and output index.
63
- */
64
- deconstruct36ByteStructure(outpoint) {
65
- const [txid, index] = outpoint.split('.');
66
- const result = {
67
- txid,
68
- outputIndex: parseInt(index, 10)
69
- };
70
- this.logData(`Deconstructed 36-byte structure: ${outpoint} into txid: ${txid}, outputIndex: ${result.outputIndex}`);
71
- return result;
72
- }
73
- /**
74
- * Computes the transaction ID for a given transaction.
75
- * @param tx The transaction string.
76
- * @returns The computed transaction ID.
77
- */
78
- computeTXID(tx) {
79
- const txid = sdk_1.Transaction.fromHex(tx).id('hex');
80
- this.logData(`Computed TXID: ${txid} from transaction: ${tx}`);
81
- return txid;
82
- }
83
- /**
84
- * Synchronizes the transaction data between the local and remote participants.
85
- */
86
- async sync() {
87
- this.logData(`Starting sync process. Last interaction timestamp: ${this.lastInteraction}`);
88
- const initialRequest = await this.buildInitialRequest(this.lastInteraction);
89
- const initialResponse = await this.remote.getInitialResponse(initialRequest);
90
- // 1. Pull the remote UTXOs that we don't already have
91
- if (initialResponse.UTXOList.length > 0) {
92
- const foreignUTXOs = await this.storage.findKnownUTXOs(0);
93
- // -- REPLACE Promise.all WITH A FOR LOOP --
94
- const missingUTXOs = initialResponse.UTXOList.filter(x => !foreignUTXOs.some(y => x.txid === y.txid && x.outputIndex === y.outputIndex));
95
- for (const UTXO of missingUTXOs) {
96
- try {
97
- this.logData(`Requesting node for UTXO: ${JSON.stringify(UTXO)}`);
98
- const resolvedNode = await this.remote.requestNode(this.compute36ByteStructure(UTXO.txid, UTXO.outputIndex), UTXO.txid, UTXO.outputIndex, true);
99
- this.logData(`Received unspent graph node from remote: ${JSON.stringify(resolvedNode)}`);
100
- await this.processIncomingNode(resolvedNode);
101
- await this.completeGraph(resolvedNode.graphID);
102
- }
103
- catch (e) {
104
- this.logData(`Error with incoming UTXO ${UTXO.txid}.${UTXO.outputIndex}: ${e.message}`);
105
- }
106
- }
107
- }
108
- // 2. Only do the “reply” half if unidirectional is disabled
109
- if (!this.unidirectional) {
110
- const initialReply = await this.getInitialReply(initialResponse);
111
- this.logData(`Received initial reply: ${JSON.stringify(initialReply)}`);
112
- if (initialReply.UTXOList.length > 0) {
113
- // -- REPLACE Promise.all WITH A FOR LOOP --
114
- for (const UTXO of initialReply.UTXOList) {
115
- try {
116
- this.logData(`Hydrating GASP node for UTXO: ${JSON.stringify(UTXO)}`);
117
- const outgoingNode = await this.storage.hydrateGASPNode(this.compute36ByteStructure(UTXO.txid, UTXO.outputIndex), UTXO.txid, UTXO.outputIndex, true);
118
- this.logData(`Sending unspent graph node for remote: ${JSON.stringify(outgoingNode)}`);
119
- await this.processOutgoingNode(outgoingNode);
120
- }
121
- catch (e) {
122
- this.logData(`Error with outgoing UTXO ${UTXO.txid}.${UTXO.outputIndex}: ${e.message}`);
123
- }
124
- }
125
- }
126
- }
127
- this.logData('Sync completed!');
128
- }
129
- /**
130
- * Builds the initial request for the sync process.
131
- * @returns A promise for the initial request object.
132
- */
133
- async buildInitialRequest(since) {
134
- const request = {
135
- version: this.version,
136
- since
137
- };
138
- this.logData(`Built initial request: ${JSON.stringify(request)}`);
139
- return request;
140
- }
141
- /**
142
- * Builds the initial response based on the received request.
143
- * @param request The initial request object.
144
- * @returns A promise for an initial response
145
- */
146
- async getInitialResponse(request) {
147
- this.logData(`Received initial request: ${JSON.stringify(request)}`);
148
- if (request.version !== this.version) {
149
- const error = new GASPVersionMismatchError(`GASP version mismatch. Current version: ${this.version}, foreign version: ${request.version}`, this.version, request.version);
150
- console.error(`GASP version mismatch error: ${error.message}`);
151
- throw error;
152
- }
153
- this.validateTimestamp(request.since);
154
- const response = {
155
- since: this.lastInteraction,
156
- UTXOList: await this.storage.findKnownUTXOs(request.since)
157
- };
158
- this.logData(`Built initial response: ${JSON.stringify(response)}`);
159
- return response;
160
- }
161
- /**
162
- * Builds the initial reply based on the received response.
163
- * @param response The initial response object.
164
- * @returns A promise for an initial reply
165
- */
166
- async getInitialReply(response) {
167
- this.logData(`Received initial response: ${JSON.stringify(response)}`);
168
- const knownUTXOs = await this.storage.findKnownUTXOs(response.since);
169
- const filteredUTXOs = knownUTXOs.filter(x => !response.UTXOList.some(y => y.txid === x.txid && y.outputIndex === x.outputIndex));
170
- const reply = {
171
- UTXOList: filteredUTXOs
172
- };
173
- this.logData(`Built initial reply: ${JSON.stringify(reply)}`);
174
- return reply;
175
- }
176
- /**
177
- * Provides a requested node to a foreign instance who requested it.
178
- */
179
- async requestNode(graphID, txid, outputIndex, metadata) {
180
- this.logData(`Remote is requesting node with graphID: ${graphID}, txid: ${txid}, outputIndex: ${outputIndex}, metadata: ${metadata}`);
181
- const node = await this.storage.hydrateGASPNode(graphID, txid, outputIndex, metadata);
182
- this.logData(`Returning node: ${JSON.stringify(node)}`);
183
- return node;
184
- }
185
- /**
186
- * Provides a set of inputs we care about after processing a new incoming node.
187
- * Also finalizes or discards a graph if no additional data is requested from the foreign instance.
188
- */
189
- async submitNode(node) {
190
- this.logData(`Remote party is submitting node: ${JSON.stringify(node)}`);
191
- await this.storage.appendToGraph(node);
192
- const requestedInputs = await this.storage.findNeededInputs(node);
193
- this.logData(`Requested inputs: ${JSON.stringify(requestedInputs)}`);
194
- if (!requestedInputs) {
195
- await this.completeGraph(node.graphID);
196
- }
197
- return requestedInputs;
198
- }
199
- /**
200
- * Handles the completion of a newly-synced graph
201
- * @param {string} graphID The ID of the newly-synced graph
202
- */
203
- async completeGraph(graphID) {
204
- this.logData(`Completing newly-synced graph: ${graphID}`);
205
- try {
206
- await this.storage.validateGraphAnchor(graphID);
207
- this.logData(`Graph validated for node: ${graphID}`);
208
- await this.storage.finalizeGraph(graphID);
209
- this.logData(`Graph finalized for node: ${graphID}`);
210
- }
211
- catch (e) {
212
- this.logData(`Error validating graph: ${e.message}. Discarding graph for node: ${graphID}`);
213
- await this.storage.discardGraph(graphID);
214
- }
215
- }
216
- /**
217
- * Processes an incoming node from the remote participant.
218
- * @param node The incoming GASP node.
219
- * @param spentBy The 36-byte structure of the node that spent this one, if applicable.
220
- */
221
- async processIncomingNode(node, spentBy, seenNodes = new Set()) {
222
- const nodeId = `${this.computeTXID(node.rawTx)}.${node.outputIndex}`;
223
- this.logData(`Processing incoming node: ${JSON.stringify(node)}, spentBy: ${spentBy}`);
224
- if (seenNodes.has(nodeId)) {
225
- this.logData(`Node ${nodeId} already processed, skipping.`);
226
- return; // Prevent infinite recursion
227
- }
228
- seenNodes.add(nodeId);
229
- await this.storage.appendToGraph(node, spentBy);
230
- const neededInputs = await this.storage.findNeededInputs(node);
231
- this.logData(`Needed inputs for node ${nodeId}: ${JSON.stringify(neededInputs)}`);
232
- if (neededInputs) {
233
- // -- REPLACE Promise.all WITH A FOR LOOP --
234
- for (const [outpoint, { metadata }] of Object.entries(neededInputs.requestedInputs)) {
235
- const { txid, outputIndex } = this.deconstruct36ByteStructure(outpoint);
236
- this.logData(`Requesting new node for txid: ${txid}, outputIndex: ${outputIndex}, metadata: ${metadata}`);
237
- const newNode = await this.remote.requestNode(node.graphID, txid, outputIndex, metadata);
238
- this.logData(`Received new node: ${JSON.stringify(newNode)}`);
239
- await this.processIncomingNode(newNode, this.compute36ByteStructure(this.computeTXID(node.rawTx), node.outputIndex), seenNodes);
240
- }
241
- }
242
- }
243
- /**
244
- * Processes an outgoing node to the remote participant.
245
- * @param node The outgoing GASP node.
246
- */
247
- async processOutgoingNode(node, seenNodes = new Set()) {
248
- if (this.unidirectional) {
249
- this.logData(`Skipping outgoing node processing in unidirectional mode.`);
250
- return;
251
- }
252
- const nodeId = `${this.computeTXID(node.rawTx)}.${node.outputIndex}`;
253
- this.logData(`Processing outgoing node: ${JSON.stringify(node)}`);
254
- if (seenNodes.has(nodeId)) {
255
- this.logData(`Node ${nodeId} already processed, skipping.`);
256
- return; // Prevent infinite recursion
257
- }
258
- seenNodes.add(nodeId);
259
- // Attempt to submit the node to the remote
260
- const response = await this.remote.submitNode(node);
261
- this.logData(`Received response for submitted node: ${JSON.stringify(response)}`);
262
- if (response) {
263
- // Process each requested input sequentially
264
- for (const [outpoint, { metadata }] of Object.entries(response.requestedInputs)) {
265
- const { txid, outputIndex } = this.deconstruct36ByteStructure(outpoint);
266
- try {
267
- this.logData(`Hydrating node for txid: ${txid}, outputIndex: ${outputIndex}, metadata: ${metadata}`);
268
- const hydratedNode = await this.storage.hydrateGASPNode(node.graphID, txid, outputIndex, metadata);
269
- this.logData(`Hydrated node: ${JSON.stringify(hydratedNode)}`);
270
- await this.processOutgoingNode(hydratedNode, seenNodes);
271
- }
272
- catch (e) {
273
- this.logData(`Error hydrating node: ${e.message}`);
274
- // If we can't send the outgoing node, we just stop.
275
- // The remote won't validate the anchor, and their temporary graph will be discarded.
276
- return;
277
- }
278
- }
279
- }
280
- }
281
- }
282
- exports.GASP = GASP;
283
- //# sourceMappingURL=GASP.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"GASP.js","sourceRoot":"","sources":["../../../../src/GASP/GASP.ts"],"names":[],"mappings":"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"}