@bsv/overlay 0.4.1 → 0.4.3

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 (60) hide show
  1. package/dist/cjs/package.json +5 -4
  2. package/dist/cjs/src/Engine.js +18 -8
  3. package/dist/cjs/src/Engine.js.map +1 -1
  4. package/dist/cjs/src/GASP/OverlayGASPRemote.js +8 -4
  5. package/dist/cjs/src/GASP/OverlayGASPRemote.js.map +1 -1
  6. package/dist/cjs/src/GASP/OverlayGASPStorage.js +25 -20
  7. package/dist/cjs/src/GASP/OverlayGASPStorage.js.map +1 -1
  8. package/dist/cjs/src/storage/knex/KnexStorage.js +32 -7
  9. package/dist/cjs/src/storage/knex/KnexStorage.js.map +1 -1
  10. package/dist/cjs/src/storage/knex/all-migrations.js +3 -1
  11. package/dist/cjs/src/storage/knex/all-migrations.js.map +1 -1
  12. package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.js +43 -0
  13. package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.js.map +1 -0
  14. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  15. package/dist/esm/src/Engine.js +17 -10
  16. package/dist/esm/src/Engine.js.map +1 -1
  17. package/dist/esm/src/GASP/OverlayGASPRemote.js +2 -1
  18. package/dist/esm/src/GASP/OverlayGASPRemote.js.map +1 -1
  19. package/dist/esm/src/GASP/OverlayGASPStorage.js +15 -14
  20. package/dist/esm/src/GASP/OverlayGASPStorage.js.map +1 -1
  21. package/dist/esm/src/storage/knex/KnexStorage.js +32 -7
  22. package/dist/esm/src/storage/knex/KnexStorage.js.map +1 -1
  23. package/dist/esm/src/storage/knex/all-migrations.js +3 -1
  24. package/dist/esm/src/storage/knex/all-migrations.js.map +1 -1
  25. package/dist/esm/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.js +39 -0
  26. package/dist/esm/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.js.map +1 -0
  27. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  28. package/dist/types/src/Engine.d.ts.map +1 -1
  29. package/dist/types/src/GASP/OverlayGASPRemote.d.ts +1 -1
  30. package/dist/types/src/GASP/OverlayGASPRemote.d.ts.map +1 -1
  31. package/dist/types/src/GASP/OverlayGASPStorage.d.ts +2 -5
  32. package/dist/types/src/GASP/OverlayGASPStorage.d.ts.map +1 -1
  33. package/dist/types/src/Output.d.ts +1 -0
  34. package/dist/types/src/Output.d.ts.map +1 -1
  35. package/dist/types/src/storage/Storage.d.ts +17 -2
  36. package/dist/types/src/storage/Storage.d.ts.map +1 -1
  37. package/dist/types/src/storage/knex/KnexStorage.d.ts +3 -1
  38. package/dist/types/src/storage/knex/KnexStorage.d.ts.map +1 -1
  39. package/dist/types/src/storage/knex/all-migrations.d.ts.map +1 -1
  40. package/dist/types/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts +4 -0
  41. package/dist/types/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts.map +1 -0
  42. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  43. package/package.json +5 -4
  44. package/src/Engine.ts +20 -11
  45. package/src/GASP/OverlayGASPRemote.ts +3 -2
  46. package/src/GASP/OverlayGASPStorage.ts +17 -16
  47. package/src/Output.ts +1 -0
  48. package/src/__tests/Engine.test.ts +65 -63
  49. package/src/__tests/OverlayGASPRemote.test.ts +1 -1
  50. package/src/__tests/OverlayGASPStorage.test.ts +21 -17
  51. package/src/storage/Storage.ts +19 -2
  52. package/src/storage/knex/KnexStorage.ts +37 -7
  53. package/src/storage/knex/all-migrations.ts +4 -1
  54. package/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.ts +46 -0
  55. package/dist/cjs/src/GASP/GASP.js +0 -283
  56. package/dist/cjs/src/GASP/GASP.js.map +0 -1
  57. package/dist/esm/src/GASP/GASP.js +0 -288
  58. package/dist/esm/src/GASP/GASP.js.map +0 -1
  59. package/dist/types/src/GASP/GASP.d.ts +0 -218
  60. package/dist/types/src/GASP/GASP.d.ts.map +0 -1
package/src/Engine.ts CHANGED
@@ -2,7 +2,6 @@ import { TopicManager } from './TopicManager.js'
2
2
  import { LookupService } from './LookupService.js'
3
3
  import { Storage } from './storage/Storage.js'
4
4
  import type { Output } from './Output.js'
5
- import { LookupFormula } from './LookupFormula.js'
6
5
  import {
7
6
  Transaction,
8
7
  ChainTracker,
@@ -25,6 +24,8 @@ import { SyncConfiguration } from './SyncConfiguration.js'
25
24
  import { OverlayGASPRemote } from './GASP/OverlayGASPRemote.js'
26
25
  import { OverlayGASPStorage } from './GASP/OverlayGASPStorage.js'
27
26
 
27
+ const DEFAULT_GASP_SYNC_LIMIT = 10000
28
+
28
29
  /**
29
30
  * An engine for running BSV Overlay Services (topic managers and lookup services).
30
31
  */
@@ -355,7 +356,8 @@ export class Engine {
355
356
  spent: false,
356
357
  beef: taggedBEEF.beef,
357
358
  consumedBy: [],
358
- outputsConsumed
359
+ outputsConsumed,
360
+ score: Date.now()
359
361
  })
360
362
  this.endTime(`insertNewOutput_${txid.substring(0, 10)}`)
361
363
  newUTXOs.push({ txid, outputIndex })
@@ -632,15 +634,23 @@ export class Engine {
632
634
  this.logger.info(`[GASP SYNC] Starting sync for topic "${topic}" with peer "${endpoint}"`)
633
635
 
634
636
  try {
637
+ // Read the last interaction score from storage
638
+ const lastInteraction = await this.storage.getLastInteraction(endpoint, topic)
639
+
635
640
  const gasp = new GASP(
636
641
  new OverlayGASPStorage(topic, this),
637
642
  new OverlayGASPRemote(endpoint, topic),
638
- 0,
643
+ lastInteraction,
639
644
  `[GASP Sync of ${topic} with ${endpoint}]`,
640
645
  true,
641
646
  true
642
647
  )
643
- await gasp.sync()
648
+ await gasp.sync(endpoint, DEFAULT_GASP_SYNC_LIMIT)
649
+
650
+ // Save the updated last interaction score
651
+ if (gasp.lastInteraction > lastInteraction) {
652
+ await this.storage.updateLastInteraction(endpoint, topic, gasp.lastInteraction)
653
+ }
644
654
 
645
655
  this.logger.info(`[GASP SYNC] Sync successful for topic "${topic}" with peer "${endpoint}"`)
646
656
  } catch (err) {
@@ -667,15 +677,14 @@ export class Engine {
667
677
  * @returns A promise that resolves to a GASPInitialResponse containing the list of UTXOs and the provided min block height.
668
678
  */
669
679
  async provideForeignSyncResponse (initialRequest: GASPInitialRequest, topic: string): Promise<GASPInitialResponse> {
670
- const UTXOs = await this.storage.findUTXOsForTopic(topic, initialRequest.since)
680
+ const outputs = await this.storage.findUTXOsForTopic(topic, initialRequest.since, initialRequest.limit)
671
681
 
672
682
  return {
673
- UTXOList: UTXOs.map(output => {
674
- return {
675
- txid: output.txid,
676
- outputIndex: output.outputIndex
677
- }
678
- }),
683
+ UTXOList: outputs.map(output => ({
684
+ txid: output.txid,
685
+ outputIndex: output.outputIndex,
686
+ score: output.score ?? 0
687
+ })),
679
688
  since: initialRequest.since
680
689
  }
681
690
  }
@@ -36,7 +36,8 @@ export class OverlayGASPRemote implements GASPRemote {
36
36
  return {
37
37
  UTXOList: result.UTXOList.map((utxo: any) => ({
38
38
  txid: utxo.txid,
39
- outputIndex: utxo.outputIndex
39
+ outputIndex: utxo.outputIndex,
40
+ score: utxo.score ?? 0
40
41
  })),
41
42
  since: result.since
42
43
  }
@@ -101,7 +102,7 @@ export class OverlayGASPRemote implements GASPRemote {
101
102
 
102
103
  // Only used when supporting bidirectional sync.
103
104
  // Overlay services does not support this.
104
- async submitNode (node: GASPNode): Promise<void | GASPNodeResponse> {
105
+ async submitNode (node: GASPNode): Promise<GASPNodeResponse | undefined> {
105
106
  throw new Error('Node submission not supported!')
106
107
  }
107
108
  }
@@ -1,4 +1,4 @@
1
- import { GASPNode, GASPNodeResponse, GASPStorage } from '@bsv/gasp'
1
+ import { GASPNode, GASPNodeResponse, GASPStorage, GASPOutput } from '@bsv/gasp'
2
2
  import { MerklePath, Transaction, Utils } from '@bsv/sdk'
3
3
  import { Engine } from '../Engine.js'
4
4
 
@@ -29,11 +29,12 @@ export class OverlayGASPStorage implements GASPStorage {
29
29
  * @param since
30
30
  * @returns
31
31
  */
32
- async findKnownUTXOs (since: number): Promise<Array<{ txid: string, outputIndex: number }>> {
32
+ async findKnownUTXOs (since: number): Promise<GASPOutput[]> {
33
33
  const UTXOs = await this.engine.storage.findUTXOsForTopic(this.topic, since)
34
34
  return UTXOs.map(output => ({
35
35
  txid: output.txid,
36
- outputIndex: output.outputIndex
36
+ outputIndex: output.outputIndex,
37
+ score: output.score ?? 0
37
38
  }))
38
39
  }
39
40
 
@@ -80,7 +81,7 @@ export class OverlayGASPStorage implements GASPStorage {
80
81
  const parsedTx = Transaction.fromHex(tx.rawTx)
81
82
  if (tx.proof === undefined) {
82
83
  for (const input of parsedTx.inputs) {
83
- response.requestedInputs[`${input.sourceTXID}.${input.sourceOutputIndex}`] = {
84
+ response.requestedInputs[`${input.sourceTXID ?? ''}.${input.sourceOutputIndex}`] = {
84
85
  metadata: false
85
86
  }
86
87
  }
@@ -220,12 +221,13 @@ export class OverlayGASPStorage implements GASPStorage {
220
221
  // For any input to this transaction, see if it's a valid coin that's admitted. If so, it's a previous coin.
221
222
  const previousCoins: number[] = []
222
223
  const tx = Transaction.fromBEEF(beef)
223
- for (const inputIndex in tx.inputs) {
224
- const input = tx.inputs[inputIndex]
225
- const sourceTXID = input.sourceTXID || input.sourceTransaction?.id('hex')
226
- const coin = `${sourceTXID}.${input.sourceOutputIndex}`
227
- if (coins.has(coin)) {
228
- previousCoins.push(Number(inputIndex))
224
+ for (const [inputIndex, input] of tx.inputs.entries()) {
225
+ const sourceTXID = input.sourceTXID ?? input.sourceTransaction?.id('hex')
226
+ if (sourceTXID != null && sourceTXID !== '') {
227
+ const coin = `${sourceTXID}.${input.sourceOutputIndex}`
228
+ if (coins.has(coin)) {
229
+ previousCoins.push(Number(inputIndex))
230
+ }
229
231
  }
230
232
  }
231
233
  const admittanceInstructions = await this.engine.managers[this.topic].identifyAdmissibleOutputs(beef, previousCoins)
@@ -293,7 +295,7 @@ export class OverlayGASPStorage implements GASPStorage {
293
295
 
294
296
  // Start the hydrator with the root node
295
297
  const foundRoot = this.temporaryGraphNodeRefs[graphID]
296
- if (!foundRoot) {
298
+ if (foundRoot == null) {
297
299
  throw new Error('Unable to find root node in graph for finalization!')
298
300
  }
299
301
  hydrator(foundRoot)
@@ -309,15 +311,14 @@ export class OverlayGASPStorage implements GASPStorage {
309
311
  // Given a node, hydrate its merkle proof or all inputs, returning a reference to the hydrated node's Transaction object
310
312
  const hydrator = (node: GraphNode): Transaction => {
311
313
  const tx = Transaction.fromHex(node.rawTx)
312
- if (node.proof) {
314
+ if (node.proof != null && node.proof !== '') {
313
315
  tx.merklePath = MerklePath.fromHex(node.proof)
314
316
  return tx // Transaction with proof, end of the line.
315
317
  }
316
318
  // For each input, look it up and recurse.
317
- for (const inputIndex in tx.inputs) {
318
- const input = tx.inputs[inputIndex]
319
- const foundNode = this.temporaryGraphNodeRefs[`${input.sourceTXID}.${input.sourceOutputIndex}`]
320
- if (!foundNode) {
319
+ for (const [inputIndex, input] of tx.inputs.entries()) {
320
+ const foundNode = this.temporaryGraphNodeRefs[`${input.sourceTXID ?? ''}.${input.sourceOutputIndex}`]
321
+ if (foundNode == null) {
321
322
  throw new Error('Required input node for unproven parent not found in temporary graph store. Ensure, for every parent of any given already-proven node (kept for Overlay-specific historical reasons), that a proof is also provided on those inputs. While implicitly they are valid by virtue of their descendents being proven in the blockchain, BEEF serialization will still fail when winding forward the topical UTXO set histories during sync.')
322
323
  }
323
324
  tx.inputs[inputIndex].sourceTransaction = hydrator(foundNode)
package/src/Output.ts CHANGED
@@ -27,4 +27,5 @@ export interface Output {
27
27
  /** The transaction data for the output */
28
28
  beef?: number[]
29
29
  blockHeight?: number
30
+ score?: number
30
31
  }
@@ -6,13 +6,19 @@ import { Storage } from '../storage/Storage'
6
6
  import { Transaction, Utils, TaggedBEEF, AdmittanceInstructions, STEAK } from '@bsv/sdk'
7
7
  import { Output } from '../Output'
8
8
  import { SyncConfiguration } from '../SyncConfiguration'
9
- import { Advertiser } from '../Advertiser.js'
9
+ import { Advertiser } from '../Advertiser'
10
10
 
11
11
  const mockChainTracker = {
12
12
  isValidRootForHeight: jest.fn(async () => true),
13
13
  currentHeight: jest.fn(async () => 800000)
14
14
  }
15
15
 
16
+ // Call the method that would normally trigger syncAdvertisements
17
+ const engineSubmit = jest.fn(async (taggedBEEF: TaggedBEEF, onSteakReady: any, mode?: string): Promise<STEAK> => {
18
+ const result: STEAK = { tm_helloworld: { outputsToAdmit: [], coinsToRetain: [], coinsRemoved: [] } }
19
+ return result
20
+ })
21
+
16
22
  const BRC62Hex = '0100beef01fe636d0c0007021400fe507c0c7aa754cef1f7889d5fd395cf1f785dd7de98eed895dbedfe4e5bc70d1502ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e010b00bc4ff395efd11719b277694cface5aa50d085a0bb81f613f70313acd28cf4557010400574b2d9142b8d28b61d88e3b2c3f44d858411356b49a28a4643b6d1a6a092a5201030051a05fc84d531b5d250c23f4f886f6812f9fe3f402d61607f977b4ecd2701c19010000fd781529d58fc2523cf396a7f25440b409857e7e221766c57214b1d38c7b481f01010062f542f45ea3660f86c013ced80534cb5fd4c19d66c56e7e8c5d4bf2d40acc5e010100b121e91836fd7cd5102b654e9f72f3cf6fdbfd0b161c53a9c54b12c841126331020100000001cd4e4cac3c7b56920d1e7655e7e260d31f29d9a388d04910f1bbd72304a79029010000006b483045022100e75279a205a547c445719420aa3138bf14743e3f42618e5f86a19bde14bb95f7022064777d34776b05d816daf1699493fcdf2ef5a5ab1ad710d9c97bfb5b8f7cef3641210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013e660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000001000100000001ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e000000006a47304402203a61a2e931612b4bda08d541cfb980885173b8dcf64a3471238ae7abcd368d6402204cbf24f04b9aa2256d8901f0ed97866603d2be8324c2bfb7a37bf8fc90edd5b441210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013c660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000000'
17
23
  const exampleTX = Transaction.fromHexBEEF(BRC62Hex)
18
24
 
@@ -29,7 +35,8 @@ const mockOutput: Output = {
29
35
  beef: exampleBeef,
30
36
  spent: false,
31
37
  outputsConsumed: [],
32
- consumedBy: []
38
+ consumedBy: [],
39
+ score: 1234567890
33
40
  }
34
41
 
35
42
  const invalidHostingUrls = [
@@ -90,7 +97,9 @@ describe('BSV Overlay Services Engine', () => {
90
97
  updateConsumedBy: jest.fn(),
91
98
  updateTransactionBEEF: jest.fn(),
92
99
  deleteOutput: jest.fn(),
93
- findUTXOsForTopic: jest.fn()
100
+ findUTXOsForTopic: jest.fn(),
101
+ updateLastInteraction: jest.fn(),
102
+ getLastInteraction: jest.fn(async () => 0)
94
103
  }
95
104
  })
96
105
 
@@ -109,7 +118,7 @@ describe('BSV Overlay Services Engine', () => {
109
118
  undefined
110
119
  )
111
120
 
112
- engine.submit = jest.fn(async (taggedBEEF: TaggedBEEF, onSteakReady: any, mode?: string) => ({ tm_helloworld: { outputsToAdmit: [], coinsToRetain: [], coinsRemoved: [] } } as STEAK))
121
+ engine.submit = engineSubmit
113
122
 
114
123
  // Call the method that would normally trigger syncAdvertisements
115
124
  const result = await engine.syncAdvertisements()
@@ -148,9 +157,8 @@ describe('BSV Overlay Services Engine', () => {
148
157
  undefined
149
158
  )
150
159
 
151
- // Call the method that would normally trigger syncAdvertisements
152
- engine.submit = jest.fn(async (taggedBEEF: TaggedBEEF, onSteakReady: any, mode?: string) => ({ tm_helloworld: { outputsToAdmit: [], coinsToRetain: [], coinsRemoved: [] } } as STEAK))
153
- const result = await engine.syncAdvertisements()
160
+ engine.submit = engineSubmit
161
+ await engine.syncAdvertisements()
154
162
  expect(engine.submit).toHaveBeenCalled()
155
163
 
156
164
  // Verify that Advertiser methods are called
@@ -237,23 +245,12 @@ describe('BSV Overlay Services Engine', () => {
237
245
  })
238
246
  })
239
247
  describe('handleNewMerkleProof tests', () => {
240
- const mockOutput: Output = {
241
- txid: exampleTXID,
242
- outputIndex: 0,
243
- outputScript: exampleTX.outputs[0].lockingScript.toBinary(),
244
- topic: 'hello',
245
- satoshis: exampleTX.outputs[0].satoshis,
246
- beef: exampleBeef,
247
- spent: false,
248
- outputsConsumed: [],
249
- consumedBy: []
250
- }
251
248
  it('0 simple proof', async () => {
252
- const beef = beef27c8f_1
249
+ const beef = beef27c8f1
253
250
  const txid = txid27c8f
254
251
  const tx = Transaction.fromHexBEEF(beef)
255
252
 
256
- const output27c8f_0: Output = {
253
+ const output27c8fZero: Output = {
257
254
  txid,
258
255
  outputIndex: 0,
259
256
  outputScript: tx.outputs[0].lockingScript.toBinary(),
@@ -262,7 +259,8 @@ describe('BSV Overlay Services Engine', () => {
262
259
  beef: tx.toBEEF(),
263
260
  spent: false,
264
261
  outputsConsumed: [],
265
- consumedBy: []
262
+ consumedBy: [],
263
+ score: Date.now()
266
264
  }
267
265
 
268
266
  mockLookupService.lookup = jest.fn(async () => [{
@@ -271,8 +269,8 @@ describe('BSV Overlay Services Engine', () => {
271
269
  history: 1
272
270
  }])
273
271
  let newBEEF: number[] = []
274
- mockStorageEngine.findOutput = jest.fn(async () => output27c8f_0)
275
- mockStorageEngine.findOutputsForTransaction = jest.fn(async () => [output27c8f_0])
272
+ mockStorageEngine.findOutput = jest.fn(async () => output27c8fZero)
273
+ mockStorageEngine.findOutputsForTransaction = jest.fn(async () => [output27c8fZero])
276
274
  mockStorageEngine.updateTransactionBEEF = jest.fn(async (txid: string, beef: number[]) => {
277
275
  newBEEF = beef
278
276
  })
@@ -287,8 +285,8 @@ describe('BSV Overlay Services Engine', () => {
287
285
  mockChainTracker
288
286
  )
289
287
 
290
- const merklePath = Transaction.fromHexBEEF(beef27c8f_0).merklePath
291
- if (!merklePath) throw ('improper test setup')
288
+ const merklePath = Transaction.fromHexBEEF(beef27c8f0).merklePath
289
+ if (merklePath == null) throw new Error('improper test setup')
292
290
  await engine.handleNewMerkleProof(txid, merklePath)
293
291
  expect(newBEEF.length > 0 && newBEEF.length < beef.length / 2).toBe(true)
294
292
  })
@@ -311,9 +309,10 @@ describe('BSV Overlay Services Engine', () => {
311
309
  beef: tx.toBEEF(),
312
310
  spent: false,
313
311
  outputsConsumed: [],
314
- consumedBy: []
312
+ consumedBy: [],
313
+ score: Date.now()
315
314
  }
316
- if (consumes) {
315
+ if (consumes != null) {
317
316
  const c = findOutput(consumes.txid, consumes.outputIndex)
318
317
  c.consumedBy.push(o)
319
318
  o.outputsConsumed.push(c)
@@ -346,52 +345,52 @@ describe('BSV Overlay Services Engine', () => {
346
345
  txid a7d4cac391f5b3d8dedaacb3cd8b5afac54df2d8e043ae0452abcfe190686494 output 17 1132 sats
347
346
  */
348
347
 
349
- const tx17d182 = Transaction.fromHexBEEF(beef17d182_4)
348
+ const tx17d182 = Transaction.fromHexBEEF(beef17d1824)
350
349
  const tx942620 = tx17d182.inputs[0].sourceTransaction
351
350
  const tx509f5e = tx942620.inputs[0].sourceTransaction
352
351
  const tx877734 = tx509f5e.inputs[0].sourceTransaction
353
352
  const tx37abad = tx877734.inputs[0].sourceTransaction
354
353
 
355
- const output37abad_0 = addConsumingOutput(tx37abad, 0)
356
- const output877734_0 = addConsumingOutput(tx877734, 0, output37abad_0)
357
- const output509f5e_0 = addConsumingOutput(tx509f5e, 0, output877734_0)
358
- const output942620_0 = addConsumingOutput(tx942620, 0, output509f5e_0)
359
- const output17d182_0 = addConsumingOutput(tx17d182, 0, output942620_0)
354
+ const output37abadZero = addConsumingOutput(tx37abad, 0)
355
+ const output877734Zero = addConsumingOutput(tx877734, 0, output37abadZero)
356
+ const output509f5eZero = addConsumingOutput(tx509f5e, 0, output877734Zero)
357
+ const output942620Zero = addConsumingOutput(tx942620, 0, output509f5eZero)
358
+ addConsumingOutput(tx17d182, 0, output942620Zero)
360
359
 
361
- const mp37abad = Transaction.fromHexBEEF(beef37abad_0).merklePath
360
+ const mp37abad = Transaction.fromHexBEEF(beef37abad0).merklePath
362
361
  await engine.handleNewMerkleProof(txid37abad, mp37abad)
363
362
  expect(Object.keys(newBEEF).length).toBe(0)
364
363
 
365
- const mp877734 = Transaction.fromHexBEEF(beef877734_0).merklePath
364
+ const mp877734 = Transaction.fromHexBEEF(beef8777340).merklePath
366
365
  await engine.handleNewMerkleProof(txid877734, mp877734)
367
- expect(newBEEF[`${txid877734}`]).toBe(beef877734_0)
368
- expect(newBEEF[`${txid509f5e}`].length).toBeGreaterThan(beef509f5e_0.length)
369
- expect(newBEEF[`${txid942620}`].length).toBeGreaterThan(beef942620_0.length)
370
- expect(newBEEF[`${txid17d182}`].length).toBeGreaterThan(beef17d182_0.length)
366
+ expect(newBEEF[`${txid877734}`]).toBe(beef8777340)
367
+ expect(newBEEF[`${txid509f5e}`].length).toBeGreaterThan(beef509f5e0.length)
368
+ expect(newBEEF[`${txid942620}`].length).toBeGreaterThan(beef9426200.length)
369
+ expect(newBEEF[`${txid17d182}`].length).toBeGreaterThan(beef17d1820.length)
371
370
  expect(Object.keys(newBEEF).length).toBe(4)
372
371
 
373
- const mp509f5e = Transaction.fromHexBEEF(beef509f5e_0).merklePath
372
+ const mp509f5e = Transaction.fromHexBEEF(beef509f5e0).merklePath
374
373
  await engine.handleNewMerkleProof(txid509f5e, mp509f5e)
375
- expect(newBEEF[`${txid877734}`]).toBe(beef877734_0)
376
- expect(newBEEF[`${txid509f5e}`]).toBe(beef509f5e_0)
377
- expect(newBEEF[`${txid942620}`].length).toBeGreaterThan(beef942620_0.length)
378
- expect(newBEEF[`${txid17d182}`].length).toBeGreaterThan(beef17d182_0.length)
374
+ expect(newBEEF[`${txid877734}`]).toBe(beef8777340)
375
+ expect(newBEEF[`${txid509f5e}`]).toBe(beef509f5e0)
376
+ expect(newBEEF[`${txid942620}`].length).toBeGreaterThan(beef9426200.length)
377
+ expect(newBEEF[`${txid17d182}`].length).toBeGreaterThan(beef17d1820.length)
379
378
  expect(Object.keys(newBEEF).length).toBe(4)
380
379
 
381
- const mp942620 = Transaction.fromHexBEEF(beef942620_0).merklePath
380
+ const mp942620 = Transaction.fromHexBEEF(beef9426200).merklePath
382
381
  await engine.handleNewMerkleProof(txid942620, mp942620)
383
- expect(newBEEF[`${txid877734}`]).toBe(beef877734_0)
384
- expect(newBEEF[`${txid509f5e}`]).toBe(beef509f5e_0)
385
- expect(newBEEF[`${txid942620}`]).toBe(beef942620_0)
386
- expect(newBEEF[`${txid17d182}`].length).toBeGreaterThan(beef17d182_0.length)
382
+ expect(newBEEF[`${txid877734}`]).toBe(beef8777340)
383
+ expect(newBEEF[`${txid509f5e}`]).toBe(beef509f5e0)
384
+ expect(newBEEF[`${txid942620}`]).toBe(beef9426200)
385
+ expect(newBEEF[`${txid17d182}`].length).toBeGreaterThan(beef17d1820.length)
387
386
  expect(Object.keys(newBEEF).length).toBe(4)
388
387
 
389
- const mp17d182 = Transaction.fromHexBEEF(beef17d182_0).merklePath
388
+ const mp17d182 = Transaction.fromHexBEEF(beef17d1820).merklePath
390
389
  await engine.handleNewMerkleProof(txid17d182, mp17d182)
391
- expect(newBEEF[`${txid877734}`]).toBe(beef877734_0)
392
- expect(newBEEF[`${txid509f5e}`]).toBe(beef509f5e_0)
393
- expect(newBEEF[`${txid942620}`]).toBe(beef942620_0)
394
- expect(newBEEF[`${txid17d182}`]).toBe(beef17d182_0)
390
+ expect(newBEEF[`${txid877734}`]).toBe(beef8777340)
391
+ expect(newBEEF[`${txid509f5e}`]).toBe(beef509f5e0)
392
+ expect(newBEEF[`${txid942620}`]).toBe(beef9426200)
393
+ expect(newBEEF[`${txid17d182}`]).toBe(beef17d1820)
395
394
  expect(Object.keys(newBEEF).length).toBe(4)
396
395
  })
397
396
  })
@@ -631,7 +630,10 @@ describe('BSV Overlay Services Engine', () => {
631
630
  topics: ['hello']
632
631
  })
633
632
  // Test the new UTXO was added
634
- expect(mockStorageEngine.insertOutput).toHaveBeenCalledWith(mockOutput)
633
+ expect(mockStorageEngine.insertOutput).toHaveBeenCalledWith(expect.objectContaining({
634
+ ...mockOutput,
635
+ score: expect.any(Number)
636
+ }))
635
637
  })
636
638
  it('Notifies lookup services about incoming admissible UTXOs', async () => {
637
639
  // Mock findUTXO to return a UTXO
@@ -1137,9 +1139,9 @@ describe('BSV Overlay Services Engine', () => {
1137
1139
  })
1138
1140
 
1139
1141
  // beef27c8f_1 is 1 level deep, i.e. proofs on inputs
1140
- const beef27c8f_1 = '0100beef01feafcf0c000802020044ef36fc6c79af360b13f2164c6e772e6e5e2083a54d72894f34710fe6a68cf80302792c8c563b17721744d8f101d79e873aae62816d0fd47dd9d90ada219c1b252d010000059e83aae2cfc6a35477566439dd9d58969cc41a134864c671c9b844f2a409a60101004991c129bba819832d6dd315c3a2065bd31b1a60aff8192be335f83731641e690101002f87724a7387df82417902266d2a4fb2fee857451e963a352ce8acbb279aa9eb010100a9e9eb55d20cef840bacbdb26ad8d6e7680ef7653364830b1e770fa140ece6bb010100f0ecd03fff2df21b0034d331cade50152781be9052dcec54eb95c963ef1cdcbf0101006fdb2c01a266e761a32f53b3ad0fabbca31ae234d31d85d9af9bbd624b321c9b0101008108cad77aabf7b0ee371f42afd773cada534244535f7108c7590df2b9d5f8a2020100000006e9847b33c5d666ccee1dccd52235489d8d58bf432be1b8c0cc7c13af80f10703030000006b483045022100fe8a18e043d693246a56c9b32ff553bea89a0bbe389bc55e8244044dcd865de3022065245c30fbbcc6820e4761048ed28c587bf41ece1933dfb85b8fa8c3433d93504121022f2ac22e73a55aa4ff21e905f66959200642ae74716544f834a48af5aa53fb27ffffffff7e8823d7e3e5565fdb9e53c015ad5b63bc6dd6ee1a9be7f7a8e105c903783d45050000006b483045022100e6332a6459350ff2486a39b6d79cdc0b65b6d52cf898fc457e45880c0db01b2a02206e34126bc81e6f03c6de90278788d6b2be3a3f5d7bd85e6442bbea90b8a5e2394121029780a1a98be5ff17a3228feb830e3bc32395ebecc5cf6de08f7a5685faf66c07ffffffff7e8823d7e3e5565fdb9e53c015ad5b63bc6dd6ee1a9be7f7a8e105c903783d45040000006b483045022100dfba71c6c33d2366b2202ea136271fd2f3a26c0a7295f6dd6018fbe12fc48708022052387ca532247bbfffef4b076fd2fbbdebe85b3cf53c73004897cd07be5d1ad6412103f735eb69c9f0b3c90dd2798b709e12a5b3d176a4690bd2a31ec616af9ecd47b5ffffffff7e8823d7e3e5565fdb9e53c015ad5b63bc6dd6ee1a9be7f7a8e105c903783d45020000006b483045022100f941b74c5da1e7f0ce4d1c08745e4d6b8ef56656199505caeff4b74f54890cec02201eaa0d01112497c89ebe4818687c5f3305629a83abd3b0bf5135feee68f57ca0412103fb0ec51f9e6e45b213cb1deff921e3322624cfb6d4a4ba5dc699867cf163b736ffffffff7e8823d7e3e5565fdb9e53c015ad5b63bc6dd6ee1a9be7f7a8e105c903783d45030000006a47304402205a3a2848350fdc19cae73243bea16dfc1e8fbbd5cecde4b5c88a57790505cebb022043db51d04998d52ec394d0e945a76e9922dd63caffb882242bec2ced2a35c7f1412102bae3e2f47dceaed7db2a212e059a00674a0087e3395b92d9f12cdbefdbc84efaffffffffad49b6f41125ffadab03cb2dcc24caf2c897c2cd4aed6e0a3b5c003f2dde757a030000006b483045022100aa7aaec7fc6c5a2f4d5318b7fcbbca0f223c5c198f8fc0e1c8d59aee7155af1202207a4960fb58aa86eba12852f84fa3cdb4c427c7374fe737b3d5bcd9e2a6d76e3b412102b55cfd872e85b3dd424dcceafe01ed5a87dfb599d354ab7fc50d9ec48af77f6effffffff1a400d0300000000001976a914f182138abcb69c7c2339040605aa0a442cfec44288acc8000000000000001976a914e9d2276f468223a6d650672ab9d4c1271fb157e288acacb00000000000001976a914c0ea0208a097df8146933ae15f3ce545d5451e7388ac7a140000000000001976a9142d9ae3291f9393639fe2c38c9a7a948d136a10b488acea070000000000001976a91435d1c396b7ccb467118a41c2d77ebf79fb5fb7eb88ac17050000000000001976a9140dac1e2ddc975ee6d9f393e2ccd4876edb386cdd88ac51060000000000001976a91422878cb2f57a1ebe513815562003128f6e99eda188ac67040000000000001976a91439a752923ba1ef8b126ba7886646d58f54521c6588ac1e040000000000001976a914a939565af52d3fa974a040f42c35365d76b98f6a88acec030000000000001976a914c90823a53e29cafd35a225cc14e3a44eda143a5788ac05040000000000001976a914af6f8d6aaec3bf123efc17c8977bfad1ec1c859388acec030000000000001976a914d4c5e66a062e634be1c6f0eb1f2af0acebecb47988acf1030000000000001976a914a94a3e84bd740095ab39bed110cd3b3922c8595088ace8030000000000001976a914aa2e3c3bf6f3ee79f5af3b94eab2be60a394ce4c88aceb030000000000001976a91459394fa083ff3726571c79624dba3b38f14d924c88ace8030000000000001976a914726eb3ae6211711aa1624f3d767035b0b44abdc488ace8030000000000001976a9145f4680a099a6dc6891af3094aaab96aa66a6259888ace8030000000000001976a914f1a4fbd99e3923b0934abdb8839c337eb2fd50b888ace8030000000000001976a9147ed0ad307f5fe9993f984e7525c0012e3569594588ace8030000000000001976a91407f386bcdc4892cc2d0be3226b721ceaf04750b288ace8030000000000001976a914b51edaaa3c92d0af94109d952c7ddf13aa345f9988ace8030000000000001976a914018f4e02bd74f17a75f5b3965db4633ab514c57588ace8030000000000001976a914cc8d1f7ba5406b81efbc23da0fcccb4d3fe5b40a88ace8030000000000001976a914db1d4b7bd3e62f535dc3f2c8d0a989b000fa736d88ace8030000000000001976a914b8ea69358da7bea9506b15db6fc6597b3849b0a188ace8030000000000001976a914ecd769fdd67e04786e871c50c1a10504861bd79488ac0000000001000100000001792c8c563b17721744d8f101d79e873aae62816d0fd47dd9d90ada219c1b252d070000006b48304502210090368b5925795abb0415a3cd6265d7abae7d3541fb7f2fdb1fdffdd916b3fce3022068136da44df8ee367807c7c59c950ea8d428adfe142268cd6371cf4c1b4c3208412102858ea1804708d1e16e77b49ba1559b3742797f5f43efd3922fa3d21557521d7cffffffff03f401000000000000fde10121026afd108ddbc05e093207ebdf06557a3d66e15b96accbf1766c2e7d6efa3d1981ac22313852713463583334356b6a7461734467764b41455a435135564c5a72516f76786d2081d846bcae65ce281829b6d81eff3862a68b8eaf4e99ad97c0c4ad2e149631c020f936ace2d2b4034710dec819e343f69ddbb1a16d721b88413b697c2475f75f504c4e8470062e6966cb7ec80864e5e1217ecf2b3f113bb5307e1ec1d338678e54b426a11a267e990f7dcd85072c7b3870fabb54812d7f8a7aaa672ce01e4f8a2c6a1d532849877329bd23be0b3383fcb437ffd1ff05aad824343bfe5bae52d5775ed99ba9d5a72038dd50f81153e674a8b1c5d659aac361e660cc5657b291eda149de2505efb0bbac0a353331393633353238334c797f3040f01934bb25733d4700a0309194a7defd8ceea7a8a59ad279c6237e2917264a115773777c5746546288b15ec1cf6788b801dd3952fec804cadbba2ff4e05464735e2db83c3509b59119efc843716c34becd2884f011f2e7f61f382237891875115b6ea5bac1c3e9ca6c575b44547d589c38c1b5a8cadb463044022016e1fdb226b465f3151aaa1e033b122dd58f3b495d2846d532f784ec8f6fc9ae022031dc12155c02e7f588c9f6d975a79c9c26cf341c1c88578cd6a87a3c01f6a73d6d6d6d6dc8000000000000001976a914010b7bfa8ff585b6d95f8a18a998cbd87508bd4188aca9010000000000001976a914254c6f1ce67d27ae4dafb03d2ba08318df2883c588ac0000000000'
1142
+ const beef27c8f1 = '0100beef01feafcf0c000802020044ef36fc6c79af360b13f2164c6e772e6e5e2083a54d72894f34710fe6a68cf80302792c8c563b17721744d8f101d79e873aae62816d0fd47dd9d90ada219c1b252d010000059e83aae2cfc6a35477566439dd9d58969cc41a134864c671c9b844f2a409a60101004991c129bba819832d6dd315c3a2065bd31b1a60aff8192be335f83731641e690101002f87724a7387df82417902266d2a4fb2fee857451e963a352ce8acbb279aa9eb010100a9e9eb55d20cef840bacbdb26ad8d6e7680ef7653364830b1e770fa140ece6bb010100f0ecd03fff2df21b0034d331cade50152781be9052dcec54eb95c963ef1cdcbf0101006fdb2c01a266e761a32f53b3ad0fabbca31ae234d31d85d9af9bbd624b321c9b0101008108cad77aabf7b0ee371f42afd773cada534244535f7108c7590df2b9d5f8a2020100000006e9847b33c5d666ccee1dccd52235489d8d58bf432be1b8c0cc7c13af80f10703030000006b483045022100fe8a18e043d693246a56c9b32ff553bea89a0bbe389bc55e8244044dcd865de3022065245c30fbbcc6820e4761048ed28c587bf41ece1933dfb85b8fa8c3433d93504121022f2ac22e73a55aa4ff21e905f66959200642ae74716544f834a48af5aa53fb27ffffffff7e8823d7e3e5565fdb9e53c015ad5b63bc6dd6ee1a9be7f7a8e105c903783d45050000006b483045022100e6332a6459350ff2486a39b6d79cdc0b65b6d52cf898fc457e45880c0db01b2a02206e34126bc81e6f03c6de90278788d6b2be3a3f5d7bd85e6442bbea90b8a5e2394121029780a1a98be5ff17a3228feb830e3bc32395ebecc5cf6de08f7a5685faf66c07ffffffff7e8823d7e3e5565fdb9e53c015ad5b63bc6dd6ee1a9be7f7a8e105c903783d45040000006b483045022100dfba71c6c33d2366b2202ea136271fd2f3a26c0a7295f6dd6018fbe12fc48708022052387ca532247bbfffef4b076fd2fbbdebe85b3cf53c73004897cd07be5d1ad6412103f735eb69c9f0b3c90dd2798b709e12a5b3d176a4690bd2a31ec616af9ecd47b5ffffffff7e8823d7e3e5565fdb9e53c015ad5b63bc6dd6ee1a9be7f7a8e105c903783d45020000006b483045022100f941b74c5da1e7f0ce4d1c08745e4d6b8ef56656199505caeff4b74f54890cec02201eaa0d01112497c89ebe4818687c5f3305629a83abd3b0bf5135feee68f57ca0412103fb0ec51f9e6e45b213cb1deff921e3322624cfb6d4a4ba5dc699867cf163b736ffffffff7e8823d7e3e5565fdb9e53c015ad5b63bc6dd6ee1a9be7f7a8e105c903783d45030000006a47304402205a3a2848350fdc19cae73243bea16dfc1e8fbbd5cecde4b5c88a57790505cebb022043db51d04998d52ec394d0e945a76e9922dd63caffb882242bec2ced2a35c7f1412102bae3e2f47dceaed7db2a212e059a00674a0087e3395b92d9f12cdbefdbc84efaffffffffad49b6f41125ffadab03cb2dcc24caf2c897c2cd4aed6e0a3b5c003f2dde757a030000006b483045022100aa7aaec7fc6c5a2f4d5318b7fcbbca0f223c5c198f8fc0e1c8d59aee7155af1202207a4960fb58aa86eba12852f84fa3cdb4c427c7374fe737b3d5bcd9e2a6d76e3b412102b55cfd872e85b3dd424dcceafe01ed5a87dfb599d354ab7fc50d9ec48af77f6effffffff1a400d0300000000001976a914f182138abcb69c7c2339040605aa0a442cfec44288acc8000000000000001976a914e9d2276f468223a6d650672ab9d4c1271fb157e288acacb00000000000001976a914c0ea0208a097df8146933ae15f3ce545d5451e7388ac7a140000000000001976a9142d9ae3291f9393639fe2c38c9a7a948d136a10b488acea070000000000001976a91435d1c396b7ccb467118a41c2d77ebf79fb5fb7eb88ac17050000000000001976a9140dac1e2ddc975ee6d9f393e2ccd4876edb386cdd88ac51060000000000001976a91422878cb2f57a1ebe513815562003128f6e99eda188ac67040000000000001976a91439a752923ba1ef8b126ba7886646d58f54521c6588ac1e040000000000001976a914a939565af52d3fa974a040f42c35365d76b98f6a88acec030000000000001976a914c90823a53e29cafd35a225cc14e3a44eda143a5788ac05040000000000001976a914af6f8d6aaec3bf123efc17c8977bfad1ec1c859388acec030000000000001976a914d4c5e66a062e634be1c6f0eb1f2af0acebecb47988acf1030000000000001976a914a94a3e84bd740095ab39bed110cd3b3922c8595088ace8030000000000001976a914aa2e3c3bf6f3ee79f5af3b94eab2be60a394ce4c88aceb030000000000001976a91459394fa083ff3726571c79624dba3b38f14d924c88ace8030000000000001976a914726eb3ae6211711aa1624f3d767035b0b44abdc488ace8030000000000001976a9145f4680a099a6dc6891af3094aaab96aa66a6259888ace8030000000000001976a914f1a4fbd99e3923b0934abdb8839c337eb2fd50b888ace8030000000000001976a9147ed0ad307f5fe9993f984e7525c0012e3569594588ace8030000000000001976a91407f386bcdc4892cc2d0be3226b721ceaf04750b288ace8030000000000001976a914b51edaaa3c92d0af94109d952c7ddf13aa345f9988ace8030000000000001976a914018f4e02bd74f17a75f5b3965db4633ab514c57588ace8030000000000001976a914cc8d1f7ba5406b81efbc23da0fcccb4d3fe5b40a88ace8030000000000001976a914db1d4b7bd3e62f535dc3f2c8d0a989b000fa736d88ace8030000000000001976a914b8ea69358da7bea9506b15db6fc6597b3849b0a188ace8030000000000001976a914ecd769fdd67e04786e871c50c1a10504861bd79488ac0000000001000100000001792c8c563b17721744d8f101d79e873aae62816d0fd47dd9d90ada219c1b252d070000006b48304502210090368b5925795abb0415a3cd6265d7abae7d3541fb7f2fdb1fdffdd916b3fce3022068136da44df8ee367807c7c59c950ea8d428adfe142268cd6371cf4c1b4c3208412102858ea1804708d1e16e77b49ba1559b3742797f5f43efd3922fa3d21557521d7cffffffff03f401000000000000fde10121026afd108ddbc05e093207ebdf06557a3d66e15b96accbf1766c2e7d6efa3d1981ac22313852713463583334356b6a7461734467764b41455a435135564c5a72516f76786d2081d846bcae65ce281829b6d81eff3862a68b8eaf4e99ad97c0c4ad2e149631c020f936ace2d2b4034710dec819e343f69ddbb1a16d721b88413b697c2475f75f504c4e8470062e6966cb7ec80864e5e1217ecf2b3f113bb5307e1ec1d338678e54b426a11a267e990f7dcd85072c7b3870fabb54812d7f8a7aaa672ce01e4f8a2c6a1d532849877329bd23be0b3383fcb437ffd1ff05aad824343bfe5bae52d5775ed99ba9d5a72038dd50f81153e674a8b1c5d659aac361e660cc5657b291eda149de2505efb0bbac0a353331393633353238334c797f3040f01934bb25733d4700a0309194a7defd8ceea7a8a59ad279c6237e2917264a115773777c5746546288b15ec1cf6788b801dd3952fec804cadbba2ff4e05464735e2db83c3509b59119efc843716c34becd2884f011f2e7f61f382237891875115b6ea5bac1c3e9ca6c575b44547d589c38c1b5a8cadb463044022016e1fdb226b465f3151aaa1e033b122dd58f3b495d2846d532f784ec8f6fc9ae022031dc12155c02e7f588c9f6d975a79c9c26cf341c1c88578cd6a87a3c01f6a73d6d6d6d6dc8000000000000001976a914010b7bfa8ff585b6d95f8a18a998cbd87508bd4188aca9010000000000001976a914254c6f1ce67d27ae4dafb03d2ba08318df2883c588ac0000000000'
1141
1143
  // beef27c8f_0 is 0 levels deep, i.e. tx has merklePath
1142
- const beef27c8f_0 = '0100beef01fef4f10c000902fd020100ab2d9b3bbfc2ecf5c834f7719bf10dcabef31cfa3b78fa714bd3a2b3958fc6b5fd0301029515629d935d81e704ca97b8dc02a698c39d78f06bbb3d8d46bcaa5178f3c827018000fc94b1da08b5d0850afcc4948a9e129a2c2c0bb629090ca84fef5d326ceadbc2014100fa78356192a22189dd3c2a03bfb2e043667a55a85386a8d0f0853bbaa54ba748012100e37888006acc37cbfc6a459117c3e957b00e8ff3a90d93bc9aaa3fc9d972453701110052564e444cbe4b1f24015d91f5cfd7eff63e5f403613a7b48e77efa32761f950010900acef65445ef232f1fcb527bab67fccc90ae9d0c610a4f752d84c5e10a3415d74010500e1fadd176551150808ae96ff2b4dff487c432788d23db25fd3fb5e8b3e16b96f0103003e530f146a992ed9378807fa43bc1e6033de0711d728ee3a0ea1507b5ffff2940100006c8eca8a9d680ab2ae4dc1f0e247f282de3f32b99175e6f9ddb4f8e76ae0bf1b010100000001792c8c563b17721744d8f101d79e873aae62816d0fd47dd9d90ada219c1b252d070000006b48304502210090368b5925795abb0415a3cd6265d7abae7d3541fb7f2fdb1fdffdd916b3fce3022068136da44df8ee367807c7c59c950ea8d428adfe142268cd6371cf4c1b4c3208412102858ea1804708d1e16e77b49ba1559b3742797f5f43efd3922fa3d21557521d7cffffffff03f401000000000000fde10121026afd108ddbc05e093207ebdf06557a3d66e15b96accbf1766c2e7d6efa3d1981ac22313852713463583334356b6a7461734467764b41455a435135564c5a72516f76786d2081d846bcae65ce281829b6d81eff3862a68b8eaf4e99ad97c0c4ad2e149631c020f936ace2d2b4034710dec819e343f69ddbb1a16d721b88413b697c2475f75f504c4e8470062e6966cb7ec80864e5e1217ecf2b3f113bb5307e1ec1d338678e54b426a11a267e990f7dcd85072c7b3870fabb54812d7f8a7aaa672ce01e4f8a2c6a1d532849877329bd23be0b3383fcb437ffd1ff05aad824343bfe5bae52d5775ed99ba9d5a72038dd50f81153e674a8b1c5d659aac361e660cc5657b291eda149de2505efb0bbac0a353331393633353238334c797f3040f01934bb25733d4700a0309194a7defd8ceea7a8a59ad279c6237e2917264a115773777c5746546288b15ec1cf6788b801dd3952fec804cadbba2ff4e05464735e2db83c3509b59119efc843716c34becd2884f011f2e7f61f382237891875115b6ea5bac1c3e9ca6c575b44547d589c38c1b5a8cadb463044022016e1fdb226b465f3151aaa1e033b122dd58f3b495d2846d532f784ec8f6fc9ae022031dc12155c02e7f588c9f6d975a79c9c26cf341c1c88578cd6a87a3c01f6a73d6d6d6d6dc8000000000000001976a914010b7bfa8ff585b6d95f8a18a998cbd87508bd4188aca9010000000000001976a914254c6f1ce67d27ae4dafb03d2ba08318df2883c588ac000000000100'
1144
+ const beef27c8f0 = '0100beef01fef4f10c000902fd020100ab2d9b3bbfc2ecf5c834f7719bf10dcabef31cfa3b78fa714bd3a2b3958fc6b5fd0301029515629d935d81e704ca97b8dc02a698c39d78f06bbb3d8d46bcaa5178f3c827018000fc94b1da08b5d0850afcc4948a9e129a2c2c0bb629090ca84fef5d326ceadbc2014100fa78356192a22189dd3c2a03bfb2e043667a55a85386a8d0f0853bbaa54ba748012100e37888006acc37cbfc6a459117c3e957b00e8ff3a90d93bc9aaa3fc9d972453701110052564e444cbe4b1f24015d91f5cfd7eff63e5f403613a7b48e77efa32761f950010900acef65445ef232f1fcb527bab67fccc90ae9d0c610a4f752d84c5e10a3415d74010500e1fadd176551150808ae96ff2b4dff487c432788d23db25fd3fb5e8b3e16b96f0103003e530f146a992ed9378807fa43bc1e6033de0711d728ee3a0ea1507b5ffff2940100006c8eca8a9d680ab2ae4dc1f0e247f282de3f32b99175e6f9ddb4f8e76ae0bf1b010100000001792c8c563b17721744d8f101d79e873aae62816d0fd47dd9d90ada219c1b252d070000006b48304502210090368b5925795abb0415a3cd6265d7abae7d3541fb7f2fdb1fdffdd916b3fce3022068136da44df8ee367807c7c59c950ea8d428adfe142268cd6371cf4c1b4c3208412102858ea1804708d1e16e77b49ba1559b3742797f5f43efd3922fa3d21557521d7cffffffff03f401000000000000fde10121026afd108ddbc05e093207ebdf06557a3d66e15b96accbf1766c2e7d6efa3d1981ac22313852713463583334356b6a7461734467764b41455a435135564c5a72516f76786d2081d846bcae65ce281829b6d81eff3862a68b8eaf4e99ad97c0c4ad2e149631c020f936ace2d2b4034710dec819e343f69ddbb1a16d721b88413b697c2475f75f504c4e8470062e6966cb7ec80864e5e1217ecf2b3f113bb5307e1ec1d338678e54b426a11a267e990f7dcd85072c7b3870fabb54812d7f8a7aaa672ce01e4f8a2c6a1d532849877329bd23be0b3383fcb437ffd1ff05aad824343bfe5bae52d5775ed99ba9d5a72038dd50f81153e674a8b1c5d659aac361e660cc5657b291eda149de2505efb0bbac0a353331393633353238334c797f3040f01934bb25733d4700a0309194a7defd8ceea7a8a59ad279c6237e2917264a115773777c5746546288b15ec1cf6788b801dd3952fec804cadbba2ff4e05464735e2db83c3509b59119efc843716c34becd2884f011f2e7f61f382237891875115b6ea5bac1c3e9ca6c575b44547d589c38c1b5a8cadb463044022016e1fdb226b465f3151aaa1e033b122dd58f3b495d2846d532f784ec8f6fc9ae022031dc12155c02e7f588c9f6d975a79c9c26cf341c1c88578cd6a87a3c01f6a73d6d6d6d6dc8000000000000001976a914010b7bfa8ff585b6d95f8a18a998cbd87508bd4188aca9010000000000001976a914254c6f1ce67d27ae4dafb03d2ba08318df2883c588ac000000000100'
1143
1145
  // full txid
1144
1146
  const txid27c8f = '27c8f37851aabc468d3dbb6bf0789dc398a602dcb897ca04e7815d939d621595'
1145
1147
 
@@ -1153,13 +1155,13 @@ const txid27c8f = '27c8f37851aabc468d3dbb6bf0789dc398a602dcb897ca04e7815d939d621
1153
1155
  txid a7d4cac391f5b3d8dedaacb3cd8b5afac54df2d8e043ae0452abcfe190686494 output 17 1132 sats
1154
1156
  */
1155
1157
  const txid17d182 = '17d1829ba8424b97369ee8b528ee8b65d9d4b9c08d037d224a7be7c025f78f78'
1156
- const beef17d182_0 = '0100beef01feaff60c000c02fd660302788ff725c0e77b4a227d038dc0b9d4d9658bee28b5e89e36974b42a89b82d117fd670300fe2b9c31ae10a15803f91f63df0cf6a5b6771db9dd831e5bc37a36460a1ffa0701fdb201009bf0e3a5854c4b4c8d62c0ade2754ae1bbc0f16a3e49251ed964486b8dc387f101d80003ae2ec810744139e9989e4f36d8f5ebb160981481e68647890d11e3dda971fa016d0081efcddc14858ead1f427f235d2b21c8de43e33bfeb155ed79f666dd4dd490c2013700a6a847f6e264cfa64c0b6aad6b74912c63c6fc89728be813d104abd2724b98a5011a0062c7d393b9518b05d2d0891ae8c2cd840c93e051d2944bb8e651c976301c4e50010c008d8ee79c1a042cf43f4dcd880aeb4ff7f54552cd3a4c9a40c72cc16ed6847cee01070050194d11d12eddf5e56926b28778f0f64ad54e79eb4df4ce7834ddfc57bf882d010200bd66a0cdbb0af933166931b1050dd108e02d76cff19afd614e681fcbdf1be0740100008b36364159630b47b4c27f6e1e056e820b0aa350ab5dde2520fc1753129b628c010100cab620ac48bb55de40c8c5ddb62bbc44c907fbf5d882326d427f4cd4b34334800101001163b76c2c6824c07a53af2ea86da1b1d946340d9377d3b9b84560e074aea3a6010100000001d75744af67fa32a56ba0256a818e23747133cca140926eb65b1eb00310202694000000006a473044022027137acdeb7583e966a9e8825c9f5edb909b2fd46d40e6ee83846d164529630e0220644e82a46d813fac2836650ce832cdba7eb46b6c134982b36e84f5f08a921dbb412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001ac040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac000000000100'
1157
- const beef17d182_4 = '0100beef01fedcee0c000d02f8020954d485defac354e5fc826434eaf6ee4f3a81969f0f7d104e7dd46871adab37f900fa225908f222f8f7f291731a10e93cdf410189ca01adc688edf5577552aa8ff2017d006cfea8ad6ace7ecce9bbbe729eb9dca25f9be064b150d290588c3364867ee112013f00bfbacc8bfdda7f734c160fcfee972dd9182aede73290367aab62b5ce242e5655011e007a0eb94f2bcf98489247a01551a843d9cf1e091522c32f50ce347ba5057b439d010e00beb857ba543d2bd199e68c63a480c73b83660eca27d2ce105355741f27017876010600d5fb1ea8440e41b4ffde093dd53d6ed741c4fe4fe66ff43f6a4eb9af3c6ddebb010200169db3be130a9e8aff1dbff39abee477d3a224cb3e118c39794bb7ba427b707501000083cd0ce2d2734835c45331cd06f38556cf4e07634d599f59692ad9980175a7f201010022f4a629e99ca5838f56f87b050bdefa9b9f621c08745cb27b941d9c6750d99a01010032b90a4148ff984b0efee309a578cb87d278142c014f1a7fd5351b3e3cd4c7da01010082dc3ea570250357fca88652ee1628928136c29eb4910b7e5627e0ffef1d958a010100f5194f41c28960f0dba669f4715229f59f59afb5d55f50a2a7556111b8da67130101009cd7f9a65014cc6fd5c0b71391d0b70eb983ea3b03a2fbd7a13150775855953405010000000237c2148f306355078cedfe61c8574f393476a43abd2cee5b31f1c73e39db86d7000000006a47304402207ee089c64fa810ad88e03f7bad4ae57c9b7de215d222a5bf52f9259d171b717a0220643d3cb4423b5b7ffcb7b2b59eb81dede0a25319aaa0228d27e59d5e238c35a2412102641b5a033ce332a9fc0f81dee7948c099b194758d8352bd5572988ac760ae2faffffffff94646890e1cfab5204ae43e0d8f24dc5fa5a8bcdb3acdaded8b3f591c3cad4a7110000006a47304402205b92eaba4a6c029385e9cf291631b98d0c7aa6851755fb44279e2b1ebd12759702206e96f3bac252f2c5cf5edf7f8828d6358baaa9fe34aa163faeb9b31aac830e47412102577e2a3877a253d9e7587c3d77a4038b4af5e51113393c2713c6d50c9da5de6effffffff03b0040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88acc8000000000000001976a914292e61672fc24a9a70cfa6f73a9af5655652629d88ac13050000000000001976a914ad2ab55d09559074e41e2a2c1c89742e13bee03088ac00000000010001000000010954d485defac354e5fc826434eaf6ee4f3a81969f0f7d104e7dd46871adab37000000006b4830450221009c67deb38aa6b5ed8bfa140e9464e1b1ee2908a296bb70e3d164725254491c6a02207b595dcced1136c276edfc1e5e5901279cf6f0a6432e4c1a908468262e7d9543412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001af040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac00000000000100000001c5b2a279b9b1fec42846b9d6ed7cf6345b08dcdd4b4a17e2d017b98edb347787000000006b483045022100c0992a684be99a07bc87e03bf88701acefc033c1aa57cf4a3b15253e64c8962a022031d5b3af1684a1a1a201eb16fc1fbca8522ae16b9c0d340e8bec48eeea1fe458412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001ae040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac00000000000100000001e9d6dc3084dca9606fe43406208a8dcbba568f60671ee2df4f50189cf75e9f50000000006b483045022100c9c8baa777fe09c1310788614d4eba88b6027d39eada18a5a79c6a33e55919c302205222961b1f63dddd425cd418571797cb3a68aa59ac37331c0fc61fc9de66c34d412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001ad040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac00000000000100000001d75744af67fa32a56ba0256a818e23747133cca140926eb65b1eb00310202694000000006a473044022027137acdeb7583e966a9e8825c9f5edb909b2fd46d40e6ee83846d164529630e0220644e82a46d813fac2836650ce832cdba7eb46b6c134982b36e84f5f08a921dbb412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001ac040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac0000000000'
1158
+ const beef17d1820 = '0100beef01feaff60c000c02fd660302788ff725c0e77b4a227d038dc0b9d4d9658bee28b5e89e36974b42a89b82d117fd670300fe2b9c31ae10a15803f91f63df0cf6a5b6771db9dd831e5bc37a36460a1ffa0701fdb201009bf0e3a5854c4b4c8d62c0ade2754ae1bbc0f16a3e49251ed964486b8dc387f101d80003ae2ec810744139e9989e4f36d8f5ebb160981481e68647890d11e3dda971fa016d0081efcddc14858ead1f427f235d2b21c8de43e33bfeb155ed79f666dd4dd490c2013700a6a847f6e264cfa64c0b6aad6b74912c63c6fc89728be813d104abd2724b98a5011a0062c7d393b9518b05d2d0891ae8c2cd840c93e051d2944bb8e651c976301c4e50010c008d8ee79c1a042cf43f4dcd880aeb4ff7f54552cd3a4c9a40c72cc16ed6847cee01070050194d11d12eddf5e56926b28778f0f64ad54e79eb4df4ce7834ddfc57bf882d010200bd66a0cdbb0af933166931b1050dd108e02d76cff19afd614e681fcbdf1be0740100008b36364159630b47b4c27f6e1e056e820b0aa350ab5dde2520fc1753129b628c010100cab620ac48bb55de40c8c5ddb62bbc44c907fbf5d882326d427f4cd4b34334800101001163b76c2c6824c07a53af2ea86da1b1d946340d9377d3b9b84560e074aea3a6010100000001d75744af67fa32a56ba0256a818e23747133cca140926eb65b1eb00310202694000000006a473044022027137acdeb7583e966a9e8825c9f5edb909b2fd46d40e6ee83846d164529630e0220644e82a46d813fac2836650ce832cdba7eb46b6c134982b36e84f5f08a921dbb412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001ac040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac000000000100'
1159
+ const beef17d1824 = '0100beef01fedcee0c000d02f8020954d485defac354e5fc826434eaf6ee4f3a81969f0f7d104e7dd46871adab37f900fa225908f222f8f7f291731a10e93cdf410189ca01adc688edf5577552aa8ff2017d006cfea8ad6ace7ecce9bbbe729eb9dca25f9be064b150d290588c3364867ee112013f00bfbacc8bfdda7f734c160fcfee972dd9182aede73290367aab62b5ce242e5655011e007a0eb94f2bcf98489247a01551a843d9cf1e091522c32f50ce347ba5057b439d010e00beb857ba543d2bd199e68c63a480c73b83660eca27d2ce105355741f27017876010600d5fb1ea8440e41b4ffde093dd53d6ed741c4fe4fe66ff43f6a4eb9af3c6ddebb010200169db3be130a9e8aff1dbff39abee477d3a224cb3e118c39794bb7ba427b707501000083cd0ce2d2734835c45331cd06f38556cf4e07634d599f59692ad9980175a7f201010022f4a629e99ca5838f56f87b050bdefa9b9f621c08745cb27b941d9c6750d99a01010032b90a4148ff984b0efee309a578cb87d278142c014f1a7fd5351b3e3cd4c7da01010082dc3ea570250357fca88652ee1628928136c29eb4910b7e5627e0ffef1d958a010100f5194f41c28960f0dba669f4715229f59f59afb5d55f50a2a7556111b8da67130101009cd7f9a65014cc6fd5c0b71391d0b70eb983ea3b03a2fbd7a13150775855953405010000000237c2148f306355078cedfe61c8574f393476a43abd2cee5b31f1c73e39db86d7000000006a47304402207ee089c64fa810ad88e03f7bad4ae57c9b7de215d222a5bf52f9259d171b717a0220643d3cb4423b5b7ffcb7b2b59eb81dede0a25319aaa0228d27e59d5e238c35a2412102641b5a033ce332a9fc0f81dee7948c099b194758d8352bd5572988ac760ae2faffffffff94646890e1cfab5204ae43e0d8f24dc5fa5a8bcdb3acdaded8b3f591c3cad4a7110000006a47304402205b92eaba4a6c029385e9cf291631b98d0c7aa6851755fb44279e2b1ebd12759702206e96f3bac252f2c5cf5edf7f8828d6358baaa9fe34aa163faeb9b31aac830e47412102577e2a3877a253d9e7587c3d77a4038b4af5e51113393c2713c6d50c9da5de6effffffff03b0040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88acc8000000000000001976a914292e61672fc24a9a70cfa6f73a9af5655652629d88ac13050000000000001976a914ad2ab55d09559074e41e2a2c1c89742e13bee03088ac00000000010001000000010954d485defac354e5fc826434eaf6ee4f3a81969f0f7d104e7dd46871adab37000000006b4830450221009c67deb38aa6b5ed8bfa140e9464e1b1ee2908a296bb70e3d164725254491c6a02207b595dcced1136c276edfc1e5e5901279cf6f0a6432e4c1a908468262e7d9543412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001af040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac00000000000100000001c5b2a279b9b1fec42846b9d6ed7cf6345b08dcdd4b4a17e2d017b98edb347787000000006b483045022100c0992a684be99a07bc87e03bf88701acefc033c1aa57cf4a3b15253e64c8962a022031d5b3af1684a1a1a201eb16fc1fbca8522ae16b9c0d340e8bec48eeea1fe458412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001ae040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac00000000000100000001e9d6dc3084dca9606fe43406208a8dcbba568f60671ee2df4f50189cf75e9f50000000006b483045022100c9c8baa777fe09c1310788614d4eba88b6027d39eada18a5a79c6a33e55919c302205222961b1f63dddd425cd418571797cb3a68aa59ac37331c0fc61fc9de66c34d412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001ad040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac00000000000100000001d75744af67fa32a56ba0256a818e23747133cca140926eb65b1eb00310202694000000006a473044022027137acdeb7583e966a9e8825c9f5edb909b2fd46d40e6ee83846d164529630e0220644e82a46d813fac2836650ce832cdba7eb46b6c134982b36e84f5f08a921dbb412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001ac040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac0000000000'
1158
1160
  const txid942620 = '9426201003b01e5bb66e9240a1cc337174238e816a25a06ba532fa67af4457d7'
1159
- const beef942620_0 = '0100beef01fe07ef0c0008022802d75744af67fa32a56ba0256a818e23747133cca140926eb65b1eb00310202694290089598873694014f9706b6074a1b349fcf59df875e58a0cf3a8900c6e5830f33601150036fffb4b882171c594a27d37c5d7827cc706811c3e0acd152e0f3ee1c7ac98f9010b002b3b5c611cbea3bbdf9e99646cb23e80b62fc2dc2e619656157296878d46d8b30104002877b6b97c95fd55c0584d0ccf683c9ad6c60be146dd73605b93ef042da648a0010300d1d01ac14f19d974f8131b9cdb38ae2abcf5403d5c3f0d2772a1c995c136356b01000017ece09932fa7c6d464c633df200003c2328beb26290c7b9b1333eb84c90c544010100cd4147f92f226bc39354a78209db1f521ffd8720e3fcc9c942ee1dd535513d0f0101005daeb3f84528a58700c58c8479f29cd627b07c198602b6c08033340262c0c607010100000001e9d6dc3084dca9606fe43406208a8dcbba568f60671ee2df4f50189cf75e9f50000000006b483045022100c9c8baa777fe09c1310788614d4eba88b6027d39eada18a5a79c6a33e55919c302205222961b1f63dddd425cd418571797cb3a68aa59ac37331c0fc61fc9de66c34d412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001ad040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac000000000100'
1161
+ const beef9426200 = '0100beef01fe07ef0c0008022802d75744af67fa32a56ba0256a818e23747133cca140926eb65b1eb00310202694290089598873694014f9706b6074a1b349fcf59df875e58a0cf3a8900c6e5830f33601150036fffb4b882171c594a27d37c5d7827cc706811c3e0acd152e0f3ee1c7ac98f9010b002b3b5c611cbea3bbdf9e99646cb23e80b62fc2dc2e619656157296878d46d8b30104002877b6b97c95fd55c0584d0ccf683c9ad6c60be146dd73605b93ef042da648a0010300d1d01ac14f19d974f8131b9cdb38ae2abcf5403d5c3f0d2772a1c995c136356b01000017ece09932fa7c6d464c633df200003c2328beb26290c7b9b1333eb84c90c544010100cd4147f92f226bc39354a78209db1f521ffd8720e3fcc9c942ee1dd535513d0f0101005daeb3f84528a58700c58c8479f29cd627b07c198602b6c08033340262c0c607010100000001e9d6dc3084dca9606fe43406208a8dcbba568f60671ee2df4f50189cf75e9f50000000006b483045022100c9c8baa777fe09c1310788614d4eba88b6027d39eada18a5a79c6a33e55919c302205222961b1f63dddd425cd418571797cb3a68aa59ac37331c0fc61fc9de66c34d412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001ad040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac000000000100'
1160
1162
  const txid509f5e = '509f5ef79c18504fdfe21e67608f56bacb8d8a200634e46f60a9dc8430dcd6e9'
1161
- const beef509f5e_0 = '0100beef01fe05ef0c0009029800fb09f12f5632f7af6409432fc914d1e68fc499270fa29f3b3c43fb00ebeb8ed09902e9d6dc3084dca9606fe43406208a8dcbba568f60671ee2df4f50189cf75e9f50014d00ccb4f6ea2fabdfaf86b0e3837cc702ddde19cb5112e680f00832330b03be1d290127000480365544cd4dca6262cc25567f241d650339773f3f9ef1b9a25b9a034945c30112004ab6bdfecf74af1da1e0574671d06aef63dcde80133d1a6c46beda2ad08abc15010800ce75abf88ee3de3e2f89be0462e1cb0b9470b67947b9880e941bb6565a44bc86010500940d69edc69fc8694485452b961b8df8a2e0c1ff7bdbfeee1e6855542a7e7cd10103002217abcbd7e5431fffc0f52624ee3cf659e39734ae5335d5d196193f7f6e39ab01000082d3a59ca910a1c6fc1f06ab2a5ea80ffee7a39d9a987c85701cfaaf604219f80101008bed85ed818e9d69147000e8687b2efed77e5ee0073e5a2640033715747bd0c2010100000001c5b2a279b9b1fec42846b9d6ed7cf6345b08dcdd4b4a17e2d017b98edb347787000000006b483045022100c0992a684be99a07bc87e03bf88701acefc033c1aa57cf4a3b15253e64c8962a022031d5b3af1684a1a1a201eb16fc1fbca8522ae16b9c0d340e8bec48eeea1fe458412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001ae040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac000000000100'
1163
+ const beef509f5e0 = '0100beef01fe05ef0c0009029800fb09f12f5632f7af6409432fc914d1e68fc499270fa29f3b3c43fb00ebeb8ed09902e9d6dc3084dca9606fe43406208a8dcbba568f60671ee2df4f50189cf75e9f50014d00ccb4f6ea2fabdfaf86b0e3837cc702ddde19cb5112e680f00832330b03be1d290127000480365544cd4dca6262cc25567f241d650339773f3f9ef1b9a25b9a034945c30112004ab6bdfecf74af1da1e0574671d06aef63dcde80133d1a6c46beda2ad08abc15010800ce75abf88ee3de3e2f89be0462e1cb0b9470b67947b9880e941bb6565a44bc86010500940d69edc69fc8694485452b961b8df8a2e0c1ff7bdbfeee1e6855542a7e7cd10103002217abcbd7e5431fffc0f52624ee3cf659e39734ae5335d5d196193f7f6e39ab01000082d3a59ca910a1c6fc1f06ab2a5ea80ffee7a39d9a987c85701cfaaf604219f80101008bed85ed818e9d69147000e8687b2efed77e5ee0073e5a2640033715747bd0c2010100000001c5b2a279b9b1fec42846b9d6ed7cf6345b08dcdd4b4a17e2d017b98edb347787000000006b483045022100c0992a684be99a07bc87e03bf88701acefc033c1aa57cf4a3b15253e64c8962a022031d5b3af1684a1a1a201eb16fc1fbca8522ae16b9c0d340e8bec48eeea1fe458412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001ae040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac000000000100'
1162
1164
  const txid877734 = '877734db8eb917d0e2174a4bdddc085b34f67cedd6b94628c4feb1b979a2b2c5'
1163
- const beef877734_0 = '0100beef01fedfee0c000e02fd202900eeebd665d8893249c90dabbd50266fb7e0f125dd8ee087a3c8089debf09499d4fd212902c5b2a279b9b1fec42846b9d6ed7cf6345b08dcdd4b4a17e2d017b98edb34778701fd911400b32e903076f6869d4e0a9fa2d00f5c8bad598d3ede5b4bafef88debdb1d3de6901fd490a000a79573c8eb909d30c4e9653f90ea2869e44220d551da81cc96913b2fcd734aa01fd2505003d6db6b39d889be1535a478da0c3428e8107872ac55f0fcfdd2225b6a402d1dc01fd9302008ae31e7e4d4e766814ea11b337b5b51f5d807737f802c44c8d0d75a583a61bde01fd480100e962a171fcc0e79bf55b1433d79564f8d3b2622ecbcb84c9ede792d353e0afb901a5007d0a8d8a3a2f0eb209e82f5e80ef6c4835b7d07c85d6ac8c613cc8754a47bd79015300b070576d92310f1e08a12581ecea4d1d4de48beb15ff7e42eddc1b957fa2909d012800544a58cac6b4dd78cebd84178dc1572693552fec5777a055dad0068bd67aec6101150025674a887fff026640fe9851cbffb37bd8e02780c4f6c0476ed3cee4e4884c2f010b0023aa2b01e8a59c3b79976918d95ae6a044ace2264e3690f9846f452499c0cbad01040078033c2619a3b115bfc71d09addf13e288660be1841cf6769ffb9924aca246970103004d2d6ae3e2c02703b773c49af789b7372b5b71bf92b49e2dc4b921880df984540100003b86537a85e5047596a482ac2dba30d1798b26d200c08a2fb19d8d75b2c2a1b50101000000010954d485defac354e5fc826434eaf6ee4f3a81969f0f7d104e7dd46871adab37000000006b4830450221009c67deb38aa6b5ed8bfa140e9464e1b1ee2908a296bb70e3d164725254491c6a02207b595dcced1136c276edfc1e5e5901279cf6f0a6432e4c1a908468262e7d9543412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001af040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac000000000100'
1165
+ const beef8777340 = '0100beef01fedfee0c000e02fd202900eeebd665d8893249c90dabbd50266fb7e0f125dd8ee087a3c8089debf09499d4fd212902c5b2a279b9b1fec42846b9d6ed7cf6345b08dcdd4b4a17e2d017b98edb34778701fd911400b32e903076f6869d4e0a9fa2d00f5c8bad598d3ede5b4bafef88debdb1d3de6901fd490a000a79573c8eb909d30c4e9653f90ea2869e44220d551da81cc96913b2fcd734aa01fd2505003d6db6b39d889be1535a478da0c3428e8107872ac55f0fcfdd2225b6a402d1dc01fd9302008ae31e7e4d4e766814ea11b337b5b51f5d807737f802c44c8d0d75a583a61bde01fd480100e962a171fcc0e79bf55b1433d79564f8d3b2622ecbcb84c9ede792d353e0afb901a5007d0a8d8a3a2f0eb209e82f5e80ef6c4835b7d07c85d6ac8c613cc8754a47bd79015300b070576d92310f1e08a12581ecea4d1d4de48beb15ff7e42eddc1b957fa2909d012800544a58cac6b4dd78cebd84178dc1572693552fec5777a055dad0068bd67aec6101150025674a887fff026640fe9851cbffb37bd8e02780c4f6c0476ed3cee4e4884c2f010b0023aa2b01e8a59c3b79976918d95ae6a044ace2264e3690f9846f452499c0cbad01040078033c2619a3b115bfc71d09addf13e288660be1841cf6769ffb9924aca246970103004d2d6ae3e2c02703b773c49af789b7372b5b71bf92b49e2dc4b921880df984540100003b86537a85e5047596a482ac2dba30d1798b26d200c08a2fb19d8d75b2c2a1b50101000000010954d485defac354e5fc826434eaf6ee4f3a81969f0f7d104e7dd46871adab37000000006b4830450221009c67deb38aa6b5ed8bfa140e9464e1b1ee2908a296bb70e3d164725254491c6a02207b595dcced1136c276edfc1e5e5901279cf6f0a6432e4c1a908468262e7d9543412102b963cd9b5a1419c041a4c4ac46f5205a2b8cb691cd5633e5d498ddcf3d37460b0000000001af040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88ac000000000100'
1164
1166
  const txid37abad = '37abad7168d47d4e107d0f9f96813a4feef6ea346482fce554c3fade85d45409'
1165
- const beef37abad_0 = '0100beef01fedcee0c000d02f8020954d485defac354e5fc826434eaf6ee4f3a81969f0f7d104e7dd46871adab37f900fa225908f222f8f7f291731a10e93cdf410189ca01adc688edf5577552aa8ff2017d006cfea8ad6ace7ecce9bbbe729eb9dca25f9be064b150d290588c3364867ee112013f00bfbacc8bfdda7f734c160fcfee972dd9182aede73290367aab62b5ce242e5655011e007a0eb94f2bcf98489247a01551a843d9cf1e091522c32f50ce347ba5057b439d010e00beb857ba543d2bd199e68c63a480c73b83660eca27d2ce105355741f27017876010600d5fb1ea8440e41b4ffde093dd53d6ed741c4fe4fe66ff43f6a4eb9af3c6ddebb010200169db3be130a9e8aff1dbff39abee477d3a224cb3e118c39794bb7ba427b707501000083cd0ce2d2734835c45331cd06f38556cf4e07634d599f59692ad9980175a7f201010022f4a629e99ca5838f56f87b050bdefa9b9f621c08745cb27b941d9c6750d99a01010032b90a4148ff984b0efee309a578cb87d278142c014f1a7fd5351b3e3cd4c7da01010082dc3ea570250357fca88652ee1628928136c29eb4910b7e5627e0ffef1d958a010100f5194f41c28960f0dba669f4715229f59f59afb5d55f50a2a7556111b8da67130101009cd7f9a65014cc6fd5c0b71391d0b70eb983ea3b03a2fbd7a13150775855953401010000000237c2148f306355078cedfe61c8574f393476a43abd2cee5b31f1c73e39db86d7000000006a47304402207ee089c64fa810ad88e03f7bad4ae57c9b7de215d222a5bf52f9259d171b717a0220643d3cb4423b5b7ffcb7b2b59eb81dede0a25319aaa0228d27e59d5e238c35a2412102641b5a033ce332a9fc0f81dee7948c099b194758d8352bd5572988ac760ae2faffffffff94646890e1cfab5204ae43e0d8f24dc5fa5a8bcdb3acdaded8b3f591c3cad4a7110000006a47304402205b92eaba4a6c029385e9cf291631b98d0c7aa6851755fb44279e2b1ebd12759702206e96f3bac252f2c5cf5edf7f8828d6358baaa9fe34aa163faeb9b31aac830e47412102577e2a3877a253d9e7587c3d77a4038b4af5e51113393c2713c6d50c9da5de6effffffff03b0040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88acc8000000000000001976a914292e61672fc24a9a70cfa6f73a9af5655652629d88ac13050000000000001976a914ad2ab55d09559074e41e2a2c1c89742e13bee03088ac000000000100'
1167
+ const beef37abad0 = '0100beef01fedcee0c000d02f8020954d485defac354e5fc826434eaf6ee4f3a81969f0f7d104e7dd46871adab37f900fa225908f222f8f7f291731a10e93cdf410189ca01adc688edf5577552aa8ff2017d006cfea8ad6ace7ecce9bbbe729eb9dca25f9be064b150d290588c3364867ee112013f00bfbacc8bfdda7f734c160fcfee972dd9182aede73290367aab62b5ce242e5655011e007a0eb94f2bcf98489247a01551a843d9cf1e091522c32f50ce347ba5057b439d010e00beb857ba543d2bd199e68c63a480c73b83660eca27d2ce105355741f27017876010600d5fb1ea8440e41b4ffde093dd53d6ed741c4fe4fe66ff43f6a4eb9af3c6ddebb010200169db3be130a9e8aff1dbff39abee477d3a224cb3e118c39794bb7ba427b707501000083cd0ce2d2734835c45331cd06f38556cf4e07634d599f59692ad9980175a7f201010022f4a629e99ca5838f56f87b050bdefa9b9f621c08745cb27b941d9c6750d99a01010032b90a4148ff984b0efee309a578cb87d278142c014f1a7fd5351b3e3cd4c7da01010082dc3ea570250357fca88652ee1628928136c29eb4910b7e5627e0ffef1d958a010100f5194f41c28960f0dba669f4715229f59f59afb5d55f50a2a7556111b8da67130101009cd7f9a65014cc6fd5c0b71391d0b70eb983ea3b03a2fbd7a13150775855953401010000000237c2148f306355078cedfe61c8574f393476a43abd2cee5b31f1c73e39db86d7000000006a47304402207ee089c64fa810ad88e03f7bad4ae57c9b7de215d222a5bf52f9259d171b717a0220643d3cb4423b5b7ffcb7b2b59eb81dede0a25319aaa0228d27e59d5e238c35a2412102641b5a033ce332a9fc0f81dee7948c099b194758d8352bd5572988ac760ae2faffffffff94646890e1cfab5204ae43e0d8f24dc5fa5a8bcdb3acdaded8b3f591c3cad4a7110000006a47304402205b92eaba4a6c029385e9cf291631b98d0c7aa6851755fb44279e2b1ebd12759702206e96f3bac252f2c5cf5edf7f8828d6358baaa9fe34aa163faeb9b31aac830e47412102577e2a3877a253d9e7587c3d77a4038b4af5e51113393c2713c6d50c9da5de6effffffff03b0040000000000001976a914ae0545a670c6f739f9a8963aee37be95df0d9b2a88acc8000000000000001976a914292e61672fc24a9a70cfa6f73a9af5655652629d88ac13050000000000001976a914ad2ab55d09559074e41e2a2c1c89742e13bee03088ac000000000100'
@@ -22,7 +22,7 @@ describe('OverlayGASPRemote', () => {
22
22
  it('should send a request and return a valid response', async () => {
23
23
  const mockRequest: GASPInitialRequest = { version: 1, since: 0 }
24
24
  const mockResponse: GASPInitialResponse = {
25
- UTXOList: [{ txid: 'txid1', outputIndex: 0 }],
25
+ UTXOList: [{ txid: 'txid1', outputIndex: 0, score: 0 }],
26
26
  since: 1234567890
27
27
  };
28
28
 
@@ -30,7 +30,8 @@ describe('OverlayGASPStorage', () => {
30
30
 
31
31
  it('throws error when max nodes are exceeded', async () => {
32
32
  overlayStorage.maxNodesInGraph = 1
33
- overlayStorage.temporaryGraphNodeRefs['txid123.0'] = { txid: 'txid123', children: [] } as GraphNode
33
+ const graphNode: GraphNode = { txid: 'txid123', children: [], rawTx: '', graphID: 'txid4321.2', outputIndex: 0 }
34
+ overlayStorage.temporaryGraphNodeRefs['txid123.0'] = graphNode
34
35
 
35
36
  const mockTx = {
36
37
  rawTx: '334455',
@@ -44,12 +45,15 @@ describe('OverlayGASPStorage', () => {
44
45
 
45
46
  describe('findKnownUTXOs', () => {
46
47
  it('should return known UTXOs since a given timestamp', async () => {
47
- const mockUTXOs = [{ txid: 'txid1', outputIndex: 0 }, { txid: 'txid2', outputIndex: 1 }]
48
+ const mockUTXOs = [{ txid: 'txid1', outputIndex: 0, score: 0 }, { txid: 'txid2', outputIndex: 1, score: 0 }]
48
49
  mockEngine.storage.findUTXOsForTopic.mockResolvedValue(mockUTXOs)
49
50
 
50
51
  const result = await overlayStorage.findKnownUTXOs(1234567890)
51
52
 
52
- expect(result).toEqual(mockUTXOs)
53
+ expect(result).toEqual([
54
+ { txid: 'txid1', outputIndex: 0, score: 0 },
55
+ { txid: 'txid2', outputIndex: 1, score: 0 }
56
+ ])
53
57
  expect(mockEngine.storage.findUTXOsForTopic).toHaveBeenCalledWith('test-topic', 1234567890)
54
58
  })
55
59
 
@@ -125,32 +129,32 @@ describe('OverlayGASPStorage', () => {
125
129
 
126
130
  describe('discardGraph', () => {
127
131
  it('should discard the graph and its nodes', async () => {
128
- overlayStorage.temporaryGraphNodeRefs['txid123.0'] = {
132
+ const graphNode1: GraphNode = {
129
133
  txid: 'txid123',
130
- time: Date.now(),
131
134
  graphID: 'txid123.0',
132
135
  rawTx: 'rawTxData',
133
136
  outputIndex: 0,
134
137
  children: [],
135
138
  parent: undefined
136
- } as GraphNode
139
+ }
140
+ overlayStorage.temporaryGraphNodeRefs['txid123.0'] = graphNode1
137
141
 
138
- overlayStorage.temporaryGraphNodeRefs['txid124.0'] = {
142
+ const parentNode: GraphNode = {
143
+ txid: 'txid123',
144
+ graphID: 'txid123.0',
145
+ rawTx: 'rawTxData',
146
+ outputIndex: 0,
147
+ children: []
148
+ }
149
+ const graphNode2: GraphNode = {
139
150
  txid: 'txid124',
140
- time: Date.now(),
141
151
  graphID: 'txid123.0',
142
152
  rawTx: 'rawTxData',
143
153
  outputIndex: 1,
144
154
  children: [],
145
- parent: {
146
- txid: 'txid123',
147
- time: Date.now(),
148
- graphID: 'txid123.0',
149
- rawTx: 'rawTxData',
150
- outputIndex: 0,
151
- children: []
152
- } as GraphNode
153
- } as GraphNode
155
+ parent: parentNode
156
+ }
157
+ overlayStorage.temporaryGraphNodeRefs['txid124.0'] = graphNode2
154
158
 
155
159
  await overlayStorage.discardGraph('txid123.0')
156
160
 
@@ -38,10 +38,11 @@ export interface Storage {
38
38
  /**
39
39
  * Finds current UTXOs that have been admitted into a given topic
40
40
  * @param topic - The topic for which we want to find Unspent Transaction Outputs (UTXOs).
41
- * @param since - Optional parameter indicating the minimum date (timestamp? block height?) to retrieve matching UTXOs from. TODO: We need to decide about which time format to use for synchronization.
41
+ * @param since - Optional parameter indicating the minimum score value to retrieve matching UTXOs from. This is used for score-based filtering.
42
+ * @param limit - Optional parameter to limit the number of results returned
42
43
  * @returns A promise that resolves to an array of matching UTXOs.
43
44
  */
44
- findUTXOsForTopic: (topic: string, since?: number, includeBEEF?: boolean) => Promise<Output[]>
45
+ findUTXOsForTopic: (topic: string, since?: number, limit?: number, includeBEEF?: boolean) => Promise<Output[]>
45
46
 
46
47
  /**
47
48
  * Deletes an output from storage
@@ -99,4 +100,20 @@ export interface Storage {
99
100
  * @returns Whether the transaction is already applied
100
101
  */
101
102
  doesAppliedTransactionExist: (tx: AppliedTransaction) => Promise<boolean>
103
+
104
+ /**
105
+ * Updates the last interaction score for a given host and topic
106
+ * @param host — The host identifier
107
+ * @param topic — The topic for which to update the interaction score
108
+ * @param since — The score value to store
109
+ */
110
+ updateLastInteraction: (host: string, topic: string, since: number) => Promise<void>
111
+
112
+ /**
113
+ * Retrieves the last interaction score for a given host and topic
114
+ * @param host — The host identifier
115
+ * @param topic — The topic to query
116
+ * @returns The last interaction score, or 0 if not found
117
+ */
118
+ getLastInteraction: (host: string, topic: string) => Promise<number>
102
119
  }