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 +123 -0
- package/lib/core.js +14 -0
- package/lib/merkle-tree.js +4 -0
- package/lib/replicator.js +3 -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
|
|
package/lib/merkle-tree.js
CHANGED
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 () {
|