mirror of
https://github.com/varun-r-mallya/py-libp2p.git
synced 2026-02-12 16:10:57 +00:00
feat: init mDNS discovery module
This commit is contained in:
36
examples/discovery/mDNS/mDNS.py
Normal file
36
examples/discovery/mDNS/mDNS.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import secrets
|
||||||
|
import multiaddr
|
||||||
|
import trio
|
||||||
|
|
||||||
|
from libp2p import new_host
|
||||||
|
from libp2p.crypto.secp256k1 import create_new_key_pair
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
# Generate a key pair for the host
|
||||||
|
secret = secrets.token_bytes(32)
|
||||||
|
key_pair = create_new_key_pair(secret)
|
||||||
|
|
||||||
|
# Listen on a random TCP port
|
||||||
|
listen_addr = multiaddr.Multiaddr("/ip4/0.0.0.0/tcp/0")
|
||||||
|
|
||||||
|
# Enable mDNS discovery
|
||||||
|
host = new_host(key_pair=key_pair, enable_mDNS=True)
|
||||||
|
|
||||||
|
async with host.run(listen_addrs=[listen_addr]):
|
||||||
|
print("Host started!")
|
||||||
|
print("Peer ID:", host.get_id())
|
||||||
|
print("Listening on:", [str(addr) for addr in host.get_addrs()])
|
||||||
|
|
||||||
|
# Print discovered peers via mDNS
|
||||||
|
print("Waiting for mDNS peer discovery events (Ctrl+C to exit)...")
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
# Print all known peers every 5 seconds
|
||||||
|
peers = host.get_peerstore().peer_ids()
|
||||||
|
print("Known peers:", [str(p) for p in peers if p != host.get_id()])
|
||||||
|
await trio.sleep(5)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("Exiting...")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
trio.run(main)
|
||||||
@ -71,6 +71,9 @@ from libp2p.transport.upgrader import (
|
|||||||
from libp2p.utils.logging import (
|
from libp2p.utils.logging import (
|
||||||
setup_logging,
|
setup_logging,
|
||||||
)
|
)
|
||||||
|
from libp2p.discovery.mdns.mdns import (
|
||||||
|
MDNSDiscovery
|
||||||
|
)
|
||||||
|
|
||||||
# Initialize logging configuration
|
# Initialize logging configuration
|
||||||
setup_logging()
|
setup_logging()
|
||||||
@ -236,13 +239,14 @@ def new_swarm(
|
|||||||
|
|
||||||
|
|
||||||
def new_host(
|
def new_host(
|
||||||
key_pair: KeyPair | None = None,
|
key_pair: Optional[KeyPair] = None,
|
||||||
muxer_opt: TMuxerOptions | None = None,
|
muxer_opt: Optional[TMuxerOptions] = None,
|
||||||
sec_opt: TSecurityOptions | None = None,
|
sec_opt: Optional[TSecurityOptions] = None,
|
||||||
peerstore_opt: IPeerStore | None = None,
|
peerstore_opt: Optional[IPeerStore] = None,
|
||||||
disc_opt: IPeerRouting | None = None,
|
disc_opt: Optional[IPeerRouting] = None,
|
||||||
muxer_preference: Literal["YAMUX", "MPLEX"] | None = None,
|
muxer_preference: Optional[Literal["YAMUX", "MPLEX"]] = None,
|
||||||
listen_addrs: Sequence[multiaddr.Multiaddr] | None = None,
|
listen_addrs: Sequence[multiaddr.Multiaddr] = None,
|
||||||
|
enable_mDNS: bool = False,
|
||||||
) -> IHost:
|
) -> IHost:
|
||||||
"""
|
"""
|
||||||
Create a new libp2p host based on the given parameters.
|
Create a new libp2p host based on the given parameters.
|
||||||
@ -266,8 +270,15 @@ def new_host(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if disc_opt is not None:
|
if disc_opt is not None:
|
||||||
return RoutedHost(swarm, disc_opt)
|
host = RoutedHost(swarm, disc_opt)
|
||||||
return BasicHost(swarm)
|
else:
|
||||||
|
host = BasicHost(swarm)
|
||||||
|
|
||||||
|
if enable_mDNS:
|
||||||
|
mdns = MDNSDiscovery(swarm)
|
||||||
|
mdns.start()
|
||||||
|
host._mdns = mdns
|
||||||
|
|
||||||
|
return host
|
||||||
|
|
||||||
__version__ = __version("libp2p")
|
__version__ = __version("libp2p")
|
||||||
|
|||||||
0
libp2p/discovery/__init__.py
Normal file
0
libp2p/discovery/__init__.py
Normal file
0
libp2p/discovery/mdns/__init__.py
Normal file
0
libp2p/discovery/mdns/__init__.py
Normal file
0
libp2p/discovery/mdns/broadcaster.py
Normal file
0
libp2p/discovery/mdns/broadcaster.py
Normal file
0
libp2p/discovery/mdns/listener.py
Normal file
0
libp2p/discovery/mdns/listener.py
Normal file
85
libp2p/discovery/mdns/mdns.py
Normal file
85
libp2p/discovery/mdns/mdns.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
"""
|
||||||
|
mDNS-based peer discovery for py-libp2p.
|
||||||
|
Conforms to https://github.com/libp2p/specs/blob/master/discovery/mdns.md
|
||||||
|
Uses zeroconf for mDNS broadcast/listen. Async operations use trio.
|
||||||
|
"""
|
||||||
|
import trio
|
||||||
|
from typing import Callable, Optional
|
||||||
|
from zeroconf import ServiceInfo, Zeroconf, ServiceBrowser, ServiceStateChange
|
||||||
|
from .utils import (
|
||||||
|
stringGen
|
||||||
|
)
|
||||||
|
from libp2p.abc import (
|
||||||
|
INetworkService
|
||||||
|
)
|
||||||
|
|
||||||
|
SERVICE_TYPE = "_p2p._udp.local."
|
||||||
|
MCAST_PORT = 5353
|
||||||
|
MCAST_ADDR = "224.0.0.251"
|
||||||
|
|
||||||
|
class MDNSDiscovery:
|
||||||
|
def __init__(self, swarm: INetworkService):
|
||||||
|
self.peer_id = swarm.get_peer_id()
|
||||||
|
self.port = 8000 # Default port, can be overridden
|
||||||
|
# self.broadcast = init.get('broadcast', True) is not False
|
||||||
|
# self.on_peer = on_peer # Callback: async def on_peer(peer_info)
|
||||||
|
# self.service_name = service_name or f"{peer_id}.{SERVICE_TYPE}"
|
||||||
|
# self.zeroconf = Zeroconf()
|
||||||
|
# self._service_info = ServiceInfo(
|
||||||
|
# SERVICE_TYPE,
|
||||||
|
# self.service_name,
|
||||||
|
# addresses=[], # Will be set on register
|
||||||
|
# port=self.port,
|
||||||
|
# properties={b'id': peer_id.encode()},
|
||||||
|
# )
|
||||||
|
# self._browser = None
|
||||||
|
# self._running = False
|
||||||
|
|
||||||
|
def main(self) -> None:
|
||||||
|
"""
|
||||||
|
Main entry point for the mDNS discovery service.
|
||||||
|
This method is intended to be run in an event loop.
|
||||||
|
"""
|
||||||
|
trio.run(self.start)
|
||||||
|
|
||||||
|
async def start(self):
|
||||||
|
await trio.sleep(10)
|
||||||
|
# self._running = True
|
||||||
|
# await trio.to_thread.run_sync(self.zeroconf.register_service, self._service_info)
|
||||||
|
# self._browser = ServiceBrowser(self.zeroconf, SERVICE_TYPE, handlers=[self._on_service_state_change])
|
||||||
|
print(f"Starting mDNS discovery for peer {self.peer_id} on port {self.port}")
|
||||||
|
|
||||||
|
async def stop(self):
|
||||||
|
# self._running = False
|
||||||
|
# await trio.to_thread.run_sync(self.zeroconf.unregister_service, self._service_info)
|
||||||
|
# await trio.to_thread.run_sync(self.zeroconf.close)
|
||||||
|
print(f"Stopping mDNS discovery for peer {self.peer_id}")
|
||||||
|
|
||||||
|
def _on_service_state_change(self, zeroconf, service_type, name, state_change):
|
||||||
|
if state_change is not ServiceStateChange.Added:
|
||||||
|
return
|
||||||
|
info = zeroconf.get_service_info(service_type, name)
|
||||||
|
if not info or name == self.service_name:
|
||||||
|
return
|
||||||
|
peer_id = info.properties.get(b'id')
|
||||||
|
if not peer_id:
|
||||||
|
return
|
||||||
|
peer_id = peer_id.decode()
|
||||||
|
addresses = [addr for addr in info.parsed_addresses()]
|
||||||
|
port = info.port
|
||||||
|
peer_info = {'peer_id': peer_id, 'addresses': addresses, 'port': port}
|
||||||
|
if self.on_peer:
|
||||||
|
# Schedule callback in the background
|
||||||
|
trio.lowlevel.spawn_system_task(self._call_on_peer, peer_info)
|
||||||
|
|
||||||
|
async def _call_on_peer(self, peer_info):
|
||||||
|
if self.on_peer:
|
||||||
|
await self.on_peer(peer_info)
|
||||||
|
|
||||||
|
# Example usage:
|
||||||
|
# async def on_peer(peer_info):
|
||||||
|
# print(f"Discovered peer: {peer_info['peer_id']} at {peer_info['addresses']}:{peer_info['port']}")
|
||||||
|
# mdns = MDNSDiscovery(peer_id, port, on_peer)
|
||||||
|
# await mdns.start()
|
||||||
|
# ...
|
||||||
|
# await mdns.stop()
|
||||||
Reference in New Issue
Block a user