hypercore 9.12.0 → 10.0.0-alpha.11

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.
Files changed (86) hide show
  1. package/.github/workflows/test-node.yml +3 -4
  2. package/README.md +131 -404
  3. package/__snapshots__/test/storage.js.snapshot.cjs +15 -0
  4. package/examples/announce.js +19 -0
  5. package/examples/basic.js +10 -0
  6. package/examples/http.js +123 -0
  7. package/examples/lookup.js +20 -0
  8. package/index.js +365 -1600
  9. package/lib/bitfield.js +113 -285
  10. package/lib/block-encryption.js +68 -0
  11. package/lib/block-store.js +58 -0
  12. package/lib/core.js +468 -0
  13. package/lib/extensions.js +76 -0
  14. package/lib/merkle-tree.js +1110 -0
  15. package/lib/messages.js +571 -0
  16. package/lib/mutex.js +39 -0
  17. package/lib/oplog.js +224 -0
  18. package/lib/protocol.js +525 -0
  19. package/lib/random-iterator.js +46 -0
  20. package/lib/remote-bitfield.js +24 -0
  21. package/lib/replicator.js +857 -0
  22. package/lib/streams.js +39 -0
  23. package/package.json +44 -45
  24. package/test/basic.js +59 -471
  25. package/test/bitfield.js +48 -133
  26. package/test/core.js +290 -0
  27. package/test/encodings.js +18 -0
  28. package/test/encryption.js +123 -0
  29. package/test/extension.js +71 -0
  30. package/test/helpers/index.js +23 -0
  31. package/test/merkle-tree.js +518 -0
  32. package/test/mutex.js +137 -0
  33. package/test/oplog.js +399 -0
  34. package/test/preload.js +72 -0
  35. package/test/replicate.js +227 -824
  36. package/test/sessions.js +173 -0
  37. package/test/storage.js +31 -0
  38. package/test/streams.js +39 -146
  39. package/test/user-data.js +47 -0
  40. package/bench/all.sh +0 -65
  41. package/bench/copy-64kb-blocks.js +0 -51
  42. package/bench/helpers/read-throttled.js +0 -27
  43. package/bench/helpers/read.js +0 -47
  44. package/bench/helpers/write.js +0 -29
  45. package/bench/read-16kb-blocks-proof-throttled.js +0 -1
  46. package/bench/read-16kb-blocks-proof.js +0 -1
  47. package/bench/read-16kb-blocks-throttled.js +0 -1
  48. package/bench/read-16kb-blocks.js +0 -1
  49. package/bench/read-512b-blocks.js +0 -1
  50. package/bench/read-64kb-blocks-linear-batch.js +0 -18
  51. package/bench/read-64kb-blocks-linear.js +0 -18
  52. package/bench/read-64kb-blocks-proof.js +0 -1
  53. package/bench/read-64kb-blocks.js +0 -1
  54. package/bench/replicate-16kb-blocks.js +0 -19
  55. package/bench/replicate-64kb-blocks.js +0 -19
  56. package/bench/write-16kb-blocks.js +0 -1
  57. package/bench/write-512b-blocks.js +0 -1
  58. package/bench/write-64kb-blocks-static.js +0 -1
  59. package/bench/write-64kb-blocks.js +0 -1
  60. package/example.js +0 -23
  61. package/lib/cache.js +0 -26
  62. package/lib/crypto.js +0 -5
  63. package/lib/replicate.js +0 -829
  64. package/lib/safe-buffer-equals.js +0 -6
  65. package/lib/storage.js +0 -421
  66. package/lib/tree-index.js +0 -183
  67. package/test/ack.js +0 -306
  68. package/test/audit.js +0 -36
  69. package/test/cache.js +0 -93
  70. package/test/compat.js +0 -209
  71. package/test/copy.js +0 -377
  72. package/test/default-storage.js +0 -51
  73. package/test/extensions.js +0 -137
  74. package/test/get.js +0 -64
  75. package/test/head.js +0 -65
  76. package/test/helpers/create-tracking-ram.js +0 -27
  77. package/test/helpers/create.js +0 -6
  78. package/test/helpers/replicate.js +0 -4
  79. package/test/seek.js +0 -234
  80. package/test/selections.js +0 -95
  81. package/test/set-uploading-downloading.js +0 -91
  82. package/test/stats.js +0 -77
  83. package/test/timeouts.js +0 -22
  84. package/test/tree-index.js +0 -841
  85. package/test/update.js +0 -156
  86. package/test/value-encoding.js +0 -52
@@ -0,0 +1,857 @@
1
+ const Protocol = require('./protocol')
2
+ const RemoteBitfield = require('./remote-bitfield')
3
+ const RandomIterator = require('random-array-iterator')
4
+ const b4a = require('b4a')
5
+
6
+ const PKG = require('../package.json')
7
+ const USER_AGENT = PKG.name + '/' + PKG.version + '@nodejs'
8
+
9
+ class RemoteState {
10
+ constructor (core) {
11
+ this.receivedInfo = false
12
+ this.inflight = 0
13
+ this.maxInflight = 16
14
+ this.bitfield = new RemoteBitfield()
15
+ this.length = 0
16
+ this.fork = 0
17
+ }
18
+ }
19
+
20
+ class InvertedPromise {
21
+ constructor (resolve, reject, index) {
22
+ this.index = index
23
+ this.resolve = resolve
24
+ this.reject = reject
25
+ }
26
+ }
27
+
28
+ class Request {
29
+ constructor (index, seek, nodes) {
30
+ this.peer = null
31
+ this.index = index
32
+ this.seek = seek
33
+ this.value = seek === 0
34
+ this.nodes = nodes
35
+ this.promises = []
36
+ }
37
+
38
+ createPromise () {
39
+ return new Promise((resolve, reject) => {
40
+ this.promises.push(new InvertedPromise(resolve, reject, this.promises.length))
41
+ })
42
+ }
43
+
44
+ resolve (val) {
45
+ for (let i = 0; i < this.promises.length; i++) {
46
+ this.promises[i].resolve(val)
47
+ }
48
+ }
49
+
50
+ reject (err) {
51
+ for (let i = 0; i < this.promises.length; i++) {
52
+ this.promises[i].reject(err)
53
+ }
54
+ }
55
+ }
56
+
57
+ class Upgrade {
58
+ constructor (minLength) {
59
+ this.minLength = minLength
60
+ this.promises = []
61
+ }
62
+
63
+ update (peers, fork) {
64
+ for (const peer of peers) {
65
+ if (peer.state.length >= this.minLength && !paused(peer, fork)) return true
66
+ if (!peer.state.receivedInfo) return true
67
+ }
68
+
69
+ return false
70
+ }
71
+
72
+ createPromise () {
73
+ return new Promise((resolve, reject) => {
74
+ this.promises.push(new InvertedPromise(resolve, reject, this.promises.length))
75
+ })
76
+ }
77
+
78
+ resolve (val) {
79
+ for (let i = 0; i < this.promises.length; i++) {
80
+ this.promises[i].resolve(val)
81
+ }
82
+ }
83
+
84
+ reject (err) {
85
+ for (let i = 0; i < this.promises.length; i++) {
86
+ this.promises[i].reject(err)
87
+ }
88
+ }
89
+ }
90
+
91
+ class UpgradeLock {
92
+ constructor (peer, length) {
93
+ this.peer = peer
94
+ this.length = length
95
+ this.resolve = null
96
+ this.promise = new Promise((resolve) => { this.resolve = resolve })
97
+ }
98
+ }
99
+
100
+ class Seek {
101
+ constructor (seeker) {
102
+ this.request = null
103
+ this.seeker = seeker
104
+ this.promise = null
105
+ this.finished = false
106
+ }
107
+
108
+ async update () {
109
+ const res = await this.seeker.update()
110
+ if (!res) return false
111
+ this.finished = true
112
+ this.promise.resolve(res)
113
+ return true
114
+ }
115
+
116
+ createPromise () {
117
+ return new Promise((resolve, reject) => {
118
+ this.promise = new InvertedPromise(resolve, reject, 0)
119
+ })
120
+ }
121
+ }
122
+
123
+ class Range {
124
+ constructor (start, end, filter, linear) {
125
+ this.start = start
126
+ this.end = end
127
+ this.filter = filter
128
+ this.linear = !!linear
129
+ this.promise = null
130
+ this.done = false
131
+
132
+ this._inv = null
133
+ this._start = start // can be updated
134
+ this._ranges = null // set be replicator
135
+ this._resolved = false
136
+ this._error = null
137
+ }
138
+
139
+ contains (req) {
140
+ return this._start <= req.index && req.index < this.end
141
+ }
142
+
143
+ update (bitfield) {
144
+ if (this.end === -1) {
145
+ while (bitfield.get(this._start)) this._start++
146
+ return false
147
+ }
148
+
149
+ for (; this._start < this.end; this._start++) {
150
+ if (this.filter(this._start) && !bitfield.get(this._start)) return false
151
+ }
152
+
153
+ return true
154
+ }
155
+
156
+ resolve (done) {
157
+ this._done(null, done)
158
+ }
159
+
160
+ destroy (err) {
161
+ this._done(err, false)
162
+ }
163
+
164
+ downloaded () {
165
+ if (this.promise) return this.promise
166
+ if (!this.done) return this._makePromise()
167
+ if (this._error !== null) return Promise.reject(this._error)
168
+ return Promise.resolve(this._resolved)
169
+ }
170
+
171
+ _done (err, done) {
172
+ if (this.done) return
173
+ this.done = true
174
+
175
+ if (this._ranges) {
176
+ const i = this._ranges.indexOf(this)
177
+
178
+ if (i === this._ranges.length - 1) this._ranges.pop()
179
+ else if (i > -1) this._ranges[i] = this._ranges.pop()
180
+ }
181
+
182
+ this._ranges = null
183
+ this._resolved = done
184
+ this._error = err
185
+
186
+ if (this._inv === null) return
187
+ if (err) this._inv.reject(err)
188
+ else this._inv.resolve(done)
189
+ }
190
+
191
+ _makePromise () {
192
+ this.promise = new Promise((resolve, reject) => {
193
+ this._inv = new InvertedPromise(resolve, reject, 0)
194
+ })
195
+
196
+ return this.promise
197
+ }
198
+ }
199
+
200
+ class RequestPool {
201
+ constructor (replicator, core) {
202
+ this.replicator = replicator
203
+ this.core = core
204
+ this.pending = []
205
+ this.seeks = []
206
+ this.ranges = []
207
+ this.requests = new Map()
208
+ this.upgrading = null
209
+ this.unforking = null
210
+ this.eagerUpgrades = true
211
+ this.paused = false
212
+ }
213
+
214
+ // We could make this faster by using some memory of each peer to store what they are involved in,
215
+ // but that might not be worth the cost/complexity.
216
+ clear (peer) {
217
+ peer.state.inflight = 0
218
+
219
+ for (const seek of this.seeks) {
220
+ if (seek.request && seek.request.peer === peer) {
221
+ // TODO: should prob remove cancel this request all together if no one else wants it
222
+ // and nothing is inflight
223
+ seek.request = null
224
+ }
225
+ }
226
+ for (const req of this.requests.values()) {
227
+ if (req.peer === peer) {
228
+ req.peer = null
229
+ this.pending.push(req)
230
+ }
231
+ }
232
+ if (this.upgrading && this.upgrading.peer === peer) {
233
+ this.upgrading.resolve()
234
+ this.upgrading = null
235
+ }
236
+ }
237
+
238
+ isRequesting (index) {
239
+ return this.requests.has(index)
240
+ }
241
+
242
+ update (peer) {
243
+ if (peer.state.inflight >= peer.state.maxInflight) return false
244
+ if (this.paused) return false
245
+
246
+ if (peer.state.fork > this.core.tree.fork) {
247
+ // we message them in the info recv
248
+ return true
249
+ }
250
+
251
+ // technically we'd like to run seeks at the same custom prio as reqs
252
+ // but this is a lot simpler and they should run asap anyway as they
253
+ // are super low cost (hash only request)
254
+ for (const seek of this.seeks) {
255
+ if (this._updateSeek(peer, seek)) return true
256
+ }
257
+
258
+ if (this.pendingUpgrade) {
259
+ if (this._updateUpgrade(peer)) return true
260
+ }
261
+
262
+ const pending = new RandomIterator(this.pending) // can be cached
263
+ for (const req of pending) {
264
+ if (this._updatePeer(peer, req)) {
265
+ pending.dequeue()
266
+ return true
267
+ }
268
+ }
269
+
270
+ const ranges = new RandomIterator(this.ranges) // can be cached
271
+ for (const range of ranges) {
272
+ if (this._updateRange(peer, range)) return true
273
+ }
274
+
275
+ if (this.eagerUpgrades && !this.upgrading) {
276
+ return this._updateUpgrade(peer)
277
+ }
278
+
279
+ return false
280
+ }
281
+
282
+ _updateSeek (peer, seek) {
283
+ if (seek.request) return false
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)
287
+ return seek.request !== null
288
+ }
289
+
290
+ _updatePeer (peer, req) {
291
+ const remote = peer.state.bitfield
292
+ const local = this.core.bitfield
293
+
294
+ if (!remote.get(req.index) || local.get(req.index)) return false
295
+
296
+ this.send(peer, req)
297
+ return true
298
+ }
299
+
300
+ _updateRange (peer, range) {
301
+ const end = range.end === -1 ? peer.state.length : range.end
302
+ if (end <= range._start) return false
303
+
304
+ if (range.linear) return !!this._requestRange(peer, range._start, end, 0, 0, range.filter)
305
+
306
+ const r = range._start + Math.floor(Math.random() * (end - range._start))
307
+ return !!(
308
+ this._requestRange(peer, r, end, 0, 0, range.filter) ||
309
+ this._requestRange(peer, range._start, r, 0, 0, range.filter)
310
+ )
311
+ }
312
+
313
+ _updateUpgrade (peer) {
314
+ const minLength = this.pendingUpgrade
315
+ ? this.pendingUpgrade.minLength
316
+ : this.core.tree.length + 1
317
+
318
+ if (this.upgrading || peer.state.length < minLength) return false
319
+ this.upgrading = new UpgradeLock(peer, peer.state.length)
320
+
321
+ const data = {
322
+ fork: this.core.tree.fork,
323
+ seek: null,
324
+ block: null,
325
+ upgrade: { start: this.core.tree.length, length: peer.state.length - this.core.tree.length }
326
+ }
327
+
328
+ peer.request(data)
329
+ return true
330
+ }
331
+
332
+ checkTimeouts (peers) {
333
+ if (!this.pendingUpgrade || this.upgrading) return
334
+ if (this.pendingUpgrade.update(peers, this.core.tree.fork)) return
335
+ this.pendingUpgrade.resolve(false)
336
+ this.pendingUpgrade = null
337
+ }
338
+
339
+ _requestRange (peer, start, end, seek, nodes, filter = tautology) {
340
+ const remote = peer.state.bitfield
341
+ const local = this.core.bitfield
342
+
343
+ // TODO: use 0 instead of -1 as end=0 should never be added!
344
+ if (end === -1) end = peer.state.length
345
+
346
+ for (let i = start; i < end; i++) {
347
+ if (!filter(i) || !remote.get(i) || local.get(i)) continue
348
+ // TODO: if this was a NO_VALUE request, retry if no blocks can be found elsewhere
349
+ if (this.requests.has(i)) continue
350
+
351
+ // TODO: if seeking and i >= core.length, let that takes precendance in the upgrade req
352
+ const req = new Request(i, i < this.core.tree.length ? seek : 0, nodes)
353
+ this.requests.set(i, req)
354
+ this.send(peer, req)
355
+ return req
356
+ }
357
+
358
+ return null
359
+ }
360
+
361
+ // send handles it's own errors so we do not need to await/catch it
362
+ async send (peer, req) {
363
+ req.peer = peer
364
+ peer.state.inflight++ // TODO: a non value request should count less than a value one
365
+
366
+ // TODO: also check if remote can even upgrade us lol
367
+ let needsUpgrade = peer.state.length > this.core.tree.length || !!(!this.upgrading && this.pendingUpgrade)
368
+ const fork = this.core.tree.fork
369
+ let upgrading = false
370
+
371
+ while (needsUpgrade) {
372
+ if (!this.upgrading) {
373
+ if (peer.state.length <= this.core.tree.length) {
374
+ needsUpgrade = false
375
+ break
376
+ }
377
+ // TODO: if the peer fails, we need to resolve the promise as well woop woop
378
+ // so we need some tracking mechanics for upgrades in general.
379
+ this.upgrading = new UpgradeLock(peer, peer.state.length)
380
+ upgrading = true
381
+ break
382
+ }
383
+ if (req.index < this.core.tree.length) {
384
+ needsUpgrade = false
385
+ break
386
+ }
387
+
388
+ await this.upgrading.promise
389
+ needsUpgrade = peer.state.length > this.core.tree.length || !!(!this.upgrading && this.pendingUpgrade)
390
+ }
391
+
392
+ const data = {
393
+ fork,
394
+ seek: req.seek ? { bytes: req.seek } : null,
395
+ block: { index: req.index, value: req.value, nodes: 0 },
396
+ upgrade: needsUpgrade ? { start: this.core.tree.length, length: peer.state.length - this.core.tree.length } : null
397
+ }
398
+
399
+ if (data.block.index < this.core.tree.length || this.core.truncating > 0) {
400
+ try {
401
+ data.block.nodes = Math.max(req.nodes, await this.core.tree.nodes(data.block.index * 2))
402
+ } catch (err) {
403
+ console.error('TODO handle me:', err.stack)
404
+ }
405
+ }
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
+
418
+ if (fork !== this.core.tree.fork || paused(peer, this.core.tree.fork) || this.core.truncating > 0) {
419
+ if (peer.state.inflight > 0) peer.state.inflight--
420
+ if (req.promises.length) { // someone is eagerly waiting for this request
421
+ req.peer = null // resend on some other peer
422
+ this.pending.push(req)
423
+ } else { // otherwise delete the request
424
+ this.requests.delete(req.index)
425
+ }
426
+ if (upgrading) {
427
+ this.upgrading.resolve()
428
+ this.upgrading = null
429
+ }
430
+ return
431
+ }
432
+
433
+ peer.request(data)
434
+ }
435
+
436
+ async _onupgrade (proof, peer) {
437
+ if (!this.upgrading || !proof.upgrade) return
438
+ if (this.unforking) return
439
+
440
+ await this.core.verify(proof, peer)
441
+
442
+ // TODO: validate that we actually upgraded our length as well
443
+ this.upgrading.resolve()
444
+ this.upgrading = null
445
+
446
+ if (this.pendingUpgrade) {
447
+ this.pendingUpgrade.resolve(true)
448
+ this.pendingUpgrade = null
449
+ }
450
+
451
+ if (this.seeks.length) await this._updateSeeks(null)
452
+
453
+ this.update(peer)
454
+ }
455
+
456
+ async _onfork (proof, peer) {
457
+ // TODO: if proof is from a newer fork than currently unforking, restart
458
+
459
+ if (this.unforking) {
460
+ await this.unforking.update(proof)
461
+ } else {
462
+ const reorg = await this.core.tree.reorg(proof)
463
+ const verified = reorg.signedBy(this.core.header.signer.publicKey)
464
+ if (!verified) throw new Error('Remote signature could not be verified')
465
+ this.unforking = reorg
466
+ }
467
+
468
+ if (!this.unforking.finished) {
469
+ for (let i = this.unforking.want.start; i < this.unforking.want.end; i++) {
470
+ if (peer.state.bitfield.get(i)) {
471
+ const data = {
472
+ fork: this.unforking.fork,
473
+ seek: null,
474
+ block: { index: i, value: false, nodes: this.unforking.want.nodes },
475
+ upgrade: null
476
+ }
477
+ peer.request(data)
478
+ return
479
+ }
480
+ }
481
+ return
482
+ }
483
+
484
+ const reorg = this.unforking
485
+ this.unforking = null
486
+ await this.core.reorg(reorg)
487
+
488
+ // reset ranges, also need to reset seeks etc
489
+ for (const r of this.ranges) {
490
+ r._start = 0
491
+ }
492
+
493
+ this.replicator.updateAll()
494
+
495
+ // TODO: we gotta clear out old requests as well here pointing at the old fork
496
+ }
497
+
498
+ async ondata (proof, peer) {
499
+ // technically if the remote peer pushes a DATA someone else requested inflight can go to zero
500
+ if (peer.state.inflight > 0) peer.state.inflight--
501
+
502
+ // if we get a message from another fork, maybe "unfork".
503
+ if (peer.state.fork !== this.core.tree.fork) {
504
+ // TODO: user should opt-in to this behaivour
505
+ if (peer.state.fork > this.core.tree.fork) return this._onfork(proof, peer)
506
+ return
507
+ }
508
+
509
+ // ignore incoming messages during an unfork.
510
+ if (this.unforking) return
511
+
512
+ if (!proof.block) return this._onupgrade(proof, peer)
513
+
514
+ const { index, value } = proof.block
515
+ const req = this.requests.get(index)
516
+
517
+ // no push allowed, TODO: add flag to allow pushes
518
+ if (!req || req.peer !== peer || (value && !req.value) || (proof.upgrade && !this.upgrading)) return
519
+
520
+ try {
521
+ await this.core.verify(proof, peer)
522
+ } catch (err) {
523
+ this.requests.delete(index)
524
+ throw err
525
+ }
526
+
527
+ // TODO: validate that we actually upgraded our length as well
528
+ if (proof.upgrade) {
529
+ this.upgrading.resolve()
530
+ this.upgrading = null
531
+
532
+ if (this.pendingUpgrade) {
533
+ this.pendingUpgrade.resolve(true)
534
+ this.pendingUpgrade = null
535
+ }
536
+ }
537
+
538
+ await this._resolveRequest(req, index, value)
539
+
540
+ this.update(peer)
541
+ }
542
+
543
+ async _resolveRequest (req, index, value) {
544
+ // if our request types match, clear inflight, otherwise we upgraded a hash req to a value req
545
+ const resolved = req.value === !!value
546
+ if (resolved) {
547
+ this.requests.delete(index)
548
+ req.resolve(value)
549
+ }
550
+
551
+ if (this.seeks.length) await this._updateSeeks(req)
552
+
553
+ // TODO: only do this for active ranges, ie ranges with inflight reqs...
554
+ for (let i = 0; i < this.ranges.length; i++) {
555
+ const r = this.ranges[i]
556
+ if (!r.contains(req)) continue
557
+ if (!r.update(this.core.bitfield)) continue
558
+ r.resolve(true)
559
+ i--
560
+ }
561
+ }
562
+
563
+ async _updateSeeks (req) {
564
+ for (let i = 0; i < this.seeks.length; i++) {
565
+ await this.seeks[i].update()
566
+ }
567
+
568
+ for (let i = 0; i < this.seeks.length; i++) {
569
+ const seek = this.seeks[i]
570
+
571
+ if (seek.finished) {
572
+ if (this.seeks.length > 1 && i < this.seeks.length - 1) {
573
+ this.seeks[i] = this.seeks[this.seeks.length - 1]
574
+ i--
575
+ }
576
+ this.seeks.pop()
577
+ }
578
+ if (req !== null && seek.request === req) seek.request = null
579
+ }
580
+ }
581
+
582
+ async resolveBlock (index, value) {
583
+ const req = this.requests.get(index)
584
+ if (req) await this._resolveRequest(req, index, value)
585
+ }
586
+
587
+ upgrade () {
588
+ if (this.pendingUpgrade) return this.pendingUpgrade.createPromise()
589
+ this.pendingUpgrade = new Upgrade(this.core.tree.length + 1)
590
+ return this.pendingUpgrade.createPromise()
591
+ }
592
+
593
+ range (range) {
594
+ if (range.ranges === null || this.ranges.index(range) > -1) return
595
+ this.ranges.push(range)
596
+ range.ranges = range
597
+ }
598
+
599
+ seek (seeker) {
600
+ const s = new Seek(seeker)
601
+ this.seeks.push(s)
602
+ return s.createPromise()
603
+ }
604
+
605
+ block (index) {
606
+ const e = this.requests.get(index)
607
+
608
+ if (e) {
609
+ if (!e.value) {
610
+ e.value = true
611
+ if (e.peer) this.send(e.peer, e)
612
+ }
613
+
614
+ return e.createPromise()
615
+ }
616
+
617
+ const r = new Request(index, 0, 0)
618
+
619
+ this.requests.set(index, r)
620
+ this.pending.push(r)
621
+
622
+ return r.createPromise()
623
+ }
624
+ }
625
+
626
+ module.exports = class Replicator {
627
+ constructor (core, { onupdate }) {
628
+ this.core = core
629
+ this.peers = []
630
+ this.requests = new RequestPool(this, core)
631
+ this.updating = null
632
+ this.pendingPeers = new Set()
633
+ this.onupdate = onupdate
634
+ }
635
+
636
+ static createProtocol (noiseStream) {
637
+ return new Protocol(noiseStream, {
638
+ protocolVersion: 0,
639
+ userAgent: USER_AGENT
640
+ })
641
+ }
642
+
643
+ joinProtocol (protocol, key, discoveryKey) {
644
+ if (protocol.isRegistered(discoveryKey)) return
645
+ const peer = protocol.registerPeer(key, discoveryKey, this, new RemoteState(this.core))
646
+ this.pendingPeers.add(peer)
647
+ }
648
+
649
+ broadcastBlock (start) {
650
+ const msg = { start, length: 1 }
651
+ for (const peer of this.peers) peer.have(msg)
652
+
653
+ // in case of writable peer waiting for the block itself
654
+ // we trigger the resolver to see if can't resolve it now
655
+ if (this.requests.isRequesting(start)) {
656
+ this._resolveBlock(start).then(noop, noop)
657
+ }
658
+ }
659
+
660
+ broadcastInfo () {
661
+ const msg = { length: this.core.tree.length, fork: this.core.tree.fork }
662
+ for (const peer of this.peers) peer.info(msg)
663
+ this.updateAll()
664
+ }
665
+
666
+ requestUpgrade () {
667
+ const promise = this.requests.upgrade()
668
+ this.updateAll()
669
+ return promise
670
+ }
671
+
672
+ requestSeek (seeker) {
673
+ if (typeof seeker === 'number') seeker = this.core.tree.seek(seeker)
674
+ const promise = this.requests.seek(seeker)
675
+ this.updateAll()
676
+ return promise
677
+ }
678
+
679
+ requestBlock (index) {
680
+ const promise = this.requests.block(index)
681
+ this.updateAll()
682
+ return promise
683
+ }
684
+
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)
697
+ }
698
+
699
+ addRange (range) {
700
+ if (!range.done) {
701
+ range._ranges = this.requests.ranges
702
+ this.requests.ranges.push(range)
703
+ if (range.update(this.core.bitfield)) range.resolve(true)
704
+ }
705
+ this.updateAll()
706
+ }
707
+
708
+ async _resolveBlock (index) {
709
+ const value = await this.core.blocks.get(index)
710
+ this.requests.resolveBlock(index, value)
711
+ }
712
+
713
+ updateAll () {
714
+ const peers = new RandomIterator(this.peers)
715
+ for (const peer of peers) {
716
+ if (paused(peer, this.core.tree.fork)) continue
717
+ if (this.requests.update(peer)) peers.requeue()
718
+ }
719
+ // TODO: this is a silly way of doing it
720
+ if (this.pendingPeers.size === 0) this.requests.checkTimeouts(this.peers)
721
+ }
722
+
723
+ onunregister (peer, err) {
724
+ const idx = this.peers.indexOf(peer)
725
+ if (idx === -1) return
726
+
727
+ this.pendingPeers.delete(peer)
728
+ this.peers.splice(idx, 1)
729
+ this.requests.clear(peer)
730
+ this.onupdate(false, peer)
731
+
732
+ this.updateAll()
733
+ }
734
+
735
+ oncore (_, peer) {
736
+ this.pendingPeers.delete(peer)
737
+ this.peers.push(peer)
738
+ this.onupdate(true, peer)
739
+
740
+ peer.info({ length: this.core.tree.length, fork: this.core.tree.fork })
741
+
742
+ // YOLO send over all the pages for now
743
+ const p = pages(this.core)
744
+ for (let index = 0; index < p.length; index++) {
745
+ peer.bitfield({ start: index, bitfield: p[index] })
746
+ }
747
+ }
748
+
749
+ onunknowncore (_, peer) {
750
+ this.pendingPeers.delete(peer)
751
+ this.updateAll()
752
+ // This is a no-op because there isn't any state to dealloc currently
753
+ }
754
+
755
+ oninfo ({ length, fork }, peer) {
756
+ const len = peer.state.length
757
+ const forked = peer.state.fork !== fork
758
+
759
+ peer.state.length = length
760
+ peer.state.fork = fork
761
+
762
+ if (forked) {
763
+ for (let i = peer.state.length; i < len; i++) {
764
+ peer.state.bitfield.set(i, false)
765
+ }
766
+
767
+ if (fork > this.core.tree.fork && length > 0) {
768
+ this.requests.clear(peer) // clear all pending requests from this peer as they forked, this can be removed if we add remote cancel
769
+ peer.request({ fork, upgrade: { start: 0, length } })
770
+ }
771
+ }
772
+
773
+ if (!peer.state.receivedInfo) {
774
+ peer.state.receivedInfo = true
775
+ }
776
+
777
+ // TODO: do we need to update ALL peers here? prob not
778
+ this.updateAll()
779
+ }
780
+
781
+ onbitfield ({ start, bitfield }, peer) {
782
+ if (bitfield.length < 1024) {
783
+ const buf = b4a.from(bitfield.buffer, bitfield.byteOffset, bitfield.byteLength)
784
+ const bigger = b4a.concat([buf, b4a.alloc(4096 - buf.length)])
785
+ bitfield = new Uint32Array(bigger.buffer, bigger.byteOffset, 1024)
786
+ }
787
+ peer.state.bitfield.pages.set(start, bitfield)
788
+
789
+ // TODO: do we need to update ALL peers here? prob not
790
+ this.updateAll()
791
+ }
792
+
793
+ onhave ({ start, length }, peer) {
794
+ const end = start + length
795
+ for (; start < end; start++) {
796
+ peer.state.bitfield.set(start, true)
797
+ }
798
+
799
+ // TODO: do we need to update ALL peers here? prob not
800
+ this.updateAll()
801
+ }
802
+
803
+ async ondata (proof, peer) {
804
+ try {
805
+ await this.requests.ondata(proof, peer)
806
+ } catch (err) {
807
+ // TODO: the request pool should have this cap, so we can just bubble it up
808
+ this.updateAll()
809
+ throw err
810
+ }
811
+ }
812
+
813
+ async onrequest (req, peer) {
814
+ const fork = req.fork || peer.state.fork
815
+ if (fork !== this.core.tree.fork) return
816
+
817
+ const proof = await this.core.tree.proof(req)
818
+
819
+ if (req.block && req.block.value) {
820
+ proof.block.value = await this.core.blocks.get(req.block.index)
821
+ }
822
+
823
+ peer.data(proof)
824
+ }
825
+ }
826
+
827
+ function paused (peer, fork) {
828
+ return peer.state.fork !== fork
829
+ }
830
+
831
+ function pages (core) {
832
+ const res = []
833
+
834
+ for (let i = 0; i < core.tree.length; i += core.bitfield.pageSize) {
835
+ const p = core.bitfield.page(i / core.bitfield.pageSize)
836
+ res.push(p)
837
+ }
838
+
839
+ return res
840
+ }
841
+
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
+ }