hypercore 10.0.0-alpha.22 → 10.0.0-alpha.25

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
@@ -101,6 +101,10 @@ Truncate the core to a smaller length.
101
101
  Per default this will update the fork id of the core to `+ 1`, but you can set the fork id you prefer with the option.
102
102
  Note that the fork id should be monotonely incrementing.
103
103
 
104
+ #### `const hash = await core.treeHash([length])`
105
+
106
+ Get the Merkle Tree hash of the core at a given length, defaulting to the current length of the core.
107
+
104
108
  #### `const stream = core.createReadStream([options])`
105
109
 
106
110
  Make a read stream. Options include:
@@ -212,6 +216,12 @@ Buffer containing the public key identifying this core.
212
216
 
213
217
  Populated after `ready` has been emitted. Will be `null` before the event.
214
218
 
219
+ #### `core.keyPair`
220
+
221
+ Object containing buffers of the core's public and secret key
222
+
223
+ Populated after `ready` has been emitted. Will be `null` before the event.
224
+
215
225
  #### `core.discoveryKey`
216
226
 
217
227
  Buffer containing a key derived from the core's public key.
@@ -274,6 +284,6 @@ socket.pipe(localCore.replicate(true)).pipe(socket)
274
284
 
275
285
  Emitted when the core has been appended to (i.e. has a new length / byteLength), either locally or remotely.
276
286
 
277
- #### `core.on('truncate')`
287
+ #### `core.on('truncate', ancestors, forkId)`
278
288
 
279
289
  Emitted when the core has been truncated, either locally or remotely.
package/index.js CHANGED
@@ -58,6 +58,7 @@ module.exports = class Hypercore extends EventEmitter {
58
58
  this.encodeBatch = null
59
59
 
60
60
  this.key = key || null
61
+ this.keyPair = null
61
62
  this.discoveryKey = null
62
63
  this.readable = true
63
64
  this.writable = false
@@ -72,6 +73,7 @@ module.exports = class Hypercore extends EventEmitter {
72
73
  this.opening.catch(noop)
73
74
 
74
75
  this._preappend = preappend.bind(this)
76
+ this._snapshot = opts.snapshot || null
75
77
  }
76
78
 
77
79
  [inspect] (depth, opts) {
@@ -107,7 +109,7 @@ module.exports = class Hypercore extends EventEmitter {
107
109
  if (!noiseStream) throw new Error('Invalid stream')
108
110
 
109
111
  if (!noiseStream.userData) {
110
- const protocol = Replicator.createProtocol(noiseStream)
112
+ const protocol = Replicator.createProtocol(noiseStream, opts)
111
113
  if (opts.keepAlive !== false) protocol.setKeepAlive(true)
112
114
  noiseStream.userData = protocol
113
115
  noiseStream.on('error', noop) // All noise errors already propagate through outerStream
@@ -128,6 +130,10 @@ module.exports = class Hypercore extends EventEmitter {
128
130
  }
129
131
  }
130
132
 
133
+ snapshot () {
134
+ return this.session({ snapshot: { length: this.length, byteLength: this.byteLength, fork: this.fork } })
135
+ }
136
+
131
137
  session (opts = {}) {
132
138
  if (this.closing) {
133
139
  // This makes the closing logic alot easier. If this turns out to be a problem
@@ -245,11 +251,13 @@ module.exports = class Hypercore extends EventEmitter {
245
251
  }
246
252
 
247
253
  this.replicator = new Replicator(this.core, {
248
- onupdate: this._onpeerupdate.bind(this)
254
+ onupdate: this._onpeerupdate.bind(this),
255
+ onupload: this._onupload.bind(this)
249
256
  })
250
257
 
251
258
  this.discoveryKey = this.crypto.discoveryKey(this.core.header.signer.publicKey)
252
259
  this.key = this.core.header.signer.publicKey
260
+ this.keyPair = this.core.header.signer
253
261
 
254
262
  if (!this.encryption && opts.encryptionKey) {
255
263
  this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
@@ -274,6 +282,7 @@ module.exports = class Hypercore extends EventEmitter {
274
282
  this.readable = false
275
283
  this.writable = false
276
284
  this.closed = true
285
+ this.opened = false
277
286
 
278
287
  if (this.sessions.length) {
279
288
  // if this is the last session and we are auto closing, trigger that first to enforce error handling
@@ -303,15 +312,21 @@ module.exports = class Hypercore extends EventEmitter {
303
312
  }
304
313
 
305
314
  get length () {
306
- return this.core === null ? 0 : this.core.tree.length
315
+ return this._snapshot
316
+ ? this._snapshot.length
317
+ : (this.core === null ? 0 : this.core.tree.length)
307
318
  }
308
319
 
309
320
  get byteLength () {
310
- return this.core === null ? 0 : this.core.tree.byteLength - (this.core.tree.length * this.padding)
321
+ return this._snapshot
322
+ ? this._snapshot.byteLength
323
+ : (this.core === null ? 0 : this.core.tree.byteLength - (this.core.tree.length * this.padding))
311
324
  }
312
325
 
313
326
  get fork () {
314
- return this.core === null ? 0 : this.core.tree.fork
327
+ return this._snapshot
328
+ ? this._snapshot.fork
329
+ : (this.core === null ? 0 : this.core.tree.fork)
315
330
  }
316
331
 
317
332
  get peers () {
@@ -330,12 +345,20 @@ module.exports = class Hypercore extends EventEmitter {
330
345
  return this.opening
331
346
  }
332
347
 
348
+ _onupload (index, value, from) {
349
+ const byteLength = value.byteLength - this.padding
350
+
351
+ for (let i = 0; i < this.sessions.length; i++) {
352
+ this.sessions[i].emit('upload', index, byteLength, from)
353
+ }
354
+ }
355
+
333
356
  _oncoreupdate (status, bitfield, value, from) {
334
357
  if (status !== 0) {
335
358
  for (let i = 0; i < this.sessions.length; i++) {
336
359
  if ((status & 0b10) !== 0) {
337
360
  if (this.cache) this.cache.clear()
338
- this.sessions[i].emit('truncate', this.core.tree.fork)
361
+ this.sessions[i].emit('truncate', bitfield.start, this.core.tree.fork)
339
362
  }
340
363
  if ((status & 0b01) !== 0) {
341
364
  this.sessions[i].emit('append')
@@ -507,6 +530,16 @@ module.exports = class Hypercore extends EventEmitter {
507
530
  return await this.core.append(buffers, this.sign, { preappend })
508
531
  }
509
532
 
533
+ async treeHash (length) {
534
+ if (length === undefined) {
535
+ await this.ready()
536
+ length = this.core.length
537
+ }
538
+
539
+ const roots = await this.core.tree.getRoots(length)
540
+ return this.crypto.tree(roots)
541
+ }
542
+
510
543
  registerExtension (name, handlers) {
511
544
  return this.extensions.register(name, handlers)
512
545
  }
@@ -392,6 +392,21 @@ module.exports = class MerkleTree {
392
392
  return Promise.all(roots)
393
393
  }
394
394
 
395
+ async upgradeable (length) {
396
+ const indexes = flat.fullRoots(2 * length)
397
+ const roots = new Array(indexes.length)
398
+
399
+ for (let i = 0; i < indexes.length; i++) {
400
+ roots[i] = this.get(indexes[i], false)
401
+ }
402
+
403
+ for (const node of await Promise.all(roots)) {
404
+ if (node === null) return false
405
+ }
406
+
407
+ return true
408
+ }
409
+
395
410
  get (index, error = true) {
396
411
  let node = this.unflushed.get(index)
397
412
 
package/lib/messages.js CHANGED
@@ -256,16 +256,25 @@ exports.info = {
256
256
  preencode (state, i) {
257
257
  c.uint.preencode(state, i.length)
258
258
  c.uint.preencode(state, i.fork)
259
+ c.uint.preencode(state, 1) // flags
259
260
  },
260
261
  encode (state, i) {
261
262
  c.uint.encode(state, i.length)
262
263
  c.uint.encode(state, i.fork)
264
+ c.uint.encode(state, (i.uploading ? 1 : 0) | (i.downloading ? 2 : 0))
263
265
  },
264
266
  decode (state) {
265
- return {
267
+ const i = {
266
268
  length: c.uint.decode(state),
267
- fork: c.uint.decode(state)
269
+ fork: c.uint.decode(state),
270
+ uploading: true,
271
+ downloading: true
268
272
  }
273
+ if (state.end <= state.start) return i // backwards compat with prev alphas
274
+ const flags = c.uint.decode(state)
275
+ i.uploading = (flags & 1) !== 0
276
+ i.downloading = (flags & 2) !== 0
277
+ return i
269
278
  }
270
279
  }
271
280
 
package/lib/protocol.js CHANGED
@@ -136,11 +136,34 @@ class Peer {
136
136
  this.resend = false
137
137
  this.state = state
138
138
  this.extensions = new Map()
139
+ this.uploading = true
140
+ this.downloading = true
139
141
  this.destroyed = false
140
142
 
141
143
  this._destroyer = this._safeDestroy.bind(this)
142
144
  }
143
145
 
146
+ setUploading (uploading) {
147
+ if (uploading === this.uploading) return
148
+ this.uploading = uploading
149
+ this._sendInfo()
150
+ }
151
+
152
+ setDownloading (downloading) {
153
+ if (downloading === this.downloading) return
154
+ this.downloading = downloading
155
+ this._sendInfo()
156
+ }
157
+
158
+ _sendInfo () {
159
+ this.info({
160
+ length: this.handlers.core.tree.length,
161
+ fork: this.handlers.core.tree.fork,
162
+ uploading: this.uploading,
163
+ downloading: this.downloading
164
+ })
165
+ }
166
+
144
167
  onmessage (type, state) {
145
168
  const handlers = this.handlers
146
169
 
package/lib/replicator.js CHANGED
@@ -14,6 +14,8 @@ class RemoteState {
14
14
  this.bitfield = new RemoteBitfield()
15
15
  this.length = 0
16
16
  this.fork = 0
17
+ this.uploading = true
18
+ this.downloading = true
17
19
  }
18
20
  }
19
21
 
@@ -241,6 +243,7 @@ class RequestPool {
241
243
 
242
244
  update (peer) {
243
245
  if (peer.state.inflight >= peer.state.maxInflight) return false
246
+ if (peer.downloading === false || peer.state.uploading === false) return false
244
247
  if (this.paused) return false
245
248
 
246
249
  if (peer.state.fork > this.core.tree.fork) {
@@ -624,17 +627,19 @@ class RequestPool {
624
627
  }
625
628
 
626
629
  module.exports = class Replicator {
627
- constructor (core, { onupdate }) {
630
+ constructor (core, { onupdate, onupload }) {
628
631
  this.core = core
629
632
  this.peers = []
630
633
  this.requests = new RequestPool(this, core)
631
634
  this.updating = null
632
635
  this.pendingPeers = new Set()
633
636
  this.onupdate = onupdate
637
+ this.onupload = onupload
634
638
  }
635
639
 
636
- static createProtocol (noiseStream) {
640
+ static createProtocol (noiseStream, opts) {
637
641
  return new Protocol(noiseStream, {
642
+ ...opts,
638
643
  protocolVersion: 0,
639
644
  userAgent: USER_AGENT
640
645
  })
@@ -658,8 +663,14 @@ module.exports = class Replicator {
658
663
  }
659
664
 
660
665
  broadcastInfo () {
661
- const msg = { length: this.core.tree.length, fork: this.core.tree.fork }
662
- for (const peer of this.peers) peer.info(msg)
666
+ for (const peer of this.peers) {
667
+ peer.info({
668
+ length: this.core.tree.length,
669
+ fork: this.core.tree.fork,
670
+ uploading: peer.uploading,
671
+ downloading: peer.downloading
672
+ })
673
+ }
663
674
  this.updateAll()
664
675
  }
665
676
 
@@ -737,7 +748,12 @@ module.exports = class Replicator {
737
748
  this.peers.push(peer)
738
749
  this.onupdate(true, peer)
739
750
 
740
- peer.info({ length: this.core.tree.length, fork: this.core.tree.fork })
751
+ peer.info({
752
+ length: this.core.tree.length,
753
+ fork: this.core.tree.fork,
754
+ uploading: peer.uploading,
755
+ downloading: peer.downloading
756
+ })
741
757
 
742
758
  // YOLO send over all the pages for now
743
759
  const p = pages(this.core)
@@ -752,12 +768,14 @@ module.exports = class Replicator {
752
768
  // This is a no-op because there isn't any state to dealloc currently
753
769
  }
754
770
 
755
- oninfo ({ length, fork }, peer) {
771
+ oninfo ({ length, fork, uploading, downloading }, peer) {
756
772
  const len = peer.state.length
757
773
  const forked = peer.state.fork !== fork
758
774
 
759
775
  peer.state.length = length
760
776
  peer.state.fork = fork
777
+ peer.state.downloading = downloading
778
+ peer.state.uploading = uploading
761
779
 
762
780
  if (forked) {
763
781
  for (let i = peer.state.length; i < len; i++) {
@@ -813,11 +831,13 @@ module.exports = class Replicator {
813
831
  async onrequest (req, peer) {
814
832
  const fork = req.fork || peer.state.fork
815
833
  if (fork !== this.core.tree.fork) return
834
+ if (!peer.uploading) return
816
835
 
817
836
  const proof = await this.core.tree.proof(req)
818
837
 
819
838
  if (req.block && req.block.value) {
820
839
  proof.block.value = await this.core.blocks.get(req.block.index)
840
+ this.onupload(req.block.index, proof.block.value, peer)
821
841
  }
822
842
 
823
843
  peer.data(proof)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.0.0-alpha.22",
3
+ "version": "10.0.0-alpha.25",
4
4
  "description": "Hypercore 10",
5
5
  "main": "index.js",
6
6
  "scripts": {