@decentnetwork/peer 0.1.4 → 0.1.6
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.d.ts +25 -0
- package/dist/peer.js +93 -5
- package/package.json +1 -1
package/dist/peer.d.ts
CHANGED
|
@@ -13,6 +13,31 @@ export declare class Peer {
|
|
|
13
13
|
joinNetwork(): Promise<BootstrapResult>;
|
|
14
14
|
lookup(pubkey: string): Promise<LookupResult>;
|
|
15
15
|
announceSelf(timeoutMs?: number): Promise<NetworkNode[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Aggregate DHT health snapshot — surfaces the layers that have to
|
|
18
|
+
* work for UDP holepunch / route discovery to succeed.
|
|
19
|
+
* bootstrapsConfigured – nodes the SDK is allowed to talk to
|
|
20
|
+
* knownNodesCount – nodes discovered or persisted in our DHT view
|
|
21
|
+
* lastSelfAnnounceMs – last time we ran a self-announce sweep
|
|
22
|
+
* selfAnnounceStoredOn – how many nodes acknowledged STORING our
|
|
23
|
+
* announce last time. **If 0, our outbound
|
|
24
|
+
* DHT is broken** — peers can't find us,
|
|
25
|
+
* #discoverFriendRoutes returns nothing,
|
|
26
|
+
* UDP holepunch is impossible. Most common
|
|
27
|
+
* failure mode when sessions stay on
|
|
28
|
+
* tcp-relay forever.
|
|
29
|
+
* udpLocalPort – the OS-assigned UDP port (null = no socket)
|
|
30
|
+
* tcpRelayConnected – fallback relay path; if >0, sessions get
|
|
31
|
+
* through even when DHT is dead.
|
|
32
|
+
*/
|
|
33
|
+
dhtHealth(): {
|
|
34
|
+
bootstrapsConfigured: number;
|
|
35
|
+
knownNodesCount: number;
|
|
36
|
+
lastSelfAnnounceMs: number;
|
|
37
|
+
selfAnnounceStoredOn: number;
|
|
38
|
+
udpLocalPort: number | null;
|
|
39
|
+
tcpRelayConnected: number;
|
|
40
|
+
};
|
|
16
41
|
addKnownNodes(nodes: NetworkNode[]): void;
|
|
17
42
|
knownNodes(): NetworkNode[];
|
|
18
43
|
sendFriendRequest(pubkey: string, hello?: string): Promise<void>;
|
package/dist/peer.js
CHANGED
|
@@ -434,6 +434,33 @@ export class Peer {
|
|
|
434
434
|
async announceSelf(timeoutMs = 15000) {
|
|
435
435
|
return this.#runSelfAnnounce(true, Date.now() + timeoutMs);
|
|
436
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* Aggregate DHT health snapshot — surfaces the layers that have to
|
|
439
|
+
* work for UDP holepunch / route discovery to succeed.
|
|
440
|
+
* bootstrapsConfigured – nodes the SDK is allowed to talk to
|
|
441
|
+
* knownNodesCount – nodes discovered or persisted in our DHT view
|
|
442
|
+
* lastSelfAnnounceMs – last time we ran a self-announce sweep
|
|
443
|
+
* selfAnnounceStoredOn – how many nodes acknowledged STORING our
|
|
444
|
+
* announce last time. **If 0, our outbound
|
|
445
|
+
* DHT is broken** — peers can't find us,
|
|
446
|
+
* #discoverFriendRoutes returns nothing,
|
|
447
|
+
* UDP holepunch is impossible. Most common
|
|
448
|
+
* failure mode when sessions stay on
|
|
449
|
+
* tcp-relay forever.
|
|
450
|
+
* udpLocalPort – the OS-assigned UDP port (null = no socket)
|
|
451
|
+
* tcpRelayConnected – fallback relay path; if >0, sessions get
|
|
452
|
+
* through even when DHT is dead.
|
|
453
|
+
*/
|
|
454
|
+
dhtHealth() {
|
|
455
|
+
return {
|
|
456
|
+
bootstrapsConfigured: this.#opts.bootstrapNodes.length,
|
|
457
|
+
knownNodesCount: this.#knownNodes.length,
|
|
458
|
+
lastSelfAnnounceMs: this.#lastSelfAnnounceMs,
|
|
459
|
+
selfAnnounceStoredOn: this.#lastSelfAnnounceStoredCount,
|
|
460
|
+
udpLocalPort: this.#udp?.localPort() ?? null,
|
|
461
|
+
tcpRelayConnected: this.#tcpRelays?.connectedRelays(99).length ?? 0,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
437
464
|
addKnownNodes(nodes) {
|
|
438
465
|
this.#knownNodes = dedupeNodes([...nodes, ...this.#knownNodes]);
|
|
439
466
|
}
|
|
@@ -2788,10 +2815,73 @@ export class Peer {
|
|
|
2788
2815
|
// residential ISPs where self-announce doesn't propagate).
|
|
2789
2816
|
// Mirrors toxcore's send_dht_pk_packet which calls
|
|
2790
2817
|
// tcp_copy_connected_relays to fill extras.
|
|
2791
|
-
|
|
2818
|
+
// Also pack OUR OWN UDP endpoint(s) as extras (family 0x02 =
|
|
2819
|
+
// UDP_FAMILY_IPV4). Without this, peers receiving our DHT-PK push
|
|
2820
|
+
// learn our TCP relays but have NO IDEA where to send UDP cookie
|
|
2821
|
+
// requests — so they never even try UDP holepunch and every
|
|
2822
|
+
// session stays on tcp-relay (~500ms+ RTT) instead of the
|
|
2823
|
+
// ~80ms UDP path. Observed in the wild: even same-LAN peers
|
|
2824
|
+
// (snoopy and mac-dev on the same /24) stuck on tcp-relay
|
|
2825
|
+
// forever because neither side ever announces a UDP endpoint
|
|
2826
|
+
// to the other.
|
|
2827
|
+
//
|
|
2828
|
+
// For peers behind NAT, the LAN address we advertise is
|
|
2829
|
+
// reachable by same-LAN peers and harmless for WAN peers
|
|
2830
|
+
// (they fall through to tcp-relay as before). For peers with
|
|
2831
|
+
// a public IP (e.g. a Vultr VPS), the LAN address IS the
|
|
2832
|
+
// reachable one; same-format works. STUN-style reflexive
|
|
2833
|
+
// discovery is the next layer but isn't required to fix the
|
|
2834
|
+
// LAN case immediately.
|
|
2835
|
+
const packed = [];
|
|
2836
|
+
const ourUdpPort = this.#udp?.localPort();
|
|
2837
|
+
if (ourUdpPort && ourUdpPort > 0) {
|
|
2838
|
+
const ourDhtPk = this.#keyPair?.publicKey;
|
|
2839
|
+
if (ourDhtPk && ourDhtPk.length === 32) {
|
|
2840
|
+
// Enumerate non-loopback IPv4 addresses on this host.
|
|
2841
|
+
// We use os.networkInterfaces() rather than guessing — this
|
|
2842
|
+
// covers a multi-homed peer with several reachable IPs.
|
|
2843
|
+
try {
|
|
2844
|
+
const ifaces = (await import("os")).networkInterfaces();
|
|
2845
|
+
const seenIps = new Set();
|
|
2846
|
+
for (const iface of Object.values(ifaces)) {
|
|
2847
|
+
if (!iface)
|
|
2848
|
+
continue;
|
|
2849
|
+
for (const addr of iface) {
|
|
2850
|
+
if (addr.family !== "IPv4")
|
|
2851
|
+
continue;
|
|
2852
|
+
if (addr.internal)
|
|
2853
|
+
continue;
|
|
2854
|
+
if (addr.address === "0.0.0.0")
|
|
2855
|
+
continue;
|
|
2856
|
+
if (seenIps.has(addr.address))
|
|
2857
|
+
continue;
|
|
2858
|
+
seenIps.add(addr.address);
|
|
2859
|
+
const parts = addr.address
|
|
2860
|
+
.split(".")
|
|
2861
|
+
.map((p) => Number.parseInt(p, 10));
|
|
2862
|
+
if (parts.length !== 4 || parts.some((n) => !(n >= 0 && n <= 255)))
|
|
2863
|
+
continue;
|
|
2864
|
+
const entry = new Uint8Array(1 + 4 + 2 + 32);
|
|
2865
|
+
entry[0] = 0x02; // UDP_FAMILY_IPV4
|
|
2866
|
+
entry[1] = parts[0];
|
|
2867
|
+
entry[2] = parts[1];
|
|
2868
|
+
entry[3] = parts[2];
|
|
2869
|
+
entry[4] = parts[3];
|
|
2870
|
+
entry[5] = (ourUdpPort >> 8) & 0xff;
|
|
2871
|
+
entry[6] = ourUdpPort & 0xff;
|
|
2872
|
+
entry.set(ourDhtPk, 7);
|
|
2873
|
+
packed.push(entry);
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
catch {
|
|
2878
|
+
// os import or interface enumeration failed; skip UDP extras
|
|
2879
|
+
// and let TCP relay carry the session as before.
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2792
2883
|
if (this.#tcpRelays) {
|
|
2793
2884
|
const relays = this.#tcpRelays.connectedRelays(3); // MAX_SHARED_RELAYS
|
|
2794
|
-
const packed = [];
|
|
2795
2885
|
for (const r of relays) {
|
|
2796
2886
|
// Only IPv4 TCP entries supported here; matches what we parse.
|
|
2797
2887
|
// Format per packed-nodes spec: family(1) + ipv4(4) + port(2 BE) + pk(32)
|
|
@@ -2809,10 +2899,8 @@ export class Peer {
|
|
|
2809
2899
|
entry.set(r.serverPublicKey, 7);
|
|
2810
2900
|
packed.push(entry);
|
|
2811
2901
|
}
|
|
2812
|
-
if (packed.length > 0) {
|
|
2813
|
-
extras = concatBytes(packed);
|
|
2814
|
-
}
|
|
2815
2902
|
}
|
|
2903
|
+
const extras = packed.length > 0 ? concatBytes(packed) : new Uint8Array(0);
|
|
2816
2904
|
const innerPayload = concatBytes([
|
|
2817
2905
|
noReplayBytes,
|
|
2818
2906
|
this.#keyPair.publicKey,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/peer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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",
|