hypercore 10.0.0-alpha.20 → 10.0.0-alpha.24
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 +7 -1
- package/index.js +16 -3
- package/lib/messages.js +11 -2
- package/lib/protocol.js +62 -2
- package/lib/replicator.js +26 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -212,6 +212,12 @@ Buffer containing the public key identifying this core.
|
|
|
212
212
|
|
|
213
213
|
Populated after `ready` has been emitted. Will be `null` before the event.
|
|
214
214
|
|
|
215
|
+
#### `core.keyPair`
|
|
216
|
+
|
|
217
|
+
Object containing buffers of the core's public and secret key
|
|
218
|
+
|
|
219
|
+
Populated after `ready` has been emitted. Will be `null` before the event.
|
|
220
|
+
|
|
215
221
|
#### `core.discoveryKey`
|
|
216
222
|
|
|
217
223
|
Buffer containing a key derived from the core's public key.
|
|
@@ -274,6 +280,6 @@ socket.pipe(localCore.replicate(true)).pipe(socket)
|
|
|
274
280
|
|
|
275
281
|
Emitted when the core has been appended to (i.e. has a new length / byteLength), either locally or remotely.
|
|
276
282
|
|
|
277
|
-
#### `core.on('truncate')`
|
|
283
|
+
#### `core.on('truncate', ancestors, forkId)`
|
|
278
284
|
|
|
279
285
|
Emitted when the core has been truncated, either locally or remotely.
|
package/index.js
CHANGED
|
@@ -58,6 +58,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
58
58
|
this.encodeBatch = null
|
|
59
59
|
|
|
60
60
|
this.key = key || null
|
|
61
|
+
this.keyPair = null
|
|
61
62
|
this.discoveryKey = null
|
|
62
63
|
this.readable = true
|
|
63
64
|
this.writable = false
|
|
@@ -107,7 +108,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
107
108
|
if (!noiseStream) throw new Error('Invalid stream')
|
|
108
109
|
|
|
109
110
|
if (!noiseStream.userData) {
|
|
110
|
-
const protocol = Replicator.createProtocol(noiseStream)
|
|
111
|
+
const protocol = Replicator.createProtocol(noiseStream, opts)
|
|
111
112
|
if (opts.keepAlive !== false) protocol.setKeepAlive(true)
|
|
112
113
|
noiseStream.userData = protocol
|
|
113
114
|
noiseStream.on('error', noop) // All noise errors already propagate through outerStream
|
|
@@ -231,6 +232,8 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
231
232
|
this.storage = Hypercore.defaultStorage(opts.storage || storage)
|
|
232
233
|
|
|
233
234
|
this.core = await Core.open(this.storage, {
|
|
235
|
+
createIfMissing: opts.createIfMissing,
|
|
236
|
+
overwrite: opts.overwrite,
|
|
234
237
|
keyPair,
|
|
235
238
|
crypto: this.crypto,
|
|
236
239
|
onupdate: this._oncoreupdate.bind(this)
|
|
@@ -243,11 +246,13 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
243
246
|
}
|
|
244
247
|
|
|
245
248
|
this.replicator = new Replicator(this.core, {
|
|
246
|
-
onupdate: this._onpeerupdate.bind(this)
|
|
249
|
+
onupdate: this._onpeerupdate.bind(this),
|
|
250
|
+
onupload: this._onupload.bind(this)
|
|
247
251
|
})
|
|
248
252
|
|
|
249
253
|
this.discoveryKey = this.crypto.discoveryKey(this.core.header.signer.publicKey)
|
|
250
254
|
this.key = this.core.header.signer.publicKey
|
|
255
|
+
this.keyPair = this.core.header.signer
|
|
251
256
|
|
|
252
257
|
if (!this.encryption && opts.encryptionKey) {
|
|
253
258
|
this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
|
|
@@ -328,12 +333,20 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
328
333
|
return this.opening
|
|
329
334
|
}
|
|
330
335
|
|
|
336
|
+
_onupload (index, value, from) {
|
|
337
|
+
const byteLength = value.byteLength - this.padding
|
|
338
|
+
|
|
339
|
+
for (let i = 0; i < this.sessions.length; i++) {
|
|
340
|
+
this.sessions[i].emit('upload', index, byteLength, from)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
331
344
|
_oncoreupdate (status, bitfield, value, from) {
|
|
332
345
|
if (status !== 0) {
|
|
333
346
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
334
347
|
if ((status & 0b10) !== 0) {
|
|
335
348
|
if (this.cache) this.cache.clear()
|
|
336
|
-
this.sessions[i].emit('truncate', this.core.tree.fork)
|
|
349
|
+
this.sessions[i].emit('truncate', bitfield.start, this.core.tree.fork)
|
|
337
350
|
}
|
|
338
351
|
if ((status & 0b01) !== 0) {
|
|
339
352
|
this.sessions[i].emit('append')
|
package/lib/messages.js
CHANGED
|
@@ -256,16 +256,25 @@ exports.info = {
|
|
|
256
256
|
preencode (state, i) {
|
|
257
257
|
c.uint.preencode(state, i.length)
|
|
258
258
|
c.uint.preencode(state, i.fork)
|
|
259
|
+
c.uint.preencode(state, 1) // flags
|
|
259
260
|
},
|
|
260
261
|
encode (state, i) {
|
|
261
262
|
c.uint.encode(state, i.length)
|
|
262
263
|
c.uint.encode(state, i.fork)
|
|
264
|
+
c.uint.encode(state, (i.uploading ? 1 : 0) | (i.downloading ? 2 : 0))
|
|
263
265
|
},
|
|
264
266
|
decode (state) {
|
|
265
|
-
|
|
267
|
+
const i = {
|
|
266
268
|
length: c.uint.decode(state),
|
|
267
|
-
fork: c.uint.decode(state)
|
|
269
|
+
fork: c.uint.decode(state),
|
|
270
|
+
uploading: true,
|
|
271
|
+
downloading: true
|
|
268
272
|
}
|
|
273
|
+
if (state.end <= state.start) return i // backwards compat with prev alphas
|
|
274
|
+
const flags = c.uint.decode(state)
|
|
275
|
+
i.uploading = (flags & 1) !== 0
|
|
276
|
+
i.downloading = (flags & 2) !== 0
|
|
277
|
+
return i
|
|
269
278
|
}
|
|
270
279
|
}
|
|
271
280
|
|
package/lib/protocol.js
CHANGED
|
@@ -2,9 +2,19 @@ const { uint, from: fromEncoding } = require('compact-encoding')
|
|
|
2
2
|
const b4a = require('b4a')
|
|
3
3
|
const safetyCatch = require('safety-catch')
|
|
4
4
|
const codecs = require('codecs')
|
|
5
|
+
const sodium = require('sodium-universal')
|
|
5
6
|
|
|
6
7
|
const messages = require('./messages')
|
|
7
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
|
+
|
|
8
18
|
class Extension {
|
|
9
19
|
constructor (protocol, type, name, handlers) {
|
|
10
20
|
this.protocol = protocol
|
|
@@ -126,11 +136,34 @@ class Peer {
|
|
|
126
136
|
this.resend = false
|
|
127
137
|
this.state = state
|
|
128
138
|
this.extensions = new Map()
|
|
139
|
+
this.uploading = true
|
|
140
|
+
this.downloading = true
|
|
129
141
|
this.destroyed = false
|
|
130
142
|
|
|
131
143
|
this._destroyer = this._safeDestroy.bind(this)
|
|
132
144
|
}
|
|
133
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
|
+
|
|
134
167
|
onmessage (type, state) {
|
|
135
168
|
const handlers = this.handlers
|
|
136
169
|
|
|
@@ -257,10 +290,14 @@ module.exports = class Protocol {
|
|
|
257
290
|
this._remoteExtensions = []
|
|
258
291
|
this._extensions = new Map()
|
|
259
292
|
this._keepAliveInterval = null
|
|
293
|
+
this._pendingCaps = []
|
|
260
294
|
|
|
261
295
|
this._destroyer = this._safeDestroy.bind(this)
|
|
262
296
|
this.noiseStream.on('data', this.onmessage.bind(this))
|
|
263
297
|
this.noiseStream.on('end', this.noiseStream.end) // no half open
|
|
298
|
+
this.noiseStream.on('finish', () => {
|
|
299
|
+
this.setKeepAlive(false)
|
|
300
|
+
})
|
|
264
301
|
this.noiseStream.on('close', () => {
|
|
265
302
|
this.setKeepAlive(false)
|
|
266
303
|
// TODO: If the stream was destroyed with an error, we probably want to forward it here
|
|
@@ -346,6 +383,11 @@ module.exports = class Protocol {
|
|
|
346
383
|
|
|
347
384
|
if (batch.length === 0) return
|
|
348
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
|
+
|
|
349
391
|
const state = { start: 0, end: 0, buffer: null }
|
|
350
392
|
const lens = new Array(batch.length)
|
|
351
393
|
|
|
@@ -386,10 +428,18 @@ module.exports = class Protocol {
|
|
|
386
428
|
}
|
|
387
429
|
|
|
388
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
|
+
|
|
389
439
|
this.send(2, messages.core, -1, {
|
|
390
440
|
alias: alias,
|
|
391
441
|
discoveryKey: discoveryKey,
|
|
392
|
-
capability:
|
|
442
|
+
capability: cap
|
|
393
443
|
})
|
|
394
444
|
}
|
|
395
445
|
|
|
@@ -469,7 +519,11 @@ module.exports = class Protocol {
|
|
|
469
519
|
if (m.alias === this._remoteAliases.length) this._remoteAliases.push(null)
|
|
470
520
|
|
|
471
521
|
if (peer) {
|
|
472
|
-
|
|
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
|
+
}
|
|
473
527
|
|
|
474
528
|
if (m.alias >= this._remoteAliases.length) {
|
|
475
529
|
this.destroy(new Error('Remote alias out of bounds'))
|
|
@@ -544,3 +598,9 @@ function noop () {}
|
|
|
544
598
|
function isPromise (p) {
|
|
545
599
|
return !!p && typeof p.then === 'function'
|
|
546
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
|
+
}
|
package/lib/replicator.js
CHANGED
|
@@ -14,6 +14,8 @@ class RemoteState {
|
|
|
14
14
|
this.bitfield = new RemoteBitfield()
|
|
15
15
|
this.length = 0
|
|
16
16
|
this.fork = 0
|
|
17
|
+
this.uploading = true
|
|
18
|
+
this.downloading = true
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -241,6 +243,7 @@ class RequestPool {
|
|
|
241
243
|
|
|
242
244
|
update (peer) {
|
|
243
245
|
if (peer.state.inflight >= peer.state.maxInflight) return false
|
|
246
|
+
if (peer.downloading === false || peer.state.uploading === false) return false
|
|
244
247
|
if (this.paused) return false
|
|
245
248
|
|
|
246
249
|
if (peer.state.fork > this.core.tree.fork) {
|
|
@@ -624,17 +627,19 @@ class RequestPool {
|
|
|
624
627
|
}
|
|
625
628
|
|
|
626
629
|
module.exports = class Replicator {
|
|
627
|
-
constructor (core, { onupdate }) {
|
|
630
|
+
constructor (core, { onupdate, onupload }) {
|
|
628
631
|
this.core = core
|
|
629
632
|
this.peers = []
|
|
630
633
|
this.requests = new RequestPool(this, core)
|
|
631
634
|
this.updating = null
|
|
632
635
|
this.pendingPeers = new Set()
|
|
633
636
|
this.onupdate = onupdate
|
|
637
|
+
this.onupload = onupload
|
|
634
638
|
}
|
|
635
639
|
|
|
636
|
-
static createProtocol (noiseStream) {
|
|
640
|
+
static createProtocol (noiseStream, opts) {
|
|
637
641
|
return new Protocol(noiseStream, {
|
|
642
|
+
...opts,
|
|
638
643
|
protocolVersion: 0,
|
|
639
644
|
userAgent: USER_AGENT
|
|
640
645
|
})
|
|
@@ -658,8 +663,14 @@ module.exports = class Replicator {
|
|
|
658
663
|
}
|
|
659
664
|
|
|
660
665
|
broadcastInfo () {
|
|
661
|
-
const
|
|
662
|
-
|
|
666
|
+
for (const peer of this.peers) {
|
|
667
|
+
peer.info({
|
|
668
|
+
length: this.core.tree.length,
|
|
669
|
+
fork: this.core.tree.fork,
|
|
670
|
+
uploading: peer.uploading,
|
|
671
|
+
downloading: peer.downloading
|
|
672
|
+
})
|
|
673
|
+
}
|
|
663
674
|
this.updateAll()
|
|
664
675
|
}
|
|
665
676
|
|
|
@@ -737,7 +748,12 @@ module.exports = class Replicator {
|
|
|
737
748
|
this.peers.push(peer)
|
|
738
749
|
this.onupdate(true, peer)
|
|
739
750
|
|
|
740
|
-
peer.info({
|
|
751
|
+
peer.info({
|
|
752
|
+
length: this.core.tree.length,
|
|
753
|
+
fork: this.core.tree.fork,
|
|
754
|
+
uploading: peer.uploading,
|
|
755
|
+
downloading: peer.downloading
|
|
756
|
+
})
|
|
741
757
|
|
|
742
758
|
// YOLO send over all the pages for now
|
|
743
759
|
const p = pages(this.core)
|
|
@@ -752,12 +768,14 @@ module.exports = class Replicator {
|
|
|
752
768
|
// This is a no-op because there isn't any state to dealloc currently
|
|
753
769
|
}
|
|
754
770
|
|
|
755
|
-
oninfo ({ length, fork }, peer) {
|
|
771
|
+
oninfo ({ length, fork, uploading, downloading }, peer) {
|
|
756
772
|
const len = peer.state.length
|
|
757
773
|
const forked = peer.state.fork !== fork
|
|
758
774
|
|
|
759
775
|
peer.state.length = length
|
|
760
776
|
peer.state.fork = fork
|
|
777
|
+
peer.state.downloading = downloading
|
|
778
|
+
peer.state.uploading = uploading
|
|
761
779
|
|
|
762
780
|
if (forked) {
|
|
763
781
|
for (let i = peer.state.length; i < len; i++) {
|
|
@@ -813,11 +831,13 @@ module.exports = class Replicator {
|
|
|
813
831
|
async onrequest (req, peer) {
|
|
814
832
|
const fork = req.fork || peer.state.fork
|
|
815
833
|
if (fork !== this.core.tree.fork) return
|
|
834
|
+
if (!peer.uploading) return
|
|
816
835
|
|
|
817
836
|
const proof = await this.core.tree.proof(req)
|
|
818
837
|
|
|
819
838
|
if (req.block && req.block.value) {
|
|
820
839
|
proof.block.value = await this.core.blocks.get(req.block.index)
|
|
840
|
+
this.onupload(req.block.index, proof.block.value, peer)
|
|
821
841
|
}
|
|
822
842
|
|
|
823
843
|
peer.data(proof)
|