hypercore 10.22.3 → 10.23.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 -43
- 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 +1 -1
package/index.js
CHANGED
|
@@ -15,6 +15,7 @@ const BlockEncryption = require('./lib/block-encryption')
|
|
|
15
15
|
const Info = require('./lib/info')
|
|
16
16
|
const Download = require('./lib/download')
|
|
17
17
|
const Batch = require('./lib/batch')
|
|
18
|
+
const { manifestHash, defaultSignerManifest, createVerifier, createManifest, isCompat } = require('./lib/manifest')
|
|
18
19
|
const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
|
|
19
20
|
const {
|
|
20
21
|
BAD_ARGUMENT,
|
|
@@ -69,7 +70,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
69
70
|
|
|
70
71
|
this.id = null
|
|
71
72
|
this.key = key || null
|
|
72
|
-
this.keyPair = null
|
|
73
|
+
this.keyPair = opts.keyPair || null
|
|
73
74
|
this.readable = true
|
|
74
75
|
this.writable = false
|
|
75
76
|
this.opened = false
|
|
@@ -77,22 +78,23 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
77
78
|
this.snapshotted = !!opts.snapshot
|
|
78
79
|
this.sparse = opts.sparse !== false
|
|
79
80
|
this.sessions = opts._sessions || [this]
|
|
80
|
-
this.auth = opts.auth || null
|
|
81
81
|
this.autoClose = !!opts.autoClose
|
|
82
82
|
this.onwait = opts.onwait || null
|
|
83
83
|
this.wait = opts.wait !== false
|
|
84
84
|
this.timeout = opts.timeout || 0
|
|
85
|
-
this._clone = opts.clone || null
|
|
86
|
-
this._readonly = opts.writable === false
|
|
87
|
-
|
|
88
85
|
this.closing = null
|
|
89
|
-
this.opening =
|
|
90
|
-
this.opening.catch(noop)
|
|
86
|
+
this.opening = null
|
|
91
87
|
|
|
88
|
+
this._manifest = opts.manifest || null
|
|
89
|
+
this._clone = opts.clone || null
|
|
90
|
+
this._readonly = opts.writable === false
|
|
92
91
|
this._preappend = preappend.bind(this)
|
|
93
92
|
this._snapshot = null
|
|
94
93
|
this._batch = opts._batch || null
|
|
95
94
|
this._findingPeers = 0
|
|
95
|
+
|
|
96
|
+
this.opening = this._openSession(key, storage, opts)
|
|
97
|
+
this.opening.catch(noop)
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
[inspect] (depth, opts) {
|
|
@@ -139,6 +141,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
139
141
|
indent + ')'
|
|
140
142
|
}
|
|
141
143
|
|
|
144
|
+
static key (manifest, { compat } = {}) {
|
|
145
|
+
return compat ? manifest.signer.publicKey : manifestHash(manifest)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static discoveryKey (key) {
|
|
149
|
+
return hypercoreCrypto.discoveryKey(key)
|
|
150
|
+
}
|
|
151
|
+
|
|
142
152
|
static getProtocolMuxer (stream) {
|
|
143
153
|
return stream.noiseStream.userData
|
|
144
154
|
}
|
|
@@ -245,20 +255,19 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
245
255
|
}
|
|
246
256
|
|
|
247
257
|
setKeyPair (keyPair) {
|
|
248
|
-
this.
|
|
249
|
-
this.writable =
|
|
258
|
+
this.keyPair = keyPair
|
|
259
|
+
this.writable = this._isWritable()
|
|
250
260
|
}
|
|
251
261
|
|
|
252
262
|
_passCapabilities (o) {
|
|
253
|
-
if (!this.
|
|
254
|
-
|
|
263
|
+
if (!this.keyPair) this.keyPair = o.keyPair
|
|
255
264
|
this.crypto = o.crypto
|
|
256
265
|
this.id = o.id
|
|
257
266
|
this.key = o.key
|
|
258
267
|
this.core = o.core
|
|
259
268
|
this.replicator = o.replicator
|
|
260
269
|
this.encryption = o.encryption
|
|
261
|
-
this.writable =
|
|
270
|
+
this.writable = this._isWritable()
|
|
262
271
|
this.autoClose = o.autoClose
|
|
263
272
|
|
|
264
273
|
if (this.snapshotted && this.core && !this._snapshot) this._updateSnapshot()
|
|
@@ -288,14 +297,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
288
297
|
if (!isFirst) await opts._opening
|
|
289
298
|
if (opts.preload) opts = { ...opts, ...(await this._retryPreload(opts.preload)) }
|
|
290
299
|
|
|
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
300
|
if (isFirst) {
|
|
300
301
|
await this._openCapabilities(key, storage, opts)
|
|
301
302
|
// Only the root session should pass capabilities to other sessions.
|
|
@@ -306,15 +307,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
306
307
|
|
|
307
308
|
// copy state over
|
|
308
309
|
if (this._clone) {
|
|
309
|
-
const { from,
|
|
310
|
+
const { from, signature } = this._clone
|
|
310
311
|
await from.opening
|
|
311
|
-
await this.core.copyFrom(from.core,
|
|
312
|
+
await this.core.copyFrom(from.core, signature)
|
|
312
313
|
this._clone = null
|
|
313
314
|
}
|
|
314
315
|
}
|
|
315
316
|
|
|
316
|
-
|
|
317
|
-
this.writable = !this._readonly && !!this.auth && !!this.auth.sign
|
|
317
|
+
this.writable = this._isWritable()
|
|
318
318
|
|
|
319
319
|
if (opts.valueEncoding) {
|
|
320
320
|
this.valueEncoding = c.from(opts.valueEncoding)
|
|
@@ -353,7 +353,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
353
353
|
this.storage = Hypercore.defaultStorage(opts.storage || storage, { unlocked, writable: !unlocked })
|
|
354
354
|
|
|
355
355
|
this.core = await Core.open(this.storage, {
|
|
356
|
-
compat: opts.compat
|
|
356
|
+
compat: opts.compat,
|
|
357
357
|
force: opts.force,
|
|
358
358
|
createIfMissing: opts.createIfMissing,
|
|
359
359
|
readonly: unlocked,
|
|
@@ -362,7 +362,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
362
362
|
keyPair: opts.keyPair,
|
|
363
363
|
crypto: this.crypto,
|
|
364
364
|
legacy: opts.legacy,
|
|
365
|
-
|
|
365
|
+
manifest: opts.manifest,
|
|
366
366
|
onupdate: this._oncoreupdate.bind(this),
|
|
367
367
|
onconflict: this._oncoreconflict.bind(this)
|
|
368
368
|
})
|
|
@@ -417,6 +417,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
417
417
|
return prev.length !== next.length || prev.fork !== next.fork
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
+
_isWritable () {
|
|
421
|
+
return !this._readonly && !!(this.keyPair && this.keyPair.secretKey)
|
|
422
|
+
}
|
|
423
|
+
|
|
420
424
|
close (err) {
|
|
421
425
|
if (this.closing) return this.closing
|
|
422
426
|
this.closing = this._close(err || null)
|
|
@@ -465,23 +469,22 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
465
469
|
this.emit('close', true)
|
|
466
470
|
}
|
|
467
471
|
|
|
468
|
-
clone (storage, opts = {}) {
|
|
472
|
+
clone (keyPair, storage, opts = {}) {
|
|
469
473
|
// TODO: current limitation is no forking
|
|
470
474
|
if ((opts.fork && opts.fork !== 0) || this.fork !== 0) {
|
|
471
475
|
throw BAD_ARGUMENT('Cannot clone a fork')
|
|
472
476
|
}
|
|
473
477
|
|
|
474
|
-
const
|
|
475
|
-
const
|
|
478
|
+
const manifest = opts.manifest || defaultSignerManifest(keyPair.publicKey)
|
|
479
|
+
const key = opts.key || (opts.compat !== false ? manifest.signer.publicKey : manifestHash(manifest))
|
|
476
480
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
auth = opts.auth
|
|
480
|
-
} else if (keyPair && keyPair.secretKey) {
|
|
481
|
-
auth = Core.createAuth(this.crypto, { keyPair, manifest: { signer: { publicKey: keyPair.publicKey } } })
|
|
481
|
+
if (b4a.equals(key, this.key)) {
|
|
482
|
+
throw BAD_ARGUMENT('Clone cannot share verification information')
|
|
482
483
|
}
|
|
483
484
|
|
|
484
|
-
const
|
|
485
|
+
const signature = opts.signature === undefined
|
|
486
|
+
? createVerifier(createManifest(manifest), { compat: isCompat(key, manifest) }).sign(this.core.tree.batch(), keyPair)
|
|
487
|
+
: opts.signature
|
|
485
488
|
|
|
486
489
|
const sparse = opts.sparse === false ? false : this.sparse
|
|
487
490
|
const wait = opts.wait === false ? false : this.wait
|
|
@@ -497,11 +500,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
497
500
|
wait,
|
|
498
501
|
onwait,
|
|
499
502
|
timeout,
|
|
500
|
-
|
|
503
|
+
manifest,
|
|
501
504
|
overwrite: true,
|
|
502
505
|
clone: {
|
|
503
506
|
from: this,
|
|
504
|
-
|
|
507
|
+
signature
|
|
505
508
|
}
|
|
506
509
|
})
|
|
507
510
|
}
|
|
@@ -542,6 +545,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
542
545
|
return this.replicator === null ? null : this.replicator.discoveryKey
|
|
543
546
|
}
|
|
544
547
|
|
|
548
|
+
get manifest () {
|
|
549
|
+
return this._manifest || (this.core === null ? null : this.core.header.manifest)
|
|
550
|
+
}
|
|
551
|
+
|
|
545
552
|
get length () {
|
|
546
553
|
if (this._snapshot) return this._snapshot.length
|
|
547
554
|
if (this.core === null) return 0
|
|
@@ -633,6 +640,13 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
633
640
|
this.replicator.onupgrade()
|
|
634
641
|
}
|
|
635
642
|
|
|
643
|
+
if (status & 0b10000) {
|
|
644
|
+
for (let i = 0; i < this.sessions.length; i++) {
|
|
645
|
+
const s = this.sessions[i]
|
|
646
|
+
if (!s._manifest) s.emit('manifest')
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
636
650
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
637
651
|
const s = this.sessions[i]
|
|
638
652
|
|
|
@@ -932,22 +946,34 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
932
946
|
|
|
933
947
|
async truncate (newLength = 0, opts = {}) {
|
|
934
948
|
if (this.opened === false) await this.opening
|
|
935
|
-
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
936
949
|
|
|
937
|
-
const {
|
|
950
|
+
const {
|
|
951
|
+
fork = this.core.tree.fork + 1,
|
|
952
|
+
force = false,
|
|
953
|
+
keyPair = this.keyPair,
|
|
954
|
+
signature = null
|
|
955
|
+
} = typeof opts === 'number' ? { fork: opts } : opts
|
|
956
|
+
|
|
957
|
+
const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))
|
|
958
|
+
|
|
959
|
+
if (writable === false) throw SESSION_NOT_WRITABLE()
|
|
938
960
|
if (this._batch && !force) throw BATCH_UNFLUSHED()
|
|
939
961
|
|
|
940
|
-
await this.core.truncate(newLength, fork,
|
|
962
|
+
await this.core.truncate(newLength, fork, { keyPair, signature })
|
|
941
963
|
|
|
942
964
|
// TODO: Should propagate from an event triggered by the oplog
|
|
943
965
|
this.replicator.updateAll()
|
|
944
966
|
}
|
|
945
967
|
|
|
946
|
-
async append (blocks, opts) {
|
|
968
|
+
async append (blocks, opts = {}) {
|
|
947
969
|
if (this._batch && this !== this._batch.session) throw BATCH_UNFLUSHED()
|
|
948
970
|
|
|
949
971
|
if (this.opened === false) await this.opening
|
|
950
|
-
|
|
972
|
+
|
|
973
|
+
const { keyPair = this.keyPair, signature = null } = opts
|
|
974
|
+
const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))
|
|
975
|
+
|
|
976
|
+
if (writable === false) throw SESSION_NOT_WRITABLE()
|
|
951
977
|
|
|
952
978
|
blocks = Array.isArray(blocks) ? blocks : [blocks]
|
|
953
979
|
|
|
@@ -961,7 +987,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
961
987
|
}
|
|
962
988
|
}
|
|
963
989
|
|
|
964
|
-
return this.core.append(buffers,
|
|
990
|
+
return this.core.append(buffers, { keyPair, signature, preappend })
|
|
965
991
|
}
|
|
966
992
|
|
|
967
993
|
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)
|