hypercore 10.0.0-alpha.13 → 10.0.0-alpha.17
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 +4 -1
- package/index.js +18 -6
- package/lib/core.js +3 -2
- package/lib/merkle-tree.js +17 -6
- package/lib/streams.js +18 -1
- package/package.json +7 -2
- package/.github/workflows/test-node.yml +0 -23
- package/UPGRADE.md +0 -15
- 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 -183
- 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.
|
|
@@ -84,7 +87,7 @@ Options include
|
|
|
84
87
|
|
|
85
88
|
``` js
|
|
86
89
|
{
|
|
87
|
-
wait: true, // wait for
|
|
90
|
+
wait: true, // wait for block to be downloaded
|
|
88
91
|
onwait: () => {}, // hook that is called if the get is waiting for download
|
|
89
92
|
timeout: 0, // wait at max some milliseconds (0 means no timeout)
|
|
90
93
|
valueEncoding: 'json' | 'utf-8' | 'binary' // defaults to the core's valueEncoding
|
package/index.js
CHANGED
|
@@ -14,7 +14,7 @@ const Replicator = require('./lib/replicator')
|
|
|
14
14
|
const Extensions = require('./lib/extensions')
|
|
15
15
|
const Core = require('./lib/core')
|
|
16
16
|
const BlockEncryption = require('./lib/block-encryption')
|
|
17
|
-
const { ReadStream } = require('./lib/streams')
|
|
17
|
+
const { ReadStream, WriteStream } = require('./lib/streams')
|
|
18
18
|
|
|
19
19
|
const promises = Symbol.for('hypercore.promises')
|
|
20
20
|
const inspect = Symbol.for('nodejs.util.inspect.custom')
|
|
@@ -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
|
|
@@ -131,7 +133,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
131
133
|
_passCapabilities (o) {
|
|
132
134
|
if (!this.sign) this.sign = o.sign
|
|
133
135
|
this.crypto = o.crypto
|
|
134
|
-
this.opened = o.opened
|
|
135
136
|
this.key = o.key
|
|
136
137
|
this.discoveryKey = o.discoveryKey
|
|
137
138
|
this.core = o.core
|
|
@@ -193,6 +194,9 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
193
194
|
if (opts.valueEncoding) {
|
|
194
195
|
this.valueEncoding = c.from(codecs(opts.valueEncoding))
|
|
195
196
|
}
|
|
197
|
+
if (opts.encodeBatch) {
|
|
198
|
+
this.encodeBatch = opts.encodeBatch
|
|
199
|
+
}
|
|
196
200
|
|
|
197
201
|
// This is a hidden option that's only used by Corestore.
|
|
198
202
|
// It's required so that corestore can load a name from userData before 'ready' is emitted.
|
|
@@ -205,7 +209,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
205
209
|
async _openCapabilities (keyPair, storage, opts) {
|
|
206
210
|
if (opts.from) return this._openFromExisting(opts.from, opts)
|
|
207
211
|
|
|
208
|
-
|
|
212
|
+
this.storage = Hypercore.defaultStorage(opts.storage || storage)
|
|
209
213
|
|
|
210
214
|
this.core = await Core.open(this.storage, {
|
|
211
215
|
keyPair,
|
|
@@ -412,6 +416,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
412
416
|
if (this.core.bitfield.get(index)) {
|
|
413
417
|
block = await this.core.blocks.get(index)
|
|
414
418
|
} else {
|
|
419
|
+
if (opts && opts.wait === false) return null
|
|
415
420
|
if (opts && opts.onwait) opts.onwait(index)
|
|
416
421
|
block = await this.replicator.requestBlock(index)
|
|
417
422
|
}
|
|
@@ -424,6 +429,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
424
429
|
return new ReadStream(this, opts)
|
|
425
430
|
}
|
|
426
431
|
|
|
432
|
+
createWriteStream (opts) {
|
|
433
|
+
return new WriteStream(this, opts)
|
|
434
|
+
}
|
|
435
|
+
|
|
427
436
|
download (range) {
|
|
428
437
|
const linear = !!(range && range.linear)
|
|
429
438
|
|
|
@@ -481,10 +490,13 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
481
490
|
blocks = Array.isArray(blocks) ? blocks : [blocks]
|
|
482
491
|
|
|
483
492
|
const preappend = this.encryption && this._preappend
|
|
484
|
-
const buffers = new Array(blocks.length)
|
|
485
493
|
|
|
486
|
-
|
|
487
|
-
|
|
494
|
+
const buffers = this.encodeBatch !== null ? this.encodeBatch(blocks) : new Array(blocks.length)
|
|
495
|
+
|
|
496
|
+
if (this.encodeBatch === null) {
|
|
497
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
498
|
+
buffers[i] = this._encode(this.valueEncoding, blocks[i])
|
|
499
|
+
}
|
|
488
500
|
}
|
|
489
501
|
|
|
490
502
|
return await this.core.append(buffers, this.sign, { preappend })
|
package/lib/core.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const hypercoreCrypto = require('hypercore-crypto')
|
|
2
|
+
const b4a = require('b4a')
|
|
2
3
|
const Oplog = require('./oplog')
|
|
3
4
|
const Mutex = require('./mutex')
|
|
4
5
|
const MerkleTree = require('./merkle-tree')
|
|
@@ -90,7 +91,7 @@ module.exports = class Core {
|
|
|
90
91
|
await oplog.flush(header)
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
if (opts.keyPair && !header.signer.publicKey
|
|
94
|
+
if (opts.keyPair && !b4a.equals(header.signer.publicKey, opts.keyPair.publicKey)) {
|
|
94
95
|
throw new Error('Another hypercore is stored here')
|
|
95
96
|
}
|
|
96
97
|
|
|
@@ -176,7 +177,7 @@ module.exports = class Core {
|
|
|
176
177
|
|
|
177
178
|
for (const u of this.header.userData) {
|
|
178
179
|
if (u.key !== key) continue
|
|
179
|
-
if (value &&
|
|
180
|
+
if (value && b4a.equals(u.value, value)) return
|
|
180
181
|
empty = false
|
|
181
182
|
break
|
|
182
183
|
}
|
package/lib/merkle-tree.js
CHANGED
|
@@ -215,7 +215,7 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
215
215
|
const nodes = []
|
|
216
216
|
const root = verifyBlock(proof, this.tree.crypto, nodes)
|
|
217
217
|
|
|
218
|
-
if (root === null || !root.hash
|
|
218
|
+
if (root === null || !b4a.equals(root.hash, this.diff.hash)) return false
|
|
219
219
|
|
|
220
220
|
this.nodes.push(...nodes)
|
|
221
221
|
return this._update(nodes)
|
|
@@ -233,7 +233,7 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
233
233
|
if (!left) break
|
|
234
234
|
|
|
235
235
|
const existing = await this.tree.get(left.index, false)
|
|
236
|
-
if (!existing || !existing.hash
|
|
236
|
+
if (!existing || !b4a.equals(existing.hash, left.hash)) {
|
|
237
237
|
diff = left
|
|
238
238
|
} else {
|
|
239
239
|
diff = n.get(ite.sibling())
|
|
@@ -355,7 +355,7 @@ module.exports = class MerkleTree {
|
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
addNode (node) {
|
|
358
|
-
if (node.size === 0 && node.hash
|
|
358
|
+
if (node.size === 0 && b4a.equals(node.hash, BLANK_HASH)) node = blankNode(node.index)
|
|
359
359
|
this.unflushed.set(node.index, node)
|
|
360
360
|
}
|
|
361
361
|
|
|
@@ -379,6 +379,17 @@ module.exports = class MerkleTree {
|
|
|
379
379
|
return this.signature !== null && this.crypto.verify(this.signable(), this.signature, key)
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
+
getRoots (length) {
|
|
383
|
+
const indexes = flat.fullRoots(2 * length)
|
|
384
|
+
const roots = new Array(indexes.length)
|
|
385
|
+
|
|
386
|
+
for (let i = 0; i < indexes.length; i++) {
|
|
387
|
+
roots[i] = this.get(indexes[i], true)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return Promise.all(roots)
|
|
391
|
+
}
|
|
392
|
+
|
|
382
393
|
get (index, error = true) {
|
|
383
394
|
let node = this.unflushed.get(index)
|
|
384
395
|
|
|
@@ -534,7 +545,7 @@ module.exports = class MerkleTree {
|
|
|
534
545
|
|
|
535
546
|
for (const root of batch.roots) {
|
|
536
547
|
const existing = await this.get(root.index, false)
|
|
537
|
-
if (existing && existing.hash
|
|
548
|
+
if (existing && b4a.equals(existing.hash, root.hash)) continue
|
|
538
549
|
batch._updateDiffRoot(root)
|
|
539
550
|
break
|
|
540
551
|
}
|
|
@@ -562,7 +573,7 @@ module.exports = class MerkleTree {
|
|
|
562
573
|
|
|
563
574
|
if (unverified) {
|
|
564
575
|
const verified = await this.get(unverified.index)
|
|
565
|
-
if (!verified.hash
|
|
576
|
+
if (!b4a.equals(verified.hash, unverified.hash)) {
|
|
566
577
|
throw new Error('Invalid checksum at node ' + unverified.index)
|
|
567
578
|
}
|
|
568
579
|
}
|
|
@@ -707,7 +718,7 @@ module.exports = class MerkleTree {
|
|
|
707
718
|
await new Promise((resolve, reject) => {
|
|
708
719
|
storage.read(0, OLD_TREE.length, (err, buf) => {
|
|
709
720
|
if (err) return resolve()
|
|
710
|
-
if (
|
|
721
|
+
if (b4a.equals(buf, OLD_TREE)) return reject(new Error('Storage contains an incompatible merkle tree'))
|
|
711
722
|
resolve()
|
|
712
723
|
})
|
|
713
724
|
})
|
package/lib/streams.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { Readable } = require('streamx')
|
|
1
|
+
const { Writable, Readable } = require('streamx')
|
|
2
2
|
|
|
3
3
|
class ReadStream extends Readable {
|
|
4
4
|
constructor (core, opts = {}) {
|
|
@@ -37,3 +37,20 @@ class ReadStream extends Readable {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
exports.ReadStream = ReadStream
|
|
40
|
+
|
|
41
|
+
class WriteStream extends Writable {
|
|
42
|
+
constructor (core) {
|
|
43
|
+
super()
|
|
44
|
+
this.core = core
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
_writev (batch, cb) {
|
|
48
|
+
this._writevP(batch).then(cb, cb)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async _writevP (batch) {
|
|
52
|
+
await this.core.append(batch)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
exports.WriteStream = WriteStream
|
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.17",
|
|
4
4
|
"description": "Hypercore 10",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -27,13 +27,18 @@
|
|
|
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",
|
|
33
37
|
"big-sparse-array": "^1.0.2",
|
|
34
|
-
"codecs": "^
|
|
38
|
+
"codecs": "^3.0.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
43
|
"hypercore-crypto": "^3.1.0",
|
|
39
44
|
"is-options": "^1.0.1",
|
|
@@ -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,15 +0,0 @@
|
|
|
1
|
-
# Upgrade Notes
|
|
2
|
-
|
|
3
|
-
Notes for downstream developers who are upgrading their modules to new, breaking versions of hypercore.
|
|
4
|
-
|
|
5
|
-
## 10.0.0
|
|
6
|
-
|
|
7
|
-
- All number encodings are now LE
|
|
8
|
-
- Introduces an "oplog" to atomically track changes locally
|
|
9
|
-
- Updated merkle format that only requires a single signature (stored in the oplog)
|
|
10
|
-
|
|
11
|
-
## 9.0.0
|
|
12
|
-
|
|
13
|
-
- 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.
|
|
14
|
-
- 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.
|
|
15
|
-
- 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.
|