@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.
- package/dist/cjs/package.json +2 -2
- package/dist/cjs/src/Engine.js +249 -220
- package/dist/cjs/src/Engine.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/Engine.js +245 -216
- package/dist/esm/src/Engine.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/Engine.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/API.md +2 -2
- package/package.json +2 -2
- package/src/Engine.ts +243 -218
- package/dist/cjs/src/AdmittanceInstructions.js +0 -3
- package/dist/cjs/src/AdmittanceInstructions.js.map +0 -1
- package/dist/cjs/src/LookupAnswer.js +0 -3
- package/dist/cjs/src/LookupAnswer.js.map +0 -1
- package/dist/cjs/src/LookupQuestion.js +0 -3
- package/dist/cjs/src/LookupQuestion.js.map +0 -1
- package/dist/cjs/src/SHIPAdvertisement.js +0 -3
- package/dist/cjs/src/SHIPAdvertisement.js.map +0 -1
- package/dist/cjs/src/SLAPAdvertisement.js +0 -3
- package/dist/cjs/src/SLAPAdvertisement.js.map +0 -1
- package/dist/cjs/src/STEAK.js +0 -3
- package/dist/cjs/src/STEAK.js.map +0 -1
- package/dist/cjs/src/TaggedBEEF.js +0 -3
- package/dist/cjs/src/TaggedBEEF.js.map +0 -1
- package/dist/esm/src/AdmittanceInstructions.js +0 -2
- package/dist/esm/src/AdmittanceInstructions.js.map +0 -1
- package/dist/esm/src/LookupAnswer.js +0 -2
- package/dist/esm/src/LookupAnswer.js.map +0 -1
- package/dist/esm/src/LookupQuestion.js +0 -2
- package/dist/esm/src/LookupQuestion.js.map +0 -1
- package/dist/esm/src/SHIPAdvertisement.js +0 -2
- package/dist/esm/src/SHIPAdvertisement.js.map +0 -1
- package/dist/esm/src/SLAPAdvertisement.js +0 -2
- package/dist/esm/src/SLAPAdvertisement.js.map +0 -1
- package/dist/esm/src/STEAK.js +0 -2
- package/dist/esm/src/STEAK.js.map +0 -1
- package/dist/esm/src/TaggedBEEF.js +0 -2
- package/dist/esm/src/TaggedBEEF.js.map +0 -1
- package/dist/types/src/AdmittanceInstructions.d.ts +0 -14
- package/dist/types/src/AdmittanceInstructions.d.ts.map +0 -1
- package/dist/types/src/LookupAnswer.d.ts +0 -15
- package/dist/types/src/LookupAnswer.d.ts.map +0 -1
- package/dist/types/src/LookupQuestion.d.ts +0 -15
- package/dist/types/src/LookupQuestion.d.ts.map +0 -1
- package/dist/types/src/SHIPAdvertisement.d.ts +0 -9
- package/dist/types/src/SHIPAdvertisement.d.ts.map +0 -1
- package/dist/types/src/SLAPAdvertisement.d.ts +0 -9
- package/dist/types/src/SLAPAdvertisement.d.ts.map +0 -1
- package/dist/types/src/STEAK.d.ts +0 -10
- package/dist/types/src/STEAK.d.ts.map +0 -1
- package/dist/types/src/TaggedBEEF.d.ts +0 -11
- package/dist/types/src/TaggedBEEF.d.ts.map +0 -1
package/dist/esm/src/Engine.js
CHANGED
|
@@ -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
|
-
|
|
137
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
225
|
-
|
|
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
|
-
|
|
230
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
273
|
-
|
|
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
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
outputIndex,
|
|
361
|
-
topic,
|
|
362
|
-
offChainValues
|
|
322
|
+
outputsConsumed.push({
|
|
323
|
+
txid: previousTXID,
|
|
324
|
+
outputIndex: previousOutputIndex
|
|
363
325
|
});
|
|
364
326
|
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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.
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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') {
|