From 87550113a4d2caf1cec6ae8c60269090a0794ccd Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Tue, 2 Sep 2025 03:00:18 +0530 Subject: [PATCH 01/17] chore: Update network address config to use loopback (127.0.0.1) instead of wildcard (0.0.0.0) across multiple examples and utilities. --- examples/advanced/network_discover.py | 4 ++-- examples/bootstrap/bootstrap.py | 2 +- examples/chat/chat.py | 2 +- examples/doc-examples/example_encryption_insecure.py | 2 +- examples/doc-examples/example_encryption_noise.py | 2 +- examples/doc-examples/example_encryption_secio.py | 2 +- examples/doc-examples/example_multiplexer.py | 2 +- examples/doc-examples/example_net_stream.py | 2 +- examples/doc-examples/example_peer_discovery.py | 2 +- examples/doc-examples/example_running.py | 2 +- examples/doc-examples/example_transport.py | 2 +- examples/identify/identify.py | 7 +++---- examples/identify_push/identify_push_listener_dialer.py | 4 ++-- examples/mDNS/mDNS.py | 2 +- examples/ping/ping.py | 2 +- examples/pubsub/pubsub.py | 2 +- examples/random_walk/random_walk.py | 4 ++-- libp2p/utils/address_validation.py | 6 +++--- 18 files changed, 25 insertions(+), 26 deletions(-) diff --git a/examples/advanced/network_discover.py b/examples/advanced/network_discover.py index 87b44ddf3..71edd2094 100644 --- a/examples/advanced/network_discover.py +++ b/examples/advanced/network_discover.py @@ -18,7 +18,7 @@ except ImportError: # Fallbacks if utilities are missing def get_available_interfaces(port: int, protocol: str = "tcp"): - return [Multiaddr(f"/ip4/0.0.0.0/{protocol}/{port}")] + return [Multiaddr(f"/ip4/127.0.0.1/{protocol}/{port}")] def expand_wildcard_address(addr: Multiaddr, port: int | None = None): if port is None: @@ -27,7 +27,7 @@ def expand_wildcard_address(addr: Multiaddr, port: int | None = None): return [Multiaddr(addr_str + f"/{port}")] def get_optimal_binding_address(port: int, protocol: str = "tcp"): - return Multiaddr(f"/ip4/0.0.0.0/{protocol}/{port}") + return Multiaddr(f"/ip4/127.0.0.1/{protocol}/{port}") def main() -> None: diff --git a/examples/bootstrap/bootstrap.py b/examples/bootstrap/bootstrap.py index af7d08cc9..93a6913af 100644 --- a/examples/bootstrap/bootstrap.py +++ b/examples/bootstrap/bootstrap.py @@ -59,7 +59,7 @@ async def run(port: int, bootstrap_addrs: list[str]) -> None: key_pair = create_new_key_pair(secret) # Create listen address - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") # Register peer discovery handler peerDiscovery.register_peer_discovered_handler(on_peer_discovery) diff --git a/examples/chat/chat.py b/examples/chat/chat.py index 05a9b918d..c06e20a77 100755 --- a/examples/chat/chat.py +++ b/examples/chat/chat.py @@ -40,7 +40,7 @@ async def write_data(stream: INetStream) -> None: async def run(port: int, destination: str) -> None: - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") host = new_host() async with host.run(listen_addrs=[listen_addr]), trio.open_nursery() as nursery: # Start the peer-store cleanup task diff --git a/examples/doc-examples/example_encryption_insecure.py b/examples/doc-examples/example_encryption_insecure.py index c15368080..089fb72fd 100644 --- a/examples/doc-examples/example_encryption_insecure.py +++ b/examples/doc-examples/example_encryption_insecure.py @@ -40,7 +40,7 @@ async def main(): # Configure the listening address port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") # Start the host async with host.run(listen_addrs=[listen_addr]): diff --git a/examples/doc-examples/example_encryption_noise.py b/examples/doc-examples/example_encryption_noise.py index a2a4318c5..7d037610b 100644 --- a/examples/doc-examples/example_encryption_noise.py +++ b/examples/doc-examples/example_encryption_noise.py @@ -41,7 +41,7 @@ async def main(): # Configure the listening address port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") # Start the host async with host.run(listen_addrs=[listen_addr]): diff --git a/examples/doc-examples/example_encryption_secio.py b/examples/doc-examples/example_encryption_secio.py index 603ad6ea0..3b1cb4056 100644 --- a/examples/doc-examples/example_encryption_secio.py +++ b/examples/doc-examples/example_encryption_secio.py @@ -34,7 +34,7 @@ async def main(): # Configure the listening address port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") # Start the host async with host.run(listen_addrs=[listen_addr]): diff --git a/examples/doc-examples/example_multiplexer.py b/examples/doc-examples/example_multiplexer.py index 0d6f26622..6963ace01 100644 --- a/examples/doc-examples/example_multiplexer.py +++ b/examples/doc-examples/example_multiplexer.py @@ -41,7 +41,7 @@ async def main(): # Configure the listening address port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") # Start the host async with host.run(listen_addrs=[listen_addr]): diff --git a/examples/doc-examples/example_net_stream.py b/examples/doc-examples/example_net_stream.py index d8842beab..a77a75094 100644 --- a/examples/doc-examples/example_net_stream.py +++ b/examples/doc-examples/example_net_stream.py @@ -173,7 +173,7 @@ async def run_enhanced_demo( """ Run enhanced echo demo with NetStream state management. """ - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") # Generate or use provided key if seed: diff --git a/examples/doc-examples/example_peer_discovery.py b/examples/doc-examples/example_peer_discovery.py index 7ceec3754..eb3e19145 100644 --- a/examples/doc-examples/example_peer_discovery.py +++ b/examples/doc-examples/example_peer_discovery.py @@ -44,7 +44,7 @@ async def main(): # Configure the listening address port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") # Start the host async with host.run(listen_addrs=[listen_addr]): diff --git a/examples/doc-examples/example_running.py b/examples/doc-examples/example_running.py index a01699310..7f3ade321 100644 --- a/examples/doc-examples/example_running.py +++ b/examples/doc-examples/example_running.py @@ -41,7 +41,7 @@ async def main(): # Configure the listening address port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") # Start the host async with host.run(listen_addrs=[listen_addr]): diff --git a/examples/doc-examples/example_transport.py b/examples/doc-examples/example_transport.py index e981fa7d1..8f4c9fa10 100644 --- a/examples/doc-examples/example_transport.py +++ b/examples/doc-examples/example_transport.py @@ -21,7 +21,7 @@ async def main(): # Configure the listening address port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") # Start the host async with host.run(listen_addrs=[listen_addr]): diff --git a/examples/identify/identify.py b/examples/identify/identify.py index 98980f99f..445962c38 100644 --- a/examples/identify/identify.py +++ b/examples/identify/identify.py @@ -58,7 +58,7 @@ def print_identify_response(identify_response: Identify): async def run(port: int, destination: str, use_varint_format: bool = True) -> None: - localhost_ip = "0.0.0.0" + localhost_ip = "127.0.0.1" if not destination: # Create first host (listener) @@ -79,10 +79,9 @@ async def run(port: int, destination: str, use_varint_format: bool = True) -> No # Start the peer-store cleanup task nursery.start_soon(host_a.get_peerstore().start_cleanup_task, 60) - # Get the actual address and replace 0.0.0.0 with 127.0.0.1 for client - # connections + # Get the actual address server_addr = str(host_a.get_addrs()[0]) - client_addr = server_addr.replace("/ip4/0.0.0.0/", "/ip4/127.0.0.1/") + client_addr = server_addr format_name = "length-prefixed" if use_varint_format else "raw protobuf" format_flag = "--raw-format" if not use_varint_format else "" diff --git a/examples/identify_push/identify_push_listener_dialer.py b/examples/identify_push/identify_push_listener_dialer.py index c23e62bb4..a9974b82b 100644 --- a/examples/identify_push/identify_push_listener_dialer.py +++ b/examples/identify_push/identify_push_listener_dialer.py @@ -216,7 +216,7 @@ async def run_listener( ) # Start listening - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") try: async with host.run([listen_addr]): @@ -275,7 +275,7 @@ async def run_dialer( ) # Start listening on a different port - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") async with host.run([listen_addr]): logger.info("Dialer host ready!") diff --git a/examples/mDNS/mDNS.py b/examples/mDNS/mDNS.py index d3f11b567..499ca2248 100644 --- a/examples/mDNS/mDNS.py +++ b/examples/mDNS/mDNS.py @@ -33,7 +33,7 @@ def onPeerDiscovery(peerinfo: PeerInfo): async def run(port: int) -> None: secret = secrets.token_bytes(32) key_pair = create_new_key_pair(secret) - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") peerDiscovery.register_peer_discovered_handler(onPeerDiscovery) diff --git a/examples/ping/ping.py b/examples/ping/ping.py index d1a5daae4..bb47bd95d 100644 --- a/examples/ping/ping.py +++ b/examples/ping/ping.py @@ -55,7 +55,7 @@ async def send_ping(stream: INetStream) -> None: async def run(port: int, destination: str) -> None: - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") host = new_host(listen_addrs=[listen_addr]) async with host.run(listen_addrs=[listen_addr]), trio.open_nursery() as nursery: diff --git a/examples/pubsub/pubsub.py b/examples/pubsub/pubsub.py index 41545658d..843a28292 100644 --- a/examples/pubsub/pubsub.py +++ b/examples/pubsub/pubsub.py @@ -109,7 +109,7 @@ async def run(topic: str, destination: str | None, port: int | None) -> None: port = find_free_port() logger.info(f"Using random available port: {port}") - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") # Create a new libp2p host host = new_host( diff --git a/examples/random_walk/random_walk.py b/examples/random_walk/random_walk.py index 845ccd57b..b90d63041 100644 --- a/examples/random_walk/random_walk.py +++ b/examples/random_walk/random_walk.py @@ -130,7 +130,7 @@ async def run_node(port: int, mode: str, demo_interval: int = 30) -> None: # Create host and DHT key_pair = create_new_key_pair(secrets.token_bytes(32)) host = new_host(key_pair=key_pair, bootstrap=DEFAULT_BOOTSTRAP_NODES) - listen_addr = Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + listen_addr = Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") async with host.run(listen_addrs=[listen_addr]), trio.open_nursery() as nursery: # Start maintenance tasks @@ -139,7 +139,7 @@ async def run_node(port: int, mode: str, demo_interval: int = 30) -> None: peer_id = host.get_id().pretty() logger.info(f"Node peer ID: {peer_id}") - logger.info(f"Node address: /ip4/0.0.0.0/tcp/{port}/p2p/{peer_id}") + logger.info(f"Node address: /ip4/127.0.0.1/tcp/{port}/p2p/{peer_id}") # Create and start DHT with Random Walk enabled dht = KadDHT(host, dht_mode, enable_random_walk=True) diff --git a/libp2p/utils/address_validation.py b/libp2p/utils/address_validation.py index 77b797a19..106772412 100644 --- a/libp2p/utils/address_validation.py +++ b/libp2p/utils/address_validation.py @@ -99,7 +99,7 @@ def get_available_interfaces(port: int, protocol: str = "tcp") -> list[Multiaddr # Fallback if nothing discovered if not addrs: - addrs.append(Multiaddr(f"/ip4/0.0.0.0/{protocol}/{port}")) + addrs.append(Multiaddr(f"/ip4/127.0.0.1/{protocol}/{port}")) return addrs @@ -148,8 +148,8 @@ def is_non_loopback(ma: Multiaddr) -> bool: if "/ip4/127." in str(c) or "/ip6/::1" in str(c): return c - # As a final fallback, produce a wildcard - return Multiaddr(f"/ip4/0.0.0.0/{protocol}/{port}") + # As a final fallback, produce a loopback address + return Multiaddr(f"/ip4/127.0.0.1/{protocol}/{port}") __all__ = [ From 68af8766e290da3605de43902f0c781206ff4d53 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Tue, 2 Sep 2025 03:01:16 +0530 Subject: [PATCH 02/17] doc: Update examples documentation files for use of loopback (127.0.0.1) --- docs/examples.circuit_relay.rst | 12 ++++++------ docs/examples.identify.rst | 8 ++++---- docs/examples.identify_push.rst | 8 ++++---- docs/examples.pubsub.rst | 4 ++-- docs/examples.random_walk.rst | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/examples.circuit_relay.rst b/docs/examples.circuit_relay.rst index 2a14c3c51..85326b005 100644 --- a/docs/examples.circuit_relay.rst +++ b/docs/examples.circuit_relay.rst @@ -41,7 +41,7 @@ Create a file named ``relay_node.py`` with the following content: logger = logging.getLogger("relay_node") async def run_relay(): - listen_addr = multiaddr.Multiaddr("/ip4/0.0.0.0/tcp/9000") + listen_addr = multiaddr.Multiaddr("/ip4/127.0.0.1/tcp/9000") host = new_host() config = RelayConfig( @@ -139,7 +139,7 @@ Create a file named ``destination_node.py`` with the following content: Run a simple destination node that accepts connections. This is a simplified version that doesn't use the relay functionality. """ - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/9001") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/9001") host = new_host() # Configure as a relay receiver (stop) @@ -259,7 +259,7 @@ Create a file named ``source_node.py`` with the following content: async def run_source(relay_peer_id=None, destination_peer_id=None): # Create a libp2p host - listen_addr = multiaddr.Multiaddr("/ip4/0.0.0.0/tcp/9002") + listen_addr = multiaddr.Multiaddr("/ip4/127.0.0.1/tcp/9002") host = new_host() # Configure as a relay client @@ -428,7 +428,7 @@ Running the Example Relay node multiaddr: /ip4/127.0.0.1/tcp/9000/p2p/QmaUigQJ9nJERa6GaZuyfaiX91QjYwoQJ46JS3k7ys7SLx ================================================== - Listening on: [] + Listening on: [] Protocol service started Relay service started successfully Relay limits: RelayLimits(duration=3600, data=10485760, max_circuit_conns=8, max_reservations=4) @@ -447,7 +447,7 @@ Running the Example Use this ID in the source node: QmPBr38KeQG2ibyL4fxq6yJWpfoVNCqJMHBdNyn1Qe4h5s ================================================== - Listening on: [] + Listening on: [] Registered echo protocol handler Protocol service started Transport created @@ -469,7 +469,7 @@ Running the Example $ python source_node.py Source node started with ID: QmPyM56cgmFoHTgvMgGfDWRdVRQznmxCDDDg2dJ8ygVXj3 - Listening on: [] + Listening on: [] Protocol service started No relay peer ID provided. Please enter the relay\'s peer ID: Enter relay peer ID: QmaUigQJ9nJERa6GaZuyfaiX91QjYwoQJ46JS3k7ys7SLx diff --git a/docs/examples.identify.rst b/docs/examples.identify.rst index 9623f112f..ba3e13c43 100644 --- a/docs/examples.identify.rst +++ b/docs/examples.identify.rst @@ -12,7 +12,7 @@ This example demonstrates how to use the libp2p ``identify`` protocol. $ identify-demo First host listening. Run this from another console: - identify-demo -p 8889 -d /ip4/0.0.0.0/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM + identify-demo -p 8889 -d /ip4/127.0.0.1/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM Waiting for incoming identify request... @@ -21,13 +21,13 @@ folder and paste it in: .. code-block:: console - $ identify-demo -p 8889 -d /ip4/0.0.0.0/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM - dialer (host_b) listening on /ip4/0.0.0.0/tcp/8889 + $ identify-demo -p 8889 -d /ip4/127.0.0.1/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM + dialer (host_b) listening on /ip4/127.0.0.1/tcp/8889 Second host connecting to peer: QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM Starting identify protocol... Identify response: Public Key (Base64): CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDC6c/oNPP9X13NDQ3Xrlp3zOj+ErXIWb/A4JGwWchiDBwMhMslEX3ct8CqI0BqUYKuwdFjowqqopOJ3cS2MlqtGaiP6Dg9bvGqSDoD37BpNaRVNcebRxtB0nam9SQy3PYLbHAmz0vR4ToSiL9OLRORnGOxCtHBuR8ZZ5vS0JEni8eQMpNa7IuXwyStnuty/QjugOZudBNgYSr8+9gH722KTjput5IRL7BrpIdd4HNXGVRm4b9BjNowvHu404x3a/ifeNblpy/FbYyFJEW0looygKF7hpRHhRbRKIDZt2BqOfT1sFkbqsHE85oY859+VMzP61YELgvGwai2r7KcjkW/AgMBAAE= - Listen Addresses: ['/ip4/0.0.0.0/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM'] + Listen Addresses: ['/ip4/127.0.0.1/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM'] Protocols: ['/ipfs/id/1.0.0', '/ipfs/ping/1.0.0'] Observed Address: ['/ip4/127.0.0.1/tcp/38082'] Protocol Version: ipfs/0.1.0 diff --git a/docs/examples.identify_push.rst b/docs/examples.identify_push.rst index 5b217d38f..614d37bd7 100644 --- a/docs/examples.identify_push.rst +++ b/docs/examples.identify_push.rst @@ -34,11 +34,11 @@ There is also a more interactive version of the example which runs as separate l ==== Starting Identify-Push Listener on port 8888 ==== Listener host ready! - Listening on: /ip4/0.0.0.0/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM + Listening on: /ip4/127.0.0.1/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM Peer ID: QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM Run dialer with command: - identify-push-listener-dialer-demo -d /ip4/0.0.0.0/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM + identify-push-listener-dialer-demo -d /ip4/127.0.0.1/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM Waiting for incoming connections... (Ctrl+C to exit) @@ -47,12 +47,12 @@ folder and paste it in: .. code-block:: console - $ identify-push-listener-dialer-demo -d /ip4/0.0.0.0/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM + $ identify-push-listener-dialer-demo -d /ip4/127.0.0.1/tcp/8888/p2p/QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM ==== Starting Identify-Push Dialer on port 8889 ==== Dialer host ready! - Listening on: /ip4/0.0.0.0/tcp/8889/p2p/QmZyXwVuTaBcDeRsSkJpOpWrSt + Listening on: /ip4/127.0.0.1/tcp/8889/p2p/QmZyXwVuTaBcDeRsSkJpOpWrSt Connecting to peer: QmUiN4R3fNrCoQugGgmmb3v35neMEjKFNrsbNGVDsRHWpM Successfully connected to listener! diff --git a/docs/examples.pubsub.rst b/docs/examples.pubsub.rst index f3a8500f1..8990e5c08 100644 --- a/docs/examples.pubsub.rst +++ b/docs/examples.pubsub.rst @@ -15,7 +15,7 @@ This example demonstrates how to create a chat application using libp2p's PubSub 2025-04-06 23:59:17,471 - pubsub-demo - INFO - Your selected topic is: pubsub-chat 2025-04-06 23:59:17,472 - pubsub-demo - INFO - Using random available port: 33269 2025-04-06 23:59:17,490 - pubsub-demo - INFO - Node started with peer ID: QmcJnocH1d1tz3Zp4MotVDjNfNFawXHw2dpB9tMYGTXJp7 - 2025-04-06 23:59:17,490 - pubsub-demo - INFO - Listening on: /ip4/0.0.0.0/tcp/33269 + 2025-04-06 23:59:17,490 - pubsub-demo - INFO - Listening on: /ip4/127.0.0.1/tcp/33269 2025-04-06 23:59:17,490 - pubsub-demo - INFO - Initializing PubSub and GossipSub... 2025-04-06 23:59:17,491 - pubsub-demo - INFO - Pubsub and GossipSub services started. 2025-04-06 23:59:17,491 - pubsub-demo - INFO - Pubsub ready. @@ -35,7 +35,7 @@ Copy the line that starts with ``pubsub-demo -d``, open a new terminal and paste 2025-04-07 00:00:59,846 - pubsub-demo - INFO - Your selected topic is: pubsub-chat 2025-04-07 00:00:59,846 - pubsub-demo - INFO - Using random available port: 51977 2025-04-07 00:00:59,864 - pubsub-demo - INFO - Node started with peer ID: QmYQKCm95Ut1aXsjHmWVYqdaVbno1eKTYC8KbEVjqUaKaQ - 2025-04-07 00:00:59,864 - pubsub-demo - INFO - Listening on: /ip4/0.0.0.0/tcp/51977 + 2025-04-07 00:00:59,864 - pubsub-demo - INFO - Listening on: /ip4/127.0.0.1/tcp/51977 2025-04-07 00:00:59,864 - pubsub-demo - INFO - Initializing PubSub and GossipSub... 2025-04-07 00:00:59,864 - pubsub-demo - INFO - Pubsub and GossipSub services started. 2025-04-07 00:00:59,865 - pubsub-demo - INFO - Pubsub ready. diff --git a/docs/examples.random_walk.rst b/docs/examples.random_walk.rst index baa3f81f2..ea9ea220b 100644 --- a/docs/examples.random_walk.rst +++ b/docs/examples.random_walk.rst @@ -23,7 +23,7 @@ The Random Walk implementation performs the following key operations: 2025-08-12 19:51:25,424 - random-walk-example - INFO - Mode: server, Port: 0 Demo interval: 30s 2025-08-12 19:51:25,426 - random-walk-example - INFO - Starting server node on port 45123 2025-08-12 19:51:25,426 - random-walk-example - INFO - Node peer ID: 16Uiu2HAm7EsNv5vvjPAehGAVfChjYjD63ZHyWogQRdzntSbAg9ef - 2025-08-12 19:51:25,426 - random-walk-example - INFO - Node address: /ip4/0.0.0.0/tcp/45123/p2p/16Uiu2HAm7EsNv5vvjPAehGAVfChjYjD63ZHyWogQRdzntSbAg9ef + 2025-08-12 19:51:25,426 - random-walk-example - INFO - Node address: /ip4/127.0.0.1/tcp/45123/p2p/16Uiu2HAm7EsNv5vvjPAehGAVfChjYjD63ZHyWogQRdzntSbAg9ef 2025-08-12 19:51:25,427 - random-walk-example - INFO - Initial routing table size: 0 2025-08-12 19:51:25,427 - random-walk-example - INFO - DHT service started in SERVER mode 2025-08-12 19:51:25,430 - libp2p.discovery.random_walk.rt_refresh_manager - INFO - RT Refresh Manager started From 5633d52a63bff979f245a207f1144cc83b8b3e83 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Tue, 2 Sep 2025 03:02:41 +0530 Subject: [PATCH 03/17] test: Add comprehensive tests for address validation utilities and ensure secure binding addresses (127.0.0.1) are used instead of wildcard (0.0.0.0) --- test_address_validation_demo.py | 64 ++++++++ tests/examples/test_examples_bind_address.py | 110 +++++++++++++ tests/utils/test_default_bind_address.py | 161 +++++++++++++++++++ 3 files changed, 335 insertions(+) create mode 100644 test_address_validation_demo.py create mode 100644 tests/examples/test_examples_bind_address.py create mode 100644 tests/utils/test_default_bind_address.py diff --git a/test_address_validation_demo.py b/test_address_validation_demo.py new file mode 100644 index 000000000..dfca5de37 --- /dev/null +++ b/test_address_validation_demo.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +""" +Demonstration script to test address validation utilities +""" + +from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, +) + +def main(): + print("=== Address Validation Utilities Demo ===\n") + + port = 8000 + + # Test available interfaces + print(f"Available interfaces for port {port}:") + interfaces = get_available_interfaces(port) + for i, addr in enumerate(interfaces, 1): + print(f" {i}. {addr}") + + print() + + # Test optimal binding address + print(f"Optimal binding address for port {port}:") + optimal = get_optimal_binding_address(port) + print(f" -> {optimal}") + + print() + + # Check for wildcard addresses + wildcard_found = any("0.0.0.0" in str(addr) for addr in interfaces) + print(f"Wildcard addresses found: {wildcard_found}") + + # Check for loopback addresses + loopback_found = any("127.0.0.1" in str(addr) for addr in interfaces) + print(f"Loopback addresses found: {loopback_found}") + + # Check if optimal is wildcard + optimal_is_wildcard = "0.0.0.0" in str(optimal) + print(f"Optimal address is wildcard: {optimal_is_wildcard}") + + print() + + if not wildcard_found and loopback_found and not optimal_is_wildcard: + print("✅ All checks passed! Address validation is working correctly.") + print(" - No wildcard addresses") + print(" - Loopback always available") + print(" - Optimal address is secure") + else: + print("❌ Some checks failed. Address validation needs attention.") + + print() + + # Test different protocols + print("Testing different protocols:") + for protocol in ["tcp", "udp"]: + addr = get_optimal_binding_address(port, protocol=protocol) + print(f" {protocol.upper()}: {addr}") + if "0.0.0.0" in str(addr): + print(f" ⚠️ Warning: {protocol} returned wildcard address") + +if __name__ == "__main__": + main() diff --git a/tests/examples/test_examples_bind_address.py b/tests/examples/test_examples_bind_address.py new file mode 100644 index 000000000..2f64ea46f --- /dev/null +++ b/tests/examples/test_examples_bind_address.py @@ -0,0 +1,110 @@ +""" +Tests to verify that all examples use 127.0.0.1 instead of 0.0.0.0 +""" + +import ast +import os +from pathlib import Path + + +class TestExamplesBindAddress: + """Test suite to verify all examples use secure bind addresses""" + + def get_example_files(self): + """Get all Python files in the examples directory""" + examples_dir = Path("examples") + return list(examples_dir.rglob("*.py")) + + def check_file_for_wildcard_binding(self, filepath): + """Check if a file contains 0.0.0.0 binding""" + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + # Check for various forms of wildcard binding + wildcard_patterns = [ + '0.0.0.0', + '/ip4/0.0.0.0/', + ] + + found_wildcards = [] + for line_num, line in enumerate(content.splitlines(), 1): + for pattern in wildcard_patterns: + if pattern in line and not line.strip().startswith('#'): + found_wildcards.append((line_num, line.strip())) + + return found_wildcards + + def test_no_wildcard_binding_in_examples(self): + """Test that no example files use 0.0.0.0 for binding""" + example_files = self.get_example_files() + + # Skip certain files that might legitimately discuss wildcards + skip_files = [ + 'network_discover.py', # This demonstrates wildcard expansion + ] + + files_with_wildcards = {} + + for filepath in example_files: + if any(skip in str(filepath) for skip in skip_files): + continue + + wildcards = self.check_file_for_wildcard_binding(filepath) + if wildcards: + files_with_wildcards[str(filepath)] = wildcards + + # Assert no wildcards found + if files_with_wildcards: + error_msg = "Found wildcard bindings in example files:\n" + for filepath, occurrences in files_with_wildcards.items(): + error_msg += f"\n{filepath}:\n" + for line_num, line in occurrences: + error_msg += f" Line {line_num}: {line}\n" + + assert False, error_msg + + def test_examples_use_loopback_address(self): + """Test that examples use 127.0.0.1 for local binding""" + example_files = self.get_example_files() + + # Files that should contain listen addresses + files_with_networking = [ + 'ping/ping.py', + 'chat/chat.py', + 'bootstrap/bootstrap.py', + 'pubsub/pubsub.py', + 'identify/identify.py', + ] + + for filename in files_with_networking: + filepath = None + for example_file in example_files: + if filename in str(example_file): + filepath = example_file + break + + if filepath is None: + continue + + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + # Check for proper loopback usage + has_loopback = '127.0.0.1' in content or 'localhost' in content + has_multiaddr_loopback = '/ip4/127.0.0.1/' in content + + assert has_loopback or has_multiaddr_loopback, \ + f"{filepath} should use loopback address (127.0.0.1)" + + def test_doc_examples_use_loopback(self): + """Test that documentation examples use secure addresses""" + doc_examples_dir = Path("examples/doc-examples") + if not doc_examples_dir.exists(): + return + + doc_example_files = list(doc_examples_dir.glob("*.py")) + + for filepath in doc_example_files: + wildcards = self.check_file_for_wildcard_binding(filepath) + assert not wildcards, \ + f"Documentation example {filepath} contains wildcard binding" diff --git a/tests/utils/test_default_bind_address.py b/tests/utils/test_default_bind_address.py new file mode 100644 index 000000000..e5cc412d6 --- /dev/null +++ b/tests/utils/test_default_bind_address.py @@ -0,0 +1,161 @@ +""" +Tests for default bind address changes from 0.0.0.0 to 127.0.0.1 +""" + +import pytest +from multiaddr import Multiaddr + +from libp2p import new_host +from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, +) + + +class TestDefaultBindAddress: + """Test suite for verifying default bind addresses use secure addresses (not 0.0.0.0)""" + + def test_default_bind_address_is_not_wildcard(self): + """Test that default bind address is NOT 0.0.0.0 (wildcard)""" + port = 8000 + addr = get_optimal_binding_address(port) + + # Should NOT return wildcard address + assert "0.0.0.0" not in str(addr) + + # Should return a valid IP address (could be loopback or local network) + addr_str = str(addr) + assert "/ip4/" in addr_str + assert f"/tcp/{port}" in addr_str + + def test_available_interfaces_includes_loopback(self): + """Test that available interfaces always includes loopback address""" + port = 8000 + interfaces = get_available_interfaces(port) + + # Should have at least one interface + assert len(interfaces) > 0 + + # Should include loopback address + loopback_found = any("127.0.0.1" in str(addr) for addr in interfaces) + assert loopback_found, "Loopback address not found in available interfaces" + + # Should not have wildcard as the only option + if len(interfaces) == 1: + assert "0.0.0.0" not in str(interfaces[0]) + + def test_host_default_listen_address(self): + """Test that new hosts use secure default addresses""" + # Create a host with a specific port + port = 8000 + listen_addr = Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + host = new_host(listen_addrs=[listen_addr]) + + # Verify the host configuration + assert host is not None + # Note: We can't test actual binding without running the host, + # but we've verified the address format is correct + + def test_no_wildcard_in_fallback(self): + """Test that fallback addresses don't use wildcard binding""" + # When no interfaces are discovered, fallback should be loopback + port = 8000 + + # Even if we can't discover interfaces, we should get loopback + addr = get_optimal_binding_address(port) + # Should NOT be wildcard + assert "0.0.0.0" not in str(addr) + + # Should be a valid IP address + addr_str = str(addr) + assert "/ip4/" in addr_str + assert f"/tcp/{port}" in addr_str + + @pytest.mark.parametrize("protocol", ["tcp", "udp"]) + def test_different_protocols_use_secure_addresses(self, protocol): + """Test that different protocols still use secure addresses by default""" + port = 8000 + addr = get_optimal_binding_address(port, protocol=protocol) + + # Should NOT be wildcard + assert "0.0.0.0" not in str(addr) + assert protocol in str(addr) + + # Should be a valid IP address + addr_str = str(addr) + assert "/ip4/" in addr_str + assert f"/{protocol}/{port}" in addr_str + + def test_security_no_public_binding_by_default(self): + """Test that no public interface binding occurs by default""" + port = 8000 + interfaces = get_available_interfaces(port) + + # Check that we don't expose on all interfaces by default + wildcard_addrs = [addr for addr in interfaces if "0.0.0.0" in str(addr)] + assert len(wildcard_addrs) == 0, "Found wildcard addresses in default interfaces" + + # Verify optimal address selection doesn't choose wildcard + optimal = get_optimal_binding_address(port) + assert "0.0.0.0" not in str(optimal), "Optimal address should not be wildcard" + + # Should be a valid IP address (could be loopback or local network) + addr_str = str(optimal) + assert "/ip4/" in addr_str + assert f"/tcp/{port}" in addr_str + + def test_loopback_is_always_available(self): + """Test that loopback address is always available as an option""" + port = 8000 + interfaces = get_available_interfaces(port) + + # Loopback should always be available + loopback_addrs = [addr for addr in interfaces if "127.0.0.1" in str(addr)] + assert len(loopback_addrs) > 0, "Loopback address should always be available" + + # At least one loopback address should have the correct port + loopback_with_port = [addr for addr in loopback_addrs if f"/tcp/{port}" in str(addr)] + assert len(loopback_with_port) > 0, f"Loopback address with port {port} should be available" + + def test_optimal_address_selection_behavior(self): + """Test that optimal address selection works correctly""" + port = 8000 + interfaces = get_available_interfaces(port) + optimal = get_optimal_binding_address(port) + + # Should never return wildcard + assert "0.0.0.0" not in str(optimal) + + # Should return one of the available interfaces + optimal_str = str(optimal) + interface_strs = [str(addr) for addr in interfaces] + assert optimal_str in interface_strs, f"Optimal address {optimal_str} should be in available interfaces" + + # If non-loopback interfaces are available, should prefer them + non_loopback_interfaces = [addr for addr in interfaces if "127.0.0.1" not in str(addr)] + if non_loopback_interfaces: + # Should prefer non-loopback when available + assert "127.0.0.1" not in str(optimal), "Should prefer non-loopback when available" + else: + # Should use loopback when no other interfaces available + assert "127.0.0.1" in str(optimal), "Should use loopback when no other interfaces available" + + def test_address_validation_utilities_behavior(self): + """Test that address validation utilities behave as expected""" + port = 8000 + + # Test that we get multiple interface options + interfaces = get_available_interfaces(port) + assert len(interfaces) >= 2, "Should have at least loopback + one network interface" + + # Test that loopback is always included + has_loopback = any("127.0.0.1" in str(addr) for addr in interfaces) + assert has_loopback, "Loopback should always be available" + + # Test that no wildcards are included + has_wildcard = any("0.0.0.0" in str(addr) for addr in interfaces) + assert not has_wildcard, "Wildcard addresses should never be included" + + # Test optimal selection + optimal = get_optimal_binding_address(port) + assert optimal in interfaces, "Optimal address should be from available interfaces" From e8d1a0fc3282fc74268a6c39a97d12537c329cbf Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Tue, 2 Sep 2025 03:03:12 +0530 Subject: [PATCH 04/17] chore: add newsfragment for 885 issue fix --- newsfragments/885.feature.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 newsfragments/885.feature.rst diff --git a/newsfragments/885.feature.rst b/newsfragments/885.feature.rst new file mode 100644 index 000000000..e0566b6af --- /dev/null +++ b/newsfragments/885.feature.rst @@ -0,0 +1,2 @@ +Enhanced security by defaulting to loopback address (127.0.0.1) instead of wildcard binding. +All examples and core modules now use secure default addresses to prevent unintended public exposure. From 05867be37eafac1789ff02bff60600f557c75b42 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Tue, 2 Sep 2025 03:06:39 +0530 Subject: [PATCH 05/17] refactor: performed pre-commit checks --- test_address_validation_demo.py | 26 +++--- tests/examples/test_examples_bind_address.py | 68 ++++++++-------- tests/utils/test_default_bind_address.py | 85 ++++++++++++-------- 3 files changed, 101 insertions(+), 78 deletions(-) diff --git a/test_address_validation_demo.py b/test_address_validation_demo.py index dfca5de37..2c6e04fa8 100644 --- a/test_address_validation_demo.py +++ b/test_address_validation_demo.py @@ -8,40 +8,41 @@ get_optimal_binding_address, ) + def main(): print("=== Address Validation Utilities Demo ===\n") - + port = 8000 - + # Test available interfaces print(f"Available interfaces for port {port}:") interfaces = get_available_interfaces(port) for i, addr in enumerate(interfaces, 1): print(f" {i}. {addr}") - + print() - + # Test optimal binding address print(f"Optimal binding address for port {port}:") optimal = get_optimal_binding_address(port) print(f" -> {optimal}") - + print() - + # Check for wildcard addresses wildcard_found = any("0.0.0.0" in str(addr) for addr in interfaces) print(f"Wildcard addresses found: {wildcard_found}") - + # Check for loopback addresses loopback_found = any("127.0.0.1" in str(addr) for addr in interfaces) print(f"Loopback addresses found: {loopback_found}") - + # Check if optimal is wildcard optimal_is_wildcard = "0.0.0.0" in str(optimal) print(f"Optimal address is wildcard: {optimal_is_wildcard}") - + print() - + if not wildcard_found and loopback_found and not optimal_is_wildcard: print("✅ All checks passed! Address validation is working correctly.") print(" - No wildcard addresses") @@ -49,9 +50,9 @@ def main(): print(" - Optimal address is secure") else: print("❌ Some checks failed. Address validation needs attention.") - + print() - + # Test different protocols print("Testing different protocols:") for protocol in ["tcp", "udp"]: @@ -60,5 +61,6 @@ def main(): if "0.0.0.0" in str(addr): print(f" ⚠️ Warning: {protocol} returned wildcard address") + if __name__ == "__main__": main() diff --git a/tests/examples/test_examples_bind_address.py b/tests/examples/test_examples_bind_address.py index 2f64ea46f..1045c90ba 100644 --- a/tests/examples/test_examples_bind_address.py +++ b/tests/examples/test_examples_bind_address.py @@ -2,8 +2,6 @@ Tests to verify that all examples use 127.0.0.1 instead of 0.0.0.0 """ -import ast -import os from pathlib import Path @@ -17,42 +15,42 @@ def get_example_files(self): def check_file_for_wildcard_binding(self, filepath): """Check if a file contains 0.0.0.0 binding""" - with open(filepath, 'r', encoding='utf-8') as f: + with open(filepath, encoding="utf-8") as f: content = f.read() - + # Check for various forms of wildcard binding wildcard_patterns = [ - '0.0.0.0', - '/ip4/0.0.0.0/', + "0.0.0.0", + "/ip4/0.0.0.0/", ] - + found_wildcards = [] for line_num, line in enumerate(content.splitlines(), 1): for pattern in wildcard_patterns: - if pattern in line and not line.strip().startswith('#'): + if pattern in line and not line.strip().startswith("#"): found_wildcards.append((line_num, line.strip())) - + return found_wildcards def test_no_wildcard_binding_in_examples(self): """Test that no example files use 0.0.0.0 for binding""" example_files = self.get_example_files() - + # Skip certain files that might legitimately discuss wildcards skip_files = [ - 'network_discover.py', # This demonstrates wildcard expansion + "network_discover.py", # This demonstrates wildcard expansion ] - + files_with_wildcards = {} - + for filepath in example_files: if any(skip in str(filepath) for skip in skip_files): continue - + wildcards = self.check_file_for_wildcard_binding(filepath) if wildcards: files_with_wildcards[str(filepath)] = wildcards - + # Assert no wildcards found if files_with_wildcards: error_msg = "Found wildcard bindings in example files:\n" @@ -60,51 +58,53 @@ def test_no_wildcard_binding_in_examples(self): error_msg += f"\n{filepath}:\n" for line_num, line in occurrences: error_msg += f" Line {line_num}: {line}\n" - + assert False, error_msg def test_examples_use_loopback_address(self): """Test that examples use 127.0.0.1 for local binding""" example_files = self.get_example_files() - + # Files that should contain listen addresses files_with_networking = [ - 'ping/ping.py', - 'chat/chat.py', - 'bootstrap/bootstrap.py', - 'pubsub/pubsub.py', - 'identify/identify.py', + "ping/ping.py", + "chat/chat.py", + "bootstrap/bootstrap.py", + "pubsub/pubsub.py", + "identify/identify.py", ] - + for filename in files_with_networking: filepath = None for example_file in example_files: if filename in str(example_file): filepath = example_file break - + if filepath is None: continue - - with open(filepath, 'r', encoding='utf-8') as f: + + with open(filepath, encoding="utf-8") as f: content = f.read() - + # Check for proper loopback usage - has_loopback = '127.0.0.1' in content or 'localhost' in content - has_multiaddr_loopback = '/ip4/127.0.0.1/' in content - - assert has_loopback or has_multiaddr_loopback, \ + has_loopback = "127.0.0.1" in content or "localhost" in content + has_multiaddr_loopback = "/ip4/127.0.0.1/" in content + + assert has_loopback or has_multiaddr_loopback, ( f"{filepath} should use loopback address (127.0.0.1)" + ) def test_doc_examples_use_loopback(self): """Test that documentation examples use secure addresses""" doc_examples_dir = Path("examples/doc-examples") if not doc_examples_dir.exists(): return - + doc_example_files = list(doc_examples_dir.glob("*.py")) - + for filepath in doc_example_files: wildcards = self.check_file_for_wildcard_binding(filepath) - assert not wildcards, \ + assert not wildcards, ( f"Documentation example {filepath} contains wildcard binding" + ) diff --git a/tests/utils/test_default_bind_address.py b/tests/utils/test_default_bind_address.py index e5cc412d6..b8a501d2e 100644 --- a/tests/utils/test_default_bind_address.py +++ b/tests/utils/test_default_bind_address.py @@ -13,16 +13,19 @@ class TestDefaultBindAddress: - """Test suite for verifying default bind addresses use secure addresses (not 0.0.0.0)""" + """ + Test suite for verifying default bind addresses use + secure addresses (not 0.0.0.0) + """ def test_default_bind_address_is_not_wildcard(self): """Test that default bind address is NOT 0.0.0.0 (wildcard)""" port = 8000 addr = get_optimal_binding_address(port) - + # Should NOT return wildcard address assert "0.0.0.0" not in str(addr) - + # Should return a valid IP address (could be loopback or local network) addr_str = str(addr) assert "/ip4/" in addr_str @@ -32,14 +35,14 @@ def test_available_interfaces_includes_loopback(self): """Test that available interfaces always includes loopback address""" port = 8000 interfaces = get_available_interfaces(port) - + # Should have at least one interface assert len(interfaces) > 0 - + # Should include loopback address loopback_found = any("127.0.0.1" in str(addr) for addr in interfaces) assert loopback_found, "Loopback address not found in available interfaces" - + # Should not have wildcard as the only option if len(interfaces) == 1: assert "0.0.0.0" not in str(interfaces[0]) @@ -50,7 +53,7 @@ def test_host_default_listen_address(self): port = 8000 listen_addr = Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") host = new_host(listen_addrs=[listen_addr]) - + # Verify the host configuration assert host is not None # Note: We can't test actual binding without running the host, @@ -60,12 +63,12 @@ def test_no_wildcard_in_fallback(self): """Test that fallback addresses don't use wildcard binding""" # When no interfaces are discovered, fallback should be loopback port = 8000 - + # Even if we can't discover interfaces, we should get loopback addr = get_optimal_binding_address(port) # Should NOT be wildcard assert "0.0.0.0" not in str(addr) - + # Should be a valid IP address addr_str = str(addr) assert "/ip4/" in addr_str @@ -76,11 +79,11 @@ def test_different_protocols_use_secure_addresses(self, protocol): """Test that different protocols still use secure addresses by default""" port = 8000 addr = get_optimal_binding_address(port, protocol=protocol) - + # Should NOT be wildcard assert "0.0.0.0" not in str(addr) assert protocol in str(addr) - + # Should be a valid IP address addr_str = str(addr) assert "/ip4/" in addr_str @@ -90,15 +93,17 @@ def test_security_no_public_binding_by_default(self): """Test that no public interface binding occurs by default""" port = 8000 interfaces = get_available_interfaces(port) - + # Check that we don't expose on all interfaces by default wildcard_addrs = [addr for addr in interfaces if "0.0.0.0" in str(addr)] - assert len(wildcard_addrs) == 0, "Found wildcard addresses in default interfaces" - + assert len(wildcard_addrs) == 0, ( + "Found wildcard addresses in default interfaces" + ) + # Verify optimal address selection doesn't choose wildcard optimal = get_optimal_binding_address(port) assert "0.0.0.0" not in str(optimal), "Optimal address should not be wildcard" - + # Should be a valid IP address (could be loopback or local network) addr_str = str(optimal) assert "/ip4/" in addr_str @@ -108,54 +113,70 @@ def test_loopback_is_always_available(self): """Test that loopback address is always available as an option""" port = 8000 interfaces = get_available_interfaces(port) - + # Loopback should always be available loopback_addrs = [addr for addr in interfaces if "127.0.0.1" in str(addr)] assert len(loopback_addrs) > 0, "Loopback address should always be available" - + # At least one loopback address should have the correct port - loopback_with_port = [addr for addr in loopback_addrs if f"/tcp/{port}" in str(addr)] - assert len(loopback_with_port) > 0, f"Loopback address with port {port} should be available" + loopback_with_port = [ + addr for addr in loopback_addrs if f"/tcp/{port}" in str(addr) + ] + assert len(loopback_with_port) > 0, ( + f"Loopback address with port {port} should be available" + ) def test_optimal_address_selection_behavior(self): """Test that optimal address selection works correctly""" port = 8000 interfaces = get_available_interfaces(port) optimal = get_optimal_binding_address(port) - + # Should never return wildcard assert "0.0.0.0" not in str(optimal) - + # Should return one of the available interfaces optimal_str = str(optimal) interface_strs = [str(addr) for addr in interfaces] - assert optimal_str in interface_strs, f"Optimal address {optimal_str} should be in available interfaces" - + assert optimal_str in interface_strs, ( + f"Optimal address {optimal_str} should be in available interfaces" + ) + # If non-loopback interfaces are available, should prefer them - non_loopback_interfaces = [addr for addr in interfaces if "127.0.0.1" not in str(addr)] + non_loopback_interfaces = [ + addr for addr in interfaces if "127.0.0.1" not in str(addr) + ] if non_loopback_interfaces: # Should prefer non-loopback when available - assert "127.0.0.1" not in str(optimal), "Should prefer non-loopback when available" + assert "127.0.0.1" not in str(optimal), ( + "Should prefer non-loopback when available" + ) else: # Should use loopback when no other interfaces available - assert "127.0.0.1" in str(optimal), "Should use loopback when no other interfaces available" + assert "127.0.0.1" in str(optimal), ( + "Should use loopback when no other interfaces available" + ) def test_address_validation_utilities_behavior(self): """Test that address validation utilities behave as expected""" port = 8000 - + # Test that we get multiple interface options interfaces = get_available_interfaces(port) - assert len(interfaces) >= 2, "Should have at least loopback + one network interface" - + assert len(interfaces) >= 2, ( + "Should have at least loopback + one network interface" + ) + # Test that loopback is always included has_loopback = any("127.0.0.1" in str(addr) for addr in interfaces) assert has_loopback, "Loopback should always be available" - + # Test that no wildcards are included has_wildcard = any("0.0.0.0" in str(addr) for addr in interfaces) assert not has_wildcard, "Wildcard addresses should never be included" - + # Test optimal selection optimal = get_optimal_binding_address(port) - assert optimal in interfaces, "Optimal address should be from available interfaces" + assert optimal in interfaces, ( + "Optimal address should be from available interfaces" + ) From 809a32a7123beede0f4677fc2b6828ae66869cf3 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Tue, 2 Sep 2025 03:35:35 +0530 Subject: [PATCH 06/17] chore: remove temp test valid script --- test_address_validation_demo.py | 66 --------------------------------- 1 file changed, 66 deletions(-) delete mode 100644 test_address_validation_demo.py diff --git a/test_address_validation_demo.py b/test_address_validation_demo.py deleted file mode 100644 index 2c6e04fa8..000000000 --- a/test_address_validation_demo.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 -""" -Demonstration script to test address validation utilities -""" - -from libp2p.utils.address_validation import ( - get_available_interfaces, - get_optimal_binding_address, -) - - -def main(): - print("=== Address Validation Utilities Demo ===\n") - - port = 8000 - - # Test available interfaces - print(f"Available interfaces for port {port}:") - interfaces = get_available_interfaces(port) - for i, addr in enumerate(interfaces, 1): - print(f" {i}. {addr}") - - print() - - # Test optimal binding address - print(f"Optimal binding address for port {port}:") - optimal = get_optimal_binding_address(port) - print(f" -> {optimal}") - - print() - - # Check for wildcard addresses - wildcard_found = any("0.0.0.0" in str(addr) for addr in interfaces) - print(f"Wildcard addresses found: {wildcard_found}") - - # Check for loopback addresses - loopback_found = any("127.0.0.1" in str(addr) for addr in interfaces) - print(f"Loopback addresses found: {loopback_found}") - - # Check if optimal is wildcard - optimal_is_wildcard = "0.0.0.0" in str(optimal) - print(f"Optimal address is wildcard: {optimal_is_wildcard}") - - print() - - if not wildcard_found and loopback_found and not optimal_is_wildcard: - print("✅ All checks passed! Address validation is working correctly.") - print(" - No wildcard addresses") - print(" - Loopback always available") - print(" - Optimal address is secure") - else: - print("❌ Some checks failed. Address validation needs attention.") - - print() - - # Test different protocols - print("Testing different protocols:") - for protocol in ["tcp", "udp"]: - addr = get_optimal_binding_address(port, protocol=protocol) - print(f" {protocol.upper()}: {addr}") - if "0.0.0.0" in str(addr): - print(f" ⚠️ Warning: {protocol} returned wildcard address") - - -if __name__ == "__main__": - main() From 030deb42b423e60f2279c73ccb696a98464aa8d4 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Fri, 5 Sep 2025 20:05:10 +0530 Subject: [PATCH 07/17] refactor: update examples to use available interfaces for listening addresses and improve logging configuration --- examples/bootstrap/bootstrap.py | 22 +++++++-- examples/chat/chat.py | 29 ++++++++++-- examples/echo/echo.py | 6 +++ examples/echo/echo_quic.py | 27 +++++++++-- examples/identify/identify.py | 46 ++++++++++++++----- .../identify_push_listener_dialer.py | 35 ++++++++++---- examples/kademlia/kademlia.py | 25 +++++++--- examples/mDNS/mDNS.py | 39 ++++++++++------ examples/ping/ping.py | 31 ++++++++++--- examples/pubsub/pubsub.py | 27 +++++------ examples/random_walk/random_walk.py | 15 ++++-- libp2p/utils/address_validation.py | 10 ++-- 12 files changed, 231 insertions(+), 81 deletions(-) diff --git a/examples/bootstrap/bootstrap.py b/examples/bootstrap/bootstrap.py index 93a6913af..825f3a089 100644 --- a/examples/bootstrap/bootstrap.py +++ b/examples/bootstrap/bootstrap.py @@ -2,7 +2,6 @@ import logging import secrets -import multiaddr import trio from libp2p import new_host @@ -54,18 +53,22 @@ def on_peer_discovery(peer_info: PeerInfo) -> None: async def run(port: int, bootstrap_addrs: list[str]) -> None: """Run the bootstrap discovery example.""" + from libp2p.utils.address_validation import find_free_port, get_available_interfaces + + if port <= 0: + port = find_free_port() + # Generate key pair secret = secrets.token_bytes(32) key_pair = create_new_key_pair(secret) - # Create listen address - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + # Create listen addresses for all available interfaces + listen_addrs = get_available_interfaces(port) # Register peer discovery handler peerDiscovery.register_peer_discovered_handler(on_peer_discovery) logger.info("🚀 Starting Bootstrap Discovery Example") - logger.info(f"📍 Listening on: {listen_addr}") logger.info(f"🌐 Bootstrap peers: {len(bootstrap_addrs)}") print("\n" + "=" * 60) @@ -80,7 +83,16 @@ async def run(port: int, bootstrap_addrs: list[str]) -> None: host = new_host(key_pair=key_pair, bootstrap=bootstrap_addrs) try: - async with host.run(listen_addrs=[listen_addr]): + async with host.run(listen_addrs=listen_addrs): + # Get all available addresses with peer ID + all_addrs = host.get_addrs() + + logger.info("Listener ready, listening on:") + print("Listener ready, listening on:") + for addr in all_addrs: + logger.info(f"{addr}") + print(f"{addr}") + # Keep running and log peer discovery events await trio.sleep_forever() except KeyboardInterrupt: diff --git a/examples/chat/chat.py b/examples/chat/chat.py index c06e20a77..35f98d257 100755 --- a/examples/chat/chat.py +++ b/examples/chat/chat.py @@ -1,4 +1,5 @@ import argparse +import logging import sys import multiaddr @@ -17,6 +18,11 @@ info_from_p2p_addr, ) +# Configure minimal logging +logging.basicConfig(level=logging.WARNING) +logging.getLogger("multiaddr").setLevel(logging.WARNING) +logging.getLogger("libp2p").setLevel(logging.WARNING) + PROTOCOL_ID = TProtocol("/chat/1.0.0") MAX_READ_LEN = 2**32 - 1 @@ -40,9 +46,14 @@ async def write_data(stream: INetStream) -> None: async def run(port: int, destination: str) -> None: - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + from libp2p.utils.address_validation import find_free_port, get_available_interfaces + + if port <= 0: + port = find_free_port() + + listen_addrs = get_available_interfaces(port) host = new_host() - async with host.run(listen_addrs=[listen_addr]), trio.open_nursery() as nursery: + async with host.run(listen_addrs=listen_addrs), trio.open_nursery() as nursery: # Start the peer-store cleanup task nursery.start_soon(host.get_peerstore().start_cleanup_task, 60) @@ -54,10 +65,18 @@ async def stream_handler(stream: INetStream) -> None: host.set_stream_handler(PROTOCOL_ID, stream_handler) + # Get all available addresses with peer ID + all_addrs = host.get_addrs() + + print("Listener ready, listening on:\n") + for addr in all_addrs: + print(f"{addr}") + + # Use the first address as the default for the client command + default_addr = all_addrs[0] print( - "Run this from the same folder in another console:\n\n" - f"chat-demo " - f"-d {host.get_addrs()[0]}\n" + f"\nRun this from the same folder in another console:\n\n" + f"chat-demo -d {default_addr}\n" ) print("Waiting for incoming connection...") diff --git a/examples/echo/echo.py b/examples/echo/echo.py index 19e983771..42e3ff0cd 100644 --- a/examples/echo/echo.py +++ b/examples/echo/echo.py @@ -1,4 +1,5 @@ import argparse +import logging import random import secrets @@ -28,6 +29,11 @@ get_available_interfaces, ) +# Configure minimal logging +logging.basicConfig(level=logging.WARNING) +logging.getLogger("multiaddr").setLevel(logging.WARNING) +logging.getLogger("libp2p").setLevel(logging.WARNING) + PROTOCOL_ID = TProtocol("/echo/1.0.0") MAX_READ_LEN = 2**32 - 1 diff --git a/examples/echo/echo_quic.py b/examples/echo/echo_quic.py index 248aed9f6..667a50dcd 100644 --- a/examples/echo/echo_quic.py +++ b/examples/echo/echo_quic.py @@ -20,6 +20,11 @@ from libp2p.network.stream.net_stream import INetStream from libp2p.peer.peerinfo import info_from_p2p_addr +# Configure minimal logging +logging.basicConfig(level=logging.WARNING) +logging.getLogger("multiaddr").setLevel(logging.WARNING) +logging.getLogger("libp2p").setLevel(logging.WARNING) + PROTOCOL_ID = TProtocol("/echo/1.0.0") @@ -38,6 +43,12 @@ async def _echo_stream_handler(stream: INetStream) -> None: async def run_server(port: int, seed: int | None = None) -> None: """Run echo server with QUIC transport.""" + from libp2p.utils.address_validation import find_free_port + + if port <= 0: + port = find_free_port() + + # For QUIC, we need to use UDP addresses listen_addr = Multiaddr(f"/ip4/0.0.0.0/udp/{port}/quic") if seed: @@ -63,10 +74,18 @@ async def run_server(port: int, seed: int | None = None) -> None: print(f"I am {host.get_id().to_string()}") host.set_stream_handler(PROTOCOL_ID, _echo_stream_handler) + # Get all available addresses with peer ID + all_addrs = host.get_addrs() + + print("Listener ready, listening on:") + for addr in all_addrs: + print(f"{addr}") + + # Use the first address as the default for the client command + default_addr = all_addrs[0] print( - "Run this from the same folder in another console:\n\n" - f"python3 ./examples/echo/echo_quic.py " - f"-d {host.get_addrs()[0]}\n" + f"\nRun this from the same folder in another console:\n\n" + f"python3 ./examples/echo/echo_quic.py -d {default_addr}\n" ) print("Waiting for incoming QUIC connections...") await trio.sleep_forever() @@ -173,6 +192,4 @@ def main() -> None: if __name__ == "__main__": - logging.basicConfig(level=logging.DEBUG) - logging.getLogger("aioquic").setLevel(logging.DEBUG) main() diff --git a/examples/identify/identify.py b/examples/identify/identify.py index 445962c38..bd973a3e1 100644 --- a/examples/identify/identify.py +++ b/examples/identify/identify.py @@ -20,6 +20,11 @@ info_from_p2p_addr, ) +# Configure minimal logging +logging.basicConfig(level=logging.WARNING) +logging.getLogger("multiaddr").setLevel(logging.WARNING) +logging.getLogger("libp2p").setLevel(logging.WARNING) + logger = logging.getLogger("libp2p.identity.identify-example") @@ -58,11 +63,16 @@ def print_identify_response(identify_response: Identify): async def run(port: int, destination: str, use_varint_format: bool = True) -> None: - localhost_ip = "127.0.0.1" + from libp2p.utils.address_validation import get_available_interfaces if not destination: # Create first host (listener) - listen_addr = multiaddr.Multiaddr(f"/ip4/{localhost_ip}/tcp/{port}") + if port <= 0: + from libp2p.utils.address_validation import find_free_port + + port = find_free_port() + + listen_addrs = get_available_interfaces(port) host_a = new_host() # Set up identify handler with specified format @@ -73,22 +83,28 @@ async def run(port: int, destination: str, use_varint_format: bool = True) -> No host_a.set_stream_handler(IDENTIFY_PROTOCOL_ID, identify_handler) async with ( - host_a.run(listen_addrs=[listen_addr]), + host_a.run(listen_addrs=listen_addrs), trio.open_nursery() as nursery, ): # Start the peer-store cleanup task nursery.start_soon(host_a.get_peerstore().start_cleanup_task, 60) - # Get the actual address - server_addr = str(host_a.get_addrs()[0]) - client_addr = server_addr + # Get all available addresses with peer ID + all_addrs = host_a.get_addrs() format_name = "length-prefixed" if use_varint_format else "raw protobuf" format_flag = "--raw-format" if not use_varint_format else "" + + print(f"First host listening (using {format_name} format).") + print("Listener ready, listening on:\n") + for addr in all_addrs: + print(f"{addr}") + + # Use the first address as the default for the client command + default_addr = all_addrs[0] print( - f"First host listening (using {format_name} format). " - f"Run this from another console:\n\n" - f"identify-demo {format_flag} -d {client_addr}\n" + f"\nRun this from the same folder in another console:\n\n" + f"identify-demo {format_flag} -d {default_addr}\n" ) print("Waiting for incoming identify request...") @@ -133,11 +149,19 @@ async def custom_identify_handler(stream): else: # Create second host (dialer) - listen_addr = multiaddr.Multiaddr(f"/ip4/{localhost_ip}/tcp/{port}") + from libp2p.utils.address_validation import ( + find_free_port, + get_available_interfaces, + ) + + if port <= 0: + port = find_free_port() + + listen_addrs = get_available_interfaces(port) host_b = new_host() async with ( - host_b.run(listen_addrs=[listen_addr]), + host_b.run(listen_addrs=listen_addrs), trio.open_nursery() as nursery, ): # Start the peer-store cleanup task diff --git a/examples/identify_push/identify_push_listener_dialer.py b/examples/identify_push/identify_push_listener_dialer.py index a9974b82b..3701aaf52 100644 --- a/examples/identify_push/identify_push_listener_dialer.py +++ b/examples/identify_push/identify_push_listener_dialer.py @@ -56,6 +56,11 @@ info_from_p2p_addr, ) +# Configure minimal logging +logging.basicConfig(level=logging.WARNING) +logging.getLogger("multiaddr").setLevel(logging.WARNING) +logging.getLogger("libp2p").setLevel(logging.WARNING) + # Configure logging logger = logging.getLogger("libp2p.identity.identify-push-example") @@ -194,6 +199,11 @@ async def run_listener( port: int, use_varint_format: bool = True, raw_format_flag: bool = False ) -> None: """Run a host in listener mode.""" + from libp2p.utils.address_validation import find_free_port, get_available_interfaces + + if port <= 0: + port = find_free_port() + format_name = "length-prefixed" if use_varint_format else "raw protobuf" print( f"\n==== Starting Identify-Push Listener on port {port} " @@ -215,26 +225,33 @@ async def run_listener( custom_identify_push_handler_for(host, use_varint_format=use_varint_format), ) - # Start listening - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + # Start listening on all available interfaces + listen_addrs = get_available_interfaces(port) try: - async with host.run([listen_addr]): - addr = host.get_addrs()[0] + async with host.run(listen_addrs): + all_addrs = host.get_addrs() logger.info("Listener host ready!") print("Listener host ready!") - logger.info(f"Listening on: {addr}") - print(f"Listening on: {addr}") + logger.info("Listener ready, listening on:") + print("Listener ready, listening on:") + for addr in all_addrs: + logger.info(f"{addr}") + print(f"{addr}") logger.info(f"Peer ID: {host.get_id().pretty()}") print(f"Peer ID: {host.get_id().pretty()}") - print("\nRun dialer with command:") + # Use the first address as the default for the dialer command + default_addr = all_addrs[0] + print("\nRun this from the same folder in another console:") if raw_format_flag: - print(f"identify-push-listener-dialer-demo -d {addr} --raw-format") + print( + f"identify-push-listener-dialer-demo -d {default_addr} --raw-format" + ) else: - print(f"identify-push-listener-dialer-demo -d {addr}") + print(f"identify-push-listener-dialer-demo -d {default_addr}") print("\nWaiting for incoming identify/push requests... (Ctrl+C to exit)") # Keep running until interrupted diff --git a/examples/kademlia/kademlia.py b/examples/kademlia/kademlia.py index faaa66be7..80bbc9955 100644 --- a/examples/kademlia/kademlia.py +++ b/examples/kademlia/kademlia.py @@ -150,26 +150,39 @@ async def run_node( key_pair = create_new_key_pair(secrets.token_bytes(32)) host = new_host(key_pair=key_pair) - listen_addr = Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") - async with host.run(listen_addrs=[listen_addr]), trio.open_nursery() as nursery: + from libp2p.utils.address_validation import get_available_interfaces + + listen_addrs = get_available_interfaces(port) + + async with host.run(listen_addrs=listen_addrs), trio.open_nursery() as nursery: # Start the peer-store cleanup task nursery.start_soon(host.get_peerstore().start_cleanup_task, 60) peer_id = host.get_id().pretty() - addr_str = f"/ip4/127.0.0.1/tcp/{port}/p2p/{peer_id}" + + # Get all available addresses with peer ID + all_addrs = host.get_addrs() + + logger.info("Listener ready, listening on:") + for addr in all_addrs: + logger.info(f"{addr}") + + # Use the first address as the default for the bootstrap command + default_addr = all_addrs[0] + bootstrap_cmd = f"--bootstrap {default_addr}" + logger.info("To connect to this node, use: %s", bootstrap_cmd) + await connect_to_bootstrap_nodes(host, bootstrap_nodes) dht = KadDHT(host, dht_mode) # take all peer ids from the host and add them to the dht for peer_id in host.get_peerstore().peer_ids(): await dht.routing_table.add_peer(peer_id) logger.info(f"Connected to bootstrap nodes: {host.get_connected_peers()}") - bootstrap_cmd = f"--bootstrap {addr_str}" - logger.info("To connect to this node, use: %s", bootstrap_cmd) # Save server address in server mode if dht_mode == DHTMode.SERVER: - save_server_addr(addr_str) + save_server_addr(str(default_addr)) # Start the DHT service async with background_trio_service(dht): diff --git a/examples/mDNS/mDNS.py b/examples/mDNS/mDNS.py index 499ca2248..9f0cf74b9 100644 --- a/examples/mDNS/mDNS.py +++ b/examples/mDNS/mDNS.py @@ -2,7 +2,6 @@ import logging import secrets -import multiaddr import trio from libp2p import ( @@ -14,6 +13,11 @@ ) from libp2p.discovery.events.peerDiscovery import peerDiscovery +# Configure minimal logging +logging.basicConfig(level=logging.WARNING) +logging.getLogger("multiaddr").setLevel(logging.WARNING) +logging.getLogger("libp2p").setLevel(logging.WARNING) + logger = logging.getLogger("libp2p.discovery.mdns") logger.setLevel(logging.INFO) handler = logging.StreamHandler() @@ -22,34 +26,43 @@ ) logger.addHandler(handler) -# Set root logger to DEBUG to capture all logs from dependencies -logging.getLogger().setLevel(logging.DEBUG) - def onPeerDiscovery(peerinfo: PeerInfo): logger.info(f"Discovered: {peerinfo.peer_id}") async def run(port: int) -> None: + from libp2p.utils.address_validation import find_free_port, get_available_interfaces + + if port <= 0: + port = find_free_port() + secret = secrets.token_bytes(32) key_pair = create_new_key_pair(secret) - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + listen_addrs = get_available_interfaces(port) peerDiscovery.register_peer_discovered_handler(onPeerDiscovery) - print( - "Run this from the same folder in another console to " - "start another peer on a different port:\n\n" - "mdns-demo -p \n" - ) - print("Waiting for mDNS peer discovery events...\n") - logger.info("Starting peer Discovery") host = new_host(key_pair=key_pair, enable_mDNS=True) - async with host.run(listen_addrs=[listen_addr]), trio.open_nursery() as nursery: + async with host.run(listen_addrs=listen_addrs), trio.open_nursery() as nursery: # Start the peer-store cleanup task nursery.start_soon(host.get_peerstore().start_cleanup_task, 60) + # Get all available addresses with peer ID + all_addrs = host.get_addrs() + + print("Listener ready, listening on:") + for addr in all_addrs: + print(f"{addr}") + + print( + "\nRun this from the same folder in another console to " + "start another peer on a different port:\n\n" + "mdns-demo -p \n" + ) + print("Waiting for mDNS peer discovery events...\n") + await trio.sleep_forever() diff --git a/examples/ping/ping.py b/examples/ping/ping.py index bb47bd95d..52bb759a3 100644 --- a/examples/ping/ping.py +++ b/examples/ping/ping.py @@ -1,4 +1,5 @@ import argparse +import logging import multiaddr import trio @@ -16,6 +17,11 @@ info_from_p2p_addr, ) +# Configure minimal logging +logging.basicConfig(level=logging.WARNING) +logging.getLogger("multiaddr").setLevel(logging.WARNING) +logging.getLogger("libp2p").setLevel(logging.WARNING) + PING_PROTOCOL_ID = TProtocol("/ipfs/ping/1.0.0") PING_LENGTH = 32 RESP_TIMEOUT = 60 @@ -55,20 +61,33 @@ async def send_ping(stream: INetStream) -> None: async def run(port: int, destination: str) -> None: - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") - host = new_host(listen_addrs=[listen_addr]) + from libp2p.utils.address_validation import find_free_port, get_available_interfaces + + if port <= 0: + port = find_free_port() + + listen_addrs = get_available_interfaces(port) + host = new_host(listen_addrs=listen_addrs) - async with host.run(listen_addrs=[listen_addr]), trio.open_nursery() as nursery: + async with host.run(listen_addrs=listen_addrs), trio.open_nursery() as nursery: # Start the peer-store cleanup task nursery.start_soon(host.get_peerstore().start_cleanup_task, 60) if not destination: host.set_stream_handler(PING_PROTOCOL_ID, handle_ping) + # Get all available addresses with peer ID + all_addrs = host.get_addrs() + + print("Listener ready, listening on:\n") + for addr in all_addrs: + print(f"{addr}") + + # Use the first address as the default for the client command + default_addr = all_addrs[0] print( - "Run this from the same folder in another console:\n\n" - f"ping-demo " - f"-d {host.get_addrs()[0]}\n" + f"\nRun this from the same folder in another console:\n\n" + f"ping-demo -d {default_addr}\n" ) print("Waiting for incoming connection...") diff --git a/examples/pubsub/pubsub.py b/examples/pubsub/pubsub.py index 843a28292..6e8495c10 100644 --- a/examples/pubsub/pubsub.py +++ b/examples/pubsub/pubsub.py @@ -102,14 +102,13 @@ async def monitor_peer_topics(pubsub, nursery, termination_event): async def run(topic: str, destination: str | None, port: int | None) -> None: - # Initialize network settings - localhost_ip = "127.0.0.1" + from libp2p.utils.address_validation import get_available_interfaces if port is None or port == 0: port = find_free_port() logger.info(f"Using random available port: {port}") - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + listen_addrs = get_available_interfaces(port) # Create a new libp2p host host = new_host( @@ -138,12 +137,11 @@ async def run(topic: str, destination: str | None, port: int | None) -> None: pubsub = Pubsub(host, gossipsub) termination_event = trio.Event() # Event to signal termination - async with host.run(listen_addrs=[listen_addr]), trio.open_nursery() as nursery: + async with host.run(listen_addrs=listen_addrs), trio.open_nursery() as nursery: # Start the peer-store cleanup task nursery.start_soon(host.get_peerstore().start_cleanup_task, 60) logger.info(f"Node started with peer ID: {host.get_id()}") - logger.info(f"Listening on: {listen_addr}") logger.info("Initializing PubSub and GossipSub...") async with background_trio_service(pubsub): async with background_trio_service(gossipsub): @@ -157,10 +155,18 @@ async def run(topic: str, destination: str | None, port: int | None) -> None: if not destination: # Server mode + # Get all available addresses with peer ID + all_addrs = host.get_addrs() + + logger.info("Listener ready, listening on:") + for addr in all_addrs: + logger.info(f"{addr}") + + # Use the first address as the default for the client command + default_addr = all_addrs[0] logger.info( - "Run this script in another console with:\n" - f"pubsub-demo " - f"-d /ip4/{localhost_ip}/tcp/{port}/p2p/{host.get_id()}\n" + f"\nRun this from the same folder in another console:\n\n" + f"pubsub-demo -d {default_addr}\n" ) logger.info("Waiting for peers...") @@ -182,11 +188,6 @@ async def run(topic: str, destination: str | None, port: int | None) -> None: f"Connecting to peer: {info.peer_id} " f"using protocols: {protocols_in_maddr}" ) - logger.info( - "Run this script in another console with:\n" - f"pubsub-demo " - f"-d /ip4/{localhost_ip}/tcp/{port}/p2p/{host.get_id()}\n" - ) try: await host.connect(info) logger.info(f"Connected to peer: {info.peer_id}") diff --git a/examples/random_walk/random_walk.py b/examples/random_walk/random_walk.py index b90d63041..d2278b162 100644 --- a/examples/random_walk/random_walk.py +++ b/examples/random_walk/random_walk.py @@ -16,7 +16,6 @@ import secrets import sys -from multiaddr import Multiaddr import trio from libp2p import new_host @@ -130,16 +129,24 @@ async def run_node(port: int, mode: str, demo_interval: int = 30) -> None: # Create host and DHT key_pair = create_new_key_pair(secrets.token_bytes(32)) host = new_host(key_pair=key_pair, bootstrap=DEFAULT_BOOTSTRAP_NODES) - listen_addr = Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") - async with host.run(listen_addrs=[listen_addr]), trio.open_nursery() as nursery: + from libp2p.utils.address_validation import get_available_interfaces + + listen_addrs = get_available_interfaces(port) + + async with host.run(listen_addrs=listen_addrs), trio.open_nursery() as nursery: # Start maintenance tasks nursery.start_soon(host.get_peerstore().start_cleanup_task, 60) nursery.start_soon(maintain_connections, host) peer_id = host.get_id().pretty() logger.info(f"Node peer ID: {peer_id}") - logger.info(f"Node address: /ip4/127.0.0.1/tcp/{port}/p2p/{peer_id}") + + # Get all available addresses with peer ID + all_addrs = host.get_addrs() + logger.info("Listener ready, listening on:") + for addr in all_addrs: + logger.info(f"{addr}") # Create and start DHT with Random Walk enabled dht = KadDHT(host, dht_mode, enable_random_walk=True) diff --git a/libp2p/utils/address_validation.py b/libp2p/utils/address_validation.py index 106772412..a470ad243 100644 --- a/libp2p/utils/address_validation.py +++ b/libp2p/utils/address_validation.py @@ -73,8 +73,9 @@ def get_available_interfaces(port: int, protocol: str = "tcp") -> list[Multiaddr seen_v4: set[str] = set() for ip in _safe_get_network_addrs(4): - seen_v4.add(ip) - addrs.append(Multiaddr(f"/ip4/{ip}/{protocol}/{port}")) + if ip not in seen_v4: # Avoid duplicates + seen_v4.add(ip) + addrs.append(Multiaddr(f"/ip4/{ip}/{protocol}/{port}")) # Ensure IPv4 loopback is always included when IPv4 interfaces are discovered if seen_v4 and "127.0.0.1" not in seen_v4: @@ -89,8 +90,9 @@ def get_available_interfaces(port: int, protocol: str = "tcp") -> list[Multiaddr # # seen_v6: set[str] = set() # for ip in _safe_get_network_addrs(6): - # seen_v6.add(ip) - # addrs.append(Multiaddr(f"/ip6/{ip}/{protocol}/{port}")) + # if ip not in seen_v6: # Avoid duplicates + # seen_v6.add(ip) + # addrs.append(Multiaddr(f"/ip6/{ip}/{protocol}/{port}")) # # # Always include IPv6 loopback for testing purposes when IPv6 is available # # This ensures IPv6 functionality can be tested even without global IPv6 addresses From aa2a650f853e1e50116e61207ec2c0aceb875d39 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Fri, 5 Sep 2025 20:54:33 +0530 Subject: [PATCH 08/17] fix: update QUIC examples to use loopback address for improved security --- examples/doc-examples/example_quic_transport.py | 2 +- examples/echo/echo_quic.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/doc-examples/example_quic_transport.py b/examples/doc-examples/example_quic_transport.py index da2f53951..2ec45c2d4 100644 --- a/examples/doc-examples/example_quic_transport.py +++ b/examples/doc-examples/example_quic_transport.py @@ -21,7 +21,7 @@ async def main(): # Configure the listening address port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/udp/{port}/quic-v1") + listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/udp/{port}/quic-v1") # Start the host async with host.run(listen_addrs=[listen_addr]): diff --git a/examples/echo/echo_quic.py b/examples/echo/echo_quic.py index 667a50dcd..700db1ded 100644 --- a/examples/echo/echo_quic.py +++ b/examples/echo/echo_quic.py @@ -48,8 +48,8 @@ async def run_server(port: int, seed: int | None = None) -> None: if port <= 0: port = find_free_port() - # For QUIC, we need to use UDP addresses - listen_addr = Multiaddr(f"/ip4/0.0.0.0/udp/{port}/quic") + # For QUIC, we need to use UDP addresses - use loopback for security + listen_addr = Multiaddr(f"/ip4/127.0.0.1/udp/{port}/quic") if seed: import random From a69db8a716b19313b144c4304abad65bf7ee9631 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Sat, 6 Sep 2025 02:00:19 +0530 Subject: [PATCH 09/17] refactor(app): 885 Add ignore comment since SO attr not supported to Win --- libp2p/transport/quic/listener.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/transport/quic/listener.py b/libp2p/transport/quic/listener.py index 0e8e66ad9..42c8c6625 100644 --- a/libp2p/transport/quic/listener.py +++ b/libp2p/transport/quic/listener.py @@ -901,7 +901,7 @@ async def _create_socket(self, host: str, port: int) -> trio.socket.SocketType: # Set socket options sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if hasattr(socket, "SO_REUSEPORT"): - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) # type: ignore[attr-defined] # Bind to address await sock.bind((host, port)) From 7d364da950c5d2b4fa76bfdbb4811bda49a5a437 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Tue, 9 Sep 2025 12:10:28 +0530 Subject: [PATCH 10/17] Refactor: update examples to utilize new address paradigm with wildcard support - Introduced `get_wildcard_address` function for explicit wildcard binding. - Updated examples to use `get_available_interfaces` and `get_optimal_binding_address` for address selection. - Ensured consistent usage of the new address paradigm across all example files. - Added tests to verify the implementation of the new address paradigm and wildcard feature. --- examples/advanced/network_discover.py | 9 +- examples/bootstrap/bootstrap.py | 12 +- examples/chat/chat.py | 13 +- examples/doc-examples/example_multiplexer.py | 13 +- examples/doc-examples/example_net_stream.py | 14 +- examples/doc-examples/example_running.py | 13 +- examples/doc-examples/example_transport.py | 13 +- examples/echo/echo.py | 7 +- examples/identify/identify.py | 13 +- examples/kademlia/kademlia.py | 14 +- examples/ping/ping.py | 13 +- examples/pubsub/pubsub.py | 14 +- libp2p/utils/address_validation.py | 64 ++++----- tests/examples/test_examples_bind_address.py | 105 +++++++------- tests/utils/test_default_bind_address.py | 144 +++++++++++-------- 15 files changed, 280 insertions(+), 181 deletions(-) diff --git a/examples/advanced/network_discover.py b/examples/advanced/network_discover.py index 71edd2094..13f7d03aa 100644 --- a/examples/advanced/network_discover.py +++ b/examples/advanced/network_discover.py @@ -14,6 +14,7 @@ expand_wildcard_address, get_available_interfaces, get_optimal_binding_address, + get_wildcard_address, ) except ImportError: # Fallbacks if utilities are missing @@ -29,6 +30,9 @@ def expand_wildcard_address(addr: Multiaddr, port: int | None = None): def get_optimal_binding_address(port: int, protocol: str = "tcp"): return Multiaddr(f"/ip4/127.0.0.1/{protocol}/{port}") + def get_wildcard_address(port: int, protocol: str = "tcp"): + return Multiaddr(f"/ip4/0.0.0.0/{protocol}/{port}") + def main() -> None: port = 8080 @@ -37,7 +41,10 @@ def main() -> None: for a in interfaces: print(f" - {a}") - wildcard_v4 = Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + # Demonstrate wildcard address as a feature + wildcard_v4 = get_wildcard_address(port) + print(f"\nWildcard address (feature): {wildcard_v4}") + expanded_v4 = expand_wildcard_address(wildcard_v4) print("\nExpanded IPv4 wildcard:") for a in expanded_v4: diff --git a/examples/bootstrap/bootstrap.py b/examples/bootstrap/bootstrap.py index 825f3a089..b4fa9234c 100644 --- a/examples/bootstrap/bootstrap.py +++ b/examples/bootstrap/bootstrap.py @@ -53,7 +53,11 @@ def on_peer_discovery(peer_info: PeerInfo) -> None: async def run(port: int, bootstrap_addrs: list[str]) -> None: """Run the bootstrap discovery example.""" - from libp2p.utils.address_validation import find_free_port, get_available_interfaces + from libp2p.utils.address_validation import ( + find_free_port, + get_available_interfaces, + get_optimal_binding_address, + ) if port <= 0: port = find_free_port() @@ -93,6 +97,12 @@ async def run(port: int, bootstrap_addrs: list[str]) -> None: logger.info(f"{addr}") print(f"{addr}") + # Display optimal address for reference + optimal_addr = get_optimal_binding_address(port) + optimal_addr_with_peer = f"{optimal_addr}/p2p/{host.get_id().to_string()}" + logger.info(f"Optimal address: {optimal_addr_with_peer}") + print(f"Optimal address: {optimal_addr_with_peer}") + # Keep running and log peer discovery events await trio.sleep_forever() except KeyboardInterrupt: diff --git a/examples/chat/chat.py b/examples/chat/chat.py index 35f98d257..ee133af1a 100755 --- a/examples/chat/chat.py +++ b/examples/chat/chat.py @@ -46,7 +46,11 @@ async def write_data(stream: INetStream) -> None: async def run(port: int, destination: str) -> None: - from libp2p.utils.address_validation import find_free_port, get_available_interfaces + from libp2p.utils.address_validation import ( + find_free_port, + get_available_interfaces, + get_optimal_binding_address, + ) if port <= 0: port = find_free_port() @@ -72,11 +76,12 @@ async def stream_handler(stream: INetStream) -> None: for addr in all_addrs: print(f"{addr}") - # Use the first address as the default for the client command - default_addr = all_addrs[0] + # Use optimal address for the client command + optimal_addr = get_optimal_binding_address(port) + optimal_addr_with_peer = f"{optimal_addr}/p2p/{host.get_id().to_string()}" print( f"\nRun this from the same folder in another console:\n\n" - f"chat-demo -d {default_addr}\n" + f"chat-demo -d {optimal_addr_with_peer}\n" ) print("Waiting for incoming connection...") diff --git a/examples/doc-examples/example_multiplexer.py b/examples/doc-examples/example_multiplexer.py index 6963ace01..63a29fc57 100644 --- a/examples/doc-examples/example_multiplexer.py +++ b/examples/doc-examples/example_multiplexer.py @@ -1,6 +1,5 @@ import secrets -import multiaddr import trio from libp2p import ( @@ -13,6 +12,10 @@ PROTOCOL_ID as NOISE_PROTOCOL_ID, Transport as NoiseTransport, ) +from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, +) async def main(): @@ -39,14 +42,16 @@ async def main(): # Create a host with the key pair, Noise security, and mplex multiplexer host = new_host(key_pair=key_pair, sec_opt=security_options) - # Configure the listening address + # Configure the listening address using the new paradigm port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + listen_addrs = get_available_interfaces(port) + optimal_addr = get_optimal_binding_address(port) # Start the host - async with host.run(listen_addrs=[listen_addr]): + async with host.run(listen_addrs=listen_addrs): print("libp2p has started with Noise encryption and mplex multiplexing") print("libp2p is listening on:", host.get_addrs()) + print(f"Optimal address: {optimal_addr}") # Keep the host running await trio.sleep_forever() diff --git a/examples/doc-examples/example_net_stream.py b/examples/doc-examples/example_net_stream.py index a77a75094..6f7eb4b04 100644 --- a/examples/doc-examples/example_net_stream.py +++ b/examples/doc-examples/example_net_stream.py @@ -38,6 +38,10 @@ from libp2p.peer.peerinfo import ( info_from_p2p_addr, ) +from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, +) PROTOCOL_ID = TProtocol("/echo/1.0.0") @@ -173,7 +177,9 @@ async def run_enhanced_demo( """ Run enhanced echo demo with NetStream state management. """ - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + # Use the new address paradigm + listen_addrs = get_available_interfaces(port) + optimal_addr = get_optimal_binding_address(port) # Generate or use provided key if seed: @@ -185,7 +191,7 @@ async def run_enhanced_demo( host = new_host(key_pair=create_new_key_pair(secret)) - async with host.run(listen_addrs=[listen_addr]): + async with host.run(listen_addrs=listen_addrs): print(f"Host ID: {host.get_id().to_string()}") print("=" * 60) @@ -196,10 +202,12 @@ async def run_enhanced_demo( # type: ignore: Stream is type of NetStream host.set_stream_handler(PROTOCOL_ID, enhanced_echo_handler) + # Use optimal address for client command + optimal_addr_with_peer = f"{optimal_addr}/p2p/{host.get_id().to_string()}" print( "Run client from another console:\n" f"python3 example_net_stream.py " - f"-d {host.get_addrs()[0]}\n" + f"-d {optimal_addr_with_peer}\n" ) print("Waiting for connections...") print("Press Ctrl+C to stop server") diff --git a/examples/doc-examples/example_running.py b/examples/doc-examples/example_running.py index 7f3ade321..2f4959793 100644 --- a/examples/doc-examples/example_running.py +++ b/examples/doc-examples/example_running.py @@ -1,6 +1,5 @@ import secrets -import multiaddr import trio from libp2p import ( @@ -13,6 +12,10 @@ PROTOCOL_ID as NOISE_PROTOCOL_ID, Transport as NoiseTransport, ) +from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, +) async def main(): @@ -39,14 +42,16 @@ async def main(): # Create a host with the key pair, Noise security, and mplex multiplexer host = new_host(key_pair=key_pair, sec_opt=security_options) - # Configure the listening address + # Configure the listening address using the new paradigm port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + listen_addrs = get_available_interfaces(port) + optimal_addr = get_optimal_binding_address(port) # Start the host - async with host.run(listen_addrs=[listen_addr]): + async with host.run(listen_addrs=listen_addrs): print("libp2p has started") print("libp2p is listening on:", host.get_addrs()) + print(f"Optimal address: {optimal_addr}") # Keep the host running await trio.sleep_forever() diff --git a/examples/doc-examples/example_transport.py b/examples/doc-examples/example_transport.py index 8f4c9fa10..9d29d457b 100644 --- a/examples/doc-examples/example_transport.py +++ b/examples/doc-examples/example_transport.py @@ -1,6 +1,5 @@ import secrets -import multiaddr import trio from libp2p import ( @@ -9,6 +8,10 @@ from libp2p.crypto.secp256k1 import ( create_new_key_pair, ) +from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, +) async def main(): @@ -19,14 +22,16 @@ async def main(): # Create a host with the key pair host = new_host(key_pair=key_pair) - # Configure the listening address + # Configure the listening address using the new paradigm port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + listen_addrs = get_available_interfaces(port) + optimal_addr = get_optimal_binding_address(port) # Start the host - async with host.run(listen_addrs=[listen_addr]): + async with host.run(listen_addrs=listen_addrs): print("libp2p has started with TCP transport") print("libp2p is listening on:", host.get_addrs()) + print(f"Optimal address: {optimal_addr}") # Keep the host running await trio.sleep_forever() diff --git a/examples/echo/echo.py b/examples/echo/echo.py index 42e3ff0cd..d998f6e89 100644 --- a/examples/echo/echo.py +++ b/examples/echo/echo.py @@ -27,6 +27,7 @@ from libp2p.utils.address_validation import ( find_free_port, get_available_interfaces, + get_optimal_binding_address, ) # Configure minimal logging @@ -82,9 +83,13 @@ async def run(port: int, destination: str, seed: int | None = None) -> None: for addr in listen_addr: print(f"{addr}/p2p/{peer_id}") + # Get optimal address for display + optimal_addr = get_optimal_binding_address(port) + optimal_addr_with_peer = f"{optimal_addr}/p2p/{peer_id}" + print( "\nRun this from the same folder in another console:\n\n" - f"echo-demo -d {host.get_addrs()[0]}\n" + f"echo-demo -d {optimal_addr_with_peer}\n" ) print("Waiting for incoming connections...") await trio.sleep_forever() diff --git a/examples/identify/identify.py b/examples/identify/identify.py index bd973a3e1..addfff894 100644 --- a/examples/identify/identify.py +++ b/examples/identify/identify.py @@ -63,7 +63,10 @@ def print_identify_response(identify_response: Identify): async def run(port: int, destination: str, use_varint_format: bool = True) -> None: - from libp2p.utils.address_validation import get_available_interfaces + from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, + ) if not destination: # Create first host (listener) @@ -100,11 +103,12 @@ async def run(port: int, destination: str, use_varint_format: bool = True) -> No for addr in all_addrs: print(f"{addr}") - # Use the first address as the default for the client command - default_addr = all_addrs[0] + # Use optimal address for the client command + optimal_addr = get_optimal_binding_address(port) + optimal_addr_with_peer = f"{optimal_addr}/p2p/{host_a.get_id().to_string()}" print( f"\nRun this from the same folder in another console:\n\n" - f"identify-demo {format_flag} -d {default_addr}\n" + f"identify-demo {format_flag} -d {optimal_addr_with_peer}\n" ) print("Waiting for incoming identify request...") @@ -152,6 +156,7 @@ async def custom_identify_handler(stream): from libp2p.utils.address_validation import ( find_free_port, get_available_interfaces, + get_optimal_binding_address, ) if port <= 0: diff --git a/examples/kademlia/kademlia.py b/examples/kademlia/kademlia.py index 80bbc9955..cf4b2988c 100644 --- a/examples/kademlia/kademlia.py +++ b/examples/kademlia/kademlia.py @@ -151,7 +151,10 @@ async def run_node( key_pair = create_new_key_pair(secrets.token_bytes(32)) host = new_host(key_pair=key_pair) - from libp2p.utils.address_validation import get_available_interfaces + from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, + ) listen_addrs = get_available_interfaces(port) @@ -168,9 +171,10 @@ async def run_node( for addr in all_addrs: logger.info(f"{addr}") - # Use the first address as the default for the bootstrap command - default_addr = all_addrs[0] - bootstrap_cmd = f"--bootstrap {default_addr}" + # Use optimal address for the bootstrap command + optimal_addr = get_optimal_binding_address(port) + optimal_addr_with_peer = f"{optimal_addr}/p2p/{host.get_id().to_string()}" + bootstrap_cmd = f"--bootstrap {optimal_addr_with_peer}" logger.info("To connect to this node, use: %s", bootstrap_cmd) await connect_to_bootstrap_nodes(host, bootstrap_nodes) @@ -182,7 +186,7 @@ async def run_node( # Save server address in server mode if dht_mode == DHTMode.SERVER: - save_server_addr(str(default_addr)) + save_server_addr(str(optimal_addr_with_peer)) # Start the DHT service async with background_trio_service(dht): diff --git a/examples/ping/ping.py b/examples/ping/ping.py index 52bb759a3..5c7f54e46 100644 --- a/examples/ping/ping.py +++ b/examples/ping/ping.py @@ -61,7 +61,11 @@ async def send_ping(stream: INetStream) -> None: async def run(port: int, destination: str) -> None: - from libp2p.utils.address_validation import find_free_port, get_available_interfaces + from libp2p.utils.address_validation import ( + find_free_port, + get_available_interfaces, + get_optimal_binding_address, + ) if port <= 0: port = find_free_port() @@ -83,11 +87,12 @@ async def run(port: int, destination: str) -> None: for addr in all_addrs: print(f"{addr}") - # Use the first address as the default for the client command - default_addr = all_addrs[0] + # Use optimal address for the client command + optimal_addr = get_optimal_binding_address(port) + optimal_addr_with_peer = f"{optimal_addr}/p2p/{host.get_id().to_string()}" print( f"\nRun this from the same folder in another console:\n\n" - f"ping-demo -d {default_addr}\n" + f"ping-demo -d {optimal_addr_with_peer}\n" ) print("Waiting for incoming connection...") diff --git a/examples/pubsub/pubsub.py b/examples/pubsub/pubsub.py index 6e8495c10..adb3a1d0e 100644 --- a/examples/pubsub/pubsub.py +++ b/examples/pubsub/pubsub.py @@ -102,7 +102,10 @@ async def monitor_peer_topics(pubsub, nursery, termination_event): async def run(topic: str, destination: str | None, port: int | None) -> None: - from libp2p.utils.address_validation import get_available_interfaces + from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, + ) if port is None or port == 0: port = find_free_port() @@ -162,11 +165,14 @@ async def run(topic: str, destination: str | None, port: int | None) -> None: for addr in all_addrs: logger.info(f"{addr}") - # Use the first address as the default for the client command - default_addr = all_addrs[0] + # Use optimal address for the client command + optimal_addr = get_optimal_binding_address(port) + optimal_addr_with_peer = ( + f"{optimal_addr}/p2p/{host.get_id().to_string()}" + ) logger.info( f"\nRun this from the same folder in another console:\n\n" - f"pubsub-demo -d {default_addr}\n" + f"pubsub-demo -d {optimal_addr_with_peer}\n" ) logger.info("Waiting for peers...") diff --git a/libp2p/utils/address_validation.py b/libp2p/utils/address_validation.py index a470ad243..5ce58671f 100644 --- a/libp2p/utils/address_validation.py +++ b/libp2p/utils/address_validation.py @@ -3,38 +3,24 @@ import socket from multiaddr import Multiaddr - -try: - from multiaddr.utils import ( # type: ignore - get_network_addrs, - get_thin_waist_addresses, - ) - - _HAS_THIN_WAIST = True -except ImportError: # pragma: no cover - only executed in older environments - _HAS_THIN_WAIST = False - get_thin_waist_addresses = None # type: ignore - get_network_addrs = None # type: ignore +from multiaddr.utils import get_network_addrs, get_thin_waist_addresses def _safe_get_network_addrs(ip_version: int) -> list[str]: """ Internal safe wrapper. Returns a list of IP addresses for the requested IP version. - Falls back to minimal defaults when Thin Waist helpers are missing. :param ip_version: 4 or 6 """ - if _HAS_THIN_WAIST and get_network_addrs: - try: - return get_network_addrs(ip_version) or [] - except Exception: # pragma: no cover - defensive - return [] - # Fallback behavior (very conservative) - if ip_version == 4: - return ["127.0.0.1"] - if ip_version == 6: - return ["::1"] - return [] + try: + return get_network_addrs(ip_version) or [] + except Exception: # pragma: no cover - defensive + # Fallback behavior (very conservative) + if ip_version == 4: + return ["127.0.0.1"] + if ip_version == 6: + return ["::1"] + return [] def find_free_port() -> int: @@ -47,16 +33,13 @@ def find_free_port() -> int: def _safe_expand(addr: Multiaddr, port: int | None = None) -> list[Multiaddr]: """ Internal safe expansion wrapper. Returns a list of Multiaddr objects. - If Thin Waist isn't available, returns [addr] (identity). """ - if _HAS_THIN_WAIST and get_thin_waist_addresses: - try: - if port is not None: - return get_thin_waist_addresses(addr, port=port) or [] - return get_thin_waist_addresses(addr) or [] - except Exception: # pragma: no cover - defensive - return [addr] - return [addr] + try: + if port is not None: + return get_thin_waist_addresses(addr, port=port) or [] + return get_thin_waist_addresses(addr) or [] + except Exception: # pragma: no cover - defensive + return [addr] def get_available_interfaces(port: int, protocol: str = "tcp") -> list[Multiaddr]: @@ -122,6 +105,20 @@ def expand_wildcard_address( return expanded +def get_wildcard_address(port: int, protocol: str = "tcp") -> Multiaddr: + """ + Get wildcard address (0.0.0.0) when explicitly needed. + + This function provides access to wildcard binding as a feature when + explicitly required, preserving the ability to bind to all interfaces. + + :param port: Port number. + :param protocol: Transport protocol. + :return: A Multiaddr with wildcard binding (0.0.0.0). + """ + return Multiaddr(f"/ip4/0.0.0.0/{protocol}/{port}") + + def get_optimal_binding_address(port: int, protocol: str = "tcp") -> Multiaddr: """ Choose an optimal address for an example to bind to: @@ -157,6 +154,7 @@ def is_non_loopback(ma: Multiaddr) -> bool: __all__ = [ "get_available_interfaces", "get_optimal_binding_address", + "get_wildcard_address", "expand_wildcard_address", "find_free_port", ] diff --git a/tests/examples/test_examples_bind_address.py b/tests/examples/test_examples_bind_address.py index 1045c90ba..c0dd9de36 100644 --- a/tests/examples/test_examples_bind_address.py +++ b/tests/examples/test_examples_bind_address.py @@ -1,12 +1,12 @@ """ -Tests to verify that all examples use 127.0.0.1 instead of 0.0.0.0 +Tests to verify that all examples use the new address paradigm consistently """ from pathlib import Path -class TestExamplesBindAddress: - """Test suite to verify all examples use secure bind addresses""" +class TestExamplesAddressParadigm: + """Test suite to verify all examples use the new address paradigm consistently""" def get_example_files(self): """Get all Python files in the examples directory""" @@ -32,49 +32,26 @@ def check_file_for_wildcard_binding(self, filepath): return found_wildcards - def test_no_wildcard_binding_in_examples(self): - """Test that no example files use 0.0.0.0 for binding""" + def test_examples_use_address_paradigm(self): + """Test that examples use the new address paradigm functions""" example_files = self.get_example_files() - # Skip certain files that might legitimately discuss wildcards - skip_files = [ - "network_discover.py", # This demonstrates wildcard expansion - ] - - files_with_wildcards = {} - - for filepath in example_files: - if any(skip in str(filepath) for skip in skip_files): - continue - - wildcards = self.check_file_for_wildcard_binding(filepath) - if wildcards: - files_with_wildcards[str(filepath)] = wildcards - - # Assert no wildcards found - if files_with_wildcards: - error_msg = "Found wildcard bindings in example files:\n" - for filepath, occurrences in files_with_wildcards.items(): - error_msg += f"\n{filepath}:\n" - for line_num, line in occurrences: - error_msg += f" Line {line_num}: {line}\n" - - assert False, error_msg - - def test_examples_use_loopback_address(self): - """Test that examples use 127.0.0.1 for local binding""" - example_files = self.get_example_files() - - # Files that should contain listen addresses - files_with_networking = [ - "ping/ping.py", + # Files that should use the new paradigm + networking_examples = [ + "echo/echo.py", "chat/chat.py", + "ping/ping.py", "bootstrap/bootstrap.py", "pubsub/pubsub.py", "identify/identify.py", ] - for filename in files_with_networking: + paradigm_functions = [ + "get_available_interfaces", + "get_optimal_binding_address", + ] + + for filename in networking_examples: filepath = None for example_file in example_files: if filename in str(example_file): @@ -87,24 +64,54 @@ def test_examples_use_loopback_address(self): with open(filepath, encoding="utf-8") as f: content = f.read() - # Check for proper loopback usage - has_loopback = "127.0.0.1" in content or "localhost" in content - has_multiaddr_loopback = "/ip4/127.0.0.1/" in content + # Check that the file uses the new paradigm functions + for func in paradigm_functions: + assert func in content, ( + f"{filepath} should use {func} from the new address paradigm" + ) + + def test_wildcard_available_as_feature(self): + """Test that wildcard is available as a feature when needed""" + example_files = self.get_example_files() + + # Check that network_discover.py demonstrates wildcard usage + network_discover_file = None + for example_file in example_files: + if "network_discover.py" in str(example_file): + network_discover_file = example_file + break - assert has_loopback or has_multiaddr_loopback, ( - f"{filepath} should use loopback address (127.0.0.1)" + if network_discover_file: + with open(network_discover_file, encoding="utf-8") as f: + content = f.read() + + # Should demonstrate wildcard expansion + assert "0.0.0.0" in content, ( + f"{network_discover_file} should demonstrate wildcard usage" + ) + assert "expand_wildcard_address" in content, ( + f"{network_discover_file} should use expand_wildcard_address" ) - def test_doc_examples_use_loopback(self): - """Test that documentation examples use secure addresses""" + def test_doc_examples_use_paradigm(self): + """Test that documentation examples use the new address paradigm""" doc_examples_dir = Path("examples/doc-examples") if not doc_examples_dir.exists(): return doc_example_files = list(doc_examples_dir.glob("*.py")) + paradigm_functions = [ + "get_available_interfaces", + "get_optimal_binding_address", + ] + for filepath in doc_example_files: - wildcards = self.check_file_for_wildcard_binding(filepath) - assert not wildcards, ( - f"Documentation example {filepath} contains wildcard binding" - ) + with open(filepath, encoding="utf-8") as f: + content = f.read() + + # Check that doc examples use the new paradigm + for func in paradigm_functions: + assert func in content, ( + f"Documentation example {filepath} should use {func}" + ) diff --git a/tests/utils/test_default_bind_address.py b/tests/utils/test_default_bind_address.py index b8a501d2e..b0598b5a0 100644 --- a/tests/utils/test_default_bind_address.py +++ b/tests/utils/test_default_bind_address.py @@ -1,5 +1,5 @@ """ -Tests for default bind address changes from 0.0.0.0 to 127.0.0.1 +Tests for the new address paradigm with wildcard support as a feature """ import pytest @@ -9,28 +9,43 @@ from libp2p.utils.address_validation import ( get_available_interfaces, get_optimal_binding_address, + get_wildcard_address, ) -class TestDefaultBindAddress: +class TestAddressParadigm: """ - Test suite for verifying default bind addresses use - secure addresses (not 0.0.0.0) + Test suite for verifying the new address paradigm: + - get_available_interfaces() returns all available interfaces + - get_optimal_binding_address() returns optimal address for examples + - get_wildcard_address() provides wildcard as a feature when needed """ - def test_default_bind_address_is_not_wildcard(self): - """Test that default bind address is NOT 0.0.0.0 (wildcard)""" + def test_wildcard_address_function(self): + """Test that get_wildcard_address() provides wildcard as a feature""" port = 8000 - addr = get_optimal_binding_address(port) + addr = get_wildcard_address(port) - # Should NOT return wildcard address - assert "0.0.0.0" not in str(addr) + # Should return wildcard address when explicitly requested + assert "0.0.0.0" in str(addr) + addr_str = str(addr) + assert "/ip4/" in addr_str + assert f"/tcp/{port}" in addr_str + + def test_optimal_binding_address_selection(self): + """Test that optimal binding address uses good heuristics""" + port = 8000 + addr = get_optimal_binding_address(port) # Should return a valid IP address (could be loopback or local network) addr_str = str(addr) assert "/ip4/" in addr_str assert f"/tcp/{port}" in addr_str + # Should be from available interfaces + available_interfaces = get_available_interfaces(port) + assert addr in available_interfaces + def test_available_interfaces_includes_loopback(self): """Test that available interfaces always includes loopback address""" port = 8000 @@ -43,9 +58,12 @@ def test_available_interfaces_includes_loopback(self): loopback_found = any("127.0.0.1" in str(addr) for addr in interfaces) assert loopback_found, "Loopback address not found in available interfaces" - # Should not have wildcard as the only option - if len(interfaces) == 1: - assert "0.0.0.0" not in str(interfaces[0]) + # Available interfaces should not include wildcard by default + # (wildcard is available as a feature through get_wildcard_address()) + wildcard_found = any("0.0.0.0" in str(addr) for addr in interfaces) + assert not wildcard_found, ( + "Wildcard should not be in default available interfaces" + ) def test_host_default_listen_address(self): """Test that new hosts use secure default addresses""" @@ -59,55 +77,66 @@ def test_host_default_listen_address(self): # Note: We can't test actual binding without running the host, # but we've verified the address format is correct - def test_no_wildcard_in_fallback(self): - """Test that fallback addresses don't use wildcard binding""" - # When no interfaces are discovered, fallback should be loopback + def test_paradigm_consistency(self): + """Test that the address paradigm is consistent""" port = 8000 - # Even if we can't discover interfaces, we should get loopback - addr = get_optimal_binding_address(port) - # Should NOT be wildcard - assert "0.0.0.0" not in str(addr) + # get_optimal_binding_address should return a valid address + optimal_addr = get_optimal_binding_address(port) + assert "/ip4/" in str(optimal_addr) + assert f"/tcp/{port}" in str(optimal_addr) - # Should be a valid IP address - addr_str = str(addr) - assert "/ip4/" in addr_str - assert f"/tcp/{port}" in addr_str + # get_wildcard_address should return wildcard when explicitly needed + wildcard_addr = get_wildcard_address(port) + assert "0.0.0.0" in str(wildcard_addr) + assert f"/tcp/{port}" in str(wildcard_addr) + + # Both should be valid Multiaddr objects + assert isinstance(optimal_addr, Multiaddr) + assert isinstance(wildcard_addr, Multiaddr) @pytest.mark.parametrize("protocol", ["tcp", "udp"]) - def test_different_protocols_use_secure_addresses(self, protocol): - """Test that different protocols still use secure addresses by default""" + def test_different_protocols_support(self, protocol): + """Test that different protocols are supported by the paradigm""" port = 8000 - addr = get_optimal_binding_address(port, protocol=protocol) - # Should NOT be wildcard - assert "0.0.0.0" not in str(addr) - assert protocol in str(addr) + # Test optimal address with different protocols + optimal_addr = get_optimal_binding_address(port, protocol=protocol) + assert protocol in str(optimal_addr) + assert f"/{protocol}/{port}" in str(optimal_addr) - # Should be a valid IP address - addr_str = str(addr) - assert "/ip4/" in addr_str - assert f"/{protocol}/{port}" in addr_str + # Test wildcard address with different protocols + wildcard_addr = get_wildcard_address(port, protocol=protocol) + assert "0.0.0.0" in str(wildcard_addr) + assert protocol in str(wildcard_addr) + assert f"/{protocol}/{port}" in str(wildcard_addr) + + # Test available interfaces with different protocols + interfaces = get_available_interfaces(port, protocol=protocol) + assert len(interfaces) > 0 + for addr in interfaces: + assert protocol in str(addr) - def test_security_no_public_binding_by_default(self): - """Test that no public interface binding occurs by default""" + def test_wildcard_available_as_feature(self): + """Test that wildcard binding is available as a feature when needed""" port = 8000 - interfaces = get_available_interfaces(port) - # Check that we don't expose on all interfaces by default - wildcard_addrs = [addr for addr in interfaces if "0.0.0.0" in str(addr)] - assert len(wildcard_addrs) == 0, ( - "Found wildcard addresses in default interfaces" + # Wildcard should be available through get_wildcard_address() + wildcard_addr = get_wildcard_address(port) + assert "0.0.0.0" in str(wildcard_addr) + + # But should not be in default available interfaces + interfaces = get_available_interfaces(port) + wildcard_in_interfaces = any("0.0.0.0" in str(addr) for addr in interfaces) + assert not wildcard_in_interfaces, ( + "Wildcard should not be in default interfaces" ) - # Verify optimal address selection doesn't choose wildcard + # Optimal address should not be wildcard by default optimal = get_optimal_binding_address(port) - assert "0.0.0.0" not in str(optimal), "Optimal address should not be wildcard" - - # Should be a valid IP address (could be loopback or local network) - addr_str = str(optimal) - assert "/ip4/" in addr_str - assert f"/tcp/{port}" in addr_str + assert "0.0.0.0" not in str(optimal), ( + "Optimal address should not be wildcard by default" + ) def test_loopback_is_always_available(self): """Test that loopback address is always available as an option""" @@ -132,9 +161,6 @@ def test_optimal_address_selection_behavior(self): interfaces = get_available_interfaces(port) optimal = get_optimal_binding_address(port) - # Should never return wildcard - assert "0.0.0.0" not in str(optimal) - # Should return one of the available interfaces optimal_str = str(optimal) interface_strs = [str(addr) for addr in interfaces] @@ -142,7 +168,7 @@ def test_optimal_address_selection_behavior(self): f"Optimal address {optimal_str} should be in available interfaces" ) - # If non-loopback interfaces are available, should prefer them + # Should prefer non-loopback when available, fallback to loopback non_loopback_interfaces = [ addr for addr in interfaces if "127.0.0.1" not in str(addr) ] @@ -157,23 +183,21 @@ def test_optimal_address_selection_behavior(self): "Should use loopback when no other interfaces available" ) - def test_address_validation_utilities_behavior(self): - """Test that address validation utilities behave as expected""" + def test_address_paradigm_completeness(self): + """Test that the address paradigm provides all necessary functionality""" port = 8000 - # Test that we get multiple interface options + # Test that we get interface options interfaces = get_available_interfaces(port) - assert len(interfaces) >= 2, ( - "Should have at least loopback + one network interface" - ) + assert len(interfaces) >= 1, "Should have at least one interface" # Test that loopback is always included has_loopback = any("127.0.0.1" in str(addr) for addr in interfaces) assert has_loopback, "Loopback should always be available" - # Test that no wildcards are included - has_wildcard = any("0.0.0.0" in str(addr) for addr in interfaces) - assert not has_wildcard, "Wildcard addresses should never be included" + # Test that wildcard is available as a feature + wildcard_addr = get_wildcard_address(port) + assert "0.0.0.0" in str(wildcard_addr) # Test optimal selection optimal = get_optimal_binding_address(port) From 4dd2454a467dc6fc3b62117fba90bbd55c2aad1f Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Thu, 18 Sep 2025 02:09:51 +0530 Subject: [PATCH 11/17] Update examples to use dynamic host IP instead of hardcoded localhost --- examples/advanced/network_discover.py | 23 ++++++++++++++-- examples/bootstrap/bootstrap.py | 2 +- examples/chat/chat.py | 2 +- .../example_encryption_insecure.py | 18 +++++++------ .../doc-examples/example_encryption_noise.py | 13 ++++++--- .../doc-examples/example_encryption_secio.py | 13 ++++++--- examples/doc-examples/example_net_stream.py | 2 +- .../doc-examples/example_peer_discovery.py | 13 ++++++--- .../doc-examples/example_quic_transport.py | 21 ++++++++++++--- examples/echo/echo.py | 2 +- examples/echo/echo_quic.py | 27 +++++++++++++------ examples/identify/identify.py | 2 +- examples/identify_push/identify_push_demo.py | 13 +++++---- .../identify_push_listener_dialer.py | 10 ++++--- examples/ping/ping.py | 2 +- 15 files changed, 114 insertions(+), 49 deletions(-) diff --git a/examples/advanced/network_discover.py b/examples/advanced/network_discover.py index 13f7d03aa..5bf13c5a5 100644 --- a/examples/advanced/network_discover.py +++ b/examples/advanced/network_discover.py @@ -17,9 +17,22 @@ get_wildcard_address, ) except ImportError: - # Fallbacks if utilities are missing + # Fallbacks if utilities are missing - use minimal network discovery + import socket def get_available_interfaces(port: int, protocol: str = "tcp"): - return [Multiaddr(f"/ip4/127.0.0.1/{protocol}/{port}")] + # Try to get local network interfaces, fallback to loopback + addrs = [] + try: + # Get hostname IP (better than hardcoded localhost) + hostname = socket.gethostname() + local_ip = socket.gethostbyname(hostname) + if local_ip != "127.0.0.1": + addrs.append(Multiaddr(f"/ip4/{local_ip}/{protocol}/{port}")) + except exception: + pass + # Always include loopback as fallback + addrs.append(Multiaddr(f"/ip4/127.0.0.1/{protocol}/{port}")) + return addrs def expand_wildcard_address(addr: Multiaddr, port: int | None = None): if port is None: @@ -28,6 +41,12 @@ def expand_wildcard_address(addr: Multiaddr, port: int | None = None): return [Multiaddr(addr_str + f"/{port}")] def get_optimal_binding_address(port: int, protocol: str = "tcp"): + # Try to get a non-loopback address first + interfaces = get_available_interfaces(port, protocol) + for addr in interfaces: + if "127.0.0.1" not in str(addr): + return addr + # Fallback to loopback if no other interfaces found return Multiaddr(f"/ip4/127.0.0.1/{protocol}/{port}") def get_wildcard_address(port: int, protocol: str = "tcp"): diff --git a/examples/bootstrap/bootstrap.py b/examples/bootstrap/bootstrap.py index b4fa9234c..70ac3b0a9 100644 --- a/examples/bootstrap/bootstrap.py +++ b/examples/bootstrap/bootstrap.py @@ -120,7 +120,7 @@ def main() -> None: Usage: python bootstrap.py -p 8000 python bootstrap.py -p 8001 --custom-bootstrap \\ - "/ip4/127.0.0.1/tcp/8000/p2p/QmYourPeerID" + "/ip4/[HOST_IP]/tcp/8000/p2p/QmYourPeerID" """ parser = argparse.ArgumentParser( diff --git a/examples/chat/chat.py b/examples/chat/chat.py index ee133af1a..80b627e5e 100755 --- a/examples/chat/chat.py +++ b/examples/chat/chat.py @@ -110,7 +110,7 @@ def main() -> None: where is the multiaddress of the previous listener host. """ example_maddr = ( - "/ip4/127.0.0.1/tcp/8000/p2p/QmQn4SwGkDZKkUEpBRBvTmheQycxAHJUNmVEnjA2v1qe8Q" + "/ip4/[HOST_IP]/tcp/8000/p2p/QmQn4SwGkDZKkUEpBRBvTmheQycxAHJUNmVEnjA2v1qe8Q" ) parser = argparse.ArgumentParser(description=description) parser.add_argument("-p", "--port", default=0, type=int, help="source port number") diff --git a/examples/doc-examples/example_encryption_insecure.py b/examples/doc-examples/example_encryption_insecure.py index 089fb72fd..6c1455795 100644 --- a/examples/doc-examples/example_encryption_insecure.py +++ b/examples/doc-examples/example_encryption_insecure.py @@ -1,6 +1,5 @@ import secrets -import multiaddr import trio from libp2p import ( @@ -9,9 +8,10 @@ from libp2p.crypto.secp256k1 import ( create_new_key_pair, ) -from libp2p.security.insecure.transport import ( - PLAINTEXT_PROTOCOL_ID, - InsecureTransport, +from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID, Transport +from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, ) @@ -21,7 +21,7 @@ async def main(): key_pair = create_new_key_pair(secret) # Create an insecure transport (not recommended for production) - insecure_transport = InsecureTransport( + insecure_transport = Transport( # local_key_pair: The key pair used for libp2p identity local_key_pair=key_pair, # secure_bytes_provider: Optional function to generate secure random bytes @@ -38,17 +38,19 @@ async def main(): # Create a host with the key pair and insecure transport host = new_host(key_pair=key_pair, sec_opt=security_options) - # Configure the listening address + # Configure the listening address using the new paradigm port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + listen_addrs = get_available_interfaces(port) + optimal_addr = get_optimal_binding_address(port) # Start the host - async with host.run(listen_addrs=[listen_addr]): + async with host.run(listen_addrs=listen_addrs): print( "libp2p has started with insecure transport " "(not recommended for production)" ) print("libp2p is listening on:", host.get_addrs()) + print(f"Optimal address: {optimal_addr}") # Keep the host running await trio.sleep_forever() diff --git a/examples/doc-examples/example_encryption_noise.py b/examples/doc-examples/example_encryption_noise.py index 7d037610b..4138354f9 100644 --- a/examples/doc-examples/example_encryption_noise.py +++ b/examples/doc-examples/example_encryption_noise.py @@ -1,6 +1,5 @@ import secrets -import multiaddr import trio from libp2p import ( @@ -13,6 +12,10 @@ PROTOCOL_ID as NOISE_PROTOCOL_ID, Transport as NoiseTransport, ) +from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, +) async def main(): @@ -39,14 +42,16 @@ async def main(): # Create a host with the key pair and Noise security host = new_host(key_pair=key_pair, sec_opt=security_options) - # Configure the listening address + # Configure the listening address using the new paradigm port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + listen_addrs = get_available_interfaces(port) + optimal_addr = get_optimal_binding_address(port) # Start the host - async with host.run(listen_addrs=[listen_addr]): + async with host.run(listen_addrs=listen_addrs): print("libp2p has started with Noise encryption") print("libp2p is listening on:", host.get_addrs()) + print(f"Optimal address: {optimal_addr}") # Keep the host running await trio.sleep_forever() diff --git a/examples/doc-examples/example_encryption_secio.py b/examples/doc-examples/example_encryption_secio.py index 3b1cb4056..b90c28bba 100644 --- a/examples/doc-examples/example_encryption_secio.py +++ b/examples/doc-examples/example_encryption_secio.py @@ -1,6 +1,5 @@ import secrets -import multiaddr import trio from libp2p import ( @@ -13,6 +12,10 @@ ID as SECIO_PROTOCOL_ID, Transport as SecioTransport, ) +from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, +) async def main(): @@ -32,14 +35,16 @@ async def main(): # Create a host with the key pair and SECIO security host = new_host(key_pair=key_pair, sec_opt=security_options) - # Configure the listening address + # Configure the listening address using the new paradigm port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + listen_addrs = get_available_interfaces(port) + optimal_addr = get_optimal_binding_address(port) # Start the host - async with host.run(listen_addrs=[listen_addr]): + async with host.run(listen_addrs=listen_addrs): print("libp2p has started with SECIO encryption") print("libp2p is listening on:", host.get_addrs()) + print(f"Optimal address: {optimal_addr}") # Keep the host running await trio.sleep_forever() diff --git a/examples/doc-examples/example_net_stream.py b/examples/doc-examples/example_net_stream.py index 6f7eb4b04..edd2ac907 100644 --- a/examples/doc-examples/example_net_stream.py +++ b/examples/doc-examples/example_net_stream.py @@ -234,7 +234,7 @@ async def run_enhanced_demo( def main() -> None: example_maddr = ( - "/ip4/127.0.0.1/tcp/8000/p2p/QmQn4SwGkDZKkUEpBRBvTmheQycxAHJUNmVEnjA2v1qe8Q" + "/ip4/[HOST_IP]/tcp/8000/p2p/QmQn4SwGkDZKkUEpBRBvTmheQycxAHJUNmVEnjA2v1qe8Q" ) parser = argparse.ArgumentParser( diff --git a/examples/doc-examples/example_peer_discovery.py b/examples/doc-examples/example_peer_discovery.py index eb3e19145..de69e4e18 100644 --- a/examples/doc-examples/example_peer_discovery.py +++ b/examples/doc-examples/example_peer_discovery.py @@ -1,6 +1,5 @@ import secrets -import multiaddr import trio from libp2p import ( @@ -16,6 +15,10 @@ PROTOCOL_ID as NOISE_PROTOCOL_ID, Transport as NoiseTransport, ) +from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, +) async def main(): @@ -42,14 +45,16 @@ async def main(): # Create a host with the key pair, Noise security, and mplex multiplexer host = new_host(key_pair=key_pair, sec_opt=security_options) - # Configure the listening address + # Configure the listening address using the new paradigm port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + listen_addrs = get_available_interfaces(port) + optimal_addr = get_optimal_binding_address(port) # Start the host - async with host.run(listen_addrs=[listen_addr]): + async with host.run(listen_addrs=listen_addrs): print("libp2p has started") print("libp2p is listening on:", host.get_addrs()) + print(f"Optimal address: {optimal_addr}") # Connect to bootstrap peers manually bootstrap_list = [ diff --git a/examples/doc-examples/example_quic_transport.py b/examples/doc-examples/example_quic_transport.py index 2ec45c2d4..3ee6fb515 100644 --- a/examples/doc-examples/example_quic_transport.py +++ b/examples/doc-examples/example_quic_transport.py @@ -1,6 +1,5 @@ import secrets -import multiaddr import trio from libp2p import ( @@ -9,6 +8,10 @@ from libp2p.crypto.secp256k1 import ( create_new_key_pair, ) +from libp2p.utils.address_validation import ( + get_available_interfaces, + get_optimal_binding_address, +) async def main(): @@ -19,14 +22,24 @@ async def main(): # Create a host with the key pair host = new_host(key_pair=key_pair, enable_quic=True) - # Configure the listening address + # Configure the listening address using the new paradigm port = 8000 - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/udp/{port}/quic-v1") + listen_addrs = get_available_interfaces(port, protocol="udp") + # Convert TCP addresses to QUIC-v1 addresses + quic_addrs = [] + for addr in listen_addrs: + addr_str = str(addr).replace("/tcp/", "/udp/") + "/quic-v1" + from multiaddr import Multiaddr + quic_addrs.append(Multiaddr(addr_str)) + + optimal_addr = get_optimal_binding_address(port, protocol="udp") + optimal_quic_str = str(optimal_addr).replace("/tcp/", "/udp/") + "/quic-v1" # Start the host - async with host.run(listen_addrs=[listen_addr]): + async with host.run(listen_addrs=quic_addrs): print("libp2p has started with QUIC transport") print("libp2p is listening on:", host.get_addrs()) + print(f"Optimal address: {optimal_quic_str}") # Keep the host running await trio.sleep_forever() diff --git a/examples/echo/echo.py b/examples/echo/echo.py index d998f6e89..f95c9add6 100644 --- a/examples/echo/echo.py +++ b/examples/echo/echo.py @@ -125,7 +125,7 @@ def main() -> None: where is the multiaddress of the previous listener host. """ example_maddr = ( - "/ip4/127.0.0.1/tcp/8000/p2p/QmQn4SwGkDZKkUEpBRBvTmheQycxAHJUNmVEnjA2v1qe8Q" + "/ip4/[HOST_IP]/tcp/8000/p2p/QmQn4SwGkDZKkUEpBRBvTmheQycxAHJUNmVEnjA2v1qe8Q" ) parser = argparse.ArgumentParser(description=description) parser.add_argument("-p", "--port", default=0, type=int, help="source port number") diff --git a/examples/echo/echo_quic.py b/examples/echo/echo_quic.py index 700db1ded..ae6b826d3 100644 --- a/examples/echo/echo_quic.py +++ b/examples/echo/echo_quic.py @@ -43,13 +43,22 @@ async def _echo_stream_handler(stream: INetStream) -> None: async def run_server(port: int, seed: int | None = None) -> None: """Run echo server with QUIC transport.""" - from libp2p.utils.address_validation import find_free_port + from libp2p.utils.address_validation import ( + find_free_port, + get_available_interfaces, + get_optimal_binding_address, + ) if port <= 0: port = find_free_port() - # For QUIC, we need to use UDP addresses - use loopback for security - listen_addr = Multiaddr(f"/ip4/127.0.0.1/udp/{port}/quic") + # For QUIC, we need UDP addresses - use the new address paradigm + tcp_addrs = get_available_interfaces(port) + # Convert TCP addresses to QUIC addresses + quic_addrs = [] + for addr in tcp_addrs: + addr_str = str(addr).replace("/tcp/", "/udp/") + "/quic" + quic_addrs.append(Multiaddr(addr_str)) if seed: import random @@ -69,7 +78,7 @@ async def run_server(port: int, seed: int | None = None) -> None: ) # Server mode: start listener - async with host.run(listen_addrs=[listen_addr]): + async with host.run(listen_addrs=quic_addrs): try: print(f"I am {host.get_id().to_string()}") host.set_stream_handler(PROTOCOL_ID, _echo_stream_handler) @@ -81,11 +90,13 @@ async def run_server(port: int, seed: int | None = None) -> None: for addr in all_addrs: print(f"{addr}") - # Use the first address as the default for the client command - default_addr = all_addrs[0] + # Use optimal address for the client command + optimal_tcp = get_optimal_binding_address(port) + optimal_quic_str = str(optimal_tcp).replace("/tcp/", "/udp/") + "/quic" + optimal_quic_with_peer = f"{optimal_quic_str}/p2p/{host.get_id().to_string()}" print( f"\nRun this from the same folder in another console:\n\n" - f"python3 ./examples/echo/echo_quic.py -d {default_addr}\n" + f"python3 ./examples/echo/echo_quic.py -d {optimal_quic_with_peer}\n" ) print("Waiting for incoming QUIC connections...") await trio.sleep_forever() @@ -167,7 +178,7 @@ def main() -> None: where is the QUIC multiaddress of the previous listener host. """ - example_maddr = "/ip4/127.0.0.1/udp/8000/quic/p2p/QmQn4SwGkDZKkUEpBRBv" + example_maddr = "/ip4/[HOST_IP]/udp/8000/quic/p2p/QmQn4SwGkDZKkUEpBRBv" parser = argparse.ArgumentParser(description=description) parser.add_argument("-p", "--port", default=0, type=int, help="UDP port number") diff --git a/examples/identify/identify.py b/examples/identify/identify.py index addfff894..01b270f0c 100644 --- a/examples/identify/identify.py +++ b/examples/identify/identify.py @@ -262,7 +262,7 @@ def main() -> None: """ example_maddr = ( - "/ip4/127.0.0.1/tcp/8888/p2p/QmQn4SwGkDZKkUEpBRBvTmheQycxAHJUNmVEnjA2v1qe8Q" + "/ip4/[HOST_IP]/tcp/8888/p2p/QmQn4SwGkDZKkUEpBRBvTmheQycxAHJUNmVEnjA2v1qe8Q" ) parser = argparse.ArgumentParser(description=description) diff --git a/examples/identify_push/identify_push_demo.py b/examples/identify_push/identify_push_demo.py index ccd8b29d2..98e1e937f 100644 --- a/examples/identify_push/identify_push_demo.py +++ b/examples/identify_push/identify_push_demo.py @@ -36,6 +36,9 @@ from libp2p.peer.peerinfo import ( info_from_p2p_addr, ) +from libp2p.utils.address_validation import ( + get_available_interfaces, +) # Configure logging logger = logging.getLogger(__name__) @@ -207,13 +210,13 @@ async def main() -> None: ID_PUSH, create_custom_identify_push_handler(host_2, "Host 2") ) - # Start listening on random ports using the run context manager - listen_addr_1 = multiaddr.Multiaddr("/ip4/127.0.0.1/tcp/0") - listen_addr_2 = multiaddr.Multiaddr("/ip4/127.0.0.1/tcp/0") + # Start listening on available interfaces using random ports + listen_addrs_1 = get_available_interfaces(0) # 0 for random port + listen_addrs_2 = get_available_interfaces(0) # 0 for random port async with ( - host_1.run([listen_addr_1]), - host_2.run([listen_addr_2]), + host_1.run(listen_addrs_1), + host_2.run(listen_addrs_2), trio.open_nursery() as nursery, ): # Start the peer-store cleanup task diff --git a/examples/identify_push/identify_push_listener_dialer.py b/examples/identify_push/identify_push_listener_dialer.py index 3701aaf52..079457a22 100644 --- a/examples/identify_push/identify_push_listener_dialer.py +++ b/examples/identify_push/identify_push_listener_dialer.py @@ -14,7 +14,7 @@ python identify_push_listener_dialer.py # Then in another console, run as a dialer (default port 8889): - python identify_push_listener_dialer.py -d /ip4/127.0.0.1/tcp/8888/p2p/PEER_ID + python identify_push_listener_dialer.py -d /ip4/[HOST_IP]/tcp/8888/p2p/PEER_ID (where PEER_ID is the peer ID displayed by the listener) """ @@ -291,10 +291,12 @@ async def run_dialer( identify_push_handler_for(host, use_varint_format=use_varint_format), ) - # Start listening on a different port - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}") + # Start listening on available interfaces + from libp2p.utils.address_validation import get_available_interfaces - async with host.run([listen_addr]): + listen_addrs = get_available_interfaces(port) + + async with host.run(listen_addrs): logger.info("Dialer host ready!") print("Dialer host ready!") diff --git a/examples/ping/ping.py b/examples/ping/ping.py index 5c7f54e46..f62689aa5 100644 --- a/examples/ping/ping.py +++ b/examples/ping/ping.py @@ -118,7 +118,7 @@ def main() -> None: """ example_maddr = ( - "/ip4/127.0.0.1/tcp/8000/p2p/QmQn4SwGkDZKkUEpBRBvTmheQycxAHJUNmVEnjA2v1qe8Q" + "/ip4/[HOST_IP]/tcp/8000/p2p/QmQn4SwGkDZKkUEpBRBvTmheQycxAHJUNmVEnjA2v1qe8Q" ) parser = argparse.ArgumentParser(description=description) From bf132cf3ddc6d9f0dbc38e4325b9f39ef4a48ea5 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Thu, 18 Sep 2025 02:22:31 +0530 Subject: [PATCH 12/17] Fix import statements and improve error handling in examples --- examples/advanced/network_discover.py | 3 ++- examples/doc-examples/example_encryption_insecure.py | 4 ++-- examples/doc-examples/example_peer_discovery.py | 3 ++- examples/doc-examples/example_quic_transport.py | 1 + examples/echo/echo_quic.py | 3 ++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/examples/advanced/network_discover.py b/examples/advanced/network_discover.py index 5bf13c5a5..945ed12c0 100644 --- a/examples/advanced/network_discover.py +++ b/examples/advanced/network_discover.py @@ -19,6 +19,7 @@ except ImportError: # Fallbacks if utilities are missing - use minimal network discovery import socket + def get_available_interfaces(port: int, protocol: str = "tcp"): # Try to get local network interfaces, fallback to loopback addrs = [] @@ -28,7 +29,7 @@ def get_available_interfaces(port: int, protocol: str = "tcp"): local_ip = socket.gethostbyname(hostname) if local_ip != "127.0.0.1": addrs.append(Multiaddr(f"/ip4/{local_ip}/{protocol}/{port}")) - except exception: + except Exception: pass # Always include loopback as fallback addrs.append(Multiaddr(f"/ip4/127.0.0.1/{protocol}/{port}")) diff --git a/examples/doc-examples/example_encryption_insecure.py b/examples/doc-examples/example_encryption_insecure.py index 6c1455795..859ab2958 100644 --- a/examples/doc-examples/example_encryption_insecure.py +++ b/examples/doc-examples/example_encryption_insecure.py @@ -8,7 +8,7 @@ from libp2p.crypto.secp256k1 import ( create_new_key_pair, ) -from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID, Transport +from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID, InsecureTransport from libp2p.utils.address_validation import ( get_available_interfaces, get_optimal_binding_address, @@ -21,7 +21,7 @@ async def main(): key_pair = create_new_key_pair(secret) # Create an insecure transport (not recommended for production) - insecure_transport = Transport( + insecure_transport = InsecureTransport( # local_key_pair: The key pair used for libp2p identity local_key_pair=key_pair, # secure_bytes_provider: Optional function to generate secure random bytes diff --git a/examples/doc-examples/example_peer_discovery.py b/examples/doc-examples/example_peer_discovery.py index de69e4e18..a85796c05 100644 --- a/examples/doc-examples/example_peer_discovery.py +++ b/examples/doc-examples/example_peer_discovery.py @@ -1,5 +1,6 @@ import secrets +from multiaddr import Multiaddr import trio from libp2p import ( @@ -66,7 +67,7 @@ async def main(): for addr in bootstrap_list: try: - peer_info = info_from_p2p_addr(multiaddr.Multiaddr(addr)) + peer_info = info_from_p2p_addr(Multiaddr(addr)) await host.connect(peer_info) print(f"Connected to {peer_info.peer_id.to_string()}") except Exception as e: diff --git a/examples/doc-examples/example_quic_transport.py b/examples/doc-examples/example_quic_transport.py index 3ee6fb515..15fef1a3c 100644 --- a/examples/doc-examples/example_quic_transport.py +++ b/examples/doc-examples/example_quic_transport.py @@ -30,6 +30,7 @@ async def main(): for addr in listen_addrs: addr_str = str(addr).replace("/tcp/", "/udp/") + "/quic-v1" from multiaddr import Multiaddr + quic_addrs.append(Multiaddr(addr_str)) optimal_addr = get_optimal_binding_address(port, protocol="udp") diff --git a/examples/echo/echo_quic.py b/examples/echo/echo_quic.py index ae6b826d3..87618fbb6 100644 --- a/examples/echo/echo_quic.py +++ b/examples/echo/echo_quic.py @@ -93,7 +93,8 @@ async def run_server(port: int, seed: int | None = None) -> None: # Use optimal address for the client command optimal_tcp = get_optimal_binding_address(port) optimal_quic_str = str(optimal_tcp).replace("/tcp/", "/udp/") + "/quic" - optimal_quic_with_peer = f"{optimal_quic_str}/p2p/{host.get_id().to_string()}" + peer_id = host.get_id().to_string() + optimal_quic_with_peer = f"{optimal_quic_str}/p2p/{peer_id}" print( f"\nRun this from the same folder in another console:\n\n" f"python3 ./examples/echo/echo_quic.py -d {optimal_quic_with_peer}\n" From 67a3cab2e2b1f8efd5643bee529679f5ad1ea5a2 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Thu, 18 Sep 2025 02:34:01 +0530 Subject: [PATCH 13/17] Add example for new address paradigm in multiple connections --- .../multiple_connections_example.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/examples/doc-examples/multiple_connections_example.py b/examples/doc-examples/multiple_connections_example.py index f07382830..20a7fd869 100644 --- a/examples/doc-examples/multiple_connections_example.py +++ b/examples/doc-examples/multiple_connections_example.py @@ -7,6 +7,7 @@ 2. Use different load balancing strategies 3. Access multiple connections through the new API 4. Maintain backward compatibility +5. Use the new address paradigm for network configuration """ import logging @@ -15,6 +16,7 @@ from libp2p import new_swarm from libp2p.network.swarm import ConnectionConfig, RetryConfig +from libp2p.utils import get_available_interfaces, get_optimal_binding_address # Set up logging logging.basicConfig(level=logging.INFO) @@ -103,10 +105,45 @@ async def example_backward_compatibility() -> None: logger.info("Backward compatibility example completed") +async def example_network_address_paradigm() -> None: + """Example of using the new address paradigm with multiple connections.""" + logger.info("Demonstrating network address paradigm...") + + # Get available interfaces using the new paradigm + port = 8000 # Example port + available_interfaces = get_available_interfaces(port) + logger.info(f"Available interfaces: {available_interfaces}") + + # Get optimal binding address + optimal_address = get_optimal_binding_address(port) + logger.info(f"Optimal binding address: {optimal_address}") + + # Create connection config for multiple connections with network awareness + connection_config = ConnectionConfig( + max_connections_per_peer=3, load_balancing_strategy="round_robin" + ) + + # Create swarm with address paradigm + swarm = new_swarm(connection_config=connection_config) + + logger.info("Network address paradigm features:") + logger.info(" - get_available_interfaces() for interface discovery") + logger.info(" - get_optimal_binding_address() for smart address selection") + logger.info(" - Multiple connections with proper network binding") + + await swarm.close() + logger.info("Network address paradigm example completed") + + async def example_production_ready_config() -> None: """Example of production-ready configuration.""" logger.info("Creating swarm with production-ready configuration...") + # Get optimal network configuration using the new paradigm + port = 8001 # Example port + optimal_address = get_optimal_binding_address(port) + logger.info(f"Using optimal binding address: {optimal_address}") + # Production-ready retry configuration retry_config = RetryConfig( max_retries=3, # Reasonable retry limit @@ -156,6 +193,9 @@ async def main() -> None: await example_backward_compatibility() logger.info("-" * 30) + await example_network_address_paradigm() + logger.info("-" * 30) + await example_production_ready_config() logger.info("-" * 30) From 3f30ed4437c3d53d613913b52121e7f0a61a360b Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Thu, 18 Sep 2025 21:36:25 +0530 Subject: [PATCH 14/17] Fix typo in connection timeout comment and improve identify example output formatting --- .../multiple_connections_example.py | 2 +- examples/identify/identify.py | 46 ++++++++++++------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/examples/doc-examples/multiple_connections_example.py b/examples/doc-examples/multiple_connections_example.py index 20a7fd869..ebc8119f9 100644 --- a/examples/doc-examples/multiple_connections_example.py +++ b/examples/doc-examples/multiple_connections_example.py @@ -156,7 +156,7 @@ async def example_production_ready_config() -> None: # Production-ready connection configuration connection_config = ConnectionConfig( max_connections_per_peer=3, # Balance between performance and resource usage - connection_timeout=30.0, # Reasonable timeout + connection_timeout=30.0, # Reasonable timeouta load_balancing_strategy="round_robin", # Simple, predictable strategy ) diff --git a/examples/identify/identify.py b/examples/identify/identify.py index 01b270f0c..1e3eb62d2 100644 --- a/examples/identify/identify.py +++ b/examples/identify/identify.py @@ -95,22 +95,36 @@ async def run(port: int, destination: str, use_varint_format: bool = True) -> No # Get all available addresses with peer ID all_addrs = host_a.get_addrs() - format_name = "length-prefixed" if use_varint_format else "raw protobuf" - format_flag = "--raw-format" if not use_varint_format else "" - - print(f"First host listening (using {format_name} format).") - print("Listener ready, listening on:\n") - for addr in all_addrs: - print(f"{addr}") - - # Use optimal address for the client command - optimal_addr = get_optimal_binding_address(port) - optimal_addr_with_peer = f"{optimal_addr}/p2p/{host_a.get_id().to_string()}" - print( - f"\nRun this from the same folder in another console:\n\n" - f"identify-demo {format_flag} -d {optimal_addr_with_peer}\n" - ) - print("Waiting for incoming identify request...") + if use_varint_format: + format_name = "length-prefixed" + print(f"First host listening (using {format_name} format).") + print("Listener ready, listening on:\n") + for addr in all_addrs: + print(f"{addr}") + + # Use optimal address for the client command + optimal_addr = get_optimal_binding_address(port) + optimal_addr_with_peer = f"{optimal_addr}/p2p/{host_a.get_id().to_string()}" + print( + f"\nRun this from the same folder in another console:\n\n" + f"identify-demo -d {optimal_addr_with_peer}\n" + ) + print("Waiting for incoming identify request...") + else: + format_name = "raw protobuf" + print(f"First host listening (using {format_name} format).") + print("Listener ready, listening on:\n") + for addr in all_addrs: + print(f"{addr}") + + # Use optimal address for the client command + optimal_addr = get_optimal_binding_address(port) + optimal_addr_with_peer = f"{optimal_addr}/p2p/{host_a.get_id().to_string()}" + print( + f"\nRun this from the same folder in another console:\n\n" + f"identify-demo -d {optimal_addr_with_peer}\n" + ) + print("Waiting for incoming identify request...") # Add a custom handler to show connection events async def custom_identify_handler(stream): From a862ac83cd88d5c3515512bcd58904dbffc8c229 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Thu, 18 Sep 2025 23:37:26 +0530 Subject: [PATCH 15/17] Invert raw format flag to determine varint format usage in main function --- examples/identify/identify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/identify/identify.py b/examples/identify/identify.py index 1e3eb62d2..e62e5ff77 100644 --- a/examples/identify/identify.py +++ b/examples/identify/identify.py @@ -300,7 +300,7 @@ def main() -> None: # Determine format: use varint (length-prefixed) if --raw-format is specified, # otherwise use raw protobuf format (old format) - use_varint_format = args.raw_format + use_varint_format = not args.raw_format try: if args.destination: From ae3e2ff943d211961850986a33f52f5c6cdc68e5 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Sat, 20 Sep 2025 13:11:22 +0530 Subject: [PATCH 16/17] Update examples to use wildcard addresses for network binding and improve connection timeout comments --- docs/examples.circuit_relay.rst | 13 +++++++++---- .../doc-examples/multiple_connections_example.py | 2 +- examples/identify/identify.py | 8 ++++++-- newsfragments/885.feature.rst | 4 ++-- tests/core/pubsub/test_gossipsub_px_and_backoff.py | 2 +- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/examples.circuit_relay.rst b/docs/examples.circuit_relay.rst index 85326b005..055aafdfd 100644 --- a/docs/examples.circuit_relay.rst +++ b/docs/examples.circuit_relay.rst @@ -36,12 +36,14 @@ Create a file named ``relay_node.py`` with the following content: from libp2p.relay.circuit_v2.transport import CircuitV2Transport from libp2p.relay.circuit_v2.config import RelayConfig from libp2p.tools.async_service import background_trio_service + from libp2p.utils import get_wildcard_address logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger("relay_node") async def run_relay(): - listen_addr = multiaddr.Multiaddr("/ip4/127.0.0.1/tcp/9000") + # Use wildcard address to listen on all interfaces + listen_addr = get_wildcard_address(9000) host = new_host() config = RelayConfig( @@ -107,6 +109,7 @@ Create a file named ``destination_node.py`` with the following content: from libp2p.relay.circuit_v2.config import RelayConfig from libp2p.peer.peerinfo import info_from_p2p_addr from libp2p.tools.async_service import background_trio_service + from libp2p.utils import get_wildcard_address logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger("destination_node") @@ -139,7 +142,8 @@ Create a file named ``destination_node.py`` with the following content: Run a simple destination node that accepts connections. This is a simplified version that doesn't use the relay functionality. """ - listen_addr = multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/9001") + # Create a libp2p host - use wildcard address to listen on all interfaces + listen_addr = get_wildcard_address(9001) host = new_host() # Configure as a relay receiver (stop) @@ -252,14 +256,15 @@ Create a file named ``source_node.py`` with the following content: from libp2p.peer.peerinfo import info_from_p2p_addr from libp2p.tools.async_service import background_trio_service from libp2p.relay.circuit_v2.discovery import RelayInfo + from libp2p.utils import get_wildcard_address # Configure logging logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger("source_node") async def run_source(relay_peer_id=None, destination_peer_id=None): - # Create a libp2p host - listen_addr = multiaddr.Multiaddr("/ip4/127.0.0.1/tcp/9002") + # Create a libp2p host - use wildcard address to listen on all interfaces + listen_addr = get_wildcard_address(9002) host = new_host() # Configure as a relay client diff --git a/examples/doc-examples/multiple_connections_example.py b/examples/doc-examples/multiple_connections_example.py index ebc8119f9..20a7fd869 100644 --- a/examples/doc-examples/multiple_connections_example.py +++ b/examples/doc-examples/multiple_connections_example.py @@ -156,7 +156,7 @@ async def example_production_ready_config() -> None: # Production-ready connection configuration connection_config = ConnectionConfig( max_connections_per_peer=3, # Balance between performance and resource usage - connection_timeout=30.0, # Reasonable timeouta + connection_timeout=30.0, # Reasonable timeout load_balancing_strategy="round_robin", # Simple, predictable strategy ) diff --git a/examples/identify/identify.py b/examples/identify/identify.py index e62e5ff77..327ea4d67 100644 --- a/examples/identify/identify.py +++ b/examples/identify/identify.py @@ -104,7 +104,9 @@ async def run(port: int, destination: str, use_varint_format: bool = True) -> No # Use optimal address for the client command optimal_addr = get_optimal_binding_address(port) - optimal_addr_with_peer = f"{optimal_addr}/p2p/{host_a.get_id().to_string()}" + optimal_addr_with_peer = ( + f"{optimal_addr}/p2p/{host_a.get_id().to_string()}" + ) print( f"\nRun this from the same folder in another console:\n\n" f"identify-demo -d {optimal_addr_with_peer}\n" @@ -119,7 +121,9 @@ async def run(port: int, destination: str, use_varint_format: bool = True) -> No # Use optimal address for the client command optimal_addr = get_optimal_binding_address(port) - optimal_addr_with_peer = f"{optimal_addr}/p2p/{host_a.get_id().to_string()}" + optimal_addr_with_peer = ( + f"{optimal_addr}/p2p/{host_a.get_id().to_string()}" + ) print( f"\nRun this from the same folder in another console:\n\n" f"identify-demo -d {optimal_addr_with_peer}\n" diff --git a/newsfragments/885.feature.rst b/newsfragments/885.feature.rst index e0566b6af..4b2074eb8 100644 --- a/newsfragments/885.feature.rst +++ b/newsfragments/885.feature.rst @@ -1,2 +1,2 @@ -Enhanced security by defaulting to loopback address (127.0.0.1) instead of wildcard binding. -All examples and core modules now use secure default addresses to prevent unintended public exposure. +Updated all example scripts and core modules to use secure loopback addresses instead of wildcard addresses for network binding. +The `get_wildcard_address` function and related logic now utilize all available interfaces safely, improving security and consistency across the codebase. \ No newline at end of file diff --git a/tests/core/pubsub/test_gossipsub_px_and_backoff.py b/tests/core/pubsub/test_gossipsub_px_and_backoff.py index 72ad5f9d7..261195573 100644 --- a/tests/core/pubsub/test_gossipsub_px_and_backoff.py +++ b/tests/core/pubsub/test_gossipsub_px_and_backoff.py @@ -65,7 +65,7 @@ async def test_prune_backoff(): @pytest.mark.trio async def test_unsubscribe_backoff(): async with PubsubFactory.create_batch_with_gossipsub( - 2, heartbeat_interval=1, prune_back_off=1, unsubscribe_back_off=2 + 2, heartbeat_interval=0.5, prune_back_off=2, unsubscribe_back_off=4 ) as pubsubs: gsub0 = pubsubs[0].router gsub1 = pubsubs[1].router From 77208e95cc629fdce621117337b791416f2f4946 Mon Sep 17 00:00:00 2001 From: yashksaini-coder Date: Sat, 20 Sep 2025 13:32:43 +0530 Subject: [PATCH 17/17] Refactor example scripts and core modules to enhance security by using secure loopback addresses for network binding --- newsfragments/885.feature.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/newsfragments/885.feature.rst b/newsfragments/885.feature.rst index 4b2074eb8..e255be4f9 100644 --- a/newsfragments/885.feature.rst +++ b/newsfragments/885.feature.rst @@ -1,2 +1,2 @@ -Updated all example scripts and core modules to use secure loopback addresses instead of wildcard addresses for network binding. -The `get_wildcard_address` function and related logic now utilize all available interfaces safely, improving security and consistency across the codebase. \ No newline at end of file +Updated all example scripts and core modules to use secure loopback addresses instead of wildcard addresses for network binding. +The `get_wildcard_address` function and related logic now utilize all available interfaces safely, improving security and consistency across the codebase.