hypercore 10.38.1 → 11.0.0
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 +390 -442
- 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 +413 -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
package/index.js
CHANGED
|
@@ -1,46 +1,42 @@
|
|
|
1
1
|
const { EventEmitter } = require('events')
|
|
2
|
-
const RAF = require('random-access-file')
|
|
3
2
|
const isOptions = require('is-options')
|
|
4
|
-
const
|
|
3
|
+
const crypto = require('hypercore-crypto')
|
|
4
|
+
const CoreStorage = require('hypercore-storage')
|
|
5
5
|
const c = require('compact-encoding')
|
|
6
6
|
const b4a = require('b4a')
|
|
7
|
-
const Xache = require('xache')
|
|
8
7
|
const NoiseSecretStream = require('@hyperswarm/secret-stream')
|
|
9
8
|
const Protomux = require('protomux')
|
|
10
|
-
const z32 = require('z32')
|
|
11
9
|
const id = require('hypercore-id-encoding')
|
|
12
10
|
const safetyCatch = require('safety-catch')
|
|
13
11
|
const unslab = require('unslab')
|
|
14
12
|
|
|
15
|
-
const Replicator = require('./lib/replicator')
|
|
16
13
|
const Core = require('./lib/core')
|
|
17
14
|
const BlockEncryption = require('./lib/block-encryption')
|
|
18
15
|
const Info = require('./lib/info')
|
|
19
16
|
const Download = require('./lib/download')
|
|
20
|
-
const Batch = require('./lib/batch')
|
|
21
17
|
const { manifestHash, createManifest } = require('./lib/verifier')
|
|
22
18
|
const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
|
|
23
19
|
const {
|
|
24
20
|
ASSERTION,
|
|
25
21
|
BAD_ARGUMENT,
|
|
26
22
|
SESSION_CLOSED,
|
|
23
|
+
SESSION_MOVED,
|
|
27
24
|
SESSION_NOT_WRITABLE,
|
|
28
25
|
SNAPSHOT_NOT_AVAILABLE,
|
|
29
26
|
DECODING_ERROR
|
|
30
27
|
} = require('hypercore-errors')
|
|
31
28
|
|
|
32
|
-
const promises = Symbol.for('hypercore.promises')
|
|
33
29
|
const inspect = Symbol.for('nodejs.util.inspect.custom')
|
|
34
30
|
|
|
35
31
|
// Hypercore actually does not have any notion of max/min block sizes
|
|
36
32
|
// but we enforce 15mb to ensure smooth replication (each block is transmitted atomically)
|
|
37
33
|
const MAX_SUGGESTED_BLOCK_SIZE = 15 * 1024 * 1024
|
|
38
34
|
|
|
39
|
-
|
|
35
|
+
class Hypercore extends EventEmitter {
|
|
40
36
|
constructor (storage, key, opts) {
|
|
41
37
|
super()
|
|
42
38
|
|
|
43
|
-
if (isOptions(storage)) {
|
|
39
|
+
if (isOptions(storage) && !storage.db) {
|
|
44
40
|
opts = storage
|
|
45
41
|
storage = null
|
|
46
42
|
key = opts.key || null
|
|
@@ -54,34 +50,30 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
54
50
|
|
|
55
51
|
if (!storage) storage = opts.storage
|
|
56
52
|
|
|
57
|
-
this[promises] = true
|
|
58
|
-
|
|
59
|
-
this.storage = null
|
|
60
|
-
this.crypto = opts.crypto || hypercoreCrypto
|
|
61
53
|
this.core = null
|
|
62
|
-
this.
|
|
54
|
+
this.state = null
|
|
63
55
|
this.encryption = null
|
|
64
56
|
this.extensions = new Map()
|
|
65
|
-
this.cache = createCache(opts.cache)
|
|
66
57
|
|
|
67
58
|
this.valueEncoding = null
|
|
68
59
|
this.encodeBatch = null
|
|
69
60
|
this.activeRequests = []
|
|
61
|
+
this.sessions = null
|
|
62
|
+
this.ongc = null
|
|
70
63
|
|
|
71
|
-
this.id = null
|
|
72
|
-
this.key = key || null
|
|
73
64
|
this.keyPair = opts.keyPair || null
|
|
74
65
|
this.readable = true
|
|
75
66
|
this.writable = false
|
|
67
|
+
this.exclusive = false
|
|
76
68
|
this.opened = false
|
|
77
69
|
this.closed = false
|
|
70
|
+
this.weak = !!opts.weak
|
|
78
71
|
this.snapshotted = !!opts.snapshot
|
|
79
|
-
this.
|
|
80
|
-
this.sessions = opts._sessions || [this]
|
|
81
|
-
this.autoClose = !!opts.autoClose
|
|
72
|
+
this.draft = !!opts.draft
|
|
82
73
|
this.onwait = opts.onwait || null
|
|
83
74
|
this.wait = opts.wait !== false
|
|
84
75
|
this.timeout = opts.timeout || 0
|
|
76
|
+
this.preload = null
|
|
85
77
|
this.closing = null
|
|
86
78
|
this.opening = null
|
|
87
79
|
|
|
@@ -91,8 +83,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
91
83
|
this._findingPeers = 0
|
|
92
84
|
this._active = opts.active !== false
|
|
93
85
|
|
|
94
|
-
this.
|
|
86
|
+
this._sessionIndex = -1
|
|
87
|
+
this._stateIndex = -1 // maintained by session state
|
|
88
|
+
this._monitorIndex = -1 // maintained by replication state
|
|
89
|
+
|
|
90
|
+
this.opening = this._open(storage, key, opts)
|
|
95
91
|
this.opening.catch(safetyCatch)
|
|
92
|
+
|
|
93
|
+
this.on('newListener', maybeAddMonitor)
|
|
96
94
|
}
|
|
97
95
|
|
|
98
96
|
[inspect] (depth, opts) {
|
|
@@ -129,7 +127,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
129
127
|
indent + ' opened: ' + opts.stylize(this.opened, 'boolean') + '\n' +
|
|
130
128
|
indent + ' closed: ' + opts.stylize(this.closed, 'boolean') + '\n' +
|
|
131
129
|
indent + ' snapshotted: ' + opts.stylize(this.snapshotted, 'boolean') + '\n' +
|
|
132
|
-
indent + ' sparse: ' + opts.stylize(this.sparse, 'boolean') + '\n' +
|
|
133
130
|
indent + ' writable: ' + opts.stylize(this.writable, 'boolean') + '\n' +
|
|
134
131
|
indent + ' length: ' + opts.stylize(this.length, 'number') + '\n' +
|
|
135
132
|
indent + ' fork: ' + opts.stylize(this.fork, 'number') + '\n' +
|
|
@@ -147,13 +144,17 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
147
144
|
}
|
|
148
145
|
|
|
149
146
|
static discoveryKey (key) {
|
|
150
|
-
return
|
|
147
|
+
return crypto.discoveryKey(key)
|
|
151
148
|
}
|
|
152
149
|
|
|
153
150
|
static getProtocolMuxer (stream) {
|
|
154
151
|
return stream.noiseStream.userData
|
|
155
152
|
}
|
|
156
153
|
|
|
154
|
+
static createCore (storage, opts) {
|
|
155
|
+
return new Core(Hypercore.defaultStorage(storage), { autoClose: false, ...opts })
|
|
156
|
+
}
|
|
157
|
+
|
|
157
158
|
static createProtocolStream (isInitiator, opts = {}) {
|
|
158
159
|
let outerStream = Protomux.isProtomux(isInitiator)
|
|
159
160
|
? isInitiator.stream
|
|
@@ -188,29 +189,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
static defaultStorage (storage, opts = {}) {
|
|
191
|
-
if (
|
|
192
|
-
if (!isRandomAccessClass(storage)) return storage
|
|
193
|
-
const Cls = storage // just to satisfy standard...
|
|
194
|
-
return name => new Cls(name)
|
|
195
|
-
}
|
|
192
|
+
if (CoreStorage.isCoreStorage(storage)) return storage
|
|
196
193
|
|
|
197
194
|
const directory = storage
|
|
198
|
-
|
|
199
|
-
const pool = opts.pool || (opts.poolSize ? RAF.createPool(opts.poolSize) : null)
|
|
200
|
-
const rmdir = !!opts.rmdir
|
|
201
|
-
const writable = opts.writable !== false
|
|
202
|
-
|
|
203
|
-
return createFile
|
|
204
|
-
|
|
205
|
-
function createFile (name) {
|
|
206
|
-
const lock = toLock === null ? false : isFile(name, toLock)
|
|
207
|
-
const sparse = isFile(name, 'data') || isFile(name, 'bitfield') || isFile(name, 'tree')
|
|
208
|
-
return new RAF(name, { directory, lock, sparse, pool: lock ? null : pool, rmdir, writable })
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function isFile (name, n) {
|
|
212
|
-
return name === n || name.endsWith('/' + n)
|
|
213
|
-
}
|
|
195
|
+
return new CoreStorage(directory, opts)
|
|
214
196
|
}
|
|
215
197
|
|
|
216
198
|
snapshot (opts) {
|
|
@@ -224,49 +206,33 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
224
206
|
throw SESSION_CLOSED('Cannot make sessions on a closing core')
|
|
225
207
|
}
|
|
226
208
|
|
|
227
|
-
const sparse = opts.sparse === false ? false : this.sparse
|
|
228
209
|
const wait = opts.wait === false ? false : this.wait
|
|
229
210
|
const writable = opts.writable === false ? false : !this._readonly
|
|
230
211
|
const onwait = opts.onwait === undefined ? this.onwait : opts.onwait
|
|
231
212
|
const timeout = opts.timeout === undefined ? this.timeout : opts.timeout
|
|
213
|
+
const weak = opts.weak === undefined ? this.weak : opts.weak
|
|
232
214
|
const Clz = opts.class || Hypercore
|
|
233
|
-
const s = new Clz(
|
|
215
|
+
const s = new Clz(null, this.key, {
|
|
234
216
|
...opts,
|
|
235
|
-
sparse,
|
|
236
217
|
wait,
|
|
237
218
|
onwait,
|
|
238
219
|
timeout,
|
|
239
220
|
writable,
|
|
240
|
-
|
|
241
|
-
|
|
221
|
+
weak,
|
|
222
|
+
parent: this
|
|
242
223
|
})
|
|
243
224
|
|
|
244
|
-
s._passCapabilities(this)
|
|
245
|
-
|
|
246
|
-
// Configure the cache unless explicitly disabled.
|
|
247
|
-
if (opts.cache !== false) {
|
|
248
|
-
s.cache = opts.cache === true || !opts.cache ? this.cache : opts.cache
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (this.opened) ensureEncryption(s, opts)
|
|
252
|
-
this._addSession(s)
|
|
253
|
-
|
|
254
225
|
return s
|
|
255
226
|
}
|
|
256
227
|
|
|
257
|
-
_addSession (s) {
|
|
258
|
-
this.sessions.push(s)
|
|
259
|
-
if (this.core) this.core.active++
|
|
260
|
-
}
|
|
261
|
-
|
|
262
228
|
async setEncryptionKey (encryptionKey, opts) {
|
|
263
229
|
if (!this.opened) await this.opening
|
|
264
230
|
this.encryption = encryptionKey ? new BlockEncryption(encryptionKey, this.key, { compat: this.core.compat, ...opts }) : null
|
|
231
|
+
if (!this.core.encryption) this.core.encryption = this.encryption
|
|
265
232
|
}
|
|
266
233
|
|
|
267
234
|
setKeyPair (keyPair) {
|
|
268
235
|
this.keyPair = keyPair
|
|
269
|
-
this.writable = this._isWritable()
|
|
270
236
|
}
|
|
271
237
|
|
|
272
238
|
setActive (bool) {
|
|
@@ -274,74 +240,87 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
274
240
|
if (active === this._active || this.closing) return
|
|
275
241
|
this._active = active
|
|
276
242
|
if (!this.opened) return
|
|
277
|
-
this.replicator.updateActivity(this._active ? 1 : -1)
|
|
243
|
+
this.core.replicator.updateActivity(this._active ? 1 : -1)
|
|
278
244
|
}
|
|
279
245
|
|
|
280
|
-
|
|
281
|
-
if (!this.keyPair) this.keyPair =
|
|
282
|
-
this.crypto = o.crypto
|
|
283
|
-
this.id = o.id
|
|
284
|
-
this.key = o.key
|
|
285
|
-
this.core = o.core
|
|
286
|
-
this.replicator = o.replicator
|
|
287
|
-
this.encryption = o.encryption
|
|
246
|
+
_setupSession (parent) {
|
|
247
|
+
if (!this.keyPair) this.keyPair = parent.keyPair
|
|
288
248
|
this.writable = this._isWritable()
|
|
289
|
-
|
|
249
|
+
|
|
250
|
+
const s = parent.state
|
|
251
|
+
|
|
252
|
+
if (s) {
|
|
253
|
+
const shouldSnapshot = this.snapshotted && !s.isSnapshot()
|
|
254
|
+
this.state = shouldSnapshot ? s.snapshot() : s.ref()
|
|
255
|
+
}
|
|
290
256
|
|
|
291
257
|
if (this.snapshotted && this.core && !this._snapshot) this._updateSnapshot()
|
|
292
258
|
}
|
|
293
259
|
|
|
294
|
-
async
|
|
295
|
-
|
|
260
|
+
async _open (storage, key, opts) {
|
|
261
|
+
const preload = opts.preload || (opts.parent && opts.parent.preload)
|
|
296
262
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
s._passCapabilities(from)
|
|
303
|
-
s._addSession(s)
|
|
263
|
+
if (preload) {
|
|
264
|
+
this.sessions = [] // in case someone looks at it like with peers
|
|
265
|
+
this.preload = preload
|
|
266
|
+
opts = { ...opts, ...(await this.preload) }
|
|
267
|
+
this.preload = null
|
|
304
268
|
}
|
|
305
269
|
|
|
306
|
-
|
|
307
|
-
|
|
270
|
+
const parent = opts.parent || null
|
|
271
|
+
const core = opts.core || (parent && parent.core)
|
|
272
|
+
const sessions = opts.sessions || (parent && parent.sessions)
|
|
273
|
+
const ongc = opts.ongc || (parent && parent.ongc)
|
|
274
|
+
|
|
275
|
+
if (core) this.core = core
|
|
276
|
+
if (ongc) this.ongc = ongc
|
|
277
|
+
if (sessions) this.sessions = sessions
|
|
278
|
+
|
|
279
|
+
if (this.sessions === null) this.sessions = []
|
|
280
|
+
this._sessionIndex = this.sessions.push(this) - 1
|
|
308
281
|
|
|
309
|
-
|
|
282
|
+
if (this.core === null) initOnce(this, storage, key, opts)
|
|
283
|
+
if (this._monitorIndex === -2) this.core.addMonitor(this)
|
|
310
284
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
285
|
+
try {
|
|
286
|
+
await this._openSession(opts)
|
|
287
|
+
} catch (err) {
|
|
288
|
+
if (this.closing) return
|
|
289
|
+
if (this.core.autoClose && this.core.hasSession() === false) await this.core.close()
|
|
290
|
+
|
|
291
|
+
if (this.exclusive) this.core.unlockExclusive()
|
|
292
|
+
|
|
293
|
+
this.core.removeMonitor(this)
|
|
294
|
+
this._removeSession()
|
|
295
|
+
|
|
296
|
+
if (this.state !== null) this.state.removeSession(this)
|
|
297
|
+
|
|
298
|
+
this.emit('close', this.core.hasSession() === false)
|
|
299
|
+
throw err
|
|
314
300
|
}
|
|
301
|
+
|
|
302
|
+
this.emit('ready')
|
|
315
303
|
}
|
|
316
304
|
|
|
317
|
-
|
|
318
|
-
|
|
305
|
+
_removeSession () {
|
|
306
|
+
if (this._sessionIndex === -1) return
|
|
307
|
+
const head = this.sessions.pop()
|
|
308
|
+
if (head !== this) this.sessions[(head._sessionIndex = this._sessionIndex)] = head
|
|
309
|
+
this._sessionIndex = -1
|
|
310
|
+
if (this.ongc !== null) this.ongc(this)
|
|
311
|
+
}
|
|
319
312
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
if (opts.preload) opts = { ...opts, ...(await this._retryPreload(opts.preload)) }
|
|
324
|
-
if (this.cache === null && opts.cache) this.cache = createCache(opts.cache)
|
|
325
|
-
|
|
326
|
-
if (isFirst) {
|
|
327
|
-
await this._openCapabilities(key, storage, opts)
|
|
328
|
-
|
|
329
|
-
// check we are the actual root and not a opts.from session
|
|
330
|
-
if (!opts.from) {
|
|
331
|
-
// Only the root session should pass capabilities to other sessions.
|
|
332
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
333
|
-
const s = this.sessions[i]
|
|
334
|
-
if (s !== this) s._passCapabilities(this)
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
} else {
|
|
338
|
-
ensureEncryption(this, opts)
|
|
339
|
-
}
|
|
313
|
+
async _openSession (opts) {
|
|
314
|
+
if (this.core.opened === false) await this.core.ready()
|
|
340
315
|
|
|
341
|
-
if (opts.
|
|
342
|
-
|
|
316
|
+
if (this.keyPair === null) this.keyPair = opts.keyPair || this.core.header.keyPair
|
|
317
|
+
|
|
318
|
+
if (!this.core.encryption && opts.encryptionKey) {
|
|
319
|
+
this.core.encryption = new BlockEncryption(opts.encryptionKey, this.key, { compat: this.core.compat, isBlockKey: opts.isBlockKey })
|
|
343
320
|
}
|
|
344
321
|
|
|
322
|
+
if (this.core.encryption) this.encryption = this.core.encryption
|
|
323
|
+
|
|
345
324
|
this.writable = this._isWritable()
|
|
346
325
|
|
|
347
326
|
if (opts.valueEncoding) {
|
|
@@ -351,96 +330,68 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
351
330
|
this.encodeBatch = opts.encodeBatch
|
|
352
331
|
}
|
|
353
332
|
|
|
354
|
-
|
|
355
|
-
|
|
333
|
+
if (opts.parent) {
|
|
334
|
+
if (opts.parent._stateIndex === -1) await opts.parent.ready()
|
|
335
|
+
this._setupSession(opts.parent)
|
|
336
|
+
}
|
|
356
337
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
338
|
+
if (opts.exclusive) {
|
|
339
|
+
this.exclusive = true
|
|
340
|
+
await this.core.lockExclusive()
|
|
341
|
+
}
|
|
360
342
|
|
|
361
|
-
|
|
343
|
+
const parent = opts.parent || this.core
|
|
362
344
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
345
|
+
if (opts.atom) {
|
|
346
|
+
this.state = await parent.state.createSession(null, -1, false, opts.atom)
|
|
347
|
+
} else if (opts.name) {
|
|
348
|
+
// todo: need to make named sessions safe before ready
|
|
349
|
+
// atm we always copy the state in passCapabilities
|
|
350
|
+
const checkout = opts.checkout === undefined ? -1 : opts.checkout
|
|
351
|
+
const state = this.state
|
|
352
|
+
|
|
353
|
+
this.state = await parent.state.createSession(opts.name, checkout, !!opts.overwrite, null)
|
|
354
|
+
if (state) state.unref() // ref'ed above in setup session
|
|
366
355
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const result = await preload()
|
|
370
|
-
const from = result && result.from
|
|
371
|
-
if (from) {
|
|
372
|
-
if (!from.opened) await from.ready()
|
|
373
|
-
if (from.closing) continue
|
|
356
|
+
if (checkout !== -1 && checkout < this.state.length) {
|
|
357
|
+
await this.state.truncate(checkout, this.fork)
|
|
374
358
|
}
|
|
375
|
-
|
|
359
|
+
} else if (this.state === null) {
|
|
360
|
+
this.state = this.core.state.ref()
|
|
376
361
|
}
|
|
377
|
-
}
|
|
378
362
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
this.
|
|
384
|
-
|
|
385
|
-
this.core = await Core.open(this.storage, {
|
|
386
|
-
compat: opts.compat,
|
|
387
|
-
force: opts.force,
|
|
388
|
-
sessions: this.sessions,
|
|
389
|
-
createIfMissing: opts.createIfMissing,
|
|
390
|
-
readonly: unlocked,
|
|
391
|
-
overwrite: opts.overwrite,
|
|
392
|
-
key,
|
|
393
|
-
keyPair: opts.keyPair,
|
|
394
|
-
crypto: this.crypto,
|
|
395
|
-
legacy: opts.legacy,
|
|
396
|
-
manifest: opts.manifest,
|
|
397
|
-
globalCache: opts.globalCache || null, // This is a temp option, not to be relied on unless you know what you are doing (no semver guarantees)
|
|
398
|
-
onupdate: this._oncoreupdate.bind(this),
|
|
399
|
-
onconflict: this._oncoreconflict.bind(this)
|
|
400
|
-
})
|
|
363
|
+
if (this.snapshotted && this.core) this._updateSnapshot()
|
|
364
|
+
|
|
365
|
+
this.state.addSession(this)
|
|
366
|
+
// TODO: we need to rework the core reference flow, as the state and session do not always agree now due to moveTo
|
|
367
|
+
this.core = this.state.core // in case it was wrong...
|
|
401
368
|
|
|
402
369
|
if (opts.userData) {
|
|
370
|
+
const tx = this.state.storage.write()
|
|
403
371
|
for (const [key, value] of Object.entries(opts.userData)) {
|
|
404
|
-
|
|
372
|
+
tx.putUserData(key, value)
|
|
405
373
|
}
|
|
374
|
+
await tx.flush()
|
|
406
375
|
}
|
|
407
376
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
this.replicator = new Replicator(this.core, this.key, {
|
|
413
|
-
eagerUpgrade: true,
|
|
414
|
-
notDownloadingLinger: opts.notDownloadingLinger,
|
|
415
|
-
allowFork: opts.allowFork !== false,
|
|
416
|
-
inflightRange: opts.inflightRange,
|
|
417
|
-
onpeerupdate: this._onpeerupdate.bind(this),
|
|
418
|
-
onupload: this._onupload.bind(this),
|
|
419
|
-
oninvalid: this._oninvalid.bind(this)
|
|
420
|
-
})
|
|
377
|
+
if (opts.manifest && !this.core.header.manifest) {
|
|
378
|
+
await this.core.setManifest(opts.manifest)
|
|
379
|
+
}
|
|
421
380
|
|
|
422
|
-
this.replicator.
|
|
381
|
+
this.core.replicator.updateActivity(this._active ? 1 : 0)
|
|
423
382
|
|
|
424
|
-
|
|
425
|
-
this.encryption = new BlockEncryption(opts.encryptionKey, this.key, { compat: this.core.compat, isBlockKey: opts.isBlockKey })
|
|
426
|
-
}
|
|
383
|
+
this.opened = true
|
|
427
384
|
}
|
|
428
385
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
length: this.core.tree.length,
|
|
433
|
-
byteLength: this.core.tree.byteLength,
|
|
434
|
-
fork: this.core.tree.fork,
|
|
435
|
-
compatLength: this.core.tree.length
|
|
436
|
-
}
|
|
437
|
-
}
|
|
386
|
+
get replicator () {
|
|
387
|
+
return this.core === null ? null : this.core.replicator
|
|
388
|
+
}
|
|
438
389
|
|
|
390
|
+
_getSnapshot () {
|
|
439
391
|
return {
|
|
440
|
-
length: this.
|
|
441
|
-
byteLength:
|
|
442
|
-
fork: this.
|
|
443
|
-
compatLength: this.core.header.hints.contiguousLength
|
|
392
|
+
length: this.state.length,
|
|
393
|
+
byteLength: this.state.byteLength,
|
|
394
|
+
fork: this.state.fork
|
|
444
395
|
}
|
|
445
396
|
}
|
|
446
397
|
|
|
@@ -453,26 +404,27 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
453
404
|
}
|
|
454
405
|
|
|
455
406
|
_isWritable () {
|
|
407
|
+
if (this.draft) return true
|
|
456
408
|
return !this._readonly && !!(this.keyPair && this.keyPair.secretKey)
|
|
457
409
|
}
|
|
458
410
|
|
|
459
|
-
close (
|
|
411
|
+
close ({ error } = {}) {
|
|
460
412
|
if (this.closing) return this.closing
|
|
461
|
-
|
|
413
|
+
|
|
414
|
+
this.closing = this._close(error || null)
|
|
462
415
|
return this.closing
|
|
463
416
|
}
|
|
464
417
|
|
|
465
|
-
async _close (
|
|
418
|
+
async _close (error) {
|
|
466
419
|
if (this.opened === false) await this.opening
|
|
420
|
+
if (this.closed === true) return
|
|
467
421
|
|
|
468
|
-
|
|
469
|
-
|
|
422
|
+
this.core.removeMonitor(this)
|
|
423
|
+
this.state.removeSession(this)
|
|
424
|
+
this._removeSession()
|
|
470
425
|
|
|
471
|
-
this.sessions.splice(i, 1)
|
|
472
|
-
this.core.active--
|
|
473
426
|
this.readable = false
|
|
474
427
|
this.writable = false
|
|
475
|
-
this.closed = true
|
|
476
428
|
this.opened = false
|
|
477
429
|
|
|
478
430
|
const gc = []
|
|
@@ -481,31 +433,36 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
481
433
|
}
|
|
482
434
|
for (const ext of gc) ext.destroy()
|
|
483
435
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
this.replicator.updateActivity(this._active ? -1 : 0)
|
|
488
|
-
}
|
|
436
|
+
this.core.replicator.findingPeers -= this._findingPeers
|
|
437
|
+
this.core.replicator.clearRequests(this.activeRequests, error)
|
|
438
|
+
this.core.replicator.updateActivity(this._active ? -1 : 0)
|
|
489
439
|
|
|
490
440
|
this._findingPeers = 0
|
|
491
441
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
442
|
+
this.state.unref()
|
|
443
|
+
|
|
444
|
+
if (this.exclusive) this.core.unlockExclusive()
|
|
445
|
+
|
|
446
|
+
if (this.core.hasSession()) {
|
|
495
447
|
// emit "fake" close as this is a session
|
|
448
|
+
this.closed = true
|
|
496
449
|
this.emit('close', false)
|
|
497
450
|
return
|
|
498
451
|
}
|
|
499
452
|
|
|
500
|
-
if (this.
|
|
501
|
-
await this.replicator.destroy()
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
await this.core.close()
|
|
453
|
+
if (this.core.autoClose) await this.core.close()
|
|
505
454
|
|
|
455
|
+
this.closed = true
|
|
506
456
|
this.emit('close', true)
|
|
507
457
|
}
|
|
508
458
|
|
|
459
|
+
async commit (session, opts) {
|
|
460
|
+
await this.ready()
|
|
461
|
+
await session.ready()
|
|
462
|
+
|
|
463
|
+
return this.state.commit(session.state, { keyPair: this.keyPair, ...opts })
|
|
464
|
+
}
|
|
465
|
+
|
|
509
466
|
replicate (isInitiator, opts = {}) {
|
|
510
467
|
// Only limitation here is that ondiscoverykey doesn't work atm when passing a muxer directly,
|
|
511
468
|
// because it doesn't really make a lot of sense.
|
|
@@ -517,64 +474,67 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
517
474
|
const protocolStream = Hypercore.createProtocolStream(isInitiator, opts)
|
|
518
475
|
const noiseStream = protocolStream.noiseStream
|
|
519
476
|
const protocol = noiseStream.userData
|
|
520
|
-
const useSession = !!opts.session
|
|
521
477
|
|
|
522
|
-
this._attachToMuxer(protocol
|
|
478
|
+
this._attachToMuxer(protocol)
|
|
523
479
|
|
|
524
480
|
return protocolStream
|
|
525
481
|
}
|
|
526
482
|
|
|
527
483
|
_isAttached (stream) {
|
|
528
|
-
return stream.userData && this.replicator && this.replicator.attached(stream.userData)
|
|
484
|
+
return stream.userData && this.core && this.core.replicator && this.core.replicator.attached(stream.userData)
|
|
529
485
|
}
|
|
530
486
|
|
|
531
|
-
_attachToMuxer (mux
|
|
487
|
+
_attachToMuxer (mux) {
|
|
532
488
|
if (this.opened) {
|
|
533
|
-
this.
|
|
489
|
+
this.core.replicator.attachTo(mux)
|
|
534
490
|
} else {
|
|
535
|
-
this.opening.then(this.
|
|
491
|
+
this.opening.then(() => this.core.replicator.attachTo(mux), mux.destroy.bind(mux))
|
|
536
492
|
}
|
|
537
493
|
|
|
538
494
|
return mux
|
|
539
495
|
}
|
|
540
496
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
497
|
+
get id () {
|
|
498
|
+
return this.core === null ? null : this.core.id
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
get key () {
|
|
502
|
+
return this.core === null ? null : this.core.key
|
|
545
503
|
}
|
|
546
504
|
|
|
547
505
|
get discoveryKey () {
|
|
548
|
-
return this.
|
|
506
|
+
return this.core === null ? null : this.core.discoveryKey
|
|
549
507
|
}
|
|
550
508
|
|
|
551
509
|
get manifest () {
|
|
552
|
-
return this.core === null ? null : this.core.
|
|
510
|
+
return this.core === null ? null : this.core.manifest
|
|
553
511
|
}
|
|
554
512
|
|
|
555
513
|
get length () {
|
|
556
514
|
if (this._snapshot) return this._snapshot.length
|
|
557
|
-
|
|
558
|
-
if (!this.sparse) return this.contiguousLength
|
|
559
|
-
return this.core.tree.length
|
|
515
|
+
return this.opened === false ? 0 : this.state.length
|
|
560
516
|
}
|
|
561
517
|
|
|
562
|
-
get
|
|
563
|
-
|
|
518
|
+
get signedLength () {
|
|
519
|
+
if (this.opened === false) return 0
|
|
520
|
+
if (this.state === this.core.state) return this.core.state.length
|
|
521
|
+
const flushed = this.state.flushedLength()
|
|
522
|
+
|
|
523
|
+
return flushed === -1 ? this.state.length : flushed
|
|
564
524
|
}
|
|
565
525
|
|
|
566
526
|
/**
|
|
567
527
|
* Deprecated. Use `const { byteLength } = await core.info()`.
|
|
568
528
|
*/
|
|
569
529
|
get byteLength () {
|
|
530
|
+
if (this.opened === false) return 0
|
|
570
531
|
if (this._snapshot) return this._snapshot.byteLength
|
|
571
|
-
|
|
572
|
-
if (!this.sparse) return this.contiguousByteLength
|
|
573
|
-
return this.core.tree.byteLength - (this.core.tree.length * this.padding)
|
|
532
|
+
return this.state.byteLength - (this.state.length * this.padding)
|
|
574
533
|
}
|
|
575
534
|
|
|
576
535
|
get contiguousLength () {
|
|
577
|
-
|
|
536
|
+
if (this.opened === false) return 0
|
|
537
|
+
return Math.min(this.core.state.length, this.core.header.hints.contiguousLength)
|
|
578
538
|
}
|
|
579
539
|
|
|
580
540
|
get contiguousByteLength () {
|
|
@@ -582,11 +542,12 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
582
542
|
}
|
|
583
543
|
|
|
584
544
|
get fork () {
|
|
585
|
-
|
|
545
|
+
if (this.opened === false) return 0
|
|
546
|
+
return this.state.fork
|
|
586
547
|
}
|
|
587
548
|
|
|
588
549
|
get peers () {
|
|
589
|
-
return this.
|
|
550
|
+
return this.opened === false ? [] : this.core.replicator.peers
|
|
590
551
|
}
|
|
591
552
|
|
|
592
553
|
get encryptionKey () {
|
|
@@ -598,155 +559,69 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
598
559
|
}
|
|
599
560
|
|
|
600
561
|
get globalCache () {
|
|
601
|
-
return this.
|
|
562
|
+
return this.opened === false ? null : this.core.globalCache
|
|
602
563
|
}
|
|
603
564
|
|
|
604
565
|
ready () {
|
|
605
566
|
return this.opening
|
|
606
567
|
}
|
|
607
568
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
612
|
-
this.sessions[i].emit('upload', index, byteLength, from)
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
_oninvalid (err, req, res, from) {
|
|
617
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
618
|
-
this.sessions[i].emit('verification-error', err, req, res, from)
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
async _oncoreconflict (proof, from) {
|
|
623
|
-
await this.replicator.onconflict(from)
|
|
624
|
-
|
|
625
|
-
for (const s of this.sessions) s.emit('conflict', proof.upgrade.length, proof.fork, proof)
|
|
626
|
-
|
|
627
|
-
const err = new Error('Two conflicting signatures exist for length ' + proof.upgrade.length)
|
|
628
|
-
await this._closeAllSessions(err)
|
|
569
|
+
async setUserData (key, value) {
|
|
570
|
+
if (this.opened === false) await this.opening
|
|
571
|
+
await this.state.setUserData(key, value)
|
|
629
572
|
}
|
|
630
573
|
|
|
631
|
-
async
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
for (const s of sessions) all.push(s.close(err))
|
|
638
|
-
await Promise.allSettled(all)
|
|
574
|
+
async getUserData (key) {
|
|
575
|
+
if (this.opened === false) await this.opening
|
|
576
|
+
const batch = this.state.storage.read()
|
|
577
|
+
const p = batch.getUserData(key)
|
|
578
|
+
batch.tryFlush()
|
|
579
|
+
return p
|
|
639
580
|
}
|
|
640
581
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
const truncatedNonSparse = (status & 0b1000) !== 0
|
|
644
|
-
const appendedNonSparse = (status & 0b0100) !== 0
|
|
645
|
-
const truncated = (status & 0b0010) !== 0
|
|
646
|
-
const appended = (status & 0b0001) !== 0
|
|
582
|
+
transferSession (core) {
|
|
583
|
+
// todo: validate we can move
|
|
647
584
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
if ((status & 0b10011) !== 0) {
|
|
653
|
-
this.replicator.onupgrade()
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
if (status & 0b10000) {
|
|
657
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
658
|
-
const s = this.sessions[i]
|
|
659
|
-
|
|
660
|
-
if (s.encryption && s.encryption.compat !== this.core.compat) {
|
|
661
|
-
s.encryption = new BlockEncryption(s.encryption.key, this.key, { compat: this.core.compat, isBlockKey: s.encryption.isBlockKey })
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
666
|
-
this.sessions[i].emit('manifest')
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
671
|
-
const s = this.sessions[i]
|
|
672
|
-
|
|
673
|
-
if (truncated) {
|
|
674
|
-
if (s.cache) s.cache.clear()
|
|
675
|
-
|
|
676
|
-
// If snapshotted, make sure to update our compat so we can fail gets
|
|
677
|
-
if (s._snapshot && bitfield.start < s._snapshot.compatLength) s._snapshot.compatLength = bitfield.start
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
if (s.sparse ? truncated : truncatedNonSparse) {
|
|
681
|
-
s.emit('truncate', bitfield.start, this.core.tree.fork)
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// For sparse sessions, immediately emit appends. If non-sparse, emit if contig length has updated
|
|
685
|
-
if (s.sparse ? appended : appendedNonSparse) {
|
|
686
|
-
s.emit('append')
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
const contig = this.core.header.hints.contiguousLength
|
|
691
|
-
|
|
692
|
-
// When the contig length catches up, broadcast the non-sparse length to peers
|
|
693
|
-
if (appendedNonSparse && contig === this.core.tree.length) {
|
|
694
|
-
for (const peer of this.peers) {
|
|
695
|
-
if (peer.broadcastedNonSparse) continue
|
|
696
|
-
|
|
697
|
-
peer.broadcastRange(0, contig)
|
|
698
|
-
peer.broadcastedNonSparse = true
|
|
699
|
-
}
|
|
700
|
-
}
|
|
585
|
+
if (this.weak === false) {
|
|
586
|
+
this.core.activeSessions--
|
|
587
|
+
core.activeSessions++
|
|
701
588
|
}
|
|
702
589
|
|
|
703
|
-
if (
|
|
704
|
-
this.
|
|
590
|
+
if (this._monitorIndex >= 0) {
|
|
591
|
+
this.core.removeMonitor(this)
|
|
592
|
+
core.addMonitor(this)
|
|
705
593
|
}
|
|
706
594
|
|
|
707
|
-
|
|
708
|
-
const byteLength = value.byteLength - this.padding
|
|
709
|
-
|
|
710
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
711
|
-
this.sessions[i].emit('download', bitfield.start, byteLength, from)
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
}
|
|
595
|
+
const old = this.core
|
|
715
596
|
|
|
716
|
-
|
|
717
|
-
const name = added ? 'peer-add' : 'peer-remove'
|
|
597
|
+
this.core = core
|
|
718
598
|
|
|
719
|
-
|
|
720
|
-
this.sessions[i].emit(name, peer)
|
|
599
|
+
old.replicator.clearRequests(this.activeRequests, SESSION_MOVED())
|
|
721
600
|
|
|
722
|
-
|
|
723
|
-
for (const ext of this.sessions[i].extensions.values()) {
|
|
724
|
-
peer.extensions.set(ext.name, ext)
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
}
|
|
601
|
+
this.emit('migrate', this.key)
|
|
728
602
|
}
|
|
729
603
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
return this.core.userData(key, value, flush)
|
|
604
|
+
createTreeBatch () {
|
|
605
|
+
return this.state.createTreeBatch()
|
|
733
606
|
}
|
|
734
607
|
|
|
735
|
-
async
|
|
608
|
+
async restoreBatch (length, additionalBlocks = []) {
|
|
736
609
|
if (this.opened === false) await this.opening
|
|
737
|
-
|
|
738
|
-
|
|
610
|
+
const batch = this.state.createTreeBatch()
|
|
611
|
+
|
|
612
|
+
if (length > batch.length + additionalBlocks.length) {
|
|
613
|
+
throw BAD_ARGUMENT('Insufficient additional blocks were passed')
|
|
739
614
|
}
|
|
740
|
-
return null
|
|
741
|
-
}
|
|
742
615
|
|
|
743
|
-
|
|
744
|
-
|
|
616
|
+
let i = 0
|
|
617
|
+
while (batch.length < length) batch.append(additionalBlocks[i++])
|
|
618
|
+
|
|
619
|
+
return length < batch.length ? batch.restore(length) : batch
|
|
745
620
|
}
|
|
746
621
|
|
|
747
622
|
findingPeers () {
|
|
748
623
|
this._findingPeers++
|
|
749
|
-
if (this.
|
|
624
|
+
if (this.core !== null && !this.closing) this.core.replicator.findingPeers++
|
|
750
625
|
|
|
751
626
|
let once = true
|
|
752
627
|
|
|
@@ -754,8 +629,8 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
754
629
|
if (this.closing || !once) return
|
|
755
630
|
once = false
|
|
756
631
|
this._findingPeers--
|
|
757
|
-
if (this.
|
|
758
|
-
this.replicator.updateAll()
|
|
632
|
+
if (this.core !== null && --this.core.replicator.findingPeers === 0) {
|
|
633
|
+
this.core.replicator.updateAll()
|
|
759
634
|
}
|
|
760
635
|
}
|
|
761
636
|
}
|
|
@@ -769,42 +644,40 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
769
644
|
async update (opts) {
|
|
770
645
|
if (this.opened === false) await this.opening
|
|
771
646
|
if (this.closing !== null) return false
|
|
647
|
+
if (this.snapshotted) return false
|
|
772
648
|
|
|
773
|
-
if (this.writable && (!opts || opts.force !== true))
|
|
774
|
-
if (!this.snapshotted) return false
|
|
775
|
-
return this._updateSnapshot()
|
|
776
|
-
}
|
|
649
|
+
if (this.writable && (!opts || opts.force !== true)) return false
|
|
777
650
|
|
|
778
|
-
const remoteWait = this._shouldWait(opts, this.replicator.findingPeers > 0)
|
|
651
|
+
const remoteWait = this._shouldWait(opts, this.core.replicator.findingPeers > 0)
|
|
779
652
|
|
|
780
653
|
let upgraded = false
|
|
781
654
|
|
|
782
|
-
if (await this.replicator.applyPendingReorg()) {
|
|
655
|
+
if (await this.core.replicator.applyPendingReorg()) {
|
|
783
656
|
upgraded = true
|
|
784
657
|
}
|
|
785
658
|
|
|
786
659
|
if (!upgraded && remoteWait) {
|
|
787
660
|
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
788
|
-
const req = this.replicator.addUpgrade(activeRequests)
|
|
661
|
+
const req = this.core.replicator.addUpgrade(activeRequests)
|
|
789
662
|
|
|
790
|
-
|
|
663
|
+
try {
|
|
664
|
+
upgraded = await req.promise
|
|
665
|
+
} catch (err) {
|
|
666
|
+
if (isSessionMoved(err)) return this.update(opts)
|
|
667
|
+
throw err
|
|
668
|
+
}
|
|
791
669
|
}
|
|
792
670
|
|
|
793
671
|
if (!upgraded) return false
|
|
794
|
-
if (this.snapshotted) return this._updateSnapshot()
|
|
795
672
|
return true
|
|
796
673
|
}
|
|
797
674
|
|
|
798
|
-
batch ({ checkout = -1, autoClose = true, session = true, restore = false, clear = false } = {}) {
|
|
799
|
-
return new Batch(session ? this.session() : this, checkout, autoClose, restore, clear)
|
|
800
|
-
}
|
|
801
|
-
|
|
802
675
|
async seek (bytes, opts) {
|
|
803
676
|
if (this.opened === false) await this.opening
|
|
804
677
|
if (!isValidIndex(bytes)) throw ASSERTION('seek is invalid')
|
|
805
678
|
|
|
806
|
-
const tree = (opts && opts.tree) || this.core.tree
|
|
807
|
-
const s = tree.seek(bytes, this.padding)
|
|
679
|
+
const tree = (opts && opts.tree) || this.state.core.tree
|
|
680
|
+
const s = tree.seek(this.state, bytes, this.padding)
|
|
808
681
|
|
|
809
682
|
const offset = await s.update()
|
|
810
683
|
if (offset) return offset
|
|
@@ -814,22 +687,47 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
814
687
|
if (!this._shouldWait(opts, this.wait)) return null
|
|
815
688
|
|
|
816
689
|
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
817
|
-
const req = this.replicator.addSeek(activeRequests, s)
|
|
690
|
+
const req = this.core.replicator.addSeek(activeRequests, s)
|
|
818
691
|
|
|
819
692
|
const timeout = opts && opts.timeout !== undefined ? opts.timeout : this.timeout
|
|
820
693
|
if (timeout) req.context.setTimeout(req, timeout)
|
|
821
694
|
|
|
822
|
-
|
|
695
|
+
try {
|
|
696
|
+
return await req.promise
|
|
697
|
+
} catch (err) {
|
|
698
|
+
if (isSessionMoved(err)) return this.seek(bytes, opts)
|
|
699
|
+
throw err
|
|
700
|
+
}
|
|
823
701
|
}
|
|
824
702
|
|
|
825
703
|
async has (start, end = start + 1) {
|
|
826
704
|
if (this.opened === false) await this.opening
|
|
827
705
|
if (!isValidIndex(start) || !isValidIndex(end)) throw ASSERTION('has range is invalid')
|
|
828
706
|
|
|
829
|
-
if (
|
|
707
|
+
if (this.state.isDefault()) {
|
|
708
|
+
if (end === start + 1) return this.core.bitfield.get(start)
|
|
709
|
+
|
|
710
|
+
const i = this.core.bitfield.firstUnset(start)
|
|
711
|
+
return i === -1 || i >= end
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
if (end === start + 1) {
|
|
715
|
+
const rx = this.state.storage.read()
|
|
716
|
+
const block = rx.getBlock(start)
|
|
717
|
+
rx.tryFlush()
|
|
718
|
+
|
|
719
|
+
return (await block) !== null
|
|
720
|
+
}
|
|
830
721
|
|
|
831
|
-
|
|
832
|
-
|
|
722
|
+
let count = 0
|
|
723
|
+
|
|
724
|
+
const stream = this.state.storage.createBlockStream({ gte: start, lt: end })
|
|
725
|
+
for await (const block of stream) {
|
|
726
|
+
if (block === null) return false
|
|
727
|
+
count++
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
return count === (end - start)
|
|
833
731
|
}
|
|
834
732
|
|
|
835
733
|
async get (index, opts) {
|
|
@@ -837,12 +735,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
837
735
|
if (!isValidIndex(index)) throw ASSERTION('block index is invalid')
|
|
838
736
|
|
|
839
737
|
if (this.closing !== null) throw SESSION_CLOSED()
|
|
840
|
-
if (this._snapshot !== null && index >= this._snapshot.compatLength) throw SNAPSHOT_NOT_AVAILABLE()
|
|
841
738
|
|
|
842
739
|
const encoding = (opts && opts.valueEncoding && c.from(opts.valueEncoding)) || this.valueEncoding
|
|
843
740
|
|
|
844
|
-
|
|
845
|
-
if (!req) req = this._get(index, opts)
|
|
741
|
+
const req = this._get(index, opts)
|
|
846
742
|
|
|
847
743
|
let block = await req
|
|
848
744
|
if (!block) return null
|
|
@@ -853,6 +749,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
853
749
|
// Copy the block as it might be shared with other sessions.
|
|
854
750
|
block = b4a.from(block)
|
|
855
751
|
|
|
752
|
+
if (this.encryption.compat !== this.core.compat) this._updateEncryption()
|
|
856
753
|
this.encryption.decrypt(index, block)
|
|
857
754
|
}
|
|
858
755
|
|
|
@@ -875,7 +772,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
875
772
|
if (start >= end) return cleared
|
|
876
773
|
if (start >= this.length) return cleared
|
|
877
774
|
|
|
878
|
-
await this.
|
|
775
|
+
await this.state.clear(start, end, cleared)
|
|
879
776
|
|
|
880
777
|
return cleared
|
|
881
778
|
}
|
|
@@ -886,46 +783,55 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
886
783
|
}
|
|
887
784
|
|
|
888
785
|
async _get (index, opts) {
|
|
889
|
-
|
|
786
|
+
if (this.core.isFlushing) await this.core.flushed()
|
|
890
787
|
|
|
891
|
-
|
|
892
|
-
const tree = (opts && opts.tree) || this.core.tree
|
|
893
|
-
block = this.core.blocks.get(index, tree)
|
|
788
|
+
const block = await readBlock(this.state.storage.read(), index)
|
|
894
789
|
|
|
895
|
-
|
|
896
|
-
} else {
|
|
897
|
-
if (!this._shouldWait(opts, this.wait)) return null
|
|
790
|
+
if (block !== null) return block
|
|
898
791
|
|
|
899
|
-
|
|
900
|
-
if (this.onwait) this.onwait(index, this)
|
|
901
|
-
|
|
902
|
-
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
792
|
+
if (this.closing !== null) throw SESSION_CLOSED()
|
|
903
793
|
|
|
904
|
-
|
|
905
|
-
|
|
794
|
+
// snapshot should check if core has block
|
|
795
|
+
if (this._snapshot !== null) {
|
|
796
|
+
checkSnapshot(this, index)
|
|
797
|
+
const coreBlock = await readBlock(this.core.state.storage.read(), index)
|
|
906
798
|
|
|
907
|
-
|
|
908
|
-
if (
|
|
799
|
+
checkSnapshot(this, index)
|
|
800
|
+
if (coreBlock !== null) return coreBlock
|
|
801
|
+
}
|
|
909
802
|
|
|
910
|
-
|
|
803
|
+
// lets check the bitfield to see if we got it during the above async calls
|
|
804
|
+
// this is the last resort before replication, so always safe.
|
|
805
|
+
if (this.core.bitfield.get(index)) {
|
|
806
|
+
const coreBlock = await readBlock(this.state.storage.read(), index)
|
|
807
|
+
// TODO: this should not be needed, only needed atm in case we are doing a moveTo during this (we should fix)
|
|
808
|
+
if (coreBlock !== null) return coreBlock
|
|
911
809
|
}
|
|
912
810
|
|
|
913
|
-
return
|
|
914
|
-
|
|
811
|
+
if (!this._shouldWait(opts, this.wait)) return null
|
|
812
|
+
|
|
813
|
+
if (opts && opts.onwait) opts.onwait(index, this)
|
|
814
|
+
if (this.onwait) this.onwait(index, this)
|
|
915
815
|
|
|
916
|
-
|
|
917
|
-
const resolved = await req
|
|
816
|
+
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
918
817
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
? unslab(resolved)
|
|
922
|
-
: resolved
|
|
818
|
+
const req = this.core.replicator.addBlock(activeRequests, index)
|
|
819
|
+
req.snapshot = index < this.length
|
|
923
820
|
|
|
924
|
-
|
|
925
|
-
|
|
821
|
+
const timeout = opts && opts.timeout !== undefined ? opts.timeout : this.timeout
|
|
822
|
+
if (timeout) req.context.setTimeout(req, timeout)
|
|
823
|
+
|
|
824
|
+
let replicatedBlock = null
|
|
825
|
+
|
|
826
|
+
try {
|
|
827
|
+
replicatedBlock = await req.promise
|
|
828
|
+
} catch (err) {
|
|
829
|
+
if (isSessionMoved(err)) return this._get(index, opts)
|
|
830
|
+
throw err
|
|
926
831
|
}
|
|
927
832
|
|
|
928
|
-
|
|
833
|
+
if (this._snapshot !== null) checkSnapshot(this, index)
|
|
834
|
+
return maybeUnslab(replicatedBlock)
|
|
929
835
|
}
|
|
930
836
|
|
|
931
837
|
_shouldWait (opts, defaultValue) {
|
|
@@ -949,19 +855,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
949
855
|
}
|
|
950
856
|
|
|
951
857
|
download (range) {
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
// do not crash in the background...
|
|
955
|
-
req.catch(safetyCatch)
|
|
956
|
-
|
|
957
|
-
return new Download(req)
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
async _download (range) {
|
|
961
|
-
if (this.opened === false) await this.opening
|
|
962
|
-
|
|
963
|
-
const activeRequests = (range && range.activeRequests) || this.activeRequests
|
|
964
|
-
return this.replicator.addRange(activeRequests, range)
|
|
858
|
+
return new Download(this, range)
|
|
965
859
|
}
|
|
966
860
|
|
|
967
861
|
// TODO: get rid of this / deprecate it?
|
|
@@ -978,27 +872,31 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
978
872
|
if (this.opened === false) await this.opening
|
|
979
873
|
|
|
980
874
|
const {
|
|
981
|
-
fork = this.
|
|
875
|
+
fork = this.state.fork + 1,
|
|
982
876
|
keyPair = this.keyPair,
|
|
983
877
|
signature = null
|
|
984
878
|
} = typeof opts === 'number' ? { fork: opts } : opts
|
|
985
879
|
|
|
880
|
+
const isDefault = this.state === this.core.state
|
|
986
881
|
const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))
|
|
987
|
-
if (writable === false && (newLength > 0 || fork !== this.
|
|
882
|
+
if (isDefault && writable === false && (newLength > 0 || fork !== this.state.fork)) throw SESSION_NOT_WRITABLE()
|
|
988
883
|
|
|
989
|
-
await this.
|
|
884
|
+
await this.state.truncate(newLength, fork, { keyPair, signature })
|
|
990
885
|
|
|
991
886
|
// TODO: Should propagate from an event triggered by the oplog
|
|
992
|
-
this.replicator.updateAll()
|
|
887
|
+
if (this.state === this.core.state) this.core.replicator.updateAll()
|
|
993
888
|
}
|
|
994
889
|
|
|
995
890
|
async append (blocks, opts = {}) {
|
|
996
891
|
if (this.opened === false) await this.opening
|
|
997
892
|
|
|
998
|
-
const
|
|
999
|
-
const
|
|
893
|
+
const isDefault = this.state === this.core.state
|
|
894
|
+
const defaultKeyPair = this.state.name === null ? this.keyPair : null
|
|
1000
895
|
|
|
1001
|
-
|
|
896
|
+
const { keyPair = defaultKeyPair, signature = null } = opts
|
|
897
|
+
const writable = !!this.draft || !isDefault || !!signature || !!(keyPair && keyPair.secretKey)
|
|
898
|
+
|
|
899
|
+
if (this._readonly || writable === false) throw SESSION_NOT_WRITABLE()
|
|
1002
900
|
|
|
1003
901
|
blocks = Array.isArray(blocks) ? blocks : [blocks]
|
|
1004
902
|
|
|
@@ -1017,17 +915,17 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
1017
915
|
}
|
|
1018
916
|
}
|
|
1019
917
|
|
|
1020
|
-
return this.
|
|
918
|
+
return this.state.append(buffers, { keyPair, signature, preappend })
|
|
1021
919
|
}
|
|
1022
920
|
|
|
1023
921
|
async treeHash (length) {
|
|
1024
922
|
if (length === undefined) {
|
|
1025
923
|
await this.ready()
|
|
1026
|
-
length = this.
|
|
924
|
+
length = this.state.length
|
|
1027
925
|
}
|
|
1028
926
|
|
|
1029
|
-
const roots = await this.
|
|
1030
|
-
return
|
|
927
|
+
const roots = await this.state.tree.getRoots(length)
|
|
928
|
+
return crypto.tree(roots)
|
|
1031
929
|
}
|
|
1032
930
|
|
|
1033
931
|
registerExtension (name, handlers = {}) {
|
|
@@ -1067,6 +965,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
1067
965
|
}
|
|
1068
966
|
|
|
1069
967
|
this.extensions.set(name, ext)
|
|
968
|
+
|
|
969
|
+
if (this.core === null) this._monitorIndex = -2
|
|
970
|
+
else this.core.addMonitor(this)
|
|
971
|
+
|
|
1070
972
|
for (const peer of this.peers) {
|
|
1071
973
|
peer.extensions.set(name, ext)
|
|
1072
974
|
}
|
|
@@ -1105,41 +1007,87 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
1105
1007
|
}
|
|
1106
1008
|
return block
|
|
1107
1009
|
}
|
|
1010
|
+
|
|
1011
|
+
_updateEncryption () {
|
|
1012
|
+
const e = this.encryption
|
|
1013
|
+
this.encryption = new BlockEncryption(e.key, this.key, { compat: this.core.compat, isBlockKey: e.isBlockKey })
|
|
1014
|
+
if (e === this.core.encryption) this.core.encryption = this.encryption
|
|
1015
|
+
}
|
|
1108
1016
|
}
|
|
1109
1017
|
|
|
1018
|
+
module.exports = Hypercore
|
|
1019
|
+
|
|
1110
1020
|
function isStream (s) {
|
|
1111
1021
|
return typeof s === 'object' && s && typeof s.pipe === 'function'
|
|
1112
1022
|
}
|
|
1113
1023
|
|
|
1114
|
-
function isRandomAccessClass (fn) {
|
|
1115
|
-
return !!(typeof fn === 'function' && fn.prototype && typeof fn.prototype.open === 'function')
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
1024
|
function toHex (buf) {
|
|
1119
1025
|
return buf && b4a.toString(buf, 'hex')
|
|
1120
1026
|
}
|
|
1121
1027
|
|
|
1122
1028
|
function preappend (blocks) {
|
|
1123
|
-
const offset = this.
|
|
1124
|
-
const fork = this.
|
|
1029
|
+
const offset = this.state.length
|
|
1030
|
+
const fork = this.state.fork
|
|
1031
|
+
|
|
1032
|
+
if (this.encryption.compat !== this.core.compat) this._updateEncryption()
|
|
1125
1033
|
|
|
1126
1034
|
for (let i = 0; i < blocks.length; i++) {
|
|
1127
1035
|
this.encryption.encrypt(offset + i, blocks[i], fork)
|
|
1128
1036
|
}
|
|
1129
1037
|
}
|
|
1130
1038
|
|
|
1131
|
-
function
|
|
1132
|
-
|
|
1133
|
-
// Only override the block encryption if it's either not already set or if
|
|
1134
|
-
// the caller provided a different key.
|
|
1135
|
-
if (core.encryption && b4a.equals(core.encryption.key, opts.encryptionKey) && core.encryption.compat === core.core.compat) return
|
|
1136
|
-
core.encryption = new BlockEncryption(opts.encryptionKey, core.key, { compat: core.core ? core.core.compat : true, isBlockKey: opts.isBlockKey })
|
|
1039
|
+
function isValidIndex (index) {
|
|
1040
|
+
return index === 0 || index > 0
|
|
1137
1041
|
}
|
|
1138
1042
|
|
|
1139
|
-
function
|
|
1140
|
-
|
|
1043
|
+
function maybeUnslab (block) {
|
|
1044
|
+
// Unslab only when it takes up less then half the slab
|
|
1045
|
+
return block !== null && 2 * block.byteLength < block.buffer.byteLength ? unslab(block) : block
|
|
1141
1046
|
}
|
|
1142
1047
|
|
|
1143
|
-
function
|
|
1144
|
-
|
|
1048
|
+
function checkSnapshot (snapshot, index) {
|
|
1049
|
+
if (index >= snapshot.state.snapshotCompatLength) throw SNAPSHOT_NOT_AVAILABLE()
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
function readBlock (rx, index) {
|
|
1053
|
+
const promise = rx.getBlock(index)
|
|
1054
|
+
rx.tryFlush()
|
|
1055
|
+
return promise
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
function initOnce (session, storage, key, opts) {
|
|
1059
|
+
if (storage === null) storage = opts.storage || null
|
|
1060
|
+
if (key === null) key = opts.key || null
|
|
1061
|
+
|
|
1062
|
+
session.core = new Core(Hypercore.defaultStorage(storage), {
|
|
1063
|
+
eagerUpgrade: true,
|
|
1064
|
+
notDownloadingLinger: opts.notDownloadingLinger,
|
|
1065
|
+
allowFork: opts.allowFork !== false,
|
|
1066
|
+
inflightRange: opts.inflightRange,
|
|
1067
|
+
compat: opts.compat === true,
|
|
1068
|
+
force: opts.force,
|
|
1069
|
+
createIfMissing: opts.createIfMissing,
|
|
1070
|
+
discoveryKey: opts.discoveryKey,
|
|
1071
|
+
overwrite: opts.overwrite,
|
|
1072
|
+
key,
|
|
1073
|
+
keyPair: opts.keyPair,
|
|
1074
|
+
legacy: opts.legacy,
|
|
1075
|
+
manifest: opts.manifest,
|
|
1076
|
+
globalCache: opts.globalCache || null // session is a temp option, not to be relied on unless you know what you are doing (no semver guarantees)
|
|
1077
|
+
})
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function maybeAddMonitor (name) {
|
|
1081
|
+
if (name === 'append' || name === 'truncate') return
|
|
1082
|
+
if (this._monitorIndex >= 0 || this.closing) return
|
|
1083
|
+
|
|
1084
|
+
if (this.core === null) {
|
|
1085
|
+
this._monitorIndex = -2
|
|
1086
|
+
} else {
|
|
1087
|
+
this.core.addMonitor(this)
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
function isSessionMoved (err) {
|
|
1092
|
+
return err.code === 'SESSION_MOVED'
|
|
1145
1093
|
}
|