hypercore 10.36.2 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.36.2",
3
+ "version": "10.36.3",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {