@bsv/overlay 0.1.5 → 0.1.7
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/mod.js.map +1 -1
- package/dist/cjs/package.json +2 -2
- package/dist/cjs/src/Advertisement.js +3 -0
- package/dist/cjs/src/Advertisement.js.map +1 -0
- package/dist/cjs/src/Engine.js +53 -52
- package/dist/cjs/src/Engine.js.map +1 -1
- package/dist/cjs/src/storage/knex/KnexStorage.js +11 -5
- package/dist/cjs/src/storage/knex/KnexStorage.js.map +1 -1
- package/dist/cjs/src/storage/knex/all-migrations.js +3 -4
- package/dist/cjs/src/storage/knex/all-migrations.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.js +18 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/mod.js.map +1 -1
- package/dist/esm/src/Advertisement.js +2 -0
- package/dist/esm/src/Advertisement.js.map +1 -0
- package/dist/esm/src/Engine.js +51 -51
- package/dist/esm/src/Engine.js.map +1 -1
- package/dist/esm/src/storage/knex/KnexStorage.js +11 -5
- package/dist/esm/src/storage/knex/KnexStorage.js.map +1 -1
- package/dist/esm/src/storage/knex/all-migrations.js +4 -5
- package/dist/esm/src/storage/knex/all-migrations.js.map +1 -1
- package/dist/esm/src/storage/knex/migrations/2024-07-10-001-block-height.js +13 -0
- package/dist/esm/src/storage/knex/migrations/2024-07-10-001-block-height.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/mod.d.ts +2 -0
- package/dist/types/mod.d.ts.map +1 -1
- package/dist/types/src/Advertisement.d.ts +9 -0
- package/dist/types/src/Advertisement.d.ts.map +1 -0
- package/dist/types/src/Advertiser.d.ts +16 -24
- package/dist/types/src/Advertiser.d.ts.map +1 -1
- package/dist/types/src/Engine.d.ts +7 -9
- package/dist/types/src/Engine.d.ts.map +1 -1
- package/dist/types/src/Output.d.ts +1 -0
- package/dist/types/src/Output.d.ts.map +1 -1
- package/dist/types/src/storage/Storage.d.ts +8 -0
- package/dist/types/src/storage/Storage.d.ts.map +1 -1
- package/dist/types/src/storage/knex/KnexStorage.d.ts +1 -0
- package/dist/types/src/storage/knex/KnexStorage.d.ts.map +1 -1
- package/dist/types/src/storage/knex/all-migrations.d.ts +3 -2
- package/dist/types/src/storage/knex/all-migrations.d.ts.map +1 -1
- package/dist/types/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts +4 -0
- package/dist/types/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/mod.ts +2 -0
- package/package.json +2 -2
- package/src/Advertisement.ts +8 -0
- package/src/Advertiser.ts +17 -25
- package/src/Engine.ts +58 -62
- package/src/Output.ts +1 -0
- package/src/storage/Storage.ts +10 -0
- package/src/storage/knex/KnexStorage.ts +12 -5
- package/src/storage/knex/all-migrations.ts +10 -7
- package/src/storage/knex/migrations/2024-07-10-001-block-height.ts +15 -0
- package/src/GASP.ts +0 -421
- package/src/SHIPAdvertisement.ts +0 -8
- package/src/SLAPAdvertisement.ts +0 -8
package/src/Engine.ts
CHANGED
|
@@ -9,10 +9,10 @@ import { LookupQuestion } from './LookupQuestion.js'
|
|
|
9
9
|
import { LookupAnswer } from './LookupAnswer.js'
|
|
10
10
|
import { LookupFormula } from './LookupFormula.js'
|
|
11
11
|
import { Transaction, ChainTracker, MerklePath, Broadcaster, isBroadcastFailure } from '@bsv/sdk'
|
|
12
|
-
import { Advertiser } from './Advertiser.js'
|
|
13
|
-
import {
|
|
14
|
-
import { GASP, GASPInitialReply, GASPInitialRequest, GASPInitialResponse, GASPNode, GASPNodeResponse, GASPRemote, GASPStorage } from './GASP.js'
|
|
12
|
+
import { AdvertisementData, Advertiser } from './Advertiser.js'
|
|
13
|
+
import { GASP, GASPInitialReply, GASPInitialRequest, GASPInitialResponse, GASPNode, GASPNodeResponse, GASPRemote, GASPStorage } from '@bsv/gasp'
|
|
15
14
|
import { SyncConfiguration } from './SyncConfiguration.js'
|
|
15
|
+
import { Advertisement } from './Advertisement.js'
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Am engine for running BSV Overlay Services (topic managers and lookup services).
|
|
@@ -272,7 +272,7 @@ export class Engine {
|
|
|
272
272
|
|
|
273
273
|
// Lookup will currently always return type output-list
|
|
274
274
|
if (lookupAnswer.type === 'output-list') {
|
|
275
|
-
const shipAdvertisements:
|
|
275
|
+
const shipAdvertisements: Advertisement[] = []
|
|
276
276
|
lookupAnswer.outputs.forEach(output => {
|
|
277
277
|
try {
|
|
278
278
|
// Parse out the advertisements using the provided parser
|
|
@@ -286,7 +286,7 @@ export class Engine {
|
|
|
286
286
|
}
|
|
287
287
|
})
|
|
288
288
|
if (shipAdvertisements.length > 0) {
|
|
289
|
-
shipAdvertisements.forEach((advertisement:
|
|
289
|
+
shipAdvertisements.forEach((advertisement: Advertisement) => {
|
|
290
290
|
if (!domainToTopicsMap.has(advertisement.domain)) {
|
|
291
291
|
domainToTopicsMap.set(advertisement.domain, new Set<string>())
|
|
292
292
|
}
|
|
@@ -423,57 +423,49 @@ export class Engine {
|
|
|
423
423
|
const configuredServices = Object.keys(this.lookupServices)
|
|
424
424
|
|
|
425
425
|
// Step 2: Fetch Existing Advertisements
|
|
426
|
-
const currentSHIPAdvertisements = await advertiser.
|
|
427
|
-
const currentSLAPAdvertisements = await advertiser.
|
|
426
|
+
const currentSHIPAdvertisements = await advertiser.findAllAdvertisements('SHIP')
|
|
427
|
+
const currentSLAPAdvertisements = await advertiser.findAllAdvertisements('SLAP')
|
|
428
428
|
|
|
429
429
|
// Step 3: Compare and Determine Actions
|
|
430
430
|
const requiredSHIPAdvertisements = new Set(configuredTopics)
|
|
431
431
|
const requiredSLAPAdvertisements = new Set(configuredServices)
|
|
432
432
|
|
|
433
|
-
const
|
|
434
|
-
const
|
|
433
|
+
const existingTopics = new Set(currentSHIPAdvertisements.map(ad => ad.topicOrService))
|
|
434
|
+
const existingServices = new Set(currentSLAPAdvertisements.map(ad => ad.topicOrService))
|
|
435
435
|
|
|
436
|
-
const
|
|
437
|
-
const
|
|
436
|
+
const shipsToCreate = Array.from(requiredSHIPAdvertisements).filter(topicOrService => !existingTopics.has(topicOrService))
|
|
437
|
+
const slapsToCreate = Array.from(requiredSLAPAdvertisements).filter(topicOrService => !existingServices.has(topicOrService))
|
|
438
|
+
const shipsToRevoke = currentSHIPAdvertisements.filter(ad => !requiredSHIPAdvertisements.has(ad.topicOrService))
|
|
439
|
+
const slapsToRevoke = currentSLAPAdvertisements.filter(ad => !requiredSLAPAdvertisements.has(ad.topicOrService))
|
|
438
440
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
try {
|
|
454
|
-
const taggedBEEF = await advertiser.createSLAPAdvertisement(service)
|
|
455
|
-
await this.submit(taggedBEEF)
|
|
456
|
-
} catch (error) {
|
|
457
|
-
console.error('Failed to create SLAP advertisement:', error)
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
for (const ad of shipToRevoke) {
|
|
462
|
-
try {
|
|
463
|
-
const taggedBEEF = await advertiser.revokeAdvertisement(ad)
|
|
441
|
+
// Create needed SHIP/SLAP advertisements
|
|
442
|
+
try {
|
|
443
|
+
if (shipsToCreate.length > 0 || slapsToCreate.length > 0) {
|
|
444
|
+
const advertisementData: AdvertisementData[] = [
|
|
445
|
+
...shipsToCreate.map(topic => ({
|
|
446
|
+
protocol: 'SHIP' as 'SHIP',
|
|
447
|
+
topicOrServiceName: topic
|
|
448
|
+
})),
|
|
449
|
+
...slapsToCreate.map(service => ({
|
|
450
|
+
protocol: 'SLAP' as 'SLAP',
|
|
451
|
+
topicOrServiceName: service
|
|
452
|
+
}))
|
|
453
|
+
]
|
|
454
|
+
const taggedBEEF = await advertiser.createAdvertisements(advertisementData)
|
|
464
455
|
await this.submit(taggedBEEF)
|
|
465
|
-
} catch (error) {
|
|
466
|
-
console.error('Failed to revoke SHIP advertisement:', error)
|
|
467
456
|
}
|
|
457
|
+
} catch (error) {
|
|
458
|
+
console.error('Failed to create SHIP advertisement:', error)
|
|
468
459
|
}
|
|
469
460
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
461
|
+
// Revoke all advertisements to revoke
|
|
462
|
+
try {
|
|
463
|
+
if (shipsToRevoke.length > 0 || slapsToRevoke.length > 0) {
|
|
464
|
+
const taggedBEEF = await advertiser.revokeAdvertisements([...shipsToRevoke, ...slapsToRevoke])
|
|
473
465
|
await this.submit(taggedBEEF)
|
|
474
|
-
} catch (error) {
|
|
475
|
-
console.error('Failed to revoke SLAP advertisement:', error)
|
|
476
466
|
}
|
|
467
|
+
} catch (error) {
|
|
468
|
+
console.error('Failed to revoke SHIP/SLAP advertisements:', error)
|
|
477
469
|
}
|
|
478
470
|
}
|
|
479
471
|
|
|
@@ -539,12 +531,12 @@ export class Engine {
|
|
|
539
531
|
* Given a GASP request, create an initial response.
|
|
540
532
|
*
|
|
541
533
|
* This method processes an initial synchronization request by finding the relevant UTXOs for the given topic
|
|
542
|
-
* since the provided
|
|
543
|
-
* and the
|
|
534
|
+
* since the provided block height in the request. It constructs a response that includes a list of these UTXOs
|
|
535
|
+
* and the min block height from the initial request.
|
|
544
536
|
*
|
|
545
|
-
* @param initialRequest - The GASP initial request containing the version and the
|
|
537
|
+
* @param initialRequest - The GASP initial request containing the version and the block height since the last sync.
|
|
546
538
|
* @param topic - The topic for which UTXOs are being requested.
|
|
547
|
-
* @returns A promise that resolves to a GASPInitialResponse containing the list of UTXOs and the provided
|
|
539
|
+
* @returns A promise that resolves to a GASPInitialResponse containing the list of UTXOs and the provided min block height.
|
|
548
540
|
*/
|
|
549
541
|
async provideForeignSyncResponse(initialRequest: GASPInitialRequest, topic: string): Promise<GASPInitialResponse> {
|
|
550
542
|
const UTXOs = await this.storage.findUTXOsForTopic(topic, initialRequest.since)
|
|
@@ -770,20 +762,19 @@ export class Engine {
|
|
|
770
762
|
|
|
771
763
|
/**
|
|
772
764
|
* Given a new transaction proof (txid, proof),
|
|
773
|
-
*
|
|
774
765
|
* update tx.merklePath if appropriate,
|
|
775
|
-
*
|
|
776
766
|
* and if not, recurse through all input sourceTransactions.
|
|
777
|
-
*
|
|
767
|
+
*
|
|
778
768
|
* @param tx transaction which may benefit from new proof.
|
|
779
769
|
* @param txid BE hex string double hash of transaction proven by proof.
|
|
780
770
|
* @param proof for txid
|
|
781
771
|
*/
|
|
782
|
-
private updateInputProofs(tx: Transaction, txid: string, proof: MerklePath) {
|
|
783
|
-
if (tx.merklePath)
|
|
784
|
-
//
|
|
772
|
+
private updateInputProofs(tx: Transaction, txid: string, proof: MerklePath): void {
|
|
773
|
+
if (tx.merklePath !== undefined) {
|
|
774
|
+
// Update the merkle path to handle potential reorgs
|
|
775
|
+
tx.merklePath = proof
|
|
785
776
|
return
|
|
786
|
-
|
|
777
|
+
}
|
|
787
778
|
if (tx.id('hex') === txid) {
|
|
788
779
|
tx.merklePath = proof
|
|
789
780
|
} else {
|
|
@@ -803,12 +794,12 @@ export class Engine {
|
|
|
803
794
|
* @param proof - The merklePath proving txid is a mined transaction hash
|
|
804
795
|
*/
|
|
805
796
|
private async updateMerkleProof(output: Output, txid: string, proof: MerklePath): Promise<void> {
|
|
806
|
-
|
|
807
797
|
const tx = Transaction.fromBEEF(output.beef)
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
798
|
+
if (tx.merklePath !== undefined) {
|
|
799
|
+
// Update the merkle path to handle potential reorgs
|
|
800
|
+
tx.merklePath = proof
|
|
811
801
|
return
|
|
802
|
+
}
|
|
812
803
|
|
|
813
804
|
// recursively update all sourceTransactions proven by (txid,proof)
|
|
814
805
|
this.updateInputProofs(tx, txid, proof)
|
|
@@ -830,16 +821,23 @@ export class Engine {
|
|
|
830
821
|
*
|
|
831
822
|
* @param txid - Transaction ID of the associated outputs to prune.
|
|
832
823
|
* @param proof - Merkle proof containing the Merkle path and other relevant data to verify the transaction.
|
|
824
|
+
* @param blockHeight - The block height associated with the incoming merkle proof.
|
|
833
825
|
*/
|
|
834
|
-
async handleNewMerkleProof(txid: string, proof: MerklePath): Promise<void> {
|
|
826
|
+
async handleNewMerkleProof(txid: string, proof: MerklePath, blockHeight?: number): Promise<void> {
|
|
835
827
|
const outputs = await this.storage.findOutputsForTransaction(txid)
|
|
836
828
|
|
|
837
|
-
if (outputs
|
|
829
|
+
if (outputs === undefined || outputs.length === 0) {
|
|
838
830
|
throw new Error('Could not find matching transaction outputs for proof ingest!')
|
|
839
831
|
}
|
|
840
832
|
|
|
841
833
|
for (const output of outputs) {
|
|
842
834
|
await this.updateMerkleProof(output, txid, proof)
|
|
835
|
+
|
|
836
|
+
// Add the associated blockHeight
|
|
837
|
+
if (blockHeight !== undefined) {
|
|
838
|
+
output.blockHeight = blockHeight
|
|
839
|
+
await this.storage.updateOutputBlockHeight?.(output.txid, output.outputIndex, output.topic, blockHeight)
|
|
840
|
+
}
|
|
843
841
|
}
|
|
844
842
|
}
|
|
845
843
|
|
|
@@ -1006,7 +1004,6 @@ export class OverlayGASPRemote implements GASPRemote {
|
|
|
1006
1004
|
*/
|
|
1007
1005
|
export interface GraphNode {
|
|
1008
1006
|
txid: string
|
|
1009
|
-
time: number
|
|
1010
1007
|
graphID: string
|
|
1011
1008
|
rawTx: string
|
|
1012
1009
|
outputIndex: number
|
|
@@ -1158,7 +1155,6 @@ export class OverlayGASPStorage implements GASPStorage {
|
|
|
1158
1155
|
// Use the spentBy param which should be a txid.inputIndex for the node which spent this one in 36-byte format
|
|
1159
1156
|
const newGraphNode: GraphNode = {
|
|
1160
1157
|
txid,
|
|
1161
|
-
time: 0, // TODO: Determine required format for Time (either block height or timestamp, undefined / Infinity for unconfirmed transactions
|
|
1162
1158
|
graphID: tx.graphID,
|
|
1163
1159
|
rawTx: tx.rawTx,
|
|
1164
1160
|
outputIndex: tx.outputIndex,
|
package/src/Output.ts
CHANGED
package/src/storage/Storage.ts
CHANGED
|
@@ -80,6 +80,16 @@ export interface Storage {
|
|
|
80
80
|
*/
|
|
81
81
|
updateOutputBeef: (txid: string, outputIndex: number, topic: string, beef: number[]) => Promise<void>
|
|
82
82
|
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Updates the block height on an output
|
|
86
|
+
* @param txid — TXID of the output to update
|
|
87
|
+
* @param outputIndex — Index of the output to update
|
|
88
|
+
* @param topic— Topic in which the output should be updated
|
|
89
|
+
* @param blockHeight - height of the block the transaction associated with this output was included in
|
|
90
|
+
*/
|
|
91
|
+
updateOutputBlockHeight?: (txid: string, outputIndex: number, topic: string, blockHeight: number) => Promise<void>
|
|
92
|
+
|
|
83
93
|
/**
|
|
84
94
|
* Inserts record of the applied transaction
|
|
85
95
|
* @param tx — The transaction to insert
|
|
@@ -59,11 +59,10 @@ export class KnexStorage implements Storage {
|
|
|
59
59
|
async findUTXOsForTopic(topic: string, since?: number): Promise<Output[]> {
|
|
60
60
|
const query = this.knex('outputs').where({ topic, spent: false })
|
|
61
61
|
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// }
|
|
62
|
+
// If provided, additionally filters UTXOs by block height
|
|
63
|
+
if (since !== undefined && since > 0) {
|
|
64
|
+
await query.andWhere('blockHeight', '>=', since)
|
|
65
|
+
}
|
|
67
66
|
|
|
68
67
|
const outputs = await query.select(
|
|
69
68
|
'txid', 'outputIndex', 'outputScript', 'topic', 'satoshis', 'beef', 'outputsConsumed', 'spent', 'consumedBy'
|
|
@@ -127,6 +126,14 @@ export class KnexStorage implements Storage {
|
|
|
127
126
|
}).update('beef', Buffer.from(beef))
|
|
128
127
|
}
|
|
129
128
|
|
|
129
|
+
async updateOutputBlockHeight(txid: string, outputIndex: number, topic: string, blockHeight: number): Promise<void> {
|
|
130
|
+
await this.knex('outputs').where({
|
|
131
|
+
txid,
|
|
132
|
+
outputIndex,
|
|
133
|
+
topic
|
|
134
|
+
}).update('blockHeight', blockHeight)
|
|
135
|
+
}
|
|
136
|
+
|
|
130
137
|
async insertAppliedTransaction(tx: { txid: string, topic: string }): Promise<void> {
|
|
131
138
|
await this.knex('applied_transactions').insert({
|
|
132
139
|
txid: tx.txid,
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { Knex } from 'knex'
|
|
2
|
-
import { up as
|
|
2
|
+
import { up as initialSchemaUp, down as initialSchemaDown } from './migrations/2024-05-18-001-initial.js'
|
|
3
|
+
import { up as addBlockHeightColumnUp, down as addBlockHeightColumnDown } from './migrations/2024-07-10-001-block-height.js'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* An array of all migrations, in order.
|
|
6
7
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
{ up: oneUp, down: oneDown }
|
|
12
|
-
]
|
|
8
|
+
type Migration = {
|
|
9
|
+
up: (knex: Knex) => Promise<void>;
|
|
10
|
+
down: (knex: Knex) => Promise<void>;
|
|
11
|
+
};
|
|
13
12
|
|
|
13
|
+
const allMigrations: Migration[] = [
|
|
14
|
+
{ up: initialSchemaUp, down: initialSchemaDown },
|
|
15
|
+
{ up: addBlockHeightColumnUp, down: addBlockHeightColumnDown }
|
|
16
|
+
];
|
|
14
17
|
export default allMigrations
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Knex } from 'knex'
|
|
2
|
+
|
|
3
|
+
export async function up(knex: Knex): Promise<void> {
|
|
4
|
+
// Add new column for blockHeight associated with outputs
|
|
5
|
+
await knex.schema.table('outputs', table => {
|
|
6
|
+
table.integer('blockHeight').unsigned().nullable()
|
|
7
|
+
})
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function down(knex: Knex): Promise<void> {
|
|
11
|
+
// Remove the blockHeight column (rollback operation)
|
|
12
|
+
await knex.schema.table('outputs', table => {
|
|
13
|
+
table.dropColumn('blockHeight')
|
|
14
|
+
})
|
|
15
|
+
}
|