hypercore 10.0.0-alpha.22 → 10.0.0-alpha.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/index.js +39 -6
- package/lib/merkle-tree.js +15 -0
- package/lib/messages.js +11 -2
- package/lib/protocol.js +23 -0
- package/lib/replicator.js +26 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,6 +101,10 @@ Truncate the core to a smaller length.
|
|
|
101
101
|
Per default this will update the fork id of the core to `+ 1`, but you can set the fork id you prefer with the option.
|
|
102
102
|
Note that the fork id should be monotonely incrementing.
|
|
103
103
|
|
|
104
|
+
#### `const hash = await core.treeHash([length])`
|
|
105
|
+
|
|
106
|
+
Get the Merkle Tree hash of the core at a given length, defaulting to the current length of the core.
|
|
107
|
+
|
|
104
108
|
#### `const stream = core.createReadStream([options])`
|
|
105
109
|
|
|
106
110
|
Make a read stream. Options include:
|
|
@@ -212,6 +216,12 @@ Buffer containing the public key identifying this core.
|
|
|
212
216
|
|
|
213
217
|
Populated after `ready` has been emitted. Will be `null` before the event.
|
|
214
218
|
|
|
219
|
+
#### `core.keyPair`
|
|
220
|
+
|
|
221
|
+
Object containing buffers of the core's public and secret key
|
|
222
|
+
|
|
223
|
+
Populated after `ready` has been emitted. Will be `null` before the event.
|
|
224
|
+
|
|
215
225
|
#### `core.discoveryKey`
|
|
216
226
|
|
|
217
227
|
Buffer containing a key derived from the core's public key.
|
|
@@ -274,6 +284,6 @@ socket.pipe(localCore.replicate(true)).pipe(socket)
|
|
|
274
284
|
|
|
275
285
|
Emitted when the core has been appended to (i.e. has a new length / byteLength), either locally or remotely.
|
|
276
286
|
|
|
277
|
-
#### `core.on('truncate')`
|
|
287
|
+
#### `core.on('truncate', ancestors, forkId)`
|
|
278
288
|
|
|
279
289
|
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
|
|
@@ -72,6 +73,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
72
73
|
this.opening.catch(noop)
|
|
73
74
|
|
|
74
75
|
this._preappend = preappend.bind(this)
|
|
76
|
+
this._snapshot = opts.snapshot || null
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
[inspect] (depth, opts) {
|
|
@@ -107,7 +109,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
107
109
|
if (!noiseStream) throw new Error('Invalid stream')
|
|
108
110
|
|
|
109
111
|
if (!noiseStream.userData) {
|
|
110
|
-
const protocol = Replicator.createProtocol(noiseStream)
|
|
112
|
+
const protocol = Replicator.createProtocol(noiseStream, opts)
|
|
111
113
|
if (opts.keepAlive !== false) protocol.setKeepAlive(true)
|
|
112
114
|
noiseStream.userData = protocol
|
|
113
115
|
noiseStream.on('error', noop) // All noise errors already propagate through outerStream
|
|
@@ -128,6 +130,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
|
|
133
|
+
snapshot () {
|
|
134
|
+
return this.session({ snapshot: { length: this.length, byteLength: this.byteLength, fork: this.fork } })
|
|
135
|
+
}
|
|
136
|
+
|
|
131
137
|
session (opts = {}) {
|
|
132
138
|
if (this.closing) {
|
|
133
139
|
// This makes the closing logic alot easier. If this turns out to be a problem
|
|
@@ -245,11 +251,13 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
245
251
|
}
|
|
246
252
|
|
|
247
253
|
this.replicator = new Replicator(this.core, {
|
|
248
|
-
onupdate: this._onpeerupdate.bind(this)
|
|
254
|
+
onupdate: this._onpeerupdate.bind(this),
|
|
255
|
+
onupload: this._onupload.bind(this)
|
|
249
256
|
})
|
|
250
257
|
|
|
251
258
|
this.discoveryKey = this.crypto.discoveryKey(this.core.header.signer.publicKey)
|
|
252
259
|
this.key = this.core.header.signer.publicKey
|
|
260
|
+
this.keyPair = this.core.header.signer
|
|
253
261
|
|
|
254
262
|
if (!this.encryption && opts.encryptionKey) {
|
|
255
263
|
this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
|
|
@@ -274,6 +282,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
274
282
|
this.readable = false
|
|
275
283
|
this.writable = false
|
|
276
284
|
this.closed = true
|
|
285
|
+
this.opened = false
|
|
277
286
|
|
|
278
287
|
if (this.sessions.length) {
|
|
279
288
|
// if this is the last session and we are auto closing, trigger that first to enforce error handling
|
|
@@ -303,15 +312,21 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
303
312
|
}
|
|
304
313
|
|
|
305
314
|
get length () {
|
|
306
|
-
return this.
|
|
315
|
+
return this._snapshot
|
|
316
|
+
? this._snapshot.length
|
|
317
|
+
: (this.core === null ? 0 : this.core.tree.length)
|
|
307
318
|
}
|
|
308
319
|
|
|
309
320
|
get byteLength () {
|
|
310
|
-
return this.
|
|
321
|
+
return this._snapshot
|
|
322
|
+
? this._snapshot.byteLength
|
|
323
|
+
: (this.core === null ? 0 : this.core.tree.byteLength - (this.core.tree.length * this.padding))
|
|
311
324
|
}
|
|
312
325
|
|
|
313
326
|
get fork () {
|
|
314
|
-
return this.
|
|
327
|
+
return this._snapshot
|
|
328
|
+
? this._snapshot.fork
|
|
329
|
+
: (this.core === null ? 0 : this.core.tree.fork)
|
|
315
330
|
}
|
|
316
331
|
|
|
317
332
|
get peers () {
|
|
@@ -330,12 +345,20 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
330
345
|
return this.opening
|
|
331
346
|
}
|
|
332
347
|
|
|
348
|
+
_onupload (index, value, from) {
|
|
349
|
+
const byteLength = value.byteLength - this.padding
|
|
350
|
+
|
|
351
|
+
for (let i = 0; i < this.sessions.length; i++) {
|
|
352
|
+
this.sessions[i].emit('upload', index, byteLength, from)
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
333
356
|
_oncoreupdate (status, bitfield, value, from) {
|
|
334
357
|
if (status !== 0) {
|
|
335
358
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
336
359
|
if ((status & 0b10) !== 0) {
|
|
337
360
|
if (this.cache) this.cache.clear()
|
|
338
|
-
this.sessions[i].emit('truncate', this.core.tree.fork)
|
|
361
|
+
this.sessions[i].emit('truncate', bitfield.start, this.core.tree.fork)
|
|
339
362
|
}
|
|
340
363
|
if ((status & 0b01) !== 0) {
|
|
341
364
|
this.sessions[i].emit('append')
|
|
@@ -507,6 +530,16 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
507
530
|
return await this.core.append(buffers, this.sign, { preappend })
|
|
508
531
|
}
|
|
509
532
|
|
|
533
|
+
async treeHash (length) {
|
|
534
|
+
if (length === undefined) {
|
|
535
|
+
await this.ready()
|
|
536
|
+
length = this.core.length
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const roots = await this.core.tree.getRoots(length)
|
|
540
|
+
return this.crypto.tree(roots)
|
|
541
|
+
}
|
|
542
|
+
|
|
510
543
|
registerExtension (name, handlers) {
|
|
511
544
|
return this.extensions.register(name, handlers)
|
|
512
545
|
}
|
package/lib/merkle-tree.js
CHANGED
|
@@ -392,6 +392,21 @@ module.exports = class MerkleTree {
|
|
|
392
392
|
return Promise.all(roots)
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
+
async upgradeable (length) {
|
|
396
|
+
const indexes = flat.fullRoots(2 * length)
|
|
397
|
+
const roots = new Array(indexes.length)
|
|
398
|
+
|
|
399
|
+
for (let i = 0; i < indexes.length; i++) {
|
|
400
|
+
roots[i] = this.get(indexes[i], false)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
for (const node of await Promise.all(roots)) {
|
|
404
|
+
if (node === null) return false
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return true
|
|
408
|
+
}
|
|
409
|
+
|
|
395
410
|
get (index, error = true) {
|
|
396
411
|
let node = this.unflushed.get(index)
|
|
397
412
|
|
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
|
@@ -136,11 +136,34 @@ class Peer {
|
|
|
136
136
|
this.resend = false
|
|
137
137
|
this.state = state
|
|
138
138
|
this.extensions = new Map()
|
|
139
|
+
this.uploading = true
|
|
140
|
+
this.downloading = true
|
|
139
141
|
this.destroyed = false
|
|
140
142
|
|
|
141
143
|
this._destroyer = this._safeDestroy.bind(this)
|
|
142
144
|
}
|
|
143
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
|
+
|
|
144
167
|
onmessage (type, state) {
|
|
145
168
|
const handlers = this.handlers
|
|
146
169
|
|
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)
|