@decentnetwork/peer 0.1.144 → 0.1.145

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 +63 -5
  2. package/package.json +1 -1
package/dist/peer.js CHANGED
@@ -6873,6 +6873,41 @@ export class Peer {
6873
6873
  async #sendThroughOnionPath(payloadForNodeD, nodeD, pathOffset = 0, forcedPath) {
6874
6874
  const path = forcedPath === "direct" ? undefined : forcedPath ?? this.#selectOnionPath(nodeD, pathOffset);
6875
6875
  if (!path) {
6876
+ // No 3-hop UDP path. "Direct" then meant a plain UDP send — which on a
6877
+ // host with no UDP (a browser, where dgram is a socket that drops) goes
6878
+ // straight into the void, silently, forever. Every announce and every
6879
+ // friend-request lookup died here.
6880
+ //
6881
+ // A RELAYED onion request needs only two hops: the relay is node A. So
6882
+ // try that before giving up, and only fall through to the UDP send when
6883
+ // there are no relays or not even two usable hops.
6884
+ if (this.#tcpRelays && this.#tcpRelays.connectedCount() > 0) {
6885
+ const hops = this.#selectTcpOnionHops(nodeD, pathOffset);
6886
+ if (hops) {
6887
+ try {
6888
+ const tcpPacket = createOnionRequest0Tcp({
6889
+ nodeBHost: hops.nodeB.host,
6890
+ nodeBPort: hops.nodeB.port,
6891
+ nodeBPublicKey: hops.nodeB.publicKey,
6892
+ nodeCHost: hops.nodeC.host,
6893
+ nodeCPort: hops.nodeC.port,
6894
+ nodeCPublicKey: hops.nodeC.publicKey,
6895
+ nodeDHost: nodeD.host,
6896
+ nodeDPort: nodeD.port,
6897
+ payloadForNodeD
6898
+ });
6899
+ const sent = this.#tcpRelays.sendOnionRequest(tcpPacket);
6900
+ if (sent > 0) {
6901
+ this.#diagTcpOnionSent += 1;
6902
+ this.#debugLog(`no 3-hop path for ${nodeD.host}:${nodeD.port} — relayed 2-hop onion via ${sent} relay(s) (B=${hops.nodeB.host} C=${hops.nodeC.host})`);
6903
+ return "direct";
6904
+ }
6905
+ }
6906
+ catch {
6907
+ /* malformed hop key — fall through to the UDP attempt */
6908
+ }
6909
+ }
6910
+ }
6876
6911
  this.#debugLog(`no onion path for ${nodeD.host}:${nodeD.port}, sending direct`);
6877
6912
  await this.#sendPacket(payloadForNodeD, nodeD);
6878
6913
  return "direct";
@@ -6923,7 +6958,10 @@ export class Peer {
6923
6958
  }
6924
6959
  return path;
6925
6960
  }
6926
- #selectOnionPath(nodeD, pathOffset = 0) {
6961
+ /** Ranked hop candidates for a path to nodeD. Shared by the 3-hop UDP
6962
+ * selector and the 2-hop TCP one — a relay stands in for node A, so a
6963
+ * relayed path needs one fewer usable node than a direct one. */
6964
+ #onionCandidatePool(nodeD) {
6927
6965
  const nodeDId = `${nodeD.host}:${nodeD.port}`;
6928
6966
  const candidates = [];
6929
6967
  const bootstrapIds = new Set(this.#opts.bootstrapNodes.map((node) => `${node.host}:${node.port}`));
@@ -6972,13 +7010,33 @@ export class Peer {
6972
7010
  // Ignore bad keys.
6973
7011
  }
6974
7012
  }
6975
- if (candidates.length < 3) {
6976
- return undefined;
6977
- }
6978
7013
  candidates.sort((a, b) => b.score - a.score);
6979
7014
  const preferredRelays = candidates.filter((item) => isLikelyStableRelayPort(item.node.port));
6980
7015
  const healthy = preferredRelays.filter((item) => item.score > -2);
6981
- const pool = healthy.length >= 3 ? healthy : preferredRelays.length >= 3 ? preferredRelays : candidates;
7016
+ return healthy.length >= 3 ? healthy : preferredRelays.length >= 3 ? preferredRelays : candidates;
7017
+ }
7018
+ /** Two hops (B and C) for a relayed onion request. The relay is node A, so
7019
+ * this succeeds on node sets too thin for a direct 3-hop path — which is
7020
+ * the browser's normal state, and where every request used to fall through
7021
+ * to a UDP send that goes nowhere. */
7022
+ #selectTcpOnionHops(nodeD, pathOffset = 0) {
7023
+ const pool = this.#onionCandidatePool(nodeD);
7024
+ if (pool.length < 2)
7025
+ return undefined;
7026
+ const i1 = pathOffset % pool.length;
7027
+ const i2 = (pathOffset + 1) % pool.length;
7028
+ if (i1 === i2)
7029
+ return undefined;
7030
+ const hop = (x) => ({
7031
+ node: x.node, host: x.node.host, port: x.node.port, publicKey: x.publicKey
7032
+ });
7033
+ return { nodeB: hop(pool[i1]), nodeC: hop(pool[i2]) };
7034
+ }
7035
+ #selectOnionPath(nodeD, pathOffset = 0) {
7036
+ const pool = this.#onionCandidatePool(nodeD);
7037
+ if (pool.length < 3) {
7038
+ return undefined;
7039
+ }
6982
7040
  const count = pool.length;
6983
7041
  const i0 = pathOffset % count;
6984
7042
  const i1 = (pathOffset + 1) % count;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/peer",
3
- "version": "0.1.144",
3
+ "version": "0.1.145",
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",