hypercore 10.0.0-alpha.8 → 10.1.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 +148 -36
- package/index.js +575 -212
- package/lib/bitfield.js +110 -42
- package/lib/block-encryption.js +3 -2
- package/lib/block-store.js +10 -5
- package/lib/caps.js +32 -0
- package/lib/core.js +189 -47
- package/lib/download.js +22 -0
- package/lib/errors.js +50 -0
- package/lib/info.js +24 -0
- package/lib/merkle-tree.js +182 -106
- package/lib/messages.js +249 -168
- package/lib/oplog.js +6 -5
- package/lib/remote-bitfield.js +28 -7
- package/lib/replicator.js +1415 -624
- package/lib/streams.js +56 -0
- package/package.json +22 -16
- package/.github/workflows/test-node.yml +0 -23
- package/CHANGELOG.md +0 -37
- package/UPGRADE.md +0 -9
- package/examples/announce.js +0 -19
- package/examples/basic.js +0 -10
- package/examples/http.js +0 -123
- package/examples/lookup.js +0 -20
- package/lib/extensions.js +0 -76
- package/lib/protocol.js +0 -524
- package/lib/random-iterator.js +0 -46
- package/test/basic.js +0 -90
- package/test/bitfield.js +0 -71
- package/test/core.js +0 -290
- package/test/encodings.js +0 -18
- package/test/encryption.js +0 -85
- package/test/extension.js +0 -71
- package/test/helpers/index.js +0 -23
- package/test/merkle-tree.js +0 -518
- package/test/mutex.js +0 -137
- package/test/oplog.js +0 -399
- package/test/preload.js +0 -72
- package/test/replicate.js +0 -372
- package/test/sessions.js +0 -173
- package/test/user-data.js +0 -47
package/index.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
const { EventEmitter } = require('events')
|
|
2
|
-
const
|
|
2
|
+
const RAF = require('random-access-file')
|
|
3
3
|
const isOptions = require('is-options')
|
|
4
4
|
const hypercoreCrypto = require('hypercore-crypto')
|
|
5
5
|
const c = require('compact-encoding')
|
|
6
|
+
const b4a = require('b4a')
|
|
6
7
|
const Xache = require('xache')
|
|
7
8
|
const NoiseSecretStream = require('@hyperswarm/secret-stream')
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const fsctl = requireMaybe('fsctl') || { lock: noop, sparse: noop }
|
|
9
|
+
const Protomux = require('protomux')
|
|
11
10
|
|
|
12
11
|
const Replicator = require('./lib/replicator')
|
|
13
|
-
const Extensions = require('./lib/extensions')
|
|
14
12
|
const Core = require('./lib/core')
|
|
15
13
|
const BlockEncryption = require('./lib/block-encryption')
|
|
14
|
+
const Info = require('./lib/info')
|
|
15
|
+
const Download = require('./lib/download')
|
|
16
|
+
const { ReadStream, WriteStream } = require('./lib/streams')
|
|
17
|
+
const { BAD_ARGUMENT, SESSION_CLOSED, SESSION_NOT_WRITABLE, SNAPSHOT_NOT_AVAILABLE } = require('./lib/errors')
|
|
16
18
|
|
|
17
19
|
const promises = Symbol.for('hypercore.promises')
|
|
18
20
|
const inspect = Symbol.for('nodejs.util.inspect.custom')
|
|
@@ -31,13 +33,13 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
if (key && typeof key === 'string') {
|
|
34
|
-
key =
|
|
36
|
+
key = b4a.from(key, 'hex')
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
if (!opts) opts = {}
|
|
38
40
|
|
|
39
41
|
if (!opts.crypto && key && key.byteLength !== 32) {
|
|
40
|
-
throw
|
|
42
|
+
throw BAD_ARGUMENT('Hypercore key should be 32 bytes')
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
if (!storage) storage = opts.storage
|
|
@@ -49,25 +51,34 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
49
51
|
this.core = null
|
|
50
52
|
this.replicator = null
|
|
51
53
|
this.encryption = null
|
|
52
|
-
this.extensions =
|
|
54
|
+
this.extensions = new Map()
|
|
53
55
|
this.cache = opts.cache === true ? new Xache({ maxSize: 65536, maxAge: 0 }) : (opts.cache || null)
|
|
54
56
|
|
|
55
57
|
this.valueEncoding = null
|
|
58
|
+
this.encodeBatch = null
|
|
59
|
+
this.activeRequests = []
|
|
60
|
+
|
|
56
61
|
this.key = key || null
|
|
57
|
-
this.
|
|
62
|
+
this.keyPair = null
|
|
58
63
|
this.readable = true
|
|
59
64
|
this.writable = false
|
|
60
65
|
this.opened = false
|
|
61
66
|
this.closed = false
|
|
67
|
+
this.snapshotted = !!opts.snapshot
|
|
68
|
+
this.sparse = opts.sparse !== false
|
|
62
69
|
this.sessions = opts._sessions || [this]
|
|
63
|
-
this.
|
|
70
|
+
this.auth = opts.auth || null
|
|
64
71
|
this.autoClose = !!opts.autoClose
|
|
72
|
+
this.onwait = opts.onwait || null
|
|
73
|
+
this.wait = opts.wait !== false
|
|
65
74
|
|
|
66
75
|
this.closing = null
|
|
67
|
-
this.opening =
|
|
76
|
+
this.opening = this._openSession(key, storage, opts)
|
|
68
77
|
this.opening.catch(noop)
|
|
69
78
|
|
|
70
79
|
this._preappend = preappend.bind(this)
|
|
80
|
+
this._snapshot = null
|
|
81
|
+
this._findingPeers = 0
|
|
71
82
|
}
|
|
72
83
|
|
|
73
84
|
[inspect] (depth, opts) {
|
|
@@ -76,75 +87,284 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
76
87
|
while (indent.length < opts.indentationLvl) indent += ' '
|
|
77
88
|
}
|
|
78
89
|
|
|
90
|
+
let peers = ''
|
|
91
|
+
const min = Math.min(this.peers.length, 5)
|
|
92
|
+
|
|
93
|
+
for (let i = 0; i < min; i++) {
|
|
94
|
+
const peer = this.peers[i]
|
|
95
|
+
|
|
96
|
+
peers += indent + ' Peer(\n'
|
|
97
|
+
peers += indent + ' remotePublicKey: ' + opts.stylize(toHex(peer.remotePublicKey), 'string') + '\n'
|
|
98
|
+
peers += indent + ' remoteLength: ' + opts.stylize(peer.remoteLength, 'number') + '\n'
|
|
99
|
+
peers += indent + ' remoteFork: ' + opts.stylize(peer.remoteFork, 'number') + '\n'
|
|
100
|
+
peers += indent + ' remoteCanUpgrade: ' + opts.stylize(peer.remoteCanUpgrade, 'boolean') + '\n'
|
|
101
|
+
peers += indent + ' )' + '\n'
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (this.peers.length > 5) {
|
|
105
|
+
peers += indent + ' ... and ' + (this.peers.length - 5) + ' more\n'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (peers) peers = '[\n' + peers + indent + ' ]'
|
|
109
|
+
else peers = '[ ' + opts.stylize(0, 'number') + ' ]'
|
|
110
|
+
|
|
79
111
|
return this.constructor.name + '(\n' +
|
|
80
|
-
indent + ' key: ' + opts.stylize(
|
|
112
|
+
indent + ' key: ' + opts.stylize(toHex(this.key), 'string') + '\n' +
|
|
81
113
|
indent + ' discoveryKey: ' + opts.stylize(toHex(this.discoveryKey), 'string') + '\n' +
|
|
82
114
|
indent + ' opened: ' + opts.stylize(this.opened, 'boolean') + '\n' +
|
|
115
|
+
indent + ' closed: ' + opts.stylize(this.closed, 'boolean') + '\n' +
|
|
116
|
+
indent + ' snapshotted: ' + opts.stylize(this.snapshotted, 'boolean') + '\n' +
|
|
117
|
+
indent + ' sparse: ' + opts.stylize(this.sparse, 'boolean') + '\n' +
|
|
83
118
|
indent + ' writable: ' + opts.stylize(this.writable, 'boolean') + '\n' +
|
|
84
|
-
indent + ' sessions: ' + opts.stylize(this.sessions.length, 'number') + '\n' +
|
|
85
|
-
indent + ' peers: [ ' + opts.stylize(this.peers.length, 'number') + ' ]\n' +
|
|
86
119
|
indent + ' length: ' + opts.stylize(this.length, 'number') + '\n' +
|
|
87
|
-
indent + '
|
|
120
|
+
indent + ' fork: ' + opts.stylize(this.fork, 'number') + '\n' +
|
|
121
|
+
indent + ' sessions: [ ' + opts.stylize(this.sessions.length, 'number') + ' ]\n' +
|
|
122
|
+
indent + ' activeRequests: [ ' + opts.stylize(this.activeRequests.length, 'number') + ' ]\n' +
|
|
123
|
+
indent + ' peers: ' + peers + '\n' +
|
|
88
124
|
indent + ')'
|
|
89
125
|
}
|
|
90
126
|
|
|
91
|
-
static
|
|
92
|
-
|
|
93
|
-
|
|
127
|
+
static getProtocolMuxer (stream) {
|
|
128
|
+
return stream.noiseStream.userData
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static createProtocolStream (isInitiator, opts = {}) {
|
|
132
|
+
let outerStream = Protomux.isProtomux(isInitiator)
|
|
133
|
+
? isInitiator.stream
|
|
134
|
+
: isStream(isInitiator)
|
|
135
|
+
? isInitiator
|
|
136
|
+
: opts.stream
|
|
137
|
+
|
|
138
|
+
let noiseStream = null
|
|
139
|
+
|
|
140
|
+
if (outerStream) {
|
|
141
|
+
noiseStream = outerStream.noiseStream
|
|
142
|
+
} else {
|
|
143
|
+
noiseStream = new NoiseSecretStream(isInitiator, null, opts)
|
|
144
|
+
outerStream = noiseStream.rawStream
|
|
145
|
+
}
|
|
146
|
+
if (!noiseStream) throw BAD_ARGUMENT('Invalid stream')
|
|
147
|
+
|
|
148
|
+
if (!noiseStream.userData) {
|
|
149
|
+
const protocol = new Protomux(noiseStream)
|
|
150
|
+
|
|
151
|
+
if (opts.ondiscoverykey) {
|
|
152
|
+
protocol.pair({ protocol: 'hypercore/alpha' }, opts.ondiscoverykey)
|
|
153
|
+
}
|
|
154
|
+
if (opts.keepAlive !== false) {
|
|
155
|
+
noiseStream.setKeepAlive(5000)
|
|
156
|
+
}
|
|
157
|
+
noiseStream.userData = protocol
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return outerStream
|
|
94
161
|
}
|
|
95
162
|
|
|
96
163
|
static defaultStorage (storage, opts = {}) {
|
|
97
|
-
if (typeof storage !== 'string')
|
|
164
|
+
if (typeof storage !== 'string') {
|
|
165
|
+
if (!isRandomAccessClass(storage)) return storage
|
|
166
|
+
const Cls = storage // just to satisfy standard...
|
|
167
|
+
return name => new Cls(name)
|
|
168
|
+
}
|
|
169
|
+
|
|
98
170
|
const directory = storage
|
|
99
171
|
const toLock = opts.lock || 'oplog'
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
172
|
+
|
|
173
|
+
return createFile
|
|
174
|
+
|
|
175
|
+
function createFile (name) {
|
|
176
|
+
const lock = isFile(name, toLock)
|
|
177
|
+
const sparse = isFile(name, 'data') || isFile(name, 'bitfield') || isFile(name, 'tree')
|
|
178
|
+
return new RAF(name, { directory, lock, sparse })
|
|
105
179
|
}
|
|
180
|
+
|
|
181
|
+
function isFile (name, n) {
|
|
182
|
+
return name === n || name.endsWith('/' + n)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
snapshot (opts) {
|
|
187
|
+
return this.session({ ...opts, snapshot: true })
|
|
106
188
|
}
|
|
107
189
|
|
|
108
190
|
session (opts = {}) {
|
|
109
191
|
if (this.closing) {
|
|
110
192
|
// This makes the closing logic alot easier. If this turns out to be a problem
|
|
111
193
|
// in practive, open an issue and we'll try to make a solution for it.
|
|
112
|
-
throw
|
|
194
|
+
throw SESSION_CLOSED('Cannot make sessions on a closing core')
|
|
113
195
|
}
|
|
114
196
|
|
|
197
|
+
const sparse = opts.sparse === false ? false : this.sparse
|
|
198
|
+
const wait = opts.wait === false ? false : this.wait
|
|
199
|
+
const onwait = opts.onwait === undefined ? this.onwait : opts.onwait
|
|
115
200
|
const Clz = opts.class || Hypercore
|
|
116
|
-
const keyPair = opts.keyPair && opts.keyPair.secretKey && { ...opts.keyPair }
|
|
117
|
-
|
|
118
|
-
// This only works if the hypercore was fully loaded,
|
|
119
|
-
// but we only do this to validate the keypair to help catch bugs so yolo
|
|
120
|
-
if (this.key && keyPair) keyPair.publicKey = this.key
|
|
121
|
-
|
|
122
201
|
const s = new Clz(this.storage, this.key, {
|
|
123
202
|
...opts,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
203
|
+
sparse,
|
|
204
|
+
wait,
|
|
205
|
+
onwait,
|
|
127
206
|
_opening: this.opening,
|
|
128
207
|
_sessions: this.sessions
|
|
129
208
|
})
|
|
130
209
|
|
|
131
|
-
s.
|
|
210
|
+
s._passCapabilities(this)
|
|
211
|
+
|
|
212
|
+
// Configure the cache unless explicitly disabled.
|
|
213
|
+
if (opts.cache !== false) {
|
|
214
|
+
s.cache = opts.cache === true || !opts.cache ? this.cache : opts.cache
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
ensureEncryption(s, opts)
|
|
218
|
+
|
|
132
219
|
this.sessions.push(s)
|
|
133
220
|
|
|
134
221
|
return s
|
|
135
222
|
}
|
|
136
223
|
|
|
137
|
-
|
|
138
|
-
if (!this.
|
|
224
|
+
_passCapabilities (o) {
|
|
225
|
+
if (!this.auth) this.auth = o.auth
|
|
226
|
+
|
|
139
227
|
this.crypto = o.crypto
|
|
140
|
-
this.opened = o.opened
|
|
141
228
|
this.key = o.key
|
|
142
|
-
this.discoveryKey = o.discoveryKey
|
|
143
229
|
this.core = o.core
|
|
144
230
|
this.replicator = o.replicator
|
|
145
231
|
this.encryption = o.encryption
|
|
146
|
-
this.writable = !!this.sign
|
|
232
|
+
this.writable = !!(this.auth && this.auth.sign)
|
|
147
233
|
this.autoClose = o.autoClose
|
|
234
|
+
|
|
235
|
+
if (this.snapshotted && this.core && !this._snapshot) this._updateSnapshot()
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async _openFromExisting (from, opts) {
|
|
239
|
+
await from.opening
|
|
240
|
+
|
|
241
|
+
this._passCapabilities(from)
|
|
242
|
+
this.sessions = from.sessions
|
|
243
|
+
this.storage = from.storage
|
|
244
|
+
this.replicator.findingPeers += this._findingPeers
|
|
245
|
+
|
|
246
|
+
ensureEncryption(this, opts)
|
|
247
|
+
|
|
248
|
+
this.sessions.push(this)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async _openSession (key, storage, opts) {
|
|
252
|
+
const isFirst = !opts._opening
|
|
253
|
+
|
|
254
|
+
if (!isFirst) await opts._opening
|
|
255
|
+
if (opts.preload) opts = { ...opts, ...(await opts.preload()) }
|
|
256
|
+
|
|
257
|
+
const keyPair = (key && opts.keyPair)
|
|
258
|
+
? { ...opts.keyPair, publicKey: key }
|
|
259
|
+
: key
|
|
260
|
+
? { publicKey: key, secretKey: null }
|
|
261
|
+
: opts.keyPair
|
|
262
|
+
|
|
263
|
+
// This only works if the hypercore was fully loaded,
|
|
264
|
+
// but we only do this to validate the keypair to help catch bugs so yolo
|
|
265
|
+
if (this.key && keyPair) keyPair.publicKey = this.key
|
|
266
|
+
|
|
267
|
+
if (opts.auth) {
|
|
268
|
+
this.auth = opts.auth
|
|
269
|
+
} else if (opts.sign) {
|
|
270
|
+
this.auth = Core.createAuth(this.crypto, keyPair, opts)
|
|
271
|
+
} else if (keyPair && keyPair.secretKey) {
|
|
272
|
+
this.auth = Core.createAuth(this.crypto, keyPair)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (isFirst) {
|
|
276
|
+
await this._openCapabilities(keyPair, storage, opts)
|
|
277
|
+
// Only the root session should pass capabilities to other sessions.
|
|
278
|
+
for (let i = 0; i < this.sessions.length; i++) {
|
|
279
|
+
const s = this.sessions[i]
|
|
280
|
+
if (s !== this) s._passCapabilities(this)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (!this.auth) this.auth = this.core.defaultAuth
|
|
285
|
+
this.writable = !!this.auth.sign
|
|
286
|
+
|
|
287
|
+
if (opts.valueEncoding) {
|
|
288
|
+
this.valueEncoding = c.from(opts.valueEncoding)
|
|
289
|
+
}
|
|
290
|
+
if (opts.encodeBatch) {
|
|
291
|
+
this.encodeBatch = opts.encodeBatch
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Start continous replication if not in sparse mode.
|
|
295
|
+
if (!this.sparse) this.download({ start: 0, end: -1 })
|
|
296
|
+
|
|
297
|
+
// This is a hidden option that's only used by Corestore.
|
|
298
|
+
// It's required so that corestore can load a name from userData before 'ready' is emitted.
|
|
299
|
+
if (opts._preready) await opts._preready(this)
|
|
300
|
+
|
|
301
|
+
this.opened = true
|
|
302
|
+
this.emit('ready')
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async _openCapabilities (keyPair, storage, opts) {
|
|
306
|
+
if (opts.from) return this._openFromExisting(opts.from, opts)
|
|
307
|
+
|
|
308
|
+
this.storage = Hypercore.defaultStorage(opts.storage || storage)
|
|
309
|
+
|
|
310
|
+
this.core = await Core.open(this.storage, {
|
|
311
|
+
force: opts.force,
|
|
312
|
+
createIfMissing: opts.createIfMissing,
|
|
313
|
+
overwrite: opts.overwrite,
|
|
314
|
+
keyPair,
|
|
315
|
+
crypto: this.crypto,
|
|
316
|
+
legacy: opts.legacy,
|
|
317
|
+
auth: opts.auth,
|
|
318
|
+
onupdate: this._oncoreupdate.bind(this)
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
if (opts.userData) {
|
|
322
|
+
for (const [key, value] of Object.entries(opts.userData)) {
|
|
323
|
+
await this.core.userData(key, value)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
this.key = this.core.header.signer.publicKey
|
|
328
|
+
this.keyPair = this.core.header.signer
|
|
329
|
+
|
|
330
|
+
this.replicator = new Replicator(this.core, this.key, {
|
|
331
|
+
eagerUpdate: true,
|
|
332
|
+
allowFork: opts.allowFork !== false,
|
|
333
|
+
onpeerupdate: this._onpeerupdate.bind(this),
|
|
334
|
+
onupload: this._onupload.bind(this)
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
this.replicator.findingPeers += this._findingPeers
|
|
338
|
+
|
|
339
|
+
if (!this.encryption && opts.encryptionKey) {
|
|
340
|
+
this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
_getSnapshot () {
|
|
345
|
+
if (this.sparse) {
|
|
346
|
+
return {
|
|
347
|
+
length: this.core.tree.length,
|
|
348
|
+
byteLength: this.core.tree.byteLength,
|
|
349
|
+
fork: this.core.tree.fork,
|
|
350
|
+
compatLength: this.core.tree.length
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
length: this.core.header.contiguousLength,
|
|
356
|
+
byteLength: 0,
|
|
357
|
+
fork: this.core.tree.fork,
|
|
358
|
+
compatLength: this.core.header.contiguousLength
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
_updateSnapshot () {
|
|
363
|
+
const prev = this._snapshot
|
|
364
|
+
const next = this._snapshot = this._getSnapshot()
|
|
365
|
+
|
|
366
|
+
if (!prev) return true
|
|
367
|
+
return prev.length !== next.length || prev.fork !== next.fork
|
|
148
368
|
}
|
|
149
369
|
|
|
150
370
|
close () {
|
|
@@ -163,6 +383,20 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
163
383
|
this.readable = false
|
|
164
384
|
this.writable = false
|
|
165
385
|
this.closed = true
|
|
386
|
+
this.opened = false
|
|
387
|
+
|
|
388
|
+
const gc = []
|
|
389
|
+
for (const ext of this.extensions.values()) {
|
|
390
|
+
if (ext.session === this) gc.push(ext)
|
|
391
|
+
}
|
|
392
|
+
for (const ext of gc) ext.destroy()
|
|
393
|
+
|
|
394
|
+
if (this.replicator !== null) {
|
|
395
|
+
this.replicator.findingPeers -= this._findingPeers
|
|
396
|
+
this.replicator.clearRequests(this.activeRequests)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
this._findingPeers = 0
|
|
166
400
|
|
|
167
401
|
if (this.sessions.length) {
|
|
168
402
|
// if this is the last session and we are auto closing, trigger that first to enforce error handling
|
|
@@ -178,41 +412,63 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
178
412
|
}
|
|
179
413
|
|
|
180
414
|
replicate (isInitiator, opts = {}) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
let noiseStream = null
|
|
415
|
+
// Only limitation here is that ondiscoverykey doesn't work atm when passing a muxer directly,
|
|
416
|
+
// because it doesn't really make a lot of sense.
|
|
417
|
+
if (Protomux.isProtomux(isInitiator)) return this._attachToMuxer(isInitiator, opts)
|
|
185
418
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
outerStream = Hypercore.createProtocolStream(isInitiator, opts)
|
|
190
|
-
noiseStream = outerStream.noiseStream
|
|
191
|
-
}
|
|
192
|
-
if (!noiseStream) throw new Error('Invalid stream passed to replicate')
|
|
419
|
+
const protocolStream = Hypercore.createProtocolStream(isInitiator, opts)
|
|
420
|
+
const noiseStream = protocolStream.noiseStream
|
|
421
|
+
const protocol = noiseStream.userData
|
|
193
422
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
423
|
+
this._attachToMuxer(protocol, opts)
|
|
424
|
+
|
|
425
|
+
return protocolStream
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
_attachToMuxer (mux, opts) {
|
|
429
|
+
// If the user wants to, we can make this replication run in a session
|
|
430
|
+
// that way the core wont close "under them" during replication
|
|
431
|
+
if (opts.session) {
|
|
432
|
+
const s = this.session()
|
|
433
|
+
mux.stream.on('close', () => s.close().catch(noop))
|
|
198
434
|
}
|
|
199
435
|
|
|
200
|
-
const protocol = noiseStream.userData
|
|
201
436
|
if (this.opened) {
|
|
202
|
-
this.replicator.
|
|
437
|
+
this.replicator.attachTo(mux)
|
|
203
438
|
} else {
|
|
204
|
-
this.opening.then(() => this.replicator.
|
|
439
|
+
this.opening.then(() => this.replicator.attachTo(mux), mux.destroy.bind(mux))
|
|
205
440
|
}
|
|
206
441
|
|
|
207
|
-
return
|
|
442
|
+
return mux
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
get discoveryKey () {
|
|
446
|
+
return this.replicator === null ? null : this.replicator.discoveryKey
|
|
208
447
|
}
|
|
209
448
|
|
|
210
449
|
get length () {
|
|
211
|
-
|
|
450
|
+
if (this._snapshot) return this._snapshot.length
|
|
451
|
+
if (this.core === null) return 0
|
|
452
|
+
if (!this.sparse) return this.contiguousLength
|
|
453
|
+
return this.core.tree.length
|
|
212
454
|
}
|
|
213
455
|
|
|
456
|
+
/**
|
|
457
|
+
* Deprecated. Use `const { byteLength } = await core.info()`.
|
|
458
|
+
*/
|
|
214
459
|
get byteLength () {
|
|
215
|
-
|
|
460
|
+
if (this._snapshot) return this._snapshot.byteLength
|
|
461
|
+
if (this.core === null) return 0
|
|
462
|
+
if (!this.sparse) return this.contiguousByteLength
|
|
463
|
+
return this.core.tree.byteLength - (this.core.tree.length * this.padding)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
get contiguousLength () {
|
|
467
|
+
return this.core === null ? 0 : this.core.header.contiguousLength
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
get contiguousByteLength () {
|
|
471
|
+
return 0
|
|
216
472
|
}
|
|
217
473
|
|
|
218
474
|
get fork () {
|
|
@@ -235,111 +491,86 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
235
491
|
return this.opening
|
|
236
492
|
}
|
|
237
493
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
this.valueEncoding = opts.valueEncoding ? c.from(codecs(opts.valueEncoding)) : null
|
|
242
|
-
|
|
243
|
-
const keyPair = (key && opts.keyPair)
|
|
244
|
-
? { ...opts.keyPair, publicKey: key }
|
|
245
|
-
: key
|
|
246
|
-
? { publicKey: key, secretKey: null }
|
|
247
|
-
: opts.keyPair
|
|
494
|
+
_onupload (index, value, from) {
|
|
495
|
+
const byteLength = value.byteLength - this.padding
|
|
248
496
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
await from.opening
|
|
252
|
-
for (const [name, ext] of this.extensions) from.extensions.register(name, null, ext)
|
|
253
|
-
this._initSession(from)
|
|
254
|
-
this.extensions = from.extensions
|
|
255
|
-
this.sessions = from.sessions
|
|
256
|
-
this.storage = from.storage
|
|
257
|
-
if (!this.sign) this.sign = opts.sign || ((keyPair && keyPair.secretKey) ? Core.createSigner(this.crypto, keyPair) : null)
|
|
258
|
-
this.writable = !!this.sign
|
|
259
|
-
this.sessions.push(this)
|
|
260
|
-
return
|
|
497
|
+
for (let i = 0; i < this.sessions.length; i++) {
|
|
498
|
+
this.sessions[i].emit('upload', index, byteLength, from)
|
|
261
499
|
}
|
|
500
|
+
}
|
|
262
501
|
|
|
263
|
-
|
|
502
|
+
_oncoreupdate (status, bitfield, value, from) {
|
|
503
|
+
if (status !== 0) {
|
|
504
|
+
const truncatedNonSparse = (status & 0b1000) !== 0
|
|
505
|
+
const appendedNonSparse = (status & 0b0100) !== 0
|
|
506
|
+
const truncated = (status & 0b0010) !== 0
|
|
507
|
+
const appended = (status & 0b0001) !== 0
|
|
264
508
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
onupdate: this._oncoreupdate.bind(this)
|
|
269
|
-
})
|
|
509
|
+
if (truncated) {
|
|
510
|
+
this.replicator.ontruncate(bitfield.start)
|
|
511
|
+
}
|
|
270
512
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
await this.core.userData(key, value)
|
|
513
|
+
if ((status & 0b0011) !== 0) {
|
|
514
|
+
this.replicator.onupgrade()
|
|
274
515
|
}
|
|
275
|
-
}
|
|
276
516
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
})
|
|
517
|
+
for (let i = 0; i < this.sessions.length; i++) {
|
|
518
|
+
const s = this.sessions[i]
|
|
280
519
|
|
|
281
|
-
|
|
520
|
+
if (truncated) {
|
|
521
|
+
if (s.cache) s.cache.clear()
|
|
282
522
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
523
|
+
// If snapshotted, make sure to update our compat so we can fail gets
|
|
524
|
+
if (s._snapshot && bitfield.start < s._snapshot.compatLength) s._snapshot.compatLength = bitfield.start
|
|
525
|
+
}
|
|
286
526
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
527
|
+
if (s.sparse ? truncated : truncatedNonSparse) {
|
|
528
|
+
s.emit('truncate', bitfield.start, this.core.tree.fork)
|
|
529
|
+
}
|
|
290
530
|
|
|
291
|
-
|
|
292
|
-
|
|
531
|
+
// For sparse sessions, immediately emit appends. If non-sparse, emit if contig length has updated
|
|
532
|
+
if (s.sparse ? appended : appendedNonSparse) {
|
|
533
|
+
s.emit('append')
|
|
534
|
+
}
|
|
535
|
+
}
|
|
293
536
|
|
|
294
|
-
|
|
537
|
+
const contig = this.core.header.contiguousLength
|
|
295
538
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
}
|
|
539
|
+
// When the contig length catches up, broadcast the non-sparse length to peers
|
|
540
|
+
if (appendedNonSparse && contig === this.core.tree.length) {
|
|
541
|
+
for (const peer of this.peers) {
|
|
542
|
+
if (peer.broadcastedNonSparse) continue
|
|
302
543
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
306
|
-
if ((status & 0b10) !== 0) {
|
|
307
|
-
if (this.cache) this.cache.clear()
|
|
308
|
-
this.sessions[i].emit('truncate', this.core.tree.fork)
|
|
309
|
-
}
|
|
310
|
-
if ((status & 0b01) !== 0) {
|
|
311
|
-
this.sessions[i].emit('append')
|
|
544
|
+
peer.broadcastRange(0, contig)
|
|
545
|
+
peer.broadcastedNonSparse = true
|
|
312
546
|
}
|
|
313
547
|
}
|
|
314
|
-
|
|
315
|
-
this.replicator.broadcastInfo()
|
|
316
548
|
}
|
|
317
549
|
|
|
318
|
-
if (bitfield
|
|
319
|
-
|
|
320
|
-
this.replicator.broadcastBlock(bitfield.start + i)
|
|
321
|
-
}
|
|
550
|
+
if (bitfield) {
|
|
551
|
+
this.replicator.onhave(bitfield.start, bitfield.length, bitfield.drop)
|
|
322
552
|
}
|
|
323
553
|
|
|
324
554
|
if (value) {
|
|
325
|
-
|
|
326
|
-
this.encryption.decrypt(bitfield.start, value)
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
value = value.subarray(this.padding)
|
|
555
|
+
const byteLength = value.byteLength - this.padding
|
|
330
556
|
|
|
331
557
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
332
|
-
this.sessions[i].emit('download', bitfield.start,
|
|
558
|
+
this.sessions[i].emit('download', bitfield.start, byteLength, from)
|
|
333
559
|
}
|
|
334
560
|
}
|
|
335
561
|
}
|
|
336
562
|
|
|
337
563
|
_onpeerupdate (added, peer) {
|
|
338
|
-
if (added) this.extensions.update(peer)
|
|
339
564
|
const name = added ? 'peer-add' : 'peer-remove'
|
|
340
565
|
|
|
341
566
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
342
567
|
this.sessions[i].emit(name, peer)
|
|
568
|
+
|
|
569
|
+
if (added) {
|
|
570
|
+
for (const ext of this.sessions[i].extensions.values()) {
|
|
571
|
+
peer.extensions.set(ext.name, ext)
|
|
572
|
+
}
|
|
573
|
+
}
|
|
343
574
|
}
|
|
344
575
|
}
|
|
345
576
|
|
|
@@ -356,19 +587,72 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
356
587
|
return null
|
|
357
588
|
}
|
|
358
589
|
|
|
359
|
-
|
|
590
|
+
findingPeers () {
|
|
591
|
+
this._findingPeers++
|
|
592
|
+
if (this.replicator !== null && !this.closing) this.replicator.findingPeers++
|
|
593
|
+
|
|
594
|
+
let once = true
|
|
595
|
+
|
|
596
|
+
return () => {
|
|
597
|
+
if (this.closing || !once) return
|
|
598
|
+
once = false
|
|
599
|
+
this._findingPeers--
|
|
600
|
+
if (this.replicator !== null && --this.replicator.findingPeers === 0) {
|
|
601
|
+
this.replicator.updateAll()
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
async info () {
|
|
360
607
|
if (this.opened === false) await this.opening
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
608
|
+
|
|
609
|
+
return Info.from(this.core, this.padding, this._snapshot)
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
async update (opts) {
|
|
613
|
+
if (this.opened === false) await this.opening
|
|
614
|
+
if (this.closing !== null) return false
|
|
615
|
+
|
|
616
|
+
if (this.writable && (!opts || opts.force !== true)) {
|
|
617
|
+
if (!this.snapshotted) return false
|
|
618
|
+
return this._updateSnapshot()
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
622
|
+
const req = this.replicator.addUpgrade(activeRequests)
|
|
623
|
+
|
|
624
|
+
let upgraded = await req.promise
|
|
625
|
+
|
|
626
|
+
if (!this.sparse) {
|
|
627
|
+
// Download all available blocks in non-sparse mode
|
|
628
|
+
const start = this.length
|
|
629
|
+
const end = this.core.tree.length
|
|
630
|
+
const contig = this.contiguousLength
|
|
631
|
+
|
|
632
|
+
await this.download({ start, end, ifAvailable: true }).done()
|
|
633
|
+
|
|
634
|
+
if (!upgraded) upgraded = this.contiguousLength !== contig
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (!upgraded) return false
|
|
638
|
+
if (this.snapshotted) return this._updateSnapshot()
|
|
639
|
+
return true
|
|
364
640
|
}
|
|
365
641
|
|
|
366
|
-
async seek (bytes) {
|
|
642
|
+
async seek (bytes, opts) {
|
|
367
643
|
if (this.opened === false) await this.opening
|
|
368
644
|
|
|
369
645
|
const s = this.core.tree.seek(bytes, this.padding)
|
|
370
646
|
|
|
371
|
-
|
|
647
|
+
const offset = await s.update()
|
|
648
|
+
if (offset) return offset
|
|
649
|
+
|
|
650
|
+
if (this.closing !== null) throw SESSION_CLOSED()
|
|
651
|
+
|
|
652
|
+
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
653
|
+
const req = this.replicator.addSeek(activeRequests, s)
|
|
654
|
+
|
|
655
|
+
return req.promise
|
|
372
656
|
}
|
|
373
657
|
|
|
374
658
|
async has (index) {
|
|
@@ -379,62 +663,97 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
379
663
|
|
|
380
664
|
async get (index, opts) {
|
|
381
665
|
if (this.opened === false) await this.opening
|
|
382
|
-
|
|
383
|
-
if (
|
|
384
|
-
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
666
|
+
if (this.closing !== null) throw SESSION_CLOSED()
|
|
667
|
+
if (this._snapshot !== null && index >= this._snapshot.compatLength) throw SNAPSHOT_NOT_AVAILABLE()
|
|
668
|
+
|
|
669
|
+
const encoding = (opts && opts.valueEncoding && c.from(opts.valueEncoding)) || this.valueEncoding
|
|
670
|
+
|
|
671
|
+
let req = this.cache && this.cache.get(index)
|
|
672
|
+
if (!req) req = this._get(index, opts)
|
|
673
|
+
|
|
674
|
+
let block = await req
|
|
675
|
+
if (!block) return null
|
|
676
|
+
|
|
677
|
+
if (this.encryption) {
|
|
678
|
+
// Copy the block as it might be shared with other sessions.
|
|
679
|
+
block = b4a.from(block)
|
|
680
|
+
|
|
681
|
+
this.encryption.decrypt(index, block)
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
return this._decode(encoding, block)
|
|
388
685
|
}
|
|
389
686
|
|
|
390
|
-
async
|
|
391
|
-
|
|
687
|
+
async clear (start, end = start + 1, opts) {
|
|
688
|
+
if (this.opened === false) await this.opening
|
|
689
|
+
if (this.closing !== null) throw SESSION_CLOSED()
|
|
690
|
+
|
|
691
|
+
if (typeof end === 'object') {
|
|
692
|
+
opts = end
|
|
693
|
+
end = start + 1
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (start >= end) return
|
|
697
|
+
|
|
698
|
+
await this.core.clear(start, end)
|
|
699
|
+
}
|
|
392
700
|
|
|
701
|
+
async _get (index, opts) {
|
|
393
702
|
let block
|
|
394
703
|
|
|
395
704
|
if (this.core.bitfield.get(index)) {
|
|
396
|
-
block =
|
|
705
|
+
block = this.core.blocks.get(index)
|
|
706
|
+
|
|
707
|
+
if (this.cache) this.cache.set(index, block)
|
|
397
708
|
} else {
|
|
398
|
-
if (opts && opts.
|
|
399
|
-
|
|
709
|
+
if (opts && opts.wait === false) return null
|
|
710
|
+
if (this.wait === false && (!opts || !opts.wait)) return null
|
|
711
|
+
if (opts && opts.onwait) opts.onwait(index, this)
|
|
712
|
+
if (this.onwait) this.onwait(index, this)
|
|
713
|
+
|
|
714
|
+
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
715
|
+
|
|
716
|
+
const req = this.replicator.addBlock(activeRequests, index)
|
|
717
|
+
|
|
718
|
+
block = this._cacheOnResolve(index, req.promise, this.core.tree.fork)
|
|
400
719
|
}
|
|
401
720
|
|
|
402
|
-
|
|
403
|
-
return this._decode(encoding, block)
|
|
721
|
+
return block
|
|
404
722
|
}
|
|
405
723
|
|
|
406
|
-
|
|
407
|
-
const
|
|
724
|
+
async _cacheOnResolve (index, req, fork) {
|
|
725
|
+
const block = await req
|
|
408
726
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
727
|
+
if (this.cache && fork === this.core.tree.fork) {
|
|
728
|
+
this.cache.set(index, Promise.resolve(block))
|
|
729
|
+
}
|
|
412
730
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
? range.blocks
|
|
416
|
-
: new Set(range.blocks)
|
|
731
|
+
return block
|
|
732
|
+
}
|
|
417
733
|
|
|
418
|
-
|
|
419
|
-
|
|
734
|
+
createReadStream (opts) {
|
|
735
|
+
return new ReadStream(this, opts)
|
|
736
|
+
}
|
|
420
737
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
end = typeof (range && range.end) === 'number' ? range.end : -1 // download all
|
|
425
|
-
}
|
|
738
|
+
createWriteStream (opts) {
|
|
739
|
+
return new WriteStream(this, opts)
|
|
740
|
+
}
|
|
426
741
|
|
|
427
|
-
|
|
742
|
+
download (range) {
|
|
743
|
+
const req = this._download(range)
|
|
428
744
|
|
|
429
|
-
|
|
430
|
-
|
|
745
|
+
// do not crash in the background...
|
|
746
|
+
req.catch(noop)
|
|
431
747
|
|
|
432
|
-
return
|
|
748
|
+
return new Download(req)
|
|
433
749
|
}
|
|
434
750
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
751
|
+
async _download (range) {
|
|
752
|
+
if (this.opened === false) await this.opening
|
|
753
|
+
|
|
754
|
+
const activeRequests = (range && range.activeRequests) || this.activeRequests
|
|
755
|
+
|
|
756
|
+
return this.replicator.addRange(activeRequests, range)
|
|
438
757
|
}
|
|
439
758
|
|
|
440
759
|
// TODO: get rid of this / deprecate it?
|
|
@@ -442,12 +761,17 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
442
761
|
range.destroy(null)
|
|
443
762
|
}
|
|
444
763
|
|
|
764
|
+
// TODO: get rid of this / deprecate it?
|
|
765
|
+
cancel (request) {
|
|
766
|
+
// Do nothing for now
|
|
767
|
+
}
|
|
768
|
+
|
|
445
769
|
async truncate (newLength = 0, fork = -1) {
|
|
446
770
|
if (this.opened === false) await this.opening
|
|
447
|
-
if (this.writable === false) throw
|
|
771
|
+
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
448
772
|
|
|
449
773
|
if (fork === -1) fork = this.core.tree.fork + 1
|
|
450
|
-
await this.core.truncate(newLength, fork, this.
|
|
774
|
+
await this.core.truncate(newLength, fork, this.auth)
|
|
451
775
|
|
|
452
776
|
// TODO: Should propagate from an event triggered by the oplog
|
|
453
777
|
this.replicator.updateAll()
|
|
@@ -455,44 +779,92 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
455
779
|
|
|
456
780
|
async append (blocks) {
|
|
457
781
|
if (this.opened === false) await this.opening
|
|
458
|
-
if (this.writable === false) throw
|
|
782
|
+
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
459
783
|
|
|
460
784
|
blocks = Array.isArray(blocks) ? blocks : [blocks]
|
|
461
785
|
|
|
462
786
|
const preappend = this.encryption && this._preappend
|
|
463
|
-
const buffers = new Array(blocks.length)
|
|
464
787
|
|
|
465
|
-
|
|
466
|
-
|
|
788
|
+
const buffers = this.encodeBatch !== null ? this.encodeBatch(blocks) : new Array(blocks.length)
|
|
789
|
+
|
|
790
|
+
if (this.encodeBatch === null) {
|
|
791
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
792
|
+
buffers[i] = this._encode(this.valueEncoding, blocks[i])
|
|
793
|
+
}
|
|
467
794
|
}
|
|
468
795
|
|
|
469
|
-
return await this.core.append(buffers, this.
|
|
796
|
+
return await this.core.append(buffers, this.auth, { preappend })
|
|
470
797
|
}
|
|
471
798
|
|
|
472
|
-
|
|
473
|
-
|
|
799
|
+
async treeHash (length) {
|
|
800
|
+
if (length === undefined) {
|
|
801
|
+
await this.ready()
|
|
802
|
+
length = this.core.length
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
const roots = await this.core.tree.getRoots(length)
|
|
806
|
+
return this.crypto.tree(roots)
|
|
474
807
|
}
|
|
475
808
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
809
|
+
registerExtension (name, handlers = {}) {
|
|
810
|
+
if (this.extensions.has(name)) {
|
|
811
|
+
const ext = this.extensions.get(name)
|
|
812
|
+
ext.handlers = handlers
|
|
813
|
+
ext.encoding = c.from(handlers.encoding || c.buffer)
|
|
814
|
+
ext.session = this
|
|
815
|
+
return ext
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
const ext = {
|
|
819
|
+
name,
|
|
820
|
+
handlers,
|
|
821
|
+
encoding: c.from(handlers.encoding || c.buffer),
|
|
822
|
+
session: this,
|
|
823
|
+
send (message, peer) {
|
|
824
|
+
const buffer = c.encode(this.encoding, message)
|
|
825
|
+
peer.extension(name, buffer)
|
|
826
|
+
},
|
|
827
|
+
broadcast (message) {
|
|
828
|
+
const buffer = c.encode(this.encoding, message)
|
|
829
|
+
for (const peer of this.session.peers) {
|
|
830
|
+
peer.extension(name, buffer)
|
|
831
|
+
}
|
|
832
|
+
},
|
|
833
|
+
destroy () {
|
|
834
|
+
for (const peer of this.session.peers) {
|
|
835
|
+
if (peer.extensions.get(name) === ext) peer.extensions.delete(name)
|
|
836
|
+
}
|
|
837
|
+
this.session.extensions.delete(name)
|
|
838
|
+
},
|
|
839
|
+
_onmessage (state, peer) {
|
|
840
|
+
const m = this.encoding.decode(state)
|
|
841
|
+
if (this.handlers.onmessage) this.handlers.onmessage(m, peer)
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
this.extensions.set(name, ext)
|
|
846
|
+
for (const peer of this.peers) {
|
|
847
|
+
peer.extensions.set(name, ext)
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
return ext
|
|
479
851
|
}
|
|
480
852
|
|
|
481
853
|
_encode (enc, val) {
|
|
482
854
|
const state = { start: this.padding, end: this.padding, buffer: null }
|
|
483
855
|
|
|
484
|
-
if (
|
|
856
|
+
if (b4a.isBuffer(val)) {
|
|
485
857
|
if (state.start === 0) return val
|
|
486
858
|
state.end += val.byteLength
|
|
487
859
|
} else if (enc) {
|
|
488
860
|
enc.preencode(state, val)
|
|
489
861
|
} else {
|
|
490
|
-
val =
|
|
862
|
+
val = b4a.from(val)
|
|
491
863
|
if (state.start === 0) return val
|
|
492
864
|
state.end += val.byteLength
|
|
493
865
|
}
|
|
494
866
|
|
|
495
|
-
state.buffer =
|
|
867
|
+
state.buffer = b4a.allocUnsafe(state.end)
|
|
496
868
|
|
|
497
869
|
if (enc) enc.encode(state, val)
|
|
498
870
|
else state.buffer.set(val, state.start)
|
|
@@ -501,7 +873,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
501
873
|
}
|
|
502
874
|
|
|
503
875
|
_decode (enc, block) {
|
|
504
|
-
block = block.subarray(this.padding)
|
|
876
|
+
if (this.padding) block = block.subarray(this.padding)
|
|
505
877
|
if (enc) return c.decode(enc, block)
|
|
506
878
|
return block
|
|
507
879
|
}
|
|
@@ -513,29 +885,12 @@ function isStream (s) {
|
|
|
513
885
|
return typeof s === 'object' && s && typeof s.pipe === 'function'
|
|
514
886
|
}
|
|
515
887
|
|
|
516
|
-
function
|
|
517
|
-
|
|
518
|
-
return require(name)
|
|
519
|
-
} catch (_) {
|
|
520
|
-
return null
|
|
521
|
-
}
|
|
888
|
+
function isRandomAccessClass (fn) {
|
|
889
|
+
return !!(typeof fn === 'function' && fn.prototype && typeof fn.prototype.open === 'function')
|
|
522
890
|
}
|
|
523
891
|
|
|
524
892
|
function toHex (buf) {
|
|
525
|
-
return buf &&
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
function reduce (iter, fn, acc) {
|
|
529
|
-
for (const item of iter) acc = fn(acc, item)
|
|
530
|
-
return acc
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
function min (arr) {
|
|
534
|
-
return reduce(arr, (a, b) => Math.min(a, b), Infinity)
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
function max (arr) {
|
|
538
|
-
return reduce(arr, (a, b) => Math.max(a, b), -Infinity)
|
|
893
|
+
return buf && b4a.toString(buf, 'hex')
|
|
539
894
|
}
|
|
540
895
|
|
|
541
896
|
function preappend (blocks) {
|
|
@@ -546,3 +901,11 @@ function preappend (blocks) {
|
|
|
546
901
|
this.encryption.encrypt(offset + i, blocks[i], fork)
|
|
547
902
|
}
|
|
548
903
|
}
|
|
904
|
+
|
|
905
|
+
function ensureEncryption (core, opts) {
|
|
906
|
+
if (!opts.encryptionKey) return
|
|
907
|
+
// Only override the block encryption if its either not already set or if
|
|
908
|
+
// the caller provided a different key.
|
|
909
|
+
if (core.encryption && b4a.equals(core.encryption.key, opts.encryptionKey)) return
|
|
910
|
+
core.encryption = new BlockEncryption(opts.encryptionKey, core.key)
|
|
911
|
+
}
|