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