hypercore 10.19.0 → 10.20.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.
Files changed (3) hide show
  1. package/index.js +53 -0
  2. package/lib/core.js +66 -0
  3. package/package.json +2 -2
package/index.js CHANGED
@@ -82,6 +82,7 @@ module.exports = class Hypercore extends EventEmitter {
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
85
86
  this._readonly = opts.writable === false
86
87
 
87
88
  this.closing = null
@@ -306,6 +307,14 @@ module.exports = class Hypercore extends EventEmitter {
306
307
  const s = this.sessions[i]
307
308
  if (s !== this) s._passCapabilities(this)
308
309
  }
310
+
311
+ // copy state over
312
+ if (this._clone) {
313
+ const { from, upgrade } = this._clone
314
+ await from.opening
315
+ await this.core.copyFrom(from.core, upgrade)
316
+ this._clone = null
317
+ }
309
318
  }
310
319
 
311
320
  if (!this.auth) this.auth = this.core.defaultAuth
@@ -458,6 +467,50 @@ module.exports = class Hypercore extends EventEmitter {
458
467
  this.emit('close', true)
459
468
  }
460
469
 
470
+ clone (storage, opts = {}) {
471
+ // TODO: current limitation is no forking
472
+ if ((opts.fork && opts.fork !== 0) || this.fork !== 0) {
473
+ throw BAD_ARGUMENT('Cannot clone a fork')
474
+ }
475
+
476
+ const key = opts.key === undefined ? opts.keyPair ? null : this.key : opts.key
477
+ const keyPair = (opts.auth || opts.keyPair === undefined) ? null : opts.keyPair
478
+
479
+ let auth = this.core.defaultAuth
480
+ if (opts.auth) {
481
+ auth = opts.auth
482
+ } else if (opts.sign && keyPair) {
483
+ auth = Core.createAuth(this.crypto, keyPair, opts)
484
+ } else if (opts.sign) {
485
+ // TODO: dangerous to just update sign?
486
+ auth.sign = opts.sign
487
+ } else if (keyPair && keyPair.secretKey) {
488
+ auth = Core.createAuth(this.crypto, keyPair)
489
+ }
490
+
491
+ const upgrade = opts.upgrade === undefined ? null : opts.upgrade
492
+
493
+ const sparse = opts.sparse === false ? false : this.sparse
494
+ const wait = opts.wait === false ? false : this.wait
495
+ const onwait = opts.onwait === undefined ? this.onwait : opts.onwait
496
+ const timeout = opts.timeout === undefined ? this.timeout : opts.timeout
497
+
498
+ const Clz = this.constructor
499
+ return new Clz(storage, key, {
500
+ ...opts,
501
+ sparse,
502
+ wait,
503
+ onwait,
504
+ timeout,
505
+ auth,
506
+ overwrite: true,
507
+ clone: {
508
+ from: this,
509
+ upgrade
510
+ }
511
+ })
512
+ }
513
+
461
514
  replicate (isInitiator, opts = {}) {
462
515
  // Only limitation here is that ondiscoverykey doesn't work atm when passing a muxer directly,
463
516
  // because it doesn't really make a lot of sense.
package/lib/core.js CHANGED
@@ -13,6 +13,7 @@ module.exports = class Core {
13
13
  constructor (header, crypto, oplog, tree, blocks, bitfield, auth, legacy, onupdate, onconflict) {
14
14
  this.onupdate = onupdate
15
15
  this.onconflict = onconflict
16
+ this.preupdate = null
16
17
  this.header = header
17
18
  this.crypto = crypto
18
19
  this.oplog = oplog
@@ -174,6 +175,70 @@ module.exports = class Core {
174
175
  return false
175
176
  }
176
177
 
178
+ async copyFrom (src, signature, auth = this.defaultAuth) {
179
+ this._mutex.lock()
180
+
181
+ try {
182
+ let pos = src.bitfield.firstSet(0)
183
+
184
+ while (pos >= 0) {
185
+ const segmentStart = pos
186
+ const segmentEnd = src.bitfield.firstUnset(pos)
187
+
188
+ if (segmentStart < 0) break
189
+
190
+ const segment = []
191
+
192
+ while (pos < segmentEnd) {
193
+ const val = await src.blocks.get(pos++)
194
+ segment.push(val)
195
+ }
196
+
197
+ const [offset] = await src.tree.byteRange(2 * segmentStart)
198
+ await this.blocks.putBatch(segmentStart, segment, offset)
199
+
200
+ this.bitfield.setRange(segmentStart, segmentEnd, true)
201
+
202
+ pos = src.bitfield.firstSet(segmentEnd + 1)
203
+ }
204
+
205
+ // TODO: is it an issue to move the nodes directly?
206
+ // TODO: make flat iterator that computes needed nodes
207
+
208
+ for (let i = 0; i <= src.tree.length * 2; i++) {
209
+ const node = await src.tree.get(i, false)
210
+ if (node === null) continue
211
+
212
+ await this.tree.addNode(node)
213
+ }
214
+
215
+ await this.tree.flush()
216
+
217
+ this.tree.fork = src.tree.fork
218
+ this.tree.roots = [...src.tree.roots]
219
+ this.tree.length = src.tree.length
220
+ this.tree.byteLength = src.tree.byteLength
221
+ this.tree.signature = null // must provide signature
222
+
223
+ this.tree.signature = signature || auth.sign(this.tree.signable())
224
+
225
+ if (signature && !this._signed(this.tree)) {
226
+ // TODO: how to handle signature failure?
227
+ this.tree.signature = null
228
+ throw INVALID_SIGNATURE('Clone was provided with an invalid signature')
229
+ }
230
+
231
+ this.header.tree.length = this.tree.length
232
+ this.header.tree.rootHash = this.tree.hash()
233
+ this.header.tree.signature = this.tree.signature
234
+ this.header.userData = src.header.userData // should copy?
235
+
236
+ await this._flushOplog()
237
+ } finally {
238
+ this._mutex.unlock()
239
+ }
240
+ }
241
+
177
242
  async _flushOplog () {
178
243
  // TODO: the apis using this, actually do not need to wait for the bitfields, tree etc to flush
179
244
  // as their mutations are already stored in the oplog. We could potentially just run this in the
@@ -381,6 +446,7 @@ module.exports = class Core {
381
446
  bitfield
382
447
  }
383
448
 
449
+ if (this.preupdate !== null) await this.preupdate(batch, this.header.signer.publicKey)
384
450
  if (bitfield) await this._writeBlock(batch, bitfield.start, value)
385
451
 
386
452
  await this.oplog.append([entry], false)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.19.0",
3
+ "version": "10.20.0",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -64,8 +64,8 @@
64
64
  "range-parser": "^1.2.1",
65
65
  "speedometer": "^1.1.0",
66
66
  "standard": "^17.0.0",
67
+ "test-tmp": "^1.0.2",
67
68
  "tiny-byte-size": "^1.1.0",
68
- "tmp-promise": "^3.0.2",
69
69
  "udx-native": "^1.6.1"
70
70
  }
71
71
  }