hypercore 10.0.0-alpha.23 → 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,832 +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
34
+ return r
17
35
  }
18
- }
19
36
 
20
- class InvertedPromise {
21
- constructor (resolve, reject, index) {
22
- this.index = index
23
- this.resolve = resolve
24
- 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
25
45
  }
26
- }
27
46
 
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 = []
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
36
57
  }
37
58
 
38
- createPromise () {
39
- return new Promise((resolve, reject) => {
40
- this.promises.push(new InvertedPromise(resolve, reject, this.promises.length))
41
- })
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
42
69
  }
43
70
 
44
71
  resolve (val) {
45
- for (let i = 0; i < this.promises.length; i++) {
46
- 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)
47
75
  }
48
76
  }
49
77
 
50
78
  reject (err) {
51
- for (let i = 0; i < this.promises.length; i++) {
52
- 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)
53
82
  }
54
83
  }
55
84
  }
56
85
 
57
- class Upgrade {
58
- constructor (minLength) {
59
- this.minLength = minLength
60
- this.promises = []
61
- }
86
+ class BlockRequest extends Attachable {
87
+ constructor (tracker, fork, index) {
88
+ super()
62
89
 
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
- }
90
+ this.fork = fork
91
+ this.index = index
92
+ this.inflight = []
93
+ this.queued = false
94
+ this.tracker = tracker
95
+ }
68
96
 
69
- return false
97
+ _unref () {
98
+ if (this.inflight.length > 0) return
99
+ this.tracker.remove(this.fork, this.index)
70
100
  }
101
+ }
71
102
 
72
- createPromise () {
73
- return new Promise((resolve, reject) => {
74
- this.promises.push(new InvertedPromise(resolve, reject, this.promises.length))
75
- })
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
76
117
  }
77
118
 
78
- resolve (val) {
79
- for (let i = 0; i < this.promises.length; i++) {
80
- this.promises[i].resolve(val)
81
- }
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
82
124
  }
83
125
 
84
- reject (err) {
85
- for (let i = 0; i < this.promises.length; i++) {
86
- this.promises[i].reject(err)
87
- }
126
+ _cancel (r) {
127
+ r.resolve(false)
88
128
  }
89
129
  }
90
130
 
91
- class UpgradeLock {
92
- constructor (peer, length) {
93
- this.peer = peer
131
+ class UpgradeRequest extends Attachable {
132
+ constructor (replicator, fork, length) {
133
+ super()
134
+
135
+ this.fork = fork
94
136
  this.length = length
95
- this.resolve = null
96
- 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)
97
148
  }
98
149
  }
99
150
 
100
- class Seek {
101
- constructor (seeker) {
102
- this.request = null
151
+ class SeekRequest extends Attachable {
152
+ constructor (seeks, fork, seeker) {
153
+ super()
154
+
155
+ this.fork = fork
103
156
  this.seeker = seeker
104
- this.promise = null
105
- this.finished = false
157
+ this.inflight = []
158
+ this.seeks = seeks
106
159
  }
107
160
 
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
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
114
167
  }
168
+ }
115
169
 
116
- createPromise () {
117
- return new Promise((resolve, reject) => {
118
- this.promise = new InvertedPromise(resolve, reject, 0)
119
- })
170
+ class InflightTracker {
171
+ constructor () {
172
+ this._requests = []
173
+ this._free = []
120
174
  }
121
- }
122
175
 
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
176
+ * [Symbol.iterator] () {
177
+ for (const req of this._requests) {
178
+ if (req !== null) yield req
179
+ }
180
+ }
131
181
 
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
182
+ add (req) {
183
+ const id = this._free.length ? this._free.pop() : this._requests.push(null)
184
+
185
+ req.id = id
186
+ this._requests[id - 1] = req
187
+ return req
137
188
  }
138
189
 
139
- contains (req) {
140
- return this._start <= req.index && req.index < this.end
190
+ get (id) {
191
+ return id <= this._requests.length ? this._requests[id - 1] : null
141
192
  }
142
193
 
143
- update (bitfield) {
144
- if (this.end === -1) {
145
- while (bitfield.get(this._start)) this._start++
146
- return false
194
+ remove (id) {
195
+ if (id <= this._requests.length) {
196
+ this._requests[id - 1] = null
197
+ this._free.push(id)
147
198
  }
199
+ }
200
+ }
148
201
 
149
- for (; this._start < this.end; this._start++) {
150
- if (this.filter(this._start) && !bitfield.get(this._start)) return false
151
- }
202
+ class BlockTracker {
203
+ constructor (core) {
204
+ this._core = core
205
+ this._fork = core.tree.fork
152
206
 
153
- return true
207
+ this._indexed = new Map()
208
+ this._additional = []
154
209
  }
155
210
 
156
- resolve (done) {
157
- this._done(null, done)
211
+ * [Symbol.iterator] () {
212
+ yield * this._indexed.values()
213
+ yield * this._additional
158
214
  }
159
215
 
160
- destroy (err) {
161
- this._done(err, false)
216
+ has (fork, index) {
217
+ return this.get(fork, index) !== null
162
218
  }
163
219
 
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)
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
169
226
  }
170
227
 
171
- _done (err, done) {
172
- if (this.done) return
173
- 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)
174
231
 
175
- if (this._ranges) {
176
- const i = this._ranges.indexOf(this)
232
+ let b = this.get(fork, index)
233
+ if (b) return b
177
234
 
178
- if (i === this._ranges.length - 1) this._ranges.pop()
179
- else if (i > -1) this._ranges[i] = this._ranges.pop()
235
+ b = new BlockRequest(this, fork, index)
236
+
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
180
248
  }
181
249
 
182
- this._ranges = null
183
- this._resolved = done
184
- 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
+ }
185
257
 
186
- if (this._inv === null) return
187
- if (err) this._inv.reject(err)
188
- else this._inv.resolve(done)
258
+ return null
189
259
  }
190
260
 
191
- _makePromise () {
192
- this.promise = new Promise((resolve, reject) => {
193
- this._inv = new InvertedPromise(resolve, reject, 0)
194
- })
261
+ update (fork) {
262
+ if (this._fork === fork) return
263
+
264
+ const additional = this._additional
265
+ this._additional = []
195
266
 
196
- return this.promise
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
+ }
278
+
279
+ this._fork = fork
197
280
  }
198
281
  }
199
282
 
200
- class RequestPool {
201
- constructor (replicator, core) {
283
+ class Peer {
284
+ constructor (replicator, protomux, channel) {
285
+ this.core = replicator.core
202
286
  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
- }
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++
236
333
  }
237
334
 
238
- isRequesting (index) {
239
- return this.requests.has(index)
335
+ signalUpgrade () {
336
+ if (this._shouldUpdateCanUpgrade() === true) this._updateCanUpgradeAndSync()
337
+ else this.sendSync()
240
338
  }
241
339
 
242
- update (peer) {
243
- if (peer.state.inflight >= peer.state.maxInflight) return false
244
- if (this.paused) return false
340
+ broadcastRange (start, length, drop) {
341
+ this.wireRange.send({
342
+ drop,
343
+ start,
344
+ length
345
+ })
346
+ }
245
347
 
246
- if (peer.state.fork > this.core.tree.fork) {
247
- // we message them in the info recv
248
- return true
249
- }
348
+ extension (name, message) {
349
+ this.wireExtension.send({ name: name === this.lastExtensionSent ? '' : name, message })
350
+ this.lastExtensionSent = name
351
+ }
250
352
 
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
- }
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
+ }
257
359
 
258
- if (this.pendingUpgrade) {
259
- if (this._updateUpgrade(peer)) return true
360
+ sendSync () {
361
+ if (this.syncsProcessing !== 0) {
362
+ this.needsSync = true
363
+ return
260
364
  }
261
365
 
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
- }
366
+ if (this.core.tree.fork !== this.remoteFork) {
367
+ this.canUpgrade = false
268
368
  }
269
369
 
270
- const ranges = new RandomIterator(this.ranges) // can be cached
271
- for (const range of ranges) {
272
- 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')
273
387
  }
274
388
 
275
- if (this.eagerUpgrades && !this.upgrading) {
276
- 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
+ })
277
403
  }
278
404
 
279
- return false
405
+ this.replicator._ifAvailable--
406
+ this.replicator._addPeer(this)
407
+
408
+ this.protomux.uncork()
280
409
  }
281
410
 
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
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)
288
420
  }
289
421
 
290
- _updatePeer (peer, req) {
291
- const remote = peer.state.bitfield
292
- 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)
293
425
 
294
- 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
295
432
 
296
- this.send(peer, req)
297
- return true
298
- }
433
+ this.lengthAcked = sameFork ? remoteLength : 0
434
+ this.syncsProcessing++
299
435
 
300
- _updateRange (peer, range) {
301
- const end = range.end === -1 ? peer.state.length : range.end
302
- if (end <= range._start) return false
436
+ this.replicator._updateFork(this)
303
437
 
304
- 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
+ }
305
441
 
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
- }
442
+ const upgrade = (lengthChanged === false || sameFork === false)
443
+ ? this.canUpgrade && sameFork
444
+ : await this._canUpgrade(length, fork)
312
445
 
313
- _updateUpgrade (peer) {
314
- const minLength = this.pendingUpgrade
315
- ? this.pendingUpgrade.minLength
316
- : this.core.tree.length + 1
446
+ if (length === this.remoteLength && fork === this.core.tree.fork) {
447
+ this.canUpgrade = upgrade
448
+ }
317
449
 
318
- if (this.upgrading || peer.state.length < minLength) return false
319
- this.upgrading = new UpgradeLock(peer, peer.state.length)
450
+ if (--this.syncsProcessing !== 0) return // ie not latest
320
451
 
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 }
452
+ if (this.needsSync === true || (this.core.tree.fork === this.remoteFork && this.core.tree.length > this.remoteLength)) {
453
+ this.signalUpgrade()
326
454
  }
327
455
 
328
- peer.request(data)
329
- return true
456
+ this._update()
330
457
  }
331
458
 
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
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
337
464
  }
338
465
 
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
466
+ async _updateCanUpgradeAndSync () {
467
+ const len = this.core.tree.length
468
+ const fork = this.core.tree.fork
345
469
 
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
470
+ const canUpgrade = await this._canUpgrade(this.remoteLength, this.remoteFork)
350
471
 
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
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
356
477
  }
357
478
 
358
- return null
479
+ this.canUpgrade = canUpgrade
480
+ this.sendSync()
359
481
  }
360
482
 
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
483
+ // Safe to call in the background - never fails
484
+ async _canUpgrade (remoteLength, remoteFork) {
485
+ if (remoteFork !== this.core.tree.fork) return false
365
486
 
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
487
+ if (remoteLength === 0) return true
488
+ if (remoteLength >= this.core.tree.length) return false
370
489
 
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
- }
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
387
495
 
388
- await this.upgrading.promise
389
- needsUpgrade = peer.state.length > this.core.tree.length || !!(!this.upgrading && this.pendingUpgrade)
496
+ return canUpgrade
497
+ } catch {
498
+ return false
390
499
  }
500
+ }
391
501
 
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
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)
397
508
  }
398
509
 
399
- 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) {
400
518
  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)
519
+ proof = await this._getProof(msg)
520
+ } catch (err) { // TODO: better error handling here, ie custom errors
521
+ safetyCatch(err)
404
522
  }
405
523
  }
406
524
 
407
- if (peer.destroyed) {
408
- req.peer = null
409
- this.pending.push(req)
410
- if (upgrading) {
411
- this.upgrading.resolve()
412
- this.upgrading = null
525
+ if (proof !== null) {
526
+ if (proof.block !== null) {
527
+ this.replicator.onupload(proof.block.index, proof.block.value, this)
413
528
  }
414
- 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
+ })
415
538
  return
416
539
  }
417
540
 
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
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
429
566
  }
430
- 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()
431
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
432
583
 
433
- peer.request(data)
584
+ this.inflight--
585
+ this.replicator._inflight.remove(req.id)
586
+ this.replicator._onnodata(this, req)
434
587
  }
435
588
 
436
- async _onupgrade (proof, peer) {
437
- if (!this.upgrading || !proof.upgrade) return
438
- if (this.unforking) return
589
+ onwant () {
590
+ // TODO
591
+ }
439
592
 
440
- await this.core.verify(proof, peer)
593
+ onunwant () {
594
+ // TODO
595
+ }
441
596
 
442
- // TODO: validate that we actually upgraded our length as well
443
- this.upgrading.resolve()
444
- this.upgrading = null
597
+ onbitfield ({ start, bitfield }) {
598
+ // TODO: tweak this to be more generic
445
599
 
446
- if (this.pendingUpgrade) {
447
- this.pendingUpgrade.resolve(true)
448
- this.pendingUpgrade = null
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)
449
604
  }
450
605
 
451
- if (this.seeks.length) await this._updateSeeks(null)
606
+ this.remoteBitfield.pages.set(start / this.core.bitfield.pageSize, bitfield)
452
607
 
453
- this.update(peer)
608
+ this._update()
454
609
  }
455
610
 
456
- async _onfork (proof, peer) {
457
- // TODO: if proof is from a newer fork than currently unforking, restart
611
+ onrange ({ drop, start, length }) {
612
+ const has = drop === false
458
613
 
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
614
+ for (const end = start + length; start < end; start++) {
615
+ this.remoteBitfield.set(start, has)
482
616
  }
483
617
 
484
- const reorg = this.unforking
485
- this.unforking = null
486
- await this.core.reorg(reorg)
618
+ if (drop === false) this._update()
619
+ }
620
+
621
+ onreorghint () {
622
+ // TODO
623
+ }
624
+
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
+ }
630
+
631
+ _makeRequest (fork, needsUpgrade) {
632
+ if (needsUpgrade === true && this.replicator._shouldUpgrade(this) === false) {
633
+ return null
634
+ }
635
+
636
+ if (needsUpgrade === false && fork === this.core.tree.fork && this.replicator._autoUpgrade(this) === true) {
637
+ needsUpgrade = true
638
+ }
487
639
 
488
- // reset ranges, also need to reset seeks etc
489
- for (const r of this.ranges) {
490
- 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 }
491
650
  }
651
+ }
492
652
 
493
- this.replicator.updateAll()
653
+ _requestUpgrade (u) {
654
+ const req = this._makeRequest(u.fork, true)
655
+ if (req === null) return false
494
656
 
495
- // TODO: we gotta clear out old requests as well here pointing at the old fork
657
+ this._send(req)
658
+
659
+ return true
496
660
  }
497
661
 
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--
662
+ _requestSeek (s) {
663
+ if (s.seeker.start >= this.core.tree.length) {
664
+ const req = this._makeRequest(s.fork, true)
501
665
 
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
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
507
675
  }
508
676
 
509
- // ignore incoming messages during an unfork.
510
- if (this.unforking) return
677
+ const len = s.seeker.end - s.seeker.start
678
+ const off = s.seeker.start + Math.floor(Math.random() * len)
511
679
 
512
- 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
513
683
 
514
- const { index, value } = proof.block
515
- const req = this.requests.get(index)
684
+ if (this.remoteBitfield.get(index) === false) continue
685
+ if (this.core.bitfield.get(index) === true) continue
516
686
 
517
- // no push allowed, TODO: add flag to allow pushes
518
- 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
519
690
 
520
- try {
521
- await this.core.verify(proof, peer)
522
- } catch (err) {
523
- this.requests.delete(index)
524
- throw err
525
- }
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
526
694
 
527
- // TODO: validate that we actually upgraded our length as well
528
- if (proof.upgrade) {
529
- this.upgrading.resolve()
530
- this.upgrading = null
695
+ const req = this._makeRequest(s.fork, false)
531
696
 
532
- if (this.pendingUpgrade) {
533
- this.pendingUpgrade.resolve(true)
534
- this.pendingUpgrade = null
535
- }
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
536
705
  }
537
706
 
538
- await this._resolveRequest(req, index, value)
707
+ return false
708
+ }
539
709
 
540
- 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)
541
713
  }
542
714
 
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
- }
715
+ _requestBlock (b) {
716
+ if (this.remoteBitfield.get(b.index) === false) return false
550
717
 
551
- 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
552
720
 
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
- }
721
+ req.block = { index: b.index, nodes: 0 }
722
+
723
+ b.inflight.push(req)
724
+ this._send(req)
725
+
726
+ return true
561
727
  }
562
728
 
563
- async _updateSeeks (req) {
564
- for (let i = 0; i < this.seeks.length; i++) {
565
- await this.seeks[i].update()
566
- }
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
567
732
 
568
- for (let i = 0; i < this.seeks.length; i++) {
569
- const seek = this.seeks[i]
733
+ const len = end - r.start
734
+ const off = r.start + (r.linear ? 0 : Math.floor(Math.random() * len))
570
735
 
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()
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
577
758
  }
578
- 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
579
766
  }
580
- }
581
767
 
582
- async resolveBlock (index, value) {
583
- const req = this.requests.get(index)
584
- if (req) await this._resolveRequest(req, index, value)
768
+ return false
585
769
  }
586
770
 
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
- }
771
+ _requestForkProof (f) {
772
+ const req = this._makeRequest(f.fork, false)
592
773
 
593
- range (range) {
594
- if (range.ranges === null || this.ranges.index(range) > -1) return
595
- this.ranges.push(range)
596
- range.ranges = range
597
- }
774
+ req.upgrade = { start: 0, length: this.remoteLength }
598
775
 
599
- seek (seeker) {
600
- const s = new Seek(seeker)
601
- this.seeks.push(s)
602
- return s.createPromise()
776
+ f.inflight.push(req)
777
+ this._send(req)
603
778
  }
604
779
 
605
- block (index) {
606
- const e = this.requests.get(index)
780
+ _requestForkRange (f) {
781
+ if (f.fork !== this.remoteFork || f.batch.want === null) return false
607
782
 
608
- if (e) {
609
- if (!e.value) {
610
- e.value = true
611
- if (e.peer) this.send(e.peer, e)
612
- }
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)
613
796
 
614
- 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
615
803
  }
616
804
 
617
- const r = new Request(index, 0, 0)
805
+ return false
806
+ }
807
+
808
+ async _send (req) {
809
+ const fork = this.core.tree.fork
618
810
 
619
- this.requests.set(index, r)
620
- this.pending.push(r)
811
+ this.inflight++
812
+ this.replicator._inflight.add(req)
621
813
 
622
- return r.createPromise()
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
+ }
826
+
827
+ this.wireRequest.send(req)
623
828
  }
624
829
  }
625
830
 
626
831
  module.exports = class Replicator {
627
- constructor (core, { onupdate }) {
832
+ constructor (core, key, { eagerUpgrade = true, allowFork = true, onpeerupdate = noop, onupload = noop } = {}) {
833
+ this.key = key
834
+ this.discoveryKey = core.crypto.discoveryKey(key)
628
835
  this.core = core
836
+ this.eagerUpgrade = eagerUpgrade
837
+ this.allowFork = allowFork
838
+ this.onpeerupdate = onpeerupdate
839
+ this.onupload = onupload
629
840
  this.peers = []
630
- this.requests = new RequestPool(this, core)
631
- this.updating = null
632
- this.pendingPeers = new Set()
633
- this.onupdate = onupdate
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
634
856
  }
635
857
 
636
- static createProtocol (noiseStream, opts) {
637
- return new Protocol(noiseStream, {
638
- ...opts,
639
- protocolVersion: 0,
640
- userAgent: USER_AGENT
641
- })
858
+ cork () {
859
+ for (const peer of this.peers) peer.protomux.cork()
642
860
  }
643
861
 
644
- joinProtocol (protocol, key, discoveryKey) {
645
- if (protocol.isRegistered(discoveryKey)) return
646
- const peer = protocol.registerPeer(key, discoveryKey, this, new RemoteState(this.core))
647
- this.pendingPeers.add(peer)
862
+ uncork () {
863
+ for (const peer of this.peers) peer.protomux.uncork()
648
864
  }
649
865
 
650
- broadcastBlock (start) {
651
- const msg = { start, length: 1 }
652
- for (const peer of this.peers) peer.have(msg)
866
+ signalUpgrade () {
867
+ for (const peer of this.peers) peer.signalUpgrade()
868
+ }
653
869
 
654
- // in case of writable peer waiting for the block itself
655
- // we trigger the resolver to see if can't resolve it now
656
- if (this.requests.isRequesting(start)) {
657
- this._resolveBlock(start).then(noop, noop)
658
- }
870
+ broadcastRange (start, length, drop = false) {
871
+ for (const peer of this.peers) peer.broadcastRange(start, length, drop)
659
872
  }
660
873
 
661
- broadcastInfo () {
662
- const msg = { length: this.core.tree.length, fork: this.core.tree.fork }
663
- for (const peer of this.peers) peer.info(msg)
874
+ addUpgrade (session) {
875
+ if (this._upgrade !== null) {
876
+ const ref = this._upgrade.attach(session)
877
+ this._checkUpgradeIfAvailable()
878
+ return ref
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
+
664
891
  this.updateAll()
892
+
893
+ return ref
665
894
  }
666
895
 
667
- requestUpgrade () {
668
- 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)
669
901
  this.updateAll()
670
- return promise
902
+
903
+ return ref
671
904
  }
672
905
 
673
- requestSeek (seeker) {
674
- if (typeof seeker === 'number') seeker = this.core.tree.seek(seeker)
675
- 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)
676
911
  this.updateAll()
677
- return promise
912
+
913
+ return ref
678
914
  }
679
915
 
680
- requestBlock (index) {
681
- const promise = this.requests.block(index)
682
- this.updateAll()
683
- 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
684
932
  }
685
933
 
686
- static createRange (start, end, filter, linear) {
687
- // createRange(start, end)
688
- if (filter === undefined) {
689
- filter = tautology
934
+ cancel (ref) {
935
+ ref.context.detach(ref)
936
+ }
690
937
 
691
- // createRange(start, end, linear)
692
- } else if (typeof filter === 'boolean') {
693
- linear = filter
694
- filter = tautology
938
+ clearRequests (session) {
939
+ while (session.length > 0) {
940
+ const ref = session[session.length - 1]
941
+ ref.context.detach(ref)
695
942
  }
696
943
 
697
- return new Range(start, end, filter, linear)
944
+ this.updateAll()
945
+ }
946
+
947
+ _addUpgradeMaybe () {
948
+ return this.eagerUpgrade === true ? this._addUpgrade() : this._upgrade
698
949
  }
699
950
 
700
- addRange (range) {
701
- if (!range.done) {
702
- range._ranges = this.requests.ranges
703
- this.requests.ranges.push(range)
704
- 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
705
972
  }
706
- 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)
707
991
  }
708
992
 
709
- async _resolveBlock (index) {
710
- const value = await this.core.blocks.get(index)
711
- 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
712
1000
  }
713
1001
 
714
- updateAll () {
715
- const peers = new RandomIterator(this.peers)
716
- for (const peer of peers) {
717
- if (paused(peer, this.core.tree.fork)) continue
718
- 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
719
1027
  }
720
- // TODO: this is a silly way of doing it
721
- 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)
722
1047
  }
723
1048
 
724
- onunregister (peer, err) {
725
- const idx = this.peers.indexOf(peer)
726
- if (idx === -1) return
1049
+ _removePeer (peer) {
1050
+ this.peers.splice(this.peers.indexOf(peer), 1)
727
1051
 
728
- this.pendingPeers.delete(peer)
729
- this.peers.splice(idx, 1)
730
- this.requests.clear(peer)
731
- 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
+ }
732
1057
 
1058
+ this.onpeerupdate(false, peer)
733
1059
  this.updateAll()
734
1060
  }
735
1061
 
736
- oncore (_, peer) {
737
- this.pendingPeers.delete(peer)
738
- this.peers.push(peer)
739
- this.onupdate(true, peer)
1062
+ _queueBlock (b) {
1063
+ if (b.queued === true) return
1064
+ b.queued = true
1065
+ this._queued.push(b)
1066
+ }
740
1067
 
741
- peer.info({ length: this.core.tree.length, fork: this.core.tree.fork })
1068
+ _resolveBlockRequest (tracker, fork, index, value, req) {
1069
+ const b = tracker.remove(fork, index)
1070
+ if (b === null) return false
742
1071
 
743
- // YOLO send over all the pages for now
744
- const p = pages(this.core)
745
- for (let index = 0; index < p.length; index++) {
746
- 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
747
1100
  }
1101
+
1102
+ b.gc()
748
1103
  }
749
1104
 
750
- onunknowncore (_, peer) {
751
- this.pendingPeers.delete(peer)
752
- this.updateAll()
753
- // This is a no-op because there isn't any state to dealloc currently
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()
1114
+ }
1115
+ }
1116
+
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
+ }
754
1131
  }
755
1132
 
756
- oninfo ({ length, fork }, peer) {
757
- const len = peer.state.length
758
- 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]
759
1139
 
760
- peer.state.length = length
761
- peer.state.fork = fork
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--
762
1142
 
763
- if (forked) {
764
- for (let i = peer.state.length; i < len; i++) {
765
- peer.state.bitfield.set(i, false)
1143
+ if (r.end === -1 || r.start < r.end) continue
1144
+
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)
766
1151
  }
767
1152
 
768
- if (fork > this.core.tree.fork && length > 0) {
769
- this.requests.clear(peer) // clear all pending requests from this peer as they forked, this can be removed if we add remote cancel
770
- 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)
771
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)
772
1188
  }
773
1189
 
774
- if (!peer.state.receivedInfo) {
775
- peer.state.receivedInfo = true
1190
+ if (req.hash !== null) {
1191
+ this._clearInflightBlock(this._hashes, req)
776
1192
  }
777
1193
 
778
- // TODO: do we need to update ALL peers here? prob not
1194
+ if (req.upgrade !== null && this._upgrade !== null) {
1195
+ this._clearInflightUpgrade(req)
1196
+ }
1197
+
1198
+ if (this._seeks.length > 0) {
1199
+ this._clearInflightSeeks(req)
1200
+ }
1201
+
1202
+ if (this._reorgs.length > 0) {
1203
+ this._clearInflightReorgs(req)
1204
+ }
1205
+ }
1206
+
1207
+ _onnodata (peer, req) {
1208
+ this._clearRequest(peer, req)
779
1209
  this.updateAll()
780
1210
  }
781
1211
 
782
- onbitfield ({ start, bitfield }, peer) {
783
- if (bitfield.length < 1024) {
784
- const buf = b4a.from(bitfield.buffer, bitfield.byteOffset, bitfield.byteLength)
785
- const bigger = b4a.concat([buf, b4a.alloc(4096 - buf.length)])
786
- 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)
787
1215
  }
788
- peer.state.bitfield.pages.set(start, bitfield)
789
1216
 
790
- // TODO: do we need to update ALL peers here? prob not
791
- 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)
792
1235
  }
793
1236
 
794
- onhave ({ start, length }, peer) {
795
- const end = start + length
796
- for (; start < end; start++) {
797
- 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
+ }
798
1260
  }
799
1261
 
800
- // TODO: do we need to update ALL peers here? prob not
801
1262
  this.updateAll()
802
1263
  }
803
1264
 
804
- 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
+
805
1276
  try {
806
- await this.requests.ondata(proof, peer)
1277
+ await this.core.reorg(f.batch, null) // TODO: null should be the first/last peer?
807
1278
  } catch (err) {
808
- // TODO: the request pool should have this cap, so we can just bubble it up
809
- this.updateAll()
810
- 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)
811
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()
812
1301
  }
813
1302
 
814
- async onrequest (req, peer) {
815
- const fork = req.fork || peer.state.fork
816
- if (fork !== this.core.tree.fork) return
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
1310
+ }
817
1311
 
818
- const proof = await this.core.tree.proof(req)
1312
+ const f = this._addReorg(peer.remoteFork, peer)
819
1313
 
820
- if (req.block && req.block.value) {
821
- proof.block.value = await this.core.blocks.get(req.block.index)
1314
+ // TODO: one per peer is better
1315
+ if (f !== null && f.batch === null && f.inflight.length === 0) {
1316
+ return peer._requestForkProof(f)
822
1317
  }
823
1318
 
824
- peer.data(proof)
1319
+ return false
825
1320
  }
826
- }
827
1321
 
828
- function paused (peer, fork) {
829
- return peer.state.fork !== fork
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
+ }
1333
+
1334
+ // Implied that any block in the queue should be requested, no matter how many inflights
1335
+ const blks = new RandomIterator(this._queued)
1336
+
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
+ }
1343
+ }
1344
+
1345
+ return false
1346
+ }
1347
+
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
+ }
830
1457
  }
831
1458
 
832
1459
  function pages (core) {
@@ -840,19 +1467,72 @@ function pages (core) {
840
1467
  return res
841
1468
  }
842
1469
 
843
- 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
+ }
844
1477
 
845
- 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()
846
1483
  return true
847
1484
  }
848
1485
 
849
- function log2 (n) {
850
- let res = 1
1486
+ function mapIndex (blocks, index) {
1487
+ return blocks === null ? index : blocks[index]
1488
+ }
851
1489
 
852
- while (n > 2) {
853
- n /= 2
854
- res++
855
- }
1490
+ function noop () {}
856
1491
 
857
- 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)
858
1538
  }