hypercore 10.0.0-alpha.8 → 10.1.0

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