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 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 = this._openSession(key, storage, opts)
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.auth = Core.createAuth(this.crypto, { keyPair })
249
- this.writable = !this._readonly && !!this.auth && !!this.auth.sign
258
+ this.keyPair = keyPair
259
+ this.writable = this._isWritable()
250
260
  }
251
261
 
252
262
  _passCapabilities (o) {
253
- if (!this.auth) this.auth = o.auth
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 = !this._readonly && !!this.auth && !!this.auth.sign
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, upgrade } = this._clone
310
+ const { from, signature } = this._clone
310
311
  await from.opening
311
- await this.core.copyFrom(from.core, upgrade)
312
+ await this.core.copyFrom(from.core, signature)
312
313
  this._clone = null
313
314
  }
314
315
  }
315
316
 
316
- if (!this.auth) this.auth = this.core.defaultAuth
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 !== false, // default to true for now
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
- auth: opts.auth,
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 key = opts.key === undefined ? opts.keyPair ? null : this.key : opts.key
475
- const keyPair = (opts.auth || opts.keyPair === undefined) ? null : opts.keyPair
478
+ const manifest = opts.manifest || defaultSignerManifest(keyPair.publicKey)
479
+ const key = opts.key || (opts.compat !== false ? manifest.signer.publicKey : manifestHash(manifest))
476
480
 
477
- let auth = this.core.defaultAuth
478
- if (opts.auth) {
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 upgrade = opts.upgrade === undefined ? null : opts.upgrade
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
- auth,
503
+ manifest,
501
504
  overwrite: true,
502
505
  clone: {
503
506
  from: this,
504
- upgrade
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 { fork = this.core.tree.fork + 1, force = false } = typeof opts === 'number' ? { fork: opts } : opts
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, this.auth)
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
- if (this.writable === false) throw SESSION_NOT_WRITABLE()
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, (opts && opts.auth) || this.auth, { preappend })
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 { SESSION_NOT_WRITABLE, BLOCK_NOT_AVAILABLE, SESSION_CLOSED } = require('hypercore-errors')
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 auth () {
63
- return this.session.auth
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
- const { fork = this.fork + 1, force = false } = typeof opts === 'number' ? { fork: opts } : opts
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 (length = this._appends.length, auth) {
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, auth)
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, auth) { // TODO: make this safe to interact with a parallel truncate...
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 info = await this.session.append(flushingLength < this._appends.length ? this._appends.slice(0, flushingLength) : this._appends, { auth })
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.manifestHash = function (manifest) {
20
- const state = { start: 0, end: 32 + manifest.byteLength, buffer: null }
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.treeSignableLegacy = function (hash, length, fork) {
39
- const state = { start: 0, end: 48, buffer: b4a.allocUnsafe(48) }
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)