hypercore 11.20.2 → 11.21.1
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 +0 -1
- package/index.js +5 -3
- package/lib/core.js +9 -5
- package/lib/merkle-tree.js +48 -0
- package/lib/messages.js +4 -2
- package/lib/replicator.js +196 -46
- package/lib/session-state.js +15 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -762,7 +762,6 @@ Create an encrypted noise stream with a protomux instance attached used for Hype
|
|
|
762
762
|
|
|
763
763
|
```
|
|
764
764
|
{
|
|
765
|
-
keepAlive: true, // Whether to keep the stream alive
|
|
766
765
|
ondiscoverykey: () => {}, // A handler for when a discovery key is set over the stream for corestore management
|
|
767
766
|
}
|
|
768
767
|
```
|
package/index.js
CHANGED
|
@@ -146,7 +146,7 @@ class Hypercore extends EventEmitter {
|
|
|
146
146
|
if (!noiseStream.userData) {
|
|
147
147
|
const protocol = Protomux.from(noiseStream)
|
|
148
148
|
|
|
149
|
-
if (opts.keepAlive !== false) {
|
|
149
|
+
if (opts.keepAlive !== false && noiseStream.keepAlive === 0) {
|
|
150
150
|
noiseStream.setKeepAlive(5000)
|
|
151
151
|
}
|
|
152
152
|
noiseStream.userData = protocol
|
|
@@ -938,7 +938,7 @@ class Hypercore extends EventEmitter {
|
|
|
938
938
|
const isDefault = this.state === this.core.state
|
|
939
939
|
const defaultKeyPair = this.state.name === null ? this.keyPair : null
|
|
940
940
|
|
|
941
|
-
const { keyPair = defaultKeyPair, signature = null, maxLength } = opts
|
|
941
|
+
const { keyPair = defaultKeyPair, signature = null, maxLength, postappend = null } = opts
|
|
942
942
|
const writable =
|
|
943
943
|
!isDefault || !!signature || !!(keyPair && keyPair.secretKey) || opts.writable === true
|
|
944
944
|
|
|
@@ -966,7 +966,7 @@ class Hypercore extends EventEmitter {
|
|
|
966
966
|
}
|
|
967
967
|
}
|
|
968
968
|
|
|
969
|
-
return this.state.append(buffers, { keyPair, signature, preappend, maxLength })
|
|
969
|
+
return this.state.append(buffers, { keyPair, signature, preappend, postappend, maxLength })
|
|
970
970
|
}
|
|
971
971
|
|
|
972
972
|
async signable(length = -1, fork = -1) {
|
|
@@ -1149,6 +1149,8 @@ function initOnce(session, storage, key, opts) {
|
|
|
1149
1149
|
eagerUpgrade: opts.eagerUpgrade !== false,
|
|
1150
1150
|
notDownloadingLinger: opts.notDownloadingLinger,
|
|
1151
1151
|
allowFork: opts.allowFork !== false,
|
|
1152
|
+
allowPush: !!opts.allowPush,
|
|
1153
|
+
alwaysLatestBlock: !!opts.allowLatestBlock,
|
|
1152
1154
|
inflightRange: opts.inflightRange,
|
|
1153
1155
|
compat: opts.compat === true,
|
|
1154
1156
|
force: opts.force,
|
package/lib/core.js
CHANGED
|
@@ -441,6 +441,7 @@ module.exports = class Core {
|
|
|
441
441
|
const verifier =
|
|
442
442
|
this.verifier ||
|
|
443
443
|
new Verifier(this.header.key, Verifier.createManifest(manifest), { legacy: this._legacy })
|
|
444
|
+
|
|
444
445
|
if (!verifier.verify(batch, batch.signature)) {
|
|
445
446
|
throw INVALID_SIGNATURE('Proof contains an invalid signature', this.discoveryKey)
|
|
446
447
|
}
|
|
@@ -451,10 +452,6 @@ module.exports = class Core {
|
|
|
451
452
|
async _verifyExclusive({ batch, bitfield, value, manifest }) {
|
|
452
453
|
manifest = this._verifyBatchUpgrade(batch, manifest)
|
|
453
454
|
|
|
454
|
-
if (!batch.commitable()) return false
|
|
455
|
-
|
|
456
|
-
if (this.preupdate !== null) await this.preupdate(batch, this.header.key)
|
|
457
|
-
|
|
458
455
|
if (
|
|
459
456
|
!(await this.state._verifyBlock(
|
|
460
457
|
batch,
|
|
@@ -668,7 +665,14 @@ module.exports = class Core {
|
|
|
668
665
|
if (proof.fork !== this.state.fork) return false
|
|
669
666
|
|
|
670
667
|
const batch = await MerkleTree.verify(this.state, proof)
|
|
671
|
-
|
|
668
|
+
|
|
669
|
+
if (batch.upgraded && batch.length <= this.state.length) {
|
|
670
|
+
await batch.downgrade()
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (!batch.commitable()) {
|
|
674
|
+
return false
|
|
675
|
+
}
|
|
672
676
|
|
|
673
677
|
const value = (proof.block && proof.block.value) || null
|
|
674
678
|
const op = {
|
package/lib/merkle-tree.js
CHANGED
|
@@ -293,6 +293,32 @@ class MerkleTreeBatch {
|
|
|
293
293
|
return offset
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
async downgrade() {
|
|
297
|
+
if (!this.upgraded) return true
|
|
298
|
+
|
|
299
|
+
const rx = this.storage.read()
|
|
300
|
+
const nodePromises = []
|
|
301
|
+
|
|
302
|
+
for (const r of this.roots) {
|
|
303
|
+
nodePromises.push(rx.getTreeNode(r.index))
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
rx.tryFlush()
|
|
307
|
+
|
|
308
|
+
const nodes = await Promise.all(nodePromises)
|
|
309
|
+
|
|
310
|
+
for (let i = 0; i < this.roots.length; i++) {
|
|
311
|
+
const root = this.roots[i]
|
|
312
|
+
const node = nodes[i]
|
|
313
|
+
|
|
314
|
+
if (!node) return false
|
|
315
|
+
if (!b4a.equals(node.hash, root.hash)) return false
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
this.upgraded = false
|
|
319
|
+
return true
|
|
320
|
+
}
|
|
321
|
+
|
|
296
322
|
async restore(length) {
|
|
297
323
|
if (length === this.length) return this
|
|
298
324
|
|
|
@@ -684,6 +710,28 @@ class MerkleTree {
|
|
|
684
710
|
return generateProof(session, rx, block, hash, seek, upgrade)
|
|
685
711
|
}
|
|
686
712
|
|
|
713
|
+
static maxMissingNodes(index, length) {
|
|
714
|
+
const head = 2 * length
|
|
715
|
+
const ite = flat.iterator(index)
|
|
716
|
+
|
|
717
|
+
// See iterator.rightSpan()
|
|
718
|
+
const iteRightSpan = ite.index + ite.factor / 2 - 1
|
|
719
|
+
// If the index is not in the current tree, we do not know how many missing nodes there are...
|
|
720
|
+
if (iteRightSpan >= head) return 0
|
|
721
|
+
|
|
722
|
+
for (const r of flat.fullRoots(head)) {
|
|
723
|
+
if (r === index) return 0
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
let cnt = 0
|
|
727
|
+
while (!ite.contains(head)) {
|
|
728
|
+
cnt++
|
|
729
|
+
ite.parent()
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
return cnt
|
|
733
|
+
}
|
|
734
|
+
|
|
687
735
|
static async missingNodes(session, index, length) {
|
|
688
736
|
const head = 2 * length
|
|
689
737
|
const ite = flat.iterator(index)
|
package/lib/messages.js
CHANGED
|
@@ -629,7 +629,8 @@ wire.sync = {
|
|
|
629
629
|
(m.canUpgrade ? 1 : 0) |
|
|
630
630
|
(m.uploading ? 2 : 0) |
|
|
631
631
|
(m.downloading ? 4 : 0) |
|
|
632
|
-
(m.hasManifest ? 8 : 0)
|
|
632
|
+
(m.hasManifest ? 8 : 0) |
|
|
633
|
+
(m.allowPush ? 16 : 0)
|
|
633
634
|
)
|
|
634
635
|
c.uint.encode(state, m.fork)
|
|
635
636
|
c.uint.encode(state, m.length)
|
|
@@ -645,7 +646,8 @@ wire.sync = {
|
|
|
645
646
|
canUpgrade: (flags & 1) !== 0,
|
|
646
647
|
uploading: (flags & 2) !== 0,
|
|
647
648
|
downloading: (flags & 4) !== 0,
|
|
648
|
-
hasManifest: (flags & 8) !== 0
|
|
649
|
+
hasManifest: (flags & 8) !== 0,
|
|
650
|
+
allowPush: (flags & 16) !== 0
|
|
649
651
|
}
|
|
650
652
|
}
|
|
651
653
|
}
|
package/lib/replicator.js
CHANGED
|
@@ -187,6 +187,11 @@ class RangeRequest extends Attachable {
|
|
|
187
187
|
this.ifAvailable = ifAvailable
|
|
188
188
|
this.blocks = blocks
|
|
189
189
|
this.ranges = ranges
|
|
190
|
+
ranges.push(this)
|
|
191
|
+
|
|
192
|
+
if (this.end === -1) {
|
|
193
|
+
this.replicator._alwaysLatestBlock++
|
|
194
|
+
}
|
|
190
195
|
|
|
191
196
|
// As passed by the user, immut
|
|
192
197
|
this.userStart = start
|
|
@@ -194,10 +199,17 @@ class RangeRequest extends Attachable {
|
|
|
194
199
|
}
|
|
195
200
|
|
|
196
201
|
_unref() {
|
|
197
|
-
const
|
|
198
|
-
if (
|
|
202
|
+
const rangeIndex = this.ranges.indexOf(this)
|
|
203
|
+
if (rangeIndex === -1) return
|
|
204
|
+
|
|
199
205
|
const h = this.ranges.pop()
|
|
200
|
-
if (
|
|
206
|
+
if (h !== this) {
|
|
207
|
+
this.ranges[rangeIndex] = h
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (this.end === -1) {
|
|
211
|
+
this.replicator._alwaysLatestBlock--
|
|
212
|
+
}
|
|
201
213
|
}
|
|
202
214
|
|
|
203
215
|
_cancel(r) {
|
|
@@ -371,7 +383,6 @@ class ProofRequest {
|
|
|
371
383
|
const [proof, block] = await Promise.all([this.proof.settle(), this.block])
|
|
372
384
|
|
|
373
385
|
if (this.manifest) proof.manifest = this.manifest
|
|
374
|
-
|
|
375
386
|
if (!block && proof.block) return null
|
|
376
387
|
|
|
377
388
|
if (block) proof.block.value = block
|
|
@@ -450,6 +461,8 @@ class Peer {
|
|
|
450
461
|
this.remoteBitfield = new RemoteBitfield()
|
|
451
462
|
this.missingBlocks = new RemoteBitfield()
|
|
452
463
|
|
|
464
|
+
this.pushing = false
|
|
465
|
+
|
|
453
466
|
this.remoteFork = 0
|
|
454
467
|
this.remoteLength = 0
|
|
455
468
|
this.remoteCanUpgrade = false
|
|
@@ -457,6 +470,7 @@ class Peer {
|
|
|
457
470
|
this.remoteDownloading = true
|
|
458
471
|
this.remoteSynced = false
|
|
459
472
|
this.remoteHasManifest = false
|
|
473
|
+
this.remoteAllowPush = false
|
|
460
474
|
this.remoteRequests = new Map()
|
|
461
475
|
|
|
462
476
|
this.segmentsWanted = new Set()
|
|
@@ -592,7 +606,8 @@ class Peer {
|
|
|
592
606
|
canUpgrade: this.canUpgrade,
|
|
593
607
|
uploading: true,
|
|
594
608
|
downloading: this.replicator.isDownloading(),
|
|
595
|
-
hasManifest: !!this.core.header.manifest && this.core.compat === false
|
|
609
|
+
hasManifest: !!this.core.header.manifest && this.core.compat === false,
|
|
610
|
+
allowPush: this.replicator.allowPush
|
|
596
611
|
})
|
|
597
612
|
incrementTx(this.stats.wireSync, this.replicator.stats.wireSync)
|
|
598
613
|
}
|
|
@@ -677,7 +692,18 @@ class Peer {
|
|
|
677
692
|
return false
|
|
678
693
|
}
|
|
679
694
|
|
|
680
|
-
async onsync(
|
|
695
|
+
async onsync(msg) {
|
|
696
|
+
const {
|
|
697
|
+
fork,
|
|
698
|
+
length,
|
|
699
|
+
remoteLength,
|
|
700
|
+
canUpgrade,
|
|
701
|
+
uploading,
|
|
702
|
+
downloading,
|
|
703
|
+
hasManifest,
|
|
704
|
+
allowPush
|
|
705
|
+
} = msg
|
|
706
|
+
|
|
681
707
|
const lengthChanged = length !== this.remoteLength
|
|
682
708
|
const sameFork = fork === this.core.state.fork
|
|
683
709
|
|
|
@@ -688,6 +714,7 @@ class Peer {
|
|
|
688
714
|
this.remoteUploading = uploading
|
|
689
715
|
this.remoteDownloading = downloading
|
|
690
716
|
this.remoteHasManifest = hasManifest
|
|
717
|
+
this.remoteAllowPush = allowPush
|
|
691
718
|
|
|
692
719
|
if (this.closeIfIdle()) return
|
|
693
720
|
|
|
@@ -783,13 +810,13 @@ class Peer {
|
|
|
783
810
|
}
|
|
784
811
|
}
|
|
785
812
|
|
|
786
|
-
async _getProof(batch, msg) {
|
|
813
|
+
async _getProof(batch, msg, pushing) {
|
|
787
814
|
let block = null
|
|
788
815
|
|
|
789
816
|
if (msg.block) {
|
|
790
817
|
const index = msg.block.index
|
|
791
818
|
|
|
792
|
-
if (msg.fork !== this.core.state.fork || !this.core.bitfield.get(index)) {
|
|
819
|
+
if (!pushing && (msg.fork !== this.core.state.fork || !this.core.bitfield.get(index))) {
|
|
793
820
|
return new ProofRequest(msg, null, null, null)
|
|
794
821
|
}
|
|
795
822
|
|
|
@@ -866,13 +893,50 @@ class Peer {
|
|
|
866
893
|
this.receiverBusy = false
|
|
867
894
|
}
|
|
868
895
|
|
|
896
|
+
async push(index) {
|
|
897
|
+
if (!this.remoteAllowPush) return
|
|
898
|
+
if (this.core.state.fork !== this.remoteFork) return
|
|
899
|
+
if (this.remoteBitfield.get(index)) return
|
|
900
|
+
|
|
901
|
+
const msg = {
|
|
902
|
+
id: 0,
|
|
903
|
+
fork: this.core.state.fork,
|
|
904
|
+
block: null,
|
|
905
|
+
hash: null,
|
|
906
|
+
seek: null,
|
|
907
|
+
upgrade: null,
|
|
908
|
+
manifest: this.remoteLength === 0,
|
|
909
|
+
priority: 0
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
msg.block = {
|
|
913
|
+
index,
|
|
914
|
+
nodes: MerkleTree.maxMissingNodes(2 * index, this.remoteLength)
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
if (index >= this.remoteLength) {
|
|
918
|
+
msg.upgrade = {
|
|
919
|
+
start: this.remoteLength,
|
|
920
|
+
length: this.core.state.length - this.remoteLength
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
const batch = this.core.storage.read()
|
|
925
|
+
const req = await this._getProof(batch, msg, true)
|
|
926
|
+
if (req === null) return
|
|
927
|
+
|
|
928
|
+
batch.tryFlush()
|
|
929
|
+
|
|
930
|
+
await this._fulfillRequest(req, true)
|
|
931
|
+
}
|
|
932
|
+
|
|
869
933
|
async _handleRequest(msg) {
|
|
870
934
|
const batch = this.core.storage.read()
|
|
871
935
|
|
|
872
936
|
// TODO: could still be answerable if (index, fork) is an ancestor of the current fork
|
|
873
937
|
const req =
|
|
874
938
|
msg.fork === this.core.state.fork
|
|
875
|
-
? await this._getProof(batch, msg)
|
|
939
|
+
? await this._getProof(batch, msg, false)
|
|
876
940
|
: new ProofRequest(msg, null, null, null)
|
|
877
941
|
|
|
878
942
|
if (req === null) {
|
|
@@ -882,20 +946,25 @@ class Peer {
|
|
|
882
946
|
|
|
883
947
|
batch.tryFlush()
|
|
884
948
|
|
|
885
|
-
await this._fulfillRequest(req)
|
|
949
|
+
await this._fulfillRequest(req, false)
|
|
886
950
|
}
|
|
887
951
|
|
|
888
|
-
async _fulfillRequest(req) {
|
|
952
|
+
async _fulfillRequest(req, pushing) {
|
|
889
953
|
const proof = await req.fulfill()
|
|
890
954
|
|
|
891
|
-
|
|
892
|
-
|
|
955
|
+
if (!pushing) {
|
|
956
|
+
// if cancelled do not reply
|
|
957
|
+
if (this.remoteRequests.get(req.msg.id) !== req.msg) {
|
|
958
|
+
return
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// sync from now on, so safe to delete from the map
|
|
962
|
+
this.remoteRequests.delete(req.msg.id)
|
|
963
|
+
} else if (!this.remoteAllowPush) {
|
|
964
|
+
// if pushing but remote disabled it, just drop it
|
|
893
965
|
return
|
|
894
966
|
}
|
|
895
967
|
|
|
896
|
-
// sync from now on, so safe to delete from the map
|
|
897
|
-
this.remoteRequests.delete(req.msg.id)
|
|
898
|
-
|
|
899
968
|
if (!this.isActive() && proof.block !== null) {
|
|
900
969
|
return
|
|
901
970
|
}
|
|
@@ -924,6 +993,15 @@ class Peer {
|
|
|
924
993
|
this.replicator._onupload(proof.block.index, proof.block.value.byteLength, this)
|
|
925
994
|
}
|
|
926
995
|
|
|
996
|
+
// TODO: we should prob move to a sep length prop for this. This is just quick and dirty
|
|
997
|
+
// to produce better push upgrade proofs as we can better predict what length the remote
|
|
998
|
+
// is going to be at.
|
|
999
|
+
if ((pushing || this.pushing) && proof.upgrade) {
|
|
1000
|
+
this.pushing = true
|
|
1001
|
+
const remoteLength = proof.upgrade.start + proof.upgrade.length
|
|
1002
|
+
if (remoteLength > this.remoteLength) this.remoteLength = remoteLength
|
|
1003
|
+
}
|
|
1004
|
+
|
|
927
1005
|
this.wireData.send({
|
|
928
1006
|
request: req.msg.id,
|
|
929
1007
|
fork: req.msg.fork,
|
|
@@ -975,6 +1053,11 @@ class Peer {
|
|
|
975
1053
|
incrementTx(this.stats.wireRequest, this.replicator.stats.wireRequest)
|
|
976
1054
|
}
|
|
977
1055
|
|
|
1056
|
+
_unmarkInflightBlockRequest(req, data) {
|
|
1057
|
+
if (isBlockRequest(req)) this.replicator._unmarkInflight(req.block.index)
|
|
1058
|
+
else if (data.block && !req) this.replicator._unmarkInflight(data.block.index)
|
|
1059
|
+
}
|
|
1060
|
+
|
|
978
1061
|
async ondata(data) {
|
|
979
1062
|
// always allow a fork conflict proof to be sent
|
|
980
1063
|
if (data.request === 0 && data.upgrade && data.upgrade.start === 0) {
|
|
@@ -987,7 +1070,14 @@ class Peer {
|
|
|
987
1070
|
|
|
988
1071
|
// no push atm, TODO: check if this satisfies another pending request
|
|
989
1072
|
// allow reorg pushes tho as those are not written to storage so we'll take all the help we can get
|
|
990
|
-
if (req === null && reorg === false)
|
|
1073
|
+
if (req === null && reorg === false && !this.replicator.allowPush) {
|
|
1074
|
+
return
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
if (req === null && reorg === false && data.block) {
|
|
1078
|
+
// mark this as inflight to avoid parallel requests
|
|
1079
|
+
this.replicator._markInflight(data.block.index)
|
|
1080
|
+
}
|
|
991
1081
|
|
|
992
1082
|
if (req !== null) {
|
|
993
1083
|
if (req.peer !== this) return
|
|
@@ -998,7 +1088,7 @@ class Peer {
|
|
|
998
1088
|
if (reorg === true) return await this.replicator._onreorgdata(this, req, data)
|
|
999
1089
|
} catch (err) {
|
|
1000
1090
|
safetyCatch(err)
|
|
1001
|
-
|
|
1091
|
+
this._unmarkInflightBlockRequest(req, data)
|
|
1002
1092
|
|
|
1003
1093
|
this.paused = true
|
|
1004
1094
|
this.replicator._oninvaliddata(err, req, data, this)
|
|
@@ -1009,13 +1099,13 @@ class Peer {
|
|
|
1009
1099
|
if (isBlockRequest(req)) this.replicator._markProcessing(req.block.index)
|
|
1010
1100
|
|
|
1011
1101
|
try {
|
|
1012
|
-
if (!
|
|
1102
|
+
if (!this.replicator._matchingRequest(req, data) || !(await this.core.verify(data, this))) {
|
|
1013
1103
|
this.replicator._onnodata(this, req, 0)
|
|
1014
1104
|
return
|
|
1015
1105
|
}
|
|
1016
1106
|
} catch (err) {
|
|
1017
1107
|
safetyCatch(err)
|
|
1018
|
-
|
|
1108
|
+
this._unmarkInflightBlockRequest(req, data)
|
|
1019
1109
|
|
|
1020
1110
|
if (err.code === 'WRITE_FAILED') {
|
|
1021
1111
|
// For example, we don't want to keep pulling data when storage is full
|
|
@@ -1031,7 +1121,12 @@ class Peer {
|
|
|
1031
1121
|
this._checkIfConflict()
|
|
1032
1122
|
}
|
|
1033
1123
|
|
|
1034
|
-
|
|
1124
|
+
// if push mode, we MIGHT get an occasional bad message
|
|
1125
|
+
// no need to mega bail for that. TODO: rate limit instead as a general thing
|
|
1126
|
+
if (!this.replicator.allowPush) {
|
|
1127
|
+
this.paused = true
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1035
1130
|
this.replicator._onnodata(this, req, 0)
|
|
1036
1131
|
this.replicator._oninvaliddata(err, req, data, this)
|
|
1037
1132
|
return
|
|
@@ -1049,7 +1144,11 @@ class Peer {
|
|
|
1049
1144
|
|
|
1050
1145
|
onnodata({ request, reason }) {
|
|
1051
1146
|
const req = request > 0 ? this.replicator._inflight.get(request) : null
|
|
1052
|
-
|
|
1147
|
+
|
|
1148
|
+
if (req === null || req.peer !== this) {
|
|
1149
|
+
this.replicator.updateAll()
|
|
1150
|
+
return
|
|
1151
|
+
}
|
|
1053
1152
|
|
|
1054
1153
|
this._onrequestroundtrip(req)
|
|
1055
1154
|
this.replicator._onnodata(this, req, reason)
|
|
@@ -1256,11 +1355,32 @@ class Peer {
|
|
|
1256
1355
|
this._send(req)
|
|
1257
1356
|
}
|
|
1258
1357
|
|
|
1358
|
+
_includeLastBlock() {
|
|
1359
|
+
if (this.replicator._alwaysLatestBlock === 0) return null
|
|
1360
|
+
|
|
1361
|
+
const index = this.remoteLength - 1
|
|
1362
|
+
|
|
1363
|
+
// only valid if its an OOB get
|
|
1364
|
+
if (index < this.core.state.length) return null
|
|
1365
|
+
|
|
1366
|
+
// do the normal checks
|
|
1367
|
+
if (!this._remoteHasBlock(index)) return null
|
|
1368
|
+
if (!this._canRequest(index)) return null
|
|
1369
|
+
|
|
1370
|
+
// atm we only ever do one upgrade request in parallel, if that changes
|
|
1371
|
+
// then we should add some check here for inflights to avoid over requesting
|
|
1372
|
+
const b = this.replicator._blocks.add(index, PRIORITY.NORMAL)
|
|
1373
|
+
return b
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1259
1376
|
_requestUpgrade(u) {
|
|
1260
1377
|
const req = this._makeRequest(true, 0, 0)
|
|
1261
1378
|
if (req === null) return false
|
|
1262
1379
|
|
|
1263
|
-
this.
|
|
1380
|
+
const b = this._includeLastBlock()
|
|
1381
|
+
|
|
1382
|
+
if (b) this._sendBlockRequest(req, b)
|
|
1383
|
+
else this._send(req)
|
|
1264
1384
|
|
|
1265
1385
|
return true
|
|
1266
1386
|
}
|
|
@@ -1597,12 +1717,15 @@ module.exports = class Replicator {
|
|
|
1597
1717
|
notDownloadingLinger = NOT_DOWNLOADING_SLACK,
|
|
1598
1718
|
eagerUpgrade = true,
|
|
1599
1719
|
allowFork = true,
|
|
1720
|
+
allowPush = false,
|
|
1721
|
+
alwaysLatestBlock = false,
|
|
1600
1722
|
inflightRange = null
|
|
1601
1723
|
} = {}
|
|
1602
1724
|
) {
|
|
1603
1725
|
this.core = core
|
|
1604
1726
|
this.eagerUpgrade = eagerUpgrade
|
|
1605
1727
|
this.allowFork = allowFork
|
|
1728
|
+
this.allowPush = allowPush
|
|
1606
1729
|
this.ondownloading = null // optional external hook for monitoring downloading status
|
|
1607
1730
|
this.peers = []
|
|
1608
1731
|
this.findingPeers = 0 // updatable from the outside
|
|
@@ -1635,6 +1758,7 @@ module.exports = class Replicator {
|
|
|
1635
1758
|
this._blocks = new BlockTracker(this)
|
|
1636
1759
|
this._hashes = new BlockTracker(this)
|
|
1637
1760
|
|
|
1761
|
+
this._alwaysLatestBlock = alwaysLatestBlock ? 1 : 0
|
|
1638
1762
|
this._queued = []
|
|
1639
1763
|
|
|
1640
1764
|
this._seeks = []
|
|
@@ -1668,6 +1792,20 @@ module.exports = class Replicator {
|
|
|
1668
1792
|
return this.downloading || !this._inflight.idle
|
|
1669
1793
|
}
|
|
1670
1794
|
|
|
1795
|
+
async push(index) {
|
|
1796
|
+
const all = []
|
|
1797
|
+
for (const peer of this.peers) {
|
|
1798
|
+
all.push(peer.push(index))
|
|
1799
|
+
}
|
|
1800
|
+
await Promise.all(all)
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
setAllowPush(allowPush) {
|
|
1804
|
+
if (allowPush === this.allowPush) return
|
|
1805
|
+
this.allowPush = allowPush
|
|
1806
|
+
for (const peer of this.peers) peer.sendSync()
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1671
1809
|
setDownloading(downloading) {
|
|
1672
1810
|
clearTimeout(this._downloadingTimer)
|
|
1673
1811
|
|
|
@@ -1843,10 +1981,8 @@ module.exports = class Replicator {
|
|
|
1843
1981
|
// Also auto compresses the range based on local bitfield
|
|
1844
1982
|
clampRange(this.core, r)
|
|
1845
1983
|
|
|
1846
|
-
this._ranges.push(r)
|
|
1847
|
-
|
|
1848
1984
|
if (r.end !== -1 && r.start >= r.end) {
|
|
1849
|
-
this._resolveRangeRequest(r
|
|
1985
|
+
this._resolveRangeRequest(r)
|
|
1850
1986
|
return ref
|
|
1851
1987
|
}
|
|
1852
1988
|
|
|
@@ -1886,6 +2022,25 @@ module.exports = class Replicator {
|
|
|
1886
2022
|
if (cleared) this.updateAll()
|
|
1887
2023
|
}
|
|
1888
2024
|
|
|
2025
|
+
_matchingRequest(req, data) {
|
|
2026
|
+
if (this.allowPush) {
|
|
2027
|
+
return true
|
|
2028
|
+
}
|
|
2029
|
+
if (data.block !== null && (req.block === null || req.block.index !== data.block.index)) {
|
|
2030
|
+
return false
|
|
2031
|
+
}
|
|
2032
|
+
if (data.hash !== null && (req.hash === null || req.hash.index !== data.hash.index)) {
|
|
2033
|
+
return false
|
|
2034
|
+
}
|
|
2035
|
+
if (data.seek !== null && (req.seek === null || req.seek.bytes !== data.seek.bytes)) {
|
|
2036
|
+
return false
|
|
2037
|
+
}
|
|
2038
|
+
if (data.upgrade !== null && req.upgrade === null) {
|
|
2039
|
+
return false
|
|
2040
|
+
}
|
|
2041
|
+
return req.fork === data.fork
|
|
2042
|
+
}
|
|
2043
|
+
|
|
1889
2044
|
_addUpgradeMaybe() {
|
|
1890
2045
|
return this.eagerUpgrade === true ? this._addUpgrade() : this._upgrade
|
|
1891
2046
|
}
|
|
@@ -2115,12 +2270,9 @@ module.exports = class Replicator {
|
|
|
2115
2270
|
return true
|
|
2116
2271
|
}
|
|
2117
2272
|
|
|
2118
|
-
_resolveRangeRequest(req
|
|
2119
|
-
const head = this._ranges.pop()
|
|
2120
|
-
|
|
2121
|
-
if (index < this._ranges.length) this._ranges[index] = head
|
|
2122
|
-
|
|
2273
|
+
_resolveRangeRequest(req) {
|
|
2123
2274
|
req.resolve(true)
|
|
2275
|
+
req.gc()
|
|
2124
2276
|
}
|
|
2125
2277
|
|
|
2126
2278
|
_clearInflightBlock(tracker, req) {
|
|
@@ -2143,7 +2295,7 @@ module.exports = class Replicator {
|
|
|
2143
2295
|
}
|
|
2144
2296
|
|
|
2145
2297
|
_clearInflightUpgrade(req) {
|
|
2146
|
-
if (removeInflight(this._upgrade.inflight, req) === false) return
|
|
2298
|
+
if (this._upgrade === null || removeInflight(this._upgrade.inflight, req) === false) return
|
|
2147
2299
|
this._upgrade.gc()
|
|
2148
2300
|
}
|
|
2149
2301
|
|
|
@@ -2182,7 +2334,8 @@ module.exports = class Replicator {
|
|
|
2182
2334
|
clampRange(this.core, r)
|
|
2183
2335
|
|
|
2184
2336
|
if (r.end !== -1 && r.start >= r.end) {
|
|
2185
|
-
this._resolveRangeRequest(r
|
|
2337
|
+
this._resolveRangeRequest(r)
|
|
2338
|
+
i--
|
|
2186
2339
|
if (len > this._ranges.length) len--
|
|
2187
2340
|
if (this._ranges.length === MAX_RANGES) updateAll = true
|
|
2188
2341
|
}
|
|
@@ -2231,7 +2384,8 @@ module.exports = class Replicator {
|
|
|
2231
2384
|
const r = this._ranges[i]
|
|
2232
2385
|
|
|
2233
2386
|
if (r.ifAvailable) {
|
|
2234
|
-
this._resolveRangeRequest(r
|
|
2387
|
+
this._resolveRangeRequest(r)
|
|
2388
|
+
i--
|
|
2235
2389
|
}
|
|
2236
2390
|
}
|
|
2237
2391
|
}
|
|
@@ -2269,7 +2423,10 @@ module.exports = class Replicator {
|
|
|
2269
2423
|
}
|
|
2270
2424
|
}
|
|
2271
2425
|
|
|
2272
|
-
|
|
2426
|
+
if (req) {
|
|
2427
|
+
this._clearRequest(peer, req)
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2273
2430
|
this.updateAll()
|
|
2274
2431
|
}
|
|
2275
2432
|
|
|
@@ -2322,7 +2479,10 @@ module.exports = class Replicator {
|
|
|
2322
2479
|
}
|
|
2323
2480
|
|
|
2324
2481
|
_ondata(peer, req, data) {
|
|
2325
|
-
|
|
2482
|
+
if (req) {
|
|
2483
|
+
req.elapsed = Date.now() - req.timestamp
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2326
2486
|
if (data.block !== null) {
|
|
2327
2487
|
this._resolveBlockRequest(this._blocks, data.block.index, data.block.value, req)
|
|
2328
2488
|
this._ondownload(data.block.index, data.block.value.byteLength, peer, req)
|
|
@@ -2770,16 +2930,6 @@ module.exports = class Replicator {
|
|
|
2770
2930
|
}
|
|
2771
2931
|
}
|
|
2772
2932
|
|
|
2773
|
-
function matchingRequest(req, data) {
|
|
2774
|
-
if (data.block !== null && (req.block === null || req.block.index !== data.block.index)) {
|
|
2775
|
-
return false
|
|
2776
|
-
}
|
|
2777
|
-
if (data.hash !== null && (req.hash === null || req.hash.index !== data.hash.index)) return false
|
|
2778
|
-
if (data.seek !== null && (req.seek === null || req.seek.bytes !== data.seek.bytes)) return false
|
|
2779
|
-
if (data.upgrade !== null && req.upgrade === null) return false
|
|
2780
|
-
return req.fork === data.fork
|
|
2781
|
-
}
|
|
2782
|
-
|
|
2783
2933
|
function removeHotswap(block) {
|
|
2784
2934
|
if (block.hotswap === null) return false
|
|
2785
2935
|
block.hotswap.ref.remove(block)
|
package/lib/session-state.js
CHANGED
|
@@ -325,7 +325,18 @@ module.exports = class SessionState {
|
|
|
325
325
|
await this.mutex.lock()
|
|
326
326
|
|
|
327
327
|
try {
|
|
328
|
-
if (
|
|
328
|
+
if (batch.upgraded && batch.length <= this.length) {
|
|
329
|
+
await batch.downgrade()
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (!batch.commitable()) {
|
|
333
|
+
return false
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (this.core.preupdate !== null) {
|
|
337
|
+
await this.core.preupdate(batch, this.core.header.key)
|
|
338
|
+
}
|
|
339
|
+
|
|
329
340
|
const tx = this.createWriteBatch()
|
|
330
341
|
this.updating = true
|
|
331
342
|
|
|
@@ -551,7 +562,7 @@ module.exports = class SessionState {
|
|
|
551
562
|
}
|
|
552
563
|
}
|
|
553
564
|
|
|
554
|
-
async append(values, { signature, keyPair, preappend, maxLength = -1 } = {}) {
|
|
565
|
+
async append(values, { signature, keyPair, preappend, postappend, maxLength = -1 } = {}) {
|
|
555
566
|
if (!keyPair && this.isDefault()) keyPair = this.core.header.keyPair
|
|
556
567
|
|
|
557
568
|
await this.mutex.lock()
|
|
@@ -623,6 +634,8 @@ module.exports = class SessionState {
|
|
|
623
634
|
this.byteLength = batch.byteLength
|
|
624
635
|
this.signature = batch.signature
|
|
625
636
|
|
|
637
|
+
if (postappend) await postappend(values)
|
|
638
|
+
|
|
626
639
|
this.onappend(tree, bitfield, flushed)
|
|
627
640
|
|
|
628
641
|
if (this.core.hintsChanged && this.isDefault()) await this.core.flushHints()
|