hypercore 10.20.0 → 10.20.2

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
@@ -840,6 +840,7 @@ module.exports = class Hypercore extends EventEmitter {
840
840
  const cleared = (opts && opts.diff) ? { blocks: 0 } : null
841
841
 
842
842
  if (start >= end) return cleared
843
+ if (start >= this.length) return cleared
843
844
 
844
845
  await this.core.clear(start, end, cleared)
845
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
@@ -222,7 +223,7 @@ module.exports = class Core {
222
223
 
223
224
  this.tree.signature = signature || auth.sign(this.tree.signable())
224
225
 
225
- if (signature && !this._signed(this.tree)) {
226
+ if (signature && !this._verifyBatch(this.tree)) {
226
227
  // TODO: how to handle signature failure?
227
228
  this.tree.signature = null
228
229
  throw INVALID_SIGNATURE('Clone was provided with an invalid signature')
@@ -335,8 +336,8 @@ module.exports = class Core {
335
336
  if (start >= end || start >= this.tree.length) return
336
337
 
337
338
  const offset = await this.tree.byteOffset(start * 2)
338
- const [byteEnd, byteEndLength] = await this.tree.byteRange((end - 1) * 2)
339
- const length = (byteEnd + byteEndLength) - offset
339
+ const endOffset = await this.tree.byteOffset(end * 2)
340
+ const length = endOffset - offset
340
341
 
341
342
  const before = cleared ? await Info.bytesUsed(this.blocks.storage) : null
342
343
 
@@ -422,17 +423,18 @@ module.exports = class Core {
422
423
  }
423
424
  }
424
425
 
425
- _signed (batch, hash, auth = this.defaultAuth) {
426
+ _verifyBatch (batch) {
427
+ const hash = batch.hash()
426
428
  const signable = this._legacy ? batch.signableLegacy(hash) : batch.signable(hash)
427
- return auth.verify(signable, batch.signature, batch)
428
- }
429
+ const auth = this.defaultAuth
429
430
 
430
- async _verifyExclusive ({ batch, bitfield, value, from }) {
431
- // TODO: move this to tree.js
432
- const hash = batch.hash()
433
- if (!batch.signature || !this._signed(batch, hash)) {
431
+ if (!batch.signature || !auth.verify(signable, batch.signature, batch)) {
434
432
  throw INVALID_SIGNATURE('Proof contains an invalid signature')
435
433
  }
434
+ }
435
+
436
+ async _verifyExclusive ({ batch, bitfield, value, from }) {
437
+ this._verifyBatch(batch)
436
438
 
437
439
  await this._mutex.lock()
438
440
 
@@ -540,9 +542,7 @@ module.exports = class Core {
540
542
 
541
543
  const batch = this.tree.verifyFullyRemote(proof)
542
544
 
543
- if (!batch.signature || !this._signed(batch, batch.hash())) {
544
- throw INVALID_SIGNATURE('Proof contains an invalid signature with no input from us')
545
- }
545
+ this._verifyBatch(batch)
546
546
 
547
547
  const remoteTreeHash = this.crypto.tree(proof.upgrade.nodes)
548
548
  const localTreeHash = this.crypto.tree(await this.tree.getRoots(proof.upgrade.length))
@@ -553,6 +553,14 @@ module.exports = class Core {
553
553
  return true
554
554
  }
555
555
 
556
+ async verifyReorg (proof) {
557
+ const batch = await this.tree.reorg(proof)
558
+
559
+ this._verifyBatch(batch)
560
+
561
+ return batch
562
+ }
563
+
556
564
  async verify (proof, from) {
557
565
  // We cannot apply "other forks" atm.
558
566
  // We should probably still try and they are likely super similar for non upgrades
@@ -639,6 +647,7 @@ module.exports = class Core {
639
647
  }
640
648
 
641
649
  async close () {
650
+ this.closed = true
642
651
  await this._mutex.destroy()
643
652
  await Promise.allSettled([
644
653
  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/oplog.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const cenc = require('compact-encoding')
2
2
  const b4a = require('b4a')
3
3
  const { crc32 } = require('crc-universal')
4
- const { OPLOG_CORRUPT } = require('hypercore-errors')
4
+ const { OPLOG_CORRUPT, OPLOG_HEADER_OVERFLOW } = require('hypercore-errors')
5
5
 
6
6
  module.exports = class Oplog {
7
7
  constructor (storage, { pageSize = 4096, headerEncoding = cenc.raw, entryEncoding = cenc.raw, readonly = false } = {}) {
@@ -155,6 +155,7 @@ module.exports = class Oplog {
155
155
  const bit = (this._headers[i] + 1) & 1
156
156
 
157
157
  this.headerEncoding.preencode(state, header)
158
+ if (state.end > this._pageSize) throw OPLOG_HEADER_OVERFLOW()
158
159
  state.buffer = b4a.allocUnsafe(state.end)
159
160
  this.headerEncoding.encode(state, header)
160
161
  this._addHeader(state, state.end - 8, bit, 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()
@@ -1524,6 +1526,7 @@ module.exports = class Replicator {
1524
1526
  }
1525
1527
 
1526
1528
  async _onreorgdata (peer, req, data) {
1529
+ const newBatch = data.upgrade && await this.core.verifyReorg(data)
1527
1530
  const f = this._addReorg(data.fork, peer)
1528
1531
 
1529
1532
  if (f === null) {
@@ -1536,7 +1539,7 @@ module.exports = class Replicator {
1536
1539
  if (f.batch) {
1537
1540
  await f.batch.update(data)
1538
1541
  } else if (data.upgrade) {
1539
- f.batch = await this.core.tree.reorg(data)
1542
+ f.batch = newBatch
1540
1543
 
1541
1544
  // Remove "older" reorgs in progress as we just verified this one.
1542
1545
  this._clearOldReorgs(f.fork)
@@ -1711,7 +1714,7 @@ module.exports = class Replicator {
1711
1714
  protomux.stream.opened.then((opened) => {
1712
1715
  this._ifAvailable--
1713
1716
 
1714
- if (opened) makePeer()
1717
+ if (opened && !this.destroyed) makePeer()
1715
1718
  else if (session) session.close().catch(noop)
1716
1719
  this._checkUpgradeIfAvailable()
1717
1720
  })
@@ -1725,6 +1728,7 @@ module.exports = class Replicator {
1725
1728
  }
1726
1729
 
1727
1730
  destroy () {
1731
+ this.destroyed = true
1728
1732
  for (const peer of this.peers) {
1729
1733
  this.detachFrom(peer.protomux)
1730
1734
  peer.channel.close()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.20.0",
3
+ "version": "10.20.2",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -44,7 +44,7 @@
44
44
  "fast-fifo": "^1.3.0",
45
45
  "flat-tree": "^1.9.0",
46
46
  "hypercore-crypto": "^3.2.1",
47
- "hypercore-errors": "^1.0.0",
47
+ "hypercore-errors": "^1.1.0",
48
48
  "is-options": "^1.0.1",
49
49
  "protomux": "^3.5.0",
50
50
  "quickbit-universal": "^2.1.1",