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.
- package/.github/workflows/test-node.yml +3 -4
- package/README.md +131 -404
- package/__snapshots__/test/storage.js.snapshot.cjs +15 -0
- package/examples/announce.js +19 -0
- package/examples/basic.js +10 -0
- package/examples/http.js +123 -0
- package/examples/lookup.js +20 -0
- package/index.js +365 -1600
- package/lib/bitfield.js +113 -285
- package/lib/block-encryption.js +68 -0
- package/lib/block-store.js +58 -0
- package/lib/core.js +468 -0
- package/lib/extensions.js +76 -0
- package/lib/merkle-tree.js +1110 -0
- package/lib/messages.js +571 -0
- package/lib/mutex.js +39 -0
- package/lib/oplog.js +224 -0
- package/lib/protocol.js +525 -0
- package/lib/random-iterator.js +46 -0
- package/lib/remote-bitfield.js +24 -0
- package/lib/replicator.js +857 -0
- package/lib/streams.js +39 -0
- package/package.json +44 -45
- package/test/basic.js +59 -471
- package/test/bitfield.js +48 -133
- package/test/core.js +290 -0
- package/test/encodings.js +18 -0
- package/test/encryption.js +123 -0
- package/test/extension.js +71 -0
- package/test/helpers/index.js +23 -0
- package/test/merkle-tree.js +518 -0
- package/test/mutex.js +137 -0
- package/test/oplog.js +399 -0
- package/test/preload.js +72 -0
- package/test/replicate.js +227 -824
- package/test/sessions.js +173 -0
- package/test/storage.js +31 -0
- package/test/streams.js +39 -146
- package/test/user-data.js +47 -0
- package/bench/all.sh +0 -65
- package/bench/copy-64kb-blocks.js +0 -51
- package/bench/helpers/read-throttled.js +0 -27
- package/bench/helpers/read.js +0 -47
- package/bench/helpers/write.js +0 -29
- package/bench/read-16kb-blocks-proof-throttled.js +0 -1
- package/bench/read-16kb-blocks-proof.js +0 -1
- package/bench/read-16kb-blocks-throttled.js +0 -1
- package/bench/read-16kb-blocks.js +0 -1
- package/bench/read-512b-blocks.js +0 -1
- package/bench/read-64kb-blocks-linear-batch.js +0 -18
- package/bench/read-64kb-blocks-linear.js +0 -18
- package/bench/read-64kb-blocks-proof.js +0 -1
- package/bench/read-64kb-blocks.js +0 -1
- package/bench/replicate-16kb-blocks.js +0 -19
- package/bench/replicate-64kb-blocks.js +0 -19
- package/bench/write-16kb-blocks.js +0 -1
- package/bench/write-512b-blocks.js +0 -1
- package/bench/write-64kb-blocks-static.js +0 -1
- package/bench/write-64kb-blocks.js +0 -1
- package/example.js +0 -23
- package/lib/cache.js +0 -26
- package/lib/crypto.js +0 -5
- package/lib/replicate.js +0 -829
- package/lib/safe-buffer-equals.js +0 -6
- package/lib/storage.js +0 -421
- package/lib/tree-index.js +0 -183
- package/test/ack.js +0 -306
- package/test/audit.js +0 -36
- package/test/cache.js +0 -93
- package/test/compat.js +0 -209
- package/test/copy.js +0 -377
- package/test/default-storage.js +0 -51
- package/test/extensions.js +0 -137
- package/test/get.js +0 -64
- package/test/head.js +0 -65
- package/test/helpers/create-tracking-ram.js +0 -27
- package/test/helpers/create.js +0 -6
- package/test/helpers/replicate.js +0 -4
- package/test/seek.js +0 -234
- package/test/selections.js +0 -95
- package/test/set-uploading-downloading.js +0 -91
- package/test/stats.js +0 -77
- package/test/timeouts.js +0 -22
- package/test/tree-index.js +0 -841
- package/test/update.js +0 -156
- package/test/value-encoding.js +0 -52
package/index.js
CHANGED
|
@@ -1,1785 +1,550 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
Nanoresource.call(this)
|
|
19
|
+
const promises = Symbol.for('hypercore.promises')
|
|
20
|
+
const inspect = Symbol.for('nodejs.util.inspect.custom')
|
|
56
21
|
|
|
57
|
-
|
|
58
|
-
|
|
22
|
+
module.exports = class Hypercore extends EventEmitter {
|
|
23
|
+
constructor (storage, key, opts) {
|
|
24
|
+
super()
|
|
59
25
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
key
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
145
|
-
this.open(onerror)
|
|
39
|
+
if (!opts) opts = {}
|
|
146
40
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
41
|
+
if (!opts.crypto && key && key.byteLength !== 32) {
|
|
42
|
+
throw new Error('Hypercore key should be 32 bytes')
|
|
43
|
+
}
|
|
150
44
|
|
|
151
|
-
|
|
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
|
-
|
|
157
|
-
if (!self._merkle) return self._reloadMerkleStateBeforeAppend(work, values, cb)
|
|
158
|
-
self._append(values, cb)
|
|
159
|
-
}
|
|
47
|
+
this[promises] = true
|
|
160
48
|
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
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
|
-
|
|
68
|
+
this.closing = null
|
|
69
|
+
this.opening = opts._opening || this._open(key, storage, opts)
|
|
70
|
+
this.opening.catch(noop)
|
|
169
71
|
|
|
170
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
93
|
+
static createProtocolStream (isInitiator, opts) {
|
|
94
|
+
const noiseStream = new NoiseSecretStream(isInitiator, null, opts)
|
|
95
|
+
return noiseStream.rawStream
|
|
225
96
|
}
|
|
226
97
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
295
|
-
if (
|
|
296
|
-
|
|
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
|
-
|
|
327
|
-
|
|
117
|
+
const Clz = opts.class || Hypercore
|
|
118
|
+
const keyPair = opts.keyPair && opts.keyPair.secretKey && { ...opts.keyPair }
|
|
328
119
|
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
-
|
|
355
|
-
|
|
133
|
+
s._initSession(this)
|
|
134
|
+
this.sessions.push(s)
|
|
356
135
|
|
|
357
|
-
|
|
358
|
-
return process.nextTick(w.callback, new Error('Expected length is less than current length'))
|
|
136
|
+
return s
|
|
359
137
|
}
|
|
360
138
|
|
|
361
|
-
|
|
362
|
-
if (
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
-
|
|
425
|
-
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
var generatedKey = false
|
|
441
|
-
var retryOpen = true
|
|
161
|
+
const i = this.sessions.indexOf(this)
|
|
162
|
+
if (i === -1) return
|
|
442
163
|
|
|
443
|
-
|
|
164
|
+
this.sessions.splice(i, 1)
|
|
165
|
+
this.readable = false
|
|
166
|
+
this.writable = false
|
|
167
|
+
this.closed = true
|
|
444
168
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
|
|
468
|
-
self._overwrite = true
|
|
469
|
-
}
|
|
177
|
+
await this.core.close()
|
|
470
178
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
state.key = state.secretKey = null
|
|
474
|
-
}
|
|
179
|
+
this.emit('close', true)
|
|
180
|
+
}
|
|
475
181
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
182
|
+
replicate (isInitiator, opts = {}) {
|
|
183
|
+
let outerStream = isStream(isInitiator)
|
|
184
|
+
? isInitiator
|
|
185
|
+
: opts.stream
|
|
186
|
+
let noiseStream = null
|
|
480
187
|
|
|
481
|
-
if (
|
|
482
|
-
|
|
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 (
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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
|
-
|
|
584
|
-
range.end = max
|
|
209
|
+
return outerStream
|
|
585
210
|
}
|
|
586
211
|
|
|
587
|
-
|
|
588
|
-
|
|
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
|
-
|
|
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
|
-
|
|
620
|
-
|
|
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
|
-
|
|
640
|
-
|
|
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
|
-
|
|
712
|
-
|
|
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
|
-
|
|
729
|
-
this.
|
|
232
|
+
get padding () {
|
|
233
|
+
return this.encryption === null ? 0 : this.encryption.padding
|
|
730
234
|
}
|
|
731
235
|
|
|
732
|
-
|
|
733
|
-
|
|
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
|
-
|
|
756
|
-
if (
|
|
240
|
+
async _open (key, storage, opts) {
|
|
241
|
+
if (opts.preload) opts = { ...opts, ...(await opts.preload()) }
|
|
757
242
|
|
|
758
|
-
|
|
243
|
+
this.valueEncoding = opts.valueEncoding ? c.from(codecs(opts.valueEncoding)) : null
|
|
759
244
|
|
|
760
|
-
|
|
245
|
+
const keyPair = (key && opts.keyPair)
|
|
246
|
+
? { ...opts.keyPair, publicKey: key }
|
|
247
|
+
: key
|
|
248
|
+
? { publicKey: key, secretKey: null }
|
|
249
|
+
: opts.keyPair
|
|
761
250
|
|
|
762
|
-
|
|
763
|
-
|
|
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 (!
|
|
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
|
-
|
|
776
|
-
|
|
777
|
-
|
|
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
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
840
|
-
|
|
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 (!
|
|
843
|
-
|
|
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
|
-
|
|
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
|
-
|
|
865
|
-
self._updatePeers()
|
|
866
|
-
if (w.ifAvailable) self._ifAvailableSeek(w)
|
|
867
|
-
})
|
|
296
|
+
if (opts.postload) await opts.postload(this)
|
|
868
297
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
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
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
951
|
-
|
|
326
|
+
if (value) {
|
|
327
|
+
const byteLength = value.byteLength - this.padding
|
|
952
328
|
|
|
953
|
-
|
|
954
|
-
|
|
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
|
-
|
|
967
|
-
|
|
968
|
-
|
|
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
|
-
|
|
975
|
-
|
|
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
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
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
|
-
|
|
1027
|
-
if (
|
|
1028
|
-
|
|
1029
|
-
|
|
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
|
-
|
|
1033
|
-
if (
|
|
1034
|
-
|
|
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
|
-
|
|
1039
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1059
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
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
|
-
|
|
1161
|
-
|
|
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
|
-
|
|
1174
|
-
}
|
|
391
|
+
let block
|
|
1175
392
|
|
|
1176
|
-
|
|
1177
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1267
|
-
|
|
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
|
-
|
|
1348
|
-
|
|
1349
|
-
var codec = enc ? toCodec(enc) : this._codec
|
|
408
|
+
download (range) {
|
|
409
|
+
const linear = !!(range && range.linear)
|
|
1350
410
|
|
|
1351
|
-
|
|
411
|
+
let start
|
|
412
|
+
let end
|
|
413
|
+
let filter
|
|
1352
414
|
|
|
1353
|
-
|
|
1354
|
-
|
|
415
|
+
if (range && range.blocks) {
|
|
416
|
+
const blocks = range.blocks instanceof Set
|
|
417
|
+
? range.blocks
|
|
418
|
+
: new Set(range.blocks)
|
|
1355
419
|
|
|
1356
|
-
|
|
420
|
+
start = range.start || (blocks.size ? min(range.blocks) : 0)
|
|
421
|
+
end = range.end || (blocks.size ? max(range.blocks) + 1 : 0)
|
|
1357
422
|
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
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
|
-
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
429
|
+
const r = Replicator.createRange(start, end, filter, linear)
|
|
1369
430
|
|
|
1370
|
-
|
|
1371
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
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
|
|
1435
|
-
|
|
1436
|
-
|
|
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
|
-
|
|
1443
|
-
|
|
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
|
-
|
|
1446
|
-
|
|
451
|
+
if (fork === -1) fork = this.core.tree.fork + 1
|
|
452
|
+
await this.core.truncate(newLength, fork, this.sign)
|
|
1447
453
|
|
|
1448
|
-
|
|
1449
|
-
|
|
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
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1477
|
-
|
|
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
|
-
|
|
1485
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1530
|
-
|
|
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
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
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
|
-
|
|
1560
|
-
|
|
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
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1600
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1613
|
-
|
|
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
|
|
1703
|
-
return
|
|
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
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
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
|
|
1752
|
-
return
|
|
526
|
+
function toHex (buf) {
|
|
527
|
+
return buf && b4a.toString(buf, 'hex')
|
|
1753
528
|
}
|
|
1754
529
|
|
|
1755
|
-
function
|
|
1756
|
-
|
|
1757
|
-
|
|
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
|
|
1763
|
-
return
|
|
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
|
|
1769
|
-
return
|
|
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
|
|
1776
|
-
|
|
1777
|
-
|
|
543
|
+
function preappend (blocks) {
|
|
544
|
+
const offset = this.core.tree.length
|
|
545
|
+
const fork = this.core.tree.fork
|
|
1778
546
|
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
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
|
}
|