hypercore 10.0.0-alpha.5 → 10.0.0-alpha.50

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