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