hypercore 10.36.2 → 10.36.4
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 +123 -0
- package/lib/core.js +15 -1
- package/lib/merkle-tree.js +7 -1
- package/package.json +1 -1
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
|
|
|
@@ -601,7 +615,7 @@ module.exports = class Core {
|
|
|
601
615
|
}
|
|
602
616
|
}
|
|
603
617
|
|
|
604
|
-
await this.blocks.putBatch(treeLength, adding
|
|
618
|
+
await this.blocks.putBatch(treeLength, adding < values.length ? values.slice(0, adding) : values, byteOffset)
|
|
605
619
|
await this.oplog.append([entry], false)
|
|
606
620
|
|
|
607
621
|
this._setBitfieldRange(entry.bitfield.start, entry.bitfield.length, true)
|
package/lib/merkle-tree.js
CHANGED
|
@@ -4,7 +4,7 @@ const c = require('compact-encoding')
|
|
|
4
4
|
const Xache = require('xache')
|
|
5
5
|
const b4a = require('b4a')
|
|
6
6
|
const caps = require('./caps')
|
|
7
|
-
const { INVALID_PROOF, INVALID_CHECKSUM, INVALID_OPERATION, BAD_ARGUMENT } = require('hypercore-errors')
|
|
7
|
+
const { INVALID_PROOF, INVALID_CHECKSUM, INVALID_OPERATION, BAD_ARGUMENT, ASSERTION } = require('hypercore-errors')
|
|
8
8
|
|
|
9
9
|
const BLANK_HASH = b4a.alloc(32)
|
|
10
10
|
const OLD_TREE = b4a.from([5, 2, 87, 2, 0, 0, 40, 7, 66, 76, 65, 75, 69, 50, 98])
|
|
@@ -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
|
|
@@ -763,6 +767,8 @@ async function getByteOffset (tree, index) {
|
|
|
763
767
|
|
|
764
768
|
return offset
|
|
765
769
|
}
|
|
770
|
+
|
|
771
|
+
throw ASSERTION('Failed to find offset')
|
|
766
772
|
}
|
|
767
773
|
|
|
768
774
|
// All the methods needed for proof verification
|