hypercore 10.38.2 → 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 +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 +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,68 +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
518
|
get signedLength () {
|
|
563
|
-
|
|
564
|
-
|
|
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()
|
|
565
522
|
|
|
566
|
-
|
|
567
|
-
return this.length
|
|
523
|
+
return flushed === -1 ? this.state.length : flushed
|
|
568
524
|
}
|
|
569
525
|
|
|
570
526
|
/**
|
|
571
527
|
* Deprecated. Use `const { byteLength } = await core.info()`.
|
|
572
528
|
*/
|
|
573
529
|
get byteLength () {
|
|
530
|
+
if (this.opened === false) return 0
|
|
574
531
|
if (this._snapshot) return this._snapshot.byteLength
|
|
575
|
-
|
|
576
|
-
if (!this.sparse) return this.contiguousByteLength
|
|
577
|
-
return this.core.tree.byteLength - (this.core.tree.length * this.padding)
|
|
532
|
+
return this.state.byteLength - (this.state.length * this.padding)
|
|
578
533
|
}
|
|
579
534
|
|
|
580
535
|
get contiguousLength () {
|
|
581
|
-
|
|
536
|
+
if (this.opened === false) return 0
|
|
537
|
+
return Math.min(this.core.state.length, this.core.header.hints.contiguousLength)
|
|
582
538
|
}
|
|
583
539
|
|
|
584
540
|
get contiguousByteLength () {
|
|
@@ -586,11 +542,12 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
586
542
|
}
|
|
587
543
|
|
|
588
544
|
get fork () {
|
|
589
|
-
|
|
545
|
+
if (this.opened === false) return 0
|
|
546
|
+
return this.state.fork
|
|
590
547
|
}
|
|
591
548
|
|
|
592
549
|
get peers () {
|
|
593
|
-
return this.
|
|
550
|
+
return this.opened === false ? [] : this.core.replicator.peers
|
|
594
551
|
}
|
|
595
552
|
|
|
596
553
|
get encryptionKey () {
|
|
@@ -602,155 +559,69 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
602
559
|
}
|
|
603
560
|
|
|
604
561
|
get globalCache () {
|
|
605
|
-
return this.
|
|
562
|
+
return this.opened === false ? null : this.core.globalCache
|
|
606
563
|
}
|
|
607
564
|
|
|
608
565
|
ready () {
|
|
609
566
|
return this.opening
|
|
610
567
|
}
|
|
611
568
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
616
|
-
this.sessions[i].emit('upload', index, byteLength, from)
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
_oninvalid (err, req, res, from) {
|
|
621
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
622
|
-
this.sessions[i].emit('verification-error', err, req, res, from)
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
async _oncoreconflict (proof, from) {
|
|
627
|
-
await this.replicator.onconflict(from)
|
|
628
|
-
|
|
629
|
-
for (const s of this.sessions) s.emit('conflict', proof.upgrade.length, proof.fork, proof)
|
|
630
|
-
|
|
631
|
-
const err = new Error('Two conflicting signatures exist for length ' + proof.upgrade.length)
|
|
632
|
-
await this._closeAllSessions(err)
|
|
569
|
+
async setUserData (key, value) {
|
|
570
|
+
if (this.opened === false) await this.opening
|
|
571
|
+
await this.state.setUserData(key, value)
|
|
633
572
|
}
|
|
634
573
|
|
|
635
|
-
async
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
for (const s of sessions) all.push(s.close(err))
|
|
642
|
-
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
|
|
643
580
|
}
|
|
644
581
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const truncatedNonSparse = (status & 0b1000) !== 0
|
|
648
|
-
const appendedNonSparse = (status & 0b0100) !== 0
|
|
649
|
-
const truncated = (status & 0b0010) !== 0
|
|
650
|
-
const appended = (status & 0b0001) !== 0
|
|
582
|
+
transferSession (core) {
|
|
583
|
+
// todo: validate we can move
|
|
651
584
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
if ((status & 0b10011) !== 0) {
|
|
657
|
-
this.replicator.onupgrade()
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
if (status & 0b10000) {
|
|
661
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
662
|
-
const s = this.sessions[i]
|
|
663
|
-
|
|
664
|
-
if (s.encryption && s.encryption.compat !== this.core.compat) {
|
|
665
|
-
s.encryption = new BlockEncryption(s.encryption.key, this.key, { compat: this.core.compat, isBlockKey: s.encryption.isBlockKey })
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
670
|
-
this.sessions[i].emit('manifest')
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
675
|
-
const s = this.sessions[i]
|
|
676
|
-
|
|
677
|
-
if (truncated) {
|
|
678
|
-
if (s.cache) s.cache.clear()
|
|
679
|
-
|
|
680
|
-
// If snapshotted, make sure to update our compat so we can fail gets
|
|
681
|
-
if (s._snapshot && bitfield.start < s._snapshot.compatLength) s._snapshot.compatLength = bitfield.start
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
if (s.sparse ? truncated : truncatedNonSparse) {
|
|
685
|
-
s.emit('truncate', bitfield.start, this.core.tree.fork)
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// For sparse sessions, immediately emit appends. If non-sparse, emit if contig length has updated
|
|
689
|
-
if (s.sparse ? appended : appendedNonSparse) {
|
|
690
|
-
s.emit('append')
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
const contig = this.core.header.hints.contiguousLength
|
|
695
|
-
|
|
696
|
-
// When the contig length catches up, broadcast the non-sparse length to peers
|
|
697
|
-
if (appendedNonSparse && contig === this.core.tree.length) {
|
|
698
|
-
for (const peer of this.peers) {
|
|
699
|
-
if (peer.broadcastedNonSparse) continue
|
|
700
|
-
|
|
701
|
-
peer.broadcastRange(0, contig)
|
|
702
|
-
peer.broadcastedNonSparse = true
|
|
703
|
-
}
|
|
704
|
-
}
|
|
585
|
+
if (this.weak === false) {
|
|
586
|
+
this.core.activeSessions--
|
|
587
|
+
core.activeSessions++
|
|
705
588
|
}
|
|
706
589
|
|
|
707
|
-
if (
|
|
708
|
-
this.
|
|
590
|
+
if (this._monitorIndex >= 0) {
|
|
591
|
+
this.core.removeMonitor(this)
|
|
592
|
+
core.addMonitor(this)
|
|
709
593
|
}
|
|
710
594
|
|
|
711
|
-
|
|
712
|
-
const byteLength = value.byteLength - this.padding
|
|
713
|
-
|
|
714
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
715
|
-
this.sessions[i].emit('download', bitfield.start, byteLength, from)
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
}
|
|
595
|
+
const old = this.core
|
|
719
596
|
|
|
720
|
-
|
|
721
|
-
const name = added ? 'peer-add' : 'peer-remove'
|
|
597
|
+
this.core = core
|
|
722
598
|
|
|
723
|
-
|
|
724
|
-
this.sessions[i].emit(name, peer)
|
|
599
|
+
old.replicator.clearRequests(this.activeRequests, SESSION_MOVED())
|
|
725
600
|
|
|
726
|
-
|
|
727
|
-
for (const ext of this.sessions[i].extensions.values()) {
|
|
728
|
-
peer.extensions.set(ext.name, ext)
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
}
|
|
601
|
+
this.emit('migrate', this.key)
|
|
732
602
|
}
|
|
733
603
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
return this.core.userData(key, value, flush)
|
|
604
|
+
createTreeBatch () {
|
|
605
|
+
return this.state.createTreeBatch()
|
|
737
606
|
}
|
|
738
607
|
|
|
739
|
-
async
|
|
608
|
+
async restoreBatch (length, additionalBlocks = []) {
|
|
740
609
|
if (this.opened === false) await this.opening
|
|
741
|
-
|
|
742
|
-
|
|
610
|
+
const batch = this.state.createTreeBatch()
|
|
611
|
+
|
|
612
|
+
if (length > batch.length + additionalBlocks.length) {
|
|
613
|
+
throw BAD_ARGUMENT('Insufficient additional blocks were passed')
|
|
743
614
|
}
|
|
744
|
-
return null
|
|
745
|
-
}
|
|
746
615
|
|
|
747
|
-
|
|
748
|
-
|
|
616
|
+
let i = 0
|
|
617
|
+
while (batch.length < length) batch.append(additionalBlocks[i++])
|
|
618
|
+
|
|
619
|
+
return length < batch.length ? batch.restore(length) : batch
|
|
749
620
|
}
|
|
750
621
|
|
|
751
622
|
findingPeers () {
|
|
752
623
|
this._findingPeers++
|
|
753
|
-
if (this.
|
|
624
|
+
if (this.core !== null && !this.closing) this.core.replicator.findingPeers++
|
|
754
625
|
|
|
755
626
|
let once = true
|
|
756
627
|
|
|
@@ -758,8 +629,8 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
758
629
|
if (this.closing || !once) return
|
|
759
630
|
once = false
|
|
760
631
|
this._findingPeers--
|
|
761
|
-
if (this.
|
|
762
|
-
this.replicator.updateAll()
|
|
632
|
+
if (this.core !== null && --this.core.replicator.findingPeers === 0) {
|
|
633
|
+
this.core.replicator.updateAll()
|
|
763
634
|
}
|
|
764
635
|
}
|
|
765
636
|
}
|
|
@@ -773,42 +644,40 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
773
644
|
async update (opts) {
|
|
774
645
|
if (this.opened === false) await this.opening
|
|
775
646
|
if (this.closing !== null) return false
|
|
647
|
+
if (this.snapshotted) return false
|
|
776
648
|
|
|
777
|
-
if (this.writable && (!opts || opts.force !== true))
|
|
778
|
-
if (!this.snapshotted) return false
|
|
779
|
-
return this._updateSnapshot()
|
|
780
|
-
}
|
|
649
|
+
if (this.writable && (!opts || opts.force !== true)) return false
|
|
781
650
|
|
|
782
|
-
const remoteWait = this._shouldWait(opts, this.replicator.findingPeers > 0)
|
|
651
|
+
const remoteWait = this._shouldWait(opts, this.core.replicator.findingPeers > 0)
|
|
783
652
|
|
|
784
653
|
let upgraded = false
|
|
785
654
|
|
|
786
|
-
if (await this.replicator.applyPendingReorg()) {
|
|
655
|
+
if (await this.core.replicator.applyPendingReorg()) {
|
|
787
656
|
upgraded = true
|
|
788
657
|
}
|
|
789
658
|
|
|
790
659
|
if (!upgraded && remoteWait) {
|
|
791
660
|
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
792
|
-
const req = this.replicator.addUpgrade(activeRequests)
|
|
661
|
+
const req = this.core.replicator.addUpgrade(activeRequests)
|
|
793
662
|
|
|
794
|
-
|
|
663
|
+
try {
|
|
664
|
+
upgraded = await req.promise
|
|
665
|
+
} catch (err) {
|
|
666
|
+
if (isSessionMoved(err)) return this.update(opts)
|
|
667
|
+
throw err
|
|
668
|
+
}
|
|
795
669
|
}
|
|
796
670
|
|
|
797
671
|
if (!upgraded) return false
|
|
798
|
-
if (this.snapshotted) return this._updateSnapshot()
|
|
799
672
|
return true
|
|
800
673
|
}
|
|
801
674
|
|
|
802
|
-
batch ({ checkout = -1, autoClose = true, session = true, restore = false, clear = false } = {}) {
|
|
803
|
-
return new Batch(session ? this.session() : this, checkout, autoClose, restore, clear)
|
|
804
|
-
}
|
|
805
|
-
|
|
806
675
|
async seek (bytes, opts) {
|
|
807
676
|
if (this.opened === false) await this.opening
|
|
808
677
|
if (!isValidIndex(bytes)) throw ASSERTION('seek is invalid')
|
|
809
678
|
|
|
810
|
-
const tree = (opts && opts.tree) || this.core.tree
|
|
811
|
-
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)
|
|
812
681
|
|
|
813
682
|
const offset = await s.update()
|
|
814
683
|
if (offset) return offset
|
|
@@ -818,22 +687,47 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
818
687
|
if (!this._shouldWait(opts, this.wait)) return null
|
|
819
688
|
|
|
820
689
|
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
821
|
-
const req = this.replicator.addSeek(activeRequests, s)
|
|
690
|
+
const req = this.core.replicator.addSeek(activeRequests, s)
|
|
822
691
|
|
|
823
692
|
const timeout = opts && opts.timeout !== undefined ? opts.timeout : this.timeout
|
|
824
693
|
if (timeout) req.context.setTimeout(req, timeout)
|
|
825
694
|
|
|
826
|
-
|
|
695
|
+
try {
|
|
696
|
+
return await req.promise
|
|
697
|
+
} catch (err) {
|
|
698
|
+
if (isSessionMoved(err)) return this.seek(bytes, opts)
|
|
699
|
+
throw err
|
|
700
|
+
}
|
|
827
701
|
}
|
|
828
702
|
|
|
829
703
|
async has (start, end = start + 1) {
|
|
830
704
|
if (this.opened === false) await this.opening
|
|
831
705
|
if (!isValidIndex(start) || !isValidIndex(end)) throw ASSERTION('has range is invalid')
|
|
832
706
|
|
|
833
|
-
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
|
+
}
|
|
834
721
|
|
|
835
|
-
|
|
836
|
-
|
|
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)
|
|
837
731
|
}
|
|
838
732
|
|
|
839
733
|
async get (index, opts) {
|
|
@@ -841,12 +735,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
841
735
|
if (!isValidIndex(index)) throw ASSERTION('block index is invalid')
|
|
842
736
|
|
|
843
737
|
if (this.closing !== null) throw SESSION_CLOSED()
|
|
844
|
-
if (this._snapshot !== null && index >= this._snapshot.compatLength) throw SNAPSHOT_NOT_AVAILABLE()
|
|
845
738
|
|
|
846
739
|
const encoding = (opts && opts.valueEncoding && c.from(opts.valueEncoding)) || this.valueEncoding
|
|
847
740
|
|
|
848
|
-
|
|
849
|
-
if (!req) req = this._get(index, opts)
|
|
741
|
+
const req = this._get(index, opts)
|
|
850
742
|
|
|
851
743
|
let block = await req
|
|
852
744
|
if (!block) return null
|
|
@@ -857,6 +749,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
857
749
|
// Copy the block as it might be shared with other sessions.
|
|
858
750
|
block = b4a.from(block)
|
|
859
751
|
|
|
752
|
+
if (this.encryption.compat !== this.core.compat) this._updateEncryption()
|
|
860
753
|
this.encryption.decrypt(index, block)
|
|
861
754
|
}
|
|
862
755
|
|
|
@@ -879,7 +772,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
879
772
|
if (start >= end) return cleared
|
|
880
773
|
if (start >= this.length) return cleared
|
|
881
774
|
|
|
882
|
-
await this.
|
|
775
|
+
await this.state.clear(start, end, cleared)
|
|
883
776
|
|
|
884
777
|
return cleared
|
|
885
778
|
}
|
|
@@ -890,46 +783,55 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
890
783
|
}
|
|
891
784
|
|
|
892
785
|
async _get (index, opts) {
|
|
893
|
-
|
|
786
|
+
if (this.core.isFlushing) await this.core.flushed()
|
|
894
787
|
|
|
895
|
-
|
|
896
|
-
const tree = (opts && opts.tree) || this.core.tree
|
|
897
|
-
block = this.core.blocks.get(index, tree)
|
|
788
|
+
const block = await readBlock(this.state.storage.read(), index)
|
|
898
789
|
|
|
899
|
-
|
|
900
|
-
} else {
|
|
901
|
-
if (!this._shouldWait(opts, this.wait)) return null
|
|
790
|
+
if (block !== null) return block
|
|
902
791
|
|
|
903
|
-
|
|
904
|
-
if (this.onwait) this.onwait(index, this)
|
|
905
|
-
|
|
906
|
-
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
792
|
+
if (this.closing !== null) throw SESSION_CLOSED()
|
|
907
793
|
|
|
908
|
-
|
|
909
|
-
|
|
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)
|
|
910
798
|
|
|
911
|
-
|
|
912
|
-
if (
|
|
799
|
+
checkSnapshot(this, index)
|
|
800
|
+
if (coreBlock !== null) return coreBlock
|
|
801
|
+
}
|
|
913
802
|
|
|
914
|
-
|
|
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
|
|
915
809
|
}
|
|
916
810
|
|
|
917
|
-
return
|
|
918
|
-
|
|
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)
|
|
919
815
|
|
|
920
|
-
|
|
921
|
-
const resolved = await req
|
|
816
|
+
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
922
817
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
? unslab(resolved)
|
|
926
|
-
: resolved
|
|
818
|
+
const req = this.core.replicator.addBlock(activeRequests, index)
|
|
819
|
+
req.snapshot = index < this.length
|
|
927
820
|
|
|
928
|
-
|
|
929
|
-
|
|
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
|
|
930
831
|
}
|
|
931
832
|
|
|
932
|
-
|
|
833
|
+
if (this._snapshot !== null) checkSnapshot(this, index)
|
|
834
|
+
return maybeUnslab(replicatedBlock)
|
|
933
835
|
}
|
|
934
836
|
|
|
935
837
|
_shouldWait (opts, defaultValue) {
|
|
@@ -953,19 +855,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
953
855
|
}
|
|
954
856
|
|
|
955
857
|
download (range) {
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
// do not crash in the background...
|
|
959
|
-
req.catch(safetyCatch)
|
|
960
|
-
|
|
961
|
-
return new Download(req)
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
async _download (range) {
|
|
965
|
-
if (this.opened === false) await this.opening
|
|
966
|
-
|
|
967
|
-
const activeRequests = (range && range.activeRequests) || this.activeRequests
|
|
968
|
-
return this.replicator.addRange(activeRequests, range)
|
|
858
|
+
return new Download(this, range)
|
|
969
859
|
}
|
|
970
860
|
|
|
971
861
|
// TODO: get rid of this / deprecate it?
|
|
@@ -982,27 +872,31 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
982
872
|
if (this.opened === false) await this.opening
|
|
983
873
|
|
|
984
874
|
const {
|
|
985
|
-
fork = this.
|
|
875
|
+
fork = this.state.fork + 1,
|
|
986
876
|
keyPair = this.keyPair,
|
|
987
877
|
signature = null
|
|
988
878
|
} = typeof opts === 'number' ? { fork: opts } : opts
|
|
989
879
|
|
|
880
|
+
const isDefault = this.state === this.core.state
|
|
990
881
|
const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))
|
|
991
|
-
if (writable === false && (newLength > 0 || fork !== this.
|
|
882
|
+
if (isDefault && writable === false && (newLength > 0 || fork !== this.state.fork)) throw SESSION_NOT_WRITABLE()
|
|
992
883
|
|
|
993
|
-
await this.
|
|
884
|
+
await this.state.truncate(newLength, fork, { keyPair, signature })
|
|
994
885
|
|
|
995
886
|
// TODO: Should propagate from an event triggered by the oplog
|
|
996
|
-
this.replicator.updateAll()
|
|
887
|
+
if (this.state === this.core.state) this.core.replicator.updateAll()
|
|
997
888
|
}
|
|
998
889
|
|
|
999
890
|
async append (blocks, opts = {}) {
|
|
1000
891
|
if (this.opened === false) await this.opening
|
|
1001
892
|
|
|
1002
|
-
const
|
|
1003
|
-
const
|
|
893
|
+
const isDefault = this.state === this.core.state
|
|
894
|
+
const defaultKeyPair = this.state.name === null ? this.keyPair : null
|
|
1004
895
|
|
|
1005
|
-
|
|
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()
|
|
1006
900
|
|
|
1007
901
|
blocks = Array.isArray(blocks) ? blocks : [blocks]
|
|
1008
902
|
|
|
@@ -1021,17 +915,17 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
1021
915
|
}
|
|
1022
916
|
}
|
|
1023
917
|
|
|
1024
|
-
return this.
|
|
918
|
+
return this.state.append(buffers, { keyPair, signature, preappend })
|
|
1025
919
|
}
|
|
1026
920
|
|
|
1027
921
|
async treeHash (length) {
|
|
1028
922
|
if (length === undefined) {
|
|
1029
923
|
await this.ready()
|
|
1030
|
-
length = this.
|
|
924
|
+
length = this.state.length
|
|
1031
925
|
}
|
|
1032
926
|
|
|
1033
|
-
const roots = await this.
|
|
1034
|
-
return
|
|
927
|
+
const roots = await this.state.tree.getRoots(length)
|
|
928
|
+
return crypto.tree(roots)
|
|
1035
929
|
}
|
|
1036
930
|
|
|
1037
931
|
registerExtension (name, handlers = {}) {
|
|
@@ -1071,6 +965,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
1071
965
|
}
|
|
1072
966
|
|
|
1073
967
|
this.extensions.set(name, ext)
|
|
968
|
+
|
|
969
|
+
if (this.core === null) this._monitorIndex = -2
|
|
970
|
+
else this.core.addMonitor(this)
|
|
971
|
+
|
|
1074
972
|
for (const peer of this.peers) {
|
|
1075
973
|
peer.extensions.set(name, ext)
|
|
1076
974
|
}
|
|
@@ -1109,41 +1007,87 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
1109
1007
|
}
|
|
1110
1008
|
return block
|
|
1111
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
|
+
}
|
|
1112
1016
|
}
|
|
1113
1017
|
|
|
1018
|
+
module.exports = Hypercore
|
|
1019
|
+
|
|
1114
1020
|
function isStream (s) {
|
|
1115
1021
|
return typeof s === 'object' && s && typeof s.pipe === 'function'
|
|
1116
1022
|
}
|
|
1117
1023
|
|
|
1118
|
-
function isRandomAccessClass (fn) {
|
|
1119
|
-
return !!(typeof fn === 'function' && fn.prototype && typeof fn.prototype.open === 'function')
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
1024
|
function toHex (buf) {
|
|
1123
1025
|
return buf && b4a.toString(buf, 'hex')
|
|
1124
1026
|
}
|
|
1125
1027
|
|
|
1126
1028
|
function preappend (blocks) {
|
|
1127
|
-
const offset = this.
|
|
1128
|
-
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()
|
|
1129
1033
|
|
|
1130
1034
|
for (let i = 0; i < blocks.length; i++) {
|
|
1131
1035
|
this.encryption.encrypt(offset + i, blocks[i], fork)
|
|
1132
1036
|
}
|
|
1133
1037
|
}
|
|
1134
1038
|
|
|
1135
|
-
function
|
|
1136
|
-
|
|
1137
|
-
// Only override the block encryption if it's either not already set or if
|
|
1138
|
-
// the caller provided a different key.
|
|
1139
|
-
if (core.encryption && b4a.equals(core.encryption.key, opts.encryptionKey) && core.encryption.compat === core.core.compat) return
|
|
1140
|
-
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
|
|
1141
1041
|
}
|
|
1142
1042
|
|
|
1143
|
-
function
|
|
1144
|
-
|
|
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
|
|
1145
1046
|
}
|
|
1146
1047
|
|
|
1147
|
-
function
|
|
1148
|
-
|
|
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'
|
|
1149
1093
|
}
|