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 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 [byteEnd, byteEndLength] = await this.tree.byteRange((end - 1) * 2)
275
- const length = (byteEnd + byteEndLength) - offset
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(),
@@ -714,6 +714,7 @@ module.exports = class MerkleTree {
714
714
  }
715
715
 
716
716
  async byteOffset (index) {
717
+ if (index === 2 * this.length) return this.byteLength
717
718
  if ((index & 1) === 1) index = flat.leftSpan(index)
718
719
 
719
720
  let head = 0
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.19.1",
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
  }