@decentnetwork/peer 0.1.93 → 0.1.94

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.
Files changed (2) hide show
  1. package/dist/peer.js +18 -4
  2. package/package.json +1 -1
package/dist/peer.js CHANGED
@@ -1760,10 +1760,19 @@ export class Peer {
1760
1760
  const now = Date.now();
1761
1761
  const peerSendingUndecryptable = state.undecryptableRecvMs !== undefined &&
1762
1762
  now - state.undecryptableRecvMs < FRIEND_TIMEOUT_MS;
1763
+ // A duplicate handshake can also be the first packet from a freshly
1764
+ // restarted peer whose KILL notice was lost during shutdown. The
1765
+ // holder still has the peer's old accepted session shell, while the
1766
+ // restarted process needs our handshake reply before it can derive the
1767
+ // shared key. Re-send our CURRENT handshake (same key, so no local
1768
+ // reset) even if we also initiated a cookie chain: after a blackout
1769
+ // both sides commonly initiate at once, so `weInitiated` cannot tell a
1770
+ // request from a reply here. The 2s rate limit bounds duplicate traffic;
1771
+ // the immediate reply cannot bounce because it arrives inside the
1772
+ // limit. This is also the normal lost-reply recovery path.
1763
1773
  const resyncDue = now - (state.lastResyncHandshakeMs ?? 0) > 2000;
1764
- if (peerSendingUndecryptable && resyncDue) {
1774
+ if (resyncDue) {
1765
1775
  state.lastResyncHandshakeMs = now;
1766
- state.lastPingRecvMs = now;
1767
1776
  const reply = createCryptoHandshake({
1768
1777
  recipientCookie: hs.embeddedCookie,
1769
1778
  baseNonce: state.ourBaseNonce,
@@ -1779,11 +1788,16 @@ export class Peer {
1779
1788
  ? Promise.resolve(this.#tcpRelays?.sendOobToFriend(hs.senderDhtPublicKey, reply) ?? 0)
1780
1789
  : this.#sendPacket(reply, { host: remote.address, port: remote.port });
1781
1790
  void sendResync()
1782
- .then(() => this.#debugLog(`hs_recv duplicate but peer sent undecryptable data — re-sent our handshake to resync friend=${friendId}`))
1791
+ .then(() => this.#debugLog(`hs_recv duplicate — re-sent our handshake to resync friend=${friendId} ` +
1792
+ `reason=${peerSendingUndecryptable ? "undecryptable-data" : "duplicate-handshake"}`))
1783
1793
  .catch((error) => this.#debugLog(`resync handshake failed for ${friendId}: ${error.message}`));
1784
1794
  return;
1785
1795
  }
1786
- state.lastPingRecvMs = now;
1796
+ // A handshake proves reachability, not that this net_crypto session can
1797
+ // decrypt data. Refreshing lastPingRecvMs here kept a stale session
1798
+ // immortal: the restarted peer retransmitted handshakes every second,
1799
+ // and each retransmit postponed the 32s dead-session breaker forever.
1800
+ // Only successfully decrypted CRYPTO_DATA may refresh liveness.
1787
1801
  this.#debugVerboseLog(`hs_recv duplicate friend=${friendId} remote=${remote.address}:${remote.port} (session preserved)`);
1788
1802
  return;
1789
1803
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/peer",
3
- "version": "0.1.93",
3
+ "version": "0.1.94",
4
4
  "description": "Pure TypeScript port of Elastos Carrier (toxcore-derived) P2P messaging. DHT, onion routing, TCP relay, FlatBuffers app payloads, Express offline relay. Wire-compatible with iOS Beagle and the Carrier C SDK.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",