hypercore 10.0.0-alpha.25 → 10.0.0-alpha.28
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 -85
- 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 +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/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 =
|
|
54
|
+
this.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 getProtocolMuxer (stream) {
|
|
98
|
+
return stream.noiseStream.userData
|
|
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
|
|
@@ -144,7 +157,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
144
157
|
const Clz = opts.class || Hypercore
|
|
145
158
|
const s = new Clz(this.storage, this.key, {
|
|
146
159
|
...opts,
|
|
147
|
-
extensions: this.extensions,
|
|
148
160
|
_opening: this.opening,
|
|
149
161
|
_sessions: this.sessions
|
|
150
162
|
})
|
|
@@ -159,7 +171,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
159
171
|
if (!this.sign) this.sign = o.sign
|
|
160
172
|
this.crypto = o.crypto
|
|
161
173
|
this.key = o.key
|
|
162
|
-
this.discoveryKey = o.discoveryKey
|
|
163
174
|
this.core = o.core
|
|
164
175
|
this.replicator = o.replicator
|
|
165
176
|
this.encryption = o.encryption
|
|
@@ -170,12 +181,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
170
181
|
async _openFromExisting (from, opts) {
|
|
171
182
|
await from.opening
|
|
172
183
|
|
|
173
|
-
for (const [name, ext] of this.extensions) {
|
|
174
|
-
from.extensions.register(name, null, ext)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
184
|
this._passCapabilities(from)
|
|
178
|
-
this.extensions = from.extensions
|
|
179
185
|
this.sessions = from.sessions
|
|
180
186
|
this.storage = from.storage
|
|
181
187
|
|
|
@@ -237,10 +243,12 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
237
243
|
this.storage = Hypercore.defaultStorage(opts.storage || storage)
|
|
238
244
|
|
|
239
245
|
this.core = await Core.open(this.storage, {
|
|
246
|
+
force: opts.force,
|
|
240
247
|
createIfMissing: opts.createIfMissing,
|
|
241
248
|
overwrite: opts.overwrite,
|
|
242
249
|
keyPair,
|
|
243
250
|
crypto: this.crypto,
|
|
251
|
+
legacy: opts.legacy,
|
|
244
252
|
onupdate: this._oncoreupdate.bind(this)
|
|
245
253
|
})
|
|
246
254
|
|
|
@@ -250,20 +258,19 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
250
258
|
}
|
|
251
259
|
}
|
|
252
260
|
|
|
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
261
|
this.key = this.core.header.signer.publicKey
|
|
260
262
|
this.keyPair = this.core.header.signer
|
|
261
263
|
|
|
264
|
+
this.replicator = new Replicator(this.core, this.key, {
|
|
265
|
+
eagerUpdate: true,
|
|
266
|
+
allowFork: opts.allowFork !== false,
|
|
267
|
+
onpeerupdate: this._onpeerupdate.bind(this),
|
|
268
|
+
onupload: this._onupload.bind(this)
|
|
269
|
+
})
|
|
270
|
+
|
|
262
271
|
if (!this.encryption && opts.encryptionKey) {
|
|
263
272
|
this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
|
|
264
273
|
}
|
|
265
|
-
|
|
266
|
-
this.extensions.attach(this.replicator)
|
|
267
274
|
}
|
|
268
275
|
|
|
269
276
|
close () {
|
|
@@ -284,6 +291,16 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
284
291
|
this.closed = true
|
|
285
292
|
this.opened = false
|
|
286
293
|
|
|
294
|
+
const gc = []
|
|
295
|
+
for (const ext of this.extensions.values()) {
|
|
296
|
+
if (ext.session === this) gc.push(ext)
|
|
297
|
+
}
|
|
298
|
+
for (const ext of gc) ext.destroy()
|
|
299
|
+
|
|
300
|
+
if (this.replicator !== null) {
|
|
301
|
+
this.replicator.clearRequests(this.activeRequests)
|
|
302
|
+
}
|
|
303
|
+
|
|
287
304
|
if (this.sessions.length) {
|
|
288
305
|
// if this is the last session and we are auto closing, trigger that first to enforce error handling
|
|
289
306
|
if (this.sessions.length === 1 && this.autoClose) await this.sessions[0].close()
|
|
@@ -303,14 +320,18 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
303
320
|
const protocol = noiseStream.userData
|
|
304
321
|
|
|
305
322
|
if (this.opened) {
|
|
306
|
-
this.replicator.
|
|
323
|
+
this.replicator.attachTo(protocol)
|
|
307
324
|
} else {
|
|
308
|
-
this.opening.then(() => this.replicator.
|
|
325
|
+
this.opening.then(() => this.replicator.attachTo(protocol), protocol.destroy.bind(protocol))
|
|
309
326
|
}
|
|
310
327
|
|
|
311
328
|
return protocolStream
|
|
312
329
|
}
|
|
313
330
|
|
|
331
|
+
get discoveryKey () {
|
|
332
|
+
return this.replicator === null ? null : this.replicator.discoveryKey
|
|
333
|
+
}
|
|
334
|
+
|
|
314
335
|
get length () {
|
|
315
336
|
return this._snapshot
|
|
316
337
|
? this._snapshot.length
|
|
@@ -365,13 +386,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
365
386
|
}
|
|
366
387
|
}
|
|
367
388
|
|
|
368
|
-
this.replicator.
|
|
389
|
+
this.replicator.localUpgrade()
|
|
369
390
|
}
|
|
370
391
|
|
|
371
|
-
if (bitfield
|
|
372
|
-
|
|
373
|
-
this.replicator.broadcastBlock(bitfield.start + i)
|
|
374
|
-
}
|
|
392
|
+
if (bitfield) {
|
|
393
|
+
this.replicator.broadcastRange(bitfield.start, bitfield.length, bitfield.drop)
|
|
375
394
|
}
|
|
376
395
|
|
|
377
396
|
if (value) {
|
|
@@ -384,11 +403,16 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
384
403
|
}
|
|
385
404
|
|
|
386
405
|
_onpeerupdate (added, peer) {
|
|
387
|
-
if (added) this.extensions.update(peer)
|
|
388
406
|
const name = added ? 'peer-add' : 'peer-remove'
|
|
389
407
|
|
|
390
408
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
391
409
|
this.sessions[i].emit(name, peer)
|
|
410
|
+
|
|
411
|
+
if (added) {
|
|
412
|
+
for (const ext of this.sessions[i].extensions.values()) {
|
|
413
|
+
peer.extensions.set(ext.name, ext)
|
|
414
|
+
}
|
|
415
|
+
}
|
|
392
416
|
}
|
|
393
417
|
}
|
|
394
418
|
|
|
@@ -405,19 +429,32 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
405
429
|
return null
|
|
406
430
|
}
|
|
407
431
|
|
|
408
|
-
async update () {
|
|
432
|
+
async update (opts) {
|
|
409
433
|
if (this.opened === false) await this.opening
|
|
434
|
+
|
|
410
435
|
// TODO: add an option where a writer can bootstrap it's state from the network also
|
|
411
|
-
if (this.writable) return false
|
|
412
|
-
|
|
436
|
+
if (this.writable || this.closing !== null) return false
|
|
437
|
+
|
|
438
|
+
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
439
|
+
const req = this.replicator.addUpgrade(activeRequests)
|
|
440
|
+
|
|
441
|
+
return req.promise
|
|
413
442
|
}
|
|
414
443
|
|
|
415
|
-
async seek (bytes) {
|
|
444
|
+
async seek (bytes, opts) {
|
|
416
445
|
if (this.opened === false) await this.opening
|
|
417
446
|
|
|
418
447
|
const s = this.core.tree.seek(bytes, this.padding)
|
|
419
448
|
|
|
420
|
-
|
|
449
|
+
const offset = await s.update()
|
|
450
|
+
if (offset) return offset
|
|
451
|
+
|
|
452
|
+
if (this.closing !== null) throw new Error('Session is closed')
|
|
453
|
+
|
|
454
|
+
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
455
|
+
const req = this.replicator.addSeek(activeRequests, s)
|
|
456
|
+
|
|
457
|
+
return req.promise
|
|
421
458
|
}
|
|
422
459
|
|
|
423
460
|
async has (index) {
|
|
@@ -428,6 +465,8 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
428
465
|
|
|
429
466
|
async get (index, opts) {
|
|
430
467
|
if (this.opened === false) await this.opening
|
|
468
|
+
if (this.closing !== null) throw new Error('Session is closed')
|
|
469
|
+
|
|
431
470
|
const c = this.cache && this.cache.get(index)
|
|
432
471
|
if (c) return c
|
|
433
472
|
const fork = this.core.tree.fork
|
|
@@ -446,7 +485,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
446
485
|
} else {
|
|
447
486
|
if (opts && opts.wait === false) return null
|
|
448
487
|
if (opts && opts.onwait) opts.onwait(index)
|
|
449
|
-
|
|
488
|
+
|
|
489
|
+
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
490
|
+
const req = this.replicator.addBlock(activeRequests, index)
|
|
491
|
+
|
|
492
|
+
block = await req.promise
|
|
450
493
|
}
|
|
451
494
|
|
|
452
495
|
if (this.encryption) this.encryption.decrypt(index, block)
|
|
@@ -462,37 +505,27 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
462
505
|
}
|
|
463
506
|
|
|
464
507
|
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
|
|
508
|
+
const reqP = this._download(range)
|
|
509
|
+
|
|
510
|
+
// do not crash in the background...
|
|
511
|
+
reqP.catch(noop)
|
|
512
|
+
|
|
513
|
+
// TODO: turn this into an actual object...
|
|
514
|
+
return {
|
|
515
|
+
async downloaded () {
|
|
516
|
+
const req = await reqP
|
|
517
|
+
return req.promise
|
|
518
|
+
},
|
|
519
|
+
destroy () {
|
|
520
|
+
reqP.then(req => req.context && req.context.detach(req), noop)
|
|
521
|
+
}
|
|
483
522
|
}
|
|
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
523
|
}
|
|
492
524
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
525
|
+
async _download (range) {
|
|
526
|
+
if (this.opened === false) await this.opening
|
|
527
|
+
const activeRequests = (range && range.activeRequests) || this.activeRequests
|
|
528
|
+
return this.replicator.addRange(activeRequests, range)
|
|
496
529
|
}
|
|
497
530
|
|
|
498
531
|
// TODO: get rid of this / deprecate it?
|
|
@@ -500,6 +533,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
500
533
|
range.destroy(null)
|
|
501
534
|
}
|
|
502
535
|
|
|
536
|
+
// TODO: get rid of this / deprecate it?
|
|
537
|
+
cancel (request) {
|
|
538
|
+
// Do nothing for now
|
|
539
|
+
}
|
|
540
|
+
|
|
503
541
|
async truncate (newLength = 0, fork = -1) {
|
|
504
542
|
if (this.opened === false) await this.opening
|
|
505
543
|
if (this.writable === false) throw new Error('Core is not writable')
|
|
@@ -540,13 +578,48 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
540
578
|
return this.crypto.tree(roots)
|
|
541
579
|
}
|
|
542
580
|
|
|
543
|
-
registerExtension (name, handlers) {
|
|
544
|
-
|
|
545
|
-
|
|
581
|
+
registerExtension (name, handlers = {}) {
|
|
582
|
+
if (this.extensions.has(name)) {
|
|
583
|
+
const ext = this.extensions.get(name)
|
|
584
|
+
ext.handlers = handlers
|
|
585
|
+
ext.encoding = c.from(codecs(handlers.encoding) || c.buffer)
|
|
586
|
+
ext.session = this
|
|
587
|
+
return ext
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const ext = {
|
|
591
|
+
name,
|
|
592
|
+
handlers,
|
|
593
|
+
encoding: c.from(codecs(handlers.encoding) || c.buffer),
|
|
594
|
+
session: this,
|
|
595
|
+
send (message, peer) {
|
|
596
|
+
const buffer = c.encode(this.encoding, message)
|
|
597
|
+
peer.extension(name, buffer)
|
|
598
|
+
},
|
|
599
|
+
broadcast (message) {
|
|
600
|
+
const buffer = c.encode(this.encoding, message)
|
|
601
|
+
for (const peer of this.session.peers) {
|
|
602
|
+
peer.extension(name, buffer)
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
destroy () {
|
|
606
|
+
for (const peer of this.session.peers) {
|
|
607
|
+
if (peer.extensions.get(name) === ext) peer.extensions.delete(name)
|
|
608
|
+
}
|
|
609
|
+
this.session.extensions.delete(name)
|
|
610
|
+
},
|
|
611
|
+
_onmessage (state, peer) {
|
|
612
|
+
const m = this.encoding.decode(state)
|
|
613
|
+
if (this.handlers.onmessage) this.handlers.onmessage(m, peer)
|
|
614
|
+
}
|
|
615
|
+
}
|
|
546
616
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
617
|
+
this.extensions.set(name, ext)
|
|
618
|
+
for (const peer of this.peers) {
|
|
619
|
+
peer.extensions.set(name, ext)
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return ext
|
|
550
623
|
}
|
|
551
624
|
|
|
552
625
|
_encode (enc, val) {
|
|
@@ -596,19 +669,6 @@ function toHex (buf) {
|
|
|
596
669
|
return buf && b4a.toString(buf, 'hex')
|
|
597
670
|
}
|
|
598
671
|
|
|
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
672
|
function preappend (blocks) {
|
|
613
673
|
const offset = this.core.tree.length
|
|
614
674
|
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
|
|