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.
- package/.github/workflows/test-node.yml +3 -4
- package/README.md +123 -409
- 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 +362 -1597
- 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/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/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/streams.js +0 -162
- 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,1780 +1,545 @@
|
|
|
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')
|
|
52
17
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Nanoresource.call(this)
|
|
18
|
+
const promises = Symbol.for('hypercore.promises')
|
|
19
|
+
const inspect = Symbol.for('nodejs.util.inspect.custom')
|
|
56
20
|
|
|
57
|
-
|
|
58
|
-
|
|
21
|
+
module.exports = class Hypercore extends EventEmitter {
|
|
22
|
+
constructor (storage, key, opts) {
|
|
23
|
+
super()
|
|
59
24
|
|
|
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._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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
144
|
-
this.open(onerror)
|
|
38
|
+
if (!opts) opts = {}
|
|
145
39
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
40
|
+
if (!opts.crypto && key && key.byteLength !== 32) {
|
|
41
|
+
throw new Error('Hypercore key should be 32 bytes')
|
|
42
|
+
}
|
|
149
43
|
|
|
150
|
-
|
|
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
|
-
|
|
156
|
-
if (!self._merkle) return self._reloadMerkleStateBeforeAppend(work, values, cb)
|
|
157
|
-
self._append(values, cb)
|
|
158
|
-
}
|
|
46
|
+
this[promises] = true
|
|
159
47
|
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67
|
+
this.closing = null
|
|
68
|
+
this.opening = opts._opening || this._open(key, storage, opts)
|
|
69
|
+
this.opening.catch(noop)
|
|
168
70
|
|
|
169
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
92
|
+
static createProtocolStream (isInitiator, opts) {
|
|
93
|
+
const noiseStream = new NoiseSecretStream(isInitiator, null, opts)
|
|
94
|
+
return noiseStream.rawStream
|
|
224
95
|
}
|
|
225
96
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
294
|
-
if (
|
|
295
|
-
|
|
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
|
-
|
|
326
|
-
|
|
116
|
+
const Clz = opts.class || Hypercore
|
|
117
|
+
const keyPair = opts.keyPair && opts.keyPair.secretKey && { ...opts.keyPair }
|
|
327
118
|
|
|
328
|
-
|
|
329
|
-
|
|
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
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
354
|
-
|
|
132
|
+
s._initSession(this)
|
|
133
|
+
this.sessions.push(s)
|
|
355
134
|
|
|
356
|
-
|
|
357
|
-
return process.nextTick(w.callback, new Error('Expected length is less than current length'))
|
|
135
|
+
return s
|
|
358
136
|
}
|
|
359
137
|
|
|
360
|
-
|
|
361
|
-
if (
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
424
|
-
|
|
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
|
-
|
|
438
|
-
|
|
439
|
-
var generatedKey = false
|
|
440
|
-
var retryOpen = true
|
|
160
|
+
const i = this.sessions.indexOf(this)
|
|
161
|
+
if (i === -1) return
|
|
441
162
|
|
|
442
|
-
|
|
163
|
+
this.sessions.splice(i, 1)
|
|
164
|
+
this.readable = false
|
|
165
|
+
this.writable = false
|
|
166
|
+
this.closed = true
|
|
443
167
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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
|
-
|
|
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
|
-
|
|
462
|
-
|
|
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
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
181
|
+
replicate (isInitiator, opts = {}) {
|
|
182
|
+
let outerStream = isStream(isInitiator)
|
|
183
|
+
? isInitiator
|
|
184
|
+
: opts.stream
|
|
185
|
+
let noiseStream = null
|
|
475
186
|
|
|
476
|
-
if (
|
|
477
|
-
|
|
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 (
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
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
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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
|
-
|
|
579
|
-
range.end = max
|
|
208
|
+
return outerStream
|
|
580
209
|
}
|
|
581
210
|
|
|
582
|
-
|
|
583
|
-
|
|
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
|
-
|
|
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
|
-
|
|
615
|
-
|
|
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
|
-
|
|
643
|
-
|
|
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
|
-
|
|
702
|
-
|
|
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
|
-
|
|
724
|
-
this.
|
|
231
|
+
get padding () {
|
|
232
|
+
return this.encryption === null ? 0 : this.encryption.padding
|
|
725
233
|
}
|
|
726
234
|
|
|
727
|
-
|
|
728
|
-
|
|
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
|
-
|
|
747
|
-
|
|
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
|
-
|
|
751
|
-
if (err) return cb(err)
|
|
242
|
+
this.valueEncoding = opts.valueEncoding ? c.from(codecs(opts.valueEncoding)) : null
|
|
752
243
|
|
|
753
|
-
|
|
244
|
+
const keyPair = (key && opts.keyPair)
|
|
245
|
+
? { ...opts.keyPair, publicKey: key }
|
|
246
|
+
: key
|
|
247
|
+
? { publicKey: key, secretKey: null }
|
|
248
|
+
: opts.keyPair
|
|
754
249
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
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 (!
|
|
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
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
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
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
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
|
-
|
|
784
|
-
|
|
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
|
-
|
|
818
|
-
this._getRootsToVerify(index * 2 + 2, {}, [], cb)
|
|
819
|
-
}
|
|
282
|
+
if (!this.sign) this.sign = opts.sign || this.core.defaultSign
|
|
820
283
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
860
|
-
self._updatePeers()
|
|
861
|
-
if (w.ifAvailable) self._ifAvailableSeek(w)
|
|
862
|
-
})
|
|
295
|
+
if (opts.postload) await opts.postload(this)
|
|
863
296
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
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
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
946
|
-
|
|
325
|
+
if (value) {
|
|
326
|
+
const byteLength = value.byteLength - this.padding
|
|
947
327
|
|
|
948
|
-
|
|
949
|
-
|
|
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
|
-
|
|
974
|
-
|
|
975
|
-
|
|
334
|
+
_onpeerupdate (added, peer) {
|
|
335
|
+
if (added) this.extensions.update(peer)
|
|
336
|
+
const name = added ? 'peer-add' : 'peer-remove'
|
|
976
337
|
|
|
977
|
-
|
|
978
|
-
|
|
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
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
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
|
-
|
|
1022
|
-
if (
|
|
1023
|
-
|
|
1024
|
-
|
|
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
|
-
|
|
1028
|
-
if (
|
|
1029
|
-
|
|
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
|
-
|
|
1042
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1072
|
-
|
|
1073
|
-
this.tree.set(2 * index)
|
|
371
|
+
async has (index) {
|
|
372
|
+
if (this.opened === false) await this.opening
|
|
1074
373
|
|
|
1075
|
-
|
|
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
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
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
|
-
|
|
1169
|
-
|
|
387
|
+
async _get (index, opts) {
|
|
388
|
+
const encoding = (opts && opts.valueEncoding && c.from(codecs(opts.valueEncoding))) || this.valueEncoding
|
|
1170
389
|
|
|
1171
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1262
|
-
|
|
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
|
-
|
|
1343
|
-
|
|
1344
|
-
var codec = enc ? toCodec(enc) : this._codec
|
|
403
|
+
download (range) {
|
|
404
|
+
const linear = !!(range && range.linear)
|
|
1345
405
|
|
|
1346
|
-
|
|
406
|
+
let start
|
|
407
|
+
let end
|
|
408
|
+
let filter
|
|
1347
409
|
|
|
1348
|
-
|
|
1349
|
-
|
|
410
|
+
if (range && range.blocks) {
|
|
411
|
+
const blocks = range.blocks instanceof Set
|
|
412
|
+
? range.blocks
|
|
413
|
+
: new Set(range.blocks)
|
|
1350
414
|
|
|
1351
|
-
|
|
415
|
+
start = range.start || (blocks.size ? min(range.blocks) : 0)
|
|
416
|
+
end = range.end || (blocks.size ? max(range.blocks) + 1 : 0)
|
|
1352
417
|
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
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
|
-
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
424
|
+
const r = Replicator.createRange(start, end, filter, linear)
|
|
1364
425
|
|
|
1365
|
-
|
|
1366
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
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
|
|
1430
|
-
|
|
1431
|
-
|
|
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
|
-
|
|
1438
|
-
|
|
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
|
-
|
|
1441
|
-
|
|
446
|
+
if (fork === -1) fork = this.core.tree.fork + 1
|
|
447
|
+
await this.core.truncate(newLength, fork, this.sign)
|
|
1442
448
|
|
|
1443
|
-
|
|
1444
|
-
|
|
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
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1472
|
-
|
|
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
|
-
|
|
1480
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1525
|
-
|
|
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
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
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
|
-
|
|
1555
|
-
|
|
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
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1595
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1608
|
-
|
|
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
|
|
1698
|
-
return
|
|
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
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
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
|
|
1747
|
-
return
|
|
521
|
+
function toHex (buf) {
|
|
522
|
+
return buf && b4a.toString(buf, 'hex')
|
|
1748
523
|
}
|
|
1749
524
|
|
|
1750
|
-
function
|
|
1751
|
-
|
|
1752
|
-
|
|
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
|
|
1758
|
-
return
|
|
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
|
|
1764
|
-
return
|
|
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
|
|
1771
|
-
|
|
1772
|
-
|
|
538
|
+
function preappend (blocks) {
|
|
539
|
+
const offset = this.core.tree.length
|
|
540
|
+
const fork = this.core.tree.fork
|
|
1773
541
|
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
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
|
}
|