hypercore 10.22.3 → 10.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +69 -51
- package/lib/batch.js +14 -15
- package/lib/caps.js +8 -14
- package/lib/core.js +95 -68
- package/lib/manifest.js +218 -0
- package/lib/merkle-tree.js +6 -6
- package/lib/messages.js +91 -5
- package/lib/multisig.js +123 -0
- package/lib/replicator.js +47 -21
- package/package.json +2 -1
package/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const Xache = require('xache')
|
|
|
8
8
|
const NoiseSecretStream = require('@hyperswarm/secret-stream')
|
|
9
9
|
const Protomux = require('protomux')
|
|
10
10
|
const z32 = require('z32')
|
|
11
|
+
const id = require('hypercore-id-encoding')
|
|
11
12
|
|
|
12
13
|
const Replicator = require('./lib/replicator')
|
|
13
14
|
const Core = require('./lib/core')
|
|
@@ -15,6 +16,7 @@ const BlockEncryption = require('./lib/block-encryption')
|
|
|
15
16
|
const Info = require('./lib/info')
|
|
16
17
|
const Download = require('./lib/download')
|
|
17
18
|
const Batch = require('./lib/batch')
|
|
19
|
+
const { manifestHash, defaultSignerManifest, createVerifier, createManifest, isCompat } = require('./lib/manifest')
|
|
18
20
|
const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
|
|
19
21
|
const {
|
|
20
22
|
BAD_ARGUMENT,
|
|
@@ -41,16 +43,9 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
41
43
|
key = opts.key || null
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
if (key && typeof key === 'string')
|
|
45
|
-
key = b4a.from(key, 'hex')
|
|
46
|
-
}
|
|
47
|
-
|
|
46
|
+
if (key && typeof key === 'string') key = id.decode(key)
|
|
48
47
|
if (!opts) opts = {}
|
|
49
48
|
|
|
50
|
-
if (!opts.crypto && key && key.byteLength !== 32) {
|
|
51
|
-
throw BAD_ARGUMENT('Hypercore key should be 32 bytes')
|
|
52
|
-
}
|
|
53
|
-
|
|
54
49
|
if (!storage) storage = opts.storage
|
|
55
50
|
|
|
56
51
|
this[promises] = true
|
|
@@ -69,7 +64,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
69
64
|
|
|
70
65
|
this.id = null
|
|
71
66
|
this.key = key || null
|
|
72
|
-
this.keyPair = null
|
|
67
|
+
this.keyPair = opts.keyPair || null
|
|
73
68
|
this.readable = true
|
|
74
69
|
this.writable = false
|
|
75
70
|
this.opened = false
|
|
@@ -77,22 +72,22 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
77
72
|
this.snapshotted = !!opts.snapshot
|
|
78
73
|
this.sparse = opts.sparse !== false
|
|
79
74
|
this.sessions = opts._sessions || [this]
|
|
80
|
-
this.auth = opts.auth || null
|
|
81
75
|
this.autoClose = !!opts.autoClose
|
|
82
76
|
this.onwait = opts.onwait || null
|
|
83
77
|
this.wait = opts.wait !== false
|
|
84
78
|
this.timeout = opts.timeout || 0
|
|
85
|
-
this._clone = opts.clone || null
|
|
86
|
-
this._readonly = opts.writable === false
|
|
87
|
-
|
|
88
79
|
this.closing = null
|
|
89
|
-
this.opening =
|
|
90
|
-
this.opening.catch(noop)
|
|
80
|
+
this.opening = null
|
|
91
81
|
|
|
82
|
+
this._clone = opts.clone || null
|
|
83
|
+
this._readonly = opts.writable === false
|
|
92
84
|
this._preappend = preappend.bind(this)
|
|
93
85
|
this._snapshot = null
|
|
94
86
|
this._batch = opts._batch || null
|
|
95
87
|
this._findingPeers = 0
|
|
88
|
+
|
|
89
|
+
this.opening = this._openSession(key, storage, opts)
|
|
90
|
+
this.opening.catch(noop)
|
|
96
91
|
}
|
|
97
92
|
|
|
98
93
|
[inspect] (depth, opts) {
|
|
@@ -139,6 +134,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
139
134
|
indent + ')'
|
|
140
135
|
}
|
|
141
136
|
|
|
137
|
+
static key (manifest, { compat } = {}) {
|
|
138
|
+
return compat ? manifest.signer.publicKey : manifestHash(manifest)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
static discoveryKey (key) {
|
|
142
|
+
return hypercoreCrypto.discoveryKey(key)
|
|
143
|
+
}
|
|
144
|
+
|
|
142
145
|
static getProtocolMuxer (stream) {
|
|
143
146
|
return stream.noiseStream.userData
|
|
144
147
|
}
|
|
@@ -245,20 +248,19 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
245
248
|
}
|
|
246
249
|
|
|
247
250
|
setKeyPair (keyPair) {
|
|
248
|
-
this.
|
|
249
|
-
this.writable =
|
|
251
|
+
this.keyPair = keyPair
|
|
252
|
+
this.writable = this._isWritable()
|
|
250
253
|
}
|
|
251
254
|
|
|
252
255
|
_passCapabilities (o) {
|
|
253
|
-
if (!this.
|
|
254
|
-
|
|
256
|
+
if (!this.keyPair) this.keyPair = o.keyPair
|
|
255
257
|
this.crypto = o.crypto
|
|
256
258
|
this.id = o.id
|
|
257
259
|
this.key = o.key
|
|
258
260
|
this.core = o.core
|
|
259
261
|
this.replicator = o.replicator
|
|
260
262
|
this.encryption = o.encryption
|
|
261
|
-
this.writable =
|
|
263
|
+
this.writable = this._isWritable()
|
|
262
264
|
this.autoClose = o.autoClose
|
|
263
265
|
|
|
264
266
|
if (this.snapshotted && this.core && !this._snapshot) this._updateSnapshot()
|
|
@@ -288,14 +290,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
288
290
|
if (!isFirst) await opts._opening
|
|
289
291
|
if (opts.preload) opts = { ...opts, ...(await this._retryPreload(opts.preload)) }
|
|
290
292
|
|
|
291
|
-
const keyPair = opts.keyPair
|
|
292
|
-
|
|
293
|
-
if (opts.auth) {
|
|
294
|
-
this.auth = opts.auth
|
|
295
|
-
} else if (keyPair && keyPair.secretKey) {
|
|
296
|
-
this.setKeyPair(keyPair)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
293
|
if (isFirst) {
|
|
300
294
|
await this._openCapabilities(key, storage, opts)
|
|
301
295
|
// Only the root session should pass capabilities to other sessions.
|
|
@@ -306,15 +300,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
306
300
|
|
|
307
301
|
// copy state over
|
|
308
302
|
if (this._clone) {
|
|
309
|
-
const { from,
|
|
303
|
+
const { from, signature } = this._clone
|
|
310
304
|
await from.opening
|
|
311
|
-
await this.core.copyFrom(from.core,
|
|
305
|
+
await this.core.copyFrom(from.core, signature)
|
|
312
306
|
this._clone = null
|
|
313
307
|
}
|
|
314
308
|
}
|
|
315
309
|
|
|
316
|
-
|
|
317
|
-
this.writable = !this._readonly && !!this.auth && !!this.auth.sign
|
|
310
|
+
this.writable = this._isWritable()
|
|
318
311
|
|
|
319
312
|
if (opts.valueEncoding) {
|
|
320
313
|
this.valueEncoding = c.from(opts.valueEncoding)
|
|
@@ -353,7 +346,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
353
346
|
this.storage = Hypercore.defaultStorage(opts.storage || storage, { unlocked, writable: !unlocked })
|
|
354
347
|
|
|
355
348
|
this.core = await Core.open(this.storage, {
|
|
356
|
-
compat: opts.compat
|
|
349
|
+
compat: opts.compat,
|
|
357
350
|
force: opts.force,
|
|
358
351
|
createIfMissing: opts.createIfMissing,
|
|
359
352
|
readonly: unlocked,
|
|
@@ -362,7 +355,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
362
355
|
keyPair: opts.keyPair,
|
|
363
356
|
crypto: this.crypto,
|
|
364
357
|
legacy: opts.legacy,
|
|
365
|
-
|
|
358
|
+
manifest: opts.manifest,
|
|
366
359
|
onupdate: this._oncoreupdate.bind(this),
|
|
367
360
|
onconflict: this._oncoreconflict.bind(this)
|
|
368
361
|
})
|
|
@@ -417,6 +410,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
417
410
|
return prev.length !== next.length || prev.fork !== next.fork
|
|
418
411
|
}
|
|
419
412
|
|
|
413
|
+
_isWritable () {
|
|
414
|
+
return !this._readonly && !!(this.keyPair && this.keyPair.secretKey)
|
|
415
|
+
}
|
|
416
|
+
|
|
420
417
|
close (err) {
|
|
421
418
|
if (this.closing) return this.closing
|
|
422
419
|
this.closing = this._close(err || null)
|
|
@@ -465,23 +462,22 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
465
462
|
this.emit('close', true)
|
|
466
463
|
}
|
|
467
464
|
|
|
468
|
-
clone (storage, opts = {}) {
|
|
465
|
+
clone (keyPair, storage, opts = {}) {
|
|
469
466
|
// TODO: current limitation is no forking
|
|
470
467
|
if ((opts.fork && opts.fork !== 0) || this.fork !== 0) {
|
|
471
468
|
throw BAD_ARGUMENT('Cannot clone a fork')
|
|
472
469
|
}
|
|
473
470
|
|
|
474
|
-
const
|
|
475
|
-
const
|
|
471
|
+
const manifest = opts.manifest || defaultSignerManifest(keyPair.publicKey)
|
|
472
|
+
const key = opts.key || (opts.compat !== false ? manifest.signer.publicKey : manifestHash(manifest))
|
|
476
473
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
auth = opts.auth
|
|
480
|
-
} else if (keyPair && keyPair.secretKey) {
|
|
481
|
-
auth = Core.createAuth(this.crypto, { keyPair, manifest: { signer: { publicKey: keyPair.publicKey } } })
|
|
474
|
+
if (b4a.equals(key, this.key)) {
|
|
475
|
+
throw BAD_ARGUMENT('Clone cannot share verification information')
|
|
482
476
|
}
|
|
483
477
|
|
|
484
|
-
const
|
|
478
|
+
const signature = opts.signature === undefined
|
|
479
|
+
? createVerifier(createManifest(manifest), { compat: isCompat(key, manifest) }).sign(this.core.tree.batch(), keyPair)
|
|
480
|
+
: opts.signature
|
|
485
481
|
|
|
486
482
|
const sparse = opts.sparse === false ? false : this.sparse
|
|
487
483
|
const wait = opts.wait === false ? false : this.wait
|
|
@@ -497,11 +493,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
497
493
|
wait,
|
|
498
494
|
onwait,
|
|
499
495
|
timeout,
|
|
500
|
-
|
|
496
|
+
manifest,
|
|
501
497
|
overwrite: true,
|
|
502
498
|
clone: {
|
|
503
499
|
from: this,
|
|
504
|
-
|
|
500
|
+
signature
|
|
505
501
|
}
|
|
506
502
|
})
|
|
507
503
|
}
|
|
@@ -542,6 +538,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
542
538
|
return this.replicator === null ? null : this.replicator.discoveryKey
|
|
543
539
|
}
|
|
544
540
|
|
|
541
|
+
get manifest () {
|
|
542
|
+
return this.core === null ? null : this.core.header.manifest
|
|
543
|
+
}
|
|
544
|
+
|
|
545
545
|
get length () {
|
|
546
546
|
if (this._snapshot) return this._snapshot.length
|
|
547
547
|
if (this.core === null) return 0
|
|
@@ -633,6 +633,12 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
633
633
|
this.replicator.onupgrade()
|
|
634
634
|
}
|
|
635
635
|
|
|
636
|
+
if (status & 0b10000) {
|
|
637
|
+
for (let i = 0; i < this.sessions.length; i++) {
|
|
638
|
+
this.sessions[i].emit('manifest')
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
636
642
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
637
643
|
const s = this.sessions[i]
|
|
638
644
|
|
|
@@ -932,22 +938,34 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
932
938
|
|
|
933
939
|
async truncate (newLength = 0, opts = {}) {
|
|
934
940
|
if (this.opened === false) await this.opening
|
|
935
|
-
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
936
941
|
|
|
937
|
-
const {
|
|
942
|
+
const {
|
|
943
|
+
fork = this.core.tree.fork + 1,
|
|
944
|
+
force = false,
|
|
945
|
+
keyPair = this.keyPair,
|
|
946
|
+
signature = null
|
|
947
|
+
} = typeof opts === 'number' ? { fork: opts } : opts
|
|
948
|
+
|
|
949
|
+
const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))
|
|
950
|
+
|
|
951
|
+
if (writable === false) throw SESSION_NOT_WRITABLE()
|
|
938
952
|
if (this._batch && !force) throw BATCH_UNFLUSHED()
|
|
939
953
|
|
|
940
|
-
await this.core.truncate(newLength, fork,
|
|
954
|
+
await this.core.truncate(newLength, fork, { keyPair, signature })
|
|
941
955
|
|
|
942
956
|
// TODO: Should propagate from an event triggered by the oplog
|
|
943
957
|
this.replicator.updateAll()
|
|
944
958
|
}
|
|
945
959
|
|
|
946
|
-
async append (blocks, opts) {
|
|
960
|
+
async append (blocks, opts = {}) {
|
|
947
961
|
if (this._batch && this !== this._batch.session) throw BATCH_UNFLUSHED()
|
|
948
962
|
|
|
949
963
|
if (this.opened === false) await this.opening
|
|
950
|
-
|
|
964
|
+
|
|
965
|
+
const { keyPair = this.keyPair, signature = null } = opts
|
|
966
|
+
const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))
|
|
967
|
+
|
|
968
|
+
if (writable === false) throw SESSION_NOT_WRITABLE()
|
|
951
969
|
|
|
952
970
|
blocks = Array.isArray(blocks) ? blocks : [blocks]
|
|
953
971
|
|
|
@@ -961,7 +979,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
961
979
|
}
|
|
962
980
|
}
|
|
963
981
|
|
|
964
|
-
return this.core.append(buffers,
|
|
982
|
+
return this.core.append(buffers, { keyPair, signature, preappend })
|
|
965
983
|
}
|
|
966
984
|
|
|
967
985
|
async treeHash (length) {
|
package/lib/batch.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { BLOCK_NOT_AVAILABLE, SESSION_CLOSED } = require('hypercore-errors')
|
|
2
2
|
const EventEmitter = require('events')
|
|
3
3
|
const c = require('compact-encoding')
|
|
4
4
|
|
|
@@ -11,6 +11,7 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
11
11
|
this.closed = false
|
|
12
12
|
this.opening = null
|
|
13
13
|
this.closing = null
|
|
14
|
+
this.writable = true // always writable...
|
|
14
15
|
this.autoClose = autoClose
|
|
15
16
|
this.fork = 0
|
|
16
17
|
|
|
@@ -51,16 +52,12 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
51
52
|
return this._sessionByteLength + this._byteLength
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
get writable () {
|
|
55
|
-
return this.session.writable
|
|
56
|
-
}
|
|
57
|
-
|
|
58
55
|
get core () {
|
|
59
56
|
return this.session.core
|
|
60
57
|
}
|
|
61
58
|
|
|
62
|
-
get
|
|
63
|
-
return this.session.
|
|
59
|
+
get manifest () {
|
|
60
|
+
return this.session.manifest
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
async ready () {
|
|
@@ -172,20 +169,20 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
172
169
|
|
|
173
170
|
async truncate (newLength = 0, opts = {}) {
|
|
174
171
|
if (this.opened === false) await this.opening
|
|
175
|
-
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
176
172
|
if (this.closing) throw SESSION_CLOSED()
|
|
177
173
|
|
|
178
174
|
// wait for any pending flush... (prop needs a lock)
|
|
179
175
|
await this._waitForFlush()
|
|
180
176
|
|
|
181
|
-
|
|
177
|
+
if (typeof opts === 'number') opts = { fork: opts }
|
|
178
|
+
const { fork = this.fork + 1, force = false } = opts
|
|
182
179
|
|
|
183
180
|
const length = this._sessionLength
|
|
184
181
|
if (newLength < length) {
|
|
185
182
|
if (!force) throw new Error('Cannot truncate committed blocks')
|
|
186
183
|
this._appends.length = 0
|
|
187
184
|
this._byteLength = 0
|
|
188
|
-
await this.session.truncate(newLength, { fork, force: true })
|
|
185
|
+
await this.session.truncate(newLength, { fork, force: true, ...opts })
|
|
189
186
|
this._sessionLength = this.session.length
|
|
190
187
|
this._sessionByteLength = this.session.byteLength
|
|
191
188
|
} else {
|
|
@@ -202,7 +199,6 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
202
199
|
const session = this.session
|
|
203
200
|
|
|
204
201
|
if (this.opened === false) await this.opening
|
|
205
|
-
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
206
202
|
if (this.closing) throw SESSION_CLOSED()
|
|
207
203
|
|
|
208
204
|
// wait for any pending flush... (prop needs a lock)
|
|
@@ -230,12 +226,14 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
230
226
|
return info
|
|
231
227
|
}
|
|
232
228
|
|
|
233
|
-
async flush (
|
|
229
|
+
async flush (opts = {}) {
|
|
234
230
|
if (this.opened === false) await this.opening
|
|
235
231
|
if (this.closing) throw SESSION_CLOSED()
|
|
236
232
|
|
|
233
|
+
const { length = this._appends.length, keyPair = this.session.keyPair, signature = null } = opts
|
|
234
|
+
|
|
237
235
|
while (this._flushing) await this._flushing
|
|
238
|
-
this._flushing = this._flush(length,
|
|
236
|
+
this._flushing = this._flush(length, keyPair, signature)
|
|
239
237
|
|
|
240
238
|
try {
|
|
241
239
|
await this._flushing
|
|
@@ -246,11 +244,12 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
246
244
|
if (this.autoClose) await this.close()
|
|
247
245
|
}
|
|
248
246
|
|
|
249
|
-
async _flush (length,
|
|
247
|
+
async _flush (length, keyPair, signature) { // TODO: make this safe to interact with a parallel truncate...
|
|
250
248
|
if (this._appends.length === 0) return
|
|
251
249
|
|
|
252
250
|
const flushingLength = Math.min(length, this._appends.length)
|
|
253
|
-
const
|
|
251
|
+
const blocks = flushingLength < this._appends.length ? this._appends.slice(0, flushingLength) : this._appends
|
|
252
|
+
const info = await this.session.append(blocks, { keyPair, signature })
|
|
254
253
|
const delta = info.byteLength - this._sessionByteLength
|
|
255
254
|
|
|
256
255
|
this._sessionLength = info.length
|
package/lib/caps.js
CHANGED
|
@@ -8,6 +8,7 @@ const c = require('compact-encoding')
|
|
|
8
8
|
|
|
9
9
|
const [TREE, REPLICATE_INITIATOR, REPLICATE_RESPONDER, MANIFEST, DEFAULT_NAMESPACE] = crypto.namespace('hypercore', 5)
|
|
10
10
|
|
|
11
|
+
exports.MANIFEST = MANIFEST
|
|
11
12
|
exports.DEFAULT_NAMESPACE = DEFAULT_NAMESPACE
|
|
12
13
|
|
|
13
14
|
exports.replicate = function (isInitiator, key, handshakeHash) {
|
|
@@ -16,27 +17,20 @@ exports.replicate = function (isInitiator, key, handshakeHash) {
|
|
|
16
17
|
return out
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
exports.
|
|
20
|
-
const state = { start: 0, end:
|
|
21
|
-
state.buffer = b4a.allocUnsafe(state.end)
|
|
22
|
-
c.raw.encode(state, MANIFEST)
|
|
23
|
-
c.raw.encode(state, manifest)
|
|
24
|
-
const out = b4a.allocUnsafe(32)
|
|
25
|
-
sodium.crypto_generichash(out, state.buffer)
|
|
26
|
-
return out
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
exports.treeSignable = function (hash, length, fork) {
|
|
30
|
-
const state = { start: 0, end: 80, buffer: b4a.allocUnsafe(80) }
|
|
20
|
+
exports.treeSignable = function (namespace, hash, length, fork) {
|
|
21
|
+
const state = { start: 0, end: 112, buffer: b4a.allocUnsafe(112) }
|
|
31
22
|
c.raw.encode(state, TREE)
|
|
23
|
+
c.raw.encode(state, namespace)
|
|
32
24
|
c.raw.encode(state, hash)
|
|
33
25
|
c.uint64.encode(state, length)
|
|
34
26
|
c.uint64.encode(state, fork)
|
|
35
27
|
return state.buffer
|
|
36
28
|
}
|
|
37
29
|
|
|
38
|
-
exports.
|
|
39
|
-
const
|
|
30
|
+
exports.treeSignableCompat = function (hash, length, fork, noHeader) {
|
|
31
|
+
const end = noHeader ? 48 : 80
|
|
32
|
+
const state = { start: 0, end, buffer: b4a.allocUnsafe(end) }
|
|
33
|
+
if (!noHeader) c.raw.encode(state, TREE) // ultra legacy mode, kill in future major
|
|
40
34
|
c.raw.encode(state, hash)
|
|
41
35
|
c.uint64.encode(state, length)
|
|
42
36
|
c.uint64.encode(state, fork)
|