mirror of
https://github.com/varun-r-mallya/py-libp2p.git
synced 2025-12-31 20:36:24 +00:00
172 lines
5.5 KiB
Python
172 lines
5.5 KiB
Python
import logging
|
|
|
|
from multiaddr import (
|
|
Multiaddr,
|
|
)
|
|
|
|
from libp2p.abc import (
|
|
IHost,
|
|
INetStream,
|
|
)
|
|
from libp2p.custom_types import (
|
|
StreamHandlerFn,
|
|
TProtocol,
|
|
)
|
|
from libp2p.network.stream.exceptions import (
|
|
StreamClosed,
|
|
)
|
|
from libp2p.peer.peerstore import env_to_send_in_RPC
|
|
from libp2p.utils import (
|
|
decode_varint_with_size,
|
|
get_agent_version,
|
|
varint,
|
|
)
|
|
|
|
from .pb.identify_pb2 import (
|
|
Identify,
|
|
)
|
|
|
|
# Not sure I can do this or I break a pattern
|
|
# logger = logging.getLogger("libp2p.identity.identify")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
ID = TProtocol("/ipfs/id/1.0.0")
|
|
PROTOCOL_VERSION = "ipfs/0.1.0"
|
|
AGENT_VERSION = get_agent_version()
|
|
|
|
|
|
def _multiaddr_to_bytes(maddr: Multiaddr) -> bytes:
|
|
return maddr.to_bytes()
|
|
|
|
|
|
def _remote_address_to_multiaddr(
|
|
remote_address: tuple[str, int] | None,
|
|
) -> Multiaddr | None:
|
|
"""Convert a (host, port) tuple to a Multiaddr."""
|
|
if remote_address is None:
|
|
return None
|
|
|
|
host, port = remote_address
|
|
|
|
# Check if the address is IPv6 (contains ':')
|
|
if ":" in host:
|
|
# IPv6 address
|
|
return Multiaddr(f"/ip6/{host}/tcp/{port}")
|
|
else:
|
|
# IPv4 address
|
|
return Multiaddr(f"/ip4/{host}/tcp/{port}")
|
|
|
|
|
|
def _mk_identify_protobuf(
|
|
host: IHost, observed_multiaddr: Multiaddr | None
|
|
) -> Identify:
|
|
public_key = host.get_public_key()
|
|
laddrs = host.get_addrs()
|
|
protocols = tuple(str(p) for p in host.get_mux().get_protocols() if p is not None)
|
|
|
|
# Create a signed peer-record for the remote peer
|
|
envelope_bytes, _ = env_to_send_in_RPC(host)
|
|
|
|
observed_addr = observed_multiaddr.to_bytes() if observed_multiaddr else b""
|
|
return Identify(
|
|
protocol_version=PROTOCOL_VERSION,
|
|
agent_version=AGENT_VERSION,
|
|
public_key=public_key.serialize(),
|
|
listen_addrs=map(_multiaddr_to_bytes, laddrs),
|
|
observed_addr=observed_addr,
|
|
protocols=protocols,
|
|
signedPeerRecord=envelope_bytes,
|
|
)
|
|
|
|
|
|
def parse_identify_response(response: bytes) -> Identify:
|
|
"""
|
|
Parse identify response that could be either:
|
|
- Old format: raw protobuf
|
|
- New format: length-prefixed protobuf
|
|
|
|
This function provides backward and forward compatibility.
|
|
"""
|
|
# Try new format first: length-prefixed protobuf
|
|
if len(response) >= 1:
|
|
length, varint_size = decode_varint_with_size(response)
|
|
if varint_size > 0 and length > 0 and varint_size + length <= len(response):
|
|
protobuf_data = response[varint_size : varint_size + length]
|
|
try:
|
|
identify_response = Identify()
|
|
identify_response.ParseFromString(protobuf_data)
|
|
# Sanity check: must have agent_version (protocol_version is optional)
|
|
if identify_response.agent_version:
|
|
logger.debug(
|
|
"Parsed length-prefixed identify response (new format)"
|
|
)
|
|
return identify_response
|
|
except Exception:
|
|
pass # Fall through to old format
|
|
|
|
# Fall back to old format: raw protobuf
|
|
try:
|
|
identify_response = Identify()
|
|
identify_response.ParseFromString(response)
|
|
logger.debug("Parsed raw protobuf identify response (old format)")
|
|
return identify_response
|
|
except Exception as e:
|
|
logger.error(f"Failed to parse identify response: {e}")
|
|
logger.error(f"Response length: {len(response)}")
|
|
logger.error(f"Response hex: {response.hex()}")
|
|
raise
|
|
|
|
|
|
def identify_handler_for(
|
|
host: IHost, use_varint_format: bool = True
|
|
) -> StreamHandlerFn:
|
|
async def handle_identify(stream: INetStream) -> None:
|
|
# get observed address from ``stream``
|
|
peer_id = (
|
|
stream.muxed_conn.peer_id
|
|
) # remote peer_id is in class Mplex (mplex.py )
|
|
observed_multiaddr: Multiaddr | None = None
|
|
# Get the remote address
|
|
try:
|
|
remote_address = stream.get_remote_address()
|
|
# Convert to multiaddr
|
|
if remote_address:
|
|
observed_multiaddr = _remote_address_to_multiaddr(remote_address)
|
|
|
|
logger.debug(
|
|
"Connection from remote peer %s, address: %s, multiaddr: %s",
|
|
peer_id,
|
|
remote_address,
|
|
observed_multiaddr,
|
|
)
|
|
except Exception as e:
|
|
logger.error("Error getting remote address: %s", e)
|
|
remote_address = None
|
|
|
|
protobuf = _mk_identify_protobuf(host, observed_multiaddr)
|
|
response = protobuf.SerializeToString()
|
|
|
|
try:
|
|
if use_varint_format:
|
|
# Send length-prefixed protobuf message (new format)
|
|
await stream.write(varint.encode_uvarint(len(response)))
|
|
await stream.write(response)
|
|
logger.debug(
|
|
"Sent new format (length-prefixed) identify response to %s",
|
|
peer_id,
|
|
)
|
|
else:
|
|
# Send raw protobuf message (old format for backward compatibility)
|
|
await stream.write(response)
|
|
logger.debug(
|
|
"Sent old format (raw protobuf) identify response to %s",
|
|
peer_id,
|
|
)
|
|
except StreamClosed:
|
|
logger.debug("Fail to respond to %s request: stream closed", ID)
|
|
else:
|
|
await stream.close()
|
|
logger.debug("successfully handled request for %s from %s", ID, peer_id)
|
|
|
|
return handle_identify
|