hypercore 10.38.2 → 11.0.1
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/README.md +13 -30
- package/index.js +388 -444
- package/lib/audit.js +33 -41
- package/lib/bit-interlude.js +174 -0
- package/lib/bitfield.js +79 -87
- package/lib/block-store.js +12 -50
- package/lib/copy-prologue.js +236 -0
- package/lib/core.js +414 -746
- package/lib/download.js +42 -4
- package/lib/merkle-tree.js +263 -406
- package/lib/multisig.js +9 -6
- package/lib/mutex.js +4 -0
- package/lib/remote-bitfield.js +9 -9
- package/lib/replicator.js +247 -177
- package/lib/session-state.js +949 -0
- package/lib/verifier.js +20 -13
- package/package.json +2 -2
- package/lib/batch.js +0 -431
- package/lib/big-header.js +0 -55
- package/lib/oplog.js +0 -228
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
const crypto = require('hypercore-crypto')
|
|
2
|
+
const flat = require('flat-tree')
|
|
3
|
+
const b4a = require('b4a')
|
|
4
|
+
const quickbit = require('quickbit-universal')
|
|
5
|
+
const Bitfield = require('./bitfield')
|
|
6
|
+
|
|
7
|
+
const MAX_BATCH_USED = 4 * 1024 * 1024
|
|
8
|
+
const MIN_BATCH_USED = 512 * 1024
|
|
9
|
+
|
|
10
|
+
// just in its own file as its a bit involved
|
|
11
|
+
|
|
12
|
+
module.exports = copyPrologue
|
|
13
|
+
|
|
14
|
+
async function copyPrologue (src, dst) {
|
|
15
|
+
const prologue = dst.header.manifest.prologue
|
|
16
|
+
|
|
17
|
+
if (src.tree.length < prologue.length || prologue.length === 0) return
|
|
18
|
+
|
|
19
|
+
const stack = []
|
|
20
|
+
const roots = flat.fullRoots(prologue.length * 2)
|
|
21
|
+
const batch = { roots, first: true, last: false, contig: 0, used: 0, tree: [], blocks: [] }
|
|
22
|
+
|
|
23
|
+
for (let i = 0; i < roots.length; i++) {
|
|
24
|
+
const node = roots[i]
|
|
25
|
+
batch.tree.push(node)
|
|
26
|
+
stack.push(node)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let lastPage = -1
|
|
30
|
+
let lastBlock = -1
|
|
31
|
+
|
|
32
|
+
for await (const data of src.storage.createBlockStream({ gte: 0, lt: prologue.length, reverse: true })) {
|
|
33
|
+
if (walkTree(stack, data.index * 2, batch) === false) {
|
|
34
|
+
throw new Error('Missing block or tree node for ' + data.index)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
batch.contig = data.index + 1 === lastBlock ? batch.contig + 1 : 1
|
|
38
|
+
lastBlock = data.index
|
|
39
|
+
|
|
40
|
+
const page = getBitfieldPage(data.index)
|
|
41
|
+
batch.blocks.push(data)
|
|
42
|
+
|
|
43
|
+
if (lastPage !== page) batch.used += 4096
|
|
44
|
+
batch.used += Math.max(data.value.byteLength, 128) // 128 is just a sanity number to avoid mega batches
|
|
45
|
+
|
|
46
|
+
// always safe to partially flush so we do that ondemand to reduce memory usage...
|
|
47
|
+
if ((batch.used >= MIN_BATCH_USED && page !== lastPage) || (batch.used >= MAX_BATCH_USED)) {
|
|
48
|
+
await flushBatch(prologue, src, dst, batch)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
lastPage = page
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (lastBlock !== 0) batch.contig = 0
|
|
55
|
+
|
|
56
|
+
batch.last = true
|
|
57
|
+
await flushBatch(prologue, src, dst, batch)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function flushBatch (prologue, src, dst, batch) {
|
|
61
|
+
const nodePromises = []
|
|
62
|
+
|
|
63
|
+
const srcReader = src.storage.read()
|
|
64
|
+
for (const index of batch.tree) {
|
|
65
|
+
nodePromises.push(srcReader.getTreeNode(index))
|
|
66
|
+
}
|
|
67
|
+
srcReader.tryFlush()
|
|
68
|
+
|
|
69
|
+
const nodes = await Promise.all(nodePromises)
|
|
70
|
+
|
|
71
|
+
const pagePromises = []
|
|
72
|
+
const dstReader = dst.storage.read()
|
|
73
|
+
|
|
74
|
+
const headPromise = batch.first ? dstReader.getHead() : null
|
|
75
|
+
if (headPromise) headPromise.catch(noop)
|
|
76
|
+
|
|
77
|
+
let lastPage = -1
|
|
78
|
+
for (const { index } of batch.blocks) {
|
|
79
|
+
const page = getBitfieldPage(index)
|
|
80
|
+
if (page === lastPage) continue
|
|
81
|
+
lastPage = page
|
|
82
|
+
pagePromises.push(dstReader.getBitfieldPage(page))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
dstReader.tryFlush()
|
|
86
|
+
|
|
87
|
+
const pages = await Promise.all(pagePromises)
|
|
88
|
+
const head = headPromise === null ? null : await headPromise
|
|
89
|
+
const userData = []
|
|
90
|
+
|
|
91
|
+
// reads done!
|
|
92
|
+
|
|
93
|
+
if (batch.first) {
|
|
94
|
+
const treeHash = crypto.tree(nodes.slice(0, batch.roots.length))
|
|
95
|
+
if (!b4a.equals(treeHash, prologue.hash)) throw new Error('Prologue does not match source')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (batch.first) {
|
|
99
|
+
for await (const data of src.storage.createUserDataStream()) userData.push(data)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (let i = 0; i < pages.length; i++) {
|
|
103
|
+
if (!pages[i]) pages[i] = b4a.alloc(4096)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const tx = dst.storage.write()
|
|
107
|
+
|
|
108
|
+
for (const node of nodes) tx.putTreeNode(node)
|
|
109
|
+
|
|
110
|
+
lastPage = -1
|
|
111
|
+
let pageIndex = -1
|
|
112
|
+
|
|
113
|
+
for (const { index, value } of batch.blocks) {
|
|
114
|
+
const page = getBitfieldPage(index)
|
|
115
|
+
|
|
116
|
+
if (page !== lastPage) {
|
|
117
|
+
lastPage = page
|
|
118
|
+
pageIndex++
|
|
119
|
+
// queue the page now, we mutate it below but its the same ref
|
|
120
|
+
tx.putBitfieldPage(pageIndex, pages[pageIndex])
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const pageBuffer = pages[pageIndex]
|
|
124
|
+
quickbit.set(pageBuffer, getBitfieldOffset(index), true)
|
|
125
|
+
tx.putBlock(index, value)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const { key, value } of userData) {
|
|
129
|
+
tx.setUserData(key, value)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let upgraded = batch.first && !head
|
|
133
|
+
if (upgraded) {
|
|
134
|
+
tx.setHead(prologueToTree(prologue))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await tx.flush()
|
|
138
|
+
|
|
139
|
+
if (upgraded) {
|
|
140
|
+
const roots = nodes.slice(0, batch.roots.length)
|
|
141
|
+
dst.state.setRoots(roots)
|
|
142
|
+
dst.header.tree = prologueToTree(prologue)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (userData.length > 0) {
|
|
146
|
+
dst.header.userData = userData.concat(dst.header.userData)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (batch.contig) {
|
|
150
|
+
// TODO: we need to persist this somehow
|
|
151
|
+
dst.header.hints.contiguousLength = batch.contig
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let start = 0
|
|
155
|
+
let length = 0
|
|
156
|
+
|
|
157
|
+
// update in memory bitfield
|
|
158
|
+
for (const { index } of batch.blocks) {
|
|
159
|
+
if (start === 0 || start - 1 === index) {
|
|
160
|
+
length++
|
|
161
|
+
} else {
|
|
162
|
+
if (length > 0) signalReplicator(dst, upgraded, start, length)
|
|
163
|
+
upgraded = false
|
|
164
|
+
length = 1
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
start = index
|
|
168
|
+
dst.bitfield.set(index, true)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (length > 0) signalReplicator(dst, upgraded, start, length)
|
|
172
|
+
|
|
173
|
+
// unlink
|
|
174
|
+
batch.tree = []
|
|
175
|
+
batch.blocks = []
|
|
176
|
+
batch.first = false
|
|
177
|
+
batch.used = 0
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function signalReplicator (core, upgraded, start, length) {
|
|
181
|
+
if (upgraded) {
|
|
182
|
+
core.replicator.cork()
|
|
183
|
+
core.replicator.onhave(start, length, false)
|
|
184
|
+
core.replicator.onupgrade()
|
|
185
|
+
core.replicator.uncork()
|
|
186
|
+
} else {
|
|
187
|
+
core.replicator.onhave(start, length, false)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function prologueToTree (prologue) {
|
|
192
|
+
return {
|
|
193
|
+
fork: 0,
|
|
194
|
+
length: prologue.length,
|
|
195
|
+
rootHash: prologue.hash,
|
|
196
|
+
signature: null
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getBitfieldPage (index) {
|
|
201
|
+
return Math.floor(index / Bitfield.BITS_PER_PAGE)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function getBitfieldOffset (index) {
|
|
205
|
+
return index & (Bitfield.BITS_PER_PAGE - 1)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function walkTree (stack, target, batch) {
|
|
209
|
+
while (stack.length > 0) {
|
|
210
|
+
const node = stack.pop()
|
|
211
|
+
|
|
212
|
+
if ((node & 1) === 0) {
|
|
213
|
+
if (node === target) return true
|
|
214
|
+
continue
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const ite = flat.iterator(node)
|
|
218
|
+
if (!ite.contains(target)) continue
|
|
219
|
+
|
|
220
|
+
while ((ite.index & 1) !== 0) {
|
|
221
|
+
const left = ite.leftChild()
|
|
222
|
+
const right = ite.sibling() // is right child
|
|
223
|
+
|
|
224
|
+
batch.tree.push(left, right)
|
|
225
|
+
|
|
226
|
+
if (ite.contains(target)) stack.push(left)
|
|
227
|
+
else ite.sibling()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (ite.index === target) return true
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return false
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function noop () {}
|