hypercore 10.0.0-alpha.25 → 10.0.0-alpha.26
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 +145 -79
- package/lib/bitfield.js +6 -3
- package/lib/caps.js +34 -0
- package/lib/core.js +26 -10
- package/lib/merkle-tree.js +114 -81
- package/lib/messages.js +246 -177
- package/lib/replicator.js +1253 -592
- package/package.json +5 -3
- package/lib/extensions.js +0 -76
- package/lib/protocol.js +0 -606
- package/lib/random-iterator.js +0 -46
package/index.js
CHANGED
|
@@ -6,12 +6,12 @@ const c = require('compact-encoding')
|
|
|
6
6
|
const b4a = require('b4a')
|
|
7
7
|
const Xache = require('xache')
|
|
8
8
|
const NoiseSecretStream = require('@hyperswarm/secret-stream')
|
|
9
|
+
const Protomux = require('protomux')
|
|
9
10
|
const codecs = require('codecs')
|
|
10
11
|
|
|
11
12
|
const fsctl = requireMaybe('fsctl') || { lock: noop, sparse: noop }
|
|
12
13
|
|
|
13
14
|
const Replicator = require('./lib/replicator')
|
|
14
|
-
const Extensions = require('./lib/extensions')
|
|
15
15
|
const Core = require('./lib/core')
|
|
16
16
|
const BlockEncryption = require('./lib/block-encryption')
|
|
17
17
|
const { ReadStream, WriteStream } = require('./lib/streams')
|
|
@@ -51,15 +51,15 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
51
51
|
this.core = null
|
|
52
52
|
this.replicator = null
|
|
53
53
|
this.encryption = null
|
|
54
|
-
this.extensions = opts.extensions || new
|
|
54
|
+
this.extensions = opts.extensions || new Map()
|
|
55
55
|
this.cache = opts.cache === true ? new Xache({ maxSize: 65536, maxAge: 0 }) : (opts.cache || null)
|
|
56
56
|
|
|
57
57
|
this.valueEncoding = null
|
|
58
58
|
this.encodeBatch = null
|
|
59
|
+
this.activeRequests = []
|
|
59
60
|
|
|
60
61
|
this.key = key || null
|
|
61
62
|
this.keyPair = null
|
|
62
|
-
this.discoveryKey = null
|
|
63
63
|
this.readable = true
|
|
64
64
|
this.writable = false
|
|
65
65
|
this.opened = false
|
|
@@ -94,10 +94,17 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
94
94
|
indent + ')'
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
static protomux (stream, opts) {
|
|
98
|
+
return stream.noiseStream.userData.open(opts)
|
|
99
|
+
}
|
|
100
|
+
|
|
97
101
|
static createProtocolStream (isInitiator, opts = {}) {
|
|
98
|
-
let outerStream =
|
|
99
|
-
? isInitiator
|
|
100
|
-
:
|
|
102
|
+
let outerStream = Protomux.isProtomux(isInitiator)
|
|
103
|
+
? isInitiator.stream
|
|
104
|
+
: isStream(isInitiator)
|
|
105
|
+
? isInitiator
|
|
106
|
+
: opts.stream
|
|
107
|
+
|
|
101
108
|
let noiseStream = null
|
|
102
109
|
|
|
103
110
|
if (outerStream) {
|
|
@@ -109,10 +116,16 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
109
116
|
if (!noiseStream) throw new Error('Invalid stream')
|
|
110
117
|
|
|
111
118
|
if (!noiseStream.userData) {
|
|
112
|
-
const protocol =
|
|
113
|
-
|
|
119
|
+
const protocol = new Protomux(noiseStream)
|
|
120
|
+
|
|
121
|
+
if (opts.ondiscoverykey) {
|
|
122
|
+
protocol.pair({ protocol: 'hypercore/alpha' }, opts.ondiscoverykey)
|
|
123
|
+
}
|
|
124
|
+
if (opts.keepAlive !== false) {
|
|
125
|
+
noiseStream.setKeepAlive(5000)
|
|
126
|
+
noiseStream.setTimeout(7000)
|
|
127
|
+
}
|
|
114
128
|
noiseStream.userData = protocol
|
|
115
|
-
noiseStream.on('error', noop) // All noise errors already propagate through outerStream
|
|
116
129
|
}
|
|
117
130
|
|
|
118
131
|
return outerStream
|
|
@@ -159,7 +172,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
159
172
|
if (!this.sign) this.sign = o.sign
|
|
160
173
|
this.crypto = o.crypto
|
|
161
174
|
this.key = o.key
|
|
162
|
-
this.discoveryKey = o.discoveryKey
|
|
163
175
|
this.core = o.core
|
|
164
176
|
this.replicator = o.replicator
|
|
165
177
|
this.encryption = o.encryption
|
|
@@ -237,10 +249,12 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
237
249
|
this.storage = Hypercore.defaultStorage(opts.storage || storage)
|
|
238
250
|
|
|
239
251
|
this.core = await Core.open(this.storage, {
|
|
252
|
+
force: opts.force,
|
|
240
253
|
createIfMissing: opts.createIfMissing,
|
|
241
254
|
overwrite: opts.overwrite,
|
|
242
255
|
keyPair,
|
|
243
256
|
crypto: this.crypto,
|
|
257
|
+
legacy: opts.legacy,
|
|
244
258
|
onupdate: this._oncoreupdate.bind(this)
|
|
245
259
|
})
|
|
246
260
|
|
|
@@ -250,20 +264,19 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
250
264
|
}
|
|
251
265
|
}
|
|
252
266
|
|
|
253
|
-
this.replicator = new Replicator(this.core, {
|
|
254
|
-
onupdate: this._onpeerupdate.bind(this),
|
|
255
|
-
onupload: this._onupload.bind(this)
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
this.discoveryKey = this.crypto.discoveryKey(this.core.header.signer.publicKey)
|
|
259
267
|
this.key = this.core.header.signer.publicKey
|
|
260
268
|
this.keyPair = this.core.header.signer
|
|
261
269
|
|
|
270
|
+
this.replicator = new Replicator(this.core, this.key, {
|
|
271
|
+
eagerUpdate: true,
|
|
272
|
+
allowFork: opts.allowFork !== false,
|
|
273
|
+
onpeerupdate: this._onpeerupdate.bind(this),
|
|
274
|
+
onupload: this._onupload.bind(this)
|
|
275
|
+
})
|
|
276
|
+
|
|
262
277
|
if (!this.encryption && opts.encryptionKey) {
|
|
263
278
|
this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
|
|
264
279
|
}
|
|
265
|
-
|
|
266
|
-
this.extensions.attach(this.replicator)
|
|
267
280
|
}
|
|
268
281
|
|
|
269
282
|
close () {
|
|
@@ -284,6 +297,16 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
284
297
|
this.closed = true
|
|
285
298
|
this.opened = false
|
|
286
299
|
|
|
300
|
+
const gc = []
|
|
301
|
+
for (const ext of this.extensions.values()) {
|
|
302
|
+
if (ext.session === this) gc.push(ext)
|
|
303
|
+
}
|
|
304
|
+
for (const ext of gc) ext.destroy()
|
|
305
|
+
|
|
306
|
+
if (this.replicator !== null) {
|
|
307
|
+
this.replicator.clearRequests(this.activeRequests)
|
|
308
|
+
}
|
|
309
|
+
|
|
287
310
|
if (this.sessions.length) {
|
|
288
311
|
// if this is the last session and we are auto closing, trigger that first to enforce error handling
|
|
289
312
|
if (this.sessions.length === 1 && this.autoClose) await this.sessions[0].close()
|
|
@@ -303,14 +326,18 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
303
326
|
const protocol = noiseStream.userData
|
|
304
327
|
|
|
305
328
|
if (this.opened) {
|
|
306
|
-
this.replicator.
|
|
329
|
+
this.replicator.attachTo(protocol)
|
|
307
330
|
} else {
|
|
308
|
-
this.opening.then(() => this.replicator.
|
|
331
|
+
this.opening.then(() => this.replicator.attachTo(protocol), protocol.destroy.bind(protocol))
|
|
309
332
|
}
|
|
310
333
|
|
|
311
334
|
return protocolStream
|
|
312
335
|
}
|
|
313
336
|
|
|
337
|
+
get discoveryKey () {
|
|
338
|
+
return this.replicator === null ? null : this.replicator.discoveryKey
|
|
339
|
+
}
|
|
340
|
+
|
|
314
341
|
get length () {
|
|
315
342
|
return this._snapshot
|
|
316
343
|
? this._snapshot.length
|
|
@@ -365,13 +392,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
365
392
|
}
|
|
366
393
|
}
|
|
367
394
|
|
|
368
|
-
this.replicator.
|
|
395
|
+
this.replicator.signalUpgrade()
|
|
369
396
|
}
|
|
370
397
|
|
|
371
|
-
if (bitfield
|
|
372
|
-
|
|
373
|
-
this.replicator.broadcastBlock(bitfield.start + i)
|
|
374
|
-
}
|
|
398
|
+
if (bitfield) {
|
|
399
|
+
this.replicator.broadcastRange(bitfield.start, bitfield.length, bitfield.drop)
|
|
375
400
|
}
|
|
376
401
|
|
|
377
402
|
if (value) {
|
|
@@ -384,9 +409,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
384
409
|
}
|
|
385
410
|
|
|
386
411
|
_onpeerupdate (added, peer) {
|
|
387
|
-
if (added) this.extensions.update(peer)
|
|
388
412
|
const name = added ? 'peer-add' : 'peer-remove'
|
|
389
413
|
|
|
414
|
+
if (added) {
|
|
415
|
+
for (const ext of this.extensions.values()) {
|
|
416
|
+
peer.extensions.set(ext.name, ext)
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
390
420
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
391
421
|
this.sessions[i].emit(name, peer)
|
|
392
422
|
}
|
|
@@ -405,19 +435,32 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
405
435
|
return null
|
|
406
436
|
}
|
|
407
437
|
|
|
408
|
-
async update () {
|
|
438
|
+
async update (opts) {
|
|
409
439
|
if (this.opened === false) await this.opening
|
|
440
|
+
|
|
410
441
|
// TODO: add an option where a writer can bootstrap it's state from the network also
|
|
411
|
-
if (this.writable) return false
|
|
412
|
-
|
|
442
|
+
if (this.writable || this.closing !== null) return false
|
|
443
|
+
|
|
444
|
+
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
445
|
+
const req = this.replicator.addUpgrade(activeRequests)
|
|
446
|
+
|
|
447
|
+
return req.promise
|
|
413
448
|
}
|
|
414
449
|
|
|
415
|
-
async seek (bytes) {
|
|
450
|
+
async seek (bytes, opts) {
|
|
416
451
|
if (this.opened === false) await this.opening
|
|
417
452
|
|
|
418
453
|
const s = this.core.tree.seek(bytes, this.padding)
|
|
419
454
|
|
|
420
|
-
|
|
455
|
+
const offset = await s.update()
|
|
456
|
+
if (offset) return offset
|
|
457
|
+
|
|
458
|
+
if (this.closing !== null) throw new Error('Session is closed')
|
|
459
|
+
|
|
460
|
+
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
461
|
+
const req = this.replicator.addSeek(activeRequests, s)
|
|
462
|
+
|
|
463
|
+
return req.promise
|
|
421
464
|
}
|
|
422
465
|
|
|
423
466
|
async has (index) {
|
|
@@ -428,6 +471,8 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
428
471
|
|
|
429
472
|
async get (index, opts) {
|
|
430
473
|
if (this.opened === false) await this.opening
|
|
474
|
+
if (this.closing !== null) throw new Error('Session is closed')
|
|
475
|
+
|
|
431
476
|
const c = this.cache && this.cache.get(index)
|
|
432
477
|
if (c) return c
|
|
433
478
|
const fork = this.core.tree.fork
|
|
@@ -446,7 +491,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
446
491
|
} else {
|
|
447
492
|
if (opts && opts.wait === false) return null
|
|
448
493
|
if (opts && opts.onwait) opts.onwait(index)
|
|
449
|
-
|
|
494
|
+
|
|
495
|
+
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
496
|
+
const req = this.replicator.addBlock(activeRequests, index)
|
|
497
|
+
|
|
498
|
+
block = await req.promise
|
|
450
499
|
}
|
|
451
500
|
|
|
452
501
|
if (this.encryption) this.encryption.decrypt(index, block)
|
|
@@ -462,37 +511,27 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
462
511
|
}
|
|
463
512
|
|
|
464
513
|
download (range) {
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
filter = (i) => blocks.has(i)
|
|
480
|
-
} else {
|
|
481
|
-
start = (range && range.start) || 0
|
|
482
|
-
end = typeof (range && range.end) === 'number' ? range.end : -1 // download all
|
|
514
|
+
const reqP = this._download(range)
|
|
515
|
+
|
|
516
|
+
// do not crash in the background...
|
|
517
|
+
reqP.catch(noop)
|
|
518
|
+
|
|
519
|
+
// TODO: turn this into an actual object...
|
|
520
|
+
return {
|
|
521
|
+
async downloaded () {
|
|
522
|
+
const req = await reqP
|
|
523
|
+
return req.promise
|
|
524
|
+
},
|
|
525
|
+
destroy () {
|
|
526
|
+
reqP.then(req => req.context && req.context.detach(req), noop)
|
|
527
|
+
}
|
|
483
528
|
}
|
|
484
|
-
|
|
485
|
-
const r = Replicator.createRange(start, end, filter, linear)
|
|
486
|
-
|
|
487
|
-
if (this.opened) this.replicator.addRange(r)
|
|
488
|
-
else this.opening.then(() => this.replicator.addRange(r), noop)
|
|
489
|
-
|
|
490
|
-
return r
|
|
491
529
|
}
|
|
492
530
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
531
|
+
async _download (range) {
|
|
532
|
+
if (this.opened === false) await this.opening
|
|
533
|
+
const activeRequests = (range && range.activeRequests) || this.activeRequests
|
|
534
|
+
return this.replicator.addRange(activeRequests, range)
|
|
496
535
|
}
|
|
497
536
|
|
|
498
537
|
// TODO: get rid of this / deprecate it?
|
|
@@ -500,6 +539,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
500
539
|
range.destroy(null)
|
|
501
540
|
}
|
|
502
541
|
|
|
542
|
+
// TODO: get rid of this / deprecate it?
|
|
543
|
+
cancel (request) {
|
|
544
|
+
// Do nothing for now
|
|
545
|
+
}
|
|
546
|
+
|
|
503
547
|
async truncate (newLength = 0, fork = -1) {
|
|
504
548
|
if (this.opened === false) await this.opening
|
|
505
549
|
if (this.writable === false) throw new Error('Core is not writable')
|
|
@@ -540,13 +584,48 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
540
584
|
return this.crypto.tree(roots)
|
|
541
585
|
}
|
|
542
586
|
|
|
543
|
-
registerExtension (name, handlers) {
|
|
544
|
-
|
|
545
|
-
|
|
587
|
+
registerExtension (name, handlers = {}) {
|
|
588
|
+
if (this.extensions.has(name)) {
|
|
589
|
+
const ext = this.extensions.get(name)
|
|
590
|
+
ext.handlers = handlers
|
|
591
|
+
ext.encoding = c.from(codecs(handlers.encoding) || c.buffer)
|
|
592
|
+
ext.session = this
|
|
593
|
+
return ext
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const ext = {
|
|
597
|
+
name,
|
|
598
|
+
handlers,
|
|
599
|
+
encoding: c.from(codecs(handlers.encoding) || c.buffer),
|
|
600
|
+
session: this,
|
|
601
|
+
send (message, peer) {
|
|
602
|
+
const buffer = c.encode(this.encoding, message)
|
|
603
|
+
peer.extension(name, buffer)
|
|
604
|
+
},
|
|
605
|
+
broadcast (message) {
|
|
606
|
+
const buffer = c.encode(this.encoding, message)
|
|
607
|
+
for (const peer of this.session.peers) {
|
|
608
|
+
peer.extension(name, buffer)
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
destroy () {
|
|
612
|
+
for (const peer of this.session.peers) {
|
|
613
|
+
peer.extensions.delete(name)
|
|
614
|
+
}
|
|
615
|
+
this.session.extensions.delete(name)
|
|
616
|
+
},
|
|
617
|
+
_onmessage (state, peer) {
|
|
618
|
+
const m = this.encoding.decode(state)
|
|
619
|
+
if (this.handlers.onmessage) this.handlers.onmessage(m, peer)
|
|
620
|
+
}
|
|
621
|
+
}
|
|
546
622
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
623
|
+
this.extensions.set(name, ext)
|
|
624
|
+
for (const peer of this.peers) {
|
|
625
|
+
peer.extensions.set(name, ext)
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return ext
|
|
550
629
|
}
|
|
551
630
|
|
|
552
631
|
_encode (enc, val) {
|
|
@@ -596,19 +675,6 @@ function toHex (buf) {
|
|
|
596
675
|
return buf && b4a.toString(buf, 'hex')
|
|
597
676
|
}
|
|
598
677
|
|
|
599
|
-
function reduce (iter, fn, acc) {
|
|
600
|
-
for (const item of iter) acc = fn(acc, item)
|
|
601
|
-
return acc
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
function min (arr) {
|
|
605
|
-
return reduce(arr, (a, b) => Math.min(a, b), Infinity)
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
function max (arr) {
|
|
609
|
-
return reduce(arr, (a, b) => Math.max(a, b), -Infinity)
|
|
610
|
-
}
|
|
611
|
-
|
|
612
678
|
function preappend (blocks) {
|
|
613
679
|
const offset = this.core.tree.length
|
|
614
680
|
const fork = this.core.tree.fork
|
package/lib/bitfield.js
CHANGED
|
@@ -41,8 +41,9 @@ module.exports = class Bitfield {
|
|
|
41
41
|
this.pages = new BigSparseArray()
|
|
42
42
|
this.unflushed = []
|
|
43
43
|
this.storage = storage
|
|
44
|
+
this.resumed = !!(buf && buf.byteLength >= 4)
|
|
44
45
|
|
|
45
|
-
const all =
|
|
46
|
+
const all = this.resumed
|
|
46
47
|
? new Uint32Array(buf.buffer, buf.byteOffset, Math.floor(buf.byteLength / 4))
|
|
47
48
|
: new Uint32Array(1024)
|
|
48
49
|
|
|
@@ -93,8 +94,10 @@ module.exports = class Bitfield {
|
|
|
93
94
|
clear () {
|
|
94
95
|
return new Promise((resolve, reject) => {
|
|
95
96
|
this.storage.del(0, Infinity, (err) => {
|
|
96
|
-
if (err) reject(err)
|
|
97
|
-
|
|
97
|
+
if (err) return reject(err)
|
|
98
|
+
this.pages = new BigSparseArray()
|
|
99
|
+
this.unflushed = []
|
|
100
|
+
resolve()
|
|
98
101
|
})
|
|
99
102
|
})
|
|
100
103
|
}
|
package/lib/caps.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const crypto = require('hypercore-crypto')
|
|
2
|
+
const sodium = require('sodium-universal')
|
|
3
|
+
const b4a = require('b4a')
|
|
4
|
+
const c = require('compact-encoding')
|
|
5
|
+
|
|
6
|
+
// TODO: rename this to "crypto" and move everything hashing related etc in here
|
|
7
|
+
// Also lets move the tree stuff from hypercore-crypto here, and loose the types
|
|
8
|
+
// from the hashes there - they are not needed since we lock the indexes in the tree
|
|
9
|
+
// hash and just makes alignment etc harder in other languages
|
|
10
|
+
|
|
11
|
+
const [TREE, REPLICATE_INITIATOR, REPLICATE_RESPONDER] = crypto.namespace('hypercore', 3)
|
|
12
|
+
|
|
13
|
+
exports.replicate = function (isInitiator, key, handshakeHash) {
|
|
14
|
+
const out = b4a.allocUnsafe(32)
|
|
15
|
+
sodium.crypto_generichash_batch(out, [isInitiator ? REPLICATE_INITIATOR : REPLICATE_RESPONDER, key], handshakeHash)
|
|
16
|
+
return out
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
exports.treeSignable = function (hash, length, fork) {
|
|
20
|
+
const state = { start: 0, end: 80, buffer: b4a.allocUnsafe(80) }
|
|
21
|
+
c.raw.encode(state, TREE)
|
|
22
|
+
c.raw.encode(state, hash)
|
|
23
|
+
c.uint64.encode(state, length)
|
|
24
|
+
c.uint64.encode(state, fork)
|
|
25
|
+
return state.buffer
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
exports.treeSignableLegacy = function (hash, length, fork) {
|
|
29
|
+
const state = { start: 0, end: 48, buffer: b4a.allocUnsafe(48) }
|
|
30
|
+
c.raw.encode(state, hash)
|
|
31
|
+
c.uint64.encode(state, length)
|
|
32
|
+
c.uint64.encode(state, fork)
|
|
33
|
+
return state.buffer
|
|
34
|
+
}
|
package/lib/core.js
CHANGED
|
@@ -5,10 +5,10 @@ const Mutex = require('./mutex')
|
|
|
5
5
|
const MerkleTree = require('./merkle-tree')
|
|
6
6
|
const BlockStore = require('./block-store')
|
|
7
7
|
const Bitfield = require('./bitfield')
|
|
8
|
-
const
|
|
8
|
+
const m = require('./messages')
|
|
9
9
|
|
|
10
10
|
module.exports = class Core {
|
|
11
|
-
constructor (header, crypto, oplog, tree, blocks, bitfield, sign, onupdate) {
|
|
11
|
+
constructor (header, crypto, oplog, tree, blocks, bitfield, sign, legacy, onupdate) {
|
|
12
12
|
this.onupdate = onupdate
|
|
13
13
|
this.header = header
|
|
14
14
|
this.crypto = crypto
|
|
@@ -24,6 +24,7 @@ module.exports = class Core {
|
|
|
24
24
|
this._verifies = null
|
|
25
25
|
this._verifiesFlushed = null
|
|
26
26
|
this._mutex = new Mutex()
|
|
27
|
+
this._legacy = legacy
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
static async open (storage, opts = {}) {
|
|
@@ -57,18 +58,24 @@ module.exports = class Core {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
static async resume (oplogFile, treeFile, bitfieldFile, dataFile, opts) {
|
|
60
|
-
|
|
61
|
+
let overwrite = opts.overwrite === true
|
|
62
|
+
|
|
63
|
+
const force = opts.force === true
|
|
61
64
|
const createIfMissing = opts.createIfMissing !== false
|
|
62
65
|
const crypto = opts.crypto || hypercoreCrypto
|
|
63
66
|
|
|
64
67
|
const oplog = new Oplog(oplogFile, {
|
|
65
|
-
headerEncoding:
|
|
66
|
-
entryEncoding:
|
|
68
|
+
headerEncoding: m.oplog.header,
|
|
69
|
+
entryEncoding: m.oplog.entry
|
|
67
70
|
})
|
|
68
71
|
|
|
69
72
|
let { header, entries } = await oplog.open()
|
|
70
73
|
|
|
71
|
-
if (
|
|
74
|
+
if (force && opts.keyPair && header && header.signer && !b4a.equals(header.signer.publicKey, opts.keyPair.publicKey)) {
|
|
75
|
+
overwrite = true
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!header || overwrite) {
|
|
72
79
|
if (!createIfMissing) {
|
|
73
80
|
throw new Error('No hypercore is stored here')
|
|
74
81
|
}
|
|
@@ -103,6 +110,10 @@ module.exports = class Core {
|
|
|
103
110
|
await tree.clear()
|
|
104
111
|
await blocks.clear()
|
|
105
112
|
await bitfield.clear()
|
|
113
|
+
entries = []
|
|
114
|
+
} else if (bitfield.resumed && header.tree.length === 0) {
|
|
115
|
+
// If this was an old bitfield, reset it since it loads based on disk size atm (TODO: change that)
|
|
116
|
+
await bitfield.clear()
|
|
106
117
|
}
|
|
107
118
|
|
|
108
119
|
const sign = opts.sign || (header.signer.secretKey ? this.createSigner(crypto, header.signer) : null)
|
|
@@ -119,7 +130,7 @@ module.exports = class Core {
|
|
|
119
130
|
}
|
|
120
131
|
|
|
121
132
|
if (e.bitfield) {
|
|
122
|
-
bitfield.setRange(e.bitfield.start, e.bitfield.length)
|
|
133
|
+
bitfield.setRange(e.bitfield.start, e.bitfield.length, !e.bitfield.drop)
|
|
123
134
|
}
|
|
124
135
|
|
|
125
136
|
if (e.treeUpgrade) {
|
|
@@ -136,7 +147,7 @@ module.exports = class Core {
|
|
|
136
147
|
}
|
|
137
148
|
}
|
|
138
149
|
|
|
139
|
-
return new this(header, crypto, oplog, tree, blocks, bitfield, sign, opts.onupdate || noop)
|
|
150
|
+
return new this(header, crypto, oplog, tree, blocks, bitfield, sign, !!opts.legacy, opts.onupdate || noop)
|
|
140
151
|
}
|
|
141
152
|
|
|
142
153
|
_shouldFlush () {
|
|
@@ -227,7 +238,7 @@ module.exports = class Core {
|
|
|
227
238
|
for (const val of values) batch.append(val)
|
|
228
239
|
|
|
229
240
|
const hash = batch.hash()
|
|
230
|
-
batch.signature = await sign(batch.signable(hash))
|
|
241
|
+
batch.signature = await sign(this._legacy ? batch.signableLegacy(hash) : batch.signable(hash))
|
|
231
242
|
|
|
232
243
|
const entry = {
|
|
233
244
|
userData: null,
|
|
@@ -259,10 +270,15 @@ module.exports = class Core {
|
|
|
259
270
|
}
|
|
260
271
|
}
|
|
261
272
|
|
|
273
|
+
_signed (batch, hash) {
|
|
274
|
+
const signable = this._legacy ? batch.signableLegacy(hash) : batch.signable(hash)
|
|
275
|
+
return this.crypto.verify(signable, batch.signature, this.header.signer.publicKey)
|
|
276
|
+
}
|
|
277
|
+
|
|
262
278
|
async _verifyExclusive ({ batch, bitfield, value, from }) {
|
|
263
279
|
// TODO: move this to tree.js
|
|
264
280
|
const hash = batch.hash()
|
|
265
|
-
if (!batch.signature || !this.
|
|
281
|
+
if (!batch.signature || !this._signed(batch, hash)) {
|
|
266
282
|
throw new Error('Remote signature does not match')
|
|
267
283
|
}
|
|
268
284
|
|