hypercore 10.36.1 → 10.36.3

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/lib/audit.js ADDED
@@ -0,0 +1,123 @@
1
+ const hypercoreCrypto = require('hypercore-crypto')
2
+ const flat = require('flat-tree')
3
+ const c = require('compact-encoding')
4
+ const b4a = require('b4a')
5
+
6
+ // this is optimised for speed over mem atm
7
+ // can be tweaked in the future
8
+
9
+ module.exports = async function auditCore (core) {
10
+ const corrections = {
11
+ tree: 0,
12
+ blocks: 0
13
+ }
14
+
15
+ const length = core.header.tree.length
16
+
17
+ const data = await readFullStorage(core.blocks.storage)
18
+ const tree = await readFullStorage(core.tree.storage)
19
+
20
+ const valid = new Uint8Array(Math.ceil(tree.byteLength / 40))
21
+ const stack = []
22
+
23
+ for (const r of core.tree.roots) {
24
+ valid[r.index] = 1
25
+ stack.push(r)
26
+ }
27
+
28
+ while (stack.length > 0) {
29
+ const node = stack.pop()
30
+ if ((node.index & 1) === 0) continue
31
+
32
+ const [left, right] = flat.children(node.index)
33
+ const leftNode = getNode(left)
34
+ const rightNode = getNode(right)
35
+
36
+ stack.push(leftNode, rightNode)
37
+
38
+ if (valid[node.index]) {
39
+ const hash = hypercoreCrypto.parent(leftNode, rightNode)
40
+ if (b4a.equals(hash, node.hash) && node.size === (leftNode.size + rightNode.size)) {
41
+ valid[leftNode.index] = 1
42
+ valid[rightNode.index] = 1
43
+ continue
44
+ }
45
+ }
46
+
47
+ if (leftNode.size) clearNode(leftNode)
48
+ if (rightNode.size) clearNode(rightNode)
49
+ }
50
+
51
+ if (corrections.tree) {
52
+ core.tree.cache.clear()
53
+ }
54
+
55
+ let i = 0
56
+ let nextOffset = -1
57
+ while (i < length) {
58
+ const has = core.bitfield.get(i)
59
+
60
+ if (!has) {
61
+ if (i + 1 === length) break
62
+ i = core.bitfield.findFirst(true, i + 1)
63
+ if (i < 0) break
64
+ nextOffset = -1
65
+ continue
66
+ }
67
+
68
+ if (nextOffset === -1) {
69
+ try {
70
+ nextOffset = await core.tree.byteOffset(i * 2)
71
+ } catch {
72
+ core._setBitfield(i, false)
73
+ corrections.blocks++
74
+ i++
75
+ continue
76
+ }
77
+ }
78
+
79
+ const node = getNode(i * 2)
80
+ const blk = data.subarray(nextOffset, nextOffset + node.size)
81
+ const hash = hypercoreCrypto.data(blk)
82
+
83
+ nextOffset += blk.byteLength
84
+
85
+ if (!b4a.equals(hash, node.hash)) {
86
+ core._setBitfield(i, false)
87
+ corrections.blocks++
88
+ }
89
+
90
+ i++
91
+ }
92
+
93
+ return corrections
94
+
95
+ function getNode (index) {
96
+ const state = { start: index * 40, end: index * 40 + 40, buffer: tree }
97
+ const size = c.uint64.decode(state)
98
+ const hash = c.fixed32.decode(state)
99
+ return { index, size, hash }
100
+ }
101
+
102
+ function clearNode (node) {
103
+ valid[node.index] = 0
104
+
105
+ if (node.size) {
106
+ b4a.fill(tree, 0, node.index * 40, node.index * 40 + 40)
107
+ core.tree.unflushed.set(node.index, core.tree.blankNode(node.index))
108
+ corrections.tree++
109
+ }
110
+ }
111
+ }
112
+
113
+ function readFullStorage (storage) {
114
+ return new Promise((resolve, reject) => {
115
+ storage.stat((_, st) => {
116
+ if (!st) return resolve(b4a.alloc(0))
117
+ storage.read(0, st.size, (err, data) => {
118
+ if (err) reject(err)
119
+ else resolve(data)
120
+ })
121
+ })
122
+ })
123
+ }
package/lib/core.js CHANGED
@@ -11,6 +11,7 @@ const Info = require('./info')
11
11
  const { BAD_ARGUMENT, STORAGE_EMPTY, STORAGE_CONFLICT, INVALID_OPERATION, INVALID_SIGNATURE, INVALID_CHECKSUM } = require('hypercore-errors')
12
12
  const m = require('./messages')
13
13
  const Verifier = require('./verifier')
14
+ const audit = require('./audit')
14
15
  const { createTracer } = require('hypertrace')
15
16
 
16
17
  module.exports = class Core {
@@ -194,6 +195,19 @@ module.exports = class Core {
194
195
  return new this(header, compat, crypto, oplog, bigHeader, tree, blocks, bitfield, verifier, legacy, opts.onupdate || noop, opts.onconflict || noop)
195
196
  }
196
197
 
198
+ async audit () {
199
+ await this._mutex.lock()
200
+
201
+ try {
202
+ await this._flushOplog()
203
+ const corrections = await audit(this)
204
+ if (corrections.blocks || corrections.tree) await this._flushOplog()
205
+ return corrections
206
+ } finally {
207
+ await this._mutex.unlock()
208
+ }
209
+ }
210
+
197
211
  async setManifest (manifest) {
198
212
  await this._mutex.lock()
199
213
 
@@ -444,6 +444,10 @@ module.exports = class MerkleTree {
444
444
  return true
445
445
  }
446
446
 
447
+ blankNode (index) {
448
+ return blankNode(index)
449
+ }
450
+
447
451
  get (index, error = true) {
448
452
  const c = this.cache.get(index)
449
453
  if (c) return c
package/lib/replicator.js CHANGED
@@ -1491,12 +1491,14 @@ module.exports = class Replicator {
1491
1491
  }
1492
1492
 
1493
1493
  clearRequests (session, err = null) {
1494
+ let cleared = false
1494
1495
  while (session.length > 0) {
1495
1496
  const ref = session[session.length - 1]
1496
1497
  ref.context.detach(ref, err)
1498
+ cleared = true
1497
1499
  }
1498
1500
 
1499
- this.updateAll()
1501
+ if (cleared) this.updateAll()
1500
1502
  }
1501
1503
 
1502
1504
  _addUpgradeMaybe () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.36.1",
3
+ "version": "10.36.3",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {