@decentnetwork/peer 0.1.124 → 0.1.127
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 +50 -20
- package/package.json +1 -1
package/dist/peer.js
CHANGED
|
@@ -393,8 +393,9 @@ export class Peer {
|
|
|
393
393
|
* same friend, and sharing one timestamp would let whichever ran first
|
|
394
394
|
* suppress the other for a full budget. */
|
|
395
395
|
#onionLookupCooldown = new Map();
|
|
396
|
-
/** Consecutive failed endpoint lookups per (friend,target)
|
|
397
|
-
* the
|
|
396
|
+
/** Consecutive failed endpoint lookups per (friend,target). Kept for
|
|
397
|
+
* diagnostics — it no longer narrows the sweep (see the width comment in
|
|
398
|
+
* #discoverAndCacheFriendEndpoint for why that was reverted). */
|
|
398
399
|
#onionLookupMisses = new Map();
|
|
399
400
|
// Per-friend cooldown for re-asserting the TCP-relay route toward an
|
|
400
401
|
// unconnected friend (the "accepted friend that never connects" wedge —
|
|
@@ -1901,7 +1902,7 @@ export class Peer {
|
|
|
1901
1902
|
this.#debugLog(`cookie response: no tcp relay accepted oob send for ${senderId}`);
|
|
1902
1903
|
}
|
|
1903
1904
|
}
|
|
1904
|
-
else if (this.#isUnroutableSelfSource(remote.address, remote.port)) {
|
|
1905
|
+
else if (this.#isUnroutableSelfSource(remote.address, remote.port, true)) {
|
|
1905
1906
|
// The request arrived sourced from our own TUN address (or our own
|
|
1906
1907
|
// socket) — an overlay routing artifact. Replying there loops into
|
|
1907
1908
|
// ourselves and dies. Answer over the relay OOB path instead, which
|
|
@@ -1996,7 +1997,7 @@ export class Peer {
|
|
|
1996
1997
|
// poison the UDP send-back path.
|
|
1997
1998
|
const fromTcp = this.#remoteIsTcp(remote);
|
|
1998
1999
|
if (!fromTcp) {
|
|
1999
|
-
this.#adoptRemote(matchedSession, remote.address, remote.port);
|
|
2000
|
+
this.#adoptRemote(matchedSession, remote.address, remote.port, true);
|
|
2000
2001
|
}
|
|
2001
2002
|
else {
|
|
2002
2003
|
matchedSession.hasTcpRoute = true;
|
|
@@ -2070,7 +2071,7 @@ export class Peer {
|
|
|
2070
2071
|
state.hasTcpRoute = true;
|
|
2071
2072
|
}
|
|
2072
2073
|
else {
|
|
2073
|
-
this.#adoptRemote(state, remote.address, remote.port);
|
|
2074
|
+
this.#adoptRemote(state, remote.address, remote.port, true);
|
|
2074
2075
|
}
|
|
2075
2076
|
// CONVERGENCE RESYNC: the peer is retransmitting the SAME session pubkey
|
|
2076
2077
|
// we already hold, so OUR view of the session looks fine. But if we've
|
|
@@ -2267,7 +2268,7 @@ export class Peer {
|
|
|
2267
2268
|
// Don't set state.remote — there's no UDP endpoint for this peer.
|
|
2268
2269
|
}
|
|
2269
2270
|
else {
|
|
2270
|
-
this.#adoptRemote(state, remote.address, remote.port);
|
|
2271
|
+
this.#adoptRemote(state, remote.address, remote.port, true);
|
|
2271
2272
|
}
|
|
2272
2273
|
if (isNewSession) {
|
|
2273
2274
|
state.sendPacketNumber = 0;
|
|
@@ -2456,7 +2457,7 @@ export class Peer {
|
|
|
2456
2457
|
}
|
|
2457
2458
|
state.lastRelayRecvMs = Date.now();
|
|
2458
2459
|
}
|
|
2459
|
-
else if (this.#isUnroutableSelfSource(remote.address, remote.port)) {
|
|
2460
|
+
else if (this.#isUnroutableSelfSource(remote.address, remote.port, true)) {
|
|
2460
2461
|
// Sourced from our own TUN address / own socket (overlay routing
|
|
2461
2462
|
// artifact): the payload is real — deliver it — but this is NOT a
|
|
2462
2463
|
// usable bidirectional UDP path. Don't adopt it (replies loop into
|
|
@@ -2469,7 +2470,7 @@ export class Peer {
|
|
|
2469
2470
|
// Tailscale-exit hairpin source. Cheap string compare, NO interface
|
|
2470
2471
|
// syscall (that was the CCTV CPU regression) — safe at CCTV packet
|
|
2471
2472
|
// rates.
|
|
2472
|
-
this.#adoptRemote(state, remote.address, remote.port);
|
|
2473
|
+
this.#adoptRemote(state, remote.address, remote.port, true);
|
|
2473
2474
|
// A successfully decrypted packet arriving from a private address is
|
|
2474
2475
|
// stronger proof than an endpoint hint: it demonstrates that this
|
|
2475
2476
|
// exact host:port already reaches us. Do not require the address to be
|
|
@@ -2484,7 +2485,7 @@ export class Peer {
|
|
|
2484
2485
|
if (!state.lanRemoteHost &&
|
|
2485
2486
|
isPrivateAddress(remote.address) &&
|
|
2486
2487
|
!isCgnatAddress(remote.address) &&
|
|
2487
|
-
!this.#isUnroutableSelfSource(remote.address, remote.port)) {
|
|
2488
|
+
!this.#isUnroutableSelfSource(remote.address, remote.port, true)) {
|
|
2488
2489
|
state.lanRemoteHost = remote.address;
|
|
2489
2490
|
this.#rememberEndpointCandidate(state, remote.address, remote.port);
|
|
2490
2491
|
this.#debugLog(`lan_lock_confirmed friend=${friendId} host=${remote.address}:${remote.port} (decrypted UDP source)`);
|
|
@@ -3114,9 +3115,18 @@ export class Peer {
|
|
|
3114
3115
|
// peer that only just came back is still found without waiting for a
|
|
3115
3116
|
// restart. Widening beats a permanently narrow sweep: the failure we must
|
|
3116
3117
|
// not introduce is "never discovers", not "discovers a bit later".
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3118
|
+
// Width stays FULL. An earlier cut narrowed repeats to 4 nodes and only
|
|
3119
|
+
// went wide every 8th attempt — with the 25s budget that put 200s between
|
|
3120
|
+
// full sweeps, and iOS peers (which come online in short windows and are
|
|
3121
|
+
// the hardest to find at the best of times) stopped being seen at all.
|
|
3122
|
+
// Reported from the field the same day: weili.beagles.eth went offline and
|
|
3123
|
+
// stayed offline.
|
|
3124
|
+
//
|
|
3125
|
+
// Cost here is width x frequency, and the frequency gate above already
|
|
3126
|
+
// took 3s -> 25s, an 8x cut on its own. Narrowing width bought another 6x
|
|
3127
|
+
// and paid for it with the one property this code exists to provide.
|
|
3128
|
+
// Keep the cheap half, drop the expensive one.
|
|
3129
|
+
const width = Math.max(8, Math.min(24, MAX_FRIEND_ROUTE_ATTEMPTS));
|
|
3120
3130
|
const queue = dedupeNodes(this.#knownNodes.length > 0 ? this.#knownNodes : this.#opts.bootstrapNodes)
|
|
3121
3131
|
.filter((n) => !this.#isNodeBlacklisted(`${n.host}:${n.port}`))
|
|
3122
3132
|
.sort((a, b) => this.#nodeScore(`${b.host}:${b.port}`) - this.#nodeScore(`${a.host}:${a.port}`))
|
|
@@ -3186,9 +3196,8 @@ export class Peer {
|
|
|
3186
3196
|
return true;
|
|
3187
3197
|
}
|
|
3188
3198
|
}
|
|
3189
|
-
// Nothing found:
|
|
3190
|
-
|
|
3191
|
-
this.#onionLookupMisses.set(budgetKey, misses + 1);
|
|
3199
|
+
// Nothing found: count it, for diagnostics only.
|
|
3200
|
+
this.#onionLookupMisses.set(budgetKey, (this.#onionLookupMisses.get(budgetKey) ?? 0) + 1);
|
|
3192
3201
|
return false;
|
|
3193
3202
|
}
|
|
3194
3203
|
async #announceSelfBestEffort(force = false, deadlineMs = Number.POSITIVE_INFINITY) {
|
|
@@ -4704,7 +4713,10 @@ export class Peer {
|
|
|
4704
4713
|
}
|
|
4705
4714
|
// An unroutable self-source is never the friend's endpoint (see
|
|
4706
4715
|
// #adoptRemote) — don't let it poison the in-memory record either.
|
|
4707
|
-
|
|
4716
|
+
// observed=false: this path takes SPECULATIVE hints (DHT-PK extras,
|
|
4717
|
+
// sendnodes-derived knownNodes), so a self-address here is a claim, not
|
|
4718
|
+
// proof of a same-host peer.
|
|
4719
|
+
if (this.#isUnroutableSelfSource(host, port, false)) {
|
|
4708
4720
|
return;
|
|
4709
4721
|
}
|
|
4710
4722
|
// Important: do NOT persist this candidate to disk. Speculative endpoint
|
|
@@ -4755,19 +4767,37 @@ export class Peer {
|
|
|
4755
4767
|
* router and loop), or it's literally our own UDP socket. A self
|
|
4756
4768
|
* PHYSICAL address with a DIFFERENT port is routable — that's a
|
|
4757
4769
|
* same-host peer (e.g. the iOS simulator running on this Mac). */
|
|
4758
|
-
#isUnroutableSelfSource(host, port) {
|
|
4770
|
+
#isUnroutableSelfSource(host, port, observed) {
|
|
4759
4771
|
if (isOwnVirtualAddress(host))
|
|
4760
4772
|
return true;
|
|
4761
4773
|
if (!isOwnAddress(host))
|
|
4762
4774
|
return false;
|
|
4763
4775
|
const ourPort = this.#udp?.localPort();
|
|
4764
|
-
|
|
4776
|
+
if (ourPort !== undefined && port === ourPort)
|
|
4777
|
+
return true;
|
|
4778
|
+
// Our own physical IP on a DIFFERENT port is a same-host peer (the iOS
|
|
4779
|
+
// simulator on this Mac) — but only when we actually RECEIVED a packet
|
|
4780
|
+
// from there. A peer merely CLAIMING that address in its candidate list
|
|
4781
|
+
// is not evidence: private ranges are reused everywhere, so two machines
|
|
4782
|
+
// on unrelated networks routinely share one. Seen in the field between
|
|
4783
|
+
// ubuntu-aaec and air-wli, both sitting on a 172.29/20: aaec adopted its
|
|
4784
|
+
// OWN eth0 address as air-wli's endpoint and sent every IP packet to
|
|
4785
|
+
// itself — friend "online", chat fine, ping 100% loss, and not a single
|
|
4786
|
+
// drop recorded because nothing was dropped, just delivered to the wrong
|
|
4787
|
+
// machine.
|
|
4788
|
+
return !observed;
|
|
4765
4789
|
}
|
|
4766
|
-
|
|
4790
|
+
/**
|
|
4791
|
+
* @param observed true when host:port is the source of a datagram we
|
|
4792
|
+
* actually received, false when it is an address the peer advertised.
|
|
4793
|
+
* The distinction matters for self-addresses — see
|
|
4794
|
+
* {@link #isUnroutableSelfSource}.
|
|
4795
|
+
*/
|
|
4796
|
+
#adoptRemote(session, host, port, observed = false) {
|
|
4767
4797
|
// NEVER adopt an unroutable self-source as a friend's remote — replies
|
|
4768
4798
|
// would loop into our own interface and die, the peer never hears back
|
|
4769
4799
|
// and re-handshakes forever.
|
|
4770
|
-
if (this.#isUnroutableSelfSource(host, port)) {
|
|
4800
|
+
if (this.#isUnroutableSelfSource(host, port, observed)) {
|
|
4771
4801
|
return;
|
|
4772
4802
|
}
|
|
4773
4803
|
if (session.lanRemoteHost &&
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/peer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.127",
|
|
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",
|