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
package/lib/core.js
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
const hypercoreCrypto = require('hypercore-crypto')
|
|
2
|
+
const Oplog = require('./oplog')
|
|
3
|
+
const Mutex = require('./mutex')
|
|
4
|
+
const MerkleTree = require('./merkle-tree')
|
|
5
|
+
const BlockStore = require('./block-store')
|
|
6
|
+
const Bitfield = require('./bitfield')
|
|
7
|
+
const { oplogHeader, oplogEntry } = require('./messages')
|
|
8
|
+
|
|
9
|
+
module.exports = class Core {
|
|
10
|
+
constructor (header, crypto, oplog, tree, blocks, bitfield, sign, onupdate) {
|
|
11
|
+
this.onupdate = onupdate
|
|
12
|
+
this.header = header
|
|
13
|
+
this.crypto = crypto
|
|
14
|
+
this.oplog = oplog
|
|
15
|
+
this.tree = tree
|
|
16
|
+
this.blocks = blocks
|
|
17
|
+
this.bitfield = bitfield
|
|
18
|
+
this.defaultSign = sign
|
|
19
|
+
this.truncating = 0
|
|
20
|
+
|
|
21
|
+
this._maxOplogSize = 65536
|
|
22
|
+
this._autoFlush = 1
|
|
23
|
+
this._verifies = null
|
|
24
|
+
this._verifiesFlushed = null
|
|
25
|
+
this._mutex = new Mutex()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static async open (storage, opts = {}) {
|
|
29
|
+
const oplogFile = storage('oplog')
|
|
30
|
+
const treeFile = storage('tree')
|
|
31
|
+
const bitfieldFile = storage('bitfield')
|
|
32
|
+
const dataFile = storage('data')
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
return await this.resume(oplogFile, treeFile, bitfieldFile, dataFile, opts)
|
|
36
|
+
} catch (err) {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
let missing = 4
|
|
39
|
+
|
|
40
|
+
oplogFile.close(done)
|
|
41
|
+
treeFile.close(done)
|
|
42
|
+
bitfieldFile.close(done)
|
|
43
|
+
dataFile.close(done)
|
|
44
|
+
|
|
45
|
+
function done () {
|
|
46
|
+
if (--missing === 0) reject(err)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// TODO: we should prob have a general "auth" abstraction instead somewhere?
|
|
53
|
+
static createSigner (crypto, { publicKey, secretKey }) {
|
|
54
|
+
if (!crypto.validateKeyPair({ publicKey, secretKey })) throw new Error('Invalid key pair')
|
|
55
|
+
return signable => crypto.sign(signable, secretKey)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static async resume (oplogFile, treeFile, bitfieldFile, dataFile, opts) {
|
|
59
|
+
const overwrite = opts.overwrite === true
|
|
60
|
+
const createIfMissing = opts.createIfMissing !== false
|
|
61
|
+
const crypto = opts.crypto || hypercoreCrypto
|
|
62
|
+
|
|
63
|
+
const oplog = new Oplog(oplogFile, {
|
|
64
|
+
headerEncoding: oplogHeader,
|
|
65
|
+
entryEncoding: oplogEntry
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
let { header, entries } = await oplog.open()
|
|
69
|
+
|
|
70
|
+
if (!header || overwrite === true) {
|
|
71
|
+
if (!createIfMissing) {
|
|
72
|
+
throw new Error('No hypercore is stored here')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
header = {
|
|
76
|
+
types: { tree: 'blake2b', bitfield: 'raw', signer: 'ed25519' },
|
|
77
|
+
userData: [],
|
|
78
|
+
tree: {
|
|
79
|
+
fork: 0,
|
|
80
|
+
length: 0,
|
|
81
|
+
rootHash: null,
|
|
82
|
+
signature: null
|
|
83
|
+
},
|
|
84
|
+
signer: opts.keyPair || crypto.keyPair(),
|
|
85
|
+
hints: {
|
|
86
|
+
reorgs: []
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await oplog.flush(header)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (opts.keyPair && !header.signer.publicKey.equals(opts.keyPair.publicKey)) {
|
|
94
|
+
throw new Error('Another hypercore is stored here')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const tree = await MerkleTree.open(treeFile, { crypto, ...header.tree })
|
|
98
|
+
const bitfield = await Bitfield.open(bitfieldFile)
|
|
99
|
+
const blocks = new BlockStore(dataFile, tree)
|
|
100
|
+
|
|
101
|
+
if (overwrite) {
|
|
102
|
+
await tree.clear()
|
|
103
|
+
await blocks.clear()
|
|
104
|
+
await bitfield.clear()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const sign = opts.sign || (header.signer.secretKey ? this.createSigner(crypto, header.signer) : null)
|
|
108
|
+
|
|
109
|
+
for (const e of entries) {
|
|
110
|
+
if (e.userData) {
|
|
111
|
+
updateUserData(header.userData, e.userData.key, e.userData.value)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (e.treeNodes) {
|
|
115
|
+
for (const node of e.treeNodes) {
|
|
116
|
+
tree.addNode(node)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (e.bitfield) {
|
|
121
|
+
bitfield.setRange(e.bitfield.start, e.bitfield.length)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (e.treeUpgrade) {
|
|
125
|
+
const batch = await tree.truncate(e.treeUpgrade.length, e.treeUpgrade.fork)
|
|
126
|
+
batch.ancestors = e.treeUpgrade.ancestors
|
|
127
|
+
batch.signature = e.treeUpgrade.signature
|
|
128
|
+
addReorgHint(header.hints.reorgs, tree, batch)
|
|
129
|
+
batch.commit()
|
|
130
|
+
|
|
131
|
+
header.tree.length = tree.length
|
|
132
|
+
header.tree.fork = tree.fork
|
|
133
|
+
header.tree.rootHash = tree.hash()
|
|
134
|
+
header.tree.signature = tree.signature
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return new this(header, crypto, oplog, tree, blocks, bitfield, sign, opts.onupdate || noop)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
_shouldFlush () {
|
|
142
|
+
// TODO: make something more fancy for auto flush mode (like fibonacci etc)
|
|
143
|
+
if (--this._autoFlush <= 0 || this.oplog.byteLength >= this._maxOplogSize) {
|
|
144
|
+
this._autoFlush = 4
|
|
145
|
+
return true
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async _flushOplog () {
|
|
152
|
+
// TODO: the apis using this, actually do not need to wait for the bitfields, tree etc to flush
|
|
153
|
+
// as their mutations are already stored in the oplog. We could potentially just run this in the
|
|
154
|
+
// background. Might be easier to impl that where it is called instead and keep this one simple.
|
|
155
|
+
await this.bitfield.flush()
|
|
156
|
+
await this.tree.flush()
|
|
157
|
+
await this.oplog.flush(this.header)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
_appendBlocks (values) {
|
|
161
|
+
return this.blocks.putBatch(this.tree.length, values, this.tree.byteLength)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async _writeBlock (batch, index, value) {
|
|
165
|
+
const byteOffset = await batch.byteOffset(index * 2)
|
|
166
|
+
await this.blocks.put(index, value, byteOffset)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async userData (key, value) {
|
|
170
|
+
// TODO: each oplog append can set user data, so we should have a way
|
|
171
|
+
// to just hitch a ride on one of the other ongoing appends?
|
|
172
|
+
await this._mutex.lock()
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
let empty = true
|
|
176
|
+
|
|
177
|
+
for (const u of this.header.userData) {
|
|
178
|
+
if (u.key !== key) continue
|
|
179
|
+
if (value && u.value.equals(value)) return
|
|
180
|
+
empty = false
|
|
181
|
+
break
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (empty && !value) return
|
|
185
|
+
|
|
186
|
+
const entry = {
|
|
187
|
+
userData: { key, value },
|
|
188
|
+
treeNodes: null,
|
|
189
|
+
treeUpgrade: null,
|
|
190
|
+
bitfield: null
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await this.oplog.append([entry], false)
|
|
194
|
+
|
|
195
|
+
updateUserData(this.header.userData, key, value)
|
|
196
|
+
|
|
197
|
+
if (this._shouldFlush()) await this._flushOplog()
|
|
198
|
+
} finally {
|
|
199
|
+
this._mutex.unlock()
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async truncate (length, fork, sign = this.defaultSign) {
|
|
204
|
+
this.truncating++
|
|
205
|
+
await this._mutex.lock()
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
const batch = await this.tree.truncate(length, fork)
|
|
209
|
+
batch.signature = await sign(batch.signable())
|
|
210
|
+
await this._truncate(batch, null)
|
|
211
|
+
} finally {
|
|
212
|
+
this.truncating--
|
|
213
|
+
this._mutex.unlock()
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async append (values, sign = this.defaultSign, hooks = {}) {
|
|
218
|
+
await this._mutex.lock()
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
if (hooks.preappend) await hooks.preappend(values)
|
|
222
|
+
|
|
223
|
+
if (!values.length) return this.tree.length
|
|
224
|
+
|
|
225
|
+
const batch = this.tree.batch()
|
|
226
|
+
for (const val of values) batch.append(val)
|
|
227
|
+
|
|
228
|
+
const hash = batch.hash()
|
|
229
|
+
batch.signature = await sign(batch.signable(hash))
|
|
230
|
+
|
|
231
|
+
const entry = {
|
|
232
|
+
userData: null,
|
|
233
|
+
treeNodes: batch.nodes,
|
|
234
|
+
treeUpgrade: batch,
|
|
235
|
+
bitfield: {
|
|
236
|
+
drop: false,
|
|
237
|
+
start: batch.ancestors,
|
|
238
|
+
length: values.length
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
await this._appendBlocks(values)
|
|
243
|
+
await this.oplog.append([entry], false)
|
|
244
|
+
|
|
245
|
+
this.bitfield.setRange(batch.ancestors, batch.length - batch.ancestors, true)
|
|
246
|
+
batch.commit()
|
|
247
|
+
|
|
248
|
+
this.header.tree.length = batch.length
|
|
249
|
+
this.header.tree.rootHash = hash
|
|
250
|
+
this.header.tree.signature = batch.signature
|
|
251
|
+
this.onupdate(0b01, entry.bitfield, null, null)
|
|
252
|
+
|
|
253
|
+
if (this._shouldFlush()) await this._flushOplog()
|
|
254
|
+
|
|
255
|
+
return batch.ancestors
|
|
256
|
+
} finally {
|
|
257
|
+
this._mutex.unlock()
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async _verifyExclusive ({ batch, bitfield, value, from }) {
|
|
262
|
+
// TODO: move this to tree.js
|
|
263
|
+
const hash = batch.hash()
|
|
264
|
+
if (!batch.signature || !this.crypto.verify(batch.signable(hash), batch.signature, this.header.signer.publicKey)) {
|
|
265
|
+
throw new Error('Remote signature does not match')
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
await this._mutex.lock()
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
if (!batch.commitable()) return false
|
|
272
|
+
|
|
273
|
+
const entry = {
|
|
274
|
+
userData: null,
|
|
275
|
+
treeNodes: batch.nodes,
|
|
276
|
+
treeUpgrade: batch,
|
|
277
|
+
bitfield
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (bitfield) await this._writeBlock(batch, bitfield.start, value)
|
|
281
|
+
|
|
282
|
+
await this.oplog.append([entry], false)
|
|
283
|
+
|
|
284
|
+
if (bitfield) this.bitfield.set(bitfield.start, true)
|
|
285
|
+
batch.commit()
|
|
286
|
+
|
|
287
|
+
this.header.tree.fork = batch.fork
|
|
288
|
+
this.header.tree.length = batch.length
|
|
289
|
+
this.header.tree.rootHash = batch.rootHash
|
|
290
|
+
this.header.tree.signature = batch.signature
|
|
291
|
+
this.onupdate(0b01, bitfield, value, from)
|
|
292
|
+
|
|
293
|
+
if (this._shouldFlush()) await this._flushOplog()
|
|
294
|
+
} finally {
|
|
295
|
+
this._mutex.unlock()
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return true
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async _verifyShared () {
|
|
302
|
+
if (!this._verifies.length) return false
|
|
303
|
+
|
|
304
|
+
await this._mutex.lock()
|
|
305
|
+
|
|
306
|
+
const verifies = this._verifies
|
|
307
|
+
this._verifies = null
|
|
308
|
+
this._verified = null
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
const entries = []
|
|
312
|
+
|
|
313
|
+
for (const { batch, bitfield, value } of verifies) {
|
|
314
|
+
if (!batch.commitable()) continue
|
|
315
|
+
|
|
316
|
+
if (bitfield) {
|
|
317
|
+
await this._writeBlock(batch, bitfield.start, value)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
entries.push({
|
|
321
|
+
userData: null,
|
|
322
|
+
treeNodes: batch.nodes,
|
|
323
|
+
treeUpgrade: null,
|
|
324
|
+
bitfield
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
await this.oplog.append(entries, false)
|
|
329
|
+
|
|
330
|
+
for (let i = 0; i < verifies.length; i++) {
|
|
331
|
+
const { batch, bitfield, value, from } = verifies[i]
|
|
332
|
+
|
|
333
|
+
if (!batch.commitable()) {
|
|
334
|
+
verifies[i] = null // signal that we cannot commit this one
|
|
335
|
+
continue
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (bitfield) this.bitfield.set(bitfield.start, true)
|
|
339
|
+
batch.commit()
|
|
340
|
+
this.onupdate(0, bitfield, value, from)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (this._shouldFlush()) await this._flushOplog()
|
|
344
|
+
} finally {
|
|
345
|
+
this._mutex.unlock()
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return verifies[0] !== null
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async verify (proof, from) {
|
|
352
|
+
// We cannot apply "other forks" atm.
|
|
353
|
+
// We should probably still try and they are likely super similar for non upgrades
|
|
354
|
+
// but this is easy atm (and the above layer will just retry)
|
|
355
|
+
|
|
356
|
+
if (proof.fork !== this.tree.fork) return false
|
|
357
|
+
|
|
358
|
+
const batch = await this.tree.verify(proof)
|
|
359
|
+
if (!batch.commitable()) return false
|
|
360
|
+
|
|
361
|
+
const value = (proof.block && proof.block.value) || null
|
|
362
|
+
const op = {
|
|
363
|
+
batch,
|
|
364
|
+
bitfield: value && { drop: false, start: proof.block.index, length: 1 },
|
|
365
|
+
value: value,
|
|
366
|
+
from
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (batch.upgraded) return this._verifyExclusive(op)
|
|
370
|
+
|
|
371
|
+
if (this._verifies !== null) {
|
|
372
|
+
const verifies = this._verifies
|
|
373
|
+
const i = verifies.push(op)
|
|
374
|
+
await this._verified
|
|
375
|
+
return verifies[i] !== null
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
this._verifies = [op]
|
|
379
|
+
this._verified = this._verifyShared()
|
|
380
|
+
return this._verified
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async reorg (batch, from) {
|
|
384
|
+
if (!batch.commitable()) return false
|
|
385
|
+
|
|
386
|
+
this.truncating++
|
|
387
|
+
await this._mutex.lock()
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
if (!batch.commitable()) return false
|
|
391
|
+
await this._truncate(batch, from)
|
|
392
|
+
} finally {
|
|
393
|
+
this.truncating--
|
|
394
|
+
this._mutex.unlock()
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return true
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async _truncate (batch, from) {
|
|
401
|
+
const entry = {
|
|
402
|
+
userData: null,
|
|
403
|
+
treeNodes: batch.nodes,
|
|
404
|
+
treeUpgrade: batch,
|
|
405
|
+
bitfield: {
|
|
406
|
+
drop: true,
|
|
407
|
+
start: batch.ancestors,
|
|
408
|
+
length: this.tree.length - batch.ancestors
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
await this.oplog.append([entry], false)
|
|
413
|
+
|
|
414
|
+
this.bitfield.setRange(batch.ancestors, this.tree.length - batch.ancestors, false)
|
|
415
|
+
addReorgHint(this.header.hints.reorgs, this.tree, batch)
|
|
416
|
+
batch.commit()
|
|
417
|
+
|
|
418
|
+
const appended = batch.length > batch.ancestors
|
|
419
|
+
|
|
420
|
+
this.header.tree.fork = batch.fork
|
|
421
|
+
this.header.tree.length = batch.length
|
|
422
|
+
this.header.tree.rootHash = batch.hash()
|
|
423
|
+
this.header.tree.signature = batch.signature
|
|
424
|
+
this.onupdate(appended ? 0b11 : 0b10, entry.bitfield, null, from)
|
|
425
|
+
|
|
426
|
+
// TODO: there is a bug in the merkle tree atm where it cannot handle unflushed
|
|
427
|
+
// truncates if we append or download anything after the truncation point later on
|
|
428
|
+
// This is because tree.get checks the truncated flag. We should fix this so we can do
|
|
429
|
+
// the later flush here as well
|
|
430
|
+
// if (this._shouldFlush()) await this._flushOplog()
|
|
431
|
+
await this._flushOplog()
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async close () {
|
|
435
|
+
await this._mutex.destroy()
|
|
436
|
+
await Promise.allSettled([
|
|
437
|
+
this.oplog.close(),
|
|
438
|
+
this.bitfield.close(),
|
|
439
|
+
this.tree.close(),
|
|
440
|
+
this.blocks.close()
|
|
441
|
+
])
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function addReorgHint (list, tree, batch) {
|
|
446
|
+
if (tree.length === 0 || tree.fork === batch.fork) return
|
|
447
|
+
|
|
448
|
+
while (list.length >= 4) list.shift() // 4 here is arbitrary, just want it to be small (hints only)
|
|
449
|
+
while (list.length > 0) {
|
|
450
|
+
if (list[list.length - 1].ancestors > batch.ancestors) list.pop()
|
|
451
|
+
else break
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
list.push({ from: tree.fork, to: batch.fork, ancestors: batch.ancestors })
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function updateUserData (list, key, value) {
|
|
458
|
+
for (let i = 0; i < list.length; i++) {
|
|
459
|
+
if (list[i].key === key) {
|
|
460
|
+
if (value) list[i].value = value
|
|
461
|
+
else list.splice(i, 1)
|
|
462
|
+
return
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (value) list.push({ key, value })
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function noop () {}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
class Extension {
|
|
2
|
+
constructor (extensions, name, handlers) {
|
|
3
|
+
this.extensions = extensions
|
|
4
|
+
this.name = name
|
|
5
|
+
this.encoding = handlers.encoding
|
|
6
|
+
this.destroyed = false
|
|
7
|
+
// TODO: should avoid the bind here by calling directly on handlers instead?
|
|
8
|
+
this.onmessage = (handlers.onmessage || noop).bind(handlers)
|
|
9
|
+
this.onremotesupports = (handlers.onremotesupports || noop).bind(handlers)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
send (message, peer) {
|
|
13
|
+
if (this.destroyed) return
|
|
14
|
+
const ext = peer.extensions.get(this.name)
|
|
15
|
+
if (ext) ext.send(message)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
broadcast (message) {
|
|
19
|
+
if (this.extensions.replicator === null || this.destroyed) return
|
|
20
|
+
for (const peer of this.extensions.replicator.peers) this.send(message, peer)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
destroy () {
|
|
24
|
+
if (this.destroyed) return
|
|
25
|
+
this.destroyed = true
|
|
26
|
+
this.extensions.all.delete(this.name)
|
|
27
|
+
if (this.extensions.replicator === null) return
|
|
28
|
+
for (const peer of this.extensions.replicator.peers) {
|
|
29
|
+
const ext = peer.extensions.get(this.name)
|
|
30
|
+
if (ext) ext.destroy()
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = class Extensions {
|
|
36
|
+
constructor () {
|
|
37
|
+
this.replicator = null
|
|
38
|
+
this.all = new Map()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[Symbol.iterator] () {
|
|
42
|
+
return this.all[Symbol.iterator]()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
attach (replicator) {
|
|
46
|
+
if (replicator === this.replicator) return
|
|
47
|
+
this.replicator = replicator
|
|
48
|
+
|
|
49
|
+
for (const [name, ext] of this.all) {
|
|
50
|
+
for (const peer of this.replicator.peers) {
|
|
51
|
+
peer.registerExtension(name, ext)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
register (name, handlers, ext = new Extension(this, name, handlers)) {
|
|
57
|
+
if (this.all.has(name)) this.all.get(name).destroy()
|
|
58
|
+
this.all.set(name, ext)
|
|
59
|
+
|
|
60
|
+
if (this.replicator !== null) {
|
|
61
|
+
for (const peer of this.replicator.peers) {
|
|
62
|
+
peer.registerExtension(name, ext)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return ext
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
update (peer) {
|
|
70
|
+
for (const ext of this.all.values()) {
|
|
71
|
+
peer.registerExtension(ext.name, ext)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function noop () {}
|