hypercore 10.0.0-alpha.4 → 10.0.0-alpha.40

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