hypercore 10.0.0-alpha.2 → 10.0.0-alpha.23

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/lib/replicator.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const Protocol = require('./protocol')
2
2
  const RemoteBitfield = require('./remote-bitfield')
3
3
  const RandomIterator = require('random-array-iterator')
4
+ const b4a = require('b4a')
4
5
 
5
6
  const PKG = require('../package.json')
6
7
  const USER_AGENT = PKG.name + '/' + PKG.version + '@nodejs'
@@ -25,11 +26,12 @@ class InvertedPromise {
25
26
  }
26
27
 
27
28
  class Request {
28
- constructor (index, seek) {
29
+ constructor (index, seek, nodes) {
29
30
  this.peer = null
30
31
  this.index = index
31
32
  this.seek = seek
32
33
  this.value = seek === 0
34
+ this.nodes = nodes
33
35
  this.promises = []
34
36
  }
35
37
 
@@ -119,9 +121,10 @@ class Seek {
119
121
  }
120
122
 
121
123
  class Range {
122
- constructor (start, end, linear) {
124
+ constructor (start, end, filter, linear) {
123
125
  this.start = start
124
126
  this.end = end
127
+ this.filter = filter
125
128
  this.linear = !!linear
126
129
  this.promise = null
127
130
  this.done = false
@@ -144,7 +147,7 @@ class Range {
144
147
  }
145
148
 
146
149
  for (; this._start < this.end; this._start++) {
147
- if (!bitfield.get(this._start)) return false
150
+ if (this.filter(this._start) && !bitfield.get(this._start)) return false
148
151
  }
149
152
 
150
153
  return true
@@ -278,7 +281,9 @@ class RequestPool {
278
281
 
279
282
  _updateSeek (peer, seek) {
280
283
  if (seek.request) return false
281
- seek.request = this._requestRange(peer, seek.seeker.start, seek.seeker.end, seek.seeker.bytes)
284
+ // We have to snapshot the nodes here now, due to the caching of the request...
285
+ const nodes = log2(seek.seeker.end - seek.seeker.start)
286
+ seek.request = this._requestRange(peer, seek.seeker.start, seek.seeker.end, seek.seeker.bytes, nodes)
282
287
  return seek.request !== null
283
288
  }
284
289
 
@@ -296,10 +301,13 @@ class RequestPool {
296
301
  const end = range.end === -1 ? peer.state.length : range.end
297
302
  if (end <= range._start) return false
298
303
 
299
- if (range.linear) return !!this._requestRange(peer, range._start, end, 0)
304
+ if (range.linear) return !!this._requestRange(peer, range._start, end, 0, 0, range.filter)
300
305
 
301
306
  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))
307
+ return !!(
308
+ this._requestRange(peer, r, end, 0, 0, range.filter) ||
309
+ this._requestRange(peer, range._start, r, 0, 0, range.filter)
310
+ )
303
311
  }
304
312
 
305
313
  _updateUpgrade (peer) {
@@ -328,7 +336,7 @@ class RequestPool {
328
336
  this.pendingUpgrade = null
329
337
  }
330
338
 
331
- _requestRange (peer, start, end, seek) {
339
+ _requestRange (peer, start, end, seek, nodes, filter = tautology) {
332
340
  const remote = peer.state.bitfield
333
341
  const local = this.core.bitfield
334
342
 
@@ -336,12 +344,12 @@ class RequestPool {
336
344
  if (end === -1) end = peer.state.length
337
345
 
338
346
  for (let i = start; i < end; i++) {
339
- if (!remote.get(i) || local.get(i)) continue
347
+ if (!filter(i) || !remote.get(i) || local.get(i)) continue
340
348
  // TODO: if this was a NO_VALUE request, retry if no blocks can be found elsewhere
341
349
  if (this.requests.has(i)) continue
342
350
 
343
351
  // 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)
352
+ const req = new Request(i, i < this.core.tree.length ? seek : 0, nodes)
345
353
  this.requests.set(i, req)
346
354
  this.send(peer, req)
347
355
  return req
@@ -390,16 +398,28 @@ class RequestPool {
390
398
 
391
399
  if (data.block.index < this.core.tree.length || this.core.truncating > 0) {
392
400
  try {
393
- data.block.nodes = await this.core.tree.nodes(data.block.index * 2)
401
+ data.block.nodes = Math.max(req.nodes, await this.core.tree.nodes(data.block.index * 2))
394
402
  } catch (err) {
395
403
  console.error('TODO handle me:', err.stack)
396
404
  }
397
405
  }
398
406
 
407
+ if (peer.destroyed) {
408
+ req.peer = null
409
+ this.pending.push(req)
410
+ if (upgrading) {
411
+ this.upgrading.resolve()
412
+ this.upgrading = null
413
+ }
414
+ this.replicator.updateAll()
415
+ return
416
+ }
417
+
399
418
  if (fork !== this.core.tree.fork || paused(peer, this.core.tree.fork) || this.core.truncating > 0) {
400
419
  if (peer.state.inflight > 0) peer.state.inflight--
401
420
  if (req.promises.length) { // someone is eagerly waiting for this request
402
421
  req.peer = null // resend on some other peer
422
+ this.pending.push(req)
403
423
  } else { // otherwise delete the request
404
424
  this.requests.delete(req.index)
405
425
  }
@@ -594,7 +614,7 @@ class RequestPool {
594
614
  return e.createPromise()
595
615
  }
596
616
 
597
- const r = new Request(index, 0)
617
+ const r = new Request(index, 0, 0)
598
618
 
599
619
  this.requests.set(index, r)
600
620
  this.pending.push(r)
@@ -613,8 +633,9 @@ module.exports = class Replicator {
613
633
  this.onupdate = onupdate
614
634
  }
615
635
 
616
- static createProtocol (noiseStream) {
636
+ static createProtocol (noiseStream, opts) {
617
637
  return new Protocol(noiseStream, {
638
+ ...opts,
618
639
  protocolVersion: 0,
619
640
  userAgent: USER_AGENT
620
641
  })
@@ -662,8 +683,18 @@ module.exports = class Replicator {
662
683
  return promise
663
684
  }
664
685
 
665
- static createRange (start, end, linear) {
666
- return new Range(start, end, linear)
686
+ static createRange (start, end, filter, linear) {
687
+ // createRange(start, end)
688
+ if (filter === undefined) {
689
+ filter = tautology
690
+
691
+ // createRange(start, end, linear)
692
+ } else if (typeof filter === 'boolean') {
693
+ linear = filter
694
+ filter = tautology
695
+ }
696
+
697
+ return new Range(start, end, filter, linear)
667
698
  }
668
699
 
669
700
  addRange (range) {
@@ -750,8 +781,8 @@ module.exports = class Replicator {
750
781
 
751
782
  onbitfield ({ start, bitfield }, peer) {
752
783
  if (bitfield.length < 1024) {
753
- const buf = Buffer.from(bitfield.buffer, bitfield.byteOffset, bitfield.byteLength)
754
- const bigger = Buffer.concat([buf, Buffer.alloc(4096 - buf.length)])
784
+ const buf = b4a.from(bitfield.buffer, bitfield.byteOffset, bitfield.byteLength)
785
+ const bigger = b4a.concat([buf, b4a.alloc(4096 - buf.length)])
755
786
  bitfield = new Uint32Array(bigger.buffer, bigger.byteOffset, 1024)
756
787
  }
757
788
  peer.state.bitfield.pages.set(start, bitfield)
@@ -810,3 +841,18 @@ function pages (core) {
810
841
  }
811
842
 
812
843
  function noop () {}
844
+
845
+ function tautology () {
846
+ return true
847
+ }
848
+
849
+ function log2 (n) {
850
+ let res = 1
851
+
852
+ while (n > 2) {
853
+ n /= 2
854
+ res++
855
+ }
856
+
857
+ return res
858
+ }
package/lib/streams.js ADDED
@@ -0,0 +1,56 @@
1
+ const { Writable, Readable } = require('streamx')
2
+
3
+ class ReadStream extends Readable {
4
+ constructor (core, opts = {}) {
5
+ super()
6
+
7
+ this.core = core
8
+ this.start = opts.start || 0
9
+ this.end = typeof opts.end === 'number' ? opts.end : -1
10
+ this.snapshot = !opts.live && opts.snapshot !== false
11
+ this.live = !!opts.live
12
+ }
13
+
14
+ _open (cb) {
15
+ this._openP().then(cb, cb)
16
+ }
17
+
18
+ _read (cb) {
19
+ this._readP().then(cb, cb)
20
+ }
21
+
22
+ async _openP () {
23
+ if (this.end === -1) await this.core.update()
24
+ else await this.core.ready()
25
+ if (this.snapshot && this.end === -1) this.end = this.core.length
26
+ }
27
+
28
+ async _readP () {
29
+ const end = this.live ? -1 : (this.end === -1 ? this.core.length : this.end)
30
+ if (end >= 0 && this.start >= end) {
31
+ this.push(null)
32
+ return
33
+ }
34
+
35
+ this.push(await this.core.get(this.start++))
36
+ }
37
+ }
38
+
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.2",
3
+ "version": "10.0.0-alpha.23",
4
4
  "description": "Hypercore 10",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -27,22 +27,29 @@
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",
36
+ "b4a": "^1.1.0",
32
37
  "big-sparse-array": "^1.0.2",
33
- "codecs": "^2.2.0",
34
- "compact-encoding": "^2.0.0",
38
+ "codecs": "^3.0.0",
39
+ "compact-encoding": "^2.5.0",
35
40
  "crc32-universal": "^1.0.1",
36
- "flat-tree": "^1.7.0",
37
- "hypercore-crypto": "^2.1.1",
41
+ "events": "^3.3.0",
42
+ "flat-tree": "^1.9.0",
43
+ "hypercore-crypto": "^3.1.0",
38
44
  "is-options": "^1.0.1",
39
45
  "random-access-file": "^2.1.4",
40
46
  "random-array-iterator": "^1.0.0",
41
47
  "safety-catch": "^1.0.1",
42
- "uint64le": "^1.0.0"
48
+ "sodium-universal": "^3.0.4",
49
+ "xache": "^1.0.0"
43
50
  },
44
51
  "devDependencies": {
45
- "brittle": "^1.4.1",
52
+ "brittle": "^2.0.0",
46
53
  "hyperswarm": "next",
47
54
  "random-access-memory": "^3.1.2",
48
55
  "standard": "^16.0.3",
@@ -1,24 +0,0 @@
1
- name: Build Status
2
-
3
- on:
4
- push:
5
- branches:
6
- - master
7
- pull_request:
8
- branches:
9
- - master
10
- jobs:
11
- build:
12
- strategy:
13
- matrix:
14
- node-version: [14.x]
15
- os: [ubuntu-latest, macos-latest, windows-latest]
16
- runs-on: ${{ matrix.os }}
17
- steps:
18
- - uses: actions/checkout@v2
19
- - name: Use Node.js ${{ matrix.node-version }}
20
- uses: actions/setup-node@v2
21
- with:
22
- node-version: ${{ matrix.node-version }}
23
- - run: npm install
24
- - 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.
@@ -1,19 +0,0 @@
1
- const Hypercore = require('../')
2
- const Hyperswarm = require('hyperswarm')
3
-
4
- const core = new Hypercore('./source')
5
-
6
- start()
7
-
8
- async function start () {
9
- await core.ready()
10
- while (core.length < 1000) {
11
- await core.append('block #' + core.length)
12
- }
13
-
14
- const swarm = new Hyperswarm()
15
- swarm.on('connection', socket => core.replicate(socket))
16
- swarm.join(core.discoveryKey, { server: true, client: false })
17
-
18
- console.log('Core:', core.key.toString('hex'))
19
- }
package/examples/basic.js DELETED
@@ -1,10 +0,0 @@
1
- const Hypercore = require('../')
2
-
3
- start()
4
-
5
- async function start () {
6
- const core = new Hypercore('/tmp/basic')
7
- await core.append(['Hello', 'World'])
8
- console.log(core)
9
- await core.close()
10
- }
package/examples/http.js DELETED
@@ -1,123 +0,0 @@
1
- const Hypercore = require('../')
2
- const streamx = require('streamx')
3
- const replicator = require('@hyperswarm/replicator')
4
-
5
- const core = new Hypercore('/tmp/movie')
6
-
7
- if (process.argv[2] === 'bench') bench()
8
- else if (process.argv[2]) importData()
9
- else start()
10
-
11
- class ByteStream extends streamx.Readable {
12
- constructor (core, byteOffset, byteLength) {
13
- super()
14
-
15
- this.core = core
16
- this.byteOffset = byteOffset
17
- this.byteLength = byteLength
18
- this.index = 0
19
- this.range = null
20
- }
21
-
22
- async _read (cb) {
23
- let data = null
24
-
25
- if (!this.byteLength) {
26
- this.push(null)
27
- return cb(null)
28
- }
29
-
30
- if (this.byteOffset > 0) {
31
- const [block, byteOffset] = await core.seek(this.byteOffset)
32
- this.byteOffset = 0
33
- this.index = block + 1
34
- this._select(this.index)
35
- data = (await core.get(block)).slice(byteOffset)
36
- } else {
37
- this._select(this.index + 1)
38
- data = await core.get(this.index++)
39
- }
40
-
41
- if (data.length >= this.byteLength) {
42
- data = data.slice(0, this.byteLength)
43
- this.push(data)
44
- this.push(null)
45
- } else {
46
- this.push(data)
47
- }
48
-
49
- this.byteLength -= data.length
50
-
51
- cb(null)
52
- }
53
-
54
- _select (index) {
55
- if (this.range !== null) this.range.destroy(null)
56
- this.range = this.core.download({ start: index, end: index + 32, linear: true })
57
- }
58
-
59
- _destroy (cb) {
60
- if (this.range) this.range.destroy(null)
61
- cb(null)
62
- }
63
- }
64
-
65
- async function bench () {
66
- await core.ready()
67
-
68
- console.time()
69
- for (let i = 0; i < core.length; i++) {
70
- await core.get(i)
71
- }
72
- console.timeEnd()
73
- }
74
-
75
- async function start () {
76
- const http = require('http')
77
- const parse = require('range-parser')
78
-
79
- await core.ready()
80
-
81
- core.on('download', (index) => console.log('Downloaded block #' + index))
82
- core.download({ start: 0, end: 1 })
83
-
84
- // hack until we update the replicator
85
- core.ready = (cb) => cb(null)
86
-
87
- replicator(core, {
88
- discoveryKey: require('crypto').createHash('sha256').update('http').digest(),
89
- announce: true,
90
- lookup: true
91
- })
92
-
93
- http.createServer(function (req, res) {
94
- res.setHeader('Content-Type', 'video/x-matroska')
95
- res.setHeader('Accept-Ranges', 'bytes')
96
-
97
- let s
98
-
99
- if (req.headers.range) {
100
- const range = parse(core.byteLength, req.headers.range)[0]
101
- const byteLength = range.end - range.start + 1
102
- res.statusCode = 206
103
- res.setHeader('Content-Range', 'bytes ' + range.start + '-' + range.end + '/' + core.byteLength)
104
- s = new ByteStream(core, range.start, byteLength)
105
- } else {
106
- s = new ByteStream(core, 0, core.byteLength)
107
- }
108
-
109
- res.setHeader('Content-Length', s.byteLength)
110
- s.pipe(res, () => {})
111
- }).listen(10101)
112
- }
113
-
114
- async function importData () {
115
- const fs = require('fs')
116
- const rs = fs.createReadStream(process.argv[2])
117
-
118
- for await (const data of rs) {
119
- await core.append(data)
120
- }
121
-
122
- console.log('done!', core)
123
- }
@@ -1,20 +0,0 @@
1
- const Hypercore = require('../')
2
- const Hyperswarm = require('hyperswarm')
3
-
4
- const core = new Hypercore('./clone', process.argv[2])
5
-
6
- start()
7
-
8
- async function start () {
9
- await core.ready()
10
-
11
- const swarm = new Hyperswarm()
12
- swarm.on('connection', socket => core.replicate(socket))
13
- swarm.join(core.discoveryKey, { server: false, client: true })
14
-
15
- console.log((await core.get(42)).toString())
16
- console.log((await core.get(142)).toString())
17
- console.log((await core.get(511)).toString())
18
- console.log((await core.get(512)).toString())
19
- console.log((await core.get(513)).toString())
20
- }
package/test/basic.js DELETED
@@ -1,78 +0,0 @@
1
- const test = require('brittle')
2
- const ram = require('random-access-memory')
3
-
4
- const Hypercore = require('../')
5
- const { create } = require('./helpers')
6
-
7
- test('basic', async function (t) {
8
- const core = await create()
9
- let appends = 0
10
-
11
- t.is(core.length, 0)
12
- t.is(core.byteLength, 0)
13
- t.is(core.writable, true)
14
- t.is(core.readable, true)
15
-
16
- core.on('append', function () {
17
- appends++
18
- })
19
-
20
- await core.append('hello')
21
- await core.append('world')
22
-
23
- t.is(core.length, 2)
24
- t.is(core.byteLength, 10)
25
- t.is(appends, 2)
26
-
27
- t.end()
28
- })
29
-
30
- test('session', async function (t) {
31
- const core = await create()
32
-
33
- const session = core.session()
34
-
35
- await session.append('test')
36
- t.alike(await core.get(0), Buffer.from('test'))
37
- t.alike(await session.get(0), Buffer.from('test'))
38
- t.end()
39
- })
40
-
41
- test('close', async function (t) {
42
- const core = await create()
43
- await core.append('hello world')
44
-
45
- await core.close()
46
-
47
- try {
48
- await core.get(0)
49
- t.fail('core should be closed')
50
- } catch {
51
- t.pass('get threw correctly when core was closed')
52
- }
53
- })
54
-
55
- test('close multiple', async function (t) {
56
- const core = await create()
57
- await core.append('hello world')
58
-
59
- const ev = t.test('events')
60
-
61
- ev.plan(4)
62
-
63
- let i = 0
64
-
65
- core.on('close', () => ev.is(i++, 0, 'on close'))
66
- core.close().then(() => ev.is(i++, 1, 'first close'))
67
- core.close().then(() => ev.is(i++, 2, 'second close'))
68
- core.close().then(() => ev.is(i++, 3, 'third close'))
69
-
70
- await ev
71
- })
72
-
73
- test('storage options', async function (t) {
74
- const core = new Hypercore({ storage: ram })
75
- await core.append('hello')
76
- t.alike(await core.get(0), Buffer.from('hello'))
77
- t.end()
78
- })
package/test/bitfield.js DELETED
@@ -1,71 +0,0 @@
1
- const test = require('brittle')
2
- const ram = require('random-access-memory')
3
- const Bitfield = require('../lib/bitfield')
4
-
5
- test('bitfield - set and get', async function (t) {
6
- const b = await Bitfield.open(ram())
7
-
8
- t.absent(b.get(42))
9
- b.set(42, true)
10
- t.ok(b.get(42))
11
-
12
- // bigger offsets
13
- t.absent(b.get(42000000))
14
- b.set(42000000, true)
15
- t.ok(b.get(42000000))
16
-
17
- b.set(42000000, false)
18
- t.absent(b.get(42000000))
19
-
20
- await b.flush()
21
- })
22
-
23
- test('bitfield - random set and gets', async function (t) {
24
- const b = await Bitfield.open(ram())
25
- const set = new Set()
26
-
27
- for (let i = 0; i < 200; i++) {
28
- const idx = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
29
- b.set(idx, true)
30
- set.add(idx)
31
- }
32
-
33
- for (let i = 0; i < 500; i++) {
34
- const idx = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
35
- const expected = set.has(idx)
36
- const val = b.get(idx)
37
- if (val !== expected) {
38
- t.fail('expected ' + expected + ' but got ' + val + ' at ' + idx)
39
- return
40
- }
41
- }
42
-
43
- for (const idx of set) {
44
- const val = b.get(idx)
45
- if (val !== true) {
46
- t.fail('expected true but got ' + val + ' at ' + idx)
47
- return
48
- }
49
- }
50
-
51
- t.pass('all random set and gets pass')
52
- })
53
-
54
- test('bitfield - reload', async function (t) {
55
- const s = ram()
56
-
57
- {
58
- const b = await Bitfield.open(s)
59
- b.set(142, true)
60
- b.set(40000, true)
61
- b.set(1424242424, true)
62
- await b.flush()
63
- }
64
-
65
- {
66
- const b = await Bitfield.open(s)
67
- t.ok(b.get(142))
68
- t.ok(b.get(40000))
69
- t.ok(b.get(1424242424))
70
- }
71
- })