hypercore 10.0.0-alpha.1 → 10.0.0-alpha.5

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.
@@ -1,5 +1,4 @@
1
1
  name: Build Status
2
-
3
2
  on:
4
3
  push:
5
4
  branches:
@@ -11,7 +10,7 @@ jobs:
11
10
  build:
12
11
  strategy:
13
12
  matrix:
14
- node-version: [14.x]
13
+ node-version: [lts/*]
15
14
  os: [ubuntu-latest, macos-latest, windows-latest]
16
15
  runs-on: ${{ matrix.os }}
17
16
  steps:
package/index.js CHANGED
@@ -3,6 +3,7 @@ const raf = require('random-access-file')
3
3
  const isOptions = require('is-options')
4
4
  const hypercoreCrypto = require('hypercore-crypto')
5
5
  const c = require('compact-encoding')
6
+ const Xache = require('xache')
6
7
  const NoiseSecretStream = require('@hyperswarm/secret-stream')
7
8
  const codecs = require('codecs')
8
9
 
@@ -44,6 +45,7 @@ module.exports = class Hypercore extends EventEmitter {
44
45
  this.core = null
45
46
  this.replicator = null
46
47
  this.extensions = opts.extensions || new Extensions()
48
+ this.cache = opts.cache === true ? new Xache({ maxSize: 65536, maxAge: 0 }) : (opts.cache || null)
47
49
 
48
50
  this.valueEncoding = null
49
51
  this.key = key || null
@@ -89,8 +91,9 @@ module.exports = class Hypercore extends EventEmitter {
89
91
  const directory = storage
90
92
  const toLock = opts.lock || 'oplog'
91
93
  return function createFile (name) {
92
- const lock = name === toLock ? fsctl.lock : null
93
- const sparse = name !== toLock ? fsctl.sparse : null
94
+ const locked = name === toLock || name.endsWith('/' + toLock)
95
+ const lock = locked ? fsctl.lock : null
96
+ const sparse = locked ? null : null // fsctl.sparse, disable sparse on windows - seems to fail for some people. TODO: investigate
94
97
  return raf(name, { directory, lock, sparse })
95
98
  }
96
99
  }
@@ -280,8 +283,13 @@ module.exports = class Hypercore extends EventEmitter {
280
283
  _oncoreupdate (status, bitfield, value, from) {
281
284
  if (status !== 0) {
282
285
  for (let i = 0; i < this.sessions.length; i++) {
283
- if ((status & 0b10) !== 0) this.sessions[i].emit('truncate', this.core.tree.fork)
284
- if ((status & 0b01) !== 0) this.sessions[i].emit('append')
286
+ if ((status & 0b10) !== 0) {
287
+ if (this.cache) this.cache.clear()
288
+ this.sessions[i].emit('truncate', this.core.tree.fork)
289
+ }
290
+ if ((status & 0b01) !== 0) {
291
+ this.sessions[i].emit('append')
292
+ }
285
293
  }
286
294
 
287
295
  this.replicator.broadcastInfo()
@@ -345,6 +353,15 @@ module.exports = class Hypercore extends EventEmitter {
345
353
 
346
354
  async get (index, opts) {
347
355
  if (this.opened === false) await this.opening
356
+ const c = this.cache && this.cache.get(index)
357
+ if (c) return c
358
+ const fork = this.core.tree.fork
359
+ const b = await this._get(index, opts)
360
+ if (this.cache && fork === this.core.tree.fork && b) this.cache.set(index, b)
361
+ return b
362
+ }
363
+
364
+ async _get (index, opts) {
348
365
  const encoding = (opts && opts.valueEncoding && c.from(codecs(opts.valueEncoding))) || this.valueEncoding
349
366
 
350
367
  if (this.core.bitfield.get(index)) return decode(encoding, await this.core.blocks.get(index))
package/lib/protocol.js CHANGED
@@ -125,6 +125,7 @@ class Peer {
125
125
  this.resend = false
126
126
  this.state = state
127
127
  this.extensions = new Map()
128
+ this.destroyed = false
128
129
 
129
130
  this._destroyer = this._safeDestroy.bind(this)
130
131
  }
@@ -229,6 +230,7 @@ class Peer {
229
230
  }
230
231
 
231
232
  destroy (err) {
233
+ this.destroyed = true
232
234
  return this.protocol.unregisterPeer(this, err)
233
235
  }
234
236
  }
@@ -296,7 +298,7 @@ module.exports = class Protocol {
296
298
  peer.remoteAlias = -1
297
299
  }
298
300
 
299
- peer.handlers.onunregister(this, err)
301
+ peer.handlers.onunregister(peer, err)
300
302
 
301
303
  if (err) this.noiseStream.destroy(err)
302
304
  }
package/lib/replicator.js CHANGED
@@ -25,11 +25,12 @@ class InvertedPromise {
25
25
  }
26
26
 
27
27
  class Request {
28
- constructor (index, seek) {
28
+ constructor (index, seek, nodes) {
29
29
  this.peer = null
30
30
  this.index = index
31
31
  this.seek = seek
32
32
  this.value = seek === 0
33
+ this.nodes = nodes
33
34
  this.promises = []
34
35
  }
35
36
 
@@ -278,7 +279,9 @@ class RequestPool {
278
279
 
279
280
  _updateSeek (peer, seek) {
280
281
  if (seek.request) return false
281
- seek.request = this._requestRange(peer, seek.seeker.start, seek.seeker.end, seek.seeker.bytes)
282
+ // We have to snapshot the nodes here now, due to the caching of the request...
283
+ const nodes = log2(seek.seeker.end - seek.seeker.start)
284
+ seek.request = this._requestRange(peer, seek.seeker.start, seek.seeker.end, seek.seeker.bytes, nodes)
282
285
  return seek.request !== null
283
286
  }
284
287
 
@@ -296,10 +299,10 @@ class RequestPool {
296
299
  const end = range.end === -1 ? peer.state.length : range.end
297
300
  if (end <= range._start) return false
298
301
 
299
- if (range.linear) return !!this._requestRange(peer, range._start, end, 0)
302
+ if (range.linear) return !!this._requestRange(peer, range._start, end, 0, 0)
300
303
 
301
304
  const r = range._start + Math.floor(Math.random() * (end - range._start))
302
- return !!(this._requestRange(peer, r, end, 0) || this._requestRange(peer, range._start, r, 0))
305
+ return !!(this._requestRange(peer, r, end, 0, 0) || this._requestRange(peer, range._start, r, 0, 0))
303
306
  }
304
307
 
305
308
  _updateUpgrade (peer) {
@@ -328,7 +331,7 @@ class RequestPool {
328
331
  this.pendingUpgrade = null
329
332
  }
330
333
 
331
- _requestRange (peer, start, end, seek) {
334
+ _requestRange (peer, start, end, seek, nodes) {
332
335
  const remote = peer.state.bitfield
333
336
  const local = this.core.bitfield
334
337
 
@@ -341,7 +344,7 @@ class RequestPool {
341
344
  if (this.requests.has(i)) continue
342
345
 
343
346
  // TODO: if seeking and i >= core.length, let that takes precendance in the upgrade req
344
- const req = new Request(i, i < this.core.tree.length ? seek : 0)
347
+ const req = new Request(i, i < this.core.tree.length ? seek : 0, nodes)
345
348
  this.requests.set(i, req)
346
349
  this.send(peer, req)
347
350
  return req
@@ -390,16 +393,28 @@ class RequestPool {
390
393
 
391
394
  if (data.block.index < this.core.tree.length || this.core.truncating > 0) {
392
395
  try {
393
- data.block.nodes = await this.core.tree.nodes(data.block.index * 2)
396
+ data.block.nodes = Math.max(req.nodes, await this.core.tree.nodes(data.block.index * 2))
394
397
  } catch (err) {
395
398
  console.error('TODO handle me:', err.stack)
396
399
  }
397
400
  }
398
401
 
402
+ if (peer.destroyed) {
403
+ req.peer = null
404
+ this.pending.push(req)
405
+ if (upgrading) {
406
+ this.upgrading.resolve()
407
+ this.upgrading = null
408
+ }
409
+ this.replicator.updateAll()
410
+ return
411
+ }
412
+
399
413
  if (fork !== this.core.tree.fork || paused(peer, this.core.tree.fork) || this.core.truncating > 0) {
400
414
  if (peer.state.inflight > 0) peer.state.inflight--
401
415
  if (req.promises.length) { // someone is eagerly waiting for this request
402
416
  req.peer = null // resend on some other peer
417
+ this.pending.push(req)
403
418
  } else { // otherwise delete the request
404
419
  this.requests.delete(req.index)
405
420
  }
@@ -594,7 +609,7 @@ class RequestPool {
594
609
  return e.createPromise()
595
610
  }
596
611
 
597
- const r = new Request(index, 0)
612
+ const r = new Request(index, 0, 0)
598
613
 
599
614
  this.requests.set(index, r)
600
615
  this.pending.push(r)
@@ -810,3 +825,14 @@ function pages (core) {
810
825
  }
811
826
 
812
827
  function noop () {}
828
+
829
+ function log2 (n) {
830
+ let res = 1
831
+
832
+ while (n > 2) {
833
+ n /= 2
834
+ res++
835
+ }
836
+
837
+ return res
838
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.0.0-alpha.1",
3
+ "version": "10.0.0-alpha.5",
4
4
  "description": "Hypercore 10",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -39,7 +39,8 @@
39
39
  "random-access-file": "^2.1.4",
40
40
  "random-array-iterator": "^1.0.0",
41
41
  "safety-catch": "^1.0.1",
42
- "uint64le": "^1.0.0"
42
+ "uint64le": "^1.0.0",
43
+ "xache": "^1.0.0"
43
44
  },
44
45
  "devDependencies": {
45
46
  "brittle": "^1.4.1",
package/test/replicate.js CHANGED
@@ -294,3 +294,40 @@ test('multiplexing multiple times over the same stream', async function (t) {
294
294
  t.is(b1.length, a1.length, 'same length')
295
295
  t.end()
296
296
  })
297
+
298
+ test('destroying a stream and re-replicating works', async function (t) {
299
+ const core = await create()
300
+
301
+ while (core.length < 33) await core.append(Buffer.from('#' + core.length))
302
+
303
+ const clone = await create(core.key)
304
+
305
+ let s1 = core.replicate(true)
306
+ let s2 = clone.replicate(false)
307
+
308
+ s1.pipe(s2).pipe(s1)
309
+
310
+ await s2.opened
311
+
312
+ const all = []
313
+ for (let i = 0; i < 33; i++) {
314
+ all.push(clone.get(i))
315
+ }
316
+
317
+ clone.once('download', function () {
318
+ // simulate stream failure in the middle of bulk downloading
319
+ s1.destroy()
320
+ })
321
+
322
+ await new Promise((resolve) => s1.once('close', resolve))
323
+
324
+ // retry
325
+ s1 = core.replicate(true)
326
+ s2 = clone.replicate(false)
327
+
328
+ s1.pipe(s2).pipe(s1)
329
+
330
+ const blocks = await Promise.all(all)
331
+
332
+ t.is(blocks.length, 33, 'downloaded 33 blocks')
333
+ })