hypercore 10.19.1 → 10.20.1
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/index.js +54 -0
- package/lib/core.js +68 -2
- package/lib/merkle-tree.js +1 -0
- package/lib/replicator.js +4 -1
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -82,6 +82,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
82
82
|
this.onwait = opts.onwait || null
|
|
83
83
|
this.wait = opts.wait !== false
|
|
84
84
|
this.timeout = opts.timeout || 0
|
|
85
|
+
this._clone = opts.clone || null
|
|
85
86
|
this._readonly = opts.writable === false
|
|
86
87
|
|
|
87
88
|
this.closing = null
|
|
@@ -306,6 +307,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
306
307
|
const s = this.sessions[i]
|
|
307
308
|
if (s !== this) s._passCapabilities(this)
|
|
308
309
|
}
|
|
310
|
+
|
|
311
|
+
// copy state over
|
|
312
|
+
if (this._clone) {
|
|
313
|
+
const { from, upgrade } = this._clone
|
|
314
|
+
await from.opening
|
|
315
|
+
await this.core.copyFrom(from.core, upgrade)
|
|
316
|
+
this._clone = null
|
|
317
|
+
}
|
|
309
318
|
}
|
|
310
319
|
|
|
311
320
|
if (!this.auth) this.auth = this.core.defaultAuth
|
|
@@ -458,6 +467,50 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
458
467
|
this.emit('close', true)
|
|
459
468
|
}
|
|
460
469
|
|
|
470
|
+
clone (storage, opts = {}) {
|
|
471
|
+
// TODO: current limitation is no forking
|
|
472
|
+
if ((opts.fork && opts.fork !== 0) || this.fork !== 0) {
|
|
473
|
+
throw BAD_ARGUMENT('Cannot clone a fork')
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const key = opts.key === undefined ? opts.keyPair ? null : this.key : opts.key
|
|
477
|
+
const keyPair = (opts.auth || opts.keyPair === undefined) ? null : opts.keyPair
|
|
478
|
+
|
|
479
|
+
let auth = this.core.defaultAuth
|
|
480
|
+
if (opts.auth) {
|
|
481
|
+
auth = opts.auth
|
|
482
|
+
} else if (opts.sign && keyPair) {
|
|
483
|
+
auth = Core.createAuth(this.crypto, keyPair, opts)
|
|
484
|
+
} else if (opts.sign) {
|
|
485
|
+
// TODO: dangerous to just update sign?
|
|
486
|
+
auth.sign = opts.sign
|
|
487
|
+
} else if (keyPair && keyPair.secretKey) {
|
|
488
|
+
auth = Core.createAuth(this.crypto, keyPair)
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const upgrade = opts.upgrade === undefined ? null : opts.upgrade
|
|
492
|
+
|
|
493
|
+
const sparse = opts.sparse === false ? false : this.sparse
|
|
494
|
+
const wait = opts.wait === false ? false : this.wait
|
|
495
|
+
const onwait = opts.onwait === undefined ? this.onwait : opts.onwait
|
|
496
|
+
const timeout = opts.timeout === undefined ? this.timeout : opts.timeout
|
|
497
|
+
|
|
498
|
+
const Clz = this.constructor
|
|
499
|
+
return new Clz(storage, key, {
|
|
500
|
+
...opts,
|
|
501
|
+
sparse,
|
|
502
|
+
wait,
|
|
503
|
+
onwait,
|
|
504
|
+
timeout,
|
|
505
|
+
auth,
|
|
506
|
+
overwrite: true,
|
|
507
|
+
clone: {
|
|
508
|
+
from: this,
|
|
509
|
+
upgrade
|
|
510
|
+
}
|
|
511
|
+
})
|
|
512
|
+
}
|
|
513
|
+
|
|
461
514
|
replicate (isInitiator, opts = {}) {
|
|
462
515
|
// Only limitation here is that ondiscoverykey doesn't work atm when passing a muxer directly,
|
|
463
516
|
// because it doesn't really make a lot of sense.
|
|
@@ -787,6 +840,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
787
840
|
const cleared = (opts && opts.diff) ? { blocks: 0 } : null
|
|
788
841
|
|
|
789
842
|
if (start >= end) return cleared
|
|
843
|
+
if (start >= this.length) return cleared
|
|
790
844
|
|
|
791
845
|
await this.core.clear(start, end, cleared)
|
|
792
846
|
|
package/lib/core.js
CHANGED
|
@@ -22,6 +22,7 @@ module.exports = class Core {
|
|
|
22
22
|
this.bitfield = bitfield
|
|
23
23
|
this.defaultAuth = auth
|
|
24
24
|
this.truncating = 0
|
|
25
|
+
this.closed = false
|
|
25
26
|
|
|
26
27
|
this._maxOplogSize = 65536
|
|
27
28
|
this._autoFlush = 1
|
|
@@ -175,6 +176,70 @@ module.exports = class Core {
|
|
|
175
176
|
return false
|
|
176
177
|
}
|
|
177
178
|
|
|
179
|
+
async copyFrom (src, signature, auth = this.defaultAuth) {
|
|
180
|
+
this._mutex.lock()
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
let pos = src.bitfield.firstSet(0)
|
|
184
|
+
|
|
185
|
+
while (pos >= 0) {
|
|
186
|
+
const segmentStart = pos
|
|
187
|
+
const segmentEnd = src.bitfield.firstUnset(pos)
|
|
188
|
+
|
|
189
|
+
if (segmentStart < 0) break
|
|
190
|
+
|
|
191
|
+
const segment = []
|
|
192
|
+
|
|
193
|
+
while (pos < segmentEnd) {
|
|
194
|
+
const val = await src.blocks.get(pos++)
|
|
195
|
+
segment.push(val)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const [offset] = await src.tree.byteRange(2 * segmentStart)
|
|
199
|
+
await this.blocks.putBatch(segmentStart, segment, offset)
|
|
200
|
+
|
|
201
|
+
this.bitfield.setRange(segmentStart, segmentEnd, true)
|
|
202
|
+
|
|
203
|
+
pos = src.bitfield.firstSet(segmentEnd + 1)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// TODO: is it an issue to move the nodes directly?
|
|
207
|
+
// TODO: make flat iterator that computes needed nodes
|
|
208
|
+
|
|
209
|
+
for (let i = 0; i <= src.tree.length * 2; i++) {
|
|
210
|
+
const node = await src.tree.get(i, false)
|
|
211
|
+
if (node === null) continue
|
|
212
|
+
|
|
213
|
+
await this.tree.addNode(node)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
await this.tree.flush()
|
|
217
|
+
|
|
218
|
+
this.tree.fork = src.tree.fork
|
|
219
|
+
this.tree.roots = [...src.tree.roots]
|
|
220
|
+
this.tree.length = src.tree.length
|
|
221
|
+
this.tree.byteLength = src.tree.byteLength
|
|
222
|
+
this.tree.signature = null // must provide signature
|
|
223
|
+
|
|
224
|
+
this.tree.signature = signature || auth.sign(this.tree.signable())
|
|
225
|
+
|
|
226
|
+
if (signature && !this._signed(this.tree)) {
|
|
227
|
+
// TODO: how to handle signature failure?
|
|
228
|
+
this.tree.signature = null
|
|
229
|
+
throw INVALID_SIGNATURE('Clone was provided with an invalid signature')
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
this.header.tree.length = this.tree.length
|
|
233
|
+
this.header.tree.rootHash = this.tree.hash()
|
|
234
|
+
this.header.tree.signature = this.tree.signature
|
|
235
|
+
this.header.userData = src.header.userData // should copy?
|
|
236
|
+
|
|
237
|
+
await this._flushOplog()
|
|
238
|
+
} finally {
|
|
239
|
+
this._mutex.unlock()
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
178
243
|
async _flushOplog () {
|
|
179
244
|
// TODO: the apis using this, actually do not need to wait for the bitfields, tree etc to flush
|
|
180
245
|
// as their mutations are already stored in the oplog. We could potentially just run this in the
|
|
@@ -271,8 +336,8 @@ module.exports = class Core {
|
|
|
271
336
|
if (start >= end || start >= this.tree.length) return
|
|
272
337
|
|
|
273
338
|
const offset = await this.tree.byteOffset(start * 2)
|
|
274
|
-
const
|
|
275
|
-
const length =
|
|
339
|
+
const endOffset = await this.tree.byteOffset(end * 2)
|
|
340
|
+
const length = endOffset - offset
|
|
276
341
|
|
|
277
342
|
const before = cleared ? await Info.bytesUsed(this.blocks.storage) : null
|
|
278
343
|
|
|
@@ -575,6 +640,7 @@ module.exports = class Core {
|
|
|
575
640
|
}
|
|
576
641
|
|
|
577
642
|
async close () {
|
|
643
|
+
this.closed = true
|
|
578
644
|
await this._mutex.destroy()
|
|
579
645
|
await Promise.allSettled([
|
|
580
646
|
this.oplog.close(),
|
package/lib/merkle-tree.js
CHANGED
package/lib/replicator.js
CHANGED
|
@@ -560,6 +560,7 @@ class Peer {
|
|
|
560
560
|
proof = await this._getProof(msg)
|
|
561
561
|
} catch (err) { // TODO: better error handling here, ie custom errors
|
|
562
562
|
safetyCatch(err)
|
|
563
|
+
if (this.replicator.core.closed) throw err // just an extra safety check...
|
|
563
564
|
}
|
|
564
565
|
}
|
|
565
566
|
|
|
@@ -1004,6 +1005,7 @@ module.exports = class Replicator {
|
|
|
1004
1005
|
this.onupload = onupload
|
|
1005
1006
|
this.peers = []
|
|
1006
1007
|
this.findingPeers = 0 // updateable from the outside
|
|
1008
|
+
this.destroyed = false
|
|
1007
1009
|
|
|
1008
1010
|
this._attached = new Set()
|
|
1009
1011
|
this._inflight = new InflightTracker()
|
|
@@ -1711,7 +1713,7 @@ module.exports = class Replicator {
|
|
|
1711
1713
|
protomux.stream.opened.then((opened) => {
|
|
1712
1714
|
this._ifAvailable--
|
|
1713
1715
|
|
|
1714
|
-
if (opened) makePeer()
|
|
1716
|
+
if (opened && !this.destroyed) makePeer()
|
|
1715
1717
|
else if (session) session.close().catch(noop)
|
|
1716
1718
|
this._checkUpgradeIfAvailable()
|
|
1717
1719
|
})
|
|
@@ -1725,6 +1727,7 @@ module.exports = class Replicator {
|
|
|
1725
1727
|
}
|
|
1726
1728
|
|
|
1727
1729
|
destroy () {
|
|
1730
|
+
this.destroyed = true
|
|
1728
1731
|
for (const peer of this.peers) {
|
|
1729
1732
|
this.detachFrom(peer.protomux)
|
|
1730
1733
|
peer.channel.close()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.20.1",
|
|
4
4
|
"description": "Hypercore is a secure, distributed append-only log",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"range-parser": "^1.2.1",
|
|
65
65
|
"speedometer": "^1.1.0",
|
|
66
66
|
"standard": "^17.0.0",
|
|
67
|
+
"test-tmp": "^1.0.2",
|
|
67
68
|
"tiny-byte-size": "^1.1.0",
|
|
68
|
-
"tmp-promise": "^3.0.2",
|
|
69
69
|
"udx-native": "^1.6.1"
|
|
70
70
|
}
|
|
71
71
|
}
|