@decentnetwork/peer 0.1.99 → 0.1.100
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/dist/peer.js +72 -4
- package/package.json +1 -1
package/dist/peer.js
CHANGED
|
@@ -1893,10 +1893,33 @@ export class Peer {
|
|
|
1893
1893
|
// breaker" churned because it accepted new keys with NO such evidence).
|
|
1894
1894
|
const peerReKeyed = state.undecryptableRecvMs !== undefined &&
|
|
1895
1895
|
state.undecryptableRecvMs > (state.lastPingRecvMs ?? 0);
|
|
1896
|
-
|
|
1897
|
-
|
|
1896
|
+
const now = Date.now();
|
|
1897
|
+
const sameRehsKey = state.rehsSessionPublicKey !== undefined &&
|
|
1898
|
+
bytesEqual(state.rehsSessionPublicKey, hs.sessionPublicKey);
|
|
1899
|
+
const rehsWindowExpired = state.rehsLastMs === undefined ||
|
|
1900
|
+
now - state.rehsLastMs > REHS_WINDOW_MS;
|
|
1901
|
+
if (!sameRehsKey || rehsWindowExpired) {
|
|
1902
|
+
state.rehsSessionPublicKey = hs.sessionPublicKey.slice();
|
|
1903
|
+
state.rehsCount = 1;
|
|
1904
|
+
state.rehsFirstMs = now;
|
|
1905
|
+
}
|
|
1906
|
+
else {
|
|
1907
|
+
state.rehsCount = (state.rehsCount ?? 0) + 1;
|
|
1908
|
+
}
|
|
1909
|
+
state.rehsLastMs = now;
|
|
1910
|
+
const rehsElapsed = now - (state.rehsFirstMs ?? now);
|
|
1911
|
+
const persistentRehs = (state.rehsCount ?? 0) >= REHS_ACCEPT_COUNT &&
|
|
1912
|
+
rehsElapsed >= REHS_ACCEPT_MS &&
|
|
1913
|
+
rehsElapsed <= REHS_WINDOW_MS;
|
|
1914
|
+
if (sinceEstablished > 1000 && currentSessionAlive && !peerReKeyed && !persistentRehs) {
|
|
1915
|
+
this.#debugVerboseLog(`hs_recv ignored friend=${friendId} (new session pubkey on live established session, ` +
|
|
1916
|
+
`${sinceEstablished}ms after establish, attempts=${state.rehsCount ?? 0})`);
|
|
1898
1917
|
return;
|
|
1899
1918
|
}
|
|
1919
|
+
if (persistentRehs) {
|
|
1920
|
+
this.#debugLog(`hs_recv accepting persistent re-handshake friend=${friendId} ` +
|
|
1921
|
+
`(attempts=${state.rehsCount}, elapsed=${rehsElapsed}ms)`);
|
|
1922
|
+
}
|
|
1900
1923
|
if (peerReKeyed) {
|
|
1901
1924
|
this.#debugLog(`hs_recv accepting re-handshake friend=${friendId} (peer re-keyed: undecryptable data since last good packet)`);
|
|
1902
1925
|
}
|
|
@@ -1908,6 +1931,10 @@ export class Peer {
|
|
|
1908
1931
|
state.sessionSharedKey = nacl.box.before(hs.sessionPublicKey, state.ourSessionKeyPair.secretKey);
|
|
1909
1932
|
state.established = true;
|
|
1910
1933
|
state.sessionEstablishedAtMs = Date.now();
|
|
1934
|
+
state.rehsCount = undefined;
|
|
1935
|
+
state.rehsFirstMs = undefined;
|
|
1936
|
+
state.rehsLastMs = undefined;
|
|
1937
|
+
state.rehsSessionPublicKey = undefined;
|
|
1911
1938
|
// Fresh session up — reset the higher-pubkey defer window for the
|
|
1912
1939
|
// next outage (see #initiateSession).
|
|
1913
1940
|
this.#initiateDeferSinceMs.delete(friendId);
|
|
@@ -1936,7 +1963,13 @@ export class Peer {
|
|
|
1936
1963
|
state.sendPacketNumber ??= 0;
|
|
1937
1964
|
state.receiveBufferStart ??= 0;
|
|
1938
1965
|
}
|
|
1939
|
-
|
|
1966
|
+
// A handshake authenticates identities and proposes keys, but it does not
|
|
1967
|
+
// prove that CRYPTO_DATA encrypted under those keys works in both
|
|
1968
|
+
// directions. Keep a fresh session "unproven" until the first packet is
|
|
1969
|
+
// actually decrypted; otherwise a half-open session receives the 180s
|
|
1970
|
+
// proven-session blackout grace instead of the 32s recovery deadline.
|
|
1971
|
+
if (isNewSession)
|
|
1972
|
+
state.lastPingRecvMs = undefined;
|
|
1940
1973
|
state.pendingEcho = undefined;
|
|
1941
1974
|
state.pendingCookiePeerDhtPublicKey = undefined;
|
|
1942
1975
|
this.#debugLog(`hs_recv friend=${friendId} initiated=${weInitiated ? 1 : 0} remote=${remote.address}:${remote.port}`);
|
|
@@ -2084,6 +2117,13 @@ export class Peer {
|
|
|
2084
2117
|
// stays unset → udpFresh false → bulk data (files) never ride the LAN
|
|
2085
2118
|
// even when LAN UDP is flowing both ways (the snoopy slow-file bug).
|
|
2086
2119
|
state.lastPingRecvMs = Date.now();
|
|
2120
|
+
// Any successfully decrypted packet proves the current session is
|
|
2121
|
+
// healthy. Forget rejected new-key proposals so late/replayed
|
|
2122
|
+
// handshakes can never accumulate across healthy traffic.
|
|
2123
|
+
state.rehsCount = undefined;
|
|
2124
|
+
state.rehsFirstMs = undefined;
|
|
2125
|
+
state.rehsLastMs = undefined;
|
|
2126
|
+
state.rehsSessionPublicKey = undefined;
|
|
2087
2127
|
if (this.#remoteIsTcp(remote)) {
|
|
2088
2128
|
state.hasTcpRoute = true;
|
|
2089
2129
|
}
|
|
@@ -2932,6 +2972,18 @@ export class Peer {
|
|
|
2932
2972
|
// sessions a long grace so a blackout doesn't churn them via re-handshake;
|
|
2933
2973
|
// unproven sessions still time out fast to self-heal a wedged handshake.
|
|
2934
2974
|
const proven = session.lastPingRecvMs !== undefined;
|
|
2975
|
+
// A proven session normally gets a long blackout grace: if the network
|
|
2976
|
+
// disappears, keeping the known-good keys avoids unnecessary churn.
|
|
2977
|
+
// A restarted peer is different. Once our last good decrypt goes quiet
|
|
2978
|
+
// while fresh packets from that same friend keep failing authentication,
|
|
2979
|
+
// the transport is alive but the keys are not. Waiting the full proven
|
|
2980
|
+
// timeout turns every peer restart into a multi-minute wedge. Require a
|
|
2981
|
+
// sustained good-packet silence plus recent desync evidence so a single
|
|
2982
|
+
// late relay duplicate can never tear down a healthy session.
|
|
2983
|
+
const recentUndecryptable = session.undecryptableRecvMs !== undefined &&
|
|
2984
|
+
session.undecryptableRecvMs > lastAlive &&
|
|
2985
|
+
now - session.undecryptableRecvMs <= REINIT_STUCK_MS;
|
|
2986
|
+
const rekeyStuck = recentUndecryptable && silentFor > REINIT_ON_DESYNC_MS;
|
|
2935
2987
|
// A relay-only session (synthetic tcp: remote, no UDP path) whose
|
|
2936
2988
|
// relay route is gone has NO transport at all — nothing can arrive
|
|
2937
2989
|
// and nothing we send leaves. The proven-session blackout grace
|
|
@@ -2942,7 +2994,23 @@ export class Peer {
|
|
|
2942
2994
|
const relayOnly = !session.remote || session.remote.host.startsWith("tcp:");
|
|
2943
2995
|
const pathless = relayOnly && !session.hasTcpRoute;
|
|
2944
2996
|
const deadline = proven && !pathless ? PROVEN_SESSION_HARD_TIMEOUT_MS : FRIEND_TIMEOUT_MS;
|
|
2945
|
-
if (
|
|
2997
|
+
if (rekeyStuck) {
|
|
2998
|
+
this.#debugLog(`session re-key desync for ${friendId} (no good decrypt in ${silentFor}ms; ` +
|
|
2999
|
+
`undecryptable packets still arriving) — tearing down to re-handshake`);
|
|
3000
|
+
this.#friendSessions.delete(friendId);
|
|
3001
|
+
this.#setFriendOffline(friendId);
|
|
3002
|
+
try {
|
|
3003
|
+
const pk = base58ToBytes(friend.pubkey);
|
|
3004
|
+
if (pk.length === 32)
|
|
3005
|
+
this.#tcpRelays?.requestRoute(pk);
|
|
3006
|
+
const dhtPk = this.#friendDhtKeys.get(friendId);
|
|
3007
|
+
if (dhtPk && dhtPk.length === 32)
|
|
3008
|
+
this.#tcpRelays?.requestRoute(dhtPk);
|
|
3009
|
+
}
|
|
3010
|
+
catch { /* skip malformed */ }
|
|
3011
|
+
continue;
|
|
3012
|
+
}
|
|
3013
|
+
else if (proven && !pathless && silentFor > FRIEND_TIMEOUT_MS && silentFor <= deadline) {
|
|
2946
3014
|
// Blackout grace: keep the proven session and keep probing (the
|
|
2947
3015
|
// keepalive + UDP re-punch + relay-keepalive below all still run). Do
|
|
2948
3016
|
// NOT delete or re-handshake — that's what desyncs and churns. The peer,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/peer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.100",
|
|
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",
|