@bsv/overlay 0.5.1 → 0.5.2

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 (54) hide show
  1. package/dist/cjs/package.json +2 -2
  2. package/dist/cjs/src/Engine.js +249 -220
  3. package/dist/cjs/src/Engine.js.map +1 -1
  4. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  5. package/dist/esm/src/Engine.js +245 -216
  6. package/dist/esm/src/Engine.js.map +1 -1
  7. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  8. package/dist/types/src/Engine.d.ts.map +1 -1
  9. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  10. package/docs/API.md +2 -2
  11. package/package.json +2 -2
  12. package/src/Engine.ts +243 -218
  13. package/dist/cjs/src/AdmittanceInstructions.js +0 -3
  14. package/dist/cjs/src/AdmittanceInstructions.js.map +0 -1
  15. package/dist/cjs/src/LookupAnswer.js +0 -3
  16. package/dist/cjs/src/LookupAnswer.js.map +0 -1
  17. package/dist/cjs/src/LookupQuestion.js +0 -3
  18. package/dist/cjs/src/LookupQuestion.js.map +0 -1
  19. package/dist/cjs/src/SHIPAdvertisement.js +0 -3
  20. package/dist/cjs/src/SHIPAdvertisement.js.map +0 -1
  21. package/dist/cjs/src/SLAPAdvertisement.js +0 -3
  22. package/dist/cjs/src/SLAPAdvertisement.js.map +0 -1
  23. package/dist/cjs/src/STEAK.js +0 -3
  24. package/dist/cjs/src/STEAK.js.map +0 -1
  25. package/dist/cjs/src/TaggedBEEF.js +0 -3
  26. package/dist/cjs/src/TaggedBEEF.js.map +0 -1
  27. package/dist/esm/src/AdmittanceInstructions.js +0 -2
  28. package/dist/esm/src/AdmittanceInstructions.js.map +0 -1
  29. package/dist/esm/src/LookupAnswer.js +0 -2
  30. package/dist/esm/src/LookupAnswer.js.map +0 -1
  31. package/dist/esm/src/LookupQuestion.js +0 -2
  32. package/dist/esm/src/LookupQuestion.js.map +0 -1
  33. package/dist/esm/src/SHIPAdvertisement.js +0 -2
  34. package/dist/esm/src/SHIPAdvertisement.js.map +0 -1
  35. package/dist/esm/src/SLAPAdvertisement.js +0 -2
  36. package/dist/esm/src/SLAPAdvertisement.js.map +0 -1
  37. package/dist/esm/src/STEAK.js +0 -2
  38. package/dist/esm/src/STEAK.js.map +0 -1
  39. package/dist/esm/src/TaggedBEEF.js +0 -2
  40. package/dist/esm/src/TaggedBEEF.js.map +0 -1
  41. package/dist/types/src/AdmittanceInstructions.d.ts +0 -14
  42. package/dist/types/src/AdmittanceInstructions.d.ts.map +0 -1
  43. package/dist/types/src/LookupAnswer.d.ts +0 -15
  44. package/dist/types/src/LookupAnswer.d.ts.map +0 -1
  45. package/dist/types/src/LookupQuestion.d.ts +0 -15
  46. package/dist/types/src/LookupQuestion.d.ts.map +0 -1
  47. package/dist/types/src/SHIPAdvertisement.d.ts +0 -9
  48. package/dist/types/src/SHIPAdvertisement.d.ts.map +0 -1
  49. package/dist/types/src/SLAPAdvertisement.d.ts +0 -9
  50. package/dist/types/src/SLAPAdvertisement.d.ts.map +0 -1
  51. package/dist/types/src/STEAK.d.ts +0 -10
  52. package/dist/types/src/STEAK.d.ts.map +0 -1
  53. package/dist/types/src/TaggedBEEF.d.ts +0 -11
  54. package/dist/types/src/TaggedBEEF.d.ts.map +0 -1
@@ -131,132 +131,149 @@ export class Engine {
131
131
  this.endTime(`chainTracker_${txid.substring(0, 10)}`);
132
132
  const steak = {};
133
133
  const dupeTopics = new Set();
134
+ const failedTopics = new Set();
134
135
  // Parallelize the topic processing
135
136
  const topicPromises = taggedBEEF.topics.map(async (topic) => {
136
- if (this.managers[topic] === undefined || this.managers[topic] === null) {
137
- throw new Error(`This server does not support this topic: ${topic}`);
138
- }
139
- // Check for duplicate transactions
140
- this.startTime(`dupCheck_${txid.substring(0, 10)}`);
141
- const dupeCheck = await this.storage.doesAppliedTransactionExist({ txid, topic });
142
- this.endTime(`dupCheck_${txid.substring(0, 10)}`);
143
- if (dupeCheck) {
144
- dupeTopics.add(topic);
145
- steak[topic] = { outputsToAdmit: [], coinsToRetain: [] };
146
- return;
147
- }
148
- // Identify previous coins admitted to this specific topic
149
- const previousCoins = [];
150
- const outputPromises = tx.inputs.map(async (input, i) => {
151
- const previousTXID = input.sourceTXID !== undefined ? input.sourceTXID : input.sourceTransaction?.id('hex');
152
- if (previousTXID !== undefined) {
153
- // Check if the previous output was admitted to this specific topic
154
- const output = await this.storage.findOutput(previousTXID, input.sourceOutputIndex, topic);
155
- if (output !== undefined && output !== null) {
156
- previousCoins.push(i);
157
- return output;
158
- }
137
+ try {
138
+ if (this.managers[topic] === undefined || this.managers[topic] === null) {
139
+ throw new Error(`This server does not support this topic: ${topic}`);
159
140
  }
160
- return null;
161
- });
162
- this.startTime(`previousOutputQuery_${txid.substring(0, 10)}`);
163
- const outputs = await Promise.all(outputPromises);
164
- this.endTime(`previousOutputQuery_${txid.substring(0, 10)}`);
165
- const markSpentPromises = outputs.map(async (output) => {
166
- if (output !== undefined && output !== null) {
167
- try {
168
- await this.storage.markUTXOAsSpent(output.txid, output.outputIndex, topic);
169
- await Promise.all(Object.values(this.lookupServices).map(async (l) => {
170
- try {
171
- if (typeof l.outputSpent === 'function') {
172
- if (l.spendNotificationMode === 'txid') {
173
- await l.outputSpent({
174
- mode: 'txid',
175
- spendingTxid: txid,
176
- txid: output.txid,
177
- outputIndex: output.outputIndex,
178
- topic
179
- });
180
- }
181
- else if (l.spendNotificationMode === 'script') {
182
- const inputIndex = tx.inputs.findIndex(i => {
183
- let realSource = i.sourceTXID;
184
- if (!realSource) {
185
- realSource = i.sourceTransaction?.id('hex');
141
+ // Check for duplicate transactions
142
+ this.startTime(`dupCheck_${txid.substring(0, 10)}`);
143
+ const dupeCheck = await this.storage.doesAppliedTransactionExist({ txid, topic });
144
+ this.endTime(`dupCheck_${txid.substring(0, 10)}`);
145
+ if (dupeCheck) {
146
+ dupeTopics.add(topic);
147
+ steak[topic] = { outputsToAdmit: [], coinsToRetain: [] };
148
+ return;
149
+ }
150
+ // Identify previous coins admitted to this specific topic
151
+ const previousCoins = [];
152
+ const outputPromises = tx.inputs.map(async (input, i) => {
153
+ const previousTXID = input.sourceTXID !== undefined ? input.sourceTXID : input.sourceTransaction?.id('hex');
154
+ if (previousTXID !== undefined) {
155
+ // Check if the previous output was admitted to this specific topic
156
+ const output = await this.storage.findOutput(previousTXID, input.sourceOutputIndex, topic);
157
+ if (output !== undefined && output !== null) {
158
+ previousCoins.push(i);
159
+ return output;
160
+ }
161
+ }
162
+ return null;
163
+ });
164
+ this.startTime(`previousOutputQuery_${txid.substring(0, 10)}`);
165
+ const outputs = await Promise.all(outputPromises);
166
+ this.endTime(`previousOutputQuery_${txid.substring(0, 10)}`);
167
+ const markSpentPromises = outputs.map(async (output) => {
168
+ if (output !== undefined && output !== null) {
169
+ try {
170
+ await this.storage.markUTXOAsSpent(output.txid, output.outputIndex, topic);
171
+ await Promise.all(Object.values(this.lookupServices).map(async (l) => {
172
+ try {
173
+ if (typeof l.outputSpent === 'function') {
174
+ if (l.spendNotificationMode === 'txid') {
175
+ await l.outputSpent({
176
+ mode: 'txid',
177
+ spendingTxid: txid,
178
+ txid: output.txid,
179
+ outputIndex: output.outputIndex,
180
+ topic
181
+ });
182
+ }
183
+ else if (l.spendNotificationMode === 'script') {
184
+ const inputIndex = tx.inputs.findIndex(i => {
185
+ let realSource = i.sourceTXID;
186
+ if (!realSource) {
187
+ realSource = i.sourceTransaction?.id('hex');
188
+ }
189
+ return realSource === output.txid && i.sourceOutputIndex === output.outputIndex;
190
+ });
191
+ if (inputIndex === -1) {
192
+ throw new Error('Could not find input index');
186
193
  }
187
- return realSource === output.txid && i.sourceOutputIndex === output.outputIndex;
188
- });
189
- if (!inputIndex) {
190
- throw new Error('Could not find input index');
194
+ await l.outputSpent({
195
+ mode: 'script',
196
+ spendingTxid: txid,
197
+ inputIndex,
198
+ sequenceNumber: tx.inputs[inputIndex].sequence ?? 0xffffffff,
199
+ unlockingScript: tx.inputs[inputIndex].unlockingScript,
200
+ txid: output.txid,
201
+ outputIndex: output.outputIndex,
202
+ topic,
203
+ offChainValues
204
+ });
205
+ }
206
+ else if (l.spendNotificationMode === 'whole-tx') {
207
+ await l.outputSpent({
208
+ mode: 'whole-tx',
209
+ spendingAtomicBEEF: tx.toAtomicBEEF(),
210
+ txid: output.txid,
211
+ outputIndex: output.outputIndex,
212
+ topic,
213
+ offChainValues
214
+ });
215
+ }
216
+ else { // none
217
+ await l.outputSpent({
218
+ mode: 'none',
219
+ txid: output.txid,
220
+ outputIndex: output.outputIndex,
221
+ topic
222
+ });
191
223
  }
192
- await l.outputSpent({
193
- mode: 'script',
194
- spendingTxid: txid,
195
- inputIndex,
196
- sequenceNumber: tx.inputs[inputIndex].sequence ?? 0xffffffff,
197
- unlockingScript: tx.inputs[inputIndex].unlockingScript,
198
- txid: output.txid,
199
- outputIndex: output.outputIndex,
200
- topic,
201
- offChainValues
202
- });
203
- }
204
- else if (l.spendNotificationMode === 'whole-tx') {
205
- await l.outputSpent({
206
- mode: 'whole-tx',
207
- spendingAtomicBEEF: tx.toAtomicBEEF(),
208
- txid: output.txid,
209
- outputIndex: output.outputIndex,
210
- topic,
211
- offChainValues
212
- });
213
- }
214
- else { // none
215
- await l.outputSpent({
216
- mode: 'none',
217
- txid: output.txid,
218
- outputIndex: output.outputIndex,
219
- topic
220
- });
221
224
  }
222
225
  }
223
- }
224
- catch (error) {
225
- this.logger.error('Error in lookup service for outputSpent:', error);
226
- }
227
- }));
226
+ catch (error) {
227
+ this.logger.error('Error in lookup service for outputSpent:', error);
228
+ }
229
+ }));
230
+ }
231
+ catch (error) {
232
+ this.logger.error('Error marking UTXO as spent:', error);
233
+ }
228
234
  }
229
- catch (error) {
230
- this.logger.error('Error marking UTXO as spent:', error);
235
+ });
236
+ let admissibleOutputs = { outputsToAdmit: [], coinsToRetain: [] };
237
+ // Determine which outputs are admissible for this topic
238
+ const admissibleOutputsPromise = (async () => {
239
+ try {
240
+ this.startTime(`identifyAdmissibleOutputs_${txid.substring(0, 10)}`);
241
+ admissibleOutputs = await this.managers[topic].identifyAdmissibleOutputs(taggedBEEF.beef, previousCoins, offChainValues);
242
+ this.endTime(`identifyAdmissibleOutputs_${txid.substring(0, 10)}`);
231
243
  }
232
- }
233
- });
234
- let admissibleOutputs = { outputsToAdmit: [], coinsToRetain: [] };
235
- // Determine which outputs are admissible for this topic
236
- const admissibleOutputsPromise = (async () => {
237
- try {
238
- this.startTime(`identifyAdmissibleOutputs_${txid.substring(0, 10)}`);
239
- admissibleOutputs = await this.managers[topic].identifyAdmissibleOutputs(taggedBEEF.beef, previousCoins, offChainValues);
240
- this.endTime(`identifyAdmissibleOutputs_${txid.substring(0, 10)}`);
241
- }
242
- catch (_) {
243
- steak[topic] = { outputsToAdmit: [], coinsToRetain: [] };
244
- }
245
- })();
246
- // Wait for both tasks to complete
247
- await Promise.all([markSpentPromises, admissibleOutputsPromise]);
248
- // Keep track of what outputs were admitted for what topic
249
- steak[topic] = admissibleOutputs;
244
+ catch (_) {
245
+ failedTopics.add(topic);
246
+ steak[topic] = { outputsToAdmit: [], coinsToRetain: [] };
247
+ }
248
+ })();
249
+ // Wait for both tasks to complete
250
+ await Promise.all([...markSpentPromises, admissibleOutputsPromise]);
251
+ // Keep track of what outputs were admitted for what topic
252
+ steak[topic] = admissibleOutputs;
253
+ }
254
+ catch (error) {
255
+ this.logger.error('Error processing topic during submit:', error);
256
+ failedTopics.add(topic);
257
+ steak[topic] = { outputsToAdmit: [], coinsToRetain: [] };
258
+ }
250
259
  });
251
260
  await Promise.all(topicPromises);
252
261
  // Broadcast the transaction if not historical and broadcaster is configured
253
262
  this.startTime(`broadcast_${txid.substring(0, 10)}`);
254
263
  if (mode !== 'historical-tx' && this.broadcaster !== undefined) {
255
- const response = await this.broadcaster.broadcast(tx);
256
- if (isBroadcastFailure(response) && this.throwOnBroadcastFailure) {
257
- const e = new Error(`Failed to broadcast transaction! Error: ${response.description}`);
258
- e.more = response.more;
259
- throw e;
264
+ try {
265
+ const response = await this.broadcaster.broadcast(tx);
266
+ if (isBroadcastFailure(response) && this.throwOnBroadcastFailure) {
267
+ const e = new Error(`Failed to broadcast transaction! Error: ${response.description}`);
268
+ e.more = response.more;
269
+ throw e;
270
+ }
271
+ }
272
+ catch (error) {
273
+ if (this.throwOnBroadcastFailure) {
274
+ throw error;
275
+ }
276
+ this.logger.error('Error broadcasting transaction:', error);
260
277
  }
261
278
  }
262
279
  this.endTime(`broadcast_${txid.substring(0, 10)}`);
@@ -269,118 +286,130 @@ export class Engine {
269
286
  if (dupeTopics.has(topic)) {
270
287
  continue;
271
288
  }
272
- // Keep track of which outputs to admit, mark as stale, or retain
273
- const admissibleOutputs = steak[topic];
274
- const outputsToAdmit = admissibleOutputs.outputsToAdmit;
275
- const outputsConsumed = [];
276
- const outputsToMarkStale = [];
277
- // Recompute previousCoins for this topic to use in the update logic
278
- const previousCoins = [];
279
- await Promise.all(tx.inputs.map(async (input, i) => {
280
- const previousTXID = input.sourceTXID !== undefined ? input.sourceTXID : input.sourceTransaction?.id('hex');
281
- if (previousTXID !== undefined) {
282
- const output = await this.storage.findOutput(previousTXID, input.sourceOutputIndex, topic);
283
- if (output !== undefined && output !== null) {
284
- previousCoins.push(i);
285
- }
286
- }
287
- }));
288
- // 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.
289
- for (const inputIndex of previousCoins) {
290
- const previousTXID = tx.inputs[inputIndex].sourceTXID ?? tx.inputs[inputIndex].sourceTransaction?.id('hex');
291
- if (typeof previousTXID !== 'string')
292
- continue;
293
- const previousOutputIndex = tx.inputs[inputIndex].sourceOutputIndex;
294
- if (!admissibleOutputs.coinsToRetain.includes(inputIndex)) {
295
- outputsToMarkStale.push({
296
- txid: previousTXID,
297
- previousOutputIndex,
298
- inputIndex
299
- });
300
- }
301
- else {
302
- outputsConsumed.push({
303
- txid: previousTXID,
304
- outputIndex: previousOutputIndex
305
- });
306
- }
289
+ if (failedTopics.has(topic)) {
290
+ continue;
307
291
  }
308
- // Remove stale outputs recursively
309
- this.startTime(`lookForStaleOutputs_${txid.substring(0, 10)}`);
310
- await Promise.all(outputsToMarkStale.map(async (coin) => {
311
- const output = await this.storage.findOutput(coin.txid, coin.previousOutputIndex, topic);
312
- if (output !== undefined && output !== null) {
313
- await this.deleteUTXODeep(output);
314
- }
315
- }));
316
- this.endTime(`lookForStaleOutputs_${txid.substring(0, 10)}`);
317
- // Update the STEAK to indicate which coins were removed
318
- steak[topic].coinsRemoved = outputsToMarkStale.map(x => x.inputIndex);
319
- // Handle admittance and notification of incoming UTXOs
320
- const newUTXOs = [];
321
- await Promise.all(outputsToAdmit.map(async (outputIndex) => {
322
- if (typeof tx.outputs[outputIndex].satoshis !== 'number')
323
- return;
324
- this.startTime(`insertNewOutput_${txid.substring(0, 10)}`);
325
- await this.storage.insertOutput({
326
- txid,
327
- outputIndex,
328
- outputScript: tx.outputs[outputIndex].lockingScript.toBinary(),
329
- satoshis: tx.outputs[outputIndex].satoshis,
330
- topic,
331
- spent: false,
332
- beef: taggedBEEF.beef,
333
- consumedBy: [],
334
- outputsConsumed,
335
- score: Date.now()
336
- });
337
- this.endTime(`insertNewOutput_${txid.substring(0, 10)}`);
338
- newUTXOs.push({ txid, outputIndex });
339
- this.startTime(`notifyLookupService${txid.substring(0, 10)}`);
340
- await Promise.all(Object.values(this.lookupServices).map(async (l) => {
341
- if (l.admissionMode === 'locking-script') {
342
- if (typeof tx.outputs[outputIndex].lockingScript !== 'object' ||
343
- typeof tx.outputs[outputIndex].satoshis !== 'number') {
344
- return;
292
+ try {
293
+ const admissibleOutputs = steak[topic];
294
+ const outputsToAdmit = admissibleOutputs.outputsToAdmit;
295
+ const outputsConsumed = [];
296
+ const outputsToMarkStale = [];
297
+ // Recompute previousCoins for this topic to use in the update logic
298
+ const previousCoins = [];
299
+ await Promise.all(tx.inputs.map(async (input, i) => {
300
+ const previousTXID = input.sourceTXID !== undefined ? input.sourceTXID : input.sourceTransaction?.id('hex');
301
+ if (previousTXID !== undefined) {
302
+ const output = await this.storage.findOutput(previousTXID, input.sourceOutputIndex, topic);
303
+ if (output !== undefined && output !== null) {
304
+ previousCoins.push(i);
345
305
  }
346
- await l.outputAdmittedByTopic({
347
- mode: 'locking-script',
348
- txid,
349
- outputIndex,
350
- lockingScript: tx.outputs[outputIndex].lockingScript,
351
- satoshis: tx.outputs[outputIndex].satoshis,
352
- topic,
353
- offChainValues
306
+ }
307
+ }));
308
+ // 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.
309
+ for (const inputIndex of previousCoins) {
310
+ const previousTXID = tx.inputs[inputIndex].sourceTXID ?? tx.inputs[inputIndex].sourceTransaction?.id('hex');
311
+ if (typeof previousTXID !== 'string')
312
+ continue;
313
+ const previousOutputIndex = tx.inputs[inputIndex].sourceOutputIndex;
314
+ if (!admissibleOutputs.coinsToRetain.includes(inputIndex)) {
315
+ outputsToMarkStale.push({
316
+ txid: previousTXID,
317
+ previousOutputIndex,
318
+ inputIndex
354
319
  });
355
320
  }
356
321
  else {
357
- await l.outputAdmittedByTopic({
358
- mode: 'whole-tx',
359
- atomicBEEF: tx.toAtomicBEEF(),
360
- outputIndex,
361
- topic,
362
- offChainValues
322
+ outputsConsumed.push({
323
+ txid: previousTXID,
324
+ outputIndex: previousOutputIndex
363
325
  });
364
326
  }
365
- }));
366
- this.endTime(`notifyLookupService${txid.substring(0, 10)}`);
367
- }));
368
- this.startTime(`outputConsumed_${txid.substring(0, 10)}`);
369
- // Update each output consumed to know who consumed it and insert applied transaction in parallel
370
- await Promise.all([
371
- ...outputsConsumed.map(async (output) => {
372
- const outputToUpdate = await this.storage.findOutput(output.txid, output.outputIndex, topic);
373
- if (outputToUpdate !== undefined && outputToUpdate !== null) {
374
- const newConsumedBy = [...new Set([...newUTXOs, ...outputToUpdate.consumedBy])];
375
- await this.storage.updateConsumedBy(output.txid, output.outputIndex, topic, newConsumedBy);
327
+ }
328
+ // Remove stale outputs recursively
329
+ this.startTime(`lookForStaleOutputs_${txid.substring(0, 10)}`);
330
+ await Promise.all(outputsToMarkStale.map(async (coin) => {
331
+ const output = await this.storage.findOutput(coin.txid, coin.previousOutputIndex, topic);
332
+ if (output !== undefined && output !== null) {
333
+ await this.deleteUTXODeep(output);
376
334
  }
377
- }),
378
- this.storage.insertAppliedTransaction({
379
- txid,
380
- topic
381
- })
382
- ]);
383
- this.endTime(`outputConsumed_${txid.substring(0, 10)}`);
335
+ }));
336
+ this.endTime(`lookForStaleOutputs_${txid.substring(0, 10)}`);
337
+ // Update the STEAK to indicate which coins were removed
338
+ steak[topic].coinsRemoved = outputsToMarkStale.map(x => x.inputIndex);
339
+ // Handle admittance and notification of incoming UTXOs
340
+ const newUTXOs = [];
341
+ await Promise.all(outputsToAdmit.map(async (outputIndex) => {
342
+ if (typeof tx.outputs[outputIndex].satoshis !== 'number')
343
+ return;
344
+ this.startTime(`insertNewOutput_${txid.substring(0, 10)}`);
345
+ await this.storage.insertOutput({
346
+ txid,
347
+ outputIndex,
348
+ outputScript: tx.outputs[outputIndex].lockingScript.toBinary(),
349
+ satoshis: tx.outputs[outputIndex].satoshis,
350
+ topic,
351
+ spent: false,
352
+ beef: taggedBEEF.beef,
353
+ consumedBy: [],
354
+ outputsConsumed,
355
+ score: Date.now()
356
+ });
357
+ this.endTime(`insertNewOutput_${txid.substring(0, 10)}`);
358
+ newUTXOs.push({ txid, outputIndex });
359
+ this.startTime(`notifyLookupService${txid.substring(0, 10)}`);
360
+ await Promise.all(Object.values(this.lookupServices).map(async (l) => {
361
+ try {
362
+ if (l.admissionMode === 'locking-script') {
363
+ if (typeof tx.outputs[outputIndex].lockingScript !== 'object' ||
364
+ typeof tx.outputs[outputIndex].satoshis !== 'number') {
365
+ return;
366
+ }
367
+ await l.outputAdmittedByTopic({
368
+ mode: 'locking-script',
369
+ txid,
370
+ outputIndex,
371
+ lockingScript: tx.outputs[outputIndex].lockingScript,
372
+ satoshis: tx.outputs[outputIndex].satoshis,
373
+ topic,
374
+ offChainValues
375
+ });
376
+ }
377
+ else {
378
+ await l.outputAdmittedByTopic({
379
+ mode: 'whole-tx',
380
+ atomicBEEF: tx.toAtomicBEEF(),
381
+ outputIndex,
382
+ topic,
383
+ offChainValues
384
+ });
385
+ }
386
+ }
387
+ catch (error) {
388
+ this.logger.error('Error in lookup service for outputAdmittedByTopic:', error);
389
+ }
390
+ }));
391
+ this.endTime(`notifyLookupService${txid.substring(0, 10)}`);
392
+ }));
393
+ this.startTime(`outputConsumed_${txid.substring(0, 10)}`);
394
+ // Update each output consumed to know who consumed it and insert applied transaction in parallel
395
+ await Promise.all([
396
+ ...outputsConsumed.map(async (output) => {
397
+ const outputToUpdate = await this.storage.findOutput(output.txid, output.outputIndex, topic);
398
+ if (outputToUpdate !== undefined && outputToUpdate !== null) {
399
+ const newConsumedBy = [...new Set([...newUTXOs, ...outputToUpdate.consumedBy])];
400
+ await this.storage.updateConsumedBy(output.txid, output.outputIndex, topic, newConsumedBy);
401
+ }
402
+ }),
403
+ this.storage.insertAppliedTransaction({
404
+ txid,
405
+ topic
406
+ })
407
+ ]);
408
+ this.endTime(`outputConsumed_${txid.substring(0, 10)}`);
409
+ }
410
+ catch (error) {
411
+ this.logger.error('Error updating storage and notifying lookup services for topic', topic, error);
412
+ }
384
413
  }
385
414
  // If we don't have an advertiser or we are dealing with historical transactions, just return the steak
386
415
  if (this.advertiser === undefined || mode === 'historical-tx') {