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