@bsv/overlay 0.1.24 → 0.1.26

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/src/Engine.ts CHANGED
@@ -137,7 +137,7 @@ export class Engine {
137
137
  this.endTime(`chainTracker_${txid.substring(0, 10)}`)
138
138
 
139
139
  const steak: STEAK = {}
140
- const previousCoins: number[] = []
140
+ const dupeTopics = new Set<string>()
141
141
 
142
142
  // Parallelize the topic processing
143
143
  const topicPromises = taggedBEEF.topics.map(async topic => {
@@ -151,21 +151,24 @@ export class Engine {
151
151
  this.endTime(`dupCheck_${txid.substring(0, 10)}`)
152
152
 
153
153
  if (dupeCheck) {
154
+ dupeTopics.add(topic)
154
155
  steak[topic] = { outputsToAdmit: [], coinsToRetain: [] }
155
156
  return
156
157
  }
157
158
 
158
- // Check if any input of this transaction is a previous UTXO
159
+ // Identify previous coins admitted to this specific topic
160
+ const previousCoins: number[] = []
159
161
  const outputPromises = tx.inputs.map(async (input, i) => {
160
162
  const previousTXID = input.sourceTXID !== undefined ? input.sourceTXID : input.sourceTransaction?.id('hex')
161
163
  if (previousTXID !== undefined) {
162
- const output = this.storage.findOutput(previousTXID, input.sourceOutputIndex, topic)
164
+ // Check if the previous output was admitted to this specific topic
165
+ const output = await this.storage.findOutput(previousTXID, input.sourceOutputIndex, topic)
163
166
  if (output !== undefined && output !== null) {
164
167
  previousCoins.push(i)
165
- return await Promise.resolve(output)
168
+ return output
166
169
  }
167
170
  }
168
- return await Promise.resolve(null)
171
+ return null
169
172
  })
170
173
 
171
174
  this.startTime(`previousOutputQuery_${txid.substring(0, 10)}`)
@@ -190,7 +193,7 @@ export class Engine {
190
193
  })
191
194
 
192
195
  let admissibleOutputs: AdmittanceInstructions = { outputsToAdmit: [], coinsToRetain: [] }
193
- // Determine which outputs are admissible
196
+ // Determine which outputs are admissible for this topic
194
197
  const admissibleOutputsPromise = (async () => {
195
198
  try {
196
199
  this.startTime(`identifyAdmissibleOutputs_${txid.substring(0, 10)}`)
@@ -221,14 +224,16 @@ export class Engine {
221
224
  }
222
225
  this.endTime(`broadcast_${txid.substring(0, 10)}`)
223
226
 
224
- // Call the callback function if it is provided and there are no previousCoins to handle
225
- if (onSteakReady !== undefined && previousCoins.length === 0) {
226
- this.endTime(`submit_${txid}`)
227
+ // Call the callback function if it is provided (moved here to ensure topic processing is complete)
228
+ if (onSteakReady !== undefined) {
227
229
  onSteakReady(steak)
228
230
  }
229
231
 
230
232
  // Update storage and notify lookup services
231
233
  for (const topic of taggedBEEF.topics) {
234
+ if (dupeTopics.has(topic)) {
235
+ continue
236
+ }
232
237
  // Keep track of which outputs to admit, mark as stale, or retain
233
238
  const admissibleOutputs = steak[topic]
234
239
  const outputsToAdmit: number[] = admissibleOutputs.outputsToAdmit
@@ -243,7 +248,19 @@ export class Engine {
243
248
  inputIndex: number
244
249
  }> = []
245
250
 
246
- // For each of the previous UTXOs, if the UTXO was not included in the list of UTXOs identified for retention, then it will be marked as stale.
251
+ // Recompute previousCoins for this topic to use in the update logic
252
+ const previousCoins: number[] = []
253
+ await Promise.all(tx.inputs.map(async (input, i) => {
254
+ const previousTXID = input.sourceTXID !== undefined ? input.sourceTXID : input.sourceTransaction?.id('hex')
255
+ if (previousTXID !== undefined) {
256
+ const output = await this.storage.findOutput(previousTXID, input.sourceOutputIndex, topic)
257
+ if (output !== undefined && output !== null) {
258
+ previousCoins.push(i)
259
+ }
260
+ }
261
+ }))
262
+
263
+ // 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.
247
264
  for (const inputIndex of previousCoins) {
248
265
  const previousTXID = tx.inputs[inputIndex].sourceTXID || tx.inputs[inputIndex].sourceTransaction?.id('hex') as string
249
266
  const previousOutputIndex = tx.inputs[inputIndex].sourceOutputIndex
@@ -272,7 +289,6 @@ export class Engine {
272
289
  this.endTime(`lookForStaleOutputs_${txid.substring(0, 10)}`)
273
290
 
274
291
  // Update the STEAK to indicate which coins were removed
275
- // TODO: Handle case where deleteUTXODeep fails for some reason
276
292
  steak[topic].coinsRemoved = outputsToMarkStale.map(x => x.inputIndex)
277
293
 
278
294
  // Handle admittance and notification of incoming UTXOs
@@ -317,14 +333,13 @@ export class Engine {
317
333
  }
318
334
 
319
335
  // If we don't have an advertiser or we are dealing with historical transactions, just return the steak
320
- // This is because we are not prepared to advertise, so we are not going to engage other nodes with the SHIP process.
321
336
  if (this.advertiser === undefined || mode === 'historical-tx') {
322
337
  return steak
323
338
  }
324
339
 
325
340
  this.startTime(`transactionPropagation_${txid.substring(0, 10)}`)
326
341
  const relevantTopics = taggedBEEF.topics.filter(topic =>
327
- steak[topic] !== undefined && steak[topic].outputsToAdmit.length !== 0
342
+ steak[topic] !== undefined && !dupeTopics.has(topic) && (steak[topic].outputsToAdmit.length !== 0 || steak[topic].coinsRemoved?.length !== 0)
328
343
  )
329
344
 
330
345
  if (relevantTopics.length === 0) {