@bsv/overlay 2.0.3 → 2.0.4
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 +226 -106
- package/dist/cjs/mod.js +6 -1
- package/dist/cjs/mod.js.map +1 -1
- package/dist/cjs/package.json +13 -11
- package/dist/cjs/src/BASM.js +92 -0
- package/dist/cjs/src/BASM.js.map +1 -0
- package/dist/cjs/src/BASMRemote.js +43 -0
- package/dist/cjs/src/BASMRemote.js.map +1 -0
- package/dist/cjs/src/Engine.js +649 -47
- package/dist/cjs/src/Engine.js.map +1 -1
- package/dist/cjs/src/GASP/OverlayGASPRemote.js +2 -2
- package/dist/cjs/src/GASP/OverlayGASPRemote.js.map +1 -1
- package/dist/cjs/src/GASP/OverlayGASPStorage.js +18 -25
- package/dist/cjs/src/GASP/OverlayGASPStorage.js.map +1 -1
- package/dist/cjs/src/storage/knex/KnexStorage.js +329 -12
- package/dist/cjs/src/storage/knex/KnexStorage.js.map +1 -1
- package/dist/cjs/src/storage/knex/all-migrations.js +3 -1
- package/dist/cjs/src/storage/knex/all-migrations.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.js +2 -24
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.js +114 -0
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/mod.js +1 -0
- package/dist/esm/mod.js.map +1 -1
- package/dist/esm/src/BASM.js +85 -0
- package/dist/esm/src/BASM.js.map +1 -0
- package/dist/esm/src/BASMRemote.js +42 -0
- package/dist/esm/src/BASMRemote.js.map +1 -0
- package/dist/esm/src/Engine.js +637 -46
- 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 +18 -25
- package/dist/esm/src/GASP/OverlayGASPStorage.js.map +1 -1
- package/dist/esm/src/storage/knex/KnexStorage.js +323 -12
- package/dist/esm/src/storage/knex/KnexStorage.js.map +1 -1
- package/dist/esm/src/storage/knex/all-migrations.js +3 -1
- package/dist/esm/src/storage/knex/all-migrations.js.map +1 -1
- package/dist/esm/src/storage/knex/migrations/2025-05-28-001-enlarge.js +2 -24
- package/dist/esm/src/storage/knex/migrations/2025-05-28-001-enlarge.js.map +1 -1
- package/dist/esm/src/storage/knex/migrations/2026-05-29-001-brc136-basm.js +109 -0
- package/dist/esm/src/storage/knex/migrations/2026-05-29-001-brc136-basm.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/BASM.d.ts +110 -0
- package/dist/types/src/BASM.d.ts.map +1 -0
- package/dist/types/src/BASMRemote.d.ts +14 -0
- package/dist/types/src/BASMRemote.d.ts.map +1 -0
- package/dist/types/src/Engine.d.ts +78 -1
- package/dist/types/src/Engine.d.ts.map +1 -1
- package/dist/types/src/GASP/OverlayGASPStorage.d.ts +1 -1
- package/dist/types/src/GASP/OverlayGASPStorage.d.ts.map +1 -1
- package/dist/types/src/LookupService.d.ts.map +1 -1
- package/dist/types/src/storage/Storage.d.ts +118 -0
- package/dist/types/src/storage/Storage.d.ts.map +1 -1
- package/dist/types/src/storage/knex/KnexStorage.d.ts +38 -6
- 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/dist/types/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts.map +1 -1
- package/dist/types/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts +4 -0
- package/dist/types/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/API.md +72 -580
- package/docs/BRC-136-BASM.md +285 -0
- package/docs/README.md +2 -0
- package/docs/Synchronization.md +10 -1
- package/docs/examples/README.md +2 -2
- package/docs/examples/gs-wip.md +68 -76
- package/mod.ts +25 -1
- package/package.json +25 -23
- package/src/BASM.ts +208 -0
- package/src/BASMRemote.ts +54 -0
- package/src/Engine.ts +762 -47
- package/src/GASP/OverlayGASPRemote.ts +2 -2
- package/src/GASP/OverlayGASPStorage.ts +17 -22
- package/src/LookupService.ts +2 -1
- package/src/__tests/BASM.test.ts +44 -0
- package/src/__tests/BASMChain.test.ts +290 -0
- package/src/__tests/Engine.test.ts +3 -4
- package/src/storage/Storage.ts +127 -0
- package/src/storage/knex/KnexStorage.ts +376 -14
- package/src/storage/knex/all-migrations.ts +3 -1
- package/src/storage/knex/migrations/2025-05-28-001-enlarge.ts +2 -22
- package/src/storage/knex/migrations/2026-05-29-001-brc136-basm.ts +123 -0
package/README.md
CHANGED
|
@@ -23,123 +23,243 @@ 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
|
-
|
|
36
26
|
### Installation
|
|
37
27
|
|
|
38
|
-
|
|
28
|
+
You'll usually want to wrap the Engine within an HTTP server. To get set up with Express, create a new project and install everything you'll need:
|
|
39
29
|
|
|
40
30
|
```
|
|
41
|
-
npm i @bsv/
|
|
31
|
+
npm i express body-parser @bsv/sdk @bsv/overlay hello-services knex
|
|
42
32
|
```
|
|
43
33
|
|
|
44
34
|
### Basic Usage
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
36
|
+
In your server's main file, you can set everything up. Create a new Engine to run the overlay services you want, then expose some routes over HTTP. For example:
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
const express = require('express')
|
|
40
|
+
const bodyparser = require('body-parser')
|
|
41
|
+
const { Engine, KnexStorage, HelloTopicManager, HelloLookupService, HelloStorageEngine } = require('@bsv/overlay')
|
|
42
|
+
import { WhatsOnChain, NodejsHttpClient, ARC, ArcConfig, MerklePath } from '@bsv/sdk'
|
|
43
|
+
// Populate a Knexfile with your database credentials
|
|
44
|
+
const knex = require('knex')(require('../knexfile.js'))
|
|
45
|
+
const app = express()
|
|
46
|
+
app.use(bodyparser.json({ limit: '1gb', type: 'application/json' }))
|
|
47
|
+
|
|
48
|
+
const engine = new Engine(
|
|
49
|
+
{
|
|
50
|
+
hello: new HelloTopicManager(),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
hello: new HelloLookupService({
|
|
54
|
+
storageEngine: new HelloStorageEngine({
|
|
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
|
|
77
152
|
}
|
|
153
|
+
|
|
154
|
+
// Using a callback function, we can just return once our steak is ready
|
|
155
|
+
// instead of having to wait for all the broadcasts to occur.
|
|
156
|
+
await engine.submit(taggedBEEF, (steak: STEAK) => {
|
|
157
|
+
return res.status(200).json(steak)
|
|
158
|
+
})
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return res.status(400).json({
|
|
161
|
+
status: 'error',
|
|
162
|
+
code: error.code,
|
|
163
|
+
description: error.message
|
|
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
|
+
})
|
|
78
177
|
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
async
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
+
})
|
|
89
192
|
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
193
|
+
})().catch(() => {
|
|
194
|
+
res.status(500).json({
|
|
195
|
+
status: 'error',
|
|
196
|
+
message: 'Unexpected error'
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
app.post('/requestSyncResponse', (req, res) => {
|
|
202
|
+
(async () => {
|
|
203
|
+
try {
|
|
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
|
+
})
|
|
101
213
|
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
async
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
214
|
+
})().catch(() => {
|
|
215
|
+
res.status(500).json({
|
|
216
|
+
status: 'error',
|
|
217
|
+
message: 'Unexpected error'
|
|
218
|
+
})
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
app.post('/requestForeignGASPNode', (req, res) => {
|
|
223
|
+
(async () => {
|
|
224
|
+
try {
|
|
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
|
+
})
|
|
235
|
+
}
|
|
236
|
+
})().catch(() => {
|
|
237
|
+
res.status(500).json({
|
|
238
|
+
status: 'error',
|
|
239
|
+
message: 'Unexpected error'
|
|
240
|
+
})
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
// 404, all other routes are not found.
|
|
245
|
+
app.use((req, res) => {
|
|
246
|
+
console.log('404', req.url)
|
|
247
|
+
res.status(404).json({
|
|
248
|
+
status: 'error',
|
|
249
|
+
code: 'ERR_ROUTE_NOT_FOUND',
|
|
250
|
+
description: 'Route not found.'
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
// Start your Engines!
|
|
255
|
+
app.listen(8080, () => {
|
|
256
|
+
console.log('BSV Overlay Services Engine is listening on port', 8080)
|
|
257
|
+
})
|
|
136
258
|
```
|
|
137
259
|
|
|
138
|
-
For
|
|
139
|
-
|
|
140
|
-
For lower-level engine examples, check out the [full documentation](#documentation).
|
|
260
|
+
For more detailed tutorials and examples, check out the [full documentation](#documentation).
|
|
141
261
|
|
|
142
|
-
The Overlay Services Engine is also richly documented with code-level annotations. This should show up well within editors like VSCode.
|
|
262
|
+
The Overlay Services Engine is also richly documented with code-level annotations. This should show up well within editors like VSCode.
|
|
143
263
|
|
|
144
264
|
<!-- ## Documentation
|
|
145
265
|
|
|
@@ -151,10 +271,10 @@ The Overlay Services Engine is also richly documented with code-level annotation
|
|
|
151
271
|
- History management and state tracking
|
|
152
272
|
- Lookup Services
|
|
153
273
|
- Storage engine abstractions
|
|
154
|
-
- Examples, HTTP wrapper
|
|
155
|
-
-
|
|
156
|
-
- Distributed
|
|
157
|
-
- Federated
|
|
274
|
+
- [WIP] Examples, HTTP wrapper and Docs
|
|
275
|
+
- [WIP] Arc Proof Acquisition
|
|
276
|
+
- [WIP] Distributed Overlay Availability Advertisements
|
|
277
|
+
- [WIP] Federated Transaction Synchronization
|
|
158
278
|
|
|
159
279
|
## Contribution Guidelines
|
|
160
280
|
|
package/dist/cjs/mod.js
CHANGED
|
@@ -33,10 +33,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.KnexStorageMigrations = exports.KnexStorage = exports.Engine = void 0;
|
|
36
|
+
exports.KnexStorageMigrations = exports.KnexStorage = exports.extractMerkleProofMetadata = exports.computeTac = exports.computeBasmRoot = exports.BASM_ZERO_HASH = exports.Engine = void 0;
|
|
37
37
|
// Fundamentals
|
|
38
38
|
var Engine_js_1 = require("./src/Engine.js");
|
|
39
39
|
Object.defineProperty(exports, "Engine", { enumerable: true, get: function () { return Engine_js_1.Engine; } });
|
|
40
|
+
var BASM_js_1 = require("./src/BASM.js");
|
|
41
|
+
Object.defineProperty(exports, "BASM_ZERO_HASH", { enumerable: true, get: function () { return BASM_js_1.BASM_ZERO_HASH; } });
|
|
42
|
+
Object.defineProperty(exports, "computeBasmRoot", { enumerable: true, get: function () { return BASM_js_1.computeBasmRoot; } });
|
|
43
|
+
Object.defineProperty(exports, "computeTac", { enumerable: true, get: function () { return BASM_js_1.computeTac; } });
|
|
44
|
+
Object.defineProperty(exports, "extractMerkleProofMetadata", { enumerable: true, get: function () { return BASM_js_1.extractMerkleProofMetadata; } });
|
|
40
45
|
// The Knex storage system
|
|
41
46
|
var KnexStorage_js_1 = require("./src/storage/knex/KnexStorage.js");
|
|
42
47
|
Object.defineProperty(exports, "KnexStorage", { enumerable: true, get: function () { return KnexStorage_js_1.KnexStorage; } });
|
package/dist/cjs/mod.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../../mod.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,eAAe;AACf,6CAAwC;AAA/B,mGAAA,MAAM,OAAA;
|
|
1
|
+
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../../mod.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,eAAe;AACf,6CAAwC;AAA/B,mGAAA,MAAM,OAAA;AAWf,yCAKsB;AAJpB,yGAAA,cAAc,OAAA;AACd,0GAAA,eAAe,OAAA;AACf,qGAAA,UAAU,OAAA;AACV,qHAAA,0BAA0B,OAAA;AAqB5B,0BAA0B;AAC1B,oEAA+D;AAAtD,6GAAA,WAAW,OAAA;AACpB,8FAA6E"}
|
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.4",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"description": "BSV Blockchain Overlay Services Engine",
|
|
6
6
|
"files": [
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
},
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|
|
26
|
-
"url": "git+https://github.com/bsv-blockchain/
|
|
26
|
+
"url": "git+https://github.com/bsv-blockchain/ts-stack.git",
|
|
27
|
+
"directory": "packages/overlays/overlay"
|
|
27
28
|
},
|
|
28
29
|
"keywords": [
|
|
29
30
|
"BSV",
|
|
@@ -35,21 +36,22 @@
|
|
|
35
36
|
"author": "BSV Association",
|
|
36
37
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
37
38
|
"bugs": {
|
|
38
|
-
"url": "https://github.com/
|
|
39
|
+
"url": "https://github.com/bsv-blockchain/ts-stack/issues"
|
|
39
40
|
},
|
|
40
|
-
"homepage": "https://github.com/
|
|
41
|
+
"homepage": "https://github.com/bsv-blockchain/ts-stack/tree/main/packages/overlays/overlay#readme",
|
|
41
42
|
"devDependencies": {
|
|
42
|
-
"@types/jest": "^
|
|
43
|
-
"
|
|
44
|
-
"
|
|
43
|
+
"@types/jest": "^30.0.0",
|
|
44
|
+
"@types/node": "^25.9.3",
|
|
45
|
+
"jest": "^30.4.2",
|
|
46
|
+
"ts-jest": "^29.4.11",
|
|
45
47
|
"ts-standard": "^12.0.2",
|
|
46
48
|
"ts2md": "^0.2.0",
|
|
47
49
|
"tsconfig-to-dual-package": "^1.2.0",
|
|
48
|
-
"typescript": "^
|
|
50
|
+
"typescript": "^6.0.3"
|
|
49
51
|
},
|
|
50
52
|
"dependencies": {
|
|
51
|
-
"@bsv/gasp": "^1.2.
|
|
52
|
-
"@bsv/sdk": "^2.
|
|
53
|
-
"knex": "^3.
|
|
53
|
+
"@bsv/gasp": "^1.2.3",
|
|
54
|
+
"@bsv/sdk": "^2.1.4",
|
|
55
|
+
"knex": "^3.2.10"
|
|
54
56
|
}
|
|
55
57
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BASM_ZERO_HASH = void 0;
|
|
4
|
+
exports.computeBasmRoot = computeBasmRoot;
|
|
5
|
+
exports.computeTac = computeTac;
|
|
6
|
+
exports.extractMerkleProofMetadata = extractMerkleProofMetadata;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
exports.BASM_ZERO_HASH = '0000000000000000000000000000000000000000000000000000000000000000';
|
|
9
|
+
function sha256d(buffer) {
|
|
10
|
+
const first = (0, node_crypto_1.createHash)('sha256').update(buffer).digest();
|
|
11
|
+
return (0, node_crypto_1.createHash)('sha256').update(first).digest();
|
|
12
|
+
}
|
|
13
|
+
function assertHashHex(hash, label) {
|
|
14
|
+
if (!/^[0-9a-fA-F]{64}$/.test(hash)) {
|
|
15
|
+
throw new Error(`${label} must be 32 bytes of hex`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function displayHexToInternal(hash) {
|
|
19
|
+
assertHashHex(hash, 'hash');
|
|
20
|
+
return Buffer.from(hash, 'hex').reverse();
|
|
21
|
+
}
|
|
22
|
+
function internalToDisplayHex(hash) {
|
|
23
|
+
return Buffer.from(hash).reverse().toString('hex');
|
|
24
|
+
}
|
|
25
|
+
function normalizeAdmittedTxids(admitted) {
|
|
26
|
+
return admitted
|
|
27
|
+
.map((item, originalIndex) => {
|
|
28
|
+
if (typeof item === 'string') {
|
|
29
|
+
return { txid: item, blockIndex: originalIndex };
|
|
30
|
+
}
|
|
31
|
+
return item;
|
|
32
|
+
})
|
|
33
|
+
.sort((a, b) => a.blockIndex - b.blockIndex)
|
|
34
|
+
.map(item => item.txid.toLowerCase());
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Computes a BRC-136 BASM root from admitted topic txids.
|
|
38
|
+
*
|
|
39
|
+
* The API accepts normal display-order txid hex because that is what the BSV
|
|
40
|
+
* TypeScript stack exposes at its public boundaries. Hashing is performed on
|
|
41
|
+
* internal byte order as required by the BRC, and the returned root is display
|
|
42
|
+
* order for JSON/wire compatibility.
|
|
43
|
+
*/
|
|
44
|
+
function computeBasmRoot(admitted) {
|
|
45
|
+
const txids = normalizeAdmittedTxids(admitted);
|
|
46
|
+
if (txids.length === 0) {
|
|
47
|
+
return exports.BASM_ZERO_HASH;
|
|
48
|
+
}
|
|
49
|
+
let layer = txids.map(txid => displayHexToInternal(txid));
|
|
50
|
+
if (layer.length === 1) {
|
|
51
|
+
return internalToDisplayHex(layer[0]);
|
|
52
|
+
}
|
|
53
|
+
while (layer.length > 1) {
|
|
54
|
+
const next = [];
|
|
55
|
+
for (let i = 0; i < layer.length; i += 2) {
|
|
56
|
+
const left = layer[i];
|
|
57
|
+
const right = i + 1 < layer.length ? layer[i + 1] : left;
|
|
58
|
+
next.push(sha256d(Buffer.concat([left, right])));
|
|
59
|
+
}
|
|
60
|
+
layer = next;
|
|
61
|
+
}
|
|
62
|
+
return internalToDisplayHex(layer[0]);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Computes the BRC-136 cumulative Topic Anchor Chain hash:
|
|
66
|
+
* SHA256d(prevTac || blockHash || basmRoot), with all inputs reversed to
|
|
67
|
+
* internal byte order before hashing and the output returned as display hex.
|
|
68
|
+
*/
|
|
69
|
+
function computeTac(prevTac, blockHash, basmRoot) {
|
|
70
|
+
const input = Buffer.concat([
|
|
71
|
+
displayHexToInternal(prevTac.toLowerCase()),
|
|
72
|
+
displayHexToInternal(blockHash.toLowerCase()),
|
|
73
|
+
displayHexToInternal(basmRoot.toLowerCase())
|
|
74
|
+
]);
|
|
75
|
+
return internalToDisplayHex(sha256d(input));
|
|
76
|
+
}
|
|
77
|
+
function extractMerkleProofMetadata(txid, proof) {
|
|
78
|
+
var _a;
|
|
79
|
+
if (proof === undefined) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
const leaf = (_a = proof.path[0]) === null || _a === void 0 ? void 0 : _a.find(candidate => candidate.hash === txid);
|
|
83
|
+
if (leaf === undefined) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
blockHeight: proof.blockHeight,
|
|
88
|
+
blockIndex: leaf.offset,
|
|
89
|
+
merkleRoot: proof.computeRoot(txid)
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=BASM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BASM.js","sourceRoot":"","sources":["../../../src/BASM.ts"],"names":[],"mappings":";;;AAyJA,0CAuBC;AAOD,gCAOC;AAED,gEAeC;AA/MD,6CAAwC;AAG3B,QAAA,cAAc,GAAG,kEAAkE,CAAA;AA8GhG,SAAS,OAAO,CAAC,MAAc;IAC7B,MAAM,KAAK,GAAG,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAA;IAC1D,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAA;AACpD,CAAC;AAED,SAAS,aAAa,CAAC,IAAY,EAAE,KAAa;IAChD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,0BAA0B,CAAC,CAAA;IACrD,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAY;IACxC,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAC3B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAA;AAC3C,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAY;IACxC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AACpD,CAAC;AAED,SAAS,sBAAsB,CAAC,QAA0B;IACxD,OAAO,QAAQ;SACZ,GAAG,CAAC,CAAC,IAAI,EAAE,aAAa,EAAE,EAAE;QAC3B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,CAAA;QAClD,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;SAC3C,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;AACzC,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,eAAe,CAAC,QAA0B;IACxD,MAAM,KAAK,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAA;IAE9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,sBAAc,CAAA;IACvB,CAAC;IAED,IAAI,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAA;IACzD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IACvC,CAAC;IAED,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,GAAa,EAAE,CAAA;QACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;YACrB,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;YACxD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAClD,CAAC;QACD,KAAK,GAAG,IAAI,CAAA;IACd,CAAC;IAED,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AACvC,CAAC;AAED;;;;GAIG;AACH,SAAgB,UAAU,CAAC,OAAe,EAAE,SAAiB,EAAE,QAAgB;IAC7E,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;QAC1B,oBAAoB,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QAC3C,oBAAoB,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;QAC7C,oBAAoB,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;KAC7C,CAAC,CAAA;IACF,OAAO,oBAAoB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;AAC7C,CAAC;AAED,SAAgB,0BAA0B,CAAC,IAAY,EAAE,KAAkB;;IACzE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,MAAM,IAAI,GAAG,MAAA,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,0CAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;IACtE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,OAAO;QACL,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,UAAU,EAAE,IAAI,CAAC,MAAM;QACvB,UAAU,EAAE,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC;KACpC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BASMRemote = void 0;
|
|
4
|
+
class BASMRemote {
|
|
5
|
+
constructor(endpoint, topic, fetchImpl = fetch.bind(globalThis)) {
|
|
6
|
+
this.endpoint = endpoint;
|
|
7
|
+
this.topic = topic;
|
|
8
|
+
this.fetchImpl = fetchImpl;
|
|
9
|
+
}
|
|
10
|
+
async requestTopicAnchorTip() {
|
|
11
|
+
return await this.post('/requestTopicAnchorTip', {});
|
|
12
|
+
}
|
|
13
|
+
async requestTopicAnchorRange(fromHeight, toHeight) {
|
|
14
|
+
return await this.post('/requestTopicAnchorRange', { fromHeight, toHeight });
|
|
15
|
+
}
|
|
16
|
+
async requestAdmittedList(blockHeight, blockHash) {
|
|
17
|
+
return await this.post('/requestAdmittedList', { blockHeight, blockHash });
|
|
18
|
+
}
|
|
19
|
+
async requestCompoundMerklePath(blockHeight, txids) {
|
|
20
|
+
return await this.post('/requestCompoundMerklePath', { blockHeight, txids });
|
|
21
|
+
}
|
|
22
|
+
async requestRawTransactions(txids) {
|
|
23
|
+
return await this.post('/requestRawTransactions', { txids });
|
|
24
|
+
}
|
|
25
|
+
async post(path, body) {
|
|
26
|
+
const response = await this.fetchImpl(new URL(path, this.endpoint).toString(), {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: {
|
|
29
|
+
Accept: 'application/json',
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
'x-bsv-topic': this.topic
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify(body)
|
|
34
|
+
});
|
|
35
|
+
const text = await response.text();
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
throw new Error(`BASM peer ${this.endpoint} returned ${response.status}: ${text}`);
|
|
38
|
+
}
|
|
39
|
+
return (text.length === 0 ? {} : JSON.parse(text));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.BASMRemote = BASMRemote;
|
|
43
|
+
//# sourceMappingURL=BASMRemote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BASMRemote.js","sourceRoot":"","sources":["../../../src/BASMRemote.ts"],"names":[],"mappings":";;;AAQA,MAAa,UAAU;IACrB,YACmB,QAAgB,EAChB,KAAa,EACb,YAA0B,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;QAFhD,aAAQ,GAAR,QAAQ,CAAQ;QAChB,UAAK,GAAL,KAAK,CAAQ;QACb,cAAS,GAAT,SAAS,CAAuC;IAC/D,CAAC;IAEL,KAAK,CAAC,qBAAqB;QACzB,OAAO,MAAM,IAAI,CAAC,IAAI,CAAiB,wBAAwB,EAAE,EAAE,CAAC,CAAA;IACtE,CAAC;IAED,KAAK,CAAC,uBAAuB,CAAC,UAAkB,EAAE,QAAgB;QAChE,OAAO,MAAM,IAAI,CAAC,IAAI,CAA2B,0BAA0B,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAA;IACxG,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,WAAmB,EAAE,SAAkB;QAC/D,OAAO,MAAM,IAAI,CAAC,IAAI,CAAuB,sBAAsB,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAA;IAClG,CAAC;IAED,KAAK,CAAC,yBAAyB,CAAC,WAAmB,EAAE,KAAe;QAClE,OAAO,MAAM,IAAI,CAAC,IAAI,CAA6B,4BAA4B,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAA;IAC1G,CAAC;IAED,KAAK,CAAC,sBAAsB,CAAC,KAAe;QAC1C,OAAO,MAAM,IAAI,CAAC,IAAI,CAAyB,yBAAyB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IACtF,CAAC;IAEO,KAAK,CAAC,IAAI,CAAI,IAAY,EAAE,IAAa;QAC/C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,EAAE;YAC7E,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,MAAM,EAAE,kBAAkB;gBAC1B,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,IAAI,CAAC,KAAK;aAC1B;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;QAClC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,aAAa,IAAI,CAAC,QAAQ,aAAa,QAAQ,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAA;QACpF,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAM,CAAA;IACzD,CAAC;CACF;AA7CD,gCA6CC"}
|