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