hypercore 10.0.0-alpha.11 → 10.0.0-alpha.15
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 +3 -0
- package/index.js +106 -80
- package/package.json +7 -2
- package/.github/workflows/test-node.yml +0 -23
- package/UPGRADE.md +0 -9
- package/__snapshots__/test/storage.js.snapshot.cjs +0 -15
- package/examples/announce.js +0 -19
- package/examples/basic.js +0 -10
- package/examples/http.js +0 -123
- package/examples/lookup.js +0 -20
- package/test/basic.js +0 -90
- package/test/bitfield.js +0 -71
- package/test/core.js +0 -290
- package/test/encodings.js +0 -18
- package/test/encryption.js +0 -123
- package/test/extension.js +0 -71
- package/test/helpers/index.js +0 -23
- package/test/merkle-tree.js +0 -518
- package/test/mutex.js +0 -137
- package/test/oplog.js +0 -399
- package/test/preload.js +0 -72
- package/test/replicate.js +0 -372
- package/test/sessions.js +0 -173
- package/test/storage.js +0 -31
- package/test/streams.js +0 -55
- package/test/user-data.js +0 -47
package/README.md
CHANGED
|
@@ -63,6 +63,7 @@ Note that `tree`, `data`, and `bitfield` are normally heavily sparse files.
|
|
|
63
63
|
createIfMissing: true, // create a new Hypercore key pair if none was present in storage
|
|
64
64
|
overwrite: false, // overwrite any old Hypercore that might already exist
|
|
65
65
|
valueEncoding: 'json' | 'utf-8' | 'binary', // defaults to binary
|
|
66
|
+
encodeBatch: batch => { ... }, // optionally apply an encoding to complete batches
|
|
66
67
|
keyPair: kp, // optionally pass the public key and secret key as a key pair
|
|
67
68
|
encryptionKey: k // optionally pass an encryption key to enable block encryption
|
|
68
69
|
}
|
|
@@ -70,6 +71,8 @@ Note that `tree`, `data`, and `bitfield` are normally heavily sparse files.
|
|
|
70
71
|
|
|
71
72
|
You can also set valueEncoding to any [abstract-encoding](https://github.com/mafintosh/abstract-encoding) or [compact-encoding](https://github.com/compact-encoding) instance.
|
|
72
73
|
|
|
74
|
+
valueEncodings will be applied to individually blocks, even if you append batches. If you want to control encoding at the batch-level, you can use the `encodeBatch` option, which is a function that takes a batch and returns a binary-encoded batch. If you provide a custom valueEncoding, it will not be applied prior to `encodeBatch`.
|
|
75
|
+
|
|
73
76
|
#### `const seq = await core.append(block)`
|
|
74
77
|
|
|
75
78
|
Append a block of data (or an array of blocks) to the core.
|
package/index.js
CHANGED
|
@@ -55,6 +55,8 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
55
55
|
this.cache = opts.cache === true ? new Xache({ maxSize: 65536, maxAge: 0 }) : (opts.cache || null)
|
|
56
56
|
|
|
57
57
|
this.valueEncoding = null
|
|
58
|
+
this.encodeBatch = null
|
|
59
|
+
|
|
58
60
|
this.key = key || null
|
|
59
61
|
this.discoveryKey = null
|
|
60
62
|
this.readable = true
|
|
@@ -66,7 +68,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
66
68
|
this.autoClose = !!opts.autoClose
|
|
67
69
|
|
|
68
70
|
this.closing = null
|
|
69
|
-
this.opening =
|
|
71
|
+
this.opening = this._openSession(key, storage, opts)
|
|
70
72
|
this.opening.catch(noop)
|
|
71
73
|
|
|
72
74
|
this._preappend = preappend.bind(this)
|
|
@@ -115,31 +117,22 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
const Clz = opts.class || Hypercore
|
|
118
|
-
const keyPair = opts.keyPair && opts.keyPair.secretKey && { ...opts.keyPair }
|
|
119
|
-
|
|
120
|
-
// This only works if the hypercore was fully loaded,
|
|
121
|
-
// but we only do this to validate the keypair to help catch bugs so yolo
|
|
122
|
-
if (this.key && keyPair) keyPair.publicKey = this.key
|
|
123
|
-
|
|
124
120
|
const s = new Clz(this.storage, this.key, {
|
|
125
121
|
...opts,
|
|
126
|
-
sign: opts.sign || (keyPair && keyPair.secretKey && Core.createSigner(this.crypto, keyPair)) || this.sign,
|
|
127
|
-
valueEncoding: this.valueEncoding,
|
|
128
122
|
extensions: this.extensions,
|
|
129
123
|
_opening: this.opening,
|
|
130
124
|
_sessions: this.sessions
|
|
131
125
|
})
|
|
132
126
|
|
|
133
|
-
s.
|
|
127
|
+
s._passCapabilities(this)
|
|
134
128
|
this.sessions.push(s)
|
|
135
129
|
|
|
136
130
|
return s
|
|
137
131
|
}
|
|
138
132
|
|
|
139
|
-
|
|
133
|
+
_passCapabilities (o) {
|
|
140
134
|
if (!this.sign) this.sign = o.sign
|
|
141
135
|
this.crypto = o.crypto
|
|
142
|
-
this.opened = o.opened
|
|
143
136
|
this.key = o.key
|
|
144
137
|
this.discoveryKey = o.discoveryKey
|
|
145
138
|
this.core = o.core
|
|
@@ -149,6 +142,101 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
149
142
|
this.autoClose = o.autoClose
|
|
150
143
|
}
|
|
151
144
|
|
|
145
|
+
async _openFromExisting (from, opts) {
|
|
146
|
+
await from.opening
|
|
147
|
+
|
|
148
|
+
for (const [name, ext] of this.extensions) {
|
|
149
|
+
from.extensions.register(name, null, ext)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this._passCapabilities(from)
|
|
153
|
+
this.extensions = from.extensions
|
|
154
|
+
this.sessions = from.sessions
|
|
155
|
+
this.storage = from.storage
|
|
156
|
+
|
|
157
|
+
this.sessions.push(this)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async _openSession (key, storage, opts) {
|
|
161
|
+
const isFirst = !opts._opening
|
|
162
|
+
|
|
163
|
+
if (!isFirst) await opts._opening
|
|
164
|
+
if (opts.preload) opts = { ...opts, ...(await opts.preload()) }
|
|
165
|
+
|
|
166
|
+
const keyPair = (key && opts.keyPair)
|
|
167
|
+
? { ...opts.keyPair, publicKey: key }
|
|
168
|
+
: key
|
|
169
|
+
? { publicKey: key, secretKey: null }
|
|
170
|
+
: opts.keyPair
|
|
171
|
+
|
|
172
|
+
// This only works if the hypercore was fully loaded,
|
|
173
|
+
// but we only do this to validate the keypair to help catch bugs so yolo
|
|
174
|
+
if (this.key && keyPair) keyPair.publicKey = this.key
|
|
175
|
+
|
|
176
|
+
if (opts.sign) {
|
|
177
|
+
this.sign = opts.sign
|
|
178
|
+
} else if (keyPair && keyPair.secretKey) {
|
|
179
|
+
this.sign = Core.createSigner(this.crypto, keyPair)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (isFirst) {
|
|
183
|
+
await this._openCapabilities(keyPair, storage, opts)
|
|
184
|
+
// Only the root session should pass capabilities to other sessions.
|
|
185
|
+
for (let i = 0; i < this.sessions.length; i++) {
|
|
186
|
+
const s = this.sessions[i]
|
|
187
|
+
if (s !== this) s._passCapabilities(this)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!this.sign) this.sign = this.core.defaultSign
|
|
192
|
+
this.writable = !!this.sign
|
|
193
|
+
|
|
194
|
+
if (opts.valueEncoding) {
|
|
195
|
+
this.valueEncoding = c.from(codecs(opts.valueEncoding))
|
|
196
|
+
}
|
|
197
|
+
if (opts.encodeBatch) {
|
|
198
|
+
this.encodeBatch = opts.encodeBatch
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// This is a hidden option that's only used by Corestore.
|
|
202
|
+
// It's required so that corestore can load a name from userData before 'ready' is emitted.
|
|
203
|
+
if (opts._preready) await opts._preready(this)
|
|
204
|
+
|
|
205
|
+
this.opened = true
|
|
206
|
+
this.emit('ready')
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async _openCapabilities (keyPair, storage, opts) {
|
|
210
|
+
if (opts.from) return this._openFromExisting(opts.from, opts)
|
|
211
|
+
|
|
212
|
+
this.storage = Hypercore.defaultStorage(opts.storage || storage)
|
|
213
|
+
|
|
214
|
+
this.core = await Core.open(this.storage, {
|
|
215
|
+
keyPair,
|
|
216
|
+
crypto: this.crypto,
|
|
217
|
+
onupdate: this._oncoreupdate.bind(this)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
if (opts.userData) {
|
|
221
|
+
for (const [key, value] of Object.entries(opts.userData)) {
|
|
222
|
+
await this.core.userData(key, value)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
this.replicator = new Replicator(this.core, {
|
|
227
|
+
onupdate: this._onpeerupdate.bind(this)
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
this.discoveryKey = this.crypto.discoveryKey(this.core.header.signer.publicKey)
|
|
231
|
+
this.key = this.core.header.signer.publicKey
|
|
232
|
+
|
|
233
|
+
if (!this.encryption && opts.encryptionKey) {
|
|
234
|
+
this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
this.extensions.attach(this.replicator)
|
|
238
|
+
}
|
|
239
|
+
|
|
152
240
|
close () {
|
|
153
241
|
if (this.closing) return this.closing
|
|
154
242
|
this.closing = this._close()
|
|
@@ -237,71 +325,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
237
325
|
return this.opening
|
|
238
326
|
}
|
|
239
327
|
|
|
240
|
-
async _open (key, storage, opts) {
|
|
241
|
-
if (opts.preload) opts = { ...opts, ...(await opts.preload()) }
|
|
242
|
-
|
|
243
|
-
this.valueEncoding = opts.valueEncoding ? c.from(codecs(opts.valueEncoding)) : null
|
|
244
|
-
|
|
245
|
-
const keyPair = (key && opts.keyPair)
|
|
246
|
-
? { ...opts.keyPair, publicKey: key }
|
|
247
|
-
: key
|
|
248
|
-
? { publicKey: key, secretKey: null }
|
|
249
|
-
: opts.keyPair
|
|
250
|
-
|
|
251
|
-
if (opts.from) {
|
|
252
|
-
const from = opts.from
|
|
253
|
-
await from.opening
|
|
254
|
-
for (const [name, ext] of this.extensions) from.extensions.register(name, null, ext)
|
|
255
|
-
this._initSession(from)
|
|
256
|
-
this.extensions = from.extensions
|
|
257
|
-
this.sessions = from.sessions
|
|
258
|
-
this.storage = from.storage
|
|
259
|
-
if (!this.sign) this.sign = opts.sign || ((keyPair && keyPair.secretKey) ? Core.createSigner(this.crypto, keyPair) : null)
|
|
260
|
-
this.writable = !!this.sign
|
|
261
|
-
this.sessions.push(this)
|
|
262
|
-
return
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (!this.storage) this.storage = Hypercore.defaultStorage(opts.storage || storage)
|
|
266
|
-
|
|
267
|
-
this.core = await Core.open(this.storage, {
|
|
268
|
-
keyPair,
|
|
269
|
-
crypto: this.crypto,
|
|
270
|
-
onupdate: this._oncoreupdate.bind(this)
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
if (opts.userData) {
|
|
274
|
-
for (const [key, value] of Object.entries(opts.userData)) {
|
|
275
|
-
await this.core.userData(key, value)
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
this.replicator = new Replicator(this.core, {
|
|
280
|
-
onupdate: this._onpeerupdate.bind(this)
|
|
281
|
-
})
|
|
282
|
-
|
|
283
|
-
if (!this.sign) this.sign = opts.sign || this.core.defaultSign
|
|
284
|
-
|
|
285
|
-
this.discoveryKey = this.crypto.discoveryKey(this.core.header.signer.publicKey)
|
|
286
|
-
this.key = this.core.header.signer.publicKey
|
|
287
|
-
this.writable = !!this.sign
|
|
288
|
-
|
|
289
|
-
if (!this.encryption && opts.encryptionKey) {
|
|
290
|
-
this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
this.extensions.attach(this.replicator)
|
|
294
|
-
this.opened = true
|
|
295
|
-
|
|
296
|
-
if (opts.postload) await opts.postload(this)
|
|
297
|
-
|
|
298
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
299
|
-
const s = this.sessions[i]
|
|
300
|
-
if (s !== this) s._initSession(this)
|
|
301
|
-
s.emit('ready')
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
328
|
_oncoreupdate (status, bitfield, value, from) {
|
|
306
329
|
if (status !== 0) {
|
|
307
330
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
@@ -462,10 +485,13 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
462
485
|
blocks = Array.isArray(blocks) ? blocks : [blocks]
|
|
463
486
|
|
|
464
487
|
const preappend = this.encryption && this._preappend
|
|
465
|
-
const buffers = new Array(blocks.length)
|
|
466
488
|
|
|
467
|
-
|
|
468
|
-
|
|
489
|
+
const buffers = this.encodeBatch !== null ? this.encodeBatch(blocks) : new Array(blocks.length)
|
|
490
|
+
|
|
491
|
+
if (this.encodeBatch === null) {
|
|
492
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
493
|
+
buffers[i] = this._encode(this.valueEncoding, blocks[i])
|
|
494
|
+
}
|
|
469
495
|
}
|
|
470
496
|
|
|
471
497
|
return await this.core.append(buffers, this.sign, { preappend })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore",
|
|
3
|
-
"version": "10.0.0-alpha.
|
|
3
|
+
"version": "10.0.0-alpha.15",
|
|
4
4
|
"description": "Hypercore 10",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
"url": "https://github.com/hypercore-protocol/hypercore/issues"
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/hypercore-protocol/hypercore#readme",
|
|
30
|
+
"files": [
|
|
31
|
+
"index.js",
|
|
32
|
+
"lib/**.js"
|
|
33
|
+
],
|
|
30
34
|
"dependencies": {
|
|
31
35
|
"@hyperswarm/secret-stream": "^5.0.0",
|
|
32
36
|
"b4a": "^1.1.0",
|
|
@@ -34,8 +38,9 @@
|
|
|
34
38
|
"codecs": "^2.2.0",
|
|
35
39
|
"compact-encoding": "^2.5.0",
|
|
36
40
|
"crc32-universal": "^1.0.1",
|
|
41
|
+
"events": "^3.3.0",
|
|
37
42
|
"flat-tree": "^1.9.0",
|
|
38
|
-
"hypercore-crypto": "^
|
|
43
|
+
"hypercore-crypto": "^3.1.0",
|
|
39
44
|
"is-options": "^1.0.1",
|
|
40
45
|
"random-access-file": "^2.1.4",
|
|
41
46
|
"random-array-iterator": "^1.0.0",
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
name: Build Status
|
|
2
|
-
on:
|
|
3
|
-
push:
|
|
4
|
-
branches:
|
|
5
|
-
- master
|
|
6
|
-
pull_request:
|
|
7
|
-
branches:
|
|
8
|
-
- master
|
|
9
|
-
jobs:
|
|
10
|
-
build:
|
|
11
|
-
strategy:
|
|
12
|
-
matrix:
|
|
13
|
-
node-version: [lts/*]
|
|
14
|
-
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
15
|
-
runs-on: ${{ matrix.os }}
|
|
16
|
-
steps:
|
|
17
|
-
- uses: actions/checkout@v2
|
|
18
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
19
|
-
uses: actions/setup-node@v2
|
|
20
|
-
with:
|
|
21
|
-
node-version: ${{ matrix.node-version }}
|
|
22
|
-
- run: npm install
|
|
23
|
-
- run: npm test
|
package/UPGRADE.md
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# Upgrade Notes
|
|
2
|
-
|
|
3
|
-
Notes for downstream developers who are upgrading their modules to new, breaking versions of hypercore.
|
|
4
|
-
|
|
5
|
-
## 9.0.0
|
|
6
|
-
|
|
7
|
-
- The format of signatures [has been changed](https://github.com/mafintosh/hypercore/issues/260). This is backwards-compatible (v9 can read v8 signatures), but forward-incompatible (v8 cannot read v9 signatures). If a v8 peer replicates with a v9 peer, it will emit a "REMOTE SIGNTURE INVALID" error on the replication stream.
|
|
8
|
-
- The encryption ([NOISE](https://github.com/emilbayes/noise-protocol)) handshake has been changed in an backwards- and forwards-incompatible way. v8 peers can not handshake with v9 peers, and vice-versa. A NOISE-related error is emitted on the replication stream.
|
|
9
|
-
- There is no way (yet) to detect whether a peer is running an incompatible version of hypercore at the replication level. One workaround for downstream developers is to include their own application-level handshake before piping to the replication stream, to communicate a "app protocol version" (maybe "v8" and "v9") and abort the connection if the peer is running an incompatible version.
|