hypercore 10.0.0-alpha.25 → 10.0.0-alpha.28

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