hypercore 9.12.0 → 10.0.0-alpha.11
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/.github/workflows/test-node.yml +3 -4
- package/README.md +131 -404
- package/__snapshots__/test/storage.js.snapshot.cjs +15 -0
- package/examples/announce.js +19 -0
- package/examples/basic.js +10 -0
- package/examples/http.js +123 -0
- package/examples/lookup.js +20 -0
- package/index.js +365 -1600
- package/lib/bitfield.js +113 -285
- package/lib/block-encryption.js +68 -0
- package/lib/block-store.js +58 -0
- package/lib/core.js +468 -0
- package/lib/extensions.js +76 -0
- package/lib/merkle-tree.js +1110 -0
- package/lib/messages.js +571 -0
- package/lib/mutex.js +39 -0
- package/lib/oplog.js +224 -0
- package/lib/protocol.js +525 -0
- package/lib/random-iterator.js +46 -0
- package/lib/remote-bitfield.js +24 -0
- package/lib/replicator.js +857 -0
- package/lib/streams.js +39 -0
- package/package.json +44 -45
- package/test/basic.js +59 -471
- package/test/bitfield.js +48 -133
- package/test/core.js +290 -0
- package/test/encodings.js +18 -0
- package/test/encryption.js +123 -0
- package/test/extension.js +71 -0
- package/test/helpers/index.js +23 -0
- package/test/merkle-tree.js +518 -0
- package/test/mutex.js +137 -0
- package/test/oplog.js +399 -0
- package/test/preload.js +72 -0
- package/test/replicate.js +227 -824
- package/test/sessions.js +173 -0
- package/test/storage.js +31 -0
- package/test/streams.js +39 -146
- package/test/user-data.js +47 -0
- package/bench/all.sh +0 -65
- package/bench/copy-64kb-blocks.js +0 -51
- package/bench/helpers/read-throttled.js +0 -27
- package/bench/helpers/read.js +0 -47
- package/bench/helpers/write.js +0 -29
- package/bench/read-16kb-blocks-proof-throttled.js +0 -1
- package/bench/read-16kb-blocks-proof.js +0 -1
- package/bench/read-16kb-blocks-throttled.js +0 -1
- package/bench/read-16kb-blocks.js +0 -1
- package/bench/read-512b-blocks.js +0 -1
- package/bench/read-64kb-blocks-linear-batch.js +0 -18
- package/bench/read-64kb-blocks-linear.js +0 -18
- package/bench/read-64kb-blocks-proof.js +0 -1
- package/bench/read-64kb-blocks.js +0 -1
- package/bench/replicate-16kb-blocks.js +0 -19
- package/bench/replicate-64kb-blocks.js +0 -19
- package/bench/write-16kb-blocks.js +0 -1
- package/bench/write-512b-blocks.js +0 -1
- package/bench/write-64kb-blocks-static.js +0 -1
- package/bench/write-64kb-blocks.js +0 -1
- package/example.js +0 -23
- package/lib/cache.js +0 -26
- package/lib/crypto.js +0 -5
- package/lib/replicate.js +0 -829
- package/lib/safe-buffer-equals.js +0 -6
- package/lib/storage.js +0 -421
- package/lib/tree-index.js +0 -183
- package/test/ack.js +0 -306
- package/test/audit.js +0 -36
- package/test/cache.js +0 -93
- package/test/compat.js +0 -209
- package/test/copy.js +0 -377
- package/test/default-storage.js +0 -51
- package/test/extensions.js +0 -137
- package/test/get.js +0 -64
- package/test/head.js +0 -65
- package/test/helpers/create-tracking-ram.js +0 -27
- package/test/helpers/create.js +0 -6
- package/test/helpers/replicate.js +0 -4
- package/test/seek.js +0 -234
- package/test/selections.js +0 -95
- package/test/set-uploading-downloading.js +0 -91
- package/test/stats.js +0 -77
- package/test/timeouts.js +0 -22
- package/test/tree-index.js +0 -841
- package/test/update.js +0 -156
- package/test/value-encoding.js +0 -52
|
@@ -0,0 +1,1110 @@
|
|
|
1
|
+
const flat = require('flat-tree')
|
|
2
|
+
const crypto = require('hypercore-crypto')
|
|
3
|
+
const c = require('compact-encoding')
|
|
4
|
+
const b4a = require('b4a')
|
|
5
|
+
|
|
6
|
+
const BLANK_HASH = b4a.alloc(32)
|
|
7
|
+
const OLD_TREE = b4a.from([5, 2, 87, 2, 0, 0, 40, 7, 66, 76, 65, 75, 69, 50, 98])
|
|
8
|
+
|
|
9
|
+
class NodeQueue {
|
|
10
|
+
constructor (nodes, extra = null) {
|
|
11
|
+
this.i = 0
|
|
12
|
+
this.nodes = nodes
|
|
13
|
+
this.extra = extra
|
|
14
|
+
this.length = nodes.length + (this.extra === null ? 0 : 1)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
shift (index) {
|
|
18
|
+
if (this.extra !== null && this.extra.index === index) {
|
|
19
|
+
const node = this.extra
|
|
20
|
+
this.extra = null
|
|
21
|
+
this.length--
|
|
22
|
+
return node
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (this.i >= this.nodes.length) {
|
|
26
|
+
throw new Error('Expected node ' + index + ', got (nil)')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const node = this.nodes[this.i++]
|
|
30
|
+
if (node.index !== index) {
|
|
31
|
+
throw new Error('Expected node ' + index + ', got node ' + node.index)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.length--
|
|
35
|
+
return node
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class MerkleTreeBatch {
|
|
40
|
+
constructor (tree) {
|
|
41
|
+
this.fork = tree.fork
|
|
42
|
+
this.roots = [...tree.roots]
|
|
43
|
+
this.length = tree.length
|
|
44
|
+
this.ancestors = tree.length
|
|
45
|
+
this.byteLength = tree.byteLength
|
|
46
|
+
this.signature = null
|
|
47
|
+
|
|
48
|
+
this.treeLength = tree.length
|
|
49
|
+
this.treeFork = tree.fork
|
|
50
|
+
this.tree = tree
|
|
51
|
+
this.nodes = []
|
|
52
|
+
this.upgraded = false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
hash () {
|
|
56
|
+
return this.tree.crypto.tree(this.roots)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
signable (hash = this.hash()) {
|
|
60
|
+
return signable(hash, this.length, this.fork)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
signedBy (key) {
|
|
64
|
+
return this.signature !== null && this.tree.crypto.verify(this.signable(), this.signature, key)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
append (buf) {
|
|
68
|
+
const head = this.length * 2
|
|
69
|
+
const ite = flat.iterator(head)
|
|
70
|
+
const node = blockNode(this.tree.crypto, head, buf)
|
|
71
|
+
|
|
72
|
+
this.appendRoot(node, ite)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
appendRoot (node, ite) {
|
|
76
|
+
this.upgraded = true
|
|
77
|
+
this.length += ite.factor / 2
|
|
78
|
+
this.byteLength += node.size
|
|
79
|
+
this.roots.push(node)
|
|
80
|
+
this.nodes.push(node)
|
|
81
|
+
|
|
82
|
+
while (this.roots.length > 1) {
|
|
83
|
+
const a = this.roots[this.roots.length - 1]
|
|
84
|
+
const b = this.roots[this.roots.length - 2]
|
|
85
|
+
|
|
86
|
+
// TODO: just have a peek sibling instead? (pretty sure it's always the left sib as well)
|
|
87
|
+
if (ite.sibling() !== b.index) {
|
|
88
|
+
ite.sibling() // unset so it always points to last root
|
|
89
|
+
break
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const node = parentNode(this.tree.crypto, ite.parent(), a, b)
|
|
93
|
+
this.nodes.push(node)
|
|
94
|
+
this.roots.pop()
|
|
95
|
+
this.roots.pop()
|
|
96
|
+
this.roots.push(node)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
commitable () {
|
|
101
|
+
return this.treeFork === this.tree.fork && (
|
|
102
|
+
this.upgraded
|
|
103
|
+
? this.treeLength === this.tree.length
|
|
104
|
+
: this.treeLength <= this.tree.length
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
commit () {
|
|
109
|
+
if (!this.commitable()) throw new Error('Tree was modified during batch, refusing to commit')
|
|
110
|
+
|
|
111
|
+
if (this.upgraded) this._commitUpgrade()
|
|
112
|
+
|
|
113
|
+
for (let i = 0; i < this.nodes.length; i++) {
|
|
114
|
+
const node = this.nodes[i]
|
|
115
|
+
this.tree.unflushed.set(node.index, node)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
_commitUpgrade () {
|
|
120
|
+
// TODO: If easy to detect, we should refuse an trunc+append here without a fork id
|
|
121
|
+
// change. Will only happen on user error so mostly to prevent that.
|
|
122
|
+
|
|
123
|
+
if (this.ancestors < this.treeLength) {
|
|
124
|
+
if (this.ancestors > 0) {
|
|
125
|
+
const head = 2 * this.ancestors
|
|
126
|
+
const ite = flat.iterator(head - 2)
|
|
127
|
+
|
|
128
|
+
while (true) {
|
|
129
|
+
if (ite.contains(head) && ite.index < head) {
|
|
130
|
+
this.tree.unflushed.set(ite.index, blankNode(ite.index))
|
|
131
|
+
}
|
|
132
|
+
if (ite.offset === 0) break
|
|
133
|
+
ite.parent()
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.tree.truncateTo = this.tree.truncated
|
|
138
|
+
? Math.min(this.tree.truncateTo, this.ancestors)
|
|
139
|
+
: this.ancestors
|
|
140
|
+
|
|
141
|
+
this.tree.truncated = true
|
|
142
|
+
truncateMap(this.tree.unflushed, this.ancestors)
|
|
143
|
+
if (this.tree.flushing !== null) truncateMap(this.tree.flushing, this.ancestors)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this.tree.roots = this.roots
|
|
147
|
+
this.tree.length = this.length
|
|
148
|
+
this.tree.byteLength = this.byteLength
|
|
149
|
+
this.tree.fork = this.fork
|
|
150
|
+
this.tree.signature = this.signature
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// TODO: this is the only async method on the batch, so unsure if it should go here
|
|
154
|
+
// this is important so you know where to right data without committing the batch
|
|
155
|
+
// so we'll keep it here for now.
|
|
156
|
+
|
|
157
|
+
async byteOffset (index) {
|
|
158
|
+
if (2 * this.tree.length === index) return this.tree.byteLength
|
|
159
|
+
|
|
160
|
+
const ite = flat.iterator(index)
|
|
161
|
+
|
|
162
|
+
let treeOffset = 0
|
|
163
|
+
let isRight = false
|
|
164
|
+
let parent = null
|
|
165
|
+
|
|
166
|
+
for (const node of this.nodes) {
|
|
167
|
+
if (node.index === ite.index) {
|
|
168
|
+
if (isRight && parent) treeOffset += node.size - parent.size
|
|
169
|
+
parent = node
|
|
170
|
+
isRight = ite.isRight()
|
|
171
|
+
ite.parent()
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const r = this.roots.indexOf(parent)
|
|
176
|
+
if (r > -1) {
|
|
177
|
+
for (let i = 0; i < r; i++) {
|
|
178
|
+
treeOffset += this.roots[i].size
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return treeOffset
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const byteOffset = await this.tree.byteOffset(parent ? parent.index : index)
|
|
185
|
+
|
|
186
|
+
return byteOffset + treeOffset
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
class ReorgBatch extends MerkleTreeBatch {
|
|
191
|
+
constructor (tree) {
|
|
192
|
+
super(tree)
|
|
193
|
+
this.roots = []
|
|
194
|
+
this.length = 0
|
|
195
|
+
this.byteLength = 0
|
|
196
|
+
this.diff = null
|
|
197
|
+
this.ancestors = 0
|
|
198
|
+
// We set upgraded because reorgs are signed so hit will
|
|
199
|
+
// hit the same code paths (like the treeLength check in commit)
|
|
200
|
+
this.upgraded = true
|
|
201
|
+
this.want = {
|
|
202
|
+
nodes: 0,
|
|
203
|
+
start: 0,
|
|
204
|
+
end: 0
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
get finished () {
|
|
209
|
+
return this.want === null
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
update (proof) {
|
|
213
|
+
if (this.want === null) return true
|
|
214
|
+
|
|
215
|
+
const nodes = []
|
|
216
|
+
const root = verifyBlock(proof, this.tree.crypto, nodes)
|
|
217
|
+
|
|
218
|
+
if (root === null || !root.hash.equals(this.diff.hash)) return false
|
|
219
|
+
|
|
220
|
+
this.nodes.push(...nodes)
|
|
221
|
+
return this._update(nodes)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async _update (nodes) {
|
|
225
|
+
const n = new Map()
|
|
226
|
+
for (const node of nodes) n.set(node.index, node)
|
|
227
|
+
|
|
228
|
+
let diff = null
|
|
229
|
+
const ite = flat.iterator(this.diff.index)
|
|
230
|
+
|
|
231
|
+
while ((ite.index & 1) !== 0) {
|
|
232
|
+
const left = n.get(ite.leftChild())
|
|
233
|
+
if (!left) break
|
|
234
|
+
|
|
235
|
+
const existing = await this.tree.get(left.index, false)
|
|
236
|
+
if (!existing || !existing.hash.equals(left.hash)) {
|
|
237
|
+
diff = left
|
|
238
|
+
} else {
|
|
239
|
+
diff = n.get(ite.sibling())
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if ((this.diff.index & 1) === 0) return true
|
|
244
|
+
if (diff === null) return false
|
|
245
|
+
|
|
246
|
+
return this._updateDiffRoot(diff)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
_updateDiffRoot (diff) {
|
|
250
|
+
const spans = flat.spans(diff.index)
|
|
251
|
+
const start = spans[0] / 2
|
|
252
|
+
const end = Math.min(this.treeLength, spans[1] / 2 + 1)
|
|
253
|
+
const len = end - start
|
|
254
|
+
|
|
255
|
+
if (this.diff !== null && len >= this.want.end - this.want.start) {
|
|
256
|
+
return false
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
this.ancestors = start
|
|
260
|
+
this.diff = diff
|
|
261
|
+
|
|
262
|
+
if ((diff.index & 1) === 0 || this.want.start >= this.treeLength || len <= 0) {
|
|
263
|
+
this.want = null
|
|
264
|
+
return true
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
this.want.start = start
|
|
268
|
+
this.want.end = end
|
|
269
|
+
this.want.nodes = log2(spans[1] - spans[0] + 2) - 1
|
|
270
|
+
|
|
271
|
+
return false
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
class ByteSeeker {
|
|
276
|
+
constructor (tree, bytes, padding = 0) {
|
|
277
|
+
this.tree = tree
|
|
278
|
+
this.bytes = bytes
|
|
279
|
+
this.padding = padding
|
|
280
|
+
|
|
281
|
+
const size = tree.byteLength - (tree.length * padding)
|
|
282
|
+
|
|
283
|
+
this.start = bytes >= size ? tree.length : 0
|
|
284
|
+
this.end = bytes < size ? tree.length : 0
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
nodes () {
|
|
288
|
+
return this.tree.nodes(this.start * 2)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async _seek (bytes) {
|
|
292
|
+
if (!bytes) return [0, 0]
|
|
293
|
+
|
|
294
|
+
for (const node of this.tree.roots) { // all async ticks happen once we find the root so safe
|
|
295
|
+
let size = node.size
|
|
296
|
+
if (this.padding > 0) size -= this.padding * flat.countLeaves(node.index)
|
|
297
|
+
|
|
298
|
+
if (bytes === size) return [flat.rightSpan(node.index) + 2, 0]
|
|
299
|
+
if (bytes > size) {
|
|
300
|
+
bytes -= size
|
|
301
|
+
continue
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const ite = flat.iterator(node.index)
|
|
305
|
+
|
|
306
|
+
while ((ite.index & 1) !== 0) {
|
|
307
|
+
const l = await this.tree.get(ite.leftChild(), false)
|
|
308
|
+
if (l) {
|
|
309
|
+
let size = l.size
|
|
310
|
+
if (this.padding > 0) size -= this.padding * ite.countLeaves()
|
|
311
|
+
|
|
312
|
+
if (size === bytes) return [ite.rightSpan() + 2, 0]
|
|
313
|
+
if (size > bytes) continue
|
|
314
|
+
bytes -= size
|
|
315
|
+
ite.sibling()
|
|
316
|
+
} else {
|
|
317
|
+
ite.parent()
|
|
318
|
+
return [ite.index, bytes]
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return [ite.index, bytes]
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return null
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async update () { // TODO: combine _seek and this, much simpler
|
|
329
|
+
const res = await this._seek(this.bytes)
|
|
330
|
+
if (!res) return null
|
|
331
|
+
if ((res[0] & 1) === 0) return [res[0] / 2, res[1]]
|
|
332
|
+
|
|
333
|
+
const span = flat.spans(res[0])
|
|
334
|
+
this.start = span[0] / 2
|
|
335
|
+
this.end = span[1] / 2 + 1
|
|
336
|
+
|
|
337
|
+
return null
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
module.exports = class MerkleTree {
|
|
342
|
+
constructor (storage, roots, fork, signature) {
|
|
343
|
+
this.crypto = crypto
|
|
344
|
+
this.fork = fork
|
|
345
|
+
this.roots = roots
|
|
346
|
+
this.length = roots.length ? totalSpan(roots) / 2 : 0
|
|
347
|
+
this.byteLength = totalSize(roots)
|
|
348
|
+
this.signature = signature
|
|
349
|
+
|
|
350
|
+
this.storage = storage
|
|
351
|
+
this.unflushed = new Map()
|
|
352
|
+
this.flushing = null
|
|
353
|
+
this.truncated = false
|
|
354
|
+
this.truncateTo = 0
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
addNode (node) {
|
|
358
|
+
if (node.size === 0 && node.hash.equals(BLANK_HASH)) node = blankNode(node.index)
|
|
359
|
+
this.unflushed.set(node.index, node)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
batch () {
|
|
363
|
+
return new MerkleTreeBatch(this)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
seek (bytes, padding) {
|
|
367
|
+
return new ByteSeeker(this, bytes, padding)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
hash () {
|
|
371
|
+
return this.crypto.tree(this.roots)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
signable (hash = this.hash()) {
|
|
375
|
+
return signable(hash, this.length, this.fork)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
signedBy (key) {
|
|
379
|
+
return this.signature !== null && this.crypto.verify(this.signable(), this.signature, key)
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
get (index, error = true) {
|
|
383
|
+
let node = this.unflushed.get(index)
|
|
384
|
+
|
|
385
|
+
if (this.flushing !== null && node === undefined) {
|
|
386
|
+
node = this.flushing.get(index)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// TODO: test this
|
|
390
|
+
if (this.truncated && node !== undefined && node.index >= 2 * this.truncateTo) {
|
|
391
|
+
node = blankNode(index)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (node !== undefined) {
|
|
395
|
+
if (node.hash === BLANK_HASH) {
|
|
396
|
+
if (error) throw new Error('Could not load node: ' + index)
|
|
397
|
+
return Promise.resolve(null)
|
|
398
|
+
}
|
|
399
|
+
return Promise.resolve(node)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return getStoredNode(this.storage, index, error)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async flush () {
|
|
406
|
+
this.flushing = this.unflushed
|
|
407
|
+
this.unflushed = new Map()
|
|
408
|
+
|
|
409
|
+
try {
|
|
410
|
+
if (this.truncated) await this._flushTruncation()
|
|
411
|
+
await this._flushNodes()
|
|
412
|
+
} catch (err) {
|
|
413
|
+
for (const node of this.flushing.values()) {
|
|
414
|
+
if (!this.unflushed.has(node.index)) this.unflushed.set(node.index, node)
|
|
415
|
+
}
|
|
416
|
+
throw err
|
|
417
|
+
} finally {
|
|
418
|
+
this.flushing = null
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
_flushTruncation () {
|
|
423
|
+
return new Promise((resolve, reject) => {
|
|
424
|
+
const t = this.truncateTo
|
|
425
|
+
const offset = t === 0 ? 0 : (t - 1) * 80 + 40
|
|
426
|
+
|
|
427
|
+
this.storage.del(offset, Infinity, (err) => {
|
|
428
|
+
if (err) return reject(err)
|
|
429
|
+
|
|
430
|
+
if (this.truncateTo === t) {
|
|
431
|
+
this.truncateTo = 0
|
|
432
|
+
this.truncated = false
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
resolve()
|
|
436
|
+
})
|
|
437
|
+
})
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
_flushNodes () {
|
|
441
|
+
// TODO: write neighbors together etc etc
|
|
442
|
+
// TODO: bench loading a full disk page and copy to that instead
|
|
443
|
+
return new Promise((resolve, reject) => {
|
|
444
|
+
const slab = b4a.allocUnsafe(40 * this.flushing.size)
|
|
445
|
+
|
|
446
|
+
let error = null
|
|
447
|
+
let missing = this.flushing.size + 1
|
|
448
|
+
let offset = 0
|
|
449
|
+
|
|
450
|
+
for (const node of this.flushing.values()) {
|
|
451
|
+
const state = {
|
|
452
|
+
start: 0,
|
|
453
|
+
end: 40,
|
|
454
|
+
buffer: slab.subarray(offset, offset += 40)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
c.uint64.encode(state, node.size)
|
|
458
|
+
c.raw.encode(state, node.hash)
|
|
459
|
+
|
|
460
|
+
this.storage.write(node.index * 40, state.buffer, done)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
done(null)
|
|
464
|
+
|
|
465
|
+
function done (err) {
|
|
466
|
+
if (err) error = err
|
|
467
|
+
if (--missing > 0) return
|
|
468
|
+
if (error) reject(error)
|
|
469
|
+
else resolve()
|
|
470
|
+
}
|
|
471
|
+
})
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
clear () {
|
|
475
|
+
this.truncated = true
|
|
476
|
+
this.truncateTo = 0
|
|
477
|
+
this.roots = []
|
|
478
|
+
this.length = 0
|
|
479
|
+
this.byteLength = 0
|
|
480
|
+
this.fork = 0
|
|
481
|
+
this.signature = null
|
|
482
|
+
if (this.flushing !== null) this.flushing.clear()
|
|
483
|
+
this.unflushed.clear()
|
|
484
|
+
return this.flush()
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
close () {
|
|
488
|
+
return new Promise((resolve, reject) => {
|
|
489
|
+
this.storage.close(err => {
|
|
490
|
+
if (err) reject(err)
|
|
491
|
+
else resolve()
|
|
492
|
+
})
|
|
493
|
+
})
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async truncate (length, fork = this.fork) {
|
|
497
|
+
const head = length * 2
|
|
498
|
+
const batch = new MerkleTreeBatch(this)
|
|
499
|
+
const fullRoots = flat.fullRoots(head)
|
|
500
|
+
|
|
501
|
+
for (let i = 0; i < fullRoots.length; i++) {
|
|
502
|
+
const root = fullRoots[i]
|
|
503
|
+
if (i < batch.roots.length && batch.roots[i].index === root) continue
|
|
504
|
+
|
|
505
|
+
while (batch.roots.length > i) batch.roots.pop()
|
|
506
|
+
batch.roots.push(await this.get(root))
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
while (batch.roots.length > fullRoots.length) {
|
|
510
|
+
batch.roots.pop()
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
batch.fork = fork
|
|
514
|
+
batch.length = length
|
|
515
|
+
batch.ancestors = length
|
|
516
|
+
batch.byteLength = totalSize(batch.roots)
|
|
517
|
+
batch.upgraded = true
|
|
518
|
+
|
|
519
|
+
return batch
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
async reorg (proof) {
|
|
523
|
+
const batch = new ReorgBatch(this)
|
|
524
|
+
|
|
525
|
+
let unverified = null
|
|
526
|
+
|
|
527
|
+
if (proof.block || proof.seek) {
|
|
528
|
+
unverified = verifyBlock(proof, this.crypto, batch.nodes)
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (!verifyUpgrade(proof, unverified, batch)) {
|
|
532
|
+
throw new Error('Fork proof not verifiable')
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
for (const root of batch.roots) {
|
|
536
|
+
const existing = await this.get(root.index, false)
|
|
537
|
+
if (existing && existing.hash.equals(root.hash)) continue
|
|
538
|
+
batch._updateDiffRoot(root)
|
|
539
|
+
break
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (batch.diff !== null) {
|
|
543
|
+
await batch._update(batch.nodes)
|
|
544
|
+
} else {
|
|
545
|
+
batch.want = null
|
|
546
|
+
batch.ancestors = batch.length
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return batch
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
async verify (proof) {
|
|
553
|
+
const batch = new MerkleTreeBatch(this)
|
|
554
|
+
|
|
555
|
+
let unverified = verifyBlock(proof, this.crypto, batch.nodes)
|
|
556
|
+
|
|
557
|
+
if (proof.upgrade) {
|
|
558
|
+
if (verifyUpgrade(proof, unverified, batch)) {
|
|
559
|
+
unverified = null
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (unverified) {
|
|
564
|
+
const verified = await this.get(unverified.index)
|
|
565
|
+
if (!verified.hash.equals(unverified.hash)) {
|
|
566
|
+
throw new Error('Invalid checksum at node ' + unverified.index)
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
return batch
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
async proof ({ block, seek, upgrade }) {
|
|
574
|
+
// Important that this does not throw inbetween making the promise arrays
|
|
575
|
+
// and finalise being called, otherwise there will be lingering promises in the background
|
|
576
|
+
|
|
577
|
+
const signature = this.signature
|
|
578
|
+
const fork = this.fork
|
|
579
|
+
const head = 2 * this.length
|
|
580
|
+
const from = upgrade ? upgrade.start * 2 : 0
|
|
581
|
+
const to = upgrade ? from + upgrade.length * 2 : head
|
|
582
|
+
|
|
583
|
+
if (from >= to || to > head) {
|
|
584
|
+
throw new Error('Invalid upgrade')
|
|
585
|
+
}
|
|
586
|
+
if (seek && block && upgrade && block.index * 2 >= from) {
|
|
587
|
+
throw new Error('Cannot both do a seek and block request when upgrading')
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
let subTree = head
|
|
591
|
+
|
|
592
|
+
const p = {
|
|
593
|
+
block: null,
|
|
594
|
+
seek: null,
|
|
595
|
+
upgrade: null,
|
|
596
|
+
additionalUpgrade: null
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (block && (!upgrade || block.index < upgrade.start)) {
|
|
600
|
+
subTree = nodesToRoot(2 * block.index, block.nodes, to)
|
|
601
|
+
const seekRoot = seek ? await seekUntrustedTree(this, subTree, seek.bytes) : head
|
|
602
|
+
blockAndSeekProof(this, block, seek, seekRoot, subTree, p)
|
|
603
|
+
} else if ((block || seek) && upgrade) {
|
|
604
|
+
subTree = seek ? await seekFromHead(this, to, seek.bytes) : 2 * block.index
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (upgrade) {
|
|
608
|
+
upgradeProof(this, block, seek, from, to, subTree, p)
|
|
609
|
+
if (head > to) additionalUpgradeProof(this, to, head, p)
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
try {
|
|
613
|
+
const result = { fork, block: null, seek: null, upgrade: null }
|
|
614
|
+
|
|
615
|
+
if (block) {
|
|
616
|
+
const nodes = await Promise.all(p.block)
|
|
617
|
+
|
|
618
|
+
result.block = {
|
|
619
|
+
index: block.index,
|
|
620
|
+
value: null,
|
|
621
|
+
nodes
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (seek && p.seek !== null) {
|
|
625
|
+
const nodes = await Promise.all(p.seek)
|
|
626
|
+
|
|
627
|
+
result.seek = {
|
|
628
|
+
bytes: seek.bytes,
|
|
629
|
+
nodes
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
if (upgrade) {
|
|
633
|
+
const nodes = await Promise.all(p.upgrade)
|
|
634
|
+
const additionalNodes = await Promise.all(p.additionalUpgrade || [])
|
|
635
|
+
|
|
636
|
+
result.upgrade = {
|
|
637
|
+
start: upgrade.start,
|
|
638
|
+
length: upgrade.length,
|
|
639
|
+
nodes,
|
|
640
|
+
additionalNodes,
|
|
641
|
+
signature
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return result
|
|
646
|
+
} catch (err) {
|
|
647
|
+
// Make sure we await all pending p so don't have background async state...
|
|
648
|
+
if (p.seek !== null) await Promise.allSettled(p.seek)
|
|
649
|
+
if (p.block !== null) await Promise.allSettled(p.block)
|
|
650
|
+
if (p.upgrade !== null) await Promise.allSettled(p.upgrade)
|
|
651
|
+
if (p.additionalUpgrade !== null) await Promise.allSettled(p.additionalUpgrade)
|
|
652
|
+
throw err
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
async nodes (index) {
|
|
657
|
+
const head = 2 * this.length
|
|
658
|
+
const ite = flat.iterator(index)
|
|
659
|
+
|
|
660
|
+
let cnt = 0
|
|
661
|
+
while (!ite.contains(head) && (await this.get(ite.index, false)) === null) {
|
|
662
|
+
cnt++
|
|
663
|
+
ite.parent()
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
return cnt
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
async byteRange (index) {
|
|
670
|
+
const head = 2 * this.length
|
|
671
|
+
if (((index & 1) === 0 ? index : flat.rightSpan(index)) >= head) {
|
|
672
|
+
throw new Error('Index is out of bounds')
|
|
673
|
+
}
|
|
674
|
+
return [await this.byteOffset(index), (await this.get(index)).size]
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
async byteOffset (index) {
|
|
678
|
+
if ((index & 1) === 1) index = flat.leftSpan(index)
|
|
679
|
+
|
|
680
|
+
let head = 0
|
|
681
|
+
let offset = 0
|
|
682
|
+
|
|
683
|
+
for (const node of this.roots) { // all async ticks happen once we find the root so safe
|
|
684
|
+
head += 2 * ((node.index - head) + 1)
|
|
685
|
+
|
|
686
|
+
if (index >= head) {
|
|
687
|
+
offset += node.size
|
|
688
|
+
continue
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const ite = flat.iterator(node.index)
|
|
692
|
+
|
|
693
|
+
while (ite.index !== index) {
|
|
694
|
+
if (index < ite.index) {
|
|
695
|
+
ite.leftChild()
|
|
696
|
+
} else {
|
|
697
|
+
offset += (await this.get(ite.leftChild())).size
|
|
698
|
+
ite.sibling()
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
return offset
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
static async open (storage, opts = {}) {
|
|
707
|
+
await new Promise((resolve, reject) => {
|
|
708
|
+
storage.read(0, OLD_TREE.length, (err, buf) => {
|
|
709
|
+
if (err) return resolve()
|
|
710
|
+
if (buf.equals(OLD_TREE)) return reject(new Error('Storage contains an incompatible merkle tree'))
|
|
711
|
+
resolve()
|
|
712
|
+
})
|
|
713
|
+
})
|
|
714
|
+
|
|
715
|
+
const length = typeof opts.length === 'number'
|
|
716
|
+
? opts.length
|
|
717
|
+
: await autoLength(storage)
|
|
718
|
+
|
|
719
|
+
const roots = []
|
|
720
|
+
for (const index of flat.fullRoots(2 * length)) {
|
|
721
|
+
roots.push(await getStoredNode(storage, index, true))
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return new MerkleTree(storage, roots, opts.fork || 0, opts.signature || null)
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// All the methods needed for proof verification
|
|
729
|
+
|
|
730
|
+
function verifyBlock ({ block, seek }, crypto, nodes) {
|
|
731
|
+
if (!block && (!seek || !seek.nodes.length)) return null
|
|
732
|
+
|
|
733
|
+
let root = null
|
|
734
|
+
|
|
735
|
+
if (seek && seek.nodes.length) {
|
|
736
|
+
const ite = flat.iterator(seek.nodes[0].index)
|
|
737
|
+
const q = new NodeQueue(seek.nodes)
|
|
738
|
+
|
|
739
|
+
root = q.shift(ite.index)
|
|
740
|
+
nodes.push(root)
|
|
741
|
+
|
|
742
|
+
while (q.length > 0) {
|
|
743
|
+
const node = q.shift(ite.sibling())
|
|
744
|
+
|
|
745
|
+
root = parentNode(crypto, ite.parent(), root, node)
|
|
746
|
+
nodes.push(node)
|
|
747
|
+
nodes.push(root)
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (!block) return root
|
|
752
|
+
|
|
753
|
+
const ite = flat.iterator(2 * block.index)
|
|
754
|
+
const blockHash = block.value && blockNode(crypto, ite.index, block.value)
|
|
755
|
+
|
|
756
|
+
const q = new NodeQueue(block.nodes, root)
|
|
757
|
+
|
|
758
|
+
root = blockHash || q.shift(ite.index)
|
|
759
|
+
nodes.push(root)
|
|
760
|
+
|
|
761
|
+
while (q.length > 0) {
|
|
762
|
+
const node = q.shift(ite.sibling())
|
|
763
|
+
|
|
764
|
+
root = parentNode(crypto, ite.parent(), root, node)
|
|
765
|
+
nodes.push(node)
|
|
766
|
+
nodes.push(root)
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
return root
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
function verifyUpgrade ({ fork, upgrade }, blockRoot, batch) {
|
|
773
|
+
const q = new NodeQueue(upgrade.nodes, blockRoot)
|
|
774
|
+
|
|
775
|
+
let grow = batch.roots.length > 0
|
|
776
|
+
let i = 0
|
|
777
|
+
|
|
778
|
+
const to = 2 * (upgrade.start + upgrade.length)
|
|
779
|
+
const ite = flat.iterator(0)
|
|
780
|
+
|
|
781
|
+
for (; ite.fullRoot(to); ite.nextTree()) {
|
|
782
|
+
if (i < batch.roots.length && batch.roots[i].index === ite.index) {
|
|
783
|
+
i++
|
|
784
|
+
continue
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (grow) {
|
|
788
|
+
grow = false
|
|
789
|
+
const root = ite.index
|
|
790
|
+
if (i < batch.roots.length) {
|
|
791
|
+
ite.seek(batch.roots[batch.roots.length - 1].index)
|
|
792
|
+
while (ite.index !== root) {
|
|
793
|
+
batch.appendRoot(q.shift(ite.sibling()), ite)
|
|
794
|
+
}
|
|
795
|
+
continue
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
batch.appendRoot(q.shift(ite.index), ite)
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const extra = upgrade.additionalNodes
|
|
803
|
+
|
|
804
|
+
ite.seek(batch.roots[batch.roots.length - 1].index)
|
|
805
|
+
i = 0
|
|
806
|
+
|
|
807
|
+
while (i < extra.length && extra[i].index === ite.sibling()) {
|
|
808
|
+
batch.appendRoot(extra[i++], ite)
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
while (i < extra.length) {
|
|
812
|
+
const node = extra[i++]
|
|
813
|
+
|
|
814
|
+
while (node.index !== ite.index) {
|
|
815
|
+
if (ite.factor === 2) throw new Error('Unexpected node: ' + node.index)
|
|
816
|
+
ite.leftChild()
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
batch.appendRoot(node, ite)
|
|
820
|
+
ite.sibling()
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
batch.signature = upgrade.signature
|
|
824
|
+
batch.fork = fork
|
|
825
|
+
|
|
826
|
+
return q.extra === null
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
async function seekFromHead (tree, head, bytes) {
|
|
830
|
+
const roots = flat.fullRoots(head)
|
|
831
|
+
|
|
832
|
+
for (let i = 0; i < roots.length; i++) {
|
|
833
|
+
const root = roots[i]
|
|
834
|
+
const node = await tree.get(root)
|
|
835
|
+
|
|
836
|
+
if (bytes === node.size) return root
|
|
837
|
+
if (bytes > node.size) {
|
|
838
|
+
bytes -= node.size
|
|
839
|
+
continue
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
return seekTrustedTree(tree, root, bytes)
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
return head
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// trust that bytes are within the root tree and find the block at bytes
|
|
849
|
+
|
|
850
|
+
async function seekTrustedTree (tree, root, bytes) {
|
|
851
|
+
if (!bytes) return root
|
|
852
|
+
|
|
853
|
+
const ite = flat.iterator(root)
|
|
854
|
+
|
|
855
|
+
while ((ite.index & 1) !== 0) {
|
|
856
|
+
const l = await tree.get(ite.leftChild(), false)
|
|
857
|
+
if (l) {
|
|
858
|
+
if (l.size === bytes) return ite.index
|
|
859
|
+
if (l.size > bytes) continue
|
|
860
|
+
bytes -= l.size
|
|
861
|
+
ite.sibling()
|
|
862
|
+
} else {
|
|
863
|
+
ite.parent()
|
|
864
|
+
return ite.index
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
return ite.index
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// try to find the block at bytes without trusting that is *is* within the root passed
|
|
872
|
+
|
|
873
|
+
async function seekUntrustedTree (tree, root, bytes) {
|
|
874
|
+
const offset = await tree.byteOffset(root)
|
|
875
|
+
|
|
876
|
+
if (offset > bytes) throw new Error('Invalid seek')
|
|
877
|
+
if (offset === bytes) return root
|
|
878
|
+
|
|
879
|
+
bytes -= offset
|
|
880
|
+
|
|
881
|
+
const node = await tree.get(root)
|
|
882
|
+
|
|
883
|
+
if (node.size <= bytes) throw new Error('Invalid seek')
|
|
884
|
+
|
|
885
|
+
return seekTrustedTree(tree, root, bytes)
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Below is proof production, ie, construct proofs to verify a request
|
|
889
|
+
// Note, that all these methods are sync as we can statically infer which nodes
|
|
890
|
+
// are needed for the remote to verify given they arguments they passed us
|
|
891
|
+
|
|
892
|
+
function seekProof (tree, seekRoot, root, p) {
|
|
893
|
+
const ite = flat.iterator(seekRoot)
|
|
894
|
+
|
|
895
|
+
p.seek = []
|
|
896
|
+
p.seek.push(tree.get(ite.index))
|
|
897
|
+
|
|
898
|
+
while (ite.index !== root) {
|
|
899
|
+
ite.sibling()
|
|
900
|
+
p.seek.push(tree.get(ite.index))
|
|
901
|
+
ite.parent()
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function blockAndSeekProof (tree, block, seek, seekRoot, root, p) {
|
|
906
|
+
if (!block) return seekProof(tree, seekRoot, root, p)
|
|
907
|
+
|
|
908
|
+
const ite = flat.iterator(2 * block.index)
|
|
909
|
+
|
|
910
|
+
p.block = []
|
|
911
|
+
if (!block.value) p.block.push(tree.get(ite.index))
|
|
912
|
+
|
|
913
|
+
while (ite.index !== root) {
|
|
914
|
+
ite.sibling()
|
|
915
|
+
|
|
916
|
+
if (seek && ite.contains(seekRoot) && ite.index !== seekRoot) {
|
|
917
|
+
seekProof(tree, seekRoot, ite.index, p)
|
|
918
|
+
} else {
|
|
919
|
+
p.block.push(tree.get(ite.index))
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
ite.parent()
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function upgradeProof (tree, block, seek, from, to, subTree, p) {
|
|
927
|
+
if (from === 0) p.upgrade = []
|
|
928
|
+
|
|
929
|
+
for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) {
|
|
930
|
+
// check if they already have the node
|
|
931
|
+
if (ite.index + ite.factor / 2 < from) continue
|
|
932
|
+
|
|
933
|
+
// connect existing tree
|
|
934
|
+
if (p.upgrade === null && ite.contains(from - 2)) {
|
|
935
|
+
p.upgrade = []
|
|
936
|
+
|
|
937
|
+
const root = ite.index
|
|
938
|
+
const target = from - 2
|
|
939
|
+
|
|
940
|
+
ite.seek(target)
|
|
941
|
+
|
|
942
|
+
while (ite.index !== root) {
|
|
943
|
+
ite.sibling()
|
|
944
|
+
if (ite.index > target) {
|
|
945
|
+
if (p.block === null && p.seek === null && ite.contains(subTree)) {
|
|
946
|
+
blockAndSeekProof(tree, block, seek, subTree, ite.index, p)
|
|
947
|
+
} else {
|
|
948
|
+
p.upgrade.push(tree.get(ite.index))
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
ite.parent()
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
continue
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
if (p.upgrade === null) {
|
|
958
|
+
p.upgrade = []
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// if the subtree included is a child of this tree, include that one
|
|
962
|
+
// instead of a dup node
|
|
963
|
+
if (p.block === null && p.seek === null && ite.contains(subTree)) {
|
|
964
|
+
blockAndSeekProof(tree, block, seek, subTree, ite.index, p)
|
|
965
|
+
continue
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// add root (can be optimised since the root might be in tree.roots)
|
|
969
|
+
p.upgrade.push(tree.get(ite.index))
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
function additionalUpgradeProof (tree, from, to, p) {
|
|
974
|
+
if (from === 0) p.additionalUpgrade = []
|
|
975
|
+
|
|
976
|
+
for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) {
|
|
977
|
+
// check if they already have the node
|
|
978
|
+
if (ite.index + ite.factor / 2 < from) continue
|
|
979
|
+
|
|
980
|
+
// connect existing tree
|
|
981
|
+
if (p.additionalUpgrade === null && ite.contains(from - 2)) {
|
|
982
|
+
p.additionalUpgrade = []
|
|
983
|
+
|
|
984
|
+
const root = ite.index
|
|
985
|
+
const target = from - 2
|
|
986
|
+
|
|
987
|
+
ite.seek(target)
|
|
988
|
+
|
|
989
|
+
while (ite.index !== root) {
|
|
990
|
+
ite.sibling()
|
|
991
|
+
if (ite.index > target) {
|
|
992
|
+
p.additionalUpgrade.push(tree.get(ite.index))
|
|
993
|
+
}
|
|
994
|
+
ite.parent()
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
continue
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
if (p.additionalUpgrade === null) {
|
|
1001
|
+
p.additionalUpgrade = []
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// add root (can be optimised since the root is in tree.roots)
|
|
1005
|
+
p.additionalUpgrade.push(tree.get(ite.index))
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
function nodesToRoot (index, nodes, head) {
|
|
1010
|
+
const ite = flat.iterator(index)
|
|
1011
|
+
|
|
1012
|
+
for (let i = 0; i < nodes; i++) {
|
|
1013
|
+
ite.parent()
|
|
1014
|
+
if (ite.contains(head)) throw new Error('Nodes is out of bounds')
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
return ite.index
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
function totalSize (nodes) {
|
|
1021
|
+
let s = 0
|
|
1022
|
+
for (const node of nodes) s += node.size
|
|
1023
|
+
return s
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
function totalSpan (nodes) {
|
|
1027
|
+
let s = 0
|
|
1028
|
+
for (const node of nodes) s += 2 * ((node.index - s) + 1)
|
|
1029
|
+
return s
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
function blockNode (crypto, index, value) {
|
|
1033
|
+
return { index, size: value.byteLength, hash: crypto.data(value) }
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
function parentNode (crypto, index, a, b) {
|
|
1037
|
+
return { index, size: a.size + b.size, hash: crypto.parent(a, b) }
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
function blankNode (index) {
|
|
1041
|
+
return { index, size: 0, hash: BLANK_HASH }
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
// Storage methods
|
|
1045
|
+
|
|
1046
|
+
function getStoredNode (storage, index, error) {
|
|
1047
|
+
return new Promise((resolve, reject) => {
|
|
1048
|
+
storage.read(40 * index, 40, (err, data) => {
|
|
1049
|
+
if (err) {
|
|
1050
|
+
if (error) return reject(err)
|
|
1051
|
+
else resolve(null)
|
|
1052
|
+
return
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
const hash = data.subarray(8)
|
|
1056
|
+
const size = c.decode(c.uint64, data)
|
|
1057
|
+
|
|
1058
|
+
if (size === 0 && b4a.compare(hash, BLANK_HASH) === 0) {
|
|
1059
|
+
if (error) reject(new Error('Could not load node: ' + index))
|
|
1060
|
+
else resolve(null)
|
|
1061
|
+
return
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
resolve({ index, size, hash })
|
|
1065
|
+
})
|
|
1066
|
+
})
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
function storedNodes (storage) {
|
|
1070
|
+
return new Promise((resolve) => {
|
|
1071
|
+
storage.stat((_, st) => {
|
|
1072
|
+
if (!st) return resolve(0)
|
|
1073
|
+
resolve((st.size - (st.size % 40)) / 40)
|
|
1074
|
+
})
|
|
1075
|
+
})
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
async function autoLength (storage) {
|
|
1079
|
+
const nodes = await storedNodes(storage)
|
|
1080
|
+
if (!nodes) return 0
|
|
1081
|
+
const ite = flat.iterator(nodes - 1)
|
|
1082
|
+
let index = nodes - 1
|
|
1083
|
+
while (await getStoredNode(storage, ite.parent(), false)) index = ite.index
|
|
1084
|
+
return flat.rightSpan(index) / 2 + 1
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
function truncateMap (map, len) {
|
|
1088
|
+
for (const node of map.values()) {
|
|
1089
|
+
if (node.index >= 2 * len) map.delete(node.index)
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
function log2 (n) {
|
|
1094
|
+
let res = 1
|
|
1095
|
+
|
|
1096
|
+
while (n > 2) {
|
|
1097
|
+
n /= 2
|
|
1098
|
+
res++
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
return res
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
function signable (hash, length, fork) {
|
|
1105
|
+
const state = { start: 0, end: 48, buffer: b4a.alloc(48) }
|
|
1106
|
+
c.raw.encode(state, hash)
|
|
1107
|
+
c.uint64.encode(state, length)
|
|
1108
|
+
c.uint64.encode(state, fork)
|
|
1109
|
+
return state.buffer
|
|
1110
|
+
}
|