hypercore 10.0.0-alpha.24 → 10.0.0-alpha.27
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/README.md +4 -0
- package/index.js +169 -81
- package/lib/bitfield.js +6 -3
- package/lib/caps.js +34 -0
- package/lib/core.js +26 -10
- package/lib/merkle-tree.js +129 -81
- package/lib/messages.js +246 -177
- package/lib/replicator.js +1334 -639
- 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/README.md
CHANGED
|
@@ -101,6 +101,10 @@ Truncate the core to a smaller length.
|
|
|
101
101
|
Per default this will update the fork id of the core to `+ 1`, but you can set the fork id you prefer with the option.
|
|
102
102
|
Note that the fork id should be monotonely incrementing.
|
|
103
103
|
|
|
104
|
+
#### `const hash = await core.treeHash([length])`
|
|
105
|
+
|
|
106
|
+
Get the Merkle Tree hash of the core at a given length, defaulting to the current length of the core.
|
|
107
|
+
|
|
104
108
|
#### `const stream = core.createReadStream([options])`
|
|
105
109
|
|
|
106
110
|
Make a read stream. Options include:
|
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
|
|
@@ -73,6 +73,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
73
73
|
this.opening.catch(noop)
|
|
74
74
|
|
|
75
75
|
this._preappend = preappend.bind(this)
|
|
76
|
+
this._snapshot = opts.snapshot || null
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
[inspect] (depth, opts) {
|
|
@@ -93,10 +94,17 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
93
94
|
indent + ')'
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
static getProtocolMuxer (stream) {
|
|
98
|
+
return stream.noiseStream.userData
|
|
99
|
+
}
|
|
100
|
+
|
|
96
101
|
static createProtocolStream (isInitiator, opts = {}) {
|
|
97
|
-
let outerStream =
|
|
98
|
-
? isInitiator
|
|
99
|
-
:
|
|
102
|
+
let outerStream = Protomux.isProtomux(isInitiator)
|
|
103
|
+
? isInitiator.stream
|
|
104
|
+
: isStream(isInitiator)
|
|
105
|
+
? isInitiator
|
|
106
|
+
: opts.stream
|
|
107
|
+
|
|
100
108
|
let noiseStream = null
|
|
101
109
|
|
|
102
110
|
if (outerStream) {
|
|
@@ -108,10 +116,16 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
108
116
|
if (!noiseStream) throw new Error('Invalid stream')
|
|
109
117
|
|
|
110
118
|
if (!noiseStream.userData) {
|
|
111
|
-
const protocol =
|
|
112
|
-
|
|
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
|
+
}
|
|
113
128
|
noiseStream.userData = protocol
|
|
114
|
-
noiseStream.on('error', noop) // All noise errors already propagate through outerStream
|
|
115
129
|
}
|
|
116
130
|
|
|
117
131
|
return outerStream
|
|
@@ -129,6 +143,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
129
143
|
}
|
|
130
144
|
}
|
|
131
145
|
|
|
146
|
+
snapshot () {
|
|
147
|
+
return this.session({ snapshot: { length: this.length, byteLength: this.byteLength, fork: this.fork } })
|
|
148
|
+
}
|
|
149
|
+
|
|
132
150
|
session (opts = {}) {
|
|
133
151
|
if (this.closing) {
|
|
134
152
|
// This makes the closing logic alot easier. If this turns out to be a problem
|
|
@@ -154,7 +172,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
154
172
|
if (!this.sign) this.sign = o.sign
|
|
155
173
|
this.crypto = o.crypto
|
|
156
174
|
this.key = o.key
|
|
157
|
-
this.discoveryKey = o.discoveryKey
|
|
158
175
|
this.core = o.core
|
|
159
176
|
this.replicator = o.replicator
|
|
160
177
|
this.encryption = o.encryption
|
|
@@ -232,10 +249,12 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
232
249
|
this.storage = Hypercore.defaultStorage(opts.storage || storage)
|
|
233
250
|
|
|
234
251
|
this.core = await Core.open(this.storage, {
|
|
252
|
+
force: opts.force,
|
|
235
253
|
createIfMissing: opts.createIfMissing,
|
|
236
254
|
overwrite: opts.overwrite,
|
|
237
255
|
keyPair,
|
|
238
256
|
crypto: this.crypto,
|
|
257
|
+
legacy: opts.legacy,
|
|
239
258
|
onupdate: this._oncoreupdate.bind(this)
|
|
240
259
|
})
|
|
241
260
|
|
|
@@ -245,20 +264,19 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
245
264
|
}
|
|
246
265
|
}
|
|
247
266
|
|
|
248
|
-
this.replicator = new Replicator(this.core, {
|
|
249
|
-
onupdate: this._onpeerupdate.bind(this),
|
|
250
|
-
onupload: this._onupload.bind(this)
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
this.discoveryKey = this.crypto.discoveryKey(this.core.header.signer.publicKey)
|
|
254
267
|
this.key = this.core.header.signer.publicKey
|
|
255
268
|
this.keyPair = this.core.header.signer
|
|
256
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
|
+
|
|
257
277
|
if (!this.encryption && opts.encryptionKey) {
|
|
258
278
|
this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
|
|
259
279
|
}
|
|
260
|
-
|
|
261
|
-
this.extensions.attach(this.replicator)
|
|
262
280
|
}
|
|
263
281
|
|
|
264
282
|
close () {
|
|
@@ -277,6 +295,17 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
277
295
|
this.readable = false
|
|
278
296
|
this.writable = false
|
|
279
297
|
this.closed = true
|
|
298
|
+
this.opened = false
|
|
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
|
+
}
|
|
280
309
|
|
|
281
310
|
if (this.sessions.length) {
|
|
282
311
|
// if this is the last session and we are auto closing, trigger that first to enforce error handling
|
|
@@ -297,24 +326,34 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
297
326
|
const protocol = noiseStream.userData
|
|
298
327
|
|
|
299
328
|
if (this.opened) {
|
|
300
|
-
this.replicator.
|
|
329
|
+
this.replicator.attachTo(protocol)
|
|
301
330
|
} else {
|
|
302
|
-
this.opening.then(() => this.replicator.
|
|
331
|
+
this.opening.then(() => this.replicator.attachTo(protocol), protocol.destroy.bind(protocol))
|
|
303
332
|
}
|
|
304
333
|
|
|
305
334
|
return protocolStream
|
|
306
335
|
}
|
|
307
336
|
|
|
337
|
+
get discoveryKey () {
|
|
338
|
+
return this.replicator === null ? null : this.replicator.discoveryKey
|
|
339
|
+
}
|
|
340
|
+
|
|
308
341
|
get length () {
|
|
309
|
-
return this.
|
|
342
|
+
return this._snapshot
|
|
343
|
+
? this._snapshot.length
|
|
344
|
+
: (this.core === null ? 0 : this.core.tree.length)
|
|
310
345
|
}
|
|
311
346
|
|
|
312
347
|
get byteLength () {
|
|
313
|
-
return this.
|
|
348
|
+
return this._snapshot
|
|
349
|
+
? this._snapshot.byteLength
|
|
350
|
+
: (this.core === null ? 0 : this.core.tree.byteLength - (this.core.tree.length * this.padding))
|
|
314
351
|
}
|
|
315
352
|
|
|
316
353
|
get fork () {
|
|
317
|
-
return this.
|
|
354
|
+
return this._snapshot
|
|
355
|
+
? this._snapshot.fork
|
|
356
|
+
: (this.core === null ? 0 : this.core.tree.fork)
|
|
318
357
|
}
|
|
319
358
|
|
|
320
359
|
get peers () {
|
|
@@ -353,13 +392,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
353
392
|
}
|
|
354
393
|
}
|
|
355
394
|
|
|
356
|
-
this.replicator.
|
|
395
|
+
this.replicator.localUpgrade()
|
|
357
396
|
}
|
|
358
397
|
|
|
359
|
-
if (bitfield
|
|
360
|
-
|
|
361
|
-
this.replicator.broadcastBlock(bitfield.start + i)
|
|
362
|
-
}
|
|
398
|
+
if (bitfield) {
|
|
399
|
+
this.replicator.broadcastRange(bitfield.start, bitfield.length, bitfield.drop)
|
|
363
400
|
}
|
|
364
401
|
|
|
365
402
|
if (value) {
|
|
@@ -372,9 +409,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
372
409
|
}
|
|
373
410
|
|
|
374
411
|
_onpeerupdate (added, peer) {
|
|
375
|
-
if (added) this.extensions.update(peer)
|
|
376
412
|
const name = added ? 'peer-add' : 'peer-remove'
|
|
377
413
|
|
|
414
|
+
if (added) {
|
|
415
|
+
for (const ext of this.extensions.values()) {
|
|
416
|
+
peer.extensions.set(ext.name, ext)
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
378
420
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
379
421
|
this.sessions[i].emit(name, peer)
|
|
380
422
|
}
|
|
@@ -393,19 +435,32 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
393
435
|
return null
|
|
394
436
|
}
|
|
395
437
|
|
|
396
|
-
async update () {
|
|
438
|
+
async update (opts) {
|
|
397
439
|
if (this.opened === false) await this.opening
|
|
440
|
+
|
|
398
441
|
// TODO: add an option where a writer can bootstrap it's state from the network also
|
|
399
|
-
if (this.writable) return false
|
|
400
|
-
|
|
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
|
|
401
448
|
}
|
|
402
449
|
|
|
403
|
-
async seek (bytes) {
|
|
450
|
+
async seek (bytes, opts) {
|
|
404
451
|
if (this.opened === false) await this.opening
|
|
405
452
|
|
|
406
453
|
const s = this.core.tree.seek(bytes, this.padding)
|
|
407
454
|
|
|
408
|
-
|
|
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
|
|
409
464
|
}
|
|
410
465
|
|
|
411
466
|
async has (index) {
|
|
@@ -416,6 +471,8 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
416
471
|
|
|
417
472
|
async get (index, opts) {
|
|
418
473
|
if (this.opened === false) await this.opening
|
|
474
|
+
if (this.closing !== null) throw new Error('Session is closed')
|
|
475
|
+
|
|
419
476
|
const c = this.cache && this.cache.get(index)
|
|
420
477
|
if (c) return c
|
|
421
478
|
const fork = this.core.tree.fork
|
|
@@ -434,7 +491,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
434
491
|
} else {
|
|
435
492
|
if (opts && opts.wait === false) return null
|
|
436
493
|
if (opts && opts.onwait) opts.onwait(index)
|
|
437
|
-
|
|
494
|
+
|
|
495
|
+
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
496
|
+
const req = this.replicator.addBlock(activeRequests, index)
|
|
497
|
+
|
|
498
|
+
block = await req.promise
|
|
438
499
|
}
|
|
439
500
|
|
|
440
501
|
if (this.encryption) this.encryption.decrypt(index, block)
|
|
@@ -450,37 +511,27 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
450
511
|
}
|
|
451
512
|
|
|
452
513
|
download (range) {
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
filter = (i) => blocks.has(i)
|
|
468
|
-
} else {
|
|
469
|
-
start = (range && range.start) || 0
|
|
470
|
-
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
|
+
}
|
|
471
528
|
}
|
|
472
|
-
|
|
473
|
-
const r = Replicator.createRange(start, end, filter, linear)
|
|
474
|
-
|
|
475
|
-
if (this.opened) this.replicator.addRange(r)
|
|
476
|
-
else this.opening.then(() => this.replicator.addRange(r), noop)
|
|
477
|
-
|
|
478
|
-
return r
|
|
479
529
|
}
|
|
480
530
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
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)
|
|
484
535
|
}
|
|
485
536
|
|
|
486
537
|
// TODO: get rid of this / deprecate it?
|
|
@@ -488,6 +539,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
488
539
|
range.destroy(null)
|
|
489
540
|
}
|
|
490
541
|
|
|
542
|
+
// TODO: get rid of this / deprecate it?
|
|
543
|
+
cancel (request) {
|
|
544
|
+
// Do nothing for now
|
|
545
|
+
}
|
|
546
|
+
|
|
491
547
|
async truncate (newLength = 0, fork = -1) {
|
|
492
548
|
if (this.opened === false) await this.opening
|
|
493
549
|
if (this.writable === false) throw new Error('Core is not writable')
|
|
@@ -518,13 +574,58 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
518
574
|
return await this.core.append(buffers, this.sign, { preappend })
|
|
519
575
|
}
|
|
520
576
|
|
|
521
|
-
|
|
522
|
-
|
|
577
|
+
async treeHash (length) {
|
|
578
|
+
if (length === undefined) {
|
|
579
|
+
await this.ready()
|
|
580
|
+
length = this.core.length
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const roots = await this.core.tree.getRoots(length)
|
|
584
|
+
return this.crypto.tree(roots)
|
|
523
585
|
}
|
|
524
586
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
+
}
|
|
622
|
+
|
|
623
|
+
this.extensions.set(name, ext)
|
|
624
|
+
for (const peer of this.peers) {
|
|
625
|
+
peer.extensions.set(name, ext)
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return ext
|
|
528
629
|
}
|
|
529
630
|
|
|
530
631
|
_encode (enc, val) {
|
|
@@ -574,19 +675,6 @@ function toHex (buf) {
|
|
|
574
675
|
return buf && b4a.toString(buf, 'hex')
|
|
575
676
|
}
|
|
576
677
|
|
|
577
|
-
function reduce (iter, fn, acc) {
|
|
578
|
-
for (const item of iter) acc = fn(acc, item)
|
|
579
|
-
return acc
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
function min (arr) {
|
|
583
|
-
return reduce(arr, (a, b) => Math.min(a, b), Infinity)
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
function max (arr) {
|
|
587
|
-
return reduce(arr, (a, b) => Math.max(a, b), -Infinity)
|
|
588
|
-
}
|
|
589
|
-
|
|
590
678
|
function preappend (blocks) {
|
|
591
679
|
const offset = this.core.tree.length
|
|
592
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
|
|