hypercore 10.0.0-alpha.14 → 10.0.0-alpha.18

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 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 index to be downloaded
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
@@ -90,9 +92,27 @@ module.exports = class Hypercore extends EventEmitter {
90
92
  indent + ')'
91
93
  }
92
94
 
93
- static createProtocolStream (isInitiator, opts) {
94
- const noiseStream = new NoiseSecretStream(isInitiator, null, opts)
95
- return noiseStream.rawStream
95
+ static createProtocolStream (isInitiator, opts = {}) {
96
+ let outerStream = isStream(isInitiator)
97
+ ? isInitiator
98
+ : opts.stream
99
+ let noiseStream = null
100
+
101
+ if (outerStream) {
102
+ noiseStream = outerStream.noiseStream
103
+ } else {
104
+ noiseStream = new NoiseSecretStream(isInitiator, null, opts)
105
+ outerStream = noiseStream.rawStream
106
+ }
107
+ if (!noiseStream) throw new Error('Invalid stream')
108
+
109
+ if (!noiseStream.userData) {
110
+ const protocol = Replicator.createProtocol(noiseStream)
111
+ noiseStream.userData = protocol
112
+ noiseStream.on('error', noop) // All noise errors already propagate through outerStream
113
+ }
114
+
115
+ return outerStream
96
116
  }
97
117
 
98
118
  static defaultStorage (storage, opts = {}) {
@@ -192,6 +212,9 @@ module.exports = class Hypercore extends EventEmitter {
192
212
  if (opts.valueEncoding) {
193
213
  this.valueEncoding = c.from(codecs(opts.valueEncoding))
194
214
  }
215
+ if (opts.encodeBatch) {
216
+ this.encodeBatch = opts.encodeBatch
217
+ }
195
218
 
196
219
  // This is a hidden option that's only used by Corestore.
197
220
  // It's required so that corestore can load a name from userData before 'ready' is emitted.
@@ -263,33 +286,17 @@ module.exports = class Hypercore extends EventEmitter {
263
286
  }
264
287
 
265
288
  replicate (isInitiator, opts = {}) {
266
- let outerStream = isStream(isInitiator)
267
- ? isInitiator
268
- : opts.stream
269
- let noiseStream = null
270
-
271
- if (outerStream) {
272
- noiseStream = outerStream.noiseStream
273
- } else {
274
- outerStream = Hypercore.createProtocolStream(isInitiator, opts)
275
- noiseStream = outerStream.noiseStream
276
- }
277
- if (!noiseStream) throw new Error('Invalid stream passed to replicate')
278
-
279
- if (!noiseStream.userData) {
280
- const protocol = Replicator.createProtocol(noiseStream)
281
- noiseStream.userData = protocol
282
- noiseStream.on('error', noop) // All noise errors already propagate through outerStream
283
- }
284
-
289
+ const protocolStream = Hypercore.createProtocolStream(isInitiator, opts)
290
+ const noiseStream = protocolStream.noiseStream
285
291
  const protocol = noiseStream.userData
292
+
286
293
  if (this.opened) {
287
294
  this.replicator.joinProtocol(protocol, this.key, this.discoveryKey)
288
295
  } else {
289
296
  this.opening.then(() => this.replicator.joinProtocol(protocol, this.key, this.discoveryKey), protocol.destroy.bind(protocol))
290
297
  }
291
298
 
292
- return outerStream
299
+ return protocolStream
293
300
  }
294
301
 
295
302
  get length () {
@@ -411,6 +418,7 @@ module.exports = class Hypercore extends EventEmitter {
411
418
  if (this.core.bitfield.get(index)) {
412
419
  block = await this.core.blocks.get(index)
413
420
  } else {
421
+ if (opts && opts.wait === false) return null
414
422
  if (opts && opts.onwait) opts.onwait(index)
415
423
  block = await this.replicator.requestBlock(index)
416
424
  }
@@ -423,6 +431,10 @@ module.exports = class Hypercore extends EventEmitter {
423
431
  return new ReadStream(this, opts)
424
432
  }
425
433
 
434
+ createWriteStream (opts) {
435
+ return new WriteStream(this, opts)
436
+ }
437
+
426
438
  download (range) {
427
439
  const linear = !!(range && range.linear)
428
440
 
@@ -480,10 +492,13 @@ module.exports = class Hypercore extends EventEmitter {
480
492
  blocks = Array.isArray(blocks) ? blocks : [blocks]
481
493
 
482
494
  const preappend = this.encryption && this._preappend
483
- const buffers = new Array(blocks.length)
484
495
 
485
- for (let i = 0; i < blocks.length; i++) {
486
- buffers[i] = this._encode(this.valueEncoding, blocks[i])
496
+ const buffers = this.encodeBatch !== null ? this.encodeBatch(blocks) : new Array(blocks.length)
497
+
498
+ if (this.encodeBatch === null) {
499
+ for (let i = 0; i < blocks.length; i++) {
500
+ buffers[i] = this._encode(this.valueEncoding, blocks[i])
501
+ }
487
502
  }
488
503
 
489
504
  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.equals(opts.keyPair.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 && u.value.equals(value)) return
180
+ if (value && b4a.equals(u.value, value)) return
180
181
  empty = false
181
182
  break
182
183
  }
@@ -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.equals(this.diff.hash)) return false
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.equals(left.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.equals(BLANK_HASH)) node = blankNode(node.index)
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.equals(root.hash)) continue
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.equals(unverified.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 (buf.equals(OLD_TREE)) return reject(new Error('Storage contains an incompatible merkle tree'))
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/protocol.js CHANGED
@@ -39,7 +39,7 @@ class Extension {
39
39
  _sendAlias (message, alias) {
40
40
  if (this.destroyed) return
41
41
 
42
- if (this._remoteAliases) {
42
+ if (this.remoteSupports) {
43
43
  return this.protocol.send(this.type, this.encoding, alias, message)
44
44
  }
45
45
 
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.14",
3
+ "version": "10.0.0-alpha.18",
4
4
  "description": "Hypercore 10",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -35,9 +35,10 @@
35
35
  "@hyperswarm/secret-stream": "^5.0.0",
36
36
  "b4a": "^1.1.0",
37
37
  "big-sparse-array": "^1.0.2",
38
- "codecs": "^2.2.0",
38
+ "codecs": "^3.0.0",
39
39
  "compact-encoding": "^2.5.0",
40
40
  "crc32-universal": "^1.0.1",
41
+ "events": "^3.3.0",
41
42
  "flat-tree": "^1.9.0",
42
43
  "hypercore-crypto": "^3.1.0",
43
44
  "is-options": "^1.0.1",