hypercore 10.24.1 → 10.24.3

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
@@ -135,7 +135,7 @@ module.exports = class Hypercore extends EventEmitter {
135
135
  }
136
136
 
137
137
  static key (manifest, { compat } = {}) {
138
- return compat ? manifest.signer.publicKey : manifestHash(manifest)
138
+ return compat ? manifest.signer.publicKey : manifestHash(createManifest(manifest))
139
139
  }
140
140
 
141
141
  static discoveryKey (key) {
@@ -960,6 +960,9 @@ module.exports = class Hypercore extends EventEmitter {
960
960
  async append (blocks, opts = {}) {
961
961
  if (this._batch && this !== this._batch.session) throw BATCH_UNFLUSHED()
962
962
 
963
+ // if a batched append, run unlocked as it already locked...
964
+ const lock = !this._batch
965
+
963
966
  if (this.opened === false) await this.opening
964
967
 
965
968
  const { keyPair = this.keyPair, signature = null } = opts
@@ -979,7 +982,7 @@ module.exports = class Hypercore extends EventEmitter {
979
982
  }
980
983
  }
981
984
 
982
- return this.core.append(buffers, { keyPair, signature, preappend })
985
+ return this.core.append(buffers, { lock, keyPair, signature, preappend })
983
986
  }
984
987
 
985
988
  async treeHash (length) {
package/lib/batch.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const { BLOCK_NOT_AVAILABLE, SESSION_CLOSED } = require('hypercore-errors')
2
2
  const EventEmitter = require('events')
3
3
  const c = require('compact-encoding')
4
+ const b4a = require('b4a')
4
5
 
5
6
  module.exports = class HypercoreBatch extends EventEmitter {
6
7
  constructor (session, autoClose) {
@@ -19,6 +20,7 @@ module.exports = class HypercoreBatch extends EventEmitter {
19
20
  this._byteLength = 0
20
21
  this._sessionLength = 0
21
22
  this._sessionByteLength = 0
23
+ this._sessionBatch = null
22
24
  this._flushing = null
23
25
 
24
26
  this.opening = this.ready().catch(noop)
@@ -65,6 +67,7 @@ module.exports = class HypercoreBatch extends EventEmitter {
65
67
  if (this.opened) return
66
68
  this._sessionLength = this.session.length
67
69
  this._sessionByteLength = this.session.byteLength
70
+ this._sessionBatch = this.session.createTreeBatch()
68
71
  this.fork = this.session.fork
69
72
  this.opened = true
70
73
  this.emit('ready')
@@ -73,6 +76,7 @@ module.exports = class HypercoreBatch extends EventEmitter {
73
76
  async update (opts) {
74
77
  if (this.opened === false) await this.ready()
75
78
  await this.session.update(opts)
79
+ await this._catchup()
76
80
  }
77
81
 
78
82
  setUserData (key, value, opts) {
@@ -149,7 +153,7 @@ module.exports = class HypercoreBatch extends EventEmitter {
149
153
  if (!length && length !== 0) length = this.length + blocks.length
150
154
 
151
155
  const maxLength = this.length + blocks.length
152
- const b = this.session.createTreeBatch()
156
+ const b = this._sessionBatch.clone()
153
157
  const len = Math.min(length, this.length)
154
158
 
155
159
  if (len < this._sessionLength || length > maxLength) return null
@@ -185,6 +189,7 @@ module.exports = class HypercoreBatch extends EventEmitter {
185
189
  await this.session.truncate(newLength, { fork, force: true, ...opts })
186
190
  this._sessionLength = this.session.length
187
191
  this._sessionByteLength = this.session.byteLength
192
+ this._sessionBatch = this.session.createTreeBatch()
188
193
  } else {
189
194
  for (let i = newLength - length; i < this._appends.length; i++) this._byteLength -= this._appends[i].byteLength
190
195
  this._appends.length = newLength - length
@@ -230,34 +235,82 @@ module.exports = class HypercoreBatch extends EventEmitter {
230
235
  if (this.opened === false) await this.opening
231
236
  if (this.closing) throw SESSION_CLOSED()
232
237
 
233
- const { length = this._appends.length, keyPair = this.session.keyPair, signature = null } = opts
238
+ const { length = this.length, keyPair = this.session.keyPair, signature = null } = opts
234
239
 
235
240
  while (this._flushing) await this._flushing
236
241
  this._flushing = this._flush(length, keyPair, signature)
237
242
 
243
+ let flushed = false
244
+
238
245
  try {
239
- await this._flushing
246
+ flushed = await this._flushing
240
247
  } finally {
241
248
  this._flushing = null
242
249
  }
243
250
 
244
251
  if (this.autoClose) await this.close()
252
+
253
+ return flushed
254
+ }
255
+
256
+ async _catchup () {
257
+ if (this.core.tree.length === this._sessionLength) return true
258
+
259
+ const batchLength = this._sessionLength + this._appends.length
260
+
261
+ if (this.core.tree.length > batchLength) return false
262
+
263
+ const b = this.createTreeBatch()
264
+
265
+ for (const root of this.core.tree.roots) {
266
+ const batchRoot = await b.get(root.index)
267
+
268
+ if (batchRoot === null) continue // in the shared tree
269
+ if (batchRoot.size !== root.size || !b4a.equals(batchRoot.hash, root.hash)) {
270
+ // TODO: type this error
271
+ throw new Error('Batch is uncommitable due to a conflict at ' + root.index)
272
+ }
273
+ }
274
+
275
+ const offset = this._sessionLength + this._appends.length - this.core.tree.length
276
+ for (let i = 0; i < offset; i++) this._byteLength -= this._appends[i]
277
+
278
+ this._sessionLength = this.session.length
279
+ this._sessionByteLength = this.session.byteLength
280
+ this._sessionBatch = this.session.createTreeBatch()
281
+ this._appends = this._appends.slice(this._appends.length - offset)
282
+
283
+ return true
245
284
  }
246
285
 
247
286
  async _flush (length, keyPair, signature) { // TODO: make this safe to interact with a parallel truncate...
248
- if (this._appends.length === 0) return
287
+ if (this._appends.length === 0) return true
288
+ if (!(await this._catchup())) return false
289
+
290
+ await this.core._mutex.lock()
291
+
292
+ try {
293
+ const flushingLength = Math.min(length - this._sessionLength, this._appends.length)
294
+ if (flushingLength <= 0) return true
295
+
296
+ const blocks = flushingLength < this._appends.length ? this._appends.slice(0, flushingLength) : this._appends
297
+
298
+ const info = await this.session.append(blocks, { keyPair, signature })
299
+ const delta = info.byteLength - this._sessionByteLength
249
300
 
250
- const flushingLength = Math.min(length, this._appends.length)
251
- const blocks = flushingLength < this._appends.length ? this._appends.slice(0, flushingLength) : this._appends
252
- const info = await this.session.append(blocks, { keyPair, signature })
253
- const delta = info.byteLength - this._sessionByteLength
301
+ this._sessionLength = info.length
302
+ this._sessionByteLength = info.byteLength
303
+ this._sessionBatch = this.session.createTreeBatch()
254
304
 
255
- this._sessionLength = info.length
256
- this._sessionByteLength = info.byteLength
257
- this._appends = this._appends.slice(flushingLength)
258
- this._byteLength -= delta
305
+ this._appends = this._appends.slice(flushingLength)
306
+ this._byteLength -= delta
307
+
308
+ this.emit('flush')
309
+ } finally {
310
+ this.core._mutex.unlock()
311
+ }
259
312
 
260
- this.emit('flush')
313
+ return true
261
314
  }
262
315
 
263
316
  close () {
package/lib/core.js CHANGED
@@ -432,8 +432,8 @@ module.exports = class Core {
432
432
  })
433
433
  }
434
434
 
435
- async append (values, { signature, keyPair = this.header.keyPair, preappend } = {}) {
436
- await this._mutex.lock()
435
+ async append (values, { lock, signature, keyPair = this.header.keyPair, preappend } = {}) {
436
+ if (lock) await this._mutex.lock()
437
437
 
438
438
  // upsert compat manifest
439
439
  if (this.verifier === null && keyPair) this.setManifest(null, keyPair)
@@ -479,7 +479,7 @@ module.exports = class Core {
479
479
 
480
480
  return { length: batch.length, byteLength }
481
481
  } finally {
482
- this._mutex.unlock()
482
+ if (lock) this._mutex.unlock()
483
483
  }
484
484
  }
485
485
 
@@ -92,14 +92,14 @@ class MerkleTreeBatch {
92
92
  return null
93
93
  }
94
94
 
95
- if (index < this.treeLength * 2) {
96
- return this.tree.get(index)
97
- }
98
-
99
95
  for (const n of this.nodes) {
100
96
  if (n.index === index) return n
101
97
  }
102
98
 
99
+ if (index < this.treeLength * 2) {
100
+ return this.tree.get(index)
101
+ }
102
+
103
103
  return null
104
104
  }
105
105
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.24.1",
3
+ "version": "10.24.3",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {