@bsv/overlay 2.0.2 → 2.0.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.
- package/README.md +106 -226
- package/dist/cjs/package.json +2 -2
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/API.md +580 -72
- package/docs/examples/README.md +2 -2
- package/docs/examples/gs-wip.md +76 -68
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -23,243 +23,123 @@ The Overlay Services Engine enables dynamic tracking and management of UTXO-base
|
|
|
23
23
|
|
|
24
24
|
## Getting Started
|
|
25
25
|
|
|
26
|
+
### Choose the Right Package
|
|
27
|
+
|
|
28
|
+
Most application developers should start with the higher-level deployment tools:
|
|
29
|
+
|
|
30
|
+
- [`@bsv/overlay-express`](https://github.com/bsv-blockchain/overlay-express): opinionated Express wrapper for running overlay nodes with HTTP routes, health checks, storage configuration, SHIP/SLAP discovery, and GASP synchronization.
|
|
31
|
+
- [`@bsv/lars`](https://github.com/bsv-blockchain/lars): local runtime for BSV application projects that use `deployment-info.json`.
|
|
32
|
+
- [`@bsv/cars-cli`](https://github.com/bsv-blockchain/cars-cli): cloud deployment workflow for the same `deployment-info.json` project structure.
|
|
33
|
+
|
|
34
|
+
Use this package, `@bsv/overlay`, when you need direct control over the Overlay Services Engine itself, such as custom storage, custom broadcasting, or a non-Express host.
|
|
35
|
+
|
|
26
36
|
### Installation
|
|
27
37
|
|
|
28
|
-
|
|
38
|
+
For direct engine integration, install the engine with the current BSV TypeScript SDK:
|
|
29
39
|
|
|
30
40
|
```
|
|
31
|
-
npm i
|
|
41
|
+
npm i @bsv/overlay @bsv/sdk knex
|
|
32
42
|
```
|
|
33
43
|
|
|
34
44
|
### Basic Usage
|
|
35
45
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
knex
|
|
56
|
-
})
|
|
57
|
-
}),
|
|
58
|
-
},
|
|
59
|
-
new KnexStorageEngine({
|
|
60
|
-
knex
|
|
61
|
-
}),
|
|
62
|
-
new CombinatorialChainTracker([
|
|
63
|
-
new WhatsOnChain(
|
|
64
|
-
NODE_ENV === 'production' ? 'main' : 'test',
|
|
65
|
-
{
|
|
66
|
-
httpClient: new NodejsHttpClient(https)
|
|
67
|
-
})
|
|
68
|
-
]),
|
|
69
|
-
HOSTING_DOMAIN as string,
|
|
70
|
-
SHIP_TRACKERS,
|
|
71
|
-
SLAP_TRACKERS,
|
|
72
|
-
new ARC('https://arc.taal.com', arcConfig)
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
// This allows the API to be used everywhere when CORS is enforced
|
|
76
|
-
app.use((req, res, next) => {
|
|
77
|
-
res.header('Access-Control-Allow-Origin', '*')
|
|
78
|
-
res.header('Access-Control-Allow-Headers', '*')
|
|
79
|
-
res.header('Access-Control-Allow-Methods', '*')
|
|
80
|
-
res.header('Access-Control-Expose-Headers', '*')
|
|
81
|
-
res.header('Access-Control-Allow-Private-Network', 'true')
|
|
82
|
-
if (req.method === 'OPTIONS') {
|
|
83
|
-
res.sendStatus(200)
|
|
84
|
-
} else {
|
|
85
|
-
next()
|
|
86
|
-
}
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
// Serve a static documentstion site, if you have one.
|
|
90
|
-
app.use(express.static('public'))
|
|
91
|
-
|
|
92
|
-
// List hosted topic managers and lookup services
|
|
93
|
-
app.get(`/listTopicManagers`, async (req, res) => {
|
|
94
|
-
try {
|
|
95
|
-
const result = await engine.listTopicManagers()
|
|
96
|
-
return res.status(200).json(result)
|
|
97
|
-
} catch (error) {
|
|
98
|
-
return res.status(400).json({
|
|
99
|
-
status: 'error',
|
|
100
|
-
code: error.code,
|
|
101
|
-
description: error.message
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
})
|
|
105
|
-
app.get(`/listLookupServiceProviders`, async (req, res) => {
|
|
106
|
-
try {
|
|
107
|
-
const result = await engine.listLookupServiceProviders()
|
|
108
|
-
return res.status(200).json(result)
|
|
109
|
-
} catch (error) {
|
|
110
|
-
return res.status(400).json({
|
|
111
|
-
status: 'error',
|
|
112
|
-
code: error.code,
|
|
113
|
-
description: error.message
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
// Host documentation for the services
|
|
119
|
-
app.get(`/getDocumentationForTopicManager`, async (req, res) => {
|
|
120
|
-
try {
|
|
121
|
-
const result = await engine.getDocumentationForTopicManger(req.query.manager)
|
|
122
|
-
return res.status(200).json(result)
|
|
123
|
-
} catch (error) {
|
|
124
|
-
return res.status(400).json({
|
|
125
|
-
status: 'error',
|
|
126
|
-
code: error.code,
|
|
127
|
-
description: error.message
|
|
128
|
-
})
|
|
129
|
-
}
|
|
130
|
-
})
|
|
131
|
-
app.get(`/getDocumentationForLookupServiceProvider`, async (req, res) => {
|
|
132
|
-
try {
|
|
133
|
-
const result = await engine.getDocumentationForLookupServiceProvider(req.query.lookupServices)
|
|
134
|
-
return res.status(200).json(result)
|
|
135
|
-
} catch (error) {
|
|
136
|
-
return res.status(400).json({
|
|
137
|
-
status: 'error',
|
|
138
|
-
code: error.code,
|
|
139
|
-
description: error.message
|
|
140
|
-
})
|
|
141
|
-
}
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
// Submit transactions and facilitate lookup requests
|
|
145
|
-
app.post(`/submit`, async (req, res) => {
|
|
146
|
-
try {
|
|
147
|
-
// Parse out the topics and construct the tagged BEEF
|
|
148
|
-
const topics = JSON.parse(req.headers['x-topics'] as string)
|
|
149
|
-
const taggedBEEF: TaggedBEEF = {
|
|
150
|
-
beef: Array.from(req.body as number[]),
|
|
151
|
-
topics
|
|
46
|
+
Create an `Engine` with topic managers, lookup services, a storage implementation, and a chain tracker. The example below uses minimal in-memory stubs so the shape is clear. Production services should use durable storage, a real chain tracker, and a broadcaster appropriate for the target network.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import {
|
|
50
|
+
Engine,
|
|
51
|
+
type AdmittanceInstructions,
|
|
52
|
+
type LookupFormula,
|
|
53
|
+
type LookupQuestion,
|
|
54
|
+
type Output,
|
|
55
|
+
type Storage,
|
|
56
|
+
type TopicManager,
|
|
57
|
+
type LookupService
|
|
58
|
+
} from '@bsv/overlay'
|
|
59
|
+
|
|
60
|
+
const topicManager: TopicManager = {
|
|
61
|
+
async identifyAdmissibleOutputs (): Promise<AdmittanceInstructions> {
|
|
62
|
+
return {
|
|
63
|
+
outputsToAdmit: [],
|
|
64
|
+
coinsToRetain: []
|
|
152
65
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
})
|
|
165
|
-
}
|
|
166
|
-
})
|
|
167
|
-
app.post(`/lookup`, async (req, res) => {
|
|
168
|
-
try {
|
|
169
|
-
const result = await engine.lookup(req.body)
|
|
170
|
-
return res.status(200).json(result)
|
|
171
|
-
} catch (error) {
|
|
172
|
-
return res.status(400).json({
|
|
173
|
-
status: 'error',
|
|
174
|
-
code: error.code,
|
|
175
|
-
description: error.message
|
|
176
|
-
})
|
|
177
|
-
}
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
app.post('/arc-ingest', (req, res) => {
|
|
181
|
-
(async () => {
|
|
182
|
-
try {
|
|
183
|
-
const merklePath = MerklePath.fromHex(req.body.merklePath)
|
|
184
|
-
await engine.handleNewMerkleProof(req.body.txid, merklePath, req.body.blockHeight)
|
|
185
|
-
return res.status(200).json({ status: 'success', message: 'transaction status updated' })
|
|
186
|
-
} catch (error) {
|
|
187
|
-
console.error(error)
|
|
188
|
-
return res.status(400).json({
|
|
189
|
-
status: 'error',
|
|
190
|
-
message: error instanceof Error ? error.message : 'An unknown error occurred'
|
|
191
|
-
})
|
|
66
|
+
},
|
|
67
|
+
async getDocumentation () {
|
|
68
|
+
return 'Admits outputs for the example topic.'
|
|
69
|
+
},
|
|
70
|
+
async getMetaData () {
|
|
71
|
+
return {
|
|
72
|
+
name: 'Example Topic Manager',
|
|
73
|
+
shortDescription: 'Example topic manager',
|
|
74
|
+
iconURL: '',
|
|
75
|
+
version: '1.0.0',
|
|
76
|
+
informationURL: ''
|
|
192
77
|
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const topic = req.headers['x-bsv-topic'] as string
|
|
205
|
-
const response = await engine.provideForeignSyncResponse(req.body, topic)
|
|
206
|
-
return res.status(200).json(response)
|
|
207
|
-
} catch (error) {
|
|
208
|
-
console.error(error)
|
|
209
|
-
return res.status(400).json({
|
|
210
|
-
status: 'error',
|
|
211
|
-
message: error instanceof Error ? error.message : 'An unknown error occurred'
|
|
212
|
-
})
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const lookupService: LookupService = {
|
|
82
|
+
async outputAdmittedByTopic (): Promise<void> {},
|
|
83
|
+
async outputSpent (): Promise<void> {},
|
|
84
|
+
async outputEvicted (): Promise<void> {},
|
|
85
|
+
async lookup (_question: LookupQuestion): Promise<LookupFormula> {
|
|
86
|
+
return {
|
|
87
|
+
type: 'formula',
|
|
88
|
+
outpoints: []
|
|
213
89
|
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
console.log(req.body)
|
|
226
|
-
const { graphID, txid, outputIndex, metadata } = req.body
|
|
227
|
-
const response = await engine.provideForeignGASPNode(graphID, txid, outputIndex)
|
|
228
|
-
return res.status(200).json(response)
|
|
229
|
-
} catch (error) {
|
|
230
|
-
console.error(error)
|
|
231
|
-
return res.status(400).json({
|
|
232
|
-
status: 'error',
|
|
233
|
-
message: error instanceof Error ? error.message : 'An unknown error occurred'
|
|
234
|
-
})
|
|
90
|
+
},
|
|
91
|
+
async getDocumentation () {
|
|
92
|
+
return 'Looks up outputs admitted by the example topic.'
|
|
93
|
+
},
|
|
94
|
+
async getMetaData () {
|
|
95
|
+
return {
|
|
96
|
+
name: 'Example Lookup Service',
|
|
97
|
+
shortDescription: 'Example lookup service',
|
|
98
|
+
iconURL: '',
|
|
99
|
+
version: '1.0.0',
|
|
100
|
+
informationURL: ''
|
|
235
101
|
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const storage: Storage = {
|
|
106
|
+
async findOutput (): Promise<Output | null> { return null },
|
|
107
|
+
async findOutputsForTransaction (): Promise<Output[]> { return [] },
|
|
108
|
+
async findOutputsForTopic (): Promise<Output[]> { return [] },
|
|
109
|
+
async findUTXOHistory (): Promise<Output[]> { return [] },
|
|
110
|
+
async insertOutput (): Promise<void> {},
|
|
111
|
+
async updateConsumedBy (): Promise<void> {},
|
|
112
|
+
async updateTransactionBEEF (): Promise<void> {},
|
|
113
|
+
async deleteOutput (): Promise<void> {},
|
|
114
|
+
async startGASPSync (): Promise<void> {},
|
|
115
|
+
async updateLastInteraction (): Promise<void> {},
|
|
116
|
+
async getLastInteraction (): Promise<Date | null> { return null },
|
|
117
|
+
async getSyncState (): Promise<unknown> { return undefined },
|
|
118
|
+
async setSyncState (): Promise<void> {}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const engine = new Engine(
|
|
122
|
+
{ tm_example: topicManager },
|
|
123
|
+
{ ls_example: lookupService },
|
|
124
|
+
storage,
|
|
125
|
+
'scripts only',
|
|
126
|
+
'https://example-overlay.example',
|
|
127
|
+
[],
|
|
128
|
+
[],
|
|
129
|
+
undefined,
|
|
130
|
+
undefined,
|
|
131
|
+
{ tm_example: false }
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
const topics = await engine.listTopicManagers()
|
|
135
|
+
const services = await engine.listLookupServiceProviders()
|
|
258
136
|
```
|
|
259
137
|
|
|
260
|
-
For
|
|
138
|
+
For deployable HTTP examples, use [`@bsv/overlay-express`](https://github.com/bsv-blockchain/overlay-express) and [`overlay-express-examples`](https://github.com/bsv-blockchain/overlay-express-examples). For local and cloud application runtime workflows, use LARS and CARS with the BRC-102 `deployment-info.json` project structure.
|
|
139
|
+
|
|
140
|
+
For lower-level engine examples, check out the [full documentation](#documentation).
|
|
261
141
|
|
|
262
|
-
The Overlay Services Engine is also richly documented with code-level annotations. This should show up well within editors like VSCode.
|
|
142
|
+
The Overlay Services Engine is also richly documented with code-level annotations. This should show up well within editors like VSCode.
|
|
263
143
|
|
|
264
144
|
<!-- ## Documentation
|
|
265
145
|
|
|
@@ -271,10 +151,10 @@ The Overlay Services Engine is also richly documented with code-level annotation
|
|
|
271
151
|
- History management and state tracking
|
|
272
152
|
- Lookup Services
|
|
273
153
|
- Storage engine abstractions
|
|
274
|
-
-
|
|
275
|
-
-
|
|
276
|
-
-
|
|
277
|
-
-
|
|
154
|
+
- Examples, HTTP wrapper, and docs through `@bsv/overlay-express`
|
|
155
|
+
- ARC callback handling through overlay hosts
|
|
156
|
+
- Distributed overlay availability advertisements with SHIP and SLAP
|
|
157
|
+
- Federated transaction synchronization with GASP
|
|
278
158
|
|
|
279
159
|
## Contribution Guidelines
|
|
280
160
|
|
package/dist/cjs/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsv/overlay",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"description": "BSV Blockchain Overlay Services Engine",
|
|
6
6
|
"files": [
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@bsv/gasp": "^1.2.2",
|
|
52
|
-
"@bsv/sdk": "^2.0.
|
|
52
|
+
"@bsv/sdk": "^2.0.14",
|
|
53
53
|
"knex": "^3.1.0"
|
|
54
54
|
}
|
|
55
55
|
}
|