hypercore 10.0.0-alpha.2 → 10.0.0-alpha.20

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.
@@ -0,0 +1,68 @@
1
+ const sodium = require('sodium-universal')
2
+ const c = require('compact-encoding')
3
+ const b4a = require('b4a')
4
+
5
+ const nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES)
6
+
7
+ module.exports = class BlockEncryption {
8
+ constructor (encryptionKey, hypercoreKey) {
9
+ const subKeys = b4a.alloc(2 * sodium.crypto_stream_KEYBYTES)
10
+
11
+ this.key = encryptionKey
12
+ this.blockKey = subKeys.subarray(0, sodium.crypto_stream_KEYBYTES)
13
+ this.blindingKey = subKeys.subarray(sodium.crypto_stream_KEYBYTES)
14
+ this.padding = 8
15
+
16
+ sodium.crypto_generichash(this.blockKey, encryptionKey, hypercoreKey)
17
+ sodium.crypto_generichash(this.blindingKey, this.blockKey)
18
+ }
19
+
20
+ encrypt (index, block, fork) {
21
+ const padding = block.subarray(0, this.padding)
22
+ block = block.subarray(this.padding)
23
+
24
+ c.uint64.encode({ start: 0, end: 8, buffer: padding }, fork)
25
+ c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
26
+
27
+ // Zero out any previous padding.
28
+ nonce.fill(0, 8, 8 + padding.byteLength)
29
+
30
+ // Blind the fork ID, possibly risking reusing the nonce on a reorg of the
31
+ // Hypercore. This is fine as the blinding is best-effort and the latest
32
+ // fork ID shared on replication anyway.
33
+ sodium.crypto_stream_xor(
34
+ padding,
35
+ padding,
36
+ nonce,
37
+ this.blindingKey
38
+ )
39
+
40
+ nonce.set(padding, 8)
41
+
42
+ // The combination of a (blinded) fork ID and a block index is unique for a
43
+ // given Hypercore and is therefore a valid nonce for encrypting the block.
44
+ sodium.crypto_stream_xor(
45
+ block,
46
+ block,
47
+ nonce,
48
+ this.blockKey
49
+ )
50
+ }
51
+
52
+ decrypt (index, block) {
53
+ const padding = block.subarray(0, this.padding)
54
+ block = block.subarray(this.padding)
55
+
56
+ c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
57
+
58
+ nonce.set(padding, 8)
59
+
60
+ // Decrypt the block using the blinded fork ID.
61
+ sodium.crypto_stream_xor(
62
+ block,
63
+ block,
64
+ nonce,
65
+ this.blockKey
66
+ )
67
+ }
68
+ }
@@ -1,3 +1,5 @@
1
+ const b4a = require('b4a')
2
+
1
3
  module.exports = class BlockStore {
2
4
  constructor (storage, tree) {
3
5
  this.storage = storage
@@ -15,7 +17,7 @@ module.exports = class BlockStore {
15
17
 
16
18
  putBatch (i, batch, offset) {
17
19
  if (batch.length === 0) return Promise.resolve()
18
- return this.put(i, batch.length === 1 ? batch[0] : Buffer.concat(batch), offset)
20
+ return this.put(i, batch.length === 1 ? batch[0] : b4a.concat(batch), offset)
19
21
  }
20
22
 
21
23
  clear () {
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
  }
@@ -214,10 +215,12 @@ module.exports = class Core {
214
215
  }
215
216
  }
216
217
 
217
- async append (values, sign = this.defaultSign) {
218
+ async append (values, sign = this.defaultSign, hooks = {}) {
218
219
  await this._mutex.lock()
219
220
 
220
221
  try {
222
+ if (hooks.preappend) await hooks.preappend(values)
223
+
221
224
  if (!values.length) return this.tree.length
222
225
 
223
226
  const batch = this.tree.batch()
@@ -1,9 +1,10 @@
1
1
  const flat = require('flat-tree')
2
2
  const crypto = require('hypercore-crypto')
3
- const uint64le = require('uint64le')
3
+ const c = require('compact-encoding')
4
+ const b4a = require('b4a')
4
5
 
5
- const BLANK_HASH = Buffer.alloc(32)
6
- const OLD_TREE = Buffer.from([5, 2, 87, 2, 0, 0, 40, 7, 66, 76, 65, 75, 69, 50, 98])
6
+ const BLANK_HASH = b4a.alloc(32)
7
+ const OLD_TREE = b4a.from([5, 2, 87, 2, 0, 0, 40, 7, 66, 76, 65, 75, 69, 50, 98])
7
8
 
8
9
  class NodeQueue {
9
10
  constructor (nodes, extra = null) {
@@ -214,7 +215,7 @@ class ReorgBatch extends MerkleTreeBatch {
214
215
  const nodes = []
215
216
  const root = verifyBlock(proof, this.tree.crypto, nodes)
216
217
 
217
- if (root === null || !root.hash.equals(this.diff.hash)) return false
218
+ if (root === null || !b4a.equals(root.hash, this.diff.hash)) return false
218
219
 
219
220
  this.nodes.push(...nodes)
220
221
  return this._update(nodes)
@@ -232,7 +233,7 @@ class ReorgBatch extends MerkleTreeBatch {
232
233
  if (!left) break
233
234
 
234
235
  const existing = await this.tree.get(left.index, false)
235
- if (!existing || !existing.hash.equals(left.hash)) {
236
+ if (!existing || !b4a.equals(existing.hash, left.hash)) {
236
237
  diff = left
237
238
  } else {
238
239
  diff = n.get(ite.sibling())
@@ -246,6 +247,8 @@ class ReorgBatch extends MerkleTreeBatch {
246
247
  }
247
248
 
248
249
  _updateDiffRoot (diff) {
250
+ if (this.want === null) return true
251
+
249
252
  const spans = flat.spans(diff.index)
250
253
  const start = spans[0] / 2
251
254
  const end = Math.min(this.treeLength, spans[1] / 2 + 1)
@@ -272,11 +275,15 @@ class ReorgBatch extends MerkleTreeBatch {
272
275
  }
273
276
 
274
277
  class ByteSeeker {
275
- constructor (tree, bytes) {
278
+ constructor (tree, bytes, padding = 0) {
276
279
  this.tree = tree
277
280
  this.bytes = bytes
278
- this.start = bytes >= tree.byteLength ? tree.length : 0
279
- this.end = bytes < tree.byteLength ? tree.length : 0
281
+ this.padding = padding
282
+
283
+ const size = tree.byteLength - (tree.length * padding)
284
+
285
+ this.start = bytes >= size ? tree.length : 0
286
+ this.end = bytes < size ? tree.length : 0
280
287
  }
281
288
 
282
289
  nodes () {
@@ -287,12 +294,12 @@ class ByteSeeker {
287
294
  if (!bytes) return [0, 0]
288
295
 
289
296
  for (const node of this.tree.roots) { // all async ticks happen once we find the root so safe
290
- if (bytes === node.size) {
291
- return [flat.rightSpan(node.index) + 2, 0]
292
- }
297
+ let size = node.size
298
+ if (this.padding > 0) size -= this.padding * flat.countLeaves(node.index)
293
299
 
294
- if (bytes > node.size) {
295
- bytes -= node.size
300
+ if (bytes === size) return [flat.rightSpan(node.index) + 2, 0]
301
+ if (bytes > size) {
302
+ bytes -= size
296
303
  continue
297
304
  }
298
305
 
@@ -301,9 +308,12 @@ class ByteSeeker {
301
308
  while ((ite.index & 1) !== 0) {
302
309
  const l = await this.tree.get(ite.leftChild(), false)
303
310
  if (l) {
304
- if (l.size === bytes) return [ite.rightSpan() + 2, 0]
305
- if (l.size > bytes) continue
306
- bytes -= l.size
311
+ let size = l.size
312
+ if (this.padding > 0) size -= this.padding * ite.countLeaves()
313
+
314
+ if (size === bytes) return [ite.rightSpan() + 2, 0]
315
+ if (size > bytes) continue
316
+ bytes -= size
307
317
  ite.sibling()
308
318
  } else {
309
319
  ite.parent()
@@ -347,7 +357,7 @@ module.exports = class MerkleTree {
347
357
  }
348
358
 
349
359
  addNode (node) {
350
- if (node.size === 0 && node.hash.equals(BLANK_HASH)) node = blankNode(node.index)
360
+ if (node.size === 0 && b4a.equals(node.hash, BLANK_HASH)) node = blankNode(node.index)
351
361
  this.unflushed.set(node.index, node)
352
362
  }
353
363
 
@@ -355,8 +365,8 @@ module.exports = class MerkleTree {
355
365
  return new MerkleTreeBatch(this)
356
366
  }
357
367
 
358
- seek (bytes) {
359
- return new ByteSeeker(this, bytes)
368
+ seek (bytes, padding) {
369
+ return new ByteSeeker(this, bytes, padding)
360
370
  }
361
371
 
362
372
  hash () {
@@ -371,6 +381,17 @@ module.exports = class MerkleTree {
371
381
  return this.signature !== null && this.crypto.verify(this.signable(), this.signature, key)
372
382
  }
373
383
 
384
+ getRoots (length) {
385
+ const indexes = flat.fullRoots(2 * length)
386
+ const roots = new Array(indexes.length)
387
+
388
+ for (let i = 0; i < indexes.length; i++) {
389
+ roots[i] = this.get(indexes[i], true)
390
+ }
391
+
392
+ return Promise.all(roots)
393
+ }
394
+
374
395
  get (index, error = true) {
375
396
  let node = this.unflushed.get(index)
376
397
 
@@ -433,17 +454,23 @@ module.exports = class MerkleTree {
433
454
  // TODO: write neighbors together etc etc
434
455
  // TODO: bench loading a full disk page and copy to that instead
435
456
  return new Promise((resolve, reject) => {
436
- const slab = Buffer.allocUnsafe(40 * this.flushing.size)
457
+ const slab = b4a.allocUnsafe(40 * this.flushing.size)
437
458
 
438
459
  let error = null
439
460
  let missing = this.flushing.size + 1
440
461
  let offset = 0
441
462
 
442
463
  for (const node of this.flushing.values()) {
443
- const b = slab.slice(offset, offset += 40)
444
- uint64le.encode(node.size, b, 0)
445
- node.hash.copy(b, 8)
446
- this.storage.write(node.index * 40, b, done)
464
+ const state = {
465
+ start: 0,
466
+ end: 40,
467
+ buffer: slab.subarray(offset, offset += 40)
468
+ }
469
+
470
+ c.uint64.encode(state, node.size)
471
+ c.raw.encode(state, node.hash)
472
+
473
+ this.storage.write(node.index * 40, state.buffer, done)
447
474
  }
448
475
 
449
476
  done(null)
@@ -520,7 +547,7 @@ module.exports = class MerkleTree {
520
547
 
521
548
  for (const root of batch.roots) {
522
549
  const existing = await this.get(root.index, false)
523
- if (existing && existing.hash.equals(root.hash)) continue
550
+ if (existing && b4a.equals(existing.hash, root.hash)) continue
524
551
  batch._updateDiffRoot(root)
525
552
  break
526
553
  }
@@ -548,7 +575,7 @@ module.exports = class MerkleTree {
548
575
 
549
576
  if (unverified) {
550
577
  const verified = await this.get(unverified.index)
551
- if (!verified.hash.equals(unverified.hash)) {
578
+ if (!b4a.equals(verified.hash, unverified.hash)) {
552
579
  throw new Error('Invalid checksum at node ' + unverified.index)
553
580
  }
554
581
  }
@@ -693,7 +720,7 @@ module.exports = class MerkleTree {
693
720
  await new Promise((resolve, reject) => {
694
721
  storage.read(0, OLD_TREE.length, (err, buf) => {
695
722
  if (err) return resolve()
696
- if (buf.equals(OLD_TREE)) return reject(new Error('Storage contains an incompatible merkle tree'))
723
+ if (b4a.equals(buf, OLD_TREE)) return reject(new Error('Storage contains an incompatible merkle tree'))
697
724
  resolve()
698
725
  })
699
726
  })
@@ -1038,10 +1065,10 @@ function getStoredNode (storage, index, error) {
1038
1065
  return
1039
1066
  }
1040
1067
 
1041
- const hash = data.slice(8)
1042
- const size = uint64le.decode(data, 0)
1068
+ const hash = data.subarray(8)
1069
+ const size = c.decode(c.uint64, data)
1043
1070
 
1044
- if (size === 0 && Buffer.compare(hash, BLANK_HASH) === 0) {
1071
+ if (size === 0 && b4a.compare(hash, BLANK_HASH) === 0) {
1045
1072
  if (error) reject(new Error('Could not load node: ' + index))
1046
1073
  else resolve(null)
1047
1074
  return
@@ -1088,9 +1115,9 @@ function log2 (n) {
1088
1115
  }
1089
1116
 
1090
1117
  function signable (hash, length, fork) {
1091
- const buf = Buffer.alloc(48)
1092
- hash.copy(buf)
1093
- uint64le.encode(length, buf, 32)
1094
- uint64le.encode(fork, buf, 40)
1095
- return buf
1118
+ const state = { start: 0, end: 48, buffer: b4a.alloc(48) }
1119
+ c.raw.encode(state, hash)
1120
+ c.uint64.encode(state, length)
1121
+ c.uint64.encode(state, fork)
1122
+ return state.buffer
1096
1123
  }
package/lib/oplog.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const cenc = require('compact-encoding')
2
+ const b4a = require('b4a')
2
3
  const crc32 = require('crc32-universal')
3
4
 
4
5
  module.exports = class Oplog {
@@ -133,7 +134,7 @@ module.exports = class Oplog {
133
134
  return new Promise((resolve, reject) => {
134
135
  this.storage.open(err => {
135
136
  if (err && err.code !== 'ENOENT') return reject(err)
136
- if (err) return resolve(Buffer.alloc(0))
137
+ if (err) return resolve(b4a.alloc(0))
137
138
  this.storage.stat((err, stat) => {
138
139
  if (err && err.code !== 'ENOENT') return reject(err)
139
140
  this.storage.read(0, stat.size, (err, buf) => {
@@ -151,7 +152,7 @@ module.exports = class Oplog {
151
152
  const bit = (this._headers[i] + 1) & 1
152
153
 
153
154
  this.headerEncoding.preencode(state, header)
154
- state.buffer = Buffer.allocUnsafe(state.end)
155
+ state.buffer = b4a.allocUnsafe(state.end)
155
156
  this.headerEncoding.encode(state, header)
156
157
  this._addHeader(state, state.end - 8, bit, 0)
157
158
 
@@ -187,7 +188,7 @@ module.exports = class Oplog {
187
188
  this.entryEncoding.preencode(state, batch[i])
188
189
  }
189
190
 
190
- state.buffer = Buffer.allocUnsafe(state.end)
191
+ state.buffer = b4a.allocUnsafe(state.end)
191
192
 
192
193
  for (let i = 0; i < batch.length; i++) {
193
194
  const start = state.start += 8 // space for header
package/lib/protocol.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const { uint, from: fromEncoding } = require('compact-encoding')
2
+ const b4a = require('b4a')
2
3
  const safetyCatch = require('safety-catch')
3
4
  const codecs = require('codecs')
4
5
 
@@ -38,7 +39,7 @@ class Extension {
38
39
  _sendAlias (message, alias) {
39
40
  if (this.destroyed) return
40
41
 
41
- if (this._remoteAliases) {
42
+ if (this.remoteSupports) {
42
43
  return this.protocol.send(this.type, this.encoding, alias, message)
43
44
  }
44
45
 
@@ -125,6 +126,7 @@ class Peer {
125
126
  this.resend = false
126
127
  this.state = state
127
128
  this.extensions = new Map()
129
+ this.destroyed = false
128
130
 
129
131
  this._destroyer = this._safeDestroy.bind(this)
130
132
  }
@@ -229,6 +231,7 @@ class Peer {
229
231
  }
230
232
 
231
233
  destroy (err) {
234
+ this.destroyed = true
232
235
  return this.protocol.unregisterPeer(this, err)
233
236
  }
234
237
  }
@@ -253,11 +256,13 @@ module.exports = class Protocol {
253
256
  this._localExtensions = 128
254
257
  this._remoteExtensions = []
255
258
  this._extensions = new Map()
259
+ this._keepAliveInterval = null
256
260
 
257
261
  this._destroyer = this._safeDestroy.bind(this)
258
262
  this.noiseStream.on('data', this.onmessage.bind(this))
259
263
  this.noiseStream.on('end', this.noiseStream.end) // no half open
260
264
  this.noiseStream.on('close', () => {
265
+ this.setKeepAlive(false)
261
266
  // TODO: If the stream was destroyed with an error, we probably want to forward it here
262
267
  for (const peer of this._peers.values()) {
263
268
  peer.destroy(null)
@@ -267,6 +272,18 @@ module.exports = class Protocol {
267
272
  this._sendHandshake()
268
273
  }
269
274
 
275
+ setKeepAlive (enable) {
276
+ if (enable) {
277
+ if (this._keepAliveInterval) return
278
+ this._keepAliveInterval = setInterval(this.ping.bind(this), 5000)
279
+ if (this._keepAliveInterval.unref) this._keepAliveInterval.unref()
280
+ } else {
281
+ if (!this._keepAliveInterval) return
282
+ clearInterval(this._keepAliveInterval)
283
+ this._keepAliveInterval = null
284
+ }
285
+ }
286
+
270
287
  _sendHandshake () {
271
288
  const m = { protocolVersion: this.protocolVersion, userAgent: this.userAgent }
272
289
  const state = { start: 0, end: 0, buffer: null }
@@ -283,20 +300,20 @@ module.exports = class Protocol {
283
300
 
284
301
  registerPeer (key, discoveryKey, handlers = {}, state = null) {
285
302
  const peer = new Peer(this, this._localAliases++, key, discoveryKey, handlers, state)
286
- this._peers.set(discoveryKey.toString('hex'), peer)
303
+ this._peers.set(b4a.toString(discoveryKey, 'hex'), peer)
287
304
  this._announceCore(peer.alias, key, discoveryKey)
288
305
  return peer
289
306
  }
290
307
 
291
308
  unregisterPeer (peer, err) {
292
- this._peers.delete(peer.discoveryKey.toString('hex'))
309
+ this._peers.delete(b4a.toString(peer.discoveryKey, 'hex'))
293
310
 
294
311
  if (peer.remoteAlias > -1) {
295
312
  this._remoteAliases[peer.remoteAlias] = null
296
313
  peer.remoteAlias = -1
297
314
  }
298
315
 
299
- peer.handlers.onunregister(this, err)
316
+ peer.handlers.onunregister(peer, err)
300
317
 
301
318
  if (err) this.noiseStream.destroy(err)
302
319
  }
@@ -372,11 +389,13 @@ module.exports = class Protocol {
372
389
  this.send(2, messages.core, -1, {
373
390
  alias: alias,
374
391
  discoveryKey: discoveryKey,
375
- capability: Buffer.alloc(32) // TODO
392
+ capability: b4a.alloc(32) // TODO
376
393
  })
377
394
  }
378
395
 
379
396
  _decode (buffer) {
397
+ if (buffer.byteLength === 0) return
398
+
380
399
  const state = { start: 0, end: buffer.length, buffer }
381
400
 
382
401
  if (this._firstMessage === true) {
@@ -441,7 +460,7 @@ module.exports = class Protocol {
441
460
  }
442
461
 
443
462
  _oncore (m) {
444
- const hex = m.discoveryKey.toString('hex')
463
+ const hex = b4a.toString(m.discoveryKey, 'hex')
445
464
  const peer = this._peers.get(hex)
446
465
 
447
466
  // allow one alloc
@@ -477,7 +496,7 @@ module.exports = class Protocol {
477
496
  }
478
497
 
479
498
  _onunknowncore (m) {
480
- const peer = this._peers.get(m.discoveryKey.toString('hex'))
499
+ const peer = this._peers.get(b4a.toString(m.discoveryKey, 'hex'))
481
500
  if (!peer) return
482
501
 
483
502
  peer.resend = true
@@ -505,6 +524,11 @@ module.exports = class Protocol {
505
524
  return this.noiseStream.write(state.buffer)
506
525
  }
507
526
 
527
+ ping () {
528
+ const empty = this.noiseStream.alloc(0)
529
+ this.noiseStream.write(empty)
530
+ }
531
+
508
532
  destroy (err) {
509
533
  return this.noiseStream.destroy(err)
510
534
  }
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)
@@ -662,8 +682,18 @@ module.exports = class Replicator {
662
682
  return promise
663
683
  }
664
684
 
665
- static createRange (start, end, linear) {
666
- return new Range(start, end, linear)
685
+ static createRange (start, end, filter, linear) {
686
+ // createRange(start, end)
687
+ if (filter === undefined) {
688
+ filter = tautology
689
+
690
+ // createRange(start, end, linear)
691
+ } else if (typeof filter === 'boolean') {
692
+ linear = filter
693
+ filter = tautology
694
+ }
695
+
696
+ return new Range(start, end, filter, linear)
667
697
  }
668
698
 
669
699
  addRange (range) {
@@ -750,8 +780,8 @@ module.exports = class Replicator {
750
780
 
751
781
  onbitfield ({ start, bitfield }, peer) {
752
782
  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)])
783
+ const buf = b4a.from(bitfield.buffer, bitfield.byteOffset, bitfield.byteLength)
784
+ const bigger = b4a.concat([buf, b4a.alloc(4096 - buf.length)])
755
785
  bitfield = new Uint32Array(bigger.buffer, bigger.byteOffset, 1024)
756
786
  }
757
787
  peer.state.bitfield.pages.set(start, bitfield)
@@ -810,3 +840,18 @@ function pages (core) {
810
840
  }
811
841
 
812
842
  function noop () {}
843
+
844
+ function tautology () {
845
+ return true
846
+ }
847
+
848
+ function log2 (n) {
849
+ let res = 1
850
+
851
+ while (n > 2) {
852
+ n /= 2
853
+ res++
854
+ }
855
+
856
+ return res
857
+ }