diff --git a/packages/node/meshnet_node/relay_bridge.py b/packages/node/meshnet_node/relay_bridge.py index a66a513..af68e43 100644 --- a/packages/node/meshnet_node/relay_bridge.py +++ b/packages/node/meshnet_node/relay_bridge.py @@ -6,8 +6,10 @@ import base64 import json import logging import os +import re import threading import time +import urllib.parse import urllib.error import urllib.request from concurrent.futures import ThreadPoolExecutor @@ -259,5 +261,30 @@ class RelayHttpBridge: }) -def peer_id_from_wallet(wallet_address: str) -> str: - return wallet_address[:16] if len(wallet_address) >= 16 else wallet_address +def _peer_id_suffix(value: str) -> str: + """Return a relay-safe suffix for a human node name or numeric instance id.""" + suffix = re.sub(r"[^A-Za-z0-9_.-]+", "-", value.strip()).strip("-._") + return suffix[:32] + + +def peer_id_from_wallet( + wallet_address: str, + *, + node_name: str | None = None, + advertised_addr: str | None = None, +) -> str: + """Build a per-node relay peer id from the wallet plus node identity. + + Multiple nodes can legitimately share one wallet for payouts, but the relay + registry is keyed by peer_id. Using only the wallet prefix makes those + nodes overwrite each other at the relay. Prefer the operator-provided node + name; if absent, use the advertised endpoint port as the stable integer + instance suffix (7001, 7002, ... for local multi-node runs). + """ + wallet_prefix = wallet_address[:16] if len(wallet_address) >= 16 else wallet_address + suffix = _peer_id_suffix(node_name or "") if node_name else "" + if not suffix and advertised_addr: + parsed = urllib.parse.urlparse(advertised_addr) + if parsed.port is not None: + suffix = str(parsed.port) + return f"{wallet_prefix}-{suffix}" if suffix else wallet_prefix diff --git a/packages/node/meshnet_node/startup.py b/packages/node/meshnet_node/startup.py index fb02857..31ebf6c 100644 --- a/packages/node/meshnet_node/startup.py +++ b/packages/node/meshnet_node/startup.py @@ -346,11 +346,16 @@ def _start_relay_bridge_if_available( local_base_url: str, advertised_endpoint: str, relay_url: str | None = None, + node_name: str | None = None, ) -> tuple[RelayHttpBridge | None, dict]: relay_url = relay_url or _discover_relay_url(tracker_url) if not relay_url: return None, {} - peer_id = peer_id_from_wallet(wallet_address) + peer_id = peer_id_from_wallet( + wallet_address, + node_name=node_name, + advertised_addr=advertised_endpoint, + ) bridge = RelayHttpBridge( relay_url=relay_url, peer_id=peer_id, @@ -863,6 +868,7 @@ def run_startup( local_base_url, endpoint, relay_url=relay_url, + node_name=node_name, ) _attach_relay_bridge(node, relay_bridge) # Register with tracker so other nodes can auto-join this model. @@ -1015,6 +1021,7 @@ def run_startup( local_base_url, endpoint, relay_url=relay_url, + node_name=node_name, ) _attach_relay_bridge(node, relay_bridge) model_cache_path = _model_cache_path(assigned_hf_repo, cache_dir) @@ -1196,6 +1203,7 @@ def run_startup( local_base_url, endpoint, relay_url=relay_url, + node_name=node_name, ) _attach_relay_bridge(node, relay_bridge) reg_payload = { @@ -1257,6 +1265,7 @@ def run_startup( local_base_url, endpoint, relay_url=relay_url, + node_name=node_name, ) _attach_relay_bridge(node, relay_bridge) diff --git a/tests/test_gossip_and_relay.py b/tests/test_gossip_and_relay.py index 9c272ce..bb6b1ad 100644 --- a/tests/test_gossip_and_relay.py +++ b/tests/test_gossip_and_relay.py @@ -41,6 +41,28 @@ def test_identity_different_for_different_paths(tmp_path): assert a["peer_id"] != b["peer_id"] +def test_relay_peer_id_includes_node_name_for_shared_wallet(): + from meshnet_node.relay_bridge import peer_id_from_wallet + + wallet = "5gMLrmyBYTpkFjmyc4eGwcaWhYquyWgCBFFEqHzR5Qur" + + assert peer_id_from_wallet(wallet, node_name="gpu head") == "5gMLrmyBYTpkFjmy-gpu-head" + assert peer_id_from_wallet(wallet, node_name="cpu-tail") == "5gMLrmyBYTpkFjmy-cpu-tail" + + +def test_relay_peer_id_falls_back_to_endpoint_port_integer(): + from meshnet_node.relay_bridge import peer_id_from_wallet + + wallet = "5gMLrmyBYTpkFjmyc4eGwcaWhYquyWgCBFFEqHzR5Qur" + + first = peer_id_from_wallet(wallet, advertised_addr="http://192.168.0.20:7001") + second = peer_id_from_wallet(wallet, advertised_addr="http://192.168.0.20:7002") + + assert first == "5gMLrmyBYTpkFjmy-7001" + assert second == "5gMLrmyBYTpkFjmy-7002" + assert first != second + + # --------------------------------------------------------------------------- # TLS / certificate tests # ---------------------------------------------------------------------------