hypercore 10.0.0-alpha.25 → 10.0.0-alpha.26

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,851 +1,1459 @@
1
- const Protocol = require('./protocol')
2
- const RemoteBitfield = require('./remote-bitfield')
3
- const RandomIterator = require('random-array-iterator')
4
1
  const b4a = require('b4a')
2
+ const safetyCatch = require('safety-catch')
3
+ const RandomIterator = require('random-array-iterator')
4
+ const RemoteBitfield = require('./remote-bitfield')
5
+ const m = require('./messages')
6
+ const caps = require('./caps')
7
+
8
+ const DEFAULT_MAX_INFLIGHT = 32
9
+
10
+ class Attachable {
11
+ constructor () {
12
+ this.resolved = false
13
+ this.refs = []
14
+ }
15
+
16
+ attach (session) {
17
+ const r = {
18
+ context: this,
19
+ session,
20
+ sindex: 0,
21
+ rindex: 0,
22
+ resolve: null,
23
+ reject: null,
24
+ promise: null
25
+ }
5
26
 
6
- const PKG = require('../package.json')
7
- const USER_AGENT = PKG.name + '/' + PKG.version + '@nodejs'
27
+ r.sindex = session.push(r) - 1
28
+ r.rindex = this.refs.push(r) - 1
29
+ r.promise = new Promise((resolve, reject) => {
30
+ r.resolve = resolve
31
+ r.reject = reject
32
+ })
8
33
 
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
- this.uploading = true
18
- this.downloading = true
34
+ return r
19
35
  }
20
- }
21
36
 
22
- class InvertedPromise {
23
- constructor (resolve, reject, index) {
24
- this.index = index
25
- this.resolve = resolve
26
- this.reject = reject
37
+ detach (r) {
38
+ if (r.context !== this) return false
39
+
40
+ this._detach(r)
41
+ this._cancel(r)
42
+ this.gc()
43
+
44
+ return true
27
45
  }
28
- }
29
46
 
30
- class Request {
31
- constructor (index, seek, nodes) {
32
- this.peer = null
33
- this.index = index
34
- this.seek = seek
35
- this.value = seek === 0
36
- this.nodes = nodes
37
- this.promises = []
47
+ _detach (r) {
48
+ const rh = this.refs.pop()
49
+ const sh = r.session.pop()
50
+
51
+ if (r.rindex < this.refs.length - 1) this.refs[rh.rindex = r.rindex] = rh
52
+ if (r.sindex < r.session.length - 1) r.session[sh.sindex = r.sindex] = sh
53
+
54
+ r.context = null
55
+
56
+ return r
38
57
  }
39
58
 
40
- createPromise () {
41
- return new Promise((resolve, reject) => {
42
- this.promises.push(new InvertedPromise(resolve, reject, this.promises.length))
43
- })
59
+ gc () {
60
+ if (this.refs.length === 0) this._unref()
61
+ }
62
+
63
+ _cancel (r) {
64
+ r.reject(new Error('Request cancelled'))
65
+ }
66
+
67
+ _unref () {
68
+ // overwrite me
44
69
  }
45
70
 
46
71
  resolve (val) {
47
- for (let i = 0; i < this.promises.length; i++) {
48
- this.promises[i].resolve(val)
72
+ this.resolved = true
73
+ while (this.refs.length > 0) {
74
+ this._detach(this.refs[this.refs.length - 1]).resolve(val)
49
75
  }
50
76
  }
51
77
 
52
78
  reject (err) {
53
- for (let i = 0; i < this.promises.length; i++) {
54
- this.promises[i].reject(err)
79
+ this.resolved = true
80
+ while (this.refs.length > 0) {
81
+ this._detach(this.refs[this.refs.length - 1]).reject(err)
55
82
  }
56
83
  }
57
84
  }
58
85
 
59
- class Upgrade {
60
- constructor (minLength) {
61
- this.minLength = minLength
62
- this.promises = []
63
- }
86
+ class BlockRequest extends Attachable {
87
+ constructor (tracker, fork, index) {
88
+ super()
64
89
 
65
- update (peers, fork) {
66
- for (const peer of peers) {
67
- if (peer.state.length >= this.minLength && !paused(peer, fork)) return true
68
- if (!peer.state.receivedInfo) return true
69
- }
90
+ this.fork = fork
91
+ this.index = index
92
+ this.inflight = []
93
+ this.queued = false
94
+ this.tracker = tracker
95
+ }
70
96
 
71
- return false
97
+ _unref () {
98
+ if (this.inflight.length > 0) return
99
+ this.tracker.remove(this.fork, this.index)
72
100
  }
101
+ }
73
102
 
74
- createPromise () {
75
- return new Promise((resolve, reject) => {
76
- this.promises.push(new InvertedPromise(resolve, reject, this.promises.length))
77
- })
103
+ class RangeRequest extends Attachable {
104
+ constructor (ranges, fork, start, end, linear, blocks) {
105
+ super()
106
+
107
+ this.fork = fork
108
+ this.start = start
109
+ this.end = end
110
+ this.linear = linear
111
+ this.blocks = blocks
112
+ this.ranges = ranges
113
+
114
+ // As passed by the user, immut
115
+ this.userStart = start
116
+ this.userEnd = end
78
117
  }
79
118
 
80
- resolve (val) {
81
- for (let i = 0; i < this.promises.length; i++) {
82
- this.promises[i].resolve(val)
83
- }
119
+ _unref () {
120
+ const i = this.ranges.indexOf(this)
121
+ if (i === -1) return
122
+ const h = this.ranges.pop()
123
+ if (i < this.ranges.length - 1) this.ranges[i] = h
84
124
  }
85
125
 
86
- reject (err) {
87
- for (let i = 0; i < this.promises.length; i++) {
88
- this.promises[i].reject(err)
89
- }
126
+ _cancel (r) {
127
+ r.resolve(false)
90
128
  }
91
129
  }
92
130
 
93
- class UpgradeLock {
94
- constructor (peer, length) {
95
- this.peer = peer
131
+ class UpgradeRequest extends Attachable {
132
+ constructor (replicator, fork, length) {
133
+ super()
134
+
135
+ this.fork = fork
96
136
  this.length = length
97
- this.resolve = null
98
- this.promise = new Promise((resolve) => { this.resolve = resolve })
137
+ this.inflight = []
138
+ this.replicator = replicator
139
+ }
140
+
141
+ _unref () {
142
+ if (this.replicator.eagerUpgrade === true || this.inflight.length > 0) return
143
+ this.replicator._upgrade = null
144
+ }
145
+
146
+ _cancel (r) {
147
+ r.resolve(false)
99
148
  }
100
149
  }
101
150
 
102
- class Seek {
103
- constructor (seeker) {
104
- this.request = null
151
+ class SeekRequest extends Attachable {
152
+ constructor (seeks, fork, seeker) {
153
+ super()
154
+
155
+ this.fork = fork
105
156
  this.seeker = seeker
106
- this.promise = null
107
- this.finished = false
157
+ this.inflight = []
158
+ this.seeks = seeks
108
159
  }
109
160
 
110
- async update () {
111
- const res = await this.seeker.update()
112
- if (!res) return false
113
- this.finished = true
114
- this.promise.resolve(res)
115
- return true
161
+ _unref () {
162
+ if (this.inflight.length > 0) return
163
+ const i = this.seeks.indexOf(this)
164
+ if (i === -1) return
165
+ const h = this.seeks.pop()
166
+ if (i < this.seeks.length - 1) this.seeks[i] = h
167
+ }
168
+ }
169
+
170
+ class InflightTracker {
171
+ constructor () {
172
+ this._requests = []
173
+ this._free = []
116
174
  }
117
175
 
118
- createPromise () {
119
- return new Promise((resolve, reject) => {
120
- this.promise = new InvertedPromise(resolve, reject, 0)
121
- })
176
+ * [Symbol.iterator] () {
177
+ for (const req of this._requests) {
178
+ if (req !== null) yield req
179
+ }
122
180
  }
123
- }
124
181
 
125
- class Range {
126
- constructor (start, end, filter, linear) {
127
- this.start = start
128
- this.end = end
129
- this.filter = filter
130
- this.linear = !!linear
131
- this.promise = null
132
- this.done = false
182
+ add (req) {
183
+ const id = this._free.length ? this._free.pop() : this._requests.push(null)
133
184
 
134
- this._inv = null
135
- this._start = start // can be updated
136
- this._ranges = null // set be replicator
137
- this._resolved = false
138
- this._error = null
185
+ req.id = id
186
+ this._requests[id - 1] = req
187
+ return req
139
188
  }
140
189
 
141
- contains (req) {
142
- return this._start <= req.index && req.index < this.end
190
+ get (id) {
191
+ return id <= this._requests.length ? this._requests[id - 1] : null
143
192
  }
144
193
 
145
- update (bitfield) {
146
- if (this.end === -1) {
147
- while (bitfield.get(this._start)) this._start++
148
- return false
194
+ remove (id) {
195
+ if (id <= this._requests.length) {
196
+ this._requests[id - 1] = null
197
+ this._free.push(id)
149
198
  }
199
+ }
200
+ }
150
201
 
151
- for (; this._start < this.end; this._start++) {
152
- if (this.filter(this._start) && !bitfield.get(this._start)) return false
153
- }
202
+ class BlockTracker {
203
+ constructor (core) {
204
+ this._core = core
205
+ this._fork = core.tree.fork
154
206
 
155
- return true
207
+ this._indexed = new Map()
208
+ this._additional = []
156
209
  }
157
210
 
158
- resolve (done) {
159
- this._done(null, done)
211
+ * [Symbol.iterator] () {
212
+ yield * this._indexed.values()
213
+ yield * this._additional
160
214
  }
161
215
 
162
- destroy (err) {
163
- this._done(err, false)
216
+ has (fork, index) {
217
+ return this.get(fork, index) !== null
164
218
  }
165
219
 
166
- downloaded () {
167
- if (this.promise) return this.promise
168
- if (!this.done) return this._makePromise()
169
- if (this._error !== null) return Promise.reject(this._error)
170
- return Promise.resolve(this._resolved)
220
+ get (fork, index) {
221
+ if (this._fork === fork) return this._indexed.get(index) || null
222
+ for (const b of this._additional) {
223
+ if (b.index === index && b.fork === fork) return b
224
+ }
225
+ return null
171
226
  }
172
227
 
173
- _done (err, done) {
174
- if (this.done) return
175
- this.done = true
228
+ add (fork, index) {
229
+ // TODO: just rely on someone calling .update(fork) instead
230
+ if (this._fork !== this._core.tree.fork) this.update(this._core.tree.fork)
231
+
232
+ let b = this.get(fork, index)
233
+ if (b) return b
176
234
 
177
- if (this._ranges) {
178
- const i = this._ranges.indexOf(this)
235
+ b = new BlockRequest(this, fork, index)
179
236
 
180
- if (i === this._ranges.length - 1) this._ranges.pop()
181
- else if (i > -1) this._ranges[i] = this._ranges.pop()
237
+ if (fork === this._fork) this._indexed.set(index, b)
238
+ else this._additional.push(b)
239
+
240
+ return b
241
+ }
242
+
243
+ remove (fork, index) {
244
+ if (this._fork === fork) {
245
+ const b = this._indexed.get(index)
246
+ this._indexed.delete(index)
247
+ return b || null
182
248
  }
183
249
 
184
- this._ranges = null
185
- this._resolved = done
186
- this._error = err
250
+ for (let i = 0; i < this._additional.length; i++) {
251
+ const b = this._additional[i]
252
+ if (b.index !== index || b.fork !== fork) continue
253
+ if (i === this._additional.length - 1) this._additional.pop()
254
+ else this._additional[i] = this._additional.pop()
255
+ return b
256
+ }
187
257
 
188
- if (this._inv === null) return
189
- if (err) this._inv.reject(err)
190
- else this._inv.resolve(done)
258
+ return null
191
259
  }
192
260
 
193
- _makePromise () {
194
- this.promise = new Promise((resolve, reject) => {
195
- this._inv = new InvertedPromise(resolve, reject, 0)
196
- })
261
+ update (fork) {
262
+ if (this._fork === fork) return
263
+
264
+ const additional = this._additional
265
+ this._additional = []
266
+
267
+ for (const b of this._indexed.values()) {
268
+ // TODO: this is only needed cause we hot patch the fork ids below, revert that later
269
+ if (b.fork !== this._fork) additional.push(b)
270
+ else this._additional.push(b)
271
+ }
272
+ this._indexed.clear()
273
+
274
+ for (const b of additional) {
275
+ if (b.fork === fork) this._indexed.set(b.index, b)
276
+ else this._additional.push(b)
277
+ }
197
278
 
198
- return this.promise
279
+ this._fork = fork
199
280
  }
200
281
  }
201
282
 
202
- class RequestPool {
203
- constructor (replicator, core) {
283
+ class Peer {
284
+ constructor (replicator, protomux, channel) {
285
+ this.core = replicator.core
204
286
  this.replicator = replicator
205
- this.core = core
206
- this.pending = []
207
- this.seeks = []
208
- this.ranges = []
209
- this.requests = new Map()
210
- this.upgrading = null
211
- this.unforking = null
212
- this.eagerUpgrades = true
213
- this.paused = false
214
- }
215
-
216
- // We could make this faster by using some memory of each peer to store what they are involved in,
217
- // but that might not be worth the cost/complexity.
218
- clear (peer) {
219
- peer.state.inflight = 0
220
-
221
- for (const seek of this.seeks) {
222
- if (seek.request && seek.request.peer === peer) {
223
- // TODO: should prob remove cancel this request all together if no one else wants it
224
- // and nothing is inflight
225
- seek.request = null
226
- }
227
- }
228
- for (const req of this.requests.values()) {
229
- if (req.peer === peer) {
230
- req.peer = null
231
- this.pending.push(req)
232
- }
233
- }
234
- if (this.upgrading && this.upgrading.peer === peer) {
235
- this.upgrading.resolve()
236
- this.upgrading = null
237
- }
287
+ this.stream = protomux.stream
288
+ this.protomux = protomux
289
+
290
+ this.channel = channel
291
+ this.channel.userData = this
292
+
293
+ this.wireSync = this.channel.messages[0]
294
+ this.wireRequest = this.channel.messages[1]
295
+ this.wireCancel = null
296
+ this.wireData = this.channel.messages[3]
297
+ this.wireNoData = this.channel.messages[4]
298
+ this.wireWant = this.channel.messages[5]
299
+ this.wireUnwant = this.channel.messages[6]
300
+ this.wireBitfield = this.channel.messages[7]
301
+ this.wireRange = this.channel.messages[8]
302
+ this.wireExtension = this.channel.messages[9]
303
+
304
+ this.inflight = 0
305
+ this.maxInflight = DEFAULT_MAX_INFLIGHT
306
+
307
+ this.canUpgrade = true
308
+
309
+ this.needsSync = false
310
+ this.syncsProcessing = 0
311
+
312
+ // TODO: tweak pipelining so that data sent BEFORE remoteOpened is not cap verified!
313
+ // we might wanna tweak that with some crypto, ie use the cap to encrypt it...
314
+ // or just be aware of that, to only push non leaky data
315
+
316
+ this.remoteOpened = false
317
+ this.remoteBitfield = new RemoteBitfield()
318
+
319
+ this.remoteFork = 0
320
+ this.remoteLength = 0
321
+ this.remoteCanUpgrade = false
322
+ this.remoteUploading = true
323
+ this.remoteDownloading = true
324
+ this.remoteSynced = false
325
+
326
+ this.lengthAcked = 0
327
+
328
+ this.extensions = new Map()
329
+ this.lastExtensionSent = ''
330
+ this.lastExtensionRecv = ''
331
+
332
+ replicator._ifAvailable++
238
333
  }
239
334
 
240
- isRequesting (index) {
241
- return this.requests.has(index)
335
+ signalUpgrade () {
336
+ if (this._shouldUpdateCanUpgrade() === true) this._updateCanUpgradeAndSync()
337
+ else this.sendSync()
242
338
  }
243
339
 
244
- update (peer) {
245
- if (peer.state.inflight >= peer.state.maxInflight) return false
246
- if (peer.downloading === false || peer.state.uploading === false) return false
247
- if (this.paused) return false
340
+ broadcastRange (start, length, drop) {
341
+ this.wireRange.send({
342
+ drop,
343
+ start,
344
+ length
345
+ })
346
+ }
248
347
 
249
- if (peer.state.fork > this.core.tree.fork) {
250
- // we message them in the info recv
251
- return true
252
- }
348
+ extension (name, message) {
349
+ this.wireExtension.send({ name: name === this.lastExtensionSent ? '' : name, message })
350
+ this.lastExtensionSent = name
351
+ }
253
352
 
254
- // technically we'd like to run seeks at the same custom prio as reqs
255
- // but this is a lot simpler and they should run asap anyway as they
256
- // are super low cost (hash only request)
257
- for (const seek of this.seeks) {
258
- if (this._updateSeek(peer, seek)) return true
259
- }
353
+ onextension (message) {
354
+ const name = message.name || this.lastExtensionRecv
355
+ this.lastExtensionRecv = name
356
+ const ext = this.extensions.get(name)
357
+ if (ext) ext._onmessage({ start: 0, end: message.byteLength, buffer: message.message }, this)
358
+ }
260
359
 
261
- if (this.pendingUpgrade) {
262
- if (this._updateUpgrade(peer)) return true
360
+ sendSync () {
361
+ if (this.syncsProcessing !== 0) {
362
+ this.needsSync = true
363
+ return
263
364
  }
264
365
 
265
- const pending = new RandomIterator(this.pending) // can be cached
266
- for (const req of pending) {
267
- if (this._updatePeer(peer, req)) {
268
- pending.dequeue()
269
- return true
270
- }
366
+ if (this.core.tree.fork !== this.remoteFork) {
367
+ this.canUpgrade = false
271
368
  }
272
369
 
273
- const ranges = new RandomIterator(this.ranges) // can be cached
274
- for (const range of ranges) {
275
- if (this._updateRange(peer, range)) return true
370
+ this.needsSync = false
371
+
372
+ this.wireSync.send({
373
+ fork: this.core.tree.fork,
374
+ length: this.core.tree.length,
375
+ remoteLength: this.core.tree.fork === this.remoteFork ? this.remoteLength : 0,
376
+ canUpgrade: this.canUpgrade,
377
+ uploading: true,
378
+ downloading: true
379
+ })
380
+ }
381
+
382
+ onopen ({ capability }) {
383
+ const expected = caps.replicate(this.stream.isInitiator === false, this.replicator.key, this.stream.handshakeHash)
384
+
385
+ if (b4a.equals(capability, expected) !== true) { // TODO: change this to a rejection instead, less leakage
386
+ throw new Error('Remote sent an invalid capability')
276
387
  }
277
388
 
278
- if (this.eagerUpgrades && !this.upgrading) {
279
- return this._updateUpgrade(peer)
389
+ if (this.remoteOpened === true) return
390
+ this.remoteOpened = true
391
+
392
+ this.protomux.cork()
393
+
394
+ this.sendSync()
395
+
396
+ const p = pages(this.core)
397
+
398
+ for (let index = 0; index < p.length; index++) {
399
+ this.wireBitfield.send({
400
+ start: index * this.core.bitfield.pageSize,
401
+ bitfield: p[index]
402
+ })
280
403
  }
281
404
 
282
- return false
405
+ this.replicator._ifAvailable--
406
+ this.replicator._addPeer(this)
407
+
408
+ this.protomux.uncork()
283
409
  }
284
410
 
285
- _updateSeek (peer, seek) {
286
- if (seek.request) return false
287
- // We have to snapshot the nodes here now, due to the caching of the request...
288
- const nodes = log2(seek.seeker.end - seek.seeker.start)
289
- seek.request = this._requestRange(peer, seek.seeker.start, seek.seeker.end, seek.seeker.bytes, nodes)
290
- return seek.request !== null
411
+ onclose (isRemote) {
412
+ if (this.remoteOpened === false) {
413
+ this.replicator._ifAvailable--
414
+ this.replicator.updateAll()
415
+ return
416
+ }
417
+
418
+ this.remoteOpened = false
419
+ this.replicator._removePeer(this)
291
420
  }
292
421
 
293
- _updatePeer (peer, req) {
294
- const remote = peer.state.bitfield
295
- const local = this.core.bitfield
422
+ async onsync ({ fork, length, remoteLength, canUpgrade, uploading, downloading }) {
423
+ const lengthChanged = length !== this.remoteLength
424
+ const sameFork = (fork === this.core.tree.fork)
296
425
 
297
- if (!remote.get(req.index) || local.get(req.index)) return false
426
+ this.remoteSynced = true
427
+ this.remoteFork = fork
428
+ this.remoteLength = length
429
+ this.remoteCanUpgrade = canUpgrade
430
+ this.remoteUploading = uploading
431
+ this.remoteDownloading = downloading
298
432
 
299
- this.send(peer, req)
300
- return true
301
- }
433
+ this.lengthAcked = sameFork ? remoteLength : 0
434
+ this.syncsProcessing++
302
435
 
303
- _updateRange (peer, range) {
304
- const end = range.end === -1 ? peer.state.length : range.end
305
- if (end <= range._start) return false
436
+ this.replicator._updateFork(this)
306
437
 
307
- if (range.linear) return !!this._requestRange(peer, range._start, end, 0, 0, range.filter)
438
+ if (this.remoteLength > this.core.tree.length && this.lengthAcked === this.core.tree.length) {
439
+ if (this.replicator._addUpgradeMaybe() !== null) this._update()
440
+ }
308
441
 
309
- const r = range._start + Math.floor(Math.random() * (end - range._start))
310
- return !!(
311
- this._requestRange(peer, r, end, 0, 0, range.filter) ||
312
- this._requestRange(peer, range._start, r, 0, 0, range.filter)
313
- )
314
- }
442
+ const upgrade = (lengthChanged === false || sameFork === false)
443
+ ? this.canUpgrade && sameFork
444
+ : await this._canUpgrade(length, fork)
315
445
 
316
- _updateUpgrade (peer) {
317
- const minLength = this.pendingUpgrade
318
- ? this.pendingUpgrade.minLength
319
- : this.core.tree.length + 1
446
+ if (length === this.remoteLength && fork === this.core.tree.fork) {
447
+ this.canUpgrade = upgrade
448
+ }
320
449
 
321
- if (this.upgrading || peer.state.length < minLength) return false
322
- this.upgrading = new UpgradeLock(peer, peer.state.length)
450
+ if (--this.syncsProcessing !== 0) return // ie not latest
323
451
 
324
- const data = {
325
- fork: this.core.tree.fork,
326
- seek: null,
327
- block: null,
328
- upgrade: { start: this.core.tree.length, length: peer.state.length - this.core.tree.length }
452
+ if (this.needsSync === true || (this.core.tree.fork === this.remoteFork && this.core.tree.length > this.remoteLength)) {
453
+ this.signalUpgrade()
329
454
  }
330
455
 
331
- peer.request(data)
332
- return true
456
+ this._update()
333
457
  }
334
458
 
335
- checkTimeouts (peers) {
336
- if (!this.pendingUpgrade || this.upgrading) return
337
- if (this.pendingUpgrade.update(peers, this.core.tree.fork)) return
338
- this.pendingUpgrade.resolve(false)
339
- this.pendingUpgrade = null
459
+ _shouldUpdateCanUpgrade () {
460
+ return this.core.tree.fork === this.remoteFork &&
461
+ this.core.tree.length > this.remoteLength &&
462
+ this.canUpgrade === false &&
463
+ this.syncsProcessing === 0
340
464
  }
341
465
 
342
- _requestRange (peer, start, end, seek, nodes, filter = tautology) {
343
- const remote = peer.state.bitfield
344
- const local = this.core.bitfield
345
-
346
- // TODO: use 0 instead of -1 as end=0 should never be added!
347
- if (end === -1) end = peer.state.length
466
+ async _updateCanUpgradeAndSync () {
467
+ const len = this.core.tree.length
468
+ const fork = this.core.tree.fork
348
469
 
349
- for (let i = start; i < end; i++) {
350
- if (!filter(i) || !remote.get(i) || local.get(i)) continue
351
- // TODO: if this was a NO_VALUE request, retry if no blocks can be found elsewhere
352
- if (this.requests.has(i)) continue
470
+ const canUpgrade = await this._canUpgrade(this.remoteLength, this.remoteFork)
353
471
 
354
- // TODO: if seeking and i >= core.length, let that takes precendance in the upgrade req
355
- const req = new Request(i, i < this.core.tree.length ? seek : 0, nodes)
356
- this.requests.set(i, req)
357
- this.send(peer, req)
358
- return req
472
+ if (this.syncsProcessing > 0 || len !== this.core.tree.length || fork !== this.core.tree.fork) {
473
+ return
474
+ }
475
+ if (canUpgrade === this.canUpgrade) {
476
+ return
359
477
  }
360
478
 
361
- return null
479
+ this.canUpgrade = canUpgrade
480
+ this.sendSync()
362
481
  }
363
482
 
364
- // send handles it's own errors so we do not need to await/catch it
365
- async send (peer, req) {
366
- req.peer = peer
367
- peer.state.inflight++ // TODO: a non value request should count less than a value one
483
+ // Safe to call in the background - never fails
484
+ async _canUpgrade (remoteLength, remoteFork) {
485
+ if (remoteFork !== this.core.tree.fork) return false
368
486
 
369
- // TODO: also check if remote can even upgrade us lol
370
- let needsUpgrade = peer.state.length > this.core.tree.length || !!(!this.upgrading && this.pendingUpgrade)
371
- const fork = this.core.tree.fork
372
- let upgrading = false
487
+ if (remoteLength === 0) return true
488
+ if (remoteLength >= this.core.tree.length) return false
373
489
 
374
- while (needsUpgrade) {
375
- if (!this.upgrading) {
376
- if (peer.state.length <= this.core.tree.length) {
377
- needsUpgrade = false
378
- break
379
- }
380
- // TODO: if the peer fails, we need to resolve the promise as well woop woop
381
- // so we need some tracking mechanics for upgrades in general.
382
- this.upgrading = new UpgradeLock(peer, peer.state.length)
383
- upgrading = true
384
- break
385
- }
386
- if (req.index < this.core.tree.length) {
387
- needsUpgrade = false
388
- break
389
- }
490
+ try {
491
+ // Rely on caching to make sure this is cheap...
492
+ const canUpgrade = await this.core.tree.upgradeable(remoteLength)
493
+
494
+ if (remoteFork !== this.core.tree.fork) return false
390
495
 
391
- await this.upgrading.promise
392
- needsUpgrade = peer.state.length > this.core.tree.length || !!(!this.upgrading && this.pendingUpgrade)
496
+ return canUpgrade
497
+ } catch {
498
+ return false
393
499
  }
500
+ }
394
501
 
395
- const data = {
396
- fork,
397
- seek: req.seek ? { bytes: req.seek } : null,
398
- block: { index: req.index, value: req.value, nodes: 0 },
399
- upgrade: needsUpgrade ? { start: this.core.tree.length, length: peer.state.length - this.core.tree.length } : null
502
+ async _getProof (msg) {
503
+ const proof = await this.core.tree.proof(msg)
504
+
505
+ if (proof.block) {
506
+ if (msg.fork !== this.core.tree.fork) return null
507
+ proof.block.value = await this.core.blocks.get(msg.block.index)
400
508
  }
401
509
 
402
- if (data.block.index < this.core.tree.length || this.core.truncating > 0) {
510
+ return proof
511
+ }
512
+
513
+ async onrequest (msg) {
514
+ let proof = null
515
+
516
+ // TODO: could still be answerable if (index, fork) is an ancestor of the current fork
517
+ if (msg.fork === this.core.tree.fork) {
403
518
  try {
404
- data.block.nodes = Math.max(req.nodes, await this.core.tree.nodes(data.block.index * 2))
405
- } catch (err) {
406
- console.error('TODO handle me:', err.stack)
519
+ proof = await this._getProof(msg)
520
+ } catch (err) { // TODO: better error handling here, ie custom errors
521
+ safetyCatch(err)
407
522
  }
408
523
  }
409
524
 
410
- if (peer.destroyed) {
411
- req.peer = null
412
- this.pending.push(req)
413
- if (upgrading) {
414
- this.upgrading.resolve()
415
- this.upgrading = null
525
+ if (proof !== null) {
526
+ if (proof.block !== null) {
527
+ this.replicator.onupload(proof.block.index, proof.block.value, this)
416
528
  }
417
- this.replicator.updateAll()
529
+
530
+ this.wireData.send({
531
+ request: msg.id,
532
+ fork: msg.fork,
533
+ block: proof.block,
534
+ hash: proof.hash,
535
+ seek: proof.seek,
536
+ upgrade: proof.upgrade
537
+ })
418
538
  return
419
539
  }
420
540
 
421
- if (fork !== this.core.tree.fork || paused(peer, this.core.tree.fork) || this.core.truncating > 0) {
422
- if (peer.state.inflight > 0) peer.state.inflight--
423
- if (req.promises.length) { // someone is eagerly waiting for this request
424
- req.peer = null // resend on some other peer
425
- this.pending.push(req)
426
- } else { // otherwise delete the request
427
- this.requests.delete(req.index)
428
- }
429
- if (upgrading) {
430
- this.upgrading.resolve()
431
- this.upgrading = null
541
+ this.wireNoData.send({
542
+ request: msg.id
543
+ })
544
+ }
545
+
546
+ async ondata (data) {
547
+ const req = data.request > 0 ? this.replicator._inflight.get(data.request) : null
548
+ const reorg = data.fork > this.core.tree.fork
549
+
550
+ // no push atm, TODO: check if this satisfies another pending request
551
+ // allow reorg pushes tho as those are not written to storage so we'll take all the help we can get
552
+ if (req === null && reorg === false) return
553
+
554
+ if (req !== null) {
555
+ if (req.peer !== this) return
556
+ this.inflight--
557
+ this.replicator._inflight.remove(req.id)
558
+ }
559
+
560
+ if (reorg === true) return this.replicator._onreorgdata(this, req, data)
561
+
562
+ try {
563
+ if (!matchingRequest(req, data) || !(await this.core.verify(data, this))) {
564
+ this.replicator._onnodata(this, req)
565
+ return
432
566
  }
433
- return
567
+ } catch (err) {
568
+ this.replicator._onnodata(this, req)
569
+ throw err
570
+ }
571
+
572
+ this.replicator._ondata(this, req, data)
573
+
574
+ if (this._shouldUpdateCanUpgrade() === true) {
575
+ this._updateCanUpgradeAndSync()
434
576
  }
577
+ }
578
+
579
+ onnodata ({ request }) {
580
+ const req = request > 0 ? this.replicator._inflight.get(request) : null
581
+
582
+ if (req === null || req.peer !== this) return
583
+
584
+ this.inflight--
585
+ this.replicator._inflight.remove(req.id)
586
+ this.replicator._onnodata(this, req)
587
+ }
588
+
589
+ onwant () {
590
+ // TODO
591
+ }
435
592
 
436
- peer.request(data)
593
+ onunwant () {
594
+ // TODO
437
595
  }
438
596
 
439
- async _onupgrade (proof, peer) {
440
- if (!this.upgrading || !proof.upgrade) return
441
- if (this.unforking) return
597
+ onbitfield ({ start, bitfield }) {
598
+ // TODO: tweak this to be more generic
599
+
600
+ if (bitfield.length < 1024) {
601
+ const buf = b4a.from(bitfield.buffer, bitfield.byteOffset, bitfield.byteLength)
602
+ const bigger = b4a.concat([buf, b4a.alloc(4096 - buf.length)])
603
+ bitfield = new Uint32Array(bigger.buffer, bigger.byteOffset, 1024)
604
+ }
605
+
606
+ this.remoteBitfield.pages.set(start / this.core.bitfield.pageSize, bitfield)
442
607
 
443
- await this.core.verify(proof, peer)
608
+ this._update()
609
+ }
444
610
 
445
- // TODO: validate that we actually upgraded our length as well
446
- this.upgrading.resolve()
447
- this.upgrading = null
611
+ onrange ({ drop, start, length }) {
612
+ const has = drop === false
448
613
 
449
- if (this.pendingUpgrade) {
450
- this.pendingUpgrade.resolve(true)
451
- this.pendingUpgrade = null
614
+ for (const end = start + length; start < end; start++) {
615
+ this.remoteBitfield.set(start, has)
452
616
  }
453
617
 
454
- if (this.seeks.length) await this._updateSeeks(null)
618
+ if (drop === false) this._update()
619
+ }
455
620
 
456
- this.update(peer)
621
+ onreorghint () {
622
+ // TODO
457
623
  }
458
624
 
459
- async _onfork (proof, peer) {
460
- // TODO: if proof is from a newer fork than currently unforking, restart
625
+ _update () {
626
+ // TODO: if this is in a batch or similar it would be better to defer it
627
+ // we could do that with nextTick/microtick mb? (combined with a property on the session to signal read buffer mb)
628
+ this.replicator.updatePeer(this)
629
+ }
461
630
 
462
- if (this.unforking) {
463
- await this.unforking.update(proof)
464
- } else {
465
- const reorg = await this.core.tree.reorg(proof)
466
- const verified = reorg.signedBy(this.core.header.signer.publicKey)
467
- if (!verified) throw new Error('Remote signature could not be verified')
468
- this.unforking = reorg
469
- }
470
-
471
- if (!this.unforking.finished) {
472
- for (let i = this.unforking.want.start; i < this.unforking.want.end; i++) {
473
- if (peer.state.bitfield.get(i)) {
474
- const data = {
475
- fork: this.unforking.fork,
476
- seek: null,
477
- block: { index: i, value: false, nodes: this.unforking.want.nodes },
478
- upgrade: null
479
- }
480
- peer.request(data)
481
- return
482
- }
483
- }
484
- return
631
+ _makeRequest (fork, needsUpgrade) {
632
+ if (needsUpgrade === true && this.replicator._shouldUpgrade(this) === false) {
633
+ return null
485
634
  }
486
635
 
487
- const reorg = this.unforking
488
- this.unforking = null
489
- await this.core.reorg(reorg)
636
+ if (needsUpgrade === false && fork === this.core.tree.fork && this.replicator._autoUpgrade(this) === true) {
637
+ needsUpgrade = true
638
+ }
490
639
 
491
- // reset ranges, also need to reset seeks etc
492
- for (const r of this.ranges) {
493
- r._start = 0
640
+ return {
641
+ peer: this,
642
+ id: 0,
643
+ fork,
644
+ block: null,
645
+ hash: null,
646
+ seek: null,
647
+ upgrade: needsUpgrade === false
648
+ ? null
649
+ : { start: this.core.tree.length, length: this.remoteLength - this.core.tree.length }
494
650
  }
651
+ }
495
652
 
496
- this.replicator.updateAll()
653
+ _requestUpgrade (u) {
654
+ const req = this._makeRequest(u.fork, true)
655
+ if (req === null) return false
497
656
 
498
- // TODO: we gotta clear out old requests as well here pointing at the old fork
657
+ this._send(req)
658
+
659
+ return true
499
660
  }
500
661
 
501
- async ondata (proof, peer) {
502
- // technically if the remote peer pushes a DATA someone else requested inflight can go to zero
503
- if (peer.state.inflight > 0) peer.state.inflight--
662
+ _requestSeek (s) {
663
+ if (s.seeker.start >= this.core.tree.length) {
664
+ const req = this._makeRequest(s.fork, true)
504
665
 
505
- // if we get a message from another fork, maybe "unfork".
506
- if (peer.state.fork !== this.core.tree.fork) {
507
- // TODO: user should opt-in to this behaivour
508
- if (peer.state.fork > this.core.tree.fork) return this._onfork(proof, peer)
509
- return
666
+ // We need an upgrade for the seek, if non can be provided, skip
667
+ if (req === null) return false
668
+
669
+ req.seek = { bytes: s.seeker.bytes }
670
+
671
+ s.inflight.push(req)
672
+ this._send(req)
673
+
674
+ return true
510
675
  }
511
676
 
512
- // ignore incoming messages during an unfork.
513
- if (this.unforking) return
677
+ const len = s.seeker.end - s.seeker.start
678
+ const off = s.seeker.start + Math.floor(Math.random() * len)
514
679
 
515
- if (!proof.block) return this._onupgrade(proof, peer)
680
+ for (let i = 0; i < len; i++) {
681
+ let index = off + i
682
+ if (index > s.seeker.end) index -= len
516
683
 
517
- const { index, value } = proof.block
518
- const req = this.requests.get(index)
684
+ if (this.remoteBitfield.get(index) === false) continue
685
+ if (this.core.bitfield.get(index) === true) continue
519
686
 
520
- // no push allowed, TODO: add flag to allow pushes
521
- if (!req || req.peer !== peer || (value && !req.value) || (proof.upgrade && !this.upgrading)) return
687
+ // Check if this block is currently inflight - if so pick another
688
+ const b = this.replicator._blocks.get(s.fork, index)
689
+ if (b !== null && b.inflight.length > 0) continue
522
690
 
523
- try {
524
- await this.core.verify(proof, peer)
525
- } catch (err) {
526
- this.requests.delete(index)
527
- throw err
528
- }
691
+ // Block is not inflight, but we only want the hash, check if that is inflight
692
+ const h = this.replicator._hashes.add(s.fork, index)
693
+ if (h.inflight.length > 0) continue
529
694
 
530
- // TODO: validate that we actually upgraded our length as well
531
- if (proof.upgrade) {
532
- this.upgrading.resolve()
533
- this.upgrading = null
695
+ const req = this._makeRequest(s.fork, false)
534
696
 
535
- if (this.pendingUpgrade) {
536
- this.pendingUpgrade.resolve(true)
537
- this.pendingUpgrade = null
538
- }
697
+ req.hash = { index: 2 * index, nodes: 0 }
698
+ req.seek = { bytes: s.seeker.bytes }
699
+
700
+ s.inflight.push(req)
701
+ h.inflight.push(req)
702
+ this._send(req)
703
+
704
+ return true
539
705
  }
540
706
 
541
- await this._resolveRequest(req, index, value)
707
+ return false
708
+ }
542
709
 
543
- this.update(peer)
710
+ // mb turn this into a YES/NO/MAYBE enum, could simplify ifavail logic
711
+ _blockAvailable (b) { // TODO: fork also
712
+ return this.remoteBitfield.get(b.index)
544
713
  }
545
714
 
546
- async _resolveRequest (req, index, value) {
547
- // if our request types match, clear inflight, otherwise we upgraded a hash req to a value req
548
- const resolved = req.value === !!value
549
- if (resolved) {
550
- this.requests.delete(index)
551
- req.resolve(value)
552
- }
715
+ _requestBlock (b) {
716
+ if (this.remoteBitfield.get(b.index) === false) return false
553
717
 
554
- if (this.seeks.length) await this._updateSeeks(req)
718
+ const req = this._makeRequest(b.fork, b.index >= this.core.tree.length)
719
+ if (req === null) return false
555
720
 
556
- // TODO: only do this for active ranges, ie ranges with inflight reqs...
557
- for (let i = 0; i < this.ranges.length; i++) {
558
- const r = this.ranges[i]
559
- if (!r.contains(req)) continue
560
- if (!r.update(this.core.bitfield)) continue
561
- r.resolve(true)
562
- i--
563
- }
721
+ req.block = { index: b.index, nodes: 0 }
722
+
723
+ b.inflight.push(req)
724
+ this._send(req)
725
+
726
+ return true
564
727
  }
565
728
 
566
- async _updateSeeks (req) {
567
- for (let i = 0; i < this.seeks.length; i++) {
568
- await this.seeks[i].update()
569
- }
729
+ _requestRange (r) {
730
+ const end = Math.min(r.end === -1 ? this.remoteLength : r.end, this.remoteLength)
731
+ if (end < r.start || r.fork !== this.remoteFork) return false
570
732
 
571
- for (let i = 0; i < this.seeks.length; i++) {
572
- const seek = this.seeks[i]
733
+ const len = end - r.start
734
+ const off = r.start + (r.linear ? 0 : Math.floor(Math.random() * len))
573
735
 
574
- if (seek.finished) {
575
- if (this.seeks.length > 1 && i < this.seeks.length - 1) {
576
- this.seeks[i] = this.seeks[this.seeks.length - 1]
577
- i--
578
- }
579
- this.seeks.pop()
736
+ // TODO: we should weight this to request blocks < .length first
737
+ // as they are "cheaper" and will trigger an auto upgrade if possible
738
+ // If no blocks < .length is avaible then try the "needs upgrade" range
739
+
740
+ for (let i = 0; i < len; i++) {
741
+ let index = off + i
742
+ if (index >= end) index -= len
743
+
744
+ if (r.blocks !== null) index = r.blocks[index]
745
+
746
+ if (this.remoteBitfield.get(index) === false) continue
747
+ if (this.core.bitfield.get(index) === true) continue
748
+
749
+ const b = this.replicator._blocks.add(r.fork, index)
750
+ if (b.inflight.length > 0) continue
751
+
752
+ const req = this._makeRequest(r.fork, index >= this.core.tree.length)
753
+
754
+ // If the request cannot be satisfied, dealloc the block request if no one is subscribed to it
755
+ if (req === null) {
756
+ b.gc()
757
+ return false
580
758
  }
581
- if (req !== null && seek.request === req) seek.request = null
759
+
760
+ req.block = { index, nodes: 0 }
761
+
762
+ b.inflight.push(req)
763
+ this._send(req)
764
+
765
+ return true
582
766
  }
583
- }
584
767
 
585
- async resolveBlock (index, value) {
586
- const req = this.requests.get(index)
587
- if (req) await this._resolveRequest(req, index, value)
768
+ return false
588
769
  }
589
770
 
590
- upgrade () {
591
- if (this.pendingUpgrade) return this.pendingUpgrade.createPromise()
592
- this.pendingUpgrade = new Upgrade(this.core.tree.length + 1)
593
- return this.pendingUpgrade.createPromise()
594
- }
771
+ _requestForkProof (f) {
772
+ const req = this._makeRequest(f.fork, false)
595
773
 
596
- range (range) {
597
- if (range.ranges === null || this.ranges.index(range) > -1) return
598
- this.ranges.push(range)
599
- range.ranges = range
600
- }
774
+ req.upgrade = { start: 0, length: this.remoteLength }
601
775
 
602
- seek (seeker) {
603
- const s = new Seek(seeker)
604
- this.seeks.push(s)
605
- return s.createPromise()
776
+ f.inflight.push(req)
777
+ this._send(req)
606
778
  }
607
779
 
608
- block (index) {
609
- const e = this.requests.get(index)
780
+ _requestForkRange (f) {
781
+ if (f.fork !== this.remoteFork || f.batch.want === null) return false
610
782
 
611
- if (e) {
612
- if (!e.value) {
613
- e.value = true
614
- if (e.peer) this.send(e.peer, e)
615
- }
783
+ const end = Math.min(f.batch.want.end, this.remoteLength)
784
+ if (end < f.batch.want.start) return false
785
+
786
+ const len = end - f.batch.want.start
787
+ const off = f.batch.want.start + Math.floor(Math.random() * len)
788
+
789
+ for (let i = 0; i < len; i++) {
790
+ let index = off + i
791
+ if (index >= end) index -= len
792
+
793
+ if (this.remoteBitfield.get(index) === false) continue
794
+
795
+ const req = this._makeRequest(f.fork, false)
616
796
 
617
- return e.createPromise()
797
+ req.hash = { index: 2 * index, nodes: f.batch.want.nodes }
798
+
799
+ f.inflight.push(req)
800
+ this._send(req)
801
+
802
+ return true
618
803
  }
619
804
 
620
- const r = new Request(index, 0, 0)
805
+ return false
806
+ }
807
+
808
+ async _send (req) {
809
+ const fork = this.core.tree.fork
810
+
811
+ this.inflight++
812
+ this.replicator._inflight.add(req)
621
813
 
622
- this.requests.set(index, r)
623
- this.pending.push(r)
814
+ if (req.upgrade !== null && req.fork === fork) {
815
+ const u = this.replicator._addUpgrade()
816
+ u.inflight.push(req)
817
+ }
818
+
819
+ try {
820
+ if (req.block !== null && req.fork === fork) req.block.nodes = await this.core.tree.missingNodes(2 * req.block.index)
821
+ if (req.hash !== null && req.fork === fork) req.hash.nodes = await this.core.tree.missingNodes(req.hash.index)
822
+ } catch (err) {
823
+ this.stream.destroy(err)
824
+ return
825
+ }
624
826
 
625
- return r.createPromise()
827
+ this.wireRequest.send(req)
626
828
  }
627
829
  }
628
830
 
629
831
  module.exports = class Replicator {
630
- constructor (core, { onupdate, onupload }) {
832
+ constructor (core, key, { eagerUpgrade = true, allowFork = true, onpeerupdate = noop, onupload = noop } = {}) {
833
+ this.key = key
834
+ this.discoveryKey = core.crypto.discoveryKey(key)
631
835
  this.core = core
632
- this.peers = []
633
- this.requests = new RequestPool(this, core)
634
- this.updating = null
635
- this.pendingPeers = new Set()
636
- this.onupdate = onupdate
836
+ this.eagerUpgrade = eagerUpgrade
837
+ this.allowFork = allowFork
838
+ this.onpeerupdate = onpeerupdate
637
839
  this.onupload = onupload
840
+ this.peers = []
841
+
842
+ this._inflight = new InflightTracker()
843
+ this._blocks = new BlockTracker(core)
844
+ this._hashes = new BlockTracker(core)
845
+
846
+ this._queued = []
847
+
848
+ this._seeks = []
849
+ this._upgrade = null
850
+ this._reorgs = []
851
+ this._ranges = []
852
+
853
+ this._ifAvailable = 0
854
+ this._updatesPending = 0
855
+ this._applyingReorg = false
638
856
  }
639
857
 
640
- static createProtocol (noiseStream, opts) {
641
- return new Protocol(noiseStream, {
642
- ...opts,
643
- protocolVersion: 0,
644
- userAgent: USER_AGENT
645
- })
858
+ cork () {
859
+ for (const peer of this.peers) peer.protomux.cork()
646
860
  }
647
861
 
648
- joinProtocol (protocol, key, discoveryKey) {
649
- if (protocol.isRegistered(discoveryKey)) return
650
- const peer = protocol.registerPeer(key, discoveryKey, this, new RemoteState(this.core))
651
- this.pendingPeers.add(peer)
862
+ uncork () {
863
+ for (const peer of this.peers) peer.protomux.uncork()
652
864
  }
653
865
 
654
- broadcastBlock (start) {
655
- const msg = { start, length: 1 }
656
- for (const peer of this.peers) peer.have(msg)
866
+ signalUpgrade () {
867
+ for (const peer of this.peers) peer.signalUpgrade()
868
+ }
657
869
 
658
- // in case of writable peer waiting for the block itself
659
- // we trigger the resolver to see if can't resolve it now
660
- if (this.requests.isRequesting(start)) {
661
- this._resolveBlock(start).then(noop, noop)
662
- }
870
+ broadcastRange (start, length, drop = false) {
871
+ for (const peer of this.peers) peer.broadcastRange(start, length, drop)
663
872
  }
664
873
 
665
- broadcastInfo () {
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
- })
874
+ addUpgrade (session) {
875
+ if (this._upgrade !== null) {
876
+ const ref = this._upgrade.attach(session)
877
+ this._checkUpgradeIfAvailable()
878
+ return ref
673
879
  }
880
+
881
+ const ref = this._addUpgrade().attach(session)
882
+
883
+ for (let i = this._reorgs.length - 1; i >= 0 && this._applyingReorg === false; i--) {
884
+ const f = this._reorgs[i]
885
+ if (f.batch !== null && f.batch.finished) {
886
+ this._applyReorg(f)
887
+ break
888
+ }
889
+ }
890
+
674
891
  this.updateAll()
892
+
893
+ return ref
675
894
  }
676
895
 
677
- requestUpgrade () {
678
- const promise = this.requests.upgrade()
896
+ addBlock (session, index, fork = this.core.tree.fork) {
897
+ const b = this._blocks.add(fork, index)
898
+ const ref = b.attach(session)
899
+
900
+ this._queueBlock(b)
679
901
  this.updateAll()
680
- return promise
902
+
903
+ return ref
681
904
  }
682
905
 
683
- requestSeek (seeker) {
684
- if (typeof seeker === 'number') seeker = this.core.tree.seek(seeker)
685
- const promise = this.requests.seek(seeker)
906
+ addSeek (session, seeker) {
907
+ const s = new SeekRequest(this._seeks, this.core.tree.fork, seeker)
908
+ const ref = s.attach(session)
909
+
910
+ this._seeks.push(s)
686
911
  this.updateAll()
687
- return promise
912
+
913
+ return ref
688
914
  }
689
915
 
690
- requestBlock (index) {
691
- const promise = this.requests.block(index)
692
- this.updateAll()
693
- return promise
916
+ addRange (session, { start = 0, end = -1, length = toLength(start, end), blocks = null, linear = false } = {}) {
917
+ if (blocks !== null) {
918
+ if (start >= blocks.length) start = blocks.length
919
+ if (length === -1 || start + length > blocks.length) length = blocks.length - start
920
+ }
921
+
922
+ const r = new RangeRequest(this._ranges, this.core.tree.fork, start, length === -1 ? -1 : start + length, linear, blocks)
923
+ const ref = r.attach(session)
924
+
925
+ this._ranges.push(r)
926
+
927
+ // Trigger this to see if this is already resolved...
928
+ // Also auto compresses the range based on local bitfield
929
+ this._updateNonPrimary()
930
+
931
+ return ref
694
932
  }
695
933
 
696
- static createRange (start, end, filter, linear) {
697
- // createRange(start, end)
698
- if (filter === undefined) {
699
- filter = tautology
934
+ cancel (ref) {
935
+ ref.context.detach(ref)
936
+ }
700
937
 
701
- // createRange(start, end, linear)
702
- } else if (typeof filter === 'boolean') {
703
- linear = filter
704
- filter = tautology
938
+ clearRequests (session) {
939
+ while (session.length > 0) {
940
+ const ref = session[session.length - 1]
941
+ ref.context.detach(ref)
705
942
  }
706
943
 
707
- return new Range(start, end, filter, linear)
944
+ this.updateAll()
945
+ }
946
+
947
+ _addUpgradeMaybe () {
948
+ return this.eagerUpgrade === true ? this._addUpgrade() : this._upgrade
708
949
  }
709
950
 
710
- addRange (range) {
711
- if (!range.done) {
712
- range._ranges = this.requests.ranges
713
- this.requests.ranges.push(range)
714
- if (range.update(this.core.bitfield)) range.resolve(true)
951
+ // TODO: this function is OVER called atm, at each updatePeer/updateAll
952
+ // instead its more efficient to only call it when the conditions in here change - ie on sync/add/remove peer
953
+ // Do this when we have more tests.
954
+ _checkUpgradeIfAvailable () {
955
+ if (this._ifAvailable > 0 || this._upgrade === null || this._upgrade.refs.length === 0) return
956
+
957
+ // check if a peer can upgrade us
958
+
959
+ for (let i = 0; i < this.peers.length; i++) {
960
+ const peer = this.peers[i]
961
+
962
+ if (peer.remoteSynced === false) return
963
+
964
+ if (this.core.tree.length === 0 && peer.remoteLength > 0) return
965
+
966
+ if (peer.remoteLength <= this._upgrade.length || peer.remoteFork !== this._upgrade.fork) continue
967
+
968
+ if (peer.syncsProcessing > 0) return
969
+
970
+ if (peer.lengthAcked !== this.core.tree.length && peer.remoteFork === this.core.tree.fork) return
971
+ if (peer.remoteCanUpgrade === true) return
715
972
  }
716
- this.updateAll()
973
+
974
+ // check if reorgs in progress...
975
+
976
+ if (this._applyingReorg === true) return
977
+
978
+ // TODO: we prob should NOT wait for inflight reorgs here, seems better to just resolve the upgrade
979
+ // and then apply the reorg on the next call in case it's slow - needs some testing in practice
980
+
981
+ for (let i = 0; i < this._reorgs.length; i++) {
982
+ const r = this._reorgs[i]
983
+ if (r.inflight.length > 0) return
984
+ }
985
+
986
+ // nothing to do, indicate no update avail
987
+
988
+ const u = this._upgrade
989
+ this._upgrade = null
990
+ u.resolve(false)
717
991
  }
718
992
 
719
- async _resolveBlock (index) {
720
- const value = await this.core.blocks.get(index)
721
- this.requests.resolveBlock(index, value)
993
+ _addUpgrade () {
994
+ if (this._upgrade !== null) return this._upgrade
995
+
996
+ // TODO: needs a reorg: true/false flag to indicate if the user requested a reorg
997
+ this._upgrade = new UpgradeRequest(this, this.core.tree.fork, this.core.tree.length)
998
+
999
+ return this._upgrade
722
1000
  }
723
1001
 
724
- updateAll () {
725
- const peers = new RandomIterator(this.peers)
726
- for (const peer of peers) {
727
- if (paused(peer, this.core.tree.fork)) continue
728
- if (this.requests.update(peer)) peers.requeue()
1002
+ _addReorg (fork, peer) {
1003
+ if (this.allowFork === false) return null
1004
+
1005
+ // TODO: eager gc old reorgs from the same peer
1006
+ // not super important because they'll get gc'ed when the request finishes
1007
+ // but just spam the remote can do ...
1008
+
1009
+ for (const f of this._reorgs) {
1010
+ if (f.fork > fork && f.batch !== null) return null
1011
+ if (f.fork === fork) return f
1012
+ }
1013
+
1014
+ const f = {
1015
+ fork,
1016
+ inflight: [],
1017
+ batch: null
1018
+ }
1019
+
1020
+ this._reorgs.push(f)
1021
+
1022
+ // maintain sorted by fork
1023
+ let i = this._reorgs.length - 1
1024
+ while (i > 0 && this._reorgs[i - 1].fork > fork) {
1025
+ this._reorgs[i] = this._reorgs[i - 1]
1026
+ this._reorgs[--i] = f
729
1027
  }
730
- // TODO: this is a silly way of doing it
731
- if (this.pendingPeers.size === 0) this.requests.checkTimeouts(this.peers)
1028
+
1029
+ return f
1030
+ }
1031
+
1032
+ _shouldUpgrade (peer) {
1033
+ if (this._upgrade !== null && this._upgrade.inflight.length > 0) return false
1034
+ return peer.remoteCanUpgrade === true &&
1035
+ peer.remoteLength > this.core.tree.length &&
1036
+ peer.lengthAcked === this.core.tree.length
1037
+ }
1038
+
1039
+ _autoUpgrade (peer) {
1040
+ return this._upgrade !== null && this._shouldUpgrade(peer)
1041
+ }
1042
+
1043
+ _addPeer (peer) {
1044
+ this.peers.push(peer)
1045
+ this.updatePeer(peer)
1046
+ this.onpeerupdate(true, peer)
732
1047
  }
733
1048
 
734
- onunregister (peer, err) {
735
- const idx = this.peers.indexOf(peer)
736
- if (idx === -1) return
1049
+ _removePeer (peer) {
1050
+ this.peers.splice(this.peers.indexOf(peer), 1)
737
1051
 
738
- this.pendingPeers.delete(peer)
739
- this.peers.splice(idx, 1)
740
- this.requests.clear(peer)
741
- this.onupdate(false, peer)
1052
+ for (const req of this._inflight) {
1053
+ if (req.peer !== peer) continue
1054
+ this._inflight.remove(req.id)
1055
+ this._clearRequest(peer, req)
1056
+ }
742
1057
 
1058
+ this.onpeerupdate(false, peer)
743
1059
  this.updateAll()
744
1060
  }
745
1061
 
746
- oncore (_, peer) {
747
- this.pendingPeers.delete(peer)
748
- this.peers.push(peer)
749
- this.onupdate(true, peer)
1062
+ _queueBlock (b) {
1063
+ if (b.queued === true) return
1064
+ b.queued = true
1065
+ this._queued.push(b)
1066
+ }
750
1067
 
751
- peer.info({
752
- length: this.core.tree.length,
753
- fork: this.core.tree.fork,
754
- uploading: peer.uploading,
755
- downloading: peer.downloading
756
- })
1068
+ _resolveBlockRequest (tracker, fork, index, value, req) {
1069
+ const b = tracker.remove(fork, index)
1070
+ if (b === null) return false
757
1071
 
758
- // YOLO send over all the pages for now
759
- const p = pages(this.core)
760
- for (let index = 0; index < p.length; index++) {
761
- peer.bitfield({ start: index, bitfield: p[index] })
1072
+ removeInflight(b.inflight, req)
1073
+ b.queued = false
1074
+
1075
+ b.resolve(value)
1076
+
1077
+ return true
1078
+ }
1079
+
1080
+ _resolveUpgradeRequest (req) {
1081
+ if (req !== null) removeInflight(this._upgrade.inflight, req)
1082
+
1083
+ if (this.core.tree.length === this._upgrade.length && this.core.tree.fork === this._upgrade.fork) return false
1084
+
1085
+ const u = this._upgrade
1086
+ this._upgrade = null
1087
+ u.resolve(true)
1088
+
1089
+ return true
1090
+ }
1091
+
1092
+ _clearInflightBlock (tracker, req) {
1093
+ const b = tracker.get(req.fork, req.block.index)
1094
+
1095
+ if (b === null || removeInflight(b.inflight, req) === false) return
1096
+
1097
+ if (b.refs.length > 0 && tracker === this._blocks) {
1098
+ this._queueBlock(b)
1099
+ return
1100
+ }
1101
+
1102
+ b.gc()
1103
+ }
1104
+
1105
+ _clearInflightUpgrade (req) {
1106
+ if (removeInflight(this._upgrade.inflight, req) === false) return
1107
+ this._upgrade.gc()
1108
+ }
1109
+
1110
+ _clearInflightSeeks (req) {
1111
+ for (const s of this._seeks) {
1112
+ if (removeInflight(s.inflight, req) === false) continue
1113
+ s.gc()
762
1114
  }
763
1115
  }
764
1116
 
765
- onunknowncore (_, peer) {
766
- this.pendingPeers.delete(peer)
767
- this.updateAll()
768
- // This is a no-op because there isn't any state to dealloc currently
1117
+ _clearInflightReorgs (req) {
1118
+ for (const r of this._reorgs) {
1119
+ removeInflight(r.inflight, req)
1120
+ }
1121
+ }
1122
+
1123
+ _clearOldReorgs (fork) {
1124
+ for (let i = 0; i < this._reorgs.length; i++) {
1125
+ const f = this._reorgs[i]
1126
+ if (f.fork >= fork) continue
1127
+ if (i === this._reorgs.length - 1) this._reorgs.pop()
1128
+ else this._reorgs[i] = this._reorgs.pop()
1129
+ i--
1130
+ }
769
1131
  }
770
1132
 
771
- oninfo ({ length, fork, uploading, downloading }, peer) {
772
- const len = peer.state.length
773
- const forked = peer.state.fork !== fork
1133
+ // "slow" updates here - async but not allowed to ever throw
1134
+ async _updateNonPrimary () {
1135
+ // Check if running, if so skip it and the running one will issue another update for us (debounce)
1136
+ while (++this._updatesPending === 1) {
1137
+ for (let i = 0; i < this._ranges.length; i++) {
1138
+ const r = this._ranges[i]
1139
+
1140
+ while (r.start < r.end && this.core.bitfield.get(mapIndex(r.blocks, r.start)) === true) r.start++
1141
+ while (r.start < r.end && this.core.bitfield.get(mapIndex(r.blocks, r.end - 1)) === true) r.end--
774
1142
 
775
- peer.state.length = length
776
- peer.state.fork = fork
777
- peer.state.downloading = downloading
778
- peer.state.uploading = uploading
1143
+ if (r.end === -1 || r.start < r.end) continue
779
1144
 
780
- if (forked) {
781
- for (let i = peer.state.length; i < len; i++) {
782
- peer.state.bitfield.set(i, false)
1145
+ if (i < this._ranges.length - 1) this._ranges[i] = this._ranges.pop()
1146
+ else this._ranges.pop()
1147
+
1148
+ i--
1149
+
1150
+ r.resolve(true)
783
1151
  }
784
1152
 
785
- if (fork > this.core.tree.fork && length > 0) {
786
- this.requests.clear(peer) // clear all pending requests from this peer as they forked, this can be removed if we add remote cancel
787
- peer.request({ fork, upgrade: { start: 0, length } })
1153
+ for (let i = 0; i < this._seeks.length; i++) {
1154
+ const s = this._seeks[i]
1155
+
1156
+ let err = null
1157
+ let res = null
1158
+
1159
+ try {
1160
+ res = await s.seeker.update()
1161
+ } catch (error) {
1162
+ err = error
1163
+ }
1164
+
1165
+ if (!res && !err) continue
1166
+
1167
+ if (i < this._seeks.length - 1) this._seeks[i] = this._seeks.pop()
1168
+ else this._seeks.pop()
1169
+
1170
+ i--
1171
+
1172
+ if (err) s.reject(err)
1173
+ else s.resolve(res)
788
1174
  }
1175
+
1176
+ this.updateAll()
1177
+
1178
+ // No additional updates scheduled - return
1179
+ if (--this._updatesPending === 0) return
1180
+ // Debounce the additional updates - continue
1181
+ this._updatesPending = 0
1182
+ }
1183
+ }
1184
+
1185
+ _clearRequest (peer, req) {
1186
+ if (req.block !== null) {
1187
+ this._clearInflightBlock(this._blocks, req)
1188
+ }
1189
+
1190
+ if (req.hash !== null) {
1191
+ this._clearInflightBlock(this._hashes, req)
1192
+ }
1193
+
1194
+ if (req.upgrade !== null && this._upgrade !== null) {
1195
+ this._clearInflightUpgrade(req)
1196
+ }
1197
+
1198
+ if (this._seeks.length > 0) {
1199
+ this._clearInflightSeeks(req)
789
1200
  }
790
1201
 
791
- if (!peer.state.receivedInfo) {
792
- peer.state.receivedInfo = true
1202
+ if (this._reorgs.length > 0) {
1203
+ this._clearInflightReorgs(req)
793
1204
  }
1205
+ }
794
1206
 
795
- // TODO: do we need to update ALL peers here? prob not
1207
+ _onnodata (peer, req) {
1208
+ this._clearRequest(peer, req)
796
1209
  this.updateAll()
797
1210
  }
798
1211
 
799
- onbitfield ({ start, bitfield }, peer) {
800
- if (bitfield.length < 1024) {
801
- const buf = b4a.from(bitfield.buffer, bitfield.byteOffset, bitfield.byteLength)
802
- const bigger = b4a.concat([buf, b4a.alloc(4096 - buf.length)])
803
- bitfield = new Uint32Array(bigger.buffer, bigger.byteOffset, 1024)
1212
+ _ondata (peer, req, data) {
1213
+ if (data.block !== null) {
1214
+ this._resolveBlockRequest(this._blocks, data.fork, data.block.index, data.block.value, req)
804
1215
  }
805
- peer.state.bitfield.pages.set(start, bitfield)
806
1216
 
807
- // TODO: do we need to update ALL peers here? prob not
808
- this.updateAll()
1217
+ if (data.hash !== null && (data.hash.index & 1) === 0) {
1218
+ this._resolveBlockRequest(this._hashes, data.fork, data.hash.index / 2, null, req)
1219
+ }
1220
+
1221
+ if (this._upgrade !== null) {
1222
+ this._resolveUpgradeRequest(req)
1223
+ }
1224
+
1225
+ if (this._seeks.length > 0) {
1226
+ this._clearInflightSeeks(req)
1227
+ }
1228
+
1229
+ if (this._reorgs.length > 0) {
1230
+ this._clearInflightReorgs(req)
1231
+ }
1232
+
1233
+ if (this._seeks.length > 0 || this._ranges.length > 0) this._updateNonPrimary()
1234
+ else this.updatePeer(peer)
809
1235
  }
810
1236
 
811
- onhave ({ start, length }, peer) {
812
- const end = start + length
813
- for (; start < end; start++) {
814
- peer.state.bitfield.set(start, true)
1237
+ async _onreorgdata (peer, req, data) {
1238
+ const f = this._addReorg(data.fork, peer)
1239
+
1240
+ if (f === null) {
1241
+ this.updateAll()
1242
+ return
1243
+ }
1244
+
1245
+ removeInflight(f.inflight, req)
1246
+
1247
+ if (f.batch) {
1248
+ await f.batch.update(data)
1249
+ } else {
1250
+ f.batch = await this.core.tree.reorg(data)
1251
+
1252
+ // Remove "older" reorgs in progress as we just verified this one.
1253
+ this._clearOldReorgs(f.fork)
1254
+ }
1255
+
1256
+ if (f.batch.finished) {
1257
+ if (this._addUpgradeMaybe() !== null) {
1258
+ await this._applyReorg(f)
1259
+ }
815
1260
  }
816
1261
 
817
- // TODO: do we need to update ALL peers here? prob not
818
1262
  this.updateAll()
819
1263
  }
820
1264
 
821
- async ondata (proof, peer) {
1265
+ async _applyReorg (f) {
1266
+ // TODO: more optimal here to check if potentially a better reorg
1267
+ // is available, ie higher fork, and request that one first.
1268
+ // This will request that one after this finishes, which is fine, but we
1269
+ // should investigate the complexity in going the other way
1270
+
1271
+ const u = this._upgrade
1272
+
1273
+ this._applyingReorg = true
1274
+ this._reorgs = [] // clear all as the nodes are against the old tree - easier
1275
+
822
1276
  try {
823
- await this.requests.ondata(proof, peer)
1277
+ await this.core.reorg(f.batch, null) // TODO: null should be the first/last peer?
824
1278
  } catch (err) {
825
- // TODO: the request pool should have this cap, so we can just bubble it up
826
- this.updateAll()
827
- throw err
1279
+ this._upgrade = null
1280
+ u.reject(err)
1281
+ }
1282
+
1283
+ this._applyingReorg = false
1284
+
1285
+ if (this._upgrade !== null) {
1286
+ this._resolveUpgradeRequest(null)
1287
+ }
1288
+
1289
+ for (const peer of this.peers) this._updateFork(peer)
1290
+
1291
+ // TODO: all the remaining is a tmp workaround until we have a flag/way for ANY_FORK
1292
+ for (const r of this._ranges) {
1293
+ r.fork = this.core.tree.fork
1294
+ r.start = r.userStart
1295
+ r.end = r.userEnd
1296
+ }
1297
+ for (const s of this._seeks) s.fork = this.core.tree.fork
1298
+ for (const b of this._blocks) b.fork = this.core.tree.fork
1299
+ this._blocks.update(this.core.tree.fork)
1300
+ this.updateAll()
1301
+ }
1302
+
1303
+ _maybeUpdate () {
1304
+ return this._upgrade !== null && this._upgrade.inflight.length === 0
1305
+ }
1306
+
1307
+ _updateFork (peer) {
1308
+ if (this._applyingReorg === true || this.allowFork === false || peer.remoteFork <= this.core.tree.fork) {
1309
+ return false
828
1310
  }
1311
+
1312
+ const f = this._addReorg(peer.remoteFork, peer)
1313
+
1314
+ // TODO: one per peer is better
1315
+ if (f !== null && f.batch === null && f.inflight.length === 0) {
1316
+ return peer._requestForkProof(f)
1317
+ }
1318
+
1319
+ return false
829
1320
  }
830
1321
 
831
- async onrequest (req, peer) {
832
- const fork = req.fork || peer.state.fork
833
- if (fork !== this.core.tree.fork) return
834
- if (!peer.uploading) return
1322
+ _updatePeer (peer) {
1323
+ if (peer.inflight >= peer.maxInflight) {
1324
+ return false
1325
+ }
1326
+
1327
+ for (const s of this._seeks) {
1328
+ if (s.inflight.length > 0) continue // TODO: one per peer is better
1329
+ if (peer._requestSeek(s) === true) {
1330
+ return true
1331
+ }
1332
+ }
835
1333
 
836
- const proof = await this.core.tree.proof(req)
1334
+ // Implied that any block in the queue should be requested, no matter how many inflights
1335
+ const blks = new RandomIterator(this._queued)
837
1336
 
838
- if (req.block && req.block.value) {
839
- proof.block.value = await this.core.blocks.get(req.block.index)
840
- this.onupload(req.block.index, proof.block.value, peer)
1337
+ for (const b of blks) {
1338
+ if (b.queued === false || peer._requestBlock(b) === true) {
1339
+ b.queued = false
1340
+ blks.dequeue()
1341
+ return true
1342
+ }
841
1343
  }
842
1344
 
843
- peer.data(proof)
1345
+ return false
844
1346
  }
845
- }
846
1347
 
847
- function paused (peer, fork) {
848
- return peer.state.fork !== fork
1348
+ _updatePeerNonPrimary (peer) {
1349
+ const ranges = new RandomIterator(this._ranges)
1350
+
1351
+ for (const r of ranges) {
1352
+ if (peer._requestRange(r) === true) {
1353
+ return true
1354
+ }
1355
+ }
1356
+
1357
+ // Iterate from newest fork to oldest fork...
1358
+ for (let i = this._reorgs.length - 1; i >= 0; i--) {
1359
+ const f = this._reorgs[i]
1360
+ if (f.batch !== null && f.inflight.length === 0 && peer._requestForkRange(f) === true) {
1361
+ return true
1362
+ }
1363
+ }
1364
+
1365
+ if (this._maybeUpdate() === true && peer._requestUpgrade(this._upgrade) === true) {
1366
+ return true
1367
+ }
1368
+
1369
+ return false
1370
+ }
1371
+
1372
+ updatePeer (peer) {
1373
+ // Quick shortcut to wait for flushing reorgs - not needed but less waisted requests
1374
+ if (this._applyingReorg === true) return
1375
+
1376
+ while (this._updatePeer(peer) === true);
1377
+ while (this._updatePeerNonPrimary(peer) === true);
1378
+
1379
+ this._checkUpgradeIfAvailable()
1380
+ }
1381
+
1382
+ updateAll () {
1383
+ // Quick shortcut to wait for flushing reorgs - not needed but less waisted requests
1384
+ if (this._applyingReorg === true) return
1385
+
1386
+ const peers = new RandomIterator(this.peers)
1387
+
1388
+ for (const peer of peers) {
1389
+ if (this._updatePeer(peer) === true) {
1390
+ peers.requeue()
1391
+ }
1392
+ }
1393
+
1394
+ // Check if we can skip the non primary check fully
1395
+ if (this._maybeUpdate() === false && this._ranges.length === 0 && this._reorgs.length === 0) {
1396
+ this._checkUpgradeIfAvailable()
1397
+ return
1398
+ }
1399
+
1400
+ for (const peer of peers.restart()) {
1401
+ if (this._updatePeerNonPrimary(peer) === true) {
1402
+ peers.requeue()
1403
+ }
1404
+ }
1405
+
1406
+ this._checkUpgradeIfAvailable()
1407
+ }
1408
+
1409
+ attachTo (protomux) {
1410
+ const makePeer = this._makePeer.bind(this, protomux)
1411
+
1412
+ protomux.pair({ protocol: 'hypercore/alpha', id: this.discoveryKey }, makePeer)
1413
+
1414
+ this._ifAvailable++
1415
+ protomux.stream.opened.then((opened) => {
1416
+ this._ifAvailable--
1417
+ if (opened) makePeer()
1418
+ this._checkUpgradeIfAvailable()
1419
+ })
1420
+ }
1421
+
1422
+ _makePeer (protomux) {
1423
+ if (protomux.opened({ protocol: 'hypercore/alpha', id: this.discoveryKey })) return false
1424
+
1425
+ const channel = protomux.createChannel({
1426
+ userData: null,
1427
+ protocol: 'hypercore/alpha',
1428
+ id: this.discoveryKey,
1429
+ handshake: m.wire.handshake,
1430
+ messages: [
1431
+ { encoding: m.wire.sync, onmessage: onwiresync },
1432
+ { encoding: m.wire.request, onmessage: onwirerequest },
1433
+ null, // oncancel
1434
+ { encoding: m.wire.data, onmessage: onwiredata },
1435
+ { encoding: m.wire.noData, onmessage: onwirenodata },
1436
+ { encoding: m.wire.want, onmessage: onwirewant },
1437
+ { encoding: m.wire.unwant, onmessage: onwireunwant },
1438
+ { encoding: m.wire.bitfield, onmessage: onwirebitfield },
1439
+ { encoding: m.wire.range, onmessage: onwirerange },
1440
+ { encoding: m.wire.extension, onmessage: onwireextension }
1441
+ ],
1442
+ onopen: onwireopen,
1443
+ onclose: onwireclose
1444
+ })
1445
+
1446
+ if (channel === null) return false
1447
+
1448
+ const peer = new Peer(this, protomux, channel)
1449
+ const stream = protomux.stream
1450
+
1451
+ peer.channel.open({
1452
+ capability: caps.replicate(stream.isInitiator, this.key, stream.handshakeHash)
1453
+ })
1454
+
1455
+ return true
1456
+ }
849
1457
  }
850
1458
 
851
1459
  function pages (core) {
@@ -859,19 +1467,72 @@ function pages (core) {
859
1467
  return res
860
1468
  }
861
1469
 
862
- function noop () {}
1470
+ function matchingRequest (req, data) {
1471
+ if (data.block !== null && (req.block === null || req.block.index !== data.block.index)) return false
1472
+ if (data.hash !== null && (req.hash === null || req.hash.index !== data.hash.index)) return false
1473
+ if (data.seek !== null && (req.seek === null || req.seek.bytes !== data.seek.bytes)) return false
1474
+ if (data.upgrade !== null && req.upgrade === null) return false
1475
+ return req.fork === data.fork
1476
+ }
863
1477
 
864
- function tautology () {
1478
+ function removeInflight (inf, req) {
1479
+ const i = inf.indexOf(req)
1480
+ if (i === -1) return false
1481
+ if (i < inf.length - 1) inf[i] = inf.pop()
1482
+ else inf.pop()
865
1483
  return true
866
1484
  }
867
1485
 
868
- function log2 (n) {
869
- let res = 1
1486
+ function mapIndex (blocks, index) {
1487
+ return blocks === null ? index : blocks[index]
1488
+ }
870
1489
 
871
- while (n > 2) {
872
- n /= 2
873
- res++
874
- }
1490
+ function noop () {}
875
1491
 
876
- return res
1492
+ function toLength (start, end) {
1493
+ return end === -1 ? -1 : (end < start ? 0 : end - start)
1494
+ }
1495
+
1496
+ function onwireopen (m, c) {
1497
+ return c.userData.onopen(m)
1498
+ }
1499
+
1500
+ function onwireclose (isRemote, c) {
1501
+ return c.userData.onclose(isRemote)
1502
+ }
1503
+
1504
+ function onwiresync (m, c) {
1505
+ return c.userData.onsync(m)
1506
+ }
1507
+
1508
+ function onwirerequest (m, c) {
1509
+ return c.userData.onrequest(m)
1510
+ }
1511
+
1512
+ function onwiredata (m, c) {
1513
+ return c.userData.ondata(m)
1514
+ }
1515
+
1516
+ function onwirenodata (m, c) {
1517
+ return c.userData.onnodata(m)
1518
+ }
1519
+
1520
+ function onwirewant (m, c) {
1521
+ return c.userData.onwant(m)
1522
+ }
1523
+
1524
+ function onwireunwant (m, c) {
1525
+ return c.userData.onunwant(m)
1526
+ }
1527
+
1528
+ function onwirebitfield (m, c) {
1529
+ return c.userData.onbitfield(m)
1530
+ }
1531
+
1532
+ function onwirerange (m, c) {
1533
+ return c.userData.onrange(m)
1534
+ }
1535
+
1536
+ function onwireextension (m, c) {
1537
+ return c.userData.onextension(m)
877
1538
  }