Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b840eaa
Implement advanced network discovery example and address validation u…
Aug 8, 2025
fa17423
Refactor echo example to use optimal binding address
Aug 8, 2025
59a898c
Add tests for echo example and address validation utilities
Aug 8, 2025
09cd8b3
Merge branch 'main' into feat/804-add-thin-waist-address
yashksaini-coder Aug 10, 2025
a14c42e
Merge branch 'main' into feat/804-add-thin-waist-address
yashksaini-coder Aug 10, 2025
9a0f224
Merge branch 'libp2p:main' into feat/804-add-thin-waist-address
yashksaini-coder Aug 18, 2025
b363d1d
fix: update listening address handling to use all available interfaces
yashksaini-coder Aug 18, 2025
a2fcf33
refactor: migrate echo example test to use Trio for process handling
yashksaini-coder Aug 18, 2025
9378490
fix: ensure loopback addresses are included in available interfaces
yashksaini-coder Aug 18, 2025
fe71c47
Merge branch 'main' into feat/804-add-thin-waist-address
seetadev Aug 18, 2025
05b372b
Fix linting and type checking issues for Thin Waist feature
acul71 Aug 18, 2025
7f6469d
Merge remote-tracking branch 'acul71/feat/804-add-thin-waist-address'…
yashksaini-coder Aug 19, 2025
55dd883
Merge branch 'main' into feat/804-add-thin-waist-address
yashksaini-coder Aug 19, 2025
a1b1624
fix: correct listening address variable in echo example and streamlin…
yashksaini-coder Aug 19, 2025
3ff5728
Merge branch 'feat/804-add-thin-waist-address' of
yashksaini-coder Aug 19, 2025
69d5274
fix: update listening address parameter in echo example to accept a list
yashksaini-coder Aug 19, 2025
905f3a5
Merge branch 'main' into feat/804-add-thin-waist-address
yashksaini-coder Aug 20, 2025
8a2d1f7
Merge branch 'main' into feat/804-add-thin-waist-address
yashksaini-coder Aug 20, 2025
c2c91b8
refactor: Improve comment formatting in test_echo_thin_waist.py for c…
yashksaini-coder Aug 20, 2025
5b9bec8
fix: Enhance error handling in echo stream handler to manage stream c…
yashksaini-coder Aug 20, 2025
ed2716c
feat: Enhance echo example to dynamically find free ports and improve…
yashksaini-coder Aug 22, 2025
b6cbd78
Fix multi-address listening bug in swarm.listen()
acul71 Aug 23, 2025
3bd6d1f
doc: add newsfragment
acul71 Aug 24, 2025
b38d504
Merge pull request #1 from acul71/fix/multi-address-listening-bug
yashksaini-coder Aug 24, 2025
88a1f0a
cherry pick https://github.com/acul71/py-libp2p-fork/blob/7a1198c8c6e…
yashksaini-coder Aug 24, 2025
cf48d2e
chore(app): Add 811.internal.rst
yashksaini-coder Aug 24, 2025
75ffb79
fix: Ensure newline at end of file in address_validation.py and updat…
yashksaini-coder Aug 24, 2025
ed91ee0
refactor(app): 804 refactored find_free_port() in address_validation.py
yashksaini-coder Aug 24, 2025
63a8458
add import to __init__
yashksaini-coder Aug 24, 2025
fde8c8f
Merge branch 'main' into feat/804-add-thin-waist-address
yashksaini-coder Aug 24, 2025
6a0a7c2
chore(app): Add newsfragment for 811.feature.rst
yashksaini-coder Aug 24, 2025
6c6adf7
chore(app): 804 Suggested changes - Remove the comment
yashksaini-coder Aug 25, 2025
c9795e3
Merge branch 'main' into feat/804-add-thin-waist-address
seetadev Aug 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions examples/advanced/network_discover.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
Advanced demonstration of Thin Waist address handling.

Run:
python -m examples.advanced.network_discovery
"""

from __future__ import annotations

from multiaddr import Multiaddr

try:
from libp2p.utils.address_validation import (
expand_wildcard_address,
get_available_interfaces,
get_optimal_binding_address,
)
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}")]

def expand_wildcard_address(addr: Multiaddr, port: int | None = None):
if port is None:
return [addr]
addr_str = str(addr).rsplit("/", 1)[0]
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}")


def main() -> None:
port = 8080
interfaces = get_available_interfaces(port)
print(f"Discovered interfaces for port {port}:")
for a in interfaces:
print(f" - {a}")

wildcard_v4 = Multiaddr(f"/ip4/0.0.0.0/tcp/{port}")
expanded_v4 = expand_wildcard_address(wildcard_v4)
print("\nExpanded IPv4 wildcard:")
for a in expanded_v4:
print(f" - {a}")

wildcard_v6 = Multiaddr(f"/ip6/::/tcp/{port}")
expanded_v6 = expand_wildcard_address(wildcard_v6)
print("\nExpanded IPv6 wildcard:")
for a in expanded_v6:
print(f" - {a}")

print("\nOptimal binding address heuristic result:")
print(f" -> {get_optimal_binding_address(port)}")

override_port = 9000
overridden = expand_wildcard_address(wildcard_v4, port=override_port)
print(f"\nPort override expansion to {override_port}:")
for a in overridden:
print(f" - {a}")


if __name__ == "__main__":
main()
48 changes: 35 additions & 13 deletions examples/echo/echo.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import argparse
import random
import secrets

import multiaddr
import trio
Expand All @@ -12,40 +14,55 @@
from libp2p.custom_types import (
TProtocol,
)
from libp2p.network.stream.exceptions import (
StreamEOF,
)
from libp2p.network.stream.net_stream import (
INetStream,
)
from libp2p.peer.peerinfo import (
info_from_p2p_addr,
)
from libp2p.utils.address_validation import (
find_free_port,
get_available_interfaces,
)

PROTOCOL_ID = TProtocol("/echo/1.0.0")
MAX_READ_LEN = 2**32 - 1


async def _echo_stream_handler(stream: INetStream) -> None:
# Wait until EOF
msg = await stream.read(MAX_READ_LEN)
await stream.write(msg)
await stream.close()
try:
peer_id = stream.muxed_conn.peer_id
print(f"Received connection from {peer_id}")
# Wait until EOF
msg = await stream.read(MAX_READ_LEN)
print(f"Echoing message: {msg.decode('utf-8')}")
await stream.write(msg)
except StreamEOF:
print("Stream closed by remote peer.")
except Exception as e:
print(f"Error in echo handler: {e}")
finally:
await stream.close()


async def run(port: int, destination: str, seed: int | None = None) -> None:
listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}")
# CHANGED: previously hardcoded 0.0.0.0
if port <= 0:
port = find_free_port()
listen_addr = get_available_interfaces(port)

if seed:
import random

random.seed(seed)
secret_number = random.getrandbits(32 * 8)
secret = secret_number.to_bytes(length=32, byteorder="big")
else:
import secrets

secret = secrets.token_bytes(32)

host = new_host(key_pair=create_new_key_pair(secret))
async with host.run(listen_addrs=[listen_addr]), trio.open_nursery() as nursery:
async with host.run(listen_addrs=listen_addr), trio.open_nursery() as nursery:
# Start the peer-store cleanup task
nursery.start_soon(host.get_peerstore().start_cleanup_task, 60)

Expand All @@ -54,10 +71,15 @@ async def run(port: int, destination: str, seed: int | None = None) -> None:
if not destination: # its the server
host.set_stream_handler(PROTOCOL_ID, _echo_stream_handler)

# Print all listen addresses with peer ID (JS parity)
print("Listener ready, listening on:\n")
peer_id = host.get_id().to_string()
for addr in listen_addr:
print(f"{addr}/p2p/{peer_id}")

print(
"Run this from the same folder in another console:\n\n"
f"echo-demo "
f"-d {host.get_addrs()[0]}\n"
"\nRun this from the same folder in another console:\n\n"
f"echo-demo -d {host.get_addrs()[0]}\n"
)
print("Waiting for incoming connections...")
await trio.sleep_forever()
Expand Down
11 changes: 3 additions & 8 deletions examples/pubsub/pubsub.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import argparse
import logging
import socket

import base58
import multiaddr
Expand Down Expand Up @@ -31,6 +30,9 @@
from libp2p.tools.async_service.trio_service import (
background_trio_service,
)
from libp2p.utils.address_validation import (
find_free_port,
)

# Configure logging
logging.basicConfig(
Expand Down Expand Up @@ -77,13 +79,6 @@ async def publish_loop(pubsub, topic, termination_event):
await trio.sleep(1) # Avoid tight loop on error


def find_free_port():
"""Find a free port on localhost."""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(("", 0)) # Bind to a free port provided by the OS
return s.getsockname()[1]


async def monitor_peer_topics(pubsub, nursery, termination_event):
"""
Monitor for new topics that peers are subscribed to and
Expand Down
11 changes: 7 additions & 4 deletions libp2p/network/swarm.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,11 @@ async def listen(self, *multiaddrs: Multiaddr) -> bool:
# We need to wait until `self.listener_nursery` is created.
await self.event_listener_nursery_created.wait()

success_count = 0
for maddr in multiaddrs:
if str(maddr) in self.listeners:
return True
success_count += 1
continue

async def conn_handler(
read_write_closer: ReadWriteCloser, maddr: Multiaddr = maddr
Expand Down Expand Up @@ -302,13 +304,14 @@ async def conn_handler(
# Call notifiers since event occurred
await self.notify_listen(maddr)

return True
success_count += 1
logger.debug("successfully started listening on: %s", maddr)
except OSError:
# Failed. Continue looping.
logger.debug("fail to listen on: %s", maddr)

# No maddr succeeded
return False
# Return true if at least one address succeeded
return success_count > 0

async def close(self) -> None:
"""
Expand Down
11 changes: 11 additions & 0 deletions libp2p/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
get_agent_version,
)

from libp2p.utils.address_validation import (
get_available_interfaces,
get_optimal_binding_address,
expand_wildcard_address,
find_free_port,
)

__all__ = [
"decode_uvarint_from_stream",
"encode_delim",
Expand All @@ -26,4 +33,8 @@
"decode_varint_from_bytes",
"decode_varint_with_size",
"read_length_prefixed_protobuf",
"get_available_interfaces",
"get_optimal_binding_address",
"expand_wildcard_address",
"find_free_port",
]
Loading
Loading