@bsv/overlay 2.2.1 → 2.3.0
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/LICENSE.txt +57 -27
- package/README.md +102 -269
- package/dist/cjs/mod.d.ts +15 -0
- package/dist/cjs/mod.d.ts.map +1 -0
- package/dist/cjs/mod.js +4 -1
- package/dist/cjs/mod.js.map +1 -1
- package/dist/cjs/package.json +41 -13
- package/dist/cjs/src/Advertisement.d.ts +9 -0
- package/dist/cjs/src/Advertisement.d.ts.map +1 -0
- package/dist/cjs/src/Advertiser.d.ts +36 -0
- package/dist/cjs/src/Advertiser.d.ts.map +1 -0
- package/dist/cjs/src/BASM.d.ts +110 -0
- package/dist/cjs/src/BASM.d.ts.map +1 -0
- package/dist/cjs/src/BASM.js.map +1 -1
- package/dist/cjs/src/BASMRemote.d.ts +14 -0
- package/dist/cjs/src/BASMRemote.d.ts.map +1 -0
- package/dist/cjs/src/BASMRemote.js.map +1 -1
- package/dist/cjs/src/Engine.d.ts +379 -0
- package/dist/cjs/src/Engine.d.ts.map +1 -0
- package/dist/cjs/src/Engine.js +425 -418
- package/dist/cjs/src/Engine.js.map +1 -1
- package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts +24 -0
- package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts.map +1 -0
- package/dist/cjs/src/GASP/OverlayGASPRemote.js +7 -10
- package/dist/cjs/src/GASP/OverlayGASPRemote.js.map +1 -1
- package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts +103 -0
- package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts.map +1 -0
- package/dist/cjs/src/GASP/OverlayGASPStorage.js +32 -30
- package/dist/cjs/src/GASP/OverlayGASPStorage.js.map +1 -1
- package/dist/cjs/src/LookupFormula.d.ts +29 -0
- package/dist/cjs/src/LookupFormula.d.ts.map +1 -0
- package/dist/cjs/src/LookupService.d.ts +85 -0
- package/dist/cjs/src/LookupService.d.ts.map +1 -0
- package/dist/cjs/src/Output.d.ts +32 -0
- package/dist/cjs/src/Output.d.ts.map +1 -0
- package/dist/cjs/src/SafeLog.d.ts +14 -0
- package/dist/cjs/src/SafeLog.d.ts.map +1 -0
- package/dist/cjs/src/SafeLog.js +44 -0
- package/dist/cjs/src/SafeLog.js.map +1 -0
- package/{src/SyncConfiguration.ts → dist/cjs/src/SyncConfiguration.d.ts} +2 -1
- package/dist/cjs/src/SyncConfiguration.d.ts.map +1 -0
- package/dist/cjs/src/TopicManager.d.ts +35 -0
- package/dist/cjs/src/TopicManager.d.ts.map +1 -0
- package/dist/cjs/src/storage/Storage.d.ts +237 -0
- package/dist/cjs/src/storage/Storage.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/KnexStorage.d.ts +69 -0
- package/dist/cjs/src/storage/knex/KnexStorage.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/KnexStorage.js +7 -10
- package/dist/cjs/src/storage/knex/KnexStorage.js.map +1 -1
- package/dist/cjs/src/storage/knex/all-migrations.d.ts +11 -0
- package/dist/cjs/src/storage/knex/all-migrations.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/all-migrations.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.d.ts +9 -0
- package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.js.map +1 -1
- package/dist/esm/mod.js +2 -1
- package/dist/esm/mod.js.map +1 -1
- package/dist/esm/src/BASMRemote.js.map +1 -1
- package/dist/esm/src/Engine.js +404 -396
- package/dist/esm/src/Engine.js.map +1 -1
- package/dist/esm/src/GASP/OverlayGASPRemote.js +2 -2
- package/dist/esm/src/GASP/OverlayGASPRemote.js.map +1 -1
- package/dist/esm/src/GASP/OverlayGASPStorage.js +21 -18
- package/dist/esm/src/GASP/OverlayGASPStorage.js.map +1 -1
- package/dist/esm/src/SafeLog.js +40 -0
- package/dist/esm/src/SafeLog.js.map +1 -0
- package/dist/esm/src/storage/knex/KnexStorage.js.map +1 -1
- package/dist/types/mod.d.ts +5 -4
- package/dist/types/mod.d.ts.map +1 -1
- package/dist/types/src/BASMRemote.d.ts.map +1 -1
- package/dist/types/src/Engine.d.ts +18 -1
- package/dist/types/src/Engine.d.ts.map +1 -1
- package/dist/types/src/GASP/OverlayGASPRemote.d.ts +2 -2
- package/dist/types/src/GASP/OverlayGASPRemote.d.ts.map +1 -1
- package/dist/types/src/GASP/OverlayGASPStorage.d.ts +3 -1
- package/dist/types/src/GASP/OverlayGASPStorage.d.ts.map +1 -1
- package/dist/types/src/SafeLog.d.ts +14 -0
- package/dist/types/src/SafeLog.d.ts.map +1 -0
- package/dist/types/src/storage/knex/KnexStorage.d.ts.map +1 -1
- package/dist/types/src/storage/knex/all-migrations.d.ts.map +1 -1
- package/package.json +85 -28
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +0 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +0 -1
- package/dist/types/tsconfig.types.tsbuildinfo +0 -1
- package/docs/API.md +0 -944
- package/docs/BRC-136-BASM.md +0 -327
- package/docs/README.md +0 -16
- package/docs/Synchronization.md +0 -212
- package/docs/concepts/00-overview.md +0 -85
- package/docs/concepts/01-best-practices.md +0 -202
- package/docs/concepts/02-query-performance.md +0 -345
- package/docs/concepts/03-database-monitoring.md +0 -211
- package/docs/concepts/04-pagination-example.md +0 -186
- package/docs/concepts/05-recommendations-summary.md +0 -158
- package/docs/concepts/README.md +0 -20
- package/docs/examples/README.md +0 -15
- package/docs/examples/gs-wip.md +0 -111
- package/docs/internal/README.md +0 -11
- package/mod.ts +0 -40
- package/src/Advertisement.ts +0 -8
- package/src/Advertiser.ts +0 -41
- package/src/BASM.ts +0 -208
- package/src/BASMRemote.ts +0 -54
- package/src/Engine.ts +0 -2194
- package/src/GASP/OverlayGASPRemote.ts +0 -108
- package/src/GASP/OverlayGASPStorage.ts +0 -384
- package/src/LookupFormula.ts +0 -31
- package/src/LookupService.ts +0 -136
- package/src/Output.ts +0 -31
- package/src/TopicManager.ts +0 -40
- package/src/__tests/BASM.test.ts +0 -44
- package/src/__tests/BASMChain.test.ts +0 -295
- package/src/__tests/Engine.test.ts +0 -1381
- package/src/__tests/OverlayGASPRemote.test.ts +0 -135
- package/src/__tests/OverlayGASPStorage.test.ts +0 -205
- package/src/storage/Storage.ts +0 -260
- package/src/storage/knex/KnexStorage.ts +0 -651
- package/src/storage/knex/all-migrations.ts +0 -32
- package/src/storage/knex/migrations/2024-05-18-001-initial.ts +0 -30
- package/src/storage/knex/migrations/2024-07-10-001-block-height.ts +0 -15
- package/src/storage/knex/migrations/2024-07-17-001-transactions.ts +0 -64
- package/src/storage/knex/migrations/2024-07-18-001-indexes.ts +0 -30
- package/src/storage/knex/migrations/2025-05-28-001-enlarge.ts +0 -42
- package/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.ts +0 -46
- package/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.ts +0 -24
- package/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.ts +0 -18
- package/src/storage/knex/migrations/2026-05-29-001-brc136-basm.ts +0 -123
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { OverlayGASPRemote } from '../GASP/OverlayGASPRemote'
|
|
2
|
-
import { GASPInitialRequest, GASPInitialResponse, GASPNode } from '@bsv/gasp'
|
|
3
|
-
|
|
4
|
-
global.fetch = jest.fn(async () => {
|
|
5
|
-
await Promise.resolve({
|
|
6
|
-
ok: true,
|
|
7
|
-
status: 200,
|
|
8
|
-
json: async () => await Promise.resolve({})
|
|
9
|
-
})
|
|
10
|
-
}
|
|
11
|
-
) as jest.Mock
|
|
12
|
-
|
|
13
|
-
describe('OverlayGASPRemote', () => {
|
|
14
|
-
let overlayRemote: OverlayGASPRemote
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
overlayRemote = new OverlayGASPRemote('http://example.com', 'tm_test');
|
|
18
|
-
(fetch as jest.Mock).mockClear()
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
describe('getInitialResponse', () => {
|
|
22
|
-
it('should send a request and return a valid response', async () => {
|
|
23
|
-
const mockRequest: GASPInitialRequest = { version: 1, since: 0 }
|
|
24
|
-
const mockResponse: GASPInitialResponse = {
|
|
25
|
-
UTXOList: [{ txid: 'txid1', outputIndex: 0, score: 0 }],
|
|
26
|
-
since: 1234567890
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
(fetch as jest.Mock).mockResolvedValue({
|
|
30
|
-
ok: true,
|
|
31
|
-
status: 200,
|
|
32
|
-
json: jest.fn().mockResolvedValue(mockResponse)
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
const response = await overlayRemote.getInitialResponse(mockRequest)
|
|
36
|
-
|
|
37
|
-
expect(fetch).toHaveBeenCalledWith('http://example.com/requestSyncResponse', {
|
|
38
|
-
method: 'POST',
|
|
39
|
-
headers: {
|
|
40
|
-
'Content-Type': 'application/json',
|
|
41
|
-
'X-BSV-Topic': 'tm_test'
|
|
42
|
-
},
|
|
43
|
-
body: JSON.stringify(mockRequest)
|
|
44
|
-
})
|
|
45
|
-
expect(response).toEqual(mockResponse)
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('should throw an error if the response status is not OK', async () => {
|
|
49
|
-
const mockRequest: GASPInitialRequest = { version: 1, since: 0 };
|
|
50
|
-
|
|
51
|
-
(fetch as jest.Mock).mockResolvedValue({
|
|
52
|
-
ok: false,
|
|
53
|
-
status: 500
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
await expect(overlayRemote.getInitialResponse(mockRequest)).rejects.toThrow('HTTP error! Status: 500')
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('should throw an error if the response format is invalid', async () => {
|
|
60
|
-
const mockRequest: GASPInitialRequest = { version: 1, since: 0 }
|
|
61
|
-
const invalidResponse = { invalid: 'data' };
|
|
62
|
-
|
|
63
|
-
(fetch as jest.Mock).mockResolvedValue({
|
|
64
|
-
ok: true,
|
|
65
|
-
status: 200,
|
|
66
|
-
json: jest.fn().mockResolvedValue(invalidResponse)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
await expect(overlayRemote.getInitialResponse(mockRequest)).rejects.toThrow('Invalid response format')
|
|
70
|
-
})
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
describe('requestNode', () => {
|
|
74
|
-
it('should send a request and return a valid GASPNode', async () => {
|
|
75
|
-
const graphID = 'graphID1'
|
|
76
|
-
const txid = 'txid1'
|
|
77
|
-
const outputIndex = 0
|
|
78
|
-
const metadata = true
|
|
79
|
-
const mockResponse: GASPNode = {
|
|
80
|
-
graphID,
|
|
81
|
-
rawTx: 'rawTxData',
|
|
82
|
-
outputIndex,
|
|
83
|
-
proof: 'proofData',
|
|
84
|
-
txMetadata: 'txMetadata',
|
|
85
|
-
outputMetadata: 'outputMetadata',
|
|
86
|
-
inputs: {}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
(fetch as jest.Mock).mockResolvedValue({
|
|
90
|
-
ok: true,
|
|
91
|
-
status: 200,
|
|
92
|
-
json: jest.fn().mockResolvedValue(mockResponse)
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
const response = await overlayRemote.requestNode(graphID, txid, outputIndex, metadata)
|
|
96
|
-
|
|
97
|
-
expect(fetch).toHaveBeenCalledWith('http://example.com/requestForeignGASPNode', {
|
|
98
|
-
method: 'POST',
|
|
99
|
-
headers: { 'Content-Type': 'application/json' },
|
|
100
|
-
body: JSON.stringify({ graphID, txid, outputIndex, metadata })
|
|
101
|
-
})
|
|
102
|
-
expect(response).toEqual(mockResponse)
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
it('should throw an error if the response status is not OK', async () => {
|
|
106
|
-
const graphID = 'graphID1'
|
|
107
|
-
const txid = 'txid1'
|
|
108
|
-
const outputIndex = 0
|
|
109
|
-
const metadata = true;
|
|
110
|
-
|
|
111
|
-
(fetch as jest.Mock).mockResolvedValue({
|
|
112
|
-
ok: false,
|
|
113
|
-
status: 500
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
await expect(overlayRemote.requestNode(graphID, txid, outputIndex, metadata)).rejects.toThrow('HTTP error! Status: 500')
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it('should throw an error if the response format is invalid', async () => {
|
|
120
|
-
const graphID = 'graphID1'
|
|
121
|
-
const txid = 'txid1'
|
|
122
|
-
const outputIndex = 0
|
|
123
|
-
const metadata = true
|
|
124
|
-
const invalidResponse = { invalid: 'data' };
|
|
125
|
-
|
|
126
|
-
(fetch as jest.Mock).mockResolvedValue({
|
|
127
|
-
ok: true,
|
|
128
|
-
status: 200,
|
|
129
|
-
json: jest.fn().mockResolvedValue(invalidResponse)
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
await expect(overlayRemote.requestNode(graphID, txid, outputIndex, metadata)).rejects.toThrow('Invalid response format')
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
})
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { GraphNode, OverlayGASPStorage } from '../GASP/OverlayGASPStorage'
|
|
2
|
-
import { Transaction, MerklePath } from '@bsv/sdk'
|
|
3
|
-
import { GASPNode } from '@bsv/gasp'
|
|
4
|
-
|
|
5
|
-
describe('OverlayGASPStorage', () => {
|
|
6
|
-
let overlayStorage: OverlayGASPStorage
|
|
7
|
-
let mockEngine: any
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
mockEngine = { storage: { findOutput: jest.fn(() => undefined), findUTXOsForTopic: jest.fn() }, managers: {} }
|
|
11
|
-
overlayStorage = new OverlayGASPStorage('test-topic', mockEngine)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
describe('appendToGraph', () => {
|
|
15
|
-
it('should append a new node to an empty graph', async () => {
|
|
16
|
-
const mockTx = {
|
|
17
|
-
rawTx: '001122',
|
|
18
|
-
outputIndex: 0,
|
|
19
|
-
graphID: 'txid123.0'
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Use actual Transaction implementation
|
|
23
|
-
const parsedTx = Transaction.fromHex(mockTx.rawTx)
|
|
24
|
-
|
|
25
|
-
await overlayStorage.appendToGraph(mockTx)
|
|
26
|
-
|
|
27
|
-
expect(Object.keys(overlayStorage.temporaryGraphNodeRefs).length).toBe(1)
|
|
28
|
-
expect(overlayStorage.temporaryGraphNodeRefs['txid123.0'].txid).toBe(parsedTx.id('hex'))
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('throws error when max nodes are exceeded', async () => {
|
|
32
|
-
overlayStorage.maxNodesInGraph = 1
|
|
33
|
-
const graphNode: GraphNode = { txid: 'txid123', children: [], rawTx: '', graphID: 'txid4321.2', outputIndex: 0 }
|
|
34
|
-
overlayStorage.temporaryGraphNodeRefs['txid123.0'] = graphNode
|
|
35
|
-
|
|
36
|
-
const mockTx = {
|
|
37
|
-
rawTx: '334455',
|
|
38
|
-
outputIndex: 1,
|
|
39
|
-
graphID: 'txid234.1'
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
await expect(overlayStorage.appendToGraph(mockTx)).rejects.toThrow('The max number of nodes in transaction graph has been reached!')
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
describe('findKnownUTXOs', () => {
|
|
47
|
-
it('should return known UTXOs since a given timestamp', async () => {
|
|
48
|
-
const mockUTXOs = [{ txid: 'txid1', outputIndex: 0, score: 0 }, { txid: 'txid2', outputIndex: 1, score: 0 }]
|
|
49
|
-
mockEngine.storage.findUTXOsForTopic.mockResolvedValue(mockUTXOs)
|
|
50
|
-
|
|
51
|
-
const result = await overlayStorage.findKnownUTXOs(1234567890)
|
|
52
|
-
|
|
53
|
-
expect(result).toEqual([
|
|
54
|
-
{ txid: 'txid1', outputIndex: 0, score: 0 },
|
|
55
|
-
{ txid: 'txid2', outputIndex: 1, score: 0 }
|
|
56
|
-
])
|
|
57
|
-
expect(mockEngine.storage.findUTXOsForTopic).toHaveBeenCalledWith('test-topic', 1234567890)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('should handle errors correctly', async () => {
|
|
61
|
-
mockEngine.storage.findUTXOsForTopic.mockRejectedValue(new Error('Database error'))
|
|
62
|
-
|
|
63
|
-
await expect(overlayStorage.findKnownUTXOs(1234567890)).rejects.toThrow('Database error')
|
|
64
|
-
})
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
describe('hydrateGASPNode', () => {
|
|
68
|
-
it('should throw an error if no output is found', async () => {
|
|
69
|
-
await expect(overlayStorage.hydrateGASPNode('graphID', 'txid', 0, false)).rejects.toThrow('No matching output found!')
|
|
70
|
-
})
|
|
71
|
-
// TODO: Further test coverage
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
describe('findNeededInputs', () => {
|
|
75
|
-
// TODO: Write more complicated test cases
|
|
76
|
-
it('should return inputs needed for further verification when no proof is present', async () => {
|
|
77
|
-
const mockTx: GASPNode = {
|
|
78
|
-
rawTx: '001122',
|
|
79
|
-
proof: undefined,
|
|
80
|
-
graphID: 'txid123.0',
|
|
81
|
-
outputIndex: 0
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const parsedTx = {
|
|
85
|
-
inputs: [{ sourceTXID: 'inputTxid1', sourceOutputIndex: 0 }],
|
|
86
|
-
toBEEF: jest.fn(),
|
|
87
|
-
id: jest.fn().mockReturnValue('txid123')
|
|
88
|
-
}
|
|
89
|
-
Transaction.fromHex = jest.fn().mockReturnValue(parsedTx)
|
|
90
|
-
|
|
91
|
-
const result = await overlayStorage.findNeededInputs(mockTx)
|
|
92
|
-
|
|
93
|
-
expect(result).toEqual({
|
|
94
|
-
requestedInputs: { 'inputTxid1.0': { metadata: false } }
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it('should return inputs needed for further verification when proof is present', async () => {
|
|
99
|
-
const mockTx: GASPNode = {
|
|
100
|
-
rawTx: '001122',
|
|
101
|
-
proof: 'someproof',
|
|
102
|
-
graphID: 'txid123.0',
|
|
103
|
-
outputIndex: 0
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const parsedTx = {
|
|
107
|
-
inputs: [{ sourceTXID: 'neededTxid', sourceOutputIndex: 1 }],
|
|
108
|
-
toBEEF: jest.fn(),
|
|
109
|
-
id: jest.fn().mockReturnValue('txid123'),
|
|
110
|
-
merklePath: {}
|
|
111
|
-
}
|
|
112
|
-
Transaction.fromHex = jest.fn().mockReturnValue(parsedTx)
|
|
113
|
-
MerklePath.fromHex = jest.fn().mockReturnValue(parsedTx.merklePath)
|
|
114
|
-
|
|
115
|
-
mockEngine.managers['test-topic'] = {
|
|
116
|
-
identifyAdmissibleOutputs: jest.fn().mockResolvedValue({
|
|
117
|
-
outputsToAdmit: []
|
|
118
|
-
}),
|
|
119
|
-
identifyNeededInputs: jest.fn().mockResolvedValue([{ txid: 'neededTxid', outputIndex: 1 }])
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const result = await overlayStorage.findNeededInputs(mockTx)
|
|
123
|
-
|
|
124
|
-
expect(result).toEqual({
|
|
125
|
-
requestedInputs: { 'neededTxid.1': { metadata: false } }
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
it.each([
|
|
130
|
-
[new Error('manager failed'), 'manager failed'],
|
|
131
|
-
['manager failed as a string', 'manager failed as a string']
|
|
132
|
-
])('terminates the graph and logs normalized identifyNeededInputs errors', async (failure, message) => {
|
|
133
|
-
const mockTx: GASPNode = {
|
|
134
|
-
rawTx: '001122',
|
|
135
|
-
proof: 'someproof',
|
|
136
|
-
graphID: 'txid123.0',
|
|
137
|
-
outputIndex: 0
|
|
138
|
-
}
|
|
139
|
-
const parsedTx = {
|
|
140
|
-
inputs: [],
|
|
141
|
-
toBEEF: jest.fn().mockReturnValue([1, 2, 3]),
|
|
142
|
-
id: jest.fn().mockReturnValue('txid123'),
|
|
143
|
-
merklePath: {}
|
|
144
|
-
}
|
|
145
|
-
Transaction.fromHex = jest.fn().mockReturnValue(parsedTx)
|
|
146
|
-
MerklePath.fromHex = jest.fn().mockReturnValue(parsedTx.merklePath)
|
|
147
|
-
mockEngine.managers['test-topic'] = {
|
|
148
|
-
identifyAdmissibleOutputs: jest.fn().mockResolvedValue({
|
|
149
|
-
outputsToAdmit: []
|
|
150
|
-
}),
|
|
151
|
-
identifyNeededInputs: jest.fn().mockRejectedValue(failure)
|
|
152
|
-
}
|
|
153
|
-
const consoleError = jest.spyOn(console, 'error').mockImplementation(() => {})
|
|
154
|
-
|
|
155
|
-
await expect(overlayStorage.findNeededInputs(mockTx)).resolves.toBeUndefined()
|
|
156
|
-
|
|
157
|
-
expect(consoleError).toHaveBeenCalledWith(
|
|
158
|
-
`An error occurred when identifying needed inputs for transaction: txid123.0: ${message}`
|
|
159
|
-
)
|
|
160
|
-
consoleError.mockRestore()
|
|
161
|
-
})
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
describe('discardGraph', () => {
|
|
165
|
-
it('should discard the graph and its nodes', async () => {
|
|
166
|
-
const graphNode1: GraphNode = {
|
|
167
|
-
txid: 'txid123',
|
|
168
|
-
graphID: 'txid123.0',
|
|
169
|
-
rawTx: 'rawTxData',
|
|
170
|
-
outputIndex: 0,
|
|
171
|
-
children: [],
|
|
172
|
-
parent: undefined
|
|
173
|
-
}
|
|
174
|
-
overlayStorage.temporaryGraphNodeRefs['txid123.0'] = graphNode1
|
|
175
|
-
|
|
176
|
-
const parentNode: GraphNode = {
|
|
177
|
-
txid: 'txid123',
|
|
178
|
-
graphID: 'txid123.0',
|
|
179
|
-
rawTx: 'rawTxData',
|
|
180
|
-
outputIndex: 0,
|
|
181
|
-
children: []
|
|
182
|
-
}
|
|
183
|
-
const graphNode2: GraphNode = {
|
|
184
|
-
txid: 'txid124',
|
|
185
|
-
graphID: 'txid123.0',
|
|
186
|
-
rawTx: 'rawTxData',
|
|
187
|
-
outputIndex: 1,
|
|
188
|
-
children: [],
|
|
189
|
-
parent: parentNode
|
|
190
|
-
}
|
|
191
|
-
overlayStorage.temporaryGraphNodeRefs['txid124.0'] = graphNode2
|
|
192
|
-
|
|
193
|
-
await overlayStorage.discardGraph('txid123.0')
|
|
194
|
-
|
|
195
|
-
expect(overlayStorage.temporaryGraphNodeRefs['txid123.0']).toBeUndefined()
|
|
196
|
-
expect(overlayStorage.temporaryGraphNodeRefs['txid124.0']).toBeUndefined()
|
|
197
|
-
})
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
it('should handle non-existent graphID', async () => {
|
|
201
|
-
await overlayStorage.discardGraph('nonexistent.0')
|
|
202
|
-
|
|
203
|
-
expect(Object.keys(overlayStorage.temporaryGraphNodeRefs).length).toBe(0)
|
|
204
|
-
})
|
|
205
|
-
})
|
package/src/storage/Storage.ts
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import type { Output } from '../Output.js'
|
|
2
|
-
import type {
|
|
3
|
-
AdmittedTxRef,
|
|
4
|
-
RawTransactionRecord,
|
|
5
|
-
TopicAnchorTip,
|
|
6
|
-
TopicBlockAnchor
|
|
7
|
-
} from '../BASM.js'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Represents a transaction that has been applied to a topic.
|
|
11
|
-
*/
|
|
12
|
-
export interface AppliedTransaction {
|
|
13
|
-
/** TXID of the applied transaction */
|
|
14
|
-
txid: string
|
|
15
|
-
/** Output index of the applied transaction */
|
|
16
|
-
topic: string
|
|
17
|
-
/** Block height of the transaction proof, when known */
|
|
18
|
-
blockHeight?: number
|
|
19
|
-
/** Block hash for the proven transaction's block, when known */
|
|
20
|
-
blockHash?: string
|
|
21
|
-
/** Transaction's canonical in-block index, when known */
|
|
22
|
-
blockIndex?: number
|
|
23
|
-
/** Merkle root computed from the direct proof, when known */
|
|
24
|
-
merkleRoot?: string
|
|
25
|
-
/** Chain height when this transaction was first seen by this overlay */
|
|
26
|
-
firstSeenHeight?: number
|
|
27
|
-
/** Whether this applied transaction has a direct proof of inclusion */
|
|
28
|
-
proven?: boolean
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface StoredTransactionRecord {
|
|
32
|
-
txid: string
|
|
33
|
-
beef?: number[]
|
|
34
|
-
rawTx?: number[]
|
|
35
|
-
merklePath?: number[]
|
|
36
|
-
blockHeight?: number
|
|
37
|
-
blockHash?: string
|
|
38
|
-
blockIndex?: number
|
|
39
|
-
merkleRoot?: string
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface AppliedTransactionProofUpdate {
|
|
43
|
-
txid: string
|
|
44
|
-
topic: string
|
|
45
|
-
blockHeight: number
|
|
46
|
-
blockHash?: string
|
|
47
|
-
blockIndex?: number
|
|
48
|
-
merkleRoot?: string
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface UnprovenAppliedTransactionCandidate {
|
|
52
|
-
txid: string
|
|
53
|
-
topic: string
|
|
54
|
-
firstSeenHeight?: number
|
|
55
|
-
outputs: Array<{ txid: string, outputIndex: number }>
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Defines the Storage Engine interface used internally by the Overlay Services Engine.
|
|
60
|
-
*/
|
|
61
|
-
export interface Storage {
|
|
62
|
-
/**
|
|
63
|
-
* Adds a new output to storage
|
|
64
|
-
* @param utxo — The output to add
|
|
65
|
-
*/
|
|
66
|
-
insertOutput: (utxo: Output) => Promise<void>
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Finds an output from storage
|
|
70
|
-
* @param txid — TXID of hte output to find
|
|
71
|
-
* @param outputIndex — Output index for the output to find
|
|
72
|
-
* @param topic — The topic in which the output is stored (optional)
|
|
73
|
-
* @param spent — Whether the output must be spent to be returned (optional)
|
|
74
|
-
* @param includeBEEF — Whether to include the BEEF data for the output (optional)
|
|
75
|
-
*/
|
|
76
|
-
findOutput: (txid: string, outputIndex: number, topic?: string, spent?: boolean, includeBEEF?: boolean) => Promise<Output | null>
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Finds multiple outputs from storage by txid/output index pairs.
|
|
80
|
-
* Implementations can use this to collapse many point lookups into a single query.
|
|
81
|
-
* @param outpoints — txid/output index pairs to find
|
|
82
|
-
* @param includeBEEF — Whether to include the BEEF data for the outputs (optional)
|
|
83
|
-
*/
|
|
84
|
-
findOutputsByOutpoints?: (
|
|
85
|
-
outpoints: Array<{ txid: string, outputIndex: number }>,
|
|
86
|
-
includeBEEF?: boolean
|
|
87
|
-
) => Promise<Output[]>
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Finds outputs with a matching transaction ID from storage
|
|
91
|
-
* @param txid — TXID of the outputs to find
|
|
92
|
-
* @param includeBEEF — Whether to include the BEEF data for the outputs (optional)
|
|
93
|
-
*/
|
|
94
|
-
findOutputsForTransaction: (txid: string, includeBEEF?: boolean) => Promise<Output[]>
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Finds current UTXOs that have been admitted into a given topic
|
|
98
|
-
* @param topic - The topic for which we want to find Unspent Transaction Outputs (UTXOs).
|
|
99
|
-
* @param since - Optional parameter indicating the minimum score value to retrieve matching UTXOs from. This is used for score-based filtering.
|
|
100
|
-
* @param limit - Optional parameter to limit the number of results returned
|
|
101
|
-
* @param includeBEEF — Whether to include the BEEF data for the outputs (optional)
|
|
102
|
-
* @returns A promise that resolves to an array of matching UTXOs.
|
|
103
|
-
*/
|
|
104
|
-
findUTXOsForTopic: (topic: string, since?: number, limit?: number, includeBEEF?: boolean) => Promise<Output[]>
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Deletes an output from storage
|
|
108
|
-
* @param txid — The TXID of the output to delete
|
|
109
|
-
* @param outputIndex — The index of the output to delete
|
|
110
|
-
* @param topic — The topic where the output should be deleted
|
|
111
|
-
*/
|
|
112
|
-
deleteOutput: (txid: string, outputIndex: number, topic: string) => Promise<void>
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Updates a UTXO as spent
|
|
116
|
-
* @param txid — TXID of the output to update
|
|
117
|
-
* @param outputIndex — Index of the output to update
|
|
118
|
-
* @param topic — Topic in which the output should be updated
|
|
119
|
-
*/
|
|
120
|
-
markUTXOAsSpent: (txid: string, outputIndex: number, topic: string) => Promise<void>
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Updates which outputs are consumed by this output
|
|
124
|
-
* @param txid — TXID of the output to update
|
|
125
|
-
* @param outputIndex — Index of the output to update
|
|
126
|
-
* @param topic — Topic in which the output should be updated
|
|
127
|
-
* @param consumedBy — The new set of outputs consumed by this output
|
|
128
|
-
*/
|
|
129
|
-
updateConsumedBy: (txid: string, outputIndex: number, topic: string, consumedBy: Array<{
|
|
130
|
-
txid: string
|
|
131
|
-
outputIndex: number
|
|
132
|
-
}>) => Promise<void>
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Updates the beef data for a transaction
|
|
136
|
-
* @param txid — TXID of the transaction to update
|
|
137
|
-
* @param beef - BEEF data to update
|
|
138
|
-
*/
|
|
139
|
-
updateTransactionBEEF: (txid: string, beef: number[]) => Promise<void>
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Updates the block height on an output
|
|
143
|
-
* @param txid — TXID of the output to update
|
|
144
|
-
* @param outputIndex — Index of the output to update
|
|
145
|
-
* @param topic— Topic in which the output should be updated
|
|
146
|
-
* @param blockHeight - height of the block the transaction associated with this output was included in
|
|
147
|
-
*/
|
|
148
|
-
updateOutputBlockHeight?: (txid: string, outputIndex: number, topic: string, blockHeight: number) => Promise<void>
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Upserts transaction-level data used for compact BEEF, raw-tx BASM fetches,
|
|
152
|
-
* and direct proof retrieval.
|
|
153
|
-
*/
|
|
154
|
-
upsertTransactionRecord?: (record: StoredTransactionRecord) => Promise<void>
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Updates direct proof metadata for an applied topic transaction.
|
|
158
|
-
*/
|
|
159
|
-
updateAppliedTransactionProof?: (record: AppliedTransactionProofUpdate) => Promise<void>
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Inserts record of the applied transaction
|
|
163
|
-
* @param tx — The transaction to insert
|
|
164
|
-
*/
|
|
165
|
-
insertAppliedTransaction: (tx: AppliedTransaction) => Promise<void>
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Checks if a duplicate transaction exists
|
|
169
|
-
* @param tx — Transaction to check
|
|
170
|
-
* @returns Whether the transaction is already applied
|
|
171
|
-
*/
|
|
172
|
-
doesAppliedTransactionExist: (tx: AppliedTransaction) => Promise<boolean>
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Returns proven topic transactions admitted in one block, in canonical block order.
|
|
176
|
-
*/
|
|
177
|
-
findAdmittedTransactionsForBlock?: (topic: string, blockHeight: number, blockHash?: string) => Promise<AdmittedTxRef[]>
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Persists or replaces a topic block anchor for one height.
|
|
181
|
-
*/
|
|
182
|
-
upsertTopicBlockAnchor?: (anchor: TopicBlockAnchor) => Promise<void>
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Returns one topic block anchor.
|
|
186
|
-
*/
|
|
187
|
-
findTopicBlockAnchor?: (topic: string, blockHeight: number, blockHash?: string) => Promise<TopicBlockAnchor | undefined>
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Returns topic block anchors across a closed height range.
|
|
191
|
-
*/
|
|
192
|
-
findTopicBlockAnchors?: (topic: string, fromHeight: number, toHeight: number) => Promise<TopicBlockAnchor[]>
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Returns the latest known topic anchor tip.
|
|
196
|
-
*/
|
|
197
|
-
findTopicAnchorTip?: (topic: string) => Promise<TopicAnchorTip | undefined>
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Returns raw transactions for the requested txids.
|
|
201
|
-
*/
|
|
202
|
-
findRawTransactions?: (txids: string[]) => Promise<RawTransactionRecord[]>
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Returns direct Merkle paths for the requested txids.
|
|
206
|
-
*/
|
|
207
|
-
findTransactionMerklePaths?: (txids: string[]) => Promise<Array<{
|
|
208
|
-
txid: string
|
|
209
|
-
merklePath: string
|
|
210
|
-
blockHeight?: number
|
|
211
|
-
blockHash?: string
|
|
212
|
-
blockIndex?: number
|
|
213
|
-
merkleRoot?: string
|
|
214
|
-
}>>
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Finds topic-applied transactions with no direct proof old enough to evict.
|
|
218
|
-
*/
|
|
219
|
-
findUnprovenAppliedTransactions?: (cutoffHeight: number, topic?: string) => Promise<UnprovenAppliedTransactionCandidate[]>
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Deletes a topic-applied transaction record.
|
|
223
|
-
*/
|
|
224
|
-
deleteAppliedTransaction?: (txid: string, topic: string) => Promise<void>
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Returns proven topic-applied transactions whose proof anchors to the given
|
|
228
|
-
* block hash. Used to demote admissions when that block is reorged out.
|
|
229
|
-
*/
|
|
230
|
-
findProvenAppliedTransactionsByBlockHash?: (blockHash: string) => Promise<Array<{ txid: string, topic: string, blockHeight: number }>>
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Returns proven topic-applied transactions in a closed block-height range,
|
|
234
|
-
* including the proof's block hash and merkle root for revalidation sweeps.
|
|
235
|
-
*/
|
|
236
|
-
findProvenAppliedTransactionsInRange?: (fromHeight: number, toHeight: number, topic?: string) => Promise<Array<{ txid: string, topic: string, blockHeight: number, blockHash?: string, merkleRoot?: string }>>
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Demotes a proven applied transaction back to unproven: clears the block
|
|
240
|
-
* proof metadata and `proven` flag while retaining `firstSeenHeight`, so the
|
|
241
|
-
* "received" record survives until re-proof or unproven eviction.
|
|
242
|
-
*/
|
|
243
|
-
demoteAppliedTransactionToUnproven?: (txid: string, topic: string) => Promise<void>
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Updates the last interaction score for a given host and topic
|
|
247
|
-
* @param host — The host identifier
|
|
248
|
-
* @param topic — The topic for which to update the interaction score
|
|
249
|
-
* @param since — The score value to store
|
|
250
|
-
*/
|
|
251
|
-
updateLastInteraction: (host: string, topic: string, since: number) => Promise<void>
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Retrieves the last interaction score for a given host and topic
|
|
255
|
-
* @param host — The host identifier
|
|
256
|
-
* @param topic — The topic to query
|
|
257
|
-
* @returns The last interaction score, or 0 if not found
|
|
258
|
-
*/
|
|
259
|
-
getLastInteraction: (host: string, topic: string) => Promise<number>
|
|
260
|
-
}
|