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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.0.0-alpha.23",
3
+ "version": "10.0.0-alpha.26",
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,583 +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.destroyed = false
140
-
141
- this._destroyer = this._safeDestroy.bind(this)
142
- }
143
-
144
- onmessage (type, state) {
145
- const handlers = this.handlers
146
-
147
- switch (type) {
148
- case 4: {
149
- this._catch(handlers.oninfo(messages.info.decode(state), this))
150
- break
151
- }
152
-
153
- case 5: {
154
- // options
155
- break
156
- }
157
-
158
- case 6: {
159
- // want
160
- break
161
- }
162
-
163
- case 7: {
164
- this._catch(handlers.onhave(messages.have.decode(state), this))
165
- break
166
- }
167
-
168
- case 8: {
169
- this._catch(handlers.onbitfield(messages.bitfield.decode(state), this))
170
- break
171
- }
172
-
173
- case 9: {
174
- this._catch(handlers.onrequest(messages.request.decode(state), this))
175
- break
176
- }
177
-
178
- case 10: {
179
- this._catch(handlers.ondata(messages.data.decode(state), this))
180
- break
181
- }
182
- }
183
-
184
- state.start = state.end
185
- }
186
-
187
- _catch (p) {
188
- if (isPromise(p)) p.then(noop, this._destroyer)
189
- }
190
-
191
- registerExtension (name, handlers) {
192
- if (this.extensions.has(name)) return this.extensions.get(name)
193
- const ext = this.protocol.registerExtension(name, { aliased: true, encoding: handlers.encoding })
194
- const coreExt = new CoreExtension(ext, this, name, handlers)
195
- ext.peers.add(coreExt)
196
- this.extensions.set(name, coreExt)
197
- return coreExt
198
- }
199
-
200
- cork () {
201
- this.protocol.cork()
202
- }
203
-
204
- uncork () {
205
- this.protocol.uncork()
206
- }
207
-
208
- info (message) {
209
- return this.protocol.send(4, messages.info, this.alias, message)
210
- }
211
-
212
- options (message) {
213
- // TODO
214
- // this._send(5, messages.info, this.alias, message)
215
- }
216
-
217
- want (message) {
218
- // TODO
219
- // this._send(6, messages.info, this.alias, message)
220
- }
221
-
222
- have (message) {
223
- return this.protocol.send(7, messages.have, this.alias, message)
224
- }
225
-
226
- bitfield (message) {
227
- return this.protocol.send(8, messages.bitfield, this.alias, message)
228
- }
229
-
230
- request (message) {
231
- return this.protocol.send(9, messages.request, this.alias, message)
232
- }
233
-
234
- data (message) {
235
- return this.protocol.send(10, messages.data, this.alias, message)
236
- }
237
-
238
- _safeDestroy (err) {
239
- safetyCatch(err)
240
- return this.destroy(err)
241
- }
242
-
243
- destroy (err) {
244
- this.destroyed = true
245
- return this.protocol.unregisterPeer(this, err)
246
- }
247
- }
248
-
249
- module.exports = class Protocol {
250
- constructor (noiseStream, handlers = {}) {
251
- this.noiseStream = noiseStream
252
-
253
- this.protocolVersion = handlers.protocolVersion || 0
254
- this.userAgent = handlers.userAgent || ''
255
- this.remoteUserAgent = ''
256
- this.handlers = handlers
257
-
258
- this._firstMessage = true
259
- this._corks = 1
260
- this._batch = []
261
-
262
- this._localAliases = 0
263
- this._remoteAliases = []
264
- this._peers = new Map()
265
-
266
- this._localExtensions = 128
267
- this._remoteExtensions = []
268
- this._extensions = new Map()
269
- this._keepAliveInterval = null
270
- this._pendingCaps = []
271
-
272
- this._destroyer = this._safeDestroy.bind(this)
273
- this.noiseStream.on('data', this.onmessage.bind(this))
274
- this.noiseStream.on('end', this.noiseStream.end) // no half open
275
- this.noiseStream.on('finish', () => {
276
- this.setKeepAlive(false)
277
- })
278
- this.noiseStream.on('close', () => {
279
- this.setKeepAlive(false)
280
- // TODO: If the stream was destroyed with an error, we probably want to forward it here
281
- for (const peer of this._peers.values()) {
282
- peer.destroy(null)
283
- }
284
- })
285
-
286
- this._sendHandshake()
287
- }
288
-
289
- setKeepAlive (enable) {
290
- if (enable) {
291
- if (this._keepAliveInterval) return
292
- this._keepAliveInterval = setInterval(this.ping.bind(this), 5000)
293
- if (this._keepAliveInterval.unref) this._keepAliveInterval.unref()
294
- } else {
295
- if (!this._keepAliveInterval) return
296
- clearInterval(this._keepAliveInterval)
297
- this._keepAliveInterval = null
298
- }
299
- }
300
-
301
- _sendHandshake () {
302
- const m = { protocolVersion: this.protocolVersion, userAgent: this.userAgent }
303
- const state = { start: 0, end: 0, buffer: null }
304
-
305
- messages.handshake.preencode(state, m)
306
- state.buffer = this.noiseStream.alloc(state.end)
307
- messages.handshake.encode(state, m)
308
- this.noiseStream.write(state.buffer)
309
- }
310
-
311
- isRegistered (discoveryKey) {
312
- return this._peers.has(discoveryKey.toString('hex'))
313
- }
314
-
315
- registerPeer (key, discoveryKey, handlers = {}, state = null) {
316
- const peer = new Peer(this, this._localAliases++, key, discoveryKey, handlers, state)
317
- this._peers.set(b4a.toString(discoveryKey, 'hex'), peer)
318
- this._announceCore(peer.alias, key, discoveryKey)
319
- return peer
320
- }
321
-
322
- unregisterPeer (peer, err) {
323
- this._peers.delete(b4a.toString(peer.discoveryKey, 'hex'))
324
-
325
- if (peer.remoteAlias > -1) {
326
- this._remoteAliases[peer.remoteAlias] = null
327
- peer.remoteAlias = -1
328
- }
329
-
330
- peer.handlers.onunregister(peer, err)
331
-
332
- if (err) this.noiseStream.destroy(err)
333
- }
334
-
335
- registerExtension (name, handlers) {
336
- let ext = this._extensions.get(name)
337
- if (ext) return ext
338
- ext = new Extension(this, this._localExtensions++, name, handlers)
339
- this._extensions.set(name, ext)
340
- return ext
341
- }
342
-
343
- unregisterExtension (name) {
344
- const ext = this._extensions.get(name)
345
- if (!ext) return
346
- if (!ext.destroyed) return ext.destroy()
347
- this._extensions.delete(name)
348
- this._remoteExtensions[ext.type - 128] = null
349
- }
350
-
351
- cork () {
352
- if (++this._corks === 1) this._batch = []
353
- }
354
-
355
- uncork () {
356
- if (--this._corks > 0) return
357
-
358
- const batch = this._batch
359
- this._batch = null
360
-
361
- if (batch.length === 0) return
362
-
363
- while (this._pendingCaps.length > 0) {
364
- const [key, cap] = this._pendingCaps.pop()
365
- hypercoreCapability(this.noiseStream.isInitiator, this.noiseStream.handshakeHash, key, cap)
366
- }
367
-
368
- const state = { start: 0, end: 0, buffer: null }
369
- const lens = new Array(batch.length)
370
-
371
- uint.preencode(state, 0)
372
- for (let i = 0; i < batch.length; i++) {
373
- const [type, enc, dk, message] = batch[i]
374
- const start = state.end
375
- uint.preencode(state, type)
376
- if (dk > -1) uint.preencode(state, dk)
377
- enc.preencode(state, message)
378
- uint.preencode(state, (lens[i] = state.end - start))
379
- }
380
-
381
- state.buffer = this.noiseStream.alloc(state.end)
382
-
383
- uint.encode(state, 0)
384
- for (let i = 0; i < batch.length; i++) {
385
- const [type, enc, dk, message] = batch[i]
386
- uint.encode(state, lens[i])
387
- uint.encode(state, type)
388
- if (dk > -1) uint.encode(state, dk)
389
- enc.encode(state, message)
390
- }
391
-
392
- this.noiseStream.write(state.buffer)
393
- }
394
-
395
- onmessage (message) {
396
- try {
397
- this._decode(message)
398
- } catch (err) {
399
- this._safeDestroy(err)
400
- }
401
- }
402
-
403
- _catch (p) {
404
- if (isPromise(p)) p.then(noop, this._destroyer)
405
- }
406
-
407
- _announceCore (alias, key, discoveryKey) {
408
- const cap = b4a.alloc(32)
409
-
410
- if (!this.noiseStream.handshakeHash) {
411
- this._pendingCaps.push([key, cap]) // encode it later...
412
- } else {
413
- hypercoreCapability(this.noiseStream.isInitiator, this.noiseStream.handshakeHash, key, cap)
414
- }
415
-
416
- this.send(2, messages.core, -1, {
417
- alias: alias,
418
- discoveryKey: discoveryKey,
419
- capability: cap
420
- })
421
- }
422
-
423
- _decode (buffer) {
424
- if (buffer.byteLength === 0) return
425
-
426
- const state = { start: 0, end: buffer.length, buffer }
427
-
428
- if (this._firstMessage === true) {
429
- this._firstMessage = false
430
- const { userAgent } = messages.handshake.decode(state)
431
- this.remoteUserAgent = userAgent
432
- this.uncork()
433
- return
434
- }
435
-
436
- const type = uint.decode(state)
437
-
438
- if (type === 0) { // batch
439
- while (state.start < state.end) {
440
- const len = uint.decode(state)
441
- state.end = state.start + len
442
- const type = uint.decode(state)
443
- this._decodeMessage(type, state)
444
- state.end = buffer.length
445
- }
446
- } else {
447
- this._decodeMessage(type, state)
448
- }
449
- }
450
-
451
- _decodeMessage (type, state) {
452
- switch (type) {
453
- case 1: return this._onextension(messages.extension.decode(state))
454
- case 2: return this._oncore(messages.core.decode(state))
455
- case 3: return this._onunknowncore(messages.unknownCore.decode(state))
456
- }
457
-
458
- if (type < 11) {
459
- const remoteAlias = uint.decode(state)
460
- const peer = this._remoteAliases[remoteAlias]
461
- if (peer) peer.onmessage(type, state)
462
- } else if (type >= 128) {
463
- const ext = this._remoteExtensions[type - 128]
464
- if (ext) ext._onmessage(state)
465
- }
466
-
467
- state.start = state.end
468
- }
469
-
470
- _onextension (m) {
471
- const type = m.alias - 128
472
- const ext = this._extensions.get(m.name)
473
-
474
- if (type === this._remoteExtensions.length) {
475
- this._remoteExtensions.push(null)
476
- }
477
-
478
- if (!ext) return
479
-
480
- if (type < 0 || type >= this._remoteExtensions.length) {
481
- this.destroy(new Error('Remote alias out of bounds'))
482
- return
483
- }
484
-
485
- this._remoteExtensions[type] = ext
486
- if (!ext.remoteSupports) ext._onremotesupports()
487
- }
488
-
489
- _oncore (m) {
490
- const hex = b4a.toString(m.discoveryKey, 'hex')
491
- const peer = this._peers.get(hex)
492
-
493
- // allow one alloc
494
- // TODO: if the remote allocs too many "holes", move to slower sparse firendly
495
- // data structures such as a Map
496
- if (m.alias === this._remoteAliases.length) this._remoteAliases.push(null)
497
-
498
- if (peer) {
499
- const expectedCap = hypercoreCapability(!this.noiseStream.isInitiator, this.noiseStream.handshakeHash, peer.key)
500
- if (!b4a.equals(expectedCap, m.capability)) {
501
- this.destroy(new Error('Remote sent an invalid capability'))
502
- return
503
- }
504
-
505
- if (m.alias >= this._remoteAliases.length) {
506
- this.destroy(new Error('Remote alias out of bounds'))
507
- return
508
- }
509
-
510
- this._remoteAliases[m.alias] = peer
511
- peer.remoteAlias = m.alias
512
- if (peer.resend) this._announceCore(peer.alias, peer.key, peer.discoveryKey)
513
- this._catch(peer.handlers.oncore(m, peer))
514
- return
515
- }
516
-
517
- const self = this
518
- const p = this.handlers.ondiscoverykey ? this.handlers.ondiscoverykey(m.discoveryKey) : undefined
519
-
520
- if (isPromise(p)) p.then(next, next)
521
- else next()
522
-
523
- function next () {
524
- if (self._peers.has(hex)) return self._oncore(m)
525
- self.send(3, messages.unknownCore, -1, { discoveryKey: m.discoveryKey })
526
- }
527
- }
528
-
529
- _onunknowncore (m) {
530
- const peer = this._peers.get(b4a.toString(m.discoveryKey, 'hex'))
531
- if (!peer) return
532
-
533
- peer.resend = true
534
- this._catch(peer.handlers.onunknowncore(m, peer))
535
- }
536
-
537
- send (type, enc, dk, message) {
538
- if (this._corks > 0) {
539
- this._batch.push([type, enc, dk, message])
540
- return false
541
- }
542
-
543
- const state = { start: 0, end: 0, buffer: null }
544
-
545
- uint.preencode(state, type)
546
- if (dk > -1) uint.preencode(state, dk)
547
- enc.preencode(state, message)
548
-
549
- state.buffer = this.noiseStream.alloc(state.end)
550
-
551
- uint.encode(state, type)
552
- if (dk > -1) uint.encode(state, dk)
553
- enc.encode(state, message)
554
-
555
- return this.noiseStream.write(state.buffer)
556
- }
557
-
558
- ping () {
559
- const empty = this.noiseStream.alloc(0)
560
- this.noiseStream.write(empty)
561
- }
562
-
563
- destroy (err) {
564
- return this.noiseStream.destroy(err)
565
- }
566
-
567
- _safeDestroy (err) {
568
- safetyCatch(err) // check if this was an accidental catch
569
- this.destroy(err)
570
- }
571
- }
572
-
573
- function noop () {}
574
-
575
- function isPromise (p) {
576
- return !!p && typeof p.then === 'function'
577
- }
578
-
579
- function hypercoreCapability (initiator, handshakeHash, key, cap = b4a.alloc(32)) {
580
- const ns = initiator ? NS_HYPERCORE_INITIATOR : NS_HYPERCORE_RESPONDER
581
- sodium.crypto_generichash_batch(cap, [handshakeHash, key], ns)
582
- return cap
583
- }