hypercore 9.12.0 → 10.0.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/.github/workflows/test-node.yml +3 -4
  2. package/README.md +131 -404
  3. package/__snapshots__/test/storage.js.snapshot.cjs +15 -0
  4. package/examples/announce.js +19 -0
  5. package/examples/basic.js +10 -0
  6. package/examples/http.js +123 -0
  7. package/examples/lookup.js +20 -0
  8. package/index.js +365 -1600
  9. package/lib/bitfield.js +113 -285
  10. package/lib/block-encryption.js +68 -0
  11. package/lib/block-store.js +58 -0
  12. package/lib/core.js +468 -0
  13. package/lib/extensions.js +76 -0
  14. package/lib/merkle-tree.js +1110 -0
  15. package/lib/messages.js +571 -0
  16. package/lib/mutex.js +39 -0
  17. package/lib/oplog.js +224 -0
  18. package/lib/protocol.js +525 -0
  19. package/lib/random-iterator.js +46 -0
  20. package/lib/remote-bitfield.js +24 -0
  21. package/lib/replicator.js +857 -0
  22. package/lib/streams.js +39 -0
  23. package/package.json +44 -45
  24. package/test/basic.js +59 -471
  25. package/test/bitfield.js +48 -133
  26. package/test/core.js +290 -0
  27. package/test/encodings.js +18 -0
  28. package/test/encryption.js +123 -0
  29. package/test/extension.js +71 -0
  30. package/test/helpers/index.js +23 -0
  31. package/test/merkle-tree.js +518 -0
  32. package/test/mutex.js +137 -0
  33. package/test/oplog.js +399 -0
  34. package/test/preload.js +72 -0
  35. package/test/replicate.js +227 -824
  36. package/test/sessions.js +173 -0
  37. package/test/storage.js +31 -0
  38. package/test/streams.js +39 -146
  39. package/test/user-data.js +47 -0
  40. package/bench/all.sh +0 -65
  41. package/bench/copy-64kb-blocks.js +0 -51
  42. package/bench/helpers/read-throttled.js +0 -27
  43. package/bench/helpers/read.js +0 -47
  44. package/bench/helpers/write.js +0 -29
  45. package/bench/read-16kb-blocks-proof-throttled.js +0 -1
  46. package/bench/read-16kb-blocks-proof.js +0 -1
  47. package/bench/read-16kb-blocks-throttled.js +0 -1
  48. package/bench/read-16kb-blocks.js +0 -1
  49. package/bench/read-512b-blocks.js +0 -1
  50. package/bench/read-64kb-blocks-linear-batch.js +0 -18
  51. package/bench/read-64kb-blocks-linear.js +0 -18
  52. package/bench/read-64kb-blocks-proof.js +0 -1
  53. package/bench/read-64kb-blocks.js +0 -1
  54. package/bench/replicate-16kb-blocks.js +0 -19
  55. package/bench/replicate-64kb-blocks.js +0 -19
  56. package/bench/write-16kb-blocks.js +0 -1
  57. package/bench/write-512b-blocks.js +0 -1
  58. package/bench/write-64kb-blocks-static.js +0 -1
  59. package/bench/write-64kb-blocks.js +0 -1
  60. package/example.js +0 -23
  61. package/lib/cache.js +0 -26
  62. package/lib/crypto.js +0 -5
  63. package/lib/replicate.js +0 -829
  64. package/lib/safe-buffer-equals.js +0 -6
  65. package/lib/storage.js +0 -421
  66. package/lib/tree-index.js +0 -183
  67. package/test/ack.js +0 -306
  68. package/test/audit.js +0 -36
  69. package/test/cache.js +0 -93
  70. package/test/compat.js +0 -209
  71. package/test/copy.js +0 -377
  72. package/test/default-storage.js +0 -51
  73. package/test/extensions.js +0 -137
  74. package/test/get.js +0 -64
  75. package/test/head.js +0 -65
  76. package/test/helpers/create-tracking-ram.js +0 -27
  77. package/test/helpers/create.js +0 -6
  78. package/test/helpers/replicate.js +0 -4
  79. package/test/seek.js +0 -234
  80. package/test/selections.js +0 -95
  81. package/test/set-uploading-downloading.js +0 -91
  82. package/test/stats.js +0 -77
  83. package/test/timeouts.js +0 -22
  84. package/test/tree-index.js +0 -841
  85. package/test/update.js +0 -156
  86. package/test/value-encoding.js +0 -52
package/index.js CHANGED
@@ -1,1785 +1,550 @@
1
- var low = require('last-one-wins')
2
- var remove = require('unordered-array-remove')
3
- var set = require('unordered-set')
4
- var MerkleGenerator = require('merkle-tree-stream/generator')
5
- var flat = require('flat-tree')
6
- var codecs = require('codecs')
7
- var batcher = require('atomic-batcher')
8
- var inherits = require('inherits')
9
- var bitfield = require('./lib/bitfield')
10
- var sparseBitfield = require('sparse-bitfield')
11
- var treeIndex = require('./lib/tree-index')
12
- var storage = require('./lib/storage')
13
- var crypto = require('hypercore-crypto')
14
- var inspect = require('inspect-custom-symbol')
15
- var pretty = require('pretty-hash')
16
- var Nanoguard = require('nanoguard')
17
- var safeBufferEquals = require('./lib/safe-buffer-equals')
18
- var replicate = require('./lib/replicate')
19
- var Protocol = require('hypercore-protocol')
20
- var Message = require('abstract-extension')
21
- var Nanoresource = require('nanoresource/emitter')
22
- var defaultStorage = require('hypercore-default-storage')
23
- var { WriteStream, ReadStream } = require('hypercore-streams')
24
-
25
- class Extension extends Message {
26
- broadcast (message) {
27
- const feed = this.local.handlers
28
- const buf = this.encoding.encode(message)
29
- let broadcasted = false
30
- for (const peer of feed.peers) {
31
- broadcasted = true
32
- peer.extension(this.id, buf)
33
- }
34
- return broadcasted
35
- }
1
+ const { EventEmitter } = require('events')
2
+ const raf = require('random-access-file')
3
+ const isOptions = require('is-options')
4
+ const hypercoreCrypto = require('hypercore-crypto')
5
+ const c = require('compact-encoding')
6
+ const b4a = require('b4a')
7
+ const Xache = require('xache')
8
+ const NoiseSecretStream = require('@hyperswarm/secret-stream')
9
+ const codecs = require('codecs')
36
10
 
37
- send (message, peer) {
38
- peer.extension(this.id, this.encode(message))
39
- }
40
- }
41
-
42
- var defaultCrypto = {
43
- sign (data, sk, cb) {
44
- return cb(null, crypto.sign(data, sk))
45
- },
46
- verify (sig, data, pk, cb) {
47
- return cb(null, crypto.verify(sig, data, pk))
48
- }
49
- }
11
+ const fsctl = requireMaybe('fsctl') || { lock: noop, sparse: noop }
50
12
 
51
- module.exports = Feed
13
+ const Replicator = require('./lib/replicator')
14
+ const Extensions = require('./lib/extensions')
15
+ const Core = require('./lib/core')
16
+ const BlockEncryption = require('./lib/block-encryption')
17
+ const { ReadStream } = require('./lib/streams')
52
18
 
53
- function Feed (createStorage, key, opts) {
54
- if (!(this instanceof Feed)) return new Feed(createStorage, key, opts)
55
- Nanoresource.call(this)
19
+ const promises = Symbol.for('hypercore.promises')
20
+ const inspect = Symbol.for('nodejs.util.inspect.custom')
56
21
 
57
- if (typeof createStorage === 'string') createStorage = defaultStorageDir(createStorage)
58
- if (typeof createStorage !== 'function') throw new Error('Storage should be a function or string')
22
+ module.exports = class Hypercore extends EventEmitter {
23
+ constructor (storage, key, opts) {
24
+ super()
59
25
 
60
- if (typeof key === 'string') key = Buffer.from(key, 'hex')
61
-
62
- if (!Buffer.isBuffer(key) && !opts) {
63
- opts = key
64
- key = null
65
- }
66
-
67
- if (!opts) opts = {}
68
-
69
- var self = this
70
-
71
- var secretKey = opts.secretKey || null
72
- if (typeof secretKey === 'string') secretKey = Buffer.from(secretKey, 'hex')
73
-
74
- this.noiseKeyPair = opts.noiseKeyPair || Protocol.keyPair()
75
- this.live = opts.live !== false
76
- this.sparse = !!opts.sparse
77
- this.length = 0
78
- this.byteLength = 0
79
- this.maxRequests = opts.maxRequests || 16
80
- this.key = key || opts.key || null
81
- this.discoveryKey = this.key && crypto.discoveryKey(this.key)
82
- this.secretKey = secretKey
83
- this.bitfield = null
84
- this.tree = null
85
- this.writable = !!opts.writable
86
- this.readable = true
87
- this.downloading = opts.downloading !== false
88
- this.uploading = opts.uploading !== false
89
- this.allowPush = !!opts.allowPush
90
- this.peers = []
91
- this.ifAvailable = new Nanoguard()
92
- this.extensions = Extension.createLocal(this) // set Feed as the handlers
93
-
94
- this.crypto = opts.crypto || defaultCrypto
95
-
96
- // hooks
97
- this._onwrite = opts.onwrite || null
98
-
99
- this._force = !!opts.force
100
- this._expectedLength = -1
101
- this._indexing = !!opts.indexing
102
- this._createIfMissing = opts.createIfMissing !== false
103
- this._overwrite = !!opts.overwrite
104
- this._storeSecretKey = opts.storeSecretKey !== false
105
- this._alwaysIfAvailable = !!opts.ifAvailable
106
- this._merkle = null
107
- this._storage = storage(createStorage, opts)
108
- this._batch = batcher(this._onwrite ? workHook : work)
109
-
110
- this.timeouts = opts.timeouts || {
111
- get (cb) {
112
- cb(null)
113
- },
114
- update (cb) {
115
- cb(null)
26
+ if (isOptions(storage)) {
27
+ opts = storage
28
+ storage = null
29
+ key = null
30
+ } else if (isOptions(key)) {
31
+ opts = key
32
+ key = null
116
33
  }
117
- }
118
-
119
- this._seq = 0
120
- this._waiting = []
121
- this._selections = []
122
- this._reserved = sparseBitfield()
123
- this._synced = null
124
- this._downloadingSet = typeof opts.downloading === 'boolean'
125
-
126
- this._stats = (typeof opts.stats !== 'undefined' && !opts.stats) ? null : {
127
- downloadedBlocks: 0,
128
- downloadedBytes: 0,
129
- uploadedBlocks: 0,
130
- uploadedBytes: 0
131
- }
132
34
 
133
- this._codec = toCodec(opts.valueEncoding)
134
- this._sync = low(sync)
135
- if (!this.sparse) this.download({ start: 0, end: -1 })
136
-
137
- if (this.sparse && opts.eagerUpdate) {
138
- this.update(function loop (err) {
139
- if (err) self.emit('update-error', err)
140
- self.update(loop)
141
- })
142
- }
35
+ if (key && typeof key === 'string') {
36
+ key = b4a.from(key, 'hex')
37
+ }
143
38
 
144
- // open it right away
145
- this.open(onerror)
39
+ if (!opts) opts = {}
146
40
 
147
- function onerror (err) {
148
- if (err) self.emit('error', err)
149
- }
41
+ if (!opts.crypto && key && key.byteLength !== 32) {
42
+ throw new Error('Hypercore key should be 32 bytes')
43
+ }
150
44
 
151
- function workHook (values, cb) {
152
- if (!self._merkle) return self._reloadMerkleStateBeforeAppend(workHook, values, cb)
153
- self._appendHook(values, cb)
154
- }
45
+ if (!storage) storage = opts.storage
155
46
 
156
- function work (values, cb) {
157
- if (!self._merkle) return self._reloadMerkleStateBeforeAppend(work, values, cb)
158
- self._append(values, cb)
159
- }
47
+ this[promises] = true
160
48
 
161
- function sync (_, cb) {
162
- self._syncBitfield(cb)
163
- }
164
- }
49
+ this.storage = null
50
+ this.crypto = opts.crypto || hypercoreCrypto
51
+ this.core = null
52
+ this.replicator = null
53
+ this.encryption = null
54
+ this.extensions = opts.extensions || new Extensions()
55
+ this.cache = opts.cache === true ? new Xache({ maxSize: 65536, maxAge: 0 }) : (opts.cache || null)
165
56
 
166
- inherits(Feed, Nanoresource)
57
+ this.valueEncoding = null
58
+ this.key = key || null
59
+ this.discoveryKey = null
60
+ this.readable = true
61
+ this.writable = false
62
+ this.opened = false
63
+ this.closed = false
64
+ this.sessions = opts._sessions || [this]
65
+ this.sign = opts.sign || null
66
+ this.autoClose = !!opts.autoClose
167
67
 
168
- Feed.discoveryKey = crypto.discoveryKey
68
+ this.closing = null
69
+ this.opening = opts._opening || this._open(key, storage, opts)
70
+ this.opening.catch(noop)
169
71
 
170
- Feed.prototype[inspect] = function (depth, opts) {
171
- var indent = ''
172
- if (typeof opts.indentationLvl === 'number') {
173
- while (indent.length < opts.indentationLvl) indent += ' '
72
+ this._preappend = preappend.bind(this)
174
73
  }
175
- return 'Hypercore(\n' +
176
- indent + ' key: ' + opts.stylize((this.key && pretty(this.key)), 'string') + '\n' +
177
- indent + ' discoveryKey: ' + opts.stylize((this.discoveryKey && pretty(this.discoveryKey)), 'string') + '\n' +
178
- indent + ' opened: ' + opts.stylize(this.opened, 'boolean') + '\n' +
179
- indent + ' sparse: ' + opts.stylize(this.sparse, 'boolean') + '\n' +
180
- indent + ' writable: ' + opts.stylize(this.writable, 'boolean') + '\n' +
181
- indent + ' length: ' + opts.stylize(this.length, 'number') + '\n' +
182
- indent + ' byteLength: ' + opts.stylize(this.byteLength, 'number') + '\n' +
183
- indent + ' peers: ' + opts.stylize(this.peers.length, 'number') + '\n' +
184
- indent + ')'
185
- }
186
74
 
187
- // TODO: instead of using a getter, update on remote-update/add/remove
188
- Object.defineProperty(Feed.prototype, 'remoteLength', {
189
- enumerable: true,
190
- get: function () {
191
- var len = 0
192
- for (var i = 0; i < this.peers.length; i++) {
193
- var remoteLength = this.peers[i].remoteLength
194
- if (remoteLength > len) len = remoteLength
75
+ [inspect] (depth, opts) {
76
+ let indent = ''
77
+ if (typeof opts.indentationLvl === 'number') {
78
+ while (indent.length < opts.indentationLvl) indent += ' '
195
79
  }
196
- return len
197
- }
198
- })
199
-
200
- Object.defineProperty(Feed.prototype, 'stats', {
201
- enumerable: true,
202
- get: function () {
203
- if (!this._stats) return null
204
- var peerStats = []
205
- for (var i = 0; i < this.peers.length; i++) {
206
- var peer = this.peers[i]
207
- peerStats[i] = peer.stats
208
- }
209
- return {
210
- peers: peerStats,
211
- totals: this._stats
212
- }
213
- }
214
- })
215
80
 
216
- Feed.prototype.replicate = function (initiator, opts) {
217
- if ((!this._selections.length || this._selections[0].end !== -1) && !this.sparse && !(opts && opts.live)) {
218
- // hack!! proper fix is to refactor ./replicate to *not* clear our non-sparse selection
219
- this.download({ start: 0, end: -1 })
81
+ return this.constructor.name + '(\n' +
82
+ indent + ' key: ' + opts.stylize((toHex(this.key)), 'string') + '\n' +
83
+ indent + ' discoveryKey: ' + opts.stylize(toHex(this.discoveryKey), 'string') + '\n' +
84
+ indent + ' opened: ' + opts.stylize(this.opened, 'boolean') + '\n' +
85
+ indent + ' writable: ' + opts.stylize(this.writable, 'boolean') + '\n' +
86
+ indent + ' sessions: ' + opts.stylize(this.sessions.length, 'number') + '\n' +
87
+ indent + ' peers: [ ' + opts.stylize(this.peers.length, 'number') + ' ]\n' +
88
+ indent + ' length: ' + opts.stylize(this.length, 'number') + '\n' +
89
+ indent + ' byteLength: ' + opts.stylize(this.byteLength, 'number') + '\n' +
90
+ indent + ')'
220
91
  }
221
92
 
222
- if (isOptions(initiator) && !opts) {
223
- opts = initiator
224
- initiator = opts.initiator
93
+ static createProtocolStream (isInitiator, opts) {
94
+ const noiseStream = new NoiseSecretStream(isInitiator, null, opts)
95
+ return noiseStream.rawStream
225
96
  }
226
97
 
227
- opts = opts || {}
228
- opts.stats = !!this._stats
229
- opts.noise = !(opts.noise === false && opts.encrypted === false)
230
-
231
- return replicate(this, initiator, opts)
232
- }
233
-
234
- Feed.prototype.registerExtension = function (name, handlers) {
235
- return this.extensions.add(name, handlers)
236
- }
237
-
238
- Feed.prototype.onextensionupdate = function () {
239
- for (const peer of this.peers) peer._updateOptions()
240
- }
241
-
242
- Feed.prototype.setDownloading = function (downloading) {
243
- if (this.downloading === downloading && this._downloadingSet) return
244
- this.downloading = downloading
245
- this._downloadingSet = true
246
- this.ready((err) => {
247
- if (err) return
248
- for (const peer of this.peers) peer.setDownloading(this.downloading)
249
- })
250
- }
251
-
252
- Feed.prototype.setUploading = function (uploading) {
253
- if (uploading === this.uploading) return
254
- this.uploading = uploading
255
- this.ready((err) => {
256
- if (err) return
257
- for (const peer of this.peers) peer.setUploading(this.uploading)
258
- })
259
- }
260
-
261
- // Alias the nanoresource open method
262
- Feed.prototype.ready = Feed.prototype.open
263
-
264
- Feed.prototype.update = function (opts, cb) {
265
- if (typeof opts === 'function') return this.update(-1, opts)
266
- if (typeof opts === 'number') opts = { minLength: opts }
267
- if (!opts) opts = {}
268
- if (!cb) cb = noop
269
-
270
- var self = this
271
- var len = typeof opts.minLength === 'number' ? opts.minLength : -1
272
-
273
- this.ready(function (err) {
274
- if (err) return cb(err)
275
- if (len === -1) len = self.length + 1
276
- if (self.length >= len) return cb(null)
277
-
278
- const ifAvailable = typeof opts.ifAvailable === 'boolean'
279
- ? opts.ifAvailable
280
- : self._alwaysIfAvailable
281
-
282
- if (ifAvailable && self.writable && !opts.force) return cb(new Error('No update available from peers'))
283
- if (self.writable) cb = self._writeStateReloader(cb)
284
-
285
- var w = {
286
- hash: opts.hash !== false,
287
- bytes: 0,
288
- index: len - 1,
289
- options: opts,
290
- update: true,
291
- callback: cb
98
+ static defaultStorage (storage, opts = {}) {
99
+ if (typeof storage !== 'string') return storage
100
+ const directory = storage
101
+ const toLock = opts.lock || 'oplog'
102
+ return function createFile (name) {
103
+ const locked = name === toLock || name.endsWith('/' + toLock)
104
+ const lock = locked ? fsctl.lock : null
105
+ const sparse = locked ? null : null // fsctl.sparse, disable sparse on windows - seems to fail for some people. TODO: investigate
106
+ return raf(name, { directory, lock, sparse })
292
107
  }
108
+ }
293
109
 
294
- self._waiting.push(w)
295
- if (ifAvailable) self._ifAvailable(w, len)
296
- self._updatePeers()
297
- })
298
- }
299
-
300
- // Used to hint to the update guard if it can bail early
301
- Feed.prototype.setExpectedLength = function (len) {
302
- this._expectedLength = len
303
- this.ready((err) => {
304
- if (err) return
305
-
306
- this.ifAvailable.ready(() => {
307
- this._expectedLength = -1
308
- })
309
-
310
- if (this._expectedLength === -1 || this._expectedLength > this.length) return
311
-
312
- for (const w of this._waiting) {
313
- if (w.update && w.ifAvailable) w.callback(new Error('Expected length is less than current length'))
110
+ session (opts = {}) {
111
+ if (this.closing) {
112
+ // This makes the closing logic alot easier. If this turns out to be a problem
113
+ // in practive, open an issue and we'll try to make a solution for it.
114
+ throw new Error('Cannot make sessions on a closing core')
314
115
  }
315
- })
316
- }
317
-
318
- // Beware! This might break your core if you share forks with other people through replication
319
- Feed.prototype.truncate = function (newLength, cb) {
320
- if (!cb) cb = noop
321
- const self = this
322
-
323
- this.ready(function (err) {
324
- if (err) return cb(err)
325
116
 
326
- self._roots(newLength, function (err, roots) {
327
- if (err) return cb(err)
117
+ const Clz = opts.class || Hypercore
118
+ const keyPair = opts.keyPair && opts.keyPair.secretKey && { ...opts.keyPair }
328
119
 
329
- const oldLength = self.length
330
- if (oldLength <= newLength) return cb(null)
120
+ // This only works if the hypercore was fully loaded,
121
+ // but we only do this to validate the keypair to help catch bugs so yolo
122
+ if (this.key && keyPair) keyPair.publicKey = this.key
331
123
 
332
- let byteLength = 0
333
- for (const { size } of roots) byteLength += size
334
-
335
- for (let i = oldLength; i < newLength; i++) self.data.set(i, false)
336
- self.byteLength = byteLength
337
- self.length = newLength
338
- self.tree.truncate(2 * newLength)
339
- self._merkle = new MerkleGenerator(crypto, roots)
340
-
341
- self._sync(null, function (err) {
342
- if (err) return cb(err)
343
- self._storage.deleteSignatures(newLength, oldLength, cb)
344
- })
124
+ const s = new Clz(this.storage, this.key, {
125
+ ...opts,
126
+ sign: opts.sign || (keyPair && keyPair.secretKey && Core.createSigner(this.crypto, keyPair)) || this.sign,
127
+ valueEncoding: this.valueEncoding,
128
+ extensions: this.extensions,
129
+ _opening: this.opening,
130
+ _sessions: this.sessions
345
131
  })
346
- })
347
- }
348
-
349
- Feed.prototype._ifAvailable = function (w, minLength) {
350
- var cb = w.callback
351
- var called = false
352
- var self = this
353
132
 
354
- w.callback = done
355
- w.ifAvailable = true
133
+ s._initSession(this)
134
+ this.sessions.push(s)
356
135
 
357
- if (this._expectedLength > -1 && this._expectedLength <= this.length) {
358
- return process.nextTick(w.callback, new Error('Expected length is less than current length'))
136
+ return s
359
137
  }
360
138
 
361
- this.timeouts.update(function () {
362
- if (self.closed) return done(new Error('Closed'))
363
-
364
- process.nextTick(readyNT, self.ifAvailable, function () {
365
- if (self.closed) return done(new Error('Closed'))
366
- if (self.length >= minLength || self.remoteLength >= minLength) return
367
- done(new Error('No update available from peers'))
368
- })
369
- })
370
-
371
- function done (err) {
372
- if (called) return
373
- called = true
374
-
375
- var i = self._waiting.indexOf(w)
376
- if (i > -1) remove(self._waiting, i)
377
- cb(err)
139
+ _initSession (o) {
140
+ if (!this.sign) this.sign = o.sign
141
+ this.crypto = o.crypto
142
+ this.opened = o.opened
143
+ this.key = o.key
144
+ this.discoveryKey = o.discoveryKey
145
+ this.core = o.core
146
+ this.replicator = o.replicator
147
+ this.encryption = o.encryption
148
+ this.writable = !!this.sign
149
+ this.autoClose = o.autoClose
378
150
  }
379
- }
380
-
381
- Feed.prototype._ifAvailableGet = function (w) {
382
- var cb = w.callback
383
- var called = false
384
- var self = this
385
-
386
- w.callback = done
387
-
388
- self.timeouts.get(function () {
389
- if (self.closed) return done(new Error('Closed'))
390
-
391
- process.nextTick(readyNT, self.ifAvailable, function () {
392
- if (self.closed) return done(new Error('Closed'))
393
151
 
394
- for (var i = 0; i < self.peers.length; i++) {
395
- var peer = self.peers[i]
396
- if (peer.remoteBitfield.get(w.index)) return
397
- }
398
- done(new Error('Block not available from peers'))
399
- })
400
- })
401
-
402
- function done (err, data) {
403
- if (called) return
404
- called = true
405
-
406
- var i = self._waiting.indexOf(w)
407
- if (i > -1) remove(self._waiting, i)
408
- cb(err, data)
409
- }
410
- }
411
-
412
- // will reload the writable state. used by .update on a writable peer
413
- Feed.prototype._writeStateReloader = function (cb) {
414
- var self = this
415
- return function (err) {
416
- if (err) return cb(err)
417
- self._reloadMerkleState(cb)
152
+ close () {
153
+ if (this.closing) return this.closing
154
+ this.closing = this._close()
155
+ return this.closing
418
156
  }
419
- }
420
-
421
- Feed.prototype._reloadMerkleState = function (cb) {
422
- var self = this
423
157
 
424
- this._roots(self.length, function (err, roots) {
425
- if (err) return cb(err)
426
- self._merkle = new MerkleGenerator(crypto, roots)
427
- cb(null)
428
- })
429
- }
430
-
431
- Feed.prototype._reloadMerkleStateBeforeAppend = function (work, values, cb) {
432
- this._reloadMerkleState(function (err) {
433
- if (err) return cb(err)
434
- work(values, cb)
435
- })
436
- }
158
+ async _close () {
159
+ await this.opening
437
160
 
438
- Feed.prototype._open = function (cb) {
439
- var self = this
440
- var generatedKey = false
441
- var retryOpen = true
161
+ const i = this.sessions.indexOf(this)
162
+ if (i === -1) return
442
163
 
443
- // TODO: clean up the duplicate code below ...
164
+ this.sessions.splice(i, 1)
165
+ this.readable = false
166
+ this.writable = false
167
+ this.closed = true
444
168
 
445
- this._storage.openKey(function (_, key) {
446
- if (key && !self._overwrite && !self.key) self.key = key
447
-
448
- if (!self.key && self.live) {
449
- var keyPair = crypto.keyPair()
450
- self.secretKey = keyPair.secretKey
451
- self.key = keyPair.publicKey
452
- generatedKey = true
453
- }
454
-
455
- self.discoveryKey = self.key && crypto.discoveryKey(self.key)
456
- self._storage.open({ key: self.key, discoveryKey: self.discoveryKey }, onopen)
457
- })
458
-
459
- function onopen (err, state) {
460
- if (err) return cb(err)
461
-
462
- // if no key but we have data do a bitfield reset since we cannot verify the data.
463
- if (!state.key && state.bitfield.length) {
464
- self._overwrite = true
169
+ if (this.sessions.length) {
170
+ // if this is the last session and we are auto closing, trigger that first to enforce error handling
171
+ if (this.sessions.length === 1 && this.autoClose) await this.sessions[0].close()
172
+ // emit "fake" close as this is a session
173
+ this.emit('close', false)
174
+ return
465
175
  }
466
176
 
467
- if (self._force && state.key && self.key && Buffer.compare(state.key, self.key) !== 0) {
468
- self._overwrite = true
469
- }
177
+ await this.core.close()
470
178
 
471
- if (self._overwrite) {
472
- state.bitfield = []
473
- state.key = state.secretKey = null
474
- }
179
+ this.emit('close', true)
180
+ }
475
181
 
476
- self.bitfield = bitfield(state.bitfieldPageSize, state.bitfield)
477
- self.tree = treeIndex(self.bitfield.tree)
478
- self.length = self.tree.blocks()
479
- self._seq = self.length
182
+ replicate (isInitiator, opts = {}) {
183
+ let outerStream = isStream(isInitiator)
184
+ ? isInitiator
185
+ : opts.stream
186
+ let noiseStream = null
480
187
 
481
- if (state.key && self.key && Buffer.compare(state.key, self.key) !== 0) {
482
- return self._forceClose(cb, new Error('Another hypercore is stored here'))
188
+ if (outerStream) {
189
+ noiseStream = outerStream.noiseStream
190
+ } else {
191
+ outerStream = Hypercore.createProtocolStream(isInitiator, opts)
192
+ noiseStream = outerStream.noiseStream
483
193
  }
194
+ if (!noiseStream) throw new Error('Invalid stream passed to replicate')
484
195
 
485
- if (state.key) self.key = state.key
486
- if (state.secretKey) self.secretKey = state.secretKey
487
-
488
- if (!self.length) return onsignature(null, null)
489
- self._storage.getSignature(self.length - 1, onsignature)
490
-
491
- function onsignature (_, sig) {
492
- if (self.length) self.live = !!sig
493
-
494
- if ((generatedKey || !self.key) && !self._createIfMissing) {
495
- return self._forceClose(cb, new Error('No hypercore is stored here'))
496
- }
497
-
498
- if (!self.key && self.live) {
499
- var keyPair = crypto.keyPair()
500
- self.secretKey = keyPair.secretKey
501
- self.key = keyPair.publicKey
502
- }
503
-
504
- var writable = !!self.secretKey || self.key === null
505
-
506
- if (!writable && self.writable) return self._forceClose(cb, new Error('Feed is not writable'))
507
- self.writable = writable
508
- if (!self._downloadingSet) self.downloading = !writable
509
- self.discoveryKey = self.key && crypto.discoveryKey(self.key)
510
-
511
- if (self._storeSecretKey && !self.secretKey) {
512
- self._storeSecretKey = false
513
- }
514
-
515
- var shouldWriteKey = generatedKey || !safeBufferEquals(self.key, state.key)
516
- var shouldWriteSecretKey = self._storeSecretKey && (generatedKey || !safeBufferEquals(self.secretKey, state.secretKey))
517
-
518
- var missing = 1 +
519
- (shouldWriteKey ? 1 : 0) +
520
- (shouldWriteSecretKey ? 1 : 0) +
521
- (self._overwrite ? 1 : 0)
522
- var error = null
523
-
524
- if (shouldWriteKey) self._storage.key.write(0, self.key, done)
525
- if (shouldWriteSecretKey) self._storage.secretKey.write(0, self.secretKey, done)
526
-
527
- if (self._overwrite) {
528
- self._storage.bitfield.del(32, Infinity, done)
529
- }
530
-
531
- done(null)
532
-
533
- function done (err) {
534
- if (err) error = err
535
- if (--missing) return
536
- if (error) return self._forceClose(cb, error)
537
- self._roots(self.length, onroots)
538
- }
539
-
540
- function onroots (err, roots) {
541
- if (err && retryOpen) {
542
- retryOpen = false
543
- self.length--
544
- self._storage.getSignature(self.length - 1, onsignature)
545
- return
546
- }
547
-
548
- if (err) return self._forceClose(cb, err)
549
-
550
- self._merkle = new MerkleGenerator(crypto, roots)
551
- self.byteLength = roots.reduce(addSize, 0)
552
- self.emit('ready')
553
-
554
- cb(null)
555
- }
196
+ if (!noiseStream.userData) {
197
+ const protocol = Replicator.createProtocol(noiseStream)
198
+ noiseStream.userData = protocol
199
+ noiseStream.on('error', noop) // All noise errors already propagate through outerStream
556
200
  }
557
- }
558
- }
559
201
 
560
- Feed.prototype.download = function (range, cb) {
561
- if (typeof range === 'function') return this.download(null, range)
562
- if (typeof range === 'number') range = { start: range, end: range + 1 }
563
- if (Array.isArray(range)) range = { blocks: range }
564
- if (!range) range = {}
565
- if (!cb) cb = noop
566
- if (!this.readable) return cb(new Error('Feed is closed'))
567
-
568
- // TODO: if no peers, check if range is already satisfied and nextTick(cb) if so
569
- // this._updatePeers does this for us when there is a peer though, so not critical
570
-
571
- // We need range.start, end for the want messages so make sure to infer these
572
- // when blocks are passed and start,end is not set
573
- if (range.blocks && typeof range.start !== 'number') {
574
- var min = -1
575
- var max = 0
576
-
577
- for (var i = 0; i < range.blocks.length; i++) {
578
- const blk = range.blocks[i]
579
- if (min === -1 || blk < min) min = blk
580
- if (blk >= max) max = blk + 1
202
+ const protocol = noiseStream.userData
203
+ if (this.opened) {
204
+ this.replicator.joinProtocol(protocol, this.key, this.discoveryKey)
205
+ } else {
206
+ this.opening.then(() => this.replicator.joinProtocol(protocol, this.key, this.discoveryKey), protocol.destroy.bind(protocol))
581
207
  }
582
208
 
583
- range.start = min === -1 ? 0 : min
584
- range.end = max
209
+ return outerStream
585
210
  }
586
211
 
587
- var sel = {
588
- _index: this._selections.length,
589
- hash: !!range.hash,
590
- iterator: null,
591
- start: range.start || 0,
592
- end: range.end || -1,
593
- want: 0,
594
- linear: !!range.linear,
595
- blocks: range.blocks || null,
596
- blocksDownloaded: 0,
597
- requested: 0,
598
- callback: cb
212
+ get length () {
213
+ return this.core === null ? 0 : this.core.tree.length
599
214
  }
600
215
 
601
- sel.want = toWantRange(sel.start)
602
-
603
- this._selections.push(sel)
604
- this._updatePeers()
605
-
606
- return sel
607
- }
608
-
609
- Feed.prototype.undownload = function (range) {
610
- if (typeof range === 'number') range = { start: range, end: range + 1 }
611
- if (!range) range = {}
612
-
613
- if (range.callback && range._index > -1) {
614
- set.remove(this._selections, range)
615
- process.nextTick(range.callback, createError('ECANCELED', -11, 'Download was cancelled'))
616
- return
216
+ get byteLength () {
217
+ return this.core === null ? 0 : this.core.tree.byteLength - (this.core.tree.length * this.padding)
617
218
  }
618
219
 
619
- var start = range.start || 0
620
- var end = range.end || -1
621
- var hash = !!range.hash
622
- var linear = !!range.linear
623
-
624
- for (var i = 0; i < this._selections.length; i++) {
625
- var s = this._selections[i]
626
-
627
- if (s.start === start && s.end === end && s.hash === hash && s.linear === linear) {
628
- set.remove(this._selections, s)
629
- process.nextTick(range.callback, createError('ECANCELED', -11, 'Download was cancelled'))
630
- return
631
- }
220
+ get fork () {
221
+ return this.core === null ? 0 : this.core.tree.fork
632
222
  }
633
- }
634
-
635
- Feed.prototype.digest = function (index) {
636
- return this.tree.digest(2 * index)
637
- }
638
223
 
639
- Feed.prototype.proof = function (index, opts, cb) {
640
- if (typeof opts === 'function') return this.proof(index, null, opts)
641
- if (!this.opened) return this._readyAndProof(index, opts, cb)
642
- if (!opts) opts = {}
643
-
644
- var proof = this.tree.proof(2 * index, opts)
645
- if (!proof) return cb(new Error('No proof available for this index'))
646
-
647
- var needsSig = this.live && !!proof.verifiedBy
648
- var pending = proof.nodes.length + (needsSig ? 1 : 0)
649
- var error = null
650
- var signature = null
651
- var nodes = new Array(proof.nodes.length)
652
-
653
- if (!pending) return cb(null, { nodes: nodes, signature: null })
654
-
655
- for (var i = 0; i < proof.nodes.length; i++) {
656
- this._storage.getNode(proof.nodes[i], onnode)
657
- }
658
- if (needsSig) {
659
- this._storage.getSignature(proof.verifiedBy / 2 - 1, onsignature)
660
- }
661
-
662
- function onsignature (err, sig) {
663
- if (sig) signature = sig
664
- onnode(err, null)
665
- }
666
-
667
- function onnode (err, node) {
668
- if (err) error = err
669
-
670
- if (node) {
671
- nodes[proof.nodes.indexOf(node.index)] = node
672
- }
673
-
674
- if (--pending) return
675
- if (error) return cb(error)
676
- cb(null, { nodes: nodes, signature: signature })
224
+ get peers () {
225
+ return this.replicator === null ? [] : this.replicator.peers
677
226
  }
678
- }
679
-
680
- Feed.prototype._readyAndProof = function (index, opts, cb) {
681
- var self = this
682
- this.ready(function (err) {
683
- if (err) return cb(err)
684
- self.proof(index, opts, cb)
685
- })
686
- }
687
-
688
- Feed.prototype.put = function (index, data, proof, cb) {
689
- if (!this.opened) return this._readyAndPut(index, data, proof, cb)
690
- this._putBuffer(index, data === null ? null : this._codec.encode(data), proof, null, cb)
691
- }
692
-
693
- Feed.prototype.cancel = function (start, end) { // TODO: use same argument scheme as download
694
- if (typeof start !== 'symbol') {
695
- if (!end) end = start + 1
696
-
697
- // cancel these right away as .download does not wait for ready
698
- for (var i = this._selections.length - 1; i >= 0; i--) {
699
- var sel = this._selections[i]
700
- if (start <= sel.start && sel.end <= end) {
701
- this.undownload(sel)
702
- }
703
- }
704
- }
705
-
706
- // defer the last part until after ready as .get does that as well
707
- if (this.opened) this._cancel(start, end)
708
- else this._readyAndCancel(start, end)
709
- }
710
227
 
711
- Feed.prototype._cancel = function (start, end) {
712
- var i = 0
713
-
714
- if (typeof start === 'symbol') {
715
- for (i = this._waiting.length - 1; i >= 0; i--) {
716
- const w = this._waiting[i]
717
- if (w.options.cancel === start) {
718
- remove(this._waiting, i)
719
- this._reserved.set(w.index, false)
720
- if (w.callback) process.nextTick(w.callback, new Error('Request cancelled'))
721
- this._updatePeers()
722
- return
723
- }
724
- }
725
- return
228
+ get encryptionKey () {
229
+ return this.encryption && this.encryption.key
726
230
  }
727
231
 
728
- for (i = start; i < end; i++) {
729
- this._reserved.set(i, false) // TODO: send cancel message if set returns true
232
+ get padding () {
233
+ return this.encryption === null ? 0 : this.encryption.padding
730
234
  }
731
235
 
732
- for (i = this._waiting.length - 1; i >= 0; i--) {
733
- var w = this._waiting[i]
734
- if ((start <= w.start && w.end <= end) || (start <= w.index && w.index < end)) {
735
- remove(this._waiting, i)
736
- if (w.callback) process.nextTick(w.callback, new Error('Request cancelled'))
737
- }
236
+ ready () {
237
+ return this.opening
738
238
  }
739
- }
740
-
741
- Feed.prototype.clear = function (start, end, opts, cb) { // TODO: use same argument scheme as download
742
- if (typeof end === 'function') return this.clear(start, start + 1, null, end)
743
- if (typeof opts === 'function') return this.clear(start, end, null, opts)
744
- if (!opts) opts = {}
745
- if (!end) end = start + 1
746
- if (!cb) cb = noop
747
-
748
- // TODO: this needs some work. fx we can only calc byte offset for blocks we know about
749
- // so internally we should make sure to only do that. We should use the merkle tree for this
750
-
751
- var self = this
752
- var byteOffset = start === 0 ? 0 : (typeof opts.byteOffset === 'number' ? opts.byteOffset : -1)
753
- var byteLength = typeof opts.byteLength === 'number' ? opts.byteLength : -1
754
239
 
755
- this.ready(function (err) {
756
- if (err) return cb(err)
240
+ async _open (key, storage, opts) {
241
+ if (opts.preload) opts = { ...opts, ...(await opts.preload()) }
757
242
 
758
- var modified = false
243
+ this.valueEncoding = opts.valueEncoding ? c.from(codecs(opts.valueEncoding)) : null
759
244
 
760
- // TODO: use a buffer.fill thing here to speed this up!
245
+ const keyPair = (key && opts.keyPair)
246
+ ? { ...opts.keyPair, publicKey: key }
247
+ : key
248
+ ? { publicKey: key, secretKey: null }
249
+ : opts.keyPair
761
250
 
762
- for (var i = start; i < end; i++) {
763
- if (self.bitfield.set(i, false)) modified = true
251
+ if (opts.from) {
252
+ const from = opts.from
253
+ await from.opening
254
+ for (const [name, ext] of this.extensions) from.extensions.register(name, null, ext)
255
+ this._initSession(from)
256
+ this.extensions = from.extensions
257
+ this.sessions = from.sessions
258
+ this.storage = from.storage
259
+ if (!this.sign) this.sign = opts.sign || ((keyPair && keyPair.secretKey) ? Core.createSigner(this.crypto, keyPair) : null)
260
+ this.writable = !!this.sign
261
+ this.sessions.push(this)
262
+ return
764
263
  }
765
264
 
766
- if (!modified) return process.nextTick(cb)
767
-
768
- // TODO: write to a tmp/update file that we want to del this incase it crashes will del'ing
769
-
770
- self._unannounce({ start: start, length: end - start })
771
- if (opts.delete === false || self._indexing) return sync()
772
- if (byteOffset > -1) return onstartbytes(null, byteOffset)
773
- self._storage.dataOffset(start, [], onstartbytes)
265
+ if (!this.storage) this.storage = Hypercore.defaultStorage(opts.storage || storage)
774
266
 
775
- function sync () {
776
- self.emit('clear', start, end)
777
- self._sync(null, cb)
778
- }
779
-
780
- function onstartbytes (err, offset) {
781
- if (err) return cb(err)
782
- byteOffset = offset
783
- if (byteLength > -1) return onendbytes(null, byteLength + byteOffset)
784
- if (end === self.length) return onendbytes(null, self.byteLength)
785
- self._storage.dataOffset(end, [], onendbytes)
786
- }
267
+ this.core = await Core.open(this.storage, {
268
+ keyPair,
269
+ crypto: this.crypto,
270
+ onupdate: this._oncoreupdate.bind(this)
271
+ })
787
272
 
788
- function onendbytes (err, end) {
789
- if (err) return cb(err)
790
- if (!self._storage.data.del) return sync() // Not all data storage impls del
791
- self._storage.data.del(byteOffset, end - byteOffset, sync)
273
+ if (opts.userData) {
274
+ for (const [key, value] of Object.entries(opts.userData)) {
275
+ await this.core.userData(key, value)
276
+ }
792
277
  }
793
- })
794
- }
795
-
796
- Feed.prototype.signature = function (index, cb) {
797
- if (typeof index === 'function') return this.signature(this.length - 1, index)
798
-
799
- if (index < 0 || index >= this.length) return cb(new Error('No signature available for this index'))
800
-
801
- this._storage.nextSignature(index, cb)
802
- }
803
-
804
- Feed.prototype.verify = function (index, signature, cb) {
805
- var self = this
806
-
807
- this.rootHashes(index, function (err, roots) {
808
- if (err) return cb(err)
809
-
810
- var checksum = crypto.signable(roots, index + 1)
811
-
812
- verifyCompat(self, checksum, signature, function (err, valid) {
813
- if (err) return cb(err)
814
-
815
- if (!valid) return cb(new Error('Signature verification failed'))
816
278
 
817
- return cb(null, true)
279
+ this.replicator = new Replicator(this.core, {
280
+ onupdate: this._onpeerupdate.bind(this)
818
281
  })
819
- })
820
- }
821
-
822
- Feed.prototype.rootHashes = function (index, cb) {
823
- this._getRootsToVerify(index * 2 + 2, {}, [], cb)
824
- }
825
-
826
- Feed.prototype.seek = function (bytes, opts, cb) {
827
- if (typeof opts === 'function') return this.seek(bytes, null, opts)
828
- if (!opts) opts = {}
829
- if (!this.opened) return this._readyAndSeek(bytes, opts, cb)
830
-
831
- var self = this
832
-
833
- if (bytes === this.byteLength) return process.nextTick(cb, null, this.length, 0)
834
282
 
835
- this._seek(bytes, function (err, index, offset) {
836
- if (!err && isBlock(index)) return done(index / 2, offset)
837
- if (opts.wait === false) return cb(err || new Error('Unable to seek to this offset'))
283
+ if (!this.sign) this.sign = opts.sign || this.core.defaultSign
838
284
 
839
- var start = opts.start || 0
840
- var end = opts.end || -1
285
+ this.discoveryKey = this.crypto.discoveryKey(this.core.header.signer.publicKey)
286
+ this.key = this.core.header.signer.publicKey
287
+ this.writable = !!this.sign
841
288
 
842
- if (!err) {
843
- var left = flat.leftSpan(index) / 2
844
- var right = flat.rightSpan(index) / 2 + 1
845
-
846
- if (left > start) start = left
847
- if (right < end || end === -1) end = right
289
+ if (!this.encryption && opts.encryptionKey) {
290
+ this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
848
291
  }
849
292
 
850
- if (end > -1 && end <= start) return cb(new Error('Unable to seek to this offset'))
851
-
852
- var w = {
853
- hash: opts.hash !== false,
854
- bytes: bytes,
855
- index: -1,
856
- ifAvailable: opts && typeof opts.ifAvailable === 'boolean' ? opts.ifAvailable : self._alwaysIfAvailable,
857
- start: start,
858
- end: end,
859
- want: toWantRange(start),
860
- requested: 0,
861
- callback: cb || noop
862
- }
293
+ this.extensions.attach(this.replicator)
294
+ this.opened = true
863
295
 
864
- self._waiting.push(w)
865
- self._updatePeers()
866
- if (w.ifAvailable) self._ifAvailableSeek(w)
867
- })
296
+ if (opts.postload) await opts.postload(this)
868
297
 
869
- function done (index, offset) {
870
- for (var i = 0; i < self.peers.length; i++) {
871
- self.peers[i].haveBytes(bytes)
298
+ for (let i = 0; i < this.sessions.length; i++) {
299
+ const s = this.sessions[i]
300
+ if (s !== this) s._initSession(this)
301
+ s.emit('ready')
872
302
  }
873
- cb(null, index, offset)
874
303
  }
875
- }
876
-
877
- Feed.prototype._ifAvailableSeek = function (w) {
878
- var self = this
879
- var cb = w.callback
880
-
881
- self.timeouts.get(function () {
882
- if (self.closed) return done(new Error('Closed'))
883
-
884
- process.nextTick(readyNT, self.ifAvailable, function () {
885
- if (self.closed) return done(new Error('Closed'))
886
304
 
887
- let available = false
888
- for (const peer of self.peers) {
889
- const ite = peer._iterator
890
- let i = ite.seek(w.start).next(true)
891
- while (self.tree.get(i * 2) && i > -1) i = ite.next(true)
892
- if (i > -1 && (w.end === -1 || i < w.end)) {
893
- available = true
894
- break
305
+ _oncoreupdate (status, bitfield, value, from) {
306
+ if (status !== 0) {
307
+ for (let i = 0; i < this.sessions.length; i++) {
308
+ if ((status & 0b10) !== 0) {
309
+ if (this.cache) this.cache.clear()
310
+ this.sessions[i].emit('truncate', this.core.tree.fork)
311
+ }
312
+ if ((status & 0b01) !== 0) {
313
+ this.sessions[i].emit('append')
895
314
  }
896
315
  }
897
316
 
898
- if (!available) done(new Error('Seek not available from peers'))
899
- })
900
- })
901
-
902
- function done (err) {
903
- var i = self._waiting.indexOf(w)
904
- if (i > -1) {
905
- remove(self._waiting, i)
906
- w.callback = noop
907
- cb(err)
908
- }
909
- }
910
- }
911
-
912
- Feed.prototype._seek = function (offset, cb) {
913
- if (offset === 0) return cb(null, 0, 0)
914
-
915
- var self = this
916
- var roots = flat.fullRoots(this.length * 2)
917
- var nearestRoot = 0
918
-
919
- loop(null, null)
920
-
921
- function onroot (top) {
922
- if (isBlock(top)) return cb(null, nearestRoot, offset)
923
-
924
- var left = flat.leftChild(top)
925
- while (!self.tree.get(left)) {
926
- if (isBlock(left)) return cb(null, nearestRoot, offset)
927
- left = flat.leftChild(left)
317
+ this.replicator.broadcastInfo()
928
318
  }
929
319
 
930
- self._storage.getNode(left, onleftchild)
931
- }
932
-
933
- function onleftchild (err, node) {
934
- if (err) return cb(err)
935
-
936
- if (node.size > offset) {
937
- nearestRoot = node.index
938
- onroot(node.index)
939
- } else {
940
- offset -= node.size
941
- if (flat.parent(node.index) === nearestRoot) {
942
- nearestRoot = flat.sibling(node.index)
943
- onroot(nearestRoot)
944
- } else {
945
- onroot(flat.sibling(node.index))
320
+ if (bitfield && !bitfield.drop) { // TODO: support drop!
321
+ for (let i = 0; i < bitfield.length; i++) {
322
+ this.replicator.broadcastBlock(bitfield.start + i)
946
323
  }
947
324
  }
948
- }
949
325
 
950
- function loop (err, node) {
951
- if (err) return cb(err)
326
+ if (value) {
327
+ const byteLength = value.byteLength - this.padding
952
328
 
953
- if (node) {
954
- if (node.size > offset) {
955
- nearestRoot = node.index
956
- return onroot(node.index)
329
+ for (let i = 0; i < this.sessions.length; i++) {
330
+ this.sessions[i].emit('download', bitfield.start, byteLength, from)
957
331
  }
958
- offset -= node.size
959
332
  }
960
-
961
- if (!roots.length) return cb(new Error('Out of bounds'))
962
- self._storage.getNode(roots.shift(), loop)
963
333
  }
964
- }
965
334
 
966
- Feed.prototype._readyAndSeek = function (bytes, opts, cb) {
967
- var self = this
968
- this.ready(function (err) {
969
- if (err) return cb(err)
970
- self.seek(bytes, opts, cb)
971
- })
972
- }
335
+ _onpeerupdate (added, peer) {
336
+ if (added) this.extensions.update(peer)
337
+ const name = added ? 'peer-add' : 'peer-remove'
973
338
 
974
- Feed.prototype._getBuffer = function (index, cb) {
975
- this._storage.getData(index, cb)
976
- }
977
-
978
- Feed.prototype._putBuffer = function (index, data, proof, from, cb) {
979
- // TODO: this nodes in proof are not instances of our Node prototype
980
- // but just similar. Check if this has any v8 perf implications.
981
-
982
- // TODO: if the proof contains a valid signature BUT fails, emit a critical error
983
- // --> feed should be considered dead
984
-
985
- var self = this
986
- var trusted = -1
987
- var missing = []
988
- var next = 2 * index
989
- var i = data ? 0 : 1
990
-
991
- while (true) {
992
- if (this.tree.get(next)) {
993
- trusted = next
994
- break
339
+ for (let i = 0; i < this.sessions.length; i++) {
340
+ this.sessions[i].emit(name, peer)
995
341
  }
996
-
997
- var sib = flat.sibling(next)
998
- next = flat.parent(next)
999
-
1000
- if (i < proof.nodes.length && proof.nodes[i].index === sib) {
1001
- i++
1002
- continue
1003
- }
1004
-
1005
- if (!this.tree.get(sib)) break
1006
- missing.push(sib)
1007
342
  }
1008
343
 
1009
- if (trusted === -1 && this.tree.get(next)) trusted = next
1010
-
1011
- var error = null
1012
- var trustedNode = null
1013
- var missingNodes = new Array(missing.length)
1014
- var pending = missing.length + (trusted > -1 ? 1 : 0)
1015
-
1016
- for (i = 0; i < missing.length; i++) this._storage.getNode(missing[i], onmissing)
1017
- if (trusted > -1) this._storage.getNode(trusted, ontrusted)
1018
- if (!missing.length && trusted === -1) onmissingloaded(null)
1019
-
1020
- function ontrusted (err, node) {
1021
- if (err) error = err
1022
- if (node) trustedNode = node
1023
- if (!--pending) onmissingloaded(error)
344
+ async setUserData (key, value) {
345
+ if (this.opened === false) await this.opening
346
+ return this.core.userData(key, value)
1024
347
  }
1025
348
 
1026
- function onmissing (err, node) {
1027
- if (err) error = err
1028
- if (node) missingNodes[missing.indexOf(node.index)] = node
1029
- if (!--pending) onmissingloaded(error)
349
+ async getUserData (key) {
350
+ if (this.opened === false) await this.opening
351
+ for (const { key: savedKey, value } of this.core.header.userData) {
352
+ if (key === savedKey) return value
353
+ }
354
+ return null
1030
355
  }
1031
356
 
1032
- function onmissingloaded (err) {
1033
- if (err) return cb(err)
1034
- self._verifyAndWrite(index, data, proof, missingNodes, trustedNode, from, cb)
357
+ async update () {
358
+ if (this.opened === false) await this.opening
359
+ // TODO: add an option where a writer can bootstrap it's state from the network also
360
+ if (this.writable) return false
361
+ return this.replicator.requestUpgrade()
1035
362
  }
1036
- }
1037
363
 
1038
- Feed.prototype._readyAndPut = function (index, data, proof, cb) {
1039
- var self = this
1040
- this.ready(function (err) {
1041
- if (err) return cb(err)
1042
- self.put(index, data, proof, cb)
1043
- })
1044
- }
364
+ async seek (bytes) {
365
+ if (this.opened === false) await this.opening
1045
366
 
1046
- Feed.prototype._write = function (index, data, nodes, sig, from, cb) {
1047
- if (!this._onwrite) return this._writeAfterHook(index, data, nodes, sig, from, cb)
1048
- this._onwrite(index, data, from, writeHookDone(this, index, data, nodes, sig, from, cb))
1049
- }
367
+ const s = this.core.tree.seek(bytes, this.padding)
1050
368
 
1051
- function writeHookDone (self, index, data, nodes, sig, from, cb) {
1052
- return function (err) {
1053
- if (err) return cb(err)
1054
- self._writeAfterHook(index, data, nodes, sig, from, cb)
369
+ return (await s.update()) || this.replicator.requestSeek(s)
1055
370
  }
1056
- }
1057
371
 
1058
- Feed.prototype._writeAfterHook = function (index, data, nodes, sig, from, cb) {
1059
- var self = this
1060
- var pending = nodes.length + 1 + (sig ? 1 : 0)
1061
- var error = null
1062
-
1063
- for (var i = 0; i < nodes.length; i++) this._storage.putNode(nodes[i].index, nodes[i], ondone)
1064
- if (data) this._storage.putData(index, data, nodes, ondone)
1065
- else ondone()
1066
- if (sig) this._storage.putSignature(sig.index, sig.signature, ondone)
1067
-
1068
- function ondone (err) {
1069
- if (err) error = err
1070
- if (--pending) return
1071
- if (error) return cb(error)
1072
- self._writeDone(index, data, nodes, from, cb)
1073
- }
1074
- }
372
+ async has (index) {
373
+ if (this.opened === false) await this.opening
1075
374
 
1076
- Feed.prototype._writeDone = function (index, data, nodes, from, cb) {
1077
- for (var i = 0; i < nodes.length; i++) this.tree.set(nodes[i].index)
1078
- this.tree.set(2 * index)
1079
-
1080
- if (data) {
1081
- if (this.bitfield.set(index, true)) {
1082
- if (this._stats) {
1083
- this._stats.downloadedBlocks += 1
1084
- this._stats.downloadedBytes += data.length
1085
- }
1086
- this.emit('download', index, data, from)
1087
- }
1088
- if (this.peers.length) this._announce({ start: index }, from)
1089
-
1090
- if (!this.writable) {
1091
- if (!this._synced) this._synced = this.bitfield.iterator(0, this.length)
1092
- if (this._synced.next() === -1) {
1093
- this._synced.range(0, this.length)
1094
- this._synced.seek(0)
1095
- if (this._synced.next() === -1) {
1096
- this.emit('sync')
1097
- }
1098
- }
1099
- }
375
+ return this.core.bitfield.get(index)
1100
376
  }
1101
377
 
1102
- this._sync(null, cb)
1103
- }
1104
-
1105
- Feed.prototype._verifyAndWrite = function (index, data, proof, localNodes, trustedNode, from, cb) {
1106
- var visited = []
1107
- var remoteNodes = proof.nodes
1108
- var top = data ? new storage.Node(2 * index, crypto.data(data), data.length) : remoteNodes.shift()
1109
-
1110
- // check if we already have the hash for this node
1111
- if (verifyNode(trustedNode, top)) {
1112
- this._write(index, data, visited, null, from, cb)
1113
- return
1114
- }
1115
-
1116
- // keep hashing with siblings until we reach or trusted node
1117
- while (true) {
1118
- var node = null
1119
- var next = flat.sibling(top.index)
1120
-
1121
- if (remoteNodes.length && remoteNodes[0].index === next) {
1122
- node = remoteNodes.shift()
1123
- visited.push(node)
1124
- } else if (localNodes.length && localNodes[0].index === next) {
1125
- node = localNodes.shift()
1126
- } else {
1127
- // we cannot create another parent, i.e. these nodes must be roots in the tree
1128
- this._verifyRootsAndWrite(index, data, top, proof, visited, from, cb)
1129
- return
1130
- }
1131
-
1132
- visited.push(top)
1133
- top = new storage.Node(flat.parent(top.index), crypto.parent(top, node), top.size + node.size)
1134
-
1135
- // the tree checks out, write the data and the visited nodes
1136
- if (verifyNode(trustedNode, top)) {
1137
- this._write(index, data, visited, null, from, cb)
1138
- return
1139
- }
378
+ async get (index, opts) {
379
+ if (this.opened === false) await this.opening
380
+ const c = this.cache && this.cache.get(index)
381
+ if (c) return c
382
+ const fork = this.core.tree.fork
383
+ const b = await this._get(index, opts)
384
+ if (this.cache && fork === this.core.tree.fork && b) this.cache.set(index, b)
385
+ return b
1140
386
  }
1141
- }
1142
-
1143
- Feed.prototype._verifyRootsAndWrite = function (index, data, top, proof, nodes, from, cb) {
1144
- var remoteNodes = proof.nodes
1145
- var lastNode = remoteNodes.length ? remoteNodes[remoteNodes.length - 1].index : top.index
1146
- var verifiedBy = Math.max(flat.rightSpan(top.index), flat.rightSpan(lastNode)) + 2
1147
- var length = verifiedBy / 2
1148
- var self = this
1149
-
1150
- this._getRootsToVerify(verifiedBy, top, remoteNodes, function (err, roots, extraNodes) {
1151
- if (err) return cb(err)
1152
-
1153
- var checksum = crypto.signable(roots, length)
1154
- var signature = null
1155
-
1156
- if (self.length && self.live && !proof.signature) {
1157
- return cb(new Error('Remote did not include a signature'))
1158
- }
1159
387
 
1160
- if (proof.signature) { // check signatures
1161
- verifyCompat(self, checksum, proof.signature, function (err, valid) {
1162
- if (err) return cb(err)
1163
- if (!valid) return cb(new Error('Remote signature could not be verified'))
1164
-
1165
- signature = { index: verifiedBy / 2 - 1, signature: proof.signature }
1166
- write()
1167
- })
1168
- } else { // check tree root
1169
- if (Buffer.compare(checksum.slice(0, 32), self.key) !== 0) {
1170
- return cb(new Error('Remote checksum failed'))
1171
- }
388
+ async _get (index, opts) {
389
+ const encoding = (opts && opts.valueEncoding && c.from(codecs(opts.valueEncoding))) || this.valueEncoding
1172
390
 
1173
- write()
1174
- }
391
+ let block
1175
392
 
1176
- function write () {
1177
- self.live = !!signature
1178
-
1179
- if (length > self.length) {
1180
- // TODO: only emit this after the info has been flushed to storage
1181
- if (self.writable) self._merkle = null // We need to reload merkle state now
1182
- self.length = length
1183
- self._seq = length
1184
- self.byteLength = roots.reduce(addSize, 0)
1185
- if (self._synced) self._synced.seek(0, self.length)
1186
- self.emit('append')
1187
- }
1188
-
1189
- self._write(index, data, nodes.concat(extraNodes), signature, from, cb)
1190
- }
1191
- })
1192
- }
1193
-
1194
- Feed.prototype._getRootsToVerify = function (verifiedBy, top, remoteNodes, cb) {
1195
- var indexes = flat.fullRoots(verifiedBy)
1196
- var roots = new Array(indexes.length)
1197
- var nodes = []
1198
- var error = null
1199
- var pending = roots.length
1200
-
1201
- for (var i = 0; i < indexes.length; i++) {
1202
- if (indexes[i] === top.index) {
1203
- nodes.push(top)
1204
- onnode(null, top)
1205
- } else if (remoteNodes.length && indexes[i] === remoteNodes[0].index) {
1206
- nodes.push(remoteNodes[0])
1207
- onnode(null, remoteNodes.shift())
1208
- } else if (this.tree.get(indexes[i])) {
1209
- this._storage.getNode(indexes[i], onnode)
393
+ if (this.core.bitfield.get(index)) {
394
+ block = await this.core.blocks.get(index)
1210
395
  } else {
1211
- onnode(new Error('Missing tree roots needed for verify'))
396
+ if (opts && opts.onwait) opts.onwait(index)
397
+ block = await this.replicator.requestBlock(index)
1212
398
  }
1213
- }
1214
-
1215
- function onnode (err, node) {
1216
- if (err) error = err
1217
- if (node) roots[indexes.indexOf(node.index)] = node
1218
- if (!--pending) done(error)
1219
- }
1220
-
1221
- function done (err) {
1222
- if (err) return cb(err)
1223
399
 
1224
- cb(null, roots, nodes)
400
+ if (this.encryption) this.encryption.decrypt(index, block)
401
+ return this._decode(encoding, block)
1225
402
  }
1226
- }
1227
-
1228
- Feed.prototype._announce = function (message, from) {
1229
- for (var i = 0; i < this.peers.length; i++) {
1230
- var peer = this.peers[i]
1231
- if (peer !== from) peer.have(message)
1232
- }
1233
- }
1234
-
1235
- Feed.prototype._unannounce = function (message) {
1236
- for (var i = 0; i < this.peers.length; i++) this.peers[i].unhave(message)
1237
- }
1238
-
1239
- Feed.prototype.downloaded = function (start, end, cb) {
1240
- const count = this.bitfield.total(start, end)
1241
- if (cb) process.nextTick(cb, null, count) // prepare async interface for this
1242
- return count
1243
- }
1244
-
1245
- Feed.prototype.has = function (start, end, cb) {
1246
- if (typeof end === 'function') return this.has(start, undefined, end)
1247
- if (end === undefined) {
1248
- const res = this.bitfield.get(start)
1249
- if (cb) process.nextTick(cb, null, res)
1250
- return res
1251
- }
1252
- const total = end - start
1253
- const res = total === this.bitfield.total(start, end)
1254
- if (cb) process.nextTick(cb, null, res)
1255
- return res
1256
- }
1257
-
1258
- Feed.prototype.getBlockInfo = function (index, cb) {
1259
- var self = this
1260
- this.ready(function (err) {
1261
- if (err) return cb(err)
1262
- self._storage.getNode(2 * index, cb)
1263
- })
1264
- }
1265
403
 
1266
- Feed.prototype.head = function (opts, cb) {
1267
- if (typeof opts === 'function') return this.head({}, opts)
1268
- var self = this
1269
- this.ready(function (err) {
1270
- if (err) return cb(err)
1271
- if (opts && opts.update) self.update(opts, onupdate)
1272
- else process.nextTick(onupdate)
1273
- })
1274
-
1275
- function onupdate () {
1276
- if (self.length === 0) cb(new Error('feed is empty'))
1277
- else self.get(self.length - 1, opts, cb)
404
+ createReadStream (opts) {
405
+ return new ReadStream(this, opts)
1278
406
  }
1279
- }
1280
-
1281
- Feed.prototype.get = function (index, opts, cb) {
1282
- if (typeof opts === 'function') return this.get(index, null, opts)
1283
-
1284
- opts = { ...opts }
1285
- if (!opts.cancel) opts.cancel = Symbol('hypercore-get')
1286
-
1287
- if (!this.opened) return this._readyAndGet(index, opts, cb)
1288
-
1289
- if (!this.readable) {
1290
- process.nextTick(cb, new Error('Feed is closed'))
1291
- return opts.cancel
1292
- }
1293
-
1294
- if (opts.timeout) cb = timeoutCallback(cb, opts.timeout)
1295
-
1296
- if (!this.bitfield.get(index)) {
1297
- if (opts && opts.wait === false) return process.nextTick(cb, new Error('Block not downloaded'))
1298
-
1299
- var w = { bytes: 0, hash: false, index: index, options: opts, requested: 0, callback: cb }
1300
- this._waiting.push(w)
1301
-
1302
- if (opts && typeof opts.ifAvailable === 'boolean' ? opts.ifAvailable : this._alwaysIfAvailable) this._ifAvailableGet(w)
1303
-
1304
- this._updatePeers()
1305
- if (opts.onwait) {
1306
- const onwait = opts.onwait
1307
- opts.onwait = null
1308
- onwait(index)
1309
- }
1310
- return opts.cancel
1311
- }
1312
-
1313
- if (opts && opts.valueEncoding) cb = wrapCodec(toCodec(opts.valueEncoding), cb)
1314
- else if (this._codec !== codecs.binary) cb = wrapCodec(this._codec, cb)
1315
-
1316
- this._getBuffer(index, cb)
1317
- return opts.cancel
1318
- }
1319
-
1320
- Feed.prototype._readyAndGet = function (index, opts, cb) {
1321
- var self = this
1322
- this.ready(function (err) {
1323
- if (err) return cb(err)
1324
- self.get(index, opts, cb)
1325
- })
1326
- return opts.cancel
1327
- }
1328
-
1329
- Feed.prototype.getBatch = function (start, end, opts, cb) {
1330
- if (typeof opts === 'function') return this.getBatch(start, end, null, opts)
1331
- if (!this.opened) return this._readyAndGetBatch(start, end, opts, cb)
1332
-
1333
- var self = this
1334
- var wait = !opts || opts.wait !== false
1335
-
1336
- if (this.has(start, end)) return this._getBatch(start, end, opts, cb)
1337
- if (!wait) return process.nextTick(cb, new Error('Block not downloaded'))
1338
-
1339
- if (opts && opts.timeout) cb = timeoutCallback(cb, opts.timeout)
1340
-
1341
- this.download({ start: start, end: end }, function (err) {
1342
- if (err) return cb(err)
1343
- self._getBatch(start, end, opts, cb)
1344
- })
1345
- }
1346
407
 
1347
- Feed.prototype._getBatch = function (start, end, opts, cb) {
1348
- var enc = opts && opts.valueEncoding
1349
- var codec = enc ? toCodec(enc) : this._codec
408
+ download (range) {
409
+ const linear = !!(range && range.linear)
1350
410
 
1351
- this._storage.getDataBatch(start, end - start, onbatch)
411
+ let start
412
+ let end
413
+ let filter
1352
414
 
1353
- function onbatch (err, buffers) {
1354
- if (err) return cb(err)
415
+ if (range && range.blocks) {
416
+ const blocks = range.blocks instanceof Set
417
+ ? range.blocks
418
+ : new Set(range.blocks)
1355
419
 
1356
- var batch = new Array(buffers.length)
420
+ start = range.start || (blocks.size ? min(range.blocks) : 0)
421
+ end = range.end || (blocks.size ? max(range.blocks) + 1 : 0)
1357
422
 
1358
- for (var i = 0; i < buffers.length; i++) {
1359
- try {
1360
- batch[i] = codec ? codec.decode(buffers[i]) : buffers[i]
1361
- } catch (err) {
1362
- return cb(err)
1363
- }
423
+ filter = (i) => blocks.has(i)
424
+ } else {
425
+ start = (range && range.start) || 0
426
+ end = typeof (range && range.end) === 'number' ? range.end : -1 // download all
1364
427
  }
1365
428
 
1366
- cb(null, batch)
1367
- }
1368
- }
429
+ const r = Replicator.createRange(start, end, filter, linear)
1369
430
 
1370
- Feed.prototype._readyAndGetBatch = function (start, end, opts, cb) {
1371
- var self = this
1372
- this.ready(function (err) {
1373
- if (err) return cb(err)
1374
- self.getBatch(start, end, opts, cb)
1375
- })
1376
- }
1377
-
1378
- Feed.prototype._updatePeers = function () {
1379
- for (var i = 0; i < this.peers.length; i++) this.peers[i].update()
1380
- }
431
+ if (this.opened) this.replicator.addRange(r)
432
+ else this.opening.then(() => this.replicator.addRange(r), noop)
1381
433
 
1382
- Feed.prototype.createWriteStream = function (opts) {
1383
- return new WriteStream(this, opts)
1384
- }
1385
-
1386
- Feed.prototype.createReadStream = function (opts) {
1387
- return new ReadStream(this, opts)
1388
- }
1389
-
1390
- // TODO: when calling finalize on a live feed write an END_OF_FEED block (length === 0?)
1391
- Feed.prototype.finalize = function (cb) {
1392
- if (!this.key) {
1393
- this.key = crypto.tree(this._merkle.roots)
1394
- this.discoveryKey = crypto.discoveryKey(this.key)
434
+ return r
1395
435
  }
1396
- this._storage.key.write(0, this.key, cb)
1397
- }
1398
-
1399
- Feed.prototype.append = function (batch, cb) {
1400
- if (!cb) cb = noop
1401
436
 
1402
- var self = this
1403
- var list = Array.isArray(batch) ? batch : [batch]
1404
- this._batch(list, onappend)
1405
-
1406
- function onappend (err) {
1407
- if (err) return cb(err)
1408
- var seq = self._seq
1409
- self._seq += list.length
1410
- cb(null, seq)
1411
- }
1412
- }
1413
-
1414
- Feed.prototype.flush = function (cb) {
1415
- this.append([], cb)
1416
- }
1417
-
1418
- Feed.prototype.destroyStorage = function (cb) {
1419
- const self = this
1420
-
1421
- this.close(function (err) {
1422
- if (err) cb(err)
1423
- else self._storage.destroy(cb)
1424
- })
1425
- }
1426
-
1427
- Feed.prototype._close = function (cb) {
1428
- const self = this
1429
-
1430
- for (const peer of this.peers) {
1431
- if (!peer._destroyed) peer._close()
437
+ // TODO: get rid of this / deprecate it?
438
+ cancel (request) {
439
+ // Do nothing for now
1432
440
  }
1433
441
 
1434
- this._forceClose(onclose, null)
1435
-
1436
- function onclose (err) {
1437
- if (!err) self.emit('close')
1438
- cb(err)
442
+ // TODO: get rid of this / deprecate it?
443
+ undownload (range) {
444
+ range.destroy(null)
1439
445
  }
1440
- }
1441
446
 
1442
- Feed.prototype._forceClose = function (cb, error) {
1443
- var self = this
447
+ async truncate (newLength = 0, fork = -1) {
448
+ if (this.opened === false) await this.opening
449
+ if (this.writable === false) throw new Error('Core is not writable')
1444
450
 
1445
- this.writable = false
1446
- this.readable = false
451
+ if (fork === -1) fork = this.core.tree.fork + 1
452
+ await this.core.truncate(newLength, fork, this.sign)
1447
453
 
1448
- this._storage.close(function (err) {
1449
- if (!err) err = error
1450
- self._destroy(err || new Error('Feed is closed'))
1451
- cb(err)
1452
- })
1453
- }
1454
-
1455
- Feed.prototype._destroy = function (err) {
1456
- this.ifAvailable.destroy()
1457
-
1458
- while (this._waiting.length) {
1459
- this._waiting.pop().callback(err)
454
+ // TODO: Should propagate from an event triggered by the oplog
455
+ this.replicator.updateAll()
1460
456
  }
1461
- while (this._selections.length) {
1462
- this._selections.pop().callback(err)
1463
- }
1464
- }
1465
457
 
1466
- Feed.prototype._appendHook = function (batch, cb) {
1467
- var self = this
1468
- var missing = batch.length
1469
- var error = null
458
+ async append (blocks) {
459
+ if (this.opened === false) await this.opening
460
+ if (this.writable === false) throw new Error('Core is not writable')
1470
461
 
1471
- if (!missing) return this._append(batch, cb)
1472
- for (var i = 0; i < batch.length; i++) {
1473
- this._onwrite(i + this.length, batch[i], null, done)
1474
- }
462
+ blocks = Array.isArray(blocks) ? blocks : [blocks]
1475
463
 
1476
- function done (err) {
1477
- if (err) error = err
1478
- if (--missing) return
1479
- if (error) return cb(error)
1480
- self._append(batch, cb)
1481
- }
1482
- }
464
+ const preappend = this.encryption && this._preappend
465
+ const buffers = new Array(blocks.length)
1483
466
 
1484
- Feed.prototype._append = function (batch, cb) {
1485
- if (!this.opened) return this._readyAndAppend(batch, cb)
1486
- if (!this.writable) return cb(new Error('This feed is not writable. Did you create it?'))
1487
-
1488
- var self = this
1489
- var pending = 1
1490
- var offset = 0
1491
- var error = null
1492
- var nodeBatch = new Array(batch.length ? batch.length * 2 - 1 : 0)
1493
- var nodeOffset = this.length * 2
1494
- var dataBatch = new Array(batch.length)
1495
-
1496
- if (!pending) return cb()
1497
-
1498
- for (var i = 0; i < batch.length; i++) {
1499
- var data = this._codec.encode(batch[i])
1500
- var nodes = this._merkle.next(data)
1501
-
1502
- // the replication stream rejects frames >8MB for DOS defense. Is configurable there, so
1503
- // we could bubble that up here. For now just hardcode it so you can't accidentally "brick" your core
1504
- // note: this is *only* for individual blocks and is just a sanity check. most blocks are <1MB
1505
- if (data.length > 8388608) return cb(new Error('Individual blocks has be less than 8MB'))
1506
-
1507
- offset += data.length
1508
- dataBatch[i] = data
1509
-
1510
- for (var j = 0; j < nodes.length; j++) {
1511
- var node = nodes[j]
1512
- if (node.index >= nodeOffset && node.index - nodeOffset < nodeBatch.length) {
1513
- nodeBatch[node.index - nodeOffset] = node
1514
- } else {
1515
- pending++
1516
- this._storage.putNode(node.index, node, done)
1517
- }
467
+ for (let i = 0; i < blocks.length; i++) {
468
+ buffers[i] = this._encode(this.valueEncoding, blocks[i])
1518
469
  }
1519
- }
1520
470
 
1521
- if (this.live && batch.length) {
1522
- pending++
1523
- this.crypto.sign(crypto.signable(this._merkle.roots, self.length + batch.length), this.secretKey, function (err, sig) {
1524
- if (err) return done(err)
1525
- self._storage.putSignature(self.length + batch.length - 1, sig, done)
1526
- })
471
+ return await this.core.append(buffers, this.sign, { preappend })
1527
472
  }
1528
473
 
1529
- if (!this._indexing) {
1530
- pending++
1531
- if (dataBatch.length === 1) this._storage.data.write(this.byteLength, dataBatch[0], done)
1532
- else this._storage.data.write(this.byteLength, Buffer.concat(dataBatch), done)
474
+ registerExtension (name, handlers) {
475
+ return this.extensions.register(name, handlers)
1533
476
  }
1534
477
 
1535
- this._storage.putNodeBatch(nodeOffset, nodeBatch, done)
1536
-
1537
- function done (err) {
1538
- if (err) error = err
1539
- if (--pending) return
1540
- if (error) return cb(error)
1541
-
1542
- var start = self.length
1543
-
1544
- // TODO: only emit append and update length / byteLength after the info has been flushed to storage
1545
- self.byteLength += offset
1546
- for (var i = 0; i < batch.length; i++) {
1547
- self.bitfield.set(self.length, true)
1548
- self.tree.set(2 * self.length++)
1549
- }
1550
- self.emit('append')
1551
-
1552
- var message = self.length - start > 1 ? { start: start, length: self.length - start } : { start: start }
1553
- if (self.peers.length) self._announce(message)
1554
-
1555
- self._sync(null, cb)
478
+ // called by the extensions
479
+ onextensionupdate () {
480
+ if (this.replicator !== null) this.replicator.broadcastOptions()
1556
481
  }
1557
- }
1558
482
 
1559
- Feed.prototype._readyAndAppend = function (batch, cb) {
1560
- var self = this
1561
- this.ready(function (err) {
1562
- if (err) return cb(err)
1563
- self._append(batch, cb)
1564
- })
1565
- }
1566
-
1567
- Feed.prototype._readyAndCancel = function (start, end) {
1568
- var self = this
1569
- this.ready(function () {
1570
- self._cancel(start, end)
1571
- })
1572
- }
483
+ _encode (enc, val) {
484
+ const state = { start: this.padding, end: this.padding, buffer: null }
1573
485
 
1574
- Feed.prototype._pollWaiting = function () {
1575
- var len = this._waiting.length
1576
-
1577
- for (var i = 0; i < len; i++) {
1578
- var next = this._waiting[i]
1579
- if (!next.bytes && !this.bitfield.get(next.index) && (!next.hash || !this.tree.get(next.index * 2))) {
1580
- continue
486
+ if (b4a.isBuffer(val)) {
487
+ if (state.start === 0) return val
488
+ state.end += val.byteLength
489
+ } else if (enc) {
490
+ enc.preencode(state, val)
491
+ } else {
492
+ val = b4a.from(val)
493
+ if (state.start === 0) return val
494
+ state.end += val.byteLength
1581
495
  }
1582
496
 
1583
- remove(this._waiting, i--)
1584
- len--
1585
-
1586
- if (next.bytes) this.seek(next.bytes, next, next.callback)
1587
- else if (next.update) this.update(next.index + 1, next.callback)
1588
- else this.get(next.index, next.options, next.callback)
1589
- }
1590
- }
1591
-
1592
- Feed.prototype._syncBitfield = function (cb) {
1593
- var missing = this.bitfield.pages.updates.length
1594
- var next = null
1595
- var error = null
1596
-
1597
- // All data / nodes have been written now. We still need to update the bitfields though
497
+ state.buffer = b4a.allocUnsafe(state.end)
1598
498
 
1599
- // TODO 1: if the program fails during this write the bitfield might not have been fully written
1600
- // HOWEVER, we can easily recover from this by traversing the tree and checking if the nodes exists
1601
- // on disk. So if a get fails, it should try and recover once.
499
+ if (enc) enc.encode(state, val)
500
+ else state.buffer.set(val, state.start)
1602
501
 
1603
- // TODO 2: if .writable append bitfield updates into a single buffer for extra perf
1604
- // Added benefit is that if the program exits while flushing the bitfield the feed will only get
1605
- // truncated and not have missing chunks which is what you expect.
1606
-
1607
- if (!missing) {
1608
- this._pollWaiting()
1609
- return cb(null)
502
+ return state.buffer
1610
503
  }
1611
504
 
1612
- while ((next = this.bitfield.pages.lastUpdate()) !== null) {
1613
- this._storage.putBitfield(next.offset, next.buffer, ondone)
1614
- }
1615
-
1616
- this._pollWaiting()
1617
-
1618
- function ondone (err) {
1619
- if (err) error = err
1620
- if (--missing) return
1621
- cb(error)
1622
- }
1623
- }
1624
-
1625
- Feed.prototype._roots = function (index, cb) {
1626
- var roots = flat.fullRoots(2 * index)
1627
- var result = new Array(roots.length)
1628
- var pending = roots.length
1629
- var error = null
1630
-
1631
- if (!pending) return cb(null, result)
1632
-
1633
- for (var i = 0; i < roots.length; i++) {
1634
- this._storage.getNode(roots[i], onnode)
1635
- }
1636
-
1637
- function onnode (err, node) {
1638
- if (err) error = err
1639
- if (node) result[roots.indexOf(node.index)] = node
1640
- if (--pending) return
1641
- if (error) return cb(error)
1642
- cb(null, result)
1643
- }
1644
- }
1645
-
1646
- Feed.prototype.audit = function (cb) {
1647
- if (!cb) cb = noop
1648
-
1649
- var self = this
1650
- var report = {
1651
- valid: 0,
1652
- invalid: 0
1653
- }
1654
-
1655
- this.ready(function (err) {
1656
- if (err) return cb(err)
1657
-
1658
- var block = 0
1659
- var max = self.length
1660
-
1661
- next()
1662
-
1663
- function onnode (err, node) {
1664
- if (err) return ondata(null, null)
1665
- self._storage.getData(block, ondata)
1666
-
1667
- function ondata (_, data) {
1668
- var verified = data && crypto.data(data).equals(node.hash)
1669
- if (verified) report.valid++
1670
- else report.invalid++
1671
- self.bitfield.set(block, verified)
1672
- block++
1673
- next()
1674
- }
1675
- }
1676
-
1677
- function next () {
1678
- while (block < max && !self.bitfield.get(block)) block++
1679
- if (block >= max) return done()
1680
- self._storage.getNode(2 * block, onnode)
1681
- }
1682
-
1683
- function done () {
1684
- self._sync(null, function (err) {
1685
- if (err) return cb(err)
1686
- cb(null, report)
1687
- })
1688
- }
1689
- })
1690
- }
1691
-
1692
- Feed.prototype.extension = function (name, message) {
1693
- var peers = this.peers
1694
-
1695
- for (var i = 0; i < peers.length; i++) {
1696
- peers[i].extension(name, message)
505
+ _decode (enc, block) {
506
+ block = block.subarray(this.padding)
507
+ if (enc) return c.decode(enc, block)
508
+ return block
1697
509
  }
1698
510
  }
1699
511
 
1700
512
  function noop () {}
1701
513
 
1702
- function verifyNode (trusted, node) {
1703
- return trusted && trusted.index === node.index && Buffer.compare(trusted.hash, node.hash) === 0
1704
- }
1705
-
1706
- function addSize (size, node) {
1707
- return size + node.size
1708
- }
1709
-
1710
- function isBlock (index) {
1711
- return (index & 1) === 0
1712
- }
1713
-
1714
- function toCodec (enc) {
1715
- // Switch to ndjson encoding if JSON is used. That way data files parse like ndjson \o/
1716
- return codecs(enc === 'json' ? 'ndjson' : enc)
1717
- }
1718
-
1719
- function wrapCodec (enc, cb) {
1720
- return function (err, buf) {
1721
- if (err) return cb(err)
1722
- try {
1723
- buf = enc.decode(buf)
1724
- } catch (err) {
1725
- return cb(err)
1726
- }
1727
- cb(null, buf)
1728
- }
514
+ function isStream (s) {
515
+ return typeof s === 'object' && s && typeof s.pipe === 'function'
1729
516
  }
1730
517
 
1731
- function timeoutCallback (cb, timeout) {
1732
- var failed = false
1733
- var id = setTimeout(ontimeout, timeout)
1734
- return done
1735
-
1736
- function ontimeout () {
1737
- failed = true
1738
- // TODO: make libs/errors for all this stuff
1739
- var err = new Error('ETIMEDOUT')
1740
- err.code = 'ETIMEDOUT'
1741
- cb(err)
1742
- }
1743
-
1744
- function done (err, val) {
1745
- if (failed) return
1746
- clearTimeout(id)
1747
- cb(err, val)
518
+ function requireMaybe (name) {
519
+ try {
520
+ return require(name)
521
+ } catch (_) {
522
+ return null
1748
523
  }
1749
524
  }
1750
525
 
1751
- function toWantRange (i) {
1752
- return Math.floor(i / 1024 / 1024) * 1024 * 1024
526
+ function toHex (buf) {
527
+ return buf && b4a.toString(buf, 'hex')
1753
528
  }
1754
529
 
1755
- function createError (code, errno, msg) {
1756
- var err = new Error(msg)
1757
- err.code = code
1758
- err.errno = errno
1759
- return err
530
+ function reduce (iter, fn, acc) {
531
+ for (const item of iter) acc = fn(acc, item)
532
+ return acc
1760
533
  }
1761
534
 
1762
- function defaultStorageDir (directory) {
1763
- return function (name) {
1764
- return defaultStorage(name, { directory })
1765
- }
535
+ function min (arr) {
536
+ return reduce(arr, (a, b) => Math.min(a, b), Infinity)
1766
537
  }
1767
538
 
1768
- function isOptions (initiator) {
1769
- return !Protocol.isProtocolStream(initiator) &&
1770
- typeof initiator === 'object' &&
1771
- !!initiator &&
1772
- typeof initiator.initiator === 'boolean'
539
+ function max (arr) {
540
+ return reduce(arr, (a, b) => Math.max(a, b), -Infinity)
1773
541
  }
1774
542
 
1775
- function readyNT (ifAvailable, fn) {
1776
- ifAvailable.ready(fn)
1777
- }
543
+ function preappend (blocks) {
544
+ const offset = this.core.tree.length
545
+ const fork = this.core.tree.fork
1778
546
 
1779
- function verifyCompat (self, checksum, signature, cb) {
1780
- self.crypto.verify(checksum, signature, self.key, function (err, valid) {
1781
- if (err || valid) return cb(err, valid)
1782
- // compat mode, will be removed in a later version
1783
- self.crypto.verify(checksum.slice(0, 32), signature, self.key, cb)
1784
- })
547
+ for (let i = 0; i < blocks.length; i++) {
548
+ this.encryption.encrypt(offset + i, blocks[i], fork)
549
+ }
1785
550
  }