hypercore 11.20.2 → 11.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +197 -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
|
+
this.rangeIndex = ranges.push(this) - 1
|
|
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,18 @@ class RangeRequest extends Attachable {
|
|
|
194
199
|
}
|
|
195
200
|
|
|
196
201
|
_unref() {
|
|
197
|
-
|
|
198
|
-
if (
|
|
202
|
+
if (this.rangeIndex >= this.ranges.length) return
|
|
203
|
+
if (this.ranges[this.rangeIndex] !== this) return
|
|
204
|
+
|
|
199
205
|
const h = this.ranges.pop()
|
|
200
|
-
if (
|
|
206
|
+
if (h !== this) {
|
|
207
|
+
this.ranges[this.rangeIndex] = h
|
|
208
|
+
h.rangeIndex = this.rangeIndex
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (this.end === -1) {
|
|
212
|
+
this.replicator._alwaysLatestBlock--
|
|
213
|
+
}
|
|
201
214
|
}
|
|
202
215
|
|
|
203
216
|
_cancel(r) {
|
|
@@ -371,7 +384,6 @@ class ProofRequest {
|
|
|
371
384
|
const [proof, block] = await Promise.all([this.proof.settle(), this.block])
|
|
372
385
|
|
|
373
386
|
if (this.manifest) proof.manifest = this.manifest
|
|
374
|
-
|
|
375
387
|
if (!block && proof.block) return null
|
|
376
388
|
|
|
377
389
|
if (block) proof.block.value = block
|
|
@@ -450,6 +462,8 @@ class Peer {
|
|
|
450
462
|
this.remoteBitfield = new RemoteBitfield()
|
|
451
463
|
this.missingBlocks = new RemoteBitfield()
|
|
452
464
|
|
|
465
|
+
this.pushing = false
|
|
466
|
+
|
|
453
467
|
this.remoteFork = 0
|
|
454
468
|
this.remoteLength = 0
|
|
455
469
|
this.remoteCanUpgrade = false
|
|
@@ -457,6 +471,7 @@ class Peer {
|
|
|
457
471
|
this.remoteDownloading = true
|
|
458
472
|
this.remoteSynced = false
|
|
459
473
|
this.remoteHasManifest = false
|
|
474
|
+
this.remoteAllowPush = false
|
|
460
475
|
this.remoteRequests = new Map()
|
|
461
476
|
|
|
462
477
|
this.segmentsWanted = new Set()
|
|
@@ -592,7 +607,8 @@ class Peer {
|
|
|
592
607
|
canUpgrade: this.canUpgrade,
|
|
593
608
|
uploading: true,
|
|
594
609
|
downloading: this.replicator.isDownloading(),
|
|
595
|
-
hasManifest: !!this.core.header.manifest && this.core.compat === false
|
|
610
|
+
hasManifest: !!this.core.header.manifest && this.core.compat === false,
|
|
611
|
+
allowPush: this.replicator.allowPush
|
|
596
612
|
})
|
|
597
613
|
incrementTx(this.stats.wireSync, this.replicator.stats.wireSync)
|
|
598
614
|
}
|
|
@@ -677,7 +693,18 @@ class Peer {
|
|
|
677
693
|
return false
|
|
678
694
|
}
|
|
679
695
|
|
|
680
|
-
async onsync(
|
|
696
|
+
async onsync(msg) {
|
|
697
|
+
const {
|
|
698
|
+
fork,
|
|
699
|
+
length,
|
|
700
|
+
remoteLength,
|
|
701
|
+
canUpgrade,
|
|
702
|
+
uploading,
|
|
703
|
+
downloading,
|
|
704
|
+
hasManifest,
|
|
705
|
+
allowPush
|
|
706
|
+
} = msg
|
|
707
|
+
|
|
681
708
|
const lengthChanged = length !== this.remoteLength
|
|
682
709
|
const sameFork = fork === this.core.state.fork
|
|
683
710
|
|
|
@@ -688,6 +715,7 @@ class Peer {
|
|
|
688
715
|
this.remoteUploading = uploading
|
|
689
716
|
this.remoteDownloading = downloading
|
|
690
717
|
this.remoteHasManifest = hasManifest
|
|
718
|
+
this.remoteAllowPush = allowPush
|
|
691
719
|
|
|
692
720
|
if (this.closeIfIdle()) return
|
|
693
721
|
|
|
@@ -783,13 +811,13 @@ class Peer {
|
|
|
783
811
|
}
|
|
784
812
|
}
|
|
785
813
|
|
|
786
|
-
async _getProof(batch, msg) {
|
|
814
|
+
async _getProof(batch, msg, pushing) {
|
|
787
815
|
let block = null
|
|
788
816
|
|
|
789
817
|
if (msg.block) {
|
|
790
818
|
const index = msg.block.index
|
|
791
819
|
|
|
792
|
-
if (msg.fork !== this.core.state.fork || !this.core.bitfield.get(index)) {
|
|
820
|
+
if (!pushing && (msg.fork !== this.core.state.fork || !this.core.bitfield.get(index))) {
|
|
793
821
|
return new ProofRequest(msg, null, null, null)
|
|
794
822
|
}
|
|
795
823
|
|
|
@@ -866,13 +894,50 @@ class Peer {
|
|
|
866
894
|
this.receiverBusy = false
|
|
867
895
|
}
|
|
868
896
|
|
|
897
|
+
async push(index) {
|
|
898
|
+
if (!this.remoteAllowPush) return
|
|
899
|
+
if (this.core.state.fork !== this.remoteFork) return
|
|
900
|
+
if (this.remoteBitfield.get(index)) return
|
|
901
|
+
|
|
902
|
+
const msg = {
|
|
903
|
+
id: 0,
|
|
904
|
+
fork: this.core.state.fork,
|
|
905
|
+
block: null,
|
|
906
|
+
hash: null,
|
|
907
|
+
seek: null,
|
|
908
|
+
upgrade: null,
|
|
909
|
+
manifest: this.remoteLength === 0,
|
|
910
|
+
priority: 0
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
msg.block = {
|
|
914
|
+
index,
|
|
915
|
+
nodes: MerkleTree.maxMissingNodes(2 * index, this.remoteLength)
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
if (index >= this.remoteLength) {
|
|
919
|
+
msg.upgrade = {
|
|
920
|
+
start: this.remoteLength,
|
|
921
|
+
length: this.core.state.length - this.remoteLength
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
const batch = this.core.storage.read()
|
|
926
|
+
const req = await this._getProof(batch, msg, true)
|
|
927
|
+
if (req === null) return
|
|
928
|
+
|
|
929
|
+
batch.tryFlush()
|
|
930
|
+
|
|
931
|
+
await this._fulfillRequest(req, true)
|
|
932
|
+
}
|
|
933
|
+
|
|
869
934
|
async _handleRequest(msg) {
|
|
870
935
|
const batch = this.core.storage.read()
|
|
871
936
|
|
|
872
937
|
// TODO: could still be answerable if (index, fork) is an ancestor of the current fork
|
|
873
938
|
const req =
|
|
874
939
|
msg.fork === this.core.state.fork
|
|
875
|
-
? await this._getProof(batch, msg)
|
|
940
|
+
? await this._getProof(batch, msg, false)
|
|
876
941
|
: new ProofRequest(msg, null, null, null)
|
|
877
942
|
|
|
878
943
|
if (req === null) {
|
|
@@ -882,20 +947,25 @@ class Peer {
|
|
|
882
947
|
|
|
883
948
|
batch.tryFlush()
|
|
884
949
|
|
|
885
|
-
await this._fulfillRequest(req)
|
|
950
|
+
await this._fulfillRequest(req, false)
|
|
886
951
|
}
|
|
887
952
|
|
|
888
|
-
async _fulfillRequest(req) {
|
|
953
|
+
async _fulfillRequest(req, pushing) {
|
|
889
954
|
const proof = await req.fulfill()
|
|
890
955
|
|
|
891
|
-
|
|
892
|
-
|
|
956
|
+
if (!pushing) {
|
|
957
|
+
// if cancelled do not reply
|
|
958
|
+
if (this.remoteRequests.get(req.msg.id) !== req.msg) {
|
|
959
|
+
return
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// sync from now on, so safe to delete from the map
|
|
963
|
+
this.remoteRequests.delete(req.msg.id)
|
|
964
|
+
} else if (!this.remoteAllowPush) {
|
|
965
|
+
// if pushing but remote disabled it, just drop it
|
|
893
966
|
return
|
|
894
967
|
}
|
|
895
968
|
|
|
896
|
-
// sync from now on, so safe to delete from the map
|
|
897
|
-
this.remoteRequests.delete(req.msg.id)
|
|
898
|
-
|
|
899
969
|
if (!this.isActive() && proof.block !== null) {
|
|
900
970
|
return
|
|
901
971
|
}
|
|
@@ -924,6 +994,15 @@ class Peer {
|
|
|
924
994
|
this.replicator._onupload(proof.block.index, proof.block.value.byteLength, this)
|
|
925
995
|
}
|
|
926
996
|
|
|
997
|
+
// TODO: we should prob move to a sep length prop for this. This is just quick and dirty
|
|
998
|
+
// to produce better push upgrade proofs as we can better predict what length the remote
|
|
999
|
+
// is going to be at.
|
|
1000
|
+
if ((pushing || this.pushing) && proof.upgrade) {
|
|
1001
|
+
this.pushing = true
|
|
1002
|
+
const remoteLength = proof.upgrade.start + proof.upgrade.length
|
|
1003
|
+
if (remoteLength > this.remoteLength) this.remoteLength = remoteLength
|
|
1004
|
+
}
|
|
1005
|
+
|
|
927
1006
|
this.wireData.send({
|
|
928
1007
|
request: req.msg.id,
|
|
929
1008
|
fork: req.msg.fork,
|
|
@@ -975,6 +1054,11 @@ class Peer {
|
|
|
975
1054
|
incrementTx(this.stats.wireRequest, this.replicator.stats.wireRequest)
|
|
976
1055
|
}
|
|
977
1056
|
|
|
1057
|
+
_unmarkInflightBlockRequest(req, data) {
|
|
1058
|
+
if (isBlockRequest(req)) this.replicator._unmarkInflight(req.block.index)
|
|
1059
|
+
else if (data.block && !req) this.replicator._unmarkInflight(data.block.index)
|
|
1060
|
+
}
|
|
1061
|
+
|
|
978
1062
|
async ondata(data) {
|
|
979
1063
|
// always allow a fork conflict proof to be sent
|
|
980
1064
|
if (data.request === 0 && data.upgrade && data.upgrade.start === 0) {
|
|
@@ -987,7 +1071,14 @@ class Peer {
|
|
|
987
1071
|
|
|
988
1072
|
// no push atm, TODO: check if this satisfies another pending request
|
|
989
1073
|
// 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)
|
|
1074
|
+
if (req === null && reorg === false && !this.replicator.allowPush) {
|
|
1075
|
+
return
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
if (req === null && reorg === false && data.block) {
|
|
1079
|
+
// mark this as inflight to avoid parallel requests
|
|
1080
|
+
this.replicator._markInflight(data.block.index)
|
|
1081
|
+
}
|
|
991
1082
|
|
|
992
1083
|
if (req !== null) {
|
|
993
1084
|
if (req.peer !== this) return
|
|
@@ -998,7 +1089,7 @@ class Peer {
|
|
|
998
1089
|
if (reorg === true) return await this.replicator._onreorgdata(this, req, data)
|
|
999
1090
|
} catch (err) {
|
|
1000
1091
|
safetyCatch(err)
|
|
1001
|
-
|
|
1092
|
+
this._unmarkInflightBlockRequest(req, data)
|
|
1002
1093
|
|
|
1003
1094
|
this.paused = true
|
|
1004
1095
|
this.replicator._oninvaliddata(err, req, data, this)
|
|
@@ -1009,13 +1100,13 @@ class Peer {
|
|
|
1009
1100
|
if (isBlockRequest(req)) this.replicator._markProcessing(req.block.index)
|
|
1010
1101
|
|
|
1011
1102
|
try {
|
|
1012
|
-
if (!
|
|
1103
|
+
if (!this.replicator._matchingRequest(req, data) || !(await this.core.verify(data, this))) {
|
|
1013
1104
|
this.replicator._onnodata(this, req, 0)
|
|
1014
1105
|
return
|
|
1015
1106
|
}
|
|
1016
1107
|
} catch (err) {
|
|
1017
1108
|
safetyCatch(err)
|
|
1018
|
-
|
|
1109
|
+
this._unmarkInflightBlockRequest(req, data)
|
|
1019
1110
|
|
|
1020
1111
|
if (err.code === 'WRITE_FAILED') {
|
|
1021
1112
|
// For example, we don't want to keep pulling data when storage is full
|
|
@@ -1031,7 +1122,12 @@ class Peer {
|
|
|
1031
1122
|
this._checkIfConflict()
|
|
1032
1123
|
}
|
|
1033
1124
|
|
|
1034
|
-
|
|
1125
|
+
// if push mode, we MIGHT get an occasional bad message
|
|
1126
|
+
// no need to mega bail for that. TODO: rate limit instead as a general thing
|
|
1127
|
+
if (!this.replicator.allowPush) {
|
|
1128
|
+
this.paused = true
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1035
1131
|
this.replicator._onnodata(this, req, 0)
|
|
1036
1132
|
this.replicator._oninvaliddata(err, req, data, this)
|
|
1037
1133
|
return
|
|
@@ -1049,7 +1145,11 @@ class Peer {
|
|
|
1049
1145
|
|
|
1050
1146
|
onnodata({ request, reason }) {
|
|
1051
1147
|
const req = request > 0 ? this.replicator._inflight.get(request) : null
|
|
1052
|
-
|
|
1148
|
+
|
|
1149
|
+
if (req === null || req.peer !== this) {
|
|
1150
|
+
this.replicator.updateAll()
|
|
1151
|
+
return
|
|
1152
|
+
}
|
|
1053
1153
|
|
|
1054
1154
|
this._onrequestroundtrip(req)
|
|
1055
1155
|
this.replicator._onnodata(this, req, reason)
|
|
@@ -1256,11 +1356,32 @@ class Peer {
|
|
|
1256
1356
|
this._send(req)
|
|
1257
1357
|
}
|
|
1258
1358
|
|
|
1359
|
+
_includeLastBlock() {
|
|
1360
|
+
if (this.replicator._alwaysLatestBlock === 0) return null
|
|
1361
|
+
|
|
1362
|
+
const index = this.remoteLength - 1
|
|
1363
|
+
|
|
1364
|
+
// only valid if its an OOB get
|
|
1365
|
+
if (index < this.core.state.length) return null
|
|
1366
|
+
|
|
1367
|
+
// do the normal checks
|
|
1368
|
+
if (!this._remoteHasBlock(index)) return null
|
|
1369
|
+
if (!this._canRequest(index)) return null
|
|
1370
|
+
|
|
1371
|
+
// atm we only ever do one upgrade request in parallel, if that changes
|
|
1372
|
+
// then we should add some check here for inflights to avoid over requesting
|
|
1373
|
+
const b = this.replicator._blocks.add(index, PRIORITY.NORMAL)
|
|
1374
|
+
return b
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1259
1377
|
_requestUpgrade(u) {
|
|
1260
1378
|
const req = this._makeRequest(true, 0, 0)
|
|
1261
1379
|
if (req === null) return false
|
|
1262
1380
|
|
|
1263
|
-
this.
|
|
1381
|
+
const b = this._includeLastBlock()
|
|
1382
|
+
|
|
1383
|
+
if (b) this._sendBlockRequest(req, b)
|
|
1384
|
+
else this._send(req)
|
|
1264
1385
|
|
|
1265
1386
|
return true
|
|
1266
1387
|
}
|
|
@@ -1597,12 +1718,15 @@ module.exports = class Replicator {
|
|
|
1597
1718
|
notDownloadingLinger = NOT_DOWNLOADING_SLACK,
|
|
1598
1719
|
eagerUpgrade = true,
|
|
1599
1720
|
allowFork = true,
|
|
1721
|
+
allowPush = false,
|
|
1722
|
+
alwaysLatestBlock = false,
|
|
1600
1723
|
inflightRange = null
|
|
1601
1724
|
} = {}
|
|
1602
1725
|
) {
|
|
1603
1726
|
this.core = core
|
|
1604
1727
|
this.eagerUpgrade = eagerUpgrade
|
|
1605
1728
|
this.allowFork = allowFork
|
|
1729
|
+
this.allowPush = allowPush
|
|
1606
1730
|
this.ondownloading = null // optional external hook for monitoring downloading status
|
|
1607
1731
|
this.peers = []
|
|
1608
1732
|
this.findingPeers = 0 // updatable from the outside
|
|
@@ -1635,6 +1759,7 @@ module.exports = class Replicator {
|
|
|
1635
1759
|
this._blocks = new BlockTracker(this)
|
|
1636
1760
|
this._hashes = new BlockTracker(this)
|
|
1637
1761
|
|
|
1762
|
+
this._alwaysLatestBlock = alwaysLatestBlock ? 1 : 0
|
|
1638
1763
|
this._queued = []
|
|
1639
1764
|
|
|
1640
1765
|
this._seeks = []
|
|
@@ -1668,6 +1793,20 @@ module.exports = class Replicator {
|
|
|
1668
1793
|
return this.downloading || !this._inflight.idle
|
|
1669
1794
|
}
|
|
1670
1795
|
|
|
1796
|
+
async push(index) {
|
|
1797
|
+
const all = []
|
|
1798
|
+
for (const peer of this.peers) {
|
|
1799
|
+
all.push(peer.push(index))
|
|
1800
|
+
}
|
|
1801
|
+
await Promise.all(all)
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
setAllowPush(allowPush) {
|
|
1805
|
+
if (allowPush === this.allowPush) return
|
|
1806
|
+
this.allowPush = allowPush
|
|
1807
|
+
for (const peer of this.peers) peer.sendSync()
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1671
1810
|
setDownloading(downloading) {
|
|
1672
1811
|
clearTimeout(this._downloadingTimer)
|
|
1673
1812
|
|
|
@@ -1843,10 +1982,8 @@ module.exports = class Replicator {
|
|
|
1843
1982
|
// Also auto compresses the range based on local bitfield
|
|
1844
1983
|
clampRange(this.core, r)
|
|
1845
1984
|
|
|
1846
|
-
this._ranges.push(r)
|
|
1847
|
-
|
|
1848
1985
|
if (r.end !== -1 && r.start >= r.end) {
|
|
1849
|
-
this._resolveRangeRequest(r
|
|
1986
|
+
this._resolveRangeRequest(r)
|
|
1850
1987
|
return ref
|
|
1851
1988
|
}
|
|
1852
1989
|
|
|
@@ -1886,6 +2023,25 @@ module.exports = class Replicator {
|
|
|
1886
2023
|
if (cleared) this.updateAll()
|
|
1887
2024
|
}
|
|
1888
2025
|
|
|
2026
|
+
_matchingRequest(req, data) {
|
|
2027
|
+
if (this.allowPush) {
|
|
2028
|
+
return true
|
|
2029
|
+
}
|
|
2030
|
+
if (data.block !== null && (req.block === null || req.block.index !== data.block.index)) {
|
|
2031
|
+
return false
|
|
2032
|
+
}
|
|
2033
|
+
if (data.hash !== null && (req.hash === null || req.hash.index !== data.hash.index)) {
|
|
2034
|
+
return false
|
|
2035
|
+
}
|
|
2036
|
+
if (data.seek !== null && (req.seek === null || req.seek.bytes !== data.seek.bytes)) {
|
|
2037
|
+
return false
|
|
2038
|
+
}
|
|
2039
|
+
if (data.upgrade !== null && req.upgrade === null) {
|
|
2040
|
+
return false
|
|
2041
|
+
}
|
|
2042
|
+
return req.fork === data.fork
|
|
2043
|
+
}
|
|
2044
|
+
|
|
1889
2045
|
_addUpgradeMaybe() {
|
|
1890
2046
|
return this.eagerUpgrade === true ? this._addUpgrade() : this._upgrade
|
|
1891
2047
|
}
|
|
@@ -2115,12 +2271,9 @@ module.exports = class Replicator {
|
|
|
2115
2271
|
return true
|
|
2116
2272
|
}
|
|
2117
2273
|
|
|
2118
|
-
_resolveRangeRequest(req
|
|
2119
|
-
const head = this._ranges.pop()
|
|
2120
|
-
|
|
2121
|
-
if (index < this._ranges.length) this._ranges[index] = head
|
|
2122
|
-
|
|
2274
|
+
_resolveRangeRequest(req) {
|
|
2123
2275
|
req.resolve(true)
|
|
2276
|
+
req.gc()
|
|
2124
2277
|
}
|
|
2125
2278
|
|
|
2126
2279
|
_clearInflightBlock(tracker, req) {
|
|
@@ -2143,7 +2296,7 @@ module.exports = class Replicator {
|
|
|
2143
2296
|
}
|
|
2144
2297
|
|
|
2145
2298
|
_clearInflightUpgrade(req) {
|
|
2146
|
-
if (removeInflight(this._upgrade.inflight, req) === false) return
|
|
2299
|
+
if (this._upgrade === null || removeInflight(this._upgrade.inflight, req) === false) return
|
|
2147
2300
|
this._upgrade.gc()
|
|
2148
2301
|
}
|
|
2149
2302
|
|
|
@@ -2182,7 +2335,8 @@ module.exports = class Replicator {
|
|
|
2182
2335
|
clampRange(this.core, r)
|
|
2183
2336
|
|
|
2184
2337
|
if (r.end !== -1 && r.start >= r.end) {
|
|
2185
|
-
this._resolveRangeRequest(r
|
|
2338
|
+
this._resolveRangeRequest(r)
|
|
2339
|
+
i--
|
|
2186
2340
|
if (len > this._ranges.length) len--
|
|
2187
2341
|
if (this._ranges.length === MAX_RANGES) updateAll = true
|
|
2188
2342
|
}
|
|
@@ -2231,7 +2385,8 @@ module.exports = class Replicator {
|
|
|
2231
2385
|
const r = this._ranges[i]
|
|
2232
2386
|
|
|
2233
2387
|
if (r.ifAvailable) {
|
|
2234
|
-
this._resolveRangeRequest(r
|
|
2388
|
+
this._resolveRangeRequest(r)
|
|
2389
|
+
i--
|
|
2235
2390
|
}
|
|
2236
2391
|
}
|
|
2237
2392
|
}
|
|
@@ -2269,7 +2424,10 @@ module.exports = class Replicator {
|
|
|
2269
2424
|
}
|
|
2270
2425
|
}
|
|
2271
2426
|
|
|
2272
|
-
|
|
2427
|
+
if (req) {
|
|
2428
|
+
this._clearRequest(peer, req)
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2273
2431
|
this.updateAll()
|
|
2274
2432
|
}
|
|
2275
2433
|
|
|
@@ -2322,7 +2480,10 @@ module.exports = class Replicator {
|
|
|
2322
2480
|
}
|
|
2323
2481
|
|
|
2324
2482
|
_ondata(peer, req, data) {
|
|
2325
|
-
|
|
2483
|
+
if (req) {
|
|
2484
|
+
req.elapsed = Date.now() - req.timestamp
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2326
2487
|
if (data.block !== null) {
|
|
2327
2488
|
this._resolveBlockRequest(this._blocks, data.block.index, data.block.value, req)
|
|
2328
2489
|
this._ondownload(data.block.index, data.block.value.byteLength, peer, req)
|
|
@@ -2770,16 +2931,6 @@ module.exports = class Replicator {
|
|
|
2770
2931
|
}
|
|
2771
2932
|
}
|
|
2772
2933
|
|
|
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
2934
|
function removeHotswap(block) {
|
|
2784
2935
|
if (block.hotswap === null) return false
|
|
2785
2936
|
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()
|