hypercore 10.24.1 → 10.24.2

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')
@@ -149,7 +152,7 @@ module.exports = class HypercoreBatch extends EventEmitter {
149
152
  if (!length && length !== 0) length = this.length + blocks.length
150
153
 
151
154
  const maxLength = this.length + blocks.length
152
- const b = this.session.createTreeBatch()
155
+ const b = this._sessionBatch.clone()
153
156
  const len = Math.min(length, this.length)
154
157
 
155
158
  if (len < this._sessionLength || length > maxLength) return null
@@ -185,6 +188,7 @@ module.exports = class HypercoreBatch extends EventEmitter {
185
188
  await this.session.truncate(newLength, { fork, force: true, ...opts })
186
189
  this._sessionLength = this.session.length
187
190
  this._sessionByteLength = this.session.byteLength
191
+ this._sessionBatch = this.session.createTreeBatch()
188
192
  } else {
189
193
  for (let i = newLength - length; i < this._appends.length; i++) this._byteLength -= this._appends[i].byteLength
190
194
  this._appends.length = newLength - length
@@ -230,34 +234,83 @@ module.exports = class HypercoreBatch extends EventEmitter {
230
234
  if (this.opened === false) await this.opening
231
235
  if (this.closing) throw SESSION_CLOSED()
232
236
 
233
- const { length = this._appends.length, keyPair = this.session.keyPair, signature = null } = opts
237
+ const { length = this.length, keyPair = this.session.keyPair, signature = null } = opts
234
238
 
235
239
  while (this._flushing) await this._flushing
236
240
  this._flushing = this._flush(length, keyPair, signature)
237
241
 
242
+ let flushed = false
243
+
238
244
  try {
239
- await this._flushing
245
+ flushed = await this._flushing
240
246
  } finally {
241
247
  this._flushing = null
242
248
  }
243
249
 
244
250
  if (this.autoClose) await this.close()
251
+
252
+ return flushed
253
+ }
254
+
255
+ async _catchup () {
256
+ if (this.core.tree.length === this._sessionLength) return true
257
+
258
+ const batchLength = this._sessionLength + this._appends.length
259
+
260
+ if (this.core.tree.length > batchLength) return false
261
+
262
+ const b = this.createTreeBatch()
263
+
264
+ for (const root of this.core.tree.roots) {
265
+ const batchRoot = await b.get(root.index)
266
+
267
+ if (batchRoot === null) continue // in the shared tree
268
+ if (batchRoot.size !== root.size || !b4a.equals(batchRoot.hash, root.hash)) {
269
+ // TODO: type this error
270
+ throw new Error('Batch is uncommitable due to a conflict at ' + root.index)
271
+ }
272
+ }
273
+
274
+ const offset = this._sessionLength + this._appends.length - this.core.tree.length
275
+ for (let i = 0; i < offset; i++) this._byteLength -= this._appends[i]
276
+
277
+ this._sessionLength = this.session.length
278
+ this._sessionByteLength = this.session.byteLength
279
+ this._sessionBatch = this.session.createTreeBatch()
280
+
281
+ if (offset) 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()
249
291
 
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
292
+ try {
293
+ const flushingLength = Math.min(length - this._sessionLength, this._appends.length)
294
+ if (flushingLength <= 0) return true
254
295
 
255
- this._sessionLength = info.length
256
- this._sessionByteLength = info.byteLength
257
- this._appends = this._appends.slice(flushingLength)
258
- this._byteLength -= delta
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
300
+
301
+ this._sessionLength = info.length
302
+ this._sessionByteLength = info.byteLength
303
+ this._sessionBatch = this.session.createTreeBatch()
304
+
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.2",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {