hypercore 10.28.11 → 10.29.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.
@@ -345,8 +345,7 @@ class ByteSeeker {
345
345
  if (!bytes) return [0, 0]
346
346
 
347
347
  for (const node of this.tree.roots) { // all async ticks happen once we find the root so safe
348
- let size = node.size
349
- if (this.padding > 0) size -= this.padding * flat.countLeaves(node.index)
348
+ const size = getUnpaddedSize(node, this.padding, null)
350
349
 
351
350
  if (bytes === size) return [flat.rightSpan(node.index) + 2, 0]
352
351
  if (bytes > size) {
@@ -359,8 +358,7 @@ class ByteSeeker {
359
358
  while ((ite.index & 1) !== 0) {
360
359
  const l = await this.tree.get(ite.leftChild(), false)
361
360
  if (l) {
362
- let size = l.size
363
- if (this.padding > 0) size -= this.padding * ite.countLeaves()
361
+ const size = getUnpaddedSize(l, this.padding, ite)
364
362
 
365
363
  if (size === bytes) return [ite.rightSpan() + 2, 0]
366
364
  if (size > bytes) continue
@@ -875,20 +873,21 @@ function verifyUpgrade ({ fork, upgrade }, blockRoot, batch) {
875
873
  return q.extra === null
876
874
  }
877
875
 
878
- async function seekFromHead (tree, head, bytes) {
876
+ async function seekFromHead (tree, head, bytes, padding) {
879
877
  const roots = flat.fullRoots(head)
880
878
 
881
879
  for (let i = 0; i < roots.length; i++) {
882
880
  const root = roots[i]
883
881
  const node = await tree.get(root)
882
+ const size = getUnpaddedSize(node, padding, null)
884
883
 
885
- if (bytes === node.size) return root
886
- if (bytes > node.size) {
887
- bytes -= node.size
884
+ if (bytes === size) return root
885
+ if (bytes > size) {
886
+ bytes -= size
888
887
  continue
889
888
  }
890
889
 
891
- return seekTrustedTree(tree, root, bytes)
890
+ return seekTrustedTree(tree, root, bytes, padding)
892
891
  }
893
892
 
894
893
  return head
@@ -896,7 +895,7 @@ async function seekFromHead (tree, head, bytes) {
896
895
 
897
896
  // trust that bytes are within the root tree and find the block at bytes
898
897
 
899
- async function seekTrustedTree (tree, root, bytes) {
898
+ async function seekTrustedTree (tree, root, bytes, padding) {
900
899
  if (!bytes) return root
901
900
 
902
901
  const ite = flat.iterator(root)
@@ -904,9 +903,10 @@ async function seekTrustedTree (tree, root, bytes) {
904
903
  while ((ite.index & 1) !== 0) {
905
904
  const l = await tree.get(ite.leftChild(), false)
906
905
  if (l) {
907
- if (l.size === bytes) return ite.index
908
- if (l.size > bytes) continue
909
- bytes -= l.size
906
+ const size = getUnpaddedSize(l, padding, ite)
907
+ if (size === bytes) return ite.index
908
+ if (size > bytes) continue
909
+ bytes -= size
910
910
  ite.sibling()
911
911
  } else {
912
912
  ite.parent()
@@ -919,8 +919,8 @@ async function seekTrustedTree (tree, root, bytes) {
919
919
 
920
920
  // try to find the block at bytes without trusting that is *is* within the root passed
921
921
 
922
- async function seekUntrustedTree (tree, root, bytes) {
923
- const offset = await tree.byteOffset(root)
922
+ async function seekUntrustedTree (tree, root, bytes, padding) {
923
+ const offset = await tree.byteOffset(root) - (padding ? padding * flat.leftSpan(root) / 2 : 0)
924
924
 
925
925
  if (offset > bytes) throw INVALID_OPERATION('Invalid seek')
926
926
  if (offset === bytes) return root
@@ -929,9 +929,9 @@ async function seekUntrustedTree (tree, root, bytes) {
929
929
 
930
930
  const node = await tree.get(root)
931
931
 
932
- if (node.size <= bytes) throw INVALID_OPERATION('Invalid seek')
932
+ if (getUnpaddedSize(node, padding, null) <= bytes) throw INVALID_OPERATION('Invalid seek')
933
933
 
934
- return seekTrustedTree(tree, root, bytes)
934
+ return seekTrustedTree(tree, root, bytes, padding)
935
935
  }
936
936
 
937
937
  // Below is proof production, ie, construct proofs to verify a request
@@ -1212,10 +1212,10 @@ async function generateProof (tree, block, hash, seek, upgrade) {
1212
1212
 
1213
1213
  if (node !== null && (!upgrade || node.lastIndex < upgrade.start)) {
1214
1214
  subTree = nodesToRoot(node.index, node.nodes, to)
1215
- const seekRoot = seek ? await seekUntrustedTree(tree, subTree, seek.bytes) : head
1215
+ const seekRoot = seek ? await seekUntrustedTree(tree, subTree, seek.bytes, seek.padding) : head
1216
1216
  blockAndSeekProof(tree, node, seek, seekRoot, subTree, p)
1217
1217
  } else if ((node || seek) && upgrade) {
1218
- subTree = seek ? await seekFromHead(tree, to, seek.bytes) : node.index
1218
+ subTree = seek ? await seekFromHead(tree, to, seek.bytes, seek.padding) : node.index
1219
1219
  }
1220
1220
 
1221
1221
  if (upgrade) {
@@ -1257,3 +1257,7 @@ async function generateProof (tree, block, hash, seek, upgrade) {
1257
1257
 
1258
1258
  return result
1259
1259
  }
1260
+
1261
+ function getUnpaddedSize (node, padding, ite) {
1262
+ return padding === 0 ? node.size : node.size - padding * (ite ? ite.countLeaves() : flat.countLeaves(node.index))
1263
+ }
package/lib/messages.js CHANGED
@@ -165,16 +165,17 @@ const wire = exports.wire = {}
165
165
 
166
166
  wire.handshake = {
167
167
  preencode (state, m) {
168
- c.uint.preencode(state, 0)
168
+ c.uint.preencode(state, 1)
169
169
  c.fixed32.preencode(state, m.capability)
170
170
  },
171
171
  encode (state, m) {
172
- c.uint.encode(state, 0) // flags
172
+ c.uint.encode(state, m.seeks ? 1 : 0)
173
173
  c.fixed32.encode(state, m.capability)
174
174
  },
175
175
  decode (state) {
176
- c.uint.decode(state) // flags
176
+ const flags = c.uint.decode(state)
177
177
  return {
178
+ seeks: (flags & 1) !== 0,
178
179
  capability: c.fixed32.decode(state)
179
180
  }
180
181
  }
@@ -200,13 +201,16 @@ const requestBlock = {
200
201
  const requestSeek = {
201
202
  preencode (state, s) {
202
203
  c.uint.preencode(state, s.bytes)
204
+ c.uint.preencode(state, s.padding)
203
205
  },
204
206
  encode (state, s) {
205
207
  c.uint.encode(state, s.bytes)
208
+ c.uint.encode(state, s.padding)
206
209
  },
207
210
  decode (state) {
208
211
  return {
209
- bytes: c.uint.decode(state)
212
+ bytes: c.uint.decode(state),
213
+ padding: c.uint.decode(state)
210
214
  }
211
215
  }
212
216
  }
package/lib/replicator.js CHANGED
@@ -269,6 +269,7 @@ class Peer {
269
269
  this.stream = protomux.stream
270
270
  this.protomux = protomux
271
271
  this.remotePublicKey = this.stream.remotePublicKey
272
+ this.remoteSupportsSeeks = false
272
273
 
273
274
  this.paused = false
274
275
 
@@ -389,7 +390,7 @@ class Peer {
389
390
  })
390
391
  }
391
392
 
392
- onopen ({ capability }) {
393
+ onopen ({ seeks, capability }) {
393
394
  const expected = caps.replicate(this.stream.isInitiator === false, this.replicator.key, this.stream.handshakeHash)
394
395
 
395
396
  if (b4a.equals(capability, expected) !== true) { // TODO: change this to a rejection instead, less leakage
@@ -398,6 +399,7 @@ class Peer {
398
399
 
399
400
  if (this.remoteOpened === true) return
400
401
  this.remoteOpened = true
402
+ this.remoteSupportsSeeks = seeks
401
403
 
402
404
  this.protomux.cork()
403
405
 
@@ -811,6 +813,9 @@ class Peer {
811
813
  }
812
814
 
813
815
  _requestSeek (s) {
816
+ // if replicator is updating the seeks etc, bail and wait for it to drain
817
+ if (this.replicator._updatesPending > 0) return false
818
+
814
819
  const { length, fork } = this.core.tree
815
820
 
816
821
  if (fork !== this.remoteFork) return false
@@ -821,7 +826,7 @@ class Peer {
821
826
  // We need an upgrade for the seek, if non can be provided, skip
822
827
  if (req === null) return false
823
828
 
824
- req.seek = { bytes: s.seeker.padding * s.seeker.start + s.seeker.bytes }
829
+ req.seek = this.remoteSupportsSeeks ? { bytes: s.seeker.bytes, padding: s.seeker.padding } : null
825
830
 
826
831
  s.inflight.push(req)
827
832
  this._send(req)
@@ -849,9 +854,10 @@ class Peer {
849
854
  if (h.inflight.length > 0) continue
850
855
 
851
856
  const req = this._makeRequest(false, h.priority)
857
+ const nodes = flatTree.depth(s.seeker.start + s.seeker.end - 1)
852
858
 
853
- req.hash = { index: 2 * index, nodes: 0 }
854
- req.seek = { bytes: s.seeker.padding * s.seeker.start + s.seeker.bytes }
859
+ req.hash = { index: 2 * index, nodes }
860
+ req.seek = this.remoteSupportsSeeks ? { bytes: s.seeker.bytes, padding: s.seeker.padding } : null
855
861
 
856
862
  s.inflight.push(req)
857
863
  h.inflight.push(req)
@@ -1059,23 +1065,30 @@ class Peer {
1059
1065
  this.inflight++
1060
1066
  this.replicator._inflight.add(req)
1061
1067
 
1062
- if (req.upgrade !== null && req.fork === fork) {
1063
- const u = this.replicator._addUpgrade()
1064
- u.inflight.push(req)
1065
- }
1066
-
1067
1068
  try {
1068
1069
  if (req.block !== null && req.fork === fork) {
1069
1070
  req.block.nodes = await this.core.tree.missingNodes(2 * req.block.index)
1070
1071
  }
1071
- if (req.hash !== null && req.fork === fork) {
1072
+ if (req.hash !== null && req.fork === fork && req.hash.nodes === 0) {
1072
1073
  req.hash.nodes = await this.core.tree.missingNodes(req.hash.index)
1074
+
1075
+ // nodes === 0, we already have it, bail
1076
+ if (req.hash.nodes === 0 && (req.hash.index & 1) === 0) {
1077
+ this.inflight--
1078
+ this.replicator._resolveHashLocally(this, req)
1079
+ return
1080
+ }
1073
1081
  }
1074
1082
  } catch (err) {
1075
1083
  this.stream.destroy(err)
1076
1084
  return
1077
1085
  }
1078
1086
 
1087
+ if (req.upgrade !== null && req.fork === fork) {
1088
+ const u = this.replicator._addUpgrade()
1089
+ u.inflight.push(req)
1090
+ }
1091
+
1079
1092
  this.wireRequest.send(req)
1080
1093
  }
1081
1094
  }
@@ -1186,7 +1199,7 @@ module.exports = class Replicator {
1186
1199
  for (const peer of this.peers) peer.signalUpgrade()
1187
1200
  if (this._blocks.isEmpty() === false) this._resolveBlocksLocally()
1188
1201
  if (this._upgrade !== null) this._resolveUpgradeRequest(null)
1189
- if (this._ranges.length !== 0 || this._seeks.length !== 0) this._updateNonPrimary()
1202
+ if (this._ranges.length !== 0 || this._seeks.length !== 0) this._updateNonPrimary(true)
1190
1203
  }
1191
1204
 
1192
1205
  // Called externally when a conflict has been detected and verified
@@ -1270,7 +1283,7 @@ module.exports = class Replicator {
1270
1283
 
1271
1284
  // Trigger this to see if this is already resolved...
1272
1285
  // Also auto compresses the range based on local bitfield
1273
- this._updateNonPrimary()
1286
+ this._updateNonPrimary(true)
1274
1287
 
1275
1288
  return ref
1276
1289
  }
@@ -1420,6 +1433,12 @@ module.exports = class Replicator {
1420
1433
  this._queued.push(b)
1421
1434
  }
1422
1435
 
1436
+ _resolveHashLocally (peer, req) {
1437
+ this._removeInflight(req.id)
1438
+ this._resolveBlockRequest(this._hashes, req.hash.index / 2, null, req)
1439
+ this.updatePeer(peer)
1440
+ }
1441
+
1423
1442
  // Runs in the background - not allowed to throw
1424
1443
  async _resolveBlocksLocally () {
1425
1444
  // TODO: check if fork compat etc. Requires that we pass down truncation info
@@ -1528,7 +1547,7 @@ module.exports = class Replicator {
1528
1547
  }
1529
1548
 
1530
1549
  // "slow" updates here - async but not allowed to ever throw
1531
- async _updateNonPrimary () {
1550
+ async _updateNonPrimary (updateAll) {
1532
1551
  // Check if running, if so skip it and the running one will issue another update for us (debounce)
1533
1552
  while (++this._updatesPending === 1) {
1534
1553
  for (let i = 0; i < this._ranges.length; i++) {
@@ -1564,13 +1583,13 @@ module.exports = class Replicator {
1564
1583
  else s.resolve(res)
1565
1584
  }
1566
1585
 
1567
- if (this._inflight.idle) this.updateAll()
1568
-
1569
- // No additional updates scheduled - return
1570
- if (--this._updatesPending === 0) return
1586
+ // No additional updates scheduled - break
1587
+ if (--this._updatesPending === 0) break
1571
1588
  // Debounce the additional updates - continue
1572
1589
  this._updatesPending = 0
1573
1590
  }
1591
+
1592
+ if (this._inflight.idle || updateAll) this.updateAll()
1574
1593
  }
1575
1594
 
1576
1595
  _maybeResolveIfAvailableRanges () {
@@ -1641,8 +1660,8 @@ module.exports = class Replicator {
1641
1660
  this._manifestPeer = null
1642
1661
  }
1643
1662
 
1644
- if (this._seeks.length > 0 || this._ranges.length > 0) this._updateNonPrimary()
1645
- else this.updatePeer(peer)
1663
+ if (this._seeks.length > 0 || this._ranges.length > 0) this._updateNonPrimary(this._seeks.length > 0)
1664
+ this.updatePeer(peer)
1646
1665
  }
1647
1666
 
1648
1667
  _onwant (peer, start, length) {
@@ -1921,6 +1940,7 @@ module.exports = class Replicator {
1921
1940
  const stream = protomux.stream
1922
1941
 
1923
1942
  peer.channel.open({
1943
+ seeks: true,
1924
1944
  capability: caps.replicate(stream.isInitiator, this.key, stream.handshakeHash)
1925
1945
  })
1926
1946
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.28.11",
3
+ "version": "10.29.1",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {