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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.0.0-alpha.25",
3
+ "version": "10.0.0-alpha.28",
4
4
  "description": "Hypercore 10",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -32,7 +32,7 @@
32
32
  "lib/**.js"
33
33
  ],
34
34
  "dependencies": {
35
- "@hyperswarm/secret-stream": "^5.0.0",
35
+ "@hyperswarm/secret-stream": "^5.2.0",
36
36
  "b4a": "^1.1.0",
37
37
  "big-sparse-array": "^1.0.2",
38
38
  "codecs": "^3.0.0",
@@ -40,12 +40,14 @@
40
40
  "crc32-universal": "^1.0.1",
41
41
  "events": "^3.3.0",
42
42
  "flat-tree": "^1.9.0",
43
- "hypercore-crypto": "^3.1.0",
43
+ "hypercore-crypto": "^3.2.1",
44
44
  "is-options": "^1.0.1",
45
+ "protomux": "^3.2.0",
45
46
  "random-access-file": "^2.1.4",
46
47
  "random-array-iterator": "^1.0.0",
47
48
  "safety-catch": "^1.0.1",
48
49
  "sodium-universal": "^3.0.4",
50
+ "streamx": "^2.12.4",
49
51
  "xache": "^1.0.0"
50
52
  },
51
53
  "devDependencies": {
package/lib/extensions.js DELETED
@@ -1,76 +0,0 @@
1
- class Extension {
2
- constructor (extensions, name, handlers) {
3
- this.extensions = extensions
4
- this.name = name
5
- this.encoding = handlers.encoding
6
- this.destroyed = false
7
- // TODO: should avoid the bind here by calling directly on handlers instead?
8
- this.onmessage = (handlers.onmessage || noop).bind(handlers)
9
- this.onremotesupports = (handlers.onremotesupports || noop).bind(handlers)
10
- }
11
-
12
- send (message, peer) {
13
- if (this.destroyed) return
14
- const ext = peer.extensions.get(this.name)
15
- if (ext) ext.send(message)
16
- }
17
-
18
- broadcast (message) {
19
- if (this.extensions.replicator === null || this.destroyed) return
20
- for (const peer of this.extensions.replicator.peers) this.send(message, peer)
21
- }
22
-
23
- destroy () {
24
- if (this.destroyed) return
25
- this.destroyed = true
26
- this.extensions.all.delete(this.name)
27
- if (this.extensions.replicator === null) return
28
- for (const peer of this.extensions.replicator.peers) {
29
- const ext = peer.extensions.get(this.name)
30
- if (ext) ext.destroy()
31
- }
32
- }
33
- }
34
-
35
- module.exports = class Extensions {
36
- constructor () {
37
- this.replicator = null
38
- this.all = new Map()
39
- }
40
-
41
- [Symbol.iterator] () {
42
- return this.all[Symbol.iterator]()
43
- }
44
-
45
- attach (replicator) {
46
- if (replicator === this.replicator) return
47
- this.replicator = replicator
48
-
49
- for (const [name, ext] of this.all) {
50
- for (const peer of this.replicator.peers) {
51
- peer.registerExtension(name, ext)
52
- }
53
- }
54
- }
55
-
56
- register (name, handlers, ext = new Extension(this, name, handlers)) {
57
- if (this.all.has(name)) this.all.get(name).destroy()
58
- this.all.set(name, ext)
59
-
60
- if (this.replicator !== null) {
61
- for (const peer of this.replicator.peers) {
62
- peer.registerExtension(name, ext)
63
- }
64
- }
65
-
66
- return ext
67
- }
68
-
69
- update (peer) {
70
- for (const ext of this.all.values()) {
71
- peer.registerExtension(ext.name, ext)
72
- }
73
- }
74
- }
75
-
76
- function noop () {}
package/lib/protocol.js DELETED
@@ -1,606 +0,0 @@
1
- const { uint, from: fromEncoding } = require('compact-encoding')
2
- const b4a = require('b4a')
3
- const safetyCatch = require('safety-catch')
4
- const codecs = require('codecs')
5
- const sodium = require('sodium-universal')
6
-
7
- const messages = require('./messages')
8
-
9
- const slab = b4a.alloc(96)
10
- const NS = slab.subarray(0, 32)
11
- const NS_HYPERCORE_INITIATOR = slab.subarray(32, 64)
12
- const NS_HYPERCORE_RESPONDER = slab.subarray(64, 96)
13
-
14
- sodium.crypto_generichash(NS, b4a.from('hypercore'))
15
- sodium.crypto_generichash(NS_HYPERCORE_INITIATOR, b4a.from([0]), NS)
16
- sodium.crypto_generichash(NS_HYPERCORE_RESPONDER, b4a.from([1]), NS)
17
-
18
- class Extension {
19
- constructor (protocol, type, name, handlers) {
20
- this.protocol = protocol
21
- this.name = name
22
- this.type = type
23
- this.peers = new Set()
24
- this.aliased = !!handlers.aliased
25
- this.remoteSupports = false
26
- this.destroyed = false
27
-
28
- this.onerror = handlers.onerror || noop
29
- this.onclose = handlers.onclose || noop
30
- this.onmessage = handlers.onmessage || noop
31
- this.onremotesupports = handlers.onremotesupports || noop
32
-
33
- this.encoding = fromEncoding(codecs(handlers.encoding || 'binary'))
34
- this.announce()
35
- }
36
-
37
- announce () {
38
- if (this.destroyed) return
39
-
40
- this.protocol.send(1, messages.extension, -1, { alias: this.type, name: this.name })
41
- }
42
-
43
- send (message) {
44
- if (this.destroyed) return
45
-
46
- return this._sendAlias(message, -1)
47
- }
48
-
49
- _sendAlias (message, alias) {
50
- if (this.destroyed) return
51
-
52
- if (this.remoteSupports) {
53
- return this.protocol.send(this.type, this.encoding, alias, message)
54
- }
55
-
56
- this.protocol.cork()
57
- this.announce()
58
- this.protocol.send(this.type, this.encoding, alias, message)
59
- this.protocol.uncork()
60
-
61
- return false
62
- }
63
-
64
- _onremotesupports () {
65
- if (this.destroyed) return
66
-
67
- this.remoteSupports = true
68
- this.onremotesupports(this)
69
- for (const peer of this.peers) {
70
- peer.onremotesupports(peer)
71
- }
72
- }
73
-
74
- _onmessage (state) {
75
- if (this.destroyed) return
76
-
77
- if (!this.aliased) {
78
- this.onmessage(this.encoding.decode(state))
79
- return
80
- }
81
-
82
- const alias = uint.decode(state)
83
- const m = this.encoding.decode(state)
84
-
85
- for (const peer of this.peers) {
86
- if (peer.alias === alias) {
87
- peer.onmessage(m, peer.peer)
88
- }
89
- }
90
- }
91
-
92
- destroy () {
93
- if (this.destroyed) return
94
- this.destroyed = true
95
- this.protocol.unregisterExtension(this.name)
96
- this.onclose()
97
- }
98
- }
99
-
100
- class CoreExtension {
101
- constructor (ext, peer, name, handlers) {
102
- this.extension = ext
103
- this.peer = peer
104
- this.name = name
105
- this.alias = peer.alias
106
- this.onmessage = handlers.onmessage || noop
107
- this.onremotesupports = handlers.onremotesupports || noop
108
- }
109
-
110
- get remoteSupports () {
111
- return this.extension.remoteSupports
112
- }
113
-
114
- announce () {
115
- this.extension.announce()
116
- }
117
-
118
- send (message) {
119
- return this.extension._sendAlias(message, this.peer.alias)
120
- }
121
-
122
- destroy () {
123
- this.peer.extensions.delete(this.name)
124
- this.extension.peers.delete(this)
125
- }
126
- }
127
-
128
- class Peer {
129
- constructor (protocol, alias, key, discoveryKey, handlers, state) {
130
- this.protocol = protocol
131
- this.handlers = handlers
132
- this.key = key
133
- this.discoveryKey = discoveryKey
134
- this.alias = alias
135
- this.remoteAlias = -1
136
- this.resend = false
137
- this.state = state
138
- this.extensions = new Map()
139
- this.uploading = true
140
- this.downloading = true
141
- this.destroyed = false
142
-
143
- this._destroyer = this._safeDestroy.bind(this)
144
- }
145
-
146
- setUploading (uploading) {
147
- if (uploading === this.uploading) return
148
- this.uploading = uploading
149
- this._sendInfo()
150
- }
151
-
152
- setDownloading (downloading) {
153
- if (downloading === this.downloading) return
154
- this.downloading = downloading
155
- this._sendInfo()
156
- }
157
-
158
- _sendInfo () {
159
- this.info({
160
- length: this.handlers.core.tree.length,
161
- fork: this.handlers.core.tree.fork,
162
- uploading: this.uploading,
163
- downloading: this.downloading
164
- })
165
- }
166
-
167
- onmessage (type, state) {
168
- const handlers = this.handlers
169
-
170
- switch (type) {
171
- case 4: {
172
- this._catch(handlers.oninfo(messages.info.decode(state), this))
173
- break
174
- }
175
-
176
- case 5: {
177
- // options
178
- break
179
- }
180
-
181
- case 6: {
182
- // want
183
- break
184
- }
185
-
186
- case 7: {
187
- this._catch(handlers.onhave(messages.have.decode(state), this))
188
- break
189
- }
190
-
191
- case 8: {
192
- this._catch(handlers.onbitfield(messages.bitfield.decode(state), this))
193
- break
194
- }
195
-
196
- case 9: {
197
- this._catch(handlers.onrequest(messages.request.decode(state), this))
198
- break
199
- }
200
-
201
- case 10: {
202
- this._catch(handlers.ondata(messages.data.decode(state), this))
203
- break
204
- }
205
- }
206
-
207
- state.start = state.end
208
- }
209
-
210
- _catch (p) {
211
- if (isPromise(p)) p.then(noop, this._destroyer)
212
- }
213
-
214
- registerExtension (name, handlers) {
215
- if (this.extensions.has(name)) return this.extensions.get(name)
216
- const ext = this.protocol.registerExtension(name, { aliased: true, encoding: handlers.encoding })
217
- const coreExt = new CoreExtension(ext, this, name, handlers)
218
- ext.peers.add(coreExt)
219
- this.extensions.set(name, coreExt)
220
- return coreExt
221
- }
222
-
223
- cork () {
224
- this.protocol.cork()
225
- }
226
-
227
- uncork () {
228
- this.protocol.uncork()
229
- }
230
-
231
- info (message) {
232
- return this.protocol.send(4, messages.info, this.alias, message)
233
- }
234
-
235
- options (message) {
236
- // TODO
237
- // this._send(5, messages.info, this.alias, message)
238
- }
239
-
240
- want (message) {
241
- // TODO
242
- // this._send(6, messages.info, this.alias, message)
243
- }
244
-
245
- have (message) {
246
- return this.protocol.send(7, messages.have, this.alias, message)
247
- }
248
-
249
- bitfield (message) {
250
- return this.protocol.send(8, messages.bitfield, this.alias, message)
251
- }
252
-
253
- request (message) {
254
- return this.protocol.send(9, messages.request, this.alias, message)
255
- }
256
-
257
- data (message) {
258
- return this.protocol.send(10, messages.data, this.alias, message)
259
- }
260
-
261
- _safeDestroy (err) {
262
- safetyCatch(err)
263
- return this.destroy(err)
264
- }
265
-
266
- destroy (err) {
267
- this.destroyed = true
268
- return this.protocol.unregisterPeer(this, err)
269
- }
270
- }
271
-
272
- module.exports = class Protocol {
273
- constructor (noiseStream, handlers = {}) {
274
- this.noiseStream = noiseStream
275
-
276
- this.protocolVersion = handlers.protocolVersion || 0
277
- this.userAgent = handlers.userAgent || ''
278
- this.remoteUserAgent = ''
279
- this.handlers = handlers
280
-
281
- this._firstMessage = true
282
- this._corks = 1
283
- this._batch = []
284
-
285
- this._localAliases = 0
286
- this._remoteAliases = []
287
- this._peers = new Map()
288
-
289
- this._localExtensions = 128
290
- this._remoteExtensions = []
291
- this._extensions = new Map()
292
- this._keepAliveInterval = null
293
- this._pendingCaps = []
294
-
295
- this._destroyer = this._safeDestroy.bind(this)
296
- this.noiseStream.on('data', this.onmessage.bind(this))
297
- this.noiseStream.on('end', this.noiseStream.end) // no half open
298
- this.noiseStream.on('finish', () => {
299
- this.setKeepAlive(false)
300
- })
301
- this.noiseStream.on('close', () => {
302
- this.setKeepAlive(false)
303
- // TODO: If the stream was destroyed with an error, we probably want to forward it here
304
- for (const peer of this._peers.values()) {
305
- peer.destroy(null)
306
- }
307
- })
308
-
309
- this._sendHandshake()
310
- }
311
-
312
- setKeepAlive (enable) {
313
- if (enable) {
314
- if (this._keepAliveInterval) return
315
- this._keepAliveInterval = setInterval(this.ping.bind(this), 5000)
316
- if (this._keepAliveInterval.unref) this._keepAliveInterval.unref()
317
- } else {
318
- if (!this._keepAliveInterval) return
319
- clearInterval(this._keepAliveInterval)
320
- this._keepAliveInterval = null
321
- }
322
- }
323
-
324
- _sendHandshake () {
325
- const m = { protocolVersion: this.protocolVersion, userAgent: this.userAgent }
326
- const state = { start: 0, end: 0, buffer: null }
327
-
328
- messages.handshake.preencode(state, m)
329
- state.buffer = this.noiseStream.alloc(state.end)
330
- messages.handshake.encode(state, m)
331
- this.noiseStream.write(state.buffer)
332
- }
333
-
334
- isRegistered (discoveryKey) {
335
- return this._peers.has(discoveryKey.toString('hex'))
336
- }
337
-
338
- registerPeer (key, discoveryKey, handlers = {}, state = null) {
339
- const peer = new Peer(this, this._localAliases++, key, discoveryKey, handlers, state)
340
- this._peers.set(b4a.toString(discoveryKey, 'hex'), peer)
341
- this._announceCore(peer.alias, key, discoveryKey)
342
- return peer
343
- }
344
-
345
- unregisterPeer (peer, err) {
346
- this._peers.delete(b4a.toString(peer.discoveryKey, 'hex'))
347
-
348
- if (peer.remoteAlias > -1) {
349
- this._remoteAliases[peer.remoteAlias] = null
350
- peer.remoteAlias = -1
351
- }
352
-
353
- peer.handlers.onunregister(peer, err)
354
-
355
- if (err) this.noiseStream.destroy(err)
356
- }
357
-
358
- registerExtension (name, handlers) {
359
- let ext = this._extensions.get(name)
360
- if (ext) return ext
361
- ext = new Extension(this, this._localExtensions++, name, handlers)
362
- this._extensions.set(name, ext)
363
- return ext
364
- }
365
-
366
- unregisterExtension (name) {
367
- const ext = this._extensions.get(name)
368
- if (!ext) return
369
- if (!ext.destroyed) return ext.destroy()
370
- this._extensions.delete(name)
371
- this._remoteExtensions[ext.type - 128] = null
372
- }
373
-
374
- cork () {
375
- if (++this._corks === 1) this._batch = []
376
- }
377
-
378
- uncork () {
379
- if (--this._corks > 0) return
380
-
381
- const batch = this._batch
382
- this._batch = null
383
-
384
- if (batch.length === 0) return
385
-
386
- while (this._pendingCaps.length > 0) {
387
- const [key, cap] = this._pendingCaps.pop()
388
- hypercoreCapability(this.noiseStream.isInitiator, this.noiseStream.handshakeHash, key, cap)
389
- }
390
-
391
- const state = { start: 0, end: 0, buffer: null }
392
- const lens = new Array(batch.length)
393
-
394
- uint.preencode(state, 0)
395
- for (let i = 0; i < batch.length; i++) {
396
- const [type, enc, dk, message] = batch[i]
397
- const start = state.end
398
- uint.preencode(state, type)
399
- if (dk > -1) uint.preencode(state, dk)
400
- enc.preencode(state, message)
401
- uint.preencode(state, (lens[i] = state.end - start))
402
- }
403
-
404
- state.buffer = this.noiseStream.alloc(state.end)
405
-
406
- uint.encode(state, 0)
407
- for (let i = 0; i < batch.length; i++) {
408
- const [type, enc, dk, message] = batch[i]
409
- uint.encode(state, lens[i])
410
- uint.encode(state, type)
411
- if (dk > -1) uint.encode(state, dk)
412
- enc.encode(state, message)
413
- }
414
-
415
- this.noiseStream.write(state.buffer)
416
- }
417
-
418
- onmessage (message) {
419
- try {
420
- this._decode(message)
421
- } catch (err) {
422
- this._safeDestroy(err)
423
- }
424
- }
425
-
426
- _catch (p) {
427
- if (isPromise(p)) p.then(noop, this._destroyer)
428
- }
429
-
430
- _announceCore (alias, key, discoveryKey) {
431
- const cap = b4a.alloc(32)
432
-
433
- if (!this.noiseStream.handshakeHash) {
434
- this._pendingCaps.push([key, cap]) // encode it later...
435
- } else {
436
- hypercoreCapability(this.noiseStream.isInitiator, this.noiseStream.handshakeHash, key, cap)
437
- }
438
-
439
- this.send(2, messages.core, -1, {
440
- alias: alias,
441
- discoveryKey: discoveryKey,
442
- capability: cap
443
- })
444
- }
445
-
446
- _decode (buffer) {
447
- if (buffer.byteLength === 0) return
448
-
449
- const state = { start: 0, end: buffer.length, buffer }
450
-
451
- if (this._firstMessage === true) {
452
- this._firstMessage = false
453
- const { userAgent } = messages.handshake.decode(state)
454
- this.remoteUserAgent = userAgent
455
- this.uncork()
456
- return
457
- }
458
-
459
- const type = uint.decode(state)
460
-
461
- if (type === 0) { // batch
462
- while (state.start < state.end) {
463
- const len = uint.decode(state)
464
- state.end = state.start + len
465
- const type = uint.decode(state)
466
- this._decodeMessage(type, state)
467
- state.end = buffer.length
468
- }
469
- } else {
470
- this._decodeMessage(type, state)
471
- }
472
- }
473
-
474
- _decodeMessage (type, state) {
475
- switch (type) {
476
- case 1: return this._onextension(messages.extension.decode(state))
477
- case 2: return this._oncore(messages.core.decode(state))
478
- case 3: return this._onunknowncore(messages.unknownCore.decode(state))
479
- }
480
-
481
- if (type < 11) {
482
- const remoteAlias = uint.decode(state)
483
- const peer = this._remoteAliases[remoteAlias]
484
- if (peer) peer.onmessage(type, state)
485
- } else if (type >= 128) {
486
- const ext = this._remoteExtensions[type - 128]
487
- if (ext) ext._onmessage(state)
488
- }
489
-
490
- state.start = state.end
491
- }
492
-
493
- _onextension (m) {
494
- const type = m.alias - 128
495
- const ext = this._extensions.get(m.name)
496
-
497
- if (type === this._remoteExtensions.length) {
498
- this._remoteExtensions.push(null)
499
- }
500
-
501
- if (!ext) return
502
-
503
- if (type < 0 || type >= this._remoteExtensions.length) {
504
- this.destroy(new Error('Remote alias out of bounds'))
505
- return
506
- }
507
-
508
- this._remoteExtensions[type] = ext
509
- if (!ext.remoteSupports) ext._onremotesupports()
510
- }
511
-
512
- _oncore (m) {
513
- const hex = b4a.toString(m.discoveryKey, 'hex')
514
- const peer = this._peers.get(hex)
515
-
516
- // allow one alloc
517
- // TODO: if the remote allocs too many "holes", move to slower sparse firendly
518
- // data structures such as a Map
519
- if (m.alias === this._remoteAliases.length) this._remoteAliases.push(null)
520
-
521
- if (peer) {
522
- const expectedCap = hypercoreCapability(!this.noiseStream.isInitiator, this.noiseStream.handshakeHash, peer.key)
523
- if (!b4a.equals(expectedCap, m.capability)) {
524
- this.destroy(new Error('Remote sent an invalid capability'))
525
- return
526
- }
527
-
528
- if (m.alias >= this._remoteAliases.length) {
529
- this.destroy(new Error('Remote alias out of bounds'))
530
- return
531
- }
532
-
533
- this._remoteAliases[m.alias] = peer
534
- peer.remoteAlias = m.alias
535
- if (peer.resend) this._announceCore(peer.alias, peer.key, peer.discoveryKey)
536
- this._catch(peer.handlers.oncore(m, peer))
537
- return
538
- }
539
-
540
- const self = this
541
- const p = this.handlers.ondiscoverykey ? this.handlers.ondiscoverykey(m.discoveryKey) : undefined
542
-
543
- if (isPromise(p)) p.then(next, next)
544
- else next()
545
-
546
- function next () {
547
- if (self._peers.has(hex)) return self._oncore(m)
548
- self.send(3, messages.unknownCore, -1, { discoveryKey: m.discoveryKey })
549
- }
550
- }
551
-
552
- _onunknowncore (m) {
553
- const peer = this._peers.get(b4a.toString(m.discoveryKey, 'hex'))
554
- if (!peer) return
555
-
556
- peer.resend = true
557
- this._catch(peer.handlers.onunknowncore(m, peer))
558
- }
559
-
560
- send (type, enc, dk, message) {
561
- if (this._corks > 0) {
562
- this._batch.push([type, enc, dk, message])
563
- return false
564
- }
565
-
566
- const state = { start: 0, end: 0, buffer: null }
567
-
568
- uint.preencode(state, type)
569
- if (dk > -1) uint.preencode(state, dk)
570
- enc.preencode(state, message)
571
-
572
- state.buffer = this.noiseStream.alloc(state.end)
573
-
574
- uint.encode(state, type)
575
- if (dk > -1) uint.encode(state, dk)
576
- enc.encode(state, message)
577
-
578
- return this.noiseStream.write(state.buffer)
579
- }
580
-
581
- ping () {
582
- const empty = this.noiseStream.alloc(0)
583
- this.noiseStream.write(empty)
584
- }
585
-
586
- destroy (err) {
587
- return this.noiseStream.destroy(err)
588
- }
589
-
590
- _safeDestroy (err) {
591
- safetyCatch(err) // check if this was an accidental catch
592
- this.destroy(err)
593
- }
594
- }
595
-
596
- function noop () {}
597
-
598
- function isPromise (p) {
599
- return !!p && typeof p.then === 'function'
600
- }
601
-
602
- function hypercoreCapability (initiator, handshakeHash, key, cap = b4a.alloc(32)) {
603
- const ns = initiator ? NS_HYPERCORE_INITIATOR : NS_HYPERCORE_RESPONDER
604
- sodium.crypto_generichash_batch(cap, [handshakeHash, key], ns)
605
- return cap
606
- }