hypercore 9.11.0 → 10.0.0-alpha.10

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